开头废几句话
Boa不多介绍了,wiki上说的比我好。
之所以写这么个东西
1 因为兴趣,想看看别人源代码。
2 看linux0.11版源代码看到字符设备、文件系统的地方比较费劲,又不想跳过……憋了一肚子热情先发泄在boa上吧!
3 正在做的某项目弄的博主头昏脑涨,不妨换换思路
与Unix网络编程相关的,基于linux c编程,不涉及图形界面的,代码量很小。
--------------------------------------------------------------------------------------------------------------------------------------------------
这只是本人的笔记,并没想完整的分析整个boa架构,虽然也许最后会总结一下。
因此不能面面俱到,up主还只是刚开学的大三学生,以学习简单的linux下编程技巧、编程经验为主,高手觉着理解肤浅或者不对的话请尽情指出。
鸣谢强大的google、百度!
--------------------------------------------------------------------------------------------------------------------------------------------------
Makefile
忘了哪本书上说,看项目先看Makefile。
不过一般的开源项目Makefile还真是看不了。回想其他的大部分开源代码,那些自动生成的Makefile。。。可真是受不了。。。我一直就怀疑那些是是不是人写的,后来知道还真不是。
boa的Makefile写的相当小,一共不到100行,但是常见的基本框架和风格还是有的,可以借鉴一下用在自己的Makefile里。
因为极其短,我就贴一下吧。
- # Generated automatically from Makefile.in by configure.
- # $Id: Makefile.in,v 1.59 2002/03/24 22:20:19 jnelson Exp $
- # 说实话下边这两个.SUFIXES我不知道啥意思。.PHONY是定义为伪目标。
- .SUFFIXES:
- .SUFFIXES: .o .c
- .PHONY: clean mrclean distclean depend all dist
- # 这么多的FLAGS我就只见过-Wall = =。而且整个Makefile我只看到这一处定义了GCC_FLAGS,没看到使用,是make隐含调用的么?
- GCC_FLAGS = -Wstrict-prototypes -Wpointer-arith -Wcast-align -Wcast-qual\
- -Wtraditional\
- -Wshadow\
- -Wconversion\
- -Waggregate-return\
- -Wmissing-prototypes\
- -Wnested-externs\
- -Wall \
- -Wundef -Wwrite-strings -Wredundant-decls -Winline
-
-
- srcdir = .
- VPATH = .:./../extras
- LDFLAGS = -g
- LIBS =
- CFLAGS = -g -O2 -pipe -Wall -I.
-
- # Change these if necessary
-
- YACC = bison -y
- LEX = lex
- CC = gcc
- CPP = gcc -E
-
- SOURCES = alias.c boa.c buffer.c cgi.c cgi_header.c config.c escape.c \
- get.c hash.c ip.c log.c mmap_cache.c pipe.c queue.c read.c \
- request.c response.c select.c signals.c util.c sublog.c
-
- OBJS = y.tab.o lex.yy.o $(SOURCES:.c=.o) timestamp.o # 这个$(SOURCES:.c=.o)用法可以学一下
-
- # 主程序的生成规则。这个boa_indexer不知道啥用,所以我就把它删掉了。
- all: boa boa_indexer
-
- boa: $(OBJS)
- $(CC) -o $@ $^ $(LDFLAGS) $(LIBS)
-
- # 关于lex和 yacc的生成规则
- # parser dependencies
-
- y.tab.c y.tab.h: boa_grammar.y
- $(YACC) -d $^
-
- lex.yy.c: boa_lexer.l
- $(LEX) $^
- # 好象是生成依赖关系 使用
- # depend stuff
- .depend:
- $(CPP) -MM $(SOURCES) > .depend
-
- depend:
- -rm -f .depend
- $(MAKE) .depend
-
- include .depend
虽然Makefile本身已经非常小,不过我还是删掉了一些东西。只剩下主程序的生成规则,词法语法分析的生成规则,自动生成依赖关系的规则。
我这种只看过《跟我一起写Makefile》不到一半还忘掉一半的基本看懂了。没学过Makefile的同学可以马上学起。
boa_lexer.l
因为up主这两天刚学了flex和bison怎么用,所以先从这个lex看起。
关于这个词法分析的文件得到的TOKEN怎么与boa相互作用还不知道。从文件本身,能得到如下信息:
这个文件的主要功能是:依次解析boa.conf和mime.types里的配置。
由defines.h注释知,boa.conf位于defines.h中指定的SERVER_ROOT/conf文件夹下。SERVER_ROOT可以通过-c指定。
mime.types由boa.conf指定,默认在/etc/mime.types下。
文件中有一个全局变量file,file变量为0时,解析boa.conf。为1时,解析mime.types,同时BEGIN MIME。如果大于1了,重新解析boa.conf,BEGIN INITIAL,并file设为0。
重新定义了yyerror()函数,用于语法解析过程中的错误处理。
具体的规则也是非常基础简单,我这样没写过几个lex文件只看过1/4教程的也依然能看懂。现在先不逐条解释,等到以后分析程序的时候看看是否重要。
boa.c
从main说起
/* set umask to u+rw, u-x, go-rwx */
c = umask(~0600);
先设置umask。以0开头的是8进制,提醒一下。调用umask时,直接~,这样文件最终的权限就是0600,要是我可能先傻傻的纸上算好0600的反码然后调用。
新建文件的权限变为u+rw,其他权限均没有。
在处理错误时,代码使用了DIE("can't open /dev/null");
宏定义为#define DIE(mesg) log_error_mesg(__FILE__, __LINE__, mesg), exit(1)
而log_error_mesg声明为void log_error_mesg(char *file, int line, char *mesg)的普通函数。
关于这个__FILE__, __LINE__ google了一下有如下解释:
Q: What are '__FILE__' and '__LINE__'?
A: '__FILE__' and '__LINE__' are predefined macros and part of the C/C++ standard. During preprocessing, they are replaced respectively by a constant string holding the current file name and by a integer representing the current line number.
There are other preprocessor variables including:
'__DATE__' -> a string literal of the form "Mmm dd yyyy"
'__TIME__' -> a string literal of the form "hh:mm:ss"
'__TIMESTAMP__' -> a string literal of the form "Mmm dd yyyy hh:mm:ss"
'__FUNCTION__' -> a string literal which contains the function name (this is part of C99, the new C standard and not all C++ compilers support it)
可见这些标识是预定义的宏,属于C/C++标准。很方便啊!!
处理程序选项时使用了getopt,由于选项相当少(只有3个,甚至还不如up主写的一些小程序多),所以也贴一下:
- while ((c = getopt(argc, argv, "c:r:d")) != -1) {
- switch (c) {
- case 'c': ......
- 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);
- }
case 'c'里的选项被我省略了。
关于getopt()的使用可能不少老老实实上课的同学也没见过,不妨自己man一下,或者百度百科。这还是一个挺好用的处理选项的函数。
关键在于case ‘r'的处理:先chdir(optarg),然后chroot(optarg),再chdir("/")。这里,我google了一下为什么要这么调用。
发现一个网页https://buildsecurityin.us-cert.gov/bsi/articles/knowledge/coding/711-BSI.html
这个网页不仅介绍了为什么chroot()后要有一个chdir("/"),还给出了很多的安全编程习惯。
在这里,我的理解是,chroot()不直接改变当前路径,而只是给出一个限制。需要用chdir("/")来切换到之前设定的路径,不chdir的话,路径还是在原来位置。
至于boa源代码里第一个chdir()的调用,我认为就是为了保险吧。(应该不是必须的,也许有些系统实现只允许chroot()设置的路径在当前路径外?)
这次就先到这儿,放松一下,等啥时候那个程序做的又郁闷的时候再继续。
我们先看看main剩下的代码:
- 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;
-
-
- c = getrlimit(RLIMIT_NOFILE, &rl);
- if (c < 0) {
- perror("getrlimit");
- exit(1);
- }
- max_connections = rl.rlim_cur;
- }
-
-
- if (do_fork) {
- switch(fork()) {
- case -1:
-
- perror("fork");
- exit(1);
- break;
- case 0:
-
- break;
- default:
-
- exit(0);
- break;
- }
- }
-
- timestamp();
-
- status.requests = 0;
- status.errors = 0;
-
- start_time = current_time;
- select_loop(server_s);
- return 0;
恩,看上去就这么一点儿。而且基本上看看函数名就知道要做什么了(虽然那个select_loop实际上很复杂)。
一句话,boa真是很适合向我这样刚学完unix c编程和unix网络编程但又向看看开源代码的本科生啊!