Browse Source

upd to v4.1.1

221V 3 years ago
parent
commit
c66aa4b57a
6 changed files with 70 additions and 47 deletions
  1. 2 0
      LICENSE
  2. 18 38
      README.md
  3. 3 3
      ebin/sh.app
  4. 31 0
      src/exe.erl
  5. 12 3
      src/sh.erl
  6. 4 3
      src/sh_sup.erl

+ 2 - 0
LICENSE

@@ -1,3 +1,5 @@
+ISC License
+
 Copyright (c) 2013 Vladimir Kirillov <proger@hackndev.com>
 
 Permission to use, copy, modify, and/or distribute this software for any

+ 18 - 38
README.md

@@ -1,20 +1,16 @@
-SH Executor - https://github.com/synrc/sh (exe) v1.9 fork
-===========
+# SH Executor - https://github.com/synrc/sh (exe) v1.9 fork (upd to v4.1.1)
 
-Family of functions and ports involving interacting with the system shell,
-paths and external programs.
+Family of functions and ports to interact with system shell, paths and external programs.
 
-Reason
-------
+## Avoid `os:cmd/1` on user input!
 
 ```erlang
-> Email = "hacker+/somepath&reboot#@example.com". % this is a valid email!
+> Email = "proger+/&reboot%23@hackndev.com". % valid email!
 > os:cmd(["mkdir -p ", Email]).
 % path clobbering and a reboot may happen here!
 ```
 
-Examples
---------
+## Examples
 
 ### Onliners
 
@@ -22,9 +18,8 @@ Examples
 > sh:oneliner(["touch", filename:join("/tmp/", Path)]).
 {done,0,<<>>}
 
-> sh:oneliner("uname -v"). % oneliner/1,2 funs do not include newlines
-{done,0,
-      <<"Darwin Kernel Version 12.4.0: Wed May  1 17:57:12 PDT 2013; root:xnu-2050.24.15~1/RELEASE_X86_64">>}
+> sh:oneliner("uname -mnprs").
+{done,0,<<"Darwin mac 18.2.0 x86_64 i386">>}
 
 > sh:oneliner("git describe --always").
 {done,128,<<"fatal: Not a valid object name HEAD">>}
@@ -77,47 +72,32 @@ p2p0: flags=8843<UP,BROADCAST,RUNNING,SIMPLEX,MULTICAST> mtu 2304
 >>> {{2013,8,28},{8,39,14}} exit status: 0
 ```
 
-fdlink Port
------------
+## fdlink: make sure your ports exit when you exit
 
 Consider a case of spawning a port that does not actually
 read its standard input (e.g. `socat` that bridges `AF_UNIX` with `AF_INET`):
 
 ```shell
