Подтвердить что ты не робот

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

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

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

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

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

Это было протестировано на Debian 6 64bit с 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 exec принимает как ожидалось массив строковых args вместо уникальной строки. Но pcntl_fork просто останавливает запросы без даже журнала.

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

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

Любой намек оценивается.

4b9b3361

Ответ 1

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

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

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

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

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

$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 не будет выполнено).

config.m4 конфигурационный файл 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 script запустите эти команды, конечно, используйте свой собственный каталог модулей

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

Result

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, поэтому я думаю, что для этого есть более чистые способы. Но у вас есть идея.

Ответ 2

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

Ответ 3

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

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