BOA代码笔记 1

开头废几句话


Boa不多介绍了,wiki上说的比我好。

之所以写这么个东西

1 因为兴趣,想看看别人源代码。

2 看linux0.11版源代码看到字符设备、文件系统的地方比较费劲,又不想跳过……憋了一肚子热情先发泄在boa上吧!

3 正在做的某项目弄的博主头昏脑涨,不妨换换思路


与Unix网络编程相关的,基于linux c编程,不涉及图形界面的,代码量很小。

 --------------------------------------------------------------------------------------------------------------------------------------------------

这只是本人的笔记,并没想完整的分析整个boa架构,虽然也许最后会总结一下。

因此不能面面俱到,up主还只是刚开学的大三学生,以学习简单的linux下编程技巧、编程经验为主,高手觉着理解肤浅或者不对的话请尽情指出。

鸣谢强大的google、百度!

--------------------------------------------------------------------------------------------------------------------------------------------------

Makefile


忘了哪本书上说,看项目先看Makefile。

不过一般的开源项目Makefile还真是看不了。回想其他的大部分开源代码,那些自动生成的Makefile。。。可真是受不了。。。我一直就怀疑那些是是不是人写的,后来知道还真不是。

boa的Makefile写的相当小,一共不到100行,但是常见的基本框架和风格还是有的,可以借鉴一下用在自己的Makefile里。

因为极其短,我就贴一下吧。

[plain]  view plain copy print ?
  1. # Generated automatically from Makefile.in by configure.  
  2. # $Id: Makefile.in,v 1.59 2002/03/24 22:20:19 jnelson Exp $  
  3. # 说实话下边这两个.SUFIXES我不知道啥意思。.PHONY是定义为伪目标。  
  4. .SUFFIXES:  
  5. .SUFFIXES: .o .c  
  6. .PHONY: clean mrclean distclean depend all dist  
  7. # 这么多的FLAGS我就只见过-Wall = =。而且整个Makefile我只看到这一处定义了GCC_FLAGS,没看到使用,是make隐含调用的么?  
  8. GCC_FLAGS = -Wstrict-prototypes -Wpointer-arith -Wcast-align -Wcast-qual\  
  9.   -Wtraditional\  
  10.   -Wshadow\  
  11.   -Wconversion\  
  12.   -Waggregate-return\  
  13.   -Wmissing-prototypes\  
  14.   -Wnested-externs\  
  15.   -Wall \  
  16.   -Wundef -Wwrite-strings -Wredundant-decls -Winline  
  17.   
  18.   
  19. srcdir = .  
  20. VPATH = .:./../extras  
  21. LDFLAGS =  -g  
  22. LIBS =    
  23. CFLAGS = -g -O2 -pipe -Wall -I.  
  24.   
  25. # Change these if necessary  
  26.   
  27. YACC = bison -y   
  28. LEX = lex   
  29. CC = gcc   
  30. CPP = gcc -E  
  31.   
  32. SOURCES = alias.c boa.c buffer.c cgi.c cgi_header.c config.c escape.c \  
  33.     get.c hash.c ip.c log.c mmap_cache.c pipe.c queue.c read.c \  
  34.     request.c response.c select.c signals.c util.c sublog.c  
  35.   
  36. OBJS = y.tab.o lex.yy.o $(SOURCES:.c=.o) timestamp.o  # 这个$(SOURCES:.c=.o)用法可以学一下  
  37.   
  38. # 主程序的生成规则。这个boa_indexer不知道啥用,所以我就把它删掉了。  
  39. all:    boa boa_indexer  
  40.   
  41. boa:    $(OBJS)  
  42.     $(CC) -o $@ $^ $(LDFLAGS) $(LIBS)  
  43.       
  44. # 关于lex和 yacc的生成规则  
  45. # parser dependencies  
  46.   
  47. y.tab.c y.tab.h:    boa_grammar.y  
  48.     $(YACC) -d $^  
  49.   
  50. lex.yy.c:   boa_lexer.l  
  51.     $(LEX) $^  
  52. # 好象是生成依赖关系 使用  
  53. # depend stuff  
  54. .depend:  
  55.     $(CPP) -MM $(SOURCES) > .depend  
  56.           
  57. depend:  
  58.     -rm -f .depend  
  59.     $(MAKE) .depend  
  60.           
  61. 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主写的一些小程序多),所以也贴一下:

  1. while ((c = getopt(argc, argv, "c:r:d")) != -1) {  
  2.         switch (c) {  
  3.         case 'c':  ......  
  4.             break;  
  5.         case 'r':  
  6.             if (chdir(optarg) == -1) {  
  7.                 log_error_time();  
  8.                 perror("chdir (to chroot)");  
  9.                 exit(1);  
  10.             }  
  11.             if (chroot(optarg) == -1) {  
  12.                 log_error_time();  
  13.                 perror("chroot");  
  14.                 exit(1);  
  15.             }  
  16.             if (chdir("/") == -1) {  
  17.                 log_error_time();  
  18.                 perror("chdir (after chroot)");  
  19.                 exit(1);  
  20.             }  
  21.             break;  
  22.         case 'd':  
  23.             do_fork = 0;  
  24.             break;  
  25.         default:  
  26.             fprintf(stderr, "Usage: %s [-c serverroot] [-r chroot] [-d]\n", argv[0]);  
  27.             exit(1);  
  28.         }  
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剩下的代码:

  1. fixup_server_root();  
  2.    read_config_files();  
  3.    open_logs();  
  4.    server_s = create_server_socket();  
  5.    init_signals();  
  6.    drop_privs();  
  7.    create_common_env();  
  8.    build_needs_escape();  
  9.   
  10.    if (max_connections < 1) {  
  11.        struct rlimit rl;  
  12.   
  13.        /* has not been set explicitly */  
  14.        c = getrlimit(RLIMIT_NOFILE, &rl);  
  15.        if (c < 0) {  
  16.            perror("getrlimit");  
  17.            exit(1);  
  18.        }  
  19.        max_connections = rl.rlim_cur;  
  20.    }  
  21.   
  22.    /* background ourself */  
  23.    if (do_fork) {  
  24.        switch(fork()) {  
  25.        case -1:  
  26.            /* error */  
  27.            perror("fork");  
  28.            exit(1);  
  29.            break;  
  30.        case 0:  
  31.            /* child, success */  
  32.            break;  
  33.        default:  
  34.            /* parent, success */  
  35.            exit(0);  
  36.            break;  
  37.        }  
  38.    }  
  39.   
  40.    timestamp();  
  41.   
  42.    status.requests = 0;  
  43.    status.errors = 0;  
  44.   
  45.    start_time = current_time;  
  46.    select_loop(server_s);  
  47.    return 0;  

恩,看上去就这么一点儿。而且基本上看看函数名就知道要做什么了(虽然那个select_loop实际上很复杂)。

一句话,boa真是很适合向我这样刚学完unix c编程和unix网络编程但又向看看开源代码的本科生啊!

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值