init: Add a configurable service system instead of always starting /bin/sh
All checks were successful
continuous-integration/drone/push Build is passing
All checks were successful
continuous-integration/drone/push Build is passing
This commit is contained in:
parent
63a2df112d
commit
257c2ffd0a
208
apps/init.cpp
208
apps/init.cpp
@ -1,13 +1,23 @@
|
||||
#include <luna/PathParser.h>
|
||||
#include <luna/String.h>
|
||||
#include <luna/Vector.h>
|
||||
#include <os/File.h>
|
||||
#include <os/Process.h>
|
||||
|
||||
#include <dirent.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/syscall.h>
|
||||
#include <sys/sysmacros.h>
|
||||
#include <sys/wait.h>
|
||||
#include <unistd.h>
|
||||
|
||||
FILE* g_init_log;
|
||||
|
||||
#define xmknod(path, mode, maj, min) \
|
||||
if (mknod(path, mode, makedev(maj, min)) < 0) exit(255);
|
||||
|
||||
@ -23,6 +33,170 @@ static void populate_devfs()
|
||||
xmknod("/dev/fb0", 0222, 3, 0);
|
||||
}
|
||||
|
||||
struct Service
|
||||
{
|
||||
String name;
|
||||
String command;
|
||||
bool restart { false };
|
||||
String environment;
|
||||
Option<pid_t> pid {};
|
||||
};
|
||||
|
||||
Vector<Service> g_services;
|
||||
|
||||
static Result<void> service_child(const Service& service)
|
||||
{
|
||||
auto args = TRY(service.command.split(" \n"));
|
||||
|
||||
if (service.environment.is_empty()) { TRY(os::Process::exec(args[0].view(), args.slice(), false)); }
|
||||
else
|
||||
{
|
||||
auto env = TRY(service.environment.split(",\n"));
|
||||
TRY(os::Process::exec(args[0].view(), args.slice(), env.slice(), false));
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
static Result<void> try_start_service(Service& service)
|
||||
{
|
||||
pid_t pid = TRY(os::Process::fork());
|
||||
if (pid == 0)
|
||||
{
|
||||
auto rc = service_child(service);
|
||||
if (rc.has_error())
|
||||
{
|
||||
fprintf(g_init_log, "[child %d] failed to start service %s due to error: %s\n", getpid(),
|
||||
service.name.chars(), rc.error_string());
|
||||
}
|
||||
fclose(g_init_log);
|
||||
exit(127);
|
||||
}
|
||||
|
||||
fprintf(g_init_log, "[init] created new child process %d for service %s\n", pid, service.name.chars());
|
||||
|
||||
service.pid = pid;
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
static void start_service(Service& service)
|
||||
{
|
||||
auto rc = try_start_service(service);
|
||||
if (rc.has_error())
|
||||
{
|
||||
fprintf(g_init_log, "[init] failed to start service %s due to error: %s\n", service.name.chars(),
|
||||
rc.error_string());
|
||||
}
|
||||
}
|
||||
|
||||
static Result<void> load_service(StringView path)
|
||||
{
|
||||
fprintf(g_init_log, "[init] reading service file: %s\n", path.chars());
|
||||
|
||||
auto file = TRY(os::File::open(path, os::File::ReadOnly));
|
||||
|
||||
Service service;
|
||||
|
||||
while (true)
|
||||
{
|
||||
auto line = TRY(file->read_line(false));
|
||||
if (line.is_empty()) break;
|
||||
|
||||
auto parts = TRY(line.split_once('='));
|
||||
if (parts.size() < 2 || parts[0].is_empty() || parts[1].is_empty())
|
||||
{
|
||||
fprintf(g_init_log, "[init] file contains invalid line, aborting: '%s'\n", line.chars());
|
||||
return {};
|
||||
}
|
||||
|
||||
if (parts[0].view() == "Name")
|
||||
{
|
||||
service.name = move(parts[1]);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (parts[0].view() == "Command")
|
||||
{
|
||||
service.command = move(parts[1]);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (parts[0].view() == "Restart")
|
||||
{
|
||||
if (parts[1].view() == "true" || parts[1].view().to_uint().value_or(0) == 1)
|
||||
{
|
||||
service.restart = true;
|
||||
continue;
|
||||
}
|
||||
service.restart = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (parts[0].view() == "Environment")
|
||||
{
|
||||
service.environment = move(parts[1]);
|
||||
continue;
|
||||
}
|
||||
|
||||
fprintf(g_init_log, "[init] skipping unknown entry name %s\n", parts[0].chars());
|
||||
continue;
|
||||
}
|
||||
|
||||
if (service.name.is_empty())
|
||||
{
|
||||
fprintf(g_init_log, "[init] service file is missing 'Name' entry, aborting!\n");
|
||||
return {};
|
||||
}
|
||||
|
||||
if (service.command.is_empty())
|
||||
{
|
||||
fprintf(g_init_log, "[init] service file is missing 'Command' entry, aborting!\n");
|
||||
return {};
|
||||
}
|
||||
|
||||
fprintf(g_init_log, "[init] loaded service %s into memory\n", service.name.chars());
|
||||
|
||||
TRY(g_services.try_append(move(service)));
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
static Result<void> load_services()
|
||||
{
|
||||
DIR* dp = opendir("/etc/init");
|
||||
if (!dp)
|
||||
{
|
||||
fprintf(g_init_log, "[init] cannot open service directory: %s\n", strerror(errno));
|
||||
return {};
|
||||
}
|
||||
|
||||
dirent* ent;
|
||||
while ((ent = readdir(dp)))
|
||||
{
|
||||
if ("."_sv == ent->d_name || ".."_sv == ent->d_name) continue;
|
||||
|
||||
auto service_path = TRY(PathParser::join("/etc/init"_sv, ent->d_name));
|
||||
TRY(load_service(service_path.view()));
|
||||
}
|
||||
|
||||
closedir(dp);
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
static Result<void> start_services()
|
||||
{
|
||||
TRY(load_services());
|
||||
for (auto& service : g_services)
|
||||
{
|
||||
fprintf(g_init_log, "[init] starting service %s\n", service.name.chars());
|
||||
start_service(service);
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
if (getpid() != 1)
|
||||
@ -39,16 +213,32 @@ int main()
|
||||
stdout = fopen("/dev/console", "w");
|
||||
stderr = fopen("/dev/console", "w");
|
||||
|
||||
pid_t ret = fork();
|
||||
g_init_log = fopen("/init.log", "w+");
|
||||
fcntl(fileno(g_init_log), F_SETFD, FD_CLOEXEC);
|
||||
|
||||
if (ret == 0)
|
||||
start_services();
|
||||
|
||||
while (1)
|
||||
{
|
||||
char* argv[] = { "/bin/sh", NULL };
|
||||
char* envp[] = { "PATH=/bin:/sbin", NULL };
|
||||
execve(argv[0], argv, envp);
|
||||
perror("execv");
|
||||
return 1;
|
||||
}
|
||||
int status;
|
||||
pid_t child = wait(&status);
|
||||
|
||||
while (1) wait(NULL);
|
||||
for (auto& service : g_services)
|
||||
{
|
||||
if (service.pid.has_value() && service.pid.value() == child)
|
||||
{
|
||||
fprintf(g_init_log, "[init] service %s exited with status %d\n", service.name.chars(),
|
||||
WEXITSTATUS(status));
|
||||
|
||||
if (service.restart)
|
||||
{
|
||||
fprintf(g_init_log, "[init] restarting service %s\n", service.name.chars());
|
||||
|
||||
start_service(service);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
4
initrd/etc/init/99-shell
Normal file
4
initrd/etc/init/99-shell
Normal file
@ -0,0 +1,4 @@
|
||||
Name=shell
|
||||
Command=/bin/sh
|
||||
Restart=true
|
||||
Environment=PATH=/bin:/sbin
|
Loading…
Reference in New Issue
Block a user