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