我需要从一个PHP脚本的规定范围内调用的可执行文件。 兼顾性能和安全明智的,最好不要打电话给在所有Web服务器进程和可执行文件之间的外壳。
当然,我在网上搜索,但没有成功(在这样的背景下PHP)。 许多其他语言允许,它清楚地记录。
可惜的是,反引号, exec()
shell_exec()
passthru()
system()
proc_open()
popen()
调用的壳。 和pcntl_fork()
似乎是不可用的。
如何测试如果一个函数调用壳与否。
这对用PHP 5.3.3-7 + squeeze15一个Debian 6 64位测试。 在测试代码http://pastebin.com/y4C7MeJz
为了得到一个有意义的测试我使用的一招是要求执行shell命令不是也可以作为一个可执行文件。 一个很好的例子是umask
。 任何函数返回像0022绝对称得上一个壳。 exec()
shell_exec()
passthru()
system()
proc_open()
都做到了。 查看详细的结果http://pastebin.com/RBcBz02F 。
pcntl_fork失败
现在,回到了目标:如何无需启动一个shell来执行任意程序?
PHP的高管需要按预期字符串ARGS,而不是唯一的字符串数组。 但pcntl_fork只是停止的要求,甚至没有日志。
编辑:pcntl_fork失败是因为服务器使用Apache的mod_php,并且看到http://www.php.net/manual/en/function.pcntl-fork.php#49949 。
编辑:添加popen()
的测试,以下@hakre建议。
任何提示赞赏。
要回答你的句子:
兼顾性能和安全明智的,最好不要打电话给在所有Web服务器进程和可执行文件之间的外壳。
关于演出,好了,是的,PHP内部叉,与外壳本身叉太多所以这是一个有点沉重。 但你真的需要执行大量的过程来考虑这些问题的演出。
关于安全性,我看不出有什么问题在这里。 PHP有escapeshellarg功能消毒参数。
我遇到的唯一真正的问题exec
没有了pcntl不是资源,也不安全问题:这真的很难创造真正的守护程序(无需添加任何附件到其父,particularily 阿帕奇 )。 我解决了这个用at
后双逃避我的命令:
$arg1 = escapeshellarg($arg1);
$arg2 = escapeshellarg($arg2);
$command = escapeshellarg("/some/bin $arg1 $arg2 > /dev/null 2>&1 &");
exec("$command | at now -M");
要回到你的问题,我知道,在标准 (叉+可执行程序)的方式是使用执行程序的唯一途径PCNTL扩展(如已mentionned)。 无论如何,祝你好运!
为了完成我的回答,您可以创建一个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脚本来运行这些命令,当然使用自己的模块目录
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开发,所以我觉得有简洁的方式来实现这一目标。 但你的想法。
无壳不能启动任意程序 - 外壳是围绕执行程序,它提供了环境设置,如PWD和路径包装。
如果你想,而无需启动一个shell来运行系统的能力命令,重新写为PHP模块的系统命令,并将其安装到您的PHP服务器,并运行它们的功能。