utils-systemd: provide enumeration of units
[src/app-framework-main.git] / src / utils-systemd.c
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);
+}
+