summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTom Gundersen <teg@jklm.no>2014-11-05 23:34:39 +0100
committerTom Gundersen <teg@jklm.no>2014-12-11 13:54:17 +0100
commiteeeeb5294afa2ecca22c63ef761abdba0175d17a (patch)
tree321ffcab2194e3b60bd0db9f0fec7ae2bffafec8
parent3b5bf48d38a0daf8c095beaad1abd65d5bae30d5 (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.c115
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;
+}