From c966880af06052b9d76eefdd412a615828d7a109 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=B6ren=20Tempel?= Date: Tue, 18 Mar 2025 07:43:48 +0100 Subject: [PATCH] openrc-run: Support standard input redirection too OpenRC already supports redirecting standard output/error of services. This patch adds support for redirecting standard input as well. I encountered the lack of this feature while working on a user service for a Wayland compositor status bar which reads status text to be displayed from stdin. Input redirection allows me to connect this user services to another user service providing the status text via a named pipe. Note that input redirection is also supported by SystemD (and potentially other service supervisors) through the `StandardInput=` directive [1]. [1]: https://www.freedesktop.org/software/systemd/man/latest/systemd.exec.html#StandardInput= --- man/openrc-run.8 | 15 ++++++++++----- man/start-stop-daemon.8 | 7 +++++++ man/supervise-daemon.8 | 17 ++++++++++++++--- sh/start-stop-daemon.sh | 1 + sh/supervise-daemon.sh | 1 + src/start-stop-daemon/start-stop-daemon.c | 16 +++++++++++++++- src/supervise-daemon/supervise-daemon.c | 16 +++++++++++++++- 7 files changed, 63 insertions(+), 10 deletions(-) diff --git a/man/openrc-run.8 b/man/openrc-run.8 index c74767283..e02559430 100644 --- a/man/openrc-run.8 +++ b/man/openrc-run.8 @@ -158,18 +158,23 @@ use this to change the user id, and optionally group id, before or .Xr supervise-daemon 8 launches the daemon. -.It Ar output_log -This is the path to a file or named pipe where the standard output from -the service will be redirected. If you are starting this service with +.It Ar input_file +This is the path to a file or named pipe to which the standard input of the +service will be redirected to. If you are starting this service with .Xr start-stop-daemon 8 , , you must set .Pa command_background to true. Keep in mind that this path will be inside the chroot if the .Pa chroot -variable is set. +variable is set. Contrary to output and error redirection, input redirection +assumes that the file exists already. +.It Ar output_log +The same thing as +.Pa input_file +but for the standard output. .It Ar error_log The same thing as -.Pa output_log +.Pa input_file but for the standard error output. .It Ar output_logger This is a process which will be used to log the standard output from the diff --git a/man/start-stop-daemon.8 b/man/start-stop-daemon.8 index b6d62ff0d..32103a37e 100644 --- a/man/start-stop-daemon.8 +++ b/man/start-stop-daemon.8 @@ -143,6 +143,13 @@ on POSIX systems and, additionally, batch and idle on Linux. If is an integer, it is passed directly to pthread_setschedparam(3). .It Fl -scheduler-priority Ar priority Sets the priority parameter of the scheduling policy of the daemon. See sched(7) for details. +.It Fl 0 , -stdin Ar file +Redirect the standard input of the process to file when started with +.Fl background . +The file Must be an absolute pathname, but relative to the path +optionally given with +.Fl r , -chroot . +The file can also be a named pipe. The file must exist, otherwise an error is emitted. .It Fl 1 , -stdout Ar logfile Redirect the standard output of the process to logfile when started with .Fl background . diff --git a/man/supervise-daemon.8 b/man/supervise-daemon.8 index 5fa226e91..7abb034b4 100644 --- a/man/supervise-daemon.8 +++ b/man/supervise-daemon.8 @@ -50,6 +50,8 @@ servicename .Ar chrootpath .Fl u , -user .Ar user +.Fl 0 , -stdin +.Ar file .Fl 1 , -stdout .Ar logfile .Fl 2 , -stderr @@ -154,11 +156,20 @@ process to communicate with is determined by the name of the service taken from the RC_SVCNAME environment variable. .It Fl u , -user Ar user Start the daemon as the specified user. -.It Fl 1 , -stdout Ar logfile -Redirect the standard output of the process to logfile. +.It Fl 0 , -stdin Ar file +Redirect the standard input of the process to file. Must be an absolute pathname, but relative to the path optionally given with .Fl r , -chroot . -The logfile can also be a named pipe. +The file can also be a named pipe. Input redirection assumes that the file +exists already while output redirection via +.Fl 1 , -stdout +or +.Fl 1 , -stderr +creates it, if it doesn't. +.It Fl 1 , -stdout Ar logfile +The same thing as +.Fl 0 , -stdin +but with the standard output. .It Fl 2 , -stderr Ar logfile The same thing as .Fl 1 , -stdout diff --git a/sh/start-stop-daemon.sh b/sh/start-stop-daemon.sh index 1f9dfa5a7..ba90bb47f 100644 --- a/sh/start-stop-daemon.sh +++ b/sh/start-stop-daemon.sh @@ -45,6 +45,7 @@ ssd_start() --exec $command \ ${chroot:+--chroot} $chroot \ ${directory:+--chdir} $directory \ + ${input_file+--stdin} $input_file \ ${output_log+--stdout} $output_log \ ${error_log+--stderr} $error_log \ ${output_logger:+--stdout-logger \"$output_logger\"} \ diff --git a/sh/supervise-daemon.sh b/sh/supervise-daemon.sh index 8cdde18b5..7a6b46746 100644 --- a/sh/supervise-daemon.sh +++ b/sh/supervise-daemon.sh @@ -28,6 +28,7 @@ supervise_start() ${retry:+--retry} $retry \ ${directory:+--chdir} $directory \ ${chroot:+--chroot} $chroot \ + ${input_file+--stdin} $input_file \ ${output_log+--stdout} ${output_log} \ ${error_log+--stderr} $error_log \ ${output_logger:+--stdout-logger \"$output_logger\"} \ diff --git a/src/start-stop-daemon/start-stop-daemon.c b/src/start-stop-daemon/start-stop-daemon.c index 5c21960f4..0af29c42d 100644 --- a/src/start-stop-daemon/start-stop-daemon.c +++ b/src/start-stop-daemon/start-stop-daemon.c @@ -94,7 +94,7 @@ enum { const char *applet = NULL; const char *extraopts = NULL; -const char getoptstring[] = "I:KN:PR:Sa:bc:d:e:g:ik:mn:op:s:tu:r:w:x:1:2:3:4:" \ +const char getoptstring[] = "I:KN:PR:Sa:bc:d:e:g:ik:mn:op:s:tu:r:w:x:0:1:2:3:4:" \ getoptstring_COMMON; const struct option longopts[] = { { "capabilities", 1, NULL, LONGOPT_CAPABILITIES}, @@ -124,6 +124,7 @@ const struct option longopts[] = { { "chroot", 1, NULL, 'r'}, { "wait", 1, NULL, 'w'}, { "exec", 1, NULL, 'x'}, + { "stdin", 1, NULL, '0'}, { "stdout", 1, NULL, '1'}, { "stderr", 1, NULL, '2'}, { "stdout-logger",1, NULL, '3'}, @@ -162,6 +163,7 @@ const char * const longopts_help[] = { "Chroot to this directory", "Milliseconds to wait for daemon start", "Binary to start/stop", + "Redirect stdin to file", "Redirect stdout to file", "Redirect stderr to file", "Redirect stdout to process", @@ -318,6 +320,7 @@ int main(int argc, char **argv) gid_t gid = 0; char *home = NULL; int tid = 0; + char *redirect_stdin = NULL; char *redirect_stderr = NULL; char *redirect_stdout = NULL; char *stderr_process = NULL; @@ -601,6 +604,10 @@ int main(int argc, char **argv) exec = optarg; break; + case '0': /* --stdin /path/to/stdin.input-file */ + redirect_stdin = optarg; + break; + case '1': /* --stdout /path/to/stdout.lgfile */ redirect_stdout = optarg; break; @@ -1071,6 +1078,13 @@ int main(int argc, char **argv) stdin_fd = devnull_fd; stdout_fd = devnull_fd; stderr_fd = devnull_fd; + if (redirect_stdin) { + if ((stdin_fd = open(redirect_stdin, + O_RDONLY)) == -1) + eerrorx("%s: unable to open the input file" + " for stdin `%s': %s", + applet, redirect_stdin, strerror(errno)); + } if (redirect_stdout) { if ((stdout_fd = open(redirect_stdout, O_WRONLY | O_CREAT | O_APPEND, diff --git a/src/supervise-daemon/supervise-daemon.c b/src/supervise-daemon/supervise-daemon.c index 210d572db..57f8d2b8d 100644 --- a/src/supervise-daemon/supervise-daemon.c +++ b/src/supervise-daemon/supervise-daemon.c @@ -87,7 +87,7 @@ enum { const char *applet = NULL; const char *extraopts = NULL; -const char getoptstring[] = "A:a:D:d:e:g:H:I:Kk:m:N:p:R:r:s:Su:1:2:3" \ +const char getoptstring[] = "A:a:D:d:e:g:H:I:Kk:m:N:p:R:r:s:Su:0:1:2:3" \ getoptstring_COMMON; const struct option longopts[] = { { "healthcheck-timer", 1, NULL, 'a'}, @@ -112,6 +112,7 @@ const struct option longopts[] = { { "signal", 1, NULL, 's'}, { "start", 0, NULL, 'S'}, { "user", 1, NULL, 'u'}, + { "stdin", 1, NULL, '0'}, { "stdout", 1, NULL, '1'}, { "stderr", 1, NULL, '2'}, { "stdout-logger",1, NULL, LONGOPT_STDOUT_LOGGER}, @@ -143,6 +144,7 @@ const char * const longopts_help[] = { "Send a signal to the daemon", "Start daemon", "Change the process user", + "Redirect stdin to file", "Redirect stdout to file", "Redirect stderr to file", "Redirect stdout to process", @@ -168,6 +170,7 @@ static int stdin_fd; static int stdout_fd; static int stderr_fd; static struct ready ready; +static char *redirect_stdin = NULL; static char *redirect_stderr = NULL; static char *redirect_stdout = NULL; static char *stderr_process = NULL; @@ -554,6 +557,13 @@ RC_NORETURN static void child_process(char *exec, char **argv) stdin_fd = devnull_fd; stdout_fd = devnull_fd; stderr_fd = devnull_fd; + if (redirect_stdin) { + if ((stdin_fd = open(redirect_stdin, + O_RDONLY)) == -1) + eerrorx("%s: unable to open the input file" + " for stdin `%s': %s", + applet, redirect_stdin, strerror(errno)); + } if (redirect_stdout) { if ((stdout_fd = open(redirect_stdout, O_WRONLY | O_CREAT | O_APPEND, @@ -1057,6 +1067,10 @@ int main(int argc, char **argv) } break; + case '0': /* --stdin /path/to/stdin.input-file */ + redirect_stdin = optarg; + break; + case '1': /* --stdout /path/to/stdout.lgfile */ redirect_stdout = optarg; break;