summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGreg Hackmann <ghackmann@google.com>2013-11-18 15:24:40 -0800
committerGreg Hackmann <ghackmann@google.com>2013-11-26 13:18:23 -0800
commit3312aa8379d877044def52f3b3be5c912a5e61a2 (patch)
tree5a6ddb2bcffa458e69c4d789b6683207e0318354
parentf6e009ee2650d8812942aa7f5761e86402346739 (diff)
downloadcore-3312aa8379d877044def52f3b3be5c912a5e61a2.tar.gz
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 <s> devname (uevent_devname|uevent_devpath) [dirname <dir>] Devices matching SUBSYSTEM=<s> will be populated under <dir>. dirname is optional and defaults to /dev. If dirname is provided, <dir> must start with "/". If devname is uevent_devname, ueventd will create the device node as <dir>/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 <dir>/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 <ghackmann@google.com>
-rw-r--r--init/devices.c87
-rw-r--r--init/ueventd.h15
-rw-r--r--init/ueventd_keywords.h15
-rw-r--r--init/ueventd_parser.c181
-rw-r--r--init/ueventd_parser.h3
5 files changed, 268 insertions, 33 deletions
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 <cutils/uevent.h>
#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));
- 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));
+ if (!assemble_devpath(devpath, "/dev", uevent->device_name))
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 <cutils/list.h>
+#include <sys/types.h>
+
+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 <ctype.h>
+#include <errno.h>
#include <stdio.h>
#include <unistd.h>
#include <stdarg.h>
+#include <stdlib.h>
#include <string.h>
+#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