syslogシステムコール(ログ参照)


printk()はシステムログをリングバッファのlog_bufにlog_endを書込みインデックスとし、log_end++としながら設定します。log_startはlog_bufを読み込む時のインデックスで、log_start++としながら読込みます。書込み済サイズはlog_end++毎にlogged_chars++とします。logged_chars=0ならログを有さない事になります。
#define CONFIG_LOG_BUF_SHIFT 18
#define __LOG_BUF_LEN   (1 << CONFIG_LOG_BUF_SHIFT)

static char __log_buf[__LOG_BUF_LEN];
static char *log_buf = __log_buf;
static int log_buf_len = __LOG_BUF_LEN

#define LOG_BUF_MASK (log_buf_len-1)
#define LOG_BUF(idx) (log_buf[(idx) & LOG_BUF_MASK])

static void emit_log_char(char c)
{
       LOG_BUF(log_end) = c;
       log_end++;
       if (log_end - log_start > log_buf_len)
               log_start = log_end - log_buf_len;
       if (log_end - con_start > log_buf_len)
               con_start = log_end - log_buf_len;
       if (logged_chars < log_buf_len)
               logged_chars++;
}
検証サンプル
[root@localhost c]# cat syslog.c
#include <sys/syscall.h>
#include <stdio.h>
#include <sys/syslog.h>

#define SYSLOG_ACTION_CLOSE          0
#define SYSLOG_ACTION_OPEN           1
#define SYSLOG_ACTION_READ           2
#define SYSLOG_ACTION_READ_ALL       3
#define SYSLOG_ACTION_READ_CLEAR     4
#define SYSLOG_ACTION_CLEAR          5
#define SYSLOG_ACTION_CONSOLE_OFF    6
#define SYSLOG_ACTION_CONSOLE_ON     7
#define SYSLOG_ACTION_CONSOLE_LEVEL  8
#define SYSLOG_ACTION_SIZE_UNREAD    9
#define SYSLOG_ACTION_SIZE_BUFFER   10

void    main(int argc, char* argv[])
{
       int     len, type = SYSLOG_ACTION_READ, ret = 0;
       char    buff[100];

       if (argc ==2) {
               if (!strcmp(argv[1], "READ")) {
                       type = SYSLOG_ACTION_READ;
               }
               if (!strcmp(argv[1], "READ_ALL")) {
                       type = SYSLOG_ACTION_READ_ALL;
               }
               if (!strcmp(argv[1], "READ_CLEAR")) {
                       type = SYSLOG_ACTION_READ_CLEAR;
               }
               if (!strcmp(argv[1], "SIZE_UNREAD")) {
                       type = SYSLOG_ACTION_SIZE_UNREAD;
                       ret = 1;
               }
               if (!strcmp(argv[1], "SIZE_BUFFER")) {
                       type = SYSLOG_ACTION_SIZE_BUFFER;
                       ret = 1;
               }
       }
       buff[sizeof(buff) - 1] = 0;
       len = syscall(SYS_syslog, type, buff, sizeof(buff) - 1);
       if (!ret) {
               if (len >= 0) {
                       if (len == 0) {
                               printf("no log\n");
                       }
                       else {
                               printf("%s\n", buff);
                       }
               }
               else {
                       printf("err\n");
               }
       }
       else {
               printf("%d\n", len);
       }
}

[root@localhost lkm]# cat setsyslog.c
#include <linux/module.h>

MODULE_LICENSE("GPL");

static int mod_init(void)
{
  int     i;

 for (i = 1; i < 10; i++) {
      printk("log%d", i);
  }
  return -1;
}
module_init(mod_init);
結果
[root@localhost c]# dmesg -c
[root@localhost c]# insmod setsyslog.ko
insmod: error inserting 'setsyslog.ko': -1 Operation not permitted
[root@localhost c]# ./a.out SIZE_UNREAD
0

[root@localhost c]# ./a.out SIZE_BUFFER
262144
READ_ALLはlog_buf[]の終端から、最大logged_charsまでの読み込みサイズ分遡ってログを取得します。dmesgコマンドはSIZE_BUFFERで取得したサイズ長で、READ_ALLする事で実装されています。
[root@localhost c]# ./a.out READ_ALL
] log5
<4>[ 8383.068406] log6
<4>[ 8383.068411] log7
<4>[ 8383.068415] log8
<4>[ 8383.068420] log9

[root@localhost c]# ./a.out READ_CLEAR
] log5
<4>[ 8383.068406] log6
<4>[ 8383.068411] log7
<4>[ 8383.068415] log8
<4>[ 8383.068420] log9

