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