diff options
Diffstat (limited to 'lib/kobject_uevent.c')
| -rw-r--r-- | lib/kobject_uevent.c | 109 |
1 files changed, 97 insertions, 12 deletions
diff --git a/lib/kobject_uevent.c b/lib/kobject_uevent.c index 7b48d44ced6e..59c15511d58a 100644 --- a/lib/kobject_uevent.c +++ b/lib/kobject_uevent.c | |||
| @@ -19,18 +19,24 @@ | |||
| 19 | #include <linux/kobject.h> | 19 | #include <linux/kobject.h> |
| 20 | #include <linux/module.h> | 20 | #include <linux/module.h> |
| 21 | #include <linux/slab.h> | 21 | #include <linux/slab.h> |
| 22 | 22 | #include <linux/user_namespace.h> | |
| 23 | #include <linux/socket.h> | 23 | #include <linux/socket.h> |
| 24 | #include <linux/skbuff.h> | 24 | #include <linux/skbuff.h> |
| 25 | #include <linux/netlink.h> | 25 | #include <linux/netlink.h> |
| 26 | #include <net/sock.h> | 26 | #include <net/sock.h> |
| 27 | #include <net/net_namespace.h> | ||
| 27 | 28 | ||
| 28 | 29 | ||
| 29 | u64 uevent_seqnum; | 30 | u64 uevent_seqnum; |
| 30 | char uevent_helper[UEVENT_HELPER_PATH_LEN] = CONFIG_UEVENT_HELPER_PATH; | 31 | char uevent_helper[UEVENT_HELPER_PATH_LEN] = CONFIG_UEVENT_HELPER_PATH; |
| 31 | static DEFINE_SPINLOCK(sequence_lock); | 32 | static DEFINE_SPINLOCK(sequence_lock); |
| 32 | #if defined(CONFIG_NET) | 33 | #ifdef CONFIG_NET |
| 33 | 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); | ||
| 34 | #endif | 40 | #endif |
| 35 | 41 | ||
| 36 | /* 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 */ |
| @@ -77,6 +83,37 @@ out: | |||
| 77 | return ret; | 83 | return ret; |
| 78 | } | 84 | } |
| 79 | 85 | ||
| 86 | static int kobj_bcast_filter(struct sock *dsk, struct sk_buff *skb, void *data) | ||
| 87 | { | ||
| 88 | struct kobject *kobj = data; | ||
| 89 | const struct kobj_ns_type_operations *ops; | ||
| 90 | |||
| 91 | ops = kobj_ns_ops(kobj); | ||
| 92 | if (ops) { | ||
| 93 | const void *sock_ns, *ns; | ||
| 94 | ns = kobj->ktype->namespace(kobj); | ||
| 95 | sock_ns = ops->netlink_ns(dsk); | ||
| 96 | return sock_ns != ns; | ||
| 97 | } | ||
| 98 | |||
| 99 | return 0; | ||
| 100 | } | ||
| 101 | |||
| 102 | static int kobj_usermode_filter(struct kobject *kobj) | ||
| 103 | { | ||
| 104 | const struct kobj_ns_type_operations *ops; | ||
| 105 | |||
| 106 | ops = kobj_ns_ops(kobj); | ||
| 107 | if (ops) { | ||
| 108 | const void *init_ns, *ns; | ||
| 109 | ns = kobj->ktype->namespace(kobj); | ||
| 110 | init_ns = ops->initial_ns(); | ||
| 111 | return ns != init_ns; | ||
| 112 | } | ||
| 113 | |||
| 114 | return 0; | ||
| 115 | } | ||
| 116 | |||
| 80 | /** | 117 | /** |
| 81 | * kobject_uevent_env - send an uevent with environmental data | 118 | * kobject_uevent_env - send an uevent with environmental data |
| 82 | * | 119 | * |
| @@ -100,6 +137,9 @@ int kobject_uevent_env(struct kobject *kobj, enum kobject_action action, | |||
| 100 | u64 seq; | 137 | u64 seq; |
| 101 | int i = 0; | 138 | int i = 0; |
| 102 | int retval = 0; | 139 | int retval = 0; |
| 140 | #ifdef CONFIG_NET | ||
| 141 | struct uevent_sock *ue_sk; | ||
| 142 | #endif | ||
| 103 | 143 | ||
| 104 | pr_debug("kobject: '%s' (%p): %s\n", | 144 | pr_debug("kobject: '%s' (%p): %s\n", |
| 105 | kobject_name(kobj), kobj, __func__); | 145 | kobject_name(kobj), kobj, __func__); |
| @@ -211,7 +251,9 @@ int kobject_uevent_env(struct kobject *kobj, enum kobject_action action, | |||
| 211 | 251 | ||
| 212 | #if defined(CONFIG_NET) | 252 | #if defined(CONFIG_NET) |
| 213 | /* send netlink message */ | 253 | /* send netlink message */ |
| 214 | if (uevent_sock) { | 254 | mutex_lock(&uevent_sock_mutex); |
| 255 | list_for_each_entry(ue_sk, &uevent_sock_list, list) { | ||
| 256 | struct sock *uevent_sock = ue_sk->sk; | ||
| 215 | struct sk_buff *skb; | 257 | struct sk_buff *skb; |
| 216 | size_t len; | 258 | size_t len; |
| 217 | 259 | ||
| @@ -233,18 +275,21 @@ int kobject_uevent_env(struct kobject *kobj, enum kobject_action action, | |||
| 233 | } | 275 | } |
| 234 | 276 | ||
| 235 | NETLINK_CB(skb).dst_group = 1; | 277 | NETLINK_CB(skb).dst_group = 1; |
| 236 | retval = netlink_broadcast(uevent_sock, skb, 0, 1, | 278 | retval = netlink_broadcast_filtered(uevent_sock, skb, |
| 237 | GFP_KERNEL); | 279 | 0, 1, GFP_KERNEL, |
| 280 | kobj_bcast_filter, | ||
| 281 | kobj); | ||
| 238 | /* ENOBUFS should be handled in userspace */ | 282 | /* ENOBUFS should be handled in userspace */ |
| 239 | if (retval == -ENOBUFS) | 283 | if (retval == -ENOBUFS) |
| 240 | retval = 0; | 284 | retval = 0; |
| 241 | } else | 285 | } else |
| 242 | retval = -ENOMEM; | 286 | retval = -ENOMEM; |
| 243 | } | 287 | } |
| 288 | mutex_unlock(&uevent_sock_mutex); | ||
| 244 | #endif | 289 | #endif |
| 245 | 290 | ||
| 246 | /* call uevent_helper, usually only enabled during early boot */ | 291 | /* call uevent_helper, usually only enabled during early boot */ |
| 247 | if (uevent_helper[0]) { | 292 | if (uevent_helper[0] && !kobj_usermode_filter(kobj)) { |
| 248 | char *argv [3]; | 293 | char *argv [3]; |
| 249 | 294 | ||
| 250 | argv [0] = uevent_helper; | 295 | argv [0] = uevent_helper; |
| @@ -320,18 +365,58 @@ int add_uevent_var(struct kobj_uevent_env *env, const char *format, ...) | |||
| 320 | EXPORT_SYMBOL_GPL(add_uevent_var); | 365 | EXPORT_SYMBOL_GPL(add_uevent_var); |
| 321 | 366 | ||
| 322 | #if defined(CONFIG_NET) | 367 | #if defined(CONFIG_NET) |
| 323 | static int __init kobject_uevent_init(void) | 368 | static int uevent_net_init(struct net *net) |
| 324 | { | 369 | { |
| 325 | uevent_sock = netlink_kernel_create(&init_net, NETLINK_KOBJECT_UEVENT, | 370 | struct uevent_sock *ue_sk; |
| 326 | 1, NULL, NULL, THIS_MODULE); | 371 | |
| 327 | if (!uevent_sock) { | 372 | ue_sk = kzalloc(sizeof(*ue_sk), GFP_KERNEL); |
| 373 | if (!ue_sk) | ||
| 374 | return -ENOMEM; | ||
| 375 | |||
| 376 | ue_sk->sk = netlink_kernel_create(net, NETLINK_KOBJECT_UEVENT, | ||
| 377 | 1, NULL, NULL, THIS_MODULE); | ||
| 378 | if (!ue_sk->sk) { | ||
| 328 | printk(KERN_ERR | 379 | printk(KERN_ERR |
| 329 | "kobject_uevent: unable to create netlink socket!\n"); | 380 | "kobject_uevent: unable to create netlink socket!\n"); |
| 330 | return -ENODEV; | 381 | return -ENODEV; |
| 331 | } | 382 | } |
| 332 | netlink_set_nonroot(NETLINK_KOBJECT_UEVENT, NL_NONROOT_RECV); | 383 | mutex_lock(&uevent_sock_mutex); |
| 384 | list_add_tail(&ue_sk->list, &uevent_sock_list); | ||
| 385 | mutex_unlock(&uevent_sock_mutex); | ||
| 333 | return 0; | 386 | return 0; |
| 334 | } | 387 | } |
| 335 | 388 | ||
| 389 | static void uevent_net_exit(struct net *net) | ||
| 390 | { | ||
| 391 | struct uevent_sock *ue_sk; | ||
| 392 | |||
| 393 | mutex_lock(&uevent_sock_mutex); | ||
| 394 | list_for_each_entry(ue_sk, &uevent_sock_list, list) { | ||
| 395 | if (sock_net(ue_sk->sk) == net) | ||
| 396 | goto found; | ||
| 397 | } | ||
| 398 | mutex_unlock(&uevent_sock_mutex); | ||
| 399 | return; | ||
| 400 | |||
| 401 | found: | ||
| 402 | list_del(&ue_sk->list); | ||
| 403 | mutex_unlock(&uevent_sock_mutex); | ||
| 404 | |||
| 405 | netlink_kernel_release(ue_sk->sk); | ||
| 406 | kfree(ue_sk); | ||
| 407 | } | ||
| 408 | |||
| 409 | static struct pernet_operations uevent_net_ops = { | ||
| 410 | .init = uevent_net_init, | ||
| 411 | .exit = uevent_net_exit, | ||
| 412 | }; | ||
| 413 | |||
| 414 | static int __init kobject_uevent_init(void) | ||
| 415 | { | ||
| 416 | netlink_set_nonroot(NETLINK_KOBJECT_UEVENT, NL_NONROOT_RECV); | ||
| 417 | return register_pernet_subsys(&uevent_net_ops); | ||
| 418 | } | ||
| 419 | |||
| 420 | |||
| 336 | postcore_initcall(kobject_uevent_init); | 421 | postcore_initcall(kobject_uevent_init); |
| 337 | #endif | 422 | #endif |
