X-Git-Url: https://git.decadent.org.uk/gitweb/?p=nfs-utils.git;a=blobdiff_plain;f=utils%2Fmountd%2Fmountd.c;h=6adb68fdc5e33b7af04bef23f8e829326536b26b;hp=0d4ddb43970a762e4a5547ec0f6f61d2cd825cb4;hb=603017f2c1587d760e2649b889b581ca267ffee7;hpb=06c962bc87aba28a3169be6a18ce8d52060b661f diff --git a/utils/mountd/mountd.c b/utils/mountd/mountd.c index 0d4ddb4..6adb68f 100644 --- a/utils/mountd/mountd.c +++ b/utils/mountd/mountd.c @@ -6,7 +6,9 @@ * Copyright (C) 1995, 1996 Olaf Kirch */ -#include "config.h" +#ifdef HAVE_CONFIG_H +#include +#endif #include #include @@ -19,22 +21,39 @@ #include #include #include +#include #include "xmalloc.h" #include "misc.h" #include "mountd.h" #include "rpcmisc.h" +#include "pseudoflavors.h" extern void cache_open(void); extern struct nfs_fh_len *cache_get_filehandle(nfs_export *exp, int len, char *p); -extern void cache_export(nfs_export *exp); +extern int cache_export(nfs_export *exp, char *path); extern void my_svc_run(void); static void usage(const char *, int exitcode); static exports get_exportlist(void); -static struct nfs_fh_len *get_rootfh(struct svc_req *, dirpath *, int *, int v3); +static struct nfs_fh_len *get_rootfh(struct svc_req *, dirpath *, nfs_export **, mountstat3 *, int v3); +int reverse_resolve = 0; int new_cache = 0; +int manage_gids; +int use_ipaddr = -1; + +/* PRC: a high-availability callout program can be specified with -H + * When this is done, the program will receive callouts whenever clients + * send mount or unmount requests -- the callout is not needed for 2.6 kernel */ +char *ha_callout_prog = NULL; + +/* Number of mountd threads to start. Default is 1 and + * that's probably enough unless you need hundreds of + * clients to be able to mount at once. */ +static int num_threads = 1; +/* Arbitrary limit on number of threads */ +#define MAX_THREADS 64 static struct option longopts[] = { @@ -48,24 +67,118 @@ static struct option longopts[] = { "version", 0, 0, 'v' }, { "port", 1, 0, 'p' }, { "no-tcp", 0, 0, 'n' }, + { "ha-callout", 1, 0, 'H' }, + { "state-directory-path", 1, 0, 's' }, + { "num-threads", 1, 0, 't' }, + { "reverse-lookup", 0, 0, 'r' }, + { "manage-gids", 0, 0, 'g' }, { NULL, 0, 0, 0 } }; static int nfs_version = -1; +static void +unregister_services (void) +{ + if (nfs_version & 0x1) + pmap_unset (MOUNTPROG, MOUNTVERS); + if (nfs_version & (0x1 << 1)) + pmap_unset (MOUNTPROG, MOUNTVERS_POSIX); + if (nfs_version & (0x1 << 2)) + pmap_unset (MOUNTPROG, MOUNTVERS_NFSV3); +} + +/* Wait for all worker child processes to exit and reap them */ +static void +wait_for_workers (void) +{ + int status; + pid_t pid; + + for (;;) { + + pid = waitpid(0, &status, 0); + + if (pid < 0) { + if (errno == ECHILD) + return; /* no more children */ + xlog(L_FATAL, "mountd: can't wait: %s\n", + strerror(errno)); + } + + /* Note: because we SIG_IGN'd SIGCHLD earlier, this + * does not happen on 2.6 kernels, and waitpid() blocks + * until all the children are dead then returns with + * -ECHILD. But, we don't need to do anything on the + * death of individual workers, so we don't care. */ + xlog(L_NOTICE, "mountd: reaped child %d, status %d\n", + (int)pid, status); + } +} + +/* Fork num_threads worker children and wait for them */ +static void +fork_workers(void) +{ + int i; + pid_t pid; + + xlog(L_NOTICE, "mountd: starting %d threads\n", num_threads); + + for (i = 0 ; i < num_threads ; i++) { + pid = fork(); + if (pid < 0) { + xlog(L_FATAL, "mountd: cannot fork: %s\n", + strerror(errno)); + } + if (pid == 0) { + /* worker child */ + + /* Re-enable the default action on SIGTERM et al + * so that workers die naturally when sent them. + * Only the parent unregisters with pmap and + * hence needs to do special SIGTERM handling. */ + struct sigaction sa; + sa.sa_handler = SIG_DFL; + sa.sa_flags = 0; + sigemptyset(&sa.sa_mask); + sigaction(SIGHUP, &sa, NULL); + sigaction(SIGINT, &sa, NULL); + sigaction(SIGTERM, &sa, NULL); + + /* fall into my_svc_run in caller */ + return; + } + } + + /* in parent */ + wait_for_workers(); + unregister_services(); + xlog(L_NOTICE, "mountd: no more workers, exiting\n"); + exit(0); +} + /* * Signal handler. */ static void killer (int sig) { - if (nfs_version & 0x1) - pmap_unset (MOUNTPROG, MOUNTVERS); - if (nfs_version & (0x1 << 1)) - pmap_unset (MOUNTPROG, MOUNTVERS_POSIX); - if (nfs_version & (0x1 << 2)) - pmap_unset (MOUNTPROG, MOUNTVERS_NFSV3); - xlog (L_FATAL, "Caught signal %d, un-registering and exiting.", sig); + unregister_services(); + if (num_threads > 1) { + /* play Kronos and eat our children */ + kill(0, SIGTERM); + wait_for_workers(); + } + xlog (L_FATAL, "Caught signal %d, un-registering and exiting.", sig); +} + +static void +sig_hup (int sig) +{ + /* don't exit on SIGHUP */ + xlog (L_NOTICE, "Received SIGHUP... Ignoring.\n", sig); + return; } bool_t @@ -80,7 +193,8 @@ mount_mnt_1_svc(struct svc_req *rqstp, dirpath *path, fhstatus *res) struct nfs_fh_len *fh; xlog(D_CALL, "MNT1(%s) called", *path); - if ((fh = get_rootfh(rqstp, path, &res->fhs_status, 0)) != NULL) + fh = get_rootfh(rqstp, path, NULL, &res->fhs_status, 0); + if (fh) memcpy(&res->fhstatus_u.fhs_fhandle, fh->fh_handle, 32); return 1; } @@ -90,10 +204,10 @@ mount_dump_1_svc(struct svc_req *rqstp, void *argp, mountlist *res) { struct sockaddr_in *addr = (struct sockaddr_in *) svc_getcaller(rqstp->rq_xprt); - xlog(L_NOTICE, "dump request from %s", - inet_ntoa(addr->sin_addr)); + xlog(D_CALL, "dump request from %s.", inet_ntoa(addr->sin_addr)); *res = mountlist_list(); + return 1; } @@ -117,14 +231,8 @@ mount_umnt_1_svc(struct svc_req *rqstp, dirpath *argp, void *resp) if (!(exp = auth_authenticate("unmount", sin, p))) { return 1; } - if (new_cache) { - if (strcmp(inet_ntoa(exp->m_client->m_addrlist[0]), exp->m_client->m_hostname)) - mountlist_del(inet_ntoa(exp->m_client->m_addrlist[0]), exp->m_client->m_hostname); - mountlist_del(exp->m_client->m_hostname, p); - } else { - mountlist_del(exp->m_client->m_hostname, p); - export_reset (exp); - } + + mountlist_del(inet_ntoa(sin->sin_addr), p); return 1; } @@ -143,9 +251,10 @@ mount_export_1_svc(struct svc_req *rqstp, void *argp, exports *resp) { struct sockaddr_in *addr = (struct sockaddr_in *) svc_getcaller(rqstp->rq_xprt); - xlog(L_NOTICE, "export request from %s", - inet_ntoa(addr->sin_addr)); + + xlog(D_CALL, "export request from %s.", inet_ntoa(addr->sin_addr)); *resp = get_exportlist(); + return 1; } @@ -154,9 +263,10 @@ mount_exportall_1_svc(struct svc_req *rqstp, void *argp, exports *resp) { struct sockaddr_in *addr = (struct sockaddr_in *) svc_getcaller(rqstp->rq_xprt); - xlog(L_NOTICE, "exportall request from %s", - inet_ntoa(addr->sin_addr)); + + xlog(D_CALL, "exportall request from %s.", inet_ntoa(addr->sin_addr)); *resp = get_exportlist(); + return 1; } @@ -196,12 +306,12 @@ mount_pathconf_2_svc(struct svc_req *rqstp, dirpath *path, ppathcnf *res) } /* Now authenticate the intruder... */ - if (!(exp = auth_authenticate("pathconf", sin, p))) { + exp = auth_authenticate("pathconf", sin, p); + if (!exp) { return 1; } else if (stat(p, &stb) < 0) { xlog(L_WARNING, "can't stat exported dir %s: %s", p, strerror(errno)); - export_reset (exp); return 1; } @@ -217,39 +327,67 @@ mount_pathconf_2_svc(struct svc_req *rqstp, dirpath *path, ppathcnf *res) res->pc_mask[0] = 0; res->pc_mask[1] = 0; - export_reset (exp); - return 1; } +/* + * We should advertise the preferred flavours first. (See RFC 2623 + * section 2.7.) We leave that to the administrator, by advertising + * flavours in the order they were listed in /etc/exports. AUTH_NULL is + * dropped from the list to avoid backward compatibility issue with + * older Linux clients, who inspect the list in reversed order. + * + * XXX: It might be more helpful to rearrange these so that flavors + * giving more access (as determined from readonly and id-squashing + * options) come first. (If we decide to do that we should probably do + * that when reading the exports rather than here.) + */ +static void set_authflavors(struct mountres3_ok *ok, nfs_export *exp) +{ + struct sec_entry *s; + static int flavors[SECFLAVOR_COUNT]; + int i = 0; + + for (s = exp->m_export.e_secinfo; s->flav; s++) { + if (s->flav->fnum == AUTH_NULL) + continue; + flavors[i] = s->flav->fnum; + i++; + } + ok->auth_flavors.auth_flavors_val = flavors; + ok->auth_flavors.auth_flavors_len = i; +} + /* * NFSv3 MOUNT procedure */ bool_t mount_mnt_3_svc(struct svc_req *rqstp, dirpath *path, mountres3 *res) { - static int flavors[] = { AUTH_NULL, AUTH_UNIX }; + struct mountres3_ok *ok = &res->mountres3_u.mountinfo; + nfs_export *exp; struct nfs_fh_len *fh; xlog(D_CALL, "MNT3(%s) called", *path); - if ((fh = get_rootfh(rqstp, path, (int *) &res->fhs_status, 1)) != NULL) { - struct mountres3_ok *ok = &res->mountres3_u.mountinfo; + fh = get_rootfh(rqstp, path, &exp, &res->fhs_status, 1); + if (!fh) + return 1; - ok->fhandle.fhandle3_len = fh->fh_size; - ok->fhandle.fhandle3_val = fh->fh_handle; - ok->auth_flavors.auth_flavors_len = 2; - ok->auth_flavors.auth_flavors_val = flavors; - } + ok->fhandle.fhandle3_len = fh->fh_size; + ok->fhandle.fhandle3_val = (char *)fh->fh_handle; + set_authflavors(ok, exp); return 1; } static struct nfs_fh_len * -get_rootfh(struct svc_req *rqstp, dirpath *path, int *error, int v3) +get_rootfh(struct svc_req *rqstp, dirpath *path, nfs_export **expret, + mountstat3 *error, int v3) { struct sockaddr_in *sin = (struct sockaddr_in *) svc_getcaller(rqstp->rq_xprt); struct stat stb, estb; nfs_export *exp; + struct nfs_fh_len *fh; char rpath[MAXPATHLEN+1]; char *p = *path; @@ -266,52 +404,65 @@ get_rootfh(struct svc_req *rqstp, dirpath *path, int *error, int v3) } /* Now authenticate the intruder... */ - if (!(exp = auth_authenticate("mount", sin, p))) { + exp = auth_authenticate("mount", sin, p); + if (!exp) { *error = NFSERR_ACCES; - } else if (stat(p, &stb) < 0) { + return NULL; + } + if (stat(p, &stb) < 0) { xlog(L_WARNING, "can't stat exported dir %s: %s", p, strerror(errno)); if (errno == ENOENT) *error = NFSERR_NOENT; else *error = NFSERR_ACCES; - } else if (!S_ISDIR(stb.st_mode) && !S_ISREG(stb.st_mode)) { + return NULL; + } + if (!S_ISDIR(stb.st_mode) && !S_ISREG(stb.st_mode)) { xlog(L_WARNING, "%s is not a directory or regular file", p); *error = NFSERR_NOTDIR; - } else if (stat(exp->m_export.e_path, &estb) < 0) { + return NULL; + } + if (stat(exp->m_export.e_path, &estb) < 0) { xlog(L_WARNING, "can't stat export point %s: %s", p, strerror(errno)); *error = NFSERR_NOENT; - } else if (estb.st_dev != stb.st_dev - /* && (!new_cache || !(exp->m_export.e_flags & NFSEXP_CROSSMOUNT)) */ - ) { + return NULL; + } + if (estb.st_dev != stb.st_dev + && (!new_cache + || !(exp->m_export.e_flags & NFSEXP_CROSSMOUNT))) { xlog(L_WARNING, "request to export directory %s below nearest filesystem %s", p, exp->m_export.e_path); *error = NFSERR_ACCES; - } else if (exp->m_export.e_mountpoint && + return NULL; + } + if (exp->m_export.e_mountpoint && !is_mountpoint(exp->m_export.e_mountpoint[0]? exp->m_export.e_mountpoint: exp->m_export.e_path)) { xlog(L_WARNING, "request to export an unmounted filesystem: %s", p); *error = NFSERR_NOENT; - } else if (new_cache) { + return NULL; + } + + if (new_cache) { /* This will be a static private nfs_export with just one * address. We feed it to kernel then extract the filehandle, * */ - struct nfs_fh_len *fh; - cache_export(exp); + if (cache_export(exp, p)) { + *error = NFSERR_ACCES; + return NULL; + } fh = cache_get_filehandle(exp, v3?64:32, p); - if (fh == NULL) + if (fh == NULL) { *error = NFSERR_ACCES; - else - *error = NFS_OK; - return fh; + return NULL; + } } else { - struct nfs_fh_len *fh; - if (exp->m_exported<1) export_export(exp); if (!exp->m_xtabent) @@ -327,17 +478,17 @@ get_rootfh(struct svc_req *rqstp, dirpath *path, int *error, int v3) fh = getfh_old ((struct sockaddr *) sin, stb.st_dev, stb.st_ino); } - if (fh != NULL) { - mountlist_add(exp->m_client->m_hostname, p); - *error = NFS_OK; - export_reset (exp); - return fh; + if (fh == NULL) { + xlog(L_WARNING, "getfh failed: %s", strerror(errno)); + *error = NFSERR_ACCES; + return NULL; } - xlog(L_WARNING, "getfh failed: %s", strerror(errno)); - *error = NFSERR_ACCES; } - export_reset (exp); - return NULL; + *error = NFS_OK; + mountlist_add(inet_ntoa(sin->sin_addr), p); + if (expret) + *expret = exp; + return fh; } static exports @@ -348,10 +499,15 @@ get_exportlist(void) struct groupnode *g, *ng, *c, **cp; nfs_export *exp; int i; + static unsigned int ecounter; + unsigned int acounter; - if (!auth_reload() && elist) + acounter = auth_reload(); + if (elist && acounter == ecounter) return elist; + ecounter = acounter; + for (e = elist; e != NULL; e = ne) { ne = e->ex_next; for (g = e->ex_groups; g != NULL; g = ng) { @@ -367,14 +523,14 @@ get_exportlist(void) for (i = 0; i < MCL_MAXTYPES; i++) { for (exp = exportlist[i]; exp; exp = exp->m_next) { for (e = elist; e != NULL; e = e->ex_next) { - if (!strcmp(exp->m_export.m_path, e->ex_dir)) + if (!strcmp(exp->m_export.e_path, e->ex_dir)) break; } if (!e) { e = (struct exportnode *) xmalloc(sizeof(*e)); e->ex_next = elist; e->ex_groups = NULL; - e->ex_dir = xstrdup(exp->m_export.m_path); + e->ex_dir = xstrdup(exp->m_export.e_path); elist = e; } @@ -403,11 +559,9 @@ get_exportlist(void) xfree(c->gr_name); xfree(c); xfree (hp); - if ((c = *cp) == NULL) - break; + continue; } - else - xfree (hp); + xfree (hp); } cp = &(c->gr_next); } @@ -435,6 +589,7 @@ int main(int argc, char **argv) { char *export_file = _PATH_EXPORTS; + char *state_dir = NFS_STATEDIR; int foreground = 0; int port = 0; int descriptors = 0; @@ -444,8 +599,11 @@ main(int argc, char **argv) /* Parse the command line options and arguments. */ opterr = 0; - while ((c = getopt_long(argc, argv, "o:n:Fd:f:p:P:hN:V:v", longopts, NULL)) != EOF) + while ((c = getopt_long(argc, argv, "o:nFd:f:p:P:hH:N:V:vrs:t:g", longopts, NULL)) != EOF) switch (c) { + case 'g': + manage_gids = 1; + break; case 'o': descriptors = atoi(optarg); if (descriptors <= 0) { @@ -463,6 +621,9 @@ main(int argc, char **argv) case 'f': export_file = optarg; break; + case 'H': /* PRC: specify a high-availability callout program */ + ha_callout_prog = optarg; + break; case 'h': usage(argv [0], 0); break; @@ -481,6 +642,19 @@ main(int argc, char **argv) case 'n': _rpcfdtype = SOCK_DGRAM; break; + case 'r': + reverse_resolve = 1; + break; + case 's': + if ((state_dir = xstrdup(optarg)) == NULL) { + fprintf(stderr, "%s: xstrdup(%s) failed!\n", + argv[0], optarg); + exit(1); + } + break; + case 't': + num_threads = atoi (optarg); + break; case 'V': nfs_version |= 1 << (atoi (optarg) - 1); break; @@ -498,9 +672,9 @@ main(int argc, char **argv) if (optind != argc || !(nfs_version & 0x7)) usage(argv [0], 1); - if (chdir(NFS_STATEDIR)) { + if (chdir(state_dir)) { fprintf(stderr, "%s: chdir(%s) failed: %s\n", - argv [0], NFS_STATEDIR, strerror(errno)); + argv [0], state_dir, strerror(errno)); exit(1); } @@ -509,10 +683,10 @@ main(int argc, char **argv) argv [0], strerror(errno)); else { /* glibc sunrpc code dies if getdtablesize > FD_SETSIZE */ - if (descriptors == 0 && rlim.rlim_cur > FD_SETSIZE) + if ((descriptors == 0 && rlim.rlim_cur > FD_SETSIZE) || + descriptors > FD_SETSIZE) descriptors = FD_SETSIZE; if (descriptors) { - rlim.rlim_cur = descriptors; if (setrlimit (RLIMIT_NOFILE, &rlim) != 0) { fprintf(stderr, "%s: setrlimit (RLIMIT_NOFILE) failed: %s\n", @@ -522,7 +696,8 @@ main(int argc, char **argv) } } /* Initialize logging. */ -/* xlog_open("mountd"); */ + if (!foreground) xlog_stderr(0); + xlog_open("mountd"); sa.sa_handler = SIG_IGN; sa.sa_flags = 0; @@ -535,11 +710,8 @@ main(int argc, char **argv) sigaction(SIGCHLD, &sa, NULL); /* Daemons should close all extra filehandles ... *before* RPC init. */ - if (!foreground) { - int fd = sysconf (_SC_OPEN_MAX); - while (--fd > 2) - (void) close(fd); - } + if (!foreground) + closeall(3); new_cache = check_new_cache(); if (new_cache) @@ -556,9 +728,10 @@ main(int argc, char **argv) mount_dispatch, port); sa.sa_handler = killer; - sigaction(SIGHUP, &sa, NULL); sigaction(SIGINT, &sa, NULL); sigaction(SIGTERM, &sa, NULL); + sa.sa_handler = sig_hup; + sigaction(SIGHUP, &sa, NULL); auth_init(export_file); @@ -580,9 +753,19 @@ main(int argc, char **argv) if (fd > 2) (void) close(fd); } setsid(); - xlog_background(); } + /* silently bounds check num_threads */ + if (foreground) + num_threads = 1; + else if (num_threads < 1) + num_threads = 1; + else if (num_threads > MAX_THREADS) + num_threads = MAX_THREADS; + + if (num_threads > 1) + fork_workers(); + my_svc_run(); xlog(L_ERROR, "Ack! Gack! svc_run returned!\n"); @@ -596,6 +779,8 @@ usage(const char *prog, int n) "Usage: %s [-F|--foreground] [-h|--help] [-v|--version] [-d kind|--debug kind]\n" " [-o num|--descriptors num] [-f exports-file|--exports-file=file]\n" " [-p|--port port] [-V version|--nfs-version version]\n" -" [-N version|--no-nfs-version version] [-n|--no-tcp]\n", prog); +" [-N version|--no-nfs-version version] [-n|--no-tcp]\n" +" [-H ha-callout-prog] [-s|--state-directory-path path]\n" +" [-g|--manage-gids] [-t num|--num-threads=num]\n", prog); exit(n); }