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                /* 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
 312exit:
 313        kfree(devpath);
 314        kfree(env);
 315        return retval;
 316}
 317EXPORT_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 */
 328int kobject_uevent(struct kobject *kobj, enum kobject_action action)
 329{
 330        return kobject_uevent_env(kobj, action, NULL);
 331}
 332EXPORT_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 */
 342int 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}
 367EXPORT_SYMBOL_GPL(add_uevent_var);
 368
 369#if defined(CONFIG_NET)
 370static 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
 392static 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
 404found:
 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
 412static struct pernet_operations uevent_net_ops = {
 413        .init   = uevent_net_init,
 414        .exit   = uevent_net_exit,
 415};
 416
 417static 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
 424postcore_initcall(kobject_uevent_init);
 425#endif
 426
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.