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
  22#include <linux/socket.h>
  23#include <linux/skbuff.h>
  24#include <linux/netlink.h>
  25#include <net/sock.h>
  26
  27
  28u64 uevent_seqnum;
  29char uevent_helper[UEVENT_HELPER_PATH_LEN] = CONFIG_UEVENT_HELPER_PATH;
  30static DEFINE_SPINLOCK(sequence_lock);
  31#if defined(CONFIG_NET)
  32static struct sock *uevent_sock;
  33#endif
  34
  35/* the strings here must match the enum in include/linux/kobject.h */
  36static const char *kobject_actions[] = {
  37        [KOBJ_ADD] =            "add",
  38        [KOBJ_REMOVE] =         "remove",
  39        [KOBJ_CHANGE] =         "change",
  40        [KOBJ_MOVE] =           "move",
  41        [KOBJ_ONLINE] =         "online",
  42        [KOBJ_OFFLINE] =        "offline",
  43};
  44
  45/**
  46 * kobject_action_type - translate action string to numeric type
  47 *
  48 * @buf: buffer containing the action string, newline is ignored
  49 * @len: length of buffer
  50 * @type: pointer to the location to store the action type
  51 *
  52 * Returns 0 if the action string was recognized.
  53 */
  54int kobject_action_type(const char *buf, size_t count,
  55                        enum kobject_action *type)
  56{
  57        enum kobject_action action;
  58        int ret = -EINVAL;
  59
  60        if (count && (buf[count-1] == '\n' || buf[count-1] == '\0'))
  61                count--;
  62
  63        if (!count)
  64                goto out;
  65
  66        for (action = 0; action < ARRAY_SIZE(kobject_actions); action++) {
  67                if (strncmp(kobject_actions[action], buf, count) != 0)
  68                        continue;
  69                if (kobject_actions[action][count] != '\0')
  70                        continue;
  71                *type = action;
  72                ret = 0;
  73                break;
  74        }
  75out:
  76        return ret;
  77}
  78
  79/**
  80 * kobject_uevent_env - send an uevent with environmental data
  81 *
  82 * @action: action that is happening
  83 * @kobj: struct kobject that the action is happening to
  84 * @envp_ext: pointer to environmental data
  85 *
  86 * Returns 0 if kobject_uevent() is completed with success or the
  87 * corresponding error when it fails.
  88 */
  89int kobject_uevent_env(struct kobject *kobj, enum kobject_action action,
  90                       char *envp_ext[])
  91{
  92        struct kobj_uevent_env *env;
  93        const char *action_string = kobject_actions[action];
  94        const char *devpath = NULL;
  95        const char *subsystem;
  96        struct kobject *top_kobj;
  97        struct kset *kset;
  98        struct kset_uevent_ops *uevent_ops;
  99        u64 seq;
 100        int i = 0;
 101        int retval = 0;
 102
 103        pr_debug("kobject: '%s' (%p): %s\n",
 104                 kobject_name(kobj), kobj, __func__);
 105
 106        /* search the kset we belong to */
 107        top_kobj = kobj;
 108        while (!top_kobj->kset && top_kobj->parent)
 109                top_kobj = top_kobj->parent;
 110
 111        if (!top_kobj->kset) {
 112                pr_debug("kobject: '%s' (%p): %s: attempted to send uevent "
 113                         "without kset!\n", kobject_name(kobj), kobj,
 114                         __func__);
 115                return -EINVAL;
 116        }
 117
 118        kset = top_kobj->kset;
 119        uevent_ops = kset->uevent_ops;
 120
 121        /* skip the event, if the filter returns zero. */
 122        if (uevent_ops && uevent_ops->filter)
 123                if (!uevent_ops->filter(kset, kobj)) {
 124                        pr_debug("kobject: '%s' (%p): %s: filter function "
 125                                 "caused the event to drop!\n",
 126                                 kobject_name(kobj), kobj, __func__);
 127                        return 0;
 128                }
 129
 130        /* originating subsystem */
 131        if (uevent_ops && uevent_ops->name)
 132                subsystem = uevent_ops->name(kset, kobj);
 133        else
 134                subsystem = kobject_name(&kset->kobj);
 135        if (!subsystem) {
 136                pr_debug("kobject: '%s' (%p): %s: unset subsystem caused the "
 137                         "event to drop!\n", kobject_name(kobj), kobj,
 138                         __func__);
 139                return 0;
 140        }
 141
 142        /* environment buffer */
 143        env = kzalloc(sizeof(struct kobj_uevent_env), GFP_KERNEL);
 144        if (!env)
 145                return -ENOMEM;
 146
 147        /* complete object path */
 148        devpath = kobject_get_path(kobj, GFP_KERNEL);
 149        if (!devpath) {
 150                retval = -ENOENT;
 151                goto exit;
 152        }
 153
 154        /* default keys */
 155        retval = add_uevent_var(env, "ACTION=%s", action_string);
 156        if (retval)
 157                goto exit;
 158        retval = add_uevent_var(env, "DEVPATH=%s", devpath);
 159        if (retval)
 160                goto exit;
 161        retval = add_uevent_var(env, "SUBSYSTEM=%s", subsystem);
 162        if (retval)
 163                goto exit;
 164
 165        /* keys passed in from the caller */
 166        if (envp_ext) {
 167                for (i = 0; envp_ext[i]; i++) {
 168                        retval = add_uevent_var(env, envp_ext[i]);
 169                        if (retval)
 170                                goto exit;
 171                }
 172        }
 173
 174        /* let the kset specific function add its stuff */
 175        if (uevent_ops && uevent_ops->uevent) {
 176                retval = uevent_ops->uevent(kset, kobj, env);
 177                if (retval) {
 178                        pr_debug("kobject: '%s' (%p): %s: uevent() returned "
 179                                 "%d\n", kobject_name(kobj), kobj,
 180                                 __func__, retval);
 181                        goto exit;
 182                }
 183        }
 184
 185        /*
 186         * Mark "add" and "remove" events in the object to ensure proper
 187         * events to userspace during automatic cleanup. If the object did
 188         * send an "add" event, "remove" will automatically generated by
 189         * the core, if not already done by the caller.
 190         */
 191        if (action == KOBJ_ADD)
 192                kobj->state_add_uevent_sent = 1;
 193        else if (action == KOBJ_REMOVE)
 194                kobj->state_remove_uevent_sent = 1;
 195
 196        /* we will send an event, so request a new sequence number */
 197        spin_lock(&sequence_lock);
 198        seq = ++uevent_seqnum;
 199        spin_unlock(&sequence_lock);
 200        retval = add_uevent_var(env, "SEQNUM=%llu", (unsigned long long)seq);
 201        if (retval)
 202                goto exit;
 203
 204#if defined(CONFIG_NET)
 205        /* send netlink message */
 206        if (uevent_sock) {
 207                struct sk_buff *skb;
 208                size_t len;
 209
 210                /* allocate message with the maximum possible size */
 211                len = strlen(action_string) + strlen(devpath) + 2;
 212                skb = alloc_skb(len + env->buflen, GFP_KERNEL);
 213                if (skb) {
 214                        char *scratch;
 215
 216                        /* add header */
 217                        scratch = skb_put(skb, len);
 218                        sprintf(scratch, "%s@%s", action_string, devpath);
 219
 220                        /* copy keys to our continuous event payload buffer */
 221                        for (i = 0; i < env->envp_idx; i++) {
 222                                len = strlen(env->envp[i]) + 1;
 223                                scratch = skb_put(skb, len);
 224                                strcpy(scratch, env->envp[i]);
 225                        }
 226
 227                        NETLINK_CB(skb).dst_group = 1;
 228                        netlink_broadcast(uevent_sock, skb, 0, 1, GFP_KERNEL);
 229                }
 230        }
 231#endif
 232
 233        /* call uevent_helper, usually only enabled during early boot */
 234        if (uevent_helper[0]) {
 235                char *argv [3];
 236
 237                argv [0] = uevent_helper;
 238                argv [1] = (char *)subsystem;
 239                argv [2] = NULL;
 240                retval = add_uevent_var(env, "HOME=/");
 241                if (retval)
 242                        goto exit;
 243                retval = add_uevent_var(env,
 244                                        "PATH=/sbin:/bin:/usr/sbin:/usr/bin");
 245                if (retval)
 246                        goto exit;
 247
 248                retval = call_usermodehelper(argv[0], argv,
 249                                             env->envp, UMH_WAIT_EXEC);
 250        }
 251
 252exit:
 253        kfree(devpath);
 254        kfree(env);
 255        return retval;
 256}
 257EXPORT_SYMBOL_GPL(kobject_uevent_env);
 258
 259/**
 260 * kobject_uevent - notify userspace by ending an uevent
 261 *
 262 * @action: action that is happening
 263 * @kobj: struct kobject that the action is happening to
 264 *
 265 * Returns 0 if kobject_uevent() is completed with success or the
 266 * corresponding error when it fails.
 267 */
 268int kobject_uevent(struct kobject *kobj, enum kobject_action action)
 269{
 270        return kobject_uevent_env(kobj, action, NULL);
 271}
 272EXPORT_SYMBOL_GPL(kobject_uevent);
 273
 274/**
 275 * add_uevent_var - add key value string to the environment buffer
 276 * @env: environment buffer structure
 277 * @format: printf format for the key=value pair
 278 *
 279 * Returns 0 if environment variable was added successfully or -ENOMEM
 280 * if no space was available.
 281 */
 282int add_uevent_var(struct kobj_uevent_env *env, const char *format, ...)
 283{
 284        va_list args;
 285        int len;
 286
 287        if (env->envp_idx >= ARRAY_SIZE(env->envp)) {
 288                WARN(1, KERN_ERR "add_uevent_var: too many keys\n");
 289                return -ENOMEM;
 290        }
 291
 292        va_start(args, format);
 293        len = vsnprintf(&env->buf[env->buflen],
 294                        sizeof(env->buf) - env->buflen,
 295                        format, args);
 296        va_end(args);
 297
 298        if (len >= (sizeof(env->buf) - env->buflen)) {
 299                WARN(1, KERN_ERR "add_uevent_var: buffer size too small\n");
 300                return -ENOMEM;
 301        }
 302
 303        env->envp[env->envp_idx++] = &env->buf[env->buflen];
 304        env->buflen += len + 1;
 305        return 0;
 306}
 307EXPORT_SYMBOL_GPL(add_uevent_var);
 308
 309#if defined(CONFIG_NET)
 310static int __init kobject_uevent_init(void)
 311{
 312        uevent_sock = netlink_kernel_create(&init_net, NETLINK_KOBJECT_UEVENT,
 313                                            1, NULL, NULL, THIS_MODULE);
 314        if (!uevent_sock) {
 315                printk(KERN_ERR
 316                       "kobject_uevent: unable to create netlink socket!\n");
 317                return -ENODEV;
 318        }
 319
 320        return 0;
 321}
 322
 323postcore_initcall(kobject_uevent_init);
 324#endif
 325
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.