kobject_uevent.c 7.3 KB
Newer Older
L
Linus Torvalds 已提交
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
/*
 * kernel userspace event delivery
 *
 * Copyright (C) 2004 Red Hat, Inc.  All rights reserved.
 * Copyright (C) 2004 Novell, Inc.  All rights reserved.
 * Copyright (C) 2004 IBM, Inc. All rights reserved.
 *
 * Licensed under the GNU GPL v2.
 *
 * Authors:
 *	Robert Love		<rml@novell.com>
 *	Kay Sievers		<kay.sievers@vrfy.org>
 *	Arjan van de Ven	<arjanv@redhat.com>
 *	Greg Kroah-Hartman	<greg@kroah.com>
 */

#include <linux/spinlock.h>
#include <linux/socket.h>
#include <linux/skbuff.h>
#include <linux/netlink.h>
#include <linux/string.h>
#include <linux/kobject.h>
#include <net/sock.h>


26
u64 uevent_seqnum;
27
char uevent_helper[UEVENT_HELPER_PATH_LEN] = CONFIG_UEVENT_HELPER_PATH;
28 29 30 31 32
static DEFINE_SPINLOCK(sequence_lock);
#if defined(CONFIG_NET)
static struct sock *uevent_sock;
#endif

33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76
/* the strings here must match the enum in include/linux/kobject.h */
static const char *kobject_actions[] = {
	[KOBJ_ADD] =		"add",
	[KOBJ_REMOVE] =		"remove",
	[KOBJ_CHANGE] =		"change",
	[KOBJ_MOVE] =		"move",
	[KOBJ_ONLINE] =		"online",
	[KOBJ_OFFLINE] =	"offline",
};

/**
 * kobject_action_type - translate action string to numeric type
 *
 * @buf: buffer containing the action string, newline is ignored
 * @len: length of buffer
 * @type: pointer to the location to store the action type
 *
 * Returns 0 if the action string was recognized.
 */
int kobject_action_type(const char *buf, size_t count,
			enum kobject_action *type)
{
	enum kobject_action action;
	int ret = -EINVAL;

	if (count && buf[count-1] == '\n')
		count--;

	if (!count)
		goto out;

	for (action = 0; action < ARRAY_SIZE(kobject_actions); action++) {
		if (strncmp(kobject_actions[action], buf, count) != 0)
			continue;
		if (kobject_actions[action][count] != '\0')
			continue;
		*type = action;
		ret = 0;
		break;
	}
out:
	return ret;
}

L
Linus Torvalds 已提交
77
/**
78
 * kobject_uevent_env - send an uevent with environmental data
L
Linus Torvalds 已提交
79
 *
80
 * @action: action that is happening (usually KOBJ_MOVE)
L
Linus Torvalds 已提交
81
 * @kobj: struct kobject that the action is happening to
82
 * @envp_ext: pointer to environmental data
83 84 85
 *
 * Returns 0 if kobject_uevent() is completed with success or the
 * corresponding error when it fails.
L
Linus Torvalds 已提交
86
 */
87
int kobject_uevent_env(struct kobject *kobj, enum kobject_action action,
88
		       char *envp_ext[])
