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 /* 23 * Copyright (c) 1996, 2010, Oracle and/or its affiliates. All rights reserved. 24 */ 25 26 #include <stdlib.h> 27 #include <ctype.h> 28 #include <unistd.h> 29 #include <limits.h> 30 #include <fcntl.h> 31 #include <sys/types.h> 32 #include <sys/stat.h> 33 #include <utime.h> 34 #include <synch.h> 35 #include <strings.h> 36 #include <string.h> 37 #include <libintl.h> 38 #include <errno.h> 39 #include <auth_list.h> 40 #include <syslog.h> 41 #include <bsm/devices.h> 42 #include <bsm/devalloc.h> 43 #include <tsol/label.h> 44 45 #define DA_DEFS "/etc/security/tsol/devalloc_defaults" 46 47 extern int _readbufline(char *, int, char *, int, int *); 48 extern char *strtok_r(char *, const char *, char **); 49 extern char *_strtok_escape(char *, char *, char **); 50 extern int getdaon(void); 51 extern int da_matchname(devalloc_t *, char *); 52 extern int da_match(devalloc_t *, da_args *); 53 extern int dmap_matchname(devmap_t *, char *); 54 extern int dm_match(devmap_t *, da_args *); 55 extern int dmap_matchtype(devmap_t *dmap, char *type); 56 extern int dmap_matchdev(devmap_t *dmap, char *dev); 57 extern int dmap_exact_dev(devmap_t *dmap, char *dev, int *num); 58 extern char *dmap_physname(devmap_t *dmap); 59 60 /* 61 * The following structure is for recording old entries to be retained. 62 * We read the entries from the database into a linked list in memory, 63 * then turn around and write them out again. 64 */ 65 typedef struct strentry { 66 struct strentry *se_next; 67 char se_str[4096 + 1]; 68 } strentry_t; 69 70 /* 71 * da_check_longindevperm - 72 * reads /etc/logindevperm and checks if specified device is in the file. 73 * returns 1 if specified device found in /etc/logindevperm, else returns 0 74 */ 75 int 76 da_check_logindevperm(char *devname) 77 { 78 int ret = 0; 79 int fd = -1; 80 int nlen, plen, slen, lineno, fsize; 81 char line[MAX_CANON]; 82 char *field_delims = " \t\n"; 83 char *fbuf = NULL; 84 char *ptr, *device; 85 char *lasts = NULL; 86 FILE *fp; 87 struct stat f_stat; 88 89 /* 90 * check if /etc/logindevperm exists and get its size 91 */ 92 if ((fd = open(LOGINDEVPERM, O_RDONLY)) == -1) 93 return (0); 94 if (fstat(fd, &f_stat) != 0) { 95 (void) close(fd); 96 return (0); 97 } 98 fsize = f_stat.st_size; 99 if ((fbuf = (char *)malloc(fsize)) == NULL) { 100 (void) close(fd); 101 return (0); 102 } 103 if ((fp = fdopen(fd, "rF")) == NULL) { 104 free(fbuf); 105 (void) close(fd); 106 return (0); 107 } 108 109 /* 110 * read and parse /etc/logindevperm 111 */ 112 plen = nlen = lineno = 0; 113 while (fgets(line, MAX_CANON, fp) != NULL) { 114 lineno++; 115 if ((ptr = strchr(line, '#')) != NULL) 116 *ptr = '\0'; /* handle comments */ 117 if (strtok_r(line, field_delims, &lasts) == NULL) 118 continue; /* ignore blank lines */ 119 if (strtok_r(NULL, field_delims, &lasts) == NULL) 120 /* invalid entry */ 121 continue; 122 if ((ptr = strtok_r(NULL, field_delims, &lasts)) == NULL) 123 /* empty device list */ 124 continue; 125 nlen = strlen(ptr) + 1; /* +1 terminator */ 126 nlen += (plen + 1); 127 if (plen == 0) 128 slen = snprintf(fbuf, nlen, "%s", ptr); 129 else 130 slen = snprintf(fbuf + plen, nlen - plen, ":%s", ptr); 131 if (slen >= fsize) { 132 fbuf[0] = '\0'; 133 (void) fclose(fp); 134 return (slen); 135 } 136 plen += slen; 137 } 138 (void) fclose(fp); 139 140 /* 141 * check if devname exists in /etc/logindevperm 142 */ 143 device = strtok_r(fbuf, ":", &lasts); 144 while (device != NULL) { 145 /* 146 * device and devname may be one of these types - 147 * /dev/xx 148 * /dev/xx* 149 * /dev/dir/xx 150 * /dev/dir/xx* 151 * /dev/dir/"*" 152 */ 153 if (strcmp(device, devname) == 0) { 154 /* /dev/xx, /dev/dir/xx */ 155 free(fbuf); 156 return (1); 157 } 158 if ((ptr = strrchr(device, KV_WILDCHAR)) != NULL) { 159 /* all wildcard types */ 160 *ptr = '\0'; 161 if (strncmp(device, devname, strlen(device)) == 0) { 162 free(fbuf); 163 return (1); 164 } 165 } 166 device = strtok_r(NULL, ":", &lasts); 167 } 168 169 return (ret); 170 } 171 172 /* 173 * _da_read_file - 174 * establishes readers/writer lock on fname; reads in the file if its 175 * contents changed since the last time we read it. 176 * returns size of buffer read, or -1 on failure. 177 */ 178 int 179 _da_read_file(char *fname, char **fbuf, time_t *ftime, rwlock_t *flock, 180 int flag) 181 { 182 int fd = -1; 183 int fsize = 0; 184 time_t newtime; 185 struct stat f_stat; 186 187 if (flag & DA_FORCE) 188 *ftime = 0; 189 190 /* check the size and the time stamp on the file */ 191 if (rw_rdlock(flock) != 0) 192 return (-1); 193 if (stat(fname, &f_stat) != 0) { 194 (void) rw_unlock(flock); 195 return (-1); 196 } 197 fsize = f_stat.st_size; 198 newtime = f_stat.st_mtime; 199 (void) rw_unlock(flock); 200 201 while (newtime > *ftime) { 202 /* 203 * file has been modified since we last read it; or this 204 * is a forced read. 205 * read file into the buffer with rw lock. 206 */ 207 if (rw_wrlock(flock) != 0) 208 return (-1); 209 if ((fd = open(fname, O_RDONLY)) == -1) { 210 (void) rw_unlock(flock); 211 return (-1); 212 } 213 if (*fbuf != NULL) { 214 free(*fbuf); 215 *fbuf = NULL; 216 } 217 if ((*fbuf = malloc(fsize)) == NULL) { 218 (void) rw_unlock(flock); 219 (void) close(fd); 220 return (-1); 221 } 222 if (read(fd, *fbuf, fsize) < fsize) { 223 free(*fbuf); 224 (void) rw_unlock(flock); 225 (void) close(fd); 226 return (-1); 227 } 228 (void) rw_unlock(flock); 229 /* 230 * verify that the file did not change just after we read it. 231 */ 232 if (rw_rdlock(flock) != 0) { 233 free(*fbuf); 234 (void) close(fd); 235 return (-1); 236 } 237 if (stat(fname, &f_stat) != 0) { 238 free(*fbuf); 239 (void) rw_unlock(flock); 240 (void) close(fd); 241 return (-1); 242 } 243 fsize = f_stat.st_size; 244 newtime = f_stat.st_mtime; 245 (void) rw_unlock(flock); 246 (void) close(fd); 247 *ftime = newtime; 248 } 249 250 return (fsize); 251 } 252 253 /* 254 * _update_zonename - 255 * add/remove current zone's name to the given devalloc_t. 256 */ 257 void 258 _update_zonename(da_args *dargs, devalloc_t *dap) 259 { 260 int i, j; 261 int oldsize, newsize; 262 int has_zonename = 0; 263 char *zonename; 264 kva_t *newkva, *oldkva; 265 kv_t *newdata, *olddata; 266 devinfo_t *devinfo; 267 268 devinfo = dargs->devinfo; 269 oldkva = dap->da_devopts; 270 if (oldkva == NULL) { 271 if (dargs->optflag & DA_REMOVE_ZONE) 272 return; 273 if (dargs->optflag & DA_ADD_ZONE) { 274 newkva = _str2kva(devinfo->devopts, KV_ASSIGN, 275 KV_TOKEN_DELIMIT); 276 if (newkva != NULL) 277 dap->da_devopts = newkva; 278 return; 279 } 280 } 281 newsize = oldsize = oldkva->length; 282 if (kva_match(oldkva, DAOPT_ZONE)) 283 has_zonename = 1; 284 if (dargs->optflag & DA_ADD_ZONE) { 285 if ((zonename = index(devinfo->devopts, '=')) == NULL) 286 return; 287 zonename++; 288 if (has_zonename) { 289 (void) _insert2kva(oldkva, DAOPT_ZONE, zonename); 290 return; 291 } 292 newsize += 1; 293 } else if (dargs->optflag & DA_REMOVE_ZONE) { 294 if (has_zonename) { 295 newsize -= 1; 296 if (newsize == 0) { 297 /* 298 * If zone name was the only key/value pair, 299 * put 'reserved' in the empty slot. 300 */ 301 _kva_free(oldkva); 302 dap->da_devopts = NULL; 303 return; 304 } 305 } else { 306 return; 307 } 308 } 309 newkva = _new_kva(newsize); 310 newkva->length = 0; 311 newdata = newkva->data; 312 olddata = oldkva->data; 313 for (i = 0, j = 0; i < oldsize; i++) { 314 if ((dargs->optflag & DA_REMOVE_ZONE) && 315 (strcmp(olddata[i].key, DAOPT_ZONE) == 0)) 316 continue; 317 newdata[j].key = strdup(olddata[i].key); 318 newdata[j].value = strdup(olddata[i].value); 319 newkva->length++; 320 j++; 321 } 322 if (dargs->optflag & DA_ADD_ZONE) { 323 newdata[j].key = strdup(DAOPT_ZONE); 324 newdata[j].value = strdup(zonename); 325 newkva->length++; 326 } 327 _kva_free(oldkva); 328 dap->da_devopts = newkva; 329 } 330 331 /* 332 * _dmap2str - 333 * converts a device_map entry into a printable string 334 * returns 0 on success, -1 on error. 335 */ 336 /*ARGSUSED*/ 337 static int 338 _dmap2str(devmap_t *dmp, char *buf, int size, const char *sep) 339 { 340 int length; 341 342 length = snprintf(buf, size, "%s%s", dmp->dmap_devname, sep); 343 if (length >= size) 344 return (-1); 345 length += snprintf(buf + length, size - length, "%s%s", 346 dmp->dmap_devtype, sep); 347 if (length >= size) 348 return (-1); 349 length += snprintf(buf + length, size - length, "%s\n", 350 dmp->dmap_devlist); 351 if (length >= size) 352 return (-1); 353 return (0); 354 } 355 356 /* 357 * _dmap2strentry - 358 * calls dmap2str to break given devmap_t into printable entry. 359 * returns pointer to decoded entry, NULL on error. 360 */ 361 static strentry_t * 362 _dmap2strentry(devmap_t *devmapp) 363 { 364 strentry_t *sep; 365 366 if ((sep = (strentry_t *)malloc(sizeof (strentry_t))) == NULL) 367 return (NULL); 368 if (_dmap2str(devmapp, sep->se_str, sizeof (sep->se_str), 369 KV_TOKEN_DELIMIT"\\\n\t") != 0) { 370 free(sep); 371 return (NULL); 372 } 373 return (sep); 374 } 375 376 /* 377 * fix_optstr - 378 * removes trailing ':' from buf. 379 */ 380 void 381 fix_optstr(char *buf) 382 { 383 char *p = NULL; 384 385 if (p = rindex(buf, ':')) 386 *p = ';'; 387 } 388 389 /* 390 * _da2str - 391 * converts a device_allocate entry into a printable string 392 * returns 0 on success, -1 on error. 393 */ 394 static int 395 _da2str(da_args *dargs, devalloc_t *dap, char *buf, int size, const char *sep, 396 const char *osep) 397 { 398 int length; 399 int matching_entry = 0; 400 char **dnames; 401 402 if (dargs->optflag & DA_UPDATE && 403 (dargs->optflag & DA_ADD_ZONE || 404 dargs->optflag & DA_REMOVE_ZONE) && 405 dargs->devnames) { 406 for (dnames = dargs->devnames; *dnames != NULL; dnames++) { 407 if (da_matchname(dap, *dnames)) { 408 matching_entry = 1; 409 break; 410 } 411 } 412 } 413 length = snprintf(buf, size, "%s%s", dap->da_devname, sep); 414 if (length >= size) 415 return (-1); 416 length += snprintf(buf + length, size - length, "%s%s", 417 dap->da_devtype, sep); 418 if (length >= size) 419 return (-1); 420 if (matching_entry) 421 _update_zonename(dargs, dap); 422 if ((dap->da_devopts == NULL) || ((dap->da_devopts->length == 1) && 423 (strcmp(dap->da_devopts->data->key, DA_RESERVED) == 0))) { 424 length += snprintf(buf + length, size - length, "%s%s", 425 DA_RESERVED, sep); 426 } else { 427 if (_kva2str(dap->da_devopts, buf + length, size - length, 428 KV_ASSIGN, (char *)osep) != 0) 429 return (-1); 430 length = strlen(buf); 431 } 432 if (dap->da_devopts) 433 fix_optstr(buf); 434 if (length >= size) 435 return (-1); 436 length += snprintf(buf + length, size - length, "%s%s", 437 DA_RESERVED, sep); 438 if (length >= size) 439 return (-1); 440 length += snprintf(buf + length, size - length, "%s%s", 441 dap->da_devauth ? dap->da_devauth : DA_ANYUSER, sep); 442 if (length >= size) 443 return (-1); 444 length += snprintf(buf + length, size - length, "%s\n", 445 dap->da_devexec ? dap->da_devexec : ""); 446 if (length >= size) 447 return (-1); 448 449 return (0); 450 } 451 452 /* 453 * _da2strentry - 454 * calls da2str to break given devalloc_t into printable entry. 455 * returns pointer to decoded entry, NULL on error. 456 */ 457 static strentry_t * 458 _da2strentry(da_args *dargs, devalloc_t *dap) 459 { 460 strentry_t *sep; 461 462 if ((sep = (strentry_t *)malloc(sizeof (strentry_t))) == NULL) 463 return (NULL); 464 if (_da2str(dargs, dap, sep->se_str, sizeof (sep->se_str), 465 KV_DELIMITER "\\\n\t", KV_TOKEN_DELIMIT "\\\n\t") != 0) { 466 free(sep); 467 return (NULL); 468 } 469 return (sep); 470 } 471 472 /* 473 * _def2str 474 * converts da_defs_t into a printable string. 475 * returns 0 on success, -1 on error. 476 */ 477 static int 478 _def2str(da_defs_t *da_defs, char *buf, int size, const char *sep) 479 { 480 int length; 481 482 length = snprintf(buf, size, "%s%s", da_defs->devtype, sep); 483 if (length >= size) 484 return (-1); 485 if (da_defs->devopts) { 486 if (_kva2str(da_defs->devopts, buf + length, size - length, 487 KV_ASSIGN, KV_DELIMITER) != 0) 488 return (-1); 489 length = strlen(buf); 490 } 491 if (length >= size) 492 return (-1); 493 494 return (0); 495 } 496 497 /* 498 * _def2strentry 499 * calls _def2str to break given da_defs_t into printable entry. 500 * returns pointer decoded entry, NULL on error. 501 */ 502 static strentry_t * 503 _def2strentry(da_defs_t *da_defs) 504 { 505 strentry_t *sep; 506 507 if ((sep = (strentry_t *)malloc(sizeof (strentry_t))) == NULL) 508 return (NULL); 509 if (_def2str(da_defs, sep->se_str, sizeof (sep->se_str), 510 KV_TOKEN_DELIMIT) != 0) { 511 free(sep); 512 return (NULL); 513 } 514 515 return (sep); 516 } 517 518 /* 519 * _build_defattrs 520 * cycles through all defattr entries, stores them in memory. removes 521 * entries with the given search_key (device type). 522 * returns 0 if given entry not found, 1 if given entry removed, 2 on 523 * error. 524 */ 525 static int 526 _build_defattrs(da_args *dargs, strentry_t **head_defent) 527 { 528 int rc = 0; 529 da_defs_t *da_defs; 530 strentry_t *tail_str, *tmp_str; 531 532 setdadefent(); 533 while ((da_defs = getdadefent()) != NULL) { 534 rc = !(strcmp(da_defs->devtype, dargs->devinfo->devtype)); 535 if (rc && dargs->optflag & DA_ADD && 536 !(dargs->optflag & DA_FORCE)) { 537 /* 538 * During DA_ADD, we keep an existing entry unless 539 * we have DA_FORCE set to override that entry. 540 */ 541 dargs->optflag |= DA_NO_OVERRIDE; 542 rc = 0; 543 } 544 if (rc == 0) { 545 tmp_str = _def2strentry(da_defs); 546 if (tmp_str == NULL) { 547 freedadefent(da_defs); 548 enddadefent(); 549 return (2); 550 } 551 /* retaining defattr entry: tmp_str->se_str */ 552 tmp_str->se_next = NULL; 553 if (*head_defent == NULL) { 554 *head_defent = tail_str = tmp_str; 555 } else { 556 tail_str->se_next = tmp_str; 557 tail_str = tmp_str; 558 } 559 } 560 freedadefent(da_defs); 561 } 562 enddadefent(); 563 564 return (rc); 565 } 566 567 /* 568 * We have to handle the "standard" types in devlist differently than 569 * other devices, which are not covered by our auto-naming conventions. 570 * 571 * buf must be a buffer of size DA_MAX_NAME + 1 572 */ 573 int 574 da_std_type(da_args *dargs, char *namebuf) 575 { 576 char *type = dargs->devinfo->devtype; 577 int system_labeled; 578 579 system_labeled = is_system_labeled(); 580 581 /* check safely for sizes */ 582 if (strcmp(DA_AUDIO_TYPE, type) == 0) { 583 (void) strlcpy(namebuf, DA_AUDIO_NAME, DA_MAXNAME); 584 return (1); 585 } 586 if (strcmp(DA_CD_TYPE, type) == 0) { 587 if (system_labeled) 588 (void) strlcpy(namebuf, DA_CD_NAME, DA_MAXNAME); 589 else 590 (void) strlcpy(namebuf, DA_CD_TYPE, DA_MAXNAME); 591 return (1); 592 } 593 if (strcmp(DA_FLOPPY_TYPE, type) == 0) { 594 if (system_labeled) 595 (void) strlcpy(namebuf, DA_FLOPPY_NAME, DA_MAXNAME); 596 else 597 (void) strlcpy(namebuf, DA_FLOPPY_TYPE, DA_MAXNAME); 598 return (1); 599 } 600 if (strcmp(DA_TAPE_TYPE, type) == 0) { 601 if (system_labeled) 602 (void) strlcpy(namebuf, DA_TAPE_NAME, DA_MAXNAME); 603 else 604 (void) strlcpy(namebuf, DA_TAPE_TYPE, DA_MAXNAME); 605 return (1); 606 } 607 if (strcmp(DA_RMDISK_TYPE, type) == 0) { 608 (void) strlcpy(namebuf, DA_RMDISK_NAME, DA_MAXNAME); 609 return (1); 610 } 611 namebuf[0] = '\0'; 612 return (0); 613 } 614 615 /* 616 * allocatable: returns 617 * -1 if no auths field, 618 * 0 if not allocatable (marked '*') 619 * 1 if not marked '*' 620 */ 621 static int 622 allocatable(da_args *dargs) 623 { 624 625 if (!dargs->devinfo->devauths) 626 return (-1); 627 if (strcmp("*", dargs->devinfo->devauths) == 0) 628 return (0); 629 return (1); 630 } 631 632 /* 633 * _rebuild_lists - 634 * 635 * If dargs->optflag & DA_EVENT, does not assume the dargs list is 636 * complete or completely believable, since devfsadm caches 637 * ONLY what it has been exposed to via syseventd. 638 * 639 * Cycles through all the entries in the /etc files, stores them 640 * in memory, takes note of device->dname numbers (e.g. rmdisk0, 641 * rmdisk12) 642 * 643 * Cycles through again, adds dargs entry 644 * with the name tname%d (lowest unused number for the device type) 645 * to the list of things for the caller to write out to a file, 646 * IFF it is a new entry. 647 * 648 * It is an error for it to already be there, if it is allocatable. 649 * 650 * Add: 651 * Returns 0 if successful and 2 on error. 652 * Remove: 653 * Returns 0 if not found, 1 if found, 2 on error. 654 */ 655 static int 656 _rebuild_lists(da_args *dargs, strentry_t **head_devallocp, 657 strentry_t **head_devmapp) 658 { 659 int rc = 0; 660 devalloc_t *devallocp; 661 devmap_t *devmapp; 662 strentry_t *tail_str; 663 strentry_t *tmp_str; 664 uint64_t tmp_bitmap = 0; 665 uint_t tmp = 0; 666 char *realname; 667 int suffix; 668 int found = 0; 669 int stdtype = 1; 670 int is_allocatable = 1; 671 char new_devname[DA_MAXNAME + 1]; 672 char defname[DA_MAXNAME + 1]; /* default name for type */ 673 char errmsg[DA_MAXNAME + 1 + (PATH_MAX * 2) + 80]; 674 675 if (dargs->optflag & (DA_MAPS_ONLY | DA_ALLOC_ONLY)) 676 return (2); 677 678 if (dargs->optflag & DA_FORCE) 679 return (2); 680 681 if (dargs->optflag & DA_ADD) { 682 stdtype = da_std_type(dargs, defname); 683 is_allocatable = allocatable(dargs); 684 } 685 686 /* read both files, maps first so we can compare actual devices */ 687 688 /* build device_maps */ 689 setdmapent(); 690 while ((devmapp = getdmapent()) != NULL) { 691 suffix = DA_MAX_DEVNO + 1; 692 if ((rc = dmap_matchtype(devmapp, dargs->devinfo->devtype)) 693 == 1) { 694 if (dargs->optflag & DA_REMOVE) { 695 if ((devmapp->dmap_devarray == NULL) || 696 (devmapp->dmap_devarray[0] == NULL)) { 697 freedmapent(devmapp); 698 enddmapent(); 699 return (2); 700 } 701 realname = dmap_physname(devmapp); 702 if (realname == NULL) { 703 freedmapent(devmapp); 704 enddmapent(); 705 return (2); 706 } 707 if (strstr(realname, dargs->devinfo->devlist) 708 != NULL) { 709 /* if need to free and safe to free */ 710 if (dargs->devinfo->devname != NULL && 711 (dargs->optflag & DA_EVENT) != 0) 712 free(dargs->devinfo->devname); 713 dargs->devinfo->devname = 714 strdup(devmapp->dmap_devname); 715 found = 1; 716 freedmapent(devmapp); 717 continue; /* don't retain */ 718 } 719 } else if (dargs->optflag & DA_ADD) { 720 /* 721 * Need to know which suffixes are in use 722 */ 723 rc = (dmap_exact_dev(devmapp, 724 dargs->devinfo->devlist, &suffix)); 725 726 if (rc == 0) { 727 /* 728 * Same type, different device. Record 729 * device suffix already in use, if 730 * applicable. 731 */ 732 if ((suffix < DA_MAX_DEVNO && 733 suffix != -1) && stdtype) 734 tmp_bitmap |= 735 (uint64_t)(1LL << suffix); 736 } else if ((rc == 1) && !is_allocatable) { 737 rc = 0; 738 } else { 739 /* 740 * Match allocatable on add is an error 741 * or mapping attempt returned error 742 */ 743 (void) snprintf(errmsg, sizeof (errmsg), 744 "Cannot add %s on node %s", 745 dargs->devinfo->devtype, 746 devmapp->dmap_devname); 747 syslog(LOG_ERR, "%s", errmsg); 748 freedmapent(devmapp); 749 enddmapent(); 750 return (2); 751 } 752 } else 753 /* add other transaction types as needed */ 754 return (2); 755 } else if ((dargs->optflag & DA_ADD) && 756 (stdtype || is_allocatable) && 757 dmap_exact_dev(devmapp, dargs->devinfo->devlist, 758 &suffix)) { 759 /* 760 * no dups w/o DA_FORCE, even if type differs, 761 * if there is a chance this operation is 762 * machine-driven. The 5 "standard types" 763 * can be machine-driven adds, and tend to 764 * be allocatable. 765 */ 766 (void) snprintf(errmsg, sizeof (errmsg), 767 "Cannot add %s on node %s type %s", 768 dargs->devinfo->devtype, 769 devmapp->dmap_devname, 770 devmapp->dmap_devtype); 771 syslog(LOG_ERR, "%s", errmsg); 772 freedmapent(devmapp); 773 enddmapent(); 774 return (2); 775 } 776 777 tmp_str = _dmap2strentry(devmapp); 778 if (tmp_str == NULL) { 779 freedmapent(devmapp); 780 enddmapent(); 781 return (2); 782 } 783 /* retaining devmap entry: tmp_str->se_str */ 784 tmp_str->se_next = NULL; 785 if (*head_devmapp == NULL) { 786 *head_devmapp = tail_str = tmp_str; 787 } else { 788 tail_str->se_next = tmp_str; 789 tail_str = tmp_str; 790 } 791 freedmapent(devmapp); 792 } 793 enddmapent(); 794 795 /* 796 * No need to rewrite the files if the item to be removed is not 797 * in the files -- wait for another call on another darg. 798 */ 799 if ((dargs->optflag & DA_REMOVE) && !found) 800 return (0); 801 802 803 if (dargs->optflag & DA_ADD) { 804 int len; 805 /* 806 * If we got here from an event, or from devfsadm, 807 * we know the stored devname is a useless guess, 808 * since the files had not been read when the name 809 * was chosen, and we don't keep them anywhere else 810 * that is sufficiently definitive. 811 */ 812 813 for (tmp = 0; tmp <= DA_MAX_DEVNO; tmp++) 814 if (!(tmp_bitmap & (1LL << tmp))) 815 break; 816 /* Future: support more than 64 hotplug devices per type? */ 817 if (tmp > DA_MAX_DEVNO) 818 return (2); 819 820 /* 821 * Let the caller choose the name unless BOTH the name and 822 * device type one of: cdrom, floppy, audio, rmdisk, or tape. 823 * (or sr, fd for unlabeled) 824 */ 825 len = strlen(defname); 826 if (stdtype && 827 (strncmp(dargs->devinfo->devname, defname, len) == 0)) { 828 (void) snprintf(new_devname, DA_MAXNAME + 1, "%s%u", 829 defname, tmp); 830 /* if need to free and safe to free */ 831 if (dargs->devinfo->devname != NULL && 832 (dargs->optflag & DA_EVENT) != 0) 833 free(dargs->devinfo->devname); 834 dargs->devinfo->devname = strdup(new_devname); 835 } 836 } 837 838 /* 839 * Now adjust devalloc list to match devmaps 840 * Note we now have the correct devname for da_match to use. 841 */ 842 setdaent(); 843 while ((devallocp = getdaent()) != NULL) { 844 rc = da_match(devallocp, dargs); 845 if (rc == 1) { 846 if (dargs->optflag & DA_ADD) { 847 /* logging is on if DA_EVENT is set */ 848 if (dargs->optflag & DA_EVENT) { 849 (void) snprintf(errmsg, sizeof (errmsg), 850 "%s and %s out of sync," 851 "%s only in %s.", 852 DEVALLOC, DEVMAP, 853 devallocp->da_devname, DEVALLOC); 854 syslog(LOG_ERR, "%s", errmsg); 855 } 856 freedaent(devallocp); 857 enddaent(); 858 return (2); 859 } else if (dargs->optflag & DA_REMOVE) { 860 /* make list w/o this entry */ 861 freedaent(devallocp); 862 continue; 863 } 864 } 865 tmp_str = _da2strentry(dargs, devallocp); 866 if (tmp_str == NULL) { 867 freedaent(devallocp); 868 enddaent(); 869 return (2); 870 } 871 /* retaining devalloc entry: tmp_str->se_str */ 872 tmp_str->se_next = NULL; 873 if (*head_devallocp == NULL) { 874 *head_devallocp = tail_str = tmp_str; 875 } else { 876 tail_str->se_next = tmp_str; 877 tail_str = tmp_str; 878 } 879 freedaent(devallocp); 880 } 881 enddaent(); 882 883 /* the caller needs to know if a remove needs to rewrite files */ 884 if (dargs->optflag & DA_REMOVE) 885 return (1); /* 0 and 2 cases returned earlier */ 886 887 return (0); /* Successful DA_ADD */ 888 } 889 890 /* 891 * _build_lists - 892 * Cycles through all the entries, stores them in memory. removes entries 893 * with the given search_key (device name or type). 894 * returns 0 if given entry not found, 1 if given entry removed, 2 on 895 * error. 896 */ 897 static int 898 _build_lists(da_args *dargs, strentry_t **head_devallocp, 899 strentry_t **head_devmapp) 900 { 901 int rc = 0; 902 int found = 0; 903 devalloc_t *devallocp; 904 devmap_t *devmapp; 905 strentry_t *tail_str; 906 strentry_t *tmp_str; 907 908 if (dargs->optflag & DA_MAPS_ONLY) 909 goto dmap_only; 910 911 /* build device_allocate */ 912 setdaent(); 913 while ((devallocp = getdaent()) != NULL) { 914 rc = da_match(devallocp, dargs); 915 /* if in _build_lists and DA_ADD is set, so is DA_FORCE */ 916 if (rc == 0) { 917 tmp_str = _da2strentry(dargs, devallocp); 918 if (tmp_str == NULL) { 919 freedaent(devallocp); 920 enddaent(); 921 return (2); 922 } 923 /* retaining devalloc entry: tmp_str->se_str */ 924 tmp_str->se_next = NULL; 925 if (*head_devallocp == NULL) { 926 *head_devallocp = tail_str = tmp_str; 927 } else { 928 tail_str->se_next = tmp_str; 929 tail_str = tmp_str; 930 } 931 } else if (rc == 1) 932 found = 1; 933 934 freedaent(devallocp); 935 } 936 enddaent(); 937 938 dmap_only: 939 if (dargs->optflag & DA_ALLOC_ONLY) 940 return (rc); 941 942 /* build device_maps */ 943 rc = 0; 944 setdmapent(); 945 while ((devmapp = getdmapent()) != NULL) { 946 rc = dm_match(devmapp, dargs); 947 if (rc == 0) { 948 tmp_str = _dmap2strentry(devmapp); 949 if (tmp_str == NULL) { 950 freedmapent(devmapp); 951 enddmapent(); 952 return (2); 953 } 954 /* retaining devmap entry: tmp_str->se_str */ 955 tmp_str->se_next = NULL; 956 if (*head_devmapp == NULL) { 957 *head_devmapp = tail_str = tmp_str; 958 } else { 959 tail_str->se_next = tmp_str; 960 tail_str = tmp_str; 961 } 962 } 963 freedmapent(devmapp); 964 } 965 enddmapent(); 966 967 /* later code cleanup may cause the use of "found" in other cases */ 968 if (dargs->optflag & DA_REMOVE) 969 return (found); 970 return (rc); 971 } 972 973 /* 974 * _write_defattrs 975 * writes current entries to devalloc_defaults. 976 */ 977 static void 978 _write_defattrs(FILE *fp, strentry_t *head_defent) 979 { 980 strentry_t *tmp_str; 981 982 for (tmp_str = head_defent; tmp_str != NULL; 983 tmp_str = tmp_str->se_next) { 984 (void) fputs(tmp_str->se_str, fp); 985 (void) fputs("\n", fp); 986 } 987 988 } 989 990 /* 991 * _write_device_allocate - 992 * writes current entries in the list to device_allocate. 993 * frees the strings 994 */ 995 static void 996 _write_device_allocate(char *odevalloc, FILE *dafp, strentry_t *head_devallocp) 997 { 998 int is_on = -1; 999 strentry_t *tmp_str, *old_str; 1000 struct stat dastat; 1001 1002 (void) fseek(dafp, (off_t)0, SEEK_SET); 1003 1004 /* 1005 * if the devalloc on/off string existed before, 1006 * put it back before anything else. 1007 * we need to check for the string only if the file 1008 * exists. 1009 */ 1010 if (stat(odevalloc, &dastat) == 0) { 1011 is_on = da_is_on(); 1012 if (is_on == 0) 1013 (void) fputs(DA_OFF_STR, dafp); 1014 else if (is_on == 1) 1015 (void) fputs(DA_ON_STR, dafp); 1016 } 1017 tmp_str = head_devallocp; 1018 while (tmp_str) { 1019 (void) fputs(tmp_str->se_str, dafp); 1020 (void) fputs("\n", dafp); 1021 old_str = tmp_str; 1022 tmp_str = tmp_str->se_next; 1023 free(old_str); 1024 } 1025 } 1026 1027 /* 1028 * _write_device_maps - 1029 * writes current entries in the list to device_maps. 1030 * and frees the strings 1031 */ 1032 static void 1033 _write_device_maps(FILE *dmfp, strentry_t *head_devmapp) 1034 { 1035 strentry_t *tmp_str, *old_str; 1036 1037 (void) fseek(dmfp, (off_t)0, SEEK_SET); 1038 1039 tmp_str = head_devmapp; 1040 while (tmp_str) { 1041 (void) fputs(tmp_str->se_str, dmfp); 1042 (void) fputs("\n", dmfp); 1043 old_str = tmp_str; 1044 tmp_str = tmp_str->se_next; 1045 free(old_str); 1046 } 1047 } 1048 1049 /* 1050 * _write_new_defattrs 1051 * writes the new entry to devalloc_defaults. 1052 * returns 0 on success, -1 on error. 1053 */ 1054 static int 1055 _write_new_defattrs(FILE *fp, da_args *dargs) 1056 { 1057 int count; 1058 char *tok = NULL, *tokp = NULL; 1059 char *lasts; 1060 devinfo_t *devinfo = dargs->devinfo; 1061 1062 if (fseek(fp, (off_t)0, SEEK_END) == (off_t)-1) 1063 return (-1); 1064 if (!devinfo->devopts) 1065 return (0); 1066 (void) fprintf(fp, "%s%s", (devinfo->devtype ? devinfo->devtype : ""), 1067 KV_TOKEN_DELIMIT); 1068 if ((tokp = (char *)malloc(strlen(devinfo->devopts) +1)) != NULL) { 1069 (void) strcpy(tokp, devinfo->devopts); 1070 if ((tok = strtok_r(tokp, KV_DELIMITER, &lasts)) != NULL) { 1071 (void) fprintf(fp, "%s", tok); 1072 count = 1; 1073 } 1074 while ((tok = strtok_r(NULL, KV_DELIMITER, &lasts)) != NULL) { 1075 if (count) 1076 (void) fprintf(fp, "%s", KV_DELIMITER); 1077 (void) fprintf(fp, "%s", tok); 1078 count++; 1079 } 1080 } else { 1081 (void) fprintf(fp, "%s", devinfo->devopts); 1082 } 1083 1084 return (0); 1085 } 1086 1087 /* 1088 * _write_new_entry - 1089 * writes the new devalloc_t to device_allocate or the new devmap_t to 1090 * device_maps. 1091 * returns 0 on success, -1 on error. 1092 */ 1093 static int 1094 _write_new_entry(FILE *fp, da_args *dargs, int flag) 1095 { 1096 int count; 1097 char *tok = NULL, *tokp = NULL; 1098 char *lasts; 1099 devinfo_t *devinfo = dargs->devinfo; 1100 1101 if (flag & DA_MAPS_ONLY) 1102 goto dmap_only; 1103 1104 if (fseek(fp, (off_t)0, SEEK_END) == (off_t)-1) 1105 return (-1); 1106 1107 (void) fprintf(fp, "%s%s\\\n\t", 1108 (devinfo->devname ? devinfo->devname : ""), KV_DELIMITER); 1109 (void) fprintf(fp, "%s%s\\\n\t", 1110 (devinfo->devtype ? devinfo->devtype : ""), KV_DELIMITER); 1111 if (devinfo->devopts == NULL) { 1112 (void) fprintf(fp, "%s%s\\\n\t", DA_RESERVED, 1113 KV_DELIMITER); 1114 } else { 1115 if ((tokp = (char *)malloc(strlen(devinfo->devopts) + 1)) 1116 != NULL) { 1117 (void) strcpy(tokp, devinfo->devopts); 1118 if ((tok = strtok_r(tokp, KV_TOKEN_DELIMIT, &lasts)) != 1119 NULL) { 1120 (void) fprintf(fp, "%s", tok); 1121 count = 1; 1122 } 1123 while ((tok = strtok_r(NULL, KV_TOKEN_DELIMIT, 1124 &lasts)) != NULL) { 1125 if (count) 1126 (void) fprintf(fp, "%s", 1127 KV_TOKEN_DELIMIT "\\\n\t"); 1128 (void) fprintf(fp, "%s", tok); 1129 count++; 1130 } 1131 if (count) 1132 (void) fprintf(fp, "%s", 1133 KV_DELIMITER "\\\n\t"); 1134 } else { 1135 (void) fprintf(fp, "%s%s", devinfo->devopts, 1136 KV_DELIMITER "\\\n\t"); 1137 } 1138 } 1139 (void) fprintf(fp, "%s%s\\\n\t", DA_RESERVED, KV_DELIMITER); 1140 (void) fprintf(fp, "%s%s\\\n\t", 1141 (devinfo->devauths ? devinfo->devauths : DA_ANYUSER), 1142 KV_DELIMITER); 1143 (void) fprintf(fp, "%s\n", 1144 (devinfo->devexec ? devinfo->devexec : KV_DELIMITER)); 1145 1146 dmap_only: 1147 if (flag & DA_ALLOC_ONLY) 1148 return (0); 1149 1150 if (fseek(fp, (off_t)0, SEEK_END) == (off_t)-1) 1151 return (-1); 1152 1153 (void) fprintf(fp, "%s%s\\\n", 1154 (devinfo->devname ? devinfo->devname : ""), KV_TOKEN_DELIMIT); 1155 (void) fprintf(fp, "\t%s%s\\\n", 1156 (devinfo->devtype ? devinfo->devtype : ""), KV_TOKEN_DELIMIT); 1157 (void) fprintf(fp, "\t%s\n", 1158 (devinfo->devlist ? devinfo->devlist : KV_TOKEN_DELIMIT)); 1159 1160 return (0); 1161 } 1162 1163 /* 1164 * _da_lock_devdb - 1165 * locks the database files; lock can be either broken explicitly by 1166 * closing the fd of the lock file, or it expires automatically at process 1167 * termination. 1168 * returns fd of the lock file or -1 on error. 1169 */ 1170 int 1171 _da_lock_devdb(char *rootdir) 1172 { 1173 int lockfd = -1; 1174 int ret; 1175 int count = 0; 1176 int retry = 10; 1177 int retry_sleep; 1178 uint_t seed; 1179 char *lockfile; 1180 char path[MAXPATHLEN]; 1181 int size = sizeof (path); 1182 1183 if (rootdir == NULL) { 1184 lockfile = DA_DB_LOCK; 1185 } else { 1186 path[0] = '\0'; 1187 if (snprintf(path, size, "%s%s", rootdir, DA_DB_LOCK) >= size) 1188 return (-1); 1189 lockfile = path; 1190 } 1191 1192 if ((lockfd = open(lockfile, O_RDWR | O_CREAT, 0600)) == -1) 1193 /* cannot open lock file */ 1194 return (-1); 1195 1196 (void) fchown(lockfd, DA_UID, DA_GID); 1197 1198 if (lseek(lockfd, (off_t)0, SEEK_SET) == -1) { 1199 /* cannot position lock file */ 1200 (void) close(lockfd); 1201 return (-1); 1202 } 1203 errno = 0; 1204 while (retry > 0) { 1205 count++; 1206 seed = (uint_t)gethrtime(); 1207 ret = lockf(lockfd, F_TLOCK, 0); 1208 if (ret == 0) { 1209 (void) utime(lockfile, NULL); 1210 return (lockfd); 1211 } 1212 if ((errno != EACCES) && (errno != EAGAIN)) { 1213 /* cannot set lock */ 1214 (void) close(lockfd); 1215 return (-1); 1216 } 1217 retry--; 1218 retry_sleep = rand_r(&seed)/((RAND_MAX + 2)/3) + count; 1219 (void) sleep(retry_sleep); 1220 errno = 0; 1221 } 1222 1223 return (-1); 1224 } 1225 1226 /* 1227 * da_open_devdb - 1228 * opens one or both database files - device_allocate, device_maps - in 1229 * the specified mode. 1230 * locks the database files; lock is either broken explicitly by the 1231 * caller by closing the lock file fd, or it expires automatically at 1232 * process termination. 1233 * writes the file pointer of opened file in the input args - dafp, dmfp. 1234 * returns fd of the lock file on success, -2 if database file does not 1235 * exist, -1 on other errors. 1236 */ 1237 int 1238 da_open_devdb(char *rootdir, FILE **dafp, FILE **dmfp, int flag) 1239 { 1240 int oflag = 0; 1241 int fda = -1; 1242 int fdm = -1; 1243 int lockfd = -1; 1244 char *fname; 1245 char *fmode; 1246 char path[MAXPATHLEN]; 1247 FILE *devfile; 1248 1249 if ((dafp == NULL) && (dmfp == NULL)) 1250 return (-1); 1251 1252 if (flag & DA_RDWR) { 1253 oflag = DA_RDWR; 1254 fmode = "r+F"; 1255 } else if (flag & DA_RDONLY) { 1256 oflag = DA_RDONLY; 1257 fmode = "rF"; 1258 } 1259 1260 if ((lockfd = _da_lock_devdb(rootdir)) == -1) 1261 return (-1); 1262 1263 if ((dafp == NULL) || (flag & DA_MAPS_ONLY)) 1264 goto dmap_only; 1265 1266 path[0] = '\0'; 1267 1268 /* 1269 * open the device allocation file 1270 */ 1271 if (rootdir == NULL) { 1272 fname = DEVALLOC; 1273 } else { 1274 if (snprintf(path, sizeof (path), "%s%s", rootdir, 1275 DEVALLOC) >= sizeof (path)) { 1276 if (lockfd != -1) 1277 (void) close(lockfd); 1278 return (-1); 1279 } 1280 fname = path; 1281 } 1282 if ((fda = open(fname, oflag, DA_DBMODE)) == -1) { 1283 if (lockfd != -1) 1284 (void) close(lockfd); 1285 return ((errno == ENOENT) ? -2 : -1); 1286 } 1287 if ((devfile = fdopen(fda, fmode)) == NULL) { 1288 (void) close(fda); 1289 if (lockfd != -1) 1290 (void) close(lockfd); 1291 return (-1); 1292 } 1293 *dafp = devfile; 1294 (void) fchmod(fda, DA_DBMODE); 1295 1296 if ((flag & DA_ALLOC_ONLY)) 1297 goto out; 1298 1299 dmap_only: 1300 path[0] = '\0'; 1301 /* 1302 * open the device map file 1303 */ 1304 if (rootdir == NULL) { 1305 fname = DEVMAP; 1306 } else { 1307 if (snprintf(path, sizeof (path), "%s%s", rootdir, 1308 DEVMAP) >= sizeof (path)) { 1309 (void) close(fda); 1310 if (lockfd != -1) 1311 (void) close(lockfd); 1312 return (-1); 1313 } 1314 fname = path; 1315 } 1316 1317 if ((fdm = open(fname, oflag, DA_DBMODE)) == -1) { 1318 if (lockfd != -1) 1319 (void) close(lockfd); 1320 return ((errno == ENOENT) ? -2 : -1); 1321 } 1322 1323 if ((devfile = fdopen(fdm, fmode)) == NULL) { 1324 (void) close(fdm); 1325 (void) close(fda); 1326 if (lockfd != -1) 1327 (void) close(lockfd); 1328 return (-1); 1329 } 1330 *dmfp = devfile; 1331 (void) fchmod(fdm, DA_DBMODE); 1332 1333 out: 1334 return (lockfd); 1335 } 1336 1337 /* 1338 * _record_on_off - 1339 * adds either DA_ON_STR or DA_OFF_STR to device_allocate 1340 * returns 0 on success, -1 on error. 1341 */ 1342 static int 1343 _record_on_off(da_args *dargs, FILE *tafp, FILE *dafp) 1344 { 1345 int dafd; 1346 int nsize; 1347 int nitems = 1; 1348 int actionlen; 1349 int str_found = 0; 1350 int len = 0, nlen = 0, plen = 0; 1351 char *ptr = NULL; 1352 char *actionstr; 1353 char *nbuf = NULL; 1354 char line[MAX_CANON]; 1355 struct stat dastat; 1356 1357 if (dargs->optflag & DA_ON) 1358 actionstr = DA_ON_STR; 1359 else 1360 actionstr = DA_OFF_STR; 1361 actionlen = strlen(actionstr); 1362 dafd = fileno(dafp); 1363 if (fstat(dafd, &dastat) == -1) 1364 return (-1); 1365 1366 /* check the old device_allocate for on/off string */ 1367 ptr = fgets(line, MAX_CANON, dafp); 1368 if (ptr != NULL) { 1369 if ((strcmp(line, DA_ON_STR) == 0) || 1370 (strcmp(line, DA_OFF_STR) == 0)) { 1371 str_found = 1; 1372 nsize = dastat.st_size; 1373 } 1374 } 1375 if (!ptr || !str_found) { 1376 /* 1377 * the file never had either the on or the off string; 1378 * make room for it. 1379 */ 1380 str_found = 0; 1381 nsize = dastat.st_size + actionlen + 1; 1382 } 1383 if ((nbuf = (char *)malloc(nsize + 1)) == NULL) 1384 return (-1); 1385 nbuf[0] = '\0'; 1386 /* put the on/off string */ 1387 (void) strcpy(nbuf, actionstr); 1388 nlen = strlen(nbuf); 1389 plen = nlen; 1390 if (ptr && !str_found) { 1391 /* now put the first line that we read in fgets */ 1392 nlen = plen + strlen(line) + 1; 1393 len = snprintf(nbuf + plen, nlen - plen, "%s", line); 1394 if (len >= nsize) { 1395 free(nbuf); 1396 return (-1); 1397 } 1398 plen += len; 1399 } 1400 1401 /* now get the rest of the old file */ 1402 while (fgets(line, MAX_CANON, dafp) != NULL) { 1403 nlen = plen + strlen(line) + 1; 1404 len = snprintf(nbuf + plen, nlen - plen, "%s", line); 1405 if (len >= nsize) { 1406 free(nbuf); 1407 return (-1); 1408 } 1409 plen += len; 1410 } 1411 len = strlen(nbuf) + 1; 1412 if (len < nsize) 1413 nbuf[len] = '\n'; 1414 1415 /* write the on/off str + the old device_allocate to the temp file */ 1416 if (fwrite(nbuf, nsize, nitems, tafp) < nitems) { 1417 free(nbuf); 1418 return (-1); 1419 } 1420 1421 free(nbuf); 1422 1423 return (0); 1424 } 1425 1426 /* 1427 * da_update_defattrs - 1428 * writes default attributes to devalloc_defaults 1429 * returns 0 on success, -1 on error. 1430 */ 1431 int 1432 da_update_defattrs(da_args *dargs) 1433 { 1434 int rc = 0, lockfd = 0, tmpfd = 0; 1435 char *defpath = DEFATTRS; 1436 char *tmpdefpath = TMPATTRS; 1437 FILE *tmpfp = NULL; 1438 struct stat dstat; 1439 strentry_t *head_defent = NULL; 1440 1441 if (dargs == NULL) 1442 return (0); 1443 if ((lockfd = _da_lock_devdb(NULL)) == -1) 1444 return (-1); 1445 if ((tmpfd = open(tmpdefpath, O_RDWR|O_CREAT, DA_DBMODE)) == -1) { 1446 (void) close(lockfd); 1447 return (-1); 1448 } 1449 (void) fchown(tmpfd, DA_UID, DA_GID); 1450 if ((tmpfp = fdopen(tmpfd, "r+")) == NULL) { 1451 (void) close(tmpfd); 1452 (void) unlink(tmpdefpath); 1453 (void) close(lockfd); 1454 return (-1); 1455 } 1456 /* 1457 * examine all entries, remove an old one if required, check 1458 * if a new one needs to be added. 1459 */ 1460 if (stat(defpath, &dstat) == 0) { 1461 if ((rc = _build_defattrs(dargs, &head_defent)) != 0) { 1462 if (rc == 1) { 1463 (void) close(tmpfd); 1464 (void) unlink(tmpdefpath); 1465 (void) close(lockfd); 1466 return (rc); 1467 } 1468 } 1469 } 1470 /* 1471 * write back any existing entries. 1472 */ 1473 _write_defattrs(tmpfp, head_defent); 1474 1475 if (dargs->optflag & DA_ADD && !(dargs->optflag & DA_NO_OVERRIDE)) { 1476 /* add new entries */ 1477 rc = _write_new_defattrs(tmpfp, dargs); 1478 (void) fclose(tmpfp); 1479 } else { 1480 (void) fclose(tmpfp); 1481 } 1482 if (rename(tmpdefpath, defpath) != 0) { 1483 rc = -1; 1484 (void) unlink(tmpdefpath); 1485 } 1486 (void) close(lockfd); 1487 1488 return (rc); 1489 } 1490 1491 /* 1492 * da_update_device - 1493 * Writes existing entries and the SINGLE change requested by da_args, 1494 * to device_allocate and device_maps. 1495 * Returns 0 on success, -1 on error. 1496 */ 1497 int 1498 da_update_device(da_args *dargs) 1499 { 1500 int rc; 1501 int tafd = -1, tmfd = -1; 1502 int lockfd = -1; 1503 char *rootdir = NULL; 1504 char *apathp = NULL, *mpathp = NULL; 1505 char *dapathp = NULL, *dmpathp = NULL; 1506 char apath[MAXPATHLEN], mpath[MAXPATHLEN]; 1507 char dapath[MAXPATHLEN], dmpath[MAXPATHLEN]; 1508 FILE *tafp = NULL, *tmfp = NULL, *dafp = NULL; 1509 struct stat dastat; 1510 devinfo_t *devinfo; 1511 strentry_t *head_devmapp = NULL; 1512 strentry_t *head_devallocp = NULL; 1513 1514 if (dargs == NULL) 1515 return (0); 1516 1517 rootdir = dargs->rootdir; 1518 devinfo = dargs->devinfo; 1519 1520 /* 1521 * adding/removing entries should be done in both 1522 * device_allocate and device_maps. updates can be 1523 * done in both or either of the files. 1524 */ 1525 if (dargs->optflag & DA_ADD || dargs->optflag & DA_REMOVE) { 1526 if (dargs->optflag & DA_ALLOC_ONLY || 1527 dargs->optflag & DA_MAPS_ONLY) 1528 return (0); 1529 } 1530 1531 /* 1532 * name, type and list are required fields for adding a new 1533 * device. 1534 */ 1535 if ((dargs->optflag & DA_ADD) && 1536 ((devinfo->devname == NULL) || 1537 (devinfo->devtype == NULL) || 1538 (devinfo->devlist == NULL))) { 1539 return (-1); 1540 } 1541 1542 if (rootdir != NULL) { 1543 if (snprintf(apath, sizeof (apath), "%s%s", rootdir, 1544 TMPALLOC) >= sizeof (apath)) 1545 return (-1); 1546 apathp = apath; 1547 if (snprintf(dapath, sizeof (dapath), "%s%s", rootdir, 1548 DEVALLOC) >= sizeof (dapath)) 1549 return (-1); 1550 dapathp = dapath; 1551 if (!(dargs->optflag & DA_ALLOC_ONLY)) { 1552 if (snprintf(mpath, sizeof (mpath), "%s%s", rootdir, 1553 TMPMAP) >= sizeof (mpath)) 1554 return (-1); 1555 mpathp = mpath; 1556 if (snprintf(dmpath, sizeof (dmpath), "%s%s", rootdir, 1557 DEVMAP) >= sizeof (dmpath)) 1558 return (-1); 1559 dmpathp = dmpath; 1560 } 1561 } else { 1562 apathp = TMPALLOC; 1563 dapathp = DEVALLOC; 1564 mpathp = TMPMAP; 1565 dmpathp = DEVMAP; 1566 } 1567 1568 if (dargs->optflag & DA_MAPS_ONLY) 1569 goto dmap_only; 1570 1571 /* 1572 * Check if we are here just to record on/off status of 1573 * device_allocation. 1574 */ 1575 if (dargs->optflag & DA_ON || dargs->optflag & DA_OFF) 1576 lockfd = da_open_devdb(dargs->rootdir, &dafp, NULL, 1577 DA_RDONLY|DA_ALLOC_ONLY); 1578 else 1579 lockfd = _da_lock_devdb(rootdir); 1580 if (lockfd == -1) 1581 return (-1); 1582 1583 if ((tafd = open(apathp, O_RDWR|O_CREAT, DA_DBMODE)) == -1) { 1584 (void) close(lockfd); 1585 (void) fclose(dafp); 1586 return (-1); 1587 } 1588 (void) fchown(tafd, DA_UID, DA_GID); 1589 if ((tafp = fdopen(tafd, "r+")) == NULL) { 1590 (void) close(tafd); 1591 (void) unlink(apathp); 1592 (void) fclose(dafp); 1593 (void) close(lockfd); 1594 return (-1); 1595 } 1596 1597 /* 1598 * We don't need to parse the file if we are here just to record 1599 * on/off status of device_allocation. 1600 */ 1601 if (dargs->optflag & DA_ON || dargs->optflag & DA_OFF) { 1602 if (_record_on_off(dargs, tafp, dafp) == -1) { 1603 (void) close(tafd); 1604 (void) unlink(apathp); 1605 (void) fclose(dafp); 1606 (void) close(lockfd); 1607 return (-1); 1608 } 1609 (void) fclose(dafp); 1610 goto out; 1611 } 1612 1613 /* 1614 * If reacting to a hotplug, read the file entries, 1615 * figure out what dname (tname + a new number) goes to the 1616 * device being added/removed, and create a good head_devallocp and 1617 * head_devmapp with everything good still in it (_rebuild_lists) 1618 * 1619 * Else examine all the entries, remove an old one if it is 1620 * a duplicate with a device being added, returning the 1621 * remaining list (_build_lists.) 1622 * 1623 * We need to do this only if the file exists already. 1624 * 1625 * Once we have built these lists, we need to free the strings 1626 * in the head_* arrays before returning. 1627 */ 1628 if (stat(dapathp, &dastat) == 0) { 1629 /* for device allocation, the /etc files are the "master" */ 1630 if ((dargs->optflag & (DA_ADD| DA_EVENT)) && 1631 (!(dargs->optflag & DA_FORCE))) 1632 rc = _rebuild_lists(dargs, &head_devallocp, 1633 &head_devmapp); 1634 else 1635 rc = _build_lists(dargs, &head_devallocp, 1636 &head_devmapp); 1637 1638 if (rc != 0 && rc != 1) { 1639 (void) close(tafd); 1640 (void) unlink(apathp); 1641 (void) close(lockfd); 1642 return (-1); 1643 } 1644 } else 1645 rc = 0; 1646 1647 if ((dargs->optflag & DA_REMOVE) && (rc == 0)) { 1648 (void) close(tafd); 1649 (void) unlink(apathp); 1650 (void) close(lockfd); 1651 return (0); 1652 } 1653 /* 1654 * TODO: clean up the workings of DA_UPDATE. 1655 * Due to da_match looking at fields that are missing 1656 * in dargs for DA_UPDATE, the da_match call returns no match, 1657 * but due to the way _da2str combines the devalloc_t info with 1658 * the *dargs info, the DA_ADD_ZONE and DA_REMOVE_ZONE work. 1659 * 1660 * This would not scale if any type of update was ever needed 1661 * from the daemon. 1662 */ 1663 1664 /* 1665 * Write out devallocp along with the devalloc on/off string. 1666 */ 1667 _write_device_allocate(dapathp, tafp, head_devallocp); 1668 1669 if (dargs->optflag & DA_ALLOC_ONLY) 1670 goto out; 1671 1672 dmap_only: 1673 if ((tmfd = open(mpathp, O_RDWR|O_CREAT, DA_DBMODE)) == -1) { 1674 (void) close(tafd); 1675 (void) unlink(apathp); 1676 (void) close(lockfd); 1677 return (-1); 1678 } 1679 (void) fchown(tmfd, DA_UID, DA_GID); 1680 if ((tmfp = fdopen(tmfd, "r+")) == NULL) { 1681 (void) close(tafd); 1682 (void) unlink(apathp); 1683 (void) close(tmfd); 1684 (void) unlink(mpathp); 1685 (void) close(lockfd); 1686 return (-1); 1687 } 1688 1689 /* 1690 * Write back any non-removed pre-existing entries. 1691 */ 1692 if (head_devmapp != NULL) 1693 _write_device_maps(tmfp, head_devmapp); 1694 1695 out: 1696 /* 1697 * Add any new entries here. 1698 */ 1699 if (dargs->optflag & DA_ADD && !(dargs->optflag & DA_NO_OVERRIDE)) { 1700 /* add any new entries */ 1701 rc = _write_new_entry(tafp, dargs, DA_ALLOC_ONLY); 1702 (void) fclose(tafp); 1703 1704 if (rc == 0) 1705 rc = _write_new_entry(tmfp, dargs, DA_MAPS_ONLY); 1706 (void) fclose(tmfp); 1707 } else { 1708 if (tafp) 1709 (void) fclose(tafp); 1710 if (tmfp) 1711 (void) fclose(tmfp); 1712 } 1713 1714 rc = 0; 1715 if (!(dargs->optflag & DA_MAPS_ONLY)) { 1716 if (rename(apathp, dapathp) != 0) { 1717 rc = -1; 1718 (void) unlink(apathp); 1719 } 1720 } 1721 if (!(dargs->optflag & DA_ALLOC_ONLY)) { 1722 if (rename(mpathp, dmpathp) != 0) { 1723 rc = -1; 1724 (void) unlink(mpathp); 1725 } 1726 } 1727 1728 (void) close(lockfd); 1729 1730 return (rc); 1731 } 1732 1733 /* 1734 * da_add_list - 1735 * adds new /dev link name to the linked list of devices. 1736 * returns 0 if link added successfully, -1 on error. 1737 */ 1738 int 1739 da_add_list(devlist_t *dlist, char *link, int new_instance, int flag) 1740 { 1741 int instance; 1742 int nlen, plen; 1743 int new_entry = 0; 1744 char *dtype, *dexec, *tname, *kval; 1745 char *minstr = NULL, *maxstr = NULL; 1746 char dname[DA_MAXNAME + 1]; 1747 kva_t *kva; 1748 deventry_t *dentry = NULL, *nentry = NULL, *pentry = NULL; 1749 da_defs_t *da_defs; 1750 1751 if (dlist == NULL || link == NULL) 1752 return (-1); 1753 1754 dname[0] = '\0'; 1755 if (flag & DA_AUDIO) { 1756 dentry = dlist->audio; 1757 tname = DA_AUDIO_NAME; 1758 dtype = DA_AUDIO_TYPE; 1759 dexec = DA_DEFAULT_AUDIO_CLEAN; 1760 } else if (flag & DA_CD) { 1761 dentry = dlist->cd; 1762 tname = DA_CD_NAME; 1763 dtype = DA_CD_TYPE; 1764 dexec = DA_DEFAULT_DISK_CLEAN; 1765 } else if (flag & DA_FLOPPY) { 1766 dentry = dlist->floppy; 1767 tname = DA_FLOPPY_NAME; 1768 dtype = DA_FLOPPY_TYPE; 1769 dexec = DA_DEFAULT_DISK_CLEAN; 1770 } else if (flag & DA_TAPE) { 1771 dentry = dlist->tape; 1772 tname = DA_TAPE_NAME; 1773 dtype = DA_TAPE_TYPE; 1774 dexec = DA_DEFAULT_TAPE_CLEAN; 1775 } else if (flag & DA_RMDISK) { 1776 dentry = dlist->rmdisk; 1777 tname = DA_RMDISK_NAME; 1778 dtype = DA_RMDISK_TYPE; 1779 dexec = DA_DEFAULT_DISK_CLEAN; 1780 } else { 1781 return (-1); 1782 } 1783 1784 for (nentry = dentry; nentry != NULL; nentry = nentry->next) { 1785 pentry = nentry; 1786 (void) sscanf(nentry->devinfo.devname, "%*[a-z]%d", &instance); 1787 if (nentry->devinfo.instance == new_instance) 1788 /* 1789 * Add the new link name to the list of links 1790 * that the device 'dname' has. 1791 */ 1792 break; 1793 } 1794 1795 if (nentry == NULL) { 1796 /* 1797 * Either this is the first entry ever, or no matching entry 1798 * was found. Create a new one and add to the list. 1799 */ 1800 if (dentry == NULL) /* first entry ever */ 1801 instance = 0; 1802 else /* no matching entry */ 1803 instance++; 1804 (void) snprintf(dname, sizeof (dname), "%s%d", tname, instance); 1805 if ((nentry = (deventry_t *)malloc(sizeof (deventry_t))) == 1806 NULL) 1807 return (-1); 1808 if (pentry != NULL) 1809 pentry->next = nentry; 1810 new_entry = 1; 1811 nentry->devinfo.devname = strdup(dname); 1812 nentry->devinfo.devtype = dtype; 1813 nentry->devinfo.devauths = DEFAULT_DEV_ALLOC_AUTH; 1814 nentry->devinfo.devexec = dexec; 1815 nentry->devinfo.instance = new_instance; 1816 /* 1817 * Look for default label range, authorizations and cleaning 1818 * program in devalloc_defaults. If label range is not 1819 * specified in devalloc_defaults, assume it to be admin_low 1820 * to admin_high. 1821 */ 1822 minstr = DA_DEFAULT_MIN; 1823 maxstr = DA_DEFAULT_MAX; 1824 setdadefent(); 1825 if (da_defs = getdadeftype(nentry->devinfo.devtype)) { 1826 kva = da_defs->devopts; 1827 if ((kval = kva_match(kva, DAOPT_MINLABEL)) != NULL) 1828 minstr = strdup(kval); 1829 if ((kval = kva_match(kva, DAOPT_MAXLABEL)) != NULL) 1830 maxstr = strdup(kval); 1831 if ((kval = kva_match(kva, DAOPT_AUTHS)) != NULL) 1832 nentry->devinfo.devauths = strdup(kval); 1833 if ((kval = kva_match(kva, DAOPT_CSCRIPT)) != NULL) 1834 nentry->devinfo.devexec = strdup(kval); 1835 freedadefent(da_defs); 1836 } 1837 enddadefent(); 1838 kval = NULL; 1839 nlen = strlen(DAOPT_MINLABEL) + strlen(KV_ASSIGN) + 1840 strlen(minstr) + strlen(KV_TOKEN_DELIMIT) + 1841 strlen(DAOPT_MAXLABEL) + strlen(KV_ASSIGN) + strlen(maxstr) 1842 + 1; /* +1 for terminator */ 1843 if (kval = (char *)malloc(nlen)) 1844 (void) snprintf(kval, nlen, "%s%s%s%s%s%s%s", 1845 DAOPT_MINLABEL, KV_ASSIGN, minstr, KV_TOKEN_DELIMIT, 1846 DAOPT_MAXLABEL, KV_ASSIGN, maxstr); 1847 nentry->devinfo.devopts = kval; 1848 1849 nentry->devinfo.devlist = NULL; 1850 nentry->next = NULL; 1851 } 1852 1853 nlen = strlen(link) + 1; /* +1 terminator */ 1854 if (nentry->devinfo.devlist) { 1855 plen = strlen(nentry->devinfo.devlist); 1856 nlen = nlen + plen + 1; /* +1 for blank to separate entries */ 1857 } else { 1858 plen = 0; 1859 } 1860 1861 if ((nentry->devinfo.devlist = 1862 (char *)realloc(nentry->devinfo.devlist, nlen)) == NULL) { 1863 if (new_entry) { 1864 free(nentry->devinfo.devname); 1865 free(nentry); 1866 if (pentry != NULL) 1867 pentry->next = NULL; 1868 } 1869 return (-1); 1870 } 1871 1872 if (plen == 0) 1873 (void) snprintf(nentry->devinfo.devlist, nlen, "%s", link); 1874 else 1875 (void) snprintf(nentry->devinfo.devlist + plen, nlen - plen, 1876 " %s", link); 1877 1878 if (pentry == NULL) { 1879 /* 1880 * This is the first entry of this device type. 1881 */ 1882 if (flag & DA_AUDIO) 1883 dlist->audio = nentry; 1884 else if (flag & DA_CD) 1885 dlist->cd = nentry; 1886 else if (flag & DA_FLOPPY) 1887 dlist->floppy = nentry; 1888 else if (flag & DA_TAPE) 1889 dlist->tape = nentry; 1890 else if (flag & DA_RMDISK) 1891 dlist->rmdisk = nentry; 1892 } 1893 1894 return (0); 1895 } 1896 1897 /* 1898 * da_remove_list - 1899 * removes a /dev link name from the linked list of devices. 1900 * returns type of device if link for that device removed 1901 * successfully, else returns -1 on error. 1902 * if all links for a device are removed, stores that device 1903 * name in devname. 1904 */ 1905 int 1906 da_remove_list(devlist_t *dlist, char *link, int type, char *devname, int size) 1907 { 1908 int flag; 1909 int remove_dev = 0; 1910 int nlen, plen, slen; 1911 char *lasts, *lname, *oldlist; 1912 struct stat rmstat; 1913 deventry_t *dentry, *current, *prev; 1914 1915 if (type != NULL) 1916 flag = type; 1917 else if (link == NULL) 1918 return (-1); 1919 else if (strstr(link, DA_AUDIO_NAME) || strstr(link, DA_SOUND_NAME)) 1920 flag = DA_AUDIO; 1921 else if (strstr(link, "dsk") || strstr(link, "rdsk") || 1922 strstr(link, "sr") || strstr(link, "rsr")) 1923 flag = DA_CD; 1924 else if (strstr(link, "fd") || strstr(link, "rfd") || 1925 strstr(link, "diskette") || strstr(link, "rdiskette")) 1926 flag = DA_FLOPPY; 1927 else if (strstr(link, DA_TAPE_NAME)) 1928 flag = DA_TAPE; 1929 else 1930 flag = DA_RMDISK; 1931 1932 switch (type) { 1933 case DA_AUDIO: 1934 dentry = dlist->audio; 1935 break; 1936 case DA_CD: 1937 dentry = dlist->cd; 1938 break; 1939 case DA_FLOPPY: 1940 dentry = dlist->floppy; 1941 break; 1942 case DA_TAPE: 1943 dentry = dlist->tape; 1944 break; 1945 case DA_RMDISK: 1946 dentry = dlist->rmdisk; 1947 break; 1948 default: 1949 return (-1); 1950 } 1951 1952 if ((type != NULL) && (link == NULL)) { 1953 for (current = dentry, prev = dentry; current != NULL; 1954 current = current->next) { 1955 oldlist = strdup(current->devinfo.devlist); 1956 for (lname = strtok_r(oldlist, " ", &lasts); 1957 lname != NULL; 1958 lname = strtok_r(NULL, " ", &lasts)) { 1959 if (stat(lname, &rmstat) != 0) { 1960 remove_dev = 1; 1961 goto remove_dev; 1962 } 1963 } 1964 prev = current; 1965 } 1966 return (-1); 1967 } 1968 1969 for (current = dentry, prev = dentry; current != NULL; 1970 current = current->next) { 1971 plen = strlen(current->devinfo.devlist); 1972 nlen = strlen(link); 1973 if (plen == nlen) { 1974 if (strcmp(current->devinfo.devlist, link) == 0) { 1975 /* last name in the list */ 1976 remove_dev = 1; 1977 break; 1978 } 1979 } 1980 if (strstr(current->devinfo.devlist, link)) { 1981 nlen = plen - nlen + 1; 1982 oldlist = strdup(current->devinfo.devlist); 1983 if ((current->devinfo.devlist = 1984 (char *)realloc(current->devinfo.devlist, 1985 nlen)) == NULL) { 1986 free(oldlist); 1987 return (-1); 1988 } 1989 current->devinfo.devlist[0] = '\0'; 1990 nlen = plen = slen = 0; 1991 for (lname = strtok_r(oldlist, " ", &lasts); 1992 lname != NULL; 1993 lname = strtok_r(NULL, " ", &lasts)) { 1994 if (strcmp(lname, link) == 0) 1995 continue; 1996 nlen = strlen(lname) + plen + 1; 1997 if (plen == 0) { 1998 slen = 1999 snprintf(current->devinfo.devlist, 2000 nlen, "%s", lname); 2001 } else { 2002 slen = 2003 snprintf(current->devinfo.devlist + 2004 plen, nlen - plen, " %s", lname); 2005 } 2006 plen = plen + slen + 1; 2007 } 2008 free(oldlist); 2009 break; 2010 } 2011 prev = current; 2012 } 2013 2014 remove_dev: 2015 if (remove_dev == 1) { 2016 (void) strlcpy(devname, current->devinfo.devname, size); 2017 free(current->devinfo.devname); 2018 free(current->devinfo.devlist); 2019 current->devinfo.devname = current->devinfo.devlist = NULL; 2020 prev->next = current->next; 2021 free(current); 2022 current = NULL; 2023 } 2024 if ((remove_dev == 1) && (prev->devinfo.devname == NULL)) { 2025 if (prev->next) { 2026 /* 2027 * what we removed above was the first entry 2028 * in the list. make the next entry to be the 2029 * first. 2030 */ 2031 current = prev->next; 2032 } else { 2033 /* 2034 * the matching entry was the only entry in the list 2035 * for this type. 2036 */ 2037 current = NULL; 2038 } 2039 if (flag & DA_AUDIO) 2040 dlist->audio = current; 2041 else if (flag & DA_CD) 2042 dlist->cd = current; 2043 else if (flag & DA_FLOPPY) 2044 dlist->floppy = current; 2045 else if (flag & DA_TAPE) 2046 dlist->tape = current; 2047 else if (flag & DA_RMDISK) 2048 dlist->rmdisk = current; 2049 } 2050 2051 return (flag); 2052 } 2053 2054 /* 2055 * da_rm_list_entry - 2056 * 2057 * The adding of devnames to a devlist and the removal of a 2058 * device are not symmetrical -- hot_cleanup gives a /devices 2059 * name which is used to remove the dentry whose links all point to 2060 * that /devices entry. 2061 * 2062 * The link argument is present if available to make debugging 2063 * easier. 2064 * 2065 * da_rm_list_entry removes an entry from the linked list of devices. 2066 * 2067 * Returns 1 if the devname was removed successfully, 2068 * 0 if not found, -1 for error. 2069 */ 2070 /*ARGSUSED*/ 2071 int 2072 da_rm_list_entry(devlist_t *dlist, char *link, int type, char *devname) 2073 { 2074 int retval = 0; 2075 deventry_t **dentry, *current, *prev; 2076 2077 switch (type) { 2078 case DA_AUDIO: 2079 dentry = &(dlist->audio); 2080 break; 2081 case DA_CD: 2082 dentry = &(dlist->cd); 2083 break; 2084 case DA_FLOPPY: 2085 dentry = &(dlist->floppy); 2086 break; 2087 case DA_TAPE: 2088 dentry = &(dlist->tape); 2089 break; 2090 case DA_RMDISK: 2091 dentry = &(dlist->rmdisk); 2092 break; 2093 default: 2094 return (-1); 2095 } 2096 2097 /* Presumably in daemon mode, no need to remove entry, list is empty */ 2098 if (*dentry == (deventry_t *)NULL) 2099 return (0); 2100 2101 prev = NULL; 2102 for (current = *dentry; current != NULL; 2103 prev = current, current = current->next) { 2104 if (strcmp(devname, current->devinfo.devname)) 2105 continue; 2106 retval = 1; 2107 break; 2108 } 2109 if (retval == 0) 2110 return (0); 2111 free(current->devinfo.devname); 2112 if (current->devinfo.devlist != NULL) 2113 free(current->devinfo.devlist); 2114 if (current->devinfo.devopts != NULL) 2115 free(current->devinfo.devopts); 2116 2117 if (prev == NULL) 2118 *dentry = current->next; 2119 else 2120 prev->next = current->next; 2121 2122 free(current); 2123 return (retval); 2124 } 2125 2126 /* 2127 * da_is_on - 2128 * checks if device allocation feature is turned on. 2129 * returns 1 if on, 0 if off, -1 if status string not 2130 * found in device_allocate. 2131 */ 2132 int 2133 da_is_on() 2134 { 2135 return (getdaon()); 2136 } 2137 2138 /* 2139 * da_print_device - 2140 * debug routine to print device entries. 2141 */ 2142 void 2143 da_print_device(int flag, devlist_t *devlist) 2144 { 2145 deventry_t *entry, *dentry; 2146 devinfo_t *devinfo; 2147 2148 if (flag & DA_AUDIO) 2149 dentry = devlist->audio; 2150 else if (flag & DA_CD) 2151 dentry = devlist->cd; 2152 else if (flag & DA_FLOPPY) 2153 dentry = devlist->floppy; 2154 else if (flag & DA_TAPE) 2155 dentry = devlist->tape; 2156 else if (flag & DA_RMDISK) 2157 dentry = devlist->rmdisk; 2158 else 2159 return; 2160 2161 for (entry = dentry; entry != NULL; entry = entry->next) { 2162 devinfo = &(entry->devinfo); 2163 (void) fprintf(stdout, "name: %s\n", devinfo->devname); 2164 (void) fprintf(stdout, "type: %s\n", devinfo->devtype); 2165 (void) fprintf(stdout, "auth: %s\n", devinfo->devauths); 2166 (void) fprintf(stdout, "exec: %s\n", devinfo->devexec); 2167 (void) fprintf(stdout, "list: %s\n\n", devinfo->devlist); 2168 } 2169 } 2170