libgdbus: add api to start or stop systemd unit 63/197363/15 submit/tizen/20190208.064159
authorsanghyeok.oh <sanghyeok.oh@samsung.com>
Thu, 10 Jan 2019 12:48:35 +0000 (21:48 +0900)
committersanghyeok.oh <sanghyeok.oh@samsung.com>
Thu, 31 Jan 2019 00:22:58 +0000 (09:22 +0900)
Change-Id: Iaa17cc32cff1b763c5ed89ddc69582f9b70e535a
Signed-off-by: sanghyeok.oh <sanghyeok.oh@samsung.com>
packaging/libsyscommon.spec
src/Makefile.am [deleted file]
src/libgdbus/CMakeLists.txt
src/libgdbus/dbus-system.c
src/libgdbus/dbus-system.h
src/libgdbus/dbus-systemd.c [new file with mode: 0644]
src/libgdbus/dbus-systemd.h [new file with mode: 0644]

index 3dc79ff..fb7114f 100644 (file)
@@ -77,4 +77,5 @@ touch debugsources.list
 %license LICENSE.Apache-2.0
 %{_libdir}/libgdbus.so
 %{_includedir}/libgdbus/dbus-system.h
+%{_includedir}/libgdbus/dbus-systemd.h
 %{_libdir}/pkgconfig/libgdbus.pc
diff --git a/src/Makefile.am b/src/Makefile.am
deleted file mode 100644 (file)
index bbee945..0000000
+++ /dev/null
@@ -1,92 +0,0 @@
-AUTOMAKE_OPTIONS = color-tests parallel-tests
-
-pkgconfiglibdir=$(libdir)/pkgconfig
-
-CLEANFILES =
-EXTRA_DIST =
-
-lib_LTLIBRARIES =
-noinst_LTLIBRARIES =
-noinst_DATA =
-pkgconfiglib_DATA =
-
-check_PROGRAMS =
-check_DATA =
-tests=
-#noinst_PROGRAMS = $(tests)
-#check_PROGRAMS += $(tests)
-#TESTS = $(tests)
-
-DEFAULT_CFLAGS = \
-       $(OUR_CFLAGS)
-
-DEFAULT_LDFLAGS = \
-       $(OUR_LDFLAGS)
-
-AM_CPPFLAGS = \
-       -include $(top_builddir)/config.h \
-       $(DEFAULT_CFLAGS)
-
-AM_CFLAGS = $(DEFAULT_CFLAGS)
-AM_LDFLAGS = $(DEFAULT_LDFLAGS)
-
-INSTALL_EXEC_HOOKS =
-UNINSTALL_EXEC_HOOKS =
-
-# ------------------------------------------------------------------------------
-pkgconfiglib_DATA += \
-       libgdbus/libgdbus.pc
-
-EXTRA_DIST += \
-       libgdbus/libgdbus.pc.in
-
-CLEANFILES += \
-       libgdbus/libgdbus.pc
-
-libgdbus_pkgincludedir=$(includedir)/libgdbus
-libgdbus_pkginclude_HEADERS =
-
-libgdbus_pkginclude_HEADERS += \
-       libgdbus/dbus-system.h
-
-lib_LTLIBRARIES += \
-       libgdbus.la
-
-libgdbus_la_SOURCES = \
-       libgdbus/dbus-system.c
-
-libgdbus_la_CFLAGS = \
-       $(AM_CFLAGS) \
-       $(GLIB_CFLAGS) \
-       $(GIO_UNIX_CFLAGS) \
-       $(GIO_CFLAGS)
-
-libgdbus_la_LIBADD = \
-       -lrt \
-       $(GIO_LIBS) \
-       $(GIO_UNIX_LIBS)
-
-# ------------------------------------------------------------------------------
-substitutions = \
-       '|PACKAGE_VERSION=$(PACKAGE_VERSION)|' \
-       '|PACKAGE_NAME=$(PACKAGE_NAME)|' \
-       '|PACKAGE_URL=$(PACKAGE_URL)|' \
-       '|LIBGDBUS_PC_REQUIRES=$(LIBGDBUS_PC_REQUIRES)|' \
-       '|LIBGDBUS_PC_CFLAGS=$(LIBGDBUS_PC_CFLAGS)|' \
-       '|LIBGDBUS_PC_LIBS=$(LIBGDBUS_PC_LIBS)|' \
-       '|includedir=$(includedir)|' \
-       '|VERSION=$(VERSION)|'
-
-SED_PROCESS = \
-       $(AM_V_GEN)$(MKDIR_P) $(dir $@) && \
-       $(SED) $(subst '|,-e 's|@,$(subst =,\@|,$(subst |',|g',$(substitutions)))) \
-               < $< > $@
-
-%.pc: %.pc.in
-       $(SED_PROCESS)
-
-%.c: %.gperf
-       $(AM_V_at)$(MKDIR_P) $(dir $@)
-       $(AM_V_GPERF)$(GPERF) < $< > $@
-
-install-exec-hook: $(INSTALL_EXEC_HOOKS)
index 327ea2b..06f1bc2 100644 (file)
@@ -9,9 +9,11 @@ SET(VERSION 4.1)
 
 SET(libgdbus_SRCS
        dbus-system.c
+       dbus-systemd.c
 )
 SET(HEADERS
        dbus-system.h
+       dbus-systemd.h
 )
 
 # CHECK PKG
