Changeset 1770 in MondoRescue for branches/stable/mindi-busybox/util-linux/mdev.c
- Timestamp:
- Nov 6, 2007, 11:01:53 AM (16 years ago)
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
branches/stable/mindi-busybox/util-linux/mdev.c
r902 r1770 1 /* vi:set ts=4: 1 /* vi: set sw=4 ts=4: */ 2 /* 2 3 * 3 4 * mdev - Mini udev for busybox … … 6 7 * Copyright 2005 Frank Sorenson <frank@tuxrocks.com> 7 8 * 8 * Licensed under GPL v2 or later, see file LICENSE in this tarball for details.9 * Licensed under GPL version 2, see file LICENSE in this tarball for details. 9 10 */ 10 11 11 #include "busybox.h" 12 #include <ctype.h> 13 #include <errno.h> 14 #include <sys/mman.h> 15 #include <sys/sysmacros.h> 12 #include "libbb.h" 16 13 #include "xregex.h" 17 14 18 #define DEV_PATH "/dev" 19 20 struct mdev_globals 21 { 15 struct globals { 22 16 int root_major, root_minor; 23 } mdev_globals; 24 25 #define bbg mdev_globals 17 }; 18 #define G (*(struct globals*)&bb_common_bufsiz1) 19 #define root_major (G.root_major) 20 #define root_minor (G.root_minor) 21 22 #define MAX_SYSFS_DEPTH 3 /* prevent infinite loops in /sys symlinks */ 26 23 27 24 /* mknod in /dev based on a path like "/sys/block/hda/hda1" */ 28 25 static void make_device(char *path, int delete) 29 26 { 30 c har *device_name;31 int major, minor, type, len , fd;27 const char *device_name; 28 int major, minor, type, len; 32 29 int mode = 0660; 33 30 uid_t uid = 0; … … 43 40 if (!delete) { 44 41 strcat(path, "/dev"); 45 fd = open(path, O_RDONLY); 46 len = read(fd, temp + 1, 64); 42 len = open_read_close(path, temp + 1, 64); 47 43 *temp++ = 0; 48 close(fd);49 44 if (len < 1) return; 50 45 } … … 52 47 /* Determine device name, type, major and minor */ 53 48 54 device_name = strrchr(path, '/') + 1;49 device_name = bb_basename(path); 55 50 type = path[5]=='c' ? S_IFCHR : S_IFBLK; 56 51 … … 59 54 if (ENABLE_FEATURE_MDEV_CONF) { 60 55 char *conf, *pos, *end; 56 int line, fd; 61 57 62 58 /* mmap the config file */ 63 if (-1 != (fd=open("/etc/mdev.conf",O_RDONLY))) { 64 len = lseek(fd, 0, SEEK_END); 65 conf = mmap(NULL, len, PROT_READ, MAP_PRIVATE, fd, 0); 66 if (conf) { 67 int line = 0; 68 69 /* Loop through lines in mmaped file*/ 70 for (pos=conf; pos-conf<len;) { 71 int field; 72 char *end2; 73 74 line++; 75 /* find end of this line */ 76 for(end=pos; end-conf<len && *end!='\n'; end++) 59 fd = open("/etc/mdev.conf", O_RDONLY); 60 if (fd < 0) 61 goto end_parse; 62 len = xlseek(fd, 0, SEEK_END); 63 conf = mmap(NULL, len, PROT_READ, MAP_PRIVATE, fd, 0); 64 close(fd); 65 if (!conf) 66 goto end_parse; 67 68 line = 0; 69 /* Loop through lines in mmaped file*/ 70 for (pos=conf; pos-conf<len;) { 71 int field; 72 char *end2; 73 74 line++; 75 /* find end of this line */ 76 for (end=pos; end-conf<len && *end!='\n'; end++) 77 ; 78 79 /* Three fields: regex, uid:gid, mode */ 80 for (field=0; field < (3 + ENABLE_FEATURE_MDEV_EXEC); 81 field++) 82 { 83 /* Skip whitespace */ 84 while (pos<end && isspace(*pos)) pos++; 85 if (pos==end || *pos=='#') break; 86 for (end2=pos; 87 end2<end && !isspace(*end2) && *end2!='#'; end2++) 88 ; 89 90 if (field == 0) { 91 /* Regex to match this device */ 92 93 char *regex = xstrndup(pos, end2-pos); 94 regex_t match; 95 regmatch_t off; 96 int result; 97 98 /* Is this it? */ 99 xregcomp(&match,regex, REG_EXTENDED); 100 result = regexec(&match, device_name, 1, &off, 0); 101 regfree(&match); 102 free(regex); 103 104 /* If not this device, skip rest of line */ 105 if (result || off.rm_so 106 || off.rm_eo != strlen(device_name)) 107 break; 108 } 109 if (field == 1) { 110 /* uid:gid */ 111 112 char *s, *s2; 113 114 /* Find : */ 115 for (s=pos; s<end2 && *s!=':'; s++) 77 116 ; 78 79 /* Three fields: regex, uid:gid, mode */ 80 for (field=0; field < (3 + ENABLE_FEATURE_MDEV_EXEC); 81 field++) 82 { 83 /* Skip whitespace */ 84 while (pos<end && isspace(*pos)) pos++; 85 if (pos==end || *pos=='#') break; 86 for (end2=pos; 87 end2<end && !isspace(*end2) && *end2!='#'; end2++) 88 ; 89 90 if (!field) { 91 /* Regex to match this device */ 92 93 char *regex = strndupa(pos, end2-pos); 94 regex_t match; 95 regmatch_t off; 96 int result; 97 98 /* Is this it? */ 99 xregcomp(&match,regex, REG_EXTENDED); 100 result = regexec(&match, device_name, 1, &off, 0); 101 regfree(&match); 102 103 /* If not this device, skip rest of line */ 104 if (result || off.rm_so 105 || off.rm_eo != strlen(device_name)) 106 break; 107 108 } else if (field == 1) { 109 /* uid:gid */ 110 111 char *s, *s2; 112 113 /* Find : */ 114 for(s=pos; s<end2 && *s!=':'; s++) 115 ; 116 if (s == end2) break; 117 118 /* Parse UID */ 119 uid = strtoul(pos,&s2,10); 120 if (s != s2) { 121 struct passwd *pass; 122 pass = getpwnam(strndupa(pos, s-pos)); 123 if (!pass) break; 124 uid = pass->pw_uid; 125 } 126 s++; 127 /* parse GID */ 128 gid = strtoul(s, &s2, 10); 129 if (end2 != s2) { 130 struct group *grp; 131 grp = getgrnam(strndupa(s, end2-s)); 132 if (!grp) break; 133 gid = grp->gr_gid; 134 } 135 } else if (field == 2) { 136 /* mode */ 137 138 mode = strtoul(pos, &pos, 8); 139 if (pos != end2) break; 140 } else if (ENABLE_FEATURE_MDEV_EXEC && field == 3) { 141 // Command to run 142 char *s = "@$*", *s2; 143 if (!(s2 = strchr(s, *pos++))) { 144 // Force error 145 field = 1; 146 break; 147 } 148 if ((s2-s+1) & (1<<delete)) 149 command = bb_xstrndup(pos, end-pos); 150 } 151 152 pos = end2; 117 if (s == end2) break; 118 119 /* Parse UID */ 120 uid = strtoul(pos, &s2, 10); 121 if (s != s2) { 122 struct passwd *pass; 123 char *_unam = xstrndup(pos, s-pos); 124 pass = getpwnam(_unam); 125 free(_unam); 126 if (!pass) break; 127 uid = pass->pw_uid; 153 128 } 154 155 /* Did everything parse happily? */ 156 157 if (field > 2) break; 158 if (field) bb_error_msg_and_die("Bad line %d",line); 159 160 /* Next line */ 161 pos = ++end; 129 s++; 130 /* parse GID */ 131 gid = strtoul(s, &s2, 10); 132 if (end2 != s2) { 133 struct group *grp; 134 char *_grnam = xstrndup(s, end2-s); 135 grp = getgrnam(_grnam); 136 free(_grnam); 137 if (!grp) break; 138 gid = grp->gr_gid; 139 } 162 140 } 163 munmap(conf, len); 141 if (field == 2) { 142 /* mode */ 143 144 mode = strtoul(pos, &pos, 8); 145 if (pos != end2) break; 146 } 147 if (ENABLE_FEATURE_MDEV_EXEC && field == 3) { 148 // Command to run 149 const char *s = "@$*"; 150 const char *s2; 151 s2 = strchr(s, *pos++); 152 if (!s2) { 153 // Force error 154 field = 1; 155 break; 156 } 157 if ((s2-s+1) & (1<<delete)) 158 command = xstrndup(pos, end-pos); 159 } 160 161 pos = end2; 164 162 } 165 close(fd); 163 164 /* Did everything parse happily? */ 165 166 if (field > 2) break; 167 if (field) bb_error_msg_and_die("bad line %d",line); 168 169 /* Next line */ 170 pos = ++end; 166 171 } 172 munmap(conf, len); 173 end_parse: /* nothing */ ; 167 174 } 168 175 … … 171 178 if (sscanf(temp, "%d:%d", &major, &minor) != 2) return; 172 179 if (mknod(device_name, mode | type, makedev(major, minor)) && errno != EEXIST) 173 bb_perror_msg_and_die("mknod %s failed", device_name);174 175 if (major == bbg.root_major && minor == bbg.root_minor)180 bb_perror_msg_and_die("mknod %s", device_name); 181 182 if (major == root_major && minor == root_minor) 176 183 symlink(device_name, "root"); 177 184 178 185 if (ENABLE_FEATURE_MDEV_CONF) chown(device_name, uid, gid); 179 186 } 180 187 if (command) { 181 int rc; 182 char *s; 183 184 s=bb_xasprintf("MDEV=%s",device_name); 188 /* setenv will leak memory, so use putenv */ 189 char *s = xasprintf("MDEV=%s", device_name); 185 190 putenv(s); 186 rc = system(command); 187 s[4]=0; 188 putenv(s); 191 if (system(command) == -1) 192 bb_perror_msg_and_die("cannot run %s", command); 193 s[4] = '\0'; 194 unsetenv(s); 189 195 free(s); 190 196 free(command); 191 if (rc == -1) bb_perror_msg_and_die("Couldn't run %s", command);192 197 } 193 198 if (delete) unlink(device_name); 194 199 } 195 200 196 /* Recursive search of /sys/block or /sys/class. path must be a writeable 197 * buffer of size PATH_MAX containing the directory string to start at. */ 198 199 static void find_dev(char *path) 200 { 201 DIR *dir; 202 size_t len = strlen(path); 203 struct dirent *entry; 204 205 if ((dir = opendir(path)) == NULL) 206 return; 207 208 while ((entry = readdir(dir)) != NULL) { 209 struct stat st; 210 211 /* Skip "." and ".." (also skips hidden files, which is ok) */ 212 213 if (entry->d_name[0] == '.') 214 continue; 215 216 // uClibc doesn't fill out entry->d_type reliably. so we use lstat(). 217 218 snprintf(path+len, PATH_MAX-len, "/%s", entry->d_name); 219 if (!lstat(path, &st) && S_ISDIR(st.st_mode)) find_dev(path); 220 path[len] = 0; 221 222 /* If there's a dev entry, mknod it */ 223 224 if (!strcmp(entry->d_name, "dev")) make_device(path, 0); 225 } 226 227 closedir(dir); 228 } 229 230 int mdev_main(int argc, char *argv[]) 201 /* File callback for /sys/ traversal */ 202 static int fileAction(const char *fileName, struct stat *statbuf, 203 void *userData, int depth) 204 { 205 size_t len = strlen(fileName) - 4; 206 char *scratch = userData; 207 208 if (strcmp(fileName + len, "/dev")) 209 return FALSE; 210 211 strcpy(scratch, fileName); 212 scratch[len] = 0; 213 make_device(scratch, 0); 214 215 return TRUE; 216 } 217 218 /* Directory callback for /sys/ traversal */ 219 static int dirAction(const char *fileName, struct stat *statbuf, 220 void *userData, int depth) 221 { 222 return (depth >= MAX_SYSFS_DEPTH ? SKIP : TRUE); 223 } 224 225 /* For the full gory details, see linux/Documentation/firmware_class/README 226 * 227 * Firmware loading works like this: 228 * - kernel sets FIRMWARE env var 229 * - userspace checks /lib/firmware/$FIRMWARE 230 * - userspace waits for /sys/$DEVPATH/loading to appear 231 * - userspace writes "1" to /sys/$DEVPATH/loading 232 * - userspace copies /lib/firmware/$FIRMWARE into /sys/$DEVPATH/data 233 * - userspace writes "0" (worked) or "-1" (failed) to /sys/$DEVPATH/loading 234 * - kernel loads firmware into device 235 */ 236 static void load_firmware(const char *const firmware, const char *const sysfs_path) 237 { 238 int cnt; 239 int firmware_fd, loading_fd, data_fd; 240 241 /* check for $FIRMWARE from kernel */ 242 /* XXX: dont bother: open(NULL) works same as open("no-such-file") 243 * if (!firmware) 244 * return; 245 */ 246 247 /* check for /lib/firmware/$FIRMWARE */ 248 xchdir("/lib/firmware"); 249 firmware_fd = xopen(firmware, O_RDONLY); 250 251 /* in case we goto out ... */ 252 data_fd = -1; 253 254 /* check for /sys/$DEVPATH/loading ... give 30 seconds to appear */ 255 xchdir(sysfs_path); 256 for (cnt = 0; cnt < 30; ++cnt) { 257 loading_fd = open("loading", O_WRONLY); 258 if (loading_fd == -1) 259 sleep(1); 260 else 261 break; 262 } 263 if (loading_fd == -1) 264 goto out; 265 266 /* tell kernel we're loading by `echo 1 > /sys/$DEVPATH/loading` */ 267 if (write(loading_fd, "1", 1) != 1) 268 goto out; 269 270 /* load firmware by `cat /lib/firmware/$FIRMWARE > /sys/$DEVPATH/data */ 271 data_fd = open("data", O_WRONLY); 272 if (data_fd == -1) 273 goto out; 274 cnt = bb_copyfd_eof(firmware_fd, data_fd); 275 276 /* tell kernel result by `echo [0|-1] > /sys/$DEVPATH/loading` */ 277 if (cnt > 0) 278 write(loading_fd, "0", 1); 279 else 280 write(loading_fd, "-1", 2); 281 282 out: 283 if (ENABLE_FEATURE_CLEAN_UP) { 284 close(firmware_fd); 285 close(loading_fd); 286 close(data_fd); 287 } 288 } 289 290 int mdev_main(int argc, char **argv); 291 int mdev_main(int argc, char **argv) 231 292 { 232 293 char *action; … … 234 295 RESERVE_CONFIG_BUFFER(temp,PATH_MAX); 235 296 236 bb_xchdir(DEV_PATH);297 xchdir("/dev"); 237 298 238 299 /* Scan */ … … 241 302 struct stat st; 242 303 243 stat("/", &st); // If this fails, we have bigger problems. 244 bbg.root_major=major(st.st_dev); 245 bbg.root_minor=minor(st.st_dev); 246 strcpy(temp,"/sys/block"); 247 find_dev(temp); 248 strcpy(temp,"/sys/class"); 249 find_dev(temp); 304 xstat("/", &st); 305 root_major = major(st.st_dev); 306 root_minor = minor(st.st_dev); 307 308 recursive_action("/sys/block", 309 ACTION_RECURSE | ACTION_FOLLOWLINKS, 310 fileAction, dirAction, temp, 0); 311 312 recursive_action("/sys/class", 313 ACTION_RECURSE | ACTION_FOLLOWLINKS, 314 fileAction, dirAction, temp, 0); 250 315 251 316 /* Hotplug */ … … 254 319 action = getenv("ACTION"); 255 320 env_path = getenv("DEVPATH"); 256 321 if (!action || !env_path) 257 322 bb_show_usage(); 258 323 259 324 sprintf(temp, "/sys%s", env_path); 260 if (!strcmp(action, "add")) make_device(temp,0); 261 else if (!strcmp(action, "remove")) make_device(temp,1); 325 if (!strcmp(action, "remove")) 326 make_device(temp, 1); 327 else if (!strcmp(action, "add")) { 328 make_device(temp, 0); 329 330 if (ENABLE_FEATURE_MDEV_LOAD_FIRMWARE) 331 load_firmware(getenv("FIRMWARE"), temp); 332 } 262 333 } 263 334
Note:
See TracChangeset
for help on using the changeset viewer.