4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License, Version 1.0 only
6 * (the "License"). You may not use this file except in compliance
9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10 * or http://www.opensolaris.org/os/licensing.
11 * See the License for the specific language governing permissions
12 * and limitations under the License.
14 * When distributing Covered Code, include this CDDL HEADER in each
15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16 * If applicable, add the following below this CDDL HEADER, with the
17 * fields enclosed by brackets "[]" replaced with your own identifying
18 * information: Portions Copyright [yyyy] [name of copyright owner]
24 * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
25 * Use is subject to license terms.
26 * Copyright 2017 Nexenta Systems, Inc.
36 #include <sys/types.h>
39 #include <sys/modctl.h>
42 #include <libdevinfo.h>
46 * Get Device Id from an open file descriptor
49 devid_get(int fd
, ddi_devid_t
*devidp
)
56 if (fstat(fd
, &statb
) != 0)
59 /* If not char or block device, then error */
60 if (!S_ISCHR(statb
.st_mode
) && !S_ISBLK(statb
.st_mode
))
63 /* Get the device id size */
65 if (modctl(MODSIZEOF_DEVID
, dev
, &len
) != 0)
68 /* Allocate space to return device id */
69 if ((mydevid
= (ddi_devid_t
)malloc(len
)) == NULL
)
72 /* Get the device id */
73 if (modctl(MODGETDEVID
, dev
, len
, mydevid
) != 0) {
78 /* Return the device id copy */
87 devid_get_minor_name(int fd
, char **minor_namep
)
95 if (fstat(fd
, &statb
) != 0)
98 /* If not a char or block device, then return an error */
99 if (!S_ISCHR(statb
.st_mode
) && !S_ISBLK(statb
.st_mode
))
102 spectype
= statb
.st_mode
& S_IFMT
;
105 /* Get the minor name size */
106 if (modctl(MODSIZEOF_MINORNAME
, dev
, spectype
, &len
) != 0)
109 /* Allocate space for the minor name */
110 if ((myminor_name
= (char *)malloc(len
)) == NULL
)
113 /* Get the minor name */
114 if (modctl(MODGETMINORNAME
, dev
, spectype
, len
, myminor_name
) != 0) {
119 /* return the minor name copy */
120 *minor_namep
= myminor_name
;
125 devid_str_from_path(const char *path
)
129 char *minor
, *ret
= NULL
;
131 if ((fd
= open(path
, O_RDONLY
)) < 0)
134 if (devid_get(fd
, &devid
) == 0) {
135 if (devid_get_minor_name(fd
, &minor
) != 0)
137 ret
= devid_str_encode(devid
, minor
);
139 devid_str_free(minor
);
147 /* list element of devid_nmlist_t information */
149 struct nmlist
*nl_next
;
154 /* add list element to end of nmlist headed by *nlhp */
156 nmlist_add(struct nmlist
**nlhp
, char *path
)
162 /* stat and get the devt for char or block */
163 if ((stat(path
, &statb
) == 0) &&
164 (S_ISCHR(statb
.st_mode
) || S_ISBLK(statb
.st_mode
)))
169 /* find the end of the list */
170 for (; (nl
= *nlhp
) != NULL
; nlhp
= &nl
->nl_next
)
173 /* allocate and initialize new entry */
174 if ((nl
= malloc(sizeof (*nl
))) == NULL
)
177 if ((nl
->nl_devname
= strdup(path
)) == NULL
) {
184 /* link new entry at end */
189 /* information needed by devlink_callback to call nmlist_add */
190 struct devlink_cbinfo
{
191 struct nmlist
**cbi_nlhp
;
192 char *cbi_search_path
;
196 /* di_devlink callback to add a /dev entry to nmlist */
198 devlink_callback(di_devlink_t dl
, void *arg
)
200 struct devlink_cbinfo
*cbip
= (struct devlink_cbinfo
*)arg
;
201 char *devpath
= (char *)di_devlink_path(dl
);
203 if (strncmp(devpath
, cbip
->cbi_search_path
,
204 strlen(cbip
->cbi_search_path
)) == 0) {
205 if (nmlist_add(cbip
->cbi_nlhp
, devpath
) == NULL
) {
207 return (DI_WALK_TERMINATE
);
210 return (DI_WALK_CONTINUE
);
214 * Resolve /dev names to DI_PRIMARY_LINK, DI_SECONDARY_LINK, or both.
215 * The default is to resolve to just the DI_PRIMARY_LINK.
217 int devid_deviceid_to_nmlist_link
= DI_PRIMARY_LINK
;
220 * Options for the devid_deviceid_to_nmlist implementation:
222 * DEVICEID_NMLIST_SLINK - reduce overhead by reuse the previous
225 #define DEVICEID_NMLIST_SLINK 1
226 int devid_deviceid_to_nmlist_flg
= 0;
227 static di_devlink_handle_t devid_deviceid_to_nmlist_dlh
= NULL
; /* SLINK */
229 #define DEVICEID_NMLIST_NRETRY 10
232 * Convert the specified devid/minor_name into a devid_nmlist_t array
233 * with names that resolve into /devices or /dev depending on search_path.
235 * The man page indicates that:
237 * This function traverses the file tree, starting at search_path.
239 * This is not true, we reverse engineer the paths relative to
240 * the specified search path to avoid attaching all devices.
243 devid_deviceid_to_nmlist(char *search_path
, ddi_devid_t devid
, char *minor_name
,
244 devid_nmlist_t
**retlist
)
251 di_devlink_handle_t dlh
= NULL
;
253 struct devlink_cbinfo cbi
;
254 struct nmlist
*nlh
= NULL
;
263 /* verify valid search path starts with "/devices" or "/dev" */
264 if ((strcmp(search_path
, "/devices") == 0) ||
265 (strncmp(search_path
, "/devices/", 9) == 0))
267 else if ((strcmp(search_path
, "/dev") == 0) ||
268 (strncmp(search_path
, "/dev/", 5) == 0))
276 /* translate devid/minor_name to /devices paths */
277 again
: if (modctl(MODDEVID2PATHS
, devid
, minor_name
, 0, &lens
, NULL
) != 0)
279 if ((paths
= (char *)malloc(lens
)) == NULL
)
281 if (modctl(MODDEVID2PATHS
, devid
, minor_name
, 0, &lens
, paths
) != 0) {
282 if ((errno
== EAGAIN
) && (nagain
++ < DEVICEID_NMLIST_NRETRY
)) {
291 * initialize for /devices path to /dev path translation. To reduce
292 * overhead we reuse the last snapshot if DEVICEID_NMLIST_SLINK is set.
295 dlh
= devid_deviceid_to_nmlist_dlh
;
297 !(devid_deviceid_to_nmlist_flg
& DEVICEID_NMLIST_SLINK
)) {
298 (void) di_devlink_fini(&dlh
);
299 dlh
= devid_deviceid_to_nmlist_dlh
= NULL
;
302 ((dlh
= di_devlink_init(NULL
, 0)) == NULL
))
307 * iterate over all the devtspectype resolutions of the devid and
308 * convert them into the appropriate path form and add items to return
309 * to the nmlist list;
311 for (path
= paths
; *path
; path
+= strlen(path
) + 1) {
313 /* add /dev entries */
315 cbi
.cbi_search_path
= search_path
;
318 (void) di_devlink_walk(dlh
, NULL
, path
,
319 devid_deviceid_to_nmlist_link
,
320 (void *)&cbi
, devlink_callback
);
324 /* add /devices entry */
325 cp
= malloc(strlen("/devices") + strlen(path
) + 1);
326 (void) strcpy(cp
, "/devices");
327 (void) strcat(cp
, path
);
328 if (strncmp(cp
, search_path
,
329 strlen(search_path
)) == 0) {
330 if (nmlist_add(&nlh
, cp
) == NULL
) {
339 /* convert from nmlist to retlist array */
340 for (nl
= nlh
, nret
= 0; nl
; nl
= nl
->nl_next
)
346 if ((*retlist
= calloc(nret
+ 1, sizeof (devid_nmlist_t
))) == NULL
) {
350 for (nl
= nlh
, rl
= *retlist
; nl
; nl
= nl
->nl_next
, rl
++) {
351 rl
->devname
= nl
->nl_devname
;
352 rl
->dev
= nl
->nl_dev
;
360 while ((nl
= nlh
) != NULL
) { /* free the nmlist */
367 (devid_deviceid_to_nmlist_flg
& DEVICEID_NMLIST_SLINK
))
368 devid_deviceid_to_nmlist_dlh
= dlh
;
370 (void) di_devlink_fini(&dlh
);
380 * Free Device Id Name List
383 devid_free_nmlist(devid_nmlist_t
*list
)
385 devid_nmlist_t
*p
= list
;
390 /* Free all the device names */
391 while (p
->devname
!= NULL
) {