chiark / gitweb /
volume_id: fix UUID raw buffer usage
[elogind.git] / udevtrigger.c
1 /*
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.,
16  *      51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
17  *
18  */
19
20 #include <stdlib.h>
21 #include <stddef.h>
22 #include <string.h>
23 #include <stdio.h>
24 #include <unistd.h>
25 #include <getopt.h>
26 #include <errno.h>
27 #include <dirent.h>
28 #include <fcntl.h>
29 #include <syslog.h>
30 #include <fnmatch.h>
31 #include <sys/stat.h>
32 #include <sys/types.h>
33
34 #include "udev.h"
35 #include "udevd.h"
36
37 static int verbose;
38 static int dry_run;
39 LIST_HEAD(device_list);
40 LIST_HEAD(filter_subsystem_match_list);
41 LIST_HEAD(filter_subsystem_nomatch_list);
42 LIST_HEAD(filter_attr_match_list);
43 LIST_HEAD(filter_attr_nomatch_list);
44
45 /* devices that should run last cause of their dependencies */
46 static int delay_device(const char *devpath)
47 {
48         static const char *delay_device_list[] = {
49                 "*/md*",
50                 "*/dm-*",
51                 NULL
52         };
53         int i;
54
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 }
60
61 static int device_list_insert(const char *path)
62 {
63         char filename[PATH_SIZE];
64         char devpath[PATH_SIZE];
65         struct stat statbuf;
66
67         dbg("add '%s'" , path);
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);
87         return 0;
88 }
89
90 static void trigger_uevent(const char *devpath, const char *action)
91 {
92         char filename[PATH_SIZE];
93         int fd;
94
95         strlcpy(filename, sysfs_path, sizeof(filename));
96         strlcat(filename, devpath, sizeof(filename));
97         strlcat(filename, "/uevent", sizeof(filename));
98
99         if (verbose)
100                 printf("%s\n", devpath);
101
102         if (dry_run)
103                 return;
104
105         fd = open(filename, O_WRONLY);
106         if (fd < 0) {
107                 dbg("error on opening %s: %s", filename, strerror(errno));
108                 return;
109         }
110
111         if (write(fd, action, strlen(action)) < 0)
112                 info("error writing '%s' to '%s': %s", action, filename, strerror(errno));
113
114         close(fd);
115 }
116
117 static void exec_list(const char *action)
118 {
119         struct name_entry *loop_device;
120         struct name_entry *tmp_device;
121
122         list_for_each_entry_safe(loop_device, tmp_device, &device_list, node) {
123                 if (delay_device(loop_device->name))
124                         continue;
125
126                 trigger_uevent(loop_device->name, action);
127                 list_del(&loop_device->node);
128                 free(loop_device);
129         }
130
131         /* trigger remaining delayed devices */
132         list_for_each_entry_safe(loop_device, tmp_device, &device_list, node) {
133                 trigger_uevent(loop_device->name, action);
134                 list_del(&loop_device->node);
135                 free(loop_device);
136         }
137 }
138
139 static int subsystem_filtered(const char *subsystem)
140 {
141         struct name_entry *loop_name;
142
143         /* skip devices matching the listed subsystems */
144         list_for_each_entry(loop_name, &filter_subsystem_nomatch_list, node)
145                 if (fnmatch(loop_name->name, subsystem, 0) == 0)
146                         return 1;
147
148         /* skip devices not matching the listed subsystems */
149         if (!list_empty(&filter_subsystem_match_list)) {
150                 list_for_each_entry(loop_name, &filter_subsystem_match_list, node)
151                         if (fnmatch(loop_name->name, subsystem, 0) == 0)
152                                 return 0;
153                 return 1;
154         }
155
156         return 0;
157 }
158
159 static 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
207 static 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
226 enum scan_type {
227         SCAN_DEVICES,
228         SCAN_SUBSYSTEM,
229 };
230
231 static void scan_subsystem(const char *subsys, enum scan_type scan)
232 {
233         char base[PATH_SIZE];
234         DIR *dir;
235         struct dirent *dent;
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;
244
245         strlcpy(base, sysfs_path, sizeof(base));
246         strlcat(base, "/", sizeof(base));
247         strlcat(base, subsys, sizeof(base));
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
259                         if (scan == SCAN_DEVICES)
260                                 if (subsystem_filtered(dent->d_name))
261                                         continue;
262
263                         strlcpy(dirname, base, sizeof(dirname));
264                         strlcat(dirname, "/", sizeof(dirname));
265                         strlcat(dirname, dent->d_name, sizeof(dirname));
266
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 */
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));
288                                         if (attr_filtered(dirname2))
289                                                 continue;
290                                         device_list_insert(dirname2);
291                                 }
292                                 closedir(dir2);
293                         }
294                 }
295                 closedir(dir);
296         }
297 }
298
299 static void scan_block(void)
300 {
301         char base[PATH_SIZE];
302         DIR *dir;
303         struct dirent *dent;
304
305         if (subsystem_filtered("block"))
306                 return;
307
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));
324                         if (attr_filtered(dirname))
325                                 continue;
326                         if (device_list_insert(dirname) != 0)
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));
344                                         if (attr_filtered(dirname2))
345                                                 continue;
346                                         device_list_insert(dirname2);
347                                 }
348                                 closedir(dir2);
349                         }
350                 }
351                 closedir(dir);
352         }
353 }
354
355 static void scan_class(void)
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
374                         if (subsystem_filtered(dent->d_name))
375                                 continue;
376
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));
394                                         if (attr_filtered(dirname2))
395                                                 continue;
396                                         device_list_insert(dirname2);
397                                 }
398                                 closedir(dir2);
399                         }
400                 }
401                 closedir(dir);
402         }
403 }
404
405 static 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));
412         strlcat(base, "/" EVENT_FAILED_DIR, sizeof(base));
413
414         dir = opendir(base);
415         if (dir != NULL) {
416                 for (dent = readdir(dir); dent != NULL; dent = readdir(dir)) {
417                         char device[PATH_SIZE];
418                         size_t start;
419
420                         if (dent->d_name[0] == '.')
421                                 continue;
422
423                         start = strlcpy(device, sysfs_path, sizeof(device));
424                         if(start >= sizeof(device))
425                                 start = sizeof(device) - 1;
426                         strlcat(device, dent->d_name, sizeof(device));
427                         path_decode(&device[start]);
428                         device_list_insert(device);
429                 }
430                 closedir(dir);
431         }
432 }
433
434 int udevtrigger(int argc, char *argv[], char *envp[])
435 {
436         int failed = 0;
437         int option;
438         const char *action = "add";
439         static const struct option options[] = {
440                 { "verbose", 0, NULL, 'v' },
441                 { "dry-run", 0, NULL, 'n' },
442                 { "retry-failed", 0, NULL, 'F' },
443                 { "help", 0, NULL, 'h' },
444                 { "action", 1, NULL, 'c' },
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         };
451
452         logging_init("udevtrigger");
453         udev_config_init();
454         dbg("version %s", UDEV_VERSION);
455         sysfs_init();
456
457         while (1) {
458                 option = getopt_long(argc, argv, "vnFhc:s:S:a:A:", options, NULL);
459                 if (option == -1)
460                         break;
461
462                 switch (option) {
463                 case 'v':
464                         verbose = 1;
465                         break;
466                 case 'n':
467                         dry_run = 1;
468                         break;
469                 case 'F':
470                         failed = 1;
471                         break;
472                 case 'c':
473                         action = optarg;
474                         break;
475                 case 's':
476                         name_list_add(&filter_subsystem_match_list, optarg, 0);
477                         break;
478                 case 'S':
479                         name_list_add(&filter_subsystem_nomatch_list, optarg, 0);
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':
488                         printf("Usage: udevadm trigger OPTIONS\n"
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"
500                                "\n");
501                         goto exit;
502                 default:
503                         goto exit;
504                 }
505         }
506
507         if (failed) {
508                 scan_failed();
509                 exec_list(action);
510         } else {
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));
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);
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();
533                         exec_list(action);
534                 }
535         }
536
537 exit:
538         name_list_cleanup(&filter_subsystem_match_list);
539         name_list_cleanup(&filter_subsystem_nomatch_list);
540         name_list_cleanup(&filter_attr_match_list);
541         name_list_cleanup(&filter_attr_nomatch_list);
542
543         sysfs_cleanup();
544         logging_close();
545         return 0;
546 }