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 262144READ_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 loginsmod 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); }