resolved conflicts for merge of 5ebc2f5b to master

Change-Id: I9fdb437051e2f1c9afef13101ae40f881c4c6f19
This commit is contained in:
Dan Albert 2015-03-12 16:57:31 -07:00
commit 47145db46e
10 changed files with 393 additions and 175 deletions

View File

@ -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 += \

View File

@ -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;

View File

@ -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;

View File

@ -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 */

View File

@ -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));

View File

@ -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

134
init/init_parser_test.cpp Normal file
View File

@ -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);
}

View File

@ -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

View File

@ -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;
}

View File

@ -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"));
}