X-Git-Url: https://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?a=blobdiff_plain;f=src%2Fsocket-proxy%2Fsocket-proxyd.c;h=a449b0eec42ba9d28af64c85b4f7642d4f264f5b;hb=5f7e83e8ad6f83095569c452bff2d0702ac02987;hp=d64b0d286785827c462a999e0b7599bf3aafdeb4;hpb=6298945d5c4b9a8116f2b1d1f9c7f6c0ff644a05;p=elogind.git diff --git a/src/socket-proxy/socket-proxyd.c b/src/socket-proxy/socket-proxyd.c index d64b0d286..a449b0eec 100644 --- a/src/socket-proxy/socket-proxyd.c +++ b/src/socket-proxy/socket-proxyd.c @@ -37,6 +37,7 @@ #include "socket-util.h" #include "util.h" #include "event-util.h" +#include "build.h" #define BUFFER_SIZE 16384 #define _cleanup_freeaddrinfo_ _cleanup_(freeaddrinfop) @@ -64,10 +65,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) { @@ -106,11 +110,11 @@ static int remove_event_from_connection(struct connection *c, uint32_t events) { } if (c->events == 0) { - r = sd_event_source_set_enabled(c->w, SD_EVENT_OFF); - if (r < 0) { - log_error("Error %d disabling source: %s", r, strerror(-r)); - return r; - } + r = sd_event_source_set_enabled(c->w, SD_EVENT_OFF); + if (r < 0) { + log_error("Error %d disabling source: %s", r, strerror(-r)); + return r; + } } return 0; @@ -126,7 +130,7 @@ static int send_buffer(struct connection *sender) { * 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); + log_debug("send(%d, ...)=%zd", receiver->fd, len); if (len < 0) { if (errno != EWOULDBLOCK && errno != EAGAIN) { log_error("Error %d in send to fd=%d: %m", errno, receiver->fd); @@ -144,18 +148,18 @@ static int send_buffer(struct connection *sender) { sender->buffer_sent_len += len; } - log_debug("send(%d, ...) completed with %lu bytes still buffered.", receiver->fd, sender->buffer_filled_len - sender->buffer_sent_len); + log_debug("send(%d, ...) completed with %zu bytes still buffered.", receiver->fd, sender->buffer_filled_len - sender->buffer_sent_len); /* Detect a would-block state or partial send. */ if (sender->buffer_filled_len > sender->buffer_sent_len) { /* If the buffer is full, disable events coming for recv. */ if (sender->buffer_filled_len == BUFFER_SIZE) { - r = remove_event_from_connection(sender, EPOLLIN); - if (r < 0) { - log_error("Error %d disabling EPOLLIN for fd=%d: %s", r, sender->fd, strerror(-r)); - return r; - } + r = remove_event_from_connection(sender, EPOLLIN); + if (r < 0) { + log_error("Error %d disabling EPOLLIN for fd=%d: %s", r, sender->fd, strerror(-r)); + return r; + } } /* Watch for when the recipient can be sent data again. */ @@ -203,19 +207,18 @@ static int transfer_data_cb(sd_event_source *s, int fd, uint32_t revents, void * log_debug("Got event revents=%d from fd=%d (conn %p).", revents, fd, c); if (revents & EPOLLIN) { - log_debug("About to recv up to %lu bytes from fd=%d (%lu/BUFFER_SIZE).", BUFFER_SIZE - c->buffer_filled_len, fd, c->buffer_filled_len); + log_debug("About to recv up to %zu bytes from fd=%d (%zu/BUFFER_SIZE).", BUFFER_SIZE - c->buffer_filled_len, fd, c->buffer_filled_len); /* Receive until the buffer's full, there's no more data, * or the client/server disconnects. */ while (c->buffer_filled_len < BUFFER_SIZE) { len = recv(fd, c->buffer + c->buffer_filled_len, BUFFER_SIZE - c->buffer_filled_len, 0); - log_debug("recv(%d, ...)=%ld", fd, len); + log_debug("recv(%d, ...)=%zd", fd, len); if (len < 0) { if (errno != EWOULDBLOCK && errno != EAGAIN) { log_error("Error %d in recv from fd=%d: %m", errno, fd); return -errno; - } - else { + } else { /* recv() is in a blocking state. */ break; } @@ -229,9 +232,9 @@ static int transfer_data_cb(sd_event_source *s, int fd, uint32_t revents, void * } assert(len > 0); - log_debug("Recording that the buffer got %ld more bytes full.", len); + log_debug("Recording that the buffer got %zd more bytes full.", len); c->buffer_filled_len += len; - log_debug("Buffer now has %ld bytes full.", c->buffer_filled_len); + log_debug("Buffer now has %zu bytes full.", c->buffer_filled_len); } /* Try sending the data immediately. */ @@ -276,7 +279,7 @@ static int connected_to_server_cb(sd_event_source *s, int fd, uint32_t revents, } c_client_to_server->events = EPOLLIN; -goto finish; + goto finish; fail: free_connection(c_client_to_server); @@ -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,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; } @@ -444,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; } @@ -467,10 +511,6 @@ static int help(void) { return 0; } -static void version(void) { - puts(PACKAGE_STRING " socket-proxyd"); -} - static int parse_argv(int argc, char *argv[], struct proxy *p) { enum { @@ -482,7 +522,7 @@ static int parse_argv(int argc, char *argv[], struct proxy *p) { { "help", no_argument, NULL, 'h' }, { "version", no_argument, NULL, ARG_VERSION }, { "ignore-env", no_argument, NULL, ARG_IGNORE_ENV}, - { NULL, 0, NULL, 0 } + {} }; int c; @@ -495,23 +535,22 @@ static int parse_argv(int argc, char *argv[], struct proxy *p) { switch (c) { case 'h': - help(); - return 0; - - case '?': - return -EINVAL; + return help(); case ARG_VERSION: - version(); + puts(PACKAGE_STRING); + puts(SYSTEMD_FEATURES); return 0; case ARG_IGNORE_ENV: p->ignore_env = true; continue; - default: - log_error("Unknown option code %c", c); + case '?': return -EINVAL; + + default: + assert_not_reached("Unhandled option"); } }