init: Add a few more configuration options for services
All checks were successful
continuous-integration/drone/push Build is passing
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:
parent
97e9fceaa4
commit
5b69ce554c
@ -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());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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();
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user