Skip to content

Commit

Permalink
stdout/2,3, stderr/2,3: return list of binaries
Browse files Browse the repository at this point in the history
Change stdout/2,3 and stderr/2,3 to return a list of values as discussed
in:

#3

The spec is changed from:

% from
-spec stdout(alcove_drv:ref(),[pid_t()]) -> 'false' | binary().

% to: [] indicates no stdout
-spec stdout(alcove_drv:ref(),[pid_t()]) -> [binary()].

In the case where an unreponsive child causes the pipe buffer between the
alcove parent process and the child to fill up, stdout/2,3 and stderr/2,3
will now both crash. If the caller wants to handle this situation, e.g.,
to wait and retry, the `alcove_pipe` message can be processed directly
from the mailbox.

Thanks @neeraj9!
  • Loading branch information
msantos committed Apr 18, 2018
1 parent ddd0387 commit 2b8bacb
Show file tree
Hide file tree
Showing 4 changed files with 40 additions and 26 deletions.
14 changes: 7 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ We can interact with the process via stdin, stdout and stderr:

```erlang
alcove:stdin(Drv, [Child1,Child2], "hello process\n"),
<<"hello process\n">> = alcove:stdout(Drv, [Child1,Child2]).
[<<"hello process\n">>] = alcove:stdout(Drv, [Child1,Child2]).
```

Setting Up Privileges
Expand Down Expand Up @@ -215,7 +215,7 @@ rebar shell

