1 /* SPDX-License-Identifier: GPL-2.0-or-later */
2 /*
3  * Copyright © 2003-2004 Greg Kroah-Hartman <greg@kroah.com>
4  */
5 
6 #include <errno.h>
7 #include <getopt.h>
8 #include <signal.h>
9 #include <stddef.h>
10 #include <stdio.h>
11 #include <stdlib.h>
12 #include <sys/signalfd.h>
13 #include <unistd.h>
14 
15 #include "sd-device.h"
16 
17 #include "device-private.h"
18 #include "device-util.h"
19 #include "path-util.h"
20 #include "string-util.h"
21 #include "strxcpyx.h"
22 #include "udev-builtin.h"
23 #include "udev-event.h"
24 #include "udevadm-util.h"
25 #include "udevadm.h"
26 
27 static sd_device_action_t arg_action = SD_DEVICE_ADD;
28 static ResolveNameTiming arg_resolve_name_timing = RESOLVE_NAME_EARLY;
29 static const char *arg_syspath = NULL;
30 
help(void)31 static int help(void) {
32 
33         printf("%s test [OPTIONS] DEVPATH\n\n"
34                "Test an event run.\n\n"
35                "  -h --help                            Show this help\n"
36                "  -V --version                         Show package version\n"
37                "  -a --action=ACTION|help              Set action string\n"
38                "  -N --resolve-names=early|late|never  When to resolve names\n",
39                program_invocation_short_name);
40 
41         return 0;
42 }
43 
parse_argv(int argc,char * argv[])44 static int parse_argv(int argc, char *argv[]) {
45         static const struct option options[] = {
46                 { "action",        required_argument, NULL, 'a' },
47                 { "resolve-names", required_argument, NULL, 'N' },
48                 { "version",       no_argument,       NULL, 'V' },
49                 { "help",          no_argument,       NULL, 'h' },
50                 {}
51         };
52 
53         int r, c;
54 
55         while ((c = getopt_long(argc, argv, "a:N:Vh", options, NULL)) >= 0)
56                 switch (c) {
57                 case 'a':
58                         r = parse_device_action(optarg, &arg_action);
59                         if (r < 0)
60                                 return log_error_errno(r, "Invalid action '%s'", optarg);
61                         if (r == 0)
62                                 return 0;
63                         break;
64                 case 'N':
65                         arg_resolve_name_timing = resolve_name_timing_from_string(optarg);
66                         if (arg_resolve_name_timing < 0)
67                                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
68                                                        "--resolve-names= must be early, late or never");
69                         break;
70                 case 'V':
71                         return print_version();
72                 case 'h':
73                         return help();
74                 case '?':
75                         return -EINVAL;
76                 default:
77                         assert_not_reached();
78                 }
79 
80         arg_syspath = argv[optind];
81         if (!arg_syspath)
82                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "syspath parameter missing.");
83 
84         return 1;
85 }
86 
test_main(int argc,char * argv[],void * userdata)87 int test_main(int argc, char *argv[], void *userdata) {
88         _cleanup_(udev_rules_freep) UdevRules *rules = NULL;
89         _cleanup_(udev_event_freep) UdevEvent *event = NULL;
90         _cleanup_(sd_device_unrefp) sd_device *dev = NULL;
91         const char *cmd, *key, *value;
92         sigset_t mask, sigmask_orig;
93         void *val;
94         int r;
95 
96         log_set_max_level(LOG_DEBUG);
97 
98         r = parse_argv(argc, argv);
99         if (r <= 0)
100                 return r;
101 
102         printf("This program is for debugging only, it does not run any program\n"
103                "specified by a RUN key. It may show incorrect results, because\n"
104                "some values may be different, or not available at a simulation run.\n"
105                "\n");
106 
107         assert_se(sigprocmask(SIG_SETMASK, NULL, &sigmask_orig) >= 0);
108 
109         udev_builtin_init();
110 
111         r = udev_rules_load(&rules, arg_resolve_name_timing);
112         if (r < 0) {
113                 log_error_errno(r, "Failed to read udev rules: %m");
114                 goto out;
115         }
116 
117         r = find_device_with_action(arg_syspath, arg_action, &dev);
118         if (r < 0) {
119                 log_error_errno(r, "Failed to open device '%s': %m", arg_syspath);
120                 goto out;
121         }
122 
123         /* don't read info from the db */
124         device_seal(dev);
125 
126         event = udev_event_new(dev, 0, NULL, LOG_DEBUG);
127 
128         assert_se(sigfillset(&mask) >= 0);
129         assert_se(sigprocmask(SIG_SETMASK, &mask, &sigmask_orig) >= 0);
130 
131         udev_event_execute_rules(event, -1, 60 * USEC_PER_SEC, SIGKILL, NULL, rules);
132 
133         FOREACH_DEVICE_PROPERTY(dev, key, value)
134                 printf("%s=%s\n", key, value);
135 
136         ORDERED_HASHMAP_FOREACH_KEY(val, cmd, event->run_list) {
137                 char program[UDEV_PATH_SIZE];
138                 bool truncated;
139 
140                 (void) udev_event_apply_format(event, cmd, program, sizeof(program), false, &truncated);
141                 if (truncated)
142                         log_warning("The command '%s' is truncated while substituting into '%s'.", program, cmd);
143                 printf("run: '%s'\n", program);
144         }
145 
146         r = 0;
147 out:
148         udev_builtin_exit();
149         return r;
150 }
151