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"
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>

View File

@ -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);
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,
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:

View File

@ -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;