安装

参考如下博文安装thttpd软件

http://blog.csdn.net/21aspnet/article/details/7045845

http://blog.csdn.net/dragoncheng/article/details/5614559

thttpd配置文件:

root@fqs:/usr/local/bin# cat /usr/local/thttpd/
conf/ etc/  logs/ man/  sbin/ www/ 
root@fqs:/usr/local/bin# cat /usr/local/thttpd/conf/thttpd.conf

port=80
user=www
host=0.0.0.0
logfile=/usr/local/thttpd/logs/thttpd.log
pidfile=/usr/local/thttpd/logs/thttpd.pid
#throttles=/usr/local/thttpd/etc/throttle.conf
#urlpat=*.txt|*.mp3
#charset=utf-8
dir=/usr/local/thttpd/www
cgipat=/cgi-bin/*

cgilua采用luarocks安装。 其依赖 wsapi运行。

cgilua.cgi launcher:

root@fqs:/usr/local/bin# cat cgilua.cgi
#!/bin/sh

exec '/usr/bin/lua5.1' -e 'package.path="/root/.luarocks/share/lua/5.1/?.lua;/root/.luarocks/share/lua/5.1/?/init.lua;/usr/local/share/lua/5.1/?.lua;/usr/local/share/lua/5.1/?/init.lua;"..package.path; package.cpath="/root/.luarocks/lib/lua/5.1/?.so;/usr/local/lib/lua/5.1/?.so;"..package.cpath' -e 'local k,l,_=pcall(require,"luarocks.loader") _=k and l.add_context("cgilua","5.1.4-2")' '/usr/local/lib/luarocks/rocks/cgilua/5.1.4-2/bin/cgilua.cgi' "$@"
root@fqs:/usr/local/bin#
root@fqs:/usr/local/bin#
root@fqs:/usr/local/bin# cat /usr/local/lib/luarocks/rocks/cgilua/5.1.4-2/bin/cgilua.cgi
#!/usr/bin/env lua

-- CGILua (SAPI) launcher, extracts script to launch
-- either from the command line (use #!cgilua in the script)
-- or from SCRIPT_FILENAME/PATH_TRANSLATED
 
pcall(require, "luarocks.require")
 
local common = require "wsapi.common"
local cgi = require "wsapi.cgi"
 
local sapi = require "wsapi.sapi"
 
local arg_filename = (...)
 
local function sapi_loader(wsapi_env)
  common.normalize_paths(wsapi_env, arg_filename, "cgilua.cgi")
  return sapi.run(wsapi_env)
end
 
cgi.run(sapi_loader)
root@fqs:/usr/local/bin#

流程分析

thttpd配置遇到 cgipat规则的请求, 则启动cgi程序

cgipat规则为

cgipat=/cgi-bin/*

即, URL中含有  /cgi-bin/开头的文件名请求。

对应thttpd中的启动cgi子程序代码:

/* Is it world-executable and in the CGI area? */
    if ( hc->hs->cgi_pattern != (char*) 0 &&
     ( hc->sb.st_mode & S_IXOTH ) &&
     match( hc->hs->cgi_pattern, hc->expnfilename ) )
    return cgi( hc );

启动cgi程序逻辑

cgi函数中,其实是fork了一个子进程

r = fork( );
if ( r < 0 )
    {
    syslog( LOG_ERR, "fork - %m" );
    httpd_send_err(
    hc, 500, err500title, "", err500form, hc->encodedurl );
    return -1;
    }
if ( r == 0 )
    {
    /* Child process. */
    sub_process = 1;
    httpd_unlisten( hc->hs );
    cgi_child( hc );
    }

cgi_child为子进程继续执行逻辑

1、 准备环境变量:

/* Make the environment vector. */
envp = make_envp( hc );

/* Make the argument vector. */
argp = make_argp( hc );

环境变量中, 包括若干 cgi参数:

