utils-systemd: implement start/stop of units x-first-with-systemd
authorJosé Bollo <jose.bollo@iot.bzh>
Thu, 16 Mar 2017 08:39:08 +0000 (09:39 +0100)
committerJosé Bollo <jose.bollo@iot.bzh>
Fri, 17 Mar 2017 12:01:40 +0000 (13:01 +0100)
This implementation is very basic and
must be improved.

Change-Id: Id1540e646c83285c61849092bbd8fb645c0954ed
Signed-off-by: José Bollo <jose.bollo@iot.bzh>
src/CMakeLists.txt
src/afm-urun.c [new file with mode: 0644]
src/afm-urun.h [new file with mode: 0644]
src/afm-user-daemon.c
src/utils-systemd.c
src/utils-systemd.h

index 903a7de..bbcf04b 100644 (file)
@@ -131,6 +131,7 @@ add_library(afm STATIC
        afm-launch.c
        afm-launch-mode.c
        afm-run.c
+       afm-urun.c
        )
 
 ###########################################################################
diff --git a/src/afm-urun.c b/src/afm-urun.c
new file mode 100644 (file)
index 0000000..48f8436
--- /dev/null
@@ -0,0 +1,357 @@
+/*
+ Copyright 2015, 2016, 2017 IoT.bzh
+
+ author: José Bollo <jose.bollo@iot.bzh>
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+*/
+
+#define _GNU_SOURCE
+
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <errno.h>
+#include <assert.h>
+#include <stdio.h>
+#include <limits.h>
+#include <string.h>
+
+#include <json-c/json.h>
+
+#include "verbose.h"
+#include "utils-dir.h"
+#include "utils-json.h"
+#include "utils-systemd.h"
+#include "afm-udb.h"
+#include "afm-urun.h"
+
+/**************** get appli basis *********************/
+
+static int get_basis(struct json_object *appli, int *isuser, const char **dpath, int load)
+{
+       char *dp;
+       const char *uname, *uscope;
+
+       /* get the scope */
+       if (!j_read_string_at(appli, "unit-scope", &uscope)) {
+               ERROR("'unit-scope' missing in appli description %s", json_object_get_string(appli));
+               goto inval;
+       }
+       *isuser = strcmp(uscope, "system") != 0;
+
+       /* get dpath */
+       if (!j_read_string_at(appli, "-unit-dpath-", dpath)) {
+               if (!load) {
+                       errno = ENOENT;
+                       goto error;
+               }
+               if (!j_read_string_at(appli, "unit-name", &uname)) {
+                       ERROR("'unit-name' missing in appli description %s", json_object_get_string(appli));
+                       goto inval;
+               }
+               dp = systemd_unit_dpath_by_name(*isuser, uname, 1);
+               if (dp == NULL) {
+                       ERROR("Can't load unit of name %s for %s: %m", uname, uscope);
+                       goto error;
+               }
+               if (!j_add_string(appli, "-unit-dpath-", dp)) {
+                       free(dp);
+                       goto nomem;
+               }
+               free(dp);
+               j_read_string_at(appli, "-unit-dpath-", dpath);
+       }
+
+       return 0;
+
+nomem:
+       ERROR("out of memory");
+       errno = ENOMEM;
+       goto error;
+
+inval:
+       errno = EINVAL;
+error:
+       return -1;
+}
+
+static const char *wait_state_stable(int isuser, const char *dpath)
+{
+       const char *state;
+
+       for (;;) {
+               state = systemd_unit_state_of_dpath(isuser, dpath);
+               if (state == NULL || state == SysD_State_Active
+                || state == SysD_State_Failed || state == SysD_State_Inactive)
+                       return state;
+               /* TODO: sleep */
+       }
+}
+
+/*
+ * Creates a json object that describes the state for:
+ *  - 'id', the id of the application
+ *  - 'runid', its runid
+ *  - 'pid', its pid
+ *  - 'state', its systemd state
+ *
+ * Returns the created object or NULL in case of error.
+ */
+static json_object *mkstate(const char *id, int runid, int pid, const char *state)
+{
+       struct json_object *result, *pids;
+
+       /* the structure */
+       result = json_object_new_object();
+       if (result == NULL)
+               goto error;
+
+       /* the runid */
+       if (!j_add_integer(result, "runid", runid))
+               goto error;
+
+       /* the pids */
+       if (pid > 0) {
+               pids = j_add_new_array(result, "pids");
+               if (!pids)
+                       goto error;
+               if (!j_add_integer(pids, NULL, pid))
+                       goto error;
+       }
+
+       /* the state */
+       if (!j_add_string(result, "state", state == SysD_State_Active ? "running" : "terminated"))
+               goto error;
+
+       /* the application id */
+       if (!j_add_string(result, "id", id))
+               goto error;
+
+       /* done */
+       return result;
+
+error:
+       json_object_put(result);
+       errno = ENOMEM;
+       return NULL;
+}
+
+/**************** API handling ************************/
+
+/*
+ * Starts the application described by 'appli' for the 'mode'.
+ * In case of remote start, it returns in uri the uri to
+ * connect to.
+ *
+ * A reference to 'appli' is kept during the live of the
+ * runner. This is made using json_object_get. Thus be aware
+ * that further modifications to 'appli' might create errors.
+ *
+ * Returns the runid in case of success or -1 in case of error
+ */
+int afm_urun_start(struct json_object *appli)
+{
+       return afm_urun_once(appli);
+}
+
+/*
+ * Returns the runid of a previously started application 'appli'
+ * or if none is running, starts the application described by 'appli'
+ * in local mode.
+ *
+ * A reference to 'appli' is kept during the live of the
+ * runner. This is made using json_object_get. Thus be aware
+ * that further modifications to 'appli' might create errors.
+ *
+ * Returns the runid in case of success or -1 in case of error
+ */
+int afm_urun_once(struct json_object *appli)
+{
+       const char *udpath, *state, *uscope, *uname;
+       int rc, isuser;
+
+       /* retrieve basis */
+       rc = get_basis(appli, &isuser, &udpath, 1);
+       if (rc < 0)
+               goto error;
+
+       /* start the unit */
+       rc = systemd_unit_start_dpath(isuser, udpath);
+       if (rc < 0) {
+               j_read_string_at(appli, "unit-scope", &uscope);
+               j_read_string_at(appli, "unit-name", &uname);
+               ERROR("can't start %s unit %s", uscope, uname);
+               goto error;
+       }
+
+       state = wait_state_stable(isuser, udpath);
+       if (state == NULL) {
+               j_read_string_at(appli, "unit-scope", &uscope);
+               j_read_string_at(appli, "unit-name", &uname);
+               ERROR("can't wait %s unit %s: %m", uscope, uname);
+               goto error;
+       }
+       if (state != SysD_State_Active) {
+               j_read_string_at(appli, "unit-scope", &uscope);
+               j_read_string_at(appli, "unit-name", &uname);
+               ERROR("start error %s unit %s: %s", uscope, uname, state);
+               goto error;
+       }
+
+       rc = systemd_unit_pid_of_dpath(isuser, udpath);
+       if (rc < 0) {
+               j_read_string_at(appli, "unit-scope", &uscope);
+               j_read_string_at(appli, "unit-name", &uname);
+               ERROR("can't getpid of %s unit %s: %m", uscope, uname);
+               goto error;
+       }
+               
+       return rc;
+
+error:
+       return -1;
+}
+
+static int not_yet_implemented(const char *what)
+{
+       ERROR("%s isn't yet implemented", what);
+       errno = ENOTSUP;
+       return -1;
+}
+
+/*
+ * Terminates the runner of 'runid'
+ *
+ * Returns 0 in case of success or -1 in case of error
+ */
+int afm_urun_terminate(int runid)
+{
+       int rc = systemd_unit_stop_pid(1 /* TODO: isuser? */, (unsigned)runid);
+       return rc < 0 ? rc : 0;
+}
+
+/*
+ * Stops (aka pause) the runner of 'runid'
+ *
+ * Returns 0 in case of success or -1 in case of error
+ */
+int afm_urun_pause(int runid)
+{
+       return not_yet_implemented("pause");
+}
+
+/*
+ * Continue (aka resume) the runner of 'runid'
+ *
+ * Returns 0 in case of success or -1 in case of error
+ */
+int afm_urun_resume(int runid)
+{
+       return not_yet_implemented("resume");
+}
+
+/*
+ * Get the list of the runners.
+ *
+ * Returns the list or NULL in case of error.
+ */
+struct json_object *afm_urun_list(struct afm_udb *db)
+{
+       int i, n, isuser, pid;
+       const char *udpath;
+       const char *id;
+       const char *state;
+       struct json_object *desc;
+       struct json_object *appli;
+       struct json_object *apps;
+       struct json_object *result;
+
+       apps = NULL;
+       result = json_object_new_array();
+       if (result == NULL)
+               goto error;
+
+       apps = afm_udb_applications_private(db);
+       n = json_object_array_length(apps);
+       for (i = 0 ; i < n ; i++) {
+               appli = json_object_array_get_idx(apps, i);
+               if (appli && get_basis(appli, &isuser, &udpath, 0) >= 0) {
+                       pid = systemd_unit_pid_of_dpath(isuser, udpath);
+                       if (pid > 0 && j_read_string_at(appli, "id", &id)) {
+                               state = systemd_unit_state_of_dpath(isuser, udpath);
+                               desc = mkstate(id, pid, pid, state);
+                               if (desc && json_object_array_add(result, desc) == -1) {
+                                       ERROR("can't add desc %s to result", json_object_get_string(desc));
+                                       json_object_put(desc);
+                               }
+                       }
+               }
+       }
+
+error:
+       json_object_put(apps);
+       return result;
+}
+
+/*
+ * Get the state of the runner of 'runid'.
+ *
+ * Returns the state or NULL in case of success
+ */
+struct json_object *afm_urun_state(struct afm_udb *db, int runid)
+{
+       int i, n, isuser, pid;
+       char *dpath;
+       const char *udpath;
+       const char *id;
+       const char *state;
+       struct json_object *appli;
+       struct json_object *apps;
+       struct json_object *result;
+
+       result = NULL;
+
+       /* get the dpath */
+       dpath = systemd_unit_dpath_by_pid(1 /* TODO: isuser? */, (unsigned)runid);
+       if (!dpath) {
+               result = NULL;
+               errno = EINVAL;
+               WARNING("searched runid %d not found", runid);
+       } else {
+               /* search in the base */
+               apps = afm_udb_applications_private(db);
+               n = json_object_array_length(apps);
+               for (i = 0 ; i < n ; i++) {
+                       appli = json_object_array_get_idx(apps, i);
+                       if (appli
+                        && get_basis(appli, &isuser, &udpath, 0) >= 0
+                        && !strcmp(dpath, udpath)
+                        && j_read_string_at(appli, "id", &id)) {
+                               pid = systemd_unit_pid_of_dpath(isuser, udpath);
+                               state = systemd_unit_state_of_dpath(isuser, dpath);
+                               result = mkstate(id, runid, pid, state);
+                               goto end;
+                       }
+               }
+               result = NULL;
+               errno = ENOENT;
+               WARNING("searched runid %d of dpath %s isn't an applications", runid, dpath);
+end:
+               json_object_put(apps);
+               free(dpath);    
+       }
+
+       return result;
+}
+
diff --git a/src/afm-urun.h b/src/afm-urun.h
new file mode 100644 (file)
index 0000000..7f85e0f
--- /dev/null
@@ -0,0 +1,28 @@
+/*
+ Copyright 2015, 2016, 2017 IoT.bzh
+
+ author: José Bollo <jose.bollo@iot.bzh>
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+*/
+
+struct afm_udb;
+
+extern int afm_urun_start(struct json_object *appli);
+extern int afm_urun_once(struct json_object *appli);
+extern int afm_urun_terminate(int runid);
+extern int afm_urun_pause(int runid);
+extern int afm_urun_resume(int runid);
+extern struct json_object *afm_urun_list(struct afm_udb *db);
+extern struct json_object *afm_urun_state(struct afm_udb *db, int runid);
+
index 7c7824d..c75aadb 100644 (file)
 #include "utils-jbus.h"
 #include "utils-json.h"
 #include "afm.h"
