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
This commit is contained in:
parent
537e4af235
commit
dcb3d15611
11 changed files with 427 additions and 39 deletions
|
@ -224,6 +224,7 @@ cc_test {
|
|||
|
||||
srcs: [
|
||||
"devices_test.cpp",
|
||||
"firmware_handler_test.cpp",
|
||||
"init_test.cpp",
|
||||
"keychords_test.cpp",
|
||||
"persistent_properties_test.cpp",
|
||||
|
|
|
@ -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 <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
|
||||
|
|
|
@ -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>
|
||||
|
@ -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.
|
||||
|
@ -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 +
|
||||
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
125
init/firmware_handler_test.cpp
Normal file
125
init/firmware_handler_test.cpp
Normal file
|
@ -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 <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;
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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<FirmwareHandler>(
|
||||
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<std::string> base_paths = {"/odm/lib/modules", "/vendor/lib/modules"};
|
||||
|
|
|
@ -88,6 +88,31 @@ Result<void> ParseFirmwareDirectoriesLine(std::vector<std::string>&& args,
|
|||
return {};
|
||||
}
|
||||
|
||||
Result<void> ParseExternalFirmwareHandlerLine(
|
||||
std::vector<std::string>&& args,
|
||||
std::vector<ExternalFirmwareHandler>* 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<void> ParseEnabledDisabledLine(std::vector<std::string>&& 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<std::string>& 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));
|
||||
|
|
|
@ -20,6 +20,7 @@
|
|||
#include <vector>
|
||||
|
||||
#include "devices.h"
|
||||
#include "firmware_handler.h"
|
||||
|
||||
namespace android {
|
||||
namespace init {
|
||||
|
@ -29,6 +30,7 @@ struct UeventdConfiguration {
|
|||
std::vector<SysfsPermissions> sysfs_permissions;
|
||||
std::vector<Permissions> dev_permissions;
|
||||
std::vector<std::string> firmware_directories;
|
||||
std::vector<ExternalFirmwareHandler> external_firmware_handlers;
|
||||
bool enable_modalias_handling = false;
|
||||
size_t uevent_socket_rcvbuf_size = 0;
|
||||
bool enable_parallel_restorecon = false;
|
||||
|
|
|
@ -20,6 +20,8 @@
|
|||
#include <gtest/gtest.h>
|
||||
#include <private/android_filesystem_config.h>
|
||||
|
||||
#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<ExternalFirmwareHandler>{
|
||||
{
|
||||
"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<ExternalFirmwareHandler>{
|
||||
{
|
||||
"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<ExternalFirmwareHandler>{
|
||||
{"/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, {});
|
||||
|
|
Loading…
Reference in a new issue