init: add exec_start command
Exec services may also want to set other service flags such as
priority. Instead of expanding the exec syntax to handle this, create
a new command, exec_start, that will treat an existing service
definition as an exec service. The new exec_start command will start
the service then halt init from executing further commands until the
service has exited.
This change additionally encapsulates the waiting_for_exec logic into
ServiceManager and removes the ambiguous 'bool' return value from
Reap() which previously indicated if a Reaped service was an exec
service or not.
Bug: 36511808
Bug: 36102163
Test: Bullhead boots, services run with exec_start as they do exec.
Change-Id: I44f775cf1c1dd81d5c715f44fdc150c651a2c80a
diff --git a/init/service.cpp b/init/service.cpp
index c8d1cb1..a6efe52 100644
--- a/init/service.cpp
+++ b/init/service.cpp
@@ -174,8 +174,8 @@
}
void Service::NotifyStateChange(const std::string& new_state) const {
- if ((flags_ & SVC_EXEC) != 0) {
- // 'exec' commands don't have properties tracking their state.
+ if ((flags_ & SVC_TEMPORARY) != 0) {
+ // Services created by 'exec' are temporary and don't have properties tracking their state.
return;
}
@@ -242,7 +242,7 @@
}
}
-bool Service::Reap() {
+void Service::Reap() {
if (!(flags_ & SVC_ONESHOT) || (flags_ & SVC_RESTART)) {
KillProcessGroup(SIGKILL);
}
@@ -253,7 +253,10 @@
if (flags_ & SVC_EXEC) {
LOG(INFO) << "SVC_EXEC pid " << pid_ << " finished...";
- return true;
+ }
+
+ if (flags_ & SVC_TEMPORARY) {
+ return;
}
pid_ = 0;
@@ -268,7 +271,7 @@
// Disabled and reset processes do not get restarted automatically.
if (flags_ & (SVC_DISABLED | SVC_RESET)) {
NotifyStateChange("stopped");
- return false;
+ return;
}
// If we crash > 4 times in 4 minutes, reboot into recovery.
@@ -292,7 +295,7 @@
onrestart_.ExecuteAllCommands();
NotifyStateChange("restarting");
- return false;
+ return;
}
void Service::DumpState() const {
@@ -558,6 +561,18 @@
return (this->*parser)(args, err);
}
+bool Service::ExecStart(std::unique_ptr<Timer>* exec_waiter) {
+ flags_ |= SVC_EXEC | SVC_ONESHOT;
+
+ exec_waiter->reset(new Timer);
+
+ if (!Start()) {
+ exec_waiter->reset();
+ return false;
+ }
+ return true;
+}
+
bool Service::Start() {
// Starting a service removes it from the disabled or reset state and
// immediately takes it out of the restarting state if it was in there.
@@ -844,6 +859,35 @@
services_.emplace_back(std::move(service));
}
+bool ServiceManager::Exec(const std::vector<std::string>& args) {
+ Service* svc = MakeExecOneshotService(args);
+ if (!svc) {
+ LOG(ERROR) << "Could not create exec service";
+ return false;
+ }
+ if (!svc->ExecStart(&exec_waiter_)) {
+ LOG(ERROR) << "Could not start exec service";
+ ServiceManager::GetInstance().RemoveService(*svc);
+ return false;
+ }
+ return true;
+}
+
+bool ServiceManager::ExecStart(const std::string& name) {
+ Service* svc = FindServiceByName(name);
+ if (!svc) {
+ LOG(ERROR) << "ExecStart(" << name << "): Service not found";
+ return false;
+ }
+ if (!svc->ExecStart(&exec_waiter_)) {
+ LOG(ERROR) << "ExecStart(" << name << "): Could not start Service";
+ return false;
+ }
+ return true;
+}
+
+bool ServiceManager::IsWaitingForExec() const { return exec_waiter_ != nullptr; }
+
Service* ServiceManager::MakeExecOneshotService(const std::vector<std::string>& args) {
// Parse the arguments: exec [SECLABEL [UID [GID]*] --] COMMAND ARGS...
// SECLABEL can be a - to denote default
@@ -867,7 +911,7 @@
exec_count_++;
std::string name = StringPrintf("exec %d (%s)", exec_count_, str_args[0].c_str());
- unsigned flags = SVC_EXEC | SVC_ONESHOT;
+ unsigned flags = SVC_EXEC | SVC_ONESHOT | SVC_TEMPORARY;
CapSet no_capabilities;
unsigned namespace_flags = 0;
@@ -1007,8 +1051,13 @@
return true;
}
- if (svc->Reap()) {
- stop_waiting_for_exec();
+ svc->Reap();
+
+ if (svc->flags() & SVC_EXEC) {
+ LOG(INFO) << "Wait for exec took " << *exec_waiter_;
+ exec_waiter_.reset();
+ }
+ if (svc->flags() & SVC_TEMPORARY) {
RemoveService(*svc);
}