2008-10-21 16:00:00 +02:00
|
|
|
/*
|
|
|
|
* Copyright (C) 2008 The Android Open Source Project
|
|
|
|
*
|
|
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
* you may not use this file except in compliance with the License.
|
|
|
|
* You may obtain a copy of the License at
|
|
|
|
*
|
|
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
*
|
|
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
* See the License for the specific language governing permissions and
|
|
|
|
* limitations under the License.
|
|
|
|
*/
|
|
|
|
|
2017-04-07 01:30:22 +02:00
|
|
|
#include "init.h"
|
|
|
|
|
2015-02-28 15:39:11 +01:00
|
|
|
#include <dirent.h>
|
2015-02-07 05:15:18 +01:00
|
|
|
#include <fcntl.h>
|
2015-03-18 04:01:13 +01:00
|
|
|
#include <paths.h>
|
2018-02-02 02:14:30 +01:00
|
|
|
#include <pthread.h>
|
2017-07-18 00:14:02 +02:00
|
|
|
#include <seccomp_policy.h>
|
2015-02-07 05:15:18 +01:00
|
|
|
#include <signal.h>
|
2008-10-21 16:00:00 +02:00
|
|
|
#include <stdlib.h>
|
|
|
|
#include <string.h>
|
2015-04-25 06:13:44 +02:00
|
|
|
#include <sys/epoll.h>
|
2008-10-21 16:00:00 +02:00
|
|
|
#include <sys/mount.h>
|
2017-09-06 22:43:57 +02:00
|
|
|
#include <sys/signalfd.h>
|
2016-07-26 18:32:33 +02:00
|
|
|
#include <sys/sysmacros.h>
|
2015-02-07 05:15:18 +01:00
|
|
|
#include <sys/types.h>
|
|
|
|
#include <unistd.h>
|
|
|
|
|
2017-03-24 19:43:02 +01:00
|
|
|
#include <android-base/chrono_utils.h>
|
2015-12-05 07:00:26 +01:00
|
|
|
#include <android-base/file.h>
|
2017-04-07 01:30:22 +02:00
|
|
|
#include <android-base/logging.h>
|
2017-03-29 01:40:41 +02:00
|
|
|
#include <android-base/properties.h>
|
2018-02-21 19:37:44 +01:00
|
|
|
#include <android-base/stringprintf.h>
|
2015-12-05 07:00:26 +01:00
|
|
|
#include <android-base/strings.h>
|
2017-08-18 02:28:30 +02:00
|
|
|
#include <cutils/android_reboot.h>
|
2017-05-10 02:09:06 +02:00
|
|
|
#include <keyutils.h>
|
2017-02-14 15:06:20 +01:00
|
|
|
#include <libavb/libavb.h>
|
2010-04-21 21:04:20 +02:00
|
|
|
#include <private/android_filesystem_config.h>
|
2017-05-10 02:09:06 +02:00
|
|
|
#include <selinux/android.h>
|
2008-10-21 16:00:00 +02:00
|
|
|
|
2015-02-28 15:39:11 +01:00
|
|
|
#include <memory>
|
2017-07-31 22:23:18 +02:00
|
|
|
#include <optional>
|
2015-02-28 15:39:11 +01:00
|
|
|
|
2018-02-14 00:25:29 +01:00
|
|
|
#include "action_parser.h"
|
2015-08-26 20:43:36 +02:00
|
|
|
#include "import_parser.h"
|
2017-04-17 16:17:09 +02:00
|
|
|
#include "init_first_stage.h"
|
2015-07-31 21:45:25 +02:00
|
|
|
#include "keychords.h"
|
2010-04-20 02:05:34 +02:00
|
|
|
#include "log.h"
|
2008-10-21 16:00:00 +02:00
|
|
|
#include "property_service.h"
|
2017-04-18 01:34:20 +02:00
|
|
|
#include "reboot.h"
|
2017-08-10 21:22:44 +02:00
|
|
|
#include "security.h"
|
|
|
|
#include "selinux.h"
|
2017-09-06 22:43:57 +02:00
|
|
|
#include "sigchld_handler.h"
|
2010-04-21 21:04:20 +02:00
|
|
|
#include "ueventd.h"
|
2015-07-31 21:45:25 +02:00
|
|
|
#include "util.h"
|
2012-06-14 06:51:56 +02:00
|
|
|
#include "watchdogd.h"
|
2008-10-21 16:00:00 +02:00
|
|
|
|
2017-06-23 01:50:31 +02:00
|
|
|
using namespace std::string_literals;
|
|
|
|
|
2017-03-24 19:43:02 +01:00
|
|
|
using android::base::boot_clock;
|
2017-03-29 01:40:41 +02:00
|
|
|
using android::base::GetProperty;
|
2018-02-21 19:37:44 +01:00
|
|
|
using android::base::ReadFileToString;
|
|
|
|
using android::base::StringPrintf;
|
2017-07-06 23:20:11 +02:00
|
|
|
using android::base::Timer;
|
2018-02-21 19:37:44 +01:00
|
|
|
using android::base::Trim;
|
2016-11-11 02:43:47 +01:00
|
|
|
|
2017-06-22 21:53:17 +02:00
|
|
|
namespace android {
|
|
|
|
namespace init {
|
|
|
|
|
2008-10-21 16:00:00 +02:00
|
|
|
static int property_triggers_enabled = 0;
|
|
|
|
|
|
|
|
static char qemu[32];
|
|
|
|
|
2016-03-21 09:08:07 +01:00
|
|
|
std::string default_console = "/dev/console";
|
2008-10-21 16:00:00 +02:00
|
|
|
|
2015-04-25 06:13:44 +02:00
|
|
|
static int epoll_fd = -1;
|
2018-04-12 03:46:38 +02:00
|
|
|
static int signal_fd = -1;
|
2015-04-25 06:13:44 +02:00
|
|
|
|
2017-01-26 01:27:03 +01:00
|
|
|
static std::unique_ptr<Timer> waiting_for_prop(nullptr);
|
|
|
|
static std::string wait_prop_name;
|
|
|
|
static std::string wait_prop_value;
|
2017-06-28 07:08:45 +02:00
|
|
|
static bool shutting_down;
|
init: fix crash when reboot is triggered by a builtin
Builtin commands may set the sys.powerctl property, which causes
reboot to be immediately processed. Unfortunately, part of the reboot
processing involves clearing the action queue, so when this scenario
happens, ActionManager::ExecuteOneCommand() can abort due to its state
being unexpectedly changed.
Longer term, the real fix here is to split init and property service.
In this case, the property sets will be sent to property service and
the reboot will only be processed once property service responds back
to init that the property has been set. Since that will not happen
within the action queue, there will be no risk of failure.
Short term, this change sets a flag in init to shutdown the device
before the next action is run, which defers the shutdown enough to fix
the crash, but continues to prevent any further commands from running.
Bug: 65374456
Test: force bullhead into the repro case and observe that it no longer
repros
Change-Id: I89c73dad8d7912a845d694b095cab061b8dcc05e
2017-09-13 23:39:45 +02:00
|
|
|
static std::string shutdown_command;
|
|
|
|
static bool do_shutdown = false;
|
2017-01-26 01:27:03 +01:00
|
|
|
|
2017-07-27 21:54:48 +02:00
|
|
|
std::vector<std::string> late_import_paths;
|
|
|
|
|
2017-09-13 00:58:47 +02:00
|
|
|
static std::vector<Subcontext>* subcontexts;
|
|
|
|
|
2017-04-20 00:31:58 +02:00
|
|
|
void DumpState() {
|
2017-07-28 01:20:58 +02:00
|
|
|
ServiceList::GetInstance().DumpState();
|
2017-04-20 00:31:58 +02:00
|
|
|
ActionManager::GetInstance().DumpState();
|
|
|
|
}
|
|
|
|
|
2017-07-28 01:20:58 +02:00
|
|
|
Parser CreateParser(ActionManager& action_manager, ServiceList& service_list) {
|
2017-07-27 21:54:48 +02:00
|
|
|
Parser parser;
|
|
|
|
|
2017-09-13 00:58:47 +02:00
|
|
|
parser.AddSectionParser("service", std::make_unique<ServiceParser>(&service_list, subcontexts));
|
|
|
|
parser.AddSectionParser("on", std::make_unique<ActionParser>(&action_manager, subcontexts));
|
2017-07-27 21:54:48 +02:00
|
|
|
parser.AddSectionParser("import", std::make_unique<ImportParser>(&parser));
|
|
|
|
|
|
|
|
return parser;
|
|
|
|
}
|
|
|
|
|
2017-07-28 01:20:58 +02:00
|
|
|
static void LoadBootScripts(ActionManager& action_manager, ServiceList& service_list) {
|
|
|
|
Parser parser = CreateParser(action_manager, service_list);
|
2017-07-27 21:54:48 +02:00
|
|
|
|
|
|
|
std::string bootscript = GetProperty("ro.boot.init_rc", "");
|
|
|
|
if (bootscript.empty()) {
|
|
|
|
parser.ParseConfig("/init.rc");
|
|
|
|
if (!parser.ParseConfig("/system/etc/init")) {
|
|
|
|
late_import_paths.emplace_back("/system/etc/init");
|
|
|
|
}
|
2017-11-28 04:10:10 +01:00
|
|
|
if (!parser.ParseConfig("/product/etc/init")) {
|
|
|
|
late_import_paths.emplace_back("/product/etc/init");
|
2017-07-27 21:54:48 +02:00
|
|
|
}
|
|
|
|
if (!parser.ParseConfig("/odm/etc/init")) {
|
|
|
|
late_import_paths.emplace_back("/odm/etc/init");
|
|
|
|
}
|
2017-11-28 04:10:10 +01:00
|
|
|
if (!parser.ParseConfig("/vendor/etc/init")) {
|
|
|
|
late_import_paths.emplace_back("/vendor/etc/init");
|
|
|
|
}
|
2017-07-27 21:54:48 +02:00
|
|
|
} else {
|
|
|
|
parser.ParseConfig(bootscript);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-04-25 06:13:44 +02:00
|
|
|
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) {
|
2016-06-25 00:12:21 +02:00
|
|
|
PLOG(ERROR) << "epoll_ctl failed";
|
2015-04-25 06:13:44 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-02-02 19:52:39 +01:00
|
|
|
bool start_waiting_for_property(const char *name, const char *value)
|
2017-01-26 01:27:03 +01:00
|
|
|
{
|
|
|
|
if (waiting_for_prop) {
|
|
|
|
return false;
|
|
|
|
}
|
2017-03-29 01:40:41 +02:00
|
|
|
if (GetProperty(name, "") != value) {
|
2017-01-26 01:27:03 +01:00
|
|
|
// Current property value is not equal to expected value
|
|
|
|
wait_prop_name = name;
|
|
|
|
wait_prop_value = value;
|
|
|
|
waiting_for_prop.reset(new Timer());
|
|
|
|
} else {
|
2017-02-02 19:52:39 +01:00
|
|
|
LOG(INFO) << "start_waiting_for_property(\""
|
|
|
|
<< name << "\", \"" << value << "\"): already set";
|
2017-01-26 01:27:03 +01:00
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2017-06-28 07:08:45 +02:00
|
|
|
void ResetWaitForProp() {
|
|
|
|
wait_prop_name.clear();
|
|
|
|
wait_prop_value.clear();
|
|
|
|
waiting_for_prop.reset();
|
|
|
|
}
|
|
|
|
|
2017-04-18 01:34:20 +02:00
|
|
|
void property_changed(const std::string& name, const std::string& value) {
|
|
|
|
// If the property is sys.powerctl, we bypass the event queue and immediately handle it.
|
|
|
|
// This is to ensure that init will always and immediately shutdown/reboot, regardless of
|
|
|
|
// if there are other pending events to process or if init is waiting on an exec service or
|
|
|
|
// waiting on a property.
|
2017-06-28 07:08:45 +02:00
|
|
|
// In non-thermal-shutdown case, 'shutdown' trigger will be fired to let device specific
|
|
|
|
// commands to be executed.
|
|
|
|
if (name == "sys.powerctl") {
|
init: fix crash when reboot is triggered by a builtin
Builtin commands may set the sys.powerctl property, which causes
reboot to be immediately processed. Unfortunately, part of the reboot
processing involves clearing the action queue, so when this scenario
happens, ActionManager::ExecuteOneCommand() can abort due to its state
being unexpectedly changed.
Longer term, the real fix here is to split init and property service.
In this case, the property sets will be sent to property service and
the reboot will only be processed once property service responds back
to init that the property has been set. Since that will not happen
within the action queue, there will be no risk of failure.
Short term, this change sets a flag in init to shutdown the device
before the next action is run, which defers the shutdown enough to fix
the crash, but continues to prevent any further commands from running.
Bug: 65374456
Test: force bullhead into the repro case and observe that it no longer
repros
Change-Id: I89c73dad8d7912a845d694b095cab061b8dcc05e
2017-09-13 23:39:45 +02:00
|
|
|
// Despite the above comment, we can't call HandlePowerctlMessage() in this function,
|
|
|
|
// because it modifies the contents of the action queue, which can cause the action queue
|
|
|
|
// to get into a bad state if this function is called from a command being executed by the
|
|
|
|
// action queue. Instead we set this flag and ensure that shutdown happens before the next
|
|
|
|
// command is run in the main init loop.
|
|
|
|
// TODO: once property service is removed from init, this will never happen from a builtin,
|
|
|
|
// but rather from a callback from the property service socket, in which case this hack can
|
|
|
|
// go away.
|
|
|
|
shutdown_command = value;
|
|
|
|
do_shutdown = true;
|
2017-06-28 07:08:45 +02:00
|
|
|
}
|
2017-04-18 01:34:20 +02:00
|
|
|
|
2017-04-17 22:25:29 +02:00
|
|
|
if (property_triggers_enabled) ActionManager::GetInstance().QueuePropertyChange(name, value);
|
|
|
|
|
2017-01-26 01:27:03 +01:00
|
|
|
if (waiting_for_prop) {
|
|
|
|
if (wait_prop_name == name && wait_prop_value == value) {
|
|
|
|
LOG(INFO) << "Wait for property took " << *waiting_for_prop;
|
2017-06-28 07:08:45 +02:00
|
|
|
ResetWaitForProp();
|
2017-01-26 01:27:03 +01:00
|
|
|
}
|
|
|
|
}
|
2008-10-21 16:00:00 +02:00
|
|
|
}
|
|
|
|
|
2017-07-31 22:23:18 +02:00
|
|
|
static std::optional<boot_clock::time_point> RestartProcesses() {
|
|
|
|
std::optional<boot_clock::time_point> next_process_restart_time;
|
2017-07-28 01:20:58 +02:00
|
|
|
for (const auto& s : ServiceList::GetInstance()) {
|
|
|
|
if (!(s->flags() & SVC_RESTARTING)) continue;
|
2017-07-31 22:23:18 +02:00
|
|
|
|
|
|
|
auto restart_time = s->time_started() + 5s;
|
|
|
|
if (boot_clock::now() > restart_time) {
|
2017-08-25 19:36:52 +02:00
|
|
|
if (auto result = s->Start(); !result) {
|
|
|
|
LOG(ERROR) << "Could not restart process '" << s->name() << "': " << result.error();
|
|
|
|
}
|
2017-07-31 22:23:18 +02:00
|
|
|
} else {
|
|
|
|
if (!next_process_restart_time || restart_time < *next_process_restart_time) {
|
|
|
|
next_process_restart_time = restart_time;
|
|
|
|
}
|
|
|
|
}
|
2017-07-28 01:20:58 +02:00
|
|
|
}
|
2017-07-31 22:23:18 +02:00
|
|
|
return next_process_restart_time;
|
2008-10-21 16:00:00 +02:00
|
|
|
}
|
|
|
|
|
2017-10-06 03:50:22 +02:00
|
|
|
static Result<Success> DoControlStart(Service* service) {
|
|
|
|
return service->Start();
|
|
|
|
}
|
|
|
|
|
|
|
|
static Result<Success> DoControlStop(Service* service) {
|
|
|
|
service->Stop();
|
|
|
|
return Success();
|
|
|
|
}
|
|
|
|
|
|
|
|
static Result<Success> DoControlRestart(Service* service) {
|
|
|
|
service->Restart();
|
|
|
|
return Success();
|
|
|
|
}
|
|
|
|
|
|
|
|
enum class ControlTarget {
|
|
|
|
SERVICE, // function gets called for the named service
|
|
|
|
INTERFACE, // action gets called for every service that holds this interface
|
|
|
|
};
|
|
|
|
|
|
|
|
struct ControlMessageFunction {
|
|
|
|
ControlTarget target;
|
|
|
|
std::function<Result<Success>(Service*)> action;
|
|
|
|
};
|
|
|
|
|
|
|
|
static const std::map<std::string, ControlMessageFunction>& get_control_message_map() {
|
|
|
|
// clang-format off
|
|
|
|
static const std::map<std::string, ControlMessageFunction> control_message_functions = {
|
2018-04-17 23:48:44 +02:00
|
|
|
{"sigstop_on", {ControlTarget::SERVICE,
|
|
|
|
[](auto* service) { service->set_sigstop(true); return Success(); }}},
|
|
|
|
{"sigstop_off", {ControlTarget::SERVICE,
|
|
|
|
[](auto* service) { service->set_sigstop(false); return Success(); }}},
|
2017-10-06 03:50:22 +02:00
|
|
|
{"start", {ControlTarget::SERVICE, DoControlStart}},
|
|
|
|
{"stop", {ControlTarget::SERVICE, DoControlStop}},
|
|
|
|
{"restart", {ControlTarget::SERVICE, DoControlRestart}},
|
|
|
|
{"interface_start", {ControlTarget::INTERFACE, DoControlStart}},
|
|
|
|
{"interface_stop", {ControlTarget::INTERFACE, DoControlStop}},
|
|
|
|
{"interface_restart", {ControlTarget::INTERFACE, DoControlRestart}},
|
|
|
|
};
|
|
|
|
// clang-format on
|
|
|
|
|
|
|
|
return control_message_functions;
|
|
|
|
}
|
|
|
|
|
2018-02-21 19:37:44 +01:00
|
|
|
void HandleControlMessage(const std::string& msg, const std::string& name, pid_t pid) {
|
2017-10-06 03:50:22 +02:00
|
|
|
const auto& map = get_control_message_map();
|
|
|
|
const auto it = map.find(msg);
|
|
|
|
|
|
|
|
if (it == map.end()) {
|
|
|
|
LOG(ERROR) << "Unknown control msg '" << msg << "'";
|
2016-04-13 00:38:27 +02:00
|
|
|
return;
|
2012-01-26 05:48:46 +01:00
|
|
|
}
|
|
|
|
|
2018-02-21 19:37:44 +01:00
|
|
|
std::string cmdline_path = StringPrintf("proc/%d/cmdline", pid);
|
|
|
|
std::string process_cmdline;
|
|
|
|
if (ReadFileToString(cmdline_path, &process_cmdline)) {
|
|
|
|
std::replace(process_cmdline.begin(), process_cmdline.end(), '\0', ' ');
|
|
|
|
process_cmdline = Trim(process_cmdline);
|
|
|
|
} else {
|
|
|
|
process_cmdline = "unknown process";
|
|
|
|
}
|
|
|
|
|
|
|
|
LOG(INFO) << "Received control message '" << msg << "' for '" << name << "' from pid: " << pid
|
|
|
|
<< " (" << process_cmdline << ")";
|
|
|
|
|
2017-10-06 03:50:22 +02:00
|
|
|
const ControlMessageFunction& function = it->second;
|
|
|
|
|
|
|
|
if (function.target == ControlTarget::SERVICE) {
|
|
|
|
Service* svc = ServiceList::GetInstance().FindService(name);
|
|
|
|
if (svc == nullptr) {
|
|
|
|
LOG(ERROR) << "No such service '" << name << "' for ctl." << msg;
|
|
|
|
return;
|
2017-08-25 19:36:52 +02:00
|
|
|
}
|
2017-10-06 03:50:22 +02:00
|
|
|
if (auto result = function.action(svc); !result) {
|
|
|
|
LOG(ERROR) << "Could not ctl." << msg << " for service " << name << ": "
|
|
|
|
<< result.error();
|
|
|
|
}
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (function.target == ControlTarget::INTERFACE) {
|
|
|
|
for (const auto& svc : ServiceList::GetInstance()) {
|
|
|
|
if (svc->interfaces().count(name) == 0) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (auto result = function.action(svc.get()); !result) {
|
|
|
|
LOG(ERROR) << "Could not handle ctl." << msg << " for service " << svc->name()
|
|
|
|
<< " with interface " << name << ": " << result.error();
|
|
|
|
}
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
LOG(ERROR) << "Could not find service hosting interface " << name;
|
|
|
|
return;
|
2008-10-21 16:00:00 +02:00
|
|
|
}
|
2017-10-06 03:50:22 +02:00
|
|
|
|
|
|
|
LOG(ERROR) << "Invalid function target from static map key '" << msg
|
|
|
|
<< "': " << static_cast<std::underlying_type<ControlTarget>::type>(function.target);
|
2008-10-21 16:00:00 +02:00
|
|
|
}
|
|
|
|
|
2017-09-13 00:58:47 +02:00
|
|
|
static Result<Success> wait_for_coldboot_done_action(const BuiltinArguments& args) {
|
2015-04-25 03:50:30 +02:00
|
|
|
Timer t;
|
|
|
|
|
2016-06-25 00:12:21 +02:00
|
|
|
LOG(VERBOSE) << "Waiting for " COLDBOOT_DONE "...";
|
2016-11-29 20:20:58 +01:00
|
|
|
|
|
|
|
// Historically we had a 1s timeout here because we weren't otherwise
|
|
|
|
// tracking boot time, and many OEMs made their sepolicy regular
|
|
|
|
// expressions too expensive (http://b/19899875).
|
|
|
|
|
|
|
|
// Now we're tracking boot time, just log the time taken to a system
|
|
|
|
// property. We still panic if it takes more than a minute though,
|
|
|
|
// because any build that slow isn't likely to boot at all, and we'd
|
|
|
|
// rather any test lab devices fail back to the bootloader.
|
|
|
|
if (wait_for_file(COLDBOOT_DONE, 60s) < 0) {
|
2017-08-18 02:28:30 +02:00
|
|
|
LOG(FATAL) << "Timed out waiting for " COLDBOOT_DONE;
|
2015-04-25 03:50:30 +02:00
|
|
|
}
|
|
|
|
|
2017-07-06 23:20:11 +02:00
|
|
|
property_set("ro.boottime.init.cold_boot_wait", std::to_string(t.duration().count()));
|
2017-08-01 22:50:23 +02:00
|
|
|
return Success();
|
2010-04-14 04:52:01 +02:00
|
|
|
}
|
|
|
|
|
2017-09-13 00:58:47 +02:00
|
|
|
static Result<Success> keychord_init_action(const BuiltinArguments& args) {
|
2010-04-14 04:52:01 +02:00
|
|
|
keychord_init();
|
2017-08-01 22:50:23 +02:00
|
|
|
return Success();
|
2010-04-14 04:52:01 +02:00
|
|
|
}
|
|
|
|
|
2017-09-13 00:58:47 +02:00
|
|
|
static Result<Success> console_init_action(const BuiltinArguments& args) {
|
2017-03-29 01:40:41 +02:00
|
|
|
std::string console = GetProperty("ro.boot.console", "");
|
2016-03-21 09:23:43 +01:00
|
|
|
if (!console.empty()) {
|
2016-03-21 09:08:07 +01:00
|
|
|
default_console = "/dev/" + console;
|
2010-04-14 04:52:01 +02:00
|
|
|
}
|
2017-08-01 22:50:23 +02:00
|
|
|
return Success();
|
2010-04-14 04:52:01 +02:00
|
|
|
}
|
|
|
|
|
2015-05-07 04:19:24 +02:00
|
|
|
static void import_kernel_nv(const std::string& key, const std::string& value, bool for_emulator) {
|
|
|
|
if (key.empty()) return;
|
2011-12-19 20:21:32 +01:00
|
|
|
|
|
|
|
if (for_emulator) {
|
2015-05-07 04:19:24 +02:00
|
|
|
// In the emulator, export any kernel option with the "ro.kernel." prefix.
|
2017-06-23 01:50:31 +02:00
|
|
|
property_set("ro.kernel." + key, value);
|
2011-12-19 20:21:32 +01:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2015-05-07 04:19:24 +02:00
|
|
|
if (key == "qemu") {
|
|
|
|
strlcpy(qemu, value.c_str(), sizeof(qemu));
|
|
|
|
} else if (android::base::StartsWith(key, "androidboot.")) {
|
2017-06-23 01:50:31 +02:00
|
|
|
property_set("ro.boot." + key.substr(12), value);
|
2011-12-19 20:21:32 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-12-07 13:33:58 +01:00
|
|
|
static void export_oem_lock_status() {
|
2017-03-29 01:40:41 +02:00
|
|
|
if (!android::base::GetBoolProperty("ro.oem_unlock_supported", false)) {
|
2015-12-07 13:33:58 +01:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2017-03-29 01:40:41 +02:00
|
|
|
std::string value = GetProperty("ro.boot.verifiedbootstate", "");
|
2015-12-07 13:33:58 +01:00
|
|
|
|
|
|
|
if (!value.empty()) {
|
|
|
|
property_set("ro.boot.flash.locked", value == "orange" ? "0" : "1");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-03-20 16:50:46 +01:00
|
|
|
static void export_kernel_boot_props() {
|
2011-12-19 20:21:32 +01:00
|
|
|
struct {
|
|
|
|
const char *src_prop;
|
2015-03-20 16:50:46 +01:00
|
|
|
const char *dst_prop;
|
|
|
|
const char *default_value;
|
2011-12-19 20:21:32 +01:00
|
|
|
} prop_map[] = {
|
2015-03-20 16:50:46 +01:00
|
|
|
{ "ro.boot.serialno", "ro.serialno", "", },
|
|
|
|
{ "ro.boot.mode", "ro.bootmode", "unknown", },
|
|
|
|
{ "ro.boot.baseband", "ro.baseband", "unknown", },
|
2011-12-19 20:21:32 +01:00
|
|
|
{ "ro.boot.bootloader", "ro.bootloader", "unknown", },
|
2015-03-20 16:50:46 +01:00
|
|
|
{ "ro.boot.hardware", "ro.hardware", "unknown", },
|
|
|
|
{ "ro.boot.revision", "ro.revision", "0", },
|
2011-12-19 20:21:32 +01:00
|
|
|
};
|
2016-06-25 00:12:21 +02:00
|
|
|
for (size_t i = 0; i < arraysize(prop_map); i++) {
|
2017-03-29 01:40:41 +02:00
|
|
|
std::string value = GetProperty(prop_map[i].src_prop, "");
|
2017-06-23 01:50:31 +02:00
|
|
|
property_set(prop_map[i].dst_prop, (!value.empty()) ? value : prop_map[i].default_value);
|
2011-12-19 20:21:32 +01:00
|
|
|
}
|
|
|
|
}
|
2010-04-14 04:52:01 +02:00
|
|
|
|
2017-02-10 04:19:31 +01:00
|
|
|
static void process_kernel_dt() {
|
2017-04-17 16:17:09 +02:00
|
|
|
if (!is_android_dt_value_expected("compatible", "android,firmware")) {
|
|
|
|
return;
|
|
|
|
}
|
2017-02-10 04:19:31 +01:00
|
|
|
|
Allow the use of a custom Android DT directory
On platforms that use ACPI instead of Device Tree (DT), such as
Ranchu x86/x86_64, /proc/device-tree/firmware/android/ does not
exist. As a result, Android O is unable to mount /system, etc.
at the first stage of init:
init: First stage mount skipped (missing/incompatible fstab in
device tree)
Those platforms may create another directory that mimics the layout
of the standard DT directory in procfs, and store early mount
configuration there. E.g., Ranchu x86/x86_64 creates one in sysfs
using information encoded in the ACPI tables:
https://android-review.googlesource.com/442472
https://android-review.googlesource.com/443432
https://android-review.googlesource.com/442393
https://android-review.googlesource.com/442395
Therefore, instead of hardcoding the Android DT path, load it from
the kernel command line using a new Android-specific property key
("androidboot.android_dt_dir"). If no such property exists, fall
back to the standard procfs path (so no change is needed for DT-
aware platforms).
Note that init/ and fs_mgr/ each have their own copy of the Android
DT path, because they do not share any global state. A future CL
should remove the duplication by refactoring.
With this CL as well as the above ones, the said warning is gone,
but early mount fails. That is a separate bug, though, and will be
addressed by another CL.
Test: Boot patched sdk_phone_x86-userdebug system image with patched
Goldfish 3.18 x86 kernel in patched Android Emulator, verify
the "init: First stage mount skipped" warning no longer shows
in dmesg.
Change-Id: Ib6df577319503ec1ca778de2b5458cc72ce07415
Signed-off-by: Yu Ning <yu.ning@intel.com>
2017-07-26 11:54:08 +02:00
|
|
|
std::unique_ptr<DIR, int (*)(DIR*)> dir(opendir(get_android_dt_dir().c_str()), closedir);
|
2015-05-07 04:19:24 +02:00
|
|
|
if (!dir) return;
|
2015-02-28 15:39:11 +01:00
|
|
|
|
2017-02-10 04:19:31 +01:00
|
|
|
std::string dt_file;
|
2015-02-28 15:39:11 +01:00
|
|
|
struct dirent *dp;
|
|
|
|
while ((dp = readdir(dir.get())) != NULL) {
|
2015-11-09 02:51:50 +01:00
|
|
|
if (dp->d_type != DT_REG || !strcmp(dp->d_name, "compatible") || !strcmp(dp->d_name, "name")) {
|
2015-02-28 15:39:11 +01:00
|
|
|
continue;
|
2015-05-07 04:19:24 +02:00
|
|
|
}
|
2015-02-28 15:39:11 +01:00
|
|
|
|
Allow the use of a custom Android DT directory
On platforms that use ACPI instead of Device Tree (DT), such as
Ranchu x86/x86_64, /proc/device-tree/firmware/android/ does not
exist. As a result, Android O is unable to mount /system, etc.
at the first stage of init:
init: First stage mount skipped (missing/incompatible fstab in
device tree)
Those platforms may create another directory that mimics the layout
of the standard DT directory in procfs, and store early mount
configuration there. E.g., Ranchu x86/x86_64 creates one in sysfs
using information encoded in the ACPI tables:
https://android-review.googlesource.com/442472
https://android-review.googlesource.com/443432
https://android-review.googlesource.com/442393
https://android-review.googlesource.com/442395
Therefore, instead of hardcoding the Android DT path, load it from
the kernel command line using a new Android-specific property key
("androidboot.android_dt_dir"). If no such property exists, fall
back to the standard procfs path (so no change is needed for DT-
aware platforms).
Note that init/ and fs_mgr/ each have their own copy of the Android
DT path, because they do not share any global state. A future CL
should remove the duplication by refactoring.
With this CL as well as the above ones, the said warning is gone,
but early mount fails. That is a separate bug, though, and will be
addressed by another CL.
Test: Boot patched sdk_phone_x86-userdebug system image with patched
Goldfish 3.18 x86 kernel in patched Android Emulator, verify
the "init: First stage mount skipped" warning no longer shows
in dmesg.
Change-Id: Ib6df577319503ec1ca778de2b5458cc72ce07415
Signed-off-by: Yu Ning <yu.ning@intel.com>
2017-07-26 11:54:08 +02:00
|
|
|
std::string file_name = get_android_dt_dir() + dp->d_name;
|
2015-02-28 15:39:11 +01:00
|
|
|
|
|
|
|
android::base::ReadFileToString(file_name, &dt_file);
|
|
|
|
std::replace(dt_file.begin(), dt_file.end(), ',', '.');
|
|
|
|
|
2017-06-23 01:50:31 +02:00
|
|
|
property_set("ro.boot."s + dp->d_name, dt_file);
|
2015-02-28 15:39:11 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-05-07 04:19:24 +02:00
|
|
|
static void process_kernel_cmdline() {
|
|
|
|
// The first pass does the common stuff, and finds if we are in qemu.
|
|
|
|
// The second pass is only necessary for qemu to export all kernel params
|
|
|
|
// as properties.
|
2015-04-26 02:42:52 +02:00
|
|
|
import_kernel_cmdline(false, import_kernel_nv);
|
2015-05-07 04:19:24 +02:00
|
|
|
if (qemu[0]) import_kernel_cmdline(true, import_kernel_nv);
|
2010-04-14 04:52:01 +02:00
|
|
|
}
|
|
|
|
|
2017-09-13 00:58:47 +02:00
|
|
|
static Result<Success> property_enable_triggers_action(const BuiltinArguments& args) {
|
2016-11-26 14:14:07 +01:00
|
|
|
/* Enable property triggers. */
|
|
|
|
property_triggers_enabled = 1;
|
2017-08-01 22:50:23 +02:00
|
|
|
return Success();
|
2016-11-26 14:14:07 +01:00
|
|
|
}
|
|
|
|
|
2017-09-13 00:58:47 +02:00
|
|
|
static Result<Success> queue_property_triggers_action(const BuiltinArguments& args) {
|
2016-11-26 14:14:07 +01:00
|
|
|
ActionManager::GetInstance().QueueBuiltinAction(property_enable_triggers_action, "enable_property_trigger");
|
2017-04-17 22:25:29 +02:00
|
|
|
ActionManager::GetInstance().QueueAllPropertyActions();
|
2017-08-01 22:50:23 +02:00
|
|
|
return Success();
|
2010-04-14 04:52:01 +02:00
|
|
|
}
|
|
|
|
|
2017-07-18 00:14:02 +02:00
|
|
|
static void global_seccomp() {
|
|
|
|
import_kernel_cmdline(false, [](const std::string& key, const std::string& value, bool in_qemu) {
|
|
|
|
if (key == "androidboot.seccomp" && value == "global" && !set_global_seccomp_filter()) {
|
2017-08-18 02:28:30 +02:00
|
|
|
LOG(FATAL) << "Failed to globally enable seccomp!";
|
2017-07-18 00:14:02 +02:00
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2016-06-29 15:30:00 +02:00
|
|
|
// Set the UDC controller for the ConfigFS USB Gadgets.
|
|
|
|
// Read the UDC controller in use from "/sys/class/udc".
|
|
|
|
// In case of multiple UDC controllers select the first one.
|
|
|
|
static void set_usb_controller() {
|
|
|
|
std::unique_ptr<DIR, decltype(&closedir)>dir(opendir("/sys/class/udc"), closedir);
|
|
|
|
if (!dir) return;
|
|
|
|
|
|
|
|
dirent* dp;
|
|
|
|
while ((dp = readdir(dir.get())) != nullptr) {
|
|
|
|
if (dp->d_name[0] == '.') continue;
|
|
|
|
|
|
|
|
property_set("sys.usb.controller", dp->d_name);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-08-26 00:10:48 +02:00
|
|
|
static void InstallRebootSignalHandlers() {
|
2017-03-10 23:46:38 +01:00
|
|
|
// Instead of panic'ing the kernel as is the default behavior when init crashes,
|
|
|
|
// we prefer to reboot to bootloader on development builds, as this will prevent
|
|
|
|
// boot looping bad configurations and allow both developers and test farms to easily
|
|
|
|
// recover.
|
|
|
|
struct sigaction action;
|
|
|
|
memset(&action, 0, sizeof(action));
|
|
|
|
sigfillset(&action.sa_mask);
|
2017-08-26 00:10:48 +02:00
|
|
|
action.sa_handler = [](int signal) {
|
|
|
|
// These signal handlers are also caught for processes forked from init, however we do not
|
|
|
|
// want them to trigger reboot, so we directly call _exit() for children processes here.
|
|
|
|
if (getpid() != 1) {
|
|
|
|
_exit(signal);
|
|
|
|
}
|
|
|
|
|
2017-08-18 02:28:30 +02:00
|
|
|
// Calling DoReboot() or LOG(FATAL) is not a good option as this is a signal handler.
|
|
|
|
// RebootSystem uses syscall() which isn't actually async-signal-safe, but our only option
|
|
|
|
// and probably good enough given this is already an error case and only enabled for
|
|
|
|
// development builds.
|
|
|
|
RebootSystem(ANDROID_RB_RESTART2, "bootloader");
|
2017-03-10 23:46:38 +01:00
|
|
|
};
|
|
|
|
action.sa_flags = SA_RESTART;
|
|
|
|
sigaction(SIGABRT, &action, nullptr);
|
|
|
|
sigaction(SIGBUS, &action, nullptr);
|
|
|
|
sigaction(SIGFPE, &action, nullptr);
|
|
|
|
sigaction(SIGILL, &action, nullptr);
|
|
|
|
sigaction(SIGSEGV, &action, nullptr);
|
|
|
|
#if defined(SIGSTKFLT)
|
|
|
|
sigaction(SIGSTKFLT, &action, nullptr);
|
|
|
|
#endif
|
|
|
|
sigaction(SIGSYS, &action, nullptr);
|
|
|
|
sigaction(SIGTRAP, &action, nullptr);
|
|
|
|
}
|
|
|
|
|
2018-04-12 03:46:38 +02:00
|
|
|
static void HandleSigtermSignal(const signalfd_siginfo& siginfo) {
|
2017-09-06 22:43:57 +02:00
|
|
|
if (siginfo.ssi_pid != 0) {
|
|
|
|
// Drop any userspace SIGTERM requests.
|
|
|
|
LOG(DEBUG) << "Ignoring SIGTERM from pid " << siginfo.ssi_pid;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2017-09-26 17:26:12 +02:00
|
|
|
HandlePowerctlMessage("shutdown,container");
|
2017-09-06 22:43:57 +02:00
|
|
|
}
|
|
|
|
|
2018-04-12 03:46:38 +02:00
|
|
|
static void HandleSignalFd() {
|
|
|
|
signalfd_siginfo siginfo;
|
|
|
|
ssize_t bytes_read = TEMP_FAILURE_RETRY(read(signal_fd, &siginfo, sizeof(siginfo)));
|
|
|
|
if (bytes_read != sizeof(siginfo)) {
|
|
|
|
PLOG(ERROR) << "Failed to read siginfo from signal_fd";
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
switch (siginfo.ssi_signo) {
|
|
|
|
case SIGCHLD:
|
|
|
|
ReapAnyOutstandingChildren();
|
|
|
|
break;
|
|
|
|
case SIGTERM:
|
|
|
|
HandleSigtermSignal(siginfo);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
PLOG(ERROR) << "signal_fd: received unexpected signal " << siginfo.ssi_signo;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void UnblockSignals() {
|
|
|
|
const struct sigaction act { .sa_handler = SIG_DFL };
|
|
|
|
sigaction(SIGCHLD, &act, nullptr);
|
|
|
|
|
2018-02-02 02:14:30 +01:00
|
|
|
sigset_t mask;
|
|
|
|
sigemptyset(&mask);
|
2018-04-12 03:46:38 +02:00
|
|
|
sigaddset(&mask, SIGCHLD);
|
2018-02-02 02:14:30 +01:00
|
|
|
sigaddset(&mask, SIGTERM);
|
|
|
|
|
|
|
|
if (sigprocmask(SIG_UNBLOCK, &mask, nullptr) == -1) {
|
2018-04-12 03:46:38 +02:00
|
|
|
PLOG(FATAL) << "failed to unblock signals for PID " << getpid();
|
2018-02-02 02:14:30 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-04-12 03:46:38 +02:00
|
|
|
static void InstallSignalFdHandler() {
|
|
|
|
// Applying SA_NOCLDSTOP to a defaulted SIGCHLD handler prevents the signalfd from receiving
|
|
|
|
// SIGCHLD when a child process stops or continues (b/77867680#comment9).
|
|
|
|
const struct sigaction act { .sa_handler = SIG_DFL, .sa_flags = SA_NOCLDSTOP };
|
|
|
|
sigaction(SIGCHLD, &act, nullptr);
|
|
|
|
|
2017-09-06 22:43:57 +02:00
|
|
|
sigset_t mask;
|
|
|
|
sigemptyset(&mask);
|
2018-04-12 03:46:38 +02:00
|
|
|
sigaddset(&mask, SIGCHLD);
|
|
|
|
|
|
|
|
if (!IsRebootCapable()) {
|
|
|
|
// If init does not have the CAP_SYS_BOOT capability, it is running in a container.
|
|
|
|
// In that case, receiving SIGTERM will cause the system to shut down.
|
|
|
|
sigaddset(&mask, SIGTERM);
|
|
|
|
}
|
2017-09-06 22:43:57 +02:00
|
|
|
|
|
|
|
if (sigprocmask(SIG_BLOCK, &mask, nullptr) == -1) {
|
2018-04-12 03:46:38 +02:00
|
|
|
PLOG(FATAL) << "failed to block signals";
|
2017-09-06 22:43:57 +02:00
|
|
|
}
|
|
|
|
|
2018-04-12 03:46:38 +02:00
|
|
|
// Register a handler to unblock signals in the child processes.
|
|
|
|
const int result = pthread_atfork(nullptr, nullptr, &UnblockSignals);
|
2018-02-02 02:14:30 +01:00
|
|
|
if (result != 0) {
|
|
|
|
LOG(FATAL) << "Failed to register a fork handler: " << strerror(result);
|
|
|
|
}
|
|
|
|
|
2018-04-12 03:46:38 +02:00
|
|
|
signal_fd = signalfd(-1, &mask, SFD_CLOEXEC);
|
|
|
|
if (signal_fd == -1) {
|
|
|
|
PLOG(FATAL) << "failed to create signalfd";
|
2017-09-06 22:43:57 +02:00
|
|
|
}
|
|
|
|
|
2018-04-12 03:46:38 +02:00
|
|
|
register_epoll_handler(signal_fd, HandleSignalFd);
|
2017-09-06 22:43:57 +02:00
|
|
|
}
|
|
|
|
|
2015-02-07 05:15:18 +01:00
|
|
|
int main(int argc, char** argv) {
|
2015-03-28 07:20:44 +01:00
|
|
|
if (!strcmp(basename(argv[0]), "ueventd")) {
|
2010-04-21 21:04:20 +02:00
|
|
|
return ueventd_main(argc, argv);
|
2015-03-28 07:20:44 +01:00
|
|
|
}
|
2010-04-21 21:04:20 +02:00
|
|
|
|
2015-03-28 07:20:44 +01:00
|
|
|
if (!strcmp(basename(argv[0]), "watchdogd")) {
|
2012-06-14 06:51:56 +02:00
|
|
|
return watchdogd_main(argc, argv);
|
2015-03-28 07:20:44 +01:00
|
|
|
}
|
2012-06-14 06:51:56 +02:00
|
|
|
|
2017-09-13 00:58:47 +02:00
|
|
|
if (argc > 1 && !strcmp(argv[1], "subcontext")) {
|
|
|
|
InitKernelLogging(argv);
|
|
|
|
const BuiltinFunctionMap function_map;
|
|
|
|
return SubcontextMain(argc, argv, &function_map);
|
|
|
|
}
|
|
|
|
|
2017-03-10 23:46:38 +01:00
|
|
|
if (REBOOT_BOOTLOADER_ON_PANIC) {
|
2017-08-26 00:10:48 +02:00
|
|
|
InstallRebootSignalHandlers();
|
2017-03-10 23:46:38 +01:00
|
|
|
}
|
|
|
|
|
2016-11-11 02:43:47 +01:00
|
|
|
bool is_first_stage = (getenv("INIT_SECOND_STAGE") == nullptr);
|
2015-04-26 00:50:03 +02:00
|
|
|
|
|
|
|
if (is_first_stage) {
|
init: cleanup is_first_stage conditionals
A recent change to the is_first_stage conditionals created a unneeded
else { } block as both the code in the else { } block and any code
that runs after it are both in the second stage of init. A first step
to clean this up is to remove this else block.
Secondly, given the above confusion, it makes sense to simplify the two
if (is_first_stage) conditions into one, which only now requires
duplicating one line to initialize logging and the actual "init
first/second stage started!" logs.
Lastly, there are a few commands ran at the beginning of both init
stages that do not need to be,
* boot_clock::time_point start_time = boot_clock::now();
This is only used in the first stage so keep it there
* umask(0);
umasks are preserved across execve() so it only needs to be set in the
first stage
* chmod("/proc/cmdline", 0440);
This needs to be moved until after /proc is mounted in the first
stage, but otherwise only needs to be done once
Test: Boot bullhead, check umask, check cmdline permissions, check
boot time property
Change-Id: Idb7df1d4330960ce282d9609f5c62281ee2638b9
2017-03-17 02:08:56 +01:00
|
|
|
boot_clock::time_point start_time = boot_clock::now();
|
|
|
|
|
|
|
|
// Clear the umask.
|
|
|
|
umask(0);
|
|
|
|
|
2017-08-23 00:41:03 +02:00
|
|
|
clearenv();
|
|
|
|
setenv("PATH", _PATH_DEFPATH, 1);
|
init: cleanup is_first_stage conditionals
A recent change to the is_first_stage conditionals created a unneeded
else { } block as both the code in the else { } block and any code
that runs after it are both in the second stage of init. A first step
to clean this up is to remove this else block.
Secondly, given the above confusion, it makes sense to simplify the two
if (is_first_stage) conditions into one, which only now requires
duplicating one line to initialize logging and the actual "init
first/second stage started!" logs.
Lastly, there are a few commands ran at the beginning of both init
stages that do not need to be,
* boot_clock::time_point start_time = boot_clock::now();
This is only used in the first stage so keep it there
* umask(0);
umasks are preserved across execve() so it only needs to be set in the
first stage
* chmod("/proc/cmdline", 0440);
This needs to be moved until after /proc is mounted in the first
stage, but otherwise only needs to be done once
Test: Boot bullhead, check umask, check cmdline permissions, check
boot time property
Change-Id: Idb7df1d4330960ce282d9609f5c62281ee2638b9
2017-03-17 02:08:56 +01:00
|
|
|
// 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.
|
2015-04-26 02:42:52 +02:00
|
|
|
mount("tmpfs", "/dev", "tmpfs", MS_NOSUID, "mode=0755");
|
|
|
|
mkdir("/dev/pts", 0755);
|
|
|
|
mkdir("/dev/socket", 0755);
|
|
|
|
mount("devpts", "/dev/pts", "devpts", 0, NULL);
|
2015-11-08 01:52:17 +01:00
|
|
|
#define MAKE_STR(x) __STRING(x)
|
|
|
|
mount("proc", "/proc", "proc", 0, "hidepid=2,gid=" MAKE_STR(AID_READPROC));
|
init: cleanup is_first_stage conditionals
A recent change to the is_first_stage conditionals created a unneeded
else { } block as both the code in the else { } block and any code
that runs after it are both in the second stage of init. A first step
to clean this up is to remove this else block.
Secondly, given the above confusion, it makes sense to simplify the two
if (is_first_stage) conditions into one, which only now requires
duplicating one line to initialize logging and the actual "init
first/second stage started!" logs.
Lastly, there are a few commands ran at the beginning of both init
stages that do not need to be,
* boot_clock::time_point start_time = boot_clock::now();
This is only used in the first stage so keep it there
* umask(0);
umasks are preserved across execve() so it only needs to be set in the
first stage
* chmod("/proc/cmdline", 0440);
This needs to be moved until after /proc is mounted in the first
stage, but otherwise only needs to be done once
Test: Boot bullhead, check umask, check cmdline permissions, check
boot time property
Change-Id: Idb7df1d4330960ce282d9609f5c62281ee2638b9
2017-03-17 02:08:56 +01:00
|
|
|
// Don't expose the raw commandline to unprivileged processes.
|
|
|
|
chmod("/proc/cmdline", 0440);
|
2016-10-29 21:20:00 +02:00
|
|
|
gid_t groups[] = { AID_READPROC };
|
|
|
|
setgroups(arraysize(groups), groups);
|
2015-04-26 00:50:03 +02:00
|
|
|
mount("sysfs", "/sys", "sysfs", 0, NULL);
|
2016-03-03 19:40:12 +01:00
|
|
|
mount("selinuxfs", "/sys/fs/selinux", "selinuxfs", 0, NULL);
|
2017-03-28 22:07:15 +02:00
|
|
|
|
2016-07-26 18:32:33 +02:00
|
|
|
mknod("/dev/kmsg", S_IFCHR | 0600, makedev(1, 11));
|
2017-03-28 22:07:15 +02:00
|
|
|
|
|
|
|
if constexpr (WORLD_WRITABLE_KMSG) {
|
2017-08-11 22:22:37 +02:00
|
|
|
mknod("/dev/kmsg_debug", S_IFCHR | 0622, makedev(1, 11));
|
2017-03-28 22:07:15 +02:00
|
|
|
}
|
|
|
|
|
2016-11-18 23:58:40 +01:00
|
|
|
mknod("/dev/random", S_IFCHR | 0666, makedev(1, 8));
|
|
|
|
mknod("/dev/urandom", S_IFCHR | 0666, makedev(1, 9));
|
2008-10-21 16:00:00 +02:00
|
|
|
|
2018-04-11 05:20:13 +02:00
|
|
|
// Mount staging areas for devices managed by vold
|
|
|
|
// See storage config details at http://source.android.com/devices/storage/
|
|
|
|
mount("tmpfs", "/mnt", "tmpfs", MS_NOEXEC | MS_NOSUID | MS_NODEV,
|
|
|
|
"mode=0755,uid=0,gid=1000");
|
|
|
|
// /mnt/vendor is used to mount vendor-specific partitions that can not be
|
|
|
|
// part of the vendor partition, e.g. because they are mounted read-write.
|
|
|
|
mkdir("/mnt/vendor", 0755);
|
|
|
|
|
init: cleanup is_first_stage conditionals
A recent change to the is_first_stage conditionals created a unneeded
else { } block as both the code in the else { } block and any code
that runs after it are both in the second stage of init. A first step
to clean this up is to remove this else block.
Secondly, given the above confusion, it makes sense to simplify the two
if (is_first_stage) conditions into one, which only now requires
duplicating one line to initialize logging and the actual "init
first/second stage started!" logs.
Lastly, there are a few commands ran at the beginning of both init
stages that do not need to be,
* boot_clock::time_point start_time = boot_clock::now();
This is only used in the first stage so keep it there
* umask(0);
umasks are preserved across execve() so it only needs to be set in the
first stage
* chmod("/proc/cmdline", 0440);
This needs to be moved until after /proc is mounted in the first
stage, but otherwise only needs to be done once
Test: Boot bullhead, check umask, check cmdline permissions, check
boot time property
Change-Id: Idb7df1d4330960ce282d9609f5c62281ee2638b9
2017-03-17 02:08:56 +01:00
|
|
|
// Now that tmpfs is mounted on /dev and we have /dev/kmsg, we can actually
|
|
|
|
// talk to the outside world...
|
|
|
|
InitKernelLogging(argv);
|
2015-03-28 07:20:44 +01:00
|
|
|
|
init: cleanup is_first_stage conditionals
A recent change to the is_first_stage conditionals created a unneeded
else { } block as both the code in the else { } block and any code
that runs after it are both in the second stage of init. A first step
to clean this up is to remove this else block.
Secondly, given the above confusion, it makes sense to simplify the two
if (is_first_stage) conditions into one, which only now requires
duplicating one line to initialize logging and the actual "init
first/second stage started!" logs.
Lastly, there are a few commands ran at the beginning of both init
stages that do not need to be,
* boot_clock::time_point start_time = boot_clock::now();
This is only used in the first stage so keep it there
* umask(0);
umasks are preserved across execve() so it only needs to be set in the
first stage
* chmod("/proc/cmdline", 0440);
This needs to be moved until after /proc is mounted in the first
stage, but otherwise only needs to be done once
Test: Boot bullhead, check umask, check cmdline permissions, check
boot time property
Change-Id: Idb7df1d4330960ce282d9609f5c62281ee2638b9
2017-03-17 02:08:56 +01:00
|
|
|
LOG(INFO) << "init first stage started!";
|
2016-10-28 21:22:32 +02:00
|
|
|
|
2017-04-17 16:17:09 +02:00
|
|
|
if (!DoFirstStageMount()) {
|
2017-08-18 02:28:30 +02:00
|
|
|
LOG(FATAL) << "Failed to mount required partitions early ...";
|
2017-02-09 05:27:12 +01:00
|
|
|
}
|
2016-10-28 21:22:32 +02:00
|
|
|
|
2017-04-24 12:36:25 +02:00
|
|
|
SetInitAvbVersionInRecovery();
|
|
|
|
|
2017-07-18 00:14:02 +02:00
|
|
|
// Enable seccomp if global boot option was passed (otherwise it is enabled in zygote).
|
|
|
|
global_seccomp();
|
|
|
|
|
2016-11-11 02:43:47 +01:00
|
|
|
// Set up SELinux, loading the SELinux policy.
|
2017-08-10 21:22:44 +02:00
|
|
|
SelinuxSetupKernelLogging();
|
|
|
|
SelinuxInitialize();
|
2016-10-28 21:22:32 +02:00
|
|
|
|
2016-11-11 02:43:47 +01:00
|
|
|
// We're in the kernel domain, so re-exec init to transition to the init domain now
|
2016-10-28 21:22:32 +02:00
|
|
|
// that the SELinux policy has been loaded.
|
2017-05-08 22:38:15 +02:00
|
|
|
if (selinux_android_restorecon("/init", 0) == -1) {
|
2017-08-18 02:28:30 +02:00
|
|
|
PLOG(FATAL) << "restorecon failed of /init failed";
|
2016-10-28 21:22:32 +02:00
|
|
|
}
|
2016-11-11 02:43:47 +01:00
|
|
|
|
|
|
|
setenv("INIT_SECOND_STAGE", "true", 1);
|
|
|
|
|
2017-01-26 20:55:44 +01:00
|
|
|
static constexpr uint32_t kNanosecondsPerMillisecond = 1e6;
|
|
|
|
uint64_t start_ms = start_time.time_since_epoch().count() / kNanosecondsPerMillisecond;
|
2017-06-23 01:50:31 +02:00
|
|
|
setenv("INIT_STARTED_AT", std::to_string(start_ms).c_str(), 1);
|
2016-11-11 02:43:47 +01:00
|
|
|
|
2016-10-28 21:22:32 +02:00
|
|
|
char* path = argv[0];
|
2016-11-11 02:43:47 +01:00
|
|
|
char* args[] = { path, nullptr };
|
init: cleanup is_first_stage conditionals
A recent change to the is_first_stage conditionals created a unneeded
else { } block as both the code in the else { } block and any code
that runs after it are both in the second stage of init. A first step
to clean this up is to remove this else block.
Secondly, given the above confusion, it makes sense to simplify the two
if (is_first_stage) conditions into one, which only now requires
duplicating one line to initialize logging and the actual "init
first/second stage started!" logs.
Lastly, there are a few commands ran at the beginning of both init
stages that do not need to be,
* boot_clock::time_point start_time = boot_clock::now();
This is only used in the first stage so keep it there
* umask(0);
umasks are preserved across execve() so it only needs to be set in the
first stage
* chmod("/proc/cmdline", 0440);
This needs to be moved until after /proc is mounted in the first
stage, but otherwise only needs to be done once
Test: Boot bullhead, check umask, check cmdline permissions, check
boot time property
Change-Id: Idb7df1d4330960ce282d9609f5c62281ee2638b9
2017-03-17 02:08:56 +01:00
|
|
|
execv(path, args);
|
2011-09-28 18:55:31 +02:00
|
|
|
|
init: cleanup is_first_stage conditionals
A recent change to the is_first_stage conditionals created a unneeded
else { } block as both the code in the else { } block and any code
that runs after it are both in the second stage of init. A first step
to clean this up is to remove this else block.
Secondly, given the above confusion, it makes sense to simplify the two
if (is_first_stage) conditions into one, which only now requires
duplicating one line to initialize logging and the actual "init
first/second stage started!" logs.
Lastly, there are a few commands ran at the beginning of both init
stages that do not need to be,
* boot_clock::time_point start_time = boot_clock::now();
This is only used in the first stage so keep it there
* umask(0);
umasks are preserved across execve() so it only needs to be set in the
first stage
* chmod("/proc/cmdline", 0440);
This needs to be moved until after /proc is mounted in the first
stage, but otherwise only needs to be done once
Test: Boot bullhead, check umask, check cmdline permissions, check
boot time property
Change-Id: Idb7df1d4330960ce282d9609f5c62281ee2638b9
2017-03-17 02:08:56 +01:00
|
|
|
// execv() only returns if an error happened, in which case we
|
|
|
|
// panic and never fall through this conditional.
|
2017-08-18 02:28:30 +02:00
|
|
|
PLOG(FATAL) << "execv(\"" << path << "\") failed";
|
init: cleanup is_first_stage conditionals
A recent change to the is_first_stage conditionals created a unneeded
else { } block as both the code in the else { } block and any code
that runs after it are both in the second stage of init. A first step
to clean this up is to remove this else block.
Secondly, given the above confusion, it makes sense to simplify the two
if (is_first_stage) conditions into one, which only now requires
duplicating one line to initialize logging and the actual "init
first/second stage started!" logs.
Lastly, there are a few commands ran at the beginning of both init
stages that do not need to be,
* boot_clock::time_point start_time = boot_clock::now();
This is only used in the first stage so keep it there
* umask(0);
umasks are preserved across execve() so it only needs to be set in the
first stage
* chmod("/proc/cmdline", 0440);
This needs to be moved until after /proc is mounted in the first
stage, but otherwise only needs to be done once
Test: Boot bullhead, check umask, check cmdline permissions, check
boot time property
Change-Id: Idb7df1d4330960ce282d9609f5c62281ee2638b9
2017-03-17 02:08:56 +01:00
|
|
|
}
|
2011-12-19 20:21:32 +01:00
|
|
|
|
init: cleanup is_first_stage conditionals
A recent change to the is_first_stage conditionals created a unneeded
else { } block as both the code in the else { } block and any code
that runs after it are both in the second stage of init. A first step
to clean this up is to remove this else block.
Secondly, given the above confusion, it makes sense to simplify the two
if (is_first_stage) conditions into one, which only now requires
duplicating one line to initialize logging and the actual "init
first/second stage started!" logs.
Lastly, there are a few commands ran at the beginning of both init
stages that do not need to be,
* boot_clock::time_point start_time = boot_clock::now();
This is only used in the first stage so keep it there
* umask(0);
umasks are preserved across execve() so it only needs to be set in the
first stage
* chmod("/proc/cmdline", 0440);
This needs to be moved until after /proc is mounted in the first
stage, but otherwise only needs to be done once
Test: Boot bullhead, check umask, check cmdline permissions, check
boot time property
Change-Id: Idb7df1d4330960ce282d9609f5c62281ee2638b9
2017-03-17 02:08:56 +01:00
|
|
|
// At this point we're in the second stage of init.
|
|
|
|
InitKernelLogging(argv);
|
|
|
|
LOG(INFO) << "init second stage started!";
|
2015-04-26 02:42:52 +02:00
|
|
|
|
2017-05-02 23:44:39 +02:00
|
|
|
// Set up a session keyring that all processes will have access to. It
|
|
|
|
// will hold things like FBE encryption keys. No process should override
|
|
|
|
// its session keyring.
|
2017-05-10 02:09:06 +02:00
|
|
|
keyctl_get_keyring_ID(KEY_SPEC_SESSION_KEYRING, 1);
|
2017-05-02 23:44:39 +02:00
|
|
|
|
init: cleanup is_first_stage conditionals
A recent change to the is_first_stage conditionals created a unneeded
else { } block as both the code in the else { } block and any code
that runs after it are both in the second stage of init. A first step
to clean this up is to remove this else block.
Secondly, given the above confusion, it makes sense to simplify the two
if (is_first_stage) conditions into one, which only now requires
duplicating one line to initialize logging and the actual "init
first/second stage started!" logs.
Lastly, there are a few commands ran at the beginning of both init
stages that do not need to be,
* boot_clock::time_point start_time = boot_clock::now();
This is only used in the first stage so keep it there
* umask(0);
umasks are preserved across execve() so it only needs to be set in the
first stage
* chmod("/proc/cmdline", 0440);
This needs to be moved until after /proc is mounted in the first
stage, but otherwise only needs to be done once
Test: Boot bullhead, check umask, check cmdline permissions, check
boot time property
Change-Id: Idb7df1d4330960ce282d9609f5c62281ee2638b9
2017-03-17 02:08:56 +01:00
|
|
|
// Indicate that booting is in progress to background fw loaders, etc.
|
|
|
|
close(open("/dev/.booting", O_WRONLY | O_CREAT | O_CLOEXEC, 0000));
|
2015-02-28 15:39:11 +01:00
|
|
|
|
init: cleanup is_first_stage conditionals
A recent change to the is_first_stage conditionals created a unneeded
else { } block as both the code in the else { } block and any code
that runs after it are both in the second stage of init. A first step
to clean this up is to remove this else block.
Secondly, given the above confusion, it makes sense to simplify the two
if (is_first_stage) conditions into one, which only now requires
duplicating one line to initialize logging and the actual "init
first/second stage started!" logs.
Lastly, there are a few commands ran at the beginning of both init
stages that do not need to be,
* boot_clock::time_point start_time = boot_clock::now();
This is only used in the first stage so keep it there
* umask(0);
umasks are preserved across execve() so it only needs to be set in the
first stage
* chmod("/proc/cmdline", 0440);
This needs to be moved until after /proc is mounted in the first
stage, but otherwise only needs to be done once
Test: Boot bullhead, check umask, check cmdline permissions, check
boot time property
Change-Id: Idb7df1d4330960ce282d9609f5c62281ee2638b9
2017-03-17 02:08:56 +01:00
|
|
|
property_init();
|
2016-11-29 20:20:58 +01:00
|
|
|
|
init: cleanup is_first_stage conditionals
A recent change to the is_first_stage conditionals created a unneeded
else { } block as both the code in the else { } block and any code
that runs after it are both in the second stage of init. A first step
to clean this up is to remove this else block.
Secondly, given the above confusion, it makes sense to simplify the two
if (is_first_stage) conditions into one, which only now requires
duplicating one line to initialize logging and the actual "init
first/second stage started!" logs.
Lastly, there are a few commands ran at the beginning of both init
stages that do not need to be,
* boot_clock::time_point start_time = boot_clock::now();
This is only used in the first stage so keep it there
* umask(0);
umasks are preserved across execve() so it only needs to be set in the
first stage
* chmod("/proc/cmdline", 0440);
This needs to be moved until after /proc is mounted in the first
stage, but otherwise only needs to be done once
Test: Boot bullhead, check umask, check cmdline permissions, check
boot time property
Change-Id: Idb7df1d4330960ce282d9609f5c62281ee2638b9
2017-03-17 02:08:56 +01:00
|
|
|
// 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();
|
2017-02-14 15:06:20 +01:00
|
|
|
|
init: cleanup is_first_stage conditionals
A recent change to the is_first_stage conditionals created a unneeded
else { } block as both the code in the else { } block and any code
that runs after it are both in the second stage of init. A first step
to clean this up is to remove this else block.
Secondly, given the above confusion, it makes sense to simplify the two
if (is_first_stage) conditions into one, which only now requires
duplicating one line to initialize logging and the actual "init
first/second stage started!" logs.
Lastly, there are a few commands ran at the beginning of both init
stages that do not need to be,
* boot_clock::time_point start_time = boot_clock::now();
This is only used in the first stage so keep it there
* umask(0);
umasks are preserved across execve() so it only needs to be set in the
first stage
* chmod("/proc/cmdline", 0440);
This needs to be moved until after /proc is mounted in the first
stage, but otherwise only needs to be done once
Test: Boot bullhead, check umask, check cmdline permissions, check
boot time property
Change-Id: Idb7df1d4330960ce282d9609f5c62281ee2638b9
2017-03-17 02:08:56 +01:00
|
|
|
// Propagate the kernel variables to internal variables
|
|
|
|
// used by init as well as the current required properties.
|
|
|
|
export_kernel_boot_props();
|
2016-11-11 02:43:47 +01:00
|
|
|
|
init: cleanup is_first_stage conditionals
A recent change to the is_first_stage conditionals created a unneeded
else { } block as both the code in the else { } block and any code
that runs after it are both in the second stage of init. A first step
to clean this up is to remove this else block.
Secondly, given the above confusion, it makes sense to simplify the two
if (is_first_stage) conditions into one, which only now requires
duplicating one line to initialize logging and the actual "init
first/second stage started!" logs.
Lastly, there are a few commands ran at the beginning of both init
stages that do not need to be,
* boot_clock::time_point start_time = boot_clock::now();
This is only used in the first stage so keep it there
* umask(0);
umasks are preserved across execve() so it only needs to be set in the
first stage
* chmod("/proc/cmdline", 0440);
This needs to be moved until after /proc is mounted in the first
stage, but otherwise only needs to be done once
Test: Boot bullhead, check umask, check cmdline permissions, check
boot time property
Change-Id: Idb7df1d4330960ce282d9609f5c62281ee2638b9
2017-03-17 02:08:56 +01:00
|
|
|
// Make the time that init started available for bootstat to log.
|
|
|
|
property_set("ro.boottime.init", getenv("INIT_STARTED_AT"));
|
|
|
|
property_set("ro.boottime.init.selinux", getenv("INIT_SELINUX_TOOK"));
|
|
|
|
|
|
|
|
// Set libavb version for Framework-only OTA match in Treble build.
|
2017-04-13 15:17:48 +02:00
|
|
|
const char* avb_version = getenv("INIT_AVB_VERSION");
|
|
|
|
if (avb_version) property_set("ro.boot.avb_version", avb_version);
|
init: cleanup is_first_stage conditionals
A recent change to the is_first_stage conditionals created a unneeded
else { } block as both the code in the else { } block and any code
that runs after it are both in the second stage of init. A first step
to clean this up is to remove this else block.
Secondly, given the above confusion, it makes sense to simplify the two
if (is_first_stage) conditions into one, which only now requires
duplicating one line to initialize logging and the actual "init
first/second stage started!" logs.
Lastly, there are a few commands ran at the beginning of both init
stages that do not need to be,
* boot_clock::time_point start_time = boot_clock::now();
This is only used in the first stage so keep it there
* umask(0);
umasks are preserved across execve() so it only needs to be set in the
first stage
* chmod("/proc/cmdline", 0440);
This needs to be moved until after /proc is mounted in the first
stage, but otherwise only needs to be done once
Test: Boot bullhead, check umask, check cmdline permissions, check
boot time property
Change-Id: Idb7df1d4330960ce282d9609f5c62281ee2638b9
2017-03-17 02:08:56 +01:00
|
|
|
|
|
|
|
// Clean up our environment.
|
|
|
|
unsetenv("INIT_SECOND_STAGE");
|
|
|
|
unsetenv("INIT_STARTED_AT");
|
|
|
|
unsetenv("INIT_SELINUX_TOOK");
|
2017-04-13 15:17:48 +02:00
|
|
|
unsetenv("INIT_AVB_VERSION");
|
init: cleanup is_first_stage conditionals
A recent change to the is_first_stage conditionals created a unneeded
else { } block as both the code in the else { } block and any code
that runs after it are both in the second stage of init. A first step
to clean this up is to remove this else block.
Secondly, given the above confusion, it makes sense to simplify the two
if (is_first_stage) conditions into one, which only now requires
duplicating one line to initialize logging and the actual "init
first/second stage started!" logs.
Lastly, there are a few commands ran at the beginning of both init
stages that do not need to be,
* boot_clock::time_point start_time = boot_clock::now();
This is only used in the first stage so keep it there
* umask(0);
umasks are preserved across execve() so it only needs to be set in the
first stage
* chmod("/proc/cmdline", 0440);
This needs to be moved until after /proc is mounted in the first
stage, but otherwise only needs to be done once
Test: Boot bullhead, check umask, check cmdline permissions, check
boot time property
Change-Id: Idb7df1d4330960ce282d9609f5c62281ee2638b9
2017-03-17 02:08:56 +01:00
|
|
|
|
|
|
|
// Now set up SELinux for second stage.
|
2017-08-10 21:22:44 +02:00
|
|
|
SelinuxSetupKernelLogging();
|
|
|
|
SelabelInitialize();
|
|
|
|
SelinuxRestoreContext();
|
2012-01-13 14:48:47 +01:00
|
|
|
|
2015-04-25 06:13:44 +02:00
|
|
|
epoll_fd = epoll_create1(EPOLL_CLOEXEC);
|
|
|
|
if (epoll_fd == -1) {
|
2017-09-27 01:30:03 +02:00
|
|
|
PLOG(FATAL) << "epoll_create1 failed";
|
2015-04-25 06:13:44 +02:00
|
|
|
}
|
|
|
|
|
2018-04-12 03:46:38 +02:00
|
|
|
InstallSignalFdHandler();
|
2015-04-25 02:43:21 +02:00
|
|
|
|
2014-06-17 00:06:21 +02:00
|
|
|
property_load_boot_defaults();
|
2015-12-07 13:33:58 +01:00
|
|
|
export_oem_lock_status();
|
2015-04-25 03:50:30 +02:00
|
|
|
start_property_service();
|
2016-06-29 15:30:00 +02:00
|
|
|
set_usb_controller();
|
2011-12-16 23:18:06 +01:00
|
|
|
|
2015-08-26 20:43:36 +02:00
|
|
|
const BuiltinFunctionMap function_map;
|
|
|
|
Action::set_function_map(&function_map);
|
|
|
|
|
2017-09-13 00:58:47 +02:00
|
|
|
subcontexts = InitializeSubcontexts();
|
|
|
|
|
2017-04-20 00:31:58 +02:00
|
|
|
ActionManager& am = ActionManager::GetInstance();
|
2017-07-28 01:20:58 +02:00
|
|
|
ServiceList& sm = ServiceList::GetInstance();
|
2017-04-20 00:31:58 +02:00
|
|
|
|
2017-07-27 21:54:48 +02:00
|
|
|
LoadBootScripts(am, sm);
|
2008-10-21 16:00:00 +02:00
|
|
|
|
2017-03-13 20:24:49 +01:00
|
|
|
// Turning this on and letting the INFO logging be discarded adds 0.2s to
|
|
|
|
// Nexus 9 boot time, so it's disabled by default.
|
2017-04-20 00:31:58 +02:00
|
|
|
if (false) DumpState();
|
2015-07-24 02:53:11 +02:00
|
|
|
|
|
|
|
am.QueueEventTrigger("early-init");
|
2008-10-21 16:00:00 +02:00
|
|
|
|
2015-04-25 03:50:30 +02:00
|
|
|
// Queue an action that waits for coldboot done so we know ueventd has set up all of /dev...
|
2015-07-24 02:53:11 +02:00
|
|
|
am.QueueBuiltinAction(wait_for_coldboot_done_action, "wait_for_coldboot_done");
|
2015-04-25 03:50:30 +02:00
|
|
|
// ... so that we can start queuing up actions that require stuff from /dev.
|
2017-08-10 21:22:44 +02:00
|
|
|
am.QueueBuiltinAction(MixHwrngIntoLinuxRngAction, "MixHwrngIntoLinuxRng");
|
|
|
|
am.QueueBuiltinAction(SetMmapRndBitsAction, "SetMmapRndBits");
|
|
|
|
am.QueueBuiltinAction(SetKptrRestrictAction, "SetKptrRestrict");
|
2015-07-24 02:53:11 +02:00
|
|
|
am.QueueBuiltinAction(keychord_init_action, "keychord_init");
|
|
|
|
am.QueueBuiltinAction(console_init_action, "console_init");
|
2008-10-21 16:00:00 +02:00
|
|
|
|
2015-03-28 07:20:44 +01:00
|
|
|
// Trigger all the boot actions to get us started.
|
2015-07-24 02:53:11 +02:00
|
|
|
am.QueueEventTrigger("init");
|
2011-08-25 00:28:23 +02:00
|
|
|
|
2015-02-07 05:15:18 +01:00
|
|
|
// Repeat mix_hwrng_into_linux_rng in case /dev/hw_random or /dev/random
|
|
|
|
// wasn't ready immediately after wait_for_coldboot_done
|
2017-08-10 21:22:44 +02:00
|
|
|
am.QueueBuiltinAction(MixHwrngIntoLinuxRngAction, "MixHwrngIntoLinuxRng");
|
2008-10-21 16:00:00 +02:00
|
|
|
|
2015-02-07 05:15:18 +01:00
|
|
|
// Don't mount filesystems or start core system services in charger mode.
|
2017-03-29 01:40:41 +02:00
|
|
|
std::string bootmode = GetProperty("ro.bootmode", "");
|
2015-07-24 19:11:05 +02:00
|
|
|
if (bootmode == "charger") {
|
2015-07-24 02:53:11 +02:00
|
|
|
am.QueueEventTrigger("charger");
|
2011-08-25 00:28:23 +02:00
|
|
|
} else {
|
2015-07-24 02:53:11 +02:00
|
|
|
am.QueueEventTrigger("late-init");
|
2011-08-25 00:28:23 +02:00
|
|
|
}
|
2008-10-21 16:00:00 +02:00
|
|
|
|
2015-02-07 05:15:18 +01:00
|
|
|
// Run all property triggers based on current state of the properties.
|
2015-07-24 02:53:11 +02:00
|
|
|
am.QueueBuiltinAction(queue_property_triggers_action, "queue_property_triggers");
|
2010-04-14 04:52:01 +02:00
|
|
|
|
2015-04-25 03:50:30 +02:00
|
|
|
while (true) {
|
2017-03-24 00:54:38 +01:00
|
|
|
// By default, sleep until something happens.
|
|
|
|
int epoll_timeout_ms = -1;
|
|
|
|
|
init: fix crash when reboot is triggered by a builtin
Builtin commands may set the sys.powerctl property, which causes
reboot to be immediately processed. Unfortunately, part of the reboot
processing involves clearing the action queue, so when this scenario
happens, ActionManager::ExecuteOneCommand() can abort due to its state
being unexpectedly changed.
Longer term, the real fix here is to split init and property service.
In this case, the property sets will be sent to property service and
the reboot will only be processed once property service responds back
to init that the property has been set. Since that will not happen
within the action queue, there will be no risk of failure.
Short term, this change sets a flag in init to shutdown the device
before the next action is run, which defers the shutdown enough to fix
the crash, but continues to prevent any further commands from running.
Bug: 65374456
Test: force bullhead into the repro case and observe that it no longer
repros
Change-Id: I89c73dad8d7912a845d694b095cab061b8dcc05e
2017-09-13 23:39:45 +02:00
|
|
|
if (do_shutdown && !shutting_down) {
|
|
|
|
do_shutdown = false;
|
|
|
|
if (HandlePowerctlMessage(shutdown_command)) {
|
|
|
|
shutting_down = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-07-28 23:48:41 +02:00
|
|
|
if (!(waiting_for_prop || Service::is_exec_service_running())) {
|
2015-07-24 02:53:11 +02:00
|
|
|
am.ExecuteOneCommand();
|
2015-02-07 05:15:18 +01:00
|
|
|
}
|
2017-07-28 23:48:41 +02:00
|
|
|
if (!(waiting_for_prop || Service::is_exec_service_running())) {
|
2017-07-31 22:23:18 +02:00
|
|
|
if (!shutting_down) {
|
|
|
|
auto next_process_restart_time = RestartProcesses();
|
|
|
|
|
|
|
|
// If there's a process that needs restarting, wake up in time for that.
|
|
|
|
if (next_process_restart_time) {
|
|
|
|
epoll_timeout_ms = std::chrono::ceil<std::chrono::milliseconds>(
|
|
|
|
*next_process_restart_time - boot_clock::now())
|
|
|
|
.count();
|
|
|
|
if (epoll_timeout_ms < 0) epoll_timeout_ms = 0;
|
|
|
|
}
|
2017-03-24 00:54:38 +01:00
|
|
|
}
|
2016-11-11 02:43:47 +01:00
|
|
|
|
2017-03-24 00:54:38 +01:00
|
|
|
// If there's more work to do, wake up again immediately.
|
|
|
|
if (am.HasMoreCommands()) epoll_timeout_ms = 0;
|
2015-02-04 23:46:36 +01:00
|
|
|
}
|
2010-04-14 04:52:01 +02:00
|
|
|
|
2015-04-25 06:13:44 +02:00
|
|
|
epoll_event ev;
|
2016-11-11 02:43:47 +01:00
|
|
|
int nr = TEMP_FAILURE_RETRY(epoll_wait(epoll_fd, &ev, 1, epoll_timeout_ms));
|
2015-04-25 06:13:44 +02:00
|
|
|
if (nr == -1) {
|
2016-06-25 00:12:21 +02:00
|
|
|
PLOG(ERROR) << "epoll_wait failed";
|
2015-04-25 06:13:44 +02:00
|
|
|
} else if (nr == 1) {
|
|
|
|
((void (*)()) ev.data.ptr)();
|
2008-10-21 16:00:00 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
2017-06-22 21:53:17 +02:00
|
|
|
|
|
|
|
} // namespace init
|
|
|
|
} // namespace android
|