~ubuntu-branches/ubuntu/precise/linux-lowlatency/precise

« back to all changes in this revision

Viewing changes to lib/kobject_uevent.c

  • Committer: Package Import Robot
  • Author(s): Alessio Igor Bogani
  • Date: 2011-10-26 11:13:05 UTC
  • Revision ID: package-import@ubuntu.com-20111026111305-tz023xykf0i6eosh
Tags: upstream-3.2.0
ImportĀ upstreamĀ versionĀ 3.2.0

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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/string.h>
 
19
#include <linux/kobject.h>
 
20
#include <linux/module.h>
 
21
#include <linux/slab.h>
 
22
#include <linux/user_namespace.h>
 
23
#include <linux/socket.h>
 
24
#include <linux/skbuff.h>
 
25
#include <linux/netlink.h>
 
26
#include <net/sock.h>
 
27
#include <net/net_namespace.h>
 
28
 
 
29
 
 
30
u64 uevent_seqnum;
 
31
char uevent_helper[UEVENT_HELPER_PATH_LEN] = CONFIG_UEVENT_HELPER_PATH;
 
32
static DEFINE_SPINLOCK(sequence_lock);
 
33
#ifdef CONFIG_NET
 
34
struct uevent_sock {
 
35
        struct list_head list;
 
36
        struct sock *sk;
 
37
};
 
38
static LIST_HEAD(uevent_sock_list);
 
39
static DEFINE_MUTEX(uevent_sock_mutex);
 
40
#endif
 
41
 
 
42
/* the strings here must match the enum in include/linux/kobject.h */
 
43
static const char *kobject_actions[] = {
 
44
        [KOBJ_ADD] =            "add",
 
45
        [KOBJ_REMOVE] =         "remove",
 
46
        [KOBJ_CHANGE] =         "change",
 
47
        [KOBJ_MOVE] =           "move",
 
48
        [KOBJ_ONLINE] =         "online",
 
49
        [KOBJ_OFFLINE] =        "offline",
 
50
};
 
51
 
 
52
/**
 
53
 * kobject_action_type - translate action string to numeric type
 
54
 *
 
55
 * @buf: buffer containing the action string, newline is ignored
 
56
 * @len: length of buffer
 
57
 * @type: pointer to the location to store the action type
 
58
 *
 
59
 * Returns 0 if the action string was recognized.
 
60
 */
 
61
int kobject_action_type(const char *buf, size_t count,
 
62
                        enum kobject_action *type)
 
63
{
 
64
        enum kobject_action action;
 
65
        int ret = -EINVAL;
 
66
 
 
67
        if (count && (buf[count-1] == '\n' || buf[count-1] == '\0'))
 
68
                count--;
 
69
 
 
70
        if (!count)
 
71
                goto out;
 
72
 
 
73
        for (action = 0; action < ARRAY_SIZE(kobject_actions); action++) {
 
74
                if (strncmp(kobject_actions[action], buf, count) != 0)
 
75
                        continue;
 
76
                if (kobject_actions[action][count] != '\0')
 
77
                        continue;
 
78
                *type = action;
 
79
                ret = 0;
 
80
                break;
 
81
        }
 
82
out:
 
83
        return ret;
 
84
}
 
85
 
 
86
#ifdef CONFIG_NET
 
87
static int kobj_bcast_filter(struct sock *dsk, struct sk_buff *skb, void *data)
 
88
{
 
89
        struct kobject *kobj = data;
 
90
        const struct kobj_ns_type_operations *ops;
 
91
 
 
92
        ops = kobj_ns_ops(kobj);
 
93
        if (ops) {
 
94
                const void *sock_ns, *ns;
 
95
                ns = kobj->ktype->namespace(kobj);
 
96
                sock_ns = ops->netlink_ns(dsk);
 
97
                return sock_ns != ns;
 
98
        }
 
99
 
 
100
        return 0;
 
101
}
 
102
#endif
 
103
 
 
104
static int kobj_usermode_filter(struct kobject *kobj)
 
105
{
 
106
        const struct kobj_ns_type_operations *ops;
 
107
 
 
108
        ops = kobj_ns_ops(kobj);
 
109
        if (ops) {
 
110
                const void *init_ns, *ns;
 
111
                ns = kobj->ktype->namespace(kobj);
 
112
                init_ns = ops->initial_ns();
 
113
                return ns != init_ns;
 
114
        }
 
115
 
 
116
        return 0;
 
117
}
 
