Merge "ueventd: allow using external firmware handlers"
This commit is contained in:
commit
385aeb2dda
|
@ -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
|
||||
|
|
|
@ -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 New Issue