diff options
Diffstat (limited to 'utils/gssd/gssd.c')
-rw-r--r-- | utils/gssd/gssd.c | 224 |
1 files changed, 220 insertions, 4 deletions
diff --git a/utils/gssd/gssd.c b/utils/gssd/gssd.c index dc84b3e..5e580e7 100644 --- a/utils/gssd/gssd.c +++ b/utils/gssd/gssd.c @@ -1,7 +1,7 @@ /* gssd.c - Copyright (c) 2000 The Regents of the University of Michigan. + Copyright (c) 2000, 2004 The Regents of the University of Michigan. All rights reserved. Copyright (c) 2000 Dug Song <dugsong@UMICH.EDU>. @@ -40,9 +40,15 @@ #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 <rpc/rpc.h> +#include <netinet/in.h> #include <unistd.h> #include <err.h> @@ -51,13 +57,17 @@ #include <stdlib.h> #include <string.h> #include <signal.h> +#include <memory.h> +#include <fcntl.h> +#include <dirent.h> + #include "gssd.h" #include "err_util.h" #include "gss_util.h" #include "krb5_util.h" #include "nfslib.h" -char pipefs_dir[PATH_MAX] = GSSD_PIPEFS_DIR; +static char pipefs_dir[PATH_MAX] = GSSD_PIPEFS_DIR; char keytabfile[PATH_MAX] = GSSD_DEFAULT_KEYTAB_FILE; char ccachedir[PATH_MAX] = GSSD_DEFAULT_CRED_DIR ":" GSSD_USER_CRED_DIR; char *ccachesearch[GSSD_MAX_CCACHE_SEARCH + 1]; @@ -66,8 +76,213 @@ int root_uses_machine_creds = 1; unsigned int context_timeout = 0; unsigned int rpc_timeout = 5; char *preferred_realm = NULL; +extern struct pollfd *pollarray; +extern unsigned long pollsize; + +#define POLL_MILLISECS 500 + +static volatile int dir_changed = 1; + +static void dir_notify_handler(__attribute__((unused))int sig) +{ + dir_changed = 1; +} + +static void +scan_poll_results(int ret) +{ + int i; + struct clnt_info *clp; + + for (clp = clnt_list.tqh_first; clp != NULL; clp = clp->list.tqe_next) + { + i = clp->gssd_poll_index; + if (i >= 0 && pollarray[i].revents) { + if (pollarray[i].revents & POLLHUP) { + clp->gssd_close_me = 1; + dir_changed = 1; + } + if (pollarray[i].revents & POLLIN) + handle_gssd_upcall(clp); + pollarray[clp->gssd_poll_index].revents = 0; + ret--; + if (!ret) + break; + } + i = clp->krb5_poll_index; + if (i >= 0 && pollarray[i].revents) { + if (pollarray[i].revents & POLLHUP) { + clp->krb5_close_me = 1; + dir_changed = 1; + } + if (pollarray[i].revents & POLLIN) + 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; -void + 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; + } + if (TAILQ_EMPTY(&topdirs_list)) { + printerr(0, "ERROR: rpc_pipefs directory '%s' is empty!\n", pipefs_dir); + return -1; + } + 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 */ + +static void +gssd_run(void) +{ + struct sigaction dn_act = { + .sa_handler = dir_notify_handler + }; + sigset_t set; + + sigemptyset(&dn_act.sa_mask); + sigaction(DNOTIFY_SIGNAL, &dn_act, NULL); + + /* just in case the signal is blocked... */ + sigemptyset(&set); + sigaddset(&set, DNOTIFY_SIGNAL); + sigprocmask(SIG_UNBLOCK, &set, NULL); + + if (topdirs_init_list() != 0) { + /* Error msg is already printed */ + exit(1); + } + init_client_list(); + + printerr(1, "beginning poll\n"); + while (1) { + while (dir_changed) { + dir_changed = 0; + if (update_client_list()) { + /* Error msg is already printed */ + exit(1); + } + + daemon_ready(); + } + gssd_poll(pollarray, pollsize); + } + topdirs_free_list(); + + return; +} + +static void sig_die(int signal) { /* destroy krb5 machine creds */ @@ -77,7 +292,7 @@ sig_die(int signal) exit(0); } -void +static void sig_hup(int signal) { /* don't exit on SIGHUP */ @@ -215,3 +430,4 @@ main(int argc, char *argv[]) printerr(0, "gssd_run returned!\n"); abort(); } + |