From b6afa1aa893544b459cb767cc5a2ad8d2148228c Mon Sep 17 00:00:00 2001 From: =?utf8?q?Jos=C3=A9=20Bollo?= Date: Thu, 16 Mar 2017 09:39:08 +0100 Subject: [PATCH] utils-systemd: implement start/stop of units MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit This implementation is very basic and must be improved. Change-Id: Id1540e646c83285c61849092bbd8fb645c0954ed Signed-off-by: José Bollo --- src/CMakeLists.txt | 1 + src/afm-urun.c | 357 ++++++++++++++++++++++++++++++++++++++++++++++++++ src/afm-urun.h | 28 ++++ src/afm-user-daemon.c | 58 +++++--- src/utils-systemd.c | 356 ++++++++++++++++++++++++++++++++++++++++++++++--- src/utils-systemd.h | 20 +++ 6 files changed, 788 insertions(+), 32 deletions(-) create mode 100644 src/afm-urun.c create mode 100644 src/afm-urun.h diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 903a7de..bbcf04b 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -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 index 0000000..48f8436 --- /dev/null +++ b/src/afm-urun.c @@ -0,0 +1,357 @@ +/* + Copyright 2015, 2016, 2017 IoT.bzh + + author: José Bollo + + 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 +#include +#include +#include +#include +#include +#include +#include + +#include + +#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 index 0000000..7f85e0f --- /dev/null +++ b/src/afm-urun.h @@ -0,0 +1,28 @@ +/* + Copyright 2015, 2016, 2017 IoT.bzh + + author: José Bollo + + 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); + diff --git a/src/afm-user-daemon.c b/src/afm-user-daemon.c index 7c7824d..c75aadb 100644 --- a/src/afm-user-daemon.c +++ b/src/afm-user-daemon.c @@ -30,13 +30,14 @@ #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; } - - - - - - - - - - - - - - diff --git a/src/utils-systemd.c b/src/utils-systemd.c index d804144..830fec6 100644 --- a/src/utils-systemd.c +++ b/src/utils-systemd.c @@ -32,9 +32,16 @@ # include #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" @@ -46,7 +53,25 @@ 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); +} + diff --git a/src/utils-systemd.h b/src/utils-systemd.h index f704d45..b4c3b8c 100644 --- a/src/utils-systemd.h +++ b/src/utils-systemd.h @@ -18,12 +18,32 @@ #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); -- 2.16.6