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