mirror of
https://github.com/php/php-src.git
synced 2024-09-21 09:57:23 +00:00
Support redirect+null descriptors in proc_open
This adds support for doing something like: [1 => ['pipe', 'w'], 2 => ['redirect', 1]] This will make descriptor 2 on the child end a dup'd descriptor 1. This is mainly useful in conjunction with shell-less mode, because we don't have an easy way to do "2>&1" there. Additionally we support: [1 => ['pipe', 'w'], 2 => ['null']] Which would be the same as a >/dev/null or >nul redirect, depending on platform.
This commit is contained in:
parent
42cac9d7d7
commit
6285bb52fa
@ -304,6 +304,13 @@ PHP 7.4 UPGRADE NOTES
|
||||
|
||||
proc_open(['php', '-r', 'echo "Hello World\n";'], $descriptors, $pipes);
|
||||
|
||||
. proc_open() now supports "redirect" and "null" descriptors. For example:
|
||||
|
||||
// Like 2>&1 on the shell
|
||||
proc_open($cmd, [1 => ['pipe', 'w'], 2 => ['redirect', 1]], $pipes);
|
||||
// Like 2>/dev/null or 2>nul on the shell
|
||||
proc_open($cmd, [1 => ['pipe', 'w'], 2 => ['null']], $pipes);
|
||||
|
||||
. password_hash() has argon2i(d) implementations from ext/sodium when PHP is
|
||||
built without libargon.
|
||||
|
||||
|
@ -390,6 +390,7 @@ static inline HANDLE dup_fd_as_handle(int fd)
|
||||
|
||||
#define DESC_PIPE 1
|
||||
#define DESC_FILE 2
|
||||
#define DESC_REDIRECT 3
|
||||
#define DESC_PARENT_MODE_WRITE 8
|
||||
|
||||
struct php_proc_open_descriptor_item {
|
||||
@ -760,6 +761,85 @@ PHP_FUNCTION(proc_open)
|
||||
#else
|
||||
descriptors[ndesc].childend = fd;
|
||||
#endif
|
||||
} else if (strcmp(Z_STRVAL_P(ztype), "redirect") == 0) {
|
||||
zval *ztarget = zend_hash_index_find_deref(Z_ARRVAL_P(descitem), 1);
|
||||
struct php_proc_open_descriptor_item *target = NULL;
|
||||
php_file_descriptor_t childend;
|
||||
|
||||
if (!ztarget) {
|
||||
php_error_docref(NULL, E_WARNING, "Missing redirection target");
|
||||
goto exit_fail;
|
||||
}
|
||||
if (Z_TYPE_P(ztarget) != IS_LONG) {
|
||||
php_error_docref(NULL, E_WARNING, "Redirection target must be an integer");
|
||||
goto exit_fail;
|
||||
}
|
||||
|
||||
for (i = 0; i < ndesc; i++) {
|
||||
if (descriptors[i].index == Z_LVAL_P(ztarget)) {
|
||||
target = &descriptors[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (target) {
|
||||
childend = target->childend;
|
||||
} else {
|
||||
if (Z_LVAL_P(ztarget) < 0 || Z_LVAL_P(ztarget) > 2) {
|
||||
php_error_docref(NULL, E_WARNING,
|
||||
"Redirection target " ZEND_LONG_FMT " not found", Z_LVAL_P(ztarget));
|
||||
goto exit_fail;
|
||||
}
|
||||
|
||||
/* Support referring to a stdin/stdout/stderr pipe adopted from the parent,
|
||||
* which happens whenever an explicit override is not provided. */
|
||||
#ifndef PHP_WIN32
|
||||
childend = Z_LVAL_P(ztarget);
|
||||
#else
|
||||
switch (Z_LVAL_P(ztarget)) {
|
||||
case 0: childend = GetStdHandle(STD_INPUT_HANDLE); break;
|
||||
case 1: childend = GetStdHandle(STD_OUTPUT_HANDLE); break;
|
||||
case 2: childend = GetStdHandle(STD_ERROR_HANDLE); break;
|
||||
EMPTY_SWITCH_DEFAULT_CASE()
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef PHP_WIN32
|
||||
descriptors[ndesc].childend = dup_handle(childend, TRUE, FALSE);
|
||||
if (descriptors[ndesc].childend == NULL) {
|
||||
php_error_docref(NULL, E_WARNING,
|
||||
"Failed to dup() for descriptor " ZEND_LONG_FMT, nindex);
|
||||
goto exit_fail;
|
||||
}
|
||||
#else
|
||||
descriptors[ndesc].childend = dup(childend);
|
||||
if (descriptors[ndesc].childend < 0) {
|
||||
php_error_docref(NULL, E_WARNING,
|
||||
"Failed to dup() for descriptor " ZEND_LONG_FMT " - %s",
|
||||
nindex, strerror(errno));
|
||||
goto exit_fail;
|
||||
}
|
||||
#endif
|
||||
descriptors[ndesc].mode = DESC_REDIRECT;
|
||||
} else if (strcmp(Z_STRVAL_P(ztype), "null") == 0) {
|
||||
#ifndef PHP_WIN32
|
||||
descriptors[ndesc].childend = open("/dev/null", O_RDWR);
|
||||
if (descriptors[ndesc].childend < 0) {
|
||||
php_error_docref(NULL, E_WARNING,
|
||||
"Failed to open /dev/null - %s", strerror(errno));
|
||||
goto exit_fail;
|
||||
}
|
||||
#else
|
||||
descriptors[ndesc].childend = CreateFileA(
|
||||
"nul", GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE,
|
||||
NULL, OPEN_EXISTING, 0, NULL);
|
||||
descriptors[ndesc].childend = VCWD_OPEN("nul", O_RDWR);
|
||||
if (descriptors[ndesc].childend == NULL) {
|
||||
php_error_docref(NULL, E_WARNING, "Failed to open nul");
|
||||
goto exit_fail;
|
||||
}
|
||||
#endif
|
||||
descriptors[ndesc].mode = DESC_FILE;
|
||||
} else if (strcmp(Z_STRVAL_P(ztype), "pty") == 0) {
|
||||
#if PHP_CAN_DO_PTS
|
||||
if (dev_ptmx == -1) {
|
||||
|
30
ext/standard/tests/general_functions/proc_open_null.phpt
Normal file
30
ext/standard/tests/general_functions/proc_open_null.phpt
Normal file
@ -0,0 +1,30 @@
|
||||
--TEST--
|
||||
Null pipes in proc_open()
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
$php = getenv('TEST_PHP_EXECUTABLE');
|
||||
$cmd = [$php, '-r', 'echo "Test"; fprintf(STDERR, "Error");'];
|
||||
|
||||
$proc = proc_open($cmd, [1 => ['null'], 2 => ['pipe', 'w']], $pipes);
|
||||
var_dump($pipes);
|
||||
var_dump(stream_get_contents($pipes[2]));
|
||||
proc_close($proc);
|
||||
|
||||
$proc = proc_open($cmd, [1 => ['pipe', 'w'], 2 => ['null']], $pipes);
|
||||
var_dump($pipes);
|
||||
var_dump(stream_get_contents($pipes[1]));
|
||||
proc_close($proc);
|
||||
|
||||
?>
|
||||
--EXPECT--
|
||||
array(1) {
|
||||
[2]=>
|
||||
resource(4) of type (stream)
|
||||
}
|
||||
string(5) "Error"
|
||||
array(1) {
|
||||
[1]=>
|
||||
resource(6) of type (stream)
|
||||
}
|
||||
string(4) "Test"
|
72
ext/standard/tests/general_functions/proc_open_redirect.phpt
Normal file
72
ext/standard/tests/general_functions/proc_open_redirect.phpt
Normal file
@ -0,0 +1,72 @@
|
||||
--TEST--
|
||||
Redirection support in proc_open
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
$php = getenv('TEST_PHP_EXECUTABLE');
|
||||
var_dump(proc_open([$php], [['redirect']], $pipes));
|
||||
var_dump(proc_open([$php], [['redirect', 'foo']], $pipes));
|
||||
var_dump(proc_open([$php], [['redirect', 42]], $pipes));
|
||||
|
||||
echo "\nWith pipe:\n";
|
||||
$cmd = [$php, '-r', 'echo "Test\n"; fprintf(STDERR, "Error");'];
|
||||
$proc = proc_open($cmd, [1 => ['pipe', 'w'], 2 => ['redirect', 1]], $pipes);
|
||||
var_dump($pipes);
|
||||
var_dump(stream_get_contents($pipes[1]));
|
||||
proc_close($proc);
|
||||
|
||||
echo "\nWith filename:\n";
|
||||
$fileName = __DIR__ . '/proc_open_redirect.txt';
|
||||
$proc = proc_open($cmd, [1 => ['file', $fileName, 'w'], 2 => ['redirect', 1]], $pipes);
|
||||
var_dump($pipes);
|
||||
proc_close($proc);
|
||||
var_dump(file_get_contents($fileName));
|
||||
unlink($fileName);
|
||||
|
||||
echo "\nWith file:\n";
|
||||
$file = fopen($fileName, 'w');
|
||||
$proc = proc_open($cmd, [1 => $file, 2 => ['redirect', 1]], $pipes);
|
||||
var_dump($pipes);
|
||||
proc_close($proc);
|
||||
fclose($file);
|
||||
var_dump(file_get_contents($fileName));
|
||||
unlink($fileName);
|
||||
|
||||
echo "\nWith inherited stdout:\n";
|
||||
$proc = proc_open($cmd, [2 => ['redirect', 1]], $pipes);
|
||||
proc_close($proc);
|
||||
|
||||
?>
|
||||
--EXPECTF--
|
||||
Warning: proc_open(): Missing redirection target in %s on line %d
|
||||
bool(false)
|
||||
|
||||
Warning: proc_open(): Redirection target must be an integer in %s on line %d
|
||||
bool(false)
|
||||
|
||||
Warning: proc_open(): Redirection target 42 not found in %s on line %d
|
||||
bool(false)
|
||||
|
||||
With pipe:
|
||||
array(1) {
|
||||
[1]=>
|
||||
resource(4) of type (stream)
|
||||
}
|
||||
string(10) "Test
|
||||
Error"
|
||||
|
||||
With filename:
|
||||
array(0) {
|
||||
}
|
||||
string(10) "Test
|
||||
Error"
|
||||
|
||||
With file:
|
||||
array(0) {
|
||||
}
|
||||
string(10) "Test
|
||||
Error"
|
||||
|
||||
With inherited stdout:
|
||||
Test
|
||||
Error
|
Loading…
Reference in New Issue
Block a user