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;
- }
}
}
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) {
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)
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);
--- /dev/null
+/*
+ * 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;
+}