Запуск исполняемого файла из php без появления оболочки

Мне нужно вызвать исполняемый файл из наложенного контекста скрипта PHP. Как производительность, так и безопасность мудрее, лучше не вызывать оболочку между процессом веб-сервера и исполняемым файлом.

Конечно, я искал в Интернете без успеха (в таком контексте PHP). Многие другие языки позволяют это и четко документировать.

Увы, backticks, exec() , shell_exec() , passthru() , system() , proc_open() , popen() вызывают оболочку. И pcntl_fork() кажется недоступным.

Как проверить, вызывает ли функция вызов оболочки или нет.

Это было протестировано на 64-битном Debian с PHP 5.3.3-7 + squeeze15. Тестовый код на http://pastebin.com/y4C7MeJz

Чтобы получить осмысленный тест, я использовал трюк, который должен попросить выполнить команду оболочки, также недоступную в качестве исполняемого файла. Хорошим примером является umask . Любая функция, возвращающая что-то вроде 0022, определенно называется оболочкой. exec() , shell_exec() , passthru() , system() , proc_open() все сделали. Подробные результаты см. На странице http://pastebin.com/RBcBz02F .

Ошибка pcntl_fork

Теперь вернемся к цели: как выполнить произвольную программу без запуска оболочки?

Исполнение php принимает как ожидалось массив строковых args вместо уникальной строки. Но pcntl_fork просто останавливает запросы без даже журнала.

Изменить: сбой pcntl_fork заключается в том, что сервер использует mod_php Apache, см. Http://www.php.net/manual/en/function.pcntl-fork.php#49949 .

Изменить: добавлено popen() к тестам, после предложения @hakre.

Любой намек оценили.

Чтобы ответить на ваше предложение:

Как производительность, так и безопасность мудрее, лучше не вызывать оболочку между процессом веб-сервера и исполняемым файлом.

О спектаклях, ну да, php внутренних вилок, и сама оболочка вилки тоже так немного тяжело. Но вам действительно нужно выполнить много процессов, чтобы рассмотреть эти проблемы с исполнением.

О безопасности я здесь не вижу. PHP имеет функцию escapeshellarg для дезинфекции аргументов.

Единственная реальная проблема, с которой я столкнулся с exec без pcntl, – это не проблема ресурса и безопасности: создавать настоящие деамоны (без привязанности к ее родителям, особенно Apache ) действительно сложно. Я решил это, используя, после двойной сбегающей моей команды:

 $arg1 = escapeshellarg($arg1); $arg2 = escapeshellarg($arg2); $command = escapeshellarg("/some/bin $arg1 $arg2 > /dev/null 2>&1 &"); exec("$command | at now -M"); 

Чтобы вернуться к вашему вопросу, единственный способ реализовать программы в стандартном (fork + exec) пути – использовать расширение PCNTL (как уже упоминалось). Во всяком случае, удачи!


Чтобы завершить мой ответ, вы можете самостоятельно создать функцию exec которая делает то же самое, что и pcntl_fork + pcntl_exec .

Я сделал расширение my_exec которое делает классическую конструкцию exec +, но на самом деле я не думаю, что это решит ваши проблемы, если вы используете эту функцию под apache , потому что будет применяться то же поведение, что и pcntl_fork (apache2 будет разветвлен и там может быть неожиданным поведением с execv сигнала и т. д., когда execv не удался).

config.m4 phpize конфигурации phpize

 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 

my_exec_extension.c расширение

 #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "php.h" #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <string.h> #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 &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 >= 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 >= 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) < 0) { return strdup("Could not pipe!"); } if ((pid = fork()) < 0) { return strdup("Could not fork!"); } if (pid == 0) { dup2(link[1], STDOUT_FILENO); close(link[0]); if (execv(command, argv) < 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))) > 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", &command, &command_len, &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, &pointer); zend_hash_get_current_data_ex(arr_hash, (void**) &data, &pointer) == SUCCESS; zend_hash_move_forward_ex(arr_hash, &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 < 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); } 

test.php пример использования

 <?php dl("my_exec.so"); $output = my_exec("/bin/ls", array("-l", "/")); var_dump($output); 

shell-скрипт запускает эти команды, конечно же, используя свой собственный каталог модулей

 phpize ./configure make sudo cp modules/my_exec_extension.so /opt/local/lib/php/extensions/no-debug-non-zts-20090626/my_exec.so 

результат

 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" 

Я не C dev, поэтому я думаю, что для этого есть более чистые способы. Но ты получил идею.

Id рассмотрим попытку pcntl_exec ()

Вы не можете запускать какую-либо произвольную программу без оболочки – оболочка является оберткой вокруг исполняющей программы, которая предоставляет свои настройки среды, такие как PWD и PATH.

Если вам нужна возможность запускать системные команды без запуска оболочки, перезапишите системные команды в виде модулей PHP и установите их на свой PHP-сервер и запустите их как функции.