分享
 
 
 

如何阅读源代码(4)

王朝other·作者佚名  2008-05-21
窄屏简体版  字體: |||超大  

这一段代码,读入一行,如果这一行超过了程序允许的最大字符数(则是错误的日志数据纪录),则跳过本行剩下的数据,忽略掉(continue进行下一次循环)。同时把total_bad增加一个。如果没有超过程序允许的最大字符数(则是正确的日志数据纪录),则/* got a record... */

strcpy(tmp_buf, buffer); /* save buffer in case of error */

if (parse_record(buffer)) /* parse the record

*/

将该数据拷贝到一个缓冲区中,然后调用parse_record()进行处理。我们可以同样的推测一下,get_record()是这个程序的一个主要处理部分,分析了日志数据。在parse_record.c中,有此函数,/*********************************************//* PARSE_RECORD - uhhh, you know... *//*********************************************/int parse_record(char *buffer){

/* clear out structure */

memset(&log_rec,0,sizeof(struct log_struct));/*

log_rec.hostname[0]=0;

log_rec.datetime[0]=0;

log_rec.url[0]=0;

log_rec.resp_code=0;

log_rec.xfer_size=0;

log_rec.refer[0]=0;

log_rec.agent[0]=0;

log_rec.srchstr[0]=0;

log_rec.ident[0]=0;*/#ifdef USE_DNS

memset(&log_rec.addr,0,sizeof(struct in_addr));#endif/* call appropriate handler */

switch (log_type)

{

default:

case LOG_CLF: return parse_record_web(buffer); break; /* clf */

case LOG_FTP: return parse_record_ftp(buffer); break; /* ftp */

case LOG_SQUID: return parse_record_squid(buffer); break; /* squid */

}}

可以看到,log_rec是一个全局变量,该函数根据日志文件的类型,分别调用三种不同的分析函数。在webalizer.h中,找到该变量的定义,从结构定义中可以看到,结构定义了一个日志文件所可能包含的所有信息(参考CLF,FTP, SQUID日志文件的格式说明)。/* log record structure */struct log_struct { char hostname[MAXHOST]; /* hostname */

char datetime[29]; /* raw timestamp */

char url[MAXURL]; /* raw request field */

int resp_code; /* response code */

u_long xfer_size; /* xfer size in bytes */#ifdef USE_DNS

struct in_addr addr; /* IP address structure */#endif /* USE_DNS */

char refer[MAXREF]; /* referrer */

char agent[MAXAGENT]; /* user agent (browser) */

char srchstr[MAXSRCH]; /* search string */

char ident[MAXIDENT]; }; /* ident string (user) */extern struct log_struct log_rec;

先看一下一个parser.c用的内部函数,然后再来以parse_record_web()为例子看看这个函数是怎么工作的,parse_record_ftp, parse_record_squid留给读者自己分析作为练习。/*********************************************//* FMT_LOGREC - terminate log fields w/zeros *//*********************************************/void fmt_logrec(char *buffer){

char *cp=buffer;

int q=0,b=0,p=0;while (*cp != '')

{

/* break record up, terminate fields with '' */

switch (*cp)

{

case ' ': if (b || q || p) break; *cp=''; break;

case '"': q^=1; break;

case '[': if (q) break; b++; break;

case ']': if (q) break; if (b>0) b--; break;

case '(': if (q) break; p++; break;

case ')': if (q) break; if (p>0) p--; break;

}

cp++;

}}

