]> git.decadent.org.uk Git - nfs-utils.git/blobdiff - utils/gssd/gssd_main_loop.c
rpc.gssd: don't call poll(2) twice a second
[nfs-utils.git] / utils / gssd / gssd_main_loop.c
index b9f3a06520265c02f7ec5a14b1367d30297141c3..ccf7fe534009fad885ce404d82178ba2b6426b7c 100644 (file)
   SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
 
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif /* HAVE_CONFIG_H */
+
 #ifndef _GNU_SOURCE
 #define _GNU_SOURCE
 #endif
+
 #include <sys/param.h>
 #include <sys/socket.h>
 #include <sys/poll.h>
 #include <fcntl.h>
 #include <signal.h>
 #include <unistd.h>
+#include <dirent.h>
 
 #include "gssd.h"
 #include "err_util.h"
 
 extern struct pollfd *pollarray;
-extern int pollsize;
+extern unsigned long pollsize;
 
 #define POLL_MILLISECS 500
 
 static volatile int dir_changed = 1;
 
-static void dir_notify_handler(int sig, siginfo_t *si, void *data)
+static void dir_notify_handler(__attribute__((unused))int sig)
 {
        dir_changed = 1;
 }
@@ -68,43 +74,157 @@ scan_poll_results(int ret)
 
        for (clp = clnt_list.tqh_first; clp != NULL; clp = clp->list.tqe_next)
        {
-               i = clp->krb5_poll_index;
+               i = clp->gssd_poll_index;
                if (i >= 0 && pollarray[i].revents) {
-                       if (pollarray[i].revents & POLLHUP)
+                       if (pollarray[i].revents & POLLHUP) {
+                               clp->gssd_close_me = 1;
                                dir_changed = 1;
+                       }
                        if (pollarray[i].revents & POLLIN)
-                               handle_krb5_upcall(clp);
-                       pollarray[clp->krb5_poll_index].revents = 0;
+                               handle_gssd_upcall(clp);
+                       pollarray[clp->gssd_poll_index].revents = 0;
                        ret--;
                        if (!ret)
                                break;
                }
-               i = clp->spkm3_poll_index;
+               i = clp->krb5_poll_index;
                if (i >= 0 && pollarray[i].revents) {
-                       if (pollarray[i].revents & POLLHUP)
+                       if (pollarray[i].revents & POLLHUP) {
+                               clp->krb5_close_me = 1;
                                dir_changed = 1;
+                       }
                        if (pollarray[i].revents & POLLIN)
-                               handle_spkm3_upcall(clp);
-                       pollarray[clp->spkm3_poll_index].revents = 0;
+                               handle_krb5_upcall(clp);
+                       pollarray[clp->krb5_poll_index].revents = 0;
                        ret--;
                        if (!ret)
                                break;
                }
        }
