summaryrefslogtreecommitdiffstats
path: root/init
diff options
context:
space:
mode:
Diffstat (limited to 'init')
-rw-r--r--init/Android.mk130
-rw-r--r--init/README.BOOTCHART52
-rw-r--r--init/bootchart.c378
-rw-r--r--init/bootchart.cpp273
-rw-r--r--init/bootchart.h16
-rw-r--r--init/builtins.cpp (renamed from init/builtins.c)339
-rw-r--r--init/devices.cpp (renamed from init/devices.c)187
-rw-r--r--init/devices.h1
-rwxr-xr-xinit/grab-bootchart.sh16
-rw-r--r--init/init.cpp (renamed from init/init.c)742
-rw-r--r--init/init.h65
-rw-r--r--init/init_parser.cpp (renamed from init/init_parser.c)344
-rw-r--r--init/init_parser.h5
-rw-r--r--init/init_parser_test.cpp134
-rw-r--r--init/keychords.cpp (renamed from init/keychords.c)64
-rw-r--r--init/keychords.h6
-rw-r--r--init/keywords.h23
-rw-r--r--init/log.cpp62
-rw-r--r--init/log.h9
-rw-r--r--init/parser.cpp (renamed from init/parser.c)50
-rw-r--r--init/parser.h2
-rw-r--r--init/property_service.cpp (renamed from init/property_service.c)264
-rw-r--r--init/property_service.h12
-rw-r--r--init/readme.txt256
-rw-r--r--init/signal_handler.c165
-rw-r--r--init/signal_handler.cpp185
-rw-r--r--init/signal_handler.h4
-rw-r--r--init/ueventd.cpp (renamed from init/ueventd.c)77
-rw-r--r--init/ueventd.h12
-rw-r--r--init/ueventd_parser.cpp (renamed from init/ueventd_parser.c)33
-rw-r--r--init/util.cpp (renamed from init/util.c)198
-rw-r--r--init/util.h30
-rw-r--r--init/util_test.cpp43
-rw-r--r--init/watchdogd.cpp (renamed from init/watchdogd.c)38
34 files changed, 2057 insertions, 2158 deletions
diff --git a/init/Android.mk b/init/Android.mk
index 228e645..31d2fcd 100644
--- a/init/Android.mk
+++ b/init/Android.mk
@@ -1,73 +1,99 @@
# Copyright 2005 The Android Open Source Project
LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-LOCAL_SRC_FILES:= \
- builtins.c \
- init.c \
- devices.c \
- property_service.c \
- util.c \
- parser.c \
- keychords.c \
- signal_handler.c \
- init_parser.c \
- ueventd.c \
- ueventd_parser.c \
- watchdogd.c
-
-LOCAL_CFLAGS += -Wno-unused-parameter
-
-ifeq ($(strip $(INIT_BOOTCHART)),true)
-LOCAL_SRC_FILES += bootchart.c
-LOCAL_CFLAGS += -DBOOTCHART=1
-endif
+# --
ifneq (,$(filter userdebug eng,$(TARGET_BUILD_VARIANT)))
-LOCAL_CFLAGS += -DALLOW_LOCAL_PROP_OVERRIDE=1 -DALLOW_DISABLE_SELINUX=1
+init_options += -DALLOW_LOCAL_PROP_OVERRIDE=1 -DALLOW_DISABLE_SELINUX=1
+else
+init_options += -DALLOW_LOCAL_PROP_OVERRIDE=0 -DALLOW_DISABLE_SELINUX=0
endif
-# Enable ueventd logging
-#LOCAL_CFLAGS += -DLOG_UEVENTS=1
+init_options += -DLOG_UEVENTS=0
-LOCAL_MODULE:= init
+init_cflags += \
+ $(init_options) \
+ -Wall -Wextra \
+ -Wno-unused-parameter \
+ -Werror \
+
+init_clang := true
+
+# --
+
+include $(CLEAR_VARS)
+LOCAL_CPPFLAGS := $(init_cflags)
+LOCAL_SRC_FILES:= \
+ init_parser.cpp \
+ log.cpp \
+ parser.cpp \
+ util.cpp \
+LOCAL_STATIC_LIBRARIES := libbase
+LOCAL_MODULE := libinit
+LOCAL_CLANG := $(init_clang)
+include $(BUILD_STATIC_LIBRARY)
+
+include $(CLEAR_VARS)
+LOCAL_CPPFLAGS := $(init_cflags)
+LOCAL_SRC_FILES:= \
+ bootchart.cpp \
+ builtins.cpp \
+ devices.cpp \
+ init.cpp \
+ keychords.cpp \
+ property_service.cpp \
+ signal_handler.cpp \
+ ueventd.cpp \
+ ueventd_parser.cpp \
+ watchdogd.cpp \
+
+LOCAL_MODULE:= init
+LOCAL_C_INCLUDES += system/extras/ext4_utils
LOCAL_FORCE_STATIC_EXECUTABLE := true
LOCAL_MODULE_PATH := $(TARGET_ROOT_OUT)
LOCAL_UNSTRIPPED_PATH := $(TARGET_ROOT_OUT_UNSTRIPPED)
LOCAL_STATIC_LIBRARIES := \
- libfs_mgr \
- liblogwrap \
- libcutils \
- liblog \
- libc \
- libselinux \
- libmincrypt \
- libext4_utils_static \
- libsparse_static \
- libz
-
-LOCAL_ADDITIONAL_DEPENDENCIES += $(LOCAL_PATH)/Android.mk
+ libinit \
+ libfs_mgr \
+ libsquashfs_utils \
+ liblogwrap \
+ libcutils \
+ libbase \
+ libext4_utils_static \
+ libutils \
+ liblog \
+ libc \
+ libselinux \
+ libmincrypt \
+ libc++_static \
+ libdl \
+ libsparse_static \
+ libz
+# Create symlinks
+LOCAL_POST_INSTALL_CMD := $(hide) mkdir -p $(TARGET_ROOT_OUT)/sbin; \
+ ln -sf ../init $(TARGET_ROOT_OUT)/sbin/ueventd; \
+ ln -sf ../init $(TARGET_ROOT_OUT)/sbin/watchdogd
+
+LOCAL_CLANG := $(init_clang)
include $(BUILD_EXECUTABLE)
-# Make a symlink from /sbin/ueventd and /sbin/watchdogd to /init
-SYMLINKS := \
- $(TARGET_ROOT_OUT)/sbin/ueventd \
- $(TARGET_ROOT_OUT)/sbin/watchdogd
-$(SYMLINKS): INIT_BINARY := $(LOCAL_MODULE)
-$(SYMLINKS): $(LOCAL_INSTALLED_MODULE) $(LOCAL_PATH)/Android.mk
- @echo "Symlink: $@ -> ../$(INIT_BINARY)"
- @mkdir -p $(dir $@)
- @rm -rf $@
- $(hide) ln -sf ../$(INIT_BINARY) $@
-ALL_DEFAULT_INSTALLED_MODULES += $(SYMLINKS)
-# We need this so that the installed files could be picked up based on the
-# local module name
-ALL_MODULES.$(LOCAL_MODULE).INSTALLED := \
- $(ALL_MODULES.$(LOCAL_MODULE).INSTALLED) $(SYMLINKS)
+include $(CLEAR_VARS)
+LOCAL_MODULE := init_tests
+LOCAL_SRC_FILES := \
+ init_parser_test.cpp \
+ util_test.cpp \
+
+LOCAL_SHARED_LIBRARIES += \
+ libcutils \
+ libbase \
+
+LOCAL_STATIC_LIBRARIES := libinit
+LOCAL_CLANG := $(init_clang)
+include $(BUILD_NATIVE_TEST)
diff --git a/init/README.BOOTCHART b/init/README.BOOTCHART
deleted file mode 100644
index 70cf2c3..0000000
--- a/init/README.BOOTCHART
+++ /dev/null
@@ -1,52 +0,0 @@
-This version of init contains code to perform "bootcharting", i.e. generating log
-files that can be later processed by the tools provided by www.bootchart.org.
-
-To activate it, you need to define build 'init' with the INIT_BOOTCHART environment
-variable defined to 'true', for example:
-
- touch system/init/init.c
- m INIT_BOOTCHART=true
-
-On the emulator, use the new -bootchart <timeout> option to boot with bootcharting
-activated for <timeout> seconds.
-
-Otherwise, flash your device, and start it. Then create a file on the /data partition
-with a command like the following:
-
- adb shell 'echo $TIMEOUT > /data/bootchart-start'
-
-Where the value of $TIMEOUT corresponds to the wanted bootcharted period in seconds;
-for example, to bootchart for 2 minutes, do:
-
- adb shell 'echo 120 > /data/bootchart-start'
-
-Reboot your device, bootcharting will begin and stop after the period you gave.
-You can also stop the bootcharting at any moment by doing the following:
-
- adb shell 'echo 1 > /data/bootchart-stop'
-
-Note that /data/bootchart-stop is deleted automatically by init at the end of the
-bootcharting. This is not the case of /data/bootchart-start, so don't forget to delete it
-when you're done collecting data:
-
- adb shell rm /data/bootchart-start
-
-The log files are placed in /data/bootchart/. you must run the script tools/grab-bootchart.sh
-which will use ADB to retrieve them and create a bootchart.tgz file that can be used with
-the bootchart parser/renderer, or even uploaded directly to the form located at:
-
- http://www.bootchart.org/download.html
-
-NOTE: the bootchart.org webform doesn't seem to work at the moment, you can generate an
- image on your machine by doing the following:
-
- 1/ download the sources from www.bootchart.org
- 2/ unpack them
- 3/ in the source directory, type 'ant' to build the bootchart program
- 4/ type 'java -jar bootchart.jar /path/to/bootchart.tgz
-
-technical note:
-
-this implementation of bootcharting does use the 'bootchartd' script provided by
-www.bootchart.org, but a C re-implementation that is directly compiled into our init
-program.
diff --git a/init/bootchart.c b/init/bootchart.c
deleted file mode 100644
index f72fcaa..0000000
--- a/init/bootchart.c
+++ /dev/null
@@ -1,378 +0,0 @@
-/*
- * 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.
- */
-
-/* this code is used to generate a boot sequence profile that can be used
- * with the 'bootchart' graphics generation tool. see www.bootchart.org
- * note that unlike the original bootchartd, this is not a Bash script but
- * some C code that is run right from the init script.
- */
-
-#include <stdio.h>
-#include <time.h>
-#include <dirent.h>
-#include <unistd.h>
-#include <fcntl.h>
-#include <unistd.h>
-#include <fcntl.h>
-#include <unistd.h>
-#include <fcntl.h>
-#include <errno.h>
-#include <stdlib.h>
-#include <sys/stat.h>
-#include "bootchart.h"
-
-#define VERSION "0.8"
-#define SAMPLE_PERIOD 0.2
-#define LOG_ROOT "/data/bootchart"
-#define LOG_STAT LOG_ROOT"/proc_stat.log"
-#define LOG_PROCS LOG_ROOT"/proc_ps.log"
-#define LOG_DISK LOG_ROOT"/proc_diskstats.log"
-#define LOG_HEADER LOG_ROOT"/header"
-#define LOG_ACCT LOG_ROOT"/kernel_pacct"
-
-#define LOG_STARTFILE "/data/bootchart-start"
-#define LOG_STOPFILE "/data/bootchart-stop"
-
-static int
-unix_read(int fd, void* buff, int len)
-{
- int ret;
- do { ret = read(fd, buff, len); } while (ret < 0 && errno == EINTR);
- return ret;
-}
-
-static int
-unix_write(int fd, const void* buff, int len)
-{
- int ret;
- do { ret = write(fd, buff, len); } while (ret < 0 && errno == EINTR);
- return ret;
-}
-
-static int
-proc_read(const char* filename, char* buff, size_t buffsize)
-{
- int len = 0;
- int fd = open(filename, O_RDONLY);
- if (fd >= 0) {
- len = unix_read(fd, buff, buffsize-1);
- close(fd);
- }
- buff[len > 0 ? len : 0] = 0;
- return len;
-}
-
-#define FILE_BUFF_SIZE 65536
-
-typedef struct {
- int count;
- int fd;
- char data[FILE_BUFF_SIZE];
-} FileBuffRec, *FileBuff;
-
-static void
-file_buff_open( FileBuff buff, const char* path )
-{
- buff->count = 0;
- buff->fd = open(path, O_WRONLY|O_CREAT|O_TRUNC, 0755);
-}
-
-static void
-file_buff_write( FileBuff buff, const void* src, int len )
-{
- while (len > 0) {
- int avail = sizeof(buff->data) - buff->count;
- if (avail > len)
- avail = len;
-
- memcpy( buff->data + buff->count, src, avail );
- len -= avail;
- src = (char*)src + avail;
-
- buff->count += avail;
- if (buff->count == FILE_BUFF_SIZE) {
- unix_write( buff->fd, buff->data, buff->count );
- buff->count = 0;
- }
- }
-}
-
-static void
-file_buff_done( FileBuff buff )
-{
- if (buff->count > 0) {
- unix_write( buff->fd, buff->data, buff->count );
- buff->count = 0;
- }
-}
-
-static void
-log_header(void)
-{
- FILE* out;
- char cmdline[1024];
- char uname[128];
- char cpuinfo[128];
- char* cpu;
- char date[32];
- time_t now_t = time(NULL);
- struct tm now = *localtime(&now_t);
- strftime(date, sizeof(date), "%x %X", &now);
-
- out = fopen( LOG_HEADER, "w" );
- if (out == NULL)
- return;
-
- proc_read("/proc/cmdline", cmdline, sizeof(cmdline));
- proc_read("/proc/version", uname, sizeof(uname));
- proc_read("/proc/cpuinfo", cpuinfo, sizeof(cpuinfo));
-
- cpu = strchr( cpuinfo, ':' );
- if (cpu) {
- char* p = strchr(cpu, '\n');
- cpu += 2;
- if (p)
- *p = 0;
- }
-
- fprintf(out, "version = %s\n", VERSION);
- fprintf(out, "title = Boot chart for Android ( %s )\n", date);
- fprintf(out, "system.uname = %s\n", uname);
- fprintf(out, "system.release = 0.0\n");
- fprintf(out, "system.cpu = %s\n", cpu);
- fprintf(out, "system.kernel.options = %s\n", cmdline);
- fclose(out);
-}
-
-static void
-close_on_exec(int fd)
-{
- fcntl(fd, F_SETFD, FD_CLOEXEC);
-}
-
-static void
-open_log_file(int* plogfd, const char* logfile)
-{
- int logfd = *plogfd;
-
- /* create log file if needed */
- if (logfd < 0)
- {
- logfd = open(logfile,O_WRONLY|O_CREAT|O_TRUNC,0755);
- if (logfd < 0) {
- *plogfd = -2;
- return;
- }
- close_on_exec(logfd);
- *plogfd = logfd;
- }
-}
-
-static void
-do_log_uptime(FileBuff log)
-{
- char buff[65];
- int fd, ret, len;
-
- fd = open("/proc/uptime",O_RDONLY);
- if (fd >= 0) {
- int ret;
- ret = unix_read(fd, buff, 64);
- close(fd);
- buff[64] = 0;
- if (ret >= 0) {
- long long jiffies = 100LL*strtod(buff,NULL);
- int len;
- snprintf(buff,sizeof(buff),"%lld\n",jiffies);
- len = strlen(buff);
- file_buff_write(log, buff, len);
- }
- }
-}
-
-static void
-do_log_ln(FileBuff log)
-{
- file_buff_write(log, "\n", 1);
-}
-
-
-static void
-do_log_file(FileBuff log, const char* procfile)
-{
- char buff[1024];
- int fd;
-
- do_log_uptime(log);
-
- /* append file content */
- fd = open(procfile,O_RDONLY);
- if (fd >= 0) {
- close_on_exec(fd);
- for (;;) {
- int ret;
- ret = unix_read(fd, buff, sizeof(buff));
- if (ret <= 0)
- break;
-
- file_buff_write(log, buff, ret);
- if (ret < (int)sizeof(buff))
- break;
- }
- close(fd);
- }
-
- do_log_ln(log);
-}
-
-static void
-do_log_procs(FileBuff log)
-{
- DIR* dir = opendir("/proc");
- struct dirent* entry;
-
- do_log_uptime(log);
-
- while ((entry = readdir(dir)) != NULL) {
- /* only match numeric values */
- char* end;
- int pid = strtol( entry->d_name, &end, 10);
- if (end != NULL && end > entry->d_name && *end == 0) {
- char filename[32];
- char buff[1024];
- char cmdline[1024];
- int len;
- int fd;
-
- /* read command line and extract program name */
- snprintf(filename,sizeof(filename),"/proc/%d/cmdline",pid);
- proc_read(filename, cmdline, sizeof(cmdline));
-
- /* read process stat line */
- snprintf(filename,sizeof(filename),"/proc/%d/stat",pid);
- fd = open(filename,O_RDONLY);
- if (fd >= 0) {
- len = unix_read(fd, buff, sizeof(buff)-1);
- close(fd);
- if (len > 0) {
- int len2 = strlen(cmdline);
- if (len2 > 0) {
- /* we want to substitute the process name with its real name */
- const char* p1;
- const char* p2;
- buff[len] = 0;
- p1 = strchr(buff, '(');
- p2 = strchr(p1, ')');
- file_buff_write(log, buff, p1+1-buff);
- file_buff_write(log, cmdline, strlen(cmdline));
- file_buff_write(log, p2, strlen(p2));
- } else {
- /* no substitution */
- file_buff_write(log,buff,len);
- }
- }
- }
- }
- }
- closedir(dir);
- do_log_ln(log);
-}
-
-static FileBuffRec log_stat[1];
-static FileBuffRec log_procs[1];
-static FileBuffRec log_disks[1];
-
-/* called to setup bootcharting */
-int bootchart_init( void )
-{
- int ret;
- char buff[4];
- int timeout = 0, count = 0;
-
- buff[0] = 0;
- proc_read( LOG_STARTFILE, buff, sizeof(buff) );
- if (buff[0] != 0) {
- timeout = atoi(buff);
- }
- else {
- /* when running with emulator, androidboot.bootchart=<timeout>
- * might be passed by as kernel parameters to specify the bootchart
- * timeout. this is useful when using -wipe-data since the /data
- * partition is fresh
- */
- char cmdline[1024];
- char* s;
-#define KERNEL_OPTION "androidboot.bootchart="
- proc_read( "/proc/cmdline", cmdline, sizeof(cmdline) );
- s = strstr(cmdline, KERNEL_OPTION);
- if (s) {
- s += sizeof(KERNEL_OPTION)-1;
- timeout = atoi(s);
- }
- }
- if (timeout == 0)
- return 0;
-
- if (timeout > BOOTCHART_MAX_TIME_SEC)
- timeout = BOOTCHART_MAX_TIME_SEC;
-
- count = (timeout*1000 + BOOTCHART_POLLING_MS-1)/BOOTCHART_POLLING_MS;
-
- do {ret=mkdir(LOG_ROOT,0755);}while (ret < 0 && errno == EINTR);
-
- file_buff_open(log_stat, LOG_STAT);
- file_buff_open(log_procs, LOG_PROCS);
- file_buff_open(log_disks, LOG_DISK);
-
- /* create kernel process accounting file */
- {
- int fd = open( LOG_ACCT, O_WRONLY|O_CREAT|O_TRUNC,0644);
- if (fd >= 0) {
- close(fd);
- acct( LOG_ACCT );
- }
- }
-
- log_header();
- return count;
-}
-
-/* called each time you want to perform a bootchart sampling op */
-int bootchart_step( void )
-{
- do_log_file(log_stat, "/proc/stat");
- do_log_file(log_disks, "/proc/diskstats");
- do_log_procs(log_procs);
-
- /* we stop when /data/bootchart-stop contains 1 */
- {
- char buff[2];
- if (proc_read(LOG_STOPFILE,buff,sizeof(buff)) > 0 && buff[0] == '1') {
- return -1;
- }
- }
-
- return 0;
-}
-
-void bootchart_finish( void )
-{
- unlink( LOG_STOPFILE );
- file_buff_done(log_stat);
- file_buff_done(log_disks);
- file_buff_done(log_procs);
- acct(NULL);
-}
diff --git a/init/bootchart.cpp b/init/bootchart.cpp
new file mode 100644
index 0000000..95687cb
--- /dev/null
+++ b/init/bootchart.cpp
@@ -0,0 +1,273 @@
+/*
+ * 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 "bootchart.h"
+#include "keywords.h"
+#include "log.h"
+#include "property_service.h"
+
+#include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/utsname.h>
+#include <time.h>
+#include <unistd.h>
+
+#include <memory>
+#include <string>
+
+#include <base/file.h>
+
+#define LOG_ROOT "/data/bootchart"
+#define LOG_STAT LOG_ROOT"/proc_stat.log"
+#define LOG_PROCS LOG_ROOT"/proc_ps.log"
+#define LOG_DISK LOG_ROOT"/proc_diskstats.log"
+#define LOG_HEADER LOG_ROOT"/header"
+#define LOG_ACCT LOG_ROOT"/kernel_pacct"
+
+#define LOG_STARTFILE LOG_ROOT"/start"
+#define LOG_STOPFILE LOG_ROOT"/stop"
+
+// Polling period in ms.
+static const int BOOTCHART_POLLING_MS = 200;
+
+// Max polling time in seconds.
+static const int BOOTCHART_MAX_TIME_SEC = 10*60;
+
+static long long g_last_bootchart_time;
+static int g_remaining_samples;
+
+static FILE* log_stat;
+static FILE* log_procs;
+static FILE* log_disks;
+
+static long long get_uptime_jiffies() {
+ std::string uptime;
+ if (!android::base::ReadFileToString("/proc/uptime", &uptime)) {
+ return 0;
+ }
+ return 100LL * strtod(uptime.c_str(), NULL);
+}
+
+static void log_header() {
+ char date[32];
+ time_t now_t = time(NULL);
+ struct tm now = *localtime(&now_t);
+ strftime(date, sizeof(date), "%F %T", &now);
+
+ utsname uts;
+ if (uname(&uts) == -1) {
+ return;
+ }
+
+ char fingerprint[PROP_VALUE_MAX];
+ if (property_get("ro.build.fingerprint", fingerprint) == -1) {
+ return;
+ }
+
+ std::string kernel_cmdline;
+ android::base::ReadFileToString("/proc/cmdline", &kernel_cmdline);
+
+ FILE* out = fopen(LOG_HEADER, "we");
+ if (out == NULL) {
+ return;
+ }
+ fprintf(out, "version = Android init 0.8 " __TIME__ "\n");
+ fprintf(out, "title = Boot chart for Android (%s)\n", date);
+ fprintf(out, "system.uname = %s %s %s %s\n", uts.sysname, uts.release, uts.version, uts.machine);
+ fprintf(out, "system.release = %s\n", fingerprint);
+ // TODO: use /proc/cpuinfo "model name" line for x86, "Processor" line for arm.
+ fprintf(out, "system.cpu = %s\n", uts.machine);
+ fprintf(out, "system.kernel.options = %s\n", kernel_cmdline.c_str());
+ fclose(out);
+}
+
+static void do_log_uptime(FILE* log) {
+ fprintf(log, "%lld\n", get_uptime_jiffies());
+}
+
+static void do_log_file(FILE* log, const char* procfile) {
+ do_log_uptime(log);
+
+ std::string content;
+ if (android::base::ReadFileToString(procfile, &content)) {
+ fprintf(log, "%s\n", content.c_str());
+ }
+}
+
+static void do_log_procs(FILE* log) {
+ do_log_uptime(log);
+
+ std::unique_ptr<DIR, int(*)(DIR*)> dir(opendir("/proc"), closedir);
+ struct dirent* entry;
+ while ((entry = readdir(dir.get())) != NULL) {
+ // Only match numeric values.
+ char* end;
+ int pid = strtol(entry->d_name, &end, 10);
+ if (end != NULL && end > entry->d_name && *end == 0) {
+ char filename[32];
+
+ // /proc/<pid>/stat only has truncated task names, so get the full
+ // name from /proc/<pid>/cmdline.
+ snprintf(filename, sizeof(filename), "/proc/%d/cmdline", pid);
+ std::string cmdline;
+ android::base::ReadFileToString(filename, &cmdline);
+ const char* full_name = cmdline.c_str(); // So we stop at the first NUL.
+
+ // Read process stat line.
+ snprintf(filename, sizeof(filename), "/proc/%d/stat", pid);
+ std::string stat;
+ if (android::base::ReadFileToString(filename, &stat)) {
+ if (!cmdline.empty()) {
+ // Substitute the process name with its real name.
+ size_t open = stat.find('(');
+ size_t close = stat.find_last_of(')');
+ if (open != std::string::npos && close != std::string::npos) {
+ stat.replace(open + 1, close - open - 1, full_name);
+ }
+ }
+ fputs(stat.c_str(), log);
+ }
+ }
+ }
+
+ fputc('\n', log);
+}
+
+static int bootchart_init() {
+ int timeout = 0;
+
+ std::string start;
+ android::base::ReadFileToString(LOG_STARTFILE, &start);
+ if (!start.empty()) {
+ timeout = atoi(start.c_str());
+ } else {
+ // When running with emulator, androidboot.bootchart=<timeout>
+ // might be passed by as kernel parameters to specify the bootchart
+ // timeout. this is useful when using -wipe-data since the /data
+ // partition is fresh.
+ std::string cmdline;
+ android::base::ReadFileToString("/proc/cmdline", &cmdline);
+#define KERNEL_OPTION "androidboot.bootchart="
+ if (strstr(cmdline.c_str(), KERNEL_OPTION) != NULL) {
+ timeout = atoi(cmdline.c_str() + sizeof(KERNEL_OPTION) - 1);
+ }
+ }
+ if (timeout == 0)
+ return 0;
+
+ if (timeout > BOOTCHART_MAX_TIME_SEC)
+ timeout = BOOTCHART_MAX_TIME_SEC;
+
+ int count = (timeout*1000 + BOOTCHART_POLLING_MS-1)/BOOTCHART_POLLING_MS;
+
+ log_stat = fopen(LOG_STAT, "we");
+ if (log_stat == NULL) {
+ return -1;
+ }
+ log_procs = fopen(LOG_PROCS, "we");
+ if (log_procs == NULL) {
+ fclose(log_stat);
+ return -1;
+ }
+ log_disks = fopen(LOG_DISK, "we");
+ if (log_disks == NULL) {
+ fclose(log_stat);
+ fclose(log_procs);
+ return -1;
+ }
+
+ // Create kernel process accounting file.
+ close(open(LOG_ACCT, O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC, 0644));
+ acct(LOG_ACCT);
+
+ log_header();
+ return count;
+}
+
+int do_bootchart_init(int nargs, char** args) {
+ g_remaining_samples = bootchart_init();
+ if (g_remaining_samples < 0) {
+ ERROR("Bootcharting init failure: %s\n", strerror(errno));
+ } else if (g_remaining_samples > 0) {
+ NOTICE("Bootcharting started (will run for %d s).\n",
+ (g_remaining_samples * BOOTCHART_POLLING_MS) / 1000);
+ } else {
+ NOTICE("Not bootcharting.\n");
+ }
+ return 0;
+}
+
+static int bootchart_step() {
+ do_log_file(log_stat, "/proc/stat");
+ do_log_file(log_disks, "/proc/diskstats");
+ do_log_procs(log_procs);
+
+ // Stop if /data/bootchart/stop contains 1.
+ std::string stop;
+ if (android::base::ReadFileToString(LOG_STOPFILE, &stop) && stop == "1") {
+ return -1;
+ }
+
+ return 0;
+}
+
+/* called to get time (in ms) used by bootchart */
+static long long bootchart_gettime() {
+ return 10LL*get_uptime_jiffies();
+}
+
+static void bootchart_finish() {
+ unlink(LOG_STOPFILE);
+ fclose(log_stat);
+ fclose(log_disks);
+ fclose(log_procs);
+ acct(NULL);
+}
+
+void bootchart_sample(int* timeout) {
+ // Do we have any more bootcharting to do?
+ if (g_remaining_samples <= 0) {
+ return;
+ }
+
+ long long current_time = bootchart_gettime();
+ int elapsed_time = current_time - g_last_bootchart_time;
+
+ if (elapsed_time >= BOOTCHART_POLLING_MS) {
+ /* count missed samples */
+ while (elapsed_time >= BOOTCHART_POLLING_MS) {
+ elapsed_time -= BOOTCHART_POLLING_MS;
+ g_remaining_samples--;
+ }
+ /* count may be negative, take a sample anyway */
+ g_last_bootchart_time = current_time;
+ if (bootchart_step() < 0 || g_remaining_samples <= 0) {
+ bootchart_finish();
+ g_remaining_samples = 0;
+ }
+ }
+ if (g_remaining_samples > 0) {
+ int remaining_time = BOOTCHART_POLLING_MS - elapsed_time;
+ if (*timeout < 0 || *timeout > remaining_time) {
+ *timeout = remaining_time;
+ }
+ }
+}
diff --git a/init/bootchart.h b/init/bootchart.h
index 39d2d4f..cf61d83 100644
--- a/init/bootchart.h
+++ b/init/bootchart.h
@@ -17,20 +17,6 @@
#ifndef _BOOTCHART_H
#define _BOOTCHART_H
-#ifndef BOOTCHART
-# define BOOTCHART 0
-#endif
-
-#if BOOTCHART
-
-extern int bootchart_init(void);
-extern int bootchart_step(void);
-extern void bootchart_finish(void);
-
-# define BOOTCHART_POLLING_MS 200 /* polling period in ms */
-# define BOOTCHART_DEFAULT_TIME_SEC (2*60) /* default polling time in seconds */
-# define BOOTCHART_MAX_TIME_SEC (10*60) /* max polling time in seconds */
-
-#endif /* BOOTCHART */
+void bootchart_sample(int* timeout);
#endif /* _BOOTCHART_H */
diff --git a/init/builtins.c b/init/builtins.cpp
index c192551..4567b04 100644
--- a/init/builtins.c
+++ b/init/builtins.cpp
@@ -14,30 +14,32 @@
* limitations under the License.
*/
-#include <sys/types.h>
-#include <sys/stat.h>
+#include <errno.h>
#include <fcntl.h>
-#include <unistd.h>
-#include <string.h>
+#include <net/if.h>
#include <stdio.h>
-#include <linux/kd.h>
-#include <errno.h>
-#include <sys/socket.h>
-#include <netinet/in.h>
-#include <linux/if.h>
-#include <arpa/inet.h>
#include <stdlib.h>
+#include <string.h>
+#include <sys/socket.h>
#include <sys/mount.h>
#include <sys/resource.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/stat.h>
#include <sys/wait.h>
+#include <unistd.h>
#include <linux/loop.h>
-#include <cutils/partition_utils.h>
-#include <cutils/android_reboot.h>
-#include <fs_mgr.h>
+#include <ext4_crypt.h>
#include <selinux/selinux.h>
#include <selinux/label.h>
+#include <fs_mgr.h>
+#include <base/stringprintf.h>
+#include <cutils/partition_utils.h>
+#include <cutils/android_reboot.h>
+#include <private/android_filesystem_config.h>
+
#include "init.h"
#include "keywords.h"
#include "property_service.h"
@@ -46,121 +48,22 @@
#include "util.h"
#include "log.h"
-#include <private/android_filesystem_config.h>
+#define chmod DO_NOT_USE_CHMOD_USE_FCHMODAT_SYMLINK_NOFOLLOW
int add_environment(const char *name, const char *value);
-extern int init_module(void *, unsigned long, const char *);
-
-static int write_file(const char *path, const char *value)
-{
- int fd, ret, len;
-
- fd = open(path, O_WRONLY|O_CREAT|O_NOFOLLOW, 0600);
-
- if (fd < 0)
- return -errno;
-
- len = strlen(value);
-
- do {
- ret = write(fd, value, len);
- } while (ret < 0 && errno == EINTR);
-
- close(fd);
- if (ret < 0) {
- return -errno;
- } else {
- return 0;
- }
-}
-
-static int _open(const char *path)
-{
- int fd;
-
- fd = open(path, O_RDONLY | O_NOFOLLOW);
- if (fd < 0)
- fd = open(path, O_WRONLY | O_NOFOLLOW);
-
- return fd;
-}
-
-static int _chown(const char *path, unsigned int uid, unsigned int gid)
-{
- int fd;
- int ret;
-
- fd = _open(path);
- if (fd < 0) {
- return -1;
- }
-
- ret = fchown(fd, uid, gid);
- if (ret < 0) {
- int errno_copy = errno;
- close(fd);
- errno = errno_copy;
- return -1;
- }
-
- close(fd);
-
- return 0;
-}
-
-static int _chmod(const char *path, mode_t mode)
-{
- int fd;
- int ret;
-
- fd = _open(path);
- if (fd < 0) {
- return -1;
- }
-
- ret = fchmod(fd, mode);
- if (ret < 0) {
- int errno_copy = errno;
- close(fd);
- errno = errno_copy;
- return -1;
- }
-
- close(fd);
-
- return 0;
-}
+// System call provided by bionic but not in any header file.
+extern "C" int init_module(void *, unsigned long, const char *);
static int insmod(const char *filename, char *options)
{
- void *module;
- unsigned size;
- int ret;
-
- module = read_file(filename, &size);
- if (!module)
+ std::string module;
+ if (!read_file(filename, &module)) {
return -1;
+ }
- ret = init_module(module, size, options);
-
- free(module);
-
- return ret;
-}
-
-static int setkey(struct kbentry *kbe)
-{
- int fd, ret;
-
- fd = open("/dev/tty0", O_RDWR | O_SYNC);
- if (fd < 0)
- return -1;
-
- ret = ioctl(fd, KDSKBENT, kbe);
-
- close(fd);
- return ret;
+ // TODO: use finit_module for >= 3.8 kernels.
+ return init_module(&module[0], module.size(), options);
}
static int __ifupdown(const char *interface, int up)
@@ -185,7 +88,7 @@ static int __ifupdown(const char *interface, int up)
ifr.ifr_flags &= ~IFF_UP;
ret = ioctl(s, SIOCSIFFLAGS, &ifr);
-
+
done:
close(s);
return ret;
@@ -200,18 +103,6 @@ static void service_start_if_not_disabled(struct service *svc)
}
}
-int do_chdir(int nargs, char **args)
-{
- chdir(args[1]);
- return 0;
-}
-
-int do_chroot(int nargs, char **args)
-{
- chroot(args[1]);
- return 0;
-}
-
int do_class_start(int nargs, char **args)
{
/* Starting a class does not start services
@@ -254,9 +145,13 @@ 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;
}
int do_export(int nargs, char **args)
@@ -319,7 +214,7 @@ int do_mkdir(int nargs, char **args)
ret = make_dir(args[1], mode);
/* chmod in case the directory already exists */
if (ret == -1 && errno == EEXIST) {
- ret = _chmod(args[1], mode);
+ ret = fchmodat(AT_FDCWD, args[1], mode, AT_SYMLINK_NOFOLLOW);
}
if (ret == -1) {
return -errno;
@@ -333,20 +228,20 @@ int do_mkdir(int nargs, char **args)
gid = decode_uid(args[4]);
}
- if (_chown(args[1], uid, gid) < 0) {
+ if (lchown(args[1], uid, gid) == -1) {
return -errno;
}
/* chown may have cleared S_ISUID and S_ISGID, chmod again */
if (mode & (S_ISUID | S_ISGID)) {
- ret = _chmod(args[1], mode);
+ ret = fchmodat(AT_FDCWD, args[1], mode, AT_SYMLINK_NOFOLLOW);
if (ret == -1) {
return -errno;
}
}
}
- return 0;
+ return e4crypt_set_directory_policy(args[1]);
}
static struct {
@@ -410,7 +305,7 @@ int do_mount(int nargs, char **args)
return -1;
}
- sprintf(tmp, "/dev/block/mtdblock%d", n);
+ snprintf(tmp, sizeof(tmp), "/dev/block/mtdblock%d", n);
if (wait)
wait_for_file(tmp, COMMAND_RETRY_TIMEOUT);
@@ -424,15 +319,16 @@ int do_mount(int nargs, char **args)
struct loop_info info;
mode = (flags & MS_RDONLY) ? O_RDONLY : O_RDWR;
- fd = open(source + 5, mode);
+ fd = open(source + 5, mode | O_CLOEXEC);
if (fd < 0) {
return -1;
}
for (n = 0; ; n++) {
- sprintf(tmp, "/dev/block/loop%d", n);
- loop = open(tmp, mode);
+ snprintf(tmp, sizeof(tmp), "/dev/block/loop%d", n);
+ loop = open(tmp, mode | O_CLOEXEC);
if (loop < 0) {
+ close(fd);
return -1;
}
@@ -476,7 +372,7 @@ exit_success:
static int wipe_data_via_recovery()
{
mkdir("/cache/recovery", 0700);
- int fd = open("/cache/recovery/command", O_RDWR|O_CREAT|O_TRUNC, 0600);
+ int fd = open("/cache/recovery/command", O_RDWR|O_CREAT|O_TRUNC|O_CLOEXEC, 0600);
if (fd >= 0) {
write(fd, "--wipe_data\n", strlen("--wipe_data\n") + 1);
write(fd, "--reason=wipe_data_via_recovery\n", strlen("--reason=wipe_data_via_recovery\n") + 1);
@@ -489,6 +385,17 @@ static int wipe_data_via_recovery()
while (1) { pause(); } // never reached
}
+/*
+ * Callback to make a directory from the ext4 code
+ */
+static int do_mount_alls_make_dir(const char* dir)
+{
+ if (make_dir(dir, 0700) && errno != EEXIST) {
+ return -1;
+ }
+
+ return 0;
+}
/*
* This function might request a reboot, in which case it will
@@ -500,7 +407,6 @@ int do_mount_all(int nargs, char **args)
int ret = -1;
int child_ret = -1;
int status;
- const char *prop;
struct fstab *fstab;
if (nargs != 2) {
@@ -519,7 +425,7 @@ int do_mount_all(int nargs, char **args)
int wp_ret = TEMP_FAILURE_RETRY(waitpid(pid, &status, 0));
if (wp_ret < 0) {
/* Unexpected error code. We will continue anyway. */
- NOTICE("waitpid failed rc=%d, errno=%d\n", wp_ret, errno);
+ NOTICE("waitpid failed rc=%d: %s\n", wp_ret, strerror(errno));
}
if (WIFEXITED(status)) {
@@ -558,6 +464,37 @@ int do_mount_all(int nargs, char **args)
ERROR("fs_mgr_mount_all suggested recovery, so wiping data via recovery.\n");
ret = wipe_data_via_recovery();
/* If reboot worked, there is no return. */
+ } else if (ret == FS_MGR_MNTALL_DEV_DEFAULT_FILE_ENCRYPTED) {
+ // We have to create the key files here. Only init can call make_dir,
+ // and we can't do it from fs_mgr as then fs_mgr would depend on
+ // make_dir creating a circular dependency.
+ fstab = fs_mgr_read_fstab(args[1]);
+ for (int i = 0; i < fstab->num_entries; ++i) {
+ if (fs_mgr_is_file_encrypted(&fstab->recs[i])) {
+ if (e4crypt_create_device_key(fstab->recs[i].mount_point,
+ do_mount_alls_make_dir)) {
+ ERROR("Could not create device key on %s"
+ " - continue unencrypted\n",
+ fstab->recs[i].mount_point);
+ }
+ }
+ }
+ fs_mgr_free_fstab(fstab);
+
+ if (e4crypt_install_keyring()) {
+ return -1;
+ }
+ property_set("ro.crypto.state", "encrypted");
+
+ // Although encrypted, we have device key, so we do not need to
+ // do anything different from the nonencrypted case.
+ action_for_each_trigger("nonencrypted", action_add_queue_tail);
+ } else if (ret == FS_MGR_MNTALL_DEV_NON_DEFAULT_FILE_ENCRYPTED) {
+ if (e4crypt_install_keyring()) {
+ return -1;
+ }
+ property_set("ro.crypto.state", "encrypted");
+ property_set("vold.decrypt", "trigger_restart_min_framework");
} else if (ret > 0) {
ERROR("fs_mgr_mount_all returned unexpected error %d\n", ret);
}
@@ -578,33 +515,6 @@ int do_swapon_all(int nargs, char **args)
return ret;
}
-int do_setcon(int nargs, char **args) {
- if (is_selinux_enabled() <= 0)
- return 0;
- if (setcon(args[1]) < 0) {
- return -errno;
- }
- return 0;
-}
-
-int do_setenforce(int nargs, char **args) {
- if (is_selinux_enabled() <= 0)
- return 0;
- if (security_setenforce(atoi(args[1])) < 0) {
- return -errno;
- }
- return 0;
-}
-
-int do_setkey(int nargs, char **args)
-{
- struct kbentry kbe;
- kbe.kb_table = strtoul(args[1], 0, 0);
- kbe.kb_index = strtoul(args[2], 0, 0);
- kbe.kb_value = strtoul(args[3], 0, 0);
- return setkey(&kbe);
-}
-
int do_setprop(int nargs, char **args)
{
const char *name = args[1];
@@ -667,7 +577,7 @@ int do_powerctl(int nargs, char **args)
int res;
int len = 0;
int cmd = 0;
- char *reboot_target;
+ const char *reboot_target;
res = expand_props(command, args[1], sizeof(command));
if (res) {
@@ -727,25 +637,41 @@ int do_sysclktz(int nargs, char **args)
return -1;
memset(&tz, 0, sizeof(tz));
- tz.tz_minuteswest = atoi(args[1]);
+ tz.tz_minuteswest = atoi(args[1]);
if (settimeofday(NULL, &tz))
return -1;
return 0;
}
+int do_verity_load_state(int nargs, char **args) {
+ int mode = -1;
+ int rc = fs_mgr_load_verity_state(&mode);
+ if (rc == 0 && mode == VERITY_MODE_LOGGING) {
+ action_for_each_trigger("verity-logging", action_add_queue_tail);
+ }
+ return rc;
+}
+
+static void verity_update_property(fstab_rec *fstab, const char *mount_point, int mode, int status) {
+ property_set(android::base::StringPrintf("partition.%s.verified", mount_point).c_str(),
+ android::base::StringPrintf("%d", mode).c_str());
+}
+
+int do_verity_update_state(int nargs, char** args) {
+ return fs_mgr_update_verity_state(verity_update_property);
+}
+
int do_write(int nargs, char **args)
{
const char *path = args[1];
const char *value = args[2];
- char prop_val[PROP_VALUE_MAX];
- int ret;
- ret = expand_props(prop_val, value, sizeof(prop_val));
- if (ret) {
+ char expanded_value[256];
+ if (expand_props(expanded_value, value, sizeof(expanded_value))) {
ERROR("cannot expand '%s' while writing to '%s'\n", value, path);
return -EINVAL;
}
- return write_file(path, prop_val);
+ return write_file(path, expanded_value);
}
int do_copy(int nargs, char **args)
@@ -760,16 +686,16 @@ int do_copy(int nargs, char **args)
if (nargs != 3)
return -1;
- if (stat(args[1], &info) < 0)
+ if (stat(args[1], &info) < 0)
return -1;
- if ((fd1 = open(args[1], O_RDONLY)) < 0)
+ if ((fd1 = open(args[1], O_RDONLY|O_CLOEXEC)) < 0)
goto out_err;
- if ((fd2 = open(args[2], O_WRONLY|O_CREAT|O_TRUNC, 0660)) < 0)
+ if ((fd2 = open(args[2], O_WRONLY|O_CREAT|O_TRUNC|O_CLOEXEC, 0660)) < 0)
goto out_err;
- if (!(buffer = malloc(info.st_size)))
+ if (!(buffer = (char*) malloc(info.st_size)))
goto out_err;
p = buffer;
@@ -813,10 +739,10 @@ out:
int do_chown(int nargs, char **args) {
/* GID is optional. */
if (nargs == 3) {
- if (_chown(args[2], decode_uid(args[1]), -1) < 0)
+ if (lchown(args[2], decode_uid(args[1]), -1) == -1)
return -errno;
} else if (nargs == 4) {
- if (_chown(args[3], decode_uid(args[1]), decode_uid(args[2])) < 0)
+ if (lchown(args[3], decode_uid(args[1]), decode_uid(args[2])) == -1)
return -errno;
} else {
return -1;
@@ -839,7 +765,7 @@ static mode_t get_mode(const char *s) {
int do_chmod(int nargs, char **args) {
mode_t mode = get_mode(args[1]);
- if (_chmod(args[2], mode) < 0) {
+ if (fchmodat(AT_FDCWD, args[2], mode, AT_SYMLINK_NOFOLLOW) < 0) {
return -errno;
}
return 0;
@@ -867,34 +793,6 @@ int do_restorecon_recursive(int nargs, char **args) {
return ret;
}
-int do_setsebool(int nargs, char **args) {
- const char *name = args[1];
- const char *value = args[2];
- SELboolean b;
- int ret;
-
- if (is_selinux_enabled() <= 0)
- return 0;
-
- b.name = name;
- if (!strcmp(value, "1") || !strcasecmp(value, "true") || !strcasecmp(value, "on"))
- b.value = 1;
- else if (!strcmp(value, "0") || !strcasecmp(value, "false") || !strcasecmp(value, "off"))
- b.value = 0;
- else {
- ERROR("setsebool: invalid value %s\n", value);
- return -EINVAL;
- }
-
- if (security_set_boolean_list(1, &b, 0) < 0) {
- ret = -errno;
- ERROR("setsebool: could not set %s to %s\n", name, value);
- return ret;
- }
-
- return 0;
-}
-
int do_loglevel(int nargs, char **args) {
int log_level;
char log_level_str[PROP_VALUE_MAX] = "";
@@ -941,3 +839,12 @@ int do_wait(int nargs, char **args)
} else
return -1;
}
+
+int do_installkey(int nargs, char **args)
+{
+ if (nargs == 2) {
+ return e4crypt_install_key(args[1]);
+ }
+
+ return -1;
+}
diff --git a/init/devices.c b/init/devices.cpp
index 73fe223..4944cec 100644
--- a/init/devices.c
+++ b/init/devices.cpp
@@ -48,12 +48,10 @@
#include "util.h"
#include "log.h"
-#define UNUSED __attribute__((__unused__))
-
#define SYSFS_PREFIX "/sys"
-#define FIRMWARE_DIR1 "/etc/firmware"
-#define FIRMWARE_DIR2 "/vendor/firmware"
-#define FIRMWARE_DIR3 "/firmware/image"
+static const char *firmware_dirs[] = { "/etc/firmware",
+ "/vendor/firmware",
+ "/firmware/image" };
extern struct selabel_handle *sehandle;
@@ -101,7 +99,7 @@ int add_dev_perms(const char *name, const char *attr,
mode_t perm, unsigned int uid, unsigned int gid,
unsigned short prefix,
unsigned short wildcard) {
- struct perm_node *node = calloc(1, sizeof(*node));
+ struct perm_node *node = (perm_node*) calloc(1, sizeof(*node));
if (!node)
return -ENOMEM;
@@ -154,7 +152,7 @@ void fixup_sys_perms(const char *upath)
if ((strlen(upath) + strlen(dp->attr) + 6) > sizeof(buf))
break;
- sprintf(buf,"/sys%s/%s", upath, dp->attr);
+ snprintf(buf, sizeof(buf), "/sys%s/%s", upath, dp->attr);
INFO("fixup %s %d %d 0%o\n", buf, dp->uid, dp->gid, dp->perm);
chown(buf, dp->uid, dp->gid);
chmod(buf, dp->perm);
@@ -191,7 +189,6 @@ static bool perm_path_matches(const char *path, struct perms_ *dp)
static mode_t get_device_perm(const char *path, const char **links,
unsigned *uid, unsigned *gid)
{
- mode_t perm;
struct listnode *node;
struct perm_node *perm_node;
struct perms_ *dp;
@@ -232,7 +229,7 @@ static mode_t get_device_perm(const char *path, const char **links,
}
static void make_device(const char *path,
- const char *upath UNUSED,
+ const char */*upath*/,
int block, int major, int minor,
const char **links)
{
@@ -269,7 +266,6 @@ static void make_device(const char *path,
static void add_platform_device(const char *path)
{
int path_len = strlen(path);
- struct listnode *node;
struct platform_node *bus;
const char *name = path;
@@ -279,18 +275,9 @@ static void add_platform_device(const char *path)
name += 9;
}
- list_for_each_reverse(node, &platform_names) {
- bus = node_to_item(node, struct platform_node, list);
- if ((bus->path_len < path_len) &&
- (path[bus->path_len] == '/') &&
- !strncmp(path, bus->path, bus->path_len))
- /* subdevice of an existing platform, ignore it */
- return;
- }
-
INFO("adding platform device %s (%s)\n", name, path);
- bus = calloc(1, sizeof(struct platform_node));
+ bus = (platform_node*) calloc(1, sizeof(struct platform_node));
bus->path = strdup(path);
bus->path_len = path_len;
bus->name = bus->path + (name - path);
@@ -367,24 +354,6 @@ static int find_pci_device_prefix(const char *path, char *buf, ssize_t buf_sz)
return 0;
}
-#if LOG_UEVENTS
-
-static inline suseconds_t get_usecs(void)
-{
- struct timeval tv;
- gettimeofday(&tv, 0);
- return tv.tv_sec * (suseconds_t) 1000000 + tv.tv_usec;
-}
-
-#define log_event_print(x...) INFO(x)
-
-#else
-
-#define log_event_print(fmt, args...) do { } while (0)
-#define get_usecs() 0
-
-#endif
-
static void parse_event(const char *msg, struct uevent *uevent)
{
uevent->action = "";
@@ -433,9 +402,11 @@ static void parse_event(const char *msg, struct uevent *uevent)
;
}
- log_event_print("event { '%s', '%s', '%s', '%s', %d, %d }\n",
- uevent->action, uevent->path, uevent->subsystem,
- uevent->firmware, uevent->major, uevent->minor);
+ if (LOG_UEVENTS) {
+ INFO("event { '%s', '%s', '%s', '%s', %d, %d }\n",
+ uevent->action, uevent->path, uevent->subsystem,
+ uevent->firmware, uevent->major, uevent->minor);
+ }
}
static char **get_character_device_symlinks(struct uevent *uevent)
@@ -451,14 +422,14 @@ static char **get_character_device_symlinks(struct uevent *uevent)
if (!pdev)
return NULL;
- links = malloc(sizeof(char *) * 2);
+ links = (char**) malloc(sizeof(char *) * 2);
if (!links)
return NULL;
memset(links, 0, sizeof(char *) * 2);
/* skip "/devices/platform/<driver>" */
parent = strchr(uevent->path + pdev->path_len, '/');
- if (!*parent)
+ if (!parent)
goto err;
if (!strncmp(parent, "/usb", 4)) {
@@ -497,15 +468,10 @@ static char **get_block_device_symlinks(struct uevent *uevent)
struct platform_node *pdev;
char *slash;
const char *type;
- int width;
char buf[256];
char link_path[256];
- int fd;
int link_num = 0;
- int ret;
char *p;
- unsigned int size;
- struct stat info;
pdev = find_platform_device(uevent->path);
if (pdev) {
@@ -518,7 +484,7 @@ static char **get_block_device_symlinks(struct uevent *uevent)
return NULL;
}
- char **links = malloc(sizeof(char *) * 4);
+ char **links = (char**) malloc(sizeof(char *) * 4);
if (!links)
return NULL;
memset(links, 0, sizeof(char *) * 4);
@@ -667,14 +633,9 @@ static void mkdir_recursive_for_devpath(const char *devpath)
mkdir_recursive(dir, 0755);
}
-static inline void __attribute__((__deprecated__)) kernel_logger()
-{
- INFO("kernel logger is deprecated\n");
-}
-
static void handle_generic_device_event(struct uevent *uevent)
{
- char *base;
+ const char *base;
const char *name;
char devpath[DEVPATH_LEN] = {0};
char **links = NULL;
@@ -758,7 +719,7 @@ static void handle_generic_device_event(struct uevent *uevent)
make_dir(base, 0755);
} else if(!strncmp(uevent->subsystem, "misc", 4) &&
!strncmp(name, "log_", 4)) {
- kernel_logger();
+ INFO("kernel logger is deprecated\n");
base = "/dev/log/";
make_dir(base, 0755);
name += 4;
@@ -840,8 +801,9 @@ static int is_booting(void)
static void process_firmware_event(struct uevent *uevent)
{
- char *root, *loading, *data, *file1 = NULL, *file2 = NULL, *file3 = NULL;
+ char *root, *loading, *data;
int l, loading_fd, data_fd, fw_fd;
+ size_t i;
int booting = is_booting();
INFO("firmware: loading '%s' for '%s'\n",
@@ -859,62 +821,49 @@ static void process_firmware_event(struct uevent *uevent)
if (l == -1)
goto loading_free_out;
- l = asprintf(&file1, FIRMWARE_DIR1"/%s", uevent->firmware);
- if (l == -1)
- goto data_free_out;
-
- l = asprintf(&file2, FIRMWARE_DIR2"/%s", uevent->firmware);
- if (l == -1)
- goto data_free_out;
-
- l = asprintf(&file3, FIRMWARE_DIR3"/%s", uevent->firmware);
- if (l == -1)
- goto data_free_out;
-
- loading_fd = open(loading, O_WRONLY);
+ loading_fd = open(loading, O_WRONLY|O_CLOEXEC);
if(loading_fd < 0)
- goto file_free_out;
+ goto data_free_out;
- data_fd = open(data, O_WRONLY);
+ data_fd = open(data, O_WRONLY|O_CLOEXEC);
if(data_fd < 0)
goto loading_close_out;
try_loading_again:
- fw_fd = open(file1, O_RDONLY);
- if(fw_fd < 0) {
- fw_fd = open(file2, O_RDONLY);
- if (fw_fd < 0) {
- fw_fd = open(file3, O_RDONLY);
- if (fw_fd < 0) {
- if (booting) {
- /* If we're not fully booted, we may be missing
- * filesystems needed for firmware, wait and retry.
- */
- usleep(100000);
- booting = is_booting();
- goto try_loading_again;
- }
- INFO("firmware: could not open '%s' %d\n", uevent->firmware, errno);
- write(loading_fd, "-1", 2);
- goto data_close_out;
- }
+ for (i = 0; i < ARRAY_SIZE(firmware_dirs); i++) {
+ char *file = NULL;
+ l = asprintf(&file, "%s/%s", firmware_dirs[i], uevent->firmware);
+ if (l == -1)
+ goto data_free_out;
+ fw_fd = open(file, O_RDONLY|O_CLOEXEC);
+ free(file);
+ if (fw_fd >= 0) {
+ if(!load_firmware(fw_fd, loading_fd, data_fd))
+ INFO("firmware: copy success { '%s', '%s' }\n", root, uevent->firmware);
+ else
+ INFO("firmware: copy failure { '%s', '%s' }\n", root, uevent->firmware);
+ break;
}
}
-
- if(!load_firmware(fw_fd, loading_fd, data_fd))
- INFO("firmware: copy success { '%s', '%s' }\n", root, uevent->firmware);
- else
- INFO("firmware: copy failure { '%s', '%s' }\n", root, uevent->firmware);
+ if (fw_fd < 0) {
+ if (booting) {
+ /* If we're not fully booted, we may be missing
+ * filesystems needed for firmware, wait and retry.
+ */
+ usleep(100000);
+ booting = is_booting();
+ goto try_loading_again;
+ }
+ INFO("firmware: could not open '%s': %s\n", uevent->firmware, strerror(errno));
+ write(loading_fd, "-1", 2);
+ goto data_close_out;
+ }
close(fw_fd);
data_close_out:
close(data_fd);
loading_close_out:
close(loading_fd);
-file_free_out:
- free(file1);
- free(file2);
- free(file3);
data_free_out:
free(data);
loading_free_out:
@@ -926,7 +875,6 @@ root_free_out:
static void handle_firmware_event(struct uevent *uevent)
{
pid_t pid;
- int ret;
if(strcmp(uevent->subsystem, "firmware"))
return;
@@ -938,7 +886,9 @@ static void handle_firmware_event(struct uevent *uevent)
pid = fork();
if (!pid) {
process_firmware_event(uevent);
- exit(EXIT_SUCCESS);
+ _exit(EXIT_SUCCESS);
+ } else if (pid < 0) {
+ ERROR("could not fork to process firmware event: %s\n", strerror(errno));
}
}
@@ -977,7 +927,7 @@ void handle_device_fd()
**
** We drain any pending events from the netlink socket every time
** we poke another uevent file to make sure we don't overrun the
-** socket's buffer.
+** socket's buffer.
*/
static void do_coldboot(DIR *d)
@@ -1023,12 +973,7 @@ static void coldboot(const char *path)
}
}
-void device_init(void)
-{
- suseconds_t t0, t1;
- struct stat info;
- int fd;
-
+void device_init() {
sehandle = NULL;
if (is_selinux_enabled() > 0) {
sehandle = selinux_android_file_context_handle();
@@ -1037,24 +982,22 @@ void device_init(void)
/* is 256K enough? udev uses 16MB! */
device_fd = uevent_open_socket(256*1024, true);
- if(device_fd < 0)
+ if (device_fd == -1) {
return;
-
- fcntl(device_fd, F_SETFD, FD_CLOEXEC);
+ }
fcntl(device_fd, F_SETFL, O_NONBLOCK);
- if (stat(coldboot_done, &info) < 0) {
- t0 = get_usecs();
- coldboot("/sys/class");
- coldboot("/sys/block");
- coldboot("/sys/devices");
- t1 = get_usecs();
- fd = open(coldboot_done, O_WRONLY|O_CREAT, 0000);
- close(fd);
- log_event_print("coldboot %ld uS\n", ((long) (t1 - t0)));
- } else {
- log_event_print("skipping coldboot, already done\n");
+ if (access(COLDBOOT_DONE, F_OK) == 0) {
+ NOTICE("Skipping coldboot, already done!\n");
+ return;
}
+
+ Timer t;
+ coldboot("/sys/class");
+ coldboot("/sys/block");
+ coldboot("/sys/devices");
+ close(open(COLDBOOT_DONE, O_WRONLY|O_CREAT|O_CLOEXEC, 0000));
+ NOTICE("Coldboot took %.2fs.\n", t.duration());
}
int get_device_fd()
diff --git a/init/devices.h b/init/devices.h
index 5d0fe88..6cb0a77 100644
--- a/init/devices.h
+++ b/init/devices.h
@@ -26,4 +26,5 @@ extern int add_dev_perms(const char *name, const char *attr,
unsigned int gid, unsigned short prefix,
unsigned short wildcard);
int get_device_fd();
+
#endif /* _INIT_DEVICES_H */
diff --git a/init/grab-bootchart.sh b/init/grab-bootchart.sh
index 7fe8904..d6082aa 100755
--- a/init/grab-bootchart.sh
+++ b/init/grab-bootchart.sh
@@ -1,10 +1,9 @@
#!/bin/sh
#
-# this script is used to retrieve the bootchart log generated
-# by init when compiled with INIT_BOOTCHART=true.
-#
-# for all details, see //device/system/init/README.BOOTCHART
-#
+# This script is used to retrieve a bootchart log generated by init.
+# All options are passed to adb, for better or for worse.
+# See the readme in this directory for more on bootcharting.
+
TMPDIR=/tmp/android-bootchart
rm -rf $TMPDIR
mkdir -p $TMPDIR
@@ -15,8 +14,9 @@ TARBALL=bootchart.tgz
FILES="header proc_stat.log proc_ps.log proc_diskstats.log kernel_pacct"
for f in $FILES; do
- adb pull $LOGROOT/$f $TMPDIR/$f 2>&1 > /dev/null
+ adb "${@}" pull $LOGROOT/$f $TMPDIR/$f 2>&1 > /dev/null
done
(cd $TMPDIR && tar -czf $TARBALL $FILES)
-cp -f $TMPDIR/$TARBALL ./$TARBALL
-echo "look at $TARBALL"
+bootchart ${TMPDIR}/${TARBALL}
+gnome-open ${TARBALL%.tgz}.png
+echo "Clean up ${TMPDIR}/ and ./${TARBALL%.tgz}.png when done"
diff --git a/init/init.c b/init/init.cpp
index bd1db7a..dd74538 100644
--- a/init/init.c
+++ b/init/init.cpp
@@ -14,37 +14,43 @@
* limitations under the License.
*/
+#include <ctype.h>
+#include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <libgen.h>
+#include <paths.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/epoll.h>
#include <sys/mount.h>
+#include <sys/socket.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>
+#include <sys/wait.h>
+#include <termios.h>
+#include <unistd.h>
+
+#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 <base/file.h>
+#include <base/stringprintf.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 <memory>
#include "devices.h"
#include "init.h"
@@ -63,29 +69,10 @@ struct selabel_handle *sehandle_prop;
static int property_triggers_enabled = 0;
-#if BOOTCHART
-static int bootchart_count;
-#endif
-
-static char console[32];
-static char bootmode[32];
-static char hardware[32];
-static unsigned revision = 0;
static char qemu[32];
static struct action *cur_action = NULL;
static struct command *cur_command = NULL;
-static struct listnode *command_queue = 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";
@@ -93,6 +80,40 @@ static time_t process_needs_restart;
static const char *ENV[32];
+bool waiting_for_exec = false;
+
+static int epoll_fd = -1;
+
+void register_epoll_handler(int fd, void (*fn)()) {
+ epoll_event ev;
+ ev.events = EPOLLIN;
+ ev.data.ptr = reinterpret_cast<void*>(fn);
+ if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fd, &ev) == -1) {
+ ERROR("epoll_ctl failed: %s\n", strerror(errno));
+ }
+}
+
+void service::NotifyStateChange(const char* new_state) {
+ if (!properties_initialized()) {
+ // If properties aren't available yet, we can't set them.
+ 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)
{
@@ -113,9 +134,8 @@ int add_environment(const char *key, const char *val)
/* Add entry if a free slot is available */
if (ENV[n] == NULL) {
- size_t len = key_len + strlen(val) + 2;
- char *entry = malloc(len);
- snprintf(entry, len, "%s=%s", key, val);
+ char* entry;
+ asprintf(&entry, "%s=%s", key, val);
ENV[n] = entry;
return 0;
}
@@ -126,7 +146,7 @@ int add_environment(const char *key, const char *val)
return -1;
}
-static void zap_stdio(void)
+void zap_stdio(void)
{
int fd;
fd = open("/dev/null", O_RDWR);
@@ -166,36 +186,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;
- int n;
- 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;
@@ -209,6 +219,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);
@@ -220,7 +231,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;
@@ -246,10 +257,9 @@ void service_start(struct service *svc, const char *dynamic_args)
}
}
- NOTICE("starting '%s'\n", svc->name);
-
- pid = fork();
+ NOTICE("Starting service '%s'...\n", svc->name);
+ pid_t pid = fork();
if (pid == 0) {
struct socketinfo *si;
struct svcenvinfo *ei;
@@ -257,9 +267,9 @@ void service_start(struct service *svc, const char *dynamic_args)
int fd, sz;
umask(077);
- if (properties_inited()) {
+ if (properties_initialized()) {
get_property_workspace(&fd, &sz);
- sprintf(tmp, "%d,%d", dup(fd), sz);
+ snprintf(tmp, sizeof(tmp), "%d,%d", dup(fd), sz);
add_environment("ANDROID_PROPERTY_WORKSPACE", tmp);
}
@@ -294,18 +304,18 @@ void service_start(struct service *svc, const char *dynamic_args)
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]);
+ if (false) {
+ for (size_t n = 0; svc->args[n]; n++) {
+ INFO("args[%zu] = '%s'\n", n, svc->args[n]);
+ }
+ for (size_t n = 0; ENV[n]; n++) {
+ INFO("env[%zu] = '%s'\n", n, ENV[n]);
+ }
}
-#endif
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));
@@ -350,7 +360,7 @@ void service_start(struct service *svc, const char *dynamic_args)
if (arg_idx == INIT_PARSER_MAXARGS)
break;
}
- arg_ptrs[arg_idx] = '\0';
+ arg_ptrs[arg_idx] = NULL;
execve(svc->args[0], (char**) arg_ptrs, (char**) ENV);
}
_exit(127);
@@ -368,8 +378,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+%zu 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 */
@@ -393,11 +408,11 @@ static void service_stop_or_reset(struct service *svc, int how)
}
if (svc->pid) {
- NOTICE("service '%s' is being killed\n", svc->name);
+ 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");
}
}
@@ -541,47 +556,74 @@ static int is_last_command(struct action *act, struct command *cmd)
return (list_tail(&act->commands) == &cmd->clist);
}
-void execute_one_command(void)
-{
- int ret, i;
+
+void build_triggers_string(char *name_str, int length, struct action *cur_action) {
+ struct listnode *node;
+ struct trigger *cur_trigger;
+
+ list_for_each(node, &cur_action->triggers) {
+ cur_trigger = node_to_item(node, struct trigger, nlist);
+ if (node != cur_action->triggers.next) {
+ strlcat(name_str, " " , length);
+ }
+ strlcat(name_str, cur_trigger->name , length);
+ }
+}
+
+void execute_one_command() {
+ Timer t;
+
char cmd_str[256] = "";
+ char name_str[256] = "";
if (!cur_action || !cur_command || is_last_command(cur_action, cur_command)) {
cur_action = action_remove_queue_head();
cur_command = NULL;
- if (!cur_action)
+ if (!cur_action) {
return;
- INFO("processing action %p (%s)\n", cur_action, cur_action->name);
+ }
+
+ build_triggers_string(name_str, sizeof(name_str), cur_action);
+
+ INFO("processing action %p (%s)\n", cur_action, name_str);
cur_command = get_first_command(cur_action);
} else {
cur_command = get_next_command(cur_action, cur_command);
}
- if (!cur_command)
+ if (!cur_command) {
return;
+ }
- ret = cur_command->func(cur_command->nargs, cur_command->args);
+ int result = cur_command->func(cur_command->nargs, cur_command->args);
if (klog_get_level() >= KLOG_INFO_LEVEL) {
- for (i = 0; i < cur_command->nargs; i++) {
+ for (int 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);
+ char source[256];
+ if (cur_command->filename) {
+ snprintf(source, sizeof(source), " (%s:%d)", cur_command->filename, cur_command->line);
+ } else {
+ *source = '\0';
+ }
+ INFO("Command '%s' action=%s%s returned %d took %.2fs\n",
+ cmd_str, cur_action ? name_str : "", source, result, t.duration());
}
}
-static int wait_for_coldboot_done_action(int nargs, char **args)
-{
- int ret;
- INFO("wait for %s\n", coldboot_done);
- ret = wait_for_file(coldboot_done, COMMAND_RETRY_TIMEOUT);
- if (ret)
- ERROR("Timed out waiting for %s\n", coldboot_done);
- return ret;
+static int wait_for_coldboot_done_action(int nargs, char **args) {
+ Timer t;
+
+ NOTICE("Waiting for %s...\n", COLDBOOT_DONE);
+ if (wait_for_file(COLDBOOT_DONE, COMMAND_RETRY_TIMEOUT)) {
+ ERROR("Timed out waiting for %s\n", COLDBOOT_DONE);
+ }
+
+ NOTICE("Waiting for %s took %.2fs.\n", COLDBOOT_DONE, t.duration());
+ return 0;
}
/*
@@ -609,7 +651,7 @@ static int mix_hwrng_into_linux_rng_action(int nargs, char **args)
size_t total_bytes_written = 0;
hwrandom_fd = TEMP_FAILURE_RETRY(
- open("/dev/hw_random", O_RDONLY | O_NOFOLLOW));
+ open("/dev/hw_random", O_RDONLY | O_NOFOLLOW | O_CLOEXEC));
if (hwrandom_fd == -1) {
if (errno == ENOENT) {
ERROR("/dev/hw_random not found\n");
@@ -622,7 +664,7 @@ static int mix_hwrng_into_linux_rng_action(int nargs, char **args)
}
urandom_fd = TEMP_FAILURE_RETRY(
- open("/dev/urandom", O_WRONLY | O_NOFOLLOW));
+ open("/dev/urandom", O_WRONLY | O_NOFOLLOW | O_CLOEXEC));
if (urandom_fd == -1) {
ERROR("Failed to open /dev/urandom: %s\n", strerror(errno));
goto ret;
@@ -658,7 +700,6 @@ ret:
if (urandom_fd != -1) {
close(urandom_fd);
}
- memset(buf, 0, sizeof(buf));
return result;
}
@@ -670,18 +711,17 @@ static int keychord_init_action(int nargs, char **args)
static int console_init_action(int nargs, char **args)
{
- int fd;
-
- if (console[0]) {
+ char console[PROP_VALUE_MAX];
+ if (property_get("ro.boot.console", console) > 0) {
snprintf(console_name, sizeof(console_name), "/dev/%s", console);
}
- fd = open(console_name, O_RDWR);
+ int fd = open(console_name, O_RDWR | O_CLOEXEC);
if (fd >= 0)
have_console = 1;
close(fd);
- fd = open("/dev/tty0", O_WRONLY);
+ fd = open("/dev/tty0", O_WRONLY | O_CLOEXEC);
if (fd >= 0) {
const char *msg;
msg = "\n"
@@ -706,7 +746,7 @@ static int console_init_action(int nargs, char **args)
return 0;
}
-static void import_kernel_nv(char *name, int for_emulator)
+static void import_kernel_nv(char *name, bool for_emulator)
{
char *value = strchr(name, '=');
int name_len = strlen(name);
@@ -739,55 +779,56 @@ static void import_kernel_nv(char *name, int for_emulator)
}
}
-static void export_kernel_boot_props(void)
-{
- char tmp[PROP_VALUE_MAX];
- int ret;
- unsigned i;
+static void export_kernel_boot_props() {
struct {
const char *src_prop;
- const char *dest_prop;
- const char *def_val;
+ const char *dst_prop;
+ const char *default_value;
} prop_map[] = {
- { "ro.boot.serialno", "ro.serialno", "", },
- { "ro.boot.mode", "ro.bootmode", "unknown", },
- { "ro.boot.baseband", "ro.baseband", "unknown", },
+ { "ro.boot.serialno", "ro.serialno", "", },
+ { "ro.boot.mode", "ro.bootmode", "unknown", },
+ { "ro.boot.baseband", "ro.baseband", "unknown", },
{ "ro.boot.bootloader", "ro.bootloader", "unknown", },
+ { "ro.boot.hardware", "ro.hardware", "unknown", },
+ { "ro.boot.revision", "ro.revision", "0", },
};
+ for (size_t i = 0; i < ARRAY_SIZE(prop_map); i++) {
+ char value[PROP_VALUE_MAX];
+ int rc = property_get(prop_map[i].src_prop, value);
+ property_set(prop_map[i].dst_prop, (rc > 0) ? value : prop_map[i].default_value);
+ }
+}
- for (i = 0; i < ARRAY_SIZE(prop_map); i++) {
- ret = property_get(prop_map[i].src_prop, tmp);
- if (ret > 0)
- property_set(prop_map[i].dest_prop, tmp);
- else
- property_set(prop_map[i].dest_prop, prop_map[i].def_val);
+static void process_kernel_dt(void)
+{
+ static const char android_dir[] = "/proc/device-tree/firmware/android";
+
+ std::string file_name = android::base::StringPrintf("%s/compatible", android_dir);
+
+ std::string dt_file;
+ android::base::ReadFileToString(file_name, &dt_file);
+ if (!dt_file.compare("android,firmware")) {
+ ERROR("firmware/android is not compatible with 'android,firmware'\n");
+ return;
}
- ret = property_get("ro.boot.console", tmp);
- if (ret)
- strlcpy(console, tmp, sizeof(console));
-
- /* save a copy for init's usage during boot */
- property_get("ro.bootmode", tmp);
- strlcpy(bootmode, tmp, sizeof(bootmode));
-
- /* if this was given on kernel command line, override what we read
- * before (e.g. from /proc/cpuinfo), if anything */
- ret = property_get("ro.boot.hardware", tmp);
- if (ret)
- strlcpy(hardware, tmp, sizeof(hardware));
- property_set("ro.hardware", hardware);
-
- snprintf(tmp, PROP_VALUE_MAX, "%d", revision);
- property_set("ro.revision", tmp);
-
- /* TODO: these are obsolete. We should delete them */
- 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");
+ std::unique_ptr<DIR, int(*)(DIR*)>dir(opendir(android_dir), closedir);
+ if (!dir)
+ return;
+
+ struct dirent *dp;
+ while ((dp = readdir(dir.get())) != NULL) {
+ if (dp->d_type != DT_REG || !strcmp(dp->d_name, "compatible"))
+ continue;
+
+ file_name = android::base::StringPrintf("%s/%s", android_dir, dp->d_name);
+
+ android::base::ReadFileToString(file_name, &dt_file);
+ std::replace(dt_file.begin(), dt_file.end(), ',', '.');
+
+ std::string property_name = android::base::StringPrintf("ro.boot.%s", dp->d_name);
+ property_set(property_name.c_str(), dt_file.c_str());
+ }
}
static void process_kernel_cmdline(void)
@@ -799,40 +840,9 @@ static void process_kernel_cmdline(void)
* second pass is only necessary for qemu to export all kernel params
* as props.
*/
- import_kernel_cmdline(0, import_kernel_nv);
+ import_kernel_cmdline(false, import_kernel_nv);
if (qemu[0])
- import_kernel_cmdline(1, import_kernel_nv);
-
- /* 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();
-}
-
-static int property_service_init_action(int nargs, char **args)
-{
- /* read any property files on system or data and
- * fire up the property service. This must happen
- * after the ro.foo properties are set above so
- * that /data/local.prop cannot interfere with them.
- */
- start_property_service();
- if (get_property_set_fd() < 0) {
- ERROR("start_property_service() failed\n");
- exit(1);
- }
-
- return 0;
-}
-
-static int signal_init_action(int nargs, char **args)
-{
- signal_init();
- if (get_signal_fd() < 0) {
- ERROR("signal_init() failed\n");
- exit(1);
- }
- return 0;
+ import_kernel_cmdline(true, import_kernel_nv);
}
static int queue_property_triggers_action(int nargs, char **args)
@@ -843,90 +853,55 @@ static int queue_property_triggers_action(int nargs, char **args)
return 0;
}
-#if BOOTCHART
-static int bootchart_init_action(int nargs, char **args)
+static void selinux_init_all_handles(void)
{
- 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");
- }
-
- return 0;
+ sehandle = selinux_android_file_context_handle();
+ selinux_android_set_sehandle(sehandle);
+ sehandle_prop = selinux_android_prop_context_handle();
}
-#endif
-static const struct selinux_opt seopts_prop[] = {
- { SELABEL_OPT_PATH, "/property_contexts" },
- { SELABEL_OPT_PATH, "/data/security/current/property_contexts" },
- { 0, NULL }
-};
+enum selinux_enforcing_status { SELINUX_DISABLED, SELINUX_PERMISSIVE, SELINUX_ENFORCING };
-struct selabel_handle* selinux_android_prop_context_handle(void)
-{
- 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);
- if (!sehandle) {
- ERROR("SELinux: Could not load property_contexts: %s\n",
- strerror(errno));
- return NULL;
- }
- INFO("SELinux: Loaded property contexts from %s\n", seopts_prop[policy_index].value);
- return sehandle;
-}
+static selinux_enforcing_status selinux_status_from_cmdline() {
+ selinux_enforcing_status status = SELINUX_ENFORCING;
-void selinux_init_all_handles(void)
-{
- sehandle = selinux_android_file_context_handle();
- selinux_android_set_sehandle(sehandle);
- sehandle_prop = selinux_android_prop_context_handle();
+ std::function<void(char*,bool)> fn = [&](char* name, bool in_qemu) {
+ char *value = strchr(name, '=');
+ if (value == nullptr) { return; }
+ *value++ = '\0';
+ if (strcmp(name, "androidboot.selinux") == 0) {
+ if (strcmp(value, "disabled") == 0) {
+ status = SELINUX_DISABLED;
+ } else if (strcmp(value, "permissive") == 0) {
+ status = SELINUX_PERMISSIVE;
+ }
+ }
+ };
+ import_kernel_cmdline(false, fn);
+
+ return status;
}
+
static bool selinux_is_disabled(void)
{
-#ifdef ALLOW_DISABLE_SELINUX
- 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;
+ if (ALLOW_DISABLE_SELINUX) {
+ 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;
+ }
+ return selinux_status_from_cmdline() == SELINUX_DISABLED;
}
-#endif
return false;
}
static bool selinux_is_enforcing(void)
{
-#ifdef ALLOW_DISABLE_SELINUX
- char tmp[PROP_VALUE_MAX];
-
- if (property_get("ro.boot.selinux", tmp) == 0) {
- /* Property is not set. Assume enforcing */
- return true;
- }
-
- if (strcmp(tmp, "permissive") == 0) {
- /* SELinux is in the kernel, but we've been told to go into permissive mode */
- return false;
- }
-
- if (strcmp(tmp, "enforcing") != 0) {
- ERROR("SELinux: Unknown value of ro.boot.selinux. Got: \"%s\". Assuming enforcing.\n", tmp);
+ if (ALLOW_DISABLE_SELINUX) {
+ return selinux_status_from_cmdline() == SELINUX_ENFORCING;
}
-
-#endif
return true;
}
@@ -952,223 +927,196 @@ int selinux_reload_policy(void)
return 0;
}
-static int audit_callback(void *data, security_class_t cls __attribute__((unused)), char *buf, size_t len)
-{
+static int audit_callback(void *data, security_class_t /*cls*/, char *buf, size_t len) {
snprintf(buf, len, "property=%s", !data ? "NULL" : (char *)data);
return 0;
}
-int log_callback(int type, const char *fmt, ...)
-{
- 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;
+static void security_failure() {
+ ERROR("Security failure; rebooting into recovery mode...\n");
+ android_reboot(ANDROID_RB_RESTART2, 0, "recovery");
+ while (true) { pause(); } // never reached
}
-static void selinux_initialize(void)
-{
+static void selinux_initialize(bool in_kernel_domain) {
+ Timer t;
+
+ selinux_callback cb;
+ cb.func_log = selinux_klog_callback;
+ selinux_set_callback(SELINUX_CB_LOG, cb);
+ cb.func_audit = audit_callback;
+ selinux_set_callback(SELINUX_CB_AUDIT, cb);
+
if (selinux_is_disabled()) {
return;
}
- INFO("loading selinux policy\n");
- if (selinux_android_load_policy() < 0) {
- ERROR("SELinux: Failed to load policy; rebooting into recovery mode\n");
- android_reboot(ANDROID_RB_RESTART2, 0, "recovery");
- while (1) { pause(); } // never reached
- }
+ if (in_kernel_domain) {
+ INFO("Loading SELinux policy...\n");
+ if (selinux_android_load_policy() < 0) {
+ ERROR("failed to load policy: %s\n", strerror(errno));
+ security_failure();
+ }
- selinux_init_all_handles();
- bool is_enforcing = selinux_is_enforcing();
- INFO("SELinux: security_setenforce(%d)\n", is_enforcing);
- security_setenforce(is_enforcing);
+ bool is_enforcing = selinux_is_enforcing();
+ security_setenforce(is_enforcing);
+
+ if (write_file("/sys/fs/selinux/checkreqprot", "0") == -1) {
+ security_failure();
+ }
+
+ NOTICE("(Initializing SELinux %s took %.2fs.)\n",
+ is_enforcing ? "enforcing" : "non-enforcing", t.duration());
+ } else {
+ selinux_init_all_handles();
+ }
}
-int main(int argc, char **argv)
-{
- int fd_count = 0;
- struct pollfd ufds[4];
- char *tmpdev;
- char* debuggable;
- char tmp[32];
- int property_set_fd_init = 0;
- int signal_fd_init = 0;
- int keychord_fd_init = 0;
- bool is_charger = false;
-
- if (!strcmp(basename(argv[0]), "ueventd"))
+int main(int argc, char** argv) {
+ if (!strcmp(basename(argv[0]), "ueventd")) {
return ueventd_main(argc, argv);
+ }
- if (!strcmp(basename(argv[0]), "watchdogd"))
+ 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.
- */
- mkdir("/dev", 0755);
- mkdir("/proc", 0755);
- mkdir("/sys", 0755);
-
- mount("tmpfs", "/dev", "tmpfs", MS_NOSUID, "mode=0755");
- mkdir("/dev/pts", 0755);
- mkdir("/dev/socket", 0755);
- mount("devpts", "/dev/pts", "devpts", 0, NULL);
- mount("proc", "/proc", "proc", 0, NULL);
- mount("sysfs", "/sys", "sysfs", 0, NULL);
-
- /* indicate that booting is in progress to background fw loaders, etc */
- close(open("/dev/.booting", O_WRONLY | O_CREAT, 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.
- */
+ add_environment("PATH", _PATH_DEFPATH);
+
+ bool is_first_stage = (argc == 1) || (strcmp(argv[1], "--second-stage") != 0);
+
+ // Get the basic filesystem setup we need put together in the initramdisk
+ // on / and then we'll let the rc file figure out the rest.
+ if (is_first_stage) {
+ mount("tmpfs", "/dev", "tmpfs", MS_NOSUID, "mode=0755");
+ mkdir("/dev/pts", 0755);
+ mkdir("/dev/socket", 0755);
+ mount("devpts", "/dev/pts", "devpts", 0, NULL);
+ mount("proc", "/proc", "proc", 0, NULL);
+ mount("sysfs", "/sys", "sysfs", 0, NULL);
+ }
+
+ // We must have some place other than / to create the device nodes for
+ // kmsg and null, otherwise we won't be able to remount / read-only
+ // later on. Now that tmpfs is mounted on /dev, we can actually talk
+ // to the outside world.
open_devnull_stdio();
klog_init();
- property_init();
+ klog_set_level(KLOG_NOTICE_LEVEL);
- get_hardware_name(hardware, &revision);
+ NOTICE("init%s started!\n", is_first_stage ? "" : " second stage");
- process_kernel_cmdline();
+ if (!is_first_stage) {
+ // Indicate that booting is in progress to background fw loaders, etc.
+ close(open("/dev/.booting", O_WRONLY | O_CREAT | O_CLOEXEC, 0000));
- union selinux_callback cb;
- cb.func_log = log_callback;
- selinux_set_callback(SELINUX_CB_LOG, cb);
+ property_init();
- cb.func_audit = audit_callback;
- selinux_set_callback(SELINUX_CB_AUDIT, cb);
+ // If arguments are passed both on the command line and in DT,
+ // properties set in DT always have priority over the command-line ones.
+ process_kernel_dt();
+ process_kernel_cmdline();
- 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.
- */
+ // Propogate the kernel variables to internal variables
+ // used by init as well as the current required properties.
+ export_kernel_boot_props();
+ }
+
+ // Set up SELinux, including loading the SELinux policy if we're in the kernel domain.
+ selinux_initialize(is_first_stage);
+
+ // If we're in the kernel domain, re-exec init to transition to the init domain now
+ // that the SELinux policy has been loaded.
+ if (is_first_stage) {
+ if (restorecon("/init") == -1) {
+ ERROR("restorecon failed: %s\n", strerror(errno));
+ security_failure();
+ }
+ char* path = argv[0];
+ char* args[] = { path, const_cast<char*>("--second-stage"), nullptr };
+ if (execv(path, args) == -1) {
+ ERROR("execv(\"%s\") failed: %s\n", path, strerror(errno));
+ security_failure();
+ }
+ }
+
+ // These directories were necessarily created before initial policy load
+ // and therefore need their security context restored to the proper value.
+ // This must happen before /dev is populated by ueventd.
+ INFO("Running restorecon...\n");
restorecon("/dev");
restorecon("/dev/socket");
restorecon("/dev/__properties__");
restorecon_recursive("/sys");
- is_charger = !strcmp(bootmode, "charger");
+ epoll_fd = epoll_create1(EPOLL_CLOEXEC);
+ if (epoll_fd == -1) {
+ ERROR("epoll_create1 failed: %s\n", strerror(errno));
+ exit(1);
+ }
+
+ signal_handler_init();
- INFO("property init\n");
property_load_boot_defaults();
+ start_property_service();
- INFO("reading config file\n");
init_parse_config_file("/init.rc");
action_for_each_trigger("early-init", action_add_queue_tail);
+ // Queue an action that waits for coldboot done so we know ueventd has set up all of /dev...
queue_builtin_action(wait_for_coldboot_done_action, "wait_for_coldboot_done");
+ // ... so that we can start queuing up actions that require stuff from /dev.
queue_builtin_action(mix_hwrng_into_linux_rng_action, "mix_hwrng_into_linux_rng");
queue_builtin_action(keychord_init_action, "keychord_init");
queue_builtin_action(console_init_action, "console_init");
- /* execute all the boot actions to get us started */
+ // Trigger 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.
+ char bootmode[PROP_VALUE_MAX];
+ if (property_get("ro.bootmode", bootmode) > 0 && 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");
-
-#if BOOTCHART
- queue_builtin_action(bootchart_init_action, "bootchart_init");
-#endif
-
- for(;;) {
- int nr, i, timeout = -1;
-
- 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;
- }
- 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;
+ while (true) {
+ if (!waiting_for_exec) {
+ execute_one_command();
+ restart_processes();
}
+ int timeout = -1;
if (process_needs_restart) {
timeout = (process_needs_restart - gettime()) * 1000;
if (timeout < 0)
timeout = 0;
}
- if (!action_queue_empty() || cur_action)
+ if (!action_queue_empty() || cur_action) {
timeout = 0;
-
-#if BOOTCHART
- if (bootchart_count > 0) {
- if (timeout < 0 || timeout > BOOTCHART_POLLING_MS)
- timeout = BOOTCHART_POLLING_MS;
- if (bootchart_step() < 0 || --bootchart_count == 0) {
- bootchart_finish();
- bootchart_count = 0;
- }
}
-#endif
- nr = poll(ufds, fd_count, timeout);
- if (nr <= 0)
- continue;
+ bootchart_sample(&timeout);
- for (i = 0; i < fd_count; i++) {
- if (ufds[i].revents & POLLIN) {
- if (ufds[i].fd == get_property_set_fd())
- handle_property_set_fd();
- else if (ufds[i].fd == get_keychord_fd())
- handle_keychord();
- else if (ufds[i].fd == get_signal_fd())
- handle_signal();
- }
+ epoll_event ev;
+ int nr = TEMP_FAILURE_RETRY(epoll_wait(epoll_fd, &ev, 1, timeout));
+ if (nr == -1) {
+ ERROR("epoll_wait failed: %s\n", strerror(errno));
+ } else if (nr == 1) {
+ ((void (*)()) ev.data.ptr)();
}
}
diff --git a/init/init.h b/init/init.h
index a7615a3..1cabb14 100644
--- a/init/init.h
+++ b/init/init.h
@@ -17,11 +17,10 @@
#ifndef _INIT_INIT_H
#define _INIT_INIT_H
-#include <cutils/list.h>
-
-#include <sys/stat.h>
+#include <sys/types.h>
-void handle_control_message(const char *msg, const char *arg);
+#include <cutils/list.h>
+#include <cutils/iosched_policy.h>
struct command
{
@@ -37,6 +36,11 @@ struct command
char *args[1];
};
+struct trigger {
+ struct listnode nlist;
+ const char *name;
+};
+
struct action {
/* node in list of all actions */
struct listnode alist;
@@ -46,8 +50,9 @@ struct action {
struct listnode tlist;
unsigned hash;
- const char *name;
+ /* list of actions which triggers the commands*/
+ struct listnode triggers;
struct listnode commands;
struct command *current;
};
@@ -68,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;
@@ -96,25 +103,25 @@ 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;
int keychord_id;
- int ioprio_class;
+ IoSchedClass ioprio_class;
int ioprio_pri;
int nargs;
@@ -122,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);
@@ -138,8 +151,10 @@ 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);
+
+void register_epoll_handler(int fd, void (*fn)());
#endif /* _INIT_INIT_H */
diff --git a/init/init_parser.c b/init/init_parser.cpp
index 6466db2..b76b04e 100644
--- a/init/init_parser.c
+++ b/init/init_parser.cpp
@@ -14,14 +14,16 @@
* limitations under the License.
*/
-#include <stdio.h>
-#include <stdlib.h>
-#include <unistd.h>
+#include <ctype.h>
+#include <errno.h>
#include <fcntl.h>
+#include <inttypes.h>
#include <stdarg.h>
-#include <string.h>
#include <stddef.h>
-#include <ctype.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
#include "init.h"
#include "parser.h"
@@ -73,14 +75,53 @@ static struct {
#define kw_func(kw) (keyword_info[kw].func)
#define kw_nargs(kw) (keyword_info[kw].nargs)
+void dump_parser_state() {
+ if (false) {
+ struct listnode* node;
+ list_for_each(node, &service_list) {
+ service* svc = node_to_item(node, struct service, slist);
+ INFO("service %s\n", svc->name);
+ INFO(" class '%s'\n", svc->classname);
+ INFO(" exec");
+ for (int n = 0; n < svc->nargs; n++) {
+ INFO(" '%s'", svc->args[n]);
+ }
+ INFO("\n");
+ for (socketinfo* si = svc->sockets; si; si = si->next) {
+ INFO(" socket %s %s 0%o\n", si->name, si->type, si->perm);
+ }
+ }
+
+ list_for_each(node, &action_list) {
+ action* act = node_to_item(node, struct action, alist);
+ INFO("on ");
+ char name_str[256] = "";
+ build_triggers_string(name_str, sizeof(name_str), act);
+ INFO("%s", name_str);
+ INFO("\n");
+
+ struct listnode* node2;
+ list_for_each(node2, &act->commands) {
+ command* cmd = node_to_item(node2, struct command, clist);
+ INFO(" %p", cmd->func);
+ for (int n = 0; n < cmd->nargs; n++) {
+ INFO(" %s", cmd->args[n]);
+ }
+ INFO("\n");
+ }
+ INFO("\n");
+ }
+ }
+}
+
static int lookup_keyword(const char *s)
{
switch (*s++) {
+ case 'b':
+ if (!strcmp(s, "ootchart_init")) return K_bootchart_init;
+ break;
case 'c':
- if (!strcmp(s, "opy")) return K_copy;
- if (!strcmp(s, "apability")) return K_capability;
- if (!strcmp(s, "hdir")) return K_chdir;
- if (!strcmp(s, "hroot")) return K_chroot;
+ if (!strcmp(s, "opy")) return K_copy;
if (!strcmp(s, "lass")) return K_class;
if (!strcmp(s, "lass_start")) return K_class_start;
if (!strcmp(s, "lass_stop")) return K_class_stop;
@@ -110,6 +151,7 @@ static int lookup_keyword(const char *s)
if (!strcmp(s, "fup")) return K_ifup;
if (!strcmp(s, "nsmod")) return K_insmod;
if (!strcmp(s, "mport")) return K_import;
+ if (!strcmp(s, "nstallkey")) return K_installkey;
break;
case 'k':
if (!strcmp(s, "eycodes")) return K_keycodes;
@@ -131,6 +173,7 @@ static int lookup_keyword(const char *s)
break;
case 'p':
if (!strcmp(s, "owerctl")) return K_powerctl;
+ break;
case 'r':
if (!strcmp(s, "estart")) return K_restart;
if (!strcmp(s, "estorecon")) return K_restorecon;
@@ -141,13 +184,9 @@ static int lookup_keyword(const char *s)
case 's':
if (!strcmp(s, "eclabel")) return K_seclabel;
if (!strcmp(s, "ervice")) return K_service;
- if (!strcmp(s, "etcon")) return K_setcon;
- if (!strcmp(s, "etenforce")) return K_setenforce;
if (!strcmp(s, "etenv")) return K_setenv;
- if (!strcmp(s, "etkey")) return K_setkey;
if (!strcmp(s, "etprop")) return K_setprop;
if (!strcmp(s, "etrlimit")) return K_setrlimit;
- if (!strcmp(s, "etsebool")) return K_setsebool;
if (!strcmp(s, "ocket")) return K_socket;
if (!strcmp(s, "tart")) return K_start;
if (!strcmp(s, "top")) return K_stop;
@@ -161,6 +200,10 @@ static int lookup_keyword(const char *s)
case 'u':
if (!strcmp(s, "ser")) return K_user;
break;
+ case 'v':
+ if (!strcmp(s, "erity_load_state")) return K_verity_load_state;
+ if (!strcmp(s, "erity_update_state")) return K_verity_update_state;
+ break;
case 'w':
if (!strcmp(s, "rite")) return K_write;
if (!strcmp(s, "ait")) return K_wait;
@@ -169,8 +212,7 @@ static int lookup_keyword(const char *s)
return K_UNKNOWN;
}
-static void parse_line_no_op(struct parse_state *state, int nargs, char **args)
-{
+static void parse_line_no_op(struct parse_state*, int, char**) {
}
static int push_chars(char **dst, int *len, const char *chars, int cnt)
@@ -187,19 +229,14 @@ static int push_chars(char **dst, int *len, const char *chars, int cnt)
int expand_props(char *dst, const char *src, int dst_size)
{
- int cnt = 0;
char *dst_ptr = dst;
const char *src_ptr = src;
- int src_len;
- int idx = 0;
int ret = 0;
int left = dst_size - 1;
if (!src || !dst || dst_size == 0)
return -1;
- src_len = strlen(src);
-
/* - variables can either be $x.y or ${x.y}, in case they are only part
* of the string.
* - will accept $$ as a literal $.
@@ -294,8 +331,7 @@ err:
static void parse_import(struct parse_state *state, int nargs, char **args)
{
- struct listnode *import_list = state->priv;
- struct import *import;
+ struct listnode *import_list = (listnode*) state->priv;
char conf_file[PATH_MAX];
int ret;
@@ -311,10 +347,10 @@ static void parse_import(struct parse_state *state, int nargs, char **args)
return;
}
- import = calloc(1, sizeof(struct import));
+ struct import* import = (struct import*) calloc(1, sizeof(struct import));
import->filename = strdup(conf_file);
list_add_tail(import_list, &import->list);
- INFO("found import '%s', adding to import list", import->filename);
+ INFO("Added '%s' to import list\n", import->filename);
}
static void parse_new_section(struct parse_state *state, int kw,
@@ -344,7 +380,7 @@ static void parse_new_section(struct parse_state *state, int kw,
state->parse_line = parse_line_no_op;
}
-static void parse_config(const char *fn, char *s)
+static void parse_config(const char *fn, const std::string& data)
{
struct parse_state state;
struct listnode import_list;
@@ -355,7 +391,7 @@ static void parse_config(const char *fn, char *s)
nargs = 0;
state.filename = fn;
state.line = 0;
- state.ptr = s;
+ state.ptr = strdup(data.c_str()); // TODO: fix this code!
state.nexttoken = 0;
state.parse_line = parse_line_no_op;
@@ -393,7 +429,6 @@ parser_done:
struct import *import = node_to_item(node, struct import, list);
int ret;
- INFO("importing '%s'", import->filename);
ret = init_parse_config_file(import->filename);
if (ret)
ERROR("could not import file '%s' from '%s'\n",
@@ -401,14 +436,18 @@ parser_done:
}
}
-int init_parse_config_file(const char *fn)
-{
- char *data;
- data = read_file(fn, 0);
- if (!data) return -1;
+int init_parse_config_file(const char* path) {
+ INFO("Parsing %s...\n", path);
+ Timer t;
+ std::string data;
+ if (!read_file(path, &data)) {
+ return -1;
+ }
+
+ parse_config(path, data);
+ dump_parser_state();
- parse_config(fn, data);
- DUMP();
+ NOTICE("(Parsing %s took %.2fs.)\n", path, t.duration());
return 0;
}
@@ -504,83 +543,94 @@ void service_for_each_flags(unsigned matchflags,
void action_for_each_trigger(const char *trigger,
void (*func)(struct action *act))
{
- struct listnode *node;
+ struct listnode *node, *node2;
struct action *act;
+ struct trigger *cur_trigger;
+
list_for_each(node, &action_list) {
act = node_to_item(node, struct action, alist);
- if (!strcmp(act->name, trigger)) {
- func(act);
+ list_for_each(node2, &act->triggers) {
+ cur_trigger = node_to_item(node2, struct trigger, nlist);
+ if (!strcmp(cur_trigger->name, trigger)) {
+ func(act);
+ }
}
}
}
+
void queue_property_triggers(const char *name, const char *value)
{
- struct listnode *node;
+ struct listnode *node, *node2;
struct action *act;
+ struct trigger *cur_trigger;
+ bool match;
+ int name_length;
+
list_for_each(node, &action_list) {
act = node_to_item(node, struct action, alist);
- if (!strncmp(act->name, "property:", strlen("property:"))) {
- const char *test = act->name + strlen("property:");
- int name_length = strlen(name);
-
- if (!strncmp(name, test, name_length) &&
- test[name_length] == '=' &&
- (!strcmp(test + name_length + 1, value) ||
- !strcmp(test + name_length + 1, "*"))) {
- action_add_queue_tail(act);
- }
+ match = !name;
+ list_for_each(node2, &act->triggers) {
+ cur_trigger = node_to_item(node2, struct trigger, nlist);
+ if (!strncmp(cur_trigger->name, "property:", strlen("property:"))) {
+ const char *test = cur_trigger->name + strlen("property:");
+ if (!match) {
+ name_length = strlen(name);
+ if (!strncmp(name, test, name_length) &&
+ test[name_length] == '=' &&
+ (!strcmp(test + name_length + 1, value) ||
+ !strcmp(test + name_length + 1, "*"))) {
+ match = true;
+ continue;
+ }
+ } else {
+ const char* equals = strchr(test, '=');
+ if (equals) {
+ char prop_name[PROP_NAME_MAX + 1];
+ char value[PROP_VALUE_MAX];
+ int length = equals - test;
+ if (length <= PROP_NAME_MAX) {
+ int ret;
+ memcpy(prop_name, test, length);
+ prop_name[length] = 0;
+
+ /* does the property exist, and match the trigger value? */
+ ret = property_get(prop_name, value);
+ if (ret > 0 && (!strcmp(equals + 1, value) ||
+ !strcmp(equals + 1, "*"))) {
+ continue;
+ }
+ }
+ }
+ }
+ }
+ match = false;
+ break;
+ }
+ if (match) {
+ action_add_queue_tail(act);
}
}
}
void queue_all_property_triggers()
{
- struct listnode *node;
- struct action *act;
- list_for_each(node, &action_list) {
- act = node_to_item(node, struct action, alist);
- if (!strncmp(act->name, "property:", strlen("property:"))) {
- /* parse property name and value
- syntax is property:<name>=<value> */
- const char* name = act->name + strlen("property:");
- const char* equals = strchr(name, '=');
- if (equals) {
- char prop_name[PROP_NAME_MAX + 1];
- char value[PROP_VALUE_MAX];
- int length = equals - name;
- if (length > PROP_NAME_MAX) {
- ERROR("property name too long in trigger %s", act->name);
- } else {
- int ret;
- memcpy(prop_name, name, length);
- prop_name[length] = 0;
-
- /* does the property exist, and match the trigger value? */
- ret = property_get(prop_name, value);
- if (ret > 0 && (!strcmp(equals + 1, value) ||
- !strcmp(equals + 1, "*"))) {
- action_add_queue_tail(act);
- }
- }
- }
- }
- }
+ queue_property_triggers(NULL, NULL);
}
-void queue_builtin_action(int (*func)(int nargs, char **args), char *name)
+void queue_builtin_action(int (*func)(int nargs, char **args), const char *name)
{
- struct action *act;
- struct command *cmd;
-
- act = calloc(1, sizeof(*act));
- act->name = name;
+ action* act = (action*) calloc(1, sizeof(*act));
+ trigger* cur_trigger = (trigger*) calloc(1, sizeof(*cur_trigger));
+ cur_trigger->name = name;
+ list_init(&act->triggers);
+ list_add_tail(&act->triggers, &cur_trigger->nlist);
list_init(&act->commands);
list_init(&act->qlist);
- cmd = calloc(1, sizeof(*cmd));
+ command* cmd = (command*) calloc(1, sizeof(*cmd));
cmd->func = func;
- cmd->args[0] = name;
+ cmd->args[0] = const_cast<char*>(name);
cmd->nargs = 1;
list_add_tail(&act->commands, &cmd->clist);
@@ -613,9 +663,67 @@ 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)
{
- struct service *svc;
if (nargs < 3) {
parse_error(state, "services must have a name and a program\n");
return 0;
@@ -625,24 +733,27 @@ static void *parse_service(struct parse_state *state, int nargs, char **args)
return 0;
}
- svc = service_find_by_name(args[1]);
+ service* svc = (service*) service_find_by_name(args[1]);
if (svc) {
parse_error(state, "ignored duplicate definition of service '%s'\n", args[1]);
return 0;
}
nargs -= 2;
- svc = calloc(1, sizeof(*svc) + sizeof(char*) * nargs);
+ svc = (service*) calloc(1, sizeof(*svc) + sizeof(char*) * nargs);
if (!svc) {
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));
svc->args[nargs] = 0;
svc->nargs = nargs;
- svc->onrestart.name = "onrestart";
+ list_init(&svc->onrestart.triggers);
+ cur_trigger->name = "onrestart";
+ list_add_tail(&svc->onrestart.triggers, &cur_trigger->nlist);
list_init(&svc->onrestart.commands);
list_add_tail(&service_list, &svc->slist);
return svc;
@@ -650,7 +761,7 @@ static void *parse_service(struct parse_state *state, int nargs, char **args)
static void parse_line_service(struct parse_state *state, int nargs, char **args)
{
- struct service *svc = state->context;
+ struct service *svc = (service*) state->context;
struct command *cmd;
int i, kw, kw_nargs;
@@ -662,8 +773,6 @@ static void parse_line_service(struct parse_state *state, int nargs, char **args
kw = lookup_keyword(args[0]);
switch (kw) {
- case K_capability:
- break;
case K_class:
if (nargs != 2) {
parse_error(state, "class option requires a classname\n");
@@ -719,7 +828,7 @@ static void parse_line_service(struct parse_state *state, int nargs, char **args
if (nargs < 2) {
parse_error(state, "keycodes option requires atleast one keycode\n");
} else {
- svc->keycodes = malloc((nargs - 1) * sizeof(svc->keycodes[0]));
+ svc->keycodes = (int*) malloc((nargs - 1) * sizeof(svc->keycodes[0]));
if (!svc->keycodes) {
parse_error(state, "could not allocate keycodes\n");
} else {
@@ -748,7 +857,7 @@ static void parse_line_service(struct parse_state *state, int nargs, char **args
break;
}
- cmd = malloc(sizeof(*cmd) + sizeof(char*) * nargs);
+ cmd = (command*) malloc(sizeof(*cmd) + sizeof(char*) * nargs);
cmd->func = kw_func(kw);
cmd->nargs = nargs;
memcpy(cmd->args, args, sizeof(char*) * nargs);
@@ -758,12 +867,11 @@ static void parse_line_service(struct parse_state *state, int nargs, char **args
svc->flags |= SVC_CRITICAL;
break;
case K_setenv: { /* name value */
- struct svcenvinfo *ei;
if (nargs < 3) {
parse_error(state, "setenv option requires name and value arguments\n");
break;
}
- ei = calloc(1, sizeof(*ei));
+ svcenvinfo* ei = (svcenvinfo*) calloc(1, sizeof(*ei));
if (!ei) {
parse_error(state, "out of memory\n");
break;
@@ -775,7 +883,6 @@ static void parse_line_service(struct parse_state *state, int nargs, char **args
break;
}
case K_socket: {/* name type perm [ uid gid context ] */
- struct socketinfo *si;
if (nargs < 4) {
parse_error(state, "socket option requires name, type, perm arguments\n");
break;
@@ -785,7 +892,7 @@ static void parse_line_service(struct parse_state *state, int nargs, char **args
parse_error(state, "socket type must be 'dgram', 'stream' or 'seqpacket'\n");
break;
}
- si = calloc(1, sizeof(*si));
+ socketinfo* si = (socketinfo*) calloc(1, sizeof(*si));
if (!si) {
parse_error(state, "out of memory\n");
break;
@@ -825,17 +932,36 @@ static void parse_line_service(struct parse_state *state, int nargs, char **args
static void *parse_action(struct parse_state *state, int nargs, char **args)
{
- struct action *act;
+ struct trigger *cur_trigger;
+ int i;
if (nargs < 2) {
parse_error(state, "actions must have a trigger\n");
return 0;
}
- if (nargs > 2) {
- parse_error(state, "actions may not have extra parameters\n");
- return 0;
+
+ action* act = (action*) calloc(1, sizeof(*act));
+ list_init(&act->triggers);
+
+ for (i = 1; i < nargs; i++) {
+ if (!(i % 2)) {
+ if (strcmp(args[i], "&&")) {
+ struct listnode *node;
+ struct listnode *node2;
+ parse_error(state, "& is the only symbol allowed to concatenate actions\n");
+ list_for_each_safe(node, node2, &act->triggers) {
+ struct trigger *trigger = node_to_item(node, struct trigger, nlist);
+ free(trigger);
+ }
+ free(act);
+ return 0;
+ } else
+ continue;
+ }
+ cur_trigger = (trigger*) calloc(1, sizeof(*cur_trigger));
+ cur_trigger->name = args[i];
+ list_add_tail(&act->triggers, &cur_trigger->nlist);
}
- act = calloc(1, sizeof(*act));
- act->name = args[1];
+
list_init(&act->commands);
list_init(&act->qlist);
list_add_tail(&action_list, &act->alist);
@@ -845,9 +971,7 @@ static void *parse_action(struct parse_state *state, int nargs, char **args)
static void parse_line_action(struct parse_state* state, int nargs, char **args)
{
- struct command *cmd;
- struct action *act = state->context;
- int (*func)(int nargs, char **args);
+ struct action *act = (action*) state->context;
int kw, n;
if (nargs == 0) {
@@ -866,7 +990,7 @@ static void parse_line_action(struct parse_state* state, int nargs, char **args)
n > 2 ? "arguments" : "argument");
return;
}
- cmd = malloc(sizeof(*cmd) + sizeof(char*) * nargs);
+ command* cmd = (command*) malloc(sizeof(*cmd) + sizeof(char*) * nargs);
cmd->func = kw_func(kw);
cmd->line = state->line;
cmd->filename = state->filename;
diff --git a/init/init_parser.h b/init/init_parser.h
index b078cad..6348607 100644
--- a/init/init_parser.h
+++ b/init/init_parser.h
@@ -20,6 +20,7 @@
#define INIT_PARSER_MAXARGS 64
struct action;
+struct service;
struct action *action_remove_queue_head(void);
void action_add_queue_tail(struct action *act);
@@ -28,9 +29,11 @@ void action_for_each_trigger(const char *trigger,
int action_queue_empty(void);
void queue_property_triggers(const char *name, const char *value);
void queue_all_property_triggers();
-void queue_builtin_action(int (*func)(int nargs, char **args), char *name);
+void queue_builtin_action(int (*func)(int nargs, char **args), const char *name);
int init_parse_config_file(const char *fn);
int expand_props(char *dst, const char *src, int len);
+service* make_exec_oneshot_service(int argc, char** argv);
+
#endif
diff --git a/init/init_parser_test.cpp b/init/init_parser_test.cpp
new file mode 100644
index 0000000..170a73a
--- /dev/null
+++ b/init/init_parser_test.cpp
@@ -0,0 +1,134 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "init_parser.h"
+
+#include "init.h"
+#include "util.h"
+
+#include <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);
+}
diff --git a/init/keychords.c b/init/keychords.cpp
index 4a64042..10d9573 100644
--- a/init/keychords.c
+++ b/init/keychords.cpp
@@ -40,7 +40,7 @@ void add_service_keycodes(struct service *svc)
if (svc->keycodes) {
/* add a new keychord to the list */
size = sizeof(*keychord) + svc->nkeycodes * sizeof(keychord->keycodes[0]);
- keychords = realloc(keychords, keychords_length + size);
+ keychords = (input_keychord*) realloc(keychords, keychords_length + size);
if (!keychords) {
ERROR("could not allocate keychords\n");
keychords_length = 0;
@@ -62,38 +62,7 @@ void add_service_keycodes(struct service *svc)
}
}
-void keychord_init()
-{
- int fd, ret;
-
- service_for_each(add_service_keycodes);
-
- /* nothing to do if no services require keychords */
- if (!keychords)
- return;
-
- fd = open("/dev/keychord", O_RDWR);
- if (fd < 0) {
- ERROR("could not open /dev/keychord\n");
- return;
- }
- fcntl(fd, F_SETFD, FD_CLOEXEC);
-
- ret = write(fd, keychords, keychords_length);
- if (ret != keychords_length) {
- ERROR("could not configure /dev/keychord %d (%d)\n", ret, errno);
- close(fd);
- fd = -1;
- }
-
- free(keychords);
- keychords = 0;
-
- keychord_fd = fd;
-}
-
-void handle_keychord()
-{
+static void handle_keychord() {
struct service *svc;
char adb_enabled[PROP_VALUE_MAX];
int ret;
@@ -110,7 +79,7 @@ void handle_keychord()
if (!strcmp(adb_enabled, "running")) {
svc = service_find_by_keychord(id);
if (svc) {
- INFO("starting service %s from keychord\n", svc->name);
+ INFO("Starting service %s from keychord\n", svc->name);
service_start(svc, NULL);
} else {
ERROR("service for keychord %d not found\n", id);
@@ -118,7 +87,28 @@ void handle_keychord()
}
}
-int get_keychord_fd()
-{
- return keychord_fd;
+void keychord_init() {
+ service_for_each(add_service_keycodes);
+
+ // Nothing to do if no services require keychords.
+ if (!keychords) {
+ return;
+ }
+
+ keychord_fd = TEMP_FAILURE_RETRY(open("/dev/keychord", O_RDWR | O_CLOEXEC));
+ if (keychord_fd == -1) {
+ ERROR("could not open /dev/keychord: %s\n", strerror(errno));
+ return;
+ }
+
+ int ret = write(keychord_fd, keychords, keychords_length);
+ if (ret != keychords_length) {
+ ERROR("could not configure /dev/keychord %d: %s\n", ret, strerror(errno));
+ close(keychord_fd);
+ }
+
+ free(keychords);
+ keychords = nullptr;
+
+ register_epoll_handler(keychord_fd, handle_keychord);
}
diff --git a/init/keychords.h b/init/keychords.h
index 070b858..d2723b7 100644
--- a/init/keychords.h
+++ b/init/keychords.h
@@ -19,9 +19,7 @@
struct service;
-void add_service_keycodes(struct service *svc);
-void keychord_init(void);
-void handle_keychord(void);
-int get_keychord_fd(void);
+void add_service_keycodes(service*);
+void keychord_init();
#endif
diff --git a/init/keywords.h b/init/keywords.h
index 2d97e5b..37f01b8 100644
--- a/init/keywords.h
+++ b/init/keywords.h
@@ -1,7 +1,5 @@
-
#ifndef KEYWORD
-int do_chroot(int nargs, char **args);
-int do_chdir(int nargs, char **args);
+int do_bootchart_init(int nargs, char **args);
int do_class_start(int nargs, char **args);
int do_class_stop(int nargs, char **args);
int do_class_reset(int nargs, char **args);
@@ -12,6 +10,7 @@ int do_export(int nargs, char **args);
int do_hostname(int nargs, char **args);
int do_ifup(int nargs, char **args);
int do_insmod(int nargs, char **args);
+int do_installkey(int nargs, char **args);
int do_mkdir(int nargs, char **args);
int do_mount_all(int nargs, char **args);
int do_mount(int nargs, char **args);
@@ -21,12 +20,8 @@ int do_restorecon(int nargs, char **args);
int do_restorecon_recursive(int nargs, char **args);
int do_rm(int nargs, char **args);
int do_rmdir(int nargs, char **args);
-int do_setcon(int nargs, char **args);
-int do_setenforce(int nargs, char **args);
-int do_setkey(int nargs, char **args);
int do_setprop(int nargs, char **args);
int do_setrlimit(int nargs, char **args);
-int do_setsebool(int nargs, char **args);
int do_start(int nargs, char **args);
int do_stop(int nargs, char **args);
int do_swapon_all(int nargs, char **args);
@@ -40,15 +35,14 @@ int do_chmod(int nargs, char **args);
int do_loglevel(int nargs, char **args);
int do_load_persist_props(int nargs, char **args);
int do_load_all_props(int nargs, char **args);
+int do_verity_load_state(int nargs, char **args);
+int do_verity_update_state(int nargs, char **args);
int do_wait(int nargs, char **args);
#define __MAKE_KEYWORD_ENUM__
#define KEYWORD(symbol, flags, nargs, func) K_##symbol,
enum {
K_UNKNOWN,
#endif
- KEYWORD(capability, OPTION, 0, 0)
- KEYWORD(chdir, COMMAND, 1, do_chdir)
- KEYWORD(chroot, COMMAND, 1, do_chroot)
KEYWORD(class, OPTION, 0, 0)
KEYWORD(class_start, COMMAND, 1, do_class_start)
KEYWORD(class_stop, COMMAND, 1, do_class_stop)
@@ -64,6 +58,7 @@ enum {
KEYWORD(hostname, COMMAND, 1, do_hostname)
KEYWORD(ifup, COMMAND, 1, do_ifup)
KEYWORD(insmod, COMMAND, 1, do_insmod)
+ KEYWORD(installkey, COMMAND, 1, do_installkey)
KEYWORD(import, SECTION, 1, 0)
KEYWORD(keycodes, OPTION, 0, 0)
KEYWORD(mkdir, COMMAND, 1, do_mkdir)
@@ -80,13 +75,9 @@ enum {
KEYWORD(rmdir, COMMAND, 1, do_rmdir)
KEYWORD(seclabel, OPTION, 0, 0)
KEYWORD(service, SECTION, 0, 0)
- KEYWORD(setcon, COMMAND, 1, do_setcon)
- KEYWORD(setenforce, COMMAND, 1, do_setenforce)
KEYWORD(setenv, OPTION, 2, 0)
- KEYWORD(setkey, COMMAND, 0, do_setkey)
KEYWORD(setprop, COMMAND, 2, do_setprop)
KEYWORD(setrlimit, COMMAND, 3, do_setrlimit)
- KEYWORD(setsebool, COMMAND, 2, do_setsebool)
KEYWORD(socket, OPTION, 0, 0)
KEYWORD(start, COMMAND, 1, do_start)
KEYWORD(stop, COMMAND, 1, do_stop)
@@ -95,6 +86,8 @@ enum {
KEYWORD(symlink, COMMAND, 1, do_symlink)
KEYWORD(sysclktz, COMMAND, 1, do_sysclktz)
KEYWORD(user, OPTION, 0, 0)
+ KEYWORD(verity_load_state, COMMAND, 0, do_verity_load_state)
+ KEYWORD(verity_update_state, COMMAND, 0, do_verity_update_state)
KEYWORD(wait, COMMAND, 1, do_wait)
KEYWORD(write, COMMAND, 2, do_write)
KEYWORD(copy, COMMAND, 2, do_copy)
@@ -104,10 +97,10 @@ enum {
KEYWORD(load_persist_props, COMMAND, 0, do_load_persist_props)
KEYWORD(load_all_props, COMMAND, 0, do_load_all_props)
KEYWORD(ioprio, OPTION, 0, 0)
+ KEYWORD(bootchart_init, COMMAND, 0, do_bootchart_init)
#ifdef __MAKE_KEYWORD_ENUM__
KEYWORD_COUNT,
};
#undef __MAKE_KEYWORD_ENUM__
#undef KEYWORD
#endif
-
diff --git a/init/log.cpp b/init/log.cpp
new file mode 100644
index 0000000..d32f2da
--- /dev/null
+++ b/init/log.cpp
@@ -0,0 +1,62 @@
+/*
+ * 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 <stdlib.h>
+#include <string.h>
+#include <sys/uio.h>
+
+#include <selinux/selinux.h>
+
+#include "log.h"
+
+static void init_klog_vwrite(int level, const char* fmt, va_list ap) {
+ static const char* tag = basename(getprogname());
+
+ char prefix[64];
+ snprintf(prefix, sizeof(prefix), "<%d>%s: ", level, tag);
+
+ char msg[512];
+ vsnprintf(msg, sizeof(msg), fmt, ap);
+
+ iovec iov[2];
+ iov[0].iov_base = prefix;
+ iov[0].iov_len = strlen(prefix);
+ iov[1].iov_base = msg;
+ iov[1].iov_len = strlen(msg);
+
+ klog_writev(level, iov, 2);
+}
+
+void init_klog_write(int level, const char* fmt, ...) {
+ va_list ap;
+ va_start(ap, fmt);
+ init_klog_vwrite(level, fmt, ap);
+ va_end(ap);
+}
+
+int selinux_klog_callback(int type, const char *fmt, ...) {
+ int level = KLOG_ERROR_LEVEL;
+ if (type == SELINUX_WARNING) {
+ level = KLOG_WARNING_LEVEL;
+ } else if (type == SELINUX_INFO) {
+ level = KLOG_INFO_LEVEL;
+ }
+ va_list ap;
+ va_start(ap, fmt);
+ init_klog_vwrite(level, fmt, ap);
+ va_end(ap);
+ return 0;
+}
diff --git a/init/log.h b/init/log.h
index e9cb65a..b804d1f 100644
--- a/init/log.h
+++ b/init/log.h
@@ -19,10 +19,11 @@
#include <cutils/klog.h>
-#define ERROR(x...) KLOG_ERROR("init", x)
-#define NOTICE(x...) KLOG_NOTICE("init", x)
-#define INFO(x...) KLOG_INFO("init", x)
+#define ERROR(x...) init_klog_write(KLOG_ERROR_LEVEL, x)
+#define NOTICE(x...) init_klog_write(KLOG_NOTICE_LEVEL, x)
+#define INFO(x...) init_klog_write(KLOG_INFO_LEVEL, x)
-extern int log_callback(int type, const char *fmt, ...);
+void init_klog_write(int level, const char* fmt, ...) __printflike(2, 3);
+int selinux_klog_callback(int level, const char* fmt, ...) __printflike(2, 3);
#endif
diff --git a/init/parser.c b/init/parser.cpp
index 48e7aec..8193729 100644
--- a/init/parser.c
+++ b/init/parser.cpp
@@ -1,59 +1,17 @@
-#include <stdio.h>
+#include "parser.h"
+
#include <stdarg.h>
+#include <stdio.h>
#include <string.h>
-#include "parser.h"
#include "log.h"
-#define RAW(x...) log_write(6, x)
-
-void DUMP(void)
-{
-#if 0
- struct service *svc;
- struct action *act;
- struct command *cmd;
- struct listnode *node;
- struct listnode *node2;
- struct socketinfo *si;
- int n;
-
- list_for_each(node, &service_list) {
- svc = node_to_item(node, struct service, slist);
- RAW("service %s\n", svc->name);
- RAW(" class '%s'\n", svc->classname);
- RAW(" exec");
- for (n = 0; n < svc->nargs; n++) {
- RAW(" '%s'", svc->args[n]);
- }
- RAW("\n");
- for (si = svc->sockets; si; si = si->next) {
- RAW(" socket %s %s 0%o\n", si->name, si->type, si->perm);
- }
- }
-
- list_for_each(node, &action_list) {
- act = node_to_item(node, struct action, alist);
- RAW("on %s\n", act->name);
- list_for_each(node2, &act->commands) {
- cmd = node_to_item(node2, struct command, clist);
- RAW(" %p", cmd->func);
- for (n = 0; n < cmd->nargs; n++) {
- RAW(" %s", cmd->args[n]);
- }
- RAW("\n");
- }
- RAW("\n");
- }
-#endif
-}
-
void parse_error(struct parse_state *state, const char *fmt, ...)
{
va_list ap;
char buf[128];
int off;
-
+
snprintf(buf, 128, "%s: %d: ", state->filename, state->line);
buf[127] = 0;
off = strlen(buf);
diff --git a/init/parser.h b/init/parser.h
index a58272a..95e1164 100644
--- a/init/parser.h
+++ b/init/parser.h
@@ -33,7 +33,7 @@ struct parse_state
void *priv;
};
-void DUMP(void);
+void dump_parser_state(void);
int next_token(struct parse_state *state);
void parse_error(struct parse_state *state, const char *fmt, ...);
diff --git a/init/property_service.c b/init/property_service.cpp
index 91ef251..930ef82 100644
--- a/init/property_service.c
+++ b/init/property_service.cpp
@@ -26,6 +26,8 @@
#include <errno.h>
#include <sys/poll.h>
+#include <memory>
+
#include <cutils/misc.h>
#include <cutils/sockets.h>
#include <cutils/multiuser.h>
@@ -52,44 +54,34 @@
#define PERSISTENT_PROPERTY_DIR "/data/property"
static int persistent_properties_loaded = 0;
-static int property_area_inited = 0;
+static bool property_area_initialized = false;
static int property_set_fd = -1;
-typedef struct {
+struct workspace {
size_t size;
int fd;
-} workspace;
-
-static int init_workspace(workspace *w, size_t size)
-{
- void *data;
- int fd = open(PROP_FILENAME, O_RDONLY | O_NOFOLLOW);
- if (fd < 0)
- return -1;
-
- w->size = size;
- w->fd = fd;
- return 0;
-}
+};
static workspace pa_workspace;
-static int init_property_area(void)
-{
- if (property_area_inited)
- return -1;
-
- if(__system_property_area_init())
- return -1;
+void property_init() {
+ if (property_area_initialized) {
+ return;
+ }
- if(init_workspace(&pa_workspace, 0))
- return -1;
+ property_area_initialized = true;
- fcntl(pa_workspace.fd, F_SETFD, FD_CLOEXEC);
+ if (__system_property_area_init()) {
+ return;
+ }
- property_area_inited = 1;
- return 0;
+ pa_workspace.size = 0;
+ pa_workspace.fd = open(PROP_FILENAME, O_RDONLY | O_NOFOLLOW | O_CLOEXEC);
+ if (pa_workspace.fd == -1) {
+ ERROR("Failed to open %s: %s\n", PROP_FILENAME, strerror(errno));
+ return;
+ }
}
static int check_mac_perms(const char *name, char *sctx)
@@ -98,8 +90,6 @@ static int check_mac_perms(const char *name, char *sctx)
return 1;
char *tctx = NULL;
- const char *class = "property_service";
- const char *perm = "set";
int result = 0;
if (!sctx)
@@ -111,7 +101,7 @@ static int check_mac_perms(const char *name, char *sctx)
if (selabel_lookup(sehandle_prop, &tctx, name, 1) != 0)
goto err;
- if (selinux_check_access(sctx, tctx, class, perm, (void*) name) == 0)
+ if (selinux_check_access(sctx, tctx, "property_service", "set", (void*) name) == 0)
result = 1;
freecon(tctx);
@@ -142,9 +132,6 @@ static int check_control_mac_perms(const char *name, char *sctx)
*/
static int check_perms(const char *name, char *sctx)
{
- int i;
- unsigned int app_id;
-
if(!strncmp(name, "ro.", 3))
name +=3;
@@ -165,7 +152,7 @@ static void write_persistent_property(const char *name, const char *value)
snprintf(tempPath, sizeof(tempPath), "%s/.temp.XXXXXX", PERSISTENT_PROPERTY_DIR);
fd = mkstemp(tempPath);
if (fd < 0) {
- ERROR("Unable to write persistent property to temp file %s errno: %d\n", tempPath, errno);
+ ERROR("Unable to write persistent property to temp file %s: %s\n", tempPath, strerror(errno));
return;
}
write(fd, value, strlen(value));
@@ -205,18 +192,14 @@ static bool is_legal_property_name(const char* name, size_t namelen)
return true;
}
-int property_set(const char *name, const char *value)
-{
- prop_info *pi;
- int ret;
-
+static int property_set_impl(const char* name, const char* value) {
size_t namelen = strlen(name);
size_t valuelen = strlen(value);
if (!is_legal_property_name(name, namelen)) return -1;
if (valuelen >= PROP_VALUE_MAX) return -1;
- pi = (prop_info*) __system_property_find(name);
+ prop_info* pi = (prop_info*) __system_property_find(name);
if(pi != 0) {
/* ro.* properties may NEVER be modified once set */
@@ -224,10 +207,9 @@ int property_set(const char *name, const char *value)
__system_property_update(pi, value, valuelen);
} else {
- ret = __system_property_add(name, namelen, value, valuelen);
- if (ret < 0) {
- ERROR("Failed to set '%s'='%s'\n", name, value);
- return ret;
+ int rc = __system_property_add(name, namelen, value, valuelen);
+ if (rc < 0) {
+ return rc;
}
}
/* If name starts with "net." treat as a DNS property. */
@@ -256,12 +238,19 @@ int property_set(const char *name, const char *value)
return 0;
}
-void handle_property_set_fd()
+int property_set(const char* name, const char* value) {
+ int rc = property_set_impl(name, value);
+ if (rc == -1) {
+ ERROR("property_set(\"%s\", \"%s\") failed\n", name, value);
+ }
+ return rc;
+}
+
+static void handle_property_set_fd()
{
prop_msg msg;
int s;
int r;
- int res;
struct ucred cr;
struct sockaddr_un addr;
socklen_t addr_size = sizeof(addr);
@@ -291,15 +280,15 @@ void handle_property_set_fd()
close(s);
return;
} else if (nr < 0) {
- ERROR("sys_prop: error waiting for uid=%d to send property message. err=%d %s\n", cr.uid, errno, strerror(errno));
+ ERROR("sys_prop: error waiting for uid=%d to send property message: %s\n", cr.uid, strerror(errno));
close(s);
return;
}
r = TEMP_FAILURE_RETRY(recv(s, &msg, sizeof(msg), MSG_DONTWAIT));
if(r != sizeof(prop_msg)) {
- ERROR("sys_prop: mis-match msg size received: %d expected: %zu errno: %d\n",
- r, sizeof(prop_msg), errno);
+ ERROR("sys_prop: mis-match msg size received: %d expected: %zu: %s\n",
+ r, sizeof(prop_msg), strerror(errno));
close(s);
return;
}
@@ -421,126 +410,106 @@ static void load_properties(char *data, const char *filter)
* Filter is used to decide which properties to load: NULL loads all keys,
* "ro.foo.*" is a prefix match, and "ro.foo.bar" is an exact match.
*/
-static void load_properties_from_file(const char *fn, const char *filter)
-{
- char *data;
- unsigned sz;
+static void load_properties_from_file(const char* filename, const char* filter) {
+ Timer t;
+ std::string data;
+ if (read_file(filename, &data)) {
+ load_properties(&data[0], filter);
+ }
+ NOTICE("(Loading properties from %s took %.2fs.)\n", filename, t.duration());
+}
- data = read_file(fn, &sz);
+static void load_persistent_properties() {
+ persistent_properties_loaded = 1;
- if(data != 0) {
- load_properties(data, filter);
- free(data);
+ std::unique_ptr<DIR, int(*)(DIR*)> dir(opendir(PERSISTENT_PROPERTY_DIR), closedir);
+ if (!dir) {
+ ERROR("Unable to open persistent property directory \"%s\": %s\n",
+ PERSISTENT_PROPERTY_DIR, strerror(errno));
+ return;
}
-}
-static void load_persistent_properties()
-{
- DIR* dir = opendir(PERSISTENT_PROPERTY_DIR);
- int dir_fd;
- struct dirent* entry;
- char value[PROP_VALUE_MAX];
- int fd, length;
- struct stat sb;
-
- if (dir) {
- dir_fd = dirfd(dir);
- while ((entry = readdir(dir)) != NULL) {
- if (strncmp("persist.", entry->d_name, strlen("persist.")))
- continue;
-#if HAVE_DIRENT_D_TYPE
- if (entry->d_type != DT_REG)
- continue;
-#endif
- /* open the file and read the property value */
- fd = openat(dir_fd, entry->d_name, O_RDONLY | O_NOFOLLOW);
- if (fd < 0) {
- ERROR("Unable to open persistent property file \"%s\" errno: %d\n",
- entry->d_name, errno);
- continue;
- }
- if (fstat(fd, &sb) < 0) {
- ERROR("fstat on property file \"%s\" failed errno: %d\n", entry->d_name, errno);
- close(fd);
- continue;
- }
+ struct dirent* entry;
+ while ((entry = readdir(dir.get())) != NULL) {
+ if (strncmp("persist.", entry->d_name, strlen("persist."))) {
+ continue;
+ }
+ if (entry->d_type != DT_REG) {
+ continue;
+ }
- // File must not be accessible to others, be owned by root/root, and
- // not be a hard link to any other file.
- if (((sb.st_mode & (S_IRWXG | S_IRWXO)) != 0)
- || (sb.st_uid != 0)
- || (sb.st_gid != 0)
- || (sb.st_nlink != 1)) {
- ERROR("skipping insecure property file %s (uid=%u gid=%u nlink=%d mode=%o)\n",
- entry->d_name, (unsigned int)sb.st_uid, (unsigned int)sb.st_gid,
- sb.st_nlink, sb.st_mode);
- close(fd);
- continue;
- }
+ // Open the file and read the property value.
+ int fd = openat(dirfd(dir.get()), entry->d_name, O_RDONLY | O_NOFOLLOW);
+ if (fd == -1) {
+ ERROR("Unable to open persistent property file \"%s\": %s\n",
+ entry->d_name, strerror(errno));
+ continue;
+ }
- length = read(fd, value, sizeof(value) - 1);
- if (length >= 0) {
- value[length] = 0;
- property_set(entry->d_name, value);
- } else {
- ERROR("Unable to read persistent property file %s errno: %d\n",
- entry->d_name, errno);
- }
+ struct stat sb;
+ if (fstat(fd, &sb) == -1) {
+ ERROR("fstat on property file \"%s\" failed: %s\n", entry->d_name, strerror(errno));
close(fd);
+ continue;
}
- closedir(dir);
- } else {
- ERROR("Unable to open persistent property directory %s errno: %d\n", PERSISTENT_PROPERTY_DIR, errno);
- }
- persistent_properties_loaded = 1;
-}
+ // File must not be accessible to others, be owned by root/root, and
+ // not be a hard link to any other file.
+ if (((sb.st_mode & (S_IRWXG | S_IRWXO)) != 0) || (sb.st_uid != 0) || (sb.st_gid != 0) ||
+ (sb.st_nlink != 1)) {
+ ERROR("skipping insecure property file %s (uid=%u gid=%u nlink=%u mode=%o)\n",
+ entry->d_name, (unsigned int)sb.st_uid, (unsigned int)sb.st_gid,
+ (unsigned int)sb.st_nlink, sb.st_mode);
+ close(fd);
+ continue;
+ }
-void property_init(void)
-{
- init_property_area();
+ char value[PROP_VALUE_MAX];
+ int length = read(fd, value, sizeof(value) - 1);
+ if (length >= 0) {
+ value[length] = 0;
+ property_set(entry->d_name, value);
+ } else {
+ ERROR("Unable to read persistent property file %s: %s\n",
+ entry->d_name, strerror(errno));
+ }
+ close(fd);
+ }
}
-void property_load_boot_defaults(void)
-{
+void property_load_boot_defaults() {
load_properties_from_file(PROP_PATH_RAMDISK_DEFAULT, NULL);
}
-int properties_inited(void)
-{
- return property_area_inited;
+bool properties_initialized() {
+ return property_area_initialized;
}
static void load_override_properties() {
-#ifdef ALLOW_LOCAL_PROP_OVERRIDE
- char debuggable[PROP_VALUE_MAX];
- int ret;
-
- ret = property_get("ro.debuggable", debuggable);
- if (ret && (strcmp(debuggable, "1") == 0)) {
- load_properties_from_file(PROP_PATH_LOCAL_OVERRIDE, NULL);
+ if (ALLOW_LOCAL_PROP_OVERRIDE) {
+ char debuggable[PROP_VALUE_MAX];
+ int ret = property_get("ro.debuggable", debuggable);
+ if (ret && (strcmp(debuggable, "1") == 0)) {
+ load_properties_from_file(PROP_PATH_LOCAL_OVERRIDE, NULL);
+ }
}
-#endif /* ALLOW_LOCAL_PROP_OVERRIDE */
}
-
/* When booting an encrypted system, /data is not mounted when the
* property service is started, so any properties stored there are
* not loaded. Vold triggers init to load these properties once it
* has mounted /data.
*/
-void load_persist_props(void)
-{
+void load_persist_props(void) {
load_override_properties();
/* Read persistent properties after all default values have been loaded. */
load_persistent_properties();
}
-void load_all_props(void)
-{
+void load_all_props() {
load_properties_from_file(PROP_PATH_SYSTEM_BUILD, NULL);
- load_properties_from_file(PROP_PATH_SYSTEM_DEFAULT, NULL);
load_properties_from_file(PROP_PATH_VENDOR_BUILD, NULL);
+ load_properties_from_file(PROP_PATH_BOOTIMAGE_BUILD, NULL);
load_properties_from_file(PROP_PATH_FACTORY, "ro.*");
load_override_properties();
@@ -549,20 +518,15 @@ void load_all_props(void)
load_persistent_properties();
}
-void start_property_service(void)
-{
- int fd;
-
- fd = create_socket(PROP_SERVICE_NAME, SOCK_STREAM, 0666, 0, 0, NULL);
- if(fd < 0) return;
- fcntl(fd, F_SETFD, FD_CLOEXEC);
- fcntl(fd, F_SETFL, O_NONBLOCK);
+void start_property_service() {
+ property_set_fd = create_socket(PROP_SERVICE_NAME, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK,
+ 0666, 0, 0, NULL);
+ if (property_set_fd == -1) {
+ ERROR("start_property_service socket creation failed: %s\n", strerror(errno));
+ exit(1);
+ }
- listen(fd, 8);
- property_set_fd = fd;
-}
+ listen(property_set_fd, 8);
-int get_property_set_fd()
-{
- return property_set_fd;
+ register_epoll_handler(property_set_fd, handle_property_set_fd);
}
diff --git a/init/property_service.h b/init/property_service.h
index 730495e..a27053d 100644
--- a/init/property_service.h
+++ b/init/property_service.h
@@ -17,10 +17,9 @@
#ifndef _INIT_PROPERTY_H
#define _INIT_PROPERTY_H
-#include <stdbool.h>
+#include <stddef.h>
#include <sys/system_properties.h>
-extern void handle_property_set_fd(void);
extern void property_init(void);
extern void property_load_boot_defaults(void);
extern void load_persist_props(void);
@@ -29,16 +28,21 @@ extern void start_property_service(void);
void get_property_workspace(int *fd, int *sz);
extern int __property_get(const char *name, char *value);
extern int property_set(const char *name, const char *value);
-extern int properties_inited();
-int get_property_set_fd(void);
+extern bool properties_initialized();
+#ifndef __clang__
extern void __property_get_size_error()
__attribute__((__error__("property_get called with too small buffer")));
+#else
+extern void __property_get_size_error();
+#endif
static inline
__attribute__ ((always_inline))
__attribute__ ((gnu_inline))
+#ifndef __clang__
__attribute__ ((artificial))
+#endif
int property_get(const char *name, char *value)
{
size_t value_len = __builtin_object_size(value, 0);
diff --git a/init/readme.txt b/init/readme.txt
index 26be536..6b9c42d 100644
--- a/init/readme.txt
+++ b/init/readme.txt
@@ -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.
@@ -110,6 +110,7 @@ class <name>
onrestart
Execute a Command (see below) when service restarts.
+
Triggers
--------
Triggers are strings which can be used to match certain kinds
@@ -123,40 +124,22 @@ boot
Triggers of this form occur when the property <name> is set
to the specific value <value>.
-device-added-<path>
-device-removed-<path>
- Triggers of these forms occur when a device node is added
- or removed.
+ One can also test multiple properties to execute a group
+ of commands. For example:
+
+ on property:test.a=1 && property:test.b=1
+ setprop test.c 1
-service-exited-<name>
- Triggers of this form occur when the specified service exits.
+ The above stub sets test.c to 1 only when
+ both test.a=1 and test.b=1
Commands
--------
-exec <path> [ <argument> ]*
- Fork and execute a program (<path>). This will block until
- the program completes execution. It is best to avoid exec
- as unlike the builtin commands, it runs the risk of getting
- init "stuck". (??? maybe there should be a timeout?)
-
-export <name> <value>
- Set the environment variable <name> equal to <value> in the
- global environment (which will be inherited by all processes
- started after this command is executed)
-
-ifup <interface>
- Bring the network interface <interface> online.
-
-import <filename>
- Parse an init config file, extending the current configuration.
-
-hostname <name>
- Set the host name.
-
-chdir <directory>
- Change working directory.
+bootchart_init
+ Start bootcharting if configured (see below).
+ This is included in the default init.rc.
chmod <octal-mode> <path>
Change file access permissions.
@@ -164,17 +147,23 @@ chmod <octal-mode> <path>
chown <owner> <group> <path>
Change file owner and group.
-chroot <directory>
- Change process root directory.
-
class_start <serviceclass>
Start all services of the specified class if they are
not already running.
class_stop <serviceclass>
- Stop all services of the specified class if they are
+ Stop and disable all services of the specified class if they are
currently running.
+class_reset <serviceclass>
+ Stop all services of the specified class if they are
+ currently running, without disabling them. They can be restarted
+ later using class_start.
+
+copy <src> <dst>
+ Copies a file. Similar to write, but useful for binary/large
+ amounts of data.
+
domainname <name>
Set the domain name.
@@ -187,20 +176,63 @@ enable <servicename>
on property:ro.boot.myfancyhardware=1
enable my_fancy_service_for_my_fancy_hardware
+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.
+
+export <name> <value>
+ Set the environment variable <name> equal to <value> in the
+ global environment (which will be inherited by all processes
+ started after this command is executed)
+
+hostname <name>
+ Set the host name.
+
+ifup <interface>
+ Bring the network interface <interface> online.
+
+import <filename>
+ Parse an init config file, extending the current configuration.
insmod <path>
Install the module at <path>
+load_all_props
+ Loads properties from /system, /vendor, et cetera.
+ This is included in the default init.rc.
+
+load_persist_props
+ Loads persistent properties when /data has been decrypted.
+ This is included in the default init.rc.
+
+loglevel <level>
+ Sets the kernel log level to level. Properties are expanded within <level>.
+
mkdir <path> [mode] [owner] [group]
Create a directory at <path>, optionally with the given mode, owner, and
group. If not provided, the directory is created with permissions 755 and
- owned by the root user and root group.
+ owned by the root user and root group. If provided, the mode, owner and group
+ will be updated if the directory exists already.
-mount <type> <device> <dir> [ <mountoption> ]*
+mount_all <fstab>
+ Calls fs_mgr_mount_all on the given fs_mgr-format fstab.
+
+mount <type> <device> <dir> [ <flag> ]* [<options>]
Attempt to mount the named device at the directory <dir>
<device> may be of the form mtd@name to specify a mtd block
device by name.
- <mountoption>s include "ro", "rw", "remount", "noatime", ...
+ <flag>s include "ro", "rw", "remount", "noatime", ...
+ <options> include "barrier=1", "noauto_da_alloc", "discard", ... as
+ a comma separated string, eg: barrier=1,noauto_da_alloc
+
+powerctl
+ Internal implementation detail used to respond to changes to the
+ "sys.powerctl" system property, used to implement rebooting.
+
+restart <service>
+ Like stop, but doesn't disable the service.
restorecon <path> [ <path> ]*
Restore the file named by <path> to the security context specified
@@ -211,37 +243,31 @@ restorecon <path> [ <path> ]*
restorecon_recursive <path> [ <path> ]*
Recursively restore the directory tree named by <path> to the
security contexts specified in the file_contexts configuration.
- Do NOT use this with paths leading to shell-writable or app-writable
- directories, e.g. /data/local/tmp, /data/data or any prefix thereof.
-
-setcon <securitycontext>
- 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.
-setenforce 0|1
- Set the SELinux system-wide enforcing status.
- 0 is permissive (i.e. log but do not deny), 1 is enforcing.
+rm <path>
+ Calls unlink(2) on the given path. You might want to
+ use "exec -- rm ..." instead (provided the system partition is
+ already mounted).
-setkey
- TBD
+rmdir <path>
+ Calls rmdir(2) on the given path.
setprop <name> <value>
- Set system property <name> to <value>.
+ Set system property <name> to <value>. Properties are expanded
+ within <value>.
setrlimit <resource> <cur> <max>
Set the rlimit for a resource.
-setsebool <name> <value>
- Set SELinux boolean <name> to <value>.
- <value> may be 1|true|on or 0|false|off
-
start <service>
Start a service running if it is not already running.
stop <service>
Stop a service from running if it is currently running.
+swapon_all <fstab>
+ Calls fs_mgr_swapon_all on the given fstab file.
+
symlink <target> <path>
Create a symbolic link at <path> with the value <target>
@@ -252,14 +278,23 @@ trigger <event>
Trigger an event. Used to queue an action from another
action.
+verity_load_state
+ Internal implementation detail used to load dm-verity state.
+
+verity_update_state <mount_point>
+ Internal implementation detail used to update dm-verity state and
+ set the partition.<mount_point>.verified properties used by adb remount
+ because fs_mgr can't set them directly itself.
+
wait <path> [ <timeout> ]
- Poll for the existence of the given file and return when found,
- or the timeout has been reached. If timeout is not specified it
- currently defaults to five seconds.
+ Poll for the existence of the given file and return when found,
+ or the timeout has been reached. If timeout is not specified it
+ currently defaults to five seconds.
-write <path> <string>
- Open the file at <path> and write a string to it with write(2)
- without appending.
+write <path> <content>
+ Open the file at <path> and write a string to it with write(2).
+ If the file does not exist, it will be created. If it does exist,
+ it will be truncated. Properties are expanded within <content>.
Properties
@@ -267,7 +302,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
@@ -277,73 +312,62 @@ init.svc.<name>
State of a named service ("stopped", "running", "restarting")
-Example init.conf
------------------
-
-# not complete -- just providing some examples of usage
-#
-on boot
- export PATH /sbin:/system/sbin:/system/bin
- export LD_LIBRARY_PATH /system/lib
-
- mkdir /dev
- mkdir /proc
- mkdir /sys
-
- mount tmpfs tmpfs /dev
- mkdir /dev/pts
- mkdir /dev/socket
- mount devpts devpts /dev/pts
- mount proc proc /proc
- mount sysfs sysfs /sys
-
- write /proc/cpu/alignment 4
+Bootcharting
+------------
+This version of init contains code to perform "bootcharting": generating log
+files that can be later processed by the tools provided by www.bootchart.org.
- ifup lo
+On the emulator, use the -bootchart <timeout> option to boot with bootcharting
+activated for <timeout> seconds.
- hostname localhost
- domainname localhost
+On a device, create /data/bootchart/start with a command like the following:
- mount yaffs2 mtd@system /system
- mount yaffs2 mtd@userdata /data
+ adb shell 'echo $TIMEOUT > /data/bootchart/start'
- import /system/etc/init.conf
+Where the value of $TIMEOUT corresponds to the desired bootcharted period in
+seconds. Bootcharting will stop after that many seconds have elapsed.
+You can also stop the bootcharting at any moment by doing the following:
- class_start default
+ adb shell 'echo 1 > /data/bootchart/stop'
-service adbd /sbin/adbd
- user adb
- group adb
+Note that /data/bootchart/stop is deleted automatically by init at the end of
+the bootcharting. This is not the case with /data/bootchart/start, so don't
+forget to delete it when you're done collecting data.
-service usbd /system/bin/usbd -r
- user usbd
- group usbd
- socket usbd 666
+The log files are written to /data/bootchart/. A script is provided to
+retrieve them and create a bootchart.tgz file that can be used with the
+bootchart command-line utility:
-service zygote /system/bin/app_process -Xzygote /system/bin --zygote
- socket zygote 666
+ sudo apt-get install pybootchartgui
+ # grab-bootchart.sh uses $ANDROID_SERIAL.
+ $ANDROID_BUILD_TOP/system/core/init/grab-bootchart.sh
-service runtime /system/bin/runtime
- user system
- group system
+One thing to watch for is that the bootchart will show init as if it started
+running at 0s. You'll have to look at dmesg to work out when the kernel
+actually started init.
-on device-added-/dev/compass
- start akmd
-on device-removed-/dev/compass
- stop akmd
-
-service akmd /sbin/akmd
- disabled
- user akmd
- group akmd
-
-Debugging notes
----------------
+Debugging init
+--------------
By default, programs executed by init will drop stdout and stderr into
/dev/null. To help with debugging, you can execute your program via the
-Andoird program logwrapper. This will redirect stdout/stderr into the
+Android program logwrapper. This will redirect stdout/stderr into the
Android logging system (accessed via logcat).
For example
service akmd /system/bin/logwrapper /sbin/akmd
+
+For quicker turnaround when working on init itself, use:
+
+ mm -j
+ m ramdisk-nodeps
+ m bootimage-nodeps
+ adb reboot bootloader
+ fastboot boot $ANDROID_PRODUCT_OUT/boot.img
+
+Alternatively, use the emulator:
+
+ emulator -partition-size 1024 -verbose -show-kernel -no-window
+
+You might want to call klog_set_level(6) after the klog_init() call
+so you see the kernel logging in dmesg (or the emulator output).
diff --git a/init/signal_handler.c b/init/signal_handler.c
deleted file mode 100644
index 7e8e1a7..0000000
--- a/init/signal_handler.c
+++ /dev/null
@@ -1,165 +0,0 @@
-/*
- * Copyright (C) 2010 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 <errno.h>
-#include <signal.h>
-#include <unistd.h>
-#include <fcntl.h>
-#include <sys/types.h>
-#include <sys/socket.h>
-#include <sys/wait.h>
-#include <cutils/sockets.h>
-#include <cutils/android_reboot.h>
-#include <cutils/list.h>
-
-#include "init.h"
-#include "util.h"
-#include "log.h"
-
-static int signal_fd = -1;
-static int signal_recv_fd = -1;
-
-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*/
-
-static int wait_for_one_process(int block)
-{
- pid_t pid;
- int status;
- struct service *svc;
- struct socketinfo *si;
- time_t now;
- struct listnode *node;
- struct command *cmd;
-
- while ( (pid = waitpid(-1, &status, block ? 0 : WNOHANG)) == -1 && errno == EINTR );
- if (pid <= 0) return -1;
- INFO("waitpid returned pid %d, status = %08x\n", pid, status);
-
- svc = service_find_by_pid(pid);
- if (!svc) {
- if (WIFEXITED(status)) {
- ERROR("untracked pid %d exited with status %d\n", pid, WEXITSTATUS(status));
- } else if (WIFSIGNALED(status)) {
- ERROR("untracked pid %d killed by signal %d\n", pid, WTERMSIG(status));
- } else if (WIFSTOPPED(status)) {
- ERROR("untracked pid %d stopped by signal %d\n", pid, WSTOPSIG(status));
- } else {
- ERROR("untracked pid %d state changed\n", pid);
- }
- return 0;
- }
-
- 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);
- }
-
- /* remove any sockets we may have created */
- for (si = svc->sockets; si; si = si->next) {
- char tmp[128];
- snprintf(tmp, sizeof(tmp), ANDROID_SOCKET_DIR"/%s", si->name);
- unlink(tmp);
- }
-
- svc->pid = 0;
- svc->flags &= (~SVC_RUNNING);
-
- /* 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");
- return 0;
- }
-
- 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) {
- ERROR("critical process '%s' exited %d times in %d minutes; "
- "rebooting into recovery mode\n", svc->name,
- CRITICAL_CRASH_THRESHOLD, CRITICAL_CRASH_WINDOW / 60);
- android_reboot(ANDROID_RB_RESTART2, 0, "recovery");
- return 0;
- }
- } else {
- svc->time_crashed = now;
- svc->nr_crashed = 1;
- }
- }
-
- svc->flags &= (~SVC_RESTART);
- svc->flags |= SVC_RESTARTING;
-
- /* Execute all onrestart commands for this service. */
- list_for_each(node, &svc->onrestart.commands) {
- cmd = node_to_item(node, struct command, clist);
- cmd->func(cmd->nargs, cmd->args);
- }
- notify_service_state(svc->name, "restarting");
- return 0;
-}
-
-void handle_signal(void)
-{
- 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))
- ;
-}
-
-void signal_init(void)
-{
- int s[2];
-
- 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 */
- if (socketpair(AF_UNIX, SOCK_STREAM, 0, s) == 0) {
- signal_fd = s[0];
- signal_recv_fd = s[1];
- fcntl(s[0], F_SETFD, FD_CLOEXEC);
- fcntl(s[0], F_SETFL, O_NONBLOCK);
- fcntl(s[1], F_SETFD, FD_CLOEXEC);
- fcntl(s[1], F_SETFL, O_NONBLOCK);
- }
-
- handle_signal();
-}
-
-int get_signal_fd()
-{
- return signal_recv_fd;
-}
diff --git a/init/signal_handler.cpp b/init/signal_handler.cpp
new file mode 100644
index 0000000..39a466d
--- /dev/null
+++ b/init/signal_handler.cpp
@@ -0,0 +1,185 @@
+/*
+ * Copyright (C) 2010 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 <errno.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <stdio.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include <base/stringprintf.h>
+#include <cutils/android_reboot.h>
+#include <cutils/list.h>
+#include <cutils/sockets.h>
+
+#include "init.h"
+#include "log.h"
+#include "util.h"
+
+#define CRITICAL_CRASH_THRESHOLD 4 /* if we crash >4 times ... */
+#define CRITICAL_CRASH_WINDOW (4*60) /* ... in 4 minutes, goto recovery */
+
+static int signal_write_fd = -1;
+static int signal_read_fd = -1;
+
+static std::string DescribeStatus(int status) {
+ if (WIFEXITED(status)) {
+ return android::base::StringPrintf("exited with status %d", WEXITSTATUS(status));
+ } else if (WIFSIGNALED(status)) {
+ return android::base::StringPrintf("killed by signal %d", WTERMSIG(status));
+ } else if (WIFSTOPPED(status)) {
+ return android::base::StringPrintf("stopped by signal %d", WSTOPSIG(status));
+ } else {
+ return "state changed";
+ }
+}
+
+static bool wait_for_one_process() {
+ int status;
+ pid_t pid = TEMP_FAILURE_RETRY(waitpid(-1, &status, WNOHANG));
+ if (pid == 0) {
+ return false;
+ } else if (pid == -1) {
+ ERROR("waitpid failed: %s\n", strerror(errno));
+ return false;
+ }
+
+ service* svc = service_find_by_pid(pid);
+
+ std::string name;
+ if (svc) {
+ name = android::base::StringPrintf("Service '%s' (pid %d)", svc->name, pid);
+ } else {
+ name = android::base::StringPrintf("Untracked pid %d", pid);
+ }
+
+ NOTICE("%s %s\n", name.c_str(), DescribeStatus(status).c_str());
+
+ if (!svc) {
+ return true;
+ }
+
+ // TODO: all the code from here down should be a member function on service.
+
+ if (!(svc->flags & SVC_ONESHOT) || (svc->flags & SVC_RESTART)) {
+ NOTICE("Service '%s' (pid %d) killing any children in process group\n", svc->name, pid);
+ kill(-pid, SIGKILL);
+ }
+
+ // 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 true;
+ }
+
+ svc->pid = 0;
+ svc->flags &= (~SVC_RUNNING);
+
+ // 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)) {
+ svc->NotifyStateChange("stopped");
+ return true;
+ }
+
+ 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) {
+ ERROR("critical process '%s' exited %d times in %d minutes; "
+ "rebooting into recovery mode\n", svc->name,
+ CRITICAL_CRASH_THRESHOLD, CRITICAL_CRASH_WINDOW / 60);
+ android_reboot(ANDROID_RB_RESTART2, 0, "recovery");
+ return true;
+ }
+ } else {
+ svc->time_crashed = now;
+ svc->nr_crashed = 1;
+ }
+ }
+
+ svc->flags &= (~SVC_RESTART);
+ svc->flags |= SVC_RESTARTING;
+
+ // Execute all onrestart commands for this service.
+ struct listnode* node;
+ list_for_each(node, &svc->onrestart.commands) {
+ command* cmd = node_to_item(node, struct command, clist);
+ cmd->func(cmd->nargs, cmd->args);
+ }
+ svc->NotifyStateChange("restarting");
+ return true;
+}
+
+static void reap_any_outstanding_children() {
+ while (wait_for_one_process()) {
+ }
+}
+
+static void handle_signal() {
+ // Clear outstanding requests.
+ char buf[32];
+ read(signal_read_fd, buf, sizeof(buf));
+
+ reap_any_outstanding_children();
+}
+
+static void SIGCHLD_handler(int) {
+ if (TEMP_FAILURE_RETRY(write(signal_write_fd, "1", 1)) == -1) {
+ ERROR("write(signal_write_fd) failed: %s\n", strerror(errno));
+ }
+}
+
+void signal_handler_init() {
+ // Create a signalling mechanism for SIGCHLD.
+ int s[2];
+ if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC, 0, s) == -1) {
+ ERROR("socketpair failed: %s\n", strerror(errno));
+ exit(1);
+ }
+
+ signal_write_fd = s[0];
+ signal_read_fd = s[1];
+
+ // Write to signal_write_fd if we catch SIGCHLD.
+ struct sigaction act;
+ memset(&act, 0, sizeof(act));
+ act.sa_handler = SIGCHLD_handler;
+ act.sa_flags = SA_NOCLDSTOP;
+ sigaction(SIGCHLD, &act, 0);
+
+ reap_any_outstanding_children();
+
+ register_epoll_handler(signal_read_fd, handle_signal);
+}
diff --git a/init/signal_handler.h b/init/signal_handler.h
index b092ccb..449b4af 100644
--- a/init/signal_handler.h
+++ b/init/signal_handler.h
@@ -17,8 +17,6 @@
#ifndef _INIT_SIGNAL_HANDLER_H_
#define _INIT_SIGNAL_HANDLER_H_
-void signal_init(void);
-void handle_signal(void);
-int get_signal_fd(void);
+void signal_handler_init(void);
#endif
diff --git a/init/ueventd.c b/init/ueventd.cpp
index 833e4fd..c63fdaa 100644
--- a/init/ueventd.c
+++ b/init/ueventd.cpp
@@ -14,46 +14,27 @@
* limitations under the License.
*/
-#include <poll.h>
-#include <fcntl.h>
-#include <string.h>
-#include <stdlib.h>
-#include <stdio.h>
#include <ctype.h>
+#include <fcntl.h>
+#include <poll.h>
#include <signal.h>
-#include <selinux/selinux.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <base/stringprintf.h>
#include <private/android_filesystem_config.h>
+#include <selinux/selinux.h>
#include "ueventd.h"
#include "log.h"
#include "util.h"
#include "devices.h"
#include "ueventd_parser.h"
-
-static char hardware[32];
-static unsigned revision = 0;
-
-static void import_kernel_nv(char *name, int in_qemu)
-{
- if (*name != '\0') {
- char *value = strchr(name, '=');
- if (value != NULL) {
- *value++ = 0;
- if (!strcmp(name,"androidboot.hardware"))
- {
- strlcpy(hardware, value, sizeof(hardware));
- }
- }
- }
-}
+#include "property_service.h"
int ueventd_main(int argc, char **argv)
{
- struct pollfd ufd;
- int nr;
- char tmp[32];
-
/*
* init sets the umask to 077 for forked processes. We need to
* create files with exact permissions, without modification by
@@ -70,44 +51,38 @@ int ueventd_main(int argc, char **argv)
open_devnull_stdio();
klog_init();
-#if LOG_UEVENTS
- /* Ensure we're at a logging level that will show the events */
- if (klog_get_level() < KLOG_INFO_LEVEL) {
- klog_set_level(KLOG_INFO_LEVEL);
- }
-#endif
+ klog_set_level(KLOG_NOTICE_LEVEL);
- union selinux_callback cb;
- cb.func_log = log_callback;
- selinux_set_callback(SELINUX_CB_LOG, cb);
+ NOTICE("ueventd started!\n");
- INFO("starting ueventd\n");
-
- /* Respect hardware passed in through the kernel cmd line. Here we will look
- * for androidboot.hardware param in kernel cmdline, and save its value in
- * hardware[]. */
- import_kernel_cmdline(0, import_kernel_nv);
+ selinux_callback cb;
+ cb.func_log = selinux_klog_callback;
+ selinux_set_callback(SELINUX_CB_LOG, cb);
- get_hardware_name(hardware, &revision);
+ char hardware[PROP_VALUE_MAX];
+ property_get("ro.hardware", hardware);
ueventd_parse_config_file("/ueventd.rc");
-
- snprintf(tmp, sizeof(tmp), "/ueventd.%s.rc", hardware);
- ueventd_parse_config_file(tmp);
+ ueventd_parse_config_file(android::base::StringPrintf("/ueventd.%s.rc", hardware).c_str());
device_init();
+ pollfd ufd;
ufd.events = POLLIN;
ufd.fd = get_device_fd();
- while(1) {
+ while (true) {
ufd.revents = 0;
- nr = poll(&ufd, 1, -1);
- if (nr <= 0)
+ int nr = poll(&ufd, 1, -1);
+ if (nr <= 0) {
continue;
- if (ufd.revents & POLLIN)
- handle_device_fd();
+ }
+ if (ufd.revents & POLLIN) {
+ handle_device_fd();
+ }
}
+
+ return 0;
}
static int get_android_id(const char *id)
diff --git a/init/ueventd.h b/init/ueventd.h
index 0a454c5..d12d7fe 100644
--- a/init/ueventd.h
+++ b/init/ueventd.h
@@ -20,16 +20,18 @@
#include <cutils/list.h>
#include <sys/types.h>
+enum devname_src_t {
+ DEVNAME_UNKNOWN = 0,
+ DEVNAME_UEVENT_DEVNAME,
+ DEVNAME_UEVENT_DEVPATH,
+};
+
struct ueventd_subsystem {
struct listnode slist;
const char *name;
- enum {
- DEVNAME_UNKNOWN = 0,
- DEVNAME_UEVENT_DEVNAME,
- DEVNAME_UEVENT_DEVPATH,
- } devname_src;
const char *dirname;
+ devname_src_t devname_src;
};
int ueventd_main(int argc, char **argv);
diff --git a/init/ueventd_parser.c b/init/ueventd_parser.cpp
index e447006..7a4841f 100644
--- a/init/ueventd_parser.c
+++ b/init/ueventd_parser.cpp
@@ -67,9 +67,7 @@ static int lookup_keyword(const char *s)
return K_UNKNOWN;
}
-static void parse_line_no_op(struct parse_state *state __attribute__((unused)),
- int nargs __attribute__((unused)), char **args __attribute__((unused)))
-{
+static void parse_line_no_op(struct parse_state*, int, char**) {
}
static int valid_name(const char *name)
@@ -97,24 +95,20 @@ struct ueventd_subsystem *ueventd_subsystem_find_by_name(const char *name)
return 0;
}
-static void *parse_subsystem(struct parse_state *state,
- int nargs __attribute__((unused)), char **args)
-{
- struct ueventd_subsystem *s;
-
+static void *parse_subsystem(parse_state* state, int /*nargs*/, char** args) {
if (!valid_name(args[1])) {
parse_error(state, "invalid subsystem name '%s'\n", args[1]);
return 0;
}
- s = ueventd_subsystem_find_by_name(args[1]);
+ ueventd_subsystem* s = ueventd_subsystem_find_by_name(args[1]);
if (s) {
parse_error(state, "ignored duplicate definition of subsystem '%s'\n",
args[1]);
return 0;
}
- s = calloc(1, sizeof(*s));
+ s = (ueventd_subsystem*) calloc(1, sizeof(*s));
if (!s) {
parse_error(state, "out of memory\n");
return 0;
@@ -128,7 +122,7 @@ static void *parse_subsystem(struct parse_state *state,
static void parse_line_subsystem(struct parse_state *state, int nargs,
char **args)
{
- struct ueventd_subsystem *s = state->context;
+ struct ueventd_subsystem *s = (ueventd_subsystem*) state->context;
int kw;
if (nargs == 0) {
@@ -197,7 +191,7 @@ static void parse_line(struct parse_state *state, char **args, int nargs)
}
}
-static void parse_config(const char *fn, char *s)
+static void parse_config(const char *fn, const std::string& data)
{
struct parse_state state;
char *args[UEVENTD_PARSER_MAXARGS];
@@ -205,7 +199,7 @@ static void parse_config(const char *fn, char *s)
nargs = 0;
state.filename = fn;
state.line = 1;
- state.ptr = s;
+ state.ptr = strdup(data.c_str()); // TODO: fix this code!
state.nexttoken = 0;
state.parse_line = parse_line_no_op;
for (;;) {
@@ -232,17 +226,16 @@ static void parse_config(const char *fn, char *s)
int ueventd_parse_config_file(const char *fn)
{
- char *data;
- data = read_file(fn, 0);
- if (!data) return -1;
+ std::string data;
+ if (!read_file(fn, &data)) {
+ return -1;
+ }
parse_config(fn, data);
- DUMP();
+ dump_parser_state();
return 0;
}
-static void parse_line_device(struct parse_state *state __attribute__((unused)),
- int nargs, char **args)
-{
+static void parse_line_device(parse_state*, int nargs, char** args) {
set_device_permission(nargs, args);
}
diff --git a/init/util.c b/init/util.cpp
index 12cb11d..20ce806 100644
--- a/init/util.c
+++ b/init/util.cpp
@@ -32,6 +32,8 @@
#include <sys/socket.h>
#include <sys/un.h>
+#include <base/file.h>
+
/* for ANDROID_SOCKET_* */
#include <cutils/sockets.h>
@@ -146,48 +148,46 @@ out_close:
return -1;
}
-/* reads a file, making sure it is terminated with \n \0 */
-void *read_file(const char *fn, unsigned *_sz)
-{
- char *data;
- int sz;
- int fd;
- struct stat sb;
+bool read_file(const char* path, std::string* content) {
+ content->clear();
- data = 0;
- fd = open(fn, O_RDONLY);
- if(fd < 0) return 0;
+ int fd = TEMP_FAILURE_RETRY(open(path, O_RDONLY|O_NOFOLLOW|O_CLOEXEC));
+ if (fd == -1) {
+ return false;
+ }
- // for security reasons, disallow world-writable
- // or group-writable files
- if (fstat(fd, &sb) < 0) {
- ERROR("fstat failed for '%s'\n", fn);
- goto oops;
+ // For security reasons, disallow world-writable
+ // or group-writable files.
+ struct stat sb;
+ if (fstat(fd, &sb) == -1) {
+ ERROR("fstat failed for '%s': %s\n", path, strerror(errno));
+ return false;
}
if ((sb.st_mode & (S_IWGRP | S_IWOTH)) != 0) {
- ERROR("skipping insecure file '%s'\n", fn);
- goto oops;
+ ERROR("skipping insecure file '%s'\n", path);
+ return false;
}
- sz = lseek(fd, 0, SEEK_END);
- if(sz < 0) goto oops;
-
- if(lseek(fd, 0, SEEK_SET) != 0) goto oops;
-
- data = (char*) malloc(sz + 2);
- if(data == 0) goto oops;
-
- if(read(fd, data, sz) != sz) goto oops;
- close(fd);
- data[sz] = '\n';
- data[sz+1] = 0;
- if(_sz) *_sz = sz;
- return data;
+ bool okay = android::base::ReadFdToString(fd, content);
+ TEMP_FAILURE_RETRY(close(fd));
+ if (okay) {
+ content->append("\n", 1);
+ }
+ return okay;
+}
-oops:
- close(fd);
- if(data != 0) free(data);
- return 0;
+int write_file(const char* path, const char* content) {
+ int fd = TEMP_FAILURE_RETRY(open(path, O_WRONLY|O_CREAT|O_NOFOLLOW|O_CLOEXEC, 0600));
+ if (fd == -1) {
+ NOTICE("write_file: Unable to open '%s': %s\n", path, strerror(errno));
+ return -1;
+ }
+ int result = android::base::WriteStringToFd(content, fd) ? 0 : -1;
+ if (result == -1) {
+ NOTICE("write_file: Unable to write to '%s': %s\n", path, strerror(errno));
+ }
+ TEMP_FAILURE_RETRY(close(fd));
+ return result;
}
#define MAX_MTD_PARTITIONS 16
@@ -207,7 +207,7 @@ static void find_mtd_partitions(void)
ssize_t pmtdsize;
int r;
- fd = open("/proc/mtd", O_RDONLY);
+ fd = open("/proc/mtd", O_RDONLY|O_CLOEXEC);
if (fd < 0)
return;
@@ -262,22 +262,16 @@ int mtd_name_to_number(const char *name)
return -1;
}
-/*
- * gettime() - returns the time in seconds of the system's monotonic clock or
- * zero on error.
- */
-time_t gettime(void)
-{
- struct timespec ts;
- int ret;
-
- ret = clock_gettime(CLOCK_MONOTONIC, &ts);
- if (ret < 0) {
- ERROR("clock_gettime(CLOCK_MONOTONIC) failed: %s\n", strerror(errno));
- return 0;
- }
+time_t gettime() {
+ timespec now;
+ clock_gettime(CLOCK_MONOTONIC, &now);
+ return now.tv_sec;
+}
- return ts.tv_sec;
+uint64_t gettime_ns() {
+ timespec now;
+ clock_gettime(CLOCK_MONOTONIC, &now);
+ return static_cast<uint64_t>(now.tv_sec) * UINT64_C(1000000000) + now.tv_nsec;
}
int mkdir_recursive(const char *pathname, mode_t mode)
@@ -385,101 +379,37 @@ int wait_for_file(const char *filename, int timeout)
void open_devnull_stdio(void)
{
- int fd;
- static const char *name = "/dev/__null__";
- if (mknod(name, S_IFCHR | 0600, (1 << 8) | 3) == 0) {
- fd = open(name, O_RDWR);
- unlink(name);
- if (fd >= 0) {
- dup2(fd, 0);
- dup2(fd, 1);
- dup2(fd, 2);
- if (fd > 2) {
- close(fd);
- }
- return;
- }
- }
-
- exit(1);
-}
-
-void get_hardware_name(char *hardware, unsigned int *revision)
-{
- const char *cpuinfo = "/proc/cpuinfo";
- char *data = NULL;
- size_t len = 0, limit = 1024;
- int fd, n;
- char *x, *hw, *rev;
-
- /* Hardware string was provided on kernel command line */
- if (hardware[0])
- return;
-
- fd = open(cpuinfo, O_RDONLY);
- if (fd < 0) return;
-
- for (;;) {
- x = realloc(data, limit);
- if (!x) {
- ERROR("Failed to allocate memory to read %s\n", cpuinfo);
- goto done;
+ // Try to avoid the mknod() call if we can. Since SELinux makes
+ // a /dev/null replacement available for free, let's use it.
+ int fd = open("/sys/fs/selinux/null", O_RDWR);
+ if (fd == -1) {
+ // OOPS, /sys/fs/selinux/null isn't available, likely because
+ // /sys/fs/selinux isn't mounted. Fall back to mknod.
+ static const char *name = "/dev/__null__";
+ if (mknod(name, S_IFCHR | 0600, (1 << 8) | 3) == 0) {
+ fd = open(name, O_RDWR);
+ unlink(name);
}
- data = x;
-
- n = read(fd, data + len, limit - len);
- if (n < 0) {
- ERROR("Failed reading %s: %s (%d)\n", cpuinfo, strerror(errno), errno);
- goto done;
+ if (fd == -1) {
+ exit(1);
}
- len += n;
-
- if (len < limit)
- break;
-
- /* We filled the buffer, so increase size and loop to read more */
- limit *= 2;
}
- data[len] = 0;
- hw = strstr(data, "\nHardware");
- rev = strstr(data, "\nRevision");
-
- if (hw) {
- x = strstr(hw, ": ");
- if (x) {
- x += 2;
- n = 0;
- while (*x && *x != '\n') {
- if (!isspace(*x))
- hardware[n++] = tolower(*x);
- x++;
- if (n == 31) break;
- }
- hardware[n] = 0;
- }
- }
-
- if (rev) {
- x = strstr(rev, ": ");
- if (x) {
- *revision = strtoul(x + 2, 0, 16);
- }
+ dup2(fd, 0);
+ dup2(fd, 1);
+ dup2(fd, 2);
+ if (fd > 2) {
+ close(fd);
}
-
-done:
- close(fd);
- free(data);
}
-void import_kernel_cmdline(int in_qemu,
- void (*import_kernel_nv)(char *name, int in_qemu))
+void import_kernel_cmdline(bool in_qemu, std::function<void(char*,bool)> import_kernel_nv)
{
char cmdline[2048];
char *ptr;
int fd;
- fd = open("/proc/cmdline", O_RDONLY);
+ fd = open("/proc/cmdline", O_RDONLY | O_CLOEXEC);
if (fd >= 0) {
int n = read(fd, cmdline, sizeof(cmdline) - 1);
if (n < 0) n = 0;
diff --git a/init/util.h b/init/util.h
index a7e7c8b..1c947ec 100644
--- a/init/util.h
+++ b/init/util.h
@@ -20,15 +20,36 @@
#include <sys/stat.h>
#include <sys/types.h>
+#include <string>
+#include <functional>
+
#define ARRAY_SIZE(x) (sizeof(x)/sizeof(x[0]))
-static const char *coldboot_done = "/dev/.coldboot_done";
+#define COLDBOOT_DONE "/dev/.coldboot_done"
int mtd_name_to_number(const char *name);
int create_socket(const char *name, int type, mode_t perm,
uid_t uid, gid_t gid, const char *socketcon);
-void *read_file(const char *fn, unsigned *_sz);
-time_t gettime(void);
+
+bool read_file(const char* path, std::string* content);
+int write_file(const char* path, const char* content);
+
+time_t gettime();
+uint64_t gettime_ns();
+
+class Timer {
+ public:
+ Timer() : t0(gettime_ns()) {
+ }
+
+ double duration() {
+ return static_cast<double>(gettime_ns() - t0) / 1000000000.0;
+ }
+
+ private:
+ uint64_t t0;
+};
+
unsigned int decode_uid(const char *s);
int mkdir_recursive(const char *pathname, mode_t mode);
@@ -37,8 +58,7 @@ void make_link_init(const char *oldpath, const char *newpath);
void remove_link(const char *oldpath, const char *newpath);
int wait_for_file(const char *filename, int timeout);
void open_devnull_stdio(void);
-void get_hardware_name(char *hardware, unsigned int *revision);
-void import_kernel_cmdline(int in_qemu, void (*import_kernel_nv)(char *name, int in_qemu));
+void import_kernel_cmdline(bool in_qemu, std::function<void(char*,bool)>);
int make_dir(const char *path, mode_t mode);
int restorecon(const char *pathname);
int restorecon_recursive(const char *pathname);
diff --git a/init/util_test.cpp b/init/util_test.cpp
new file mode 100644
index 0000000..5b3ab50
--- /dev/null
+++ b/init/util_test.cpp
@@ -0,0 +1,43 @@
+/*
+ * 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 "util.h"
+
+#include <errno.h>
+#include <gtest/gtest.h>
+
+TEST(util, read_file_ENOENT) {
+ std::string s("hello");
+ errno = 0;
+ EXPECT_FALSE(read_file("/proc/does-not-exist", &s));
+ EXPECT_EQ(ENOENT, errno);
+ EXPECT_EQ("", s); // s was cleared.
+}
+
+TEST(util, read_file_success) {
+ std::string s("hello");
+ EXPECT_TRUE(read_file("/proc/version", &s));
+ EXPECT_GT(s.length(), 6U);
+ EXPECT_EQ('\n', s[s.length() - 1]);
+ 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"));
+}
diff --git a/init/watchdogd.c b/init/watchdogd.cpp
index fb53836..881a4df 100644
--- a/init/watchdogd.c
+++ b/init/watchdogd.cpp
@@ -18,6 +18,7 @@
#include <fcntl.h>
#include <stdlib.h>
#include <string.h>
+#include <unistd.h>
#include <linux/watchdog.h>
@@ -26,52 +27,45 @@
#define DEV_NAME "/dev/watchdog"
-int watchdogd_main(int argc, char **argv)
-{
- int fd;
- int ret;
- int interval = 10;
- int margin = 10;
- int timeout;
-
+int watchdogd_main(int argc, char **argv) {
open_devnull_stdio();
klog_init();
+ klog_set_level(KLOG_NOTICE_LEVEL);
- INFO("Starting watchdogd\n");
-
- if (argc >= 2)
- interval = atoi(argv[1]);
+ int interval = 10;
+ if (argc >= 2) interval = atoi(argv[1]);
- if (argc >= 3)
- margin = atoi(argv[2]);
+ int margin = 10;
+ if (argc >= 3) margin = atoi(argv[2]);
- timeout = interval + margin;
+ NOTICE("watchdogd started (interval %d, margin %d)!\n", interval, margin);
- fd = open(DEV_NAME, O_RDWR);
- if (fd < 0) {
+ int fd = open(DEV_NAME, O_RDWR|O_CLOEXEC);
+ if (fd == -1) {
ERROR("watchdogd: Failed to open %s: %s\n", DEV_NAME, strerror(errno));
return 1;
}
- ret = ioctl(fd, WDIOC_SETTIMEOUT, &timeout);
+ int timeout = interval + margin;
+ int ret = ioctl(fd, WDIOC_SETTIMEOUT, &timeout);
if (ret) {
ERROR("watchdogd: Failed to set timeout to %d: %s\n", timeout, strerror(errno));
ret = ioctl(fd, WDIOC_GETTIMEOUT, &timeout);
if (ret) {
ERROR("watchdogd: Failed to get timeout: %s\n", strerror(errno));
} else {
- if (timeout > margin)
+ if (timeout > margin) {
interval = timeout - margin;
- else
+ } else {
interval = 1;
+ }
ERROR("watchdogd: Adjusted interval to timeout returned by driver: timeout %d, interval %d, margin %d\n",
timeout, interval, margin);
}
}
- while(1) {
+ while (true) {
write(fd, "", 1);
sleep(interval);
}
}
-