X-Git-Url: https://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?a=blobdiff_plain;f=src%2Flibudev%2Flibudev-util.c;h=d9cdde1751483e8dbad65a5b10b129ba40991836;hb=6992efee44d3affd17dd9174e8673ae63ba01ec7;hp=24d402cd2af67a0f862043c3a06aaefde09c58ff;hpb=33502ffe2eb7b56cdd018a4fb6830d7828519fad;p=elogind.git diff --git a/src/libudev/libudev-util.c b/src/libudev/libudev-util.c index 24d402cd2..d9cdde175 100644 --- a/src/libudev/libudev-util.c +++ b/src/libudev/libudev-util.c @@ -1,13 +1,21 @@ -/* - * libudev - interface to udev device information - * - * Copyright (C) 2008-2011 Kay Sievers - * - * This library 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. - */ +/*** + This file is part of systemd. + + Copyright 2008-2012 Kay Sievers + + 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 . +***/ #include #include @@ -19,16 +27,183 @@ #include #include #include +#include +#include #include +#include +#include "device-nodes.h" #include "libudev.h" #include "libudev-private.h" +#include "utf8.h" +#include "MurmurHash2.h" /** * SECTION:libudev-util * @short_description: utils + * + * Utilities useful when dealing with devices and device node names. */ +int util_delete_path(struct udev *udev, const char *path) +{ + char p[UTIL_PATH_SIZE]; + char *pos; + int err = 0; + + if (path[0] == '/') + while(path[1] == '/') + path++; + strscpy(p, sizeof(p), path); + pos = strrchr(p, '/'); + if (pos == p || pos == NULL) + return 0; + + for (;;) { + *pos = '\0'; + pos = strrchr(p, '/'); + + /* don't remove the last one */ + if ((pos == p) || (pos == NULL)) + break; + + err = rmdir(p); + if (err < 0) { + if (errno == ENOENT) + err = 0; + break; + } + } + return err; +} + +uid_t util_lookup_user(struct udev *udev, const char *user) +{ + char *endptr; + struct passwd pwbuf; + struct passwd *pw; + uid_t uid; + size_t buflen = sysconf(_SC_GETPW_R_SIZE_MAX); + char *buf = alloca(buflen); + + if (streq(user, "root")) + return 0; + uid = strtoul(user, &endptr, 10); + if (endptr[0] == '\0') + return uid; + + errno = getpwnam_r(user, &pwbuf, buf, buflen, &pw); + if (pw != NULL) + return pw->pw_uid; + if (errno == 0 || errno == ENOENT || errno == ESRCH) + udev_err(udev, "specified user '%s' unknown\n", user); + else + udev_err(udev, "error resolving user '%s': %m\n", user); + return 0; +} + +gid_t util_lookup_group(struct udev *udev, const char *group) +{ + char *endptr; + struct group grbuf; + struct group *gr; + gid_t gid = 0; + size_t buflen = sysconf(_SC_GETPW_R_SIZE_MAX); + char *buf = NULL; + + if (streq(group, "root")) + return 0; + gid = strtoul(group, &endptr, 10); + if (endptr[0] == '\0') + return gid; + gid = 0; + for (;;) { + char *newbuf; + + newbuf = realloc(buf, buflen); + if (!newbuf) + break; + buf = newbuf; + errno = getgrnam_r(group, &grbuf, buf, buflen, &gr); + if (gr != NULL) { + gid = gr->gr_gid; + } else if (errno == ERANGE) { + buflen *= 2; + continue; + } else if (errno == 0 || errno == ENOENT || errno == ESRCH) { + udev_err(udev, "specified group '%s' unknown\n", group); + } else { + udev_err(udev, "error resolving group '%s': %m\n", group); + } + break; + } + free(buf); + return gid; +} + +/* handle "[/]" format */ +int util_resolve_subsys_kernel(struct udev *udev, 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; + + if (string[0] != '[') + return -1; + + strscpy(temp, sizeof(temp), string); + + subsys = &temp[1]; + + sysname = strchr(subsys, '/'); + if (sysname == NULL) + return -1; + sysname[0] = '\0'; + sysname = &sysname[1]; + + attr = strchr(sysname, ']'); + if (attr == NULL) + return -1; + attr[0] = '\0'; + attr = &attr[1]; + if (attr[0] == '/') + attr = &attr[1]; + if (attr[0] == '\0') + attr = NULL; + + if (read_value && attr == NULL) + return -1; + + dev = udev_device_new_from_subsystem_sysname(udev, subsys, sysname); + if (dev == NULL) + return -1; + + if (read_value) { + const char *val; + + val = udev_device_get_sysattr_value(dev, attr); + if (val != NULL) + strscpy(result, maxsize, val); + else + result[0] = '\0'; + udev_dbg(udev, "value '[%s/%s]%s' is '%s'\n", subsys, sysname, attr, result); + } else { + size_t l; + char *s; + + s = result; + l = strpcpyl(&s, maxsize, udev_device_get_syspath(dev), NULL); + if (attr != NULL) + strpcpyl(&s, l, "/", attr, NULL); + udev_dbg(udev, "path '[%s/%s]%s' is '%s'\n", subsys, sysname, attr, result); + } + udev_device_unref(dev); + return 0; +} + ssize_t util_get_sys_core_link_value(struct udev *udev, const char *slink, const char *syspath, char *value, size_t size) { char path[UTIL_PATH_SIZE]; @@ -36,7 +211,7 @@ ssize_t util_get_sys_core_link_value(struct udev *udev, const char *slink, const ssize_t len; const char *pos; - util_strscpyl(path, sizeof(path), syspath, "/", slink, NULL); + strscpyl(path, sizeof(path), syspath, "/", slink, NULL); len = readlink(path, target, sizeof(target)); if (len <= 0 || len == (ssize_t)sizeof(target)) return -1; @@ -45,7 +220,7 @@ ssize_t util_get_sys_core_link_value(struct udev *udev, const char *slink, const if (pos == NULL) return -1; pos = &pos[1]; - return util_strscpy(value, size, pos); + return strscpy(value, size, pos); } int util_resolve_sys_link(struct udev *udev, char *syspath, size_t size) @@ -70,9 +245,8 @@ int util_resolve_sys_link(struct udev *udev, char *syspath, size_t size) return -EINVAL; base[0] = '\0'; } - if (base == NULL) - return -EINVAL; - util_strscpyl(base, size - (base - syspath), "/", &link_target[back * 3], NULL); + + strscpyl(base, size - (base - syspath), "/", &link_target[back * 3], NULL); return 0; } @@ -125,26 +299,6 @@ size_t util_path_encode(const char *src, char *dest, size_t size) return j; } -size_t util_path_decode(char *s) -{ - size_t i, j; - - for (i = 0, j = 0; s[i] != '\0'; j++) { - if (memcmp(&s[i], "\\x2f", 4) == 0) { - s[j] = '/'; - i += 4; - } else if (memcmp(&s[i], "\\x5c", 4) == 0) { - s[j] = '\\'; - i += 4; - } else { - s[j] = s[i]; - i++; - } - } - s[j] = '\0'; - return j; -} - void util_remove_trailing_chars(char *path, char c) { size_t len; @@ -156,195 +310,6 @@ void util_remove_trailing_chars(char *path, char c) path[--len] = '\0'; } -/* - * Concatenates strings. In any case, terminates in _all_ cases with '\0' - * and moves the @dest pointer forward to the added '\0'. Returns the - * remaining size, and 0 if the string was truncated. - */ -size_t util_strpcpy(char **dest, size_t size, const char *src) -{ - size_t len; - - len = strlen(src); - if (len >= size) { - if (size > 1) - *dest = mempcpy(*dest, src, size-1); - size = 0; - *dest[0] = '\0'; - } else { - if (len > 0) { - *dest = mempcpy(*dest, src, len); - size -= len; - } - *dest[0] = '\0'; - } - return size; -} - -/* concatenates list of strings, moves dest forward */ -size_t util_strpcpyl(char **dest, size_t size, const char *src, ...) -{ - va_list va; - - va_start(va, src); - do { - size = util_strpcpy(dest, size, src); - src = va_arg(va, char *); - } while (src != NULL); - va_end(va); - - return size; -} - -/* copies string */ -size_t util_strscpy(char *dest, size_t size, const char *src) -{ - char *s; - - s = dest; - return util_strpcpy(&s, size, src); -} - -/* concatenates list of strings */ -size_t util_strscpyl(char *dest, size_t size, const char *src, ...) -{ - va_list va; - char *s; - - va_start(va, src); - s = dest; - do { - size = util_strpcpy(&s, size, src); - src = va_arg(va, char *); - } while (src != NULL); - va_end(va); - - return size; -} - -/* count of characters used to encode one unicode char */ -static int utf8_encoded_expected_len(const char *str) -{ - unsigned char c = (unsigned char)str[0]; - - if (c < 0x80) - return 1; - if ((c & 0xe0) == 0xc0) - return 2; - if ((c & 0xf0) == 0xe0) - return 3; - if ((c & 0xf8) == 0xf0) - return 4; - if ((c & 0xfc) == 0xf8) - return 5; - if ((c & 0xfe) == 0xfc) - return 6; - return 0; -} - -/* decode one unicode char */ -static int utf8_encoded_to_unichar(const char *str) -{ - int unichar; - int len; - int i; - - len = utf8_encoded_expected_len(str); - switch (len) { - case 1: - return (int)str[0]; - case 2: - unichar = str[0] & 0x1f; - break; - case 3: - unichar = (int)str[0] & 0x0f; - break; - case 4: - unichar = (int)str[0] & 0x07; - break; - case 5: - unichar = (int)str[0] & 0x03; - break; - case 6: - unichar = (int)str[0] & 0x01; - break; - default: - return -1; - } - - for (i = 1; i < len; i++) { - if (((int)str[i] & 0xc0) != 0x80) - return -1; - unichar <<= 6; - unichar |= (int)str[i] & 0x3f; - } - - return unichar; -} - -/* expected size used to encode one unicode char */ -static int utf8_unichar_to_encoded_len(int unichar) -{ - if (unichar < 0x80) - return 1; - if (unichar < 0x800) - return 2; - if (unichar < 0x10000) - return 3; - if (unichar < 0x200000) - return 4; - if (unichar < 0x4000000) - return 5; - return 6; -} - -/* check if unicode char has a valid numeric range */ -static int utf8_unichar_valid_range(int unichar) -{ - if (unichar > 0x10ffff) - return 0; - if ((unichar & 0xfffff800) == 0xd800) - return 0; - if ((unichar > 0xfdcf) && (unichar < 0xfdf0)) - return 0; - if ((unichar & 0xffff) == 0xffff) - return 0; - return 1; -} - -/* validate one encoded unicode char and return its length */ -static int utf8_encoded_valid_unichar(const char *str) -{ - int len; - int unichar; - int i; - - len = utf8_encoded_expected_len(str); - if (len == 0) - return -1; - - /* ascii is valid */ - if (len == 1) - return 1; - - /* check if expected encoded chars are available */ - for (i = 0; i < len; i++) - if ((str[i] & 0x80) != 0x80) - return -1; - - unichar = utf8_encoded_to_unichar(str); - - /* check if encoded length matches encoded value */ - if (utf8_unichar_to_encoded_len(unichar) != len) - return -1; - - /* check if value has valid range */ - if (!utf8_unichar_valid_range(unichar)) - return -1; - - return len; -} - int util_replace_whitespace(const char *str, char *to, size_t len) { size_t i, j; @@ -373,17 +338,6 @@ int util_replace_whitespace(const char *str, char *to, size_t len) return 0; } -static int is_whitelisted(char c, const char *white) -{ - if ((c >= '0' && c <= '9') || - (c >= 'A' && c <= 'Z') || - (c >= 'a' && c <= 'z') || - strchr("#+-.:=@_", c) != NULL || - (white != NULL && strchr(white, c) != NULL)) - return 1; - return 0; -} - /* allow chars in whitelist, plain ascii, hex-escaping and valid utf8 */ int util_replace_chars(char *str, const char *white) { @@ -393,7 +347,7 @@ int util_replace_chars(char *str, const char *white) while (str[i] != '\0') { int len; - if (is_whitelisted(str[i], white)) { + if (whitelisted_char_for_devnode(str[i], white)) { i++; continue; } @@ -441,98 +395,12 @@ int util_replace_chars(char *str, const char *white) **/ _public_ int udev_util_encode_string(const char *str, char *str_enc, size_t len) { - size_t i, j; - - if (str == NULL || str_enc == NULL) - return -1; - - for (i = 0, j = 0; str[i] != '\0'; i++) { - int seqlen; - - seqlen = utf8_encoded_valid_unichar(&str[i]); - if (seqlen > 1) { - if (len-j < (size_t)seqlen) - goto err; - memcpy(&str_enc[j], &str[i], seqlen); - j += seqlen; - i += (seqlen-1); - } else if (str[i] == '\\' || !is_whitelisted(str[i], NULL)) { - if (len-j < 4) - goto err; - sprintf(&str_enc[j], "\\x%02x", (unsigned char) str[i]); - j += 4; - } else { - if (len-j < 1) - goto err; - str_enc[j] = str[i]; - j++; - } - } - if (len-j < 1) - goto err; - str_enc[j] = '\0'; - return 0; -err: - return -1; -} - -/* - * http://sites.google.com/site/murmurhash/ - * - * All code is released to the public domain. For business purposes, - * Murmurhash is under the MIT license. - * - */ -static unsigned int murmur_hash2(const char *key, int len, unsigned int seed) -{ - /* - * 'm' and 'r' are mixing constants generated offline. - * They're not really 'magic', they just happen to work well. - */ - const unsigned int m = 0x5bd1e995; - const int r = 24; - - /* initialize the hash to a 'random' value */ - unsigned int h = seed ^ len; - - /* mix 4 bytes at a time into the hash */ - const unsigned char * data = (const unsigned char *)key; - - while(len >= 4) { - unsigned int k = *(unsigned int *)data; - - k *= m; - k ^= k >> r; - k *= m; - h *= m; - h ^= k; - - data += 4; - len -= 4; - } - - /* handle the last few bytes of the input array */ - switch(len) { - case 3: - h ^= data[2] << 16; - case 2: - h ^= data[1] << 8; - case 1: - h ^= data[0]; - h *= m; - }; - - /* do a few final mixes of the hash to ensure the last few bytes are well-incorporated */ - h ^= h >> 13; - h *= m; - h ^= h >> 15; - - return h; + return encode_devnode_name(str, str_enc, len); } unsigned int util_string_hash32(const char *str) { - return murmur_hash2(str, strlen(str), 0); + return MurmurHash2(str, strlen(str), 0); } /* get a bunch of bit numbers out of the hash, and set the bits in our bit field */ @@ -548,19 +416,27 @@ uint64_t util_string_bloom64(const char *str) return bits; } -#define USEC_PER_SEC 1000000ULL -#define NSEC_PER_USEC 1000ULL -unsigned long long ts_usec(const struct timespec *ts) +ssize_t print_kmsg(const char *fmt, ...) { - return (unsigned long long) ts->tv_sec * USEC_PER_SEC + - (unsigned long long) ts->tv_nsec / NSEC_PER_USEC; -} + _cleanup_close_ int fd = -1; + va_list ap; + char text[1024]; + ssize_t len; + ssize_t ret; -unsigned long long now_usec(void) -{ - struct timespec ts; + fd = open("/dev/kmsg", O_WRONLY|O_NOCTTY|O_CLOEXEC); + if (fd < 0) + return -errno; - if (clock_gettime(CLOCK_MONOTONIC, &ts) != 0) - return 0; - return ts_usec(&ts); + len = snprintf(text, sizeof(text), "<30>systemd-udevd[%u]: ", getpid()); + + va_start(ap, fmt); + len += vsnprintf(text + len, sizeof(text) - len, fmt, ap); + va_end(ap); + + ret = write(fd, text, len); + if (ret < 0) + return -errno; + + return ret; }