4 * Copyright (c) 2019 Samsung Electronics Co., Ltd.
6 * Licensed under the Apache License, Version 2.0 (the License);
7 * you may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
10 * http://www.apache.org/licenses/LICENSE-2.0
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an "AS IS" BASIS,
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License.
24 #include <libgdbus/dbus-system.h>
26 #include "shared/log.h"
28 #define SYSTEMD_DBUS_SERVICE "org.freedesktop.systemd1"
29 #define SYSTEMD_DBUS_PATH "/org/freedesktop/systemd1"
30 #define SYSTEMD_DBUS_UNIT_PATH "/org/freedesktop/systemd1/unit/"
32 #define SYSTEMD_DBUS_MANAGER_IFACE "org.freedesktop.systemd1.Manager"
33 #define SYSTEMD_DBUS_UNIT_IFACE "org.freedesktop.systemd1.Unit"
34 #define SYSTEMD_DBUS_SERVICE_IFACE "org.freedesktop.systemd1.Service"
35 #define SYSTEMD_DBUS_TARGET_IFACE "org.freedesktop.systemd1.Target"
37 #define DBUS_IFACE_DBUS_PROPERTIES "org.freedesktop.DBus.Properties"
39 #define SUFFIX_SERVICE ".service"
40 #define SUFFIX_SOCKET ".socket"
41 #define SUFFIX_BUSNAME ".busname"
42 #define SUFFIX_TARGET ".target"
43 #define SUFFIX_DEVICE ".device"
44 #define SUFFIX_MOUNT ".mount"
45 #define SUFFIX_SWAP ".swap"
46 #define SUFFIX_TIMER ".timer"
47 #define SUFFIX_PATH ".path"
48 #define SUFFIX_SLICE ".slice"
49 #define SUFFIX_SCOPE ".scope"
51 #define UNIT_NAME_MAX 256
58 static void _cb_JobRemoved(GDBusConnection *conn,
68 gchar *unit_name = NULL;
69 unitinfo *uinfo = NULL;
72 _E("User data ctx is null");
76 uinfo = ctx->user_data;
78 _E("User_data uinfo is null");
81 if (!dh_get_param_from_var(param, "(uoss)", NULL, &job_id, &unit_name, NULL)) {
82 _E("Failed to get param");
85 if (strcmp(uinfo->job_id, job_id) || strcmp(uinfo->unit_name, unit_name)) {
86 _E("Not matched: job_id:%s, unit_name:%s", job_id, unit_name);
90 /* otherwise, if matched signal, quit loop */
92 ctx->quit_reason = CTX_QUIT_NORMAL;
93 if (ctx->timeout_src) {
94 g_source_destroy(ctx->timeout_src);
95 ctx->timeout_src = NULL;
98 g_main_loop_quit(ctx->loop);
105 static int _systemd_control_unit_sync(const char *method, const char *name, int timeout_msec)
107 GVariant *reply = NULL;
108 gchar *objpath = NULL;
111 gchar *unit_name = NULL;
115 ctx = dbus_handle_new_signal_ctx();
119 _I("Starting: %s %s", method, name);
121 /* synchronous siganl subscriptsion */
122 ret = subscribe_dbus_signal_ctx(NULL, ctx, SYSTEMD_DBUS_SERVICE, SYSTEMD_DBUS_PATH, SYSTEMD_DBUS_IFACE_MANAGER, "JobRemoved", _cb_JobRemoved);
128 reply = dbus_handle_method_sync_with_reply_var(SYSTEMD_DBUS_DEST,
130 SYSTEMD_DBUS_MANAGER_IFACE,
132 g_variant_new("(ss)", name, "replace"));
133 if (!reply || !dh_get_param_from_var(reply, "(o)", &objpath)) {
134 _E("fail (%s): no message", method);
139 uinfo.job_id = objpath;
140 uinfo.unit_name = name;
141 ctx->user_data = &uinfo;
144 ret = dbus_handle_signal_ctx_add_timeout(ctx, timeout_msec);
146 _E("Failed to set timeout, %d", ret);
150 /* run loop and wait signal callback */
151 quit_reason = dbus_handle_signal_ctx_wait(ctx);
152 if (quit_reason != CTX_QUIT_NORMAL) {
154 _E("Failed to receive JobRemoved signal %d", quit_reason);
158 _I("Finished: %s %s", method, name);
165 g_variant_unref(reply);
168 dbus_handle_free_signal_ctx(ctx);
173 static int _systemd_control_unit_async(const char *method, const char *name)
175 GVariant *reply = NULL;
176 gchar *objpath = NULL;
179 _I("Starting: %s %s", method, name);
181 reply = dbus_handle_method_sync_with_reply_var(SYSTEMD_DBUS_DEST,
183 SYSTEMD_DBUS_MANAGER_IFACE,
185 g_variant_new("(ss)", name, "replace"));
187 if (!reply || !dh_get_param_from_var(reply, "(o)", &objpath)) {
188 _E("fail (%s): no message", method);
193 _I("Finished: %s %s", method, name);
196 g_variant_unref(reply);
202 static int _has_suffix(const char *service_name, const char *suffix)
206 if (!service_name || !suffix)
209 index = strlen(service_name) - strlen(suffix);
213 if (strcmp(service_name + index, suffix) == 0)
218 static int _change_suffix(const char *name, const char *suffix, char **new_name)
222 unsigned int len = 0;
225 if (!name || !suffix || !new_name) {
226 _E("Wrong param name:%s, suffix:%s, new_name:%s", name, suffix, new_name);
230 ext = strrchr(name, '.');
232 _E("Wrong file name %s", name);
236 /* if ext is same as suffix */
237 if (ext && strcmp(ext, suffix) == 0) {
238 *new_name = strdup(name);
242 /* otherwise, make new unit name */
249 if ((len + strlen(suffix)) >= UNIT_NAME_MAX) {
250 _E("Name is too long:%d", (len + strlen(suffix)));
251 return -ENAMETOOLONG;
254 buf = (char *)malloc(sizeof(char) * (len + strlen(suffix) + 1));
256 _E("Failed to alloc mem");
260 ret = snprintf(buf, len + 1, "%s", name);
263 _E("Failed to snprintf %d", ret);
266 ret = snprintf(buf + len, strlen(suffix) + 1, "%s", suffix);
269 _E("Failed to snprintf %d", ret);
283 _systemd_start_unit_internal
285 Start or Stop systemd unit.
287 - Send StartUnit/StopUnit Method call(sync)
288 reply:(o):/org/freedesktop/systemd1/job/[jobid]
289 - Wait JobRemoved signal from systemd
290 (uoss):(uint32 [jobid], objectpath '/org/freedesktop/systemd1/job/[jobid]', '[unit name]', '[result]')
292 - Send StartUnit/StopUnit Method call(sync)
294 @param name: unit name
295 @param suffix: (nullable): change extension of unit name, or %NULL
296 @param timeout_msec: the timeout in milliseconds, -1 to use the default
297 @param method: method name, "StartUnit" or "StopUnit"
299 Returns: the exit status
301 static int _systemd_start_unit_internal(const char *name, const char *suffix,
302 int timeout_msec, const char *method, int sync)
304 unsigned int len = 0;
305 char *new_name = NULL;
308 if (!name || !method) {
309 _E("Wrong param name %s, method %s", name, method);
312 if (timeout_msec < -1) {
313 _E("wrong timeout. timeout(>=0 or -1)");
318 ret = _change_suffix(name, suffix, &new_name);
323 if (strlen(name) > UNIT_NAME_MAX) {
324 _E("Invalid name length %d(>%d)", strlen(name), UNIT_NAME_MAX);
330 ret = _systemd_control_unit_sync(method, name, timeout_msec);
332 ret = _systemd_control_unit_async(method, name);
340 int systemd_start_unit_sync(const char *name, const char *suffix, int timeout_msec)
342 return _systemd_start_unit_internal(name, suffix, timeout_msec, "StartUnit", TRUE);
345 int systemd_stop_unit_sync(const char *name, const char *suffix, int timeout_msec)
347 return _systemd_start_unit_internal(name, suffix, timeout_msec, "StopUnit", TRUE);
350 int systemd_start_unit_async(const char *name, const char *suffix)
352 return _systemd_start_unit_internal(name, suffix, -1, "StartUnit", FALSE);
355 int systemd_stop_unit_async(const char *name, const char *suffix)
357 return _systemd_start_unit_internal(name, suffix, -1, "StopUnit", FALSE);
360 #define SYSTEMD_UNIT_ESCAPE_CHAR ".-"
362 static char *systemd_get_unit_dbus_path(const char *unit)
366 size_t p, k, prefix_len, unit_len;
367 size_t path_len, len, escape;
371 unit_len = strlen(unit);
373 for (escape = 0, p = 0; p < unit_len; escape++) {
374 k = strcspn(unit + p, SYSTEMD_UNIT_ESCAPE_CHAR);
375 if (p + k >= unit_len)
380 prefix_len = strlen(SYSTEMD_DBUS_UNIT_PATH);
381 /* assume we try to get object path of foo-bar.service then
382 * the object path will be
383 * "/org/freedesktop/systemd1/unit/foo_2dbar_2eservice\n". In
384 * this case we can find two escape characters, one of escape
385 * char('-') is changed to three of char("_2d"). So the total
387 /* (PREFIX) + (unit - escape + 3*escape) + NULL */
388 path_len = prefix_len + (unit_len - escape)
389 + (escape * 3 * sizeof(char)) + 1;
390 path = (char *)calloc(path_len, sizeof(char));
394 strncpy(path, SYSTEMD_DBUS_UNIT_PATH, prefix_len);
395 for (i = 0, p = 0; i <= escape; i++) {
396 k = strcspn(unit + p, SYSTEMD_UNIT_ESCAPE_CHAR);
397 strncpy(path + prefix_len, unit + p, k);
398 if (k < strlen(unit + p)) {
399 len = path_len - (prefix_len + k);
400 snprintf(path + prefix_len + k, len,
401 "_%x", *(unit + p + k) & 0xff);
410 GVariant *systemd_get_manager_property(const char *property)
412 GVariant *reply = NULL;
413 GVariant *val = NULL;
418 reply = dbus_handle_method_sync_with_reply_var(SYSTEMD_DBUS_DEST,
420 DBUS_IFACE_DBUS_PROPERTIES,
422 g_variant_new("(ss)", SYSTEMD_DBUS_MANAGER_IFACE, property));
423 if (!reply || !dh_get_param_from_var(reply, "(v)", &val))
424 _E("Failed to get variant");
426 g_variant_unref(reply);
431 GVariant *systemd_get_unit_property(const char *unit,
432 const char *property)
435 GVariant *reply = NULL;
436 GVariant *val = NULL;
438 if (!unit || !property)
441 escaped = systemd_get_unit_dbus_path(unit);
443 reply = dbus_handle_method_sync_with_reply_var(SYSTEMD_DBUS_DEST,
445 DBUS_IFACE_DBUS_PROPERTIES,
447 g_variant_new("(ss)", SYSTEMD_DBUS_UNIT_IFACE, property));
449 if (!reply || !dh_get_param_from_var(reply, "(v)", &val))
450 _E("Failed to get variant");
452 g_variant_unref(reply);
458 GVariant *systemd_get_service_property(const char *unit,
459 const char *property)
462 GVariant *reply = NULL;
463 GVariant *val = NULL;
465 if (!unit || !property)
468 escaped = systemd_get_unit_dbus_path(unit);
470 reply = dbus_handle_method_sync_with_reply_var(SYSTEMD_DBUS_DEST,
472 DBUS_IFACE_DBUS_PROPERTIES,
474 g_variant_new("(ss)", SYSTEMD_DBUS_SERVICE_IFACE, property));
475 if (!reply || !dh_get_param_from_var(reply, "(v)", &val))
476 _E("Failed to get variant");
478 g_variant_unref(reply);