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>
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
#ifdef CONFIG_UEVENT_HELPER
32
char uevent_helper[UEVENT_HELPER_PATH_LEN] = CONFIG_UEVENT_HELPER_PATH;
33
#endif
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);
40 41
#endif

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

45 46 47 48 49 50 51 52 53 54 55 56 57 58
/* 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
59
 * @count: length of buffer
60 61 62 63 64 65 66 67 68 69
 * @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 已提交
70
	if (count && (buf[count-1] == '\n' || buf[count-1] == '\0'))
71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88
		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;
}

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

	ops = kobj_ns_ops(kobj);
96 97 98 99 100 101 102
	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) {
103 104 105 106 107 108 109 110
		const void *sock_ns, *ns;
		ns = kobj->ktype->namespace(kobj);
		sock_ns = ops->netlink_ns(dsk);
		return sock_ns != ns;
	}

	return 0;
}
111
#endif
112

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

129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151
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);
}
152
#endif
153

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

180
	pr_debug("kobject: '%s' (%p): %s\n",
181
		 kobject_name(kobj), kobj, __func__);
182 183 184

	/* search the kset we belong to */
	top_kobj = kobj;
185
	while (!top_kobj->kset && top_kobj->parent)
186
		top_kobj = top_kobj->parent;
187

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

195
	kset = top_kobj->kset;
196
	uevent_ops = kset->uevent_ops;
L
Linus Torvalds 已提交
197

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

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

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

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

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

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

269 270 271 272 273 274 275 276 277 278 279
	/*
	 * 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;

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

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

295 296 297
		if (!netlink_has_listeners(uevent_sock, 1))
			continue;

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

304 305 306 307 308
			/* add header */
			scratch = skb_put(skb, len);
			sprintf(scratch, "%s@%s", action_string, devpath);

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

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

329
#ifdef CONFIG_UEVENT_HELPER
330
	/* call uevent_helper, usually only enabled during early boot */
331
	if (uevent_helper[0] && !kobj_usermode_filter(kobj)) {
332
		struct subprocess_info *info;
L
Linus Torvalds 已提交
333

334 335 336
		retval = add_uevent_var(env, "HOME=/");
		if (retval)
			goto exit;
337 338
		retval = add_uevent_var(env,
					"PATH=/sbin:/bin:/usr/sbin:/usr/bin");
339 340
		if (retval)
			goto exit;
341 342 343
		retval = init_uevent_argv(env, subsystem);
		if (retval)
			goto exit;
344

345 346 347 348 349 350 351 352
		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 */
		}
353
	}
354
#endif
L
Linus Torvalds 已提交
355 356

exit:
357
	kfree(devpath);
358
	kfree(env);
359
	return retval;
L
Linus Torvalds 已提交
360
}
361 362 363
EXPORT_SYMBOL_GPL(kobject_uevent_env);

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

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

391
	if (env->envp_idx >= ARRAY_SIZE(env->envp)) {
A
Arjan van de Ven 已提交
392
		WARN(1, KERN_ERR "add_uevent_var: too many keys\n");
L
Linus Torvalds 已提交
393
		return -ENOMEM;
394
	}
L
Linus Torvalds 已提交
395 396

	va_start(args, format);
397 398 399
	len = vsnprintf(&env->buf[env->buflen],
			sizeof(env->buf) - env->buflen,
			format, args);
L
Linus Torvalds 已提交
400 401
	va_end(args);

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

407 408
	env->envp[env->envp_idx++] = &env->buf[env->buflen];
	env->buflen += len + 1;
L
Linus Torvalds 已提交
409 410
	return 0;
}
411
EXPORT_SYMBOL_GPL(add_uevent_var);
L
Linus Torvalds 已提交
412

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

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

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

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 465 466 467 468 469
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);
}


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