| /* |
| Copyright (c) 2004 The Regents of the University of Michigan. |
| All rights reserved. |
| |
| Redistribution and use in source and binary forms, with or without |
| modification, are permitted provided that the following conditions |
| are met: |
| |
| 1. Redistributions of source code must retain the above copyright |
| notice, this list of conditions and the following disclaimer. |
| 2. Redistributions in binary form must reproduce the above copyright |
| notice, this list of conditions and the following disclaimer in the |
| documentation and/or other materials provided with the distribution. |
| 3. Neither the name of the University nor the names of its |
| contributors may be used to endorse or promote products derived |
| from this software without specific prior written permission. |
| |
| THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED |
| WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF |
| MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE |
| DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE |
| FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
| CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
| SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR |
| BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF |
| LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING |
| NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
| 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 <netinet/in.h> |
| |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include <memory.h> |
| #include <errno.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; |
| |
| #define POLL_MILLISECS 500 |
| |
| static volatile int dir_changed = 1; |
| |
| static void dir_notify_handler(int sig, siginfo_t *si, void *data) |
| { |
| 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) |
| 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) |
| dir_changed = 1; |
| if (pollarray[i].revents & POLLIN) |
| handle_krb5_upcall(clp); |
| pollarray[clp->krb5_poll_index].revents = 0; |
| ret--; |
| if (!ret) |
| break; |
| } |
| i = clp->spkm3_poll_index; |
| if (i >= 0 && pollarray[i].revents) { |
| if (pollarray[i].revents & POLLHUP) |
| dir_changed = 1; |
| if (pollarray[i].revents & POLLIN) |
| handle_spkm3_upcall(clp); |
| pollarray[clp->spkm3_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) { |
| 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; |
| } |
| |
| void |
| gssd_run() |
| { |
| int ret; |
| struct sigaction dn_act; |
| 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... */ |
| sigemptyset(&set); |
| sigaddset(&set, DNOTIFY_SIGNAL); |
| sigprocmask(SIG_UNBLOCK, &set, NULL); |
| |
| if (topdirs_init_list() != 0) |
| return; |
| |
| 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); |
| } |
| } |
| /* 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); |
| } |
| } |
| topdirs_free_list(); |
| |
| return; |
| } |