Gatekeeperd maintenance
am: 3a1eb672c5
Change-Id: I7ae604143fa811ce33611cef20b61196c77b1b5a
diff --git a/adb/daemon/include/adbd/usb.h b/adb/daemon/include/adbd/usb.h
index 85a5711..fca3c58 100644
--- a/adb/daemon/include/adbd/usb.h
+++ b/adb/daemon/include/adbd/usb.h
@@ -16,8 +16,6 @@
* limitations under the License.
*/
-#include <linux/usb/functionfs.h>
-
#include <atomic>
#include <condition_variable>
#include <mutex>
@@ -64,9 +62,5 @@
};
usb_handle *create_usb_handle(unsigned num_bufs, unsigned io_size);
-
-struct usb_functionfs_event;
-const char* ffs_event_to_string(enum usb_functionfs_event_type type);
-bool read_functionfs_setup(android::base::borrowed_fd fd, usb_functionfs_event* event);
bool open_functionfs(android::base::unique_fd* control, android::base::unique_fd* bulk_out,
android::base::unique_fd* bulk_in);
diff --git a/adb/daemon/usb.cpp b/adb/daemon/usb.cpp
index 48fa771..f4aa9fb 100644
--- a/adb/daemon/usb.cpp
+++ b/adb/daemon/usb.cpp
@@ -66,6 +66,25 @@
static constexpr size_t kUsbWriteQueueDepth = 8;
static constexpr size_t kUsbWriteSize = 4 * PAGE_SIZE;
+static const char* to_string(enum usb_functionfs_event_type type) {
+ switch (type) {
+ case FUNCTIONFS_BIND:
+ return "FUNCTIONFS_BIND";
+ case FUNCTIONFS_UNBIND:
+ return "FUNCTIONFS_UNBIND";
+ case FUNCTIONFS_ENABLE:
+ return "FUNCTIONFS_ENABLE";
+ case FUNCTIONFS_DISABLE:
+ return "FUNCTIONFS_DISABLE";
+ case FUNCTIONFS_SETUP:
+ return "FUNCTIONFS_SETUP";
+ case FUNCTIONFS_SUSPEND:
+ return "FUNCTIONFS_SUSPEND";
+ case FUNCTIONFS_RESUME:
+ return "FUNCTIONFS_RESUME";
+ }
+}
+
enum class TransferDirection : uint64_t {
READ = 0,
WRITE = 1,
@@ -150,12 +169,12 @@
};
struct UsbFfsConnection : public Connection {
- UsbFfsConnection(unique_fd* control, unique_fd read, unique_fd write,
+ UsbFfsConnection(unique_fd control, unique_fd read, unique_fd write,
std::promise<void> destruction_notifier)
: worker_started_(false),
stopped_(false),
destruction_notifier_(std::move(destruction_notifier)),
- control_fd_(control),
+ control_fd_(std::move(control)),
read_fd_(std::move(read)),
write_fd_(std::move(write)) {
LOG(INFO) << "UsbFfsConnection constructed";
@@ -164,6 +183,11 @@
PLOG(FATAL) << "failed to create eventfd";
}
+ monitor_event_fd_.reset(eventfd(0, EFD_CLOEXEC));
+ if (monitor_event_fd_ == -1) {
+ PLOG(FATAL) << "failed to create eventfd";
+ }
+
aio_context_ = ScopedAioContext::Create(kUsbReadQueueDepth + kUsbWriteQueueDepth);
}
@@ -175,6 +199,7 @@
// We need to explicitly close our file descriptors before we notify our destruction,
// because the thread listening on the future will immediately try to reopen the endpoint.
aio_context_.reset();
+ control_fd_.reset();
read_fd_.reset();
write_fd_.reset();
@@ -221,6 +246,13 @@
PLOG(FATAL) << "failed to notify worker eventfd to stop UsbFfsConnection";
}
CHECK_EQ(static_cast<size_t>(rc), sizeof(notify));
+
+ rc = adb_write(monitor_event_fd_.get(), ¬ify, sizeof(notify));
+ if (rc < 0) {
+ PLOG(FATAL) << "failed to notify monitor eventfd to stop UsbFfsConnection";
+ }
+
+ CHECK_EQ(static_cast<size_t>(rc), sizeof(notify));
}
private:
@@ -239,24 +271,33 @@
monitor_thread_ = std::thread([this]() {
adb_thread_setname("UsbFfs-monitor");
+ bool bound = false;
bool enabled = false;
bool running = true;
while (running) {
adb_pollfd pfd[2] = {
- {.fd = control_fd_->get(), .events = POLLIN, .revents = 0},
+ { .fd = control_fd_.get(), .events = POLLIN, .revents = 0 },
+ { .fd = monitor_event_fd_.get(), .events = POLLIN, .revents = 0 },
};
- int rc = TEMP_FAILURE_RETRY(adb_poll(pfd, 2, -1));
+ // If we don't see our first bind within a second, try again.
+ int timeout_ms = bound ? -1 : 1000;
+
+ int rc = TEMP_FAILURE_RETRY(adb_poll(pfd, 2, timeout_ms));
if (rc == -1) {
PLOG(FATAL) << "poll on USB control fd failed";
+ } else if (rc == 0) {
+ LOG(WARNING) << "timed out while waiting for FUNCTIONFS_BIND, trying again";
+ break;
}
if (pfd[1].revents) {
- // We were told to die, continue reading until FUNCTIONFS_UNBIND.
+ // We were told to die.
+ break;
}
struct usb_functionfs_event event;
- rc = TEMP_FAILURE_RETRY(adb_read(control_fd_->get(), &event, sizeof(event)));
+ rc = TEMP_FAILURE_RETRY(adb_read(control_fd_.get(), &event, sizeof(event)));
if (rc == -1) {
PLOG(FATAL) << "failed to read functionfs event";
} else if (rc == 0) {
@@ -268,15 +309,32 @@
}
LOG(INFO) << "USB event: "
- << ffs_event_to_string(
- static_cast<usb_functionfs_event_type>(event.type));
+ << to_string(static_cast<usb_functionfs_event_type>(event.type));
switch (event.type) {
case FUNCTIONFS_BIND:
- LOG(FATAL) << "received FUNCTIONFS_BIND after already opened?";
+ if (bound) {
+ LOG(WARNING) << "received FUNCTIONFS_BIND while already bound?";
+ running = false;
+ break;
+ }
+
+ if (enabled) {
+ LOG(WARNING) << "received FUNCTIONFS_BIND while already enabled?";
+ running = false;
+ break;
+ }
+
+ bound = true;
break;
case FUNCTIONFS_ENABLE:
+ if (!bound) {
+ LOG(WARNING) << "received FUNCTIONFS_ENABLE while not bound?";
+ running = false;
+ break;
+ }
+
if (enabled) {
LOG(WARNING) << "received FUNCTIONFS_ENABLE while already enabled?";
running = false;
@@ -288,6 +346,10 @@
break;
case FUNCTIONFS_DISABLE:
+ if (!bound) {
+ LOG(WARNING) << "received FUNCTIONFS_DISABLE while not bound?";
+ }
+
if (!enabled) {
LOG(WARNING) << "received FUNCTIONFS_DISABLE while not enabled?";
}
@@ -301,12 +363,44 @@
LOG(WARNING) << "received FUNCTIONFS_UNBIND while still enabled?";
}
+ if (!bound) {
+ LOG(WARNING) << "received FUNCTIONFS_UNBIND when not bound?";
+ }
+
+ bound = false;
running = false;
break;
case FUNCTIONFS_SETUP: {
- read_functionfs_setup(*control_fd_, &event);
- break;
+ LOG(INFO) << "received FUNCTIONFS_SETUP control transfer: bRequestType = "
+ << static_cast<int>(event.u.setup.bRequestType)
+ << ", bRequest = " << static_cast<int>(event.u.setup.bRequest)
+ << ", wValue = " << static_cast<int>(event.u.setup.wValue)
+ << ", wIndex = " << static_cast<int>(event.u.setup.wIndex)
+ << ", wLength = " << static_cast<int>(event.u.setup.wLength);
+
+ if ((event.u.setup.bRequestType & USB_DIR_IN)) {
+ LOG(INFO) << "acking device-to-host control transfer";
+ ssize_t rc = adb_write(control_fd_.get(), "", 0);
+ if (rc != 0) {
+ PLOG(ERROR) << "failed to write empty packet to host";
+ break;
+ }
+ } else {
+ std::string buf;
+ buf.resize(event.u.setup.wLength + 1);
+
+ ssize_t rc = adb_read(control_fd_.get(), buf.data(), buf.size());
+ if (rc != event.u.setup.wLength) {
+ LOG(ERROR)
+ << "read " << rc
+ << " bytes when trying to read control request, expected "
+ << event.u.setup.wLength;
+ }
+
+ LOG(INFO) << "control request contents: " << buf;
+ break;
+ }
}
}
}
@@ -332,12 +426,6 @@
uint64_t dummy;
ssize_t rc = adb_read(worker_event_fd_.get(), &dummy, sizeof(dummy));
if (rc == -1) {
- if (errno == EINTR) {
- // We were interrupted either to stop, or because of a backtrace.
- // Check stopped_ again to see if we need to exit.
- continue;
- }
-
PLOG(FATAL) << "failed to read from eventfd";
} else if (rc == 0) {
LOG(FATAL) << "hit EOF on eventfd";
@@ -374,7 +462,6 @@
}
worker_thread_.join();
- worker_started_ = false;
}
void PrepareReadBlock(IoBlock* block, uint64_t id) {
@@ -605,13 +692,10 @@
std::once_flag error_flag_;
unique_fd worker_event_fd_;
+ unique_fd monitor_event_fd_;
ScopedAioContext aio_context_;
-
- // We keep a pointer to the control fd, so that we can reuse it to avoid USB reconfiguration,
- // and still be able to reset it to force a reopen after FUNCTIONFS_UNBIND or running into an
- // unexpected situation.
- unique_fd* control_fd_;
+ unique_fd control_fd_;
unique_fd read_fd_;
unique_fd write_fd_;
@@ -640,16 +724,15 @@
static void usb_ffs_open_thread() {
adb_thread_setname("usb ffs open");
- unique_fd control;
- unique_fd bulk_out;
- unique_fd bulk_in;
-
while (true) {
if (gFfsAioSupported.has_value() && !gFfsAioSupported.value()) {
LOG(INFO) << "failed to use nonblocking ffs, falling back to legacy";
return usb_init_legacy();
}
+ unique_fd control;
+ unique_fd bulk_out;
+ unique_fd bulk_in;
if (!open_functionfs(&control, &bulk_out, &bulk_in)) {
std::this_thread::sleep_for(1s);
continue;
@@ -660,7 +743,7 @@
std::promise<void> destruction_notifier;
std::future<void> future = destruction_notifier.get_future();
transport->SetConnection(std::make_unique<UsbFfsConnection>(
- &control, std::move(bulk_out), std::move(bulk_in),
+ std::move(control), std::move(bulk_out), std::move(bulk_in),
std::move(destruction_notifier)));
register_transport(transport);
future.wait();
diff --git a/adb/daemon/usb_ffs.cpp b/adb/daemon/usb_ffs.cpp
index 5fbca3b..a64ce40 100644
--- a/adb/daemon/usb_ffs.cpp
+++ b/adb/daemon/usb_ffs.cpp
@@ -248,56 +248,6 @@
};
// clang-format on
-const char* ffs_event_to_string(enum usb_functionfs_event_type type) {
- switch (type) {
- case FUNCTIONFS_BIND:
- return "FUNCTIONFS_BIND";
- case FUNCTIONFS_UNBIND:
- return "FUNCTIONFS_UNBIND";
- case FUNCTIONFS_ENABLE:
- return "FUNCTIONFS_ENABLE";
- case FUNCTIONFS_DISABLE:
- return "FUNCTIONFS_DISABLE";
- case FUNCTIONFS_SETUP:
- return "FUNCTIONFS_SETUP";
- case FUNCTIONFS_SUSPEND:
- return "FUNCTIONFS_SUSPEND";
- case FUNCTIONFS_RESUME:
- return "FUNCTIONFS_RESUME";
- }
-}
-
-bool read_functionfs_setup(borrowed_fd fd, usb_functionfs_event* event) {
- LOG(INFO) << "received FUNCTIONFS_SETUP control transfer: bRequestType = "
- << static_cast<int>(event->u.setup.bRequestType)
- << ", bRequest = " << static_cast<int>(event->u.setup.bRequest)
- << ", wValue = " << static_cast<int>(event->u.setup.wValue)
- << ", wIndex = " << static_cast<int>(event->u.setup.wIndex)
- << ", wLength = " << static_cast<int>(event->u.setup.wLength);
-
- if ((event->u.setup.bRequestType & USB_DIR_IN)) {
- LOG(INFO) << "acking device-to-host control transfer";
- ssize_t rc = adb_write(fd.get(), "", 0);
- if (rc != 0) {
- PLOG(ERROR) << "failed to write empty packet to host";
- return false;
- }
- } else {
- std::string buf;
- buf.resize(event->u.setup.wLength + 1);
-
- ssize_t rc = adb_read(fd.get(), buf.data(), buf.size());
- if (rc != event->u.setup.wLength) {
- LOG(ERROR) << "read " << rc << " bytes when trying to read control request, expected "
- << event->u.setup.wLength;
- }
-
- LOG(INFO) << "control request contents: " << buf;
- }
-
- return true;
-}
-
bool open_functionfs(android::base::unique_fd* out_control, android::base::unique_fd* out_bulk_out,
android::base::unique_fd* out_bulk_in) {
unique_fd control, bulk_out, bulk_in;
@@ -347,37 +297,11 @@
PLOG(ERROR) << "failed to write USB strings";
return false;
}
-
- // Signal init after we've written our descriptors.
+ // Signal only when writing the descriptors to ffs
android::base::SetProperty("sys.usb.ffs.ready", "1");
*out_control = std::move(control);
}
- // Read until we get FUNCTIONFS_BIND from the control endpoint.
- while (true) {
- struct usb_functionfs_event event;
- ssize_t rc = TEMP_FAILURE_RETRY(adb_read(*out_control, &event, sizeof(event)));
-
- if (rc == -1) {
- PLOG(FATAL) << "failed to read from FFS control fd";
- } else if (rc == 0) {
- LOG(WARNING) << "hit EOF on functionfs control fd during initialization";
- } else if (rc != sizeof(event)) {
- LOG(FATAL) << "read functionfs event of unexpected size, expected " << sizeof(event)
- << ", got " << rc;
- }
-
- LOG(INFO) << "USB event: "
- << ffs_event_to_string(static_cast<usb_functionfs_event_type>(event.type));
- if (event.type == FUNCTIONFS_BIND) {
- break;
- } else if (event.type == FUNCTIONFS_SETUP) {
- read_functionfs_setup(*out_control, &event);
- } else {
- continue;
- }
- }
-
bulk_out.reset(adb_open(USB_FFS_ADB_OUT, O_RDONLY));
if (bulk_out < 0) {
PLOG(ERROR) << "cannot open bulk-out endpoint " << USB_FFS_ADB_OUT;
diff --git a/bootstat/bootstat.cpp b/bootstat/bootstat.cpp
index c7be00b..6936cc2 100644
--- a/bootstat/bootstat.cpp
+++ b/bootstat/bootstat.cpp
@@ -310,6 +310,7 @@
{"shutdown,userrequested,fastboot", 181},
{"shutdown,userrequested,recovery", 182},
{"reboot,unknown[0-9]*", 183},
+ {"reboot,longkey,.*", 184},
};
// Converts a string value representing the reason the system booted to an
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/fs_mgr/fs_mgr.cpp b/fs_mgr/fs_mgr.cpp
index 2a9a9d0..259f800 100644
--- a/fs_mgr/fs_mgr.cpp
+++ b/fs_mgr/fs_mgr.cpp
@@ -908,7 +908,7 @@
public:
CheckpointManager(int needs_checkpoint = -1) : needs_checkpoint_(needs_checkpoint) {}
- bool Update(FstabEntry* entry) {
+ bool Update(FstabEntry* entry, const std::string& block_device = std::string()) {
if (!entry->fs_mgr_flags.checkpoint_blk && !entry->fs_mgr_flags.checkpoint_fs) {
return true;
}
@@ -927,7 +927,7 @@
return true;
}
- if (!UpdateCheckpointPartition(entry)) {
+ if (!UpdateCheckpointPartition(entry, block_device)) {
LERROR << "Could not set up checkpoint partition, skipping!";
return false;
}
@@ -957,7 +957,7 @@
}
private:
- bool UpdateCheckpointPartition(FstabEntry* entry) {
+ bool UpdateCheckpointPartition(FstabEntry* entry, const std::string& block_device) {
if (entry->fs_mgr_flags.checkpoint_fs) {
if (is_f2fs(entry->fs_type)) {
entry->fs_options += ",checkpoint=disable";
@@ -965,39 +965,43 @@
LERROR << entry->fs_type << " does not implement checkpoints.";
}
} else if (entry->fs_mgr_flags.checkpoint_blk) {
- unique_fd fd(TEMP_FAILURE_RETRY(open(entry->blk_device.c_str(), O_RDONLY | O_CLOEXEC)));
- if (fd < 0) {
- PERROR << "Cannot open device " << entry->blk_device;
- return false;
- }
+ auto actual_block_device = block_device.empty() ? entry->blk_device : block_device;
+ if (fs_mgr_find_bow_device(actual_block_device).empty()) {
+ unique_fd fd(
+ TEMP_FAILURE_RETRY(open(entry->blk_device.c_str(), O_RDONLY | O_CLOEXEC)));
+ if (fd < 0) {
+ PERROR << "Cannot open device " << entry->blk_device;
+ return false;
+ }
- uint64_t size = get_block_device_size(fd) / 512;
- if (!size) {
- PERROR << "Cannot get device size";
- return false;
- }
+ uint64_t size = get_block_device_size(fd) / 512;
+ if (!size) {
+ PERROR << "Cannot get device size";
+ return false;
+ }
- android::dm::DmTable table;
- if (!table.AddTarget(
- std::make_unique<android::dm::DmTargetBow>(0, size, entry->blk_device))) {
- LERROR << "Failed to add bow target";
- return false;
- }
+ android::dm::DmTable table;
+ if (!table.AddTarget(std::make_unique<android::dm::DmTargetBow>(
+ 0, size, entry->blk_device))) {
+ LERROR << "Failed to add bow target";
+ return false;
+ }
- DeviceMapper& dm = DeviceMapper::Instance();
- if (!dm.CreateDevice("bow", table)) {
- PERROR << "Failed to create bow device";
- return false;
- }
+ DeviceMapper& dm = DeviceMapper::Instance();
+ if (!dm.CreateDevice("bow", table)) {
+ PERROR << "Failed to create bow device";
+ return false;
+ }
- std::string name;
- if (!dm.GetDmDevicePathByName("bow", &name)) {
- PERROR << "Failed to get bow device name";
- return false;
- }
+ std::string name;
+ if (!dm.GetDmDevicePathByName("bow", &name)) {
+ PERROR << "Failed to get bow device name";
+ return false;
+ }
- device_map_[name] = entry->blk_device;
- entry->blk_device = name;
+ device_map_[name] = entry->blk_device;
+ entry->blk_device = name;
+ }
}
return true;
}
@@ -1007,6 +1011,50 @@
std::map<std::string, std::string> device_map_;
};
+std::string fs_mgr_find_bow_device(const std::string& block_device) {
+ if (block_device.substr(0, 5) != "/dev/") {
+ LOG(ERROR) << "Expected block device, got " << block_device;
+ return std::string();
+ }
+
+ std::string sys_dir = std::string("/sys/") + block_device.substr(5);
+
+ for (;;) {
+ std::string name;
+ if (!android::base::ReadFileToString(sys_dir + "/dm/name", &name)) {
+ PLOG(ERROR) << block_device << " is not dm device";
+ return std::string();
+ }
+
+ if (name == "bow\n") return sys_dir;
+
+ std::string slaves = sys_dir + "/slaves";
+ std::unique_ptr<DIR, decltype(&closedir)> directory(opendir(slaves.c_str()), closedir);
+ if (!directory) {
+ PLOG(ERROR) << "Can't open slave directory " << slaves;
+ return std::string();
+ }
+
+ int count = 0;
+ for (dirent* entry = readdir(directory.get()); entry; entry = readdir(directory.get())) {
+ if (entry->d_type != DT_LNK) continue;
+
+ if (count == 1) {
+ LOG(ERROR) << "Too many slaves in " << slaves;
+ return std::string();
+ }
+
+ ++count;
+ sys_dir = std::string("/sys/block/") + entry->d_name;
+ }
+
+ if (count != 1) {
+ LOG(ERROR) << "No slave in " << slaves;
+ return std::string();
+ }
+ }
+}
+
static bool IsMountPointMounted(const std::string& mount_point) {
// Check if this is already mounted.
Fstab fstab;
@@ -1144,7 +1192,8 @@
}
encryptable = status;
if (status == FS_MGR_MNTALL_DEV_NEEDS_METADATA_ENCRYPTION) {
- if (!call_vdc({"cryptfs", "encryptFstab", attempted_entry.mount_point})) {
+ if (!call_vdc({"cryptfs", "encryptFstab", attempted_entry.blk_device,
+ attempted_entry.mount_point})) {
LERROR << "Encryption failed";
return FS_MGR_MNTALL_FAIL;
}
@@ -1215,7 +1264,8 @@
encryptable = FS_MGR_MNTALL_DEV_MIGHT_BE_ENCRYPTED;
} else if (mount_errno != EBUSY && mount_errno != EACCES &&
should_use_metadata_encryption(attempted_entry)) {
- if (!call_vdc({"cryptfs", "mountFstab", attempted_entry.mount_point})) {
+ if (!call_vdc({"cryptfs", "mountFstab", attempted_entry.blk_device,
+ attempted_entry.mount_point})) {
++error_count;
}
encryptable = FS_MGR_MNTALL_DEV_IS_METADATA_ENCRYPTED;
@@ -1345,7 +1395,7 @@
}
}
- if (!checkpoint_manager.Update(&fstab_entry)) {
+ if (!checkpoint_manager.Update(&fstab_entry, n_blk_device)) {
LERROR << "Could not set up checkpoint partition, skipping!";
continue;
}
diff --git a/fs_mgr/include/fs_mgr.h b/fs_mgr/include/fs_mgr.h
index 88b2f8f..bdec7be 100644
--- a/fs_mgr/include/fs_mgr.h
+++ b/fs_mgr/include/fs_mgr.h
@@ -104,3 +104,7 @@
// fs_mgr_umount_all() is the reverse of fs_mgr_mount_all. In particular,
// it destroys verity devices from device mapper after the device is unmounted.
int fs_mgr_umount_all(android::fs_mgr::Fstab* fstab);
+
+// Finds the dm_bow device on which this block device is stacked, or returns
+// empty string
+std::string fs_mgr_find_bow_device(const std::string& block_device);
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 44cac4b..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) {
@@ -588,7 +590,12 @@
if (!ReadFstabFromFile(fstab_file, &fstab)) {
return Error() << "Could not read fstab";
}
- auto mount_fstab_return_code = fs_mgr_mount_all(&fstab, mount_mode);
+
+ auto mount_fstab_return_code =
+ CallFunctionAndHandleProperties(fs_mgr_mount_all, &fstab, mount_mode);
+ if (!mount_fstab_return_code) {
+ return Error() << "Could not call fs_mgr_mount_all(): " << mount_fstab_return_code.error();
+ }
property_set(prop_name, std::to_string(t.duration().count()));
if (import_rc && SelinuxGetVendorAndroidVersion() <= __ANDROID_API_Q__) {
@@ -599,7 +606,7 @@
if (queue_event) {
/* queue_fs_event will queue event based on mount_fstab return code
* and return processed return code*/
- auto queue_fs_result = queue_fs_event(mount_fstab_return_code);
+ auto queue_fs_result = queue_fs_event(*mount_fstab_return_code);
if (!queue_fs_result) {
return Error() << "queue_fs_event() failed: " << queue_fs_result.error();
}
@@ -615,8 +622,13 @@
return Error() << "Could not read fstab";
}
- if (auto result = fs_mgr_umount_all(&fstab); result != 0) {
- return Error() << "umount_fstab() failed " << result;
+ auto result = CallFunctionAndHandleProperties(fs_mgr_umount_all, &fstab);
+ if (!result) {
+ return Error() << "Could not call fs_mgr_mount_all() " << result.error();
+ }
+
+ if (*result != 0) {
+ return Error() << "fs_mgr_mount_all() failed: " << *result;
}
return {};
}
@@ -627,8 +639,13 @@
return Error() << "Could not read fstab '" << args[1] << "'";
}
- if (!fs_mgr_swapon_all(fstab)) {
- return Error() << "fs_mgr_swapon_all() failed";
+ auto result = CallFunctionAndHandleProperties(fs_mgr_swapon_all, fstab);
+ if (!result) {
+ return Error() << "Could not call fs_mgr_swapon_all() " << result.error();
+ }
+
+ if (*result == 0) {
+ return Error() << "fs_mgr_swapon_all() failed.";
}
return {};
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..9323aa0 100644
--- a/init/host_init_verifier.cpp
+++ b/init/host_init_verifier.cpp
@@ -15,11 +15,13 @@
//
#include <errno.h>
+#include <getopt.h>
#include <pwd.h>
#include <stdio.h>
#include <stdlib.h>
#include <iostream>
+#include <iterator>
#include <string>
#include <vector>
@@ -36,6 +38,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"
@@ -46,9 +50,9 @@
using android::base::ReadFileToString;
using android::base::Split;
-static std::string passwd_file;
+static std::vector<std::string> passwd_files;
-static std::vector<std::pair<std::string, int>> GetVendorPasswd() {
+static std::vector<std::pair<std::string, int>> GetVendorPasswd(const std::string& passwd_file) {
std::string passwd;
if (!ReadFileToString(passwd_file, &passwd)) {
return {};
@@ -70,6 +74,16 @@
return result;
}
+static std::vector<std::pair<std::string, int>> GetVendorPasswd() {
+ std::vector<std::pair<std::string, int>> result;
+ for (const auto& passwd_file : passwd_files) {
+ auto individual_result = GetVendorPasswd(passwd_file);
+ std::move(individual_result.begin(), individual_result.end(),
+ std::back_insert_iterator(result));
+ }
+ return result;
+}
+
passwd* getpwnam(const char* login) { // NOLINT: implementing bad function.
// This isn't thread safe, but that's okay for our purposes.
static char static_name[32] = "";
@@ -124,17 +138,50 @@
#include "generated_stub_builtin_function_map.h"
+void PrintUsage() {
+ std::cout << "usage: host_init_verifier [-p FILE] <init rc file>\n"
+ "\n"
+ "Tests an init script for correctness\n"
+ "\n"
+ "-p FILE\tSearch this passwd file for users and groups\n"
+ << std::endl;
+}
+
int main(int argc, char** argv) {
android::base::InitLogging(argv, &android::base::StdioLogger);
android::base::SetMinimumLogSeverity(android::base::ERROR);
- if (argc != 2 && argc != 3) {
- LOG(ERROR) << "Usage: " << argv[0] << " <init rc file> [passwd file]";
- return EXIT_FAILURE;
+ while (true) {
+ static const struct option long_options[] = {
+ {"help", no_argument, nullptr, 'h'},
+ {nullptr, 0, nullptr, 0},
+ };
+
+ int arg = getopt_long(argc, argv, "p:", long_options, nullptr);
+
+ if (arg == -1) {
+ break;
+ }
+
+ switch (arg) {
+ case 'h':
+ PrintUsage();
+ return EXIT_FAILURE;
+ case 'p':
+ passwd_files.emplace_back(optarg);
+ break;
+ default:
+ std::cerr << "getprop: getopt returned invalid result: " << arg << std::endl;
+ return EXIT_FAILURE;
+ }
}
- if (argc == 3) {
- passwd_file = argv[2];
+ argc -= optind;
+ argv += optind;
+
+ if (argc != 1) {
+ PrintUsage();
+ return EXIT_FAILURE;
}
const BuiltinFunctionMap function_map;
@@ -146,12 +193,12 @@
parser.AddSectionParser("on", std::make_unique<ActionParser>(&am, nullptr));
parser.AddSectionParser("import", std::make_unique<HostImportParser>());
- if (!parser.ParseConfigFileInsecure(argv[1])) {
- LOG(ERROR) << "Failed to open init rc script '" << argv[1] << "'";
+ if (!parser.ParseConfigFileInsecure(*argv)) {
+ LOG(ERROR) << "Failed to open init rc script '" << *argv << "'";
return EXIT_FAILURE;
}
if (parser.parse_error_count() > 0) {
- LOG(ERROR) << "Failed to parse init script '" << argv[1] << "' with "
+ LOG(ERROR) << "Failed to parse init script '" << *argv << "' with "
<< parser.parse_error_count() << " errors";
return EXIT_FAILURE;
}
diff --git a/init/init.cpp b/init/init.cpp
index 1412e4a..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() {
@@ -198,6 +199,14 @@
if (property_triggers_enabled) ActionManager::GetInstance().QueuePropertyChange(name, value);
+ // We always record how long init waited for ueventd to tell us cold boot finished.
+ // If we aren't waiting on this property, it means that ueventd finished before we even started
+ // to wait.
+ if (name == kColdBootDoneProp) {
+ auto time_waited = waiting_for_prop ? waiting_for_prop->duration().count() : 0;
+ property_set("ro.boottime.init.cold_boot_wait", std::to_string(time_waited));
+ }
+
if (waiting_for_prop) {
if (wait_prop_name == name && wait_prop_value == value) {
LOG(INFO) << "Wait for property '" << wait_prop_name << "=" << wait_prop_value
@@ -331,31 +340,10 @@
}
static Result<void> wait_for_coldboot_done_action(const BuiltinArguments& args) {
- Timer t;
-
- LOG(VERBOSE) << "Waiting for " COLDBOOT_DONE "...";
-
- // Historically we had a 1s timeout here because we weren't otherwise
- // tracking boot time, and many OEMs made their sepolicy regular
- // expressions too expensive (http://b/19899875).
-
- // Now we're tracking boot time, just log the time taken to a system
- // property. We still panic if it takes more than a minute though,
- // because any build that slow isn't likely to boot at all, and we'd
- // rather any test lab devices fail back to the bootloader.
- if (wait_for_file(COLDBOOT_DONE, 60s) < 0) {
- LOG(FATAL) << "Timed out waiting for " COLDBOOT_DONE;
+ if (!start_waiting_for_property(kColdBootDoneProp, "true")) {
+ LOG(FATAL) << "Could not wait for '" << kColdBootDoneProp << "'";
}
- property_set("ro.boottime.init.cold_boot_wait", std::to_string(t.duration().count()));
- 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 {};
}
@@ -765,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/property_service.cpp b/init/property_service.cpp
index 14bb819..1520c9f 100644
--- a/init/property_service.cpp
+++ b/init/property_service.cpp
@@ -39,6 +39,7 @@
#define _REALLY_INCLUDE_SYS__SYSTEM_PROPERTIES_H_
#include <sys/_system_properties.h>
+#include <atomic>
#include <map>
#include <memory>
#include <mutex>
@@ -52,6 +53,7 @@
#include <android-base/properties.h>
#include <android-base/stringprintf.h>
#include <android-base/strings.h>
+#include <android-base/unique_fd.h>
#include <property_info_parser/property_info_parser.h>
#include <property_info_serializer/property_info_serializer.h>
#include <selinux/android.h>
@@ -59,7 +61,6 @@
#include <selinux/selinux.h>
#include "debug_ramdisk.h"
-#include "epoll.h"
#include "init.h"
#include "persistent_properties.h"
#include "property_type.h"
@@ -76,6 +77,7 @@
using android::base::StringPrintf;
using android::base::Timer;
using android::base::Trim;
+using android::base::unique_fd;
using android::base::WriteStringToFile;
using android::properties::BuildTrie;
using android::properties::ParsePropertyInfoFile;
@@ -1006,5 +1008,42 @@
}
}
+Result<int> CallFunctionAndHandlePropertiesImpl(const std::function<int()>& f) {
+ unique_fd reader;
+ unique_fd writer;
+ if (!Socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, &reader, &writer)) {
+ return ErrnoError() << "Could not create socket pair";
+ }
+
+ int result = 0;
+ std::atomic<bool> end = false;
+ auto thread = std::thread{[&f, &result, &end, &writer] {
+ result = f();
+ end = true;
+ send(writer, "1", 1, 0);
+ }};
+
+ Epoll epoll;
+ if (auto result = epoll.Open(); !result) {
+ return Error() << "Could not create epoll: " << result.error();
+ }
+ if (auto result = epoll.RegisterHandler(property_set_fd, handle_property_set_fd); !result) {
+ return Error() << "Could not register epoll handler for property fd: " << result.error();
+ }
+
+ // No-op function, just used to break from loop.
+ if (auto result = epoll.RegisterHandler(reader, [] {}); !result) {
+ return Error() << "Could not register epoll handler for ending thread:" << result.error();
+ }
+
+ while (!end) {
+ epoll.Wait({});
+ }
+
+ thread.join();
+
+ return result;
+}
+
} // namespace init
} // namespace android
diff --git a/init/property_service.h b/init/property_service.h
index 7f9f844..dc47b4d 100644
--- a/init/property_service.h
+++ b/init/property_service.h
@@ -18,9 +18,11 @@
#include <sys/socket.h>
+#include <functional>
#include <string>
#include "epoll.h"
+#include "result.h"
namespace android {
namespace init {
@@ -37,5 +39,13 @@
void load_persist_props();
void StartPropertyService(Epoll* epoll);
+template <typename F, typename... Args>
+Result<int> CallFunctionAndHandleProperties(F&& f, Args&&... args) {
+ Result<int> CallFunctionAndHandlePropertiesImpl(const std::function<int()>& f);
+
+ auto func = [&] { return f(args...); };
+ return CallFunctionAndHandlePropertiesImpl(func);
+}
+
} // namespace init
} // namespace android
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/ueventd.cpp b/init/ueventd.cpp
index f550bc2..3b9de0f 100644
--- a/init/ueventd.cpp
+++ b/init/ueventd.cpp
@@ -214,7 +214,7 @@
WaitForSubProcesses();
- close(open(COLDBOOT_DONE, O_WRONLY | O_CREAT | O_CLOEXEC, 0000));
+ android::base::SetProperty(kColdBootDoneProp, "true");
LOG(INFO) << "Coldboot took " << cold_boot_timer.duration().count() / 1000.0f << " seconds";
}
@@ -256,7 +256,7 @@
}
UeventListener uevent_listener(ueventd_configuration.uevent_socket_rcvbuf_size);
- if (access(COLDBOOT_DONE, F_OK) != 0) {
+ if (!android::base::GetBoolProperty(kColdBootDoneProp, false)) {
ColdBoot cold_boot(uevent_listener, uevent_handlers);
cold_boot.Run();
}
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/init/util.h b/init/util.h
index 770084b..1929cb5 100644
--- a/init/util.h
+++ b/init/util.h
@@ -30,14 +30,14 @@
#include "result.h"
-#define COLDBOOT_DONE "/dev/.coldboot_done"
-
using android::base::boot_clock;
using namespace std::chrono_literals;
namespace android {
namespace init {
+static const char kColdBootDoneProp[] = "ro.cold_boot_done";
+
int CreateSocket(const char* name, int type, bool passcred, mode_t perm, uid_t uid, gid_t gid,
const char* socketcon);
diff --git a/libcutils/fs_config.cpp b/libcutils/fs_config.cpp
index 5b5f2eb..897a169 100644
--- a/libcutils/fs_config.cpp
+++ b/libcutils/fs_config.cpp
@@ -39,6 +39,8 @@
#include <private/android_filesystem_config.h>
#include <utils/Compat.h>
+#include "fs_config.h"
+
#ifndef O_BINARY
#define O_BINARY 0
#endif
@@ -47,6 +49,7 @@
using android::base::StartsWith;
#define ALIGN(x, alignment) (((x) + ((alignment)-1)) & ~((alignment)-1))
+#define CAP_MASK_LONG(cap_name) (1ULL << (cap_name))
// Rules for directories.
// These rules are applied based on "first match", so they
diff --git a/libcutils/fs_config.h b/libcutils/fs_config.h
new file mode 100644
index 0000000..66ad48b
--- /dev/null
+++ b/libcutils/fs_config.h
@@ -0,0 +1,37 @@
+/*
+ * 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 <stdint.h>
+
+// Binary format for the runtime <partition>/etc/fs_config_(dirs|files) filesystem override files.
+struct fs_path_config_from_file {
+ uint16_t len;
+ uint16_t mode;
+ uint16_t uid;
+ uint16_t gid;
+ uint64_t capabilities;
+ char prefix[];
+} __attribute__((__aligned__(sizeof(uint64_t))));
+
+struct fs_path_config {
+ unsigned mode;
+ unsigned uid;
+ unsigned gid;
+ uint64_t capabilities;
+ const char* prefix;
+};
diff --git a/libcutils/fs_config_test.cpp b/libcutils/fs_config_test.cpp
index c26315f..9627152 100644
--- a/libcutils/fs_config_test.cpp
+++ b/libcutils/fs_config_test.cpp
@@ -25,7 +25,8 @@
#include <android-base/strings.h>
#include <private/android_filesystem_config.h>
-#include <private/fs_config.h>
+
+#include "fs_config.h"
extern const fs_path_config* __for_testing_only__android_dirs;
extern const fs_path_config* __for_testing_only__android_files;
diff --git a/libcutils/include/private/fs_config.h b/libcutils/include/private/fs_config.h
index 603cf1a..8a9a1ff 100644
--- a/libcutils/include/private/fs_config.h
+++ b/libcutils/include/private/fs_config.h
@@ -19,44 +19,17 @@
** by the device side of adb.
*/
-#ifndef _LIBS_CUTILS_PRIVATE_FS_CONFIG_H
-#define _LIBS_CUTILS_PRIVATE_FS_CONFIG_H
+#pragma once
#include <stdint.h>
#include <sys/cdefs.h>
-#include <sys/types.h>
#if defined(__BIONIC__)
#include <linux/capability.h>
#else // defined(__BIONIC__)
-#include "android_filesystem_capability.h"
+#include <private/android_filesystem_capability.h>
#endif // defined(__BIONIC__)
-#define CAP_MASK_LONG(cap_name) (1ULL << (cap_name))
-
-/*
- * binary format for the runtime <partition>/etc/fs_config_(dirs|files)
- * filesystem override files.
- */
-
-/* The following structure is stored little endian */
-struct fs_path_config_from_file {
- uint16_t len;
- uint16_t mode;
- uint16_t uid;
- uint16_t gid;
- uint64_t capabilities;
- char prefix[];
-} __attribute__((__aligned__(sizeof(uint64_t))));
-
-struct fs_path_config {
- unsigned mode;
- unsigned uid;
- unsigned gid;
- uint64_t capabilities;
- const char* prefix;
-};
-
/* Rules for directories and files has moved to system/code/libcutils/fs_config.c */
__BEGIN_DECLS
@@ -75,5 +48,3 @@
unsigned* mode, uint64_t* capabilities);
__END_DECLS
-
-#endif /* _LIBS_CUTILS_PRIVATE_FS_CONFIG_H */
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 {
diff --git a/logd/tests/logd_test.cpp b/logd/tests/logd_test.cpp
index 447b067..b6c33d7 100644
--- a/logd/tests/logd_test.cpp
+++ b/logd/tests/logd_test.cpp
@@ -952,7 +952,7 @@
void __android_log_btwrite_multiple__helper(int count) {
#ifdef __ANDROID__
log_time ts(CLOCK_MONOTONIC);
-
+ usleep(100);
log_time ts1(CLOCK_MONOTONIC);
// We fork to create a unique pid for the submitted log messages