【问题标题】:How to run a linux command to compile a c program, from php script如何从 php 脚本运行 linux 命令来编译 c 程序
【发布时间】:2014-01-15 11:34:06
【问题描述】:

我正在尝试为 c 编程做一个在线评委。当用户输入 c 代码并提交时,我的表单会重定向到judge.php,这是表单的操作文件。 这是我在judge.php中写的

<?php 
$text=$_POST['code'];
//echo $text;

$var_str = var_export($text, true);

file_put_contents('code.c', $text);

$ans=exec('pwd');

$ans= exec('gcc code.c');

echo $ans;

?>

我已在 $text 中捕获用户输入并将其写入 c 文件(code.c)。到现在为止,还好。 但是exec(gcc code.c) 不工作并且没有给出任何输出。我尝试了其他 linux commnads,如 pwd、date 等。它们工作正常。这可能是什么原因以及如何解决? 这不是我尝试exec(pwd) 的目录问题,它给出的输出与代码所在的目录相同。 我试图从终端运行相同的 code.c 文件,它运行良好。所以,这也不是“权限”问题。

还有一件事,如果任何 exec() 命令无法正常工作,如何回显生成的错误消息?

从下面的答案中得到建议后,我尝试了 $cmd="gcc -std=c99 code.c -g -Wall mysql_config --libs --cflags -o db_obj.o --pedantic";

exec($cmd,$out,$status);

但它也不起作用。返回的状态是 1

很可能是权限问题。 “whoami”没有人说。请告诉如何将所有者从nobody更改为root或如何分配从nobody执行gcc的权限

【问题讨论】:

  • 就我个人而言,我永远不会让任何人直接从网络上编译,尤其是不使用 exec() 函数如此危险。等等。我会对代码进行批处理/排队,并编译一个 cron 作业,然后在完成后通过电子邮件向他们发送已编译应用程序的下载链接。更好的方法是使用基于 git/svn 或类似用户的适当构建环境将他们的代码放入 repos 并构建它并将其返回到 build 文件夹中的 repos。

标签: php linux


【解决方案1】:

我的回答的三个主要方面

exec函数使用不当。

看看the man pages。首先,exec 函数的签名是:

string exec ( string $command [, array &$output [, int &$return_var ]] )

所以exec 最多可以接受 3 个参数。它返回命令输出的最后一行,就像文档中的状态一样清楚:

命令结果的最后一行。如果您需要执行命令并将命令中的所有数据直接传回而不受任何干扰,请使用 passthru() 函数。

要获取执行命令的输出,请务必设置并使用输出参数。

所以在你的情况下:

$lastLine = exec($command, $fullOutput, $status);

是您正在寻找的。如果$status0 以外的任何else,则您的命令不成功。这就是您应该检查以做出相应反应的内容。
任何命令的完整输出都可以在 $fullOutput 中找到,以每行数组的形式显示。
输出如下:

all went well
except for this

$fullOutput 数组中看起来像这样:

array('all went well', 'except for this');

权限可能仍然是个问题。

您说权限不太可能是问题的原因,因为您可以从命令行运行gcc。一切都很好,但哪个用户在服务器上运行 PHP 脚本?
在网络服务器的情况下,该用户通常被称为 nobodyapache 或其他名称,而 那个 用户很可能 不是 允许运行gcc。是 PHP 运行它设置的任何默认 shell(可能是 bash)的新实例,并且是 PHP 的用户登录到该 shell,并且是那个用户正在调用 gcc...

知道你是谁,你属于什么群体。尝试将此添加到您的脚本中:

echo 'Script is running under user: ', exec('whoami'), '<br>', PHP_EOL;
echo 'member of the following groups: ', exec('groups'), '<br>', PHP_EOL;

