Skip to content
This repository has been archived by the owner on Oct 4, 2024. It is now read-only.

Commit

Permalink
Release 1.1. Add separate_streams support.
Browse files Browse the repository at this point in the history
  • Loading branch information
Rubén Caro committed Jun 27, 2015
1 parent eb30894 commit edeccc2
Show file tree
Hide file tree
Showing 3 changed files with 50 additions and 12 deletions.
21 changes: 20 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ raise freely.

## Use

Just add `{:sshex, "1.0.0"}` to your deps on `mix.exs`.
Just add `{:sshex, "1.1.0"}` to your deps on `mix.exs`.

Then assuming `:ssh` application is already started (hence it is listed on deps),
you should acquire an SSH connection using `:ssh.connect/4` like this:
Expand All @@ -40,6 +40,14 @@ status code too, you can use `run/4` like this:
{:ok, res, 0} = SSHEx.run conn, 'ls /some/path'
```

You can pass the option `:separate_streams` to get separated stdout and stderr.
Like this:

```elixir
{:ok, stdout, stderr, 2} = SSHEx.run conn, 'ls /nonexisting/path',
5000, 5000, [separate_streams: true]
```

You will be reusing the same SSH connection all over.


Expand All @@ -52,5 +60,16 @@ the `:user_dir` option for `:ssh.connect/4`. See

## TODOs

* Put every optional parameter under a unique Keyword list
* Add testing using [meck](https://github.com/eproxus/meck)
* Add tunnelling helpers [*](http://erlang.org/pipermail/erlang-questions/2014-June/079481.html)

## Changelog

### 1.1

* Add support for separate stdout/stderr responses.

### 1.0

* Initial release
39 changes: 29 additions & 10 deletions lib/sshex.ex
Original file line number Diff line number Diff line change
Expand Up @@ -18,25 +18,33 @@ defmodule SSHEx do
Any failure related with the SSH connection itself is raised without mercy.
Returns `{:ok,data,status}` on success. Otherwise `{:error, details}`.
If `:separate_streams` is `true` then the response on success looks like `{:ok,stdout,stderr,status}`.
TODO: For 2.0 release, join every optional argument into one big opts list
"""
def run(conn, cmd, channel_timeout \\ 5000, exec_timeout \\ 5000) do
def run(conn, cmd, channel_timeout \\ 5000, exec_timeout \\ 5000, opts \\ []) do
conn
|> open_channel(channel_timeout)
|> exec(conn, cmd, exec_timeout)
|> get_response(exec_timeout)
|> get_response(exec_timeout, "", "", nil, false, opts)
end

@doc """
Convenience function to run `run/4` and get output string straight from it,
Convenience function to run `run/5` and get output string straight from it,
like `:os.cmd/1`.
Returns `response` only if `run/4` return value matches `{:ok, response, _}`.
Raises any `{:error, details}` returned by `run/4`. Note return status from
Returns `response` only if `run/5` return value matches `{:ok, response, _}`,
or returns `{stdout, stderr}` if `run/5` returns `{:ok, stdout, stderr, _}`.
Raises any `{:error, details}` returned by `run/5`. Note return status from
`cmd` is ignored.
TODO: For 2.0 release, join every optional argument into one big opts list
"""
def cmd!(conn, cmd, channel_timeout \\ 5000, exec_timeout \\ 5000) do
case run(conn, cmd, channel_timeout, exec_timeout) do
def cmd!(conn, cmd, channel_timeout \\ 5000, exec_timeout \\ 5000, opts \\ []) do
case run(conn, cmd, channel_timeout, exec_timeout, opts) do
{:ok, response, _} -> response
{:ok, stdout, stderr, _} -> {stdout, stderr}
any -> raise inspect(any)
end
end
Expand All @@ -63,18 +71,20 @@ defmodule SSHEx do

# Loop until all data is received. Return read data and the exit_status.
#
defp get_response(channel, timeout, stdout \\ "", stderr \\ "", status \\ nil, closed \\ false) do
# TODO: For 2.0 release, join every optional argument into one big opts list
#
defp get_response(channel, timeout, stdout \\ "", stderr \\ "", status \\ nil, closed \\ false, opts \\ []) do

# if we got status and closed, then we are done
parsed = case {status, closed} do
{st, true} when not is_nil(st) -> {:ok, stdout, stderr, status}
{st, true} when not is_nil(st) -> format_response({:ok, stdout, stderr, status}, opts)
_ -> receive_and_parse_response(channel, timeout, stdout, stderr, status, closed)
end

# tail recursion
case parsed do
{:loop, {channel, timeout, stdout, stderr, status, closed}} -> # loop again, still things missing
get_response(channel, timeout, stdout, stderr, status, closed)
get_response(channel, timeout, stdout, stderr, status, closed, opts)
x -> x
end
end
Expand All @@ -98,4 +108,13 @@ defmodule SSHEx do
end
end

# Format response for given raw response and given options
defp format_response(raw, opts) do
case opts[:separate_streams] do
true -> raw
_ -> {:ok, stdout, stderr, status} = raw
{:ok, stdout <> stderr, status}
end
end

end
2 changes: 1 addition & 1 deletion mix.exs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ defmodule SSHEx.Mixfile do

def project do
[app: :sshex,
version: "1.0.0",
version: "1.1.0",
elixir: "~> 1.0.0",
package: package,
description: "Simple SSH helpers for Elixir" ]
Expand Down

0 comments on commit edeccc2

Please sign in to comment.