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