-
//start from the very beginning,and to create greatness
-
//@author: Chuangwei Lin
-
//@E-mail:979951191@qq.com
-
//@brief: SHTTPD支持CGI的实现
-
#include "lcw_shttpd.h"
-
/******************************************************
-
函数名: GenerateDirFile(struct worker_ctl *wctl)
-
参数:
-
功能:生成目录下的文件列表
-
*******************************************************/
-
int GenerateDirFile(struct worker_ctl *wctl)
-
{
-
struct conn_request *req = &wctl->conn.con_req;
-
struct conn_response *res = &wctl->conn.con_res;
-
char *command = strstr(req->uri, CGISTR) + strlen(CGISTR);
-
char *arg[ARGNUM];
-
int num = 0;
-
char *rpath = wctl->conn.con_req.rpath;
-
stat *fs = &wctl->conn.con_res.fsate;
-
//打开目录
-
DIR *dir = opendir(rpath);
-
if(dir == NULL)
-
{
-
//错误
-
res->status = 500;
-
retval = -1;
-
goto EXITgenerateIndex;
-
}
-
//建立临时文件保存目录列表
-
File *tmpfile;
-
char tmpbuff[2048];
-
int filesize = 0;
-
char *uri = wctl->conn.con_req.uri;
-
//以wb+形式创建一个临时二进制文件
-
tmpfile = tmpfile();
-
//标题部分
-
sprintf(tmpbuff,
-
"%s%s%s",
-
"<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 3.2 Final//EN\">\n<HTML><HEAD><TITLE>",
-
uri,
-
"</TITLE></HEAD>\n" );
-
fprintf(tmpfile, "%s", tmpbuff);
-
filesize += strlen(tmpbuff);
-
//标识部分
-
sprintf(tmpbuff,
-
"%s %s %s",
-
"<BODY><H1>Index of:",
-
uri,
-
" </H1> <HR><P><I>Date: </I> <I>Size: </I></P><HR>");
-
fprintf(tmpfile, "%s", tmpbuff);
-
filesize += strlen(tmpbuff);
-
//读取目录中的文件列表
-
struct dirent *de;
-
#define PATHLENGTH 2048
-
char path[PATHLENGTH];
-
char tmpath[PATHLENGTH];
-
char linkname[PATHLENGTH];
-
struct stat fs;//stat 结构定义于:/usr/include/sys/stat.h 文件中
-
strcpy(path, rpath);
-
if(rpath[strlen(rpath)]!='/')
-
{
-
rpath[strlen(rpath)]='/';
-
}
-
while ((de = readdir(dir)) != NULL)//读取一个文件
-
{
-
menset(tmpath, 0, sizeof(tmpath));
-
menset(linkname, 0, sizeof(linkname));
-
if(strcmp(de->d_name, "."))//不是当前目录
-
{
-
if(strcmp(de->d_name, ".."))//不是父目录
-
{
-
strcpy(linkname,de->d_name);//将目录名称作为链接名称
-
}
-
else//是父目录
-
{
-
strcpy(linkname, "Parent Directory");//将父目录作为链接名称
-
}
-
-
sprintf(tmpath, "%s%s",path, de->d_name);//构建当前文件的全路径
-
stat(tmpath, &fs);//获得文件信息
-
if(S_ISDIR(fs.st_mode))//是一个目录
-
{
-
//打印目录的连接为目录名称
-
sprintf(tmpbuff, "<A HREF=\"%s/\">%s/</A><BR>\n", de->d_name,tmpath);
-
}
-
else//正常文件
-
{
-
char size_str[32];
-
off_t size_int;
-
size_int = fs.st_size;//文件大小
-
if (size_int < 1024)//不到1K
-
sprintf(size_str, "%d bytes", (int) size_int);
-
else if (size_int < 1024*1024)//不到1M
-
sprintf(size_str, "%1.2f Kbytes", (float) size_int / 1024);
-
else//其他
-
sprintf(size_str, "%1.2f Mbytes", (float) size_int / (1024*1024));
-
//输出文件大小
-
sprintf(tmpbuff, "<A HREF=\"%s\">%s</A> (%s)<BR>\n", de->d_name, linkname, size_int);
-
}
-
//将形成的字符串写入临时文件
-
fprintf(tmpfile, "%s", tmpbuff);
-
filesize += strlen(tmpbuff);
-
}
-
}
-
//生成临时的文件信息,主要是文件大小
-
fs.st_ctime = time(NULL);
-
fs.st_mtime = time(NULL);
-
fs.st_size = filesize;
-
fseek(tmpfile, (long) 0, SEEK_SET);//移动文件指针到头部
-
EXITgenerateIndex:
-
return 0;
-
}
-
-
#define CGISTR "/cgi-bin/"//CGI目录的字符串
-
#define ARGNUM 16 //CGI程序变量的最大个数
-
#define READIN 0 //读出管道
-
#define WRITEOUT 1 //写入管道
-
/******************************************************
-
函数名:cgiHandler(struct worker_ctl *wctl)
-
参数:
-
功能:
-
*******************************************************/
-
int cgiHandler(struct worker_ctl *wctl)
-
{
-
struct conn_request *req = &wctl->conn.con_req;
-
struct conn_response *res = &wctl->conn.con_res;
-
//strstr(str1,str2);str1:被查找目标 str2:要查找对象
-
char *command = strstr(req->uri, CGISTR) + strlen(CGISTR);//获得匹配字符串/cgi-bin/
-
char *arg[ARGNUM];
-
int num = 0;
-
char *rpath = wctl->conn.con_req.rpath;
-
stat *fs = &wctl->conn.con_res.fsate;
-
int retval = -1;
-
char *pos = command;//查找CGI的命令
-
for(;*pos != '?' && *pos !='\0';pos++);//找到命令尾
-
{
-
*pos = '\0';
-
}
-
sprintf(rpath, "%s%s",conf_para.CGIRoot,command);//构建全路径
-
//查找CGI的参数
-
pos++;
-
for(;*pos != '\0' && num < ARGNUM;)
-
{ //CGI的参数为紧跟CGI命令后的?的字符串,多个变量之间用+连接起来,所以可以根据加号的个数确定参数的个数
-
arg[num] = pos;//参数头
-
for(;*pos != '+' && *pos!='\0';pos++);
-
if(*pos == '+')
-
{
-
*pos = '\0';//参数尾
-
pos++;
-
num++;
-
}
-
}
-
arg[num] = NULL;
-
//命令的属性
-
if(stat(rpath,fs)<0)
-
{
-
//错误
-
res->status = 403;
-
retval = -1;
-
goto EXITcgiHandler;
-
}
-
else if((fs->st_mode & S_IFDIR) == S_IFDIR)
-
{
-
//是一个目录,列出目录下的文件
-
GenerateDirFile(wctl);
-
retval = 0;
-
goto EXITcgiHandler;
-
}
-
else if((fs->st_mode & S_IXUSR) != S_IXUSR)
-
{
-
//所指文件不能执行
-
res->status = 403;
-
retval = -1;
-
goto EXITcgiHandler;
-
}
-
//创建进程间通信的管道
-
int pipe_in[2];
-
int pipe_out[2];
-
-
if(pipe(pipe_in) < 0)//创建管道
-
{
-
res->status = 500;
-
retval = -1;
-
goto EXITcgiHandler;
-
}
-
if(pipe(pipe_out) < 0)
-
{
-
res->status = 500;
-
retval = -1;
-
goto EXITcgiHandler;
-
}
-
//进程分叉
-
int pid = 0;
-
pid = fork();
-
if(pid < 0)//错误
-
{
-
res->status = 500;
-
retval = -1;
-
goto EXITcgiHandler;
-
}
-
else if(pid > 0)//父进程
-
{
-
close(pipe_out[WRITEOUT]);//关闭写端
-
close(pipe_in[READIN]);//关闭读端
-
//主进程从CGI的标准输出读取数据 ,并将数据发送到网络资源请求的客户端
-
int size = 0;//这里初始化为0,怎么进入while循环?改为下面的情况
-
int end = 0;
-
//读取CGI进程数据
-
size = read(pipe_out[READIN], res->res.ptr, sizeof(wctl->conn.dres));
-
while(size > 0 && !end)
-
{
-
if(size > 0)
-
{//将数据发送给客户端
-
send(wctl->conn.cs, res->res.ptr, strlen(res->res.ptr));
-
}
-
else
-
{
-
end = 1;
-
}
-
size = read(pipe_out[READIN], res->res.ptr, sizeof(wctl->conn.dres));
-
}
-
wait(&end);//等待其子进程全部结束
-
close(pipe_out[READIN]);//关闭管道
-
close(pipe_in[WRITEOUT]);
-
retval = 0;
-
}
-
else//子进程
-
{
-
char cmdarg[2048];
-
char onearg[2048];
-
char *pos = NULL;
-
int i = 0;
-
//形成执行命令
-
memset(onearg, 0, 2048];
-
for(i = 0;i<num;i++)
-
sprintf(cmdarg,"%s %s", onearg, arg[i]);
-
//将写入的管道绑定到标注输出
-
close(pipe_out[READIN]); //关闭无用的读管道
-
dup2(pipe_out[WRITEOUT], 1); //将写管道绑定到标准输出
-
close(pipe_out[WRITEOUT]); //关闭写管道
-
-
close(pipe_in[WRITEOUT]); // 关闭无用的写管道
-
dup2(pipe_in[READIN], 0); // 将读管道绑定到标准输入
-
close(pipe_in[READIN]); // 关闭写管道
-
//execlp()会从PATH 环境变量所指的目录中查找符合参数file的文件名,
-
//找到后便执行该文件,然后将第二个以后的参数当做该文件的argv[0]、
-
//argv[1]……,最后一个参数必须用空指针(NULL)作结束
-
execlp(rpath, arg);//执行命令,命令的输出需要为标准输出
-
}
-
EXITcgiHandler:
-
return retval;
-
}
阅读(1983) | 评论(0) | 转发(0) |