Re: [patch] kernel events layer

Robert Love
2004-07-24T05:01:18Z

On Sat, 2004-07-24 at 14:42 +1000, Keith Owens wrote:

> Never use the return value from snprintf to work out the next buffer
> position, it is not reliable when the data is truncated. The example
> above uses a second call to snprintf which will generate a warning for
> truncated data and fail safe, but not all code is that trustworthy. I
> always use strlen to get the real buffer length.

If we are going to use snprintf, we might as well use it right.

Updated patch attached. Thanks.

Thinking about it, though, the from and signal buffers are never ever
going to approach PAGE_SIZE so this check is really being anal. We
certainly want to be safe later, when we construct the message, but
here ... oh well. Safe is better than sorry.

Robert Love

Kernel to user-space communication layer using netlink
X-Signed-Off-By: Robert Love <rml@ximian.com>

arch/i386/kernel/cpu/mcheck/p4.c | 9 ++
include/linux/kevent.h | 37 ++++++++++
include/linux/netlink.h | 1
init/Kconfig | 14 +++
kernel/Makefile | 1
kernel/kevent.c | 143 +++++++++++++++++++++++++++++++++++++++
6 files changed, 204 insertions(+), 1 deletion(-)

diff -urN linux-2.6.8-rc2/arch/i386/kernel/cpu/mcheck/p4.c linux/arch/i386/kernel/cpu/mcheck/p4.c
--- linux-2.6.8-rc2/arch/i386/kernel/cpu/mcheck/p4.c 2004-06-16 01:19:37.000000000 -0400
+++ linux/arch/i386/kernel/cpu/mcheck/p4.c 2004-07-23 22:55:20.000000000 -0400
@@ -9,6 +9,7 @@
#include <linux/irq.h>
#include <linux/interrupt.h>
#include <linux/smp.h>
+#include <linux/kevent.h>

#include <asm/processor.h>
#include <asm/system.h>
@@ -59,9 +60,15 @@
if (l & 0x1) {
printk(KERN_EMERG "CPU%d: Temperature above threshold\n", cpu);
printk(KERN_EMERG "CPU%d: Running in modulated clock mode\n",
- cpu);
+ cpu);
+ send_kevent(KMSG_POWER,
+ "/org/kernel/devices/system/cpu/temperature", "high",
+ "Cpu: %d\n", cpu);
} else {
printk(KERN_INFO "CPU%d: Temperature/speed normal\n", cpu);
+ send_kevent(KMSG_POWER,
+ "/org/kernel/devices/system/cpu/temperature", "normal",
+ "Cpu: %d\n", cpu);
}
}

diff -urN linux-2.6.8-rc2/include/linux/kevent.h linux/include/linux/kevent.h
--- linux-2.6.8-rc2/include/linux/kevent.h 1969-12-31 19:00:00.000000000 -0500
+++ linux/include/linux/kevent.h 2004-07-23 22:53:45.000000000 -0400
@@ -0,0 +1,37 @@
+#ifndef _LINUX_KEVENT_H
+#define _LINUX_KEVENT_H
+
+#include <linux/config.h>
+
+/* kevent types - these are used as the multicast group */
+#define KMSG_GENERAL 0
+#define KMSG_STORAGE 1
+#define KMSG_POWER 2
+#define KMSG_FS 3
+#define KMSG_HOTPLUG 4
+
+#ifdef CONFIG_KERNEL_EVENTS
+
+int send_kevent(int type, const char *object, const char *signal,
+ const char *fmt, ...);
+
+int send_kevent_atomic(int type, const char *object, const char *signal,
+ const char *fmt, ...);
+
+#else
+
+static inline int send_kevent(int type, const char *object, const char *signal,
+ const char *fmt, ...)
+{
+ return 0;
+}
+
+static inline int send_kevent_atomic(int type, const char *object,
+ const char *signal, const char *fmt, ...)
+{
+ return 0;
+}
+
+#endif /* ! CONFIG_KERNEL_EVENTS */
+
+#endif /* _LINUX_KEVENT_H */
diff -urN linux-2.6.8-rc2/include/linux/netlink.h linux/include/linux/netlink.h
--- linux-2.6.8-rc2/include/linux/netlink.h 2004-07-23 22:18:04.000000000 -0400
+++ linux/include/linux/netlink.h 2004-07-23 22:21:18.000000000 -0400
@@ -17,6 +17,7 @@
#define NETLINK_ROUTE6 11 /* af_inet6 route comm channel */
#define NETLINK_IP6_FW 13
#define NETLINK_DNRTMSG 14 /* DECnet routing messages */
+#define NETLINK_KMESSAGE 15 /* Kernel messages to userspace */
#define NETLINK_TAPBASE 16 /* 16 to 31 are ethertap */

#define MAX_LINKS 32
diff -urN linux-2.6.8-rc2/init/Kconfig linux/init/Kconfig
--- linux-2.6.8-rc2/init/Kconfig 2004-07-23 22:18:04.000000000 -0400
+++ linux/init/Kconfig 2004-07-23 22:44:26.000000000 -0400
@@ -160,6 +160,20 @@
logging of avc messages output). Does not do system-call
auditing without CONFIG_AUDITSYSCALL.

