pacemaker/SOURCES/003-systemd-dbus.patch

642 lines
24 KiB
Diff

From dac88ca0fa3d7fbcc4924599e90a3b3465cd8525 Mon Sep 17 00:00:00 2001
From: Reid Wahl <nrwahl@protonmail.com>
Date: Wed, 5 Feb 2025 16:42:44 -0800
Subject: [PATCH 1/4] Refactor: libcrmservice: New services__copy_result()
function
Ref T25
Signed-off-by: Reid Wahl <nrwahl@protonmail.com>
---
daemons/execd/execd_commands.c | 9 +++------
include/crm/services_internal.h | 7 ++++++-
lib/fencing/st_actions.c | 5 ++---
lib/lrmd/lrmd_client.c | 6 ++----
lib/services/services.c | 16 +++++++++++++++-
5 files changed, 28 insertions(+), 15 deletions(-)
diff --git a/daemons/execd/execd_commands.c b/daemons/execd/execd_commands.c
index 454f8756916..afb5f599b33 100644
--- a/daemons/execd/execd_commands.c
+++ b/daemons/execd/execd_commands.c
@@ -1,5 +1,5 @@
/*
- * Copyright 2012-2024 the Pacemaker project contributors
+ * Copyright 2012-2025 the Pacemaker project contributors
*
* The version control history for this file may have further details.
*
@@ -1344,8 +1344,7 @@ execute_nonstonith_action(lrmd_rsc_t *rsc, lrmd_cmd_t *cmd)
}
if (action->rc != PCMK_OCF_UNKNOWN) {
- pcmk__set_result(&(cmd->result), action->rc, action->status,
- services__exit_reason(action));
+ services__copy_result(action, &(cmd->result));
services_action_free(action);
cmd_finalize(cmd, rsc);
return;
@@ -1370,9 +1369,7 @@ execute_nonstonith_action(lrmd_rsc_t *rsc, lrmd_cmd_t *cmd)
* called cmd_finalize(), which in this case should only reset (not
* free) cmd.
*/
-
- pcmk__set_result(&(cmd->result), action->rc, action->status,
- services__exit_reason(action));
+ services__copy_result(action, &(cmd->result));
services_action_free(action);
}
}
diff --git a/include/crm/services_internal.h b/include/crm/services_internal.h
index ada97e1e711..63b034dcd24 100644
--- a/include/crm/services_internal.h
+++ b/include/crm/services_internal.h
@@ -1,5 +1,5 @@
/*
- * Copyright 2010-2022 the Pacemaker project contributors
+ * Copyright 2010-2025 the Pacemaker project contributors
*
* The version control history for this file may have further details.
*
@@ -10,6 +10,9 @@
#ifndef PCMK__SERVICES_INTERNAL__H
# define PCMK__SERVICES_INTERNAL__H
+#include <crm/common/results_internal.h> // pcmk__action_result_t
+#include <crm/services.h> // svc_action_t
+
#ifdef __cplusplus
extern "C" {
#endif
@@ -50,6 +53,8 @@ char *services__grab_stderr(svc_action_t *action);
void services__set_result(svc_action_t *action, int agent_status,
enum pcmk_exec_status exec_status,
const char *exit_reason);
+void services__copy_result(const svc_action_t *action,
+ pcmk__action_result_t *result);
void services__format_result(svc_action_t *action, int agent_status,
enum pcmk_exec_status exec_status,
diff --git a/lib/fencing/st_actions.c b/lib/fencing/st_actions.c
index 6b4d1e73527..a0f3e0377c0 100644
--- a/lib/fencing/st_actions.c
+++ b/lib/fencing/st_actions.c
@@ -1,5 +1,5 @@
/*
- * Copyright 2004-2024 the Pacemaker project contributors
+ * Copyright 2004-2025 the Pacemaker project contributors
*
* The version control history for this file may have further details.
*
@@ -63,8 +63,7 @@ static void log_action(stonith_action_t *action, pid_t pid);
static void
set_result_from_svc_action(stonith_action_t *action, svc_action_t *svc_action)
{
- pcmk__set_result(&(action->result), svc_action->rc, svc_action->status,
- services__exit_reason(svc_action));
+ services__copy_result(svc_action, &(action->result));
pcmk__set_result_output(&(action->result),
services__grab_stdout(svc_action),
services__grab_stderr(svc_action));
diff --git a/lib/lrmd/lrmd_client.c b/lib/lrmd/lrmd_client.c
index ae4c100def0..5fcdf2377e8 100644
--- a/lib/lrmd/lrmd_client.c
+++ b/lib/lrmd/lrmd_client.c
@@ -2555,8 +2555,7 @@ metadata_complete(svc_action_t *action)
struct metadata_cb *metadata_cb = (struct metadata_cb *) action->cb_data;
pcmk__action_result_t result = PCMK__UNKNOWN_RESULT;
- pcmk__set_result(&result, action->rc, action->status,
- services__exit_reason(action));
+ services__copy_result(action, &result);
pcmk__set_result_output(&result, action->stdout_data, action->stderr_data);
metadata_cb->callback(0, &result, metadata_cb->user_data);
@@ -2624,8 +2623,7 @@ lrmd__metadata_async(const lrmd_rsc_info_t *rsc,
return ENOMEM;
}
if (action->rc != PCMK_OCF_UNKNOWN) {
- pcmk__set_result(&result, action->rc, action->status,
- services__exit_reason(action));
+ services__copy_result(action, &result);
callback(0, &result, user_data);
pcmk__reset_result(&result);
services_action_free(action);
diff --git a/lib/services/services.c b/lib/services/services.c
index 2ba0eb24b9d..0d0eb46d342 100644
--- a/lib/services/services.c
+++ b/lib/services/services.c
@@ -1,5 +1,5 @@
/*
- * Copyright 2010-2024 the Pacemaker project contributors
+ * Copyright 2010-2025 the Pacemaker project contributors
*
* The version control history for this file may have further details.
*
@@ -1336,6 +1336,20 @@ services__set_result(svc_action_t *action, int agent_status,
}
}
+/*!
+ * \internal
+ * \brief Set a \c pcmk__action_result_t based on a \c svc_action_t
+ *
+ * \param[in] action Service action whose result to copy
+ * \param[in,out] result Action result object to set
+ */
+void
+services__copy_result(const svc_action_t *action, pcmk__action_result_t *result)
+{
+ pcmk__set_result(result, action->rc, action->status,
+ action->opaque->exit_reason);
+}
+
/*!
* \internal
* \brief Set the result of an action, with a formatted exit reason
From 283225383916358f6fc905afdc40e423583bc9bd Mon Sep 17 00:00:00 2001
From: Reid Wahl <nrwahl@protonmail.com>
Date: Wed, 5 Feb 2025 03:35:04 -0800
Subject: [PATCH 2/4] Refactor: libcrmservice: Store systemd job path in
svc_action_private_t
We will use this later for D-Bus signal message filtering.
Ref T25
Signed-off-by: Reid Wahl <nrwahl@protonmail.com>
---
lib/services/services.c | 5 +++++
lib/services/services_private.h | 6 +++++-
lib/services/systemd.c | 2 ++
3 files changed, 12 insertions(+), 1 deletion(-)
diff --git a/lib/services/services.c b/lib/services/services.c
index 0d0eb46d342..e1e98120c09 100644
--- a/lib/services/services.c
+++ b/lib/services/services.c
@@ -635,6 +635,11 @@ services_action_free(svc_action_t * op)
}
free(op->opaque->exit_reason);
+
+#if SUPPORT_SYSTEMD
+ free(op->opaque->job_path);
+#endif // SUPPORT_SYSTEMD
+
free(op->opaque);
free(op->rsc);
free(op->action);
diff --git a/lib/services/services_private.h b/lib/services/services_private.h
index 48269b80308..91b49b4248c 100644
--- a/lib/services/services_private.h
+++ b/lib/services/services_private.h
@@ -1,6 +1,6 @@
/*
* Copyright 2010-2011 Red Hat, Inc.
- * Later changes copyright 2012-2022 the Pacemaker project contributors
+ * Later changes copyright 2012-2025 the Pacemaker project contributors
*
* The version control history for this file may have further details.
*
@@ -42,6 +42,10 @@ struct svc_action_private_s {
DBusPendingCall* pending;
unsigned timerid;
#endif
+
+#if SUPPORT_SYSTEMD
+ char *job_path;
+#endif // SUPPORT_SYSTEMD
};
G_GNUC_INTERNAL
diff --git a/lib/services/systemd.c b/lib/services/systemd.c
index 19878efaa35..090a30bb1ca 100644
--- a/lib/services/systemd.c
+++ b/lib/services/systemd.c
@@ -760,8 +760,10 @@ process_unit_method_reply(DBusMessage *reply, svc_action_t *op)
dbus_message_get_args(reply, NULL,
DBUS_TYPE_OBJECT_PATH, &path,
DBUS_TYPE_INVALID);
+
crm_debug("DBus request for %s of %s using %s succeeded",
op->action, pcmk__s(op->rsc, "unknown resource"), path);
+ pcmk__str_update(&(op->opaque->job_path), path);
services__set_result(op, PCMK_OCF_OK, PCMK_EXEC_DONE, NULL);
}
}
From 67c1821648f314b510a24ed505a560fa34f2d5c0 Mon Sep 17 00:00:00 2001
From: Chris Lumens <clumens@redhat.com>
Date: Wed, 12 Mar 2025 10:54:54 -0400
Subject: [PATCH 3/4] Refactor: libcrmservice: Subscribe to systemd D-Bus
signals
We'll add a filter later. This does not meaningfully change behavior.
Ref T25
Signed-off-by: Reid Wahl <nrwahl@protonmail.com>
---
lib/services/systemd.c | 58 +++++++++++++++++++++++++++++++++++++++---
1 file changed, 54 insertions(+), 4 deletions(-)
diff --git a/lib/services/systemd.c b/lib/services/systemd.c
index 090a30bb1ca..df843edce05 100644
--- a/lib/services/systemd.c
+++ b/lib/services/systemd.c
@@ -25,6 +25,9 @@
static void invoke_unit_by_path(svc_action_t *op, const char *unit);
+/* Systemd D-Bus interface
+ * https://www.freedesktop.org/software/systemd/man/latest/org.freedesktop.systemd1.html
+ */
#define BUS_NAME "org.freedesktop.systemd1"
#define BUS_NAME_MANAGER BUS_NAME ".Manager"
#define BUS_NAME_UNIT BUS_NAME ".Unit"
@@ -137,6 +140,50 @@ systemd_call_simple_method(const char *method)
return reply;
}
+/*!
+ * \internal
+ * \brief Subscribe to D-Bus signals from systemd
+ *
+ * Systemd does not broadcast signal messages unless at least one client has
+ * called the \c Subscribe() method. Also, a D-Bus client ignores broadcast
+ * messages unless an appropriate match rule is set, so we set one here.
+ *
+ * \return Standard Pacemaker return code
+ */
+static int
+subscribe_to_signals(void)
+{
+ const char *match_rule = "type='signal',"
+ "sender='" BUS_NAME "',"
+ "interface='" BUS_NAME_MANAGER "',"
+ "path='" BUS_PATH "'";
+ DBusMessage *reply = NULL;
+ DBusError error;
+
+ /* Tell D-Bus to accept signal messages from systemd.
+ * https://dbus.freedesktop.org/doc/dbus-specification.html#message-bus-routing-match-rules
+ */
+ dbus_error_init(&error);
+ dbus_bus_add_match(systemd_proxy, match_rule, &error);
+
+ if (dbus_error_is_set(&error)) {
+ crm_err("Could not listen for systemd DBus signals: %s " QB_XS " (%s)",
+ error.message, error.name);
+ dbus_error_free(&error);
+ return ECOMM;
+ }
+
+ // Tell systemd to broadcast signals
+ reply = systemd_call_simple_method("Subscribe");
+ if (reply == NULL) {
+ dbus_bus_remove_match(systemd_proxy, match_rule, &error);
+ return ECOMM;
+ }
+
+ dbus_message_unref(reply);
+ return pcmk_rc_ok;
+}
+
static bool
systemd_init(void)
{
@@ -154,11 +201,14 @@ systemd_init(void)
if (need_init) {
need_init = 0;
systemd_proxy = pcmk_dbus_connect();
+
+ if (subscribe_to_signals() != pcmk_rc_ok) {
+ pcmk_dbus_disconnect(systemd_proxy);
+ systemd_proxy = NULL;
+ }
}
- if (systemd_proxy == NULL) {
- return false;
- }
- return true;
+
+ return (systemd_proxy != NULL);
}
static inline char *
From 373a7a317023f9d530cd43ab80bdb62a72bb2b05 Mon Sep 17 00:00:00 2001
From: Chris Lumens <clumens@redhat.com>
Date: Thu, 16 Jan 2025 16:21:44 -0500
Subject: [PATCH 4/4] Fix: various: Correctly detect completion of systemd
start/stop actions
When systemd receives a StartUnit() or StopUnit() method call, it
returns almost immediately, as soon as a start/stop job is enqueued. A
successful return code does NOT indicate that the start/stop has
finished.
Previously, we worked around this in action_complete() with a hack that
scheduled a follow-up monitor after a successful start/stop method call,
which polled the service after 2 seconds to see whether it was actually
running. However, this was not a robust solution. Timing issues could
result in Pacemaker having an incorrect view of the resource's status or
prematurely declaring the action as failed.
Now, we follow the best practice as documented in the systemd D-Bus API
doc (see StartUnit()):
https://www.freedesktop.org/software/systemd/man/latest/org.freedesktop.systemd1.html#Methods
After kicking off a systemd start/stop action, we make note of the job's
D-Bus object path. Then we register a D-Bus message filter that looks
for a JobRemoved signal whose bus path matches. This signal indicates
that the job has completed and includes its result. When we find the
matching signal, we set the action's result. We then remove the filter,
which causes the action to be finalized and freed. In the case of the
executor daemon, the action has a callback (action_complete()) that runs
during finalization and sets the executor's view of the action result.
Monitor actions still need much of the existing workaround code in
action_complete(), so we keep it for now. We bail out for start/stop
actions after setting the result as described above.
Ref T25
Co-authored-by: Reid Wahl <nrwahl@protonmail.com>
Signed-off-by: Reid Wahl <nrwahl@protonmail.com>
---
daemons/execd/execd_commands.c | 13 ++-
lib/services/services_private.h | 2 +-
lib/services/systemd.c | 167 ++++++++++++++++++++++++++++++--
3 files changed, 167 insertions(+), 15 deletions(-)
diff --git a/daemons/execd/execd_commands.c b/daemons/execd/execd_commands.c
index afb5f599b33..67283ce1012 100644
--- a/daemons/execd/execd_commands.c
+++ b/daemons/execd/execd_commands.c
@@ -861,14 +861,13 @@ action_complete(svc_action_t * action)
if (pcmk__result_ok(&(cmd->result))
&& pcmk__strcase_any_of(cmd->action, PCMK_ACTION_START,
PCMK_ACTION_STOP, NULL)) {
- /* systemd returns from start and stop actions after the action
- * begins, not after it completes. We have to jump through a few
- * hoops so that we don't report 'complete' to the rest of pacemaker
- * until it's actually done.
+ /* Getting results for when a start or stop action completes is now
+ * handled by watching for JobRemoved() signals from systemd and
+ * reacting to them. So, we can bypass the rest of the code in this
+ * function for those actions, and simply finalize cmd.
*/
- goagain = true;
- cmd->real_action = cmd->action;
- cmd->action = pcmk__str_copy(PCMK_ACTION_MONITOR);
+ services__copy_result(action, &(cmd->result));
+ goto finalize;
} else if (cmd->real_action != NULL) {
// This is follow-up monitor to check whether start/stop completed
diff --git a/lib/services/services_private.h b/lib/services/services_private.h
index 91b49b4248c..b29833c3988 100644
--- a/lib/services/services_private.h
+++ b/lib/services/services_private.h
@@ -44,7 +44,7 @@ struct svc_action_private_s {
#endif
#if SUPPORT_SYSTEMD
- char *job_path;
+ char *job_path; // D-Bus object path for enqueued start/stop job
#endif // SUPPORT_SYSTEMD
};
diff --git a/lib/services/systemd.c b/lib/services/systemd.c
index df843edce05..f10d24a050e 100644
--- a/lib/services/systemd.c
+++ b/lib/services/systemd.c
@@ -14,7 +14,9 @@
#include <crm/services_internal.h>
#include <crm/common/mainloop.h>
+#include <inttypes.h> // PRIu32
#include <stdbool.h>
+#include <stdint.h> // uint32_t
#include <stdio.h> // fopen(), NULL, etc.
#include <sys/stat.h>
#include <gio/gio.h>
@@ -785,6 +787,8 @@ systemd_unit_metadata(const char *name, int timeout)
static void
process_unit_method_reply(DBusMessage *reply, svc_action_t *op)
{
+ bool start_stop = pcmk__strcase_any_of(op->action, PCMK_ACTION_START,
+ PCMK_ACTION_STOP, NULL);
DBusError error;
dbus_error_init(&error);
@@ -798,11 +802,25 @@ process_unit_method_reply(DBusMessage *reply, svc_action_t *op)
} else if (!pcmk_dbus_type_check(reply, NULL, DBUS_TYPE_OBJECT_PATH,
__func__, __LINE__)) {
+ const char *reason = "systemd D-Bus method had unexpected reply";
+
crm_info("DBus request for %s of %s succeeded but "
"return type was unexpected",
op->action, pcmk__s(op->rsc, "unknown resource"));
- services__set_result(op, PCMK_OCF_OK, PCMK_EXEC_DONE,
- "systemd DBus method had unexpected reply");
+
+ if (!op->synchronous && start_stop) {
+ /* The start or stop job is enqueued but is not complete. We need a
+ * job path to detect completion in job_removed_filter().
+ */
+ services__set_result(op, PCMK_OCF_UNKNOWN_ERROR, PCMK_EXEC_ERROR,
+ reason);
+
+ } else {
+ /* Something weird happened, but the action is finished and there
+ * was no D-Bus error. So call it a success.
+ */
+ services__set_result(op, PCMK_OCF_OK, PCMK_EXEC_DONE, reason);
+ }
} else {
const char *path = NULL;
@@ -813,9 +831,114 @@ process_unit_method_reply(DBusMessage *reply, svc_action_t *op)
crm_debug("DBus request for %s of %s using %s succeeded",
op->action, pcmk__s(op->rsc, "unknown resource"), path);
- pcmk__str_update(&(op->opaque->job_path), path);
- services__set_result(op, PCMK_OCF_OK, PCMK_EXEC_DONE, NULL);
+
+ if (!op->synchronous && start_stop) {
+ // Should be set to unknown/pending already
+ services__set_result(op, PCMK_OCF_UNKNOWN, PCMK_EXEC_PENDING, NULL);
+ pcmk__str_update(&(op->opaque->job_path), path);
+
+ } else {
+ services__set_result(op, PCMK_OCF_OK, PCMK_EXEC_DONE, NULL);
+ }
+ }
+}
+
+/*!
+ * \internal
+ * \brief Process a systemd \c JobRemoved signal for a given service action
+ *
+ * This filter is expected to be added with \c finalize_async_action_dbus() as
+ * the \c free_data_function. Then if \p message is a \c JobRemoved signal for
+ * the action specified by \p user_data, the action's result is set, the filter
+ * is removed, and the action is finalized.
+ *
+ * \param[in,out] connection D-Bus connection
+ * \param[in] message D-Bus message
+ * \param[in,out] user_data Service action (\c svc_action_t)
+ *
+ * \retval \c DBUS_HANDLER_RESULT_HANDLED if \p message is a \c JobRemoved
+ * signal for \p user_data
+ * \retval \c DBUS_HANDLER_RESULT_NOT_YET_HANDLED otherwise (on error, if
+ * \p message is not a \c JobRemoved signal, or if the signal is for
+ * some other action's job)
+ */
+static DBusHandlerResult
+job_removed_filter(DBusConnection *connection, DBusMessage *message,
+ void *user_data)
+{
+ svc_action_t *action = user_data;
+ const char *action_name = NULL;
+ uint32_t job_id = 0;
+ const char *bus_path = NULL;
+ const char *unit_name = NULL;
+ const char *result = NULL;
+ DBusError error;
+
+ CRM_CHECK((connection != NULL) && (message != NULL),
+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED);
+
+ // action should always be set when the filter is added
+ if ((action == NULL)
+ || !dbus_message_is_signal(message, BUS_NAME_MANAGER, "JobRemoved")) {
+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+ }
+
+ dbus_error_init(&error);
+ if (!dbus_message_get_args(message, &error,
+ DBUS_TYPE_UINT32, &job_id,
+ DBUS_TYPE_OBJECT_PATH, &bus_path,
+ DBUS_TYPE_STRING, &unit_name,
+ DBUS_TYPE_STRING, &result,
+ DBUS_TYPE_INVALID)) {
+ crm_err("Could not interpret systemd DBus signal: %s " QB_XS " (%s)",
+ error.message, error.name);
+ dbus_error_free(&error);
+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+ }
+
+ if (!pcmk__str_eq(bus_path, action->opaque->job_path, pcmk__str_none)) {
+ // This filter is not for this job
+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
}
+
+ action_name = pcmk__s(action->action, "(unknown)");
+
+ crm_trace("Setting %s result for %s (JobRemoved id=%" PRIu32 ", result=%s",
+ action_name, unit_name, job_id, result);
+
+ if (pcmk__str_eq(result, "done", pcmk__str_none)) {
+ services__set_result(action, PCMK_OCF_OK, PCMK_EXEC_DONE, NULL);
+
+ } else if (pcmk__str_eq(result, "timeout", pcmk__str_none)) {
+ services__format_result(action, PCMK_OCF_UNKNOWN_ERROR, PCMK_EXEC_TIMEOUT,
+ "systemd %s job for %s timed out",
+ action_name, unit_name);
+
+ } else {
+ services__format_result(action, PCMK_OCF_UNKNOWN_ERROR, PCMK_EXEC_ERROR,
+ "systemd %s job for %s failed with result '%s'",
+ action_name, unit_name, result);
+ }
+
+ /* This instance of the filter was specifically for the given action.
+ *
+ * The action gets finalized by services__finalize_async_op() via the
+ * filter's free_data_function.
+ */
+ dbus_connection_remove_filter(systemd_proxy, job_removed_filter, action);
+ return DBUS_HANDLER_RESULT_HANDLED;
+}
+
+/*!
+ * \internal
+ * \brief \c DBusFreeFunction wrapper for \c services__finalize_async_op()
+ *
+ * \param[in,out] action Asynchronous service action to finalize
+ */
+static void
+finalize_async_action_dbus(void *action)
+{
+ services__finalize_async_op((svc_action_t *) action);
}
/*!
@@ -842,12 +965,34 @@ unit_method_complete(DBusPendingCall *pending, void *user_data)
CRM_LOG_ASSERT(pending == op->opaque->pending);
services_set_op_pending(op, NULL);
- // Determine result and finalize action
process_unit_method_reply(reply, op);
- services__finalize_async_op(op);
+
if (reply != NULL) {
dbus_message_unref(reply);
}
+
+ if ((op->status == PCMK_EXEC_PENDING)
+ && pcmk__strcase_any_of(op->action, PCMK_ACTION_START, PCMK_ACTION_STOP,
+ NULL)) {
+ /* Start and stop method calls return when the job is enqueued, not when
+ * it's complete. Start and stop actions must be finalized after the job
+ * is complete, because the action callback function may use it. We add
+ * a message filter to process the JobRemoved signal, which indicates
+ * completion.
+ *
+ * The filter takes ownership of op, which will be finalized when the
+ * filter is later removed.
+ */
+ if (dbus_connection_add_filter(systemd_proxy, job_removed_filter, op,
+ finalize_async_action_dbus)) {
+ return;
+ }
+ crm_err("Could not add D-Bus filter for systemd JobRemoved signals");
+ services__set_result(op, PCMK_OCF_UNKNOWN_ERROR, PCMK_EXEC_ERROR,
+ "Failed to add D-Bus filter for systemd "
+ "JobRemoved signal");
+ }
+ services__finalize_async_op(op);
}
/* When the cluster manages a systemd resource, we create a unit file override
@@ -1193,7 +1338,14 @@ systemd_timeout_callback(gpointer p)
services__format_result(op, PCMK_OCF_UNKNOWN_ERROR, PCMK_EXEC_TIMEOUT,
"%s action for systemd unit %s "
"did not complete in time", op->action, op->agent);
- services__finalize_async_op(op);
+
+ if (op->opaque->job_path != NULL) {
+ // A filter owns this op
+ dbus_connection_remove_filter(systemd_proxy, job_removed_filter, op);
+
+ } else {
+ services__finalize_async_op(op);
+ }
return FALSE;
}
@@ -1247,6 +1399,7 @@ services__execute_systemd(svc_action_t *op)
"Bug in service library");
if (invoke_unit_by_name(op->agent, op, NULL) == pcmk_rc_ok) {
+ // @TODO Why plus 5000? No explanation in fccd046.
op->opaque->timerid = g_timeout_add(op->timeout + 5000,
systemd_timeout_callback, op);
services_add_inflight_op(op);