diff --git a/daemons/fenced/pacemaker-fenced.c b/daemons/fenced/pacemaker-fenced.c
index f5a729741f..9c6fcf4c76 100644
--- a/daemons/fenced/pacemaker-fenced.c
+++ b/daemons/fenced/pacemaker-fenced.c
@@ -1,666 +1,666 @@
 /*
  * Copyright 2009-2025 the Pacemaker project contributors
  *
  * The version control history for this file may have further details.
  *
  * This source code is licensed under the GNU General Public License version 2
  * or later (GPLv2+) WITHOUT ANY WARRANTY.
  */
 
 #include <crm_internal.h>
 
 #include <sys/param.h>
 #include <stdio.h>
 #include <sys/types.h>
 #include <sys/stat.h>
 #include <unistd.h>
 #include <sys/utsname.h>
 
 #include <stdlib.h>
 #include <errno.h>
 #include <fcntl.h>
 #include <inttypes.h>  // PRIu32, PRIx32
 
 #include <crm/crm.h>
 #include <crm/common/cmdline_internal.h>
 #include <crm/common/ipc.h>
 #include <crm/common/ipc_internal.h>
 #include <crm/common/output_internal.h>
 
 #include <crm/stonith-ng.h>
 #include <crm/fencing/internal.h>
 #include <crm/common/xml.h>
 #include <crm/common/xml_internal.h>
 
 #include <crm/common/mainloop.h>
 
 #include <crm/cib/internal.h>
 
 #include <pacemaker-fenced.h>
 
 #define SUMMARY "daemon for executing fencing devices in a Pacemaker cluster"
 
 // @TODO This should be guint
 long long stonith_watchdog_timeout_ms = 0;
 
 GList *stonith_watchdog_targets = NULL;
 
 static GMainLoop *mainloop = NULL;
 
 gboolean stonith_shutdown_flag = FALSE;
 
 static qb_ipcs_service_t *ipcs = NULL;
 static pcmk__output_t *out = NULL;
 
 pcmk__supported_format_t formats[] = {
     PCMK__SUPPORTED_FORMAT_NONE,
     PCMK__SUPPORTED_FORMAT_TEXT,
     PCMK__SUPPORTED_FORMAT_XML,
     { NULL, NULL, NULL }
 };
 
 static struct {
     gboolean stand_alone;
     gchar **log_files;
 } options;
 
 crm_exit_t exit_code = CRM_EX_OK;
 
 static void stonith_cleanup(void);
 
 static int32_t
 st_ipc_accept(qb_ipcs_connection_t * c, uid_t uid, gid_t gid)
 {
     if (stonith_shutdown_flag) {
         crm_info("Ignoring new client [%d] during shutdown",
                  pcmk__client_pid(c));
         return -ECONNREFUSED;
     }
 
     if (pcmk__new_client(c, uid, gid) == NULL) {
         return -ENOMEM;
     }
     return 0;
 }
 
 /* Exit code means? */
 static int32_t
 st_ipc_dispatch(qb_ipcs_connection_t * qbc, void *data, size_t size)
 {
     uint32_t id = 0;
     uint32_t flags = 0;
     uint32_t call_options = st_opt_none;
     xmlNode *request = NULL;
     pcmk__client_t *c = pcmk__find_client(qbc);
     const char *op = NULL;
     int rc = pcmk_rc_ok;
 
     if (c == NULL) {
         crm_info("Invalid client: %p", qbc);
         return 0;
     }
 
     request = pcmk__client_data2xml(c, data, &id, &flags);
     if (request == NULL) {
         pcmk__ipc_send_ack(c, id, flags, PCMK__XE_NACK, NULL, CRM_EX_PROTOCOL);
         return 0;
     }
 
 
     op = crm_element_value(request, PCMK__XA_CRM_TASK);
     if(pcmk__str_eq(op, CRM_OP_RM_NODE_CACHE, pcmk__str_casei)) {
         crm_xml_add(request, PCMK__XA_T, PCMK__VALUE_STONITH_NG);
         crm_xml_add(request, PCMK__XA_ST_OP, op);
         crm_xml_add(request, PCMK__XA_ST_CLIENTID, c->id);
         crm_xml_add(request, PCMK__XA_ST_CLIENTNAME, pcmk__client_name(c));
         crm_xml_add(request, PCMK__XA_ST_CLIENTNODE, fenced_get_local_node());
 
         pcmk__cluster_send_message(NULL, pcmk_ipc_fenced, request);
         pcmk__xml_free(request);
         return 0;
     }
 
     if (c->name == NULL) {
         const char *value = crm_element_value(request, PCMK__XA_ST_CLIENTNAME);
 
         c->name = crm_strdup_printf("%s.%u", pcmk__s(value, "unknown"), c->pid);
     }
 
     rc = pcmk__xe_get_flags(request, PCMK__XA_ST_CALLOPT, &call_options,
                             st_opt_none);
     if (rc != pcmk_rc_ok) {
         crm_warn("Couldn't parse options from IPC request: %s",
                  pcmk_rc_str(rc));
     }
 
     crm_trace("Flags %#08" PRIx32 "/%#08x for command %" PRIu32
               " from client %s", flags, call_options, id, pcmk__client_name(c));
 
     if (pcmk_is_set(call_options, st_opt_sync_call)) {
         pcmk__assert(pcmk_is_set(flags, crm_ipc_client_response));
         CRM_LOG_ASSERT(c->request_id == 0);     /* This means the client has two synchronous events in-flight */
         c->request_id = id;     /* Reply only to the last one */
     }
 
     crm_xml_add(request, PCMK__XA_ST_CLIENTID, c->id);
     crm_xml_add(request, PCMK__XA_ST_CLIENTNAME, pcmk__client_name(c));
     crm_xml_add(request, PCMK__XA_ST_CLIENTNODE, fenced_get_local_node());
 
     crm_log_xml_trace(request, "ipc-received");
     stonith_command(c, id, flags, request, NULL);
 
     pcmk__xml_free(request);
     return 0;
 }
 
 /* Error code means? */
 static int32_t
 st_ipc_closed(qb_ipcs_connection_t * c)
 {
     pcmk__client_t *client = pcmk__find_client(c);
 
     if (client == NULL) {
         return 0;
     }
 
     crm_trace("Connection %p closed", c);
     pcmk__free_client(client);
 
     /* 0 means: yes, go ahead and destroy the connection */
     return 0;
 }
 
 static void
 st_ipc_destroy(qb_ipcs_connection_t * c)
 {
     crm_trace("Connection %p destroyed", c);
     st_ipc_closed(c);
 }
 
 static void
 stonith_peer_callback(xmlNode * msg, void *private_data)
 {
     const char *remote_peer = crm_element_value(msg, PCMK__XA_SRC);
     const char *op = crm_element_value(msg, PCMK__XA_ST_OP);
 
     if (pcmk__str_eq(op, STONITH_OP_POKE, pcmk__str_none)) {
         return;
     }
 
     crm_log_xml_trace(msg, "Peer[inbound]");
     stonith_command(NULL, 0, 0, msg, remote_peer);
 }
 
 #if SUPPORT_COROSYNC
 static void
 handle_cpg_message(cpg_handle_t handle, const struct cpg_name *groupName,
                    uint32_t nodeid, uint32_t pid, void *msg, size_t msg_len)
 {
     xmlNode *xml = NULL;
     const char *from = NULL;
     char *data = pcmk__cpg_message_data(handle, nodeid, pid, msg, &from);
 
     if(data == NULL) {
         return;
     }
 
     xml = pcmk__xml_parse(data);
     if (xml == NULL) {
         crm_err("Invalid XML: '%.120s'", data);
         free(data);
         return;
     }
     crm_xml_add(xml, PCMK__XA_SRC, from);
     stonith_peer_callback(xml, NULL);
 
     pcmk__xml_free(xml);
     free(data);
 }
 
 static void
 stonith_peer_cs_destroy(gpointer user_data)
 {
     crm_crit("Lost connection to cluster layer, shutting down");
     stonith_shutdown(0);
 }
 #endif
 
 void
 do_local_reply(const xmlNode *notify_src, pcmk__client_t *client,
                int call_options)
 {
     /* send callback to originating child */
     int local_rc = pcmk_rc_ok;
     int rid = 0;
     uint32_t ipc_flags = crm_ipc_server_event;
 
     if (pcmk_is_set(call_options, st_opt_sync_call)) {
         CRM_LOG_ASSERT(client->request_id);
         rid = client->request_id;
         client->request_id = 0;
         ipc_flags = crm_ipc_flags_none;
     }
 
     local_rc = pcmk__ipc_send_xml(client, rid, notify_src, ipc_flags);
     if (local_rc == pcmk_rc_ok) {
         crm_trace("Sent response %d to client %s",
                   rid, pcmk__client_name(client));
     } else {
         crm_warn("%synchronous reply to client %s failed: %s",
                  (pcmk_is_set(call_options, st_opt_sync_call)? "S" : "As"),
                  pcmk__client_name(client), pcmk_rc_str(local_rc));
     }
 }
 
 uint64_t
 get_stonith_flag(const char *name)
 {
     if (pcmk__str_eq(name, PCMK__VALUE_ST_NOTIFY_FENCE, pcmk__str_none)) {
         return fenced_nf_fence_result;
 
     } else if (pcmk__str_eq(name, STONITH_OP_DEVICE_ADD, pcmk__str_casei)) {
         return fenced_nf_device_registered;
 
     } else if (pcmk__str_eq(name, STONITH_OP_DEVICE_DEL, pcmk__str_casei)) {
-        return st_callback_device_del;
+        return fenced_nf_device_removed;
 
     } else if (pcmk__str_eq(name, PCMK__VALUE_ST_NOTIFY_HISTORY,
                             pcmk__str_none)) {
         return st_callback_notify_history;
 
     } else if (pcmk__str_eq(name, PCMK__VALUE_ST_NOTIFY_HISTORY_SYNCED,
                             pcmk__str_none)) {
         return st_callback_notify_history_synced;
 
     }
     return fenced_nf_none;
 }
 
 static void
 stonith_notify_client(gpointer key, gpointer value, gpointer user_data)
 {
 
     const xmlNode *update_msg = user_data;
     pcmk__client_t *client = value;
     const char *type = NULL;
 
     CRM_CHECK(client != NULL, return);
     CRM_CHECK(update_msg != NULL, return);
 
     type = crm_element_value(update_msg, PCMK__XA_SUBT);
     CRM_CHECK(type != NULL, crm_log_xml_err(update_msg, "notify"); return);
 
     if (client->ipcs == NULL) {
         crm_trace("Skipping client with NULL channel");
         return;
     }
 
     if (pcmk_is_set(client->flags, get_stonith_flag(type))) {
         int rc = pcmk__ipc_send_xml(client, 0, update_msg,
                                     crm_ipc_server_event);
 
         if (rc != pcmk_rc_ok) {
             crm_warn("%s notification of client %s failed: %s "
                      QB_XS " id=%.8s rc=%d", type, pcmk__client_name(client),
                      pcmk_rc_str(rc), client->id, rc);
         } else {
             crm_trace("Sent %s notification to client %s",
                       type, pcmk__client_name(client));
         }
     }
 }
 
 void
 do_stonith_async_timeout_update(const char *client_id, const char *call_id, int timeout)
 {
     pcmk__client_t *client = NULL;
     xmlNode *notify_data = NULL;
 
     if (!timeout || !call_id || !client_id) {
         return;
     }
 
     client = pcmk__find_client_by_id(client_id);
     if (!client) {
         return;
     }
 
     notify_data = pcmk__xe_create(NULL, PCMK__XE_ST_ASYNC_TIMEOUT_VALUE);
     crm_xml_add(notify_data, PCMK__XA_T, PCMK__VALUE_ST_ASYNC_TIMEOUT_VALUE);
     crm_xml_add(notify_data, PCMK__XA_ST_CALLID, call_id);
     crm_xml_add_int(notify_data, PCMK__XA_ST_TIMEOUT, timeout);
 
     crm_trace("timeout update is %d for client %s and call id %s", timeout, client_id, call_id);
 
     if (client) {
         pcmk__ipc_send_xml(client, 0, notify_data, crm_ipc_server_event);
     }
 
     pcmk__xml_free(notify_data);
 }
 
 /*!
  * \internal
  * \brief Notify relevant IPC clients of a fencing operation result
  *
  * \param[in] type     Notification type
  * \param[in] result   Result of fencing operation (assume success if NULL)
  * \param[in] data     If not NULL, add to notification as call data
  */
 void
 fenced_send_notification(const char *type, const pcmk__action_result_t *result,
                          xmlNode *data)
 {
     /* TODO: Standardize the contents of data */
     xmlNode *update_msg = pcmk__xe_create(NULL, PCMK__XE_NOTIFY);
 
     CRM_LOG_ASSERT(type != NULL);
 
     crm_xml_add(update_msg, PCMK__XA_T, PCMK__VALUE_ST_NOTIFY);
     crm_xml_add(update_msg, PCMK__XA_SUBT, type);
     crm_xml_add(update_msg, PCMK__XA_ST_OP, type);
     stonith__xe_set_result(update_msg, result);
 
     if (data != NULL) {
         xmlNode *wrapper = pcmk__xe_create(update_msg, PCMK__XE_ST_CALLDATA);
 
         pcmk__xml_copy(wrapper, data);
     }
 
     crm_trace("Notifying clients");
     pcmk__foreach_ipc_client(stonith_notify_client, update_msg);
     pcmk__xml_free(update_msg);
     crm_trace("Notify complete");
 }
 
 /*!
  * \internal
  * \brief Send notifications for a configuration change to subscribed clients
  *
  * \param[in] op      Notification type (\c STONITH_OP_DEVICE_ADD,
  *                    \c STONITH_OP_DEVICE_DEL, \c STONITH_OP_LEVEL_ADD, or
  *                    \c STONITH_OP_LEVEL_DEL)
  * \param[in] result  Operation result
  * \param[in] desc    Description of what changed (either device ID or string
  *                    representation of level
  *                    (<tt><target>[<level_index>]</tt>))
  */
 void
 fenced_send_config_notification(const char *op,
                                 const pcmk__action_result_t *result,
                                 const char *desc)
 {
     xmlNode *notify_data = pcmk__xe_create(NULL, op);
 
     crm_xml_add(notify_data, PCMK__XA_ST_DEVICE_ID, desc);
 
     fenced_send_notification(op, result, notify_data);
     pcmk__xml_free(notify_data);
 }
 
 /*!
  * \internal
  * \brief Check whether a node does watchdog-fencing
  *
  * \param[in] node    Name of node to check
  *
  * \return TRUE if node found in stonith_watchdog_targets
  *         or stonith_watchdog_targets is empty indicating
  *         all nodes are doing watchdog-fencing
  */
 gboolean
 node_does_watchdog_fencing(const char *node)
 {
     return ((stonith_watchdog_targets == NULL) ||
             pcmk__str_in_list(node, stonith_watchdog_targets, pcmk__str_casei));
 }
 
 void
 stonith_shutdown(int nsig)
 {
     crm_info("Terminating with %d clients", pcmk__ipc_client_count());
     stonith_shutdown_flag = TRUE;
     if (mainloop != NULL && g_main_loop_is_running(mainloop)) {
         g_main_loop_quit(mainloop);
     }
 }
 
 static void
 stonith_cleanup(void)
 {
     fenced_cib_cleanup();
     if (ipcs) {
         qb_ipcs_destroy(ipcs);
     }
 
     pcmk__cluster_destroy_node_caches();
     pcmk__client_cleanup();
     free_stonith_remote_op_list();
     free_topology_list();
     fenced_free_device_table();
     free_metadata_cache();
     fenced_unregister_handlers();
 }
 
 struct qb_ipcs_service_handlers ipc_callbacks = {
     .connection_accept = st_ipc_accept,
     .connection_created = NULL,
     .msg_process = st_ipc_dispatch,
     .connection_closed = st_ipc_closed,
     .connection_destroyed = st_ipc_destroy
 };
 
 /*!
  * \internal
  * \brief Callback for peer status changes
  *
  * \param[in] type  What changed
  * \param[in] node  What peer had the change
  * \param[in] data  Previous value of what changed
  */
 static void
 st_peer_update_callback(enum pcmk__node_update type, pcmk__node_status_t *node,
                         const void *data)
 {
     if ((type != pcmk__node_update_processes)
         && !pcmk_is_set(node->flags, pcmk__node_status_remote)) {
         /*
          * This is a hack until we can send to a nodeid and/or we fix node name lookups
          * These messages are ignored in stonith_peer_callback()
          */
         xmlNode *query = pcmk__xe_create(NULL, PCMK__XE_STONITH_COMMAND);
 
         crm_xml_add(query, PCMK__XA_T, PCMK__VALUE_STONITH_NG);
         crm_xml_add(query, PCMK__XA_ST_OP, STONITH_OP_POKE);
 
         crm_debug("Broadcasting our uname because of node %" PRIu32,
                   node->cluster_layer_id);
         pcmk__cluster_send_message(NULL, pcmk_ipc_fenced, query);
 
         pcmk__xml_free(query);
     }
 }
 
 /* @COMPAT Deprecated since 2.1.8. Use pcmk_list_fence_attrs() or
  * crm_resource --list-options=fencing instead of querying daemon metadata.
  *
  * NOTE: pcs (as of at least 0.11.8) uses this
  */
 static int
 fencer_metadata(void)
 {
     const char *name = PCMK__SERVER_FENCED;
     const char *desc_short = N_("Instance attributes available for all "
                                 "\"stonith\"-class resources");
     const char *desc_long = N_("Instance attributes available for all "
                                "\"stonith\"-class resources and used by "
                                "Pacemaker's fence daemon");
 
     return pcmk__daemon_metadata(out, name, desc_short, desc_long,
                                  pcmk__opt_fencing);
 }
 
 static GOptionEntry entries[] = {
     { "stand-alone", 's', G_OPTION_FLAG_NONE, G_OPTION_ARG_NONE,
       &options.stand_alone, N_("Intended for use in regression testing only"),
       NULL },
 
     { "logfile", 'l', G_OPTION_FLAG_NONE, G_OPTION_ARG_FILENAME_ARRAY,
       &options.log_files, N_("Send logs to the additional named logfile"), NULL },
 
     { NULL }
 };
 
 static GOptionContext *
 build_arg_context(pcmk__common_args_t *args, GOptionGroup **group)
 {
     GOptionContext *context = NULL;
 
     context = pcmk__build_arg_context(args, "text (default), xml", group, NULL);
     pcmk__add_main_args(context, entries);
     return context;
 }
 
 int
 main(int argc, char **argv)
 {
     int rc = pcmk_rc_ok;
     pcmk_cluster_t *cluster = NULL;
     crm_ipc_t *old_instance = NULL;
 
     GError *error = NULL;
 
     GOptionGroup *output_group = NULL;
     pcmk__common_args_t *args = pcmk__new_common_args(SUMMARY);
     gchar **processed_args = pcmk__cmdline_preproc(argv, "l");
     GOptionContext *context = build_arg_context(args, &output_group);
 
     crm_log_preinit(NULL, argc, argv);
 
     pcmk__register_formats(output_group, formats);
     if (!g_option_context_parse_strv(context, &processed_args, &error)) {
         exit_code = CRM_EX_USAGE;
         goto done;
     }
 
     rc = pcmk__output_new(&out, args->output_ty, args->output_dest, argv);
     if (rc != pcmk_rc_ok) {
         exit_code = CRM_EX_ERROR;
         g_set_error(&error, PCMK__EXITC_ERROR, exit_code,
                     "Error creating output format %s: %s",
                     args->output_ty, pcmk_rc_str(rc));
         goto done;
     }
 
     if (args->version) {
         out->version(out, false);
         goto done;
     }
 
     if ((g_strv_length(processed_args) >= 2)
         && pcmk__str_eq(processed_args[1], "metadata", pcmk__str_none)) {
 
         rc = fencer_metadata();
         if (rc != pcmk_rc_ok) {
             exit_code = CRM_EX_FATAL;
             g_set_error(&error, PCMK__EXITC_ERROR, exit_code,
                         "Unable to display metadata: %s", pcmk_rc_str(rc));
         }
         goto done;
     }
 
     // Open additional log files
     pcmk__add_logfiles(options.log_files, out);
 
     crm_log_init(NULL, LOG_INFO + args->verbosity, TRUE,
                  (args->verbosity > 0), argc, argv, FALSE);
 
     crm_notice("Starting Pacemaker fencer");
 
     old_instance = crm_ipc_new("stonith-ng", 0);
     if (old_instance == NULL) {
         /* crm_ipc_new() will have already logged an error message with
          * crm_err()
          */
         exit_code = CRM_EX_FATAL;
         goto done;
     }
 
     if (pcmk__connect_generic_ipc(old_instance) == pcmk_rc_ok) {
         // IPC endpoint already up
         crm_ipc_close(old_instance);
         crm_ipc_destroy(old_instance);
         crm_crit("Aborting start-up because another fencer instance is "
                  "already active");
         goto done;
     } else {
         // Not up or not authentic, we'll proceed either way
         crm_ipc_destroy(old_instance);
         old_instance = NULL;
     }
 
     mainloop_add_signal(SIGTERM, stonith_shutdown);
 
     pcmk__cluster_init_node_caches();
 
     rc = fenced_scheduler_init();
     if (rc != pcmk_rc_ok) {
         exit_code = CRM_EX_FATAL;
         g_set_error(&error, PCMK__EXITC_ERROR, exit_code,
                     "Error initializing scheduler data: %s", pcmk_rc_str(rc));
         goto done;
     }
 
     cluster = pcmk_cluster_new();
 
 #if SUPPORT_COROSYNC
     if (pcmk_get_cluster_layer() == pcmk_cluster_layer_corosync) {
         pcmk_cluster_set_destroy_fn(cluster, stonith_peer_cs_destroy);
         pcmk_cpg_set_deliver_fn(cluster, handle_cpg_message);
         pcmk_cpg_set_confchg_fn(cluster, pcmk__cpg_confchg_cb);
     }
 #endif // SUPPORT_COROSYNC
 
     pcmk__cluster_set_status_callback(&st_peer_update_callback);
 
     if (pcmk_cluster_connect(cluster) != pcmk_rc_ok) {
         exit_code = CRM_EX_FATAL;
         crm_crit("Cannot sign in to the cluster... terminating");
         goto done;
     }
     fenced_set_local_node(cluster->priv->node_name);
 
     if (!options.stand_alone) {
         setup_cib();
     }
 
     fenced_init_device_table();
     init_topology_list();
 
     pcmk__serve_fenced_ipc(&ipcs, &ipc_callbacks);
 
     // Create the mainloop and run it...
     mainloop = g_main_loop_new(NULL, FALSE);
     crm_notice("Pacemaker fencer successfully started and accepting connections");
     g_main_loop_run(mainloop);
 
 done:
     g_strfreev(processed_args);
     pcmk__free_arg_context(context);
 
     g_strfreev(options.log_files);
 
     stonith_cleanup();
     pcmk_cluster_free(cluster);
     fenced_scheduler_cleanup();
 
     pcmk__output_and_clear_error(&error, out);
 
     if (out != NULL) {
         out->finish(out, exit_code, true, NULL);
         pcmk__output_free(out);
     }
 
     pcmk__unregister_formats();
     crm_exit(exit_code);
 }
