2 * Copyright (C) 2004-2006 Kay Sievers <kay@vrfy.org>
3 * Copyright (C) 2006 Hannes Reinecke <hare@suse.de>
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.
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.
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.
32 #include <sys/types.h>
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
);
46 void log_message(int priority
, const char *format
, ...)
50 if (priority
> udev_log_priority
)
53 va_start(args
, format
);
54 vsyslog(priority
, format
, args
);
59 /* devices that should run last cause of their dependencies */
60 static int delay_device(const char *devpath
)
62 static const char *delay_device_list
[] = {
69 for (i
= 0; delay_device_list
[i
] != NULL
; i
++)
70 if (fnmatch(delay_device_list
[i
], devpath
, 0) == 0)
75 static int device_list_insert(const char *path
)
77 char filename
[PATH_SIZE
];
78 char devpath
[PATH_SIZE
];
81 dbg("add '%s'" , path
);
83 /* we only have a device, if we have an uevent file */
84 strlcpy(filename
, path
, sizeof(filename
));
85 strlcat(filename
, "/uevent", sizeof(filename
));
86 if (stat(filename
, &statbuf
) < 0)
88 if (!(statbuf
.st_mode
& S_IWUSR
))
91 strlcpy(devpath
, &path
[strlen(sysfs_path
)], sizeof(devpath
));
93 /* resolve possible link to real target */
94 if (lstat(path
, &statbuf
) < 0)
96 if (S_ISLNK(statbuf
.st_mode
))
97 if (sysfs_resolve_link(devpath
, sizeof(devpath
)) != 0)
100 name_list_add(&device_list
, devpath
, 1);
104 static void trigger_uevent(const char *devpath
)
106 char filename
[PATH_SIZE
];
109 strlcpy(filename
, sysfs_path
, sizeof(filename
));
110 strlcat(filename
, devpath
, sizeof(filename
));
111 strlcat(filename
, "/uevent", sizeof(filename
));
114 printf("%s\n", devpath
);
119 fd
= open(filename
, O_WRONLY
);
121 dbg("error on opening %s: %s", filename
, strerror(errno
));
125 if (write(fd
, "add", 3) < 0)
126 info("error on triggering %s: %s", filename
, strerror(errno
));
131 static void exec_list(void)
133 struct name_entry
*loop_device
;
134 struct name_entry
*tmp_device
;
136 list_for_each_entry_safe(loop_device
, tmp_device
, &device_list
, node
) {
137 if (delay_device(loop_device
->name
))
140 trigger_uevent(loop_device
->name
);
141 list_del(&loop_device
->node
);
145 /* trigger remaining delayed devices */
146 list_for_each_entry_safe(loop_device
, tmp_device
, &device_list
, node
) {
147 trigger_uevent(loop_device
->name
);
148 list_del(&loop_device
->node
);
153 static int subsystem_filtered(const char *subsystem
)
155 struct name_entry
*loop_name
;
157 /* skip devices matching the listed subsystems */
158 list_for_each_entry(loop_name
, &filter_subsystem_nomatch_list
, node
)
159 if (fnmatch(loop_name
->name
, subsystem
, 0) == 0)
162 /* skip devices not matching the listed subsystems */
163 if (!list_empty(&filter_subsystem_match_list
)) {
164 list_for_each_entry(loop_name
, &filter_subsystem_match_list
, node
)
165 if (fnmatch(loop_name
->name
, subsystem
, 0) == 0)
173 static int attr_match(const char *path
, const char *attr_value
)
175 char attr
[NAME_SIZE
];
176 char file
[PATH_SIZE
];
179 strlcpy(attr
, attr_value
, sizeof(attr
));
181 /* separate attr and match value */
182 match_value
= strchr(attr
, '=');
183 if (match_value
!= NULL
) {
184 match_value
[0] = '\0';
185 match_value
= &match_value
[1];
188 strlcpy(file
, path
, sizeof(file
));
189 strlcat(file
, "/", sizeof(file
));
190 strlcat(file
, attr
, sizeof(file
));
192 if (match_value
!= NULL
) {
193 /* match file content */
194 char value
[NAME_SIZE
];
198 fd
= open(file
, O_RDONLY
);
201 size
= read(fd
, value
, sizeof(value
));
206 remove_trailing_chars(value
, '\n');
208 /* match if attribute value matches */
209 if (fnmatch(match_value
, value
, 0) == 0)
212 /* match if attribute exists */
215 if (stat(file
, &statbuf
) == 0)
221 static int attr_filtered(const char *path
)
223 struct name_entry
*loop_name
;
225 /* skip devices matching the listed sysfs attributes */
226 list_for_each_entry(loop_name
, &filter_attr_nomatch_list
, node
)
227 if (attr_match(path
, loop_name
->name
))
230 /* skip devices not matching the listed sysfs attributes */
231 if (!list_empty(&filter_attr_match_list
)) {
232 list_for_each_entry(loop_name
, &filter_attr_match_list
, node
)
233 if (attr_match(path
, loop_name
->name
))
240 static void scan_subsystem(const char *subsys
)
242 char base
[PATH_SIZE
];
246 strlcpy(base
, sysfs_path
, sizeof(base
));
247 strlcat(base
, "/", sizeof(base
));
248 strlcat(base
, subsys
, sizeof(base
));
252 for (dent
= readdir(dir
); dent
!= NULL
; dent
= readdir(dir
)) {
253 char dirname
[PATH_SIZE
];
255 struct dirent
*dent2
;
257 if (dent
->d_name
[0] == '.')
260 if (subsystem_filtered(dent
->d_name
))
263 strlcpy(dirname
, base
, sizeof(dirname
));
264 strlcat(dirname
, "/", sizeof(dirname
));
265 strlcat(dirname
, dent
->d_name
, sizeof(dirname
));
266 strlcat(dirname
, "/devices", sizeof(dirname
));
268 /* look for devices */
269 dir2
= opendir(dirname
);
271 for (dent2
= readdir(dir2
); dent2
!= NULL
; dent2
= readdir(dir2
)) {
272 char dirname2
[PATH_SIZE
];
274 if (dent2
->d_name
[0] == '.')
277 strlcpy(dirname2
, dirname
, sizeof(dirname2
));
278 strlcat(dirname2
, "/", sizeof(dirname2
));
279 strlcat(dirname2
, dent2
->d_name
, sizeof(dirname2
));
280 if (attr_filtered(dirname2
))
282 device_list_insert(dirname2
);
291 static void scan_block(void)
293 char base
[PATH_SIZE
];
297 if (subsystem_filtered("block"))
300 strlcpy(base
, sysfs_path
, sizeof(base
));
301 strlcat(base
, "/block", sizeof(base
));
305 for (dent
= readdir(dir
); dent
!= NULL
; dent
= readdir(dir
)) {
306 char dirname
[PATH_SIZE
];
308 struct dirent
*dent2
;
310 if (dent
->d_name
[0] == '.')
313 strlcpy(dirname
, base
, sizeof(dirname
));
314 strlcat(dirname
, "/", sizeof(dirname
));
315 strlcat(dirname
, dent
->d_name
, sizeof(dirname
));
316 if (attr_filtered(dirname
))
318 if (device_list_insert(dirname
) != 0)
321 /* look for partitions */
322 dir2
= opendir(dirname
);
324 for (dent2
= readdir(dir2
); dent2
!= NULL
; dent2
= readdir(dir2
)) {
325 char dirname2
[PATH_SIZE
];
327 if (dent2
->d_name
[0] == '.')
330 if (!strcmp(dent2
->d_name
,"device"))
333 strlcpy(dirname2
, dirname
, sizeof(dirname2
));
334 strlcat(dirname2
, "/", sizeof(dirname2
));
335 strlcat(dirname2
, dent2
->d_name
, sizeof(dirname2
));
336 if (attr_filtered(dirname2
))
338 device_list_insert(dirname2
);
347 static void scan_class(void)
349 char base
[PATH_SIZE
];
353 strlcpy(base
, sysfs_path
, sizeof(base
));
354 strlcat(base
, "/class", sizeof(base
));
358 for (dent
= readdir(dir
); dent
!= NULL
; dent
= readdir(dir
)) {
359 char dirname
[PATH_SIZE
];
361 struct dirent
*dent2
;
363 if (dent
->d_name
[0] == '.')
366 if (subsystem_filtered(dent
->d_name
))
369 strlcpy(dirname
, base
, sizeof(dirname
));
370 strlcat(dirname
, "/", sizeof(dirname
));
371 strlcat(dirname
, dent
->d_name
, sizeof(dirname
));
372 dir2
= opendir(dirname
);
374 for (dent2
= readdir(dir2
); dent2
!= NULL
; dent2
= readdir(dir2
)) {
375 char dirname2
[PATH_SIZE
];
377 if (dent2
->d_name
[0] == '.')
380 if (!strcmp(dent2
->d_name
, "device"))
383 strlcpy(dirname2
, dirname
, sizeof(dirname2
));
384 strlcat(dirname2
, "/", sizeof(dirname2
));
385 strlcat(dirname2
, dent2
->d_name
, sizeof(dirname2
));
386 if (attr_filtered(dirname2
))
388 device_list_insert(dirname2
);
397 static void scan_failed(void)
399 char base
[PATH_SIZE
];
403 strlcpy(base
, udev_root
, sizeof(base
));
404 strlcat(base
, "/" EVENT_FAILED_DIR
, sizeof(base
));
408 for (dent
= readdir(dir
); dent
!= NULL
; dent
= readdir(dir
)) {
409 char device
[PATH_SIZE
];
412 if (dent
->d_name
[0] == '.')
415 start
= strlcpy(device
, sysfs_path
, sizeof(device
));
416 strlcat(device
, dent
->d_name
, sizeof(device
));
417 path_decode(&device
[start
]);
418 device_list_insert(device
);
424 int main(int argc
, char *argv
[], char *envp
[])
428 static const struct option options
[] = {
429 { "verbose", 0, NULL
, 'v' },
430 { "dry-run", 0, NULL
, 'n' },
431 { "retry-failed", 0, NULL
, 'F' },
432 { "help", 0, NULL
, 'h' },
433 { "subsystem-match", 1, NULL
, 's' },
434 { "subsystem-nomatch", 1, NULL
, 'S' },
435 { "attr-match", 1, NULL
, 'a' },
436 { "attr-nomatch", 1, NULL
, 'A' },
440 logging_init("udevtrigger");
441 dbg("version %s", UDEV_VERSION
);
445 option
= getopt_long(argc
, argv
, "vnFhs:S:a:A:", options
, NULL
);
460 name_list_add(&filter_subsystem_match_list
, optarg
, 0);
463 name_list_add(&filter_subsystem_nomatch_list
, optarg
, 0);
466 name_list_add(&filter_attr_match_list
, optarg
, 0);
469 name_list_add(&filter_attr_nomatch_list
, optarg
, 0);
472 printf("Usage: udevtrigger OPTIONS\n"
473 " --verbose print the list of devices while running\n"
474 " --dry-run do not actually trigger the events\n"
475 " --retry-failed trigger only the events which have been\n"
476 " marked as failed during a previous run\n"
477 " --subsystem-match=<subsystem> trigger devices from a matching subystem\n"
478 " --subsystem-nomatch=<subsystem> exclude devices from a matching subystem\n"
479 " --attr-match=<file[=<value>]> trigger devices with a matching sysfs\n"
481 " --attr-nomatch=<file[=<value>]> exclude devices with a matching sysfs\n"
483 " --help print this text\n"
494 char base
[PATH_SIZE
];
497 /* if we have /sys/subsystem, forget all the old stuff */
498 strlcpy(base
, sysfs_path
, sizeof(base
));
499 strlcat(base
, "/subsystem", sizeof(base
));
500 if (stat(base
, &statbuf
) == 0)
501 scan_subsystem("subsystem");
503 scan_subsystem("bus");
506 /* scan "block" if it isn't a "class" */
507 strlcpy(base
, sysfs_path
, sizeof(base
));
508 strlcat(base
, "/class/block", sizeof(base
));
509 if (stat(base
, &statbuf
) != 0)
516 name_list_cleanup(&filter_subsystem_match_list
);
517 name_list_cleanup(&filter_subsystem_nomatch_list
);
518 name_list_cleanup(&filter_attr_match_list
);
519 name_list_cleanup(&filter_attr_nomatch_list
);