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 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_handler == 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 * 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 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 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 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 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 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