kobject_uevent.c 11.3 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
#include <linux/string.h>
#include <linux/kobject.h>
20 21
#include <linux/export.h>
#include <linux/kmod.h>
22
#include <linux/slab.h>
23
#include <linux/user_namespace.h>
L
Linus Torvalds 已提交
24 25 26 27
#include <linux/socket.h>
#include <linux/skbuff.h>
#include <linux/netlink.h>
#include <net/sock.h>
28
#include <net/net_namespace.h>
L
Linus Torvalds 已提交
29 30


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

41 42 43
/* This lock protects uevent_seqnum and uevent_sock_list */
static DEFINE_MUTEX(uevent_sock_mutex);

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

88
#ifdef CONFIG_NET
89 90
static int kobj_bcast_filter(struct sock *dsk, struct sk_buff *skb, void *data)
{
91
	struct kobject *kobj = data, *ksobj;
92 93 94
	const struct kobj_ns_type_operations *ops;

	ops = kobj_ns_ops(kobj);
95 96 97 98 99 100 101
	if (!ops && kobj->kset) {
		ksobj = &kobj->kset->kobj;
		if (ksobj->parent != NULL)
			ops = kobj_ns_ops(ksobj->parent);
	}

	if (ops && ops->netlink_ns && kobj->ktype->namespace) {
102 103 104 105 106 107 108 109
		const void *sock_ns, *ns;
		ns = kobj->ktype->namespace(kobj);
		sock_ns = ops->netlink_ns(dsk);
		return sock_ns != ns;
	}

	return 0;
}
110
#endif
111

112 113 114 115 116 117 118 119 120 121 122 123 124 125 126
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;
}

127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150
static int init_uevent_argv(struct kobj_uevent_env *env, const char *subsystem)
{
	int len;

	len = strlcpy(&env->buf[env->buflen], subsystem,
		      sizeof(env->buf) - env->buflen);
	if (len >= (sizeof(env->buf) - env->buflen)) {
		WARN(1, KERN_ERR "init_uevent_argv: buffer size too small\n");
		return -ENOMEM;
	}

	env->argv[0] = uevent_helper;
	env->argv[1] = &env->buf[env->buflen];
	env->argv[2] = NULL;

	env->buflen += len + 1;
	return 0;
}

static void cleanup_uevent_env(struct subprocess_info *info)
{
	kfree(info->data);
}

L
Linus Torvalds 已提交
151
/**
152
 * kobject_uevent_env - send an uevent with environmental data
L
Linus Torvalds 已提交
153
 *
154
 * @action: action that is happening
L
Linus Torvalds 已提交
155
 * @kobj: struct kobject that the action is happening to
156
 * @envp_ext: pointer to environmental data
157
 *
158
 * Returns 0 if kobject_uevent_env() is completed with success or the
159
 * corresponding error when it fails.
L
Linus Torvalds 已提交
160
 */
161
int kobject_uevent_env(struct kobject *kobj, enum kobject_action action,
162
		       char *envp_ext[])