118
 
 
119
/**
 
120
 * kobject_uevent_env - send an uevent with environmental data
 
121
 *
 
122
 * @action: action that is happening
 
123
 * @kobj: struct kobject that the action is happening to
 
124
 * @envp_ext: pointer to environmental data
 
125
 *
 
126
 * Returns 0 if kobject_uevent_env() is completed with success or the
 
127
 * corresponding error when it fails.
 
128
 */
 
129
int kobject_uevent_env(struct kobject *kobj, enum kobject_action action,
 
130
                       char *envp_ext[])
 
131
{
 
132
        struct kobj_uevent_env *env;
 
133
        const char *action_string = kobject_actions[action];
 
134
        const char *devpath = NULL;
 
135
        const char *subsystem;
 
136
        struct kobject *top_kobj;
 
137
        struct kset *kset;
 
138
        const struct kset_uevent_ops *uevent_ops;
 
139
        u64 seq;
 
140
        int i = 0;
 
141
        int retval = 0;
 
142
#ifdef CONFIG_NET
 
143
        struct uevent_sock *ue_sk;
 
144
#endif
 
145
 
 
146
        pr_debug("kobject: '%s' (%p): %s\n",
 
147
                 kobject_name(kobj), kobj, __func__);
 
148
 
 
149
        /* search the kset we belong to */
 
150
        top_kobj = kobj;
 
151
        while (!top_kobj->kset && top_kobj->parent)
 
152
                top_kobj = top_kobj->parent;
 
153
 
 
154
        if (!top_kobj->kset) {
 
155
                pr_debug("kobject: '%s' (%p): %s: attempted to send uevent "
 
156
                         "without kset!\n", kobject_name(kobj), kobj,
 
157
                         __func__);
 
158
                return -EINVAL;
 
159
        }
 
160
 
 
161
        kset = top_kobj->kset;
 
162
        uevent_ops = kset->uevent_ops;
 
163
 
 
164
        /* skip the event, if uevent_suppress is set*/
 
165
        if (kobj->uevent_suppress) {
 
166
                pr_debug("kobject: '%s' (%p): %s: uevent_suppress "
 
167
                                 "caused the event to drop!\n",
 
168
                                 kobject_name(kobj), kobj, __func__);
 
169
                return 0;
 
170
        }
 
171
        /* skip the event, if the filter returns zero. */
 
172
        if (uevent_ops && uevent_ops->filter)
 
173
                if (!uevent_ops->filter(kset, kobj)) {
 
174
                        pr_debug("kobject: '%s' (%p): %s: filter function "
 
175
                                 "caused the event to drop!\n",
 
176
                                 kobject_name(kobj), kobj, __func__);
 
177
                        return 0;
 
178
                }
 
179
 
 
180
        /* originating subsystem */
 
181
        if (uevent_ops && uevent_ops->name)
 
182
                subsystem = uevent_ops->name(kset, kobj);
 
183
        else
 
184
                subsystem = kobject_name(&kset->kobj);
 
185
        if (!subsystem) {
 
186
                pr_debug("kobject: '%s' (%p): %s: unset subsystem caused the "
 
187
                         "event to drop!\n", kobject_name(kobj), kobj,
 
188
                         __func__);
 
189
                return 0;
 
190
        }
 
191
 
 
192
        /* environment buffer */
 
193
        env = kzalloc(sizeof(struct kobj_uevent_env), GFP_KERNEL);
 
194
        if (!env)
 
195
                return -ENOMEM;
 
196
 
 
197
        /* complete object path */
 
198
        devpath = kobject_get_path(kobj, GFP_KERNEL);
 
199
        if (!devpath) {
 
200
                retval = -ENOENT;
 
201
                goto exit;
 
202
        }
 
203
 
 
204
        /* default keys */
 
205
        retval = add_uevent_var(env, "ACTION=%s", action_string);
 
206
        if (retval)
 
207
                goto exit;
 
208
        retval = add_uevent_var(env, "DEVPATH=%s", devpath);
 
209
        if (retval)
 
210
                goto exit;
 
211
        retval = add_uevent_var(env, "SUBSYSTEM=%s", subsystem);
 
212
        if (retval)
 
213
                goto exit;
 
214
 
 
215
        /* keys passed in from the caller */
 
216
        if (envp_ext) {
 
217
                for (i = 0; envp_ext[i]; i++) {
 
218
                        retval = add_uevent_var(env, "%s", envp_ext[i]);
 
219
                        if (retval)
 
220
                                goto exit;
 
221
                }
 
222
        }
 
223
 
 
224
        /* let the kset specific function add its stuff */
 
225
        if (uevent_ops && uevent_ops->uevent) {
 
226
                retval = uevent_ops->uevent(kset, kobj, env);
 
227
                if (retval) {
 
228
                        pr_debug("kobject: '%s' (%p): %s: uevent() returned "
 
229
                                 "%d\n", kobject_name(kobj), kobj,
 
230
                                 __func__, retval);
 
231
                        goto exit;
 
232
                }
 
233
        }
 
234
 
 
235
        /*
 
236
         * Mark "add" and "remove" events in the object to ensure proper
 
237
         * events to userspace during automatic cleanup. If the object did
 
238
         * send an "add" event, "remove" will automatically generated by
 
239
         * the core, if not already done by the caller.
 
240
         */
 
241
        if (action == KOBJ_ADD)
 
242
                kobj->state_add_uevent_sent = 1;
 
243
        else if (action == KOBJ_REMOVE)
 
244
                kobj->state_remove_uevent_sent = 1;
 
245
 
 
246
        /* we will send an event, so request a new sequence number */
 
247
        spin_lock(&sequence_lock);
 
248
        seq = ++uevent_seqnum;
 
249
        spin_unlock(&sequence_lock);
 
250
        retval = add_uevent_var(env, "SEQNUM=%llu", (unsigned long long)seq);
 
251
        if (retval)
 
252
                goto exit;
 
253
 
 
254
#if defined(CONFIG_NET)
 
255
        /* send netlink message */
 
256
        mutex_lock(&uevent_sock_mutex);
 
257
        list_for_each_entry(ue_sk, &uevent_sock_list, list) {
 
258
                struct sock *uevent_sock = ue_sk->sk;
 
259
                struct sk_buff *skb;
 
260
                size_t len;
 
261
 
 
262
                /* allocate message with the maximum possible size */
 
263
                len = strlen(action_string) + strlen(devpath) + 2;
 
264
                skb = alloc_skb(len + env->buflen, GFP_KERNEL);
 
265
                if (skb) {
 
266
                        char *scratch;
 
267
 
 
268
                        /* add header */
 
269
                        scratch = skb_put(skb, len);
 
270
                        sprintf(scratch, "%s@%s", action_string, devpath);
 
271
 
 
272
                        /* copy keys to our continuous event payload buffer */
 
273
                        for (i = 0; i < env->envp_idx; i++) {
 
274
                                len = strlen(env->envp[i]) + 1;
 
275
                                scratch = skb_put(skb, len);
 
276
                                strcpy(scratch, env->envp[i]);
 
277
                        }
 
278
 
 
279
                        NETLINK_CB(skb).dst_group = 1;
 
280
                        retval = netlink_broadcast_filtered(uevent_sock, skb,
 
281
                                                            0, 1, GFP_KERNEL,
 
282
                                                            kobj_bcast_filter,
 
283
                                                            kobj);
 
284
                        /* ENOBUFS should be handled in userspace */
 
285
                        if (retval == -ENOBUFS || retval == -ESRCH)
 
286
                                retval = 0;
 
287
                } else
 
288
                        retval = -ENOMEM;
 
289
        }
 
290
        mutex_unlock(&uevent_sock_mutex);
 
291
#endif
 
292
 
 
293
        /* call uevent_helper, usually only enabled during early boot */
 
294
        if (uevent_helper[0] && !kobj_usermode_filter(kobj)) {
 
295
                char *argv [3];
 
296
 
 
297
                argv [0] = uevent_helper;
 
298
                argv [1] = (char *)subsystem;
 
299
                argv [2] = NULL;
 
300
                retval = add_uevent_var(env, "HOME=/");
 
301
                if (retval)
 
302
                        goto exit;
 
303
                retval = add_uevent_var(env,
 
304
                                        "PATH=/sbin:/bin:/usr/sbin:/usr/bin");
 
305
                if (retval)
 
306
                        goto exit;
 
307
 
 
308
                retval = call_usermodehelper(argv[0], argv,
 
309
                                             env->envp, UMH_WAIT_EXEC);
 
310
        }
 
311
 
 
312
exit:
 
313
        kfree(devpath);
 
314
        kfree(env);
 
315
        return retval;
 
316
}
 
