kobject_uevent.c 10.4 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
#ifdef CONFIG_NET
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
#endif
103

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

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

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

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

161
	kset = top_kobj->kset;
162
	uevent_ops = kset->uevent_ops;
L
Linus Torvalds 已提交
163

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

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

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

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

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

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

235 236 237 238 239 240 241 242 243 244 245
	/*
	 * 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;

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

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

262 263 264
		if (!netlink_has_listeners(uevent_sock, 1))
			continue;

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

271 272 273 274 275
			/* add header */
			scratch = skb_put(skb, len);
			sprintf(scratch, "%s@%s", action_string, devpath);

			/* copy keys to our continuous event payload buffer */
276 277
			for (i = 0; i < env->envp_idx; i++) {
				len = strlen(env->envp[i]) + 1;
278
				scratch = skb_put(skb, len);
279
				strcpy(scratch, env->envp[i]);
280 281 282
			}

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

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

300
		argv [0] = uevent_helper;
301 302
		argv [1] = (char *)subsystem;
		argv [2] = NULL;
303 304 305
		retval = add_uevent_var(env, "HOME=/");
		if (retval)
			goto exit;
306 307
		retval = add_uevent_var(env,
					"PATH=/sbin:/bin:/usr/sbin:/usr/bin");
308 309 310
		if (retval)
			goto exit;

311
		retval = call_usermodehelper(argv[0], argv,
312
					     env->envp, UMH_WAIT_EXEC);
313
	}
L
Linus Torvalds 已提交
314 315

exit:
316
	kfree(devpath);
317
	kfree(env);
318
	return retval;
L
Linus Torvalds 已提交
319
}
320 321 322
EXPORT_SYMBOL_GPL(kobject_uevent_env);

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

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

350
	if (env->envp_idx >= ARRAY_SIZE(env->envp)) {
A
Arjan van de Ven 已提交
351
		WARN(1, KERN_ERR "add_uevent_var: too many keys\n");
L
Linus Torvalds 已提交
352
		return -ENOMEM;
353
	}
L
Linus Torvalds 已提交
354 355

	va_start(args, format);
356 357 358
	len = vsnprintf(&env->buf[env->buflen],
			sizeof(env->buf) - env->buflen,
			format, args);
L
Linus Torvalds 已提交
359 360
	va_end(args);

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

366 367
	env->envp[env->envp_idx++] = &env->buf[env->buflen];
	env->buflen += len + 1;
L
Linus Torvalds 已提交
368 369
	return 0;
}
370
EXPORT_SYMBOL_GPL(add_uevent_var);
L
Linus Torvalds 已提交
371

K
Kay Sievers 已提交
372
#if defined(CONFIG_NET)
373
static int uevent_net_init(struct net *net)
374
{
375 376 377 378 379 380 381 382 383
	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) {
384 385
		printk(KERN_ERR
		       "kobject_uevent: unable to create netlink socket!\n");
386
		kfree(ue_sk);
387 388
		return -ENODEV;
	}
389 390 391
	mutex_lock(&uevent_sock_mutex);
	list_add_tail(&ue_sk->list, &uevent_sock_list);
	mutex_unlock(&uevent_sock_mutex);
392 393 394
	return 0;
}

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 421 422 423 424 425 426
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);
}


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