从parser.h头文件中就可以看到,这个函数是一个内部函数,这个函数把一行字符串中间的空格字符用''字符(结束字符)来代替,同时考虑了不替换在双引号,方括号,圆括号中间的空格字符以免得将一行数据错误的分隔开了。(请参考WEB日志的文件格式,可以更清楚的理解这一函数)int parse_record_web(char *buffer){

int size;

char *cp1, *cp2, *cpx, *eob, *eos;size = strlen(buffer); /* get length of buffer */

eob = buffer+size; /* calculate end of buffer */

fmt_logrec(buffer); /* seperate fields with 's *//* HOSTNAME */

cp1 = cpx = buffer; cp2=log_rec.hostname;

eos = (cp1+MAXHOST)-1;

if (eos >= eob) eos=eob-1;while ( (*cp1 != '') && (cp1 != eos) ) *cp2++ = *cp1++;

*cp2 = '';

if (*cp1 != '')

{

if (verbose)

{

fprintf(stderr,"%s",msg_big_host);

if (debug_mode) fprintf(stderr,": %s ",cpx);

else fprintf(stderr," ");

}

while (*cp1 != '') cp1++;

}

if (cp1 < eob) cp1++;/* skip next field (ident) */

while ( (*cp1 != '') && (cp1 < eob) ) cp1++;

if (cp1 < eob) cp1++;/* IDENT (authuser) field */

cpx = cp1;

cp2 = log_rec.ident;

eos = (cp1+MAXIDENT-1);

if (eos >= eob) eos=eob-1;while ( (*cp1 != '[') && (cp1 < eos) ) /* remove embeded spaces */

{

if (*cp1=='') *cp1=' ';

*cp2++=*cp1++;

}

*cp2--='';if (cp1 >= eob) return 0;/* check if oversized username */

if (*cp1 != '[')

{

if (verbose)

{

fprintf(stderr,"%s",msg_big_user);

if (debug_mode) fprintf(stderr,": %s ",cpx);

else fprintf(stderr," ");

}

while ( (*cp1 != '[') && (cp1 < eob) ) cp1++;

}/* strip trailing space(s) */

while (*cp2==' ') *cp2--='';/* date/time string */

cpx = cp1;

cp2 = log_rec.datetime;

eos = (cp1+28);

if (eos >= eob) eos=eob-1;while ( (*cp1 != '') && (cp1 != eos) ) *cp2++ = *cp1++;

*cp2 = '';

if (*cp1 != '')

{

if (verbose)

{

fprintf(stderr,"%s",msg_big_date);

if (debug_mode) fprintf(stderr,": %s ",cpx);

else fprintf(stderr," ");

}

while (*cp1 != '') cp1++;

}

if (cp1 < eob) cp1++;/* minimal sanity check on timestamp */

if ( (log_rec.datetime[0] != '[') ||

(log_rec.datetime[3] != '/') ||

(cp1 >= eob)) return 0;/* HTTP request */

cpx = cp1;

cp2 = log_rec.url;

eos = (cp1+MAXURL-1);

if (eos >= eob) eos = eob-1;while ( (*cp1 != '') && (cp1 != eos) ) *cp2++ = *cp1++;

*cp2 = '';

if (*cp1 != '')

{

if (verbose)

{

fprintf(stderr,"%s",msg_big_req);

if (debug_mode) fprintf(stderr,": %s ",cpx);

else fprintf(stderr," ");

}

while (*cp1 != '') cp1++;

}

if (cp1 < eob) cp1++;if ( (log_rec.url[0] != '"') ||

(cp1 >= eob) ) return 0;/* response code */

log_rec.resp_code = atoi(cp1);/* xfer size */

while ( (*cp1 != '') && (cp1 < eob) ) cp1++;

if (cp1 < eob) cp1++;

if (*cp1<'0'||*cp1>'9') log_rec.xfer_size=0;

else log_rec.xfer_size = strtoul(cp1,NULL,10);/* done with CLF record */

if (cp1>=eob) return 1;while ( (*cp1 != '') && (*cp1 != ' ') && (cp1 < eob) ) cp1++;

if (cp1 < eob) cp1++;

/* get referrer if present */

cpx = cp1;

cp2 = log_rec.refer;

eos = (cp1+MAXREF-1);

if (eos >= eob) eos = eob-1;while ( (*cp1 != '') && (*cp1 != ' ') && (cp1 != eos) ) *cp2++ = *cp1++;

*cp2 = '';

if (*cp1 != '')

{

if (verbose)

{

fprintf(stderr,"%s",msg_big_ref);

if (debug_mode) fprintf(stderr,": %s ",cpx);

else fprintf(stderr," ");

}

while (*cp1 != '') cp1++;

}

if (cp1 < eob) cp1++;cpx = cp1;

cp2 = log_rec.agent;

eos = cp1+(MAXAGENT-1);

if (eos >= eob) eos = eob-1;while ( (*cp1 != '') && (cp1 != eos) ) *cp2++ = *cp1++;

*cp2 = '';return 1; /* maybe a valid record, return with TRUE */}

