mirror of
git://git.gnupg.org/gnupg.git
synced 2024-12-22 10:19:57 +01:00
kbx: Have threads monitoring socket takeover and homedir if no inotify.
* kbx/keyboxd.c (CHECK_PROBLEMS_INTERVAL): New. (have_homedir_inotify): Remove the global. [HAVE_W32_SYSTEM] (create_an_event): New. (handle_tick): Remove. (handle_signal): Add handling SIGCONT. (keyboxd_kick_the_loop): New. (handle_connections): Spawn check_own_socket_thread and check_others_thread if no inotify. (check_own_socket_thread, check_others_thread): New. -- This change follows the change of gpg-agent. Signed-off-by: NIIBE Yutaka <gniibe@fsij.org>
This commit is contained in:
parent
6ddaf2be9f
commit
ccfbb9ebdf
350
kbx/keyboxd.c
350
kbx/keyboxd.c
@ -144,14 +144,13 @@ static struct debug_flags_s debug_flags [] =
|
||||
{ 77, NULL } /* 77 := Do not exit on "help" or "?". */
|
||||
};
|
||||
|
||||
/* The timer tick used for housekeeping stuff. Note that on Windows
|
||||
* we use a SetWaitableTimer seems to signal earlier than about 2
|
||||
* seconds. Thus we use 4 seconds on all platforms.
|
||||
* CHECK_OWN_SOCKET_INTERVAL defines how often we check
|
||||
* our own socket in standard socket mode. If that value is 0 we
|
||||
* don't check at all. All values are in seconds. */
|
||||
# define TIMERTICK_INTERVAL (4)
|
||||
# define CHECK_OWN_SOCKET_INTERVAL (60)
|
||||
/* CHECK_OWN_SOCKET_INTERVAL defines how often we check our own socket
|
||||
* in standard socket mode. If that value is 0 we don't check at all.
|
||||
* Values is in seconds. */
|
||||
#define CHECK_OWN_SOCKET_INTERVAL (60)
|
||||
/* CHECK_PROBLEMS_INTERVAL defines how often we check the existence of
|
||||
* homedir. Value is in seconds. */
|
||||
#define CHECK_PROBLEMS_INTERVAL (4)
|
||||
|
||||
/* The list of open file descriptors at startup. Note that this list
|
||||
* has been allocated using the standard malloc. */
|
||||
@ -171,8 +170,10 @@ static int shutdown_pending;
|
||||
/* Flag indicating to start the daemon even if one already runs. */
|
||||
static int steal_socket;
|
||||
|
||||
/* Counter for the currently running own socket checks. */
|
||||
static int check_own_socket_running;
|
||||
/* Flag to monitor problems. */
|
||||
static int problem_detected;
|
||||
#define KEYBOXD_PROBLEM_SOCKET_TAKEOVER (1 << 0)
|
||||
#define KEYBOXD_PROBLEM_HOMEDIR_REMOVED (1 << 1)
|
||||
|
||||
/* Flag to indicate that we shall not watch our own socket. */
|
||||
static int disable_check_own_socket;
|
||||
@ -192,6 +193,17 @@ static assuan_sock_nonce_t socket_nonce;
|
||||
* Let's try this as default. Change at runtime with --listen-backlog. */
|
||||
static int listen_backlog = 64;
|
||||
|
||||
#ifdef HAVE_W32_SYSTEM
|
||||
/* The event to break the select call. */
|
||||
static HANDLE the_event2;
|
||||
#elif defined(HAVE_PSELECT_NO_EINTR)
|
||||
/* An FD to break the select call. */
|
||||
static int event_pipe_fd;
|
||||
#else
|
||||
/* PID of the main thread. */
|
||||
static pid_t main_thread_pid;
|
||||
#endif
|
||||
|
||||
/* Name of a config file, which will be reread on a HUP if it is not NULL. */
|
||||
static char *config_filename;
|
||||
|
||||
@ -199,11 +211,6 @@ static char *config_filename;
|
||||
* the log file after a SIGHUP if it didn't changed. Malloced. */
|
||||
static char *current_logfile;
|
||||
|
||||
/* This flag is true if the inotify mechanism for detecting the
|
||||
* removal of the homedir is active. This flag is used to disable the
|
||||
* alternative but portable stat based check. */
|
||||
static int have_homedir_inotify;
|
||||
|
||||
/* Number of active connections. */
|
||||
static int active_connections;
|
||||
|
||||
@ -250,8 +257,11 @@ static void kbxd_init_default_ctrl (ctrl_t ctrl);
|
||||
static void kbxd_deinit_default_ctrl (ctrl_t ctrl);
|
||||
|
||||
static void handle_connections (gnupg_fd_t listen_fd);
|
||||
static void check_own_socket (void);
|
||||
static int check_for_running_kbxd (int silent);
|
||||
#if CHECK_OWN_SOCKET_INTERVAL > 0
|
||||
static void *check_own_socket_thread (void *arg);
|
||||
#endif
|
||||
static void *check_others_thread (void *arg);
|
||||
|
||||
/*
|
||||
* Functions.
|
||||
@ -1055,6 +1065,44 @@ get_kbxd_active_connection_count (void)
|
||||
}
|
||||
|
||||
|
||||
/* Under W32, this function returns the handle of the scdaemon
|
||||
notification event. Calling it the first time creates that
|
||||
event. */
|
||||
#if defined(HAVE_W32_SYSTEM)
|
||||
static void *
|
||||
create_an_event (void)
|
||||
{
|
||||
HANDLE h, h2;
|
||||
SECURITY_ATTRIBUTES sa = { sizeof (SECURITY_ATTRIBUTES), NULL, TRUE};
|
||||
|
||||
/* We need to use a manual reset event object due to the way our
|
||||
w32-pth wait function works: If we would use an automatic
|
||||
reset event we are not able to figure out which handle has
|
||||
been signaled because at the time we single out the signaled
|
||||
handles using WFSO the event has already been reset due to
|
||||
the WFMO. */
|
||||
h = CreateEvent (&sa, TRUE, FALSE, NULL);
|
||||
if (!h)
|
||||
log_error ("can't create an event: %s\n", w32_strerror (-1) );
|
||||
else if (!DuplicateHandle (GetCurrentProcess(), h,
|
||||
GetCurrentProcess(), &h2,
|
||||
EVENT_MODIFY_STATE|SYNCHRONIZE, TRUE, 0))
|
||||
{
|
||||
log_error ("setting synchronize for an event failed: %s\n",
|
||||
w32_strerror (-1) );
|
||||
CloseHandle (h);
|
||||
}
|
||||
else
|
||||
{
|
||||
CloseHandle (h);
|
||||
return h2;
|
||||
}
|
||||
|
||||
return INVALID_HANDLE_VALUE;
|
||||
}
|
||||
#endif /*HAVE_W32_SYSTEM*/
|
||||
|
||||
|
||||
/* Create a name for the socket in the home directory as using
|
||||
* STANDARD_NAME. We also check for valid characters as well as
|
||||
* against a maximum allowed length for a Unix domain socket is done.
|
||||
@ -1267,38 +1315,6 @@ create_directories (void)
|
||||
|
||||
|
||||
|
||||
/* This is the worker for the ticker. It is called every few seconds
|
||||
* and may only do fast operations. */
|
||||
static void
|
||||
handle_tick (void)
|
||||
{
|
||||
static time_t last_minute;
|
||||
struct stat statbuf;
|
||||
|
||||
if (!last_minute)
|
||||
last_minute = time (NULL);
|
||||
|
||||
/* Code to be run from time to time. */
|
||||
#if CHECK_OWN_SOCKET_INTERVAL > 0
|
||||
if (last_minute + CHECK_OWN_SOCKET_INTERVAL <= time (NULL))
|
||||
{
|
||||
check_own_socket ();
|
||||
last_minute = time (NULL);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
/* Check whether the homedir is still available. */
|
||||
if (!shutdown_pending
|
||||
&& !have_homedir_inotify
|
||||
&& gnupg_stat (gnupg_homedir (), &statbuf) && errno == ENOENT)
|
||||
{
|
||||
shutdown_pending = 1;
|
||||
log_info ("homedir has been removed - shutting down\n");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* A global function which allows us to call the reload stuff from
|
||||
* other places too. This is only used when build for W32. */
|
||||
void
|
||||
@ -1344,6 +1360,11 @@ handle_signal (int signo)
|
||||
kbxd_sigusr2_action ();
|
||||
break;
|
||||
|
||||
case SIGCONT:
|
||||
/* Do nothing, but break the syscall. */
|
||||
log_debug ("SIGCONT received - breaking select\n");
|
||||
break;
|
||||
|
||||
case SIGTERM:
|
||||
if (!shutdown_pending)
|
||||
log_info ("SIGTERM received - shutting down ...\n");
|
||||
@ -1435,6 +1456,28 @@ start_connection_thread (void *arg)
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
keyboxd_kick_the_loop (void)
|
||||
{
|
||||
/* Kick the select loop. */
|
||||
#ifdef HAVE_W32_SYSTEM
|
||||
int ret = SetEvent (the_event2);
|
||||
if (ret == 0)
|
||||
log_error ("SetEvent for agent_kick_the_loop failed: %s\n",
|
||||
w32_strerror (-1));
|
||||
#else
|
||||
# ifdef HAVE_PSELECT_NO_EINTR
|
||||
write (event_pipe_fd, "", 1);
|
||||
# else
|
||||
int ret = kill (main_thread_pid, SIGCONT);
|
||||
if (ret < 0)
|
||||
log_error ("sending signal for agent_kick_the_loop failed: %s\n",
|
||||
gpg_strerror (gpg_error_from_syserror ()));
|
||||
# endif
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
/* Connection handler loop. Wait for connection requests and spawn a
|
||||
* thread after accepting a connection. */
|
||||
static void
|
||||
@ -1449,12 +1492,14 @@ handle_connections (gnupg_fd_t listen_fd)
|
||||
gnupg_fd_t fd;
|
||||
int nfd;
|
||||
int saved_errno;
|
||||
struct timespec abstime;
|
||||
struct timespec curtime;
|
||||
struct timespec timeout;
|
||||
#ifdef HAVE_W32_SYSTEM
|
||||
HANDLE events[2];
|
||||
unsigned int events_set;
|
||||
#else
|
||||
int signo;
|
||||
# ifdef HAVE_PSELECT_NO_EINTR
|
||||
int pipe_fd[2];
|
||||
# endif
|
||||
#endif
|
||||
int sock_inotify_fd = -1;
|
||||
int home_inotify_fd = -1;
|
||||
@ -1465,7 +1510,7 @@ handle_connections (gnupg_fd_t listen_fd)
|
||||
} listentbl[] = {
|
||||
{ "std", start_connection_thread },
|
||||
};
|
||||
|
||||
int have_homedir_inotify = 0;
|
||||
|
||||
ret = npth_attr_init(&tattr);
|
||||
if (ret)
|
||||
@ -1478,10 +1523,23 @@ handle_connections (gnupg_fd_t listen_fd)
|
||||
npth_sigev_add (SIGUSR1);
|
||||
npth_sigev_add (SIGUSR2);
|
||||
npth_sigev_add (SIGINT);
|
||||
npth_sigev_add (SIGCONT);
|
||||
npth_sigev_add (SIGTERM);
|
||||
npth_sigev_fini ();
|
||||
# ifdef HAVE_PSELECT_NO_EINTR
|
||||
ret = gnupg_create_pipe (pipe_fd);
|
||||
if (ret)
|
||||
{
|
||||
log_error ("pipe creation failed: %s\n", gpg_strerror (ret));
|
||||
return;
|
||||
}
|
||||
event_pipe_fd = pipe_fd[1];
|
||||
# else
|
||||
main_thread_pid = getpid ();
|
||||
# endif
|
||||
#else
|
||||
events[0] = INVALID_HANDLE_VALUE;
|
||||
events[0] = the_event2 = create_an_event ();
|
||||
events[1] = INVALID_HANDLE_VALUE;
|
||||
#endif
|
||||
|
||||
if (disable_check_own_socket)
|
||||
@ -1503,6 +1561,26 @@ handle_connections (gnupg_fd_t listen_fd)
|
||||
else
|
||||
have_homedir_inotify = 1;
|
||||
|
||||
#if CHECK_OWN_SOCKET_INTERVAL > 0
|
||||
if (!disable_check_own_socket && sock_inotify_fd == -1)
|
||||
{
|
||||
npth_t thread;
|
||||
|
||||
err = npth_create (&thread, &tattr, check_own_socket_thread, NULL);
|
||||
if (err)
|
||||
log_error ("error spawning check_own_socket_thread: %s\n", strerror (err));
|
||||
}
|
||||
#endif
|
||||
|
||||
if (!have_homedir_inotify)
|
||||
{
|
||||
npth_t thread;
|
||||
|
||||
err = npth_create (&thread, &tattr, check_others_thread, NULL);
|
||||
if (err)
|
||||
log_error ("error spawning check_others_thread: %s\n", strerror (err));
|
||||
}
|
||||
|
||||
FD_ZERO (&fdset);
|
||||
FD_SET (FD2INT (listen_fd), &fdset);
|
||||
nfd = FD2NUM (listen_fd);
|
||||
@ -1521,9 +1599,6 @@ handle_connections (gnupg_fd_t listen_fd)
|
||||
|
||||
listentbl[0].l_fd = listen_fd;
|
||||
|
||||
npth_clock_gettime (&abstime);
|
||||
abstime.tv_sec += TIMERTICK_INTERVAL;
|
||||
|
||||
for (;;)
|
||||
{
|
||||
/* Shutdown test. */
|
||||
@ -1556,28 +1631,21 @@ handle_connections (gnupg_fd_t listen_fd)
|
||||
|
||||
read_fdset = fdset;
|
||||
|
||||
npth_clock_gettime (&curtime);
|
||||
if (!(npth_timercmp (&curtime, &abstime, <)))
|
||||
{
|
||||
/* Timeout. */
|
||||
handle_tick ();
|
||||
npth_clock_gettime (&abstime);
|
||||
abstime.tv_sec += TIMERTICK_INTERVAL;
|
||||
}
|
||||
npth_timersub (&abstime, &curtime, &timeout);
|
||||
#ifdef HAVE_PSELECT_NO_EINTR
|
||||
FD_SET (pipe_fd[0], &read_fdset);
|
||||
if (nfd < pipe_fd[0])
|
||||
nfd = pipe_fd[0];
|
||||
#endif
|
||||
|
||||
#ifndef HAVE_W32_SYSTEM
|
||||
ret = npth_pselect (nfd+1, &read_fdset, NULL, NULL, &timeout,
|
||||
ret = npth_pselect (nfd+1, &read_fdset, NULL, NULL, NULL,
|
||||
npth_sigev_sigmask ());
|
||||
saved_errno = errno;
|
||||
|
||||
{
|
||||
int signo;
|
||||
while (npth_sigev_get_pending (&signo))
|
||||
handle_signal (signo);
|
||||
}
|
||||
while (npth_sigev_get_pending (&signo))
|
||||
handle_signal (signo);
|
||||
#else
|
||||
ret = npth_eselect (nfd+1, &read_fdset, NULL, NULL, &timeout,
|
||||
ret = npth_eselect (nfd+1, &read_fdset, NULL, NULL, NULL,
|
||||
events, &events_set);
|
||||
saved_errno = errno;
|
||||
|
||||
@ -1593,6 +1661,22 @@ handle_connections (gnupg_fd_t listen_fd)
|
||||
gnupg_sleep (1);
|
||||
continue;
|
||||
}
|
||||
|
||||
if ((problem_detected & KEYBOXD_PROBLEM_SOCKET_TAKEOVER))
|
||||
{
|
||||
/* We may not remove the socket as it is now in use by another
|
||||
server. */
|
||||
inhibit_socket_removal = 1;
|
||||
shutdown_pending = 2;
|
||||
log_info ("this process is useless - shutting down\n");
|
||||
}
|
||||
|
||||
if ((problem_detected & KEYBOXD_PROBLEM_HOMEDIR_REMOVED))
|
||||
{
|
||||
shutdown_pending = 1;
|
||||
log_info ("homedir has been removed - shutting down\n");
|
||||
}
|
||||
|
||||
if (ret <= 0)
|
||||
{
|
||||
/* Interrupt or timeout. Will be handled when calculating the
|
||||
@ -1600,6 +1684,15 @@ handle_connections (gnupg_fd_t listen_fd)
|
||||
continue;
|
||||
}
|
||||
|
||||
#ifdef HAVE_PSELECT_NO_EINTR
|
||||
if (FD_ISSET (pipe_fd[0], &read_fdset))
|
||||
{
|
||||
char buf[256];
|
||||
|
||||
read (pipe_fd[0], buf, sizeof buf);
|
||||
}
|
||||
#endif
|
||||
|
||||
/* The inotify fds are set even when a shutdown is pending (see
|
||||
* above). So we must handle them in any case. To avoid that
|
||||
* they trigger a second time we close them immediately. */
|
||||
@ -1670,13 +1763,21 @@ handle_connections (gnupg_fd_t listen_fd)
|
||||
close (sock_inotify_fd);
|
||||
if (home_inotify_fd != -1)
|
||||
close (home_inotify_fd);
|
||||
#ifdef HAVE_W32_SYSTEM
|
||||
if (the_event2 != INVALID_HANDLE_VALUE)
|
||||
CloseHandle (the_event2);
|
||||
#endif
|
||||
#ifdef HAVE_PSELECT_NO_EINTR
|
||||
close (pipe_fd[0]);
|
||||
close (pipe_fd[1]);
|
||||
#endif
|
||||
cleanup ();
|
||||
log_info (_("%s %s stopped\n"), gpgrt_strusage(11), gpgrt_strusage(13));
|
||||
npth_attr_destroy (&tattr);
|
||||
}
|
||||
|
||||
|
||||
|
||||
#if CHECK_OWN_SOCKET_INTERVAL > 0
|
||||
/* Helper for check_own_socket. */
|
||||
static gpg_error_t
|
||||
check_own_socket_pid_cb (void *opaque, const void *buffer, size_t length)
|
||||
@ -1687,20 +1788,18 @@ check_own_socket_pid_cb (void *opaque, const void *buffer, size_t length)
|
||||
}
|
||||
|
||||
|
||||
/* The thread running the actual check. We need to run this in a
|
||||
* separate thread so that check_own_thread can be called from the
|
||||
* timer tick. */
|
||||
static void *
|
||||
check_own_socket_thread (void *arg)
|
||||
/* Check whether we are still listening on our own socket. In case
|
||||
another gpg-agent process started after us has taken ownership of
|
||||
our socket, we would linger around without any real task. Thus we
|
||||
better check once in a while whether we are really needed. */
|
||||
static int
|
||||
do_check_own_socket (const char *sockname)
|
||||
{
|
||||
int rc;
|
||||
char *sockname = arg;
|
||||
assuan_context_t ctx = NULL;
|
||||
membuf_t mb;
|
||||
char *buffer;
|
||||
|
||||
check_own_socket_running++;
|
||||
|
||||
rc = assuan_new (&ctx);
|
||||
if (rc)
|
||||
{
|
||||
@ -1738,57 +1837,70 @@ check_own_socket_thread (void *arg)
|
||||
xfree (buffer);
|
||||
|
||||
leave:
|
||||
xfree (sockname);
|
||||
if (ctx)
|
||||
assuan_release (ctx);
|
||||
if (rc)
|
||||
{
|
||||
/* We may not remove the socket as it is now in use by another
|
||||
* server. */
|
||||
inhibit_socket_removal = 1;
|
||||
shutdown_pending = 2;
|
||||
log_info ("this process is useless - shutting down\n");
|
||||
}
|
||||
check_own_socket_running--;
|
||||
return NULL;
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
/* Check whether we are still listening on our own socket. In case
|
||||
* another keyboxd process started after us has taken ownership of our
|
||||
* socket, we would linger around without any real task. Thus we
|
||||
* better check once in a while whether we are really needed. */
|
||||
static void
|
||||
check_own_socket (void)
|
||||
/* The thread running the actual check. */
|
||||
static void *
|
||||
check_own_socket_thread (void *arg)
|
||||
{
|
||||
char *sockname;
|
||||
npth_t thread;
|
||||
npth_attr_t tattr;
|
||||
int err;
|
||||
|
||||
if (disable_check_own_socket)
|
||||
return;
|
||||
|
||||
if (check_own_socket_running || shutdown_pending)
|
||||
return; /* Still running or already shutting down. */
|
||||
(void)arg;
|
||||
|
||||
sockname = make_filename_try (gnupg_socketdir (), KEYBOXD_SOCK_NAME, NULL);
|
||||
if (!sockname)
|
||||
return; /* Out of memory. */
|
||||
return NULL; /* Out of memory. */
|
||||
|
||||
err = npth_attr_init (&tattr);
|
||||
if (err)
|
||||
while (!problem_detected)
|
||||
{
|
||||
xfree (sockname);
|
||||
return;
|
||||
}
|
||||
npth_attr_setdetachstate (&tattr, NPTH_CREATE_DETACHED);
|
||||
err = npth_create (&thread, &tattr, check_own_socket_thread, sockname);
|
||||
if (err)
|
||||
log_error ("error spawning check_own_socket_thread: %s\n", strerror (err));
|
||||
npth_attr_destroy (&tattr);
|
||||
}
|
||||
if (shutdown_pending)
|
||||
goto leave;
|
||||
|
||||
gnupg_sleep (CHECK_OWN_SOCKET_INTERVAL);
|
||||
|
||||
if (do_check_own_socket (sockname))
|
||||
problem_detected |= KEYBOXD_PROBLEM_SOCKET_TAKEOVER;
|
||||
}
|
||||
|
||||
keyboxd_kick_the_loop ();
|
||||
|
||||
leave:
|
||||
xfree (sockname);
|
||||
return NULL;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* The thread running other checks. */
|
||||
static void *
|
||||
check_others_thread (void *arg)
|
||||
{
|
||||
const char *homedir = gnupg_homedir ();
|
||||
|
||||
(void)arg;
|
||||
|
||||
while (!problem_detected)
|
||||
{
|
||||
struct stat statbuf;
|
||||
|
||||
if (shutdown_pending)
|
||||
goto leave;
|
||||
|
||||
gnupg_sleep (CHECK_PROBLEMS_INTERVAL);
|
||||
|
||||
/* Check whether the homedir is still available. */
|
||||
if (gnupg_stat (homedir, &statbuf) && errno == ENOENT)
|
||||
problem_detected |= KEYBOXD_PROBLEM_HOMEDIR_REMOVED;
|
||||
}
|
||||
|
||||
keyboxd_kick_the_loop ();
|
||||
|
||||
leave:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
/* Figure out whether a keyboxd is available and running. Prints an
|
||||
|
Loading…
x
Reference in New Issue
Block a user