先感谢您!
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
如果你一定要让CGI由Apache运行,那么将会有点棘手去附加GDB到正确的进程上。例如,您可以将Apache配置为仅具有一个工作进程,使用gdb -p
连接到它,并使用set follow-fork-mode child
确保它在请求到达时切换到CGI进程。
除非使用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()
函数到达应用程序中的“断点”。这里的技巧是一直跳出睡眠函数,直到达到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()
,你可以继续调试程序或让它运行到完成。
详细说明的完整示例在这里。
bool file_exists(const char *filename)
{
ifstream ifile(filename);
return ifile;
}
int cgiMain()
{
while (file_exists ("/var/tmp/flag"))
sleep (1);
...
your code
SCRIPT_NAME
QUERY_STRING
CONTENT_LENGTH
CONTENT_TYPE
REMOTE_ADDR
这些变量由像Apache这样的http服务器提供。在使用gdb进行调试时,我们需要使用set environment
自己设置这些值。在我的情况下,只需要一些变量(而且源代码非常古老,仍然使用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
对我来说,上面介绍的两种在没有 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"}