diff options
author | Greg Kroah-Hartman <greg@kroah.com> | 2004-09-24 10:04:39 -0700 |
---|---|---|
committer | Greg Kroah-Hartman <greg@kroah.com> | 2004-09-24 10:04:39 -0700 |
commit | a1e66cc0011a9f9fae58a5609e329357d747635d (patch) | |
tree | ab77b98593b8ad32dfe0575e8163fdbd7003f79d /lib | |
parent | 038f9a8f2bb8d02cade66afff652629aba83cf8a (diff) | |
parent | 9ec20a24ed211fe9bbb0dce022dcf31a43629454 (diff) | |
download | history-a1e66cc0011a9f9fae58a5609e329357d747635d.tar.gz |
Merge kroah.com:/home/greg/linux/BK/bleed-2.6
into kroah.com:/home/greg/linux/BK/driver-2.6
Diffstat (limited to 'lib')
-rw-r--r-- | lib/Makefile | 2 | ||||
-rw-r--r-- | lib/kobject.c | 137 | ||||
-rw-r--r-- | lib/kobject_uevent.c | 295 |
3 files changed, 303 insertions, 131 deletions
diff --git a/lib/Makefile b/lib/Makefile index da5dca19c2c8ba..24bbe1cac5bdb1 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -6,7 +6,7 @@ lib-y := errno.o ctype.o string.o vsprintf.o cmdline.o \ bust_spinlocks.o rbtree.o radix-tree.o dump_stack.o \ kobject.o kref.o idr.o div64.o parser.o int_sqrt.o \ - bitmap.o extable.o + bitmap.o extable.o kobject_uevent.o lib-$(CONFIG_RWSEM_GENERIC_SPINLOCK) += rwsem-spinlock.o lib-$(CONFIG_RWSEM_XCHGADD_ALGORITHM) += rwsem.o diff --git a/lib/kobject.c b/lib/kobject.c index a971b8e55e6b75..d623f0575f2300 100644 --- a/lib/kobject.c +++ b/lib/kobject.c @@ -63,7 +63,7 @@ static inline struct kobject * to_kobj(struct list_head * entry) return container_of(entry,struct kobject,entry); } -static int get_kobj_path_length(struct kset *kset, struct kobject *kobj) +static int get_kobj_path_length(struct kobject *kobj) { int length = 1; struct kobject * parent = kobj; @@ -79,7 +79,7 @@ static int get_kobj_path_length(struct kset *kset, struct kobject *kobj) return length; } -static void fill_kobj_path(struct kset *kset, struct kobject *kobj, char *path, int length) +static void fill_kobj_path(struct kobject *kobj, char *path, int length) { struct kobject * parent; @@ -99,146 +99,24 @@ static void fill_kobj_path(struct kset *kset, struct kobject *kobj, char *path, * kobject_get_path - generate and return the path associated with a given kobj * and kset pair. The result must be freed by the caller with kfree(). * - * @kset: kset in question, with which to build the path * @kobj: kobject in question, with which to build the path * @gfp_mask: the allocation type used to allocate the path */ -char * kobject_get_path(struct kset *kset, struct kobject *kobj, int gfp_mask) +char *kobject_get_path(struct kobject *kobj, int gfp_mask) { char *path; int len; - len = get_kobj_path_length(kset, kobj); + len = get_kobj_path_length(kobj); path = kmalloc(len, gfp_mask); if (!path) return NULL; memset(path, 0x00, len); - fill_kobj_path(kset, kobj, path, len); + fill_kobj_path(kobj, path, len); return path; } -#ifdef CONFIG_HOTPLUG - -#define BUFFER_SIZE 1024 /* should be enough memory for the env */ -#define NUM_ENVP 32 /* number of env pointers */ -static unsigned long sequence_num; -static spinlock_t sequence_lock = SPIN_LOCK_UNLOCKED; - -static void kset_hotplug(const char *action, struct kset *kset, - struct kobject *kobj) -{ - char *argv [3]; - char **envp = NULL; - char *buffer = NULL; - char *scratch; - int i = 0; - int retval; - char *kobj_path = NULL; - char *name = NULL; - unsigned long seq; - - /* If the kset has a filter operation, call it. If it returns - failure, no hotplug event is required. */ - if (kset->hotplug_ops->filter) { - if (!kset->hotplug_ops->filter(kset, kobj)) - return; - } - - pr_debug ("%s\n", __FUNCTION__); - - if (!hotplug_path[0]) - return; - - envp = kmalloc(NUM_ENVP * sizeof (char *), GFP_KERNEL); - if (!envp) - return; - memset (envp, 0x00, NUM_ENVP * sizeof (char *)); - - buffer = kmalloc(BUFFER_SIZE, GFP_KERNEL); - if (!buffer) - goto exit; - - if (kset->hotplug_ops->name) - name = kset->hotplug_ops->name(kset, kobj); - if (name == NULL) - name = kset->kobj.name; - - argv [0] = hotplug_path; - argv [1] = name; - argv [2] = NULL; - - /* minimal command environment */ - envp [i++] = "HOME=/"; - envp [i++] = "PATH=/sbin:/bin:/usr/sbin:/usr/bin"; - - scratch = buffer; - - envp [i++] = scratch; - scratch += sprintf(scratch, "ACTION=%s", action) + 1; - - spin_lock(&sequence_lock); - seq = sequence_num++; - spin_unlock(&sequence_lock); - - envp [i++] = scratch; - scratch += sprintf(scratch, "SEQNUM=%ld", seq) + 1; - - kobj_path = kobject_get_path(kset, kobj, GFP_KERNEL); - if (!kobj_path) - goto exit; - - envp [i++] = scratch; - scratch += sprintf (scratch, "DEVPATH=%s", kobj_path) + 1; - - if (kset->hotplug_ops->hotplug) { - /* have the kset specific function add its stuff */ - retval = kset->hotplug_ops->hotplug (kset, kobj, - &envp[i], NUM_ENVP - i, scratch, - BUFFER_SIZE - (scratch - buffer)); - if (retval) { - pr_debug ("%s - hotplug() returned %d\n", - __FUNCTION__, retval); - goto exit; - } - } - - pr_debug ("%s: %s %s %s %s %s %s %s\n", __FUNCTION__, argv[0], argv[1], - envp[0], envp[1], envp[2], envp[3], envp[4]); - retval = call_usermodehelper (argv[0], argv, envp, 0); - if (retval) - pr_debug ("%s - call_usermodehelper returned %d\n", - __FUNCTION__, retval); - -exit: - kfree(kobj_path); - kfree(buffer); - kfree(envp); - return; -} - -void kobject_hotplug(const char *action, struct kobject *kobj) -{ - struct kobject * top_kobj = kobj; - - /* If this kobj does not belong to a kset, - try to find a parent that does. */ - if (!top_kobj->kset && top_kobj->parent) { - do { - top_kobj = top_kobj->parent; - } while (!top_kobj->kset && top_kobj->parent); - } - - if (top_kobj->kset && top_kobj->kset->hotplug_ops) - kset_hotplug(action, top_kobj->kset, kobj); -} -#else -void kobject_hotplug(const char *action, struct kobject *kobj) -{ - return; -} -#endif /* CONFIG_HOTPLUG */ - /** * kobject_init - initialize object. * @kobj: object in question. @@ -308,7 +186,7 @@ int kobject_add(struct kobject * kobj) if (parent) kobject_put(parent); } else { - kobject_hotplug("add", kobj); + kobject_hotplug(kobj, KOBJ_ADD); } return error; @@ -422,7 +300,7 @@ int kobject_rename(struct kobject * kobj, char *new_name) void kobject_del(struct kobject * kobj) { - kobject_hotplug("remove", kobj); + kobject_hotplug(kobj, KOBJ_REMOVE); sysfs_remove_dir(kobj); unlink(kobj); } @@ -654,7 +532,6 @@ EXPORT_SYMBOL(kobject_put); EXPORT_SYMBOL(kobject_add); EXPORT_SYMBOL(kobject_del); EXPORT_SYMBOL(kobject_rename); -EXPORT_SYMBOL(kobject_hotplug); EXPORT_SYMBOL(kset_register); EXPORT_SYMBOL(kset_unregister); diff --git a/lib/kobject_uevent.c b/lib/kobject_uevent.c new file mode 100644 index 00000000000000..33e0cef31ba869 --- /dev/null +++ b/lib/kobject_uevent.c @@ -0,0 +1,295 @@ +/* + * kernel userspace event delivery + * + * Copyright (C) 2004 Red Hat, Inc. All rights reserved. + * Copyright (C) 2004 Novell, Inc. All rights reserved. + * Copyright (C) 2004 IBM, Inc. All rights reserved. + * + * Licensed under the GNU GPL v2. + * + * Authors: + * Robert Love <rml@novell.com> + * Kay Sievers <kay.sievers@vrfy.org> + * Arjan van de Ven <arjanv@redhat.com> + * Greg Kroah-Hartman <greg@kroah.com> + */ + +#include <linux/spinlock.h> +#include <linux/socket.h> +#include <linux/skbuff.h> +#include <linux/netlink.h> +#include <linux/string.h> +#include <linux/kobject_uevent.h> +#include <linux/kobject.h> +#include <net/sock.h> + +/* + * These must match up with the values for enum kobject_action + * as found in include/linux/kobject_uevent.h + */ +static char *actions[] = { + "add", /* 0x00 */ + "remove", /* 0x01 */ + "change", /* 0x02 */ + "mount", /* 0x03 */ + "umount", /* 0x04 */ +}; + +static char *action_to_string(enum kobject_action action) +{ + if (action >= KOBJ_MAX_ACTION) + return NULL; + else + return actions[action]; +} + +#ifdef CONFIG_KOBJECT_UEVENT +static struct sock *uevent_sock; + +/** + * send_uevent - notify userspace by sending event trough netlink socket + * + * @signal: signal name + * @obj: object path (kobject) + * @buf: buffer used to pass auxiliary data like the hotplug environment + * @buflen: + * gfp_mask: + */ +static int send_uevent(const char *signal, const char *obj, const void *buf, + int buflen, int gfp_mask) +{ + struct sk_buff *skb; + char *pos; + int len; + + if (!uevent_sock) + return -EIO; + + len = strlen(signal) + 1; + len += strlen(obj) + 1; + len += buflen; + + skb = alloc_skb(len, gfp_mask); + if (!skb) + return -ENOMEM; + + pos = skb_put(skb, len); + + pos += sprintf(pos, "%s@%s", signal, obj) + 1; + memcpy(pos, buf, buflen); + + return netlink_broadcast(uevent_sock, skb, 0, 1, gfp_mask); +} + +static int do_kobject_uevent(struct kobject *kobj, enum kobject_action action, + struct attribute *attr, int gfp_mask) +{ + char *path; + char *attrpath; + char *signal; + int len; + int rc = -ENOMEM; + + path = kobject_get_path(kobj, gfp_mask); + if (!path) + return -ENOMEM; + + signal = action_to_string(action); + if (!signal) + return -EINVAL; + + if (attr) { + len = strlen(path); + len += strlen(attr->name) + 2; + attrpath = kmalloc(len, gfp_mask); + if (!attrpath) + goto exit; + sprintf(attrpath, "%s/%s", path, attr->name); + rc = send_uevent(signal, attrpath, NULL, 0, gfp_mask); + kfree(attrpath); + } else { + rc = send_uevent(signal, path, NULL, 0, gfp_mask); + } + +exit: + kfree(path); + return rc; +} + +/** + * kobject_uevent - notify userspace by sending event through netlink socket + * + * @signal: signal name + * @kobj: struct kobject that the event is happening to + * @attr: optional struct attribute the event belongs to + */ +int kobject_uevent(struct kobject *kobj, enum kobject_action action, + struct attribute *attr) +{ + return do_kobject_uevent(kobj, action, attr, GFP_KERNEL); +} +EXPORT_SYMBOL_GPL(kobject_uevent); + +int kobject_uevent_atomic(struct kobject *kobj, enum kobject_action action, + struct attribute *attr) +{ + return do_kobject_uevent(kobj, action, attr, GFP_ATOMIC); +} + +EXPORT_SYMBOL_GPL(kobject_uevent_atomic); + +static int __init kobject_uevent_init(void) +{ + uevent_sock = netlink_kernel_create(NETLINK_KOBJECT_UEVENT, NULL); + + if (!uevent_sock) { + printk(KERN_ERR + "kobject_uevent: unable to create netlink socket!\n"); + return -ENODEV; + } + + return 0; +} + +core_initcall(kobject_uevent_init); + +#else +static inline int send_uevent(const char *signal, const char *obj, + const void *buf, int buflen, int gfp_mask) +{ + return 0; +} + +#endif /* CONFIG_KOBJECT_UEVENT */ + + +#ifdef CONFIG_HOTPLUG +u64 hotplug_seqnum; +static spinlock_t sequence_lock = SPIN_LOCK_UNLOCKED; + +#define BUFFER_SIZE 1024 /* should be enough memory for the env */ +#define NUM_ENVP 32 /* number of env pointers */ +/** + * kobject_hotplug - notify userspace by executing /sbin/hotplug + * + * @action: action that is happening (usually "ADD" or "REMOVE") + * @kobj: struct kobject that the action is happening to + */ +void kobject_hotplug(struct kobject *kobj, enum kobject_action action) +{ + char *argv [3]; + char **envp = NULL; + char *buffer = NULL; + char *scratch; + int i = 0; + int retval; + char *kobj_path = NULL; + char *name = NULL; + char *action_string; + u64 seq; + struct kobject *top_kobj = kobj; + struct kset *kset; + + if (!top_kobj->kset && top_kobj->parent) { + do { + top_kobj = top_kobj->parent; + } while (!top_kobj->kset && top_kobj->parent); + } + + if (top_kobj->kset && top_kobj->kset->hotplug_ops) + kset = top_kobj->kset; + else + return; + + /* If the kset has a filter operation, call it. + Skip the event, if the filter returns zero. */ + if (kset->hotplug_ops->filter) { + if (!kset->hotplug_ops->filter(kset, kobj)) + return; + } + + pr_debug ("%s\n", __FUNCTION__); + + action_string = action_to_string(action); + if (!action_string) + return; + + envp = kmalloc(NUM_ENVP * sizeof (char *), GFP_KERNEL); + if (!envp) + return; + memset (envp, 0x00, NUM_ENVP * sizeof (char *)); + + buffer = kmalloc(BUFFER_SIZE, GFP_KERNEL); + if (!buffer) + goto exit; + + if (kset->hotplug_ops->name) + name = kset->hotplug_ops->name(kset, kobj); + if (name == NULL) + name = kset->kobj.name; + + argv [0] = hotplug_path; + argv [1] = name; + argv [2] = NULL; + + /* minimal command environment */ + envp [i++] = "HOME=/"; + envp [i++] = "PATH=/sbin:/bin:/usr/sbin:/usr/bin"; + + scratch = buffer; + + envp [i++] = scratch; + scratch += sprintf(scratch, "ACTION=%s", action_string) + 1; + + kobj_path = kobject_get_path(kobj, GFP_KERNEL); + if (!kobj_path) + goto exit; + + envp [i++] = scratch; + scratch += sprintf (scratch, "DEVPATH=%s", kobj_path) + 1; + + spin_lock(&sequence_lock); + seq = ++hotplug_seqnum; + spin_unlock(&sequence_lock); + + envp [i++] = scratch; + scratch += sprintf(scratch, "SEQNUM=%lld", (long long)seq) + 1; + + envp [i++] = scratch; + scratch += sprintf(scratch, "SUBSYSTEM=%s", name) + 1; + + if (kset->hotplug_ops->hotplug) { + /* have the kset specific function add its stuff */ + retval = kset->hotplug_ops->hotplug (kset, kobj, + &envp[i], NUM_ENVP - i, scratch, + BUFFER_SIZE - (scratch - buffer)); + if (retval) { + pr_debug ("%s - hotplug() returned %d\n", + __FUNCTION__, retval); + goto exit; + } + } + + pr_debug ("%s: %s %s %s %s %s %s %s\n", __FUNCTION__, argv[0], argv[1], + envp[0], envp[1], envp[2], envp[3], envp[4]); + + send_uevent(action_string, kobj_path, buffer, scratch - buffer, GFP_KERNEL); + + if (!hotplug_path[0]) + goto exit; + + retval = call_usermodehelper (argv[0], argv, envp, 0); + if (retval) + pr_debug ("%s - call_usermodehelper returned %d\n", + __FUNCTION__, retval); + +exit: + kfree(kobj_path); + kfree(buffer); + kfree(envp); + return; +} +EXPORT_SYMBOL(kobject_hotplug); +#endif /* CONFIG_HOTPLUG */ + + |