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