]>
Commit | Line | Data |
---|---|---|
1 | /* | |
2 | * Copyright (C) 2008-2009 Kay Sievers <kay@vrfy.org> | |
3 | * | |
4 | * This program is free software: you can redistribute it and/or modify | |
5 | * it under the terms of the GNU General Public License as published by | |
6 | * the Free Software Foundation, either version 2 of the License, or | |
7 | * (at your option) any later version. | |
8 | * | |
9 | * This program is distributed in the hope that it will be useful, | |
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
12 | * GNU General Public License for more details. | |
13 | * | |
14 | * You should have received a copy of the GNU General Public License | |
15 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | |
16 | */ | |
17 | ||
18 | #include <errno.h> | |
19 | #include <fcntl.h> | |
20 | #include <getopt.h> | |
21 | #include <stddef.h> | |
22 | #include <stdio.h> | |
23 | #include <string.h> | |
24 | #include <unistd.h> | |
25 | ||
26 | #include "string-util.h" | |
27 | #include "udev-util.h" | |
28 | #include "udev.h" | |
29 | #include "udevadm-util.h" | |
30 | #include "util.h" | |
31 | ||
32 | static int verbose; | |
33 | static int dry_run; | |
34 | ||
35 | static void exec_list(struct udev_enumerate *udev_enumerate, const char *action) { | |
36 | struct udev_list_entry *entry; | |
37 | ||
38 | udev_list_entry_foreach(entry, udev_enumerate_get_list_entry(udev_enumerate)) { | |
39 | char filename[UTIL_PATH_SIZE]; | |
40 | int fd; | |
41 | ||
42 | if (verbose) | |
43 | printf("%s\n", udev_list_entry_get_name(entry)); | |
44 | if (dry_run) | |
45 | continue; | |
46 | strscpyl(filename, sizeof(filename), udev_list_entry_get_name(entry), "/uevent", NULL); | |
47 | fd = open(filename, O_WRONLY|O_CLOEXEC); | |
48 | if (fd < 0) | |
49 | continue; | |
50 | if (write(fd, action, strlen(action)) < 0) | |
51 | log_debug_errno(errno, "error writing '%s' to '%s': %m", action, filename); | |
52 | close(fd); | |
53 | } | |
54 | } | |
55 | ||
56 | static const char *keyval(const char *str, const char **val, char *buf, size_t size) { | |
57 | char *pos; | |
58 | ||
59 | strscpy(buf, size,str); | |
60 | pos = strchr(buf, '='); | |
61 | if (pos != NULL) { | |
62 | pos[0] = 0; | |
63 | pos++; | |
64 | } | |
65 | *val = pos; | |
66 | return buf; | |
67 | } | |
68 | ||
69 | static void help(void) { | |
70 | printf("%s trigger OPTIONS\n\n" | |
71 | "Request events from the kernel.\n\n" | |
72 | " -h --help Show this help\n" | |
73 | " --version Show package version\n" | |
74 | " -v --verbose Print the list of devices while running\n" | |
75 | " -n --dry-run Do not actually trigger the events\n" | |
76 | " -t --type= Type of events to trigger\n" | |
77 | " devices sysfs devices (default)\n" | |
78 | " subsystems sysfs subsystems and drivers\n" | |
79 | " -c --action=ACTION Event action value, default is \"change\"\n" | |
80 | " -s --subsystem-match=SUBSYSTEM Trigger devices from a matching subsystem\n" | |
81 | " -S --subsystem-nomatch=SUBSYSTEM Exclude devices from a matching subsystem\n" | |
82 | " -a --attr-match=FILE[=VALUE] Trigger devices with a matching attribute\n" | |
83 | " -A --attr-nomatch=FILE[=VALUE] Exclude devices with a matching attribute\n" | |
84 | " -p --property-match=KEY=VALUE Trigger devices with a matching property\n" | |
85 | " -g --tag-match=KEY=VALUE Trigger devices with a matching property\n" | |
86 | " -y --sysname-match=NAME Trigger devices with this /sys path\n" | |
87 | " --name-match=NAME Trigger devices with this /dev name\n" | |
88 | " -b --parent-match=NAME Trigger devices with that parent device\n" | |
89 | , program_invocation_short_name); | |
90 | } | |
91 | ||
92 | static int adm_trigger(struct udev *udev, int argc, char *argv[]) { | |
93 | enum { | |
94 | ARG_NAME = 0x100, | |
95 | }; | |
96 | ||
97 | static const struct option options[] = { | |
98 | { "verbose", no_argument, NULL, 'v' }, | |
99 | { "dry-run", no_argument, NULL, 'n' }, | |
100 | { "type", required_argument, NULL, 't' }, | |
101 | { "action", required_argument, NULL, 'c' }, | |
102 | { "subsystem-match", required_argument, NULL, 's' }, | |
103 | { "subsystem-nomatch", required_argument, NULL, 'S' }, | |
104 | { "attr-match", required_argument, NULL, 'a' }, | |
105 | { "attr-nomatch", required_argument, NULL, 'A' }, | |
106 | { "property-match", required_argument, NULL, 'p' }, | |
107 | { "tag-match", required_argument, NULL, 'g' }, | |
108 | { "sysname-match", required_argument, NULL, 'y' }, | |
109 | { "name-match", required_argument, NULL, ARG_NAME }, | |
110 | { "parent-match", required_argument, NULL, 'b' }, | |
111 | { "help", no_argument, NULL, 'h' }, | |
112 | {} | |
113 | }; | |
114 | enum { | |
115 | TYPE_DEVICES, | |
116 | TYPE_SUBSYSTEMS, | |
117 | } device_type = TYPE_DEVICES; | |
118 | const char *action = "change"; | |
119 | _cleanup_udev_enumerate_unref_ struct udev_enumerate *udev_enumerate = NULL; | |
120 | int c, r; | |
121 | ||
122 | udev_enumerate = udev_enumerate_new(udev); | |
123 | if (udev_enumerate == NULL) | |
124 | return 1; | |
125 | ||
126 | while ((c = getopt_long(argc, argv, "vno:t:c:s:S:a:A:p:g:y:b:h", options, NULL)) >= 0) { | |
127 | const char *key; | |
128 | const char *val; | |
129 | char buf[UTIL_PATH_SIZE]; | |
130 | ||
131 | switch (c) { | |
132 | case 'v': | |
133 | verbose = 1; | |
134 | break; | |
135 | case 'n': | |
136 | dry_run = 1; | |
137 | break; | |
138 | case 't': | |
139 | if (streq(optarg, "devices")) | |
140 | device_type = TYPE_DEVICES; | |
141 | else if (streq(optarg, "subsystems")) | |
142 | device_type = TYPE_SUBSYSTEMS; | |
143 | else { | |
144 | log_error("unknown type --type=%s", optarg); | |
145 | return 2; | |
146 | } | |
147 | break; | |
148 | case 'c': | |
149 | if (!nulstr_contains("add\0" "remove\0" "change\0", optarg)) { | |
150 | log_error("unknown action '%s'", optarg); | |
151 | return 2; | |
152 | } else | |
153 | action = optarg; | |
154 | ||
155 | break; | |
156 | case 's': | |
157 | r = udev_enumerate_add_match_subsystem(udev_enumerate, optarg); | |
158 | if (r < 0) { | |
159 | log_error_errno(r, "could not add subsystem match '%s': %m", optarg); | |
160 | return 2; | |
161 | } | |
162 | break; | |
163 | case 'S': | |
164 | r = udev_enumerate_add_nomatch_subsystem(udev_enumerate, optarg); | |
165 | if (r < 0) { | |
166 | log_error_errno(r, "could not add negative subsystem match '%s': %m", optarg); | |
167 | return 2; | |
168 | } | |
169 | break; | |
170 | case 'a': | |
171 | key = keyval(optarg, &val, buf, sizeof(buf)); | |
172 | r = udev_enumerate_add_match_sysattr(udev_enumerate, key, val); | |
173 | if (r < 0) { | |
174 | log_error_errno(r, "could not add sysattr match '%s=%s': %m", key, val); | |
175 | return 2; | |
176 | } | |
177 | break; | |
178 | case 'A': | |
179 | key = keyval(optarg, &val, buf, sizeof(buf)); | |
180 | r = udev_enumerate_add_nomatch_sysattr(udev_enumerate, key, val); | |
181 | if (r < 0) { | |
182 | log_error_errno(r, "could not add negative sysattr match '%s=%s': %m", key, val); | |
183 | return 2; | |
184 | } | |
185 | break; | |
186 | case 'p': | |
187 | key = keyval(optarg, &val, buf, sizeof(buf)); | |
188 | r = udev_enumerate_add_match_property(udev_enumerate, key, val); | |
189 | if (r < 0) { | |
190 | log_error_errno(r, "could not add property match '%s=%s': %m", key, val); | |
191 | return 2; | |
192 | } | |
193 | break; | |
194 | case 'g': | |
195 | r = udev_enumerate_add_match_tag(udev_enumerate, optarg); | |
196 | if (r < 0) { | |
197 | log_error_errno(r, "could not add tag match '%s': %m", optarg); | |
198 | return 2; | |
199 | } | |
200 | break; | |
201 | case 'y': | |
202 | r = udev_enumerate_add_match_sysname(udev_enumerate, optarg); | |
203 | if (r < 0) { | |
204 | log_error_errno(r, "could not add sysname match '%s': %m", optarg); | |
205 | return 2; | |
206 | } | |
207 | break; | |
208 | case 'b': { | |
209 | _cleanup_udev_device_unref_ struct udev_device *dev; | |
210 | ||
211 | dev = find_device(udev, optarg, "/sys"); | |
212 | if (dev == NULL) { | |
213 | log_error("unable to open the device '%s'", optarg); | |
214 | return 2; | |
215 | } | |
216 | ||
217 | r = udev_enumerate_add_match_parent(udev_enumerate, dev); | |
218 | if (r < 0) { | |
219 | log_error_errno(r, "could not add parent match '%s': %m", optarg); | |
220 | return 2; | |
221 | } | |
222 | break; | |
223 | } | |
224 | ||
225 | case ARG_NAME: { | |
226 | _cleanup_udev_device_unref_ struct udev_device *dev; | |
227 | ||
228 | dev = find_device(udev, optarg, "/dev/"); | |
229 | if (dev == NULL) { | |
230 | log_error("unable to open the device '%s'", optarg); | |
231 | return 2; | |
232 | } | |
233 | ||
234 | r = udev_enumerate_add_match_parent(udev_enumerate, dev); | |
235 | if (r < 0) { | |
236 | log_error_errno(r, "could not add parent match '%s': %m", optarg); | |
237 | return 2; | |
238 | } | |
239 | break; | |
240 | } | |
241 | ||
242 | case 'h': | |
243 | help(); | |
244 | return 0; | |
245 | case '?': | |
246 | return 1; | |
247 | default: | |
248 | assert_not_reached("Unknown option"); | |
249 | } | |
250 | } | |
251 | ||
252 | for (; optind < argc; optind++) { | |
253 | _cleanup_udev_device_unref_ struct udev_device *dev; | |
254 | ||
255 | dev = find_device(udev, argv[optind], NULL); | |
256 | if (dev == NULL) { | |
257 | log_error("unable to open the device '%s'", argv[optind]); | |
258 | return 2; | |
259 | } | |
260 | ||
261 | r = udev_enumerate_add_match_parent(udev_enumerate, dev); | |
262 | if (r < 0) { | |
263 | log_error_errno(r, "could not add tag match '%s': %m", optarg); | |
264 | return 2; | |
265 | } | |
266 | } | |
267 | ||
268 | switch (device_type) { | |
269 | case TYPE_SUBSYSTEMS: | |
270 | udev_enumerate_scan_subsystems(udev_enumerate); | |
271 | exec_list(udev_enumerate, action); | |
272 | return 0; | |
273 | case TYPE_DEVICES: | |
274 | udev_enumerate_scan_devices(udev_enumerate); | |
275 | exec_list(udev_enumerate, action); | |
276 | return 0; | |
277 | default: | |
278 | assert_not_reached("device_type"); | |
279 | } | |
280 | } | |
281 | ||
282 | const struct udevadm_cmd udevadm_trigger = { | |
283 | .name = "trigger", | |
284 | .cmd = adm_trigger, | |
285 | .help = "Request events from the kernel", | |
286 | }; |