浅析syslog()库函数执行流程
/usr/include/syslog.h
==> #include
/usr/include/sys/syslog.h
==> #include
/usr/include/bits/syslog-path.h
==> #define _PATH_LOG "/dev/log"
luther@gliethttp:~$ ll /dev/log
srw-rw-rw- 1 root root 0 2009-06-28 12:06 /dev/log
编写一个2行的测试小程序
luther@gliethttp:/vobs/tmp$ vim syslog.c
#include
int main(void)
{
openlog ("luther.gliethttp", LOG_PID, LOG_DAEMON); // 使用unistd libc库
syslog(LOG_EMERG, "hello . gliethttp \n");
return 0;
}
luther@gliethttp:/vobs/tmp$ gcc -o mysyslog syslog.c
luther@gliethttp:/vobs/tmp$ ./mysyslog // 执行syslog库发送函数
luther@gliethttp:/vobs/tmp$ tail -n 5 /var/log/syslog // 查看log是否打进去了
Jun 28 13:17:01 gliethttp /USR/SBIN/CRON[12934]: (root) CMD ( cd / && run-parts --report /etc/cron.hourly)
Jun 28 13:46:43 gliethttp -- MARK --
Jun 28 14:06:43 gliethttp -- MARK --
Jun 28 14:17:01 gliethttp /USR/SBIN/CRON[32578]: (root) CMD ( cd / && run-parts --report /etc/cron.hourly)
Jun 28 14:45:58 gliethttp luther.gliethttp[809]: hello . gliethttp // 这就是我们发的log信息,对了吧,呵呵:)
接下来让我们看看lib库是怎么连接"/dev/log"这个unix文件的,
static struct syslog_data sdata = SYSLOG_DATA_INIT; // lib库被联编加载到内存后,全局量sdata的初始值
void
openlog(const char *ident, int logstat, int logfac)
{
openlog_r(ident, logstat, logfac, &sdata);
}
void
openlog_r(const char *ident, int logstat, int logfac, struct syslog_data *data)
{
if (ident != NULL)
data->log_tag = ident; // 追加到log之前的tag字符串
data->log_stat = logstat;
if (logfac != 0 && (logfac &~ LOG_FACMASK) == 0)
data->log_fac = logfac;
if (data->log_stat & LOG_NDELAY) /* open immediately */
connectlog_r(data); // 与/dev/log建立连接
data->opened = 1; /* ident and facility has been set */
}
static void
connectlog_r(struct syslog_data *data)
{
union {
struct sockaddr syslogAddr;
struct sockaddr_un syslogAddrUn;
} u;
#define SyslogAddr u.syslogAddrUn
if (data->log_file == -1) {
// 创建unix通道
if ((data->log_file = socket(AF_UNIX, SOCK_DGRAM, 0)) == -1)
return;
(void)fcntl(data->log_file, F_SETFD, 1);
}
if (data->log_file != -1 && !data->connected) {
memset(&SyslogAddr, '\0', sizeof(SyslogAddr));
#if 0
/* BIONIC: no sun_len field to fill on Linux */
SyslogAddr.sun_len = sizeof(SyslogAddr);
#endif
SyslogAddr.sun_family = AF_UNIX;
// 与地址_PATH_LOG绑定,/usr/include/bits/syslog-path.h已经定义了该值为:"/dev/log"
strlcpy(SyslogAddr.sun_path, _PATH_LOG,
sizeof(SyslogAddr.sun_path));
// 与监听"/dev/log"的作为server的syslogd daemon程序建立连接.
if (connect(data->log_file, &u.syslogAddr,
sizeof(SyslogAddr)) == -1) {
(void)close(data->log_file);
data->log_file = -1;
} else
data->connected = 1; // ok,成功.
}
}
上面于daemon程序syslogd建立连接了,接下来,syslog(LOG_EMERG, "hello . gliethttp \n");传数据过去.
void
syslog(int pri, const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
vsyslog(pri, fmt, ap);
va_end(ap);
}
void
vsyslog(int pri, const char *fmt, va_list ap)
{
vsyslog_r(pri, &sdata, fmt, ap);
}
void
vsyslog_r(int pri, struct syslog_data *data, const char *fmt, va_list ap)
{
int cnt;
char ch, *p, *t;
time_t now;
int fd, saved_errno, error;
#define TBUF_LEN 2048
#define FMT_LEN 1024
char *stdp = NULL, tbuf[TBUF_LEN], fmt_cpy[FMT_LEN];
int tbuf_left, fmt_left, prlen;
#define INTERNALLOG LOG_ERR|LOG_CONS|LOG_PERROR|LOG_PID
/* Check for invalid bits. */
if (pri & ~(LOG_PRIMASK|LOG_FACMASK)) {
// log数据级别非法
if (data == &sdata) {
syslog(INTERNALLOG,
"syslog: unknown facility/priority: %x", pri);
} else {
syslog_r(INTERNALLOG, data,
"syslog_r: unknown facility/priority: %x", pri);
}
pri &= LOG_PRIMASK|LOG_FACMASK;
}
/* Check priority against setlogmask values. */
if (!(LOG_MASK(LOG_PRI(pri)) & data->log_mask))
return;
saved_errno = errno; // 将lib库的错误号保存起来先
/* Set default facility if none specified. */
if ((pri & LOG_FACMASK) == 0)
pri |= data->log_fac;
/* If we have been called through syslog(), no need for reentrancy. */
if (data == &sdata)
(void)time(&now); // 读取时间log信息
p = tbuf;
tbuf_left = TBUF_LEN;
#define DEC() \
do { \
if (prlen < 0) \
prlen = 0; \
if (prlen >= tbuf_left) \
prlen = tbuf_left - 1; \
p += prlen; \
tbuf_left -= prlen; \
} while (0)
prlen = snprintf(p, tbuf_left, "<%d>", pri); // 首先追加pri作为log数据头,syslogd会提取该pri,接下来追加的内容
// 才是syslod真正输送到/var/log/syslog文件的log字符串[luther.gliethttp]
DEC();
/*
* syslogd will expand time automagically for reentrant case, and
* for normal case, just do like before
*/
if (data == &sdata) {
prlen = strftime(p, tbuf_left, "%h %e %T ", localtime(&now)); // 追加时间
DEC();
}
if (data->log_stat & LOG_PERROR)
stdp = p;
if (data->log_tag == NULL)
data->log_tag = __progname;
if (data->log_tag != NULL) {
prlen = snprintf(p, tbuf_left, "%s", data->log_tag); // 追加tag字符串"luther.gliethttp"
DEC();
}
if (data->log_stat & LOG_PID) {
prlen = snprintf(p, tbuf_left, "[%ld]", (long)getpid()); // 追加我们这个小程序的pid
DEC();
}
if (data->log_tag != NULL) {
if (tbuf_left > 1) {
*p++ = ':'; // 追加':'
tbuf_left--;
}
if (tbuf_left > 1) {
*p++ = ' '; // 追加' '空格
tbuf_left--;
}
}
/* strerror() is not reentrant */
// 追加我们应用程序发送的字符串,这里就是简单的字符串"hello . gliethttp \n"
for (t = fmt_cpy, fmt_left = FMT_LEN; (ch = *fmt); ++fmt) {
if (ch == '%' && fmt[1] == 'm') {
++fmt;
if (data == &sdata) {
prlen = snprintf(t, fmt_left, "%s",
strerror(saved_errno));
} else {
prlen = snprintf(t, fmt_left, "Error %d",
saved_errno);
}
if (prlen < 0)
prlen = 0;
if (prlen >= fmt_left)
prlen = fmt_left - 1;
t += prlen;
fmt_left -= prlen;
} else if (ch == '%' && fmt[1] == '%' && fmt_left > 2) {
*t++ = '%';
*t++ = '%';
fmt++;
fmt_left -= 2;
} else {
if (fmt_left > 1) {
*t++ = ch;
fmt_left--;
}
}
}
*t = '\0'; // 追加0结尾
prlen = vsnprintf(p, tbuf_left, fmt_cpy, ap);
DEC();
cnt = p - tbuf; // 数据长度
/* Output to stderr if requested. */
if (data->log_stat & LOG_PERROR) { // 将发往syslod的数据,在stderr上同时也打印一份出来.
struct iovec iov[2];
iov[0].iov_base = stdp;
iov[0].iov_len = cnt - (stdp - tbuf);
iov[1].iov_base = "\n"; // 追加一个回车
iov[1].iov_len = 1;
(void)writev(STDERR_FILENO, iov, 2); // 阻塞等待数据全部传送完毕
}
/* Get connected, output the message to the local logger. */
if (!data->opened) // 如果还没有opened,那么打开
openlog_r(data->log_tag, data->log_stat, 0, data);
connectlog_r(data); // 连接,如果之前没有连接过,那么将执行真正的连接,否则什么也不做[luther.gliethtp]
/*
* If the send() failed, there are two likely scenarios:
* 1) syslogd was restarted
* 2) /dev/log is out of socket buffer space
* We attempt to reconnect to /dev/log to take care of
* case #1 and keep send()ing data to cover case #2
* to give syslogd a chance to empty its socket buffer.
*/
if ((error = send(data->log_file, tbuf, cnt, 0)) < 0) { // 发送给"/dev/log"这个unix文件,daemon程序syslogd将收到数据.
if (errno != ENOBUFS) {
disconnectlog_r(data);
connectlog_r(data);
}
do {
usleep(1);
if ((error = send(data->log_file, tbuf, cnt, 0)) >= 0)
break;
} while (errno == ENOBUFS);
}
/*
* Output the message to the console; try not to block
* as a blocking console should not stop other processes.
* Make sure the error reported is the one from the syslogd failure.
*/
// 库中定义#define _PATH_CONSOLE "/dev/console"
// 但是没有地方能够出现log显示
// 倒是:
// root@gliethttp:~# ps
// PID TTY TIME CMD
// 1490 pts/5 00:00:00 su
// 1496 pts/5 00:00:00 bash
// 2266 pts/5 00:00:00 ps
// root@gliethttp:~# echo 'aaaaaa' >/dev/pts/5
// aaaaaa
// root@gliethttp:~# echo 'aaaaaa' >/dev/tty0
// root@gliethttp:~# echo 'aaaaaa' >/dev/console
if (error == -1 && (data->log_stat & LOG_CONS) &&
(fd = open(_PATH_CONSOLE, O_WRONLY|O_NONBLOCK, 0)) >= 0) {
struct iovec iov[2];
p = strchr(tbuf, '>') + 1;
iov[0].iov_base = p;
iov[0].iov_len = cnt - (p - tbuf);
iov[1].iov_base = "\r\n";
iov[1].iov_len = 2;
(void)writev(fd, iov, 2);
(void)close(fd);
}
if (data != &sdata)
closelog_r(data);
}
阅读(1929) | 评论(0) | 转发(0) |