]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/udev/udev-builtin-usb_id.c
Merge pull request #9685 from yuwata/fix-9663
[thirdparty/systemd.git] / src / udev / udev-builtin-usb_id.c
1 /* SPDX-License-Identifier: GPL-2.0+ */
2 /*
3 * USB device properties and persistent device path
4 *
5 * Copyright (c) 2005 SUSE Linux Products GmbH, Germany
6 * Author: Hannes Reinecke <hare@suse.de>
7 */
8
9 #include <ctype.h>
10 #include <errno.h>
11 #include <fcntl.h>
12 #include <stdarg.h>
13 #include <stdio.h>
14 #include <stdlib.h>
15 #include <string.h>
16 #include <unistd.h>
17
18 #include "alloc-util.h"
19 #include "fd-util.h"
20 #include "string-util.h"
21 #include "udev.h"
22
23 static void set_usb_iftype(char *to, int if_class_num, size_t len) {
24 const char *type = "generic";
25
26 switch (if_class_num) {
27 case 1:
28 type = "audio";
29 break;
30 case 2: /* CDC-Control */
31 break;
32 case 3:
33 type = "hid";
34 break;
35 case 5: /* Physical */
36 break;
37 case 6:
38 type = "media";
39 break;
40 case 7:
41 type = "printer";
42 break;
43 case 8:
44 type = "storage";
45 break;
46 case 9:
47 type = "hub";
48 break;
49 case 0x0a: /* CDC-Data */
50 break;
51 case 0x0b: /* Chip/Smart Card */
52 break;
53 case 0x0d: /* Content Security */
54 break;
55 case 0x0e:
56 type = "video";
57 break;
58 case 0xdc: /* Diagnostic Device */
59 break;
60 case 0xe0: /* Wireless Controller */
61 break;
62 case 0xfe: /* Application-specific */
63 break;
64 case 0xff: /* Vendor-specific */
65 break;
66 default:
67 break;
68 }
69 strncpy(to, type, len);
70 to[len-1] = '\0';
71 }
72
73 static int set_usb_mass_storage_ifsubtype(char *to, const char *from, size_t len) {
74 int type_num = 0;
75 char *eptr;
76 const char *type = "generic";
77
78 type_num = strtoul(from, &eptr, 0);
79 if (eptr != from) {
80 switch (type_num) {
81 case 1: /* RBC devices */
82 type = "rbc";
83 break;
84 case 2:
85 type = "atapi";
86 break;
87 case 3:
88 type = "tape";
89 break;
90 case 4: /* UFI */
91 type = "floppy";
92 break;
93 case 6: /* Transparent SPC-2 devices */
94 type = "scsi";
95 break;
96 default:
97 break;
98 }
99 }
100 strscpy(to, len, type);
101 return type_num;
102 }
103
104 static void set_scsi_type(char *to, const char *from, size_t len) {
105 int type_num;
106 char *eptr;
107 const char *type = "generic";
108
109 type_num = strtoul(from, &eptr, 0);
110 if (eptr != from) {
111 switch (type_num) {
112 case 0:
113 case 0xe:
114 type = "disk";
115 break;
116 case 1:
117 type = "tape";
118 break;
119 case 4:
120 case 7:
121 case 0xf:
122 type = "optical";
123 break;
124 case 5:
125 type = "cd";
126 break;
127 default:
128 break;
129 }
130 }
131 strscpy(to, len, type);
132 }
133
134 #define USB_DT_DEVICE 0x01
135 #define USB_DT_INTERFACE 0x04
136
137 static int dev_if_packed_info(struct udev_device *dev, char *ifs_str, size_t len) {
138 _cleanup_free_ char *filename = NULL;
139 _cleanup_close_ int fd = -1;
140 ssize_t size;
141 unsigned char buf[18 + 65535];
142 size_t pos = 0;
143 unsigned strpos = 0;
144 struct usb_interface_descriptor {
145 uint8_t bLength;
146 uint8_t bDescriptorType;
147 uint8_t bInterfaceNumber;
148 uint8_t bAlternateSetting;
149 uint8_t bNumEndpoints;
150 uint8_t bInterfaceClass;
151 uint8_t bInterfaceSubClass;
152 uint8_t bInterfaceProtocol;
153 uint8_t iInterface;
154 } _packed_;
155
156 if (asprintf(&filename, "%s/descriptors", udev_device_get_syspath(dev)) < 0)
157 return log_oom();
158
159 fd = open(filename, O_RDONLY|O_CLOEXEC);
160 if (fd < 0)
161 return log_debug_errno(errno, "Error opening USB device 'descriptors' file: %m");
162
163 size = read(fd, buf, sizeof(buf));
164 if (size < 18 || (size_t) size >= sizeof(buf))
165 return -EIO;
166
167 ifs_str[0] = '\0';
168 while (pos + sizeof(struct usb_interface_descriptor) < (size_t) size &&
169 strpos + 7 < len - 2) {
170
171 struct usb_interface_descriptor *desc;
172 char if_str[8];
173
174 desc = (struct usb_interface_descriptor *) &buf[pos];
175 if (desc->bLength < 3)
176 break;
177 pos += desc->bLength;
178
179 if (desc->bDescriptorType != USB_DT_INTERFACE)
180 continue;
181
182 if (snprintf(if_str, 8, ":%02x%02x%02x",
183 desc->bInterfaceClass,
184 desc->bInterfaceSubClass,
185 desc->bInterfaceProtocol) != 7)
186 continue;
187
188 if (strstr(ifs_str, if_str) != NULL)
189 continue;
190
191 memcpy(&ifs_str[strpos], if_str, 8),
192 strpos += 7;
193 }
194
195 if (strpos > 0) {
196 ifs_str[strpos++] = ':';
197 ifs_str[strpos++] = '\0';
198 }
199
200 return 0;
201 }
202
203 /*
204 * A unique USB identification is generated like this:
205 *
206 * 1.) Get the USB device type from InterfaceClass and InterfaceSubClass
207 * 2.) If the device type is 'Mass-Storage/SPC-2' or 'Mass-Storage/RBC',
208 * use the SCSI vendor and model as USB-Vendor and USB-model.
209 * 3.) Otherwise, use the USB manufacturer and product as
210 * USB-Vendor and USB-model. Any non-printable characters
211 * in those strings will be skipped; a slash '/' will be converted
212 * into a full stop '.'.
213 * 4.) If that fails, too, we will use idVendor and idProduct
214 * as USB-Vendor and USB-model.
215 * 5.) The USB identification is the USB-vendor and USB-model
216 * string concatenated with an underscore '_'.
217 * 6.) If the device supplies a serial number, this number
218 * is concatenated with the identification with an underscore '_'.
219 */
220 static int builtin_usb_id(struct udev_device *dev, int argc, char *argv[], bool test) {
221 char vendor_str[64] = "";
222 char vendor_str_enc[256];
223 const char *vendor_id;
224 char model_str[64] = "";
225 char model_str_enc[256];
226 const char *product_id;
227 char serial_str[UTIL_NAME_SIZE] = "";
228 char packed_if_str[UTIL_NAME_SIZE] = "";
229 char revision_str[64] = "";
230 char type_str[64] = "";
231 char instance_str[64] = "";
232 const char *ifnum = NULL;
233 const char *driver = NULL;
234 char serial[256];
235
236 struct udev_device *dev_interface = NULL;
237 struct udev_device *dev_usb = NULL;
238 const char *if_class, *if_subclass;
239 int if_class_num;
240 int protocol = 0;
241 size_t l;
242 char *s;
243
244 assert(dev);
245
246 /* shortcut, if we are called directly for a "usb_device" type */
247 if (udev_device_get_devtype(dev) != NULL && streq(udev_device_get_devtype(dev), "usb_device")) {
248 dev_if_packed_info(dev, packed_if_str, sizeof(packed_if_str));
249 dev_usb = dev;
250 goto fallback;
251 }
252
253 /* usb interface directory */
254 dev_interface = udev_device_get_parent_with_subsystem_devtype(dev, "usb", "usb_interface");
255 if (dev_interface == NULL) {
256 log_debug("unable to access usb_interface device of '%s'",
257 udev_device_get_syspath(dev));
258 return EXIT_FAILURE;
259 }
260
261 ifnum = udev_device_get_sysattr_value(dev_interface, "bInterfaceNumber");
262 driver = udev_device_get_sysattr_value(dev_interface, "driver");
263
264 if_class = udev_device_get_sysattr_value(dev_interface, "bInterfaceClass");
265 if (!if_class) {
266 log_debug("%s: cannot get bInterfaceClass attribute",
267 udev_device_get_sysname(dev));
268 return EXIT_FAILURE;
269 }
270
271 if_class_num = strtoul(if_class, NULL, 16);
272 if (if_class_num == 8) {
273 /* mass storage */
274 if_subclass = udev_device_get_sysattr_value(dev_interface, "bInterfaceSubClass");
275 if (if_subclass != NULL)
276 protocol = set_usb_mass_storage_ifsubtype(type_str, if_subclass, sizeof(type_str)-1);
277 } else {
278 set_usb_iftype(type_str, if_class_num, sizeof(type_str)-1);
279 }
280
281 log_debug("%s: if_class %d protocol %d",
282 udev_device_get_syspath(dev_interface), if_class_num, protocol);
283
284 /* usb device directory */
285 dev_usb = udev_device_get_parent_with_subsystem_devtype(dev_interface, "usb", "usb_device");
286 if (!dev_usb) {
287 log_debug("unable to find parent 'usb' device of '%s'",
288 udev_device_get_syspath(dev));
289 return EXIT_FAILURE;
290 }
291
292 /* all interfaces of the device in a single string */
293 dev_if_packed_info(dev_usb, packed_if_str, sizeof(packed_if_str));
294
295 /* mass storage : SCSI or ATAPI */
296 if (IN_SET(protocol, 6, 2)) {
297 struct udev_device *dev_scsi;
298 const char *scsi_model, *scsi_vendor, *scsi_type, *scsi_rev;
299 int host, bus, target, lun;
300
301 /* get scsi device */
302 dev_scsi = udev_device_get_parent_with_subsystem_devtype(dev, "scsi", "scsi_device");
303 if (dev_scsi == NULL) {
304 log_debug("unable to find parent 'scsi' device of '%s'",
305 udev_device_get_syspath(dev));
306 goto fallback;
307 }
308 if (sscanf(udev_device_get_sysname(dev_scsi), "%d:%d:%d:%d", &host, &bus, &target, &lun) != 4) {
309 log_debug("invalid scsi device '%s'", udev_device_get_sysname(dev_scsi));
310 goto fallback;
311 }
312
313 /* Generic SPC-2 device */
314 scsi_vendor = udev_device_get_sysattr_value(dev_scsi, "vendor");
315 if (!scsi_vendor) {
316 log_debug("%s: cannot get SCSI vendor attribute",
317 udev_device_get_sysname(dev_scsi));
318 goto fallback;
319 }
320 udev_util_encode_string(scsi_vendor, vendor_str_enc, sizeof(vendor_str_enc));
321 util_replace_whitespace(scsi_vendor, vendor_str, sizeof(vendor_str)-1);
322 util_replace_chars(vendor_str, NULL);
323
324 scsi_model = udev_device_get_sysattr_value(dev_scsi, "model");
325 if (!scsi_model) {
326 log_debug("%s: cannot get SCSI model attribute",
327 udev_device_get_sysname(dev_scsi));
328 goto fallback;
329 }
330 udev_util_encode_string(scsi_model, model_str_enc, sizeof(model_str_enc));
331 util_replace_whitespace(scsi_model, model_str, sizeof(model_str)-1);
332 util_replace_chars(model_str, NULL);
333
334 scsi_type = udev_device_get_sysattr_value(dev_scsi, "type");
335 if (!scsi_type) {
336 log_debug("%s: cannot get SCSI type attribute",
337 udev_device_get_sysname(dev_scsi));
338 goto fallback;
339 }
340 set_scsi_type(type_str, scsi_type, sizeof(type_str)-1);
341
342 scsi_rev = udev_device_get_sysattr_value(dev_scsi, "rev");
343 if (!scsi_rev) {
344 log_debug("%s: cannot get SCSI revision attribute",
345 udev_device_get_sysname(dev_scsi));
346 goto fallback;
347 }
348 util_replace_whitespace(scsi_rev, revision_str, sizeof(revision_str)-1);
349 util_replace_chars(revision_str, NULL);
350
351 /*
352 * some broken devices have the same identifiers
353 * for all luns, export the target:lun number
354 */
355 sprintf(instance_str, "%d:%d", target, lun);
356 }
357
358 fallback:
359 vendor_id = udev_device_get_sysattr_value(dev_usb, "idVendor");
360 product_id = udev_device_get_sysattr_value(dev_usb, "idProduct");
361
362 /* fallback to USB vendor & device */
363 if (vendor_str[0] == '\0') {
364 const char *usb_vendor = NULL;
365
366 usb_vendor = udev_device_get_sysattr_value(dev_usb, "manufacturer");
367 if (!usb_vendor)
368 usb_vendor = vendor_id;
369 if (!usb_vendor) {
370 log_debug("No USB vendor information available");
371 return EXIT_FAILURE;
372 }
373 udev_util_encode_string(usb_vendor, vendor_str_enc, sizeof(vendor_str_enc));
374 util_replace_whitespace(usb_vendor, vendor_str, sizeof(vendor_str)-1);
375 util_replace_chars(vendor_str, NULL);
376 }
377
378 if (model_str[0] == '\0') {
379 const char *usb_model = NULL;
380
381 usb_model = udev_device_get_sysattr_value(dev_usb, "product");
382 if (!usb_model)
383 usb_model = product_id;
384 if (!usb_model)
385 return EXIT_FAILURE;
386 udev_util_encode_string(usb_model, model_str_enc, sizeof(model_str_enc));
387 util_replace_whitespace(usb_model, model_str, sizeof(model_str)-1);
388 util_replace_chars(model_str, NULL);
389 }
390
391 if (revision_str[0] == '\0') {
392 const char *usb_rev;
393
394 usb_rev = udev_device_get_sysattr_value(dev_usb, "bcdDevice");
395 if (usb_rev) {
396 util_replace_whitespace(usb_rev, revision_str, sizeof(revision_str)-1);
397 util_replace_chars(revision_str, NULL);
398 }
399 }
400
401 if (serial_str[0] == '\0') {
402 const char *usb_serial;
403
404 usb_serial = udev_device_get_sysattr_value(dev_usb, "serial");
405 if (usb_serial) {
406 const unsigned char *p;
407
408 /* http://msdn.microsoft.com/en-us/library/windows/hardware/gg487321.aspx */
409 for (p = (unsigned char *)usb_serial; *p != '\0'; p++)
410 if (*p < 0x20 || *p > 0x7f || *p == ',') {
411 usb_serial = NULL;
412 break;
413 }
414 }
415
416 if (usb_serial) {
417 util_replace_whitespace(usb_serial, serial_str, sizeof(serial_str)-1);
418 util_replace_chars(serial_str, NULL);
419 }
420 }
421
422 s = serial;
423 l = strpcpyl(&s, sizeof(serial), vendor_str, "_", model_str, NULL);
424 if (!isempty(serial_str))
425 l = strpcpyl(&s, l, "_", serial_str, NULL);
426
427 if (!isempty(instance_str))
428 strpcpyl(&s, l, "-", instance_str, NULL);
429
430 udev_builtin_add_property(dev, test, "ID_VENDOR", vendor_str);
431 udev_builtin_add_property(dev, test, "ID_VENDOR_ENC", vendor_str_enc);
432 udev_builtin_add_property(dev, test, "ID_VENDOR_ID", vendor_id);
433 udev_builtin_add_property(dev, test, "ID_MODEL", model_str);
434 udev_builtin_add_property(dev, test, "ID_MODEL_ENC", model_str_enc);
435 udev_builtin_add_property(dev, test, "ID_MODEL_ID", product_id);
436 udev_builtin_add_property(dev, test, "ID_REVISION", revision_str);
437 udev_builtin_add_property(dev, test, "ID_SERIAL", serial);
438 if (!isempty(serial_str))
439 udev_builtin_add_property(dev, test, "ID_SERIAL_SHORT", serial_str);
440 if (!isempty(type_str))
441 udev_builtin_add_property(dev, test, "ID_TYPE", type_str);
442 if (!isempty(instance_str))
443 udev_builtin_add_property(dev, test, "ID_INSTANCE", instance_str);
444 udev_builtin_add_property(dev, test, "ID_BUS", "usb");
445 if (!isempty(packed_if_str))
446 udev_builtin_add_property(dev, test, "ID_USB_INTERFACES", packed_if_str);
447 if (ifnum != NULL)
448 udev_builtin_add_property(dev, test, "ID_USB_INTERFACE_NUM", ifnum);
449 if (driver != NULL)
450 udev_builtin_add_property(dev, test, "ID_USB_DRIVER", driver);
451 return EXIT_SUCCESS;
452 }
453
454 const struct udev_builtin udev_builtin_usb_id = {
455 .name = "usb_id",
456 .cmd = builtin_usb_id,
457 .help = "USB device properties",
458 .run_once = true,
459 };