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