diff --git a/init/README.ueventd.md b/init/README.ueventd.md index aac4acb6a..0e84c6f5f 100644 --- a/init/README.ueventd.md +++ b/init/README.ueventd.md @@ -76,17 +76,17 @@ For example When `/dev/null` is created, its mode will be set to `0666`, its user to `root` and its group to `root`. -The path can be modified using a ueventd.rc script and a `subsystem` section. There are three to set -for a subsystem: the subsystem name, which device name to use, and which directory to place the -device in. The section takes the below format of +The path can be modified using a ueventd.rc script and a `subsystem` and/or `driver` section. +There are three options to set for a subsystem or driver: the name, which device name to use, +and which directory to place the device in. The section takes the below format of subsystem devname uevent_devname|uevent_devpath [dirname ] -`subsystem_name` is used to match uevent `SUBSYSTEM` value +`subsystem_name` is used to match the uevent `SUBSYSTEM` value. -`devname` takes one of three options +`devname` takes one of three options: 1. `uevent_devname` specifies that the name of the node will be the uevent `DEVNAME` 2. `uevent_devpath` specifies that the name of the node will be basename uevent `DEVPATH` 3. `sys_name` specifies that the name of the node will be the contents of `/sys/DEVPATH/name` @@ -99,9 +99,13 @@ For example subsystem sound devname uevent_devpath dirname /dev/snd -Indicates that all uevents with `SUBSYSTEM=sound` will create nodes as `/dev/snd/`. +The `driver` section has the exact same structure as a `subsystem` section, but +will instead match the `DRIVER` value in a `bind`/`unbind` uevent. However, the +`driver` section will be ignored for block devices. + ## /sys ---- Ueventd by default takes no action for `/sys`, however it can be instructed to set permissions for diff --git a/init/block_dev_initializer.cpp b/init/block_dev_initializer.cpp index 7f83037ae..deb68e9ac 100644 --- a/init/block_dev_initializer.cpp +++ b/init/block_dev_initializer.cpp @@ -33,7 +33,8 @@ BlockDevInitializer::BlockDevInitializer() : uevent_listener_(16 * 1024 * 1024) auto boot_devices = android::fs_mgr::GetBootDevices(); device_handler_ = std::make_unique( std::vector{}, std::vector{}, std::vector{}, - std::move(boot_devices), android::fs_mgr::GetBootPartUuid(), false); + std::vector{}, std::move(boot_devices), android::fs_mgr::GetBootPartUuid(), + false); } // If boot_part_uuid is specified, use it to set boot_devices diff --git a/init/devices.cpp b/init/devices.cpp index fafa58f45..aeaa43133 100644 --- a/init/devices.cpp +++ b/init/devices.cpp @@ -345,6 +345,26 @@ bool DeviceHandler::FindScsiDevice(const std::string& path, std::string* scsi_de return FindSubsystemDevice(path, scsi_device_path, subsystem_paths); } +void DeviceHandler::TrackDeviceUevent(const Uevent& uevent) { + // No need to track any events if we won't bother handling any bind events + // later. + if (drivers_.size() == 0) return; + + // Only track add, and not for block devices. We don't track remove because + // unbind events may arrive after remove events, so unbind will be the + // trigger to untrack those events. + if ((uevent.action != "add") || uevent.subsystem == "block" || + (uevent.major < 0 || uevent.minor < 0)) { + return; + } + + std::string path = sysfs_mount_point_ + uevent.path + "/device"; + std::string device; + if (!Realpath(path, &device)) return; + + tracked_uevents_.emplace_back(uevent, device); +} + void DeviceHandler::FixupSysPermissions(const std::string& upath, const std::string& subsystem) const { // upaths omit the "/sys" that paths in this list @@ -664,12 +684,53 @@ bool DeviceHandler::CheckUeventForBootPartUuid(const Uevent& uevent) { return true; } +void DeviceHandler::HandleBindInternal(std::string driver_name, std::string action, + const Uevent& uevent) { + if (uevent.subsystem == "block") { + LOG(FATAL) << "Tried to handle bind event for block device"; + } + + // Get tracked uevents for all devices that have this uevent's path as + // their canonical device path. Then handle those again if their driver + // is one of the ones we're interested in. + const auto driver = std::find(drivers_.cbegin(), drivers_.cend(), driver_name); + if (driver == drivers_.cend()) return; + + std::string bind_path = sysfs_mount_point_ + uevent.path; + for (const TrackedUevent& tracked : tracked_uevents_) { + if (tracked.canonical_device_path != bind_path) continue; + + LOG(VERBOSE) << "Propagating " << uevent.action << " as " << action << " for " + << uevent.path; + + std::string devpath = driver->ParseDevPath(tracked.uevent); + mkdir_recursive(Dirname(devpath), 0755); + HandleDevice(action, devpath, false, tracked.uevent.major, tracked.uevent.minor, + std::vector{}); + } +} + void DeviceHandler::HandleUevent(const Uevent& uevent) { if (uevent.action == "add" || uevent.action == "change" || uevent.action == "bind" || uevent.action == "online") { FixupSysPermissions(uevent.path, uevent.subsystem); } + if (uevent.action == "bind") { + bound_drivers_[uevent.path] = uevent.driver; + HandleBindInternal(uevent.driver, "add", uevent); + return; + } else if (uevent.action == "unbind") { + if (bound_drivers_.count(uevent.path) == 0) return; + HandleBindInternal(bound_drivers_[uevent.path], "remove", uevent); + + std::string sys_path = sysfs_mount_point_ + uevent.path; + std::erase_if(tracked_uevents_, [&sys_path](const TrackedUevent& tracked) { + return sys_path == tracked.canonical_device_path; + }); + return; + } + // if it's not a /dev device, nothing to do if (uevent.major < 0 || uevent.minor < 0) return; @@ -677,6 +738,8 @@ void DeviceHandler::HandleUevent(const Uevent& uevent) { std::vector links; bool block = false; + TrackDeviceUevent(uevent); + if (uevent.subsystem == "block") { block = true; devpath = "/dev/block/" + Basename(uevent.path); @@ -725,10 +788,12 @@ void DeviceHandler::ColdbootDone() { DeviceHandler::DeviceHandler(std::vector dev_permissions, std::vector sysfs_permissions, - std::vector subsystems, std::set boot_devices, - std::string boot_part_uuid, bool skip_restorecon) + std::vector drivers, std::vector subsystems, + std::set boot_devices, std::string boot_part_uuid, + bool skip_restorecon) : dev_permissions_(std::move(dev_permissions)), sysfs_permissions_(std::move(sysfs_permissions)), + drivers_(std::move(drivers)), subsystems_(std::move(subsystems)), boot_devices_(std::move(boot_devices)), boot_part_uuid_(boot_part_uuid), @@ -744,7 +809,8 @@ DeviceHandler::DeviceHandler(std::vector dev_permissions, DeviceHandler::DeviceHandler() : DeviceHandler(std::vector{}, std::vector{}, - std::vector{}, std::set{}, "", false) {} + std::vector{}, std::vector{}, std::set{}, "", + false) {} } // namespace init } // namespace android diff --git a/init/devices.h b/init/devices.h index b8f8e542f..69a244978 100644 --- a/init/devices.h +++ b/init/devices.h @@ -21,6 +21,7 @@ #include #include +#include #include #include #include @@ -128,7 +129,7 @@ class DeviceHandler : public UeventHandler { DeviceHandler(); DeviceHandler(std::vector dev_permissions, - std::vector sysfs_permissions, + std::vector sysfs_permissions, std::vector drivers, std::vector subsystems, std::set boot_devices, std::string boot_part_uuid, bool skip_restorecon); virtual ~DeviceHandler() = default; @@ -145,6 +146,11 @@ class DeviceHandler : public UeventHandler { bool IsBootDevice(const Uevent& uevent) const; private: + struct TrackedUevent { + Uevent uevent; + std::string canonical_device_path; + }; + void ColdbootDone() override; BlockDeviceInfo GetBlockDeviceInfo(const std::string& uevent_path) const; bool FindSubsystemDevice(std::string path, std::string* device_path, @@ -163,14 +169,21 @@ class DeviceHandler : public UeventHandler { void FixupSysPermissions(const std::string& upath, const std::string& subsystem) const; void HandleAshmemUevent(const Uevent& uevent); + void TrackDeviceUevent(const Uevent& uevent); + void HandleBindInternal(std::string driver_name, std::string action, const Uevent& uevent); + std::vector dev_permissions_; std::vector sysfs_permissions_; + std::vector drivers_; std::vector subsystems_; std::set boot_devices_; std::string boot_part_uuid_; bool found_boot_part_uuid_; bool skip_restorecon_; std::string sysfs_mount_point_; + + std::vector tracked_uevents_; + std::map bound_drivers_; }; // Exposed for testing diff --git a/init/uevent.h b/init/uevent.h index c8ca52aaf..e7ed2266e 100644 --- a/init/uevent.h +++ b/init/uevent.h @@ -26,6 +26,7 @@ struct Uevent { std::string action; std::string path; std::string subsystem; + std::string driver; std::string firmware; std::string partition_name; std::string partition_uuid; diff --git a/init/uevent_listener.cpp b/init/uevent_listener.cpp index 97f3de640..d329c174f 100644 --- a/init/uevent_listener.cpp +++ b/init/uevent_listener.cpp @@ -36,6 +36,7 @@ static void ParseEvent(const char* msg, Uevent* uevent) { uevent->action.clear(); uevent->path.clear(); uevent->subsystem.clear(); + uevent->driver.clear(); uevent->firmware.clear(); uevent->partition_name.clear(); uevent->device_name.clear(); @@ -51,6 +52,9 @@ static void ParseEvent(const char* msg, Uevent* uevent) { } else if (!strncmp(msg, "SUBSYSTEM=", 10)) { msg += 10; uevent->subsystem = msg; + } else if (!strncmp(msg, "DRIVER=", 7)) { + msg += 7; + uevent->driver = msg; } else if (!strncmp(msg, "FIRMWARE=", 9)) { msg += 9; uevent->firmware = msg; diff --git a/init/ueventd.cpp b/init/ueventd.cpp index 286e47266..cb6b851d6 100644 --- a/init/ueventd.cpp +++ b/init/ueventd.cpp @@ -364,8 +364,8 @@ int ueventd_main(int argc, char** argv) { std::unique_ptr device_handler = std::make_unique( std::move(ueventd_configuration.dev_permissions), std::move(ueventd_configuration.sysfs_permissions), - std::move(ueventd_configuration.subsystems), android::fs_mgr::GetBootDevices(), - android::fs_mgr::GetBootPartUuid(), true); + std::move(ueventd_configuration.drivers), std::move(ueventd_configuration.subsystems), + android::fs_mgr::GetBootDevices(), android::fs_mgr::GetBootPartUuid(), true); uevent_listener.RegenerateUevents([&](const Uevent& uevent) -> ListenerAction { bool uuid_check_done = device_handler->CheckUeventForBootPartUuid(uevent); return uuid_check_done ? ListenerAction::kStop : ListenerAction::kContinue; diff --git a/init/ueventd_parser.cpp b/init/ueventd_parser.cpp index 4395d8838..097ef09d7 100644 --- a/init/ueventd_parser.cpp +++ b/init/ueventd_parser.cpp @@ -264,6 +264,8 @@ UeventdConfiguration ParseConfig(const std::vector& configs) { parser.AddSectionParser("import", std::make_unique(&parser)); parser.AddSectionParser("subsystem", std::make_unique(&ueventd_configuration.subsystems)); + parser.AddSectionParser("driver", + std::make_unique(&ueventd_configuration.drivers)); using namespace std::placeholders; parser.AddSingleLineParser( diff --git a/init/ueventd_parser.h b/init/ueventd_parser.h index 81f4e9d54..ffe6072df 100644 --- a/init/ueventd_parser.h +++ b/init/ueventd_parser.h @@ -27,6 +27,7 @@ namespace init { struct UeventdConfiguration { std::vector subsystems; + std::vector drivers; std::vector sysfs_permissions; std::vector dev_permissions; std::vector firmware_directories; diff --git a/init/ueventd_parser_test.cpp b/init/ueventd_parser_test.cpp index 41924e235..6d910398a 100644 --- a/init/ueventd_parser_test.cpp +++ b/init/ueventd_parser_test.cpp @@ -106,7 +106,32 @@ subsystem test_devpath_dirname {"test_devname2", Subsystem::DEVNAME_UEVENT_DEVNAME, "/dev"}, {"test_devpath_dirname", Subsystem::DEVNAME_UEVENT_DEVPATH, "/dev/graphics"}}; - TestUeventdFile(ueventd_file, {subsystems, {}, {}, {}, {}, {}}); + TestUeventdFile(ueventd_file, {subsystems, {}, {}, {}, {}, {}, {}}); +} + +TEST(ueventd_parser, Drivers) { + auto ueventd_file = R"( +driver test_devname + devname uevent_devname + +driver test_devpath_no_dirname + devname uevent_devpath + +driver test_devname2 + devname uevent_devname + +driver test_devpath_dirname + devname uevent_devpath + dirname /dev/graphics +)"; + + auto drivers = std::vector{ + {"test_devname", Subsystem::DEVNAME_UEVENT_DEVNAME, "/dev"}, + {"test_devpath_no_dirname", Subsystem::DEVNAME_UEVENT_DEVPATH, "/dev"}, + {"test_devname2", Subsystem::DEVNAME_UEVENT_DEVNAME, "/dev"}, + {"test_devpath_dirname", Subsystem::DEVNAME_UEVENT_DEVPATH, "/dev/graphics"}}; + + TestUeventdFile(ueventd_file, {{}, drivers, {}, {}, {}, {}, {}, {}}); } TEST(ueventd_parser, Permissions) { @@ -132,7 +157,7 @@ TEST(ueventd_parser, Permissions) { {"/sys/devices/virtual/*/input", "poll_delay", 0660, AID_ROOT, AID_INPUT, true}, }; - TestUeventdFile(ueventd_file, {{}, sysfs_permissions, permissions, {}, {}, {}}); + TestUeventdFile(ueventd_file, {{}, {}, sysfs_permissions, permissions, {}, {}, {}}); } TEST(ueventd_parser, FirmwareDirectories) { @@ -148,7 +173,7 @@ firmware_directories /more "/more", }; - TestUeventdFile(ueventd_file, {{}, {}, {}, firmware_directories, {}, {}}); + TestUeventdFile(ueventd_file, {{}, {}, {}, {}, firmware_directories, {}, {}}); } TEST(ueventd_parser, ExternalFirmwareHandlers) { @@ -214,7 +239,7 @@ external_firmware_handler /devices/path/firmware/something004.bin radio radio "/ }, }; - TestUeventdFile(ueventd_file, {{}, {}, {}, {}, external_firmware_handlers, {}}); + TestUeventdFile(ueventd_file, {{}, {}, {}, {}, {}, external_firmware_handlers, {}}); } TEST(ueventd_parser, ExternalFirmwareHandlersDuplicate) { @@ -232,7 +257,7 @@ external_firmware_handler devpath root handler_path2 }, }; - TestUeventdFile(ueventd_file, {{}, {}, {}, {}, external_firmware_handlers, {}}); + TestUeventdFile(ueventd_file, {{}, {}, {}, {}, {}, external_firmware_handlers, {}}); } TEST(ueventd_parser, ParallelRestoreconDirs) { @@ -246,7 +271,7 @@ parallel_restorecon_dir /sys/devices "/sys/devices", }; - TestUeventdFile(ueventd_file, {{}, {}, {}, {}, {}, parallel_restorecon_dirs}); + TestUeventdFile(ueventd_file, {{}, {}, {}, {}, {}, {}, parallel_restorecon_dirs}); } TEST(ueventd_parser, UeventSocketRcvbufSize) { @@ -255,7 +280,7 @@ uevent_socket_rcvbuf_size 8k uevent_socket_rcvbuf_size 8M )"; - TestUeventdFile(ueventd_file, {{}, {}, {}, {}, {}, {}, false, 8 * 1024 * 1024}); + TestUeventdFile(ueventd_file, {{}, {}, {}, {}, {}, {}, {}, false, 8 * 1024 * 1024}); } TEST(ueventd_parser, EnabledDisabledLines) { @@ -265,7 +290,7 @@ parallel_restorecon enabled modalias_handling disabled )"; - TestUeventdFile(ueventd_file, {{}, {}, {}, {}, {}, {}, false, 0, true}); + TestUeventdFile(ueventd_file, {{}, {}, {}, {}, {}, {}, {}, false, 0, true}); auto ueventd_file2 = R"( parallel_restorecon enabled @@ -273,7 +298,7 @@ modalias_handling enabled parallel_restorecon disabled )"; - TestUeventdFile(ueventd_file2, {{}, {}, {}, {}, {}, {}, true, 0, false}); + TestUeventdFile(ueventd_file2, {{}, {}, {}, {}, {}, {}, {}, true, 0, false}); } TEST(ueventd_parser, AllTogether) { @@ -286,6 +311,9 @@ firmware_directories /first/ /second /third subsystem test_devname devname uevent_devname +driver d_test_devpath + devname uevent_devpath + /dev/graphics/* 0660 root graphics subsystem test_devpath_no_dirname @@ -303,6 +331,10 @@ subsystem test_devpath_dirname devname uevent_devpath dirname /dev/graphics +driver d_test_devname_dirname + devname uevent_devname + dirname /dev/sound + /dev/*/test 0660 root system /sys/devices/virtual/*/input poll_delay 0660 root input no_fnm_pathname firmware_directories /more @@ -325,6 +357,10 @@ parallel_restorecon_dir /sys/devices {"test_devname2", Subsystem::DEVNAME_UEVENT_DEVNAME, "/dev"}, {"test_devpath_dirname", Subsystem::DEVNAME_UEVENT_DEVPATH, "/dev/graphics"}}; + auto drivers = std::vector{ + {"d_test_devpath", Subsystem::DEVNAME_UEVENT_DEVPATH, "/dev"}, + {"d_test_devname_dirname", Subsystem::DEVNAME_UEVENT_DEVNAME, "/dev/graphics"}}; + auto permissions = std::vector{ {"/dev/rtc0", 0640, AID_SYSTEM, AID_SYSTEM, false}, {"/dev/graphics/*", 0660, AID_ROOT, AID_GRAPHICS, false}, @@ -356,7 +392,7 @@ parallel_restorecon_dir /sys/devices size_t uevent_socket_rcvbuf_size = 6 * 1024 * 1024; TestUeventdFile(ueventd_file, - {subsystems, sysfs_permissions, permissions, firmware_directories, + {subsystems, drivers, sysfs_permissions, permissions, firmware_directories, external_firmware_handlers, parallel_restorecon_dirs, true, uevent_socket_rcvbuf_size, true}); }