1// SPDX-License-Identifier: GPL-3.0-or-later
2#include "ServiceManager.hpp"
3#include "common/ConfigurationManager.hpp"
4#include "global.hpp"
5#include "logging.hpp"
6#include "rpc/UnitStateReceiver.hpp"
7#include "rpc/rpc.hpp"
8
9#include <argparse/libargparse.h>
10#include <iostream>
11#include <stacktrace>
12#include <sys/wait.h>
13#include <thread>
14#include <unistd.h>
15
16static void sigsegv_handler(int sig)
17{
18 if (sig == SIGSEGV)
19 {
20 std::cout << RED("INIT process received SIGSEGV") << std::endl << std::endl;
21 std::cout << RED("!!!!!!!!!!!!!!!!!!!!!!!!!!") << std::endl;
22 std::cout << RED("!!! Segmentation fault !!!") << std::endl;
23 std::cout << RED("!!!!!!!!!!!!!!!!!!!!!!!!!!") << std::endl;
24 std::cout << RED("!!!") GREEN(" Good Bye~ ") RED("!!!") << std::endl;
25 std::cout << RED("!!!!!!!!!!!!!!!!!!!!!!!!!!") << std::endl;
26 std::cout << std::stacktrace::current() << std::endl << std::flush;
27 while (true)
28 sched_yield();
29 }
30}
31
32static void sigchild_handler(int sig)
33{
34 (void) sig;
35}
36
37#define DYN_ERROR_CODE (__COUNTER__ + 1)
38
39static const argparse_arg_t longopts[] = {
40 { .full: "help", .abbr: 'h', .argtype: ARGPARSE_NONE, .help: "show this help" },
41 { .full: "config", .abbr: 'C', .argtype: ARGPARSE_REQUIRED, .help: "configuration file, default: /initrd/config/init.conf" },
42 { .full: "shell", .abbr: 'S', .argtype: ARGPARSE_REQUIRED, .help: "shell to start, default: /initrd/programs/mossh" },
43 {},
44};
45
46int main(int argc, const char *argv[])
47{
48 struct sigaction sa;
49 sa.sa_handler = SIG_IGN;
50 sa.sa_flags = SA_RESTART;
51 sigaction(SIGINT, act: &sa, NULL);
52 sigaction(SIGQUIT, act: &sa, NULL);
53 sigaction(SIGTERM, act: &sa, NULL);
54
55 sa.sa_handler = sigchild_handler;
56 sigaction(SIGCHLD, act: &sa, NULL);
57
58 sa.sa_handler = sigsegv_handler;
59 sigaction(SIGSEGV, act: &sa, NULL);
60
61 std::filesystem::path configPath = "/initrd/config/init-config.toml";
62 std::string shell = "/initrd/programs/mossh";
63
64 argparse_state_t state;
65 argparse_init(options: &state, argv);
66 while (true)
67 {
68 const int option = argparse_long(options: &state, longopts, NULL);
69 if (option == -1)
70 break;
71
72 switch (option)
73 {
74 case 'C': configPath = state.optarg; break;
75 case 'S': shell = state.optarg; break;
76 case 'h': argparse_usage(options: &state, args: longopts, usage: "the init program"); return 0;
77 default: break;
78 }
79 }
80
81 if (getpid() != 1)
82 {
83 for (int i = 0; i < argc; i++)
84 printf(format: "argv[%d] = %s\n", i, argv[i]);
85 puts(string: "init: not running as PID 1, exiting...");
86 return DYN_ERROR_CODE;
87 }
88
89 Debug << "init: using config file " << configPath << std::endl;
90
91 if (!std::filesystem::exists(p: configPath))
92 {
93 std::cerr << "init: config file " << configPath << " does not exist" << std::endl;
94 return DYN_ERROR_CODE;
95 }
96
97 ConfigurationManager->LoadConfiguration(tables: ReadAllConfig(config_path: configPath));
98 ConfigurationManager->FinaliseConfiguration();
99 std::thread([]() { RpcServer->run(); }).detach();
100 std::thread([]() { UnitStateReceiverService->run(); }).detach();
101
102 if (!ServiceManager->StartDefaultTarget())
103 {
104 std::cerr << RED("init: failed to start default target") << std::endl;
105 return DYN_ERROR_CODE;
106 }
107
108 // start the shell
109 argparse_init(options: &state, argv); // reset the options
110
111 std::vector<const char *> args;
112 args.push_back(x: shell.c_str());
113 while (const auto arg = argparse_arg(options: &state))
114 args.push_back(x: arg);
115 args.push_back(x: nullptr);
116
117 const static auto StartShell = [&]()
118 {
119 const pid_t pid = fork();
120 if (pid == 0)
121 {
122 if (execv(path: shell.c_str(), argv: (char **) args.data()) <= 0)
123 {
124 std::cout << "Failed to start shell" << std::endl;
125 exit(status: 1);
126 }
127 }
128 return pid;
129 };
130
131 pid_t shell_pid = StartShell();
132
133 while (true)
134 {
135 int status = 0;
136 const pid_t pid = waitpid(pid: -1, status: &status, flags: 0);
137 if (pid == shell_pid)
138 {
139 puts(string: "init: shell exited, restarting...");
140 shell_pid = StartShell();
141 }
142
143 ServiceManager->OnProcessExit(pid, status);
144 }
145
146 return 0;
147}
148