+#include "afm-launch-mode.h"
 #ifdef LEGACY_MODE_WITHOUT_SYSTEMD
 # include "afm-db.h"
+#include "afm-run.h"
 #else
 # include "afm-udb.h"
+#include "afm-urun.h"
 #endif
-#include "afm-launch-mode.h"
-#include "afm-run.h"
 
 /*
  * name of the application
@@ -254,7 +255,11 @@ static void on_start(struct sd_bus_message *smsg, struct json_object *obj, void
 
        /* launch the application */
        uri = NULL;
+#ifdef LEGACY_MODE_WITHOUT_SYSTEMD
        runid = afm_run_start(appli, mode, &uri);
+#else
+       runid = afm_urun_start(appli);
+#endif
        if (runid <= 0) {
                jbus_reply_error_s(smsg, error_cant_start);
                free(uri);
@@ -275,7 +280,11 @@ static void on_start(struct sd_bus_message *smsg, struct json_object *obj, void
                                        && j_add_string(resp, "uri", uri))
                jbus_reply_j(smsg, resp);
        else {
+#ifdef LEGACY_MODE_WITHOUT_SYSTEMD
                afm_run_terminate(runid);
+#else
+               afm_urun_terminate(runid);
+#endif
                jbus_reply_error_s(smsg, error_system);
        }
        json_object_put(resp);
@@ -310,14 +319,22 @@ static void on_once(struct sd_bus_message *smsg, struct json_object *obj, void *
        }
 
        /* launch the application */
+#ifdef LEGACY_MODE_WITHOUT_SYSTEMD
        runid = afm_run_once(appli);
+#else
+       runid = afm_urun_once(appli);
+#endif
        if (runid <= 0) {
                jbus_reply_error_s(smsg, error_cant_start);
                return;
        }
 
        /* returns the state */
+#ifdef LEGACY_MODE_WITHOUT_SYSTEMD
        resp = afm_run_state(runid);
+#else
+       resp = afm_urun_state(afudb, runid);
+#endif
        reply(smsg, resp, error_not_found);
        json_object_put(resp);
 }
