On Wed, Apr 1, 2015 at 3:00 PM, Tom Gundersen <t...@jklm.no> wrote: > This provides equivalent functionality to libudev-device, but in the > systemd style. The public API only caters to creating sd_device objects > from for devices that already exist in /sys, there is no support for > listening for monitoring uevents or creating devices received over > the udev netlink protocol. > > The private API contains the necessary functionality to make sd-device > a drop-in replacement for libudev-device, but which we will not export > publicly. > --- > > Hi guys, > > This is another step on the way of ripping out the bits of libudev that will > still be useful once the udev netlink transport has been replaced by kdbus. > > Feedback welcome.
Not a real review. Just a few nitpicks. > > Makefile.am | 9 +- > src/libsystemd/sd-device/device-internal.h | 125 ++ > src/libsystemd/sd-device/device-private.c | 1101 +++++++++++++++++ > src/libsystemd/sd-device/device-private.h | 63 + > src/libsystemd/sd-device/device-util.h | 48 + > src/libsystemd/sd-device/sd-device.c | 1812 > ++++++++++++++++++++++++++++ > src/systemd/sd-device.h | 77 ++ > 7 files changed, 3234 insertions(+), 1 deletion(-) > create mode 100644 src/libsystemd/sd-device/device-internal.h > create mode 100644 src/libsystemd/sd-device/device-private.c > create mode 100644 src/libsystemd/sd-device/device-private.h > create mode 100644 src/libsystemd/sd-device/device-util.h > create mode 100644 src/libsystemd/sd-device/sd-device.c > create mode 100644 src/systemd/sd-device.h > > diff --git a/Makefile.am b/Makefile.am > index 93fdbc2..9509247 100644 > --- a/Makefile.am > +++ b/Makefile.am > @@ -231,6 +231,7 @@ AM_CPPFLAGS = \ > -I $(top_srcdir)/src/libsystemd/sd-rtnl \ > -I $(top_srcdir)/src/libsystemd/sd-network \ > -I $(top_srcdir)/src/libsystemd/sd-hwdb \ > + -I $(top_srcdir)/src/libsystemd/sd-device \ > -I $(top_srcdir)/src/libsystemd-network \ > -I $(top_srcdir)/src/libsystemd-terminal \ > $(OUR_CPPFLAGS) > @@ -2918,6 +2919,7 @@ libsystemd_internal_la_SOURCES = \ > src/systemd/sd-path.h \ > src/systemd/sd-network.h \ > src/systemd/sd-hwdb.h \ > + src/systemd/sd-device.h \ > src/libsystemd/sd-bus/sd-bus.c \ > src/libsystemd/sd-bus/bus-control.c \ > src/libsystemd/sd-bus/bus-control.h \ > @@ -2981,7 +2983,12 @@ libsystemd_internal_la_SOURCES = \ > src/libsystemd/sd-network/network-util.c \ > src/libsystemd/sd-hwdb/sd-hwdb.c \ > src/libsystemd/sd-hwdb/hwdb-util.h \ > - src/libsystemd/sd-hwdb/hwdb-internal.h > + src/libsystemd/sd-hwdb/hwdb-intenal.h \ > + src/libsystemd/sd-device/device-internal.h \ > + src/libsystemd/sd-device/device-util.h \ > + src/libsystemd/sd-device/sd-device.c \ > + src/libsystemd/sd-device/device-private.c \ > + src/libsystemd/sd-device/device-private.h > > nodist_libsystemd_internal_la_SOURCES = \ > src/libsystemd/libsystemd.sym > diff --git a/src/libsystemd/sd-device/device-internal.h > b/src/libsystemd/sd-device/device-internal.h > new file mode 100644 > index 0000000..59ec1a6 > --- /dev/null > +++ b/src/libsystemd/sd-device/device-internal.h > @@ -0,0 +1,125 @@ > +/*** > + This file is part of systemd. > + > + Copyright 2008-2012 Kay Sievers <k...@vrfy.org> > + Copyright 2014 Tom Gundersen <t...@jklm.no> > + > + 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 <http://www.gnu.org/licenses/>. > +***/ > + > +#pragma once > + > +#include "hashmap.h" > +#include "set.h" > + > +struct sd_device { > + uint64_t n_ref; > + > + sd_device *parent; > + bool parent_set; /* no need to try to reload parent */ > + > + OrderedHashmap *properties; > + Iterator properties_iterator; > + uint64_t properties_generation; /* changes whenever the properties > are changed */ > + uint64_t properties_iterator_generation; /* generation when > iteration was started */ > + > + /* the subset of the properties that should be written to the db*/ > + OrderedHashmap *properties_db; > + > + Hashmap *sysattr_values; /* cached sysattr values */ > + > + Set *sysattrs; /* names of sysattrs */ > + Iterator sysattrs_iterator; > + bool sysattrs_read; /* don't try to re-read sysattrs once read */ > + > + Set *tags; > + Iterator tags_iterator; > + uint64_t tags_generation; /* changes whenever the tags are changed */ > + uint64_t tags_iterator_generation; /* generation when iteration was > started */ > + bool property_tags_outdated; /* need to update TAGS= property */ > + > + Set *devlinks; > + Iterator devlinks_iterator; > + uint64_t devlinks_generation; /* changes whenever the devlinks are > changed */ > + uint64_t devlinks_iterator_generation; /* generation when iteration > was started */ > + bool property_devlinks_outdated; /* need to update DEVLINKS= > property */ > + int devlink_priority; > + > + char **properties_strv; /* the properties hashmap as a strv */ > + uint8_t *properties_nulstr; /* the same as a nulstr */ > + size_t properties_nulstr_len; > + bool properties_buf_outdated; /* need to reread hashmap */ > + > + int watch_handle; > + > + char *syspath; > + const char *devpath; > + const char *sysnum; > + char *sysname; > + bool sysname_set; /* don't reread sysname */ > + > + char *devtype; > + int ifindex; > + char *devname; > + dev_t devnum; > + > + char *subsystem; > + bool subsystem_set; /* don't reread subsystem */ > + char *driver; > + bool driver_set; /* don't reread driver */ > + > + char *id_filename; > + > + bool is_initialized; > + uint64_t usec_initialized; > + > + mode_t devmode; > + uid_t devuid; > + gid_t devgid; > + > + bool uevent_loaded; /* don't reread uevent */ > + bool db_loaded; /* don't reread db */ > + > + bool sealed; /* don't read more information from uevent/db */ > + bool db_persist; /* don't clean up the db when switching from initrd > to real root */ > +}; > + > +typedef enum DeviceAction { > + DEVICE_ACTION_ADD, > + DEVICE_ACTION_REMOVE, > + DEVICE_ACTION_CHANGE, > + DEVICE_ACTION_MOVE, > + DEVICE_ACTION_ONLINE, > + DEVICE_ACTION_OFFLINE, > + _DEVICE_ACTION_MAX, > + _DEVICE_ACTION_INVALID = -1, > +} DeviceAction; > + > +int device_new_aux(sd_device **ret); > +int device_add_property_aux(sd_device *device, const char *key, const char > *value, bool db); > +int device_add_property_internal(sd_device *device, const char *key, const > char *value); > +int device_read_uevent_file(sd_device *device); > + > +int device_set_syspath(sd_device *device, const char *_syspath, bool verify); > +int device_set_ifindex(sd_device *device, const char *ifindex); > +int device_set_devmode(sd_device *device, const char *devmode); > +int device_set_devname(sd_device *device, const char *_devname); > +int device_set_devtype(sd_device *device, const char *_devtype); > +int device_set_devnum(sd_device *device, const char *major, const char > *minor); > +int device_set_subsystem(sd_device *device, const char *_subsystem); > +int device_set_driver(sd_device *device, const char *_driver); > +int device_set_usec_initialized(sd_device *device, const char *initialized); > + > +DeviceAction device_action_from_string(const char *s) _pure_; > +const char *device_action_to_string(DeviceAction a) _const_; > diff --git a/src/libsystemd/sd-device/device-private.c > b/src/libsystemd/sd-device/device-private.c > new file mode 100644 > index 0000000..56f6dd2 > --- /dev/null > +++ b/src/libsystemd/sd-device/device-private.c > @@ -0,0 +1,1101 @@ > +/*** > + This file is part of systemd. > + > + Copyright 2008-2012 Kay Sievers <k...@vrfy.org> > + Copyright 2014 Tom Gundersen <t...@jklm.no> > + > + 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 <http://www.gnu.org/licenses/>. > +***/ > + > +#include <ctype.h> > +#include <sys/types.h> > +#include <net/if.h> > + > +#include "util.h" > +#include "macro.h" > +#include "refcnt.h" > +#include "path-util.h" > +#include "strxcpyx.h" > +#include "fileio.h" > +#include "hashmap.h" > +#include "set.h" > +#include "strv.h" > +#include "mkdir.h" > + > +#include "sd-device.h" > + > +#include "device-util.h" > +#include "device-internal.h" > +#include "device-private.h" > + > +int device_add_property(sd_device *device, const char *key, const char > *value) { > + int r; > + > + assert(device); > + assert(key); > + > + r = device_add_property_aux(device, key, value, false); > + if (r < 0) > + return r; > + > + if (key[0] != '.') { > + r = device_add_property_aux(device, key, value, true); > + if (r < 0) > + return r; > + } > + > + return 0; > +} > + > +static int device_add_property_internal_from_string(sd_device *device, const > char *str) { > + _cleanup_free_ char *key = NULL; > + char *value; > + > + assert(device); > + assert(str); > + > + key = strdup(str); > + if (!key) > + return -ENOMEM; > + > + value = strchr(key, '='); > + if (!value) > + return -EINVAL; > + > + *value = '\0'; > + > + if (isempty(++value)) > + value = NULL; > + > + return device_add_property_internal(device, key, value); > +} > + > +static int handle_db_line(sd_device *device, char key, const char *value) { > + char *path; > + int r; > + > + assert(device); > + assert(value); > + > + switch (key) { > + case 'S': > + path = strjoina("/dev/", value); > + r = device_add_devlink(device, path); > + if (r < 0) > + return r; > + > + break; > + case 'L': > + r = safe_atoi(value, &device->devlink_priority); > + if (r < 0) > + return r; > + > + break; > + case 'E': > + r = device_add_property_internal_from_string(device, value); > + if (r < 0) > + return r; > + > + break; > + case 'G': > + r = device_add_tag(device, value); > + if (r < 0) > + return r; > + > + break; > + case 'W': > + r = safe_atoi(value, &device->watch_handle); > + if (r < 0) > + return r; > + > + break; > + case 'I': > + r = device_set_usec_initialized(device, value); > + if (r < 0) > + return r; > + > + break; > + default: > + log_debug("device db: unknown key '%c'", key); > + } > + > + return 0; > +} > + > +void device_set_devlink_priority(sd_device *device, int priority) { > + assert(device); > + > + device->devlink_priority = priority; > +} > + > +void device_set_is_initialized(sd_device *device) { > + assert(device); > + > + device->is_initialized = true; > +} > + > +int device_ensure_usec_initialized(sd_device *device, sd_device *device_old) > { > + char num[DECIMAL_STR_MAX(usec_t)]; > + usec_t usec_initialized; > + int r; > + > + assert(device); > + > + if (device_old && device_old->usec_initialized > 0) > + usec_initialized = device_old->usec_initialized; > + else > + usec_initialized = now(CLOCK_MONOTONIC); > + > + r = snprintf(num, sizeof(num), USEC_FMT, usec_initialized); > + if (r < 0) > + return -errno; > + > + r = device_set_usec_initialized(device, num); > + if (r < 0) > + return r; > + > + return 0; > +} > + > +static int device_read_db(sd_device *device) { > + _cleanup_free_ char *db = NULL; > + char *path; > + const char *id, *value; > + char key; > + size_t db_len; > + unsigned i; > + int r; > + > + enum { > + PRE_KEY, > + KEY, > + PRE_VALUE, > + VALUE, > + INVALID_LINE, > + } state = PRE_KEY; > + > + assert(device); > + > + if (device->db_loaded || device->sealed) > + return 0; > + > + r = device_get_id_filename(device, &id); > + if (r < 0) > + return r; > + > + path = strjoina("/run/udev/data/", id); > + > + r = read_full_file(path, &db, &db_len); > + if (r < 0) { > + if (r == -ENOENT) > + return 0; > + else { > + log_debug("sd-device: failed to read db '%s': %s", > path, strerror(-r)); > + return r; > + } > + } > + > + /* devices with a database entry are initialized */ > + device_set_is_initialized(device); > + > + for (i = 0; i < db_len; i++) { > + switch (state) { > + case PRE_KEY: > + if (!strchr(NEWLINE, db[i])) { > + key = db[i]; > + > + state = KEY; > + } > + > + break; > + case KEY: > + if (db[i] != ':') { > + log_debug("sd-device: ignoring invalid db > entry with key '%c'", key); > + > + state = INVALID_LINE; > + } else { > + db[i] = '\0'; > + > + state = PRE_VALUE; > + } > + > + break; > + case PRE_VALUE: > + value = &db[i]; > + > + state = VALUE; > + > + break; > + case INVALID_LINE: > + if (strchr(NEWLINE, db[i])) > + state = PRE_KEY; > + > + break; > + case VALUE: > + if (strchr(NEWLINE, db[i])) { > + db[i] = '\0'; > + r = handle_db_line(device, key, value); > + if (r < 0) > + log_debug("sd-device: failed to > handle db entry '%c:%s': %s", key, value, strerror(-r)); > + > + state = PRE_KEY; > + } > + > + break; > + default: > + assert_not_reached("invalid state when parsing db"); > + } > + } > + > + device->db_loaded = true; > + > + return 0; > +} > + > +uint64_t device_get_properties_generation(sd_device *device) { > + assert(device); > + > + return device->properties_generation; > +} > + > +uint64_t device_get_tags_generation(sd_device *device) { > + assert(device); > + > + return device->tags_generation; > +} > + > +uint64_t device_get_devlinks_generation(sd_device *device) { > + assert(device); > + > + return device->devlinks_generation; > +} > + > +int device_get_devnode_mode(sd_device *device, mode_t *mode) { > + int r; > + > + assert(device); > + assert(mode); > + > + r = device_read_db(device); > + if (r < 0) > + return r; > + > + *mode = device->devmode; > + > + return 0; > +} > + > +int device_get_devnode_uid(sd_device *device, uid_t *uid) { > + int r; > + > + assert(device); > + assert(uid); > + > + r = device_read_db(device); > + if (r < 0) > + return r; > + > + *uid = device->devuid; > + > + return 0; > +} > + > +static int device_set_devuid(sd_device *device, const char *uid) { > + unsigned u; > + int r; > + > + assert(device); > + assert(uid); > + > + r = safe_atou(uid, &u); > + if (r < 0) > + return r; > + > + r = device_add_property_internal(device, "DEVUID", uid); > + if (r < 0) > + return r; > + > + device->devuid = u; > + > + return 0; > +} > + > +int device_get_devnode_gid(sd_device *device, gid_t *gid) { > + int r; > + > + assert(device); > + assert(gid); > + > + r = device_read_db(device); > + if (r < 0) > + return r; > + > + *gid = device->devgid; > + > + return 0; > +} > + > +static int device_set_devgid(sd_device *device, const char *gid) { > + unsigned g; > + int r; > + > + assert(device); > + assert(gid); > + > + r = safe_atou(gid, &g); > + if (r < 0) > + return r; > + > + r = device_add_property_internal(device, "DEVGID", gid); > + if (r < 0) > + return r; > + > + device->devgid = g; > + > + return 0; > +} > + > +static int device_ammend(sd_device *device, const char *key, const char > *value) { > + int r; > + > + assert(device); > + assert(key); > + assert(value); > + > + if (streq(key, "DEVPATH")) { > + char *path; > + > + path = strjoina("/sys", value); > + > + /* the caller must verify or trust this data (e.g., if it > comes from the kernel) */ > + r = device_set_syspath(device, path, false); > + if (r < 0) > + return log_debug_errno(r, "sd-device: could not set > syspath to '%s': %m", path); > + } else if (streq(key, "SUBSYSTEM")) { > + r = device_set_subsystem(device, value); > + if (r < 0) > + return log_debug_errno(r, "sd-device: could not set > subsystem to '%s': %m", value); > + } else if (streq(key, "DEVTYPE")) { > + r = device_set_devtype(device, value); > + if (r < 0) > + return log_debug_errno(r, "sd-device: could not set > devtype to '%s': %m", value); > + } else if (streq(key, "DEVNAME")) { > + r = device_set_devname(device, value); > + if (r < 0) > + return log_debug_errno(r, "sd-device: could not set > devname to '%s': %m", value); > + } else if (streq(key, "USEC_INITIALIZED")) { > + r = device_set_usec_initialized(device, value); > + if (r < 0) > + return log_debug_errno(r, "sd-device: could not set > usec-initialized to '%s': %m", value); > + } else if (streq(key, "DRIVER")) { > + r = device_set_driver(device, value); > + if (r < 0) > + return log_debug_errno(r, "sd-device: could not set > driver to '%s': %m", value); > + } else if (streq(key, "IFINDEX")) { > + r = device_set_ifindex(device, value); > + if (r < 0) > + return log_debug_errno(r, "sd-device: could not set > ifindex to '%s': %m", value); > + } else if (streq(key, "DEVMODE")) { > + r = device_set_devmode(device, value); > + if (r < 0) > + return log_debug_errno(r, "sd-device: could not set > devmode to '%s': %m", value); > + } else if (streq(key, "DEVUID")) { > + r = device_set_devuid(device, value); > + if (r < 0) > + return log_debug_errno(r, "sd-device: could not set > devuid to '%s': %m", value); > + } else if (streq(key, "DEVGID")) { > + r = device_set_devgid(device, value); > + if (r < 0) > + return log_debug_errno(r, "sd-device: could not set > devgid to '%s': %m", value); > + } else if (streq(key, "DEVLINKS")) { > + char *devlinks, *next; > + > + devlinks = strdupa(value); > + > + while ((next = strchr(devlinks, ' '))) { > + next[0] = '\0'; > + > + r = device_add_devlink(device, devlinks); > + if (r < 0) > + return log_debug_errno(r, "sd-device: could > not add devlink '%s': %m", devlinks); > + > + devlinks = next + 1; > + } > + } else if (streq(key, "TAGS")) { > + char *tags, *next; > + > + tags = strdupa(value); > + > + while ((next = strchr(tags, ':'))) { > + next[0] = '\0'; > + > + r = device_add_tag(device, tags); > + if (r < 0) > + return log_debug_errno(r, "sd-device: could > not add tag '%s': %m", tags); > + > + tags = next + 1; > + } > + } else { > + r = device_add_property_internal(device, key, value); > + if (r < 0) > + return log_debug_errno(r, "sd-device: could not add > property '%s=%s': %m", key, value); > + } > + > + return 0; > +} > + > +static const char* const device_action_table[_DEVICE_ACTION_MAX] = { > + [DEVICE_ACTION_ADD] = "add", > + [DEVICE_ACTION_REMOVE] = "remove", > + [DEVICE_ACTION_CHANGE] = "change", > + [DEVICE_ACTION_MOVE] = "move", > + [DEVICE_ACTION_ONLINE] = "online", > + [DEVICE_ACTION_OFFLINE] = "offline", > +}; > + > +DEFINE_STRING_TABLE_LOOKUP(device_action, DeviceAction); > + > +static int device_append(sd_device *device, char *key, const char **_major, > const char **_minor, uint64_t *_seqnum, > + DeviceAction *_action) { > + DeviceAction action = _DEVICE_ACTION_INVALID; > + uint64_t seqnum; Initialize seqnum to 0 > + const char *major = NULL, *minor = NULL; > + char *value; > + int r; > + > + assert(device); > + assert(key); > + assert(_major); > + assert(_minor); > + assert(_seqnum); > + assert(_action); > + > + value = strchr(key, '='); > + if (!value) { > + log_debug("sd-device: not a key-value pair: '%s'", key); > + return -EINVAL; > + } > + > + *value = '\0'; > + > + value++; > + > + if (streq(key, "MAJOR")) > + major = value; > + else if (streq(key, "MINOR")) > + minor = value; > + else { > + if (streq(key, "ACTION")) { > + action = device_action_from_string(value); > + if (action == _DEVICE_ACTION_INVALID) > + return -EINVAL; > + } else if (streq(key, "SEQNUM")) { > + r = safe_atou64(value, &seqnum); > + if (r < 0) > + return r; > + else if (seqnum == 0) > + /* kernel only sends seqnum > 0 */ > + return -EINVAL; > + } > + > + r = device_ammend(device, key, value); > + if (r < 0) > + return r; > + } > + > + if (major != 0) > + *_major = major; > + > + if (minor != 0) > + *_minor = minor; > + > + if (action != _DEVICE_ACTION_INVALID) > + *_action = action; > + > + if (seqnum > 0) > + *_seqnum = seqnum; > + > + return 0; > +} > + > +void device_seal(sd_device *device) { > + assert(device); > + > + device->sealed = true; > +} > + > +static int device_verify(sd_device *device, DeviceAction action, uint64_t > seqnum) { > + assert(device); > + > + if (!device->devpath || !device->subsystem || action == > _DEVICE_ACTION_INVALID || seqnum == 0) { > + log_debug("sd-device: device created from strv lacks > devpath, subsystem, action or seqnum"); > + return -EINVAL; > + } > + > + device->sealed = true; > + > + return 0; > +} > + > +int device_new_from_strv(sd_device **ret, char **strv) { > + _cleanup_device_unref_ sd_device *device = NULL; > + char **key; > + const char *major = NULL, *minor = NULL; > + DeviceAction action = _DEVICE_ACTION_INVALID; > + uint64_t seqnum; > + int r; > + > + assert(ret); > + assert(strv); > + > + r = device_new_aux(&device); > + if (r < 0) > + return r; > + > + STRV_FOREACH(key, strv) { > + r = device_append(device, *key, &major, &minor, &seqnum, > &action); > + if (r < 0) > + return r; > + } > + > + if (major) { > + r = device_set_devnum(device, major, minor); > + if (r < 0) > + return log_debug_errno(r, "sd-device: could not set > devnum %s:%s: %m", major, minor); > + } > + > + r = device_verify(device, action, seqnum); > + if (r < 0) > + return r; > + > + *ret = device; > + device = NULL; > + > + return 0; > +} > + > +int device_new_from_nulstr(sd_device **ret, uint8_t *nulstr, size_t len) { > + _cleanup_device_unref_ sd_device *device = NULL; > + const char *major = NULL, *minor = NULL; > + DeviceAction action = _DEVICE_ACTION_INVALID; > + uint64_t seqnum; > + unsigned i = 0; > + int r; > + > + assert(ret); > + assert(nulstr); > + assert(len); > + > + r = device_new_aux(&device); > + if (r < 0) > + return r; > + > + while (i < len) { > + char *key; > + const char *end; > + > + key = (char*)&nulstr[i]; > + end = memchr(key, '\0', len - i); > + if (!end) { > + log_debug("sd-device: failed to parse nulstr"); > + return -EINVAL; > + } > + i += end - key + 1; > + > + r = device_append(device, key, &major, &minor, &seqnum, > &action); > + if (r < 0) > + return r; > + } > + > + if (major) { > + r = device_set_devnum(device, major, minor); > + if (r < 0) > + return log_debug_errno(r, "sd-device: could not set > devnum %s:%s: %m", major, minor); > + } > + > + r = device_verify(device, action, seqnum); > + if (r < 0) > + return r; > + > + *ret = device; > + device = NULL; > + > + return 0; > +} > + > +static int device_update_properties_bufs(sd_device *device) { > + const char *val, *prop; > + char **buf_strv = NULL; > + uint8_t *buf_nulstr = NULL; > + size_t allocated_nulstr = 0, allocated_strv = 0; > + size_t nulstr_len = 0, strv_size = 0; > + > + assert(device); > + > + FOREACH_DEVICE_PROPERTY(device, prop, val) { > + size_t len = 0; > + > + len = strlen(prop) + 1 + strlen(val); > + > + buf_nulstr = GREEDY_REALLOC0(buf_nulstr, allocated_nulstr, > nulstr_len + len + 2); > + if (!buf_nulstr) > + return -ENOMEM; > + > + buf_strv = GREEDY_REALLOC0(buf_strv, allocated_strv, > strv_size + 2); > + if (!buf_strv) > + return -ENOMEM; > + > + buf_strv[++ strv_size] = (char *)&buf_nulstr[nulstr_len]; > + strscpyl((char *)buf_nulstr + nulstr_len, len + 1, prop, > "=", val, NULL); > + nulstr_len += len + 1; > + } > + > + free(device->properties_nulstr); > + free(device->properties_strv); > + device->properties_nulstr = buf_nulstr; > + device->properties_nulstr_len = nulstr_len; > + device->properties_strv = buf_strv; > + > + device->properties_buf_outdated = false; > + > + return 0; > +} > + > +int device_get_properties_nulstr(sd_device *device, const uint8_t **nulstr, > size_t *len) { > + int r; > + > + assert(device); > + assert(nulstr); > + assert(len); > + > + if (device->properties_buf_outdated) { > + r = device_update_properties_bufs(device); > + if (r < 0) > + return r; > + } > + > + *nulstr = device->properties_nulstr; > + *len = device->properties_nulstr_len; > + > + return 0; > +} > + > +int device_get_properties_strv(sd_device *device, char ***strv) { > + int r; > + > + assert(device); > + assert(strv); > + > + r = device_update_properties_bufs(device); > + if (r < 0) > + return r; > + > + *strv = device->properties_strv; > + > + return 0; > +} > + > +int device_get_devlink_priority(sd_device *device, int *priority) { > + int r; > + > + assert(device); > + assert(priority); > + > + r = device_read_db(device); > + if (r < 0) > + return r; > + > + *priority = device->devlink_priority; > + > + return 0; > +} > + > +int device_get_watch_handle(sd_device *device, int *handle) { > + int r; > + > + assert(device); > + assert(handle); > + > + r = device_read_db(device); > + if (r < 0) > + return r; > + > + *handle = device->watch_handle; > + > + return 0; > +} > + > +void device_set_watch_handle(sd_device *device, int handle) { > + assert(device); > + > + device->watch_handle = handle; > +} > + > +int device_rename(sd_device *device, const char *name) { > + _cleanup_free_ char *dirname = NULL; > + char *new_syspath; > + const char *interface; > + int r; > + > + assert(device); > + assert(name); > + > + dirname = dirname_malloc(device->syspath); > + if (!dirname) > + return -ENOMEM; > + > + new_syspath = strjoina(dirname, "/", name); > + > + /* the user must trust that the new name is correct */ > + r = device_set_syspath(device, new_syspath, false); > + if (r < 0) > + return r; > + > + r = sd_device_get_property_value(device, "INTERFACE", &interface); > + if (r >= 0) { > + r = device_add_property_internal(device, "INTERFACE", name); > + if (r < 0) > + return r; > + > + /* like DEVPATH_OLD, INTERFACE_OLD is not saved to the db, > but only stays around for the current event */ > + r = device_add_property_internal(device, "INTERFACE_OLD", > interface); > + if (r < 0) > + return r; > + } else if (r != -ENOENT) > + return r; > + > + return 0; > +} > + > +int device_shallow_clone(sd_device *old_device, sd_device **new_device) { > + _cleanup_device_unref_ sd_device *ret = NULL; > + int r; > + > + assert(old_device); > + assert(new_device); > + > + r = device_new_aux(&ret); > + if (r < 0) > + return r; > + > + r = device_set_syspath(ret, old_device->syspath, false); > + if (r < 0) > + return r; > + > + r = device_set_subsystem(ret, old_device->subsystem); > + if (r < 0) > + return r; > + > + ret->devnum = old_device->devnum; > + > + *new_device = ret; > + ret = NULL; > + > + return 0; > +} > + > +int device_clone_with_db(sd_device *old_device, sd_device **new_device) { > + _cleanup_device_unref_ sd_device *ret = NULL; > + int r; > + > + assert(old_device); > + assert(new_device); > + > + r = device_shallow_clone(old_device, &ret); > + if (r < 0) > + return r; > + > + r = device_read_db(ret); > + if (r < 0) > + return r; > + > + ret->sealed = true; > + > + *new_device = ret; > + ret = NULL; > + > + return 0; > +} > + > +int device_new_from_synthetic_event(sd_device **new_device, const char > *syspath, const char *action) { > + _cleanup_device_unref_ sd_device *ret = NULL; > + int r; > + > + assert(new_device); > + assert(syspath); > + assert(action); > + > + r = sd_device_new_from_syspath(&ret, syspath); > + if (r < 0) > + return r; > + > + r = device_read_uevent_file(ret); > + if (r < 0) > + return r; > + > + r = device_add_property_internal(ret, "ACTION", action); > + if (r < 0) > + return r; > + > + *new_device = ret; > + ret = NULL; > + > + return 0; > +} > + > +int device_copy_properties(sd_device *device_dst, sd_device *device_src) { > + const char *property, *value; > + int r; Initialize r to 0 unless a device with no properties is impossible. > + > + assert(device_dst); > + assert(device_src); > + > + FOREACH_DEVICE_PROPERTY(device_src, property, value) { > + r = device_add_property(device_dst, property, value); > + if (r < 0) > + return r; > + } > + > + return r; > +} > + > +void device_cleanup_tags(sd_device *device) { > + assert(device); > + > + set_free_free(device->tags); > + device->tags = NULL; > + device->property_tags_outdated = true; > + device->tags_generation ++; > +} > + > +void device_cleanup_devlinks(sd_device *device) { > + assert(device); > + > + set_free_free(device->devlinks); > + device->devlinks = NULL; > + device->property_devlinks_outdated = true; > + device->devlinks_generation ++; > +} > + > +void device_remove_tag(sd_device *device, const char *tag) { > + assert(device); > + assert(tag); > + > + free(set_remove(device->tags, tag)); > + device->property_tags_outdated = true; > + device->tags_generation ++; > +} > + > +static int device_tag(sd_device *device, const char *tag, bool add) { > + const char *id; > + char *path; > + int r; > + > + assert(device); > + assert(tag); > + > + r = device_get_id_filename(device, &id); > + if (r < 0) > + return r; > + > + path = strjoina("/run/udev/tags/", tag, "/", id); > + > + if (add) { > + r = touch_file(path, true, USEC_INFINITY, UID_INVALID, > GID_INVALID, 0444); > + if (r < 0) > + return r; > + } else { > + r = unlink(path); > + if (r < 0 && errno != ENOENT) > + return -errno; > + } > + > + return 0; > +} > + > +int device_tag_index(sd_device *device, sd_device *device_old, bool add) { > + const char *tag; > + int r = 0, k; > + > + if (add && device_old) { > + /* delete possible left-over tags */ > + FOREACH_DEVICE_TAG(device_old, tag) { > + if (!sd_device_has_tag(device, tag)) { > + k = device_tag(device_old, tag, false); > + if (r >= 0 && k < 0) > + r = k; > + } > + } > + } > + > + FOREACH_DEVICE_TAG(device, tag) { > + k = device_tag(device, tag, add); > + if (r >= 0 && k < 0) > + r = k; > + } > + > + return r; > +} > + > +static bool device_has_info(sd_device *device) { > + assert(device); > + > + if (!set_isempty(device->devlinks)) > + return true; > + > + if (device->devlink_priority != 0) > + return true; > + > + if (!ordered_hashmap_isempty(device->properties_db)) > + return true; > + > + if (!set_isempty(device->tags)) > + return true; > + > + if (device->watch_handle >= 0) > + return true; > + > + return false; > +} > + > +void device_set_db_persist(sd_device *device) { > + assert(device); > + > + device->db_persist = true; > +} > + > +int device_update_db(sd_device *device) { > + const char *id; > + char *path; > + _cleanup_fclose_ FILE *f = NULL; > + _cleanup_free_ char *path_tmp = NULL; > + bool has_info; > + int r; > + > + assert(device); > + > + has_info = device_has_info(device); > + > + r = device_get_id_filename(device, &id); > + if (r < 0) > + return r; > + > + path = strjoina("/run/udev/data/", id); > + > + /* do not store anything for otherwise empty devices */ > + if (!has_info && major(device->devnum) == 0 && device->ifindex == 0) > { > + r = unlink(path); > + if (r < 0 && errno != ENOENT) > + return -errno; > + > + return 0; > + } > + > + /* write a database file */ > + r = mkdir_parents(path, 0755); > + if (r < 0) > + return r; > + > + r = fopen_temporary(path, &f, &path_tmp); > + if (r < 0) > + return r; > + > + /* > + * set 'sticky' bit to indicate that we should not clean the > + * database when we transition from initramfs to the real root > + */ > + if (device->db_persist) { > + r = fchmod(fileno(f), 01644); > + if (r < 0) { > + r = -errno; > + goto fail; > + } > + } else { > + r = fchmod(fileno(f), 0644); > + if (r < 0) { > + r = -errno; > + goto fail; > + } > + } > + > + if (has_info) { > + const char *property, *value, *tag; > + Iterator i; > + > + if (major(device->devnum) > 0) { > + const char *devlink; > + > + FOREACH_DEVICE_DEVLINK(device, devlink) > + fprintf(f, "S:%s\n", devlink + > strlen("/dev/")); > + > + if (device->devlink_priority != 0) > + fprintf(f, "L:%i\n", > device->devlink_priority); > + > + if (device->watch_handle >= 0) > + fprintf(f, "W:%i\n", device->watch_handle); > + } > + > + if (device->usec_initialized > 0) > + fprintf(f, "I:"USEC_FMT"\n", > device->usec_initialized); > + > + ORDERED_HASHMAP_FOREACH_KEY(value, property, > device->properties_db, i) > + fprintf(f, "E:%s=%s\n", property, value); > + > + FOREACH_DEVICE_TAG(device, tag) > + fprintf(f, "G:%s\n", tag); > + } > + > + r = fflush_and_check(f); > + if (r < 0) > + goto fail; > + > + r = rename(path_tmp, path); > + if (r < 0) { > + r = -errno; > + goto fail; > + } > + > + log_debug("created %s file '%s' for '%s'", has_info ? "db" : "empty", > + path, device->devpath); > + > + return 0; > + > +fail: > + log_error_errno(r, "failed to create %s file '%s' for '%s'", > has_info ? "db" : "empty", > + path, device->devpath); > + unlink(path); > + unlink(path_tmp); > + > + return r; > +} > + > +int device_delete_db(sd_device *device) { > + const char *id; > + char *path; > + int r; > + > + assert(device); > + > + r = device_get_id_filename(device, &id); > + if (r < 0) > + return r; > + > + path = strjoina("/run/udev/data/", id); > + > + r = unlink(path); > + if (r < 0 && errno != ENOENT) > + return -errno; > + > + return 0; > +} > diff --git a/src/libsystemd/sd-device/device-private.h > b/src/libsystemd/sd-device/device-private.h > new file mode 100644 > index 0000000..7c6219c > --- /dev/null > +++ b/src/libsystemd/sd-device/device-private.h > @@ -0,0 +1,63 @@ > +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ > + > +#pragma once > + > +/*** > + This file is part of systemd. > + > + Copyright 2014 Tom Gundersen <t...@jklm.no> > + > + 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 <http://www.gnu.org/licenses/>. > +***/ > + > +int device_new_from_nulstr(sd_device **ret, uint8_t *nulstr, size_t len); > +int device_new_from_strv(sd_device **ret, char **strv); > + > +int device_get_id_filename(sd_device *device, const char **ret); > + > +int device_get_devlink_priority(sd_device *device, int *priority); > +int device_get_watch_handle(sd_device *device, int *handle); > +int device_get_devnode_mode(sd_device *device, mode_t *mode); > +int device_get_devnode_uid(sd_device *device, uid_t *uid); > +int device_get_devnode_gid(sd_device *device, gid_t *gid); > + > +void device_seal(sd_device *device); > +void device_set_is_initialized(sd_device *device); > +void device_set_watch_handle(sd_device *device, int fd); > +void device_set_db_persist(sd_device *device); > +void device_set_devlink_priority(sd_device *device, int priority); > +int device_ensure_usec_initialized(sd_device *devcie, sd_device *device_old); > +int device_add_devlink(sd_device *device, const char *devlink); > +int device_add_property(sd_device *device, const char *property, const char > *value); > +int device_add_tag(sd_device *device, const char *tag); > +void device_remove_tag(sd_device *device, const char *tag); > +void device_cleanup_tags(sd_device *device); > +void device_cleanup_devlinks(sd_device *device); > + > +uint64_t device_get_properties_generation(sd_device *device); > +uint64_t device_get_tags_generation(sd_device *device); > +uint64_t device_get_devlinks_generation(sd_device *device); > + > +int device_get_properties_nulstr(sd_device *device, const uint8_t **nulstr, > size_t *len); > +int device_get_properties_strv(sd_device *device, char ***strv); > + > +int device_rename(sd_device *device, const char *name); > +int device_shallow_clone(sd_device *old_device, sd_device **new_device); > +int device_clone_with_db(sd_device *old_device, sd_device **new_device); > +int device_copy_properties(sd_device *device_dst, sd_device *device_src); > +int device_new_from_synthetic_event(sd_device **new_device, const char > *syspath, const char *action); > + > +int device_tag_index(sd_device *dev, sd_device *dev_old, bool add); > +int device_update_db(sd_device *device); > +int device_delete_db(sd_device *device); > diff --git a/src/libsystemd/sd-device/device-util.h > b/src/libsystemd/sd-device/device-util.h > new file mode 100644 > index 0000000..bfbb328 > --- /dev/null > +++ b/src/libsystemd/sd-device/device-util.h > @@ -0,0 +1,48 @@ > +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ > + > +#pragma once > + > +/*** > + This file is part of systemd. > + > + Copyright 2014 Tom Gundersen <t...@jklm.no> > + > + 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 <http://www.gnu.org/licenses/>. > +***/ > + > +#include "util.h" > + > +DEFINE_TRIVIAL_CLEANUP_FUNC(sd_device*, sd_device_unref); > + > +#define _cleanup_device_unref_ _cleanup_(sd_device_unrefp) > + > +#define FOREACH_DEVICE_PROPERTY(device, key, value) \ > + for (key = sd_device_get_property_first(device, &(value)); \ > + key; \ > + key = sd_device_get_property_next(device, &(value))) > + > +#define FOREACH_DEVICE_TAG(device, tag) \ > + for (tag = sd_device_get_tag_first(device); \ > + tag; \ > + tag = sd_device_get_tag_next(device)) > + > +#define FOREACH_DEVICE_SYSATTR(device, attr) \ > + for (attr = sd_device_get_sysattr_first(device); \ > + attr; \ > + attr = sd_device_get_sysattr_next(device)) > + > +#define FOREACH_DEVICE_DEVLINK(device, devlink) \ > + for (devlink = sd_device_get_devlink_first(device); \ > + devlink; \ > + devlink = sd_device_get_devlink_next(device)) > diff --git a/src/libsystemd/sd-device/sd-device.c > b/src/libsystemd/sd-device/sd-device.c > new file mode 100644 > index 0000000..921adfd > --- /dev/null > +++ b/src/libsystemd/sd-device/sd-device.c > @@ -0,0 +1,1812 @@ > +/*** > + This file is part of systemd. > + > + Copyright 2008-2012 Kay Sievers <k...@vrfy.org> > + Copyright 2014 Tom Gundersen <t...@jklm.no> > + > + 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 <http://www.gnu.org/licenses/>. > +***/ > + > +#include <ctype.h> > +#include <sys/types.h> > +#include <net/if.h> > + > +#include "util.h" > +#include "macro.h" > +#include "path-util.h" > +#include "strxcpyx.h" > +#include "fileio.h" > +#include "hashmap.h" > +#include "set.h" > +#include "strv.h" > + > +#include "sd-device.h" > + > +#include "device-util.h" > +#include "device-private.h" > +#include "device-internal.h" > + > +int device_new_aux(sd_device **ret) { > + _cleanup_device_unref_ sd_device *device = NULL; > + > + assert(ret); > + > + device = new0(sd_device, 1); > + if (!device) > + return -ENOMEM; > + > + device->n_ref = 1; > + device->watch_handle = -1; > + > + *ret = device; > + device = NULL; > + > + return 0; > +} > + > +_public_ sd_device *sd_device_ref(sd_device *device) { > + if (device) > + assert_se(++ device->n_ref >= 2); > + > + return device; > +} > + > +_public_ sd_device *sd_device_unref(sd_device *device) { > + if (device && -- device->n_ref == 0) { > + sd_device_unref(device->parent); > + free(device->syspath); > + free(device->sysname); > + free(device->devtype); > + free(device->devname); > + free(device->subsystem); > + free(device->driver); > + free(device->id_filename); > + free(device->properties_strv); > + free(device->properties_nulstr); > + > + ordered_hashmap_free_free_free(device->properties); > + ordered_hashmap_free_free_free(device->properties_db); > + hashmap_free_free_free(device->sysattr_values); > + set_free_free(device->sysattrs); > + set_free_free(device->tags); > + set_free_free(device->devlinks); > + > + free(device); > + } > + > + return NULL; > +} > + > +int device_add_property_aux(sd_device *device, const char *_key, const char > *_value, bool db) { > + OrderedHashmap **properties; > + > + assert(device); > + assert(_key); > + > + if (db) > + properties = &device->properties_db; > + else > + properties = &device->properties; > + > + if (_value) { > + _cleanup_free_ char *key = NULL, *value = NULL, *old_key = > NULL, *old_value = NULL; > + int r; > + > + r = ordered_hashmap_ensure_allocated(properties, > &string_hash_ops); > + if (r < 0) > + return r; > + > + key = strdup(_key); > + if (!key) > + return -ENOMEM; > + > + value = strdup(_value); > + if (!value) > + return -ENOMEM; > + > + old_value = ordered_hashmap_get2(*properties, key, (void**) > &old_key); > + > + r = ordered_hashmap_replace(*properties, key, value); > + if (r < 0) > + return r; > + > + key = NULL; > + value = NULL; > + } else { > + _cleanup_free_ char *key = NULL; > + _cleanup_free_ char *value = NULL; > + > + value = ordered_hashmap_remove2(*properties, _key, (void**) > &key); > + } > + > + if (!db) { > + device->properties_generation ++; > + device->properties_buf_outdated = true; > + } > + > + return 0; > +} > + > +int device_add_property_internal(sd_device *device, const char *key, const > char *value) { > + return device_add_property_aux(device, key, value, false); > +} > + > +int device_set_syspath(sd_device *device, const char *_syspath, bool verify) > { > + _cleanup_free_ char *syspath = NULL; > + const char *devpath; > + int r; > + > + assert(device); > + assert(_syspath); > + > + /* must be a subdirectory of /sys */ > + if (!path_startswith(_syspath, "/sys/")) { > + log_debug("sd-device: syspath '%s' is not a subdirectory of > /sys", _syspath); > + return -EINVAL; > + } > + > + if (verify) { > + r = readlink_and_canonicalize(_syspath, &syspath); > + if (r == -EINVAL) { > + /* not a symlink */ > + syspath = canonicalize_file_name(_syspath); > + if (!syspath) { > + log_debug("sd-device: could not canonicalize > '%s': %m", _syspath); > + return -errno; > + } > + /* ignore errors due to the link not being a symlink */ > + } else if (r < 0 && r != -EINVAL) { > + log_debug("sd-device: could not get target of '%s': > %s", _syspath, strerror(-r)); > + return r; > + } > + > + if (path_startswith(syspath, "/sys/devices/")) { > + char *path; > + > + /* all 'devices' require an 'uevent' file */ > + path = strjoina(syspath, "/uevent"); > + r = access(path, F_OK); > + if (r < 0) { > + log_debug("sd-device: %s does not have an > uevent file: %m", syspath); > + return -errno; > + } > + } else { > + /* everything else just just needs to be a directory > */ > + if (!is_dir(syspath, false)) { > + log_debug("sd-device: %s is not a > directory", syspath); > + return -EINVAL; > + } > + } > + } else { > + syspath = strdup(_syspath); > + if (!syspath) > + return -ENOMEM; > + } > + > + devpath = syspath + strlen("/sys"); > + > + r = device_add_property_internal(device, "DEVPATH", devpath); > + if (r < 0) > + return r; > + > + free(device->syspath); > + device->syspath = syspath; > + syspath = NULL; > + > + device->devpath = devpath; > + > + return 0; > +} > + > +_public_ int sd_device_new_from_syspath(sd_device **ret, const char > *syspath) { > + _cleanup_device_unref_ sd_device *device = NULL; > + int r; > + > + assert_return(ret, -EINVAL); > + assert_return(syspath, -EINVAL); > + > + r = device_new_aux(&device); > + if (r < 0) > + return r; > + > + r = device_set_syspath(device, syspath, true); > + if (r < 0) > + return r; > + > + *ret = device; > + device = NULL; > + > + return 0; > +} > + > +_public_ int sd_device_new_from_devnum(sd_device **ret, char type, dev_t > devnum) { > + char *syspath; > + char id[DECIMAL_STR_MAX(unsigned) * 2 + 1]; > + > + assert_return(ret, -EINVAL); > + assert_return(type == 'b' || type == 'c', -EINVAL); > + > + /* use /sys/dev/{block,char}/<maj>:<min> link */ > + snprintf(id, sizeof(id), "%u:%u", major(devnum), minor(devnum)); > + > + syspath = strjoina("/sys/dev/", (type == 'b' ? "block" : "char"), > "/", id); > + > + return sd_device_new_from_syspath(ret, syspath); > +} > + > +_public_ int sd_device_new_from_subsystem_sysname(sd_device **ret, const > char *subsystem, const char *sysname) { > + char *syspath; > + > + assert_return(ret, -EINVAL); > + assert_return(subsystem, -EINVAL); > + assert_return(sysname, -EINVAL); > + > + if (streq(subsystem, "subsystem")) { > + syspath = strjoina("/sys/subsystem/", sysname); > + if (access(syspath, F_OK) >= 0) > + return sd_device_new_from_syspath(ret, syspath); > + > + syspath = strjoina("/sys/bus/", sysname); > + if (access(syspath, F_OK) >= 0) > + return sd_device_new_from_syspath(ret, syspath); > + > + syspath = strjoina("/sys/class/", sysname); > + if (access(syspath, F_OK) >= 0) > + return sd_device_new_from_syspath(ret, syspath); > + } else if (streq(subsystem, "module")) { > + syspath = strjoina("/sys/module/", sysname); > + if (access(syspath, F_OK) >= 0) > + return sd_device_new_from_syspath(ret, syspath); > + } else if (streq(subsystem, "drivers")) { > + char subsys[PATH_MAX]; > + char *driver; > + > + strscpy(subsys, sizeof(subsys), sysname); > + driver = strchr(subsys, ':'); > + if (driver) { > + driver[0] = '\0'; > + driver++; > + > + syspath = strjoina("/sys/subsystem/", subsys, > "/drivers/", driver); > + if (access(syspath, F_OK) >= 0) > + return sd_device_new_from_syspath(ret, > syspath); > + > + syspath = strjoina("/sys/bus/", subsys, "/drivers/", > driver); > + if (access(syspath, F_OK) >= 0) > + return sd_device_new_from_syspath(ret, > syspath); > + } else > + return -EINVAL; > + } else { > + syspath = strjoina("/sys/subsystem/", subsystem, > "/devices/", sysname); > + if (access(syspath, F_OK) >= 0) > + return sd_device_new_from_syspath(ret, syspath); > + > + syspath = strjoina("/sys/bus/", subsystem, "/devices/", > sysname); > + if (access(syspath, F_OK) >= 0) > + return sd_device_new_from_syspath(ret, syspath); > + > + syspath = strjoina("/sys/class/", subsystem, "/", sysname); > + if (access(syspath, F_OK) >= 0) > + return sd_device_new_from_syspath(ret, syspath); > + } > + > + return -ENOENT; > +} > + > +int device_set_devtype(sd_device *device, const char *_devtype) { > + _cleanup_free_ char *devtype = NULL; > + int r; > + > + assert(device); > + assert(_devtype); > + > + devtype = strdup(_devtype); > + if (!devtype) > + return -ENOMEM; > + > + r = device_add_property_internal(device, "DEVTYPE", devtype); > + if (r < 0) > + return r; > + > + free(device->devtype); > + device->devtype = devtype; > + devtype = NULL; > + > + return 0; > +} > + > +int device_set_ifindex(sd_device *device, const char *_ifindex) { > + int ifindex, r; > + > + assert(device); > + assert(_ifindex); > + > + r = safe_atoi(_ifindex, &ifindex); > + if (r < 0) > + return r; > + > + if (ifindex <= 0) > + return -EINVAL; > + > + r = device_add_property_internal(device, "IFINDEX", _ifindex); > + if (r < 0) > + return r; > + > + device->ifindex = ifindex; > + > + return 0; > +} > + > +int device_set_devname(sd_device *device, const char *_devname) { > + _cleanup_free_ char *devname = NULL; > + int r; > + > + assert(device); > + assert(_devname); > + > + if (_devname[0] != '/') { > + r = asprintf(&devname, "/dev/%s", _devname); > + if (r < 0) > + return -ENOMEM; > + } else { > + devname = strdup(_devname); > + if (!devname) > + return -ENOMEM; > + } > + > + r = device_add_property_internal(device, "DEVNAME", devname); > + if (r < 0) > + return r; > + > + free(device->devname); > + device->devname = devname; > + devname = NULL; > + > + return 0; > +} > + > +int device_set_devmode(sd_device *device, const char *_devmode) { > + unsigned devmode; > + int r; > + > + assert(device); > + assert(_devmode); > + > + r = safe_atou(_devmode, &devmode); > + if (r < 0) > + return r; > + > + if (devmode > 07777) > + return -EINVAL; > + > + r = device_add_property_internal(device, "DEVMODE", _devmode); > + if (r < 0) > + return r; > + > + device->devmode = devmode; > + > + return 0; > +} > + > +int device_set_devnum(sd_device *device, const char *major, const char > *minor) { > + unsigned maj = 0, min = 0; > + int r; > + > + assert(device); > + assert(major); > + > + r = safe_atou(major, &maj); > + if (r < 0) > + return r; > + if (!maj) > + return 0; > + > + if (minor) { > + r = safe_atou(minor, &min); > + if (r < 0) > + return r; > + } > + > + r = device_add_property_internal(device, "MAJOR", major); > + if (r < 0) > + return r; > + > + if (minor) { > + r = device_add_property_internal(device, "MINOR", minor); > + if (r < 0) > + return r; > + } > + > + device->devnum = makedev(maj, min); > + > + return 0; > +} > + > +static int handle_uevent_line(sd_device *device, const char *key, const char > *value, const char **major, const char **minor) { > + int r; > + > + assert(device); > + assert(key); > + assert(value); > + assert(major); > + assert(minor); > + > + if (streq(key, "DEVTYPE")) { > + r = device_set_devtype(device, value); > + if (r < 0) > + return r; > + } else if (streq(key, "IFINDEX")) { > + r = device_set_ifindex(device, value); > + if (r < 0) > + return r; > + } else if (streq(key, "DEVNAME")) { > + r = device_set_devname(device, value); > + if (r < 0) > + return r; > + } else if (streq(key, "DEVMODE")) { > + r = device_set_devmode(device, value); > + if (r < 0) > + return r; > + } else if (streq(key, "MAJOR")) > + *major = value; > + else if (streq(key, "MINOR")) > + *minor = value; > + else { > + r = device_add_property_internal(device, key, value); > + if (r < 0) > + return r; > + } > + > + return 0; > +} > + > +int device_read_uevent_file(sd_device *device) { > + _cleanup_free_ char *uevent = NULL; > + const char *syspath, *key, *value, *major = NULL, *minor = NULL; > + char *path; > + size_t uevent_len; > + unsigned i; > + int r; > + > + enum { > + PRE_KEY, > + KEY, > + PRE_VALUE, > + VALUE, > + INVALID_LINE, > + } state = PRE_KEY; > + > + assert(device); > + > + if (device->uevent_loaded || device->sealed) > + return 0; > + > + r = sd_device_get_syspath(device, &syspath); > + if (r < 0) > + return r; > + > + path = strjoina(syspath, "/uevent"); > + > + r = read_full_file(path, &uevent, &uevent_len); > + if (r < 0) { > + log_debug("sd-device: failed to read uevent file '%s': %s", > path, strerror(-r)); > + return r; > + } > + > + for (i = 0; i < uevent_len; i++) { > + switch (state) { > + case PRE_KEY: > + if (!strchr(NEWLINE, uevent[i])) { > + key = &uevent[i]; > + > + state = KEY; > + } > + > + break; > + case KEY: > + if (uevent[i] == '=') { > + uevent[i] = '\0'; > + > + state = PRE_VALUE; > + } else if (strchr(NEWLINE, uevent[i])) { > + uevent[i] = '\0'; > + log_debug("sd-device: ignoring invalid > uevent line '%s'", key); > + > + state = PRE_KEY; > + } > + > + break; > + case PRE_VALUE: > + value = &uevent[i]; > + > + state = VALUE; > + > + break; > + case VALUE: > + if (strchr(NEWLINE, uevent[i])) { > + uevent[i] = '\0'; > + > + r = handle_uevent_line(device, key, value, > &major, &minor); > + if (r < 0) > + log_debug("sd-device: failed to > handle uevent entry '%s=%s': %s", key, value, strerror(-r)); > + > + state = PRE_KEY; > + } > + > + break; > + default: > + assert_not_reached("invalid state when parsing > uevent file"); > + } > + } > + > + if (major) { > + r = device_set_devnum(device, major, minor); > + if (r < 0) > + log_debug("sd-device: could not set 'MAJOR=%s' or > 'MINOR=%s' from '%s': %s", major, minor, path, strerror(-r)); > + } > + > + device->uevent_loaded = true; > + > + return 0; > +} > + > +_public_ int sd_device_get_ifindex(sd_device *device, int *ifindex) { > + int r; > + > + assert_return(device, -EINVAL); > + assert_return(ifindex, -EINVAL); > + > + r = device_read_uevent_file(device); > + if (r < 0) > + return r; > + > + *ifindex = device->ifindex; > + > + return 0; > +} > + > +_public_ int sd_device_new_from_device_id(sd_device **ret, const char *id) { > + int r; > + > + assert_return(ret, -EINVAL); > + assert_return(id, -EINVAL); > + > + switch (id[0]) { > + case 'b': > + case 'c': > + { > + char type; > + int maj, min; > + > + r = sscanf(id, "%c%i:%i", &type, &maj, &min); > + if (r != 3) > + return -EINVAL; > + > + return sd_device_new_from_devnum(ret, type, makedev(maj, > min)); > + } > + case 'n': > + { > + _cleanup_device_unref_ sd_device *device = NULL; > + _cleanup_close_ int sk = -1; > + struct ifreq ifr = {}; > + int ifindex; > + > + r = safe_atoi(&id[1], &ifr.ifr_ifindex); > + if (r < 0) > + return r; > + else if (ifr.ifr_ifindex <= 0) > + return -EINVAL; > + > + sk = socket(PF_INET, SOCK_DGRAM, 0); > + if (sk < 0) > + return -errno; > + > + r = ioctl(sk, SIOCGIFNAME, &ifr); > + if (r < 0) > + return -errno; > + > + r = sd_device_new_from_subsystem_sysname(&device, "net", > ifr.ifr_name); > + if (r < 0) > + return r; > + > + r = sd_device_get_ifindex(device, &ifindex); > + if (r < 0) > + return r; > + > + /* this si racey, so we might end up with the wrong device */ > + if (ifr.ifr_ifindex != ifindex) > + return -ENODEV; > + > + *ret = device; > + device = NULL; > + > + return 0; > + } > + case '+': > + { > + char subsys[PATH_MAX]; > + char *sysname; > + > + (void)strscpy(subsys, sizeof(subsys), id + 1); > + sysname = strchr(subsys, ':'); > + if (!sysname) > + return -EINVAL; > + > + sysname[0] = '\0'; > + sysname ++; > + > + return sd_device_new_from_subsystem_sysname(ret, subsys, > sysname); > + } > + default: > + return -EINVAL; > + } > +} > + > +_public_ int sd_device_get_syspath(sd_device *device, const char **ret) { > + assert_return(device, -EINVAL); > + assert_return(ret, -EINVAL); > + > + assert(path_startswith(device->syspath, "/sys/")); > + > + *ret = device->syspath; > + > + return 0; > +} > + > +static int device_new_from_child(sd_device **ret, sd_device *child) { > + _cleanup_free_ char *path = NULL; > + const char *subdir, *syspath; > + int r; > + > + assert(ret); > + assert(child); > + > + r = sd_device_get_syspath(child, &syspath); > + if (r < 0) > + return r; > + > + path = strdup(syspath); > + if (!path) > + return -ENOMEM; > + subdir = path + strlen("/sys"); > + > + for (;;) { > + char *pos; > + > + pos = strrchr(subdir, '/'); > + if (!pos || pos < subdir + 2) > + break; > + > + *pos = '\0'; > + > + r = sd_device_new_from_syspath(ret, path); > + if (r < 0) > + continue; > + > + return 0; > + } > + > + return -ENOENT; > +} > + > +_public_ int sd_device_get_parent(sd_device *child, sd_device **ret) { > + > + assert_return(ret, -EINVAL); > + assert_return(child, -EINVAL); > + > + if (!child->parent_set) { > + child->parent_set = true; > + > + (void)device_new_from_child(&child->parent, child); > + } > + > + if (!child->parent) > + return -ENOENT; > + > + *ret = child->parent; > + > + return 0; > +} > + > +int device_set_subsystem(sd_device *device, const char *_subsystem) { > + _cleanup_free_ char *subsystem = NULL; > + int r; > + > + assert(device); > + assert(_subsystem); > + > + subsystem = strdup(_subsystem); > + if (!subsystem) > + return -ENOMEM; > + > + r = device_add_property_internal(device, "SUBSYSTEM", subsystem); > + if (r < 0) > + return r; > + > + free(device->subsystem); > + device->subsystem = subsystem; > + subsystem = NULL; > + > + device->subsystem_set = true; > + > + return 0; > +} > + > +_public_ int sd_device_get_subsystem(sd_device *device, const char **ret) { > + assert_return(ret, -EINVAL); > + assert_return(device, -EINVAL); > + > + if (!device->subsystem_set) { > + _cleanup_free_ char *subsystem = NULL; > + const char *syspath; > + char *path; > + int r; > + > + /* read 'subsystem' link */ > + r = sd_device_get_syspath(device, &syspath); > + if (r < 0) > + return r; > + > + path = strjoina(syspath, "/subsystem"); > + r = readlink_value(path, &subsystem); > + if (r >= 0) > + r = device_set_subsystem(device, subsystem); > + /* use implicit names */ > + else if (path_startswith(device->devpath, "/module/")) > + r = device_set_subsystem(device, "module"); > + else if (strstr(device->devpath, "/drivers/")) > + r = device_set_subsystem(device, "drivers"); > + else if (path_startswith(device->devpath, "/subsystem/") || > + path_startswith(device->devpath, "/class/") || > + path_startswith(device->devpath, "/buss/")) > + r = device_set_subsystem(device, "subsystem"); > + if (r < 0) > + return r; > + > + device->subsystem_set = true; > + } > + > + *ret = device->subsystem; > + > + return 0; > +} > + > +_public_ int sd_device_get_devtype(sd_device *device, const char **devtype) { > + int r; > + > + assert(devtype); > + assert(device); > + > + r = device_read_uevent_file(device); > + if (r < 0) > + return r; > + > + *devtype = device->devtype; > + > + return 0; > +} > + > +_public_ int sd_device_get_parent_with_subsystem_devtype(sd_device *child, > const char *subsystem, const char *devtype, sd_device **ret) { > + sd_device *parent = NULL; > + int r; > + > + assert_return(child, -EINVAL); > + assert_return(subsystem, -EINVAL); > + > + r = sd_device_get_parent(child, &parent); > + while (r >= 0) { > + const char *parent_subsystem; > + const char *parent_devtype; Initialize both to NULL. sd_device_get_subsystem or sd_device_get_devtype may fail and leave these as uninitialized pointers. > + > + (void)sd_device_get_subsystem(parent, &parent_subsystem); > + if (streq_ptr(parent_subsystem, subsystem)) { > + if (!devtype) > + break; > + > + (void)sd_device_get_devtype(parent, &parent_devtype); > + if (streq_ptr(parent_devtype, devtype)) > + break; > + } > + r = sd_device_get_parent(parent, &parent); > + } > + > + if (r < 0) > + return r; > + > + *ret = parent; > + > + return 0; > +} > + > +_public_ int sd_device_get_devnum(sd_device *device, dev_t *devnum) { > + int r; > + > + assert_return(device, -EINVAL); > + assert_return(devnum, -EINVAL); > + > + r = device_read_uevent_file(device); > + if (r < 0) > + return r; > + > + *devnum = device->devnum; > + > + return 0; > +} > + > +int device_set_driver(sd_device *device, const char *_driver) { > + char *driver; > + int r; > + > + assert(device); > + assert(_driver); > + > + driver = strdup(_driver); > + if (!driver) > + return -ENOMEM; > + > + r = device_add_property_internal(device, "DRIVER", driver); > + if (r < 0) > + return r; driver is leaked here > + > + free(device->driver); > + device->driver = driver; > + > + device->driver_set = true; > + > + return 0; > +} > + > +_public_ int sd_device_get_driver(sd_device *device, const char **ret) { > + assert_return(device, -EINVAL); > + assert_return(ret, -EINVAL); > + > + if (!device->driver_set) { > + _cleanup_free_ char *driver = NULL; > + const char *syspath; > + char *path; > + int r; > + > + r = sd_device_get_syspath(device, &syspath); > + if (r < 0) > + return r; > + > + path = strjoina(syspath, "/driver"); > + r = readlink_value(path, &driver); > + if (r >= 0) { > + r = device_set_driver(device, driver); > + if (r < 0) > + return r; > + } > + } > + > + *ret = device->driver; > + > + return 0; > +} > + > +_public_ int sd_device_get_devpath(sd_device *device, const char **devpath) { > + assert_return(device, -EINVAL); > + assert_return(devpath, -EINVAL); > + > + assert(device->devpath); > + assert(device->devpath[0] == '/'); > + > + *devpath = device->devpath; > + > + return 0; > +} > + > +_public_ int sd_device_get_devname(sd_device *device, const char **devname) { > + int r; > + > + assert_return(device, -EINVAL); > + assert_return(devname, -EINVAL); > + > + r = device_read_uevent_file(device); > + if (r < 0) > + return r; > + > + if (!device->devname) > + return -ENOENT; > + > + assert(path_startswith(device->devname, "/dev/")); > + > + *devname = device->devname; > + > + return 0; > +} > + > +static int device_set_sysname(sd_device *device) { > + _cleanup_free_ char *sysname = NULL; > + const char *sysnum; > + const char *pos; > + size_t len = 0; > + > + pos = strrchr(device->devpath, '/'); > + if (!pos) > + return -EINVAL; > + pos ++; > + > + /* devpath is not a root directory */ > + if (*pos == '\0' || pos <= device->devpath) > + return -EINVAL; > + > + sysname = strdup(pos); > + if (!sysname) > + return -ENOMEM; > + > + /* some devices have '!' in their name, change that to '/' */ > + while (sysname[len] != '\0') { > + if (sysname[len] == '!') > + sysname[len] = '/'; > + > + len ++; > + } > + > + /* trailing number */ > + while (len > 0 && isdigit(sysname[--len])) > + sysnum = &sysname[len]; > + > + if (len == 0) > + sysnum = NULL; > + > + free(device->sysname); > + device->sysname = sysname; > + sysname = NULL; > + > + device->sysnum = sysnum; > + > + device->sysname_set = true; > + > + return 0; > +} > + > +_public_ int sd_device_get_sysname(sd_device *device, const char **ret) { > + int r; > + > + assert_return(device, -EINVAL); > + assert_return(ret, -EINVAL); > + > + if (!device->sysname_set) { > + r = device_set_sysname(device); > + if (r < 0) > + return r; > + } > + > + *ret = device->sysname; > + > + return 0; > +} > + > +_public_ int sd_device_get_sysnum(sd_device *device, const char **ret) { > + int r; > + > + assert_return(device, -EINVAL); > + assert_return(ret, -EINVAL); > + > + if (!device->sysname_set) { > + r = device_set_sysname(device); > + if (r < 0) > + return r; > + } > + > + *ret = device->sysnum; > + > + return 0; > +} > + > +static bool is_valid_tag(const char *tag) { > + assert(tag); > + > + return !strchr(tag, ':') && !strchr(tag, ' '); > +} > + > +int device_add_tag(sd_device *device, const char *tag) { > + int r; > + > + assert(device); > + assert(tag); > + > + if (!is_valid_tag(tag)) > + return -EINVAL; > + > + r = set_ensure_allocated(&device->tags, &string_hash_ops); > + if (r < 0) > + return r; > + > + r = set_put_strdup(device->tags, tag); > + if (r < 0) > + return r; > + > + device->tags_generation ++; > + device->property_tags_outdated = true; > + > + return 0; > +} > + > +int device_add_devlink(sd_device *device, const char *devlink) { > + int r; > + > + assert(device); > + assert(devlink); > + > + r = set_ensure_allocated(&device->devlinks, &string_hash_ops); > + if (r < 0) > + return r; > + > + r = set_put_strdup(device->devlinks, devlink); > + if (r < 0) > + return r; > + > + device->devlinks_generation ++; > + device->property_devlinks_outdated = true; > + > + return 0; > +} > + > +static int device_add_property_internal_from_string(sd_device *device, const > char *str) { > + _cleanup_free_ char *key = NULL; > + char *value; > + > + assert(device); > + assert(str); > + > + key = strdup(str); > + if (!key) > + return -ENOMEM; > + > + value = strchr(key, '='); > + if (!value) > + return -EINVAL; > + > + *value = '\0'; > + > + if (isempty(++value)) > + value = NULL; > + > + return device_add_property_internal(device, key, value); > +} > + > +int device_set_usec_initialized(sd_device *device, const char *initialized) { > + uint64_t usec_initialized; > + int r; > + > + assert(device); > + assert(initialized); > + > + r = safe_atou64(initialized, &usec_initialized); > + if (r < 0) > + return r; > + > + r = device_add_property_internal(device, "USEC_INITIALIZED", > initialized); > + if (r < 0) > + return r; > + > + device->usec_initialized = usec_initialized; > + > + return 0; > +} > + > +static int handle_db_line(sd_device *device, char key, const char *value) { > + char *path; > + int r; > + > + assert(device); > + assert(value); > + > + switch (key) { > + case 'G': > + r = device_add_tag(device, value); > + if (r < 0) > + return r; > + > + break; > + case 'S': > + path = strjoina("/dev/", value); > + r = device_add_devlink(device, path); > + if (r < 0) > + return r; > + > + break; > + case 'E': > + r = device_add_property_internal_from_string(device, value); > + if (r < 0) > + return r; > + > + break; > + case 'I': > + r = device_set_usec_initialized(device, value); > + if (r < 0) > + return r; > + > + break; > + case 'L': > + r = safe_atoi(value, &device->devlink_priority); > + if (r < 0) > + return r; > + > + break; > + case 'W': > + r = safe_atoi(value, &device->watch_handle); > + if (r < 0) > + return r; > + > + break; > + default: > + log_debug("device db: unknown key '%c'", key); > + } > + > + return 0; > +} > + > +int device_get_id_filename(sd_device *device, const char **ret) { > + assert(device); > + assert(ret); > + > + if (!device->id_filename) { > + _cleanup_free_ char *id = NULL; > + const char *subsystem; > + dev_t devnum; > + int ifindex, r; > + > + r = sd_device_get_subsystem(device, &subsystem); > + if (r < 0) > + return r; > + > + r = sd_device_get_devnum(device, &devnum); > + if (r < 0) > + return r; > + > + r = sd_device_get_ifindex(device, &ifindex); > + if (r < 0) > + return r; > + > + if (major(devnum) > 0) { > + /* use dev_t -- b259:131072, c254:0 */ > + r = asprintf(&id, "%c%u:%u", > + streq(subsystem, "block") ? 'b' : 'c', > + major(devnum), minor(devnum)); > + if (r < 0) > + return -errno; > + } else if (ifindex > 0) { > + /* use netdev ifindex -- n3 */ > + r = asprintf(&id, "n%u", ifindex); > + if (r < 0) > + return -errno; > + } else { > + /* use $subsys:$sysname -- pci:0000:00:1f.2 > + * sysname() has '!' translated, get it from devpath > + */ > + const char *sysname; > + > + sysname = basename(device->devpath); > + if (!sysname) > + return -EINVAL; > + > + r = asprintf(&id, "+%s:%s", subsystem, sysname); > + if (r < 0) > + return -errno; > + } > + > + device->id_filename = id; > + id = NULL; > + } > + > + *ret = device->id_filename; > + > + return 0; > +} > + > +static int device_read_db(sd_device *device) { > + _cleanup_free_ char *db = NULL; > + char *path; > + const char *id, *value; > + char key; > + size_t db_len; > + unsigned i; > + int r; > + > + enum { > + PRE_KEY, > + KEY, > + PRE_VALUE, > + VALUE, > + INVALID_LINE, > + } state = PRE_KEY; > + > + if (device->db_loaded || device->sealed) > + return 0; > + > + r = device_get_id_filename(device, &id); > + if (r < 0) > + return r; > + > + path = strjoina("/run/udev/data/", id); > + > + r = read_full_file(path, &db, &db_len); > + if (r < 0) { > + if (r == -ENOENT) > + return 0; > + else { > + log_debug("sd-device: failed to read db '%s': %s", > path, strerror(-r)); > + return r; > + } > + } > + > + /* devices with a database entry are initialized */ > + device->is_initialized = true;; > + > + for (i = 0; i < db_len; i++) { > + switch (state) { > + case PRE_KEY: > + if (!strchr(NEWLINE, db[i])) { > + key = db[i]; > + > + state = KEY; > + } > + > + break; > + case KEY: > + if (db[i] != ':') { > + log_debug("sd-device: ignoring invalid db > entry with key '%c'", key); > + > + state = INVALID_LINE; > + } else { > + db[i] = '\0'; > + > + state = PRE_VALUE; > + } > + > + break; > + case PRE_VALUE: > + value = &db[i]; > + > + state = VALUE; > + > + break; > + case INVALID_LINE: > + if (strchr(NEWLINE, db[i])) > + state = PRE_KEY; > + > + break; > + case VALUE: > + if (strchr(NEWLINE, db[i])) { > + db[i] = '\0'; > + r = handle_db_line(device, key, value); > + if (r < 0) > + log_debug("sd-device: failed to > handle db entry '%c:%s': %s", key, value, strerror(-r)); > + > + state = PRE_KEY; > + } > + > + break; > + default: > + assert_not_reached("invalid state when parsing db"); > + } > + } > + > + device->db_loaded = true; > + > + return 0; > +} > + > +_public_ int sd_device_get_is_initialized(sd_device *device, int > *initialized) { > + int r; > + > + assert_return(device, -EINVAL); > + assert_return(initialized, -EINVAL); > + > + r = device_read_db(device); > + if (r < 0) > + return r; > + > + *initialized = device->is_initialized; > + > + return 0; > +} > + > +_public_ int sd_device_get_usec_since_initialized(sd_device *device, > uint64_t *usec) { > + usec_t now_ts; > + int r; > + > + assert_return(device, -EINVAL); > + assert_return(usec, -EINVAL); > + > + r = device_read_db(device); > + if (r < 0) > + return r; > + > + if (!device->is_initialized) > + return -EBUSY; > + > + if (!device->usec_initialized) > + return -ENODATA; > + > + now_ts = now(clock_boottime_or_monotonic()); > + > + if (now_ts < device->usec_initialized) > + return -EIO; > + > + *usec = now_ts - device->usec_initialized; > + > + return 0; > +} > + > +_public_ const char *sd_device_get_tag_first(sd_device *device) { > + assert_return(device, NULL); > + > + (void) device_read_db(device); > + > + device->tags_iterator_generation = device->tags_generation; > + device->tags_iterator = ITERATOR_FIRST; > + > + return set_iterate(device->tags, &device->tags_iterator); > +} > + > +_public_ const char *sd_device_get_tag_next(sd_device *device) { > + assert_return(device, NULL); > + > + (void) device_read_db(device); > + > + if (device->tags_iterator_generation != device->tags_generation) > + return NULL; > + > + return set_iterate(device->tags, &device->tags_iterator); > +} > + > +_public_ const char *sd_device_get_devlink_first(sd_device *device) { > + assert_return(device, NULL); > + > + (void) device_read_db(device); > + > + device->devlinks_iterator_generation = device->devlinks_generation; > + device->devlinks_iterator = ITERATOR_FIRST; > + > + return set_iterate(device->devlinks, &device->devlinks_iterator); > +} > + > +_public_ const char *sd_device_get_devlink_next(sd_device *device) { > + assert_return(device, NULL); > + > + (void) device_read_db(device); > + > + if (device->devlinks_iterator_generation != > device->devlinks_generation) > + return NULL; > + > + return set_iterate(device->devlinks, &device->devlinks_iterator); > +} > + > +static int device_properties_prepare(sd_device *device) { > + int r; > + > + assert(device); > + > + r = device_read_uevent_file(device); > + if (r < 0) > + return r; > + > + r = device_read_db(device); > + if (r < 0) > + return r; > + > + if (device->property_devlinks_outdated) { > + char *devlinks = NULL; > + const char *devlink; > + > + devlink = sd_device_get_devlink_first(device); > + if (devlink) > + devlinks = strdupa(devlink); > + > + while ((devlink = sd_device_get_devlink_next(device))) > + devlinks = strjoina(devlinks, " ", devlink); > + > + r = device_add_property_internal(device, "DEVLINKS", > devlinks); > + if (r < 0) > + return r; > + > + device->property_devlinks_outdated = false; > + } > + > + if (device->property_tags_outdated) { > + char *tags = NULL; > + const char *tag; > + > + tag = sd_device_get_tag_first(device); > + if (tag) > + tags = strjoina(":", tag); > + > + while ((tag = sd_device_get_tag_next(device))) > + tags = strjoina(tags, ":", tag); > + > + tags = strjoina(tags, ":"); > + > + r = device_add_property_internal(device, "TAGS", tags); > + if (r < 0) > + return r; > + > + device->property_tags_outdated = false; > + } > + > + return 0; > +} > + > +_public_ const char *sd_device_get_property_first(sd_device *device, const > char **_value) { > + const char *key; > + const char *value; > + int r; > + > + assert_return(device, NULL); > + > + r = device_properties_prepare(device); > + if (r < 0) > + return NULL; > + > + device->properties_iterator_generation = > device->properties_generation; > + device->properties_iterator = ITERATOR_FIRST; > + > + value = ordered_hashmap_iterate(device->properties, > &device->properties_iterator, (const void**)&key); > + > + if (_value) > + *_value = value; > + > + return key; > +} > + > +_public_ const char *sd_device_get_property_next(sd_device *device, const > char **_value) { > + const char *key; > + const char *value; > + int r; > + > + assert_return(device, NULL); > + > + r = device_properties_prepare(device); > + if (r < 0) > + return NULL; > + > + if (device->properties_iterator_generation != > device->properties_generation) > + return NULL; > + > + value = ordered_hashmap_iterate(device->properties, > &device->properties_iterator, (const void**)&key); > + > + if (_value) > + *_value = value; > + > + return key; > +} > + > +static int device_sysattrs_read_all(sd_device *device) { > + _cleanup_closedir_ DIR *dir = NULL; > + const char *syspath; > + struct dirent *dent; > + int r; > + > + assert(device); > + > + if (device->sysattrs_read) > + return 0; > + > + r = sd_device_get_syspath(device, &syspath); > + if (r < 0) > + return r; > + > + dir = opendir(syspath); > + if (!dir) > + return -errno; > + > + r = set_ensure_allocated(&device->sysattrs, &string_hash_ops); > + if (r < 0) > + return r; > + > + for (dent = readdir(dir); dent != NULL; dent = readdir(dir)) { > + _cleanup_free_ char *sysattr = NULL; Unused variable > + char *path; > + struct stat statbuf; > + > + /* only handle symlinks and regular files */ > + if (dent->d_type != DT_LNK && dent->d_type != DT_REG) > + continue; > + > + path = strjoina(syspath, "/", dent->d_name); > + > + if (lstat(path, &statbuf) != 0) > + continue; > + > + if (!(statbuf.st_mode & S_IRUSR)) > + continue; > + > + r = set_put_strdup(device->sysattrs, dent->d_name); > + if (r < 0) > + return r; > + } > + > + device->sysattrs_read = true; > + > + return 0; > +} > + > +_public_ const char *sd_device_get_sysattr_first(sd_device *device) { > + int r; > + > + assert_return(device, NULL); > + > + if (!device->sysattrs_read) { > + r = device_sysattrs_read_all(device); > + if (r < 0) { > + errno = -r; > + return NULL; > + } > + } > + > + device->sysattrs_iterator = ITERATOR_FIRST; > + > + return set_iterate(device->sysattrs, &device->sysattrs_iterator); > +} > + > +_public_ const char *sd_device_get_sysattr_next(sd_device *device) { > + assert_return(device, NULL); > + > + if (!device->sysattrs_read) > + return NULL; > + > + return set_iterate(device->sysattrs, &device->sysattrs_iterator); > +} > + > +_public_ int sd_device_has_tag(sd_device *device, const char *tag) { > + assert_return(device, -EINVAL); > + assert_return(tag, -EINVAL); > + > + (void) device_read_db(device); > + > + return !!set_contains(device->tags, tag); > +} > + > +_public_ int sd_device_get_property_value(sd_device *device, const char > *key, const char **_value) { > + char *value; > + int r; > + > + assert_return(device, -EINVAL); > + assert_return(key, -EINVAL); > + assert_return(_value, -EINVAL); > + > + r = device_properties_prepare(device); > + if (r < 0) > + return r; > + > + value = ordered_hashmap_get(device->properties, key); > + if (!value) > + return -ENOENT; > + > + *_value = value; > + > + return 0; > +} > + > +/* replaces the value if it already exists */ > +static int device_add_sysattr_value(sd_device *device, const char *_key, > const char *_value) { > + _cleanup_free_ char *key = NULL; > + _cleanup_free_ char *value = NULL; > + int r; > + > + assert(device); > + assert(_key); > + > + r = hashmap_ensure_allocated(&device->sysattr_values, > &string_hash_ops); > + if (r < 0) > + return r; > + > + value = hashmap_remove2(device->sysattr_values, _key, (void **)&key); > + if (!key) { > + key = strdup(_key); > + if (!key) > + return -ENOMEM; > + } > + > + free(value); > + value = NULL; > + > + if (_value) { > + value = strdup(_value); > + if (!value) > + return -ENOMEM; > + } > + > + r = hashmap_put(device->sysattr_values, key, value); > + if (r < 0) > + return r; > + > + key = NULL; > + value = NULL; > + > + return 0; > +} > + > +static int device_get_sysattr_value(sd_device *device, const char *_key, > const char **_value) { > + const char *key = NULL, *value; > + > + assert(device); > + assert(_key); > + > + value = hashmap_get2(device->sysattr_values, _key, (void **) &key); > + if (!key) > + return -ENOENT; > + > + if (_value) > + *_value = value; > + > + return 0; > +} > + > +/* We cache all sysattr lookups. If an attribute does not exist, it is stored > + * with a NULL value in the cache, otherwise the returned string is stored */ > +_public_ int sd_device_get_sysattr_value(sd_device *device, const char > *sysattr, const char **_value) { > + _cleanup_free_ char *value = NULL; > + const char *syspath, *cached_value = NULL; > + char *path; > + struct stat statbuf; > + int r; > + > + assert_return(device, -EINVAL); > + assert_return(sysattr, -EINVAL); > + > + /* look for possibly already cached result */ > + r = device_get_sysattr_value(device, sysattr, &cached_value); > + if (r != -ENOENT) { > + if (r < 0) > + return r; > + > + if (!cached_value) > + /* we looked up the sysattr before and it did not > exist */ > + return -ENOENT; > + > + if (_value) > + *_value = cached_value; > + > + return 0; > + } > + > + r = sd_device_get_syspath(device, &syspath); > + if (r < 0) > + return r; > + > + path = strjoina(syspath, "/", sysattr); > + r = lstat(path, &statbuf); > + if (r < 0) { > + /* remember that we could not access the sysattr */ > + r = device_add_sysattr_value(device, sysattr, NULL); > + if (r < 0) > + return r; > + > + return -ENOENT; > + } else if (S_ISLNK(statbuf.st_mode)) { > + /* Some core links return only the last element of the > target path, > + * these are just values, the paths should not be exposed. */ > + if (STR_IN_SET(sysattr, "driver", "subsystem", "module")) { > + r = readlink_value(path, &value); > + if (r < 0) > + return r; > + } else > + return -EINVAL; > + } else if (S_ISDIR(statbuf.st_mode)) { > + /* skip directories */ > + return -EINVAL; > + } else if (!(statbuf.st_mode & S_IRUSR)) { > + /* skip non-readable files */ > + return -EPERM; > + } else { > + size_t size; > + > + /* read attribute value */ > + r = read_full_file(path, &value, &size); > + if (r < 0) > + return r; > + > + /* drop trailing newlines */ > + while (size > 0 && value[--size] == '\n') > + value[size] = '\0'; > + } > + > + r = device_add_sysattr_value(device, sysattr, value); > + if (r < 0) > + return r; > + > + *_value = value; > + value = NULL; > + > + return 0; > +} > + > +static void device_remove_sysattr_value(sd_device *device, const char *_key) > { > + _cleanup_free_ char *key = NULL; > + _cleanup_free_ char *value = NULL; > + > + assert(device); > + assert(_key); > + > + value = hashmap_remove2(device->sysattr_values, _key, (void **) > &key); > + > + return; > +} > + > +/* set the attribute and save it in the cache. If a NULL value is passed the > + * attribute is cleared from the cache */ > +_public_ int sd_device_set_sysattr_value(sd_device *device, const char > *sysattr, char *value) { > + _cleanup_close_ int fd = -1; > + const char *syspath; > + char *path; > + struct stat statbuf; > + size_t value_len = 0; > + ssize_t size; > + int r; > + > + assert_return(device, -EINVAL); > + assert_return(sysattr, -EINVAL); > + > + if (!value) { > + device_remove_sysattr_value(device, sysattr); > + > + return 0; > + } > + > + r = sd_device_get_syspath(device, &syspath); > + if (r < 0) > + return r; > + > + path = strjoina(syspath, "/", sysattr); > + r = lstat(path, &statbuf); > + if (r < 0) { > + r = device_add_sysattr_value(device, sysattr, ""); > + if (r < 0) > + return r; > + > + return -ENXIO; > + } > + > + if (S_ISLNK(statbuf.st_mode)) > + return -EINVAL; > + > + /* skip directories */ > + if (S_ISDIR(statbuf.st_mode)) > + return -EISDIR; > + > + /* skip non-readable files */ > + if ((statbuf.st_mode & S_IRUSR) == 0) > + return -EACCES; > + > + value_len = strlen(value); > + > + /* drop trailing newlines */ > + while (value_len > 0 && value[--value_len] == '\n') > + value[value_len] = '\0'; > + > + /* value length is limited to 4k */ > + if (value_len > 4096) > + return -EINVAL; > + > + fd = open(path, O_WRONLY | O_CLOEXEC); > + if (fd < 0) > + return -errno; > + > + size = write(fd, value, value_len); > + if (size < 0) > + return -errno; > + > + if ((size_t)size != value_len) > + return -EIO; > + > + r = device_add_sysattr_value(device, sysattr, value); > + if (r < 0) > + return r; > + > + return 0; > +} > diff --git a/src/systemd/sd-device.h b/src/systemd/sd-device.h > new file mode 100644 > index 0000000..d737753 > --- /dev/null > +++ b/src/systemd/sd-device.h > @@ -0,0 +1,77 @@ > +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ > + > +#ifndef foosddevicehfoo > +#define foosddevicehfoo > + > +/*** > + This file is part of systemd. > + > + Copyright 2008-2012 Kay Sievers <k...@vrfy.org> > + Copyright 2014 Tom Gundersen <t...@jklm.no> > + > + 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 <http://www.gnu.org/licenses/>. > +***/ > + > +#include <sys/types.h> > +#include <stdint.h> > + > +#include "_sd-common.h" > + > +_SD_BEGIN_DECLARATIONS; > + > +typedef struct sd_device sd_device; > + > +sd_device *sd_device_ref(sd_device *device); > +sd_device *sd_device_unref(sd_device *device); > + > +int sd_device_new_from_syspath(sd_device **ret, const char *syspath); > +int sd_device_new_from_devnum(sd_device **ret, char type, dev_t devnum); > +int sd_device_new_from_subsystem_sysname(sd_device **ret, const char > *subsystem, const char *sysname); > +int sd_device_new_from_device_id(sd_device **ret, const char *id); > + > +int sd_device_get_parent(sd_device *child, sd_device **ret); > +int sd_device_get_parent_with_subsystem_devtype(sd_device *child, const char > *subsystem, const char *devtype, sd_device **ret); > + > +int sd_device_get_syspath(sd_device *device, const char **ret); > +int sd_device_get_subsystem(sd_device *device, const char **ret); > +int sd_device_get_devtype(sd_device *device, const char **ret); > +int sd_device_get_devnum(sd_device *device, dev_t *devnum); > +int sd_device_get_ifindex(sd_device *device, int *ifindex); > +int sd_device_get_driver(sd_device *device, const char **ret); > +int sd_device_get_devpath(sd_device *device, const char **ret); > +int sd_device_get_devname(sd_device *device, const char **ret); > +int sd_device_get_sysname(sd_device *device, const char **ret); > +int sd_device_get_sysnum(sd_device *device, const char **ret); > + > +int sd_device_get_is_initialized(sd_device *device, int *initialized); > +int sd_device_get_usec_since_initialized(sd_device *device, uint64_t *usec); > + > +const char *sd_device_get_tag_first(sd_device *device); > +const char *sd_device_get_tag_next(sd_device *device); > +const char *sd_device_get_devlink_first(sd_device *device); > +const char *sd_device_get_devlink_next(sd_device *device); > +const char *sd_device_get_property_first(sd_device *device, const char > **value); > +const char *sd_device_get_property_next(sd_device *device, const char > **value); > +const char *sd_device_get_sysattr_first(sd_device *device); > +const char *sd_device_get_sysattr_next(sd_device *device); > + > +int sd_device_has_tag(sd_device *device, const char *tag); > +int sd_device_get_property_value(sd_device *device, const char *key, const > char **value); > +int sd_device_get_sysattr_value(sd_device *device, const char *sysattr, > const char **_value); > + > +int sd_device_set_sysattr_value(sd_device *device, const char *sysattr, char > *value); > + > +_SD_END_DECLARATIONS; > + > +#endif > -- > 2.3.4 > > _______________________________________________ > systemd-devel mailing list > systemd-devel@lists.freedesktop.org > http://lists.freedesktop.org/mailman/listinfo/systemd-devel _______________________________________________ systemd-devel mailing list systemd-devel@lists.freedesktop.org http://lists.freedesktop.org/mailman/listinfo/systemd-devel