提交 7eff2e7a 编写于 作者: K Kay Sievers 提交者: Greg Kroah-Hartman

Driver core: change add_uevent_var to use a struct

This changes the uevent buffer functions to use a struct instead of a
long list of parameters. It does no longer require the caller to do the
proper buffer termination and size accounting, which is currently wrong
in some places. It fixes a known bug where parts of the uevent
environment are overwritten because of wrong index calculations.

Many thanks to Mathieu Desnoyers for finding bugs and improving the
error handling.
Signed-off-by: NKay Sievers <kay.sievers@vrfy.org>
Cc: Mathieu Desnoyers <mathieu.desnoyers@polymtl.ca>
Cc: Cornelia Huck <cornelia.huck@de.ibm.com>
Signed-off-by: NGreg Kroah-Hartman <gregkh@suse.de>
上级 8380770c
......@@ -66,8 +66,7 @@ static int tiocx_match(struct device *dev, struct device_driver *drv)
}
static int tiocx_uevent(struct device *dev, char **envp, int num_envp,
char *buffer, int buffer_size)
static int tiocx_uevent(struct device *dev, struct kobj_uevent_env *env)
{
return -ENODEV;
}
......
......@@ -57,26 +57,21 @@ ssize_t of_device_get_modalias(struct of_device *ofdev,
return tsize;
}
int of_device_uevent(struct device *dev,
char **envp, int num_envp, char *buffer, int buffer_size)
int of_device_uevent(struct device *dev, struct kobj_uevent_env *env)
{
struct of_device *ofdev;
const char *compat;
int i = 0, length = 0, seen = 0, cplen, sl;
int seen = 0, cplen, sl;
if (!dev)
return -ENODEV;
ofdev = to_of_device(dev);
if (add_uevent_var(envp, num_envp, &i,
buffer, buffer_size, &length,
"OF_NAME=%s", ofdev->node->name))
if (add_uevent_var(env, "OF_NAME=%s", ofdev->node->name))
return -ENOMEM;
if (add_uevent_var(envp, num_envp, &i,
buffer, buffer_size, &length,
"OF_TYPE=%s", ofdev->node->type))
if (add_uevent_var(env, "OF_TYPE=%s", ofdev->node->type))
return -ENOMEM;
/* Since the compatible field can contain pretty much anything
......@@ -85,9 +80,7 @@ int of_device_uevent(struct device *dev,
compat = of_get_property(ofdev->node, "compatible", &cplen);
while (compat && *compat && cplen > 0) {
if (add_uevent_var(envp, num_envp, &i,
buffer, buffer_size, &length,
"OF_COMPATIBLE_%d=%s", seen, compat))
if (add_uevent_var(env, "OF_COMPATIBLE_%d=%s", seen, compat))
return -ENOMEM;
sl = strlen (compat) + 1;
......@@ -96,25 +89,17 @@ int of_device_uevent(struct device *dev,
seen++;
}
if (add_uevent_var(envp, num_envp, &i,
buffer, buffer_size, &length,
"OF_COMPATIBLE_N=%d", seen))
if (add_uevent_var(env, "OF_COMPATIBLE_N=%d", seen))
return -ENOMEM;
/* modalias is trickier, we add it in 2 steps */
if (add_uevent_var(envp, num_envp, &i,
buffer, buffer_size, &length,
"MODALIAS="))
if (add_uevent_var(env, "MODALIAS="))
return -ENOMEM;
sl = of_device_get_modalias(ofdev, &buffer[length-1],
buffer_size-length);
if (sl >= (buffer_size-length))
sl = of_device_get_modalias(ofdev, &env->buf[env->buflen-1],
sizeof(env->buf) - env->buflen);
if (sl >= (sizeof(env->buf) - env->buflen))
return -ENOMEM;
length += sl;
envp[i] = NULL;
env->buflen += sl;
return 0;
}
......
......@@ -317,30 +317,20 @@ static int vio_bus_match(struct device *dev, struct device_driver *drv)
return (ids != NULL) && (vio_match_device(ids, vio_dev) != NULL);
}
static int vio_hotplug(struct device *dev, char **envp, int num_envp,
char *buffer, int buffer_size)
static int vio_hotplug(struct device *dev, struct kobj_uevent_env *env)
{
const struct vio_dev *vio_dev = to_vio_dev(dev);
struct device_node *dn;
const char *cp;
int length;
if (!num_envp)
return -ENOMEM;
dn = dev->archdata.of_node;
if (!dn)
return -ENODEV;
cp = of_get_property(dn, "compatible", &length);
cp = of_get_property(dn, "compatible", NULL);
if (!cp)
return -ENODEV;
envp[0] = buffer;
length = scnprintf(buffer, buffer_size, "MODALIAS=vio:T%sS%s",
vio_dev->type, cp);
if ((buffer_size - length) <= 0)
return -ENOMEM;
envp[1] = NULL;
add_uevent_var(env, "MODALIAS=vio:T%sS%s", vio_dev->type, cp);
return 0;
}
......
......@@ -437,18 +437,13 @@ static void ps3_system_bus_shutdown(struct device *_dev)
dev_dbg(&dev->core, " <- %s:%d\n", __func__, __LINE__);
}
static int ps3_system_bus_uevent(struct device *_dev, char **envp,
int num_envp, char *buffer, int buffer_size)
static int ps3_system_bus_uevent(struct device *_dev, struct kobj_uevent_env *env)
{
struct ps3_system_bus_device *dev = ps3_dev_to_system_bus_dev(_dev);
int i = 0, length = 0;
if (add_uevent_var(envp, num_envp, &i, buffer, buffer_size,
&length, "MODALIAS=ps3:%d",
dev->match_id))
if (add_uevent_var(env, "MODALIAS=ps3:%d", dev->match_id))
return -ENOMEM;
envp[i] = NULL;
return 0;
}
......
......@@ -540,61 +540,42 @@ static int block_uevent_filter(struct kset *kset, struct kobject *kobj)
return ((ktype == &ktype_block) || (ktype == &ktype_part));
}
static int block_uevent(struct kset *kset, struct kobject *kobj, char **envp,
int num_envp, char *buffer, int buffer_size)
static int block_uevent(struct kset *kset, struct kobject *kobj,
struct kobj_uevent_env *env)
{
struct kobj_type *ktype = get_ktype(kobj);
struct device *physdev;
struct gendisk *disk;
struct hd_struct *part;
int length = 0;
int i = 0;
if (ktype == &ktype_block) {
disk = container_of(kobj, struct gendisk, kobj);
add_uevent_var(envp, num_envp, &i, buffer, buffer_size,
&length, "MINOR=%u", disk->first_minor);
add_uevent_var(env, "MINOR=%u", disk->first_minor);
} else if (ktype == &ktype_part) {
disk = container_of(kobj->parent, struct gendisk, kobj);
part = container_of(kobj, struct hd_struct, kobj);
add_uevent_var(envp, num_envp, &i, buffer, buffer_size,
&length, "MINOR=%u",
add_uevent_var(env, "MINOR=%u",
disk->first_minor + part->partno);
} else
return 0;
add_uevent_var(envp, num_envp, &i, buffer, buffer_size, &length,
"MAJOR=%u", disk->major);
add_uevent_var(env, "MAJOR=%u", disk->major);
/* add physical device, backing this device */
physdev = disk->driverfs_dev;
if (physdev) {
char *path = kobject_get_path(&physdev->kobj, GFP_KERNEL);
add_uevent_var(envp, num_envp, &i, buffer, buffer_size,
&length, "PHYSDEVPATH=%s", path);
add_uevent_var(env, "PHYSDEVPATH=%s", path);
kfree(path);
if (physdev->bus)
add_uevent_var(envp, num_envp, &i,
buffer, buffer_size, &length,
"PHYSDEVBUS=%s",
physdev->bus->name);
add_uevent_var(env, "PHYSDEVBUS=%s", physdev->bus->name);
if (physdev->driver)
add_uevent_var(envp, num_envp, &i,
buffer, buffer_size, &length,
"PHYSDEVDRIVER=%s",
physdev->driver->name);
add_uevent_var(env, physdev->driver->name);
}
/* terminate, set to next free slot, shrink available space */
envp[i] = NULL;
envp = &envp[i];
num_envp -= i;
buffer = &buffer[length];
buffer_size -= length;
return 0;
}
......
......@@ -319,16 +319,18 @@ static int acpi_bus_match(struct device *dev, struct device_driver *drv)
return !acpi_match_device_ids(acpi_dev, acpi_drv->ids);
}
static int acpi_device_uevent(struct device *dev, char **envp, int num_envp,
char *buffer, int buffer_size)
static int acpi_device_uevent(struct device *dev, struct kobj_uevent_env *env)
{
struct acpi_device *acpi_dev = to_acpi_device(dev);
int len;
strcpy(buffer, "MODALIAS=");
if (create_modalias(acpi_dev, buffer + 9, buffer_size - 9) > 0) {
envp[0] = buffer;
envp[1] = NULL;
}
if (add_uevent_var(env, "MODALIAS="))
return -ENOMEM;
len = create_modalias(acpi_dev, &env->buf[env->buflen - 1],
sizeof(env->buf) - env->buflen);
if (len >= (sizeof(env->buf) - env->buflen))
return -ENOMEM;
env->buflen += len;
return 0;
}
......
......@@ -44,15 +44,12 @@ static int amba_match(struct device *dev, struct device_driver *drv)
}
#ifdef CONFIG_HOTPLUG
static int amba_uevent(struct device *dev, char **envp, int nr_env, char *buf, int bufsz)
static int amba_uevent(struct device *dev, struct kobj_uevent_env *env)
{
struct amba_device *pcdev = to_amba_device(dev);
int retval = 0, i = 0, len = 0;
int retval = 0;
retval = add_uevent_var(envp, nr_env, &i,
buf, bufsz, &len,
"AMBA_ID=%08x", pcdev->periphid);
envp[i] = NULL;
retval = add_uevent_var(env, "AMBA_ID=%08x", pcdev->periphid);
return retval;
}
#else
......
......@@ -180,8 +180,7 @@ static void class_device_create_release(struct class_device *class_dev)
/* needed to allow these devices to have parent class devices */
static int class_device_create_uevent(struct class_device *class_dev,
char **envp, int num_envp,
char *buffer, int buffer_size)
struct kobj_uevent_env *env)
{
pr_debug("%s called for %s\n", __FUNCTION__, class_dev->class_id);
return 0;
......@@ -403,64 +402,43 @@ static void remove_deprecated_class_device_links(struct class_device *cd)
{ }
#endif
static int class_uevent(struct kset *kset, struct kobject *kobj, char **envp,
int num_envp, char *buffer, int buffer_size)
static int class_uevent(struct kset *kset, struct kobject *kobj,
struct kobj_uevent_env *env)
{
struct class_device *class_dev = to_class_dev(kobj);
struct device *dev = class_dev->dev;
int i = 0;
int length = 0;
int retval = 0;
pr_debug("%s - name = %s\n", __FUNCTION__, class_dev->class_id);
if (MAJOR(class_dev->devt)) {
add_uevent_var(envp, num_envp, &i,
buffer, buffer_size, &length,
"MAJOR=%u", MAJOR(class_dev->devt));
add_uevent_var(env, "MAJOR=%u", MAJOR(class_dev->devt));
add_uevent_var(envp, num_envp, &i,
buffer, buffer_size, &length,
"MINOR=%u", MINOR(class_dev->devt));
add_uevent_var(env, "MINOR=%u", MINOR(class_dev->devt));
}
if (dev) {
const char *path = kobject_get_path(&dev->kobj, GFP_KERNEL);
if (path) {
add_uevent_var(envp, num_envp, &i,
buffer, buffer_size, &length,
"PHYSDEVPATH=%s", path);
add_uevent_var(env, "PHYSDEVPATH=%s", path);
kfree(path);
}
if (dev->bus)
add_uevent_var(envp, num_envp, &i,
buffer, buffer_size, &length,
"PHYSDEVBUS=%s", dev->bus->name);
add_uevent_var(env, "PHYSDEVBUS=%s", dev->bus->name);
if (dev->driver)
add_uevent_var(envp, num_envp, &i,
buffer, buffer_size, &length,
"PHYSDEVDRIVER=%s", dev->driver->name);
add_uevent_var(env, "PHYSDEVDRIVER=%s", dev->driver->name);
}
/* terminate, set to next free slot, shrink available space */
envp[i] = NULL;
envp = &envp[i];
num_envp -= i;
buffer = &buffer[length];
buffer_size -= length;
if (class_dev->uevent) {
/* have the class device specific function add its stuff */
retval = class_dev->uevent(class_dev, envp, num_envp,
buffer, buffer_size);
retval = class_dev->uevent(class_dev, env);
if (retval)
pr_debug("class_dev->uevent() returned %d\n", retval);
} else if (class_dev->class->uevent) {
/* have the class specific function add its stuff */
retval = class_dev->class->uevent(class_dev, envp, num_envp,
buffer, buffer_size);
retval = class_dev->class->uevent(class_dev, env);
if (retval)
pr_debug("class->uevent() returned %d\n", retval);
}
......
......@@ -141,33 +141,23 @@ static const char *dev_uevent_name(struct kset *kset, struct kobject *kobj)
return NULL;
}
static int dev_uevent(struct kset *kset, struct kobject *kobj, char **envp,
int num_envp, char *buffer, int buffer_size)
static int dev_uevent(struct kset *kset, struct kobject *kobj,
struct kobj_uevent_env *env)
{
struct device *dev = to_dev(kobj);
int i = 0;
int length = 0;
int retval = 0;
/* add the major/minor if present */
if (MAJOR(dev->devt)) {
add_uevent_var(envp, num_envp, &i,
buffer, buffer_size, &length,
"MAJOR=%u", MAJOR(dev->devt));
add_uevent_var(envp, num_envp, &i,
buffer, buffer_size, &length,
"MINOR=%u", MINOR(dev->devt));
add_uevent_var(env, "MAJOR=%u", MAJOR(dev->devt));
add_uevent_var(env, "MINOR=%u", MINOR(dev->devt));
}
if (dev->type && dev->type->name)
add_uevent_var(envp, num_envp, &i,
buffer, buffer_size, &length,
"DEVTYPE=%s", dev->type->name);
add_uevent_var(env, "DEVTYPE=%s", dev->type->name);
if (dev->driver)
add_uevent_var(envp, num_envp, &i,
buffer, buffer_size, &length,
"DRIVER=%s", dev->driver->name);
add_uevent_var(env, "DRIVER=%s", dev->driver->name);
#ifdef CONFIG_SYSFS_DEPRECATED
if (dev->class) {
......@@ -181,59 +171,43 @@ static int dev_uevent(struct kset *kset, struct kobject *kobj, char **envp,
path = kobject_get_path(&parent->kobj, GFP_KERNEL);
if (path) {
add_uevent_var(envp, num_envp, &i,
buffer, buffer_size, &length,
"PHYSDEVPATH=%s", path);
add_uevent_var(env, "PHYSDEVPATH=%s", path);
kfree(path);
}
add_uevent_var(envp, num_envp, &i,
buffer, buffer_size, &length,
"PHYSDEVBUS=%s", parent->bus->name);
add_uevent_var(env, "PHYSDEVBUS=%s", parent->bus->name);
if (parent->driver)
add_uevent_var(envp, num_envp, &i,
buffer, buffer_size, &length,
"PHYSDEVDRIVER=%s", parent->driver->name);
add_uevent_var(env, "PHYSDEVDRIVER=%s",
parent->driver->name);
}
} else if (dev->bus) {
add_uevent_var(envp, num_envp, &i,
buffer, buffer_size, &length,
"PHYSDEVBUS=%s", dev->bus->name);
add_uevent_var(env, "PHYSDEVBUS=%s", dev->bus->name);
if (dev->driver)
add_uevent_var(envp, num_envp, &i,
buffer, buffer_size, &length,
"PHYSDEVDRIVER=%s", dev->driver->name);
add_uevent_var(env, "PHYSDEVDRIVER=%s", dev->driver->name);
}
#endif
/* terminate, set to next free slot, shrink available space */
envp[i] = NULL;
envp = &envp[i];
num_envp -= i;
buffer = &buffer[length];
buffer_size -= length;
/* have the bus specific function add its stuff */
if (dev->bus && dev->bus->uevent) {
/* have the bus specific function add its stuff */
retval = dev->bus->uevent(dev, envp, num_envp, buffer, buffer_size);
retval = dev->bus->uevent(dev, env);
if (retval)
pr_debug ("%s: bus uevent() returned %d\n",
__FUNCTION__, retval);
}
/* have the class specific function add its stuff */
if (dev->class && dev->class->dev_uevent) {
/* have the class specific function add its stuff */
retval = dev->class->dev_uevent(dev, envp, num_envp, buffer, buffer_size);
retval = dev->class->dev_uevent(dev, env);
if (retval)
pr_debug("%s: class uevent() returned %d\n",
__FUNCTION__, retval);
}
/* have the device type specific fuction add its stuff */
if (dev->type && dev->type->uevent) {
/* have the device type specific fuction add its stuff */
retval = dev->type->uevent(dev, envp, num_envp, buffer, buffer_size);
retval = dev->type->uevent(dev, env);
if (retval)
pr_debug("%s: dev_type uevent() returned %d\n",
__FUNCTION__, retval);
......@@ -253,9 +227,7 @@ static ssize_t show_uevent(struct device *dev, struct device_attribute *attr,
{
struct kobject *top_kobj;
struct kset *kset;
char *envp[32];
char *data = NULL;
char *pos;
struct kobj_uevent_env *env = NULL;
int i;
size_t count = 0;
int retval;
......@@ -278,26 +250,20 @@ static ssize_t show_uevent(struct device *dev, struct device_attribute *attr,
if (!kset->uevent_ops->filter(kset, &dev->kobj))
goto out;
data = (char *)get_zeroed_page(GFP_KERNEL);
if (!data)
env = kzalloc(sizeof(struct kobj_uevent_env), GFP_KERNEL);
if (!env)
return -ENOMEM;
/* let the kset specific function add its keys */
pos = data;
memset(envp, 0, sizeof(envp));
retval = kset->uevent_ops->uevent(kset, &dev->kobj,
envp, ARRAY_SIZE(envp),
pos, PAGE_SIZE);
retval = kset->uevent_ops->uevent(kset, &dev->kobj, env);
if (retval)
goto out;
/* copy keys to file */
for (i = 0; envp[i]; i++) {
pos = &buf[count];
count += sprintf(pos, "%s\n", envp[i]);
}
for (i = 0; i < env->envp_idx; i++)
count += sprintf(&buf[count], "%s\n", env->envp[i]);
out:
free_page((unsigned long)data);
kfree(env);
return count;
}
......
......@@ -88,19 +88,14 @@ static CLASS_ATTR(timeout, 0644, firmware_timeout_show, firmware_timeout_store);
static void fw_dev_release(struct device *dev);
static int firmware_uevent(struct device *dev, char **envp, int num_envp,
char *buffer, int buffer_size)
static int firmware_uevent(struct device *dev, struct kobj_uevent_env *env)
{
struct firmware_priv *fw_priv = dev_get_drvdata(dev);
int i = 0, len = 0;
if (add_uevent_var(envp, num_envp, &i, buffer, buffer_size, &len,
"FIRMWARE=%s", fw_priv->fw_id))
if (add_uevent_var(env, "FIRMWARE=%s", fw_priv->fw_id))
return -ENOMEM;
if (add_uevent_var(envp, num_envp, &i, buffer, buffer_size, &len,
"TIMEOUT=%i", loading_timeout))
if (add_uevent_var(env, "TIMEOUT=%i", loading_timeout))
return -ENOMEM;
envp[i] = NULL;
return 0;
}
......
......@@ -34,8 +34,7 @@ static const char *memory_uevent_name(struct kset *kset, struct kobject *kobj)
return MEMORY_CLASS_NAME;
}
static int memory_uevent(struct kset *kset, struct kobject *kobj, char **envp,
int num_envp, char *buffer, int buffer_size)
static int memory_uevent(struct kset *kset, struct kobj_uevent_env *env)
{
int retval = 0;
......
......@@ -529,13 +529,11 @@ static struct device_attribute platform_dev_attrs[] = {
__ATTR_NULL,
};
static int platform_uevent(struct device *dev, char **envp, int num_envp,
char *buffer, int buffer_size)
static int platform_uevent(struct device *dev, struct kobj_uevent_env *env)
{
struct platform_device *pdev = to_platform_device(dev);
envp[0] = buffer;
snprintf(buffer, buffer_size, "MODALIAS=platform:%s", pdev->name);
add_uevent_var(env, "MODALIAS=platform:%s", pdev->name);
return 0;
}
......
......@@ -128,16 +128,11 @@ static int eisa_bus_match (struct device *dev, struct device_driver *drv)
return 0;
}
static int eisa_bus_uevent(struct device *dev, char **envp, int num_envp,
char *buffer, int buffer_size)
static int eisa_bus_uevent(struct device *dev, struct kobj_uevent_env *env)
{
struct eisa_device *edev = to_eisa_device(dev);
int i = 0;
int length = 0;
add_uevent_var(envp, num_envp, &i, buffer, buffer_size, &length,
"MODALIAS=" EISA_DEVICE_MODALIAS_FMT, edev->id.sig);
envp[i] = NULL;
add_uevent_var(env, "MODALIAS=" EISA_DEVICE_MODALIAS_FMT, edev->id.sig);
return 0;
}
......
......@@ -130,23 +130,16 @@ static int get_modalias(struct fw_unit *unit, char *buffer, size_t buffer_size)
}
static int
fw_unit_uevent(struct device *dev, char **envp, int num_envp,
char *buffer, int buffer_size)
fw_unit_uevent(struct device *dev, struct kobj_uevent_env *env)
{
struct fw_unit *unit = fw_unit(dev);
char modalias[64];
int length = 0;
int i = 0;
get_modalias(unit, modalias, sizeof(modalias));
if (add_uevent_var(envp, num_envp, &i,
buffer, buffer_size, &length,
"MODALIAS=%s", modalias))
if (add_uevent_var(env, "MODALIAS=%s", modalias))
return -ENOMEM;
envp[i] = NULL;
return 0;
}
......
......@@ -134,14 +134,17 @@ static struct attribute_group* sys_dmi_attribute_groups[] = {
NULL
};
static int dmi_dev_uevent(struct device *dev, char **envp,
int num_envp, char *buffer, int buffer_size)
static int dmi_dev_uevent(struct device *dev, struct kobj_uevent_env *env)
{
strcpy(buffer, "MODALIAS=");
get_modalias(buffer+9, buffer_size-9);
envp[0] = buffer;
envp[1] = NULL;
ssize_t len;
if (add_uevent_var(env, "MODALIAS="))
return -ENOMEM;
len = get_modalias(&env->buf[env->buflen - 1],
sizeof(env->buf) - env->buflen);
if (len >= (sizeof(env->buf) - env->buflen))
return -ENOMEM;
env->buflen += len;
return 0;
}
......
......@@ -67,20 +67,16 @@ static int i2c_device_match(struct device *dev, struct device_driver *drv)
#ifdef CONFIG_HOTPLUG
/* uevent helps with hotplug: modprobe -q $(MODALIAS) */
static int i2c_device_uevent(struct device *dev, char **envp, int num_envp,
char *buffer, int buffer_size)
static int i2c_device_uevent(struct device *dev, struct kobj_uevent_env *env)
{
struct i2c_client *client = to_i2c_client(dev);
int i = 0, length = 0;
/* by definition, legacy drivers can't hotplug */
if (dev->driver || !client->driver_name)
return 0;
if (add_uevent_var(envp, num_envp, &i, buffer, buffer_size, &length,
"MODALIAS=%s", client->driver_name))
if (add_uevent_var(env, "MODALIAS=%s", client->driver_name))
return -ENOMEM;
envp[i] = NULL;
dev_dbg(dev, "uevent\n");
return 0;
}
......
......@@ -1663,20 +1663,13 @@ static struct device_attribute ide_dev_attrs[] = {
__ATTR_NULL
};
static int ide_uevent(struct device *dev, char **envp, int num_envp,
char *buffer, int buffer_size)
static int ide_uevent(struct device *dev, struct kobj_uevent_env *env)
{
ide_drive_t *drive = to_ide_device(dev);
int i = 0;
int length = 0;
add_uevent_var(envp, num_envp, &i, buffer, buffer_size, &length,
"MEDIA=%s", media_string(drive));
add_uevent_var(envp, num_envp, &i, buffer, buffer_size, &length,
"DRIVENAME=%s", drive->name);
add_uevent_var(envp, num_envp, &i, buffer, buffer_size, &length,
"MODALIAS=ide:m-%s", media_string(drive));
envp[i] = NULL;
add_uevent_var(env, "MEDIA=%s", media_string(drive));
add_uevent_var(env, "DRIVENAME=%s", drive->name);
add_uevent_var(env, "MODALIAS=ide:m-%s", media_string(drive));
return 0;
}
......
......@@ -153,8 +153,7 @@ struct host_info {
};
static int nodemgr_bus_match(struct device * dev, struct device_driver * drv);
static int nodemgr_uevent(struct device *dev, char **envp, int num_envp,
char *buffer, int buffer_size);
static int nodemgr_uevent(struct device *dev, struct kobj_uevent_env *env);
static void nodemgr_resume_ne(struct node_entry *ne);
static void nodemgr_remove_ne(struct node_entry *ne);
static struct node_entry *find_entry_by_guid(u64 guid);
......@@ -1160,12 +1159,9 @@ static void nodemgr_process_root_directory(struct host_info *hi, struct node_ent
#ifdef CONFIG_HOTPLUG
static int nodemgr_uevent(struct device *dev, char **envp, int num_envp,
char *buffer, int buffer_size)
static int nodemgr_uevent(struct device *dev, struct kobj_uevent_env *env)
{
struct unit_directory *ud;
int i = 0;
int length = 0;
int retval = 0;
/* ieee1394:venNmoNspNverN */
char buf[8 + 1 + 3 + 8 + 2 + 8 + 2 + 8 + 3 + 8 + 1];
......@@ -1180,9 +1176,7 @@ static int nodemgr_uevent(struct device *dev, char **envp, int num_envp,
#define PUT_ENVP(fmt,val) \
do { \
retval = add_uevent_var(envp, num_envp, &i, \
buffer, buffer_size, &length, \
fmt, val); \
retval = add_uevent_var(env, fmt, val); \
if (retval) \
return retval; \
} while (0)
......@@ -1201,15 +1195,12 @@ do { \
#undef PUT_ENVP
envp[i] = NULL;
return 0;
}
#else
static int nodemgr_uevent(struct device *dev, char **envp, int num_envp,
char *buffer, int buffer_size)
static int nodemgr_uevent(struct device *dev, struct kobj_uevent_env *env)
{
return -ENODEV;
}
......
......@@ -434,21 +434,18 @@ static void ib_device_release(struct class_device *cdev)
kfree(dev);
}
static int ib_device_uevent(struct class_device *cdev, char **envp,
int num_envp, char *buf, int size)
static int ib_device_uevent(struct class_device *cdev,
struct kobj_uevent_env *env)
{
struct ib_device *dev = container_of(cdev, struct ib_device, class_dev);
int i = 0, len = 0;
if (add_uevent_var(envp, num_envp, &i, buf, size, &len,
"NAME=%s", dev->name))
if (add_uevent_var(env, "NAME=%s", dev->name))
return -ENOMEM;
/*
* It would be nice to pass the node GUID with the event...
*/
envp[i] = NULL;
return 0;
}
......
......@@ -859,87 +859,66 @@ static void input_dev_release(struct device *device)
* Input uevent interface - loading event handlers based on
* device bitfields.
*/
static int input_add_uevent_bm_var(char **envp, int num_envp, int *cur_index,
char *buffer, int buffer_size, int *cur_len,
static int input_add_uevent_bm_var(struct kobj_uevent_env *env,
const char *name, unsigned long *bitmap, int max)
{
if (*cur_index >= num_envp - 1)
return -ENOMEM;
envp[*cur_index] = buffer + *cur_len;
int len;
*cur_len += snprintf(buffer + *cur_len, max(buffer_size - *cur_len, 0), name);
if (*cur_len >= buffer_size)
if (add_uevent_var(env, "%s=", name))
return -ENOMEM;
*cur_len += input_print_bitmap(buffer + *cur_len,
max(buffer_size - *cur_len, 0),
bitmap, max, 0) + 1;
if (*cur_len > buffer_size)
len = input_print_bitmap(&env->buf[env->buflen - 1],
sizeof(env->buf) - env->buflen,
bitmap, max, 0);
if (len >= (sizeof(env->buf) - env->buflen))
return -ENOMEM;
(*cur_index)++;
env->buflen += len;
return 0;
}
static int input_add_uevent_modalias_var(char **envp, int num_envp, int *cur_index,
char *buffer, int buffer_size, int *cur_len,
static int input_add_uevent_modalias_var(struct kobj_uevent_env *env,
struct input_dev *dev)
{
if (*cur_index >= num_envp - 1)
return -ENOMEM;
envp[*cur_index] = buffer + *cur_len;
int len;
*cur_len += snprintf(buffer + *cur_len, max(buffer_size - *cur_len, 0),
"MODALIAS=");
if (*cur_len >= buffer_size)
if (add_uevent_var(env, "MODALIAS="))
return -ENOMEM;
*cur_len += input_print_modalias(buffer + *cur_len,
max(buffer_size - *cur_len, 0),
dev, 0) + 1;
if (*cur_len > buffer_size)
len = input_print_modalias(&env->buf[env->buflen - 1],
sizeof(env->buf) - env->buflen,
dev, 0);
if (len >= (sizeof(env->buf) - env->buflen))
return -ENOMEM;
(*cur_index)++;
env->buflen += len;
return 0;
}
#define INPUT_ADD_HOTPLUG_VAR(fmt, val...) \
do { \
int err = add_uevent_var(envp, num_envp, &i, \
buffer, buffer_size, &len, \
fmt, val); \
int err = add_uevent_var(env, fmt, val); \
if (err) \
return err; \
} while (0)
#define INPUT_ADD_HOTPLUG_BM_VAR(name, bm, max) \
do { \
int err = input_add_uevent_bm_var(envp, num_envp, &i, \
buffer, buffer_size, &len, \
name, bm, max); \
int err = input_add_uevent_bm_var(env, name, bm, max); \
if (err) \
return err; \
} while (0)
#define INPUT_ADD_HOTPLUG_MODALIAS_VAR(dev) \
do { \
int err = input_add_uevent_modalias_var(envp, \
num_envp, &i, \
buffer, buffer_size, &len, \
dev); \
int err = input_add_uevent_modalias_var(env, dev); \
if (err) \
return err; \
} while (0)
static int input_dev_uevent(struct device *device, char **envp,
int num_envp, char *buffer, int buffer_size)
static int input_dev_uevent(struct device *device, struct kobj_uevent_env *env)
{
struct input_dev *dev = to_input_dev(device);
int i = 0;
int len = 0;
INPUT_ADD_HOTPLUG_VAR("PRODUCT=%x/%x/%x/%x",
dev->id.bustype, dev->id.vendor,
......@@ -971,7 +950,6 @@ static int input_dev_uevent(struct device *device, char **envp,
INPUT_ADD_HOTPLUG_MODALIAS_VAR(dev);
envp[i] = NULL;
return 0;
}
......
......@@ -876,18 +876,14 @@ static int serio_bus_match(struct device *dev, struct device_driver *drv)
#define SERIO_ADD_UEVENT_VAR(fmt, val...) \
do { \
int err = add_uevent_var(envp, num_envp, &i, \
buffer, buffer_size, &len, \
fmt, val); \
int err = add_uevent_var(env, fmt, val); \
if (err) \
return err; \
} while (0)
static int serio_uevent(struct device *dev, char **envp, int num_envp, char *buffer, int buffer_size)
static int serio_uevent(struct device *dev, struct kobj_uevent_env *env)
{
struct serio *serio;
int i = 0;
int len = 0;
if (!dev)
return -ENODEV;
......@@ -900,7 +896,6 @@ static int serio_uevent(struct device *dev, char **envp, int num_envp, char *buf
SERIO_ADD_UEVENT_VAR("SERIO_EXTRA=%02x", serio->id.extra);
SERIO_ADD_UEVENT_VAR("MODALIAS=serio:ty%02Xpr%02Xid%02Xex%02X",
serio->id.type, serio->id.proto, serio->id.id, serio->id.extra);
envp[i] = NULL;
return 0;
}
......@@ -908,7 +903,7 @@ static int serio_uevent(struct device *dev, char **envp, int num_envp, char *buf
#else
static int serio_uevent(struct device *dev, char **envp, int num_envp, char *buffer, int buffer_size)
static int serio_uevent(struct device *dev, struct kobj_uevent_env *env)
{
return -ENODEV;
}
......
......@@ -905,8 +905,8 @@ struct pvr2_sysfs *pvr2_sysfs_create(struct pvr2_context *mp,
}
static int pvr2_sysfs_hotplug(struct device *cd,char **envp,
int numenvp,char *buf,int size)
static int pvr2_sysfs_hotplug(struct device *d,
struct kobj_uevent_env *env)
{
/* Even though we don't do anything here, we still need this function
because sysfs will still try to call it. */
......
......@@ -57,16 +57,11 @@ static int tifm_bus_match(struct device *dev, struct device_driver *drv)
return 0;
}
static int tifm_uevent(struct device *dev, char **envp, int num_envp,
char *buffer, int buffer_size)
static int tifm_uevent(struct device *dev, struct kobj_uevent_env *env)
{
struct tifm_dev *sock = container_of(dev, struct tifm_dev, dev);
int i = 0;
int length = 0;
if (add_uevent_var(envp, num_envp, &i, buffer, buffer_size, &length,
"TIFM_CARD_TYPE=%s",
tifm_media_type_name(sock->type, 1)))
if (add_uevent_var(env, "TIFM_CARD_TYPE=%s", tifm_media_type_name(sock->type, 1)))
return -ENOMEM;
return 0;
......
......@@ -58,12 +58,11 @@ static int mmc_bus_match(struct device *dev, struct device_driver *drv)
}
static int
mmc_bus_uevent(struct device *dev, char **envp, int num_envp, char *buf,
int buf_size)
mmc_bus_uevent(struct device *dev, struct kobj_uevent_env *env)
{
struct mmc_card *card = dev_to_mmc_card(dev);
const char *type;
int i = 0, length = 0;
int retval = 0;
switch (card->type) {
case MMC_TYPE_MMC:
......@@ -80,20 +79,14 @@ mmc_bus_uevent(struct device *dev, char **envp, int num_envp, char *buf,
}
if (type) {
if (add_uevent_var(envp, num_envp, &i,
buf, buf_size, &length,
"MMC_TYPE=%s", type))
return -ENOMEM;
retval = add_uevent_var(env, "MMC_TYPE=%s", type);
if (retval)
return retval;
}
if (add_uevent_var(envp, num_envp, &i,
buf, buf_size, &length,
"MMC_NAME=%s", mmc_card_name(card)))
return -ENOMEM;
retval = add_uevent_var(env, "MMC_NAME=%s", mmc_card_name(card));
envp[i] = NULL;
return 0;
return retval;
}
static int mmc_bus_probe(struct device *dev)
......
......@@ -3,12 +3,9 @@
#include <linux/module.h>
#include "pci.h"
int pci_uevent(struct device *dev, char **envp, int num_envp,
char *buffer, int buffer_size)
int pci_uevent(struct device *dev, struct kobj_uevent_env *env)
{
struct pci_dev *pdev;
int i = 0;
int length = 0;
if (!dev)
return -ENODEV;
......@@ -17,37 +14,24 @@ int pci_uevent(struct device *dev, char **envp, int num_envp,
if (!pdev)
return -ENODEV;
if (add_uevent_var(envp, num_envp, &i,
buffer, buffer_size, &length,
"PCI_CLASS=%04X", pdev->class))
if (add_uevent_var(env, "PCI_CLASS=%04X", pdev->class))
return -ENOMEM;
if (add_uevent_var(envp, num_envp, &i,
buffer, buffer_size, &length,
"PCI_ID=%04X:%04X", pdev->vendor, pdev->device))
if (add_uevent_var(env, "PCI_ID=%04X:%04X", pdev->vendor, pdev->device))
return -ENOMEM;
if (add_uevent_var(envp, num_envp, &i,
buffer, buffer_size, &length,
"PCI_SUBSYS_ID=%04X:%04X", pdev->subsystem_vendor,
if (add_uevent_var(env, "PCI_SUBSYS_ID=%04X:%04X", pdev->subsystem_vendor,
pdev->subsystem_device))
return -ENOMEM;
if (add_uevent_var(envp, num_envp, &i,
buffer, buffer_size, &length,
"PCI_SLOT_NAME=%s", pci_name(pdev)))
if (add_uevent_var(env, "PCI_SLOT_NAME=%s", pci_name(pdev)))
return -ENOMEM;
if (add_uevent_var(envp, num_envp, &i,
buffer, buffer_size, &length,
"MODALIAS=pci:v%08Xd%08Xsv%08Xsd%08Xbc%02Xsc%02Xi%02x",
if (add_uevent_var(env, "MODALIAS=pci:v%08Xd%08Xsv%08Xsd%08Xbc%02Xsc%02Xi%02x",
pdev->vendor, pdev->device,
pdev->subsystem_vendor, pdev->subsystem_device,
(u8)(pdev->class >> 16), (u8)(pdev->class >> 8),
(u8)(pdev->class)))
return -ENOMEM;
envp[i] = NULL;
return 0;
}
......@@ -532,8 +532,7 @@ void pci_dev_put(struct pci_dev *dev)
}
#ifndef CONFIG_HOTPLUG
int pci_uevent(struct device *dev, char **envp, int num_envp,
char *buffer, int buffer_size)
int pci_uevent(struct device *dev, struct kobj_uevent_env *env)
{
return -ENODEV;
}
......
/* Functions internal to the PCI core code */
extern int pci_uevent(struct device *dev, char **envp, int num_envp,
char *buffer, int buffer_size);
extern int pci_uevent(struct device *dev, struct kobj_uevent_env *env);
extern int pci_create_sysfs_dev_files(struct pci_dev *pdev);
extern void pci_remove_sysfs_dev_files(struct pci_dev *pdev);
extern void pci_cleanup_rom(struct pci_dev *dev);
......
......@@ -907,18 +907,14 @@ int pcmcia_insert_card(struct pcmcia_socket *skt)
EXPORT_SYMBOL(pcmcia_insert_card);
static int pcmcia_socket_uevent(struct device *dev, char **envp,
int num_envp, char *buffer, int buffer_size)
static int pcmcia_socket_uevent(struct device *dev,
struct kobj_uevent_env *env)
{
struct pcmcia_socket *s = container_of(dev, struct pcmcia_socket, dev);
int i = 0, length = 0;
if (add_uevent_var(envp, num_envp, &i, buffer, buffer_size,
&length, "SOCKET_NO=%u", s->sock))
if (add_uevent_var(env, "SOCKET_NO=%u", s->sock))
return -ENOMEM;
envp[i] = NULL;
return 0;
}
......
......@@ -1064,11 +1064,10 @@ static int pcmcia_bus_match(struct device * dev, struct device_driver * drv) {
#ifdef CONFIG_HOTPLUG
static int pcmcia_bus_uevent(struct device *dev, char **envp, int num_envp,
char *buffer, int buffer_size)
static int pcmcia_bus_uevent(struct device *dev, struct kobj_uevent_env *env)
{
struct pcmcia_device *p_dev;
int i, length = 0;
int i;
u32 hash[4] = { 0, 0, 0, 0};
if (!dev)
......@@ -1083,23 +1082,13 @@ static int pcmcia_bus_uevent(struct device *dev, char **envp, int num_envp,
hash[i] = crc32(0, p_dev->prod_id[i], strlen(p_dev->prod_id[i]));
}
i = 0;
if (add_uevent_var(envp, num_envp, &i,
buffer, buffer_size, &length,
"SOCKET_NO=%u",
p_dev->socket->sock))
if (add_uevent_var(env, "SOCKET_NO=%u", p_dev->socket->sock))
return -ENOMEM;
if (add_uevent_var(envp, num_envp, &i,
buffer, buffer_size, &length,
"DEVICE_NO=%02X",
p_dev->device_no))
if (add_uevent_var(env, "DEVICE_NO=%02X", p_dev->device_no))
return -ENOMEM;
if (add_uevent_var(envp, num_envp, &i,
buffer, buffer_size, &length,
"MODALIAS=pcmcia:m%04Xc%04Xf%02Xfn%02Xpfn%02X"
if (add_uevent_var(env, "MODALIAS=pcmcia:m%04Xc%04Xf%02Xfn%02Xpfn%02X"
"pa%08Xpb%08Xpc%08Xpd%08X",
p_dev->has_manf_id ? p_dev->manf_id : 0,
p_dev->has_card_id ? p_dev->card_id : 0,
......@@ -1112,15 +1101,12 @@ static int pcmcia_bus_uevent(struct device *dev, char **envp, int num_envp,
hash[3]))
return -ENOMEM;
envp[i] = NULL;
return 0;
}
#else
static int pcmcia_bus_uevent(struct device *dev, char **envp, int num_envp,
char *buffer, int buffer_size)
static int pcmcia_bus_uevent(struct device *dev, struct kobj_uevent_env *env)
{
return -ENODEV;
}
......
......@@ -14,8 +14,7 @@
extern int power_supply_create_attrs(struct power_supply *psy);
extern void power_supply_remove_attrs(struct power_supply *psy);
extern int power_supply_uevent(struct device *dev, char **envp, int num_envp,
char *buffer, int buffer_size);
extern int power_supply_uevent(struct device *dev, struct kobj_uevent_env *env);
#else
......
......@@ -195,11 +195,10 @@ static char *kstruprdup(const char *str, gfp_t gfp)
return ret;
}
int power_supply_uevent(struct device *dev, char **envp, int num_envp,
char *buffer, int buffer_size)
int power_supply_uevent(struct device *dev, struct kobj_uevent_env *env)
{
struct power_supply *psy = dev_get_drvdata(dev);
int i = 0, length = 0, ret = 0, j;
int ret = 0, j;
char *prop_buf;
char *attrname;
......@@ -212,8 +211,7 @@ int power_supply_uevent(struct device *dev, char **envp, int num_envp,
dev_dbg(dev, "POWER_SUPPLY_NAME=%s\n", psy->name);
ret = add_uevent_var(envp, num_envp, &i, buffer, buffer_size,
&length, "POWER_SUPPLY_NAME=%s", psy->name);
ret = add_uevent_var(env, "POWER_SUPPLY_NAME=%s", psy->name);
if (ret)
return ret;
......@@ -243,9 +241,7 @@ int power_supply_uevent(struct device *dev, char **envp, int num_envp,
dev_dbg(dev, "Static prop %s=%s\n", attrname, prop_buf);
ret = add_uevent_var(envp, num_envp, &i, buffer, buffer_size,
&length, "POWER_SUPPLY_%s=%s",
attrname, prop_buf);
ret = add_uevent_var(env, "POWER_SUPPLY_%s=%s", attrname, prop_buf);
kfree(attrname);
if (ret)
goto out;
......@@ -282,14 +278,11 @@ int power_supply_uevent(struct device *dev, char **envp, int num_envp,
dev_dbg(dev, "prop %s=%s\n", attrname, prop_buf);
ret = add_uevent_var(envp, num_envp, &i, buffer, buffer_size,
&length, "POWER_SUPPLY_%s=%s",
attrname, prop_buf);
ret = add_uevent_var(env, "POWER_SUPPLY_%s=%s", attrname, prop_buf);
kfree(attrname);
if (ret)
goto out;
}
envp[i] = NULL;
out:
free_page((unsigned long)prop_buf);
......
......@@ -44,8 +44,7 @@ ccwgroup_bus_match (struct device * dev, struct device_driver * drv)
return 0;
}
static int
ccwgroup_uevent (struct device *dev, char **envp, int num_envp, char *buffer,
int buffer_size)
ccwgroup_uevent (struct device *dev, struct kobj_uevent_env *env)
{
/* TODO */
return 0;
......
......@@ -78,49 +78,38 @@ static int snprint_alias(char *buf, size_t size,
/* Set up environment variables for ccw device uevent. Return 0 on success,
* non-zero otherwise. */
static int ccw_uevent(struct device *dev, char **envp, int num_envp,
char *buffer, int buffer_size)
static int ccw_uevent(struct device *dev, struct kobj_uevent_env *env)
{
struct ccw_device *cdev = to_ccwdev(dev);
struct ccw_device_id *id = &(cdev->id);
int i = 0;
int len = 0;
int ret;
char modalias_buf[30];
/* CU_TYPE= */
ret = add_uevent_var(envp, num_envp, &i, buffer, buffer_size, &len,
"CU_TYPE=%04X", id->cu_type);
ret = add_uevent_var(env, "CU_TYPE=%04X", id->cu_type);
if (ret)
return ret;
/* CU_MODEL= */
ret = add_uevent_var(envp, num_envp, &i, buffer, buffer_size, &len,
"CU_MODEL=%02X", id->cu_model);
ret = add_uevent_var(env, "CU_MODEL=%02X", id->cu_model);
if (ret)
return ret;
/* The next two can be zero, that's ok for us */
/* DEV_TYPE= */
ret = add_uevent_var(envp, num_envp, &i, buffer, buffer_size, &len,
"DEV_TYPE=%04X", id->dev_type);
ret = add_uevent_var(env, "DEV_TYPE=%04X", id->dev_type);
if (ret)
return ret;
/* DEV_MODEL= */
ret = add_uevent_var(envp, num_envp, &i, buffer, buffer_size, &len,
"DEV_MODEL=%02X", id->dev_model);
ret = add_uevent_var(env, "DEV_MODEL=%02X", id->dev_model);
if (ret)
return ret;
/* MODALIAS= */
snprint_alias(modalias_buf, sizeof(modalias_buf), id, "");
ret = add_uevent_var(envp, num_envp, &i, buffer, buffer_size, &len,
"MODALIAS=%s", modalias_buf);
if (ret)
return ret;
envp[i] = NULL;
return 0;
ret = add_uevent_var(env, "MODALIAS=%s", modalias_buf);
return ret;
}
struct bus_type ccw_bus_type;
......
......@@ -458,28 +458,22 @@ static int ap_bus_match(struct device *dev, struct device_driver *drv)
* uevent function for AP devices. It sets up a single environment
* variable DEV_TYPE which contains the hardware device type.
*/
static int ap_uevent (struct device *dev, char **envp, int num_envp,
char *buffer, int buffer_size)
static int ap_uevent (struct device *dev, struct kobj_uevent_env *env)
{
struct ap_device *ap_dev = to_ap_dev(dev);
int retval = 0, length = 0, i = 0;
int retval = 0;
if (!ap_dev)
return -ENODEV;
/* Set up DEV_TYPE environment variable. */
retval = add_uevent_var(envp, num_envp, &i,
buffer, buffer_size, &length,
"DEV_TYPE=%04X", ap_dev->device_type);
retval = add_uevent_var(env, "DEV_TYPE=%04X", ap_dev->device_type);
if (retval)
return retval;
/* Add MODALIAS= */
retval = add_uevent_var(envp, num_envp, &i,
buffer, buffer_size, &length,
"MODALIAS=ap:t%02X", ap_dev->device_type);
retval = add_uevent_var(env, "MODALIAS=ap:t%02X", ap_dev->device_type);
envp[i] = NULL;
return retval;
}
......
......@@ -277,16 +277,11 @@ static int scsi_bus_match(struct device *dev, struct device_driver *gendrv)
return (sdp->inq_periph_qual == SCSI_INQ_PQ_CON)? 1: 0;
}
static int scsi_bus_uevent(struct device *dev, char **envp, int num_envp,
char *buffer, int buffer_size)
static int scsi_bus_uevent(struct device *dev, struct kobj_uevent_env *env)
{
struct scsi_device *sdev = to_scsi_device(dev);
int i = 0;
int length = 0;
add_uevent_var(envp, num_envp, &i, buffer, buffer_size, &length,
"MODALIAS=" SCSI_DEVICE_MODALIAS_FMT, sdev->type);
envp[i] = NULL;
add_uevent_var(env, "MODALIAS=" SCSI_DEVICE_MODALIAS_FMT, sdev->type);
return 0;
}
......
......@@ -67,14 +67,11 @@ static int spi_match_device(struct device *dev, struct device_driver *drv)
return strncmp(spi->modalias, drv->name, BUS_ID_SIZE) == 0;
}
static int spi_uevent(struct device *dev, char **envp, int num_envp,
char *buffer, int buffer_size)
static int spi_uevent(struct device *dev, struct kobj_uevent_env *env)
{
const struct spi_device *spi = to_spi_device(dev);
envp[0] = buffer;
snprintf(buffer, buffer_size, "MODALIAS=%s", spi->modalias);
envp[1] = NULL;
add_uevent_var(env, "MODALIAS=%s", spi->modalias);
return 0;
}
......
......@@ -576,12 +576,9 @@ static int usb_device_match(struct device *dev, struct device_driver *drv)
}
#ifdef CONFIG_HOTPLUG
static int usb_uevent(struct device *dev, char **envp, int num_envp,
char *buffer, int buffer_size)
static int usb_uevent(struct device *dev, struct kobj_uevent_env *env)
{
struct usb_device *usb_dev;
int i = 0;
int length = 0;
if (!dev)
return -ENODEV;
......@@ -610,51 +607,39 @@ static int usb_uevent(struct device *dev, char **envp, int num_envp,
* all the device descriptors we don't tell them about. Or
* act as usermode drivers.
*/
if (add_uevent_var(envp, num_envp, &i,
buffer, buffer_size, &length,
"DEVICE=/proc/bus/usb/%03d/%03d",
if (add_uevent_var(env, "DEVICE=/proc/bus/usb/%03d/%03d",
usb_dev->bus->busnum, usb_dev->devnum))
return -ENOMEM;
#endif
/* per-device configurations are common */
if (add_uevent_var(envp, num_envp, &i,
buffer, buffer_size, &length,
"PRODUCT=%x/%x/%x",
if (add_uevent_var(env, "PRODUCT=%x/%x/%x",
le16_to_cpu(usb_dev->descriptor.idVendor),
le16_to_cpu(usb_dev->descriptor.idProduct),
le16_to_cpu(usb_dev->descriptor.bcdDevice)))
return -ENOMEM;
/* class-based driver binding models */
if (add_uevent_var(envp, num_envp, &i,
buffer, buffer_size, &length,
"TYPE=%d/%d/%d",
if (add_uevent_var(env, "TYPE=%d/%d/%d",
usb_dev->descriptor.bDeviceClass,
usb_dev->descriptor.bDeviceSubClass,
usb_dev->descriptor.bDeviceProtocol))
return -ENOMEM;
if (add_uevent_var(envp, num_envp, &i,
buffer, buffer_size, &length,
"BUSNUM=%03d",
if (add_uevent_var(env, "BUSNUM=%03d",
usb_dev->bus->busnum))
return -ENOMEM;
if (add_uevent_var(envp, num_envp, &i,
buffer, buffer_size, &length,
"DEVNUM=%03d",
if (add_uevent_var(env, "DEVNUM=%03d",
usb_dev->devnum))
return -ENOMEM;
envp[i] = NULL;
return 0;
}
#else
static int usb_uevent(struct device *dev, char **envp,
int num_envp, char *buffer, int buffer_size)
static int usb_uevent(struct device *dev, struct kobj_uevent_env *env)
{
return -ENODEV;
}
......
......@@ -1339,14 +1339,11 @@ void usb_release_interface(struct device *dev)
}
#ifdef CONFIG_HOTPLUG
static int usb_if_uevent(struct device *dev, char **envp, int num_envp,
char *buffer, int buffer_size)
static int usb_if_uevent(struct device *dev, struct kobj_uevent_env *env)
{
struct usb_device *usb_dev;
struct usb_interface *intf;
struct usb_host_interface *alt;
int i = 0;
int length = 0;
if (!dev)
return -ENODEV;
......@@ -1359,39 +1356,30 @@ static int usb_if_uevent(struct device *dev, char **envp, int num_envp,
alt = intf->cur_altsetting;
#ifdef CONFIG_USB_DEVICEFS
if (add_uevent_var(envp, num_envp, &i,
buffer, buffer_size, &length,
"DEVICE=/proc/bus/usb/%03d/%03d",
if (add_uevent_var(env, "DEVICE=/proc/bus/usb/%03d/%03d",
usb_dev->bus->busnum, usb_dev->devnum))
return -ENOMEM;
#endif
if (add_uevent_var(envp, num_envp, &i,
buffer, buffer_size, &length,
"PRODUCT=%x/%x/%x",
if (add_uevent_var(env, "PRODUCT=%x/%x/%x",
le16_to_cpu(usb_dev->descriptor.idVendor),
le16_to_cpu(usb_dev->descriptor.idProduct),
le16_to_cpu(usb_dev->descriptor.bcdDevice)))
return -ENOMEM;
if (add_uevent_var(envp, num_envp, &i,
buffer, buffer_size, &length,
"TYPE=%d/%d/%d",
if (add_uevent_var(env, "TYPE=%d/%d/%d",
usb_dev->descriptor.bDeviceClass,
usb_dev->descriptor.bDeviceSubClass,
usb_dev->descriptor.bDeviceProtocol))
return -ENOMEM;
if (add_uevent_var(envp, num_envp, &i,
buffer, buffer_size, &length,
"INTERFACE=%d/%d/%d",
if (add_uevent_var(env, "INTERFACE=%d/%d/%d",
alt->desc.bInterfaceClass,
alt->desc.bInterfaceSubClass,
alt->desc.bInterfaceProtocol))
return -ENOMEM;
if (add_uevent_var(envp, num_envp, &i,
buffer, buffer_size, &length,
if (add_uevent_var(env,
"MODALIAS=usb:v%04Xp%04Xd%04Xdc%02Xdsc%02Xdp%02Xic%02Xisc%02Xip%02X",
le16_to_cpu(usb_dev->descriptor.idVendor),
le16_to_cpu(usb_dev->descriptor.idProduct),
......@@ -1404,14 +1392,12 @@ static int usb_if_uevent(struct device *dev, char **envp, int num_envp,
alt->desc.bInterfaceProtocol))
return -ENOMEM;
envp[i] = NULL;
return 0;
}
#else
static int usb_if_uevent(struct device *dev, char **envp,
int num_envp, char *buffer, int buffer_size)
static int usb_if_uevent(struct device *dev, struct kobj_uevent_env *env)
{
return -ENODEV;
}
......
......@@ -197,7 +197,7 @@ static struct w1_family w1_default_family = {
.fops = &w1_default_fops,
};
static int w1_uevent(struct device *dev, char **envp, int num_envp, char *buffer, int buffer_size);
static int w1_uevent(struct device *dev, struct kobj_uevent_env *env);
static struct bus_type w1_bus_type = {
.name = "w1",
......@@ -396,13 +396,12 @@ static void w1_destroy_master_attributes(struct w1_master *master)
}
#ifdef CONFIG_HOTPLUG
static int w1_uevent(struct device *dev, char **envp, int num_envp,
char *buffer, int buffer_size)
static int w1_uevent(struct device *dev, struct kobj_uevent_env *env)
{
struct w1_master *md = NULL;
struct w1_slave *sl = NULL;
char *event_owner, *name;
int err, cur_index=0, cur_len=0;
int err;
if (dev->driver == &w1_master_driver) {
md = container_of(dev, struct w1_master, dev);
......@@ -423,23 +422,19 @@ static int w1_uevent(struct device *dev, char **envp, int num_envp,
if (dev->driver != &w1_slave_driver || !sl)
return 0;
err = add_uevent_var(envp, num_envp, &cur_index, buffer, buffer_size,
&cur_len, "W1_FID=%02X", sl->reg_num.family);
err = add_uevent_var(env, "W1_FID=%02X", sl->reg_num.family);
if (err)
return err;
err = add_uevent_var(envp, num_envp, &cur_index, buffer, buffer_size,
&cur_len, "W1_SLAVE_ID=%024LX",
(unsigned long long)sl->reg_num.id);
envp[cur_index] = NULL;
err = add_uevent_var(env, "W1_SLAVE_ID=%024LX",
(unsigned long long)sl->reg_num.id);
if (err)
return err;
return 0;
};
#else
static int w1_uevent(struct device *dev, char **envp, int num_envp,
char *buffer, int buffer_size)
static int w1_uevent(struct device *dev, struct kobj_uevent_env *env)
{
return 0;
}
......
......@@ -20,7 +20,7 @@ struct of_device
extern ssize_t of_device_get_modalias(struct of_device *ofdev,
char *str, ssize_t len);
extern int of_device_uevent(struct device *dev,
char **envp, int num_envp, char *buffer, int buffer_size);
struct kobj_uevent_env *env);
/* This is just here during the transition */
#include <linux/of_device.h>
......
......@@ -66,8 +66,7 @@ struct bus_type {
struct driver_attribute * drv_attrs;
int (*match)(struct device * dev, struct device_driver * drv);
int (*uevent)(struct device *dev, char **envp,
int num_envp, char *buffer, int buffer_size);
int (*uevent)(struct device *dev, struct kobj_uevent_env *env);
int (*probe)(struct device * dev);
int (*remove)(struct device * dev);
void (*shutdown)(struct device * dev);
......@@ -187,10 +186,8 @@ struct class {
struct class_device_attribute * class_dev_attrs;
struct device_attribute * dev_attrs;
int (*uevent)(struct class_device *dev, char **envp,
int num_envp, char *buffer, int buffer_size);
int (*dev_uevent)(struct device *dev, char **envp, int num_envp,
char *buffer, int buffer_size);
int (*uevent)(struct class_device *dev, struct kobj_uevent_env *env);
int (*dev_uevent)(struct device *dev, struct kobj_uevent_env *env);
void (*release)(struct class_device *dev);
void (*class_release)(struct class *class);
......@@ -266,8 +263,7 @@ struct class_device {
struct attribute_group ** groups; /* optional groups */
void (*release)(struct class_device *dev);
int (*uevent)(struct class_device *dev, char **envp,
int num_envp, char *buffer, int buffer_size);
int (*uevent)(struct class_device *dev, struct kobj_uevent_env *env);
char class_id[BUS_ID_SIZE]; /* unique to this class */
};
......@@ -335,8 +331,7 @@ extern void class_device_destroy(struct class *cls, dev_t devt);
struct device_type {
const char *name;
struct attribute_group **groups;
int (*uevent)(struct device *dev, char **envp, int num_envp,
char *buffer, int buffer_size);
int (*uevent)(struct device *dev, struct kobj_uevent_env *env);
void (*release)(struct device *dev);
int (*suspend)(struct device * dev, pm_message_t state);
int (*resume)(struct device * dev);
......
......@@ -29,6 +29,8 @@
#define KOBJ_NAME_LEN 20
#define UEVENT_HELPER_PATH_LEN 256
#define UEVENT_NUM_ENVP 32 /* number of env pointers */
#define UEVENT_BUFFER_SIZE 2048 /* buffer for the variables */
/* path to the userspace helper executed on an event */
extern char uevent_helper[];
......@@ -111,11 +113,18 @@ struct kobj_type {
struct attribute ** default_attrs;
};
struct kobj_uevent_env {
char *envp[UEVENT_NUM_ENVP];
int envp_idx;
char buf[UEVENT_BUFFER_SIZE];
int buflen;
};
struct kset_uevent_ops {
int (*filter)(struct kset *kset, struct kobject *kobj);
const char *(*name)(struct kset *kset, struct kobject *kobj);
int (*uevent)(struct kset *kset, struct kobject *kobj, char **envp,
int num_envp, char *buffer, int buffer_size);
int (*uevent)(struct kset *kset, struct kobject *kobj,
struct kobj_uevent_env *env);
};
/*
......@@ -275,10 +284,8 @@ int kobject_uevent(struct kobject *kobj, enum kobject_action action);
int kobject_uevent_env(struct kobject *kobj, enum kobject_action action,
char *envp[]);
int add_uevent_var(char **envp, int num_envp, int *cur_index,
char *buffer, int buffer_size, int *cur_len,
const char *format, ...)
__attribute__((format (printf, 7, 8)));
int add_uevent_var(struct kobj_uevent_env *env, const char *format, ...)
__attribute__((format (printf, 2, 3)));
#else
static inline int kobject_uevent(struct kobject *kobj, enum kobject_action action)
{ return 0; }
......@@ -287,9 +294,7 @@ static inline int kobject_uevent_env(struct kobject *kobj,
char *envp[])
{ return 0; }
static inline int add_uevent_var(char **envp, int num_envp, int *cur_index,
char *buffer, int buffer_size, int *cur_len,
const char *format, ...)
static inline int add_uevent_var(struct kobj_uevent_env *env, const char *format, ...)
{ return 0; }
#endif
......
......@@ -22,8 +22,6 @@
#include <linux/kobject.h>
#include <net/sock.h>
#define BUFFER_SIZE 2048 /* buffer for the variables */
#define NUM_ENVP 32 /* number of env pointers */
/* the strings here must match the enum in include/linux/kobject.h */
const char *kobject_actions[] = {
......@@ -54,31 +52,21 @@ static struct sock *uevent_sock;
* corresponding error when it fails.
*/
int kobject_uevent_env(struct kobject *kobj, enum kobject_action action,
char *envp_ext[])
char *envp_ext[])
{
char **envp;
char *buffer;
char *scratch;
const char *action_string;
struct kobj_uevent_env *env;
const char *action_string = kobject_actions[action];
const char *devpath = NULL;
const char *subsystem;
struct kobject *top_kobj;
struct kset *kset;
struct kset_uevent_ops *uevent_ops;
u64 seq;
char *seq_buff;
int i = 0;
int retval = 0;
int j;
pr_debug("%s\n", __FUNCTION__);
action_string = kobject_actions[action];
if (!action_string) {
pr_debug("kobject attempted to send uevent without action_string!\n");
return -EINVAL;
}
/* search the kset we belong to */
top_kobj = kobj;
while (!top_kobj->kset && top_kobj->parent) {
......@@ -92,7 +80,7 @@ int kobject_uevent_env(struct kobject *kobj, enum kobject_action action,
kset = top_kobj->kset;
uevent_ops = kset->uevent_ops;
/* skip the event, if the filter returns zero. */
/* skip the event, if the filter returns zero. */
if (uevent_ops && uevent_ops->filter)
if (!uevent_ops->filter(kset, kobj)) {
pr_debug("kobject filter function caused the event to drop!\n");
......@@ -109,18 +97,11 @@ int kobject_uevent_env(struct kobject *kobj, enum kobject_action action,
return 0;
}
/* environment index */
envp = kzalloc(NUM_ENVP * sizeof (char *), GFP_KERNEL);
if (!envp)
/* environment buffer */
env = kzalloc(sizeof(struct kobj_uevent_env), GFP_KERNEL);
if (!env)
return -ENOMEM;
/* environment values */
buffer = kmalloc(BUFFER_SIZE, GFP_KERNEL);
if (!buffer) {
retval = -ENOMEM;
goto exit;
}
/* complete object path */
devpath = kobject_get_path(kobj, GFP_KERNEL);
if (!devpath) {
......@@ -128,29 +109,29 @@ int kobject_uevent_env(struct kobject *kobj, enum kobject_action action,
goto exit;
}
/* event environemnt for helper process only */
envp[i++] = "HOME=/";
envp[i++] = "PATH=/sbin:/bin:/usr/sbin:/usr/bin";
/* default keys */
scratch = buffer;
envp [i++] = scratch;
scratch += sprintf(scratch, "ACTION=%s", action_string) + 1;
envp [i++] = scratch;
scratch += sprintf (scratch, "DEVPATH=%s", devpath) + 1;
envp [i++] = scratch;
scratch += sprintf(scratch, "SUBSYSTEM=%s", subsystem) + 1;
for (j = 0; envp_ext && envp_ext[j]; j++)
envp[i++] = envp_ext[j];
/* just reserve the space, overwrite it after kset call has returned */
envp[i++] = seq_buff = scratch;
scratch += strlen("SEQNUM=18446744073709551616") + 1;
retval = add_uevent_var(env, "ACTION=%s", action_string);
if (retval)
goto exit;
retval = add_uevent_var(env, "DEVPATH=%s", devpath);
if (retval)
goto exit;
retval = add_uevent_var(env, "SUBSYSTEM=%s", subsystem);
if (retval)
goto exit;
/* keys passed in from the caller */
if (envp_ext) {
for (i = 0; envp_ext[i]; i++) {
retval = add_uevent_var(env, envp_ext[i]);
if (retval)
goto exit;
}
}
/* let the kset specific function add its stuff */
if (uevent_ops && uevent_ops->uevent) {
retval = uevent_ops->uevent(kset, kobj,
&envp[i], NUM_ENVP - i, scratch,
BUFFER_SIZE - (scratch - buffer));
retval = uevent_ops->uevent(kset, kobj, env);
if (retval) {
pr_debug ("%s - uevent() returned %d\n",
__FUNCTION__, retval);
......@@ -158,11 +139,13 @@ int kobject_uevent_env(struct kobject *kobj, enum kobject_action action,
}
}
/* we will send an event, request a new sequence number */
/* we will send an event, so request a new sequence number */
spin_lock(&sequence_lock);
seq = ++uevent_seqnum;
spin_unlock(&sequence_lock);
sprintf(seq_buff, "SEQNUM=%llu", (unsigned long long)seq);
retval = add_uevent_var(env, "SEQNUM=%llu", (unsigned long long)seq);
if (retval)
goto exit;
#if defined(CONFIG_NET)
/* send netlink message */
......@@ -172,17 +155,19 @@ int kobject_uevent_env(struct kobject *kobj, enum kobject_action action,
/* allocate message with the maximum possible size */
len = strlen(action_string) + strlen(devpath) + 2;
skb = alloc_skb(len + BUFFER_SIZE, GFP_KERNEL);
skb = alloc_skb(len + env->buflen, GFP_KERNEL);
if (skb) {
char *scratch;
/* add header */
scratch = skb_put(skb, len);
sprintf(scratch, "%s@%s", action_string, devpath);
/* copy keys to our continuous event payload buffer */
for (i = 2; envp[i]; i++) {
len = strlen(envp[i]) + 1;
for (i = 0; i < env->envp_idx; i++) {
len = strlen(env->envp[i]) + 1;
scratch = skb_put(skb, len);
strcpy(scratch, envp[i]);
strcpy(scratch, env->envp[i]);
}
NETLINK_CB(skb).dst_group = 1;
......@@ -198,13 +183,19 @@ int kobject_uevent_env(struct kobject *kobj, enum kobject_action action,
argv [0] = uevent_helper;
argv [1] = (char *)subsystem;
argv [2] = NULL;
call_usermodehelper (argv[0], argv, envp, UMH_WAIT_EXEC);
retval = add_uevent_var(env, "HOME=/");
if (retval)
goto exit;
retval = add_uevent_var(env, "PATH=/sbin:/bin:/usr/sbin:/usr/bin");
if (retval)
goto exit;
call_usermodehelper (argv[0], argv, env->envp, UMH_WAIT_EXEC);
}
exit:
kfree(devpath);
kfree(buffer);
kfree(envp);
kfree(env);
return retval;
}
......@@ -227,52 +218,38 @@ int kobject_uevent(struct kobject *kobj, enum kobject_action action)
EXPORT_SYMBOL_GPL(kobject_uevent);
/**
* add_uevent_var - helper for creating event variables
* @envp: Pointer to table of environment variables, as passed into
* uevent() method.
* @num_envp: Number of environment variable slots available, as
* passed into uevent() method.
* @cur_index: Pointer to current index into @envp. It should be
* initialized to 0 before the first call to add_uevent_var(),
* and will be incremented on success.
* @buffer: Pointer to buffer for environment variables, as passed
* into uevent() method.
* @buffer_size: Length of @buffer, as passed into uevent() method.
* @cur_len: Pointer to current length of space used in @buffer.
* Should be initialized to 0 before the first call to
* add_uevent_var(), and will be incremented on success.
* @format: Format for creating environment variable (of the form
* "XXX=%x") for snprintf().
* add_uevent_var - add key value string to the environment buffer
* @env: environment buffer structure
* @format: printf format for the key=value pair
*
* Returns 0 if environment variable was added successfully or -ENOMEM
* if no space was available.
*/
int add_uevent_var(char **envp, int num_envp, int *cur_index,
char *buffer, int buffer_size, int *cur_len,
const char *format, ...)
int add_uevent_var(struct kobj_uevent_env *env, const char *format, ...)
{
va_list args;
int len;
/*
* We check against num_envp - 1 to make sure there is at
* least one slot left after we return, since kobject_uevent()
* needs to set the last slot to NULL.
*/
if (*cur_index >= num_envp - 1)
if (env->envp_idx >= ARRAY_SIZE(env->envp)) {
printk(KERN_ERR "add_uevent_var: too many keys\n");
WARN_ON(1);
return -ENOMEM;
envp[*cur_index] = buffer + *cur_len;
}
va_start(args, format);
*cur_len += vsnprintf(envp[*cur_index],
max(buffer_size - *cur_len, 0),
format, args) + 1;
len = vsnprintf(&env->buf[env->buflen],
sizeof(env->buf) - env->buflen,
format, args);
va_end(args);
if (*cur_len > buffer_size)
if (len >= (sizeof(env->buf) - env->buflen)) {
printk(KERN_ERR "add_uevent_var: buffer size too small\n");
WARN_ON(1);
return -ENOMEM;
}
(*cur_index)++;
env->envp[env->envp_idx++] = &env->buf[env->buflen];
env->buflen += len + 1;
return 0;
}
EXPORT_SYMBOL_GPL(add_uevent_var);
......
......@@ -105,10 +105,9 @@ static struct class_device_attribute *atm_attrs[] = {
NULL
};
static int atm_uevent(struct class_device *cdev, char **envp, int num_envp, char *buf, int size)
static int atm_uevent(struct class_device *cdev, struct kobj_uevent_env *env)
{
struct atm_dev *adev;
int i = 0, len = 0;
if (!cdev)
return -ENODEV;
......@@ -117,11 +116,9 @@ static int atm_uevent(struct class_device *cdev, char **envp, int num_envp, char
if (!adev)
return -ENODEV;
if (add_uevent_var(envp, num_envp, &i, buf, size, &len,
"NAME=%s%d", adev->type, adev->number))
if (add_uevent_var(env, "NAME=%s%d", adev->type, adev->number))
return -ENOMEM;
envp[i] = NULL;
return 0;
}
......
......@@ -396,28 +396,22 @@ static struct attribute_group wireless_group = {
#endif /* CONFIG_SYSFS */
#ifdef CONFIG_HOTPLUG
static int netdev_uevent(struct device *d, char **envp,
int num_envp, char *buf, int size)
static int netdev_uevent(struct device *d, struct kobj_uevent_env *env)
{
struct net_device *dev = to_net_dev(d);
int retval, len = 0, i = 0;
int retval;
/* pass interface to uevent. */
retval = add_uevent_var(envp, num_envp, &i,
buf, size, &len,
"INTERFACE=%s", dev->name);
retval = add_uevent_var(env, "INTERFACE=%s", dev->name);
if (retval)
goto exit;
/* pass ifindex to uevent.
* ifindex is useful as it won't change (interface name may change)
* and is what RtNetlink uses natively. */
retval = add_uevent_var(envp, num_envp, &i,
buf, size, &len,
"IFINDEX=%d", dev->ifindex);
retval = add_uevent_var(env, "IFINDEX=%d", dev->ifindex);
exit:
envp[i] = NULL;
return retval;
}
#endif
......
......@@ -53,8 +53,7 @@ static void wiphy_dev_release(struct device *dev)
}
#ifdef CONFIG_HOTPLUG
static int wiphy_uevent(struct device *dev, char **envp,
int num_envp, char *buf, int size)
static int wiphy_uevent(struct device *dev, struct kobj_uevent_env *env)
{
/* TODO, we probably need stuff here */
return 0;
......
......@@ -56,13 +56,12 @@ static int soundbus_probe(struct device *dev)
}
static int soundbus_uevent(struct device *dev, char **envp, int num_envp,
char *buffer, int buffer_size)
static int soundbus_uevent(struct device *dev, struct kobj_uevent_env *env)
{
struct soundbus_dev * soundbus_dev;
struct of_device * of;
const char *compat;
int retval = 0, i = 0, length = 0;
int retval = 0;
int cplen, seen = 0;
if (!dev)
......@@ -75,15 +74,11 @@ static int soundbus_uevent(struct device *dev, char **envp, int num_envp,
of = &soundbus_dev->ofdev;
/* stuff we want to pass to /sbin/hotplug */
retval = add_uevent_var(envp, num_envp, &i,
buffer, buffer_size, &length,
"OF_NAME=%s", of->node->name);
retval = add_uevent_var(env, "OF_NAME=%s", of->node->name);
if (retval)
return retval;
retval = add_uevent_var(envp, num_envp, &i,
buffer, buffer_size, &length,
"OF_TYPE=%s", of->node->type);
retval = add_uevent_var(env, "OF_TYPE=%s", of->node->type);
if (retval)
return retval;
......@@ -93,27 +88,19 @@ static int soundbus_uevent(struct device *dev, char **envp, int num_envp,
compat = of_get_property(of->node, "compatible", &cplen);
while (compat && cplen > 0) {
int tmp = length;
retval = add_uevent_var(envp, num_envp, &i,
buffer, buffer_size, &length,
"OF_COMPATIBLE_%d=%s", seen, compat);
int tmp = env->buflen;
retval = add_uevent_var(env, "OF_COMPATIBLE_%d=%s", seen, compat);
if (retval)
return retval;
compat += length - tmp;
cplen -= length - tmp;
compat += env->buflen - tmp;
cplen -= env->buflen - tmp;
seen += 1;
}
retval = add_uevent_var(envp, num_envp, &i,
buffer, buffer_size, &length,
"OF_COMPATIBLE_N=%d", seen);
retval = add_uevent_var(env, "OF_COMPATIBLE_N=%d", seen);
if (retval)
return retval;
retval = add_uevent_var(envp, num_envp, &i,
buffer, buffer_size, &length,
"MODALIAS=%s", soundbus_dev->modalias);
envp[i] = NULL;
retval = add_uevent_var(env, "MODALIAS=%s", soundbus_dev->modalias);
return retval;
}
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册