Chinaunix首页 | 论坛 | 博客
  • 博客访问: 64218
  • 博文数量: 21
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 236
  • 用 户 组: 普通用户
  • 注册时间: 2014-09-05 21:34
文章分类

全部博文(21)

文章存档

2015年(21)

我的朋友

分类: LINUX

2015-08-07 23:54:50


点击(此处)折叠或打开

  1. //start from the very beginning,and to create greatness
  2. //@author: Chuangwei Lin
  3. //@E-mail:979951191@qq.com
  4. //@brief: SHTTPD支持CGI的实现
  5. #include "lcw_shttpd.h"
  6. /******************************************************
  7. 函数名: GenerateDirFile(struct worker_ctl *wctl)
  8. 参数:
  9. 功能:生成目录下的文件列表
  10. *******************************************************/
  11. int GenerateDirFile(struct worker_ctl *wctl)
  12. {
  13.     struct conn_request *req = &wctl->conn.con_req;
  14.     struct conn_response *res = &wctl->conn.con_res;
  15.     char *command = strstr(req->uri, CGISTR) + strlen(CGISTR);
  16.     char *arg[ARGNUM];
  17.     int num = 0;
  18.     char *rpath = wctl->conn.con_req.rpath;
  19.     stat *fs = &wctl->conn.con_res.fsate;
  20.     //打开目录
  21.     DIR *dir = opendir(rpath);
  22.     if(dir == NULL)
  23.     {
  24.         //错误
  25.         res->status = 500;
  26.         retval = -1;
  27.         goto EXITgenerateIndex;
  28.     }
  29.     //建立临时文件保存目录列表
  30.     File *tmpfile;
  31.     char tmpbuff[2048];
  32.     int filesize = 0;
  33.     char *uri = wctl->conn.con_req.uri;
  34.     //以wb+形式创建一个临时二进制文件
  35.     tmpfile = tmpfile();
  36.     //标题部分
  37.     sprintf(tmpbuff,
  38.         "%s%s%s",
  39.         "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 3.2 Final//EN\">\n<HTML><HEAD><TITLE>",
  40.         uri,
  41.         "</TITLE></HEAD>\n"    );
  42.     fprintf(tmpfile, "%s", tmpbuff);
  43.     filesize += strlen(tmpbuff);
  44.     //标识部分
  45.     sprintf(tmpbuff,
  46.         "%s %s %s",
  47.         "<BODY><H1>Index of:",
  48.         uri,
  49.         " </H1> <HR><P><I>Date: </I> <I>Size: </I></P><HR>");
  50.     fprintf(tmpfile, "%s", tmpbuff);
  51.     filesize += strlen(tmpbuff);    
  52.     //读取目录中的文件列表
  53.     struct dirent *de;
  54.     #define PATHLENGTH 2048
  55.     char path[PATHLENGTH];
  56.     char tmpath[PATHLENGTH];
  57.     char linkname[PATHLENGTH];
  58.     struct stat fs;//stat 结构定义于:/usr/include/sys/stat.h 文件中
  59.     strcpy(path, rpath);
  60.     if(rpath[strlen(rpath)]!='/')
  61.     {
  62.         rpath[strlen(rpath)]='/';
  63.     }
  64.     while ((de = readdir(dir)) != NULL)//读取一个文件
  65.     {
  66.         menset(tmpath, 0, sizeof(tmpath));
  67.         menset(linkname, 0, sizeof(linkname));
  68.         if(strcmp(de->d_name, "."))//不是当前目录
  69.         {
  70.             if(strcmp(de->d_name, ".."))//不是父目录            
  71.             {
  72.                 strcpy(linkname,de->d_name);//将目录名称作为链接名称
  73.             }
  74.             else//是父目录
  75.             {
  76.                 strcpy(linkname, "Parent Directory");//将父目录作为链接名称
  77.             }

  78.             sprintf(tmpath, "%s%s",path, de->d_name);//构建当前文件的全路径
  79.             stat(tmpath, &fs);//获得文件信息
  80.             if(S_ISDIR(fs.st_mode))//是一个目录
  81.             {
  82.                 //打印目录的连接为目录名称
  83.                 sprintf(tmpbuff, "<A HREF=\"%s/\">%s/</A><BR>\n", de->d_name,tmpath);
  84.             }
  85.             else//正常文件
  86.             {
  87.                 char size_str[32];
  88.                 off_t size_int;
  89.                 size_int = fs.st_size;//文件大小
  90.                 if (size_int < 1024)//不到1K
  91.                     sprintf(size_str, "%d bytes", (int) size_int);
  92.                 else if (size_int < 1024*1024)//不到1M
  93.                     sprintf(size_str, "%1.2f Kbytes", (float) size_int / 1024);
  94.                 else//其他
  95.                     sprintf(size_str, "%1.2f Mbytes", (float) size_int / (1024*1024));
  96.                 //输出文件大小
  97.                 sprintf(tmpbuff, "<A HREF=\"%s\">%s</A> (%s)<BR>\n", de->d_name, linkname, size_int);
  98.             }
  99.             //将形成的字符串写入临时文件
  100.             fprintf(tmpfile, "%s", tmpbuff);
  101.             filesize += strlen(tmpbuff);                
  102.         }
  103.     }
  104.     //生成临时的文件信息,主要是文件大小
  105.     fs.st_ctime = time(NULL);
  106.     fs.st_mtime = time(NULL);
  107.     fs.st_size = filesize;
  108.     fseek(tmpfile, (long) 0, SEEK_SET);//移动文件指针到头部
  109. EXITgenerateIndex:
  110.     return 0;
  111. }

  112. #define CGISTR "/cgi-bin/"//CGI目录的字符串
  113. #define ARGNUM 16 //CGI程序变量的最大个数
  114. #define READIN 0 //读出管道
  115. #define WRITEOUT 1 //写入管道
  116. /******************************************************
  117. 函数名:cgiHandler(struct worker_ctl *wctl)
  118. 参数:
  119. 功能:
  120. *******************************************************/
  121. int cgiHandler(struct worker_ctl *wctl)
  122. {
  123.     struct conn_request *req = &wctl->conn.con_req;
  124.     struct conn_response *res = &wctl->conn.con_res;
  125.     //strstr(str1,str2);str1:被查找目标 str2:要查找对象 
  126.     char *command = strstr(req->uri, CGISTR) + strlen(CGISTR);//获得匹配字符串/cgi-bin/
  127.     char *arg[ARGNUM];
  128.     int num = 0;
  129.     char *rpath = wctl->conn.con_req.rpath;
  130.     stat *fs = &wctl->conn.con_res.fsate;
  131.     int retval = -1;
  132.     char *pos = command;//查找CGI的命令
  133.     for(;*pos != '?' && *pos !='\0';pos++);//找到命令尾
  134.      {
  135.          *pos = '\0';
  136.      }
  137.     sprintf(rpath, "%s%s",conf_para.CGIRoot,command);//构建全路径
  138.     //查找CGI的参数
  139.     pos++;
  140.     for(;*pos != '\0' && num < ARGNUM;)
  141.     {    //CGI的参数为紧跟CGI命令后的?的字符串,多个变量之间用+连接起来,所以可以根据加号的个数确定参数的个数
  142.         arg[num] = pos;//参数头
  143.         for(;*pos != '+' && *pos!='\0';pos++);
  144.         if(*pos == '+')
  145.         {
  146.             *pos = '\0';//参数尾
  147.             pos++;
  148.             num++;
  149.         }
  150.     }
  151.     arg[num] = NULL;
  152.     //命令的属性
  153.     if(stat(rpath,fs)<0)
  154.     {
  155.         //错误
  156.         res->status = 403;
  157.         retval = -1;
  158.         goto EXITcgiHandler;
  159.     }
  160.     else if((fs->st_mode & S_IFDIR) == S_IFDIR)
  161.     {
  162.         //是一个目录,列出目录下的文件
  163.         GenerateDirFile(wctl);
  164.         retval = 0;
  165.         goto EXITcgiHandler;
  166.     }
  167.     else if((fs->st_mode & S_IXUSR) != S_IXUSR)
  168.     {
  169.         //所指文件不能执行
  170.         res->status = 403;
  171.         retval = -1;
  172.         goto EXITcgiHandler;
  173.     }
  174.     //创建进程间通信的管道    
  175.     int pipe_in[2];
  176.     int pipe_out[2];

  177.     if(pipe(pipe_in) < 0)//创建管道
  178.     {
  179.         res->status = 500;
  180.         retval = -1;
  181.         goto EXITcgiHandler;
  182.     }
  183.     if(pipe(pipe_out) < 0)
  184.     {
  185.         res->status = 500;
  186.         retval = -1;
  187.         goto EXITcgiHandler;
  188.     }
  189.     //进程分叉
  190.     int pid = 0;
  191.     pid = fork();
  192.     if(pid < 0)//错误
  193.     {
  194.         res->status = 500;
  195.         retval = -1;
  196.         goto EXITcgiHandler;
  197.     }
  198.     else if(pid > 0)//父进程
  199.     {
  200.         close(pipe_out[WRITEOUT]);//关闭写端
  201.         close(pipe_in[READIN]);//关闭读端
  202.         //主进程从CGI的标准输出读取数据    ,并将数据发送到网络资源请求的客户端
  203.         int size = 0;//这里初始化为0,怎么进入while循环?改为下面的情况
  204.         int end = 0;
  205.         //读取CGI进程数据
  206.         size = read(pipe_out[READIN], res->res.ptr, sizeof(wctl->conn.dres));
  207.         while(size > 0 && !end)
  208.         {
  209.             if(size > 0)
  210.             {//将数据发送给客户端
  211.                 send(wctl->conn.cs, res->res.ptr, strlen(res->res.ptr));
  212.             }
  213.             else
  214.             {
  215.                 end = 1;
  216.             }
  217.             size = read(pipe_out[READIN], res->res.ptr, sizeof(wctl->conn.dres));
  218.         }
  219.         wait(&end);//等待其子进程全部结束
  220.         close(pipe_out[READIN]);//关闭管道
  221.         close(pipe_in[WRITEOUT]);
  222.         retval = 0;
  223.     }
  224.     else//子进程
  225.     {
  226.         char cmdarg[2048];
  227.         char onearg[2048];
  228.         char *pos = NULL;
  229.         int i = 0;
  230.         //形成执行命令
  231.         memset(onearg, 0, 2048];
  232.         for(i = 0;i<num;i++)
  233.             sprintf(cmdarg,"%s %s", onearg, arg[i]);
  234.         //将写入的管道绑定到标注输出
  235.         close(pipe_out[READIN]); //关闭无用的读管道
  236.         dup2(pipe_out[WRITEOUT], 1); //将写管道绑定到标准输出
  237.         close(pipe_out[WRITEOUT]); //关闭写管道

  238.         close(pipe_in[WRITEOUT]); // 关闭无用的写管道
  239.         dup2(pipe_in[READIN], 0); // 将读管道绑定到标准输入
  240.         close(pipe_in[READIN]); // 关闭写管道
  241.         //execlp()会从PATH 环境变量所指的目录中查找符合参数file的文件名,
  242.         //找到后便执行该文件,然后将第二个以后的参数当做该文件的argv[0]
  243.         //argv[1]……,最后一个参数必须用空指针(NULL)作结束
  244.         execlp(rpath, arg);//执行命令,命令的输出需要为标准输出     
  245.     }
  246. EXITcgiHandler:
  247.     return retval;
  248. }

阅读(1990) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~