static char**
make_envp( httpd_conn* hc )
    {
    static char* envp[50];
    int envn;
    char* cp;
    char buf[256];

envn = 0;
    envp[envn++] = build_env( "PATH=%s", CGI_PATH );
#ifdef CGI_LD_LIBRARY_PATH
    envp[envn++] = build_env( "LD_LIBRARY_PATH=%s", CGI_LD_LIBRARY_PATH );
#endif /* CGI_LD_LIBRARY_PATH */
    envp[envn++] = build_env( "SERVER_SOFTWARE=%s", SERVER_SOFTWARE );
    if ( hc->hs->vhost && hc->hostname != (char*) 0 && hc->hostname[0] != '\0' )
    cp = hc->hostname;
    else if ( hc->hdrhost != (char*) 0 && hc->hdrhost[0] != '\0' )
    cp = hc->hdrhost;
    else if ( hc->reqhost != (char*) 0 && hc->reqhost[0] != '\0' )
    cp = hc->reqhost;
    else
    cp = hc->hs->server_hostname;
    if ( cp != (char*) 0 )
    envp[envn++] = build_env( "SERVER_NAME=%s", cp );
    envp[envn++] = "GATEWAY_INTERFACE=CGI/1.1";
    envp[envn++] = build_env("SERVER_PROTOCOL=%s", hc->protocol);
    (void) my_snprintf( buf, sizeof(buf), "%d", (int) hc->hs->port );
    envp[envn++] = build_env( "SERVER_PORT=%s", buf );
    envp[envn++] = build_env(
    "REQUEST_METHOD=%s", httpd_method_str( hc->method ) );
    if ( hc->pathinfo[0] != '\0' )
    {
    char* cp2;
    size_t l;
    envp[envn++] = build_env( "PATH_INFO=/%s", hc->pathinfo );
    l = strlen( hc->hs->cwd ) + strlen( hc->pathinfo ) + 1;
    cp2 = NEW( char, l );
    if ( cp2 != (char*) 0 )
        {
        (void) my_snprintf( cp2, l, "%s%s", hc->hs->cwd, hc->pathinfo );
        envp[envn++] = build_env( "PATH_TRANSLATED=%s", cp2 );
        }
    }
    envp[envn++] = build_env(
    "SCRIPT_NAME=/%s", strcmp( hc->origfilename, "." ) == 0 ?
    "" : hc->origfilename );
    if ( hc->query[0] != '\0')
    envp[envn++] = build_env( "QUERY_STRING=%s", hc->query );
    envp[envn++] = build_env(
    "REMOTE_ADDR=%s", httpd_ntoa( &hc->client_addr ) );
    if ( hc->referrer[0] != '\0' )
    {
    envp[envn++] = build_env( "HTTP_REFERER=%s", hc->referrer );
    envp[envn++] = build_env( "HTTP_REFERRER=%s", hc->referrer );
    }
    if ( hc->useragent[0] != '\0' )
    envp[envn++] = build_env( "HTTP_USER_AGENT=%s", hc->useragent );
    if ( hc->accept[0] != '\0' )
    envp[envn++] = build_env( "HTTP_ACCEPT=%s", hc->accept );
    if ( hc->accepte[0] != '\0' )
    envp[envn++] = build_env( "HTTP_ACCEPT_ENCODING=%s", hc->accepte );
    if ( hc->acceptl[0] != '\0' )
    envp[envn++] = build_env( "HTTP_ACCEPT_LANGUAGE=%s", hc->acceptl );
    if ( hc->cookie[0] != '\0' )
    envp[envn++] = build_env( "HTTP_COOKIE=%s", hc->cookie );
    if ( hc->contenttype[0] != '\0' )
    envp[envn++] = build_env( "CONTENT_TYPE=%s", hc->contenttype );
    if ( hc->hdrhost[0] != '\0' )
    envp[envn++] = build_env( "HTTP_HOST=%s", hc->hdrhost );
    if ( hc->contentlength != -1 )
    {
    (void) my_snprintf(
        buf, sizeof(buf), "%lu", (unsigned long) hc->contentlength );
    envp[envn++] = build_env( "CONTENT_LENGTH=%s", buf );
    }
    if ( hc->remoteuser[0] != '\0' )
    envp[envn++] = build_env( "REMOTE_USER=%s", hc->remoteuser );
    if ( hc->authorization[0] != '\0' )
    envp[envn++] = build_env( "AUTH_TYPE=%s", "Basic" );
    /* We only support Basic auth at the moment. */
    if ( getenv( "TZ" ) != (char*) 0 )
    envp[envn++] = build_env( "TZ=%s", getenv( "TZ" ) );
    envp[envn++] = build_env( "CGI_PATTERN=%s", hc->hs->cgi_pattern );

envp[envn] = (char*) 0;
    return envp;
    }