L
Linus Torvalds 已提交
163
{
164 165
	struct kobj_uevent_env *env;
	const char *action_string = kobject_actions[action];
166 167 168 169
	const char *devpath = NULL;
	const char *subsystem;
	struct kobject *top_kobj;
	struct kset *kset;
170
	const struct kset_uevent_ops *uevent_ops;
L
Linus Torvalds 已提交
171
	int i = 0;
172
	int retval = 0;
173 174 175
#ifdef CONFIG_NET
	struct uevent_sock *ue_sk;
#endif
L
Linus Torvalds 已提交
176

177
	pr_debug("kobject: '%s' (%p): %s\n",
178
		 kobject_name(kobj), kobj, __func__);
179 180 181

	/* search the kset we belong to */
	top_kobj = kobj;
182
	while (!top_kobj->kset && top_kobj->parent)
183
		top_kobj = top_kobj->parent;
184

185
	if (!top_kobj->kset) {
186 187
		pr_debug("kobject: '%s' (%p): %s: attempted to send uevent "
			 "without kset!\n", kobject_name(kobj), kobj,
188
			 __func__);
189 190
		return -EINVAL;
	}
L
Linus Torvalds 已提交
191

192
	kset = top_kobj->kset;
193
	uevent_ops = kset->uevent_ops;
L
Linus Torvalds 已提交
194

195 196 197 198 199 200 201
	/* 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;
	}
202
	/* skip the event, if the filter returns zero. */
203
	if (uevent_ops && uevent_ops->filter)
204
		if (!uevent_ops->filter(kset, kobj)) {
205 206
			pr_debug("kobject: '%s' (%p): %s: filter function "
				 "caused the event to drop!\n",
207
				 kobject_name(kobj), kobj, __func__);
208 209
			return 0;
		}
L
Linus Torvalds 已提交
210

211 212 213 214 215 216
	/* originating subsystem */
	if (uevent_ops && uevent_ops->name)
		subsystem = uevent_ops->name(kset, kobj);
	else
		subsystem = kobject_name(&kset->kobj);
	if (!subsystem) {
217 218
		pr_debug("kobject: '%s' (%p): %s: unset subsystem caused the "
			 "event to drop!\n", kobject_name(kobj), kobj,
219
			 __func__);
220 221 222
		return 0;
	}

223 224 225
	/* environment buffer */
	env = kzalloc(sizeof(struct kobj_uevent_env), GFP_KERNEL);
	if (!env)
226
		return -ENOMEM;
L
Linus Torvalds 已提交
227

228 229
	/* complete object path */
	devpath = kobject_get_path(kobj, GFP_KERNEL);
230 231
	if (!devpath) {
		retval = -ENOENT;
232
		goto exit;
233
	}
L
Linus Torvalds 已提交
234

235
	/* default keys */
236 237 238 239 240 241 242 243 244 245 246 247 248
	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++) {
249
			retval = add_uevent_var(env, "%s", envp_ext[i]);
250 251 252 253
			if (retval)
				goto exit;
		}
	}
L
Linus Torvalds 已提交
254

255
	/* let the kset specific function add its stuff */
256
	if (uevent_ops && uevent_ops->uevent) {
257
		retval = uevent_ops->uevent(kset, kobj, env);
L
Linus Torvalds 已提交
258
		if (retval) {
259 260
			pr_debug("kobject: '%s' (%p): %s: uevent() returned "
				 "%d\n", kobject_name(kobj), kobj,
261
				 __func__, retval);
L
Linus Torvalds 已提交
262 263 264 265
			goto exit;
		}
	}

266 267 268 269 270 271 272 273 274 275 276
	/*
	 * 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;

277
	mutex_lock(&uevent_sock_mutex);
278
	/* we will send an event, so request a new sequence number */
279 280 281
	retval = add_uevent_var(env, "SEQNUM=%llu", (unsigned long long)++uevent_seqnum);
	if (retval) {
		mutex_unlock(&uevent_sock_mutex);
282
		goto exit;
283
	}
L
Linus Torvalds 已提交
284

K
Kay Sievers 已提交
285
#if defined(CONFIG_NET)
286
	/* send netlink message */
287 288
	list_for_each_entry(ue_sk, &uevent_sock_list, list) {
		struct sock *uevent_sock = ue_sk->sk;
289 290 291
		struct sk_buff *skb;
		size_t len;

292 293 294
		if (!netlink_has_listeners(uevent_sock, 1))
			continue;

295 296
		/* allocate message with the maximum possible size */
		len = strlen(action_string) + strlen(devpath) + 2;
297
		skb = alloc_skb(len + env->buflen, GFP_KERNEL);
298
		if (skb) {
299 300
			char *scratch;

301 302 303 304 305
			/* add header */
			scratch = skb_put(skb, len);
			sprintf(scratch, "%s@%s", action_string, devpath);

			/* copy keys to our continuous event payload buffer */
306 307
			for (i = 0; i < env->envp_idx; i++) {
				len = strlen(env->envp[i]) + 1;
308
				scratch = skb_put(skb, len);
309
				strcpy(scratch, env->envp[i]);
310 311 312
			}

			NETLINK_CB(skb).dst_group = 1;
313 314 315 316
			retval = netlink_broadcast_filtered(uevent_sock, skb,
							    0, 1, GFP_KERNEL,
							    kobj_bcast_filter,
							    kobj);
317
			/* ENOBUFS should be handled in userspace */
318
			if (retval == -ENOBUFS || retval == -ESRCH)
319
				retval = 0;
320 321
		} else
			retval = -ENOMEM;
322
	}
