kobject_uevent.c 8.0 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 21 22
#include <linux/string.h>
#include <linux/kobject.h>
#include <linux/module.h>

#ifdef CONFIG_NET
L
Linus Torvalds 已提交
23 24 25 26
#include <linux/socket.h>
#include <linux/skbuff.h>
#include <linux/netlink.h>
#include <net/sock.h>
27
#endif
L
Linus Torvalds 已提交
28 29


30
u64 uevent_seqnum;
31
char uevent_helper[UEVENT_HELPER_PATH_LEN] = CONFIG_UEVENT_HELPER_PATH;
32 33 34 35 36
static DEFINE_SPINLOCK(sequence_lock);
#if defined(CONFIG_NET)
static struct sock *uevent_sock;
#endif

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

105 106
	pr_debug("kobject: '%s' (%p): %s\n",
		 kobject_name(kobj), kobj, __FUNCTION__);
107 108 109

	/* search the kset we belong to */
	top_kobj = kobj;
110
	while (!top_kobj->kset && top_kobj->parent)
111
		top_kobj = top_kobj->parent;
112

113
	if (!top_kobj->kset) {
114 115 116
		pr_debug("kobject: '%s' (%p): %s: attempted to send uevent "
			 "without kset!\n", kobject_name(kobj), kobj,
			 __FUNCTION__);
117 118
		return -EINVAL;
	}
L
Linus Torvalds 已提交
119

120
	kset = top_kobj->kset;
121
	uevent_ops = kset->uevent_ops;
L
Linus Torvalds 已提交
122

123
	/* skip the event, if the filter returns zero. */
124
	if (uevent_ops && uevent_ops->filter)
125
		if (!uevent_ops->filter(kset, kobj)) {
126 127 128
			pr_debug("kobject: '%s' (%p): %s: filter function "
				 "caused the event to drop!\n",
				 kobject_name(kobj), kobj, __FUNCTION__);
129 130
			return 0;
		}
L
Linus Torvalds 已提交
131

132 133 134 135 136 137
	/* originating subsystem */
	if (uevent_ops && uevent_ops->name)
		subsystem = uevent_ops->name(kset, kobj);
	else
		subsystem = kobject_name(&kset->kobj);
	if (!subsystem) {
138 139 140
		pr_debug("kobject: '%s' (%p): %s: unset subsystem caused the "
			 "event to drop!\n", kobject_name(kobj), kobj,
			 __FUNCTION__);
141 142 143
		return 0;
	}

144 145 146
	/* environment buffer */
	env = kzalloc(sizeof(struct kobj_uevent_env), GFP_KERNEL);
	if (!env)
147
		return -ENOMEM;
L
Linus Torvalds 已提交
148

149 150
	/* complete object path */
	devpath = kobject_get_path(kobj, GFP_KERNEL);
151 152
	if (!devpath) {
		retval = -ENOENT;
153
		goto exit;
154
	}
L
Linus Torvalds 已提交
155

156
	/* default keys */
157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174
	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 已提交
175

176
	/* let the kset specific function add its stuff */
177
	if (uevent_ops && uevent_ops->uevent) {
178
		retval = uevent_ops->uevent(kset, kobj, env);
L
Linus Torvalds 已提交
179
		if (retval) {
180 181 182
			pr_debug("kobject: '%s' (%p): %s: uevent() returned "
				 "%d\n", kobject_name(kobj), kobj,
				 __FUNCTION__, retval);
L
Linus Torvalds 已提交
183 184 185 186
			goto exit;
		}
	}

187 188 189 190 191 192 193 194 195 196 197
	/*
	 * 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;

198
	/* we will send an event, so request a new sequence number */
L
Linus Torvalds 已提交
199
	spin_lock(&sequence_lock);
200
	seq = ++uevent_seqnum;
L
Linus Torvalds 已提交
201
	spin_unlock(&sequence_lock);
202 203 204
	retval = add_uevent_var(env, "SEQNUM=%llu", (unsigned long long)seq);
	if (retval)
		goto exit;
L
Linus Torvalds 已提交
205

