aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChristian Grothoff <christian@grothoff.org>2016-05-31 18:58:59 +0200
committerChristian Grothoff <christian@grothoff.org>2016-05-31 18:58:59 +0200
commitbc7c9e686ed24acc8d690f32d184c516858c77a1 (patch)
treeadcfbb19cd7470cdafc794ab676dc3646b510d2f
parenteb1fdc105cf2ea0afe3922e06b614622294d017f (diff)
implementing #3474
-rw-r--r--doc/taler-exchange-httpd.111
-rw-r--r--src/exchange/taler-exchange-httpd.c150
-rw-r--r--src/exchange/taler-exchange-httpd_keystate.c4
3 files changed, 121 insertions, 44 deletions
diff --git a/doc/taler-exchange-httpd.1 b/doc/taler-exchange-httpd.1
index 959f1ec76..2f0c37f6a 100644
--- a/doc/taler-exchange-httpd.1
+++ b/doc/taler-exchange-httpd.1
@@ -32,6 +32,17 @@ This option is only available if the exchange was compiled with the configure op
.IP \"-t SECONDS, \-\-timeout=SECONDS"
Specifies the number of SECONDS after which the HTTPD should close (idle) HTTP connections.
+.SH SIGNALS
+.B
+.IP SIGUSR1
+Sending a SIGUSR1 to the process will cause it to reload denomination and signing keys.
+.B
+.IP SIGTERM
+Sending a SIGTERM to the process will cause it to shutdown cleanly.
+.B
+.IP SIGHUP
+Sending a SIGHUP to the process will cause it to re-execute the taler\-exchange\-httpd binary in the PATH, passing it the existing listen socket. Then the old server process will automatically exit after it is done handling existing client connections; the new server process will accept and handle new client connections.
+
.SH BUGS
Report bugs by using Mantis <https://gnunet.org/bugs/> or by sending electronic mail to <taler@gnu.org>
diff --git a/src/exchange/taler-exchange-httpd.c b/src/exchange/taler-exchange-httpd.c
index 5f9f51422..45935f172 100644
--- a/src/exchange/taler-exchange-httpd.c
+++ b/src/exchange/taler-exchange-httpd.c
@@ -742,6 +742,9 @@ main (int argc,
GNUNET_GETOPT_OPTION_END
};
int ret;
+ const char *listen_pid;
+ const char *listen_fds;
+ int fh = -1;
GNUNET_assert (GNUNET_OK ==
GNUNET_log_setup ("taler-exchange-httpd",
@@ -755,7 +758,8 @@ main (int argc,
if (NULL == cfgfile)
cfgfile = GNUNET_strdup (GNUNET_OS_project_data_get ()->user_config_file);
cfg = GNUNET_CONFIGURATION_create ();
- if (GNUNET_SYSERR == GNUNET_CONFIGURATION_load (cfg, cfgfile))
+ if (GNUNET_SYSERR ==
+ GNUNET_CONFIGURATION_load (cfg, cfgfile))
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
_("Malformed configuration file `%s', exit ...\n"),
@@ -768,11 +772,34 @@ main (int argc,
exchange_serve_process_config ())
return 1;
- if (NULL != serve_unixpath)
+ /* check for systemd-style FD passing */
+ listen_pid = getenv ("LISTEN_PID");
+ listen_fds = getenv ("LISTEN_FDS");
+ if ( (NULL != listen_pid) &&
+ (NULL != listen_fds) &&
+ (getpid() == strtol (listen_pid, NULL, 10)) &&
+ (1 == strtoul (listen_fds, NULL, 10)) /* we support only 1 socket */)
+ {
+ int flags;
+
+ fh = 3;
+ flags = fcntl (fh, F_GETFD);
+ if ( (-1 == flags) && (EBADF == errno) )
+ {
+ fprintf (stderr,
+ "Bad listen socket passed, ignored\n");
+ fh = -1;
+ }
+ flags |= FD_CLOEXEC;
+ fcntl (fh, F_SETFD, flags);
+ }
+
+ /* consider unix path */
+ if ( (-1 == fh) &&
+ (NULL != serve_unixpath) )
{
struct GNUNET_NETWORK_Handle *nh;
struct sockaddr_un *un;
- int fh;
if (sizeof (un->sun_path) <= strlen (serve_unixpath))
{
@@ -782,9 +809,11 @@ main (int argc,
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
"Creating listen socket '%s' with mode %o\n",
- serve_unixpath, unixpath_mode);
+ serve_unixpath,
+ unixpath_mode);
- if (GNUNET_OK != GNUNET_DISK_directory_create_for_file (serve_unixpath))
+ if (GNUNET_OK !=
+ GNUNET_DISK_directory_create_for_file (serve_unixpath))
{
GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR,
"mkdir",
@@ -793,23 +822,28 @@ main (int argc,
un = GNUNET_new (struct sockaddr_un);
un->sun_family = AF_UNIX;
- strncpy (un->sun_path, serve_unixpath, sizeof (un->sun_path) - 1);
+ strncpy (un->sun_path,
+ serve_unixpath,
+ sizeof (un->sun_path) - 1);
GNUNET_NETWORK_unix_precheck (un);
if (NULL == (nh = GNUNET_NETWORK_socket_create (AF_UNIX, SOCK_STREAM, 0)))
{
- fprintf (stderr, "create failed for AF_UNIX\n");
+ fprintf (stderr,
+ "create failed for AF_UNIX\n");
return 1;
}
if (GNUNET_OK != GNUNET_NETWORK_socket_bind (nh, (void *) un, sizeof (struct sockaddr_un)))
{
- fprintf (stderr, "bind failed for AF_UNIX\n");
+ fprintf (stderr,
+ "bind failed for AF_UNIX\n");
return 1;
}
if (GNUNET_OK != GNUNET_NETWORK_socket_listen (nh, UNIX_BACKLOG))
{
- fprintf (stderr, "listen failed for AF_UNIX\n");
+ fprintf (stderr,
+ "listen failed for AF_UNIX\n");
return 1;
}
@@ -817,41 +851,31 @@ main (int argc,
if (0 != chmod (serve_unixpath, unixpath_mode))
{
- fprintf (stderr, "chmod failed: %s\n", strerror (errno));
+ fprintf (stderr,
+ "chmod failed: %s\n",
+ strerror (errno));
return 1;
}
- GNUNET_log (GNUNET_ERROR_TYPE_INFO, "set socket '%s' to mode %o\n", serve_unixpath, unixpath_mode);
-
- mydaemon = MHD_start_daemon (MHD_USE_SELECT_INTERNALLY | MHD_USE_DEBUG,
- 0,
- NULL, NULL,
- &handle_mhd_request, NULL,
- MHD_OPTION_LISTEN_SOCKET, fh,
- MHD_OPTION_EXTERNAL_LOGGER, &handle_mhd_logs, NULL,
- MHD_OPTION_NOTIFY_COMPLETED, &handle_mhd_completion_callback, NULL,
- MHD_OPTION_CONNECTION_TIMEOUT, connection_timeout,
-#if HAVE_DEVELOPER
- MHD_OPTION_NOTIFY_CONNECTION, &connection_done, NULL,
-#endif
- MHD_OPTION_END);
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "set socket '%s' to mode %o\n",
+ serve_unixpath,
+ unixpath_mode);
GNUNET_NETWORK_socket_free_memory_only_ (nh);
}
- else
- {
- // FIXME: refactor two calls to MHD_start_daemon
- // into one, using an options array instead of varags
- mydaemon = MHD_start_daemon (MHD_USE_SELECT_INTERNALLY | MHD_USE_DEBUG,
- serve_port,
- NULL, NULL,
- &handle_mhd_request, NULL,
- MHD_OPTION_EXTERNAL_LOGGER, &handle_mhd_logs, NULL,
- MHD_OPTION_NOTIFY_COMPLETED, &handle_mhd_completion_callback, NULL,
- MHD_OPTION_CONNECTION_TIMEOUT, connection_timeout,
+
+
+ mydaemon = MHD_start_daemon (MHD_USE_SELECT_INTERNALLY | MHD_USE_PIPE_FOR_SHUTDOWN | MHD_USE_DEBUG,
+ (-1 == fh) ? serve_port : 0,
+ NULL, NULL,
+ &handle_mhd_request, NULL,
+ MHD_OPTION_LISTEN_SOCKET, fh,
+ MHD_OPTION_EXTERNAL_LOGGER, &handle_mhd_logs, NULL,
+ MHD_OPTION_NOTIFY_COMPLETED, &handle_mhd_completion_callback, NULL,
+ MHD_OPTION_CONNECTION_TIMEOUT, connection_timeout,
#if HAVE_DEVELOPER
- MHD_OPTION_NOTIFY_CONNECTION, &connection_done, NULL,
+ MHD_OPTION_NOTIFY_CONNECTION, &connection_done, NULL,
#endif
- MHD_OPTION_END);
- }
+ MHD_OPTION_END);
if (NULL == mydaemon)
{
@@ -884,14 +908,56 @@ main (int argc,
case GNUNET_NO:
{
MHD_socket sock = MHD_quiesce_daemon (mydaemon);
-
- /* FIXME #3474: fork another MHD, passing on the listen socket! */
+ pid_t chld;
+ int flags;
+
+ /* Set flags to make 'sock' inherited by child */
+ flags = fcntl (sock, F_GETFD);
+ GNUNET_assert (-1 != flags);
+ flags &= ~FD_CLOEXEC;
+ GNUNET_assert (-1 != fcntl (sock, F_SETFD, flags));
+ chld = fork ();
+ if (-1 == chld)
+ {
+ /* fork() failed, continue clean up, unhappily */
+ GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR,
+ "fork");
+ }
+ if (0 == chld)
+ {
+ char pids[12];
+
+ /* exec another taler-exchange-httpd, passing on the listen socket;
+ as in systemd it is expected to be on FD #3 */
+ if (3 != dup2 (sock, 3))
+ {
+ GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR,
+ "dup2");
+ _exit (1);
+ }
+ /* Tell the child that it is the desired recipient for FD #3 */
+ GNUNET_snprintf (pids,
+ sizeof (pids),
+ "%u",
+ getpid ());
+ setenv ("LISTEN_PID", pids, 1);
+ setenv ("LISTEN_FDS", "1", 1);
+ /* Finally, exec the (presumably) more recent exchange binary */
+ execvp ("taler-exchange-httpd",
+ argv);
+ GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR,
+ "execvp");
+ _exit (1);
+ }
+ /* we're the original process, handle remaining contextions
+ before exiting; as the listen socket is no longer used,
+ close it here */
+ GNUNET_break (0 == close (sock));
while (0 != MHD_get_daemon_info (mydaemon,
MHD_DAEMON_INFO_CURRENT_CONNECTIONS)->num_connections)
sleep (1);
+ /* Now we're really done, practice clean shutdown */
MHD_stop_daemon (mydaemon);
-
- close (sock); /* FIXME: done like this because #3474 is open */
}
break;
default:
diff --git a/src/exchange/taler-exchange-httpd_keystate.c b/src/exchange/taler-exchange-httpd_keystate.c
index a71d7676a..3649bf436 100644
--- a/src/exchange/taler-exchange-httpd_keystate.c
+++ b/src/exchange/taler-exchange-httpd_keystate.c
@@ -903,8 +903,8 @@ TMH_KS_loop (void)
sigchld = GNUNET_SIGNAL_handler_install (SIGCHLD,
&handle_sigchld);
- ret = 0;
- while (0 == ret)
+ ret = GNUNET_OK;
+ while (GNUNET_OK == ret)
{
char c;
ssize_t res;