implementing #3474

This commit is contained in:
Christian Grothoff 2016-05-31 18:58:59 +02:00
parent eb1fdc105c
commit bc7c9e686e
3 changed files with 120 additions and 43 deletions

View File

@ -32,6 +32,17 @@ This option is only available if the exchange was compiled with the configure op
.IP \"-t SECONDS, \-\-timeout=SECONDS" .IP \"-t SECONDS, \-\-timeout=SECONDS"
Specifies the number of SECONDS after which the HTTPD should close (idle) HTTP connections. 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 .SH BUGS
Report bugs by using Mantis <https://gnunet.org/bugs/> or by sending electronic mail to <taler@gnu.org> Report bugs by using Mantis <https://gnunet.org/bugs/> or by sending electronic mail to <taler@gnu.org>

View File

@ -742,6 +742,9 @@ main (int argc,
GNUNET_GETOPT_OPTION_END GNUNET_GETOPT_OPTION_END
}; };
int ret; int ret;
const char *listen_pid;
const char *listen_fds;
int fh = -1;
GNUNET_assert (GNUNET_OK == GNUNET_assert (GNUNET_OK ==
GNUNET_log_setup ("taler-exchange-httpd", GNUNET_log_setup ("taler-exchange-httpd",
@ -755,7 +758,8 @@ main (int argc,
if (NULL == cfgfile) if (NULL == cfgfile)
cfgfile = GNUNET_strdup (GNUNET_OS_project_data_get ()->user_config_file); cfgfile = GNUNET_strdup (GNUNET_OS_project_data_get ()->user_config_file);
cfg = GNUNET_CONFIGURATION_create (); 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, GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
_("Malformed configuration file `%s', exit ...\n"), _("Malformed configuration file `%s', exit ...\n"),
@ -768,11 +772,34 @@ main (int argc,
exchange_serve_process_config ()) exchange_serve_process_config ())
return 1; 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 GNUNET_NETWORK_Handle *nh;
struct sockaddr_un *un; struct sockaddr_un *un;
int fh;
if (sizeof (un->sun_path) <= strlen (serve_unixpath)) if (sizeof (un->sun_path) <= strlen (serve_unixpath))
{ {
@ -782,9 +809,11 @@ main (int argc,
GNUNET_log (GNUNET_ERROR_TYPE_INFO, GNUNET_log (GNUNET_ERROR_TYPE_INFO,
"Creating listen socket '%s' with mode %o\n", "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, GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR,
"mkdir", "mkdir",
@ -793,23 +822,28 @@ main (int argc,
un = GNUNET_new (struct sockaddr_un); un = GNUNET_new (struct sockaddr_un);
un->sun_family = AF_UNIX; 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); GNUNET_NETWORK_unix_precheck (un);
if (NULL == (nh = GNUNET_NETWORK_socket_create (AF_UNIX, SOCK_STREAM, 0))) 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; return 1;
} }
if (GNUNET_OK != GNUNET_NETWORK_socket_bind (nh, (void *) un, sizeof (struct sockaddr_un))) 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; return 1;
} }
if (GNUNET_OK != GNUNET_NETWORK_socket_listen (nh, UNIX_BACKLOG)) 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; return 1;
} }
@ -817,41 +851,31 @@ main (int argc,
if (0 != chmod (serve_unixpath, unixpath_mode)) 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; return 1;
} }
GNUNET_log (GNUNET_ERROR_TYPE_INFO, "set socket '%s' to mode %o\n", serve_unixpath, unixpath_mode); GNUNET_log (GNUNET_ERROR_TYPE_INFO,
"set socket '%s' to mode %o\n",
mydaemon = MHD_start_daemon (MHD_USE_SELECT_INTERNALLY | MHD_USE_DEBUG, serve_unixpath,
0, unixpath_mode);
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_NETWORK_socket_free_memory_only_ (nh); GNUNET_NETWORK_socket_free_memory_only_ (nh);
} }
else
{
// FIXME: refactor two calls to MHD_start_daemon mydaemon = MHD_start_daemon (MHD_USE_SELECT_INTERNALLY | MHD_USE_PIPE_FOR_SHUTDOWN | MHD_USE_DEBUG,
// into one, using an options array instead of varags (-1 == fh) ? serve_port : 0,
mydaemon = MHD_start_daemon (MHD_USE_SELECT_INTERNALLY | MHD_USE_DEBUG, NULL, NULL,
serve_port, &handle_mhd_request, NULL,
NULL, NULL, MHD_OPTION_LISTEN_SOCKET, fh,
&handle_mhd_request, NULL, MHD_OPTION_EXTERNAL_LOGGER, &handle_mhd_logs, NULL,
MHD_OPTION_EXTERNAL_LOGGER, &handle_mhd_logs, NULL, MHD_OPTION_NOTIFY_COMPLETED, &handle_mhd_completion_callback, NULL,
MHD_OPTION_NOTIFY_COMPLETED, &handle_mhd_completion_callback, NULL, MHD_OPTION_CONNECTION_TIMEOUT, connection_timeout,
MHD_OPTION_CONNECTION_TIMEOUT, connection_timeout,
#if HAVE_DEVELOPER #if HAVE_DEVELOPER
MHD_OPTION_NOTIFY_CONNECTION, &connection_done, NULL, MHD_OPTION_NOTIFY_CONNECTION, &connection_done, NULL,
#endif #endif
MHD_OPTION_END); MHD_OPTION_END);
}
if (NULL == mydaemon) if (NULL == mydaemon)
{ {
@ -884,14 +908,56 @@ main (int argc,
case GNUNET_NO: case GNUNET_NO:
{ {
MHD_socket sock = MHD_quiesce_daemon (mydaemon); MHD_socket sock = MHD_quiesce_daemon (mydaemon);
pid_t chld;
int flags;
/* FIXME #3474: fork another MHD, passing on the listen socket! */ /* 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, while (0 != MHD_get_daemon_info (mydaemon,
MHD_DAEMON_INFO_CURRENT_CONNECTIONS)->num_connections) MHD_DAEMON_INFO_CURRENT_CONNECTIONS)->num_connections)
sleep (1); sleep (1);
/* Now we're really done, practice clean shutdown */
MHD_stop_daemon (mydaemon); MHD_stop_daemon (mydaemon);
close (sock); /* FIXME: done like this because #3474 is open */
} }
break; break;
default: default:

View File

@ -903,8 +903,8 @@ TMH_KS_loop (void)
sigchld = GNUNET_SIGNAL_handler_install (SIGCHLD, sigchld = GNUNET_SIGNAL_handler_install (SIGCHLD,
&handle_sigchld); &handle_sigchld);
ret = 0; ret = GNUNET_OK;
while (0 == ret) while (GNUNET_OK == ret)
{ {
char c; char c;
ssize_t res; ssize_t res;