summaryrefslogtreecommitdiffstats
path: root/kbx
diff options
context:
space:
mode:
authorNIIBE Yutaka <gniibe@fsij.org>2024-01-24 05:45:49 +0100
committerNIIBE Yutaka <gniibe@fsij.org>2024-01-24 05:45:49 +0100
commitccfbb9ebdf2ead9c8c9f824fa17b1e79c692c924 (patch)
tree17cd53f9829f61f2b88eab2d16ff80bc1243b8c6 /kbx
parentcommon: Remove t-b64.c. (diff)
downloadgnupg2-ccfbb9ebdf2ead9c8c9f824fa17b1e79c692c924.tar.xz
gnupg2-ccfbb9ebdf2ead9c8c9f824fa17b1e79c692c924.zip
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>
Diffstat (limited to 'kbx')
-rw-r--r--kbx/keyboxd.c346
1 files changed, 229 insertions, 117 deletions
diff --git a/kbx/keyboxd.c b/kbx/keyboxd.c
index 66ffbcb3e..73905eb7d 100644
--- a/kbx/keyboxd.c
+++ b/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;
+ if (shutdown_pending)
+ goto leave;
+
+ gnupg_sleep (CHECK_OWN_SOCKET_INTERVAL);
+
+ if (do_check_own_socket (sockname))
+ problem_detected |= KEYBOXD_PROBLEM_SOCKET_TAKEOVER;
}
- 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);
+
+ 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