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.
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.
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]
23 * Copyright (c) 1996, 2010, Oracle and/or its affiliates. All rights reserved.
31 #include <sys/types.h>
39 #include <auth_list.h>
41 #include <bsm/devices.h>
42 #include <bsm/devalloc.h>
44 extern int _readbufline(char *, int, char *, int, int *);
45 extern char *strtok_r(char *, const char *, char **);
46 extern char *_strtok_escape(char *, char *, char **);
47 extern int getdaon(void);
48 extern int da_matchname(devalloc_t
*, char *);
49 extern int da_match(devalloc_t
*, da_args
*);
50 extern int dmap_matchname(devmap_t
*, char *);
51 extern int dm_match(devmap_t
*, da_args
*);
52 extern int dmap_matchtype(devmap_t
*dmap
, char *type
);
53 extern int dmap_matchdev(devmap_t
*dmap
, char *dev
);
54 extern int dmap_exact_dev(devmap_t
*dmap
, char *dev
, int *num
);
55 extern char *dmap_physname(devmap_t
*dmap
);
58 * The following structure is for recording old entries to be retained.
59 * We read the entries from the database into a linked list in memory,
60 * then turn around and write them out again.
62 typedef struct strentry
{
63 struct strentry
*se_next
;
64 char se_str
[4096 + 1];
68 * da_check_longindevperm -
69 * reads /etc/logindevperm and checks if specified device is in the file.
70 * returns 1 if specified device found in /etc/logindevperm, else returns 0
73 da_check_logindevperm(char *devname
)
77 int nlen
, plen
, slen
, lineno
, fsize
;
79 char *field_delims
= " \t\n";
87 * check if /etc/logindevperm exists and get its size
89 if ((fd
= open(LOGINDEVPERM
, O_RDONLY
)) == -1)
91 if (fstat(fd
, &f_stat
) != 0) {
95 fsize
= f_stat
.st_size
;
96 if ((fbuf
= (char *)malloc(fsize
)) == NULL
) {
100 if ((fp
= fdopen(fd
, "rF")) == NULL
) {
107 * read and parse /etc/logindevperm
109 plen
= nlen
= lineno
= 0;
110 while (fgets(line
, MAX_CANON
, fp
) != NULL
) {
112 if ((ptr
= strchr(line
, '#')) != NULL
)
113 *ptr
= '\0'; /* handle comments */
114 if (strtok_r(line
, field_delims
, &lasts
) == NULL
)
115 continue; /* ignore blank lines */
116 if (strtok_r(NULL
, field_delims
, &lasts
) == NULL
)
119 if ((ptr
= strtok_r(NULL
, field_delims
, &lasts
)) == NULL
)
120 /* empty device list */
122 nlen
= strlen(ptr
) + 1; /* +1 terminator */
125 slen
= snprintf(fbuf
, nlen
, "%s", ptr
);
127 slen
= snprintf(fbuf
+ plen
, nlen
- plen
, ":%s", ptr
);
138 * check if devname exists in /etc/logindevperm
140 device
= strtok_r(fbuf
, ":", &lasts
);
141 while (device
!= NULL
) {
143 * device and devname may be one of these types -
150 if (strcmp(device
, devname
) == 0) {
151 /* /dev/xx, /dev/dir/xx */
155 if ((ptr
= strrchr(device
, KV_WILDCHAR
)) != NULL
) {
156 /* all wildcard types */
158 if (strncmp(device
, devname
, strlen(device
)) == 0) {
163 device
= strtok_r(NULL
, ":", &lasts
);
171 * establishes readers/writer lock on fname; reads in the file if its
172 * contents changed since the last time we read it.
173 * returns size of buffer read, or -1 on failure.
176 _da_read_file(char *fname
, char **fbuf
, time_t *ftime
, rwlock_t
*flock
,
187 /* check the size and the time stamp on the file */
188 if (rw_rdlock(flock
) != 0)
190 if (stat(fname
, &f_stat
) != 0) {
191 (void) rw_unlock(flock
);
194 fsize
= f_stat
.st_size
;
195 newtime
= f_stat
.st_mtime
;
196 (void) rw_unlock(flock
);
198 while (newtime
> *ftime
) {
200 * file has been modified since we last read it; or this
202 * read file into the buffer with rw lock.
204 if (rw_wrlock(flock
) != 0)
206 if ((fd
= open(fname
, O_RDONLY
)) == -1) {
207 (void) rw_unlock(flock
);
214 if ((*fbuf
= malloc(fsize
)) == NULL
) {
215 (void) rw_unlock(flock
);
219 if (read(fd
, *fbuf
, fsize
) < fsize
) {
221 (void) rw_unlock(flock
);
225 (void) rw_unlock(flock
);
227 * verify that the file did not change just after we read it.
229 if (rw_rdlock(flock
) != 0) {
234 if (stat(fname
, &f_stat
) != 0) {
236 (void) rw_unlock(flock
);
240 fsize
= f_stat
.st_size
;
241 newtime
= f_stat
.st_mtime
;
242 (void) rw_unlock(flock
);
252 * add/remove current zone's name to the given devalloc_t.
255 _update_zonename(da_args
*dargs
, devalloc_t
*dap
)
258 int oldsize
, newsize
;
259 int has_zonename
= 0;
261 kva_t
*newkva
, *oldkva
;
262 kv_t
*newdata
, *olddata
;
265 devinfo
= dargs
->devinfo
;
266 oldkva
= dap
->da_devopts
;
267 if (oldkva
== NULL
) {
268 if (dargs
->optflag
& DA_REMOVE_ZONE
)
270 if (dargs
->optflag
& DA_ADD_ZONE
) {
271 newkva
= _str2kva(devinfo
->devopts
, KV_ASSIGN
,
274 dap
->da_devopts
= newkva
;
278 newsize
= oldsize
= oldkva
->length
;
279 if (kva_match(oldkva
, DAOPT_ZONE
))
281 if (dargs
->optflag
& DA_ADD_ZONE
) {
282 if ((zonename
= index(devinfo
->devopts
, '=')) == NULL
)
286 (void) _insert2kva(oldkva
, DAOPT_ZONE
, zonename
);
290 } else if (dargs
->optflag
& DA_REMOVE_ZONE
) {
295 * If zone name was the only key/value pair,
296 * put 'reserved' in the empty slot.
299 dap
->da_devopts
= NULL
;
306 newkva
= _new_kva(newsize
);
308 newdata
= newkva
->data
;
309 olddata
= oldkva
->data
;
310 for (i
= 0, j
= 0; i
< oldsize
; i
++) {
311 if ((dargs
->optflag
& DA_REMOVE_ZONE
) &&
312 (strcmp(olddata
[i
].key
, DAOPT_ZONE
) == 0))
314 newdata
[j
].key
= strdup(olddata
[i
].key
);
315 newdata
[j
].value
= strdup(olddata
[i
].value
);
319 if (dargs
->optflag
& DA_ADD_ZONE
) {
320 newdata
[j
].key
= strdup(DAOPT_ZONE
);
321 newdata
[j
].value
= strdup(zonename
);
325 dap
->da_devopts
= newkva
;
330 * converts a device_map entry into a printable string
331 * returns 0 on success, -1 on error.
335 _dmap2str(devmap_t
*dmp
, char *buf
, int size
, const char *sep
)
339 length
= snprintf(buf
, size
, "%s%s", dmp
->dmap_devname
, sep
);
342 length
+= snprintf(buf
+ length
, size
- length
, "%s%s",
343 dmp
->dmap_devtype
, sep
);
346 length
+= snprintf(buf
+ length
, size
- length
, "%s\n",
355 * calls dmap2str to break given devmap_t into printable entry.
356 * returns pointer to decoded entry, NULL on error.
359 _dmap2strentry(devmap_t
*devmapp
)
363 if ((sep
= (strentry_t
*)malloc(sizeof (strentry_t
))) == NULL
)
365 if (_dmap2str(devmapp
, sep
->se_str
, sizeof (sep
->se_str
),
366 KV_TOKEN_DELIMIT
"\\\n\t") != 0) {
375 * removes trailing ':' from buf.
378 fix_optstr(char *buf
)
382 if (p
= rindex(buf
, ':'))
388 * converts a device_allocate entry into a printable string
389 * returns 0 on success, -1 on error.
392 _da2str(da_args
*dargs
, devalloc_t
*dap
, char *buf
, int size
, const char *sep
,
396 int matching_entry
= 0;
399 if (dargs
->optflag
& DA_UPDATE
&&
400 (dargs
->optflag
& DA_ADD_ZONE
||
401 dargs
->optflag
& DA_REMOVE_ZONE
) &&
403 for (dnames
= dargs
->devnames
; *dnames
!= NULL
; dnames
++) {
404 if (da_matchname(dap
, *dnames
)) {
410 length
= snprintf(buf
, size
, "%s%s", dap
->da_devname
, sep
);
413 length
+= snprintf(buf
+ length
, size
- length
, "%s%s",
414 dap
->da_devtype
, sep
);
418 _update_zonename(dargs
, dap
);
419 if ((dap
->da_devopts
== NULL
) || ((dap
->da_devopts
->length
== 1) &&
420 (strcmp(dap
->da_devopts
->data
->key
, DA_RESERVED
) == 0))) {
421 length
+= snprintf(buf
+ length
, size
- length
, "%s%s",
424 if (_kva2str(dap
->da_devopts
, buf
+ length
, size
- length
,
425 KV_ASSIGN
, (char *)osep
) != 0)
427 length
= strlen(buf
);
433 length
+= snprintf(buf
+ length
, size
- length
, "%s%s",
437 length
+= snprintf(buf
+ length
, size
- length
, "%s%s",
438 dap
->da_devauth
? dap
->da_devauth
: DA_ANYUSER
, sep
);
441 length
+= snprintf(buf
+ length
, size
- length
, "%s\n",
442 dap
->da_devexec
? dap
->da_devexec
: "");
451 * calls da2str to break given devalloc_t into printable entry.
452 * returns pointer to decoded entry, NULL on error.
455 _da2strentry(da_args
*dargs
, devalloc_t
*dap
)
459 if ((sep
= (strentry_t
*)malloc(sizeof (strentry_t
))) == NULL
)
461 if (_da2str(dargs
, dap
, sep
->se_str
, sizeof (sep
->se_str
),
462 KV_DELIMITER
"\\\n\t", KV_TOKEN_DELIMIT
"\\\n\t") != 0) {
470 * We have to handle the "standard" types in devlist differently than
471 * other devices, which are not covered by our auto-naming conventions.
473 * buf must be a buffer of size DA_MAX_NAME + 1
476 da_std_type(da_args
*dargs
, char *namebuf
)
478 char *type
= dargs
->devinfo
->devtype
;
479 /* check safely for sizes */
480 if (strcmp(DA_AUDIO_TYPE
, type
) == 0) {
481 (void) strlcpy(namebuf
, DA_AUDIO_NAME
, DA_MAXNAME
);
484 if (strcmp(DA_CD_TYPE
, type
) == 0) {
485 (void) strlcpy(namebuf
, DA_CD_TYPE
, DA_MAXNAME
);
488 if (strcmp(DA_FLOPPY_TYPE
, type
) == 0) {
489 (void) strlcpy(namebuf
, DA_FLOPPY_TYPE
, DA_MAXNAME
);
492 if (strcmp(DA_TAPE_TYPE
, type
) == 0) {
493 (void) strlcpy(namebuf
, DA_TAPE_TYPE
, DA_MAXNAME
);
496 if (strcmp(DA_RMDISK_TYPE
, type
) == 0) {
497 (void) strlcpy(namebuf
, DA_RMDISK_NAME
, DA_MAXNAME
);
505 * allocatable: returns
506 * -1 if no auths field,
507 * 0 if not allocatable (marked '*')
508 * 1 if not marked '*'
511 allocatable(da_args
*dargs
)
514 if (!dargs
->devinfo
->devauths
)
516 if (strcmp("*", dargs
->devinfo
->devauths
) == 0)
524 * If dargs->optflag & DA_EVENT, does not assume the dargs list is
525 * complete or completely believable, since devfsadm caches
526 * ONLY what it has been exposed to via syseventd.
528 * Cycles through all the entries in the /etc files, stores them
529 * in memory, takes note of device->dname numbers (e.g. rmdisk0,
532 * Cycles through again, adds dargs entry
533 * with the name tname%d (lowest unused number for the device type)
534 * to the list of things for the caller to write out to a file,
535 * IFF it is a new entry.
537 * It is an error for it to already be there, if it is allocatable.
540 * Returns 0 if successful and 2 on error.
542 * Returns 0 if not found, 1 if found, 2 on error.
545 _rebuild_lists(da_args
*dargs
, strentry_t
**head_devallocp
,
546 strentry_t
**head_devmapp
)
549 devalloc_t
*devallocp
;
551 strentry_t
*tail_str
;
553 uint64_t tmp_bitmap
= 0;
559 int is_allocatable
= 1;
560 char new_devname
[DA_MAXNAME
+ 1];
561 char defname
[DA_MAXNAME
+ 1]; /* default name for type */
562 char errmsg
[DA_MAXNAME
+ 1 + (PATH_MAX
* 2) + 80];
564 if (dargs
->optflag
& (DA_MAPS_ONLY
| DA_ALLOC_ONLY
))
567 if (dargs
->optflag
& DA_FORCE
)
570 if (dargs
->optflag
& DA_ADD
) {
571 stdtype
= da_std_type(dargs
, defname
);
572 is_allocatable
= allocatable(dargs
);
575 /* read both files, maps first so we can compare actual devices */
577 /* build device_maps */
579 while ((devmapp
= getdmapent()) != NULL
) {
580 suffix
= DA_MAX_DEVNO
+ 1;
581 if ((rc
= dmap_matchtype(devmapp
, dargs
->devinfo
->devtype
))
583 if (dargs
->optflag
& DA_REMOVE
) {
584 if ((devmapp
->dmap_devarray
== NULL
) ||
585 (devmapp
->dmap_devarray
[0] == NULL
)) {
586 freedmapent(devmapp
);
590 realname
= dmap_physname(devmapp
);
591 if (realname
== NULL
) {
592 freedmapent(devmapp
);
596 if (strstr(realname
, dargs
->devinfo
->devlist
)
598 /* if need to free and safe to free */
599 if (dargs
->devinfo
->devname
!= NULL
&&
600 (dargs
->optflag
& DA_EVENT
) != 0)
601 free(dargs
->devinfo
->devname
);
602 dargs
->devinfo
->devname
=
603 strdup(devmapp
->dmap_devname
);
605 freedmapent(devmapp
);
606 continue; /* don't retain */
608 } else if (dargs
->optflag
& DA_ADD
) {
610 * Need to know which suffixes are in use
612 rc
= (dmap_exact_dev(devmapp
,
613 dargs
->devinfo
->devlist
, &suffix
));
617 * Same type, different device. Record
618 * device suffix already in use, if
621 if ((suffix
< DA_MAX_DEVNO
&&
622 suffix
!= -1) && stdtype
)
624 (uint64_t)(1LL << suffix
);
625 } else if ((rc
== 1) && !is_allocatable
) {
629 * Match allocatable on add is an error
630 * or mapping attempt returned error
632 (void) snprintf(errmsg
, sizeof (errmsg
),
633 "Cannot add %s on node %s",
634 dargs
->devinfo
->devtype
,
635 devmapp
->dmap_devname
);
636 syslog(LOG_ERR
, "%s", errmsg
);
637 freedmapent(devmapp
);
642 /* add other transaction types as needed */
644 } else if ((dargs
->optflag
& DA_ADD
) &&
645 (stdtype
|| is_allocatable
) &&
646 dmap_exact_dev(devmapp
, dargs
->devinfo
->devlist
,
649 * no dups w/o DA_FORCE, even if type differs,
650 * if there is a chance this operation is
651 * machine-driven. The 5 "standard types"
652 * can be machine-driven adds, and tend to
655 (void) snprintf(errmsg
, sizeof (errmsg
),
656 "Cannot add %s on node %s type %s",
657 dargs
->devinfo
->devtype
,
658 devmapp
->dmap_devname
,
659 devmapp
->dmap_devtype
);
660 syslog(LOG_ERR
, "%s", errmsg
);
661 freedmapent(devmapp
);
666 tmp_str
= _dmap2strentry(devmapp
);
667 if (tmp_str
== NULL
) {
668 freedmapent(devmapp
);
672 /* retaining devmap entry: tmp_str->se_str */
673 tmp_str
->se_next
= NULL
;
674 if (*head_devmapp
== NULL
) {
675 *head_devmapp
= tail_str
= tmp_str
;
677 tail_str
->se_next
= tmp_str
;
680 freedmapent(devmapp
);
685 * No need to rewrite the files if the item to be removed is not
686 * in the files -- wait for another call on another darg.
688 if ((dargs
->optflag
& DA_REMOVE
) && !found
)
692 if (dargs
->optflag
& DA_ADD
) {
695 * If we got here from an event, or from devfsadm,
696 * we know the stored devname is a useless guess,
697 * since the files had not been read when the name
698 * was chosen, and we don't keep them anywhere else
699 * that is sufficiently definitive.
702 for (tmp
= 0; tmp
<= DA_MAX_DEVNO
; tmp
++)
703 if (!(tmp_bitmap
& (1LL << tmp
)))
705 /* Future: support more than 64 hotplug devices per type? */
706 if (tmp
> DA_MAX_DEVNO
)
710 * Let the caller choose the name unless BOTH the name and
711 * device type one of: cdrom, floppy, audio, rmdisk, tape,
714 len
= strlen(defname
);
716 (strncmp(dargs
->devinfo
->devname
, defname
, len
) == 0)) {
717 (void) snprintf(new_devname
, DA_MAXNAME
+ 1, "%s%u",
719 /* if need to free and safe to free */
720 if (dargs
->devinfo
->devname
!= NULL
&&
721 (dargs
->optflag
& DA_EVENT
) != 0)
722 free(dargs
->devinfo
->devname
);
723 dargs
->devinfo
->devname
= strdup(new_devname
);
728 * Now adjust devalloc list to match devmaps
729 * Note we now have the correct devname for da_match to use.
732 while ((devallocp
= getdaent()) != NULL
) {
733 rc
= da_match(devallocp
, dargs
);
735 if (dargs
->optflag
& DA_ADD
) {
736 /* logging is on if DA_EVENT is set */
737 if (dargs
->optflag
& DA_EVENT
) {
738 (void) snprintf(errmsg
, sizeof (errmsg
),
739 "%s and %s out of sync,"
742 devallocp
->da_devname
, DEVALLOC
);
743 syslog(LOG_ERR
, "%s", errmsg
);
745 freedaent(devallocp
);
748 } else if (dargs
->optflag
& DA_REMOVE
) {
749 /* make list w/o this entry */
750 freedaent(devallocp
);
754 tmp_str
= _da2strentry(dargs
, devallocp
);
755 if (tmp_str
== NULL
) {
756 freedaent(devallocp
);
760 /* retaining devalloc entry: tmp_str->se_str */
761 tmp_str
->se_next
= NULL
;
762 if (*head_devallocp
== NULL
) {
763 *head_devallocp
= tail_str
= tmp_str
;
765 tail_str
->se_next
= tmp_str
;
768 freedaent(devallocp
);
772 /* the caller needs to know if a remove needs to rewrite files */
773 if (dargs
->optflag
& DA_REMOVE
)
774 return (1); /* 0 and 2 cases returned earlier */
776 return (0); /* Successful DA_ADD */
781 * Cycles through all the entries, stores them in memory. removes entries
782 * with the given search_key (device name or type).
783 * returns 0 if given entry not found, 1 if given entry removed, 2 on
787 _build_lists(da_args
*dargs
, strentry_t
**head_devallocp
,
788 strentry_t
**head_devmapp
)
792 devalloc_t
*devallocp
;
794 strentry_t
*tail_str
;
797 if (dargs
->optflag
& DA_MAPS_ONLY
)
800 /* build device_allocate */
802 while ((devallocp
= getdaent()) != NULL
) {
803 rc
= da_match(devallocp
, dargs
);
804 /* if in _build_lists and DA_ADD is set, so is DA_FORCE */
806 tmp_str
= _da2strentry(dargs
, devallocp
);
807 if (tmp_str
== NULL
) {
808 freedaent(devallocp
);
812 /* retaining devalloc entry: tmp_str->se_str */
813 tmp_str
->se_next
= NULL
;
814 if (*head_devallocp
== NULL
) {
815 *head_devallocp
= tail_str
= tmp_str
;
817 tail_str
->se_next
= tmp_str
;
823 freedaent(devallocp
);
828 if (dargs
->optflag
& DA_ALLOC_ONLY
)
831 /* build device_maps */
834 while ((devmapp
= getdmapent()) != NULL
) {
835 rc
= dm_match(devmapp
, dargs
);
837 tmp_str
= _dmap2strentry(devmapp
);
838 if (tmp_str
== NULL
) {
839 freedmapent(devmapp
);
843 /* retaining devmap entry: tmp_str->se_str */
844 tmp_str
->se_next
= NULL
;
845 if (*head_devmapp
== NULL
) {
846 *head_devmapp
= tail_str
= tmp_str
;
848 tail_str
->se_next
= tmp_str
;
852 freedmapent(devmapp
);
856 /* later code cleanup may cause the use of "found" in other cases */
857 if (dargs
->optflag
& DA_REMOVE
)
863 * _write_device_allocate -
864 * writes current entries in the list to device_allocate.
868 _write_device_allocate(char *odevalloc
, FILE *dafp
, strentry_t
*head_devallocp
)
871 strentry_t
*tmp_str
, *old_str
;
874 (void) fseek(dafp
, (off_t
)0, SEEK_SET
);
877 * if the devalloc on/off string existed before,
878 * put it back before anything else.
879 * we need to check for the string only if the file
882 if (stat(odevalloc
, &dastat
) == 0) {
885 (void) fputs(DA_OFF_STR
, dafp
);
887 (void) fputs(DA_ON_STR
, dafp
);
889 tmp_str
= head_devallocp
;
891 (void) fputs(tmp_str
->se_str
, dafp
);
892 (void) fputs("\n", dafp
);
894 tmp_str
= tmp_str
->se_next
;
900 * _write_device_maps -
901 * writes current entries in the list to device_maps.
902 * and frees the strings
905 _write_device_maps(FILE *dmfp
, strentry_t
*head_devmapp
)
907 strentry_t
*tmp_str
, *old_str
;
909 (void) fseek(dmfp
, (off_t
)0, SEEK_SET
);
911 tmp_str
= head_devmapp
;
913 (void) fputs(tmp_str
->se_str
, dmfp
);
914 (void) fputs("\n", dmfp
);
916 tmp_str
= tmp_str
->se_next
;
923 * writes the new devalloc_t to device_allocate or the new devmap_t to
925 * returns 0 on success, -1 on error.
928 _write_new_entry(FILE *fp
, da_args
*dargs
, int flag
)
931 char *tok
= NULL
, *tokp
= NULL
;
933 devinfo_t
*devinfo
= dargs
->devinfo
;
935 if (flag
& DA_MAPS_ONLY
)
938 if (fseek(fp
, (off_t
)0, SEEK_END
) == (off_t
)-1)
941 (void) fprintf(fp
, "%s%s\\\n\t",
942 (devinfo
->devname
? devinfo
->devname
: ""), KV_DELIMITER
);
943 (void) fprintf(fp
, "%s%s\\\n\t",
944 (devinfo
->devtype
? devinfo
->devtype
: ""), KV_DELIMITER
);
945 if (devinfo
->devopts
== NULL
) {
946 (void) fprintf(fp
, "%s%s\\\n\t", DA_RESERVED
,
949 if ((tokp
= (char *)malloc(strlen(devinfo
->devopts
) + 1))
951 (void) strcpy(tokp
, devinfo
->devopts
);
952 if ((tok
= strtok_r(tokp
, KV_TOKEN_DELIMIT
, &lasts
)) !=
954 (void) fprintf(fp
, "%s", tok
);
957 while ((tok
= strtok_r(NULL
, KV_TOKEN_DELIMIT
,
960 (void) fprintf(fp
, "%s",
961 KV_TOKEN_DELIMIT
"\\\n\t");
962 (void) fprintf(fp
, "%s", tok
);
966 (void) fprintf(fp
, "%s",
967 KV_DELIMITER
"\\\n\t");
969 (void) fprintf(fp
, "%s%s", devinfo
->devopts
,
970 KV_DELIMITER
"\\\n\t");
973 (void) fprintf(fp
, "%s%s\\\n\t", DA_RESERVED
, KV_DELIMITER
);
974 (void) fprintf(fp
, "%s%s\\\n\t",
975 (devinfo
->devauths
? devinfo
->devauths
: DA_ANYUSER
),
977 (void) fprintf(fp
, "%s\n",
978 (devinfo
->devexec
? devinfo
->devexec
: KV_DELIMITER
));
981 if (flag
& DA_ALLOC_ONLY
)
984 if (fseek(fp
, (off_t
)0, SEEK_END
) == (off_t
)-1)
987 (void) fprintf(fp
, "%s%s\\\n",
988 (devinfo
->devname
? devinfo
->devname
: ""), KV_TOKEN_DELIMIT
);
989 (void) fprintf(fp
, "\t%s%s\\\n",
990 (devinfo
->devtype
? devinfo
->devtype
: ""), KV_TOKEN_DELIMIT
);
991 (void) fprintf(fp
, "\t%s\n",
992 (devinfo
->devlist
? devinfo
->devlist
: KV_TOKEN_DELIMIT
));
999 * locks the database files; lock can be either broken explicitly by
1000 * closing the fd of the lock file, or it expires automatically at process
1002 * returns fd of the lock file or -1 on error.
1005 _da_lock_devdb(char *rootdir
)
1014 char path
[MAXPATHLEN
];
1015 int size
= sizeof (path
);
1017 if (rootdir
== NULL
) {
1018 lockfile
= DA_DB_LOCK
;
1021 if (snprintf(path
, size
, "%s%s", rootdir
, DA_DB_LOCK
) >= size
)
1026 if ((lockfd
= open(lockfile
, O_RDWR
| O_CREAT
, 0600)) == -1)
1027 /* cannot open lock file */
1030 (void) fchown(lockfd
, DA_UID
, DA_GID
);
1032 if (lseek(lockfd
, (off_t
)0, SEEK_SET
) == -1) {
1033 /* cannot position lock file */
1034 (void) close(lockfd
);
1040 seed
= (uint_t
)gethrtime();
1041 ret
= lockf(lockfd
, F_TLOCK
, 0);
1043 (void) utime(lockfile
, NULL
);
1046 if ((errno
!= EACCES
) && (errno
!= EAGAIN
)) {
1047 /* cannot set lock */
1048 (void) close(lockfd
);
1052 retry_sleep
= rand_r(&seed
)/((RAND_MAX
+ 2)/3) + count
;
1053 (void) sleep(retry_sleep
);
1062 * opens one or both database files - device_allocate, device_maps - in
1063 * the specified mode.
1064 * locks the database files; lock is either broken explicitly by the
1065 * caller by closing the lock file fd, or it expires automatically at
1066 * process termination.
1067 * writes the file pointer of opened file in the input args - dafp, dmfp.
1068 * returns fd of the lock file on success, -2 if database file does not
1069 * exist, -1 on other errors.
1072 da_open_devdb(char *rootdir
, FILE **dafp
, FILE **dmfp
, int flag
)
1080 char path
[MAXPATHLEN
];
1083 if ((dafp
== NULL
) && (dmfp
== NULL
))
1086 if (flag
& DA_RDWR
) {
1089 } else if (flag
& DA_RDONLY
) {
1094 if ((lockfd
= _da_lock_devdb(rootdir
)) == -1)
1097 if ((dafp
== NULL
) || (flag
& DA_MAPS_ONLY
))
1103 * open the device allocation file
1105 if (rootdir
== NULL
) {
1108 if (snprintf(path
, sizeof (path
), "%s%s", rootdir
,
1109 DEVALLOC
) >= sizeof (path
)) {
1111 (void) close(lockfd
);
1116 if ((fda
= open(fname
, oflag
, DA_DBMODE
)) == -1) {
1118 (void) close(lockfd
);
1119 return ((errno
== ENOENT
) ? -2 : -1);
1121 if ((devfile
= fdopen(fda
, fmode
)) == NULL
) {
1124 (void) close(lockfd
);
1128 (void) fchmod(fda
, DA_DBMODE
);
1130 if ((flag
& DA_ALLOC_ONLY
))
1136 * open the device map file
1138 if (rootdir
== NULL
) {
1141 if (snprintf(path
, sizeof (path
), "%s%s", rootdir
,
1142 DEVMAP
) >= sizeof (path
)) {
1145 (void) close(lockfd
);
1151 if ((fdm
= open(fname
, oflag
, DA_DBMODE
)) == -1) {
1153 (void) close(lockfd
);
1154 return ((errno
== ENOENT
) ? -2 : -1);
1157 if ((devfile
= fdopen(fdm
, fmode
)) == NULL
) {
1161 (void) close(lockfd
);
1165 (void) fchmod(fdm
, DA_DBMODE
);
1173 * adds either DA_ON_STR or DA_OFF_STR to device_allocate
1174 * returns 0 on success, -1 on error.
1177 _record_on_off(da_args
*dargs
, FILE *tafp
, FILE *dafp
)
1184 int len
= 0, nlen
= 0, plen
= 0;
1188 char line
[MAX_CANON
];
1191 if (dargs
->optflag
& DA_ON
)
1192 actionstr
= DA_ON_STR
;
1194 actionstr
= DA_OFF_STR
;
1195 actionlen
= strlen(actionstr
);
1196 dafd
= fileno(dafp
);
1197 if (fstat(dafd
, &dastat
) == -1)
1200 /* check the old device_allocate for on/off string */
1201 ptr
= fgets(line
, MAX_CANON
, dafp
);
1203 if ((strcmp(line
, DA_ON_STR
) == 0) ||
1204 (strcmp(line
, DA_OFF_STR
) == 0)) {
1206 nsize
= dastat
.st_size
;
1209 if (!ptr
|| !str_found
) {
1211 * the file never had either the on or the off string;
1215 nsize
= dastat
.st_size
+ actionlen
+ 1;
1217 if ((nbuf
= (char *)malloc(nsize
+ 1)) == NULL
)
1220 /* put the on/off string */
1221 (void) strcpy(nbuf
, actionstr
);
1222 nlen
= strlen(nbuf
);
1224 if (ptr
&& !str_found
) {
1225 /* now put the first line that we read in fgets */
1226 nlen
= plen
+ strlen(line
) + 1;
1227 len
= snprintf(nbuf
+ plen
, nlen
- plen
, "%s", line
);
1235 /* now get the rest of the old file */
1236 while (fgets(line
, MAX_CANON
, dafp
) != NULL
) {
1237 nlen
= plen
+ strlen(line
) + 1;
1238 len
= snprintf(nbuf
+ plen
, nlen
- plen
, "%s", line
);
1245 len
= strlen(nbuf
) + 1;
1249 /* write the on/off str + the old device_allocate to the temp file */
1250 if (fwrite(nbuf
, nsize
, nitems
, tafp
) < nitems
) {
1261 * da_update_device -
1262 * Writes existing entries and the SINGLE change requested by da_args,
1263 * to device_allocate and device_maps.
1264 * Returns 0 on success, -1 on error.
1267 da_update_device(da_args
*dargs
)
1270 int tafd
= -1, tmfd
= -1;
1272 char *rootdir
= NULL
;
1273 char *apathp
= NULL
, *mpathp
= NULL
;
1274 char *dapathp
= NULL
, *dmpathp
= NULL
;
1275 char apath
[MAXPATHLEN
], mpath
[MAXPATHLEN
];
1276 char dapath
[MAXPATHLEN
], dmpath
[MAXPATHLEN
];
1277 FILE *tafp
= NULL
, *tmfp
= NULL
, *dafp
= NULL
;
1280 strentry_t
*head_devmapp
= NULL
;
1281 strentry_t
*head_devallocp
= NULL
;
1286 rootdir
= dargs
->rootdir
;
1287 devinfo
= dargs
->devinfo
;
1290 * adding/removing entries should be done in both
1291 * device_allocate and device_maps. updates can be
1292 * done in both or either of the files.
1294 if (dargs
->optflag
& DA_ADD
|| dargs
->optflag
& DA_REMOVE
) {
1295 if (dargs
->optflag
& DA_ALLOC_ONLY
||
1296 dargs
->optflag
& DA_MAPS_ONLY
)
1301 * name, type and list are required fields for adding a new
1304 if ((dargs
->optflag
& DA_ADD
) &&
1305 ((devinfo
->devname
== NULL
) ||
1306 (devinfo
->devtype
== NULL
) ||
1307 (devinfo
->devlist
== NULL
))) {
1311 if (rootdir
!= NULL
) {
1312 if (snprintf(apath
, sizeof (apath
), "%s%s", rootdir
,
1313 TMPALLOC
) >= sizeof (apath
))
1316 if (snprintf(dapath
, sizeof (dapath
), "%s%s", rootdir
,
1317 DEVALLOC
) >= sizeof (dapath
))
1320 if (!(dargs
->optflag
& DA_ALLOC_ONLY
)) {
1321 if (snprintf(mpath
, sizeof (mpath
), "%s%s", rootdir
,
1322 TMPMAP
) >= sizeof (mpath
))
1325 if (snprintf(dmpath
, sizeof (dmpath
), "%s%s", rootdir
,
1326 DEVMAP
) >= sizeof (dmpath
))
1337 if (dargs
->optflag
& DA_MAPS_ONLY
)
1341 * Check if we are here just to record on/off status of
1342 * device_allocation.
1344 if (dargs
->optflag
& DA_ON
|| dargs
->optflag
& DA_OFF
)
1345 lockfd
= da_open_devdb(dargs
->rootdir
, &dafp
, NULL
,
1346 DA_RDONLY
|DA_ALLOC_ONLY
);
1348 lockfd
= _da_lock_devdb(rootdir
);
1352 if ((tafd
= open(apathp
, O_RDWR
|O_CREAT
, DA_DBMODE
)) == -1) {
1353 (void) close(lockfd
);
1354 (void) fclose(dafp
);
1357 (void) fchown(tafd
, DA_UID
, DA_GID
);
1358 if ((tafp
= fdopen(tafd
, "r+")) == NULL
) {
1360 (void) unlink(apathp
);
1361 (void) fclose(dafp
);
1362 (void) close(lockfd
);
1367 * We don't need to parse the file if we are here just to record
1368 * on/off status of device_allocation.
1370 if (dargs
->optflag
& DA_ON
|| dargs
->optflag
& DA_OFF
) {
1371 if (_record_on_off(dargs
, tafp
, dafp
) == -1) {
1373 (void) unlink(apathp
);
1374 (void) fclose(dafp
);
1375 (void) close(lockfd
);
1378 (void) fclose(dafp
);
1383 * If reacting to a hotplug, read the file entries,
1384 * figure out what dname (tname + a new number) goes to the
1385 * device being added/removed, and create a good head_devallocp and
1386 * head_devmapp with everything good still in it (_rebuild_lists)
1388 * Else examine all the entries, remove an old one if it is
1389 * a duplicate with a device being added, returning the
1390 * remaining list (_build_lists.)
1392 * We need to do this only if the file exists already.
1394 * Once we have built these lists, we need to free the strings
1395 * in the head_* arrays before returning.
1397 if (stat(dapathp
, &dastat
) == 0) {
1398 /* for device allocation, the /etc files are the "master" */
1399 if ((dargs
->optflag
& (DA_ADD
| DA_EVENT
)) &&
1400 (!(dargs
->optflag
& DA_FORCE
)))
1401 rc
= _rebuild_lists(dargs
, &head_devallocp
,
1404 rc
= _build_lists(dargs
, &head_devallocp
,
1407 if (rc
!= 0 && rc
!= 1) {
1409 (void) unlink(apathp
);
1410 (void) close(lockfd
);
1416 if ((dargs
->optflag
& DA_REMOVE
) && (rc
== 0)) {
1418 (void) unlink(apathp
);
1419 (void) close(lockfd
);
1423 * TODO: clean up the workings of DA_UPDATE.
1424 * Due to da_match looking at fields that are missing
1425 * in dargs for DA_UPDATE, the da_match call returns no match,
1426 * but due to the way _da2str combines the devalloc_t info with
1427 * the *dargs info, the DA_ADD_ZONE and DA_REMOVE_ZONE work.
1429 * This would not scale if any type of update was ever needed
1434 * Write out devallocp along with the devalloc on/off string.
1436 _write_device_allocate(dapathp
, tafp
, head_devallocp
);
1438 if (dargs
->optflag
& DA_ALLOC_ONLY
)
1442 if ((tmfd
= open(mpathp
, O_RDWR
|O_CREAT
, DA_DBMODE
)) == -1) {
1444 (void) unlink(apathp
);
1445 (void) close(lockfd
);
1448 (void) fchown(tmfd
, DA_UID
, DA_GID
);
1449 if ((tmfp
= fdopen(tmfd
, "r+")) == NULL
) {
1451 (void) unlink(apathp
);
1453 (void) unlink(mpathp
);
1454 (void) close(lockfd
);
1459 * Write back any non-removed pre-existing entries.
1461 if (head_devmapp
!= NULL
)
1462 _write_device_maps(tmfp
, head_devmapp
);
1466 * Add any new entries here.
1468 if (dargs
->optflag
& DA_ADD
&& !(dargs
->optflag
& DA_NO_OVERRIDE
)) {
1469 /* add any new entries */
1470 rc
= _write_new_entry(tafp
, dargs
, DA_ALLOC_ONLY
);
1471 (void) fclose(tafp
);
1474 rc
= _write_new_entry(tmfp
, dargs
, DA_MAPS_ONLY
);
1475 (void) fclose(tmfp
);
1478 (void) fclose(tafp
);
1480 (void) fclose(tmfp
);
1484 if (!(dargs
->optflag
& DA_MAPS_ONLY
)) {
1485 if (rename(apathp
, dapathp
) != 0) {
1487 (void) unlink(apathp
);
1490 if (!(dargs
->optflag
& DA_ALLOC_ONLY
)) {
1491 if (rename(mpathp
, dmpathp
) != 0) {
1493 (void) unlink(mpathp
);
1497 (void) close(lockfd
);
1504 * adds new /dev link name to the linked list of devices.
1505 * returns 0 if link added successfully, -1 on error.
1508 da_add_list(devlist_t
*dlist
, char *link
, int new_instance
, int flag
)
1513 char *dtype
, *dexec
, *tname
, *kval
;
1514 char dname
[DA_MAXNAME
+ 1];
1515 deventry_t
*dentry
= NULL
, *nentry
= NULL
, *pentry
= NULL
;
1517 if (dlist
== NULL
|| link
== NULL
)
1521 if (flag
& DA_AUDIO
) {
1522 dentry
= dlist
->audio
;
1523 tname
= DA_AUDIO_NAME
;
1524 dtype
= DA_AUDIO_TYPE
;
1525 dexec
= DA_DEFAULT_AUDIO_CLEAN
;
1526 } else if (flag
& DA_CD
) {
1530 dexec
= DA_DEFAULT_DISK_CLEAN
;
1531 } else if (flag
& DA_FLOPPY
) {
1532 dentry
= dlist
->floppy
;
1533 tname
= DA_FLOPPY_NAME
;
1534 dtype
= DA_FLOPPY_TYPE
;
1535 dexec
= DA_DEFAULT_DISK_CLEAN
;
1536 } else if (flag
& DA_TAPE
) {
1537 dentry
= dlist
->tape
;
1538 tname
= DA_TAPE_NAME
;
1539 dtype
= DA_TAPE_TYPE
;
1540 dexec
= DA_DEFAULT_TAPE_CLEAN
;
1541 } else if (flag
& DA_RMDISK
) {
1542 dentry
= dlist
->rmdisk
;
1543 tname
= DA_RMDISK_NAME
;
1544 dtype
= DA_RMDISK_TYPE
;
1545 dexec
= DA_DEFAULT_DISK_CLEAN
;
1550 for (nentry
= dentry
; nentry
!= NULL
; nentry
= nentry
->next
) {
1552 (void) sscanf(nentry
->devinfo
.devname
, "%*[a-z]%d", &instance
);
1553 if (nentry
->devinfo
.instance
== new_instance
)
1555 * Add the new link name to the list of links
1556 * that the device 'dname' has.
1561 if (nentry
== NULL
) {
1563 * Either this is the first entry ever, or no matching entry
1564 * was found. Create a new one and add to the list.
1566 if (dentry
== NULL
) /* first entry ever */
1568 else /* no matching entry */
1570 (void) snprintf(dname
, sizeof (dname
), "%s%d", tname
, instance
);
1571 if ((nentry
= (deventry_t
*)malloc(sizeof (deventry_t
))) ==
1575 pentry
->next
= nentry
;
1577 nentry
->devinfo
.devname
= strdup(dname
);
1578 nentry
->devinfo
.devtype
= dtype
;
1579 nentry
->devinfo
.devauths
= DEFAULT_DEV_ALLOC_AUTH
;
1580 nentry
->devinfo
.devexec
= dexec
;
1581 nentry
->devinfo
.instance
= new_instance
;
1583 nlen
= strlen(KV_ASSIGN
) + strlen(KV_TOKEN_DELIMIT
) +
1585 if (kval
= (char *)malloc(nlen
))
1586 (void) snprintf(kval
, nlen
, "%s%s%s", KV_ASSIGN
,
1587 KV_TOKEN_DELIMIT
, KV_ASSIGN
);
1588 nentry
->devinfo
.devopts
= kval
;
1590 nentry
->devinfo
.devlist
= NULL
;
1591 nentry
->next
= NULL
;
1594 nlen
= strlen(link
) + 1; /* +1 terminator */
1595 if (nentry
->devinfo
.devlist
) {
1596 plen
= strlen(nentry
->devinfo
.devlist
);
1597 nlen
= nlen
+ plen
+ 1; /* +1 for blank to separate entries */
1602 if ((nentry
->devinfo
.devlist
=
1603 (char *)realloc(nentry
->devinfo
.devlist
, nlen
)) == NULL
) {
1605 free(nentry
->devinfo
.devname
);
1608 pentry
->next
= NULL
;
1614 (void) snprintf(nentry
->devinfo
.devlist
, nlen
, "%s", link
);
1616 (void) snprintf(nentry
->devinfo
.devlist
+ plen
, nlen
- plen
,
1619 if (pentry
== NULL
) {
1621 * This is the first entry of this device type.
1623 if (flag
& DA_AUDIO
)
1624 dlist
->audio
= nentry
;
1625 else if (flag
& DA_CD
)
1627 else if (flag
& DA_FLOPPY
)
1628 dlist
->floppy
= nentry
;
1629 else if (flag
& DA_TAPE
)
1630 dlist
->tape
= nentry
;
1631 else if (flag
& DA_RMDISK
)
1632 dlist
->rmdisk
= nentry
;
1640 * removes a /dev link name from the linked list of devices.
1641 * returns type of device if link for that device removed
1642 * successfully, else returns -1 on error.
1643 * if all links for a device are removed, stores that device
1647 da_remove_list(devlist_t
*dlist
, char *link
, int type
, char *devname
, int size
)
1651 int nlen
, plen
, slen
;
1652 char *lasts
, *lname
, *oldlist
;
1654 deventry_t
*dentry
, *current
, *prev
;
1658 else if (link
== NULL
)
1660 else if (strstr(link
, DA_AUDIO_NAME
) || strstr(link
, DA_SOUND_NAME
))
1662 else if (strstr(link
, "dsk") || strstr(link
, "rdsk") ||
1663 strstr(link
, "sr") || strstr(link
, "rsr"))
1665 else if (strstr(link
, "fd") || strstr(link
, "rfd") ||
1666 strstr(link
, "diskette") || strstr(link
, "rdiskette"))
1668 else if (strstr(link
, DA_TAPE_NAME
))
1675 dentry
= dlist
->audio
;
1681 dentry
= dlist
->floppy
;
1684 dentry
= dlist
->tape
;
1687 dentry
= dlist
->rmdisk
;
1693 if ((type
!= 0) && (link
== NULL
)) {
1694 for (current
= dentry
, prev
= dentry
; current
!= NULL
;
1695 current
= current
->next
) {
1696 oldlist
= strdup(current
->devinfo
.devlist
);
1697 for (lname
= strtok_r(oldlist
, " ", &lasts
);
1699 lname
= strtok_r(NULL
, " ", &lasts
)) {
1700 if (stat(lname
, &rmstat
) != 0) {
1710 for (current
= dentry
, prev
= dentry
; current
!= NULL
;
1711 current
= current
->next
) {
1712 plen
= strlen(current
->devinfo
.devlist
);
1713 nlen
= strlen(link
);
1715 if (strcmp(current
->devinfo
.devlist
, link
) == 0) {
1716 /* last name in the list */
1721 if (strstr(current
->devinfo
.devlist
, link
)) {
1722 nlen
= plen
- nlen
+ 1;
1723 oldlist
= strdup(current
->devinfo
.devlist
);
1724 if ((current
->devinfo
.devlist
=
1725 (char *)realloc(current
->devinfo
.devlist
,
1730 current
->devinfo
.devlist
[0] = '\0';
1731 nlen
= plen
= slen
= 0;
1732 for (lname
= strtok_r(oldlist
, " ", &lasts
);
1734 lname
= strtok_r(NULL
, " ", &lasts
)) {
1735 if (strcmp(lname
, link
) == 0)
1737 nlen
= strlen(lname
) + plen
+ 1;
1740 snprintf(current
->devinfo
.devlist
,
1744 snprintf(current
->devinfo
.devlist
+
1745 plen
, nlen
- plen
, " %s", lname
);
1747 plen
= plen
+ slen
+ 1;
1756 if (remove_dev
== 1) {
1757 (void) strlcpy(devname
, current
->devinfo
.devname
, size
);
1758 free(current
->devinfo
.devname
);
1759 free(current
->devinfo
.devlist
);
1760 current
->devinfo
.devname
= current
->devinfo
.devlist
= NULL
;
1761 prev
->next
= current
->next
;
1765 if ((remove_dev
== 1) && (prev
->devinfo
.devname
== NULL
)) {
1768 * what we removed above was the first entry
1769 * in the list. make the next entry to be the
1772 current
= prev
->next
;
1775 * the matching entry was the only entry in the list
1780 if (flag
& DA_AUDIO
)
1781 dlist
->audio
= current
;
1782 else if (flag
& DA_CD
)
1783 dlist
->cd
= current
;
1784 else if (flag
& DA_FLOPPY
)
1785 dlist
->floppy
= current
;
1786 else if (flag
& DA_TAPE
)
1787 dlist
->tape
= current
;
1788 else if (flag
& DA_RMDISK
)
1789 dlist
->rmdisk
= current
;
1796 * da_rm_list_entry -
1798 * The adding of devnames to a devlist and the removal of a
1799 * device are not symmetrical -- hot_cleanup gives a /devices
1800 * name which is used to remove the dentry whose links all point to
1801 * that /devices entry.
1803 * The link argument is present if available to make debugging
1806 * da_rm_list_entry removes an entry from the linked list of devices.
1808 * Returns 1 if the devname was removed successfully,
1809 * 0 if not found, -1 for error.
1813 da_rm_list_entry(devlist_t
*dlist
, char *link
, int type
, char *devname
)
1816 deventry_t
**dentry
, *current
, *prev
;
1820 dentry
= &(dlist
->audio
);
1823 dentry
= &(dlist
->cd
);
1826 dentry
= &(dlist
->floppy
);
1829 dentry
= &(dlist
->tape
);
1832 dentry
= &(dlist
->rmdisk
);
1838 /* Presumably in daemon mode, no need to remove entry, list is empty */
1839 if (*dentry
== (deventry_t
*)NULL
)
1843 for (current
= *dentry
; current
!= NULL
;
1844 prev
= current
, current
= current
->next
) {
1845 if (strcmp(devname
, current
->devinfo
.devname
))
1852 free(current
->devinfo
.devname
);
1853 free(current
->devinfo
.devlist
);
1854 free(current
->devinfo
.devopts
);
1857 *dentry
= current
->next
;
1859 prev
->next
= current
->next
;
1867 * checks if device allocation feature is turned on.
1868 * returns 1 if on, 0 if off, -1 if status string not
1869 * found in device_allocate.
1879 * debug routine to print device entries.
1882 da_print_device(int flag
, devlist_t
*devlist
)
1884 deventry_t
*entry
, *dentry
;
1887 if (flag
& DA_AUDIO
)
1888 dentry
= devlist
->audio
;
1889 else if (flag
& DA_CD
)
1890 dentry
= devlist
->cd
;
1891 else if (flag
& DA_FLOPPY
)
1892 dentry
= devlist
->floppy
;
1893 else if (flag
& DA_TAPE
)
1894 dentry
= devlist
->tape
;
1895 else if (flag
& DA_RMDISK
)
1896 dentry
= devlist
->rmdisk
;
1900 for (entry
= dentry
; entry
!= NULL
; entry
= entry
->next
) {
1901 devinfo
= &(entry
->devinfo
);
1902 (void) fprintf(stdout
, "name: %s\n", devinfo
->devname
);
1903 (void) fprintf(stdout
, "type: %s\n", devinfo
->devtype
);
1904 (void) fprintf(stdout
, "auth: %s\n", devinfo
->devauths
);
1905 (void) fprintf(stdout
, "exec: %s\n", devinfo
->devexec
);
1906 (void) fprintf(stdout
, "list: %s\n\n", devinfo
->devlist
);