3> alcove:stdin(Drv, [Cat], "test test\n").
4> alcove:stdout(Drv, [Cat]).
<<"test test\n">>
[<<"test test\n">>]
```

We can test the limits of the sandbox by using a shell instead of
Expand All @@ -229,18 +229,18 @@ herding cat's:
6> alcove:stdin(P, [Sh], "echo hello\n").
ok
7> alcove:stdout(P, [Sh]).
<<"hello\n">>
[<<"hello\n">>]

% Attempt to create a file
6> alcove:stdin(Drv, [Sh], "> foo\n").
ok
7> alcove:stderr(P, [Sh]).
<<"sh: can't create foo: Too many open files\n">>
[<<"sh: can't create foo: Too many open files\n">>]

% Try to fork a new process
8> alcove:stdin(Drv, [Sh], "ls\n").
9> alcove:stderr(P, [Sh]).
<<"sh: can't fork\n">>
[<<"sh: can't fork\n">>]

% If we check the parent for events, we can see the child has exited
10> alcove:event(P, []).
Expand Down Expand Up @@ -1126,11 +1126,11 @@ exec(3) has been called.

Send data to stdin of the process.

stdout(Drv, ForkChain) -> binary() | false
stdout(Drv, ForkChain) -> [binary()]

Read stdout from the process.

stderr(Drv, ForkChain) -> binary() | false
stderr(Drv, ForkChain) -> [binary()]

Read stderr from the process.

Expand Down
32 changes: 23 additions & 9 deletions bin/alcove.escript
Original file line number Diff line number Diff line change
Expand Up @@ -257,7 +257,9 @@ stdin(Drv, Pids, Data) ->
ok ->
ok;
{alcove_error, Error} ->
erlang:error(Error, [Drv, Pids, Data])
erlang:error(Error, [Drv, Pids, Data]);
{alcove_pipe, Error} ->
{error, Error}
end.
";

Expand All @@ -269,12 +271,18 @@ stdout(Drv, Pids) ->
static({stdout,3}) ->
"
stdout(Drv, Pids, Timeout) ->
stdout_1(Drv, Pids, Timeout, []).
stdout_1(Drv, Pids, Timeout, Acc) ->
case alcove_drv:stdout(Drv, Pids, Timeout) of
false ->
lists:reverse(Acc);
{alcove_error, Error} ->
erlang:error(Error, [Drv, Pids, Timeout]);
{alcove_pipe, Error} ->
{error, Error};
Reply -> Reply
erlang:error(Error, [Drv, Pids, Timeout]);
Reply ->
stdout_1(Drv, Pids, Timeout, [Reply|Acc])
end.
";

Expand All @@ -286,12 +294,18 @@ stderr(Drv, Pids) ->
static({stderr,3}) ->
"
stderr(Drv, Pids, Timeout) ->
stderr_1(Drv, Pids, Timeout, []).
stderr_1(Drv, Pids, Timeout, Acc) ->
case alcove_drv:stderr(Drv, Pids, Timeout) of
false ->
lists:reverse(Acc);
{alcove_error, Error} ->
erlang:error(Error, [Drv, Pids, Timeout]);
{alcove_pipe, Error} ->
{error, Error};
Reply -> Reply
erlang:error(Error, [Drv, Pids, Timeout]);
Reply ->
stderr_1(Drv, Pids, Timeout, [Reply|Acc])
end.
";

Expand Down Expand Up @@ -706,13 +720,13 @@ specs() ->
-spec syscall_constant(alcove_drv:ref(),[pid_t()],atom()) -> 'unknown' | non_neg_integer().
-spec syscall_constant(alcove_drv:ref(),[pid_t()],atom(),timeout()) -> 'unknown' | non_neg_integer().
-spec stderr(alcove_drv:ref(),[pid_t()]) -> 'false' | binary().
-spec stderr(alcove_drv:ref(),[pid_t()],timeout()) -> 'false' | binary().
-spec stderr(alcove_drv:ref(),[pid_t()]) -> [binary()].
-spec stderr(alcove_drv:ref(),[pid_t()],timeout()) -> [binary()].
-spec stdin(alcove_drv:ref(),[pid_t()],iodata()) -> 'ok'.
-spec stdout(alcove_drv:ref(),[pid_t()]) -> 'false' | binary().
-spec stdout(alcove_drv:ref(),[pid_t()],timeout()) -> 'false' | binary().
-spec stdout(alcove_drv:ref(),[pid_t()]) -> [binary()].
-spec stdout(alcove_drv:ref(),[pid_t()],timeout()) -> [binary()].
-spec symlink(alcove_drv:ref(),[pid_t()],iodata(),iodata()) -> 'ok' | {error, posix()}.
-spec symlink(alcove_drv:ref(),[pid_t()],iodata(),iodata(),timeout()) -> 'ok' | {error, posix()}.
Expand Down
2 changes: 1 addition & 1 deletion src/alcove.app.src
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{application,alcove,
[{description,"Unix process containers and sandboxes"},
{vsn,"0.23.0"},
{vsn,"0.24.0"},
{registered,[]},
{applications,[kernel,stdlib,sasl]},
{env,[]},
Expand Down
18 changes: 9 additions & 9 deletions test/alcove_SUITE.erl
Original file line number Diff line number Diff line change
Expand Up @@ -846,8 +846,8 @@ execve(Config) ->
ok = alcove:execve(Drv, [Child1], "/usr/bin/env",
["/usr/bin/env"], []),

<<"FOO=bar\nBAR=1234567\n">> = alcove:stdout(Drv, [Child0], 5000),
false = alcove:stdout(Drv, [Child1], 2000).
[<<"FOO=bar\nBAR=1234567\n">>] = alcove:stdout(Drv, [Child0], 5000),
[] = alcove:stdout(Drv, [Child1], 2000).

execvp_with_signal(Config) ->
Drv = ?config(drv, Config),
Expand Down Expand Up @@ -1048,7 +1048,7 @@ execvp_mid_chain(Config) ->
ok = alcove:execvp(Drv, Pids, "/bin/cat", ["/bin/cat"]),

alcove:stdin(Drv, Pids, "test\n"),
<<"test\n">> = alcove:stdout(Drv, Pids, 5000),
[<<"test\n">>] = alcove:stdout(Drv, Pids, 5000),
false = alcove:event(Drv, Chain, 2000),
Reply = [ alcove:kill(Drv, [], Pid, 0) || Pid <- Rest ],

Expand All @@ -1072,7 +1072,7 @@ stdout(Config) ->
ok = alcove:execvp(Drv, [Child], "/bin/sh", ["/bin/sh"]),

ok = alcove:stdin(Drv, [Child], "echo 0123456789\n"),
<<"0123456789\n">> = alcove:stdout(Drv, [Child], 5000).
[<<"0123456789\n">>] = alcove:stdout(Drv, [Child], 5000).

stderr(Config) ->
Drv = ?config(drv, Config),
Expand All @@ -1086,9 +1086,9 @@ stderr(Config) ->

case OS of
N when N =:= linux; N =:= openbsd; N =:= freebsd; N =:= darwin ->
<<"/bin/sh: ", _/binary>> = Reply;
[<<"/bin/sh: ", _/binary>>] = Reply;
netbsd ->
<<"nonexistent: not found\n">> = Reply;
[<<"nonexistent: not found\n">>] = Reply;
_ ->
{skip, "stderr test not supported on this platform"}
end.
Expand All @@ -1113,11 +1113,11 @@ pipe_buf(Config) ->

ok = alcove:stdin(Drv, [Child], Stdin),
timer:sleep(1000), % XXX allow time for the message to be received
{error, {eagain,_}} = alcove:stdout(Drv, [Child]),
{'EXIT',{{eagain,0},_}} = (catch alcove:stdout(Drv, [Child])),

ok = alcove:stdin(Drv, [Child], Stdin),
timer:sleep(1000), % XXX allow time for the message to be received
{error, {eagain,_}} = alcove:stderr(Drv, [Child]),
{'EXIT',{{eagain,0},_}} = (catch alcove:stderr(Drv, [Child])),

alcove:kill(Drv, [], Child, 9),

Expand All @@ -1135,7 +1135,7 @@ fexecve(Config) ->

{ok, FD} = alcove:open(Drv, [Child], "/usr/bin/env", [o_rdonly,o_cloexec], 0),
ok = alcove:fexecve(Drv, [Child], FD, [""], ["FOO=bar", "BAR=1234567"]),
<<"FOO=bar\nBAR=1234567\n">> = alcove:stdout(Drv, [Child], 5000).
[<<"FOO=bar\nBAR=1234567\n">>] = alcove:stdout(Drv, [Child], 5000).

%%
%% Linux
Expand Down

0 comments on commit 2b8bacb

Please sign in to comment.