Changeset 2725 in MondoRescue for branches/2.2.9/mindi-busybox/util-linux/mdev.c
- Timestamp:
- Feb 25, 2011, 9:26:54 PM (13 years ago)
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
branches/2.2.9/mindi-busybox/util-linux/mdev.c
r1765 r2725 1 1 /* vi: set sw=4 ts=4: */ 2 2 /* 3 *4 3 * mdev - Mini udev for busybox 5 4 * … … 7 6 * Copyright 2005 Frank Sorenson <frank@tuxrocks.com> 8 7 * 9 * Licensed under GPL version 2, see file LICENSE in this tarball for details.8 * Licensed under GPLv2, see file LICENSE in this source tree. 10 9 */ 11 12 10 #include "libbb.h" 13 11 #include "xregex.h" 14 12 13 /* "mdev -s" scans /sys/class/xxx, looking for directories which have dev 14 * file (it is of the form "M:m\n"). Example: /sys/class/tty/tty0/dev 15 * contains "4:0\n". Directory name is taken as device name, path component 16 * directly after /sys/class/ as subsystem. In this example, "tty0" and "tty". 17 * Then mdev creates the /dev/device_name node. 18 * If /sys/class/.../dev file does not exist, mdev still may act 19 * on this device: see "@|$|*command args..." parameter in config file. 20 * 21 * mdev w/o parameters is called as hotplug helper. It takes device 22 * and subsystem names from $DEVPATH and $SUBSYSTEM, extracts 23 * maj,min from "/sys/$DEVPATH/dev" and also examines 24 * $ACTION ("add"/"delete") and $FIRMWARE. 25 * 26 * If action is "add", mdev creates /dev/device_name similarly to mdev -s. 27 * (todo: explain "delete" and $FIRMWARE) 28 * 29 * If /etc/mdev.conf exists, it may modify /dev/device_name's properties. 30 * /etc/mdev.conf file format: 31 * 32 * [-][subsystem/]device user:grp mode [>|=path] [@|$|*command args...] 33 * [-]@maj,min[-min2] user:grp mode [>|=path] [@|$|*command args...] 34 * [-]$envvar=val user:grp mode [>|=path] [@|$|*command args...] 35 * 36 * Leading minus in 1st field means "don't stop on this line", otherwise 37 * search is stopped after the matching line is encountered. 38 * 39 * The device name or "subsystem/device" combo is matched against 1st field 40 * (which is a regex), or maj,min is matched against 1st field, 41 * or specified environment variable (as regex) is matched against 1st field. 42 * 43 * $envvar=val format is useful for loading modules for hot-plugged devices 44 * which do not have driver loaded yet. In this case /sys/class/.../dev 45 * does not exist, but $MODALIAS is set to needed module's name 46 * (actually, an alias to it) by kernel. This rule instructs mdev 47 * to load the module and exit: 48 * $MODALIAS=.* 0:0 660 @modprobe "$MODALIAS" 49 * The kernel will generate another hotplug event when /sys/class/.../dev 50 * file appears. 51 * 52 * When line matches, the device node is created, chmod'ed and chown'ed, 53 * moved to path, and if >path, a symlink to moved node is created, 54 * all this if /sys/class/.../dev exists. 55 * Examples: 56 * =loop/ - moves to /dev/loop 57 * >disk/sda%1 - moves to /dev/disk/sdaN, makes /dev/sdaN a symlink 58 * 59 * Then "command args..." is executed (via sh -c 'command args...'). 60 * @:execute on creation, $:on deletion, *:on both. 61 * This happens regardless of /sys/class/.../dev existence. 62 */ 63 15 64 struct globals { 16 65 int root_major, root_minor; 17 }; 66 char *subsystem; 67 } FIX_ALIASING; 18 68 #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 */ 23 24 /* mknod in /dev based on a path like "/sys/block/hda/hda1" */ 69 70 /* Prevent infinite loops in /sys symlinks */ 71 #define MAX_SYSFS_DEPTH 3 72 73 /* We use additional 64+ bytes in make_device() */ 74 #define SCRATCH_SIZE 80 75 76 /* Builds an alias path. 77 * This function potentionally reallocates the alias parameter. 78 * Only used for ENABLE_FEATURE_MDEV_RENAME 79 */ 80 static char *build_alias(char *alias, const char *device_name) 81 { 82 char *dest; 83 84 /* ">bar/": rename to bar/device_name */ 85 /* ">bar[/]baz": rename to bar[/]baz */ 86 dest = strrchr(alias, '/'); 87 if (dest) { /* ">bar/[baz]" ? */ 88 *dest = '\0'; /* mkdir bar */ 89 bb_make_directory(alias, 0755, FILEUTILS_RECUR); 90 *dest = '/'; 91 if (dest[1] == '\0') { /* ">bar/" => ">bar/device_name" */ 92 dest = alias; 93 alias = concat_path_file(alias, device_name); 94 free(dest); 95 } 96 } 97 98 return alias; 99 } 100 101 /* mknod in /dev based on a path like "/sys/block/hda/hda1" 102 * NB1: path parameter needs to have SCRATCH_SIZE scratch bytes 103 * after NUL, but we promise to not mangle (IOW: to restore if needed) 104 * path string. 105 * NB2: "mdev -s" may call us many times, do not leak memory/fds! 106 */ 25 107 static void make_device(char *path, int delete) 26 108 { 27 c onst char *device_name;109 char *device_name, *subsystem_slash_devname; 28 110 int major, minor, type, len; 29 int mode = 0660; 30 uid_t uid = 0; 31 gid_t gid = 0; 32 char *temp = path + strlen(path); 33 char *command = NULL; 111 mode_t mode; 112 parser_t *parser; 34 113 35 114 /* Try to read major/minor string. Note that the kernel puts \n after 36 115 * the data, so we don't need to worry about null terminating the string 37 * because sscanf() will stop at the first nondigit, which \n is. We 38 * also depend on path having writeable space after it. */ 39 116 * because sscanf() will stop at the first nondigit, which \n is. 117 * We also depend on path having writeable space after it. 118 */ 119 major = -1; 40 120 if (!delete) { 41 strcat(path, "/dev"); 42 len = open_read_close(path, temp + 1, 64); 43 *temp++ = 0; 44 if (len < 1) return; 121 char *dev_maj_min = path + strlen(path); 122 123 strcpy(dev_maj_min, "/dev"); 124 len = open_read_close(path, dev_maj_min + 1, 64); 125 *dev_maj_min = '\0'; 126 if (len < 1) { 127 if (!ENABLE_FEATURE_MDEV_EXEC) 128 return; 129 /* no "dev" file, but we can still run scripts 130 * based on device name */ 131 } else if (sscanf(++dev_maj_min, "%u:%u", &major, &minor) != 2) { 132 major = -1; 133 } 45 134 } 135 /* else: for delete, -1 still deletes the node, but < -1 suppresses that */ 46 136 47 137 /* Determine device name, type, major and minor */ 48 49 device_name = bb_basename(path); 50 type = path[5]=='c' ? S_IFCHR : S_IFBLK; 51 52 /* If we have a config file, look up permissions for this device */ 53 54 if (ENABLE_FEATURE_MDEV_CONF) { 55 char *conf, *pos, *end; 56 int line, fd; 57 58 /* mmap the config file */ 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++) 138 device_name = (char*) bb_basename(path); 139 /* http://kernel.org/doc/pending/hotplug.txt says that only 140 * "/sys/block/..." is for block devices. "/sys/bus" etc is not. 141 * But since 2.6.25 block devices are also in /sys/class/block. 142 * We use strstr("/block/") to forestall future surprises. */ 143 type = S_IFCHR; 144 if (strstr(path, "/block/") || (G.subsystem && strncmp(G.subsystem, "block", 5) == 0)) 145 type = S_IFBLK; 146 147 /* Make path point to "subsystem/device_name" */ 148 subsystem_slash_devname = NULL; 149 /* Check for coldplug invocations first */ 150 if (strncmp(path, "/sys/block/", 11) == 0) /* legacy case */ 151 path += sizeof("/sys/") - 1; 152 else if (strncmp(path, "/sys/class/", 11) == 0) 153 path += sizeof("/sys/class/") - 1; 154 else { 155 /* Example of a hotplug invocation: 156 * SUBSYSTEM="block" 157 * DEVPATH="/sys" + "/devices/virtual/mtd/mtd3/mtdblock3" 158 * ("/sys" is added by mdev_main) 159 * - path does not contain subsystem 160 */ 161 subsystem_slash_devname = concat_path_file(G.subsystem, device_name); 162 path = subsystem_slash_devname; 163 } 164 165 /* If we have config file, look up user settings */ 166 if (ENABLE_FEATURE_MDEV_CONF) 167 parser = config_open2("/etc/mdev.conf", fopen_for_read); 168 169 do { 170 int keep_matching; 171 struct bb_uidgid_t ugid; 172 char *tokens[4]; 173 char *command = NULL; 174 char *alias = NULL; 175 char aliaslink = aliaslink; /* for compiler */ 176 177 /* Defaults in case we won't match any line */ 178 ugid.uid = ugid.gid = 0; 179 keep_matching = 0; 180 mode = 0660; 181 182 if (ENABLE_FEATURE_MDEV_CONF 183 && config_read(parser, tokens, 4, 3, "# \t", PARSE_NORMAL) 184 ) { 185 char *val; 186 char *str_to_match; 187 regmatch_t off[1 + 9 * ENABLE_FEATURE_MDEV_RENAME_REGEXP]; 188 189 val = tokens[0]; 190 keep_matching = ('-' == val[0]); 191 val += keep_matching; /* swallow leading dash */ 192 193 /* Match against either "subsystem/device_name" 194 * or "device_name" alone */ 195 str_to_match = strchr(val, '/') ? path : device_name; 196 197 /* Fields: regex uid:gid mode [alias] [cmd] */ 198 199 if (val[0] == '@') { 200 /* @major,minor[-minor2] */ 201 /* (useful when name is ambiguous: 202 * "/sys/class/usb/lp0" and 203 * "/sys/class/printer/lp0") */ 204 int cmaj, cmin0, cmin1, sc; 205 if (major < 0) 206 continue; /* no dev, no match */ 207 sc = sscanf(val, "@%u,%u-%u", &cmaj, &cmin0, &cmin1); 208 if (sc < 1 209 || major != cmaj 210 || (sc == 2 && minor != cmin0) 211 || (sc == 3 && (minor < cmin0 || minor > cmin1)) 212 ) { 213 continue; /* this line doesn't match */ 214 } 215 goto line_matches; 216 } 217 if (val[0] == '$') { 218 /* regex to match an environment variable */ 219 char *eq = strchr(++val, '='); 220 if (!eq) 221 continue; 222 *eq = '\0'; 223 str_to_match = getenv(val); 224 if (!str_to_match) 225 continue; 226 str_to_match -= strlen(val) + 1; 227 *eq = '='; 228 } 229 /* else: regex to match [subsystem/]device_name */ 230 82 231 { 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++) 116 ; 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; 232 regex_t match; 233 int result; 234 235 xregcomp(&match, val, REG_EXTENDED); 236 result = regexec(&match, str_to_match, ARRAY_SIZE(off), off, 0); 237 regfree(&match); 238 //bb_error_msg("matches:"); 239 //for (int i = 0; i < ARRAY_SIZE(off); i++) { 240 // if (off[i].rm_so < 0) continue; 241 // bb_error_msg("match %d: '%.*s'\n", i, 242 // (int)(off[i].rm_eo - off[i].rm_so), 243 // device_name + off[i].rm_so); 244 //} 245 246 /* If no match, skip rest of line */ 247 /* (regexec returns whole pattern as "range" 0) */ 248 if (result 249 || off[0].rm_so 250 || ((int)off[0].rm_eo != (int)strlen(str_to_match)) 251 ) { 252 continue; /* this line doesn't match */ 253 } 254 } 255 line_matches: 256 /* This line matches. Stop parsing after parsing 257 * the rest the line unless keep_matching == 1 */ 258 259 /* 2nd field: uid:gid - device ownership */ 260 if (get_uidgid(&ugid, tokens[1], 1) == 0) 261 bb_error_msg("unknown user/group %s on line %d", tokens[1], parser->lineno); 262 263 /* 3rd field: mode - device permissions */ 264 bb_parse_mode(tokens[2], &mode); 265 266 val = tokens[3]; 267 /* 4th field (opt): ">|=alias" or "!" to not create the node */ 268 269 if (ENABLE_FEATURE_MDEV_RENAME && val) { 270 char *a, *s, *st; 271 272 a = val; 273 s = strchrnul(val, ' '); 274 st = strchrnul(val, '\t'); 275 if (st < s) 276 s = st; 277 st = (s[0] && s[1]) ? s+1 : NULL; 278 279 aliaslink = a[0]; 280 if (aliaslink == '!' && s == a+1) { 281 val = st; 282 /* "!": suppress node creation/deletion */ 283 major = -2; 284 } 285 else if (aliaslink == '>' || aliaslink == '=') { 286 val = st; 287 s[0] = '\0'; 288 if (ENABLE_FEATURE_MDEV_RENAME_REGEXP) { 289 char *p; 290 unsigned i, n; 291 292 /* substitute %1..9 with off[1..9], if any */ 293 n = 0; 294 s = a; 295 while (*s) 296 if (*s++ == '%') 297 n++; 298 299 p = alias = xzalloc(strlen(a) + n * strlen(str_to_match)); 300 s = a + 1; 301 while (*s) { 302 *p = *s; 303 if ('%' == *s) { 304 i = (s[1] - '0'); 305 if (i <= 9 && off[i].rm_so >= 0) { 306 n = off[i].rm_eo - off[i].rm_so; 307 strncpy(p, str_to_match + off[i].rm_so, n); 308 p += n - 1; 309 s++; 310 } 311 } 312 p++; 313 s++; 314 } 315 } else { 316 alias = xstrdup(a + 1); 128 317 } 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 } 140 } 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; 162 } 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; 171 } 172 munmap(conf, len); 173 end_parse: /* nothing */ ; 174 } 175 176 umask(0); 177 if (!delete) { 178 if (sscanf(temp, "%d:%d", &major, &minor) != 2) return; 179 if (mknod(device_name, mode | type, makedev(major, minor)) && errno != EEXIST) 180 bb_perror_msg_and_die("mknod %s", device_name); 181 182 if (major == root_major && minor == root_minor) 183 symlink(device_name, "root"); 184 185 if (ENABLE_FEATURE_MDEV_CONF) chown(device_name, uid, gid); 186 } 187 if (command) { 188 /* setenv will leak memory, so use putenv */ 189 char *s = xasprintf("MDEV=%s", device_name); 190 putenv(s); 191 if (system(command) == -1) 192 bb_perror_msg_and_die("cannot run %s", command); 193 s[4] = '\0'; 194 unsetenv(s); 195 free(s); 196 free(command); 197 } 198 if (delete) unlink(device_name); 318 } 319 } 320 321 if (ENABLE_FEATURE_MDEV_EXEC && val) { 322 const char *s = "$@*"; 323 const char *s2 = strchr(s, val[0]); 324 325 if (!s2) { 326 bb_error_msg("bad line %u", parser->lineno); 327 if (ENABLE_FEATURE_MDEV_RENAME) 328 free(alias); 329 continue; 330 } 331 332 /* Are we running this command now? 333 * Run $cmd on delete, @cmd on create, *cmd on both 334 */ 335 if (s2 - s != delete) { 336 /* We are here if: '*', 337 * or: '@' and delete = 0, 338 * or: '$' and delete = 1 339 */ 340 command = xstrdup(val + 1); 341 } 342 } 343 } 344 345 /* End of field parsing */ 346 347 /* "Execute" the line we found */ 348 { 349 const char *node_name; 350 351 node_name = device_name; 352 if (ENABLE_FEATURE_MDEV_RENAME && alias) 353 node_name = alias = build_alias(alias, device_name); 354 355 if (!delete && major >= 0) { 356 if (mknod(node_name, mode | type, makedev(major, minor)) && errno != EEXIST) 357 bb_perror_msg("can't create '%s'", node_name); 358 if (major == G.root_major && minor == G.root_minor) 359 symlink(node_name, "root"); 360 if (ENABLE_FEATURE_MDEV_CONF) { 361 chmod(node_name, mode); 362 chown(node_name, ugid.uid, ugid.gid); 363 } 364 if (ENABLE_FEATURE_MDEV_RENAME && alias) { 365 if (aliaslink == '>') 366 symlink(node_name, device_name); 367 } 368 } 369 370 if (ENABLE_FEATURE_MDEV_EXEC && command) { 371 /* setenv will leak memory, use putenv/unsetenv/free */ 372 char *s = xasprintf("%s=%s", "MDEV", node_name); 373 char *s1 = xasprintf("%s=%s", "SUBSYSTEM", G.subsystem); 374 putenv(s); 375 putenv(s1); 376 if (system(command) == -1) 377 bb_perror_msg("can't run '%s'", command); 378 bb_unsetenv_and_free(s1); 379 bb_unsetenv_and_free(s); 380 free(command); 381 } 382 383 if (delete && major >= -1) { 384 if (ENABLE_FEATURE_MDEV_RENAME && alias) { 385 if (aliaslink == '>') 386 unlink(device_name); 387 } 388 unlink(node_name); 389 } 390 391 if (ENABLE_FEATURE_MDEV_RENAME) 392 free(alias); 393 } 394 395 /* We found matching line. 396 * Stop unless it was prefixed with '-' */ 397 if (ENABLE_FEATURE_MDEV_CONF && !keep_matching) 398 break; 399 400 /* end of "while line is read from /etc/mdev.conf" */ 401 } while (ENABLE_FEATURE_MDEV_CONF); 402 403 if (ENABLE_FEATURE_MDEV_CONF) 404 config_close(parser); 405 free(subsystem_slash_devname); 199 406 } 200 407 201 408 /* File callback for /sys/ traversal */ 202 static int fileAction(const char *fileName, struct stat *statbuf, 203 void *userData, int depth) 409 static int FAST_FUNC fileAction(const char *fileName, 410 struct stat *statbuf UNUSED_PARAM, 411 void *userData, 412 int depth UNUSED_PARAM) 204 413 { 205 size_t len = strlen(fileName) - 4; 414 size_t len = strlen(fileName) - 4; /* can't underflow */ 206 415 char *scratch = userData; 207 416 208 if (strcmp(fileName + len, "/dev")) 417 /* len check is for paranoid reasons */ 418 if (strcmp(fileName + len, "/dev") != 0 || len >= PATH_MAX) 209 419 return FALSE; 210 420 211 421 strcpy(scratch, fileName); 212 scratch[len] = 0;213 make_device(scratch, 0);422 scratch[len] = '\0'; 423 make_device(scratch, /*delete:*/ 0); 214 424 215 425 return TRUE; … … 217 427 218 428 /* Directory callback for /sys/ traversal */ 219 static int dirAction(const char *fileName, struct stat *statbuf, 220 void *userData, int depth) 429 static int FAST_FUNC dirAction(const char *fileName UNUSED_PARAM, 430 struct stat *statbuf UNUSED_PARAM, 431 void *userData UNUSED_PARAM, 432 int depth) 221 433 { 434 /* Extract device subsystem -- the name of the directory 435 * under /sys/class/ */ 436 if (1 == depth) { 437 free(G.subsystem); 438 G.subsystem = strrchr(fileName, '/'); 439 if (G.subsystem) 440 G.subsystem = xstrdup(G.subsystem + 1); 441 } 442 222 443 return (depth >= MAX_SYSFS_DEPTH ? SKIP : TRUE); 223 444 } … … 234 455 * - kernel loads firmware into device 235 456 */ 236 static void load_firmware(const char * const firmware, const char *constsysfs_path)457 static void load_firmware(const char *firmware, const char *sysfs_path) 237 458 { 238 459 int cnt; 239 460 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 461 247 462 /* check for /lib/firmware/$FIRMWARE */ … … 256 471 for (cnt = 0; cnt < 30; ++cnt) { 257 472 loading_fd = open("loading", O_WRONLY); 258 if (loading_fd == -1) 259 sleep(1); 260 else 261 break; 473 if (loading_fd != -1) 474 goto loading; 475 sleep(1); 262 476 } 263 if (loading_fd == -1) 477 goto out; 478 479 loading: 480 /* tell kernel we're loading by "echo 1 > /sys/$DEVPATH/loading" */ 481 if (full_write(loading_fd, "1", 1) != 1) 264 482 goto out; 265 483 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 */ 484 /* load firmware into /sys/$DEVPATH/data */ 271 485 data_fd = open("data", O_WRONLY); 272 486 if (data_fd == -1) … … 274 488 cnt = bb_copyfd_eof(firmware_fd, data_fd); 275 489 276 /* tell kernel result by `echo [0|-1] > /sys/$DEVPATH/loading`*/490 /* tell kernel result by "echo [0|-1] > /sys/$DEVPATH/loading" */ 277 491 if (cnt > 0) 278 write(loading_fd, "0", 1);492 full_write(loading_fd, "0", 1); 279 493 else 280 write(loading_fd, "-1", 2);494 full_write(loading_fd, "-1", 2); 281 495 282 496 out: … … 288 502 } 289 503 290 int mdev_main(int argc, char **argv) ;291 int mdev_main(int argc , char **argv)504 int mdev_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; 505 int mdev_main(int argc UNUSED_PARAM, char **argv) 292 506 { 293 char *action; 294 char *env_path; 295 RESERVE_CONFIG_BUFFER(temp,PATH_MAX); 507 RESERVE_CONFIG_BUFFER(temp, PATH_MAX + SCRATCH_SIZE); 508 509 /* We can be called as hotplug helper */ 510 /* Kernel cannot provide suitable stdio fds for us, do it ourself */ 511 bb_sanitize_stdio(); 512 513 /* Force the configuration file settings exactly */ 514 umask(0); 296 515 297 516 xchdir("/dev"); 298 517 299 /* Scan */ 300 301 if (argc == 2 && !strcmp(argv[1],"-s")) { 518 if (argv[1] && strcmp(argv[1], "-s") == 0) { 519 /* Scan: 520 * mdev -s 521 */ 302 522 struct stat st; 303 523 304 524 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 525 G.root_major = major(st.st_dev); 526 G.root_minor = minor(st.st_dev); 527 528 /* ACTION_FOLLOWLINKS is needed since in newer kernels 529 * /sys/block/loop* (for example) are symlinks to dirs, 530 * not real directories. 531 * (kernel's CONFIG_SYSFS_DEPRECATED makes them real dirs, 532 * but we can't enforce that on users) 533 */ 534 if (access("/sys/class/block", F_OK) != 0) { 535 /* Scan obsolete /sys/block only if /sys/class/block 536 * doesn't exist. Otherwise we'll have dupes. 537 * Also, do not complain if it doesn't exist. 538 * Some people configure kernel to have no blockdevs. 539 */ 540 recursive_action("/sys/block", 541 ACTION_RECURSE | ACTION_FOLLOWLINKS | ACTION_QUIET, 542 fileAction, dirAction, temp, 0); 543 } 312 544 recursive_action("/sys/class", 313 545 ACTION_RECURSE | ACTION_FOLLOWLINKS, 314 546 fileAction, dirAction, temp, 0); 315 316 /* Hotplug */317 318 547 } else { 548 char *fw; 549 char *seq; 550 char *action; 551 char *env_path; 552 static const char keywords[] ALIGN1 = "remove\0add\0"; 553 enum { OP_remove = 0, OP_add }; 554 smalluint op; 555 556 /* Hotplug: 557 * env ACTION=... DEVPATH=... SUBSYSTEM=... [SEQNUM=...] mdev 558 * ACTION can be "add" or "remove" 559 * DEVPATH is like "/block/sda" or "/class/input/mice" 560 */ 319 561 action = getenv("ACTION"); 320 562 env_path = getenv("DEVPATH"); 321 if (!action || !env_path) 563 G.subsystem = getenv("SUBSYSTEM"); 564 if (!action || !env_path /*|| !G.subsystem*/) 322 565 bb_show_usage(); 323 324 sprintf(temp, "/sys%s", env_path); 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); 566 fw = getenv("FIRMWARE"); 567 op = index_in_strings(keywords, action); 568 /* If it exists, does /dev/mdev.seq match $SEQNUM? 569 * If it does not match, earlier mdev is running 570 * in parallel, and we need to wait */ 571 seq = getenv("SEQNUM"); 572 if (seq) { 573 int timeout = 2000 / 32; /* 2000 msec */ 574 do { 575 int seqlen; 576 char seqbuf[sizeof(int)*3 + 2]; 577 578 seqlen = open_read_close("mdev.seq", seqbuf, sizeof(seqbuf-1)); 579 if (seqlen < 0) { 580 seq = NULL; 581 break; 582 } 583 seqbuf[seqlen] = '\0'; 584 if (seqbuf[0] == '\n' /* seed file? */ 585 || strcmp(seq, seqbuf) == 0 /* correct idx? */ 586 ) { 587 break; 588 } 589 usleep(32*1000); 590 } while (--timeout); 591 } 592 593 snprintf(temp, PATH_MAX, "/sys%s", env_path); 594 if (op == OP_remove) { 595 /* Ignoring "remove firmware". It was reported 596 * to happen and to cause erroneous deletion 597 * of device nodes. */ 598 if (!fw) 599 make_device(temp, /*delete:*/ 1); 600 } 601 else if (op == OP_add) { 602 make_device(temp, /*delete:*/ 0); 603 if (ENABLE_FEATURE_MDEV_LOAD_FIRMWARE) { 604 if (fw) 605 load_firmware(fw, temp); 606 } 607 } 608 609 if (seq) { 610 xopen_xwrite_close("mdev.seq", utoa(xatou(seq) + 1)); 332 611 } 333 612 } 334 613 335 if (ENABLE_FEATURE_CLEAN_UP) RELEASE_CONFIG_BUFFER(temp); 336 return 0; 614 if (ENABLE_FEATURE_CLEAN_UP) 615 RELEASE_CONFIG_BUFFER(temp); 616 617 return EXIT_SUCCESS; 337 618 }
Note:
See TracChangeset
for help on using the changeset viewer.