mirror of
https://github.com/zeek/zeek.git
synced 2025-10-02 14:48:21 +00:00
Exec module and raw input reader fixes.
- Do stream mode for commands done by exec module, it seems important in some cases (e.g. ensure requested stdin is fully written). - For cases where the raw input reader knows the child process has been reaped, set the childpid member to a sentinel value to indicate such so we don't later think we should kill it or wait on it anymore. - More error checking on dup2/close calls. Set sentinel values when closing ends of pipes to prevent double closing a fd. - Signal flag not set when raw input reader's child exits as a result of a signal. Left out a test for this -- might be portability issues (e.g. Ubuntu seems to do things different regarding the exit code and also is printing "Killed" to stderr where other platforms don't).
This commit is contained in:
parent
1b40412818
commit
1531980f3d
8 changed files with 42 additions and 28 deletions
|
@ -28,7 +28,7 @@ event Dir::monitor_ev(dir: string, last_files: set[string],
|
|||
callback: function(fname: string),
|
||||
poll_interval: interval)
|
||||
{
|
||||
when ( local result = Exec::run([$cmd=fmt("ls -i \"%s/\"", str_shell_escape(dir))]) )
|
||||
when ( local result = Exec::run([$cmd=fmt("ls -i -1 \"%s/\"", str_shell_escape(dir))]) )
|
||||
{
|
||||
if ( result$exit_code != 0 )
|
||||
{
|
||||
|
|
|
@ -163,6 +163,7 @@ function run(cmd: Command): Result
|
|||
Input::add_event([$name=cmd$uid,
|
||||
$source=fmt("%s |", cmd$cmd),
|
||||
$reader=Input::READER_RAW,
|
||||
$mode=Input::STREAM,
|
||||
$fields=Exec::OneLine,
|
||||
$ev=Exec::line,
|
||||
$want_record=F,
|
||||
|
|
|
@ -95,29 +95,32 @@ bool Raw::Execute()
|
|||
else if ( childpid == 0 )
|
||||
{
|
||||
// we are the child.
|
||||
close(pipes[stdout_in]);
|
||||
dup2(pipes[stdout_out], stdout_fileno);
|
||||
safe_close(pipes[stdout_in]);
|
||||
if ( dup2(pipes[stdout_out], stdout_fileno) == -1 )
|
||||
Error(Fmt("Error on dup2 stdout_out: %d", errno));
|
||||
|
||||
if ( stdin_towrite )
|
||||
{
|
||||
close(pipes[stdin_out]);
|
||||
dup2(pipes[stdin_in], stdin_fileno);
|
||||
safe_close(pipes[stdin_out]);
|
||||
if ( dup2(pipes[stdin_in], stdin_fileno) == -1 )
|
||||
Error(Fmt("Error on dup2 stdin_in: %d", errno));
|
||||
}
|
||||
|
||||
if ( use_stderr )
|
||||
{
|
||||
close(pipes[stderr_in]);
|
||||
dup2(pipes[stderr_out], stderr_fileno);
|
||||
safe_close(pipes[stderr_in]);
|
||||
if ( dup2(pipes[stderr_out], stderr_fileno) == -1 )
|
||||
Error(Fmt("Error on dup2 stderr_out: %d", errno));
|
||||
}
|
||||
|
||||
execl("/bin/sh", "sh", "-c", fname.c_str(), NULL);
|
||||
execl("/bin/sh", "sh", "-c", fname.c_str(), (char*) NULL);
|
||||
fprintf(stderr, "Exec failed :(......\n");
|
||||
exit(255);
|
||||
}
|
||||
else
|
||||
{
|
||||
// we are the parent
|
||||
close(pipes[stdout_out]);
|
||||
safe_close(pipes[stdout_out]);
|
||||
pipes[stdout_out] = -1;
|
||||
|
||||
if ( Info().mode == MODE_STREAM )
|
||||
|
@ -125,7 +128,7 @@ bool Raw::Execute()
|
|||
|
||||
if ( stdin_towrite )
|
||||
{
|
||||
close(pipes[stdin_in]);
|
||||
safe_close(pipes[stdin_in]);
|
||||
pipes[stdin_in] = -1;
|
||||
fcntl(pipes[stdin_out], F_SETFL, O_NONBLOCK); // ya, just always set this to nonblocking. we do not want to block on a program receiving data.
|
||||
// note that there is a small gotcha with it. More data is queued when more data is read from the program output. Hence, when having
|
||||
|
@ -134,7 +137,7 @@ bool Raw::Execute()
|
|||
|
||||
if ( use_stderr )
|
||||
{
|
||||
close(pipes[stderr_out]);
|
||||
safe_close(pipes[stderr_out]);
|
||||
pipes[stderr_out] = -1;
|
||||
fcntl(pipes[stderr_in], F_SETFL, O_NONBLOCK); // true for this too.
|
||||
}
|
||||
|
@ -195,7 +198,10 @@ bool Raw::CloseInput()
|
|||
{
|
||||
for ( int i = 0; i < 6; i ++ )
|
||||
if ( pipes[i] != -1 )
|
||||
close(pipes[i]);
|
||||
{
|
||||
safe_close(pipes[i]);
|
||||
pipes[i] = -1;
|
||||
}
|
||||
}
|
||||
|
||||
file = 0;
|
||||
|
@ -393,11 +399,13 @@ void Raw::WriteToStdin()
|
|||
{
|
||||
Error(Fmt("Writing to child process stdin failed: %d. Stopping writing at position %d", errno, pos));
|
||||
stdin_towrite = 0;
|
||||
close(pipes[stdin_out]);
|
||||
}
|
||||
|
||||
if ( stdin_towrite == 0 ) // send EOF when we are done.
|
||||
close(pipes[stdin_out]);
|
||||
{
|
||||
safe_close(pipes[stdin_out]);
|
||||
pipes[stdin_out] = -1;
|
||||
}
|
||||
|
||||
if ( Info().mode == MODE_MANUAL && stdin_towrite != 0 )
|
||||
{
|
||||
|
@ -528,6 +536,7 @@ bool Raw::DoUpdate()
|
|||
if ( childpid != -1 && waitpid(childpid, &return_code, WNOHANG) != 0 )
|
||||
{
|
||||
// child died
|
||||
childpid = -1;
|
||||
bool signal = false;
|
||||
int code = 0;
|
||||
if ( WIFEXITED(return_code) )
|
||||
|
@ -539,7 +548,7 @@ bool Raw::DoUpdate()
|
|||
|
||||
else if ( WIFSIGNALED(return_code) )
|
||||
{
|
||||
signal = false;
|
||||
signal = true;
|
||||
code = WTERMSIG(return_code);
|
||||
Error(Fmt("Child process exited due to signal %d", code));
|
||||
}
|
||||
|
|
|
@ -3,5 +3,4 @@ test1, [exit_code=0, signal_exit=F, stdout=[done, exit, stop], stderr=<uninitial
|
|||
[out2] = [insert more text here, and there]
|
||||
}]
|
||||
test2, [exit_code=1, signal_exit=F, stdout=[here's something on stdout, some more stdout, last stdout], stderr=[and some stderr, more stderr, last stderr], files=<uninitialized>]
|
||||
test3, [exit_code=9, signal_exit=F, stdout=[FML], stderr=<uninitialized>, files=<uninitialized>]
|
||||
test4, [exit_code=0, signal_exit=F, stdout=[hibye], stderr=<uninitialized>, files=<uninitialized>]
|
||||
|
|
|
@ -39,6 +39,5 @@ event bro_init()
|
|||
try = 0;
|
||||
outfile = open("../out");
|
||||
Input::add_event([$source="cat > ../test.txt |", $reader=Input::READER_RAW, $mode=Input::STREAM, $name="input", $fields=Val, $ev=line, $want_record=F, $config=config_strings]);
|
||||
Input::remove("input");
|
||||
Input::add_event([$source="cat |", $reader=Input::READER_RAW, $mode=Input::STREAM, $name="input2", $fields=Val, $ev=line, $want_record=F, $config=config_strings]);
|
||||
}
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
# @TEST-REQUIRES: which httpd
|
||||
# @TEST-REQUIRES: which python
|
||||
#
|
||||
# @TEST-EXEC: btest-bg-run httpd python $SCRIPTS/httpd.py --max 1
|
||||
|
@ -8,7 +7,7 @@
|
|||
# @TEST-EXEC: btest-diff bro/.stdout
|
||||
|
||||
@load base/utils/active-http
|
||||
|
||||
@load base/frameworks/communication # let network-time run. otherwise there are no heartbeats...
|
||||
redef exit_only_after_terminate = T;
|
||||
|
||||
event bro_init()
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
# @TEST-EXEC: btest-bg-run bro bro -b ../dirtest.bro
|
||||
# @TEST-EXEC: btest-bg-wait 10
|
||||
# @TEST-EXEC: btest-bg-wait 15
|
||||
# @TEST-EXEC: TEST_DIFF_CANONIFIER=$SCRIPTS/diff-sort btest-diff bro/.stdout
|
||||
|
||||
@TEST-START-FILE dirtest.bro
|
||||
|
||||
@load base/utils/dir
|
||||
|
||||
@load base/frameworks/communication # let network-time run. otherwise there are no heartbeats...
|
||||
redef exit_only_after_terminate = T;
|
||||
|
||||
global c: count = 0;
|
||||
|
@ -33,14 +33,20 @@ function new_file2(fname: string)
|
|||
event change_things()
|
||||
{
|
||||
system("touch ../testdir/newone");
|
||||
system("rm ../testdir/bye && touch ../testdir/bye");
|
||||
system("rm ../testdir/bye");
|
||||
}
|
||||
|
||||
event change_things2()
|
||||
{
|
||||
system("touch ../testdir/bye");
|
||||
}
|
||||
|
||||
event bro_init()
|
||||
{
|
||||
Dir::monitor("../testdir", new_file1, .5sec);
|
||||
Dir::monitor("../testdir", new_file2, 1sec);
|
||||
schedule 1sec { change_things() };
|
||||
schedule 3sec { change_things() };
|
||||
schedule 6sec { change_things2() };
|
||||
}
|
||||
|
||||
@TEST-END-FILE
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
# @TEST-EXEC: btest-bg-run bro bro -b ../exectest.bro
|
||||
# @TEST-EXEC: btest-bg-wait 10
|
||||
# @TEST-EXEC: btest-bg-wait 15
|
||||
# @TEST-EXEC: TEST_DIFF_CANONIFIER=$SCRIPTS/diff-sort btest-diff bro/.stdout
|
||||
|
||||
@TEST-START-FILE exectest.bro
|
||||
|
||||
@load base/utils/exec
|
||||
|
||||
@load base/frameworks/communication # let network-time run. otherwise there are no heartbeats...
|
||||
redef exit_only_after_terminate = T;
|
||||
|
||||
global c: count = 0;
|
||||
|
@ -14,7 +14,7 @@ function check_exit_condition()
|
|||
{
|
||||
c += 1;
|
||||
|
||||
if ( c == 4 )
|
||||
if ( c == 3 )
|
||||
terminate();
|
||||
}
|
||||
|
||||
|
@ -32,7 +32,8 @@ event bro_init()
|
|||
test_cmd("test1", [$cmd="bash ../somescript.sh",
|
||||
$read_files=set("out1", "out2")]);
|
||||
test_cmd("test2", [$cmd="bash ../nofiles.sh"]);
|
||||
test_cmd("test3", [$cmd="bash ../suicide.sh"]);
|
||||
# Not sure of a portable way to test signals yet.
|
||||
#test_cmd("test3", [$cmd="bash ../suicide.sh"]);
|
||||
test_cmd("test4", [$cmd="bash ../stdin.sh", $stdin="hibye"]);
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue