• 一个简单的wed服务器SHTTPD(4)————SHTTPD支持CGI的实现


    //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">
    <HTML><HEAD><TITLE>",
            uri,
            "</TITLE></HEAD>
    "    );
        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>
    ", 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>
    ", 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 !='';pos++);//找到命令尾
         {
             *pos = '';
         }
        sprintf(rpath, "%s%s",conf_para.CGIRoot,command);//构建全路径
        //查找CGI的参数
        pos++;
        for(;*pos != '' && num < ARGNUM;)
        {    //CGI的参数为紧跟CGI命令后的?的字符串,多个变量之间用+连接起来,所以可以根据加号的个数确定参数的个数
            arg[num] = pos;//参数头
            for(;*pos != '+' && *pos!='';pos++);
            if(*pos == '+')
            {
                *pos = '';//参数尾
                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;
    }
  • 相关阅读:
    【PyQt5-Qt Designer】QSpinBox-微调框
    【PyQt5-Qt Designer】QProgressBar() 进度条
    【PyQt5-Qt Designer】QSlider滑块
    Tomcat eclipse 启动时一个工程影响另一个工程
    apache thrift 和 apache jersey 记录
    常用 Linux 命令
    mac 命令记录
    eclipse m2eclipse 从Maven的本地库中读取依赖库
    成功build Maven但eclipse中依然显示该工程有错误
    mac install: /usr/bin/unrar: Operation not permitted
  • 原文地址:https://www.cnblogs.com/sigma0-/p/12630523.html
Copyright © 2020-2023  润新知