L
Linus Torvalds 已提交
89
{
90 91
	struct kobj_uevent_env *env;
	const char *action_string = kobject_actions[action];
92 93 94 95
	const char *devpath = NULL;
	const char *subsystem;
	struct kobject *top_kobj;
	struct kset *kset;
96
	struct kset_uevent_ops *uevent_ops;
97
	u64 seq;
L
Linus Torvalds 已提交
98
	int i = 0;
99
	int retval = 0;
L
Linus Torvalds 已提交
100

101 102 103 104
	pr_debug("%s\n", __FUNCTION__);

	/* search the kset we belong to */
	top_kobj = kobj;
105 106
	while (!top_kobj->kset && top_kobj->parent) {
		top_kobj = top_kobj->parent;
L
Linus Torvalds 已提交
107
	}
108 109 110 111
	if (!top_kobj->kset) {
		pr_debug("kobject attempted to send uevent without kset!\n");
		return -EINVAL;
	}
L
Linus Torvalds 已提交
112

113
	kset = top_kobj->kset;
114
	uevent_ops = kset->uevent_ops;
L
Linus Torvalds 已提交
115

116
	/* skip the event, if the filter returns zero. */
117
	if (uevent_ops && uevent_ops->filter)
118 119 120 121
		if (!uevent_ops->filter(kset, kobj)) {
			pr_debug("kobject filter function caused the event to drop!\n");
			return 0;
		}
L
Linus Torvalds 已提交
122

123 124 125 126 127 128 129 130 131 132
	/* originating subsystem */
	if (uevent_ops && uevent_ops->name)
		subsystem = uevent_ops->name(kset, kobj);
	else
		subsystem = kobject_name(&kset->kobj);
	if (!subsystem) {
		pr_debug("unset subsytem caused the event to drop!\n");
		return 0;
	}

133 134 135
	/* environment buffer */
	env = kzalloc(sizeof(struct kobj_uevent_env), GFP_KERNEL);
	if (!env)
136
		return -ENOMEM;
L
Linus Torvalds 已提交
137

138 139
	/* complete object path */
	devpath = kobject_get_path(kobj, GFP_KERNEL);
140 141
	if (!devpath) {
		retval = -ENOENT;
142
		goto exit;
143
	}
L
Linus Torvalds 已提交
144

145
	/* default keys */
146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163
	retval = add_uevent_var(env, "ACTION=%s", action_string);
	if (retval)
		goto exit;
	retval = add_uevent_var(env, "DEVPATH=%s", devpath);
	if (retval)
		goto exit;
	retval = add_uevent_var(env, "SUBSYSTEM=%s", subsystem);
	if (retval)
		goto exit;

	/* keys passed in from the caller */
	if (envp_ext) {
		for (i = 0; envp_ext[i]; i++) {
			retval = add_uevent_var(env, envp_ext[i]);
			if (retval)
				goto exit;
		}
	}
L
Linus Torvalds 已提交
164

165
	/* let the kset specific function add its stuff */
166
	if (uevent_ops && uevent_ops->uevent) {
167
		retval = uevent_ops->uevent(kset, kobj, env);
L
Linus Torvalds 已提交
168
		if (retval) {
169
			pr_debug ("%s - uevent() returned %d\n",
L
Linus Torvalds 已提交
170 171 172 173 174
				  __FUNCTION__, retval);
			goto exit;
		}
	}

175
	/* we will send an event, so request a new sequence number */
L
Linus Torvalds 已提交
176
	spin_lock(&sequence_lock);
177
	seq = ++uevent_seqnum;
L
Linus Torvalds 已提交
178
	spin_unlock(&sequence_lock);
179 180 181
	retval = add_uevent_var(env, "SEQNUM=%llu", (unsigned long long)seq);
	if (retval)
		goto exit;
L
Linus Torvalds 已提交
182

K
Kay Sievers 已提交
183
#if defined(CONFIG_NET)
184 185 186 187 188 189 190
	/* send netlink message */
	if (uevent_sock) {
		struct sk_buff *skb;
		size_t len;

		/* allocate message with the maximum possible size */
		len = strlen(action_string) + strlen(devpath) + 2;
191
		skb = alloc_skb(len + env->buflen, GFP_KERNEL);
192
		if (skb) {
193 194
			char *scratch;

195 196 197 198 199
			/* add header */
			scratch = skb_put(skb, len);
			sprintf(scratch, "%s@%s", action_string, devpath);

			/* copy keys to our continuous event payload buffer */
200 201
			for (i = 0; i < env->envp_idx; i++) {
				len = strlen(env->envp[i]) + 1;
202
				scratch = skb_put(skb, len);
203
				strcpy(scratch, env->envp[i]);
204 205 206 207 208 209
			}

			NETLINK_CB(skb).dst_group = 1;
			netlink_broadcast(uevent_sock, skb, 0, 1, GFP_KERNEL);
		}
	}
K
Kay Sievers 已提交
210
#endif
L
Linus Torvalds 已提交
211

212
	/* call uevent_helper, usually only enabled during early boot */
213
	if (uevent_helper[0]) {
214
		char *argv [3];
L
Linus Torvalds 已提交
215

216
		argv [0] = uevent_helper;
217 218
		argv [1] = (char *)subsystem;
		argv [2] = NULL;
219 220 221 222 223 224 225 226
		retval = add_uevent_var(env, "HOME=/");
		if (retval)
			goto exit;
		retval = add_uevent_var(env, "PATH=/sbin:/bin:/usr/sbin:/usr/bin");
		if (retval)
			goto exit;

		call_usermodehelper (argv[0], argv, env->envp, UMH_WAIT_EXEC);
227
	}
L
Linus Torvalds 已提交
228 229

exit:
230
	kfree(devpath);
231
	kfree(env);
232
	return retval;
L
Linus Torvalds 已提交
233
}
234 235 236 237 238 239 240 241

