kobject_uevent.c 7.6 KB
Newer Older
L
Linus Torvalds 已提交
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
/*
 * 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>
#include <linux/socket.h>
#include <linux/skbuff.h>
#include <linux/netlink.h>
#include <linux/string.h>
#include <linux/kobject.h>
#include <net/sock.h>


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

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

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

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

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

116
	kset = top_kobj->kset;
117
	uevent_ops = kset->uevent_ops;
L
Linus Torvalds 已提交
118

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

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

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

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

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

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

183
	/* we will send an event, so request a new sequence number */
L
Linus Torvalds 已提交
184
	spin_lock(&sequence_lock);
185
	seq = ++uevent_seqnum;
L
Linus Torvalds 已提交
186
	spin_unlock(&sequence_lock);
187 188 189
	retval = add_uevent_var(env, "SEQNUM=%llu", (unsigned long long)seq);
	if (retval)
		goto exit;
L
Linus Torvalds 已提交
190

K
Kay Sievers 已提交
191
#if defined(CONFIG_NET)
192 193 194 195 196 197 198
	/* 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;
199
		skb = alloc_skb(len + env->buflen, GFP_KERNEL);
200
		if (skb) {
201 202
			char *scratch;

203 204 205 206 207
			/* add header */
			scratch = skb_put(skb, len);
			sprintf(scratch, "%s@%s", action_string, devpath);

			/* copy keys to our continuous event payload buffer */
208 209
			for (i = 0; i < env->envp_idx; i++) {
				len = strlen(env->envp[i]) + 1;
210
				scratch = skb_put(skb, len);
211
				strcpy(scratch, env->envp[i]);
212 213 214 215 216 217
			}

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

220
	/* call uevent_helper, usually only enabled during early boot */
221
	if (uevent_helper[0]) {
222
		char *argv [3];
L
Linus Torvalds 已提交
223

224
		argv [0] = uevent_helper;
225 226
		argv [1] = (char *)subsystem;
		argv [2] = NULL;
227 228 229 230 231 232 233 234
		retval = add_uevent_var(env, "HOME=/");
		if (retval)
			goto exit;
		retval = add_uevent_var(env, "PATH=/sbin:/bin:/usr/sbin:/usr/bin");
		if (retval)
			goto exit;

		call_usermodehelper (argv[0], argv, env->envp, UMH_WAIT_EXEC);
235
	}
L
Linus Torvalds 已提交
236 237

exit:
238
	kfree(devpath);
239
	kfree(env);
240
	return retval;
L
Linus Torvalds 已提交
241
}
242 243 244 245 246 247

EXPORT_SYMBOL_GPL(kobject_uevent_env);

/**
 * kobject_uevent - notify userspace by ending an uevent
 *
248
 * @action: action that is happening
249
 * @kobj: struct kobject that the action is happening to
250 251 252
 *
 * Returns 0 if kobject_uevent() is completed with success or the
 * corresponding error when it fails.
253
 */
254
int kobject_uevent(struct kobject *kobj, enum kobject_action action)
255
{
256
	return kobject_uevent_env(kobj, action, NULL);
257 258
}

259
EXPORT_SYMBOL_GPL(kobject_uevent);
L
Linus Torvalds 已提交
260 261

/**
262 263 264
 * 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 已提交
265 266 267 268
 *
 * Returns 0 if environment variable was added successfully or -ENOMEM
 * if no space was available.
 */
269
int add_uevent_var(struct kobj_uevent_env *env, const char *format, ...)
L
Linus Torvalds 已提交
270 271
{
	va_list args;
272
	int len;
L
Linus Torvalds 已提交
273

274 275 276
	if (env->envp_idx >= ARRAY_SIZE(env->envp)) {
		printk(KERN_ERR "add_uevent_var: too many keys\n");
		WARN_ON(1);
L
Linus Torvalds 已提交
277
		return -ENOMEM;
278
	}
L
Linus Torvalds 已提交
279 280

	va_start(args, format);
281 282 283
	len = vsnprintf(&env->buf[env->buflen],
			sizeof(env->buf) - env->buflen,
			format, args);
L
Linus Torvalds 已提交
284 285
	va_end(args);

286 287 288
	if (len >= (sizeof(env->buf) - env->buflen)) {
		printk(KERN_ERR "add_uevent_var: buffer size too small\n");
		WARN_ON(1);
L
Linus Torvalds 已提交
289
		return -ENOMEM;
290
	}
L
Linus Torvalds 已提交
291

292 293
	env->envp[env->envp_idx++] = &env->buf[env->buflen];
	env->buflen += len + 1;
L
Linus Torvalds 已提交
294 295
	return 0;
}
296
EXPORT_SYMBOL_GPL(add_uevent_var);
L
Linus Torvalds 已提交
297

K
Kay Sievers 已提交
298
#if defined(CONFIG_NET)
299 300
static int __init kobject_uevent_init(void)
{
301 302
	uevent_sock = netlink_kernel_create(&init_net, NETLINK_KOBJECT_UEVENT,
					    1, NULL, NULL, THIS_MODULE);
303 304 305 306 307 308 309 310 311 312
	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 已提交
313
#endif