Merge "init: add ctl.oneshot_on/ctl.oneshot_off"

This commit is contained in:
Tom Cherry 2020-03-20 20:57:18 +00:00 committed by Gerrit Code Review
commit 321151247c
5 changed files with 113 additions and 61 deletions

View file

@ -240,6 +240,7 @@ cc_test {
"firmware_handler_test.cpp",
"init_test.cpp",
"keychords_test.cpp",
"oneshot_on_test.cpp",
"persistent_properties_test.cpp",
"property_service_test.cpp",
"property_type_test.cpp",

View file

@ -720,23 +720,35 @@ Init provides state information with the following properties.
characteristics in a device agnostic manner.
Init responds to properties that begin with `ctl.`. These properties take the format of
`ctl.<command>` and the _value_ of the system property is used as a parameter, for example:
`SetProperty("ctl.start", "logd")` will run the `start` command on `logd`. Note that these
`ctl.[<target>_]<command>` and the _value_ of the system property is used as a parameter. The
_target_ is optional and specifies the service option that _value_ is meant to match with. There is
only one option for _target_, `interface` which indicates that _value_ will refer to an interface
that a service provides and not the service name itself.
For example:
`SetProperty("ctl.start", "logd")` will run the `start` command on `logd`.
`SetProperty("ctl.interface_start", "aidl/aidl_lazy_test_1")` will run the `start` command on the
service that exposes the `aidl aidl_lazy_test_1` interface.
Note that these
properties are only settable; they will have no value when read.
`ctl.start` \
`ctl.restart` \
`ctl.stop`
> These are equivalent to using the `start`, `restart`, and `stop` commands on the service specified
The _commands_ are listed below.
`start` \
`restart` \
`stop` \
These are equivalent to using the `start`, `restart`, and `stop` commands on the service specified
by the _value_ of the property.
`ctl.interface_start` \
`ctl.interface_restart` \
`ctl.interface_stop`
> These are equivalent to using the `interface_start`, `interface_restart`, and `interface_stop`
commands on the interface specified by the _value_ of the property.
`oneshot_one` and `oneshot_off` will turn on or off the _oneshot_
flag for the service specified by the _value_ of the property. This is
particularly intended for services that are conditionally lazy HALs. When
they are lazy HALs, oneshot must be on, otherwise oneshot should be off.
`ctl.sigstop_on` and `ctl.sigstop_off` will turn on or off the _sigstop_ feature for the service
`sigstop_on` and `sigstop_off` will turn on or off the _sigstop_ feature for the service
specified by the _value_ of the property. See the _Debugging init_ section below for more details
about this feature.

View file

@ -81,6 +81,7 @@ using namespace std::chrono_literals;
using namespace std::string_literals;
using android::base::boot_clock;
using android::base::ConsumePrefix;
using android::base::GetProperty;
using android::base::ReadFileToString;
using android::base::SetProperty;
@ -367,40 +368,27 @@ enum class ControlTarget {
INTERFACE, // action gets called for every service that holds this interface
};
struct ControlMessageFunction {
ControlTarget target;
std::function<Result<void>(Service*)> action;
};
using ControlMessageFunction = std::function<Result<void>(Service*)>;
static const std::map<std::string, ControlMessageFunction>& get_control_message_map() {
static const std::map<std::string, ControlMessageFunction, std::less<>>& GetControlMessageMap() {
// clang-format off
static const std::map<std::string, ControlMessageFunction> control_message_functions = {
{"sigstop_on", {ControlTarget::SERVICE,
[](auto* service) { service->set_sigstop(true); return Result<void>{}; }}},
{"sigstop_off", {ControlTarget::SERVICE,
[](auto* service) { service->set_sigstop(false); return Result<void>{}; }}},
{"start", {ControlTarget::SERVICE, DoControlStart}},
{"stop", {ControlTarget::SERVICE, DoControlStop}},
{"restart", {ControlTarget::SERVICE, DoControlRestart}},
{"interface_start", {ControlTarget::INTERFACE, DoControlStart}},
{"interface_stop", {ControlTarget::INTERFACE, DoControlStop}},
{"interface_restart", {ControlTarget::INTERFACE, DoControlRestart}},
static const std::map<std::string, ControlMessageFunction, std::less<>> control_message_functions = {
{"sigstop_on", [](auto* service) { service->set_sigstop(true); return Result<void>{}; }},
{"sigstop_off", [](auto* service) { service->set_sigstop(false); return Result<void>{}; }},
{"oneshot_on", [](auto* service) { service->set_oneshot(true); return Result<void>{}; }},
{"oneshot_off", [](auto* service) { service->set_oneshot(false); return Result<void>{}; }},
{"start", DoControlStart},
{"stop", DoControlStop},
{"restart", DoControlRestart},
};
// clang-format on
return control_message_functions;
}
bool HandleControlMessage(const std::string& msg, const std::string& name, pid_t pid) {
const auto& map = get_control_message_map();
const auto it = map.find(msg);
if (it == map.end()) {
LOG(ERROR) << "Unknown control msg '" << msg << "'";
return false;
}
std::string cmdline_path = StringPrintf("proc/%d/cmdline", pid);
static bool HandleControlMessage(std::string_view message, const std::string& name,
pid_t from_pid) {
std::string cmdline_path = StringPrintf("proc/%d/cmdline", from_pid);
std::string process_cmdline;
if (ReadFileToString(cmdline_path, &process_cmdline)) {
std::replace(process_cmdline.begin(), process_cmdline.end(), '\0', ' ');
@ -409,37 +397,37 @@ bool HandleControlMessage(const std::string& msg, const std::string& name, pid_t
process_cmdline = "unknown process";
}
const ControlMessageFunction& function = it->second;
Service* svc = nullptr;
switch (function.target) {
case ControlTarget::SERVICE:
svc = ServiceList::GetInstance().FindService(name);
break;
case ControlTarget::INTERFACE:
svc = ServiceList::GetInstance().FindInterface(name);
break;
default:
LOG(ERROR) << "Invalid function target from static map key ctl." << msg << ": "
<< static_cast<std::underlying_type<ControlTarget>::type>(function.target);
return false;
Service* service = nullptr;
auto action = message;
if (ConsumePrefix(&action, "interface_")) {
service = ServiceList::GetInstance().FindInterface(name);
} else {
service = ServiceList::GetInstance().FindService(name);
}
if (svc == nullptr) {
LOG(ERROR) << "Control message: Could not find '" << name << "' for ctl." << msg
<< " from pid: " << pid << " (" << process_cmdline << ")";
if (service == nullptr) {
LOG(ERROR) << "Control message: Could not find '" << name << "' for ctl." << message
<< " from pid: " << from_pid << " (" << process_cmdline << ")";
return false;
}
if (auto result = function.action(svc); !result.ok()) {
LOG(ERROR) << "Control message: Could not ctl." << msg << " for '" << name
<< "' from pid: " << pid << " (" << process_cmdline << "): " << result.error();
const auto& map = GetControlMessageMap();
const auto it = map.find(action);
if (it == map.end()) {
LOG(ERROR) << "Unknown control msg '" << message << "'";
return false;
}
const auto& function = it->second;
if (auto result = function(service); !result.ok()) {
LOG(ERROR) << "Control message: Could not ctl." << message << " for '" << name
<< "' from pid: " << from_pid << " (" << process_cmdline
<< "): " << result.error();
return false;
}
LOG(INFO) << "Control message: Processed ctl." << msg << " for '" << name
<< "' from pid: " << pid << " (" << process_cmdline << ")";
LOG(INFO) << "Control message: Processed ctl." << message << " for '" << name
<< "' from pid: " << from_pid << " (" << process_cmdline << ")";
return true;
}

44
init/oneshot_on_test.cpp Normal file
View file

@ -0,0 +1,44 @@
/*
* Copyright (C) 2020 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 <gtest/gtest.h>
#include <chrono>
#include <android-base/properties.h>
using android::base::GetProperty;
using android::base::SetProperty;
using android::base::WaitForProperty;
using namespace std::literals;
TEST(init, oneshot_on) {
// Bootanim shouldn't be running once the device has booted.
ASSERT_EQ("stopped", GetProperty("init.svc.bootanim", ""));
SetProperty("ctl.oneshot_off", "bootanim");
SetProperty("ctl.start", "bootanim");
// Bootanim exits quickly when the device is fully booted, so check that it goes back to the
// 'restarting' state that non-oneshot services enter once they've restarted.
EXPECT_TRUE(WaitForProperty("init.svc.bootanim", "restarting", 10s));
SetProperty("ctl.oneshot_on", "bootanim");
SetProperty("ctl.start", "bootanim");
// Now that oneshot is enabled again, bootanim should transition into the 'stopped' state.
EXPECT_TRUE(WaitForProperty("init.svc.bootanim", "stopped", 10s));
}

View file

@ -130,6 +130,13 @@ class Service {
bool is_updatable() const { return updatable_; }
bool is_post_data() const { return post_data_; }
bool is_from_apex() const { return from_apex_; }
void set_oneshot(bool value) {
if (value) {
flags_ |= SVC_ONESHOT;
} else {
flags_ &= ~SVC_ONESHOT;
}
}
private:
void NotifyStateChange(const std::string& new_state) const;