Merge "init: add ctl.oneshot_on/ctl.oneshot_off"
This commit is contained in:
commit
321151247c
5 changed files with 113 additions and 61 deletions
|
@ -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",
|
||||
|
|
|
@ -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.
|
||||
|
||||
|
|
|
@ -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
44
init/oneshot_on_test.cpp
Normal 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));
|
||||
}
|
|
@ -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;
|
||||
|
|
Loading…
Reference in a new issue