Add support for updatable services

A service with 'updatable' option can be overriden by the same service
definition in APEXes.

/system/etc/init/foo.rc:

service foo /system/bin/foo
    updatable

/apex/myapex/etc/init.rc:

service foo /apex/myapex/bin/foo
    override

Overriding a non-updatable (i.e. without updatable option) service
from APEXes is prohibited.

When an updatable service is started before APEXes are all activated,
the execution is delayed until when the APEXes are all activated.

Bug: 117403679
Test: m apex.test; adb push <built_apex> /data/apex; adb reboot
adb shell, then lsof -p $(pidof surfaceflinger) shows that
the process is executing
/apex/com.android.example.apex@1/bin/surfaceflinger instead of
/system/bin/surfaceflinger

Change-Id: I8a57b8e7f6da81b4d2843e261a9a935dd279067c
This commit is contained in:
Jiyong Park 2018-11-12 12:08:41 +09:00
parent 42b38900d7
commit 80aa44704c
4 changed files with 74 additions and 0 deletions

View File

@ -332,6 +332,13 @@ runs the service.
This is particularly useful for creating a periodic service combined with the restart_period
option described above.
`updatable`
> Mark that the service can be overridden (via the 'override' option) later in
the boot sequence by APEXes. When a service with updatable option is started
before APEXes are all activated, the execution is delayed until the activation
is finished. A service that is not marked as updatable cannot be overridden by
APEXes.
`user <username>`
> Change to 'username' before exec'ing this service.
Currently defaults to root. (??? probably should default to nobody)

View File

@ -1079,6 +1079,7 @@ static Result<Success> do_parse_apex_configs(const BuiltinArguments& args) {
}
success &= parser.ParseConfigFile(c);
}
ServiceList::GetInstance().MarkServicesUpdate();
if (success) {
return Success();
} else {

View File

@ -765,6 +765,11 @@ Result<Success> Service::ParseWritepid(std::vector<std::string>&& args) {
return Success();
}
Result<Success> Service::ParseUpdatable(std::vector<std::string>&& args) {
updatable_ = true;
return Success();
}
class Service::OptionParserMap : public KeywordMap<OptionParser> {
public:
OptionParserMap() {}
@ -817,6 +822,7 @@ const Service::OptionParserMap::Map& Service::OptionParserMap::map() const {
{"socket", {3, 6, &Service::ParseSocket}},
{"timeout_period",
{1, 1, &Service::ParseTimeoutPeriod}},
{"updatable", {0, 0, &Service::ParseUpdatable}},
{"user", {1, 1, &Service::ParseUser}},
{"writepid", {1, kMax, &Service::ParseWritepid}},
};
@ -834,6 +840,13 @@ Result<Success> Service::ParseLine(std::vector<std::string>&& args) {
}
Result<Success> Service::ExecStart() {
if (is_updatable() && !ServiceList::GetInstance().IsServicesUpdated()) {
// Don't delay the service for ExecStart() as the semantic is that
// the caller might depend on the side effect of the execution.
return Error() << "Cannot start an updatable service '" << name_
<< "' before configs from APEXes are all loaded";
}
flags_ |= SVC_ONESHOT;
if (auto result = Start(); !result) {
@ -851,6 +864,13 @@ Result<Success> Service::ExecStart() {
}
Result<Success> Service::Start() {
if (is_updatable() && !ServiceList::GetInstance().IsServicesUpdated()) {
ServiceList::GetInstance().DelayService(*this);
return Error() << "Cannot start an updatable service '" << name_
<< "' before configs from APEXes are all loaded. "
<< "Queued for execution.";
}
bool disabled = (flags_ & (SVC_DISABLED | SVC_RESET));
// 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.
@ -1280,6 +1300,32 @@ void ServiceList::DumpState() const {
}
}
void ServiceList::MarkServicesUpdate() {
services_update_finished_ = true;
// start the delayed services
for (const auto& name : delayed_service_names_) {
Service* service = FindService(name);
if (service == nullptr) {
LOG(ERROR) << "delayed service '" << name << "' could not be found.";
continue;
}
if (auto result = service->Start(); !result) {
LOG(ERROR) << result.error_string();
}
}
delayed_service_names_.clear();
}
void ServiceList::DelayService(const Service& service) {
if (services_update_finished_) {
LOG(ERROR) << "Cannot delay the start of service '" << service.name()
<< "' because all services are already updated. Ignoring.";
return;
}
delayed_service_names_.emplace_back(service.name());
}
Result<Success> ServiceParser::ParseSection(std::vector<std::string>&& args,
const std::string& filename, int line) {
if (args.size() < 3) {
@ -1291,6 +1337,8 @@ Result<Success> ServiceParser::ParseSection(std::vector<std::string>&& args,
return Error() << "invalid service name '" << name << "'";
}
filename_ = filename;
Subcontext* restart_action_subcontext = nullptr;
if (subcontexts_) {
for (auto& subcontext : *subcontexts_) {
@ -1326,6 +1374,11 @@ Result<Success> ServiceParser::EndSection() {
<< "'";
}
if (StartsWith(filename_, "/apex/") && !old_service->is_updatable()) {
return Error() << "cannot update a non-updatable service '" << service_->name()
<< "' with a config in APEX";
}
service_list_->RemoveService(*old_service);
old_service = nullptr;
}

View File

@ -123,6 +123,7 @@ class Service {
std::chrono::seconds restart_period() const { return restart_period_; }
std::optional<std::chrono::seconds> timeout_period() const { return timeout_period_; }
const std::vector<std::string>& args() const { return args_; }
bool is_updatable() const { return updatable_; }
private:
using OptionParser = Result<Success> (Service::*)(std::vector<std::string>&& args);
@ -170,6 +171,7 @@ class Service {
Result<Success> ParseFile(std::vector<std::string>&& args);
Result<Success> ParseUser(std::vector<std::string>&& args);
Result<Success> ParseWritepid(std::vector<std::string>&& args);
Result<Success> ParseUpdatable(std::vector<std::string>&& args);
template <typename T>
Result<Success> AddDescriptor(std::vector<std::string>&& args);
@ -235,6 +237,8 @@ class Service {
std::chrono::seconds restart_period_ = 5s;
std::optional<std::chrono::seconds> timeout_period_;
bool updatable_ = false;
std::vector<std::string> args_;
std::vector<std::function<void(const siginfo_t& siginfo)>> reap_callbacks_;
@ -279,8 +283,15 @@ class ServiceList {
const std::vector<std::unique_ptr<Service>>& services() const { return services_; }
const std::vector<Service*> services_in_shutdown_order() const;
void MarkServicesUpdate();
bool IsServicesUpdated() const { return services_update_finished_; }
void DelayService(const Service& service);
private:
std::vector<std::unique_ptr<Service>> services_;
bool services_update_finished_ = false;
std::vector<std::string> delayed_service_names_;
};
class ServiceParser : public SectionParser {
@ -291,6 +302,7 @@ class ServiceParser : public SectionParser {
int line) override;
Result<Success> ParseLineSection(std::vector<std::string>&& args, int line) override;
Result<Success> EndSection() override;
void EndFile() override { filename_ = ""; }
private:
bool IsValidName(const std::string& name) const;
@ -298,6 +310,7 @@ class ServiceParser : public SectionParser {
ServiceList* service_list_;
std::vector<Subcontext>* subcontexts_;
std::unique_ptr<Service> service_;
std::string filename_;
};
} // namespace init