sd-device.c revision afcac065c0f649ebcf0f450475a8d7c3bc776d14
d4205751d4643c272059a3728045929dd0e5e800Lennart Poettering This file is part of systemd.
d4205751d4643c272059a3728045929dd0e5e800Lennart Poettering Copyright 2008-2012 Kay Sievers <kay@vrfy.org>
d4205751d4643c272059a3728045929dd0e5e800Lennart Poettering Copyright 2014 Tom Gundersen <teg@jklm.no>
d4205751d4643c272059a3728045929dd0e5e800Lennart Poettering systemd is free software; you can redistribute it and/or modify it
d4205751d4643c272059a3728045929dd0e5e800Lennart Poettering under the terms of the GNU Lesser General Public License as published by
d4205751d4643c272059a3728045929dd0e5e800Lennart Poettering the Free Software Foundation; either version 2.1 of the License, or
d4205751d4643c272059a3728045929dd0e5e800Lennart Poettering (at your option) any later version.
d4205751d4643c272059a3728045929dd0e5e800Lennart Poettering systemd is distributed in the hope that it will be useful, but
d4205751d4643c272059a3728045929dd0e5e800Lennart Poettering WITHOUT ANY WARRANTY; without even the implied warranty of
d4205751d4643c272059a3728045929dd0e5e800Lennart Poettering MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
d4205751d4643c272059a3728045929dd0e5e800Lennart Poettering Lesser General Public License for more details.
d4205751d4643c272059a3728045929dd0e5e800Lennart Poettering You should have received a copy of the GNU Lesser General Public License
d4205751d4643c272059a3728045929dd0e5e800Lennart Poettering along with systemd; If not, see <http://www.gnu.org/licenses/>.
d4205751d4643c272059a3728045929dd0e5e800Lennart Poettering _cleanup_device_unref_ sd_device *device = NULL;
83f6936a018b08880670838756e0f4e9ea98b4a7Lennart Poettering_public_ sd_device *sd_device_ref(sd_device *device) {
d4205751d4643c272059a3728045929dd0e5e800Lennart Poettering_public_ sd_device *sd_device_unref(sd_device *device) {
9bf3b53533cdc9b95c921b71da755401f223f765Lennart Poettering ordered_hashmap_free_free_free(device->properties);
9bf3b53533cdc9b95c921b71da755401f223f765Lennart Poettering ordered_hashmap_free_free_free(device->properties_db);
9bf3b53533cdc9b95c921b71da755401f223f765Lennart Poettering hashmap_free_free_free(device->sysattr_values);
d4205751d4643c272059a3728045929dd0e5e800Lennart Poetteringint device_add_property_aux(sd_device *device, const char *_key, const char *_value, bool db) {
d4205751d4643c272059a3728045929dd0e5e800Lennart Poettering _cleanup_free_ char *key = NULL, *value = NULL, *old_key = NULL, *old_value = NULL;
d4205751d4643c272059a3728045929dd0e5e800Lennart Poettering r = ordered_hashmap_ensure_allocated(properties, &string_hash_ops);
d4205751d4643c272059a3728045929dd0e5e800Lennart Poettering old_value = ordered_hashmap_get2(*properties, key, (void**) &old_key);
d4205751d4643c272059a3728045929dd0e5e800Lennart Poettering r = ordered_hashmap_replace(*properties, key, value);
18cd5fe99f70a55a2d6f2303d6ee0624942695b1Zbigniew Jędrzejewski-Szmek value = ordered_hashmap_remove2(*properties, _key, (void**) &key);
c7332b0844e28d9b70c3c763b929f105c1056fe8Zbigniew Jędrzejewski-Szmekint device_add_property_internal(sd_device *device, const char *key, const char *value) {
c7332b0844e28d9b70c3c763b929f105c1056fe8Zbigniew Jędrzejewski-Szmek return device_add_property_aux(device, key, value, false);
c7332b0844e28d9b70c3c763b929f105c1056fe8Zbigniew Jędrzejewski-Szmekint device_set_syspath(sd_device *device, const char *_syspath, bool verify) {
c7332b0844e28d9b70c3c763b929f105c1056fe8Zbigniew Jędrzejewski-Szmek _cleanup_free_ char *syspath = NULL;
c7332b0844e28d9b70c3c763b929f105c1056fe8Zbigniew Jędrzejewski-Szmek /* must be a subdirectory of /sys */
c7332b0844e28d9b70c3c763b929f105c1056fe8Zbigniew Jędrzejewski-Szmek if (!path_startswith(_syspath, "/sys/")) {
c7332b0844e28d9b70c3c763b929f105c1056fe8Zbigniew Jędrzejewski-Szmek log_debug("sd-device: syspath '%s' is not a subdirectory of /sys", _syspath);
c7332b0844e28d9b70c3c763b929f105c1056fe8Zbigniew Jędrzejewski-Szmek r = readlink_and_canonicalize(_syspath, &syspath);
d4205751d4643c272059a3728045929dd0e5e800Lennart Poettering /* not a symlink */
d4205751d4643c272059a3728045929dd0e5e800Lennart Poettering syspath = canonicalize_file_name(_syspath);
d4205751d4643c272059a3728045929dd0e5e800Lennart Poettering log_debug("sd-device: could not canonicalize '%s': %m", _syspath);
d4205751d4643c272059a3728045929dd0e5e800Lennart Poettering /* ignore errors due to the link not being a symlink */
d4205751d4643c272059a3728045929dd0e5e800Lennart Poettering } else if (r < 0 && r != -EINVAL) {
d4205751d4643c272059a3728045929dd0e5e800Lennart Poettering log_debug("sd-device: could not get target of '%s': %s", _syspath, strerror(-r));
d4205751d4643c272059a3728045929dd0e5e800Lennart Poettering if (path_startswith(syspath, "/sys/devices/")) {
d4205751d4643c272059a3728045929dd0e5e800Lennart Poettering /* all 'devices' require an 'uevent' file */
c7332b0844e28d9b70c3c763b929f105c1056fe8Zbigniew Jędrzejewski-Szmek log_debug("sd-device: %s does not have an uevent file: %m", syspath);
c7332b0844e28d9b70c3c763b929f105c1056fe8Zbigniew Jędrzejewski-Szmek /* everything else just just needs to be a directory */
d4205751d4643c272059a3728045929dd0e5e800Lennart Poettering log_debug("sd-device: %s is not a directory", syspath);
d4205751d4643c272059a3728045929dd0e5e800Lennart Poettering r = device_add_property_internal(device, "DEVPATH", devpath);
d4205751d4643c272059a3728045929dd0e5e800Lennart Poettering_public_ int sd_device_new_from_syspath(sd_device **ret, const char *syspath) {
d4205751d4643c272059a3728045929dd0e5e800Lennart Poettering _cleanup_device_unref_ sd_device *device = NULL;
18cd5fe99f70a55a2d6f2303d6ee0624942695b1Zbigniew Jędrzejewski-Szmek r = device_set_syspath(device, syspath, true);
c7332b0844e28d9b70c3c763b929f105c1056fe8Zbigniew Jędrzejewski-Szmek_public_ int sd_device_new_from_devnum(sd_device **ret, char type, dev_t devnum) {
d4205751d4643c272059a3728045929dd0e5e800Lennart Poettering char id[DECIMAL_STR_MAX(unsigned) * 2 + 1];
d4205751d4643c272059a3728045929dd0e5e800Lennart Poettering assert_return(type == 'b' || type == 'c', -EINVAL);
d4205751d4643c272059a3728045929dd0e5e800Lennart Poettering /* use /sys/dev/{block,char}/<maj>:<min> link */
d4205751d4643c272059a3728045929dd0e5e800Lennart Poettering snprintf(id, sizeof(id), "%u:%u", major(devnum), minor(devnum));
d4205751d4643c272059a3728045929dd0e5e800Lennart Poettering syspath = strjoina("/sys/dev/", (type == 'b' ? "block" : "char"), "/", id);
c7332b0844e28d9b70c3c763b929f105c1056fe8Zbigniew Jędrzejewski-Szmek return sd_device_new_from_syspath(ret, syspath);
d4205751d4643c272059a3728045929dd0e5e800Lennart Poettering_public_ int sd_device_new_from_subsystem_sysname(sd_device **ret, const char *subsystem, const char *sysname) {
c7332b0844e28d9b70c3c763b929f105c1056fe8Zbigniew Jędrzejewski-Szmek assert_return(subsystem, -EINVAL);
c7332b0844e28d9b70c3c763b929f105c1056fe8Zbigniew Jędrzejewski-Szmek assert_return(sysname, -EINVAL);
c7332b0844e28d9b70c3c763b929f105c1056fe8Zbigniew Jędrzejewski-Szmek if (streq(subsystem, "subsystem")) {
c7332b0844e28d9b70c3c763b929f105c1056fe8Zbigniew Jędrzejewski-Szmek syspath = strjoina("/sys/subsystem/", sysname);
c7332b0844e28d9b70c3c763b929f105c1056fe8Zbigniew Jędrzejewski-Szmek if (access(syspath, F_OK) >= 0)
c7332b0844e28d9b70c3c763b929f105c1056fe8Zbigniew Jędrzejewski-Szmek return sd_device_new_from_syspath(ret, syspath);
d4205751d4643c272059a3728045929dd0e5e800Lennart Poettering syspath = strjoina("/sys/bus/", sysname);
d4205751d4643c272059a3728045929dd0e5e800Lennart Poettering return sd_device_new_from_syspath(ret, syspath);
d4205751d4643c272059a3728045929dd0e5e800Lennart Poettering syspath = strjoina("/sys/class/", sysname);
18cd5fe99f70a55a2d6f2303d6ee0624942695b1Zbigniew Jędrzejewski-Szmek return sd_device_new_from_syspath(ret, syspath);
d4205751d4643c272059a3728045929dd0e5e800Lennart Poettering syspath = strjoina("/sys/module/", sysname);
d4205751d4643c272059a3728045929dd0e5e800Lennart Poettering return sd_device_new_from_syspath(ret, syspath);
d4205751d4643c272059a3728045929dd0e5e800Lennart Poettering } else if (streq(subsystem, "drivers")) {
d4205751d4643c272059a3728045929dd0e5e800Lennart Poettering strscpy(subsys, sizeof(subsys), sysname);
d4205751d4643c272059a3728045929dd0e5e800Lennart Poettering syspath = strjoina("/sys/subsystem/", subsys, "/drivers/", driver);
d4205751d4643c272059a3728045929dd0e5e800Lennart Poettering return sd_device_new_from_syspath(ret, syspath);
d4205751d4643c272059a3728045929dd0e5e800Lennart Poettering syspath = strjoina("/sys/bus/", subsys, "/drivers/", driver);
d4205751d4643c272059a3728045929dd0e5e800Lennart Poettering return sd_device_new_from_syspath(ret, syspath);
d4205751d4643c272059a3728045929dd0e5e800Lennart Poettering syspath = strjoina("/sys/subsystem/", subsystem, "/devices/", sysname);
d4205751d4643c272059a3728045929dd0e5e800Lennart Poettering return sd_device_new_from_syspath(ret, syspath);
d4205751d4643c272059a3728045929dd0e5e800Lennart Poettering syspath = strjoina("/sys/bus/", subsystem, "/devices/", sysname);
d4205751d4643c272059a3728045929dd0e5e800Lennart Poettering return sd_device_new_from_syspath(ret, syspath);
d4205751d4643c272059a3728045929dd0e5e800Lennart Poettering syspath = strjoina("/sys/class/", subsystem, "/", sysname);
d4205751d4643c272059a3728045929dd0e5e800Lennart Poettering return sd_device_new_from_syspath(ret, syspath);
d4205751d4643c272059a3728045929dd0e5e800Lennart Poetteringint device_set_devtype(sd_device *device, const char *_devtype) {
844ec79b3c2f246114ea316ebe1f36044bdb688eZbigniew Jędrzejewski-Szmek r = device_add_property_internal(device, "DEVTYPE", devtype);
844ec79b3c2f246114ea316ebe1f36044bdb688eZbigniew Jędrzejewski-Szmekint device_set_ifindex(sd_device *device, const char *_ifindex) {
844ec79b3c2f246114ea316ebe1f36044bdb688eZbigniew Jędrzejewski-Szmek r = safe_atoi(_ifindex, &ifindex);
844ec79b3c2f246114ea316ebe1f36044bdb688eZbigniew Jędrzejewski-Szmek r = device_add_property_internal(device, "IFINDEX", _ifindex);
844ec79b3c2f246114ea316ebe1f36044bdb688eZbigniew Jędrzejewski-Szmekint device_set_devname(sd_device *device, const char *_devname) {
844ec79b3c2f246114ea316ebe1f36044bdb688eZbigniew Jędrzejewski-Szmek _cleanup_free_ char *devname = NULL;
844ec79b3c2f246114ea316ebe1f36044bdb688eZbigniew Jędrzejewski-Szmek r = asprintf(&devname, "/dev/%s", _devname);
844ec79b3c2f246114ea316ebe1f36044bdb688eZbigniew Jędrzejewski-Szmek r = device_add_property_internal(device, "DEVNAME", devname);
844ec79b3c2f246114ea316ebe1f36044bdb688eZbigniew Jędrzejewski-Szmekint device_set_devmode(sd_device *device, const char *_devmode) {
d4205751d4643c272059a3728045929dd0e5e800Lennart Poettering r = device_add_property_internal(device, "DEVMODE", _devmode);
844ec79b3c2f246114ea316ebe1f36044bdb688eZbigniew Jędrzejewski-Szmekint device_set_devnum(sd_device *device, const char *major, const char *minor) {
d4205751d4643c272059a3728045929dd0e5e800Lennart Poettering r = device_add_property_internal(device, "MAJOR", major);
d4205751d4643c272059a3728045929dd0e5e800Lennart Poettering r = device_add_property_internal(device, "MINOR", minor);
18cd5fe99f70a55a2d6f2303d6ee0624942695b1Zbigniew Jędrzejewski-Szmek device->devnum = makedev(maj, min);
d4205751d4643c272059a3728045929dd0e5e800Lennart Poetteringstatic int handle_uevent_line(sd_device *device, const char *key, const char *value, const char **major, const char **minor) {
844ec79b3c2f246114ea316ebe1f36044bdb688eZbigniew Jędrzejewski-Szmek } else if (streq(key, "IFINDEX")) {
844ec79b3c2f246114ea316ebe1f36044bdb688eZbigniew Jędrzejewski-Szmek r = device_set_ifindex(device, value);
844ec79b3c2f246114ea316ebe1f36044bdb688eZbigniew Jędrzejewski-Szmek r = device_set_devname(device, value);
844ec79b3c2f246114ea316ebe1f36044bdb688eZbigniew Jędrzejewski-Szmek } else if (streq(key, "DEVMODE")) {
d4205751d4643c272059a3728045929dd0e5e800Lennart Poettering r = device_add_property_internal(device, key, value);
d4205751d4643c272059a3728045929dd0e5e800Lennart Poetteringint device_read_uevent_file(sd_device *device) {
d4205751d4643c272059a3728045929dd0e5e800Lennart Poettering const char *syspath, *key, *value, *major = NULL, *minor = NULL;
83f6936a018b08880670838756e0f4e9ea98b4a7Lennart Poettering if (device->uevent_loaded || device->sealed)
d4205751d4643c272059a3728045929dd0e5e800Lennart Poettering r = sd_device_get_syspath(device, &syspath);
d4205751d4643c272059a3728045929dd0e5e800Lennart Poettering r = read_full_file(path, &uevent, &uevent_len);
d4205751d4643c272059a3728045929dd0e5e800Lennart Poettering log_debug("sd-device: failed to read uevent file '%s': %s", path, strerror(-r));
d4205751d4643c272059a3728045929dd0e5e800Lennart Poettering for (i = 0; i < uevent_len; i++) {
d4205751d4643c272059a3728045929dd0e5e800Lennart Poettering log_debug("sd-device: ignoring invalid uevent line '%s'", key);
83f6936a018b08880670838756e0f4e9ea98b4a7Lennart Poettering r = handle_uevent_line(device, key, value, &major, &minor);
83f6936a018b08880670838756e0f4e9ea98b4a7Lennart Poettering log_debug("sd-device: failed to handle uevent entry '%s=%s': %s", key, value, strerror(-r));
d4205751d4643c272059a3728045929dd0e5e800Lennart Poettering assert_not_reached("invalid state when parsing uevent file");
d4205751d4643c272059a3728045929dd0e5e800Lennart Poettering r = device_set_devnum(device, major, minor);
d4205751d4643c272059a3728045929dd0e5e800Lennart Poettering log_debug("sd-device: could not set 'MAJOR=%s' or 'MINOR=%s' from '%s': %s", major, minor, path, strerror(-r));
d4205751d4643c272059a3728045929dd0e5e800Lennart Poettering_public_ int sd_device_get_ifindex(sd_device *device, int *ifindex) {
d4205751d4643c272059a3728045929dd0e5e800Lennart Poettering_public_ int sd_device_new_from_device_id(sd_device **ret, const char *id) {
d4205751d4643c272059a3728045929dd0e5e800Lennart Poettering switch (id[0]) {
d4205751d4643c272059a3728045929dd0e5e800Lennart Poettering r = sscanf(id, "%c%i:%i", &type, &maj, &min);
d4205751d4643c272059a3728045929dd0e5e800Lennart Poettering return sd_device_new_from_devnum(ret, type, makedev(maj, min));
d4205751d4643c272059a3728045929dd0e5e800Lennart Poettering _cleanup_device_unref_ sd_device *device = NULL;
54b7254c1fa629937f92fd6fa34bdf127b696a00Zbigniew Jędrzejewski-Szmek r = safe_atoi(&id[1], &ifr.ifr_ifindex);
54b7254c1fa629937f92fd6fa34bdf127b696a00Zbigniew Jędrzejewski-Szmek sk = socket(PF_INET, SOCK_DGRAM, 0);
54b7254c1fa629937f92fd6fa34bdf127b696a00Zbigniew Jędrzejewski-Szmek r = ioctl(sk, SIOCGIFNAME, &ifr);
54b7254c1fa629937f92fd6fa34bdf127b696a00Zbigniew Jędrzejewski-Szmek r = sd_device_new_from_subsystem_sysname(&device, "net", ifr.ifr_name);
d4205751d4643c272059a3728045929dd0e5e800Lennart Poettering r = sd_device_get_ifindex(device, &ifindex);
d4205751d4643c272059a3728045929dd0e5e800Lennart Poettering /* this si racey, so we might end up with the wrong device */
d4205751d4643c272059a3728045929dd0e5e800Lennart Poettering (void)strscpy(subsys, sizeof(subsys), id + 1);
d4205751d4643c272059a3728045929dd0e5e800Lennart Poettering return sd_device_new_from_subsystem_sysname(ret, subsys, sysname);
d4205751d4643c272059a3728045929dd0e5e800Lennart Poettering_public_ int sd_device_get_syspath(sd_device *device, const char **ret) {
54b7254c1fa629937f92fd6fa34bdf127b696a00Zbigniew Jędrzejewski-Szmek assert_return(device, -EINVAL);
54b7254c1fa629937f92fd6fa34bdf127b696a00Zbigniew Jędrzejewski-Szmek assert(path_startswith(device->syspath, "/sys/"));
54b7254c1fa629937f92fd6fa34bdf127b696a00Zbigniew Jędrzejewski-Szmekstatic int device_new_from_child(sd_device **ret, sd_device *child) {
54b7254c1fa629937f92fd6fa34bdf127b696a00Zbigniew Jędrzejewski-Szmek _cleanup_free_ char *path = NULL;
54b7254c1fa629937f92fd6fa34bdf127b696a00Zbigniew Jędrzejewski-Szmek r = sd_device_get_syspath(child, &syspath);
54b7254c1fa629937f92fd6fa34bdf127b696a00Zbigniew Jędrzejewski-Szmek subdir = path + strlen("/sys");
return -ENOENT;
return -ENOENT;
if (!subsystem)
return -ENOMEM;
const char *syspath;
char *path;
_public_ int sd_device_get_parent_with_subsystem_devtype(sd_device *child, const char *subsystem, const char *devtype, sd_device **ret) {
if (!devtype)
if (!driver)
return -ENOMEM;
const char *syspath;
char *path;
return -ENOENT;
const char *pos;
if (!pos)
return -EINVAL;
pos ++;
return -EINVAL;
if (!sysname)
return -ENOMEM;
len ++;
if (len == 0)
return -EINVAL;
char *value;
if (!key)
return -ENOMEM;
if (!value)
return -EINVAL;
char *path;
switch (key) {
const char *subsystem;
int ifindex, r;
return -errno;
} else if (ifindex > 0) {
return -errno;
const char *sysname;
if (!sysname)
return -EINVAL;
return -errno;
char *path;
char key;
KEY,
if (r == -ENOENT)
for (i = 0; i < db_len; i++) {
switch (state) {
case PRE_KEY:
case KEY:
case PRE_VALUE:
case INVALID_LINE:
case VALUE:
return -EBUSY;
return -ENODATA;
return -EIO;
return NULL;
return NULL;
const char *devlink;
if (devlink)
const char *tag;
if (tag)
const char *key;
const char *value;
return NULL;
value = ordered_hashmap_iterate(device->properties, &device->properties_iterator, (const void**)&key);
if (_value)
return key;
const char *key;
const char *value;
return NULL;
return NULL;
value = ordered_hashmap_iterate(device->properties, &device->properties_iterator, (const void**)&key);
if (_value)
return key;
const char *syspath;
if (!dir)
return -errno;
char *path;
errno = -r;
return NULL;
return NULL;
_public_ int sd_device_get_property_value(sd_device *device, const char *key, const char **_value) {
char *value;
if (!value)
return -ENOENT;
if (!key) {
if (!key)
return -ENOMEM;
if (_value) {
if (!value)
return -ENOMEM;
if (!key)
return -ENOENT;
if (_value)
_public_ int sd_device_get_sysattr_value(sd_device *device, const char *sysattr, const char **_value) {
char *path;
if (r != -ENOENT) {
if (!cached_value)
return -ENOENT;
if (_value)
return -ENOENT;
return -EINVAL;
return -EINVAL;
return -EPERM;
const char *syspath;
char *path;
if (!value) {
return -ENXIO;
return -EINVAL;
return -EISDIR;
return -EACCES;
return -EINVAL;
if (fd < 0)
return -errno;
if (size < 0)
return -errno;
return -EIO;