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;
}
希望前辈们能够解答一下,蟹蟹。
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
问题出在父进程没有将QUERY_STRING环境变量传递给子进程以及cgi程序(login)。
fork或者调用exec系列函数,子进程都会继承父进程的环境变量。
你可以从cgi_file_name中获取query string,然后在主进程中设置QUERY_STRING环境变量:
这样login子进程就可以继承此环境变量。
或者调用execle,直接通过envp参数传递环境变量。