Merge pull request #10866 from yuwata/libudev-util-cleanups

libudev-util: several cleanups and tests
This commit is contained in:
Lennart Poettering 2018-11-21 11:15:35 +01:00 committed by GitHub
commit b390455cbb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 145 additions and 67 deletions

View File

@ -2,13 +2,12 @@
#include <ctype.h>
#include <errno.h>
#include <stddef.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "sd-device.h"
#include "device-nodes.h"
#include "libudev-util.h"
#include "string-util.h"
#include "strxcpyx.h"
#include "utf8.h"
@ -20,16 +19,14 @@
*/
/* handle "[<SUBSYSTEM>/<KERNEL>]<attribute>" format */
int util_resolve_subsys_kernel(const char *string,
char *result, size_t maxsize, int read_value) {
char temp[UTIL_PATH_SIZE];
char *subsys;
char *sysname;
struct udev_device *dev;
char *attr;
int util_resolve_subsys_kernel(const char *string, char *result, size_t maxsize, bool read_value) {
char temp[UTIL_PATH_SIZE], *subsys, *sysname, *attr;
_cleanup_(sd_device_unrefp) sd_device *dev = NULL;
const char *val;
int r;
if (string[0] != '[')
return -1;
return -EINVAL;
strscpy(temp, sizeof(temp), string);
@ -37,13 +34,13 @@ int util_resolve_subsys_kernel(const char *string,
sysname = strchr(subsys, '/');
if (!sysname)
return -1;
return -EINVAL;
sysname[0] = '\0';
sysname = &sysname[1];
attr = strchr(sysname, ']');
if (!attr)
return -1;
return -EINVAL;
attr[0] = '\0';
attr = &attr[1];
if (attr[0] == '/')
@ -52,38 +49,38 @@ int util_resolve_subsys_kernel(const char *string,
attr = NULL;
if (read_value && !attr)
return -1;
return -EINVAL;
dev = udev_device_new_from_subsystem_sysname(NULL, subsys, sysname);
if (!dev)
return -1;
r = sd_device_new_from_subsystem_sysname(&dev, subsys, sysname);
if (r < 0)
return r;
if (read_value) {
const char *val;
val = udev_device_get_sysattr_value(dev, attr);
if (val)
strscpy(result, maxsize, val);
else
r = sd_device_get_sysattr_value(dev, attr, &val);
if (r < 0 && r != -ENOENT)
return r;
if (r == -ENOENT)
result[0] = '\0';
else
strscpy(result, maxsize, val);
log_debug("value '[%s/%s]%s' is '%s'", subsys, sysname, attr, result);
} else {
size_t l;
char *s;
r = sd_device_get_syspath(dev, &val);
if (r < 0)
return r;
s = result;
l = strpcpyl(&s, maxsize, udev_device_get_syspath(dev), NULL);
if (attr)
strpcpyl(&s, l, "/", attr, NULL);
log_debug("path '[%s/%s]%s' is '%s'", subsys, sysname, attr, result);
strscpyl(result, maxsize, val, attr ? "/" : NULL, attr ?: NULL, NULL);
log_debug("path '[%s/%s]%s' is '%s'", subsys, sysname, strempty(attr), result);
}
udev_device_unref(dev);
return 0;
}
size_t util_path_encode(const char *src, char *dest, size_t size) {
size_t i, j;
assert(src);
assert(dest);
for (i = 0, j = 0; src[i] != '\0'; i++) {
if (src[i] == '/') {
if (j+4 >= size) {
@ -126,37 +123,41 @@ size_t util_path_encode(const char *src, char *dest, size_t size) {
* Note this may be called with 'str' == 'to', i.e. to replace whitespace
* in-place in a buffer. This function can handle that situation.
*/
int util_replace_whitespace(const char *str, char *to, size_t len) {
size_t i, j;
size_t util_replace_whitespace(const char *str, char *to, size_t len) {
bool is_space = false;
const char *p = str;
size_t j;
/* strip trailing whitespace */
len = strnlen(str, len);
while (len && isspace(str[len-1]))
len--;
assert(str);
assert(to);
/* strip leading whitespace */
i = 0;
while ((i < len) && isspace(str[i]))
i++;
p += strspn(p, WHITESPACE);
j = 0;
while (i < len) {
/* substitute multiple whitespace with a single '_' */
if (isspace(str[i])) {
while (isspace(str[i]))
i++;
to[j++] = '_';
for (j = 0; j < len && *p != '\0'; p++) {
if (isspace(*p)) {
is_space = true;
continue;
}
to[j++] = str[i++];
if (is_space) {
if (j + 1 >= len)
break;
to[j++] = '_';
is_space = false;
}
to[j++] = *p;
}
to[j] = '\0';
return j;
}
/* allow chars in whitelist, plain ascii, hex-escaping and valid utf8 */
int util_replace_chars(char *str, const char *white) {
size_t i = 0;
int replaced = 0;
size_t util_replace_chars(char *str, const char *white) {
size_t i = 0, replaced = 0;
assert(str);
while (str[i] != '\0') {
int len;

View File

@ -11,9 +11,9 @@
#define UTIL_LINE_SIZE 16384
#define UDEV_ALLOWED_CHARS_INPUT "/ $%?,"
size_t util_path_encode(const char *src, char *dest, size_t size);
int util_replace_whitespace(const char *str, char *to, size_t len);
int util_replace_chars(char *str, const char *white);
int util_resolve_subsys_kernel(const char *string, char *result, size_t maxsize, int read_value);
size_t util_replace_whitespace(const char *str, char *to, size_t len);
size_t util_replace_chars(char *str, const char *white);
int util_resolve_subsys_kernel(const char *string, char *result, size_t maxsize, bool read_value);
/* Cleanup functions */
DEFINE_TRIVIAL_CLEANUP_FUNC(struct udev*, udev_unref);

View File

@ -6,6 +6,7 @@
#include <sys/epoll.h>
#include <unistd.h>
#include "alloc-util.h"
#include "fd-util.h"
#include "libudev-util.h"
#include "log.h"
@ -329,6 +330,80 @@ static void test_hwdb(struct udev *udev, const char *modalias) {
assert_se(hwdb == NULL);
}
static void test_util_replace_whitespace_one_len(const char *str, size_t len, const char *expected) {
_cleanup_free_ char *result = NULL;
int r;
result = new(char, len + 1);
assert_se(result);
r = util_replace_whitespace(str, result, len);
assert_se((size_t) r == strlen(expected));
assert_se(streq(result, expected));
}
static void test_util_replace_whitespace_one(const char *str, const char *expected) {
test_util_replace_whitespace_one_len(str, strlen(str), expected);
}
static void test_util_replace_whitespace(void) {
test_util_replace_whitespace_one("hogehoge", "hogehoge");
test_util_replace_whitespace_one("hoge hoge", "hoge_hoge");
test_util_replace_whitespace_one(" hoge hoge ", "hoge_hoge");
test_util_replace_whitespace_one(" ", "");
test_util_replace_whitespace_one("hoge ", "hoge");
test_util_replace_whitespace_one_len("hoge hoge ", 9, "hoge_hoge");
test_util_replace_whitespace_one_len("hoge hoge ", 8, "hoge_hog");
test_util_replace_whitespace_one_len("hoge hoge ", 7, "hoge_ho");
test_util_replace_whitespace_one_len("hoge hoge ", 6, "hoge_h");
test_util_replace_whitespace_one_len("hoge hoge ", 5, "hoge");
test_util_replace_whitespace_one_len("hoge hoge ", 4, "hoge");
test_util_replace_whitespace_one_len("hoge hoge ", 3, "hog");
test_util_replace_whitespace_one_len("hoge hoge ", 2, "ho");
test_util_replace_whitespace_one_len("hoge hoge ", 1, "h");
test_util_replace_whitespace_one_len("hoge hoge ", 0, "");
test_util_replace_whitespace_one_len(" hoge hoge ", 9, "hoge_hoge");
test_util_replace_whitespace_one_len(" hoge hoge ", 8, "hoge_hog");
test_util_replace_whitespace_one_len(" hoge hoge ", 7, "hoge_ho");
test_util_replace_whitespace_one_len(" hoge hoge ", 6, "hoge_h");
test_util_replace_whitespace_one_len(" hoge hoge ", 5, "hoge");
test_util_replace_whitespace_one_len(" hoge hoge ", 4, "hoge");
test_util_replace_whitespace_one_len(" hoge hoge ", 3, "hog");
test_util_replace_whitespace_one_len(" hoge hoge ", 2, "ho");
test_util_replace_whitespace_one_len(" hoge hoge ", 1, "h");
test_util_replace_whitespace_one_len(" hoge hoge ", 0, "");
}
static void test_util_resolve_subsys_kernel_one(const char *str, bool read_value, int retval, const char *expected) {
char result[UTIL_PATH_SIZE];
int r;
r = util_resolve_subsys_kernel(str, result, sizeof(result), read_value);
assert_se(r == retval);
if (r >= 0)
assert_se(streq(result, expected));
}
static void test_util_resolve_subsys_kernel(void) {
test_util_resolve_subsys_kernel_one("hoge", false, -EINVAL, NULL);
test_util_resolve_subsys_kernel_one("[hoge", false, -EINVAL, NULL);
test_util_resolve_subsys_kernel_one("[hoge/foo", false, -EINVAL, NULL);
test_util_resolve_subsys_kernel_one("[hoge/]", false, -ENODEV, NULL);
test_util_resolve_subsys_kernel_one("[net/lo]", false, 0, "/sys/devices/virtual/net/lo");
test_util_resolve_subsys_kernel_one("[net/lo]/", false, 0, "/sys/devices/virtual/net/lo");
test_util_resolve_subsys_kernel_one("[net/lo]hoge", false, 0, "/sys/devices/virtual/net/lo/hoge");
test_util_resolve_subsys_kernel_one("[net/lo]/hoge", false, 0, "/sys/devices/virtual/net/lo/hoge");
test_util_resolve_subsys_kernel_one("[net/lo]", true, -EINVAL, NULL);
test_util_resolve_subsys_kernel_one("[net/lo]/", true, -EINVAL, NULL);
test_util_resolve_subsys_kernel_one("[net/lo]hoge", true, 0, "");
test_util_resolve_subsys_kernel_one("[net/lo]/hoge", true, 0, "");
test_util_resolve_subsys_kernel_one("[net/lo]address", true, 0, "00:00:00:00:00:00");
test_util_resolve_subsys_kernel_one("[net/lo]/address", true, 0, "00:00:00:00:00:00");
}
int main(int argc, char *argv[]) {
_cleanup_(udev_unrefp) struct udev *udev = NULL;
bool arg_monitor = false;
@ -397,7 +472,6 @@ int main(int argc, char *argv[]) {
test_device_subsys_name(udev, "subsystem", "pci");
test_device_subsys_name(udev, "drivers", "scsi:sd");
test_device_subsys_name(udev, "module", "printk");
test_device_parents(udev, syspath);
test_enumerate(udev, subsystem);
@ -409,5 +483,8 @@ int main(int argc, char *argv[]) {
if (arg_monitor)
test_monitor(udev);
test_util_replace_whitespace();
test_util_resolve_subsys_kernel();
return EXIT_SUCCESS;
}

View File

@ -457,12 +457,12 @@ static int set_inq_values(struct scsi_id_device *dev_scsi, const char *path) {
udev_util_encode_string(dev_scsi->vendor, vendor_enc_str, sizeof(vendor_enc_str));
udev_util_encode_string(dev_scsi->model, model_enc_str, sizeof(model_enc_str));
util_replace_whitespace(dev_scsi->vendor, vendor_str, sizeof(vendor_str));
util_replace_whitespace(dev_scsi->vendor, vendor_str, sizeof(vendor_str)-1);
util_replace_chars(vendor_str, NULL);
util_replace_whitespace(dev_scsi->model, model_str, sizeof(model_str));
util_replace_whitespace(dev_scsi->model, model_str, sizeof(model_str)-1);
util_replace_chars(model_str, NULL);
set_type(dev_scsi->type, type_str, sizeof(type_str));
util_replace_whitespace(dev_scsi->revision, revision_str, sizeof(revision_str));
util_replace_whitespace(dev_scsi->revision, revision_str, sizeof(revision_str)-1);
util_replace_chars(revision_str, NULL);
return 0;
}
@ -503,10 +503,10 @@ static int scsi_id(char *maj_min_dev) {
printf("ID_REVISION=%s\n", revision_str);
printf("ID_TYPE=%s\n", type_str);
if (dev_scsi.serial[0] != '\0') {
util_replace_whitespace(dev_scsi.serial, serial_str, sizeof(serial_str));
util_replace_whitespace(dev_scsi.serial, serial_str, sizeof(serial_str)-1);
util_replace_chars(serial_str, NULL);
printf("ID_SERIAL=%s\n", serial_str);
util_replace_whitespace(dev_scsi.serial_short, serial_str, sizeof(serial_str));
util_replace_whitespace(dev_scsi.serial_short, serial_str, sizeof(serial_str)-1);
util_replace_chars(serial_str, NULL);
printf("ID_SERIAL_SHORT=%s\n", serial_str);
}
@ -533,7 +533,7 @@ static int scsi_id(char *maj_min_dev) {
if (reformat_serial) {
char serial_str[MAX_SERIAL_LEN];
util_replace_whitespace(dev_scsi.serial, serial_str, sizeof(serial_str));
util_replace_whitespace(dev_scsi.serial, serial_str, sizeof(serial_str)-1);
util_replace_chars(serial_str, NULL);
printf("%s\n", serial_str);
goto out;

View File

@ -234,7 +234,7 @@ static ssize_t subst_format_var(struct udev_event *event,
return -EINVAL;
/* try to read the value specified by "[dmi/id]product_name" */
if (util_resolve_subsys_kernel(attr, vbuf, sizeof(vbuf), 1) == 0)
if (util_resolve_subsys_kernel(attr, vbuf, sizeof(vbuf), true) == 0)
val = vbuf;
/* try to read the attribute the device */

View File

@ -1688,7 +1688,7 @@ static int match_attr(struct udev_rules *rules, sd_device *dev, struct udev_even
return -1;
break;
case SB_SUBSYS:
if (util_resolve_subsys_kernel(name, vbuf, sizeof(vbuf), 1) != 0)
if (util_resolve_subsys_kernel(name, vbuf, sizeof(vbuf), true) != 0)
return -1;
value = vbuf;
break;
@ -1931,7 +1931,7 @@ int udev_rules_apply_to_event(
int match;
udev_event_apply_format(event, rules_str(rules, cur->key.value_off), filename, sizeof(filename), false);
if (util_resolve_subsys_kernel(filename, filename, sizeof(filename), 0) != 0) {
if (util_resolve_subsys_kernel(filename, filename, sizeof(filename), false) != 0) {
if (filename[0] != '/') {
char tmp[UTIL_PATH_SIZE];
@ -2374,7 +2374,7 @@ int udev_rules_apply_to_event(
const char *key_name;
key_name = rules_str(rules, cur->key.attr_off);
if (util_resolve_subsys_kernel(key_name, attr, sizeof(attr), 0) != 0 &&
if (util_resolve_subsys_kernel(key_name, attr, sizeof(attr), false) != 0 &&
sd_device_get_syspath(dev, &val) >= 0)
strscpyl(attr, sizeof(attr), val, "/", key_name, NULL);
attr_subst_subdir(attr, sizeof(attr));