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

Commit b1ffb1de authored by Tom Cherry's avatar Tom Cherry
Browse files

Move actual parsing from Service to ServiceParser

This is how this should have been done since the beginning.

Test: build, boot
Change-Id: Ifd795776c71a2e666da7fab90cbb3f356af93d4f
parent 2aeb1add
Loading
Loading
Loading
Loading
+0 −455
Original line number Diff line number Diff line
@@ -18,11 +18,9 @@

#include <fcntl.h>
#include <inttypes.h>
#include <linux/input.h>
#include <linux/securebits.h>
#include <sched.h>
#include <sys/prctl.h>
#include <sys/resource.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <termios.h>
@@ -30,28 +28,21 @@

#include <android-base/file.h>
#include <android-base/logging.h>
#include <android-base/parseint.h>
#include <android-base/properties.h>
#include <android-base/stringprintf.h>
#include <android-base/strings.h>
#include <hidl-util/FQName.h>
#include <processgroup/processgroup.h>
#include <selinux/selinux.h>
#include <system/thread_defs.h>

#include "rlimit_parser.h"
#include "service_list.h"
#include "util.h"

#if defined(__ANDROID__)
#include <ApexProperties.sysprop.h>
#include <android/api-level.h>
#include <sys/system_properties.h>

#include "init.h"
#include "mount_namespace.h"
#include "property_service.h"
#include "selinux.h"
#else
#include "host_init_stubs.h"
#endif
@@ -59,8 +50,6 @@
using android::base::boot_clock;
using android::base::GetProperty;
using android::base::Join;
using android::base::ParseInt;
using android::base::Split;
using android::base::StartsWith;
using android::base::StringPrintf;
using android::base::WriteStringToFile;
@@ -316,450 +305,6 @@ void Service::DumpState() const {
                  [] (const auto& info) { LOG(INFO) << *info; });
}

Result<void> Service::ParseCapabilities(std::vector<std::string>&& args) {
    capabilities_ = 0;

    if (!CapAmbientSupported()) {
        return Error()
               << "capabilities requested but the kernel does not support ambient capabilities";
    }

    unsigned int last_valid_cap = GetLastValidCap();
    if (last_valid_cap >= capabilities_->size()) {
        LOG(WARNING) << "last valid run-time capability is larger than CAP_LAST_CAP";
    }

    for (size_t i = 1; i < args.size(); i++) {
        const std::string& arg = args[i];
        int res = LookupCap(arg);
        if (res < 0) {
            return Errorf("invalid capability '{}'", arg);
        }
        unsigned int cap = static_cast<unsigned int>(res);  // |res| is >= 0.
        if (cap > last_valid_cap) {
            return Errorf("capability '{}' not supported by the kernel", arg);
        }
        (*capabilities_)[cap] = true;
    }
    return {};
}

Result<void> Service::ParseClass(std::vector<std::string>&& args) {
    classnames_ = std::set<std::string>(args.begin() + 1, args.end());
    return {};
}

Result<void> Service::ParseConsole(std::vector<std::string>&& args) {
    flags_ |= SVC_CONSOLE;
    proc_attr_.console = args.size() > 1 ? "/dev/" + args[1] : "";
    return {};
}

Result<void> Service::ParseCritical(std::vector<std::string>&& args) {
    flags_ |= SVC_CRITICAL;
    return {};
}

Result<void> Service::ParseDisabled(std::vector<std::string>&& args) {
    flags_ |= SVC_DISABLED;
    flags_ |= SVC_RC_DISABLED;
    return {};
}

Result<void> Service::ParseEnterNamespace(std::vector<std::string>&& args) {
    if (args[1] != "net") {
        return Error() << "Init only supports entering network namespaces";
    }
    if (!namespaces_.namespaces_to_enter.empty()) {
        return Error() << "Only one network namespace may be entered";
    }
    // Network namespaces require that /sys is remounted, otherwise the old adapters will still be
    // present. Therefore, they also require mount namespaces.
    namespaces_.flags |= CLONE_NEWNS;
    namespaces_.namespaces_to_enter.emplace_back(CLONE_NEWNET, std::move(args[2]));
    return {};
}

