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