Skip to content

Commit

Permalink
Support redirecting stderr to stdout
Browse files Browse the repository at this point in the history
  • Loading branch information
akash-akya committed Jun 21, 2024
1 parent 82729d1 commit 4f820bc
Show file tree
Hide file tree
Showing 7 changed files with 74 additions and 13 deletions.
12 changes: 11 additions & 1 deletion c_src/spawner.c
Original file line number Diff line number Diff line change
Expand Up @@ -146,9 +146,19 @@ static int exec_process(char const *bin, char *const *args, int socket,
_exit(FORK_EXEC_FAILURE);
}

if (strcmp(stderr_str, "consume") == 0) {
if (strcmp(stderr_str, "redirect_to_stdout") == 0) {
close(STDERR_FILENO);
close(r_cmderr);
close(w_cmderr);

if (dup2(w_cmdout, STDERR_FILENO) < 0) {
perror("[spawner] failed to redirect stderr to stdout");
_exit(FORK_EXEC_FAILURE);
}
} else if (strcmp(stderr_str, "consume") == 0) {
close(STDERR_FILENO);
close(r_cmderr);

if (dup2(w_cmderr, STDERR_FILENO) < 0) {
perror("[spawner] failed to dup to stderr");
_exit(FORK_EXEC_FAILURE);
Expand Down
5 changes: 3 additions & 2 deletions lib/exile.ex
Original file line number Diff line number Diff line change
Expand Up @@ -197,8 +197,9 @@ defmodule Exile do
* `stderr` - different ways to handle stderr stream. possible values `:console`, `:disable`, `:stream`.
1. `:console` - stderr output is redirected to console (Default)
2. `:disable` - stderr output is redirected `/dev/null` suppressing all output
3. `:consume` - connects stderr for the consumption. The output stream will contain stderr
2. `:redirect_to_stdout` - stderr output is redirected to stdout
3. `:disable` - stderr output is redirected `/dev/null` suppressing all output
4. `:consume` - connects stderr for the consumption. The output stream will contain stderr
data along with stdout. Stream data will be either `{:stdout, iodata}` or `{:stderr, iodata}`
to differentiate different streams. See example below.
Expand Down
5 changes: 3 additions & 2 deletions lib/exile/process.ex
Original file line number Diff line number Diff line change
Expand Up @@ -303,8 +303,9 @@ defmodule Exile.Process do
* `stderr` - different ways to handle stderr stream.
possible values `:console`, `:disable`, `:stream`.
1. `:console` - stderr output is redirected to console (Default)
2. `:disable` - stderr output is redirected `/dev/null` suppressing all output
3. `:consume` - connects stderr for the consumption. When set to stream the output must be consumed to
2. `:redirect_to_stdout` - stderr output is redirected to stdout
3. `:disable` - stderr output is redirected `/dev/null` suppressing all output
4. `:consume` - connects stderr for the consumption. When set to stream the output must be consumed to
avoid external program from blocking.
Caller of the process will be the owner owner of the Exile Process.
Expand Down
11 changes: 6 additions & 5 deletions lib/exile/process/exec.ex
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ defmodule Exile.Process.Exec do
cmd_with_args: nonempty_list(),
cd: charlist,
env: env,
stderr: :console | :disable | :consume
stderr: :console | :redirect_to_stdout | :disable | :consume
}}
| {:error, String.t()}
def normalize_exec_args(cmd_with_args, opts) do
Expand Down Expand Up @@ -192,18 +192,19 @@ defmodule Exile.Process.Exec do
end
end

@spec normalize_stderr(stderr :: :console | :disable | :consume | nil) ::
{:ok, :console | :disable | :consume} | {:error, String.t()}
@spec normalize_stderr(stderr :: :console | :redirect_to_stdout | :disable | :consume | nil) ::
{:ok, :console | :redirect_to_stdout | :disable | :consume} | {:error, String.t()}
defp normalize_stderr(stderr) do
case stderr do
nil ->
{:ok, :console}

stderr when stderr in [:console, :disable, :consume] ->
stderr when stderr in [:redirect_to_stdout, :console, :disable, :consume] ->
{:ok, stderr}

_ ->
{:error, ":stderr must be an atom and one of :console, :disable, :consume"}
{:error,
":stderr must be an atom and one of :redirect_to_stdout, :console, :disable, :consume"}
end
end

Expand Down
2 changes: 1 addition & 1 deletion lib/exile/process/state.ex
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ defmodule Exile.Process.State do

@type read_mode :: :stdout | :stderr | :stdout_or_stderr

@type stderr_mode :: :console | :disable | :consume
@type stderr_mode :: :console | :redirect_to_stdout | :disable | :consume

@type pipes :: %{
stdin: Pipe.t(),
Expand Down
5 changes: 3 additions & 2 deletions lib/exile/stream.ex
Original file line number Diff line number Diff line change
Expand Up @@ -297,11 +297,12 @@ defmodule Exile.Stream do
nil ->
{:ok, :console}

stderr when stderr in [:console, :disable, :consume] ->
stderr when stderr in [:console, :redirect_to_stdout, :disable, :consume] ->
{:ok, stderr}

_ ->
{:error, ":stderr must be an atom and one of :console, :disable, :consume"}
{:error,
":stderr must be an atom and one of :console, :redirect_to_stdout, :disable, :consume"}
end
end

Expand Down
47 changes: 47 additions & 0 deletions test/exile_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,53 @@ defmodule ExileTest do
assert IO.iodata_to_binary(stderr) == "Hello World\n"
end

test "stderr redirect_to_stdout" do
merged_output =
Exile.stream!(
[fixture("write_stderr.sh"), "Hello World"],
stderr: :redirect_to_stdout
)
|> Enum.to_list()
|> IO.iodata_to_binary()

assert merged_output == "Hello World\n"
end

test "order must be preserved when stderr is redirect to stdout" do
merged_output =
Exile.stream!(
["sh", "-c", "for s in $(seq 1 10); do echo stdout $s; echo stderr $s >&2; done"],
stderr: :redirect_to_stdout,
ignore_epipe: true
)
|> Enum.to_list()
|> IO.iodata_to_binary()
|> String.trim()

assert [
"stdout 1",
"stderr 1",
"stdout 2",
"stderr 2",
"stdout 3",
"stderr 3",
"stdout 4",
"stderr 4",
"stdout 5",
"stderr 5",
"stdout 6",
"stderr 6",
"stdout 7",
"stderr 7",
"stdout 8",
"stderr 8",
"stdout 9",
"stderr 9",
"stdout 10",
"stderr 10"
] == String.split(merged_output, "\n")
end

test "multiple streams" do
script = """
for i in {1..1000}; do
Expand Down

0 comments on commit 4f820bc

Please sign in to comment.