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 2006 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 292 * it into "w". Both "wq" and "w" are quote vectors 293 * for "w" and "p". Delim is the character to be used 294 * as a delimiter for the scan. A space means "whitespace". 295 * The call to getword must provide buffers w and wq of size at 296 * least wordsz. getword() will pass strings of maximum length 297 * (wordsz-1), since it needs to null terminate the string. 298 * Returns 0 on ok and -1 on error. 299 */ 300 int 301 getword(char *w, char *wq, char **p, char **pq, char delim, int wordsz) 302 { 303 char *tmp = w; 304 char *tmpq = wq; 305 int count = wordsz; 306 307 if (wordsz <= 0) { 308 if (verbose) 309 syslog(LOG_ERR, 310 "getword: input word size %d must be > 0", wordsz); 311 return (-1); 312 } 313 314 while ((delim == ' ' ? isspace(**p) : **p == delim) && **pq == ' ') 315 (*p)++, (*pq)++; 316 317 while (**p && 318 !((delim == ' ' ? isspace(**p) : **p == delim) && 319 **pq == ' ')) { 320 if (--count <= 0) { 321 *tmp = '\0'; 322 *tmpq = '\0'; 323 syslog(LOG_ERR, 324 "maximum word length (%d) exceeded", wordsz); 325 return (-1); 326 } 327 *w++ = *(*p)++; 328 *wq++ = *(*pq)++; 329 } 330 *w = '\0'; 331 *wq = '\0'; 332 333 return (0); 334 } 335 336 /* 337 * get_line attempts to get a line from the map, upto LINESZ. A line in 338 * the map is a concatenation of lines if the continuation symbol '\' 339 * is used at the end of the line. Returns line on success, a NULL on 340 * EOF, and an empty string on lines > linesz. 341 */ 342 char * 343 get_line(FILE *fp, char *map, char *line, int linesz) 344 { 345 register char *p = line; 346 register int len; 347 int excess = 0; 348 349 *p = '\0'; 350 351 for (;;) { 352 if (fgets(p, linesz - (p-line), fp) == NULL) { 353 return (*line ? line : NULL); /* EOF */ 354 } 355 356 len = strlen(line); 357 if (len <= 0) { 358 p = line; 359 continue; 360 } 361 p = &line[len - 1]; 362 363 /* 364 * Is input line too long? 365 */ 366 if (*p != '\n') { 367 excess = 1; 368 /* 369 * Perhaps last char read was '\'. Reinsert it 370 * into the stream to ease the parsing when we 371 * read the rest of the line to discard. 372 */ 373 (void) ungetc(*p, fp); 374 break; 375 } 376 trim: 377 /* trim trailing white space */ 378 while (p >= line && isspace(*(uchar_t *)p)) 379 *p-- = '\0'; 380 if (p < line) { /* empty line */ 381 p = line; 382 continue; 383 } 384 385 if (*p == '\\') { /* continuation */ 386 *p = '\0'; 387 continue; 388 } 389 390 /* 391 * Ignore comments. Comments start with '#' 392 * which must be preceded by a whitespace, unless 393 * if '#' is the first character in the line. 394 */ 395 p = line; 396 while (p = strchr(p, '#')) { 397 if (p == line || isspace(*(p-1))) { 398 *p-- = '\0'; 399 goto trim; 400 } 401 p++; 402 } 403 break; 404 } 405 if (excess) { 406 int c; 407 408 /* 409 * discard rest of line and return an empty string. 410 * done to set the stream to the correct place when 411 * we are done with this line. 412 */ 413 while ((c = getc(fp)) != EOF) { 414 *p = c; 415 if (*p == '\n') /* end of the long line */ 416 break; 417 else if (*p == '\\') { /* continuation */ 418 if (getc(fp) == EOF) /* ignore next char */ 419 break; 420 } 421 } 422 syslog(LOG_ERR, 423 "map %s: line too long (max %d chars)", 424 map, linesz-1); 425 *line = '\0'; 426 } 427 428 return (line); 429 } 430 431 /* 432 * Gets the retry=n entry from opts. 433 * Returns 0 if retry=n is not present in option string, 434 * retry=n is invalid, or when option string is NULL. 435 */ 436 int 437 get_retry(char *opts) 438 { 439 int retry = 0; 440 char buf[MAXOPTSLEN]; 441 char *p, *pb, *lasts; 442 443 if (opts == NULL) 444 return (retry); 445 446 (void) strcpy(buf, opts); 447 pb = buf; 448 while (p = (char *)strtok_r(pb, ",", &lasts)) { 449 pb = NULL; 450 if (strncmp(p, "retry=", 6) == 0) 451 retry = atoi(p+6); 452 } 453 return (retry > 0 ? retry : 0); 454 } 455 456 /* 457 * Returns zero if "opt" is found in mnt->mnt_opts, setting 458 * *sval to whatever follows the equal sign after "opt". 459 * str_opt allocates a string long enough to store the value of 460 * "opt" plus a terminating null character and returns it as *sval. 461 * It is the responsability of the caller to deallocate *sval. 462 * *sval will be equal to NULL upon return if either "opt=" is not found, 463 * or "opt=" has no value associated with it. 464 * 465 * stropt will return -1 on error. 466 */ 467 int 468 str_opt(struct mnttab *mnt, char *opt, char **sval) 469 { 470 char *str, *comma; 471 472 /* 473 * is "opt" in the options field? 474 */ 475 if (str = hasmntopt(mnt, opt)) { 476 str += strlen(opt); 477 if (*str++ != '=' || 478 (*str == ',' || *str == '\0')) { 479 syslog(LOG_ERR, "Bad option field"); 480 return (-1); 481 } 482 comma = strchr(str, ','); 483 if (comma != NULL) 484 *comma = '\0'; 485 *sval = strdup(str); 486 if (comma != NULL) 487 *comma = ','; 488 if (*sval == NULL) 489 return (-1); 490 } else 491 *sval = NULL; 492 493 return (0); 494 } 495 496 /* 497 * Performs text expansions in the string "pline". 498 * "plineq" is the quote vector for "pline". 499 * An identifier prefixed by "$" is replaced by the 500 * corresponding environment variable string. A "&" 501 * is replaced by the key string for the map entry. 502 * 503 * This routine will return an error (non-zero) if *size* would be 504 * exceeded after expansion, indicating that the macro_expand failed. 505 * This is to prevent writing past the end of pline and plineq. 506 * Both pline and plineq are left untouched in such error case. 507 */ 508 int 509 macro_expand(key, pline, plineq, size) 510 char *key, *pline, *plineq; 511 int size; 512 { 513 register char *p, *q; 514 register char *bp, *bq; 515 register char *s; 516 char buffp[LINESZ], buffq[LINESZ]; 517 char namebuf[64], *pn; 518 int expand = 0; 519 struct utsname name; 520 char procbuf[SYS_NMLN]; 521 char isaname[64]; 522 523 p = pline; q = plineq; 524 bp = buffp; bq = buffq; 525 526 while (*p) { 527 if (*p == '&' && *q == ' ') { /* insert key */ 528 /* 529 * make sure we don't overflow buffer 530 */ 531 if ((int)((bp - buffp) + strlen(key)) < size) { 532 for (s = key; *s; s++) { 533 *bp++ = *s; 534 *bq++ = ' '; 535 } 536 expand++; 537 p++; q++; 538 continue; 539 } else { 540 /* 541 * line too long... 542 */ 543 return (1); 544 } 545 } 546 547 if (*p == '$' && *q == ' ') { /* insert env var */ 548 p++; q++; 549 pn = namebuf; 550 if (*p == '{') { 551 p++; q++; 552 while (*p && *p != '}') { 553 *pn++ = *p++; 554 q++; 555 } 556 if (*p) { 557 p++; q++; 558 } 559 } else { 560 while (*p && (*p == '_' || isalnum(*p))) { 561 *pn++ = *p++; 562 q++; 563 } 564 } 565 *pn = '\0'; 566 567 s = getenv(namebuf); 568 if (!s) { 569 /* not found in env */ 570 if (strcmp(namebuf, "ARCH") == 0) { 571 if (arch(procbuf, sizeof (procbuf), 572 FALSE)) 573 s = procbuf; 574 } else if (strcmp(namebuf, "CPU") == 0) { 575 if (cpu(procbuf, sizeof (procbuf))) 576 s = procbuf; 577 } else if (strcmp(namebuf, "HOST") == 0) { 578 (void) uname(&name); 579 s = name.nodename; 580 } else if (strcmp(namebuf, "KARCH") == 0) { 581 if (arch(procbuf, sizeof (procbuf), 582 TRUE)) 583 s = procbuf; 584 } else if (strcmp(namebuf, "OSREL") == 0) { 585 (void) uname(&name); 586 s = name.release; 587 } else if (strcmp(namebuf, "OSNAME") == 0) { 588 (void) uname(&name); 589 s = name.sysname; 590 } else if (strcmp(namebuf, "OSVERS") == 0) { 591 (void) uname(&name); 592 s = name.version; 593 } else if (strcmp(namebuf, "NATISA") == 0) { 594 if (natisa(isaname, sizeof (isaname))) 595 s = isaname; 596 } else if (strcmp(namebuf, "PLATFORM") == 0) { 597 if (platform(procbuf, sizeof (procbuf))) 598 s = procbuf; 599 } 600 } 601 602 if (s) { 603 if ((int)((bp - buffp) + strlen(s)) < size) { 604 while (*s) { 605 *bp++ = *s++; 606 *bq++ = ' '; 607 } 608 } else { 609 /* 610 * line too long... 611 */ 612 return (1); 613 } 614 } 615 expand++; 616 continue; 617 } 618 /* 619 * Since buffp needs to be null terminated, we need to 620 * check that there's still room in the buffer to 621 * place at least two more characters, *p and the 622 * terminating null. 623 */ 624 if (bp - buffp == size - 1) { 625 /* 626 * There was not enough room for at least two more 627 * characters, return with an error. 628 */ 629 return (1); 630 } 631 /* 632 * The total number of characters so far better be less 633 * than the size of buffer passed in. 634 */ 635 *bp++ = *p++; 636 *bq++ = *q++; 637 638 } 639 if (!expand) 640 return (0); 641 *bp = '\0'; 642 *bq = '\0'; 643 /* 644 * We know buffp/buffq will fit in pline/plineq since we 645 * processed at most size characters. 646 */ 647 (void) strcpy(pline, buffp); 648 (void) strcpy(plineq, buffq); 649 650 return (0); 651 } 652 653 /* 654 * Removes quotes from the string "str" and returns 655 * the quoting information in "qbuf". e.g. 656 * original str: 'the "quick brown" f\ox' 657 * unquoted str: 'the quick brown fox' 658 * and the qbuf: ' ^^^^^^^^^^^ ^ ' 659 */ 660 void 661 unquote(str, qbuf) 662 char *str, *qbuf; 663 { 664 register int escaped, inquote, quoted; 665 register char *ip, *bp, *qp; 666 char buf[LINESZ]; 667 668 escaped = inquote = quoted = 0; 669 670 for (ip = str, bp = buf, qp = qbuf; *ip; ip++) { 671 if (!escaped) { 672 if (*ip == '\\') { 673 escaped = 1; 674 quoted++; 675 continue; 676 } else 677 if (*ip == '"') { 678 inquote = !inquote; 679 quoted++; 680 continue; 681 } 682 } 683 684 *bp++ = *ip; 685 *qp++ = (inquote || escaped) ? '^' : ' '; 686 escaped = 0; 687 } 688 *bp = '\0'; 689 *qp = '\0'; 690 if (quoted) 691 (void) strcpy(str, buf); 692 } 693 694 /* 695 * Removes trailing spaces from string "s". 696 */ 697 void 698 trim(s) 699 char *s; 700 { 701 char *p = &s[strlen(s) - 1]; 702 703 while (p >= s && isspace(*(uchar_t *)p)) 704 *p-- = '\0'; 705 } 706 707 /* 708 * try to allocate memory using malloc, if malloc fails, then flush the 709 * rddir caches, and retry. If the second allocation after the readdir 710 * caches have been flushed fails too, then return NULL to indicate 711 * memory could not be allocated. 712 */ 713 char * 714 auto_rddir_malloc(unsigned nbytes) 715 { 716 char *p; 717 int again = 0; 718 719 if ((p = malloc(nbytes)) == NULL) { 720 /* 721 * No memory, free rddir caches and try again 722 */ 723 mutex_lock(&cleanup_lock); 724 cond_signal(&cleanup_start_cv); 725 if (cond_wait(&cleanup_done_cv, &cleanup_lock)) { 726 mutex_unlock(&cleanup_lock); 727 syslog(LOG_ERR, "auto_rddir_malloc interrupted\n"); 728 } else { 729 mutex_unlock(&cleanup_lock); 730 again = 1; 731 } 732 } 733 734 if (again) 735 p = malloc(nbytes); 736 737 return (p); 738 } 739 740 /* 741 * try to strdup a string, if it fails, then flush the rddir caches, 742 * and retry. If the second strdup fails, return NULL to indicate failure. 743 */ 744 char * 745 auto_rddir_strdup(const char *s1) 746 { 747 char *s2; 748 int again = 0; 749 750 if ((s2 = strdup(s1)) == NULL) { 751 /* 752 * No memory, free rddir caches and try again 753 */ 754 mutex_lock(&cleanup_lock); 755 cond_signal(&cleanup_start_cv); 756 if (cond_wait(&cleanup_done_cv, &cleanup_lock)) { 757 mutex_unlock(&cleanup_lock); 758 syslog(LOG_ERR, "auto_rddir_strdup interrupted\n"); 759 } else { 760 mutex_unlock(&cleanup_lock); 761 again = 1; 762 } 763 } 764 765 if (again) 766 s2 = strdup(s1); 767 768 return (s2); 769 } 770 771 /* 772 * Returns a pointer to the entry corresponding to 'name' if found, 773 * otherwise it returns NULL. 774 */ 775 struct dir_entry * 776 btree_lookup(struct dir_entry *head, char *name) 777 { 778 register struct dir_entry *p; 779 register int direction; 780 781 for (p = head; p != NULL; ) { 782 direction = strcmp(name, p->name); 783 if (direction == 0) 784 return (p); 785 if (direction > 0) 786 p = p->right; 787 else p = p->left; 788 } 789 return (NULL); 790 } 791 792 /* 793 * Add entry to binary tree 794 * Duplicate entries are not added 795 */ 796 void 797 btree_enter(struct dir_entry **head, struct dir_entry *ent) 798 { 799 register struct dir_entry *p, *prev = NULL; 800 register int direction; 801 802 ent->right = ent->left = NULL; 803 if (*head == NULL) { 804 *head = ent; 805 return; 806 } 807 808 for (p = *head; p != NULL; ) { 809 prev = p; 810 direction = strcmp(ent->name, p->name); 811 if (direction == 0) { 812 /* 813 * entry already in btree 814 */ 815 return; 816 } 817 if (direction > 0) 818 p = p->right; 819 else p = p->left; 820 } 821 assert(prev != NULL); 822 if (direction > 0) 823 prev->right = ent; 824 else prev->left = ent; 825 } 826 827 /* 828 * If entry doesn't exist already, add it to the linear list 829 * after '*last' and to the binary tree list. 830 * If '*last == NULL' then the list is walked till the end. 831 * *last is always set to the new element after successful completion. 832 * if entry already exists '*last' is only updated if not previously 833 * provided. 834 */ 835 int 836 add_dir_entry(char *name, struct dir_entry **list, struct dir_entry **last) 837 { 838 struct dir_entry *e, *l; 839 840 if ((*list != NULL) && (*last == NULL)) { 841 /* 842 * walk the list to find last element 843 */ 844 for (l = *list; l != NULL; l = l->next) 845 *last = l; 846 } 847 848 if (btree_lookup(*list, name) == NULL) { 849 /* 850 * not a duplicate, add it to list 851 */ 852 /* LINTED pointer alignment */ 853 e = (struct dir_entry *) 854 auto_rddir_malloc(sizeof (struct dir_entry)); 855 if (e == NULL) 856 return (ENOMEM); 857 (void) memset((char *)e, 0, sizeof (*e)); 858 e->name = auto_rddir_strdup(name); 859 if (e->name == NULL) { 860 free(e); 861 return (ENOMEM); 862 } 863 e->next = NULL; 864 if (*list == NULL) { 865 /* 866 * list is empty 867 */ 868 *list = *last = e; 869 } else { 870 /* 871 * append to end of list 872 */ 873 assert(*last != NULL); 874 (*last)->next = e; 875 *last = e; 876 } 877 /* 878 * add to binary tree 879 */ 880 btree_enter(list, e); 881 } 882 return (0); 883 } 884 885 /* 886 * Print trace output. 887 * Like fprintf(stderr, fmt, ...) except that if "id" is nonzero, the output 888 * is preceeded by the ID of the calling thread. 889 */ 890 #define FMT_BUFSIZ 1024 891 892 void 893 trace_prt(int id, char *fmt, ...) 894 { 895 va_list args; 896 897 char buf[FMT_BUFSIZ]; 898 899 if (id) { 900 (void) sprintf(buf, "t%u\t%s", thr_self(), fmt); 901 fmt = buf; 902 } 903 va_start(args, fmt); 904 (void) vfprintf(stderr, fmt, args); 905 va_end(args); 906 } 907 908 /* 909 * Extract the isalist(5) for userland from the kernel. 910 */ 911 static char * 912 isalist(void) 913 { 914 char *buf; 915 size_t bufsize = BUFSIZ; /* wild guess */ 916 long ret; 917 918 buf = malloc(bufsize); 919 do { 920 ret = sysinfo(SI_ISALIST, buf, bufsize); 921 if (ret == -1l) 922 return (NULL); 923 if (ret > bufsize) { 924 bufsize = ret; 925 buf = realloc(buf, bufsize); 926 } else 927 break; 928 } while (buf != NULL); 929 930 return (buf); 931 } 932 933 /* 934 * Classify isa's as to bitness of the corresponding ABIs. 935 * isa's which have no "official" system ABI are returned 936 * unrecognised i.e. zero bits. 937 */ 938 static int 939 bitness(char *isaname) 940 { 941 if (strcmp(isaname, "sparc") == 0 || 942 strcmp(isaname, "i386") == 0) 943 return (32); 944 945 if (strcmp(isaname, "sparcv9") == 0 || 946 strcmp(isaname, "amd64") == 0) 947 return (64); 948 949 return (0); 950 } 951 952 /* 953 * Determine the application architecture (derived from uname -m) to expand 954 * the $ARCH and $KARCH macros. 955 * 956 * Like arch(1), we need to substitute "sun4" for "sun4u", "sun4v", ... for 957 * backward compatibility. When kflag is set (like arch -k), the unmodifed 958 * value is returned instead. 959 */ 960 static int 961 arch(char *buf, size_t bufsize, bool_t karch) 962 { 963 long ret; 964 965 ret = sysinfo(SI_MACHINE, buf, bufsize); 966 if (ret == -1L) 967 return (0); 968 if (!karch && strncmp(buf, "sun4", 4) == 0) 969 (void) strlcpy(buf, "sun4", bufsize); 970 return (1); 971 } 972 973 /* 974 * Determine the basic ISA (uname -p) to expand the $CPU macro. 975 */ 976 static int 977 cpu(char *buf, size_t bufsize) 978 { 979 long ret; 980 981 ret = sysinfo(SI_ARCHITECTURE, buf, bufsize); 982 if (ret == -1L) 983 return (0); 984 else 985 return (1); 986 } 987 988 /* 989 * Find the left-most element in the isalist that matches our idea of a 990 * system ABI. 991 * 992 * On machines with only one ABI, this is usually the same as uname -p. 993 */ 994 static int 995 natisa(char *buf, size_t bufsize) 996 { 997 int bits; 998 char *isa, *list; 999 char *lasts; 1000 1001 if ((list = isalist()) == NULL) 1002 return (0); 1003 1004 for (isa = strtok_r(list, " ", &lasts); 1005 isa; isa = strtok_r(0, " ", &lasts)) 1006 if ((bits = bitness(isa)) != 0) 1007 break; /* ignore "extension" architectures */ 1008 1009 if (isa == 0 || bits == 0) { 1010 free(list); 1011 return (0); /* can't figure it out :( */ 1012 } 1013 1014 (void) strncpy(buf, isa, bufsize); 1015 free(list); 1016 1017 return (1); 1018 } 1019 1020 /* 1021 * Determine the platform (uname -i) to expand the $PLATFORM macro. 1022 */ 1023 static int 1024 platform(char *buf, size_t bufsize) 1025 { 1026 long ret; 1027 1028 ret = sysinfo(SI_PLATFORM, buf, bufsize); 1029 if (ret == -1L) 1030 return (0); 1031 else 1032 return (1); 1033 } 1034 1035 /* 1036 * Set environment variables specified in /etc/default/autofs. 1037 */ 1038 void 1039 put_automountd_env(void) 1040 { 1041 char *defval; 1042 int defflags; 1043 1044 if ((defval = defread("AUTOMOUNTD_ENV=")) != NULL) { 1045 (void) putenv(strdup(defval)); 1046 defflags = defcntl(DC_GETFLAGS, 0); 1047 TURNON(defflags, DC_NOREWIND); 1048 defflags = defcntl(DC_SETFLAGS, defflags); 1049 while ((defval = defread("AUTOMOUNTD_ENV=")) != NULL) 1050 (void) putenv(strdup(defval)); 1051 (void) defcntl(DC_SETFLAGS, defflags); 1052 } 1053 } 1054