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;
  32#ifdef CONFIG_NET
  33struct uevent_sock {
  34        struct list_head list;
  35        struct sock *sk;
  36};
  37static LIST_HEAD(uevent_sock_list);
  38#endif
  39
  40/* This lock protects uevent_seqnum and uevent_sock_list */
  41static DEFINE_MUTEX(uevent_sock_mutex);
  42
  43/* the strings here must match the enum in include/linux/kobject.h */
  44static const char *kobject_actions[] = {
  45        [KOBJ_ADD] =            "add",
  46        [KOBJ_REMOVE] =         "remove",
  47        [KOBJ_CHANGE] =         "change",
  48        [KOBJ_MOVE] =           "move",
  49        [KOBJ_ONLINE] =         "online",
  50        [KOBJ_OFFLINE] =        "offline",
  51};
  52
  53/**
  54 * kobject_action_type - translate action string to numeric type
  55 *
  56 * @buf: buffer containing the action string, newline is ignored
  57 * @len: length of buffer
  58 * @type: pointer to the location to store the action type
  59 *
  60 * Returns 0 if the action string was recognized.
  61 */
  62int kobject_action_type(const char *buf, size_t count,
  63                        enum kobject_action *type)
  64{
  65        enum kobject_action action;
  66        int ret = -EINVAL;
  67
  68        if (count && (buf[count-1] == '\n' || buf[count-1] == '\0'))
  69                count--;
  70
  71        if (!count)
  72                goto out;
  73
  74        for (action = 0; action < ARRAY_SIZE(kobject_actions); action++) {
  75                if (strncmp(kobject_actions[action], buf, count) != 0)
  76                        continue;
  77                if (kobject_actions[action][count] != '\0')
  78                        continue;
  79                *type = action;
  80                ret = 0;
  81                break;
  82        }
  83out:
  84        return ret;
  85}
  86
  87#ifdef CONFIG_NET
  88static int kobj_bcast_filter(struct sock *dsk, struct sk_buff *skb, void *data)
  89{
  90        struct kobject *kobj = data;
  91        const struct kobj_ns_type_operations *ops;
  92
  93        ops = kobj_ns_ops(kobj);
  94        if (ops) {
  95                const void *sock_ns, *ns;
  96                ns = kobj->ktype->namespace(kobj);
  97                sock_ns = ops->netlink_ns(dsk);
  98                return sock_ns != ns;
  99        }
 100
 101        return 0;
 102}
 103#endif
 104
 105static int kobj_usermode_filter(struct kobject *kobj)
 106{
 107        const struct kobj_ns_type_operations *ops;
 108
 109        ops = kobj_ns_ops(kobj);
 110        if (ops) {
 111                const void *init_ns, *ns;
 112                ns = kobj->ktype->namespace(kobj);
 113                init_ns = ops->initial_ns();
 114                return ns != init_ns;
 115        }
 116
 117        return 0;
 118}
 119
 120/**
 121 * kobject_uevent_env - send an uevent with environmental data
 122 *
 123 * @action: action that is happening
 124 * @kobj: struct kobject that the action is happening to
 125 * @envp_ext: pointer to environmental data
 126 *
 127 * Returns 0 if kobject_uevent_env() is completed with success or the
 128 * corresponding error when it fails.
 129 */
 130int kobject_uevent_env(struct kobject *kobj, enum kobject_action action,
 131                       char *envp_ext[])
 132{
 133        struct kobj_uevent_env *env;
 134        const char *action_string = kobject_actions[action];
 135        const char *devpath = NULL;
 136        const char *subsystem;
 137        struct kobject *top_kobj;
 138        struct kset *kset;
 139        const struct kset_uevent_ops *uevent_ops;
 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        mutex_lock(&uevent_sock_mutex);
 247        /* we will send an event, so request a new sequence number */
 248        retval = add_uevent_var(env, "SEQNUM=%llu", (unsigned long long)++uevent_seqnum);
 249        if (retval) {
 250                mutex_unlock(&uevent_sock_mutex);
 251                goto exit;
 252        }
 253
 254#if defined(CONFIG_NET)
 255        /* send netlink message */
 256        list_for_each_entry(ue_sk, &uevent_sock_list, list) {
 257                struct sock *uevent_sock = ue_sk->sk;
 258                struct sk_buff *skb;
 259                size_t len;
 260
 261                if (!netlink_has_listeners(uevent_sock, 1))
 262                        continue;
 263
 264                /* allocate message with the maximum possible size */
 265                len = strlen(action_string) + strlen(devpath) + 2;
 266                skb = alloc_skb(len + env->buflen, GFP_KERNEL);
 267                if (skb) {
 268                        char *scratch;
 269
 270                        /* add header */
 271                        scratch = skb_put(skb, len);
 272                        sprintf(scratch, "%s@%s", action_string, devpath);
 273
 274                        /* copy keys to our continuous event payload buffer */
 275                        for (i = 0; i < env->envp_idx; i++) {
 276                                len = strlen(env->envp[i]) + 1;
 277                                scratch = skb_put(skb, len);
 278                                strcpy(scratch, env->envp[i]);
 279                        }
 280
 281                        NETLINK_CB(skb).dst_group = 1;
 282                        retval = netlink_broadcast_filtered(uevent_sock, skb,
 283                                                            0, 1, GFP_KERNEL,
 284                                                            kobj_bcast_filter,
 285                                                            kobj);
 286                        /* ENOBUFS should be handled in userspace */
 287                        if (retval == -ENOBUFS || retval == -ESRCH)
 288                                retval = 0;
 289                } else
 290                        retval = -ENOMEM;
 291        }
 292#endif
 293        mutex_unlock(&uevent_sock_mutex);
 294
 295        /* call uevent_helper, usually only enabled during early boot */
 296        if (uevent_helper[0] && !kobj_usermode_filter(kobj)) {
 297                char *argv [3];
 298
 299                argv [0] = uevent_helper;
 300                argv [1] = (char *)subsystem;
 301                argv [2] = NULL;
 302                retval = add_uevent_var(env, "HOME=/");
 303                if (retval)
 304                        goto exit;
 305                retval = add_uevent_var(env,
 306                                        "PATH=/sbin:/bin:/usr/sbin:/usr/bin");
 307                if (retval)
 308                        goto exit;
 309
 310                retval = call_usermodehelper(argv[0], argv,
 311                                             env->envp, UMH_WAIT_EXEC);
 312        }
 313
 314exit:
 315        kfree(devpath);
 316        kfree(env);
 317        return retval;
 318}
 319EXPORT_SYMBOL_GPL(kobject_uevent_env);
 320
 321/**
 322 * kobject_uevent - notify userspace by sending an uevent
 323 *
 324 * @action: action that is happening
 325 * @kobj: struct kobject that the action is happening to
 326 *
 327 * Returns 0 if kobject_uevent() is completed with success or the
 328 * corresponding error when it fails.
 329 */
 330int kobject_uevent(struct kobject *kobj, enum kobject_action action)
 331{
 332        return kobject_uevent_env(kobj, action, NULL);
 333}
 334EXPORT_SYMBOL_GPL(kobject_uevent);
 335
 336/**
 337 * add_uevent_var - add key value string to the environment buffer
 338 * @env: environment buffer structure
 339 * @format: printf format for the key=value pair
 340 *
 341 * Returns 0 if environment variable was added successfully or -ENOMEM
 342 * if no space was available.
 343 */
 344int add_uevent_var(struct kobj_uevent_env *env, const char *format, ...)
 345{
 346        va_list args;
 347        int len;
 348
 349        if (env->envp_idx >= ARRAY_SIZE(env->envp)) {
 350                WARN(1, KERN_ERR "add_uevent_var: too many keys\n");
 351                return -ENOMEM;
 352        }
 353
 354        va_start(args, format);
 355        len = vsnprintf(&env->buf[env->buflen],
 356                        sizeof(env->buf) - env->buflen,
 357                        format, args);
 358        va_end(args);
 359
 360        if (len >= (sizeof(env->buf) - env->buflen)) {
 361                WARN(1, KERN_ERR "add_uevent_var: buffer size too small\n");
 362                return -ENOMEM;
 363        }
 364
 365        env->envp[env->envp_idx++] = &env->buf[env->buflen];
 366        env->buflen += len + 1;
 367        return 0;
 368}
 369EXPORT_SYMBOL_GPL(add_uevent_var);
 370
 371#if defined(CONFIG_NET)
 372static int uevent_net_init(struct net *net)
 373{
 374        struct uevent_sock *ue_sk;
 375
 376        ue_sk = kzalloc(sizeof(*ue_sk), GFP_KERNEL);
 377        if (!ue_sk)
 378                return -ENOMEM;
 379
 380        ue_sk->sk = netlink_kernel_create(net, NETLINK_KOBJECT_UEVENT,
 381                                          1, NULL, NULL, THIS_MODULE);
 382        if (!ue_sk->sk) {
 383                printk(KERN_ERR
 384                       "kobject_uevent: unable to create netlink socket!\n");
 385                kfree(ue_sk);
 386                return -ENODEV;
 387        }
 388        mutex_lock(&uevent_sock_mutex);
 389        list_add_tail(&ue_sk->list, &uevent_sock_list);
 390        mutex_unlock(&uevent_sock_mutex);
 391        return 0;
 392}
 393
 394static void uevent_net_exit(struct net *net)
 395{
 396        struct uevent_sock *ue_sk;
 397
 398        mutex_lock(&uevent_sock_mutex);
 399        list_for_each_entry(ue_sk, &uevent_sock_list, list) {
 400                if (sock_net(ue_sk->sk) == net)
 401                        goto found;
 402        }
 403        mutex_unlock(&uevent_sock_mutex);
 404        return;
 405
 406found:
 407        list_del(&ue_sk->list);
 408        mutex_unlock(&uevent_sock_mutex);
 409
 410        netlink_kernel_release(ue_sk->sk);
 411        kfree(ue_sk);
 412}
 413
 414static struct pernet_operations uevent_net_ops = {
 415        .init   = uevent_net_init,
 416        .exit   = uevent_net_exit,
 417};
 418
 419static int __init kobject_uevent_init(void)
 420{
 421        netlink_set_nonroot(NETLINK_KOBJECT_UEVENT, NL_NONROOT_RECV);
 422        return register_pernet_subsys(&uevent_net_ops);
 423}
 424
 425
 426postcore_initcall(kobject_uevent_init);
 427#endif
 428
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.