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
#include <linux/string.h>
#include <linux/kobject.h>
#include <linux/module.h>

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


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

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

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

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

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

118
	kset = top_kobj->kset;
119
	uevent_ops = kset->uevent_ops;
L
Linus Torvalds 已提交
120

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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