mdev: add support for $DEVNAME and /dev/mdev.log debug aid
function old new delta make_device 1843 2083 +240 mdev_main 712 804 +92 packed_usage 29236 29251 +15 keywords 288 19 -269 ------------------------------------------------------------------------------ (add/remove: 0/0 grow/shrink: 3/1 up/down: 347/-269) Total: 78 bytes text data bss dec hex filename 887506 497 7584 895587 daa63 busybox_old 887982 497 7584 896063 dac3f busybox_unstripped Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
This commit is contained in:
parent
7c8aa2b47c
commit
516530c932
@ -108,6 +108,3 @@ usbdev[0-9].[0-9]_.* root:root 660
|
|||||||
# zaptel devices
|
# zaptel devices
|
||||||
zap(.*) root:dialout 660 =zap/%1
|
zap(.*) root:dialout 660 =zap/%1
|
||||||
dahdi!(.*) root:dialout 660 =dahdi/%1
|
dahdi!(.*) root:dialout 660 =dahdi/%1
|
||||||
|
|
||||||
# If $DEVNAME exists, create device with this name
|
|
||||||
$DEVNAME=bus/usb/.* 0:0 660 ! @mkdir -p "`dirname "$DEVNAME"`"; mknod -m 0660 "$DEVNAME" c "$MAJOR" "$MINOR"
|
|
||||||
|
@ -93,6 +93,8 @@
|
|||||||
//usage: "If /dev/mdev.seq file exists, mdev will wait for its value\n"
|
//usage: "If /dev/mdev.seq file exists, mdev will wait for its value\n"
|
||||||
//usage: "to match $SEQNUM variable. This prevents plug/unplug races.\n"
|
//usage: "to match $SEQNUM variable. This prevents plug/unplug races.\n"
|
||||||
//usage: "To activate this feature, create empty /dev/mdev.seq at boot."
|
//usage: "To activate this feature, create empty /dev/mdev.seq at boot."
|
||||||
|
//usage: "\n"
|
||||||
|
//usage: "If /dev/mdev.log file exists, debug log will be appended to it.\n"
|
||||||
|
|
||||||
#include "libbb.h"
|
#include "libbb.h"
|
||||||
#include "xregex.h"
|
#include "xregex.h"
|
||||||
@ -139,6 +141,98 @@
|
|||||||
* This happens regardless of /sys/class/.../dev existence.
|
* This happens regardless of /sys/class/.../dev existence.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
/* Kernel's hotplug environment constantly changes.
|
||||||
|
* Here are new cases I observed on 3.1.0:
|
||||||
|
*
|
||||||
|
* Case with $DEVNAME and $DEVICE, not just $DEVPATH:
|
||||||
|
* ACTION=add
|
||||||
|
* BUSNUM=001
|
||||||
|
* DEVICE=/proc/bus/usb/001/003
|
||||||
|
* DEVNAME=bus/usb/001/003
|
||||||
|
* DEVNUM=003
|
||||||
|
* DEVPATH=/devices/pci0000:00/0000:00:02.1/usb1/1-5
|
||||||
|
* DEVTYPE=usb_device
|
||||||
|
* MAJOR=189
|
||||||
|
* MINOR=2
|
||||||
|
* PRODUCT=18d1/4e12/227
|
||||||
|
* SUBSYSTEM=usb
|
||||||
|
* TYPE=0/0/0
|
||||||
|
*
|
||||||
|
* Case with $DEVICE, but no $DEVNAME - apparenty, usb iface notification?
|
||||||
|
* "Please load me a module" thing?
|
||||||
|
* ACTION=add
|
||||||
|
* DEVICE=/proc/bus/usb/001/003
|
||||||
|
* DEVPATH=/devices/pci0000:00/0000:00:02.1/usb1/1-5/1-5:1.0
|
||||||
|
* DEVTYPE=usb_interface
|
||||||
|
* INTERFACE=8/6/80
|
||||||
|
* MODALIAS=usb:v18D1p4E12d0227dc00dsc00dp00ic08isc06ip50
|
||||||
|
* PRODUCT=18d1/4e12/227
|
||||||
|
* SUBSYSTEM=usb
|
||||||
|
* TYPE=0/0/0
|
||||||
|
*
|
||||||
|
* ACTION=add
|
||||||
|
* DEVPATH=/devices/pci0000:00/0000:00:02.1/usb1/1-5/1-5:1.0/host5
|
||||||
|
* DEVTYPE=scsi_host
|
||||||
|
* SUBSYSTEM=scsi
|
||||||
|
*
|
||||||
|
* ACTION=add
|
||||||
|
* DEVPATH=/devices/pci0000:00/0000:00:02.1/usb1/1-5/1-5:1.0/host5/scsi_host/host5
|
||||||
|
* SUBSYSTEM=scsi_host
|
||||||
|
*
|
||||||
|
* ACTION=add
|
||||||
|
* DEVPATH=/devices/pci0000:00/0000:00:02.1/usb1/1-5/1-5:1.0/host5/target5:0:0
|
||||||
|
* DEVTYPE=scsi_target
|
||||||
|
* SUBSYSTEM=scsi
|
||||||
|
*
|
||||||
|
* Case with strange $MODALIAS:
|
||||||
|
* ACTION=add
|
||||||
|
* DEVPATH=/devices/pci0000:00/0000:00:02.1/usb1/1-5/1-5:1.0/host5/target5:0:0/5:0:0:0
|
||||||
|
* DEVTYPE=scsi_device
|
||||||
|
* MODALIAS=scsi:t-0x00
|
||||||
|
* SUBSYSTEM=scsi
|
||||||
|
*
|
||||||
|
* ACTION=add
|
||||||
|
* DEVPATH=/devices/pci0000:00/0000:00:02.1/usb1/1-5/1-5:1.0/host5/target5:0:0/5:0:0:0/scsi_disk/5:0:0:0
|
||||||
|
* SUBSYSTEM=scsi_disk
|
||||||
|
*
|
||||||
|
* ACTION=add
|
||||||
|
* DEVPATH=/devices/pci0000:00/0000:00:02.1/usb1/1-5/1-5:1.0/host5/target5:0:0/5:0:0:0/scsi_device/5:0:0:0
|
||||||
|
* SUBSYSTEM=scsi_device
|
||||||
|
*
|
||||||
|
* Case with explicit $MAJOR/$MINOR (no need to read /sys/$DEVPATH/dev?):
|
||||||
|
* ACTION=add
|
||||||
|
* DEVNAME=bsg/5:0:0:0
|
||||||
|
* DEVPATH=/devices/pci0000:00/0000:00:02.1/usb1/1-5/1-5:1.0/host5/target5:0:0/5:0:0:0/bsg/5:0:0:0
|
||||||
|
* MAJOR=253
|
||||||
|
* MINOR=1
|
||||||
|
* SUBSYSTEM=bsg
|
||||||
|
*
|
||||||
|
* ACTION=add
|
||||||
|
* DEVPATH=/devices/virtual/bdi/8:16
|
||||||
|
* SUBSYSTEM=bdi
|
||||||
|
*
|
||||||
|
* ACTION=add
|
||||||
|
* DEVNAME=sdb
|
||||||
|
* DEVPATH=/block/sdb
|
||||||
|
* DEVTYPE=disk
|
||||||
|
* MAJOR=8
|
||||||
|
* MINOR=16
|
||||||
|
* SUBSYSTEM=block
|
||||||
|
*
|
||||||
|
* Case with ACTION=change:
|
||||||
|
* ACTION=change
|
||||||
|
* DEVNAME=sdb
|
||||||
|
* DEVPATH=/block/sdb
|
||||||
|
* DEVTYPE=disk
|
||||||
|
* DISK_MEDIA_CHANGE=1
|
||||||
|
* MAJOR=8
|
||||||
|
* MINOR=16
|
||||||
|
* SUBSYSTEM=block
|
||||||
|
*/
|
||||||
|
|
||||||
|
static const char keywords[] ALIGN1 = "add\0remove\0change\0";
|
||||||
|
enum { OP_add, OP_remove };
|
||||||
|
|
||||||
struct rule {
|
struct rule {
|
||||||
bool keep_matching;
|
bool keep_matching;
|
||||||
bool regex_compiled;
|
bool regex_compiled;
|
||||||
@ -154,6 +248,7 @@ struct rule {
|
|||||||
|
|
||||||
struct globals {
|
struct globals {
|
||||||
int root_major, root_minor;
|
int root_major, root_minor;
|
||||||
|
smallint verbose;
|
||||||
char *subsystem;
|
char *subsystem;
|
||||||
#if ENABLE_FEATURE_MDEV_CONF
|
#if ENABLE_FEATURE_MDEV_CONF
|
||||||
const char *filename;
|
const char *filename;
|
||||||
@ -366,13 +461,17 @@ static char *build_alias(char *alias, const char *device_name)
|
|||||||
* after NUL, but we promise to not mangle (IOW: to restore if needed)
|
* after NUL, but we promise to not mangle (IOW: to restore if needed)
|
||||||
* path string.
|
* path string.
|
||||||
* NB2: "mdev -s" may call us many times, do not leak memory/fds!
|
* NB2: "mdev -s" may call us many times, do not leak memory/fds!
|
||||||
|
*
|
||||||
|
* device_name = $DEVNAME (may be NULL)
|
||||||
|
* path = /sys/$DEVPATH
|
||||||
*/
|
*/
|
||||||
static void make_device(char *path, int delete)
|
static void make_device(char *device_name, char *path, int operation)
|
||||||
{
|
{
|
||||||
char *device_name, *subsystem_slash_devname;
|
char *subsystem_slash_devname;
|
||||||
int major, minor, type, len;
|
int major, minor, type, len;
|
||||||
|
|
||||||
dbg("%s('%s', delete:%d)", __func__, path, delete);
|
if (G.verbose)
|
||||||
|
bb_error_msg("make_device: %s, %s, op:%d", device_name, path, operation);
|
||||||
|
|
||||||
/* Try to read major/minor string. Note that the kernel puts \n after
|
/* Try to read major/minor string. Note that the kernel puts \n after
|
||||||
* the data, so we don't need to worry about null terminating the string
|
* the data, so we don't need to worry about null terminating the string
|
||||||
@ -380,7 +479,7 @@ static void make_device(char *path, int delete)
|
|||||||
* We also depend on path having writeable space after it.
|
* We also depend on path having writeable space after it.
|
||||||
*/
|
*/
|
||||||
major = -1;
|
major = -1;
|
||||||
if (!delete) {
|
if (operation != OP_remove) {
|
||||||
char *dev_maj_min = path + strlen(path);
|
char *dev_maj_min = path + strlen(path);
|
||||||
|
|
||||||
strcpy(dev_maj_min, "/dev");
|
strcpy(dev_maj_min, "/dev");
|
||||||
@ -398,7 +497,8 @@ static void make_device(char *path, int delete)
|
|||||||
/* else: for delete, -1 still deletes the node, but < -1 suppresses that */
|
/* else: for delete, -1 still deletes the node, but < -1 suppresses that */
|
||||||
|
|
||||||
/* Determine device name, type, major and minor */
|
/* Determine device name, type, major and minor */
|
||||||
device_name = (char*) bb_basename(path);
|
if (!device_name)
|
||||||
|
device_name = (char*) bb_basename(path);
|
||||||
/* http://kernel.org/doc/pending/hotplug.txt says that only
|
/* http://kernel.org/doc/pending/hotplug.txt says that only
|
||||||
* "/sys/block/..." is for block devices. "/sys/bus" etc is not.
|
* "/sys/block/..." is for block devices. "/sys/bus" etc is not.
|
||||||
* But since 2.6.25 block devices are also in /sys/class/block.
|
* But since 2.6.25 block devices are also in /sys/class/block.
|
||||||
@ -434,7 +534,7 @@ static void make_device(char *path, int delete)
|
|||||||
char *command;
|
char *command;
|
||||||
char *alias;
|
char *alias;
|
||||||
char aliaslink = aliaslink; /* for compiler */
|
char aliaslink = aliaslink; /* for compiler */
|
||||||
const char *node_name;
|
char *node_name;
|
||||||
const struct rule *rule;
|
const struct rule *rule;
|
||||||
|
|
||||||
str_to_match = "";
|
str_to_match = "";
|
||||||
@ -456,6 +556,8 @@ static void make_device(char *path, int delete)
|
|||||||
if (!str_to_match)
|
if (!str_to_match)
|
||||||
continue;
|
continue;
|
||||||
} else {
|
} else {
|
||||||
|
//TODO: $DEVNAME can have slashes too,
|
||||||
|
// we should stop abusing '/' as a special syntax in our regex'es
|
||||||
/* regex to match [subsystem/]device_name */
|
/* regex to match [subsystem/]device_name */
|
||||||
str_to_match = (rule->regex_has_slash ? path : device_name);
|
str_to_match = (rule->regex_has_slash ? path : device_name);
|
||||||
}
|
}
|
||||||
@ -537,7 +639,7 @@ static void make_device(char *path, int delete)
|
|||||||
/* Are we running this command now?
|
/* Are we running this command now?
|
||||||
* Run $cmd on delete, @cmd on create, *cmd on both
|
* Run $cmd on delete, @cmd on create, *cmd on both
|
||||||
*/
|
*/
|
||||||
if (s2 - s != delete) {
|
if (s2 - s != (operation == OP_remove) || *s2 == '*') {
|
||||||
/* We are here if: '*',
|
/* We are here if: '*',
|
||||||
* or: '@' and delete = 0,
|
* or: '@' and delete = 0,
|
||||||
* or: '$' and delete = 1
|
* or: '$' and delete = 1
|
||||||
@ -556,8 +658,15 @@ static void make_device(char *path, int delete)
|
|||||||
dbg("alias2:'%s'", alias);
|
dbg("alias2:'%s'", alias);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!delete && major >= 0) {
|
if (operation == OP_add && major >= 0) {
|
||||||
dbg("mknod('%s',%o,(%d,%d))", node_name, rule->mode | type, major, minor);
|
char *slash = strrchr(node_name, '/');
|
||||||
|
if (slash) {
|
||||||
|
*slash = '\0';
|
||||||
|
bb_make_directory(node_name, 0755, FILEUTILS_RECUR);
|
||||||
|
*slash = '/';
|
||||||
|
}
|
||||||
|
if (G.verbose)
|
||||||
|
bb_error_msg("mknod: %s (%d,%d) %o", node_name, major, minor, rule->mode | type);
|
||||||
if (mknod(node_name, rule->mode | type, makedev(major, minor)) && errno != EEXIST)
|
if (mknod(node_name, rule->mode | type, makedev(major, minor)) && errno != EEXIST)
|
||||||
bb_perror_msg("can't create '%s'", node_name);
|
bb_perror_msg("can't create '%s'", node_name);
|
||||||
if (major == G.root_major && minor == G.root_minor)
|
if (major == G.root_major && minor == G.root_minor)
|
||||||
@ -571,6 +680,8 @@ static void make_device(char *path, int delete)
|
|||||||
//TODO: on devtmpfs, device_name already exists and symlink() fails.
|
//TODO: on devtmpfs, device_name already exists and symlink() fails.
|
||||||
//End result is that instead of symlink, we have two nodes.
|
//End result is that instead of symlink, we have two nodes.
|
||||||
//What should be done?
|
//What should be done?
|
||||||
|
if (G.verbose)
|
||||||
|
bb_error_msg("symlink: %s", device_name);
|
||||||
symlink(node_name, device_name);
|
symlink(node_name, device_name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -582,17 +693,24 @@ static void make_device(char *path, int delete)
|
|||||||
char *s1 = xasprintf("%s=%s", "SUBSYSTEM", G.subsystem);
|
char *s1 = xasprintf("%s=%s", "SUBSYSTEM", G.subsystem);
|
||||||
putenv(s);
|
putenv(s);
|
||||||
putenv(s1);
|
putenv(s1);
|
||||||
|
if (G.verbose)
|
||||||
|
bb_error_msg("running: %s", command);
|
||||||
if (system(command) == -1)
|
if (system(command) == -1)
|
||||||
bb_perror_msg("can't run '%s'", command);
|
bb_perror_msg("can't run '%s'", command);
|
||||||
bb_unsetenv_and_free(s1);
|
bb_unsetenv_and_free(s1);
|
||||||
bb_unsetenv_and_free(s);
|
bb_unsetenv_and_free(s);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (delete && major >= -1) {
|
if (operation == OP_remove && major >= -1) {
|
||||||
if (ENABLE_FEATURE_MDEV_RENAME && alias) {
|
if (ENABLE_FEATURE_MDEV_RENAME && alias) {
|
||||||
if (aliaslink == '>')
|
if (aliaslink == '>') {
|
||||||
|
if (G.verbose)
|
||||||
|
bb_error_msg("unlink: %s", device_name);
|
||||||
unlink(device_name);
|
unlink(device_name);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
if (G.verbose)
|
||||||
|
bb_error_msg("unlink: %s", node_name);
|
||||||
unlink(node_name);
|
unlink(node_name);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -624,7 +742,7 @@ static int FAST_FUNC fileAction(const char *fileName,
|
|||||||
|
|
||||||
strcpy(scratch, fileName);
|
strcpy(scratch, fileName);
|
||||||
scratch[len] = '\0';
|
scratch[len] = '\0';
|
||||||
make_device(scratch, /*delete:*/ 0);
|
make_device(/*DEVNAME:*/ NULL, scratch, OP_add);
|
||||||
|
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
@ -762,9 +880,8 @@ int mdev_main(int argc UNUSED_PARAM, char **argv)
|
|||||||
char *fw;
|
char *fw;
|
||||||
char *seq;
|
char *seq;
|
||||||
char *action;
|
char *action;
|
||||||
char *env_path;
|
char *env_devname;
|
||||||
static const char keywords[] ALIGN1 = "remove\0add\0";
|
char *env_devpath;
|
||||||
enum { OP_remove = 0, OP_add };
|
|
||||||
smalluint op;
|
smalluint op;
|
||||||
|
|
||||||
/* Hotplug:
|
/* Hotplug:
|
||||||
@ -773,9 +890,10 @@ int mdev_main(int argc UNUSED_PARAM, char **argv)
|
|||||||
* DEVPATH is like "/block/sda" or "/class/input/mice"
|
* DEVPATH is like "/block/sda" or "/class/input/mice"
|
||||||
*/
|
*/
|
||||||
action = getenv("ACTION");
|
action = getenv("ACTION");
|
||||||
env_path = getenv("DEVPATH");
|
env_devname = getenv("DEVNAME"); /* can be NULL */
|
||||||
|
env_devpath = getenv("DEVPATH");
|
||||||
G.subsystem = getenv("SUBSYSTEM");
|
G.subsystem = getenv("SUBSYSTEM");
|
||||||
if (!action || !env_path /*|| !G.subsystem*/)
|
if (!action || !env_devpath /*|| !G.subsystem*/)
|
||||||
bb_show_usage();
|
bb_show_usage();
|
||||||
fw = getenv("FIRMWARE");
|
fw = getenv("FIRMWARE");
|
||||||
op = index_in_strings(keywords, action);
|
op = index_in_strings(keywords, action);
|
||||||
@ -804,16 +922,25 @@ int mdev_main(int argc UNUSED_PARAM, char **argv)
|
|||||||
} while (--timeout);
|
} while (--timeout);
|
||||||
}
|
}
|
||||||
|
|
||||||
snprintf(temp, PATH_MAX, "/sys%s", env_path);
|
{
|
||||||
|
int logfd = open("/dev/mdev.log", O_WRONLY | O_APPEND);
|
||||||
|
if (logfd >= 0) {
|
||||||
|
xmove_fd(logfd, STDERR_FILENO);
|
||||||
|
G.verbose = 1;
|
||||||
|
bb_error_msg("pid: %u seq: %s action: %s", getpid(), seq, action);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
snprintf(temp, PATH_MAX, "/sys%s", env_devpath);
|
||||||
if (op == OP_remove) {
|
if (op == OP_remove) {
|
||||||
/* Ignoring "remove firmware". It was reported
|
/* Ignoring "remove firmware". It was reported
|
||||||
* to happen and to cause erroneous deletion
|
* to happen and to cause erroneous deletion
|
||||||
* of device nodes. */
|
* of device nodes. */
|
||||||
if (!fw)
|
if (!fw)
|
||||||
make_device(temp, /*delete:*/ 1);
|
make_device(env_devname, temp, op);
|
||||||
}
|
}
|
||||||
else if (op == OP_add) {
|
else if (op == OP_add) {
|
||||||
make_device(temp, /*delete:*/ 0);
|
make_device(env_devname, temp, op);
|
||||||
if (ENABLE_FEATURE_MDEV_LOAD_FIRMWARE) {
|
if (ENABLE_FEATURE_MDEV_LOAD_FIRMWARE) {
|
||||||
if (fw)
|
if (fw)
|
||||||
load_firmware(fw, temp);
|
load_firmware(fw, temp);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user