diff --git a/daemons/fenced/pacemaker-fenced.h b/daemons/fenced/pacemaker-fenced.h
index 13e4a43d64..80d16d1cff 100644
--- a/daemons/fenced/pacemaker-fenced.h
+++ b/daemons/fenced/pacemaker-fenced.h
@@ -1,401 +1,404 @@
 /*
  * Copyright 2009-2025 the Pacemaker project contributors
  *
  * This source code is licensed under the GNU General Public License version 2
  * or later (GPLv2+) WITHOUT ANY WARRANTY.
  */
 
 #include <stdint.h>                 // uint32_t, uint64_t
 #include <libxml/tree.h>            // xmlNode
 
 #include <crm/common/mainloop.h>
 #include <crm/cluster.h>
 #include <crm/stonith-ng.h>
 #include <crm/fencing/internal.h>
 
 /*!
  * \internal
  * \brief Check whether target has already been fenced recently
  *
  * \param[in] tolerance  Number of seconds to look back in time
  * \param[in] target     Name of node to search for
  * \param[in] action     Action we want to match
  *
  * \return TRUE if an equivalent fencing operation took place in the last
  *         \p tolerance seconds, FALSE otherwise
  */
 gboolean stonith_check_fence_tolerance(int tolerance, const char *target, const char *action);
 
 /*!
  * \internal
  * \brief Flags for \c fenced_device_t configuration, state, and support
  */
 enum fenced_device_flags {
     //! This flag has no effect
     fenced_df_none            = UINT32_C(0),
 
     //! Device supports list action
     fenced_df_supports_list   = (UINT32_C(1) << 0),
 
     //! Device supports on action
     fenced_df_supports_on     = (UINT32_C(1) << 1),
 
     //! Device supports reboot action
     fenced_df_supports_reboot = (UINT32_C(1) << 2),
 
     //! Device supports status action
     fenced_df_supports_status = (UINT32_C(1) << 3),
 
     //! Device is automatically used to unfence newly joined nodes
     fenced_df_auto_unfence    = (UINT32_C(1) << 4),
 
     //! Device has run a successful list, status, or monitor action on this node
     fenced_df_verified        = (UINT32_C(1) << 5),
 
     //! Device has been registered via the stonith API
     fenced_df_api_registered  = (UINT32_C(1) << 6),
 
     //! Device has been registered via the fencer's CIB diff callback
     fenced_df_cib_registered  = (UINT32_C(1) << 7),
 
     //! Device has not yet been re-registered after a CIB change
     fenced_df_dirty           = (UINT32_C(1) << 8),
 };
 
 /*!
  * \internal
  * \brief Set flags for a fencing device
  *
  * \param[in,out] device     Device whose flags to set (\c fenced_device_t)
  * \param[in]     set_flags  Group of <tt>enum fenced_device_flags</tt> to set
  */
 #define fenced_device_set_flags(device, set_flags) do {                     \
         pcmk__assert((device) != NULL);                                     \
         (device)->flags = pcmk__set_flags_as(__func__, __LINE__, LOG_TRACE, \
                                              "Fence device", (device)->id,  \
                                              (device)->flags, set_flags,    \
                                              #set_flags);                   \
     } while (0)
 
 /*!
  * \internal
  * \brief Clear flags for a fencing device
  *
  * \param[in,out] device       Device whose flags to clear (\c fenced_device_t)
  * \param[in]     clear_flags  Group of <tt>enum fenced_device_flags</tt> to
  *                             clear
  */
 #define fenced_device_clear_flags(device, clear_flags) do {                 \
         pcmk__assert((device) != NULL);                                     \
         (device)->flags = pcmk__clear_flags_as(__func__, __LINE__,          \
                                                LOG_TRACE, "Fence device",   \
                                                (device)->id,                \
                                                (device)->flags,             \
                                                clear_flags, #clear_flags);  \
     } while (0)
 
 /*!
  * \internal
  * \brief Flags for fencer client notification types
  */
 enum fenced_notify_flags {
     //! This flag has no effect
     fenced_nf_none              = UINT32_C(0),
 
     //! Notify about fencing operation results
     fenced_nf_fence_result      = (UINT32_C(1) << 0),
 
     // @TODO Consider notifying about device registrations via the CIB
     //! Notify about fencing device registrations via the fencer API
     fenced_nf_device_registered = (UINT32_C(1) << 1),
 
-    st_callback_device_del            = (UINT32_C(1) << 4),
+    // @TODO Consider notifying about device removals via the CIB
+    //! Notify about fencing device removals via the fencer API
+    fenced_nf_device_removed    = (UINT32_C(1) << 2),
+
     st_callback_notify_history        = (UINT32_C(1) << 5),
     st_callback_notify_history_synced = (UINT32_C(1) << 6),
 };
 
 typedef struct {
     char *id;
     char *agent;
     char *namespace;
 
     /*! list of actions that must execute on the target node. Used for unfencing */
     GString *on_target_actions;
     GList *targets;
     time_t targets_age;
 
     uint32_t flags; // Group of enum fenced_device_flags
 
     GHashTable *params;
     GHashTable *aliases;
     GList *pending_ops;
     mainloop_timer_t *timer;
     crm_trigger_t *work;
     xmlNode *agent_metadata;
     const char *default_host_arg;
 } fenced_device_t;
 
 /* These values are used to index certain arrays by "phase". Usually an
  * operation has only one "phase", so phase is always zero. However, some
  * reboots are remapped to "off" then "on", in which case "reboot" will be
  * phase 0, "off" will be phase 1 and "on" will be phase 2.
  */
 enum st_remap_phase {
     st_phase_requested = 0,
     st_phase_off = 1,
     st_phase_on = 2,
     st_phase_max = 3
 };
 
 typedef struct remote_fencing_op_s {
     /* @TODO Abstract the overlap with async_command_t (some members have
      * different names for the same thing), which should allow reducing
      * duplication in some functions
      */
 
     /* The unique id associated with this operation */
     char *id;
     /*! The node this operation will fence */
     char *target;
     /*! The fencing action to perform on the target. (reboot, on, off) */
     char *action;
 
     /*! When was the fencing action recorded (seconds since epoch) */
     time_t created;
 
     /*! Marks if the final notifications have been sent to local stonith clients. */
     gboolean notify_sent;
     /*! The number of query replies received */
     guint replies;
     /*! The number of query replies expected */
     guint replies_expected;
     /*! Does this node own control of this operation */
     gboolean owner;
     /*! After query is complete, This the high level timer that expires the entire operation */
     guint op_timer_total;
     /*! This timer expires the current fencing request. Many fencing
      * requests may exist in a single operation */
     guint op_timer_one;
     /*! This timer expires the query request sent out to determine
      * what nodes are contain what devices, and who those devices can fence */
     guint query_timer;
     /*! This is the default timeout to use for each fencing device if no
      * custom timeout is received in the query. */
     gint base_timeout;
     /*! This is the calculated total timeout an operation can take before
      * expiring. This is calculated by adding together all the timeout
      * values associated with the devices this fencing operation may call */
     gint total_timeout;
 
     /*!
      * Fencing delay (in seconds) requested by API client (used by controller to
      * implement \c PCMK_OPT_PRIORITY_FENCING_DELAY). A value of -1 means
      * disable all configured delays.
      */
     int client_delay;
 
     /*! Delegate is the node being asked to perform a fencing action
      * on behalf of the node that owns the remote operation. Some operations
      * will involve multiple delegates. This value represents the final delegate
      * that is used. */
     char *delegate;
     /*! The point at which the remote operation completed */
     time_t completed;
     //! Group of enum stonith_call_options associated with this operation
     uint32_t call_options;
 
     /*! The current state of the remote operation. This indicates
      * what stage the op is in, query, exec, done, duplicate, failed. */
     enum op_state state;
     /*! The node that owns the remote operation */
     char *originator;
     /*! The local client id that initiated the fencing request */
     char *client_id;
     /*! The client's call_id that initiated the fencing request */
     int client_callid;
     /*! The name of client that initiated the fencing request */
     char *client_name;
     /*! List of the received query results for all the nodes in the cpg group */
     GList *query_results;
     /*! The original request that initiated the remote stonith operation */
     xmlNode *request;
 
     /*! The current topology level being executed */
     guint level;
     /*! The current operation phase being executed */
     enum st_remap_phase phase;
 
     /*! Devices with automatic unfencing (always run if "on" requested, never if remapped) */
     GList *automatic_list;
     /*! List of all devices at the currently executing topology level */
     GList *devices_list;
     /*! Current entry in the topology device list */
     GList *devices;
 
     /*! List of duplicate operations attached to this operation. Once this operation
      * completes, the duplicate operations will be closed out as well. */
     GList *duplicates;
 
     /*! The point at which the remote operation completed(nsec) */
     long long completed_nsec;
 
     /*! The (potentially intermediate) result of the operation */
     pcmk__action_result_t result;
 } remote_fencing_op_t;
 
 void fenced_broadcast_op_result(const remote_fencing_op_t *op, bool op_merged);
 
 // How the user specified the target of a topology level
 enum fenced_target_by {
     fenced_target_by_unknown = -1,  // Invalid or not yet parsed
     fenced_target_by_name,          // By target name
     fenced_target_by_pattern,       // By a pattern matching target names
     fenced_target_by_attribute,     // By a node attribute/value on target
 };
 
 /*
  * Complex fencing requirements are specified via fencing topologies.
  * A topology consists of levels; each level is a list of fencing devices.
  * Topologies are stored in a hash table by node name. When a node needs to be
  * fenced, if it has an entry in the topology table, the levels are tried
  * sequentially, and the devices in each level are tried sequentially.
  * Fencing is considered successful as soon as any level succeeds;
  * a level is considered successful if all its devices succeed.
  * Essentially, all devices at a given level are "and-ed" and the
  * levels are "or-ed".
  *
  * This structure is used for the topology table entries.
  * Topology levels start from 1, so levels[0] is unused and always NULL.
  */
 typedef struct stonith_topology_s {
     enum fenced_target_by kind; // How target was specified
 
     /*! Node name regex or attribute name=value for which topology applies */
     char *target;
     char *target_value;
     char *target_pattern;
     char *target_attribute;
 
     /*! Names of fencing devices at each topology level */
     GList *levels[ST__LEVEL_COUNT];
 
 } stonith_topology_t;
 
 void stonith_shutdown(int nsig);
 
 void fenced_init_device_table(void);
 void fenced_free_device_table(void);
 bool fenced_has_watchdog_device(void);
 void fenced_foreach_device(GHFunc fn, gpointer user_data);
 void fenced_foreach_device_remove(GHRFunc fn);
 
 void init_topology_list(void);
 void free_topology_list(void);
 void free_stonith_remote_op_list(void);
 void init_stonith_remote_op_hash_table(GHashTable **table);
 void free_metadata_cache(void);
 void fenced_unregister_handlers(void);
 
 uint64_t get_stonith_flag(const char *name);
 
 void stonith_command(pcmk__client_t *client, uint32_t id, uint32_t flags,
                             xmlNode *op_request, const char *remote_peer);
 
 int fenced_device_register(const xmlNode *dev, bool from_cib);
 
 void stonith_device_remove(const char *id, bool from_cib);
 
 char *stonith_level_key(const xmlNode *msg, enum fenced_target_by);
 void fenced_register_level(xmlNode *msg, char **desc,
                            pcmk__action_result_t *result);
 void fenced_unregister_level(xmlNode *msg, char **desc,
                              pcmk__action_result_t *result);
 
 stonith_topology_t *find_topology_for_host(const char *host);
 
 void do_local_reply(const xmlNode *notify_src, pcmk__client_t *client,
                     int call_options);
 
 xmlNode *fenced_construct_reply(const xmlNode *request, xmlNode *data,
                                 const pcmk__action_result_t *result);
 
 void
  do_stonith_async_timeout_update(const char *client, const char *call_id, int timeout);
 
 void fenced_send_notification(const char *type,
                               const pcmk__action_result_t *result,
                               xmlNode *data);
 void fenced_send_config_notification(const char *op,
                                      const pcmk__action_result_t *result,
                                      const char *desc);
 
 remote_fencing_op_t *initiate_remote_stonith_op(const pcmk__client_t *client,
                                                 xmlNode *request,
                                                 gboolean manual_ack);
 
 void fenced_process_fencing_reply(xmlNode *msg);
 
 int process_remote_stonith_query(xmlNode * msg);
 
 void *create_remote_stonith_op(const char *client, xmlNode * request, gboolean peer);
 
 void stonith_fence_history(xmlNode *msg, xmlNode **output,
                            const char *remote_peer, int options);
 
 void stonith_fence_history_trim(void);
 
 bool fencing_peer_active(pcmk__node_status_t *peer);
 
 void set_fencing_completed(remote_fencing_op_t * op);
 
 int fenced_handle_manual_confirmation(const pcmk__client_t *client,
                                       xmlNode *msg);
 
 const char *fenced_device_reboot_action(const char *device_id);
 bool fenced_device_supports_on(const char *device_id);
 
 gboolean node_has_attr(const char *node, const char *name, const char *value);
 
 gboolean node_does_watchdog_fencing(const char *node);
 
 void fencing_topology_init(void);
 void setup_cib(void);
 void fenced_cib_cleanup(void);
 
 int fenced_scheduler_init(void);
 void fenced_set_local_node(const char *node_name);
 const char *fenced_get_local_node(void);
 void fenced_scheduler_cleanup(void);
 void fenced_scheduler_run(xmlNode *cib);
 
 static inline void
 fenced_set_protocol_error(pcmk__action_result_t *result)
 {
     pcmk__set_result(result, CRM_EX_PROTOCOL, PCMK_EXEC_INVALID,
                      "Fencer API request missing required information (bug?)");
 }
 
 /*!
  * \internal
  * \brief Get the device flag to use with a given action when searching devices
  *
  * \param[in] action  Action to check
  *
  * \return \c fenced_df_supports_on if \p action is "on", otherwise
  *         \c fenced_df_none
  */
 static inline uint32_t
 fenced_support_flag(const char *action)
 {
     if (pcmk__str_eq(action, PCMK_ACTION_ON, pcmk__str_none)) {
         return fenced_df_supports_on;
     }
     return fenced_df_none;
 }
 
 extern GHashTable *topology;
 extern long long stonith_watchdog_timeout_ms;
 extern GList *stonith_watchdog_targets;
 extern GHashTable *stonith_remote_op_list;
 extern crm_exit_t exit_code;
 extern gboolean stonith_shutdown_flag;