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 * Copyright 2007 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 #pragma ident "%Z%%M% %I% %E% SMI" 27 28 #include <ctype.h> 29 #include <stdio.h> 30 #include <stdlib.h> 31 #include <unistd.h> 32 #include <locale.h> 33 #include <syslog.h> 34 #include <errno.h> 35 #include <string.h> 36 #include <stdarg.h> 37 #include <dirent.h> 38 #include <thread.h> 39 #include <sys/param.h> 40 #include <sys/time.h> 41 #include <sys/vfs.h> 42 #include <sys/types.h> 43 #include <sys/stat.h> 44 #include <sys/mnttab.h> 45 #include <sys/mntent.h> 46 #include <sys/mount.h> 47 #include <sys/signal.h> 48 #include <sys/utsname.h> 49 #include <sys/systeminfo.h> 50 #include <sys/tiuser.h> 51 #include <sys/utsname.h> 52 #include <rpc/rpc.h> 53 #include <rpcsvc/nfs_prot.h> 54 #include <assert.h> 55 #include "automount.h" 56 #include <deflt.h> 57 #include <zone.h> 58 #include <priv.h> 59 #include <fcntl.h> 60 61 static char *check_hier(char *); 62 static int arch(char *, size_t, bool_t); 63 static int cpu(char *, size_t); 64 static int natisa(char *, size_t); 65 static int platform(char *, size_t); 66 67 struct mntlist *current_mounts; 68 69 static bool_t nodirect_map = FALSE; 70 71 /* 72 * If the system is labeled then we need to 73 * have a uniquely-named auto_home map for each zone. 74 * The maps are made unique by appending the zonename. 75 * The home directory is made unique by prepending /zone/<zonename> 76 * for each zone that is dominated by the current zone. 77 * The current zone's home directory mount point is not changed. 78 * 79 * For each auto_home_<zonename> a default template map is created 80 * only if it doesn't exist yet. The default entry is used to declare 81 * local home directories created within each zone. For example: 82 * 83 * +auto_home_public 84 * * -fstype=lofs :/zone/public/export/home/& 85 */ 86 static void 87 loadzone_maps(char *mntpnt, char *map, char *opts, char **stack, char ***stkptr) 88 { 89 zoneid_t *zids = NULL; 90 zoneid_t my_zoneid; 91 uint_t nzents_saved; 92 uint_t nzents; 93 int i; 94 95 if (!priv_ineffect(PRIV_SYS_MOUNT)) 96 return; 97 98 if (zone_list(NULL, &nzents) != 0) { 99 return; 100 } 101 my_zoneid = getzoneid(); 102 again: 103 if (nzents == 0) 104 return; 105 106 zids = malloc(nzents * sizeof (zoneid_t)); 107 nzents_saved = nzents; 108 109 if (zone_list(zids, &nzents) != 0) { 110 free(zids); 111 return; 112 } 113 if (nzents != nzents_saved) { 114 /* list changed, try again */ 115 free(zids); 116 goto again; 117 } 118 119 for (i = 0; i < nzents; i++) { 120 char zonename[ZONENAME_MAX]; 121 char zoneroot[MAXPATHLEN]; 122 123 if (getzonenamebyid(zids[i], zonename, ZONENAME_MAX) != -1) { 124 char appended_map[MAXPATHLEN]; 125 char prepended_mntpnt[MAXPATHLEN]; 126 char map_path[MAXPATHLEN]; 127 int fd; 128 129 (void) snprintf(appended_map, sizeof (appended_map), 130 "%s_%s", map, zonename); 131 132 /* for current zone, leave mntpnt alone */ 133 if (zids[i] != my_zoneid) { 134 (void) snprintf(prepended_mntpnt, 135 sizeof (prepended_mntpnt), 136 "/zone/%s%s", zonename, mntpnt); 137 if (zone_getattr(zids[i], ZONE_ATTR_ROOT, 138 zoneroot, sizeof (zoneroot)) == -1) 139 continue; 140 } else { 141 (void) strcpy(prepended_mntpnt, mntpnt); 142 zoneroot[0] = '\0'; 143 } 144 145 dirinit(prepended_mntpnt, appended_map, opts, 0, stack, 146 stkptr); 147 /* 148 * Next create auto_home_<zone> maps for each zone 149 */ 150 151 (void) snprintf(map_path, sizeof (map_path), 152 "/etc/%s", appended_map); 153 /* 154 * If the map file doesn't exist create a template 155 */ 156 if ((fd = open(map_path, O_RDWR | O_CREAT | O_EXCL, 157 S_IRUSR | S_IWUSR | S_IRGRP| S_IROTH)) != -1) { 158 int len; 159 char map_rec[MAXPATHLEN]; 160 161 len = snprintf(map_rec, sizeof (map_rec), 162 "+%s\n*\t-fstype=lofs\t:%s/export/home/&\n", 163 appended_map, zoneroot); 164 if (len <= sizeof (map_rec)) 165 (void) write(fd, map_rec, len); 166 (void) close(fd); 167 } 168 } 169 } 170 free(zids); 171 } 172 173 void 174 dirinit(char *mntpnt, char *map, char *opts, int direct, char **stack, 175 char ***stkptr) 176 { 177 struct autodir *dir; 178 char *p; 179 180 if (strcmp(map, "-null") == 0) { 181 if (strcmp(mntpnt, "/-") == 0) 182 nodirect_map = TRUE; 183 goto enter; 184 } 185 186 p = mntpnt + (strlen(mntpnt) - 1); 187 if (*p == '/') 188 *p = '\0'; /* trim trailing / */ 189 if (*mntpnt != '/') { 190 pr_msg("dir %s must start with '/'", mntpnt); 191 return; 192 } 193 if (p = check_hier(mntpnt)) { 194 pr_msg("hierarchical mountpoint: %s and %s", 195 p, mntpnt); 196 return; 197 } 198 199 /* 200 * If it's a direct map then call dirinit 201 * for every map entry. 202 */ 203 if ((strcmp(mntpnt, "/-") == 0) && !(nodirect_map)) { 204 (void) loaddirect_map(map, map, opts, stack, stkptr); 205 return; 206 } 207 208 /* 209 * Home directories are polyinstantiated on 210 * labeled systems. 211 */ 212 if (is_system_labeled() && 213 (strcmp(mntpnt, "/home") == 0) && 214 (strcmp(map, "auto_home") == 0)) { 215 (void) loadzone_maps(mntpnt, map, opts, stack, stkptr); 216 return; 217 } 218 enter: 219 dir = (struct autodir *)malloc(sizeof (*dir)); 220 if (dir == NULL) 221 goto alloc_failed; 222 dir->dir_name = strdup(mntpnt); 223 if (dir->dir_name == NULL) 224 goto alloc_failed; 225 dir->dir_map = strdup(map); 226 if (dir->dir_map == NULL) 227 goto alloc_failed; 228 dir->dir_opts = strdup(opts); 229 if (dir->dir_opts == NULL) 230 goto alloc_failed; 231 dir->dir_direct = direct; 232 dir->dir_remount = 0; 233 dir->dir_next = NULL; 234 235 /* 236 * Append to dir chain 237 */ 238 if (dir_head == NULL) 239 dir_head = dir; 240 else 241 dir_tail->dir_next = dir; 242 243 dir->dir_prev = dir_tail; 244 dir_tail = dir; 245 246 return; 247 248 alloc_failed: 249 if (dir != NULL) { 250 if (dir->dir_opts) 251 free(dir->dir_opts); 252 if (dir->dir_map) 253 free(dir->dir_map); 254 if (dir->dir_name) 255 free(dir->dir_name); 256 free(dir); 257 } 258 pr_msg("dirinit: memory allocation failed"); 259 } 260 261 /* 262 * Check whether the mount point is a 263 * subdirectory or a parent directory 264 * of any previously mounted automount 265 * mount point. 266 */ 267 static char * 268 check_hier(mntpnt) 269 char *mntpnt; 270 { 271 register struct autodir *dir; 272 register char *p, *q; 273 274 for (dir = dir_head; dir; dir = dir->dir_next) { 275 p = dir->dir_name; 276 q = mntpnt; 277 for (; *p == *q; p++, q++) 278 if (*p == '\0') 279 break; 280 if (*p == '/' && *q == '\0') 281 return (dir->dir_name); 282 if (*p == '\0' && *q == '/') 283 return (dir->dir_name); 284 if (*p == '\0' && *q == '\0') 285 return (NULL); 286 } 287 return (NULL); /* it's not a subdir or parent */ 288 } 289 290 /* 291 * Gets the next token from the string "p" and copies it into "w". The "wq" is 292 * a quote vector for "w" and is derived from "pq", which is a quote vector for 293 * "p". Delim is the character to be used as a delimiter for the scan. A space 294 * means "whitespace". The call to getword must provide buffers w and wq of size 295 * at least wordsz. getword() will pass strings of maximum length (wordsz-1), 296 * since it needs to null terminate the string. 297 * Returns 0 on ok and -1 on error. 298 */ 299 int 300 getword(char *w, char *wq, char **p, char **pq, char delim, int wordsz) 301 { 302 char *tmp = w; 303 char *tmpq = wq; 304 int count = wordsz; 305 306 if (wordsz <= 0) { 307 if (verbose) 308 syslog(LOG_ERR, 309 "getword: input word size %d must be > 0", wordsz); 310 return (-1); 311 } 312 313 while ((delim == ' ' ? isspace(**p) : **p == delim) && **pq == ' ') 314 (*p)++, (*pq)++; 315 316 while (**p && 317 !((delim == ' ' ? isspace(**p) : **p == delim) && 318 **pq == ' ')) { 319 if (--count <= 0) { 320 *tmp = '\0'; 321 *tmpq = '\0'; 322 syslog(LOG_ERR, 323 "maximum word length (%d) exceeded", wordsz); 324 return (-1); 325 } 326 *w++ = *(*p)++; 327 *wq++ = *(*pq)++; 328 } 329 *w = '\0'; 330 *wq = '\0'; 331 332 return (0); 333 } 334 335 /* 336 * get_line attempts to get a line from the map, upto LINESZ. A line in 337 * the map is a concatenation of lines if the continuation symbol '\' 338 * is used at the end of the line. Returns line on success, a NULL on 339 * EOF, and an empty string on lines > linesz. 340 */ 341 char * 342 get_line(FILE *fp, char *map, char *line, int linesz) 343 { 344 register char *p = line; 345 register int len; 346 int excess = 0; 347 348 *p = '\0'; 349 350 for (;;) { 351 if (fgets(p, linesz - (p-line), fp) == NULL) { 352 return (*line ? line : NULL); /* EOF */ 353 } 354 355 len = strlen(line); 356 if (len <= 0) { 357 p = line; 358 continue; 359 } 360 p = &line[len - 1]; 361 362 /* 363 * Is input line too long? 364 */ 365 if (*p != '\n') { 366 excess = 1; 367 /* 368 * Perhaps last char read was '\'. Reinsert it 369 * into the stream to ease the parsing when we 370 * read the rest of the line to discard. 371 */ 372 (void) ungetc(*p, fp); 373 break; 374 } 375 trim: 376 /* trim trailing white space */ 377 while (p >= line && isspace(*(uchar_t *)p)) 378 *p-- = '\0'; 379 if (p < line) { /* empty line */ 380 p = line; 381 continue; 382 } 383 384 if (*p == '\\') { /* continuation */ 385 *p = '\0'; 386 continue; 387 } 388 389 /* 390 * Ignore comments. Comments start with '#' 391 * which must be preceded by a whitespace, unless 392 * if '#' is the first character in the line. 393 */ 394 p = line; 395 while (p = strchr(p, '#')) { 396 if (p == line || isspace(*(p-1))) { 397 *p-- = '\0'; 398 goto trim; 399 } 400 p++; 401 } 402 break; 403 } 404 if (excess) { 405 int c; 406 407 /* 408 * discard rest of line and return an empty string. 409 * done to set the stream to the correct place when 410 * we are done with this line. 411 */ 412 while ((c = getc(fp)) != EOF) { 413 *p = c; 414 if (*p == '\n') /* end of the long line */ 415 break; 416 else if (*p == '\\') { /* continuation */ 417 if (getc(fp) == EOF) /* ignore next char */ 418 break; 419 } 420 } 421 syslog(LOG_ERR, 422 "map %s: line too long (max %d chars)", 423 map, linesz-1); 424 *line = '\0'; 425 } 426 427 return (line); 428 } 429 430 /* 431 * Gets the retry=n entry from opts. 432 * Returns 0 if retry=n is not present in option string, 433 * retry=n is invalid, or when option string is NULL. 434 */ 435 int 436 get_retry(char *opts) 437 { 438 int retry = 0; 439 char buf[MAXOPTSLEN]; 440 char *p, *pb, *lasts; 441 442 if (opts == NULL) 443 return (retry); 444 445 (void) strcpy(buf, opts); 446 pb = buf; 447 while (p = (char *)strtok_r(pb, ",", &lasts)) { 448 pb = NULL; 449 if (strncmp(p, "retry=", 6) == 0) 450 retry = atoi(p+6); 451 } 452 return (retry > 0 ? retry : 0); 453 } 454 455 /* 456 * Returns zero if "opt" is found in mnt->mnt_opts, setting 457 * *sval to whatever follows the equal sign after "opt". 458 * str_opt allocates a string long enough to store the value of 459 * "opt" plus a terminating null character and returns it as *sval. 460 * It is the responsability of the caller to deallocate *sval. 461 * *sval will be equal to NULL upon return if either "opt=" is not found, 462 * or "opt=" has no value associated with it. 463 * 464 * stropt will return -1 on error. 465 */ 466 int 467 str_opt(struct mnttab *mnt, char *opt, char **sval) 468 { 469 char *str, *comma; 470 471 /* 472 * is "opt" in the options field? 473 */ 474 if (str = hasmntopt(mnt, opt)) { 475 str += strlen(opt); 476 if (*str++ != '=' || 477 (*str == ',' || *str == '\0')) { 478 syslog(LOG_ERR, "Bad option field"); 479 return (-1); 480 } 481 comma = strchr(str, ','); 482 if (comma != NULL) 483 *comma = '\0'; 484 *sval = strdup(str); 485 if (comma != NULL) 486 *comma = ','; 487 if (*sval == NULL) 488 return (-1); 489 } else 490 *sval = NULL; 491 492 return (0); 493 } 494 495 /* 496 * Performs text expansions in the string "pline". 497 * "plineq" is the quote vector for "pline". 498 * An identifier prefixed by "$" is replaced by the 499 * corresponding environment variable string. A "&" 500 * is replaced by the key string for the map entry. 501 * 502 * This routine will return an error (non-zero) if *size* would be 503 * exceeded after expansion, indicating that the macro_expand failed. 504 * This is to prevent writing past the end of pline and plineq. 505 * Both pline and plineq are left untouched in such error case. 506 */ 507 int 508 macro_expand(key, pline, plineq, size) 509 char *key, *pline, *plineq; 510 int size; 511 { 512 register char *p, *q; 513 register char *bp, *bq; 514 register char *s; 515 char buffp[LINESZ], buffq[LINESZ]; 516 char namebuf[64], *pn; 517 int expand = 0; 518 struct utsname name; 519 char procbuf[SYS_NMLN]; 520 char isaname[64]; 521 522 p = pline; q = plineq; 523 bp = buffp; bq = buffq; 524 525 while (*p) { 526 if (*p == '&' && *q == ' ') { /* insert key */ 527 /* 528 * make sure we don't overflow buffer 529 */ 530 if ((int)((bp - buffp) + strlen(key)) < size) { 531 for (s = key; *s; s++) { 532 *bp++ = *s; 533 *bq++ = ' '; 534 } 535 expand++; 536 p++; q++; 537 continue; 538 } else { 539 /* 540 * line too long... 541 */ 542 return (1); 543 } 544 } 545 546 if (*p == '$' && *q == ' ') { /* insert env var */ 547 p++; q++; 548 pn = namebuf; 549 if (*p == '{') { 550 p++; q++; 551 while (*p && *p != '}') { 552 *pn++ = *p++; 553 q++; 554 } 555 if (*p) { 556 p++; q++; 557 } 558 } else { 559 while (*p && (*p == '_' || isalnum(*p))) { 560 *pn++ = *p++; 561 q++; 562 } 563 } 564 *pn = '\0'; 565 566 s = getenv(namebuf); 567 if (!s) { 568 /* not found in env */ 569 if (strcmp(namebuf, "ARCH") == 0) { 570 if (arch(procbuf, sizeof (procbuf), 571 FALSE)) 572 s = procbuf; 573 } else if (strcmp(namebuf, "CPU") == 0) { 574 if (cpu(procbuf, sizeof (procbuf))) 575 s = procbuf; 576 } else if (strcmp(namebuf, "HOST") == 0) { 577 (void) uname(&name); 578 s = name.nodename; 579 } else if (strcmp(namebuf, "KARCH") == 0) { 580 if (arch(procbuf, sizeof (procbuf), 581 TRUE)) 582 s = procbuf; 583 } else if (strcmp(namebuf, "OSREL") == 0) { 584 (void) uname(&name); 585 s = name.release; 586 } else if (strcmp(namebuf, "OSNAME") == 0) { 587 (void) uname(&name); 588 s = name.sysname; 589 } else if (strcmp(namebuf, "OSVERS") == 0) { 590 (void) uname(&name); 591 s = name.version; 592 } else if (strcmp(namebuf, "NATISA") == 0) { 593 if (natisa(isaname, sizeof (isaname))) 594 s = isaname; 595 } else if (strcmp(namebuf, "PLATFORM") == 0) { 596 if (platform(procbuf, sizeof (procbuf))) 597 s = procbuf; 598 } 599 } 600 601 if (s) { 602 if ((int)((bp - buffp) + strlen(s)) < size) { 603 while (*s) { 604 *bp++ = *s++; 605 *bq++ = ' '; 606 } 607 } else { 608 /* 609 * line too long... 610 */ 611 return (1); 612 } 613 } 614 expand++; 615 continue; 616 } 617 /* 618 * Since buffp needs to be null terminated, we need to 619 * check that there's still room in the buffer to 620 * place at least two more characters, *p and the 621 * terminating null. 622 */ 623 if (bp - buffp == size - 1) { 624 /* 625 * There was not enough room for at least two more 626 * characters, return with an error. 627 */ 628 return (1); 629 } 630 /* 631 * The total number of characters so far better be less 632 * than the size of buffer passed in. 633 */ 634 *bp++ = *p++; 635 *bq++ = *q++; 636 637 } 638 if (!expand) 639 return (0); 640 *bp = '\0'; 641 *bq = '\0'; 642 /* 643 * We know buffp/buffq will fit in pline/plineq since we 644 * processed at most size characters. 645 */ 646 (void) strcpy(pline, buffp); 647 (void) strcpy(plineq, buffq); 648 649 return (0); 650 } 651 652 /* 653 * Removes backslashes, quotes and brackets from the string "str" 654 * and returns the quoting information in "qbuf". Character is 655 * considered escaped when it is 656 * 657 * preceded with '\' e.g. \a 658 * within quotes e.g. "string" 659 * a ':' in brackets e.g. [an:ip:6::ad::d:re:s:s] 660 * 661 * original str: 'the "brown" f\ox said: [fe80::20a:e4ff:fe35:8b0d]' 662 * unquoted str: 'the brown fox said: [fe80::20a:e4ff:fe35:8b0d]' 663 * and the qbuf: ' ^^^^^ ^ ^^ ^ ^ ^ ' 664 */ 665 void 666 unquote(str, qbuf) 667 char *str, *qbuf; 668 { 669 register int escaped, inquote, inbracket, quoted; 670 register char *ip, *bp, *qp; 671 char buf[LINESZ]; 672 673 escaped = inquote = inbracket = quoted = 0; 674 675 for (ip = str, bp = buf, qp = qbuf; *ip; ip++) { 676 if (!escaped) { 677 if (*ip == '\\') { 678 escaped = 1; 679 quoted++; 680 continue; 681 } else 682 if (*ip == '"') { 683 inquote = !inquote; 684 quoted++; 685 continue; 686 } else 687 if (*ip == '[') { 688 inbracket++; 689 quoted++; 690 } else 691 if (*ip == ']') { 692 if (inbracket > 0) inbracket--; 693 } 694 } 695 696 *bp++ = *ip; 697 *qp++ = (inquote || escaped) ? '^' 698 : ((inbracket && (*ip == ':')) ? '^' : ' '); 699 escaped = 0; 700 } 701 702 *bp = '\0'; 703 *qp = '\0'; 704 705 if (quoted) 706 (void) strcpy(str, buf); 707 } 708 709 /* 710 * If str is enclosed in [brackets], trim them off. 711 */ 712 void 713 unbracket(s) 714 char **s; 715 { 716 char *b = *s + strlen(*s) - 1; 717 718 if (*b == ']') 719 *b = '\0'; 720 if (**s == '[') 721 (*s)++; 722 } 723 724 /* 725 * Removes trailing spaces from string "s". 726 */ 727 void 728 trim(s) 729 char *s; 730 { 731 char *p = &s[strlen(s) - 1]; 732 733 while (p >= s && isspace(*(uchar_t *)p)) 734 *p-- = '\0'; 735 } 736 737 /* 738 * try to allocate memory using malloc, if malloc fails, then flush the 739 * rddir caches, and retry. If the second allocation after the readdir 740 * caches have been flushed fails too, then return NULL to indicate 741 * memory could not be allocated. 742 */ 743 char * 744 auto_rddir_malloc(unsigned nbytes) 745 { 746 char *p; 747 int again = 0; 748 749 if ((p = malloc(nbytes)) == NULL) { 750 /* 751 * No memory, free rddir caches and try again 752 */ 753 mutex_lock(&cleanup_lock); 754 cond_signal(&cleanup_start_cv); 755 if (cond_wait(&cleanup_done_cv, &cleanup_lock)) { 756 mutex_unlock(&cleanup_lock); 757 syslog(LOG_ERR, "auto_rddir_malloc interrupted\n"); 758 } else { 759 mutex_unlock(&cleanup_lock); 760 again = 1; 761 } 762 } 763 764 if (again) 765 p = malloc(nbytes); 766 767 return (p); 768 } 769 770 /* 771 * try to strdup a string, if it fails, then flush the rddir caches, 772 * and retry. If the second strdup fails, return NULL to indicate failure. 773 */ 774 char * 775 auto_rddir_strdup(const char *s1) 776 { 777 char *s2; 778 int again = 0; 779 780 if ((s2 = strdup(s1)) == NULL) { 781 /* 782 * No memory, free rddir caches and try again 783 */ 784 mutex_lock(&cleanup_lock); 785 cond_signal(&cleanup_start_cv); 786 if (cond_wait(&cleanup_done_cv, &cleanup_lock)) { 787 mutex_unlock(&cleanup_lock); 788 syslog(LOG_ERR, "auto_rddir_strdup interrupted\n"); 789 } else { 790 mutex_unlock(&cleanup_lock); 791 again = 1; 792 } 793 } 794 795 if (again) 796 s2 = strdup(s1); 797 798 return (s2); 799 } 800 801 /* 802 * Returns a pointer to the entry corresponding to 'name' if found, 803 * otherwise it returns NULL. 804 */ 805 struct dir_entry * 806 btree_lookup(struct dir_entry *head, char *name) 807 { 808 register struct dir_entry *p; 809 register int direction; 810 811 for (p = head; p != NULL; ) { 812 direction = strcmp(name, p->name); 813 if (direction == 0) 814 return (p); 815 if (direction > 0) 816 p = p->right; 817 else p = p->left; 818 } 819 return (NULL); 820 } 821 822 /* 823 * Add entry to binary tree 824 * Duplicate entries are not added 825 */ 826 void 827 btree_enter(struct dir_entry **head, struct dir_entry *ent) 828 { 829 register struct dir_entry *p, *prev = NULL; 830 register int direction; 831 832 ent->right = ent->left = NULL; 833 if (*head == NULL) { 834 *head = ent; 835 return; 836 } 837 838 for (p = *head; p != NULL; ) { 839 prev = p; 840 direction = strcmp(ent->name, p->name); 841 if (direction == 0) { 842 /* 843 * entry already in btree 844 */ 845 return; 846 } 847 if (direction > 0) 848 p = p->right; 849 else p = p->left; 850 } 851 assert(prev != NULL); 852 if (direction > 0) 853 prev->right = ent; 854 else prev->left = ent; 855 } 856 857 /* 858 * If entry doesn't exist already, add it to the linear list 859 * after '*last' and to the binary tree list. 860 * If '*last == NULL' then the list is walked till the end. 861 * *last is always set to the new element after successful completion. 862 * if entry already exists '*last' is only updated if not previously 863 * provided. 864 */ 865 int 866 add_dir_entry(char *name, struct dir_entry **list, struct dir_entry **last) 867 { 868 struct dir_entry *e, *l; 869 870 if ((*list != NULL) && (*last == NULL)) { 871 /* 872 * walk the list to find last element 873 */ 874 for (l = *list; l != NULL; l = l->next) 875 *last = l; 876 } 877 878 if (btree_lookup(*list, name) == NULL) { 879 /* 880 * not a duplicate, add it to list 881 */ 882 /* LINTED pointer alignment */ 883 e = (struct dir_entry *) 884 auto_rddir_malloc(sizeof (struct dir_entry)); 885 if (e == NULL) 886 return (ENOMEM); 887 (void) memset((char *)e, 0, sizeof (*e)); 888 e->name = auto_rddir_strdup(name); 889 if (e->name == NULL) { 890 free(e); 891 return (ENOMEM); 892 } 893 e->next = NULL; 894 if (*list == NULL) { 895 /* 896 * list is empty 897 */ 898 *list = *last = e; 899 } else { 900 /* 901 * append to end of list 902 */ 903 assert(*last != NULL); 904 (*last)->next = e; 905 *last = e; 906 } 907 /* 908 * add to binary tree 909 */ 910 btree_enter(list, e); 911 } 912 return (0); 913 } 914 915 /* 916 * Print trace output. 917 * Like fprintf(stderr, fmt, ...) except that if "id" is nonzero, the output 918 * is preceeded by the ID of the calling thread. 919 */ 920 #define FMT_BUFSIZ 1024 921 922 void 923 trace_prt(int id, char *fmt, ...) 924 { 925 va_list args; 926 927 char buf[FMT_BUFSIZ]; 928 929 if (id) { 930 (void) sprintf(buf, "t%u\t%s", thr_self(), fmt); 931 fmt = buf; 932 } 933 va_start(args, fmt); 934 (void) vfprintf(stderr, fmt, args); 935 va_end(args); 936 } 937 938 /* 939 * Extract the isalist(5) for userland from the kernel. 940 */ 941 static char * 942 isalist(void) 943 { 944 char *buf; 945 size_t bufsize = BUFSIZ; /* wild guess */ 946 long ret; 947 948 buf = malloc(bufsize); 949 do { 950 ret = sysinfo(SI_ISALIST, buf, bufsize); 951 if (ret == -1l) 952 return (NULL); 953 if (ret > bufsize) { 954 bufsize = ret; 955 buf = realloc(buf, bufsize); 956 } else 957 break; 958 } while (buf != NULL); 959 960 return (buf); 961 } 962 963 /* 964 * Classify isa's as to bitness of the corresponding ABIs. 965 * isa's which have no "official" system ABI are returned 966 * unrecognised i.e. zero bits. 967 */ 968 static int 969 bitness(char *isaname) 970 { 971 if (strcmp(isaname, "sparc") == 0 || 972 strcmp(isaname, "i386") == 0) 973 return (32); 974 975 if (strcmp(isaname, "sparcv9") == 0 || 976 strcmp(isaname, "amd64") == 0) 977 return (64); 978 979 return (0); 980 } 981 982 /* 983 * Determine the application architecture (derived from uname -m) to expand 984 * the $ARCH and $KARCH macros. 985 * 986 * Like arch(1), we need to substitute "sun4" for "sun4u", "sun4v", ... for 987 * backward compatibility. When kflag is set (like arch -k), the unmodifed 988 * value is returned instead. 989 */ 990 static int 991 arch(char *buf, size_t bufsize, bool_t karch) 992 { 993 long ret; 994 995 ret = sysinfo(SI_MACHINE, buf, bufsize); 996 if (ret == -1L) 997 return (0); 998 if (!karch && strncmp(buf, "sun4", 4) == 0) 999 (void) strlcpy(buf, "sun4", bufsize); 1000 return (1); 1001 } 1002 1003 /* 1004 * Determine the basic ISA (uname -p) to expand the $CPU macro. 1005 */ 1006 static int 1007 cpu(char *buf, size_t bufsize) 1008 { 1009 long ret; 1010 1011 ret = sysinfo(SI_ARCHITECTURE, buf, bufsize); 1012 if (ret == -1L) 1013 return (0); 1014 else 1015 return (1); 1016 } 1017 1018 /* 1019 * Find the left-most element in the isalist that matches our idea of a 1020 * system ABI. 1021 * 1022 * On machines with only one ABI, this is usually the same as uname -p. 1023 */ 1024 static int 1025 natisa(char *buf, size_t bufsize) 1026 { 1027 int bits; 1028 char *isa, *list; 1029 char *lasts; 1030 1031 if ((list = isalist()) == NULL) 1032 return (0); 1033 1034 for (isa = strtok_r(list, " ", &lasts); 1035 isa; isa = strtok_r(0, " ", &lasts)) 1036 if ((bits = bitness(isa)) != 0) 1037 break; /* ignore "extension" architectures */ 1038 1039 if (isa == 0 || bits == 0) { 1040 free(list); 1041 return (0); /* can't figure it out :( */ 1042 } 1043 1044 (void) strncpy(buf, isa, bufsize); 1045 free(list); 1046 1047 return (1); 1048 } 1049 1050 /* 1051 * Determine the platform (uname -i) to expand the $PLATFORM macro. 1052 */ 1053 static int 1054 platform(char *buf, size_t bufsize) 1055 { 1056 long ret; 1057 1058 ret = sysinfo(SI_PLATFORM, buf, bufsize); 1059 if (ret == -1L) 1060 return (0); 1061 else 1062 return (1); 1063 } 1064 1065 /* 1066 * Set environment variables specified in /etc/default/autofs. 1067 */ 1068 void 1069 put_automountd_env(void) 1070 { 1071 char *defval; 1072 int defflags; 1073 1074 if ((defval = defread("AUTOMOUNTD_ENV=")) != NULL) { 1075 (void) putenv(strdup(defval)); 1076 defflags = defcntl(DC_GETFLAGS, 0); 1077 TURNON(defflags, DC_NOREWIND); 1078 defflags = defcntl(DC_SETFLAGS, defflags); 1079 while ((defval = defread("AUTOMOUNTD_ENV=")) != NULL) 1080 (void) putenv(strdup(defval)); 1081 (void) defcntl(DC_SETFLAGS, defflags); 1082 } 1083 } 1084