|
php.net | support | documentation | report a bug | advanced search | search howto | statistics | random bug | login |
[2007-04-05 12:09 UTC] andy dot shellam at mailnetwork dot co dot uk
Description:
------------
proc_get_status when run with a proc_open'd resource returns a PID that is one less than shown in ps output.
E.g. to kill a process opened by proc_open, you need to add 1 to the PID returned by proc_get_status for it to work.
This is because proc_get_status returns the PID of the shell that then runs the command - not the command process itself.
Reproduce code:
---------------
Create a bash script that loops endlessly to simulate a long-running:
#!/usr/local/bin/bash
while [ 0 -eq 0 ]; do
let 0
done
In PHP, run this script (from CLI):
#!/usr/local/php/bin/php
<?php
$descriptorspec = array(
0 => array("pipe", "r"), // stdin is a pipe that the child will read from
1 => array("pipe", "w"), // stdout is a pipe that the child will write to
2 => array("file", "/tmp/error-output.txt", "a") // stderr is a file to write to
);
$cwd = '/tmp';
$env = array('some_option' => 'aeiou');
$process = proc_open('/tmp/loop.sh', $descriptorspec, $pipes, $cwd, $env, Array("bypass_shell" => TRUE));
if (is_resource($process)) {
$procinfo = proc_get_status($process);
echo "Opened process ID is: " . $procinfo['pid'] . "\n";
}
?>
Expected result:
----------------
Opened process ID is: 40609
(from ps output):
[NetServe@sydney ~]$ sudo ps auxwww|grep loop.sh
root 40609 48.2 0.2 3036 1672 p0 R+ 1:06PM 0:05.94 /usr/local/bin/bash /tmp/loop.sh
root 40608 0.0 0.1 1632 988 p0 S+ 1:06PM 0:00.00 sh -c /tmp/loop.sh
Actual result:
--------------
Opened process ID is: 40608
[NetServe@sydney ~]$ sudo ps auxwww|grep loop.sh
root 40609 48.2 0.2 3036 1672 p0 R+ 1:06PM 0:05.94 /usr/local/bin/bash /tmp/loop.sh
root 40608 0.0 0.1 1632 988 p0 S+ 1:06PM 0:00.00 sh -c /tmp/loop.sh
As you can see, the PID returned by proc_get_info is the shell that is then used to start the actual command - if you kill the shell's PID, the command carries on running. Kill the shell PID+1, and it kills both off.
This is similar to http://bugs.php.net/bug.php?id=40070, however the bypass_shell does not work on Unix.
PatchesPull RequestsHistoryAllCommentsChangesGit/SVN commits
|
|||||||||||||||||||||||||||||||||||||
Copyright © 2001-2025 The PHP GroupAll rights reserved. |
Last updated: Mon Oct 27 05:00:01 2025 UTC |
I have w/changes for FreeBSD. W/pacth you can use "bypass_shell" to start binary executable file w/o /bin/sh process. To compile w/patch, modification in Makefile are needed (add "-lpopt" to EXTRA_LIBS parameter line). -----proc_open.patch start---- {{{ 19c19,21 < --- > #ifndef PHP_WIN32 > #include <popt.h> > #endif 469a472,474 > char *param_str = NULL; > char *param_dup = NULL; > char *const *av = NULL; 501d505 < int bypass_shell = 0; 502a507 > int bypass_shell = 0; 517a523,524 > command_len = strlen(command); > 533a541,552 > #else > if (other_options) { > zval **item; > if (SUCCESS == zend_hash_find(Z_ARRVAL_P(other_options), "bypass_shell", sizeof("bypass_shell"), (void**)&item)) { > if ((Z_TYPE_PP(item) == IS_BOOL || Z_TYPE_PP(item) == IS_LONG) && > Z_LVAL_PP(item)) { > param_dup = pemalloc(command_len+1, is_persistent); > memcpy(param_dup, command, command_len+1); > bypass_shell = 1; > } > } > } 536,537d554 < command_len = strlen(command); < 881c898,915 < execle("/bin/sh", "sh", "-c", command, NULL, env.envarray); --- > if (bypass_shell) { > int rc; > if ((param_str = strchr(command, ' '))) { > rc = poptParseArgvString(command, NULL, (const char ***)&av); > *param_str = '\0'; > if (!rc && av != NULL){ > execve(command, (char * const *) av, env.envarray); > free((void*)av); > } else { > param_str++; > execle(command, command, param_str, NULL, env.envarray); > } > } else { > execlp(command, NULL); > } > } else { > execle("/bin/sh", "sh", "-c", command, NULL, env.envarray); > } 883c917,934 < execl("/bin/sh", "sh", "-c", command, NULL); --- > if (bypass_shell) { > int rc; > if ((param_str = strchr(command, ' '))) { > rc = poptParseArgvString(command, NULL, (const char ***)&av); > *param_str = '\0'; > if (!rc && av != NULL){ > execvp(command, (char * const *) av); > free((void*)av); > } else { > param_str++; > execlp(command, command, param_str, NULL); > } > } else { > execlp(command, NULL); > } > } else { > execl("/bin/sh", "sh", "-c", command, NULL); > } 909c960,965 < proc->command = command; --- > if (bypass_shell) { > pefree(command, is_persistent); > proc->command = param_dup; > } else { > proc->command = command; > } 991a1048 > pefree(param_dup, is_persistent); }}} -----proc_open.patch end------Note that a very simple user-level workaround on Unix systems is to call proc_open("exec /usr/bin/myprogram") instead of proc_open("/usr/bin/myprogram"), in which case the shell is replaced by the program being executed and proc_get_status() thus returns the program's PID instead of the shell's. Similarly, the very simplest (though not most efficient) way to have the "bypass_shell" option work on Unix as well would be to simply prepend "exec " to the command string automatically in the implementation of proc_open(). It seems to me that proc_get_status() works correctly, and that indeed this bug report is really about "bypass_shell" not being honored on Unix, so perhaps the bug report could be retitled accordingly.