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:
parent
42b38900d7
commit
80aa44704c
|
@ -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)
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue