#include "device-private.h"
#include "device-util.h"
#include "dirent-util.h"
+#include "errno-util.h"
#include "fd-util.h"
#include "sort-util.h"
#include "static-destruct.h"
static usec_t arg_wait_for_initialization_timeout = 0;
static bool skip_attribute(const char *name) {
+ assert(name);
+
/* Those are either displayed separately or should not be shown at all. */
return STR_IN_SET(name,
"uevent",
STATIC_DESTRUCTOR_REGISTER(arg_properties, strv_freep);
static int sysattr_compare(const SysAttr *a, const SysAttr *b) {
+ assert(a);
+ assert(b);
+
return strcmp(a->name, b->name);
}
_cleanup_free_ SysAttr *sysattrs = NULL;
const char *name, *value;
size_t n_items = 0;
+ int r;
+
+ assert(device);
value = NULL;
(void) sd_device_get_devpath(device, &value);
if (skip_attribute(name))
continue;
- if (sd_device_get_sysattr_value(device, name, &value) < 0)
- continue;
+ r = sd_device_get_sysattr_value(device, name, &value);
+ if (r >= 0) {
+ /* skip any values that look like a path */
+ if (value[0] == '/')
+ continue;
- /* skip any values that look like a path */
- if (value[0] == '/')
- continue;
+ /* skip nonprintable attributes */
+ len = strlen(value);
+ while (len > 0 && isprint((unsigned char) value[len-1]))
+ len--;
+ if (len > 0)
+ continue;
- /* skip nonprintable attributes */
- len = strlen(value);
- while (len > 0 && isprint((unsigned char) value[len-1]))
- len--;
- if (len > 0)
+ } else if (ERRNO_IS_PRIVILEGE(r))
+ value = "(not readable)";
+ else
continue;
if (!GREEDY_REALLOC(sysattrs, n_items + 1))
sd_device *child, *parent;
int r;
+ assert(device);
+
printf("\n"
"Udevadm info starts with the device specified by the devpath and then\n"
"walks up the chain of parent devices. It prints for every device\n"
const char *str, *val;
int i;
+ assert(device);
+
(void) sd_device_get_devpath(device, &str);
printf("P: %s\n", str);
static int stat_device(const char *name, bool export, const char *prefix) {
struct stat statbuf;
+ assert(name);
+
if (stat(name, &statbuf) != 0)
return -errno;
}
static void cleanup_dir(DIR *dir, mode_t mask, int depth) {
- struct dirent *dent;
+ assert(dir);
if (depth <= 0)
return;
FOREACH_DIRENT_ALL(dent, dir, break) {
struct stat stats;
- if (dent->d_name[0] == '.')
+ if (dot_or_dot_dot(dent->d_name))
continue;
- if (fstatat(dirfd(dir), dent->d_name, &stats, AT_SYMLINK_NOFOLLOW) != 0)
+ if (fstatat(dirfd(dir), dent->d_name, &stats, AT_SYMLINK_NOFOLLOW) < 0)
continue;
if ((stats.st_mode & mask) != 0)
continue;
}
}
+/*
+ * Assume that dir is a directory with file names matching udev data base
+ * entries for devices in /run/udev/data (such as "b8:16"), and removes
+ * all files except those that haven't been deleted in /run/udev/data
+ * (i.e. they were skipped during db cleanup because of the db_persist flag).
+ */
+static void cleanup_dir_after_db_cleanup(DIR *dir, DIR *datadir) {
+ assert(dir);
+ assert(datadir);
+
+ FOREACH_DIRENT_ALL(dent, dir, break) {
+ if (dot_or_dot_dot(dent->d_name))
+ continue;
+
+ if (faccessat(dirfd(datadir), dent->d_name, F_OK, AT_SYMLINK_NOFOLLOW) >= 0)
+ /* The corresponding udev database file still exists.
+ * Assuming the parsistent flag is set for the database. */
+ continue;
+
+ (void) unlinkat(dirfd(dir), dent->d_name, 0);
+ }
+}
+
+static void cleanup_dirs_after_db_cleanup(DIR *dir, DIR *datadir) {
+ assert(dir);
+ assert(datadir);
+
+ FOREACH_DIRENT_ALL(dent, dir, break) {
+ struct stat stats;
+
+ if (dot_or_dot_dot(dent->d_name))
+ continue;
+ if (fstatat(dirfd(dir), dent->d_name, &stats, AT_SYMLINK_NOFOLLOW) < 0)
+ continue;
+ if (S_ISDIR(stats.st_mode)) {
+ _cleanup_closedir_ DIR *dir2 = NULL;
+
+ dir2 = fdopendir(openat(dirfd(dir), dent->d_name, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC));
+ if (dir2)
+ cleanup_dir_after_db_cleanup(dir2, datadir);
+
+ (void) unlinkat(dirfd(dir), dent->d_name, AT_REMOVEDIR);
+ } else
+ (void) unlinkat(dirfd(dir), dent->d_name, 0);
+ }
+}
+
static void cleanup_db(void) {
- _cleanup_closedir_ DIR *dir1 = NULL, *dir2 = NULL, *dir3 = NULL, *dir4 = NULL, *dir5 = NULL;
+ _cleanup_closedir_ DIR *dir1 = NULL, *dir2 = NULL, *dir3 = NULL, *dir4 = NULL;
dir1 = opendir("/run/udev/data");
if (dir1)
dir2 = opendir("/run/udev/links");
if (dir2)
- cleanup_dir(dir2, 0, 2);
+ cleanup_dirs_after_db_cleanup(dir2, dir1);
dir3 = opendir("/run/udev/tags");
if (dir3)
- cleanup_dir(dir3, 0, 2);
+ cleanup_dirs_after_db_cleanup(dir3, dir1);
dir4 = opendir("/run/udev/static_node-tags");
if (dir4)
cleanup_dir(dir4, 0, 2);
- dir5 = opendir("/run/udev/watch");
- if (dir5)
- cleanup_dir(dir5, 0, 1);
+ /* Do not remove /run/udev/watch. It will be handled by udevd well on restart.
+ * And should not be removed by external program when udevd is running. */
}
static int query_device(QueryType query, sd_device* device) {
case QUERY_ALL:
return print_record(device);
- }
- assert_not_reached();
- return 0;
+ default:
+ assert_not_reached();
+ }
}
static int help(void) {
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
"-x/--export or -P/--export-prefix cannot be used with --value");
- char **p;
STRV_FOREACH(p, devices) {
_cleanup_(sd_device_unrefp) sd_device *device = NULL;