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