Result<void> Service::ParseGroup(std::vector<std::string>&& args) {
    auto gid = DecodeUid(args[1]);
    if (!gid) {
        return Error() << "Unable to decode GID for '" << args[1] << "': " << gid.error();
    }
    proc_attr_.gid = *gid;

    for (std::size_t n = 2; n < args.size(); n++) {
        gid = DecodeUid(args[n]);
        if (!gid) {
            return Error() << "Unable to decode GID for '" << args[n] << "': " << gid.error();
        }
        proc_attr_.supp_gids.emplace_back(*gid);
    }
    return {};
}

Result<void> Service::ParsePriority(std::vector<std::string>&& args) {
    proc_attr_.priority = 0;
    if (!ParseInt(args[1], &proc_attr_.priority,
                  static_cast<int>(ANDROID_PRIORITY_HIGHEST),  // highest is negative
                  static_cast<int>(ANDROID_PRIORITY_LOWEST))) {
        return Errorf("process priority value must be range {} - {}", ANDROID_PRIORITY_HIGHEST,
                      ANDROID_PRIORITY_LOWEST);
    }
    return {};
}

Result<void> Service::ParseInterface(std::vector<std::string>&& args) {
    const std::string& interface_name = args[1];
    const std::string& instance_name = args[2];

    FQName fq_name;
    if (!FQName::parse(interface_name, &fq_name)) {
        return Error() << "Invalid fully-qualified name for interface '" << interface_name << "'";
    }

    if (!fq_name.isFullyQualified()) {
        return Error() << "Interface name not fully-qualified '" << interface_name << "'";
    }

    if (fq_name.isValidValueName()) {
        return Error() << "Interface name must not be a value name '" << interface_name << "'";
    }

    const std::string fullname = interface_name + "/" + instance_name;

    for (const auto& svc : ServiceList::GetInstance()) {
        if (svc->interfaces().count(fullname) > 0) {
            return Error() << "Interface '" << fullname << "' redefined in " << name()
                           << " but is already defined by " << svc->name();
        }
    }

    interfaces_.insert(fullname);

    return {};
}

Result<void> Service::ParseIoprio(std::vector<std::string>&& args) {
    if (!ParseInt(args[2], &proc_attr_.ioprio_pri, 0, 7)) {
        return Error() << "priority value must be range 0 - 7";
    }

    if (args[1] == "rt") {
        proc_attr_.ioprio_class = IoSchedClass_RT;
    } else if (args[1] == "be") {
        proc_attr_.ioprio_class = IoSchedClass_BE;
    } else if (args[1] == "idle") {
        proc_attr_.ioprio_class = IoSchedClass_IDLE;
    } else {
        return Error() << "ioprio option usage: ioprio <rt|be|idle> <0-7>";
    }

    return {};
}

Result<void> Service::ParseKeycodes(std::vector<std::string>&& args) {
    auto it = args.begin() + 1;
    if (args.size() == 2 && StartsWith(args[1], "$")) {
        std::string expanded;
        if (!expand_props(args[1], &expanded)) {
            return Error() << "Could not expand property '" << args[1] << "'";
        }

        // If the property is not set, it defaults to none, in which case there are no keycodes
        // for this service.
        if (expanded == "none") {
            return {};
        }

        args = Split(expanded, ",");
        it = args.begin();
    }

    for (; it != args.end(); ++it) {
        int code;
        if (ParseInt(*it, &code, 0, KEY_MAX)) {
            for (auto& key : keycodes_) {
                if (key == code) return Error() << "duplicate keycode: " << *it;
            }
            keycodes_.insert(std::upper_bound(keycodes_.begin(), keycodes_.end(), code), code);
        } else {
            return Error() << "invalid keycode: " << *it;
        }
    }
    return {};
}

Result<void> Service::ParseOneshot(std::vector<std::string>&& args) {
    flags_ |= SVC_ONESHOT;
    return {};
}

Result<void> Service::ParseOnrestart(std::vector<std::string>&& args) {
    args.erase(args.begin());
    int line = onrestart_.NumCommands() + 1;
    if (auto result = onrestart_.AddCommand(std::move(args), line); !result) {
        return Error() << "cannot add Onrestart command: " << result.error();
    }
    return {};
}

