main 主函数
int main(int argc, char** argv)
{
int i; /* 循环次数 */
char *zPermUser = 0; /* 守护进程的用户权限 */
const char *zPort = 0; /* http server端口 */
int useChrootJail = 1; /* 是否更改root目录 */
struct passwd *pwd = NULL; /* 用户信息 */
/* 记录启动时间 */
gettimeofday(&beginTime, 0);
/* 转换命令行参数 */
while( argc>1 && argv[1][0] == '-')
{
char *z = argv[1]; /* Key */
char *zArg = argc>=3 ? argv[2] : "0"; /* Value */
/* 允许参数前缀 - 或 -- */
if(strcmp(z[0],"-") == 0 && strcmp(z[1],"-"))
z++;
/* 参数处理 */
if(strcmp(z, "-user")==0)
{
zPermUser = zArg; /* 守护进程的用户权限 */
}
else if(strcmp(z,"-root")==0)
{
zRoot = zArg; /* web根目录 */
}
else if (strcmp(z,"-logfile") == 0)
{
zLogFile = zArg; /* 日志文件路径 */
}
else if(strcmp(z,"-max-age") == 0)
{
mxAge = zArg; /* 请求的缓存最大保持时间 */
}
else if(strcmp(z,"-max-cpu") == 0)
{
maxCpu = atoi(zArg); /* 每个进程最大的CPU时间 */
}
else if(strcmp(z,"-https") == 0)
{
useHttps = atoi(zArg); /* 是否使用HTTPS */
zHttp = useHttps ? "https" : "http";
if( useHttps )
{
/* 请求的源地址,初始为空 */
zRemoteAddr = getenv("REMOTE_HOST");
}
}
else if(strcmp(z,"-port") == 0)
{
zPort = zArg; /* 监听端口 */
standalone =1; /* 独立模式 */
}
else if( strcmp(z, "-family")==0 )
{
if( strcmp(zArg, "ipv4")==0 )
{
ipv4Only = 1; /* 仅IPv4 */
}
else if( strcmp(zArg, "ipv6")==0 )
{
ipv6Only = 1; /* 仅IPv6 */
}
else
{
/* 未知IP协议,打印日志 */
Malfunction(500, "unknown IP protocol: [%s]\n", zArg);
}
}
else if( strcmp(z, "-jail")==0 )
{
if( atoi(zArg)==0 )
{
useChrootJail = 0; /* 更改root目录,即重新指定/位置 */
}
}
else if( strcmp(z, "-debug")==0 )
{
if( atoi(zArg) )
{
useTimeout = 0; /* 调试模式,是否设置超时 */
}
}
else if( strcmp(z, "-input")==0 )
{
if( freopen(zArg, "rb", stdin)==0 || stdin==0 )
{
/* 输入文件无法打开 */
Malfunction(501, "cannot open --input file \"%s\"\n", zArg);
}
}
else if( strcmp(z, "-datetest")==0 )
{
TestParseRfc822Date(); /* RFC822格式时间戳测试 */
exit(0);
}
else
{
/* 未知参数 */
Malfunction(510, "unknown argument: [%s]\n", z);
}
argv += 2;
argc -= 2;
}
/* 强制参数检查 */
if(zRoot == 0)
{
if(standalone)
{
zRoot = ".";
}
else
{
Malfunction(520, "no --root specified");
}
}
/* 切换目录 */
if( chdir(zRoot)!=0 )
{
Malfunction(530,"cannot change to directory [%s]", zRoot);
}
/* 获取用户信息 from /etc/passwd */
if( zPermUser )
{
pwd = getpwnam(zPermUser);
}
/* 基于安全因素,更改当前目录为/根目录 */
if( zPermUser && useChrootJail && getuid()==0 )
{
if( chroot(".")<0 )
{
Malfunction(540, /* LOG: chroot() failed */
"unable to create chroot jail");
}
else
{
zRoot = "";
}
}
/* 启动http服务器*/
if( zPort && http_server(zPort, 0) )
{
Malfunction(550, "failed to start server");
}
/**************************************************************/
/* http server有连接请求后,fork子进程,子进程退出http server监听 */
/**************************************************************/
/* 限制进程的资源使用,避免过度导致系统崩溃 */
#ifdef RLIMIT_CPU
if( maxCpu>0 ){
struct rlimit rlim;
rlim.rlim_cur = maxCpu;
rlim.rlim_max = maxCpu;
setrlimit(RLIMIT_CPU, &rlim);
}
#endif
/* 设置用户身份属性,限制资源权限 */
if (zPermUser)
{
if (pwd)
{
if (setgid(pwd->pw_gid))
{
Malfunction(560, "cannot set group-id to %d", pwd->pw_gid);
}
if (setuid(pwd->pw_uid))
{
Malfunction(570, "cannot set user-id to %d", pwd->pw_uid);
}
}
else
{
Malfunction(580, "no such user [%s]", zPermUser);
}
}
if (getuid() == 0)
{
Malfunction(590, "cannot run as root");
}
/* 获取请求的地址信息,主要用于打印日志 */
if (zRemoteAddr == 0)
{
address remoteAddr;
unsigned int size = sizeof(remoteAddr);
char zHost[NI_MAXHOST];
if (getpeername(0, &remoteAddr.sa, &size) >= 0)
{
getnameinfo(&remoteAddr.sa, size, zHost, sizeof(zHost), 0, 0, NI_NUMERICHOST);
zRemoteAddr = StrDup(zHost);
}
}
if (zRemoteAddr != 0 && strncmp(zRemoteAddr, "::ffff:", 7) == 0 && strchr(zRemoteAddr + 7, ':') == 0 && strchr(zRemoteAddr + 7, '.') != 0)
{
zRemoteAddr += 7;
}
/* 处理请求,最大100次,每次15秒超时 */
for (i = 0; i < 100; i++)
{
ProcessOneRequest(0);
}
ProcessOneRequest(1);
exit(0);
return 0;
}
http_server函数
int http_server(const char *zPort, int localOnly)
{
int listener[20]; /* 服务器socket */
int connection; /* socket连接 */
fd_set readfds; /* select的文件描述符 */
address inaddr; /* 远程对端地址 */
socklen_t lenaddr; /* inaddr的长度 */
int child; /* 子进程PID */
int nchildren = 0; /* 子进程数 */
struct timeval delay; /* select超时时间 */
int opt = -1; /* 设置socket复用标志 */
struct addrinfo sHints; /* 地址信息,用于提示 */
struct addrinfo *pAddrs, *p;
int rc; /* 结果返回码 */
int i, n;
int maxFd = -1; /* 最大文件描述符 */
memset(&sHints, 0, sizeof(sHints));
/* 协议族 */
if(ipv4Only)
{
sHints.ai_family = PF_INET;
}
else if(ipv6Only)
{
sHints.ai_family = PF_INET6;
}
else
{
sHints.ai_family = PF_UNSPEC;
}
sHints.ai_socktype = SOCK_STREAM;
sHints.ai_flags = AI_PASSIVE;
sHints.ai_protocol = IPPROTO_IP;
/* 获取地址信息 */
rc = getaddrinfo(localOnly ? "localhost": 0,zPort, &sHints, &pAddrs);
if(rc)
{
fprintf(stderr, "could not get addr info: %s", rc!=EAI_SYSTEM ? gai_strerror(rc) : strerror(errno));
return 1;
}
/* MAX(n) = sizeof listener / sizeof int */
for(n=0, p=pAddrs; n<(int)(sizeof(listener)/sizeof(listener[0])) && p != 0; p=p->ai_next)
{
listener[n] = socket(p->ai_family, p->ai_socktype, p->ai_protocol);
if(listener[n] >= 0)
{
/* 复用,防止异常终止导致socket无法正常使用 */
setsockopt(listener[n], SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
/* IPv6 */
#if defined(IPV6_V6ONLY)
if( p->ai_family==AF_INET6 )
{
int v6only = 1;
setsockopt(listener[n], IPPROTO_IPV6, IPV6_V6ONLY, &v6only, sizeof(v6only));
}
#endif
/* 绑定 */
if(bind(listener[n], p->ai_addr, p->ai_addrlen) < 0)
{
printf("bind failed: %s\n", strerror(errno));
close(listener[n]);
continue;
}
/* 监听 */
if( listen(listener[n], 20) < 0 )
{
printf("listen() failed: %s\n", strerror(errno));
close(listener[n]);
continue;
}
n++;
}
}
if(n == 0)
{
fprintf(stderr, "cannot open any sockets\n");
return 1;
}
while (1)
{
/* 连接短时间过多,子进程创建过快,减慢 */
if(nchildren > MAX_PARALLEL)
{
sleep(nchildren - MAX_PARALLEL);
}
delay.tv_sec = 60;
delay.tv_usec = 0;
FD_ZERO(&readfds);
for(i=0; i<n; i++)
{
assert(listener[0] >= 0 );
FD_SET(listener[i], &readfds); /* 添加监听fd到集合 */
if(listener[i] > maxFd)
maxFd = listener[i];
}
/* select阻塞,检测是否有请求 */
select(maxFd+1, &readfds, 0, 0, &delay);
for(i=0; i<n; i++)
{
if(FD_ISSET(listener[i], &readfds))
{
lenaddr = sizeof(inaddr);
/* 建立连接 */
connection = accept(listener[i], &inaddr.sa, &lenaddr);
if(connection >= 0)
{
/* fork子进程 */
child = fork();
if(child != 0)
{
/* 父进程统计子进程数,关闭连接,该连接由子进程负责 */
if(child>0)
nchildren++;
close(connection);
}
else
{
/* 子进程退出http_server,进入ProcessOneRequest处理请求 */
int nErr =0, fd;
/* 注意区分 标准fclose和系统调用close, 0 1表示输出输入流fd */
close(0);
fd = dup(connection); /* 复制 */
if(fd != 0)
nErr++;
close(1);
fd = dup(connection);
if(fd != 1)
nErr++;
close(connection);
return nErr;
}
}
}
/* 有子进程终结 */
while( (child = waitpid(0, 0, WNOHANG))>0 )
{
nchildren--;
}
}
}
/* 此处不会到达 */
exit(1);
}
总结
- Althttpd的主要三个函数为main函数、http_server函数和ProcessOneRequest函数。
- http_server函数负责监听和fork子进程,子进程产生后退出http_server函数并调用ProcessOneRequest函数处理请求,处理请求中我们忽略掉cgi网关部分,就会发现其实是HTTP的组包收发。
- Althttpd的设计思想别出心裁,对于加深linux的学习很有帮助。