如何调试在Apache2中运行的用C编写的CGI程序?

5
我有一个用C语言编写的复杂cgi可执行文件,我已经在Apache2中进行了配置,并且现在它已经成功运行。我该如何调试源代码程序,例如设置断点和检查变量?是否有诸如gdb或eclipse之类的工具?是否有关于如何设置调试环境的教程?
先感谢您!
5个回答

8

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进程。


在连接到Apache之前,我需要gdbserver对吗?有关如何在Eclipse CDT中进行设置的任何说明吗? - Joe

2

除非使用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(),你可以继续调试程序或让它运行到完成。

详细说明的完整示例在这里


2
我这样做:在CGI主程序中,我添加了代码以查找现有文件,如/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 

1
我不确定如何在eclipse中使用gdb或其他前端,但我刚用gdb调试了我的CGI程序。我想分享一些其他答案没有提到的东西,即CGI通常需要使用getenv(3)读取在RFC 3875#4.1中定义的请求元变量。我心目中的流行请求变量包括:
  • 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

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"}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接