From 3312aa8379d877044def52f3b3be5c912a5e61a2 Mon Sep 17 00:00:00 2001 From: Greg Hackmann Date: Mon, 18 Nov 2013 15:24:40 -0800 Subject: [PATCH] init: add subsystem rules to ueventd.rc By default ueventd creates device nodes under /dev based on the ueventd DEVPATH. Several subsystems have special rules which are hardcoded in devices.c. Moving forward these special rules should go in ueventd.rc. Special rules have the syntax: subsystem devname (uevent_devname|uevent_devpath) [dirname ] Devices matching SUBSYSTEM= will be populated under . dirname is optional and defaults to /dev. If dirname is provided, must start with "/". If devname is uevent_devname, ueventd will create the device node as /DEVNAME. DEVNAME may include intermediate subdirectories, which ueventd will automatically create. If devname is uevent_devpath, ueventd will use the legacy behavior of computing DEVPATH_BASE=basepath(DEVPATH), and creating the device node as /DEVPATH_BASE. The new parsing code is based on init_parser.c, with small tweaks to handle commands which don't fall under a section header. Change-Id: I3bd1b59d7e62dfc9d289cf6ae889e237fb5bd7c5 Signed-off-by: Greg Hackmann --- init/devices.c | 87 ++++++++++++------- init/ueventd.h | 15 ++++ init/ueventd_keywords.h | 15 ++++ init/ueventd_parser.c | 181 +++++++++++++++++++++++++++++++++++++++- init/ueventd_parser.h | 3 + 5 files changed, 268 insertions(+), 33 deletions(-) create mode 100644 init/ueventd_keywords.h diff --git a/init/devices.c b/init/devices.c index 2551ca83f..f7df45340 100644 --- a/init/devices.c +++ b/init/devices.c @@ -44,6 +44,7 @@ #include #include "devices.h" +#include "ueventd_parser.h" #include "util.h" #include "log.h" @@ -560,48 +561,76 @@ static void handle_block_device_event(struct uevent *uevent) uevent->major, uevent->minor, links); } +#define DEVPATH_LEN 96 + +static bool assemble_devpath(char *devpath, const char *dirname, + const char *devname) +{ + int s = snprintf(devpath, DEVPATH_LEN, "%s/%s", dirname, devname); + if (s < 0) { + ERROR("failed to assemble device path (%s); ignoring event\n", + strerror(errno)); + return false; + } else if (s >= DEVPATH_LEN) { + ERROR("%s/%s exceeds %u-character limit on path; ignoring event\n", + dirname, devname, DEVPATH_LEN); + return false; + } + return true; +} + +static void mkdir_recursive_for_devpath(const char *devpath) +{ + char dir[DEVPATH_LEN]; + char *slash; + + strcpy(dir, devpath); + slash = strrchr(dir, '/'); + *slash = '\0'; + mkdir_recursive(dir, 0755); +} + static void handle_generic_device_event(struct uevent *uevent) { char *base; const char *name; - char devpath[96] = {0}; + char devpath[DEVPATH_LEN] = {0}; char **links = NULL; name = parse_device_name(uevent, 64); if (!name) return; - if (!strncmp(uevent->subsystem, "usb", 3)) { + struct ueventd_subsystem *subsystem = + ueventd_subsystem_find_by_name(uevent->subsystem); + + if (subsystem) { + const char *devname; + + switch (subsystem->devname_src) { + case DEVNAME_UEVENT_DEVNAME: + devname = uevent->device_name; + break; + + case DEVNAME_UEVENT_DEVPATH: + devname = name; + break; + + default: + ERROR("%s subsystem's devpath option is not set; ignoring event\n", + uevent->subsystem); + return; + } + + if (!assemble_devpath(devpath, subsystem->dirname, devname)) + return; + mkdir_recursive_for_devpath(devpath); + } else if (!strncmp(uevent->subsystem, "usb", 3)) { if (!strcmp(uevent->subsystem, "usb")) { if (uevent->device_name) { - /* - * create device node provided by kernel if present - * see drivers/base/core.c - */ - char *p = devpath; - int s = snprintf(devpath, sizeof(devpath), "/dev/%s", - uevent->device_name); - if (s < 0) { - ERROR("failed to assemble device path (%s); ignoring event\n", - strerror(errno)); + if (!assemble_devpath(devpath, "/dev", uevent->device_name)) return; - } else if ((size_t)s >= sizeof(devpath)) { - ERROR("/dev/%s exceeds %u-character limit on path; ignoring event\n", - uevent->device_name, sizeof(devpath)); - return; - } - - /* skip leading /dev/ */ - p += 5; - /* build directories */ - while (*p) { - if (*p == '/') { - *p = 0; - make_dir(devpath, 0755); - *p = '/'; - } - p++; - } + mkdir_recursive_for_devpath(devpath); } else { /* This imitates the file system that would be created diff --git a/init/ueventd.h b/init/ueventd.h index 9066e4780..0a454c5ab 100644 --- a/init/ueventd.h +++ b/init/ueventd.h @@ -17,6 +17,21 @@ #ifndef _INIT_UEVENTD_H_ #define _INIT_UEVENTD_H_ +#include +#include + +struct ueventd_subsystem { + struct listnode slist; + + const char *name; + enum { + DEVNAME_UNKNOWN = 0, + DEVNAME_UEVENT_DEVNAME, + DEVNAME_UEVENT_DEVPATH, + } devname_src; + const char *dirname; +}; + int ueventd_main(int argc, char **argv); #endif diff --git a/init/ueventd_keywords.h b/init/ueventd_keywords.h new file mode 100644 index 000000000..88e8f0158 --- /dev/null +++ b/init/ueventd_keywords.h @@ -0,0 +1,15 @@ +#ifndef KEYWORD +#define __MAKE_KEYWORD_ENUM__ +#define KEYWORD(symbol, flags, nargs) K_##symbol, +enum { + K_UNKNOWN, +#endif + KEYWORD(subsystem, SECTION, 1) + KEYWORD(devname, OPTION, 1) + KEYWORD(dirname, OPTION, 1) +#ifdef __MAKE_KEYWORD_ENUM__ + KEYWORD_COUNT, +}; +#undef __MAKE_KEYWORD_ENUM__ +#undef KEYWORD +#endif diff --git a/init/ueventd_parser.c b/init/ueventd_parser.c index 3e60df5b8..e447006e5 100644 --- a/init/ueventd_parser.c +++ b/init/ueventd_parser.c @@ -14,18 +14,189 @@ * limitations under the License. */ +#include +#include #include #include #include +#include #include +#include "ueventd.h" #include "ueventd_parser.h" #include "parser.h" #include "log.h" #include "util.h" +static list_declare(subsystem_list); + static void parse_line_device(struct parse_state *state, int nargs, char **args); +#define SECTION 0x01 +#define OPTION 0x02 + +#include "ueventd_keywords.h" + +#define KEYWORD(symbol, flags, nargs) \ + [ K_##symbol ] = { #symbol, nargs + 1, flags, }, + +static struct { + const char *name; + unsigned char nargs; + unsigned char flags; +} keyword_info[KEYWORD_COUNT] = { + [ K_UNKNOWN ] = { "unknown", 0, 0 }, +#include "ueventd_keywords.h" +}; +#undef KEYWORD + +#define kw_is(kw, type) (keyword_info[kw].flags & (type)) +#define kw_nargs(kw) (keyword_info[kw].nargs) + +static int lookup_keyword(const char *s) +{ + switch (*s++) { + case 'd': + if (!strcmp(s, "evname")) return K_devname; + if (!strcmp(s, "irname")) return K_dirname; + break; + case 's': + if (!strcmp(s, "ubsystem")) return K_subsystem; + break; + } + return K_UNKNOWN; +} + +static void parse_line_no_op(struct parse_state *state __attribute__((unused)), + int nargs __attribute__((unused)), char **args __attribute__((unused))) +{ +} + +static int valid_name(const char *name) +{ + while (*name) { + if (!isalnum(*name) && (*name != '_') && (*name != '-')) { + return 0; + } + name++; + } + return 1; +} + +struct ueventd_subsystem *ueventd_subsystem_find_by_name(const char *name) +{ + struct listnode *node; + struct ueventd_subsystem *s; + + list_for_each(node, &subsystem_list) { + s = node_to_item(node, struct ueventd_subsystem, slist); + if (!strcmp(s->name, name)) { + return s; + } + } + return 0; +} + +static void *parse_subsystem(struct parse_state *state, + int nargs __attribute__((unused)), char **args) +{ + struct ueventd_subsystem *s; + + if (!valid_name(args[1])) { + parse_error(state, "invalid subsystem name '%s'\n", args[1]); + return 0; + } + + s = ueventd_subsystem_find_by_name(args[1]); + if (s) { + parse_error(state, "ignored duplicate definition of subsystem '%s'\n", + args[1]); + return 0; + } + + s = calloc(1, sizeof(*s)); + if (!s) { + parse_error(state, "out of memory\n"); + return 0; + } + s->name = args[1]; + s->dirname = "/dev"; + list_add_tail(&subsystem_list, &s->slist); + return s; +} + +static void parse_line_subsystem(struct parse_state *state, int nargs, + char **args) +{ + struct ueventd_subsystem *s = state->context; + int kw; + + if (nargs == 0) { + return; + } + + kw = lookup_keyword(args[0]); + switch (kw) { + case K_devname: + if (!strcmp(args[1], "uevent_devname")) + s->devname_src = DEVNAME_UEVENT_DEVNAME; + else if (!strcmp(args[1], "uevent_devpath")) + s->devname_src = DEVNAME_UEVENT_DEVPATH; + else + parse_error(state, "invalid devname '%s'\n", args[1]); + break; + + case K_dirname: + if (args[1][0] == '/') + s->dirname = args[1]; + else + parse_error(state, "dirname '%s' does not start with '/'\n", + args[1]); + break; + + default: + parse_error(state, "invalid option '%s'\n", args[0]); + } +} + +static void parse_new_section(struct parse_state *state, int kw, + int nargs, char **args) +{ + printf("[ %s %s ]\n", args[0], + nargs > 1 ? args[1] : ""); + + switch(kw) { + case K_subsystem: + state->context = parse_subsystem(state, nargs, args); + if (state->context) { + state->parse_line = parse_line_subsystem; + return; + } + break; + } + state->parse_line = parse_line_no_op; +} + +static void parse_line(struct parse_state *state, char **args, int nargs) +{ + int kw = lookup_keyword(args[0]); + int kw_nargs = kw_nargs(kw); + + if (nargs < kw_nargs) { + parse_error(state, "%s requires %d %s\n", args[0], kw_nargs - 1, + kw_nargs > 2 ? "arguments" : "argument"); + return; + } + + if (kw_is(kw, SECTION)) { + parse_new_section(state, kw, nargs, args); + } else if (kw_is(kw, OPTION)) { + state->parse_line(state, nargs, args); + } else { + parse_line_device(state, nargs, args); + } +} + static void parse_config(const char *fn, char *s) { struct parse_state state; @@ -36,18 +207,19 @@ static void parse_config(const char *fn, char *s) state.line = 1; state.ptr = s; state.nexttoken = 0; - state.parse_line = parse_line_device; + state.parse_line = parse_line_no_op; for (;;) { int token = next_token(&state); switch (token) { case T_EOF: - state.parse_line(&state, 0, 0); + parse_line(&state, args, nargs); return; case T_NEWLINE: if (nargs) { - state.parse_line(&state, nargs, args); + parse_line(&state, args, nargs); nargs = 0; } + state.line++; break; case T_TEXT: if (nargs < UEVENTD_PARSER_MAXARGS) { @@ -69,7 +241,8 @@ int ueventd_parse_config_file(const char *fn) return 0; } -static void parse_line_device(struct parse_state* state, int nargs, char **args) +static void parse_line_device(struct parse_state *state __attribute__((unused)), + int nargs, char **args) { set_device_permission(nargs, args); } diff --git a/init/ueventd_parser.h b/init/ueventd_parser.h index 3684285aa..907cc49de 100644 --- a/init/ueventd_parser.h +++ b/init/ueventd_parser.h @@ -17,9 +17,12 @@ #ifndef _INIT_UEVENTD_PARSER_H_ #define _INIT_UEVENTD_PARSER_H_ +#include "ueventd.h" + #define UEVENTD_PARSER_MAXARGS 5 int ueventd_parse_config_file(const char *fn); void set_device_permission(int nargs, char **args); +struct ueventd_subsystem *ueventd_subsystem_find_by_name(const char *name); #endif