CGI程序无法获得环境变量(QUERY_STRING)?

源代码链接

我的关键代码如下:

调用CGI程序的代码段:

if (is_cgi(buff)) {
                char* cgi_file_name = get_cgi_file_name(buff);

                pid_t pid;
                pid = fork(); // 创建子进程,在子进程里执行CGI程序
                
                if (0 == pid) {
                    char path[] = "/codedir/cppCode/cgi-bin/";
                    strcat(path, cgi_file_name);
                    cout<<path<<endl;
                    cout<<cgi_file_name<<endl;
                    execl(path, cgi_file_name, NULL);
                }
            }

CGI程序代码:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

using namespace std;

int main(int argc, char const *argv[])
{
    char *buf, *p;
    char arg1[1024], arg2[1024], content[1024];

    printf("HTTP/1.0 200 OK\r\n");
    printf("Connection: close\r\n");
    printf("Content-type: text/html\r\n\r\n");
    printf("I am hht\n");

    if ((buf = getenv("QUERY_STRING")) != NULL) {
        p = strchr(buf, '&');
        *p = '\0';

        strcpy(arg1, buf);
        strcpy(arg2, p + 1);

        sprintf(content, "your name is %s\n", arg1);
        sprintf(content, "your password is %s\n", arg2);
        
        fflush(stdout);
    }

    exit(0);
}

//g++ -g -o login login.c

图片描述

上面的图片是执行的结果,可以看出,CGI程序被成功的调用了。但是,getenv("QUERY_STRING")并没有取到参数。

我的参数是这样子的:
图片描述

我的CGI程序是把一个叫做login.c的源代码编译后命名为login.cgi可执行文件。(一定要命名为login.cgi吗?可以命名为login吗?)
图片描述

我的完整代码如下(CGI的完整代码就是上面的):

#include <iostream>
#include <string>
#include <cstring>
#include <cstdlib>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/unistd.h>
#include <netinet/in.h>
#include <stdio.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

using namespace std;

const int BUFFSIZE = 1024;
const int MAXLINK = 10; // 未经过处理的连接请求队列可以容纳的最大数目
const int DEFAULT_PORT = 8080;

char* get_cgi_file_name(char* buff) {
    char* file_name = new char[BUFFSIZE];
    file_name = buff + 13;
    char* end = strchr(file_name, '?');
    *end = '\0';

    return file_name;
}

bool is_cgi(char* buff) {
    char* start = buff + 5;
    char* end = strchr(start, '/');
    const char* target = "cgi-bin"; // 只读

    for (char* p = start; p != end; p++) {
        if (*p != *target) {
            return false;
        }

        target++;
    }

    return true;
}

/*
作用:确保把数据全部写入了连接的那个文件描述符中
*/
int makesure_write(int connect_fd, void* buff, int n) {
    int remain_num = n;
    int success_write_num = 0;
    char* buff_current_postition = (char*)buff;

    while (remain_num > 0) {
        success_write_num = write(connect_fd, buff_current_postition, remain_num);
        remain_num -= success_write_num;
        buff_current_postition += success_write_num;
    }

    return n - remain_num;
}

/*
作用:
获取文件类型
*/
void get_filetype(const char* file_name, char* file_type) {
    if (strstr(file_name, ".html"))
    strcpy(file_type, "text/html");
    else if (strstr(file_name, ".gif"))
    strcpy(file_type, "image/gif");
    else if (strstr(file_name, ".png"))
    strcpy(file_type, "image/png");
    else if (strstr(file_name, ".jpg"))
    strcpy(file_type, "image/jpeg");
    else
    strcpy(file_type, "text/plain");
}

/*
作用:
获取文件内容的字节数
*/
int get_file_size(char* file_name) {
    FILE* fp = fopen(file_name, "r");
    if (!fp) {
        return -1;
    }
    fseek(fp, 0L, SEEK_END); // 移动文件指针到文件的末尾
    int size = ftell(fp); // 获得目前的文件的访问位置,从而间接获得文件的大小
    fclose(fp);

    return size;
}