317
EXPORT_SYMBOL_GPL(kobject_uevent_env);
 
318
 
 
319
/**
 
320
 * kobject_uevent - notify userspace by sending an uevent
 
321
 *
 
322
 * @action: action that is happening
 
323
 * @kobj: struct kobject that the action is happening to
 
324
 *
 
325
 * Returns 0 if kobject_uevent() is completed with success or the
 
326
 * corresponding error when it fails.
 
327
 */
 
328
int kobject_uevent(struct kobject *kobj, enum kobject_action action)
 
329
{
 
330
        return kobject_uevent_env(kobj, action, NULL);
 
331
}
 
332
EXPORT_SYMBOL_GPL(kobject_uevent);
 
333
 
 
334
/**
 
335
 * add_uevent_var - add key value string to the environment buffer
 
336
 * @env: environment buffer structure
 
337
 * @format: printf format for the key=value pair
 
338
 *
 
339
 * Returns 0 if environment variable was added successfully or -ENOMEM
 
340
 * if no space was available.
 
341
 */
 
342
int add_uevent_var(struct kobj_uevent_env *env, const char *format, ...)
 
343
{
 
344
        va_list args;
 
345
        int len;
 
346
 
 
347
        if (env->envp_idx >= ARRAY_SIZE(env->envp)) {
 
348
                WARN(1, KERN_ERR "add_uevent_var: too many keys\n");
 
349
                return -ENOMEM;
 
350
        }
 
351
 
 
352
        va_start(args, format);
 
353
        len = vsnprintf(&env->buf[env->buflen],
 
354
                        sizeof(env->buf) - env->buflen,
 
355
                        format, args);
 
356
        va_end(args);
 
357
 
 
358
        if (len >= (sizeof(env->buf) - env->buflen)) {
 
359
                WARN(1, KERN_ERR "add_uevent_var: buffer size too small\n");
 
360
                return -ENOMEM;
 
361
        }
 
362
 
 
363
        env->envp[env->envp_idx++] = &env->buf[env->buflen];
 
364
        env->buflen += len + 1;
 
365
        return 0;
 
366
}
 