2、 将连接fd设置为cgi程序的标准输入:

/* Otherwise, the request socket is stdin. */
if ( hc->conn_fd != STDIN_FILENO )
    (void) dup2( hc->conn_fd, STDIN_FILENO );

3、 将连接fd设置为cgi程序的标准输出 和 错误:

/* Otherwise, the request socket is stdout/stderr. */
if ( hc->conn_fd != STDOUT_FILENO )
    (void) dup2( hc->conn_fd, STDOUT_FILENO );
if ( hc->conn_fd != STDERR_FILENO )
    (void) dup2( hc->conn_fd, STDERR_FILENO );

4、 启动cgi的业务进程, 替代当前的 fork映像

/* Run the program. */
(void) execve( binary, argp, envp );

注意 环境变量已经被注入到 启动进程中, 即在业务进程中, 可以访问到 cgi参数。

包括当前脚本名称: SCRIPT_NAME

execv功能

The  exec()  family  of functions replaces the current process image with a new process image.  The functions described in this manual page are front-ends for execve(2).  (See the
manual page for execve(2) for further details about the replacement of the current process image.)

http://www.tutorialspoint.com/unix_system_calls/execve.htm

execve() executes the program pointed to by filename. filename must be either a binary executable, or a script starting with a line of the form "#! interpreter [arg]". In the latter case, the interpreter must be a valid pathname for an executable which is not itself a script, which will be invoked as interpreter [arg] filename.

如果待执行文件为脚本, 则启动脚本脚本的解释器程序, 并执行脚本。

脚本内容

Z:\cgilua-master\cgilua-master\examples\index.lp 样例中的此脚本

意思为 启动程序 env,  执行cgilua.cgi程序, 来处理脚本文件

#!/usr/bin/env cgilua.cgi

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
   "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
    <title>Welcome to Kepler!</title>
    <link rel="stylesheet" href="css/doc.css" type="text/css"/>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
</head>

<body>

env命令程序的作用, 可以看出此处专门是用来 启动命令的(cgilua.cgi), 并没有设置环境变量

NAME
       env - run a program in a modified environment

SYNOPSIS
       env [OPTION]... [-] [NAME=VALUE]... [COMMAND [ARG]...]

DESCRIPTION
       Set each NAME to VALUE in the environment and run COMMAND.

Mandatory arguments to long options are mandatory for short options too.

cgilua.cgi

cgilua.cgi 主要业务文件为 /usr/local/lib/luarocks/rocks/cgilua/5.1.4-2/bin/cgilua.cgi

依赖wsapi.cgi 和  wsapi.common 和 wsapi.cgi模块

root@fqs:/usr/local/bin# cat cgilua.cgi
#!/bin/sh

exec '/usr/bin/lua5.1' -e 'package.path="/root/.luarocks/share/lua/5.1/?.lua;/root/.luarocks/share/lua/5.1/?/init.lua;/usr/local/share/lua/5.1/?.lua;/usr/local/share/lua/5.1/?/init.lua;"..package.path; package.cpath="/root/.luarocks/lib/lua/5.1/?.so;/usr/local/lib/lua/5.1/?.so;"..package.cpath' -e 'local k,l,_=pcall(require,"luarocks.loader") _=k and l.add_context("cgilua","5.1.4-2")' '/usr/local/lib/luarocks/rocks/cgilua/5.1.4-2/bin/cgilua.cgi' "$@"
root@fqs:/usr/local/bin#
root@fqs:/usr/local/bin#
root@fqs:/usr/local/bin#
root@fqs:/usr/local/bin# cat /usr/local/lib/luarocks/rocks/cgilua/5.1.4-2/bin/cgilua.cgi
#!/usr/bin/env lua

