implementing #3474
This commit is contained in:
parent
eb1fdc105c
commit
bc7c9e686e
@ -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>
|
||||||
|
|
||||||
|
@ -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:
|
||||||
|
@ -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;
|
||||||
|
Loading…
Reference in New Issue
Block a user