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

最新文章

  1. caffe初试(一)happynear的caffe-windows版本的配置及遇到的问题
  2. 黑马程序员——C语言基础 char字符 数组
  3. Android开发遇到的坑(1):Java中List的安全删除问题
  4. Selenium2+python自动化20-Excel数据参数化
  5. 【转】MySQL中增加sequence管理功能(模拟创建sequence)
  6. MySQL的Explain解释器的部分理解
  7. 深入理解计算机系统第二版习题解答CSAPP 2.15
  8. 开发日志_Jan.8.2017
  9. 【转】两种方法教你在Ubuntu下轻松关闭触摸板(TinkPad)
  10. HDU2191:悼念512汶川大地震遇难同胞——珍惜现在,感恩生活(多重背包)
  11. RabbitMQ --- Routing(路由)
  12. OOP编程特性综合项目
  13. Beta项目总结
  14. # 【Python3练习题 004】输入某年某月某日,判断这一天是这一年的第几天?
  15. leetcode每日刷题计划-简单篇day3
  16. pgm7
  17. Spring AspectJ切入点语法详解
  18. 科学计算三维可视化---Mlab基础(数据可视化)
  19. R ggplot2 线性回归
  20. Tomcat部署静态网站

热门文章

  1. Array(数组)对象--&gt;slice() 方法
  2. 数据结构和算法(Golang实现)(18)排序算法-前言
  3. CVE-2020-0796 永恒之蓝?
  4. 曹工说Redis源码(6)-- redis server 主循环大体流程解析
  5. python基础:如何使用python pandas将DataFrame转换为dict
  6. E - Farthest Nodes in a Tree
  7. openssl进行RSA加解密(C++)
  8. Redis安装部署(一主二从三哨兵)
  9. Jmeter系列(1)- 环境部署
  10. WebApi参数检查验证FluentValidation的使用方法