saleyn / erlexec

Execute and control OS processes from Erlang/OTP
https://hexdocs.pm/erlexec/readme.html
Other
541 stars 141 forks source link

Close stdin #74

Closed pichi closed 8 years ago

pichi commented 8 years ago

None of the examples cover how to close stdin after providing custom generated input. For example calling tac.

1> application:start(erlexec).
ok
2> Watcher = spawn(fun F() -> receive Msg -> io:format("Got: ~p~n", [Msg]), F() end end).
<0.103.0>
3> {ok, Pid, OsPid} = exec:run("tac", [stdin, {stdout, Watcher}, {stderr, Watcher}]).
{ok,<0.105.0>,15922}
4> exec:send(Pid, <<"foo\n">>).                                                      
ok
5> exec:send(Pid, <<"bar\n">>).                                                      
ok
6> exec:send(Pid, <<"baz\n">>).                                                      
ok

And what I am supposed to do now? exec:manage/2 doesn't do the trick:

7> exec:manage(OsPid, [{stdin, close}]).
{ok,<0.110.0>,15922}

I am missing command for closing stdin of the existing and opened stdin of the existing command.

Compare with exec:run("echo -e 'foo\\nbar\\nbaz' | tac", [{stdout, Watcher}, {stderr, Watcher}]).

saleyn commented 8 years ago

This is currently not supported. I am not sure if closing the pipe's output end of the parent process (erlexec) will in fact signal the end of file in the spawned child (tac). The first example is different from the second, because in the second case the tac is invoked by the command shell via popen() versus execv() invocation in the first case.

You can try to introduce a new command in exec.cpp that will lookup the pid of the child process state, close the writing end of the pipe, and see if it triggers the OS to close the corresponding stdin end of the pipe in the child process. If so, you are welcome to send a PR.

pichi commented 8 years ago

The first example is different from the second, because in the second case the tac is invoked by the command shell via popen() versus execv() invocation in the first case.

It is actually exact way how the shell is doing it. It opens pipe by popen() and then it calls execve() in child. How do you think shell executes tac? :-)

saleyn commented 8 years ago

I would think the shell does it differently - via the pipe/fork/exec combination since popen() only returns a stream, and as the result the child is already spawned so there's no need to call execve() explicitly. I haven't looked at the library sources for the popen(), but I am nearly certain that it's got the execve() call in its implementation.

pichi commented 8 years ago

From the manual:

The popen() function opens a process by creating a pipe, forking, and invoking the shell.

popen() is just a function which does all those steps for you but the mechanism behind is exactly same.

pichi commented 8 years ago

For example: http://cnds.eecs.jacobs-university.de/courses/os-2010/src/popen/popen.c

pichi commented 8 years ago

From BSD: http://www.retro11.de/ouxr/211bsd/usr/src/lib/libc/gen/popen.c.html

Exactly same shit :)

pichi commented 8 years ago

Unfortunately, I'm not familiar with C++ only C.

pichi commented 8 years ago

Nice:

1> application:start(erlexec).
ok
2> Watcher = spawn(fun F() -> receive Msg -> io:format("Got: ~p~n", [Msg]), F() end end).
<0.112.0>
3> {ok, Pid, OsPid} = exec:run("tac", [stdin, {stdout, Watcher}, {stderr, Watcher}]).
{ok,<0.114.0>,26143}
4> exec:send(Pid, <<"foo\n">>).
ok
5> exec:send(Pid, <<"bar\n">>).
ok
6> exec:send(Pid, <<"baz\n">>).
ok
7> exec:send(Pid, eof).
ok
Got: {stdout,26143,<<"baz\nbar\nfoo\n">>}
pichi commented 8 years ago

I can't get it why they can't implement it in Erlang OTP as well.

pichi commented 8 years ago

13 tests, 0 failures :)

saleyn commented 8 years ago

Yes, that exec:send(Pid, eof). makes sense, since it signifies the consumer's end of an end of input.