Merge "ueventd: Break devices.cpp into discrete classes"
am: c495e059b7
Change-Id: I0ada340d4b422750fae7ec362834f48bc729789a
This commit is contained in:
commit
18d0144e6c
15 changed files with 1157 additions and 925 deletions
|
@ -66,11 +66,14 @@ cc_library_static {
|
|||
"capabilities.cpp",
|
||||
"descriptors.cpp",
|
||||
"devices.cpp",
|
||||
"firmware_handler.cpp",
|
||||
"import_parser.cpp",
|
||||
"init_parser.cpp",
|
||||
"log.cpp",
|
||||
"parser.cpp",
|
||||
"service.cpp",
|
||||
"uevent_listener.cpp",
|
||||
"ueventd_parser.cpp",
|
||||
"util.cpp",
|
||||
],
|
||||
whole_static_libs: ["libcap"],
|
||||
|
|
920
init/devices.cpp
920
init/devices.cpp
File diff suppressed because it is too large
Load diff
105
init/devices.h
105
init/devices.h
|
@ -20,35 +20,14 @@
|
|||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <functional>
|
||||
#include <algorithm>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "init_parser.h"
|
||||
#include <android-base/file.h>
|
||||
#include <selinux/label.h>
|
||||
|
||||
enum coldboot_action_t {
|
||||
// coldboot continues without creating the device for the uevent
|
||||
COLDBOOT_CONTINUE = 0,
|
||||
// coldboot continues after creating the device for the uevent
|
||||
COLDBOOT_CREATE,
|
||||
// coldboot stops after creating the device for uevent but doesn't
|
||||
// create the COLDBOOT_DONE file
|
||||
COLDBOOT_STOP,
|
||||
// same as COLDBOOT_STOP, but creates the COLDBOOT_DONE file
|
||||
COLDBOOT_FINISH
|
||||
};
|
||||
|
||||
struct uevent {
|
||||
std::string action;
|
||||
std::string path;
|
||||
std::string subsystem;
|
||||
std::string firmware;
|
||||
std::string partition_name;
|
||||
std::string device_name;
|
||||
int partition_num;
|
||||
int major;
|
||||
int minor;
|
||||
};
|
||||
#include "uevent.h"
|
||||
|
||||
class Permissions {
|
||||
public:
|
||||
|
@ -93,9 +72,15 @@ class 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;
|
||||
std::string ParseDevPath(const Uevent& uevent) const {
|
||||
std::string devname = devname_source_ == DevnameSource::DEVNAME_UEVENT_DEVNAME
|
||||
? uevent.device_name
|
||||
: android::base::Basename(uevent.path);
|
||||
|
||||
bool operator==(const std::string& string_name) { return name_ == string_name; }
|
||||
return dir_name_ + "/" + devname;
|
||||
}
|
||||
|
||||
bool operator==(const std::string& string_name) const { return name_ == string_name; }
|
||||
|
||||
private:
|
||||
enum class DevnameSource {
|
||||
|
@ -108,34 +93,54 @@ class Subsystem {
|
|||
DevnameSource devname_source_;
|
||||
};
|
||||
|
||||
class SubsystemParser : public SectionParser {
|
||||
class PlatformDeviceList {
|
||||
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;
|
||||
void Add(const std::string& path) { platform_devices_.emplace_back(path); }
|
||||
void Remove(const std::string& path) {
|
||||
auto it = std::find(platform_devices_.begin(), platform_devices_.end(), path);
|
||||
if (it != platform_devices_.end()) platform_devices_.erase(it);
|
||||
}
|
||||
bool Find(const std::string& path, std::string* out_path) const;
|
||||
auto size() const { return platform_devices_.size(); }
|
||||
|
||||
private:
|
||||
bool ParseDevName(std::vector<std::string>&& args, std::string* err);
|
||||
bool ParseDirName(std::vector<std::string>&& args, std::string* err);
|
||||
|
||||
Subsystem subsystem_;
|
||||
std::vector<std::string> platform_devices_;
|
||||
};
|
||||
|
||||
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);
|
||||
extern void device_close();
|
||||
int get_device_fd();
|
||||
class DeviceHandler {
|
||||
public:
|
||||
friend class DeviceHandlerTester;
|
||||
|
||||
DeviceHandler();
|
||||
DeviceHandler(std::vector<Permissions> dev_permissions,
|
||||
std::vector<SysfsPermissions> sysfs_permissions,
|
||||
std::vector<Subsystem> subsystems);
|
||||
~DeviceHandler(){};
|
||||
|
||||
void HandleDeviceEvent(const Uevent& uevent);
|
||||
std::vector<std::string> GetBlockDeviceSymlinks(const Uevent& uevent) const;
|
||||
|
||||
private:
|
||||
void FixupSysPermissions(const std::string& upath, const std::string& subsystem) const;
|
||||
std::tuple<mode_t, uid_t, gid_t> GetDevicePermissions(
|
||||
const std::string& path, const std::vector<std::string>& links) const;
|
||||
void MakeDevice(const std::string& path, int block, int major, int minor,
|
||||
const std::vector<std::string>& links) const;
|
||||
std::vector<std::string> GetCharacterDeviceSymlinks(const Uevent& uevent) const;
|
||||
void HandleDevice(const std::string& action, const std::string& devpath, int block, int major,
|
||||
int minor, const std::vector<std::string>& links) const;
|
||||
void HandlePlatformDeviceEvent(const Uevent& uevent);
|
||||
void HandleBlockDeviceEvent(const Uevent& uevent) const;
|
||||
void HandleGenericDeviceEvent(const Uevent& uevent) const;
|
||||
|
||||
std::vector<Permissions> dev_permissions_;
|
||||
std::vector<SysfsPermissions> sysfs_permissions_;
|
||||
std::vector<Subsystem> subsystems_;
|
||||
PlatformDeviceList platform_devices_;
|
||||
selabel_handle* sehandle_;
|
||||
};
|
||||
|
||||
// Exposed for testing
|
||||
extern std::vector<std::string> platform_devices;
|
||||
bool find_platform_device(const std::string& path, std::string* out_path);
|
||||
std::vector<std::string> get_character_device_symlinks(uevent* uevent);
|
||||
std::vector<std::string> get_block_device_symlinks(uevent* uevent);
|
||||
void sanitize_partition_name(std::string* string);
|
||||
void handle_platform_device_event(uevent* uevent);
|
||||
void SanitizePartitionName(std::string* string);
|
||||
|
||||
#endif /* _INIT_DEVICES_H */
|
||||
#endif
|
||||
|
|
|
@ -22,167 +22,180 @@
|
|||
#include <android-base/scopeguard.h>
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
void add_platform_device(const std::string& path) {
|
||||
uevent uevent = {
|
||||
.action = "add", .subsystem = "platform", .path = path,
|
||||
};
|
||||
handle_platform_device_event(&uevent);
|
||||
}
|
||||
|
||||
void remove_platform_device(const std::string& path) {
|
||||
uevent uevent = {
|
||||
.action = "remove", .subsystem = "platform", .path = path,
|
||||
};
|
||||
handle_platform_device_event(&uevent);
|
||||
}
|
||||
|
||||
template <std::vector<std::string> (*Function)(uevent*)>
|
||||
void test_get_symlinks(const std::string& platform_device_name, uevent* uevent,
|
||||
const std::vector<std::string> expected_links) {
|
||||
add_platform_device(platform_device_name);
|
||||
auto platform_device_remover = android::base::make_scope_guard(
|
||||
[&platform_device_name]() { remove_platform_device(platform_device_name); });
|
||||
|
||||
std::vector<std::string> result = Function(uevent);
|
||||
|
||||
auto expected_size = expected_links.size();
|
||||
ASSERT_EQ(expected_size, result.size());
|
||||
if (expected_size == 0) return;
|
||||
|
||||
// Explicitly iterate so the results are visible if a failure occurs
|
||||
for (unsigned int i = 0; i < expected_size; ++i) {
|
||||
EXPECT_EQ(expected_links[i], result[i]);
|
||||
class DeviceHandlerTester {
|
||||
public:
|
||||
void AddPlatformDevice(const std::string& path) {
|
||||
Uevent uevent = {
|
||||
.action = "add", .subsystem = "platform", .path = path,
|
||||
};
|
||||
device_handler_.HandlePlatformDeviceEvent(uevent);
|
||||
}
|
||||
}
|
||||
|
||||
TEST(devices, handle_platform_device_event) {
|
||||
platform_devices.clear();
|
||||
add_platform_device("/devices/platform/some_device_name");
|
||||
ASSERT_EQ(1U, platform_devices.size());
|
||||
remove_platform_device("/devices/platform/some_device_name");
|
||||
ASSERT_EQ(0U, platform_devices.size());
|
||||
}
|
||||
void RemovePlatformDevice(const std::string& path) {
|
||||
Uevent uevent = {
|
||||
.action = "remove", .subsystem = "platform", .path = path,
|
||||
};
|
||||
device_handler_.HandlePlatformDeviceEvent(uevent);
|
||||
}
|
||||
|
||||
TEST(devices, find_platform_device) {
|
||||
platform_devices.clear();
|
||||
add_platform_device("/devices/platform/some_device_name");
|
||||
add_platform_device("/devices/platform/some_device_name/longer");
|
||||
add_platform_device("/devices/platform/other_device_name");
|
||||
EXPECT_EQ(3U, platform_devices.size());
|
||||
void TestGetSymlinks(const std::string& platform_device_name, const Uevent& uevent,
|
||||
const std::vector<std::string> expected_links, bool block) {
|
||||
AddPlatformDevice(platform_device_name);
|
||||
auto platform_device_remover = android::base::make_scope_guard(
|
||||
[this, &platform_device_name]() { RemovePlatformDevice(platform_device_name); });
|
||||
|
||||
std::vector<std::string> result;
|
||||
if (block) {
|
||||
result = device_handler_.GetBlockDeviceSymlinks(uevent);
|
||||
} else {
|
||||
result = device_handler_.GetCharacterDeviceSymlinks(uevent);
|
||||
}
|
||||
|
||||
auto expected_size = expected_links.size();
|
||||
ASSERT_EQ(expected_size, result.size());
|
||||
if (expected_size == 0) return;
|
||||
|
||||
// Explicitly iterate so the results are visible if a failure occurs
|
||||
for (unsigned int i = 0; i < expected_size; ++i) {
|
||||
EXPECT_EQ(expected_links[i], result[i]);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
DeviceHandler device_handler_;
|
||||
};
|
||||
|
||||
TEST(device_handler, PlatformDeviceList) {
|
||||
PlatformDeviceList platform_device_list;
|
||||
|
||||
platform_device_list.Add("/devices/platform/some_device_name");
|
||||
platform_device_list.Add("/devices/platform/some_device_name/longer");
|
||||
platform_device_list.Add("/devices/platform/other_device_name");
|
||||
EXPECT_EQ(3U, platform_device_list.size());
|
||||
|
||||
std::string out_path;
|
||||
EXPECT_FALSE(find_platform_device("/devices/platform/not_found", &out_path));
|
||||
EXPECT_FALSE(platform_device_list.Find("/devices/platform/not_found", &out_path));
|
||||
EXPECT_EQ("", out_path);
|
||||
|
||||
EXPECT_FALSE(
|
||||
find_platform_device("/devices/platform/some_device_name_with_same_prefix", &out_path));
|
||||
EXPECT_FALSE(platform_device_list.Find("/devices/platform/some_device_name_with_same_prefix",
|
||||
&out_path));
|
||||
|
||||
EXPECT_TRUE(
|
||||
find_platform_device("/devices/platform/some_device_name/longer/longer_child", &out_path));
|
||||
EXPECT_TRUE(platform_device_list.Find("/devices/platform/some_device_name/longer/longer_child",
|
||||
&out_path));
|
||||
EXPECT_EQ("/devices/platform/some_device_name/longer", out_path);
|
||||
|
||||
EXPECT_TRUE(find_platform_device("/devices/platform/some_device_name/other_child", &out_path));
|
||||
EXPECT_TRUE(
|
||||
platform_device_list.Find("/devices/platform/some_device_name/other_child", &out_path));
|
||||
EXPECT_EQ("/devices/platform/some_device_name", out_path);
|
||||
}
|
||||
|
||||
TEST(devices, get_character_device_symlinks_success) {
|
||||
TEST(device_handler, get_character_device_symlinks_success) {
|
||||
const char* platform_device = "/devices/platform/some_device_name";
|
||||
uevent uevent = {
|
||||
Uevent uevent = {
|
||||
.path = "/devices/platform/some_device_name/usb/usb_device/name/tty2-1:1.0",
|
||||
.subsystem = "tty",
|
||||
};
|
||||
std::vector<std::string> expected_result{"/dev/usb/ttyname"};
|
||||
|
||||
test_get_symlinks<get_character_device_symlinks>(platform_device, &uevent, expected_result);
|
||||
DeviceHandlerTester device_handler_tester_;
|
||||
device_handler_tester_.TestGetSymlinks(platform_device, uevent, expected_result, false);
|
||||
}
|
||||
|
||||
TEST(devices, get_character_device_symlinks_no_pdev_match) {
|
||||
TEST(device_handler, get_character_device_symlinks_no_pdev_match) {
|
||||
const char* platform_device = "/devices/platform/some_device_name";
|
||||
uevent uevent = {
|
||||
Uevent uevent = {
|
||||
.path = "/device/name/tty2-1:1.0", .subsystem = "tty",
|
||||
};
|
||||
std::vector<std::string> expected_result;
|
||||
|
||||
test_get_symlinks<get_character_device_symlinks>(platform_device, &uevent, expected_result);
|
||||
DeviceHandlerTester device_handler_tester_;
|
||||
device_handler_tester_.TestGetSymlinks(platform_device, uevent, expected_result, false);
|
||||
}
|
||||
|
||||
TEST(devices, get_character_device_symlinks_nothing_after_platform_device) {
|
||||
TEST(device_handler, get_character_device_symlinks_nothing_after_platform_device) {
|
||||
const char* platform_device = "/devices/platform/some_device_name";
|
||||
uevent uevent = {
|
||||
Uevent uevent = {
|
||||
.path = "/devices/platform/some_device_name", .subsystem = "tty",
|
||||
};
|
||||
std::vector<std::string> expected_result;
|
||||
|
||||
test_get_symlinks<get_character_device_symlinks>(platform_device, &uevent, expected_result);
|
||||
DeviceHandlerTester device_handler_tester_;
|
||||
device_handler_tester_.TestGetSymlinks(platform_device, uevent, expected_result, false);
|
||||
}
|
||||
|
||||
TEST(devices, get_character_device_symlinks_no_usb_found) {
|
||||
TEST(device_handler, get_character_device_symlinks_no_usb_found) {
|
||||
const char* platform_device = "/devices/platform/some_device_name";
|
||||
uevent uevent = {
|
||||
Uevent uevent = {
|
||||
.path = "/devices/platform/some_device_name/bad/bad/", .subsystem = "tty",
|
||||
};
|
||||
std::vector<std::string> expected_result;
|
||||
|
||||
test_get_symlinks<get_character_device_symlinks>(platform_device, &uevent, expected_result);
|
||||
DeviceHandlerTester device_handler_tester_;
|
||||
device_handler_tester_.TestGetSymlinks(platform_device, uevent, expected_result, false);
|
||||
}
|
||||
|
||||
TEST(devices, get_character_device_symlinks_no_roothub) {
|
||||
TEST(device_handler, get_character_device_symlinks_no_roothub) {
|
||||
const char* platform_device = "/devices/platform/some_device_name";
|
||||
uevent uevent = {
|
||||
Uevent uevent = {
|
||||
.path = "/devices/platform/some_device_name/usb/", .subsystem = "tty",
|
||||
};
|
||||
std::vector<std::string> expected_result;
|
||||
|
||||
test_get_symlinks<get_character_device_symlinks>(platform_device, &uevent, expected_result);
|
||||
DeviceHandlerTester device_handler_tester_;
|
||||
device_handler_tester_.TestGetSymlinks(platform_device, uevent, expected_result, false);
|
||||
}
|
||||
|
||||
TEST(devices, get_character_device_symlinks_no_usb_device) {
|
||||
TEST(device_handler, get_character_device_symlinks_no_usb_device) {
|
||||
const char* platform_device = "/devices/platform/some_device_name";
|
||||
uevent uevent = {
|
||||
Uevent uevent = {
|
||||
.path = "/devices/platform/some_device_name/usb/usb_device/", .subsystem = "tty",
|
||||
};
|
||||
std::vector<std::string> expected_result;
|
||||
|
||||
test_get_symlinks<get_character_device_symlinks>(platform_device, &uevent, expected_result);
|
||||
DeviceHandlerTester device_handler_tester_;
|
||||
device_handler_tester_.TestGetSymlinks(platform_device, uevent, expected_result, false);
|
||||
}
|
||||
|
||||
TEST(devices, get_character_device_symlinks_no_final_slash) {
|
||||
TEST(device_handler, get_character_device_symlinks_no_final_slash) {
|
||||
const char* platform_device = "/devices/platform/some_device_name";
|
||||
uevent uevent = {
|
||||
Uevent uevent = {
|
||||
.path = "/devices/platform/some_device_name/usb/usb_device/name", .subsystem = "tty",
|
||||
};
|
||||
std::vector<std::string> expected_result;
|
||||
|
||||
test_get_symlinks<get_character_device_symlinks>(platform_device, &uevent, expected_result);
|
||||
DeviceHandlerTester device_handler_tester_;
|
||||
device_handler_tester_.TestGetSymlinks(platform_device, uevent, expected_result, false);
|
||||
}
|
||||
|
||||
TEST(devices, get_character_device_symlinks_no_final_name) {
|
||||
TEST(device_handler, get_character_device_symlinks_no_final_name) {
|
||||
const char* platform_device = "/devices/platform/some_device_name";
|
||||
uevent uevent = {
|
||||
Uevent uevent = {
|
||||
.path = "/devices/platform/some_device_name/usb/usb_device//", .subsystem = "tty",
|
||||
};
|
||||
std::vector<std::string> expected_result;
|
||||
|
||||
test_get_symlinks<get_character_device_symlinks>(platform_device, &uevent, expected_result);
|
||||
DeviceHandlerTester device_handler_tester_;
|
||||
device_handler_tester_.TestGetSymlinks(platform_device, uevent, expected_result, false);
|
||||
}
|
||||
|
||||
TEST(devices, get_block_device_symlinks_success_platform) {
|
||||
TEST(device_handler, get_block_device_symlinks_success_platform) {
|
||||
// These are actual paths from bullhead
|
||||
const char* platform_device = "/devices/soc.0/f9824900.sdhci";
|
||||
uevent uevent = {
|
||||
Uevent uevent = {
|
||||
.path = "/devices/soc.0/f9824900.sdhci/mmc_host/mmc0/mmc0:0001/block/mmcblk0",
|
||||
.partition_name = "",
|
||||
.partition_num = -1,
|
||||
};
|
||||
std::vector<std::string> expected_result{"/dev/block/platform/soc.0/f9824900.sdhci/mmcblk0"};
|
||||
|
||||
test_get_symlinks<get_block_device_symlinks>(platform_device, &uevent, expected_result);
|
||||
DeviceHandlerTester device_handler_tester_;
|
||||
device_handler_tester_.TestGetSymlinks(platform_device, uevent, expected_result, true);
|
||||
}
|
||||
|
||||
TEST(devices, get_block_device_symlinks_success_platform_with_partition) {
|
||||
TEST(device_handler, get_block_device_symlinks_success_platform_with_partition) {
|
||||
// These are actual paths from bullhead
|
||||
const char* platform_device = "/devices/soc.0/f9824900.sdhci";
|
||||
uevent uevent = {
|
||||
Uevent uevent = {
|
||||
.path = "/devices/soc.0/f9824900.sdhci/mmc_host/mmc0/mmc0:0001/block/mmcblk0p1",
|
||||
.partition_name = "modem",
|
||||
.partition_num = 1,
|
||||
|
@ -193,12 +206,13 @@ TEST(devices, get_block_device_symlinks_success_platform_with_partition) {
|
|||
"/dev/block/platform/soc.0/f9824900.sdhci/mmcblk0p1",
|
||||
};
|
||||
|
||||
test_get_symlinks<get_block_device_symlinks>(platform_device, &uevent, expected_result);
|
||||
DeviceHandlerTester device_handler_tester_;
|
||||
device_handler_tester_.TestGetSymlinks(platform_device, uevent, expected_result, true);
|
||||
}
|
||||
|
||||
TEST(devices, get_block_device_symlinks_success_platform_with_partition_only_num) {
|
||||
TEST(device_handler, get_block_device_symlinks_success_platform_with_partition_only_num) {
|
||||
const char* platform_device = "/devices/soc.0/f9824900.sdhci";
|
||||
uevent uevent = {
|
||||
Uevent uevent = {
|
||||
.path = "/devices/soc.0/f9824900.sdhci/mmc_host/mmc0/mmc0:0001/block/mmcblk0p1",
|
||||
.partition_name = "",
|
||||
.partition_num = 1,
|
||||
|
@ -208,12 +222,13 @@ TEST(devices, get_block_device_symlinks_success_platform_with_partition_only_num
|
|||
"/dev/block/platform/soc.0/f9824900.sdhci/mmcblk0p1",
|
||||
};
|
||||
|
||||
test_get_symlinks<get_block_device_symlinks>(platform_device, &uevent, expected_result);
|
||||
DeviceHandlerTester device_handler_tester_;
|
||||
device_handler_tester_.TestGetSymlinks(platform_device, uevent, expected_result, true);
|
||||
}
|
||||
|
||||
TEST(devices, get_block_device_symlinks_success_platform_with_partition_only_name) {
|
||||
TEST(device_handler, get_block_device_symlinks_success_platform_with_partition_only_name) {
|
||||
const char* platform_device = "/devices/soc.0/f9824900.sdhci";
|
||||
uevent uevent = {
|
||||
Uevent uevent = {
|
||||
.path = "/devices/soc.0/f9824900.sdhci/mmc_host/mmc0/mmc0:0001/block/mmcblk0p1",
|
||||
.partition_name = "modem",
|
||||
.partition_num = -1,
|
||||
|
@ -223,101 +238,107 @@ TEST(devices, get_block_device_symlinks_success_platform_with_partition_only_nam
|
|||
"/dev/block/platform/soc.0/f9824900.sdhci/mmcblk0p1",
|
||||
};
|
||||
|
||||
test_get_symlinks<get_block_device_symlinks>(platform_device, &uevent, expected_result);
|
||||
DeviceHandlerTester device_handler_tester_;
|
||||
device_handler_tester_.TestGetSymlinks(platform_device, uevent, expected_result, true);
|
||||
}
|
||||
|
||||
TEST(devices, get_block_device_symlinks_success_pci) {
|
||||
TEST(device_handler, get_block_device_symlinks_success_pci) {
|
||||
const char* platform_device = "/devices/do/not/match";
|
||||
uevent uevent = {
|
||||
Uevent uevent = {
|
||||
.path = "/devices/pci0000:00/0000:00:1f.2/mmcblk0", .partition_name = "", .partition_num = -1,
|
||||
};
|
||||
std::vector<std::string> expected_result{"/dev/block/pci/pci0000:00/0000:00:1f.2/mmcblk0"};
|
||||
|
||||
test_get_symlinks<get_block_device_symlinks>(platform_device, &uevent, expected_result);
|
||||
DeviceHandlerTester device_handler_tester_;
|
||||
device_handler_tester_.TestGetSymlinks(platform_device, uevent, expected_result, true);
|
||||
}
|
||||
|
||||
TEST(devices, get_block_device_symlinks_pci_bad_format) {
|
||||
TEST(device_handler, get_block_device_symlinks_pci_bad_format) {
|
||||
const char* platform_device = "/devices/do/not/match";
|
||||
uevent uevent = {
|
||||
Uevent uevent = {
|
||||
.path = "/devices/pci//mmcblk0", .partition_name = "", .partition_num = -1,
|
||||
};
|
||||
std::vector<std::string> expected_result{};
|
||||
|
||||
test_get_symlinks<get_block_device_symlinks>(platform_device, &uevent, expected_result);
|
||||
DeviceHandlerTester device_handler_tester_;
|
||||
device_handler_tester_.TestGetSymlinks(platform_device, uevent, expected_result, true);
|
||||
}
|
||||
|
||||
TEST(devices, get_block_device_symlinks_success_vbd) {
|
||||
TEST(device_handler, get_block_device_symlinks_success_vbd) {
|
||||
const char* platform_device = "/devices/do/not/match";
|
||||
uevent uevent = {
|
||||
Uevent uevent = {
|
||||
.path = "/devices/vbd-1234/mmcblk0", .partition_name = "", .partition_num = -1,
|
||||
};
|
||||
std::vector<std::string> expected_result{"/dev/block/vbd/1234/mmcblk0"};
|
||||
|
||||
test_get_symlinks<get_block_device_symlinks>(platform_device, &uevent, expected_result);
|
||||
DeviceHandlerTester device_handler_tester_;
|
||||
device_handler_tester_.TestGetSymlinks(platform_device, uevent, expected_result, true);
|
||||
}
|
||||
|
||||
TEST(devices, get_block_device_symlinks_vbd_bad_format) {
|
||||
TEST(device_handler, get_block_device_symlinks_vbd_bad_format) {
|
||||
const char* platform_device = "/devices/do/not/match";
|
||||
uevent uevent = {
|
||||
Uevent uevent = {
|
||||
.path = "/devices/vbd-/mmcblk0", .partition_name = "", .partition_num = -1,
|
||||
};
|
||||
std::vector<std::string> expected_result{};
|
||||
|
||||
test_get_symlinks<get_block_device_symlinks>(platform_device, &uevent, expected_result);
|
||||
DeviceHandlerTester device_handler_tester_;
|
||||
device_handler_tester_.TestGetSymlinks(platform_device, uevent, expected_result, true);
|
||||
}
|
||||
|
||||
TEST(devices, get_block_device_symlinks_no_matches) {
|
||||
TEST(device_handler, get_block_device_symlinks_no_matches) {
|
||||
const char* platform_device = "/devices/soc.0/f9824900.sdhci";
|
||||
uevent uevent = {
|
||||
Uevent uevent = {
|
||||
.path = "/devices/soc.0/not_the_device/mmc_host/mmc0/mmc0:0001/block/mmcblk0p1",
|
||||
.partition_name = "",
|
||||
.partition_num = -1,
|
||||
};
|
||||
std::vector<std::string> expected_result;
|
||||
|
||||
test_get_symlinks<get_block_device_symlinks>(platform_device, &uevent, expected_result);
|
||||
DeviceHandlerTester device_handler_tester_;
|
||||
device_handler_tester_.TestGetSymlinks(platform_device, uevent, expected_result, true);
|
||||
}
|
||||
|
||||
TEST(devices, sanitize_null) {
|
||||
sanitize_partition_name(nullptr);
|
||||
TEST(device_handler, sanitize_null) {
|
||||
SanitizePartitionName(nullptr);
|
||||
}
|
||||
|
||||
TEST(devices, sanitize_empty) {
|
||||
TEST(device_handler, sanitize_empty) {
|
||||
std::string empty;
|
||||
sanitize_partition_name(&empty);
|
||||
SanitizePartitionName(&empty);
|
||||
EXPECT_EQ(0u, empty.size());
|
||||
}
|
||||
|
||||
TEST(devices, sanitize_allgood) {
|
||||
TEST(device_handler, sanitize_allgood) {
|
||||
std::string good =
|
||||
"abcdefghijklmnopqrstuvwxyz"
|
||||
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
||||
"0123456789"
|
||||
"_-.";
|
||||
std::string good_copy = good;
|
||||
sanitize_partition_name(&good);
|
||||
SanitizePartitionName(&good);
|
||||
EXPECT_EQ(good_copy, good);
|
||||
}
|
||||
|
||||
TEST(devices, sanitize_somebad) {
|
||||
TEST(device_handler, sanitize_somebad) {
|
||||
std::string string = "abc!@#$%^&*()";
|
||||
sanitize_partition_name(&string);
|
||||
SanitizePartitionName(&string);
|
||||
EXPECT_EQ("abc__________", string);
|
||||
}
|
||||
|
||||
TEST(devices, sanitize_allbad) {
|
||||
TEST(device_handler, sanitize_allbad) {
|
||||
std::string string = "!@#$%^&*()";
|
||||
sanitize_partition_name(&string);
|
||||
SanitizePartitionName(&string);
|
||||
EXPECT_EQ("__________", string);
|
||||
}
|
||||
|
||||
TEST(devices, sanitize_onebad) {
|
||||
TEST(device_handler, sanitize_onebad) {
|
||||
std::string string = ")";
|
||||
sanitize_partition_name(&string);
|
||||
SanitizePartitionName(&string);
|
||||
EXPECT_EQ("_", string);
|
||||
}
|
||||
|
||||
TEST(devices, DevPermissionsMatchNormal) {
|
||||
TEST(device_handler, DevPermissionsMatchNormal) {
|
||||
// Basic from ueventd.rc
|
||||
// /dev/null 0666 root root
|
||||
Permissions permissions("/dev/null", 0666, 0, 0);
|
||||
|
@ -329,7 +350,7 @@ TEST(devices, DevPermissionsMatchNormal) {
|
|||
EXPECT_EQ(0U, permissions.gid());
|
||||
}
|
||||
|
||||
TEST(devices, DevPermissionsMatchPrefix) {
|
||||
TEST(device_handler, DevPermissionsMatchPrefix) {
|
||||
// Prefix from ueventd.rc
|
||||
// /dev/dri/* 0666 root graphics
|
||||
Permissions permissions("/dev/dri/*", 0666, 0, 1000);
|
||||
|
@ -342,7 +363,7 @@ TEST(devices, DevPermissionsMatchPrefix) {
|
|||
EXPECT_EQ(1000U, permissions.gid());
|
||||
}
|
||||
|
||||
TEST(devices, DevPermissionsMatchWildcard) {
|
||||
TEST(device_handler, DevPermissionsMatchWildcard) {
|
||||
// Wildcard example
|
||||
// /dev/device*name 0666 root graphics
|
||||
Permissions permissions("/dev/device*name", 0666, 0, 1000);
|
||||
|
@ -356,7 +377,7 @@ TEST(devices, DevPermissionsMatchWildcard) {
|
|||
EXPECT_EQ(1000U, permissions.gid());
|
||||
}
|
||||
|
||||
TEST(devices, DevPermissionsMatchWildcardPrefix) {
|
||||
TEST(device_handler, DevPermissionsMatchWildcardPrefix) {
|
||||
// Wildcard+Prefix example
|
||||
// /dev/device*name* 0666 root graphics
|
||||
Permissions permissions("/dev/device*name*", 0666, 0, 1000);
|
||||
|
@ -372,7 +393,7 @@ TEST(devices, DevPermissionsMatchWildcardPrefix) {
|
|||
EXPECT_EQ(1000U, permissions.gid());
|
||||
}
|
||||
|
||||
TEST(devices, SysfsPermissionsMatchWithSubsystemNormal) {
|
||||
TEST(device_handler, SysfsPermissionsMatchWithSubsystemNormal) {
|
||||
// /sys/devices/virtual/input/input* enable 0660 root input
|
||||
SysfsPermissions permissions("/sys/devices/virtual/input/input*", "enable", 0660, 0, 1001);
|
||||
EXPECT_TRUE(permissions.MatchWithSubsystem("/sys/devices/virtual/input/input0", "input"));
|
||||
|
@ -382,7 +403,7 @@ TEST(devices, SysfsPermissionsMatchWithSubsystemNormal) {
|
|||
EXPECT_EQ(1001U, permissions.gid());
|
||||
}
|
||||
|
||||
TEST(devices, SysfsPermissionsMatchWithSubsystemClass) {
|
||||
TEST(device_handler, SysfsPermissionsMatchWithSubsystemClass) {
|
||||
// /sys/class/input/event* enable 0660 root input
|
||||
SysfsPermissions permissions("/sys/class/input/event*", "enable", 0660, 0, 1001);
|
||||
EXPECT_TRUE(permissions.MatchWithSubsystem(
|
||||
|
@ -396,7 +417,7 @@ TEST(devices, SysfsPermissionsMatchWithSubsystemClass) {
|
|||
EXPECT_EQ(1001U, permissions.gid());
|
||||
}
|
||||
|
||||
TEST(devices, SysfsPermissionsMatchWithSubsystemBus) {
|
||||
TEST(device_handler, SysfsPermissionsMatchWithSubsystemBus) {
|
||||
// /sys/bus/i2c/devices/i2c-* enable 0660 root input
|
||||
SysfsPermissions permissions("/sys/bus/i2c/devices/i2c-*", "enable", 0660, 0, 1001);
|
||||
EXPECT_TRUE(permissions.MatchWithSubsystem("/sys/devices/soc.0/f9967000.i2c/i2c-5", "i2c"));
|
||||
|
|
116
init/firmware_handler.cpp
Normal file
116
init/firmware_handler.cpp
Normal file
|
@ -0,0 +1,116 @@
|
|||
/*
|
||||
* Copyright (C) 2017 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 "firmware_handler.h"
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <sys/sendfile.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <string>
|
||||
#include <thread>
|
||||
|
||||
#include <android-base/file.h>
|
||||
#include <android-base/logging.h>
|
||||
#include <android-base/unique_fd.h>
|
||||
|
||||
#include "util.h"
|
||||
|
||||
static void LoadFirmware(const Uevent& uevent, const std::string& root, int fw_fd, size_t fw_size,
|
||||
int loading_fd, int data_fd) {
|
||||
// Start transfer.
|
||||
android::base::WriteFully(loading_fd, "1", 1);
|
||||
|
||||
// Copy the firmware.
|
||||
int rc = sendfile(data_fd, fw_fd, nullptr, fw_size);
|
||||
if (rc == -1) {
|
||||
PLOG(ERROR) << "firmware: sendfile failed { '" << root << "', '" << uevent.firmware
|
||||
<< "' }";
|
||||
}
|
||||
|
||||
// Tell the firmware whether to abort or commit.
|
||||
const char* response = (rc != -1) ? "0" : "-1";
|
||||
android::base::WriteFully(loading_fd, response, strlen(response));
|
||||
}
|
||||
|
||||
static bool IsBooting() {
|
||||
return access("/dev/.booting", F_OK) == 0;
|
||||
}
|
||||
|
||||
static void ProcessFirmwareEvent(const Uevent& uevent) {
|
||||
int booting = IsBooting();
|
||||
|
||||
LOG(INFO) << "firmware: loading '" << uevent.firmware << "' for '" << uevent.path << "'";
|
||||
|
||||
std::string root = "/sys" + uevent.path;
|
||||
std::string loading = root + "/loading";
|
||||
std::string data = root + "/data";
|
||||
|
||||
android::base::unique_fd loading_fd(open(loading.c_str(), O_WRONLY | O_CLOEXEC));
|
||||
if (loading_fd == -1) {
|
||||
PLOG(ERROR) << "couldn't open firmware loading fd for " << uevent.firmware;
|
||||
return;
|
||||
}
|
||||
|
||||
android::base::unique_fd data_fd(open(data.c_str(), O_WRONLY | O_CLOEXEC));
|
||||
if (data_fd == -1) {
|
||||
PLOG(ERROR) << "couldn't open firmware data fd for " << uevent.firmware;
|
||||
return;
|
||||
}
|
||||
|
||||
static const char* firmware_dirs[] = {"/etc/firmware/", "/vendor/firmware/",
|
||||
"/firmware/image/"};
|
||||
|
||||
try_loading_again:
|
||||
for (size_t i = 0; i < arraysize(firmware_dirs); i++) {
|
||||
std::string file = firmware_dirs[i] + uevent.firmware;
|
||||
android::base::unique_fd fw_fd(open(file.c_str(), O_RDONLY | O_CLOEXEC));
|
||||
struct stat sb;
|
||||
if (fw_fd != -1 && fstat(fw_fd, &sb) != -1) {
|
||||
LoadFirmware(uevent, root, fw_fd, sb.st_size, loading_fd, data_fd);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (booting) {
|
||||
// If we're not fully booted, we may be missing
|
||||
// filesystems needed for firmware, wait and retry.
|
||||
std::this_thread::sleep_for(100ms);
|
||||
booting = IsBooting();
|
||||
goto try_loading_again;
|
||||
}
|
||||
|
||||
LOG(ERROR) << "firmware: could not find firmware for " << uevent.firmware;
|
||||
|
||||
// Write "-1" as our response to the kernel's firmware request, since we have nothing for it.
|
||||
write(loading_fd, "-1", 2);
|
||||
}
|
||||
|
||||
void HandleFirmwareEvent(const Uevent& uevent) {
|
||||
if (uevent.subsystem != "firmware" || uevent.action != "add") return;
|
||||
|
||||
// Loading the firmware in a child means we can do that in parallel...
|
||||
// (We ignore SIGCHLD rather than wait for our children.)
|
||||
pid_t pid = fork();
|
||||
if (pid == 0) {
|
||||
Timer t;
|
||||
ProcessFirmwareEvent(uevent);
|
||||
LOG(INFO) << "loading " << uevent.path << " took " << t;
|
||||
_exit(EXIT_SUCCESS);
|
||||
} else if (pid == -1) {
|
||||
PLOG(ERROR) << "could not fork to process firmware event for " << uevent.firmware;
|
||||
}
|
||||
}
|
24
init/firmware_handler.h
Normal file
24
init/firmware_handler.h
Normal file
|
@ -0,0 +1,24 @@
|
|||
/*
|
||||
* Copyright (C) 2007 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_FIRMWARE_HANDLER_H
|
||||
#define _INIT_FIRMWARE_HANDLER_H
|
||||
|
||||
#include "uevent.h"
|
||||
|
||||
void HandleFirmwareEvent(const Uevent& uevent);
|
||||
|
||||
#endif
|
|
@ -58,7 +58,6 @@
|
|||
|
||||
#include "action.h"
|
||||
#include "bootchart.h"
|
||||
#include "devices.h"
|
||||
#include "import_parser.h"
|
||||
#include "init_first_stage.h"
|
||||
#include "init_parser.h"
|
||||
|
|
|
@ -31,6 +31,8 @@
|
|||
#include "devices.h"
|
||||
#include "fs_mgr.h"
|
||||
#include "fs_mgr_avb.h"
|
||||
#include "uevent.h"
|
||||
#include "uevent_listener.h"
|
||||
#include "util.h"
|
||||
|
||||
// Class Declarations
|
||||
|
@ -51,7 +53,7 @@ class FirstStageMount {
|
|||
void InitVerityDevice(const std::string& verity_device);
|
||||
bool MountPartitions();
|
||||
|
||||
virtual coldboot_action_t ColdbootCallback(uevent* uevent);
|
||||
virtual RegenerationAction UeventCallback(const Uevent& uevent);
|
||||
|
||||
// Pure virtual functions.
|
||||
virtual bool GetRequiredDevices() = 0;
|
||||
|
@ -63,6 +65,8 @@ class FirstStageMount {
|
|||
// Eligible first stage mount candidates, only allow /system, /vendor and/or /odm.
|
||||
std::vector<fstab_rec*> mount_fstab_recs_;
|
||||
std::set<std::string> required_devices_partition_names_;
|
||||
DeviceHandler device_handler_;
|
||||
UeventListener uevent_listener_;
|
||||
};
|
||||
|
||||
class FirstStageMountVBootV1 : public FirstStageMount {
|
||||
|
@ -83,7 +87,7 @@ class FirstStageMountVBootV2 : public FirstStageMount {
|
|||
~FirstStageMountVBootV2() override = default;
|
||||
|
||||
protected:
|
||||
coldboot_action_t ColdbootCallback(uevent* uevent) override;
|
||||
RegenerationAction UeventCallback(const Uevent& uevent) override;
|
||||
bool GetRequiredDevices() override;
|
||||
bool SetUpDmVerity(fstab_rec* fstab_rec) override;
|
||||
bool InitAvbHandle();
|
||||
|
@ -165,46 +169,50 @@ void FirstStageMount::InitRequiredDevices() {
|
|||
|
||||
if (need_dm_verity_) {
|
||||
const std::string dm_path = "/devices/virtual/misc/device-mapper";
|
||||
device_init(("/sys" + dm_path).c_str(), [&dm_path](uevent* uevent) -> coldboot_action_t {
|
||||
if (uevent->path == dm_path) return COLDBOOT_STOP;
|
||||
return COLDBOOT_CONTINUE; // dm_path not found, continue to find it.
|
||||
});
|
||||
uevent_listener_.RegenerateUeventsForPath("/sys" + dm_path,
|
||||
[this, &dm_path](const Uevent& uevent) {
|
||||
if (uevent.path == dm_path) {
|
||||
device_handler_.HandleDeviceEvent(uevent);
|
||||
return RegenerationAction::kStop;
|
||||
}
|
||||
return RegenerationAction::kContinue;
|
||||
});
|
||||
}
|
||||
|
||||
device_init(nullptr,
|
||||
[this](uevent* uevent) -> coldboot_action_t { return ColdbootCallback(uevent); });
|
||||
|
||||
device_close();
|
||||
uevent_listener_.RegenerateUevents(
|
||||
[this](const Uevent& uevent) { return UeventCallback(uevent); });
|
||||
}
|
||||
|
||||
coldboot_action_t FirstStageMount::ColdbootCallback(uevent* uevent) {
|
||||
RegenerationAction FirstStageMount::UeventCallback(const Uevent& uevent) {
|
||||
// We need platform devices to create symlinks.
|
||||
if (uevent->subsystem == "platform") {
|
||||
return COLDBOOT_CREATE;
|
||||
if (uevent.subsystem == "platform") {
|
||||
device_handler_.HandleDeviceEvent(uevent);
|
||||
return RegenerationAction::kContinue;
|
||||
}
|
||||
|
||||
// Ignores everything that is not a block device.
|
||||
if (uevent->subsystem != "block") {
|
||||
return COLDBOOT_CONTINUE;
|
||||
if (uevent.subsystem != "block") {
|
||||
return RegenerationAction::kContinue;
|
||||
}
|
||||
|
||||
if (!uevent->partition_name.empty()) {
|
||||
if (!uevent.partition_name.empty()) {
|
||||
// Matches partition name to create device nodes.
|
||||
// Both required_devices_partition_names_ and uevent->partition_name have A/B
|
||||
// suffix when A/B is used.
|
||||
auto iter = required_devices_partition_names_.find(uevent->partition_name);
|
||||
auto iter = required_devices_partition_names_.find(uevent.partition_name);
|
||||
if (iter != required_devices_partition_names_.end()) {
|
||||
LOG(VERBOSE) << __FUNCTION__ << "(): found partition: " << *iter;
|
||||
required_devices_partition_names_.erase(iter);
|
||||
device_handler_.HandleDeviceEvent(uevent);
|
||||
if (required_devices_partition_names_.empty()) {
|
||||
return COLDBOOT_STOP; // Found all partitions, stop coldboot.
|
||||
return RegenerationAction::kStop;
|
||||
} else {
|
||||
return COLDBOOT_CREATE; // Creates this device and continue to find others.
|
||||
return RegenerationAction::kContinue;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Not found a partition or find an unneeded partition, continue to find others.
|
||||
return COLDBOOT_CONTINUE;
|
||||
return RegenerationAction::kContinue;
|
||||
}
|
||||
|
||||
// Creates "/dev/block/dm-XX" for dm-verity by running coldboot on /sys/block/dm-XX.
|
||||
|
@ -212,14 +220,15 @@ void FirstStageMount::InitVerityDevice(const std::string& verity_device) {
|
|||
const std::string device_name(basename(verity_device.c_str()));
|
||||
const std::string syspath = "/sys/block/" + device_name;
|
||||
|
||||
device_init(syspath.c_str(), [&](uevent* uevent) -> coldboot_action_t {
|
||||
if (uevent->device_name == device_name) {
|
||||
LOG(VERBOSE) << "Creating dm-verity device : " << verity_device;
|
||||
return COLDBOOT_STOP;
|
||||
}
|
||||
return COLDBOOT_CONTINUE;
|
||||
});
|
||||
device_close();
|
||||
uevent_listener_.RegenerateUeventsForPath(
|
||||
syspath, [&device_name, &verity_device, this](const Uevent& uevent) {
|
||||
if (uevent.device_name == device_name) {
|
||||
LOG(VERBOSE) << "Creating dm-verity device : " << verity_device;
|
||||
device_handler_.HandleDeviceEvent(uevent);
|
||||
return RegenerationAction::kStop;
|
||||
}
|
||||
return RegenerationAction::kContinue;
|
||||
});
|
||||
}
|
||||
|
||||
bool FirstStageMount::MountPartitions() {
|
||||
|
@ -345,33 +354,32 @@ bool FirstStageMountVBootV2::GetRequiredDevices() {
|
|||
return true;
|
||||
}
|
||||
|
||||
coldboot_action_t FirstStageMountVBootV2::ColdbootCallback(uevent* uevent) {
|
||||
// Invokes the parent function to see if any desired partition has been found.
|
||||
// If yes, record the by-name symlink for creating FsManagerAvbHandle later.
|
||||
coldboot_action_t parent_callback_ret = FirstStageMount::ColdbootCallback(uevent);
|
||||
|
||||
// Skips the uevent if the parent function returns COLDBOOT_CONTINUE (meaning
|
||||
// that the uevent was skipped) or there is no uevent->partition_name to
|
||||
// create the by-name symlink.
|
||||
if (parent_callback_ret != COLDBOOT_CONTINUE && !uevent->partition_name.empty()) {
|
||||
// get_block_device_symlinks() will return three symlinks at most, depending on
|
||||
RegenerationAction FirstStageMountVBootV2::UeventCallback(const Uevent& uevent) {
|
||||
// Check if this uevent corresponds to one of the required partitions and store its symlinks if
|
||||
// so, in order to create FsManagerAvbHandle later.
|
||||
// Note that the parent callback removes partitions from the list of required partitions
|
||||
// as it finds them, so this must happen first.
|
||||
if (!uevent.partition_name.empty() &&
|
||||
required_devices_partition_names_.find(uevent.partition_name) !=
|
||||
required_devices_partition_names_.end()) {
|
||||
// GetBlockDeviceSymlinks() will return three symlinks at most, depending on
|
||||
// the content of uevent. by-name symlink will be at [0] if uevent->partition_name
|
||||
// is not empty. e.g.,
|
||||
// - /dev/block/platform/soc.0/f9824900.sdhci/by-name/modem
|
||||
// - /dev/block/platform/soc.0/f9824900.sdhci/by-num/p1
|
||||
// - /dev/block/platform/soc.0/f9824900.sdhci/mmcblk0p1
|
||||
std::vector<std::string> links = get_block_device_symlinks(uevent);
|
||||
std::vector<std::string> links = device_handler_.GetBlockDeviceSymlinks(uevent);
|
||||
if (!links.empty()) {
|
||||
auto[it, inserted] = by_name_symlink_map_.emplace(uevent->partition_name, links[0]);
|
||||
auto[it, inserted] = by_name_symlink_map_.emplace(uevent.partition_name, links[0]);
|
||||
if (!inserted) {
|
||||
LOG(ERROR) << "Partition '" << uevent->partition_name
|
||||
LOG(ERROR) << "Partition '" << uevent.partition_name
|
||||
<< "' already existed in the by-name symlink map with a value of '"
|
||||
<< it->second << "', new value '" << links[0] << "' will be ignored.";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return parent_callback_ret;
|
||||
return FirstStageMount::UeventCallback(uevent);
|
||||
}
|
||||
|
||||
bool FirstStageMountVBootV2::SetUpDmVerity(fstab_rec* fstab_rec) {
|
||||
|
|
|
@ -66,9 +66,9 @@ class Parser {
|
|||
// be written.
|
||||
using LineCallback = std::function<bool(std::vector<std::string>&&, std::string*)>;
|
||||
|
||||
// TODO: init is the only user of this as a singleton; remove it.
|
||||
static Parser& GetInstance();
|
||||
|
||||
// Exposed for testing
|
||||
Parser();
|
||||
|
||||
bool ParseConfig(const std::string& path);
|
||||
|
|
34
init/uevent.h
Normal file
34
init/uevent.h
Normal file
|
@ -0,0 +1,34 @@
|
|||
/*
|
||||
* Copyright (C) 2017 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_UEVENT_H
|
||||
#define _INIT_UEVENT_H
|
||||
|
||||
#include <string>
|
||||
|
||||
struct Uevent {
|
||||
std::string action;
|
||||
std::string path;
|
||||
std::string subsystem;
|
||||
std::string firmware;
|
||||
std::string partition_name;
|
||||
std::string device_name;
|
||||
int partition_num;
|
||||
int major;
|
||||
int minor;
|
||||
};
|
||||
|
||||
#endif
|
196
init/uevent_listener.cpp
Normal file
196
init/uevent_listener.cpp
Normal file
|
@ -0,0 +1,196 @@
|
|||
/*
|
||||
* Copyright (C) 2017 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 "uevent_listener.h"
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <poll.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include <android-base/logging.h>
|
||||
#include <cutils/uevent.h>
|
||||
|
||||
static void ParseEvent(const char* msg, Uevent* uevent) {
|
||||
uevent->partition_num = -1;
|
||||
uevent->major = -1;
|
||||
uevent->minor = -1;
|
||||
uevent->action.clear();
|
||||
uevent->path.clear();
|
||||
uevent->subsystem.clear();
|
||||
uevent->firmware.clear();
|
||||
uevent->partition_name.clear();
|
||||
uevent->device_name.clear();
|
||||
// currently ignoring SEQNUM
|
||||
while (*msg) {
|
||||
if (!strncmp(msg, "ACTION=", 7)) {
|
||||
msg += 7;
|
||||
uevent->action = msg;
|
||||
} else if (!strncmp(msg, "DEVPATH=", 8)) {
|
||||
msg += 8;
|
||||
uevent->path = msg;
|
||||
} else if (!strncmp(msg, "SUBSYSTEM=", 10)) {
|
||||
msg += 10;
|
||||
uevent->subsystem = msg;
|
||||
} else if (!strncmp(msg, "FIRMWARE=", 9)) {
|
||||
msg += 9;
|
||||
uevent->firmware = msg;
|
||||
} else if (!strncmp(msg, "MAJOR=", 6)) {
|
||||
msg += 6;
|
||||
uevent->major = atoi(msg);
|
||||
} else if (!strncmp(msg, "MINOR=", 6)) {
|
||||
msg += 6;
|
||||
uevent->minor = atoi(msg);
|
||||
} else if (!strncmp(msg, "PARTN=", 6)) {
|
||||
msg += 6;
|
||||
uevent->partition_num = atoi(msg);
|
||||
} else if (!strncmp(msg, "PARTNAME=", 9)) {
|
||||
msg += 9;
|
||||
uevent->partition_name = msg;
|
||||
} else if (!strncmp(msg, "DEVNAME=", 8)) {
|
||||
msg += 8;
|
||||
uevent->device_name = msg;
|
||||
}
|
||||
|
||||
// advance to after the next \0
|
||||
while (*msg++)
|
||||
;
|
||||
}
|
||||
|
||||
if (LOG_UEVENTS) {
|
||||
LOG(INFO) << "event { '" << uevent->action << "', '" << uevent->path << "', '"
|
||||
<< uevent->subsystem << "', '" << uevent->firmware << "', " << uevent->major
|
||||
<< ", " << uevent->minor << " }";
|
||||
}
|
||||
}
|
||||
|
||||
UeventListener::UeventListener() {
|
||||
// is 256K enough? udev uses 16MB!
|
||||
device_fd_.reset(uevent_open_socket(256 * 1024, true));
|
||||
if (device_fd_ == -1) {
|
||||
LOG(FATAL) << "Could not open uevent socket";
|
||||
}
|
||||
|
||||
fcntl(device_fd_, F_SETFL, O_NONBLOCK);
|
||||
}
|
||||
|
||||
bool UeventListener::ReadUevent(Uevent* uevent) const {
|
||||
char msg[UEVENT_MSG_LEN + 2];
|
||||
int n = uevent_kernel_multicast_recv(device_fd_, msg, UEVENT_MSG_LEN);
|
||||
if (n <= 0) {
|
||||
if (errno != EAGAIN && errno != EWOULDBLOCK) {
|
||||
LOG(ERROR) << "Error reading from Uevent Fd";
|
||||
}
|
||||
return false;
|
||||
}
|
||||
if (n >= UEVENT_MSG_LEN) {
|
||||
LOG(ERROR) << "Uevent overflowed buffer, discarding";
|
||||
// Return true here even if we discard as we may have more uevents pending and we
|
||||
// want to keep processing them.
|
||||
return true;
|
||||
}
|
||||
|
||||
msg[n] = '\0';
|
||||
msg[n + 1] = '\0';
|
||||
|
||||
ParseEvent(msg, uevent);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// RegenerateUevents*() walks parts of the /sys tree and pokes the uevent files to cause the kernel
|
||||
// to regenerate device add uevents that have already happened. This is particularly useful when
|
||||
// starting ueventd, to regenerate all of the uevents that it had previously missed.
|
||||
//
|
||||
// We drain any pending events from the netlink socket every time we poke another uevent file to
|
||||
// make sure we don't overrun the socket's buffer.
|
||||
//
|
||||
|
||||
RegenerationAction UeventListener::RegenerateUeventsForDir(DIR* d,
|
||||
RegenerateCallback callback) const {
|
||||
int dfd = dirfd(d);
|
||||
|
||||
int fd = openat(dfd, "uevent", O_WRONLY);
|
||||
if (fd >= 0) {
|
||||
write(fd, "add\n", 4);
|
||||
close(fd);
|
||||
|
||||
Uevent uevent;
|
||||
while (ReadUevent(&uevent)) {
|
||||
if (callback(uevent) == RegenerationAction::kStop) return RegenerationAction::kStop;
|
||||
}
|
||||
}
|
||||
|
||||
dirent* de;
|
||||
while ((de = readdir(d)) != nullptr) {
|
||||
if (de->d_type != DT_DIR || de->d_name[0] == '.') continue;
|
||||
|
||||
fd = openat(dfd, de->d_name, O_RDONLY | O_DIRECTORY);
|
||||
if (fd < 0) continue;
|
||||
|
||||
std::unique_ptr<DIR, decltype(&closedir)> d2(fdopendir(fd), closedir);
|
||||
if (d2 == 0) {
|
||||
close(fd);
|
||||
} else {
|
||||
if (RegenerateUeventsForDir(d2.get(), callback) == RegenerationAction::kStop) {
|
||||
return RegenerationAction::kStop;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// default is always to continue looking for uevents
|
||||
return RegenerationAction::kContinue;
|
||||
}
|
||||
|
||||
RegenerationAction UeventListener::RegenerateUeventsForPath(const std::string& path,
|
||||
RegenerateCallback callback) const {
|
||||
std::unique_ptr<DIR, decltype(&closedir)> d(opendir(path.c_str()), closedir);
|
||||
if (!d) return RegenerationAction::kContinue;
|
||||
|
||||
return RegenerateUeventsForDir(d.get(), callback);
|
||||
}
|
||||
|
||||
static const char* kRegenerationPaths[] = {"/sys/class", "/sys/block", "/sys/devices"};
|
||||
|
||||
void UeventListener::RegenerateUevents(RegenerateCallback callback) const {
|
||||
for (const auto path : kRegenerationPaths) {
|
||||
if (RegenerateUeventsForPath(path, callback) == RegenerationAction::kStop) return;
|
||||
}
|
||||
}
|
||||
|
||||
void UeventListener::DoPolling(PollCallback callback) const {
|
||||
pollfd ufd;
|
||||
ufd.events = POLLIN;
|
||||
ufd.fd = device_fd_;
|
||||
|
||||
while (true) {
|
||||
ufd.revents = 0;
|
||||
int nr = poll(&ufd, 1, -1);
|
||||
if (nr <= 0) {
|
||||
continue;
|
||||
}
|
||||
if (ufd.revents & POLLIN) {
|
||||
// We're non-blocking, so if we receive a poll event keep processing until there
|
||||
// we have exhausted all uevent messages.
|
||||
Uevent uevent;
|
||||
while (ReadUevent(&uevent)) {
|
||||
callback(uevent);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
54
init/uevent_listener.h
Normal file
54
init/uevent_listener.h
Normal file
|
@ -0,0 +1,54 @@
|
|||
/*
|
||||
* Copyright (C) 2017 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_UEVENT_LISTENER_H
|
||||
#define _INIT_UEVENT_LISTENER_H
|
||||
|
||||
#include <dirent.h>
|
||||
|
||||
#include <functional>
|
||||
|
||||
#include <android-base/unique_fd.h>
|
||||
|
||||
#include "uevent.h"
|
||||
|
||||
#define UEVENT_MSG_LEN 2048
|
||||
|
||||
enum class RegenerationAction {
|
||||
kStop = 0, // Stop regenerating uevents as we've handled the one(s) we're interested in.
|
||||
kContinue, // Continue regenerating uevents as we haven't seen the one(s) we're interested in.
|
||||
};
|
||||
|
||||
using RegenerateCallback = std::function<RegenerationAction(const Uevent&)>;
|
||||
using PollCallback = std::function<void(const Uevent&)>;
|
||||
|
||||
class UeventListener {
|
||||
public:
|
||||
UeventListener();
|
||||
|
||||
void RegenerateUevents(RegenerateCallback callback) const;
|
||||
RegenerationAction RegenerateUeventsForPath(const std::string& path,
|
||||
RegenerateCallback callback) const;
|
||||
void DoPolling(PollCallback callback) const;
|
||||
|
||||
private:
|
||||
bool ReadUevent(Uevent* uevent) const;
|
||||
RegenerationAction RegenerateUeventsForDir(DIR* d, RegenerateCallback callback) const;
|
||||
|
||||
android::base::unique_fd device_fd_;
|
||||
};
|
||||
|
||||
#endif
|
|
@ -18,7 +18,6 @@
|
|||
|
||||
#include <ctype.h>
|
||||
#include <fcntl.h>
|
||||
#include <poll.h>
|
||||
#include <signal.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
@ -30,9 +29,44 @@
|
|||
#include <selinux/selinux.h>
|
||||
|
||||
#include "devices.h"
|
||||
#include "firmware_handler.h"
|
||||
#include "log.h"
|
||||
#include "uevent_listener.h"
|
||||
#include "ueventd_parser.h"
|
||||
#include "util.h"
|
||||
|
||||
DeviceHandler CreateDeviceHandler() {
|
||||
Parser parser;
|
||||
|
||||
std::vector<Subsystem> subsystems;
|
||||
parser.AddSectionParser("subsystem", std::make_unique<SubsystemParser>(&subsystems));
|
||||
|
||||
using namespace std::placeholders;
|
||||
std::vector<SysfsPermissions> sysfs_permissions;
|
||||
std::vector<Permissions> dev_permissions;
|
||||
parser.AddSingleLineParser(
|
||||
"/sys/", std::bind(ParsePermissionsLine, _1, _2, &sysfs_permissions, nullptr));
|
||||
parser.AddSingleLineParser("/dev/",
|
||||
std::bind(ParsePermissionsLine, _1, _2, nullptr, &dev_permissions));
|
||||
|
||||
parser.ParseConfig("/ueventd.rc");
|
||||
parser.ParseConfig("/vendor/ueventd.rc");
|
||||
parser.ParseConfig("/odm/ueventd.rc");
|
||||
|
||||
/*
|
||||
* keep the current product name base configuration so
|
||||
* we remain backwards compatible and allow it to override
|
||||
* everything
|
||||
* TODO: cleanup platform ueventd.rc to remove vendor specific
|
||||
* device node entries (b/34968103)
|
||||
*/
|
||||
std::string hardware = android::base::GetProperty("ro.hardware", "");
|
||||
parser.ParseConfig("/ueventd." + hardware + ".rc");
|
||||
|
||||
return DeviceHandler(std::move(dev_permissions), std::move(sysfs_permissions),
|
||||
std::move(subsystems));
|
||||
}
|
||||
|
||||
int ueventd_main(int argc, char **argv)
|
||||
{
|
||||
/*
|
||||
|
@ -57,41 +91,26 @@ int ueventd_main(int argc, char **argv)
|
|||
cb.func_log = selinux_klog_callback;
|
||||
selinux_set_callback(SELINUX_CB_LOG, cb);
|
||||
|
||||
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");
|
||||
DeviceHandler device_handler = CreateDeviceHandler();
|
||||
UeventListener uevent_listener;
|
||||
|
||||
/*
|
||||
* keep the current product name base configuration so
|
||||
* we remain backwards compatible and allow it to override
|
||||
* everything
|
||||
* TODO: cleanup platform ueventd.rc to remove vendor specific
|
||||
* device node entries (b/34968103)
|
||||
*/
|
||||
std::string hardware = android::base::GetProperty("ro.hardware", "");
|
||||
parser.ParseConfig("/ueventd." + hardware + ".rc");
|
||||
if (access(COLDBOOT_DONE, F_OK) != 0) {
|
||||
Timer t;
|
||||
|
||||
device_init();
|
||||
uevent_listener.RegenerateUevents([&device_handler](const Uevent& uevent) {
|
||||
HandleFirmwareEvent(uevent);
|
||||
device_handler.HandleDeviceEvent(uevent);
|
||||
return RegenerationAction::kContinue;
|
||||
});
|
||||
|
||||
pollfd ufd;
|
||||
ufd.events = POLLIN;
|
||||
ufd.fd = get_device_fd();
|
||||
|
||||
while (true) {
|
||||
ufd.revents = 0;
|
||||
int nr = poll(&ufd, 1, -1);
|
||||
if (nr <= 0) {
|
||||
continue;
|
||||
}
|
||||
if (ufd.revents & POLLIN) {
|
||||
handle_device_fd();
|
||||
}
|
||||
close(open(COLDBOOT_DONE, O_WRONLY | O_CREAT | O_CLOEXEC, 0000));
|
||||
LOG(INFO) << "Coldboot took " << t;
|
||||
}
|
||||
|
||||
uevent_listener.DoPolling([&device_handler](const Uevent& uevent) {
|
||||
HandleFirmwareEvent(uevent);
|
||||
device_handler.HandleDeviceEvent(uevent);
|
||||
});
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
145
init/ueventd_parser.cpp
Normal file
145
init/ueventd_parser.cpp
Normal file
|
@ -0,0 +1,145 @@
|
|||
/*
|
||||
* Copyright (C) 2017 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 <grp.h>
|
||||
#include <pwd.h>
|
||||
|
||||
#include "keyword_map.h"
|
||||
|
||||
bool ParsePermissionsLine(std::vector<std::string>&& args, std::string* err,
|
||||
std::vector<SysfsPermissions>* out_sysfs_permissions,
|
||||
std::vector<Permissions>* out_dev_permissions) {
|
||||
bool is_sysfs = out_sysfs_permissions != nullptr;
|
||||
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) {
|
||||
out_sysfs_permissions->emplace_back(name, sysfs_attribute, perm, uid, gid);
|
||||
} else {
|
||||
out_dev_permissions->emplace_back(name, perm, uid, gid);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
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_));
|
||||
}
|
46
init/ueventd_parser.h
Normal file
46
init/ueventd_parser.h
Normal file
|
@ -0,0 +1,46 @@
|
|||
/*
|
||||
* Copyright (C) 2007 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 <string>
|
||||
#include <vector>
|
||||
|
||||
#include "devices.h"
|
||||
#include "init_parser.h"
|
||||
|
||||
class SubsystemParser : public SectionParser {
|
||||
public:
|
||||
SubsystemParser(std::vector<Subsystem>* subsystems) : subsystems_(subsystems) {}
|
||||
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_;
|
||||
std::vector<Subsystem>* subsystems_;
|
||||
};
|
||||
|
||||
bool ParsePermissionsLine(std::vector<std::string>&& args, std::string* err,
|
||||
std::vector<SysfsPermissions>* out_sysfs_permissions,
|
||||
std::vector<Permissions>* out_dev_permissions);
|
||||
|
||||
#endif
|
Loading…
Reference in a new issue