/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * automount.c * * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "automount.h" #include "smfcfg.h" static int mkdir_r(char *); struct autodir *dir_head; struct autodir *dir_tail; static struct extmnttab *find_mount(); int verbose = 0; int trace = 0; static void usage(); static int compare_opts(char *, char *); static void do_unmounts(); static int mount_timeout = AUTOFS_MOUNT_TIMEOUT; static char *service_list[] = { AUTOMOUNTD, NULL }; /* * XXX * The following are needed because they're used in auto_subr.c and * we link with it. Should avoid this. */ mutex_t cleanup_lock; cond_t cleanup_start_cv; cond_t cleanup_done_cv; int main(int argc, char *argv[]) { int c; struct autofs_args ai; struct utsname utsname; char autofs_addr[MAXADDRLEN]; struct autodir *dir, *d; struct stat stbuf; char *master_map = "auto_master"; int null; struct extmnttab mnt, *mntp; struct mnttab *omntp; char mntopts[MAX_MNTOPT_STR]; int mntflgs; int count = 0; char *stack[STACKSIZ]; char **stkptr; char *defval; struct sigaction sigintact; int ret = 0, bufsz = 0; char valbuf[6]; /* * protect this command from session termination when run in background * we test background by whether SIGINT is ignored */ (void) sigaction(SIGINT, NULL, &sigintact); if (sigintact.sa_handler == SIG_IGN) { (void) signal(SIGHUP, SIG_IGN); (void) setsid(); } /* * Read in the values from SMF first before we check * commandline options so the options override the SMF values. */ bufsz = 6; ret = autofs_smf_get_prop("timeout", valbuf, DEFAULT_INSTANCE, SCF_TYPE_INTEGER, AUTOMOUNTD, &bufsz); if (ret == SA_OK) /* * Ignore errno. In event of failure, mount_timeout is * already initialized to the correct value. */ mount_timeout = strtol(valbuf, (char **)NULL, 10); bufsz = 6; ret = autofs_smf_get_prop("automount_verbose", valbuf, DEFAULT_INSTANCE, SCF_TYPE_BOOLEAN, AUTOMOUNTD, &bufsz); if (ret == SA_OK) { if (strncasecmp("true", valbuf, 4) == 0) verbose = TRUE; } put_automountd_env(); while ((c = getopt(argc, argv, "mM:D:f:t:v?")) != EOF) { switch (c) { case 'm': pr_msg("Warning: -m option not supported"); break; case 'M': pr_msg("Warning: -M option not supported"); break; case 'D': pr_msg("Warning: -D option not supported"); break; case 'f': pr_msg("Error: -f option no longer supported"); usage(); break; case 't': if (strchr(optarg, '=')) { pr_msg("Error: invalid value for -t"); usage(); } mount_timeout = atoi(optarg); break; case 'v': verbose++; break; default: usage(); break; } } if (optind < argc) { pr_msg("%s: command line mountpoints/maps " "no longer supported", argv[optind]); usage(); } current_mounts = getmntlist(); if (current_mounts == NULL) { pr_msg("Couldn't establish current mounts"); exit(1); } (void) umask(0); ns_setup(stack, &stkptr); openlog("automount", LOG_PID, LOG_DAEMON); (void) loadmaster_map(master_map, "", stack, &stkptr); if (dir_head != NULL) { /* * automount maps found. enable services as needed. */ _check_services(service_list); } closelog(); if (uname(&utsname) < 0) { pr_msg("uname: %m"); exit(1); } (void) strcpy(autofs_addr, utsname.nodename); (void) strcat(autofs_addr, ".autofs"); ai.addr.buf = autofs_addr; ai.addr.len = strlen(ai.addr.buf); ai.addr.maxlen = ai.addr.len; ai.mount_to = mount_timeout; ai.rpc_to = AUTOFS_RPC_TIMEOUT; /* * Mount the daemon at its mount points. */ for (dir = dir_head; dir; dir = dir->dir_next) { /* * Skip null entries */ if (strcmp(dir->dir_map, "-null") == 0) continue; /* * Skip null'ed entries */ null = 0; for (d = dir->dir_prev; d; d = d->dir_prev) { if (strcmp(dir->dir_name, d->dir_name) == 0) null = 1; } if (null) continue; /* * Check whether there's already an entry * in the mnttab for this mountpoint. */ if (mntp = find_mount(dir->dir_name, 1)) { /* * If it's not an autofs mount - don't * mount over it. */ if (strcmp(mntp->mnt_fstype, MNTTYPE_AUTOFS) != 0) { pr_msg("%s: already mounted", mntp->mnt_mountp); continue; } /* * Compare the mnttab entry with the master map * entry. If the map or mount options are * different, then update this information * with a remount. */ if (strcmp(mntp->mnt_special, dir->dir_map) == 0 && compare_opts(dir->dir_opts, mntp->mnt_mntopts) == 0) { continue; /* no change */ } /* * Check for an overlaid direct autofs mount. * Cannot remount since it's inaccessible. */ omntp = (struct mnttab *)mntp; if (hasmntopt(omntp, "direct") != NULL) { mntp = find_mount(dir->dir_name, 0); omntp = (struct mnttab *)mntp; if (hasmntopt(omntp, "direct") == NULL) { if (verbose) pr_msg("%s: cannot remount", dir->dir_name); continue; } } dir->dir_remount = 1; } /* * Create a mount point if necessary * If the path refers to an existing symbolic * link, refuse to mount on it. This avoids * future problems. */ if (lstat(dir->dir_name, &stbuf) == 0) { if ((stbuf.st_mode & S_IFMT) != S_IFDIR) { pr_msg("%s: Not a directory", dir->dir_name); continue; } } else { if (mkdir_r(dir->dir_name)) { pr_msg("%s: %m", dir->dir_name); continue; } } ai.path = dir->dir_name; ai.opts = dir->dir_opts; ai.map = dir->dir_map; ai.subdir = ""; ai.direct = dir->dir_direct; if (dir->dir_direct) ai.key = dir->dir_name; else ai.key = ""; (void) sprintf(mntopts, "ignore,%s", dir->dir_direct ? "direct" : "indirect"); if (dir->dir_opts && *dir->dir_opts) { (void) strcat(mntopts, ","); (void) strcat(mntopts, dir->dir_opts); } mntflgs = MS_OPTIONSTR | (dir->dir_remount ? MS_REMOUNT : 0); if (mount(dir->dir_map, dir->dir_name, MS_DATA | mntflgs, MNTTYPE_AUTOFS, &ai, sizeof (ai), mntopts, MAX_MNTOPT_STR) < 0) { pr_msg("mount %s: %m", dir->dir_name); continue; } count++; if (verbose) { if (dir->dir_remount) pr_msg("%s remounted", dir->dir_name); else pr_msg("%s mounted", dir->dir_name); } } if (verbose && count == 0) pr_msg("no mounts"); /* * Now compare the /etc/mnttab with the master * map. Any autofs mounts in the /etc/mnttab * that are not in the master map must be * unmounted */ do_unmounts(); return (0); } /* * Find a mount entry given * the mountpoint path. * Optionally return the first * or last entry. */ static struct extmnttab * find_mount(mntpnt, first) char *mntpnt; int first; { struct mntlist *mntl; struct extmnttab *found = NULL; for (mntl = current_mounts; mntl; mntl = mntl->mntl_next) { if (strcmp(mntpnt, mntl->mntl_mnt->mnt_mountp) == 0) { found = mntl->mntl_mnt; if (first) break; } } return (found); } static char *ignore_opts[] = {"ignore", "direct", "indirect", "dev", NULL}; /* * Compare mount options * ignoring "ignore", "direct", "indirect" * and "dev=". */ static int compare_opts(opts, mntopts) char *opts, *mntopts; { char optbuf1[MAX_MNTOPT_STR], *s = optbuf1; char optbuf2[MAX_MNTOPT_STR]; char **opttbl1, **opttbl2; int nopts1, nopts2; char *ostart, *optr, *valp; int j, i, notsame; opttbl1 = opttbl2 = NULL; /* * Parse the two option strings to split them both into * lists of individual options. */ if (mntopts != NULL) (void) strcpy(s, mntopts); else *s = '\0'; if (*s != '\0') nopts1 = 1; else nopts1 = 0; for (s = strchr(s, ','); s != NULL; s = strchr(s, ',')) { nopts1++; s++; } if (nopts1) if ((opttbl1 = memalign(sizeof (char *), nopts1 * sizeof (char *))) == NULL) return (1); nopts1 = 0; s = optbuf1; for (ostart = optr = s; *optr != '\0'; ostart = optr) { if (getsubopt(&optr, ignore_opts, &valp) == -1) { opttbl1[nopts1++] = ostart; } } s = optbuf2; if (opts != NULL) (void) strcpy(s, opts); else *s = '\0'; if (*s != '\0') nopts2 = 1; else nopts2 = 0; for (s = strchr(s, ','); s != NULL; s = strchr(s, ',')) { nopts2++; s++; } if (nopts2) if ((opttbl2 = memalign(sizeof (char *), nopts2 * sizeof (char *))) == NULL) { notsame = 1; goto done; } nopts2 = 0; s = optbuf2; for (ostart = optr = s; *optr != '\0'; ostart = optr) { if (getsubopt(&optr, ignore_opts, &valp) == -1) { opttbl2[nopts2++] = ostart; } } if (nopts2 != nopts1) { notsame = 1; goto done; } notsame = 0; for (i = 0; i < nopts1; i++) { notsame = 1; for (j = 0; j < nopts2; j++) { if (strcmp(opttbl1[i], opttbl2[j]) == 0) { notsame = 0; break; } } if (notsame) break; } done: if (opttbl1 != NULL) free(opttbl1); if (opttbl2 != NULL) free(opttbl2); return (notsame); } static void usage() { pr_msg("Usage: automount [ -v ] [ -t duration ]"); exit(1); /* NOTREACHED */ } /* * Unmount any autofs mounts that * aren't in the master map */ static void do_unmounts() { struct mntlist *mntl; struct extmnttab *mnt; struct mnttab *omnt; struct autodir *dir; int current; int count = 0; struct zone_summary *zsp; zsp = fs_get_zone_summaries(); if (zsp == NULL) { pr_msg("Couldn't establish active zones"); exit(1); } for (mntl = current_mounts; mntl; mntl = mntl->mntl_next) { mnt = mntl->mntl_mnt; omnt = (struct mnttab *)mnt; if (strcmp(mnt->mnt_fstype, MNTTYPE_AUTOFS) != 0) continue; if (fs_mount_in_other_zone(zsp, mnt->mnt_mountp)) continue; /* * Don't unmount autofs mounts done * from the autofs mount command. * How do we tell them apart ? * Autofs mounts not eligible for auto-unmount * have the "nest" pseudo-option. */ if (hasmntopt(omnt, "nest") != NULL) continue; current = 0; for (dir = dir_head; dir; dir = dir->dir_next) { if (strcmp(dir->dir_name, mnt->mnt_mountp) == 0) { current = strcmp(dir->dir_map, "-null"); break; } } if (current) continue; if (umount(mnt->mnt_mountp) == 0) { if (verbose) { pr_msg("%s unmounted", mnt->mnt_mountp); } count++; } } if (verbose && count == 0) pr_msg("no unmounts"); } static int mkdir_r(dir) char *dir; { int err; char *slash; if (mkdir(dir, 0555) == 0 || errno == EEXIST) return (0); if (errno != ENOENT) return (-1); slash = strrchr(dir, '/'); if (slash == NULL) return (-1); *slash = '\0'; err = mkdir_r(dir); *slash++ = '/'; if (err || !*slash) return (err); return (mkdir(dir, 0555)); } /* * Print an error. * Works like printf (fmt string and variable args) * except that it will subsititute an error message * for a "%m" string (like syslog). */ /* VARARGS1 */ void pr_msg(const char *fmt, ...) { va_list ap; char buf[BUFSIZ], *p2; char *p1; char *nfmt; (void) strcpy(buf, "automount: "); p2 = buf + strlen(buf); nfmt = gettext(fmt); for (p1 = nfmt; *p1; p1++) { if (*p1 == '%' && *(p1+1) == 'm') { (void) strcpy(p2, strerror(errno)); p2 += strlen(p2); p1++; } else { *p2++ = *p1; } } if (p2 > buf && *(p2-1) != '\n') *p2++ = '\n'; *p2 = '\0'; va_start(ap, fmt); (void) vfprintf(stderr, buf, ap); va_end(ap); }