| rob@114 | 1 | /* vi:set ts=4:
|
| rob@156 | 2 | *
|
| rob@114 | 3 | * mdev - Mini udev for busybox
|
| rob@156 | 4 | *
|
| rob@292 | 5 | * Copyright 2005, 2008 Rob Landley <rob@landley.net>
|
| rob@114 | 6 | * Copyright 2005 Frank Sorenson <frank@tuxrocks.com>
|
| rob@194 | 7 | *
|
| rob@194 | 8 | * Not in SUSv3.
|
| rob@254 | 9 |
|
| rob@257 | 10 | USE_MDEV(NEWTOY(mdev, "s", TOYFLAG_USR|TOYFLAG_BIN|TOYFLAG_UMASK))
|
| rob@233 | 11 |
|
| rob@233 | 12 | config MDEV
|
| rob@233 | 13 | bool "mdev"
|
| rob@292 | 14 | default y
|
| rob@233 | 15 | help
|
| rob@233 | 16 | usage: mdev [-s]
|
| rob@233 | 17 |
|
| rob@233 | 18 | Create devices in /dev using information from /sys.
|
| rob@233 | 19 |
|
| rob@233 | 20 | -s Scan all entries in /sys to populate /dev.
|
| rob@233 | 21 |
|
| rob@233 | 22 | config MDEV_CONF
|
| rob@233 | 23 | bool "Configuration file for mdev"
|
| rob@292 | 24 | default y
|
| rob@233 | 25 | depends on MDEV
|
| rob@233 | 26 | help
|
| rob@233 | 27 | The mdev config file (/etc/mdev.conf) contains lines that look like:
|
| rob@233 | 28 | hd[a-z][0-9]* 0:3 660
|
| rob@233 | 29 |
|
| rob@233 | 30 | Each line must contain three whitespace separated fields. The first
|
| rob@233 | 31 | field is a regular expression matching one or more device names, and
|
| rob@233 | 32 | the second and third fields are uid:gid and file permissions for
|
| rob@233 | 33 | matching devies.
|
| rob@233 | 34 | */
|
| rob@114 | 35 |
|
| rob@114 | 36 | #include "toys.h"
|
| rob@114 | 37 | #include "lib/xregcomp.h"
|
| rob@114 | 38 |
|
| rob@114 | 39 | // mknod in /dev based on a path like "/sys/block/hda/hda1"
|
| rob@114 | 40 | static void make_device(char *path)
|
| rob@114 | 41 | {
|
| rob@114 | 42 | char *device_name, *s, *temp;
|
| rob@114 | 43 | int major, minor, type, len, fd;
|
| rob@114 | 44 | int mode = 0660;
|
| rob@114 | 45 | uid_t uid = 0;
|
| rob@114 | 46 | gid_t gid = 0;
|
| rob@114 | 47 |
|
| rob@114 | 48 | // Try to read major/minor string
|
| rob@114 | 49 |
|
| rob@292 | 50 | temp = strrchr(path, '/');
|
| rob@114 | 51 | fd = open(path, O_RDONLY);
|
| rob@114 | 52 | *temp=0;
|
| rob@114 | 53 | temp++;
|
| rob@114 | 54 | len = read(fd, temp, 64);
|
| rob@114 | 55 | close(fd);
|
| rob@114 | 56 | if (len<1) return;
|
| rob@114 | 57 | temp[len] = 0;
|
| rob@156 | 58 |
|
| rob@114 | 59 | // Determine device name, type, major and minor
|
| rob@156 | 60 |
|
| rob@114 | 61 | device_name = strrchr(path, '/') + 1;
|
| rob@114 | 62 | type = path[5]=='c' ? S_IFCHR : S_IFBLK;
|
| rob@114 | 63 | major = minor = 0;
|
| rob@114 | 64 | sscanf(temp, "%u:%u", &major, &minor);
|
| rob@114 | 65 |
|
| rob@114 | 66 | // If we have a config file, look up permissions for this device
|
| rob@156 | 67 |
|
| rob@114 | 68 | if (CFG_MDEV_CONF) {
|
| rob@114 | 69 | char *conf, *pos, *end;
|
| rob@114 | 70 |
|
| rob@114 | 71 | // mmap the config file
|
| rob@114 | 72 | if (-1!=(fd = open("/etc/mdev.conf", O_RDONLY))) {
|
| rob@114 | 73 | len = lseek(fd, 0, SEEK_END);
|
| rob@114 | 74 | conf = mmap(NULL, len, PROT_READ, MAP_PRIVATE, fd, 0);
|
| rob@114 | 75 | if (conf) {
|
| rob@114 | 76 | int line = 0;
|
| rob@114 | 77 |
|
| rob@114 | 78 | // Loop through lines in mmaped file
|
| rob@114 | 79 | for (pos = conf; pos-conf<len;) {
|
| rob@114 | 80 | int field;
|
| rob@114 | 81 | char *end2;
|
| rob@156 | 82 |
|
| rob@114 | 83 | line++;
|
| rob@114 | 84 | // find end of this line
|
| rob@114 | 85 | for(end = pos; end-conf<len && *end!='\n'; end++);
|
| rob@114 | 86 |
|
| rob@114 | 87 | // Three fields: regex, uid:gid, mode
|
| rob@114 | 88 | for (field = 3; field; field--) {
|
| rob@114 | 89 | // Skip whitespace
|
| rob@114 | 90 | while (pos<end && isspace(*pos)) pos++;
|
| rob@114 | 91 | if (pos==end || *pos=='#') break;
|
| rob@114 | 92 | for (end2 = pos;
|
| rob@114 | 93 | end2<end && !isspace(*end2) && *end2!='#'; end2++);
|
| rob@114 | 94 | switch(field) {
|
| rob@114 | 95 | // Regex to match this device
|
| rob@114 | 96 | case 3:
|
| rob@114 | 97 | {
|
| rob@114 | 98 | char *regex = strndupa(pos, end2-pos);
|
| rob@114 | 99 | regex_t match;
|
| rob@114 | 100 | regmatch_t off;
|
| rob@114 | 101 | int result;
|
| rob@114 | 102 |
|
| rob@114 | 103 | // Is this it?
|
| rob@114 | 104 | xregcomp(&match, regex, REG_EXTENDED);
|
| rob@114 | 105 | result=regexec(&match, device_name, 1, &off, 0);
|
| rob@114 | 106 | regfree(&match);
|
| rob@156 | 107 |
|
| rob@114 | 108 | // If not this device, skip rest of line
|
| rob@114 | 109 | if (result || off.rm_so
|
| rob@114 | 110 | || off.rm_eo!=strlen(device_name))
|
| rob@114 | 111 | goto end_line;
|
| rob@114 | 112 |
|
| rob@114 | 113 | break;
|
| rob@114 | 114 | }
|
| rob@114 | 115 | // uid:gid
|
| rob@114 | 116 | case 2:
|
| rob@114 | 117 | {
|
| rob@114 | 118 | char *s2;
|
| rob@114 | 119 |
|
| rob@114 | 120 | // Find :
|
| rob@114 | 121 | for(s = pos; s<end2 && *s!=':'; s++);
|
| rob@114 | 122 | if (s==end2) goto end_line;
|
| rob@114 | 123 |
|
| rob@114 | 124 | // Parse UID
|
| rob@114 | 125 | uid = strtoul(pos,&s2,10);
|
| rob@114 | 126 | if (s!=s2) {
|
| rob@114 | 127 | struct passwd *pass;
|
| rob@114 | 128 | pass = getpwnam(strndupa(pos, s-pos));
|
| rob@114 | 129 | if (!pass) goto end_line;
|
| rob@114 | 130 | uid = pass->pw_uid;
|
| rob@114 | 131 | }
|
| rob@114 | 132 | s++;
|
| rob@114 | 133 | // parse GID
|
| rob@114 | 134 | gid = strtoul(s,&s2,10);
|
| rob@114 | 135 | if (end2!=s2) {
|
| rob@114 | 136 | struct group *grp;
|
| rob@114 | 137 | grp = getgrnam(strndupa(s, end2-s));
|
| rob@114 | 138 | if (!grp) goto end_line;
|
| rob@114 | 139 | gid = grp->gr_gid;
|
| rob@114 | 140 | }
|
| rob@114 | 141 | break;
|
| rob@114 | 142 | }
|
| rob@114 | 143 | // mode
|
| rob@114 | 144 | case 1:
|
| rob@114 | 145 | {
|
| rob@114 | 146 | mode = strtoul(pos, &pos, 8);
|
| rob@114 | 147 | if (pos!=end2) goto end_line;
|
| rob@114 | 148 | goto found_device;
|
| rob@114 | 149 | }
|
| rob@114 | 150 | }
|
| rob@114 | 151 | pos=end2;
|
| rob@114 | 152 | }
|
| rob@114 | 153 | end_line:
|
| rob@114 | 154 | // Did everything parse happily?
|
| rob@114 | 155 | if (field && field!=3) error_exit("Bad line %d", line);
|
| rob@114 | 156 |
|
| rob@114 | 157 | // Next line
|
| rob@114 | 158 | pos = ++end;
|
| rob@114 | 159 | }
|
| rob@114 | 160 | found_device:
|
| rob@114 | 161 | munmap(conf, len);
|
| rob@114 | 162 | }
|
| rob@114 | 163 | close(fd);
|
| rob@114 | 164 | }
|
| rob@114 | 165 | }
|
| rob@114 | 166 |
|
| rob@114 | 167 | sprintf(temp, "/dev/%s", device_name);
|
| rob@114 | 168 | if (mknod(temp, mode | type, makedev(major, minor)) && errno != EEXIST)
|
| rob@114 | 169 | perror_exit("mknod %s failed", temp);
|
| rob@114 | 170 |
|
| rob@114 | 171 | if (CFG_MDEV_CONF) chown(temp, uid, gid);
|
| rob@114 | 172 | }
|
| rob@114 | 173 |
|
| rob@292 | 174 | static int callback(char *path, struct dirtree *node)
|
| rob@292 | 175 | {
|
| rob@292 | 176 | // Entries in /sys/class/block aren't char devices, so skip 'em. (We'll
|
| rob@292 | 177 | // get block devices out of /sys/block.)
|
| rob@292 | 178 | if(!strcmp(node->name, "block")) return 1;
|
| rob@114 | 179 |
|
| rob@292 | 180 | // Does this directory have a "dev" entry in it?
|
| rob@292 | 181 | if (S_ISDIR(node->st.st_mode) || S_ISLNK(node->st.st_mode)) {
|
| rob@292 | 182 | char *dest = path+strlen(path);
|
| rob@292 | 183 | strcpy(dest, "/dev");
|
| rob@292 | 184 | if (!access(path, R_OK)) make_device(path);
|
| rob@292 | 185 | *dest = 0;
|
| rob@114 | 186 | }
|
| rob@156 | 187 |
|
| rob@292 | 188 | // Circa 2.6.25 the entries more than 2 deep are all either redundant
|
| rob@292 | 189 | // (mouse#, event#) or unnamed (every usb_* entry is called "device").
|
| rob@292 | 190 | return node->depth>1;
|
| rob@114 | 191 | }
|
| rob@114 | 192 |
|
| rob@254 | 193 | void mdev_main(void)
|
| rob@114 | 194 | {
|
| rob@292 | 195 | // Handle -s
|
| rob@292 | 196 |
|
| rob@114 | 197 | if (toys.optflags) {
|
| rob@292 | 198 | xchdir("/sys/class");
|
| rob@114 | 199 | strcpy(toybuf, "/sys/class");
|
| rob@292 | 200 | dirtree_read(toybuf, NULL, callback);
|
| rob@292 | 201 | strcpy(toybuf+5, "block");
|
| rob@292 | 202 | dirtree_read(toybuf, NULL, callback);
|
| rob@156 | 203 | }
|
| rob@292 | 204 | // if (toys.optflags) {
|
| rob@292 | 205 | // strcpy(toybuf, "/sys/block");
|
| rob@292 | 206 | // find_dev(toybuf);
|
| rob@292 | 207 | // strcpy(toybuf, "/sys/class");
|
| rob@292 | 208 | // find_dev(toybuf);
|
| rob@292 | 209 | // return;
|
| rob@292 | 210 | // }
|
| rob@156 | 211 |
|
| rob@114 | 212 | // hotplug support goes here
|
| rob@114 | 213 | }
|