blob: 1f90eea7eebc1d26d677ec0967ac34747c3d4d41 [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
2 * kernel userspace event delivery
3 *
4 * Copyright (C) 2004 Red Hat, Inc. All rights reserved.
5 * Copyright (C) 2004 Novell, Inc. All rights reserved.
6 * Copyright (C) 2004 IBM, Inc. All rights reserved.
7 *
8 * Licensed under the GNU GPL v2.
9 *
10 * Authors:
11 * Robert Love <rml@novell.com>
12 * Kay Sievers <kay.sievers@vrfy.org>
13 * Arjan van de Ven <arjanv@redhat.com>
14 * Greg Kroah-Hartman <greg@kroah.com>
15 */
16
17#include <linux/spinlock.h>
18#include <linux/socket.h>
19#include <linux/skbuff.h>
20#include <linux/netlink.h>
21#include <linux/string.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070022#include <linux/kobject.h>
23#include <net/sock.h>
24
25#define BUFFER_SIZE 1024 /* buffer for the hotplug env */
26#define NUM_ENVP 32 /* number of env pointers */
27
Kay Sievers0296b222005-11-11 05:33:52 +010028#if defined(CONFIG_HOTPLUG)
29char hotplug_path[HOTPLUG_PATH_LEN] = "/sbin/hotplug";
30u64 hotplug_seqnum;
31static DEFINE_SPINLOCK(sequence_lock);
32
Linus Torvalds1da177e2005-04-16 15:20:36 -070033static char *action_to_string(enum kobject_action action)
34{
35 switch (action) {
36 case KOBJ_ADD:
37 return "add";
38 case KOBJ_REMOVE:
39 return "remove";
40 case KOBJ_CHANGE:
41 return "change";
42 case KOBJ_MOUNT:
43 return "mount";
44 case KOBJ_UMOUNT:
45 return "umount";
46 case KOBJ_OFFLINE:
47 return "offline";
48 case KOBJ_ONLINE:
49 return "online";
50 default:
51 return NULL;
52 }
53}
Linus Torvalds1da177e2005-04-16 15:20:36 -070054
Linus Torvalds1da177e2005-04-16 15:20:36 -070055static struct sock *uevent_sock;
56
57/**
Erik Hovland4ed17dc2005-10-06 10:45:30 -070058 * send_uevent - notify userspace by sending event through netlink socket
Linus Torvalds1da177e2005-04-16 15:20:36 -070059 *
60 * @signal: signal name
61 * @obj: object path (kobject)
62 * @envp: possible hotplug environment to pass with the message
63 * @gfp_mask:
64 */
65static int send_uevent(const char *signal, const char *obj,
Al Virofd4f2df2005-10-21 03:18:50 -040066 char **envp, gfp_t gfp_mask)
Linus Torvalds1da177e2005-04-16 15:20:36 -070067{
68 struct sk_buff *skb;
69 char *pos;
70 int len;
71
72 if (!uevent_sock)
73 return -EIO;
74
75 len = strlen(signal) + 1;
76 len += strlen(obj) + 1;
77
78 /* allocate buffer with the maximum possible message size */
79 skb = alloc_skb(len + BUFFER_SIZE, gfp_mask);
80 if (!skb)
81 return -ENOMEM;
82
83 pos = skb_put(skb, len);
84 sprintf(pos, "%s@%s", signal, obj);
85
86 /* copy the environment key by key to our continuous buffer */
87 if (envp) {
88 int i;
89
90 for (i = 2; envp[i]; i++) {
91 len = strlen(envp[i]) + 1;
92 pos = skb_put(skb, len);
93 strcpy(pos, envp[i]);
94 }
95 }
96
Patrick McHardyac6d4392005-08-14 19:29:52 -070097 NETLINK_CB(skb).dst_group = 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -070098 return netlink_broadcast(uevent_sock, skb, 0, 1, gfp_mask);
99}
100
101static int do_kobject_uevent(struct kobject *kobj, enum kobject_action action,
Al Virofd4f2df2005-10-21 03:18:50 -0400102 struct attribute *attr, gfp_t gfp_mask)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700103{
104 char *path;
105 char *attrpath;
106 char *signal;
107 int len;
108 int rc = -ENOMEM;
109
110 path = kobject_get_path(kobj, gfp_mask);
111 if (!path)
112 return -ENOMEM;
113
114 signal = action_to_string(action);
115 if (!signal)
116 return -EINVAL;
117
118 if (attr) {
119 len = strlen(path);
120 len += strlen(attr->name) + 2;
121 attrpath = kmalloc(len, gfp_mask);
122 if (!attrpath)
123 goto exit;
124 sprintf(attrpath, "%s/%s", path, attr->name);
125 rc = send_uevent(signal, attrpath, NULL, gfp_mask);
126 kfree(attrpath);
127 } else
128 rc = send_uevent(signal, path, NULL, gfp_mask);
129
130exit:
131 kfree(path);
132 return rc;
133}
134
135/**
136 * kobject_uevent - notify userspace by sending event through netlink socket
137 *
138 * @signal: signal name
139 * @kobj: struct kobject that the event is happening to
140 * @attr: optional struct attribute the event belongs to
141 */
142int kobject_uevent(struct kobject *kobj, enum kobject_action action,
143 struct attribute *attr)
144{
145 return do_kobject_uevent(kobj, action, attr, GFP_KERNEL);
146}
147EXPORT_SYMBOL_GPL(kobject_uevent);
148
149int kobject_uevent_atomic(struct kobject *kobj, enum kobject_action action,
150 struct attribute *attr)
151{
152 return do_kobject_uevent(kobj, action, attr, GFP_ATOMIC);
153}
154EXPORT_SYMBOL_GPL(kobject_uevent_atomic);
155
156static int __init kobject_uevent_init(void)
157{
Patrick McHardy06628602005-08-15 12:33:26 -0700158 uevent_sock = netlink_kernel_create(NETLINK_KOBJECT_UEVENT, 1, NULL,
Harald Welte4fdb3bb2005-08-09 19:40:55 -0700159 THIS_MODULE);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700160
161 if (!uevent_sock) {
162 printk(KERN_ERR
163 "kobject_uevent: unable to create netlink socket!\n");
164 return -ENODEV;
165 }
166
167 return 0;
168}
169
170postcore_initcall(kobject_uevent_init);
171
Linus Torvalds1da177e2005-04-16 15:20:36 -0700172/**
173 * kobject_hotplug - notify userspace by executing /sbin/hotplug
174 *
175 * @action: action that is happening (usually "ADD" or "REMOVE")
176 * @kobj: struct kobject that the action is happening to
177 */
178void kobject_hotplug(struct kobject *kobj, enum kobject_action action)
179{
180 char *argv [3];
181 char **envp = NULL;
182 char *buffer = NULL;
183 char *seq_buff;
184 char *scratch;
185 int i = 0;
186 int retval;
187 char *kobj_path = NULL;
Dmitry Torokhovf3b4f3c2005-04-26 02:32:00 -0500188 const char *name = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700189 char *action_string;
190 u64 seq;
191 struct kobject *top_kobj = kobj;
192 struct kset *kset;
193 static struct kset_hotplug_ops null_hotplug_ops;
194 struct kset_hotplug_ops *hotplug_ops = &null_hotplug_ops;
195
196 /* If this kobj does not belong to a kset,
197 try to find a parent that does. */
198 if (!top_kobj->kset && top_kobj->parent) {
199 do {
200 top_kobj = top_kobj->parent;
201 } while (!top_kobj->kset && top_kobj->parent);
202 }
203
204 if (top_kobj->kset)
205 kset = top_kobj->kset;
206 else
207 return;
208
209 if (kset->hotplug_ops)
210 hotplug_ops = kset->hotplug_ops;
211
212 /* If the kset has a filter operation, call it.
213 Skip the event, if the filter returns zero. */
214 if (hotplug_ops->filter) {
215 if (!hotplug_ops->filter(kset, kobj))
216 return;
217 }
218
219 pr_debug ("%s\n", __FUNCTION__);
220
221 action_string = action_to_string(action);
222 if (!action_string)
223 return;
224
225 envp = kmalloc(NUM_ENVP * sizeof (char *), GFP_KERNEL);
226 if (!envp)
227 return;
228 memset (envp, 0x00, NUM_ENVP * sizeof (char *));
229
230 buffer = kmalloc(BUFFER_SIZE, GFP_KERNEL);
231 if (!buffer)
232 goto exit;
233
234 if (hotplug_ops->name)
235 name = hotplug_ops->name(kset, kobj);
236 if (name == NULL)
Dmitry Torokhoveb11d8f2005-04-26 02:29:58 -0500237 name = kobject_name(&kset->kobj);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700238
239 argv [0] = hotplug_path;
Dmitry Torokhovf3b4f3c2005-04-26 02:32:00 -0500240 argv [1] = (char *)name; /* won't be changed but 'const' has to go */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700241 argv [2] = NULL;
242
243 /* minimal command environment */
244 envp [i++] = "HOME=/";
245 envp [i++] = "PATH=/sbin:/bin:/usr/sbin:/usr/bin";
246
247 scratch = buffer;
248
249 envp [i++] = scratch;
250 scratch += sprintf(scratch, "ACTION=%s", action_string) + 1;
251
252 kobj_path = kobject_get_path(kobj, GFP_KERNEL);
253 if (!kobj_path)
254 goto exit;
255
256 envp [i++] = scratch;
257 scratch += sprintf (scratch, "DEVPATH=%s", kobj_path) + 1;
258
259 envp [i++] = scratch;
260 scratch += sprintf(scratch, "SUBSYSTEM=%s", name) + 1;
261
262 /* reserve space for the sequence,
263 * put the real one in after the hotplug call */
264 envp[i++] = seq_buff = scratch;
265 scratch += strlen("SEQNUM=18446744073709551616") + 1;
266
267 if (hotplug_ops->hotplug) {
268 /* have the kset specific function add its stuff */
269 retval = hotplug_ops->hotplug (kset, kobj,
270 &envp[i], NUM_ENVP - i, scratch,
271 BUFFER_SIZE - (scratch - buffer));
272 if (retval) {
273 pr_debug ("%s - hotplug() returned %d\n",
274 __FUNCTION__, retval);
275 goto exit;
276 }
277 }
278
279 spin_lock(&sequence_lock);
280 seq = ++hotplug_seqnum;
281 spin_unlock(&sequence_lock);
282 sprintf(seq_buff, "SEQNUM=%llu", (unsigned long long)seq);
283
284 pr_debug ("%s: %s %s seq=%llu %s %s %s %s %s\n",
285 __FUNCTION__, argv[0], argv[1], (unsigned long long)seq,
286 envp[0], envp[1], envp[2], envp[3], envp[4]);
287
288 send_uevent(action_string, kobj_path, envp, GFP_KERNEL);
289
290 if (!hotplug_path[0])
291 goto exit;
292
293 retval = call_usermodehelper (argv[0], argv, envp, 0);
294 if (retval)
295 pr_debug ("%s - call_usermodehelper returned %d\n",
296 __FUNCTION__, retval);
297
298exit:
299 kfree(kobj_path);
300 kfree(buffer);
301 kfree(envp);
302 return;
303}
304EXPORT_SYMBOL(kobject_hotplug);
305
306/**
307 * add_hotplug_env_var - helper for creating hotplug environment variables
308 * @envp: Pointer to table of environment variables, as passed into
309 * hotplug() method.
310 * @num_envp: Number of environment variable slots available, as
311 * passed into hotplug() method.
312 * @cur_index: Pointer to current index into @envp. It should be
313 * initialized to 0 before the first call to add_hotplug_env_var(),
314 * and will be incremented on success.
315 * @buffer: Pointer to buffer for environment variables, as passed
316 * into hotplug() method.
317 * @buffer_size: Length of @buffer, as passed into hotplug() method.
318 * @cur_len: Pointer to current length of space used in @buffer.
319 * Should be initialized to 0 before the first call to
320 * add_hotplug_env_var(), and will be incremented on success.
321 * @format: Format for creating environment variable (of the form
322 * "XXX=%x") for snprintf().
323 *
324 * Returns 0 if environment variable was added successfully or -ENOMEM
325 * if no space was available.
326 */
327int add_hotplug_env_var(char **envp, int num_envp, int *cur_index,
328 char *buffer, int buffer_size, int *cur_len,
329 const char *format, ...)
330{
331 va_list args;
332
333 /*
334 * We check against num_envp - 1 to make sure there is at
335 * least one slot left after we return, since the hotplug
336 * method needs to set the last slot to NULL.
337 */
338 if (*cur_index >= num_envp - 1)
339 return -ENOMEM;
340
341 envp[*cur_index] = buffer + *cur_len;
342
343 va_start(args, format);
344 *cur_len += vsnprintf(envp[*cur_index],
345 max(buffer_size - *cur_len, 0),
346 format, args) + 1;
347 va_end(args);
348
349 if (*cur_len > buffer_size)
350 return -ENOMEM;
351
352 (*cur_index)++;
353 return 0;
354}
355EXPORT_SYMBOL(add_hotplug_env_var);
356
357#endif /* CONFIG_HOTPLUG */