Note that there are some explanatory texts on larger screens.

plurals
  1. PO
    text
    copied!<p>To answer your sentence :</p> <blockquote> <p>Both performance and security wise it's better not to call a shell at all between web server process and executable.</p> </blockquote> <p>About performances, well, yes, php internals forks, and the shell itself forks too so that's a bit heavy. But you really need to execute a lot of processes to consider those performances issues.</p> <p>About security, I do not see any issue here. PHP has the <a href="http://php.net/manual/en/function.escapeshellarg.php" rel="nofollow noreferrer">escapeshellarg</a> function to sanitize arguments.</p> <p>The only real problem I met with <code>exec</code> without pcntl is not a resource nor security issue : it is really difficult to create <em>real</em> deamons (without any attachment to its parent, particularily <em>Apache</em>). I solved this by using <code>at</code>, after double-escaping my command:</p> <pre><code>$arg1 = escapeshellarg($arg1); $arg2 = escapeshellarg($arg2); $command = escapeshellarg("/some/bin $arg1 $arg2 &gt; /dev/null 2&gt;&amp;1 &amp;"); exec("$command | at now -M"); </code></pre> <p>To get back to your question, the only way I know to execute programs in a <em>standard</em> (fork+exec) way is to use the <a href="http://php.net/manual/en/book.pcntl.php" rel="nofollow noreferrer">PCNTL</a> extension (as already mentionned). Anyway, good luck!</p> <hr> <p>To complete my answer, you can create an <code>exec</code> function yourself that does the same thing as <code>pcntl_fork</code>+<code>pcntl_exec</code>.</p> <p>I made a <code>my_exec</code> extension that does a classic exec+fork, but actually, <strong>I do not think it will solve your issues if you're running this function under apache</strong>, because the same behaviour as <code>pcntl_fork</code> will apply (apache2 will be forked and there may be unexpected behaviours with signal catching and so on when <code>execv</code> does not succeed).</p> <p><kbd>config.m4</kbd> the <code>phpize</code> configuration file</p> <pre><code>PHP_ARG_ENABLE(my_exec_extension, whether to enable my extension, [ --enable-my-extension Enable my extension]) if test "$PHP_MY_EXEC_EXTENSION" = "yes"; then AC_DEFINE(HAVE_MY_EXEC_EXTENSION, 1, [Whether you have my extension]) PHP_NEW_EXTENSION(my_exec_extension, my_exec_extension.c, $ext_shared) fi </code></pre> <p><kbd>my_exec_extension.c</kbd> the extension</p> <pre><code>#ifdef HAVE_CONFIG_H #include "config.h" #endif #include "php.h" #include &lt;stdio.h&gt; #include &lt;stdlib.h&gt; #include &lt;unistd.h&gt; #include &lt;string.h&gt; #define PHP_MY_EXEC_EXTENSION_VERSION "1.0" #define PHP_MY_EXEC_EXTENSION_EXTNAME "my_exec_extension" extern zend_module_entry my_exec_extension_module_entry; #define phpext_my_exec_extension_ptr &amp;my_exec_extension_module_entry // declaration of a custom my_exec() PHP_FUNCTION(my_exec); // list of custom PHP functions provided by this extension // set {NULL, NULL, NULL} as the last record to mark the end of list static function_entry my_functions[] = { PHP_FE(my_exec, NULL) {NULL, NULL, NULL} }; // the following code creates an entry for the module and registers it with Zend. zend_module_entry my_exec_extension_module_entry = { #if ZEND_MODULE_API_NO &gt;= 20010901 STANDARD_MODULE_HEADER, #endif PHP_MY_EXEC_EXTENSION_EXTNAME, my_functions, NULL, // name of the MINIT function or NULL if not applicable NULL, // name of the MSHUTDOWN function or NULL if not applicable NULL, // name of the RINIT function or NULL if not applicable NULL, // name of the RSHUTDOWN function or NULL if not applicable NULL, // name of the MINFO function or NULL if not applicable #if ZEND_MODULE_API_NO &gt;= 20010901 PHP_MY_EXEC_EXTENSION_VERSION, #endif STANDARD_MODULE_PROPERTIES }; ZEND_GET_MODULE(my_exec_extension) char *concat(char *old, char *buf, int buf_len) { int str_size = strlen(old) + buf_len; char *str = malloc((str_size + 1) * sizeof(char)); snprintf(str, str_size, "%s%s", old, buf); str[str_size] = '\0'; free(old); return str; } char *exec_and_return(char *command, char **argv) { int link[2], readlen; pid_t pid; char buffer[4096]; char *output; output = strdup(""); if (pipe(link) &lt; 0) { return strdup("Could not pipe!"); } if ((pid = fork()) &lt; 0) { return strdup("Could not fork!"); } if (pid == 0) { dup2(link[1], STDOUT_FILENO); close(link[0]); if (execv(command, argv) &lt; 0) { printf("Command not found or access denied: %s\n", command); exit(1); } } else { close(link[1]); while ((readlen = read(link[0], buffer, sizeof(buffer))) &gt; 0) { output = concat(output, buffer, readlen); } wait(NULL); } return output; } PHP_FUNCTION(my_exec) { char *command; int command_len, argc, i; zval *arguments, **data; HashTable *arr_hash; HashPosition pointer; char **argv; // recovers a string (s) and an array (a) from arguments if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sa", &amp;command, &amp;command_len, &amp;arguments) == FAILURE) { RETURN_NULL(); } arr_hash = Z_ARRVAL_P(arguments); // creating argc and argv from our argument array argc = zend_hash_num_elements(arr_hash); argv = malloc((argc + 1) * sizeof(char *)); argv[argc] = NULL; for ( i = 0, zend_hash_internal_pointer_reset_ex(arr_hash, &amp;pointer); zend_hash_get_current_data_ex(arr_hash, (void**) &amp;data, &amp;pointer) == SUCCESS; zend_hash_move_forward_ex(arr_hash, &amp;pointer) ) { if (Z_TYPE_PP(data) == IS_STRING) { argv[i] = malloc((Z_STRLEN_PP(data) + 1) * sizeof(char)); argv[i][Z_STRLEN_PP(data)] = '\0'; strncpy(argv[i], Z_STRVAL_PP(data), Z_STRLEN_PP(data)); i++; } } char *output = exec_and_return(command, argv); // freeing allocated memory for (i = 0; (i &lt; argc); i++) { free(argv[i]); } free(argv); // WARNING! I guess there is a memory leak here. // Second arguemnt to 1 means to PHP: do not free memory // But if I put 0, I get a segmentation fault // So I think I do not malloc correctly for a PHP extension. RETURN_STRING(output, 1); } </code></pre> <p><kbd>test.php</kbd> a usage sample</p> <pre><code>&lt;?php dl("my_exec.so"); $output = my_exec("/bin/ls", array("-l", "/")); var_dump($output); </code></pre> <p><kbd>shell script</kbd> run those commands, of course use your own module directory</p> <pre><code>phpize ./configure make sudo cp modules/my_exec_extension.so /opt/local/lib/php/extensions/no-debug-non-zts-20090626/my_exec.so </code></pre> <p><kbd>Result</kbd></p> <pre><code>KolyMac:my_fork ninsuo$ php test.php string(329) ".DS_Store .Spotlight-V100 .Trashes .file .fseventsd .hidden .hotfiles.btree .vol AppleScript Applications Developer Installer Log File Library Microsoft Excel Documents Microsoft Word Documents Network System Users Volumes bin cores dev etc home lost+found mach_kernel net opt private sbin tmp usr var vc_command.txt vidotask.txt" </code></pre> <p>I am not a C dev, so I think there are cleaner ways to achieve this. But you get the idea.</p>
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload