Skip to content

Commit

Permalink
Merge branch 'master' into longlit
Browse files Browse the repository at this point in the history
  • Loading branch information
TimToady committed Nov 17, 2014
2 parents 4f1b3f0 + fd8e5ed commit 68dfa22
Show file tree
Hide file tree
Showing 6 changed files with 323 additions and 45 deletions.
1 change: 1 addition & 0 deletions src/vm/parrot/QAST/Operations.nqp
Expand Up @@ -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 {
Expand Down
268 changes: 268 additions & 0 deletions src/vm/parrot/ops/nqp.ops
Expand Up @@ -6,6 +6,8 @@ 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. */
#include "../6model/sixmodelobject.h"
Expand Down Expand Up @@ -563,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;
Expand Down Expand Up @@ -667,6 +760,129 @@ 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, INTVAL *status) {
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];

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) {
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 */

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);
}

if (flags & PARROT_EXEC_STDOUT) {
/* redirect stdout to pipe */
close(out_fds[0]);
close(STDOUT_FILENO);

if (dup(out_fds[1]) != STDOUT_FILENO)
PARROT_FORCE_EXIT(EXIT_FAILURE);

if (!(flags & PARROT_EXEC_STDERR)) {
close(STDERR_FILENO);

if (dup(out_fds[1]) != STDERR_FILENO)
PARROT_FORCE_EXIT(EXIT_FAILURE);
}
}

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);
}

environ = env;

*status = execvp(prog, cmd);

/* 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

/*
Expand Down Expand Up @@ -3787,6 +4003,58 @@ 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 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;
INTVAL pid;
#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);

Parrot_file_chdir(interp, dir);
os_handle = Run_OS_Command_Piped(interp, command, env, PARROT_EXEC_STDOUT, &pid, &status);
Parrot_file_chdir(interp, old_cwd);

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_PIPE | PIO_F_READ);
SETATTR_FileHandle_mode(interp, handle, Parrot_str_new_constant(interp, "rp"));
SETATTR_FileHandle_file_pos(interp, handle, 0);

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;

SETATTR_FileHandle_read_buffer(interp, handle, read_buffer);
}

$1 = handle;
}

inline op nqp_spawn(out INT, in PMC, in STR, in PMC) {
STRING *dir = $3;
PMC *env = $4;
Expand Down
22 changes: 0 additions & 22 deletions t/jvm/02-pipes.t

This file was deleted.

22 changes: 0 additions & 22 deletions t/moar/02-pipes.t

This file was deleted.

0 comments on commit 68dfa22

Please sign in to comment.