X-Git-Url: https://git.librecmc.org/?a=blobdiff_plain;f=src%2Farm%2Fgnunet-service-arm.c;h=19088c5cb7ec57bd526ead12252651986169bba1;hb=03512957fb04969d08fb7eac0952a747aa9596ae;hp=974e983bb489f7f184414a95779fdfa11d5607a4;hpb=3c0c7a5fb4051f40da0501bd6e982ad980c5b039;p=oweals%2Fgnunet.git diff --git a/src/arm/gnunet-service-arm.c b/src/arm/gnunet-service-arm.c index 974e983bb..19088c5cb 100644 --- a/src/arm/gnunet-service-arm.c +++ b/src/arm/gnunet-service-arm.c @@ -1,6 +1,6 @@ /* This file is part of GNUnet. - (C) 2009, 2010, 2011 Christian Grothoff (and other contributing authors) + Copyright (C) 2009-2011, 2015, 2016 GNUnet e.V. GNUnet is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published @@ -14,8 +14,8 @@ You should have received a copy of the GNU General Public License along with GNUnet; see the file COPYING. If not, write to the - Free Software Foundation, Inc., 59 Temple Place - Suite 330, - Boston, MA 02111-1307, USA. + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. */ /** @@ -29,6 +29,24 @@ #include "gnunet_protocols.h" #include "arm.h" +#define LOG(kind,...) GNUNET_log_from (kind, "util", __VA_ARGS__) + +#define LOG_STRERROR(kind,syscall) GNUNET_log_from_strerror (kind, "util", syscall) + + +#if HAVE_WAIT4 +/** + * Name of the file for writing resource utilization summaries to. + */ +static char *wait_filename; + +/** + * Handle for the file for writing resource summaries. + */ +static FILE *wait_file; +#endif + + /** * How many messages do we queue up at most for optional * notifications to a client? (this can cause notifications @@ -36,6 +54,7 @@ */ #define MAX_NOTIFY_QUEUE 1024 + /** * List of our services. */ @@ -68,7 +87,7 @@ struct ServiceListeningInfo struct ServiceList *sl; /** - * Number of bytes in 'service_addr' + * Number of bytes in @e service_addr */ socklen_t service_addr_len; @@ -80,7 +99,7 @@ struct ServiceListeningInfo /** * Task doing the accepting. */ - GNUNET_SCHEDULER_TaskIdentifier accept_task; + struct GNUNET_SCHEDULER_Task *accept_task; }; @@ -129,7 +148,7 @@ struct ServiceList * Client to notify upon kill completion (waitpid), NULL * if we should simply restart the process. */ - struct GNUNET_SERVER_Client *killing_client; + struct GNUNET_SERVICE_Client *killing_client; /** * ID of the request that killed the service (for reporting back). @@ -159,10 +178,10 @@ struct ServiceList /** * Is this service to be started by default (or did a client tell us explicitly - * to start it)? GNUNET_NO if the service is started only upon 'accept' on a + * to start it)? #GNUNET_NO if the service is started only upon 'accept' on a * listen socket or possibly explicitly by a client changing the value. */ - int is_default; + int force_start; /** * Should we use pipes to signal this process? (YES for Java binaries and if we @@ -199,13 +218,13 @@ static char *final_option; /** * ID of task called whenever we get a SIGCHILD. */ -static GNUNET_SCHEDULER_TaskIdentifier child_death_task; +static struct GNUNET_SCHEDULER_Task *child_death_task; /** * ID of task called whenever the timeout for restarting a child * expires. */ -static GNUNET_SCHEDULER_TaskIdentifier child_restart_task; +static struct GNUNET_SCHEDULER_Task *child_restart_task; /** * Pipe used to communicate shutdown via signal. @@ -218,91 +237,412 @@ static struct GNUNET_DISK_PipeHandle *sigpipe; static int in_shutdown; /** - * Handle to our server instance. Our server is a bit special in that + * Are we starting user services? + */ +static int start_user = GNUNET_YES; + +/** + * Are we starting system services? + */ +static int start_system = GNUNET_YES; + +/** + * Handle to our service instance. Our service is a bit special in that * its service is not immediately stopped once we get a shutdown * request (since we need to continue service until all of our child - * processes are dead). This handle is used to shut down the server + * processes are dead). This handle is used to shut down the service * (and thus trigger process termination) once all child processes are * also dead. A special option in the ARM configuration modifies the * behaviour of the service implementation to not do the shutdown * immediately. */ -static struct GNUNET_SERVER_Handle *server; +static struct GNUNET_SERVICE_Handle *service; /** * Context for notifications we need to send to our clients. */ -static struct GNUNET_SERVER_NotificationContext *notifier; +static struct GNUNET_NotificationContext *notifier; -#include "do_start_process.c" - /** - * Transmit a status result message. + * Add the given UNIX domain path as an address to the + * list (as the first entry). * - * @param cls pointer to "unit16_t*" with message type - * @param size number of bytes available in buf - * @param buf where to copy the message, NULL on error - * @return number of bytes copied to buf + * @param saddrs array to update + * @param saddrlens where to store the address length + * @param unixpath path to add + * @param abstract #GNUNET_YES to add an abstract UNIX domain socket. This + * parameter is ignore on systems other than LINUX */ -static size_t -write_result (void *cls, size_t size, void *buf) +static void +add_unixpath (struct sockaddr **saddrs, + socklen_t *saddrlens, + const char *unixpath, + int abstract) { - struct GNUNET_ARM_ResultMessage *msg = cls; - size_t msize; +#ifdef AF_UNIX + struct sockaddr_un *un; - if (buf == NULL) - { - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - _("Could not send status result to client\n")); - GNUNET_free (msg); - return 0; /* error, not much we can do */ - } - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Sending status response %u to client\n", (unsigned int) msg->result); - msize = msg->arm_msg.header.size; - GNUNET_assert (size >= msize); - msg->arm_msg.header.size = htons (msg->arm_msg.header.size); - msg->arm_msg.header.type = htons (msg->arm_msg.header.type); - msg->result = htonl (msg->result); - msg->arm_msg.request_id = GNUNET_htonll (msg->arm_msg.request_id); - memcpy (buf, msg, msize); - GNUNET_free (msg); - return msize; + un = GNUNET_new (struct sockaddr_un); + un->sun_family = AF_UNIX; + strncpy (un->sun_path, unixpath, sizeof (un->sun_path) - 1); +#ifdef LINUX + if (GNUNET_YES == abstract) + un->sun_path[0] = '\0'; +#endif +#if HAVE_SOCKADDR_UN_SUN_LEN + un->sun_len = (u_char) sizeof (struct sockaddr_un); +#endif + *saddrs = (struct sockaddr *) un; + *saddrlens = sizeof (struct sockaddr_un); +#else + /* this function should never be called + * unless AF_UNIX is defined! */ + GNUNET_assert (0); +#endif } + /** - * Transmit the list of running services. - * - * @param cls pointer to struct GNUNET_ARM_ListResultMessage with the message - * @param size number of bytes available in buf - * @param buf where to copy the message, NULL on error - * @return number of bytes copied to buf + * Get the list of addresses that a server for the given service + * should bind to. + * + * @param service_name name of the service + * @param cfg configuration (which specifies the addresses) + * @param addrs set (call by reference) to an array of pointers to the + * addresses the server should bind to and listen on; the + * array will be NULL-terminated (on success) + * @param addr_lens set (call by reference) to an array of the lengths + * of the respective `struct sockaddr` struct in the @a addrs + * array (on success) + * @return number of addresses found on success, + * #GNUNET_SYSERR if the configuration + * did not specify reasonable finding information or + * if it specified a hostname that could not be resolved; + * #GNUNET_NO if the number of addresses configured is + * zero (in this case, `*addrs` and `*addr_lens` will be + * set to NULL). */ -static size_t -write_list_result (void *cls, size_t size, void *buf) +static int +get_server_addresses (const char *service_name, + const struct GNUNET_CONFIGURATION_Handle *cfg, + struct sockaddr ***addrs, + socklen_t ** addr_lens) { - struct GNUNET_ARM_ListResultMessage *msg = cls; - size_t rslt_size; - - if (buf == NULL) + int disablev6; + struct GNUNET_NETWORK_Handle *desc; + unsigned long long port; + char *unixpath; + struct addrinfo hints; + struct addrinfo *res; + struct addrinfo *pos; + struct addrinfo *next; + unsigned int i; + int resi; + int ret; + int abstract; + struct sockaddr **saddrs; + socklen_t *saddrlens; + char *hostname; + + *addrs = NULL; + *addr_lens = NULL; + desc = NULL; + if (GNUNET_CONFIGURATION_have_value (cfg, service_name, "DISABLEV6")) { - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - _("Could not send list result to client\n")); - GNUNET_free (msg); - return 0; /* error, not much we can do */ + if (GNUNET_SYSERR == + (disablev6 = + GNUNET_CONFIGURATION_get_value_yesno (cfg, service_name, "DISABLEV6"))) + return GNUNET_SYSERR; } - - rslt_size = msg->arm_msg.header.size; - GNUNET_assert (size >= rslt_size); - msg->arm_msg.header.size = htons (msg->arm_msg.header.size); - msg->arm_msg.header.type = htons (msg->arm_msg.header.type); - msg->arm_msg.request_id = GNUNET_htonll (msg->arm_msg.request_id); - msg->count = htons (msg->count); - - memcpy (buf, msg, rslt_size); - GNUNET_free (msg); - return rslt_size; + else + disablev6 = GNUNET_NO; + + if (! disablev6) + { + /* probe IPv6 support */ + desc = GNUNET_NETWORK_socket_create (PF_INET6, SOCK_STREAM, 0); + if (NULL == desc) + { + if ((ENOBUFS == errno) || (ENOMEM == errno) || (ENFILE == errno) || + (EACCES == errno)) + { + LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "socket"); + return GNUNET_SYSERR; + } + LOG (GNUNET_ERROR_TYPE_INFO, + _("Disabling IPv6 support for service `%s', failed to create IPv6 socket: %s\n"), + service_name, STRERROR (errno)); + disablev6 = GNUNET_YES; + } + else + { + GNUNET_break (GNUNET_OK == GNUNET_NETWORK_socket_close (desc)); + desc = NULL; + } + } + + port = 0; + if (GNUNET_CONFIGURATION_have_value (cfg, service_name, "PORT")) + { + if (GNUNET_OK != + GNUNET_CONFIGURATION_get_value_number (cfg, service_name, + "PORT", &port)) + { + LOG (GNUNET_ERROR_TYPE_ERROR, + _("Require valid port number for service `%s' in configuration!\n"), + service_name); + } + if (port > 65535) + { + LOG (GNUNET_ERROR_TYPE_ERROR, + _("Require valid port number for service `%s' in configuration!\n"), + service_name); + return GNUNET_SYSERR; + } + } + + if (GNUNET_CONFIGURATION_have_value (cfg, service_name, "BINDTO")) + { + GNUNET_break (GNUNET_OK == + GNUNET_CONFIGURATION_get_value_string (cfg, service_name, + "BINDTO", &hostname)); + } + else + hostname = NULL; + + unixpath = NULL; + abstract = GNUNET_NO; +#ifdef AF_UNIX + if ((GNUNET_YES == + GNUNET_CONFIGURATION_have_value (cfg, service_name, "UNIXPATH")) && + (GNUNET_OK == + GNUNET_CONFIGURATION_get_value_filename (cfg, service_name, "UNIXPATH", + &unixpath)) && + (0 < strlen (unixpath))) + { + /* probe UNIX support */ + struct sockaddr_un s_un; + + if (strlen (unixpath) >= sizeof (s_un.sun_path)) + { + LOG (GNUNET_ERROR_TYPE_WARNING, + _("UNIXPATH `%s' too long, maximum length is %llu\n"), unixpath, + (unsigned long long) sizeof (s_un.sun_path)); + unixpath = GNUNET_NETWORK_shorten_unixpath (unixpath); + LOG (GNUNET_ERROR_TYPE_INFO, + _("Using `%s' instead\n"), + unixpath); + } +#ifdef LINUX + abstract = GNUNET_CONFIGURATION_get_value_yesno (cfg, + "TESTING", + "USE_ABSTRACT_SOCKETS"); + if (GNUNET_SYSERR == abstract) + abstract = GNUNET_NO; +#endif + if ((GNUNET_YES != abstract) + && (GNUNET_OK != + GNUNET_DISK_directory_create_for_file (unixpath))) + GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, + "mkdir", + unixpath); + } + if (NULL != unixpath) + { + desc = GNUNET_NETWORK_socket_create (AF_UNIX, SOCK_STREAM, 0); + if (NULL == desc) + { + if ((ENOBUFS == errno) || (ENOMEM == errno) || (ENFILE == errno) || + (EACCES == errno)) + { + LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "socket"); + GNUNET_free_non_null (hostname); + GNUNET_free (unixpath); + return GNUNET_SYSERR; + } + LOG (GNUNET_ERROR_TYPE_INFO, + _("Disabling UNIX domain socket support for service `%s', failed to create UNIX domain socket: %s\n"), + service_name, + STRERROR (errno)); + GNUNET_free (unixpath); + unixpath = NULL; + } + else + { + GNUNET_break (GNUNET_OK == GNUNET_NETWORK_socket_close (desc)); + desc = NULL; + } + } +#endif + + if ((0 == port) && (NULL == unixpath)) + { + LOG (GNUNET_ERROR_TYPE_ERROR, + _("Have neither PORT nor UNIXPATH for service `%s', but one is required\n"), + service_name); + GNUNET_free_non_null (hostname); + return GNUNET_SYSERR; + } + if (0 == port) + { + saddrs = GNUNET_malloc (2 * sizeof (struct sockaddr *)); + saddrlens = GNUNET_malloc (2 * sizeof (socklen_t)); + add_unixpath (saddrs, saddrlens, unixpath, abstract); + GNUNET_free_non_null (unixpath); + GNUNET_free_non_null (hostname); + *addrs = saddrs; + *addr_lens = saddrlens; + return 1; + } + + if (NULL != hostname) + { + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Resolving `%s' since that is where `%s' will bind to.\n", + hostname, + service_name); + memset (&hints, 0, sizeof (struct addrinfo)); + if (disablev6) + hints.ai_family = AF_INET; + hints.ai_protocol = IPPROTO_TCP; + if ((0 != (ret = getaddrinfo (hostname, NULL, &hints, &res))) || + (NULL == res)) + { + LOG (GNUNET_ERROR_TYPE_ERROR, + _("Failed to resolve `%s': %s\n"), + hostname, + gai_strerror (ret)); + GNUNET_free (hostname); + GNUNET_free_non_null (unixpath); + return GNUNET_SYSERR; + } + next = res; + i = 0; + while (NULL != (pos = next)) + { + next = pos->ai_next; + if ((disablev6) && (pos->ai_family == AF_INET6)) + continue; + i++; + } + if (0 == i) + { + LOG (GNUNET_ERROR_TYPE_ERROR, + _("Failed to find %saddress for `%s'.\n"), + disablev6 ? "IPv4 " : "", + hostname); + freeaddrinfo (res); + GNUNET_free (hostname); + GNUNET_free_non_null (unixpath); + return GNUNET_SYSERR; + } + resi = i; + if (NULL != unixpath) + resi++; + saddrs = GNUNET_malloc ((resi + 1) * sizeof (struct sockaddr *)); + saddrlens = GNUNET_malloc ((resi + 1) * sizeof (socklen_t)); + i = 0; + if (NULL != unixpath) + { + add_unixpath (saddrs, saddrlens, unixpath, abstract); + i++; + } + next = res; + while (NULL != (pos = next)) + { + next = pos->ai_next; + if ((disablev6) && (AF_INET6 == pos->ai_family)) + continue; + if ((IPPROTO_TCP != pos->ai_protocol) && (0 != pos->ai_protocol)) + continue; /* not TCP */ + if ((SOCK_STREAM != pos->ai_socktype) && (0 != pos->ai_socktype)) + continue; /* huh? */ + LOG (GNUNET_ERROR_TYPE_DEBUG, "Service `%s' will bind to `%s'\n", + service_name, GNUNET_a2s (pos->ai_addr, pos->ai_addrlen)); + if (AF_INET == pos->ai_family) + { + GNUNET_assert (sizeof (struct sockaddr_in) == pos->ai_addrlen); + saddrlens[i] = pos->ai_addrlen; + saddrs[i] = GNUNET_malloc (saddrlens[i]); + GNUNET_memcpy (saddrs[i], pos->ai_addr, saddrlens[i]); + ((struct sockaddr_in *) saddrs[i])->sin_port = htons (port); + } + else + { + GNUNET_assert (AF_INET6 == pos->ai_family); + GNUNET_assert (sizeof (struct sockaddr_in6) == pos->ai_addrlen); + saddrlens[i] = pos->ai_addrlen; + saddrs[i] = GNUNET_malloc (saddrlens[i]); + GNUNET_memcpy (saddrs[i], pos->ai_addr, saddrlens[i]); + ((struct sockaddr_in6 *) saddrs[i])->sin6_port = htons (port); + } + i++; + } + GNUNET_free (hostname); + freeaddrinfo (res); + resi = i; + } + else + { + /* will bind against everything, just set port */ + if (disablev6) + { + /* V4-only */ + resi = 1; + if (NULL != unixpath) + resi++; + i = 0; + saddrs = GNUNET_malloc ((resi + 1) * sizeof (struct sockaddr *)); + saddrlens = GNUNET_malloc ((resi + 1) * sizeof (socklen_t)); + if (NULL != unixpath) + { + add_unixpath (saddrs, saddrlens, unixpath, abstract); + i++; + } + saddrlens[i] = sizeof (struct sockaddr_in); + saddrs[i] = GNUNET_malloc (saddrlens[i]); +#if HAVE_SOCKADDR_IN_SIN_LEN + ((struct sockaddr_in *) saddrs[i])->sin_len = saddrlens[i]; +#endif + ((struct sockaddr_in *) saddrs[i])->sin_family = AF_INET; + ((struct sockaddr_in *) saddrs[i])->sin_port = htons (port); + } + else + { + /* dual stack */ + resi = 2; + if (NULL != unixpath) + resi++; + saddrs = GNUNET_malloc ((resi + 1) * sizeof (struct sockaddr *)); + saddrlens = GNUNET_malloc ((resi + 1) * sizeof (socklen_t)); + i = 0; + if (NULL != unixpath) + { + add_unixpath (saddrs, saddrlens, unixpath, abstract); + i++; + } + saddrlens[i] = sizeof (struct sockaddr_in6); + saddrs[i] = GNUNET_malloc (saddrlens[i]); +#if HAVE_SOCKADDR_IN_SIN_LEN + ((struct sockaddr_in6 *) saddrs[i])->sin6_len = saddrlens[0]; +#endif + ((struct sockaddr_in6 *) saddrs[i])->sin6_family = AF_INET6; + ((struct sockaddr_in6 *) saddrs[i])->sin6_port = htons (port); + i++; + saddrlens[i] = sizeof (struct sockaddr_in); + saddrs[i] = GNUNET_malloc (saddrlens[i]); +#if HAVE_SOCKADDR_IN_SIN_LEN + ((struct sockaddr_in *) saddrs[i])->sin_len = saddrlens[1]; +#endif + ((struct sockaddr_in *) saddrs[i])->sin_family = AF_INET; + ((struct sockaddr_in *) saddrs[i])->sin_port = htons (port); + } + } + GNUNET_free_non_null (unixpath); + *addrs = saddrs; + *addr_lens = saddrlens; + return resi; } @@ -317,21 +657,20 @@ write_list_result (void *cls, size_t size, void *buf) * @return NULL if it was not found */ static void -signal_result (struct GNUNET_SERVER_Client *client, const char *name, - uint64_t request_id, enum GNUNET_ARM_Result result) +signal_result (struct GNUNET_SERVICE_Client *client, + const char *name, + uint64_t request_id, + enum GNUNET_ARM_Result result) { + struct GNUNET_MQ_Envelope *env; struct GNUNET_ARM_ResultMessage *msg; - size_t msize; - msize = sizeof (struct GNUNET_ARM_ResultMessage); - msg = GNUNET_malloc (msize); - msg->arm_msg.header.size = msize; - msg->arm_msg.header.type = GNUNET_MESSAGE_TYPE_ARM_RESULT; - msg->result = result; - msg->arm_msg.request_id = request_id; - - GNUNET_SERVER_notify_transmit_ready (client, msize, - GNUNET_TIME_UNIT_FOREVER_REL, write_result, msg); + env = GNUNET_MQ_msg (msg, + GNUNET_MESSAGE_TYPE_ARM_RESULT); + msg->result = htonl (result); + msg->arm_msg.request_id = GNUNET_htonll (request_id); + GNUNET_MQ_send (GNUNET_SERVICE_client_get_mq (client), + env); } @@ -344,31 +683,39 @@ signal_result (struct GNUNET_SERVER_Client *client, const char *name, * otherwise, send to all clients in the notifier */ static void -broadcast_status (const char *name, enum GNUNET_ARM_ServiceStatus status, - struct GNUNET_SERVER_Client *unicast) +broadcast_status (const char *name, + enum GNUNET_ARM_ServiceStatus status, + struct GNUNET_SERVICE_Client *unicast) { + struct GNUNET_MQ_Envelope *env; struct GNUNET_ARM_StatusMessage *msg; size_t namelen; - if (NULL == notifier) - return; GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Sending status %u of service `%s' to client\n", - (unsigned int) status, name); - namelen = strlen (name); - msg = GNUNET_malloc (sizeof (struct GNUNET_ARM_StatusMessage) + namelen + 1); - msg->header.size = htons (sizeof (struct GNUNET_ARM_StatusMessage) + namelen + 1); - msg->header.type = htons (GNUNET_MESSAGE_TYPE_ARM_STATUS); + "Sending status %u of service `%s' to client\n", + (unsigned int) status, + name); + namelen = strlen (name) + 1; + env = GNUNET_MQ_msg_extra (msg, + namelen, + GNUNET_MESSAGE_TYPE_ARM_STATUS); msg->status = htonl ((uint32_t) (status)); - memcpy ((char *) &msg[1], name, namelen + 1); - + GNUNET_memcpy ((char *) &msg[1], + name, + namelen); if (NULL == unicast) - GNUNET_SERVER_notification_context_broadcast (notifier, - (struct GNUNET_MessageHeader *) msg, GNUNET_YES); + { + if (NULL != notifier) + GNUNET_notification_context_broadcast (notifier, + &msg->header, + GNUNET_YES); + GNUNET_MQ_discard (env); + } else - GNUNET_SERVER_notification_context_unicast (notifier, unicast, - (const struct GNUNET_MessageHeader *) msg, GNUNET_NO); - GNUNET_free (msg); + { + GNUNET_MQ_send (GNUNET_SERVICE_client_get_mq (unicast), + env); + } } @@ -381,20 +728,19 @@ broadcast_status (const char *name, enum GNUNET_ARM_ServiceStatus status, * being started. 0 if starting was not requested. */ static void -start_process (struct ServiceList *sl, struct GNUNET_SERVER_Client *client, uint64_t request_id) +start_process (struct ServiceList *sl, + struct GNUNET_SERVICE_Client *client, + uint64_t request_id) { char *loprefix; char *options; - char *optpos; - char *optend; - const char *next; int use_debug; - char b; - char *val; + int is_simple_service; struct ServiceListeningInfo *sli; SOCKTYPE *lsocks; unsigned int ls; char *binary; + char *quotedbinary; /* calculate listen socket list */ lsocks = NULL; @@ -403,117 +749,215 @@ start_process (struct ServiceList *sl, struct GNUNET_SERVER_Client *client, uint { GNUNET_array_append (lsocks, ls, GNUNET_NETWORK_get_fd (sli->listen_socket)); - if (sli->accept_task != GNUNET_SCHEDULER_NO_TASK) + if (NULL != sli->accept_task) { GNUNET_SCHEDULER_cancel (sli->accept_task); - sli->accept_task = GNUNET_SCHEDULER_NO_TASK; + sli->accept_task = NULL; } } #if WINDOWS - GNUNET_array_append (lsocks, ls, INVALID_SOCKET); + GNUNET_array_append (lsocks, + ls, + INVALID_SOCKET); #else - GNUNET_array_append (lsocks, ls, -1); + GNUNET_array_append (lsocks, + ls, + -1); #endif /* obtain configuration */ if (GNUNET_OK != - GNUNET_CONFIGURATION_get_value_string (cfg, sl->name, "PREFIX", - &loprefix)) + GNUNET_CONFIGURATION_get_value_string (cfg, + sl->name, + "PREFIX", + &loprefix)) loprefix = GNUNET_strdup (prefix_command); + else + loprefix = GNUNET_CONFIGURATION_expand_dollar (cfg, + loprefix); if (GNUNET_OK != - GNUNET_CONFIGURATION_get_value_string (cfg, sl->name, "OPTIONS", - &options)) + GNUNET_CONFIGURATION_get_value_string (cfg, + sl->name, + "OPTIONS", + &options)) + options = NULL; + else + options = GNUNET_CONFIGURATION_expand_dollar (cfg, + options); + { + char *new_options; + char *optpos; + char *fin_options; + + fin_options = GNUNET_strdup (final_option); + /* replace '{}' with service name */ + while (NULL != (optpos = strstr (fin_options, + "{}"))) { - options = GNUNET_strdup (final_option); - if (NULL == strstr (options, "%")) - { - /* replace '{}' with service name */ - while (NULL != (optpos = strstr (options, "{}"))) - { - optpos[0] = '%'; - optpos[1] = 's'; - GNUNET_asprintf (&optpos, options, sl->name); - GNUNET_free (options); - options = optpos; - } - /* replace '$PATH' with value associated with "PATH" */ - while (NULL != (optpos = strstr (options, "$"))) - { - optend = optpos + 1; - while (isupper ((unsigned char) *optend)) - optend++; - b = *optend; - if ('\0' == b) - next = ""; - else - next = optend + 1; - *optend = '\0'; - if (GNUNET_OK != - GNUNET_CONFIGURATION_get_value_string (cfg, "PATHS", - optpos + 1, &val)) - val = GNUNET_strdup (""); - *optpos = '\0'; - GNUNET_asprintf (&optpos, "%s%s%c%s", options, val, b, next); - GNUNET_free (options); - GNUNET_free (val); - options = optpos; - } - } + /* terminate string at opening parenthesis */ + *optpos = 0; + GNUNET_asprintf (&new_options, + "%s%s%s", + fin_options, + sl->name, + optpos + 2); + GNUNET_free (fin_options); + fin_options = new_options; + } + if (NULL != options) + { + /* combine "fin_options" with "options" */ + optpos = options; + GNUNET_asprintf (&options, + "%s %s", + fin_options, + optpos); + GNUNET_free (fin_options); + GNUNET_free (optpos); + } + else + { + /* only have "fin_options", use that */ + options = fin_options; } - use_debug = GNUNET_CONFIGURATION_get_value_yesno (cfg, sl->name, "DEBUG"); + } + options = GNUNET_CONFIGURATION_expand_dollar (cfg, + options); + use_debug = GNUNET_CONFIGURATION_get_value_yesno (cfg, + sl->name, + "DEBUG"); + { + const char *service_type = NULL; + const char *choices[] = { "GNUNET", "SIMPLE", NULL }; + + is_simple_service = GNUNET_NO; + if ( (GNUNET_OK == + GNUNET_CONFIGURATION_get_value_choice (cfg, + sl->name, + "TYPE", + choices, + &service_type)) && + (0 == strcasecmp (service_type, "SIMPLE")) ) + is_simple_service = GNUNET_YES; + } - /* actually start process */ - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Starting service `%s' using binary `%s' and configuration `%s'\n", - sl->name, sl->binary, sl->config); - binary = GNUNET_OS_get_libexec_binary_path (sl->binary); GNUNET_assert (NULL == sl->proc); - if (GNUNET_YES == use_debug) + if (GNUNET_YES == is_simple_service) { - if (NULL == sl->config) - sl->proc = - do_start_process (sl->pipe_control, GNUNET_OS_INHERIT_STD_OUT_AND_ERR, - lsocks, loprefix, binary, "-L", - "DEBUG", options, NULL); - else - sl->proc = - do_start_process (sl->pipe_control, GNUNET_OS_INHERIT_STD_OUT_AND_ERR, - lsocks, loprefix, binary, "-c", sl->config, "-L", - "DEBUG", options, NULL); + /* A simple service will receive no GNUnet specific + command line options. */ + binary = GNUNET_strdup (sl->binary); + binary = GNUNET_CONFIGURATION_expand_dollar (cfg, binary); + GNUNET_asprintf ("edbinary, + "\"%s\"", + sl->binary); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Starting simple service `%s' using binary `%s'\n", + sl->name, sl->binary); + /* FIXME: dollar expansion should only be done outside + * of ''-quoted strings, escaping should be considered. */ + if (NULL != options) + options = GNUNET_CONFIGURATION_expand_dollar (cfg, options); + sl->proc = + GNUNET_OS_start_process_s (sl->pipe_control, + GNUNET_OS_INHERIT_STD_OUT_AND_ERR, + lsocks, + loprefix, + quotedbinary, + options, + NULL); } else { - if (NULL == sl->config) - sl->proc = - do_start_process (sl->pipe_control, GNUNET_OS_INHERIT_STD_OUT_AND_ERR, - lsocks, loprefix, binary, - options, NULL); + /* actually start process */ + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Starting service `%s' using binary `%s' and configuration `%s'\n", + sl->name, sl->binary, sl->config); + binary = GNUNET_OS_get_libexec_binary_path (sl->binary); + GNUNET_asprintf ("edbinary, + "\"%s\"", + binary); + + if (GNUNET_YES == use_debug) + { + if (NULL == sl->config) + sl->proc = + GNUNET_OS_start_process_s (sl->pipe_control, + GNUNET_OS_INHERIT_STD_OUT_AND_ERR, + lsocks, + loprefix, + quotedbinary, + "-L", "DEBUG", + options, + NULL); + else + sl->proc = + GNUNET_OS_start_process_s (sl->pipe_control, + GNUNET_OS_INHERIT_STD_OUT_AND_ERR, + lsocks, + loprefix, + quotedbinary, + "-c", sl->config, + "-L", "DEBUG", + options, + NULL); + } else - sl->proc = - do_start_process (sl->pipe_control, GNUNET_OS_INHERIT_STD_OUT_AND_ERR, - lsocks, loprefix, binary, "-c", sl->config, - options, NULL); + { + if (NULL == sl->config) + sl->proc = + GNUNET_OS_start_process_s (sl->pipe_control, + GNUNET_OS_INHERIT_STD_OUT_AND_ERR, + lsocks, + loprefix, + quotedbinary, + options, + NULL); + else + sl->proc = + GNUNET_OS_start_process_s (sl->pipe_control, + GNUNET_OS_INHERIT_STD_OUT_AND_ERR, + lsocks, + loprefix, + quotedbinary, + "-c", sl->config, + options, + NULL); + } } GNUNET_free (binary); - if (sl->proc == NULL) + GNUNET_free (quotedbinary); + if (NULL == sl->proc) { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, _("Failed to start service `%s'\n"), + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + _("Failed to start service `%s'\n"), sl->name); if (client) - signal_result (client, sl->name, request_id, GNUNET_ARM_RESULT_START_FAILED); + signal_result (client, + sl->name, + request_id, + GNUNET_ARM_RESULT_START_FAILED); } else { - GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("Starting service `%s'\n"), + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + _("Starting service `%s'\n"), sl->name); - broadcast_status (sl->name, GNUNET_ARM_SERVICE_STARTING, NULL); + broadcast_status (sl->name, + GNUNET_ARM_SERVICE_STARTING, + NULL); if (client) - signal_result (client, sl->name, request_id, GNUNET_ARM_RESULT_STARTING); + signal_result (client, + sl->name, + request_id, + GNUNET_ARM_RESULT_STARTING); } /* clean up */ GNUNET_free (loprefix); GNUNET_free (options); - GNUNET_array_grow (lsocks, ls, 0); + GNUNET_array_grow (lsocks, + ls, + 0); } @@ -544,19 +988,16 @@ find_service (const char *name) * 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 + * @param cls callback data, `struct ServiceListeningInfo` describing a listen socket */ static void -accept_connection (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) +accept_connection (void *cls) { struct ServiceListeningInfo *sli = cls; struct ServiceList *sl = sli->sl; - sli->accept_task = GNUNET_SCHEDULER_NO_TASK; + sli->accept_task = NULL; GNUNET_assert (GNUNET_NO == in_shutdown); - if (0 != (GNUNET_SCHEDULER_REASON_SHUTDOWN & tc->reason)) - return; start_process (sl, NULL, 0); } @@ -566,86 +1007,146 @@ accept_connection (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) * wait for the first incoming connection to it * * @param sa address associated with the service - * @param addr_len length of sa + * @param addr_len length of @a sa * @param sl service entry for the service in question */ static void -create_listen_socket (struct sockaddr *sa, socklen_t addr_len, +create_listen_socket (struct sockaddr *sa, + socklen_t addr_len, struct ServiceList *sl) { static int on = 1; struct GNUNET_NETWORK_Handle *sock; struct ServiceListeningInfo *sli; +#ifndef WINDOWS + int match_uid; + int match_gid; +#endif switch (sa->sa_family) - { - case AF_INET: - sock = GNUNET_NETWORK_socket_create (PF_INET, SOCK_STREAM, 0); - break; - case AF_INET6: - sock = GNUNET_NETWORK_socket_create (PF_INET6, SOCK_STREAM, 0); - break; - case AF_UNIX: - if (strcmp (GNUNET_a2s (sa, addr_len), "@") == 0) /* Do not bind to blank UNIX path! */ - return; - sock = GNUNET_NETWORK_socket_create (PF_UNIX, SOCK_STREAM, 0); - break; - default: - GNUNET_break (0); - sock = NULL; - errno = EAFNOSUPPORT; - break; - } - if (NULL == sock) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - _("Unable to create socket for service `%s': %s\n"), - sl->name, STRERROR (errno)); - GNUNET_free (sa); + { + case AF_INET: + sock = GNUNET_NETWORK_socket_create (PF_INET, + SOCK_STREAM, + 0); + break; + case AF_INET6: + sock = GNUNET_NETWORK_socket_create (PF_INET6, + SOCK_STREAM, + 0); + break; + case AF_UNIX: + if (0 == strcmp (GNUNET_a2s (sa, + addr_len), + "@")) /* Do not bind to blank UNIX path! */ return; - } - if (GNUNET_NETWORK_socket_setsockopt - (sock, SOL_SOCKET, SO_REUSEADDR, &on, sizeof (on)) != GNUNET_OK) + sock = GNUNET_NETWORK_socket_create (PF_UNIX, + SOCK_STREAM, + 0); + break; + default: + GNUNET_break (0); + sock = NULL; + errno = EAFNOSUPPORT; + break; + } + if (NULL == sock) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + _("Unable to create socket for service `%s': %s\n"), + sl->name, + STRERROR (errno)); + GNUNET_free (sa); + return; + } + if (GNUNET_OK != + GNUNET_NETWORK_socket_setsockopt (sock, + SOL_SOCKET, + SO_REUSEADDR, + &on, + sizeof (on))) GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK, "setsockopt"); #ifdef IPV6_V6ONLY - if ((sa->sa_family == AF_INET6) && - (GNUNET_NETWORK_socket_setsockopt - (sock, IPPROTO_IPV6, IPV6_V6ONLY, &on, sizeof (on)) != GNUNET_OK)) + if ( (sa->sa_family == AF_INET6) && + (GNUNET_OK != + GNUNET_NETWORK_socket_setsockopt (sock, + IPPROTO_IPV6, + IPV6_V6ONLY, + &on, + sizeof (on))) ) GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK, "setsockopt"); #endif +#ifndef WINDOWS + if (AF_UNIX == sa->sa_family) + GNUNET_NETWORK_unix_precheck ((struct sockaddr_un *) sa); +#endif + if (GNUNET_OK != + GNUNET_NETWORK_socket_bind (sock, + (const struct sockaddr *) sa, + addr_len)) + { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + _("Unable to bind listening socket for service `%s' to address `%s': %s\n"), + sl->name, + GNUNET_a2s (sa, + addr_len), + STRERROR (errno)); + GNUNET_break (GNUNET_OK == + GNUNET_NETWORK_socket_close (sock)); + GNUNET_free (sa); + return; + } +#ifndef WINDOWS + if ((AF_UNIX == sa->sa_family) +#ifdef LINUX + /* Permission settings are not required when abstract sockets are used */ + && ('\0' != ((const struct sockaddr_un *)sa)->sun_path[0]) +#endif + ) + { + match_uid = + GNUNET_CONFIGURATION_get_value_yesno (cfg, + sl->name, + "UNIX_MATCH_UID"); + match_gid = + GNUNET_CONFIGURATION_get_value_yesno (cfg, + sl->name, + "UNIX_MATCH_GID"); + GNUNET_DISK_fix_permissions (((const struct sockaddr_un *)sa)->sun_path, + match_uid, + match_gid); - if (GNUNET_NETWORK_socket_bind - (sock, (const struct sockaddr *) sa, addr_len) != GNUNET_OK) - { - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - _ - ("Unable to bind listening socket for service `%s' to address `%s': %s\n"), - sl->name, GNUNET_a2s (sa, addr_len), STRERROR (errno)); - GNUNET_break (GNUNET_OK == GNUNET_NETWORK_socket_close (sock)); - GNUNET_free (sa); - return; - } - if (GNUNET_NETWORK_socket_listen (sock, 5) != GNUNET_OK) - { - GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "listen"); - GNUNET_break (GNUNET_OK == GNUNET_NETWORK_socket_close (sock)); - GNUNET_free (sa); - return; - } + } +#endif + if (GNUNET_OK != + GNUNET_NETWORK_socket_listen (sock, 5)) + { + GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, + "listen"); + GNUNET_break (GNUNET_OK == + GNUNET_NETWORK_socket_close (sock)); + GNUNET_free (sa); + return; + } GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("ARM now monitors connections to service `%s' at `%s'\n"), - sl->name, GNUNET_a2s (sa, addr_len)); - sli = GNUNET_malloc (sizeof (struct ServiceListeningInfo)); + sl->name, + GNUNET_a2s (sa, + addr_len)); + sli = GNUNET_new (struct ServiceListeningInfo); sli->service_addr = sa; sli->service_addr_len = addr_len; sli->listen_socket = sock; sli->sl = sl; - sli->accept_task = - GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL, sock, - &accept_connection, sli); - GNUNET_CONTAINER_DLL_insert (sl->listen_head, sl->listen_tail, sli); + sli->accept_task + = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL, + sock, + &accept_connection, sli); + GNUNET_CONTAINER_DLL_insert (sl->listen_head, + sl->listen_tail, + sli); } @@ -659,7 +1160,9 @@ static void free_service (struct ServiceList *sl) { GNUNET_assert (GNUNET_YES == in_shutdown); - GNUNET_CONTAINER_DLL_remove (running_head, running_tail, sl); + GNUNET_CONTAINER_DLL_remove (running_head, + running_tail, + sl); GNUNET_assert (NULL == sl->listen_head); GNUNET_free_non_null (sl->config); GNUNET_free_non_null (sl->binary); @@ -668,58 +1171,80 @@ free_service (struct ServiceList *sl) } +/** + * Check START-message. + * + * @param cls identification of the client + * @param amsg the actual message + * @return #GNUNET_OK to keep the connection open, + * #GNUNET_SYSERR to close it (signal serious error) + */ +static int +check_start (void *cls, + const struct GNUNET_ARM_Message *amsg) +{ + uint16_t size; + const char *servicename; + + size = ntohs (amsg->header.size) - sizeof (struct GNUNET_ARM_Message); + servicename = (const char *) &amsg[1]; + if ( (0 == size) || + (servicename[size - 1] != '\0') ) + { + GNUNET_break (0); + return GNUNET_SYSERR; + } + return GNUNET_OK; +} + + /** * Handle START-message. * - * @param cls closure (always NULL) - * @param client identification of the client - * @param message the actual message - * @return GNUNET_OK to keep the connection open, - * GNUNET_SYSERR to close it (signal serious error) + * @param cls identification of the client + * @param amsg the actual message */ static void -handle_start (void *cls, struct GNUNET_SERVER_Client *client, - const struct GNUNET_MessageHeader *message) +handle_start (void *cls, + const struct GNUNET_ARM_Message *amsg) { + struct GNUNET_SERVICE_Client *client = cls; const char *servicename; struct ServiceList *sl; - uint16_t size; uint64_t request_id; - struct GNUNET_ARM_Message *amsg; - amsg = (struct GNUNET_ARM_Message *) message; request_id = GNUNET_ntohll (amsg->request_id); - size = ntohs (amsg->header.size); - size -= sizeof (struct GNUNET_ARM_Message); servicename = (const char *) &amsg[1]; - if ((size == 0) || (servicename[size - 1] != '\0')) - { - GNUNET_break (0); - GNUNET_SERVER_receive_done (client, GNUNET_SYSERR); - return; - } + GNUNET_SERVICE_client_continue (client); if (GNUNET_YES == in_shutdown) - { - signal_result (client, servicename, request_id, GNUNET_ARM_RESULT_IN_SHUTDOWN); - GNUNET_SERVER_receive_done (client, GNUNET_OK); - return; - } + { + signal_result (client, + servicename, + request_id, + GNUNET_ARM_RESULT_IN_SHUTDOWN); + return; + } sl = find_service (servicename); if (NULL == sl) - { - signal_result (client, servicename, request_id, GNUNET_ARM_RESULT_IS_NOT_KNOWN); - GNUNET_SERVER_receive_done (client, GNUNET_OK); - return; - } - sl->is_default = GNUNET_YES; - if (sl->proc != NULL) - { - signal_result (client, servicename, request_id, GNUNET_ARM_RESULT_IS_STARTED_ALREADY); - GNUNET_SERVER_receive_done (client, GNUNET_OK); - return; - } - start_process (sl, client, request_id); - GNUNET_SERVER_receive_done (client, GNUNET_OK); + { + signal_result (client, + servicename, + request_id, + GNUNET_ARM_RESULT_IS_NOT_KNOWN); + return; + } + sl->force_start = GNUNET_YES; + if (NULL != sl->proc) + { + signal_result (client, + servicename, + request_id, + GNUNET_ARM_RESULT_IS_STARTED_ALREADY); + return; + } + start_process (sl, + client, + request_id); } @@ -727,130 +1252,159 @@ handle_start (void *cls, struct GNUNET_SERVER_Client *client, * Start a shutdown sequence. * * @param cls closure (refers to service) - * @param tc task context */ static void -trigger_shutdown (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) +trigger_shutdown (void *cls) { - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Triggering shutdown\n"); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Triggering shutdown\n"); GNUNET_SCHEDULER_shutdown (); } +/** + * Check STOP-message. + * + * @param cls identification of the client + * @param amsg the actual message + * @return #GNUNET_OK to keep the connection open, + * #GNUNET_SYSERR to close it (signal serious error) + */ +static int +check_stop (void *cls, + const struct GNUNET_ARM_Message *amsg) +{ + uint16_t size; + const char *servicename; + + size = ntohs (amsg->header.size) - sizeof (struct GNUNET_ARM_Message); + servicename = (const char *) &amsg[1]; + if ( (0 == size) || + (servicename[size - 1] != '\0') ) + { + GNUNET_break (0); + return GNUNET_SYSERR; + } + return GNUNET_OK; +} + + /** * Handle STOP-message. * - * @param cls closure (always NULL) - * @param client identification of the client - * @param message the actual message - * @return GNUNET_OK to keep the connection open, - * GNUNET_SYSERR to close it (signal serious error) + * @param cls identification of the client + * @param amsg the actual message */ static void -handle_stop (void *cls, struct GNUNET_SERVER_Client *client, - const struct GNUNET_MessageHeader *message) +handle_stop (void *cls, + const struct GNUNET_ARM_Message *amsg) { + struct GNUNET_SERVICE_Client *client = cls; struct ServiceList *sl; const char *servicename; - uint16_t size; uint64_t request_id; - struct GNUNET_ARM_Message *amsg; - amsg = (struct GNUNET_ARM_Message *) message; request_id = GNUNET_ntohll (amsg->request_id); - size = ntohs (amsg->header.size); - size -= sizeof (struct GNUNET_ARM_Message); servicename = (const char *) &amsg[1]; - if ((size == 0) || (servicename[size - 1] != '\0')) - { - GNUNET_break (0); - GNUNET_SERVER_receive_done (client, GNUNET_SYSERR); - return; - } GNUNET_log (GNUNET_ERROR_TYPE_INFO, - _("Preparing to stop `%s'\n"), servicename); - if (0 == strcasecmp (servicename, "arm")) + _("Preparing to stop `%s'\n"), + servicename); + GNUNET_SERVICE_client_continue (client); + if (0 == strcasecmp (servicename, + "arm")) { - broadcast_status (servicename, GNUNET_ARM_SERVICE_STOPPING, NULL); - signal_result (client, servicename, request_id, GNUNET_ARM_RESULT_STOPPING); - GNUNET_SERVER_client_persist_ (client); - GNUNET_SCHEDULER_add_now (trigger_shutdown, NULL); - GNUNET_SERVER_receive_done (client, GNUNET_OK); + broadcast_status (servicename, + GNUNET_ARM_SERVICE_STOPPING, + NULL); + signal_result (client, + servicename, + request_id, + GNUNET_ARM_RESULT_STOPPING); + GNUNET_SERVICE_client_persist (client); + GNUNET_SCHEDULER_add_now (&trigger_shutdown, + NULL); return; } sl = find_service (servicename); - if (sl == NULL) - { - signal_result (client, servicename, request_id, GNUNET_ARM_RESULT_IS_NOT_KNOWN); - GNUNET_SERVER_receive_done (client, GNUNET_OK); - return; - } - sl->is_default = GNUNET_NO; + if (NULL == sl) + { + signal_result (client, + servicename, + request_id, + GNUNET_ARM_RESULT_IS_NOT_KNOWN); + return; + } + sl->force_start = GNUNET_NO; if (GNUNET_YES == in_shutdown) - { - /* shutdown in progress */ - signal_result (client, servicename, request_id, GNUNET_ARM_RESULT_IN_SHUTDOWN); - GNUNET_SERVER_receive_done (client, GNUNET_OK); - return; - } - if (sl->killing_client != NULL) - { - /* killing already in progress */ - signal_result (client, servicename, request_id, - GNUNET_ARM_RESULT_IS_STOPPING_ALREADY); - GNUNET_SERVER_receive_done (client, GNUNET_OK); - return; - } - if (sl->proc == NULL) - { - /* process is down */ - signal_result (client, servicename, request_id, - GNUNET_ARM_RESULT_IS_STOPPED_ALREADY); - GNUNET_SERVER_receive_done (client, GNUNET_OK); - return; - } + { + /* shutdown in progress */ + signal_result (client, + servicename, + request_id, + GNUNET_ARM_RESULT_IN_SHUTDOWN); + return; + } + if (NULL != sl->killing_client) + { + /* killing already in progress */ + signal_result (client, + servicename, + request_id, + GNUNET_ARM_RESULT_IS_STOPPING_ALREADY); + return; + } + if (NULL == sl->proc) + { + /* process is down */ + signal_result (client, + servicename, + request_id, + GNUNET_ARM_RESULT_IS_STOPPED_ALREADY); + return; + } GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Sending kill signal to service `%s', waiting for process to die.\n", servicename); - broadcast_status (servicename, GNUNET_ARM_SERVICE_STOPPING, NULL); + broadcast_status (servicename, + GNUNET_ARM_SERVICE_STOPPING, + NULL); /* no signal_start - only when it's STOPPED */ sl->killed_at = GNUNET_TIME_absolute_get (); - if (0 != GNUNET_OS_process_kill (sl->proc, SIGTERM)) - GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "kill"); + if (0 != GNUNET_OS_process_kill (sl->proc, + GNUNET_TERM_SIG)) + GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, + "kill"); sl->killing_client = client; sl->killing_client_request_id = request_id; - GNUNET_SERVER_client_keep (client); - GNUNET_SERVER_receive_done (client, GNUNET_OK); } + /** * Handle LIST-message. * - * @param cls closure (always NULL) - * @param client identification of the client + * @param cls identification of the client * @param message the actual message */ static void -handle_list (void *cls, struct GNUNET_SERVER_Client *client, - const struct GNUNET_MessageHeader *message) +handle_list (void *cls, + const struct GNUNET_ARM_Message *request) { + struct GNUNET_SERVICE_Client *client = cls; + struct GNUNET_MQ_Envelope *env; struct GNUNET_ARM_ListResultMessage *msg; - struct GNUNET_ARM_Message *request; size_t string_list_size; - size_t total_size; struct ServiceList *sl; uint16_t count; - - if (NULL == client) - return; - - request = (struct GNUNET_ARM_Message *) message; + char *pos; + + GNUNET_break (0 == ntohl (request->reserved)); count = 0; string_list_size = 0; + /* first count the running processes get their name's size */ - for (sl = running_head; sl != NULL; sl = sl->next) + for (sl = running_head; NULL != sl; sl = sl->next) { - if (sl->proc != NULL) + if (NULL != sl->proc) { string_list_size += strlen (sl->name); string_list_size += strlen (sl->binary); @@ -859,32 +1413,54 @@ handle_list (void *cls, struct GNUNET_SERVER_Client *client, } } - total_size = sizeof (struct GNUNET_ARM_ListResultMessage) - + string_list_size; - msg = GNUNET_malloc (total_size); - msg->arm_msg.header.size = total_size; - msg->arm_msg.header.type = GNUNET_MESSAGE_TYPE_ARM_LIST_RESULT; - msg->arm_msg.request_id = GNUNET_ntohll (request->request_id); - msg->count = count; - - char *pos = (char *)&msg[1]; - for (sl = running_head; sl != NULL; sl = sl->next) + env = GNUNET_MQ_msg_extra (msg, + string_list_size, + GNUNET_MESSAGE_TYPE_ARM_LIST_RESULT); + msg->arm_msg.request_id = request->request_id; + msg->count = htons (count); + + pos = (char *) &msg[1]; + for (sl = running_head; NULL != sl; sl = sl->next) { - if (sl->proc != NULL) + if (NULL != sl->proc) { size_t s = strlen (sl->name) + strlen (sl->binary) + 4; - GNUNET_snprintf(pos, s, "%s (%s)", sl->name, sl->binary); + GNUNET_snprintf (pos, + s, + "%s (%s)", + sl->name, + sl->binary); pos += s; } } - - GNUNET_SERVER_notify_transmit_ready (client, - total_size, - GNUNET_TIME_UNIT_FOREVER_REL, - write_list_result, msg); - GNUNET_SERVER_receive_done (client, GNUNET_OK); + GNUNET_MQ_send (GNUNET_SERVICE_client_get_mq (client), + env); + GNUNET_SERVICE_client_continue (client); } + +/** + * Handle TEST-message by sending back TEST. + * + * @param cls identification of the client + * @param message the actual message + */ +static void +handle_test (void *cls, + const struct GNUNET_MessageHeader *message) +{ + struct GNUNET_SERVICE_Client *client = cls; + struct GNUNET_MQ_Envelope *env; + struct GNUNET_MessageHeader *msg; + + env = GNUNET_MQ_msg (msg, + GNUNET_MESSAGE_TYPE_ARM_TEST); + GNUNET_MQ_send (GNUNET_SERVICE_client_get_mq (client), + env); + GNUNET_SERVICE_client_continue (client); +} + + /** * We are done with everything. Stop remaining * tasks, signal handler and the server. @@ -892,84 +1468,100 @@ handle_list (void *cls, struct GNUNET_SERVER_Client *client, static void do_shutdown () { - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Last shutdown phase\n"); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Last shutdown phase\n"); if (NULL != notifier) { - GNUNET_SERVER_notification_context_destroy (notifier); + GNUNET_notification_context_destroy (notifier); notifier = NULL; } - if (NULL != server) - { - GNUNET_SERVER_destroy (server); - server = NULL; - } - if (GNUNET_SCHEDULER_NO_TASK != child_death_task) - { - GNUNET_SCHEDULER_cancel (child_death_task); - child_death_task = GNUNET_SCHEDULER_NO_TASK; - } + if (NULL != service) + { + GNUNET_SERVICE_shutdown (service); + service = NULL; + } + if (NULL != child_death_task) + { + GNUNET_SCHEDULER_cancel (child_death_task); + child_death_task = NULL; + } } -unsigned int + +/** + * Count how many services are still active. + * + * @param running_head list of services + * @return number of active services found + */ +static unsigned int list_count (struct ServiceList *running_head) { struct ServiceList *i; - unsigned int res = 0; + unsigned int res; + for (res = 0, i = running_head; i; i = i->next, res++) - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "%s\n", i->name); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "%s\n", + i->name); return res; } + /** * Task run for shutdown. * * @param cls closure, NULL if we need to self-restart - * @param tc context */ static void -shutdown_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) +shutdown_task (void *cls) { struct ServiceList *pos; struct ServiceList *nxt; struct ServiceListeningInfo *sli; - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "First shutdown phase\n"); - if (GNUNET_SCHEDULER_NO_TASK != child_restart_task) + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "First shutdown phase\n"); + if (NULL != child_restart_task) { GNUNET_SCHEDULER_cancel (child_restart_task); - child_restart_task = GNUNET_SCHEDULER_NO_TASK; + child_restart_task = NULL; } in_shutdown = GNUNET_YES; /* first, stop listening */ for (pos = running_head; NULL != pos; pos = pos->next) { while (NULL != (sli = pos->listen_head)) + { + GNUNET_CONTAINER_DLL_remove (pos->listen_head, + pos->listen_tail, + sli); + if (NULL != sli->accept_task) { - GNUNET_CONTAINER_DLL_remove (pos->listen_head, - pos->listen_tail, sli); - if (sli->accept_task != GNUNET_SCHEDULER_NO_TASK) - { - GNUNET_SCHEDULER_cancel (sli->accept_task); - sli->accept_task = GNUNET_SCHEDULER_NO_TASK; - } - GNUNET_break (GNUNET_OK == - GNUNET_NETWORK_socket_close (sli->listen_socket)); - GNUNET_free (sli->service_addr); - GNUNET_free (sli); + GNUNET_SCHEDULER_cancel (sli->accept_task); + sli->accept_task = NULL; } + GNUNET_break (GNUNET_OK == + GNUNET_NETWORK_socket_close (sli->listen_socket)); + GNUNET_free (sli->service_addr); + GNUNET_free (sli); + } } /* then, shutdown all existing service processes */ nxt = running_head; while (NULL != (pos = nxt)) { nxt = pos->next; - if (pos->proc != NULL) + if (NULL != pos->proc) { - GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Stopping service `%s'\n", + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Stopping service `%s'\n", pos->name); pos->killed_at = GNUNET_TIME_absolute_get (); - if (0 != GNUNET_OS_process_kill (pos->proc, SIGTERM)) - GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "kill"); + if (0 != GNUNET_OS_process_kill (pos->proc, + GNUNET_TERM_SIG)) + GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, + "kill"); } else { @@ -977,11 +1569,12 @@ shutdown_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) } } /* finally, should all service processes be already gone, terminate for real */ - if (running_head == NULL) + if (NULL == running_head) do_shutdown (); else GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Delaying shutdown, have %u childs still running\n", list_count (running_head)); + "Delaying shutdown, have %u childs still running\n", + list_count (running_head)); } @@ -989,19 +1582,16 @@ shutdown_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) * Task run whenever it is time to restart a child that died. * * @param cls closure, always NULL - * @param tc context */ static void -delayed_restart_task (void *cls, - const struct GNUNET_SCHEDULER_TaskContext *tc) +delayed_restart_task (void *cls) + { struct ServiceList *sl; struct GNUNET_TIME_Relative lowestRestartDelay; struct ServiceListeningInfo *sli; - child_restart_task = GNUNET_SCHEDULER_NO_TASK; - if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN)) - return; + child_restart_task = NULL; GNUNET_assert (GNUNET_NO == in_shutdown); lowestRestartDelay = GNUNET_TIME_UNIT_FOREVER_REL; @@ -1012,28 +1602,31 @@ delayed_restart_task (void *cls, if (NULL != sl->proc) continue; /* service is currently not running */ - if (GNUNET_TIME_absolute_get_remaining (sl->restart_at).rel_value == - 0) + if (0 == GNUNET_TIME_absolute_get_remaining (sl->restart_at).rel_value_us) { /* restart is now allowed */ - if (sl->is_default) + if (sl->force_start) { /* process should run by default, start immediately */ GNUNET_log (GNUNET_ERROR_TYPE_INFO, - _("Restarting service `%s'.\n"), sl->name); - start_process (sl, NULL, 0); + _("Restarting service `%s'.\n"), + sl->name); + start_process (sl, + NULL, + 0); } else { /* process is run on-demand, ensure it is re-started if there is demand */ for (sli = sl->listen_head; NULL != sli; sli = sli->next) - if (GNUNET_SCHEDULER_NO_TASK == sli->accept_task) + if (NULL == sli->accept_task) { /* accept was actually paused, so start it again */ - sli->accept_task = - GNUNET_SCHEDULER_add_read_net - (GNUNET_TIME_UNIT_FOREVER_REL, sli->listen_socket, - &accept_connection, sli); + sli->accept_task + = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL, + sli->listen_socket, + &accept_connection, + sli); } } } @@ -1046,15 +1639,17 @@ delayed_restart_task (void *cls, (sl->restart_at)); } } - if (lowestRestartDelay.rel_value != GNUNET_TIME_UNIT_FOREVER_REL.rel_value) + if (lowestRestartDelay.rel_value_us != GNUNET_TIME_UNIT_FOREVER_REL.rel_value_us) { - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Will restart process in %s\n", - GNUNET_STRINGS_relative_time_to_string (lowestRestartDelay, GNUNET_YES)); + GNUNET_STRINGS_relative_time_to_string (lowestRestartDelay, + GNUNET_YES)); child_restart_task = GNUNET_SCHEDULER_add_delayed_with_priority (lowestRestartDelay, - GNUNET_SCHEDULER_PRIORITY_IDLE, - &delayed_restart_task, NULL); + GNUNET_SCHEDULER_PRIORITY_IDLE, + &delayed_restart_task, + NULL); } } @@ -1064,10 +1659,9 @@ delayed_restart_task (void *cls, * process died). * * @param cls closure, NULL if we need to self-restart - * @param tc context */ static void -maint_child_death (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) +maint_child_death (void *cls) { struct ServiceList *pos; struct ServiceList *next; @@ -1080,111 +1674,185 @@ maint_child_death (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) unsigned long statusCode; const struct GNUNET_DISK_FileHandle *pr; - pr = GNUNET_DISK_pipe_handle (sigpipe, GNUNET_DISK_PIPE_END_READ); - child_death_task = GNUNET_SCHEDULER_NO_TASK; - if (0 == (tc->reason & GNUNET_SCHEDULER_REASON_READ_READY)) - { - /* shutdown scheduled us, ignore! */ - child_death_task = - GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_FOREVER_REL, - pr, &maint_child_death, NULL); - return; - } + pr = GNUNET_DISK_pipe_handle (sigpipe, + GNUNET_DISK_PIPE_END_READ); + child_death_task = NULL; /* consume the signal */ - GNUNET_break (0 < GNUNET_DISK_file_read (pr, &c, sizeof (c))); + GNUNET_break (0 < GNUNET_DISK_file_read (pr, + &c, + sizeof (c))); /* check for services that died (WAITPID) */ next = running_head; while (NULL != (pos = next)) - { - next = pos->next; + { + next = pos->next; - if (pos->proc == NULL) + if (NULL == pos->proc) + { + if (GNUNET_YES == in_shutdown) + free_service (pos); + continue; + } +#if HAVE_WAIT4 + if (NULL != wait_file) + { + /* need to use 'wait4()' to obtain and log performance data */ + struct rusage ru; + int status; + pid_t pid; + + pid = GNUNET_OS_process_get_pid (pos->proc); + ret = wait4 (pid, + &status, + WNOHANG, + &ru); + if (ret <= 0) + continue; /* no process done */ + if (WIFEXITED (status)) { - if (GNUNET_YES == in_shutdown) - free_service (pos); - continue; + statusType = GNUNET_OS_PROCESS_EXITED; + statusCode = WEXITSTATUS (status); } - if ((GNUNET_SYSERR == - (ret = - GNUNET_OS_process_status (pos->proc, &statusType, &statusCode))) - || ((ret == GNUNET_NO) || (statusType == GNUNET_OS_PROCESS_STOPPED) - || (statusType == GNUNET_OS_PROCESS_RUNNING))) - continue; - if (statusType == GNUNET_OS_PROCESS_EXITED) + else if (WIFSIGNALED (status)) { - statstr = _( /* process termination method */ "exit"); - statcode = statusCode; + statusType = GNUNET_OS_PROCESS_SIGNALED; + statusCode = WTERMSIG (status); } - else if (statusType == GNUNET_OS_PROCESS_SIGNALED) + else if (WIFSTOPPED (status)) { - statstr = _( /* process termination method */ "signal"); - statcode = statusCode; + statusType = GNUNET_OS_PROCESS_SIGNALED; + statusCode = WSTOPSIG (status); } - else +#ifdef WIFCONTINUED + else if (WIFCONTINUED (status)) { - statstr = _( /* process termination method */ "unknown"); - statcode = 0; + statusType = GNUNET_OS_PROCESS_RUNNING; + statusCode = 0; } - if (0 != pos->killed_at.abs_value) +#endif + else { - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - _("Service `%s' took %s to terminate\n"), - pos->name, - GNUNET_STRINGS_relative_time_to_string (GNUNET_TIME_absolute_get_duration (pos->killed_at), GNUNET_YES)); + statusType = GNUNET_OS_PROCESS_UNKNOWN; + statusCode = 0; } - GNUNET_OS_process_destroy (pos->proc); - pos->proc = NULL; - broadcast_status (pos->name, GNUNET_ARM_SERVICE_STOPPED, NULL); - if (NULL != pos->killing_client) + if ( (GNUNET_OS_PROCESS_EXITED == statusType) || + (GNUNET_OS_PROCESS_SIGNALED == statusType) ) { - signal_result (pos->killing_client, pos->name, - pos->killing_client_request_id, GNUNET_ARM_RESULT_STOPPED); - GNUNET_SERVER_client_drop (pos->killing_client); - pos->killing_client = NULL; - pos->killing_client_request_id = 0; + double utime = ru.ru_utime.tv_sec + (ru.ru_utime.tv_usec / 10e6); + double stime = ru.ru_stime.tv_sec + (ru.ru_stime.tv_usec / 10e6); + fprintf (wait_file, + "%s(%u) %.3f %.3f %llu %llu %llu %llu %llu\n", + pos->binary, + (unsigned int) pid, + utime, + stime, + (unsigned long long) ru.ru_maxrss, + (unsigned long long) ru.ru_inblock, + (unsigned long long) ru.ru_oublock, + (unsigned long long) ru.ru_nvcsw, + (unsigned long long) ru.ru_nivcsw); } - if (GNUNET_YES != in_shutdown) + } + else /* continue with JUST this "if" as "else" (intentionally no brackets!) */ +#endif + if ( (GNUNET_SYSERR == + (ret = + GNUNET_OS_process_status (pos->proc, + &statusType, + &statusCode))) || + (ret == GNUNET_NO) || + (statusType == GNUNET_OS_PROCESS_STOPPED) || + (statusType == GNUNET_OS_PROCESS_UNKNOWN) || + (statusType == GNUNET_OS_PROCESS_RUNNING) ) + continue; + + if (statusType == GNUNET_OS_PROCESS_EXITED) + { + statstr = _( /* process termination method */ "exit"); + statcode = statusCode; + } + else if (statusType == GNUNET_OS_PROCESS_SIGNALED) + { + statstr = _( /* process termination method */ "signal"); + statcode = statusCode; + } + else + { + statstr = _( /* process termination method */ "unknown"); + statcode = 0; + } + if (0 != pos->killed_at.abs_value_us) + { + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + _("Service `%s' took %s to terminate\n"), + pos->name, + GNUNET_STRINGS_relative_time_to_string (GNUNET_TIME_absolute_get_duration (pos->killed_at), + GNUNET_YES)); + } + GNUNET_OS_process_destroy (pos->proc); + pos->proc = NULL; + broadcast_status (pos->name, + GNUNET_ARM_SERVICE_STOPPED, + NULL); + if (NULL != pos->killing_client) + { + signal_result (pos->killing_client, pos->name, + pos->killing_client_request_id, + GNUNET_ARM_RESULT_STOPPED); + pos->killing_client = NULL; + pos->killing_client_request_id = 0; + } + if (GNUNET_YES != in_shutdown) + { + if ( (statusType == GNUNET_OS_PROCESS_EXITED) && + (statcode == 0) ) { - if ((statusType == GNUNET_OS_PROCESS_EXITED) && (statcode == 0)) + /* process terminated normally, allow restart at any time */ + pos->restart_at.abs_value_us = 0; + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + _("Service `%s' terminated normally, will restart at any time\n"), + pos->name); + /* process can still be re-started on-demand, ensure it is re-started if there is demand */ + for (sli = pos->listen_head; NULL != sli; sli = sli->next) { - /* process terminated normally, allow restart at any time */ - pos->restart_at.abs_value = 0; - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - _("Service `%s' terminated normally, will restart at any time\n"), - pos->name); - /* process can still be re-started on-demand, ensure it is re-started if there is demand */ - for (sli = pos->listen_head; NULL != sli; sli = sli->next) - { - GNUNET_break (GNUNET_SCHEDULER_NO_TASK == sli->accept_task); - sli->accept_task = - GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL, - sli->listen_socket, &accept_connection, sli); - } - } - else - { - if (0 == (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN)) - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - _("Service `%s' terminated with status %s/%d, will restart in %s\n"), - pos->name, statstr, statcode, - GNUNET_STRINGS_relative_time_to_string (pos->backoff, GNUNET_YES)); - /* schedule restart */ - pos->restart_at = GNUNET_TIME_relative_to_absolute (pos->backoff); - pos->backoff = GNUNET_TIME_STD_BACKOFF (pos->backoff); - if (GNUNET_SCHEDULER_NO_TASK != child_restart_task) - GNUNET_SCHEDULER_cancel (child_restart_task); - child_restart_task = GNUNET_SCHEDULER_add_with_priority ( - GNUNET_SCHEDULER_PRIORITY_IDLE, &delayed_restart_task, NULL); + GNUNET_break (NULL == sli->accept_task); + sli->accept_task = + GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL, + sli->listen_socket, + &accept_connection, + sli); } } else { - free_service (pos); + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + _("Service `%s' terminated with status %s/%d, will restart in %s\n"), + pos->name, + statstr, + statcode, + GNUNET_STRINGS_relative_time_to_string (pos->backoff, + GNUNET_YES)); + /* schedule restart */ + pos->restart_at = GNUNET_TIME_relative_to_absolute (pos->backoff); + pos->backoff = GNUNET_TIME_STD_BACKOFF (pos->backoff); + if (NULL != child_restart_task) + GNUNET_SCHEDULER_cancel (child_restart_task); + child_restart_task + = GNUNET_SCHEDULER_add_with_priority (GNUNET_SCHEDULER_PRIORITY_IDLE, + &delayed_restart_task, + NULL); } } + else + { + free_service (pos); + } + } child_death_task = GNUNET_SCHEDULER_add_read_file ( - GNUNET_TIME_UNIT_FOREVER_REL, pr, &maint_child_death, NULL); + GNUNET_TIME_UNIT_FOREVER_REL, + pr, + &maint_child_death, NULL); if ((NULL == running_head) && (GNUNET_YES == in_shutdown)) do_shutdown (); else if (GNUNET_YES == in_shutdown) @@ -1206,9 +1874,10 @@ sighandler_child_death () int old_errno = errno; /* back-up errno */ GNUNET_break (1 == - GNUNET_DISK_file_write (GNUNET_DISK_pipe_handle - (sigpipe, GNUNET_DISK_PIPE_END_WRITE), - &c, sizeof (c))); + GNUNET_DISK_file_write (GNUNET_DISK_pipe_handle (sigpipe, + GNUNET_DISK_PIPE_END_WRITE), + &c, + sizeof (c))); errno = old_errno; /* restore errno */ } @@ -1219,10 +1888,11 @@ sighandler_child_death () * * @param cls unused * @param section a section in the configuration file - * @return GNUNET_OK (continue) + * @return #GNUNET_OK (continue) */ static void -setup_service (void *cls, const char *section) +setup_service (void *cls, + const char *section) { struct ServiceList *sl; char *binary; @@ -1233,40 +1903,72 @@ setup_service (void *cls, const char *section) int ret; unsigned int i; - if (strcasecmp (section, "arm") == 0) + if (0 == strcasecmp (section, + "arm")) return; if (GNUNET_OK != - GNUNET_CONFIGURATION_get_value_string (cfg, section, "BINARY", &binary)) + GNUNET_CONFIGURATION_get_value_string (cfg, + section, + "BINARY", + &binary)) + { + /* not a service section */ + return; + } + if ((GNUNET_YES == + GNUNET_CONFIGURATION_have_value (cfg, + section, + "USER_SERVICE")) && + (GNUNET_YES == + GNUNET_CONFIGURATION_get_value_yesno (cfg, + section, + "USER_SERVICE"))) + { + if (GNUNET_NO == start_user) { - /* not a service section */ - return; + GNUNET_free (binary); + return; /* user service, and we don't deal with those */ + } + } + else + { + if (GNUNET_NO == start_system) + { + GNUNET_free (binary); + return; /* system service, and we don't deal with those */ } + } sl = find_service (section); if (NULL != sl) { /* got the same section twice!? */ GNUNET_break (0); + GNUNET_free (binary); return; } config = NULL; if (( (GNUNET_OK != - GNUNET_CONFIGURATION_get_value_filename (cfg, section, "CONFIG", - &config)) && + GNUNET_CONFIGURATION_get_value_filename (cfg, + section, + "CONFIG", + &config)) && (GNUNET_OK != - GNUNET_CONFIGURATION_get_value_filename (cfg, "PATHS", "DEFAULTCONFIG", + GNUNET_CONFIGURATION_get_value_filename (cfg, + "PATHS", + "DEFAULTCONFIG", &config)) ) || (0 != STAT (config, &sbuf))) { if (NULL != config) { - GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_WARNING, + GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_WARNING, section, "CONFIG", STRERROR (errno)); GNUNET_free (config); config = NULL; } } - sl = GNUNET_malloc (sizeof (struct ServiceList)); + sl = GNUNET_new (struct ServiceList); sl->name = GNUNET_strdup (section); sl->binary = binary; sl->config = config; @@ -1275,147 +1977,206 @@ setup_service (void *cls, const char *section) #if WINDOWS sl->pipe_control = GNUNET_YES; #else - if (GNUNET_CONFIGURATION_have_value (cfg, section, "PIPECONTROL")) - sl->pipe_control = GNUNET_CONFIGURATION_get_value_yesno (cfg, section, "PIPECONTROL"); -#endif - GNUNET_CONTAINER_DLL_insert (running_head, running_tail, sl); - if (GNUNET_YES != - GNUNET_CONFIGURATION_get_value_yesno (cfg, section, "AUTOSTART")) - return; - if (0 >= (ret = GNUNET_SERVICE_get_server_addresses (section, cfg, - &addrs, &addr_lens))) + if (GNUNET_CONFIGURATION_have_value (cfg, + section, + "PIPECONTROL")) + sl->pipe_control = GNUNET_CONFIGURATION_get_value_yesno (cfg, + section, + "PIPECONTROL"); +#endif + GNUNET_CONTAINER_DLL_insert (running_head, + running_tail, + sl); + if (GNUNET_YES == + GNUNET_CONFIGURATION_get_value_yesno (cfg, + section, + "FORCESTART")) + { + sl->force_start = GNUNET_YES; + if (GNUNET_YES == + GNUNET_CONFIGURATION_get_value_yesno (cfg, + section, + "NOARMBIND")) + return; + } + else + { + if (GNUNET_YES != + GNUNET_CONFIGURATION_get_value_yesno (cfg, + section, + "AUTOSTART")) + return; + } + if (0 >= (ret = get_server_addresses (section, + cfg, + &addrs, + &addr_lens))) return; /* this will free (or capture) addrs[i] */ for (i = 0; i < ret; i++) - create_listen_socket (addrs[i], addr_lens[i], sl); + create_listen_socket (addrs[i], + addr_lens[i], + sl); GNUNET_free (addrs); GNUNET_free (addr_lens); } /** - * A client connected, add it to the notification context. + * A client connected, mark as a monitoring client. * * @param cls closure * @param client identification of the client + * @param mq queue to talk to @a client + * @return @a client */ -static void -handle_client_connecting (void *cls, struct GNUNET_SERVER_Client *client) +static void * +client_connect_cb (void *cls, + struct GNUNET_SERVICE_Client *client, + struct GNUNET_MQ_Handle *mq) { /* All clients are considered to be of the "monitor" kind * (that is, they don't affect ARM shutdown). */ - if (NULL != client) - GNUNET_SERVER_client_mark_monitor (client); + GNUNET_SERVICE_client_mark_monitor (client); + return client; +} + + +/** + * A client disconnected, clean up associated state. + * + * @param cls closure + * @param client identification of the client + * @param app_ctx must match @a client + */ +static void +client_disconnect_cb (void *cls, + struct GNUNET_SERVICE_Client *client, + void *app_ctx) +{ + struct ServiceList *sl; + + GNUNET_assert (client == app_ctx); + + for (sl = running_head; NULL != sl; sl = sl->next) + if (sl->killing_client == client) + sl->killing_client = NULL; } + /** * Handle MONITOR-message. * - * @param cls closure (always NULL) - * @param client identification of the client + * @param cls identification of the client * @param message the actual message - * @return GNUNET_OK to keep the connection open, - * GNUNET_SYSERR to close it (signal serious error) + * @return #GNUNET_OK to keep the connection open, + * #GNUNET_SYSERR to close it (signal serious error) */ static void -handle_monitor (void *cls, struct GNUNET_SERVER_Client *client, - const struct GNUNET_MessageHeader *message) +handle_monitor (void *cls, + const struct GNUNET_MessageHeader *message) { + struct GNUNET_SERVICE_Client *client = cls; + + /* FIXME: might want to start by letting monitor know about + services that are already running */ /* Removal is handled by the server implementation, internally. */ - if ((NULL != client) && (NULL != notifier)) - { - GNUNET_SERVER_notification_context_add (notifier, client); - broadcast_status ("arm", GNUNET_ARM_SERVICE_MONITORING_STARTED, client); - GNUNET_SERVER_receive_done (client, GNUNET_OK); - } + GNUNET_notification_context_add (notifier, + GNUNET_SERVICE_client_get_mq (client)); + broadcast_status ("arm", + GNUNET_ARM_SERVICE_MONITORING_STARTED, + client); + GNUNET_SERVICE_client_continue (client); } + /** * Process arm requests. * * @param cls closure - * @param serv the initialized server + * @param serv the initialized service * @param c configuration to use */ static void -run (void *cls, struct GNUNET_SERVER_Handle *serv, - const struct GNUNET_CONFIGURATION_Handle *c) +run (void *cls, + const struct GNUNET_CONFIGURATION_Handle *c, + struct GNUNET_SERVICE_Handle *serv) { - static const struct GNUNET_SERVER_MessageHandler handlers[] = { - {&handle_start, NULL, GNUNET_MESSAGE_TYPE_ARM_START, 0}, - {&handle_stop, NULL, GNUNET_MESSAGE_TYPE_ARM_STOP, 0}, - {&handle_monitor, NULL, GNUNET_MESSAGE_TYPE_ARM_MONITOR, - sizeof (struct GNUNET_MessageHeader)}, - {&handle_list, NULL, GNUNET_MESSAGE_TYPE_ARM_LIST, - sizeof (struct GNUNET_ARM_Message)}, - {NULL, NULL, 0, 0} - }; - char *defaultservices; - const char *pos; struct ServiceList *sl; cfg = c; - server = serv; - GNUNET_assert (serv != NULL); - GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL, &shutdown_task, - NULL); + service = serv; + GNUNET_SCHEDULER_add_shutdown (&shutdown_task, + NULL); child_death_task = GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_FOREVER_REL, GNUNET_DISK_pipe_handle (sigpipe, GNUNET_DISK_PIPE_END_READ), - &maint_child_death, NULL); - + &maint_child_death, + NULL); +#if HAVE_WAIT4 + if (GNUNET_OK == + GNUNET_CONFIGURATION_get_value_filename (cfg, + "ARM", + "RESOURCE_DIAGNOSTICS", + &wait_filename)) + { + wait_file = fopen (wait_filename, + "w"); + if (NULL == wait_file) + { + GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, + "fopen", + wait_filename); + } + } +#endif if (GNUNET_OK != - GNUNET_CONFIGURATION_get_value_string (cfg, "ARM", "GLOBAL_PREFIX", - &prefix_command)) + GNUNET_CONFIGURATION_get_value_string (cfg, + "ARM", + "GLOBAL_PREFIX", + &prefix_command)) prefix_command = GNUNET_strdup (""); + else + prefix_command = GNUNET_CONFIGURATION_expand_dollar (cfg, + prefix_command); if (GNUNET_OK != - GNUNET_CONFIGURATION_get_value_string (cfg, "ARM", "GLOBAL_POSTFIX", - &final_option)) + GNUNET_CONFIGURATION_get_value_string (cfg, + "ARM", + "GLOBAL_POSTFIX", + &final_option)) final_option = GNUNET_strdup (""); - - GNUNET_CONFIGURATION_iterate_sections (cfg, &setup_service, NULL); - - /* start default services... */ - if (GNUNET_OK == - GNUNET_CONFIGURATION_get_value_string (cfg, "ARM", "DEFAULTSERVICES", - &defaultservices)) - { - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - _("Starting default services `%s'\n"), defaultservices); - if (0 < strlen (defaultservices)) - { - for (pos = strtok (defaultservices, " "); NULL != pos; - pos = strtok (NULL, " ")) - { - sl = find_service (pos); - if (NULL == sl) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - _ - ("Default service `%s' not configured correctly!\n"), - pos); - continue; - } - sl->is_default = GNUNET_YES; - start_process (sl, NULL, 0); - } - } - GNUNET_free (defaultservices); - } else - { - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - _ - ("No default services configured, GNUnet will not really start right now.\n")); - } + final_option = GNUNET_CONFIGURATION_expand_dollar (cfg, + final_option); + if (GNUNET_YES == + GNUNET_CONFIGURATION_get_value_yesno (cfg, + "ARM", + "USER_ONLY")) + { + GNUNET_break (GNUNET_YES == start_user); + start_system = GNUNET_NO; + } + if (GNUNET_YES == + GNUNET_CONFIGURATION_get_value_yesno (cfg, + "ARM", + "SYSTEM_ONLY")) + { + GNUNET_break (GNUNET_YES == start_system); + start_user = GNUNET_NO; + } + GNUNET_CONFIGURATION_iterate_sections (cfg, + &setup_service, + NULL); - notifier = - GNUNET_SERVER_notification_context_create (server, MAX_NOTIFY_QUEUE); - GNUNET_SERVER_connect_notify (server, handle_client_connecting, NULL); - /* process client requests */ - GNUNET_SERVER_add_handlers (server, handlers); + /* start default services... */ + for (sl = running_head; NULL != sl; sl = sl->next) + if (GNUNET_YES == sl->force_start) + start_process (sl, + NULL, + 0); + notifier = GNUNET_notification_context_create (MAX_NOTIFY_QUEUE); } @@ -1427,19 +2188,64 @@ run (void *cls, struct GNUNET_SERVER_Handle *serv, * @return 0 ok, 1 on error */ int -main (int argc, char *const *argv) +main (int argc, + char *const *argv) { int ret; struct GNUNET_SIGNAL_Context *shc_chld; + struct GNUNET_MQ_MessageHandler handlers[] = { + GNUNET_MQ_hd_var_size (start, + GNUNET_MESSAGE_TYPE_ARM_START, + struct GNUNET_ARM_Message, + NULL), + GNUNET_MQ_hd_var_size (stop, + GNUNET_MESSAGE_TYPE_ARM_STOP, + struct GNUNET_ARM_Message, + NULL), + GNUNET_MQ_hd_fixed_size (monitor, + GNUNET_MESSAGE_TYPE_ARM_MONITOR, + struct GNUNET_MessageHeader, + NULL), + GNUNET_MQ_hd_fixed_size (list, + GNUNET_MESSAGE_TYPE_ARM_LIST, + struct GNUNET_ARM_Message, + NULL), + GNUNET_MQ_hd_fixed_size (test, + GNUNET_MESSAGE_TYPE_ARM_TEST, + struct GNUNET_MessageHeader, + NULL), + GNUNET_MQ_handler_end () + }; - sigpipe = GNUNET_DISK_pipe (GNUNET_NO, GNUNET_NO, GNUNET_NO, GNUNET_NO); - GNUNET_assert (sigpipe != NULL); + sigpipe = GNUNET_DISK_pipe (GNUNET_NO, + GNUNET_NO, + GNUNET_NO, + GNUNET_NO); + GNUNET_assert (NULL != sigpipe); shc_chld = - GNUNET_SIGNAL_handler_install (GNUNET_SIGCHLD, &sighandler_child_death); - ret = - (GNUNET_OK == - GNUNET_SERVICE_run (argc, argv, "arm", - GNUNET_SERVICE_OPTION_MANUAL_SHUTDOWN, &run, NULL)) ? 0 : 1; + GNUNET_SIGNAL_handler_install (GNUNET_SIGCHLD, + &sighandler_child_death); + ret = GNUNET_SERVICE_run_ (argc, + argv, + "arm", + GNUNET_SERVICE_OPTION_MANUAL_SHUTDOWN, + &run, + &client_connect_cb, + &client_disconnect_cb, + NULL, + handlers); +#if HAVE_WAIT4 + if (NULL != wait_file) + { + fclose (wait_file); + wait_file = NULL; + } + if (NULL != wait_filename) + { + GNUNET_free (wait_filename); + wait_filename = NULL; + } +#endif GNUNET_SIGNAL_handler_uninstall (shc_chld); shc_chld = NULL; GNUNET_DISK_pipe_close (sigpipe); @@ -1448,7 +2254,7 @@ main (int argc, char *const *argv) } -#ifdef LINUX +#if defined(LINUX) && defined(__GLIBC__) #include /**