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