kobject_uevent.c 7.9 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 184 185 186 187 188 189 190 191 192 193
	/*
	 * 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;

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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