-# pstree -A -a $(pgrep make)
-make run
-  `-sh -c...
-      `-beam.smp -- -root /usr/lib/erlang -progname erl -- -home /root -- -pa ebin -config run/sys.config -eval[ok = application:
-          |-socat tcp-listen:32133,reuseaddr,bind=127.0.0.1 unix-connect:/var/run/docker.sock
-          `-16*[{beam.smp}]
+# pstree -A -a $(pgrep beam.smp)
+beam.smp -- -root /usr/lib/erlang -progname erl -- -home /root -- -pa ebin -config sys.config
+  |-socat tcp-listen:32133,reuseaddr,bind=127.0.0.1 unix-connect:/var/run/docker.sock
+  `-16*[{beam.smp}]
 ```
 
-If you terminate the node, `beam` will close the port but the process
+If you terminate the node, beam will close the port but the process
 will still remain alive (thus, it will leak). To mitigate this issue,
-you can use `fdlink` that will track `stdin` availability for you:
-
-``` shell
-# pstree -A -a $(pgrep make)
-make run
-  `-sh -c...
-      `-beam.smp -- -root /usr/lib/erlang -progname erl -- -home /root -- -pa ebin -config run/sys.config -eval[ok = application:
-          |-fdlink /usr/bin/socat tcp-listen:32133,reuseaddr,bind=127.0.0.1 unix-connect:/var/run/docker.sock
-          |   `-socat tcp-listen:32133,reuseaddr,bind=127.0.0.1 unix-connect:/var/run/docker.sock
-          `-16*[{beam.smp}]
-```
+you can use `fdlink` that tracks `stdin` availability for you:
 
-### Using
+### Usage
 
 ```erlang
-> Fdlink = sh:fdlink_executable().               % make sure your app dir is setup correctly
-> Fdlink = filename:join("./priv", "fdlink").    % in case you're running directly from erlsh root
-> erlang:open_port({spawn_executable, Fdlink}, [stream, exit_status, {args, ["/usr/bin/socat"|RestOfArgs]}).
+> Fdlink = sh:fdlink_executable().
+> erlang:open_port({spawn_executable, Fdlink}, [stream, exit_status, {args, ["/usr/bin/socat"|Args]}).
 ```
 
 `fdlink` will also close the standard input of its child process.
 
-Credits
--------
+## Credits
 
 * Vladimir Kirillov
 

+ 3 - 3
ebin/sh.app

@@ -1,9 +1,9 @@
 {application, sh,
- [{description, "SH VXZ Executor"},
-  {vsn, "1.9"},
+ [{description, "SH Shell Exec"},
+  {vsn, "4.1.1"},
   {registered, [sh_sup]},
   {applications, [kernel,stdlib]},
-  {modules, [sh_app, sh_path, sh_sup, sh]},
+  {modules, [exe, sh_app, sh_path, sh_sup, sh]},
   {mod, { sh_app, []}},
   {env, []}
  ]}.

+ 31 - 0
src/exe.erl

@@ -0,0 +1,31 @@
+-module(exe).
+-export([reduce/0, run/1, sh/1, sh/3]).
+
+
+reduce() ->
+  fun({_, Chunk}, Acc) ->
+    [Chunk|Acc]
+  end.
+
+run(Args) ->
+  erlang:open_port({spawn_executable, os:find_executable("sh")},
+    [stream, in, out, eof, use_stdio, stderr_to_stdout, binary, exit_status,
+     {args, ["-c", Args]}, {cd, erlang:element(2, file:get_cwd())}, {env, []}]).
+
+sh(Port) -> sh(Port, reduce(), []).
+
+sh(Port, Fun, Acc) ->
+  receive
+    {Port, {exit_status, Status}} -> {done, Status, erlang:iolist_to_binary(lists:reverse(Acc))};
+    {Port, {data, {eol, Line}}}   -> sh(Port, Fun, Fun({eol,   Line}, Acc));
+    {Port, {data, {noeol, Line}}} -> sh(Port, Fun, Fun({noeol, Line}, Acc));
+    {Port, {data, Data}} ->
+      case {binary:match(Data, <<"Sign the certificate? [y/n]:">>) =/= nomatch,
+            binary:match(Data, <<"requests certified, commit?">>)  =/= nomatch} of
+        {true,_}   -> Port ! {erlang:self(), {command,<<"y\n">>}};
+        {_,true}   -> Port ! {erlang:self(), {command,<<"y\n">>}};
+        {_,_}      -> skip
+      end,
+      sh(Port, Fun, Fun({data, Data}, Acc))
+  end.
+

+ 12 - 3
src/sh.erl

@@ -1,5 +1,8 @@
 -module(sh).
--compile([export_all, nowarn_export_all]).
+-export([fdlink_executable/0, executable/1, oneliner/1, oneliner/2,
+  sh_loop/2, sh_loop/3, sh_loop/4,
+  run/1, run/2, run/3, run/4, run/5]).
+
 
 fdlink_executable() -> filename:absname(filename:join(code:priv_dir(sh), "fdlink")).
 oneliner(C) -> run(C, ignoreeol, ".").
@@ -16,7 +19,12 @@ executable(C) ->
     end.
 
 run([C|Args], Log, Cwd) when is_list(C)      -> run(executable(C), Args, Log, Cwd);
-run(Command, Log, Cwd) when is_list(Command) -> run("/bin/sh", ["-c", Command], Log, Cwd).
+run(Command, Log, Cwd) when is_list(Command) ->
+  run(executable("sh"), ["-c", Command], Log, Cwd).
+%  case os:type() of
+%    {win32, _} -> run(executable("cmd"), ["/c", Command], Log, Cwd);
+%    _          -> run(executable("sh"), ["-c", Command], Log, Cwd)
+%  end.
 
 run(Command, Args, ignoreeol, Cwd) ->
     Port = erlang:open_port({spawn_executable, Command},
@@ -55,5 +63,6 @@ sh_loop(Port, Fun, Acc, Flatten) when is_function(Fun) ->
     end.
 
 ts() ->
-    Ts = {{_Y,_M,_D},{_H,_Min,_S}} = calendar:now_to_datetime(erlang:timestamp()),
+    Ts = {{_Y, _M, _D}, {_H, _Min, _S}} = calendar:now_to_datetime(os:timestamp()),
     io_lib:format("~p", [Ts]).
+

+ 4 - 3
src/sh_sup.erl

@@ -1,9 +1,10 @@
 -module(sh_sup).
 -behaviour(supervisor).
--export([start_link/0]).
--export([init/1]).
--define(CHILD(I, Type), {I, {I, start_link, []}, permanent, 5000, Type, [I]}).
+
+-export([start_link/0, init/1]).
+
 
 start_link() -> supervisor:start_link({local, ?MODULE}, ?MODULE, []).
+
 init([]) -> {ok, { {one_for_one, 5, 10}, []} }.