681 lines
17 KiB
C
681 lines
17 KiB
C
|
/* gcryptrnd.c - Libgcrypt Random Number Daemon
|
|||
|
* Copyright (C) 2006 Free Software Foundation, Inc.
|
|||
|
*
|
|||
|
* Gcryptend is free software; you can redistribute it and/or modify
|
|||
|
* it under the terms of the GNU General Public License as published
|
|||
|
* by the Free Software Foundation; either version 2 of the License,
|
|||
|
* or (at your option) any later version.
|
|||
|
*
|
|||
|
* Gcryptrnd is distributed in the hope that it will be useful, but
|
|||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
|||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|||
|
* General Public License for more details.
|
|||
|
*
|
|||
|
* You should have received a copy of the GNU General Public License
|
|||
|
* along with this program; if not, write to the Free Software
|
|||
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
|||
|
* 02110-1301, USA.
|
|||
|
*/
|
|||
|
|
|||
|
/* We require vsyslog pth
|
|||
|
We need to test for: setrlimit
|
|||
|
|
|||
|
We should also prioritize requests. This is best done by putting
|
|||
|
the requests into queues and have a main thread processing these
|
|||
|
queues.
|
|||
|
|
|||
|
*/
|
|||
|
|
|||
|
#include <config.h>
|
|||
|
#include <stdio.h>
|
|||
|
#include <stddef.h>
|
|||
|
#include <stdlib.h>
|
|||
|
#include <assert.h>
|
|||
|
#include <time.h>
|
|||
|
#include <sys/times.h>
|
|||
|
#include <sys/types.h>
|
|||
|
#include <sys/stat.h>
|
|||
|
#include <stdarg.h>
|
|||
|
#include <syslog.h>
|
|||
|
#include <sys/socket.h>
|
|||
|
#include <sys/un.h>
|
|||
|
#include <unistd.h>
|
|||
|
#include <errno.h>
|
|||
|
#include <pth.h>
|
|||
|
#include <gcrypt.h>
|
|||
|
|
|||
|
#define PGM "gcryptrnd"
|
|||
|
#define MYVERSION_LINE PGM " (Libgcrypt) " VERSION
|
|||
|
#define BUGREPORT_LINE "\nReport bugs to <bug-libgcrypt@gnupg.org>.\n"
|
|||
|
|
|||
|
/* Pth wrapper function definitions. */
|
|||
|
GCRY_THREAD_OPTION_PTH_IMPL;
|
|||
|
|
|||
|
|
|||
|
/* Flag set to true if we have been daemonized. */
|
|||
|
static int running_detached;
|
|||
|
/* Flag indicating that a shutdown has been requested. */
|
|||
|
static int shutdown_pending;
|
|||
|
/* Counter for active connections. */
|
|||
|
static int active_connections;
|
|||
|
|
|||
|
|
|||
|
|
|||
|
/* Local prototypes. */
|
|||
|
static void serve (int listen_fd);
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
/* To avoid that a compiler optimizes certain memset calls away, these
|
|||
|
macros may be used instead. */
|
|||
|
#define wipememory2(_ptr,_set,_len) do { \
|
|||
|
volatile char *_vptr=(volatile char *)(_ptr); \
|
|||
|
size_t _vlen=(_len); \
|
|||
|
while(_vlen) { *_vptr=(_set); _vptr++; _vlen--; } \
|
|||
|
} while(0)
|
|||
|
#define wipememory(_ptr,_len) wipememory2(_ptr,0,_len)
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
/* Error printing utility. PRIORITY should be one of syslog's
|
|||
|
priority levels. This functions prints to the stderr or syslog
|
|||
|
depending on whether we are already daemonized. */
|
|||
|
static void
|
|||
|
logit (int priority, const char *format, ...)
|
|||
|
{
|
|||
|
va_list arg_ptr;
|
|||
|
|
|||
|
va_start (arg_ptr, format) ;
|
|||
|
if (running_detached)
|
|||
|
{
|
|||
|
vsyslog (priority, format, arg_ptr);
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
fputs (PGM ": ", stderr);
|
|||
|
vfprintf (stderr, format, arg_ptr);
|
|||
|
putc ('\n', stderr);
|
|||
|
}
|
|||
|
va_end (arg_ptr);
|
|||
|
}
|
|||
|
|
|||
|
/* Callback used by libgcrypt for logging. */
|
|||
|
static void
|
|||
|
my_gcry_logger (void *dummy, int level, const char *format, va_list arg_ptr)
|
|||
|
{
|
|||
|
(void)dummy;
|
|||
|
|
|||
|
/* Map the log levels. */
|
|||
|
switch (level)
|
|||
|
{
|
|||
|
case GCRY_LOG_CONT: level = LOG_INFO /* FIXME */; break;
|
|||
|
case GCRY_LOG_INFO: level = LOG_INFO; break;
|
|||
|
case GCRY_LOG_WARN: level = LOG_WARNING; break;
|
|||
|
case GCRY_LOG_ERROR:level = LOG_ERR; break;
|
|||
|
case GCRY_LOG_FATAL:level = LOG_CRIT; break;
|
|||
|
case GCRY_LOG_BUG: level = LOG_CRIT; break;
|
|||
|
case GCRY_LOG_DEBUG:level = LOG_DEBUG; break;
|
|||
|
default: level = LOG_ERR; break;
|
|||
|
}
|
|||
|
if (running_detached)
|
|||
|
{
|
|||
|
vsyslog (level, format, arg_ptr);
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
fputs (PGM ": ", stderr);
|
|||
|
vfprintf (stderr, format, arg_ptr);
|
|||
|
if (!*format || format[strlen (format)-1] != '\n')
|
|||
|
putc ('\n', stderr);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
/* The cleanup handler - used to wipe out the secure memory. */
|
|||
|
static void
|
|||
|
cleanup (void)
|
|||
|
{
|
|||
|
gcry_control (GCRYCTL_TERM_SECMEM );
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
/* Make us a daemon and open the syslog. */
|
|||
|
static void
|
|||
|
daemonize (void)
|
|||
|
{
|
|||
|
int i;
|
|||
|
pid_t pid;
|
|||
|
|
|||
|
fflush (NULL);
|
|||
|
|
|||
|
pid = fork ();
|
|||
|
if (pid == (pid_t)-1)
|
|||
|
{
|
|||
|
logit (LOG_CRIT, "fork failed: %s", strerror (errno));
|
|||
|
exit (1);
|
|||
|
}
|
|||
|
if (pid)
|
|||
|
exit (0);
|
|||
|
|
|||
|
if (setsid() == -1)
|
|||
|
{
|
|||
|
logit (LOG_CRIT, "setsid() failed: %s", strerror(errno));
|
|||
|
exit (1);
|
|||
|
}
|
|||
|
|
|||
|
signal (SIGHUP, SIG_IGN);
|
|||
|
|
|||
|
pid = fork ();
|
|||
|
if (pid == (pid_t)-1)
|
|||
|
{
|
|||
|
logit (LOG_CRIT, PGM ": second fork failed: %s", strerror (errno));
|
|||
|
exit (1);
|
|||
|
}
|
|||
|
if (pid)
|
|||
|
exit (0); /* First child exits. */
|
|||
|
|
|||
|
running_detached = 1;
|
|||
|
|
|||
|
if (chdir("/"))
|
|||
|
{
|
|||
|
logit (LOG_CRIT, "chdir(\"/\") failed: %s", strerror (errno));
|
|||
|
exit (1);
|
|||
|
}
|
|||
|
umask (0);
|
|||
|
|
|||
|
for (i=0; i <= 2; i++)
|
|||
|
close (i);
|
|||
|
|
|||
|
openlog (PGM, LOG_PID, LOG_DAEMON);
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
static void
|
|||
|
disable_core_dumps (void)
|
|||
|
{
|
|||
|
#ifdef HAVE_SETRLIMIT
|
|||
|
struct rlimit limit;
|
|||
|
|
|||
|
if (getrlimit (RLIMIT_CORE, &limit))
|
|||
|
limit.rlim_max = 0;
|
|||
|
limit.rlim_cur = 0;
|
|||
|
if( !setrlimit (RLIMIT_CORE, &limit) )
|
|||
|
return 0;
|
|||
|
if (errno != EINVAL && errno != ENOSYS)
|
|||
|
logit (LOG_ERR, "can't disable core dumps: %s\n", strerror (errno));
|
|||
|
#endif /* HAVE_SETRLIMIT */
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
static void
|
|||
|
print_version (int with_help)
|
|||
|
{
|
|||
|
fputs (MYVERSION_LINE "\n"
|
|||
|
"Copyright (C) 2006 Free Software Foundation, Inc.\n"
|
|||
|
"License GPLv2+: GNU GPL version 2 or later "
|
|||
|
"<http://www.gnu.org/licenses/old-licenses/gpl-2.0.html>\n"
|
|||
|
"This is free software: you are free to change and redistribute it.\n"
|
|||
|
"There is NO WARRANTY, to the extent permitted by law.\n",
|
|||
|
stdout);
|
|||
|
|
|||
|
if (with_help)
|
|||
|
fputs ("\n"
|
|||
|
"Usage: " PGM " [OPTIONS] [SOCKETNAME]\n"
|
|||
|
"Start Libgcrypt's random number daemon listening"
|
|||
|
" on socket SOCKETNAME\n"
|
|||
|
"SOCKETNAME defaults to XXX\n"
|
|||
|
"\n"
|
|||
|
" --no-detach do not deatach from the console\n"
|
|||
|
" --version print version of the program and exit\n"
|
|||
|
" --help display this help and exit\n"
|
|||
|
BUGREPORT_LINE, stdout );
|
|||
|
|
|||
|
exit (0);
|
|||
|
}
|
|||
|
|
|||
|
static int
|
|||
|
print_usage (void)
|
|||
|
{
|
|||
|
fputs ("usage: " PGM " [OPTIONS] [SOCKETNAME]\n", stderr);
|
|||
|
fputs (" (use --help to display options)\n", stderr);
|
|||
|
exit (1);
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
int
|
|||
|
main (int argc, char **argv)
|
|||
|
{
|
|||
|
int no_detach = 0;
|
|||
|
gpg_error_t err;
|
|||
|
struct sockaddr_un *srvr_addr;
|
|||
|
socklen_t addrlen;
|
|||
|
int fd;
|
|||
|
int rc;
|
|||
|
const char *socketname = "/var/run/libgcrypt/S.gcryptrnd";
|
|||
|
|
|||
|
|
|||
|
if (argc)
|
|||
|
{
|
|||
|
argc--; argv++;
|
|||
|
}
|
|||
|
while (argc && **argv == '-' && (*argv)[1] == '-')
|
|||
|
{
|
|||
|
if (!(*argv)[2])
|
|||
|
{
|
|||
|
argc--; argv++;
|
|||
|
break;
|
|||
|
}
|
|||
|
else if (!strcmp (*argv, "--version"))
|
|||
|
print_version (0);
|
|||
|
else if (!strcmp (*argv, "--help"))
|
|||
|
print_version (1);
|
|||
|
else if (!strcmp (*argv, "--no-detach"))
|
|||
|
{
|
|||
|
no_detach = 1;
|
|||
|
argc--; argv++;
|
|||
|
}
|
|||
|
else
|
|||
|
print_usage ();
|
|||
|
}
|
|||
|
|
|||
|
if (argc == 1)
|
|||
|
socketname = argv[0];
|
|||
|
else if (argc > 1)
|
|||
|
print_usage ();
|
|||
|
|
|||
|
if (!no_detach)
|
|||
|
daemonize ();
|
|||
|
|
|||
|
signal (SIGPIPE, SIG_IGN);
|
|||
|
|
|||
|
logit (LOG_NOTICE, "started version " VERSION );
|
|||
|
|
|||
|
/* Libgcrypt requires us to register the threading model before we
|
|||
|
do anything else with it. Note that this also calls pth_init. We
|
|||
|
do the initialization while already running as a daemon to avoid
|
|||
|
overhead with double initialization of Libgcrypt. */
|
|||
|
err = gcry_control (GCRYCTL_SET_THREAD_CBS, &gcry_threads_pth);
|
|||
|
if (err)
|
|||
|
{
|
|||
|
logit (LOG_CRIT, "can't register GNU Pth with Libgcrypt: %s",
|
|||
|
gpg_strerror (err));
|
|||
|
exit (1);
|
|||
|
}
|
|||
|
|
|||
|
/* Check that the libgcrypt version is sufficient. */
|
|||
|
if (!gcry_check_version (VERSION) )
|
|||
|
{
|
|||
|
logit (LOG_CRIT, "libgcrypt is too old (need %s, have %s)",
|
|||
|
VERSION, gcry_check_version (NULL) );
|
|||
|
exit (1);
|
|||
|
}
|
|||
|
|
|||
|
/* Register the logging callback and tell Libcgrypt to put the
|
|||
|
random pool into secure memory. */
|
|||
|
gcry_set_log_handler (my_gcry_logger, NULL);
|
|||
|
gcry_control (GCRYCTL_USE_SECURE_RNDPOOL);
|
|||
|
|
|||
|
/* Obviously we don't want to allow any core dumps. */
|
|||
|
disable_core_dumps ();
|
|||
|
|
|||
|
/* Initialize the secure memory stuff which will also drop any extra
|
|||
|
privileges we have. */
|
|||
|
gcry_control (GCRYCTL_INIT_SECMEM, 16384, 0);
|
|||
|
|
|||
|
/* Register a cleanup handler. */
|
|||
|
atexit (cleanup);
|
|||
|
|
|||
|
/* Create and listen on the socket. */
|
|||
|
fd = socket (AF_UNIX, SOCK_STREAM, 0);
|
|||
|
if (fd == -1)
|
|||
|
{
|
|||
|
logit (LOG_CRIT, "can't create socket: %s", strerror (errno));
|
|||
|
exit (1);
|
|||
|
}
|
|||
|
srvr_addr = gcry_xmalloc (sizeof *srvr_addr);
|
|||
|
memset (srvr_addr, 0, sizeof *srvr_addr);
|
|||
|
srvr_addr->sun_family = AF_UNIX;
|
|||
|
if (strlen (socketname) + 1 >= sizeof (srvr_addr->sun_path))
|
|||
|
{
|
|||
|
logit (LOG_CRIT, "socket name `%s' too long", socketname);
|
|||
|
exit (1);
|
|||
|
}
|
|||
|
strcpy (srvr_addr->sun_path, socketname);
|
|||
|
addrlen = (offsetof (struct sockaddr_un, sun_path)
|
|||
|
+ strlen (srvr_addr->sun_path) + 1);
|
|||
|
rc = bind (fd, (struct sockaddr*) srvr_addr, addrlen);
|
|||
|
if (rc == -1 && errno == EADDRINUSE)
|
|||
|
{
|
|||
|
remove (socketname);
|
|||
|
rc = bind (fd, (struct sockaddr*) srvr_addr, addrlen);
|
|||
|
}
|
|||
|
if (rc == -1)
|
|||
|
{
|
|||
|
logit (LOG_CRIT, "error binding socket to `%s': %s",
|
|||
|
srvr_addr->sun_path, strerror (errno));
|
|||
|
close (fd);
|
|||
|
exit (1);
|
|||
|
}
|
|||
|
|
|||
|
if (listen (fd, 5 ) == -1)
|
|||
|
{
|
|||
|
logit (LOG_CRIT, "listen() failed: %s", strerror (errno));
|
|||
|
close (fd);
|
|||
|
exit (1);
|
|||
|
}
|
|||
|
|
|||
|
logit (LOG_INFO, "listening on socket `%s', fd=%d",
|
|||
|
srvr_addr->sun_path, fd);
|
|||
|
|
|||
|
serve (fd);
|
|||
|
close (fd);
|
|||
|
|
|||
|
logit (LOG_NOTICE, "stopped version " VERSION );
|
|||
|
return 0;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
/* Send LENGTH bytes of BUFFER to file descriptor FD. Returns 0 on
|
|||
|
success or another value on write error. */
|
|||
|
static int
|
|||
|
writen (int fd, const void *buffer, size_t length)
|
|||
|
{
|
|||
|
while (length)
|
|||
|
{
|
|||
|
ssize_t n = pth_write (fd, buffer, length);
|
|||
|
if (n < 0)
|
|||
|
{
|
|||
|
logit (LOG_ERR, "connection %d: write error: %s",
|
|||
|
fd, strerror (errno));
|
|||
|
return -1; /* write error */
|
|||
|
}
|
|||
|
length -= n;
|
|||
|
buffer = (const char*)buffer + n;
|
|||
|
}
|
|||
|
return 0; /* Okay */
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
/* Send an error response back. Returns 0 on success. */
|
|||
|
static int
|
|||
|
send_error (int fd, int errcode)
|
|||
|
{
|
|||
|
unsigned char buf[2];
|
|||
|
|
|||
|
buf[0] = errcode;
|
|||
|
buf[1] = 0;
|
|||
|
return writen (fd, buf, 2 );
|
|||
|
}
|
|||
|
|
|||
|
/* Send a pong response back. Returns 0 on success or another value
|
|||
|
on write error. */
|
|||
|
static int
|
|||
|
send_pong (int fd)
|
|||
|
{
|
|||
|
return writen (fd, "\x00\x04pong", 6);
|
|||
|
}
|
|||
|
|
|||
|
/* Send a nonce of size LENGTH back. Return 0 on success. */
|
|||
|
static int
|
|||
|
send_nonce (int fd, int length)
|
|||
|
{
|
|||
|
unsigned char buf[2+255];
|
|||
|
int rc;
|
|||
|
|
|||
|
assert (length >= 0 && length <= 255);
|
|||
|
buf[0] = 0;
|
|||
|
buf[1] = length;
|
|||
|
gcry_create_nonce (buf+2, length);
|
|||
|
rc = writen (fd, buf, 2+length );
|
|||
|
wipememory (buf+2, length);
|
|||
|
return rc;
|
|||
|
}
|
|||
|
|
|||
|
/* Send a random of size LENGTH with quality LEVEL back. Return 0 on
|
|||
|
success. */
|
|||
|
static int
|
|||
|
send_random (int fd, int length, int level)
|
|||
|
{
|
|||
|
unsigned char buf[2+255];
|
|||
|
int rc;
|
|||
|
|
|||
|
assert (length >= 0 && length <= 255);
|
|||
|
assert (level == GCRY_STRONG_RANDOM || level == GCRY_VERY_STRONG_RANDOM);
|
|||
|
buf[0] = 0;
|
|||
|
buf[1] = length;
|
|||
|
/* Note that we don't bother putting the random stuff into secure
|
|||
|
memory because this daemon is anyway intended to be run under
|
|||
|
root and it is questionable whether the kernel buffers etc. are
|
|||
|
equally well protected. */
|
|||
|
gcry_randomize (buf+2, length, level);
|
|||
|
rc = writen (fd, buf, 2+length );
|
|||
|
wipememory (buf+2, length);
|
|||
|
return rc;
|
|||
|
}
|
|||
|
|
|||
|
/* Main processing loop for a connection.
|
|||
|
|
|||
|
A request is made up of:
|
|||
|
|
|||
|
1 byte Total length of request; must be 3
|
|||
|
1 byte Command
|
|||
|
0 = Ping
|
|||
|
10 = GetNonce
|
|||
|
11 = GetStrongRandom
|
|||
|
12 = GetVeryStrongRandom
|
|||
|
(all other values are reserved)
|
|||
|
1 byte Number of requested bytes.
|
|||
|
This is ignored for command Ping.
|
|||
|
|
|||
|
A response is made up of:
|
|||
|
|
|||
|
1 byte Error Code
|
|||
|
0 = Everything is fine
|
|||
|
1 = Bad Command
|
|||
|
0xff = Other error.
|
|||
|
(For a bad request the connection will simply be closed)
|
|||
|
1 byte Length of data
|
|||
|
n byte data
|
|||
|
|
|||
|
The requests are read as long as the connection is open.
|
|||
|
|
|||
|
|
|||
|
*/
|
|||
|
static void
|
|||
|
connection_loop (int fd)
|
|||
|
{
|
|||
|
unsigned char request[3];
|
|||
|
unsigned char *p;
|
|||
|
int nleft, n;
|
|||
|
int rc;
|
|||
|
|
|||
|
for (;;)
|
|||
|
{
|
|||
|
for (nleft=3, p=request; nleft > 0; )
|
|||
|
{
|
|||
|
n = pth_read (fd, p, nleft);
|
|||
|
if (!n && p == request)
|
|||
|
return; /* Client terminated connection. */
|
|||
|
if (n <= 0)
|
|||
|
{
|
|||
|
logit (LOG_ERR, "connection %d: read error: %s",
|
|||
|
fd, n? strerror (errno) : "Unexpected EOF");
|
|||
|
return;
|
|||
|
}
|
|||
|
p += n;
|
|||
|
nleft -= n;
|
|||
|
}
|
|||
|
if (request[0] != 3)
|
|||
|
{
|
|||
|
logit (LOG_ERR, "connection %d: invalid length (%d) of request",
|
|||
|
fd, request[0]);
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
switch (request[1])
|
|||
|
{
|
|||
|
case 0: /* Ping */
|
|||
|
rc = send_pong (fd);
|
|||
|
break;
|
|||
|
case 10: /* GetNonce */
|
|||
|
rc = send_nonce (fd, request[2]);
|
|||
|
break;
|
|||
|
case 11: /* GetStrongRandom */
|
|||
|
rc = send_random (fd, request[2], GCRY_STRONG_RANDOM);
|
|||
|
break;
|
|||
|
case 12: /* GetVeryStrongRandom */
|
|||
|
rc = send_random (fd, request[2], GCRY_VERY_STRONG_RANDOM);
|
|||
|
break;
|
|||
|
|
|||
|
default: /* Invalid command */
|
|||
|
rc = send_error (fd, 1);
|
|||
|
break;
|
|||
|
}
|
|||
|
if (rc)
|
|||
|
break; /* A write error occurred while sending the response. */
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
/* Entry point for a connection's thread. */
|
|||
|
static void *
|
|||
|
connection_thread (void *arg)
|
|||
|
{
|
|||
|
int fd = (int)arg;
|
|||
|
|
|||
|
active_connections++;
|
|||
|
logit (LOG_INFO, "connection handler for fd %d started", fd);
|
|||
|
|
|||
|
connection_loop (fd);
|
|||
|
|
|||
|
close (fd);
|
|||
|
logit (LOG_INFO, "connection handler for fd %d terminated", fd);
|
|||
|
active_connections--;
|
|||
|
|
|||
|
return NULL;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
/* This signal handler is called from the main loop between acepting
|
|||
|
connections. It is called on the regular stack, thus no special
|
|||
|
caution needs to be taken. It returns true to indicate that the
|
|||
|
process should terminate. */
|
|||
|
static int
|
|||
|
handle_signal (int signo)
|
|||
|
{
|
|||
|
switch (signo)
|
|||
|
{
|
|||
|
case SIGHUP:
|
|||
|
logit (LOG_NOTICE, "SIGHUP received - re-reading configuration");
|
|||
|
break;
|
|||
|
|
|||
|
case SIGUSR1:
|
|||
|
logit (LOG_NOTICE, "SIGUSR1 received - no action defined");
|
|||
|
break;
|
|||
|
|
|||
|
case SIGUSR2:
|
|||
|
logit (LOG_NOTICE, "SIGUSR2 received - no action defined");
|
|||
|
break;
|
|||
|
|
|||
|
case SIGTERM:
|
|||
|
if (!shutdown_pending)
|
|||
|
logit (LOG_NOTICE, "SIGTERM received - shutting down ...");
|
|||
|
else
|
|||
|
logit (LOG_NOTICE, "SIGTERM received - still %d active connections",
|
|||
|
active_connections);
|
|||
|
shutdown_pending++;
|
|||
|
if (shutdown_pending > 2)
|
|||
|
{
|
|||
|
logit (LOG_NOTICE, "shutdown forced");
|
|||
|
return 1;
|
|||
|
}
|
|||
|
break;
|
|||
|
|
|||
|
case SIGINT:
|
|||
|
logit (LOG_NOTICE, "SIGINT received - immediate shutdown");
|
|||
|
return 1;
|
|||
|
|
|||
|
default:
|
|||
|
logit (LOG_NOTICE, "signal %d received - no action defined\n", signo);
|
|||
|
}
|
|||
|
return 0;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
/* Main server loop. This is called with the FD of the listening
|
|||
|
socket. */
|
|||
|
static void
|
|||
|
serve (int listen_fd)
|
|||
|
{
|
|||
|
pth_attr_t tattr;
|
|||
|
pth_event_t ev;
|
|||
|
sigset_t sigs;
|
|||
|
int signo;
|
|||
|
struct sockaddr_un paddr;
|
|||
|
socklen_t plen = sizeof (paddr);
|
|||
|
int fd;
|
|||
|
|
|||
|
tattr = pth_attr_new();
|
|||
|
pth_attr_set (tattr, PTH_ATTR_JOINABLE, 0);
|
|||
|
pth_attr_set (tattr, PTH_ATTR_STACK_SIZE, 256*1024);
|
|||
|
pth_attr_set (tattr, PTH_ATTR_NAME, "connection");
|
|||
|
|
|||
|
sigemptyset (&sigs);
|
|||
|
sigaddset (&sigs, SIGHUP);
|
|||
|
sigaddset (&sigs, SIGUSR1);
|
|||
|
sigaddset (&sigs, SIGUSR2);
|
|||
|
sigaddset (&sigs, SIGINT);
|
|||
|
sigaddset (&sigs, SIGTERM);
|
|||
|
ev = pth_event (PTH_EVENT_SIGS, &sigs, &signo);
|
|||
|
|
|||
|
for (;;)
|
|||
|
{
|
|||
|
if (shutdown_pending)
|
|||
|
{
|
|||
|
if (!active_connections)
|
|||
|
break; /* Ready. */
|
|||
|
|
|||
|
/* Do not accept anymore connections but wait for existing
|
|||
|
connections to terminate. */
|
|||
|
signo = 0;
|
|||
|
pth_wait (ev);
|
|||
|
if (pth_event_occurred (ev) && signo)
|
|||
|
if (handle_signal (signo))
|
|||
|
break; /* Stop the loop. */
|
|||
|
continue;
|
|||
|
}
|
|||
|
|
|||
|
gcry_fast_random_poll ();
|
|||
|
fd = pth_accept_ev (listen_fd, (struct sockaddr *)&paddr, &plen, ev);
|
|||
|
if (fd == -1)
|
|||
|
{
|
|||
|
if (pth_event_occurred (ev))
|
|||
|
{
|
|||
|
if (handle_signal (signo))
|
|||
|
break; /* Stop the loop. */
|
|||
|
continue;
|
|||
|
}
|
|||
|
logit (LOG_WARNING, "accept failed: %s - waiting 1s\n",
|
|||
|
strerror (errno));
|
|||
|
gcry_fast_random_poll ();
|
|||
|
pth_sleep (1);
|
|||
|
continue;
|
|||
|
}
|
|||
|
|
|||
|
if (!pth_spawn (tattr, connection_thread, (void*)fd))
|
|||
|
{
|
|||
|
logit (LOG_ERR, "error spawning connection handler: %s\n",
|
|||
|
strerror (errno) );
|
|||
|
close (fd);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
pth_event_free (ev, PTH_FREE_ALL);
|
|||
|
}
|