K
Kay Sievers 已提交
323
#endif
324
	mutex_unlock(&uevent_sock_mutex);
L
Linus Torvalds 已提交
325

326
	/* call uevent_helper, usually only enabled during early boot */
327
	if (uevent_helper[0] && !kobj_usermode_filter(kobj)) {
328
		struct subprocess_info *info;
L
Linus Torvalds 已提交
329

330 331 332
		retval = add_uevent_var(env, "HOME=/");
		if (retval)
			goto exit;
333 334
		retval = add_uevent_var(env,
					"PATH=/sbin:/bin:/usr/sbin:/usr/bin");
335 336
		if (retval)
			goto exit;
337 338 339
		retval = init_uevent_argv(env, subsystem);
		if (retval)
			goto exit;
340

341 342 343 344 345 346 347 348
		retval = -ENOMEM;
		info = call_usermodehelper_setup(env->argv[0], env->argv,
						 env->envp, GFP_KERNEL,
						 NULL, cleanup_uevent_env, env);
		if (info) {
			retval = call_usermodehelper_exec(info, UMH_NO_WAIT);
			env = NULL;	/* freed by cleanup_uevent_env */
		}
349
	}
L
Linus Torvalds 已提交
350 351

exit:
352
	kfree(devpath);
353
	kfree(env);
354
	return retval;
L
Linus Torvalds 已提交
355
}
356 357 358
EXPORT_SYMBOL_GPL(kobject_uevent_env);

/**
359
 * kobject_uevent - notify userspace by sending an uevent
360
 *
361
 * @action: action that is happening
362
 * @kobj: struct kobject that the action is happening to
363 364 365
 *
 * Returns 0 if kobject_uevent() is completed with success or the
 * corresponding error when it fails.
366
 */
367
int kobject_uevent(struct kobject *kobj, enum kobject_action action)
368
{
369
	return kobject_uevent_env(kobj, action, NULL);
370
}
371
EXPORT_SYMBOL_GPL(kobject_uevent);
L
Linus Torvalds 已提交
372 373

/**
374 375 376
 * 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 已提交
377 378 379 380
 *
 * Returns 0 if environment variable was added successfully or -ENOMEM
 * if no space was available.
 */
381
int add_uevent_var(struct kobj_uevent_env *env, const char *format, ...)
L
Linus Torvalds 已提交
382 383
{
	va_list args;
384
	int len;
L
Linus Torvalds 已提交
385

386
	if (env->envp_idx >= ARRAY_SIZE(env->envp)) {
A
Arjan van de Ven 已提交
387
		WARN(1, KERN_ERR "add_uevent_var: too many keys\n");
L
Linus Torvalds 已提交
388
		return -ENOMEM;
389
	}
L
Linus Torvalds 已提交
390 391

	va_start(args, format);
392 393 394
	len = vsnprintf(&env->buf[env->buflen],
			sizeof(env->buf) - env->buflen,
			format, args);
L
Linus Torvalds 已提交
395 396
	va_end(args);

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

402 403
	env->envp[env->envp_idx++] = &env->buf[env->buflen];
	env->buflen += len + 1;
L
Linus Torvalds 已提交
404 405
	return 0;
}
406
EXPORT_SYMBOL_GPL(add_uevent_var);
L
Linus Torvalds 已提交
407

K
Kay Sievers 已提交
408
#if defined(CONFIG_NET)
409
static int uevent_net_init(struct net *net)
410
{
411
	struct uevent_sock *ue_sk;
412 413
	struct netlink_kernel_cfg cfg = {
		.groups	= 1,
414
		.flags	= NL_CFG_F_NONROOT_RECV,
415
	};
416 417 418 419 420

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

421
	ue_sk->sk = netlink_kernel_create(net, NETLINK_KOBJECT_UEVENT, &cfg);
422
	if (!ue_sk->sk) {
423 424
		printk(KERN_ERR
		       "kobject_uevent: unable to create netlink socket!\n");
425
		kfree(ue_sk);
426 427
		return -ENODEV;
	}
428 429 430
	mutex_lock(&uevent_sock_mutex);
	list_add_tail(&ue_sk->list, &uevent_sock_list);
	mutex_unlock(&uevent_sock_mutex);
431 432 433
	return 0;
}

434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464
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)
{
	return register_pernet_subsys(&uevent_net_ops);
}


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