diff --git a/agent/gpg-agent.c b/agent/gpg-agent.c index 32e072b1e..16edae08a 100644 --- a/agent/gpg-agent.c +++ b/agent/gpg-agent.c @@ -47,9 +47,6 @@ #ifdef HAVE_SIGNAL_H # include #endif -#ifdef HAVE_INOTIFY_INIT -# include -#endif /*HAVE_INOTIFY_INIT*/ #include #define GNUPG_COMMON_NEED_AFLOCAL @@ -2716,31 +2713,6 @@ start_connection_thread_ssh (void *arg) } -#ifdef HAVE_INOTIFY_INIT -/* Read an inotify event and return true if it matches NAME. */ -static int -my_inotify_is_name (int fd, const char *name) -{ - union { - struct inotify_event ev; - char _buf[sizeof (struct inotify_event) + 100 + 1]; - } buf; - int n; - - n = npth_read (fd, &buf, sizeof buf); - if (n < sizeof (struct inotify_event)) - return 0; - if (buf.ev.len < strlen (name)+1) - return 0; - if (strcmp (buf.ev.name, name)) - return 0; /* Not the desired file. */ - - return 1; /* Found. */ -} -#endif /*HAVE_INOTIFY_INIT*/ - - - /* Connection handler loop. Wait for connection requests and spawn a thread after accepting a connection. */ static void @@ -2749,6 +2721,7 @@ handle_connections (gnupg_fd_t listen_fd, gnupg_fd_t listen_fd_browser, gnupg_fd_t listen_fd_ssh) { + gpg_error_t err; npth_attr_t tattr; struct sockaddr_un paddr; socklen_t plen; @@ -2764,9 +2737,7 @@ handle_connections (gnupg_fd_t listen_fd, HANDLE events[2]; unsigned int events_set; #endif -#ifdef HAVE_INOTIFY_INIT - int my_inotify_fd; -#endif /*HAVE_INOTIFY_INIT*/ + int my_inotify_fd = -1; struct { const char *name; void *(*func) (void *arg); @@ -2804,27 +2775,14 @@ handle_connections (gnupg_fd_t listen_fd, # endif #endif -#ifdef HAVE_INOTIFY_INIT if (disable_check_own_socket) my_inotify_fd = -1; - else if ((my_inotify_fd = inotify_init ()) == -1) - log_info ("error enabling fast daemon termination: %s\n", - strerror (errno)); - else + else if ((err = gnupg_inotify_watch_socket (&my_inotify_fd, socket_name))) { - /* We need to watch the directory for the file because there - * won't be an IN_DELETE_SELF for a socket file. */ - char *slash = strrchr (socket_name, '/'); - log_assert (slash && slash[1]); - *slash = 0; - if (inotify_add_watch (my_inotify_fd, socket_name, IN_DELETE) == -1) - { - close (my_inotify_fd); - my_inotify_fd = -1; - } - *slash = '/'; + if (gpg_err_code (err) != GPG_ERR_NOT_SUPPORTED) + log_info ("error enabling fast daemon termination: %s\n", + gpg_strerror (err)); } -#endif /*HAVE_INOTIFY_INIT*/ /* On Windows we need to fire up a separate thread to listen for requests from Putty (an SSH client), so we can replace Putty's @@ -2867,14 +2825,12 @@ handle_connections (gnupg_fd_t listen_fd, if (FD2INT (listen_fd_ssh) > nfd) nfd = FD2INT (listen_fd_ssh); } -#ifdef HAVE_INOTIFY_INIT if (my_inotify_fd != -1) { FD_SET (my_inotify_fd, &fdset); if (my_inotify_fd > nfd) nfd = my_inotify_fd; } -#endif /*HAVE_INOTIFY_INIT*/ listentbl[0].l_fd = listen_fd; listentbl[1].l_fd = listen_fd_extra; @@ -2949,14 +2905,13 @@ handle_connections (gnupg_fd_t listen_fd, ctrl_t ctrl; npth_t thread; -#ifdef HAVE_INOTIFY_INIT - if (my_inotify_fd != -1 && FD_ISSET (my_inotify_fd, &read_fdset) - && my_inotify_is_name (my_inotify_fd, GPG_AGENT_SOCK_NAME)) + if (my_inotify_fd != -1 + && FD_ISSET (my_inotify_fd, &read_fdset) + && gnupg_inotify_has_name (my_inotify_fd, GPG_AGENT_SOCK_NAME)) { shutdown_pending = 1; log_info ("socket file has been removed - shutting down\n"); } -#endif /*HAVE_INOTIFY_INIT*/ for (idx=0; idx < DIM(listentbl); idx++) { @@ -3004,10 +2959,8 @@ handle_connections (gnupg_fd_t listen_fd, } } -#ifdef HAVE_INOTIFY_INIT if (my_inotify_fd != -1) close (my_inotify_fd); -#endif /*HAVE_INOTIFY_INIT*/ cleanup (); log_info (_("%s %s stopped\n"), strusage(11), strusage(13)); npth_attr_destroy (&tattr); diff --git a/common/sysutils.c b/common/sysutils.c index 0f7b7f5cf..2e663bc8d 100644 --- a/common/sysutils.c +++ b/common/sysutils.c @@ -63,6 +63,9 @@ # endif # include #endif +#ifdef HAVE_INOTIFY_INIT +# include +#endif /*HAVE_INOTIFY_INIT*/ #ifdef HAVE_NPTH # include #endif @@ -78,6 +81,20 @@ #define tohex(n) ((n) < 10 ? ((n) + '0') : (((n) - 10) + 'A')) +static GPGRT_INLINE gpg_error_t +my_error_from_syserror (void) +{ + return gpg_err_make (default_errsource, gpg_err_code_from_syserror ()); +} + +static GPGRT_INLINE gpg_error_t +my_error (int e) +{ + return gpg_err_make (default_errsource, (e)); +} + + + #if defined(__linux__) && defined(__alpha__) && __GLIBC__ < 2 #warning using trap_unaligned static int @@ -929,3 +946,113 @@ w32_get_user_sid (void) return sid; } #endif /*HAVE_W32_SYSTEM*/ + + + +/* Support for inotify under Linux. */ + +/* Store a new inotify file handle for SOCKET_NAME at R_FD or return + * an error code. */ +gpg_error_t +gnupg_inotify_watch_socket (int *r_fd, const char *socket_name) +{ +#if HAVE_INOTIFY_INIT + gpg_error_t err; + char *fname; + int fd; + char *p; + + *r_fd = -1; + + fname = xtrystrdup (socket_name); + if (!fname) + return my_error_from_syserror (); + + fd = inotify_init (); + if (fd == -1) + { + err = my_error_from_syserror (); + xfree (fname); + return err; + } + + /* We need to watch the directory for the file because there won't + * be an IN_DELETE_SELF for a socket file. To handle a removal of + * the directory we also watch the directory itself. */ + p = strrchr (fname, '/'); + if (p) + *p = 0; + if (inotify_add_watch (fd, fname, + (IN_DELETE|IN_DELETE_SELF|IN_EXCL_UNLINK)) == -1) + { + err = my_error_from_syserror (); + close (fd); + xfree (fname); + return err; + } + + xfree (fname); + + *r_fd = fd; + return 0; +#else /*!HAVE_INOTIFY_INIT*/ + + (void)socket_name; + *r_fd = -1; + return my_error (GPG_ERR_NOT_SUPPORTED); + +#endif /*!HAVE_INOTIFY_INIT*/ +} + + +/* Read an inotify event and return true if it matches NAME or if it + * sees an IN_DELETE_SELF event for the directory of NAME. */ +int +gnupg_inotify_has_name (int fd, const char *name) +{ +#if USE_NPTH && HAVE_INOTIFY_INIT + union { + struct inotify_event ev; + char _buf[sizeof (struct inotify_event) + 255 + 1]; + } buf; + struct inotify_event *evp; + int n; + + n = npth_read (fd, &buf, sizeof buf); + /* log_debug ("notify read: n=%d\n", n); */ + evp = &buf.ev; + while (n >= sizeof (struct inotify_event)) + { + /* log_debug (" mask=%x len=%u name=(%s)\n", */ + /* evp->mask, (unsigned int)evp->len, evp->len? evp->name:""); */ + if ((evp->mask & IN_UNMOUNT)) + { + /* log_debug (" found (dir unmounted)\n"); */ + return 3; /* Directory was unmounted. */ + } + if ((evp->mask & IN_DELETE_SELF)) + { + /* log_debug (" found (dir removed)\n"); */ + return 2; /* Directory was removed. */ + } + if ((evp->mask & IN_DELETE)) + { + if (evp->len >= strlen (name) && !strcmp (evp->name, name)) + { + /* log_debug (" found (file removed)\n"); */ + return 1; /* File was removed. */ + } + } + n -= sizeof (*evp) + evp->len; + evp = (struct inotify_event *)((char*)evp + sizeof (*evp) + evp->len); + } + +#else /*!(USE_NPTH && HAVE_INOTIFY_INIT)*/ + + (void)fd; + (void)name; + +#endif /*!(USE_NPTH && HAVE_INOTIFY_INIT)*/ + + return 0; /* Not found. */ +} diff --git a/common/sysutils.h b/common/sysutils.h index ba66ce616..ea92e4c32 100644 --- a/common/sysutils.h +++ b/common/sysutils.h @@ -67,6 +67,10 @@ int gnupg_setenv (const char *name, const char *value, int overwrite); int gnupg_unsetenv (const char *name); char *gnupg_getcwd (void); +gpg_error_t gnupg_inotify_watch_socket (int *r_fd, const char *socket_name); +int gnupg_inotify_has_name (int fd, const char *name); + + #ifdef HAVE_W32_SYSTEM void *w32_get_user_sid (void);