kevent: add block mount and umount support
[linux-flexiantxendom0-3.2.10.git] / lib / kobject_uevent.c
1 /*
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>
22 #include <linux/kobject_uevent.h>
23 #include <linux/kobject.h>
24 #include <net/sock.h>
25
26 /* 
27  * These must match up with the values for enum kobject_action
28  * as found in include/linux/kobject_uevent.h
29  */
30 static char *actions[] = {
31         "add",          /* 0x00 */
32         "remove",       /* 0x01 */
33         "change",       /* 0x02 */
34         "mount",        /* 0x03 */
35         "umount",       /* 0x04 */
36 };
37
38 static char *action_to_string(enum kobject_action action)
39 {
40         if (action >= KOBJ_MAX_ACTION)
41                 return NULL;
42         else
43                 return actions[action];
44 }
45
46 #ifdef CONFIG_KOBJECT_UEVENT
47 static struct sock *uevent_sock;
48
49 /**
50  * send_uevent - notify userspace by sending event trough netlink socket
51  *
52  * @signal: signal name
53  * @obj: object path (kobject)
54  * @buf: buffer used to pass auxiliary data like the hotplug environment
55  * @buflen:
56  * gfp_mask:
57  */
58 static int send_uevent(const char *signal, const char *obj, const void *buf,
59                         int buflen, int gfp_mask)
60 {
61         struct sk_buff *skb;
62         char *pos;
63         int len;
64
65         if (!uevent_sock)
66                 return -EIO;
67
68         len = strlen(signal) + 1;
69         len += strlen(obj) + 1;
70         len += buflen;
71
72         skb = alloc_skb(len, gfp_mask);
73         if (!skb)
74                 return -ENOMEM;
75
76         pos = skb_put(skb, len);
77
78         pos += sprintf(pos, "%s@%s", signal, obj) + 1;
79         memcpy(pos, buf, buflen);
80
81         return netlink_broadcast(uevent_sock, skb, 0, 1, gfp_mask);
82 }
83
84 static int do_kobject_uevent(struct kobject *kobj, enum kobject_action action, 
85                              struct attribute *attr, int gfp_mask)
86 {
87         char *path;
88         char *attrpath;
89         char *signal;
90         int len;
91         int rc = -ENOMEM;
92
93         path = kobject_get_path(kobj, gfp_mask);
94         if (!path)
95                 return -ENOMEM;
96
97         signal = action_to_string(action);
98         if (!signal)
99                 return -EINVAL;
100
101         if (attr) {
102                 len = strlen(path);
103                 len += strlen(attr->name) + 2;
104                 attrpath = kmalloc(len, gfp_mask);
105                 if (!attrpath)
106                         goto exit;
107                 sprintf(attrpath, "%s/%s", path, attr->name);
108                 rc = send_uevent(signal, attrpath, NULL, 0, gfp_mask);
109                 kfree(attrpath);
110         } else {
111                 rc = send_uevent(signal, path, NULL, 0, gfp_mask);
112         }
113
114 exit:
115         kfree(path);
116         return rc;
117 }
118
119 /**
120  * kobject_uevent - notify userspace by sending event through netlink socket
121  * 
122  * @signal: signal name
123  * @kobj: struct kobject that the event is happening to
124  * @attr: optional struct attribute the event belongs to
125  */
126 int kobject_uevent(struct kobject *kobj, enum kobject_action action,
127                    struct attribute *attr)
128 {
129         return do_kobject_uevent(kobj, action, attr, GFP_KERNEL);
130 }
131 EXPORT_SYMBOL_GPL(kobject_uevent);
132
133 int kobject_uevent_atomic(struct kobject *kobj, enum kobject_action action,
134                           struct attribute *attr)
135 {
136         return do_kobject_uevent(kobj, action, attr, GFP_ATOMIC);
137 }
138
139 EXPORT_SYMBOL_GPL(kobject_uevent_atomic);
140
141 static int __init kobject_uevent_init(void)
142 {
143         uevent_sock = netlink_kernel_create(NETLINK_KOBJECT_UEVENT, NULL);
144
145         if (!uevent_sock) {
146                 printk(KERN_ERR
147                        "kobject_uevent: unable to create netlink socket!\n");
148                 return -ENODEV;
149         }
150
151         return 0;
152 }
153
154 core_initcall(kobject_uevent_init);
155
156 #else
157 static inline int send_uevent(const char *signal, const char *obj,
158                               const void *buf, int buflen, int gfp_mask)
159 {
160         return 0;
161 }
162
163 #endif /* CONFIG_KOBJECT_UEVENT */
164
165
166 #ifdef CONFIG_HOTPLUG
167 u64 hotplug_seqnum;
168 static spinlock_t sequence_lock = SPIN_LOCK_UNLOCKED;
169
170 #define BUFFER_SIZE     1024    /* should be enough memory for the env */
171 #define NUM_ENVP        32      /* number of env pointers */
172 /**
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  */
178 void kobject_hotplug(struct kobject *kobj, enum kobject_action action)
179 {
180         char *argv [3];
181         char **envp = NULL;
182         char *buffer = NULL;
183         char *scratch;
184         int i = 0;
185         int retval;
186         char *kobj_path = NULL;
187         char *name = NULL;
188         char *action_string;
189         u64 seq;
190         struct kobject *top_kobj = kobj;
191         struct kset *kset;
192
193         if (!top_kobj->kset && top_kobj->parent) {
194                 do {
195                         top_kobj = top_kobj->parent;
196                 } while (!top_kobj->kset && top_kobj->parent);
197         }
198
199         if (top_kobj->kset && top_kobj->kset->hotplug_ops)
200                 kset = top_kobj->kset;
201         else
202                 return;
203
204         /* If the kset has a filter operation, call it.
205            Skip the event, if the filter returns zero. */
206         if (kset->hotplug_ops->filter) {
207                 if (!kset->hotplug_ops->filter(kset, kobj))
208                         return;
209         }
210
211         pr_debug ("%s\n", __FUNCTION__);
212
213         action_string = action_to_string(action);
214         if (!action_string)
215                 return;
216
217         envp = kmalloc(NUM_ENVP * sizeof (char *), GFP_KERNEL);
218         if (!envp)
219                 return;
220         memset (envp, 0x00, NUM_ENVP * sizeof (char *));
221
222         buffer = kmalloc(BUFFER_SIZE, GFP_KERNEL);
223         if (!buffer)
224                 goto exit;
225
226         if (kset->hotplug_ops->name)
227                 name = kset->hotplug_ops->name(kset, kobj);
228         if (name == NULL)
229                 name = kset->kobj.name;
230
231         argv [0] = hotplug_path;
232         argv [1] = name;
233         argv [2] = NULL;
234
235         /* minimal command environment */
236         envp [i++] = "HOME=/";
237         envp [i++] = "PATH=/sbin:/bin:/usr/sbin:/usr/bin";
238
239         scratch = buffer;
240
241         envp [i++] = scratch;
242         scratch += sprintf(scratch, "ACTION=%s", action_string) + 1;
243
244         kobj_path = kobject_get_path(kobj, GFP_KERNEL);
245         if (!kobj_path)
246                 goto exit;
247
248         envp [i++] = scratch;
249         scratch += sprintf (scratch, "DEVPATH=%s", kobj_path) + 1;
250
251         spin_lock(&sequence_lock);
252         seq = ++hotplug_seqnum;
253         spin_unlock(&sequence_lock);
254
255         envp [i++] = scratch;
256         scratch += sprintf(scratch, "SEQNUM=%lld", (long long)seq) + 1;
257
258         envp [i++] = scratch;
259         scratch += sprintf(scratch, "SUBSYSTEM=%s", name) + 1;
260
261         if (kset->hotplug_ops->hotplug) {
262                 /* have the kset specific function add its stuff */
263                 retval = kset->hotplug_ops->hotplug (kset, kobj,
264                                   &envp[i], NUM_ENVP - i, scratch,
265                                   BUFFER_SIZE - (scratch - buffer));
266                 if (retval) {
267                         pr_debug ("%s - hotplug() returned %d\n",
268                                   __FUNCTION__, retval);
269                         goto exit;
270                 }
271         }
272
273         pr_debug ("%s: %s %s %s %s %s %s %s\n", __FUNCTION__, argv[0], argv[1],
274                   envp[0], envp[1], envp[2], envp[3], envp[4]);
275
276         send_uevent(action_string, kobj_path, buffer, scratch - buffer, GFP_KERNEL);
277
278         if (!hotplug_path[0])
279                 goto exit;
280
281         retval = call_usermodehelper (argv[0], argv, envp, 0);
282         if (retval)
283                 pr_debug ("%s - call_usermodehelper returned %d\n",
284                           __FUNCTION__, retval);
285
286 exit:
287         kfree(kobj_path);
288         kfree(buffer);
289         kfree(envp);
290         return;
291 }
292 EXPORT_SYMBOL(kobject_hotplug);
293 #endif /* CONFIG_HOTPLUG */
294
295