Result<void> Service::ParseNamespace(std::vector<std::string>&& args) {
    for (size_t i = 1; i < args.size(); i++) {
        if (args[i] == "pid") {
            namespaces_.flags |= CLONE_NEWPID;
            // PID namespaces require mount namespaces.
            namespaces_.flags |= CLONE_NEWNS;
        } else if (args[i] == "mnt") {
            namespaces_.flags |= CLONE_NEWNS;
        } else {
            return Error() << "namespace must be 'pid' or 'mnt'";
        }
    }
    return {};
}

Result<void> Service::ParseOomScoreAdjust(std::vector<std::string>&& args) {
    if (!ParseInt(args[1], &oom_score_adjust_, -1000, 1000)) {
        return Error() << "oom_score_adjust value must be in range -1000 - +1000";
    }
    return {};
}

Result<void> Service::ParseOverride(std::vector<std::string>&& args) {
    override_ = true;
    return {};
}

Result<void> Service::ParseMemcgSwappiness(std::vector<std::string>&& args) {
    if (!ParseInt(args[1], &swappiness_, 0)) {
        return Error() << "swappiness value must be equal or greater than 0";
    }
    return {};
}

Result<void> Service::ParseMemcgLimitInBytes(std::vector<std::string>&& args) {
    if (!ParseInt(args[1], &limit_in_bytes_, 0)) {
        return Error() << "limit_in_bytes value must be equal or greater than 0";
    }
    return {};
}

Result<void> Service::ParseMemcgLimitPercent(std::vector<std::string>&& args) {
    if (!ParseInt(args[1], &limit_percent_, 0)) {
        return Error() << "limit_percent value must be equal or greater than 0";
    }
    return {};
}

Result<void> Service::ParseMemcgLimitProperty(std::vector<std::string>&& args) {
    limit_property_ = std::move(args[1]);
    return {};
}

Result<void> Service::ParseMemcgSoftLimitInBytes(std::vector<std::string>&& args) {
    if (!ParseInt(args[1], &soft_limit_in_bytes_, 0)) {
        return Error() << "soft_limit_in_bytes value must be equal or greater than 0";
    }
    return {};
}

Result<void> Service::ParseProcessRlimit(std::vector<std::string>&& args) {
    auto rlimit = ParseRlimit(args);
    if (!rlimit) return rlimit.error();

    proc_attr_.rlimits.emplace_back(*rlimit);
    return {};
}

Result<void> Service::ParseRestartPeriod(std::vector<std::string>&& args) {
    int period;
    if (!ParseInt(args[1], &period, 5)) {
        return Error() << "restart_period value must be an integer >= 5";
    }
    restart_period_ = std::chrono::seconds(period);
    return {};
}

Result<void> Service::ParseSeclabel(std::vector<std::string>&& args) {
    seclabel_ = std::move(args[1]);
    return {};
}

Result<void> Service::ParseSigstop(std::vector<std::string>&& args) {
    sigstop_ = true;
    return {};
}

Result<void> Service::ParseSetenv(std::vector<std::string>&& args) {
    environment_vars_.emplace_back(std::move(args[1]), std::move(args[2]));
    return {};
}

Result<void> Service::ParseShutdown(std::vector<std::string>&& args) {
    if (args[1] == "critical") {
        flags_ |= SVC_SHUTDOWN_CRITICAL;
        return {};
    }
    return Error() << "Invalid shutdown option";
}

Result<void> Service::ParseTimeoutPeriod(std::vector<std::string>&& args) {
    int period;
    if (!ParseInt(args[1], &period, 1)) {
        return Error() << "timeout_period value must be an integer >= 1";
    }
    timeout_period_ = std::chrono::seconds(period);
    return {};
}

