1 /*
2 * Copyright (C) 2020 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include "first_stage_console.h"
18
19 #include <spawn.h>
20 #include <stdio.h>
21 #include <sys/stat.h>
22 #include <sys/sysmacros.h>
23 #include <sys/types.h>
24 #include <sys/wait.h>
25 #include <termios.h>
26
27 #include <string>
28 #include <thread>
29
30 #include <android-base/chrono_utils.h>
31 #include <android-base/file.h>
32 #include <android-base/logging.h>
33
KernelConsolePresent(const std::string & cmdline)34 static bool KernelConsolePresent(const std::string& cmdline) {
35 size_t pos = 0;
36 while (true) {
37 pos = cmdline.find("console=", pos);
38 if (pos == std::string::npos) return false;
39 if (pos == 0 || cmdline[pos - 1] == ' ') return true;
40 pos++;
41 }
42 }
43
SetupConsole()44 static bool SetupConsole() {
45 if (mknod("/dev/console", S_IFCHR | 0600, makedev(5, 1))) {
46 PLOG(ERROR) << "unable to create /dev/console";
47 return false;
48 }
49 int fd = -1;
50 int tries = 50; // should timeout after 5s
51 // The device driver for console may not be ready yet so retry for a while in case of failure.
52 while (tries--) {
53 fd = open("/dev/console", O_RDWR);
54 if (fd != -1) break;
55 std::this_thread::sleep_for(100ms);
56 }
57 if (fd == -1) {
58 PLOG(ERROR) << "could not open /dev/console";
59 return false;
60 }
61 ioctl(fd, TIOCSCTTY, 0);
62 dup2(fd, STDIN_FILENO);
63 dup2(fd, STDOUT_FILENO);
64 dup2(fd, STDERR_FILENO);
65 close(fd);
66 return true;
67 }
68
SpawnImage(const char * file)69 static pid_t SpawnImage(const char* file) {
70 const char* argv[] = {file, NULL};
71 const char* envp[] = {NULL};
72
73 char* const* argvp = const_cast<char* const*>(argv);
74 char* const* envpp = const_cast<char* const*>(envp);
75
76 pid_t pid;
77 errno = posix_spawn(&pid, argv[0], NULL, NULL, argvp, envpp);
78 if (!errno) return pid;
79
80 PLOG(ERROR) << "Failed to spawn '" << file << "'";
81
82 return (pid_t)0;
83 }
84
85 namespace android {
86 namespace init {
87
StartConsole(const std::string & cmdline)88 void StartConsole(const std::string& cmdline) {
89 bool console = KernelConsolePresent(cmdline);
90 // Use a simple sigchld handler -- first_stage_console doesn't need to track or log zombies
91 const struct sigaction chld_act {
92 .sa_flags = SA_NOCLDWAIT, .sa_handler = SIG_DFL
93 };
94
95 sigaction(SIGCHLD, &chld_act, nullptr);
96 pid_t pid = fork();
97 if (pid != 0) {
98 wait(NULL);
99 LOG(ERROR) << "console shell exited";
100 return;
101 }
102
103 if (console) console = SetupConsole();
104
105 LOG(INFO) << "Attempting to run /first_stage.sh...";
106 if (SpawnImage("/first_stage.sh")) {
107 wait(NULL);
108 LOG(INFO) << "/first_stage.sh exited";
109 }
110
111 if (console) {
112 if (SpawnImage("/system/bin/sh")) wait(NULL);
113 }
114 _exit(127);
115 }
116
FirstStageConsole(const std::string & cmdline,const std::string & bootconfig)117 int FirstStageConsole(const std::string& cmdline, const std::string& bootconfig) {
118 auto pos = bootconfig.find("androidboot.first_stage_console =");
119 if (pos != std::string::npos) {
120 int val = 0;
121 if (sscanf(bootconfig.c_str() + pos, "androidboot.first_stage_console = \"%d\"", &val) !=
122 1) {
123 return FirstStageConsoleParam::DISABLED;
124 }
125 if (val <= FirstStageConsoleParam::MAX_PARAM_VALUE && val >= 0) {
126 return val;
127 }
128 }
129
130 pos = cmdline.find("androidboot.first_stage_console=");
131 if (pos != std::string::npos) {
132 int val = 0;
133 if (sscanf(cmdline.c_str() + pos, "androidboot.first_stage_console=%d", &val) != 1) {
134 return FirstStageConsoleParam::DISABLED;
135 }
136 if (val <= FirstStageConsoleParam::MAX_PARAM_VALUE && val >= 0) {
137 return val;
138 }
139 }
140 return FirstStageConsoleParam::DISABLED;
141 }
142
143 } // namespace init
144 } // namespace android
145