]> git.ipfire.org Git - thirdparty/systemd.git/blame - udevtrigger.c
rules: remove last WAIT_FOR_SYSFS, load ppdev, switch scsi_device
[thirdparty/systemd.git] / udevtrigger.c
CommitLineData
0d5be398 1/*
0d5be398
KS
2 * Copyright (C) 2004-2006 Kay Sievers <kay@vrfy.org>
3 * Copyright (C) 2006 Hannes Reinecke <hare@suse.de>
4 *
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License as published by the
7 * Free Software Foundation version 2 of the License.
8 *
9 * This program is distributed in the hope that it will be useful, but
10 * WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License along
15 * with this program; if not, write to the Free Software Foundation, Inc.,
27b77df4 16 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
0d5be398
KS
17 *
18 */
19
20#include <stdlib.h>
21#include <stddef.h>
22#include <string.h>
23#include <stdio.h>
24#include <unistd.h>
fc89fe7e 25#include <getopt.h>
0d5be398
KS
26#include <errno.h>
27#include <dirent.h>
28#include <fcntl.h>
29#include <syslog.h>
fc89fe7e 30#include <fnmatch.h>
0d5be398
KS
31#include <sys/stat.h>
32#include <sys/types.h>
33
34#include "udev.h"
04cc3a81 35#include "udevd.h"
0d5be398 36
c48622cc
KS
37static int verbose;
38static int dry_run;
39LIST_HEAD(device_list);
40LIST_HEAD(filter_subsystem_match_list);
41LIST_HEAD(filter_subsystem_nomatch_list);
42LIST_HEAD(filter_attr_match_list);
43LIST_HEAD(filter_attr_nomatch_list);
44
c48622cc
KS
45/* devices that should run last cause of their dependencies */
46static int delay_device(const char *devpath)
47{
48 static const char *delay_device_list[] = {
49 "*/md*",
50 "*/dm-*",
51 NULL
52 };
53 int i;
0d5be398 54
c48622cc
KS
55 for (i = 0; delay_device_list[i] != NULL; i++)
56 if (fnmatch(delay_device_list[i], devpath, 0) == 0)
57 return 1;
58 return 0;
59}
fc89fe7e 60
0d5be398
KS
61static int device_list_insert(const char *path)
62{
c48622cc
KS
63 char filename[PATH_SIZE];
64 char devpath[PATH_SIZE];
65 struct stat statbuf;
0d5be398
KS
66
67 dbg("add '%s'" , path);
c48622cc
KS
68
69 /* we only have a device, if we have an uevent file */
70 strlcpy(filename, path, sizeof(filename));
71 strlcat(filename, "/uevent", sizeof(filename));
72 if (stat(filename, &statbuf) < 0)
73 return -1;
74 if (!(statbuf.st_mode & S_IWUSR))
75 return -1;
76
77 strlcpy(devpath, &path[strlen(sysfs_path)], sizeof(devpath));
78
79 /* resolve possible link to real target */
80 if (lstat(path, &statbuf) < 0)
81 return -1;
82 if (S_ISLNK(statbuf.st_mode))
83 if (sysfs_resolve_link(devpath, sizeof(devpath)) != 0)
84 return -1;
85
86 name_list_add(&device_list, devpath, 1);
0d5be398
KS
87 return 0;
88}
89
285e2a24 90static void trigger_uevent(const char *devpath, const char *action)
0d5be398
KS
91{
92 char filename[PATH_SIZE];
93 int fd;
94
c48622cc
KS
95 strlcpy(filename, sysfs_path, sizeof(filename));
96 strlcat(filename, devpath, sizeof(filename));
0d5be398
KS
97 strlcat(filename, "/uevent", sizeof(filename));
98
99 if (verbose)
c48622cc 100 printf("%s\n", devpath);
0d5be398
KS
101
102 if (dry_run)
103 return;
104
105 fd = open(filename, O_WRONLY);
106 if (fd < 0) {
92b229c7 107 dbg("error on opening %s: %s", filename, strerror(errno));
0d5be398
KS
108 return;
109 }
110
285e2a24
KS
111 if (write(fd, action, strlen(action)) < 0)
112 info("error writing '%s' to '%s': %s", action, filename, strerror(errno));
0d5be398
KS
113
114 close(fd);
115}
116
285e2a24 117static void exec_list(const char *action)
0d5be398
KS
118{
119 struct name_entry *loop_device;
120 struct name_entry *tmp_device;
121
c48622cc
KS
122 list_for_each_entry_safe(loop_device, tmp_device, &device_list, node) {
123 if (delay_device(loop_device->name))
124 continue;
0d5be398 125
285e2a24 126 trigger_uevent(loop_device->name, action);
0d5be398
KS
127 list_del(&loop_device->node);
128 free(loop_device);
129 }
130
c48622cc
KS
131 /* trigger remaining delayed devices */
132 list_for_each_entry_safe(loop_device, tmp_device, &device_list, node) {
285e2a24 133 trigger_uevent(loop_device->name, action);
0d5be398
KS
134 list_del(&loop_device->node);
135 free(loop_device);
136 }
137}
138
fc89fe7e
KS
139static int subsystem_filtered(const char *subsystem)
140{
141 struct name_entry *loop_name;
142
143 /* skip devices matching the listed subsystems */
f14326ad 144 list_for_each_entry(loop_name, &filter_subsystem_nomatch_list, node)
7c27c752 145 if (fnmatch(loop_name->name, subsystem, 0) == 0)
fc89fe7e
KS
146 return 1;
147
148 /* skip devices not matching the listed subsystems */
f14326ad
SV
149 if (!list_empty(&filter_subsystem_match_list)) {
150 list_for_each_entry(loop_name, &filter_subsystem_match_list, node)
7c27c752 151 if (fnmatch(loop_name->name, subsystem, 0) == 0)
fc89fe7e
KS
152 return 0;
153 return 1;
154 }
155
156 return 0;
157}
158
159static int attr_match(const char *path, const char *attr_value)
160{
161 char attr[NAME_SIZE];
162 char file[PATH_SIZE];
163 char *match_value;
164
165 strlcpy(attr, attr_value, sizeof(attr));
166
167 /* separate attr and match value */
168 match_value = strchr(attr, '=');
169 if (match_value != NULL) {
170 match_value[0] = '\0';
171 match_value = &match_value[1];
172 }
173
174 strlcpy(file, path, sizeof(file));
175 strlcat(file, "/", sizeof(file));
176 strlcat(file, attr, sizeof(file));
177
178 if (match_value != NULL) {
179 /* match file content */
180 char value[NAME_SIZE];
181 int fd;
182 ssize_t size;
183
184 fd = open(file, O_RDONLY);
185 if (fd < 0)
186 return 0;
187 size = read(fd, value, sizeof(value));
188 close(fd);
189 if (size < 0)
190 return 0;
191 value[size] = '\0';
192 remove_trailing_chars(value, '\n');
193
194 /* match if attribute value matches */
195 if (fnmatch(match_value, value, 0) == 0)
196 return 1;
197 } else {
198 /* match if attribute exists */
199 struct stat statbuf;
200
201 if (stat(file, &statbuf) == 0)
202 return 1;
203 }
204 return 0;
205}
206
207static int attr_filtered(const char *path)
208{
209 struct name_entry *loop_name;
210
211 /* skip devices matching the listed sysfs attributes */
212 list_for_each_entry(loop_name, &filter_attr_nomatch_list, node)
213 if (attr_match(path, loop_name->name))
214 return 1;
215
216 /* skip devices not matching the listed sysfs attributes */
217 if (!list_empty(&filter_attr_match_list)) {
218 list_for_each_entry(loop_name, &filter_attr_match_list, node)
219 if (attr_match(path, loop_name->name))
220 return 0;
221 return 1;
222 }
223 return 0;
224}
225
584fbf1b
KS
226enum scan_type {
227 SCAN_DEVICES,
228 SCAN_SUBSYSTEM,
229};
230
231static void scan_subsystem(const char *subsys, enum scan_type scan)
0d5be398
KS
232{
233 char base[PATH_SIZE];
234 DIR *dir;
235 struct dirent *dent;
584fbf1b
KS
236 const char *subdir;
237
238 if (scan == SCAN_DEVICES)
239 subdir = "/devices";
240 else if (scan == SCAN_SUBSYSTEM)
241 subdir = "/drivers";
242 else
243 return;
0d5be398
KS
244
245 strlcpy(base, sysfs_path, sizeof(base));
5ac28543
KS
246 strlcat(base, "/", sizeof(base));
247 strlcat(base, subsys, sizeof(base));
0d5be398
KS
248
249 dir = opendir(base);
250 if (dir != NULL) {
251 for (dent = readdir(dir); dent != NULL; dent = readdir(dir)) {
252 char dirname[PATH_SIZE];
253 DIR *dir2;
254 struct dirent *dent2;
255
256 if (dent->d_name[0] == '.')
257 continue;
258
584fbf1b
KS
259 if (scan == SCAN_DEVICES)
260 if (subsystem_filtered(dent->d_name))
261 continue;
fc89fe7e 262
0d5be398
KS
263 strlcpy(dirname, base, sizeof(dirname));
264 strlcat(dirname, "/", sizeof(dirname));
265 strlcat(dirname, dent->d_name, sizeof(dirname));
0d5be398 266
584fbf1b
KS
267 if (scan == SCAN_SUBSYSTEM) {
268 if (!subsystem_filtered("subsystem"))
269 device_list_insert(dirname);
270 if (subsystem_filtered("drivers"))
271 continue;
272 }
273
274 strlcat(dirname, subdir, sizeof(dirname));
275
276 /* look for devices/drivers */
0d5be398
KS
277 dir2 = opendir(dirname);
278 if (dir2 != NULL) {
279 for (dent2 = readdir(dir2); dent2 != NULL; dent2 = readdir(dir2)) {
280 char dirname2[PATH_SIZE];
281
282 if (dent2->d_name[0] == '.')
283 continue;
284
285 strlcpy(dirname2, dirname, sizeof(dirname2));
286 strlcat(dirname2, "/", sizeof(dirname2));
287 strlcat(dirname2, dent2->d_name, sizeof(dirname2));
fc89fe7e
KS
288 if (attr_filtered(dirname2))
289 continue;
c48622cc 290 device_list_insert(dirname2);
0d5be398
KS
291 }
292 closedir(dir2);
293 }
294 }
295 closedir(dir);
296 }
297}
298
04cc3a81 299static void scan_block(void)
0d5be398
KS
300{
301 char base[PATH_SIZE];
302 DIR *dir;
303 struct dirent *dent;
0d5be398 304
fc89fe7e
KS
305 if (subsystem_filtered("block"))
306 return;
307
0d5be398
KS
308 strlcpy(base, sysfs_path, sizeof(base));
309 strlcat(base, "/block", sizeof(base));
310
311 dir = opendir(base);
312 if (dir != NULL) {
313 for (dent = readdir(dir); dent != NULL; dent = readdir(dir)) {
314 char dirname[PATH_SIZE];
315 DIR *dir2;
316 struct dirent *dent2;
317
318 if (dent->d_name[0] == '.')
319 continue;
320
321 strlcpy(dirname, base, sizeof(dirname));
322 strlcat(dirname, "/", sizeof(dirname));
323 strlcat(dirname, dent->d_name, sizeof(dirname));
fc89fe7e
KS
324 if (attr_filtered(dirname))
325 continue;
d6e39538 326 if (device_list_insert(dirname) != 0)
0d5be398
KS
327 continue;
328
329 /* look for partitions */
330 dir2 = opendir(dirname);
331 if (dir2 != NULL) {
332 for (dent2 = readdir(dir2); dent2 != NULL; dent2 = readdir(dir2)) {
333 char dirname2[PATH_SIZE];
334
335 if (dent2->d_name[0] == '.')
336 continue;
337
338 if (!strcmp(dent2->d_name,"device"))
339 continue;
340
341 strlcpy(dirname2, dirname, sizeof(dirname2));
342 strlcat(dirname2, "/", sizeof(dirname2));
343 strlcat(dirname2, dent2->d_name, sizeof(dirname2));
fc89fe7e
KS
344 if (attr_filtered(dirname2))
345 continue;
c48622cc 346 device_list_insert(dirname2);
0d5be398
KS
347 }
348 closedir(dir2);
349 }
350 }
351 closedir(dir);
352 }
353}
354
04cc3a81 355static void scan_class(void)
0d5be398
KS
356{
357 char base[PATH_SIZE];
358 DIR *dir;
359 struct dirent *dent;
360
361 strlcpy(base, sysfs_path, sizeof(base));
362 strlcat(base, "/class", sizeof(base));
363
364 dir = opendir(base);
365 if (dir != NULL) {
366 for (dent = readdir(dir); dent != NULL; dent = readdir(dir)) {
367 char dirname[PATH_SIZE];
368 DIR *dir2;
369 struct dirent *dent2;
370
371 if (dent->d_name[0] == '.')
372 continue;
373
fc89fe7e
KS
374 if (subsystem_filtered(dent->d_name))
375 continue;
376
0d5be398
KS
377 strlcpy(dirname, base, sizeof(dirname));
378 strlcat(dirname, "/", sizeof(dirname));
379 strlcat(dirname, dent->d_name, sizeof(dirname));
380 dir2 = opendir(dirname);
381 if (dir2 != NULL) {
382 for (dent2 = readdir(dir2); dent2 != NULL; dent2 = readdir(dir2)) {
383 char dirname2[PATH_SIZE];
384
385 if (dent2->d_name[0] == '.')
386 continue;
387
388 if (!strcmp(dent2->d_name, "device"))
389 continue;
390
391 strlcpy(dirname2, dirname, sizeof(dirname2));
392 strlcat(dirname2, "/", sizeof(dirname2));
393 strlcat(dirname2, dent2->d_name, sizeof(dirname2));
fc89fe7e
KS
394 if (attr_filtered(dirname2))
395 continue;
c48622cc 396 device_list_insert(dirname2);
0d5be398
KS
397 }
398 closedir(dir2);
399 }
400 }
401 closedir(dir);
402 }
403}
404
04cc3a81
KS
405static void scan_failed(void)
406{
407 char base[PATH_SIZE];
408 DIR *dir;
409 struct dirent *dent;
410
411 strlcpy(base, udev_root, sizeof(base));
051445e0 412 strlcat(base, "/" EVENT_FAILED_DIR, sizeof(base));
04cc3a81
KS
413
414 dir = opendir(base);
415 if (dir != NULL) {
416 for (dent = readdir(dir); dent != NULL; dent = readdir(dir)) {
051445e0 417 char device[PATH_SIZE];
9c6ad9fb 418 size_t start;
04cc3a81
KS
419
420 if (dent->d_name[0] == '.')
421 continue;
422
fc6da921 423 start = strlcpy(device, sysfs_path, sizeof(device));
1f7a36f2
MM
424 if(start >= sizeof(device))
425 start = sizeof(device) - 1;
9c6ad9fb
KS
426 strlcat(device, dent->d_name, sizeof(device));
427 path_decode(&device[start]);
c48622cc 428 device_list_insert(device);
04cc3a81
KS
429 }
430 closedir(dir);
431 }
432}
433
225cb03b 434int udevtrigger(int argc, char *argv[], char *envp[])
0d5be398 435{
e3396a2d 436 int failed = 0;
fc89fe7e 437 int option;
285e2a24 438 const char *action = "add";
e97717ba 439 static const struct option options[] = {
fc89fe7e
KS
440 { "verbose", 0, NULL, 'v' },
441 { "dry-run", 0, NULL, 'n' },
442 { "retry-failed", 0, NULL, 'F' },
443 { "help", 0, NULL, 'h' },
285e2a24 444 { "action", 1, NULL, 'c' },
fc89fe7e
KS
445 { "subsystem-match", 1, NULL, 's' },
446 { "subsystem-nomatch", 1, NULL, 'S' },
447 { "attr-match", 1, NULL, 'a' },
448 { "attr-nomatch", 1, NULL, 'A' },
449 {}
450 };
0d5be398
KS
451
452 logging_init("udevtrigger");
453 udev_config_init();
454 dbg("version %s", UDEV_VERSION);
04cc3a81 455 sysfs_init();
0d5be398 456
fc89fe7e 457 while (1) {
285e2a24 458 option = getopt_long(argc, argv, "vnFhc:s:S:a:A:", options, NULL);
fc89fe7e
KS
459 if (option == -1)
460 break;
0d5be398 461
fc89fe7e
KS
462 switch (option) {
463 case 'v':
0d5be398 464 verbose = 1;
fc89fe7e
KS
465 break;
466 case 'n':
0d5be398 467 dry_run = 1;
fc89fe7e
KS
468 break;
469 case 'F':
e3396a2d 470 failed = 1;
fc89fe7e 471 break;
285e2a24
KS
472 case 'c':
473 action = optarg;
474 break;
fc89fe7e 475 case 's':
f14326ad 476 name_list_add(&filter_subsystem_match_list, optarg, 0);
fc89fe7e
KS
477 break;
478 case 'S':
f14326ad 479 name_list_add(&filter_subsystem_nomatch_list, optarg, 0);
fc89fe7e
KS
480 break;
481 case 'a':
482 name_list_add(&filter_attr_match_list, optarg, 0);
483 break;
484 case 'A':
485 name_list_add(&filter_attr_nomatch_list, optarg, 0);
486 break;
487 case 'h':
225cb03b 488 printf("Usage: udevadm trigger OPTIONS\n"
7c27c752
KS
489 " --verbose print the list of devices while running\n"
490 " --dry-run do not actually trigger the events\n"
491 " --retry-failed trigger only the events which have been\n"
492 " marked as failed during a previous run\n"
493 " --subsystem-match=<subsystem> trigger devices from a matching subystem\n"
494 " --subsystem-nomatch=<subsystem> exclude devices from a matching subystem\n"
495 " --attr-match=<file[=<value>]> trigger devices with a matching sysfs\n"
496 " attribute\n"
497 " --attr-nomatch=<file[=<value>]> exclude devices with a matching sysfs\n"
498 " attribute\n"
499 " --help print this text\n"
fc89fe7e
KS
500 "\n");
501 goto exit;
502 default:
04cc3a81 503 goto exit;
0d5be398
KS
504 }
505 }
506
584fbf1b 507 if (failed) {
e3396a2d 508 scan_failed();
584fbf1b
KS
509 exec_list(action);
510 } else {
5ac28543
KS
511 char base[PATH_SIZE];
512 struct stat statbuf;
513
514 /* if we have /sys/subsystem, forget all the old stuff */
515 strlcpy(base, sysfs_path, sizeof(base));
516 strlcat(base, "/subsystem", sizeof(base));
584fbf1b
KS
517 if (stat(base, &statbuf) == 0) {
518 scan_subsystem("subsystem", SCAN_SUBSYSTEM);
519 exec_list(action);
520 scan_subsystem("subsystem", SCAN_DEVICES);
521 exec_list(action);
522 } else {
523 scan_subsystem("bus", SCAN_SUBSYSTEM);
524 exec_list(action);
525 scan_subsystem("bus", SCAN_DEVICES);
5ac28543
KS
526 scan_class();
527
528 /* scan "block" if it isn't a "class" */
529 strlcpy(base, sysfs_path, sizeof(base));
530 strlcat(base, "/class/block", sizeof(base));
531 if (stat(base, &statbuf) != 0)
532 scan_block();
584fbf1b 533 exec_list(action);
5ac28543 534 }
e3396a2d 535 }
0d5be398 536
0d5be398 537exit:
f14326ad
SV
538 name_list_cleanup(&filter_subsystem_match_list);
539 name_list_cleanup(&filter_subsystem_nomatch_list);
fc89fe7e
KS
540 name_list_cleanup(&filter_attr_match_list);
541 name_list_cleanup(&filter_attr_nomatch_list);
542
04cc3a81 543 sysfs_cleanup();
0d5be398
KS
544 logging_close();
545 return 0;
546}