boa.c 续
书接上回。
main函数的调用到了 fixup_server_root()
static void fixup_server_root()
{
char *dirbuf;
if (!server_root) {
#ifdef SERVER_ROOT
server_root = strdup(SERVER_ROOT);
if (!server_root) {
perror("strdup (SERVER_ROOT)");
exit(1);
}
#else
fputs("boa: don't know where server root is. Please #define "
"SERVER_ROOT in boa.h\n"
"and recompile, or use the -c command line option to "
"specify it.\n", stderr);
exit(1);
#endif
}
if (chdir(server_root) == -1) {
fprintf(stderr, "Could not chdir to \"%s\": aborting\n",
server_root);
exit(1);
}
dirbuf = normalize_path(server_root);
free(server_root);
server_root = dirbuf;
}
该函数的功能主要是由程序运行时的选项和defines.h里的宏定义确定server_root。
如果参数没有指定server_root,那么就由宏指定,如果宏没指定,就报错。
该函数不贴了,用到了getcwd()
#include <unistd.h>
char *getcwd(char *buf, size_t size);
char *getwd(char *buf);
char *getcwd(char *buf, size_t size);
char *getwd(char *buf);
getcwd函数返回当前程序的工作目录。因此,如果相对路径不是"."的话,还需要将两个字符串连接起来。细节上的处理大家有兴趣的话可以看一下。
fixup_server_root()调用完毕,程序运行到这儿,应该已经有了规范的工作目录了。
main函数继续执行到read_config_files();
程序的注释说,Description: Reads config files via yyparse, then makes sure that all required variables were set properly.
这个函数解析配置文件,保证所有的全局变量正确初始化。
void read_config_files(void)
{
char *temp;
current_uid = getuid();
yyin = fopen("boa.conf", "r");
if (!yyin) {
fputs("Could not open boa.conf for reading.\n", stderr);
exit(1);
}
if (yyparse()) {
fputs("Error parsing config files, exiting\n", stderr);
exit(1);
}
if (!server_name) {
struct hostent *he;
char temp_name[100];
if (gethostname(temp_name, 100) == -1) {
perror("gethostname:");
exit(1);
}
he = gethostbyname(temp_name);
if (he == NULL) {
perror("gethostbyname:");
exit(1);
}
server_name = strdup(he->h_name);
if (server_name == NULL) {
perror("strdup:");
exit(1);
}
}
tempdir = getenv("TMP");
if (tempdir == NULL)
tempdir = "/tmp";
if (single_post_limit < 0) {
fprintf(stderr, "Invalid value for single_post_limit: %d\n",
single_post_limit);
exit(1);
}
if (document_root) {
temp = normalize_path(document_root);
free(document_root);
document_root = temp;
}
if (error_log_name) {
temp = normalize_path(error_log_name);
free(error_log_name);
error_log_name = temp;
}
if (access_log_name) {
temp = normalize_path(access_log_name);
free(access_log_name);
access_log_name = temp;
}
if (cgi_log_name) {
temp = normalize_path(cgi_log_name);
free(cgi_log_name);
cgi_log_name = temp;
}
if (dirmaker) {
temp = normalize_path(dirmaker);
free(dirmaker);
dirmaker = temp;
}
#if 0
if (mime_types) {
temp = normalize_path(mime_types);
free(mime_types);
mime_types = temp;
}
#endif
}
首先打开boa.conf文件,yyin被赋值。然后就可以调用yyparse()进行解析了。
关于flex和bison学习,推荐oreilly的flex&bison。
现在把bison和flex部分研究一下吧
boa_grammar.y
%{
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
/* #include "boa.h" */
#include "parse.h"
# yyerror的实现在boa_lexer.l里
int yyerror(char * msg);
/* yydebug = 1; */
#ifdef DEBUG
#define DBG(x) x
#else
#define DBG(x)
#endif
# 之后可以看到arg1hold用于临时保存TOKEN的参数内容,mime_type也是临时用于存储mime的名。
char *arg1hold;
char mime_type[256]; /* global to inherit */
%}
%union {
char * sval;
int ival;
struct ccommand * cval;
};
/* boa.conf tokens */
%token <cval> STMT_NO_ARGS STMT_ONE_ARG STMT_TWO_ARGS
/* mime.type tokens */
%token <sval> MIMETYPE
%token <sval> STRING
%token <ival> INTEGER
%start ConfigFiles
%%
ConfigFiles: BoaConfigStmts MimeTypeStmts
;
BoaConfigStmts: BoaConfigStmts BoaConfigStmt
| /* empty */
;
BoaConfigStmt:
StmtNoArgs
| StmtOneArg
| StmtTwoArgs
;
StmtNoArgs: STMT_NO_ARGS
{ if ($1->action) {
DBG(printf("StmtNoArgs: %s\n",$1->name);)
$1->action(NULL,NULL,$1->object);
}
}
;
StmtOneArg: STMT_ONE_ARG STRING
{ if ($1->action) {
DBG(printf("StmtOneArg: %s %s\n",$1->name,$2);)
$1->action($2,NULL,$1->object);
}
}
;
StmtTwoArgs: STMT_TWO_ARGS STRING
{ arg1hold = strdup($2); }
STRING
{ if ($1->action) {
DBG(printf("StmtTwoArgs: '%s' '%s' '%s'\n",
$1->name,arg1hold,$4);)
$1->action($4,arg1hold,$1->object);
}
free(arg1hold);
}
;
/******************* mime.types **********************/
MimeTypeStmts: MimeTypeStmts MimeTypeStmt
| /* empty */
;
MimeTypeStmt: MIMETYPE
{ strcpy(mime_type, $1); }
ExtensionList
;
ExtensionList: ExtensionList Extension
| /* empty */
;
Extension: STRING
{ add_mime_type($1, mime_type); }
;
%%
先说说这个宏,DBG(x)
#ifdef DEBUG
#define DBG(x) x
#else
#define DBG(x)
#endif
#define DBG(x) x
#else
#define DBG(x)
#endif
学了一招!
up主之前用#define DEBUG,然后每一处需要调试时调用的函数前后加上#ifdef DEBUG ...... #endif的方法来达到同样效果,太累。
这里只要在需要调试的函数前后加上DBG()就行了。
将boa.conf文件和mime.types文件的语法规则写在一起,简单的先后关系:ConfigFiles: BoaConfigStmts MimeTypeStmts;
BoaConfigStmts
boa config stmt分三种,无参的,一个参数的,两个参数的。
实际的配置如下:
AccessLog /var/log/boa/access_log
Alias /doc /usr/doc
词法分析得到一条stmt后,调用action函数进行操作。
每一个stmt是struct ccommand *类型,声明如下
struct ccommand{
char *name;
int type;
void (*action) (char *, char *, void *);
void *object;
};
char *name;
int type;
void (*action) (char *, char *, void *);
void *object;
};
所有的struct ccommand以数组方式存储
struct ccommand clist[] = {
{"Port", S1A, c_set_int, &server_port},
......
{"CGIPath", S1A, c_set_string, &cgi_path},
{"MaxConnections", S1A, c_set_int, &max_connections},
};
词法分析yylex每次找到一个stmt就根据名字从这个数组中查找对应项返回给yyparse,再从yyparse中调用action,完成各自的初始化。
MimeTypeStmts
mimetype的形式类似于 MIMETYPE STRING STRING STRING...
每次解析到一个MIMETYPE,先暂时存储于mime_type字符数组。解析STRING时,就调用add_mime_type($1, mime_type);
这个函数的作用就是将type-extension添加到一个哈系表中,type为key,可以有很多extension,索引方式存储。
语法分析规则boa_grammar.y就到这儿,词法分析规则boa_lexer.l就不说了。
大体功能和yywrap()、yyerror()在前一篇已经提到,具体的词法规则就太细节了。
好了,回到我们的read_config_files(),大家不会忘了这个函数还没完呢吧?^ ^
其实也没什么了,把一些路径转化为绝对路径,host_name什么的验证一下,也就完了。
最后有个#if 0 ...... #endif
我是第一次到见这个用法,查了一下就是相当于多行注释。有的文章推荐这样用。
好了,up主累了,看会儿动画去,下次更新依然不定期。。。 ^ ^