summaryrefslogtreecommitdiffstats
path: root/src/socket.c
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/socket.c151
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;
}