diff --git a/init/service.cpp b/init/service.cpp index 4ac4571f2..4cd18891d 100644 --- a/init/service.cpp +++ b/init/service.cpp @@ -18,11 +18,9 @@ #include #include -#include #include #include #include -#include #include #include #include @@ -30,28 +28,21 @@ #include #include -#include #include #include #include -#include #include #include -#include -#include "rlimit_parser.h" #include "service_list.h" #include "util.h" #if defined(__ANDROID__) #include -#include -#include #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 Service::ParseCapabilities(std::vector&& 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(res); // |res| is >= 0. - if (cap > last_valid_cap) { - return Errorf("capability '{}' not supported by the kernel", arg); - } - (*capabilities_)[cap] = true; - } - return {}; -} - -Result Service::ParseClass(std::vector&& args) { - classnames_ = std::set(args.begin() + 1, args.end()); - return {}; -} - -Result Service::ParseConsole(std::vector&& args) { - flags_ |= SVC_CONSOLE; - proc_attr_.console = args.size() > 1 ? "/dev/" + args[1] : ""; - return {}; -} - -Result Service::ParseCritical(std::vector&& args) { - flags_ |= SVC_CRITICAL; - return {}; -} - -Result Service::ParseDisabled(std::vector&& args) { - flags_ |= SVC_DISABLED; - flags_ |= SVC_RC_DISABLED; - return {}; -} - -Result Service::ParseEnterNamespace(std::vector&& 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 Service::ParseGroup(std::vector&& 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 Service::ParsePriority(std::vector&& args) { - proc_attr_.priority = 0; - if (!ParseInt(args[1], &proc_attr_.priority, - static_cast(ANDROID_PRIORITY_HIGHEST), // highest is negative - static_cast(ANDROID_PRIORITY_LOWEST))) { - return Errorf("process priority value must be range {} - {}", ANDROID_PRIORITY_HIGHEST, - ANDROID_PRIORITY_LOWEST); - } - return {}; -} - -Result Service::ParseInterface(std::vector&& 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 Service::ParseIoprio(std::vector&& 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 <0-7>"; - } - - return {}; -} - -Result Service::ParseKeycodes(std::vector&& 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 Service::ParseOneshot(std::vector&& args) { - flags_ |= SVC_ONESHOT; - return {}; -} - -Result Service::ParseOnrestart(std::vector&& 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 Service::ParseNamespace(std::vector&& 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 Service::ParseOomScoreAdjust(std::vector&& args) { - if (!ParseInt(args[1], &oom_score_adjust_, -1000, 1000)) { - return Error() << "oom_score_adjust value must be in range -1000 - +1000"; - } - return {}; -} - -Result Service::ParseOverride(std::vector&& args) { - override_ = true; - return {}; -} - -Result Service::ParseMemcgSwappiness(std::vector&& args) { - if (!ParseInt(args[1], &swappiness_, 0)) { - return Error() << "swappiness value must be equal or greater than 0"; - } - return {}; -} - -Result Service::ParseMemcgLimitInBytes(std::vector&& args) { - if (!ParseInt(args[1], &limit_in_bytes_, 0)) { - return Error() << "limit_in_bytes value must be equal or greater than 0"; - } - return {}; -} - -Result Service::ParseMemcgLimitPercent(std::vector&& args) { - if (!ParseInt(args[1], &limit_percent_, 0)) { - return Error() << "limit_percent value must be equal or greater than 0"; - } - return {}; -} - -Result Service::ParseMemcgLimitProperty(std::vector&& args) { - limit_property_ = std::move(args[1]); - return {}; -} - -Result Service::ParseMemcgSoftLimitInBytes(std::vector&& 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 Service::ParseProcessRlimit(std::vector&& args) { - auto rlimit = ParseRlimit(args); - if (!rlimit) return rlimit.error(); - - proc_attr_.rlimits.emplace_back(*rlimit); - return {}; -} - -Result Service::ParseRestartPeriod(std::vector&& 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 Service::ParseSeclabel(std::vector&& args) { - seclabel_ = std::move(args[1]); - return {}; -} - -Result Service::ParseSigstop(std::vector&& args) { - sigstop_ = true; - return {}; -} - -Result Service::ParseSetenv(std::vector&& args) { - environment_vars_.emplace_back(std::move(args[1]), std::move(args[2])); - return {}; -} - -Result Service::ParseShutdown(std::vector&& args) { - if (args[1] == "critical") { - flags_ |= SVC_SHUTDOWN_CRITICAL; - return {}; - } - return Error() << "Invalid shutdown option"; -} - -Result Service::ParseTimeoutPeriod(std::vector&& 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 -Result Service::AddDescriptor(std::vector&& args) { - int perm = args.size() > 3 ? std::strtoul(args[3].c_str(), 0, 8) : -1; - Result uid = 0; - Result 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(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 Service::ParseSocket(std::vector&& 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(std::move(args)); -} - -// name type perm [ uid gid context ] -Result Service::ParseFile(std::vector&& 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(std::move(args)); -} - -Result Service::ParseUser(std::vector&& 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 Service::ParseWritepid(std::vector&& args) { - args.erase(args.begin()); - writepid_files_ = std::move(args); - return {}; -} - -Result Service::ParseUpdatable(std::vector&& args) { - updatable_ = true; - return {}; -} - -class Service::OptionParserMap : public KeywordMap { - public: - OptionParserMap() {} - - private: - const Map& map() const override; -}; - -const Service::OptionParserMap::Map& Service::OptionParserMap::map() const { - constexpr std::size_t kMax = std::numeric_limits::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 Service::ParseLine(std::vector&& 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 Service::ExecStart() { if (is_updatable() && !ServiceList::GetInstance().IsServicesUpdated()) { diff --git a/init/service.h b/init/service.h index 0bfc2e6aa..78f94ce9e 100644 --- a/init/service.h +++ b/init/service.h @@ -14,11 +14,9 @@ * limitations under the License. */ -#ifndef _INIT_SERVICE_H -#define _INIT_SERVICE_H +#pragma once #include -#include #include #include @@ -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& args); @@ -76,7 +76,6 @@ class Service { static std::unique_ptr MakeTemporaryOneshotService(const std::vector& args); bool IsRunning() { return (flags_ & SVC_RUNNING) != 0; } - Result ParseLine(std::vector&& args); Result ExecStart(); Result Start(); Result StartIfNotDisabled(); @@ -130,51 +129,11 @@ class Service { bool is_post_data() const { return post_data_; } private: - using OptionParser = Result (Service::*)(std::vector&& args); - class OptionParserMap; - void NotifyStateChange(const std::string& new_state) const; void StopOrReset(int how); void KillProcessGroup(int signal); void SetProcessAttributesAndCaps(); - Result ParseCapabilities(std::vector&& args); - Result ParseClass(std::vector&& args); - Result ParseConsole(std::vector&& args); - Result ParseCritical(std::vector&& args); - Result ParseDisabled(std::vector&& args); - Result ParseEnterNamespace(std::vector&& args); - Result ParseGroup(std::vector&& args); - Result ParsePriority(std::vector&& args); - Result ParseInterface(std::vector&& args); - Result ParseIoprio(std::vector&& args); - Result ParseKeycodes(std::vector&& args); - Result ParseOneshot(std::vector&& args); - Result ParseOnrestart(std::vector&& args); - Result ParseOomScoreAdjust(std::vector&& args); - Result ParseOverride(std::vector&& args); - Result ParseMemcgLimitInBytes(std::vector&& args); - Result ParseMemcgLimitPercent(std::vector&& args); - Result ParseMemcgLimitProperty(std::vector&& args); - Result ParseMemcgSoftLimitInBytes(std::vector&& args); - Result ParseMemcgSwappiness(std::vector&& args); - Result ParseNamespace(std::vector&& args); - Result ParseProcessRlimit(std::vector&& args); - Result ParseRestartPeriod(std::vector&& args); - Result ParseSeclabel(std::vector&& args); - Result ParseSetenv(std::vector&& args); - Result ParseShutdown(std::vector&& args); - Result ParseSigstop(std::vector&& args); - Result ParseSocket(std::vector&& args); - Result ParseTimeoutPeriod(std::vector&& args); - Result ParseFile(std::vector&& args); - Result ParseUser(std::vector&& args); - Result ParseWritepid(std::vector&& args); - Result ParseUpdatable(std::vector&& args); - - template - Result AddDescriptor(std::vector&& args); - static unsigned long next_start_order_; static bool is_exec_service_running_; @@ -240,5 +199,3 @@ class Service { } // namespace init } // namespace android - -#endif diff --git a/init/service_parser.cpp b/init/service_parser.cpp index cdfaff3c5..33ed05097 100644 --- a/init/service_parser.cpp +++ b/init/service_parser.cpp @@ -16,11 +16,19 @@ #include "service_parser.h" -#include +#include +#include +#include +#include +#include +#include + +#include "rlimit_parser.h" #include "util.h" #if defined(__ANDROID__) +#include #include #include "selinux.h" @@ -28,11 +36,451 @@ #include "host_init_stubs.h" #endif +using android::base::ParseInt; +using android::base::Split; using android::base::StartsWith; namespace android { namespace init { +Result ServiceParser::ParseCapabilities(std::vector&& args) { + service_->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 >= service_->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(res); // |res| is >= 0. + if (cap > last_valid_cap) { + return Errorf("capability '{}' not supported by the kernel", arg); + } + (*service_->capabilities_)[cap] = true; + } + return {}; +} + +Result ServiceParser::ParseClass(std::vector&& args) { + service_->classnames_ = std::set(args.begin() + 1, args.end()); + return {}; +} + +Result ServiceParser::ParseConsole(std::vector&& args) { + service_->flags_ |= SVC_CONSOLE; + service_->proc_attr_.console = args.size() > 1 ? "/dev/" + args[1] : ""; + return {}; +} + +Result ServiceParser::ParseCritical(std::vector&& args) { + service_->flags_ |= SVC_CRITICAL; + return {}; +} + +Result ServiceParser::ParseDisabled(std::vector&& args) { + service_->flags_ |= SVC_DISABLED; + service_->flags_ |= SVC_RC_DISABLED; + return {}; +} + +Result ServiceParser::ParseEnterNamespace(std::vector&& args) { + if (args[1] != "net") { + return Error() << "Init only supports entering network namespaces"; + } + if (!service_->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. + service_->namespaces_.flags |= CLONE_NEWNS; + service_->namespaces_.namespaces_to_enter.emplace_back(CLONE_NEWNET, std::move(args[2])); + return {}; +} + +Result ServiceParser::ParseGroup(std::vector&& args) { + auto gid = DecodeUid(args[1]); + if (!gid) { + return Error() << "Unable to decode GID for '" << args[1] << "': " << gid.error(); + } + service_->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(); + } + service_->proc_attr_.supp_gids.emplace_back(*gid); + } + return {}; +} + +Result ServiceParser::ParsePriority(std::vector&& args) { + service_->proc_attr_.priority = 0; + if (!ParseInt(args[1], &service_->proc_attr_.priority, + static_cast(ANDROID_PRIORITY_HIGHEST), // highest is negative + static_cast(ANDROID_PRIORITY_LOWEST))) { + return Errorf("process priority value must be range {} - {}", ANDROID_PRIORITY_HIGHEST, + ANDROID_PRIORITY_LOWEST); + } + return {}; +} + +Result ServiceParser::ParseInterface(std::vector&& 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 : *service_list_) { + if (svc->interfaces().count(fullname) > 0) { + return Error() << "Interface '" << fullname << "' redefined in " << service_->name() + << " but is already defined by " << svc->name(); + } + } + + service_->interfaces_.insert(fullname); + + return {}; +} + +Result ServiceParser::ParseIoprio(std::vector&& args) { + if (!ParseInt(args[2], &service_->proc_attr_.ioprio_pri, 0, 7)) { + return Error() << "priority value must be range 0 - 7"; + } + + if (args[1] == "rt") { + service_->proc_attr_.ioprio_class = IoSchedClass_RT; + } else if (args[1] == "be") { + service_->proc_attr_.ioprio_class = IoSchedClass_BE; + } else if (args[1] == "idle") { + service_->proc_attr_.ioprio_class = IoSchedClass_IDLE; + } else { + return Error() << "ioprio option usage: ioprio <0-7>"; + } + + return {}; +} + +Result ServiceParser::ParseKeycodes(std::vector&& 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 : service_->keycodes_) { + if (key == code) return Error() << "duplicate keycode: " << *it; + } + service_->keycodes_.insert( + std::upper_bound(service_->keycodes_.begin(), service_->keycodes_.end(), code), + code); + } else { + return Error() << "invalid keycode: " << *it; + } + } + return {}; +} + +Result ServiceParser::ParseOneshot(std::vector&& args) { + service_->flags_ |= SVC_ONESHOT; + return {}; +} + +Result ServiceParser::ParseOnrestart(std::vector&& args) { + args.erase(args.begin()); + int line = service_->onrestart_.NumCommands() + 1; + if (auto result = service_->onrestart_.AddCommand(std::move(args), line); !result) { + return Error() << "cannot add Onrestart command: " << result.error(); + } + return {}; +} + +Result ServiceParser::ParseNamespace(std::vector&& args) { + for (size_t i = 1; i < args.size(); i++) { + if (args[i] == "pid") { + service_->namespaces_.flags |= CLONE_NEWPID; + // PID namespaces require mount namespaces. + service_->namespaces_.flags |= CLONE_NEWNS; + } else if (args[i] == "mnt") { + service_->namespaces_.flags |= CLONE_NEWNS; + } else { + return Error() << "namespace must be 'pid' or 'mnt'"; + } + } + return {}; +} + +Result ServiceParser::ParseOomScoreAdjust(std::vector&& args) { + if (!ParseInt(args[1], &service_->oom_score_adjust_, -1000, 1000)) { + return Error() << "oom_score_adjust value must be in range -1000 - +1000"; + } + return {}; +} + +Result ServiceParser::ParseOverride(std::vector&& args) { + service_->override_ = true; + return {}; +} + +Result ServiceParser::ParseMemcgSwappiness(std::vector&& args) { + if (!ParseInt(args[1], &service_->swappiness_, 0)) { + return Error() << "swappiness value must be equal or greater than 0"; + } + return {}; +} + +Result ServiceParser::ParseMemcgLimitInBytes(std::vector&& args) { + if (!ParseInt(args[1], &service_->limit_in_bytes_, 0)) { + return Error() << "limit_in_bytes value must be equal or greater than 0"; + } + return {}; +} + +Result ServiceParser::ParseMemcgLimitPercent(std::vector&& args) { + if (!ParseInt(args[1], &service_->limit_percent_, 0)) { + return Error() << "limit_percent value must be equal or greater than 0"; + } + return {}; +} + +Result ServiceParser::ParseMemcgLimitProperty(std::vector&& args) { + service_->limit_property_ = std::move(args[1]); + return {}; +} + +Result ServiceParser::ParseMemcgSoftLimitInBytes(std::vector&& args) { + if (!ParseInt(args[1], &service_->soft_limit_in_bytes_, 0)) { + return Error() << "soft_limit_in_bytes value must be equal or greater than 0"; + } + return {}; +} + +Result ServiceParser::ParseProcessRlimit(std::vector&& args) { + auto rlimit = ParseRlimit(args); + if (!rlimit) return rlimit.error(); + + service_->proc_attr_.rlimits.emplace_back(*rlimit); + return {}; +} + +Result ServiceParser::ParseRestartPeriod(std::vector&& args) { + int period; + if (!ParseInt(args[1], &period, 5)) { + return Error() << "restart_period value must be an integer >= 5"; + } + service_->restart_period_ = std::chrono::seconds(period); + return {}; +} + +Result ServiceParser::ParseSeclabel(std::vector&& args) { + service_->seclabel_ = std::move(args[1]); + return {}; +} + +Result ServiceParser::ParseSigstop(std::vector&& args) { + service_->sigstop_ = true; + return {}; +} + +Result ServiceParser::ParseSetenv(std::vector&& args) { + service_->environment_vars_.emplace_back(std::move(args[1]), std::move(args[2])); + return {}; +} + +Result ServiceParser::ParseShutdown(std::vector&& args) { + if (args[1] == "critical") { + service_->flags_ |= SVC_SHUTDOWN_CRITICAL; + return {}; + } + return Error() << "Invalid shutdown option"; +} + +Result ServiceParser::ParseTimeoutPeriod(std::vector&& args) { + int period; + if (!ParseInt(args[1], &period, 1)) { + return Error() << "timeout_period value must be an integer >= 1"; + } + service_->timeout_period_ = std::chrono::seconds(period); + return {}; +} + +template +Result ServiceParser::AddDescriptor(std::vector&& args) { + int perm = args.size() > 3 ? std::strtoul(args[3].c_str(), 0, 8) : -1; + Result uid = 0; + Result 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(args[1], args[2], *uid, *gid, perm, context); + + auto old = std::find_if( + service_->descriptors_.begin(), service_->descriptors_.end(), + [&descriptor](const auto& other) { return descriptor.get() == other.get(); }); + + if (old != service_->descriptors_.end()) { + return Error() << "duplicate descriptor " << args[1] << " " << args[2]; + } + + service_->descriptors_.emplace_back(std::move(descriptor)); + return {}; +} + +// name type perm [ uid gid context ] +Result ServiceParser::ParseSocket(std::vector&& 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(std::move(args)); +} + +// name type perm [ uid gid context ] +Result ServiceParser::ParseFile(std::vector&& 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(std::move(args)); +} + +Result ServiceParser::ParseUser(std::vector&& args) { + auto uid = DecodeUid(args[1]); + if (!uid) { + return Error() << "Unable to find UID for '" << args[1] << "': " << uid.error(); + } + service_->proc_attr_.uid = *uid; + return {}; +} + +Result ServiceParser::ParseWritepid(std::vector&& args) { + args.erase(args.begin()); + service_->writepid_files_ = std::move(args); + return {}; +} + +Result ServiceParser::ParseUpdatable(std::vector&& args) { + service_->updatable_ = true; + return {}; +} + +class ServiceParser::OptionParserMap : public KeywordMap { + public: + OptionParserMap() {} + + private: + const Map& map() const override; +}; + +const ServiceParser::OptionParserMap::Map& ServiceParser::OptionParserMap::map() const { + constexpr std::size_t kMax = std::numeric_limits::max(); + // clang-format off + static const Map option_parsers = { + {"capabilities", + {0, kMax, &ServiceParser::ParseCapabilities}}, + {"class", {1, kMax, &ServiceParser::ParseClass}}, + {"console", {0, 1, &ServiceParser::ParseConsole}}, + {"critical", {0, 0, &ServiceParser::ParseCritical}}, + {"disabled", {0, 0, &ServiceParser::ParseDisabled}}, + {"enter_namespace", + {2, 2, &ServiceParser::ParseEnterNamespace}}, + {"file", {2, 2, &ServiceParser::ParseFile}}, + {"group", {1, NR_SVC_SUPP_GIDS + 1, &ServiceParser::ParseGroup}}, + {"interface", {2, 2, &ServiceParser::ParseInterface}}, + {"ioprio", {2, 2, &ServiceParser::ParseIoprio}}, + {"keycodes", {1, kMax, &ServiceParser::ParseKeycodes}}, + {"memcg.limit_in_bytes", + {1, 1, &ServiceParser::ParseMemcgLimitInBytes}}, + {"memcg.limit_percent", + {1, 1, &ServiceParser::ParseMemcgLimitPercent}}, + {"memcg.limit_property", + {1, 1, &ServiceParser::ParseMemcgLimitProperty}}, + {"memcg.soft_limit_in_bytes", + {1, 1, &ServiceParser::ParseMemcgSoftLimitInBytes}}, + {"memcg.swappiness", + {1, 1, &ServiceParser::ParseMemcgSwappiness}}, + {"namespace", {1, 2, &ServiceParser::ParseNamespace}}, + {"oneshot", {0, 0, &ServiceParser::ParseOneshot}}, + {"onrestart", {1, kMax, &ServiceParser::ParseOnrestart}}, + {"oom_score_adjust", + {1, 1, &ServiceParser::ParseOomScoreAdjust}}, + {"override", {0, 0, &ServiceParser::ParseOverride}}, + {"priority", {1, 1, &ServiceParser::ParsePriority}}, + {"restart_period", + {1, 1, &ServiceParser::ParseRestartPeriod}}, + {"rlimit", {3, 3, &ServiceParser::ParseProcessRlimit}}, + {"seclabel", {1, 1, &ServiceParser::ParseSeclabel}}, + {"setenv", {2, 2, &ServiceParser::ParseSetenv}}, + {"shutdown", {1, 1, &ServiceParser::ParseShutdown}}, + {"sigstop", {0, 0, &ServiceParser::ParseSigstop}}, + {"socket", {3, 6, &ServiceParser::ParseSocket}}, + {"timeout_period", + {1, 1, &ServiceParser::ParseTimeoutPeriod}}, + {"updatable", {0, 0, &ServiceParser::ParseUpdatable}}, + {"user", {1, 1, &ServiceParser::ParseUser}}, + {"writepid", {1, kMax, &ServiceParser::ParseWritepid}}, + }; + // clang-format on + return option_parsers; +} + Result ServiceParser::ParseSection(std::vector&& args, const std::string& filename, int line) { if (args.size() < 3) { @@ -69,30 +517,41 @@ Result ServiceParser::ParseSection(std::vector&& args, } Result ServiceParser::ParseLineSection(std::vector&& args, int line) { - return service_ ? service_->ParseLine(std::move(args)) : Result{}; + if (!service_) { + return {}; + } + + 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 ServiceParser::EndSection() { - if (service_) { - Service* old_service = service_list_->FindService(service_->name()); - if (old_service) { - if (!service_->is_override()) { - return Error() << "ignored duplicate definition of service '" << service_->name() - << "'"; - } + if (!service_) { + return {}; + } - 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; + Service* old_service = service_list_->FindService(service_->name()); + if (old_service) { + if (!service_->is_override()) { + return Error() << "ignored duplicate definition of service '" << service_->name() + << "'"; } - service_list_->AddService(std::move(service_)); + 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; } + service_list_->AddService(std::move(service_)); + return {}; } diff --git a/init/service_parser.h b/init/service_parser.h index 8efe2ef46..0a5b29114 100644 --- a/init/service_parser.h +++ b/init/service_parser.h @@ -37,6 +37,46 @@ class ServiceParser : public SectionParser { void EndFile() override { filename_ = ""; } private: + using OptionParser = Result (ServiceParser::*)(std::vector&& args); + class OptionParserMap; + + Result ParseCapabilities(std::vector&& args); + Result ParseClass(std::vector&& args); + Result ParseConsole(std::vector&& args); + Result ParseCritical(std::vector&& args); + Result ParseDisabled(std::vector&& args); + Result ParseEnterNamespace(std::vector&& args); + Result ParseGroup(std::vector&& args); + Result ParsePriority(std::vector&& args); + Result ParseInterface(std::vector&& args); + Result ParseIoprio(std::vector&& args); + Result ParseKeycodes(std::vector&& args); + Result ParseOneshot(std::vector&& args); + Result ParseOnrestart(std::vector&& args); + Result ParseOomScoreAdjust(std::vector&& args); + Result ParseOverride(std::vector&& args); + Result ParseMemcgLimitInBytes(std::vector&& args); + Result ParseMemcgLimitPercent(std::vector&& args); + Result ParseMemcgLimitProperty(std::vector&& args); + Result ParseMemcgSoftLimitInBytes(std::vector&& args); + Result ParseMemcgSwappiness(std::vector&& args); + Result ParseNamespace(std::vector&& args); + Result ParseProcessRlimit(std::vector&& args); + Result ParseRestartPeriod(std::vector&& args); + Result ParseSeclabel(std::vector&& args); + Result ParseSetenv(std::vector&& args); + Result ParseShutdown(std::vector&& args); + Result ParseSigstop(std::vector&& args); + Result ParseSocket(std::vector&& args); + Result ParseTimeoutPeriod(std::vector&& args); + Result ParseFile(std::vector&& args); + Result ParseUser(std::vector&& args); + Result ParseWritepid(std::vector&& args); + Result ParseUpdatable(std::vector&& args); + + template + Result AddDescriptor(std::vector&& args); + bool IsValidName(const std::string& name) const; ServiceList* service_list_;