1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
2 
3 #include <errno.h>
4 #include <getopt.h>
5 #include <sys/epoll.h>
6 #include <unistd.h>
7 
8 #include "alloc-util.h"
9 #include "fd-util.h"
10 #include "libudev-list-internal.h"
11 #include "libudev-util.h"
12 #include "log.h"
13 #include "main-func.h"
14 #include "stdio-util.h"
15 #include "string-util.h"
16 #include "tests.h"
17 #include "version.h"
18 
19 static bool arg_monitor = false;
20 
print_device(struct udev_device * device)21 static void print_device(struct udev_device *device) {
22         const char *str;
23         dev_t devnum;
24         int count;
25         struct udev_list_entry *list_entry;
26 
27         log_info("*** device: %p ***", device);
28         str = udev_device_get_action(device);
29         if (str)
30                 log_info("action:    '%s'", str);
31 
32         str = udev_device_get_syspath(device);
33         log_info("syspath:   '%s'", str);
34 
35         str = udev_device_get_sysname(device);
36         log_info("sysname:   '%s'", str);
37 
38         str = udev_device_get_sysnum(device);
39         if (str)
40                 log_info("sysnum:    '%s'", str);
41 
42         str = udev_device_get_devpath(device);
43         log_info("devpath:   '%s'", str);
44 
45         str = udev_device_get_subsystem(device);
46         if (str)
47                 log_info("subsystem: '%s'", str);
48 
49         str = udev_device_get_devtype(device);
50         if (str)
51                 log_info("devtype:   '%s'", str);
52 
53         str = udev_device_get_driver(device);
54         if (str)
55                 log_info("driver:    '%s'", str);
56 
57         str = udev_device_get_devnode(device);
58         if (str)
59                 log_info("devname:   '%s'", str);
60 
61         devnum = udev_device_get_devnum(device);
62         if (major(devnum) > 0)
63                 log_info("devnum:    %u:%u", major(devnum), minor(devnum));
64 
65         count = 0;
66         udev_list_entry_foreach(list_entry, udev_device_get_devlinks_list_entry(device)) {
67                 log_info("link:      '%s'", udev_list_entry_get_name(list_entry));
68                 count++;
69         }
70         if (count > 0)
71                 log_info("found %i links", count);
72 
73         count = 0;
74         udev_list_entry_foreach(list_entry, udev_device_get_properties_list_entry(device)) {
75                 log_info("property:  '%s=%s'",
76                        udev_list_entry_get_name(list_entry),
77                        udev_list_entry_get_value(list_entry));
78                 count++;
79         }
80         if (count > 0)
81                 log_info("found %i properties", count);
82 
83         str = udev_device_get_property_value(device, "MAJOR");
84         if (str)
85                 log_info("MAJOR: '%s'", str);
86 
87         str = udev_device_get_sysattr_value(device, "dev");
88         if (str)
89                 log_info("attr{dev}: '%s'", str);
90 }
91 
test_device(struct udev * udev,const char * syspath)92 static void test_device(struct udev *udev, const char *syspath) {
93         _cleanup_(udev_device_unrefp) struct udev_device *device;
94 
95         log_info("/* %s, device %s */", __func__, syspath);
96         device = udev_device_new_from_syspath(udev, syspath);
97         if (device)
98                 print_device(device);
99         else
100                 log_warning_errno(errno, "udev_device_new_from_syspath: %m");
101 }
102 
test_device_parents(struct udev * udev,const char * syspath)103 static void test_device_parents(struct udev *udev, const char *syspath) {
104         _cleanup_(udev_device_unrefp) struct udev_device *device;
105         struct udev_device *device_parent;
106 
107         log_info("/* %s, device %s */", __func__, syspath);
108         device = udev_device_new_from_syspath(udev, syspath);
109         if (!device)
110                 return;
111 
112         log_info("looking at parents");
113         device_parent = device;
114         do {
115                 print_device(device_parent);
116                 device_parent = udev_device_get_parent(device_parent);
117         } while (device_parent != NULL);
118 
119         log_info("looking at parents again");
120         device_parent = device;
121         do {
122                 print_device(device_parent);
123                 device_parent = udev_device_get_parent(device_parent);
124         } while (device_parent != NULL);
125 }
126 
test_device_devnum(struct udev * udev)127 static void test_device_devnum(struct udev *udev) {
128         dev_t devnum = makedev(1, 3);
129         _cleanup_(udev_device_unrefp) struct udev_device *device;
130 
131         log_info("/* %s, device %d:%d */", __func__, major(devnum), minor(devnum));
132 
133         device = udev_device_new_from_devnum(udev, 'c', devnum);
134         if (device)
135                 print_device(device);
136         else
137                 log_warning_errno(errno, "udev_device_new_from_devnum: %m");
138 }
139 
test_device_subsys_name(struct udev * udev,const char * subsys,const char * dev)140 static void test_device_subsys_name(struct udev *udev, const char *subsys, const char *dev) {
141         _cleanup_(udev_device_unrefp) struct udev_device *device;
142 
143         log_info("looking up device: '%s:%s'", subsys, dev);
144         device = udev_device_new_from_subsystem_sysname(udev, subsys, dev);
145         if (!device)
146                 log_warning_errno(errno, "udev_device_new_from_subsystem_sysname: %m");
147         else
148                 print_device(device);
149 }
150 
enumerate_print_list(struct udev_enumerate * enumerate)151 static int enumerate_print_list(struct udev_enumerate *enumerate) {
152         struct udev_list_entry *list_entry;
153         int count = 0;
154 
155         udev_list_entry_foreach(list_entry, udev_enumerate_get_list_entry(enumerate)) {
156                 struct udev_device *device;
157 
158                 device = udev_device_new_from_syspath(udev_enumerate_get_udev(enumerate),
159                                                       udev_list_entry_get_name(list_entry));
160                 if (device) {
161                         log_info("device: '%s' (%s)",
162                                  udev_device_get_syspath(device),
163                                  udev_device_get_subsystem(device));
164                         udev_device_unref(device);
165                         count++;
166                 }
167         }
168         log_info("found %i devices", count);
169         return count;
170 }
171 
test_monitor(struct udev * udev)172 static void test_monitor(struct udev *udev) {
173         _cleanup_(udev_monitor_unrefp) struct udev_monitor *udev_monitor;
174         _cleanup_close_ int fd_ep;
175         int fd_udev;
176         struct epoll_event ep_udev = {
177                 .events = EPOLLIN,
178         }, ep_stdin = {
179                 .events = EPOLLIN,
180                 .data.fd = STDIN_FILENO,
181         };
182 
183         log_info("/* %s */", __func__);
184 
185         fd_ep = epoll_create1(EPOLL_CLOEXEC);
186         assert_se(fd_ep >= 0);
187 
188         udev_monitor = udev_monitor_new_from_netlink(udev, "udev");
189         assert_se(udev_monitor != NULL);
190 
191         fd_udev = udev_monitor_get_fd(udev_monitor);
192         ep_udev.data.fd = fd_udev;
193 
194         assert_se(udev_monitor_filter_add_match_subsystem_devtype(udev_monitor, "block", NULL) >= 0);
195         assert_se(udev_monitor_filter_add_match_subsystem_devtype(udev_monitor, "tty", NULL) >= 0);
196         assert_se(udev_monitor_filter_add_match_subsystem_devtype(udev_monitor, "usb", "usb_device") >= 0);
197 
198         assert_se(udev_monitor_enable_receiving(udev_monitor) >= 0);
199 
200         assert_se(epoll_ctl(fd_ep, EPOLL_CTL_ADD, fd_udev, &ep_udev) >= 0);
201         assert_se(epoll_ctl(fd_ep, EPOLL_CTL_ADD, STDIN_FILENO, &ep_stdin) >= 0);
202 
203         for (;;) {
204                 int fdcount;
205                 struct epoll_event ev[4];
206                 struct udev_device *device;
207                 int i;
208 
209                 printf("waiting for events from udev, press ENTER to exit\n");
210                 fdcount = epoll_wait(fd_ep, ev, ELEMENTSOF(ev), -1);
211                 printf("epoll fd count: %i\n", fdcount);
212 
213                 for (i = 0; i < fdcount; i++) {
214                         if (ev[i].data.fd == fd_udev && ev[i].events & EPOLLIN) {
215                                 device = udev_monitor_receive_device(udev_monitor);
216                                 if (!device) {
217                                         printf("no device from socket\n");
218                                         continue;
219                                 }
220                                 print_device(device);
221                                 udev_device_unref(device);
222                         } else if (ev[i].data.fd == STDIN_FILENO && ev[i].events & EPOLLIN) {
223                                 printf("exiting loop\n");
224                                 return;
225                         }
226                 }
227         }
228 }
229 
test_queue(struct udev * udev)230 static void test_queue(struct udev *udev) {
231         struct udev_queue *udev_queue;
232         bool empty;
233 
234         log_info("/* %s */", __func__);
235 
236         assert_se(udev_queue = udev_queue_new(udev));
237 
238         empty = udev_queue_get_queue_is_empty(udev_queue);
239         log_info("queue is %s", empty ? "empty" : "not empty");
240         udev_queue_unref(udev_queue);
241 }
242 
test_enumerate(struct udev * udev,const char * subsystem)243 static int test_enumerate(struct udev *udev, const char *subsystem) {
244         struct udev_enumerate *udev_enumerate;
245         int r;
246 
247         log_info("/* %s */", __func__);
248 
249         log_info("enumerate '%s'", subsystem == NULL ? "<all>" : subsystem);
250         udev_enumerate = udev_enumerate_new(udev);
251         if (!udev_enumerate)
252                 return -1;
253         udev_enumerate_add_match_subsystem(udev_enumerate, subsystem);
254         udev_enumerate_scan_devices(udev_enumerate);
255         enumerate_print_list(udev_enumerate);
256         udev_enumerate_unref(udev_enumerate);
257 
258         log_info("enumerate 'net' + duplicated scan + null + zero");
259         udev_enumerate = udev_enumerate_new(udev);
260         if (!udev_enumerate)
261                 return -1;
262         udev_enumerate_add_match_subsystem(udev_enumerate, "net");
263         udev_enumerate_scan_devices(udev_enumerate);
264         udev_enumerate_scan_devices(udev_enumerate);
265         udev_enumerate_add_syspath(udev_enumerate, "/sys/class/mem/zero");
266         udev_enumerate_add_syspath(udev_enumerate, "/sys/class/mem/null");
267         udev_enumerate_add_syspath(udev_enumerate, "/sys/class/mem/zero");
268         udev_enumerate_add_syspath(udev_enumerate, "/sys/class/mem/null");
269         udev_enumerate_add_syspath(udev_enumerate, "/sys/class/mem/zero");
270         udev_enumerate_add_syspath(udev_enumerate, "/sys/class/mem/null");
271         udev_enumerate_add_syspath(udev_enumerate, "/sys/class/mem/null");
272         udev_enumerate_add_syspath(udev_enumerate, "/sys/class/mem/zero");
273         udev_enumerate_add_syspath(udev_enumerate, "/sys/class/mem/zero");
274         udev_enumerate_scan_devices(udev_enumerate);
275         enumerate_print_list(udev_enumerate);
276         udev_enumerate_unref(udev_enumerate);
277 
278         log_info("enumerate 'block'");
279         udev_enumerate = udev_enumerate_new(udev);
280         if (!udev_enumerate)
281                 return -1;
282         udev_enumerate_add_match_subsystem(udev_enumerate,"block");
283         r = udev_enumerate_add_match_is_initialized(udev_enumerate);
284         if (r < 0) {
285                 udev_enumerate_unref(udev_enumerate);
286                 return r;
287         }
288         udev_enumerate_scan_devices(udev_enumerate);
289         enumerate_print_list(udev_enumerate);
290         udev_enumerate_unref(udev_enumerate);
291 
292         log_info("enumerate 'not block'");
293         udev_enumerate = udev_enumerate_new(udev);
294         if (!udev_enumerate)
295                 return -1;
296         udev_enumerate_add_nomatch_subsystem(udev_enumerate, "block");
297         udev_enumerate_scan_devices(udev_enumerate);
298         enumerate_print_list(udev_enumerate);
299         udev_enumerate_unref(udev_enumerate);
300 
301         log_info("enumerate 'pci, mem, vc'");
302         udev_enumerate = udev_enumerate_new(udev);
303         if (!udev_enumerate)
304                 return -1;
305         udev_enumerate_add_match_subsystem(udev_enumerate, "pci");
306         udev_enumerate_add_match_subsystem(udev_enumerate, "mem");
307         udev_enumerate_add_match_subsystem(udev_enumerate, "vc");
308         udev_enumerate_scan_devices(udev_enumerate);
309         enumerate_print_list(udev_enumerate);
310         udev_enumerate_unref(udev_enumerate);
311 
312         log_info("enumerate 'subsystem'");
313         udev_enumerate = udev_enumerate_new(udev);
314         if (!udev_enumerate)
315                 return -1;
316         udev_enumerate_scan_subsystems(udev_enumerate);
317         enumerate_print_list(udev_enumerate);
318         udev_enumerate_unref(udev_enumerate);
319 
320         log_info("enumerate 'property IF_FS_*=filesystem'");
321         udev_enumerate = udev_enumerate_new(udev);
322         if (!udev_enumerate)
323                 return -1;
324         udev_enumerate_add_match_property(udev_enumerate, "ID_FS*", "filesystem");
325         udev_enumerate_scan_devices(udev_enumerate);
326         enumerate_print_list(udev_enumerate);
327         udev_enumerate_unref(udev_enumerate);
328         return 0;
329 }
330 
test_hwdb(struct udev * udev,const char * modalias)331 static void test_hwdb(struct udev *udev, const char *modalias) {
332         struct udev_hwdb *hwdb;
333         struct udev_list_entry *entry;
334 
335         log_info("/* %s */", __func__);
336 
337         hwdb = udev_hwdb_new(udev);
338         if (!hwdb)
339                 log_warning_errno(errno, "Failed to open hwdb: %m");
340 
341         udev_list_entry_foreach(entry, udev_hwdb_get_properties_list_entry(hwdb, modalias, 0))
342                 log_info("'%s'='%s'", udev_list_entry_get_name(entry), udev_list_entry_get_value(entry));
343 
344         hwdb = udev_hwdb_unref(hwdb);
345         assert_se(hwdb == NULL);
346 }
347 
test_list(void)348 static void test_list(void) {
349         _cleanup_(udev_list_freep) struct udev_list *list = NULL;
350         struct udev_list_entry *e;
351 
352         /* empty list */
353         assert_se(list = udev_list_new(false));
354         assert_se(!udev_list_get_entry(list));
355         list = udev_list_free(list);
356 
357         /* unique == false */
358         assert_se(list = udev_list_new(false));
359         assert_se(udev_list_entry_add(list, "aaa", "hoge"));
360         assert_se(udev_list_entry_add(list, "aaa", "hogehoge"));
361         assert_se(udev_list_entry_add(list, "bbb", "foo"));
362         e = udev_list_get_entry(list);
363         assert_se(e);
364         assert_se(streq_ptr(udev_list_entry_get_name(e), "aaa"));
365         assert_se(streq_ptr(udev_list_entry_get_value(e), "hoge"));
366         e = udev_list_entry_get_next(e);
367         assert_se(e);
368         assert_se(streq_ptr(udev_list_entry_get_name(e), "aaa"));
369         assert_se(streq_ptr(udev_list_entry_get_value(e), "hogehoge"));
370         e = udev_list_entry_get_next(e);
371         assert_se(e);
372         assert_se(streq_ptr(udev_list_entry_get_name(e), "bbb"));
373         assert_se(streq_ptr(udev_list_entry_get_value(e), "foo"));
374         assert_se(!udev_list_entry_get_next(e));
375 
376         assert_se(!udev_list_entry_get_by_name(e, "aaa"));
377         assert_se(!udev_list_entry_get_by_name(e, "bbb"));
378         assert_se(!udev_list_entry_get_by_name(e, "ccc"));
379         list = udev_list_free(list);
380 
381         /* unique == true */
382         assert_se(list = udev_list_new(true));
383         assert_se(udev_list_entry_add(list, "aaa", "hoge"));
384         assert_se(udev_list_entry_add(list, "aaa", "hogehoge"));
385         assert_se(udev_list_entry_add(list, "bbb", "foo"));
386         e = udev_list_get_entry(list);
387         assert_se(e);
388         assert_se(streq_ptr(udev_list_entry_get_name(e), "aaa"));
389         assert_se(streq_ptr(udev_list_entry_get_value(e), "hogehoge"));
390         e = udev_list_entry_get_next(e);
391         assert_se(streq_ptr(udev_list_entry_get_name(e), "bbb"));
392         assert_se(streq_ptr(udev_list_entry_get_value(e), "foo"));
393         assert_se(!udev_list_entry_get_next(e));
394 
395         e = udev_list_entry_get_by_name(e, "bbb");
396         assert_se(e);
397         assert_se(streq_ptr(udev_list_entry_get_name(e), "bbb"));
398         assert_se(streq_ptr(udev_list_entry_get_value(e), "foo"));
399         e = udev_list_entry_get_by_name(e, "aaa");
400         assert_se(e);
401         assert_se(streq_ptr(udev_list_entry_get_name(e), "aaa"));
402         assert_se(streq_ptr(udev_list_entry_get_value(e), "hogehoge"));
403         assert_se(!udev_list_entry_get_by_name(e, "ccc"));
404 }
405 
parse_args(int argc,char * argv[],const char ** syspath,const char ** subsystem)406 static int parse_args(int argc, char *argv[], const char **syspath, const char **subsystem) {
407         static const struct option options[] = {
408                 { "syspath",   required_argument, NULL, 'p' },
409                 { "subsystem", required_argument, NULL, 's' },
410                 { "debug",     no_argument,       NULL, 'd' },
411                 { "help",      no_argument,       NULL, 'h' },
412                 { "version",   no_argument,       NULL, 'V' },
413                 { "monitor",   no_argument,       NULL, 'm' },
414                 {}
415         };
416         int c;
417 
418         while ((c = getopt_long(argc, argv, "p:s:dhVm", options, NULL)) >= 0)
419                 switch (c) {
420                 case 'p':
421                         *syspath = optarg;
422                         break;
423 
424                 case 's':
425                         *subsystem = optarg;
426                         break;
427 
428                 case 'd':
429                         log_set_max_level(LOG_DEBUG);
430                         break;
431 
432                 case 'h':
433                         printf("--debug --syspath= --subsystem= --help\n");
434                         return 0;
435 
436                 case 'V':
437                         printf("%s\n", GIT_VERSION);
438                         return 0;
439 
440                 case 'm':
441                         arg_monitor = true;
442                         break;
443 
444                 case '?':
445                         return -EINVAL;
446 
447                 default:
448                         assert_not_reached();
449                 }
450 
451         return 1;
452 }
453 
run(int argc,char * argv[])454 static int run(int argc, char *argv[]) {
455         _cleanup_(udev_unrefp) struct udev *udev = NULL;
456 
457         const char *syspath = "/devices/virtual/mem/null";
458         const char *subsystem = NULL;
459         int r;
460 
461         test_setup_logging(LOG_INFO);
462 
463         r = parse_args(argc, argv, &syspath, &subsystem);
464         if (r <= 0)
465                 return r;
466 
467         assert_se(udev = udev_new());
468 
469         /* add sys path if needed */
470         if (!startswith(syspath, "/sys"))
471                 syspath = strjoina("/sys/", syspath);
472 
473         test_device(udev, syspath);
474         test_device_devnum(udev);
475         test_device_subsys_name(udev, "block", "sda");
476         test_device_subsys_name(udev, "subsystem", "pci");
477         test_device_subsys_name(udev, "drivers", "scsi:sd");
478         test_device_subsys_name(udev, "module", "printk");
479         test_device_parents(udev, syspath);
480 
481         test_enumerate(udev, subsystem);
482 
483         test_queue(udev);
484 
485         test_hwdb(udev, "usb:v0D50p0011*");
486 
487         if (arg_monitor)
488                 test_monitor(udev);
489 
490         test_list();
491 
492         return 0;
493 }
494 
495 DEFINE_MAIN_FUNCTION(run);
496