The Pedigree Project 0.1
modules/system/init/main.cc
1/*
2 * Copyright (c) 2008-2014, Pedigree Developers
3 *
4 * Please see the CONTRIB file in the root of the source tree for a full
5 * list of contributors.
6 *
7 * Permission to use, copy, modify, and distribute this software for any
8 * purpose with or without fee is hereby granted, provided that the above
9 * copyright notice and this permission notice appear in all copies.
10 *
11 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18 */
19
20#include "pedigree/kernel/Log.h"
21#include "pedigree/kernel/Subsystem.h"
22#include "pedigree/kernel/compiler.h"
23#include "pedigree/kernel/core/BootIO.h"
24#include "pedigree/kernel/process/Process.h"
25#include "pedigree/kernel/process/Thread.h"
26#include "pedigree/kernel/processor/Processor.h"
27#include "pedigree/kernel/processor/ProcessorInformation.h"
28
29#include "modules/Module.h"
30#include "modules/subsys/posix/FileDescriptor.h"
31#include "modules/subsys/posix/PosixProcess.h"
32#include "modules/subsys/posix/PosixSubsystem.h"
33#include "modules/subsys/posix/ResolvedPath.h"
34#include "modules/system/vfs/MountView.h"
35#include "modules/system/vfs/VFS.h"
36#if HOSTED
37#include "pedigree/kernel/processor/hosted/smoke.h"
38#endif
39#include "pedigree/kernel/utilities/StaticString.h"
40#include "pedigree/kernel/utilities/String.h"
41#include "pedigree/kernel/utilities/Vector.h"
42#include "pedigree/kernel/utilities/new"
43
44#include <fcntl.h>
45
46class File;
47
48static Thread* g_pStage2Thread = 0;
49
50static void error(const char* s) {
51 extern BootIO bootIO;
52 static HugeStaticString str;
53 str += s;
54 str += "\n";
55 bootIO.write(str, BootIO::Red, BootIO::Black);
56 str.clear();
57}
58
59static File* resolveBootPath(const String& name, ResolvedPath& result) {
60 auto context =
61 Processor::information().getCurrentThread()->getParent()->acquireFilesystemContext();
62 auto* view = VFS::instance().mountView();
63 FilesystemPathRef selected;
65 if (!view || !context || !view->resolve(context, FilesystemPathRef(), name, options, selected))
66 return nullptr;
67 result.retain(selected);
68 return result.get();
69}
70
71static int init_stage2(void* param) {
72 EMIT_IF(HOSTED) {
73 if (!HOSTED_SMOKE_TESTS) {
74 extern void system_reset();
75 NOTICE("Hosted build has no smoke-test command; shutting down.");
76 system_reset();
77 return 0;
78 }
79
80#if HOSTED
81 if (g_HostedSmokeStage == HostedSmokeRoot) {
82 extern void system_reset();
83 NOTICE("HOSTED-SMOKE: root mounted");
84 system_reset();
85 return 0;
86 }
87#endif
88 }
89
90 bool tryingLinux = false;
91 bool directHostedSmokeCommand = false;
92
93 ResolvedPath executable;
94 File* file = 0;
95
96 String init_path;
97#if HOSTED
98 directHostedSmokeCommand = HOSTED_SMOKE_TESTS && ((g_HostedSmokeStage == HostedSmokeCommand) ||
99 (g_HostedSmokeStage == HostedSmokeShutdown));
100#endif
101 init_path.assign(directHostedSmokeCommand ? "/usr/bin/hosted-smoke-command" : "/usr/bin/init");
102 NOTICE("Searching for userspace program at " << init_path);
103 file = resolveBootPath(init_path, executable);
104 if (!file && !directHostedSmokeCommand) {
105 WARNING("Did not find " << init_path << ", trying for a Linux userspace...");
106 init_path.assign("/sbin/init");
107 tryingLinux = true;
108
109 NOTICE("Searching for Linux init at " << init_path);
110 file = resolveBootPath(init_path, executable);
111 }
112
113 if (!file) {
114 error(directHostedSmokeCommand
115 ? "failed to find hosted smoke command"
116 : "failed to find init program (tried /usr/bin/init and /sbin/init)");
117 return 1;
118 }
119
120 NOTICE("Found a userspace program at " << init_path);
121
122 Vector<String> argv, env;
123 argv.pushBack(init_path);
124
125#if HOSTED
126 if (!tryingLinux && HOSTED_SMOKE_TESTS) {
127 switch (g_HostedSmokeStage) {
128 case HostedSmokeInit:
129 argv.pushBack(String("init"));
130 break;
131 case HostedSmokeCommand:
132 argv.pushBack(String("command"));
133 break;
134 case HostedSmokeShutdown:
135 argv.pushBack(String("shutdown"));
136 break;
137 default:
138 break;
139 }
140 }
141#endif
142
143 if (tryingLinux) {
144 // Jump to runlevel 5
145 argv.pushBack(String("5"));
146 }
147
148 Process* pProcess = Processor::information().getCurrentThread()->getParent();
149 Process::setInit(pProcess);
150
151 NOTICE("Invoking userspace program at " << init_path);
152 if (!pProcess->getSubsystem()->invoke(init_path.cstr(), argv, env)) {
153 error("failed to load userspace program");
154 }
155
156 return 0;
157}
158
159static bool init() {
160#if THREADS
161 EMIT_IF(HOSTED) {
162 if (!HOSTED_SMOKE_TESTS) {
163 extern void system_reset();
164 NOTICE("Hosted build has no smoke-test command; shutting down.");
165 system_reset();
166 return true;
167 }
168 }
169
170 // Resolve the only fallible prerequisite before a PosixProcess constructor
171 // registers its hardware IntervalTimer callback.
172 ResolvedPath nullPath;
173 auto context =
174 Processor::information().getCurrentThread()->getParent()->acquireFilesystemContext();
175 auto* view = VFS::instance().mountView();
176 FilesystemPathRef selectedNull;
178 if (!view || !context ||
179 !view->resolve(context, FilesystemPathRef(), String("/dev/null"), options, selectedNull)) {
180 error("/dev/null does not exist");
181 return false;
182 }
183
184 nullPath.retain(selectedNull);
185
186 // Create a new process for the init process.
187 PosixProcess* pProcess =
188 new PosixProcess(Processor::information().getCurrentThread()->getParent());
189
190 if (!pProcess || !pProcess->jobControlReady() || !pProcess->filesystemContextReady()) {
191 delete pProcess;
192 error("Unable to initialise the process session");
193 return false;
194 }
195
196 Process* bootstrap = Processor::information().getCurrentThread()->getParent();
197 if (!pProcess->installUserIdentity(bootstrap->getUser(), bootstrap->getGroup(), nullptr, 0)) {
198 delete pProcess;
199 error("Unable to initialise the process identity");
200 return false;
201 }
202
203 pProcess->description() = "init";
204 pProcess->setCttyContext(SharedPointer<Process::ControllingTerminal>());
205
206 PosixSubsystem* pSubsystem = new PosixSubsystem;
207 if (!pSubsystem) {
208 delete pProcess;
209 error("Unable to initialise the process subsystem");
210 return false;
211 }
212 pProcess->setSubsystem(pSubsystem);
213
214 // add an empty stdout, stdin
215 FileDescriptor* stdinDescriptor = new FileDescriptor(nullPath.path(), 0, 0, 0, O_RDONLY);
216 FileDescriptor* stdoutDescriptor = new FileDescriptor(nullPath.path(), 0, 1, 0, O_WRONLY);
217
218 pSubsystem->addFileDescriptor(0, stdinDescriptor);
219 pSubsystem->addFileDescriptor(1, stdoutDescriptor);
220
222 if (pSubsystem->traceContext().prepareTask(preparedTrace) != TraceStatus::Success) {
223 delete pProcess;
224 error("Unable to initialise the process task identity");
225 return false;
226 }
227 UtsRef initialUts;
229 if (!pSubsystem->namespaceContext() || !pSubsystem->namespaceContext()->valid() ||
230 posix_uts_initial(initialUts) != UtsStatus::Success ||
231 posix_uts_prepare_thread(initialUts, false, preparedUts) != UtsStatus::Success) {
232 delete pProcess;
233 error("Unable to initialise the process namespace");
234 return false;
235 }
236
237 g_pStage2Thread = new Thread(pProcess, init_stage2, 0, 0, false, false, true);
238 if (!g_pStage2Thread) {
239 delete pProcess;
240 error("Unable to initialise the process thread");
241 return false;
242 }
243 pSubsystem->namespaceContext()->publishThread(preparedUts, *g_pStage2Thread, true);
244 if (pSubsystem->traceContext().publishTask(preparedTrace, *g_pStage2Thread) !=
245 TraceStatus::Success)
246 FATAL("Initial trace task publication failed");
247 g_pStage2Thread->setName("init");
248 pProcess->publish();
249 if (!g_pStage2Thread->start()) {
250 FATAL("init: delayed initial thread could not be started.");
251 }
252
253 // wait for the other process to start before we move on with startup
254 g_pStage2Thread->join();
255#endif
256
257 return true;
258}
259
260static void destroy() {}
261
262#if X86_COMMON
263#define __MOD_DEPS "vfs", "posix", "linker", "users"
264#define __MOD_DEPS_OPT "gfx-deps", "mountroot", "confignics"
265#else
266#define __MOD_DEPS "vfs", "posix", "linker", "users"
267#define __MOD_DEPS_OPT "mountroot", "confignics"
268#endif
269MODULE_INFO("init", &init, &destroy, __MOD_DEPS);
270#ifdef __MOD_DEPS_OPT
271MODULE_OPTIONAL_DEPENDS(__MOD_DEPS_OPT);
272#endif
EXPORTED_PUBLIC void write(T &str, Colour foreColour, Colour backColour)
Definition File.h:75
void addFileDescriptor(size_t fd, FileDescriptor *pFd)
Process * getParent()
Definition Process.h:620
Group * getGroup() const
LargeStaticString & description()
Definition Process.h:525
User * getUser() const
static void setInit(Process *pProcess)
Definition Process.cc:2421
static ProcessorInformation & information()
virtual bool invoke(const char *name, Vector< String > &argv, Vector< String > &env)=0
bool join()
Definition Thread.cc:2746
bool start()
Definition Thread.cc:751
static VFS & instance()
Definition VFS.cc:311
A vector / dynamic array.
Definition Vector.h:33
void pushBack(const T &value)
Definition Vector.h:275