int main(int argc, char *argv[]) { long timeout_val; int c; int flushcache = 0; int unmount_automounted = 0; // Unmount automounted mounts struct autodir *dir, *d; char real_mntpnt[PATH_MAX]; struct stat stbuf; char *master_map = "auto_master"; int null; struct statfs *mntp; int count = 0; char *stack[STACKSIZ]; char **stkptr; char *defval; int fd; int flags, altflags; struct staticmap *static_ent; /* * Read in the values from config file first before we check * commandline options so the options override the file. */ if ((defopen(AUTOFSADMIN)) == 0) { if ((defval = defread("AUTOMOUNT_TIMEOUT=")) != NULL) { errno = 0; timeout_val = strtol(defval, (char **)NULL, 10); if (errno == 0 && timeout_val > 0 && timeout_val <= INT_MAX) mount_timeout = (int)timeout_val; } if ((defval = defread("AUTOMOUNT_VERBOSE=")) != NULL) { if (strncasecmp("true", defval, 4) == 0) verbose = TRUE; else verbose = FALSE; } if ((defval = defread("AUTOMOUNTD_TRACE=")) != NULL) { /* * Turn on tracing here too if the automountd * is set up to do it - since automount calls * many of the common library functions. */ errno = 0; trace = (int)strtol(defval, (char **)NULL, 10); if (errno != 0) trace = 0; } /* close defaults file */ defopen(NULL); } while ((c = getopt(argc, argv, "mM:D:f:t:vcu?")) != 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; case 'c': flushcache++; break; case 'u': unmount_automounted++; break; default: usage(); break; } } if (optind < argc) { pr_msg("%s: command line mountpoints/maps " "no longer supported", argv[optind]); usage(); } /* * Get an array of current system mounts */ num_current_mounts = getmntinfo(¤t_mounts, MNT_NOWAIT); if (num_current_mounts == 0) { pr_msg("Couldn't get current mounts: %m"); exit(1); } autofs_control_fd = open("/dev/" AUTOFS_CONTROL_DEVICE, O_RDONLY); if (autofs_control_fd == -1 && errno == ENOENT) { /* * Oops, we probably don't have the autofs kext * loaded. */ FTS *fts; static char *const paths[] = { "/Network", NULL }; FTSENT *ftsent; int error; /* * This means there can't be any autofs mounts yet, so * this is the first time we're being run since a reboot. * Clean out any stuff left in /Network from the reboot. */ fts = fts_open(paths, FTS_NOCHDIR|FTS_PHYSICAL|FTS_XDEV, NULL); if (fts != NULL) { while ((ftsent = fts_read(fts)) != NULL) { /* * We only remove directories - if * there are files, we assume they're * there for a purpose. * * We remove directories after we've * removed their children, so we want * to process directories visited in * post-order. * * We don't remove /Network itself. */ if (ftsent->fts_info == FTS_DP && ftsent->fts_level > FTS_ROOTLEVEL) rmdir(ftsent->fts_accpath); } fts_close(fts); } /* * Now load it. */ error = load_autofs(); if (error != 0) { pr_msg("can't load autofs kext"); exit(1); } /* * Try the open again. */ autofs_control_fd = open("/dev/" AUTOFS_CONTROL_DEVICE, O_RDONLY); } if (autofs_control_fd == -1) { if (errno == EBUSY) pr_msg("Another automount is running"); else pr_msg("Couldn't open %s: %m", "/dev/" AUTOFS_CONTROL_DEVICE); exit(1); } /* * Update the mount timeout. */ if (ioctl(autofs_control_fd, AUTOFS_SET_MOUNT_TO, &mount_timeout) == -1) pr_msg("AUTOFS_SET_MOUNT_TO failed: %m"); /* * Attempt to unmount any non-busy triggered mounts; this includes * not only autofs mounts, but, for example SMB Dfs mounts. * * This is done before sleep, and after a network change, to * try to get rid of as many network mounts as we can; each * unmounted network mount is a network mount on which we * can't hang. */ if (unmount_automounted) { if (verbose) pr_msg("Unmounting triggered mounts"); if (ioctl(autofs_control_fd, AUTOFS_UNMOUNT_TRIGGERED, 0) == -1) pr_msg("AUTOFS_UNMOUNT_TRIGGERED failed: %m"); exit(0); } if (flushcache) { /* * Notify the automounter that it should flush its caches, * as we might be on a different network with different maps. */ if (ioctl(autofs_control_fd, AUTOFS_NOTIFYCHANGE, 0) == -1) pr_msg("AUTOFS_NOTIFYCHANGE failed: %m"); } (void) umask(0); ns_setup(stack, &stkptr); (void) loadmaster_map(master_map, "", stack, &stkptr); /* * Mount the daemon at its mount points. */ for (dir = dir_head; dir; dir = dir->dir_next) { if (realpath(dir->dir_name, real_mntpnt) == NULL) { /* * We couldn't get the real path for this, * perhaps because it doesn't exist. * If it's not because it doesn't exist, just * give up on this entry. Otherwise, just null * out the real path - we'll try creating the * directory later, and will set dir_realpath * then, if that succeeds. */ if (errno != ENOENT) { pr_msg("%s: Can't convert to real path: %m", dir->dir_name); continue; } dir->dir_realpath = NULL; } else { dir->dir_realpath = strdup(real_mntpnt); if (dir->dir_realpath == NULL) { pr_msg("Couldn't allocate real path: %m"); exit(1); } } /* * 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 (paths_match(dir, d)) null = 1; } if (null) continue; /* * If this is -fstab, and there are no fstab "net" entries, * skip this map if our directory search path doesn't * include Active Directory. We don't want /Network/Servers * (or wherever it shows up) to exist if this system isn't * using AD (AD supplies fstab entries on the fly, so they * might not exist right now) and we don't have any fstab * entries. */ if (strcmp(dir->dir_map, "-fstab") == 0) { if (!have_ad() && !havefstabkeys()) { /* * We're not using AD, and fstab is * inaccessible or devoid of "net" entries. */ free(dir->dir_map); dir->dir_map = strdup("-null"); continue; } endfsent(); } /* * If this is -fstab or -static, and there's another entry * that's supposed to mount something on the same directory * and isn't "-fstab" or "-static", ignore this; we might * have a server that's supplying real automounter maps for * the benefit of OS X systems with autofs and also supplying * fstab entries for the benefit of older OS X systems, and * we want to mount the real automounter map, not the -fstab * or -static map, in that case. */ if (strcmp(dir->dir_map, "-fstab") == 0 || strcmp(dir->dir_map, "-static") == 0) { for (d = dir_head; d; d = d->dir_next) { if (paths_match(dir, d) && strcmp(d->dir_map, "-fstab") != 0 && strcmp(d->dir_map, "-static") != 0) { pr_msg("%s: ignoring redundant %s map", dir->dir_name, dir->dir_map); break; } } if (d != NULL) { continue; } } /* * Parse the mount options and get additional flags to pass * to mount() (standard mount options) and autofs mount * options. * * XXX - we ignore flags on an update; if they're different * from the current flags for that mount, we'd need to do a * remount. */ if (!parse_mntopts(dir->dir_opts, &flags, &altflags)) { /* * Failed. */ continue; } /* * If this is -static, check whether the entry refers * to this host; if so, make the appropriate symlink * exist at the "mount point" path. */ if (strcmp(dir->dir_map, "-static") == 0) { static_ent = get_staticmap_entry(dir->dir_name); if (static_ent == NULL) { /* * Whiskey tango foxtrot? There should * be an entry here. Log an error and * ignore this mount. */ pr_msg("can't find fstab entry for %s", dir->dir_name); continue; } if (host_is_us(static_ent->host, strlen(static_ent->host)) || self_check(static_ent->host)) { /* * Yup, this is us. * Try to make the appropriate symlink. */ make_symlink(static_ent->localpath, dir->dir_name); release_staticmap_entry(static_ent); continue; } release_staticmap_entry(static_ent); } /* * Check whether there's already an entry * in the mnttab for this mountpoint. */ if (dir->dir_realpath != NULL && (mntp = find_mount(dir->dir_realpath)) != NULL) { struct autofs_update_args au; /* * If it's not an autofs mount - don't * mount over it. */ if (strcmp(mntp->f_fstypename, MNTTYPE_AUTOFS) != 0) { pr_msg("%s: already mounted on %s", mntp->f_mntfromname, dir->dir_realpath); continue; } /* * This is already mounted, so just update it. * We don't bother to check whether any options are * changing, as we'd have to make a trip into the * kernel to get the current options to check them, * so we might as well just make a trip to do the * update. */ au.fsid = mntp->f_fsid; au.opts = dir->dir_opts; au.map = dir->dir_map; au.mntflags = altflags; au.direct = dir->dir_direct; au.node_type = dir->dir_direct ? NT_TRIGGER : 0; if (ioctl(autofs_control_fd, AUTOFS_UPDATE_OPTIONS, &au) < 0) { pr_msg("update %s: %m", dir->dir_realpath); continue; } if (verbose) pr_msg("%s updated", dir->dir_realpath); } else { struct autofs_args ai; int st_flags = 0; /* * This trigger isn't already mounted; either * the path doesn't exist at all, or it * exists but nothing is mounted on it. * * Create a mount point if necessary * If the path refers to an existing symbolic * link, refuse to mount on it. This avoids * future problems. (We don't use dir->dir_realpath * because that's never a symbolic link.) */ 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; } st_flags = stbuf.st_flags; /* * Either realpath() succeeded or it * failed with ENOENT; otherwise, we * would have quit before getting here. * * If it failed, report an error, as * the problem isn't that "dir->dir_name" * doesn't exist, the problem is that, * somehow, we got ENOENT even though * it exists. */ if (dir->dir_realpath == NULL) { errno = ENOENT; pr_msg("%s: Can't convert to real path: %m", dir->dir_name); continue; } } else { /* * Mountpoint doesn't exist. * * Create it unless it's under /Volumes. * At boot time it's possible the volume * containing the mountpoint hasn't mounted yet. */ if (strncmp(dir->dir_name, "/Volumes/", 9) == 0) { pr_msg("%s: mountpoint unavailable", dir->dir_name); continue; } if (mkdir_r(dir->dir_name)) { pr_msg("%s: %m", dir->dir_name); continue; } /* * realpath() presumably didn't succeed, * as dir->dir_name couldn't be statted. * Call it again, to get the real path * corresponding to the newly-created * mount point. */ if (realpath(dir->dir_name, real_mntpnt) == NULL) { /* * Failed. */ pr_msg("%s: Can't convert to real path: %m", dir->dir_name); continue; } dir->dir_realpath = strdup(real_mntpnt); if (dir->dir_realpath == NULL) { pr_msg("Couldn't allocate real path for %s: %m", dir->dir_name); continue; } } /* * If the "hidefromfinder" option is set for * this autofs mountpoint then also set the * UF_HIDDEN bit on the directory so it'll still * be invisible to the Finder even if not mounted on. */ if (altflags & AUTOFS_MNT_HIDEFROMFINDER) st_flags |= UF_HIDDEN; else st_flags &= ~UF_HIDDEN; if (chflags(dir->dir_name, st_flags) < 0) pr_msg("%s: can't set hidden", dir->dir_name); /* * Mount it. Use the real path (symlink-free), * for reasons mentioned above. */ ai.version = AUTOFS_ARGSVERSION; ai.path = dir->dir_realpath; 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 = ""; ai.mntflags = altflags; ai.mount_type = MOUNT_TYPE_MAP; /* top-level autofs mount */ ai.node_type = dir->dir_direct ? NT_TRIGGER : 0; if (mount(MNTTYPE_AUTOFS, dir->dir_realpath, MNT_DONTBROWSE | MNT_AUTOMOUNTED | flags, &ai) < 0) { pr_msg("mount %s: %m", dir->dir_realpath); continue; } if (verbose) pr_msg("%s mounted", dir->dir_realpath); } count++; } 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 * * XXX - if there are no autofs mounts left, should we * unload autofs, or arrange that it be unloaded? */ do_unmounts(); /* * Let PremountHomeDirectoryWithAuthentication() know that we're * done. */ fd = open("/var/run/automount.initialized", O_CREAT|O_WRONLY, 0600); close(fd); return (0); }
/** * Executes a rule. Contains evaluation of all conditions prior * to execution. * * @1 Hotplug event structure * @2 The rule to be executed * * Returns: 0 if success, -1 if the whole event is to be * discared, 1 if bail out of this particular rule was required */ int rule_execute(struct hotplug2_event_t *event, struct rule_t *rule) { int i, last_rv, res; char **env; for (i = 0; i < rule->conditions_c; i++) { if (rule_condition_eval(event, &(rule->conditions[i])) != EVAL_MATCH) return 0; } res = 0; last_rv = 0; env = xmalloc(sizeof(char *) * event->env_vars_c); for (i = 0; i < event->env_vars_c; i++) { env[i] = alloc_env(event->env_vars[i].key, event->env_vars[i].value); putenv(env[i]); } for (i = 0; i < rule->actions_c; i++) { switch (rule->actions[i].type) { case ACT_STOP_PROCESSING: res = 1; break; case ACT_STOP_IF_FAILED: if (last_rv != 0) res = 1; break; case ACT_NEXT_EVENT: res = -1; break; case ACT_NEXT_IF_FAILED: if (last_rv != 0) res = -1; break; case ACT_MAKE_DEVICE: last_rv = make_dev_from_event(event, rule->actions[i].parameter[0], strtoul(rule->actions[i].parameter[1], NULL, 0)); break; case ACT_CHMOD: last_rv = chmod_file(event, rule->actions[i].parameter[0], rule->actions[i].parameter[1]); break; case ACT_CHOWN: case ACT_CHGRP: last_rv = chown_chgrp(event, rule->actions[i].type, rule->actions[i].parameter[0], rule->actions[i].parameter[1]); break; case ACT_SYMLINK: last_rv = make_symlink(event, rule->actions[i].parameter[0], rule->actions[i].parameter[1]); break; case ACT_RUN_SHELL: last_rv = exec_shell(event, rule->actions[i].parameter[0]); break; case ACT_RUN_NOSHELL: last_rv = exec_noshell(event, rule->actions[i].parameter[0], rule->actions[i].parameter); break; case ACT_SETENV: last_rv = setenv(rule->actions[i].parameter[0], rule->actions[i].parameter[1], 1); break; case ACT_REMOVE: last_rv = unlink(rule->actions[i].parameter[0]); rmdir_p(rule->actions[i].parameter[0]); break; case ACT_DEBUG: print_debug(event); last_rv = 0; break; } if (res != 0) break; } for (i = 0; i < event->env_vars_c; i++) { unsetenv(event->env_vars[i].key); free(env[i]); } free(env); return res; }