[BOA] 浅析boa.c中代码 boa-0.94.13
1,下面是完整的boa.c中的main函数代码
int main(int argc, char **argv)
{
int c; /* command line arg */
int server_s; /* boa socket */
/* set umask to u+rw, u-x, go-rwx */
c = umask(~0600);
if (c == -1) {
perror("umask");
exit(1);
}
devnullfd = open("/dev/null", 0);
/* make STDIN and STDOUT point to /dev/null */
if (devnullfd == -1) {
DIE("can't open /dev/null");
}
//【X】把文件标准输入和输出描述符定向为/dev/null
if (dup2(devnullfd, STDIN_FILENO) == -1) {
DIE("can't dup2 /dev/null to STDIN_FILENO");
}
if (dup2(devnullfd, STDOUT_FILENO) == -1) {
DIE("can't dup2 /dev/null to STDOUT_FILENO");
}
//【X】当前时间搓
/* but first, update timestamp, because log_error_time uses it */
(void) time(¤t_time);
//【X】参数解析
while ((c = getopt(argc, argv, "c:r:d")) != -1) {
switch (c) {
case 'c':
if (server_root)
free(server_root);
server_root = strdup(optarg);
if (!server_root) {
perror("strdup (for server_root)");
exit(1);
}
break;
case 'r':
if (chdir(optarg) == -1) {
log_error_time();
perror("chdir (to chroot)");
exit(1);
}
if (chroot(optarg) == -1) {
log_error_time();
perror("chroot");
exit(1);
}
if (chdir("/") == -1) {
log_error_time();
perror("chdir (after chroot)");
exit(1);
}
break;
case 'd':
do_fork = 0;
break;
default:
fprintf(stderr, "Usage: %s [-c serverroot] [-r chroot] [-d]\n", argv[0]);
exit(1);
}
}
fixup_server_root();
read_config_files();
open_logs();
server_s = create_server_socket();
init_signals();
drop_privs();
create_common_env();
build_needs_escape();
if (max_connections < 1) {
struct rlimit rl;
/* has not been set explicitly */
c = getrlimit(RLIMIT_NOFILE, &rl);
if (c < 0) {
perror("getrlimit");
exit(1);
}
max_connections = rl.rlim_cur;
}
/* background ourself */
if (do_fork) {
switch(fork()) {
case -1:
/* error */
perror("fork");
exit(1);
break;
case 0:
/* child, success */
break;
default:
/* parent, success */
exit(0);
break;
}
}
/* main loop */
timestamp();
status.requests = 0;
status.errors = 0;
start_time = current_time;
//网络和http协议的select实现(最主要函数)
select_loop(server_s);
return 0;
}
2,重点函数分析和调用
fixup_server_root() :
把server_root函数设置为绝对路径,调用chdir并把程序的搜索路径切到server_root
内部主要函数有:
normalize_path :把一个相对路径转换为绝对路径 ,通过getcwd函数获取当前的绝对路径,在拼接配置的path,组合
read_config_files(void):
读取配置文件的参数,设置到全家变量中的属性, 内部调用这个函数
if (yyparse()) { //Yacc 词法分析,有boa_grammar.y语法配置,编译时生成代码
fputs("Error parsing config files, exiting\n", stderr);
exit(1);
}主要设置:
server_name(服务名) tempdir(零时目录) single_post_limit (post限制)
document_root (文件根目录) error_log_name access_log_name cgi_log_name dirmaker
open_logs(); //将标准输出从定向到设置的路径或网络,具体实现在open_gen_fd
init_signals(); 通过struct sigaction设置多个信号的处理函数
drop_privs(); 降低当前程序的执行权限
主要逻辑:
if (getuid() == 0) { //降低权限 0表示root,从root用户降低权限为配置的用户名和组
if (setgid(server_gid) == -1) {
DIE("setgid");
}
if (setuid(server_uid) == -1) {
DIE("setuid");
}// 必须先设置组Id,在设置用户Id,具体id值可以查看/etc/passwd
/* test for failed-but-return-was-successful setuid
* http://www.securityportal.com/list-archive/bugtraq/2000/Jun/0101.html
*/
if (setuid(0) != -1) { //这是一个内核bug,这里尝试再次设置为root判断是否成功
DIE("icky Linux kernel bug!");
}}
else { 设置配置的uid和gid
server_gid = getgid();
server_uid = getuid();}
create_common_env(); // 创建一些全家环境变量,如path,...存储在common_cgi_env中
build_needs_escape(); 、、ai解析说是用于处理http需要的字符的预先编码,有点没看懂,附上源码:void build_needs_escape(void) { unsigned int a, b; const unsigned char special[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" "abcdefghijklmnopqrstuvwxyz" "0123456789" "-_.!~*'():@&=+$,/?"; /* 21 Mar 2002 - jnelson - confirm with Apache 1.3.23 that '?' * is safe to leave unescaped. */ unsigned short i, j; b = 1; for (a=0; b!=0; a++) b=b<<1; /* I found $a bit positions available in an unsigned long. */ if (a < NEEDS_ESCAPE_WORD_LENGTH) { fprintf(stderr, "NEEDS_ESCAPE_SHIFT configuration error -- "\ "%d should be <= log2(%d)\n", NEEDS_ESCAPE_SHIFT, a); exit(1); } else if (a >= 2*NEEDS_ESCAPE_WORD_LENGTH) { /* needs_escape_shift configuration suboptimal */ } else { /* Ahh, just right! */; } memset(_needs_escape, ~0, sizeof(_needs_escape)); for(i = 0; i < sizeof(special) - 1; ++i) { j=special[i]; if (j>=NEEDS_ESCAPE_BITS) { /* warning: character $j will be needlessly escaped. */ } else { _needs_escape[NEEDS_ESCAPE_INDEX(j)]&=~NEEDS_ESCAPE_MASK(j); } } }
守护进程的标准写法:
使用fork函数,主进程直接退出,让孩子进程接着执行,不产生僵死进程
/* background ourself */ if (do_fork) { switch(fork()) { case -1: /* error */ perror("fork"); exit(1); break; case 0: /* child, success */ break; default: /* parent, success */ exit(0); break; } }
3,对使用到的一些系统函数解析
1,int dup2(int oldfd, int newfd);
实现:
复制文件描述符,并允许指定新文件描述符的值为newfd
如果newfd打开,则会先关闭,然后将其文件指针指向oldfd
2,int chdir(const char * path);
实现:
改变当前程序的工作路径为path
3,int gethostname(char *name, size_t len);
实现:
获取当前主机的标准主机名称
4,struct hostent *gethostbyname(const char *name);
实现:域名解析函数
struct hostent {
char *h_name; /* official name of host */
char **h_aliases; /* alias list */
int h_addrtype; /* host address type */
int h_length; /* length of address */
char **h_addr_list; /* list of addresses */
}
#define h_addr h_addr_list[0] /* for backward compatibility */
h_name:表示的是主机的规范名
h_aliases: 主机的别名
h_addrtype :IPV4 or IPv6
h_length :主机IP地址长度
h_addr_list :ip地址列表
5, 一个函数的使用说明
/* set the close-on-exec to true */
if (fcntl(STDERR_FILENO, F_SETFD, 1) == -1) {
//如果不设置 close-on-exec ,子进程会继承 stderr 的文件描述符,
DIE("unable to fcntl the error log");
}
当调用exec等函数执行cgi时,关闭stderror输出文件描述符,防止cgi输出到不必要的log文件中
4,总结
boa是一个嵌入式的http服务器,源码可下载,boa-0.94.13,分析了main函数的部分实现,最主要的函数实现select_loop将接下来分解,
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐


所有评论(0)