thttpd和cgilua安装与运行流程分析
安装
参考如下博文安装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.confport=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/shexec '/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 environmentSYNOPSIS
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/shexec '/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 })
endreturn _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 = truelocal 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()
endreturn _M
至此, thttpd到cgilua的调用流程已经明确。
诚然, cgi运行模式, 为启动子进程处理请求, 对于每一个请求, 都会启动单独的cgi执行,执行完毕退出。
这样会有效率问题, 对于静态资源, 例如纯html和css图片等, 都不应该走cgi程序。
解决此问题的方法:
1、 将cgi脚本的处理 固定在 thttpd进程中处理。(是不是openresty是这种模式?)
2、 使用fastcgi代替。(下阶段研究)
thttpd和cgilua安装与运行流程分析的更多相关文章
- 【逆向&编程实战】Metasploit安卓载荷运行流程分析_复现meterpreter模块接管shell
/QQ:3496925334 作者:MG193.7 CNBLOG博客号:ALDYS4 未经许可,禁止转载/ 关于metasploit的安卓模块,前几次的博客我已经写了相应的分析和工具 [Android ...
- Struts2运行流程分析
一.Struts2运行流程图: 二.运行流程分析: 1. 请求发送给StrutsPrepareAndExecuteFilter 2.StrutsPrepareAndExecuteFilter询问Act ...
- 011-Spring Boot 运行流程分析SpringApplication.run
一.程序入口 1.1.静态方法 //直接调用run方法 ConfigurableApplicationContext context = SpringApplication.run(App.class ...
- hadoop运行流程分析源代码级
前言: 最近一直在分析hadoop的运行流程,我们查阅了大量的资料,虽然从感性上对这个流程有了一个认识但是我总是感觉对mapreduce的运行还是没有一个全面的认识,所以决定从源代码级别对mapred ...
- springmvc的运行流程分析
前几篇文章对springmvc讲解的很清楚,大家看下,有问题,我们再一起讨论. 其实springmvc最为重要是它的运行流程,接着,我们来分析一下,其运行过程,废话不多说,看图说话: 分析如下: 1, ...
- MapReduce运行流程分析
研究MapReduce已经有一段时间了.起初是从分析WordCount程序开始,后来开始阅读Hadoop源码,自认为已经看清MapReduce的运行流程.现在把自己的理解贴出来,与大家分享,欢迎纠错. ...
- SparkSteaming运行流程分析以及CheckPoint操作
本文主要通过源码来了解SparkStreaming程序从任务生成到任务完成整个执行流程以及中间伴随的checkpoint操作 注:下面源码只贴出跟分析内容有关的代码,其他省略 1 分析流程 应用程序入 ...
- 8、Struts2 运行流程分析
1.流程分析: 请求发送给 StrutsPrepareAndExecuteFilter StrutsPrepareAndExecuteFilter 询问 ActionMapper: 该请求是否是一个 ...
- yii框架详解 之 CWebApplication 运行流程分析
在 程序入口处,index.php 用一句 Yii::createWebApplication($config)->run(); 开始了app的运行. 那么,首先查看 CWebApplicat ...
随机推荐
- ACM 水池数目
水池数目 时间限制:3000 ms | 内存限制:65535 KB 难度:4 描述 南阳理工学院校园里有一些小河和一些湖泊,现在,我们把它们通一看成水池,假设有一张我们学校的某处的地图,这个地 ...
- [知识点]网络流之Edmond-Karp算法
// 此博文为迁移而来,写于2015年2月2日,不代表本人现在的观点与看法.原始地址:http://blog.sina.com.cn/s/blog_6022c4720102vr12.html ...
- win8.1上wamp环境中利用apache自带ab压力测试工具使用超简单讲解
2015.10.4apache自带ab压力测试工具使用:本地环境:win8.1 wampserver2.5 -Apache-2.4.9-Mysql-5.6.17-php5.5.12-64b 可以参考一 ...
- [iOS-UI]给输入框添加清除按钮的代码
UIButton *clearButton = [self.textField valueForKey:@"_clearButton"]; [clearButton setImag ...
- Emoji表情符号录入MySQL数据库报错
版本一: 1,查看tomcat后台日志,核心报错信息如下: Caused by: java.sql.SQLException: Incorrect string value: '\xF0\x9F\ ...
- windows2008 IIS下配置FTP服务
一.服务器管理器 1.2008的系统使用服务器管理器,选择角色,因为我之前已经开启了IIS服务器角色,所以我现在只要添加角色服务即可,如果你没有开启过的话,直接添加角色即可. 2.选择WEB服务器,打 ...
- 转自大楚网:微软SAPI:让你的软件能说会道
[IT168专稿]“没声音,再好的戏也出不来.”这虽然是一句广告,但是也说出了一个道理,我们所开发的软件,特别是一些多媒体软件,要是能够发 出声音,能说会道,将为我们的软件增添不少光彩.同时,我们面临 ...
- C#中WinForm程序退出方法技巧总结(转)
本文实例总结了C#中WinForm程序退出方法技巧.分享给大家供大家参考.具体分析如下: 在c#中退出WinForm程序包括有很多方法,如:this.Close(); Application.Exit ...
- 终于懂浏览器里面的cookies和session了
在PHP开发中对比起Cookie,session 是存储在服务器端的会话,相对安全,并且不像 Cookie 那样有存储长度限制: (Php.Asp.Jsp)---: cookie(客户端)界面没有刷新 ...
- pdf转word
一.刚需 pdf转word,这个需求肯定是有的.但是大家都知道,pdf是用来排版打印的,所以编辑起来会比较麻烦,所以,大家都会尝试将pdf的内容转成word,然后再进行编辑. 二.方法 1.用offi ...