summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDavid Strauss <david@davidstrauss.net>2013-10-21 18:32:08 -0700
committerDavid Strauss <david@davidstrauss.net>2013-10-21 18:32:08 -0700
commit40976028c6f24b9934e6c797ef6f5e123ac520d5 (patch)
tree66b6c2a39c076e4fceae77b298ababd5fdd5c3ff
parent202e6abb3d0959d8051d514ae48a140934a30f47 (diff)
socket-proxyd: Use ONESHOT to behave properly with multiple accept() processes.
-rw-r--r--src/socket-proxy/socket-proxyd.c90
1 files changed, 66 insertions, 24 deletions
diff --git a/src/socket-proxy/socket-proxyd.c b/src/socket-proxy/socket-proxyd.c
index d64b0d2867..4eec79ecb5 100644
--- a/src/socket-proxy/socket-proxyd.c
+++ b/src/socket-proxy/socket-proxyd.c
@@ -64,10 +64,13 @@ struct connection {
};
static void free_connection(struct connection *c) {
- log_debug("Freeing fd=%d (conn %p).", c->fd, c);
- sd_event_source_unref(c->w);
- close_nointr_nofail(c->fd);
- free(c);
+ if (c != NULL) {
+ log_debug("Freeing fd=%d (conn %p).", c->fd, c);
+ sd_event_source_unref(c->w);
+ if (c->fd > 0)
+ close_nointr_nofail(c->fd);
+ free(c);
+ }
}
static int add_event_to_connection(struct connection *c, uint32_t events) {
@@ -347,19 +350,27 @@ static int get_server_connection_fd(const struct proxy *proxy) {
return server_fd;
}
-static int accept_cb(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
- struct proxy *proxy = (struct proxy *) userdata;
- struct connection *c_server_to_client;
+static int do_accept(sd_event *e, struct proxy *p, int fd) {
+ struct connection *c_server_to_client = NULL;
struct connection *c_client_to_server = NULL;
int r = 0;
union sockaddr_union sa;
socklen_t salen = sizeof(sa);
+ int client_fd, server_fd;
+
+ client_fd = accept4(fd, (struct sockaddr *) &sa, &salen, SOCK_NONBLOCK|SOCK_CLOEXEC);
+ if (client_fd < 0) {
+ if (errno == EAGAIN || errno == EWOULDBLOCK)
+ return -errno;
+ log_error("Error %d accepting client connection: %m", errno);
+ r = -errno;
+ goto fail;
+ }
- assert(revents & EPOLLIN);
-
- c_server_to_client = new0(struct connection, 1);
- if (c_server_to_client == NULL) {
- log_oom();
+ server_fd = get_server_connection_fd(p);
+ if (server_fd < 0) {
+ log_error("Error initiating server connection.");
+ r = server_fd;
goto fail;
}
@@ -369,18 +380,14 @@ static int accept_cb(sd_event_source *s, int fd, uint32_t revents, void *userdat
goto fail;
}
- c_server_to_client->fd = get_server_connection_fd(proxy);
- if (c_server_to_client->fd < 0) {
- log_error("Error initiating server connection.");
- goto fail;
- }
-
- c_client_to_server->fd = accept4(fd, (struct sockaddr *) &sa, &salen, SOCK_NONBLOCK|SOCK_CLOEXEC);
- if (c_client_to_server->fd < 0) {
- log_error("Error accepting client connection.");
+ c_server_to_client = new0(struct connection, 1);
+ if (c_server_to_client == NULL) {
+ log_oom();
goto fail;
}
+ c_client_to_server->fd = client_fd;
+ c_server_to_client->fd = server_fd;
if (sa.sa.sa_family == AF_INET || sa.sa.sa_family == AF_INET6) {
char sa_str[INET6_ADDRSTRLEN];
@@ -401,7 +408,7 @@ static int accept_cb(sd_event_source *s, int fd, uint32_t revents, void *userdat
log_debug("Server fd=%d (conn %p) successfully initialized.", c_server_to_client->fd, c_server_to_client);
/* Initialize watcher for send to server; this shows connectivity. */
- r = sd_event_add_io(sd_event_get(s), c_server_to_client->fd, EPOLLOUT, connected_to_server_cb, c_server_to_client, &c_server_to_client->w);
+ r = sd_event_add_io(e, c_server_to_client->fd, EPOLLOUT, connected_to_server_cb, c_server_to_client, &c_server_to_client->w);
if (r < 0) {
log_error("Error %d creating connectivity watcher for fd=%d: %s", r, c_server_to_client->fd, strerror(-r));
goto fail;
@@ -419,7 +426,34 @@ fail:
free_connection(c_server_to_client);
finish:
- /* Preserve the main loop even if a single proxy setup fails. */
+ return r;
+}
+
+static int accept_cb(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
+ struct proxy *p = (struct proxy *) userdata;
+ sd_event *e = NULL;
+ int r = 0;
+
+ assert(revents & EPOLLIN);
+
+ e = sd_event_get(s);
+
+ for (;;) {
+ r = do_accept(e, p, fd);
+ if (r == -EAGAIN || r == -EWOULDBLOCK)
+ break;
+ if (r < 0)
+ log_error("Error %d while trying to accept: %s", r, strerror(-r));
+ }
+
+ /* Re-enable the watcher. */
+ r = sd_event_source_set_enabled(s, SD_EVENT_ONESHOT);
+ if (r < 0) {
+ log_error("Error %d while re-enabling listener with ONESHOT: %s", r, strerror(-r));
+ return r;
+ }
+
+ /* Preserve the main loop even if a single accept() fails. */
return 1;
}
@@ -444,7 +478,15 @@ static int run_main_loop(struct proxy *proxy) {
r = sd_event_add_io(e, proxy->listen_fd, EPOLLIN, accept_cb, proxy, &w_accept);
if (r < 0) {
- log_error("Failed to add event IO source: %s", strerror(-r));
+ log_error("Error %d while adding event IO source: %s", r, strerror(-r));
+ return r;
+ }
+
+ /* Set the watcher to oneshot in case other processes are also
+ * watching to accept(). */
+ r = sd_event_source_set_enabled(w_accept, SD_EVENT_ONESHOT);
+ if (r < 0) {
+ log_error("Error %d while setting event IO source to ONESHOT: %s", r, strerror(-r));
return r;
}