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