diff options
Diffstat (limited to 'lib/kobject_uevent.c')
-rw-r--r-- | lib/kobject_uevent.c | 119 |
1 files changed, 104 insertions, 15 deletions
diff --git a/lib/kobject_uevent.c b/lib/kobject_uevent.c index 920a3ca6e259..70af0a7f97c0 100644 --- a/lib/kobject_uevent.c +++ b/lib/kobject_uevent.c | |||
@@ -18,18 +18,25 @@ | |||
18 | #include <linux/string.h> | 18 | #include <linux/string.h> |
19 | #include <linux/kobject.h> | 19 | #include <linux/kobject.h> |
20 | #include <linux/module.h> | 20 | #include <linux/module.h> |
21 | 21 | #include <linux/slab.h> | |
22 | #include <linux/user_namespace.h> | ||
22 | #include <linux/socket.h> | 23 | #include <linux/socket.h> |
23 | #include <linux/skbuff.h> | 24 | #include <linux/skbuff.h> |
24 | #include <linux/netlink.h> | 25 | #include <linux/netlink.h> |
25 | #include <net/sock.h> | 26 | #include <net/sock.h> |
27 | #include <net/net_namespace.h> | ||
26 | 28 | ||
27 | 29 | ||
28 | u64 uevent_seqnum; | 30 | u64 uevent_seqnum; |
29 | char uevent_helper[UEVENT_HELPER_PATH_LEN] = CONFIG_UEVENT_HELPER_PATH; | 31 | char uevent_helper[UEVENT_HELPER_PATH_LEN] = CONFIG_UEVENT_HELPER_PATH; |
30 | static DEFINE_SPINLOCK(sequence_lock); | 32 | static DEFINE_SPINLOCK(sequence_lock); |
31 | #if defined(CONFIG_NET) | 33 | #ifdef CONFIG_NET |
32 | static struct sock *uevent_sock; | 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); | ||
33 | #endif | 40 | #endif |
34 | 41 | ||
35 | /* the strings here must match the enum in include/linux/kobject.h */ | 42 | /* the strings here must match the enum in include/linux/kobject.h */ |
@@ -76,6 +83,39 @@ out: | |||
76 | return ret; | 83 | return ret; |
77 | } | 84 | } |
78 | 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 | |||
79 | /** | 119 | /** |
80 | * kobject_uevent_env - send an uevent with environmental data | 120 | * kobject_uevent_env - send an uevent with environmental data |
81 | * | 121 | * |
@@ -83,7 +123,7 @@ out: | |||
83 | * @kobj: struct kobject that the action is happening to | 123 | * @kobj: struct kobject that the action is happening to |
84 | * @envp_ext: pointer to environmental data | 124 | * @envp_ext: pointer to environmental data |
85 | * | 125 | * |
86 | * Returns 0 if kobject_uevent() is completed with success or the | 126 | * Returns 0 if kobject_uevent_env() is completed with success or the |
87 | * corresponding error when it fails. | 127 | * corresponding error when it fails. |
88 | */ | 128 | */ |
89 | int kobject_uevent_env(struct kobject *kobj, enum kobject_action action, | 129 | int kobject_uevent_env(struct kobject *kobj, enum kobject_action action, |
@@ -95,10 +135,13 @@ int kobject_uevent_env(struct kobject *kobj, enum kobject_action action, | |||
95 | const char *subsystem; | 135 | const char *subsystem; |
96 | struct kobject *top_kobj; | 136 | struct kobject *top_kobj; |
97 | struct kset *kset; | 137 | struct kset *kset; |
98 | struct kset_uevent_ops *uevent_ops; | 138 | const struct kset_uevent_ops *uevent_ops; |
99 | u64 seq; | 139 | u64 seq; |
100 | int i = 0; | 140 | int i = 0; |
101 | int retval = 0; | 141 | int retval = 0; |
142 | #ifdef CONFIG_NET | ||
143 | struct uevent_sock *ue_sk; | ||
144 | #endif | ||
102 | 145 | ||
103 | pr_debug("kobject: '%s' (%p): %s\n", | 146 | pr_debug("kobject: '%s' (%p): %s\n", |
104 | kobject_name(kobj), kobj, __func__); | 147 | kobject_name(kobj), kobj, __func__); |
@@ -210,7 +253,9 @@ int kobject_uevent_env(struct kobject *kobj, enum kobject_action action, | |||
210 | 253 | ||
211 | #if defined(CONFIG_NET) | 254 | #if defined(CONFIG_NET) |
212 | /* send netlink message */ | 255 | /* send netlink message */ |
213 | if (uevent_sock) { | 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; | ||
214 | struct sk_buff *skb; | 259 | struct sk_buff *skb; |
215 | size_t len; | 260 | size_t len; |
216 | 261 | ||
@@ -232,18 +277,21 @@ int kobject_uevent_env(struct kobject *kobj, enum kobject_action action, | |||
232 | } | 277 | } |
233 | 278 | ||
234 | NETLINK_CB(skb).dst_group = 1; | 279 | NETLINK_CB(skb).dst_group = 1; |
235 | retval = netlink_broadcast(uevent_sock, skb, 0, 1, | 280 | retval = netlink_broadcast_filtered(uevent_sock, skb, |
236 | GFP_KERNEL); | 281 | 0, 1, GFP_KERNEL, |
282 | kobj_bcast_filter, | ||
283 | kobj); | ||
237 | /* ENOBUFS should be handled in userspace */ | 284 | /* ENOBUFS should be handled in userspace */ |
238 | if (retval == -ENOBUFS) | 285 | if (retval == -ENOBUFS) |
239 | retval = 0; | 286 | retval = 0; |
240 | } else | 287 | } else |
241 | retval = -ENOMEM; | 288 | retval = -ENOMEM; |
242 | } | 289 | } |
290 | mutex_unlock(&uevent_sock_mutex); | ||
243 | #endif | 291 | #endif |
244 | 292 | ||
245 | /* call uevent_helper, usually only enabled during early boot */ | 293 | /* call uevent_helper, usually only enabled during early boot */ |
246 | if (uevent_helper[0]) { | 294 | if (uevent_helper[0] && !kobj_usermode_filter(kobj)) { |
247 | char *argv [3]; | 295 | char *argv [3]; |
248 | 296 | ||
249 | argv [0] = uevent_helper; | 297 | argv [0] = uevent_helper; |
@@ -269,7 +317,7 @@ exit: | |||
269 | EXPORT_SYMBOL_GPL(kobject_uevent_env); | 317 | EXPORT_SYMBOL_GPL(kobject_uevent_env); |
270 | 318 | ||
271 | /** | 319 | /** |
272 | * kobject_uevent - notify userspace by ending an uevent | 320 | * kobject_uevent - notify userspace by sending an uevent |
273 | * | 321 | * |
274 | * @action: action that is happening | 322 | * @action: action that is happening |
275 | * @kobj: struct kobject that the action is happening to | 323 | * @kobj: struct kobject that the action is happening to |
@@ -319,18 +367,59 @@ int add_uevent_var(struct kobj_uevent_env *env, const char *format, ...) | |||
319 | EXPORT_SYMBOL_GPL(add_uevent_var); | 367 | EXPORT_SYMBOL_GPL(add_uevent_var); |
320 | 368 | ||
321 | #if defined(CONFIG_NET) | 369 | #if defined(CONFIG_NET) |
322 | static int __init kobject_uevent_init(void) | 370 | static int uevent_net_init(struct net *net) |
323 | { | 371 | { |
324 | uevent_sock = netlink_kernel_create(&init_net, NETLINK_KOBJECT_UEVENT, | 372 | struct uevent_sock *ue_sk; |
325 | 1, NULL, NULL, THIS_MODULE); | 373 | |
326 | if (!uevent_sock) { | 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) { | ||
327 | printk(KERN_ERR | 381 | printk(KERN_ERR |
328 | "kobject_uevent: unable to create netlink socket!\n"); | 382 | "kobject_uevent: unable to create netlink socket!\n"); |
383 | kfree(ue_sk); | ||
329 | return -ENODEV; | 384 | return -ENODEV; |
330 | } | 385 | } |
331 | netlink_set_nonroot(NETLINK_KOBJECT_UEVENT, NL_NONROOT_RECV); | 386 | mutex_lock(&uevent_sock_mutex); |
387 | list_add_tail(&ue_sk->list, &uevent_sock_list); | ||
388 | mutex_unlock(&uevent_sock_mutex); | ||
332 | return 0; | 389 | return 0; |
333 | } | 390 | } |
334 | 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 | |||
335 | postcore_initcall(kobject_uevent_init); | 424 | postcore_initcall(kobject_uevent_init); |
336 | #endif | 425 | #endif |