我有一个用C编写的复杂cgi可执行文件,我在Apache2中配置,现在它已成功运行。如何在源代码中调试此程序,例如设置断点和检查变量?像gdb或eclipse这样的工具?有关如何设置调试环境的任何教程吗?
提前致谢!!
答案 0 :(得分:7)
CGI接口基本上包括将HTTP请求传递给可执行文件的标准输入并在标准输出上获取响应。因此,您可以将测试请求写入文件并手动执行CGI,而无需使用Apache。然后可以使用GDB完成调试:
gdb ./my_cgi
>> break some_func
>> run < my_req.txt
my_req.txt包含完整请求:
GET /some/func HTTP/1.0
Host: myhost
如果您绝对需要由Apache运行CGI,那么将GDB附加到正确的进程可能会变得棘手。例如,您可以将Apache配置为只有一个工作进程,使用gdb -p
附加到它,并使用set follow-fork-mode child
确保它在请求到达时切换到CGI进程。
答案 1 :(得分:2)
我这样做了:在cgi main中我添加了代码来查找现有文件,比如/ var / tmp / flag。虽然存在,但我在循环中运行。足够的时间通过gdb附加到cgi进程。之后我删除/ var / tmp / flag,从现在开始我可以调试我的cgi代码。
bool file_exists(const char *filename)
{
ifstream ifile(filename);
return ifile;
}
int cgiMain()
{
while (file_exists ("/var/tmp/flag"))
sleep (1);
...
your code
答案 2 :(得分:1)
除非使用FastCGI或SCGI,否则CGI进程是短暂的,您需要延迟其退出,以便在进程仍在运行时有足够的时间连接调试器。对于随意调试,最简单的选择是在断点位置的无限循环中简单地使用sleep()
,并在将调试器附加到程序后退出循环。
这是一个小例子CGI程序:
#include <stdio.h>
#include <unistd.h>
void wait_for_gdb_to_attach() {
int is_waiting = 1;
while (is_waiting) {
sleep(1); // sleep for 1 second
}
}
int main(void) {
wait_for_gdb_to_attach();
printf("Content-Type: text/plain;charset=us-ascii\n\n");
printf("Hello!");
return 0;
}
假设它被编译为cgi-debugging-example
,这是应用程序进入无限循环后附加调试器的方式:
sudo cgdb cgi-debugging-example $(pgrep cgi-debugging)
接下来,您需要退出无限循环和wait_for_gdb_to_attach()
函数以到达&#34;断点&#34;在你的申请中。这里的技巧是逐步退出睡眠功能,直到达到wait_for_gdb_to_attach()
并使用调试器设置变量is_waiting
的值,以便while (is_waiting)
退出:
(gdb) finish
Run till exit from 0x8a0920 __nanosleep_nocancel () at syscall-template.S:81
0x8a07d4 in __sleep (seconds=0) at sleep.c:137
(gdb) finish
Run till exit from 0x8a07d4 in __sleep (seconds=0) at sleep.c:137
wait_for_gdb_to_attach () at cgi-debugging-example.c:6
Value returned is $1 = 0
(gdb) set is_waiting = 0 # <<<<<< to exit while
(gdb) finish
Run till exit from wait_for_gdb_to_attach () cgi-debugging-example.c:6
main () at cgi-debugging-example.c:13
离开wait_for_gdb_to_attach()
后,您可以继续调试程序或让它运行完成。
详细说明的完整示例here。
答案 3 :(得分:0)
我不确定如何在eclipse中使用gdb或其他前端,但是我只是用gdb调试了CGI程序。我想分享一些其他答案未提及的内容,即CGI通常需要使用RFC 3875#4.1读取getenv(3)中定义的请求元变量。我认为流行的请求变量是:
SCRIPT_NAME
QUERY_STRING
CONTENT_LENGTH
CONTENT_TYPE
REMOTE_ADDR
http服务器(例如Apache)提供了变量。使用gdb进行调试时,我们需要使用set environment
自行设置这些值。就我而言,只需要几个变量a(并且源代码很旧,它仍然使用SCRIPT_URL
而不是SCRIPT_NAME
),所以这是我的示例:
gdb cgi_name
set environment SCRIPT_URL /path/to/sub/cgis
set environment QUERY_STRING p1=v1&p2=v2
break foo.c:42
run
答案 4 :(得分:0)
对我来说,上面介绍的在没有 Web 服务器的情况下在 gdb 中调试 CGI 的两种解决方案都不起作用。
也许第二种解决方案适用于 GET 请求。
我需要两者的结合,首先从 rfc3875 设置环境变量(不确定是否真的需要所有这些)。 然后我只能通过文件中的 STDIN 传递参数(而不是完整的请求)。
gdb cgi_name
set environment REQUEST_METHOD=POST
set environment CONTENT_LENGTH=1337
set environment CONTENT_TYPE=application/json
set environment SCRIPT_NAME=my.cgi
set environment REMOTE_ADDR=127.0.0.1
run < ./params.txt
使用 params.txt:
{"user":"admin","pass":"admin"}