template <typename T>
Result<void> Service::AddDescriptor(std::vector<std::string>&& args) {
    int perm = args.size() > 3 ? std::strtoul(args[3].c_str(), 0, 8) : -1;
    Result<uid_t> uid = 0;
    Result<gid_t> gid = 0;
    std::string context = args.size() > 6 ? args[6] : "";

    if (args.size() > 4) {
        uid = DecodeUid(args[4]);
        if (!uid) {
            return Error() << "Unable to find UID for '" << args[4] << "': " << uid.error();
        }
    }

    if (args.size() > 5) {
        gid = DecodeUid(args[5]);
        if (!gid) {
            return Error() << "Unable to find GID for '" << args[5] << "': " << gid.error();
        }
    }

    auto descriptor = std::make_unique<T>(args[1], args[2], *uid, *gid, perm, context);

    auto old =
        std::find_if(descriptors_.begin(), descriptors_.end(),
                     [&descriptor] (const auto& other) { return descriptor.get() == other.get(); });

    if (old != descriptors_.end()) {
        return Error() << "duplicate descriptor " << args[1] << " " << args[2];
    }

    descriptors_.emplace_back(std::move(descriptor));
    return {};
}

// name type perm [ uid gid context ]
Result<void> Service::ParseSocket(std::vector<std::string>&& args) {
    if (!StartsWith(args[2], "dgram") && !StartsWith(args[2], "stream") &&
        !StartsWith(args[2], "seqpacket")) {
        return Error() << "socket type must be 'dgram', 'stream' or 'seqpacket'";
    }
    return AddDescriptor<SocketInfo>(std::move(args));
}

// name type perm [ uid gid context ]
Result<void> Service::ParseFile(std::vector<std::string>&& args) {
    if (args[2] != "r" && args[2] != "w" && args[2] != "rw") {
        return Error() << "file type must be 'r', 'w' or 'rw'";
    }
    std::string expanded;
    if (!expand_props(args[1], &expanded)) {
        return Error() << "Could not expand property in file path '" << args[1] << "'";
    }
    args[1] = std::move(expanded);
    if ((args[1][0] != '/') || (args[1].find("../") != std::string::npos)) {
        return Error() << "file name must not be relative";
    }
    return AddDescriptor<FileInfo>(std::move(args));
}

Result<void> Service::ParseUser(std::vector<std::string>&& args) {
    auto uid = DecodeUid(args[1]);
    if (!uid) {
        return Error() << "Unable to find UID for '" << args[1] << "': " << uid.error();
    }
    proc_attr_.uid = *uid;
    return {};
}

Result<void> Service::ParseWritepid(std::vector<std::string>&& args) {
    args.erase(args.begin());
    writepid_files_ = std::move(args);
    return {};
}

Result<void> Service::ParseUpdatable(std::vector<std::string>&& args) {
    updatable_ = true;
    return {};
}

class Service::OptionParserMap : public KeywordMap<OptionParser> {
  public:
    OptionParserMap() {}

  private:
    const Map& map() const override;
};

