init: Add a few more configuration options for services
All checks were successful
continuous-integration/drone/push Build is passing

Notably, redirecting a service's standard streams and waiting for a service to finish before continuing boot.
This commit is contained in:
apio 2023-04-25 21:00:12 +02:00
parent 97e9fceaa4
commit 5b69ce554c
Signed by: apio
GPG Key ID: B8A7D06E42258954
2 changed files with 79 additions and 3 deletions

View File

@ -39,15 +39,24 @@ struct Service
String command; String command;
bool restart { false }; bool restart { false };
String environment; String environment;
String standard_output;
String standard_error;
String standard_input;
bool wait { false };
Option<pid_t> pid {}; Option<pid_t> pid {};
}; };
Vector<Service> g_services; Vector<Service> g_services;
static Result<void> service_child(const Service& service) static Result<void> service_child(const Service& service, SharedPtr<os::File> output, SharedPtr<os::File> error,
SharedPtr<os::File> input)
{ {
auto args = TRY(service.command.split(" \n")); auto args = TRY(service.command.split(" \n"));
if (output) dup2(output->fd(), STDOUT_FILENO);
if (error) dup2(error->fd(), STDERR_FILENO);
if (input) dup2(input->fd(), STDIN_FILENO);
if (service.environment.is_empty()) { TRY(os::Process::exec(args[0].view(), args.slice(), false)); } if (service.environment.is_empty()) { TRY(os::Process::exec(args[0].view(), args.slice(), false)); }
else else
{ {
@ -60,10 +69,32 @@ static Result<void> service_child(const Service& service)
static Result<void> try_start_service(Service& service) static Result<void> try_start_service(Service& service)
{ {
SharedPtr<os::File> new_stdout = {};
SharedPtr<os::File> new_stderr = {};
SharedPtr<os::File> new_stdin = {};
if (!service.standard_output.is_empty())
{
new_stdout = TRY(os::File::open_or_create(service.standard_output.view(), os::File::Append, 0600));
new_stdout->set_close_on_exec();
}
if (!service.standard_error.is_empty())
{
new_stderr = TRY(os::File::open_or_create(service.standard_error.view(), os::File::Append, 0600));
new_stderr->set_close_on_exec();
}
if (!service.standard_input.is_empty())
{
new_stdin = TRY(os::File::open(service.standard_input.view(), os::File::ReadOnly));
new_stdin->set_close_on_exec();
}
pid_t pid = TRY(os::Process::fork()); pid_t pid = TRY(os::Process::fork());
if (pid == 0) if (pid == 0)
{ {
auto rc = service_child(service); auto rc = service_child(service, new_stdout, new_stderr, new_stdin);
if (rc.has_error()) if (rc.has_error())
{ {
fprintf(g_init_log, "[child %d] failed to start service %s due to error: %s\n", getpid(), fprintf(g_init_log, "[child %d] failed to start service %s due to error: %s\n", getpid(),
@ -75,7 +106,18 @@ static Result<void> try_start_service(Service& service)
fprintf(g_init_log, "[init] created new child process %d for service %s\n", pid, service.name.chars()); fprintf(g_init_log, "[init] created new child process %d for service %s\n", pid, service.name.chars());
service.pid = pid; if (service.wait)
{
fprintf(g_init_log, "[init] waiting for child process %d to finish\n", pid);
int status;
pid_t id = waitpid(pid, &status, 0);
if (id < 0) { return err(errno); }
fprintf(g_init_log, "[init] child process %d exited with code %d\n", pid, WEXITSTATUS(status));
}
else
service.pid = pid;
return {}; return {};
} }
@ -142,6 +184,35 @@ static Result<void> load_service(StringView path)
continue; continue;
} }
if (parts[0].view() == "StandardOutput")
{
service.standard_output = move(parts[1]);
continue;
}
if (parts[0].view() == "StandardError")
{
service.standard_error = move(parts[1]);
continue;
}
if (parts[0].view() == "StandardInput")
{
service.standard_input = move(parts[1]);
continue;
}
if (parts[0].view() == "Wait")
{
if (parts[1].view() == "true" || parts[1].view().to_uint().value_or(0) == 1)
{
service.wait = true;
continue;
}
service.wait = false;
continue;
}
fprintf(g_init_log, "[init] skipping unknown entry name %s\n", parts[0].chars()); fprintf(g_init_log, "[init] skipping unknown entry name %s\n", parts[0].chars());
} }

View File

@ -39,6 +39,11 @@ namespace os
Result<int> getchar(); Result<int> getchar();
int fd() const
{
return m_fd;
}
File(Badge<File>); File(Badge<File>);
~File(); ~File();