-- CGILua (SAPI) launcher, extracts script to launch
-- either from the command line (use #!cgilua in the script)
-- or from SCRIPT_FILENAME/PATH_TRANSLATED
 
pcall(require, "luarocks.require")
 
local common = require "wsapi.common"
local cgi = require "wsapi.cgi"
 
local sapi = require "wsapi.sapi"
 
local arg_filename = (...)
 
local function sapi_loader(wsapi_env)
  common.normalize_paths(wsapi_env, arg_filename, "cgilua.cgi")
  return sapi.run(wsapi_env)
end
 
cgi.run(sapi_loader)
 root@fqs:/usr/local/bin#

1、 wsapi.cgi模块为 脚本入口, 其提供了 获取环境变量的通道, 设置到 wsapi_env表中:

并将前文中说的, cgi程序将  连接fd, 接管后, 作为标准输入 和 输出 以及错误的代表。

local os = require"os"
local io = require"io"
local common = require"wsapi.common"

common.setmode()

local _M = {}

-- Runs an WSAPI application for this CGI request
function _M.run(app_run)
   common.run(app_run, { input = io.stdin, output = io.stdout,
     error = io.stderr, env = os.getenv })
end

return _M

2、 wsapi.sapi 脚本实现, 启动cgilua执行的逻辑:

local response = require "wsapi.response"

local _M = {}

function _M.run(wsapi_env)
  _G.CGILUA_APPS = _G.CGILUA_APPS or wsapi_env.DOCUMENT_ROOT .. "/cgilua"
  _G.CGILUA_CONF = _G.CGILUA_CONF or wsapi_env.DOCUMENT_ROOT .. "/cgilua"
  _G.CGILUA_TMP = _G.CGILUA_TMP or os.getenv("TMP") or os.getenv("TEMP") or "/tmp"
  _G.CGILUA_ISDIRECT = true

local res = response.new()

_G.SAPI = {
    Info =  {
      _COPYRIGHT = "Copyright (C) 2007 Kepler Project",
      _DESCRIPTION = "WSAPI SAPI implementation",
      _VERSION = "WSAPI SAPI 1.0",
      ispersistent = false,
    },
    Request = {
      servervariable = function (name) return wsapi_env[name] end,
      getpostdata = function (n) return wsapi_env.input:read(n) end
    },
    Response = {
      contenttype = function (header)
        res:content_type(header)
      end,
      errorlog = function (msg, errlevel)
        wsapi_env.error:write (msg)
      end,
      header = function (header, value)
        if res.headers[header] then
          if type(res.headers[header]) == "table" then
            table.insert(res.headers[header], value)
          else
            res.headers[header] = { res.headers[header], value }
          end
        else
          res.headers[header] = value
        end
      end,
      redirect = function (url)
        res.status = 302
        res.headers["Location"] = url
      end,
      write = function (...)
        res:write({...})
      end,
    },
  }
  local cgilua = require "cgilua"
  cgilua.main()
  return res:finish()
end

return _M

至此, thttpd到cgilua的调用流程已经明确。

诚然, cgi运行模式, 为启动子进程处理请求, 对于每一个请求, 都会启动单独的cgi执行,执行完毕退出。

这样会有效率问题, 对于静态资源, 例如纯html和css图片等, 都不应该走cgi程序。

解决此问题的方法:

1、 将cgi脚本的处理 固定在 thttpd进程中处理。(是不是openresty是这种模式?)

2、 使用fastcgi代替。(下阶段研究)

thttpd和cgilua安装与运行流程分析的更多相关文章

  1. 【逆向&编程实战】Metasploit安卓载荷运行流程分析_复现meterpreter模块接管shell

    /QQ:3496925334 作者:MG193.7 CNBLOG博客号:ALDYS4 未经许可,禁止转载/ 关于metasploit的安卓模块,前几次的博客我已经写了相应的分析和工具 [Android ...

  2. Struts2运行流程分析

    一.Struts2运行流程图: 二.运行流程分析: 1. 请求发送给StrutsPrepareAndExecuteFilter 2.StrutsPrepareAndExecuteFilter询问Act ...

  3. 011-Spring Boot 运行流程分析SpringApplication.run

    一.程序入口 1.1.静态方法 //直接调用run方法 ConfigurableApplicationContext context = SpringApplication.run(App.class ...

  4. hadoop运行流程分析源代码级

    前言: 最近一直在分析hadoop的运行流程,我们查阅了大量的资料,虽然从感性上对这个流程有了一个认识但是我总是感觉对mapreduce的运行还是没有一个全面的认识,所以决定从源代码级别对mapred ...

  5. springmvc的运行流程分析

    前几篇文章对springmvc讲解的很清楚,大家看下,有问题,我们再一起讨论. 其实springmvc最为重要是它的运行流程,接着,我们来分析一下,其运行过程,废话不多说,看图说话: 分析如下: 1, ...

  6. MapReduce运行流程分析

    研究MapReduce已经有一段时间了.起初是从分析WordCount程序开始,后来开始阅读Hadoop源码,自认为已经看清MapReduce的运行流程.现在把自己的理解贴出来,与大家分享,欢迎纠错. ...

  7. SparkSteaming运行流程分析以及CheckPoint操作

    本文主要通过源码来了解SparkStreaming程序从任务生成到任务完成整个执行流程以及中间伴随的checkpoint操作 注:下面源码只贴出跟分析内容有关的代码,其他省略 1 分析流程 应用程序入 ...

  8. 8、Struts2 运行流程分析

    1.流程分析: 请求发送给 StrutsPrepareAndExecuteFilter StrutsPrepareAndExecuteFilter 询问 ActionMapper: 该请求是否是一个 ...

  9. yii框架详解 之 CWebApplication 运行流程分析

    在 程序入口处,index.php 用一句 Yii::createWebApplication($config)->run();  开始了app的运行. 那么,首先查看 CWebApplicat ...

随机推荐

  1. CentOS下强行umount卸载设备

    fuser -cu /usr/local/tomcat7/webapps/dsideal_yy/html/down/ fuser -ck /usr/local/tomcat7/webapps/dsid ...

  2. jQuery 基本过滤选择器注意点

    $(".add_shuxing_ul > li:first")  选择class为add_shuxing_ul 的子级li元素的第一个li $(".add_shux ...

  3. xml学习

    一,数据类型 xmlChar  对char的基本代替,是一个UTF-8编码字符串中的一个字节.如果你的数据使用了其他编码,在使用libxml函数前就必须转换为UTF-8. xmlDoc和xmlDocP ...

  4. Cloudera Manager 5和CDH5离线安装

    CDH (Cloudera’s Distribution, including Apache Hadoop),是Hadoop众多分支中的一种,由Cloudera维护,基于稳定版本的Apache Had ...

  5. Android studio 一个项目中添加两个module遇到的bug

    1.在一个Android studio中,我添加了一个模块,然后就是各种bug 找到到R 是在module 名上面 右键 Make Module '模块名' 经过各种google 的时候发现了 htt ...

  6. JS相关环境搭建:Nodejs、karma测试框架、jsDuck、Express

    第一章:压缩js(nodejs,uglify) 第一步:安装nodejs环境 直接下载http://www.nodejs.org/download/ 下载完成后直接下一步下一步即可,完了我们就具有no ...

  7. Ajax分页js代码

    var pageIndex = 0; var pageSize = 10; $(function () { $("#btnSearch").click(function () { ...

  8. 点击页面其它地方隐藏div所想到的jQuery的delegate

    在网页开发的过程中经常遇到的一个需求就是点击一div内部做某些操作,而点击页面其它地方隐藏该div.比如很多导航菜单,当菜单展开的时候,就会要求点击页面其它非菜单地方,隐藏该菜单. 先从最简单的开始, ...

  9. 兼容所有浏览器的设为首页收藏本站js代码

    大家发现传统的收藏本站按钮在360浏览器下面没有效果了,但是360浏览器用户群却非常之大.所以我们在网上找到一个兼容所有浏览器的收藏本站解决方案,具体功能如下: 设为首页 和 收藏本站js代码 兼容I ...

  10. jQuery -> 获取元素的各种过滤器

    转自http://blog.csdn.net/feelang/article/details/26613023