summaryrefslogtreecommitdiffstats
path: root/init
diff options
context:
space:
mode:
Diffstat (limited to 'init')
-rw-r--r--init/builtins.cpp9
-rw-r--r--init/devices.cpp10
-rw-r--r--init/init.cpp273
-rw-r--r--init/init.h2
-rw-r--r--init/init_parser.cpp1
-rw-r--r--init/keychords.cpp59
-rw-r--r--init/keychords.h6
-rw-r--r--init/keywords.h2
-rw-r--r--init/property_service.cpp20
-rw-r--r--init/property_service.h2
-rw-r--r--init/readme.txt5
-rw-r--r--init/signal_handler.cpp78
-rw-r--r--init/signal_handler.h4
-rw-r--r--init/util.cpp44
-rw-r--r--init/util.h3
15 files changed, 245 insertions, 273 deletions
diff --git a/init/builtins.cpp b/init/builtins.cpp
index d0e56ec0c..c03968990 100644
--- a/init/builtins.cpp
+++ b/init/builtins.cpp
@@ -487,15 +487,6 @@ int do_swapon_all(int nargs, char **args)
return ret;
}
-int do_setcon(int nargs, char **args) {
- if (is_selinux_enabled() <= 0)
- return 0;
- if (setcon(args[1]) < 0) {
- return -errno;
- }
- return 0;
-}
-
int do_setprop(int nargs, char **args)
{
const char *name = args[1];
diff --git a/init/devices.cpp b/init/devices.cpp
index 96b1696b4..2c7f5a9cd 100644
--- a/init/devices.cpp
+++ b/init/devices.cpp
@@ -266,7 +266,6 @@ static void make_device(const char *path,
static void add_platform_device(const char *path)
{
int path_len = strlen(path);
- struct listnode *node;
struct platform_node *bus;
const char *name = path;
@@ -276,15 +275,6 @@ static void add_platform_device(const char *path)
name += 9;
}
- list_for_each_reverse(node, &platform_names) {
- bus = node_to_item(node, struct platform_node, list);
- if ((bus->path_len < path_len) &&
- (path[bus->path_len] == '/') &&
- !strncmp(path, bus->path, bus->path_len))
- /* subdevice of an existing platform, ignore it */
- return;
- }
-
INFO("adding platform device %s (%s)\n", name, path);
bus = (platform_node*) calloc(1, sizeof(struct platform_node));
diff --git a/init/init.cpp b/init/init.cpp
index b1d65dbc0..dd74538de 100644
--- a/init/init.cpp
+++ b/init/init.cpp
@@ -25,8 +25,8 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
+#include <sys/epoll.h>
#include <sys/mount.h>
-#include <sys/poll.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/types.h>
@@ -82,6 +82,17 @@ static const char *ENV[32];
bool waiting_for_exec = false;
+static int epoll_fd = -1;
+
+void register_epoll_handler(int fd, void (*fn)()) {
+ epoll_event ev;
+ ev.events = EPOLLIN;
+ ev.data.ptr = reinterpret_cast<void*>(fn);
+ if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fd, &ev) == -1) {
+ ERROR("epoll_ctl failed: %s\n", strerror(errno));
+ }
+}
+
void service::NotifyStateChange(const char* new_state) {
if (!properties_initialized()) {
// If properties aren't available yet, we can't set them.
@@ -603,14 +614,16 @@ void execute_one_command() {
}
}
-static int wait_for_coldboot_done_action(int nargs, char **args)
-{
- int ret;
- INFO("wait for %s\n", COLDBOOT_DONE);
- ret = wait_for_file(COLDBOOT_DONE, COMMAND_RETRY_TIMEOUT);
- if (ret)
+static int wait_for_coldboot_done_action(int nargs, char **args) {
+ Timer t;
+
+ NOTICE("Waiting for %s...\n", COLDBOOT_DONE);
+ if (wait_for_file(COLDBOOT_DONE, COMMAND_RETRY_TIMEOUT)) {
ERROR("Timed out waiting for %s\n", COLDBOOT_DONE);
- return ret;
+ }
+
+ NOTICE("Waiting for %s took %.2fs.\n", COLDBOOT_DONE, t.duration());
+ return 0;
}
/*
@@ -733,7 +746,7 @@ static int console_init_action(int nargs, char **args)
return 0;
}
-static void import_kernel_nv(char *name, int for_emulator)
+static void import_kernel_nv(char *name, bool for_emulator)
{
char *value = strchr(name, '=');
int name_len = strlen(name);
@@ -827,35 +840,9 @@ static void process_kernel_cmdline(void)
* second pass is only necessary for qemu to export all kernel params
* as props.
*/
- import_kernel_cmdline(0, import_kernel_nv);
+ import_kernel_cmdline(false, import_kernel_nv);
if (qemu[0])
- import_kernel_cmdline(1, import_kernel_nv);
-}
-
-static int property_service_init_action(int nargs, char **args)
-{
- /* read any property files on system or data and
- * fire up the property service. This must happen
- * after the ro.foo properties are set above so
- * that /data/local.prop cannot interfere with them.
- */
- start_property_service();
- if (get_property_set_fd() < 0) {
- ERROR("start_property_service() failed\n");
- exit(1);
- }
-
- return 0;
-}
-
-static int signal_init_action(int nargs, char **args)
-{
- signal_init();
- if (get_signal_fd() < 0) {
- ERROR("signal_init() failed\n");
- exit(1);
- }
- return 0;
+ import_kernel_cmdline(true, import_kernel_nv);
}
static int queue_property_triggers_action(int nargs, char **args)
@@ -866,13 +853,36 @@ static int queue_property_triggers_action(int nargs, char **args)
return 0;
}
-void selinux_init_all_handles(void)
+static void selinux_init_all_handles(void)
{
sehandle = selinux_android_file_context_handle();
selinux_android_set_sehandle(sehandle);
sehandle_prop = selinux_android_prop_context_handle();
}
+enum selinux_enforcing_status { SELINUX_DISABLED, SELINUX_PERMISSIVE, SELINUX_ENFORCING };
+
+static selinux_enforcing_status selinux_status_from_cmdline() {
+ selinux_enforcing_status status = SELINUX_ENFORCING;
+
+ std::function<void(char*,bool)> fn = [&](char* name, bool in_qemu) {
+ char *value = strchr(name, '=');
+ if (value == nullptr) { return; }
+ *value++ = '\0';
+ if (strcmp(name, "androidboot.selinux") == 0) {
+ if (strcmp(value, "disabled") == 0) {
+ status = SELINUX_DISABLED;
+ } else if (strcmp(value, "permissive") == 0) {
+ status = SELINUX_PERMISSIVE;
+ }
+ }
+ };
+ import_kernel_cmdline(false, fn);
+
+ return status;
+}
+
+
static bool selinux_is_disabled(void)
{
if (ALLOW_DISABLE_SELINUX) {
@@ -881,12 +891,7 @@ static bool selinux_is_disabled(void)
// via the kernel command line "selinux=0".
return true;
}
-
- char tmp[PROP_VALUE_MAX];
- if ((property_get("ro.boot.selinux", tmp) != 0) && (strcmp(tmp, "disabled") == 0)) {
- // SELinux is compiled into the kernel, but we've been told to disable it.
- return true;
- }
+ return selinux_status_from_cmdline() == SELINUX_DISABLED;
}
return false;
@@ -895,20 +900,7 @@ static bool selinux_is_disabled(void)
static bool selinux_is_enforcing(void)
{
if (ALLOW_DISABLE_SELINUX) {
- char tmp[PROP_VALUE_MAX];
- if (property_get("ro.boot.selinux", tmp) == 0) {
- // Property is not set. Assume enforcing.
- return true;
- }
-
- if (strcmp(tmp, "permissive") == 0) {
- // SELinux is in the kernel, but we've been told to go into permissive mode.
- return false;
- }
-
- if (strcmp(tmp, "enforcing") != 0) {
- ERROR("SELinux: Unknown value of ro.boot.selinux. Got: \"%s\". Assuming enforcing.\n", tmp);
- }
+ return selinux_status_from_cmdline() == SELINUX_ENFORCING;
}
return true;
}
@@ -940,7 +932,13 @@ static int audit_callback(void *data, security_class_t /*cls*/, char *buf, size_
return 0;
}
-static void selinux_initialize() {
+static void security_failure() {
+ ERROR("Security failure; rebooting into recovery mode...\n");
+ android_reboot(ANDROID_RB_RESTART2, 0, "recovery");
+ while (true) { pause(); } // never reached
+}
+
+static void selinux_initialize(bool in_kernel_domain) {
Timer t;
selinux_callback cb;
@@ -953,19 +951,25 @@ static void selinux_initialize() {
return;
}
- INFO("Loading SELinux policy...\n");
- if (selinux_android_load_policy() < 0) {
- ERROR("SELinux: Failed to load policy; rebooting into recovery mode\n");
- android_reboot(ANDROID_RB_RESTART2, 0, "recovery");
- while (1) { pause(); } // never reached
- }
+ if (in_kernel_domain) {
+ INFO("Loading SELinux policy...\n");
+ if (selinux_android_load_policy() < 0) {
+ ERROR("failed to load policy: %s\n", strerror(errno));
+ security_failure();
+ }
- selinux_init_all_handles();
- bool is_enforcing = selinux_is_enforcing();
- INFO("SELinux: security_setenforce(%d)\n", is_enforcing);
- security_setenforce(is_enforcing);
+ bool is_enforcing = selinux_is_enforcing();
+ security_setenforce(is_enforcing);
- NOTICE("(Initializing SELinux took %.2fs.)\n", t.duration());
+ if (write_file("/sys/fs/selinux/checkreqprot", "0") == -1) {
+ security_failure();
+ }
+
+ NOTICE("(Initializing SELinux %s took %.2fs.)\n",
+ is_enforcing ? "enforcing" : "non-enforcing", t.duration());
+ } else {
+ selinux_init_all_handles();
+ }
}
int main(int argc, char** argv) {
@@ -982,21 +986,18 @@ int main(int argc, char** argv) {
add_environment("PATH", _PATH_DEFPATH);
+ bool is_first_stage = (argc == 1) || (strcmp(argv[1], "--second-stage") != 0);
+
// Get the basic filesystem setup we need put together in the initramdisk
// on / and then we'll let the rc file figure out the rest.
- mkdir("/dev", 0755);
- mkdir("/proc", 0755);
- mkdir("/sys", 0755);
-
- mount("tmpfs", "/dev", "tmpfs", MS_NOSUID, "mode=0755");
- mkdir("/dev/pts", 0755);
- mkdir("/dev/socket", 0755);
- mount("devpts", "/dev/pts", "devpts", 0, NULL);
- mount("proc", "/proc", "proc", 0, NULL);
- mount("sysfs", "/sys", "sysfs", 0, NULL);
-
- // Indicate that booting is in progress to background fw loaders, etc.
- close(open("/dev/.booting", O_WRONLY | O_CREAT | O_CLOEXEC, 0000));
+ if (is_first_stage) {
+ mount("tmpfs", "/dev", "tmpfs", MS_NOSUID, "mode=0755");
+ mkdir("/dev/pts", 0755);
+ mkdir("/dev/socket", 0755);
+ mount("devpts", "/dev/pts", "devpts", 0, NULL);
+ mount("proc", "/proc", "proc", 0, NULL);
+ mount("sysfs", "/sys", "sysfs", 0, NULL);
+ }
// We must have some place other than / to create the device nodes for
// kmsg and null, otherwise we won't be able to remount / read-only
@@ -1006,20 +1007,41 @@ int main(int argc, char** argv) {
klog_init();
klog_set_level(KLOG_NOTICE_LEVEL);
- NOTICE("init started!\n");
+ NOTICE("init%s started!\n", is_first_stage ? "" : " second stage");
+
+ if (!is_first_stage) {
+ // Indicate that booting is in progress to background fw loaders, etc.
+ close(open("/dev/.booting", O_WRONLY | O_CREAT | O_CLOEXEC, 0000));
- property_init();
+ property_init();
- // If arguments are passed both on the command line and in DT,
- // properties set in DT always have priority over the command-line ones.
- process_kernel_dt();
- process_kernel_cmdline();
+ // If arguments are passed both on the command line and in DT,
+ // properties set in DT always have priority over the command-line ones.
+ process_kernel_dt();
+ process_kernel_cmdline();
- // Propogate the kernel variables to internal variables
- // used by init as well as the current required properties.
- export_kernel_boot_props();
+ // Propogate the kernel variables to internal variables
+ // used by init as well as the current required properties.
+ export_kernel_boot_props();
+ }
+
+ // Set up SELinux, including loading the SELinux policy if we're in the kernel domain.
+ selinux_initialize(is_first_stage);
- selinux_initialize();
+ // If we're in the kernel domain, re-exec init to transition to the init domain now
+ // that the SELinux policy has been loaded.
+ if (is_first_stage) {
+ if (restorecon("/init") == -1) {
+ ERROR("restorecon failed: %s\n", strerror(errno));
+ security_failure();
+ }
+ char* path = argv[0];
+ char* args[] = { path, const_cast<char*>("--second-stage"), nullptr };
+ if (execv(path, args) == -1) {
+ ERROR("execv(\"%s\") failed: %s\n", path, strerror(errno));
+ security_failure();
+ }
+ }
// These directories were necessarily created before initial policy load
// and therefore need their security context restored to the proper value.
@@ -1030,13 +1052,24 @@ int main(int argc, char** argv) {
restorecon("/dev/__properties__");
restorecon_recursive("/sys");
+ epoll_fd = epoll_create1(EPOLL_CLOEXEC);
+ if (epoll_fd == -1) {
+ ERROR("epoll_create1 failed: %s\n", strerror(errno));
+ exit(1);
+ }
+
+ signal_handler_init();
+
property_load_boot_defaults();
+ start_property_service();
init_parse_config_file("/init.rc");
action_for_each_trigger("early-init", action_add_queue_tail);
+ // Queue an action that waits for coldboot done so we know ueventd has set up all of /dev...
queue_builtin_action(wait_for_coldboot_done_action, "wait_for_coldboot_done");
+ // ... so that we can start queuing up actions that require stuff from /dev.
queue_builtin_action(mix_hwrng_into_linux_rng_action, "mix_hwrng_into_linux_rng");
queue_builtin_action(keychord_init_action, "keychord_init");
queue_builtin_action(console_init_action, "console_init");
@@ -1047,8 +1080,6 @@ int main(int argc, char** argv) {
// Repeat mix_hwrng_into_linux_rng in case /dev/hw_random or /dev/random
// wasn't ready immediately after wait_for_coldboot_done
queue_builtin_action(mix_hwrng_into_linux_rng_action, "mix_hwrng_into_linux_rng");
- queue_builtin_action(property_service_init_action, "property_service_init");
- queue_builtin_action(signal_init_action, "signal_init");
// Don't mount filesystems or start core system services in charger mode.
char bootmode[PROP_VALUE_MAX];
@@ -1061,41 +1092,12 @@ int main(int argc, char** argv) {
// Run all property triggers based on current state of the properties.
queue_builtin_action(queue_property_triggers_action, "queue_property_triggers");
- // TODO: why do we only initialize ufds after execute_one_command and restart_processes?
- size_t fd_count = 0;
- struct pollfd ufds[3];
- bool property_set_fd_init = false;
- bool signal_fd_init = false;
- bool keychord_fd_init = false;
-
- for (;;) {
+ while (true) {
if (!waiting_for_exec) {
execute_one_command();
restart_processes();
}
- if (!property_set_fd_init && get_property_set_fd() > 0) {
- ufds[fd_count].fd = get_property_set_fd();
- ufds[fd_count].events = POLLIN;
- ufds[fd_count].revents = 0;
- fd_count++;
- property_set_fd_init = true;
- }
- if (!signal_fd_init && get_signal_fd() > 0) {
- ufds[fd_count].fd = get_signal_fd();
- ufds[fd_count].events = POLLIN;
- ufds[fd_count].revents = 0;
- fd_count++;
- signal_fd_init = true;
- }
- if (!keychord_fd_init && get_keychord_fd() > 0) {
- ufds[fd_count].fd = get_keychord_fd();
- ufds[fd_count].events = POLLIN;
- ufds[fd_count].revents = 0;
- fd_count++;
- keychord_fd_init = true;
- }
-
int timeout = -1;
if (process_needs_restart) {
timeout = (process_needs_restart - gettime()) * 1000;
@@ -1109,21 +1111,12 @@ int main(int argc, char** argv) {
bootchart_sample(&timeout);
- int nr = poll(ufds, fd_count, timeout);
- if (nr <= 0) {
- continue;
- }
-
- for (size_t i = 0; i < fd_count; i++) {
- if (ufds[i].revents & POLLIN) {
- if (ufds[i].fd == get_property_set_fd()) {
- handle_property_set_fd();
- } else if (ufds[i].fd == get_keychord_fd()) {
- handle_keychord();
- } else if (ufds[i].fd == get_signal_fd()) {
- handle_signal();
- }
- }
+ epoll_event ev;
+ int nr = TEMP_FAILURE_RETRY(epoll_wait(epoll_fd, &ev, 1, timeout));
+ if (nr == -1) {
+ ERROR("epoll_wait failed: %s\n", strerror(errno));
+ } else if (nr == 1) {
+ ((void (*)()) ev.data.ptr)();
}
}
diff --git a/init/init.h b/init/init.h
index a104af607..1cabb147e 100644
--- a/init/init.h
+++ b/init/init.h
@@ -155,4 +155,6 @@ int selinux_reload_policy(void);
void zap_stdio(void);
+void register_epoll_handler(int fd, void (*fn)());
+
#endif /* _INIT_INIT_H */
diff --git a/init/init_parser.cpp b/init/init_parser.cpp
index ff31093cb..b76b04ee5 100644
--- a/init/init_parser.cpp
+++ b/init/init_parser.cpp
@@ -184,7 +184,6 @@ static int lookup_keyword(const char *s)
case 's':
if (!strcmp(s, "eclabel")) return K_seclabel;
if (!strcmp(s, "ervice")) return K_service;
- if (!strcmp(s, "etcon")) return K_setcon;
if (!strcmp(s, "etenv")) return K_setenv;
if (!strcmp(s, "etprop")) return K_setprop;
if (!strcmp(s, "etrlimit")) return K_setrlimit;
diff --git a/init/keychords.cpp b/init/keychords.cpp
index 27894a2e8..10d9573f2 100644
--- a/init/keychords.cpp
+++ b/init/keychords.cpp
@@ -62,37 +62,7 @@ void add_service_keycodes(struct service *svc)
}
}
-void keychord_init()
-{
- int fd, ret;
-
- service_for_each(add_service_keycodes);
-
- /* nothing to do if no services require keychords */
- if (!keychords)
- return;
-
- fd = open("/dev/keychord", O_RDWR | O_CLOEXEC);
- if (fd < 0) {
- ERROR("could not open /dev/keychord\n");
- return;
- }
-
- ret = write(fd, keychords, keychords_length);
- if (ret != keychords_length) {
- ERROR("could not configure /dev/keychord %d: %s\n", ret, strerror(errno));
- close(fd);
- fd = -1;
- }
-
- free(keychords);
- keychords = 0;
-
- keychord_fd = fd;
-}
-
-void handle_keychord()
-{
+static void handle_keychord() {
struct service *svc;
char adb_enabled[PROP_VALUE_MAX];
int ret;
@@ -117,7 +87,28 @@ void handle_keychord()
}
}
-int get_keychord_fd()
-{
- return keychord_fd;
+void keychord_init() {
+ service_for_each(add_service_keycodes);
+
+ // Nothing to do if no services require keychords.
+ if (!keychords) {
+ return;
+ }
+
+ keychord_fd = TEMP_FAILURE_RETRY(open("/dev/keychord", O_RDWR | O_CLOEXEC));
+ if (keychord_fd == -1) {
+ ERROR("could not open /dev/keychord: %s\n", strerror(errno));
+ return;
+ }
+
+ int ret = write(keychord_fd, keychords, keychords_length);
+ if (ret != keychords_length) {
+ ERROR("could not configure /dev/keychord %d: %s\n", ret, strerror(errno));
+ close(keychord_fd);
+ }
+
+ free(keychords);
+ keychords = nullptr;
+
+ register_epoll_handler(keychord_fd, handle_keychord);
}
diff --git a/init/keychords.h b/init/keychords.h
index 070b85891..d2723b71c 100644
--- a/init/keychords.h
+++ b/init/keychords.h
@@ -19,9 +19,7 @@
struct service;
-void add_service_keycodes(struct service *svc);
-void keychord_init(void);
-void handle_keychord(void);
-int get_keychord_fd(void);
+void add_service_keycodes(service*);
+void keychord_init();
#endif
diff --git a/init/keywords.h b/init/keywords.h
index 059dde126..37f01b861 100644
--- a/init/keywords.h
+++ b/init/keywords.h
@@ -20,7 +20,6 @@ int do_restorecon(int nargs, char **args);
int do_restorecon_recursive(int nargs, char **args);
int do_rm(int nargs, char **args);
int do_rmdir(int nargs, char **args);
-int do_setcon(int nargs, char **args);
int do_setprop(int nargs, char **args);
int do_setrlimit(int nargs, char **args);
int do_start(int nargs, char **args);
@@ -76,7 +75,6 @@ enum {
KEYWORD(rmdir, COMMAND, 1, do_rmdir)
KEYWORD(seclabel, OPTION, 0, 0)
KEYWORD(service, SECTION, 0, 0)
- KEYWORD(setcon, COMMAND, 1, do_setcon)
KEYWORD(setenv, OPTION, 2, 0)
KEYWORD(setprop, COMMAND, 2, do_setprop)
KEYWORD(setrlimit, COMMAND, 3, do_setrlimit)
diff --git a/init/property_service.cpp b/init/property_service.cpp
index 8544951c1..930ef8213 100644
--- a/init/property_service.cpp
+++ b/init/property_service.cpp
@@ -246,7 +246,7 @@ int property_set(const char* name, const char* value) {
return rc;
}
-void handle_property_set_fd()
+static void handle_property_set_fd()
{
prop_msg msg;
int s;
@@ -519,16 +519,14 @@ void load_all_props() {
}
void start_property_service() {
- int fd = create_socket(PROP_SERVICE_NAME, SOCK_STREAM, 0666, 0, 0, NULL);
- if (fd == -1) return;
-
- fcntl(fd, F_SETFD, FD_CLOEXEC);
- fcntl(fd, F_SETFL, O_NONBLOCK);
+ property_set_fd = create_socket(PROP_SERVICE_NAME, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK,
+ 0666, 0, 0, NULL);
+ if (property_set_fd == -1) {
+ ERROR("start_property_service socket creation failed: %s\n", strerror(errno));
+ exit(1);
+ }
- listen(fd, 8);
- property_set_fd = fd;
-}
+ listen(property_set_fd, 8);
-int get_property_set_fd() {
- return property_set_fd;
+ register_epoll_handler(property_set_fd, handle_property_set_fd);
}
diff --git a/init/property_service.h b/init/property_service.h
index 825a7dd2a..a27053d93 100644
--- a/init/property_service.h
+++ b/init/property_service.h
@@ -20,7 +20,6 @@
#include <stddef.h>
#include <sys/system_properties.h>
-extern void handle_property_set_fd(void);
extern void property_init(void);
extern void property_load_boot_defaults(void);
extern void load_persist_props(void);
@@ -30,7 +29,6 @@ void get_property_workspace(int *fd, int *sz);
extern int __property_get(const char *name, char *value);
extern int property_set(const char *name, const char *value);
extern bool properties_initialized();
-int get_property_set_fd(void);
#ifndef __clang__
extern void __property_get_size_error()
diff --git a/init/readme.txt b/init/readme.txt
index 84afd11d8..6b9c42d3f 100644
--- a/init/readme.txt
+++ b/init/readme.txt
@@ -252,11 +252,6 @@ rm <path>
rmdir <path>
Calls rmdir(2) on the given path.
-setcon <seclabel>
- Set the current process security context to the specified string.
- This is typically only used from early-init to set the init context
- before any other process is started.
-
setprop <name> <value>
Set system property <name> to <value>. Properties are expanded
within <value>.
diff --git a/init/signal_handler.cpp b/init/signal_handler.cpp
index 8be4af59c..39a466dee 100644
--- a/init/signal_handler.cpp
+++ b/init/signal_handler.cpp
@@ -35,14 +35,10 @@
#define CRITICAL_CRASH_THRESHOLD 4 /* if we crash >4 times ... */
#define CRITICAL_CRASH_WINDOW (4*60) /* ... in 4 minutes, goto recovery */
-static int signal_fd = -1;
-static int signal_recv_fd = -1;
+static int signal_write_fd = -1;
+static int signal_read_fd = -1;
-static void sigchld_handler(int s) {
- write(signal_fd, &s, 1);
-}
-
-std::string DescribeStatus(int status) {
+static std::string DescribeStatus(int status) {
if (WIFEXITED(status)) {
return android::base::StringPrintf("exited with status %d", WEXITSTATUS(status));
} else if (WIFSIGNALED(status)) {
@@ -54,11 +50,14 @@ std::string DescribeStatus(int status) {
}
}
-static int wait_for_one_process() {
+static bool wait_for_one_process() {
int status;
pid_t pid = TEMP_FAILURE_RETRY(waitpid(-1, &status, WNOHANG));
- if (pid <= 0) {
- return -1;
+ if (pid == 0) {
+ return false;
+ } else if (pid == -1) {
+ ERROR("waitpid failed: %s\n", strerror(errno));
+ return false;
}
service* svc = service_find_by_pid(pid);
@@ -73,7 +72,7 @@ static int wait_for_one_process() {
NOTICE("%s %s\n", name.c_str(), DescribeStatus(status).c_str());
if (!svc) {
- return 0;
+ return true;
}
// TODO: all the code from here down should be a member function on service.
@@ -96,7 +95,7 @@ static int wait_for_one_process() {
list_remove(&svc->slist);
free(svc->name);
free(svc);
- return 0;
+ return true;
}
svc->pid = 0;
@@ -111,7 +110,7 @@ static int wait_for_one_process() {
// Disabled and reset processes do not get restarted automatically.
if (svc->flags & (SVC_DISABLED | SVC_RESET)) {
svc->NotifyStateChange("stopped");
- return 0;
+ return true;
}
time_t now = gettime();
@@ -122,7 +121,7 @@ static int wait_for_one_process() {
"rebooting into recovery mode\n", svc->name,
CRITICAL_CRASH_THRESHOLD, CRITICAL_CRASH_WINDOW / 60);
android_reboot(ANDROID_RB_RESTART2, 0, "recovery");
- return 0;
+ return true;
}
} else {
svc->time_crashed = now;
@@ -140,34 +139,47 @@ static int wait_for_one_process() {
cmd->func(cmd->nargs, cmd->args);
}
svc->NotifyStateChange("restarting");
- return 0;
+ return true;
+}
+
+static void reap_any_outstanding_children() {
+ while (wait_for_one_process()) {
+ }
+}
+
+static void handle_signal() {
+ // Clear outstanding requests.
+ char buf[32];
+ read(signal_read_fd, buf, sizeof(buf));
+
+ reap_any_outstanding_children();
}
-void handle_signal() {
- // We got a SIGCHLD - reap and restart as needed.
- char tmp[32];
- read(signal_recv_fd, tmp, sizeof(tmp));
- while (!wait_for_one_process()) {
+static void SIGCHLD_handler(int) {
+ if (TEMP_FAILURE_RETRY(write(signal_write_fd, "1", 1)) == -1) {
+ ERROR("write(signal_write_fd) failed: %s\n", strerror(errno));
}
}
-void signal_init() {
+void signal_handler_init() {
+ // Create a signalling mechanism for SIGCHLD.
+ int s[2];
+ if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC, 0, s) == -1) {
+ ERROR("socketpair failed: %s\n", strerror(errno));
+ exit(1);
+ }
+
+ signal_write_fd = s[0];
+ signal_read_fd = s[1];
+
+ // Write to signal_write_fd if we catch SIGCHLD.
struct sigaction act;
memset(&act, 0, sizeof(act));
- act.sa_handler = sigchld_handler;
+ act.sa_handler = SIGCHLD_handler;
act.sa_flags = SA_NOCLDSTOP;
sigaction(SIGCHLD, &act, 0);
- // Create a signalling mechanism for the sigchld handler.
- int s[2];
- if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC, 0, s) == 0) {
- signal_fd = s[0];
- signal_recv_fd = s[1];
- }
-
- handle_signal();
-}
+ reap_any_outstanding_children();
-int get_signal_fd() {
- return signal_recv_fd;
+ register_epoll_handler(signal_read_fd, handle_signal);
}
diff --git a/init/signal_handler.h b/init/signal_handler.h
index b092ccb6e..449b4af80 100644
--- a/init/signal_handler.h
+++ b/init/signal_handler.h
@@ -17,8 +17,6 @@
#ifndef _INIT_SIGNAL_HANDLER_H_
#define _INIT_SIGNAL_HANDLER_H_
-void signal_init(void);
-void handle_signal(void);
-int get_signal_fd(void);
+void signal_handler_init(void);
#endif
diff --git a/init/util.cpp b/init/util.cpp
index 3b49b30d3..9343145a7 100644
--- a/init/util.cpp
+++ b/init/util.cpp
@@ -179,9 +179,13 @@ bool read_file(const char* path, std::string* content) {
int write_file(const char* path, const char* content) {
int fd = TEMP_FAILURE_RETRY(open(path, O_WRONLY|O_CREAT|O_NOFOLLOW|O_CLOEXEC, 0600));
if (fd == -1) {
- return -errno;
+ NOTICE("write_file: Unable to open '%s': %s\n", path, strerror(errno));
+ return -1;
+ }
+ int result = android::base::WriteStringToFd(content, fd) ? 0 : -1;
+ if (result == -1) {
+ NOTICE("write_file: Unable to write to '%s': %s\n", path, strerror(errno));
}
- int result = android::base::WriteStringToFd(content, fd) ? 0 : -errno;
TEMP_FAILURE_RETRY(close(fd));
return result;
}
@@ -375,27 +379,31 @@ int wait_for_file(const char *filename, int timeout)
void open_devnull_stdio(void)
{
- int fd;
- static const char *name = "/dev/__null__";
- if (mknod(name, S_IFCHR | 0600, (1 << 8) | 3) == 0) {
- fd = open(name, O_RDWR);
- unlink(name);
- if (fd >= 0) {
- dup2(fd, 0);
- dup2(fd, 1);
- dup2(fd, 2);
- if (fd > 2) {
- close(fd);
- }
- return;
+ // Try to avoid the mknod() call if we can. Since SELinux makes
+ // a /dev/null replacement available for free, let's use it.
+ int fd = open("/sys/fs/selinux/null", O_RDWR);
+ if (fd == -1) {
+ // OOPS, /sys/fs/selinux/null isn't available, likely because
+ // /sys/fs/selinux isn't mounted. Fall back to mknod.
+ static const char *name = "/dev/__null__";
+ if (mknod(name, S_IFCHR | 0600, (1 << 8) | 3) == 0) {
+ fd = open(name, O_RDWR);
+ unlink(name);
+ }
+ if (fd == -1) {
+ exit(1);
}
}
- exit(1);
+ dup2(fd, 0);
+ dup2(fd, 1);
+ dup2(fd, 2);
+ if (fd > 2) {
+ close(fd);
+ }
}
-void import_kernel_cmdline(int in_qemu,
- void (*import_kernel_nv)(char *name, int in_qemu))
+void import_kernel_cmdline(bool in_qemu, std::function<void(char*,bool)> import_kernel_nv)
{
char cmdline[2048];
char *ptr;
diff --git a/init/util.h b/init/util.h
index 8fec7a801..6864acf15 100644
--- a/init/util.h
+++ b/init/util.h
@@ -21,6 +21,7 @@
#include <sys/types.h>
#include <string>
+#include <functional>
#define ARRAY_SIZE(x) (sizeof(x)/sizeof(x[0]))
@@ -57,7 +58,7 @@ void make_link(const char *oldpath, const char *newpath);
void remove_link(const char *oldpath, const char *newpath);
int wait_for_file(const char *filename, int timeout);
void open_devnull_stdio(void);
-void import_kernel_cmdline(int in_qemu, void (*import_kernel_nv)(char *name, int in_qemu));
+void import_kernel_cmdline(bool in_qemu, std::function<void(char*,bool)>);
int make_dir(const char *path, mode_t mode);
int restorecon(const char *pathname);
int restorecon_recursive(const char *pathname);