Вопрос

I have a complex cgi executable written in C, I configured in Apache2 and now it is running succesfully. How can I debug this program in the source code, such as set break points and inspect variables? Any tools like gdb or eclipse? Any tutorial of how to set up the debugging environment?

Thanks in advance!!

Это было полезно?

Решение

The CGI interface basically consists in passing the HTTP request to the executable's standard input and getting the response on the standard output. Therefore you can write test requests to files and manually execute your CGI without having to use Apache. The debugging can then be done with GDB :

gdb ./my_cgi
>> break some_func
>> run < my_req.txt

with my_req.txt containing the full request:

GET /some/func HTTP/1.0
Host: myhost

If you absolutely need the CGI to be run by Apache it may become tricky to attach GDB to the right process. You can for example configure Apache to have only one worker process, attach to it with gdb -p and use set follow-fork-mode child to make sure it switches to the CGI process when a request arrives.

Другие советы

I did this: in cgi main i added code to look for an existing file, like /var/tmp/flag. While existing, i run in a loop. Time enough to attach to cgi process via gdb. After then i delete /var/tmp/flag and from now I can debug my cgi code.

bool file_exists(const char *filename)
{
   ifstream ifile(filename);
   return ifile;
}

int cgiMain()
{

    while (file_exists ("/var/tmp/flag"))
    sleep (1);
    ...
    your code 

Unless FastCGI or SCGI is used, the CGI process is short-lived and you need to delay its exit to have enough time to attach the debugger while the process is still running. For casual debugging the easiest option is to simply use sleep() in an endless loop at the breakpoint location and exit the loop with the debugger once it is attached to the program.

Here's a small example CGI program:

#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;
}

Suppose it is compiled into cgi-debugging-example, this is how you would attach the debugger once the application enters the endless loop:

sudo cgdb cgi-debugging-example $(pgrep cgi-debugging)

Next you need to exit the infinite loop and wait_for_gdb_to_attach() function to reach the "breakpoint" in your application. The trick here is to step out of sleep functions until you reach wait_for_gdb_to_attach() and set the value of the variable is_waiting with the debugger so that while (is_waiting) exits:

(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

Once you are out of wait_for_gdb_to_attach(), you can continue debugging the program or let it run to completion.

Full example with detailed instructions here.

I'm not sure how to use gdb or other frontends in eclipse, but I just debugged my CGI program with gdb. I'd like to share something that other answers didn't mention, that CGIs usually need to read request meta-variables defined in RFC 3875#4.1 with getenv(3). Popular request variables in my mind are:

  • SCRIPT_NAME
  • QUERY_STRING
  • CONTENT_LENGTH
  • CONTENT_TYPE
  • REMOTE_ADDR

There variables are provided by http servers such as Apache. When debugging with gdb, we need to set these values by our own with set environment. In my case, there're only a few variables neededa(and the source code is very old, it still uses SCRIPT_URL instead of SCRIPT_NAME), so here's my example:

gdb cgi_name
set environment SCRIPT_URL /path/to/sub/cgis
set environment QUERY_STRING p1=v1&p2=v2
break foo.c:42
run

For me both solutions for debugging the CGI in gdb without web server presented above didn't work.

Maybe the second solution works for a GET Request.

I needed a combination of both, first setting the environment variables from rfc3875 (not sure if all of them are really neded). Then I was able to pass only the params (not the compltete request) via STDIN from a file.

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

With params.txt:

{"user":"admin","pass":"admin"}
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top