diff options
Diffstat (limited to 'src/socket.c')
-rw-r--r-- | src/socket.c | 151 |
1 files changed, 135 insertions, 16 deletions
diff --git a/src/socket.c b/src/socket.c index 2b382119..d28df81a 100644 --- a/src/socket.c +++ b/src/socket.c @@ -46,9 +46,10 @@ int sock_setreuse(int fd, int reuse) { int sock_setreuse_port(int fd, int reuse) { int ret = -1; + #ifdef SO_REUSEPORT ret = setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &reuse, sizeof(reuse)); - if(ret == -1) + if(ret == -1 && errno != ENOPROTOOPT) error("failed to set SO_REUSEPORT on socket %d", fd); #endif @@ -80,6 +81,50 @@ int sock_enlarge_out(int fd) { // -------------------------------------------------------------------------------------------------------------------- // listening sockets +int create_listen_socket_unix(const char *path, int listen_backlog) { + int sock; + + debug(D_LISTENER, "LISTENER: UNIX creating new listening socket on path '%s'", path); + + sock = socket(AF_UNIX, SOCK_STREAM, 0); + if(sock < 0) { + error("LISTENER: UNIX socket() on path '%s' failed.", path); + return -1; + } + + sock_setnonblock(sock); + sock_enlarge_in(sock); + + struct sockaddr_un name; + memset(&name, 0, sizeof(struct sockaddr_un)); + name.sun_family = AF_UNIX; + strncpy(name.sun_path, path, sizeof(name.sun_path)-1); + + errno = 0; + if (unlink(path) == -1 && errno != ENOENT) + error("LISTENER: failed to remove existing (probably obsolete or left-over) file on UNIX socket path '%s'.", path); + + if(bind (sock, (struct sockaddr *) &name, sizeof (name)) < 0) { + close(sock); + error("LISTENER: UNIX bind() on path '%s' failed.", path); + return -1; + } + + // we have to chmod this to 0777 so that the client will be able + // to read from and write to this socket. + if(chmod(path, 0777) == -1) + error("LISTENER: failed to chmod() socket file '%s'.", path); + + if(listen(sock, listen_backlog) < 0) { + close(sock); + error("LISTENER: UNIX listen() on path '%s' failed.", path); + return -1; + } + + debug(D_LISTENER, "LISTENER: Listening on UNIX path '%s'", path); + return sock; +} + int create_listen_socket4(int socktype, const char *ip, int port, int listen_backlog) { int sock; @@ -176,7 +221,7 @@ int create_listen_socket6(int socktype, uint32_t scope_id, const char *ip, int p return sock; } -static inline int listen_sockets_add(LISTEN_SOCKETS *sockets, int fd, int socktype, const char *protocol, const char *ip, int port) { +static inline int listen_sockets_add(LISTEN_SOCKETS *sockets, int fd, int family, int socktype, const char *protocol, const char *ip, int port) { if(sockets->opened >= MAX_LISTEN_FDS) { error("LISTENER: Too many listening sockets. Failed to add listening %s socket at ip '%s' port %d, protocol %s, socktype %d", protocol, ip, port, protocol, socktype); close(fd); @@ -184,11 +229,27 @@ static inline int listen_sockets_add(LISTEN_SOCKETS *sockets, int fd, int sockty } sockets->fds[sockets->opened] = fd; + sockets->fds_types[sockets->opened] = socktype; + sockets->fds_families[sockets->opened] = family; char buffer[100 + 1]; - snprintfz(buffer, 100, "%s:[%s]:%d", protocol, ip, port); + + switch(family) { + case AF_INET: + snprintfz(buffer, 100, "%s:%s:%d", protocol, ip, port); + break; + + case AF_INET6: + default: + snprintfz(buffer, 100, "%s:[%s]:%d", protocol, ip, port); + break; + + case AF_UNIX: + snprintfz(buffer, 100, "%s:%s", protocol, ip); + break; + } + sockets->fds_names[sockets->opened] = strdupz(buffer); - sockets->fds_types[sockets->opened] = socktype; sockets->opened++; return 0; @@ -230,7 +291,7 @@ void listen_sockets_close(LISTEN_SOCKETS *sockets) { sockets->failed = 0; } -static inline int bind_to_one(LISTEN_SOCKETS *sockets, const char *definition, int default_port, int listen_backlog) { +static inline int bind_to_this(LISTEN_SOCKETS *sockets, const char *definition, int default_port, int listen_backlog) { int added = 0; struct addrinfo hints; struct addrinfo *result = NULL, *rp = NULL; @@ -258,6 +319,22 @@ static inline int bind_to_one(LISTEN_SOCKETS *sockets, const char *definition, i socktype = SOCK_DGRAM; protocol_str = "udp"; } + else if(strncmp(ip, "unix:", 5) == 0) { + char *path = ip + 5; + socktype = SOCK_STREAM; + protocol_str = "unix"; + + int fd = create_listen_socket_unix(path, listen_backlog); + if (fd == -1) { + error("LISTENER: Cannot create unix socket '%s'", path); + sockets->failed++; + } + else { + listen_sockets_add(sockets, fd, AF_UNIX, socktype, protocol_str, path, 0); + added++; + } + return added; + } char *e = ip; if(*e == '[') { @@ -314,11 +391,13 @@ static inline int bind_to_one(LISTEN_SOCKETS *sockets, const char *definition, i for (rp = result; rp != NULL; rp = rp->ai_next) { int fd = -1; + int family = -1; char rip[INET_ADDRSTRLEN + INET6_ADDRSTRLEN] = "INVALID"; int rport = default_port; - switch (rp->ai_addr->sa_family) { + family = rp->ai_addr->sa_family; + switch (family) { case AF_INET: { struct sockaddr_in *sin = (struct sockaddr_in *) rp->ai_addr; inet_ntop(AF_INET, &sin->sin_addr, rip, INET_ADDRSTRLEN); @@ -338,7 +417,7 @@ static inline int bind_to_one(LISTEN_SOCKETS *sockets, const char *definition, i } default: - debug(D_LISTENER, "LISTENER: Unknown socket family %d", rp->ai_addr->sa_family); + debug(D_LISTENER, "LISTENER: Unknown socket family %d", family); break; } @@ -347,7 +426,7 @@ static inline int bind_to_one(LISTEN_SOCKETS *sockets, const char *definition, i sockets->failed++; } else { - listen_sockets_add(sockets, fd, socktype, protocol_str, rip, rport); + listen_sockets_add(sockets, fd, family, socktype, protocol_str, rip, rport); added++; } } @@ -385,7 +464,7 @@ int listen_sockets_setup(LISTEN_SOCKETS *sockets) { char buf[e - s + 1]; strncpyz(buf, s, e - s); - bind_to_one(sockets, buf, sockets->default_port, sockets->backlog); + bind_to_this(sockets, buf, sockets->default_port, sockets->backlog); s = e; } @@ -403,7 +482,39 @@ int listen_sockets_setup(LISTEN_SOCKETS *sockets) { // -------------------------------------------------------------------------------------------------------------------- // connect to another host/port -// _connect_to() +// connect_to_this_unix() +// path the path of the unix socket +// timeout the timeout for establishing a connection + +static inline int connect_to_unix(const char *path, struct timeval *timeout) { + int fd = socket(AF_UNIX, SOCK_STREAM, 0); + if(fd == -1) { + error("Failed to create UNIX socket() for '%s'", path); + return -1; + } + + if(timeout) { + if(setsockopt(fd, SOL_SOCKET, SO_SNDTIMEO, (char *) timeout, sizeof(struct timeval)) < 0) + error("Failed to set timeout on UNIX socket '%s'", path); + } + + struct sockaddr_un addr; + memset(&addr, 0, sizeof(addr)); + addr.sun_family = AF_UNIX; + strncpy(addr.sun_path, path, sizeof(addr.sun_path)-1); + + if (connect(fd, (struct sockaddr*)&addr, sizeof(addr)) == -1) { + error("Cannot connect to UNIX socket on path '%s'.", path); + close(fd); + return -1; + } + + debug(D_CONNECT_TO, "Connected to UNIX socket on path '%s'.", path); + + return fd; +} + +// connect_to_this_ip46() // protocol IPPROTO_TCP, IPPROTO_UDP // socktype SOCK_STREAM, SOCK_DGRAM // host the destination hostname or IP address (IPv4 or IPv6) to connect to @@ -413,7 +524,7 @@ int listen_sockets_setup(LISTEN_SOCKETS *sockets) { // service the service name or port to connect to // timeout the timeout for establishing a connection -static inline int _connect_to(int protocol, int socktype, const char *host, uint32_t scope_id, const char *service, struct timeval *timeout) { +static inline int connect_to_this_ip46(int protocol, int socktype, const char *host, uint32_t scope_id, const char *service, struct timeval *timeout) { struct addrinfo hints; struct addrinfo *ai_head = NULL, *ai = NULL; @@ -519,7 +630,7 @@ static inline int _connect_to(int protocol, int socktype, const char *host, uint return fd; } -// connect_to() +// connect_to_this() // // definition format: // @@ -530,7 +641,7 @@ static inline int _connect_to(int protocol, int socktype, const char *host, uint // INTERFACE = for IPv6 only, the network interface to use // PORT = port number or service name -int connect_to(const char *definition, int default_port, struct timeval *timeout) { +int connect_to_this(const char *definition, int default_port, struct timeval *timeout) { char buffer[strlen(definition) + 1]; strcpy(buffer, definition); @@ -551,6 +662,10 @@ int connect_to(const char *definition, int default_port, struct timeval *timeout protocol = IPPROTO_UDP; socktype = SOCK_DGRAM; } + else if(strncmp(host, "unix:", 5) == 0) { + char *path = host + 5; + return connect_to_unix(path, timeout); + } char *e = host; if(*e == '[') { @@ -595,7 +710,7 @@ int connect_to(const char *definition, int default_port, struct timeval *timeout service = default_service; - return _connect_to(protocol, socktype, host, scope_id, service, timeout); + return connect_to_this_ip46(protocol, socktype, host, scope_id, service, timeout); } int connect_to_one_of(const char *destination, int default_port, struct timeval *timeout, size_t *reconnects_counter, char *connected_to, size_t connected_to_size) { @@ -617,7 +732,7 @@ int connect_to_one_of(const char *destination, int default_port, struct timeval char buf[e - s + 1]; strncpyz(buf, s, e - s); if(reconnects_counter) *reconnects_counter += 1; - sock = connect_to(buf, default_port, timeout); + sock = connect_to_this(buf, default_port, timeout); if(sock != -1) { if(connected_to && connected_to_size) { strncpy(connected_to, buf, connected_to_size); @@ -747,7 +862,7 @@ int accept_socket(int fd, int flags, char *client_ip, size_t ipsize, char *clien socklen_t addrlen = sizeof(sadr); int nfd = accept4(fd, (struct sockaddr *)&sadr, &addrlen, flags); - if (nfd >= 0) { + if (likely(nfd >= 0)) { if (getnameinfo((struct sockaddr *)&sadr, addrlen, client_ip, (socklen_t)ipsize, client_port, (socklen_t)portsize, NI_NUMERICHOST | NI_NUMERICSERV) != 0) { error("LISTENER: cannot getnameinfo() on received client connection."); strncpyz(client_ip, "UNKNOWN", ipsize - 1); @@ -775,6 +890,10 @@ int accept_socket(int fd, int flags, char *client_ip, size_t ipsize, char *clien break; } } +#ifdef HAVE_ACCEPT4 + else if(errno == ENOSYS) + error("netdata has been compiled with the assumption that the system has the accept4() call, but it is not here. Recompile netdata like this: ./configure --disable-accept4 ..."); +#endif return nfd; } |