diff --git a/init/devices.c b/init/devices.c index af88c5f09..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" @@ -530,8 +531,11 @@ static const char *parse_device_name(struct uevent *uevent, unsigned int len) name++; /* too-long names would overrun our buffer */ - if(strlen(name) > len) + if(strlen(name) > len) { + ERROR("DEVPATH=%s exceeds %u-character limit on filename; ignoring event\n", + name, len); return NULL; + } return name; } @@ -557,37 +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; - snprintf(devpath, sizeof(devpath), "/dev/%s", uevent->device_name); - /* skip leading /dev/ */ - p += 5; - /* build directories */ - while (*p) { - if (*p == '/') { - *p = 0; - make_dir(devpath, 0755); - *p = '/'; - } - p++; - } + if (!assemble_devpath(devpath, "/dev", uevent->device_name)) + return; + 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 diff --git a/rootdir/ueventd.rc b/rootdir/ueventd.rc index a60cfc5cf..ddf8753b8 100644 --- a/rootdir/ueventd.rc +++ b/rootdir/ueventd.rc @@ -1,3 +1,6 @@ +subsystem adf + devname uevent_devname + /dev/null 0666 root root /dev/zero 0666 root root /dev/full 0666 root root