#include "libudev.h"
#include "libudev-private.h"
+static int udev_device_set_devnode(struct udev_device *udev_device, const char *devnode);
+
/**
* SECTION:libudev-device
* @short_description: kernel sys devices
*
* Representation of kernel sys devices. Devices are uniquely identified
* by their syspath, every device has exactly one path in the kernel sys
- * filesystem. Devices usually belong to a kernel subsystem, and and have
+ * filesystem. Devices usually belong to a kernel subsystem, and have
* a unique name inside that subsystem.
*/
char num[32];
udev_device->ifindex = ifindex;
- snprintf(num, sizeof(num), "%u", ifindex);
+ snprintf(num, sizeof(num), "%d", ifindex);
udev_device_add_property(udev_device, "IFINDEX", num);
return 0;
}
return 0;
}
-int udev_device_set_subsystem(struct udev_device *udev_device, const char *subsystem)
+static int udev_device_set_subsystem(struct udev_device *udev_device, const char *subsystem)
{
free(udev_device->subsystem);
udev_device->subsystem = strdup(subsystem);
return udev_device_add_property(udev_device, name, val);
}
+static int udev_device_set_syspath(struct udev_device *udev_device, const char *syspath)
+{
+ const char *pos;
+ size_t len;
+
+ free(udev_device->syspath);
+ udev_device->syspath = strdup(syspath);
+ if (udev_device->syspath == NULL)
+ return -ENOMEM;
+ udev_device->devpath = udev_device->syspath + strlen("/sys");
+ udev_device_add_property(udev_device, "DEVPATH", udev_device->devpath);
+
+ pos = strrchr(udev_device->syspath, '/');
+ if (pos == NULL)
+ return -EINVAL;
+ udev_device->sysname = strdup(&pos[1]);
+ if (udev_device->sysname == NULL)
+ return -ENOMEM;
+
+ /* some devices have '!' in their name, change that to '/' */
+ len = 0;
+ while (udev_device->sysname[len] != '\0') {
+ if (udev_device->sysname[len] == '!')
+ udev_device->sysname[len] = '/';
+ len++;
+ }
+
+ /* trailing number */
+ while (len > 0 && isdigit(udev_device->sysname[--len]))
+ udev_device->sysnum = &udev_device->sysname[len];
+
+ /* sysname is completely numeric */
+ if (len == 0)
+ udev_device->sysnum = NULL;
+
+ return 0;
+}
+
/*
* parse property string, and if needed, update internal values accordingly
*
}
f = fopen(dbfile, "re");
- if (f == NULL) {
- udev_dbg(udev_device->udev, "no db file to read %s: %m\n", dbfile);
- return -1;
- }
+ if (f == NULL)
+ return log_debug_errno(errno, "no db file to read %s: %m", dbfile);
+
+ /* devices with a database entry are initialized */
udev_device->is_initialized = true;
while (fgets(line, sizeof(line), f)) {
}
fclose(f);
- udev_dbg(udev_device->udev, "device %p filled with db file data\n", udev_device);
+ log_debug("device %p filled with db file data", udev_device);
return 0;
}
strscpyl(filename, sizeof(filename), udev_device->syspath, "/uevent", NULL);
f = fopen(filename, "re");
if (f == NULL)
- return -1;
+ return -errno;
udev_device->uevent_loaded = true;
while (fgets(line, sizeof(line), f)) {
device->info_loaded = true;
}
-struct udev_device *udev_device_new(struct udev *udev)
+static struct udev_device *udev_device_new(struct udev *udev)
{
struct udev_device *udev_device;
- struct udev_list_entry *list_entry;
- if (udev == NULL)
+ if (udev == NULL) {
+ errno = EINVAL;
return NULL;
+ }
- udev_device = calloc(1, sizeof(struct udev_device));
- if (udev_device == NULL)
+ udev_device = new0(struct udev_device, 1);
+ if (udev_device == NULL) {
+ errno = ENOMEM;
return NULL;
+ }
udev_device->refcount = 1;
udev_device->udev = udev;
udev_list_init(udev, &udev_device->devlinks_list, true);
udev_list_init(udev, &udev_device->sysattr_list, false);
udev_list_init(udev, &udev_device->tags_list, true);
udev_device->watch_handle = -1;
- /* copy global properties */
- udev_list_entry_foreach(list_entry, udev_get_properties_list_entry(udev))
- udev_device_add_property(udev_device,
- udev_list_entry_get_name(list_entry),
- udev_list_entry_get_value(list_entry));
+
return udev_device;
}
struct stat statbuf;
struct udev_device *udev_device;
- if (udev == NULL)
+ if (udev == NULL) {
+ errno = EINVAL;
return NULL;
- if (syspath == NULL)
+ }
+
+ if (syspath == NULL) {
+ errno = EINVAL;
return NULL;
+ }
/* path starts in sys */
if (!startswith(syspath, "/sys")) {
- udev_dbg(udev, "not in sys :%s\n", syspath);
+ log_debug("not in sys :%s", syspath);
+ errno = EINVAL;
return NULL;
}
/* path is not a root directory */
subdir = syspath + strlen("/sys");
pos = strrchr(subdir, '/');
- if (pos == NULL || pos[1] == '\0' || pos < &subdir[2])
+ if (pos == NULL || pos[1] == '\0' || pos < &subdir[2]) {
+ errno = EINVAL;
return NULL;
+ }
/* resolve possible symlink to real path */
strscpy(path, sizeof(path), syspath);
return NULL;
} else {
/* everything else just needs to be a directory */
- if (stat(path, &statbuf) != 0 || !S_ISDIR(statbuf.st_mode))
+ if (stat(path, &statbuf) != 0)
return NULL;
+
+ if (!S_ISDIR(statbuf.st_mode)) {
+ errno = EISDIR;
+ return NULL;
+ }
}
udev_device = udev_device_new(udev);
return NULL;
udev_device_set_syspath(udev_device, path);
- udev_dbg(udev, "device %p has devpath '%s'\n", udev_device, udev_device_get_devpath(udev_device));
+ log_debug("device %p has devpath '%s'", udev_device, udev_device_get_devpath(udev_device));
return udev_device;
}
type_str = "block";
else if (type == 'c')
type_str = "char";
- else
+ else {
+ errno = EINVAL;
return NULL;
+ }
/* use /sys/dev/{block,char}/<maj>:<min> link */
snprintf(path, sizeof(path), "/sys/dev/%s/%u:%u",
*
* Returns: a new udev device, or #NULL, if it does not exist
**/
-_public_ struct udev_device *udev_device_new_from_device_id(struct udev *udev, char *id)
+_public_ struct udev_device *udev_device_new_from_device_id(struct udev *udev, const char *id)
{
char type;
int maj, min;
int ifindex;
ifindex = strtoul(&id[1], NULL, 10);
- if (ifindex <= 0)
+ if (ifindex <= 0) {
+ errno = EINVAL;
return NULL;
+ }
sk = socket(PF_INET, SOCK_DGRAM, 0);
if (sk < 0)
return NULL;
- memset(&ifr, 0x00, sizeof(struct ifreq));
+ memzero(&ifr, sizeof(struct ifreq));
ifr.ifr_ifindex = ifindex;
if (ioctl(sk, SIOCGIFNAME, &ifr) != 0) {
close(sk);
return NULL;
if (udev_device_get_ifindex(dev) == ifindex)
return dev;
+
+ /* this is racy, so we may end up with the wrong device */
udev_device_unref(dev);
+ errno = ENODEV;
return NULL;
}
case '+':
strscpy(subsys, sizeof(subsys), &id[1]);
sysname = strchr(subsys, ':');
- if (sysname == NULL)
+ if (sysname == NULL) {
+ errno = EINVAL;
return NULL;
+ }
sysname[0] = '\0';
sysname = &sysname[1];
return udev_device_new_from_subsystem_sysname(udev, subsys, sysname);
default:
+ errno = EINVAL;
return NULL;
}
}
strscpyl(path, sizeof(path), "/sys/bus/", subsys, "/drivers/", driver, NULL);
if (stat(path, &statbuf) == 0)
goto found;
- }
+ } else
+ errno = EINVAL;
+
goto out;
}
udev_device_add_property_from_string_parse(udev_device, environ[i]);
if (udev_device_add_property_from_string_parse_finish(udev_device) < 0) {
- udev_dbg(udev, "missing values, invalid device\n");
+ log_debug("missing values, invalid device");
udev_device_unref(udev_device);
udev_device = NULL;
}
if (udev_device_parent != NULL)
return udev_device_parent;
}
+
+ errno = ENOENT;
return NULL;
}
* Find the next parent device, and fill in information from the sys
* device and the udev database entry.
*
- * The returned the device is not referenced. It is attached to the
- * child device, and will be cleaned up when the child device
- * is cleaned up.
+ * Returned device is not referenced. It is attached to the child
+ * device, and will be cleaned up when the child device is cleaned up.
*
* It is not necessarily just the upper level directory, empty or not
* recognized sys directories are ignored.
**/
_public_ struct udev_device *udev_device_get_parent(struct udev_device *udev_device)
{
- if (udev_device == NULL)
+ if (udev_device == NULL) {
+ errno = EINVAL;
return NULL;
+ }
if (!udev_device->parent_set) {
udev_device->parent_set = true;
udev_device->parent_device = device_new_from_parent(udev_device);
* If devtype is #NULL, only subsystem is checked, and any devtype will
* match.
*
- * The returned the device is not referenced. It is attached to the
- * child device, and will be cleaned up when the child device
- * is cleaned up.
+ * Returned device is not referenced. It is attached to the child
+ * device, and will be cleaned up when the child device is cleaned up.
*
* It can be called as many times as needed, without caring about
* references.
{
struct udev_device *parent;
- if (subsystem == NULL)
+ if (subsystem == NULL) {
+ errno = EINVAL;
return NULL;
+ }
parent = udev_device_get_parent(udev_device);
while (parent != NULL) {
}
parent = udev_device_get_parent(parent);
}
+
+ if (!parent)
+ errno = ENOENT;
+
return parent;
}
* Drop a reference of a udev device. If the refcount reaches zero,
* the resources of the device will be released.
*
- * Returns: the passed udev device if it has still an active reference, or #NULL otherwise.
+ * Returns: #NULL
**/
_public_ struct udev_device *udev_device_unref(struct udev_device *udev_device)
{
return NULL;
udev_device->refcount--;
if (udev_device->refcount > 0)
- return udev_device;
+ return NULL;
if (udev_device->parent_device != NULL)
udev_device_unref(udev_device->parent_device);
free(udev_device->syspath);
char num[32];
udev_device->usec_initialized = usec_initialized;
- snprintf(num, sizeof(num), "%llu", (unsigned long long)usec_initialized);
+ snprintf(num, sizeof(num), USEC_FMT, usec_initialized);
udev_device_add_property(udev_device, "USEC_INITIALIZED", num);
}
int num = 0;
if (udev_device == NULL)
- return -1;
+ return -EINVAL;
if (udev_device->sysattr_list_read)
return 0;
dir = opendir(udev_device_get_syspath(udev_device));
if (!dir)
- return -1;
+ return -errno;
for (dent = readdir(dir); dent != NULL; dent = readdir(dir)) {
char path[UTIL_PATH_SIZE];
return udev_list_get_entry(&udev_device->sysattr_list);
}
-int udev_device_set_syspath(struct udev_device *udev_device, const char *syspath)
-{
- const char *pos;
- size_t len;
-
- free(udev_device->syspath);
- udev_device->syspath = strdup(syspath);
- if (udev_device->syspath == NULL)
- return -ENOMEM;
- udev_device->devpath = udev_device->syspath + strlen("/sys");
- udev_device_add_property(udev_device, "DEVPATH", udev_device->devpath);
-
- pos = strrchr(udev_device->syspath, '/');
- if (pos == NULL)
- return -EINVAL;
- udev_device->sysname = strdup(&pos[1]);
- if (udev_device->sysname == NULL)
- return -ENOMEM;
-
- /* some devices have '!' in their name, change that to '/' */
- len = 0;
- while (udev_device->sysname[len] != '\0') {
- if (udev_device->sysname[len] == '!')
- udev_device->sysname[len] = '/';
- len++;
- }
-
- /* trailing number */
- while (len > 0 && isdigit(udev_device->sysname[--len]))
- udev_device->sysnum = &udev_device->sysname[len];
-
- /* sysname is completely numeric */
- if (len == 0)
- udev_device->sysnum = NULL;
-
- return 0;
-}
-
-int udev_device_set_devnode(struct udev_device *udev_device, const char *devnode)
+static int udev_device_set_devnode(struct udev_device *udev_device, const char *devnode)
{
free(udev_device->devnode);
if (devnode[0] != '/') {
udev_device->id_filename = NULL;
} else if (udev_device_get_ifindex(udev_device) > 0) {
/* use netdev ifindex -- n3 */
- if (asprintf(&udev_device->id_filename, "n%u", udev_device_get_ifindex(udev_device)) < 0)
+ if (asprintf(&udev_device->id_filename, "n%i", udev_device_get_ifindex(udev_device)) < 0)
udev_device->id_filename = NULL;
} else {
/*
udev_device->is_initialized = true;
}
+static bool is_valid_tag(const char *tag)
+{
+ return !strchr(tag, ':') && !strchr(tag, ' ');
+}
+
int udev_device_add_tag(struct udev_device *udev_device, const char *tag)
{
- if (strchr(tag, ':') != NULL || strchr(tag, ' ') != NULL)
+ if (!is_valid_tag(tag))
return -EINVAL;
udev_device->tags_uptodate = false;
if (udev_list_entry_add(&udev_device->tags_list, tag, NULL) != NULL)
return -ENOMEM;
}
+void udev_device_remove_tag(struct udev_device *udev_device, const char *tag)
+{
+ struct udev_list_entry *e;
+
+ if (!is_valid_tag(tag))
+ return;
+ e = udev_list_get_entry(&udev_device->tags_list);
+ e = udev_list_entry_get_by_name(e, tag);
+ if (e) {
+ udev_device->tags_uptodate = false;
+ udev_list_entry_delete(e);
+ }
+}
+
void udev_device_cleanup_tags_list(struct udev_device *udev_device)
{
udev_device->tags_uptodate = false;
{
udev_device->db_persist = true;
}
+
+int udev_device_rename(struct udev_device *udev_device, const char *name)
+{
+ _cleanup_free_ char *dirname = NULL;
+ char *new_syspath;
+ int r;
+
+ if (udev_device == NULL || name == NULL)
+ return -EINVAL;
+
+ dirname = dirname_malloc(udev_device->syspath);
+ if (!dirname)
+ return -ENOMEM;
+
+ new_syspath = strjoina(dirname, "/", name);
+
+ r = udev_device_set_syspath(udev_device, new_syspath);
+ if (r < 0)
+ return r;
+
+ return 0;
+}
+
+struct udev_device *udev_device_shallow_clone(struct udev_device *old_device)
+{
+ struct udev_device *device;
+
+ if (old_device == NULL)
+ return NULL;
+
+ device = udev_device_new(old_device->udev);
+ if (!device) {
+ errno = ENOMEM;
+
+ return NULL;
+ }
+
+ udev_device_set_syspath(device, udev_device_get_syspath(old_device));
+ udev_device_set_subsystem(device, udev_device_get_subsystem(old_device));
+ udev_device_set_devnum(device, udev_device_get_devnum(old_device));
+
+ return device;
+}
+
+struct udev_device *udev_device_new_from_nulstr(struct udev *udev, char *nulstr, ssize_t buflen) {
+ struct udev_device *device;
+ ssize_t bufpos = 0;
+
+ if (nulstr == NULL || buflen <= 0) {
+ errno = EINVAL;
+
+ return NULL;
+ }
+
+ device = udev_device_new(udev);
+ if (!device) {
+ errno = ENOMEM;
+
+ return NULL;
+ }
+
+ udev_device_set_info_loaded(device);
+
+ while (bufpos < buflen) {
+ char *key;
+ size_t keylen;
+
+ key = nulstr + bufpos;
+ keylen = strlen(key);
+ if (keylen == 0)
+ break;
+
+ bufpos += keylen + 1;
+ udev_device_add_property_from_string_parse(device, key);
+ }
+
+ if (udev_device_add_property_from_string_parse_finish(device) < 0) {
+ log_debug("missing values, invalid device");
+
+ udev_device_unref(device);
+
+ errno = EINVAL;
+
+ return NULL;
+ }
+
+ return device;
+}