/*
获取文件的名字
*/
char* get_file_name (char* buff) {
    char* file_name = buff + 5;
    char *space = strchr(file_name, ' ');
    *space = '\0';
    return file_name;
}

/*
作用:
处理http请求
*/
void deal_get_http(int connect_fd, char* request_header) {
    char* file_name = get_file_name(request_header);
    int file_size = get_file_size(file_name);
    char file_type[BUFFSIZE];
    get_filetype(file_name, file_type);

    int fd = open(file_name, O_RDONLY);
    void* file_in_mem_addr = mmap(0, file_size, PROT_READ, MAP_PRIVATE, fd, 0);
    close(fd);

    char buff[BUFFSIZE];
    
    sprintf(buff, "HTTP/1.0 200 OK\r\n");
    sprintf(buff, "%sServer: HHTWS Web Server\r\n", buff);
    sprintf(buff, "%sContent-length: %d\r\n", buff, file_size);
    sprintf(buff, "%sContent-type: %s\r\n\r\n", buff, file_type);

    makesure_write(connect_fd, buff, strlen(buff));
    makesure_write(connect_fd, file_in_mem_addr, file_size);

    munmap(file_in_mem_addr, file_size);
}

/*
作用:判断是否是GET请求
*/
bool is_get_http(char* buff) {
    if (!strncmp(buff, "GET", 3)) { // 如果是GET请求
        return true;
    }
    else {
        return false;
    }
}

int main(int argc, char const *argv[])
{


    int socket_fd, connect_fd;
    struct sockaddr_in servaddr;
    char buff[BUFFSIZE];

    socket_fd = socket(AF_INET, SOCK_STREAM, 0);
    if (socket_fd == -1) {
            cout<<"create socket error"<<endl;
            return -1;
    }

    memset(&servaddr, 0, sizeof(servaddr));
    servaddr.sin_family = AF_INET;
    servaddr.sin_addr.s_addr = htonl(INADDR_ANY); // IP必须是网络字节序,INADDR_ANY是绑定本机上所有IP
    servaddr.sin_port = htons(DEFAULT_PORT); // 端口号必须是网络字节序
    
    if (bind(socket_fd, (struct sockaddr*)&servaddr, sizeof(servaddr)) == -1) {
        cout<<"bind error"<<endl;
        return -1;
    }

    if (listen(socket_fd, MAXLINK) == -1) {
        cout<<"listen error"<<endl;
    }
    
    while (true) {
        connect_fd = accept(socket_fd, (struct sockaddr*)NULL, NULL);
        
        if (connect_fd == -1) {
            cout<<"accept error"<<endl;
        }
        else {
            cout<<"连接成功"<<endl;
        }

        memset(buff, '\0', sizeof(buff));

        recv(connect_fd, buff, BUFFSIZE - 1, 0); // 把请求头(或发送的消息)写入buff中

        if (is_get_http(buff)) {
            if (is_cgi(buff)) {
                char* cgi_file_name = get_cgi_file_name(buff);

                pid_t pid;
                pid = fork(); // 创建子进程,在子进程里执行CGI程序
                
                if (0 == pid) {
                    char path[] = "/codedir/cppCode/cgi-bin/";
                    strcat(path, cgi_file_name);
                    cout<<path<<endl;
                    cout<<cgi_file_name<<endl;
                    execl(path, cgi_file_name, NULL);
                }
            }
            else {
                deal_get_http(connect_fd, buff);
            }
        }
    }

    close(connect_fd);
    close(socket_fd);

    return 0;
}

希望前辈们能够解答一下,蟹蟹。

阅读 2.7k
评论
    1 个回答

    问题出在父进程没有将QUERY_STRING环境变量传递给子进程以及cgi程序(login)。
    fork或者调用exec系列函数,子进程都会继承父进程的环境变量。
    你可以从cgi_file_name中获取query string,然后在主进程中设置QUERY_STRING环境变量:

    putenv("QUERY_STRING=xxxxx");

    这样login子进程就可以继承此环境变量。

    或者调用execle,直接通过envp参数传递环境变量。

      撰写回答

      登录后参与交流、获取后续更新提醒

      相似问题
      推荐文章