X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?a=blobdiff_plain;ds=sidebyside;f=src%2Fsocket-proxy%2Fsocket-proxyd.c;h=054a27a71b9bb185353388bac7a72bc1a3297a3d;hb=f4bd42aa3c9bfbb0fa7a597b715fc3ee8f336764;hp=7fe3e525523bfcec0ba455093a084576eb686125;hpb=b6818c7d10659495de6424bad8e019be1001979f;p=elogind.git diff --git a/src/socket-proxy/socket-proxyd.c b/src/socket-proxy/socket-proxyd.c index 7fe3e5255..054a27a71 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) { @@ -122,7 +125,8 @@ static int send_buffer(struct connection *sender) { int r = 0; /* We cannot assume that even a partial send() indicates that - * the next send() will block. Loop until it does. */ + * the next send() will return EAGAIN or EWOULDBLOCK. Loop until + * it does. */ while (sender->buffer_filled_len > sender->buffer_sent_len) { len = send(receiver->fd, sender->buffer + sender->buffer_sent_len, sender->buffer_filled_len - sender->buffer_sent_len, 0); log_debug("send(%d, ...)=%ld", receiver->fd, len); @@ -132,7 +136,7 @@ static int send_buffer(struct connection *sender) { return -errno; } else { - /* send() is in a blocking state. */ + /* send() is in a would-block state. */ break; } } @@ -167,7 +171,8 @@ static int send_buffer(struct connection *sender) { return r; } - /* If we sent everything without blocking, the buffer is now empty. */ + /* If we sent everything without any issues (would-block or + * partial send), the buffer is now empty. */ sender->buffer_filled_len = 0; sender->buffer_sent_len = 0; @@ -242,7 +247,7 @@ static int transfer_data_cb(sd_event_source *s, int fd, uint32_t revents, void * return r; } -/* Once sending to the server is unblocked, set up the real watchers. */ +/* Once sending to the server is ready, set up the real watchers. */ static int connected_to_server_cb(sd_event_source *s, int fd, uint32_t revents, void *userdata) { struct sd_event *e = NULL; struct connection *c_server_to_client = (struct connection *) userdata; @@ -345,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; } @@ -367,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]; @@ -399,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; @@ -417,7 +426,36 @@ 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)); + break; + } + } + + /* 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; } @@ -434,7 +472,7 @@ static int run_main_loop(struct proxy *proxy) { r = fd_nonblock(proxy->listen_fd, true); if (r < 0) { - log_error("Failed to make listen file descriptor non-blocking: %s", strerror(-r)); + log_error("Failed to make listen file descriptor nonblocking: %s", strerror(-r)); return r; } @@ -442,7 +480,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; } @@ -572,7 +618,6 @@ int main(int argc, char *argv[]) { } } - /* @TODO: Check if this proxy can work with datagram sockets. */ r = sd_is_socket(p.listen_fd, 0, SOCK_STREAM, 1); if (r < 0) { log_error("Error %d while checking inherited socket: %s", r, strerror(-r));