K
Kay Sievers 已提交
206
#if defined(CONFIG_NET)
207 208 209 210 211 212 213
	/* 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;
214
		skb = alloc_skb(len + env->buflen, GFP_KERNEL);
215
		if (skb) {
216 217
			char *scratch;

218 219 220 221 222
			/* add header */
			scratch = skb_put(skb, len);
			sprintf(scratch, "%s@%s", action_string, devpath);

			/* copy keys to our continuous event payload buffer */
223 224
			for (i = 0; i < env->envp_idx; i++) {
				len = strlen(env->envp[i]) + 1;
225
				scratch = skb_put(skb, len);
226
				strcpy(scratch, env->envp[i]);
227 228 229 230 231 232
			}

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

235
	/* call uevent_helper, usually only enabled during early boot */
236
	if (uevent_helper[0]) {
237
		char *argv [3];
L
Linus Torvalds 已提交
238

239
		argv [0] = uevent_helper;
240 241
		argv [1] = (char *)subsystem;
		argv [2] = NULL;
242 243 244
		retval = add_uevent_var(env, "HOME=/");
		if (retval)
			goto exit;
245 246
		retval = add_uevent_var(env,
					"PATH=/sbin:/bin:/usr/sbin:/usr/bin");
247 248 249
		if (retval)
			goto exit;

250
		call_usermodehelper(argv[0], argv, env->envp, UMH_WAIT_EXEC);
251
	}
L
Linus Torvalds 已提交
252 253

exit:
254
	kfree(devpath);
255
	kfree(env);
256
	return retval;
L
Linus Torvalds 已提交
257
}
258 259 260 261 262
EXPORT_SYMBOL_GPL(kobject_uevent_env);

/**
 * kobject_uevent - notify userspace by ending an uevent
 *
263
 * @action: action that is happening
264
 * @kobj: struct kobject that the action is happening to
265 266 267
 *
 * Returns 0 if kobject_uevent() is completed with success or the
 * corresponding error when it fails.
268
 */
269
int kobject_uevent(struct kobject *kobj, enum kobject_action action)
270
{
271
	return kobject_uevent_env(kobj, action, NULL);
272
}
273
EXPORT_SYMBOL_GPL(kobject_uevent);
L
Linus Torvalds 已提交
274 275

/**
276 277 278
 * 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 已提交
279 280 281 282
 *
 * Returns 0 if environment variable was added successfully or -ENOMEM
 * if no space was available.
 */
283
int add_uevent_var(struct kobj_uevent_env *env, const char *format, ...)
L
Linus Torvalds 已提交
284 285
{
	va_list args;
286
	int len;
L
Linus Torvalds 已提交
287

288 289 290
	if (env->envp_idx >= ARRAY_SIZE(env->envp)) {
		printk(KERN_ERR "add_uevent_var: too many keys\n");
		WARN_ON(1);
L
Linus Torvalds 已提交
291
		return -ENOMEM;
292
	}
L
Linus Torvalds 已提交
293 294

	va_start(args, format);
295 296 297
	len = vsnprintf(&env->buf[env->buflen],
			sizeof(env->buf) - env->buflen,
			format, args);
L
Linus Torvalds 已提交
298 299
	va_end(args);

300 301 302
	if (len >= (sizeof(env->buf) - env->buflen)) {
		printk(KERN_ERR "add_uevent_var: buffer size too small\n");
		WARN_ON(1);
L
Linus Torvalds 已提交
303
		return -ENOMEM;
304
	}
L
Linus Torvalds 已提交
305

306 307
	env->envp[env->envp_idx++] = &env->buf[env->buflen];
	env->buflen += len + 1;
L
Linus Torvalds 已提交
308 309
	return 0;
}
310
EXPORT_SYMBOL_GPL(add_uevent_var);
L
Linus Torvalds 已提交
311

K
Kay Sievers 已提交
312
#if defined(CONFIG_NET)
313 314
static int __init kobject_uevent_init(void)
{
315 316
	uevent_sock = netlink_kernel_create(&init_net, NETLINK_KOBJECT_UEVENT,
					    1, NULL, NULL, THIS_MODULE);
317 318 319 320 321 322 323 324 325 326
	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 已提交
327
#endif