全部博文(2759)
分类: C/C++
2013-08-10 09:24:29
原文地址:lighttpd源码阅读笔记(5)----日志系统 作者:loler_zuan
在这部分代码中,我认为存在一个漏洞,后面会解释。
这篇讲解下日志系统。
首先对linux下的syslog做一个简单的介绍。
第一部分介绍syslog:
linux可以配置不同的syslogd程序,比如本机就是rsyslogd。很多日志系统系统的函数都是一样的。
比如syslog()就是写入日志,当你调用这些函数的时候实际就是有你的程序向后台运行的守护进程发送信号,然后处理这些信号,写入日志。写入的位置由配置文件指定
这些syslog系统有着共同的syslog协议。。
很多linux上默认的是syslog,配置文件/etc/syslog.conf。
而ubuntu默认的是rsyslog,配置文件 /etc/rsyslog.conf。阅读rsyslog.conf,会发现/etc/rsyslog.d/中也有部分配置文件。
之前我配置hadoop的时候很多人说查看系统日志就看/var/log/message。但我的pc上找不到这个文件,后来发现问题处在配置文件里,里面并不写入这个文件,可以自己设置。
那么如何设置syslog的配置文件呢。
设置的时候把[消息类型.消息级别]和文件对应。这样该类型消息就发送到该文件。多个类型用”,”隔开
比如:
news.crit /var/log/news/news.crit
auth,authpriv.* /var/log/auth.log
然后需要重新启动日志服务器,才会创建对应的文件并且写入。(测试:将message和syslog设置一样的内容,发现他们都被写入日志记录)
困惑:,和;的区别,auth和auth.*的区别暂不考虑。。。刚开始我猜测,*是不是代表auth采用默认的的优先级?也就是notice。但后来我发现 如果是LOG_AUTH|LOG_DEBUG 也写入了说明猜测错误
调用syslog函数时指定的priority是LOG_USER | LOG_INFO对应的消息类型规则是user.info
系统会根据不同的消息类型和消息优先级写入不同的文件,并做不同的标记。
下面列出了所有的类型和优先级。
syslog消息类型
消息类型 |
消息来源 |
kern |
内核 |
User |
用户程序 |
Damon |
系统守护进程 |
|
电子邮件系统 |
Auth |
与安全权限相关的命令 |
Lpr |
打印机 |
News |
新闻组信息 |
Uucp |
Uucp程序 |
Cron |
记录当前登录的每个用户信息 |
wtmp |
一个用户每次登录进入和退出时间的永久记录 |
Authpriv |
授权信息 |
syslog常用优先级
优先级 |
描述 |
emerg |
最高的紧急程度状态 |
alert |
紧急状态 |
Cirt |
重要信息 |
warning |
警告 |
err |
临界状态 |
notice |
出现不寻常的事情 |
info |
一般性消息 |
Debug |
调试级信息 |
None |
不记录任何日志信息 |
在编写程序时,如果想通过syslog写入日志,那么就要调用syslog。调用openlog是可选择的。如果不调用openlog,则在第一次调用syslog时,自动调用openlog。调用closelog也是可选择的,它只是关闭被用于与syslog守护进程通信的描述符。调用openlog 使我们可以指定一个ident,以后, 此ident 将被加至每则记录消息中。ident 一般是程序的名称(例如 ,cron ,ine 等)
第二部分:log.c内容解析
Lighttpd共提供了四种记录日志的方式:输出到标准错误、输出在用户设置的日志文件,记录在操作系统日志文件,还有管道输出。
在base.h中
enum { ERRORLOG_FILE, ERRORLOG_FD, ERRORLOG_SYSLOG, ERRORLOG_PIPE } errorlog_mode;
可以看出四种方式
1.openDevNull(int fd)//关闭 fd,将fd重定向到/dev/null
2.open_logfile_or_pipe(server *srv, const char* logfile)
如果是文件,以O_APPEND | O_WRONLY | O_CREAT | O_LARGEFILE,方式打开,返回文件描述符
如果是管道,会以“| 程序名”作为参数,所以先建立管道,在子进程中关闭标准输入,把管道输入作为标准输入,然后执行”程序名” (execl("/bin/sh", "sh", "-c", logfile + 1, NULL
返回父进程的输出fd。这样可以方便的输出到管道中。
fcntl(srv->errorlog_fd, F_SETFD, FD_CLOEXEC);
设置文件的CLOEXEC状态,即exec()后,该文件描述符也不关闭。
这样execl后才可以继续使用改变过的标准输入
3.log_error_open(server *srv)
首先openlog打开系统记录。因为即使这里不是用syslog,后面的一些插件模块也会使用,打开总是没错的。
下面是这个函数流程图。我认为这里的一句srv->errorlog_fd = dup(STDERR_FILENO);写错了,这句话在180行。我个人理解,这个文件是在转化为守护进程时有breakage_log指定的文件替代标准错误输出,尤其是在将日志写入标准错误输出的时候,由于失去控制终端,所以可以写入该文件。
实现这一目的的操作有两步
srv->errorlog_fd = dup(STDERR_FILENO);
dup2(breakage_fd, STDERR_FILENO)
如果是这样,没什么用 srv->errorlog_fd还是无法正确输出,而定向到breakage_age的STDERR_FILENO却不会被显式调用。
所以,应该是这样,errorlog_fd也指向了breakage_fd,可以写入文件
srv->errorlog_fd = STDERR_FILENO;
dup2(breakage_fd, STDERR_FILENO)
log_error_close(server *srv)
关闭日志系统。
这里breakagelog_file成员不知道是保存什么信息,先不管,往后看
4.log_error_cycle()
重新打开日志文件。如果打开失败,打开到SYSLOG。应该是用来更换日志文件时候用的。比如更新配置文件,不重启,调用这样一个函数就可以更新配置文件写入位置。
5.log_error_write() 写入日志。采用变参数函数的方法,这个函数没什么难度,很好懂。
知识点一:一个进程可以打开一个文件多次,而且可以是不同状态。
如果,文件不存在,第一个调用O_CREAT|O_EXCL
那么第二个打开失败。
如果文件存在,第一个调用失败。
打开多次,就对应多个文件使用环境,比如文件读取位置也不同。
知识点二:__TIME__当前时间的字符串
__FILE__ 当前文件名的字符串
__DATE__ 当前日期字符串
__LINE__ 调用行的行数,整数
以上都是C标准里定义的宏
知识点三:变参函数编写