EXPORT_SYMBOL_GPL(kobject_uevent_env);

/**
 * kobject_uevent - notify userspace by ending an uevent
 *
 * @action: action that is happening (usually KOBJ_ADD and KOBJ_REMOVE)
 * @kobj: struct kobject that the action is happening to
242 243 244
 *
 * Returns 0 if kobject_uevent() is completed with success or the
 * corresponding error when it fails.
245
 */
246
int kobject_uevent(struct kobject *kobj, enum kobject_action action)
247
{
248
	return kobject_uevent_env(kobj, action, NULL);
249 250
}

251
EXPORT_SYMBOL_GPL(kobject_uevent);
L
Linus Torvalds 已提交
252 253

/**
254 255 256
 * add_uevent_var - add key value string to the environment buffer
 * @env: environment buffer structure
 * @format: printf format for the key=value pair
L
Linus Torvalds 已提交
257 258 259 260
 *
 * Returns 0 if environment variable was added successfully or -ENOMEM
 * if no space was available.
 */
261
int add_uevent_var(struct kobj_uevent_env *env, const char *format, ...)
L
Linus Torvalds 已提交
262 263
{
	va_list args;
264
	int len;
L
Linus Torvalds 已提交
265

266 267 268
	if (env->envp_idx >= ARRAY_SIZE(env->envp)) {
		printk(KERN_ERR "add_uevent_var: too many keys\n");
		WARN_ON(1);
L
Linus Torvalds 已提交
269
		return -ENOMEM;
270
	}
L
Linus Torvalds 已提交
271 272

	va_start(args, format);
273 274 275
	len = vsnprintf(&env->buf[env->buflen],
			sizeof(env->buf) - env->buflen,
			format, args);
L
Linus Torvalds 已提交
276 277
	va_end(args);

278 279 280
	if (len >= (sizeof(env->buf) - env->buflen)) {
		printk(KERN_ERR "add_uevent_var: buffer size too small\n");
		WARN_ON(1);
L
Linus Torvalds 已提交
281
		return -ENOMEM;
282
	}
L
Linus Torvalds 已提交
283

284 285
	env->envp[env->envp_idx++] = &env->buf[env->buflen];
	env->buflen += len + 1;
L
Linus Torvalds 已提交
286 287
	return 0;
}
288
EXPORT_SYMBOL_GPL(add_uevent_var);
L
Linus Torvalds 已提交
289

K
Kay Sievers 已提交
290
#if defined(CONFIG_NET)
291 292
static int __init kobject_uevent_init(void)
{
293 294
	uevent_sock = netlink_kernel_create(&init_net, NETLINK_KOBJECT_UEVENT,
					    1, NULL, NULL, THIS_MODULE);
295 296 297 298 299 300 301 302 303 304
	if (!uevent_sock) {
		printk(KERN_ERR
		       "kobject_uevent: unable to create netlink socket!\n");
		return -ENODEV;
	}

	return 0;
}

postcore_initcall(kobject_uevent_init);
K
Kay Sievers 已提交
305
#endif