Merge "Make ext4 userdata checkpoints work with metadata encryption"
diff --git a/debuggerd/debuggerd_test.cpp b/debuggerd/debuggerd_test.cpp
index 64df53e..1f0e420 100644
--- a/debuggerd/debuggerd_test.cpp
+++ b/debuggerd/debuggerd_test.cpp
@@ -243,9 +243,10 @@
void CrasherTest::AssertDeath(int signo) {
int status;
- pid_t pid = TIMEOUT(5, waitpid(crasher_pid, &status, 0));
+ pid_t pid = TIMEOUT(10, waitpid(crasher_pid, &status, 0));
if (pid != crasher_pid) {
- printf("failed to wait for crasher (pid %d)\n", crasher_pid);
+ printf("failed to wait for crasher (expected pid %d, return value %d): %s\n", crasher_pid, pid,
+ strerror(errno));
sleep(100);
FAIL() << "failed to wait for crasher: " << strerror(errno);
}
diff --git a/init/Android.bp b/init/Android.bp
index 9c1ed15..6bc581d 100644
--- a/init/Android.bp
+++ b/init/Android.bp
@@ -128,6 +128,8 @@
"selabel.cpp",
"selinux.cpp",
"service.cpp",
+ "service_list.cpp",
+ "service_parser.cpp",
"service_utils.cpp",
"sigchld_handler.cpp",
"subcontext.cpp",
@@ -255,11 +257,12 @@
"import_parser.cpp",
"host_import_parser.cpp",
"host_init_verifier.cpp",
- "host_init_stubs.cpp",
"parser.cpp",
"rlimit_parser.cpp",
"tokenizer.cpp",
"service.cpp",
+ "service_list.cpp",
+ "service_parser.cpp",
"service_utils.cpp",
"subcontext.cpp",
"subcontext.proto",
diff --git a/init/builtins.cpp b/init/builtins.cpp
index d9b1b85..cc84aa0 100644
--- a/init/builtins.cpp
+++ b/init/builtins.cpp
@@ -63,7 +63,6 @@
#include "action_manager.h"
#include "bootchart.h"
-#include "host_init_stubs.h"
#include "init.h"
#include "mount_namespace.h"
#include "parser.h"
@@ -73,6 +72,7 @@
#include "selabel.h"
#include "selinux.h"
#include "service.h"
+#include "service_list.h"
#include "subcontext.h"
#include "util.h"
@@ -88,6 +88,8 @@
namespace android {
namespace init {
+std::vector<std::string> late_import_paths;
+
static constexpr std::chrono::nanoseconds kCommandRetryTimeout = 5s;
static Result<void> reboot_into_recovery(const std::vector<std::string>& options) {
diff --git a/init/builtins.h b/init/builtins.h
index 5db0d1c..7bbf6aa 100644
--- a/init/builtins.h
+++ b/init/builtins.h
@@ -40,6 +40,8 @@
const Map& map() const override;
};
+extern std::vector<std::string> late_import_paths;
+
} // namespace init
} // namespace android
diff --git a/init/devices.cpp b/init/devices.cpp
index 5e760d0..e8e6cd7 100644
--- a/init/devices.cpp
+++ b/init/devices.cpp
@@ -39,10 +39,6 @@
#include "selabel.h"
#include "util.h"
-#ifdef _INIT_INIT_H
-#error "Do not include init.h in files used by ueventd; it will expose init's globals"
-#endif
-
using namespace std::chrono_literals;
using android::base::Basename;
diff --git a/init/host_init_stubs.cpp b/init/host_init_stubs.cpp
deleted file mode 100644
index b85e54a..0000000
--- a/init/host_init_stubs.cpp
+++ /dev/null
@@ -1,57 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "host_init_stubs.h"
-
-#include <android-base/properties.h>
-
-// unistd.h
-int setgroups(size_t __size, const gid_t* __list) {
- return 0;
-}
-
-namespace android {
-namespace init {
-
-// init.h
-std::string default_console = "/dev/console";
-
-// property_service.h
-bool CanReadProperty(const std::string& source_context, const std::string& name) {
- return true;
-}
-uint32_t SetProperty(const std::string& key, const std::string& value) {
- android::base::SetProperty(key, value);
- return 0;
-}
-uint32_t (*property_set)(const std::string& name, const std::string& value) = SetProperty;
-uint32_t HandlePropertySet(const std::string&, const std::string&, const std::string&, const ucred&,
- std::string*) {
- return 0;
-}
-
-// selinux.h
-int SelinuxGetVendorAndroidVersion() {
- return 10000;
-}
-void SelabelInitialize() {}
-
-bool SelabelLookupFileContext(const std::string& key, int type, std::string* result) {
- return false;
-}
-
-} // namespace init
-} // namespace android
diff --git a/init/host_init_stubs.h b/init/host_init_stubs.h
index f6e9676..7c0544a 100644
--- a/init/host_init_stubs.h
+++ b/init/host_init_stubs.h
@@ -14,8 +14,7 @@
* limitations under the License.
*/
-#ifndef _INIT_HOST_INIT_STUBS_H
-#define _INIT_HOST_INIT_STUBS_H
+#pragma once
#include <stddef.h>
#include <sys/socket.h>
@@ -23,26 +22,30 @@
#include <string>
+#include <android-base/properties.h>
+
// android/api-level.h
#define __ANDROID_API_P__ 28
// sys/system_properties.h
#define PROP_VALUE_MAX 92
-// unistd.h
-int setgroups(size_t __size, const gid_t* __list);
-
namespace android {
namespace init {
-// init.h
-extern std::string default_console;
-
// property_service.h
-bool CanReadProperty(const std::string& source_context, const std::string& name);
-extern uint32_t (*property_set)(const std::string& name, const std::string& value);
-uint32_t HandlePropertySet(const std::string& name, const std::string& value,
- const std::string& source_context, const ucred& cr, std::string* error);
+inline bool CanReadProperty(const std::string&, const std::string&) {
+ return true;
+}
+inline uint32_t SetProperty(const std::string& key, const std::string& value) {
+ android::base::SetProperty(key, value);
+ return 0;
+}
+inline uint32_t (*property_set)(const std::string& name, const std::string& value) = SetProperty;
+inline uint32_t HandlePropertySet(const std::string&, const std::string&, const std::string&,
+ const ucred&, std::string*) {
+ return 0;
+}
// reboot_utils.h
inline void SetFatalRebootTarget() {}
@@ -50,12 +53,16 @@
abort();
}
+// selabel.h
+inline void SelabelInitialize() {}
+inline bool SelabelLookupFileContext(const std::string&, int, std::string*) {
+ return false;
+}
+
// selinux.h
-int SelinuxGetVendorAndroidVersion();
-void SelabelInitialize();
-bool SelabelLookupFileContext(const std::string& key, int type, std::string* result);
+inline int SelinuxGetVendorAndroidVersion() {
+ return 10000;
+}
} // namespace init
} // namespace android
-
-#endif
diff --git a/init/host_init_verifier.cpp b/init/host_init_verifier.cpp
index cb861f3..1b4716f 100644
--- a/init/host_init_verifier.cpp
+++ b/init/host_init_verifier.cpp
@@ -36,6 +36,8 @@
#include "parser.h"
#include "result.h"
#include "service.h"
+#include "service_list.h"
+#include "service_parser.h"
#define EXCLUDE_FS_CONFIG_STRUCTURES
#include "generated_android_ids.h"
diff --git a/init/init.cpp b/init/init.cpp
index 0d3b99f..b6911e5 100644
--- a/init/init.cpp
+++ b/init/init.cpp
@@ -28,9 +28,11 @@
#include <sys/types.h>
#include <unistd.h>
+#include <functional>
#include <map>
#include <memory>
#include <optional>
+#include <vector>
#include <android-base/chrono_utils.h>
#include <android-base/file.h>
@@ -54,6 +56,7 @@
#include "action_parser.h"
#include "boringssl_self_test.h"
+#include "builtins.h"
#include "epoll.h"
#include "first_stage_init.h"
#include "first_stage_mount.h"
@@ -67,6 +70,8 @@
#include "security.h"
#include "selabel.h"
#include "selinux.h"
+#include "service.h"
+#include "service_parser.h"
#include "sigchld_handler.h"
#include "util.h"
@@ -88,8 +93,6 @@
static char qemu[32];
-std::string default_console = "/dev/console";
-
static int signal_fd = -1;
static std::unique_ptr<Timer> waiting_for_prop(nullptr);
@@ -100,8 +103,6 @@
static bool do_shutdown = false;
static bool load_debug_prop = false;
-std::vector<std::string> late_import_paths;
-
static std::vector<Subcontext>* subcontexts;
void DumpState() {
@@ -346,14 +347,6 @@
return {};
}
-static Result<void> console_init_action(const BuiltinArguments& args) {
- std::string console = GetProperty("ro.boot.console", "");
- if (!console.empty()) {
- default_console = "/dev/" + console;
- }
- return {};
-}
-
static Result<void> SetupCgroupsAction(const BuiltinArguments&) {
// Have to create <CGROUPS_RC_DIR> using make_dir function
// for appropriate sepolicy to be set for it
@@ -760,7 +753,6 @@
return {};
},
"KeychordInit");
- am.QueueBuiltinAction(console_init_action, "console_init");
// Trigger all the boot actions to get us started.
am.QueueEventTrigger("init");
diff --git a/init/init.h b/init/init.h
index 90ead0e..cfc28f1 100644
--- a/init/init.h
+++ b/init/init.h
@@ -14,29 +14,20 @@
* limitations under the License.
*/
-#ifndef _INIT_INIT_H
-#define _INIT_INIT_H
+#pragma once
#include <sys/types.h>
-#include <functional>
#include <string>
-#include <vector>
#include "action.h"
#include "action_manager.h"
#include "parser.h"
-#include "service.h"
+#include "service_list.h"
namespace android {
namespace init {
-// Note: These globals are *only* valid in init, so they should not be used in ueventd
-// or any files that may be included in ueventd, such as devices.cpp and util.cpp.
-// TODO: Have an Init class and remove all globals.
-extern std::string default_console;
-extern std::vector<std::string> late_import_paths;
-
Parser CreateParser(ActionManager& action_manager, ServiceList& service_list);
Parser CreateServiceOnlyParser(ServiceList& service_list);
@@ -54,5 +45,3 @@
} // namespace init
} // namespace android
-
-#endif /* _INIT_INIT_H */
diff --git a/init/init_test.cpp b/init/init_test.cpp
index 18c2b38..1bcc5ef 100644
--- a/init/init_test.cpp
+++ b/init/init_test.cpp
@@ -27,6 +27,8 @@
#include "keyword_map.h"
#include "parser.h"
#include "service.h"
+#include "service_list.h"
+#include "service_parser.h"
#include "test_function_map.h"
#include "util.h"
diff --git a/init/reboot.cpp b/init/reboot.cpp
index eaba3cc..d9d885c 100644
--- a/init/reboot.cpp
+++ b/init/reboot.cpp
@@ -55,6 +55,7 @@
#include "property_service.h"
#include "reboot_utils.h"
#include "service.h"
+#include "service_list.h"
#include "sigchld_handler.h"
#define PROC_SYSRQ "/proc/sysrq-trigger"
diff --git a/init/service.cpp b/init/service.cpp
index 4fe374c..cd08f3d 100644
--- a/init/service.cpp
+++ b/init/service.cpp
@@ -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,27 +28,20 @@
#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
@@ -58,8 +49,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;
@@ -315,450 +304,6 @@
[] (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()) {
@@ -813,7 +358,7 @@
bool needs_console = (flags_ & SVC_CONSOLE);
if (needs_console) {
if (proc_attr_.console.empty()) {
- proc_attr_.console = default_console;
+ proc_attr_.console = "/dev/" + GetProperty("ro.boot.console", "console");
}
// Make sure that open call succeeds to ensure a console driver is
@@ -1072,17 +617,6 @@
}
}
-ServiceList::ServiceList() {}
-
-ServiceList& ServiceList::GetInstance() {
- static ServiceList instance;
- return instance;
-}
-
-void ServiceList::AddService(std::unique_ptr<Service> service) {
- services_.emplace_back(std::move(service));
-}
-
std::unique_ptr<Service> Service::MakeTemporaryOneshotService(const std::vector<std::string>& args) {
// Parse the arguments: exec [SECLABEL [UID [GID]*] --] COMMAND ARGS...
// SECLABEL can be a - to denote default
@@ -1147,139 +681,5 @@
nullptr, str_args);
}
-// Shutdown services in the opposite order that they were started.
-const std::vector<Service*> ServiceList::services_in_shutdown_order() const {
- std::vector<Service*> shutdown_services;
- for (const auto& service : services_) {
- if (service->start_order() > 0) shutdown_services.emplace_back(service.get());
- }
- std::sort(shutdown_services.begin(), shutdown_services.end(),
- [](const auto& a, const auto& b) { return a->start_order() > b->start_order(); });
- return shutdown_services;
-}
-
-void ServiceList::RemoveService(const Service& svc) {
- auto svc_it = std::find_if(services_.begin(), services_.end(),
- [&svc] (const std::unique_ptr<Service>& s) {
- return svc.name() == s->name();
- });
- if (svc_it == services_.end()) {
- return;
- }
-
- services_.erase(svc_it);
-}
-
-void ServiceList::DumpState() const {
- for (const auto& s : services_) {
- s->DumpState();
- }
-}
-
-void ServiceList::MarkPostData() {
- post_data_ = true;
-}
-
-bool ServiceList::IsPostData() {
- return post_data_;
-}
-
-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().message();
- }
- }
- 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<void> ServiceParser::ParseSection(std::vector<std::string>&& args,
- const std::string& filename, int line) {
- if (args.size() < 3) {
- return Error() << "services must have a name and a program";
- }
-
- const std::string& name = args[1];
- if (!IsValidName(name)) {
- return Error() << "invalid service name '" << name << "'";
- }
-
- filename_ = filename;
-
- Subcontext* restart_action_subcontext = nullptr;
- if (subcontexts_) {
- for (auto& subcontext : *subcontexts_) {
- if (StartsWith(filename, subcontext.path_prefix())) {
- restart_action_subcontext = &subcontext;
- break;
- }
- }
- }
-
- std::vector<std::string> str_args(args.begin() + 2, args.end());
-
- if (SelinuxGetVendorAndroidVersion() <= __ANDROID_API_P__) {
- if (str_args[0] == "/sbin/watchdogd") {
- str_args[0] = "/system/bin/watchdogd";
- }
- }
-
- service_ = std::make_unique<Service>(name, restart_action_subcontext, str_args);
- return {};
-}
-
-Result<void> ServiceParser::ParseLineSection(std::vector<std::string>&& args, int line) {
- return service_ ? service_->ParseLine(std::move(args)) : Result<void>{};
-}
-
-Result<void> 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 (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 {};
-}
-
-bool ServiceParser::IsValidName(const std::string& name) const {
- // Property names can be any length, but may only contain certain characters.
- // Property values can contain any characters, but may only be a certain length.
- // (The latter restriction is needed because `start` and `stop` work by writing
- // the service name to the "ctl.start" and "ctl.stop" properties.)
- return IsLegalPropertyName("init.svc." + name) && name.size() <= PROP_VALUE_MAX;
-}
-
} // namespace init
} // namespace android
diff --git a/init/service.h b/init/service.h
index b4356c8..78f94ce 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 <signal.h>
-#include <sys/resource.h>
#include <sys/types.h>
#include <chrono>
@@ -64,6 +62,8 @@
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 @@
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 @@
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_;
@@ -238,79 +197,5 @@
bool running_at_post_data_reset_ = false;
};
-class ServiceList {
- public:
- static ServiceList& GetInstance();
-
- // Exposed for testing
- ServiceList();
-
- void AddService(std::unique_ptr<Service> service);
- void RemoveService(const Service& svc);
-
- template <typename T, typename F = decltype(&Service::name)>
- Service* FindService(T value, F function = &Service::name) const {
- auto svc = std::find_if(services_.begin(), services_.end(),
- [&function, &value](const std::unique_ptr<Service>& s) {
- return std::invoke(function, s) == value;
- });
- if (svc != services_.end()) {
- return svc->get();
- }
- return nullptr;
- }
-
- Service* FindInterface(const std::string& interface_name) {
- for (const auto& svc : services_) {
- if (svc->interfaces().count(interface_name) > 0) {
- return svc.get();
- }
- }
-
- return nullptr;
- }
-
- void DumpState() const;
-
- auto begin() const { return services_.begin(); }
- auto end() const { return services_.end(); }
- const std::vector<std::unique_ptr<Service>>& services() const { return services_; }
- const std::vector<Service*> services_in_shutdown_order() const;
-
- void MarkPostData();
- bool IsPostData();
- void MarkServicesUpdate();
- bool IsServicesUpdated() const { return services_update_finished_; }
- void DelayService(const Service& service);
-
- private:
- std::vector<std::unique_ptr<Service>> services_;
-
- bool post_data_ = false;
- bool services_update_finished_ = false;
- std::vector<std::string> delayed_service_names_;
-};
-
-class ServiceParser : public SectionParser {
- public:
- ServiceParser(ServiceList* service_list, std::vector<Subcontext>* subcontexts)
- : service_list_(service_list), subcontexts_(subcontexts), service_(nullptr) {}
- Result<void> ParseSection(std::vector<std::string>&& args, const std::string& filename,
- int line) override;
- Result<void> ParseLineSection(std::vector<std::string>&& args, int line) override;
- Result<void> EndSection() override;
- void EndFile() override { filename_ = ""; }
-
- private:
- bool IsValidName(const std::string& name) const;
-
- ServiceList* service_list_;
- std::vector<Subcontext>* subcontexts_;
- std::unique_ptr<Service> service_;
- std::string filename_;
-};
-
} // namespace init
} // namespace android
-
-#endif
diff --git a/init/service_list.cpp b/init/service_list.cpp
new file mode 100644
index 0000000..3a48183
--- /dev/null
+++ b/init/service_list.cpp
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "service_list.h"
+
+#include <android-base/logging.h>
+
+namespace android {
+namespace init {
+
+ServiceList::ServiceList() {}
+
+ServiceList& ServiceList::GetInstance() {
+ static ServiceList instance;
+ return instance;
+}
+
+void ServiceList::AddService(std::unique_ptr<Service> service) {
+ services_.emplace_back(std::move(service));
+}
+
+// Shutdown services in the opposite order that they were started.
+const std::vector<Service*> ServiceList::services_in_shutdown_order() const {
+ std::vector<Service*> shutdown_services;
+ for (const auto& service : services_) {
+ if (service->start_order() > 0) shutdown_services.emplace_back(service.get());
+ }
+ std::sort(shutdown_services.begin(), shutdown_services.end(),
+ [](const auto& a, const auto& b) { return a->start_order() > b->start_order(); });
+ return shutdown_services;
+}
+
+void ServiceList::RemoveService(const Service& svc) {
+ auto svc_it = std::find_if(
+ services_.begin(), services_.end(),
+ [&svc](const std::unique_ptr<Service>& s) { return svc.name() == s->name(); });
+ if (svc_it == services_.end()) {
+ return;
+ }
+
+ services_.erase(svc_it);
+}
+
+void ServiceList::DumpState() const {
+ for (const auto& s : services_) {
+ s->DumpState();
+ }
+}
+
+void ServiceList::MarkPostData() {
+ post_data_ = true;
+}
+
+bool ServiceList::IsPostData() {
+ return post_data_;
+}
+
+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().message();
+ }
+ }
+ 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());
+}
+
+} // namespace init
+} // namespace android
diff --git a/init/service_list.h b/init/service_list.h
new file mode 100644
index 0000000..2136a21
--- /dev/null
+++ b/init/service_list.h
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <memory>
+#include <vector>
+
+#include "service.h"
+
+namespace android {
+namespace init {
+
+class ServiceList {
+ public:
+ static ServiceList& GetInstance();
+
+ // Exposed for testing
+ ServiceList();
+
+ void AddService(std::unique_ptr<Service> service);
+ void RemoveService(const Service& svc);
+
+ template <typename T, typename F = decltype(&Service::name)>
+ Service* FindService(T value, F function = &Service::name) const {
+ auto svc = std::find_if(services_.begin(), services_.end(),
+ [&function, &value](const std::unique_ptr<Service>& s) {
+ return std::invoke(function, s) == value;
+ });
+ if (svc != services_.end()) {
+ return svc->get();
+ }
+ return nullptr;
+ }
+
+ Service* FindInterface(const std::string& interface_name) {
+ for (const auto& svc : services_) {
+ if (svc->interfaces().count(interface_name) > 0) {
+ return svc.get();
+ }
+ }
+
+ return nullptr;
+ }
+
+ void DumpState() const;
+
+ auto begin() const { return services_.begin(); }
+ auto end() const { return services_.end(); }
+ const std::vector<std::unique_ptr<Service>>& services() const { return services_; }
+ const std::vector<Service*> services_in_shutdown_order() const;
+
+ void MarkPostData();
+ bool IsPostData();
+ void MarkServicesUpdate();
+ bool IsServicesUpdated() const { return services_update_finished_; }
+ void DelayService(const Service& service);
+
+ private:
+ std::vector<std::unique_ptr<Service>> services_;
+
+ bool post_data_ = false;
+ bool services_update_finished_ = false;
+ std::vector<std::string> delayed_service_names_;
+};
+
+} // namespace init
+} // namespace android
diff --git a/init/service_parser.cpp b/init/service_parser.cpp
new file mode 100644
index 0000000..33ed050
--- /dev/null
+++ b/init/service_parser.cpp
@@ -0,0 +1,567 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "service_parser.h"
+
+#include <linux/input.h>
+
+#include <android-base/logging.h>
+#include <android-base/parseint.h>
+#include <android-base/strings.h>
+#include <hidl-util/FQName.h>
+#include <system/thread_defs.h>
+
+#include "rlimit_parser.h"
+#include "util.h"
+
+#if defined(__ANDROID__)
+#include <android/api-level.h>
+#include <sys/system_properties.h>
+
+#include "selinux.h"
+#else
+#include "host_init_stubs.h"
+#endif
+
+using android::base::ParseInt;
+using android::base::Split;
+using android::base::StartsWith;
+
+namespace android {
+namespace init {
+
+Result<void> ServiceParser::ParseCapabilities(std::vector<std::string>&& 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<unsigned int>(res); // |res| is >= 0.
+ if (cap > last_valid_cap) {
+ return Errorf("capability '{}' not supported by the kernel", arg);
+ }
+ (*service_->capabilities_)[cap] = true;
+ }
+ return {};
+}
+
+Result<void> ServiceParser::ParseClass(std::vector<std::string>&& args) {
+ service_->classnames_ = std::set<std::string>(args.begin() + 1, args.end());
+ return {};
+}
+
+Result<void> ServiceParser::ParseConsole(std::vector<std::string>&& args) {
+ service_->flags_ |= SVC_CONSOLE;
+ service_->proc_attr_.console = args.size() > 1 ? "/dev/" + args[1] : "";
+ return {};
+}
+
+Result<void> ServiceParser::ParseCritical(std::vector<std::string>&& args) {
+ service_->flags_ |= SVC_CRITICAL;
+ return {};
+}
+
+Result<void> ServiceParser::ParseDisabled(std::vector<std::string>&& args) {
+ service_->flags_ |= SVC_DISABLED;
+ service_->flags_ |= SVC_RC_DISABLED;
+ return {};
+}
+
+Result<void> ServiceParser::ParseEnterNamespace(std::vector<std::string>&& 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<void> ServiceParser::ParseGroup(std::vector<std::string>&& 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<void> ServiceParser::ParsePriority(std::vector<std::string>&& args) {
+ service_->proc_attr_.priority = 0;
+ if (!ParseInt(args[1], &service_->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> ServiceParser::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 : *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<void> ServiceParser::ParseIoprio(std::vector<std::string>&& 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 <rt|be|idle> <0-7>";
+ }
+
+ return {};
+}
+
+Result<void> ServiceParser::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 : 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<void> ServiceParser::ParseOneshot(std::vector<std::string>&& args) {
+ service_->flags_ |= SVC_ONESHOT;
+ return {};
+}
+
+Result<void> ServiceParser::ParseOnrestart(std::vector<std::string>&& 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<void> ServiceParser::ParseNamespace(std::vector<std::string>&& 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<void> ServiceParser::ParseOomScoreAdjust(std::vector<std::string>&& 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<void> ServiceParser::ParseOverride(std::vector<std::string>&& args) {
+ service_->override_ = true;
+ return {};
+}
+
+Result<void> ServiceParser::ParseMemcgSwappiness(std::vector<std::string>&& args) {
+ if (!ParseInt(args[1], &service_->swappiness_, 0)) {
+ return Error() << "swappiness value must be equal or greater than 0";
+ }
+ return {};
+}
+
+Result<void> ServiceParser::ParseMemcgLimitInBytes(std::vector<std::string>&& 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<void> ServiceParser::ParseMemcgLimitPercent(std::vector<std::string>&& args) {
+ if (!ParseInt(args[1], &service_->limit_percent_, 0)) {
+ return Error() << "limit_percent value must be equal or greater than 0";
+ }
+ return {};
+}
+
+Result<void> ServiceParser::ParseMemcgLimitProperty(std::vector<std::string>&& args) {
+ service_->limit_property_ = std::move(args[1]);
+ return {};
+}
+
+Result<void> ServiceParser::ParseMemcgSoftLimitInBytes(std::vector<std::string>&& 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<void> ServiceParser::ParseProcessRlimit(std::vector<std::string>&& args) {
+ auto rlimit = ParseRlimit(args);
+ if (!rlimit) return rlimit.error();
+
+ service_->proc_attr_.rlimits.emplace_back(*rlimit);
+ return {};
+}
+
+Result<void> ServiceParser::ParseRestartPeriod(std::vector<std::string>&& 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<void> ServiceParser::ParseSeclabel(std::vector<std::string>&& args) {
+ service_->seclabel_ = std::move(args[1]);
+ return {};
+}
+
+Result<void> ServiceParser::ParseSigstop(std::vector<std::string>&& args) {
+ service_->sigstop_ = true;
+ return {};
+}
+
+Result<void> ServiceParser::ParseSetenv(std::vector<std::string>&& args) {
+ service_->environment_vars_.emplace_back(std::move(args[1]), std::move(args[2]));
+ return {};
+}
+
+Result<void> ServiceParser::ParseShutdown(std::vector<std::string>&& args) {
+ if (args[1] == "critical") {
+ service_->flags_ |= SVC_SHUTDOWN_CRITICAL;
+ return {};
+ }
+ return Error() << "Invalid shutdown option";
+}
+
+Result<void> ServiceParser::ParseTimeoutPeriod(std::vector<std::string>&& 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 <typename T>
+Result<void> ServiceParser::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(
+ 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<void> ServiceParser::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> ServiceParser::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> ServiceParser::ParseUser(std::vector<std::string>&& 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<void> ServiceParser::ParseWritepid(std::vector<std::string>&& args) {
+ args.erase(args.begin());
+ service_->writepid_files_ = std::move(args);
+ return {};
+}
+
+Result<void> ServiceParser::ParseUpdatable(std::vector<std::string>&& args) {
+ service_->updatable_ = true;
+ return {};
+}
+
+class ServiceParser::OptionParserMap : public KeywordMap<OptionParser> {
+ public:
+ OptionParserMap() {}
+
+ private:
+ const Map& map() const override;
+};
+
+const ServiceParser::OptionParserMap::Map& ServiceParser::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, &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<void> ServiceParser::ParseSection(std::vector<std::string>&& args,
+ const std::string& filename, int line) {
+ if (args.size() < 3) {
+ return Error() << "services must have a name and a program";
+ }
+
+ const std::string& name = args[1];
+ if (!IsValidName(name)) {
+ return Error() << "invalid service name '" << name << "'";
+ }
+
+ filename_ = filename;
+
+ Subcontext* restart_action_subcontext = nullptr;
+ if (subcontexts_) {
+ for (auto& subcontext : *subcontexts_) {
+ if (StartsWith(filename, subcontext.path_prefix())) {
+ restart_action_subcontext = &subcontext;
+ break;
+ }
+ }
+ }
+
+ std::vector<std::string> str_args(args.begin() + 2, args.end());
+
+ if (SelinuxGetVendorAndroidVersion() <= __ANDROID_API_P__) {
+ if (str_args[0] == "/sbin/watchdogd") {
+ str_args[0] = "/system/bin/watchdogd";
+ }
+ }
+
+ service_ = std::make_unique<Service>(name, restart_action_subcontext, str_args);
+ return {};
+}
+
+Result<void> ServiceParser::ParseLineSection(std::vector<std::string>&& args, int line) {
+ 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<void> ServiceParser::EndSection() {
+ if (!service_) {
+ return {};
+ }
+
+ 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 (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 {};
+}
+
+bool ServiceParser::IsValidName(const std::string& name) const {
+ // Property names can be any length, but may only contain certain characters.
+ // Property values can contain any characters, but may only be a certain length.
+ // (The latter restriction is needed because `start` and `stop` work by writing
+ // the service name to the "ctl.start" and "ctl.stop" properties.)
+ return IsLegalPropertyName("init.svc." + name) && name.size() <= PROP_VALUE_MAX;
+}
+
+} // namespace init
+} // namespace android
diff --git a/init/service_parser.h b/init/service_parser.h
new file mode 100644
index 0000000..0a5b291
--- /dev/null
+++ b/init/service_parser.h
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <vector>
+
+#include "parser.h"
+#include "service.h"
+#include "service_list.h"
+#include "subcontext.h"
+
+namespace android {
+namespace init {
+
+class ServiceParser : public SectionParser {
+ public:
+ ServiceParser(ServiceList* service_list, std::vector<Subcontext>* subcontexts)
+ : service_list_(service_list), subcontexts_(subcontexts), service_(nullptr) {}
+ Result<void> ParseSection(std::vector<std::string>&& args, const std::string& filename,
+ int line) override;
+ Result<void> ParseLineSection(std::vector<std::string>&& args, int line) override;
+ Result<void> EndSection() override;
+ 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_;
+ std::vector<Subcontext>* subcontexts_;
+ std::unique_ptr<Service> service_;
+ std::string filename_;
+};
+
+} // namespace init
+} // namespace android
diff --git a/init/sigchld_handler.cpp b/init/sigchld_handler.cpp
index 987b2f9..c9a09cd 100644
--- a/init/sigchld_handler.cpp
+++ b/init/sigchld_handler.cpp
@@ -30,6 +30,7 @@
#include "init.h"
#include "service.h"
+#include "service_list.h"
using android::base::StringPrintf;
using android::base::boot_clock;
diff --git a/init/util.cpp b/init/util.cpp
index 14acaa2..2b34242 100644
--- a/init/util.cpp
+++ b/init/util.cpp
@@ -46,10 +46,6 @@
#include "host_init_stubs.h"
#endif
-#ifdef _INIT_INIT_H
-#error "Do not include init.h in files used by ueventd; it will expose init's globals"
-#endif
-
using android::base::boot_clock;
using namespace std::literals::string_literals;
diff --git a/libmeminfo/libdmabufinfo/tools/dmabuf_dump.cpp b/libmeminfo/libdmabufinfo/tools/dmabuf_dump.cpp
index 0851fb3..9a80c82 100644
--- a/libmeminfo/libdmabufinfo/tools/dmabuf_dump.cpp
+++ b/libmeminfo/libdmabufinfo/tools/dmabuf_dump.cpp
@@ -17,16 +17,17 @@
#include <dirent.h>
#include <errno.h>
#include <inttypes.h>
+#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
-#include <iostream>
#include <fstream>
+#include <iostream>
+#include <map>
+#include <set>
#include <sstream>
#include <string>
#include <vector>
-#include <map>
-#include <set>
#include <android-base/stringprintf.h>
#include <dmabufinfo/dmabufinfo.h>
@@ -43,7 +44,7 @@
exit(exit_status);
}
-static std::string GetProcessBaseName(pid_t pid) {
+static std::string GetProcessComm(const pid_t pid) {
std::string pid_path = android::base::StringPrintf("/proc/%d/comm", pid);
std::ifstream in{pid_path};
if (!in) return std::string("N/A");
@@ -53,85 +54,76 @@
return line;
}
-static void AddPidsToSet(const std::unordered_map<pid_t, int>& map, std::set<pid_t>* set)
-{
- for (auto it = map.begin(); it != map.end(); ++it)
- set->insert(it->first);
+static void AddPidsToSet(const std::unordered_map<pid_t, int>& map, std::set<pid_t>* set) {
+ for (auto it = map.begin(); it != map.end(); ++it) set->insert(it->first);
}
static void PrintDmaBufInfo(const std::vector<DmaBuffer>& bufs) {
- std::set<pid_t> pid_set;
- std::map<pid_t, int> pid_column;
-
if (bufs.empty()) {
- std::cout << "dmabuf info not found ¯\\_(ツ)_/¯" << std::endl;
+ printf("dmabuf info not found ¯\\_(ツ)_/¯\n");
return;
}
// Find all unique pids in the input vector, create a set
+ std::set<pid_t> pid_set;
for (int i = 0; i < bufs.size(); i++) {
AddPidsToSet(bufs[i].fdrefs(), &pid_set);
AddPidsToSet(bufs[i].maprefs(), &pid_set);
}
- int pid_count = 0;
-
- std::cout << "\t\t\t\t\t\t";
-
- // Create a map to convert each unique pid into a column number
- for (auto it = pid_set.begin(); it != pid_set.end(); ++it, ++pid_count) {
- pid_column.insert(std::make_pair(*it, pid_count));
- std::cout << ::android::base::StringPrintf("[pid: % 4d]\t", *it);
+ // Format the header string spaced and separated with '|'
+ printf(" Dmabuf Inode | Size | Ref Counts |");
+ for (auto pid : pid_set) {
+ printf("%16s:%-5d |", GetProcessComm(pid).c_str(), pid);
}
+ printf("\n");
- std::cout << std::endl << "\t\t\t\t\t\t";
+ // holds per-process dmabuf size in kB
+ std::map<pid_t, uint64_t> per_pid_size = {};
+ uint64_t dmabuf_total_size = 0;
- for (auto it = pid_set.begin(); it != pid_set.end(); ++it) {
- std::cout << ::android::base::StringPrintf("%16s",
- GetProcessBaseName(*it).c_str());
- }
+ // Iterate through all dmabufs and collect per-process sizes, refs
+ for (auto& buf : bufs) {
+ printf("%16ju |%13" PRIu64 " kB |%16" PRIu64 " |", static_cast<uintmax_t>(buf.inode()),
+ buf.size() / 1024, buf.total_refs());
+ // Iterate through each process to find out per-process references for each buffer,
+ // gather total size used by each process etc.
+ for (pid_t pid : pid_set) {
+ int pid_refs = 0;
+ if (buf.fdrefs().count(pid) == 1) {
+ // Get the total number of ref counts the process is holding
+ // on this buffer. We don't differentiate between mmap or fd.
+ pid_refs += buf.fdrefs().at(pid);
+ if (buf.maprefs().count(pid) == 1) {
+ pid_refs += buf.maprefs().at(pid);
+ }
+ }
- std::cout << std::endl << "\tinode\t\tsize\t\tcount\t";
- for (int i = 0; i < pid_count; i++) {
- std::cout << "fd\tmap\t";
- }
- std::cout << std::endl;
-
- auto fds = std::make_unique<int[]>(pid_count);
- auto maps = std::make_unique<int[]>(pid_count);
- auto pss = std::make_unique<long[]>(pid_count);
-
- memset(pss.get(), 0, sizeof(long) * pid_count);
-
- for (auto buf = bufs.begin(); buf != bufs.end(); ++buf) {
-
- std::cout << ::android::base::StringPrintf("%16lu\t%10" PRIu64 "\t%" PRIu64 "\t",
- buf->inode(),buf->size(), buf->count());
-
- memset(fds.get(), 0, sizeof(int) * pid_count);
- memset(maps.get(), 0, sizeof(int) * pid_count);
-
- for (auto it = buf->fdrefs().begin(); it != buf->fdrefs().end(); ++it) {
- fds[pid_column[it->first]] = it->second;
- pss[pid_column[it->first]] += buf->size() * it->second / buf->count();
+ if (pid_refs) {
+ // Add up the per-pid total size. Note that if a buffer is mapped
+ // in 2 different processes, the size will be shown as mapped or opened
+ // in both processes. This is intended for visibility.
+ //
+ // If one wants to get the total *unique* dma buffers, they can simply
+ // sum the size of all dma bufs shown by the tool
+ per_pid_size[pid] += buf.size() / 1024;
+ printf("%17d refs |", pid_refs);
+ } else {
+ printf("%22s |", "--");
+ }
}
-
- for (auto it = buf->maprefs().begin(); it != buf->maprefs().end(); ++it) {
- maps[pid_column[it->first]] = it->second;
- pss[pid_column[it->first]] += buf->size() * it->second / buf->count();
- }
-
- for (int i = 0; i < pid_count; i++) {
- std::cout << ::android::base::StringPrintf("%d\t%d\t", fds[i], maps[i]);
- }
- std::cout << std::endl;
+ dmabuf_total_size += buf.size() / 1024;
+ printf("\n");
}
- std::cout << "-----------------------------------------" << std::endl;
- std::cout << "PSS ";
- for (int i = 0; i < pid_count; i++) {
- std::cout << ::android::base::StringPrintf("%15ldK", pss[i] / 1024);
+
+ printf("------------------------------------\n");
+ printf("%-16s %13" PRIu64 " kB |%16s |", "TOTALS", dmabuf_total_size, "n/a");
+ for (auto pid : pid_set) {
+ printf("%19" PRIu64 " kB |", per_pid_size[pid]);
}
- std::cout << std::endl;
+ printf("\n");
+
+ return;
}
int main(int argc, char* argv[]) {
@@ -142,8 +134,7 @@
if (argc > 1) {
if (sscanf(argv[1], "%d", &pid) == 1) {
show_all = false;
- }
- else {
+ } else {
usage(EXIT_FAILURE);
}
}
@@ -178,8 +169,7 @@
exit(EXIT_FAILURE);
}
}
+
PrintDmaBufInfo(bufs);
return 0;
}
-
-
diff --git a/libmeminfo/pageacct.cpp b/libmeminfo/pageacct.cpp
index 0a26c08..cb17af8 100644
--- a/libmeminfo/pageacct.cpp
+++ b/libmeminfo/pageacct.cpp
@@ -81,7 +81,8 @@
if (!InitPageAcct()) return false;
}
- if (pread64(kpageflags_fd_, flags, sizeof(uint64_t), pfn * sizeof(uint64_t)) < 0) {
+ if (pread64(kpageflags_fd_, flags, sizeof(uint64_t), pfn * sizeof(uint64_t)) !=
+ sizeof(uint64_t)) {
PLOG(ERROR) << "Failed to read page flags for page " << pfn;
return false;
}
@@ -95,7 +96,8 @@
if (!InitPageAcct()) return false;
}
- if (pread64(kpagecount_fd_, mapcount, sizeof(uint64_t), pfn * sizeof(uint64_t)) < 0) {
+ if (pread64(kpagecount_fd_, mapcount, sizeof(uint64_t), pfn * sizeof(uint64_t)) !=
+ sizeof(uint64_t)) {
PLOG(ERROR) << "Failed to read map count for page " << pfn;
return false;
}
@@ -130,7 +132,7 @@
off64_t offset = pfn_to_idle_bitmap_offset(pfn);
uint64_t idle_bits;
- if (pread64(pageidle_fd_, &idle_bits, sizeof(uint64_t), offset) < 0) {
+ if (pread64(pageidle_fd_, &idle_bits, sizeof(uint64_t), offset) != sizeof(uint64_t)) {
PLOG(ERROR) << "Failed to read page idle bitmap for page " << pfn;
return -errno;
}
diff --git a/libmeminfo/procmeminfo.cpp b/libmeminfo/procmeminfo.cpp
index 934d65c..caf6e86 100644
--- a/libmeminfo/procmeminfo.cpp
+++ b/libmeminfo/procmeminfo.cpp
@@ -27,6 +27,7 @@
#include <memory>
#include <string>
#include <utility>
+#include <vector>
#include <android-base/file.h>
#include <android-base/logging.h>
@@ -278,68 +279,89 @@
bool ProcMemInfo::ReadVmaStats(int pagemap_fd, Vma& vma, bool get_wss, bool use_pageidle) {
PageAcct& pinfo = PageAcct::Instance();
- uint64_t pagesz = getpagesize();
- uint64_t num_pages = (vma.end - vma.start) / pagesz;
-
- std::unique_ptr<uint64_t[]> pg_frames(new uint64_t[num_pages]);
- uint64_t first = vma.start / pagesz;
- if (pread64(pagemap_fd, pg_frames.get(), num_pages * sizeof(uint64_t),
- first * sizeof(uint64_t)) < 0) {
- PLOG(ERROR) << "Failed to read page frames from page map for pid: " << pid_;
+ if (get_wss && use_pageidle && !pinfo.InitPageAcct(true)) {
+ LOG(ERROR) << "Failed to init idle page accounting";
return false;
}
- if (get_wss && use_pageidle) {
- if (!pinfo.InitPageAcct(true)) {
- LOG(ERROR) << "Failed to init idle page accounting";
- return false;
- }
- }
+ uint64_t pagesz = getpagesize();
+ size_t num_pages = (vma.end - vma.start) / pagesz;
+ size_t first_page = vma.start / pagesz;
- std::unique_ptr<uint64_t[]> pg_flags(new uint64_t[num_pages]);
- std::unique_ptr<uint64_t[]> pg_counts(new uint64_t[num_pages]);
- for (uint64_t i = 0; i < num_pages; ++i) {
+ std::vector<uint64_t> page_cache;
+ size_t cur_page_cache_index = 0;
+ size_t num_in_page_cache = 0;
+ size_t num_leftover_pages = num_pages;
+ for (size_t cur_page = first_page; cur_page < first_page + num_pages; ++cur_page) {
if (!get_wss) {
vma.usage.vss += pagesz;
}
- uint64_t p = pg_frames[i];
- if (!PAGE_PRESENT(p) && !PAGE_SWAPPED(p)) continue;
- if (PAGE_SWAPPED(p)) {
+ // Cache page map data.
+ if (cur_page_cache_index == num_in_page_cache) {
+ static constexpr size_t kMaxPages = 2048;
+ num_leftover_pages -= num_in_page_cache;
+ if (num_leftover_pages > kMaxPages) {
+ num_in_page_cache = kMaxPages;
+ } else {
+ num_in_page_cache = num_leftover_pages;
+ }
+ page_cache.resize(num_in_page_cache);
+ size_t total_bytes = page_cache.size() * sizeof(uint64_t);
+ ssize_t bytes = pread64(pagemap_fd, page_cache.data(), total_bytes,
+ cur_page * sizeof(uint64_t));
+ if (bytes != total_bytes) {
+ if (bytes == -1) {
+ PLOG(ERROR) << "Failed to read page data at offset "
+ << cur_page * sizeof(uint64_t);
+ } else {
+ LOG(ERROR) << "Failed to read page data at offset "
+ << cur_page * sizeof(uint64_t) << " read bytes " << sizeof(uint64_t)
+ << " expected bytes " << bytes;
+ }
+ return false;
+ }
+ cur_page_cache_index = 0;
+ }
+
+ uint64_t page_info = page_cache[cur_page_cache_index++];
+ if (!PAGE_PRESENT(page_info) && !PAGE_SWAPPED(page_info)) continue;
+
+ if (PAGE_SWAPPED(page_info)) {
vma.usage.swap += pagesz;
- swap_offsets_.emplace_back(PAGE_SWAP_OFFSET(p));
+ swap_offsets_.emplace_back(PAGE_SWAP_OFFSET(page_info));
continue;
}
- uint64_t page_frame = PAGE_PFN(p);
- if (!pinfo.PageFlags(page_frame, &pg_flags[i])) {
+ uint64_t page_frame = PAGE_PFN(page_info);
+ uint64_t cur_page_flags;
+ if (!pinfo.PageFlags(page_frame, &cur_page_flags)) {
LOG(ERROR) << "Failed to get page flags for " << page_frame << " in process " << pid_;
swap_offsets_.clear();
return false;
}
// skip unwanted pages from the count
- if ((pg_flags[i] & pgflags_mask_) != pgflags_) continue;
+ if ((cur_page_flags & pgflags_mask_) != pgflags_) continue;
- if (!pinfo.PageMapCount(page_frame, &pg_counts[i])) {
+ uint64_t cur_page_counts;
+ if (!pinfo.PageMapCount(page_frame, &cur_page_counts)) {
LOG(ERROR) << "Failed to get page count for " << page_frame << " in process " << pid_;
swap_offsets_.clear();
return false;
}
// Page was unmapped between the presence check at the beginning of the loop and here.
- if (pg_counts[i] == 0) {
- pg_frames[i] = 0;
- pg_flags[i] = 0;
+ if (cur_page_counts == 0) {
continue;
}
- bool is_dirty = !!(pg_flags[i] & (1 << KPF_DIRTY));
- bool is_private = (pg_counts[i] == 1);
+ bool is_dirty = !!(cur_page_flags & (1 << KPF_DIRTY));
+ bool is_private = (cur_page_counts == 1);
// Working set
if (get_wss) {
bool is_referenced = use_pageidle ? (pinfo.IsPageIdle(page_frame) == 1)
- : !!(pg_flags[i] & (1 << KPF_REFERENCED));
+ : !!(cur_page_flags & (1 << KPF_REFERENCED));
if (!is_referenced) {
continue;
}
@@ -351,7 +373,7 @@
vma.usage.rss += pagesz;
vma.usage.uss += is_private ? pagesz : 0;
- vma.usage.pss += pagesz / pg_counts[i];
+ vma.usage.pss += pagesz / cur_page_counts;
if (is_private) {
vma.usage.private_dirty += is_dirty ? pagesz : 0;
vma.usage.private_clean += is_dirty ? 0 : pagesz;
diff --git a/libmeminfo/tools/procrank.cpp b/libmeminfo/tools/procrank.cpp
index cb3757d..1e44ff9 100644
--- a/libmeminfo/tools/procrank.cpp
+++ b/libmeminfo/tools/procrank.cpp
@@ -348,7 +348,7 @@
auto rss_sort = [](ProcessRecord& a, ProcessRecord& b) {
MemUsage stats_a = show_wss ? a.Wss() : a.Usage();
MemUsage stats_b = show_wss ? b.Wss() : b.Usage();
- return reverse_sort ? stats_a.rss < stats_b.pss : stats_a.pss > stats_b.pss;
+ return reverse_sort ? stats_a.rss < stats_b.rss : stats_a.rss > stats_b.rss;
};
auto vss_sort = [](ProcessRecord& a, ProcessRecord& b) {
diff --git a/libprocessgroup/cgroup_map.cpp b/libprocessgroup/cgroup_map.cpp
index 9797d76..20ae2be 100644
--- a/libprocessgroup/cgroup_map.cpp
+++ b/libprocessgroup/cgroup_map.cpp
@@ -67,11 +67,14 @@
return controller_ != nullptr;
}
-bool CgroupController::IsUsable() const {
+bool CgroupController::IsUsable() {
if (!HasValue()) return false;
- static bool enabled = (access(GetProcsFilePath("", 0, 0).c_str(), F_OK) == 0);
- return enabled;
+ if (state_ == UNKNOWN) {
+ state_ = access(GetProcsFilePath("", 0, 0).c_str(), F_OK) == 0 ? USABLE : MISSING;
+ }
+
+ return state_ == USABLE;
}
std::string CgroupController::GetTasksFilePath(const std::string& rel_path) const {
diff --git a/libprocessgroup/cgroup_map.h b/libprocessgroup/cgroup_map.h
index 9350412..427d71b 100644
--- a/libprocessgroup/cgroup_map.h
+++ b/libprocessgroup/cgroup_map.h
@@ -31,20 +31,28 @@
class CgroupController {
public:
// Does not own controller
- explicit CgroupController(const ACgroupController* controller) : controller_(controller) {}
+ explicit CgroupController(const ACgroupController* controller)
+ : controller_(controller), state_(UNKNOWN) {}
uint32_t version() const;
const char* name() const;
const char* path() const;
bool HasValue() const;
- bool IsUsable() const;
+ bool IsUsable();
std::string GetTasksFilePath(const std::string& path) const;
std::string GetProcsFilePath(const std::string& path, uid_t uid, pid_t pid) const;
bool GetTaskGroup(int tid, std::string* group) const;
private:
+ enum ControllerState {
+ UNKNOWN = 0,
+ USABLE = 1,
+ MISSING = 2,
+ };
+
const ACgroupController* controller_ = nullptr;
+ ControllerState state_;
};
class CgroupMap {