diff -Nru a/namedev.c b/namedev.c --- a/namedev.c Fri Dec 5 04:59:53 2003 +++ b/namedev.c Fri Dec 5 04:59:53 2003 @@ -121,6 +121,7 @@ copy_string(dev, new_dev, place); copy_string(dev, new_dev, kernel_name); copy_string(dev, new_dev, exec_program); + copy_string(dev, new_dev, symlink); return 0; } @@ -366,6 +367,7 @@ if (strcmp_pattern(dev->id, udev->callout_value) != 0) continue; strfieldcpy(udev->name, dev->name); + strfieldcpy(udev->symlink, dev->symlink); dbg("callout returned matching value '%s', '%s' becomes '%s'", dev->id, class_dev->name, udev->name); return 0; @@ -416,6 +418,7 @@ continue; strfieldcpy(udev->name, dev->name); + strfieldcpy(udev->symlink, dev->symlink); dbg("found matching attribute '%s', '%s' becomes '%s' ", dev->sysfs_file, class_dev->name, udev->name); @@ -461,6 +464,7 @@ if (!found) continue; strfieldcpy(udev->name, dev->name); + strfieldcpy(udev->symlink, dev->symlink); dbg("found matching id '%s', '%s' becomes '%s'", dev->id, class_dev->name, udev->name); return 0; @@ -506,6 +510,7 @@ continue; strfieldcpy(udev->name, dev->name); + strfieldcpy(udev->symlink, dev->symlink); dbg("found matching place '%s', '%s' becomes '%s'", dev->place, class_dev->name, udev->name); return 0; @@ -528,6 +533,7 @@ continue; strfieldcpy(udev->name, dev->name); + strfieldcpy(udev->symlink, dev->symlink); dbg("found name, '%s' becomes '%s'", dev->kernel_name, udev->name); return 0; @@ -618,8 +624,9 @@ goto done; found: - /* substitute placeholder in NAME */ + /* substitute placeholder */ apply_format(udev, udev->name); + apply_format(udev, udev->symlink); done: perm = find_perm(udev->name); diff -Nru a/namedev.h b/namedev.h --- a/namedev.h Fri Dec 5 04:59:53 2003 +++ b/namedev.h Fri Dec 5 04:59:53 2003 @@ -63,6 +63,7 @@ char kernel_name[NAME_SIZE]; char exec_program[FILE_SIZE]; char name[NAME_SIZE]; + char symlink[NAME_SIZE]; }; struct perm_device { diff -Nru a/namedev_parse.c b/namedev_parse.c --- a/namedev_parse.c Fri Dec 5 04:59:53 2003 +++ b/namedev_parse.c Fri Dec 5 04:59:53 2003 @@ -291,9 +291,11 @@ if (retval) break; strfieldcpy(dev.name, temp3); + dbg_parse("REPLACE name='%s', kernel_name='%s'", dev.name, dev.kernel_name); } + if (strcasecmp(temp2, TYPE_CALLOUT) == 0) { /* number type */ dev.type = CALLOUT; @@ -324,10 +326,17 @@ if (retval) break; strfieldcpy(dev.name, temp3); + dbg_parse("CALLOUT name='%s', program='%s'", dev.name, dev.exec_program); } + /* SYMLINK="name" */ + temp2 = strsep(&temp, ","); + retval = get_value("SYMLINK", &temp, &temp3); + if (retval == 0) + strfieldcpy(dev.symlink, temp3); + retval = add_config_dev(&dev); if (retval) { dbg("add_config_dev returned with error %d", retval); @@ -414,7 +423,7 @@ dev.mode); retval = add_perm_dev(&dev); if (retval) { - dbg("add_config_dev returned with error %d", retval); + dbg("add_perm_dev returned with error %d", retval); goto exit; } } diff -Nru a/test/udev-test.pl b/test/udev-test.pl --- a/test/udev-test.pl Fri Dec 5 04:59:53 2003 +++ b/test/udev-test.pl Fri Dec 5 04:59:53 2003 @@ -39,7 +39,7 @@ expected => "boot_disk" , conf => < "callout bus type", + desc => "symlink creation (node)", + subsys => "tty", + devpath => "class/tty/ttyUSB0", + expected => "ttyUSB0" , + conf => < "symlink creation (link)", + subsys => "tty", + devpath => "class/tty/ttyUSB0", + expected => "tts/USB0" , + conf => < "symlink creation (link)", subsys => "block", - devpath => "block/sda", - expected => "scsi-0:0:0:0" , + devpath => "block/sda/sda2", + expected => "my/own/link2" , conf => <{subsys}, $config->{devpath}, \$config->{conf}); - if (-e "$PWD/$udev_root$config->{expected}") { + if ((-e "$PWD/$udev_root$config->{expected}") || + (-l "$PWD/$udev_root$config->{expected}")) { print "remove: error\n\n"; system("tree $udev_root"); $error++; diff -Nru a/udev-add.c b/udev-add.c --- a/udev-add.c Fri Dec 5 04:59:53 2003 +++ b/udev-add.c Fri Dec 5 04:59:53 2003 @@ -72,6 +72,34 @@ return retval; } +static int create_path(char *file) +{ + char p[NAME_SIZE]; + char *pos; + int retval; + struct stat stats; + + strncpy(p, file, sizeof(p)); + pos = strchr(p+1, '/'); + while (1) { + pos = strchr(pos+1, '/'); + if (pos == NULL) + break; + *pos = 0x00; + if (stat(p, &stats)) { + retval = mkdir(p, 0755); + if (retval) { + dbg("mkdir(%s) failed with error '%s'", + p, strerror(errno)); + return retval; + } + dbg("created '%s'", p); + } + *pos = '/'; + } + return 0; +} + /* * we possibly want to add some symlinks here * only numeric owner/group id's are supported @@ -79,10 +107,12 @@ static int create_node(struct udevice *dev) { char filename[255]; + char linkname[255]; int retval = 0; uid_t uid = 0; gid_t gid = 0; dev_t res; + char *pos; strncpy(filename, udev_root, sizeof(filename)); strncat(filename, dev->name, sizeof(filename)); @@ -109,31 +139,9 @@ return -EINVAL; } - /* create subdirectories if requested */ - if (strchr(dev->name, '/')) { - char path[255]; - char *pos; - struct stat stats; - - strncpy(path, filename, sizeof(path)); - pos = strchr(path+1, '/'); - while (1) { - pos = strchr(pos+1, '/'); - if (pos == NULL) - break; - *pos = 0x00; - if (stat(path, &stats)) { - retval = mkdir(path, 0755); - if (retval) { - dbg("mkdir(%s) failed with error '%s'", - path, strerror(errno)); - return retval; - } - dbg("created '%s'", path); - } - *pos = '/'; - } - } + /* create parent directories if needed */ + if (strrchr(dev->name, '/')) + create_path(filename); dbg("mknod(%s, %#o, %u, %u)", filename, dev->mode, dev->major, dev->minor); retval = mknod(filename, dev->mode, res); @@ -179,8 +187,36 @@ dbg("chown(%s, %u, %u)", filename, uid, gid); retval = chown(filename, uid, gid); if (retval) - dbg("chown(%s, %u, %u) failed with error '%s'", filename, - uid, gid, strerror(errno)); + dbg("chown(%s, %u, %u) failed with error '%s'", + filename, uid, gid, strerror(errno)); + } + + + /* create symlink if requested */ + if (*dev->symlink) { + strncpy(linkname, udev_root, sizeof(linkname)); + strncat(linkname, dev->symlink, sizeof(linkname)); + pos = strchr(dev->symlink, '/'); + if (pos) { + create_path(linkname); + strcpy(filename, "../"); + while (1) { + pos = strchr(pos+1, '/'); + if (pos == NULL) + break; + strcat(filename, "../"); + } + } else { + strcpy(filename, "./"); + } + strncat(filename, dev->name, sizeof(filename)); + + dbg("symlink(%s, %s)", filename, linkname); + + retval = symlink(filename, linkname); + if (retval) + dbg("symlink(%s, %s) failed with error '%s'", + filename, linkname, strerror(errno)); } return retval; diff -Nru a/udev-remove.c b/udev-remove.c --- a/udev-remove.c Fri Dec 5 04:59:53 2003 +++ b/udev-remove.c Fri Dec 5 04:59:53 2003 @@ -34,47 +34,43 @@ #include "udevdb.h" #include "libsysfs/libsysfs.h" - -/* - * Look up the sysfs path in the database to see if we have named this device - * something different from the kernel name. If we have, us it. If not, use - * the default kernel name for lack of anything else to know to do. - */ -static char *get_name(char *path, int major, int minor) +static int delete_path(char *path) { - static char name[100]; - struct udevice *dev; - char *temp; + char *pos; + int retval; - dev = udevdb_get_dev(path); - if (dev != NULL) { - strcpy(name, dev->name); - goto exit; + pos = strrchr(path, '/'); + while (1) { + *pos = '\0'; + pos = strrchr(path, '/'); + + /* don't remove the last one */ + if ((pos == path) || (pos == NULL)) + break; + + /* remove if empty */ + retval = rmdir(path); + if (retval) { + if (errno == ENOTEMPTY) + return 0; + dbg("rmdir(%s) failed with error '%s'", + path, strerror(errno)); + break; + } + dbg("removed '%s'", path); } - - dbg("'%s' not found in database, falling back on default name", path); - temp = strrchr(path, '/'); - if (temp == NULL) - return NULL; - strncpy(name, &temp[1], sizeof(name)); - -exit: - dbg("name is '%s'", name); - return &name[0]; + return 0; } -/* - * We also want to clean up any symlinks that were created in create_node() - */ -static int delete_node(char *name) +static int delete_node(struct udevice *dev) { char filename[255]; int retval; strncpy(filename, udev_root, sizeof(filename)); - strncat(filename, name, sizeof(filename)); + strncat(filename, dev->name, sizeof(filename)); - dbg("unlinking '%s'", filename); + dbg("unlinking node '%s'", filename); retval = unlink(filename); if (retval) { dbg("unlink(%s) failed with error '%s'", @@ -83,49 +79,43 @@ } /* remove subdirectories */ - if (strchr(name, '/')) { - char *pos; + if (strchr(dev->name, '/')) + delete_path(filename); - pos = strrchr(filename, '/'); - while (1) { - *pos = 0x00; - pos = strrchr(filename, '/'); - - /* don't remove the last one */ - if ((pos == filename) || (pos == NULL)) - break; - - /* remove if empty */ - retval = rmdir(filename); - if (retval) { - if (errno == ENOTEMPTY) - return 0; - dbg("rmdir(%s) failed with error '%s'", - filename, strerror(errno)); - break; - } - dbg("removed '%s'", filename); + if (*dev->symlink) { + strncpy(filename, udev_root, sizeof(filename)); + strncat(filename, dev->symlink, sizeof(filename)); + dbg("unlinking symlink '%s'", filename); + retval = unlink(filename); + if (retval) { + dbg("unlink(%s) failed with error '%s'", + filename, strerror(errno)); + return retval; + } + if (strchr(dev->symlink, '/')) { + delete_path(filename); } } + return retval; } -int udev_remove_device(char *device, char *subsystem) +int udev_remove_device(char *path, char *subsystem) { - char *name; - int retval = 0; + char name[100]; + struct udevice *dev; + char *temp; - name = get_name(device, 0, 0); - if (name == NULL) { - dbg ("get_name failed"); - retval = -ENODEV; - goto exit; + dev = udevdb_get_dev(path); + if (dev == NULL) { + dbg("'%s' not found in database, falling back on default name", path); + temp = strrchr(path, '/'); + if (temp == NULL) + return -ENODEV; + strncpy(name, &temp[1], sizeof(name)); } - udevdb_delete_dev(device); - - return delete_node(name); - -exit: - return retval; + dbg("name is '%s'", dev->name); + udevdb_delete_dev(path); + return delete_node(dev); } diff -Nru a/udev.h b/udev.h --- a/udev.h Fri Dec 5 04:59:53 2003 +++ b/udev.h Fri Dec 5 04:59:53 2003 @@ -64,6 +64,7 @@ int major; int minor; mode_t mode; + char symlink[NAME_SIZE]; /* fields that help us in building strings */ unsigned char bus_id[SYSFS_NAME_LEN];