Loading init/Android.bp +1 −0 Original line number Original line Diff line number Diff line Loading @@ -240,6 +240,7 @@ cc_test { "firmware_handler_test.cpp", "firmware_handler_test.cpp", "init_test.cpp", "init_test.cpp", "keychords_test.cpp", "keychords_test.cpp", "oneshot_on_test.cpp", "persistent_properties_test.cpp", "persistent_properties_test.cpp", "property_service_test.cpp", "property_service_test.cpp", "property_type_test.cpp", "property_type_test.cpp", Loading init/README.md +24 −12 Original line number Original line Diff line number Diff line Loading @@ -720,23 +720,35 @@ Init provides state information with the following properties. characteristics in a device agnostic manner. characteristics in a device agnostic manner. Init responds to properties that begin with `ctl.`. These properties take the format of 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: `ctl.[<target>_]<command>` and the _value_ of the system property is used as a parameter. The `SetProperty("ctl.start", "logd")` will run the `start` command on `logd`. Note that these _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. properties are only settable; they will have no value when read. `ctl.start` \ The _commands_ are listed below. `ctl.restart` \ `ctl.stop` `start` \ > These are equivalent to using the `start`, `restart`, and `stop` commands on the service specified `restart` \ `stop` \ These are equivalent to using the `start`, `restart`, and `stop` commands on the service specified by the _value_ of the property. by the _value_ of the property. `ctl.interface_start` \ `oneshot_one` and `oneshot_off` will turn on or off the _oneshot_ `ctl.interface_restart` \ flag for the service specified by the _value_ of the property. This is `ctl.interface_stop` particularly intended for services that are conditionally lazy HALs. When > These are equivalent to using the `interface_start`, `interface_restart`, and `interface_stop` they are lazy HALs, oneshot must be on, otherwise oneshot should be off. commands on the interface specified by the _value_ of the property. `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 specified by the _value_ of the property. See the _Debugging init_ section below for more details about this feature. about this feature. Loading init/init.cpp +36 −48 Original line number Original line Diff line number Diff line Loading @@ -81,6 +81,7 @@ using namespace std::chrono_literals; using namespace std::string_literals; using namespace std::string_literals; using android::base::boot_clock; using android::base::boot_clock; using android::base::ConsumePrefix; using android::base::GetProperty; using android::base::GetProperty; using android::base::ReadFileToString; using android::base::ReadFileToString; using android::base::SetProperty; using android::base::SetProperty; Loading Loading @@ -367,40 +368,27 @@ enum class ControlTarget { INTERFACE, // action gets called for every service that holds this interface INTERFACE, // action gets called for every service that holds this interface }; }; struct ControlMessageFunction { using ControlMessageFunction = std::function<Result<void>(Service*)>; ControlTarget target; std::function<Result<void>(Service*)> action; }; static const std::map<std::string, ControlMessageFunction>& get_control_message_map() { static const std::map<std::string, ControlMessageFunction, std::less<>>& GetControlMessageMap() { // clang-format off // clang-format off static const std::map<std::string, ControlMessageFunction> control_message_functions = { static const std::map<std::string, ControlMessageFunction, std::less<>> control_message_functions = { {"sigstop_on", {ControlTarget::SERVICE, {"sigstop_on", [](auto* service) { service->set_sigstop(true); return Result<void>{}; }}, [](auto* service) { service->set_sigstop(true); return Result<void>{}; }}}, {"sigstop_off", [](auto* service) { service->set_sigstop(false); return Result<void>{}; }}, {"sigstop_off", {ControlTarget::SERVICE, {"oneshot_on", [](auto* service) { service->set_oneshot(true); return Result<void>{}; }}, [](auto* service) { service->set_sigstop(false); return Result<void>{}; }}}, {"oneshot_off", [](auto* service) { service->set_oneshot(false); return Result<void>{}; }}, {"start", {ControlTarget::SERVICE, DoControlStart}}, {"start", DoControlStart}, {"stop", {ControlTarget::SERVICE, DoControlStop}}, {"stop", DoControlStop}, {"restart", {ControlTarget::SERVICE, DoControlRestart}}, {"restart", DoControlRestart}, {"interface_start", {ControlTarget::INTERFACE, DoControlStart}}, {"interface_stop", {ControlTarget::INTERFACE, DoControlStop}}, {"interface_restart", {ControlTarget::INTERFACE, DoControlRestart}}, }; }; // clang-format on // clang-format on return control_message_functions; return control_message_functions; } } bool HandleControlMessage(const std::string& msg, const std::string& name, pid_t pid) { static bool HandleControlMessage(std::string_view message, const std::string& name, const auto& map = get_control_message_map(); pid_t from_pid) { const auto it = map.find(msg); std::string cmdline_path = StringPrintf("proc/%d/cmdline", from_pid); if (it == map.end()) { LOG(ERROR) << "Unknown control msg '" << msg << "'"; return false; } std::string cmdline_path = StringPrintf("proc/%d/cmdline", pid); std::string process_cmdline; std::string process_cmdline; if (ReadFileToString(cmdline_path, &process_cmdline)) { if (ReadFileToString(cmdline_path, &process_cmdline)) { std::replace(process_cmdline.begin(), process_cmdline.end(), '\0', ' '); std::replace(process_cmdline.begin(), process_cmdline.end(), '\0', ' '); Loading @@ -409,37 +397,37 @@ bool HandleControlMessage(const std::string& msg, const std::string& name, pid_t process_cmdline = "unknown process"; process_cmdline = "unknown process"; } } const ControlMessageFunction& function = it->second; Service* service = nullptr; auto action = message; Service* svc = nullptr; if (ConsumePrefix(&action, "interface_")) { service = ServiceList::GetInstance().FindInterface(name); } else { service = ServiceList::GetInstance().FindService(name); } switch (function.target) { if (service == nullptr) { case ControlTarget::SERVICE: LOG(ERROR) << "Control message: Could not find '" << name << "' for ctl." << message svc = ServiceList::GetInstance().FindService(name); << " from pid: " << from_pid << " (" << process_cmdline << ")"; 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; return false; } } if (svc == nullptr) { const auto& map = GetControlMessageMap(); LOG(ERROR) << "Control message: Could not find '" << name << "' for ctl." << msg const auto it = map.find(action); << " from pid: " << pid << " (" << process_cmdline << ")"; if (it == map.end()) { LOG(ERROR) << "Unknown control msg '" << message << "'"; return false; return false; } } const auto& function = it->second; if (auto result = function.action(svc); !result.ok()) { if (auto result = function(service); !result.ok()) { LOG(ERROR) << "Control message: Could not ctl." << msg << " for '" << name LOG(ERROR) << "Control message: Could not ctl." << message << " for '" << name << "' from pid: " << pid << " (" << process_cmdline << "): " << result.error(); << "' from pid: " << from_pid << " (" << process_cmdline << "): " << result.error(); return false; return false; } } LOG(INFO) << "Control message: Processed ctl." << msg << " for '" << name LOG(INFO) << "Control message: Processed ctl." << message << " for '" << name << "' from pid: " << pid << " (" << process_cmdline << ")"; << "' from pid: " << from_pid << " (" << process_cmdline << ")"; return true; return true; } } Loading init/oneshot_on_test.cpp 0 → 100644 +44 −0 Original line number Original line Diff line number Diff line /* * 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)); } init/service.h +7 −0 Original line number Original line Diff line number Diff line Loading @@ -130,6 +130,13 @@ class Service { bool is_updatable() const { return updatable_; } bool is_updatable() const { return updatable_; } bool is_post_data() const { return post_data_; } bool is_post_data() const { return post_data_; } bool is_from_apex() const { return from_apex_; } bool is_from_apex() const { return from_apex_; } void set_oneshot(bool value) { if (value) { flags_ |= SVC_ONESHOT; } else { flags_ &= ~SVC_ONESHOT; } } private: private: void NotifyStateChange(const std::string& new_state) const; void NotifyStateChange(const std::string& new_state) const; Loading Loading
init/Android.bp +1 −0 Original line number Original line Diff line number Diff line Loading @@ -240,6 +240,7 @@ cc_test { "firmware_handler_test.cpp", "firmware_handler_test.cpp", "init_test.cpp", "init_test.cpp", "keychords_test.cpp", "keychords_test.cpp", "oneshot_on_test.cpp", "persistent_properties_test.cpp", "persistent_properties_test.cpp", "property_service_test.cpp", "property_service_test.cpp", "property_type_test.cpp", "property_type_test.cpp", Loading
init/README.md +24 −12 Original line number Original line Diff line number Diff line Loading @@ -720,23 +720,35 @@ Init provides state information with the following properties. characteristics in a device agnostic manner. characteristics in a device agnostic manner. Init responds to properties that begin with `ctl.`. These properties take the format of 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: `ctl.[<target>_]<command>` and the _value_ of the system property is used as a parameter. The `SetProperty("ctl.start", "logd")` will run the `start` command on `logd`. Note that these _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. properties are only settable; they will have no value when read. `ctl.start` \ The _commands_ are listed below. `ctl.restart` \ `ctl.stop` `start` \ > These are equivalent to using the `start`, `restart`, and `stop` commands on the service specified `restart` \ `stop` \ These are equivalent to using the `start`, `restart`, and `stop` commands on the service specified by the _value_ of the property. by the _value_ of the property. `ctl.interface_start` \ `oneshot_one` and `oneshot_off` will turn on or off the _oneshot_ `ctl.interface_restart` \ flag for the service specified by the _value_ of the property. This is `ctl.interface_stop` particularly intended for services that are conditionally lazy HALs. When > These are equivalent to using the `interface_start`, `interface_restart`, and `interface_stop` they are lazy HALs, oneshot must be on, otherwise oneshot should be off. commands on the interface specified by the _value_ of the property. `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 specified by the _value_ of the property. See the _Debugging init_ section below for more details about this feature. about this feature. Loading
init/init.cpp +36 −48 Original line number Original line Diff line number Diff line Loading @@ -81,6 +81,7 @@ using namespace std::chrono_literals; using namespace std::string_literals; using namespace std::string_literals; using android::base::boot_clock; using android::base::boot_clock; using android::base::ConsumePrefix; using android::base::GetProperty; using android::base::GetProperty; using android::base::ReadFileToString; using android::base::ReadFileToString; using android::base::SetProperty; using android::base::SetProperty; Loading Loading @@ -367,40 +368,27 @@ enum class ControlTarget { INTERFACE, // action gets called for every service that holds this interface INTERFACE, // action gets called for every service that holds this interface }; }; struct ControlMessageFunction { using ControlMessageFunction = std::function<Result<void>(Service*)>; ControlTarget target; std::function<Result<void>(Service*)> action; }; static const std::map<std::string, ControlMessageFunction>& get_control_message_map() { static const std::map<std::string, ControlMessageFunction, std::less<>>& GetControlMessageMap() { // clang-format off // clang-format off static const std::map<std::string, ControlMessageFunction> control_message_functions = { static const std::map<std::string, ControlMessageFunction, std::less<>> control_message_functions = { {"sigstop_on", {ControlTarget::SERVICE, {"sigstop_on", [](auto* service) { service->set_sigstop(true); return Result<void>{}; }}, [](auto* service) { service->set_sigstop(true); return Result<void>{}; }}}, {"sigstop_off", [](auto* service) { service->set_sigstop(false); return Result<void>{}; }}, {"sigstop_off", {ControlTarget::SERVICE, {"oneshot_on", [](auto* service) { service->set_oneshot(true); return Result<void>{}; }}, [](auto* service) { service->set_sigstop(false); return Result<void>{}; }}}, {"oneshot_off", [](auto* service) { service->set_oneshot(false); return Result<void>{}; }}, {"start", {ControlTarget::SERVICE, DoControlStart}}, {"start", DoControlStart}, {"stop", {ControlTarget::SERVICE, DoControlStop}}, {"stop", DoControlStop}, {"restart", {ControlTarget::SERVICE, DoControlRestart}}, {"restart", DoControlRestart}, {"interface_start", {ControlTarget::INTERFACE, DoControlStart}}, {"interface_stop", {ControlTarget::INTERFACE, DoControlStop}}, {"interface_restart", {ControlTarget::INTERFACE, DoControlRestart}}, }; }; // clang-format on // clang-format on return control_message_functions; return control_message_functions; } } bool HandleControlMessage(const std::string& msg, const std::string& name, pid_t pid) { static bool HandleControlMessage(std::string_view message, const std::string& name, const auto& map = get_control_message_map(); pid_t from_pid) { const auto it = map.find(msg); std::string cmdline_path = StringPrintf("proc/%d/cmdline", from_pid); if (it == map.end()) { LOG(ERROR) << "Unknown control msg '" << msg << "'"; return false; } std::string cmdline_path = StringPrintf("proc/%d/cmdline", pid); std::string process_cmdline; std::string process_cmdline; if (ReadFileToString(cmdline_path, &process_cmdline)) { if (ReadFileToString(cmdline_path, &process_cmdline)) { std::replace(process_cmdline.begin(), process_cmdline.end(), '\0', ' '); std::replace(process_cmdline.begin(), process_cmdline.end(), '\0', ' '); Loading @@ -409,37 +397,37 @@ bool HandleControlMessage(const std::string& msg, const std::string& name, pid_t process_cmdline = "unknown process"; process_cmdline = "unknown process"; } } const ControlMessageFunction& function = it->second; Service* service = nullptr; auto action = message; Service* svc = nullptr; if (ConsumePrefix(&action, "interface_")) { service = ServiceList::GetInstance().FindInterface(name); } else { service = ServiceList::GetInstance().FindService(name); } switch (function.target) { if (service == nullptr) { case ControlTarget::SERVICE: LOG(ERROR) << "Control message: Could not find '" << name << "' for ctl." << message svc = ServiceList::GetInstance().FindService(name); << " from pid: " << from_pid << " (" << process_cmdline << ")"; 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; return false; } } if (svc == nullptr) { const auto& map = GetControlMessageMap(); LOG(ERROR) << "Control message: Could not find '" << name << "' for ctl." << msg const auto it = map.find(action); << " from pid: " << pid << " (" << process_cmdline << ")"; if (it == map.end()) { LOG(ERROR) << "Unknown control msg '" << message << "'"; return false; return false; } } const auto& function = it->second; if (auto result = function.action(svc); !result.ok()) { if (auto result = function(service); !result.ok()) { LOG(ERROR) << "Control message: Could not ctl." << msg << " for '" << name LOG(ERROR) << "Control message: Could not ctl." << message << " for '" << name << "' from pid: " << pid << " (" << process_cmdline << "): " << result.error(); << "' from pid: " << from_pid << " (" << process_cmdline << "): " << result.error(); return false; return false; } } LOG(INFO) << "Control message: Processed ctl." << msg << " for '" << name LOG(INFO) << "Control message: Processed ctl." << message << " for '" << name << "' from pid: " << pid << " (" << process_cmdline << ")"; << "' from pid: " << from_pid << " (" << process_cmdline << ")"; return true; return true; } } Loading
init/oneshot_on_test.cpp 0 → 100644 +44 −0 Original line number Original line Diff line number Diff line /* * 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)); }
init/service.h +7 −0 Original line number Original line Diff line number Diff line Loading @@ -130,6 +130,13 @@ class Service { bool is_updatable() const { return updatable_; } bool is_updatable() const { return updatable_; } bool is_post_data() const { return post_data_; } bool is_post_data() const { return post_data_; } bool is_from_apex() const { return from_apex_; } bool is_from_apex() const { return from_apex_; } void set_oneshot(bool value) { if (value) { flags_ |= SVC_ONESHOT; } else { flags_ &= ~SVC_ONESHOT; } } private: private: void NotifyStateChange(const std::string& new_state) const; void NotifyStateChange(const std::string& new_state) const; Loading