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
This commit is contained in:
Eric Caruso 2024-10-28 15:30:11 -04:00
parent d68632becc
commit d17d5c585e
10 changed files with 151 additions and 23 deletions

View file

@ -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 <subsystem_name>
devname uevent_devname|uevent_devpath
[dirname <directory>]
`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/<basename uevent
indicates that all uevents with `SUBSYSTEM=sound` will create nodes as `/dev/snd/<basename uevent
DEVPATH>`.
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

View file

@ -33,7 +33,8 @@ BlockDevInitializer::BlockDevInitializer() : uevent_listener_(16 * 1024 * 1024)
auto boot_devices = android::fs_mgr::GetBootDevices();
device_handler_ = std::make_unique<DeviceHandler>(
std::vector<Permissions>{}, std::vector<SysfsPermissions>{}, std::vector<Subsystem>{},
std::move(boot_devices), android::fs_mgr::GetBootPartUuid(), false);
std::vector<Subsystem>{}, std::move(boot_devices), android::fs_mgr::GetBootPartUuid(),
false);
}
// If boot_part_uuid is specified, use it to set boot_devices

View file

@ -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<std::string>{});
}
}
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<std::string> 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<Permissions> dev_permissions,
std::vector<SysfsPermissions> sysfs_permissions,
std::vector<Subsystem> subsystems, std::set<std::string> boot_devices,
std::string boot_part_uuid, bool skip_restorecon)
std::vector<Subsystem> drivers, std::vector<Subsystem> subsystems,
std::set<std::string> 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<Permissions> dev_permissions,
DeviceHandler::DeviceHandler()
: DeviceHandler(std::vector<Permissions>{}, std::vector<SysfsPermissions>{},
std::vector<Subsystem>{}, std::set<std::string>{}, "", false) {}
std::vector<Subsystem>{}, std::vector<Subsystem>{}, std::set<std::string>{}, "",
false) {}
} // namespace init
} // namespace android

View file

@ -21,6 +21,7 @@
#include <sys/types.h>
#include <algorithm>
#include <map>
#include <set>
#include <string>
#include <vector>
@ -128,7 +129,7 @@ class DeviceHandler : public UeventHandler {
DeviceHandler();
DeviceHandler(std::vector<Permissions> dev_permissions,
std::vector<SysfsPermissions> sysfs_permissions,
std::vector<SysfsPermissions> sysfs_permissions, std::vector<Subsystem> drivers,
std::vector<Subsystem> subsystems, std::set<std::string> 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<Permissions> dev_permissions_;
std::vector<SysfsPermissions> sysfs_permissions_;
std::vector<Subsystem> drivers_;
std::vector<Subsystem> subsystems_;
std::set<std::string> boot_devices_;
std::string boot_part_uuid_;
bool found_boot_part_uuid_;
bool skip_restorecon_;
std::string sysfs_mount_point_;
std::vector<TrackedUevent> tracked_uevents_;
std::map<std::string, std::string> bound_drivers_;
};
// Exposed for testing

View file

@ -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;

View file

@ -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;

View file

@ -364,8 +364,8 @@ int ueventd_main(int argc, char** argv) {
std::unique_ptr<DeviceHandler> device_handler = std::make_unique<DeviceHandler>(
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;

View file

@ -264,6 +264,8 @@ UeventdConfiguration ParseConfig(const std::vector<std::string>& configs) {
parser.AddSectionParser("import", std::make_unique<ImportParser>(&parser));
parser.AddSectionParser("subsystem",
std::make_unique<SubsystemParser>(&ueventd_configuration.subsystems));
parser.AddSectionParser("driver",
std::make_unique<SubsystemParser>(&ueventd_configuration.drivers));
using namespace std::placeholders;
parser.AddSingleLineParser(

View file

@ -27,6 +27,7 @@ namespace init {
struct UeventdConfiguration {
std::vector<Subsystem> subsystems;
std::vector<Subsystem> drivers;
std::vector<SysfsPermissions> sysfs_permissions;
std::vector<Permissions> dev_permissions;
std::vector<std::string> firmware_directories;

View file

@ -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<Subsystem>{
{"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<Subsystem>{
{"d_test_devpath", Subsystem::DEVNAME_UEVENT_DEVPATH, "/dev"},
{"d_test_devname_dirname", Subsystem::DEVNAME_UEVENT_DEVNAME, "/dev/graphics"}};
auto permissions = std::vector<Permissions>{
{"/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});
}