367
EXPORT_SYMBOL_GPL(add_uevent_var);
 
368
 
 
369
#if defined(CONFIG_NET)
 
370
static int uevent_net_init(struct net *net)
 
371
{
 
372
        struct uevent_sock *ue_sk;
 
373
 
 
374
        ue_sk = kzalloc(sizeof(*ue_sk), GFP_KERNEL);
 
375
        if (!ue_sk)
 
376
                return -ENOMEM;
 
377
 
 
378
        ue_sk->sk = netlink_kernel_create(net, NETLINK_KOBJECT_UEVENT,
 
379
                                          1, NULL, NULL, THIS_MODULE);
 
380
        if (!ue_sk->sk) {
 
381
                printk(KERN_ERR
 
382
                       "kobject_uevent: unable to create netlink socket!\n");
 
383
                kfree(ue_sk);
 
384
                return -ENODEV;
 
385
        }
 
386
        mutex_lock(&uevent_sock_mutex);
 
387
        list_add_tail(&ue_sk->list, &uevent_sock_list);
 
388
        mutex_unlock(&uevent_sock_mutex);
 
389
        return 0;
 
390
}
 
391
 
 
392
static void uevent_net_exit(struct net *net)
 
393
{
 
394
        struct uevent_sock *ue_sk;
 
395
 
 
396
        mutex_lock(&uevent_sock_mutex);
 
397
        list_for_each_entry(ue_sk, &uevent_sock_list, list) {
 
398
                if (sock_net(ue_sk->sk) == net)
 
399
                        goto found;
 
400
        }
 
401
        mutex_unlock(&uevent_sock_mutex);
 
402
        return;
 
403
 
 
404
found:
 
405
        list_del(&ue_sk->list);
 
406
        mutex_unlock(&uevent_sock_mutex);
 
407
 
 
408
        netlink_kernel_release(ue_sk->sk);
 
409
        kfree(ue_sk);
 
410
}
 
411
 
 
412
static struct pernet_operations uevent_net_ops = {
 
413
        .init   = uevent_net_init,
 
414
        .exit   = uevent_net_exit,
 
415
};
 
416
 
 
417
static int __init kobject_uevent_init(void)
 
418
{
 
419
        netlink_set_nonroot(NETLINK_KOBJECT_UEVENT, NL_NONROOT_RECV);
 
420
        return register_pernet_subsys(&uevent_net_ops);
 
421
}
 
422
 
 
423
 
 
424
postcore_initcall(kobject_uevent_init);
 
425
#endif