1/* 2 * quick and dirty way to populate a /dev directory 3 * 4 * Copyright (C) 2004 Harald Hoyer <harald@redhat.com> 5 * Copyright (C) 2004 Greg Kroah-Hartman <greg@kroah.com> 6 * Copyright (C) 2004-2006 Kay Sievers <kay@vrfy.org> 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., 19 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 20 * 21 */ 22 23#include <stdlib.h> 24#include <stddef.h> 25#include <string.h> 26#include <stdio.h> 27#include <unistd.h> 28#include <errno.h> 29#include <ctype.h> 30#include <dirent.h> 31#include <signal.h> 32#include <syslog.h> 33#include <sys/wait.h> 34#include <sys/stat.h> 35#include <sys/types.h> 36 37#include "udev.h" 38#include "udev_rules.h" 39#include "udev_selinux.h" 40 41static const char *udev_run_str; 42static const char *udev_log_str; 43static struct udev_rules rules; 44 45#ifdef USE_LOG 46void log_message(int priority, const char *format, ...) 47{ 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); 56} 57#endif 58 59struct device { 60 struct list_head node; 61 char path[PATH_SIZE]; 62}; 63 64/* sort files in lexical order */ 65static int device_list_insert(const char *path, struct list_head *device_list) 66{ 67 struct device *loop_device; 68 struct device *new_device; 69 const char *devpath = &path[strlen(sysfs_path)]; 70 71 dbg("insert: '%s'", devpath); 72 73 list_for_each_entry(loop_device, device_list, node) { 74 if (strcmp(loop_device->path, devpath) > 0) { 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 85 strlcpy(new_device->path, devpath, sizeof(new_device->path)); 86 list_add_tail(&new_device->node, &loop_device->node); 87 dbg("add '%s'" , new_device->path); 88 return 0; 89} 90 91/* list of devices that we should run last due to any one of a number of reasons */ 92static char *last_list[] = { 93 "/block/dm", /* on here because dm wants to have the block devices around before it */ 94 NULL, 95}; 96 97/* list of devices that we should run first due to any one of a number of reasons */ 98static char *first_list[] = { 99 "/class/mem", 100 "/class/tty", 101 NULL, 102}; 103 104static int add_device(const char *devpath) 105{ 106 struct sysfs_device *dev; 107 struct udevice *udev; 108 int retval = 0; 109 110 /* clear and set environment for next event */ 111 clearenv(); 112 setenv("ACTION", "add", 1); 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); 118 119 dev = sysfs_device_get(devpath); 120 if (dev == NULL) 121 return -1; 122 123 udev = udev_device_init(NULL); 124 if (udev == NULL) 125 return -1; 126 127 /* override built-in sysfs device */ 128 udev->dev = dev; 129 strcpy(udev->action, "add"); 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; 135 } 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) { 143 dbg("device event will be ignored"); 144 goto exit; 145 } 146 if (udev->name[0] != '\0') 147 retval = udev_device_event(&rules, udev); 148 else 149 info("device node creation supressed"); 150 151 if (retval == 0 && udev_run) { 152 struct name_entry *name_loop; 153 154 dbg("executing run list"); 155 list_for_each_entry(name_loop, &udev->run_list, node) { 156 if (strncmp(name_loop->name, "socket:", strlen("socket:")) == 0) 157 pass_env_to_socket(&name_loop->name[strlen("socket:")], udev->dev->devpath, "add"); 158 else { 159 char program[PATH_SIZE]; 160 161 strlcpy(program, name_loop->name, sizeof(program)); 162 udev_rules_apply_format(udev, program, sizeof(program)); 163 run_program(program, udev->dev->subsystem, NULL, 0, NULL, (udev_log_priority >= LOG_INFO)); 164 } 165 } 166 } 167 168exit: 169 udev_device_cleanup(udev); 170 return 0; 171} 172 173static void exec_list(struct list_head *device_list) 174{ 175 struct device *loop_device; 176 struct device *tmp_device; 177 int i; 178 179 /* handle the "first" type devices first */ 180 list_for_each_entry_safe(loop_device, tmp_device, device_list, node) { 181 for (i = 0; first_list[i] != NULL; i++) { 182 if (strncmp(loop_device->path, first_list[i], strlen(first_list[i])) == 0) { 183 add_device(loop_device->path); 184 list_del(&loop_device->node); 185 free(loop_device); 186 break; 187 } 188 } 189 } 190 191 /* handle the devices we are allowed to, excluding the "last" type devices */ 192 list_for_each_entry_safe(loop_device, tmp_device, device_list, node) { 193 int found = 0; 194 for (i = 0; last_list[i] != NULL; i++) { 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 203 add_device(loop_device->path); 204 list_del(&loop_device->node); 205 free(loop_device); 206 } 207 208 /* handle the rest of the devices left over, if any */ 209 list_for_each_entry_safe(loop_device, tmp_device, device_list, node) { 210 add_device(loop_device->path); 211 list_del(&loop_device->node); 212 free(loop_device); 213 } 214} 215 216static int has_devt(const char *path) 217{ 218 char filename[PATH_SIZE]; 219 struct stat statbuf; 220 221 snprintf(filename, sizeof(filename), "%s/dev", path); 222 filename[sizeof(filename)-1] = '\0'; 223 224 if (stat(filename, &statbuf) == 0) 225 return 1; 226 227 return 0; 228} 229 230static void udev_scan_block(struct list_head *device_list) 231{ 232 char base[PATH_SIZE]; 233 DIR *dir; 234 struct dirent *dent; 235 236 snprintf(base, sizeof(base), "%s/block", sysfs_path); 237 base[sizeof(base)-1] = '\0'; 238 239 dir = opendir(base); 240 if (dir != NULL) { 241 for (dent = readdir(dir); dent != NULL; dent = readdir(dir)) { 242 char dirname[PATH_SIZE]; 243 DIR *dir2; 244 struct dirent *dent2; 245 246 if (dent->d_name[0] == '.') 247 continue; 248 249 snprintf(dirname, sizeof(dirname), "%s/%s", base, dent->d_name); 250 dirname[sizeof(dirname)-1] = '\0'; 251 if (has_devt(dirname)) 252 device_list_insert(dirname, device_list); 253 else 254 continue; 255 256 /* look for partitions */ 257 dir2 = opendir(dirname); 258 if (dir2 != NULL) { 259 for (dent2 = readdir(dir2); dent2 != NULL; dent2 = readdir(dir2)) { 260 char dirname2[PATH_SIZE]; 261 262 if (dent2->d_name[0] == '.') 263 continue; 264 265 snprintf(dirname2, sizeof(dirname2), "%s/%s", dirname, dent2->d_name); 266 dirname2[sizeof(dirname2)-1] = '\0'; 267 268 if (has_devt(dirname2)) 269 device_list_insert(dirname2, device_list); 270 } 271 closedir(dir2); 272 } 273 } 274 closedir(dir); 275 } 276} 277 278static void udev_scan_class(struct list_head *device_list) 279{ 280 char base[PATH_SIZE]; 281 DIR *dir; 282 struct dirent *dent; 283 284 snprintf(base, sizeof(base), "%s/class", sysfs_path); 285 base[sizeof(base)-1] = '\0'; 286 287 dir = opendir(base); 288 if (dir != NULL) { 289 for (dent = readdir(dir); dent != NULL; dent = readdir(dir)) { 290 char dirname[PATH_SIZE]; 291 DIR *dir2; 292 struct dirent *dent2; 293 294 if (dent->d_name[0] == '.') 295 continue; 296 297 snprintf(dirname, sizeof(dirname), "%s/%s", base, dent->d_name); 298 dirname[sizeof(dirname)-1] = '\0'; 299 300 dir2 = opendir(dirname); 301 if (dir2 != NULL) { 302 for (dent2 = readdir(dir2); dent2 != NULL; dent2 = readdir(dir2)) { 303 char dirname2[PATH_SIZE]; 304 305 if (dent2->d_name[0] == '.') 306 continue; 307 308 snprintf(dirname2, sizeof(dirname2), "%s/%s", dirname, dent2->d_name); 309 dirname2[sizeof(dirname2)-1] = '\0'; 310 311 if (has_devt(dirname2) || strcmp(dent->d_name, "net") == 0) 312 device_list_insert(dirname2, device_list); 313 } 314 closedir(dir2); 315 } 316 } 317 closedir(dir); 318 } 319} 320 321static void asmlinkage sig_handler(int signum) 322{ 323 switch (signum) { 324 case SIGALRM: 325 exit(1); 326 case SIGINT: 327 case SIGTERM: 328 exit(20 + signum); 329 } 330} 331 332int main(int argc, char *argv[], char *envp[]) 333{ 334 LIST_HEAD(device_list); 335 struct sigaction act; 336 337 logging_init("udevstart"); 338 udev_config_init(); 339 selinux_init(); 340 dbg("version %s", UDEV_VERSION); 341 342 udev_run_str = getenv("UDEV_RUN"); 343 udev_log_str = getenv("UDEV_LOG"); 344 345 /* disable all logging if not explicitely requested */ 346 if (udev_log_str == NULL) 347 udev_log_priority = 0; 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 */ 359 alarm(UDEV_ALARM_TIMEOUT); 360 361 sysfs_init(); 362 udev_rules_init(&rules, 1); 363 364 udev_scan_class(&device_list); 365 udev_scan_block(&device_list); 366 exec_list(&device_list); 367 368 udev_rules_cleanup(&rules); 369 sysfs_cleanup(); 370 selinux_exit(); 371 logging_close(); 372 return 0; 373} 374