Merge "init: clean up the 1st/2nd stage init split"
am: 3ecaf2e7f5
Change-Id: If5274001db2e455c34f9bbec2c2417e61e3a2620
diff --git a/init/Android.bp b/init/Android.bp
index ff3b61f..d82ec66 100644
--- a/init/Android.bp
+++ b/init/Android.bp
@@ -79,6 +79,7 @@
"libkeyutils",
"liblog",
"liblogwrap",
+ "liblp",
"libselinux",
"libutils",
],
@@ -99,6 +100,7 @@
"devices.cpp",
"epoll.cpp",
"firmware_handler.cpp",
+ "first_stage_init.cpp",
"first_stage_mount.cpp",
"import_parser.cpp",
"init.cpp",
@@ -117,6 +119,7 @@
"sigchld_handler.cpp",
"subcontext.cpp",
"subcontext.proto",
+ "switch_root.cpp",
"rlimit_parser.cpp",
"tokenizer.cpp",
"uevent_listener.cpp",
diff --git a/init/Android.mk b/init/Android.mk
index dc46d21..700c81e 100644
--- a/init/Android.mk
+++ b/init/Android.mk
@@ -39,12 +39,15 @@
# --
+# Do not build this even with mmma if we're system-as-root, otherwise it will overwrite the symlink.
+ifneq ($(BOARD_BUILD_SYSTEM_ROOT_IMAGE),true)
include $(CLEAR_VARS)
LOCAL_CPPFLAGS := $(init_cflags)
LOCAL_SRC_FILES := \
devices.cpp \
+ first_stage_init.cpp \
+ first_stage_main.cpp \
first_stage_mount.cpp \
- init_first_stage.cpp \
reboot_utils.cpp \
selinux.cpp \
switch_root.cpp \
@@ -93,19 +96,16 @@
# First stage init is weird: it may start without stdout/stderr, and no /proc.
LOCAL_NOSANITIZE := hwaddress
include $(BUILD_EXECUTABLE)
+endif
include $(CLEAR_VARS)
LOCAL_MODULE := init_system
+LOCAL_REQUIRED_MODULES := \
+ init_second_stage \
+
ifeq ($(BOARD_BUILD_SYSTEM_ROOT_IMAGE),true)
-LOCAL_REQUIRED_MODULES := \
- init_first_stage \
- init_second_stage \
-
-else
-LOCAL_REQUIRED_MODULES := \
- init_second_stage \
-
+LOCAL_POST_INSTALL_CMD := ln -sf /system/bin/init $(TARGET_ROOT_OUT)/init
endif
include $(BUILD_PHONY_PACKAGE)
@@ -118,5 +118,3 @@
endif
include $(BUILD_PHONY_PACKAGE)
-
-
diff --git a/init/init_first_stage.cpp b/init/first_stage_init.cpp
similarity index 97%
rename from init/init_first_stage.cpp
rename to init/first_stage_init.cpp
index c2c6868..e11d897 100644
--- a/init/init_first_stage.cpp
+++ b/init/first_stage_init.cpp
@@ -14,6 +14,8 @@
* limitations under the License.
*/
+#include "first_stage_init.h"
+
#include <dirent.h>
#include <fcntl.h>
#include <paths.h>
@@ -94,7 +96,7 @@
} // namespace
-int main(int argc, char** argv) {
+int FirstStageMain(int argc, char** argv) {
if (REBOOT_BOOTLOADER_ON_PANIC) {
InstallRebootSignalHandlers();
}
@@ -214,7 +216,7 @@
setenv("INIT_STARTED_AT", std::to_string(start_ms).c_str(), 1);
const char* path = "/system/bin/init";
- const char* args[] = {path, nullptr};
+ const char* args[] = {path, "selinux_setup", nullptr};
execv(path, const_cast<char**>(args));
// execv() only returns if an error happened, in which case we
@@ -226,7 +228,3 @@
} // namespace init
} // namespace android
-
-int main(int argc, char** argv) {
- return android::init::main(argc, argv);
-}
diff --git a/init/first_stage_init.h b/init/first_stage_init.h
new file mode 100644
index 0000000..0476e44
--- /dev/null
+++ b/init/first_stage_init.h
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2018 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.
+ */
+
+#pragma once
+
+namespace android {
+namespace init {
+
+int FirstStageMain(int argc, char** argv);
+
+} // namespace init
+} // namespace android
diff --git a/init/first_stage_main.cpp b/init/first_stage_main.cpp
new file mode 100644
index 0000000..7bae84c
--- /dev/null
+++ b/init/first_stage_main.cpp
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2018 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 "first_stage_init.h"
+
+int main(int argc, char** argv) {
+ return android::init::FirstStageMain(argc, argv);
+}
diff --git a/init/init.cpp b/init/init.cpp
index e7dbc11..39c1832 100644
--- a/init/init.cpp
+++ b/init/init.cpp
@@ -59,13 +59,8 @@
#include "security.h"
#include "selinux.h"
#include "sigchld_handler.h"
-#include "ueventd.h"
#include "util.h"
-#if __has_feature(address_sanitizer)
-#include <sanitizer/asan_interface.h>
-#endif
-
using namespace std::chrono_literals;
using namespace std::string_literals;
@@ -79,25 +74,6 @@
namespace android {
namespace init {
-#if __has_feature(address_sanitizer)
-// Load asan.options if it exists since these are not yet in the environment.
-// Always ensure detect_container_overflow=0 as there are false positives with this check.
-// Always ensure abort_on_error=1 to ensure we reboot to bootloader for development builds.
-extern "C" const char* __asan_default_options() {
- return "include_if_exists=/system/asan.options:detect_container_overflow=0:abort_on_error=1";
-}
-
-__attribute__((no_sanitize("address", "memory", "thread", "undefined"))) extern "C" void
-__sanitizer_report_error_summary(const char* summary) {
- LOG(ERROR) << "Main stage (error summary): " << summary;
-}
-
-__attribute__((no_sanitize("address", "memory", "thread", "undefined"))) static void
-AsanReportCallback(const char* str) {
- LOG(ERROR) << "Main stage: " << str;
-}
-#endif
-
static int property_triggers_enabled = 0;
static char qemu[32];
@@ -622,57 +598,11 @@
});
}
-static void SetupSelinux(char** argv) {
- android::base::InitLogging(argv, &android::base::KernelLogger, [](const char*) {
- RebootSystem(ANDROID_RB_RESTART2, "bootloader");
- });
-
- // Set up SELinux, loading the SELinux policy.
- SelinuxSetupKernelLogging();
- SelinuxInitialize();
-
- // We're in the kernel domain and want to transition to the init domain. File systems that
- // store SELabels in their xattrs, such as ext4 do not need an explicit restorecon here,
- // but other file systems do. In particular, this is needed for ramdisks such as the
- // recovery image for A/B devices.
- if (selinux_android_restorecon("/system/bin/init", 0) == -1) {
- PLOG(FATAL) << "restorecon failed of /system/bin/init failed";
- }
-
- setenv("SELINUX_INITIALIZED", "true", 1);
-
- const char* path = "/system/bin/init";
- const char* args[] = {path, nullptr};
- execv(path, const_cast<char**>(args));
-
- // execv() only returns if an error happened, in which case we
- // panic and never return from this function.
- PLOG(FATAL) << "execv(\"" << path << "\") failed";
-}
-
-int main(int argc, char** argv) {
-#if __has_feature(address_sanitizer)
- __asan_set_error_report_callback(AsanReportCallback);
-#endif
-
- if (!strcmp(basename(argv[0]), "ueventd")) {
- return ueventd_main(argc, argv);
- }
-
- if (argc > 1 && !strcmp(argv[1], "subcontext")) {
- android::base::InitLogging(argv, &android::base::KernelLogger);
- const BuiltinFunctionMap function_map;
- return SubcontextMain(argc, argv, &function_map);
- }
-
+int SecondStageMain(int argc, char** argv) {
if (REBOOT_BOOTLOADER_ON_PANIC) {
InstallRebootSignalHandlers();
}
- if (getenv("SELINUX_INITIALIZED") == nullptr) {
- SetupSelinux(argv);
- }
-
// We need to set up stdin/stdout/stderr again now that we're running in init's context.
InitKernelLogging(argv, InitAborter);
LOG(INFO) << "init second stage started!";
@@ -708,7 +638,6 @@
if (avb_version) property_set("ro.boot.avb_version", avb_version);
// Clean up our environment.
- unsetenv("SELINUX_INITIALIZED");
unsetenv("INIT_STARTED_AT");
unsetenv("INIT_SELINUX_TOOK");
unsetenv("INIT_AVB_VERSION");
diff --git a/init/init.h b/init/init.h
index 65405aa..a76da20 100644
--- a/init/init.h
+++ b/init/init.h
@@ -50,7 +50,7 @@
void ResetWaitForProp();
-int main(int argc, char** argv);
+int SecondStageMain(int argc, char** argv);
} // namespace init
} // namespace android
diff --git a/init/main.cpp b/init/main.cpp
index 9ed451b..868c409 100644
--- a/init/main.cpp
+++ b/init/main.cpp
@@ -14,8 +14,70 @@
* limitations under the License.
*/
+#include "builtins.h"
+#include "first_stage_init.h"
#include "init.h"
+#include "selinux.h"
+#include "subcontext.h"
+#include "ueventd.h"
+
+#include <android-base/logging.h>
+
+#if __has_feature(address_sanitizer)
+#include <sanitizer/asan_interface.h>
+#endif
+
+#if __has_feature(address_sanitizer)
+// Load asan.options if it exists since these are not yet in the environment.
+// Always ensure detect_container_overflow=0 as there are false positives with this check.
+// Always ensure abort_on_error=1 to ensure we reboot to bootloader for development builds.
+extern "C" const char* __asan_default_options() {
+ return "include_if_exists=/system/asan.options:detect_container_overflow=0:abort_on_error=1";
+}
+
+__attribute__((no_sanitize("address", "memory", "thread", "undefined"))) extern "C" void
+__sanitizer_report_error_summary(const char* summary) {
+ LOG(ERROR) << "Init (error summary): " << summary;
+}
+
+__attribute__((no_sanitize("address", "memory", "thread", "undefined"))) static void
+AsanReportCallback(const char* str) {
+ LOG(ERROR) << "Init: " << str;
+}
+#endif
+
+using namespace android::init;
int main(int argc, char** argv) {
- android::init::main(argc, argv);
+#if __has_feature(address_sanitizer)
+ __asan_set_error_report_callback(AsanReportCallback);
+#endif
+
+ if (!strcmp(basename(argv[0]), "ueventd")) {
+ return ueventd_main(argc, argv);
+ }
+
+ if (argc < 2) {
+ return FirstStageMain(argc, argv);
+ }
+
+ if (!strcmp(argv[1], "subcontext")) {
+ android::base::InitLogging(argv, &android::base::KernelLogger);
+ const BuiltinFunctionMap function_map;
+
+ return SubcontextMain(argc, argv, &function_map);
+ }
+
+ if (!strcmp(argv[1], "selinux_setup")) {
+ return SetupSelinux(argv);
+ }
+
+ if (!strcmp(argv[1], "second_stage")) {
+ return SecondStageMain(argc, argv);
+ }
+
+ android::base::InitLogging(argv, &android::base::KernelLogger);
+
+ LOG(ERROR) << "Unknown argument passed to init '" << argv[1] << "'";
+ return 1;
}
diff --git a/init/selinux.cpp b/init/selinux.cpp
index fd7e86f..3a09096 100644
--- a/init/selinux.cpp
+++ b/init/selinux.cpp
@@ -18,8 +18,8 @@
// for SELinux operation for init.
// When the system boots, there is no SEPolicy present and init is running in the kernel domain.
-// Init loads the SEPolicy from the file system, restores the context of /init based on this
-// SEPolicy, and finally exec()'s itself to run in the proper domain.
+// Init loads the SEPolicy from the file system, restores the context of /system/bin/init based on
+// this SEPolicy, and finally exec()'s itself to run in the proper domain.
// The SEPolicy on Android comes in two variants: monolithic and split.
@@ -58,8 +58,10 @@
#include <android-base/logging.h>
#include <android-base/parseint.h>
#include <android-base/unique_fd.h>
+#include <cutils/android_reboot.h>
#include <selinux/android.h>
+#include "reboot_utils.h"
#include "util.h"
using android::base::ParseInt;
@@ -379,8 +381,6 @@
return IsSplitPolicyDevice() ? LoadSplitPolicy() : LoadMonolithicPolicy();
}
-} // namespace
-
void SelinuxInitialize() {
Timer t;
@@ -405,6 +405,8 @@
setenv("INIT_SELINUX_TOOK", std::to_string(t.duration().count()).c_str(), 1);
}
+} // namespace
+
// The files and directories that were created before initial sepolicy load or
// files on ramdisk need to have their security context restored to the proper
// value. This must happen before /dev is populated by ueventd.
@@ -496,6 +498,39 @@
return major_version;
}
+// This function initializes SELinux then execs init to run in the init SELinux context.
+int SetupSelinux(char** argv) {
+ android::base::InitLogging(argv, &android::base::KernelLogger, [](const char*) {
+ RebootSystem(ANDROID_RB_RESTART2, "bootloader");
+ });
+
+ if (REBOOT_BOOTLOADER_ON_PANIC) {
+ InstallRebootSignalHandlers();
+ }
+
+ // Set up SELinux, loading the SELinux policy.
+ SelinuxSetupKernelLogging();
+ SelinuxInitialize();
+
+ // We're in the kernel domain and want to transition to the init domain. File systems that
+ // store SELabels in their xattrs, such as ext4 do not need an explicit restorecon here,
+ // but other file systems do. In particular, this is needed for ramdisks such as the
+ // recovery image for A/B devices.
+ if (selinux_android_restorecon("/system/bin/init", 0) == -1) {
+ PLOG(FATAL) << "restorecon failed of /system/bin/init failed";
+ }
+
+ const char* path = "/system/bin/init";
+ const char* args[] = {path, "second_stage", nullptr};
+ execv(path, const_cast<char**>(args));
+
+ // execv() only returns if an error happened, in which case we
+ // panic and never return from this function.
+ PLOG(FATAL) << "execv(\"" << path << "\") failed";
+
+ return 1;
+}
+
// selinux_android_file_context_handle() takes on the order of 10+ms to run, so we want to cache
// its value. selinux_android_restorecon() also needs an sehandle for file context look up. It
// will create and store its own copy, but selinux_android_set_sehandle() can be used to provide
diff --git a/init/selinux.h b/init/selinux.h
index c41d7f0..3aa9406 100644
--- a/init/selinux.h
+++ b/init/selinux.h
@@ -23,7 +23,7 @@
namespace android {
namespace init {
-void SelinuxInitialize();
+int SetupSelinux(char** argv);
void SelinuxRestoreContext();
void SelinuxSetupKernelLogging();