该函数,一次读入一行(其实是一段日志数据中间的一个域,因为该行数据已经被fmt_logrec分开成多行数据了。根据CLF中的定义,检查该数据并将其拷贝到log_rec结构中去,如果检查该数据有效,则返回1。回到主程序, /* convert month name to lowercase */

for (i=4;i<7;i++)

log_rec.datetime[i]=tolower(log_rec.datetime[i]);/* get year/month/day/hour/min/sec values */

for (i=0;i<12;i++)

{

if (strncmp(log_month[i],&log_rec.datetime[4],3)==0)

{ rec_month = i+1; break; }

}rec_year=atoi(&log_rec.datetime[8]); /* get year number (int) */

rec_day =atoi(&log_rec.datetime[1]); /* get day number */

rec_hour=atoi(&log_rec.datetime[13]); /* get hour number */

rec_min =atoi(&log_rec.datetime[16]); /* get minute number */

rec_sec =atoi(&log_rec.datetime[19]); /* get second number */....

在parse_record分析完数据之后,做日期的分析,把日志中的月份等数据转换成机器可读(可理解)的数据,并存入到log_rec中去。if ((i>=12)||(rec_min>59)||(rec_sec>59)||(rec_year<1990))

{

total_bad++; /* if a bad date, bump counter */

if (verbose)

{

fprintf(stderr,"%s: %s [%lu]",

msg_bad_date,log_rec.datetime,total_rec);......

如果日期,时间错误,则把total_bad计数器增加1,并且打印错误信息到标准错误输出。good_rec = 1;/* get current records timestamp (seconds since epoch) */

req_tstamp=cur_tstamp;

rec_tstamp=((jdate(rec_day,rec_month,rec_year)-epoch)*86400)+

(rec_hour*3600)+(rec_min*60)+rec_sec;/* Do we need to check for duplicate records? (incremental mode) */

if (check_dup)

{

/* check if less than/equal to last record processed */

if ( rec_tstamp <= cur_tstamp )

{

/* if it is, assume we have already processed and ignore it */

total_ignore++;

continue;

}

else

{

/* if it isn't.. disable any more checks this run */

check_dup=0;

/* now check if it's a new month */

if (cur_month != rec_month)

{

clear_month();

cur_sec = rec_sec; /* set current counters */

cur_min = rec_min;

cur_hour = rec_hour;

cur_day = rec_day;

cur_month = rec_month;

cur_year = rec_year;

cur_tstamp= rec_tstamp;

f_day=l_day=rec_day; /* reset first and last day */

}

}

}/* check for out of sequence records */

if (rec_tstamp/3600 < cur_tstamp/3600)

{

if (!fold_seq_err && ((rec_tstamp+SLOP_VAL)/3600_tstamp/3600) )

{ total_ignore++; continue; }

else

{

rec_sec = cur_sec; /* if folding sequence */

rec_min = cur_min; /* errors, just make it */

rec_hour = cur_hour; /* look like the last */

rec_day = cur_day; /* good records timestamp */

rec_month = cur_month;

rec_year = cur_year;

rec_tstamp= cur_tstamp;

}

}

cur_tstamp=rec_tstamp; /* update current timestamp */

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
推荐阅读
 
 
 
>>返回首頁<<
 
靜靜地坐在廢墟上,四周的荒凉一望無際,忽然覺得,淒涼也很美
© 2005- 王朝網路 版權所有