]>
Commit | Line | Data |
---|---|---|
82b9a637 GKH |
1 | /* |
2 | * udevstart.c | |
3 | * | |
4 | * Copyright (C) 2004 Greg Kroah-Hartman <greg@kroah.com> | |
e8d569b4 KS |
5 | * Copyright (C) 2004 Kay Sievers <kay@vrfy.org> |
6 | * | |
82b9a637 | 7 | * Quick and dirty way to populate a /dev with udev if your system |
e8d569b4 KS |
8 | * does not have access to a shell. Based originally on a patch |
9 | * from: | |
10 | * Harald Hoyer <harald@redhat.com> | |
82b9a637 GKH |
11 | * |
12 | * This program is free software; you can redistribute it and/or modify it | |
13 | * under the terms of the GNU General Public License as published by the | |
14 | * Free Software Foundation version 2 of the License. | |
15 | * | |
16 | * This program is distributed in the hope that it will be useful, but | |
17 | * WITHOUT ANY WARRANTY; without even the implied warranty of | |
18 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
19 | * General Public License for more details. | |
20 | * | |
21 | * You should have received a copy of the GNU General Public License along | |
22 | * with this program; if not, write to the Free Software Foundation, Inc., | |
23 | * 675 Mass Ave, Cambridge, MA 02139, USA. | |
24 | * | |
25 | */ | |
26 | ||
27 | #include <stdlib.h> | |
0e306d08 | 28 | #include <stddef.h> |
82b9a637 GKH |
29 | #include <string.h> |
30 | #include <stdio.h> | |
56a8a624 | 31 | #include <unistd.h> |
82b9a637 GKH |
32 | #include <errno.h> |
33 | #include <ctype.h> | |
82b9a637 | 34 | #include <dirent.h> |
56a8a624 | 35 | #include <signal.h> |
27f877e6 | 36 | #include <syslog.h> |
82b9a637 | 37 | #include <sys/wait.h> |
f22e9686 | 38 | #include <sys/stat.h> |
f27125f9 | 39 | #include <sys/types.h> |
82b9a637 | 40 | |
63f61c5c | 41 | #include "udev.h" |
e8d569b4 | 42 | #include "udev_rules.h" |
82b9a637 | 43 | |
b8476286 KS |
44 | static const char *udev_run_str; |
45 | static const char *udev_log_str; | |
8bd41f36 | 46 | static struct udev_rules rules; |
b8476286 | 47 | |
56a8a624 | 48 | #ifdef USE_LOG |
6b493a20 | 49 | void log_message(int priority, const char *format, ...) |
56a8a624 | 50 | { |
6b493a20 KS |
51 | va_list args; |
52 | ||
53 | if (priority > udev_log_priority) | |
54 | return; | |
55 | ||
56 | va_start(args, format); | |
57 | vsyslog(priority, format, args); | |
58 | va_end(args); | |
56a8a624 KS |
59 | } |
60 | #endif | |
82b9a637 | 61 | |
0e306d08 | 62 | struct device { |
47241986 | 63 | struct list_head node; |
63f61c5c | 64 | char path[PATH_SIZE]; |
0e306d08 GKH |
65 | }; |
66 | ||
67 | /* sort files in lexical order */ | |
1aa1e248 | 68 | static int device_list_insert(const char *path, struct list_head *device_list) |
0e306d08 GKH |
69 | { |
70 | struct device *loop_device; | |
71 | struct device *new_device; | |
7d38d228 | 72 | const char *devpath = &path[strlen(sysfs_path)]; |
0e306d08 | 73 | |
7d38d228 | 74 | dbg("insert: '%s'\n", devpath); |
f22e9686 | 75 | |
47241986 | 76 | list_for_each_entry(loop_device, device_list, node) { |
7d38d228 | 77 | if (strcmp(loop_device->path, devpath) > 0) { |
0e306d08 GKH |
78 | break; |
79 | } | |
80 | } | |
81 | ||
82 | new_device = malloc(sizeof(struct device)); | |
83 | if (new_device == NULL) { | |
84 | dbg("error malloc"); | |
85 | return -ENOMEM; | |
86 | } | |
87 | ||
7d38d228 | 88 | strlcpy(new_device->path, devpath, sizeof(new_device->path)); |
47241986 | 89 | list_add_tail(&new_device->node, &loop_device->node); |
1aa1e248 | 90 | dbg("add '%s'" , new_device->path); |
0e306d08 GKH |
91 | return 0; |
92 | } | |
93 | ||
0e306d08 GKH |
94 | /* list of devices that we should run last due to any one of a number of reasons */ |
95 | static char *last_list[] = { | |
96 | "/block/dm", /* on here because dm wants to have the block devices around before it */ | |
97 | NULL, | |
98 | }; | |
99 | ||
70f630f6 GKH |
100 | /* list of devices that we should run first due to any one of a number of reasons */ |
101 | static char *first_list[] = { | |
7d38d228 KS |
102 | "/class/mem", |
103 | "/class/tty", | |
70f630f6 GKH |
104 | NULL, |
105 | }; | |
106 | ||
1aa1e248 | 107 | static int add_device(const char *devpath) |
f608f8ac | 108 | { |
1aa1e248 KS |
109 | struct sysfs_device *dev; |
110 | struct udevice *udev; | |
4cfdeaf3 | 111 | int retval = 0; |
b8476286 KS |
112 | |
113 | /* clear and set environment for next event */ | |
114 | clearenv(); | |
115 | setenv("ACTION", "add", 1); | |
b8476286 KS |
116 | setenv("UDEV_START", "1", 1); |
117 | if (udev_log_str) | |
118 | setenv("UDEV_LOG", udev_log_str, 1); | |
119 | if (udev_run_str) | |
120 | setenv("UDEV_RUN", udev_run_str, 1); | |
f22e9686 | 121 | |
1aa1e248 KS |
122 | dev = sysfs_device_get(devpath); |
123 | if (dev == NULL) | |
c07669bd | 124 | return -1; |
7a947ce5 | 125 | |
1aa1e248 KS |
126 | udev = udev_device_init(); |
127 | if (udev == NULL) | |
c07669bd | 128 | return -1; |
1aa1e248 KS |
129 | |
130 | /* override built-in sysfs device */ | |
131 | udev->dev = dev; | |
132 | strcpy(udev->action, "add"); | |
133 | udev->devt = udev_device_get_devt(udev); | |
134 | ||
135 | if (strcmp(udev->dev->subsystem, "net") != 0) { | |
136 | udev->devt = udev_device_get_devt(udev); | |
137 | if (major(udev->devt) == 0) | |
138 | return -1; | |
c07669bd | 139 | } |
1aa1e248 KS |
140 | |
141 | dbg("add '%s'", udev->dev->devpath); | |
142 | setenv("DEVPATH", udev->dev->devpath, 1); | |
143 | setenv("SUBSYSTEM", udev->dev->subsystem, 1); | |
144 | ||
145 | udev_rules_get_name(&rules, udev); | |
146 | if (udev->ignore_device) { | |
561d4c5a | 147 | dbg("device event will be ignored"); |
c07669bd KS |
148 | goto exit; |
149 | } | |
37854ffc KS |
150 | if (udev->name[0] != '\0') |
151 | retval = udev_add_device(udev); | |
f7ed0d11 | 152 | else |
37854ffc | 153 | info("device node creation supressed"); |
7a947ce5 | 154 | |
f7ed0d11 | 155 | if (retval == 0 && udev_run) { |
821d0ec8 KS |
156 | struct name_entry *name_loop; |
157 | ||
158 | dbg("executing run list"); | |
1aa1e248 | 159 | list_for_each_entry(name_loop, &udev->run_list, node) { |
d455b008 | 160 | if (strncmp(name_loop->name, "socket:", strlen("socket:")) == 0) |
1aa1e248 | 161 | pass_env_to_socket(&name_loop->name[strlen("socket:")], udev->dev->devpath, "add"); |
f5f0c34f AB |
162 | else { |
163 | char program[PATH_SIZE]; | |
164 | ||
165 | strlcpy(program, name_loop->name, sizeof(program)); | |
a9bd2ed8 | 166 | udev_rules_apply_format(udev, program, sizeof(program)); |
f5f0c34f AB |
167 | run_program(program, udev->dev->subsystem, NULL, 0, NULL, (udev_log_priority >= LOG_INFO)); |
168 | } | |
d455b008 | 169 | } |
821d0ec8 | 170 | } |
37854ffc | 171 | |
c07669bd | 172 | exit: |
1aa1e248 | 173 | udev_device_cleanup(udev); |
5d24c6ca | 174 | return 0; |
f608f8ac KS |
175 | } |
176 | ||
0e306d08 GKH |
177 | static void exec_list(struct list_head *device_list) |
178 | { | |
179 | struct device *loop_device; | |
180 | struct device *tmp_device; | |
70f630f6 GKH |
181 | int i; |
182 | ||
183 | /* handle the "first" type devices first */ | |
47241986 | 184 | list_for_each_entry_safe(loop_device, tmp_device, device_list, node) { |
f22e9686 | 185 | for (i = 0; first_list[i] != NULL; i++) { |
70f630f6 | 186 | if (strncmp(loop_device->path, first_list[i], strlen(first_list[i])) == 0) { |
1aa1e248 | 187 | add_device(loop_device->path); |
47241986 | 188 | list_del(&loop_device->node); |
70f630f6 GKH |
189 | free(loop_device); |
190 | break; | |
191 | } | |
192 | } | |
193 | } | |
0e306d08 GKH |
194 | |
195 | /* handle the devices we are allowed to, excluding the "last" type devices */ | |
47241986 | 196 | list_for_each_entry_safe(loop_device, tmp_device, device_list, node) { |
0e306d08 | 197 | int found = 0; |
f22e9686 | 198 | for (i = 0; last_list[i] != NULL; i++) { |
0e306d08 GKH |
199 | if (strncmp(loop_device->path, last_list[i], strlen(last_list[i])) == 0) { |
200 | found = 1; | |
201 | break; | |
202 | } | |
203 | } | |
204 | if (found) | |
205 | continue; | |
206 | ||
1aa1e248 | 207 | add_device(loop_device->path); |
47241986 | 208 | list_del(&loop_device->node); |
0e306d08 GKH |
209 | free(loop_device); |
210 | } | |
211 | ||
212 | /* handle the rest of the devices left over, if any */ | |
47241986 | 213 | list_for_each_entry_safe(loop_device, tmp_device, device_list, node) { |
1aa1e248 | 214 | add_device(loop_device->path); |
47241986 | 215 | list_del(&loop_device->node); |
0e306d08 GKH |
216 | free(loop_device); |
217 | } | |
218 | } | |
219 | ||
7d38d228 | 220 | static int has_devt(const char *path) |
f22e9686 | 221 | { |
63f61c5c | 222 | char filename[PATH_SIZE]; |
f22e9686 KS |
223 | struct stat statbuf; |
224 | ||
7d38d228 | 225 | snprintf(filename, sizeof(filename), "%s/dev", path); |
63f61c5c | 226 | filename[sizeof(filename)-1] = '\0'; |
f22e9686 KS |
227 | |
228 | if (stat(filename, &statbuf) == 0) | |
229 | return 1; | |
230 | ||
231 | return 0; | |
232 | } | |
233 | ||
7d38d228 | 234 | static void udev_scan_block(struct list_head *device_list) |
82b9a637 | 235 | { |
63f61c5c | 236 | char base[PATH_SIZE]; |
e4dc0e11 KS |
237 | DIR *dir; |
238 | struct dirent *dent; | |
82b9a637 | 239 | |
63f61c5c KS |
240 | snprintf(base, sizeof(base), "%s/block", sysfs_path); |
241 | base[sizeof(base)-1] = '\0'; | |
f22e9686 KS |
242 | |
243 | dir = opendir(base); | |
e4dc0e11 KS |
244 | if (dir != NULL) { |
245 | for (dent = readdir(dir); dent != NULL; dent = readdir(dir)) { | |
63f61c5c | 246 | char dirname[PATH_SIZE]; |
e4dc0e11 KS |
247 | DIR *dir2; |
248 | struct dirent *dent2; | |
249 | ||
f22e9686 | 250 | if (dent->d_name[0] == '.') |
82b9a637 GKH |
251 | continue; |
252 | ||
63f61c5c KS |
253 | snprintf(dirname, sizeof(dirname), "%s/%s", base, dent->d_name); |
254 | dirname[sizeof(dirname)-1] = '\0'; | |
f22e9686 | 255 | if (has_devt(dirname)) |
1aa1e248 | 256 | device_list_insert(dirname, device_list); |
f22e9686 KS |
257 | else |
258 | continue; | |
0e306d08 | 259 | |
56a8a624 | 260 | /* look for partitions */ |
82b9a637 | 261 | dir2 = opendir(dirname); |
e4dc0e11 KS |
262 | if (dir2 != NULL) { |
263 | for (dent2 = readdir(dir2); dent2 != NULL; dent2 = readdir(dir2)) { | |
63f61c5c | 264 | char dirname2[PATH_SIZE]; |
82b9a637 | 265 | |
f22e9686 | 266 | if (dent2->d_name[0] == '.') |
82b9a637 GKH |
267 | continue; |
268 | ||
63f61c5c KS |
269 | snprintf(dirname2, sizeof(dirname2), "%s/%s", dirname, dent2->d_name); |
270 | dirname2[sizeof(dirname2)-1] = '\0'; | |
f22e9686 KS |
271 | |
272 | if (has_devt(dirname2)) | |
1aa1e248 | 273 | device_list_insert(dirname2, device_list); |
82b9a637 | 274 | } |
e13fa599 | 275 | closedir(dir2); |
82b9a637 GKH |
276 | } |
277 | } | |
e13fa599 | 278 | closedir(dir); |
82b9a637 | 279 | } |
0e306d08 GKH |
280 | } |
281 | ||
7d38d228 | 282 | static void udev_scan_class(struct list_head *device_list) |
0e306d08 | 283 | { |
63f61c5c | 284 | char base[PATH_SIZE]; |
0e306d08 GKH |
285 | DIR *dir; |
286 | struct dirent *dent; | |
0e306d08 | 287 | |
63f61c5c KS |
288 | snprintf(base, sizeof(base), "%s/class", sysfs_path); |
289 | base[sizeof(base)-1] = '\0'; | |
f22e9686 KS |
290 | |
291 | dir = opendir(base); | |
e4dc0e11 KS |
292 | if (dir != NULL) { |
293 | for (dent = readdir(dir); dent != NULL; dent = readdir(dir)) { | |
63f61c5c | 294 | char dirname[PATH_SIZE]; |
e4dc0e11 KS |
295 | DIR *dir2; |
296 | struct dirent *dent2; | |
297 | ||
f22e9686 | 298 | if (dent->d_name[0] == '.') |
82b9a637 GKH |
299 | continue; |
300 | ||
63f61c5c KS |
301 | snprintf(dirname, sizeof(dirname), "%s/%s", base, dent->d_name); |
302 | dirname[sizeof(dirname)-1] = '\0'; | |
56a8a624 | 303 | |
82b9a637 | 304 | dir2 = opendir(dirname); |
e4dc0e11 KS |
305 | if (dir2 != NULL) { |
306 | for (dent2 = readdir(dir2); dent2 != NULL; dent2 = readdir(dir2)) { | |
63f61c5c | 307 | char dirname2[PATH_SIZE]; |
82b9a637 | 308 | |
f22e9686 | 309 | if (dent2->d_name[0] == '.') |
82b9a637 GKH |
310 | continue; |
311 | ||
63f61c5c KS |
312 | snprintf(dirname2, sizeof(dirname2), "%s/%s", dirname, dent2->d_name); |
313 | dirname2[sizeof(dirname2)-1] = '\0'; | |
f22e9686 | 314 | |
7fd0de49 | 315 | if (has_devt(dirname2) || strcmp(dent->d_name, "net") == 0) |
1aa1e248 | 316 | device_list_insert(dirname2, device_list); |
82b9a637 | 317 | } |
e13fa599 | 318 | closedir(dir2); |
82b9a637 GKH |
319 | } |
320 | } | |
e13fa599 | 321 | closedir(dir); |
82b9a637 | 322 | } |
82b9a637 GKH |
323 | } |
324 | ||
56a8a624 | 325 | static void asmlinkage sig_handler(int signum) |
82b9a637 | 326 | { |
56a8a624 KS |
327 | switch (signum) { |
328 | case SIGALRM: | |
329 | exit(1); | |
330 | case SIGINT: | |
331 | case SIGTERM: | |
332 | exit(20 + signum); | |
333 | } | |
334 | } | |
335 | ||
336 | int main(int argc, char *argv[], char *envp[]) | |
337 | { | |
7d38d228 | 338 | LIST_HEAD(device_list); |
56a8a624 KS |
339 | struct sigaction act; |
340 | ||
66f74a2d | 341 | logging_init("udevstart"); |
1aa1e248 | 342 | udev_config_init(); |
b8476286 KS |
343 | dbg("version %s", UDEV_VERSION); |
344 | ||
345 | udev_run_str = getenv("UDEV_RUN"); | |
346 | udev_log_str = getenv("UDEV_LOG"); | |
347 | ||
6b493a20 | 348 | /* disable all logging if not explicitely requested */ |
b8476286 | 349 | if (udev_log_str == NULL) |
6b493a20 | 350 | udev_log_priority = 0; |
56a8a624 KS |
351 | |
352 | /* set signal handlers */ | |
353 | memset(&act, 0x00, sizeof(act)); | |
354 | act.sa_handler = (void (*) (int))sig_handler; | |
355 | sigemptyset (&act.sa_mask); | |
356 | act.sa_flags = 0; | |
357 | sigaction(SIGALRM, &act, NULL); | |
358 | sigaction(SIGINT, &act, NULL); | |
359 | sigaction(SIGTERM, &act, NULL); | |
360 | ||
361 | /* trigger timeout to prevent hanging processes */ | |
8815afa1 | 362 | alarm(UDEV_ALARM_TIMEOUT); |
56a8a624 | 363 | |
1aa1e248 | 364 | sysfs_init(); |
287814b2 | 365 | udev_rules_init(&rules, 1); |
56a8a624 | 366 | |
7d38d228 KS |
367 | udev_scan_class(&device_list); |
368 | udev_scan_block(&device_list); | |
369 | exec_list(&device_list); | |
af4b05d4 | 370 | |
1aa1e248 KS |
371 | udev_rules_cleanup(&rules); |
372 | sysfs_cleanup(); | |
6b493a20 | 373 | logging_close(); |
3f20eac0 | 374 | return 0; |
82b9a637 | 375 | } |