X-Git-Url: https://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?p=elogind.git;a=blobdiff_plain;f=src%2Flibudev%2Flibudev-device.c;h=c0a061af6c808d6d505fc13f0182fcf41d4145c9;hp=05c5fbce9858ac38b8b669a65adb650fcf505a29;hb=8f0f13f04555d3bc67511d2e334c667cef7e8971;hpb=946f1825751919a176cd0039002a514de0c9c70f diff --git a/src/libudev/libudev-device.c b/src/libudev/libudev-device.c index 05c5fbce9..c0a061af6 100644 --- a/src/libudev/libudev-device.c +++ b/src/libudev/libudev-device.c @@ -36,13 +36,16 @@ #include "libudev.h" #include "libudev-private.h" +static int udev_device_set_devnode(struct udev_device *udev_device, const char *devnode); +static struct udev_list_entry *udev_device_add_property_internal(struct udev_device *udev_device, const char *key, const char *value); + /** * SECTION:libudev-device * @short_description: kernel sys devices * * Representation of kernel sys devices. Devices are uniquely identified * by their syspath, every device has exactly one path in the kernel sys - * filesystem. Devices usually belong to a kernel subsystem, and and have + * filesystem. Devices usually belong to a kernel subsystem, and have * a unique name inside that subsystem. */ @@ -121,7 +124,7 @@ static int udev_device_set_seqnum(struct udev_device *udev_device, unsigned long udev_device->seqnum = seqnum; snprintf(num, sizeof(num), "%llu", seqnum); - udev_device_add_property(udev_device, "SEQNUM", num); + udev_device_add_property_internal(udev_device, "SEQNUM", num); return 0; } @@ -137,8 +140,8 @@ static int udev_device_set_ifindex(struct udev_device *udev_device, int ifindex) char num[32]; udev_device->ifindex = ifindex; - snprintf(num, sizeof(num), "%u", ifindex); - udev_device_add_property(udev_device, "IFINDEX", num); + snprintf(num, sizeof(num), "%d", ifindex); + udev_device_add_property_internal(udev_device, "IFINDEX", num); return 0; } @@ -166,9 +169,9 @@ static int udev_device_set_devnum(struct udev_device *udev_device, dev_t devnum) udev_device->devnum = devnum; snprintf(num, sizeof(num), "%u", major(devnum)); - udev_device_add_property(udev_device, "MAJOR", num); + udev_device_add_property_internal(udev_device, "MAJOR", num); snprintf(num, sizeof(num), "%u", minor(devnum)); - udev_device_add_property(udev_device, "MINOR", num); + udev_device_add_property_internal(udev_device, "MINOR", num); return 0; } @@ -185,7 +188,7 @@ static int udev_device_set_devpath_old(struct udev_device *udev_device, const ch udev_device->devpath_old = strdup(devpath_old); if (udev_device->devpath_old == NULL) return -ENOMEM; - udev_device_add_property(udev_device, "DEVPATH_OLD", udev_device->devpath_old); + udev_device_add_property_internal(udev_device, "DEVPATH_OLD", udev_device->devpath_old); pos = strrchr(udev_device->devpath_old, '/'); if (pos == NULL) @@ -222,7 +225,7 @@ static int udev_device_set_driver(struct udev_device *udev_device, const char *d if (udev_device->driver == NULL) return -ENOMEM; udev_device->driver_set = true; - udev_device_add_property(udev_device, "DRIVER", udev_device->driver); + udev_device_add_property_internal(udev_device, "DRIVER", udev_device->driver); return 0; } @@ -252,18 +255,18 @@ static int udev_device_set_devtype(struct udev_device *udev_device, const char * if (udev_device->devtype == NULL) return -ENOMEM; udev_device->devtype_set = true; - udev_device_add_property(udev_device, "DEVTYPE", udev_device->devtype); + udev_device_add_property_internal(udev_device, "DEVTYPE", udev_device->devtype); return 0; } -int udev_device_set_subsystem(struct udev_device *udev_device, const char *subsystem) +static int udev_device_set_subsystem(struct udev_device *udev_device, const char *subsystem) { free(udev_device->subsystem); udev_device->subsystem = strdup(subsystem); if (udev_device->subsystem == NULL) return -ENOMEM; udev_device->subsystem_set = true; - udev_device_add_property(udev_device, "SUBSYSTEM", udev_device->subsystem); + udev_device_add_property_internal(udev_device, "SUBSYSTEM", udev_device->subsystem); return 0; } @@ -321,7 +324,7 @@ static int udev_device_set_devnode_mode(struct udev_device *udev_device, mode_t udev_device->devnode_mode = mode; snprintf(num, sizeof(num), "%#o", mode); - udev_device_add_property(udev_device, "DEVMODE", num); + udev_device_add_property_internal(udev_device, "DEVMODE", num); return 0; } @@ -338,7 +341,7 @@ static int udev_device_set_devnode_uid(struct udev_device *udev_device, uid_t ui udev_device->devnode_uid = uid; snprintf(num, sizeof(num), "%u", uid); - udev_device_add_property(udev_device, "DEVUID", num); + udev_device_add_property_internal(udev_device, "DEVUID", num); return 0; } @@ -355,11 +358,11 @@ static int udev_device_set_devnode_gid(struct udev_device *udev_device, gid_t gi udev_device->devnode_gid = gid; snprintf(num, sizeof(num), "%u", gid); - udev_device_add_property(udev_device, "DEVGID", num); + udev_device_add_property_internal(udev_device, "DEVGID", num); return 0; } -struct udev_list_entry *udev_device_add_property(struct udev_device *udev_device, const char *key, const char *value) +static struct udev_list_entry *udev_device_add_property_internal(struct udev_device *udev_device, const char *key, const char *value) { udev_device->envp_uptodate = false; if (value == NULL) { @@ -374,6 +377,20 @@ struct udev_list_entry *udev_device_add_property(struct udev_device *udev_device return udev_list_entry_add(&udev_device->properties_list, key, value); } + +int udev_device_add_property(struct udev_device *udev_device, const char *key, const char *value) +{ + struct udev_list_entry *property; + + property = udev_device_add_property_internal(udev_device, key, value); + + /* store in db, skip private keys */ + if (key[0] != '.') + udev_list_entry_set_num(property, true); + + return 0; +} + static struct udev_list_entry *udev_device_add_property_from_string(struct udev_device *udev_device, const char *property) { char name[UTIL_LINE_SIZE]; @@ -387,7 +404,45 @@ static struct udev_list_entry *udev_device_add_property_from_string(struct udev_ val = &val[1]; if (val[0] == '\0') val = NULL; - return udev_device_add_property(udev_device, name, val); + return udev_device_add_property_internal(udev_device, name, val); +} + +static int udev_device_set_syspath(struct udev_device *udev_device, const char *syspath) +{ + const char *pos; + size_t len; + + free(udev_device->syspath); + udev_device->syspath = strdup(syspath); + if (udev_device->syspath == NULL) + return -ENOMEM; + udev_device->devpath = udev_device->syspath + strlen("/sys"); + udev_device_add_property_internal(udev_device, "DEVPATH", udev_device->devpath); + + pos = strrchr(udev_device->syspath, '/'); + if (pos == NULL) + return -EINVAL; + udev_device->sysname = strdup(&pos[1]); + if (udev_device->sysname == NULL) + return -ENOMEM; + + /* some devices have '!' in their name, change that to '/' */ + len = 0; + while (udev_device->sysname[len] != '\0') { + if (udev_device->sysname[len] == '!') + udev_device->sysname[len] = '/'; + len++; + } + + /* trailing number */ + while (len > 0 && isdigit(udev_device->sysname[--len])) + udev_device->sysnum = &udev_device->sysname[len]; + + /* sysname is completely numeric */ + if (len == 0) + udev_device->sysnum = NULL; + + return 0; } /* @@ -399,7 +454,7 @@ static struct udev_list_entry *udev_device_add_property_from_string(struct udev_ * udev_device_set_info_loaded() needs to be set, to avoid trying * to use a device without a DEVPATH set */ -void udev_device_add_property_from_string_parse(struct udev_device *udev_device, const char *property) +static void udev_device_add_property_from_string_parse(struct udev_device *udev_device, const char *property) { if (startswith(property, "DEVPATH=")) { char path[UTIL_PATH_SIZE]; @@ -475,7 +530,7 @@ void udev_device_add_property_from_string_parse(struct udev_device *udev_device, } } -int udev_device_add_property_from_string_parse_finish(struct udev_device *udev_device) +static int udev_device_add_property_from_string_parse_finish(struct udev_device *udev_device) { if (udev_device->maj > 0) udev_device_set_devnum(udev_device, makedev(udev_device->maj, udev_device->min)); @@ -510,32 +565,29 @@ _public_ const char *udev_device_get_property_value(struct udev_device *udev_dev return udev_list_entry_get_value(list_entry); } -int udev_device_read_db(struct udev_device *udev_device, const char *dbfile) +int udev_device_read_db(struct udev_device *udev_device) { char filename[UTIL_PATH_SIZE]; char line[UTIL_LINE_SIZE]; + const char *id; FILE *f; - /* providing a database file will always force-load it */ - if (dbfile == NULL) { - const char *id; - - if (udev_device->db_loaded) - return 0; - udev_device->db_loaded = true; + if (udev_device->db_loaded) + return 0; - id = udev_device_get_id_filename(udev_device); - if (id == NULL) - return -1; - strscpyl(filename, sizeof(filename), "/run/udev/data/", id, NULL); - dbfile = filename; - } + udev_device->db_loaded = true; - f = fopen(dbfile, "re"); - if (f == NULL) { - udev_dbg(udev_device->udev, "no db file to read %s: %m\n", dbfile); + id = udev_device_get_id_filename(udev_device); + if (id == NULL) return -1; - } + + strscpyl(filename, sizeof(filename), "/run/udev/data/", id, NULL); + + f = fopen(filename, "re"); + if (f == NULL) + return log_debug_errno(errno, "no db file to read %s: %m", filename); + + /* devices with a database entry are initialized */ udev_device->is_initialized = true; while (fgets(line, sizeof(line), f)) { @@ -573,7 +625,7 @@ int udev_device_read_db(struct udev_device *udev_device, const char *dbfile) } fclose(f); - udev_dbg(udev_device->udev, "device %p filled with db file data\n", udev_device); + log_debug("device %p filled with db file data", udev_device); return 0; } @@ -591,7 +643,7 @@ int udev_device_read_uevent_file(struct udev_device *udev_device) strscpyl(filename, sizeof(filename), udev_device->syspath, "/uevent", NULL); f = fopen(filename, "re"); if (f == NULL) - return -1; + return -errno; udev_device->uevent_loaded = true; while (fgets(line, sizeof(line), f)) { @@ -635,17 +687,20 @@ void udev_device_set_info_loaded(struct udev_device *device) device->info_loaded = true; } -struct udev_device *udev_device_new(struct udev *udev) +static struct udev_device *udev_device_new(struct udev *udev) { struct udev_device *udev_device; - struct udev_list_entry *list_entry; - if (udev == NULL) + if (udev == NULL) { + errno = EINVAL; return NULL; + } - udev_device = calloc(1, sizeof(struct udev_device)); - if (udev_device == NULL) + udev_device = new0(struct udev_device, 1); + if (udev_device == NULL) { + errno = ENOMEM; return NULL; + } udev_device->refcount = 1; udev_device->udev = udev; udev_list_init(udev, &udev_device->devlinks_list, true); @@ -654,11 +709,7 @@ struct udev_device *udev_device_new(struct udev *udev) udev_list_init(udev, &udev_device->sysattr_list, false); udev_list_init(udev, &udev_device->tags_list, true); udev_device->watch_handle = -1; - /* copy global properties */ - udev_list_entry_foreach(list_entry, udev_get_properties_list_entry(udev)) - udev_device_add_property(udev_device, - udev_list_entry_get_name(list_entry), - udev_list_entry_get_value(list_entry)); + return udev_device; } @@ -684,22 +735,30 @@ _public_ struct udev_device *udev_device_new_from_syspath(struct udev *udev, con struct stat statbuf; struct udev_device *udev_device; - if (udev == NULL) + if (udev == NULL) { + errno = EINVAL; return NULL; - if (syspath == NULL) + } + + if (syspath == NULL) { + errno = EINVAL; return NULL; + } /* path starts in sys */ if (!startswith(syspath, "/sys")) { - udev_dbg(udev, "not in sys :%s\n", syspath); + log_debug("not in sys :%s", syspath); + errno = EINVAL; return NULL; } /* path is not a root directory */ subdir = syspath + strlen("/sys"); pos = strrchr(subdir, '/'); - if (pos == NULL || pos[1] == '\0' || pos < &subdir[2]) + if (pos == NULL || pos[1] == '\0' || pos < &subdir[2]) { + errno = EINVAL; return NULL; + } /* resolve possible symlink to real path */ strscpy(path, sizeof(path), syspath); @@ -714,8 +773,13 @@ _public_ struct udev_device *udev_device_new_from_syspath(struct udev *udev, con return NULL; } else { /* everything else just needs to be a directory */ - if (stat(path, &statbuf) != 0 || !S_ISDIR(statbuf.st_mode)) + if (stat(path, &statbuf) != 0) return NULL; + + if (!S_ISDIR(statbuf.st_mode)) { + errno = EISDIR; + return NULL; + } } udev_device = udev_device_new(udev); @@ -723,7 +787,7 @@ _public_ struct udev_device *udev_device_new_from_syspath(struct udev *udev, con return NULL; udev_device_set_syspath(udev_device, path); - udev_dbg(udev, "device %p has devpath '%s'\n", udev_device, udev_device_get_devpath(udev_device)); + log_debug("device %p has devpath '%s'", udev_device, udev_device_get_devpath(udev_device)); return udev_device; } @@ -753,8 +817,10 @@ _public_ struct udev_device *udev_device_new_from_devnum(struct udev *udev, char type_str = "block"; else if (type == 'c') type_str = "char"; - else + else { + errno = EINVAL; return NULL; + } /* use /sys/dev/{block,char}/: link */ snprintf(path, sizeof(path), "/sys/dev/%s/%u:%u", @@ -780,7 +846,7 @@ _public_ struct udev_device *udev_device_new_from_devnum(struct udev *udev, char * * Returns: a new udev device, or #NULL, if it does not exist **/ -_public_ struct udev_device *udev_device_new_from_device_id(struct udev *udev, char *id) +_public_ struct udev_device *udev_device_new_from_device_id(struct udev *udev, const char *id) { char type; int maj, min; @@ -800,13 +866,15 @@ _public_ struct udev_device *udev_device_new_from_device_id(struct udev *udev, c int ifindex; ifindex = strtoul(&id[1], NULL, 10); - if (ifindex <= 0) + if (ifindex <= 0) { + errno = EINVAL; return NULL; + } sk = socket(PF_INET, SOCK_DGRAM, 0); if (sk < 0) return NULL; - memset(&ifr, 0x00, sizeof(struct ifreq)); + memzero(&ifr, sizeof(struct ifreq)); ifr.ifr_ifindex = ifindex; if (ioctl(sk, SIOCGIFNAME, &ifr) != 0) { close(sk); @@ -819,18 +887,24 @@ _public_ struct udev_device *udev_device_new_from_device_id(struct udev *udev, c return NULL; if (udev_device_get_ifindex(dev) == ifindex) return dev; + + /* this is racy, so we may end up with the wrong device */ udev_device_unref(dev); + errno = ENODEV; return NULL; } case '+': strscpy(subsys, sizeof(subsys), &id[1]); sysname = strchr(subsys, ':'); - if (sysname == NULL) + if (sysname == NULL) { + errno = EINVAL; return NULL; + } sysname[0] = '\0'; sysname = &sysname[1]; return udev_device_new_from_subsystem_sysname(udev, subsys, sysname); default: + errno = EINVAL; return NULL; } } @@ -894,7 +968,9 @@ _public_ struct udev_device *udev_device_new_from_subsystem_sysname(struct udev strscpyl(path, sizeof(path), "/sys/bus/", subsys, "/drivers/", driver, NULL); if (stat(path, &statbuf) == 0) goto found; - } + } else + errno = EINVAL; + goto out; } @@ -943,7 +1019,7 @@ _public_ struct udev_device *udev_device_new_from_environment(struct udev *udev) udev_device_add_property_from_string_parse(udev_device, environ[i]); if (udev_device_add_property_from_string_parse_finish(udev_device) < 0) { - udev_dbg(udev, "missing values, invalid device\n"); + log_debug("missing values, invalid device"); udev_device_unref(udev_device); udev_device = NULL; } @@ -970,6 +1046,8 @@ static struct udev_device *device_new_from_parent(struct udev_device *udev_devic if (udev_device_parent != NULL) return udev_device_parent; } + + errno = ENOENT; return NULL; } @@ -980,9 +1058,8 @@ static struct udev_device *device_new_from_parent(struct udev_device *udev_devic * Find the next parent device, and fill in information from the sys * device and the udev database entry. * - * The returned the device is not referenced. It is attached to the - * child device, and will be cleaned up when the child device - * is cleaned up. + * Returned device is not referenced. It is attached to the child + * device, and will be cleaned up when the child device is cleaned up. * * It is not necessarily just the upper level directory, empty or not * recognized sys directories are ignored. @@ -994,8 +1071,10 @@ static struct udev_device *device_new_from_parent(struct udev_device *udev_devic **/ _public_ struct udev_device *udev_device_get_parent(struct udev_device *udev_device) { - if (udev_device == NULL) + if (udev_device == NULL) { + errno = EINVAL; return NULL; + } if (!udev_device->parent_set) { udev_device->parent_set = true; udev_device->parent_device = device_new_from_parent(udev_device); @@ -1016,9 +1095,8 @@ _public_ struct udev_device *udev_device_get_parent(struct udev_device *udev_dev * If devtype is #NULL, only subsystem is checked, and any devtype will * match. * - * The returned the device is not referenced. It is attached to the - * child device, and will be cleaned up when the child device - * is cleaned up. + * Returned device is not referenced. It is attached to the child + * device, and will be cleaned up when the child device is cleaned up. * * It can be called as many times as needed, without caring about * references. @@ -1029,8 +1107,10 @@ _public_ struct udev_device *udev_device_get_parent_with_subsystem_devtype(struc { struct udev_device *parent; - if (subsystem == NULL) + if (subsystem == NULL) { + errno = EINVAL; return NULL; + } parent = udev_device_get_parent(udev_device); while (parent != NULL) { @@ -1047,6 +1127,10 @@ _public_ struct udev_device *udev_device_get_parent_with_subsystem_devtype(struc } parent = udev_device_get_parent(parent); } + + if (!parent) + errno = ENOENT; + return parent; } @@ -1088,7 +1172,7 @@ _public_ struct udev_device *udev_device_ref(struct udev_device *udev_device) * Drop a reference of a udev device. If the refcount reaches zero, * the resources of the device will be released. * - * Returns: the passed udev device if it has still an active reference, or #NULL otherwise. + * Returns: #NULL **/ _public_ struct udev_device *udev_device_unref(struct udev_device *udev_device) { @@ -1096,7 +1180,7 @@ _public_ struct udev_device *udev_device_unref(struct udev_device *udev_device) return NULL; udev_device->refcount--; if (udev_device->refcount > 0) - return udev_device; + return NULL; if (udev_device->parent_device != NULL) udev_device_unref(udev_device->parent_device); free(udev_device->syspath); @@ -1219,7 +1303,7 @@ _public_ struct udev_list_entry *udev_device_get_devlinks_list_entry(struct udev if (udev_device == NULL) return NULL; if (!udev_device->info_loaded) - udev_device_read_db(udev_device, NULL); + udev_device_read_db(udev_device); return udev_list_get_entry(&udev_device->devlinks_list); } @@ -1247,7 +1331,7 @@ _public_ struct udev_list_entry *udev_device_get_properties_list_entry(struct ud return NULL; if (!udev_device->info_loaded) { udev_device_read_uevent_file(udev_device); - udev_device_read_db(udev_device, NULL); + udev_device_read_db(udev_device); } if (!udev_device->devlinks_uptodate) { char symlinks[UTIL_PATH_SIZE]; @@ -1263,7 +1347,7 @@ _public_ struct udev_list_entry *udev_device_get_properties_list_entry(struct ud l = strpcpyl(&s, sizeof(symlinks), udev_list_entry_get_name(list_entry), NULL); udev_list_entry_foreach(list_entry, udev_list_entry_get_next(list_entry)) l = strpcpyl(&s, l, " ", udev_list_entry_get_name(list_entry), NULL); - udev_device_add_property(udev_device, "DEVLINKS", symlinks); + udev_device_add_property_internal(udev_device, "DEVLINKS", symlinks); } } if (!udev_device->tags_uptodate) { @@ -1278,7 +1362,7 @@ _public_ struct udev_list_entry *udev_device_get_properties_list_entry(struct ud l = strpcpyl(&s, sizeof(tags), ":", NULL); udev_list_entry_foreach(list_entry, udev_device_get_tags_list_entry(udev_device)) l = strpcpyl(&s, l, udev_list_entry_get_name(list_entry), ":", NULL); - udev_device_add_property(udev_device, "TAGS", tags); + udev_device_add_property_internal(udev_device, "TAGS", tags); } } return udev_list_get_entry(&udev_device->properties_list); @@ -1320,7 +1404,7 @@ _public_ unsigned long long int udev_device_get_usec_since_initialized(struct ud if (udev_device == NULL) return 0; if (!udev_device->info_loaded) - udev_device_read_db(udev_device, NULL); + udev_device_read_db(udev_device); if (udev_device->usec_initialized == 0) return 0; now_ts = now(CLOCK_MONOTONIC); @@ -1339,8 +1423,8 @@ void udev_device_set_usec_initialized(struct udev_device *udev_device, usec_t us char num[32]; udev_device->usec_initialized = usec_initialized; - snprintf(num, sizeof(num), "%llu", (unsigned long long)usec_initialized); - udev_device_add_property(udev_device, "USEC_INITIALIZED", num); + snprintf(num, sizeof(num), USEC_FMT, usec_initialized); + udev_device_add_property_internal(udev_device, "USEC_INITIALIZED", num); } /** @@ -1381,8 +1465,6 @@ _public_ const char *udev_device_get_sysattr_value(struct udev_device *udev_devi } if (S_ISLNK(statbuf.st_mode)) { - struct udev_device *dev; - /* * Some core links return only the last element of the target path, * these are just values, the paths should not be exposed. @@ -1398,17 +1480,6 @@ _public_ const char *udev_device_get_sysattr_value(struct udev_device *udev_devi goto out; } - /* resolve custom link to a device and return its syspath */ - if (!streq(sysattr, "device")) { - strscpyl(path, sizeof(path), udev_device->syspath, "/", sysattr, NULL); - dev = udev_device_new_from_syspath(udev_device->udev, path); - if (dev != NULL) { - list_entry = udev_list_entry_add(&udev_device->sysattr_value_list, sysattr, - udev_device_get_syspath(dev)); - val = udev_list_entry_get_value(list_entry); - udev_device_unref(dev); - } - } goto out; } @@ -1468,7 +1539,7 @@ _public_ int udev_device_set_sysattr_value(struct udev_device *udev_device, cons value_len = 0; else value_len = strlen(value); -restart: + strscpyl(path, sizeof(path), udev_device_get_syspath(dev), "/", sysattr, NULL); if (lstat(path, &statbuf) != 0) { udev_list_entry_add(&dev->sysattr_value_list, sysattr, NULL); @@ -1477,24 +1548,7 @@ restart: } if (S_ISLNK(statbuf.st_mode)) { - /* - * Cannot modify core link values - */ - if (streq(sysattr, "driver") || - streq(sysattr, "subsystem") || - streq(sysattr, "module")) { - ret = -EPERM; - } else if (!streq(sysattr, "device")) { - /* resolve custom link to a device */ - strscpyl(path, sizeof(path), udev_device->syspath, "/", sysattr, NULL); - dev = udev_device_new_from_syspath(udev_device->udev, path); - if (dev != NULL) - goto restart; - ret = -ENXIO; - } else { - /* Unhandled, to not try to modify anything */ - ret = -EINVAL; - } + ret = -EINVAL; goto out; } @@ -1549,13 +1603,13 @@ static int udev_device_sysattr_list_read(struct udev_device *udev_device) int num = 0; if (udev_device == NULL) - return -1; + return -EINVAL; if (udev_device->sysattr_list_read) return 0; dir = opendir(udev_device_get_syspath(udev_device)); if (!dir) - return -1; + return -errno; for (dent = readdir(dir); dent != NULL; dent = readdir(dir)) { char path[UTIL_PATH_SIZE]; @@ -1603,45 +1657,7 @@ _public_ struct udev_list_entry *udev_device_get_sysattr_list_entry(struct udev_ return udev_list_get_entry(&udev_device->sysattr_list); } -int udev_device_set_syspath(struct udev_device *udev_device, const char *syspath) -{ - const char *pos; - size_t len; - - free(udev_device->syspath); - udev_device->syspath = strdup(syspath); - if (udev_device->syspath == NULL) - return -ENOMEM; - udev_device->devpath = udev_device->syspath + strlen("/sys"); - udev_device_add_property(udev_device, "DEVPATH", udev_device->devpath); - - pos = strrchr(udev_device->syspath, '/'); - if (pos == NULL) - return -EINVAL; - udev_device->sysname = strdup(&pos[1]); - if (udev_device->sysname == NULL) - return -ENOMEM; - - /* some devices have '!' in their name, change that to '/' */ - len = 0; - while (udev_device->sysname[len] != '\0') { - if (udev_device->sysname[len] == '!') - udev_device->sysname[len] = '/'; - len++; - } - - /* trailing number */ - while (len > 0 && isdigit(udev_device->sysname[--len])) - udev_device->sysnum = &udev_device->sysname[len]; - - /* sysname is completely numeric */ - if (len == 0) - udev_device->sysnum = NULL; - - return 0; -} - -int udev_device_set_devnode(struct udev_device *udev_device, const char *devnode) +static int udev_device_set_devnode(struct udev_device *udev_device, const char *devnode) { free(udev_device->devnode); if (devnode[0] != '/') { @@ -1652,7 +1668,7 @@ int udev_device_set_devnode(struct udev_device *udev_device, const char *devnode } if (udev_device->devnode == NULL) return -ENOMEM; - udev_device_add_property(udev_device, "DEVNAME", udev_device->devnode); + udev_device_add_property_internal(udev_device, "DEVNAME", udev_device->devnode); return 0; } @@ -1682,7 +1698,7 @@ const char *udev_device_get_id_filename(struct udev_device *udev_device) udev_device->id_filename = NULL; } else if (udev_device_get_ifindex(udev_device) > 0) { /* use netdev ifindex -- n3 */ - if (asprintf(&udev_device->id_filename, "n%u", udev_device_get_ifindex(udev_device)) < 0) + if (asprintf(&udev_device->id_filename, "n%i", udev_device_get_ifindex(udev_device)) < 0) udev_device->id_filename = NULL; } else { /* @@ -1717,7 +1733,7 @@ const char *udev_device_get_id_filename(struct udev_device *udev_device) _public_ int udev_device_get_is_initialized(struct udev_device *udev_device) { if (!udev_device->info_loaded) - udev_device_read_db(udev_device, NULL); + udev_device_read_db(udev_device); return udev_device->is_initialized; } @@ -1726,9 +1742,14 @@ void udev_device_set_is_initialized(struct udev_device *udev_device) udev_device->is_initialized = true; } +static bool is_valid_tag(const char *tag) +{ + return !strchr(tag, ':') && !strchr(tag, ' '); +} + int udev_device_add_tag(struct udev_device *udev_device, const char *tag) { - if (strchr(tag, ':') != NULL || strchr(tag, ' ') != NULL) + if (!is_valid_tag(tag)) return -EINVAL; udev_device->tags_uptodate = false; if (udev_list_entry_add(&udev_device->tags_list, tag, NULL) != NULL) @@ -1736,6 +1757,20 @@ int udev_device_add_tag(struct udev_device *udev_device, const char *tag) return -ENOMEM; } +void udev_device_remove_tag(struct udev_device *udev_device, const char *tag) +{ + struct udev_list_entry *e; + + if (!is_valid_tag(tag)) + return; + e = udev_list_get_entry(&udev_device->tags_list); + e = udev_list_entry_get_by_name(e, tag); + if (e) { + udev_device->tags_uptodate = false; + udev_list_entry_delete(e); + } +} + void udev_device_cleanup_tags_list(struct udev_device *udev_device) { udev_device->tags_uptodate = false; @@ -1758,7 +1793,7 @@ _public_ struct udev_list_entry *udev_device_get_tags_list_entry(struct udev_dev if (udev_device == NULL) return NULL; if (!udev_device->info_loaded) - udev_device_read_db(udev_device, NULL); + udev_device_read_db(udev_device); return udev_list_get_entry(&udev_device->tags_list); } @@ -1778,7 +1813,7 @@ _public_ int udev_device_has_tag(struct udev_device *udev_device, const char *ta if (udev_device == NULL) return false; if (!udev_device->info_loaded) - udev_device_read_db(udev_device, NULL); + udev_device_read_db(udev_device); list_entry = udev_device_get_tags_list_entry(udev_device); if (udev_list_entry_get_by_name(list_entry, tag) != NULL) return true; @@ -1860,14 +1895,14 @@ int udev_device_set_action(struct udev_device *udev_device, const char *action) udev_device->action = strdup(action); if (udev_device->action == NULL) return -ENOMEM; - udev_device_add_property(udev_device, "ACTION", udev_device->action); + udev_device_add_property_internal(udev_device, "ACTION", udev_device->action); return 0; } int udev_device_get_devlink_priority(struct udev_device *udev_device) { if (!udev_device->info_loaded) - udev_device_read_db(udev_device, NULL); + udev_device_read_db(udev_device); return udev_device->devlink_priority; } @@ -1880,7 +1915,7 @@ int udev_device_set_devlink_priority(struct udev_device *udev_device, int prio) int udev_device_get_watch_handle(struct udev_device *udev_device) { if (!udev_device->info_loaded) - udev_device_read_db(udev_device, NULL); + udev_device_read_db(udev_device); return udev_device->watch_handle; } @@ -1899,3 +1934,128 @@ void udev_device_set_db_persist(struct udev_device *udev_device) { udev_device->db_persist = true; } + +int udev_device_rename(struct udev_device *udev_device, const char *name) +{ + _cleanup_free_ char *dirname = NULL; + const char *interface; + char *new_syspath; + int r; + + if (udev_device == NULL || name == NULL) + return -EINVAL; + + dirname = dirname_malloc(udev_device->syspath); + if (!dirname) + return -ENOMEM; + + new_syspath = strjoina(dirname, "/", name); + + r = udev_device_set_syspath(udev_device, new_syspath); + if (r < 0) + return r; + + interface = udev_device_get_property_value(udev_device, "INTERFACE"); + if (interface) { + /* like DEVPATH_OLD, INTERFACE_OLD is not saved to the db, but only stays around for the current event */ + udev_device_add_property_internal(udev_device, "INTERFACE_OLD", interface); + udev_device_add_property_internal(udev_device, "INTERFACE", name); + } + + return 0; +} + +struct udev_device *udev_device_shallow_clone(struct udev_device *old_device) +{ + struct udev_device *device; + + if (old_device == NULL) + return NULL; + + device = udev_device_new(old_device->udev); + if (!device) { + errno = ENOMEM; + + return NULL; + } + + udev_device_set_syspath(device, udev_device_get_syspath(old_device)); + udev_device_set_subsystem(device, udev_device_get_subsystem(old_device)); + udev_device_set_devnum(device, udev_device_get_devnum(old_device)); + + return device; +} + +struct udev_device *udev_device_clone_with_db(struct udev_device *old_device) +{ + struct udev_device *device; + + device = udev_device_shallow_clone(old_device); + if (!device) + return NULL; + + udev_device_read_db(device); + udev_device_set_info_loaded(device); + + return device; +} + +struct udev_device *udev_device_new_from_nulstr(struct udev *udev, char *nulstr, ssize_t buflen) { + struct udev_device *device; + ssize_t bufpos = 0; + + if (nulstr == NULL || buflen <= 0) { + errno = EINVAL; + + return NULL; + } + + device = udev_device_new(udev); + if (!device) { + errno = ENOMEM; + + return NULL; + } + + udev_device_set_info_loaded(device); + + while (bufpos < buflen) { + char *key; + size_t keylen; + + key = nulstr + bufpos; + keylen = strlen(key); + if (keylen == 0) + break; + + bufpos += keylen + 1; + udev_device_add_property_from_string_parse(device, key); + } + + if (udev_device_add_property_from_string_parse_finish(device) < 0) { + log_debug("missing values, invalid device"); + + udev_device_unref(device); + + errno = EINVAL; + + return NULL; + } + + return device; +} + +int udev_device_copy_properties(struct udev_device *dst, struct udev_device *src) { + struct udev_list_entry *entry; + + for ((entry = udev_device_get_properties_list_entry(src)); entry; entry = udev_list_entry_get_next(entry)) { + const char *key, *value; + + key = udev_list_entry_get_name(entry); + value = udev_list_entry_get_value(entry); + + udev_device_add_property(dst, key, value); + } + + return 0; +}