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 (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21/*
22 *	automount.c
23 *
24 * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
25 */
26
27
28#include <ctype.h>
29#include <stdio.h>
30#include <unistd.h>
31#include <stdlib.h>
32#include <locale.h>
33#include <stdarg.h>
34#include <errno.h>
35#include <string.h>
36#include <dirent.h>
37#include <signal.h>
38#include <syslog.h>
39#include <libshare.h>
40#include <libscf.h>
41#include <sys/param.h>
42#include <sys/time.h>
43#include <sys/vfs.h>
44#include <sys/types.h>
45#include <sys/stat.h>
46#include <sys/mnttab.h>
47#include <sys/mntent.h>
48#include <sys/mount.h>
49#include <sys/utsname.h>
50#include <sys/tiuser.h>
51#include <rpc/rpc.h>
52#include <rpcsvc/nfs_prot.h>
53#include <nsswitch.h>
54#include <deflt.h>
55#include <rpcsvc/daemon_utils.h>
56#include "automount.h"
57#include "smfcfg.h"
58
59static int mkdir_r(char *);
60struct autodir *dir_head;
61struct autodir *dir_tail;
62static struct extmnttab *find_mount();
63int verbose = 0;
64int trace = 0;
65
66static void usage();
67static int compare_opts(char *, char *);
68static void do_unmounts();
69
70static int mount_timeout = AUTOFS_MOUNT_TIMEOUT;
71
72static char	*service_list[] = { AUTOMOUNTD, NULL };
73
74/*
75 * XXX
76 * The following are needed because they're used in auto_subr.c and
77 * we link with it. Should avoid this.
78 */
79mutex_t cleanup_lock;
80cond_t cleanup_start_cv;
81cond_t cleanup_done_cv;
82
83int
84main(int argc, char *argv[])
85{
86	int c;
87	struct autofs_args ai;
88	struct utsname utsname;
89	char autofs_addr[MAXADDRLEN];
90	struct autodir *dir, *d;
91	struct stat stbuf;
92	char *master_map = "auto_master";
93	int null;
94	struct extmnttab mnt, *mntp;
95	struct mnttab *omntp;
96	char mntopts[MAX_MNTOPT_STR];
97	int mntflgs;
98	int count = 0;
99	char *stack[STACKSIZ];
100	char **stkptr;
101	char *defval;
102	struct sigaction sigintact;
103	int ret = 0, bufsz = 0;
104	char valbuf[6];
105
106	/*
107	 * protect this command from session termination when run in background
108	 * we test background by whether SIGINT is ignored
109	 */
110	(void) sigaction(SIGINT, NULL, &sigintact);
111	if (sigintact.sa_sigaction == SIG_IGN) {
112		(void) signal(SIGHUP, SIG_IGN);
113		(void) setsid();
114	}
115
116	/*
117	 * Read in the values from SMF first before we check
118	 * commandline options so the options override the SMF values.
119	 */
120	bufsz = 6;
121	ret = autofs_smf_get_prop("timeout", valbuf, DEFAULT_INSTANCE,
122	    SCF_TYPE_INTEGER, AUTOMOUNTD, &bufsz);
123	if (ret == SA_OK)
124		/*
125		 * Ignore errno.  In event of failure, mount_timeout is
126		 * already initialized to the correct value.
127		 */
128		mount_timeout = strtol(valbuf, (char **)NULL, 10);
129
130	bufsz = 6;
131	ret = autofs_smf_get_prop("automount_verbose", valbuf, DEFAULT_INSTANCE,
132	    SCF_TYPE_BOOLEAN, AUTOMOUNTD, &bufsz);
133	if (ret == SA_OK) {
134		if (strncasecmp("true", valbuf, 4) == 0)
135			verbose = TRUE;
136	}
137
138	put_automountd_env();
139
140	while ((c = getopt(argc, argv, "mM:D:f:t:v?")) != EOF) {
141		switch (c) {
142		case 'm':
143			pr_msg("Warning: -m option not supported");
144			break;
145		case 'M':
146			pr_msg("Warning: -M option not supported");
147			break;
148		case 'D':
149			pr_msg("Warning: -D option not supported");
150			break;
151		case 'f':
152			pr_msg("Error: -f option no longer supported");
153			usage();
154			break;
155		case 't':
156			if (strchr(optarg, '=')) {
157				pr_msg("Error: invalid value for -t");
158				usage();
159			}
160			mount_timeout = atoi(optarg);
161			break;
162		case 'v':
163			verbose++;
164			break;
165		default:
166			usage();
167			break;
168		}
169	}
170
171	if (optind < argc) {
172		pr_msg("%s: command line mountpoints/maps "
173		    "no longer supported", argv[optind]);
174		usage();
175	}
176
177	current_mounts = getmntlist();
178	if (current_mounts == NULL) {
179		pr_msg("Couldn't establish current mounts");
180		exit(1);
181	}
182
183	(void) umask(0);
184	ns_setup(stack, &stkptr);
185
186	openlog("automount", LOG_PID, LOG_DAEMON);
187	(void) loadmaster_map(master_map, "", stack, &stkptr);
188	if (dir_head != NULL) {
189		/*
190		 * automount maps found. enable services as needed.
191		 */
192		_check_services(service_list);
193	}
194
195	closelog();
196
197	if (uname(&utsname) < 0) {
198		pr_msg("uname: %m");
199		exit(1);
200	}
201	(void) strcpy(autofs_addr, utsname.nodename);
202	(void) strcat(autofs_addr, ".autofs");
203	ai.addr.buf	= autofs_addr;
204	ai.addr.len	= strlen(ai.addr.buf);
205	ai.addr.maxlen	= ai.addr.len;
206
207	ai.mount_to	= mount_timeout;
208	ai.rpc_to	= AUTOFS_RPC_TIMEOUT;
209
210	/*
211	 * Mount the daemon at its mount points.
212	 */
213	for (dir = dir_head; dir; dir = dir->dir_next) {
214
215		/*
216		 * Skip null entries
217		 */
218		if (strcmp(dir->dir_map, "-null") == 0)
219			continue;
220
221		/*
222		 * Skip null'ed entries
223		 */
224		null = 0;
225		for (d = dir->dir_prev; d; d = d->dir_prev) {
226			if (strcmp(dir->dir_name, d->dir_name) == 0)
227				null = 1;
228		}
229		if (null)
230			continue;
231
232		/*
233		 * Check whether there's already an entry
234		 * in the mnttab for this mountpoint.
235		 */
236		if (mntp = find_mount(dir->dir_name, 1)) {
237			/*
238			 * If it's not an autofs mount - don't
239			 * mount over it.
240			 */
241			if (strcmp(mntp->mnt_fstype, MNTTYPE_AUTOFS) != 0) {
242				pr_msg("%s: already mounted",
243				    mntp->mnt_mountp);
244				continue;
245			}
246
247			/*
248			 * Compare the mnttab entry with the master map
249			 * entry.  If the map or mount options are
250			 * different, then update this information
251			 * with a remount.
252			 */
253			if (strcmp(mntp->mnt_special, dir->dir_map) == 0 &&
254			    compare_opts(dir->dir_opts,
255			    mntp->mnt_mntopts) == 0) {
256				continue;	/* no change */
257			}
258
259			/*
260			 * Check for an overlaid direct autofs mount.
261			 * Cannot remount since it's inaccessible.
262			 */
263			omntp = (struct mnttab *)mntp;
264			if (hasmntopt(omntp, "direct") != NULL) {
265				mntp = find_mount(dir->dir_name, 0);
266				omntp = (struct mnttab *)mntp;
267				if (hasmntopt(omntp, "direct") == NULL) {
268					if (verbose)
269						pr_msg("%s: cannot remount",
270						    dir->dir_name);
271					continue;
272				}
273			}
274
275			dir->dir_remount = 1;
276		}
277
278		/*
279		 * Create a mount point if necessary
280		 * If the path refers to an existing symbolic
281		 * link, refuse to mount on it.  This avoids
282		 * future problems.
283		 */
284		if (lstat(dir->dir_name, &stbuf) == 0) {
285			if ((stbuf.st_mode & S_IFMT) != S_IFDIR) {
286				pr_msg("%s: Not a directory", dir->dir_name);
287				continue;
288			}
289		} else {
290			if (mkdir_r(dir->dir_name)) {
291				pr_msg("%s: %m", dir->dir_name);
292				continue;
293			}
294		}
295
296		ai.path 	= dir->dir_name;
297		ai.opts		= dir->dir_opts;
298		ai.map		= dir->dir_map;
299		ai.subdir	= "";
300		ai.direct 	= dir->dir_direct;
301		if (dir->dir_direct)
302			ai.key = dir->dir_name;
303		else
304			ai.key = "";
305
306		(void) sprintf(mntopts, "ignore,%s",
307		    dir->dir_direct  ? "direct" : "indirect");
308		if (dir->dir_opts && *dir->dir_opts) {
309			(void) strcat(mntopts, ",");
310			(void) strcat(mntopts, dir->dir_opts);
311		}
312		mntflgs = MS_OPTIONSTR | (dir->dir_remount ? MS_REMOUNT : 0);
313		if (mount(dir->dir_map, dir->dir_name, MS_DATA | mntflgs,
314		    MNTTYPE_AUTOFS, &ai, sizeof (ai), mntopts,
315		    MAX_MNTOPT_STR) < 0) {
316			pr_msg("mount %s: %m", dir->dir_name);
317			continue;
318		}
319
320		count++;
321
322		if (verbose) {
323			if (dir->dir_remount)
324				pr_msg("%s remounted", dir->dir_name);
325			else
326				pr_msg("%s mounted", dir->dir_name);
327		}
328	}
329
330	if (verbose && count == 0)
331		pr_msg("no mounts");
332
333	/*
334	 * Now compare the /etc/mnttab with the master
335	 * map.  Any autofs mounts in the /etc/mnttab
336	 * that are not in the master map must be
337	 * unmounted
338	 */
339	do_unmounts();
340
341	return (0);
342}
343
344/*
345 * Find a mount entry given
346 * the mountpoint path.
347 * Optionally return the first
348 * or last entry.
349 */
350static struct extmnttab *
351find_mount(mntpnt, first)
352	char *mntpnt;
353	int first;
354{
355	struct mntlist *mntl;
356	struct extmnttab *found = NULL;
357
358	for (mntl = current_mounts; mntl; mntl = mntl->mntl_next) {
359
360		if (strcmp(mntpnt, mntl->mntl_mnt->mnt_mountp) == 0) {
361			found = mntl->mntl_mnt;
362			if (first)
363				break;
364		}
365	}
366
367	return (found);
368}
369
370static char *ignore_opts[] = {"ignore", "direct", "indirect", "dev", NULL};
371
372/*
373 * Compare mount options
374 * ignoring "ignore", "direct", "indirect"
375 * and "dev=".
376 */
377static int
378compare_opts(opts, mntopts)
379	char *opts, *mntopts;
380{
381	char optbuf1[MAX_MNTOPT_STR], *s = optbuf1;
382	char optbuf2[MAX_MNTOPT_STR];
383	char **opttbl1, **opttbl2;
384	int nopts1, nopts2;
385	char *ostart, *optr, *valp;
386	int j, i, notsame;
387
388	opttbl1 = opttbl2 = NULL;
389	/*
390	 * Parse the two option strings to split them both into
391	 * lists of individual options.
392	 */
393	if (mntopts != NULL)
394		(void) strcpy(s, mntopts);
395	else
396		*s = '\0';
397	if (*s != '\0')
398		nopts1 = 1;
399	else
400		nopts1 = 0;
401	for (s = strchr(s, ','); s != NULL; s = strchr(s, ',')) {
402		nopts1++;
403		s++;
404	}
405	if (nopts1)
406		if ((opttbl1 = memalign(sizeof (char *),
407			nopts1 * sizeof (char *))) == NULL)
408			return (1);
409	nopts1 = 0;
410	s = optbuf1;
411	for (ostart = optr = s; *optr != '\0'; ostart = optr) {
412		if (getsubopt(&optr, ignore_opts, &valp) == -1) {
413			opttbl1[nopts1++] = ostart;
414		}
415	}
416	s = optbuf2;
417	if (opts != NULL)
418		(void) strcpy(s, opts);
419	else
420		*s = '\0';
421	if (*s != '\0')
422		nopts2 = 1;
423	else
424		nopts2 = 0;
425	for (s = strchr(s, ','); s != NULL; s = strchr(s, ',')) {
426		nopts2++;
427		s++;
428	}
429	if (nopts2)
430		if ((opttbl2 = memalign(sizeof (char *),
431			nopts2 * sizeof (char *))) == NULL) {
432			notsame = 1;
433			goto done;
434		}
435	nopts2 = 0;
436	s = optbuf2;
437	for (ostart = optr = s; *optr != '\0'; ostart = optr) {
438		if (getsubopt(&optr, ignore_opts, &valp) == -1) {
439			opttbl2[nopts2++] = ostart;
440		}
441	}
442	if (nopts2 != nopts1) {
443		notsame = 1;
444		goto done;
445	}
446	notsame = 0;
447	for (i = 0; i < nopts1; i++) {
448		notsame = 1;
449		for (j = 0; j < nopts2; j++) {
450			if (strcmp(opttbl1[i], opttbl2[j]) == 0) {
451				notsame = 0;
452				break;
453			}
454		}
455		if (notsame)
456			break;
457	}
458
459done:
460	if (opttbl1 != NULL)
461		free(opttbl1);
462	if (opttbl2 != NULL)
463		free(opttbl2);
464	return (notsame);
465}
466
467static void
468usage()
469{
470	pr_msg("Usage: automount  [ -v ]  [ -t duration ]");
471	exit(1);
472	/* NOTREACHED */
473}
474
475/*
476 * Unmount any autofs mounts that
477 * aren't in the master map
478 */
479static void
480do_unmounts()
481{
482	struct mntlist *mntl;
483	struct extmnttab *mnt;
484	struct mnttab *omnt;
485	struct autodir *dir;
486	int current;
487	int count = 0;
488	struct zone_summary *zsp;
489
490	zsp = fs_get_zone_summaries();
491	if (zsp == NULL) {
492		pr_msg("Couldn't establish active zones");
493		exit(1);
494	}
495	for (mntl = current_mounts; mntl; mntl = mntl->mntl_next) {
496		mnt = mntl->mntl_mnt;
497		omnt = (struct mnttab *)mnt;
498		if (strcmp(mnt->mnt_fstype, MNTTYPE_AUTOFS) != 0)
499			continue;
500		if (fs_mount_in_other_zone(zsp, mnt->mnt_mountp))
501			continue;
502		/*
503		 * Don't unmount autofs mounts done
504		 * from the autofs mount command.
505		 * How do we tell them apart ?
506		 * Autofs mounts not eligible for auto-unmount
507		 * have the "nest" pseudo-option.
508		 */
509		if (hasmntopt(omnt, "nest") != NULL)
510			continue;
511
512		current = 0;
513		for (dir = dir_head; dir; dir = dir->dir_next) {
514			if (strcmp(dir->dir_name, mnt->mnt_mountp) == 0) {
515				current = strcmp(dir->dir_map, "-null");
516				break;
517			}
518		}
519		if (current)
520			continue;
521
522
523		if (umount(mnt->mnt_mountp) == 0) {
524			if (verbose) {
525				pr_msg("%s unmounted",
526				    mnt->mnt_mountp);
527			}
528			count++;
529		}
530	}
531	if (verbose && count == 0)
532		pr_msg("no unmounts");
533}
534
535static int
536mkdir_r(dir)
537	char *dir;
538{
539	int err;
540	char *slash;
541
542	if (mkdir(dir, 0555) == 0 || errno == EEXIST)
543		return (0);
544	if (errno != ENOENT)
545		return (-1);
546	slash = strrchr(dir, '/');
547	if (slash == NULL)
548		return (-1);
549	*slash = '\0';
550	err = mkdir_r(dir);
551	*slash++ = '/';
552	if (err || !*slash)
553		return (err);
554	return (mkdir(dir, 0555));
555}
556
557/*
558 * Print an error.
559 * Works like printf (fmt string and variable args)
560 * except that it will subsititute an error message
561 * for a "%m" string (like syslog).
562 */
563/* VARARGS1 */
564void
565pr_msg(const char *fmt, ...)
566{
567	va_list ap;
568	char buf[BUFSIZ], *p2;
569	char *p1;
570	char *nfmt;
571
572	(void) strcpy(buf, "automount: ");
573	p2 = buf + strlen(buf);
574
575	nfmt = gettext(fmt);
576
577	for (p1 = nfmt; *p1; p1++) {
578		if (*p1 == '%' && *(p1+1) == 'm') {
579			(void) strcpy(p2, strerror(errno));
580			p2 += strlen(p2);
581			p1++;
582		} else {
583			*p2++ = *p1;
584		}
585	}
586	if (p2 > buf && *(p2-1) != '\n')
587		*p2++ = '\n';
588	*p2 = '\0';
589
590	va_start(ap, fmt);
591	(void) vfprintf(stderr, buf, ap);
592	va_end(ap);
593}
594