xref: /freebsd/sys/contrib/openzfs/tests/zfs-tests/cmd/devname2devid.c (revision fa38579f317d5c2ff2926fab9b12ee6d429bd155)
1 // SPDX-License-Identifier: CDDL-1.0
2 /*
3  * CDDL HEADER START
4  *
5  * The contents of this file are subject to the terms of the
6  * Common Development and Distribution License (the "License").
7  * You may not use this file except in compliance with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or https://opensource.org/licenses/CDDL-1.0.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
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]
19  *
20  * CDDL HEADER END
21  */
22 
23 /*
24  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
25  * Use is subject to license terms.
26  *
27  * Copyright (c) 2016, Intel Corporation.
28  */
29 
30 #include <sys/types.h>
31 #include <sys/param.h>
32 #include <sys/stat.h>
33 #include <libudev.h>
34 #include <errno.h>
35 #include <stdio.h>
36 #include <stdlib.h>
37 #include <string.h>
38 #include <fcntl.h>
39 
40 /*
41  * Linux persistent device strings for vdev labels
42  *
43  * based on udev_device_get_devid() at zfs/lib/libzfs/libzfs_import.c
44  */
45 
46 #define	DEV_BYID_PATH	"/dev/disk/by-id/"
47 
48 static int
49 udev_device_get_devid(struct udev_device *dev, char *bufptr, size_t buflen)
50 {
51 	struct udev_list_entry *entry;
52 	const char *bus;
53 	char devbyid[MAXPATHLEN];
54 
55 	/* The bus based by-id path is preferred */
56 	bus = udev_device_get_property_value(dev, "ID_BUS");
57 
58 	if (bus == NULL) {
59 		const char *dm_uuid;
60 
61 		/*
62 		 * For multipath nodes use the persistent uuid based identifier
63 		 *
64 		 * Example: 'dm-uuid-mpath-35000c5006304de3f'
65 		 */
66 		dm_uuid = udev_device_get_property_value(dev, "DM_UUID");
67 		if (dm_uuid != NULL) {
68 			(void) snprintf(bufptr, buflen, "dm-uuid-%s", dm_uuid);
69 			return (0);
70 		}
71 		return (ENODATA);
72 	}
73 
74 	/*
75 	 * locate the bus specific by-id link
76 	 *
77 	 * Example: 'scsi-MG03SCA300_350000494a8cb3d67-part1'
78 	 */
79 	(void) snprintf(devbyid, sizeof (devbyid), "%s%s-", DEV_BYID_PATH, bus);
80 	entry = udev_device_get_devlinks_list_entry(dev);
81 	while (entry != NULL) {
82 		const char *name;
83 
84 		name = udev_list_entry_get_name(entry);
85 		if (strncmp(name, devbyid, strlen(devbyid)) == 0) {
86 			name += strlen(DEV_BYID_PATH);
87 			(void) stpncpy(bufptr, name, buflen - 1);
88 			bufptr[buflen - 1] = '\0';
89 			return (0);
90 		}
91 		entry = udev_list_entry_get_next(entry);
92 	}
93 
94 	return (ENODATA);
95 }
96 
97 /*
98  * Usage: devname2devid <devicepath>
99  *
100  * Examples:
101  * # ./devname2devid /dev/sda1
102  * devid scsi-350000394a8caede4-part1
103  *
104  * # ./devname2devid /dev/dm-1
105  * devid: 'dm-uuid-mpath-35000c5006304de3f'
106  *
107  * This program accepts a disk or disk slice path and prints a
108  * device id.
109  *
110  * Exit values:
111  *	0 - means success
112  *	1 - means failure
113  *
114  */
115 int
116 main(int argc, char *argv[])
117 {
118 	struct udev *udev;
119 	struct udev_device *dev = NULL;
120 	char devid[128], nodepath[MAXPATHLEN];
121 	char *device, *sysname;
122 	int ret;
123 
124 	if (argc == 1) {
125 		(void) printf("%s <devicepath> [search path]\n", argv[0]);
126 		exit(1);
127 	}
128 	device = argv[1];
129 
130 	if ((udev = udev_new()) == NULL) {
131 		perror("udev_new");
132 		exit(1);
133 	}
134 
135 	/* resolve path to a runtime device node instance */
136 	if (realpath(device, nodepath) == NULL) {
137 		perror("realpath");
138 		exit(1);
139 	}
140 	sysname = strrchr(nodepath, '/') + 1;
141 
142 	if ((dev = udev_device_new_from_subsystem_sysname(udev, "block",
143 	    sysname)) == NULL) {
144 		perror(sysname);
145 		exit(1);
146 	}
147 
148 	if ((ret = udev_device_get_devid(dev, devid, sizeof (devid))) != 0) {
149 		udev_device_unref(dev);
150 		errno = ret;
151 		perror(sysname);
152 		exit(1);
153 	}
154 
155 	(void) printf("devid %s\n", devid);
156 
157 	udev_device_unref(dev);
158 	udev_unref(udev);
159 
160 	return (0);
161 }
162