Loading init/Android.bp +1 −0 Original line number Diff line number Diff line Loading @@ -224,6 +224,7 @@ cc_test { srcs: [ "devices_test.cpp", "firmware_handler_test.cpp", "init_test.cpp", "keychords_test.cpp", "persistent_properties_test.cpp", Loading init/README.ueventd.md +21 −1 Original line number Diff line number Diff line Loading @@ -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. Loading @@ -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 <devpath> <user name to run as> <path to external program> 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 Loading init/firmware_handler.cpp +139 −15 Original line number Diff line number Diff line Loading @@ -17,6 +17,10 @@ #include "firmware_handler.h" #include <fcntl.h> #include <pwd.h> #include <signal.h> #include <stdlib.h> #include <string.h> #include <sys/sendfile.h> #include <sys/wait.h> #include <unistd.h> Loading @@ -26,25 +30,29 @@ #include <android-base/chrono_utils.h> #include <android-base/file.h> #include <android-base/logging.h> #include <android-base/strings.h> #include <android-base/unique_fd.h> 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. Loading @@ -56,36 +64,151 @@ static bool IsBooting() { return access("/dev/.booting", F_OK) == 0; } FirmwareHandler::FirmwareHandler(std::vector<std::string> firmware_directories) : firmware_directories_(std::move(firmware_directories)) {} FirmwareHandler::FirmwareHandler(std::vector<std::string> firmware_directories, std::vector<ExternalFirmwareHandler> 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<std::string> 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<char*> 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<std::string> 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 + Loading @@ -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; } Loading @@ -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; } Loading @@ -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); } Loading init/firmware_handler.h +22 −6 Original line number Diff line number Diff line Loading @@ -14,32 +14,48 @@ * limitations under the License. */ #ifndef _INIT_FIRMWARE_HANDLER_H #define _INIT_FIRMWARE_HANDLER_H #pragma once #include <pwd.h> #include <string> #include <vector> #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<std::string> firmware_directories); FirmwareHandler(std::vector<std::string> firmware_directories, std::vector<ExternalFirmwareHandler> 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<std::string> 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<std::string> firmware_directories_; std::vector<ExternalFirmwareHandler> external_firmware_handlers_; }; } // namespace init } // namespace android #endif init/firmware_handler_test.cpp 0 → 100644 +125 −0 Original line number Diff line number Diff line /* * 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 <stdlib.h> #include <iostream> #include <android-base/file.h> #include <gtest/gtest.h> #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; } Loading
init/Android.bp +1 −0 Original line number Diff line number Diff line Loading @@ -224,6 +224,7 @@ cc_test { srcs: [ "devices_test.cpp", "firmware_handler_test.cpp", "init_test.cpp", "keychords_test.cpp", "persistent_properties_test.cpp", Loading
init/README.ueventd.md +21 −1 Original line number Diff line number Diff line Loading @@ -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. Loading @@ -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 <devpath> <user name to run as> <path to external program> 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 Loading
init/firmware_handler.cpp +139 −15 Original line number Diff line number Diff line Loading @@ -17,6 +17,10 @@ #include "firmware_handler.h" #include <fcntl.h> #include <pwd.h> #include <signal.h> #include <stdlib.h> #include <string.h> #include <sys/sendfile.h> #include <sys/wait.h> #include <unistd.h> Loading @@ -26,25 +30,29 @@ #include <android-base/chrono_utils.h> #include <android-base/file.h> #include <android-base/logging.h> #include <android-base/strings.h> #include <android-base/unique_fd.h> 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. Loading @@ -56,36 +64,151 @@ static bool IsBooting() { return access("/dev/.booting", F_OK) == 0; } FirmwareHandler::FirmwareHandler(std::vector<std::string> firmware_directories) : firmware_directories_(std::move(firmware_directories)) {} FirmwareHandler::FirmwareHandler(std::vector<std::string> firmware_directories, std::vector<ExternalFirmwareHandler> 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<std::string> 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<char*> 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<std::string> 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 + Loading @@ -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; } Loading @@ -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; } Loading @@ -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); } Loading
init/firmware_handler.h +22 −6 Original line number Diff line number Diff line Loading @@ -14,32 +14,48 @@ * limitations under the License. */ #ifndef _INIT_FIRMWARE_HANDLER_H #define _INIT_FIRMWARE_HANDLER_H #pragma once #include <pwd.h> #include <string> #include <vector> #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<std::string> firmware_directories); FirmwareHandler(std::vector<std::string> firmware_directories, std::vector<ExternalFirmwareHandler> 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<std::string> 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<std::string> firmware_directories_; std::vector<ExternalFirmwareHandler> external_firmware_handlers_; }; } // namespace init } // namespace android #endif
init/firmware_handler_test.cpp 0 → 100644 +125 −0 Original line number Diff line number Diff line /* * 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 <stdlib.h> #include <iostream> #include <android-base/file.h> #include <gtest/gtest.h> #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; }