kobject_uevent.c 10.2 KB
Newer Older
L
Linus Torvalds 已提交
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
/*
 * 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>
18 19 20
#include <linux/string.h>
#include <linux/kobject.h>
#include <linux/module.h>
21
#include <linux/slab.h>
22
#include <linux/user_namespace.h>
L
Linus Torvalds 已提交
23 24 25 26
#include <linux/socket.h>
#include <linux/skbuff.h>
#include <linux/netlink.h>
#include <net/sock.h>
27
#include <net/net_namespace.h>
L
Linus Torvalds 已提交
28 29


30
u64 uevent_seqnum;
31
char uevent_helper[UEVENT_HELPER_PATH_LEN] = CONFIG_UEVENT_HELPER_PATH;
32
static DEFINE_SPINLOCK(sequence_lock);
33 34 35 36 37 38 39
#ifdef CONFIG_NET
struct uevent_sock {
	struct list_head list;
	struct sock *sk;
};
static LIST_HEAD(uevent_sock_list);
static DEFINE_MUTEX(uevent_sock_mutex);
40 41
#endif

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
/* 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;

M
Mark Lord 已提交
67
	if (count && (buf[count-1] == '\n' || buf[count-1] == '\0'))
68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85
		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;
}

86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101
static int kobj_bcast_filter(struct sock *dsk, struct sk_buff *skb, void *data)
{
	struct kobject *kobj = data;
	const struct kobj_ns_type_operations *ops;

	ops = kobj_ns_ops(kobj);
	if (ops) {
		const void *sock_ns, *ns;
		ns = kobj->ktype->namespace(kobj);
		sock_ns = ops->netlink_ns(dsk);
		return sock_ns != ns;
	}

	return 0;
}

102 103 104 105 106 107 108 109 110 111 112 113 114 115 116
static int kobj_usermode_filter(struct kobject *kobj)
{
	const struct kobj_ns_type_operations *ops;

	ops = kobj_ns_ops(kobj);
	if (ops) {
		const void *init_ns, *ns;
		ns = kobj->ktype->namespace(kobj);
		init_ns = ops->initial_ns();
		return ns != init_ns;
	}

	return 0;
}

L
Linus Torvalds 已提交
117
/**
118
 * kobject_uevent_env - send an uevent with environmental data
L
Linus Torvalds 已提交
119
 *
120
 * @action: action that is happening
L
Linus Torvalds 已提交
121
 * @kobj: struct kobject that the action is happening to
122
 * @envp_ext: pointer to environmental data
123 124 125
 *
 * Returns 0 if kobject_uevent() is completed with success or the
 * corresponding error when it fails.
L
Linus Torvalds 已提交
126
 */
127
int kobject_uevent_env(struct kobject *kobj, enum kobject_action action,
128
		       char *envp_ext[])
L
Linus Torvalds 已提交
129
{
130 131
	struct kobj_uevent_env *env;
	const char *action_string = kobject_actions[action];
132 133 134 135
	const char *devpath = NULL;
	const char *subsystem;
	struct kobject *top_kobj;
	struct kset *kset;
136
	const struct kset_uevent_ops *uevent_ops;
137
	u64 seq;
L
Linus Torvalds 已提交
138
	int i = 0;
139
	int retval = 0;
140 141 142
#ifdef CONFIG_NET
	struct uevent_sock *ue_sk;
#endif
L
Linus Torvalds 已提交
143

144
	pr_debug("kobject: '%s' (%p): %s\n",
145
		 kobject_name(kobj), kobj, __func__);
146 147 148

	/* search the kset we belong to */
	top_kobj = kobj;
149
	while (!top_kobj->kset && top_kobj->parent)
150
		top_kobj = top_kobj->parent;
151

152
	if (!top_kobj->kset) {
153 154
		pr_debug("kobject: '%s' (%p): %s: attempted to send uevent "
			 "without kset!\n", kobject_name(kobj), kobj,
155
			 __func__);
156 157
		return -EINVAL;
	}
L
Linus Torvalds 已提交
158

159
	kset = top_kobj->kset;
160
	uevent_ops = kset->uevent_ops;
L
Linus Torvalds 已提交
161

162 163 164 165 166 167 168
	/* skip the event, if uevent_suppress is set*/
	if (kobj->uevent_suppress) {
		pr_debug("kobject: '%s' (%p): %s: uevent_suppress "
				 "caused the event to drop!\n",
				 kobject_name(kobj), kobj, __func__);
		return 0;
	}
169
	/* skip the event, if the filter returns zero. */
170
	if (uevent_ops && uevent_ops->filter)
171
		if (!uevent_ops->filter(kset, kobj)) {
172 173
			pr_debug("kobject: '%s' (%p): %s: filter function "
				 "caused the event to drop!\n",
174
				 kobject_name(kobj), kobj, __func__);
175 176
			return 0;
		}
L
Linus Torvalds 已提交
177

178 179 180 181 182 183
	/* originating subsystem */
	if (uevent_ops && uevent_ops->name)
		subsystem = uevent_ops->name(kset, kobj);
	else
		subsystem = kobject_name(&kset->kobj);
	if (!subsystem) {
184 185
		pr_debug("kobject: '%s' (%p): %s: unset subsystem caused the "
			 "event to drop!\n", kobject_name(kobj), kobj,
186
			 __func__);
187 188 189
		return 0;
	}

190 191 192
	/* environment buffer */
	env = kzalloc(sizeof(struct kobj_uevent_env), GFP_KERNEL);
	if (!env)
193
		return -ENOMEM;
L
Linus Torvalds 已提交
194

195 196
	/* complete object path */
	devpath = kobject_get_path(kobj, GFP_KERNEL);
197 198
	if (!devpath) {
		retval = -ENOENT;
199
		goto exit;
200
	}
L
Linus Torvalds 已提交
201

202
	/* default keys */
203 204 205 206 207 208 209 210 211 212 213 214 215
	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++) {
216
			retval = add_uevent_var(env, "%s", envp_ext[i]);
217 218 219 220
			if (retval)
				goto exit;
		}
	}
