1/*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License, Version 1.0 only
6 * (the "License").  You may not use this file except in compliance
7 * with the License.
8 *
9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10 * or http://www.opensolaris.org/os/licensing.
11 * See the License for the specific language governing permissions
12 * and limitations under the License.
13 *
14 * When distributing Covered Code, include this CDDL HEADER in each
15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16 * If applicable, add the following below this CDDL HEADER, with the
17 * fields enclosed by brackets "[]" replaced with your own identifying
18 * information: Portions Copyright [yyyy] [name of copyright owner]
19 *
20 * CDDL HEADER END
21 */
22/*
23 * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
24 * Use is subject to license terms.
25 */
26
27/*
28 * Portions Copyright 2007-2012 Apple Inc.
29 */
30
31#pragma ident	"@(#)automount.c	1.50	05/06/08 SMI"
32
33#include <ctype.h>
34#include <stdio.h>
35#include <unistd.h>
36#include <fcntl.h>
37#include <stdlib.h>
38#include <locale.h>
39#include <stdarg.h>
40#include <errno.h>
41#include <string.h>
42#include <dirent.h>
43#include <signal.h>
44#include <fstab.h>
45#include <mntopts.h>
46#include <syslog.h>
47#include <sys/param.h>
48#include <sys/time.h>
49#include <sys/types.h>
50#include <sys/stat.h>
51#include <sys/ioctl.h>
52#include <sys/mount.h>
53#include <fts.h>
54
55#include <OpenDirectory/OpenDirectory.h>
56
57#include "deflt.h"
58#include "autofs.h"
59#include "automount.h"
60#include "automount_od.h"
61#include "umount_by_fsid.h"
62
63static int parse_mntopts(const char *, int *, int *);
64static int paths_match(struct autodir *, struct autodir *);
65static int mkdir_r(char *);
66static void rmdir_r(char *);
67static int have_ad(void);
68struct autodir *dir_head;
69struct autodir *dir_tail;
70static int num_current_mounts;
71static struct statfs *current_mounts;
72static void make_symlink(const char *, const char *);
73static struct statfs *find_mount(const char *);
74int verbose = 0;
75int trace = 0;
76
77static int autofs_control_fd;
78
79static void usage(void);
80static void do_unmounts(void);
81static int load_autofs(void);
82
83static int mount_timeout = AUTOFS_MOUNT_TIMEOUT;
84
85static char gKextLoadCommand[] = "/sbin/kextload";
86static char gKextLoadPath[] = "/System/Library/Extensions/autofs.kext";
87
88/*
89 * XXX
90 * The following are needed because they're used in auto_subr.c and
91 * we link with it. Should avoid this.
92 */
93pthread_mutex_t cleanup_lock;
94pthread_cond_t cleanup_start_cv;
95pthread_cond_t cleanup_done_cv;
96
97int
98main(int argc, char *argv[])
99{
100	long timeout_val;
101	int c;
102	int flushcache = 0;
103	int unmount_automounted = 0;	// Unmount automounted mounts
104	struct autodir *dir, *d;
105	char real_mntpnt[PATH_MAX];
106	struct stat stbuf;
107	char *master_map = "auto_master";
108	int null;
109	struct statfs *mntp;
110	int count = 0;
111	char *stack[STACKSIZ];
112	char **stkptr;
113	char *defval;
114	int fd;
115	int flags, altflags;
116	struct staticmap *static_ent;
117
118	/*
119	 * Read in the values from config file first before we check
120	 * commandline options so the options override the file.
121	 */
122	if ((defopen(AUTOFSADMIN)) == 0) {
123		if ((defval = defread("AUTOMOUNT_TIMEOUT=")) != NULL) {
124			errno = 0;
125			timeout_val = strtol(defval, (char **)NULL, 10);
126			if (errno == 0 && timeout_val > 0 &&
127			    timeout_val <= INT_MAX)
128				mount_timeout = (int)timeout_val;
129		}
130		if ((defval = defread("AUTOMOUNT_VERBOSE=")) != NULL) {
131			if (strncasecmp("true", defval, 4) == 0)
132				verbose = TRUE;
133			else
134				verbose = FALSE;
135		}
136		if ((defval = defread("AUTOMOUNTD_TRACE=")) != NULL) {
137			/*
138			 * Turn on tracing here too if the automountd
139			 * is set up to do it - since automount calls
140			 * many of the common library functions.
141			 */
142			errno = 0;
143			trace = (int)strtol(defval, (char **)NULL, 10);
144			if (errno != 0)
145				trace = 0;
146		}
147
148		/* close defaults file */
149		defopen(NULL);
150	}
151
152	while ((c = getopt(argc, argv, "mM:D:f:t:vcu?")) != EOF) {
153		switch (c) {
154		case 'm':
155			pr_msg("Warning: -m option not supported");
156			break;
157		case 'M':
158			pr_msg("Warning: -M option not supported");
159			break;
160		case 'D':
161			pr_msg("Warning: -D option not supported");
162			break;
163		case 'f':
164			pr_msg("Error: -f option no longer supported");
165			usage();
166			break;
167		case 't':
168			if (strchr(optarg, '=')) {
169				pr_msg("Error: invalid value for -t");
170				usage();
171			}
172			mount_timeout = atoi(optarg);
173			break;
174		case 'v':
175			verbose++;
176			break;
177		case 'c':
178			flushcache++;
179			break;
180		case 'u':
181			unmount_automounted++;
182			break;
183		default:
184			usage();
185			break;
186		}
187	}
188
189	if (optind < argc) {
190		pr_msg("%s: command line mountpoints/maps "
191			"no longer supported",
192			argv[optind]);
193		usage();
194	}
195
196	/*
197	 * Get an array of current system mounts
198	 */
199	num_current_mounts = getmntinfo(&current_mounts, MNT_NOWAIT);
200	if (num_current_mounts == 0) {
201		pr_msg("Couldn't get current mounts: %m");
202		exit(1);
203	}
204
205	autofs_control_fd = open("/dev/" AUTOFS_CONTROL_DEVICE, O_RDONLY);
206	if (autofs_control_fd == -1 && errno == ENOENT) {
207		/*
208		 * Oops, we probably don't have the autofs kext
209		 * loaded.
210		 */
211		FTS *fts;
212		static char *const paths[] = { "/Network", NULL };
213		FTSENT *ftsent;
214		int error;
215
216		/*
217		 * This means there can't be any autofs mounts yet, so
218		 * this is the first time we're being run since a reboot.
219		 * Clean out any stuff left in /Network from the reboot.
220		 */
221		fts = fts_open(paths, FTS_NOCHDIR|FTS_PHYSICAL|FTS_XDEV,
222		    NULL);
223		if (fts != NULL) {
224			while ((ftsent = fts_read(fts)) != NULL) {
225				/*
226				 * We only remove directories - if
227				 * there are files, we assume they're
228				 * there for a purpose.
229				 *
230				 * We remove directories after we've
231				 * removed their children, so we want
232				 * to process directories visited in
233				 * post-order.
234				 *
235				 * We don't remove /Network itself.
236				 */
237				if (ftsent->fts_info == FTS_DP &&
238				    ftsent->fts_level > FTS_ROOTLEVEL)
239					rmdir(ftsent->fts_accpath);
240			}
241			fts_close(fts);
242		}
243
244		/*
245		 * Now load it.
246		 */
247		error = load_autofs();
248		if (error != 0) {
249			pr_msg("can't load autofs kext");
250			exit(1);
251		}
252
253		/*
254		 * Try the open again.
255		 */
256		autofs_control_fd = open("/dev/" AUTOFS_CONTROL_DEVICE,
257		    O_RDONLY);
258	}
259	if (autofs_control_fd == -1) {
260		if (errno == EBUSY)
261			pr_msg("Another automount is running");
262		else
263			pr_msg("Couldn't open %s: %m", "/dev/" AUTOFS_CONTROL_DEVICE);
264		exit(1);
265	}
266
267	/*
268	 * Update the mount timeout.
269	 */
270	if (ioctl(autofs_control_fd, AUTOFS_SET_MOUNT_TO, &mount_timeout) == -1)
271		pr_msg("AUTOFS_SET_MOUNT_TO failed: %m");
272
273	/*
274	 * Attempt to unmount any non-busy triggered mounts; this includes
275	 * not only autofs mounts, but, for example SMB Dfs mounts.
276	 *
277	 * This is done before sleep, and after a network change, to
278	 * try to get rid of as many network mounts as we can; each
279	 * unmounted network mount is a network mount on which we
280	 * can't hang.
281	 */
282	if (unmount_automounted) {
283		if (verbose)
284			pr_msg("Unmounting triggered mounts");
285		if (ioctl(autofs_control_fd, AUTOFS_UNMOUNT_TRIGGERED, 0) == -1)
286			pr_msg("AUTOFS_UNMOUNT_TRIGGERED failed: %m");
287		exit(0);
288	}
289
290	if (flushcache) {
291		/*
292		 * Notify the automounter that it should flush its caches,
293		 * as we might be on a different network with different maps.
294		 */
295		if (ioctl(autofs_control_fd, AUTOFS_NOTIFYCHANGE, 0) == -1)
296			pr_msg("AUTOFS_NOTIFYCHANGE failed: %m");
297	}
298
299	(void) umask(0);
300	ns_setup(stack, &stkptr);
301
302	(void) loadmaster_map(master_map, "", stack, &stkptr);
303
304	/*
305	 * Mount the daemon at its mount points.
306	 */
307	for (dir = dir_head; dir; dir = dir->dir_next) {
308
309		if (realpath(dir->dir_name, real_mntpnt) == NULL) {
310			/*
311			 * We couldn't get the real path for this,
312			 * perhaps because it doesn't exist.
313			 * If it's not because it doesn't exist, just
314			 * give up on this entry.  Otherwise, just null
315			 * out the real path - we'll try creating the
316			 * directory later, and will set dir_realpath
317			 * then, if that succeeds.
318			 */
319			if (errno != ENOENT) {
320				pr_msg("%s: Can't convert to real path: %m",
321				    dir->dir_name);
322				continue;
323			}
324			dir->dir_realpath = NULL;
325		} else {
326			dir->dir_realpath = strdup(real_mntpnt);
327			if (dir->dir_realpath == NULL) {
328				pr_msg("Couldn't allocate real path: %m");
329				exit(1);
330			}
331		}
332
333		/*
334		 * Skip null entries
335		 */
336		if (strcmp(dir->dir_map, "-null") == 0)
337			continue;
338
339		/*
340		 * Skip null'ed entries
341		 */
342		null = 0;
343		for (d = dir->dir_prev; d; d = d->dir_prev) {
344			if (paths_match(dir, d))
345				null = 1;
346		}
347		if (null)
348			continue;
349
350		/*
351		 * If this is -fstab, and there are no fstab "net" entries,
352		 * skip this map if our directory search path doesn't
353		 * include Active Directory.  We don't want /Network/Servers
354		 * (or wherever it shows up) to exist if this system isn't
355		 * using AD (AD supplies fstab entries on the fly, so they
356		 * might not exist right now) and we don't have any fstab
357		 * entries.
358		 */
359		if (strcmp(dir->dir_map, "-fstab") == 0) {
360			if (!have_ad() && !havefstabkeys()) {
361				/*
362				 * We're not using AD, and fstab is
363				 * inaccessible or devoid of "net" entries.
364				 */
365				free(dir->dir_map);
366				dir->dir_map = strdup("-null");
367				continue;
368			}
369			endfsent();
370		}
371
372		/*
373		 * If this is -fstab or -static, and there's another entry
374		 * that's supposed to mount something on the same directory
375		 * and isn't "-fstab" or "-static", ignore this; we might
376		 * have a server that's supplying real automounter maps for
377		 * the benefit of OS X systems with autofs and also supplying
378		 * fstab entries for the benefit of older OS X systems, and
379		 * we want to mount the real automounter map, not the -fstab
380		 * or -static map, in that case.
381		 */
382		if (strcmp(dir->dir_map, "-fstab") == 0 ||
383		    strcmp(dir->dir_map, "-static") == 0) {
384			for (d = dir_head; d; d = d->dir_next) {
385				if (paths_match(dir, d) &&
386				    strcmp(d->dir_map, "-fstab") != 0 &&
387				    strcmp(d->dir_map, "-static") != 0) {
388					pr_msg("%s: ignoring redundant %s map",
389					    dir->dir_name, dir->dir_map);
390					continue;
391				}
392			}
393		}
394
395		/*
396		 * Parse the mount options and get additional flags to pass
397		 * to mount() (standard mount options) and autofs mount
398		 * options.
399		 *
400		 * XXX - we ignore flags on an update; if they're different
401		 * from the current flags for that mount, we'd need to do a
402		 * remount.
403		 */
404		if (!parse_mntopts(dir->dir_opts, &flags, &altflags)) {
405			/*
406			 * Failed.
407			 */
408			continue;
409		}
410
411		/*
412		 * If this is -static, check whether the entry refers
413		 * to this host; if so, make the appropriate symlink
414		 * exist at the "mount point" path.
415		 */
416		if (strcmp(dir->dir_map, "-static") == 0) {
417			static_ent = get_staticmap_entry(dir->dir_name);
418			if (static_ent == NULL) {
419				/*
420				 * Whiskey tango foxtrot?  There should
421				 * be an entry here.  Log an error and
422				 * ignore this mount.
423				 */
424				pr_msg("can't find fstab entry for %s",
425				    dir->dir_name);
426				continue;
427			}
428			if (host_is_us(static_ent->host, strlen(static_ent->host)) ||
429			    self_check(static_ent->host)) {
430				/*
431				 * Yup, this is us.
432				 * Try to make the appropriate symlink.
433				 */
434				make_symlink(static_ent->localpath,
435				    dir->dir_name);
436				release_staticmap_entry(static_ent);
437				continue;
438			}
439			release_staticmap_entry(static_ent);
440		}
441
442		/*
443		 * Check whether there's already an entry
444		 * in the mnttab for this mountpoint.
445		 */
446		if (dir->dir_realpath != NULL &&
447		    (mntp = find_mount(dir->dir_realpath)) != NULL) {
448			struct autofs_update_args au;
449
450			/*
451			 * If it's not an autofs mount - don't
452			 * mount over it.
453			 */
454			if (strcmp(mntp->f_fstypename, MNTTYPE_AUTOFS) != 0) {
455				pr_msg("%s: already mounted on %s",
456					mntp->f_mntfromname, dir->dir_realpath);
457				continue;
458			}
459
460			/*
461			 * This is already mounted, so just update it.
462			 * We don't bother to check whether any options are
463			 * changing, as we'd have to make a trip into the
464			 * kernel to get the current options to check them,
465			 * so we might as well just make a trip to do the
466			 * update.
467			 */
468			au.fsid		= mntp->f_fsid;
469			au.opts		= dir->dir_opts;
470			au.map		= dir->dir_map;
471			au.mntflags	= altflags;
472			au.direct 	= dir->dir_direct;
473			au.node_type	= dir->dir_direct ? NT_TRIGGER : 0;
474
475			if (ioctl(autofs_control_fd, AUTOFS_UPDATE_OPTIONS,
476			    &au) < 0) {
477				pr_msg("update %s: %m", dir->dir_realpath);
478				continue;
479			}
480			if (verbose)
481				pr_msg("%s updated", dir->dir_realpath);
482		} else {
483			struct autofs_args ai;
484			int st_flags = 0;
485
486			/*
487			 * This trigger isn't already mounted; either
488			 * the path doesn't exist at all, or it
489			 * exists but nothing is mounted on it.
490			 *
491			 * Create a mount point if necessary
492			 * If the path refers to an existing symbolic
493			 * link, refuse to mount on it.  This avoids
494			 * future problems.  (We don't use dir->dir_realpath
495			 * because that's never a symbolic link.)
496			 */
497			if (lstat(dir->dir_name, &stbuf) == 0) {
498				if ((stbuf.st_mode & S_IFMT) != S_IFDIR) {
499					pr_msg("%s: Not a directory", dir->dir_name);
500					continue;
501				}
502				st_flags = stbuf.st_flags;
503
504				/*
505				 * Either realpath() succeeded or it
506				 * failed with ENOENT; otherwise, we
507				 * would have quit before getting here.
508				 *
509				 * If it failed, report an error, as
510				 * the problem isn't that "dir->dir_name"
511				 * doesn't exist, the problem is that,
512				 * somehow, we got ENOENT even though
513				 * it exists.
514				 */
515				if (dir->dir_realpath == NULL) {
516					errno = ENOENT;
517					pr_msg("%s: Can't convert to real path: %m",
518					    dir->dir_name);
519					continue;
520				}
521			} else {
522				/*
523				 * Mountpoint doesn't exist.
524				 *
525				 * Create it unless it's under /Volumes.
526				 * At boot time it's possible the volume
527				 * containing the mountpoint hasn't mounted yet.
528				 */
529				if (strncmp(dir->dir_name, "/Volumes/", 9) == 0) {
530					pr_msg("%s: mountpoint unavailable", dir->dir_name);
531					continue;
532				}
533
534				if (mkdir_r(dir->dir_name)) {
535					pr_msg("%s: %m", dir->dir_name);
536					continue;
537				}
538
539				/*
540				 * realpath() presumably didn't succeed,
541				 * as dir->dir_name couldn't be statted.
542				 * Call it again, to get the real path
543				 * corresponding to the newly-created
544				 * mount point.
545				 */
546				if (realpath(dir->dir_name, real_mntpnt) == NULL) {
547					/*
548					 * Failed.
549					 */
550					pr_msg("%s: Can't convert to real path: %m",
551					    dir->dir_name);
552					continue;
553				}
554				dir->dir_realpath = strdup(real_mntpnt);
555				if (dir->dir_realpath == NULL) {
556					pr_msg("Couldn't allocate real path for %s: %m",
557					    dir->dir_name);
558					continue;
559				}
560			}
561
562			/*
563			 * If the "hidefromfinder" option is set for
564			 * this autofs mountpoint then also set the
565			 * UF_HIDDEN bit on the directory so it'll still
566			 * be invisible to the Finder even if not mounted on.
567			 */
568			if (altflags & AUTOFS_MNT_HIDEFROMFINDER)
569				st_flags |= UF_HIDDEN;
570			else
571				st_flags &= ~UF_HIDDEN;
572			if (chflags(dir->dir_name, st_flags) < 0)
573				pr_msg("%s: can't set hidden", dir->dir_name);
574
575			/*
576			 * Mount it.  Use the real path (symlink-free),
577			 * for reasons mentioned above.
578			 */
579			ai.version	= AUTOFS_ARGSVERSION;
580			ai.path 	= dir->dir_realpath;
581			ai.opts		= dir->dir_opts;
582			ai.map		= dir->dir_map;
583			ai.subdir	= "";
584			ai.direct 	= dir->dir_direct;
585			if (dir->dir_direct)
586				ai.key = dir->dir_name;
587			else
588				ai.key = "";
589			ai.mntflags	= altflags;
590			ai.mount_type	= MOUNT_TYPE_MAP;	/* top-level autofs mount */
591			ai.node_type	= dir->dir_direct ? NT_TRIGGER : 0;
592
593			if (mount(MNTTYPE_AUTOFS, dir->dir_realpath,
594			    MNT_DONTBROWSE | MNT_AUTOMOUNTED | flags,
595			    &ai) < 0) {
596				pr_msg("mount %s: %m", dir->dir_realpath);
597				continue;
598			}
599			if (verbose)
600				pr_msg("%s mounted", dir->dir_realpath);
601		}
602
603		count++;
604	}
605
606	if (verbose && count == 0)
607		pr_msg("no mounts");
608
609	/*
610	 * Now compare the /etc/mnttab with the master
611	 * map.  Any autofs mounts in the /etc/mnttab
612	 * that are not in the master map must be
613	 * unmounted
614	 *
615	 * XXX - if there are no autofs mounts left, should we
616	 * unload autofs, or arrange that it be unloaded?
617	 */
618	do_unmounts();
619
620	/*
621	 * Let PremountHomeDirectoryWithAuthentication() know that we're
622	 * done.
623	 */
624	fd = open("/var/run/automount.initialized", O_CREAT|O_WRONLY, 0600);
625	close(fd);
626
627	return (0);
628}
629
630static void
631make_symlink(const char *target, const char *path)
632{
633	struct stat stbuf;
634	char linktarget[PATH_MAX + 1];
635	ssize_t pathlength;
636	struct statfs *mnt;
637	struct stat st;
638
639	/*
640	 * Does the target exist?
641	 */
642	if (lstat(path, &stbuf) == 0) {
643		/*
644		 * Yes.  What is it?
645		 */
646		if ((stbuf.st_mode & S_IFMT) == S_IFLNK) {
647			/*
648			 * It's a symlink.
649			 * What does it point to?
650			 */
651			pathlength = readlink(path, linktarget, PATH_MAX);
652			if (pathlength == -1) {
653				/*
654				 * FAIL.
655				 */
656				pr_msg("can't read target of %s: %m", path);
657				return;
658			}
659			linktarget[pathlength] = '\0';
660
661			/*
662			 * Does it point to the same place that we
663			 * want it to point to?
664			 *
665			 * XXX - case-sensitivity?  That's hard to
666			 * handle, given that the path might cross
667			 * multiple file systems with different case-
668			 * sensitivities.
669			 */
670			if (strcmp(linktarget, target) == 0) {
671				/*
672				 * Yes, it does.
673				 * We don't need to do anything.
674				 */
675				if (verbose)
676					pr_msg("link %s unchanged", path);
677				return;
678			}
679
680			/*
681			 * Get rid of the existing symlink.
682			 */
683			if (unlink(path) == -1) {
684				pr_msg("can't unlink %s: %m", path);
685				return;
686			}
687		} else if ((stbuf.st_mode & S_IFMT) == S_IFDIR) {
688			/*
689			 * It's a directory.
690			 * Is there an autofs mount atop it?
691			 */
692			mnt = find_mount(path);
693			if (mnt != NULL) {
694				/*
695				 * Something's mounted atop it; is it
696				 * autofs?
697				 */
698				if (strcmp(mnt->f_fstypename, MNTTYPE_AUTOFS) == 0) {
699					/*
700					 * Yes.  Try to unmount it (and
701					 * everything under it).
702					 */
703					if (ioctl(autofs_control_fd,
704					    AUTOFS_UNMOUNT, &mnt->f_fsid) != 0) {
705						/*
706						 * Failed.
707						 * Leave it alone for now.
708						 */
709						return;
710					}
711				}
712			}
713
714			/*
715			 * Now try to remove the directory.
716			 */
717			if (rmdir(path) != 0) {
718				/*
719				 * Failed.  Leave it alone.
720				 */
721				return;
722			}
723		} else {
724			/*
725			 * Neither a symlink nor a directory.
726			 * Leave it alone.
727			 */
728			return;
729		}
730	} else {
731		/*
732		 * lstat() failed; is it because the target doesn't
733		 * exist, or because we couldn't get its
734		 * information?
735		 */
736		if (errno != ENOENT) {
737			/*
738			 * We couldn't get its information.
739			 * Leave it alone.
740			 */
741			return;
742		}
743	}
744
745	/*
746	 * OK, the target should not exist.
747	 * Make the symlink.
748	 */
749	if (symlink(target, path) == -1) {
750		pr_msg("can't create symlink from %s to %s: %m",
751		    path, target);
752	}
753
754	/*
755	 * Validate the symlink in case the path and target
756	 * don't match but still yield an ELOOP.
757	 */
758	if (stat(path, &st) < 0) {
759		pr_msg("Invalid symbolic link: %s to %s: %m", path, target);
760		(void) unlink(path);
761	}
762}
763
764/*
765 * Find the first mount entry given the mountpoint path.
766 */
767static struct statfs *
768find_mount(mntpnt)
769	const char *mntpnt;
770{
771	int i;
772	struct statfs *mnt;
773
774	for (i = 0; i < num_current_mounts; i++) {
775		mnt = &current_mounts[i];
776		if (strcmp(mntpnt, mnt->f_mntonname) == 0)
777			return (mnt);
778	}
779
780	return (NULL);
781}
782
783static void
784usage()
785{
786	pr_msg("Usage: automount  [ -vcu ]  [ -t duration ]");
787	exit(1);
788	/* NOTREACHED */
789}
790
791/*
792 * Given a mount options string, get the flags argument to pass to mount()
793 * and the autofs mount options.
794 *
795 * We put "nobrowse" in front of MOPT_STDOPTS so that the mount
796 * options "browse" and "nobrowse" are interpreted as autofs
797 * mount options controlling whether you can get a directory listing,
798 * not OS X mount options controlling whether the mounts are treated as
799 * "real" volumes or not (we force MNT_NOBROWSE on for any mounts we do).
800 */
801static const struct mntopt mopts_autofs[] = {
802	{ "browse",			1, AUTOFS_MNT_NOBROWSE, 1 },
803	MOPT_STDOPTS,
804	{ MNTOPT_RESTRICT,		0, AUTOFS_MNT_RESTRICT, 1 },
805	{ MNTOPT_HIDEFROMFINDER,	0, AUTOFS_MNT_HIDEFROMFINDER, 1 },
806	{ NULL,				0, 0, 0 }
807};
808
809static int
810parse_mntopts(const char *opts, int *flags, int *altflags)
811{
812	mntoptparse_t mp;
813
814	/*
815	 * Parse the mount options and fill in "flags" and "altflags".
816	 */
817	*flags = *altflags = 0;
818	getmnt_silent = 1;
819	mp = getmntopts(opts, mopts_autofs, flags, altflags);
820	if (mp == NULL) {
821		pr_msg("memory allocation failure");
822		return (0);
823	}
824	freemntopts(mp);
825
826	return (1);
827}
828
829/*
830 * Unmount any autofs mounts that
831 * aren't in the master map
832 */
833static void
834do_unmounts(void)
835{
836	int i;
837	struct statfs *mnt;
838	struct autodir *dir;
839	int current;
840	int count = 0;
841	static const char triggered[] = "triggered";
842
843	for (i = 0; i < num_current_mounts; i++) {
844		mnt = &current_mounts[i];
845		if (strcmp(mnt->f_fstypename, MNTTYPE_AUTOFS) != 0)
846			continue;
847		/*
848		 * Don't unmount autofs mounts done
849		 * from the autofs mount command.
850		 * How do we tell them apart ?
851		 * Autofs mounts not eligible for auto-unmount
852		 * have an f_mntfromname of "subtrigger" (those
853		 * are subtriggers on top of a non-autofs mount)
854		 * or an f_mntfromname beginning with "triggered"
855		 * (those are autofs maps specified by map entries
856		 * to be automounted).
857		 * They will be unmounted, if possible, when the
858		 * top-level autofs mount they're under is
859		 * unmounted - as will any other mounts under
860		 * that top-level autofs mount.
861		 */
862		if (strcmp(mnt->f_mntfromname, "subtrigger") == 0 ||
863		    strncmp(mnt->f_mntfromname, triggered, sizeof (triggered) - 1) == 0)
864			continue;
865
866		current = 0;
867		for (dir = dir_head; dir; dir = dir->dir_next) {
868			if (dir->dir_realpath != NULL &&
869			    strcmp(dir->dir_realpath, mnt->f_mntonname) == 0) {
870				current = strcmp(dir->dir_map, "-null");
871				break;
872			}
873		}
874		if (current)
875			continue;
876
877		/*
878		 * Mark this as being unmounted, and try to unmount it.
879		 */
880		if (ioctl(autofs_control_fd, AUTOFS_UNMOUNT,
881		    &mnt->f_fsid) == 0) {
882			static const char slashnetwork[] = "/Network/";
883
884			if (verbose) {
885				pr_msg("%s unmounted",
886					mnt->f_mntonname);
887			}
888			count++;
889
890			/*
891			 * If the path to this was under /Network,
892			 * try to remove the directory it was
893			 * mounted on, to keep /Network clean.
894			 *
895			 * (The system "owns" /Network, so we can get rid
896			 * of stuff as we choose.  For other trigger mount
897			 * points, we don't know whether we created the
898			 * mount point, so we don't know whether we should
899			 * remove it.)
900			 */
901			if (strncmp(mnt->f_mntonname, slashnetwork,
902			    sizeof slashnetwork - 1) == 0)
903				rmdir_r(mnt->f_mntonname);
904		}
905	}
906	if (verbose && count == 0)
907		pr_msg("no unmounts");
908}
909
910/*
911 * Check whether two entries refer to the same directory; we compare
912 * both the path name and the realpath()ed path name.
913 */
914static int
915paths_match(struct autodir *d1, struct autodir *d2)
916{
917	if (strcmp(d1->dir_name, d2->dir_name) == 0)
918		return (1);
919	if (d1->dir_realpath != NULL) {
920		if (strcmp(d1->dir_realpath, d2->dir_name) == 0)
921			return (1);
922		if (d2->dir_realpath != NULL) {
923			if (strcmp(d1->dir_realpath, d2->dir_realpath) == 0)
924				return (1);
925		}
926	}
927	if (d2->dir_realpath != NULL) {
928		if (strcmp(d1->dir_name, d2->dir_realpath) == 0)
929			return (1);
930	}
931	return (0);
932}
933
934static int
935mkdir_r(dir)
936	char *dir;
937{
938	int err;
939	char *slash;
940
941	if (mkdir(dir, 0555) == 0) {
942		/*
943		 * We created the directory.
944		 */
945		return (0);
946	}
947	if (errno == EEXIST) {
948		/*
949		 * Something already existed there; we'll assume it's
950		 * a directory.  (If it's not, something will fail later.)
951		 */
952		return (0);
953	}
954	if (errno != ENOENT) {
955		/*
956		 * We failed to create it for some reason other than
957		 * the absence of a directory in the path leading up to
958		 * it.  Give up.
959		 */
960		return (-1);
961	}
962
963	/*
964	 * Create the parent directory (creating any directories leading
965	 * up to it).
966	 */
967	slash = strrchr(dir, '/');
968	if (slash == NULL)
969		return (-1);
970	*slash = '\0';
971	err = mkdir_r(dir);
972	*slash++ = '/';
973	if (err || !*slash)
974		return (err);
975	return (mkdir(dir, 0555));
976}
977
978/*
979 * Inverse of mkdir_r() - removes a directory, and then removes all
980 * parent directories that it can, except for the one right under the
981 * root directory, stopping when it can't remove one.
982 * Modifies the path argument as it goes.
983 */
984void
985rmdir_r(char *path)
986{
987	char *p;
988
989	for (;;) {
990		/*
991		 * Look for the separator between us and the parent
992		 * directory.
993		 */
994		p = strrchr(path, '/');
995		if (p == NULL) {
996			/*
997			 * Not found; this is not an absolute path, so
998			 * just give up.
999			 */
1000			break;
1001		}
1002		if (p == path) {
1003			/*
1004			 * Our parent is the root directory, so we
1005			 * shouldn't be removed (we want /Network to
1006			 * stick around).
1007			 */
1008			break;
1009		}
1010		if (rmdir(path) == -1)
1011			break;	/* failed */
1012
1013		/*
1014		 * Cut off our name, leaving the name of the parent
1015		 * directory.
1016		 */
1017		*p = '\0';
1018	}
1019}
1020
1021/*
1022 * Check whether there are any Active Directory entries in the
1023 * Directory Services search path.
1024 */
1025static int
1026have_ad(void)
1027{
1028	int have_it = 0;
1029	CFErrorRef error;
1030	char *errstring;
1031	ODNodeRef node_ref;
1032	CFArrayRef paths;
1033	CFIndex num_paths;
1034	CFIndex i;
1035	CFStringRef path;
1036
1037	/*
1038	 * Create the search node.
1039	 */
1040	error = NULL;
1041	node_ref = ODNodeCreateWithNodeType(kCFAllocatorDefault, kODSessionDefault,
1042		kODNodeTypeAuthentication, &error);
1043	if (node_ref == NULL) {
1044		errstring = od_get_error_string(error);
1045		pr_msg("have_ad: can't create search node for /Search: %s",
1046		    errstring);
1047		free(errstring);
1048		return (0);
1049	}
1050
1051	/*
1052	 * Get the search paths from the node.
1053	 */
1054	paths = ODNodeCopySubnodeNames(node_ref, &error);
1055	if (paths == NULL) {
1056		errstring = od_get_error_string(error);
1057		pr_msg("have_ad: can't get subnode names for /Search: %s",
1058		    errstring);
1059		free(errstring);
1060		return (0);
1061	}
1062
1063	/*
1064	 * Scan the paths in that array looking for an Active
1065	 * Directory search path entry, i.e. one beginning with
1066	 * "/Active Directory".
1067	 */
1068	num_paths = CFArrayGetCount(paths);
1069	for (i = 0; i < num_paths && !have_it; i++) {
1070		path = CFArrayGetValueAtIndex(paths, i);
1071
1072		/*
1073		 * Check whether this entry begins with "/Active Directory".
1074		 */
1075		have_it = CFStringHasPrefix(path, CFSTR("/Active Directory"));
1076	}
1077	CFRelease(paths);
1078	CFRelease(node_ref);
1079	return (have_it);
1080}
1081
1082/*
1083 * Print an error.
1084 * It works like printf
1085 * (fmt string and variable args) except that it will prepend "automount:"
1086 * and substitute an error message for a "%m" string (like syslog).
1087 */
1088/* VARARGS1 */
1089void
1090pr_msg(const char *fmt, ...)
1091{
1092	va_list ap;
1093	char buf[BUFSIZ], *p2;
1094	const char *p1;
1095
1096	if (!isatty(fileno(stderr))) {
1097		va_start(ap, fmt);
1098 		(void) vsyslog(LOG_ERR, fmt, ap);
1099		va_end(ap);
1100		return;
1101	}
1102
1103	(void) strlcpy(buf, "automount: ", sizeof buf);
1104	p2 = buf + strlen(buf);
1105
1106	for (p1 = fmt; *p1; p1++) {
1107		if (*p1 == '%' && *(p1+1) == 'm') {
1108			(void) strlcpy(p2, strerror(errno),
1109			    sizeof buf - (p2 - buf));
1110			p2 += strlen(p2);
1111			p1++;
1112		} else {
1113			*p2++ = *p1;
1114		}
1115	}
1116	if (p2 > buf && *(p2-1) != '\n')
1117		*p2++ = '\n';
1118	*p2 = '\0';
1119
1120
1121	va_start(ap, fmt);
1122 	(void) vfprintf(stderr, buf, ap);
1123	va_end(ap);
1124}
1125
1126static int
1127load_autofs(void)
1128{
1129	pid_t pid, terminated_pid;
1130	int result;
1131	union wait status;
1132
1133	pid = fork();
1134	if (pid == 0) {
1135		result = execl(gKextLoadCommand, gKextLoadCommand, "-q",
1136		    gKextLoadPath, NULL);
1137		/* IF WE ARE HERE, WE WERE UNSUCCESSFUL */
1138		return (result ? result : ECHILD);
1139	}
1140
1141	if (pid == -1)
1142		return (-1);
1143
1144	/* Success! Wait for completion in-line here */
1145	while ((terminated_pid = wait4(pid, (int *)&status, 0, NULL)) < 0) {
1146		/* retry if EINTR, else break out with error */
1147		if (errno != EINTR)
1148			break;
1149	}
1150
1151	if (terminated_pid == pid && WIFEXITED(status))
1152		result = WEXITSTATUS(status);
1153	else
1154		result = -1;
1155
1156	return (result);
1157}
1158