diff options
author | Tom Gundersen <teg@jklm.no> | 2014-11-05 23:34:39 +0100 |
---|---|---|
committer | Tom Gundersen <teg@jklm.no> | 2014-12-11 13:54:17 +0100 |
commit | eeeeb5294afa2ecca22c63ef761abdba0175d17a (patch) | |
tree | 321ffcab2194e3b60bd0db9f0fec7ae2bffafec8 | |
parent | 3b5bf48d38a0daf8c095beaad1abd65d5bae30d5 (diff) |
sd-device: add sd_device_new_from_syspath
Also add device_set_syspath, which does all sanity checking, unlike in libudev
where it only does some.
-rw-r--r-- | src/libsystemd/sd-device/sd-device.c | 115 |
1 files changed, 113 insertions, 2 deletions
diff --git a/src/libsystemd/sd-device/sd-device.c b/src/libsystemd/sd-device/sd-device.c index 715eae469..eca378fe3 100644 --- a/src/libsystemd/sd-device/sd-device.c +++ b/src/libsystemd/sd-device/sd-device.c @@ -18,13 +18,25 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ +#include <ctype.h> + +#include "util.h" #include "refcnt.h" +#include "path-util.h" +#include "strxcpyx.h" #include "sd-device.h" +#include "device-util.h" + struct sd_device { RefCount n_ref; -} + + char *syspath; + const char *devpath; + char *sysname; + const char *sysnum; +}; static int device_new(sd_device **ret) { _cleanup_device_unref_ sd_device *device = NULL; @@ -51,8 +63,107 @@ _public_ sd_device *sd_device_ref(sd_device *device) { } _public_ sd_device *sd_device_unref(sd_device *device) { - if (device && REFCNT_DEC(device->n_ref) <= 0) + if (device && REFCNT_DEC(device->n_ref) <= 0) { + free(device->syspath); + free(device->sysname); free(device); + } return NULL; } + +static int device_set_syspath(sd_device *device, const char *_syspath) { + _cleanup_free_ char *syspath = NULL, *sysname = NULL; + const char *devpath, *sysnum; + const char *pos; + size_t len = 0; + int r; + + assert(device); + assert(_syspath); + + /* must be a subdirectory of /sys */ + if (!path_startswith(_syspath, "/sys/")) + return -EINVAL; + + r = readlink_and_canonicalize(_syspath, &syspath); + if (r < 0) + return r; + + devpath = syspath + strlen("/sys"); + + if (path_startswith(devpath, "/devices/")) { + char *path; + + /* all 'devices' require an 'uevent' file */ + path = strappenda(syspath, "/uevent"); + r = access(path, F_OK); + if (r < 0) + return -errno; + } else + /* everything else just just needs to be a directory */ + if (!is_dir(syspath, false)) + return -EINVAL; + + pos = strrchr(syspath, '/'); + if (!pos) + return -EINVAL; + pos ++; + + /* devpath is not a root directory */ + if (*pos == '\0' || pos <= devpath) + return -EINVAL; + + sysname = strdup(pos); + if (!sysname) + return -ENOMEM; + + /* some devices have '!' in their name, change that to '/' */ + while (sysname[len] != '\0') { + if (sysname[len] == '!') + sysname[len] = '/'; + + len ++; + } + + /* trailing number */ + while (len > 0 && isdigit(sysname[--len])) + sysnum = &sysname[len]; + + if (len == 0) + sysnum = NULL; + + free(device->syspath); + device->syspath = syspath; + syspath = NULL; + + free(device->sysname); + device->sysname = sysname; + sysname = NULL; + + device->devpath = devpath; + device->sysnum = sysnum; + + return 0; +} + +_public_ int sd_device_new_from_syspath(sd_device **ret, const char *syspath) { + _cleanup_device_unref_ sd_device *device = NULL; + int r; + + assert_return(ret, -EINVAL); + assert_return(syspath, -EINVAL); + + r = device_new(&device); + if (r < 0) + return r; + + r = device_set_syspath(device, syspath); + if (r < 0) + return r; + + *ret = device; + device = NULL; + + return 0; +} |