[root@localhost c]# ./a.out READ_ALL
no log
insmod setsyslog.ko後 READすると読めるのではと思いますが、wait_queue_head_t log_waitで待機するプロセスを有し、そのプロセスが読み込んでいるか故かと思います。
[root@localhost c]# insmod setsyslog.ko
insmod: error inserting 'setsyslog.ko': -1 Operation not permitted
[root@localhost c]# ./a.out READ
^C

[root@localhost c]# ./a.out READ &
[1] 2817
[root@localhost c]# insmod setsyslog.ko
<4>[ 8944.896287] log1
<4>[ 8944.896295] log2
<4>[ 8944.896299] log3
<4>[ 8944.896303] log4
<4>[ 89
insmod: error inserting 'setsyslog.ko': -1 Operation not permitted
[1]+  終了 100                ./a.out READ

#define SYSLOG_FROM_CALL 0
#define SYSLOG_FROM_FILE 1

SYSCALL_DEFINE3(syslog, int, type, char __user *, buf, int, len)
{
       return do_syslog(type, buf, len, SYSLOG_FROM_CALL);
}

int do_syslog(int type, char __user *buf, int len, bool from_file)
{
       unsigned i, j, limit, count;
       int do_clear = 0;
       char c;
       int error;

       error = check_syslog_permissions(type, from_file);
       if (error)
               goto out;

       error = security_syslog(type);
       if (error)
               return error;

       switch (type) {
       case SYSLOG_ACTION_CLOSE:       /* Close log */
               break;
       case SYSLOG_ACTION_OPEN:        /* Open log */
               break;
       case SYSLOG_ACTION_READ:        /* Read from log */
               error = -EINVAL;
               if (!buf || len < 0)
                       goto out;
               error = 0;
               if (!len)
                       goto out;
               if (!access_ok(VERIFY_WRITE, buf, len)) {
                       error = -EFAULT;
                       goto out;
               }
               error = wait_event_interruptible(log_wait,
                                                       (log_start - log_end));
               if (error)
                       goto out;
               i = 0;
               raw_spin_lock_irq(&logbuf_lock);
               while (!error && (log_start != log_end) && i < len) {
                       c = LOG_BUF(log_start);
                       log_start++;
                       raw_spin_unlock_irq(&logbuf_lock);
                       error = __put_user(c,buf);
                       buf++;
                       i++;
                       cond_resched();
                       raw_spin_lock_irq(&logbuf_lock);
               }
               raw_spin_unlock_irq(&logbuf_lock);
               if (!error)
                       error = i;
               break;
       case SYSLOG_ACTION_READ_CLEAR:
               do_clear = 1;

       case SYSLOG_ACTION_READ_ALL:
               error = -EINVAL;
               if (!buf || len < 0)
                       goto out;
               error = 0;
               if (!len)
                       goto out;
               if (!access_ok(VERIFY_WRITE, buf, len)) {
                       error = -EFAULT;
                       goto out;
               }
               count = len;
               if (count > log_buf_len)
                       count = log_buf_len;
               raw_spin_lock_irq(&logbuf_lock);
               if (count > logged_chars)
                       count = logged_chars;
               if (do_clear)
                       logged_chars = 0;
               limit = log_end;

               for (i = 0; i < count && !error; i++) {
                       j = limit-1-i;
                       if (j + log_buf_len < log_end)
                               break;
                       c = LOG_BUF(j);
                       raw_spin_unlock_irq(&logbuf_lock);
                       error = __put_user(c,&buf[count-1-i]);
                       cond_resched();
                       raw_spin_lock_irq(&logbuf_lock);
               }
               raw_spin_unlock_irq(&logbuf_lock);
               if (error)
                       break;
               error = i;
               if (i != count) {
                       int offset = count-error;
                       for (i = 0; i < error; i++) {
                               if (__get_user(c,&buf[i+offset]) ||
                                   __put_user(c,&buf[i])) {
                                       error = -EFAULT;
                                       break;
                               }
                               cond_resched();
                       }
               }
               break;
       case SYSLOG_ACTION_CLEAR:
               logged_chars = 0;
               break;
       case SYSLOG_ACTION_CONSOLE_OFF:
               if (saved_console_loglevel == -1)
                       saved_console_loglevel = console_loglevel;
               console_loglevel = minimum_console_loglevel;
               break;
       case SYSLOG_ACTION_CONSOLE_ON:
               if (saved_console_loglevel != -1) {
                       console_loglevel = saved_console_loglevel;
                       saved_console_loglevel = -1;
               }
               break;
       case SYSLOG_ACTION_CONSOLE_LEVEL:
               error = -EINVAL;
               if (len < 1 || len > 8)
                       goto out;
               if (len < minimum_console_loglevel)
                       len = minimum_console_loglevel;
               console_loglevel = len;
               saved_console_loglevel = -1;
               error = 0;
               break;
       case SYSLOG_ACTION_SIZE_UNREAD:
               error = log_end - log_start;
               break;
       case SYSLOG_ACTION_SIZE_BUFFER:
               error = log_buf_len;
               break;
       default:
               error = -EINVAL;
               break;
       }
out:
       return error;
}
SYSLOG_ACTION_CLOSE/SYSLOG_ACTION_OPENは/proc/kmsgのファイル参照可不可を実装するだけの物で、dmesg_restrict=1ならプロセスにCAP_SYSLOGケーパビリィティが設定されていなければ参照できません。syslogシステムコールは、SYSLOG_FROM_CALL=0でコールされ無影響です。
static const struct file_operations proc_kmsg_operations = {
       .read           = kmsg_read,
       .poll           = kmsg_poll,
       .open           = kmsg_open,
       .release        = kmsg_release,
       .llseek         = generic_file_llseek,
};

static int __init proc_kmsg_init(void)
{
       proc_create("kmsg", S_IRUSR, NULL, &proc_kmsg_operations);
       return 0;
}

static int kmsg_open(struct inode * inode, struct file * file)
{
       return do_syslog(SYSLOG_ACTION_OPEN, NULL, 0, SYSLOG_FROM_FILE);
}

#ifdef CONFIG_SECURITY_DMESG_RESTRICT
int dmesg_restrict = 1;
#else
int dmesg_restrict;
#endif

static int check_syslog_permissions(int type, bool from_file)
{
       if (from_file && type != SYSLOG_ACTION_OPEN)
               return 0;

       if (syslog_action_restricted(type)) {
               if (capable(CAP_SYSLOG))
                       return 0;

               if (capable(CAP_SYS_ADMIN)) {
                       printk_once(KERN_WARNING "%s (%d): "
                                "Attempt to access syslog with CAP_SYS_ADMIN "
                                "but no CAP_SYSLOG (deprecated).\n",
                                current->comm, task_pid_nr(current));
                       return 0;
               }
               return -EPERM;
       }
       return 0;
}

static int syslog_action_restricted(int type)
{
       if (dmesg_restrict)
               return 1;
       return type != SYSLOG_ACTION_READ_ALL && type != SYSLOG_ACTION_SIZE_BUFFER;
}

[root@localhost c]# cat /proc/sys/kernel/dmesg_restrict
0

補足

カーネルオプションlog_buf_lenでシスログバッファを拡大する事が可能です。
early_param("log_buf_len", log_buf_len_setup);

static int __init log_buf_len_setup(char *str)
{
       unsigned size = memparse(str, &str);

       if (size)
               size = roundup_pow_of_two(size);
       if (size > log_buf_len)
               new_log_buf_len = size;

       return 0;
}

void __init setup_log_buf(int early)
{
       unsigned long flags;
       unsigned start, dest_idx, offset;
       char *new_log_buf;
       int free;

       if (!new_log_buf_len)
               return;

       if (early) {
               unsigned long mem;

               mem = memblock_alloc(new_log_buf_len, PAGE_SIZE);
               if (!mem)
                       return;
               new_log_buf = __va(mem);
       } else {
               new_log_buf = alloc_bootmem_nopanic(new_log_buf_len);
       }

       if (unlikely(!new_log_buf)) {
               pr_err("log_buf_len: %ld bytes not available\n",
                       new_log_buf_len);
               return;
       }

       raw_spin_lock_irqsave(&logbuf_lock, flags);
       log_buf_len = new_log_buf_len;
       log_buf = new_log_buf;
       new_log_buf_len = 0;
       free = __LOG_BUF_LEN - log_end;

       offset = start = min(con_start, log_start);
       dest_idx = 0;
       while (start != log_end) {
               unsigned log_idx_mask = start & (__LOG_BUF_LEN - 1);

               log_buf[dest_idx] = __log_buf[log_idx_mask];
               start++;
               dest_idx++;
       }
       log_start -= offset;
       con_start -= offset;
       log_end -= offset;
       raw_spin_unlock_irqrestore(&logbuf_lock, flags);

       pr_info("log_buf_len: %d\n", log_buf_len);
       pr_info("early log buf free: %d(%d%%)\n",
               free, (free * 100) / __LOG_BUF_LEN);
}


最終更新 2016/07/24 23:30:16 - north
(2016/07/24 23:14:44 作成)


検索

アクセス数
3676051
最近のコメント
コアダンプファイル - sakaia
list_head構造体 - yocto_no_yomikata
勧告ロックと強制ロック - wataash
LKMからのファイル出力 - 重松 宏昌
kprobe - ななし
ksetの実装 - スーパーコピー
カーネルスレッドとは - ノース
カーネルスレッドとは - nbyst
asmlinkageってなに? - ノース
asmlinkageってなに? - よろしく
Adsense
広告情報が設定されていません。