Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1908694
  • 博文数量: 376
  • 博客积分: 2147
  • 博客等级: 大尉
  • 技术积分: 3642
  • 用 户 组: 普通用户
  • 注册时间: 2012-02-06 10:47
文章分类

全部博文(376)

文章存档

2019年(3)

2017年(28)

2016年(15)

2015年(17)

2014年(182)

2013年(16)

2012年(115)

我的朋友

分类:

2012-03-12 15:29:04

浅析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);
}
阅读(3002) | 评论(0) | 转发(0) |
0

上一篇:telnetd分析

下一篇:linux日志机制syslogd总结

给主人留下些什么吧!~~