L
Linus Torvalds 已提交
221

222
	/* let the kset specific function add its stuff */
223
	if (uevent_ops && uevent_ops->uevent) {
224
		retval = uevent_ops->uevent(kset, kobj, env);
L
Linus Torvalds 已提交
225
		if (retval) {
226 227
			pr_debug("kobject: '%s' (%p): %s: uevent() returned "
				 "%d\n", kobject_name(kobj), kobj,
228
				 __func__, retval);
L
Linus Torvalds 已提交
229 230 231 232
			goto exit;
		}
	}

233 234 235 236 237 238 239 240 241 242 243
	/*
	 * Mark "add" and "remove" events in the object to ensure proper
	 * events to userspace during automatic cleanup. If the object did
	 * send an "add" event, "remove" will automatically generated by
	 * the core, if not already done by the caller.
	 */
	if (action == KOBJ_ADD)
		kobj->state_add_uevent_sent = 1;
	else if (action == KOBJ_REMOVE)
		kobj->state_remove_uevent_sent = 1;

244
	/* we will send an event, so request a new sequence number */
L
Linus Torvalds 已提交
245
	spin_lock(&sequence_lock);
246
	seq = ++uevent_seqnum;
L
Linus Torvalds 已提交
247
	spin_unlock(&sequence_lock);
248 249 250
	retval = add_uevent_var(env, "SEQNUM=%llu", (unsigned long long)seq);
	if (retval)
		goto exit;
L
Linus Torvalds 已提交
251

K
Kay Sievers 已提交
252
#if defined(CONFIG_NET)
253
	/* send netlink message */
254 255 256
	mutex_lock(&uevent_sock_mutex);
	list_for_each_entry(ue_sk, &uevent_sock_list, list) {
		struct sock *uevent_sock = ue_sk->sk;
257 258 259 260 261
		struct sk_buff *skb;
		size_t len;

		/* allocate message with the maximum possible size */
		len = strlen(action_string) + strlen(devpath) + 2;
262
		skb = alloc_skb(len + env->buflen, GFP_KERNEL);
263
		if (skb) {
264 265
			char *scratch;

266 267 268 269 270
			/* add header */
			scratch = skb_put(skb, len);
			sprintf(scratch, "%s@%s", action_string, devpath);

			/* copy keys to our continuous event payload buffer */
271 272
			for (i = 0; i < env->envp_idx; i++) {
				len = strlen(env->envp[i]) + 1;
273
				scratch = skb_put(skb, len);
274
				strcpy(scratch, env->envp[i]);
275 276 277
			}

			NETLINK_CB(skb).dst_group = 1;
278 279 280 281
			retval = netlink_broadcast_filtered(uevent_sock, skb,
							    0, 1, GFP_KERNEL,
							    kobj_bcast_filter,
							    kobj);
282 283 284
			/* ENOBUFS should be handled in userspace */
			if (retval == -ENOBUFS)
				retval = 0;
285 286
		} else
			retval = -ENOMEM;
287
	}
288
	mutex_unlock(&uevent_sock_mutex);
K
Kay Sievers 已提交
289
#endif
L
Linus Torvalds 已提交
290

291
	/* call uevent_helper, usually only enabled during early boot */
