]> git.ipfire.org Git - thirdparty/systemd.git/blob - udevtrigger.c
path_id: add bus to USB path
[thirdparty/systemd.git] / udevtrigger.c
1 /*
2 * udevtrigger.c
3 *
4 * Copyright (C) 2004-2006 Kay Sievers <kay@vrfy.org>
5 * Copyright (C) 2006 Hannes Reinecke <hare@suse.de>
6 *
7 * This program is free software; you can redistribute it and/or modify it
8 * under the terms of the GNU General Public License as published by the
9 * Free Software Foundation version 2 of the License.
10 *
11 * This program is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License along
17 * with this program; if not, write to the Free Software Foundation, Inc.,
18 * 675 Mass Ave, Cambridge, MA 02139, USA.
19 *
20 */
21
22 #include <stdlib.h>
23 #include <stddef.h>
24 #include <string.h>
25 #include <stdio.h>
26 #include <unistd.h>
27 #include <errno.h>
28 #include <dirent.h>
29 #include <fcntl.h>
30 #include <syslog.h>
31 #include <sys/stat.h>
32 #include <sys/types.h>
33
34 #include "udev.h"
35
36 static const char *udev_log_str;
37 static int verbose;
38 static int dry_run;
39
40 #ifdef USE_LOG
41 void log_message(int priority, const char *format, ...)
42 {
43 va_list args;
44
45 if (priority > udev_log_priority)
46 return;
47
48 va_start(args, format);
49 vsyslog(priority, format, args);
50 va_end(args);
51 }
52 #endif
53
54 /* list of devices that we should run last due to any one of a number of reasons */
55 static char *last_list[] = {
56 "/class/block/md",
57 "/class/block/dm-",
58 "/block/md",
59 "/block/dm-",
60 NULL
61 };
62
63 /* list of devices that we should run first due to any one of a number of reasons */
64 static char *first_list[] = {
65 "/class/mem",
66 "/class/tty",
67 NULL
68 };
69
70 LIST_HEAD(device_first_list);
71 LIST_HEAD(device_default_list);
72 LIST_HEAD(device_last_list);
73
74 static int device_list_insert(const char *path)
75 {
76 struct list_head *device_list = &device_default_list;
77 int i;
78
79 for (i = 0; first_list[i] != NULL; i++) {
80 if (strncmp(path, first_list[i], strlen(first_list[i])) == 0) {
81 device_list = &device_first_list;
82 break;
83 }
84 }
85 for (i = 0; last_list[i] != NULL; i++) {
86 if (strncmp(path, last_list[i], strlen(last_list[i])) == 0) {
87 device_list = &device_last_list;
88 break;
89 }
90 }
91
92 dbg("add '%s'" , path);
93 /* double entries will be ignored */
94 name_list_add(device_list, path, 0);
95 return 0;
96 }
97
98 static void trigger_uevent(const char *path)
99 {
100 char filename[PATH_SIZE];
101 int fd;
102
103 strlcpy(filename, path, sizeof(filename));
104 strlcat(filename, "/uevent", sizeof(filename));
105
106 if (verbose)
107 printf("%s\n", path);
108
109 if (dry_run)
110 return;
111
112 fd = open(filename, O_WRONLY);
113 if (fd < 0) {
114 dbg("error on opening %s: %s\n", filename, strerror(errno));
115 return;
116 }
117
118 if (write(fd, "add", 3) < 0)
119 info("error on triggering %s: %s\n", filename, strerror(errno));
120
121 close(fd);
122 }
123
124 static void exec_lists(void)
125 {
126 struct name_entry *loop_device;
127 struct name_entry *tmp_device;
128
129 /* handle the devices on the "first" list first */
130 list_for_each_entry_safe(loop_device, tmp_device, &device_first_list, node) {
131 trigger_uevent(loop_device->name);
132 list_del(&loop_device->node);
133 free(loop_device);
134 }
135
136 /* handle the devices on the "default" list next */
137 list_for_each_entry_safe(loop_device, tmp_device, &device_default_list, node) {
138 trigger_uevent(loop_device->name);
139 list_del(&loop_device->node);
140 free(loop_device);
141 }
142
143 /* handle devices on the "last" list, if any */
144 list_for_each_entry_safe(loop_device, tmp_device, &device_last_list, node) {
145 trigger_uevent(loop_device->name);
146 list_del(&loop_device->node);
147 free(loop_device);
148 }
149 }
150
151 static int is_device(const char *path)
152 {
153 char filename[PATH_SIZE];
154 struct stat statbuf;
155
156 /* look for the uevent file of the kobject */
157 strlcpy(filename, path, sizeof(filename));
158 strlcat(filename, "/uevent", sizeof(filename));
159 if (stat(filename, &statbuf) < 0)
160 return 0;
161
162 if (!(statbuf.st_mode & S_IWUSR))
163 return 0;
164
165 return 1;
166 }
167
168 static void udev_scan_bus(void)
169 {
170 char base[PATH_SIZE];
171 DIR *dir;
172 struct dirent *dent;
173
174 strlcpy(base, sysfs_path, sizeof(base));
175 strlcat(base, "/bus", sizeof(base));
176
177 dir = opendir(base);
178 if (dir != NULL) {
179 for (dent = readdir(dir); dent != NULL; dent = readdir(dir)) {
180 char dirname[PATH_SIZE];
181 DIR *dir2;
182 struct dirent *dent2;
183
184 if (dent->d_name[0] == '.')
185 continue;
186
187 strlcpy(dirname, base, sizeof(dirname));
188 strlcat(dirname, "/", sizeof(dirname));
189 strlcat(dirname, dent->d_name, sizeof(dirname));
190 strlcat(dirname, "/devices", sizeof(dirname));
191
192 /* look for devices */
193 dir2 = opendir(dirname);
194 if (dir2 != NULL) {
195 for (dent2 = readdir(dir2); dent2 != NULL; dent2 = readdir(dir2)) {
196 char dirname2[PATH_SIZE];
197
198 if (dent2->d_name[0] == '.')
199 continue;
200
201 strlcpy(dirname2, dirname, sizeof(dirname2));
202 strlcat(dirname2, "/", sizeof(dirname2));
203 strlcat(dirname2, dent2->d_name, sizeof(dirname2));
204
205 if (is_device(dirname2))
206 device_list_insert(dirname2);
207 }
208 closedir(dir2);
209 }
210 }
211 closedir(dir);
212 }
213 }
214
215 static void udev_scan_block(void)
216 {
217 char base[PATH_SIZE];
218 DIR *dir;
219 struct dirent *dent;
220 struct stat statbuf;
221
222 /* skip if "block" is already a "class" */
223 strlcpy(base, sysfs_path, sizeof(base));
224 strlcat(base, "/class/block", sizeof(base));
225 if (stat(base, &statbuf) == 0)
226 return;
227
228 strlcpy(base, sysfs_path, sizeof(base));
229 strlcat(base, "/block", sizeof(base));
230
231 dir = opendir(base);
232 if (dir != NULL) {
233 for (dent = readdir(dir); dent != NULL; dent = readdir(dir)) {
234 char dirname[PATH_SIZE];
235 DIR *dir2;
236 struct dirent *dent2;
237
238 if (dent->d_name[0] == '.')
239 continue;
240
241 strlcpy(dirname, base, sizeof(dirname));
242 strlcat(dirname, "/", sizeof(dirname));
243 strlcat(dirname, dent->d_name, sizeof(dirname));
244 if (is_device(dirname))
245 device_list_insert(dirname);
246 else
247 continue;
248
249 /* look for partitions */
250 dir2 = opendir(dirname);
251 if (dir2 != NULL) {
252 for (dent2 = readdir(dir2); dent2 != NULL; dent2 = readdir(dir2)) {
253 char dirname2[PATH_SIZE];
254
255 if (dent2->d_name[0] == '.')
256 continue;
257
258 if (!strcmp(dent2->d_name,"device"))
259 continue;
260
261 strlcpy(dirname2, dirname, sizeof(dirname2));
262 strlcat(dirname2, "/", sizeof(dirname2));
263 strlcat(dirname2, dent2->d_name, sizeof(dirname2));
264 if (is_device(dirname2))
265 device_list_insert(dirname2);
266 }
267 closedir(dir2);
268 }
269 }
270 closedir(dir);
271 }
272 }
273
274 static void udev_scan_class(void)
275 {
276 char base[PATH_SIZE];
277 DIR *dir;
278 struct dirent *dent;
279
280 strlcpy(base, sysfs_path, sizeof(base));
281 strlcat(base, "/class", sizeof(base));
282
283 dir = opendir(base);
284 if (dir != NULL) {
285 for (dent = readdir(dir); dent != NULL; dent = readdir(dir)) {
286 char dirname[PATH_SIZE];
287 DIR *dir2;
288 struct dirent *dent2;
289
290 if (dent->d_name[0] == '.')
291 continue;
292
293 strlcpy(dirname, base, sizeof(dirname));
294 strlcat(dirname, "/", sizeof(dirname));
295 strlcat(dirname, dent->d_name, sizeof(dirname));
296 dir2 = opendir(dirname);
297 if (dir2 != NULL) {
298 for (dent2 = readdir(dir2); dent2 != NULL; dent2 = readdir(dir2)) {
299 char dirname2[PATH_SIZE];
300
301 if (dent2->d_name[0] == '.')
302 continue;
303
304 if (!strcmp(dent2->d_name, "device"))
305 continue;
306
307 strlcpy(dirname2, dirname, sizeof(dirname2));
308 strlcat(dirname2, "/", sizeof(dirname2));
309 strlcat(dirname2, dent2->d_name, sizeof(dirname2));
310 if (is_device(dirname2))
311 device_list_insert(dirname2);
312 }
313 closedir(dir2);
314 }
315 }
316 closedir(dir);
317 }
318 }
319
320 int main(int argc, char *argv[], char *envp[])
321 {
322 int i;
323
324 logging_init("udevtrigger");
325 udev_config_init();
326 dbg("version %s", UDEV_VERSION);
327
328 udev_log_str = getenv("UDEV_LOG");
329
330 for (i = 1 ; i < argc; i++) {
331 char *arg = argv[i];
332
333 if (strcmp(arg, "--verbose") == 0 || strcmp(arg, "-v") == 0)
334 verbose = 1;
335 else if (strcmp(arg, "--dry-run") == 0 || strcmp(arg, "-n") == 0)
336 dry_run = 1;
337 else {
338 fprintf(stderr, "Usage: udevtrigger [--verbose] [--dry-run]\n");
339 goto exit;
340 }
341 }
342
343 sysfs_init();
344
345 udev_scan_bus();
346 udev_scan_class();
347 udev_scan_block();
348 exec_lists();
349
350 sysfs_cleanup();
351 exit:
352 logging_close();
353 return 0;
354 }