2008-10-21 16:00:00 +02:00
|
|
|
/*
|
|
|
|
* Copyright (C) 2008 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 <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/un.h>
|
2012-01-13 14:48:47 +01:00
|
|
|
|
|
|
|
#include <selinux/selinux.h>
|
|
|
|
#include <selinux/label.h>
|
2012-05-01 21:02:53 +02:00
|
|
|
#include <selinux/android.h>
|
2012-01-13 14:48:47 +01:00
|
|
|
|
2010-04-21 21:04:20 +02:00
|
|
|
#include <libgen.h>
|
2008-10-21 16:00:00 +02:00
|
|
|
|
2011-09-02 02:09:44 +02:00
|
|
|
#include <cutils/list.h>
|
2013-06-25 02:41:40 +02:00
|
|
|
#include <cutils/android_reboot.h>
|
2008-10-21 16:00:00 +02:00
|
|
|
#include <cutils/sockets.h>
|
2010-02-25 23:19:50 +01:00
|
|
|
#include <cutils/iosched_policy.h>
|
2013-08-16 22:19:24 +02:00
|
|
|
#include <cutils/fs.h>
|
2010-04-21 21:04:20 +02:00
|
|
|
#include <private/android_filesystem_config.h>
|
2008-10-21 16:00:00 +02:00
|
|
|
#include <termios.h>
|
|
|
|
|
|
|
|
#include "devices.h"
|
|
|
|
#include "init.h"
|
2010-04-20 02:05:34 +02:00
|
|
|
#include "log.h"
|
2008-10-21 16:00:00 +02:00
|
|
|
#include "property_service.h"
|
2008-12-18 03:08:08 +01:00
|
|
|
#include "bootchart.h"
|
2010-04-14 04:48:59 +02:00
|
|
|
#include "signal_handler.h"
|
2010-04-14 04:20:44 +02:00
|
|
|
#include "keychords.h"
|
2010-04-20 23:29:05 +02:00
|
|
|
#include "init_parser.h"
|
2010-04-14 05:35:46 +02:00
|
|
|
#include "util.h"
|
2010-04-21 21:04:20 +02:00
|
|
|
#include "ueventd.h"
|
2012-06-14 06:51:56 +02:00
|
|
|
#include "watchdogd.h"
|
2008-10-21 16:00:00 +02:00
|
|
|
|
2012-01-13 14:48:47 +01:00
|
|
|
struct selabel_handle *sehandle;
|
2012-08-09 16:05:49 +02:00
|
|
|
struct selabel_handle *sehandle_prop;
|
2012-01-13 14:48:47 +01:00
|
|
|
|
2008-10-21 16:00:00 +02:00
|
|
|
static int property_triggers_enabled = 0;
|
|
|
|
|
|
|
|
#if BOOTCHART
|
|
|
|
static int bootchart_count;
|
2014-07-24 19:11:35 +02:00
|
|
|
static long long bootchart_time = 0;
|
2008-10-21 16:00:00 +02:00
|
|
|
#endif
|
|
|
|
|
|
|
|
static char console[32];
|
|
|
|
static char bootmode[32];
|
|
|
|
static char hardware[32];
|
|
|
|
static unsigned revision = 0;
|
|
|
|
static char qemu[32];
|
|
|
|
|
2010-04-14 04:52:01 +02:00
|
|
|
static struct action *cur_action = NULL;
|
|
|
|
static struct command *cur_command = NULL;
|
|
|
|
|
2010-04-14 04:48:59 +02:00
|
|
|
void notify_service_state(const char *name, const char *state)
|
2008-10-21 16:00:00 +02:00
|
|
|
{
|
|
|
|
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;
|
2013-04-01 05:17:51 +02:00
|
|
|
static char console_name[PROP_VALUE_MAX] = "/dev/console";
|
2008-10-21 16:00:00 +02:00
|
|
|
static time_t process_needs_restart;
|
|
|
|
|
|
|
|
static const char *ENV[32];
|
|
|
|
|
|
|
|
/* add_environment - add "key=value" to the current environment */
|
|
|
|
int add_environment(const char *key, const char *val)
|
|
|
|
{
|
2014-05-16 12:36:36 +02:00
|
|
|
size_t n;
|
|
|
|
size_t key_len = strlen(key);
|
|
|
|
|
|
|
|
/* The last environment entry is reserved to terminate the list */
|
|
|
|
for (n = 0; n < (ARRAY_SIZE(ENV) - 1); n++) {
|
|
|
|
|
|
|
|
/* Delete any existing entry for this key */
|
|
|
|
if (ENV[n] != NULL) {
|
|
|
|
size_t entry_key_len = strcspn(ENV[n], "=");
|
|
|
|
if ((entry_key_len == key_len) && (strncmp(ENV[n], key, entry_key_len) == 0)) {
|
|
|
|
free((char*)ENV[n]);
|
|
|
|
ENV[n] = NULL;
|
|
|
|
}
|
|
|
|
}
|
2011-09-28 18:55:31 +02:00
|
|
|
|
2014-05-16 12:36:36 +02:00
|
|
|
/* Add entry if a free slot is available */
|
|
|
|
if (ENV[n] == NULL) {
|
|
|
|
size_t len = key_len + strlen(val) + 2;
|
2008-10-21 16:00:00 +02:00
|
|
|
char *entry = malloc(len);
|
|
|
|
snprintf(entry, len, "%s=%s", key, val);
|
|
|
|
ENV[n] = entry;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-05-16 12:36:36 +02:00
|
|
|
ERROR("No env. room to store: '%s':'%s'\n", key, val);
|
|
|
|
|
|
|
|
return -1;
|
2008-10-21 16:00:00 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
static void zap_stdio(void)
|
|
|
|
{
|
|
|
|
int fd;
|
|
|
|
fd = open("/dev/null", O_RDWR);
|
|
|
|
dup2(fd, 0);
|
|
|
|
dup2(fd, 1);
|
|
|
|
dup2(fd, 2);
|
|
|
|
close(fd);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void open_console()
|
|
|
|
{
|
|
|
|
int fd;
|
|
|
|
if ((fd = open(console_name, O_RDWR)) < 0) {
|
|
|
|
fd = open("/dev/null", O_RDWR);
|
|
|
|
}
|
2012-03-18 23:38:19 +01:00
|
|
|
ioctl(fd, TIOCSCTTY, 0);
|
2008-10-21 16:00:00 +02:00
|
|
|
dup2(fd, 0);
|
|
|
|
dup2(fd, 1);
|
|
|
|
dup2(fd, 2);
|
|
|
|
close(fd);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void publish_socket(const char *name, int fd)
|
|
|
|
{
|
|
|
|
char key[64] = ANDROID_SOCKET_ENV_PREFIX;
|
|
|
|
char val[64];
|
|
|
|
|
|
|
|
strlcpy(key + sizeof(ANDROID_SOCKET_ENV_PREFIX) - 1,
|
|
|
|
name,
|
|
|
|
sizeof(key) - sizeof(ANDROID_SOCKET_ENV_PREFIX));
|
|
|
|
snprintf(val, sizeof(val), "%d", fd);
|
|
|
|
add_environment(key, val);
|
|
|
|
|
|
|
|
/* make sure we don't close-on-exec */
|
|
|
|
fcntl(fd, F_SETFD, 0);
|
|
|
|
}
|
|
|
|
|
2009-05-19 22:30:46 +02:00
|
|
|
void service_start(struct service *svc, const char *dynamic_args)
|
2008-10-21 16:00:00 +02:00
|
|
|
{
|
|
|
|
struct stat s;
|
|
|
|
pid_t pid;
|
|
|
|
int needs_console;
|
2012-01-13 14:48:47 +01:00
|
|
|
char *scon = NULL;
|
|
|
|
int rc;
|
2012-10-17 08:07:05 +02:00
|
|
|
|
2010-12-04 01:33:31 +01:00
|
|
|
/* starting a service removes it from the disabled or reset
|
2008-10-21 16:00:00 +02:00
|
|
|
* state and immediately takes it out of the restarting
|
|
|
|
* state if it was in there
|
|
|
|
*/
|
2014-05-03 06:14:29 +02:00
|
|
|
svc->flags &= (~(SVC_DISABLED|SVC_RESTARTING|SVC_RESET|SVC_RESTART|SVC_DISABLED_START));
|
2008-10-21 16:00:00 +02:00
|
|
|
svc->time_started = 0;
|
2011-09-28 18:55:31 +02:00
|
|
|
|
2008-10-21 16:00:00 +02:00
|
|
|
/* 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)) {
|
|
|
|
ERROR("service '%s' requires console\n", svc->name);
|
|
|
|
svc->flags |= SVC_DISABLED;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (stat(svc->args[0], &s) != 0) {
|
|
|
|
ERROR("cannot find '%s', disabling '%s'\n", svc->args[0], svc->name);
|
|
|
|
svc->flags |= SVC_DISABLED;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2009-05-19 22:30:46 +02:00
|
|
|
if ((!(svc->flags & SVC_ONESHOT)) && dynamic_args) {
|
2009-05-20 18:52:16 +02:00
|
|
|
ERROR("service '%s' must be one-shot to use dynamic args, disabling\n",
|
|
|
|
svc->args[0]);
|
2009-05-19 22:30:46 +02:00
|
|
|
svc->flags |= SVC_DISABLED;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2012-01-13 14:48:47 +01:00
|
|
|
if (is_selinux_enabled() > 0) {
|
2012-11-16 20:34:27 +01:00
|
|
|
if (svc->seclabel) {
|
|
|
|
scon = strdup(svc->seclabel);
|
|
|
|
if (!scon) {
|
|
|
|
ERROR("Out of memory while starting '%s'\n", svc->name);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
char *mycon = NULL, *fcon = NULL;
|
|
|
|
|
|
|
|
INFO("computing context for service '%s'\n", svc->args[0]);
|
|
|
|
rc = getcon(&mycon);
|
|
|
|
if (rc < 0) {
|
|
|
|
ERROR("could not get context while starting '%s'\n", svc->name);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
rc = getfilecon(svc->args[0], &fcon);
|
|
|
|
if (rc < 0) {
|
|
|
|
ERROR("could not get context while starting '%s'\n", svc->name);
|
|
|
|
freecon(mycon);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
rc = security_compute_create(mycon, fcon, string_to_security_class("process"), &scon);
|
2013-12-09 21:40:24 +01:00
|
|
|
if (rc == 0 && !strcmp(scon, mycon)) {
|
|
|
|
ERROR("Warning! Service %s needs a SELinux domain defined; please fix!\n", svc->name);
|
|
|
|
}
|
2012-01-13 14:48:47 +01:00
|
|
|
freecon(mycon);
|
2012-11-16 20:34:27 +01:00
|
|
|
freecon(fcon);
|
|
|
|
if (rc < 0) {
|
|
|
|
ERROR("could not get context while starting '%s'\n", svc->name);
|
|
|
|
return;
|
|
|
|
}
|
2012-01-13 14:48:47 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-10-21 16:00:00 +02:00
|
|
|
NOTICE("starting '%s'\n", svc->name);
|
|
|
|
|
|
|
|
pid = fork();
|
|
|
|
|
|
|
|
if (pid == 0) {
|
|
|
|
struct socketinfo *si;
|
|
|
|
struct svcenvinfo *ei;
|
|
|
|
char tmp[32];
|
|
|
|
int fd, sz;
|
|
|
|
|
2012-03-26 18:09:11 +02:00
|
|
|
umask(077);
|
2010-04-20 02:11:33 +02:00
|
|
|
if (properties_inited()) {
|
|
|
|
get_property_workspace(&fd, &sz);
|
|
|
|
sprintf(tmp, "%d,%d", dup(fd), sz);
|
|
|
|
add_environment("ANDROID_PROPERTY_WORKSPACE", tmp);
|
|
|
|
}
|
2008-10-21 16:00:00 +02:00
|
|
|
|
|
|
|
for (ei = svc->envvars; ei; ei = ei->next)
|
|
|
|
add_environment(ei->name, ei->value);
|
|
|
|
|
|
|
|
for (si = svc->sockets; si; si = si->next) {
|
2010-10-01 14:20:36 +02:00
|
|
|
int socket_type = (
|
|
|
|
!strcmp(si->type, "stream") ? SOCK_STREAM :
|
|
|
|
(!strcmp(si->type, "dgram") ? SOCK_DGRAM : SOCK_SEQPACKET));
|
|
|
|
int s = create_socket(si->name, socket_type,
|
2013-05-13 18:37:04 +02:00
|
|
|
si->perm, si->uid, si->gid, si->socketcon ?: scon);
|
2008-10-21 16:00:00 +02:00
|
|
|
if (s >= 0) {
|
|
|
|
publish_socket(si->name, s);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-01-13 14:48:47 +01:00
|
|
|
freecon(scon);
|
|
|
|
scon = NULL;
|
|
|
|
|
2010-02-25 23:19:50 +01:00
|
|
|
if (svc->ioprio_class != IoSchedClass_NONE) {
|
|
|
|
if (android_set_ioprio(getpid(), svc->ioprio_class, svc->ioprio_pri)) {
|
|
|
|
ERROR("Failed to set pid %d ioprio = %d,%d: %s\n",
|
|
|
|
getpid(), svc->ioprio_class, svc->ioprio_pri, strerror(errno));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-10-21 16:00:00 +02:00
|
|
|
if (needs_console) {
|
|
|
|
setsid();
|
|
|
|
open_console();
|
|
|
|
} else {
|
|
|
|
zap_stdio();
|
|
|
|
}
|
|
|
|
|
|
|
|
#if 0
|
|
|
|
for (n = 0; svc->args[n]; n++) {
|
|
|
|
INFO("args[%d] = '%s'\n", n, svc->args[n]);
|
|
|
|
}
|
|
|
|
for (n = 0; ENV[n]; n++) {
|
|
|
|
INFO("env[%d] = '%s'\n", n, ENV[n]);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
setpgid(0, getpid());
|
|
|
|
|
2009-01-10 02:51:25 +01:00
|
|
|
/* as requested, set our gid, supplemental gids, and uid */
|
2008-10-21 16:00:00 +02:00
|
|
|
if (svc->gid) {
|
2010-11-18 01:55:42 +01:00
|
|
|
if (setgid(svc->gid) != 0) {
|
|
|
|
ERROR("setgid failed: %s\n", strerror(errno));
|
|
|
|
_exit(127);
|
|
|
|
}
|
2008-10-21 16:00:00 +02:00
|
|
|
}
|
|
|
|
if (svc->nr_supp_gids) {
|
2010-11-18 01:55:42 +01:00
|
|
|
if (setgroups(svc->nr_supp_gids, svc->supp_gids) != 0) {
|
|
|
|
ERROR("setgroups failed: %s\n", strerror(errno));
|
|
|
|
_exit(127);
|
|
|
|
}
|
2008-10-21 16:00:00 +02:00
|
|
|
}
|
|
|
|
if (svc->uid) {
|
2010-11-18 01:55:42 +01:00
|
|
|
if (setuid(svc->uid) != 0) {
|
|
|
|
ERROR("setuid failed: %s\n", strerror(errno));
|
|
|
|
_exit(127);
|
|
|
|
}
|
2008-10-21 16:00:00 +02:00
|
|
|
}
|
2012-01-13 14:48:47 +01:00
|
|
|
if (svc->seclabel) {
|
|
|
|
if (is_selinux_enabled() > 0 && setexeccon(svc->seclabel) < 0) {
|
|
|
|
ERROR("cannot setexeccon('%s'): %s\n", svc->seclabel, strerror(errno));
|
|
|
|
_exit(127);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-05-20 17:50:40 +02:00
|
|
|
if (!dynamic_args) {
|
|
|
|
if (execve(svc->args[0], (char**) svc->args, (char**) ENV) < 0) {
|
|
|
|
ERROR("cannot execve('%s'): %s\n", svc->args[0], strerror(errno));
|
|
|
|
}
|
|
|
|
} else {
|
2010-04-20 23:29:05 +02:00
|
|
|
char *arg_ptrs[INIT_PARSER_MAXARGS+1];
|
2009-05-20 18:52:16 +02:00
|
|
|
int arg_idx = svc->nargs;
|
2009-05-19 22:30:46 +02:00
|
|
|
char *tmp = strdup(dynamic_args);
|
2009-05-20 18:52:16 +02:00
|
|
|
char *next = tmp;
|
|
|
|
char *bword;
|
2009-05-19 22:30:46 +02:00
|
|
|
|
|
|
|
/* Copy the static arguments */
|
2009-05-20 18:52:16 +02:00
|
|
|
memcpy(arg_ptrs, svc->args, (svc->nargs * sizeof(char *)));
|
2009-05-19 22:30:46 +02:00
|
|
|
|
2009-05-20 18:52:16 +02:00
|
|
|
while((bword = strsep(&next, " "))) {
|
|
|
|
arg_ptrs[arg_idx++] = bword;
|
2010-04-20 23:29:05 +02:00
|
|
|
if (arg_idx == INIT_PARSER_MAXARGS)
|
2009-05-19 22:30:46 +02:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
arg_ptrs[arg_idx] = '\0';
|
|
|
|
execve(svc->args[0], (char**) arg_ptrs, (char**) ENV);
|
2008-11-23 22:26:39 +01:00
|
|
|
}
|
2008-10-21 16:00:00 +02:00
|
|
|
_exit(127);
|
|
|
|
}
|
|
|
|
|
2012-01-13 14:48:47 +01:00
|
|
|
freecon(scon);
|
|
|
|
|
2008-10-21 16:00:00 +02:00
|
|
|
if (pid < 0) {
|
|
|
|
ERROR("failed to start '%s'\n", svc->name);
|
|
|
|
svc->pid = 0;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
svc->time_started = gettime();
|
|
|
|
svc->pid = pid;
|
|
|
|
svc->flags |= SVC_RUNNING;
|
|
|
|
|
2010-04-20 02:11:33 +02:00
|
|
|
if (properties_inited())
|
|
|
|
notify_service_state(svc->name, "running");
|
2008-10-21 16:00:00 +02:00
|
|
|
}
|
|
|
|
|
2012-01-26 05:48:46 +01:00
|
|
|
/* The how field should be either SVC_DISABLED, SVC_RESET, or SVC_RESTART */
|
2010-12-04 01:33:31 +01:00
|
|
|
static void service_stop_or_reset(struct service *svc, int how)
|
2008-10-21 16:00:00 +02:00
|
|
|
{
|
init: Safely restart services to avoid race conditions.
Previously, service restarts (either an explicit "restart", or a "stop,
start" pair) exhibited a race condition whereby the new (restarting)
service process was often spawned before the old (stopping) process had
terminated. This may have resulted in the new service process failing to
acquire a limited resource (file lock, socket bind, etc.) that the old
process had not yet released.
Now, a stopping service remains in the SVC_RUNNING state until its exiting
process has been reaped by waitpid. This prevents a "stop, start" sequence
from spawning a second service process before resources held by the first
are released. This enables safe service restarts by stopping the service,
waiting for the old service process to terminate, and (only then) starting
the new service process. In the event of "restarting" an already stopped
service, the previous behavior is maintained whereby the service is simply
started.
This scenario could be special-cased by the restart command, however, we
have observed instances where services are, unintentionally, stopped and
started "too quickly," and so simultaneous processes for the same service
should never be allowed.
Note that this commit alters the behaviors for explicit restarts of
critical and oneshot services. Previously these serivces would simply be
restarted, whereas now, an explicit restart of a critical service counts as
a crash (which may result in a recovery reboot) and oneshot services go
into the disabled state.
2012-02-06 16:32:13 +01:00
|
|
|
/* The service is still SVC_RUNNING until its process exits, but if it has
|
|
|
|
* already exited it shoudn't attempt a restart yet. */
|
2014-05-03 06:14:29 +02:00
|
|
|
svc->flags &= ~(SVC_RESTARTING | SVC_DISABLED_START);
|
2008-10-21 16:00:00 +02:00
|
|
|
|
2012-01-26 05:48:46 +01:00
|
|
|
if ((how != SVC_DISABLED) && (how != SVC_RESET) && (how != SVC_RESTART)) {
|
2010-12-04 01:33:31 +01:00
|
|
|
/* Hrm, an illegal flag. Default to SVC_DISABLED */
|
|
|
|
how = SVC_DISABLED;
|
|
|
|
}
|
2008-10-21 16:00:00 +02:00
|
|
|
/* if the service has not yet started, prevent
|
|
|
|
* it from auto-starting with its class
|
|
|
|
*/
|
2011-10-27 01:56:00 +02:00
|
|
|
if (how == SVC_RESET) {
|
|
|
|
svc->flags |= (svc->flags & SVC_RC_DISABLED) ? SVC_DISABLED : SVC_RESET;
|
|
|
|
} else {
|
|
|
|
svc->flags |= how;
|
|
|
|
}
|
2008-10-21 16:00:00 +02:00
|
|
|
|
|
|
|
if (svc->pid) {
|
|
|
|
NOTICE("service '%s' is being killed\n", svc->name);
|
2010-12-04 01:33:31 +01:00
|
|
|
kill(-svc->pid, SIGKILL);
|
2008-10-21 16:00:00 +02:00
|
|
|
notify_service_state(svc->name, "stopping");
|
|
|
|
} else {
|
|
|
|
notify_service_state(svc->name, "stopped");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-12-04 01:33:31 +01:00
|
|
|
void service_reset(struct service *svc)
|
|
|
|
{
|
|
|
|
service_stop_or_reset(svc, SVC_RESET);
|
|
|
|
}
|
|
|
|
|
|
|
|
void service_stop(struct service *svc)
|
|
|
|
{
|
|
|
|
service_stop_or_reset(svc, SVC_DISABLED);
|
|
|
|
}
|
|
|
|
|
2012-01-26 05:48:46 +01:00
|
|
|
void service_restart(struct service *svc)
|
|
|
|
{
|
|
|
|
if (svc->flags & SVC_RUNNING) {
|
|
|
|
/* Stop, wait, then start the service. */
|
|
|
|
service_stop_or_reset(svc, SVC_RESTART);
|
|
|
|
} else if (!(svc->flags & SVC_RESTARTING)) {
|
|
|
|
/* Just start the service since it's not running. */
|
|
|
|
service_start(svc, NULL);
|
|
|
|
} /* else: Service is restarting anyways. */
|
|
|
|
}
|
|
|
|
|
2008-10-21 16:00:00 +02:00
|
|
|
void property_changed(const char *name, const char *value)
|
|
|
|
{
|
2010-04-14 04:52:01 +02:00
|
|
|
if (property_triggers_enabled)
|
2008-10-21 16:00:00 +02:00
|
|
|
queue_property_triggers(name, value);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void restart_service_if_needed(struct service *svc)
|
|
|
|
{
|
|
|
|
time_t next_start_time = svc->time_started + 5;
|
|
|
|
|
|
|
|
if (next_start_time <= gettime()) {
|
|
|
|
svc->flags &= (~SVC_RESTARTING);
|
2009-05-19 22:30:46 +02:00
|
|
|
service_start(svc, NULL);
|
2008-10-21 16:00:00 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((next_start_time < process_needs_restart) ||
|
|
|
|
(process_needs_restart == 0)) {
|
|
|
|
process_needs_restart = next_start_time;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void restart_processes()
|
|
|
|
{
|
|
|
|
process_needs_restart = 0;
|
|
|
|
service_for_each_flags(SVC_RESTARTING,
|
|
|
|
restart_service_if_needed);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void msg_start(const char *name)
|
|
|
|
{
|
2013-04-01 05:17:51 +02:00
|
|
|
struct service *svc = NULL;
|
2009-05-19 22:30:46 +02:00
|
|
|
char *tmp = NULL;
|
|
|
|
char *args = NULL;
|
|
|
|
|
|
|
|
if (!strchr(name, ':'))
|
|
|
|
svc = service_find_by_name(name);
|
|
|
|
else {
|
|
|
|
tmp = strdup(name);
|
2013-04-01 05:17:51 +02:00
|
|
|
if (tmp) {
|
|
|
|
args = strchr(tmp, ':');
|
|
|
|
*args = '\0';
|
|
|
|
args++;
|
2009-05-19 22:30:46 +02:00
|
|
|
|
2013-04-01 05:17:51 +02:00
|
|
|
svc = service_find_by_name(tmp);
|
|
|
|
}
|
2009-05-19 22:30:46 +02:00
|
|
|
}
|
2011-09-28 18:55:31 +02:00
|
|
|
|
2008-10-21 16:00:00 +02:00
|
|
|
if (svc) {
|
2009-05-19 22:30:46 +02:00
|
|
|
service_start(svc, args);
|
2008-10-21 16:00:00 +02:00
|
|
|
} else {
|
|
|
|
ERROR("no such service '%s'\n", name);
|
|
|
|
}
|
2009-05-19 22:30:46 +02:00
|
|
|
if (tmp)
|
|
|
|
free(tmp);
|
2008-10-21 16:00:00 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
static void msg_stop(const char *name)
|
|
|
|
{
|
|
|
|
struct service *svc = service_find_by_name(name);
|
|
|
|
|
|
|
|
if (svc) {
|
|
|
|
service_stop(svc);
|
|
|
|
} else {
|
2009-05-06 03:33:07 +02:00
|
|
|
ERROR("no such service '%s'\n", name);
|
2008-10-21 16:00:00 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-01-26 05:48:46 +01:00
|
|
|
static void msg_restart(const char *name)
|
|
|
|
{
|
|
|
|
struct service *svc = service_find_by_name(name);
|
|
|
|
|
|
|
|
if (svc) {
|
|
|
|
service_restart(svc);
|
|
|
|
} else {
|
|
|
|
ERROR("no such service '%s'\n", name);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-10-21 16:00:00 +02:00
|
|
|
void handle_control_message(const char *msg, const char *arg)
|
|
|
|
{
|
|
|
|
if (!strcmp(msg,"start")) {
|
|
|
|
msg_start(arg);
|
|
|
|
} else if (!strcmp(msg,"stop")) {
|
|
|
|
msg_stop(arg);
|
2010-10-03 22:30:11 +02:00
|
|
|
} else if (!strcmp(msg,"restart")) {
|
2012-01-26 05:48:46 +01:00
|
|
|
msg_restart(arg);
|
2008-10-21 16:00:00 +02:00
|
|
|
} else {
|
|
|
|
ERROR("unknown control msg '%s'\n", msg);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-04-14 04:52:01 +02:00
|
|
|
static struct command *get_first_command(struct action *act)
|
|
|
|
{
|
|
|
|
struct listnode *node;
|
|
|
|
node = list_head(&act->commands);
|
2011-08-26 22:59:18 +02:00
|
|
|
if (!node || list_empty(&act->commands))
|
2010-04-14 04:52:01 +02:00
|
|
|
return NULL;
|
|
|
|
|
|
|
|
return node_to_item(node, struct command, clist);
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct command *get_next_command(struct action *act, struct command *cmd)
|
2008-10-21 16:00:00 +02:00
|
|
|
{
|
|
|
|
struct listnode *node;
|
2010-04-14 04:52:01 +02:00
|
|
|
node = cmd->clist.next;
|
|
|
|
if (!node)
|
|
|
|
return NULL;
|
|
|
|
if (node == &act->commands)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
return node_to_item(node, struct command, clist);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int is_last_command(struct action *act, struct command *cmd)
|
|
|
|
{
|
|
|
|
return (list_tail(&act->commands) == &cmd->clist);
|
|
|
|
}
|
|
|
|
|
|
|
|
void execute_one_command(void)
|
|
|
|
{
|
2014-06-26 22:56:01 +02:00
|
|
|
int ret, i;
|
|
|
|
char cmd_str[256] = "";
|
2008-10-21 16:00:00 +02:00
|
|
|
|
2010-04-14 04:52:01 +02:00
|
|
|
if (!cur_action || !cur_command || is_last_command(cur_action, cur_command)) {
|
|
|
|
cur_action = action_remove_queue_head();
|
2010-04-22 20:52:23 +02:00
|
|
|
cur_command = NULL;
|
2010-04-14 04:52:01 +02:00
|
|
|
if (!cur_action)
|
|
|
|
return;
|
|
|
|
INFO("processing action %p (%s)\n", cur_action, cur_action->name);
|
|
|
|
cur_command = get_first_command(cur_action);
|
|
|
|
} else {
|
|
|
|
cur_command = get_next_command(cur_action, cur_command);
|
2008-10-21 16:00:00 +02:00
|
|
|
}
|
2010-04-14 04:52:01 +02:00
|
|
|
|
|
|
|
if (!cur_command)
|
|
|
|
return;
|
|
|
|
|
|
|
|
ret = cur_command->func(cur_command->nargs, cur_command->args);
|
2014-06-26 22:56:01 +02:00
|
|
|
if (klog_get_level() >= KLOG_INFO_LEVEL) {
|
|
|
|
for (i = 0; i < cur_command->nargs; i++) {
|
|
|
|
strlcat(cmd_str, cur_command->args[i], sizeof(cmd_str));
|
|
|
|
if (i < cur_command->nargs - 1) {
|
|
|
|
strlcat(cmd_str, " ", sizeof(cmd_str));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
INFO("command '%s' action=%s status=%d (%s:%d)\n",
|
|
|
|
cmd_str, cur_action ? cur_action->name : "", ret, cur_command->filename,
|
|
|
|
cur_command->line);
|
|
|
|
}
|
2008-10-21 16:00:00 +02:00
|
|
|
}
|
|
|
|
|
2010-04-21 21:04:20 +02:00
|
|
|
static int wait_for_coldboot_done_action(int nargs, char **args)
|
2010-04-14 04:52:01 +02:00
|
|
|
{
|
2010-04-21 21:04:20 +02:00
|
|
|
int ret;
|
2014-11-25 04:52:41 +01:00
|
|
|
INFO("wait for %s\n", COLDBOOT_DONE);
|
|
|
|
ret = wait_for_file(COLDBOOT_DONE, COMMAND_RETRY_TIMEOUT);
|
2010-04-21 21:04:20 +02:00
|
|
|
if (ret)
|
2014-11-25 04:52:41 +01:00
|
|
|
ERROR("Timed out waiting for %s\n", COLDBOOT_DONE);
|
2010-04-21 21:04:20 +02:00
|
|
|
return ret;
|
2010-04-14 04:52:01 +02:00
|
|
|
}
|
|
|
|
|
2013-08-16 22:19:24 +02:00
|
|
|
/*
|
|
|
|
* Writes 512 bytes of output from Hardware RNG (/dev/hw_random, backed
|
|
|
|
* by Linux kernel's hw_random framework) into Linux RNG's via /dev/urandom.
|
|
|
|
* Does nothing if Hardware RNG is not present.
|
|
|
|
*
|
|
|
|
* Since we don't yet trust the quality of Hardware RNG, these bytes are not
|
|
|
|
* mixed into the primary pool of Linux RNG and the entropy estimate is left
|
|
|
|
* unmodified.
|
|
|
|
*
|
|
|
|
* If the HW RNG device /dev/hw_random is present, we require that at least
|
|
|
|
* 512 bytes read from it are written into Linux RNG. QA is expected to catch
|
|
|
|
* devices/configurations where these I/O operations are blocking for a long
|
|
|
|
* time. We do not reboot or halt on failures, as this is a best-effort
|
|
|
|
* attempt.
|
|
|
|
*/
|
|
|
|
static int mix_hwrng_into_linux_rng_action(int nargs, char **args)
|
|
|
|
{
|
|
|
|
int result = -1;
|
|
|
|
int hwrandom_fd = -1;
|
|
|
|
int urandom_fd = -1;
|
|
|
|
char buf[512];
|
|
|
|
ssize_t chunk_size;
|
|
|
|
size_t total_bytes_written = 0;
|
|
|
|
|
|
|
|
hwrandom_fd = TEMP_FAILURE_RETRY(
|
|
|
|
open("/dev/hw_random", O_RDONLY | O_NOFOLLOW));
|
|
|
|
if (hwrandom_fd == -1) {
|
|
|
|
if (errno == ENOENT) {
|
|
|
|
ERROR("/dev/hw_random not found\n");
|
|
|
|
/* It's not an error to not have a Hardware RNG. */
|
|
|
|
result = 0;
|
|
|
|
} else {
|
|
|
|
ERROR("Failed to open /dev/hw_random: %s\n", strerror(errno));
|
|
|
|
}
|
|
|
|
goto ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
urandom_fd = TEMP_FAILURE_RETRY(
|
|
|
|
open("/dev/urandom", O_WRONLY | O_NOFOLLOW));
|
|
|
|
if (urandom_fd == -1) {
|
|
|
|
ERROR("Failed to open /dev/urandom: %s\n", strerror(errno));
|
|
|
|
goto ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
while (total_bytes_written < sizeof(buf)) {
|
|
|
|
chunk_size = TEMP_FAILURE_RETRY(
|
|
|
|
read(hwrandom_fd, buf, sizeof(buf) - total_bytes_written));
|
|
|
|
if (chunk_size == -1) {
|
|
|
|
ERROR("Failed to read from /dev/hw_random: %s\n", strerror(errno));
|
|
|
|
goto ret;
|
|
|
|
} else if (chunk_size == 0) {
|
|
|
|
ERROR("Failed to read from /dev/hw_random: EOF\n");
|
|
|
|
goto ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
chunk_size = TEMP_FAILURE_RETRY(write(urandom_fd, buf, chunk_size));
|
|
|
|
if (chunk_size == -1) {
|
|
|
|
ERROR("Failed to write to /dev/urandom: %s\n", strerror(errno));
|
|
|
|
goto ret;
|
|
|
|
}
|
|
|
|
total_bytes_written += chunk_size;
|
|
|
|
}
|
|
|
|
|
2014-01-16 19:53:11 +01:00
|
|
|
INFO("Mixed %zu bytes from /dev/hw_random into /dev/urandom",
|
2013-08-16 22:19:24 +02:00
|
|
|
total_bytes_written);
|
|
|
|
result = 0;
|
|
|
|
|
|
|
|
ret:
|
|
|
|
if (hwrandom_fd != -1) {
|
|
|
|
close(hwrandom_fd);
|
|
|
|
}
|
|
|
|
if (urandom_fd != -1) {
|
|
|
|
close(urandom_fd);
|
|
|
|
}
|
|
|
|
memset(buf, 0, sizeof(buf));
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2010-04-14 04:52:01 +02:00
|
|
|
static int keychord_init_action(int nargs, char **args)
|
|
|
|
{
|
|
|
|
keychord_init();
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int console_init_action(int nargs, char **args)
|
2008-10-21 16:00:00 +02:00
|
|
|
{
|
|
|
|
int fd;
|
2010-04-14 04:52:01 +02:00
|
|
|
|
|
|
|
if (console[0]) {
|
2013-04-01 05:17:51 +02:00
|
|
|
snprintf(console_name, sizeof(console_name), "/dev/%s", console);
|
2010-04-14 04:52:01 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
fd = open(console_name, O_RDWR);
|
|
|
|
if (fd >= 0)
|
|
|
|
have_console = 1;
|
|
|
|
close(fd);
|
|
|
|
|
2013-10-16 17:39:16 +02:00
|
|
|
fd = open("/dev/tty0", O_WRONLY);
|
|
|
|
if (fd >= 0) {
|
|
|
|
const char *msg;
|
|
|
|
msg = "\n"
|
|
|
|
"\n"
|
|
|
|
"\n"
|
|
|
|
"\n"
|
|
|
|
"\n"
|
|
|
|
"\n"
|
|
|
|
"\n" // console is 40 cols x 30 lines
|
|
|
|
"\n"
|
|
|
|
"\n"
|
|
|
|
"\n"
|
|
|
|
"\n"
|
|
|
|
"\n"
|
|
|
|
"\n"
|
|
|
|
"\n"
|
|
|
|
" A N D R O I D ";
|
|
|
|
write(fd, msg, strlen(msg));
|
|
|
|
close(fd);
|
2010-04-14 04:52:01 +02:00
|
|
|
}
|
2013-10-16 17:39:16 +02:00
|
|
|
|
2010-04-14 04:52:01 +02:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2011-12-19 20:21:32 +01:00
|
|
|
static void import_kernel_nv(char *name, int for_emulator)
|
|
|
|
{
|
|
|
|
char *value = strchr(name, '=');
|
|
|
|
int name_len = strlen(name);
|
|
|
|
|
|
|
|
if (value == 0) return;
|
|
|
|
*value++ = 0;
|
|
|
|
if (name_len == 0) return;
|
|
|
|
|
|
|
|
if (for_emulator) {
|
|
|
|
/* in the emulator, export any kernel option with the
|
|
|
|
* ro.kernel. prefix */
|
|
|
|
char buff[PROP_NAME_MAX];
|
|
|
|
int len = snprintf( buff, sizeof(buff), "ro.kernel.%s", name );
|
|
|
|
|
|
|
|
if (len < (int)sizeof(buff))
|
|
|
|
property_set( buff, value );
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!strcmp(name,"qemu")) {
|
|
|
|
strlcpy(qemu, value, sizeof(qemu));
|
|
|
|
} else if (!strncmp(name, "androidboot.", 12) && name_len > 12) {
|
|
|
|
const char *boot_prop_name = name + 12;
|
|
|
|
char prop[PROP_NAME_MAX];
|
|
|
|
int cnt;
|
|
|
|
|
|
|
|
cnt = snprintf(prop, sizeof(prop), "ro.boot.%s", boot_prop_name);
|
|
|
|
if (cnt < PROP_NAME_MAX)
|
|
|
|
property_set(prop, value);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void export_kernel_boot_props(void)
|
2010-04-14 04:52:01 +02:00
|
|
|
{
|
|
|
|
char tmp[PROP_VALUE_MAX];
|
2013-01-29 02:13:35 +01:00
|
|
|
int ret;
|
2011-12-19 20:21:32 +01:00
|
|
|
unsigned i;
|
|
|
|
struct {
|
|
|
|
const char *src_prop;
|
|
|
|
const char *dest_prop;
|
|
|
|
const char *def_val;
|
|
|
|
} prop_map[] = {
|
|
|
|
{ "ro.boot.serialno", "ro.serialno", "", },
|
|
|
|
{ "ro.boot.mode", "ro.bootmode", "unknown", },
|
|
|
|
{ "ro.boot.baseband", "ro.baseband", "unknown", },
|
|
|
|
{ "ro.boot.bootloader", "ro.bootloader", "unknown", },
|
|
|
|
};
|
|
|
|
|
|
|
|
for (i = 0; i < ARRAY_SIZE(prop_map); i++) {
|
2013-01-29 02:13:35 +01:00
|
|
|
ret = property_get(prop_map[i].src_prop, tmp);
|
2013-06-18 01:20:08 +02:00
|
|
|
if (ret > 0)
|
|
|
|
property_set(prop_map[i].dest_prop, tmp);
|
|
|
|
else
|
2013-01-29 02:13:35 +01:00
|
|
|
property_set(prop_map[i].dest_prop, prop_map[i].def_val);
|
2011-12-19 20:21:32 +01:00
|
|
|
}
|
2010-04-14 04:52:01 +02:00
|
|
|
|
2013-01-29 02:13:35 +01:00
|
|
|
ret = property_get("ro.boot.console", tmp);
|
|
|
|
if (ret)
|
|
|
|
strlcpy(console, tmp, sizeof(console));
|
2011-12-19 20:21:32 +01:00
|
|
|
|
|
|
|
/* save a copy for init's usage during boot */
|
2013-01-29 02:13:35 +01:00
|
|
|
property_get("ro.bootmode", tmp);
|
|
|
|
strlcpy(bootmode, tmp, sizeof(bootmode));
|
2011-12-19 20:21:32 +01:00
|
|
|
|
|
|
|
/* if this was given on kernel command line, override what we read
|
|
|
|
* before (e.g. from /proc/cpuinfo), if anything */
|
2013-01-29 02:13:35 +01:00
|
|
|
ret = property_get("ro.boot.hardware", tmp);
|
|
|
|
if (ret)
|
|
|
|
strlcpy(hardware, tmp, sizeof(hardware));
|
2011-12-19 20:21:32 +01:00
|
|
|
property_set("ro.hardware", hardware);
|
2010-04-14 04:52:01 +02:00
|
|
|
|
2011-12-19 20:21:32 +01:00
|
|
|
snprintf(tmp, PROP_VALUE_MAX, "%d", revision);
|
|
|
|
property_set("ro.revision", tmp);
|
|
|
|
|
|
|
|
/* TODO: these are obsolete. We should delete them */
|
2010-04-14 04:52:01 +02:00
|
|
|
if (!strcmp(bootmode,"factory"))
|
|
|
|
property_set("ro.factorytest", "1");
|
|
|
|
else if (!strcmp(bootmode,"factory2"))
|
|
|
|
property_set("ro.factorytest", "2");
|
|
|
|
else
|
|
|
|
property_set("ro.factorytest", "0");
|
2011-12-19 20:21:32 +01:00
|
|
|
}
|
2010-04-14 04:52:01 +02:00
|
|
|
|
2011-12-19 20:21:32 +01:00
|
|
|
static void process_kernel_cmdline(void)
|
|
|
|
{
|
|
|
|
/* don't expose the raw commandline to nonpriv processes */
|
|
|
|
chmod("/proc/cmdline", 0440);
|
2010-04-14 04:52:01 +02:00
|
|
|
|
2011-12-19 20:21:32 +01:00
|
|
|
/* first pass does the common stuff, and finds if we are in qemu.
|
|
|
|
* second pass is only necessary for qemu to export all kernel params
|
|
|
|
* as props.
|
|
|
|
*/
|
|
|
|
import_kernel_cmdline(0, import_kernel_nv);
|
|
|
|
if (qemu[0])
|
|
|
|
import_kernel_cmdline(1, import_kernel_nv);
|
|
|
|
|
|
|
|
/* now propogate the info given on command line to internal variables
|
|
|
|
* used by init as well as the current required properties
|
|
|
|
*/
|
|
|
|
export_kernel_boot_props();
|
2010-04-14 04:52:01 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
static int property_service_init_action(int nargs, char **args)
|
|
|
|
{
|
|
|
|
/* read any property files on system or data and
|
|
|
|
* fire up the property service. This must happen
|
|
|
|
* after the ro.foo properties are set above so
|
|
|
|
* that /data/local.prop cannot interfere with them.
|
|
|
|
*/
|
|
|
|
start_property_service();
|
2014-07-12 00:05:23 +02:00
|
|
|
if (get_property_set_fd() < 0) {
|
|
|
|
ERROR("start_property_service() failed\n");
|
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
|
2010-04-14 04:52:01 +02:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int signal_init_action(int nargs, char **args)
|
|
|
|
{
|
|
|
|
signal_init();
|
2014-07-12 00:05:23 +02:00
|
|
|
if (get_signal_fd() < 0) {
|
|
|
|
ERROR("signal_init() failed\n");
|
2010-04-14 04:52:01 +02:00
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int queue_property_triggers_action(int nargs, char **args)
|
|
|
|
{
|
|
|
|
queue_all_property_triggers();
|
|
|
|
/* enable property triggers */
|
|
|
|
property_triggers_enabled = 1;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
#if BOOTCHART
|
|
|
|
static int bootchart_init_action(int nargs, char **args)
|
|
|
|
{
|
|
|
|
bootchart_count = bootchart_init();
|
|
|
|
if (bootchart_count < 0) {
|
|
|
|
ERROR("bootcharting init failure\n");
|
|
|
|
} else if (bootchart_count > 0) {
|
|
|
|
NOTICE("bootcharting started (period=%d ms)\n", bootchart_count*BOOTCHART_POLLING_MS);
|
|
|
|
} else {
|
|
|
|
NOTICE("bootcharting ignored\n");
|
|
|
|
}
|
2011-01-14 09:35:30 +01:00
|
|
|
|
|
|
|
return 0;
|
2010-04-14 04:52:01 +02:00
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2012-08-09 16:05:49 +02:00
|
|
|
static const struct selinux_opt seopts_prop[] = {
|
|
|
|
{ SELABEL_OPT_PATH, "/property_contexts" },
|
2014-03-18 02:16:53 +01:00
|
|
|
{ SELABEL_OPT_PATH, "/data/security/current/property_contexts" },
|
2012-08-09 16:05:49 +02:00
|
|
|
{ 0, NULL }
|
|
|
|
};
|
|
|
|
|
|
|
|
struct selabel_handle* selinux_android_prop_context_handle(void)
|
|
|
|
{
|
2014-03-18 02:16:53 +01:00
|
|
|
int policy_index = selinux_android_use_data_policy() ? 1 : 0;
|
|
|
|
struct selabel_handle* sehandle = selabel_open(SELABEL_CTX_ANDROID_PROP,
|
|
|
|
&seopts_prop[policy_index], 1);
|
2012-08-09 16:05:49 +02:00
|
|
|
if (!sehandle) {
|
|
|
|
ERROR("SELinux: Could not load property_contexts: %s\n",
|
|
|
|
strerror(errno));
|
|
|
|
return NULL;
|
|
|
|
}
|
2014-03-18 02:16:53 +01:00
|
|
|
INFO("SELinux: Loaded property contexts from %s\n", seopts_prop[policy_index].value);
|
2012-08-09 16:05:49 +02:00
|
|
|
return sehandle;
|
|
|
|
}
|
|
|
|
|
2012-05-01 21:02:53 +02:00
|
|
|
void selinux_init_all_handles(void)
|
2012-01-13 14:48:47 +01:00
|
|
|
{
|
2012-05-01 21:02:53 +02:00
|
|
|
sehandle = selinux_android_file_context_handle();
|
2014-01-28 16:34:09 +01:00
|
|
|
selinux_android_set_sehandle(sehandle);
|
2012-08-09 16:05:49 +02:00
|
|
|
sehandle_prop = selinux_android_prop_context_handle();
|
2012-05-01 21:02:53 +02:00
|
|
|
}
|
2012-01-13 14:48:47 +01:00
|
|
|
|
2013-06-27 00:37:26 +02:00
|
|
|
static bool selinux_is_disabled(void)
|
|
|
|
{
|
2014-06-03 05:56:04 +02:00
|
|
|
#ifdef ALLOW_DISABLE_SELINUX
|
2013-06-27 00:37:26 +02:00
|
|
|
char tmp[PROP_VALUE_MAX];
|
|
|
|
|
|
|
|
if (access("/sys/fs/selinux", F_OK) != 0) {
|
|
|
|
/* SELinux is not compiled into the kernel, or has been disabled
|
|
|
|
* via the kernel command line "selinux=0".
|
|
|
|
*/
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((property_get("ro.boot.selinux", tmp) != 0) && (strcmp(tmp, "disabled") == 0)) {
|
|
|
|
/* SELinux is compiled into the kernel, but we've been told to disable it. */
|
|
|
|
return true;
|
|
|
|
}
|
2014-06-03 05:56:04 +02:00
|
|
|
#endif
|
2013-06-27 00:37:26 +02:00
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool selinux_is_enforcing(void)
|
|
|
|
{
|
2014-06-03 05:56:04 +02:00
|
|
|
#ifdef ALLOW_DISABLE_SELINUX
|
2013-06-27 00:37:26 +02:00
|
|
|
char tmp[PROP_VALUE_MAX];
|
|
|
|
|
|
|
|
if (property_get("ro.boot.selinux", tmp) == 0) {
|
|
|
|
/* Property is not set. Assume enforcing */
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (strcmp(tmp, "permissive") == 0) {
|
|
|
|
/* SELinux is in the kernel, but we've been told to go into permissive mode */
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (strcmp(tmp, "enforcing") != 0) {
|
|
|
|
ERROR("SELinux: Unknown value of ro.boot.selinux. Got: \"%s\". Assuming enforcing.\n", tmp);
|
|
|
|
}
|
|
|
|
|
2014-06-03 05:56:04 +02:00
|
|
|
#endif
|
2013-06-27 00:37:26 +02:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2012-05-01 21:02:53 +02:00
|
|
|
int selinux_reload_policy(void)
|
|
|
|
{
|
2013-06-27 00:37:26 +02:00
|
|
|
if (selinux_is_disabled()) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2012-05-01 21:02:53 +02:00
|
|
|
INFO("SELinux: Attempting to reload policy files\n");
|
2012-01-13 14:48:47 +01:00
|
|
|
|
2012-05-01 21:02:53 +02:00
|
|
|
if (selinux_android_reload_policy() == -1) {
|
|
|
|
return -1;
|
2012-01-13 14:48:47 +01:00
|
|
|
}
|
|
|
|
|
2012-05-01 21:02:53 +02:00
|
|
|
if (sehandle)
|
|
|
|
selabel_close(sehandle);
|
2012-01-13 14:48:47 +01:00
|
|
|
|
2012-08-09 16:05:49 +02:00
|
|
|
if (sehandle_prop)
|
|
|
|
selabel_close(sehandle_prop);
|
|
|
|
|
2012-05-01 21:02:53 +02:00
|
|
|
selinux_init_all_handles();
|
|
|
|
return 0;
|
2012-01-13 14:48:47 +01:00
|
|
|
}
|
2012-08-09 16:05:49 +02:00
|
|
|
|
2014-02-12 22:17:00 +01:00
|
|
|
static int audit_callback(void *data, security_class_t cls __attribute__((unused)), char *buf, size_t len)
|
2012-08-09 16:05:49 +02:00
|
|
|
{
|
|
|
|
snprintf(buf, len, "property=%s", !data ? "NULL" : (char *)data);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2014-06-24 19:45:43 +02:00
|
|
|
int log_callback(int type, const char *fmt, ...)
|
2014-02-12 22:17:00 +01:00
|
|
|
{
|
|
|
|
int level;
|
|
|
|
va_list ap;
|
|
|
|
switch (type) {
|
|
|
|
case SELINUX_WARNING:
|
|
|
|
level = KLOG_WARNING_LEVEL;
|
|
|
|
break;
|
|
|
|
case SELINUX_INFO:
|
|
|
|
level = KLOG_INFO_LEVEL;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
level = KLOG_ERROR_LEVEL;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
va_start(ap, fmt);
|
|
|
|
klog_vwrite(level, fmt, ap);
|
|
|
|
va_end(ap);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2013-06-25 02:41:40 +02:00
|
|
|
static void selinux_initialize(void)
|
|
|
|
{
|
2013-06-27 00:37:26 +02:00
|
|
|
if (selinux_is_disabled()) {
|
2013-06-25 02:41:40 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
INFO("loading selinux policy\n");
|
|
|
|
if (selinux_android_load_policy() < 0) {
|
|
|
|
ERROR("SELinux: Failed to load policy; rebooting into recovery mode\n");
|
|
|
|
android_reboot(ANDROID_RB_RESTART2, 0, "recovery");
|
|
|
|
while (1) { pause(); } // never reached
|
|
|
|
}
|
|
|
|
|
|
|
|
selinux_init_all_handles();
|
2013-06-27 00:37:26 +02:00
|
|
|
bool is_enforcing = selinux_is_enforcing();
|
|
|
|
INFO("SELinux: security_setenforce(%d)\n", is_enforcing);
|
|
|
|
security_setenforce(is_enforcing);
|
2013-06-25 02:41:40 +02:00
|
|
|
}
|
|
|
|
|
2010-04-14 04:52:01 +02:00
|
|
|
int main(int argc, char **argv)
|
|
|
|
{
|
|
|
|
int fd_count = 0;
|
2008-10-21 16:00:00 +02:00
|
|
|
struct pollfd ufds[4];
|
2010-04-14 04:52:01 +02:00
|
|
|
int property_set_fd_init = 0;
|
|
|
|
int signal_fd_init = 0;
|
|
|
|
int keychord_fd_init = 0;
|
2011-12-16 23:18:06 +01:00
|
|
|
bool is_charger = false;
|
2008-10-21 16:00:00 +02:00
|
|
|
|
2010-04-21 21:04:20 +02:00
|
|
|
if (!strcmp(basename(argv[0]), "ueventd"))
|
|
|
|
return ueventd_main(argc, argv);
|
|
|
|
|
2012-06-14 06:51:56 +02:00
|
|
|
if (!strcmp(basename(argv[0]), "watchdogd"))
|
|
|
|
return watchdogd_main(argc, argv);
|
|
|
|
|
2008-10-21 16:00:00 +02:00
|
|
|
/* 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.
|
|
|
|
*/
|
|
|
|
mkdir("/dev", 0755);
|
|
|
|
mkdir("/proc", 0755);
|
|
|
|
mkdir("/sys", 0755);
|
|
|
|
|
2010-06-23 01:35:43 +02:00
|
|
|
mount("tmpfs", "/dev", "tmpfs", MS_NOSUID, "mode=0755");
|
2008-10-21 16:00:00 +02:00
|
|
|
mkdir("/dev/pts", 0755);
|
|
|
|
mkdir("/dev/socket", 0755);
|
|
|
|
mount("devpts", "/dev/pts", "devpts", 0, NULL);
|
|
|
|
mount("proc", "/proc", "proc", 0, NULL);
|
|
|
|
mount("sysfs", "/sys", "sysfs", 0, NULL);
|
|
|
|
|
2011-03-24 23:45:30 +01:00
|
|
|
/* indicate that booting is in progress to background fw loaders, etc */
|
|
|
|
close(open("/dev/.booting", O_WRONLY | O_CREAT, 0000));
|
|
|
|
|
2008-10-21 16:00:00 +02:00
|
|
|
/* 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();
|
2011-09-01 03:26:17 +02:00
|
|
|
klog_init();
|
2011-12-19 20:21:32 +01:00
|
|
|
property_init();
|
2011-09-28 18:55:31 +02:00
|
|
|
|
2010-04-21 21:04:20 +02:00
|
|
|
get_hardware_name(hardware, &revision);
|
2011-12-16 23:18:06 +01:00
|
|
|
|
2011-12-19 20:21:32 +01:00
|
|
|
process_kernel_cmdline();
|
|
|
|
|
2012-08-09 16:05:49 +02:00
|
|
|
union selinux_callback cb;
|
2014-02-12 22:17:00 +01:00
|
|
|
cb.func_log = log_callback;
|
2012-08-09 16:05:49 +02:00
|
|
|
selinux_set_callback(SELINUX_CB_LOG, cb);
|
|
|
|
|
|
|
|
cb.func_audit = audit_callback;
|
|
|
|
selinux_set_callback(SELINUX_CB_AUDIT, cb);
|
|
|
|
|
2013-06-25 02:41:40 +02:00
|
|
|
selinux_initialize();
|
2012-05-01 21:02:53 +02:00
|
|
|
/* These directories were necessarily created before initial policy load
|
2012-06-11 19:37:39 +02:00
|
|
|
* 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");
|
2013-03-01 02:29:58 +01:00
|
|
|
restorecon("/dev/__properties__");
|
2013-07-12 00:38:26 +02:00
|
|
|
restorecon_recursive("/sys");
|
2012-01-13 14:48:47 +01:00
|
|
|
|
2011-12-16 23:18:06 +01:00
|
|
|
is_charger = !strcmp(bootmode, "charger");
|
|
|
|
|
|
|
|
INFO("property init\n");
|
2014-06-17 00:06:21 +02:00
|
|
|
property_load_boot_defaults();
|
2011-12-16 23:18:06 +01:00
|
|
|
|
|
|
|
INFO("reading config file\n");
|
|
|
|
init_parse_config_file("/init.rc");
|
2008-10-21 16:00:00 +02:00
|
|
|
|
|
|
|
action_for_each_trigger("early-init", action_add_queue_tail);
|
|
|
|
|
2010-04-21 21:04:20 +02:00
|
|
|
queue_builtin_action(wait_for_coldboot_done_action, "wait_for_coldboot_done");
|
2013-08-16 22:19:24 +02:00
|
|
|
queue_builtin_action(mix_hwrng_into_linux_rng_action, "mix_hwrng_into_linux_rng");
|
2010-04-14 04:52:01 +02:00
|
|
|
queue_builtin_action(keychord_init_action, "keychord_init");
|
|
|
|
queue_builtin_action(console_init_action, "console_init");
|
2008-10-21 16:00:00 +02:00
|
|
|
|
2011-08-25 00:28:23 +02:00
|
|
|
/* execute all the boot actions to get us started */
|
2008-10-21 16:00:00 +02:00
|
|
|
action_for_each_trigger("init", action_add_queue_tail);
|
2011-08-25 00:28:23 +02:00
|
|
|
|
2013-08-16 22:19:24 +02:00
|
|
|
/* 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");
|
2010-04-14 04:52:01 +02:00
|
|
|
queue_builtin_action(property_service_init_action, "property_service_init");
|
|
|
|
queue_builtin_action(signal_init_action, "signal_init");
|
2008-10-21 16:00:00 +02:00
|
|
|
|
2014-06-17 00:06:21 +02:00
|
|
|
/* Don't mount filesystems or start core system services if in charger mode. */
|
2011-12-16 23:18:06 +01:00
|
|
|
if (is_charger) {
|
2011-08-25 00:28:23 +02:00
|
|
|
action_for_each_trigger("charger", action_add_queue_tail);
|
|
|
|
} else {
|
2014-06-17 00:06:21 +02:00
|
|
|
action_for_each_trigger("late-init", action_add_queue_tail);
|
2011-08-25 00:28:23 +02:00
|
|
|
}
|
2008-10-21 16:00:00 +02:00
|
|
|
|
2014-07-12 00:05:23 +02:00
|
|
|
/* run all property triggers based on current state of the properties */
|
2012-03-02 00:29:20 +01:00
|
|
|
queue_builtin_action(queue_property_triggers_action, "queue_property_triggers");
|
2010-04-14 04:52:01 +02:00
|
|
|
|
2008-10-21 16:00:00 +02:00
|
|
|
|
|
|
|
#if BOOTCHART
|
2010-04-14 04:52:01 +02:00
|
|
|
queue_builtin_action(bootchart_init_action, "bootchart_init");
|
2008-10-21 16:00:00 +02:00
|
|
|
#endif
|
|
|
|
|
|
|
|
for(;;) {
|
2009-01-10 02:51:25 +01:00
|
|
|
int nr, i, timeout = -1;
|
2008-10-21 16:00:00 +02:00
|
|
|
|
2010-04-14 04:52:01 +02:00
|
|
|
execute_one_command();
|
2008-10-21 16:00:00 +02:00
|
|
|
restart_processes();
|
|
|
|
|
2010-04-14 04:52:01 +02:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2008-10-21 16:00:00 +02:00
|
|
|
if (process_needs_restart) {
|
|
|
|
timeout = (process_needs_restart - gettime()) * 1000;
|
|
|
|
if (timeout < 0)
|
|
|
|
timeout = 0;
|
|
|
|
}
|
|
|
|
|
2010-04-22 20:52:23 +02:00
|
|
|
if (!action_queue_empty() || cur_action)
|
2010-04-14 04:52:01 +02:00
|
|
|
timeout = 0;
|
|
|
|
|
2008-10-21 16:00:00 +02:00
|
|
|
#if BOOTCHART
|
|
|
|
if (bootchart_count > 0) {
|
2014-07-24 19:11:35 +02:00
|
|
|
long long current_time;
|
|
|
|
int elapsed_time, remaining_time;
|
|
|
|
|
|
|
|
current_time = bootchart_gettime();
|
|
|
|
elapsed_time = current_time - bootchart_time;
|
|
|
|
|
|
|
|
if (elapsed_time >= BOOTCHART_POLLING_MS) {
|
|
|
|
/* count missed samples */
|
|
|
|
while (elapsed_time >= BOOTCHART_POLLING_MS) {
|
|
|
|
elapsed_time -= BOOTCHART_POLLING_MS;
|
|
|
|
bootchart_count--;
|
|
|
|
}
|
|
|
|
/* count may be negative, take a sample anyway */
|
|
|
|
bootchart_time = current_time;
|
|
|
|
if (bootchart_step() < 0 || bootchart_count <= 0) {
|
|
|
|
bootchart_finish();
|
|
|
|
bootchart_count = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (bootchart_count > 0) {
|
|
|
|
remaining_time = BOOTCHART_POLLING_MS - elapsed_time;
|
|
|
|
if (timeout < 0 || timeout > remaining_time)
|
|
|
|
timeout = remaining_time;
|
2008-10-21 16:00:00 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
2010-04-14 04:52:01 +02:00
|
|
|
|
2009-01-10 02:51:25 +01:00
|
|
|
nr = poll(ufds, fd_count, timeout);
|
2008-10-21 16:00:00 +02:00
|
|
|
if (nr <= 0)
|
|
|
|
continue;
|
|
|
|
|
2010-04-14 04:52:01 +02:00
|
|
|
for (i = 0; i < fd_count; i++) {
|
2013-11-10 14:36:58 +01:00
|
|
|
if (ufds[i].revents & POLLIN) {
|
2010-04-21 21:04:20 +02:00
|
|
|
if (ufds[i].fd == get_property_set_fd())
|
2010-04-14 04:52:01 +02:00
|
|
|
handle_property_set_fd();
|
|
|
|
else if (ufds[i].fd == get_keychord_fd())
|
|
|
|
handle_keychord();
|
|
|
|
else if (ufds[i].fd == get_signal_fd())
|
|
|
|
handle_signal();
|
|
|
|
}
|
2008-10-21 16:00:00 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|