From dcb3d156119d12f067474f4dc12e026c7fbd4ae3 Mon Sep 17 00:00:00 2001 From: Tom Cherry Date: Wed, 7 Aug 2019 16:02:28 -0700 Subject: [PATCH] ueventd: allow using external firmware handlers Userspace may want to load a different firmware than the one that the kernel requests in some cases, therefore this change adds the ability to ueventd to run an external handler that will determine the name of the file that should actually be loaded. Bug: 138352500 Test: unit tests Change-Id: Ic5da37268fd78109f83ae52d1b903bf7322a5ee5 --- init/Android.bp | 1 + init/README.ueventd.md | 22 ++++- init/firmware_handler.cpp | 154 +++++++++++++++++++++++++++++---- init/firmware_handler.h | 28 ++++-- init/firmware_handler_test.cpp | 125 ++++++++++++++++++++++++++ init/init_test.cpp | 16 ++++ init/subcontext_test.cpp | 12 +-- init/ueventd.cpp | 3 +- init/ueventd_parser.cpp | 28 ++++++ init/ueventd_parser.h | 2 + init/ueventd_parser_test.cpp | 75 ++++++++++++++-- 11 files changed, 427 insertions(+), 39 deletions(-) create mode 100644 init/firmware_handler_test.cpp diff --git a/init/Android.bp b/init/Android.bp index 9c4b5b994..97140146c 100644 --- a/init/Android.bp +++ b/init/Android.bp @@ -224,6 +224,7 @@ cc_test { srcs: [ "devices_test.cpp", + "firmware_handler_test.cpp", "init_test.cpp", "keychords_test.cpp", "persistent_properties_test.cpp", diff --git a/init/README.ueventd.md b/init/README.ueventd.md index 7e90e0cce..053ebf813 100644 --- a/init/README.ueventd.md +++ b/init/README.ueventd.md @@ -82,7 +82,7 @@ Note that `*` matches as a wildcard and can be used anywhere in a path. ## Firmware loading ---------------- -Ueventd automatically serves firmware requests by searching through a list of firmware directories +Ueventd by default serves firmware requests by searching through a list of firmware directories for a file matching the uevent `FIRMWARE`. It then forks a process to serve this firmware to the kernel. @@ -100,6 +100,26 @@ entries. Ueventd will wait until after `post-fs` in init, to keep retrying before believing the firmwares are not present. +The exact firmware file to be served can be customized by running an external program by a +`external_firmware_handler` line in a ueventd.rc file. This line takes the format of + + external_firmware_handler +For example + + external_firmware_handler /devices/leds/red/firmware/coeffs.bin system /vendor/bin/led_coeffs.bin +Will launch `/vendor/bin/led_coeffs.bin` as the system user instead of serving the default firmware +for `/devices/leds/red/firmware/coeffs.bin`. + +Ueventd will provide the uevent `DEVPATH` and `FIRMWARE` to this external program on the environment +via environment variables with the same names. Ueventd will use the string written to stdout as the +new name of the firmware to load. It will still look for the new firmware in the list of firmware +directories stated above. It will also reject file names with `..` in them, to prevent leaving these +directories. If stdout cannot be read, or the program returns with any exit code other than +`EXIT_SUCCESS`, or the program crashes, the default firmware from the uevent will be loaded. + +Ueventd will additionally log all messages sent to stderr from the external program to the serial +console after the external program has exited. + ## Coldboot -------- Ueventd must create devices in `/dev` for all devices that have already sent their uevents before diff --git a/init/firmware_handler.cpp b/init/firmware_handler.cpp index c067f6f2d..1dce2d507 100644 --- a/init/firmware_handler.cpp +++ b/init/firmware_handler.cpp @@ -17,6 +17,10 @@ #include "firmware_handler.h" #include +#include +#include +#include +#include #include #include #include @@ -26,25 +30,29 @@ #include #include #include +#include #include +using android::base::ReadFdToString; +using android::base::Socketpair; +using android::base::Split; using android::base::Timer; +using android::base::Trim; using android::base::unique_fd; using android::base::WriteFully; namespace android { namespace init { -static void LoadFirmware(const Uevent& uevent, const std::string& root, int fw_fd, size_t fw_size, - int loading_fd, int data_fd) { +static void LoadFirmware(const std::string& firmware, const std::string& root, int fw_fd, + size_t fw_size, int loading_fd, int data_fd) { // Start transfer. 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 - << "' }"; + PLOG(ERROR) << "firmware: sendfile failed { '" << root << "', '" << firmware << "' }"; } // Tell the firmware whether to abort or commit. @@ -56,36 +64,151 @@ static bool IsBooting() { return access("/dev/.booting", F_OK) == 0; } -FirmwareHandler::FirmwareHandler(std::vector firmware_directories) - : firmware_directories_(std::move(firmware_directories)) {} +FirmwareHandler::FirmwareHandler(std::vector firmware_directories, + std::vector external_firmware_handlers) + : firmware_directories_(std::move(firmware_directories)), + external_firmware_handlers_(std::move(external_firmware_handlers)) {} -void FirmwareHandler::ProcessFirmwareEvent(const Uevent& uevent) { - int booting = IsBooting(); +Result FirmwareHandler::RunExternalHandler(const std::string& handler, uid_t uid, + const Uevent& uevent) const { + unique_fd child_stdout; + unique_fd parent_stdout; + if (!Socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, &child_stdout, &parent_stdout)) { + return ErrnoError() << "Socketpair() for stdout failed"; + } + unique_fd child_stderr; + unique_fd parent_stderr; + if (!Socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, &child_stderr, &parent_stderr)) { + return ErrnoError() << "Socketpair() for stderr failed"; + } + + signal(SIGCHLD, SIG_DFL); + + auto pid = fork(); + if (pid < 0) { + return ErrnoError() << "fork() failed"; + } + + if (pid == 0) { + setenv("FIRMWARE", uevent.firmware.c_str(), 1); + setenv("DEVPATH", uevent.path.c_str(), 1); + parent_stdout.reset(); + parent_stderr.reset(); + close(STDOUT_FILENO); + close(STDERR_FILENO); + dup2(child_stdout.get(), STDOUT_FILENO); + dup2(child_stderr.get(), STDERR_FILENO); + + auto args = Split(handler, " "); + std::vector c_args; + for (auto& arg : args) { + c_args.emplace_back(arg.data()); + } + c_args.emplace_back(nullptr); + + if (setuid(uid) != 0) { + fprintf(stderr, "setuid() failed: %s", strerror(errno)); + _exit(EXIT_FAILURE); + } + + execv(c_args[0], c_args.data()); + fprintf(stderr, "exec() failed: %s", strerror(errno)); + _exit(EXIT_FAILURE); + } + + child_stdout.reset(); + child_stderr.reset(); + + int status; + pid_t waited_pid = TEMP_FAILURE_RETRY(waitpid(pid, &status, 0)); + if (waited_pid == -1) { + return ErrnoError() << "waitpid() failed"; + } + + std::string stdout_content; + if (!ReadFdToString(parent_stdout.get(), &stdout_content)) { + return ErrnoError() << "ReadFdToString() for stdout failed"; + } + + std::string stderr_content; + if (ReadFdToString(parent_stderr.get(), &stderr_content)) { + auto messages = Split(stderr_content, "\n"); + for (const auto& message : messages) { + if (!message.empty()) { + LOG(ERROR) << "External Firmware Handler: " << message; + } + } + } else { + LOG(ERROR) << "ReadFdToString() for stderr failed"; + } + + if (WIFEXITED(status)) { + if (WEXITSTATUS(status) == EXIT_SUCCESS) { + return Trim(stdout_content); + } else { + return Error() << "exited with status " << WEXITSTATUS(status); + } + } else if (WIFSIGNALED(status)) { + return Error() << "killed by signal " << WTERMSIG(status); + } + + return Error() << "unexpected exit status " << status; +} + +std::string FirmwareHandler::GetFirmwarePath(const Uevent& uevent) const { + for (const auto& external_handler : external_firmware_handlers_) { + if (external_handler.devpath == uevent.path) { + LOG(INFO) << "Launching external firmware handler '" << external_handler.handler_path + << "' for devpath: '" << uevent.path << "' firmware: '" << uevent.firmware + << "'"; + + auto result = + RunExternalHandler(external_handler.handler_path, external_handler.uid, uevent); + if (!result) { + LOG(ERROR) << "Using default firmware; External firmware handler failed: " + << result.error(); + return uevent.firmware; + } + if (result->find("..") != std::string::npos) { + LOG(ERROR) << "Using default firmware; External firmware handler provided an " + "invalid path, '" + << *result << "'"; + return uevent.firmware; + } + LOG(INFO) << "Loading firmware '" << *result << "' in place of '" << uevent.firmware + << "'"; + return *result; + } + } LOG(INFO) << "firmware: loading '" << uevent.firmware << "' for '" << uevent.path << "'"; + return uevent.firmware; +} - std::string root = "/sys" + uevent.path; +void FirmwareHandler::ProcessFirmwareEvent(const std::string& root, + const std::string& firmware) const { std::string loading = root + "/loading"; std::string data = root + "/data"; 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; + PLOG(ERROR) << "couldn't open firmware loading fd for " << firmware; return; } 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; + PLOG(ERROR) << "couldn't open firmware data fd for " << firmware; return; } std::vector attempted_paths_and_errors; + int booting = IsBooting(); try_loading_again: attempted_paths_and_errors.clear(); for (const auto& firmware_directory : firmware_directories_) { - std::string file = firmware_directory + uevent.firmware; + std::string file = firmware_directory + firmware; unique_fd fw_fd(open(file.c_str(), O_RDONLY | O_CLOEXEC)); if (fw_fd == -1) { attempted_paths_and_errors.emplace_back("firmware: attempted " + file + @@ -98,7 +221,7 @@ try_loading_again: ", fstat failed: " + strerror(errno)); continue; } - LoadFirmware(uevent, root, fw_fd, sb.st_size, loading_fd, data_fd); + LoadFirmware(firmware, root, fw_fd, sb.st_size, loading_fd, data_fd); return; } @@ -110,7 +233,7 @@ try_loading_again: goto try_loading_again; } - LOG(ERROR) << "firmware: could not find firmware for " << uevent.firmware; + LOG(ERROR) << "firmware: could not find firmware for " << firmware; for (const auto& message : attempted_paths_and_errors) { LOG(ERROR) << message; } @@ -129,7 +252,8 @@ void FirmwareHandler::HandleUevent(const Uevent& uevent) { } if (pid == 0) { Timer t; - ProcessFirmwareEvent(uevent); + auto firmware = GetFirmwarePath(uevent); + ProcessFirmwareEvent("/sys" + uevent.path, firmware); LOG(INFO) << "loading " << uevent.path << " took " << t; _exit(EXIT_SUCCESS); } diff --git a/init/firmware_handler.h b/init/firmware_handler.h index 399609693..b4138f127 100644 --- a/init/firmware_handler.h +++ b/init/firmware_handler.h @@ -14,32 +14,48 @@ * limitations under the License. */ -#ifndef _INIT_FIRMWARE_HANDLER_H -#define _INIT_FIRMWARE_HANDLER_H +#pragma once + +#include #include #include +#include "result.h" #include "uevent.h" #include "uevent_handler.h" namespace android { namespace init { +struct ExternalFirmwareHandler { + ExternalFirmwareHandler(std::string devpath, uid_t uid, std::string handler_path) + : devpath(std::move(devpath)), uid(uid), handler_path(std::move(handler_path)) {} + std::string devpath; + uid_t uid; + std::string handler_path; +}; + class FirmwareHandler : public UeventHandler { public: - explicit FirmwareHandler(std::vector firmware_directories); + FirmwareHandler(std::vector firmware_directories, + std::vector external_firmware_handlers); virtual ~FirmwareHandler() = default; void HandleUevent(const Uevent& uevent) override; private: - void ProcessFirmwareEvent(const Uevent& uevent); + friend void FirmwareTestWithExternalHandler(const std::string& test_name, + bool expect_new_firmware); + + Result RunExternalHandler(const std::string& handler, uid_t uid, + const Uevent& uevent) const; + std::string GetFirmwarePath(const Uevent& uevent) const; + void ProcessFirmwareEvent(const std::string& root, const std::string& firmware) const; std::vector firmware_directories_; + std::vector external_firmware_handlers_; }; } // namespace init } // namespace android - -#endif diff --git a/init/firmware_handler_test.cpp b/init/firmware_handler_test.cpp new file mode 100644 index 000000000..7bb603c5d --- /dev/null +++ b/init/firmware_handler_test.cpp @@ -0,0 +1,125 @@ +/* + * Copyright (C) 2019 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 +#include + +#include +#include + +#include "uevent.h" + +using android::base::GetExecutablePath; +using namespace std::literals; + +namespace android { +namespace init { + +void FirmwareTestWithExternalHandler(const std::string& test_name, bool expect_new_firmware) { + auto test_path = GetExecutablePath() + " firmware " + test_name; + auto external_firmware_handler = ExternalFirmwareHandler( + "/devices/led/firmware/test_firmware001.bin", getuid(), test_path); + + auto firmware_handler = FirmwareHandler({"/test"}, {external_firmware_handler}); + + auto uevent = Uevent{ + .path = "/devices/led/firmware/test_firmware001.bin", + .firmware = "test_firmware001.bin", + }; + + if (expect_new_firmware) { + EXPECT_EQ("other_firmware001.bin", firmware_handler.GetFirmwarePath(uevent)); + } else { + EXPECT_EQ("test_firmware001.bin", firmware_handler.GetFirmwarePath(uevent)); + } + + // Always test the base case that the handler isn't invoked if the devpath doesn't match. + auto uevent_different_path = Uevent{ + .path = "/devices/led/not/mine", + .firmware = "test_firmware001.bin", + }; + EXPECT_EQ("test_firmware001.bin", firmware_handler.GetFirmwarePath(uevent_different_path)); +} + +TEST(firmware_handler, HandleChange) { + FirmwareTestWithExternalHandler("HandleChange", true); +} + +int HandleChange(int argc, char** argv) { + // Assert that the environment is set up correctly. + if (getenv("DEVPATH") != "/devices/led/firmware/test_firmware001.bin"s) { + std::cerr << "$DEVPATH not set correctly" << std::endl; + return EXIT_FAILURE; + } + if (getenv("FIRMWARE") != "test_firmware001.bin"s) { + std::cerr << "$FIRMWARE not set correctly" << std::endl; + return EXIT_FAILURE; + } + std::cout << "other_firmware001.bin" << std::endl; + return 0; +} + +TEST(firmware_handler, HandleAbort) { + FirmwareTestWithExternalHandler("HandleAbort", false); +} + +int HandleAbort(int argc, char** argv) { + abort(); + return 0; +} + +TEST(firmware_handler, HandleFailure) { + FirmwareTestWithExternalHandler("HandleFailure", false); +} + +int HandleFailure(int argc, char** argv) { + std::cerr << "Failed" << std::endl; + return EXIT_FAILURE; +} + +TEST(firmware_handler, HandleBadPath) { + FirmwareTestWithExternalHandler("HandleBadPath", false); +} + +int HandleBadPath(int argc, char** argv) { + std::cout << "../firmware.bin"; + return 0; +} + +} // namespace init +} // namespace android + +// init_test.cpp contains the main entry point for all init tests. +int FirmwareTestChildMain(int argc, char** argv) { + if (argc < 3) { + return 1; + } + +#define RunTest(testname) \ + if (argv[2] == std::string(#testname)) { \ + return android::init::testname(argc, argv); \ + } + + RunTest(HandleChange); + RunTest(HandleAbort); + RunTest(HandleFailure); + RunTest(HandleBadPath); + +#undef RunTest + return 1; +} diff --git a/init/init_test.cpp b/init/init_test.cpp index 0411214a7..315d584be 100644 --- a/init/init_test.cpp +++ b/init/init_test.cpp @@ -221,3 +221,19 @@ TEST(init, EventTriggerOrderMultipleFiles) { } // namespace init } // namespace android + +int SubcontextTestChildMain(int, char**); +int FirmwareTestChildMain(int, char**); + +int main(int argc, char** argv) { + if (argc > 1 && !strcmp(argv[1], "subcontext")) { + return SubcontextTestChildMain(argc, argv); + } + + if (argc > 1 && !strcmp(argv[1], "firmware")) { + return FirmwareTestChildMain(argc, argv); + } + + testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} diff --git a/init/subcontext_test.cpp b/init/subcontext_test.cpp index dcbff8259..7565eb670 100644 --- a/init/subcontext_test.cpp +++ b/init/subcontext_test.cpp @@ -224,12 +224,8 @@ BuiltinFunctionMap BuildTestFunctionMap() { } // namespace init } // namespace android -int main(int argc, char** argv) { - if (argc > 1 && !strcmp(basename(argv[1]), "subcontext")) { - auto test_function_map = android::init::BuildTestFunctionMap(); - return android::init::SubcontextMain(argc, argv, &test_function_map); - } - - testing::InitGoogleTest(&argc, argv); - return RUN_ALL_TESTS(); +// init_test.cpp contains the main entry point for all init tests. +int SubcontextTestChildMain(int argc, char** argv) { + auto test_function_map = android::init::BuildTestFunctionMap(); + return android::init::SubcontextMain(argc, argv, &test_function_map); } diff --git a/init/ueventd.cpp b/init/ueventd.cpp index 6741e2a33..59f91ee16 100644 --- a/init/ueventd.cpp +++ b/init/ueventd.cpp @@ -296,7 +296,8 @@ int ueventd_main(int argc, char** argv) { std::move(ueventd_configuration.sysfs_permissions), std::move(ueventd_configuration.subsystems), android::fs_mgr::GetBootDevices(), true)); uevent_handlers.emplace_back(std::make_unique( - std::move(ueventd_configuration.firmware_directories))); + std::move(ueventd_configuration.firmware_directories), + std::move(ueventd_configuration.external_firmware_handlers))); if (ueventd_configuration.enable_modalias_handling) { std::vector base_paths = {"/odm/lib/modules", "/vendor/lib/modules"}; diff --git a/init/ueventd_parser.cpp b/init/ueventd_parser.cpp index 1ca1715a7..a74b2472f 100644 --- a/init/ueventd_parser.cpp +++ b/init/ueventd_parser.cpp @@ -88,6 +88,31 @@ Result ParseFirmwareDirectoriesLine(std::vector&& args, return {}; } +Result ParseExternalFirmwareHandlerLine( + std::vector&& args, + std::vector* external_firmware_handlers) { + if (args.size() != 4) { + return Error() << "external_firmware_handler lines must have exactly 3 parameters"; + } + + if (std::find_if(external_firmware_handlers->begin(), external_firmware_handlers->end(), + [&args](const auto& other) { return other.devpath == args[2]; }) != + external_firmware_handlers->end()) { + return Error() << "found a previous external_firmware_handler with the same devpath, '" + << args[2] << "'"; + } + + passwd* pwd = getpwnam(args[2].c_str()); + if (!pwd) { + return ErrnoError() << "invalid handler uid'" << args[2] << "'"; + } + + ExternalFirmwareHandler handler(std::move(args[1]), pwd->pw_uid, std::move(args[3])); + external_firmware_handlers->emplace_back(std::move(handler)); + + return {}; +} + Result ParseEnabledDisabledLine(std::vector&& args, bool* feature) { if (args.size() != 2) { return Error() << args[0] << " lines take exactly one parameter"; @@ -211,6 +236,9 @@ UeventdConfiguration ParseConfig(const std::vector& configs) { parser.AddSingleLineParser("firmware_directories", std::bind(ParseFirmwareDirectoriesLine, _1, &ueventd_configuration.firmware_directories)); + parser.AddSingleLineParser("external_firmware_handler", + std::bind(ParseExternalFirmwareHandlerLine, _1, + &ueventd_configuration.external_firmware_handlers)); parser.AddSingleLineParser("modalias_handling", std::bind(ParseEnabledDisabledLine, _1, &ueventd_configuration.enable_modalias_handling)); diff --git a/init/ueventd_parser.h b/init/ueventd_parser.h index b54dba88e..eaafa5aa7 100644 --- a/init/ueventd_parser.h +++ b/init/ueventd_parser.h @@ -20,6 +20,7 @@ #include #include "devices.h" +#include "firmware_handler.h" namespace android { namespace init { @@ -29,6 +30,7 @@ struct UeventdConfiguration { std::vector sysfs_permissions; std::vector dev_permissions; std::vector firmware_directories; + std::vector external_firmware_handlers; bool enable_modalias_handling = false; size_t uevent_socket_rcvbuf_size = 0; bool enable_parallel_restorecon = false; diff --git a/init/ueventd_parser_test.cpp b/init/ueventd_parser_test.cpp index 885e79ddd..172ba0b38 100644 --- a/init/ueventd_parser_test.cpp +++ b/init/ueventd_parser_test.cpp @@ -20,6 +20,8 @@ #include #include +#include "firmware_handler.h" + namespace android { namespace init { @@ -93,7 +95,7 @@ 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, Permissions) { @@ -119,7 +121,7 @@ TEST(ueventd_parser, Permissions) { {"/sys/devices/virtual/*/input", "poll_delay", 0660, AID_ROOT, AID_INPUT}, }; - TestUeventdFile(ueventd_file, {{}, sysfs_permissions, permissions, {}}); + TestUeventdFile(ueventd_file, {{}, sysfs_permissions, permissions, {}, {}}); } TEST(ueventd_parser, FirmwareDirectories) { @@ -135,7 +137,52 @@ firmware_directories /more "/more", }; - TestUeventdFile(ueventd_file, {{}, {}, {}, firmware_directories}); + TestUeventdFile(ueventd_file, {{}, {}, {}, firmware_directories, {}}); +} + +TEST(ueventd_parser, ExternalFirmwareHandlers) { + auto ueventd_file = R"( +external_firmware_handler devpath root handler_path +external_firmware_handler /devices/path/firmware/something001.bin system /vendor/bin/firmware_handler.sh +external_firmware_handler /devices/path/firmware/something001.bin radio "/vendor/bin/firmware_handler.sh --has --arguments" +)"; + + auto external_firmware_handlers = std::vector{ + { + "devpath", + AID_ROOT, + "handler_path", + }, + { + "/devices/path/firmware/something001.bin", + AID_SYSTEM, + "/vendor/bin/firmware_handler.sh", + }, + { + "/devices/path/firmware/something001.bin", + AID_RADIO, + "/vendor/bin/firmware_handler.sh --has --arguments", + }, + }; + + TestUeventdFile(ueventd_file, {{}, {}, {}, {}, external_firmware_handlers}); +} + +TEST(ueventd_parser, ExternalFirmwareHandlersDuplicate) { + auto ueventd_file = R"( +external_firmware_handler devpath root handler_path +external_firmware_handler devpath root handler_path2 +)"; + + auto external_firmware_handlers = std::vector{ + { + "devpath", + AID_ROOT, + "handler_path", + }, + }; + + TestUeventdFile(ueventd_file, {{}, {}, {}, {}, external_firmware_handlers}); } TEST(ueventd_parser, UeventSocketRcvbufSize) { @@ -144,7 +191,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) { @@ -154,7 +201,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 @@ -162,7 +209,7 @@ modalias_handling enabled parallel_restorecon disabled )"; - TestUeventdFile(ueventd_file2, {{}, {}, {}, {}, true, 0, false}); + TestUeventdFile(ueventd_file2, {{}, {}, {}, {}, {}, true, 0, false}); } TEST(ueventd_parser, AllTogether) { @@ -196,6 +243,8 @@ subsystem test_devpath_dirname /sys/devices/virtual/*/input poll_delay 0660 root input firmware_directories /more +external_firmware_handler /devices/path/firmware/firmware001.bin root /vendor/bin/touch.sh + uevent_socket_rcvbuf_size 6M modalias_handling enabled parallel_restorecon enabled @@ -228,10 +277,15 @@ parallel_restorecon enabled "/more", }; + auto external_firmware_handlers = std::vector{ + {"/devices/path/firmware/firmware001.bin", AID_ROOT, "/vendor/bin/touch.sh"}, + }; + size_t uevent_socket_rcvbuf_size = 6 * 1024 * 1024; - TestUeventdFile(ueventd_file, {subsystems, sysfs_permissions, permissions, firmware_directories, - true, uevent_socket_rcvbuf_size, true}); + TestUeventdFile(ueventd_file, + {subsystems, sysfs_permissions, permissions, firmware_directories, + external_firmware_handlers, true, uevent_socket_rcvbuf_size, true}); } // All of these lines are ill-formed, so test that there is 0 output. @@ -257,6 +311,11 @@ modalias_handling blah parallel_restorecon parallel_restorecon enabled enabled parallel_restorecon blah + +external_firmware_handler +external_firmware_handler blah blah +external_firmware_handler blah blah blah blah + )"; TestUeventdFile(ueventd_file, {});