642 lines
24 KiB
Diff
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);
|