2009年5月22日 星期五

Deconstruction of Apache Code解構阿帕契程式碼

太久,真的是一個月沒有更新部落格。忙著,也跟著茫了。

目前正在使用Apache Http Server的程式撰寫。我想之後會寫有關於Apache的程式碼內容,希望有這個能力和空閒時間。(忙著活下去,還有時間嗎!好問題~)

先講參考書籍,討論Apache的書籍很少(手指頭的數目之內),中文可以參考下面這本:
Ben Laurie & Peter Laurie原著,郭文生編譯,Apache技術手冊(第三版),台北:歐萊禮,2003。

怎麼開始呢?Deconstruction,解構。應該不稱之為解構主義,一方面我現在不了解何謂解構主義,只是想將Apache的Code猜(拆)解開來看看,這套軟體的運作方式?我們有沒有能力做出這樣的軟體呢?需要哪些能力和人力?如何設計更是大問題!(我的疑問)

Apache的進入點是main.c,在httpd這個專案(Project)裡面,程式碼架構如下:

用到的標頭檔,Apache底層是Apache Portability Runtime(APR),因此會引入許多apr_的header file,而基於APR建置的程式庫則有APR-util,簡稱APU,以及實現iconv()的APR-iconv。另外,http_是關於http protocol的一些處理,也需要因引入。

#include "apr.h" //平台定義
#include "apr_strings.h" //字串處理程式庫
#include "apr_getopt.h" //命令參數
#include "apr_general.h" //混雜的程式庫,未歸類的
#include "apr_lib.h" //通用程式庫
#include "apr_md5.h" //md5的程式庫,屬於APU
#include "apr_time.h" //時間程式庫
#include "apr_version.h" //APR的版本介面
#include "apu_version.h" //APU的版本介面

#define APR_WANT_STDIO
#define APR_WANT_STRFUNC
#include "apr_want.h" //APR對於標準標頭檔支援

#define CORE_PRIVATE
#include "ap_config.h" //定義掛勾hook的標頭檔
#include "httpd.h" //主要基本的定義
#include "http_main.h" //main程式的定義
#include "http_log.h" //記錄檔的定義
#include "http_config.h" //一些設定的定義
#include "http_core.h" //核心模組的定義
#include "http_vhost.h" //虛擬主機的定義
#include "apr_uri.h" //uri的程式庫,屬於APU
#include "util_ebcdic.h" //定義Extended Binary Coded Decimal Interchange Code(EBCDIC)的轉換功能
#include "ap_mpm.h" //定義Multi-Processing Module(MPM),多處理模組
#include "mpm_common.h" //MPM用的程式庫

定義到的函數(function):
static void show_mpm_settings(void)
static void show_compile_settings(void)
static void destroy_and_exit_process(process_rec *process, int process_exit_value)
static process_rec *init_process(int *argc, const char * const * *argv)
static void usage(process_rec *process)

主程式碼:


