syslogシステムコール(ログ参照)
Rev.1を表示中。最新版はこちら。
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]# 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);
}





