# HG changeset patch # User Hyejin Kim # Date 1426714321 18000 # Node ID da1296acc73eecb2b5426d6a604f0aaae70318ea # Parent ba22c0d4273f18833afdc7e7d8aeb96ff9679eff mdev : add hotplugin feature As internal needs, Ranjan Kumar (ranjankumar.bth at gmail.com) added hotplug feature into mdev. diff -r ba22c0d4273f -r da1296acc73e toys/pending/mdev.c --- a/toys/pending/mdev.c Mon Mar 16 13:27:16 2015 -0500 +++ b/toys/pending/mdev.c Wed Mar 18 16:32:01 2015 -0500 @@ -29,16 +29,23 @@ 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) +static void make_device(char *path, char *operation) { - char *device_name, *s, *temp; - int major, minor, type, len, fd; - int mode = 0660; + 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; uid_t uid = 0; gid_t gid = 0; @@ -46,19 +53,32 @@ temp = strrchr(path, '/'); fd = open(path, O_RDONLY); - *temp=0; + *temp = 0; temp = toybuf; len = read(fd, temp, 64); close(fd); - if (len<1) return; + if (!strcmp(operation, "add") && len < 1) return; temp[len] = 0; - - // Determine device name, type, major and minor + major = minor = 0; + if (sscanf(temp, "%u:%u", &major, &minor) != 2) major = -1; - device_name = strrchr(path, '/') + 1; - type = path[5]=='c' ? S_IFCHR : S_IFBLK; - major = minor = 0; - sscanf(temp, "%u:%u", &major, &minor); + 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; // If we have a config file, look up permissions for this device @@ -66,96 +86,145 @@ 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= 0; field--) { // Skip whitespace - while (pospw_uid; + // 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; } - 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; + // mode + case 1: + { + mode = strtoul(pos, &pos, 8); + if (pos != end2) goto end_line; + //goto found_device; + break; } - break; - } - // mode - case 1: - { - mode = strtoul(pos, &pos, 8); - if (pos!=end2) goto end_line; - goto found_device; - } + // 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; + } } - 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; } @@ -166,11 +235,46 @@ } } - sprintf(temp, "/dev/%s", device_name); - if (mknod(temp, mode | type, makedev(major, minor)) && errno != EEXIST) - perror_exit("mknod %s failed", temp); + 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 (CFG_MDEV_CONF) mode=chown(temp, uid, gid); + 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); + } + + if (operation && !strcmp(operation, "remove") && major >= -1) { + if (alias && sign == '>') unlink(temp); + unlink(device_name); + } + free(str2); + free(ln); } static int callback(struct dirtree *node) @@ -178,31 +282,143 @@ // 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); + if (!access(dev, R_OK)) make_device(dev, "add"); 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; + 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); } 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; - if (toys.optflags) { - dirtree_read("/sys/class", callback); - dirtree_read("/sys/block", callback); + 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); + } } - - // hotplug support goes here }