From: Christian Grothoff Date: Thu, 11 Aug 2011 11:05:35 +0000 (+0000) Subject: use bind instead of connect to test if service is running to avoid long timeouts... X-Git-Tag: initial-import-from-subversion-38251~17408 X-Git-Url: https://git.librecmc.org/?a=commitdiff_plain;h=6202dff2295abe7813a12bb53b90916bf26bb132;p=oweals%2Fgnunet.git use bind instead of connect to test if service is running to avoid long timeouts during startup --- diff --git a/src/arm/arm_api.c b/src/arm/arm_api.c index c5caa5393..4114b8528 100644 --- a/src/arm/arm_api.c +++ b/src/arm/arm_api.c @@ -379,8 +379,6 @@ arm_service_report (void *cls, "Looks like `%s' is not running, will start it.\n", "gnunet-service-arm"); #endif - /* FIXME: should we check that HOSTNAME for 'arm' is localhost? */ - if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_string (pos->h->cfg, "arm", "PREFIX", &loprefix)) @@ -621,6 +619,7 @@ GNUNET_ARM_start_service (struct GNUNET_ARM_Handle *h, struct RequestContext *sctx; struct GNUNET_CLIENT_Connection *client; size_t slen; + #if DEBUG_ARM GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, _("Asked to start service `%s' within %llu ms\n"), service_name, diff --git a/src/arm/gnunet-arm.c b/src/arm/gnunet-arm.c index c2663aba9..ccad0f21f 100644 --- a/src/arm/gnunet-arm.c +++ b/src/arm/gnunet-arm.c @@ -46,7 +46,7 @@ * (by checking if running before starting, so really this time is always waited on * startup (annoying)). */ -#define START_TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MILLISECONDS, 1000) +#define START_TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 1) /** * Timeout for starting services, very short because of the strange way start works diff --git a/src/transport/Makefile.am b/src/transport/Makefile.am index 74f79013e..417fdef5a 100644 --- a/src/transport/Makefile.am +++ b/src/transport/Makefile.am @@ -333,6 +333,7 @@ test_transport_api_disconnect_SOURCES = \ test_transport_api_disconnect.c test_transport_api_disconnect_LDADD = \ $(top_builddir)/src/transport/libgnunettransport.la \ + $(top_builddir)/src/hello/libgnunethello.la \ $(top_builddir)/src/statistics/libgnunetstatistics.la \ $(top_builddir)/src/testing/libgnunettesting.la \ $(top_builddir)/src/util/libgnunetutil.la diff --git a/src/util/client.c b/src/util/client.c index 490d1f3da..a05c5bf75 100644 --- a/src/util/client.c +++ b/src/util/client.c @@ -666,6 +666,15 @@ confirm_handler (void *cls, const struct GNUNET_MessageHeader *msg) } +/** + * Send the 'TEST' message to the service. If successful, prepare to + * receive the reply. + * + * @param cls the 'struct GNUNET_CLIENT_Connection' of the connection to test + * @param size number of bytes available in buf + * @param buf where to write the message + * @return number of bytes written to buf + */ static size_t write_test (void *cls, size_t size, void *buf) { @@ -698,11 +707,13 @@ write_test (void *cls, size_t size, void *buf) /** - * Wait until the service is running. + * Test if the service is running. If we are given a UNIXPATH or a local address, + * we do this NOT by trying to connect to the service, but by trying to BIND to + * the same port. If the BIND fails, we know the service is running. * * @param service name of the service to wait for * @param cfg configuration to use - * @param timeout how long to wait at most in ms + * @param timeout how long to wait at most * @param task task to run if service is running * (reason will be "PREREQ_DONE" (service running) * or "TIMEOUT" (service not known to be running)) @@ -714,12 +725,171 @@ GNUNET_CLIENT_service_test (const char *service, struct GNUNET_TIME_Relative timeout, GNUNET_SCHEDULER_Task task, void *task_cls) { + char *hostname; + unsigned long long port; + struct GNUNET_NETWORK_Handle *sock; struct GNUNET_CLIENT_Connection *conn; #if DEBUG_CLIENT GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Testing if service `%s' is running.\n", service); #endif +#ifdef AF_UNIX + { + /* probe UNIX support */ + struct sockaddr_un s_un; + size_t slen; + char *unixpath; + + unixpath = NULL; + if ( (GNUNET_OK == + GNUNET_CONFIGURATION_get_value_string (cfg, + service, + "UNIXPATH", &unixpath)) && + (0 < strlen (unixpath)) ) /* We have a non-NULL unixpath, does that mean it's valid? */ + { + if (strlen(unixpath) >= sizeof(s_un.sun_path)) + { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + _("UNIXPATH `%s' too long, maximum length is %llu\n"), + unixpath, + sizeof(s_un.sun_path)); + } + else + { + sock = GNUNET_NETWORK_socket_create (PF_UNIX, SOCK_STREAM, 0); + if (sock != NULL) + { + memset (&s_un, 0, sizeof (s_un)); + s_un.sun_family = AF_UNIX; + slen = strlen (unixpath) + 1; + if (slen >= sizeof (s_un.sun_path)) + slen = sizeof (s_un.sun_path) - 1; + memcpy (s_un.sun_path, + unixpath, + slen); + s_un.sun_path[slen] = '\0'; + slen = sizeof (struct sockaddr_un); +#if LINUX + s_un.sun_path[0] = '\0'; +#endif +#if HAVE_SOCKADDR_IN_SIN_LEN + s_un.sun_len = (u_char) slen; +#endif + if (GNUNET_OK != + GNUNET_NETWORK_socket_bind (sock, + (const struct sockaddr*) &s_un, + slen)) + { + /* failed to bind => service must be running */ + GNUNET_free (unixpath); + (void) GNUNET_NETWORK_socket_close (sock); + GNUNET_SCHEDULER_add_continuation (task, + task_cls, + GNUNET_SCHEDULER_REASON_PREREQ_DONE); + return; + } + (void) GNUNET_NETWORK_socket_close (sock); + } + /* let's try IP */ + } + } + GNUNET_free_non_null (unixpath); + } +#endif + + hostname = NULL; + if ((GNUNET_OK != + GNUNET_CONFIGURATION_get_value_number (cfg, + service, + "PORT", + &port)) || + (port > 65535) || + (GNUNET_OK != + GNUNET_CONFIGURATION_get_value_string (cfg, + service, + "HOSTNAME", &hostname))) + { + /* UNIXPATH failed (if possible) AND IP failed => error */ + service_test_error (task, task_cls); + return; + } + + if (0 == strcmp ("localhost", hostname)) + { + /* can test using 'bind' */ + struct sockaddr_in s_in; + + memset (&s_in, 0, sizeof (s_in)); +#if HAVE_SOCKADDR_IN_SIN_LEN + s_in.sin_len = saddrlens[1]; +#endif + s_in.sin_family = AF_INET; + s_in.sin_port = htons (port); + + sock = GNUNET_NETWORK_socket_create (AF_INET, SOCK_STREAM, 0); + if (sock != NULL) + { + if (GNUNET_OK != + GNUNET_NETWORK_socket_bind (sock, + (const struct sockaddr*) &s_in, + sizeof (s_in))) + { + /* failed to bind => service must be running */ + GNUNET_free (hostname); + (void) GNUNET_NETWORK_socket_close (sock); + GNUNET_SCHEDULER_add_continuation (task, + task_cls, + GNUNET_SCHEDULER_REASON_PREREQ_DONE); + return; + } + (void) GNUNET_NETWORK_socket_close (sock); + } + } + + if (0 == strcmp ("ip6-localhost", hostname)) + { + /* can test using 'bind' */ + struct sockaddr_in6 s_in6; + + memset (&s_in6, 0, sizeof (s_in6)); +#if HAVE_SOCKADDR_IN_SIN_LEN + s_in6.sin6_len = saddrlens[1]; +#endif + s_in6.sin6_family = AF_INET6; + s_in6.sin6_port = htons (port); + + sock = GNUNET_NETWORK_socket_create (AF_INET6, SOCK_STREAM, 0); + if (sock != NULL) + { + if (GNUNET_OK != + GNUNET_NETWORK_socket_bind (sock, + (const struct sockaddr*) &s_in6, + sizeof (s_in6))) + { + /* failed to bind => service must be running */ + GNUNET_free (hostname); + (void) GNUNET_NETWORK_socket_close (sock); + GNUNET_SCHEDULER_add_continuation (task, + task_cls, + GNUNET_SCHEDULER_REASON_PREREQ_DONE); + return; + } + (void) GNUNET_NETWORK_socket_close (sock); + } + } + + if ( (0 == strcmp ("localhost", hostname)) || + (0 == strcmp ("ip6-localhost", hostname)) ) + { + /* all binds succeeded => claim service not running right now */ + GNUNET_free_non_null (hostname); + service_test_error (task, task_cls); + return; + } + GNUNET_free_non_null (hostname); + + /* non-localhost, try 'connect' method */ conn = GNUNET_CLIENT_connect (service, cfg); if (conn == NULL) { diff --git a/src/util/network.c b/src/util/network.c index 9c6d0c90b..98eeb7b0c 100644 --- a/src/util/network.c +++ b/src/util/network.c @@ -321,7 +321,7 @@ GNUNET_NETWORK_socket_bind (struct GNUNET_NETWORK_Handle *desc, SetErrnoFromWinsockError (WSAGetLastError ()); #endif if (ret != 0) - return GNUNET_SYSERR; + return GNUNET_SYSERR; #ifndef MINGW #ifndef LINUX desc->addr = GNUNET_malloc (address_len);