292
	if (uevent_helper[0] && !kobj_usermode_filter(kobj)) {
293
		char *argv [3];
L
Linus Torvalds 已提交
294

295
		argv [0] = uevent_helper;
296 297
		argv [1] = (char *)subsystem;
		argv [2] = NULL;
298 299 300
		retval = add_uevent_var(env, "HOME=/");
		if (retval)
			goto exit;
301 302
		retval = add_uevent_var(env,
					"PATH=/sbin:/bin:/usr/sbin:/usr/bin");
303 304 305
		if (retval)
			goto exit;

306
		retval = call_usermodehelper(argv[0], argv,
307
					     env->envp, UMH_WAIT_EXEC);
308
	}
L
Linus Torvalds 已提交
309 310

exit:
311
	kfree(devpath);
312
	kfree(env);
313
	return retval;
L
Linus Torvalds 已提交
314
}
315 316 317 318 319
EXPORT_SYMBOL_GPL(kobject_uevent_env);

/**
 * kobject_uevent - notify userspace by ending an uevent
 *
320
 * @action: action that is happening
321
 * @kobj: struct kobject that the action is happening to
322 323 324
 *
 * Returns 0 if kobject_uevent() is completed with success or the
 * corresponding error when it fails.
325
 */
326
int kobject_uevent(struct kobject *kobj, enum kobject_action action)
327
{
328
	return kobject_uevent_env(kobj, action, NULL);
329
}
330
EXPORT_SYMBOL_GPL(kobject_uevent);
L
Linus Torvalds 已提交
331 332

/**
333 334 335
 * 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 已提交
336 337 338 339
 *
 * Returns 0 if environment variable was added successfully or -ENOMEM
 * if no space was available.
 */
340
int add_uevent_var(struct kobj_uevent_env *env, const char *format, ...)
L
Linus Torvalds 已提交
341 342
{
	va_list args;
343
	int len;
L
Linus Torvalds 已提交
344

345
	if (env->envp_idx >= ARRAY_SIZE(env->envp)) {
A
Arjan van de Ven 已提交
346
		WARN(1, KERN_ERR "add_uevent_var: too many keys\n");
L
Linus Torvalds 已提交
347
		return -ENOMEM;
348
	}
L
Linus Torvalds 已提交
349 350

	va_start(args, format);
351 352 353
	len = vsnprintf(&env->buf[env->buflen],
			sizeof(env->buf) - env->buflen,
			format, args);
L
Linus Torvalds 已提交
354 355
	va_end(args);

356
	if (len >= (sizeof(env->buf) - env->buflen)) {
A
Arjan van de Ven 已提交
357
		WARN(1, KERN_ERR "add_uevent_var: buffer size too small\n");
L
Linus Torvalds 已提交
358
		return -ENOMEM;
359
	}
L
Linus Torvalds 已提交
360

361 362
	env->envp[env->envp_idx++] = &env->buf[env->buflen];
	env->buflen += len + 1;
L
Linus Torvalds 已提交
363 364
	return 0;
}
365
EXPORT_SYMBOL_GPL(add_uevent_var);
L
Linus Torvalds 已提交
366

K
Kay Sievers 已提交
367
#if defined(CONFIG_NET)
368
static int uevent_net_init(struct net *net)
369
{
370 371 372 373 374 375 376 377 378
	struct uevent_sock *ue_sk;

	ue_sk = kzalloc(sizeof(*ue_sk), GFP_KERNEL);
	if (!ue_sk)
		return -ENOMEM;

	ue_sk->sk = netlink_kernel_create(net, NETLINK_KOBJECT_UEVENT,
					  1, NULL, NULL, THIS_MODULE);
	if (!ue_sk->sk) {
379 380 381 382
		printk(KERN_ERR
		       "kobject_uevent: unable to create netlink socket!\n");
		return -ENODEV;
	}
383 384 385
	mutex_lock(&uevent_sock_mutex);
	list_add_tail(&ue_sk->list, &uevent_sock_list);
	mutex_unlock(&uevent_sock_mutex);
386 387 388
	return 0;
}

389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420
static void uevent_net_exit(struct net *net)
{
	struct uevent_sock *ue_sk;

	mutex_lock(&uevent_sock_mutex);
	list_for_each_entry(ue_sk, &uevent_sock_list, list) {
		if (sock_net(ue_sk->sk) == net)
			goto found;
	}
	mutex_unlock(&uevent_sock_mutex);
	return;

found:
	list_del(&ue_sk->list);
	mutex_unlock(&uevent_sock_mutex);

	netlink_kernel_release(ue_sk->sk);
	kfree(ue_sk);
}

static struct pernet_operations uevent_net_ops = {
	.init	= uevent_net_init,
	.exit	= uevent_net_exit,
};

static int __init kobject_uevent_init(void)
{
	netlink_set_nonroot(NETLINK_KOBJECT_UEVENT, NL_NONROOT_RECV);
	return register_pernet_subsys(&uevent_net_ops);
}


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