Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit a07d2959 authored by Treehugger Robot's avatar Treehugger Robot Committed by Gerrit Code Review
Browse files

Merge "Add support for updatable services"

parents d08b470e 80aa4470
Loading
Loading
Loading
Loading
+7 −0
Original line number Diff line number Diff line
@@ -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)
+1 −0
Original line number Diff line number Diff line
@@ -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 {
+53 −0
Original line number Diff line number Diff line
@@ -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;
        }
+13 −0
Original line number Diff line number Diff line
@@ -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