resolved conflicts for merge of 5ebc2f5b
to master
Change-Id: I9fdb437051e2f1c9afef13101ae40f881c4c6f19
This commit is contained in:
commit
47145db46e
|
@ -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 += \
|
||||
|
|
|
@ -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;
|
||||
|
|
188
init/init.cpp
188
init/init.cpp
|
@ -14,40 +14,40 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <ctype.h>
|
||||
#include <dirent.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <libgen.h>
|
||||
#include <signal.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <ctype.h>
|
||||
#include <signal.h>
|
||||
#include <sys/wait.h>
|
||||
#include <sys/mount.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/poll.h>
|
||||
#include <errno.h>
|
||||
#include <stdarg.h>
|
||||
#include <mtd/mtd-user.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/un.h>
|
||||
#include <dirent.h>
|
||||
#include <sys/wait.h>
|
||||
#include <termios.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include <mtd/mtd-user.h>
|
||||
|
||||
#include <selinux/selinux.h>
|
||||
#include <selinux/label.h>
|
||||
#include <selinux/android.h>
|
||||
|
||||
#include <libgen.h>
|
||||
|
||||
#include <cutils/list.h>
|
||||
#include <cutils/android_reboot.h>
|
||||
#include <cutils/sockets.h>
|
||||
#include <cutils/iosched_policy.h>
|
||||
#include <cutils/fs.h>
|
||||
#include <cutils/iosched_policy.h>
|
||||
#include <cutils/list.h>
|
||||
#include <cutils/sockets.h>
|
||||
#include <private/android_filesystem_config.h>
|
||||
#include <termios.h>
|
||||
#include <utils/file.h>
|
||||
#include <utils/stringprintf.h>
|
||||
|
||||
|
@ -75,22 +75,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)
|
||||
{
|
||||
|
@ -163,35 +176,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;
|
||||
|
@ -205,6 +209,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);
|
||||
|
@ -216,7 +221,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;
|
||||
|
@ -244,8 +249,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;
|
||||
|
@ -301,7 +305,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));
|
||||
|
@ -364,8 +368,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 */
|
||||
|
@ -391,9 +400,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");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -993,28 +1002,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);
|
||||
|
@ -1026,15 +1025,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();
|
||||
|
@ -1050,25 +1047,22 @@ int main(int argc, char **argv)
|
|||
*/
|
||||
export_kernel_boot_props();
|
||||
|
||||
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();
|
||||
|
||||
|
@ -1081,50 +1075,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;
|
||||
|
|
53
init/init.h
53
init/init.h
|
@ -17,13 +17,11 @@
|
|||
#ifndef _INIT_INIT_H
|
||||
#define _INIT_INIT_H
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <cutils/list.h>
|
||||
#include <cutils/iosched_policy.h>
|
||||
|
||||
#include <sys/stat.h>
|
||||
|
||||
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 */
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
|
@ -440,7 +441,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;
|
||||
|
@ -663,6 +664,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) {
|
||||
|
@ -686,7 +746,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));
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 <errno.h>
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
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<char*>("exec");
|
||||
ASSERT_EQ(nullptr, make_exec_oneshot_service(1, argv));
|
||||
|
||||
// No command in "exec --".
|
||||
argv[1] = const_cast<char*>("--");
|
||||
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<char*>("exec");
|
||||
argv[argc++] = const_cast<char*>("seclabel");
|
||||
argv[argc++] = const_cast<char*>("root"); // uid.
|
||||
argv[argc++] = const_cast<char*>("root"); // gid.
|
||||
for (int i = 0; i < NR_SVC_SUPP_GIDS; ++i) {
|
||||
argv[argc++] = const_cast<char*>("root"); // Supplementary gid.
|
||||
}
|
||||
argv[argc++] = const_cast<char*>("--");
|
||||
argv[argc++] = const_cast<char*>("/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<char*>("exec");
|
||||
if (seclabel) {
|
||||
argv[argc++] = const_cast<char*>("u:r:su:s0"); // seclabel
|
||||
if (uid) {
|
||||
argv[argc++] = const_cast<char*>("log"); // uid
|
||||
if (gid) {
|
||||
argv[argc++] = const_cast<char*>("shell"); // gid
|
||||
if (supplementary_gids) {
|
||||
argv[argc++] = const_cast<char*>("system"); // supplementary gid 0
|
||||
argv[argc++] = const_cast<char*>("adb"); // supplementary gid 1
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (dash_dash) {
|
||||
argv[argc++] = const_cast<char*>("--");
|
||||
}
|
||||
argv[argc++] = const_cast<char*>("/system/bin/toybox");
|
||||
argv[argc++] = const_cast<char*>("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);
|
||||
}
|
|
@ -70,11 +70,11 @@ disabled
|
|||
setenv <name> <value>
|
||||
Set the environment variable <name> to <value> in the launched process.
|
||||
|
||||
socket <name> <type> <perm> [ <user> [ <group> [ <context> ] ] ]
|
||||
socket <name> <type> <perm> [ <user> [ <group> [ <seclabel> ] ] ]
|
||||
Create a unix domain socket named /dev/socket/<name> and pass
|
||||
its fd to the launched process. <type> 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 <groupname> [ <groupname> ]*
|
|||
supplemental groups of the process (via setgroups()).
|
||||
Currently defaults to root. (??? probably should default to nobody)
|
||||
|
||||
seclabel <securitycontext>
|
||||
Change to securitycontext before exec'ing this service.
|
||||
seclabel <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 <path> [ <argument> ]*
|
||||
This command is not implemented.
|
||||
exec [ <seclabel> [ <user> [ <group> ]* ] ] -- <command> [ <argument> ]*
|
||||
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 <path> [ <argument> ]*
|
||||
Fork and execute a program (<path>). 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 <name> <value>
|
||||
|
@ -220,7 +223,7 @@ restorecon_recursive <path> [ <path> ]*
|
|||
Recursively restore the directory tree named by <path> to the
|
||||
security contexts specified in the file_contexts configuration.
|
||||
|
||||
setcon <securitycontext>
|
||||
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.
|
||||
|
@ -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
|
||||
|
|
|
@ -14,11 +14,11 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <errno.h>
|
||||
#include <signal.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <signal.h>
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/wait.h>
|
||||
|
@ -27,34 +27,28 @@
|
|||
#include <cutils/list.h>
|
||||
|
||||
#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;
|
||||
}
|
||||
|
|
|
@ -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"));
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue