From 13a7da212738f9c76c0772ca7726f91885e588d2 Mon Sep 17 00:00:00 2001 From: Christian Grothoff Date: Wed, 23 Jun 2010 14:23:50 +0000 Subject: listen socket passing support for ARM --- ChangeLog | 5 ++ src/arm/arm_api.c | 3 +- src/arm/do_start_process.c | 6 ++- src/arm/gnunet-service-arm.c | 34 ++++++++----- src/arm/gnunet-service-arm.h | 13 ++++- src/arm/gnunet-service-arm_interceptor.c | 86 ++++++++++++++++++++++++++++---- src/include/gnunet_network_lib.h | 1 + src/include/gnunet_os_lib.h | 5 +- src/util/os_priority.c | 74 ++++++++++++++++++++++++++- 9 files changed, 196 insertions(+), 31 deletions(-) diff --git a/ChangeLog b/ChangeLog index 861d8bd25..2761b94d9 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,8 @@ +Wed Jun 23 16:34:38 CEST 2010 + Added support for systemd-compatible passing of listen-sockets + by ARM to services as well as systemd compatibility for gnunet-service-arm + itself. At least for non-MINGW systems this should work. + Sat Jun 5 18:08:39 CEST 2010 Added support for UNIX domain sockets, code also defaults to them when available. diff --git a/src/arm/arm_api.c b/src/arm/arm_api.c index 083031e78..6c2b53a71 100644 --- a/src/arm/arm_api.c +++ b/src/arm/arm_api.c @@ -434,7 +434,8 @@ arm_service_report (void *cls, GNUNET_free (lopostfix); return; } - pid = do_start_process (loprefix, + pid = do_start_process (NULL, + loprefix, binary, "-c", config, #if DEBUG_ARM diff --git a/src/arm/do_start_process.c b/src/arm/do_start_process.c index 357d39920..d616ac2b7 100644 --- a/src/arm/do_start_process.c +++ b/src/arm/do_start_process.c @@ -7,12 +7,14 @@ * limitation that it does NOT allow passing command line arguments * with spaces to the new process. * + * @param lsocks array of listen sockets to dup starting at fd3 (systemd-style), or NULL * @param first_arg first argument for argv (may be an empty string) * @param ... more arguments, NULL terminated * @return PID of the started process, -1 on error */ static pid_t -do_start_process (const char *first_arg, ...) +do_start_process (const int *lsocks, + const char *first_arg, ...) { va_list ap; char **argv; @@ -86,7 +88,7 @@ do_start_process (const char *first_arg, ...) while (NULL != (arg = (va_arg (ap, const char*)))); va_end (ap); argv[argv_size] = NULL; - pid = GNUNET_OS_start_process_v (argv[0], argv); + pid = GNUNET_OS_start_process_v (lsocks, argv[0], argv); while (argv_size > 0) GNUNET_free (argv[--argv_size]); GNUNET_free (argv); diff --git a/src/arm/gnunet-service-arm.c b/src/arm/gnunet-service-arm.c index ab96906a7..59f48a58c 100644 --- a/src/arm/gnunet-service-arm.c +++ b/src/arm/gnunet-service-arm.c @@ -344,7 +344,8 @@ free_entry (struct ServiceList *pos) * @param sl identifies service to start */ static void -start_process (struct ServiceList *sl) +start_process (struct ServiceList *sl, + const int *lsocks) { char *loprefix; char *options; @@ -415,14 +416,16 @@ start_process (struct ServiceList *sl) sl->name, sl->binary, sl->config); #endif if (GNUNET_YES == use_debug) - sl->pid = do_start_process (loprefix, + sl->pid = do_start_process (lsocks, + loprefix, sl->binary, "-c", sl->config, "-L", "DEBUG", options, NULL); else - sl->pid = do_start_process (loprefix, + sl->pid = do_start_process (lsocks, + loprefix, sl->binary, "-c", sl->config, options, @@ -442,9 +445,13 @@ start_process (struct ServiceList *sl) * * @param client who is asking for this * @param servicename name of the service to start + * @param lsocks -1 terminated list of listen sockets to pass (systemd style), or NULL + * @return GNUNET_OK on success, GNUNET_SYSERR on error */ -void -start_service (struct GNUNET_SERVER_Client *client, const char *servicename) +int +start_service (struct GNUNET_SERVER_Client *client, + const char *servicename, + const int *lsocks) { struct ServiceList *sl; char *binary; @@ -457,7 +464,7 @@ start_service (struct GNUNET_SERVER_Client *client, const char *servicename) _("ARM is shutting down, service `%s' not started.\n"), servicename); signal_result (client, servicename, GNUNET_MESSAGE_TYPE_ARM_IS_DOWN); - return; + return GNUNET_SYSERR; } sl = find_name (servicename); if (sl != NULL) @@ -467,7 +474,7 @@ start_service (struct GNUNET_SERVER_Client *client, const char *servicename) sl->next = running; running = sl; signal_result (client, servicename, GNUNET_MESSAGE_TYPE_ARM_IS_UP); - return; + return GNUNET_SYSERR; } if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_string (cfg, @@ -477,7 +484,7 @@ start_service (struct GNUNET_SERVER_Client *client, const char *servicename) _("Binary implementing service `%s' not known!\n"), servicename); signal_result (client, servicename, GNUNET_MESSAGE_TYPE_ARM_IS_DOWN); - return; + return GNUNET_SYSERR; } if ((GNUNET_OK != GNUNET_CONFIGURATION_get_value_filename (cfg, @@ -492,7 +499,7 @@ start_service (struct GNUNET_SERVER_Client *client, const char *servicename) signal_result (client, servicename, GNUNET_MESSAGE_TYPE_ARM_IS_DOWN); GNUNET_free (binary); GNUNET_free_non_null (config); - return; + return GNUNET_SYSERR; } (void) stop_listening (servicename); sl = GNUNET_malloc (sizeof (struct ServiceList)); @@ -505,9 +512,10 @@ start_service (struct GNUNET_SERVER_Client *client, const char *servicename) sl->restartAt = GNUNET_TIME_UNIT_FOREVER_ABS; running = sl; - start_process (sl); + start_process (sl, lsocks); if (NULL != client) signal_result (client, servicename, GNUNET_MESSAGE_TYPE_ARM_IS_UP); + return GNUNET_OK; } @@ -610,7 +618,7 @@ handle_start (void *cls, GNUNET_SERVER_receive_done (client, GNUNET_SYSERR); return; } - start_service (client, servicename); + start_service (client, servicename, NULL); GNUNET_SERVER_receive_done (client, GNUNET_OK); } @@ -776,7 +784,7 @@ delayed_restart_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) { GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("Restarting service `%s'.\n"), pos->name); - start_process (pos); + start_process (pos, NULL); } else { @@ -1071,7 +1079,7 @@ run (void *cls, pos = strtok (defaultservices, " "); while (pos != NULL) { - start_service (NULL, pos); + start_service (NULL, pos, NULL); pos = strtok (NULL, " "); } } diff --git a/src/arm/gnunet-service-arm.h b/src/arm/gnunet-service-arm.h index 77dfb8c6c..632b46b09 100644 --- a/src/arm/gnunet-service-arm.h +++ b/src/arm/gnunet-service-arm.h @@ -27,8 +27,17 @@ #ifndef GNUNET_SERVICE_ARM__H #define GNUNET_SERVICE_ARM__H -void start_service (struct GNUNET_SERVER_Client *client, - const char *servicename); +/** + * Start the specified service. + * + * @param client who is asking for this + * @param servicename name of the service to start + * @param lsocks -1 terminated list of listen sockets to pass (systemd style), or NULL + * @return GNUNET_OK on success + */ +int start_service (struct GNUNET_SERVER_Client *client, + const char *servicename, + const int *lsocks); /** * Stop listening for connections to a service. diff --git a/src/arm/gnunet-service-arm_interceptor.c b/src/arm/gnunet-service-arm_interceptor.c index 9380a8deb..bc8db396b 100644 --- a/src/arm/gnunet-service-arm_interceptor.c +++ b/src/arm/gnunet-service-arm_interceptor.c @@ -847,7 +847,6 @@ stop_listening (const char *serviceName) return ret; } - /** * First connection has come to the listening socket associated with the service, * create the service in order to relay the incoming connection to it @@ -856,14 +855,15 @@ stop_listening (const char *serviceName) * @param tc context */ static void -acceptConnection (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) +acceptConnection (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc); + + +#if MINGW +static void +accept_and_forward (struct ServiceListeningInfo *serviceListeningInfo) { - struct ServiceListeningInfo *serviceListeningInfo = cls; struct ForwardedConnection *fc; - serviceListeningInfo->acceptTask = GNUNET_SCHEDULER_NO_TASK; - if (0 != (GNUNET_SCHEDULER_REASON_SHUTDOWN & tc->reason)) - return; fc = GNUNET_malloc (sizeof (struct ForwardedConnection)); fc->listen_info = serviceListeningInfo; fc->service_to_client_bufferPos = fc->service_to_client_buffer; @@ -879,6 +879,9 @@ acceptConnection (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) serviceListeningInfo->serviceName, STRERROR (errno)); GNUNET_free (fc); + GNUNET_CONTAINER_DLL_insert (serviceListeningInfoList_head, + serviceListeningInfoList_tail, + serviceListeningInfo); serviceListeningInfo->acceptTask = GNUNET_SCHEDULER_add_read_net (scheduler, GNUNET_TIME_UNIT_FOREVER_REL, @@ -889,10 +892,7 @@ acceptConnection (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) } GNUNET_break (GNUNET_OK == GNUNET_NETWORK_socket_close (serviceListeningInfo->listeningSocket)); - GNUNET_CONTAINER_DLL_remove (serviceListeningInfoList_head, - serviceListeningInfoList_tail, - serviceListeningInfo); - start_service (NULL, serviceListeningInfo->serviceName); + start_service (NULL, serviceListeningInfo->serviceName, NULL); GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("Service `%s' started\n"), fc->listen_info->serviceName); @@ -909,6 +909,72 @@ acceptConnection (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) &start_forwarding, fc); } +#endif + + +/** + * First connection has come to the listening socket associated with the service, + * create the service in order to relay the incoming connection to it + * + * @param cls callback data, struct ServiceListeningInfo describing a listen socket + * @param tc context + */ +static void +acceptConnection (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) +{ + struct ServiceListeningInfo *sli = cls; + struct ServiceListeningInfo *pos; + struct ServiceListeningInfo *next; + int *lsocks; + unsigned int ls; + + sli->acceptTask = GNUNET_SCHEDULER_NO_TASK; + if (0 != (GNUNET_SCHEDULER_REASON_SHUTDOWN & tc->reason)) + return; + GNUNET_CONTAINER_DLL_remove (serviceListeningInfoList_head, + serviceListeningInfoList_tail, + sli); +#ifndef MINGW + lsocks = NULL; + ls = 0; + next = serviceListeningInfoList_head; + while (NULL != (pos = next)) + { + next = pos->next; + if (0 == strcmp (pos->serviceName, + sli->serviceName)) + { + GNUNET_array_append (lsocks, ls, + GNUNET_NETWORK_get_fd (pos->listeningSocket)); + GNUNET_free (pos->listeningSocket); /* deliberately no closing! */ + GNUNET_free (pos->service_addr); + GNUNET_free (pos->serviceName); + GNUNET_SCHEDULER_cancel (scheduler, + pos->acceptTask); + GNUNET_CONTAINER_DLL_remove (serviceListeningInfoList_head, + serviceListeningInfoList_tail, + pos); + GNUNET_free (pos); + } + } + GNUNET_array_append (lsocks, ls, + GNUNET_NETWORK_get_fd (sli->listeningSocket)); + GNUNET_free (sli->listeningSocket); /* deliberately no closing! */ + GNUNET_free (sli->service_addr); + GNUNET_array_append (lsocks, ls, -1); + start_service (NULL, + sli->serviceName, + lsocks); + ls = 0; + while (lsocks[ls] != -1) + GNUNET_break (0 == close (lsocks[ls++])); + GNUNET_array_grow (lsocks, ls, 0); + GNUNET_free (sli->serviceName); + GNUNET_free (sli); +#else + accept_and_forward (sli); +#endif +} /** diff --git a/src/include/gnunet_network_lib.h b/src/include/gnunet_network_lib.h index d7d3acbf0..19fd234ee 100644 --- a/src/include/gnunet_network_lib.h +++ b/src/include/gnunet_network_lib.h @@ -199,6 +199,7 @@ int GNUNET_NETWORK_socket_select (struct GNUNET_NETWORK_FDSet *rfds, ssize_t GNUNET_NETWORK_socket_send (const struct GNUNET_NETWORK_Handle *desc, const void *buffer, size_t length); + /** * Send data to a particular destination (always non-blocking). * This function only works for UDP sockets. diff --git a/src/include/gnunet_os_lib.h b/src/include/gnunet_os_lib.h index 3aa1f1bad..87d9a8fb1 100644 --- a/src/include/gnunet_os_lib.h +++ b/src/include/gnunet_os_lib.h @@ -216,12 +216,15 @@ GNUNET_OS_start_process (struct GNUNET_DISK_PipeHandle *pipe_stdin, /** * Start a process. * + * @param lsocks array of listen sockets to dup systemd-style (or NULL); + * must be NULL on platforms where dup is not supported * @param filename name of the binary * @param argv NULL-terminated list of arguments to the process, * including the process name as the first argument * @return process ID of the new process, -1 on error */ -pid_t GNUNET_OS_start_process_v (const char *filename, char *const argv[]); +pid_t GNUNET_OS_start_process_v (const int *lsocks, + const char *filename, char *const argv[]); /** diff --git a/src/util/os_priority.c b/src/util/os_priority.c index b354ad995..7a6f0d65d 100644 --- a/src/util/os_priority.c +++ b/src/util/os_priority.c @@ -329,16 +329,43 @@ GNUNET_OS_start_process (struct GNUNET_DISK_PipeHandle *pipe_stdin, /** * Start a process. * + * @param lsocks array of listen sockets to dup systemd-style (or NULL); + * must be NULL on platforms where dup is not supported * @param filename name of the binary * @param argv NULL-terminated list of arguments to the process * @return process ID of the new process, -1 on error */ pid_t -GNUNET_OS_start_process_v (const char *filename, char *const argv[]) +GNUNET_OS_start_process_v (const int *lsocks, + const char *filename, char *const argv[]) { #ifndef MINGW pid_t ret; - + char lpid[16]; + char fds[16]; + int i; + int j; + int k; + int tgt; + int flags; + int *lscp; + unsigned int ls; + + lscp = NULL; + ls = 0; + if (lsocks != NULL) + { + i = 0; + while (-1 != (k = lsocks[i++])) + { + flags = fcntl (k, F_GETFD); + GNUNET_assert (flags >= 0); + flags &= ~FD_CLOEXEC; + (void) fcntl (k, F_SETFD, flags); + GNUNET_array_append (lscp, ls, k); + } + GNUNET_array_append (lscp, ls, -1); + } #if HAVE_WORKING_VFORK ret = vfork (); #else @@ -366,6 +393,48 @@ GNUNET_OS_start_process_v (const char *filename, char *const argv[]) } return ret; } + if (lscp != NULL) + { + /* read systemd documentation... */ + GNUNET_snprintf (lpid, sizeof (lpid), "%u", getpid()); + setenv ("LISTEN_PID", lpid, 1); + i = 0; + tgt = 3; + while (-1 != lscp[i]) + { + j = i + 1; + while (-1 != lscp[j]) + { + if (lscp[j] == tgt) + { + /* dup away */ + k = dup (lscp[j]); + GNUNET_assert (-1 != k); + GNUNET_assert (0 == close (lscp[j])); + lscp[j] = k; + break; + } + j++; + } + if (lscp[i] != tgt) + { + /* Bury any existing FD, no matter what; they should all be closed + on exec anyway and the important onces have been dup'ed away */ + (void) close (tgt); + GNUNET_assert (-1 != dup2 (lscp[i], tgt)); + } + /* set close-on-exec flag */ + flags = fcntl (tgt, F_GETFD); + GNUNET_assert (flags >= 0); + flags &= ~FD_CLOEXEC; + (void) fcntl (tgt, F_SETFD, flags); + tgt++; + i++; + } + GNUNET_snprintf (fds, sizeof (fds), "%u", i); + setenv ("LISTEN_FDS", fds, 1); + } + GNUNET_array_grow (lscp, ls, 0); execvp (filename, argv); GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, "execvp", filename); _exit (1); @@ -379,6 +448,7 @@ GNUNET_OS_start_process_v (const char *filename, char *const argv[]) char *non_const_filename = NULL; int filenamelen = 0; + GNUNET_assert (lsocks == NULL); /* Count the number of arguments */ arg = argv; while (*arg) -- cgit v1.2.3