index 42f061b..e59da08 100644 (file)
@@ -697,21 +697,20 @@ static int _check_brace(const char * expr)
                                return -1;
 
                        if (expr[i] == ')') {
-                               if (ch == '(') {
+                               if (ch == '(')
                                        --qucnt;
-                               else
+                               else
                                        return -1;
                        } else if (expr[i] == '}') {
-                               if (ch == '{') {
+                               if (ch == '{')
                                        --qucnt;
-                               else
+                               else
                                        return -1;
                        } else
                                return -1;
 
-                       if (qucnt == 0) {
+                       if (qucnt == 0)
                                return i + 1;
-                       }
                }
        }
 
@@ -1231,9 +1230,9 @@ int dbus_handle_unregister_dbus_object(dbus_handle_h handle, const char *obj_pat
        dbus_object_handle_s *oh = NULL;
        int ret = 0;
 
-       if (!obj_path) {
+       if (!obj_path)
                return -1;
-       }
+
        if (!dh) {
                dh = _dbus_handle_get_default_connection();
                if (!dh) {
@@ -1507,6 +1506,195 @@ void unsubscribe_dbus_signal(dbus_handle_h handle, guint id)
        g_dbus_connection_signal_unsubscribe(dh->conn, id);
 }
 
+static void _signal_reply_sync_cb(GDBusConnection  *conn,
+       const gchar *sender,
+       const gchar *path,
+       const gchar *iface,
+       const gchar *name,
+       GVariant *param,
+       gpointer data)
+{
+       sig_ctx *ctx = data;
+       if (!ctx) {
+               _E("user data is null");
+               assert(0);
+       }
+
+       ctx->param = g_variant_ref(param);
+       ctx->quit_reason = CTX_QUIT_NORMAL;
+
+       if (ctx->timeout_src) {
+               g_source_destroy(ctx->timeout_src);
+               ctx->timeout_src = NULL;
+       }
+       g_main_loop_quit(ctx->loop);
+}
+
+sig_ctx *dbus_handle_new_signal_ctx(void)
+{
+       sig_ctx *ctx;
+
+       ctx = (sig_ctx *)malloc(sizeof(sig_ctx));
+       if (!ctx) {
+               _E("failed to alloc mem");
+               return NULL;
+       }
+
+       ctx->sig_id = 0;
+
+       ctx->context = g_main_context_new();
+       if (!ctx->context) {
+               _E("failed to alloc context");
+               free(ctx);
+               return NULL;
+       }
+       ctx->loop = g_main_loop_new(ctx->context, FALSE);
+       if (!ctx->loop) {
+               _E("failed to alloc main loop");
+               g_main_context_unref(ctx->context);
+               free(ctx);
+               return NULL;
+       }
+       ctx->timeout_src = NULL;
+       ctx->param = NULL;
+       ctx->quit_reason = 0;
+       ctx->user_data = NULL;
+
+       return ctx;
+}
+
+void dbus_handle_free_signal_ctx(sig_ctx *ctx)
+{
+       if (!ctx)
+               return ;
+
+       if (ctx->param) {
+               g_variant_unref(ctx->param);
+               ctx->param = NULL;
+       }
+       if (ctx->sig_id) {
+               unsubscribe_dbus_signal(NULL, ctx->sig_id);
+               ctx->sig_id = 0;
+       }
+       if (ctx->timeout_src) {
+               g_source_destroy(ctx->timeout_src);
+               ctx->timeout_src = NULL;
+       }
+       if (ctx->context) {
+               g_main_context_pop_thread_default(ctx->context);
+               g_main_context_unref(ctx->context);
+               ctx->context = NULL;
+       }
+       if (ctx->loop) {
+               g_main_loop_unref(ctx->loop);
+               ctx->loop = NULL;
+       }
+       free(ctx);
+}
+
+static gboolean _cb_ctx_timeout(gpointer user_data)
+{
+       sig_ctx *ctx = user_data;
+
+       if (!ctx) {
+               _E("user_data is null");
+               return FALSE;
+       }
+
+       ctx->quit_reason = CTX_QUIT_TIMEOUT;
+       /* if cb return FALSE, source will be destroyed */
+       ctx->timeout_src = NULL;
+
+       unsubscribe_dbus_signal(NULL, ctx->sig_id);
+       ctx->sig_id = 0;
+
+       g_main_loop_quit(ctx->loop);
+
+       return FALSE;
+}
+
+#define CTX_MAX_TIMEOUT 25000
+
+int dbus_handle_signal_ctx_add_timeout(sig_ctx *ctx, int timeout_msec)
+{
+       GSource *src = NULL;
+       guint id = 0;
+
+       if (!ctx)
+               return -EINVAL;
+       if (timeout_msec < -1)
+               return -EINVAL;
+
+       if (timeout_msec == -1 || timeout_msec >= CTX_MAX_TIMEOUT)
+               timeout_msec = CTX_MAX_TIMEOUT;
+
+       src = g_timeout_source_new(timeout_msec);
+       if (!src)
+               return -ENOMEM;
+
+       g_source_set_callback(src, _cb_ctx_timeout, ctx, NULL);
+       g_source_attach(src, ctx->context);
+
+       ctx->timeout_src = src;
+
+       return 0;
+}
+
+guint subscribe_dbus_signal_ctx(dbus_handle_h handle, sig_ctx *ctx,
+               const char *sender,     const char *path,
+               const char *iface, const char *name,
+               GDBusSignalCallback _cb)
+{
+       dcl_dbus_handle();
+
+       if (!ctx) {
+               _E("wrong param ctx is null");
+               return 0;
+       }
+
+       if (!dh) {
+               dh = _dbus_handle_get_default_connection();
+               if (!dh) {
+                       _E("failed to get default connection, bustype:%d",
+                                       (int)dbus_handle_get_default_bus_type());
+                       return 0;
+               }
+       }
+
+       if (!dh->conn) {
+               _E("connection is null. check bus status");
+               return 0;
+       }
+
+       if (!_cb)
+               _cb = _signal_reply_sync_cb;
+
+       /* change context before subscribe */
+       g_main_context_push_thread_default(ctx->context);
+
+       ctx->sig_id = g_dbus_connection_signal_subscribe(dh->conn,
+                       sender, iface, name, path, NULL,
+                       G_DBUS_SIGNAL_FLAGS_NONE, _cb,
+                       (void*)ctx, NULL);
+
+       if (!ctx->sig_id)
+               _E("failed to subscribe signal");
+
+       return ctx->sig_id;
+}
+
+int dbus_handle_signal_ctx_wait(sig_ctx *ctx)
+{
+       if (!ctx || !ctx->loop)
+               return -EINVAL;
+
+       g_main_loop_run(ctx->loop);
+
+       _D("quit g_main_loop");
+
+       return ctx->quit_reason;
+}
+
 int _check_type_string_is_container(const char *signature)
 {
        if (!signature)
@@ -1574,9 +1762,8 @@ static GVariant* _append_variant(const char *signature, const char *param[])
                snprintf(container, sizeof(container) - 1, "(%s)", signature);
                sig = container;
        }
-       if (!g_variant_type_is_container(G_VARIANT_TYPE(sig))) {
+       if (!g_variant_type_is_container(G_VARIANT_TYPE(sig)))
                _E("signature (%s) is not container type", signature);
-       }
 
        builder = g_variant_builder_new(G_VARIANT_TYPE(sig));
        len = strlen(sig);
index a9df43f..ef5ba75 100644 (file)
@@ -481,6 +481,23 @@ int dbus_handle_register_dbus_object_all(dbus_handle_h handle);
 guint subscribe_dbus_signal(dbus_handle_h handle, const char *path, const char *iface, const char *name, GDBusSignalCallback cb, void *data, destroy_notified free_func);
 void unsubscribe_dbus_signal(dbus_handle_h handle, guint id);
 
+enum ctx_quit_reason {CTX_QUIT_UNKNOWN, CTX_QUIT_NORMAL, CTX_QUIT_TIMEOUT};
+
+typedef struct {
+       guint sig_id;
+       GMainContext *context;
+       GMainLoop *loop;
+       GSource *timeout_src;
+       GVariant *param;
+       enum ctx_quit_reason quit_reason;
+       void *user_data;
+} sig_ctx;
+
+sig_ctx *dbus_handle_new_signal_ctx(void);
+void dbus_handle_free_signal_ctx(sig_ctx *ctx);
+guint subscribe_dbus_signal_ctx(dbus_handle_h handle, sig_ctx *ctx, const char *sender,        const char *path, const char *iface, const char *name, GDBusSignalCallback cb);
+int dbus_handle_signal_ctx_add_timeout(sig_ctx *ctx, int timeout);
+
 GVariant *dbus_handle_make_simple_array(const char *sig, int *param);
 
 int dbus_handle_broadcast_dbus_signal(const char *path, const char *iface, const char *name, const char *signature, const char *param[]);
diff --git a/src/libgdbus/dbus-systemd.c b/src/libgdbus/dbus-systemd.c
new file mode 100644 (file)
index 0000000..998468d
--- /dev/null
@@ -0,0 +1,481 @@
+/*
+ * libsyscommon
+ *
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd.
+ *
+ * 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.
+ */
+
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <libgdbus/dbus-system.h>
+
+#include "shared/log.h"
+
+#define SYSTEMD_DBUS_SERVICE   "org.freedesktop.systemd1"
+#define SYSTEMD_DBUS_PATH              "/org/freedesktop/systemd1"
+#define SYSTEMD_DBUS_UNIT_PATH "/org/freedesktop/systemd1/unit/"
+
+#define SYSTEMD_DBUS_MANAGER_IFACE     "org.freedesktop.systemd1.Manager"
+#define SYSTEMD_DBUS_UNIT_IFACE                "org.freedesktop.systemd1.Unit"
+#define SYSTEMD_DBUS_SERVICE_IFACE     "org.freedesktop.systemd1.Service"
+#define SYSTEMD_DBUS_TARGET_IFACE      "org.freedesktop.systemd1.Target"
+
+#define DBUS_IFACE_DBUS_PROPERTIES     "org.freedesktop.DBus.Properties"
+
+#define SUFFIX_SERVICE ".service"
+#define SUFFIX_SOCKET ".socket"
+#define SUFFIX_BUSNAME ".busname"
+#define SUFFIX_TARGET ".target"
+#define SUFFIX_DEVICE ".device"
+#define SUFFIX_MOUNT ".mount"
+#define SUFFIX_SWAP ".swap"
+#define SUFFIX_TIMER ".timer"
+#define SUFFIX_PATH ".path"
+#define SUFFIX_SLICE ".slice"
+#define SUFFIX_SCOPE ".scope"
+
+#define UNIT_NAME_MAX 256
+
+typedef struct {
+       char *job_id;
+       char *unit_name;
+} unitinfo;
+
+static void _cb_JobRemoved(GDBusConnection *conn,
+       const char *sender,
+       const char *path,
+       const char *iface,
+       const char *name,
+       GVariant *param,
+       gpointer data)
+{
+       sig_ctx *ctx = data;
+       gchar *job_id = NULL;
+       gchar *unit_name = NULL;
+       unitinfo *uinfo = NULL;
+
+       if (!ctx) {
+               _E("User data ctx is null");
+               return ;
+       }
+
+       uinfo = ctx->user_data;
+       if (!uinfo) {
+               _E("User_data uinfo is null");
+               return ;
+       }
+       if (!dh_get_param_from_var(param, "(uoss)", NULL, &job_id, &unit_name, NULL)) {
+               _E("Failed to get param");
+               return ;
+       }
+       if (strcmp(uinfo->job_id, job_id) || strcmp(uinfo->unit_name, unit_name)) {
+               _E("Not matched: job_id:%s, unit_name:%s", job_id, unit_name);
+               goto err;
+       }
+
+       /* otherwise, if matched signal, quit loop */
+
+       ctx->quit_reason = CTX_QUIT_NORMAL;
+       if (ctx->timeout_src) {
+               g_source_destroy(ctx->timeout_src);
+               ctx->timeout_src = NULL;
+       }
+
+       g_main_loop_quit(ctx->loop);
+
+err:
+       g_free(job_id);
+       g_free(unit_name);
+}
+
+static int _systemd_control_unit_sync(const char *method, const char *name, int timeout_msec)
+{
+       GVariant *reply = NULL;
+       gchar *objpath = NULL;
+       int ret = 0;
+       sig_ctx *ctx = NULL;
+       gchar *unit_name = NULL;
+       unitinfo uinfo;
+       int quit_reason;
+
+       ctx = dbus_handle_new_signal_ctx();
+       if (!ctx)
+               return -ENOMEM;
+
+       _I("Starting: %s %s", method, name);
+
+       /* synchronous siganl subscriptsion */
+       ret = subscribe_dbus_signal_ctx(NULL, ctx, SYSTEMD_DBUS_SERVICE, SYSTEMD_DBUS_PATH, SYSTEMD_DBUS_IFACE_MANAGER, "JobRemoved", _cb_JobRemoved);
+       if (ret == 0) {
+               ret = -1;
+               goto finish;
+       }
+
+       reply = dbus_handle_method_sync_with_reply_var(SYSTEMD_DBUS_DEST,
+               SYSTEMD_DBUS_PATH,
+               SYSTEMD_DBUS_MANAGER_IFACE,
+               method,
+               g_variant_new("(ss)", name, "replace"));
+       if (!reply || !dh_get_param_from_var(reply, "(o)", &objpath)) {
+               _E("fail (%s): no message", method);
+               ret = -EBADMSG;
+               goto finish;
+       }
+
+       uinfo.job_id = objpath;
+       uinfo.unit_name = name;
+       ctx->user_data = &uinfo;
+
+       /* set timeout */
+       ret = dbus_handle_signal_ctx_add_timeout(ctx, timeout_msec);
+       if (ret < 0) {
+               _E("Failed to set timeout, %d", ret);
+               goto finish;
+       }
+
+       /* run loop and wait signal callback */
+       quit_reason = dbus_handle_signal_ctx_wait(ctx);
+       if (quit_reason != CTX_QUIT_NORMAL) {
+               ret = -1;
+               _E("Failed to receive JobRemoved signal %d", quit_reason);
+               goto finish;
+       }
+
+       _I("Finished: %s %s", method, name);
+
+finish:
+       if (unit_name)
+               g_free(unit_name);
+
+       if (reply)
+               g_variant_unref(reply);
+       g_free(objpath);
+
+       dbus_handle_free_signal_ctx(ctx);
+
+       return ret;
+}
+
+static int _systemd_control_unit_async(const char *method, const char *name)
+{
+       GVariant *reply = NULL;
+       gchar *objpath = NULL;
+       int ret = 0;
+
+       _I("Starting: %s %s", method, name);
+
+       reply = dbus_handle_method_sync_with_reply_var(SYSTEMD_DBUS_DEST,
+               SYSTEMD_DBUS_PATH,
+               SYSTEMD_DBUS_MANAGER_IFACE,
+               method,
+               g_variant_new("(ss)", name, "replace"));
+
+       if (!reply || !dh_get_param_from_var(reply, "(o)", &objpath)) {
+               _E("fail (%s): no message", method);
+               ret = -EBADMSG;
+               goto finish;
+       }
+
+       _I("Finished: %s %s", method, name);
+finish:
+       if (reply)
+               g_variant_unref(reply);
+       g_free(objpath);
+
+       return ret;
+}
+
+static int _has_suffix(const char *service_name, const char *suffix)
+{
+       int index = 0;
+
+       if (!service_name || !suffix)
+               return FALSE;
+
+       index = strlen(service_name) - strlen(suffix);
+       if (index <= 0)
+               return FALSE;
+
+       if (strcmp(service_name + index, suffix) == 0)
+               return TRUE;
+       return FALSE;
+}
+
+static int _change_suffix(const char *name, const char *suffix, char **new_name)
+{
+       char *buf = NULL;
+       char *ext = NULL;
+       unsigned int len = 0;
+       int ret = 0;
+
+       if (!name || !suffix || !new_name) {
+               _E("Wrong param name:%s, suffix:%s, new_name:%s", name, suffix, new_name);
+               return -EINVAL;
+       }
+
+       ext = strrchr(name, '.');
+       if (ext == name) {
+               _E("Wrong file name %s", name);
+               return -EINVAL;
+       }
+
+       /* if ext is same as suffix */
+       if (ext && strcmp(ext, suffix) == 0) {
+               *new_name = strdup(name);
+               return 0;
+       }
+
+       /* otherwise, make new unit name */
+       if (ext)
+               len = ext - name;
+       else
+               len = strlen(name);
+
+       /* check max len */
+       if ((len + strlen(suffix)) >= UNIT_NAME_MAX) {
+               _E("Name is too long:%d", (len + strlen(suffix)));
+               return -ENAMETOOLONG;
+       }
+
+       buf = (char *)malloc(sizeof(char) * (len + strlen(suffix) + 1));
+       if (!buf) {
+               _E("Failed to alloc mem");
+               return -ENOMEM;
+       }
+
+       ret = snprintf(buf, len + 1, "%s", name);
+       if (ret < 0) {
+               ret = -errno;
+               _E("Failed to snprintf %d", ret);
+               goto err;
+       }
+       ret = snprintf(buf + len, strlen(suffix) + 1, "%s", suffix);
+       if (ret < 0) {
+               ret = -errno;
+               _E("Failed to snprintf %d", ret);
+               goto err;
+       }
+
+       *new_name = buf;
+
+       return 0;
+
+err:
+       free(buf);
+       return ret;
+}
+
+/*
+_systemd_start_unit_internal
+
+Start or Stop systemd unit.
+       1) synchronous
+               - Send StartUnit/StopUnit Method call(sync)
+                       reply:(o):/org/freedesktop/systemd1/job/[jobid]
+               - Wait JobRemoved signal from systemd
+                       (uoss):(uint32 [jobid], objectpath '/org/freedesktop/systemd1/job/[jobid]', '[unit name]', '[result]')
+       2) asynchronous
+               - Send StartUnit/StopUnit Method call(sync)
+
+@param name: unit name
+@param suffix: (nullable): change extension of unit name, or %NULL
+@param timeout_msec: the timeout in milliseconds, -1 to use the default
+@param method: method name, "StartUnit" or "StopUnit"
+@param sync: %TRUE
+Returns: the exit status
+*/
+static int _systemd_start_unit_internal(const char *name, const char *suffix,
+               int timeout_msec, const char *method, int sync)
+{
+       unsigned int len = 0;
+       char *new_name = NULL;
+       int ret = 0;
+
+       if (!name || !method) {
+               _E("Wrong param name %s, method %s", name, method);
+               return -EINVAL;
+       }
+       if (timeout_msec < -1) {
+               _E("wrong timeout. timeout(>=0 or -1)");
+               return -EINVAL;
+       }
+
+       if (suffix) {
+               ret = _change_suffix(name, suffix, &new_name);
+               if (ret < 0)
+                       return ret;
+               name = new_name;
+       } else {
+               if (strlen(name) > UNIT_NAME_MAX) {
+                       _E("Invalid name length %d(>%d)", strlen(name), UNIT_NAME_MAX);
+                       return -EINVAL;
+               }
+       }
+
+       if (sync)
+               ret = _systemd_control_unit_sync(method, name, timeout_msec);
+       else
+               ret = _systemd_control_unit_async(method, name);
+
+       if (new_name)
+               free(new_name);
+
+       return ret;
+}
+
+int systemd_start_unit_sync(const char *name, const char *suffix, int timeout_msec)
+{
+       return _systemd_start_unit_internal(name, suffix, timeout_msec, "StartUnit", TRUE);
+}
+
+int systemd_stop_unit_sync(const char *name, const char *suffix, int timeout_msec)
+{
+       return _systemd_start_unit_internal(name, suffix, timeout_msec, "StopUnit", TRUE);
+}
+
+int systemd_start_unit_async(const char *name, const char *suffix)
+{
+       return _systemd_start_unit_internal(name, suffix, -1, "StartUnit", FALSE);
+}
+
+int systemd_stop_unit_async(const char *name, const char *suffix)
+{
+       return _systemd_start_unit_internal(name, suffix, -1, "StopUnit", FALSE);
+}
+
+#define SYSTEMD_UNIT_ESCAPE_CHAR ".-"
+
+static char *systemd_get_unit_dbus_path(const char *unit)
+{
+       char *path = NULL;
+       int i;
+       size_t p, k, prefix_len, unit_len;
+       size_t path_len, len, escape;
+
+       if (!unit)
+               return NULL;
+       unit_len = strlen(unit);
+
+       for (escape = 0, p = 0; p < unit_len; escape++) {
+               k = strcspn(unit + p, SYSTEMD_UNIT_ESCAPE_CHAR);
+               if (p + k >= unit_len)
+                       break;
+               p += k+1;
+       }
+
+       prefix_len = strlen(SYSTEMD_DBUS_UNIT_PATH);
+       /* assume we try to get object path of foo-bar.service then
+       * the object path will be
+       * "/org/freedesktop/systemd1/unit/foo_2dbar_2eservice\n". In
+       * this case we can find two escape characters, one of escape
+       * char('-') is changed to three of char("_2d"). So the total
+       * length will be: */
+       /* (PREFIX) + (unit - escape + 3*escape) + NULL */
+       path_len = prefix_len + (unit_len - escape)
+               + (escape * 3 * sizeof(char)) + 1;
+       path = (char *)calloc(path_len, sizeof(char));
+       if (!path)
+               return NULL;
+
+       strncpy(path, SYSTEMD_DBUS_UNIT_PATH, prefix_len);
+       for (i = 0, p = 0; i <= escape; i++) {
+               k = strcspn(unit + p, SYSTEMD_UNIT_ESCAPE_CHAR);
+               strncpy(path + prefix_len, unit + p, k);
+               if (k < strlen(unit + p)) {
+                       len = path_len - (prefix_len + k);
+                       snprintf(path + prefix_len + k, len,
+                                       "_%x", *(unit + p + k) & 0xff);
+                       prefix_len += k + 3;
+                       p += k+1;
+               }
+       }
+
+       return path;
+}
+
+GVariant *systemd_get_manager_property(const char *property)
+{
+       GVariant *reply = NULL;
+       GVariant *val = NULL;
+
+       if (!property)
+               return NULL;
+
+       reply = dbus_handle_method_sync_with_reply_var(SYSTEMD_DBUS_DEST,
+                                       SYSTEMD_DBUS_PATH,
+                                       DBUS_IFACE_DBUS_PROPERTIES,
+                                       "Get",
+                                       g_variant_new("(ss)", SYSTEMD_DBUS_MANAGER_IFACE, property));
+       if (!reply || !dh_get_param_from_var(reply, "(v)", &val))
+               _E("Failed to get variant");
+       if (reply)
+               g_variant_unref(reply);
+
+       return val;
+}
+
+GVariant *systemd_get_unit_property(const char *unit,
+                                     const char *property)
+{
+       char *escaped;
+       GVariant *reply = NULL;
+       GVariant *val = NULL;
+
+       if (!unit || !property)
+               return NULL;
+
+       escaped = systemd_get_unit_dbus_path(unit);
+
+       reply = dbus_handle_method_sync_with_reply_var(SYSTEMD_DBUS_DEST,
+                                       escaped,
+                                       DBUS_IFACE_DBUS_PROPERTIES,
+                                       "Get",
+                                       g_variant_new("(ss)", SYSTEMD_DBUS_UNIT_IFACE, property));
+
+       if (!reply || !dh_get_param_from_var(reply, "(v)", &val))
+               _E("Failed to get variant");
+       if (reply)
+               g_variant_unref(reply);
+       free(escaped);
+
+       return val;
+}
+
+GVariant *systemd_get_service_property(const char *unit,
+                                        const char *property)
+{
+       char *escaped;
+       GVariant *reply = NULL;
+       GVariant *val = NULL;
+
+       if (!unit || !property)
+               return NULL;
+
+       escaped = systemd_get_unit_dbus_path(unit);
+
+       reply = dbus_handle_method_sync_with_reply_var(SYSTEMD_DBUS_DEST,
+                                       escaped,
+                                       DBUS_IFACE_DBUS_PROPERTIES,
+                                       "Get",
+                                       g_variant_new("(ss)", SYSTEMD_DBUS_SERVICE_IFACE, property));
+       if (!reply || !dh_get_param_from_var(reply, "(v)", &val))
+               _E("Failed to get variant");
+       if (reply)
+               g_variant_unref(reply);
+       free(escaped);
+       return val;
+}
diff --git a/src/libgdbus/dbus-systemd.h b/src/libgdbus/dbus-systemd.h
new file mode 100644 (file)
index 0000000..121bbf1
--- /dev/null
@@ -0,0 +1,46 @@
+/*
+ * libsyscommon
+ *
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd.
+ *
+ * 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.
+ */
+
+
+#ifndef __DBUS_SYSTEMD_H__
+#define __DBUS_SYSTEMD_H__
+
+#include "dbus-system.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+int systemd_start_unit_sync(const char *name, const char *suffix, int timeout_msec);
+int systemd_stop_unit_sync(const char *name, const char *suffix, int timeout_msec);
+
+int systemd_start_unit_async(const char *name, const char *suffix);
+int systemd_stop_unit_async(const char *name, const char *suffix);
+
+GVariant *systemd_get_manager_property(const char *property);
+GVariant *systemd_get_unit_property(const char *unit,
+                                     const char *property);
+GVariant *systemd_get_service_property(const char *unit,
+                                        const char *property);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* __DBUS_SYSTEMD_H__ */
+