X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~ian/git?p=gnupg2.git;a=blobdiff_plain;f=agent%2Fgpg-agent.c;h=0e53549479f56044d4b26a9de2482193a41ceddd;hp=f4ed6c5c4cb06590145e7c283bc8c8fdc2b2b2b4;hb=fd200e494c0e288d4de5cd9e59136983cf9b5765;hpb=ec7411146b283f9271f7d087bb7a58e2dba45fea diff --git a/agent/gpg-agent.c b/agent/gpg-agent.c index f4ed6c5..0e53549 100644 --- a/agent/gpg-agent.c +++ b/agent/gpg-agent.c @@ -48,6 +48,9 @@ # include #endif #include +#ifdef HAVE_PRCTL +# include +#endif #define GNUPG_COMMON_NEED_AFLOCAL #include "agent.h" @@ -379,6 +382,9 @@ static char *current_logfile; watched. */ static pid_t parent_pid = (pid_t)(-1); +/* Record the pid of the main thread, for easier signalling */ +static pid_t main_thread_pid = (pid_t)(-1); + /* Number of active connections. */ static int active_connections; @@ -947,6 +953,11 @@ main (int argc, char **argv ) early_system_init (); +#if defined(HAVE_PRCTL) && defined(PR_SET_DUMPABLE) + /* Disable ptrace on Linux without sgid bit */ + prctl(PR_SET_DUMPABLE, 0); +#endif + /* Before we do anything else we save the list of currently open file descriptors and the signal mask. This info is required to do the exec call properly. */ @@ -2012,7 +2023,7 @@ get_agent_scd_notify_event (void) GetCurrentProcess(), &h2, EVENT_MODIFY_STATE|SYNCHRONIZE, TRUE, 0)) { - log_error ("setting syncronize for scd notify event failed: %s\n", + log_error ("setting synchronize for scd notify event failed: %s\n", w32_strerror (-1) ); CloseHandle (h); } @@ -2256,17 +2267,32 @@ create_directories (void) } +static int +need_tick (void) +{ +#ifdef HAVE_W32_SYSTEM + /* We do not know how to interrupt the select loop on Windows, so we + always need a short tick there. */ + return 1; +#else + /* if we were invoked like "gpg-agent cmd arg1 arg2" then we need to + watch our parent. */ + if (parent_pid != (pid_t)(-1)) + return 1; + /* if scdaemon is running, we need to check that it's alive */ + if (agent_scd_check_running ()) + return 1; + /* otherwise, nothing fine-grained to do. */ + return 0; +#endif /*HAVE_W32_SYSTEM*/ +} + /* 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; - - if (!last_minute) - last_minute = time (NULL); - /* Check whether the scdaemon has died and cleanup in this case. */ agent_scd_check_aliveness (); @@ -2285,16 +2311,6 @@ handle_tick (void) } } #endif /*HAVE_W32_SYSTEM*/ - - /* 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 - } @@ -2329,7 +2345,7 @@ agent_sigusr2_action (void) #ifndef HAVE_W32_SYSTEM /* The signal handler for this program. It is expected to be run in - its own trhead and not in the context of a signal handler. */ + its own thread and not in the context of a signal handler. */ static void handle_signal (int signo) { @@ -2353,6 +2369,10 @@ handle_signal (int signo) agent_sigusr2_action (); break; + /* nothing to do here, just take an extra cycle on the select loop */ + case SIGCONT: + break; + case SIGTERM: if (!shutdown_pending) log_info ("SIGTERM received - shutting down ...\n"); @@ -2691,6 +2711,22 @@ start_connection_thread_ssh (void *arg) } +void interrupt_main_thread_loop (void) +{ +#ifndef HAVE_W32_SYSTEM + kill (main_thread_pid, SIGCONT); +#endif +} + +/* helper function for readability: test whether a given struct + timespec is set to all-zeros */ +static inline int +tv_is_set (struct timespec tv) +{ + return tv.tv_sec || tv.tv_nsec; +} + + /* Connection handler loop. Wait for connection requests and spawn a thread after accepting a connection. */ static void @@ -2708,9 +2744,11 @@ handle_connections (gnupg_fd_t listen_fd, gnupg_fd_t fd; int nfd; int saved_errno; + int idx; struct timespec abstime; struct timespec curtime; struct timespec timeout; + struct timespec *select_timeout; #ifdef HAVE_W32_SYSTEM HANDLE events[2]; unsigned int events_set; @@ -2726,6 +2764,14 @@ handle_connections (gnupg_fd_t listen_fd, { "browser", start_connection_thread_browser }, { "ssh", start_connection_thread_ssh } }; + struct { + struct timespec interval; + void (*func) (void); + struct timespec next; + } timertbl[] = { + { { TIMERTICK_INTERVAL, 0 }, handle_tick }, + { { CHECK_OWN_SOCKET_INTERVAL, 0 }, check_own_socket } + }; ret = npth_attr_init(&tattr); @@ -2740,8 +2786,10 @@ 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 (); + main_thread_pid = getpid (); #else # ifdef HAVE_W32CE_SYSTEM /* Use a dummy event. */ @@ -2753,6 +2801,7 @@ handle_connections (gnupg_fd_t listen_fd, # endif #endif + if (disable_check_own_socket) my_inotify_fd = -1; else if ((err = gnupg_inotify_watch_socket (&my_inotify_fd, socket_name))) @@ -2815,9 +2864,6 @@ handle_connections (gnupg_fd_t listen_fd, listentbl[2].l_fd = listen_fd_browser; listentbl[3].l_fd = listen_fd_ssh; - npth_clock_gettime (&abstime); - abstime.tv_sec += TIMERTICK_INTERVAL; - for (;;) { /* Shutdown test. */ @@ -2846,18 +2892,52 @@ handle_connections (gnupg_fd_t listen_fd, thus a simple assignment is fine to copy the entire set. */ read_fdset = fdset; + /* avoid a fine-grained timer if we don't need one: */ + timertbl[0].interval.tv_sec = need_tick () ? TIMERTICK_INTERVAL : 0; + /* avoid waking up to check sockets if we can count on inotify */ + timertbl[1].interval.tv_sec = (my_inotify_fd == -1) ? CHECK_OWN_SOCKET_INTERVAL : 0; + + /* loop through all timers, fire any registered functions, and + plan next timer to trigger */ 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); + abstime.tv_sec = abstime.tv_nsec = 0; + for (idx=0; idx < DIM(timertbl); idx++) + { + /* schedule any unscheduled timers */ + if ((!tv_is_set (timertbl[idx].next)) && tv_is_set (timertbl[idx].interval)) + npth_timeradd (&timertbl[idx].interval, &curtime, &timertbl[idx].next); + /* if a timer is due, fire it ... */ + if (tv_is_set (timertbl[idx].next)) + { + if (!(npth_timercmp (&curtime, &timertbl[idx].next, <))) + { + timertbl[idx].func (); + npth_clock_gettime (&curtime); + /* ...and reschedule it, if desired: */ + if (tv_is_set (timertbl[idx].interval)) + npth_timeradd (&timertbl[idx].interval, &curtime, &timertbl[idx].next); + else + timertbl[idx].next.tv_sec = timertbl[idx].next.tv_nsec = 0; + } + } + /* accumulate next timer to come due in abstime: */ + if (tv_is_set (timertbl[idx].next) && + ((!tv_is_set (abstime)) || + (npth_timercmp (&abstime, &timertbl[idx].next, >)))) + abstime = timertbl[idx].next; + } + /* choose a timeout for the select loop: */ + if (tv_is_set (abstime)) + { + npth_timersub (&abstime, &curtime, &timeout); + select_timeout = &timeout; + } + else + select_timeout = NULL; + #ifndef HAVE_W32_SYSTEM - ret = npth_pselect (nfd+1, &read_fdset, NULL, NULL, &timeout, + ret = npth_pselect (nfd+1, &read_fdset, NULL, NULL, select_timeout, npth_sigev_sigmask ()); saved_errno = errno; @@ -2867,7 +2947,7 @@ handle_connections (gnupg_fd_t listen_fd, handle_signal (signo); } #else - ret = npth_eselect (nfd+1, &read_fdset, NULL, NULL, &timeout, + ret = npth_eselect (nfd+1, &read_fdset, NULL, NULL, select_timeout, events, &events_set); saved_errno = errno; @@ -2890,7 +2970,6 @@ handle_connections (gnupg_fd_t listen_fd, if (!shutdown_pending) { - int idx; ctrl_t ctrl; npth_t thread;