在你问之前:是的,那些是逗号的......不需要连接,你可以将多个变量/值传递给echo,用逗号分隔。它实际上更快(将其视为 C++ 的 std::cout &lt;&lt; some_var &lt;&lt; another_var;

一般问题 + 安全

这一切都说了又做了:从 php 脚本编译 C 代码并不像您想象的那么简单。假设我要这样写:

#include <stdio.h>
#include <time.h>
int main ( void )
{
    time_t t = time(NULL);
    if (t%2)
    {
        float val = (float) t/2.0;
        //do stuff with float...
    }
    else
    {
        unsigned long long val = t/2;
        //do stuff with unsigned long long...
    }
}

您的gcc test.c 命令将失败,例如,因为您未能传递参数-std=c99
如果我想要一个脚本来编译给定的文件,我还希望该脚本允许我选择编译我的代码的参数-g-Wall,更不用说:cflags 和 libs( pkg-configmysql_config --cflags --libs 的输出,举一个我最近使用的具体示例)。
基本上,你的脚本根本无法处理我想用类似的命令编译一些东西

gcc -std=c99 code.c -g -Wall `mysql_config --libs --cflags` -o db_obj.o --pedantic

这仍然是许多编译命令的简化版本,尤其是在调试正在开发的代码时。对于稳定版本,您可能会放弃 -g--pedantic,但您明白我的意思...
想想这意味着什么,允许用户传递一组 cli 参数以及代码。他们可能会传递-DSOME_MACRO-O0 之类的参数,这意味着他们也可能会传递-O0 &amp;&amp; rm -Rf *。这意味着您必须致电escapeshellcmdescapeshellarg。两者都将禁止我传递有效的论点,即:

`mysql_config --libs --cflags`

其中包含反引号,因此将被转义。

坦率地说,我很难理解这个练习的意义......而且我仍然遗漏了很多内容:编译(更不用说运行)用户提供的代码的危险例如,在您的机器上,不容忽视。你不能只编译代码,然后在你的服务器上运行它:内存泄漏、段错误……哎呀,纯 evil 代码都在你的服务器上编译 unchecked 如果这是您拥有的代码。真的,为自己省去很多眼泪,并包含一个加载键盘或类似服务的 iframe...


回顾:

  • 总是检查 man 的函数,看看你是否得到了它返回的所有信息
  • 检查实际执行命令的用户的权限和运行时
  • 永远不要相信网络,不要盲目地假设人们会提交有效、无害的代码供您编译。
  • 不要重新发明轮子:编译服务是存在的,只需转发那些(但要先征求许可)

【讨论】:

  • 没问题。我了解其中的风险。但请详细说明我错过的论点。具体来说,什么改变会让我的程序编译?
  • @SuperCoder:在我提出的 C sn-p 的情况下,编译它的命令应该看起来像这样 gcc code.c -std=c99,但我更喜欢使用 gcc code.c -std=c99 -g -Wall 编译它调试,并且在我完成调试后没有-g 标志。如果我使用#include &lt;mysql/mysq.h&gt;,我的命令可能看起来更像gcc code.c -std=c99 mysql_config --cflags --libs -g -Wall -o db_obj.o,其中mysql_config --cflags --libs 也应该被反引号包围
  • 我添加了一个我可能真正想要使用的编译命令示例。请注意,它甚至不是非常复杂:我只是在编译一个包含 mysql C api 的源文件,并且我正在将它编译为可以链接到其他文件的对象
  • 我尝试编译您的 c 代码以及您建议的编译代码,但它仍然无法正常工作。返回的状态为 1。
  • @SuperCoder:我没有测试代码,它缺少return 0 语句...但是在尝试执行gcc 之前,找出运行PHP 的用户,以及他所属的组(其命令分别为whoamigroups),然后检查是否允许用户运行gcc。完成并相应设置权限后,再试一次
【解决方案2】:

试试这段代码从 PHP 文件执行 c 程序

<?php 
    // used to compile the c file using exec() in php
    exec('gcc helloworld.c -o helloworld', $out, $status); 
    if (0 === $status) {
        var_dump($out);
        // used to execute the c file using exec() in php
        exec('./helloworld', $out, $status);
        if (0 === $status) {
            var_dump($out);
        } else {
            echo "Command failed with status: $status";
        }
    } else {
        echo "Command failed with status: $status";
    }
?>

【讨论】:

    猜你喜欢
    • 2012-10-01
    • 1970-01-01
    • 1970-01-01
    • 2015-11-11
    • 2023-03-06
    • 2016-02-20
    • 2019-07-04
    • 2013-12-28
    • 2012-05-03
    相关资源
    最近更新 更多