xref: /illumos-gate/usr/src/cmd/fs.d/autofs/automount.c (revision 6a634c9d)
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 
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 int
main(int argc,char * argv[])84 main(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  */
350 static struct extmnttab *
find_mount(mntpnt,first)351 find_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 
370 static char *ignore_opts[] = {"ignore", "direct", "indirect", "dev", NULL};
371 
372 /*
373  * Compare mount options
374  * ignoring "ignore", "direct", "indirect"
375  * and "dev=".
376  */
377 static int
compare_opts(opts,mntopts)378 compare_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 
459 done:
460 	if (opttbl1 != NULL)
461 		free(opttbl1);
462 	if (opttbl2 != NULL)
463 		free(opttbl2);
464 	return (notsame);
465 }
466 
467 static void
usage()468 usage()
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  */
479 static void
do_unmounts()480 do_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 
535 static int
mkdir_r(dir)536 mkdir_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 */
564 void
pr_msg(const char * fmt,...)565 pr_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