chiark / gitweb /
update source file headers
[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 <errno.h>
26 #include <dirent.h>
27 #include <fcntl.h>
28 #include <syslog.h>
29 #include <sys/stat.h>
30 #include <sys/types.h>
31
32 #include "udev.h"
33 #include "udevd.h"
34
35 static int verbose;
36 static int dry_run;
37
38 #ifdef USE_LOG
39 void log_message(int priority, const char *format, ...)
40 {
41         va_list args;
42
43         if (priority > udev_log_priority)
44                 return;
45
46         va_start(args, format);
47         vsyslog(priority, format, args);
48         va_end(args);
49 }
50 #endif
51
52 /* list of devices that we should run last due to any one of a number of reasons */
53 static char *last_list[] = {
54         "/class/block/md",
55         "/class/block/dm-",
56         "/block/md",
57         "/block/dm-",
58         NULL
59 };
60
61 /* list of devices that we should run first due to any one of a number of reasons */
62 static char *first_list[] = {
63         "/class/mem",
64         "/class/tty",
65         NULL
66 };
67
68 LIST_HEAD(device_first_list);
69 LIST_HEAD(device_default_list);
70 LIST_HEAD(device_last_list);
71
72 static int device_list_insert(const char *path)
73 {
74         struct list_head *device_list = &device_default_list;
75         const char *devpath = &path[strlen(sysfs_path)];
76         int i;
77
78         for (i = 0; first_list[i] != NULL; i++) {
79                 if (strncmp(devpath, first_list[i], strlen(first_list[i])) == 0) {
80                         device_list = &device_first_list;
81                         break;
82                 }
83         }
84         for (i = 0; last_list[i] != NULL; i++) {
85                 if (strncmp(devpath, last_list[i], strlen(last_list[i])) == 0) {
86                         device_list = &device_last_list;
87                         break;
88                 }
89         }
90
91         dbg("add '%s'" , path);
92         /* double entries will be ignored */
93         name_list_add(device_list, path, 0);
94         return 0;
95 }
96
97 static void trigger_uevent(const char *path)
98 {
99         char filename[PATH_SIZE];
100         int fd;
101
102         strlcpy(filename, path, sizeof(filename));
103         strlcat(filename, "/uevent", sizeof(filename));
104
105         if (verbose)
106                 printf("%s\n", path);
107
108         if (dry_run)
109                 return;
110
111         fd = open(filename, O_WRONLY);
112         if (fd < 0) {
113                 dbg("error on opening %s: %s\n", filename, strerror(errno));
114                 return;
115         }
116
117         if (write(fd, "add", 3) < 0)
118                 info("error on triggering %s: %s\n", filename, strerror(errno));
119
120         close(fd);
121 }
122
123 static void exec_lists(void)
124 {
125         struct name_entry *loop_device;
126         struct name_entry *tmp_device;
127
128         /* handle the devices on the "first" list first */
129         list_for_each_entry_safe(loop_device, tmp_device, &device_first_list, node) {
130                 trigger_uevent(loop_device->name);
131                 list_del(&loop_device->node);
132                 free(loop_device);
133         }
134
135         /* handle the devices on the "default" list next */
136         list_for_each_entry_safe(loop_device, tmp_device, &device_default_list, node) {
137                 trigger_uevent(loop_device->name);
138                 list_del(&loop_device->node);
139                 free(loop_device);
140         }
141
142         /* handle devices on the "last" list, if any */
143         list_for_each_entry_safe(loop_device, tmp_device, &device_last_list, node) {
144                 trigger_uevent(loop_device->name);
145                 list_del(&loop_device->node);
146                 free(loop_device);
147         }
148 }
149
150 static int is_device(const char *path)
151 {
152         char filename[PATH_SIZE];
153         struct stat statbuf;
154
155         /* look for the uevent file of the kobject */
156         strlcpy(filename, path, sizeof(filename));
157         strlcat(filename, "/uevent", sizeof(filename));
158         if (stat(filename, &statbuf) < 0)
159                 return 0;
160
161         if (!(statbuf.st_mode & S_IWUSR))
162                 return 0;
163
164         return 1;
165 }
166
167 static void scan_bus(void)
168 {
169         char base[PATH_SIZE];
170         DIR *dir;
171         struct dirent *dent;
172
173         strlcpy(base, sysfs_path, sizeof(base));
174         strlcat(base, "/bus", sizeof(base));
175
176         dir = opendir(base);
177         if (dir != NULL) {
178                 for (dent = readdir(dir); dent != NULL; dent = readdir(dir)) {
179                         char dirname[PATH_SIZE];
180                         DIR *dir2;
181                         struct dirent *dent2;
182
183                         if (dent->d_name[0] == '.')
184                                 continue;
185
186                         strlcpy(dirname, base, sizeof(dirname));
187                         strlcat(dirname, "/", sizeof(dirname));
188                         strlcat(dirname, dent->d_name, sizeof(dirname));
189                         strlcat(dirname, "/devices", sizeof(dirname));
190
191                         /* look for devices */
192                         dir2 = opendir(dirname);
193                         if (dir2 != NULL) {
194                                 for (dent2 = readdir(dir2); dent2 != NULL; dent2 = readdir(dir2)) {
195                                         char dirname2[PATH_SIZE];
196
197                                         if (dent2->d_name[0] == '.')
198                                                 continue;
199
200                                         strlcpy(dirname2, dirname, sizeof(dirname2));
201                                         strlcat(dirname2, "/", sizeof(dirname2));
202                                         strlcat(dirname2, dent2->d_name, sizeof(dirname2));
203
204                                         if (is_device(dirname2))
205                                                 device_list_insert(dirname2);
206                                 }
207                                 closedir(dir2);
208                         }
209                 }
210                 closedir(dir);
211         }
212 }
213
214 static void scan_block(void)
215 {
216         char base[PATH_SIZE];
217         DIR *dir;
218         struct dirent *dent;
219         struct stat statbuf;
220
221         /* skip if "block" is already a "class" */
222         strlcpy(base, sysfs_path, sizeof(base));
223         strlcat(base, "/class/block", sizeof(base));
224         if (stat(base, &statbuf) == 0)
225                 return;
226
227         strlcpy(base, sysfs_path, sizeof(base));
228         strlcat(base, "/block", sizeof(base));
229
230         dir = opendir(base);
231         if (dir != NULL) {
232                 for (dent = readdir(dir); dent != NULL; dent = readdir(dir)) {
233                         char dirname[PATH_SIZE];
234                         DIR *dir2;
235                         struct dirent *dent2;
236
237                         if (dent->d_name[0] == '.')
238                                 continue;
239
240                         strlcpy(dirname, base, sizeof(dirname));
241                         strlcat(dirname, "/", sizeof(dirname));
242                         strlcat(dirname, dent->d_name, sizeof(dirname));
243                         if (is_device(dirname))
244                                 device_list_insert(dirname);
245                         else
246                                 continue;
247
248                         /* look for partitions */
249                         dir2 = opendir(dirname);
250                         if (dir2 != NULL) {
251                                 for (dent2 = readdir(dir2); dent2 != NULL; dent2 = readdir(dir2)) {
252                                         char dirname2[PATH_SIZE];
253
254                                         if (dent2->d_name[0] == '.')
255                                                 continue;
256
257                                         if (!strcmp(dent2->d_name,"device"))
258                                                 continue;
259
260                                         strlcpy(dirname2, dirname, sizeof(dirname2));
261                                         strlcat(dirname2, "/", sizeof(dirname2));
262                                         strlcat(dirname2, dent2->d_name, sizeof(dirname2));
263                                         if (is_device(dirname2))
264                                                 device_list_insert(dirname2);
265                                 }
266                                 closedir(dir2);
267                         }
268                 }
269                 closedir(dir);
270         }
271 }
272
273 static void scan_class(void)
274 {
275         char base[PATH_SIZE];
276         DIR *dir;
277         struct dirent *dent;
278
279         strlcpy(base, sysfs_path, sizeof(base));
280         strlcat(base, "/class", sizeof(base));
281
282         dir = opendir(base);
283         if (dir != NULL) {
284                 for (dent = readdir(dir); dent != NULL; dent = readdir(dir)) {
285                         char dirname[PATH_SIZE];
286                         DIR *dir2;
287                         struct dirent *dent2;
288
289                         if (dent->d_name[0] == '.')
290                                 continue;
291
292                         strlcpy(dirname, base, sizeof(dirname));
293                         strlcat(dirname, "/", sizeof(dirname));
294                         strlcat(dirname, dent->d_name, sizeof(dirname));
295                         dir2 = opendir(dirname);
296                         if (dir2 != NULL) {
297                                 for (dent2 = readdir(dir2); dent2 != NULL; dent2 = readdir(dir2)) {
298                                         char dirname2[PATH_SIZE];
299
300                                         if (dent2->d_name[0] == '.')
301                                                 continue;
302
303                                         if (!strcmp(dent2->d_name, "device"))
304                                                 continue;
305
306                                         strlcpy(dirname2, dirname, sizeof(dirname2));
307                                         strlcat(dirname2, "/", sizeof(dirname2));
308                                         strlcat(dirname2, dent2->d_name, sizeof(dirname2));
309                                         if (is_device(dirname2))
310                                                 device_list_insert(dirname2);
311                                 }
312                                 closedir(dir2);
313                         }
314                 }
315                 closedir(dir);
316         }
317 }
318
319 static void scan_failed(void)
320 {
321         char base[PATH_SIZE];
322         DIR *dir;
323         struct dirent *dent;
324
325         strlcpy(base, udev_root, sizeof(base));
326         strlcat(base, "/", sizeof(base));
327         strlcat(base, EVENT_FAILED_DIR, sizeof(base));
328
329         dir = opendir(base);
330         if (dir != NULL) {
331                 for (dent = readdir(dir); dent != NULL; dent = readdir(dir)) {
332                         char linkname[PATH_SIZE];
333                         char target[PATH_SIZE];
334                         int len;
335
336                         if (dent->d_name[0] == '.')
337                                 continue;
338
339                         strlcpy(linkname, base, sizeof(linkname));
340                         strlcat(linkname, "/", sizeof(linkname));
341                         strlcat(linkname, dent->d_name, sizeof(linkname));
342
343                         len = readlink(linkname, target, sizeof(target));
344                         if (len <= 0)
345                                 continue;
346                         target[len] = '\0';
347
348                         if (is_device(target))
349                                 device_list_insert(target);
350                         else
351                                 continue;
352                 }
353                 closedir(dir);
354         }
355 }
356
357 int main(int argc, char *argv[], char *envp[])
358 {
359         int i;
360         int failed = 0;
361
362         logging_init("udevtrigger");
363         udev_config_init();
364         dbg("version %s", UDEV_VERSION);
365         sysfs_init();
366
367         for (i = 1 ; i < argc; i++) {
368                 char *arg = argv[i];
369
370                 if (strcmp(arg, "--verbose") == 0 || strcmp(arg, "-v") == 0) {
371                         verbose = 1;
372                 } else if (strcmp(arg, "--dry-run") == 0 || strcmp(arg, "-n") == 0) {
373                         dry_run = 1;
374                 } else if (strcmp(arg, "--retry-failed") == 0 || strcmp(arg, "-F") == 0) {
375                         failed = 1;
376                 } else if (strcmp(arg, "--help") == 0 || strcmp(arg, "-h") == 0) {
377                         printf("Usage: udevtrigger [--help] [--verbose] [--dry-run] [--retry-failed]\n");
378                         goto exit;
379                 } else {
380                         fprintf(stderr, "unrecognized option '%s'\n", arg);
381                         err("unrecognized option '%s'\n", arg);
382                 }
383         }
384
385         if (failed)
386                 scan_failed();
387         else {
388                 /* default action */
389                 scan_bus();
390                 scan_class();
391                 scan_block();
392         }
393         exec_lists();
394
395 exit:
396         sysfs_cleanup();
397         logging_close();
398         return 0;
399 }