const Service::OptionParserMap::Map& Service::OptionParserMap::map() const {
    constexpr std::size_t kMax = std::numeric_limits<std::size_t>::max();
    // clang-format off
    static const Map option_parsers = {
        {"capabilities",
                        {0,     kMax, &Service::ParseCapabilities}},
        {"class",       {1,     kMax, &Service::ParseClass}},
        {"console",     {0,     1,    &Service::ParseConsole}},
        {"critical",    {0,     0,    &Service::ParseCritical}},
        {"disabled",    {0,     0,    &Service::ParseDisabled}},
        {"enter_namespace",
                        {2,     2,    &Service::ParseEnterNamespace}},
        {"file",        {2,     2,    &Service::ParseFile}},
        {"group",       {1,     NR_SVC_SUPP_GIDS + 1, &Service::ParseGroup}},
        {"interface",   {2,     2,    &Service::ParseInterface}},
        {"ioprio",      {2,     2,    &Service::ParseIoprio}},
        {"keycodes",    {1,     kMax, &Service::ParseKeycodes}},
        {"memcg.limit_in_bytes",
                        {1,     1,    &Service::ParseMemcgLimitInBytes}},
        {"memcg.limit_percent",
                        {1,     1,    &Service::ParseMemcgLimitPercent}},
        {"memcg.limit_property",
                        {1,     1,    &Service::ParseMemcgLimitProperty}},
        {"memcg.soft_limit_in_bytes",
                        {1,     1,    &Service::ParseMemcgSoftLimitInBytes}},
        {"memcg.swappiness",
                        {1,     1,    &Service::ParseMemcgSwappiness}},
        {"namespace",   {1,     2,    &Service::ParseNamespace}},
        {"oneshot",     {0,     0,    &Service::ParseOneshot}},
        {"onrestart",   {1,     kMax, &Service::ParseOnrestart}},
        {"oom_score_adjust",
                        {1,     1,    &Service::ParseOomScoreAdjust}},
        {"override",    {0,     0,    &Service::ParseOverride}},
        {"priority",    {1,     1,    &Service::ParsePriority}},
        {"restart_period",
                        {1,     1,    &Service::ParseRestartPeriod}},
        {"rlimit",      {3,     3,    &Service::ParseProcessRlimit}},
        {"seclabel",    {1,     1,    &Service::ParseSeclabel}},
        {"setenv",      {2,     2,    &Service::ParseSetenv}},
        {"shutdown",    {1,     1,    &Service::ParseShutdown}},
        {"sigstop",     {0,     0,    &Service::ParseSigstop}},
        {"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}},
    };
    // clang-format on
    return option_parsers;
}

Result<void> Service::ParseLine(std::vector<std::string>&& args) {
    static const OptionParserMap parser_map;
    auto parser = parser_map.FindFunction(args);

    if (!parser) return parser.error();

    return std::invoke(*parser, this, std::move(args));
}

Result<void> Service::ExecStart() {
    if (is_updatable() && !ServiceList::GetInstance().IsServicesUpdated()) {
+3 −46
Original line number Diff line number Diff line
@@ -14,11 +14,9 @@
 * limitations under the License.
 */

#ifndef _INIT_SERVICE_H
#define _INIT_SERVICE_H
#pragma once

#include <signal.h>
#include <sys/resource.h>
#include <sys/types.h>

#include <chrono>
@@ -64,6 +62,8 @@ namespace android {
namespace init {

class Service {
    friend class ServiceParser;

  public:
    Service(const std::string& name, Subcontext* subcontext_for_restart_commands,
            const std::vector<std::string>& args);
@@ -76,7 +76,6 @@ class Service {
    static std::unique_ptr<Service> MakeTemporaryOneshotService(const std::vector<std::string>& args);

    bool IsRunning() { return (flags_ & SVC_RUNNING) != 0; }
    Result<void> ParseLine(std::vector<std::string>&& args);
    Result<void> ExecStart();
    Result<void> Start();
    Result<void> StartIfNotDisabled();
@@ -130,51 +129,11 @@ class Service {
    bool is_post_data() const { return post_data_; }

  private:
    using OptionParser = Result<void> (Service::*)(std::vector<std::string>&& args);
    class OptionParserMap;

    void NotifyStateChange(const std::string& new_state) const;
    void StopOrReset(int how);
    void KillProcessGroup(int signal);
    void SetProcessAttributesAndCaps();

    Result<void> ParseCapabilities(std::vector<std::string>&& args);
    Result<void> ParseClass(std::vector<std::string>&& args);
    Result<void> ParseConsole(std::vector<std::string>&& args);
    Result<void> ParseCritical(std::vector<std::string>&& args);
    Result<void> ParseDisabled(std::vector<std::string>&& args);
    Result<void> ParseEnterNamespace(std::vector<std::string>&& args);
    Result<void> ParseGroup(std::vector<std::string>&& args);
    Result<void> ParsePriority(std::vector<std::string>&& args);
    Result<void> ParseInterface(std::vector<std::string>&& args);
    Result<void> ParseIoprio(std::vector<std::string>&& args);
    Result<void> ParseKeycodes(std::vector<std::string>&& args);
    Result<void> ParseOneshot(std::vector<std::string>&& args);
    Result<void> ParseOnrestart(std::vector<std::string>&& args);
    Result<void> ParseOomScoreAdjust(std::vector<std::string>&& args);
    Result<void> ParseOverride(std::vector<std::string>&& args);
    Result<void> ParseMemcgLimitInBytes(std::vector<std::string>&& args);
    Result<void> ParseMemcgLimitPercent(std::vector<std::string>&& args);
    Result<void> ParseMemcgLimitProperty(std::vector<std::string>&& args);
    Result<void> ParseMemcgSoftLimitInBytes(std::vector<std::string>&& args);
    Result<void> ParseMemcgSwappiness(std::vector<std::string>&& args);
    Result<void> ParseNamespace(std::vector<std::string>&& args);
    Result<void> ParseProcessRlimit(std::vector<std::string>&& args);
    Result<void> ParseRestartPeriod(std::vector<std::string>&& args);
    Result<void> ParseSeclabel(std::vector<std::string>&& args);
    Result<void> ParseSetenv(std::vector<std::string>&& args);
    Result<void> ParseShutdown(std::vector<std::string>&& args);
    Result<void> ParseSigstop(std::vector<std::string>&& args);
    Result<void> ParseSocket(std::vector<std::string>&& args);
    Result<void> ParseTimeoutPeriod(std::vector<std::string>&& args);
    Result<void> ParseFile(std::vector<std::string>&& args);
    Result<void> ParseUser(std::vector<std::string>&& args);
    Result<void> ParseWritepid(std::vector<std::string>&& args);
    Result<void> ParseUpdatable(std::vector<std::string>&& args);

    template <typename T>
    Result<void> AddDescriptor(std::vector<std::string>&& args);

    static unsigned long next_start_order_;
    static bool is_exec_service_running_;

@@ -240,5 +199,3 @@ class Service {

}  // namespace init
}  // namespace android

#endif
+474 −15

File changed.

Preview size limit exceeded, changes collapsed.

+40 −0
Original line number Diff line number Diff line
@@ -37,6 +37,46 @@ class ServiceParser : public SectionParser {
    void EndFile() override { filename_ = ""; }

  private:
    using OptionParser = Result<void> (ServiceParser::*)(std::vector<std::string>&& args);
    class OptionParserMap;

    Result<void> ParseCapabilities(std::vector<std::string>&& args);
    Result<void> ParseClass(std::vector<std::string>&& args);
    Result<void> ParseConsole(std::vector<std::string>&& args);
    Result<void> ParseCritical(std::vector<std::string>&& args);
    Result<void> ParseDisabled(std::vector<std::string>&& args);
    Result<void> ParseEnterNamespace(std::vector<std::string>&& args);
    Result<void> ParseGroup(std::vector<std::string>&& args);
    Result<void> ParsePriority(std::vector<std::string>&& args);
    Result<void> ParseInterface(std::vector<std::string>&& args);
    Result<void> ParseIoprio(std::vector<std::string>&& args);
    Result<void> ParseKeycodes(std::vector<std::string>&& args);
    Result<void> ParseOneshot(std::vector<std::string>&& args);
    Result<void> ParseOnrestart(std::vector<std::string>&& args);
    Result<void> ParseOomScoreAdjust(std::vector<std::string>&& args);
    Result<void> ParseOverride(std::vector<std::string>&& args);
    Result<void> ParseMemcgLimitInBytes(std::vector<std::string>&& args);
    Result<void> ParseMemcgLimitPercent(std::vector<std::string>&& args);
    Result<void> ParseMemcgLimitProperty(std::vector<std::string>&& args);
    Result<void> ParseMemcgSoftLimitInBytes(std::vector<std::string>&& args);
    Result<void> ParseMemcgSwappiness(std::vector<std::string>&& args);
    Result<void> ParseNamespace(std::vector<std::string>&& args);
    Result<void> ParseProcessRlimit(std::vector<std::string>&& args);
    Result<void> ParseRestartPeriod(std::vector<std::string>&& args);
    Result<void> ParseSeclabel(std::vector<std::string>&& args);
    Result<void> ParseSetenv(std::vector<std::string>&& args);
    Result<void> ParseShutdown(std::vector<std::string>&& args);
    Result<void> ParseSigstop(std::vector<std::string>&& args);
    Result<void> ParseSocket(std::vector<std::string>&& args);
    Result<void> ParseTimeoutPeriod(std::vector<std::string>&& args);
    Result<void> ParseFile(std::vector<std::string>&& args);
    Result<void> ParseUser(std::vector<std::string>&& args);
    Result<void> ParseWritepid(std::vector<std::string>&& args);
    Result<void> ParseUpdatable(std::vector<std::string>&& args);

    template <typename T>
    Result<void> AddDescriptor(std::vector<std::string>&& args);

    bool IsValidName(const std::string& name) const;

    ServiceList* service_list_;