为什么printkconsole_loglevel可以低于minimum_console_loglevel?
Why the printk console_loglevel can be lower than minimum_console_loglevel?
我的 Linux
发行版是 RHEL7
,内核版本是 3.10.0
。
形成printk文件,我知道minimum_console_loglevel定义:
- minimum_console_loglevel: minimum (highest) value to which
console_loglevel can be set
查询printk
的当前日志级别:
[root@localhost kernel]# cat /proc/sys/kernel/printk
7 4 1 7
修改当前控制台日志级别:
[root@localhost kernel]# echo 0 > /proc/sys/kernel/printk
[root@localhost kernel]# cat /proc/sys/kernel/printk
0 4 1 7
据我了解,minimum_console_loglevel 是 1
,因此将 console_loglevel 修改为0
应该失败了。但是从 cat 输出来看,似乎成功了。
来自 printk.c
代码:
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;
/* Implicitly re-enable logging to console */
saved_console_loglevel = -1;
error = 0;
break;
我也认为 console_loglevel 值不应该被修改。
您查看的代码有误。 /proc/sys/kernel/printk
由 kernel/sysctl.c
提供。 printk
looks like this 的定义:
{
.procname = "printk",
.data = &console_loglevel,
.maxlen = 4*sizeof(int),
.mode = 0644,
.proc_handler = proc_dointvec,
},
这定义了一个名为 printk
的文件,它控制一个包含 4 个元素的 int 数组。至关重要的是,处理函数 proc_dointvec
是一个对整数数组进行操作的通用函数,并且 它不会验证其参数 .
因此,作为 root,您可以写入任何您想要的垃圾数字 /proc/sys/kernel/printk
,内核会很乐意接受它们。演示:
root@ubuntu:/proc/sys/kernel# echo '-989897 42 -2147483648 0' > printk
root@ubuntu:/proc/sys/kernel# cat printk
-989897 42 -18446744071562067968 0
(第三个实际上是一个有趣的 可能 无害的错误我刚刚发现...)
从printk.h
可以看出,四个日志级别参数直接来自这个四元素数组:
extern int console_printk[];
#define console_loglevel (console_printk[0])
#define default_message_loglevel (console_printk[1])
#define minimum_console_loglevel (console_printk[2])
#define default_console_loglevel (console_printk[3])
那么,那么,您发布的代码有什么作用呢?它实现了 syslog
系统调用,如函数名称 (do_syslog
) 所示。正如 the man page 所述:
SYSLOG_ACTION_CONSOLE_LEVEL (8)
The call sets console_loglevel to the value given in len,
which must be an integer between 1 and 8 (inclusive). The
kernel silently enforces a minimum value of
minimum_console_loglevel for len. See the log level section
for details. The bufp argument is ignored.
我们可以看到 syslog
系统调用还允许您设置控制台日志级别,但它实际上会检查这些值是否有效。让我们用一个简单的测试程序来测试(注意 glibc 调用函数 klogctl
而不是 syslog
,因为 user-space syslog
函数做了其他事情):
#include <sys/klog.h>
#include <stdio.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <stdlib.h>
#define SYSLOG_ACTION_CONSOLE_LEVEL 8
int main(int argc, char **argv) {
if(klogctl(SYSLOG_ACTION_CONSOLE_LEVEL, NULL, atoi(argv[1])) < 0) {
perror("klogctl");
} else {
printf("klogctl succeeded\n");
}
return 0;
}
运行 它(将 printk
重置回 7 4 1 7
后):
root@ubuntu:/tmp# ./test 1 ; cat /proc/sys/kernel/printk
klogctl succeeded
1 4 1 7
root@ubuntu:/tmp# ./test 0 ; cat /proc/sys/kernel/printk
klogctl: Invalid argument
1 4 1 7
因此您可以看到 syslog
(通过 klogctl
调用)确实会检查您的参数的有效性。
我的 Linux
发行版是 RHEL7
,内核版本是 3.10.0
。
形成printk文件,我知道minimum_console_loglevel定义:
- minimum_console_loglevel: minimum (highest) value to which console_loglevel can be set
查询printk
的当前日志级别:
[root@localhost kernel]# cat /proc/sys/kernel/printk
7 4 1 7
修改当前控制台日志级别:
[root@localhost kernel]# echo 0 > /proc/sys/kernel/printk
[root@localhost kernel]# cat /proc/sys/kernel/printk
0 4 1 7
据我了解,minimum_console_loglevel 是 1
,因此将 console_loglevel 修改为0
应该失败了。但是从 cat 输出来看,似乎成功了。
来自 printk.c
代码:
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;
/* Implicitly re-enable logging to console */
saved_console_loglevel = -1;
error = 0;
break;
我也认为 console_loglevel 值不应该被修改。
您查看的代码有误。 /proc/sys/kernel/printk
由 kernel/sysctl.c
提供。 printk
looks like this 的定义:
{
.procname = "printk",
.data = &console_loglevel,
.maxlen = 4*sizeof(int),
.mode = 0644,
.proc_handler = proc_dointvec,
},
这定义了一个名为 printk
的文件,它控制一个包含 4 个元素的 int 数组。至关重要的是,处理函数 proc_dointvec
是一个对整数数组进行操作的通用函数,并且 它不会验证其参数 .
因此,作为 root,您可以写入任何您想要的垃圾数字 /proc/sys/kernel/printk
,内核会很乐意接受它们。演示:
root@ubuntu:/proc/sys/kernel# echo '-989897 42 -2147483648 0' > printk
root@ubuntu:/proc/sys/kernel# cat printk
-989897 42 -18446744071562067968 0
(第三个实际上是一个有趣的 可能 无害的错误我刚刚发现...)
从printk.h
可以看出,四个日志级别参数直接来自这个四元素数组:
extern int console_printk[];
#define console_loglevel (console_printk[0])
#define default_message_loglevel (console_printk[1])
#define minimum_console_loglevel (console_printk[2])
#define default_console_loglevel (console_printk[3])
那么,那么,您发布的代码有什么作用呢?它实现了 syslog
系统调用,如函数名称 (do_syslog
) 所示。正如 the man page 所述:
SYSLOG_ACTION_CONSOLE_LEVEL (8)
The call sets console_loglevel to the value given in len,
which must be an integer between 1 and 8 (inclusive). The
kernel silently enforces a minimum value of
minimum_console_loglevel for len. See the log level section
for details. The bufp argument is ignored.
我们可以看到 syslog
系统调用还允许您设置控制台日志级别,但它实际上会检查这些值是否有效。让我们用一个简单的测试程序来测试(注意 glibc 调用函数 klogctl
而不是 syslog
,因为 user-space syslog
函数做了其他事情):
#include <sys/klog.h>
#include <stdio.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <stdlib.h>
#define SYSLOG_ACTION_CONSOLE_LEVEL 8
int main(int argc, char **argv) {
if(klogctl(SYSLOG_ACTION_CONSOLE_LEVEL, NULL, atoi(argv[1])) < 0) {
perror("klogctl");
} else {
printf("klogctl succeeded\n");
}
return 0;
}
运行 它(将 printk
重置回 7 4 1 7
后):
root@ubuntu:/tmp# ./test 1 ; cat /proc/sys/kernel/printk
klogctl succeeded
1 4 1 7
root@ubuntu:/tmp# ./test 0 ; cat /proc/sys/kernel/printk
klogctl: Invalid argument
1 4 1 7
因此您可以看到 syslog
(通过 klogctl
调用)确实会检查您的参数的有效性。