X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?p=elogind.git;a=blobdiff_plain;f=src%2Flibudev%2Flibudev-device.c;h=a7647b9d9cc4920b7eb7223d7b8b4f794051c306;hp=257d6355fed0d5ce54967822a8435c5845196f79;hb=df546eb56a3e8cb7cbea7a1cb630f9ed21ea5c6b;hpb=bb061708d5aa83579f213bdfb67253f7027217c3 diff --git a/src/libudev/libudev-device.c b/src/libudev/libudev-device.c index 257d6355f..a7647b9d9 100644 --- a/src/libudev/libudev-device.c +++ b/src/libudev/libudev-device.c @@ -1,13 +1,21 @@ -/* - * libudev - interface to udev device information - * - * Copyright (C) 2008-2010 Kay Sievers - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - */ +/*** + This file is part of systemd. + + Copyright 2008-2012 Kay Sievers + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ #include #include @@ -28,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. */ @@ -52,6 +63,8 @@ struct udev_device { const char *sysnum; char *devnode; mode_t devnode_mode; + uid_t devnode_uid; + gid_t devnode_gid; char *subsystem; char *devtype; char *driver; @@ -67,7 +80,7 @@ struct udev_device { struct udev_list sysattr_list; struct udev_list tags_list; unsigned long long int seqnum; - unsigned long long int usec_initialized; + usec_t usec_initialized; int devlink_priority; int refcount; dev_t devnum; @@ -111,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; } @@ -127,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; } @@ -136,7 +149,9 @@ static int udev_device_set_ifindex(struct udev_device *udev_device, int ifindex) * udev_device_get_devnum: * @udev_device: udev device * - * Returns: the device major/minor number. + * Get the device major/minor number. + * + * Returns: the dev_t number. **/ _public_ dev_t udev_device_get_devnum(struct udev_device *udev_device) { @@ -154,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; } @@ -173,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) @@ -185,7 +200,9 @@ static int udev_device_set_devpath_old(struct udev_device *udev_device, const ch * udev_device_get_driver: * @udev_device: udev device * - * Returns: the driver string, or #NULL if there is no driver attached. + * Get the kernel driver name. + * + * Returns: the driver name string, or #NULL if there is no driver attached. **/ _public_ const char *udev_device_get_driver(struct udev_device *udev_device) { @@ -208,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; } @@ -238,7 +255,7 @@ 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; } @@ -249,7 +266,7 @@ static int udev_device_set_subsystem(struct udev_device *udev_device, const char 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; } @@ -276,7 +293,7 @@ _public_ const char *udev_device_get_subsystem(struct udev_device *udev_device) return udev_device->subsystem; } /* implicit names */ - if (strncmp(udev_device->devpath, "/module/", 8) == 0) { + if (startswith(udev_device->devpath, "/module/")) { udev_device_set_subsystem(udev_device, "module"); return udev_device->subsystem; } @@ -284,9 +301,9 @@ _public_ const char *udev_device_get_subsystem(struct udev_device *udev_device) udev_device_set_subsystem(udev_device, "drivers"); return udev_device->subsystem; } - if (strncmp(udev_device->devpath, "/subsystem/", 11) == 0 || - strncmp(udev_device->devpath, "/class/", 7) == 0 || - strncmp(udev_device->devpath, "/bus/", 5) == 0) { + if (startswith(udev_device->devpath, "/subsystem/") || + startswith(udev_device->devpath, "/class/") || + startswith(udev_device->devpath, "/bus/")) { udev_device_set_subsystem(udev_device, "subsystem"); return udev_device->subsystem; } @@ -307,11 +324,45 @@ 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; } -struct udev_list_entry *udev_device_add_property(struct udev_device *udev_device, const char *key, const char *value) +uid_t udev_device_get_devnode_uid(struct udev_device *udev_device) +{ + if (!udev_device->info_loaded) + udev_device_read_uevent_file(udev_device); + return udev_device->devnode_uid; +} + +static int udev_device_set_devnode_uid(struct udev_device *udev_device, uid_t uid) +{ + char num[32]; + + udev_device->devnode_uid = uid; + snprintf(num, sizeof(num), "%u", uid); + udev_device_add_property_internal(udev_device, "DEVUID", num); + return 0; +} + +gid_t udev_device_get_devnode_gid(struct udev_device *udev_device) +{ + if (!udev_device->info_loaded) + udev_device_read_uevent_file(udev_device); + return udev_device->devnode_gid; +} + +static int udev_device_set_devnode_gid(struct udev_device *udev_device, gid_t gid) +{ + char num[32]; + + udev_device->devnode_gid = gid; + snprintf(num, sizeof(num), "%u", gid); + udev_device_add_property_internal(udev_device, "DEVGID", num); + return 0; +} + +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) { @@ -326,12 +377,26 @@ 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]; char *val; - util_strscpy(name, sizeof(name), property); + strscpy(name, sizeof(name), property); val = strchr(name, '='); if (val == NULL) return NULL; @@ -339,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; } /* @@ -351,40 +454,40 @@ 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 (strncmp(property, "DEVPATH=", 8) == 0) { + if (startswith(property, "DEVPATH=")) { char path[UTIL_PATH_SIZE]; - util_strscpyl(path, sizeof(path), udev_get_sys_path(udev_device->udev), &property[8], NULL); + strscpyl(path, sizeof(path), "/sys", &property[8], NULL); udev_device_set_syspath(udev_device, path); - } else if (strncmp(property, "SUBSYSTEM=", 10) == 0) { + } else if (startswith(property, "SUBSYSTEM=")) { udev_device_set_subsystem(udev_device, &property[10]); - } else if (strncmp(property, "DEVTYPE=", 8) == 0) { + } else if (startswith(property, "DEVTYPE=")) { udev_device_set_devtype(udev_device, &property[8]); - } else if (strncmp(property, "DEVNAME=", 8) == 0) { + } else if (startswith(property, "DEVNAME=")) { udev_device_set_devnode(udev_device, &property[8]); - } else if (strncmp(property, "DEVLINKS=", 9) == 0) { + } else if (startswith(property, "DEVLINKS=")) { char devlinks[UTIL_PATH_SIZE]; char *slink; char *next; - util_strscpy(devlinks, sizeof(devlinks), &property[9]); + strscpy(devlinks, sizeof(devlinks), &property[9]); slink = devlinks; next = strchr(slink, ' '); while (next != NULL) { next[0] = '\0'; - udev_device_add_devlink(udev_device, slink, 0); + udev_device_add_devlink(udev_device, slink); slink = &next[1]; next = strchr(slink, ' '); } if (slink[0] != '\0') - udev_device_add_devlink(udev_device, slink, 0); - } else if (strncmp(property, "TAGS=", 5) == 0) { + udev_device_add_devlink(udev_device, slink); + } else if (startswith(property, "TAGS=")) { char tags[UTIL_PATH_SIZE]; char *next; - util_strscpy(tags, sizeof(tags), &property[5]); + strscpy(tags, sizeof(tags), &property[5]); next = strchr(tags, ':'); if (next != NULL) { next++; @@ -400,30 +503,34 @@ void udev_device_add_property_from_string_parse(struct udev_device *udev_device, udev_device_add_tag(udev_device, tag); } } - } else if (strncmp(property, "USEC_INITIALIZED=", 19) == 0) { + } else if (startswith(property, "USEC_INITIALIZED=")) { udev_device_set_usec_initialized(udev_device, strtoull(&property[19], NULL, 10)); - } else if (strncmp(property, "DRIVER=", 7) == 0) { + } else if (startswith(property, "DRIVER=")) { udev_device_set_driver(udev_device, &property[7]); - } else if (strncmp(property, "ACTION=", 7) == 0) { + } else if (startswith(property, "ACTION=")) { udev_device_set_action(udev_device, &property[7]); - } else if (strncmp(property, "MAJOR=", 6) == 0) { + } else if (startswith(property, "MAJOR=")) { udev_device->maj = strtoull(&property[6], NULL, 10); - } else if (strncmp(property, "MINOR=", 6) == 0) { + } else if (startswith(property, "MINOR=")) { udev_device->min = strtoull(&property[6], NULL, 10); - } else if (strncmp(property, "DEVPATH_OLD=", 12) == 0) { + } else if (startswith(property, "DEVPATH_OLD=")) { udev_device_set_devpath_old(udev_device, &property[12]); - } else if (strncmp(property, "SEQNUM=", 7) == 0) { + } else if (startswith(property, "SEQNUM=")) { udev_device_set_seqnum(udev_device, strtoull(&property[7], NULL, 10)); - } else if (strncmp(property, "IFINDEX=", 8) == 0) { + } else if (startswith(property, "IFINDEX=")) { udev_device_set_ifindex(udev_device, strtoull(&property[8], NULL, 10)); - } else if (strncmp(property, "DEVMODE=", 8) == 0) { + } else if (startswith(property, "DEVMODE=")) { udev_device_set_devnode_mode(udev_device, strtoul(&property[8], NULL, 8)); + } else if (startswith(property, "DEVUID=")) { + udev_device_set_devnode_uid(udev_device, strtoul(&property[7], NULL, 10)); + } else if (startswith(property, "DEVGID=")) { + udev_device_set_devnode_gid(udev_device, strtoul(&property[7], NULL, 10)); } else { udev_device_add_property_from_string(udev_device, property); } } -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)); @@ -440,7 +547,9 @@ int udev_device_add_property_from_string_parse_finish(struct udev_device *udev_d * @udev_device: udev device * @key: property name * - * Returns: the value of a device property, or #NULL if there is no such property. + * Get the value of a given property. + * + * Returns: the property string, or #NULL if there is no such property. **/ _public_ const char *udev_device_get_property_value(struct udev_device *udev_device, const char *key) { @@ -473,15 +582,15 @@ int udev_device_read_db(struct udev_device *udev_device, const char *dbfile) id = udev_device_get_id_filename(udev_device); if (id == NULL) return -1; - util_strscpyl(filename, sizeof(filename), udev_get_run_path(udev_device->udev), "/data/", id, NULL); + strscpyl(filename, sizeof(filename), "/run/udev/data/", id, NULL); dbfile = filename; } f = fopen(dbfile, "re"); - if (f == NULL) { - dbg(udev_device->udev, "no db file to read %s: %m\n", dbfile); - return -1; - } + if (f == NULL) + return log_debug_errno(errno, "no db file to read %s: %m", dbfile); + + /* devices with a database entry are initialized */ udev_device->is_initialized = true; while (fgets(line, sizeof(line), f)) { @@ -496,8 +605,8 @@ int udev_device_read_db(struct udev_device *udev_device, const char *dbfile) val = &line[2]; switch(line[0]) { case 'S': - util_strscpyl(filename, sizeof(filename), udev_get_dev_path(udev_device->udev), "/", val, NULL); - udev_device_add_devlink(udev_device, filename, 0); + strscpyl(filename, sizeof(filename), "/dev/", val, NULL); + udev_device_add_devlink(udev_device, filename); break; case 'L': udev_device_set_devlink_priority(udev_device, atoi(val)); @@ -519,7 +628,7 @@ int udev_device_read_db(struct udev_device *udev_device, const char *dbfile) } fclose(f); - 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; } @@ -534,10 +643,10 @@ int udev_device_read_uevent_file(struct udev_device *udev_device) if (udev_device->uevent_loaded) return 0; - util_strscpyl(filename, sizeof(filename), udev_device->syspath, "/uevent", NULL); + 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)) { @@ -548,24 +657,24 @@ int udev_device_read_uevent_file(struct udev_device *udev_device) continue; pos[0] = '\0'; - if (strncmp(line, "DEVTYPE=", 8) == 0) { + if (startswith(line, "DEVTYPE=")) { udev_device_set_devtype(udev_device, &line[8]); continue; } - if (strncmp(line, "IFINDEX=", 8) == 0) { + if (startswith(line, "IFINDEX=")) { udev_device_set_ifindex(udev_device, strtoull(&line[8], NULL, 10)); continue; } - if (strncmp(line, "DEVNAME=", 8) == 0) { + if (startswith(line, "DEVNAME=")) { udev_device_set_devnode(udev_device, &line[8]); continue; } - if (strncmp(line, "MAJOR=", 6) == 0) + if (startswith(line, "MAJOR=")) maj = strtoull(&line[6], NULL, 10); - else if (strncmp(line, "MINOR=", 6) == 0) + else if (startswith(line, "MINOR=")) min = strtoull(&line[6], NULL, 10); - else if (strncmp(line, "DEVMODE=", 8) == 0) + else if (startswith(line, "DEVMODE=")) udev_device->devnode_mode = strtoul(&line[8], NULL, 8); udev_device_add_property_from_string(udev_device, line); @@ -581,17 +690,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); @@ -600,11 +712,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; } @@ -624,46 +732,57 @@ struct udev_device *udev_device_new(struct udev *udev) **/ _public_ struct udev_device *udev_device_new_from_syspath(struct udev *udev, const char *syspath) { - size_t len; const char *subdir; char path[UTIL_PATH_SIZE]; char *pos; 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 */ - len = strlen(udev_get_sys_path(udev)); - if (strncmp(syspath, udev_get_sys_path(udev), len) != 0) { - dbg(udev, "not in sys :%s\n", syspath); + if (!startswith(syspath, "/sys")) { + log_debug("not in sys :%s", syspath); + errno = EINVAL; return NULL; } /* path is not a root directory */ - subdir = &syspath[len+1]; + 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 */ - util_strscpy(path, sizeof(path), syspath); + strscpy(path, sizeof(path), syspath); util_resolve_sys_link(udev, path, sizeof(path)); - if (strncmp(&path[len], "/devices/", 9) == 0) { + if (startswith(path + strlen("/sys"), "/devices/")) { char file[UTIL_PATH_SIZE]; /* all "devices" require a "uevent" file */ - util_strscpyl(file, sizeof(file), path, "/uevent", NULL); + strscpyl(file, sizeof(file), path, "/uevent", NULL); if (stat(file, &statbuf) != 0) 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); @@ -671,7 +790,7 @@ _public_ struct udev_device *udev_device_new_from_syspath(struct udev *udev, con return NULL; udev_device_set_syspath(udev_device, path); - 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; } @@ -701,16 +820,36 @@ _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), "%s/dev/%s/%u:%u", - udev_get_sys_path(udev), type_str, major(devnum), minor(devnum)); + snprintf(path, sizeof(path), "/sys/dev/%s/%u:%u", + type_str, major(devnum), minor(devnum)); return udev_device_new_from_syspath(udev, path); } -struct udev_device *udev_device_new_from_id_filename(struct udev *udev, char *id) +/** + * udev_device_new_from_device_id: + * @udev: udev library context + * @id: text string identifying a kernel device + * + * Create new udev device, and fill in information from the sys + * device and the udev database entry. The device is looked-up + * by a special string: + * b8:2 - block device major:minor + * c128:1 - char device major:minor + * n3 - network device ifindex + * +sound:card29 - kernel driver core subsystem:device name + * + * The initial refcount is 1, and needs to be decremented to + * release the resources of the udev device. + * + * 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, const char *id) { char type; int maj, min; @@ -730,13 +869,15 @@ struct udev_device *udev_device_new_from_id_filename(struct udev *udev, char *id 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); @@ -749,18 +890,24 @@ struct udev_device *udev_device_new_from_id_filename(struct udev *udev, char *id 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 '+': - util_strscpy(subsys, sizeof(subsys), &id[1]); + 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; } } @@ -782,72 +929,69 @@ struct udev_device *udev_device_new_from_id_filename(struct udev *udev, char *id **/ _public_ struct udev_device *udev_device_new_from_subsystem_sysname(struct udev *udev, const char *subsystem, const char *sysname) { - char path_full[UTIL_PATH_SIZE]; - char *path; - size_t l; + char path[UTIL_PATH_SIZE]; struct stat statbuf; - path = path_full; - l = util_strpcpyl(&path, sizeof(path_full), udev_get_sys_path(udev), NULL); - - if (strcmp(subsystem, "subsystem") == 0) { - util_strscpyl(path, l, "/subsystem/", sysname, NULL); - if (stat(path_full, &statbuf) == 0) + if (streq(subsystem, "subsystem")) { + strscpyl(path, sizeof(path), "/sys/subsystem/", sysname, NULL); + if (stat(path, &statbuf) == 0) goto found; - util_strscpyl(path, l, "/bus/", sysname, NULL); - if (stat(path_full, &statbuf) == 0) + strscpyl(path, sizeof(path), "/sys/bus/", sysname, NULL); + if (stat(path, &statbuf) == 0) goto found; - util_strscpyl(path, l, "/class/", sysname, NULL); - if (stat(path_full, &statbuf) == 0) + strscpyl(path, sizeof(path), "/sys/class/", sysname, NULL); + if (stat(path, &statbuf) == 0) goto found; goto out; } - if (strcmp(subsystem, "module") == 0) { - util_strscpyl(path, l, "/module/", sysname, NULL); - if (stat(path_full, &statbuf) == 0) + if (streq(subsystem, "module")) { + strscpyl(path, sizeof(path), "/sys/module/", sysname, NULL); + if (stat(path, &statbuf) == 0) goto found; goto out; } - if (strcmp(subsystem, "drivers") == 0) { + if (streq(subsystem, "drivers")) { char subsys[UTIL_NAME_SIZE]; char *driver; - util_strscpy(subsys, sizeof(subsys), sysname); + strscpy(subsys, sizeof(subsys), sysname); driver = strchr(subsys, ':'); if (driver != NULL) { driver[0] = '\0'; driver = &driver[1]; - util_strscpyl(path, l, "/subsystem/", subsys, "/drivers/", driver, NULL); - if (stat(path_full, &statbuf) == 0) + strscpyl(path, sizeof(path), "/sys/subsystem/", subsys, "/drivers/", driver, NULL); + if (stat(path, &statbuf) == 0) goto found; - util_strscpyl(path, l, "/bus/", subsys, "/drivers/", driver, NULL); - if (stat(path_full, &statbuf) == 0) + strscpyl(path, sizeof(path), "/sys/bus/", subsys, "/drivers/", driver, NULL); + if (stat(path, &statbuf) == 0) goto found; - } + } else + errno = EINVAL; + goto out; } - util_strscpyl(path, l, "/subsystem/", subsystem, "/devices/", sysname, NULL); - if (stat(path_full, &statbuf) == 0) + strscpyl(path, sizeof(path), "/sys/subsystem/", subsystem, "/devices/", sysname, NULL); + if (stat(path, &statbuf) == 0) goto found; - util_strscpyl(path, l, "/bus/", subsystem, "/devices/", sysname, NULL); - if (stat(path_full, &statbuf) == 0) + strscpyl(path, sizeof(path), "/sys/bus/", subsystem, "/devices/", sysname, NULL); + if (stat(path, &statbuf) == 0) goto found; - util_strscpyl(path, l, "/class/", subsystem, "/", sysname, NULL); - if (stat(path_full, &statbuf) == 0) + strscpyl(path, sizeof(path), "/sys/class/", subsystem, "/", sysname, NULL); + if (stat(path, &statbuf) == 0) goto found; out: return NULL; found: - return udev_device_new_from_syspath(udev, path_full); + return udev_device_new_from_syspath(udev, path); } /** @@ -878,7 +1022,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) { - dbg(udev, "missing values, invalid device\n"); + log_debug("missing values, invalid device"); udev_device_unref(udev_device); udev_device = NULL; } @@ -892,8 +1036,8 @@ static struct udev_device *device_new_from_parent(struct udev_device *udev_devic char path[UTIL_PATH_SIZE]; const char *subdir; - util_strscpy(path, sizeof(path), udev_device->syspath); - subdir = &path[strlen(udev_get_sys_path(udev_device->udev))+1]; + strscpy(path, sizeof(path), udev_device->syspath); + subdir = path + strlen("/sys/"); for (;;) { char *pos; @@ -905,6 +1049,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; } @@ -915,9 +1061,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. @@ -929,8 +1074,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); @@ -951,9 +1098,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. @@ -964,8 +1110,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) { @@ -973,15 +1121,19 @@ _public_ struct udev_device *udev_device_get_parent_with_subsystem_devtype(struc const char *parent_devtype; parent_subsystem = udev_device_get_subsystem(parent); - if (parent_subsystem != NULL && strcmp(parent_subsystem, subsystem) == 0) { + if (parent_subsystem != NULL && streq(parent_subsystem, subsystem)) { if (devtype == NULL) break; parent_devtype = udev_device_get_devtype(parent); - if (parent_devtype != NULL && strcmp(parent_devtype, devtype) == 0) + if (parent_devtype != NULL && streq(parent_devtype, devtype)) break; } parent = udev_device_get_parent(parent); } + + if (!parent) + errno = ENOENT; + return parent; } @@ -1023,14 +1175,15 @@ _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: #NULL **/ -_public_ void udev_device_unref(struct udev_device *udev_device) +_public_ struct udev_device *udev_device_unref(struct udev_device *udev_device) { if (udev_device == NULL) - return; + return NULL; udev_device->refcount--; if (udev_device->refcount > 0) - return; + return NULL; if (udev_device->parent_device != NULL) udev_device_unref(udev_device->parent_device); free(udev_device->syspath); @@ -1050,6 +1203,7 @@ _public_ void udev_device_unref(struct udev_device *udev_device) free(udev_device->envp); free(udev_device->monitor_buf); free(udev_device); + return NULL; } /** @@ -1088,7 +1242,9 @@ _public_ const char *udev_device_get_syspath(struct udev_device *udev_device) * udev_device_get_sysname: * @udev_device: udev device * - * Returns: the sys name of the device device + * Get the kernel device name in /sys. + * + * Returns: the name string of the device device **/ _public_ const char *udev_device_get_sysname(struct udev_device *udev_device) { @@ -1101,7 +1257,9 @@ _public_ const char *udev_device_get_sysname(struct udev_device *udev_device) * udev_device_get_sysnum: * @udev_device: udev device * - * Returns: the trailing number of of the device name + * Get the instance number of the device. + * + * Returns: the trailing number string of the device name **/ _public_ const char *udev_device_get_sysnum(struct udev_device *udev_device) { @@ -1189,10 +1347,10 @@ _public_ struct udev_list_entry *udev_device_get_properties_list_entry(struct ud size_t l; s = symlinks; - l = util_strpcpyl(&s, sizeof(symlinks), udev_list_entry_get_name(list_entry), NULL); + 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 = util_strpcpyl(&s, l, " ", udev_list_entry_get_name(list_entry), NULL); - udev_device_add_property(udev_device, "DEVLINKS", symlinks); + l = strpcpyl(&s, l, " ", udev_list_entry_get_name(list_entry), NULL); + udev_device_add_property_internal(udev_device, "DEVLINKS", symlinks); } } if (!udev_device->tags_uptodate) { @@ -1204,10 +1362,10 @@ _public_ struct udev_list_entry *udev_device_get_properties_list_entry(struct ud size_t l; s = tags; - l = util_strpcpyl(&s, sizeof(tags), ":", NULL); + l = strpcpyl(&s, sizeof(tags), ":", NULL); udev_list_entry_foreach(list_entry, udev_device_get_tags_list_entry(udev_device)) - l = util_strpcpyl(&s, l, udev_list_entry_get_name(list_entry), ":", NULL); - udev_device_add_property(udev_device, "TAGS", tags); + l = strpcpyl(&s, l, udev_list_entry_get_name(list_entry), ":", NULL); + udev_device_add_property_internal(udev_device, "TAGS", tags); } } return udev_list_get_entry(&udev_device->properties_list); @@ -1244,7 +1402,7 @@ _public_ const char *udev_device_get_action(struct udev_device *udev_device) **/ _public_ unsigned long long int udev_device_get_usec_since_initialized(struct udev_device *udev_device) { - unsigned long long now; + usec_t now_ts; if (udev_device == NULL) return 0; @@ -1252,24 +1410,24 @@ _public_ unsigned long long int udev_device_get_usec_since_initialized(struct ud udev_device_read_db(udev_device, NULL); if (udev_device->usec_initialized == 0) return 0; - now = now_usec(); - if (now == 0) + now_ts = now(CLOCK_MONOTONIC); + if (now_ts == 0) return 0; - return now - udev_device->usec_initialized; + return now_ts - udev_device->usec_initialized; } -unsigned long long udev_device_get_usec_initialized(struct udev_device *udev_device) +usec_t udev_device_get_usec_initialized(struct udev_device *udev_device) { return udev_device->usec_initialized; } -void udev_device_set_usec_initialized(struct udev_device *udev_device, unsigned long long usec_initialized) +void udev_device_set_usec_initialized(struct udev_device *udev_device, usec_t usec_initialized) { char num[32]; udev_device->usec_initialized = usec_initialized; - snprintf(num, sizeof(num), "%llu", 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); } /** @@ -1303,22 +1461,20 @@ _public_ const char *udev_device_get_sysattr_value(struct udev_device *udev_devi if (list_entry != NULL) return udev_list_entry_get_value(list_entry); - util_strscpyl(path, sizeof(path), udev_device_get_syspath(udev_device), "/", sysattr, NULL); + strscpyl(path, sizeof(path), udev_device_get_syspath(udev_device), "/", sysattr, NULL); if (lstat(path, &statbuf) != 0) { udev_list_entry_add(&udev_device->sysattr_value_list, sysattr, NULL); goto out; } 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. */ - if (strcmp(sysattr, "driver") == 0 || - strcmp(sysattr, "subsystem") == 0 || - strcmp(sysattr, "module") == 0) { + if (streq(sysattr, "driver") || + streq(sysattr, "subsystem") || + streq(sysattr, "module")) { if (util_get_sys_core_link_value(udev_device->udev, sysattr, udev_device->syspath, value, sizeof(value)) < 0) return NULL; @@ -1327,16 +1483,6 @@ _public_ const char *udev_device_get_sysattr_value(struct udev_device *udev_devi goto out; } - /* resolve link to a device and return its syspath */ - util_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; } @@ -1368,6 +1514,91 @@ out: return val; } +/** + * udev_device_set_sysattr_value: + * @udev_device: udev device + * @sysattr: attribute name + * @value: new value to be set + * + * Update the contents of the sys attribute and the cached value of the device. + * + * Returns: Negative error code on failure or 0 on success. + **/ +_public_ int udev_device_set_sysattr_value(struct udev_device *udev_device, const char *sysattr, char *value) +{ + struct udev_device *dev; + char path[UTIL_PATH_SIZE]; + struct stat statbuf; + int fd; + ssize_t size, value_len; + int ret = 0; + + if (udev_device == NULL) + return -EINVAL; + dev = udev_device; + if (sysattr == NULL) + return -EINVAL; + if (value == NULL) + value_len = 0; + else + value_len = strlen(value); + + 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); + ret = -ENXIO; + goto out; + } + + if (S_ISLNK(statbuf.st_mode)) { + ret = -EINVAL; + goto out; + } + + /* skip directories */ + if (S_ISDIR(statbuf.st_mode)) { + ret = -EISDIR; + goto out; + } + + /* skip non-readable files */ + if ((statbuf.st_mode & S_IRUSR) == 0) { + ret = -EACCES; + goto out; + } + + /* Value is limited to 4k */ + if (value_len > 4096) { + ret = -EINVAL; + goto out; + } + util_remove_trailing_chars(value, '\n'); + + /* write attribute value */ + fd = open(path, O_WRONLY|O_CLOEXEC); + if (fd < 0) { + ret = -errno; + goto out; + } + size = write(fd, value, value_len); + close(fd); + if (size < 0) { + ret = -errno; + goto out; + } + if (size < value_len) { + ret = -EIO; + goto out; + } + + /* wrote a valid value, store it in cache and return it */ + udev_list_entry_add(&dev->sysattr_value_list, sysattr, value); +out: + if (dev != udev_device) + udev_device_unref(dev); + return ret; +} + static int udev_device_sysattr_list_read(struct udev_device *udev_device) { struct dirent *dent; @@ -1375,13 +1606,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]; @@ -1391,7 +1622,7 @@ static int udev_device_sysattr_list_read(struct udev_device *udev_device) if (dent->d_type != DT_LNK && dent->d_type != DT_REG) continue; - util_strscpyl(path, sizeof(path), udev_device_get_syspath(udev_device), "/", dent->d_name, NULL); + strscpyl(path, sizeof(path), udev_device_get_syspath(udev_device), "/", dent->d_name, NULL); if (lstat(path, &statbuf) != 0) continue; if ((statbuf.st_mode & S_IRUSR) == 0) @@ -1429,60 +1660,22 @@ _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(udev_get_sys_path(udev_device->udev))]; - 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] != '/') { - if (asprintf(&udev_device->devnode, "%s/%s", udev_get_dev_path(udev_device->udev), devnode) < 0) + if (asprintf(&udev_device->devnode, "/dev/%s", devnode) < 0) udev_device->devnode = NULL; } else { udev_device->devnode = strdup(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; } -int udev_device_add_devlink(struct udev_device *udev_device, const char *devlink, int unique) +int udev_device_add_devlink(struct udev_device *udev_device, const char *devlink) { struct udev_list_entry *list_entry; @@ -1490,8 +1683,6 @@ int udev_device_add_devlink(struct udev_device *udev_device, const char *devlink list_entry = udev_list_entry_add(&udev_device->devlinks_list, devlink, NULL); if (list_entry == NULL) return -ENOMEM; - if (unique) - udev_list_entry_set_num(list_entry, true); return 0; } @@ -1504,13 +1695,13 @@ const char *udev_device_get_id_filename(struct udev_device *udev_device) if (major(udev_device_get_devnum(udev_device)) > 0) { /* use dev_t -- b259:131072, c254:0 */ if (asprintf(&udev_device->id_filename, "%c%u:%u", - strcmp(udev_device_get_subsystem(udev_device), "block") == 0 ? 'b' : 'c', + streq(udev_device_get_subsystem(udev_device), "block") ? 'b' : 'c', major(udev_device_get_devnum(udev_device)), minor(udev_device_get_devnum(udev_device))) < 0) 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 { /* @@ -1554,9 +1745,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) @@ -1564,6 +1760,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; @@ -1590,6 +1800,15 @@ _public_ struct udev_list_entry *udev_device_get_tags_list_entry(struct udev_dev return udev_list_get_entry(&udev_device->tags_list); } +/** + * udev_device_has_tag: + * @udev_device: udev device + * @tag: tag name + * + * Check if a given device has a certain tag associated. + * + * Returns: 1 if the tag is found. 0 otherwise. + **/ _public_ int udev_device_has_tag(struct udev_device *udev_device, const char *tag) { struct udev_list_entry *list_entry; @@ -1643,10 +1862,10 @@ static int update_envp_monitor_buf(struct udev_device *udev_device) return -EINVAL; /* add property string to monitor buffer */ - l = util_strpcpyl(&s, l, key, "=", udev_list_entry_get_value(list_entry), NULL); + l = strpcpyl(&s, l, key, "=", udev_list_entry_get_value(list_entry), NULL); if (l == 0) return -EINVAL; - /* advance past the trailing '\0' that util_strpcpyl() guarantees */ + /* advance past the trailing '\0' that strpcpyl() guarantees */ s++; l--; } @@ -1679,7 +1898,7 @@ 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; } @@ -1718,3 +1937,99 @@ 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_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; +}