/* vi: set sw=4 ts=4: */ /* * * mdev - Mini udev for busybox * * Copyright 2005 Rob Landley * Copyright 2005 Frank Sorenson * * Licensed under GPL version 2, see file LICENSE in this tarball for details. */ #include "busybox.h" #include "xregex.h" #define DEV_PATH "/dev" struct mdev_globals { int root_major, root_minor; } mdev_globals; #define bbg mdev_globals /* mknod in /dev based on a path like "/sys/block/hda/hda1" */ static void make_device(char *path, int delete) { char *device_name; int major, minor, type, len; int mode = 0660; uid_t uid = 0; gid_t gid = 0; char *temp = path + strlen(path); char *command = NULL; /* Try to read major/minor string. Note that the kernel puts \n after * the data, so we don't need to worry about null terminating the string * because sscanf() will stop at the first nondigit, which \n is. We * also depend on path having writeable space after it. */ if (!delete) { strcat(path, "/dev"); len = open_read_close(path, temp + 1, 64); *temp++ = 0; if (len < 1) return; } /* Determine device name, type, major and minor */ device_name = strrchr(path, '/') + 1; type = path[5]=='c' ? S_IFCHR : S_IFBLK; /* If we have a config file, look up permissions for this device */ if (ENABLE_FEATURE_MDEV_CONF) { char *conf, *pos, *end; int line, fd; /* mmap the config file */ fd = open("/etc/mdev.conf", O_RDONLY); if (fd < 0) goto end_parse; len = xlseek(fd, 0, SEEK_END); conf = mmap(NULL, len, PROT_READ, MAP_PRIVATE, fd, 0); close(fd); if (!conf) goto end_parse; line = 0; /* Loop through lines in mmaped file*/ for (pos=conf; pos-confpw_uid; } s++; /* parse GID */ gid = strtoul(s, &s2, 10); if (end2 != s2) { struct group *grp; grp = getgrnam(strndupa(s, end2-s)); if (!grp) break; gid = grp->gr_gid; } } if (field == 2) { /* mode */ mode = strtoul(pos, &pos, 8); if (pos != end2) break; } if (ENABLE_FEATURE_MDEV_EXEC && field == 3) { // Command to run const char *s = "@$*"; const char *s2; s2 = strchr(s, *pos++); if (!s2) { // Force error field = 1; break; } if ((s2-s+1) & (1< 2) break; if (field) bb_error_msg_and_die("bad line %d",line); /* Next line */ pos = ++end; } munmap(conf, len); end_parse: /* nothing */ ; } umask(0); if (!delete) { if (sscanf(temp, "%d:%d", &major, &minor) != 2) return; if (mknod(device_name, mode | type, makedev(major, minor)) && errno != EEXIST) bb_perror_msg_and_die("mknod %s", device_name); if (major == bbg.root_major && minor == bbg.root_minor) symlink(device_name, "root"); if (ENABLE_FEATURE_MDEV_CONF) chown(device_name, uid, gid); } if (command) { int rc; char *s; s = xasprintf("MDEV=%s", device_name); putenv(s); rc = system(command); s[4] = 0; putenv(s); free(s); free(command); if (rc == -1) bb_perror_msg_and_die("cannot run %s", command); } if (delete) unlink(device_name); } /* Recursive search of /sys/block or /sys/class. path must be a writeable * buffer of size PATH_MAX containing the directory string to start at. */ static void find_dev(char *path) { DIR *dir; size_t len = strlen(path); struct dirent *entry; dir = opendir(path); if (dir == NULL) return; while ((entry = readdir(dir)) != NULL) { struct stat st; /* Skip "." and ".." (also skips hidden files, which is ok) */ if (entry->d_name[0] == '.') continue; // uClibc doesn't fill out entry->d_type reliably. so we use lstat(). snprintf(path+len, PATH_MAX-len, "/%s", entry->d_name); if (!lstat(path, &st) && S_ISDIR(st.st_mode)) find_dev(path); path[len] = 0; /* If there's a dev entry, mknod it */ if (!strcmp(entry->d_name, "dev")) make_device(path, 0); } closedir(dir); } int mdev_main(int argc, char *argv[]); int mdev_main(int argc, char *argv[]) { char *action; char *env_path; RESERVE_CONFIG_BUFFER(temp,PATH_MAX); xchdir(DEV_PATH); /* Scan */ if (argc == 2 && !strcmp(argv[1],"-s")) { struct stat st; xstat("/", &st); bbg.root_major = major(st.st_dev); bbg.root_minor = minor(st.st_dev); strcpy(temp,"/sys/block"); find_dev(temp); strcpy(temp,"/sys/class"); find_dev(temp); /* Hotplug */ } else { action = getenv("ACTION"); env_path = getenv("DEVPATH"); if (!action || !env_path) bb_show_usage(); sprintf(temp, "/sys%s", env_path); if (!strcmp(action, "add")) make_device(temp,0); else if (!strcmp(action, "remove")) make_device(temp,1); } if (ENABLE_FEATURE_CLEAN_UP) RELEASE_CONFIG_BUFFER(temp); return 0; }