From dd7f5b1728f6194156baabd2e0d1a326a3d74df4 Mon Sep 17 00:00:00 2001 From: Tobias Leich Date: Sun, 9 Nov 2014 17:48:23 +0100 Subject: [PATCH 1/3] first (explosive) attempt to port openpipe to nqp@parrot We need this op to actually open a pipe with a given CWD and ENV, the open("rp") whih parrot provides does not let us pass these. --- src/vm/parrot/QAST/Operations.nqp | 1 + src/vm/parrot/ops/nqp.ops | 214 ++++++++++++++++++++++++++++++ 2 files changed, 215 insertions(+) diff --git a/src/vm/parrot/QAST/Operations.nqp b/src/vm/parrot/QAST/Operations.nqp index 1919345830..47aa67dd00 100644 --- a/src/vm/parrot/QAST/Operations.nqp +++ b/src/vm/parrot/QAST/Operations.nqp @@ -2749,6 +2749,7 @@ QAST::Operations.add_core_pirop_mapping('sleep', 'sleep', '0n', :inlinable(1)); QAST::Operations.add_core_pirop_mapping('gethostname', 'nqp_gethostname', 'S'); QAST::Operations.add_core_pirop_mapping('spawn', 'nqp_spawn', 'IPsP'); QAST::Operations.add_core_pirop_mapping('shell', 'nqp_shell', 'IssP'); +QAST::Operations.add_core_pirop_mapping('openpipe', 'nqp_openpipe', 'PssPs'); QAST::Operations.add_core_pirop_mapping('getenvhash', 'nqp_getenvhash', 'P'); QAST::Operations.add_core_op('getpid', -> $qastcomp, $op { diff --git a/src/vm/parrot/ops/nqp.ops b/src/vm/parrot/ops/nqp.ops index 994365de06..3a17961367 100644 --- a/src/vm/parrot/ops/nqp.ops +++ b/src/vm/parrot/ops/nqp.ops @@ -6,6 +6,7 @@ BEGIN_OPS_PREAMBLE #include "parrot/parrot.h" #include "parrot/extend.h" #include "parrot/dynext.h" +#include "pmc/pmc_filehandle.h" /* 6modely includes. */ #include "../6model/sixmodelobject.h" @@ -666,6 +667,162 @@ static INTVAL Run_OS_Command(PARROT_INTERP, PMC *command, PMC *env_hash) /* make gcc happy */ return 1; } +static PIOHANDLE Run_OS_Command_Piped(PARROT_INTERP, PMC *command, PMC *env_hash, INTVAL flags, INTVAL *pid) { + char **saved = environ; + char **cmd = pack_arg_array(interp, command); + char **env = pack_env_hash(interp, env_hash); + char *prog = cmd[0]; + PIOHANDLE handles[3]; + int in_fds[2]; + int out_fds[2]; + int err_fds[2]; + int status; + + //~ status = execvp(prog, cmd); + + //~ *pid = Parrot_proc_exec(interp, command, PARROT_EXEC_STDOUT, handles); + if (flags & PARROT_EXEC_STDIN + && pipe(in_fds) < 0) + goto error_pipe_in; + + if (flags & PARROT_EXEC_STDOUT + && pipe(out_fds) < 0) + goto error_pipe_out; + + if (flags & PARROT_EXEC_STDERR + && pipe(err_fds) < 0) + goto error_pipe_err; + + *pid = fork(); + + if (*pid < 0) + goto error_fork; + + if (*pid > 0) { + printf("%s:%d pid > 0 (%p)\n", __FILE__, __LINE__, pid); + if (flags & PARROT_EXEC_STDIN) { + /* close fd for reading */ + close(in_fds[0]); + handles[0] = in_fds[1]; + } + + if (flags & PARROT_EXEC_STDOUT) { + /* close fd for writing */ + close(out_fds[1]); + handles[1] = out_fds[0]; + } + + if (flags & PARROT_EXEC_STDERR) { + /* close fd for writing */ + close(err_fds[1]); + handles[2] = err_fds[0]; + } + } + else /* (pid == 0) */ { + /* Child - exec process */ + //~ char * argv[4]; + /* C strings for the execv call defined without const to avoid + * const problems without copying them. + * Please don't change this without testing with a c++ compiler. + */ + //~ static char auxarg0[] = "/bin/sh"; + //~ static char auxarg1[] = "-c"; + fprintf(stderr, "%s:%d child before anything\n", __FILE__, __LINE__); + + if (flags & PARROT_EXEC_STDIN) { + /* redirect stdin to pipe */ + close(in_fds[1]); + close(STDIN_FILENO); + + if (dup(in_fds[0]) != STDIN_FILENO) + PARROT_FORCE_EXIT(EXIT_FAILURE); + } + + fprintf(stderr, "%s:%d child before anything 2\n", __FILE__, __LINE__); + if (flags & PARROT_EXEC_STDOUT) { + fprintf(stderr, "%s:%d child before anything 2.1\n", __FILE__, __LINE__); + /* redirect stdout to pipe */ + close(out_fds[0]); + fprintf(stderr, "%s:%d child before anything 2.2\n", __FILE__, __LINE__); + close(STDOUT_FILENO); + fprintf(stderr, "%s:%d child before anything 2.3\n", __FILE__, __LINE__); + + if (dup(out_fds[1]) != STDOUT_FILENO) { + fprintf(stderr, "%s:%d child before anything 2.4\n", __FILE__, __LINE__); + PARROT_FORCE_EXIT(EXIT_FAILURE); + fprintf(stderr, "%s:%d child before anything 2.5\n", __FILE__, __LINE__); + } + + if (!(flags & PARROT_EXEC_STDERR)) { + fprintf(stderr, "%s:%d child before anything 2.6\n", __FILE__, __LINE__); + //~ close(STDERR_FILENO); + fprintf(stderr, "%s:%d child before anything 2.7\n", __FILE__, __LINE__); + + if (dup(out_fds[1]) != STDERR_FILENO) { + fprintf(stderr, "%s:%d child before anything 2.8\n", __FILE__, __LINE__); + //~ PARROT_FORCE_EXIT(EXIT_FAILURE); + fprintf(stderr, "%s:%d child before anything 2.9\n", __FILE__, __LINE__); + } + } + } + + fprintf(stderr, "%s:%d child before anything 3\n", __FILE__, __LINE__); + if (flags & PARROT_EXEC_STDERR) { + /* redirect stderr to pipe */ + close(err_fds[0]); + close(STDERR_FILENO); + + if (dup(err_fds[1]) != STDERR_FILENO) + PARROT_FORCE_EXIT(EXIT_FAILURE); + } + + fprintf(stderr, "%s:%d child before anything 4\n", __FILE__, __LINE__); + environ = env; + + fprintf(stderr, "%s:%d child before execvp, '%s'\n", __FILE__, __LINE__, prog); + //~ argv [0] = auxarg0; + //~ argv [1] = auxarg1; + //~ argv [2] = Parrot_str_to_cstring(interp, command); + //~ argv [3] = NULL; + //~ execv(argv [0], argv); + status = execvp(prog, cmd); + fprintf(stderr, "%s:%d child after execvp (status=%d)\n", __FILE__, __LINE__, status); + + /* if we get here, something's horribly wrong, but free anyway... */ + free_packed(env); + free_packed(cmd); + environ = saved; + + /* Will never reach this unless exec fails. + * No need to clean up, we're just going to exit */ + perror("execvp"); + PARROT_FORCE_EXIT(EXIT_FAILURE); + } + + return handles[1]; + + error_fork: + if (flags & PARROT_EXEC_STDERR) { + close(err_fds[0]); + close(err_fds[1]); + } + + error_pipe_err: + if (flags & PARROT_EXEC_STDOUT) { + close(out_fds[0]); + close(out_fds[1]); + } + + error_pipe_out: + if (flags & PARROT_EXEC_STDIN) { + close(in_fds[0]); + close(in_fds[1]); + } + + error_pipe_in: + Parrot_ex_throw_from_c_args(interp, NULL, EXCEPTION_PIO_ERROR, + "Error executing process: %s", strerror(errno)); +} #endif /* @@ -3776,6 +3933,63 @@ inline op nqp_delete_f(out INT, in STR) :base_core { #endif } +inline op nqp_openpipe(out PMC, in STR, in STR, in PMC, in STR) { + STRING *dir = $3; + PMC *env = $4; + STRING * const old_cwd = Parrot_file_getcwd(interp); + INTVAL pid; + PIOHANDLE os_handle; + const INTVAL typenum = Parrot_hll_get_ctx_HLL_type(interp, enum_class_FileHandle); + PMC *handle = Parrot_pmc_new(interp, typenum); + const IO_VTABLE * vtable = IO_GET_VTABLE(interp, handle); + IO_BUFFER * read_buffer = IO_GET_READ_BUFFER(interp, handle); + PMC * command = Parrot_pmc_new(interp, enum_class_ResizableStringArray); +#ifdef WIN32 + const STRING *comspec = Parrot_str_new(interp, "ComSpec", 0); + VTABLE_push_string(interp, command, Parrot_getenv(interp, comspec)); + VTABLE_push_string(interp, command, Parrot_str_new_constant(interp, "/c")); +#else + VTABLE_push_string(interp, command, Parrot_str_new_constant(interp, "sh")); + VTABLE_push_string(interp, command, Parrot_str_new_constant(interp, "-c")); +#endif + VTABLE_push_string(interp, command, $2); + + printf("%s:%d nqp_openpipe (buffer1=%p)\n", __FILE__, __LINE__, read_buffer); + Parrot_file_chdir(interp, dir); + os_handle = Run_OS_Command_Piped(interp, command, env, PARROT_EXEC_STDOUT, &pid); + Parrot_file_chdir(interp, old_cwd); + + printf("%s:%d nqp_openpipe (%p)\n", __FILE__, __LINE__, pid); + /* Save the pid of the child, we'll wait for it when closing */ + VTABLE_set_integer_keyed_int(interp, handle, 0, pid); + + SETATTR_FileHandle_os_handle(interp, handle, os_handle); + SETATTR_FileHandle_flags(interp, handle, PIO_F_READ); + SETATTR_FileHandle_filename(interp, handle, Parrot_str_cstring(interp, $2)); + SETATTR_FileHandle_mode(interp, handle, Parrot_str_new_constant(interp, "rp")); + SETATTR_FileHandle_file_pos(interp, handle, 0); + + /* If this type uses buffers by default, set them up, and if we're + in an acceptable mode, set up buffers. */ + if (!read_buffer) { + read_buffer = (IO_BUFFER *)Parrot_gc_allocate_fixed_size_storage(interp, + sizeof(IO_BUFFER)); + read_buffer->encoding = NULL; + read_buffer->buffer_size = 2048; /* PIO_BUFFER_MIN_SIZE */ + read_buffer->buffer_ptr = (char *)mem_sys_allocate(2048); + read_buffer->buffer_start = read_buffer->buffer_ptr; + read_buffer->buffer_end = read_buffer->buffer_ptr; + PARROT_ASSERT(BUFFER_IS_EMPTY(read_buffer)); + read_buffer->raw_reads = 0; + read_buffer->flags |= PIO_BF_MALLOC; + printf("%s:%d nqp_openpipe (buffer2=%p)\n", __FILE__, __LINE__, read_buffer); + + VTABLE_set_pointer_keyed_int(interp, handle, IO_PTR_IDX_READ_BUFFER, read_buffer); + } + + $1 = handle; +} + inline op nqp_spawn(out INT, in PMC, in STR, in PMC) { STRING *dir = $3; PMC *env = $4; From 4818829d339e6395308affa38cf0ad7296aeb97b Mon Sep 17 00:00:00 2001 From: Tobias Leich Date: Mon, 10 Nov 2014 16:55:10 +0100 Subject: [PATCH 2/3] add working openpipe op for nqp on parrot on linux That means that we have openpipe on all platforms/backends except for parrot on windows. --- src/vm/parrot/ops/nqp.ops | 87 +++++++++++-------------------------- t/jvm/02-pipes.t | 22 ---------- t/moar/02-pipes.t | 22 ---------- t/nqp/86-pipes.t | 51 ++++++++++++++++++++++ tools/build/PARROT_REVISION | 2 +- 5 files changed, 77 insertions(+), 107 deletions(-) delete mode 100644 t/jvm/02-pipes.t delete mode 100644 t/moar/02-pipes.t create mode 100644 t/nqp/86-pipes.t diff --git a/src/vm/parrot/ops/nqp.ops b/src/vm/parrot/ops/nqp.ops index 2f4735e3bd..1bbd14eed2 100644 --- a/src/vm/parrot/ops/nqp.ops +++ b/src/vm/parrot/ops/nqp.ops @@ -6,6 +6,7 @@ BEGIN_OPS_PREAMBLE #include "parrot/parrot.h" #include "parrot/extend.h" #include "parrot/dynext.h" +#include "parrot/io.h" #include "pmc/pmc_filehandle.h" /* 6modely includes. */ @@ -667,7 +668,8 @@ static INTVAL Run_OS_Command(PARROT_INTERP, PMC *command, PMC *env_hash) /* make gcc happy */ return 1; } -static PIOHANDLE Run_OS_Command_Piped(PARROT_INTERP, PMC *command, PMC *env_hash, INTVAL flags, INTVAL *pid) { +static PIOHANDLE Run_OS_Command_Piped(PARROT_INTERP, PMC *command, PMC *env_hash, INTVAL flags, + INTVAL *pid, INTVAL *status) { char **saved = environ; char **cmd = pack_arg_array(interp, command); char **env = pack_env_hash(interp, env_hash); @@ -676,11 +678,7 @@ static PIOHANDLE Run_OS_Command_Piped(PARROT_INTERP, PMC *command, PMC *env_hash int in_fds[2]; int out_fds[2]; int err_fds[2]; - int status; - //~ status = execvp(prog, cmd); - - //~ *pid = Parrot_proc_exec(interp, command, PARROT_EXEC_STDOUT, handles); if (flags & PARROT_EXEC_STDIN && pipe(in_fds) < 0) goto error_pipe_in; @@ -699,7 +697,6 @@ static PIOHANDLE Run_OS_Command_Piped(PARROT_INTERP, PMC *command, PMC *env_hash goto error_fork; if (*pid > 0) { - printf("%s:%d pid > 0 (%p)\n", __FILE__, __LINE__, pid); if (flags & PARROT_EXEC_STDIN) { /* close fd for reading */ close(in_fds[0]); @@ -720,14 +717,6 @@ static PIOHANDLE Run_OS_Command_Piped(PARROT_INTERP, PMC *command, PMC *env_hash } else /* (pid == 0) */ { /* Child - exec process */ - //~ char * argv[4]; - /* C strings for the execv call defined without const to avoid - * const problems without copying them. - * Please don't change this without testing with a c++ compiler. - */ - //~ static char auxarg0[] = "/bin/sh"; - //~ static char auxarg1[] = "-c"; - fprintf(stderr, "%s:%d child before anything\n", __FILE__, __LINE__); if (flags & PARROT_EXEC_STDIN) { /* redirect stdin to pipe */ @@ -738,35 +727,22 @@ static PIOHANDLE Run_OS_Command_Piped(PARROT_INTERP, PMC *command, PMC *env_hash PARROT_FORCE_EXIT(EXIT_FAILURE); } - fprintf(stderr, "%s:%d child before anything 2\n", __FILE__, __LINE__); if (flags & PARROT_EXEC_STDOUT) { - fprintf(stderr, "%s:%d child before anything 2.1\n", __FILE__, __LINE__); /* redirect stdout to pipe */ close(out_fds[0]); - fprintf(stderr, "%s:%d child before anything 2.2\n", __FILE__, __LINE__); close(STDOUT_FILENO); - fprintf(stderr, "%s:%d child before anything 2.3\n", __FILE__, __LINE__); - if (dup(out_fds[1]) != STDOUT_FILENO) { - fprintf(stderr, "%s:%d child before anything 2.4\n", __FILE__, __LINE__); + if (dup(out_fds[1]) != STDOUT_FILENO) PARROT_FORCE_EXIT(EXIT_FAILURE); - fprintf(stderr, "%s:%d child before anything 2.5\n", __FILE__, __LINE__); - } if (!(flags & PARROT_EXEC_STDERR)) { - fprintf(stderr, "%s:%d child before anything 2.6\n", __FILE__, __LINE__); - //~ close(STDERR_FILENO); - fprintf(stderr, "%s:%d child before anything 2.7\n", __FILE__, __LINE__); - - if (dup(out_fds[1]) != STDERR_FILENO) { - fprintf(stderr, "%s:%d child before anything 2.8\n", __FILE__, __LINE__); - //~ PARROT_FORCE_EXIT(EXIT_FAILURE); - fprintf(stderr, "%s:%d child before anything 2.9\n", __FILE__, __LINE__); - } + close(STDERR_FILENO); + + if (dup(out_fds[1]) != STDERR_FILENO) + PARROT_FORCE_EXIT(EXIT_FAILURE); } } - fprintf(stderr, "%s:%d child before anything 3\n", __FILE__, __LINE__); if (flags & PARROT_EXEC_STDERR) { /* redirect stderr to pipe */ close(err_fds[0]); @@ -776,17 +752,9 @@ static PIOHANDLE Run_OS_Command_Piped(PARROT_INTERP, PMC *command, PMC *env_hash PARROT_FORCE_EXIT(EXIT_FAILURE); } - fprintf(stderr, "%s:%d child before anything 4\n", __FILE__, __LINE__); environ = env; - fprintf(stderr, "%s:%d child before execvp, '%s'\n", __FILE__, __LINE__, prog); - //~ argv [0] = auxarg0; - //~ argv [1] = auxarg1; - //~ argv [2] = Parrot_str_to_cstring(interp, command); - //~ argv [3] = NULL; - //~ execv(argv [0], argv); - status = execvp(prog, cmd); - fprintf(stderr, "%s:%d child after execvp (status=%d)\n", __FILE__, __LINE__, status); + *status = execvp(prog, cmd); /* if we get here, something's horribly wrong, but free anyway... */ free_packed(env); @@ -3944,16 +3912,17 @@ inline op nqp_delete_f(out INT, in STR) :base_core { } inline op nqp_openpipe(out PMC, in STR, in STR, in PMC, in STR) { - STRING *dir = $3; - PMC *env = $4; - STRING * const old_cwd = Parrot_file_getcwd(interp); - INTVAL pid; + STRING *dir = $3; + PMC *env = $4; + STRING * const old_cwd = Parrot_file_getcwd(interp); + INTVAL status = 0; + const INTVAL typenum = Parrot_hll_get_ctx_HLL_type(interp, enum_class_FileHandle); + PMC *handle = Parrot_pmc_new(interp, typenum); + IO_BUFFER * read_buffer = IO_GET_READ_BUFFER(interp, handle); + PMC * command = Parrot_pmc_new(interp, enum_class_ResizableStringArray); + const VTABLE * vtable = Parrot_io_get_vtable(interp, IO_VTABLE_PIPE, NULL); PIOHANDLE os_handle; - const INTVAL typenum = Parrot_hll_get_ctx_HLL_type(interp, enum_class_FileHandle); - PMC *handle = Parrot_pmc_new(interp, typenum); - const IO_VTABLE * vtable = IO_GET_VTABLE(interp, handle); - IO_BUFFER * read_buffer = IO_GET_READ_BUFFER(interp, handle); - PMC * command = Parrot_pmc_new(interp, enum_class_ResizableStringArray); + INTVAL pid; #ifdef WIN32 const STRING *comspec = Parrot_str_new(interp, "ComSpec", 0); VTABLE_push_string(interp, command, Parrot_getenv(interp, comspec)); @@ -3964,23 +3933,18 @@ inline op nqp_openpipe(out PMC, in STR, in STR, in PMC, in STR) { #endif VTABLE_push_string(interp, command, $2); - printf("%s:%d nqp_openpipe (buffer1=%p)\n", __FILE__, __LINE__, read_buffer); Parrot_file_chdir(interp, dir); - os_handle = Run_OS_Command_Piped(interp, command, env, PARROT_EXEC_STDOUT, &pid); + os_handle = Run_OS_Command_Piped(interp, command, env, PARROT_EXEC_STDOUT, &pid, &status); Parrot_file_chdir(interp, old_cwd); - printf("%s:%d nqp_openpipe (%p)\n", __FILE__, __LINE__, pid); - /* Save the pid of the child, we'll wait for it when closing */ - VTABLE_set_integer_keyed_int(interp, handle, 0, pid); - + SETATTR_FileHandle_io_vtable(interp, handle, vtable); + SETATTR_FileHandle_process_id(interp, handle, pid); + SETATTR_FileHandle_exit_status(interp, handle, status); SETATTR_FileHandle_os_handle(interp, handle, os_handle); - SETATTR_FileHandle_flags(interp, handle, PIO_F_READ); - SETATTR_FileHandle_filename(interp, handle, Parrot_str_cstring(interp, $2)); + SETATTR_FileHandle_flags(interp, handle, PIO_F_PIPE | PIO_F_READ); SETATTR_FileHandle_mode(interp, handle, Parrot_str_new_constant(interp, "rp")); SETATTR_FileHandle_file_pos(interp, handle, 0); - /* If this type uses buffers by default, set them up, and if we're - in an acceptable mode, set up buffers. */ if (!read_buffer) { read_buffer = (IO_BUFFER *)Parrot_gc_allocate_fixed_size_storage(interp, sizeof(IO_BUFFER)); @@ -3992,9 +3956,8 @@ inline op nqp_openpipe(out PMC, in STR, in STR, in PMC, in STR) { PARROT_ASSERT(BUFFER_IS_EMPTY(read_buffer)); read_buffer->raw_reads = 0; read_buffer->flags |= PIO_BF_MALLOC; - printf("%s:%d nqp_openpipe (buffer2=%p)\n", __FILE__, __LINE__, read_buffer); - VTABLE_set_pointer_keyed_int(interp, handle, IO_PTR_IDX_READ_BUFFER, read_buffer); + SETATTR_FileHandle_read_buffer(interp, handle, read_buffer); } $1 = handle; diff --git a/t/jvm/02-pipes.t b/t/jvm/02-pipes.t deleted file mode 100644 index 9f40dc6eab..0000000000 --- a/t/jvm/02-pipes.t +++ /dev/null @@ -1,22 +0,0 @@ -#! nqp - -# Testing nqp::openpipe on JVM. - -plan(7); - -my $p := nqp::openpipe('echo aardvarks', nqp::cwd(), nqp::getenvhash(), ''); -ok( nqp::defined($p) == 1, 'nqp::openpipe' ); - -my $pstr := nqp::readallfh($p); -ok( $pstr ~~ / 'aardvarks' /, 'nqp::readallfh with a pipe'); - -ok( nqp::closefh($p), 'nqp::closefh with a pipe'); -ok( nqp::closefh($p), 'nqp::closefh with a pipe already closed'); - -my $q := nqp::openpipe('doesnotexist', nqp::cwd(), nqp::getenvhash(), ''); -ok( nqp::defined($q) == 1, 'nqp::openpipe nonexistent cmd'); - -my $qstr := nqp::readallfh($q); -ok( $qstr ~~ / 'not found' | 'not recognized' /, 'nqp::readallfh with a pipe nonexistent command'); - -ok( nqp::closefh($q), 'nqp::closefh with a pipe nonexistent command'); diff --git a/t/moar/02-pipes.t b/t/moar/02-pipes.t deleted file mode 100644 index 6fbe700c12..0000000000 --- a/t/moar/02-pipes.t +++ /dev/null @@ -1,22 +0,0 @@ -#! nqp - -# Testing nqp::openpipe on MoarVM. - -plan(7); - -my $p := nqp::openpipe('echo aardvarks', nqp::cwd(), nqp::getenvhash(), ''); -ok( nqp::defined($p) == 1, 'nqp::openpipe' ); - -my $pstr := nqp::readallfh($p); -ok( $pstr ~~ / 'aardvarks' /, 'nqp::readallfh with a pipe'); - -ok( nqp::closefh($p), 'nqp::closefh with a pipe'); -ok( nqp::closefh($p), 'nqp::closefh with a pipe already closed'); - -my $q := nqp::openpipe('doesnotexist', nqp::cwd(), nqp::getenvhash(), ''); -ok( nqp::defined($q) == 1, 'nqp::openpipe nonexistent cmd'); - -my $qstr := nqp::readallfh($q); -ok( $qstr eq '', 'nqp::readallfh with a pipe nonexistent command'); - -ok( nqp::closefh($q), 'nqp::closefh with a pipe nonexistent command'); diff --git a/t/nqp/86-pipes.t b/t/nqp/86-pipes.t new file mode 100644 index 0000000000..bf697fed05 --- /dev/null +++ b/t/nqp/86-pipes.t @@ -0,0 +1,51 @@ +#! nqp + +# Testing nqp::openpipe on JVM. + +plan(14); + +{ + my $p := nqp::openpipe('echo aardvarks', nqp::cwd(), nqp::getenvhash(), ''); + ok( nqp::defined($p) == 1, 'nqp::openpipe' ); + + my $pstr := nqp::readallfh($p); + ok( $pstr ~~ / 'aardvarks' /, 'nqp::readallfh with a pipe'); + + # What should the return value of nqp::close be? MoarVM and JVM always return 1. + nqp::closefh($p); ok( 1, 'nqp::closefh with a pipe'); + nqp::closefh($p); ok( 1, 'nqp::closefh with a pipe already closed'); + + my $q := nqp::openpipe('doesnotexist', nqp::cwd(), nqp::getenvhash(), ''); + ok( nqp::defined($q) == 1, 'nqp::openpipe nonexistent cmd'); + + my $qstr := nqp::readallfh($q); + nqp::getcomp('nqp').backend.name eq 'moar' + ?? ok( $qstr eq '', 'nqp::readallfh with a pipe nonexistent command') + !! ok( $qstr ~~ / 'not found' | 'not recognized' /, 'nqp::readallfh with a pipe nonexistent command'); + + + nqp::closefh($q); ok( 1, 'nqp::closefh with a pipe nonexistent command'); +} + +# same tests but do nqp::closefh_i instead of nqp::closefh +{ + my $p := nqp::openpipe('echo aardvarks', nqp::cwd(), nqp::getenvhash(), ''); + ok( nqp::defined($p) == 1, 'nqp::openpipe' ); + + my $pstr := nqp::readallfh($p); + ok( $pstr ~~ / 'aardvarks' /, 'nqp::readallfh with a pipe'); + + ok( nqp::closefh_i($p) == 0, 'nqp::closefh_i with a pipe'); + ok( nqp::closefh_i($p) == 0, 'nqp::closefh_i with a pipe already closed'); + + my $q := nqp::openpipe('doesnotexist', nqp::cwd(), nqp::getenvhash(), ''); + ok( nqp::defined($q) == 1, 'nqp::openpipe nonexistent cmd'); + + my $qstr := nqp::readallfh($q); + nqp::getcomp('nqp').backend.name eq 'moar' + ?? ok( $qstr eq '', 'nqp::readallfh with a pipe nonexistent command') + !! ok( $qstr ~~ / 'not found' | 'not recognized' /, 'nqp::readallfh with a pipe nonexistent command'); + + + ok( nqp::closefh_i($q) != 0, 'nqp::closefh_i with a pipe nonexistent command'); +} diff --git a/tools/build/PARROT_REVISION b/tools/build/PARROT_REVISION index aec28837ff..4613be0a5d 100644 --- a/tools/build/PARROT_REVISION +++ b/tools/build/PARROT_REVISION @@ -1 +1 @@ -RELEASE_6_9_0-72-g4b90157 +RELEASE_6_9_0-81-g6d8687c From fd8e5ed403cd2c609450cce78d042a8bd1ce4942 Mon Sep 17 00:00:00 2001 From: Tobias Leich Date: Sun, 16 Nov 2014 16:09:54 +0100 Subject: [PATCH 3/3] implement openpipe on parrot+windows --- src/vm/parrot/ops/nqp.ops | 91 +++++++++++++++++++++++++++++++++++++ t/nqp/86-pipes.t | 18 ++++---- tools/build/PARROT_REVISION | 2 +- 3 files changed, 102 insertions(+), 9 deletions(-) diff --git a/src/vm/parrot/ops/nqp.ops b/src/vm/parrot/ops/nqp.ops index 40880f0237..cd4647bdde 100644 --- a/src/vm/parrot/ops/nqp.ops +++ b/src/vm/parrot/ops/nqp.ops @@ -565,6 +565,97 @@ static char *create_command_line(const char *const *args) { return cmd; } +static PIOHANDLE Run_OS_Command_Piped(PARROT_INTERP, PMC *command, PMC *env_hash, INTVAL flags, + INTVAL *pid, INTVAL *status) { + HANDLE current = GetCurrentProcess(); + HANDLE hnull = INVALID_HANDLE_VALUE; + HANDLE hread = INVALID_HANDLE_VALUE; + HANDLE hwrite = INVALID_HANDLE_VALUE; + HANDLE hchild = INVALID_HANDLE_VALUE; + STARTUPINFO si; + PROCESS_INFORMATION pi; + SECURITY_ATTRIBUTES sec; + const char *env = pack_env_hash(interp, env_hash); + const char *const *argv = pack_arg_array(interp, command); + const char *cmd = create_command_line(argv); + PIOHANDLE os_handle; + + pi.hThread = INVALID_HANDLE_VALUE; + pi.hProcess = INVALID_HANDLE_VALUE; + sec.nLength = sizeof sec; + sec.lpSecurityDescriptor = NULL; + sec.bInheritHandle = TRUE; + + si.cb = sizeof si; + GetStartupInfo(&si); + si.dwFlags = STARTF_USESTDHANDLES; + if (CreatePipe(&hread, &hwrite, NULL, 0) == 0) + goto fail; + if (DuplicateHandle(current, flags & PARROT_EXEC_STDOUT ? hwrite : hread, + current, &hchild, + 0, TRUE, + DUPLICATE_CLOSE_SOURCE | DUPLICATE_SAME_ACCESS) + == 0) + goto fail; + if (hchild == INVALID_HANDLE_VALUE) + goto fail; + + if (flags & PARROT_EXEC_STDOUT) { + /* Redirect input to NULL. This is to avoid + * interferences in case both the child and + * the parent tries to read from stdin. + * May be unnecessary or even interfere + * with valid usages, need more feedback. */ + hnull = CreateFile("NUL", GENERIC_READ|GENERIC_WRITE, + 0, &sec, OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL, NULL); + if (hnull == INVALID_HANDLE_VALUE) + goto fail; + si.hStdInput = hnull; + si.hStdOutput = hchild; + si.hStdError = hchild; + } + else { + si.hStdInput = hchild; + } + + if (CreateProcess(NULL, cmd, NULL, NULL, TRUE, 0, env, NULL, &si, &pi) == 0) + goto fail; + + if (flags & PARROT_EXEC_STDOUT) { + os_handle = hread; + CloseHandle(hwrite); + } + else { + os_handle = hwrite; + CloseHandle(hread); + } + + Parrot_str_free_cstring(cmd); + CloseHandle(pi.hThread); + + *pid = (INTVAL)pi.hProcess; + + return os_handle; + + fail: + if (cmd != NULL) + Parrot_str_free_cstring(cmd); + if (hnull != INVALID_HANDLE_VALUE) + CloseHandle(hnull); + if (hread != INVALID_HANDLE_VALUE) + CloseHandle(hread); + if (hwrite != INVALID_HANDLE_VALUE) + CloseHandle(hwrite); + if (hchild != INVALID_HANDLE_VALUE) + CloseHandle(hchild); + if (pi.hThread != INVALID_HANDLE_VALUE) + CloseHandle(pi.hThread); + if (pi.hProcess != INVALID_HANDLE_VALUE) + CloseHandle(pi.hProcess); + Parrot_ex_throw_from_c_noargs(interp, EXCEPTION_UNIMPLEMENTED, + "pipe open error"); +} static INTVAL Run_OS_Command(PARROT_INTERP, PMC *command, PMC *env_hash) { DWORD status = 0; diff --git a/t/nqp/86-pipes.t b/t/nqp/86-pipes.t index bf697fed05..576da61842 100644 --- a/t/nqp/86-pipes.t +++ b/t/nqp/86-pipes.t @@ -19,10 +19,11 @@ plan(14); ok( nqp::defined($q) == 1, 'nqp::openpipe nonexistent cmd'); my $qstr := nqp::readallfh($q); - nqp::getcomp('nqp').backend.name eq 'moar' - ?? ok( $qstr eq '', 'nqp::readallfh with a pipe nonexistent command') - !! ok( $qstr ~~ / 'not found' | 'not recognized' /, 'nqp::readallfh with a pipe nonexistent command'); - + nqp::getcomp('nqp').backend.name eq 'moar' ?? + ok( $qstr eq '', 'nqp::readallfh with a pipe nonexistent command') !! + nqp::getcomp('nqp').backend.name eq 'parrot' ?? + ok( $qstr ~~ / 'doesnotexist' /, 'nqp::readallfh with a pipe nonexistent command') !! + ok( $qstr ~~ / 'not found' | 'not recognized' /, 'nqp::readallfh with a pipe nonexistent command'); nqp::closefh($q); ok( 1, 'nqp::closefh with a pipe nonexistent command'); } @@ -42,10 +43,11 @@ plan(14); ok( nqp::defined($q) == 1, 'nqp::openpipe nonexistent cmd'); my $qstr := nqp::readallfh($q); - nqp::getcomp('nqp').backend.name eq 'moar' - ?? ok( $qstr eq '', 'nqp::readallfh with a pipe nonexistent command') - !! ok( $qstr ~~ / 'not found' | 'not recognized' /, 'nqp::readallfh with a pipe nonexistent command'); - + nqp::getcomp('nqp').backend.name eq 'moar' ?? + ok( $qstr eq '', 'nqp::readallfh with a pipe nonexistent command') !! + nqp::getcomp('nqp').backend.name eq 'parrot' ?? + ok( $qstr ~~ / 'doesnotexist' /, 'nqp::readallfh with a pipe nonexistent command') !! + ok( $qstr ~~ / 'not found' | 'not recognized' /, 'nqp::readallfh with a pipe nonexistent command'); ok( nqp::closefh_i($q) != 0, 'nqp::closefh_i with a pipe nonexistent command'); } diff --git a/tools/build/PARROT_REVISION b/tools/build/PARROT_REVISION index 4613be0a5d..d77e04fc8c 100644 --- a/tools/build/PARROT_REVISION +++ b/tools/build/PARROT_REVISION @@ -1 +1 @@ -RELEASE_6_9_0-81-g6d8687c +RELEASE_6_9_0-123-g17e3545