linux/lib/kobject_uevent.c
<<
>>
Prefs
   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
  30u64 uevent_seqnum;
  31char uevent_helper[UEVENT_HELPER_PATH_LEN] = CONFIG_UEVENT_HELPER_PATH;
  32static DEFINE_SPINLOCK(sequence_lock);
  33#ifdef CONFIG_NET
  34struct uevent_sock {
  35        struct list_head list;
  36        struct sock *sk;
  37};
  38static LIST_HEAD(uevent_sock_list);
  39static DEFINE_MUTEX(uevent_sock_mutex);
  40#endif
  41
  42/* the strings here must match the enum in include/linux/kobject.h */
  43static 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 */
  61int 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        }
  82out:
  83        return ret;
  84}
  85
  86#ifdef CONFIG_NET
  87static 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
 104static 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 */
 129int 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                if (!netlink_has_listeners(uevent_sock, 1))
 263                        continue;
 264
 265                /* allocate message with the maximum possible size */
 266                len = strlen(action_string) + strlen(devpath) + 2;
 267                skb = alloc_skb(len + env->buflen, GFP_KERNEL);
 268                if (skb) {
 269                        char *scratch;
 270
 271                        /* add header */
 272                        scratch = skb_put(skb, len);
 273                        sprintf(scratch, "%s@%s", action_string, devpath);
 274
 275                        /* copy keys to our continuous event payload buffer */
 276                        for (i = 0; i < env->envp_idx; i++) {
 277                                len = strlen(env->envp[i]) + 1;
 278                                scratch = skb_put(skb, len);
 279                                strcpy(scratch, env->envp[i]);
 280                        }
 281
 282                        NETLINK_CB(skb).dst_group = 1;
 283                        retval = netlink_broadcast_filtered(uevent_sock, skb,
 284                                                            0, 1, GFP_KERNEL,
 285                                                            kobj_bcast_filter,
 286                                                            kobj);
 287                        /* ENOBUFS should be handled in userspace */
 288                        if (retval == -ENOBUFS || retval == -ESRCH)
 289                                retval = 0;
 290                } else
 291                        retval = -ENOMEM;
 292        }
 293        mutex_unlock(&uevent_sock_mutex);
 294#endif
 295
 296        /* call uevent_helper, usually only enabled during early boot */
 297        if (uevent_helper[0] && !kobj_usermode_filter(kobj)) {
 298                char *argv [3];
 299
 300                argv [0] = uevent_helper;
 301                argv [1] = (char *)subsystem;
 302                argv [2] = NULL;
 303                retval = add_uevent_var(env, "HOME=/");
 304                if (retval)
 305                        goto exit;
 306                retval = add_uevent_var(env,
 307                                        "PATH=/sbin:/bin:/usr/sbin:/usr/bin");
 308                if (retval)
 309                        goto exit;
 310
 311                retval = call_usermodehelper(argv[0], argv,
 312                                             env->envp, UMH_WAIT_EXEC);
 313        }
 314
 315exit:
 316        kfree(devpath);
 317        kfree(env);
 318        return retval;
 319}
 320EXPORT_SYMBOL_GPL(kobject_uevent_env);
 321
 322/**
 323 * kobject_uevent - notify userspace by sending an uevent
 324 *
 325 * @action: action that is happening
 326 * @kobj: struct kobject that the action is happening to
 327 *
 328 * Returns 0 if kobject_uevent() is completed with success or the
 329 * corresponding error when it fails.
 330 */
 331int kobject_uevent(struct kobject *kobj, enum kobject_action action)
 332{
 333        return kobject_uevent_env(kobj, action, NULL);
 334}
 335EXPORT_SYMBOL_GPL(kobject_uevent);
 336
 337/**
 338 * add_uevent_var - add key value string to the environment buffer
 339 * @env: environment buffer structure
 340 * @format: printf format for the key=value pair
 341 *
 342 * Returns 0 if environment variable was added successfully or -ENOMEM
 343 * if no space was available.
 344 */
 345int add_uevent_var(struct kobj_uevent_env *env, const char *format, ...)
 346{
 347        va_list args;
 348        int len;
 349
 350        if (env->envp_idx >= ARRAY_SIZE(env->envp)) {
 351                WARN(1, KERN_ERR "add_uevent_var: too many keys\n");
 352                return -ENOMEM;
 353        }
 354
 355        va_start(args, format);
 356        len = vsnprintf(&env->buf[env->buflen],
 357                        sizeof(env->buf) - env->buflen,
 358                        format, args);
 359        va_end(args);
 360
 361        if (len >= (sizeof(env->buf) - env->buflen)) {
 362                WARN(1, KERN_ERR "add_uevent_var: buffer size too small\n");
 363                return -ENOMEM;
 364        }
 365
 366        env->envp[env->envp_idx++] = &env->buf[env->buflen];
 367        env->buflen += len + 1;
 368        return 0;
 369}
 370EXPORT_SYMBOL_GPL(add_uevent_var);
 371
 372#if defined(CONFIG_NET)
 373static int uevent_net_init(struct net *net)
 374{
 375        struct uevent_sock *ue_sk;
 376
 377        ue_sk = kzalloc(sizeof(*ue_sk), GFP_KERNEL);
 378        if (!ue_sk)
 379                return -ENOMEM;
 380
 381        ue_sk->sk = netlink_kernel_create(net, NETLINK_KOBJECT_UEVENT,
 382                                          1, NULL, NULL, THIS_MODULE);
 383        if (!ue_sk->sk) {
 384                printk(KERN_ERR
 385                       "kobject_uevent: unable to create netlink socket!\n");
 386                kfree(ue_sk);
 387                return -ENODEV;
 388        }
 389        mutex_lock(&uevent_sock_mutex);
 390        list_add_tail(&ue_sk->list, &uevent_sock_list);
 391        mutex_unlock(&uevent_sock_mutex);
 392        return 0;
 393}
 394
 395static void uevent_net_exit(struct net *net)
 396{
 397        struct uevent_sock *ue_sk;
 398
 399        mutex_lock(&uevent_sock_mutex);
 400        list_for_each_entry(ue_sk, &uevent_sock_list, list) {
 401                if (sock_net(ue_sk->sk) == net)
 402                        goto found;
 403        }
 404        mutex_unlock(&uevent_sock_mutex);
 405        return;
 406
 407found:
 408        list_del(&ue_sk->list);
 409        mutex_unlock(&uevent_sock_mutex);
 410
 411        netlink_kernel_release(ue_sk->sk);
 412        kfree(ue_sk);
 413}
 414
 415static struct pernet_operations uevent_net_ops = {
 416        .init   = uevent_net_init,
 417        .exit   = uevent_net_exit,
 418};
 419
 420static int __init kobject_uevent_init(void)
 421{
 422        netlink_set_nonroot(NETLINK_KOBJECT_UEVENT, NL_NONROOT_RECV);
 423        return register_pernet_subsys(&uevent_net_ops);
 424}
 425
 426
 427postcore_initcall(kobject_uevent_init);
 428#endif
 429
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.