-};
+}
+
+static int
+topdirs_add_entry(struct dirent *dent)
+{
+       struct topdirs_info *tdi;
+
+       tdi = calloc(sizeof(struct topdirs_info), 1);
+       if (tdi == NULL) {
+               printerr(0, "ERROR: Couldn't allocate struct topdirs_info\n");
+               return -1;
+       }
+       tdi->dirname = malloc(PATH_MAX);
+       if (tdi->dirname == NULL) {
+               printerr(0, "ERROR: Couldn't allocate directory name\n");
+               free(tdi);
+               return -1;
+       }
+       snprintf(tdi->dirname, PATH_MAX, "%s/%s", pipefs_dir, dent->d_name);
+       tdi->fd = open(tdi->dirname, O_RDONLY);
+       if (tdi->fd == -1) {
+               printerr(0, "ERROR: failed to open %s\n", tdi->dirname);
+               free(tdi);
+               return -1;
+       }
+       fcntl(tdi->fd, F_SETSIG, DNOTIFY_SIGNAL);
+       fcntl(tdi->fd, F_NOTIFY, DN_CREATE|DN_DELETE|DN_MODIFY|DN_MULTISHOT);
+
+       TAILQ_INSERT_HEAD(&topdirs_list, tdi, list);
+       return 0;
+}
+
+static void
+topdirs_free_list(void)
+{
+       struct topdirs_info *tdi;
+
+       TAILQ_FOREACH(tdi, &topdirs_list, list) {
+               free(tdi->dirname);
+               if (tdi->fd != -1)
+                       close(tdi->fd);
+               TAILQ_REMOVE(&topdirs_list, tdi, list);
+               free(tdi);
+       }
+}
+
+static int
+topdirs_init_list(void)
+{
+       DIR             *pipedir;
+       struct dirent   *dent;
+       int             ret;
+
+       TAILQ_INIT(&topdirs_list);
+
+       pipedir = opendir(pipefs_dir);
+       if (pipedir == NULL) {
+               printerr(0, "ERROR: could not open rpc_pipefs directory '%s': "
+                        "%s\n", pipefs_dir, strerror(errno));
+               return -1;
+       }
+       for (dent = readdir(pipedir); dent != NULL; dent = readdir(pipedir)) {
+               if (dent->d_type != DT_DIR ||
+                   strcmp(dent->d_name, ".") == 0  ||
+                   strcmp(dent->d_name, "..") == 0) {
+                       continue;
+               }
+               ret = topdirs_add_entry(dent);
+               if (ret)
+                       goto out_err;
+       }
+       closedir(pipedir);
+       return 0;
+out_err:
+       topdirs_free_list();
+       return -1;
+}
+
+#ifdef HAVE_PPOLL
+static void gssd_poll(struct pollfd *fds, unsigned long nfds)
+{
+       sigset_t emptyset;
+       int ret;
+
+       sigemptyset(&emptyset);
+       ret = ppoll(fds, nfds, NULL, &emptyset);
+       if (ret < 0) {
+               if (errno != EINTR)
+                       printerr(0, "WARNING: error return from poll\n");
+       } else if (ret == 0) {
+               printerr(0, "WARNING: unexpected timeout\n");
+       } else {
+               scan_poll_results(ret);
+       }
+}
+#else  /* !HAVE_PPOLL */
+static void gssd_poll(struct pollfd *fds, unsigned long nfds)
+{
+       int ret;
+
+       /* race condition here: dir_changed could be set before we
+        * enter the poll, and we'd never notice if it weren't for the
+        * timeout. */
+       ret = poll(fds, nfds, POLL_MILLISECS);
+       if (ret < 0) {
+               if (errno != EINTR)
+                       printerr(0, "WARNING: error return from poll\n");
+       } else if (ret == 0) {
+               /* timeout */
+       } else { /* ret > 0 */
+               scan_poll_results(ret);
+       }
+}
+#endif /* !HAVE_PPOLL */
 
 void
 gssd_run()
 {
-       int                     ret;
-       struct sigaction        dn_act;
-       int                     fd;
+       struct sigaction        dn_act = {
+               .sa_handler = dir_notify_handler
+       };
        sigset_t                set;
 
-       /* Taken from linux/Documentation/dnotify.txt: */
-       dn_act.sa_sigaction = dir_notify_handler;
        sigemptyset(&dn_act.sa_mask);
-       dn_act.sa_flags = SA_SIGINFO;
        sigaction(DNOTIFY_SIGNAL, &dn_act, NULL);
 
        /* just in case the signal is blocked... */
@@ -112,13 +232,8 @@ gssd_run()
        sigaddset(&set, DNOTIFY_SIGNAL);
        sigprocmask(SIG_UNBLOCK, &set, NULL);
 
-       if ((fd = open(pipefs_nfsdir, O_RDONLY)) == -1) {
-               printerr(0, "ERROR: failed to open %s: %s\n",
-                        pipefs_nfsdir, strerror(errno));
-               exit(1);
-       }
-       fcntl(fd, F_SETSIG, DNOTIFY_SIGNAL);
-       fcntl(fd, F_NOTIFY, DN_CREATE|DN_DELETE|DN_MODIFY|DN_MULTISHOT);
+       if (topdirs_init_list() != 0)
+               return;
 
        init_client_list();
 
@@ -127,25 +242,13 @@ gssd_run()
                while (dir_changed) {
                        dir_changed = 0;
                        if (update_client_list()) {
-                               printerr(0, "ERROR: couldn't update "
-                                        "client list\n");
+                               /* Error msg is already printed */
                                exit(1);
                        }
                }
-               /* race condition here: dir_changed could be set before we
-                * enter the poll, and we'd never notice if it weren't for the
-                * timeout. */
-               ret = poll(pollarray, pollsize, POLL_MILLISECS);
-               if (ret < 0) {
-                       if (errno != EINTR)
-                               printerr(0,
-                                        "WARNING: error return from poll\n");
-               } else if (ret == 0) {
-                       /* timeout */
-               } else { /* ret > 0 */
-                       scan_poll_results(ret);
-               }
+               gssd_poll(pollarray, pollsize);
        }
-       close(fd);
+       topdirs_free_list();
+
        return;
 }