/* vi:set ts=4: * * mdev - Mini udev for busybox * * Copyright 2005 Rob Landley * Copyright 2005 Frank Sorenson * * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. */ #include "busybox.h" #include #include #include #include #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, fd; 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. */ strcat(path, "/dev"); fd = open(path, O_RDONLY); len = read(fd, temp + 1, 64); *temp++ = 0; close(fd); 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 (sscanf(temp, "%d:%d", &major, &minor) != 2) return; /* If we have a config file, look up permissions for this device */ if (ENABLE_FEATURE_MDEV_CONF) { char *conf, *pos, *end; /* mmap the config file */ if (-1 != (fd=open("/etc/mdev.conf",O_RDONLY))) { len = lseek(fd, 0, SEEK_END); conf = mmap(NULL, len, PROT_READ, MAP_PRIVATE, fd, 0); if (conf) { int 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; } } else if (field == 2) { /* mode */ mode = strtoul(pos, &pos, 8); if (pos != end2) break; } else if (ENABLE_FEATURE_MDEV_EXEC && field == 3) { // Command to run char *s = "@$*", *s2; if (!(s2 = strchr(s, *pos++))) { // 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); } close(fd); } } umask(0); if (!delete) { if (mknod(device_name, mode | type, makedev(major, minor)) && errno != EEXIST) bb_perror_msg_and_die("mknod %s failed", 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=bb_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("Couldn't 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; if ((dir = opendir(path)) == 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[]) { char *action; char *env_path; RESERVE_CONFIG_BUFFER(temp,PATH_MAX); bb_xchdir(DEV_PATH); /* Scan */ if (argc == 2 && !strcmp(argv[1],"-s")) { struct stat st; stat("/", &st); // If this fails, we have bigger problems. 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; }