From d17d5c585efe81d2e8a048efb919c2b7faabcb4a Mon Sep 17 00:00:00 2001 From: Eric Caruso Date: Mon, 28 Oct 2024 15:30:11 -0400 Subject: [PATCH] ueventd: add support for driver section in ueventd.rc Allow ueventd configuration to specify what to do with devices based on driver. This responds to bind events and treats them similarly to add events. The format of the driver stanza is exactly the same as that of the subsystem stanza. Bug: 376900376 Test: set up cbc_mbim driver stanza and ensure it properly creates and destroys device nodes when a USB device with that driver appears and disappears or is bound and unbound Change-Id: I31f5c91bd074d14075b74fe7beefaa6ac07a7ac9 --- init/README.ueventd.md | 16 +++++--- init/block_dev_initializer.cpp | 3 +- init/devices.cpp | 72 ++++++++++++++++++++++++++++++++-- init/devices.h | 15 ++++++- init/uevent.h | 1 + init/uevent_listener.cpp | 4 ++ init/ueventd.cpp | 4 +- init/ueventd_parser.cpp | 2 + init/ueventd_parser.h | 1 + init/ueventd_parser_test.cpp | 56 +++++++++++++++++++++----- 10 files changed, 151 insertions(+), 23 deletions(-) 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}); }