utils-systemd: provide enumeration of units
authorJosé Bollo <jose.bollo@iot.bzh>
Thu, 16 Mar 2017 08:27:41 +0000 (09:27 +0100)
committerJosé Bollo <jose.bollo@iot.bzh>
Fri, 17 Mar 2017 12:01:40 +0000 (13:01 +0100)
This enumeration is based on the filesystem,
units are listed from the configured directory.
This is necessarily because not all units can be
loaded by systemd but all can be started.

Change-Id: Ic4d1331c8c54f5bbaa747ff8084da2b0c5a65c55
Signed-off-by: José Bollo <jose.bollo@iot.bzh>
src/utils-systemd.c
src/utils-systemd.h

index 62930ed..b8c3c0b 100644 (file)
 #include <stdlib.h>
 #include <stdio.h>
 #include <errno.h>
+#include <unistd.h>
+#include <dirent.h>
+#include <fcntl.h>
 #include <string.h>
-#include <poll.h>
-#include <assert.h>
+#include <sys/types.h>
+#include <sys/stat.h>
 
 #include <systemd/sd-bus.h>
 #include <systemd/sd-bus-protocol.h>
@@ -41,6 +44,29 @@ static const char sdbm_reload[] = "Reload";
 static struct sd_bus *sysbus;
 static struct sd_bus *usrbus;
 
+/*
+ * Translate systemd errors to errno errors
+ */
+static int seterrno(int value)
+{
+       errno = value;
+       return -1;
+}
+
+static int sderr2errno(int rc)
+{
+       return rc < 0 ? seterrno(-rc) : rc;
+}
+
+static int errno2sderr(int rc)
+{
+       return rc < 0 ? -errno : rc;
+}
+
+/* Returns in 'ret' either the system bus (if isuser==0)
+ * or the user bus (if isuser!=0).
+ * Returns 0 in case of success or -1 in case of error
+ */
 static int get_bus(int isuser, struct sd_bus **ret)
 {
        int rc;
@@ -59,7 +85,20 @@ static int get_bus(int isuser, struct sd_bus **ret)
                if (!rc)
                        sysbus = *ret;
        }
-       return rc;
+       return sderr2errno(rc);
+}
+static int check_snprintf_result(int rc, size_t buflen)
+{
+       return (rc >= 0 && (size_t)rc >= buflen) ? seterrno(ENAMETOOLONG) : rc;
+}
+
+int systemd_get_units_dir(char *path, size_t pathlen, int isuser)
+{
+       int rc = snprintf(path, pathlen, "%s/%s", 
+                       SYSTEMD_UNITS_ROOT,
+                       isuser ? "user" : "system");
+
+       return check_snprintf_result(rc, pathlen);
 }
 
 int systemd_get_unit_path(char *path, size_t pathlen, int isuser, const char *unit, const char *uext)
@@ -70,11 +109,7 @@ int systemd_get_unit_path(char *path, size_t pathlen, int isuser, const char *un
                        unit,
                        uext);
 
-       if (rc >= 0 && (size_t)rc >= pathlen) {
-               errno = ENAMETOOLONG;
-               rc = -1;
-       }
-       return rc;
+       return check_snprintf_result(rc, pathlen);
 }
 
 int systemd_get_wants_path(char *path, size_t pathlen, int isuser, const char *wanter, const char *unit, const char *uext)
@@ -86,22 +121,14 @@ int systemd_get_wants_path(char *path, size_t pathlen, int isuser, const char *w
                        unit,
                        uext);
 
-       if (rc >= 0 && (size_t)rc >= pathlen) {
-               errno = ENAMETOOLONG;
-               rc = -1;
-       }
-       return rc;
+       return check_snprintf_result(rc, pathlen);
 }
 
 int systemd_get_wants_target(char *path, size_t pathlen, const char *unit, const char *uext)
 {
        int rc = snprintf(path, pathlen, "../%s.%s", unit, uext);
 
-       if (rc >= 0 && (size_t)rc >= pathlen) {
-               errno = ENAMETOOLONG;
-               rc = -1;
-       }
-       return rc;
+       return check_snprintf_result(rc, pathlen);
 }
 
 int systemd_daemon_reload(int isuser)
@@ -111,8 +138,78 @@ int systemd_daemon_reload(int isuser)
 
        rc = get_bus(isuser, &bus);
        if (!rc) {
+               /* TODO: asynchronous bind... */
+               /* TODO: more diagnostic... */
                rc = sd_bus_call_method(bus, sdb_destination, sdb_path, sdbi_manager, sdbm_reload, NULL, NULL, NULL);
        }
        return rc;
 }
 
+int systemd_unit_list(int isuser, int (*callback)(void *closure, const char *name, const char *path, int isuser), void *closure)
+{
+       DIR *dir;
+       char path[PATH_MAX + 1];
+       struct dirent *dent;
+       int rc, isfile;
+       size_t offset, len;
+       struct stat st;
+
+       /* get the path */
+       rc = systemd_get_units_dir(path, sizeof path - 1, isuser);
+       if (rc < 0)
+               return rc;
+       offset = (size_t)rc;
+
+       /* open the directory */
+       dir = opendir(path);
+       if (!dir)
+               return -1;
+
+       /* prepare path */
+       path[offset++] = '/';
+
+       /* read the directory */
+       for(;;) {
+               /* get next entry */
+               errno = 0;
+               dent = readdir(dir);
+               if (dent == NULL) {
+                       /* end or error */
+                       rc = (!errno) - 1;
+                       break;
+               }
+
+               /* is a file? */
+               if (dent->d_type == DT_REG)
+                       isfile = 1;
+               else if (dent->d_type != DT_UNKNOWN)
+                       isfile = 0;
+               else {
+                       rc = fstatat(dirfd(dir), dent->d_name, &st, AT_SYMLINK_NOFOLLOW|AT_NO_AUTOMOUNT);
+                       if (rc < 0)
+                               break;
+                       isfile = S_ISREG(st.st_mode);
+               }
+
+               /* calls the callback if is a file */
+               if (isfile) {
+                       len = strlen(dent->d_name);
+                       if (offset + len >= sizeof path) {
+                               rc = seterrno(ENAMETOOLONG);
+                               break;
+                       }
+                       memcpy(&path[offset], dent->d_name, 1 + len);
+                       rc = callback(closure, &path[offset], path, isuser);
+                       if (rc)
+                               break;
+               }
+       }
+       closedir(dir);
+       return rc;
+}
+
+int systemd_unit_list_all(int (*callback)(void *closure, const char *name, const char *path, int isuser), void *closure)
+{
+       return systemd_unit_list(1, callback, closure) ? : systemd_unit_list(0, callback, closure);
+}
+
index 30f9522..f704d45 100644 (file)
 
 #pragma once
 
+extern int systemd_get_units_dir(char *path, size_t pathlen, int isuser);
 extern int systemd_get_unit_path(char *path, size_t pathlen, int isuser, const char *unit, const char *uext);
 extern int systemd_get_wants_path(char *path, size_t pathlen, int isuser, const char *wanter, const char *unit, const char *uext);
 extern int systemd_get_wants_target(char *path, size_t pathlen, const char *unit, const char *uext);
 extern int systemd_daemon_reload(int isuser);
 
+extern int systemd_unit_list(int isuser, int (*callback)(void *closure, const char *name, const char *path, int isuser), void *closure);
+extern int systemd_unit_list_all(int (*callback)(void *closure, const char *name, const char *path, int isuser), void *closure);
+