+config KERNEL_EVENTS
+ bool "Kernel Events Layer"
+ depends on NET
+ default y
+ help
+ This option enables the kernel events layer, which is a simple
+ mechanism for kernel-to-user communication over a netlink socket.
+ The goal of the kernel events layer is to provide a simple and
+ efficient logging, error, and events system. Specifically, code
+ is available to link the events into D-BUS. Say Y, unless you
+ are building a system requiring minimal memory consumption.
+
+ D-BUS is available at http://dbus.freedesktop.org/
+
config AUDITSYSCALL
bool "Enable system-call auditing support"
depends on AUDIT && (X86 || PPC64 || ARCH_S390 || IA64)
diff -urN linux-2.6.8-rc2/kernel/kevent.c linux/kernel/kevent.c
--- linux-2.6.8-rc2/kernel/kevent.c 1969-12-31 19:00:00.000000000 -0500
+++ linux/kernel/kevent.c 2004-07-24 00:57:44.180662592 -0400
@@ -0,0 +1,143 @@
+/*
+ * kernel/kevent.c - kernel event delivery over a netlink socket
+ *
+ * Copyright (C) 2004 Red Hat, Inc. All rights reserved.
+ *
+ * Licensed under the GNU GPL v2.
+ *
+ * Authors:
+ * Arjan van de Ven <arjanv@redhat.com>
+ * Kay Sievers <kay.sievers@vrfy.org>
+ * Robert Love <rml@novell.com>
+ */
+
+#include <linux/module.h>
+#include <linux/spinlock.h>
+#include <linux/socket.h>
+#include <linux/skbuff.h>
+#include <linux/netlink.h>
+#include <linux/string.h>
+#include <linux/kevent.h>
+#include <net/sock.h>
+
+/* There is one global netlink socket */
+static struct sock *kevent_sock = NULL;
+
+static void netlink_receive(struct sock *sk, int len)
+{
+ struct sk_buff *skb;
+
+ /* just drop them all */
+ while ((skb = skb_dequeue(&sk->sk_receive_queue)) != NULL)
+ kfree_skb(skb);
+}
+
+static int netlink_send(__u32 groups, int gfp_mask, const char *buffer, int len)
+{
+ struct sk_buff *skb;
+ char *data_start;
+
+ if (!kevent_sock)
+ return -EIO;
+
+ if (!buffer)
+ return -EINVAL;
+
+ if (len > PAGE_SIZE)
+ return -EINVAL;
+
+ skb = alloc_skb(len, gfp_mask);
+ if (!skb)
+ return -ENOMEM;
+ data_start = skb_put(skb, len);
+ memcpy(data_start, buffer, len);
+
+ return netlink_broadcast(kevent_sock, skb, 0, groups, gfp_mask);
+}
+
+static int do_send_kevent(int type, int gfp_mask, const char *object,
+ const char *signal, const char *fmt, va_list args)
+{
+ char *buffer;
+ int len;
+ int ret;
+
+ if (!object)
+ return -EINVAL;
+
+ if (!signal)
+ return -EINVAL;
+
+ if (strlen(object) > PAGE_SIZE)
+ return -EINVAL;
+
+ buffer = (char *) get_zeroed_page(gfp_mask);
+ if (!buffer)
+ return -ENOMEM;
+
+ snprintf(buffer, PAGE_SIZE, "From: %s\n", object);
+ len = strlen(buffer);
+ snprintf(buffer + len, PAGE_SIZE - len, "Signal: %s\n", signal);
+ len = strlen(buffer);
+
+ /* possible anxiliary data */
+ if (fmt)
+ len += vscnprintf(&buffer[len], PAGE_SIZE-len-1, fmt, args);
+ buffer[len++] = '\0';
+
+ ret = netlink_send((1 << type), gfp_mask, buffer, len);
+ free_page((unsigned long) buffer);
+
+ return ret;
+}
+
+/**
+ * send_kevent - send a message to user-space via the kernel events layer
+ */
+int send_kevent(int type, const char *object, const char *signal,
+ const char *fmt, ...)
+{
+ va_list args;
+ int ret;
+
+ va_start(args, fmt);
+ ret = do_send_kevent(type, GFP_KERNEL, object, signal, fmt, args);
+ va_end(args);
+
+ return ret;
+}
+
+EXPORT_SYMBOL_GPL(send_kevent);
+
+/**
+ * send_kevent_atomic - send a message to user-space via the kernel events layer
+ */
+int send_kevent_atomic(int type, const char *object, const char *signal,
+ const char *fmt, ...)
+{
+ va_list args;
+ int ret;
+
+ va_start(args, fmt);
+ ret = do_send_kevent(type, GFP_ATOMIC, object, signal, fmt, args);
+ va_end(args);
+
+ return ret;
+}
+
+EXPORT_SYMBOL_GPL(send_kevent_atomic);
+
+static int kevent_init(void)
+{
+ kevent_sock = netlink_kernel_create(NETLINK_KMESSAGE, netlink_receive);
+
+ if (!kevent_sock) {
+ printk(KERN_ERR "kevent: "
+ "unable to create netlink socket; aborting\n");
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
+module_init(kevent_init);
diff -urN linux-2.6.8-rc2/kernel/Makefile linux/kernel/Makefile
--- linux-2.6.8-rc2/kernel/Makefile 2004-07-23 22:18:04.000000000 -0400
+++ linux/kernel/Makefile 2004-07-23 22:33:36.000000000 -0400
@@ -23,6 +23,7 @@
obj-$(CONFIG_STOP_MACHINE) += stop_machine.o
obj-$(CONFIG_AUDIT) += audit.o
obj-$(CONFIG_AUDITSYSCALL) += auditsc.o
+obj-$(CONFIG_KERNEL_EVENTS) += kevent.o

ifneq ($(CONFIG_IA64),y)
# According to Alan Modra <alan@linuxcare.com.au>, the -fno-omit-frame-pointer is