@@ -329,7 +346,11 @@ static void on_pause(struct sd_bus_message *smsg, struct json_object *obj, void
 {
        int runid, status;
        if (onrunid(smsg, obj, "pause", &runid)) {
+#ifdef LEGACY_MODE_WITHOUT_SYSTEMD
                status = afm_run_pause(runid);
+#else
+               status = afm_urun_pause(runid);
+#endif
                reply_status(smsg, status, error_not_found);
        }
 }
@@ -341,7 +362,11 @@ static void on_resume(struct sd_bus_message *smsg, struct json_object *obj, void
 {
        int runid, status;
        if (onrunid(smsg, obj, "resume", &runid)) {
+#ifdef LEGACY_MODE_WITHOUT_SYSTEMD
                status = afm_run_resume(runid);
+#else
+               status = afm_urun_resume(runid);
+#endif
                reply_status(smsg, status, error_not_found);
        }
 }
@@ -371,7 +396,11 @@ static void on_terminate(struct sd_bus_message *smsg, struct json_object *obj, v
 {
        int runid, status;
        if (onrunid(smsg, obj, "terminate", &runid)) {
+#ifdef LEGACY_MODE_WITHOUT_SYSTEMD
                status = afm_run_terminate(runid);
+#else
+               status = afm_urun_terminate(runid);
+#endif
                reply_status(smsg, status, error_not_found);
        }
 }
@@ -383,7 +412,11 @@ static void on_runners(struct sd_bus_message *smsg, struct json_object *obj, voi
 {
        struct json_object *resp;
        INFO("method runners called");
+#ifdef LEGACY_MODE_WITHOUT_SYSTEMD
        resp = afm_run_list();
+#else
+       resp = afm_urun_list(afudb);
+#endif
        jbus_reply_j(smsg, resp);
        json_object_put(resp);
 }
@@ -396,7 +429,11 @@ static void on_state(struct sd_bus_message *smsg, struct json_object *obj, void
        int runid;
        struct json_object *resp;
        if (onrunid(smsg, obj, "state", &runid)) {
+#ifdef LEGACY_MODE_WITHOUT_SYSTEMD
                resp = afm_run_state(runid);
+#else
+               resp = afm_urun_state(afudb, runid);
+#endif
                reply(smsg, resp, error_not_found);
                json_object_put(resp);
        }
@@ -578,6 +615,7 @@ int main(int ac, char **av)
        /* init random generator */
        srandom((unsigned int)time(NULL));
 
+#ifdef LEGACY_MODE_WITHOUT_SYSTEMD
        /* init runners */
        if (afm_run_init()) {
                ERROR("afm_run_init failed");
@@ -585,7 +623,6 @@ int main(int ac, char **av)
        }
 
        /* init framework */
-#ifdef LEGACY_MODE_WITHOUT_SYSTEMD
        afdb = afm_db_create();
        if (!afdb) {
                ERROR("afm_db_create failed");
@@ -621,6 +658,7 @@ int main(int ac, char **av)
                return 1;
        }
 #else
+       /* init database */
        afudb = afm_udb_create(0, 1, "afm-appli-");
        if (!afudb) {
                ERROR("afm_udb_create failed");
@@ -717,17 +755,3 @@ int main(int ac, char **av)
        return 0;
 }
 
-
-
-
-
-
-
-
-
-
-
-
-
-
-
index d804144..830fec6 100644 (file)
 # include <systemd/sd-bus-protocol.h>
 #else
   struct sd_bus;
-# define sd_bus_default_user(p)   ((*(p)=NULL),(-ENOTSUP))
-# define sd_bus_default_system(p) ((*(p)=NULL),(-ENOTSUP))
-# define sd_bus_call_method(...)  (-ENOTSUP)
+  struct sd_bus_message;
+  typedef struct { const char *name; const char *message; } sd_bus_error;
+# define sd_bus_default_user(p)           ((*(p)=NULL),(-ENOTSUP))
+# define sd_bus_default_system(p)         ((*(p)=NULL),(-ENOTSUP))
+# define sd_bus_call_method(...)          (-ENOTSUP)
+# define SD_BUS_ERROR_NULL                {NULL,NULL}
+# define sd_bus_message_read_basic(...)   (-ENOTSUP)
+# define sd_bus_message_unref(...)        (NULL)
+# define sd_bus_get_property_string(...)  (-ENOTSUP)
+# define sd_bus_get_property_trivial(...) (-ENOTSUP)
 #endif
 
 #include "utils-systemd.h"
 static const char sdb_path[] = "/org/freedesktop/systemd1";
 static const char sdb_destination[] = "org.freedesktop.systemd1";
 static const char sdbi_manager[] = "org.freedesktop.systemd1.Manager";
+static const char sdbi_unit[] = "org.freedesktop.systemd1.Unit";
+static const char sdbi_service[] = "org.freedesktop.systemd1.Service";
 static const char sdbm_reload[] = "Reload";
+static const char sdbm_start_unit[] = "StartUnit";
+static const char sdbm_stop_unit[] = "StopUnit";
+static const char sdbm_start[] = "Start";
+static const char sdbm_stop[] = "Stop";
+static const char sdbm_get_unit[] = "GetUnit";
+static const char sdbm_get_unit_by_pid[] = "GetUnitByPID";
+static const char sdbm_load_unit[] = "LoadUnit";
+static const char sdbp_active_state[] = "ActiveState";
+static const char sdbp_exec_main_pid[] = "ExecMainPID";
+
+const char SysD_State_Inactive[] = "inactive";
+const char SysD_State_Activating[] = "activating";
+const char SysD_State_Active[] = "active";
+const char SysD_State_Deactivating[] = "deactivating";
+const char SysD_State_Reloading[] = "reloading";
+const char SysD_State_Failed[] = "failed";
 
 static struct sd_bus *sysbus;
 static struct sd_bus *usrbus;
@@ -80,20 +105,227 @@ static int get_bus(int isuser, struct sd_bus **ret)
        struct sd_bus *bus;
 
        bus = isuser ? usrbus : sysbus;
-       if (bus) {
+       if (bus)
                *ret = bus;
-               rc = 0;
-       } else if (isuser) {
+       else if (isuser) {
                rc = sd_bus_default_user(ret);
-               if (!rc)
-                       usrbus = *ret;
+               if (rc < 0)
+                       goto error;
+               usrbus = *ret;
        } else {
                rc = sd_bus_default_system(ret);
-               if (!rc)
-                       sysbus = *ret;
+               if (rc < 0)
+                       goto error;
+               sysbus = *ret;
        }
+       return 0;
+error:
        return sderr2errno(rc);
 }
+
+#if 0
+/********************************************************************
+ * routines for escaping unit names to compute dbus path of units
+ *******************************************************************/
+/*
+ * Should the char 'c' be escaped?
+ */
+static inline int should_escape_for_path(char c)
+{
+       if (c >= 'A') {
+               return c <= (c >= 'a' ? 'z' : 'Z');
+       } else {
+               return c >= '0' && c <= '9';
+       }
+}
+
+/*
+ * ascii char for the hexadecimal digit 'x'
+ */
+static inline char d2h(int x)
+{
+       return (char)(x + (x > 9 ? ('a' - 10) : '0'));
+}
+
+/*
+ * escapes in 'path' of 'pathlen' the 'unit'
+ * returns 0 in case of success or -1 in case of error
+ */
+static int unit_escape_for_path(char *path, size_t pathlen, const char *unit)
+{
+       size_t r, w;
+       char c;
+
+       c = unit[r = w = 0];
+       while (c) {
+               if (should_escape_for_path(c)) {
+                       if (w + 2 >= pathlen)
+                               goto toolong;
+                       path[w++] = '_';
+                       path[w++] = d2h((c >> 4) & 15);;
+                       path[w++] = d2h(c & 15);
+               } else {
+                       if (w >= pathlen)
+                               goto toolong;
+                       path[w++] = c;
+               }
+               c = unit[++r];
+       }
+       if (w >= pathlen)
+               goto toolong;
+       path[w] = 0;
+       return 0;
+toolong:
+       return seterrno(ENAMETOOLONG);
+}
+#endif
+
+/********************************************************************
+ * Routines for getting paths
+ *******************************************************************/
+
+static char *get_dpath(struct sd_bus_message *msg)
+{
+       int rc;
+       const char *reply;
+       char *result;
+
+       rc = sd_bus_message_read_basic(msg, 'o', &reply);
+       rc = sderr2errno(rc);
+       if (rc < 0)
+               result = NULL;
+       else {
+               result = strdup(reply);
+               if (!result)
+                       errno = ENOMEM;
+       }
+       sd_bus_message_unref(msg);
+       return result;
+}
+
+static char *get_unit_dpath(struct sd_bus *bus, const char *unit, int load)
+{
+       int rc;
+       struct sd_bus_message *ret = NULL;
+       sd_bus_error err = SD_BUS_ERROR_NULL;
+
+       rc = sd_bus_call_method(bus, sdb_destination, sdb_path, sdbi_manager, load ? sdbm_load_unit : sdbm_get_unit, &err, &ret, "s", unit);
+       if (rc < 0)
+               goto error;
+
+       return get_dpath(ret);
+error:
+       sd_bus_message_unref(ret);
+       return NULL;
+}
+
+static char *get_unit_dpath_by_pid(struct sd_bus *bus, unsigned pid)
+{
+       int rc;
+       struct sd_bus_message *ret = NULL;
+       sd_bus_error err = SD_BUS_ERROR_NULL;
+
+       rc = sd_bus_call_method(bus, sdb_destination, sdb_path, sdbi_manager, sdbm_get_unit_by_pid, &err, &ret, "u", pid);
+       if (rc < 0)
+               goto error;
+
+       return get_dpath(ret);
+error:
+       sd_bus_message_unref(ret);
+       return NULL;
+}
+
+static int unit_pid(struct sd_bus *bus, const char *dpath)
+{
+       int rc;
+       unsigned u = 0;
+       sd_bus_error err = SD_BUS_ERROR_NULL;
+
+       rc = sd_bus_get_property_trivial(bus, sdb_destination, dpath, sdbi_service, sdbp_exec_main_pid, &err, 'u', &u);
+       return rc < 0 ? rc : (int)u;
+}
+
+static const char *unit_state(struct sd_bus *bus, const char *dpath)
+{
+       int rc;
+       char *st;
+       const char *resu;
+       sd_bus_error err = SD_BUS_ERROR_NULL;
+
+       rc = sd_bus_get_property_string(bus, sdb_destination, dpath, sdbi_unit, sdbp_active_state, &err, &st);
+       if (rc < 0) {
+               errno = -rc;
+               resu = NULL;
+       } else {
+               if (!strcmp(st, SysD_State_Active))
+                       resu = SysD_State_Active;
+               else if (!strcmp(st, SysD_State_Reloading))
+                       resu = SysD_State_Reloading;
+               else if (!strcmp(st, SysD_State_Inactive))
+                       resu = SysD_State_Inactive;
+               else if (!strcmp(st, SysD_State_Failed))
+                       resu = SysD_State_Failed;
+               else if (!strcmp(st, SysD_State_Activating))
+                       resu = SysD_State_Activating;
+               else if (!strcmp(st, SysD_State_Deactivating))
+                       resu = SysD_State_Deactivating;
+               else {
+                       errno = EBADMSG;
+                       resu = NULL;
+               }
+               free(st);
+       }
+       return resu;
+}
+
+static int unit_start(struct sd_bus *bus, const char *dpath)
+{
+       int rc;
+       struct sd_bus_message *ret = NULL;
+       sd_bus_error err = SD_BUS_ERROR_NULL;
+
+       rc = sd_bus_call_method(bus, sdb_destination, dpath, sdbi_unit, sdbm_start, &err, &ret, "s", "replace");
+       sd_bus_message_unref(ret);
+       return rc;
+}
+
+static int unit_stop(struct sd_bus *bus, const char *dpath)
+{
+       int rc;
+       struct sd_bus_message *ret = NULL;
+       sd_bus_error err = SD_BUS_ERROR_NULL;
+
+       rc = sd_bus_call_method(bus, sdb_destination, dpath, sdbi_unit, sdbm_stop, &err, &ret, "s", "replace");
+       sd_bus_message_unref(ret);
+       return rc;
+}
+
+static int unit_start_name(struct sd_bus *bus, const char *name)
+{
+       int rc;
+       struct sd_bus_message *ret = NULL;
+       sd_bus_error err = SD_BUS_ERROR_NULL;
+
+       rc = sd_bus_call_method(bus, sdb_destination, sdb_path, sdbi_manager, sdbm_start_unit, &err, &ret, "ss", name, "replace");
+       sd_bus_message_unref(ret);
+       return rc;
+}
+
+static int unit_stop_name(struct sd_bus *bus, const char *name)
+{
+       int rc;
+       struct sd_bus_message *ret = NULL;
+       sd_bus_error err = SD_BUS_ERROR_NULL;
+
+       rc = sd_bus_call_method(bus, sdb_destination, sdb_path, sdbi_manager, sdbm_stop_unit, &err, &ret, "ss", name, "replace");
+       sd_bus_message_unref(ret);
+       return rc;
+}
+
+/********************************************************************
+ *
+ *******************************************************************/
+
 static int check_snprintf_result(int rc, size_t buflen)
 {
        return (rc >= 0 && (size_t)rc >= buflen) ? seterrno(ENAMETOOLONG) : rc;
@@ -101,7 +333,7 @@ static int check_snprintf_result(int rc, size_t buflen)
 
 int systemd_get_units_dir(char *path, size_t pathlen, int isuser)
 {
-       int rc = snprintf(path, pathlen, "%s/%s", 
+       int rc = snprintf(path, pathlen, "%s/%s",
                        SYSTEMD_UNITS_ROOT,
                        isuser ? "user" : "system");
 
@@ -110,7 +342,7 @@ int systemd_get_units_dir(char *path, size_t pathlen, int isuser)
 
 int systemd_get_unit_path(char *path, size_t pathlen, int isuser, const char *unit, const char *uext)
 {
-       int rc = snprintf(path, pathlen, "%s/%s/%s.%s", 
+       int rc = snprintf(path, pathlen, "%s/%s/%s.%s",
                        SYSTEMD_UNITS_ROOT,
                        isuser ? "user" : "system",
                        unit,
@@ -121,7 +353,7 @@ int systemd_get_unit_path(char *path, size_t pathlen, int isuser, const char *un
 
 int systemd_get_wants_path(char *path, size_t pathlen, int isuser, const char *wanter, const char *unit, const char *uext)
 {
-       int rc = snprintf(path, pathlen, "%s/%s/%s.wants/%s.%s", 
+       int rc = snprintf(path, pathlen, "%s/%s/%s.wants/%s.%s",
                        SYSTEMD_UNITS_ROOT,
                        isuser ? "user" : "system",
                        wanter,
@@ -142,12 +374,15 @@ int systemd_daemon_reload(int isuser)
 {
        int rc;
        struct sd_bus *bus;
+       struct sd_bus_message *ret = NULL;
+       sd_bus_error err = SD_BUS_ERROR_NULL;
 
        rc = get_bus(isuser, &bus);
-       if (!rc) {
+       if (rc >= 0) {
                /* TODO: asynchronous bind... */
                /* TODO: more diagnostic... */
-               rc = sd_bus_call_method(bus, sdb_destination, sdb_path, sdbi_manager, sdbm_reload, NULL, NULL, NULL);
+               rc = sd_bus_call_method(bus, sdb_destination, sdb_path, sdbi_manager, sdbm_reload, &err, &ret, NULL);
+               sd_bus_message_unref(ret);
        }
        return rc;
 }
@@ -220,3 +455,94 @@ int systemd_unit_list_all(int (*callback)(void *closure, const char *name, const
        return systemd_unit_list(1, callback, closure) ? : systemd_unit_list(0, callback, closure);
 }
 
+char *systemd_unit_dpath_by_name(int isuser, const char *name, int load)
+{
+       struct sd_bus *bus;
+
+       return get_bus(isuser, &bus) < 0 ? NULL : get_unit_dpath(bus, name, load);
+}
+
+char *systemd_unit_dpath_by_pid(int isuser, unsigned pid)
+{
+       struct sd_bus *bus;
+
+       return get_bus(isuser, &bus) < 0 ? NULL : get_unit_dpath_by_pid(bus, pid);
+}
+
+int systemd_unit_start_dpath(int isuser, const char *dpath)
+{
+       int rc;
+       struct sd_bus *bus;
+
+       rc = get_bus(isuser, &bus);
+       return rc < 0 ? rc : unit_start(bus, dpath);
+}
+
+int systemd_unit_stop_dpath(int isuser, const char *dpath)
+{
+       int rc;
+       struct sd_bus *bus;
+
+       rc = get_bus(isuser, &bus);
+       return rc < 0 ? rc : unit_stop(bus, dpath);
+}
+
+int systemd_unit_start_name(int isuser, const char *name)
+{
+       int rc;
+       struct sd_bus *bus;
+
+       rc = get_bus(isuser, &bus);
+       if (rc >= 0)
+               rc = unit_start_name(bus, name);
+       return rc;
+}
+
+int systemd_unit_stop_name(int isuser, const char *name)
+{
+       int rc;
+       struct sd_bus *bus;
+
+       rc = get_bus(isuser, &bus);
+       if (rc >= 0)
+               rc = unit_stop_name(bus, name);
+       return rc;
+}
+
+int systemd_unit_stop_pid(int isuser, unsigned pid)
+{
+       int rc;
+       struct sd_bus *bus;
+       char *dpath;
+
+       rc = get_bus(isuser, &bus);
+       if (rc >= 0) {
+               dpath = get_unit_dpath_by_pid(bus, pid);
+               if (!dpath)
+                       rc = -1;
+               else {
+                       rc = unit_stop(bus, dpath);
+                       free(dpath);
+               }
+       }
+       return rc;
+}
+
+int systemd_unit_pid_of_dpath(int isuser, const char *dpath)
+{
+       int rc;
+       struct sd_bus *bus;
+
+       rc = get_bus(isuser, &bus);
+       return rc < 0 ? rc : unit_pid(bus, dpath);
+}
+
+const char *systemd_unit_state_of_dpath(int isuser, const char *dpath)
+{
+       int rc;
+       struct sd_bus *bus;
+
+       rc = get_bus(isuser, &bus);
+       return rc < 0 ? NULL : unit_state(bus, dpath);
+}
+
index f704d45..b4c3b8c 100644 (file)
 
 #pragma once
 
+extern const char SysD_State_Inactive[];
+extern const char SysD_State_Activating[];
+extern const char SysD_State_Active[];
+extern const char SysD_State_Deactivating[];
+extern const char SysD_State_Reloading[];
+extern const char SysD_State_Failed[];
+
 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 char *systemd_unit_dpath_by_name(int isuser, const char *name, int load);
+extern char *systemd_unit_dpath_by_pid(int isuser, unsigned pid);
+
+extern int systemd_unit_start_dpath(int isuser, const char *dpath);
+extern int systemd_unit_stop_dpath(int isuser, const char *dpath);
+
+extern int systemd_unit_start_name(int isuser, const char *name);
+extern int systemd_unit_stop_name(int isuser, const char *name);
+extern int systemd_unit_stop_pid(int isuser, unsigned pid);
+
+extern int systemd_unit_pid_of_dpath(int isuser, const char *dpath);
+extern const char *systemd_unit_state_of_dpath(int isuser, const char *dpath);
+
 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);