diff options
-rw-r--r-- | toys/pending/mdev.c | 398 |
1 files changed, 91 insertions, 307 deletions
diff --git a/toys/pending/mdev.c b/toys/pending/mdev.c index 2aa09bc8..2d98c257 100644 --- a/toys/pending/mdev.c +++ b/toys/pending/mdev.c @@ -29,23 +29,16 @@ config MDEV_CONF matching devies. */ -#define FOR_mdev #include "toys.h" // todo, open() block devices to trigger partition scanning. -GLOBALS( - char *devname; - int root_maj, root_min, verbose; -) - - // mknod in /dev based on a path like "/sys/block/hda/hda1" -static void make_device(char *path, char *operation) +static void make_device(char *path) { - char *device_name, *s, *temp, *alias = NULL, *cmd = NULL, buf[PATH_MAX]; - char sign = 0, op_pref = 0, *str2 = NULL, *ln = NULL; - int major, minor, type, len, fd, ufd, mode = 0660; + char *device_name, *s, *temp; + int major, minor, type, len, fd; + int mode = 0660; uid_t uid = 0; gid_t gid = 0; @@ -53,32 +46,19 @@ static void make_device(char *path, char *operation) temp = strrchr(path, '/'); fd = open(path, O_RDONLY); - *temp = 0; + *temp=0; temp = toybuf; len = read(fd, temp, 64); close(fd); - if (!strcmp(operation, "add") && len < 1) return; + if (len<1) return; temp[len] = 0; + + // Determine device name, type, major and minor + + device_name = strrchr(path, '/') + 1; + type = path[5]=='c' ? S_IFCHR : S_IFBLK; major = minor = 0; - if (sscanf(temp, "%u:%u", &major, &minor) != 2) major = -1; - - memset(buf, 0, sizeof(buf)); - device_name = TT.devname; - if (!device_name) { - sprintf(buf,"%s/uevent", path); - if ((ufd = open(buf, O_RDONLY)) >= 0) { - for (; (ln = get_line(ufd)); free(ln)) { - if (strstr(ln, "DEVNAME=")) { - device_name = ln + strlen("DEVNAME=") ; - break; - } - } - close(ufd); - } - } - if (!device_name) device_name = strrchr(path, '/') + 1; - type = S_IFCHR; - if (strstr(path, "/block/")) type = S_IFBLK; + sscanf(temp, "%u:%u", &major, &minor); // If we have a config file, look up permissions for this device @@ -86,145 +66,96 @@ static void make_device(char *path, char *operation) char *conf, *pos, *end; // mmap the config file - if (-1 != (fd = open("/etc/mdev.conf", O_RDONLY))) { + if (-1!=(fd = open("/etc/mdev.conf", O_RDONLY))) { len = fdlength(fd); 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-conf < len;) { + for (pos = conf; pos-conf<len;) { int field; char *end2; line++; // find end of this line - for(end = pos; end-conf < len && *end != '\n'; end++); + for(end = pos; end-conf<len && *end!='\n'; end++); - // Fields: regex uid:gid mode [alias] [cmd] - for (field = 3; field >= 0; field--) { + // Three fields: regex, uid:gid, mode + for (field = 3; field; field--) { // Skip whitespace - while (pos < end && isspace(*pos)) pos++; - if (pos == end || *pos == '#') break; + while (pos<end && isspace(*pos)) pos++; + if (pos==end || *pos=='#') break; for (end2 = pos; - end2 < end && !isspace(*end2) && *end2 != '#'; end2++); + end2<end && !isspace(*end2) && *end2!='#'; end2++); switch(field) { // Regex to match this device case 3: - { - char *str = strndup(pos, end2-pos), *regex; - regex_t match; - regmatch_t off; - int result; - - if (*str == '$') { - char *p = strchr(str, '='); - - if (!p) { - free(str); - error_msg("bad $envvar=regex"); - goto end_line; - } else { - p++; - regex = xstrdup(p); - free(str); - } - } else { - regex = xstrdup(str); - free(str); - } - // Is this it? - if (*regex == '@') { - int maj, min1, min2; - - int ret = sscanf(regex, "@%u,%u-%u", &maj, &min1, &min2); - free(regex); - if (ret < 2 || maj < 0) { - error_msg("bad @maj,min @line %d",line); - goto end_line; - } - if (ret == 2) min2 = min1; - if (!(major == maj && ( min1 <= minor || minor <= min2))) - goto end_line; - } else { - xregcomp(&match, regex, REG_EXTENDED); - result = regexec(&match, device_name, 1, &off, 0); - regfree(&match); - free(regex); - - // If not this device, skip rest of line - if (result || off.rm_so - || off.rm_eo != strlen(device_name)) - goto end_line; - } - break; - } - // uid:gid + { + char *regex = strndup(pos, end2-pos); + regex_t match; + regmatch_t off; + int result; + + // Is this it? + xregcomp(&match, regex, REG_EXTENDED); + result=regexec(&match, device_name, 1, &off, 0); + regfree(&match); + free(regex); + + // If not this device, skip rest of line + if (result || off.rm_so + || off.rm_eo!=strlen(device_name)) + goto end_line; + + break; + } + // uid:gid case 2: - { - char *s2; - - // Find : - for(s = pos; s < end2 && *s != ':'; s++); - if (s == end2) goto end_line; - - // Parse UID - uid = strtoul(pos,&s2,10); - if (s != s2) { - struct passwd *pass; - char *str = xstrndup(pos, s-pos); - - pass = getpwnam(str); - free(str); - if (!pass) goto end_line; - uid = pass->pw_uid; - } - s++; - // parse GID - gid = strtoul(s,&s2,10); - if (end2 != s2) { - struct group *grp; - char *str = xstrndup(s, end2-s); - - grp = getgrnam(str); - free(str); - if (!grp) goto end_line; - gid = grp->gr_gid; - } - break; - } - // mode - case 1: - { - mode = strtoul(pos, &pos, 8); - if (pos != end2) goto end_line; - //goto found_device; - break; + { + char *s2; + + // Find : + for(s = pos; s<end2 && *s!=':'; s++); + if (s==end2) goto end_line; + + // Parse UID + uid = strtoul(pos,&s2,10); + if (s!=s2) { + struct passwd *pass; + char *str = strndup(pos, s-pos); + pass = getpwnam(str); + free(str); + if (!pass) goto end_line; + uid = pass->pw_uid; } - // handle 4th and 5th field - case 0: - { - char *str1 = NULL; - - str2 = xstrndup(pos, end-pos); - if (strchr(">=!", str2[0])) { - str1 = strtok(str2, " "); - sign = str1[0]; - alias = str1 + 1; - str1 += strlen(alias) +2; - } else str1 = str2; - if (str1 && strchr("$@*",str1[0])) { - op_pref = str1[0]; - cmd = str1 + 1; - } else error_exit("Bad Line %d", line); - goto found_device; + s++; + // parse GID + gid = strtoul(s,&s2,10); + if (end2!=s2) { + struct group *grp; + char *str = strndup(s, end2-s); + grp = getgrnam(str); + free(str); + if (!grp) goto end_line; + gid = grp->gr_gid; } + break; + } + // mode + case 1: + { + mode = strtoul(pos, &pos, 8); + if (pos!=end2) goto end_line; + goto found_device; + } } - pos = end2; + pos=end2; } end_line: // Did everything parse happily? - if (field && field != 3) error_exit("bad line %d", line); + if (field && field!=3) error_exit("Bad line %d", line); + // Next line pos = ++end; } @@ -235,46 +166,11 @@ found_device: } } - if (operation && !((op_pref == '@' && !strcmp(operation, "add")) || - (op_pref == '$' && !strcmp(operation, "remove")) || (op_pref == '*'))) - cmd = NULL; - - if (alias) { - if (alias[strlen(alias)-1] == '/') sprintf(temp,"%s%s",alias, device_name); - else sprintf(temp,"%s",alias); - } - if (sign != '!' && operation && !strcmp(operation, "add")) { //not to create device otherwise - - if (mknod(device_name, mode | type, makedev(major, minor)) && errno != EEXIST) - perror_msg("mknod /dev/%s failed", device_name); - if (CFG_MDEV_CONF) { - chmod(device_name, mode); - chown(device_name, uid, gid); - } - - if (TT.root_maj == major && TT.root_min == minor) symlink(device_name, "root"); - - if (alias && (sign == '>' || sign == '=')) { - mkpathat(AT_FDCWD, alias, 0, 2); - if (rename(device_name, temp)) perror_exit("rename temp"); - if (sign == '>') symlink(temp, device_name); - } - } - if (cmd) { - char *str = xmprintf("%s=%s", "MDEV", device_name); - - putenv(str); - if (system(cmd) == -1) perror_msg("can't run '%s'", cmd); - unsetenv(str); - free(str); - } + sprintf(temp, "/dev/%s", device_name); + if (mknod(temp, mode | type, makedev(major, minor)) && errno != EEXIST) + perror_exit("mknod %s failed", temp); - if (operation && !strcmp(operation, "remove") && major >= -1) { - if (alias && sign == '>') unlink(temp); - unlink(device_name); - } - free(str2); - free(ln); + if (CFG_MDEV_CONF) mode=chown(temp, uid, gid); } static int callback(struct dirtree *node) @@ -282,143 +178,31 @@ static int callback(struct dirtree *node) // Entries in /sys/class/block aren't char devices, so skip 'em. (We'll // get block devices out of /sys/block.) if(!strcmp(node->name, "block")) return 0; - if (!dirtree_notdotdot(node)) return 0; // Does this directory have a "dev" entry in it? // This is path based because the hotplug callbacks are if (S_ISDIR(node->st.st_mode) || S_ISLNK(node->st.st_mode)) { - int len = 4; + int len=4; char *dev = dirtree_path(node, &len); strcpy(dev+len, "/dev"); - if (!access(dev, R_OK)) make_device(dev, "add"); + if (!access(dev, R_OK)) make_device(dev); free(dev); } // Circa 2.6.25 the entries more than 2 deep are all either redundant // (mouse#, event#) or unnamed (every usb_* entry is called "device"). - return (node->parent && node->parent->parent) ? 0 : - DIRTREE_RECURSE|DIRTREE_SYMFOLLOW; -} - -#if 0 -static int sequence_file(char *seq) -{ - static struct timespec tspec = { 0, 32*1000*1000 }; // time out after 2 secs - int time_out = 2000 / 32, fd = -1; - sigset_t sg; - - sigemptyset(&sg); - sigaddset(&sg, SIGCHLD); - sigprocmask(SIG_BLOCK, &sg, NULL); - - for (;;) { - ssize_t slen; - char buf[sizeof(int)*3 + 2]; - - if (fd < 0 && (fd = open("/dev/mdev.seq", O_RDWR)) < 0) break; - if ((slen = pread(fd, buf, sizeof(buf) - 1, 0)) < 0) { - close(fd); - fd = -1; - break; - } - buf[slen] = '\0'; - if (buf[0] == '\n') { - xwrite(fd, seq, strlen(seq)); - xlseek(fd, 0, SEEK_SET); - break; - } - if (!strcmp(seq, buf)) break; - if (sigtimedwait(&sg, NULL, &tspec) >= 0) continue; - if (!--time_out) break; - } - sigprocmask(SIG_UNBLOCK, &sg, NULL); - return fd; -} -#endif - -static void firmware_load(char *fware, char *sysfs_path) -{ - int count, fd, lfd, dfd; - - xchdir("/lib/firmware"); - fd = open(fware, O_RDONLY); - // check for /sys/$DEVPATH/loading ... give 30 seconds to appear - xchdir(sysfs_path); - for (count = 0; count < 30; ++count) { - lfd = open("loading", O_WRONLY); - if (lfd >= 0) goto load; - sleep(1); - } - goto end; - -load: - if (fd >= 0) { - if (writeall(lfd, "1", 1) != 1) goto end; - dfd = open("data", O_WRONLY); - if (dfd < 0) goto end; - xsendfile(fd, dfd); - close(dfd); - } - if (fd >= 0) writeall(lfd, "0", 1); - else writeall(lfd, "-1", 2); - -end: - xchdir("/dev"); - close(lfd); - close(fd); + return (node->parent && node->parent->parent) ? 0 : DIRTREE_RECURSE; } void mdev_main(void) { + // Handle -s - char buf[PATH_MAX]; - struct dirtree *root; - - umask(0); - xchdir("/dev"); - if (toys.optflags & FLAG_s) { - struct stat st; - - xstat("/", &st); - TT.root_maj = major(st.st_dev); - TT.root_min = minor(st.st_dev); - putenv((char*)"ACTION=add"); - - root = dirtree_add_node(0, "/sys/class", 1); - if (root) dirtree_handle_callback(root, callback); - - root = dirtree_add_node(0, "/sys/block", 1); - if (root) dirtree_handle_callback(root, callback); - } else { // Hotplug handling - char *fware, *action, *devpath; - int logfd; - - action = getenv("ACTION"); - TT.devname = getenv("DEVNAME"); - devpath = getenv("DEVPATH"); - - if (!action || !devpath) { - toys.exithelp++; - error_exit("env var action/devpath not found"); - } - fware = getenv("FIRMWARE"); - - if ((logfd = open("/dev/mdev.log", O_WRONLY | O_APPEND)) >= 0) { - dup2(logfd, 2); // 2 -> STDERR_FILENO - TT.verbose = 1; - } - //TODO give sequencei file support. - - // seqfd = seq_num ? sequence_file(seq_num) : -1; - snprintf(buf, PATH_MAX, "/sys%s/dev", devpath); - if (action && !strcmp("remove", action) && !fware) - make_device(buf, action); - else { - make_device(buf, action); - buf[strlen(buf) - 4] = '\0'; //remove /dev from end. - if (action && !strcmp("add", action) && fware) - firmware_load(fware, buf); - } + if (toys.optflags) { + dirtree_read("/sys/class", callback); + dirtree_read("/sys/block", callback); } + + // hotplug support goes here } |