chiark / gitweb /
libudev: avoid potential misaligned accesses
[elogind.git] / src / libudev / libudev-util.c
index 24d402cd2af67a0f862043c3a06aaefde09c58ff..44f6e4a863b1df1f4f2bef99b5fb762fbb327e8c 100644 (file)
@@ -1,13 +1,21 @@
-/*
- * libudev - interface to udev device information
- *
- * Copyright (C) 2008-2011 Kay Sievers <kay.sievers@vrfy.org>
- *
- * 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 <kay@vrfy.org>
+
+  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 <http://www.gnu.org/licenses/>.
+***/
 
 #include <stdio.h>
 #include <stdlib.h>
 #include <ctype.h>
 #include <fcntl.h>
 #include <time.h>
+#include <pwd.h>
+#include <grp.h>
 #include <sys/stat.h>
+#include <sys/param.h>
 
 #include "libudev.h"
 #include "libudev-private.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 "[<SUBSYSTEM>/<KERNEL>]<attribute>" 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 +207,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 +216,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)
@@ -72,7 +243,7 @@ int util_resolve_sys_link(struct udev *udev, char *syspath, size_t size)
         }
         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 +296,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,72 +307,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)
 {
@@ -483,7 +568,7 @@ err:
  * Murmurhash is under the MIT license.
  *
  */
-static unsigned int murmur_hash2(const char *key, int len, unsigned int seed)
+static unsigned int murmur_hash2(const char *key, size_t len, unsigned int seed)
 {
         /*
          *  'm' and 'r' are mixing constants generated offline.
@@ -498,17 +583,18 @@ static unsigned int murmur_hash2(const char *key, int len, unsigned int seed)
         /* 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;
+        while(len >= sizeof(unsigned int)) {
+                unsigned int k;
 
+                memcpy(&k, data, sizeof(k));
                 k *= m;
                 k ^= k >> r;
                 k *= m;
                 h *= m;
                 h ^= k;
 
-                data += 4;
-                len -= 4;
+                data += sizeof(k);
+                len -= sizeof(k);
         }
 
         /* handle the last few bytes of the input array */
@@ -548,19 +634,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;
-}
+        int fd;
+        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)
+                ret = -errno;
+        close(fd);
+        return ret;
 }