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