int main(int argc, const char * const argv[])
{
char c;
int configtestonly = 0;
const char *confname = SERVER_CONFIG_FILE;
const char *def_server_root = HTTPD_ROOT;
const char *temp_error_log = NULL;
const char *error;
process_rec *process;
server_rec *server_conf;
apr_pool_t *pglobal;
apr_pool_t *pconf;
apr_pool_t *plog; /* Pool of log streams, reset _after_ each read of conf */
apr_pool_t *ptemp; /* Pool for temporary config stuff, reset often */
apr_pool_t *pcommands; /* Pool for -D, -C and -c switches */
apr_getopt_t *opt;
apr_status_t rv;
module **mod;
const char *optarg;
APR_OPTIONAL_FN_TYPE(ap_signal_server) *signal_server;

AP_MONCONTROL(0); /* turn off profiling of startup */

process = init_process(&argc, &argv);
pglobal = process->pool;
pconf = process->pconf;
ap_server_argv0 = process->short_name;

#if APR_CHARSET_EBCDIC
if (ap_init_ebcdic(pglobal) != APR_SUCCESS) {
destroy_and_exit_process(process, 1);
}
#endif

apr_pool_create(&pcommands, pglobal);
apr_pool_tag(pcommands, "pcommands");
ap_server_pre_read_config = apr_array_make(pcommands, 1, sizeof(char *));
ap_server_post_read_config = apr_array_make(pcommands, 1, sizeof(char *));
ap_server_config_defines = apr_array_make(pcommands, 1, sizeof(char *));

error = ap_setup_prelinked_modules(process);
if (error) {
ap_log_error(APLOG_MARK, APLOG_STARTUP|APLOG_EMERG, 0, NULL, "%s: %s",
ap_server_argv0, error);
destroy_and_exit_process(process, 1);
}

ap_run_rewrite_args(process);

/* Maintain AP_SERVER_BASEARGS list in http_main.h to allow the MPM
* to safely pass on our args from its rewrite_args() handler.
*/
apr_getopt_init(&opt, pcommands, process->argc, process->argv);

while ((rv = apr_getopt(opt, AP_SERVER_BASEARGS, &c, &optarg))
== APR_SUCCESS) {
char **new;

switch (c) {
case 'c':
new = (char **)apr_array_push(ap_server_post_read_config);
*new = apr_pstrdup(pcommands, optarg);
break;

case 'C':
new = (char **)apr_array_push(ap_server_pre_read_config);
*new = apr_pstrdup(pcommands, optarg);
break;

case 'd':
def_server_root = optarg;
break;

case 'D':
new = (char **)apr_array_push(ap_server_config_defines);
*new = apr_pstrdup(pcommands, optarg);
/* Setting -D DUMP_VHOSTS is equivalent to setting -S */
if (strcmp(optarg, "DUMP_VHOSTS") == 0)
configtestonly = 1;
/* Setting -D DUMP_MODULES is equivalent to setting -M */
if (strcmp(optarg, "DUMP_MODULES") == 0)
configtestonly = 1;
break;

case 'e':
if (strcasecmp(optarg, "emerg") == 0) {
ap_default_loglevel = APLOG_EMERG;
}
else if (strcasecmp(optarg, "alert") == 0) {
ap_default_loglevel = APLOG_ALERT;
}
else if (strcasecmp(optarg, "crit") == 0) {
ap_default_loglevel = APLOG_CRIT;
}
else if (strncasecmp(optarg, "err", 3) == 0) {
ap_default_loglevel = APLOG_ERR;
}
else if (strncasecmp(optarg, "warn", 4) == 0) {
ap_default_loglevel = APLOG_WARNING;
}
else if (strcasecmp(optarg, "notice") == 0) {
ap_default_loglevel = APLOG_NOTICE;
}
else if (strcasecmp(optarg, "info") == 0) {
ap_default_loglevel = APLOG_INFO;
}
else if (strcasecmp(optarg, "debug") == 0) {
ap_default_loglevel = APLOG_DEBUG;
}
else {
usage(process);
}
break;

case 'E':
temp_error_log = apr_pstrdup(process->pool, optarg);
break;

case 'X':
new = (char **)apr_array_push(ap_server_config_defines);
*new = "DEBUG";
break;

case 'f':
confname = optarg;
break;

case 'v':
printf("Server version: %s\n", ap_get_server_description());
printf("Server built: %s\n", ap_get_server_built());
destroy_and_exit_process(process, 0);

case 'V':
show_compile_settings();
destroy_and_exit_process(process, 0);

case 'l':
ap_show_modules();
destroy_and_exit_process(process, 0);

case 'L':
ap_show_directives();
destroy_and_exit_process(process, 0);

case 't':
configtestonly = 1;
break;

case 'S':
configtestonly = 1;
new = (char **)apr_array_push(ap_server_config_defines);
*new = "DUMP_VHOSTS";
break;

case 'M':
configtestonly = 1;
new = (char **)apr_array_push(ap_server_config_defines);
*new = "DUMP_MODULES";
break;

case 'h':
case '?':
usage(process);
}
}

/* bad cmdline option? then we die */
if (rv != APR_EOF || opt->ind < opt->argc) {
usage(process);
}

apr_pool_create(&plog, pglobal);
apr_pool_tag(plog, "plog");
apr_pool_create(&ptemp, pconf);
apr_pool_tag(ptemp, "ptemp");

/* Note that we preflight the config file once
* before reading it _again_ in the main loop.
* This allows things, log files configuration
* for example, to settle down.
*/

ap_server_root = def_server_root;
if (temp_error_log) {
ap_replace_stderr_log(process->pool, temp_error_log);
}
server_conf = ap_read_config(process, ptemp, confname, &ap_conftree);
if (!server_conf) {
destroy_and_exit_process(process, 1);
}

if (ap_run_pre_config(pconf, plog, ptemp) != OK) {
ap_log_error(APLOG_MARK, APLOG_STARTUP |APLOG_ERR, 0,
NULL, "Pre-configuration failed");
destroy_and_exit_process(process, 1);
}

rv = ap_process_config_tree(server_conf, ap_conftree,
process->pconf, ptemp);
if (rv == OK) {
ap_fixup_virtual_hosts(pconf, server_conf);
ap_fini_vhost_config(pconf, server_conf);
apr_hook_sort_all();

if (configtestonly) {
ap_run_test_config(pconf, server_conf);
ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL, "Syntax OK");
destroy_and_exit_process(process, 0);
}
}

