From: Christian Grothoff Date: Wed, 23 Jun 2010 11:58:33 +0000 (+0000) Subject: support for systemd style listen fd passing X-Git-Tag: initial-import-from-subversion-38251~21262 X-Git-Url: https://git.librecmc.org/?a=commitdiff_plain;h=fd9eec78a3c275401d4b661a49cd90b972c9f58d;p=oweals%2Fgnunet.git support for systemd style listen fd passing --- diff --git a/src/include/gnunet_network_lib.h b/src/include/gnunet_network_lib.h index 1666abc90..d7d3acbf0 100644 --- a/src/include/gnunet_network_lib.h +++ b/src/include/gnunet_network_lib.h @@ -71,6 +71,16 @@ struct GNUNET_NETWORK_Handle *GNUNET_NETWORK_socket_accept (const struct address_len); +/** + * Box a native socket (and check that it is a socket). + * + * @param fd socket to box + * @return NULL on error (including not supported on target platform) + */ +struct GNUNET_NETWORK_Handle * +GNUNET_NETWORK_socket_box_native (int fd); + + /** * Bind to a connected socket * diff --git a/src/include/gnunet_server_lib.h b/src/include/gnunet_server_lib.h index 6dd756a61..9649b1776 100644 --- a/src/include/gnunet_server_lib.h +++ b/src/include/gnunet_server_lib.h @@ -108,6 +108,29 @@ struct GNUNET_SERVER_MessageHandler }; +/** + * Create a new server. + * + * @param sched scheduler to use + * @param access function for access control + * @param access_cls closure for access + * @param lsocks NULL-terminated array of listen sockets + * @param maxbuf maximum write buffer size for accepted sockets + * @param idle_timeout after how long should we timeout idle connections? + * @param require_found if YES, connections sending messages of unknown type + * will be closed + * @return handle for the new server, NULL on error + * (typically, "port" already in use) + */ +struct GNUNET_SERVER_Handle * +GNUNET_SERVER_create_with_sockets (struct GNUNET_SCHEDULER_Handle *sched, + GNUNET_CONNECTION_AccessCheck access, void *access_cls, + struct GNUNET_NETWORK_Handle **lsocks, + size_t maxbuf, + struct GNUNET_TIME_Relative + idle_timeout, + int require_found); + /** * Create a new server. * diff --git a/src/util/network.c b/src/util/network.c index 13ed345bf..4a287ada9 100644 --- a/src/util/network.c +++ b/src/util/network.c @@ -307,6 +307,29 @@ GNUNET_NETWORK_socket_close (struct GNUNET_NETWORK_Handle *desc) } +/** + * Box a native socket (and check that it is a socket). + * + * @param fd socket to box + * @return NULL on error (including not supported on target platform) + */ +struct GNUNET_NETWORK_Handle * +GNUNET_NETWORK_socket_box_native (int fd) +{ +#if MINGW + return NULL; +#else + struct GNUNET_NETWORK_Handle *ret; + + if (fcntl (fd, F_GETFD) < 0) + return NULL; /* invalid FD */ + ret = GNUNET_malloc (sizeof (struct GNUNET_NETWORK_Handle)); + ret->fd = fd; + return ret; +#endif +} + + /** * Connect a socket * @param desc socket diff --git a/src/util/server.c b/src/util/server.c index 940aad363..d551cbbd3 100644 --- a/src/util/server.c +++ b/src/util/server.c @@ -389,6 +389,60 @@ open_listen_socket (const struct sockaddr *serverAddr, socklen_t socklen) } +/** + * Create a new server. + * + * @param sched scheduler to use + * @param access function for access control + * @param access_cls closure for access + * @param lsocks NULL-terminated array of listen sockets + * @param maxbuf maximum write buffer size for accepted sockets + * @param idle_timeout after how long should we timeout idle connections? + * @param require_found if YES, connections sending messages of unknown type + * will be closed + * @return handle for the new server, NULL on error + * (typically, "port" already in use) + */ +struct GNUNET_SERVER_Handle * +GNUNET_SERVER_create_with_sockets (struct GNUNET_SCHEDULER_Handle *sched, + GNUNET_CONNECTION_AccessCheck access, void *access_cls, + struct GNUNET_NETWORK_Handle **lsocks, + size_t maxbuf, + struct GNUNET_TIME_Relative + idle_timeout, + int require_found) +{ + struct GNUNET_SERVER_Handle *ret; + struct GNUNET_NETWORK_FDSet *r; + int i; + + ret = GNUNET_malloc (sizeof (struct GNUNET_SERVER_Handle)); + ret->sched = sched; + ret->maxbuf = maxbuf; + ret->idle_timeout = idle_timeout; + ret->listen_sockets = lsocks; + ret->access = access; + ret->access_cls = access_cls; + ret->require_found = require_found; + if (lsocks != NULL) + { + r = GNUNET_NETWORK_fdset_create (); + i = 0; + while (NULL != ret->listen_sockets[i]) + GNUNET_NETWORK_fdset_set (r, ret->listen_sockets[i++]); + ret->listen_task = GNUNET_SCHEDULER_add_select (sched, + GNUNET_SCHEDULER_PRIORITY_HIGH, + GNUNET_SCHEDULER_NO_TASK, + GNUNET_TIME_UNIT_FOREVER_REL, + r, NULL, + &process_listen_socket, + ret); + GNUNET_NETWORK_fdset_destroy (r); + } + return ret; +} + + /** * Create a new server. * @@ -414,9 +468,7 @@ GNUNET_SERVER_create (struct GNUNET_SCHEDULER_Handle *sched, struct GNUNET_TIME_Relative idle_timeout, int require_found) { - struct GNUNET_SERVER_Handle *ret; struct GNUNET_NETWORK_Handle **lsocks; - struct GNUNET_NETWORK_FDSet *r; unsigned int i; unsigned int j; @@ -448,30 +500,12 @@ GNUNET_SERVER_create (struct GNUNET_SCHEDULER_Handle *sched, { lsocks = NULL; } - ret = GNUNET_malloc (sizeof (struct GNUNET_SERVER_Handle)); - ret->sched = sched; - ret->maxbuf = maxbuf; - ret->idle_timeout = idle_timeout; - ret->listen_sockets = lsocks; - ret->access = access; - ret->access_cls = access_cls; - ret->require_found = require_found; - if (lsocks != NULL) - { - r = GNUNET_NETWORK_fdset_create (); - i = 0; - while (NULL != ret->listen_sockets[i]) - GNUNET_NETWORK_fdset_set (r, ret->listen_sockets[i++]); - ret->listen_task = GNUNET_SCHEDULER_add_select (sched, - GNUNET_SCHEDULER_PRIORITY_HIGH, - GNUNET_SCHEDULER_NO_TASK, - GNUNET_TIME_UNIT_FOREVER_REL, - r, NULL, - &process_listen_socket, - ret); - GNUNET_NETWORK_fdset_destroy (r); - } - return ret; + return GNUNET_SERVER_create_with_sockets (sched, + access, access_cls, + lsocks, + maxbuf, + idle_timeout, + require_found); } diff --git a/src/util/service.c b/src/util/service.c index cd27380ee..b45a736ae 100644 --- a/src/util/service.c +++ b/src/util/service.c @@ -435,7 +435,8 @@ struct GNUNET_SERVICE_Context struct GNUNET_SCHEDULER_Handle *sched; /** - * NULL-terminated array of addresses to bind to. + * NULL-terminated array of addresses to bind to, NULL if we got pre-bound + * listen sockets. */ struct sockaddr **addrs; @@ -482,6 +483,16 @@ struct GNUNET_SERVICE_Context */ struct GNUNET_SERVER_MessageHandler *my_handlers; + /** + * Array of the lengths of the entries in addrs. + */ + socklen_t * addrlens; + + /** + * NULL-terminated array of listen sockets we should take over. + */ + struct GNUNET_NETWORK_Handle **lsocks; + /** * Idle timeout for server. */ @@ -515,11 +526,6 @@ struct GNUNET_SERVICE_Context */ enum GNUNET_SERVICE_Options options; - /** - * Array of the lengths of the entries in addrs. - */ - socklen_t * addrlens; - }; @@ -1085,6 +1091,11 @@ setup_service (struct GNUNET_SERVICE_Context *sctx) unsigned long long maxbuf; struct GNUNET_TIME_Relative idleout; int tolerant; + const char *lpid; + unsigned int pid; + const char *nfds; + unsigned int cnt; + int flags; if (GNUNET_CONFIGURATION_have_value (sctx->cfg, sctx->serviceName, "TIMEOUT")) @@ -1140,11 +1151,45 @@ setup_service (struct GNUNET_SERVICE_Context *sctx) else tolerant = GNUNET_NO; - if (GNUNET_SYSERR == - GNUNET_SERVICE_get_server_addresses (sctx->serviceName, - sctx->cfg, - &sctx->addrs, - &sctx->addrlens)) +#ifndef MINGW + errno = 0; + if ( (NULL != (lpid = getenv ("LISTEN_PID"))) && + (1 == sscanf ("%u", lpid, &pid)) && + (getpid () == (pid_t) pid) && + (NULL != (nfds = getenv ("LISTEN_FDS"))) && + (1 == sscanf ("%u", nfds, &cnt)) && + (cnt > 0) ) + { + sctx->lsocks = GNUNET_malloc (sizeof(struct GNUNET_NETWORK_Handle*) * (cnt+1)); + while (0 < cnt--) + { + flags = fcntl (3 + cnt, F_GETFD); + if ( (flags < 0) || + (0 != (flags & FD_CLOEXEC)) || + (NULL == (sctx->lsocks[cnt] = GNUNET_NETWORK_socket_box_native (3 + cnt))) ) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + _("Could not access pre-bound socket %u, will try to bind myself\n"), + (unsigned int) 3 +cnt); + cnt++; + while (sctx->lsocks[cnt] != NULL) + GNUNET_NETWORK_socket_close (sctx->lsocks[cnt++]); + GNUNET_free (sctx->lsocks); + sctx->lsocks = NULL; + break; + } + } + unsetenv ("LISTEN_PID"); + unsetenv ("LISTEN_FDS"); + } +#endif + + if ( (sctx->lsocks == NULL) && + (GNUNET_SYSERR == + GNUNET_SERVICE_get_server_addresses (sctx->serviceName, + sctx->cfg, + &sctx->addrs, + &sctx->addrlens)) ) return GNUNET_SYSERR; sctx->require_found = tolerant ? GNUNET_NO : GNUNET_YES; sctx->maxbuf = (size_t) maxbuf; @@ -1265,23 +1310,34 @@ service_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) unsigned int i; sctx->sched = tc->sched; - sctx->server = GNUNET_SERVER_create (tc->sched, - &check_access, - sctx, - sctx->addrs, - sctx->addrlens, - sctx->maxbuf, - sctx->timeout, sctx->require_found); + if (sctx->lsocks != NULL) + sctx->server = GNUNET_SERVER_create_with_sockets (tc->sched, + &check_access, + sctx, + sctx->lsocks, + sctx->maxbuf, + sctx->timeout, sctx->require_found); + else + sctx->server = GNUNET_SERVER_create (tc->sched, + &check_access, + sctx, + sctx->addrs, + sctx->addrlens, + sctx->maxbuf, + sctx->timeout, sctx->require_found); if (sctx->server == NULL) { - i = 0; - while (sctx->addrs[i] != NULL) + if (sctx->addrs != NULL) { - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - _("Failed to start `%s' at `%s'\n"), - sctx->serviceName, - GNUNET_a2s (sctx->addrs[i], sctx->addrlens[i])); - i++; + i = 0; + while (sctx->addrs[i] != NULL) + { + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + _("Failed to start `%s' at `%s'\n"), + sctx->serviceName, + GNUNET_a2s (sctx->addrs[i], sctx->addrlens[i])); + i++; + } } sctx->ret = GNUNET_SYSERR; return; @@ -1307,14 +1363,17 @@ service_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) sctx->ready_confirm_fd = -1; write_pid_file (sctx, getpid ()); } - i = 0; - while (sctx->addrs[i] != NULL) + if (sctx->addrs != NULL) { - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - _("Service `%s' runs at %s\n"), - sctx->serviceName, - GNUNET_a2s (sctx->addrs[i], sctx->addrlens[i])); - i++; + i = 0; + while (sctx->addrs[i] != NULL) + { + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + _("Service `%s' runs at %s\n"), + sctx->serviceName, + GNUNET_a2s (sctx->addrs[i], sctx->addrlens[i])); + i++; + } } sctx->task (sctx->task_cls, tc->sched, sctx->server, sctx->cfg); } @@ -1594,15 +1653,29 @@ GNUNET_SERVICE_start (const char *serviceName, sctx->sched = sched; /* setup subsystems */ - if ((GNUNET_OK != setup_service (sctx)) || - (NULL == (sctx->server = GNUNET_SERVER_create (sched, - &check_access, - sctx, - sctx->addrs, - sctx->addrlens, - sctx->maxbuf, - sctx->timeout, - sctx->require_found)))) + if (GNUNET_OK != setup_service (sctx)) + { + GNUNET_SERVICE_stop (sctx); + return NULL; + } + if (sctx->lsocks != NULL) + sctx->server = GNUNET_SERVER_create_with_sockets (sched, + &check_access, + sctx, + sctx->lsocks, + sctx->maxbuf, + sctx->timeout, sctx->require_found); + else + sctx->server = GNUNET_SERVER_create (sched, + &check_access, + sctx, + sctx->addrs, + sctx->addrlens, + sctx->maxbuf, + sctx->timeout, + sctx->require_found); + + if (NULL == sctx->server) { GNUNET_SERVICE_stop (sctx); return NULL; @@ -1642,10 +1715,13 @@ GNUNET_SERVICE_stop (struct GNUNET_SERVICE_Context *sctx) if (NULL != sctx->server) GNUNET_SERVER_destroy (sctx->server); GNUNET_free_non_null (sctx->my_handlers); - i = 0; - while (sctx->addrs[i] != NULL) - GNUNET_free (sctx->addrs[i++]); - GNUNET_free (sctx->addrs); + if (sctx->addrs != NULL) + { + i = 0; + while (sctx->addrs[i] != NULL) + GNUNET_free (sctx->addrs[i++]); + GNUNET_free (sctx->addrs); + } GNUNET_free_non_null (sctx->addrlens); GNUNET_free_non_null (sctx->v4_denied); GNUNET_free_non_null (sctx->v6_denied);