From 5b69ce554ce0d7ab8ecf325564e0aa4a999c6c24 Mon Sep 17 00:00:00 2001 From: apio Date: Tue, 25 Apr 2023 21:00:12 +0200 Subject: [PATCH] init: Add a few more configuration options for services Notably, redirecting a service's standard streams and waiting for a service to finish before continuing boot. --- apps/init.cpp | 77 +++++++++++++++++++++++++++++++++++++++-- libos/include/os/File.h | 5 +++ 2 files changed, 79 insertions(+), 3 deletions(-) diff --git a/apps/init.cpp b/apps/init.cpp index 65bac481..f8440f95 100644 --- a/apps/init.cpp +++ b/apps/init.cpp @@ -39,15 +39,24 @@ struct Service String command; bool restart { false }; String environment; + String standard_output; + String standard_error; + String standard_input; + bool wait { false }; Option pid {}; }; Vector g_services; -static Result service_child(const Service& service) +static Result service_child(const Service& service, SharedPtr output, SharedPtr error, + SharedPtr input) { 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)); } else { @@ -60,10 +69,32 @@ static Result service_child(const Service& service) static Result try_start_service(Service& service) { + SharedPtr new_stdout = {}; + SharedPtr new_stderr = {}; + SharedPtr 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()); if (pid == 0) { - auto rc = service_child(service); + auto rc = service_child(service, new_stdout, new_stderr, new_stdin); if (rc.has_error()) { fprintf(g_init_log, "[child %d] failed to start service %s due to error: %s\n", getpid(), @@ -75,7 +106,18 @@ static Result try_start_service(Service& service) 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 {}; } @@ -142,6 +184,35 @@ static Result load_service(StringView path) 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()); } diff --git a/libos/include/os/File.h b/libos/include/os/File.h index d26050da..25f174bf 100644 --- a/libos/include/os/File.h +++ b/libos/include/os/File.h @@ -39,6 +39,11 @@ namespace os Result getchar(); + int fd() const + { + return m_fd; + } + File(Badge); ~File();