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 uevent_suppress is set*/
 122        if (kobj->uevent_suppress) {
 123                pr_debug("kobject: '%s' (%p): %s: uevent_suppress "
 124                                 "caused the event to drop!\n",
 125                                 kobject_name(kobj), kobj, __func__);
 126                return 0;
 127        }
 128        /* skip the event, if the filter returns zero. */
 129        if (uevent_ops && uevent_ops->filter)
 130                if (!uevent_ops->filter(kset, kobj)) {
 131                        pr_debug("kobject: '%s' (%p): %s: filter function "
 132                                 "caused the event to drop!\n",
 133                                 kobject_name(kobj), kobj, __func__);
 134                        return 0;
 135                }
 136
 137        /* originating subsystem */
 138        if (uevent_ops && uevent_ops->name)
 139                subsystem = uevent_ops->name(kset, kobj);
 140        else
 141                subsystem = kobject_name(&kset->kobj);
 142        if (!subsystem) {
 143                pr_debug("kobject: '%s' (%p): %s: unset subsystem caused the "
 144                         "event to drop!\n", kobject_name(kobj), kobj,
 145                         __func__);
 146                return 0;
 147        }
 148
 149        /* environment buffer */
 150        env = kzalloc(sizeof(struct kobj_uevent_env), GFP_KERNEL);
 151        if (!env)
 152                return -ENOMEM;
 153
 154        /* complete object path */
 155        devpath = kobject_get_path(kobj, GFP_KERNEL);
 156        if (!devpath) {
 157                retval = -ENOENT;
 158                goto exit;
 159        }
 160
 161        /* default keys */
 162        retval = add_uevent_var(env, "ACTION=%s", action_string);
 163        if (retval)
 164                goto exit;
 165        retval = add_uevent_var(env, "DEVPATH=%s", devpath);
 166        if (retval)
 167                goto exit;
 168        retval = add_uevent_var(env, "SUBSYSTEM=%s", subsystem);
 169        if (retval)
 170                goto exit;
 171
 172        /* keys passed in from the caller */
 173        if (envp_ext) {
 174                for (i = 0; envp_ext[i]; i++) {
 175                        retval = add_uevent_var(env, "%s", envp_ext[i]);
 176                        if (retval)
 177                                goto exit;
 178                }
 179        }
 180
 181        /* let the kset specific function add its stuff */
 182        if (uevent_ops && uevent_ops->uevent) {
 183                retval = uevent_ops->uevent(kset, kobj, env);
 184                if (retval) {
 185                        pr_debug("kobject: '%s' (%p): %s: uevent() returned "
 186                                 "%d\n", kobject_name(kobj), kobj,
 187                                 __func__, retval);
 188                        goto exit;
 189                }
 190        }
 191
 192        /*
 193         * Mark "add" and "remove" events in the object to ensure proper
 194         * events to userspace during automatic cleanup. If the object did
 195         * send an "add" event, "remove" will automatically generated by
 196         * the core, if not already done by the caller.
 197         */
 198        if (action == KOBJ_ADD)
 199                kobj->state_add_uevent_sent = 1;
 200        else if (action == KOBJ_REMOVE)
 201                kobj->state_remove_uevent_sent = 1;
 202
 203        /* we will send an event, so request a new sequence number */
 204        spin_lock(&sequence_lock);
 205        seq = ++uevent_seqnum;
 206        spin_unlock(&sequence_lock);
 207        retval = add_uevent_var(env, "SEQNUM=%llu", (unsigned long long)seq);
 208        if (retval)
 209                goto exit;
 210
 211#if defined(CONFIG_NET)
 212        /* send netlink message */
 213        if (uevent_sock) {
 214                struct sk_buff *skb;
 215                size_t len;
 216
 217                /* allocate message with the maximum possible size */
 218                len = strlen(action_string) + strlen(devpath) + 2;
 219                skb = alloc_skb(len + env->buflen, GFP_KERNEL);
 220                if (skb) {
 221                        char *scratch;
 222
 223                        /* add header */
 224                        scratch = skb_put(skb, len);
 225                        sprintf(scratch, "%s@%s", action_string, devpath);
 226
 227                        /* copy keys to our continuous event payload buffer */
 228                        for (i = 0; i < env->envp_idx; i++) {
 229                                len = strlen(env->envp[i]) + 1;
 230                                scratch = skb_put(skb, len);
 231                                strcpy(scratch, env->envp[i]);
 232                        }
 233
 234                        NETLINK_CB(skb).dst_group = 1;
 235                        retval = netlink_broadcast(uevent_sock, skb, 0, 1,
 236                                                   GFP_KERNEL);
 237                        /* ENOBUFS should be handled in userspace */
 238                        if (retval == -ENOBUFS)
 239                                retval = 0;
 240                } else
 241                        retval = -ENOMEM;
 242        }
 243#endif
 244
 245        /* call uevent_helper, usually only enabled during early boot */
 246        if (uevent_helper[0]) {
 247                char *argv [3];
 248
 249                argv [0] = uevent_helper;
 250                argv [1] = (char *)subsystem;
 251                argv [2] = NULL;
 252                retval = add_uevent_var(env, "HOME=/");
 253                if (retval)
 254                        goto exit;
 255                retval = add_uevent_var(env,
 256                                        "PATH=/sbin:/bin:/usr/sbin:/usr/bin");
 257                if (retval)
 258                        goto exit;
 259
 260                retval = call_usermodehelper(argv[0], argv,
 261                                             env->envp, UMH_WAIT_EXEC);
 262        }
 263
 264exit:
 265        kfree(devpath);
 266        kfree(env);
 267        return retval;
 268}
 269EXPORT_SYMBOL_GPL(kobject_uevent_env);
 270
 271/**
 272 * kobject_uevent - notify userspace by ending an uevent
 273 *
 274 * @action: action that is happening
 275 * @kobj: struct kobject that the action is happening to
 276 *
 277 * Returns 0 if kobject_uevent() is completed with success or the
 278 * corresponding error when it fails.
 279 */
 280int kobject_uevent(struct kobject *kobj, enum kobject_action action)
 281{
 282        return kobject_uevent_env(kobj, action, NULL);
 283}
 284EXPORT_SYMBOL_GPL(kobject_uevent);
 285
 286/**
 287 * add_uevent_var - add key value string to the environment buffer
 288 * @env: environment buffer structure
 289 * @format: printf format for the key=value pair
 290 *
 291 * Returns 0 if environment variable was added successfully or -ENOMEM
 292 * if no space was available.
 293 */
 294int add_uevent_var(struct kobj_uevent_env *env, const char *format, ...)
 295{
 296        va_list args;
 297        int len;
 298
 299        if (env->envp_idx >= ARRAY_SIZE(env->envp)) {
 300                WARN(1, KERN_ERR "add_uevent_var: too many keys\n");
 301                return -ENOMEM;
 302        }
 303
 304        va_start(args, format);
 305        len = vsnprintf(&env->buf[env->buflen],
 306                        sizeof(env->buf) - env->buflen,
 307                        format, args);
 308        va_end(args);
 309
 310        if (len >= (sizeof(env->buf) - env->buflen)) {
 311                WARN(1, KERN_ERR "add_uevent_var: buffer size too small\n");
 312                return -ENOMEM;
 313        }
 314
 315        env->envp[env->envp_idx++] = &env->buf[env->buflen];
 316        env->buflen += len + 1;
 317        return 0;
 318}
 319EXPORT_SYMBOL_GPL(add_uevent_var);
 320
 321#if defined(CONFIG_NET)
 322static int __init kobject_uevent_init(void)
 323{
 324        uevent_sock = netlink_kernel_create(&init_net, NETLINK_KOBJECT_UEVENT,
 325                                            1, NULL, NULL, THIS_MODULE);
 326        if (!uevent_sock) {
 327                printk(KERN_ERR
 328                       "kobject_uevent: unable to create netlink socket!\n");
 329                return -ENODEV;
 330        }
 331        netlink_set_nonroot(NETLINK_KOBJECT_UEVENT, NL_NONROOT_RECV);
 332        return 0;
 333}
 334
 335postcore_initcall(kobject_uevent_init);
 336#endif
 337