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:
parent
d68632becc
commit
d17d5c585e
10 changed files with 151 additions and 23 deletions
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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(
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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});
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue