diff --git a/init/Android.mk b/init/Android.mk index a3b01e1db..ec2861bf1 100644 --- a/init/Android.mk +++ b/init/Android.mk @@ -75,6 +75,7 @@ include $(BUILD_EXECUTABLE) include $(CLEAR_VARS) LOCAL_MODULE := init_tests LOCAL_SRC_FILES := \ + init_parser_test.cpp \ util_test.cpp \ LOCAL_SHARED_LIBRARIES += \ diff --git a/init/builtins.cpp b/init/builtins.cpp index 9f3dcc1c6..03b143d04 100644 --- a/init/builtins.cpp +++ b/init/builtins.cpp @@ -172,11 +172,16 @@ int do_enable(int nargs, char **args) return 0; } -int do_exec(int nargs, char **args) -{ - return -1; +int do_exec(int nargs, char** args) { + service* svc = make_exec_oneshot_service(nargs, args); + if (svc == NULL) { + return -1; + } + service_start(svc, NULL); + return 0; } +// TODO: remove execonce when exec is available. int do_execonce(int nargs, char **args) { pid_t child; diff --git a/init/init.cpp b/init/init.cpp index 41ceb0ae5..579d0e702 100644 --- a/init/init.cpp +++ b/init/init.cpp @@ -14,37 +14,37 @@ * limitations under the License. */ +#include +#include +#include +#include +#include +#include #include #include #include -#include -#include -#include -#include -#include #include -#include #include -#include -#include -#include -#include #include +#include +#include #include +#include +#include +#include + +#include #include #include #include -#include - -#include #include -#include -#include #include +#include +#include +#include #include -#include #include "devices.h" #include "init.h" @@ -72,22 +72,35 @@ static char qemu[32]; static struct action *cur_action = NULL; static struct command *cur_command = NULL; -void notify_service_state(const char *name, const char *state) -{ - char pname[PROP_NAME_MAX]; - int len = strlen(name); - if ((len + 10) > PROP_NAME_MAX) - return; - snprintf(pname, sizeof(pname), "init.svc.%s", name); - property_set(pname, state); -} - static int have_console; static char console_name[PROP_VALUE_MAX] = "/dev/console"; static time_t process_needs_restart; static const char *ENV[32]; +bool waiting_for_exec = false; + +void service::NotifyStateChange(const char* new_state) { + if (!properties_inited()) { + // If properties aren't available yet, we can't set them. + return; + } + + if ((flags & SVC_EXEC) != 0) { + // 'exec' commands don't have properties tracking their state. + return; + } + + char prop_name[PROP_NAME_MAX]; + if (snprintf(prop_name, sizeof(prop_name), "init.svc.%s", name) >= PROP_NAME_MAX) { + // If the property name would be too long, we can't set it. + ERROR("Property name \"init.svc.%s\" too long; not setting to %s\n", name, new_state); + return; + } + + property_set(prop_name, new_state); +} + /* add_environment - add "key=value" to the current environment */ int add_environment(const char *key, const char *val) { @@ -160,35 +173,26 @@ static void publish_socket(const char *name, int fd) void service_start(struct service *svc, const char *dynamic_args) { - struct stat s; - pid_t pid; - int needs_console; - char *scon = NULL; - int rc; - - /* starting a service removes it from the disabled or reset - * state and immediately takes it out of the restarting - * state if it was in there - */ + // Starting a service removes it from the disabled or reset state and + // immediately takes it out of the restarting state if it was in there. svc->flags &= (~(SVC_DISABLED|SVC_RESTARTING|SVC_RESET|SVC_RESTART|SVC_DISABLED_START)); svc->time_started = 0; - /* running processes require no additional work -- if - * they're in the process of exiting, we've ensured - * that they will immediately restart on exit, unless - * they are ONESHOT - */ + // Running processes require no additional work --- if they're in the + // process of exiting, we've ensured that they will immediately restart + // on exit, unless they are ONESHOT. if (svc->flags & SVC_RUNNING) { return; } - needs_console = (svc->flags & SVC_CONSOLE) ? 1 : 0; - if (needs_console && (!have_console)) { + bool needs_console = (svc->flags & SVC_CONSOLE); + if (needs_console && !have_console) { ERROR("service '%s' requires console\n", svc->name); svc->flags |= SVC_DISABLED; return; } + struct stat s; if (stat(svc->args[0], &s) != 0) { ERROR("cannot find '%s', disabling '%s'\n", svc->args[0], svc->name); svc->flags |= SVC_DISABLED; @@ -202,6 +206,7 @@ void service_start(struct service *svc, const char *dynamic_args) return; } + char* scon = NULL; if (is_selinux_enabled() > 0) { if (svc->seclabel) { scon = strdup(svc->seclabel); @@ -213,7 +218,7 @@ void service_start(struct service *svc, const char *dynamic_args) char *mycon = NULL, *fcon = NULL; INFO("computing context for service '%s'\n", svc->args[0]); - rc = getcon(&mycon); + int rc = getcon(&mycon); if (rc < 0) { ERROR("could not get context while starting '%s'\n", svc->name); return; @@ -241,8 +246,7 @@ void service_start(struct service *svc, const char *dynamic_args) NOTICE("starting '%s'\n", svc->name); - pid = fork(); - + pid_t pid = fork(); if (pid == 0) { struct socketinfo *si; struct svcenvinfo *ei; @@ -298,7 +302,7 @@ void service_start(struct service *svc, const char *dynamic_args) setpgid(0, getpid()); - /* as requested, set our gid, supplemental gids, and uid */ + // As requested, set our gid, supplemental gids, and uid. if (svc->gid) { if (setgid(svc->gid) != 0) { ERROR("setgid failed: %s\n", strerror(errno)); @@ -361,8 +365,13 @@ void service_start(struct service *svc, const char *dynamic_args) svc->pid = pid; svc->flags |= SVC_RUNNING; - if (properties_inited()) - notify_service_state(svc->name, "running"); + if ((svc->flags & SVC_EXEC) != 0) { + INFO("SVC_EXEC pid %d (uid %d gid %d+%d context %s) started; waiting...\n", + svc->pid, svc->uid, svc->gid, svc->nr_supp_gids, svc->seclabel); + waiting_for_exec = true; + } + + svc->NotifyStateChange("running"); } /* The how field should be either SVC_DISABLED, SVC_RESET, or SVC_RESTART */ @@ -388,9 +397,9 @@ static void service_stop_or_reset(struct service *svc, int how) if (svc->pid) { NOTICE("service '%s' is being killed\n", svc->name); kill(-svc->pid, SIGKILL); - notify_service_state(svc->name, "stopping"); + svc->NotifyStateChange("stopping"); } else { - notify_service_state(svc->name, "stopped"); + svc->NotifyStateChange("stopped"); } } @@ -969,28 +978,18 @@ static void selinux_initialize(void) security_setenforce(is_enforcing); } -int main(int argc, char **argv) -{ - size_t fd_count = 0; - struct pollfd ufds[4]; - int property_set_fd_init = 0; - int signal_fd_init = 0; - int keychord_fd_init = 0; - bool is_charger = false; - +int main(int argc, char** argv) { if (!strcmp(basename(argv[0]), "ueventd")) return ueventd_main(argc, argv); if (!strcmp(basename(argv[0]), "watchdogd")) return watchdogd_main(argc, argv); - /* clear the umask */ + // Clear the umask. umask(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. - */ + // 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); @@ -1002,15 +1001,13 @@ int main(int argc, char **argv) mount("proc", "/proc", "proc", 0, NULL); mount("sysfs", "/sys", "sysfs", 0, NULL); - /* indicate that booting is in progress to background fw loaders, etc */ + // Indicate that booting is in progress to background fw loaders, etc. close(open("/dev/.booting", O_WRONLY | O_CREAT | O_CLOEXEC, 0000)); - /* 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 later on. - * Now that tmpfs is mounted on /dev, we can actually - * talk to the outside world. - */ + // 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 + // later on. Now that tmpfs is mounted on /dev, we can actually talk + // to the outside world. open_devnull_stdio(); klog_init(); property_init(); @@ -1019,25 +1016,22 @@ int main(int argc, char **argv) process_kernel_cmdline(); - union selinux_callback cb; + selinux_callback cb; cb.func_log = log_callback; selinux_set_callback(SELINUX_CB_LOG, cb); - cb.func_audit = audit_callback; selinux_set_callback(SELINUX_CB_AUDIT, cb); selinux_initialize(); - /* These directories were necessarily created before initial policy load - * and therefore need their security context restored to the proper value. - * This must happen before /dev is populated by ueventd. - */ + + // These directories were necessarily created before initial policy load + // and therefore need their security context restored to the proper value. + // This must happen before /dev is populated by ueventd. restorecon("/dev"); restorecon("/dev/socket"); restorecon("/dev/__properties__"); restorecon_recursive("/sys"); - is_charger = !strcmp(bootmode, "charger"); - INFO("property init\n"); property_load_boot_defaults(); @@ -1050,50 +1044,58 @@ int main(int argc, char **argv) queue_builtin_action(keychord_init_action, "keychord_init"); queue_builtin_action(console_init_action, "console_init"); - /* execute all the boot actions to get us started */ + // Execute all the boot actions to get us started. action_for_each_trigger("init", action_add_queue_tail); - /* Repeat mix_hwrng_into_linux_rng in case /dev/hw_random or /dev/random - * wasn't ready immediately after wait_for_coldboot_done - */ + // 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 if in charger mode. */ - if (is_charger) { + // Don't mount filesystems or start core system services in charger mode. + if (strcmp(bootmode, "charger") == 0) { action_for_each_trigger("charger", action_add_queue_tail); } else { action_for_each_trigger("late-init", action_add_queue_tail); } - /* run all property triggers based on current state of the properties */ + // 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 (;;) { - execute_one_command(); - restart_processes(); + 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 = 1; + 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 = 1; + 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 = 1; + keychord_fd_init = true; } int timeout = -1; diff --git a/init/init.h b/init/init.h index eedec276f..a104af607 100644 --- a/init/init.h +++ b/init/init.h @@ -17,13 +17,11 @@ #ifndef _INIT_INIT_H #define _INIT_INIT_H +#include + #include #include -#include - -void handle_control_message(const char *msg, const char *arg); - struct command { /* list of commands in an action */ @@ -59,8 +57,6 @@ struct action { struct command *current; }; -void build_triggers_string(char *name_str, int length, struct action *cur_action); - struct socketinfo { struct socketinfo *next; const char *name; @@ -77,27 +73,29 @@ struct svcenvinfo { const char *value; }; -#define SVC_DISABLED 0x01 /* do not autostart with class */ -#define SVC_ONESHOT 0x02 /* do not restart on exit */ -#define SVC_RUNNING 0x04 /* currently active */ -#define SVC_RESTARTING 0x08 /* waiting to restart */ -#define SVC_CONSOLE 0x10 /* requires console */ -#define SVC_CRITICAL 0x20 /* will reboot into recovery if keeps crashing */ -#define SVC_RESET 0x40 /* Use when stopping a process, but not disabling - so it can be restarted with its class */ -#define SVC_RC_DISABLED 0x80 /* Remember if the disabled flag was set in the rc script */ -#define SVC_RESTART 0x100 /* Use to safely restart (stop, wait, start) a service */ -#define SVC_DISABLED_START 0x200 /* a start was requested but it was disabled at the time */ +#define SVC_DISABLED 0x001 // do not autostart with class +#define SVC_ONESHOT 0x002 // do not restart on exit +#define SVC_RUNNING 0x004 // currently active +#define SVC_RESTARTING 0x008 // waiting to restart +#define SVC_CONSOLE 0x010 // requires console +#define SVC_CRITICAL 0x020 // will reboot into recovery if keeps crashing +#define SVC_RESET 0x040 // Use when stopping a process, but not disabling so it can be restarted with its class. +#define SVC_RC_DISABLED 0x080 // Remember if the disabled flag was set in the rc script. +#define SVC_RESTART 0x100 // Use to safely restart (stop, wait, start) a service. +#define SVC_DISABLED_START 0x200 // A start was requested but it was disabled at the time. +#define SVC_EXEC 0x400 // This synthetic service corresponds to an 'exec'. #define NR_SVC_SUPP_GIDS 12 /* twelve supplementary groups */ #define COMMAND_RETRY_TIMEOUT 5 struct service { + void NotifyStateChange(const char* new_state); + /* list of all services */ struct listnode slist; - const char *name; + char *name; const char *classname; unsigned flags; @@ -105,19 +103,19 @@ struct service { time_t time_started; /* time of last start */ time_t time_crashed; /* first crash within inspection window */ int nr_crashed; /* number of times crashed within window */ - + uid_t uid; gid_t gid; gid_t supp_gids[NR_SVC_SUPP_GIDS]; size_t nr_supp_gids; - char *seclabel; + const char* seclabel; struct socketinfo *sockets; struct svcenvinfo *envvars; struct action onrestart; /* Actions to execute on restart. */ - + /* keycodes for triggering this service via /dev/keychord */ int *keycodes; int nkeycodes; @@ -131,7 +129,13 @@ struct service { char *args[1]; }; /* ^-------'args' MUST be at the end of this struct! */ -void notify_service_state(const char *name, const char *state); +extern bool waiting_for_exec; +extern struct selabel_handle *sehandle; +extern struct selabel_handle *sehandle_prop; + +void build_triggers_string(char *name_str, int length, struct action *cur_action); + +void handle_control_message(const char *msg, const char *arg); struct service *service_find_by_name(const char *name); struct service *service_find_by_pid(pid_t pid); @@ -147,9 +151,8 @@ void service_restart(struct service *svc); void service_start(struct service *svc, const char *dynamic_args); void property_changed(const char *name, const char *value); -extern struct selabel_handle *sehandle; -extern struct selabel_handle *sehandle_prop; -extern int selinux_reload_policy(void); +int selinux_reload_policy(void); + void zap_stdio(void); #endif /* _INIT_INIT_H */ diff --git a/init/init_parser.cpp b/init/init_parser.cpp index 61a5e0a74..691db7691 100644 --- a/init/init_parser.cpp +++ b/init/init_parser.cpp @@ -14,6 +14,7 @@ * limitations under the License. */ +#include #include #include #include @@ -437,7 +438,7 @@ parser_done: } int init_parse_config_file(const char* path) { - INFO("Parsing %s...", path); + INFO("Parsing %s...\n", path); std::string data; if (!read_file(path, &data)) { return -1; @@ -660,6 +661,65 @@ int action_queue_empty() return list_empty(&action_queue); } +service* make_exec_oneshot_service(int nargs, char** args) { + // Parse the arguments: exec [SECLABEL [UID [GID]*] --] COMMAND ARGS... + int command_arg = 1; + for (int i = 1; i < nargs; ++i) { + if (strcmp(args[i], "--") == 0) { + command_arg = i + 1; + break; + } + } + if (command_arg > 4 + NR_SVC_SUPP_GIDS) { + ERROR("exec called with too many supplementary group ids\n"); + return NULL; + } + + int argc = nargs - command_arg; + char** argv = (args + command_arg); + if (argc < 1) { + ERROR("exec called without command\n"); + return NULL; + } + + service* svc = (service*) calloc(1, sizeof(*svc) + sizeof(char*) * argc); + if (svc == NULL) { + ERROR("Couldn't allocate service for exec of '%s': %s", argv[0], strerror(errno)); + return NULL; + } + + if (command_arg > 2) { + svc->seclabel = args[1]; + } + if (command_arg > 3) { + svc->uid = decode_uid(args[2]); + } + if (command_arg > 4) { + svc->gid = decode_uid(args[3]); + svc->nr_supp_gids = command_arg - 1 /* -- */ - 4 /* exec SECLABEL UID GID */; + for (size_t i = 0; i < svc->nr_supp_gids; ++i) { + svc->supp_gids[i] = decode_uid(args[4 + i]); + } + } + + static int exec_count; // Every service needs a unique name. + char* name = NULL; + asprintf(&name, "exec %d (%s)", exec_count++, argv[0]); + if (name == NULL) { + ERROR("Couldn't allocate name for exec service '%s'\n", argv[0]); + free(svc); + return NULL; + } + svc->name = name; + svc->classname = "default"; + svc->flags = SVC_EXEC | SVC_ONESHOT; + svc->nargs = argc; + memcpy(svc->args, argv, sizeof(char*) * svc->nargs); + svc->args[argc] = NULL; + list_add_tail(&service_list, &svc->slist); + return svc; +} + static void *parse_service(struct parse_state *state, int nargs, char **args) { if (nargs < 3) { @@ -683,7 +743,7 @@ static void *parse_service(struct parse_state *state, int nargs, char **args) parse_error(state, "out of memory\n"); return 0; } - svc->name = args[1]; + svc->name = strdup(args[1]); svc->classname = "default"; memcpy(svc->args, args + 2, sizeof(char*) * nargs); trigger* cur_trigger = (trigger*) calloc(1, sizeof(*cur_trigger)); diff --git a/init/init_parser.h b/init/init_parser.h index 0047da701..6348607e5 100644 --- a/init/init_parser.h +++ b/init/init_parser.h @@ -20,6 +20,7 @@ #define INIT_PARSER_MAXARGS 64 struct action; +struct service; struct action *action_remove_queue_head(void); void action_add_queue_tail(struct action *act); @@ -33,4 +34,6 @@ void queue_builtin_action(int (*func)(int nargs, char **args), const char *name) int init_parse_config_file(const char *fn); int expand_props(char *dst, const char *src, int len); +service* make_exec_oneshot_service(int argc, char** argv); + #endif diff --git a/init/init_parser_test.cpp b/init/init_parser_test.cpp new file mode 100644 index 000000000..170a73a93 --- /dev/null +++ b/init/init_parser_test.cpp @@ -0,0 +1,134 @@ +/* + * Copyright (C) 2015 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 "init_parser.h" + +#include "init.h" +#include "util.h" + +#include +#include + +TEST(init_parser, make_exec_oneshot_service_invalid_syntax) { + char* argv[10]; + memset(argv, 0, sizeof(argv)); + + // Nothing. + ASSERT_EQ(nullptr, make_exec_oneshot_service(0, argv)); + + // No arguments to 'exec'. + argv[0] = const_cast("exec"); + ASSERT_EQ(nullptr, make_exec_oneshot_service(1, argv)); + + // No command in "exec --". + argv[1] = const_cast("--"); + ASSERT_EQ(nullptr, make_exec_oneshot_service(2, argv)); +} + +TEST(init_parser, make_exec_oneshot_service_too_many_supplementary_gids) { + int argc = 0; + char* argv[4 + NR_SVC_SUPP_GIDS + 3]; + argv[argc++] = const_cast("exec"); + argv[argc++] = const_cast("seclabel"); + argv[argc++] = const_cast("root"); // uid. + argv[argc++] = const_cast("root"); // gid. + for (int i = 0; i < NR_SVC_SUPP_GIDS; ++i) { + argv[argc++] = const_cast("root"); // Supplementary gid. + } + argv[argc++] = const_cast("--"); + argv[argc++] = const_cast("/system/bin/id"); + argv[argc] = nullptr; + ASSERT_EQ(nullptr, make_exec_oneshot_service(argc, argv)); +} + +static void Test_make_exec_oneshot_service(bool dash_dash, bool seclabel, bool uid, bool gid, bool supplementary_gids) { + int argc = 0; + char* argv[10]; + argv[argc++] = const_cast("exec"); + if (seclabel) { + argv[argc++] = const_cast("u:r:su:s0"); // seclabel + if (uid) { + argv[argc++] = const_cast("log"); // uid + if (gid) { + argv[argc++] = const_cast("shell"); // gid + if (supplementary_gids) { + argv[argc++] = const_cast("system"); // supplementary gid 0 + argv[argc++] = const_cast("adb"); // supplementary gid 1 + } + } + } + } + if (dash_dash) { + argv[argc++] = const_cast("--"); + } + argv[argc++] = const_cast("/system/bin/toybox"); + argv[argc++] = const_cast("id"); + argv[argc] = nullptr; + service* svc = make_exec_oneshot_service(argc, argv); + ASSERT_NE(nullptr, svc); + + if (seclabel) { + ASSERT_STREQ("u:r:su:s0", svc->seclabel); + } else { + ASSERT_EQ(nullptr, svc->seclabel); + } + if (uid) { + ASSERT_EQ(decode_uid("log"), svc->uid); + } else { + ASSERT_EQ(0U, svc->uid); + } + if (gid) { + ASSERT_EQ(decode_uid("shell"), svc->gid); + } else { + ASSERT_EQ(0U, svc->gid); + } + if (supplementary_gids) { + ASSERT_EQ(2U, svc->nr_supp_gids); + ASSERT_EQ(decode_uid("system"), svc->supp_gids[0]); + ASSERT_EQ(decode_uid("adb"), svc->supp_gids[1]); + } else { + ASSERT_EQ(0U, svc->nr_supp_gids); + } + + ASSERT_EQ(2, svc->nargs); + ASSERT_EQ("/system/bin/toybox", svc->args[0]); + ASSERT_EQ("id", svc->args[1]); + ASSERT_EQ(nullptr, svc->args[2]); +} + +TEST(init_parser, make_exec_oneshot_service_with_everything) { + Test_make_exec_oneshot_service(true, true, true, true, true); +} + +TEST(init_parser, make_exec_oneshot_service_with_seclabel_uid_gid) { + Test_make_exec_oneshot_service(true, true, true, true, false); +} + +TEST(init_parser, make_exec_oneshot_service_with_seclabel_uid) { + Test_make_exec_oneshot_service(true, true, true, false, false); +} + +TEST(init_parser, make_exec_oneshot_service_with_seclabel) { + Test_make_exec_oneshot_service(true, true, false, false, false); +} + +TEST(init_parser, make_exec_oneshot_service_with_just_command) { + Test_make_exec_oneshot_service(true, false, false, false, false); +} + +TEST(init_parser, make_exec_oneshot_service_with_just_command_no_dash) { + Test_make_exec_oneshot_service(false, false, false, false, false); +} diff --git a/init/readme.txt b/init/readme.txt index 9c2422019..8161858bf 100644 --- a/init/readme.txt +++ b/init/readme.txt @@ -70,11 +70,11 @@ disabled setenv Set the environment variable to in the launched process. -socket [ [ [ ] ] ] +socket [ [ [ ] ] ] Create a unix domain socket named /dev/socket/ and pass its fd to the launched process. must be "dgram", "stream" or "seqpacket". User and group default to 0. - Context is the SELinux security context for the socket. + 'seclabel' is the SELinux security context for the socket. It defaults to the service security context, as specified by seclabel or computed based on the service executable file security context. @@ -91,8 +91,8 @@ group [ ]* supplemental groups of the process (via setgroups()). Currently defaults to root. (??? probably should default to nobody) -seclabel - Change to securitycontext before exec'ing this service. +seclabel + Change to 'seclabel' before exec'ing this service. Primarily for use by services run from the rootfs, e.g. ueventd, adbd. Services on the system partition can instead use policy-defined transitions based on their file security context. @@ -137,14 +137,17 @@ boot Commands -------- -exec [ ]* - This command is not implemented. +exec [ [ [ ]* ] ] -- [ ]* + Fork and execute command with the given arguments. The command starts + after "--" so that an optional security context, user, and supplementary + groups can be provided. No other commands will be run until this one + finishes. execonce [ ]* Fork and execute a program (). This will block until the program completes execution. This command can be run at most once during init's lifetime. Subsequent invocations are ignored. - It is best to avoid exec as unlike the builtin commands, it runs + It is best to avoid execonce as unlike the builtin commands, it runs the risk of getting init "stuck". export @@ -220,7 +223,7 @@ restorecon_recursive [ ]* Recursively restore the directory tree named by to the security contexts specified in the file_contexts configuration. -setcon +setcon 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. @@ -275,7 +278,7 @@ Properties Init updates some system properties to provide some insight into what it's doing: -init.action +init.action Equal to the name of the action currently being executed or "" if none init.command diff --git a/init/signal_handler.cpp b/init/signal_handler.cpp index c0898fb20..c428b9632 100644 --- a/init/signal_handler.cpp +++ b/init/signal_handler.cpp @@ -14,11 +14,11 @@ * limitations under the License. */ -#include #include -#include -#include #include +#include +#include +#include #include #include #include @@ -27,34 +27,28 @@ #include #include "init.h" -#include "util.h" #include "log.h" +#include "util.h" static int signal_fd = -1; static int signal_recv_fd = -1; -static void sigchld_handler(int s) -{ +static void sigchld_handler(int s) { write(signal_fd, &s, 1); } #define CRITICAL_CRASH_THRESHOLD 4 /* if we crash >4 times ... */ -#define CRITICAL_CRASH_WINDOW (4*60) /* ... in 4 minutes, goto recovery*/ +#define CRITICAL_CRASH_WINDOW (4*60) /* ... in 4 minutes, goto recovery */ -static int wait_for_one_process(int block) -{ +static int wait_for_one_process() { int status; - struct service *svc; - struct socketinfo *si; - time_t now; - struct listnode *node; - struct command *cmd; - - pid_t pid = TEMP_FAILURE_RETRY(waitpid(-1, &status, block ? 0 : WNOHANG)); - if (pid <= 0) return -1; + pid_t pid = TEMP_FAILURE_RETRY(waitpid(-1, &status, WNOHANG)); + if (pid <= 0) { + return -1; + } INFO("waitpid returned pid %d, status = %08x\n", pid, status); - svc = service_find_by_pid(pid); + service* svc = service_find_by_pid(pid); if (!svc) { if (WIFEXITED(status)) { ERROR("untracked pid %d exited with status %d\n", pid, WEXITSTATUS(status)); @@ -68,36 +62,47 @@ static int wait_for_one_process(int block) return 0; } + // TODO: all the code from here down should be a member function on service. + NOTICE("process '%s', pid %d exited\n", svc->name, pid); if (!(svc->flags & SVC_ONESHOT) || (svc->flags & SVC_RESTART)) { - kill(-pid, SIGKILL); NOTICE("process '%s' killing any children in process group\n", svc->name); + kill(-pid, SIGKILL); } - /* remove any sockets we may have created */ - for (si = svc->sockets; si; si = si->next) { + // Remove any sockets we may have created. + for (socketinfo* si = svc->sockets; si; si = si->next) { char tmp[128]; snprintf(tmp, sizeof(tmp), ANDROID_SOCKET_DIR"/%s", si->name); unlink(tmp); } + if (svc->flags & SVC_EXEC) { + INFO("SVC_EXEC pid %d finished...\n", svc->pid); + waiting_for_exec = false; + list_remove(&svc->slist); + free(svc->name); + free(svc); + return 0; + } + svc->pid = 0; svc->flags &= (~SVC_RUNNING); - /* oneshot processes go into the disabled state on exit, - * except when manually restarted. */ + // Oneshot processes go into the disabled state on exit, + // except when manually restarted. if ((svc->flags & SVC_ONESHOT) && !(svc->flags & SVC_RESTART)) { svc->flags |= SVC_DISABLED; } - /* disabled and reset processes do not get restarted automatically */ - if (svc->flags & (SVC_DISABLED | SVC_RESET) ) { - notify_service_state(svc->name, "stopped"); + // Disabled and reset processes do not get restarted automatically. + if (svc->flags & (SVC_DISABLED | SVC_RESET)) { + svc->NotifyStateChange("stopped"); return 0; } - now = gettime(); + time_t now = gettime(); if ((svc->flags & SVC_CRITICAL) && !(svc->flags & SVC_RESTART)) { if (svc->time_crashed + CRITICAL_CRASH_WINDOW >= now) { if (++svc->nr_crashed > CRITICAL_CRASH_THRESHOLD) { @@ -116,36 +121,33 @@ static int wait_for_one_process(int block) svc->flags &= (~SVC_RESTART); svc->flags |= SVC_RESTARTING; - /* Execute all onrestart commands for this service. */ + // Execute all onrestart commands for this service. + struct listnode* node; list_for_each(node, &svc->onrestart.commands) { - cmd = node_to_item(node, struct command, clist); + command* cmd = node_to_item(node, struct command, clist); cmd->func(cmd->nargs, cmd->args); } - notify_service_state(svc->name, "restarting"); + svc->NotifyStateChange("restarting"); return 0; } -void handle_signal(void) -{ +void handle_signal() { + // We got a SIGCHLD - reap and restart as needed. char tmp[32]; - - /* we got a SIGCHLD - reap and restart as needed */ read(signal_recv_fd, tmp, sizeof(tmp)); - while (!wait_for_one_process(0)) - ; + while (!wait_for_one_process()) { + } } -void signal_init(void) -{ - int s[2]; - +void signal_init() { struct sigaction act; memset(&act, 0, sizeof(act)); act.sa_handler = sigchld_handler; act.sa_flags = SA_NOCLDSTOP; sigaction(SIGCHLD, &act, 0); - /* create a signalling mechanism for the sigchld handler */ + // 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]; @@ -154,7 +156,6 @@ void signal_init(void) handle_signal(); } -int get_signal_fd() -{ +int get_signal_fd() { return signal_recv_fd; } diff --git a/init/util_test.cpp b/init/util_test.cpp index e9a1581d5..5b3ab50b2 100644 --- a/init/util_test.cpp +++ b/init/util_test.cpp @@ -35,3 +35,9 @@ TEST(util, read_file_success) { s[5] = 0; EXPECT_STREQ("Linux", s.c_str()); } + +TEST(util, decode_uid) { + EXPECT_EQ(0U, decode_uid("root")); + EXPECT_EQ(-1U, decode_uid("toot")); + EXPECT_EQ(123U, decode_uid("123")); +}