signal_server = APR_RETRIEVE_OPTIONAL_FN(ap_signal_server);
if (signal_server) {
int exit_status;

if (signal_server(&exit_status, pconf) != 0) {
destroy_and_exit_process(process, exit_status);
}
}

/* If our config failed, deal with that here. */
if (rv != OK) {
destroy_and_exit_process(process, 1);
}

apr_pool_clear(plog);

if ( ap_run_open_logs(pconf, plog, ptemp, server_conf) != OK) {
ap_log_error(APLOG_MARK, APLOG_STARTUP |APLOG_ERR,
0, NULL, "Unable to open logs");
destroy_and_exit_process(process, 1);
}

if ( ap_run_post_config(pconf, plog, ptemp, server_conf) != OK) {
ap_log_error(APLOG_MARK, APLOG_STARTUP |APLOG_ERR, 0,
NULL, "Configuration Failed");
destroy_and_exit_process(process, 1);
}

apr_pool_destroy(ptemp);

for (;;) {
apr_hook_deregister_all();
apr_pool_clear(pconf);

for (mod = ap_prelinked_modules; *mod != NULL; mod++) {
ap_register_hooks(*mod, pconf);
}

/* This is a hack until we finish the code so that it only reads
* the config file once and just operates on the tree already in
* memory. rbb
*/
ap_conftree = NULL;
apr_pool_create(&ptemp, pconf);
apr_pool_tag(ptemp, "ptemp");
ap_server_root = def_server_root;
server_conf = ap_read_config(process, ptemp, confname, &ap_conftree);
if (!server_conf) {
destroy_and_exit_process(process, 1);
}

if (ap_run_pre_config(pconf, plog, ptemp) != OK) {
ap_log_error(APLOG_MARK, APLOG_STARTUP |APLOG_ERR,
0, NULL, "Pre-configuration failed");
destroy_and_exit_process(process, 1);
}

if (ap_process_config_tree(server_conf, ap_conftree, process->pconf,
ptemp) != OK) {
destroy_and_exit_process(process, 1);
}
ap_fixup_virtual_hosts(pconf, server_conf);
ap_fini_vhost_config(pconf, server_conf);
apr_hook_sort_all();
apr_pool_clear(plog);
if (ap_run_open_logs(pconf, plog, ptemp, server_conf) != OK) {
ap_log_error(APLOG_MARK, APLOG_STARTUP |APLOG_ERR,
0, NULL, "Unable to open logs");
destroy_and_exit_process(process, 1);
}

if (ap_run_post_config(pconf, plog, ptemp, server_conf) != OK) {
ap_log_error(APLOG_MARK, APLOG_STARTUP |APLOG_ERR,
0, NULL, "Configuration Failed");
destroy_and_exit_process(process, 1);
}

apr_pool_destroy(ptemp);
apr_pool_lock(pconf, 1);

ap_run_optional_fn_retrieve();

if (ap_mpm_run(pconf, plog, server_conf))
break;

apr_pool_lock(pconf, 0);
}

apr_pool_lock(pconf, 0);
destroy_and_exit_process(process, 0);

return 0; /* Termination 'ok' */
}


沒有留言:

張貼留言