diff options
-rw-r--r-- | init/Android.mk | 1 | ||||
-rw-r--r-- | init/action.cpp | 7 | ||||
-rw-r--r-- | init/devices.cpp | 169 | ||||
-rw-r--r-- | init/devices.h | 42 | ||||
-rw-r--r-- | init/keyword_map.h | 11 | ||||
-rw-r--r-- | init/service.cpp | 7 | ||||
-rw-r--r-- | init/ueventd.cpp | 75 | ||||
-rw-r--r-- | init/ueventd.h | 20 | ||||
-rw-r--r-- | init/ueventd_keywords.h | 15 | ||||
-rw-r--r-- | init/ueventd_parser.cpp | 241 | ||||
-rw-r--r-- | init/ueventd_parser.h | 28 |
11 files changed, 205 insertions, 411 deletions
diff --git a/init/Android.mk b/init/Android.mk index 0c0a60580..069f2a44a 100644 --- a/init/Android.mk +++ b/init/Android.mk @@ -92,7 +92,6 @@ LOCAL_SRC_FILES:= \ reboot.cpp \ signal_handler.cpp \ ueventd.cpp \ - ueventd_parser.cpp \ watchdogd.cpp \ LOCAL_MODULE:= init diff --git a/init/action.cpp b/init/action.cpp index 8d49e2ad4..dd03017ab 100644 --- a/init/action.cpp +++ b/init/action.cpp @@ -58,12 +58,7 @@ bool Action::AddCommand(const std::vector<std::string>& args, int line, std::str return false; } - if (args.empty()) { - *err = "command needed, but not provided"; - return false; - } - - auto function = function_map_->FindFunction(args[0], args.size() - 1, err); + auto function = function_map_->FindFunction(args, err); if (!function) { return false; } diff --git a/init/devices.cpp b/init/devices.cpp index 6e13863be..07d28d0bc 100644 --- a/init/devices.cpp +++ b/init/devices.cpp @@ -20,8 +20,10 @@ #include <errno.h> #include <fcntl.h> #include <fnmatch.h> +#include <grp.h> #include <libgen.h> #include <linux/netlink.h> +#include <pwd.h> #include <stddef.h> #include <stdio.h> #include <stdlib.h> @@ -49,7 +51,8 @@ #include <selinux/label.h> #include <selinux/selinux.h> -#include "ueventd_parser.h" +#include "keyword_map.h" +#include "ueventd.h" #include "util.h" extern struct selabel_handle *sehandle; @@ -103,6 +106,137 @@ void SysfsPermissions::SetPermissions(const std::string& path) const { std::vector<Permissions> dev_permissions; std::vector<SysfsPermissions> sysfs_permissions; +bool ParsePermissionsLine(std::vector<std::string>&& args, std::string* err, bool is_sysfs) { + if (is_sysfs && args.size() != 5) { + *err = "/sys/ lines must have 5 entries"; + return false; + } + + if (!is_sysfs && args.size() != 4) { + *err = "/dev/ lines must have 4 entries"; + return false; + } + + auto it = args.begin(); + const std::string& name = *it++; + + std::string sysfs_attribute; + if (is_sysfs) sysfs_attribute = *it++; + + // args is now common to both sys and dev entries and contains: <perm> <uid> <gid> + std::string& perm_string = *it++; + char* end_pointer = 0; + mode_t perm = strtol(perm_string.c_str(), &end_pointer, 8); + if (end_pointer == nullptr || *end_pointer != '\0') { + *err = "invalid mode '" + perm_string + "'"; + return false; + } + + std::string& uid_string = *it++; + passwd* pwd = getpwnam(uid_string.c_str()); + if (!pwd) { + *err = "invalid uid '" + uid_string + "'"; + return false; + } + uid_t uid = pwd->pw_uid; + + std::string& gid_string = *it++; + struct group* grp = getgrnam(gid_string.c_str()); + if (!grp) { + *err = "invalid gid '" + gid_string + "'"; + return false; + } + gid_t gid = grp->gr_gid; + + if (is_sysfs) { + sysfs_permissions.emplace_back(name, sysfs_attribute, perm, uid, gid); + } else { + dev_permissions.emplace_back(name, perm, uid, gid); + } + return true; +} + +// TODO: Move this to be a member variable of a future devices class. +static std::vector<Subsystem> subsystems; + +std::string Subsystem::ParseDevPath(uevent* uevent) const { + std::string devname = devname_source_ == DevnameSource::DEVNAME_UEVENT_DEVNAME + ? uevent->device_name + : android::base::Basename(uevent->path); + + return dir_name_ + "/" + devname; +} + +bool SubsystemParser::ParseSection(std::vector<std::string>&& args, const std::string& filename, + int line, std::string* err) { + if (args.size() != 2) { + *err = "subsystems must have exactly one name"; + return false; + } + + if (std::find(subsystems.begin(), subsystems.end(), args[1]) != subsystems.end()) { + *err = "ignoring duplicate subsystem entry"; + return false; + } + + subsystem_.name_ = args[1]; + + return true; +} + +bool SubsystemParser::ParseDevName(std::vector<std::string>&& args, std::string* err) { + if (args[1] == "uevent_devname") { + subsystem_.devname_source_ = Subsystem::DevnameSource::DEVNAME_UEVENT_DEVNAME; + return true; + } + if (args[1] == "uevent_devpath") { + subsystem_.devname_source_ = Subsystem::DevnameSource::DEVNAME_UEVENT_DEVPATH; + return true; + } + + *err = "invalid devname '" + args[1] + "'"; + return false; +} + +bool SubsystemParser::ParseDirName(std::vector<std::string>&& args, std::string* err) { + if (args[1].front() != '/') { + *err = "dirname '" + args[1] + " ' does not start with '/'"; + return false; + } + + subsystem_.dir_name_ = args[1]; + return true; +} + +bool SubsystemParser::ParseLineSection(std::vector<std::string>&& args, int line, std::string* err) { + using OptionParser = + bool (SubsystemParser::*)(std::vector<std::string> && args, std::string * err); + static class OptionParserMap : public KeywordMap<OptionParser> { + private: + const Map& map() const override { + // clang-format off + static const Map option_parsers = { + {"devname", {1, 1, &SubsystemParser::ParseDevName}}, + {"dirname", {1, 1, &SubsystemParser::ParseDirName}}, + }; + // clang-format on + return option_parsers; + } + } parser_map; + + auto parser = parser_map.FindFunction(args, err); + + if (!parser) { + return false; + } + + return (this->*parser)(std::move(args), err); +} + +void SubsystemParser::EndSection() { + subsystems.emplace_back(std::move(subsystem_)); +} + static void fixup_sys_permissions(const std::string& upath, const std::string& subsystem) { // upaths omit the "/sys" that paths in this list // contain, so we prepend it... @@ -483,32 +617,9 @@ static void handle_generic_device_event(uevent* uevent) { // if it's not a /dev device, nothing to do if (uevent->major < 0 || uevent->minor < 0) return; - std::string name = android::base::Basename(uevent->path); - ueventd_subsystem* subsystem = ueventd_subsystem_find_by_name(uevent->subsystem.c_str()); - std::string devpath; - if (subsystem) { - std::string devname; - - switch (subsystem->devname_src) { - case DEVNAME_UEVENT_DEVNAME: - devname = uevent->device_name; - break; - - case DEVNAME_UEVENT_DEVPATH: - devname = name; - break; - - default: - LOG(ERROR) << uevent->subsystem << " subsystem's devpath option is not set; ignoring event"; - return; - } - - // TODO: Remove std::string() - devpath = std::string(subsystem->dirname) + "/" + devname; - mkdir_recursive(android::base::Dirname(devpath), 0755); - } else if (android::base::StartsWith(uevent->subsystem, "usb")) { + if (android::base::StartsWith(uevent->subsystem, "usb")) { if (uevent->subsystem == "usb") { if (!uevent->device_name.empty()) { devpath = "/dev/" + uevent->device_name; @@ -520,15 +631,19 @@ static void handle_generic_device_event(uevent* uevent) { int device_id = uevent->minor % 128 + 1; devpath = android::base::StringPrintf("/dev/bus/usb/%03d/%03d", bus_id, device_id); } - mkdir_recursive(android::base::Dirname(devpath), 0755); } else { // ignore other USB events return; } + } else if (auto subsystem = std::find(subsystems.begin(), subsystems.end(), uevent->subsystem); + subsystem != subsystems.end()) { + devpath = subsystem->ParseDevPath(uevent); } else { - devpath = "/dev/" + name; + devpath = "/dev/" + android::base::Basename(uevent->path); } + mkdir_recursive(android::base::Dirname(devpath), 0755); + auto links = get_character_device_symlinks(uevent); handle_device(uevent->action, devpath, 0, uevent->major, uevent->minor, links); diff --git a/init/devices.h b/init/devices.h index 2cbae66a6..647b4c4bc 100644 --- a/init/devices.h +++ b/init/devices.h @@ -24,6 +24,8 @@ #include <string> #include <vector> +#include "init_parser.h" + enum coldboot_action_t { // coldboot continues without creating the device for the uevent COLDBOOT_CONTINUE = 0, @@ -83,9 +85,45 @@ class SysfsPermissions : public Permissions { const std::string attribute_; }; -extern std::vector<Permissions> dev_permissions; -extern std::vector<SysfsPermissions> sysfs_permissions; +class Subsystem { + public: + friend class SubsystemParser; + + Subsystem() {} + + // Returns the full path for a uevent of a device that is a member of this subsystem, + // according to the rules parsed from ueventd.rc + std::string ParseDevPath(uevent* uevent) const; + + bool operator==(const std::string& string_name) { return name_ == string_name; } + + private: + enum class DevnameSource { + DEVNAME_UEVENT_DEVNAME, + DEVNAME_UEVENT_DEVPATH, + }; + + std::string name_; + std::string dir_name_ = "/dev"; + DevnameSource devname_source_; +}; + +class SubsystemParser : public SectionParser { + public: + SubsystemParser() {} + bool ParseSection(std::vector<std::string>&& args, const std::string& filename, int line, + std::string* err) override; + bool ParseLineSection(std::vector<std::string>&& args, int line, std::string* err) override; + void EndSection() override; + + private: + bool ParseDevName(std::vector<std::string>&& args, std::string* err); + bool ParseDirName(std::vector<std::string>&& args, std::string* err); + + Subsystem subsystem_; +}; +bool ParsePermissionsLine(std::vector<std::string>&& args, std::string* err, bool is_sysfs); typedef std::function<coldboot_action_t(struct uevent* uevent)> coldboot_callback; extern coldboot_action_t handle_device_fd(coldboot_callback fn = nullptr); extern void device_init(const char* path = nullptr, coldboot_callback fn = nullptr); diff --git a/init/keyword_map.h b/init/keyword_map.h index 2b91260ad..88bad014a 100644 --- a/init/keyword_map.h +++ b/init/keyword_map.h @@ -31,11 +31,16 @@ class KeywordMap { virtual ~KeywordMap() { } - const Function FindFunction(const std::string& keyword, - size_t num_args, - std::string* err) const { + const Function FindFunction(const std::vector<std::string>& args, std::string* err) const { using android::base::StringPrintf; + if (args.empty()) { + *err = "keyword needed, but not provided"; + return nullptr; + } + auto& keyword = args[0]; + auto num_args = args.size() - 1; + auto function_info_it = map().find(keyword); if (function_info_it == map().end()) { *err = StringPrintf("invalid keyword '%s'", keyword.c_str()); diff --git a/init/service.cpp b/init/service.cpp index ab404a165..961926a68 100644 --- a/init/service.cpp +++ b/init/service.cpp @@ -572,13 +572,8 @@ const Service::OptionParserMap::Map& Service::OptionParserMap::map() const { } bool Service::ParseLine(const std::vector<std::string>& args, std::string* err) { - if (args.empty()) { - *err = "option needed, but not provided"; - return false; - } - static const OptionParserMap parser_map; - auto parser = parser_map.FindFunction(args[0], args.size() - 1, err); + auto parser = parser_map.FindFunction(args, err); if (!parser) { return false; diff --git a/init/ueventd.cpp b/init/ueventd.cpp index b6c6a01b1..963cc4df9 100644 --- a/init/ueventd.cpp +++ b/init/ueventd.cpp @@ -18,9 +18,7 @@ #include <ctype.h> #include <fcntl.h> -#include <grp.h> #include <poll.h> -#include <pwd.h> #include <signal.h> #include <stdio.h> #include <stdlib.h> @@ -33,9 +31,11 @@ #include "devices.h" #include "log.h" -#include "ueventd_parser.h" #include "util.h" +template <bool sysfs> +static bool ParseSingleLine(std::vector<std::string>&& line, std::string* err); + int ueventd_main(int argc, char **argv) { /* @@ -60,9 +60,14 @@ int ueventd_main(int argc, char **argv) cb.func_log = selinux_klog_callback; selinux_set_callback(SELINUX_CB_LOG, cb); - ueventd_parse_config_file("/ueventd.rc"); - ueventd_parse_config_file("/vendor/ueventd.rc"); - ueventd_parse_config_file("/odm/ueventd.rc"); + Parser& parser = Parser::GetInstance(); + parser.AddSectionParser("subsystem", std::make_unique<SubsystemParser>()); + using namespace std::placeholders; + parser.AddSingleLineParser("/sys/", std::bind(ParsePermissionsLine, _1, _2, true)); + parser.AddSingleLineParser("/dev/", std::bind(ParsePermissionsLine, _1, _2, false)); + parser.ParseConfig("/ueventd.rc"); + parser.ParseConfig("/vendor/ueventd.rc"); + parser.ParseConfig("/odm/ueventd.rc"); /* * keep the current product name base configuration so @@ -72,7 +77,7 @@ int ueventd_main(int argc, char **argv) * device node entries (b/34968103) */ std::string hardware = android::base::GetProperty("ro.hardware", ""); - ueventd_parse_config_file(android::base::StringPrintf("/ueventd.%s.rc", hardware.c_str()).c_str()); + parser.ParseConfig("/ueventd." + hardware + ".rc"); device_init(); @@ -93,59 +98,3 @@ int ueventd_main(int argc, char **argv) return 0; } - -void set_device_permission(const char* fn, int line, int nargs, char **args) -{ - char *name; - char *attr = 0; - mode_t perm; - uid_t uid; - gid_t gid; - char *endptr; - - if (nargs == 0) - return; - - if (args[0][0] == '#') - return; - - name = args[0]; - - if (!strncmp(name,"/sys/", 5) && (nargs == 5)) { - LOG(INFO) << "/sys/ rule " << args[0] << " " << args[1]; - attr = args[1]; - args++; - nargs--; - } - - if (nargs != 4) { - LOG(ERROR) << "invalid line (" << fn << ":" << line << ") line for '" << args[0] << "'"; - return; - } - - perm = strtol(args[1], &endptr, 8); - if (!endptr || *endptr != '\0') { - LOG(ERROR) << "invalid mode (" << fn << ":" << line << ") '" << args[1] << "'"; - return; - } - - struct passwd* pwd = getpwnam(args[2]); - if (!pwd) { - LOG(ERROR) << "invalid uid (" << fn << ":" << line << ") '" << args[2] << "'"; - return; - } - uid = pwd->pw_uid; - - struct group* grp = getgrnam(args[3]); - if (!grp) { - LOG(ERROR) << "invalid gid (" << fn << ":" << line << ") '" << args[3] << "'"; - return; - } - gid = grp->gr_gid; - - if (attr) { - sysfs_permissions.emplace_back(name, attr, perm, uid, gid); - } else { - dev_permissions.emplace_back(name, perm, uid, gid); - } -} diff --git a/init/ueventd.h b/init/ueventd.h index d44d1ca55..1f424d3b6 100644 --- a/init/ueventd.h +++ b/init/ueventd.h @@ -17,24 +17,6 @@ #ifndef _INIT_UEVENTD_H_ #define _INIT_UEVENTD_H_ -#include <sys/types.h> - -#include <cutils/list.h> - -enum devname_src_t { - DEVNAME_UNKNOWN = 0, - DEVNAME_UEVENT_DEVNAME, - DEVNAME_UEVENT_DEVPATH, -}; - -struct ueventd_subsystem { - struct listnode slist; - - const char *name; - const char *dirname; - devname_src_t devname_src; -}; - -int ueventd_main(int argc, char **argv); +int ueventd_main(int argc, char** argv); #endif diff --git a/init/ueventd_keywords.h b/init/ueventd_keywords.h deleted file mode 100644 index 88e8f0158..000000000 --- a/init/ueventd_keywords.h +++ /dev/null @@ -1,15 +0,0 @@ -#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.cpp b/init/ueventd_parser.cpp deleted file mode 100644 index 510d7bbd6..000000000 --- a/init/ueventd_parser.cpp +++ /dev/null @@ -1,241 +0,0 @@ -/* - * Copyright (C) 2010 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "ueventd_parser.h" - -#include <ctype.h> -#include <stdarg.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <unistd.h> - -#include <android-base/logging.h> - -#include "parser.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*, int, char**) { -} - -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(parse_state* state, int /*nargs*/, char** args) { - if (!valid_name(args[1])) { - parse_error(state, "invalid subsystem name '%s'\n", args[1]); - return 0; - } - - ueventd_subsystem* 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 = (ueventd_subsystem*) 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 = (ueventd_subsystem*) 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, const std::string& data) -{ - char *args[UEVENTD_PARSER_MAXARGS]; - - int nargs = 0; - parse_state state; - state.filename = fn; - state.line = 1; - state.ptr = strdup(data.c_str()); // TODO: fix this code! - state.nexttoken = 0; - state.parse_line = parse_line_no_op; - for (;;) { - int token = next_token(&state); - switch (token) { - case T_EOF: - parse_line(&state, args, nargs); - return; - case T_NEWLINE: - if (nargs) { - parse_line(&state, args, nargs); - nargs = 0; - } - state.line++; - break; - case T_TEXT: - if (nargs < UEVENTD_PARSER_MAXARGS) { - args[nargs++] = state.text; - } - break; - } - } -} - -int ueventd_parse_config_file(const char *fn) -{ - std::string data; - if (!read_file(fn, &data)) { - return -1; - } - - data.push_back('\n'); // TODO: fix parse_config. - parse_config(fn, data); - return 0; -} - -static void parse_line_device(parse_state* state, int nargs, char** args) { - set_device_permission(state->filename, state->line, nargs, args); -} diff --git a/init/ueventd_parser.h b/init/ueventd_parser.h deleted file mode 100644 index 4d6989730..000000000 --- a/init/ueventd_parser.h +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright (C) 2010 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#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(const char* fn, int line, int nargs, char **args); -struct ueventd_subsystem *ueventd_subsystem_find_by_name(const char *name); - -#endif |