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