LCOV - code coverage report
Current view: top level - fencing - st_client.c (source / functions) Hit Total Coverage
Test: Pacemaker code coverage Lines: 0 1193 0.0 %
Date: 2024-05-07 11:09:47 Functions: 0 81 0.0 %

          Line data    Source code
       1             : /*
       2             :  * Copyright 2004-2024 the Pacemaker project contributors
       3             :  *
       4             :  * The version control history for this file may have further details.
       5             :  *
       6             :  * This source code is licensed under the GNU Lesser General Public License
       7             :  * version 2.1 or later (LGPLv2.1+) WITHOUT ANY WARRANTY.
       8             :  */
       9             : 
      10             : #include <crm_internal.h>
      11             : 
      12             : #include <stdlib.h>
      13             : #include <stdio.h>
      14             : #include <stdbool.h>
      15             : #include <string.h>
      16             : #include <ctype.h>
      17             : #include <inttypes.h>
      18             : #include <sys/types.h>
      19             : #include <glib.h>
      20             : 
      21             : #include <crm/crm.h>
      22             : #include <crm/stonith-ng.h>
      23             : #include <crm/fencing/internal.h>
      24             : #include <crm/common/xml.h>
      25             : 
      26             : #include <crm/common/mainloop.h>
      27             : 
      28             : #include "fencing_private.h"
      29             : 
      30             : CRM_TRACE_INIT_DATA(stonith);
      31             : 
      32             : // Used as stonith_t:st_private
      33             : typedef struct stonith_private_s {
      34             :     char *token;
      35             :     crm_ipc_t *ipc;
      36             :     mainloop_io_t *source;
      37             :     GHashTable *stonith_op_callback_table;
      38             :     GList *notify_list;
      39             :     int notify_refcnt;
      40             :     bool notify_deletes;
      41             : 
      42             :     void (*op_callback) (stonith_t * st, stonith_callback_data_t * data);
      43             : 
      44             : } stonith_private_t;
      45             : 
      46             : // Used as stonith_event_t:opaque
      47             : struct event_private {
      48             :     pcmk__action_result_t result;
      49             : };
      50             : 
      51             : typedef struct stonith_notify_client_s {
      52             :     const char *event;
      53             :     const char *obj_id;         /* implement one day */
      54             :     const char *obj_type;       /* implement one day */
      55             :     void (*notify) (stonith_t * st, stonith_event_t * e);
      56             :     bool delete;
      57             : 
      58             : } stonith_notify_client_t;
      59             : 
      60             : typedef struct stonith_callback_client_s {
      61             :     void (*callback) (stonith_t * st, stonith_callback_data_t * data);
      62             :     const char *id;
      63             :     void *user_data;
      64             :     gboolean only_success;
      65             :     gboolean allow_timeout_updates;
      66             :     struct timer_rec_s *timer;
      67             : 
      68             : } stonith_callback_client_t;
      69             : 
      70             : struct notify_blob_s {
      71             :     stonith_t *stonith;
      72             :     xmlNode *xml;
      73             : };
      74             : 
      75             : struct timer_rec_s {
      76             :     int call_id;
      77             :     int timeout;
      78             :     guint ref;
      79             :     stonith_t *stonith;
      80             : };
      81             : 
      82             : typedef int (*stonith_op_t) (const char *, int, const char *, xmlNode *,
      83             :                              xmlNode *, xmlNode *, xmlNode **, xmlNode **);
      84             : 
      85             : bool stonith_dispatch(stonith_t * st);
      86             : xmlNode *stonith_create_op(int call_id, const char *token, const char *op, xmlNode * data,
      87             :                            int call_options);
      88             : static int stonith_send_command(stonith_t *stonith, const char *op,
      89             :                                 xmlNode *data, xmlNode **output_data,
      90             :                                 int call_options, int timeout);
      91             : 
      92             : static void stonith_connection_destroy(gpointer user_data);
      93             : static void stonith_send_notification(gpointer data, gpointer user_data);
      94             : static int stonith_api_del_notification(stonith_t *stonith,
      95             :                                         const char *event);
      96             : /*!
      97             :  * \brief Get agent namespace by name
      98             :  *
      99             :  * \param[in] namespace_s  Name of namespace as string
     100             :  *
     101             :  * \return Namespace as enum value
     102             :  */
     103             : enum stonith_namespace
     104           0 : stonith_text2namespace(const char *namespace_s)
     105             : {
     106           0 :     if (pcmk__str_eq(namespace_s, "any", pcmk__str_null_matches)) {
     107           0 :         return st_namespace_any;
     108             : 
     109           0 :     } else if (!strcmp(namespace_s, "redhat")
     110           0 :                || !strcmp(namespace_s, "stonith-ng")) {
     111           0 :         return st_namespace_rhcs;
     112             : 
     113           0 :     } else if (!strcmp(namespace_s, "internal")) {
     114           0 :         return st_namespace_internal;
     115             : 
     116           0 :     } else if (!strcmp(namespace_s, "heartbeat")) {
     117           0 :         return st_namespace_lha;
     118             :     }
     119           0 :     return st_namespace_invalid;
     120             : }
     121             : 
     122             : /*!
     123             :  * \brief Get agent namespace name
     124             :  *
     125             :  * \param[in] namespace  Namespace as enum value
     126             :  *
     127             :  * \return Namespace name as string
     128             :  */
     129             : const char *
     130           0 : stonith_namespace2text(enum stonith_namespace st_namespace)
     131             : {
     132           0 :     switch (st_namespace) {
     133           0 :         case st_namespace_any:      return "any";
     134           0 :         case st_namespace_rhcs:     return "stonith-ng";
     135           0 :         case st_namespace_internal: return "internal";
     136           0 :         case st_namespace_lha:      return "heartbeat";
     137           0 :         default:                    break;
     138             :     }
     139           0 :     return "unsupported";
     140             : }
     141             : 
     142             : /*!
     143             :  * \brief Determine namespace of a fence agent
     144             :  *
     145             :  * \param[in] agent        Fence agent type
     146             :  * \param[in] namespace_s  Name of agent namespace as string, if known
     147             :  *
     148             :  * \return Namespace of specified agent, as enum value
     149             :  */
     150             : enum stonith_namespace
     151           0 : stonith_get_namespace(const char *agent, const char *namespace_s)
     152             : {
     153           0 :     if (pcmk__str_eq(namespace_s, "internal", pcmk__str_none)) {
     154           0 :         return st_namespace_internal;
     155             :     }
     156             : 
     157           0 :     if (stonith__agent_is_rhcs(agent)) {
     158           0 :         return st_namespace_rhcs;
     159             :     }
     160             : 
     161             : #if HAVE_STONITH_STONITH_H
     162             :     if (stonith__agent_is_lha(agent)) {
     163             :         return st_namespace_lha;
     164             :     }
     165             : #endif
     166             : 
     167           0 :     crm_err("Unknown fence agent: %s", agent);
     168           0 :     return st_namespace_invalid;
     169             : }
     170             : 
     171             : gboolean
     172           0 : stonith__watchdog_fencing_enabled_for_node_api(stonith_t *st, const char *node)
     173             : {
     174           0 :     gboolean rv = FALSE;
     175           0 :     stonith_t *stonith_api = st?st:stonith_api_new();
     176           0 :     char *list = NULL;
     177             : 
     178           0 :     if(stonith_api) {
     179           0 :         if (stonith_api->state == stonith_disconnected) {
     180           0 :             int rc = stonith_api->cmds->connect(stonith_api, "stonith-api", NULL);
     181             : 
     182           0 :             if (rc != pcmk_ok) {
     183           0 :                 crm_err("Failed connecting to Stonith-API for watchdog-fencing-query.");
     184             :             }
     185             :         }
     186             : 
     187           0 :         if (stonith_api->state != stonith_disconnected) {
     188             :             /* caveat!!!
     189             :              * this might fail when when stonithd is just updating the device-list
     190             :              * probably something we should fix as well for other api-calls */
     191           0 :             int rc = stonith_api->cmds->list(stonith_api, st_opt_sync_call, STONITH_WATCHDOG_ID, &list, 0);
     192           0 :             if ((rc != pcmk_ok) || (list == NULL)) {
     193             :                 /* due to the race described above it can happen that
     194             :                  * we drop in here - so as not to make remote nodes
     195             :                  * panic on that answer
     196             :                  */
     197           0 :                 if (rc == -ENODEV) {
     198           0 :                     crm_notice("Cluster does not have watchdog fencing device");
     199             :                 } else {
     200           0 :                     crm_warn("Could not check for watchdog fencing device: %s",
     201             :                              pcmk_strerror(rc));
     202             :                 }
     203           0 :             } else if (list[0] == '\0') {
     204           0 :                 rv = TRUE;
     205             :             } else {
     206           0 :                 GList *targets = stonith__parse_targets(list);
     207           0 :                 rv = pcmk__str_in_list(node, targets, pcmk__str_casei);
     208           0 :                 g_list_free_full(targets, free);
     209             :             }
     210           0 :             free(list);
     211           0 :             if (!st) {
     212             :                 /* if we're provided the api we still might have done the
     213             :                  * connection - but let's assume the caller won't bother
     214             :                  */
     215           0 :                 stonith_api->cmds->disconnect(stonith_api);
     216             :             }
     217             :         }
     218             : 
     219           0 :         if (!st) {
     220           0 :             stonith_api_delete(stonith_api);
     221             :         }
     222             :     } else {
     223           0 :         crm_err("Stonith-API for watchdog-fencing-query couldn't be created.");
     224             :     }
     225           0 :     crm_trace("Pacemaker assumes node %s %sto do watchdog-fencing.",
     226             :               node, rv?"":"not ");
     227           0 :     return rv;
     228             : }
     229             : 
     230             : gboolean
     231           0 : stonith__watchdog_fencing_enabled_for_node(const char *node)
     232             : {
     233           0 :     return stonith__watchdog_fencing_enabled_for_node_api(NULL, node);
     234             : }
     235             : 
     236             : /* when cycling through the list we don't want to delete items
     237             :    so just mark them and when we know nobody is using the list
     238             :    loop over it to remove the marked items
     239             :  */
     240             : static void
     241           0 : foreach_notify_entry (stonith_private_t *private,
     242             :                 GFunc func,
     243             :                 gpointer user_data)
     244             : {
     245           0 :     private->notify_refcnt++;
     246           0 :     g_list_foreach(private->notify_list, func, user_data);
     247           0 :     private->notify_refcnt--;
     248           0 :     if ((private->notify_refcnt == 0) &&
     249           0 :         private->notify_deletes) {
     250           0 :         GList *list_item = private->notify_list;
     251             : 
     252           0 :         private->notify_deletes = FALSE;
     253           0 :         while (list_item != NULL)
     254             :         {
     255           0 :             stonith_notify_client_t *list_client = list_item->data;
     256           0 :             GList *next = g_list_next(list_item);
     257             : 
     258           0 :             if (list_client->delete) {
     259           0 :                 free(list_client);
     260           0 :                 private->notify_list =
     261           0 :                     g_list_delete_link(private->notify_list, list_item);
     262             :             }
     263           0 :             list_item = next;
     264             :         }
     265             :     }
     266           0 : }
     267             : 
     268             : static void
     269           0 : stonith_connection_destroy(gpointer user_data)
     270             : {
     271           0 :     stonith_t *stonith = user_data;
     272           0 :     stonith_private_t *native = NULL;
     273             :     struct notify_blob_s blob;
     274             : 
     275           0 :     crm_trace("Sending destroyed notification");
     276           0 :     blob.stonith = stonith;
     277           0 :     blob.xml = pcmk__xe_create(NULL, PCMK__XE_NOTIFY);
     278             : 
     279           0 :     native = stonith->st_private;
     280           0 :     native->ipc = NULL;
     281           0 :     native->source = NULL;
     282             : 
     283           0 :     free(native->token); native->token = NULL;
     284           0 :     stonith->state = stonith_disconnected;
     285           0 :     crm_xml_add(blob.xml, PCMK__XA_T, PCMK__VALUE_ST_NOTIFY);
     286           0 :     crm_xml_add(blob.xml, PCMK__XA_SUBT, PCMK__VALUE_ST_NOTIFY_DISCONNECT);
     287             : 
     288           0 :     foreach_notify_entry(native, stonith_send_notification, &blob);
     289           0 :     free_xml(blob.xml);
     290           0 : }
     291             : 
     292             : xmlNode *
     293           0 : create_device_registration_xml(const char *id, enum stonith_namespace namespace,
     294             :                                const char *agent,
     295             :                                const stonith_key_value_t *params,
     296             :                                const char *rsc_provides)
     297             : {
     298           0 :     xmlNode *data = pcmk__xe_create(NULL, PCMK__XE_ST_DEVICE_ID);
     299           0 :     xmlNode *args = pcmk__xe_create(data, PCMK__XE_ATTRIBUTES);
     300             : 
     301             : #if HAVE_STONITH_STONITH_H
     302             :     if (namespace == st_namespace_any) {
     303             :         namespace = stonith_get_namespace(agent, NULL);
     304             :     }
     305             :     if (namespace == st_namespace_lha) {
     306             :         hash2field((gpointer) "plugin", (gpointer) agent, args);
     307             :         agent = "fence_legacy";
     308             :     }
     309             : #endif
     310             : 
     311           0 :     crm_xml_add(data, PCMK_XA_ID, id);
     312           0 :     crm_xml_add(data, PCMK__XA_ST_ORIGIN, __func__);
     313           0 :     crm_xml_add(data, PCMK_XA_AGENT, agent);
     314           0 :     if ((namespace != st_namespace_any) && (namespace != st_namespace_invalid)) {
     315           0 :         crm_xml_add(data, PCMK__XA_NAMESPACE,
     316             :                     stonith_namespace2text(namespace));
     317             :     }
     318           0 :     if (rsc_provides) {
     319           0 :         crm_xml_add(data, PCMK__XA_RSC_PROVIDES, rsc_provides);
     320             :     }
     321             : 
     322           0 :     for (; params; params = params->next) {
     323           0 :         hash2field((gpointer) params->key, (gpointer) params->value, args);
     324             :     }
     325             : 
     326           0 :     return data;
     327             : }
     328             : 
     329             : static int
     330           0 : stonith_api_register_device(stonith_t *st, int call_options,
     331             :                             const char *id, const char *namespace_s,
     332             :                             const char *agent,
     333             :                             const stonith_key_value_t *params)
     334             : {
     335           0 :     int rc = 0;
     336           0 :     xmlNode *data = NULL;
     337             : 
     338           0 :     data = create_device_registration_xml(id,
     339             :                                           stonith_text2namespace(namespace_s),
     340             :                                           agent, params, NULL);
     341             : 
     342           0 :     rc = stonith_send_command(st, STONITH_OP_DEVICE_ADD, data, NULL, call_options, 0);
     343           0 :     free_xml(data);
     344             : 
     345           0 :     return rc;
     346             : }
     347             : 
     348             : static int
     349           0 : stonith_api_remove_device(stonith_t * st, int call_options, const char *name)
     350             : {
     351           0 :     int rc = 0;
     352           0 :     xmlNode *data = NULL;
     353             : 
     354           0 :     data = pcmk__xe_create(NULL, PCMK__XE_ST_DEVICE_ID);
     355           0 :     crm_xml_add(data, PCMK__XA_ST_ORIGIN, __func__);
     356           0 :     crm_xml_add(data, PCMK_XA_ID, name);
     357           0 :     rc = stonith_send_command(st, STONITH_OP_DEVICE_DEL, data, NULL, call_options, 0);
     358           0 :     free_xml(data);
     359             : 
     360           0 :     return rc;
     361             : }
     362             : 
     363             : static int
     364           0 : stonith_api_remove_level_full(stonith_t *st, int options,
     365             :                               const char *node, const char *pattern,
     366             :                               const char *attr, const char *value, int level)
     367             : {
     368           0 :     int rc = 0;
     369           0 :     xmlNode *data = NULL;
     370             : 
     371           0 :     CRM_CHECK(node || pattern || (attr && value), return -EINVAL);
     372             : 
     373           0 :     data = pcmk__xe_create(NULL, PCMK_XE_FENCING_LEVEL);
     374           0 :     crm_xml_add(data, PCMK__XA_ST_ORIGIN, __func__);
     375             : 
     376           0 :     if (node) {
     377           0 :         crm_xml_add(data, PCMK_XA_TARGET, node);
     378             : 
     379           0 :     } else if (pattern) {
     380           0 :         crm_xml_add(data, PCMK_XA_TARGET_PATTERN, pattern);
     381             : 
     382             :     } else {
     383           0 :         crm_xml_add(data, PCMK_XA_TARGET_ATTRIBUTE, attr);
     384           0 :         crm_xml_add(data, PCMK_XA_TARGET_VALUE, value);
     385             :     }
     386             : 
     387           0 :     crm_xml_add_int(data, PCMK_XA_INDEX, level);
     388           0 :     rc = stonith_send_command(st, STONITH_OP_LEVEL_DEL, data, NULL, options, 0);
     389           0 :     free_xml(data);
     390             : 
     391           0 :     return rc;
     392             : }
     393             : 
     394             : static int
     395           0 : stonith_api_remove_level(stonith_t * st, int options, const char *node, int level)
     396             : {
     397           0 :     return stonith_api_remove_level_full(st, options, node,
     398             :                                          NULL, NULL, NULL, level);
     399             : }
     400             : 
     401             : /*!
     402             :  * \internal
     403             :  * \brief Create XML for fence topology level registration request
     404             :  *
     405             :  * \param[in] node        If not NULL, target level by this node name
     406             :  * \param[in] pattern     If not NULL, target by node name using this regex
     407             :  * \param[in] attr        If not NULL, target by this node attribute
     408             :  * \param[in] value       If not NULL, target by this node attribute value
     409             :  * \param[in] level       Index number of level to register
     410             :  * \param[in] device_list List of devices in level
     411             :  *
     412             :  * \return Newly allocated XML tree on success, NULL otherwise
     413             :  *
     414             :  * \note The caller should set only one of node, pattern or attr/value.
     415             :  */
     416             : xmlNode *
     417           0 : create_level_registration_xml(const char *node, const char *pattern,
     418             :                               const char *attr, const char *value,
     419             :                               int level, const stonith_key_value_t *device_list)
     420             : {
     421           0 :     GString *list = NULL;
     422             :     xmlNode *data;
     423             : 
     424           0 :     CRM_CHECK(node || pattern || (attr && value), return NULL);
     425             : 
     426           0 :     data = pcmk__xe_create(NULL, PCMK_XE_FENCING_LEVEL);
     427             : 
     428           0 :     crm_xml_add(data, PCMK__XA_ST_ORIGIN, __func__);
     429           0 :     crm_xml_add_int(data, PCMK_XA_ID, level);
     430           0 :     crm_xml_add_int(data, PCMK_XA_INDEX, level);
     431             : 
     432           0 :     if (node) {
     433           0 :         crm_xml_add(data, PCMK_XA_TARGET, node);
     434             : 
     435           0 :     } else if (pattern) {
     436           0 :         crm_xml_add(data, PCMK_XA_TARGET_PATTERN, pattern);
     437             : 
     438             :     } else {
     439           0 :         crm_xml_add(data, PCMK_XA_TARGET_ATTRIBUTE, attr);
     440           0 :         crm_xml_add(data, PCMK_XA_TARGET_VALUE, value);
     441             :     }
     442             : 
     443           0 :     for (; device_list; device_list = device_list->next) {
     444           0 :         pcmk__add_separated_word(&list, 1024, device_list->value, ",");
     445             :     }
     446             : 
     447           0 :     if (list != NULL) {
     448           0 :         crm_xml_add(data, PCMK_XA_DEVICES, (const char *) list->str);
     449           0 :         g_string_free(list, TRUE);
     450             :     }
     451           0 :     return data;
     452             : }
     453             : 
     454             : static int
     455           0 : stonith_api_register_level_full(stonith_t *st, int options, const char *node,
     456             :                                 const char *pattern, const char *attr,
     457             :                                 const char *value, int level,
     458             :                                 const stonith_key_value_t *device_list)
     459             : {
     460           0 :     int rc = 0;
     461           0 :     xmlNode *data = create_level_registration_xml(node, pattern, attr, value,
     462             :                                                   level, device_list);
     463           0 :     CRM_CHECK(data != NULL, return -EINVAL);
     464             : 
     465           0 :     rc = stonith_send_command(st, STONITH_OP_LEVEL_ADD, data, NULL, options, 0);
     466           0 :     free_xml(data);
     467             : 
     468           0 :     return rc;
     469             : }
     470             : 
     471             : static int
     472           0 : stonith_api_register_level(stonith_t * st, int options, const char *node, int level,
     473             :                            const stonith_key_value_t * device_list)
     474             : {
     475           0 :     return stonith_api_register_level_full(st, options, node, NULL, NULL, NULL,
     476             :                                            level, device_list);
     477             : }
     478             : 
     479             : static int
     480           0 : stonith_api_device_list(stonith_t *stonith, int call_options,
     481             :                         const char *namespace_s, stonith_key_value_t **devices,
     482             :                         int timeout)
     483             : {
     484           0 :     int count = 0;
     485           0 :     enum stonith_namespace ns = stonith_text2namespace(namespace_s);
     486             : 
     487           0 :     if (devices == NULL) {
     488           0 :         crm_err("Parameter error: stonith_api_device_list");
     489           0 :         return -EFAULT;
     490             :     }
     491             : 
     492             : #if HAVE_STONITH_STONITH_H
     493             :     // Include Linux-HA agents if requested
     494             :     if ((ns == st_namespace_any) || (ns == st_namespace_lha)) {
     495             :         count += stonith__list_lha_agents(devices);
     496             :     }
     497             : #endif
     498             : 
     499             :     // Include Red Hat agents if requested
     500           0 :     if ((ns == st_namespace_any) || (ns == st_namespace_rhcs)) {
     501           0 :         count += stonith__list_rhcs_agents(devices);
     502             :     }
     503             : 
     504           0 :     return count;
     505             : }
     506             : 
     507             : // See stonith_api_operations_t:metadata() documentation
     508             : static int
     509           0 : stonith_api_device_metadata(stonith_t *stonith, int call_options,
     510             :                             const char *agent, const char *namespace_s,
     511             :                             char **output, int timeout_sec)
     512             : {
     513             :     /* By executing meta-data directly, we can get it from stonith_admin when
     514             :      * the cluster is not running, which is important for higher-level tools.
     515             :      */
     516             : 
     517           0 :     enum stonith_namespace ns = stonith_get_namespace(agent, namespace_s);
     518             : 
     519           0 :     if (timeout_sec <= 0) {
     520           0 :         timeout_sec = PCMK_DEFAULT_METADATA_TIMEOUT_MS;
     521             :     }
     522             : 
     523           0 :     crm_trace("Looking up metadata for %s agent %s",
     524             :               stonith_namespace2text(ns), agent);
     525             : 
     526           0 :     switch (ns) {
     527           0 :         case st_namespace_rhcs:
     528           0 :             return stonith__rhcs_metadata(agent, timeout_sec, output);
     529             : 
     530             : #if HAVE_STONITH_STONITH_H
     531             :         case st_namespace_lha:
     532             :             return stonith__lha_metadata(agent, timeout_sec, output);
     533             : #endif
     534             : 
     535           0 :         default:
     536           0 :             crm_err("Can't get fence agent '%s' meta-data: No such agent",
     537             :                     agent);
     538           0 :             break;
     539             :     }
     540           0 :     return -ENODEV;
     541             : }
     542             : 
     543             : static int
     544           0 : stonith_api_query(stonith_t * stonith, int call_options, const char *target,
     545             :                   stonith_key_value_t ** devices, int timeout)
     546             : {
     547           0 :     int rc = 0, lpc = 0, max = 0;
     548             : 
     549           0 :     xmlNode *data = NULL;
     550           0 :     xmlNode *output = NULL;
     551           0 :     xmlXPathObjectPtr xpathObj = NULL;
     552             : 
     553           0 :     CRM_CHECK(devices != NULL, return -EINVAL);
     554             : 
     555           0 :     data = pcmk__xe_create(NULL, PCMK__XE_ST_DEVICE_ID);
     556           0 :     crm_xml_add(data, PCMK__XA_ST_ORIGIN, __func__);
     557           0 :     crm_xml_add(data, PCMK__XA_ST_TARGET, target);
     558           0 :     crm_xml_add(data, PCMK__XA_ST_DEVICE_ACTION, PCMK_ACTION_OFF);
     559           0 :     rc = stonith_send_command(stonith, STONITH_OP_QUERY, data, &output, call_options, timeout);
     560             : 
     561           0 :     if (rc < 0) {
     562           0 :         return rc;
     563             :     }
     564             : 
     565           0 :     xpathObj = xpath_search(output, "//@agent");
     566           0 :     if (xpathObj) {
     567           0 :         max = numXpathResults(xpathObj);
     568             : 
     569           0 :         for (lpc = 0; lpc < max; lpc++) {
     570           0 :             xmlNode *match = getXpathResult(xpathObj, lpc);
     571             : 
     572           0 :             CRM_LOG_ASSERT(match != NULL);
     573           0 :             if(match != NULL) {
     574           0 :                 xmlChar *match_path = xmlGetNodePath(match);
     575             : 
     576           0 :                 crm_info("%s[%d] = %s", "//@agent", lpc, match_path);
     577           0 :                 free(match_path);
     578           0 :                 *devices = stonith_key_value_add(*devices, NULL,
     579             :                                                  crm_element_value(match,
     580             :                                                                    PCMK_XA_ID));
     581             :             }
     582             :         }
     583             : 
     584           0 :         freeXpathObject(xpathObj);
     585             :     }
     586             : 
     587           0 :     free_xml(output);
     588           0 :     free_xml(data);
     589           0 :     return max;
     590             : }
     591             : 
     592             : /*!
     593             :  * \internal
     594             :  * \brief Make a STONITH_OP_EXEC request
     595             :  *
     596             :  * \param[in,out] stonith       Fencer connection
     597             :  * \param[in]     call_options  Bitmask of \c stonith_call_options
     598             :  * \param[in]     id            Fence device ID that request is for
     599             :  * \param[in]     action        Agent action to request (list, status, monitor)
     600             :  * \param[in]     target        Name of target node for requested action
     601             :  * \param[in]     timeout_sec   Error if not completed within this many seconds
     602             :  * \param[out]    output        Where to set agent output
     603             :  */
     604             : static int
     605           0 : stonith_api_call(stonith_t *stonith, int call_options, const char *id,
     606             :                  const char *action, const char *target, int timeout_sec,
     607             :                  xmlNode **output)
     608             : {
     609           0 :     int rc = 0;
     610           0 :     xmlNode *data = NULL;
     611             : 
     612           0 :     data = pcmk__xe_create(NULL, PCMK__XE_ST_DEVICE_ID);
     613           0 :     crm_xml_add(data, PCMK__XA_ST_ORIGIN, __func__);
     614           0 :     crm_xml_add(data, PCMK__XA_ST_DEVICE_ID, id);
     615           0 :     crm_xml_add(data, PCMK__XA_ST_DEVICE_ACTION, action);
     616           0 :     crm_xml_add(data, PCMK__XA_ST_TARGET, target);
     617             : 
     618           0 :     rc = stonith_send_command(stonith, STONITH_OP_EXEC, data, output,
     619             :                               call_options, timeout_sec);
     620           0 :     free_xml(data);
     621             : 
     622           0 :     return rc;
     623             : }
     624             : 
     625             : static int
     626           0 : stonith_api_list(stonith_t * stonith, int call_options, const char *id, char **list_info,
     627             :                  int timeout)
     628             : {
     629             :     int rc;
     630           0 :     xmlNode *output = NULL;
     631             : 
     632           0 :     rc = stonith_api_call(stonith, call_options, id, PCMK_ACTION_LIST, NULL,
     633             :                           timeout, &output);
     634             : 
     635           0 :     if (output && list_info) {
     636             :         const char *list_str;
     637             : 
     638           0 :         list_str = crm_element_value(output, PCMK__XA_ST_OUTPUT);
     639             : 
     640           0 :         if (list_str) {
     641           0 :             *list_info = strdup(list_str);
     642             :         }
     643             :     }
     644             : 
     645           0 :     if (output) {
     646           0 :         free_xml(output);
     647             :     }
     648             : 
     649           0 :     return rc;
     650             : }
     651             : 
     652             : static int
     653           0 : stonith_api_monitor(stonith_t * stonith, int call_options, const char *id, int timeout)
     654             : {
     655           0 :     return stonith_api_call(stonith, call_options, id, PCMK_ACTION_MONITOR,
     656             :                             NULL, timeout, NULL);
     657             : }
     658             : 
     659             : static int
     660           0 : stonith_api_status(stonith_t * stonith, int call_options, const char *id, const char *port,
     661             :                    int timeout)
     662             : {
     663           0 :     return stonith_api_call(stonith, call_options, id, PCMK_ACTION_STATUS, port,
     664             :                             timeout, NULL);
     665             : }
     666             : 
     667             : static int
     668           0 : stonith_api_fence_with_delay(stonith_t * stonith, int call_options, const char *node,
     669             :                              const char *action, int timeout, int tolerance, int delay)
     670             : {
     671           0 :     int rc = 0;
     672           0 :     xmlNode *data = NULL;
     673             : 
     674           0 :     data = pcmk__xe_create(NULL, __func__);
     675           0 :     crm_xml_add(data, PCMK__XA_ST_TARGET, node);
     676           0 :     crm_xml_add(data, PCMK__XA_ST_DEVICE_ACTION, action);
     677           0 :     crm_xml_add_int(data, PCMK__XA_ST_TIMEOUT, timeout);
     678           0 :     crm_xml_add_int(data, PCMK__XA_ST_TOLERANCE, tolerance);
     679           0 :     crm_xml_add_int(data, PCMK__XA_ST_DELAY, delay);
     680             : 
     681           0 :     rc = stonith_send_command(stonith, STONITH_OP_FENCE, data, NULL, call_options, timeout);
     682           0 :     free_xml(data);
     683             : 
     684           0 :     return rc;
     685             : }
     686             : 
     687             : static int
     688           0 : stonith_api_fence(stonith_t * stonith, int call_options, const char *node, const char *action,
     689             :                   int timeout, int tolerance)
     690             : {
     691           0 :     return stonith_api_fence_with_delay(stonith, call_options, node, action,
     692             :                                         timeout, tolerance, 0);
     693             : }
     694             : 
     695             : static int
     696           0 : stonith_api_confirm(stonith_t * stonith, int call_options, const char *target)
     697             : {
     698           0 :     stonith__set_call_options(call_options, target, st_opt_manual_ack);
     699           0 :     return stonith_api_fence(stonith, call_options, target, PCMK_ACTION_OFF, 0,
     700             :                              0);
     701             : }
     702             : 
     703             : static int
     704           0 : stonith_api_history(stonith_t * stonith, int call_options, const char *node,
     705             :                     stonith_history_t ** history, int timeout)
     706             : {
     707           0 :     int rc = 0;
     708           0 :     xmlNode *data = NULL;
     709           0 :     xmlNode *output = NULL;
     710           0 :     stonith_history_t *last = NULL;
     711             : 
     712           0 :     *history = NULL;
     713             : 
     714           0 :     if (node) {
     715           0 :         data = pcmk__xe_create(NULL, __func__);
     716           0 :         crm_xml_add(data, PCMK__XA_ST_TARGET, node);
     717             :     }
     718             : 
     719           0 :     stonith__set_call_options(call_options, node, st_opt_sync_call);
     720           0 :     rc = stonith_send_command(stonith, STONITH_OP_FENCE_HISTORY, data, &output,
     721             :                               call_options, timeout);
     722           0 :     free_xml(data);
     723             : 
     724           0 :     if (rc == 0) {
     725           0 :         xmlNode *op = NULL;
     726           0 :         xmlNode *reply = get_xpath_object("//" PCMK__XE_ST_HISTORY, output,
     727             :                                           LOG_NEVER);
     728             : 
     729           0 :         for (op = pcmk__xe_first_child(reply, NULL, NULL, NULL); op != NULL;
     730           0 :              op = pcmk__xe_next(op)) {
     731             :             stonith_history_t *kvp;
     732             :             long long completed;
     733           0 :             long long completed_nsec = 0L;
     734             : 
     735           0 :             kvp = pcmk__assert_alloc(1, sizeof(stonith_history_t));
     736           0 :             kvp->target = crm_element_value_copy(op, PCMK__XA_ST_TARGET);
     737           0 :             kvp->action = crm_element_value_copy(op, PCMK__XA_ST_DEVICE_ACTION);
     738           0 :             kvp->origin = crm_element_value_copy(op, PCMK__XA_ST_ORIGIN);
     739           0 :             kvp->delegate = crm_element_value_copy(op, PCMK__XA_ST_DELEGATE);
     740           0 :             kvp->client = crm_element_value_copy(op, PCMK__XA_ST_CLIENTNAME);
     741           0 :             crm_element_value_ll(op, PCMK__XA_ST_DATE, &completed);
     742           0 :             kvp->completed = (time_t) completed;
     743           0 :             crm_element_value_ll(op, PCMK__XA_ST_DATE_NSEC, &completed_nsec);
     744           0 :             kvp->completed_nsec = completed_nsec;
     745           0 :             crm_element_value_int(op, PCMK__XA_ST_STATE, &kvp->state);
     746           0 :             kvp->exit_reason = crm_element_value_copy(op, PCMK_XA_EXIT_REASON);
     747             : 
     748           0 :             if (last) {
     749           0 :                 last->next = kvp;
     750             :             } else {
     751           0 :                 *history = kvp;
     752             :             }
     753           0 :             last = kvp;
     754             :         }
     755             :     }
     756             : 
     757           0 :     free_xml(output);
     758             : 
     759           0 :     return rc;
     760             : }
     761             : 
     762           0 : void stonith_history_free(stonith_history_t *history)
     763             : {
     764             :     stonith_history_t *hp, *hp_old;
     765             : 
     766           0 :     for (hp = history; hp; hp_old = hp, hp = hp->next, free(hp_old)) {
     767           0 :         free(hp->target);
     768           0 :         free(hp->action);
     769           0 :         free(hp->origin);
     770           0 :         free(hp->delegate);
     771           0 :         free(hp->client);
     772           0 :         free(hp->exit_reason);
     773             :     }
     774           0 : }
     775             : 
     776             : static gint
     777           0 : stonithlib_GCompareFunc(gconstpointer a, gconstpointer b)
     778             : {
     779           0 :     int rc = 0;
     780           0 :     const stonith_notify_client_t *a_client = a;
     781           0 :     const stonith_notify_client_t *b_client = b;
     782             : 
     783           0 :     if (a_client->delete || b_client->delete) {
     784             :         /* make entries marked for deletion not findable */
     785           0 :         return -1;
     786             :     }
     787           0 :     CRM_CHECK(a_client->event != NULL && b_client->event != NULL, return 0);
     788           0 :     rc = strcmp(a_client->event, b_client->event);
     789           0 :     if (rc == 0) {
     790           0 :         if (a_client->notify == NULL || b_client->notify == NULL) {
     791           0 :             return 0;
     792             : 
     793           0 :         } else if (a_client->notify == b_client->notify) {
     794           0 :             return 0;
     795             : 
     796           0 :         } else if (((long)a_client->notify) < ((long)b_client->notify)) {
     797           0 :             crm_err("callbacks for %s are not equal: %p vs. %p",
     798             :                     a_client->event, a_client->notify, b_client->notify);
     799           0 :             return -1;
     800             :         }
     801           0 :         crm_err("callbacks for %s are not equal: %p vs. %p",
     802             :                 a_client->event, a_client->notify, b_client->notify);
     803           0 :         return 1;
     804             :     }
     805           0 :     return rc;
     806             : }
     807             : 
     808             : xmlNode *
     809           0 : stonith_create_op(int call_id, const char *token, const char *op, xmlNode * data, int call_options)
     810             : {
     811           0 :     xmlNode *op_msg = NULL;
     812             : 
     813           0 :     CRM_CHECK(token != NULL, return NULL);
     814             : 
     815           0 :     op_msg = pcmk__xe_create(NULL, PCMK__XE_STONITH_COMMAND);
     816           0 :     crm_xml_add(op_msg, PCMK__XA_T, PCMK__VALUE_STONITH_NG);
     817           0 :     crm_xml_add(op_msg, PCMK__XA_ST_OP, op);
     818           0 :     crm_xml_add_int(op_msg, PCMK__XA_ST_CALLID, call_id);
     819           0 :     crm_trace("Sending call options: %.8lx, %d", (long)call_options, call_options);
     820           0 :     crm_xml_add_int(op_msg, PCMK__XA_ST_CALLOPT, call_options);
     821             : 
     822           0 :     if (data != NULL) {
     823           0 :         xmlNode *wrapper = pcmk__xe_create(op_msg, PCMK__XE_ST_CALLDATA);
     824             : 
     825           0 :         pcmk__xml_copy(wrapper, data);
     826             :     }
     827             : 
     828           0 :     return op_msg;
     829             : }
     830             : 
     831             : static void
     832           0 : stonith_destroy_op_callback(gpointer data)
     833             : {
     834           0 :     stonith_callback_client_t *blob = data;
     835             : 
     836           0 :     if (blob->timer && blob->timer->ref > 0) {
     837           0 :         g_source_remove(blob->timer->ref);
     838             :     }
     839           0 :     free(blob->timer);
     840           0 :     free(blob);
     841           0 : }
     842             : 
     843             : static int
     844           0 : stonith_api_signoff(stonith_t * stonith)
     845             : {
     846           0 :     stonith_private_t *native = stonith->st_private;
     847             : 
     848           0 :     crm_debug("Disconnecting from the fencer");
     849             : 
     850           0 :     if (native->source != NULL) {
     851             :         /* Attached to mainloop */
     852           0 :         mainloop_del_ipc_client(native->source);
     853           0 :         native->source = NULL;
     854           0 :         native->ipc = NULL;
     855             : 
     856           0 :     } else if (native->ipc) {
     857             :         /* Not attached to mainloop */
     858           0 :         crm_ipc_t *ipc = native->ipc;
     859             : 
     860           0 :         native->ipc = NULL;
     861           0 :         crm_ipc_close(ipc);
     862           0 :         crm_ipc_destroy(ipc);
     863             :     }
     864             : 
     865           0 :     free(native->token); native->token = NULL;
     866           0 :     stonith->state = stonith_disconnected;
     867           0 :     return pcmk_ok;
     868             : }
     869             : 
     870             : static int
     871           0 : stonith_api_del_callback(stonith_t * stonith, int call_id, bool all_callbacks)
     872             : {
     873           0 :     stonith_private_t *private = stonith->st_private;
     874             : 
     875           0 :     if (all_callbacks) {
     876           0 :         private->op_callback = NULL;
     877           0 :         g_hash_table_destroy(private->stonith_op_callback_table);
     878           0 :         private->stonith_op_callback_table = pcmk__intkey_table(stonith_destroy_op_callback);
     879             : 
     880           0 :     } else if (call_id == 0) {
     881           0 :         private->op_callback = NULL;
     882             : 
     883             :     } else {
     884           0 :         pcmk__intkey_table_remove(private->stonith_op_callback_table, call_id);
     885             :     }
     886           0 :     return pcmk_ok;
     887             : }
     888             : 
     889             : /*!
     890             :  * \internal
     891             :  * \brief Invoke a (single) specified fence action callback
     892             :  *
     893             :  * \param[in,out] st        Fencer API connection
     894             :  * \param[in]     call_id   If positive, call ID of completed fence action,
     895             :  *                          otherwise legacy return code for early failure
     896             :  * \param[in,out] result    Full result for action
     897             :  * \param[in,out] userdata  User data to pass to callback
     898             :  * \param[in]     callback  Fence action callback to invoke
     899             :  */
     900             : static void
     901           0 : invoke_fence_action_callback(stonith_t *st, int call_id,
     902             :                              pcmk__action_result_t *result,
     903             :                              void *userdata,
     904             :                              void (*callback) (stonith_t *st,
     905             :                                                stonith_callback_data_t *data))
     906             : {
     907           0 :     stonith_callback_data_t data = { 0, };
     908             : 
     909           0 :     data.call_id = call_id;
     910           0 :     data.rc = pcmk_rc2legacy(stonith__result2rc(result));
     911           0 :     data.userdata = userdata;
     912           0 :     data.opaque = (void *) result;
     913             : 
     914           0 :     callback(st, &data);
     915           0 : }
     916             : 
     917             : /*!
     918             :  * \internal
     919             :  * \brief Invoke any callbacks registered for a specified fence action result
     920             :  *
     921             :  * Given a fence action result from the fencer, invoke any callback registered
     922             :  * for that action, as well as any global callback registered.
     923             :  *
     924             :  * \param[in,out] stonith   Fencer API connection
     925             :  * \param[in]     msg       If non-NULL, fencer reply
     926             :  * \param[in]     call_id   If \p msg is NULL, call ID of action that timed out
     927             :  */
     928             : static void
     929           0 : invoke_registered_callbacks(stonith_t *stonith, const xmlNode *msg, int call_id)
     930             : {
     931           0 :     stonith_private_t *private = NULL;
     932           0 :     stonith_callback_client_t *cb_info = NULL;
     933           0 :     pcmk__action_result_t result = PCMK__UNKNOWN_RESULT;
     934             : 
     935           0 :     CRM_CHECK(stonith != NULL, return);
     936           0 :     CRM_CHECK(stonith->st_private != NULL, return);
     937             : 
     938           0 :     private = stonith->st_private;
     939             : 
     940           0 :     if (msg == NULL) {
     941             :         // Fencer didn't reply in time
     942           0 :         pcmk__set_result(&result, CRM_EX_ERROR, PCMK_EXEC_TIMEOUT,
     943             :                          "Fencer accepted request but did not reply in time");
     944           0 :         CRM_LOG_ASSERT(call_id > 0);
     945             : 
     946             :     } else {
     947             :         // We have the fencer reply
     948           0 :         if ((crm_element_value_int(msg, PCMK__XA_ST_CALLID, &call_id) != 0)
     949           0 :             || (call_id <= 0)) {
     950           0 :             crm_log_xml_warn(msg, "Bad fencer reply");
     951             :         }
     952           0 :         stonith__xe_get_result(msg, &result);
     953             :     }
     954             : 
     955           0 :     if (call_id > 0) {
     956           0 :         cb_info = pcmk__intkey_table_lookup(private->stonith_op_callback_table,
     957             :                                             call_id);
     958             :     }
     959             : 
     960           0 :     if ((cb_info != NULL) && (cb_info->callback != NULL)
     961           0 :         && (pcmk__result_ok(&result) || !(cb_info->only_success))) {
     962           0 :         crm_trace("Invoking callback %s for call %d",
     963             :                   pcmk__s(cb_info->id, "without ID"), call_id);
     964           0 :         invoke_fence_action_callback(stonith, call_id, &result,
     965             :                                      cb_info->user_data, cb_info->callback);
     966             : 
     967           0 :     } else if ((private->op_callback == NULL) && !pcmk__result_ok(&result)) {
     968           0 :         crm_warn("Fencing action without registered callback failed: %d (%s%s%s)",
     969             :                  result.exit_status,
     970             :                  pcmk_exec_status_str(result.execution_status),
     971             :                  ((result.exit_reason == NULL)? "" : ": "),
     972             :                  ((result.exit_reason == NULL)? "" : result.exit_reason));
     973           0 :         crm_log_xml_debug(msg, "Failed fence update");
     974             :     }
     975             : 
     976           0 :     if (private->op_callback != NULL) {
     977           0 :         crm_trace("Invoking global callback for call %d", call_id);
     978           0 :         invoke_fence_action_callback(stonith, call_id, &result, NULL,
     979             :                                      private->op_callback);
     980             :     }
     981             : 
     982           0 :     if (cb_info != NULL) {
     983           0 :         stonith_api_del_callback(stonith, call_id, FALSE);
     984             :     }
     985           0 :     pcmk__reset_result(&result);
     986             : }
     987             : 
     988             : static gboolean
     989           0 : stonith_async_timeout_handler(gpointer data)
     990             : {
     991           0 :     struct timer_rec_s *timer = data;
     992             : 
     993           0 :     crm_err("Async call %d timed out after %dms", timer->call_id, timer->timeout);
     994           0 :     invoke_registered_callbacks(timer->stonith, NULL, timer->call_id);
     995             : 
     996             :     /* Always return TRUE, never remove the handler
     997             :      * We do that in stonith_del_callback()
     998             :      */
     999           0 :     return TRUE;
    1000             : }
    1001             : 
    1002             : static void
    1003           0 : set_callback_timeout(stonith_callback_client_t * callback, stonith_t * stonith, int call_id,
    1004             :                      int timeout)
    1005             : {
    1006           0 :     struct timer_rec_s *async_timer = callback->timer;
    1007             : 
    1008           0 :     if (timeout <= 0) {
    1009           0 :         return;
    1010             :     }
    1011             : 
    1012           0 :     if (!async_timer) {
    1013           0 :         async_timer = pcmk__assert_alloc(1, sizeof(struct timer_rec_s));
    1014           0 :         callback->timer = async_timer;
    1015             :     }
    1016             : 
    1017           0 :     async_timer->stonith = stonith;
    1018           0 :     async_timer->call_id = call_id;
    1019             :     /* Allow a fair bit of grace to allow the server to tell us of a timeout
    1020             :      * This is only a fallback
    1021             :      */
    1022           0 :     async_timer->timeout = (timeout + 60) * 1000;
    1023           0 :     if (async_timer->ref) {
    1024           0 :         g_source_remove(async_timer->ref);
    1025             :     }
    1026           0 :     async_timer->ref =
    1027           0 :         g_timeout_add(async_timer->timeout, stonith_async_timeout_handler, async_timer);
    1028             : }
    1029             : 
    1030             : static void
    1031           0 : update_callback_timeout(int call_id, int timeout, stonith_t * st)
    1032             : {
    1033           0 :     stonith_callback_client_t *callback = NULL;
    1034           0 :     stonith_private_t *private = st->st_private;
    1035             : 
    1036           0 :     callback = pcmk__intkey_table_lookup(private->stonith_op_callback_table,
    1037             :                                          call_id);
    1038           0 :     if (!callback || !callback->allow_timeout_updates) {
    1039           0 :         return;
    1040             :     }
    1041             : 
    1042           0 :     set_callback_timeout(callback, st, call_id, timeout);
    1043             : }
    1044             : 
    1045             : static int
    1046           0 : stonith_dispatch_internal(const char *buffer, ssize_t length, gpointer userdata)
    1047             : {
    1048           0 :     const char *type = NULL;
    1049             :     struct notify_blob_s blob;
    1050             : 
    1051           0 :     stonith_t *st = userdata;
    1052           0 :     stonith_private_t *private = NULL;
    1053             : 
    1054           0 :     CRM_ASSERT(st != NULL);
    1055           0 :     private = st->st_private;
    1056             : 
    1057           0 :     blob.stonith = st;
    1058           0 :     blob.xml = pcmk__xml_parse(buffer);
    1059           0 :     if (blob.xml == NULL) {
    1060           0 :         crm_warn("Received malformed message from fencer: %s", buffer);
    1061           0 :         return 0;
    1062             :     }
    1063             : 
    1064             :     /* do callbacks */
    1065           0 :     type = crm_element_value(blob.xml, PCMK__XA_T);
    1066           0 :     crm_trace("Activating %s callbacks...", type);
    1067             : 
    1068           0 :     if (pcmk__str_eq(type, PCMK__VALUE_STONITH_NG, pcmk__str_none)) {
    1069           0 :         invoke_registered_callbacks(st, blob.xml, 0);
    1070             : 
    1071           0 :     } else if (pcmk__str_eq(type, PCMK__VALUE_ST_NOTIFY, pcmk__str_none)) {
    1072           0 :         foreach_notify_entry(private, stonith_send_notification, &blob);
    1073             : 
    1074           0 :     } else if (pcmk__str_eq(type, PCMK__VALUE_ST_ASYNC_TIMEOUT_VALUE,
    1075             :                             pcmk__str_none)) {
    1076           0 :         int call_id = 0;
    1077           0 :         int timeout = 0;
    1078             : 
    1079           0 :         crm_element_value_int(blob.xml, PCMK__XA_ST_TIMEOUT, &timeout);
    1080           0 :         crm_element_value_int(blob.xml, PCMK__XA_ST_CALLID, &call_id);
    1081             : 
    1082           0 :         update_callback_timeout(call_id, timeout, st);
    1083             :     } else {
    1084           0 :         crm_err("Unknown message type: %s", type);
    1085           0 :         crm_log_xml_warn(blob.xml, "BadReply");
    1086             :     }
    1087             : 
    1088           0 :     free_xml(blob.xml);
    1089           0 :     return 1;
    1090             : }
    1091             : 
    1092             : static int
    1093           0 : stonith_api_signon(stonith_t * stonith, const char *name, int *stonith_fd)
    1094             : {
    1095           0 :     int rc = pcmk_ok;
    1096           0 :     stonith_private_t *native = NULL;
    1097           0 :     const char *display_name = name? name : "client";
    1098             : 
    1099           0 :     struct ipc_client_callbacks st_callbacks = {
    1100             :         .dispatch = stonith_dispatch_internal,
    1101             :         .destroy = stonith_connection_destroy
    1102             :     };
    1103             : 
    1104           0 :     CRM_CHECK(stonith != NULL, return -EINVAL);
    1105             : 
    1106           0 :     native = stonith->st_private;
    1107           0 :     CRM_ASSERT(native != NULL);
    1108             : 
    1109           0 :     crm_debug("Attempting fencer connection by %s with%s mainloop",
    1110             :               display_name, (stonith_fd? "out" : ""));
    1111             : 
    1112           0 :     stonith->state = stonith_connected_command;
    1113           0 :     if (stonith_fd) {
    1114             :         /* No mainloop */
    1115           0 :         native->ipc = crm_ipc_new("stonith-ng", 0);
    1116           0 :         if (native->ipc != NULL) {
    1117           0 :             rc = pcmk__connect_generic_ipc(native->ipc);
    1118           0 :             if (rc == pcmk_rc_ok) {
    1119           0 :                 rc = pcmk__ipc_fd(native->ipc, stonith_fd);
    1120           0 :                 if (rc != pcmk_rc_ok) {
    1121           0 :                     crm_debug("Couldn't get file descriptor for IPC: %s",
    1122             :                               pcmk_rc_str(rc));
    1123             :                 }
    1124             :             }
    1125           0 :             if (rc != pcmk_rc_ok) {
    1126           0 :                 crm_ipc_close(native->ipc);
    1127           0 :                 crm_ipc_destroy(native->ipc);
    1128           0 :                 native->ipc = NULL;
    1129             :             }
    1130             :         }
    1131             : 
    1132             :     } else {
    1133             :         /* With mainloop */
    1134           0 :         native->source =
    1135           0 :             mainloop_add_ipc_client("stonith-ng", G_PRIORITY_MEDIUM, 0, stonith, &st_callbacks);
    1136           0 :         native->ipc = mainloop_get_ipc_client(native->source);
    1137             :     }
    1138             : 
    1139           0 :     if (native->ipc == NULL) {
    1140           0 :         rc = -ENOTCONN;
    1141             :     } else {
    1142           0 :         xmlNode *reply = NULL;
    1143           0 :         xmlNode *hello = pcmk__xe_create(NULL, PCMK__XE_STONITH_COMMAND);
    1144             : 
    1145           0 :         crm_xml_add(hello, PCMK__XA_T, PCMK__VALUE_STONITH_NG);
    1146           0 :         crm_xml_add(hello, PCMK__XA_ST_OP, CRM_OP_REGISTER);
    1147           0 :         crm_xml_add(hello, PCMK__XA_ST_CLIENTNAME, name);
    1148           0 :         rc = crm_ipc_send(native->ipc, hello, crm_ipc_client_response, -1, &reply);
    1149             : 
    1150           0 :         if (rc < 0) {
    1151           0 :             crm_debug("Couldn't register with the fencer: %s "
    1152             :                       CRM_XS " rc=%d", pcmk_strerror(rc), rc);
    1153           0 :             rc = -ECOMM;
    1154             : 
    1155           0 :         } else if (reply == NULL) {
    1156           0 :             crm_debug("Couldn't register with the fencer: no reply");
    1157           0 :             rc = -EPROTO;
    1158             : 
    1159             :         } else {
    1160           0 :             const char *msg_type = crm_element_value(reply, PCMK__XA_ST_OP);
    1161             : 
    1162           0 :             native->token = crm_element_value_copy(reply, PCMK__XA_ST_CLIENTID);
    1163           0 :             if (!pcmk__str_eq(msg_type, CRM_OP_REGISTER, pcmk__str_none)) {
    1164           0 :                 crm_debug("Couldn't register with the fencer: invalid reply type '%s'",
    1165             :                           (msg_type? msg_type : "(missing)"));
    1166           0 :                 crm_log_xml_debug(reply, "Invalid fencer reply");
    1167           0 :                 rc = -EPROTO;
    1168             : 
    1169           0 :             } else if (native->token == NULL) {
    1170           0 :                 crm_debug("Couldn't register with the fencer: no token in reply");
    1171           0 :                 crm_log_xml_debug(reply, "Invalid fencer reply");
    1172           0 :                 rc = -EPROTO;
    1173             : 
    1174             :             } else {
    1175           0 :                 crm_debug("Connection to fencer by %s succeeded (registration token: %s)",
    1176             :                           display_name, native->token);
    1177           0 :                 rc = pcmk_ok;
    1178             :             }
    1179             :         }
    1180             : 
    1181           0 :         free_xml(reply);
    1182           0 :         free_xml(hello);
    1183             :     }
    1184             : 
    1185           0 :     if (rc != pcmk_ok) {
    1186           0 :         crm_debug("Connection attempt to fencer by %s failed: %s "
    1187             :                   CRM_XS " rc=%d", display_name, pcmk_strerror(rc), rc);
    1188           0 :         stonith->cmds->disconnect(stonith);
    1189             :     }
    1190           0 :     return rc;
    1191             : }
    1192             : 
    1193             : static int
    1194           0 : stonith_set_notification(stonith_t * stonith, const char *callback, int enabled)
    1195             : {
    1196           0 :     int rc = pcmk_ok;
    1197           0 :     xmlNode *notify_msg = pcmk__xe_create(NULL, __func__);
    1198           0 :     stonith_private_t *native = stonith->st_private;
    1199             : 
    1200           0 :     if (stonith->state != stonith_disconnected) {
    1201             : 
    1202           0 :         crm_xml_add(notify_msg, PCMK__XA_ST_OP, STONITH_OP_NOTIFY);
    1203           0 :         if (enabled) {
    1204           0 :             crm_xml_add(notify_msg, PCMK__XA_ST_NOTIFY_ACTIVATE, callback);
    1205             :         } else {
    1206           0 :             crm_xml_add(notify_msg, PCMK__XA_ST_NOTIFY_DEACTIVATE, callback);
    1207             :         }
    1208             : 
    1209           0 :         rc = crm_ipc_send(native->ipc, notify_msg, crm_ipc_client_response, -1, NULL);
    1210           0 :         if (rc < 0) {
    1211           0 :             crm_perror(LOG_DEBUG, "Couldn't register for fencing notifications: %d", rc);
    1212           0 :             rc = -ECOMM;
    1213             :         } else {
    1214           0 :             rc = pcmk_ok;
    1215             :         }
    1216             :     }
    1217             : 
    1218           0 :     free_xml(notify_msg);
    1219           0 :     return rc;
    1220             : }
    1221             : 
    1222             : static int
    1223           0 : stonith_api_add_notification(stonith_t * stonith, const char *event,
    1224             :                              void (*callback) (stonith_t * stonith, stonith_event_t * e))
    1225             : {
    1226           0 :     GList *list_item = NULL;
    1227           0 :     stonith_notify_client_t *new_client = NULL;
    1228           0 :     stonith_private_t *private = NULL;
    1229             : 
    1230           0 :     private = stonith->st_private;
    1231           0 :     crm_trace("Adding callback for %s events (%d)", event, g_list_length(private->notify_list));
    1232             : 
    1233           0 :     new_client = pcmk__assert_alloc(1, sizeof(stonith_notify_client_t));
    1234           0 :     new_client->event = event;
    1235           0 :     new_client->notify = callback;
    1236             : 
    1237           0 :     list_item = g_list_find_custom(private->notify_list, new_client, stonithlib_GCompareFunc);
    1238             : 
    1239           0 :     if (list_item != NULL) {
    1240           0 :         crm_warn("Callback already present");
    1241           0 :         free(new_client);
    1242           0 :         return -ENOTUNIQ;
    1243             : 
    1244             :     } else {
    1245           0 :         private->notify_list = g_list_append(private->notify_list, new_client);
    1246             : 
    1247           0 :         stonith_set_notification(stonith, event, 1);
    1248             : 
    1249           0 :         crm_trace("Callback added (%d)", g_list_length(private->notify_list));
    1250             :     }
    1251           0 :     return pcmk_ok;
    1252             : }
    1253             : 
    1254             : static void
    1255           0 : del_notify_entry(gpointer data, gpointer user_data)
    1256             : {
    1257           0 :     stonith_notify_client_t *entry = data;
    1258           0 :     stonith_t * stonith = user_data;
    1259             : 
    1260           0 :     if (!entry->delete) {
    1261           0 :         crm_debug("Removing callback for %s events", entry->event);
    1262           0 :         stonith_api_del_notification(stonith, entry->event);
    1263             :     }
    1264           0 : }
    1265             : 
    1266             : static int
    1267           0 : stonith_api_del_notification(stonith_t * stonith, const char *event)
    1268             : {
    1269           0 :     GList *list_item = NULL;
    1270           0 :     stonith_notify_client_t *new_client = NULL;
    1271           0 :     stonith_private_t *private = stonith->st_private;
    1272             : 
    1273           0 :     if (event == NULL) {
    1274           0 :         foreach_notify_entry(private, del_notify_entry, stonith);
    1275           0 :         crm_trace("Removed callback");
    1276             : 
    1277           0 :         return pcmk_ok;
    1278             :     }
    1279             : 
    1280           0 :     crm_debug("Removing callback for %s events", event);
    1281             : 
    1282           0 :     new_client = pcmk__assert_alloc(1, sizeof(stonith_notify_client_t));
    1283           0 :     new_client->event = event;
    1284           0 :     new_client->notify = NULL;
    1285             : 
    1286           0 :     list_item = g_list_find_custom(private->notify_list, new_client, stonithlib_GCompareFunc);
    1287             : 
    1288           0 :     stonith_set_notification(stonith, event, 0);
    1289             : 
    1290           0 :     if (list_item != NULL) {
    1291           0 :         stonith_notify_client_t *list_client = list_item->data;
    1292             : 
    1293           0 :         if (private->notify_refcnt) {
    1294           0 :             list_client->delete = TRUE;
    1295           0 :             private->notify_deletes = TRUE;
    1296             :         } else {
    1297           0 :             private->notify_list = g_list_remove(private->notify_list, list_client);
    1298           0 :             free(list_client);
    1299             :         }
    1300             : 
    1301           0 :         crm_trace("Removed callback");
    1302             : 
    1303             :     } else {
    1304           0 :         crm_trace("Callback not present");
    1305             :     }
    1306           0 :     free(new_client);
    1307           0 :     return pcmk_ok;
    1308             : }
    1309             : 
    1310             : static int
    1311           0 : stonith_api_add_callback(stonith_t * stonith, int call_id, int timeout, int options,
    1312             :                          void *user_data, const char *callback_name,
    1313             :                          void (*callback) (stonith_t * st, stonith_callback_data_t * data))
    1314             : {
    1315           0 :     stonith_callback_client_t *blob = NULL;
    1316           0 :     stonith_private_t *private = NULL;
    1317             : 
    1318           0 :     CRM_CHECK(stonith != NULL, return -EINVAL);
    1319           0 :     CRM_CHECK(stonith->st_private != NULL, return -EINVAL);
    1320           0 :     private = stonith->st_private;
    1321             : 
    1322           0 :     if (call_id == 0) { // Add global callback
    1323           0 :         private->op_callback = callback;
    1324             : 
    1325           0 :     } else if (call_id < 0) { // Call failed immediately, so call callback now
    1326           0 :         if (!(options & st_opt_report_only_success)) {
    1327           0 :             pcmk__action_result_t result = PCMK__UNKNOWN_RESULT;
    1328             : 
    1329           0 :             crm_trace("Call failed, calling %s: %s", callback_name, pcmk_strerror(call_id));
    1330           0 :             pcmk__set_result(&result, CRM_EX_ERROR,
    1331           0 :                              stonith__legacy2status(call_id), NULL);
    1332           0 :             invoke_fence_action_callback(stonith, call_id, &result,
    1333             :                                          user_data, callback);
    1334             :         } else {
    1335           0 :             crm_warn("Fencer call failed: %s", pcmk_strerror(call_id));
    1336             :         }
    1337           0 :         return FALSE;
    1338             :     }
    1339             : 
    1340           0 :     blob = pcmk__assert_alloc(1, sizeof(stonith_callback_client_t));
    1341           0 :     blob->id = callback_name;
    1342           0 :     blob->only_success = (options & st_opt_report_only_success) ? TRUE : FALSE;
    1343           0 :     blob->user_data = user_data;
    1344           0 :     blob->callback = callback;
    1345           0 :     blob->allow_timeout_updates = (options & st_opt_timeout_updates) ? TRUE : FALSE;
    1346             : 
    1347           0 :     if (timeout > 0) {
    1348           0 :         set_callback_timeout(blob, stonith, call_id, timeout);
    1349             :     }
    1350             : 
    1351           0 :     pcmk__intkey_table_insert(private->stonith_op_callback_table, call_id,
    1352             :                               blob);
    1353           0 :     crm_trace("Added callback to %s for call %d", callback_name, call_id);
    1354             : 
    1355           0 :     return TRUE;
    1356             : }
    1357             : 
    1358             : static void
    1359           0 : stonith_dump_pending_op(gpointer key, gpointer value, gpointer user_data)
    1360             : {
    1361           0 :     int call = GPOINTER_TO_INT(key);
    1362           0 :     stonith_callback_client_t *blob = value;
    1363             : 
    1364           0 :     crm_debug("Call %d (%s): pending", call, pcmk__s(blob->id, "no ID"));
    1365           0 : }
    1366             : 
    1367             : void
    1368           0 : stonith_dump_pending_callbacks(stonith_t * stonith)
    1369             : {
    1370           0 :     stonith_private_t *private = stonith->st_private;
    1371             : 
    1372           0 :     if (private->stonith_op_callback_table == NULL) {
    1373           0 :         return;
    1374             :     }
    1375           0 :     return g_hash_table_foreach(private->stonith_op_callback_table, stonith_dump_pending_op, NULL);
    1376             : }
    1377             : 
    1378             : /*!
    1379             :  * \internal
    1380             :  * \brief Get the data section of a fencer notification
    1381             :  *
    1382             :  * \param[in] msg    Notification XML
    1383             :  * \param[in] ntype  Notification type
    1384             :  */
    1385             : static xmlNode *
    1386           0 : get_event_data_xml(xmlNode *msg, const char *ntype)
    1387             : {
    1388           0 :     char *data_addr = crm_strdup_printf("//%s", ntype);
    1389           0 :     xmlNode *data = get_xpath_object(data_addr, msg, LOG_DEBUG);
    1390             : 
    1391           0 :     free(data_addr);
    1392           0 :     return data;
    1393             : }
    1394             : 
    1395             : /*
    1396             :  <notify t="st_notify" subt="st_device_register" st_op="st_device_register" st_rc="0" >
    1397             :    <st_calldata >
    1398             :      <stonith_command t="stonith-ng" st_async_id="088fb640-431a-48b9-b2fc-c4ff78d0a2d9" st_op="st_device_register" st_callid="2" st_callopt="4096" st_timeout="0" st_clientid="088fb640-431a-48b9-b2fc-c4ff78d0a2d9" st_clientname="cts-fence-helper" >
    1399             :        <st_calldata >
    1400             :          <st_device_id id="test-id" origin="create_device_registration_xml" agent="fence_virsh" namespace="stonith-ng" >
    1401             :            <attributes ipaddr="localhost" pcmk-portmal="some-host=pcmk-1 pcmk-3=3,4" login="root" identity_file="/root/.ssh/id_dsa" />
    1402             :          </st_device_id>
    1403             :        </st_calldata>
    1404             :      </stonith_command>
    1405             :    </st_calldata>
    1406             :  </notify>
    1407             : 
    1408             :  <notify t="st_notify" subt="st_notify_fence" st_op="st_notify_fence" st_rc="0" >
    1409             :    <st_calldata >
    1410             :      <st_notify_fence st_rc="0" st_target="some-host" st_op="st_fence" st_delegate="test-id" st_origin="61dd7759-e229-4be7-b1f8-ef49dd14d9f0" />
    1411             :    </st_calldata>
    1412             :  </notify>
    1413             : */
    1414             : static stonith_event_t *
    1415           0 : xml_to_event(xmlNode *msg)
    1416             : {
    1417           0 :     stonith_event_t *event = pcmk__assert_alloc(1, sizeof(stonith_event_t));
    1418           0 :     struct event_private *event_private = NULL;
    1419             : 
    1420           0 :     event->opaque = pcmk__assert_alloc(1, sizeof(struct event_private));
    1421           0 :     event_private = (struct event_private *) event->opaque;
    1422             : 
    1423           0 :     crm_log_xml_trace(msg, "stonith_notify");
    1424             : 
    1425             :     // All notification types have the operation result and notification subtype
    1426           0 :     stonith__xe_get_result(msg, &event_private->result);
    1427           0 :     event->operation = crm_element_value_copy(msg, PCMK__XA_ST_OP);
    1428             : 
    1429             :     // @COMPAT The API originally provided the result as a legacy return code
    1430           0 :     event->result = pcmk_rc2legacy(stonith__result2rc(&event_private->result));
    1431             : 
    1432             :     // Some notification subtypes have additional information
    1433             : 
    1434           0 :     if (pcmk__str_eq(event->operation, PCMK__VALUE_ST_NOTIFY_FENCE,
    1435             :                      pcmk__str_none)) {
    1436           0 :         xmlNode *data = get_event_data_xml(msg, event->operation);
    1437             : 
    1438           0 :         if (data == NULL) {
    1439           0 :             crm_err("No data for %s event", event->operation);
    1440           0 :             crm_log_xml_notice(msg, "BadEvent");
    1441             :         } else {
    1442           0 :             event->origin = crm_element_value_copy(data, PCMK__XA_ST_ORIGIN);
    1443           0 :             event->action = crm_element_value_copy(data,
    1444             :                                                    PCMK__XA_ST_DEVICE_ACTION);
    1445           0 :             event->target = crm_element_value_copy(data, PCMK__XA_ST_TARGET);
    1446           0 :             event->executioner = crm_element_value_copy(data,
    1447             :                                                         PCMK__XA_ST_DELEGATE);
    1448           0 :             event->id = crm_element_value_copy(data, PCMK__XA_ST_REMOTE_OP);
    1449           0 :             event->client_origin =
    1450           0 :                 crm_element_value_copy(data, PCMK__XA_ST_CLIENTNAME);
    1451           0 :             event->device = crm_element_value_copy(data, PCMK__XA_ST_DEVICE_ID);
    1452             :         }
    1453             : 
    1454           0 :     } else if (pcmk__str_any_of(event->operation,
    1455             :                                 STONITH_OP_DEVICE_ADD, STONITH_OP_DEVICE_DEL,
    1456             :                                 STONITH_OP_LEVEL_ADD, STONITH_OP_LEVEL_DEL,
    1457             :                                 NULL)) {
    1458           0 :         xmlNode *data = get_event_data_xml(msg, event->operation);
    1459             : 
    1460           0 :         if (data == NULL) {
    1461           0 :             crm_err("No data for %s event", event->operation);
    1462           0 :             crm_log_xml_notice(msg, "BadEvent");
    1463             :         } else {
    1464           0 :             event->device = crm_element_value_copy(data, PCMK__XA_ST_DEVICE_ID);
    1465             :         }
    1466             :     }
    1467             : 
    1468           0 :     return event;
    1469             : }
    1470             : 
    1471             : static void
    1472           0 : event_free(stonith_event_t * event)
    1473             : {
    1474           0 :     struct event_private *event_private = event->opaque;
    1475             : 
    1476           0 :     free(event->id);
    1477           0 :     free(event->type);
    1478           0 :     free(event->message);
    1479           0 :     free(event->operation);
    1480           0 :     free(event->origin);
    1481           0 :     free(event->action);
    1482           0 :     free(event->target);
    1483           0 :     free(event->executioner);
    1484           0 :     free(event->device);
    1485           0 :     free(event->client_origin);
    1486           0 :     pcmk__reset_result(&event_private->result);
    1487           0 :     free(event->opaque);
    1488           0 :     free(event);
    1489           0 : }
    1490             : 
    1491             : static void
    1492           0 : stonith_send_notification(gpointer data, gpointer user_data)
    1493             : {
    1494           0 :     struct notify_blob_s *blob = user_data;
    1495           0 :     stonith_notify_client_t *entry = data;
    1496           0 :     stonith_event_t *st_event = NULL;
    1497           0 :     const char *event = NULL;
    1498             : 
    1499           0 :     if (blob->xml == NULL) {
    1500           0 :         crm_warn("Skipping callback - NULL message");
    1501           0 :         return;
    1502             :     }
    1503             : 
    1504           0 :     event = crm_element_value(blob->xml, PCMK__XA_SUBT);
    1505             : 
    1506           0 :     if (entry == NULL) {
    1507           0 :         crm_warn("Skipping callback - NULL callback client");
    1508           0 :         return;
    1509             : 
    1510           0 :     } else if (entry->delete) {
    1511           0 :         crm_trace("Skipping callback - marked for deletion");
    1512           0 :         return;
    1513             : 
    1514           0 :     } else if (entry->notify == NULL) {
    1515           0 :         crm_warn("Skipping callback - NULL callback");
    1516           0 :         return;
    1517             : 
    1518           0 :     } else if (!pcmk__str_eq(entry->event, event, pcmk__str_none)) {
    1519           0 :         crm_trace("Skipping callback - event mismatch %p/%s vs. %s", entry, entry->event, event);
    1520           0 :         return;
    1521             :     }
    1522             : 
    1523           0 :     st_event = xml_to_event(blob->xml);
    1524             : 
    1525           0 :     crm_trace("Invoking callback for %p/%s event...", entry, event);
    1526           0 :     entry->notify(blob->stonith, st_event);
    1527           0 :     crm_trace("Callback invoked...");
    1528             : 
    1529           0 :     event_free(st_event);
    1530             : }
    1531             : 
    1532             : /*!
    1533             :  * \internal
    1534             :  * \brief Create and send an API request
    1535             :  *
    1536             :  * \param[in,out] stonith       Stonith connection
    1537             :  * \param[in]     op            API operation to request
    1538             :  * \param[in]     data          Data to attach to request
    1539             :  * \param[out]    output_data   If not NULL, will be set to reply if synchronous
    1540             :  * \param[in]     call_options  Bitmask of stonith_call_options to use
    1541             :  * \param[in]     timeout       Error if not completed within this many seconds
    1542             :  *
    1543             :  * \return pcmk_ok (for synchronous requests) or positive call ID
    1544             :  *         (for asynchronous requests) on success, -errno otherwise
    1545             :  */
    1546             : static int
    1547           0 : stonith_send_command(stonith_t * stonith, const char *op, xmlNode * data, xmlNode ** output_data,
    1548             :                      int call_options, int timeout)
    1549             : {
    1550           0 :     int rc = 0;
    1551           0 :     int reply_id = -1;
    1552             : 
    1553           0 :     xmlNode *op_msg = NULL;
    1554           0 :     xmlNode *op_reply = NULL;
    1555           0 :     stonith_private_t *native = NULL;
    1556             : 
    1557           0 :     CRM_ASSERT(stonith && stonith->st_private && op);
    1558           0 :     native = stonith->st_private;
    1559             : 
    1560           0 :     if (output_data != NULL) {
    1561           0 :         *output_data = NULL;
    1562             :     }
    1563             : 
    1564           0 :     if ((stonith->state == stonith_disconnected) || (native->token == NULL)) {
    1565           0 :         return -ENOTCONN;
    1566             :     }
    1567             : 
    1568             :     /* Increment the call ID, which must be positive to avoid conflicting with
    1569             :      * error codes. This shouldn't be a problem unless the client mucked with
    1570             :      * it or the counter wrapped around.
    1571             :      */
    1572           0 :     stonith->call_id++;
    1573           0 :     if (stonith->call_id < 1) {
    1574           0 :         stonith->call_id = 1;
    1575             :     }
    1576             : 
    1577           0 :     op_msg = stonith_create_op(stonith->call_id, native->token, op, data, call_options);
    1578           0 :     if (op_msg == NULL) {
    1579           0 :         return -EINVAL;
    1580             :     }
    1581             : 
    1582           0 :     crm_xml_add_int(op_msg, PCMK__XA_ST_TIMEOUT, timeout);
    1583           0 :     crm_trace("Sending %s message to fencer with timeout %ds", op, timeout);
    1584             : 
    1585           0 :     if (data) {
    1586           0 :         const char *delay_s = crm_element_value(data, PCMK__XA_ST_DELAY);
    1587             : 
    1588           0 :         if (delay_s) {
    1589           0 :             crm_xml_add(op_msg, PCMK__XA_ST_DELAY, delay_s);
    1590             :         }
    1591             :     }
    1592             : 
    1593             :     {
    1594           0 :         enum crm_ipc_flags ipc_flags = crm_ipc_flags_none;
    1595             : 
    1596           0 :         if (call_options & st_opt_sync_call) {
    1597           0 :             pcmk__set_ipc_flags(ipc_flags, "stonith command",
    1598             :                                 crm_ipc_client_response);
    1599             :         }
    1600           0 :         rc = crm_ipc_send(native->ipc, op_msg, ipc_flags,
    1601           0 :                           1000 * (timeout + 60), &op_reply);
    1602             :     }
    1603           0 :     free_xml(op_msg);
    1604             : 
    1605           0 :     if (rc < 0) {
    1606           0 :         crm_perror(LOG_ERR, "Couldn't perform %s operation (timeout=%ds): %d", op, timeout, rc);
    1607           0 :         rc = -ECOMM;
    1608           0 :         goto done;
    1609             :     }
    1610             : 
    1611           0 :     crm_log_xml_trace(op_reply, "Reply");
    1612             : 
    1613           0 :     if (!(call_options & st_opt_sync_call)) {
    1614           0 :         crm_trace("Async call %d, returning", stonith->call_id);
    1615           0 :         free_xml(op_reply);
    1616           0 :         return stonith->call_id;
    1617             :     }
    1618             : 
    1619           0 :     crm_element_value_int(op_reply, PCMK__XA_ST_CALLID, &reply_id);
    1620             : 
    1621           0 :     if (reply_id == stonith->call_id) {
    1622           0 :         pcmk__action_result_t result = PCMK__UNKNOWN_RESULT;
    1623             : 
    1624           0 :         crm_trace("Synchronous reply %d received", reply_id);
    1625             : 
    1626           0 :         stonith__xe_get_result(op_reply, &result);
    1627           0 :         rc = pcmk_rc2legacy(stonith__result2rc(&result));
    1628           0 :         pcmk__reset_result(&result);
    1629             : 
    1630           0 :         if ((call_options & st_opt_discard_reply) || output_data == NULL) {
    1631           0 :             crm_trace("Discarding reply");
    1632             : 
    1633             :         } else {
    1634           0 :             *output_data = op_reply;
    1635           0 :             op_reply = NULL;    /* Prevent subsequent free */
    1636             :         }
    1637             : 
    1638           0 :     } else if (reply_id <= 0) {
    1639           0 :         crm_err("Received bad reply: No id set");
    1640           0 :         crm_log_xml_err(op_reply, "Bad reply");
    1641           0 :         free_xml(op_reply);
    1642           0 :         rc = -ENOMSG;
    1643             : 
    1644             :     } else {
    1645           0 :         crm_err("Received bad reply: %d (wanted %d)", reply_id, stonith->call_id);
    1646           0 :         crm_log_xml_err(op_reply, "Old reply");
    1647           0 :         free_xml(op_reply);
    1648           0 :         rc = -ENOMSG;
    1649             :     }
    1650             : 
    1651           0 :   done:
    1652           0 :     if (!crm_ipc_connected(native->ipc)) {
    1653           0 :         crm_err("Fencer disconnected");
    1654           0 :         free(native->token); native->token = NULL;
    1655           0 :         stonith->state = stonith_disconnected;
    1656             :     }
    1657             : 
    1658           0 :     free_xml(op_reply);
    1659           0 :     return rc;
    1660             : }
    1661             : 
    1662             : /* Not used with mainloop */
    1663             : bool
    1664           0 : stonith_dispatch(stonith_t * st)
    1665             : {
    1666           0 :     gboolean stay_connected = TRUE;
    1667           0 :     stonith_private_t *private = NULL;
    1668             : 
    1669           0 :     CRM_ASSERT(st != NULL);
    1670           0 :     private = st->st_private;
    1671             : 
    1672           0 :     while (crm_ipc_ready(private->ipc)) {
    1673             : 
    1674           0 :         if (crm_ipc_read(private->ipc) > 0) {
    1675           0 :             const char *msg = crm_ipc_buffer(private->ipc);
    1676             : 
    1677           0 :             stonith_dispatch_internal(msg, strlen(msg), st);
    1678             :         }
    1679             : 
    1680           0 :         if (!crm_ipc_connected(private->ipc)) {
    1681           0 :             crm_err("Connection closed");
    1682           0 :             stay_connected = FALSE;
    1683             :         }
    1684             :     }
    1685             : 
    1686           0 :     return stay_connected;
    1687             : }
    1688             : 
    1689             : static int
    1690           0 : stonith_api_free(stonith_t * stonith)
    1691             : {
    1692           0 :     int rc = pcmk_ok;
    1693             : 
    1694           0 :     crm_trace("Destroying %p", stonith);
    1695             : 
    1696           0 :     if (stonith->state != stonith_disconnected) {
    1697           0 :         crm_trace("Unregistering notifications and disconnecting %p first",
    1698             :                   stonith);
    1699           0 :         stonith->cmds->remove_notification(stonith, NULL);
    1700           0 :         rc = stonith->cmds->disconnect(stonith);
    1701             :     }
    1702             : 
    1703           0 :     if (stonith->state == stonith_disconnected) {
    1704           0 :         stonith_private_t *private = stonith->st_private;
    1705             : 
    1706           0 :         crm_trace("Removing %d callbacks", g_hash_table_size(private->stonith_op_callback_table));
    1707           0 :         g_hash_table_destroy(private->stonith_op_callback_table);
    1708             : 
    1709           0 :         crm_trace("Destroying %d notification clients", g_list_length(private->notify_list));
    1710           0 :         g_list_free_full(private->notify_list, free);
    1711             : 
    1712           0 :         free(stonith->st_private);
    1713           0 :         free(stonith->cmds);
    1714           0 :         free(stonith);
    1715             : 
    1716             :     } else {
    1717           0 :         crm_err("Not free'ing active connection: %s (%d)", pcmk_strerror(rc), rc);
    1718             :     }
    1719             : 
    1720           0 :     return rc;
    1721             : }
    1722             : 
    1723             : void
    1724           0 : stonith_api_delete(stonith_t * stonith)
    1725             : {
    1726           0 :     crm_trace("Destroying %p", stonith);
    1727           0 :     if(stonith) {
    1728           0 :         stonith->cmds->free(stonith);
    1729             :     }
    1730           0 : }
    1731             : 
    1732             : static int
    1733           0 : stonith_api_validate(stonith_t *st, int call_options, const char *rsc_id,
    1734             :                      const char *namespace_s, const char *agent,
    1735             :                      const stonith_key_value_t *params, int timeout_sec,
    1736             :                      char **output, char **error_output)
    1737             : {
    1738             :     /* Validation should be done directly via the agent, so we can get it from
    1739             :      * stonith_admin when the cluster is not running, which is important for
    1740             :      * higher-level tools.
    1741             :      */
    1742             : 
    1743           0 :     int rc = pcmk_ok;
    1744             : 
    1745             :     /* Use a dummy node name in case the agent requires a target. We assume the
    1746             :      * actual target doesn't matter for validation purposes (if in practice,
    1747             :      * that is incorrect, we will need to allow the caller to pass the target).
    1748             :      */
    1749           0 :     const char *target = "node1";
    1750           0 :     const char *host_arg = NULL;
    1751             : 
    1752           0 :     GHashTable *params_table = pcmk__strkey_table(free, free);
    1753             : 
    1754             :     // Convert parameter list to a hash table
    1755           0 :     for (; params; params = params->next) {
    1756           0 :         if (pcmk__str_eq(params->key, PCMK_STONITH_HOST_ARGUMENT,
    1757             :                          pcmk__str_none)) {
    1758           0 :             host_arg = params->value;
    1759             :         }
    1760           0 :         if (!pcmk_stonith_param(params->key)) {
    1761           0 :             pcmk__insert_dup(params_table, params->key, params->value);
    1762             :         }
    1763             :     }
    1764             : 
    1765             : #if SUPPORT_CIBSECRETS
    1766             :     rc = pcmk__substitute_secrets(rsc_id, params_table);
    1767             :     if (rc != pcmk_rc_ok) {
    1768             :         crm_warn("Could not replace secret parameters for validation of %s: %s",
    1769             :                  agent, pcmk_rc_str(rc));
    1770             :         // rc is standard return value, don't return it in this function
    1771             :     }
    1772             : #endif
    1773             : 
    1774           0 :     if (output) {
    1775           0 :         *output = NULL;
    1776             :     }
    1777           0 :     if (error_output) {
    1778           0 :         *error_output = NULL;
    1779             :     }
    1780             : 
    1781           0 :     if (timeout_sec <= 0) {
    1782           0 :         timeout_sec = PCMK_DEFAULT_METADATA_TIMEOUT_MS; // Questionable
    1783             :     }
    1784             : 
    1785           0 :     switch (stonith_get_namespace(agent, namespace_s)) {
    1786           0 :         case st_namespace_rhcs:
    1787           0 :             rc = stonith__rhcs_validate(st, call_options, target, agent,
    1788             :                                         params_table, host_arg, timeout_sec,
    1789             :                                         output, error_output);
    1790           0 :             break;
    1791             : 
    1792             : #if HAVE_STONITH_STONITH_H
    1793             :         case st_namespace_lha:
    1794             :             rc = stonith__lha_validate(st, call_options, target, agent,
    1795             :                                        params_table, timeout_sec, output,
    1796             :                                        error_output);
    1797             :             break;
    1798             : #endif
    1799             : 
    1800           0 :         case st_namespace_invalid:
    1801           0 :             errno = ENOENT;
    1802           0 :             rc = -errno;
    1803             : 
    1804           0 :             if (error_output) {
    1805           0 :                 *error_output = crm_strdup_printf("Agent %s not found", agent);
    1806             :             } else {
    1807           0 :                 crm_err("Agent %s not found", agent);
    1808             :             }
    1809             : 
    1810           0 :             break;
    1811             : 
    1812           0 :         default:
    1813           0 :             errno = EOPNOTSUPP;
    1814           0 :             rc = -errno;
    1815             : 
    1816           0 :             if (error_output) {
    1817           0 :                 *error_output = crm_strdup_printf("Agent %s does not support validation",
    1818             :                                                   agent);
    1819             :             } else {
    1820           0 :                 crm_err("Agent %s does not support validation", agent);
    1821             :             }
    1822             : 
    1823           0 :             break;
    1824             :     }
    1825             : 
    1826           0 :     g_hash_table_destroy(params_table);
    1827           0 :     return rc;
    1828             : }
    1829             : 
    1830             : stonith_t *
    1831           0 : stonith_api_new(void)
    1832             : {
    1833           0 :     stonith_t *new_stonith = NULL;
    1834           0 :     stonith_private_t *private = NULL;
    1835             : 
    1836           0 :     new_stonith = calloc(1, sizeof(stonith_t));
    1837           0 :     if (new_stonith == NULL) {
    1838           0 :         return NULL;
    1839             :     }
    1840             : 
    1841           0 :     private = calloc(1, sizeof(stonith_private_t));
    1842           0 :     if (private == NULL) {
    1843           0 :         free(new_stonith);
    1844           0 :         return NULL;
    1845             :     }
    1846           0 :     new_stonith->st_private = private;
    1847             : 
    1848           0 :     private->stonith_op_callback_table = pcmk__intkey_table(stonith_destroy_op_callback);
    1849           0 :     private->notify_list = NULL;
    1850           0 :     private->notify_refcnt = 0;
    1851           0 :     private->notify_deletes = FALSE;
    1852             : 
    1853           0 :     new_stonith->call_id = 1;
    1854           0 :     new_stonith->state = stonith_disconnected;
    1855             : 
    1856           0 :     new_stonith->cmds = calloc(1, sizeof(stonith_api_operations_t));
    1857           0 :     if (new_stonith->cmds == NULL) {
    1858           0 :         free(new_stonith->st_private);
    1859           0 :         free(new_stonith);
    1860           0 :         return NULL;
    1861             :     }
    1862             : 
    1863             : /* *INDENT-OFF* */
    1864           0 :     new_stonith->cmds->free       = stonith_api_free;
    1865           0 :     new_stonith->cmds->connect    = stonith_api_signon;
    1866           0 :     new_stonith->cmds->disconnect = stonith_api_signoff;
    1867             : 
    1868           0 :     new_stonith->cmds->list       = stonith_api_list;
    1869           0 :     new_stonith->cmds->monitor    = stonith_api_monitor;
    1870           0 :     new_stonith->cmds->status     = stonith_api_status;
    1871           0 :     new_stonith->cmds->fence      = stonith_api_fence;
    1872           0 :     new_stonith->cmds->fence_with_delay = stonith_api_fence_with_delay;
    1873           0 :     new_stonith->cmds->confirm    = stonith_api_confirm;
    1874           0 :     new_stonith->cmds->history    = stonith_api_history;
    1875             : 
    1876           0 :     new_stonith->cmds->list_agents  = stonith_api_device_list;
    1877           0 :     new_stonith->cmds->metadata     = stonith_api_device_metadata;
    1878             : 
    1879           0 :     new_stonith->cmds->query           = stonith_api_query;
    1880           0 :     new_stonith->cmds->remove_device   = stonith_api_remove_device;
    1881           0 :     new_stonith->cmds->register_device = stonith_api_register_device;
    1882             : 
    1883           0 :     new_stonith->cmds->remove_level          = stonith_api_remove_level;
    1884           0 :     new_stonith->cmds->remove_level_full     = stonith_api_remove_level_full;
    1885           0 :     new_stonith->cmds->register_level        = stonith_api_register_level;
    1886           0 :     new_stonith->cmds->register_level_full   = stonith_api_register_level_full;
    1887             : 
    1888           0 :     new_stonith->cmds->remove_callback       = stonith_api_del_callback;
    1889           0 :     new_stonith->cmds->register_callback     = stonith_api_add_callback;
    1890           0 :     new_stonith->cmds->remove_notification   = stonith_api_del_notification;
    1891           0 :     new_stonith->cmds->register_notification = stonith_api_add_notification;
    1892             : 
    1893           0 :     new_stonith->cmds->validate              = stonith_api_validate;
    1894             : /* *INDENT-ON* */
    1895             : 
    1896           0 :     return new_stonith;
    1897             : }
    1898             : 
    1899             : /*!
    1900             :  * \brief Make a blocking connection attempt to the fencer
    1901             :  *
    1902             :  * \param[in,out] st            Fencer API object
    1903             :  * \param[in]     name          Client name to use with fencer
    1904             :  * \param[in]     max_attempts  Return error if this many attempts fail
    1905             :  *
    1906             :  * \return pcmk_ok on success, result of last attempt otherwise
    1907             :  */
    1908             : int
    1909           0 : stonith_api_connect_retry(stonith_t *st, const char *name, int max_attempts)
    1910             : {
    1911           0 :     int rc = -EINVAL; // if max_attempts is not positive
    1912             : 
    1913           0 :     for (int attempt = 1; attempt <= max_attempts; attempt++) {
    1914           0 :         rc = st->cmds->connect(st, name, NULL);
    1915           0 :         if (rc == pcmk_ok) {
    1916           0 :             return pcmk_ok;
    1917           0 :         } else if (attempt < max_attempts) {
    1918           0 :             crm_notice("Fencer connection attempt %d of %d failed (retrying in 2s): %s "
    1919             :                        CRM_XS " rc=%d",
    1920             :                        attempt, max_attempts, pcmk_strerror(rc), rc);
    1921           0 :             sleep(2);
    1922             :         }
    1923             :     }
    1924           0 :     crm_notice("Could not connect to fencer: %s " CRM_XS " rc=%d",
    1925             :                pcmk_strerror(rc), rc);
    1926           0 :     return rc;
    1927             : }
    1928             : 
    1929             : stonith_key_value_t *
    1930           0 : stonith_key_value_add(stonith_key_value_t * head, const char *key, const char *value)
    1931             : {
    1932             :     stonith_key_value_t *p, *end;
    1933             : 
    1934           0 :     p = pcmk__assert_alloc(1, sizeof(stonith_key_value_t));
    1935           0 :     p->key = pcmk__str_copy(key);
    1936           0 :     p->value = pcmk__str_copy(value);
    1937             : 
    1938           0 :     end = head;
    1939           0 :     while (end && end->next) {
    1940           0 :         end = end->next;
    1941             :     }
    1942             : 
    1943           0 :     if (end) {
    1944           0 :         end->next = p;
    1945             :     } else {
    1946           0 :         head = p;
    1947             :     }
    1948             : 
    1949           0 :     return head;
    1950             : }
    1951             : 
    1952             : void
    1953           0 : stonith_key_value_freeall(stonith_key_value_t * head, int keys, int values)
    1954             : {
    1955             :     stonith_key_value_t *p;
    1956             : 
    1957           0 :     while (head) {
    1958           0 :         p = head->next;
    1959           0 :         if (keys) {
    1960           0 :             free(head->key);
    1961             :         }
    1962           0 :         if (values) {
    1963           0 :             free(head->value);
    1964             :         }
    1965           0 :         free(head);
    1966           0 :         head = p;
    1967             :     }
    1968           0 : }
    1969             : 
    1970             : #define api_log_open() openlog("stonith-api", LOG_CONS | LOG_NDELAY | LOG_PID, LOG_DAEMON)
    1971             : #define api_log(level, fmt, args...) syslog(level, "%s: "fmt, __func__, args)
    1972             : 
    1973             : int
    1974           0 : stonith_api_kick(uint32_t nodeid, const char *uname, int timeout, bool off)
    1975             : {
    1976           0 :     int rc = pcmk_ok;
    1977           0 :     stonith_t *st = stonith_api_new();
    1978           0 :     const char *action = off? PCMK_ACTION_OFF : PCMK_ACTION_REBOOT;
    1979             : 
    1980           0 :     api_log_open();
    1981           0 :     if (st == NULL) {
    1982           0 :         api_log(LOG_ERR, "API initialization failed, could not kick (%s) node %u/%s",
    1983             :                 action, nodeid, uname);
    1984           0 :         return -EPROTO;
    1985             :     }
    1986             : 
    1987           0 :     rc = st->cmds->connect(st, "stonith-api", NULL);
    1988           0 :     if (rc != pcmk_ok) {
    1989           0 :         api_log(LOG_ERR, "Connection failed, could not kick (%s) node %u/%s : %s (%d)",
    1990             :                 action, nodeid, uname, pcmk_strerror(rc), rc);
    1991             :     } else {
    1992           0 :         char *name = (uname == NULL)? pcmk__itoa(nodeid) : strdup(uname);
    1993           0 :         int opts = 0;
    1994             : 
    1995           0 :         stonith__set_call_options(opts, name,
    1996             :                                   st_opt_sync_call|st_opt_allow_suicide);
    1997           0 :         if ((uname == NULL) && (nodeid > 0)) {
    1998           0 :             stonith__set_call_options(opts, name, st_opt_cs_nodeid);
    1999             :         }
    2000           0 :         rc = st->cmds->fence(st, opts, name, action, timeout, 0);
    2001           0 :         free(name);
    2002             : 
    2003           0 :         if (rc != pcmk_ok) {
    2004           0 :             api_log(LOG_ERR, "Could not kick (%s) node %u/%s : %s (%d)",
    2005             :                     action, nodeid, uname, pcmk_strerror(rc), rc);
    2006             :         } else {
    2007           0 :             api_log(LOG_NOTICE, "Node %u/%s kicked: %s", nodeid, uname, action);
    2008             :         }
    2009             :     }
    2010             : 
    2011           0 :     stonith_api_delete(st);
    2012           0 :     return rc;
    2013             : }
    2014             : 
    2015             : time_t
    2016           0 : stonith_api_time(uint32_t nodeid, const char *uname, bool in_progress)
    2017             : {
    2018           0 :     int rc = pcmk_ok;
    2019           0 :     time_t when = 0;
    2020           0 :     stonith_t *st = stonith_api_new();
    2021           0 :     stonith_history_t *history = NULL, *hp = NULL;
    2022             : 
    2023           0 :     if (st == NULL) {
    2024           0 :         api_log(LOG_ERR, "Could not retrieve fence history for %u/%s: "
    2025             :                 "API initialization failed", nodeid, uname);
    2026           0 :         return when;
    2027             :     }
    2028             : 
    2029           0 :     rc = st->cmds->connect(st, "stonith-api", NULL);
    2030           0 :     if (rc != pcmk_ok) {
    2031           0 :         api_log(LOG_NOTICE, "Connection failed: %s (%d)", pcmk_strerror(rc), rc);
    2032             :     } else {
    2033           0 :         int entries = 0;
    2034           0 :         int progress = 0;
    2035           0 :         int completed = 0;
    2036           0 :         int opts = 0;
    2037           0 :         char *name = (uname == NULL)? pcmk__itoa(nodeid) : strdup(uname);
    2038             : 
    2039           0 :         stonith__set_call_options(opts, name, st_opt_sync_call);
    2040           0 :         if ((uname == NULL) && (nodeid > 0)) {
    2041           0 :             stonith__set_call_options(opts, name, st_opt_cs_nodeid);
    2042             :         }
    2043           0 :         rc = st->cmds->history(st, opts, name, &history, 120);
    2044           0 :         free(name);
    2045             : 
    2046           0 :         for (hp = history; hp; hp = hp->next) {
    2047           0 :             entries++;
    2048           0 :             if (in_progress) {
    2049           0 :                 progress++;
    2050           0 :                 if (hp->state != st_done && hp->state != st_failed) {
    2051           0 :                     when = time(NULL);
    2052             :                 }
    2053             : 
    2054           0 :             } else if (hp->state == st_done) {
    2055           0 :                 completed++;
    2056           0 :                 if (hp->completed > when) {
    2057           0 :                     when = hp->completed;
    2058             :                 }
    2059             :             }
    2060             :         }
    2061             : 
    2062           0 :         stonith_history_free(history);
    2063             : 
    2064           0 :         if(rc == pcmk_ok) {
    2065           0 :             api_log(LOG_INFO, "Found %d entries for %u/%s: %d in progress, %d completed", entries, nodeid, uname, progress, completed);
    2066             :         } else {
    2067           0 :             api_log(LOG_ERR, "Could not retrieve fence history for %u/%s: %s (%d)", nodeid, uname, pcmk_strerror(rc), rc);
    2068             :         }
    2069             :     }
    2070             : 
    2071           0 :     stonith_api_delete(st);
    2072             : 
    2073           0 :     if(when) {
    2074           0 :         api_log(LOG_INFO, "Node %u/%s last kicked at: %ld", nodeid, uname, (long int)when);
    2075             :     }
    2076           0 :     return when;
    2077             : }
    2078             : 
    2079             : bool
    2080           0 : stonith_agent_exists(const char *agent, int timeout)
    2081             : {
    2082           0 :     stonith_t *st = NULL;
    2083           0 :     stonith_key_value_t *devices = NULL;
    2084           0 :     stonith_key_value_t *dIter = NULL;
    2085           0 :     bool rc = FALSE;
    2086             : 
    2087           0 :     if (agent == NULL) {
    2088           0 :         return rc;
    2089             :     }
    2090             : 
    2091           0 :     st = stonith_api_new();
    2092           0 :     if (st == NULL) {
    2093           0 :         crm_err("Could not list fence agents: API memory allocation failed");
    2094           0 :         return FALSE;
    2095             :     }
    2096           0 :     st->cmds->list_agents(st, st_opt_sync_call, NULL, &devices, timeout == 0 ? 120 : timeout);
    2097             : 
    2098           0 :     for (dIter = devices; dIter != NULL; dIter = dIter->next) {
    2099           0 :         if (pcmk__str_eq(dIter->value, agent, pcmk__str_none)) {
    2100           0 :             rc = TRUE;
    2101           0 :             break;
    2102             :         }
    2103             :     }
    2104             : 
    2105           0 :     stonith_key_value_freeall(devices, 1, 1);
    2106           0 :     stonith_api_delete(st);
    2107           0 :     return rc;
    2108             : }
    2109             : 
    2110             : const char *
    2111           0 : stonith_action_str(const char *action)
    2112             : {
    2113           0 :     if (action == NULL) {
    2114           0 :         return "fencing";
    2115           0 :     } else if (strcmp(action, PCMK_ACTION_ON) == 0) {
    2116           0 :         return "unfencing";
    2117           0 :     } else if (strcmp(action, PCMK_ACTION_OFF) == 0) {
    2118           0 :         return "turning off";
    2119             :     } else {
    2120           0 :         return action;
    2121             :     }
    2122             : }
    2123             : 
    2124             : /*!
    2125             :  * \internal
    2126             :  * \brief Parse a target name from one line of a target list string
    2127             :  *
    2128             :  * \param[in]     line    One line of a target list string
    2129             :  * \param[in]     len     String length of line
    2130             :  * \param[in,out] output  List to add newly allocated target name to
    2131             :  */
    2132             : static void
    2133           0 : parse_list_line(const char *line, int len, GList **output)
    2134             : {
    2135           0 :     size_t i = 0;
    2136           0 :     size_t entry_start = 0;
    2137             : 
    2138             :     /* Skip complaints about additional parameters device doesn't understand
    2139             :      *
    2140             :      * @TODO Document or eliminate the implied restriction of target names
    2141             :      */
    2142           0 :     if (strstr(line, "invalid") || strstr(line, "variable")) {
    2143           0 :         crm_debug("Skipping list output line: %s", line);
    2144           0 :         return;
    2145             :     }
    2146             : 
    2147             :     // Process line content, character by character
    2148           0 :     for (i = 0; i <= len; i++) {
    2149             : 
    2150           0 :         if (isspace(line[i]) || (line[i] == ',') || (line[i] == ';')
    2151           0 :             || (line[i] == '\0')) {
    2152             :             // We've found a separator (i.e. the end of an entry)
    2153             : 
    2154           0 :             int rc = 0;
    2155           0 :             char *entry = NULL;
    2156             : 
    2157           0 :             if (i == entry_start) {
    2158             :                 // Skip leading and sequential separators
    2159           0 :                 entry_start = i + 1;
    2160           0 :                 continue;
    2161             :             }
    2162             : 
    2163           0 :             entry = pcmk__assert_alloc(i - entry_start + 1, sizeof(char));
    2164             : 
    2165             :             /* Read entry, stopping at first separator
    2166             :              *
    2167             :              * @TODO Document or eliminate these character restrictions
    2168             :              */
    2169           0 :             rc = sscanf(line + entry_start, "%[a-zA-Z0-9_-.]", entry);
    2170           0 :             if (rc != 1) {
    2171           0 :                 crm_warn("Could not parse list output entry: %s "
    2172             :                          CRM_XS " entry_start=%d position=%d",
    2173             :                          line + entry_start, entry_start, i);
    2174           0 :                 free(entry);
    2175             : 
    2176           0 :             } else if (pcmk__strcase_any_of(entry, PCMK_ACTION_ON,
    2177             :                                             PCMK_ACTION_OFF, NULL)) {
    2178             :                 /* Some agents print the target status in the list output,
    2179             :                  * though none are known now (the separate list-status command
    2180             :                  * is used for this, but it can also print "UNKNOWN"). To handle
    2181             :                  * this possibility, skip such entries.
    2182             :                  *
    2183             :                  * @TODO Document or eliminate the implied restriction of target
    2184             :                  * names.
    2185             :                  */
    2186           0 :                 free(entry);
    2187             : 
    2188             :             } else {
    2189             :                 // We have a valid entry
    2190           0 :                 *output = g_list_append(*output, entry);
    2191             :             }
    2192           0 :             entry_start = i + 1;
    2193             :         }
    2194             :     }
    2195             : }
    2196             : 
    2197             : /*!
    2198             :  * \internal
    2199             :  * \brief Parse a list of targets from a string
    2200             :  *
    2201             :  * \param[in] list_output  Target list as a string
    2202             :  *
    2203             :  * \return List of target names
    2204             :  * \note The target list string format is flexible, to allow for user-specified
    2205             :  *       lists such pcmk_host_list and the output of an agent's list action
    2206             :  *       (whether direct or via the API, which escapes newlines). There may be
    2207             :  *       multiple lines, separated by either a newline or an escaped newline
    2208             :  *       (backslash n). Each line may have one or more target names, separated
    2209             :  *       by any combination of whitespace, commas, and semi-colons. Lines
    2210             :  *       containing "invalid" or "variable" will be ignored entirely. Target
    2211             :  *       names "on" or "off" (case-insensitive) will be ignored. Target names
    2212             :  *       may contain only alphanumeric characters, underbars (_), dashes (-),
    2213             :  *       and dots (.) (if any other character occurs in the name, it and all
    2214             :  *       subsequent characters in the name will be ignored).
    2215             :  * \note The caller is responsible for freeing the result with
    2216             :  *       g_list_free_full(result, free).
    2217             :  */
    2218             : GList *
    2219           0 : stonith__parse_targets(const char *target_spec)
    2220             : {
    2221           0 :     GList *targets = NULL;
    2222             : 
    2223           0 :     if (target_spec != NULL) {
    2224           0 :         size_t out_len = strlen(target_spec);
    2225           0 :         size_t line_start = 0; // Starting index of line being processed
    2226             : 
    2227           0 :         for (size_t i = 0; i <= out_len; ++i) {
    2228           0 :             if ((target_spec[i] == '\n') || (target_spec[i] == '\0')
    2229           0 :                 || ((target_spec[i] == '\\') && (target_spec[i + 1] == 'n'))) {
    2230             :                 // We've reached the end of one line of output
    2231             : 
    2232           0 :                 int len = i - line_start;
    2233             : 
    2234           0 :                 if (len > 0) {
    2235           0 :                     char *line = strndup(target_spec + line_start, len);
    2236             : 
    2237           0 :                     line[len] = '\0'; // Because it might be a newline
    2238           0 :                     parse_list_line(line, len, &targets);
    2239           0 :                     free(line);
    2240             :                 }
    2241           0 :                 if (target_spec[i] == '\\') {
    2242           0 :                     ++i; // backslash-n takes up two positions
    2243             :                 }
    2244           0 :                 line_start = i + 1;
    2245             :             }
    2246             :         }
    2247             :     }
    2248           0 :     return targets;
    2249             : }
    2250             : 
    2251             : /*!
    2252             :  * \internal
    2253             :  * \brief Check whether a fencing failure was followed by an equivalent success
    2254             :  *
    2255             :  * \param[in] event        Fencing failure
    2256             :  * \param[in] top_history  Complete fencing history (must be sorted by
    2257             :  *                         stonith__sort_history() beforehand)
    2258             :  *
    2259             :  * \return The name of the node that executed the fencing if a later successful
    2260             :  *         event exists, or NULL if no such event exists
    2261             :  */
    2262             : const char *
    2263           0 : stonith__later_succeeded(const stonith_history_t *event,
    2264             :                          const stonith_history_t *top_history)
    2265             : {
    2266           0 :     const char *other = NULL;
    2267             : 
    2268           0 :      for (const stonith_history_t *prev_hp = top_history;
    2269           0 :           prev_hp != NULL; prev_hp = prev_hp->next) {
    2270           0 :         if (prev_hp == event) {
    2271           0 :             break;
    2272             :         }
    2273           0 :         if ((prev_hp->state == st_done) &&
    2274           0 :             pcmk__str_eq(event->target, prev_hp->target, pcmk__str_casei) &&
    2275           0 :             pcmk__str_eq(event->action, prev_hp->action, pcmk__str_none) &&
    2276           0 :             ((event->completed < prev_hp->completed) ||
    2277           0 :              ((event->completed == prev_hp->completed) && (event->completed_nsec < prev_hp->completed_nsec)))) {
    2278             : 
    2279           0 :             if ((event->delegate == NULL)
    2280           0 :                 || pcmk__str_eq(event->delegate, prev_hp->delegate,
    2281             :                                 pcmk__str_casei)) {
    2282             :                 // Prefer equivalent fencing by same executioner
    2283           0 :                 return prev_hp->delegate;
    2284             : 
    2285           0 :             } else if (other == NULL) {
    2286             :                 // Otherwise remember first successful executioner
    2287           0 :                 other = (prev_hp->delegate == NULL)? "some node" : prev_hp->delegate;
    2288             :             }
    2289             :         }
    2290             :     }
    2291           0 :     return other;
    2292             : }
    2293             : 
    2294             : /*!
    2295             :  * \internal
    2296             :  * \brief Sort fencing history, pending first then by most recently completed
    2297             :  *
    2298             :  * \param[in,out] history    List of stonith actions
    2299             :  *
    2300             :  * \return New head of sorted \p history
    2301             :  */
    2302             : stonith_history_t *
    2303           0 : stonith__sort_history(stonith_history_t *history)
    2304             : {
    2305           0 :     stonith_history_t *new = NULL, *pending = NULL, *hp, *np, *tmp;
    2306             : 
    2307           0 :     for (hp = history; hp; ) {
    2308           0 :         tmp = hp->next;
    2309           0 :         if ((hp->state == st_done) || (hp->state == st_failed)) {
    2310             :             /* sort into new */
    2311           0 :             if ((!new) || (hp->completed > new->completed) || 
    2312           0 :                 ((hp->completed == new->completed) && (hp->completed_nsec > new->completed_nsec))) {
    2313           0 :                 hp->next = new;
    2314           0 :                 new = hp;
    2315             :             } else {
    2316           0 :                 np = new;
    2317             :                 do {
    2318           0 :                     if ((!np->next) || (hp->completed > np->next->completed) ||
    2319           0 :                         ((hp->completed == np->next->completed) && (hp->completed_nsec > np->next->completed_nsec))) {
    2320           0 :                         hp->next = np->next;
    2321           0 :                         np->next = hp;
    2322           0 :                         break;
    2323             :                     }
    2324           0 :                     np = np->next;
    2325             :                 } while (1);
    2326             :             }
    2327             :         } else {
    2328             :             /* put into pending */
    2329           0 :             hp->next = pending;
    2330           0 :             pending = hp;
    2331             :         }
    2332           0 :         hp = tmp;
    2333             :     }
    2334             : 
    2335             :     /* pending actions don't have a completed-stamp so make them go front */
    2336           0 :     if (pending) {
    2337           0 :         stonith_history_t *last_pending = pending;
    2338             : 
    2339           0 :         while (last_pending->next) {
    2340           0 :             last_pending = last_pending->next;
    2341             :         }
    2342             : 
    2343           0 :         last_pending->next = new;
    2344           0 :         new = pending;
    2345             :     }
    2346           0 :     return new;
    2347             : }
    2348             : 
    2349             : /*!
    2350             :  * \brief Return string equivalent of an operation state value
    2351             :  *
    2352             :  * \param[in] state  Fencing operation state value
    2353             :  *
    2354             :  * \return Human-friendly string equivalent of state
    2355             :  */
    2356             : const char *
    2357           0 : stonith_op_state_str(enum op_state state)
    2358             : {
    2359           0 :     switch (state) {
    2360           0 :         case st_query:      return "querying";
    2361           0 :         case st_exec:       return "executing";
    2362           0 :         case st_done:       return "completed";
    2363           0 :         case st_duplicate:  return "duplicate";
    2364           0 :         case st_failed:     return "failed";
    2365             :     }
    2366           0 :     return "unknown";
    2367             : }
    2368             : 
    2369             : stonith_history_t *
    2370           0 : stonith__first_matching_event(stonith_history_t *history,
    2371             :                               bool (*matching_fn)(stonith_history_t *, void *),
    2372             :                               void *user_data)
    2373             : {
    2374           0 :     for (stonith_history_t *hp = history; hp; hp = hp->next) {
    2375           0 :         if (matching_fn(hp, user_data)) {
    2376           0 :             return hp;
    2377             :         }
    2378             :     }
    2379             : 
    2380           0 :     return NULL;
    2381             : }
    2382             : 
    2383             : bool
    2384           0 : stonith__event_state_pending(stonith_history_t *history, void *user_data)
    2385             : {
    2386           0 :     return history->state != st_failed && history->state != st_done;
    2387             : }
    2388             : 
    2389             : bool
    2390           0 : stonith__event_state_eq(stonith_history_t *history, void *user_data)
    2391             : {
    2392           0 :     return history->state == GPOINTER_TO_INT(user_data);
    2393             : }
    2394             : 
    2395             : bool
    2396           0 : stonith__event_state_neq(stonith_history_t *history, void *user_data)
    2397             : {
    2398           0 :     return history->state != GPOINTER_TO_INT(user_data);
    2399             : }
    2400             : 
    2401             : void
    2402           0 : stonith__device_parameter_flags(uint32_t *device_flags, const char *device_name,
    2403             :                                 xmlNode *metadata)
    2404             : {
    2405           0 :     xmlXPathObjectPtr xpath = NULL;
    2406           0 :     int max = 0;
    2407           0 :     int lpc = 0;
    2408             : 
    2409           0 :     CRM_CHECK((device_flags != NULL) && (metadata != NULL), return);
    2410             : 
    2411           0 :     xpath = xpath_search(metadata, "//" PCMK_XE_PARAMETER);
    2412           0 :     max = numXpathResults(xpath);
    2413             : 
    2414           0 :     if (max <= 0) {
    2415           0 :         freeXpathObject(xpath);
    2416           0 :         return;
    2417             :     }
    2418             : 
    2419           0 :     for (lpc = 0; lpc < max; lpc++) {
    2420           0 :         const char *parameter = NULL;
    2421           0 :         xmlNode *match = getXpathResult(xpath, lpc);
    2422             : 
    2423           0 :         CRM_LOG_ASSERT(match != NULL);
    2424           0 :         if (match == NULL) {
    2425           0 :             continue;
    2426             :         }
    2427             : 
    2428           0 :         parameter = crm_element_value(match, PCMK_XA_NAME);
    2429             : 
    2430           0 :         if (pcmk__str_eq(parameter, "plug", pcmk__str_casei)) {
    2431           0 :             stonith__set_device_flags(*device_flags, device_name,
    2432             :                                       st_device_supports_parameter_plug);
    2433             : 
    2434           0 :         } else if (pcmk__str_eq(parameter, "port", pcmk__str_casei)) {
    2435           0 :             stonith__set_device_flags(*device_flags, device_name,
    2436             :                                       st_device_supports_parameter_port);
    2437             :         }
    2438             :     }
    2439             : 
    2440           0 :     freeXpathObject(xpath);
    2441             : }
    2442             : 
    2443             : /*!
    2444             :  * \internal
    2445             :  * \brief Retrieve fence agent meta-data asynchronously
    2446             :  *
    2447             :  * \param[in]     agent        Agent to execute
    2448             :  * \param[in]     timeout_sec  Error if not complete within this time
    2449             :  * \param[in]     callback     Function to call with result (this will always be
    2450             :  *                             called, whether by this function directly or
    2451             :  *                             later via the main loop, and on success the
    2452             :  *                             metadata will be in its result argument's
    2453             :  *                             action_stdout)
    2454             :  * \param[in,out] user_data    User data to pass to callback
    2455             :  *
    2456             :  * \return Standard Pacemaker return code
    2457             :  * \note The caller must use a main loop. This function is not a
    2458             :  *       stonith_api_operations_t method because it does not need a stonith_t
    2459             :  *       object and does not go through the fencer, but executes the agent
    2460             :  *       directly.
    2461             :  */
    2462             : int
    2463           0 : stonith__metadata_async(const char *agent, int timeout_sec,
    2464             :                         void (*callback)(int pid,
    2465             :                                          const pcmk__action_result_t *result,
    2466             :                                          void *user_data),
    2467             :                         void *user_data)
    2468             : {
    2469           0 :     switch (stonith_get_namespace(agent, NULL)) {
    2470           0 :         case st_namespace_rhcs:
    2471             :             {
    2472           0 :                 stonith_action_t *action = NULL;
    2473           0 :                 int rc = pcmk_ok;
    2474             : 
    2475           0 :                 action = stonith__action_create(agent, PCMK_ACTION_METADATA,
    2476             :                                                 NULL, 0, timeout_sec, NULL,
    2477             :                                                 NULL, NULL);
    2478             : 
    2479           0 :                 rc = stonith__execute_async(action, user_data, callback, NULL);
    2480           0 :                 if (rc != pcmk_ok) {
    2481           0 :                     callback(0, stonith__action_result(action), user_data);
    2482           0 :                     stonith__destroy_action(action);
    2483             :                 }
    2484           0 :                 return pcmk_legacy2rc(rc);
    2485             :             }
    2486             : 
    2487             : #if HAVE_STONITH_STONITH_H
    2488             :         case st_namespace_lha:
    2489             :             // LHA metadata is simply synthesized, so simulate async
    2490             :             {
    2491             :                 pcmk__action_result_t result = {
    2492             :                     .exit_status = CRM_EX_OK,
    2493             :                     .execution_status = PCMK_EXEC_DONE,
    2494             :                     .exit_reason = NULL,
    2495             :                     .action_stdout = NULL,
    2496             :                     .action_stderr = NULL,
    2497             :                 };
    2498             : 
    2499             :                 stonith__lha_metadata(agent, timeout_sec,
    2500             :                                       &result.action_stdout);
    2501             :                 callback(0, &result, user_data);
    2502             :                 pcmk__reset_result(&result);
    2503             :                 return pcmk_rc_ok;
    2504             :             }
    2505             : #endif
    2506             : 
    2507           0 :         default:
    2508             :             {
    2509           0 :                 pcmk__action_result_t result = {
    2510             :                     .exit_status = CRM_EX_NOSUCH,
    2511             :                     .execution_status = PCMK_EXEC_ERROR_HARD,
    2512           0 :                     .exit_reason = crm_strdup_printf("No such agent '%s'",
    2513             :                                                      agent),
    2514             :                     .action_stdout = NULL,
    2515             :                     .action_stderr = NULL,
    2516             :                 };
    2517             : 
    2518           0 :                 callback(0, &result, user_data);
    2519           0 :                 pcmk__reset_result(&result);
    2520           0 :                 return ENOENT;
    2521             :             }
    2522             :     }
    2523             : }
    2524             : 
    2525             : /*!
    2526             :  * \internal
    2527             :  * \brief Return the exit status from an async action callback
    2528             :  *
    2529             :  * \param[in] data  Callback data
    2530             :  *
    2531             :  * \return Exit status from callback data
    2532             :  */
    2533             : int
    2534           0 : stonith__exit_status(const stonith_callback_data_t *data)
    2535             : {
    2536           0 :     if ((data == NULL) || (data->opaque == NULL)) {
    2537           0 :         return CRM_EX_ERROR;
    2538             :     }
    2539           0 :     return ((pcmk__action_result_t *) data->opaque)->exit_status;
    2540             : }
    2541             : 
    2542             : /*!
    2543             :  * \internal
    2544             :  * \brief Return the execution status from an async action callback
    2545             :  *
    2546             :  * \param[in] data  Callback data
    2547             :  *
    2548             :  * \return Execution status from callback data
    2549             :  */
    2550             : int
    2551           0 : stonith__execution_status(const stonith_callback_data_t *data)
    2552             : {
    2553           0 :     if ((data == NULL) || (data->opaque == NULL)) {
    2554           0 :         return PCMK_EXEC_UNKNOWN;
    2555             :     }
    2556           0 :     return ((pcmk__action_result_t *) data->opaque)->execution_status;
    2557             : }
    2558             : 
    2559             : /*!
    2560             :  * \internal
    2561             :  * \brief Return the exit reason from an async action callback
    2562             :  *
    2563             :  * \param[in] data  Callback data
    2564             :  *
    2565             :  * \return Exit reason from callback data
    2566             :  */
    2567             : const char *
    2568           0 : stonith__exit_reason(const stonith_callback_data_t *data)
    2569             : {
    2570           0 :     if ((data == NULL) || (data->opaque == NULL)) {
    2571           0 :         return NULL;
    2572             :     }
    2573           0 :     return ((pcmk__action_result_t *) data->opaque)->exit_reason;
    2574             : }
    2575             : 
    2576             : /*!
    2577             :  * \internal
    2578             :  * \brief Return the exit status from an event notification
    2579             :  *
    2580             :  * \param[in] event  Event
    2581             :  *
    2582             :  * \return Exit status from event
    2583             :  */
    2584             : int
    2585           0 : stonith__event_exit_status(const stonith_event_t *event)
    2586             : {
    2587           0 :     if ((event == NULL) || (event->opaque == NULL)) {
    2588           0 :         return CRM_EX_ERROR;
    2589             :     } else {
    2590           0 :         struct event_private *event_private = event->opaque;
    2591             : 
    2592           0 :         return event_private->result.exit_status;
    2593             :     }
    2594             : }
    2595             : 
    2596             : /*!
    2597             :  * \internal
    2598             :  * \brief Return the execution status from an event notification
    2599             :  *
    2600             :  * \param[in] event  Event
    2601             :  *
    2602             :  * \return Execution status from event
    2603             :  */
    2604             : int
    2605           0 : stonith__event_execution_status(const stonith_event_t *event)
    2606             : {
    2607           0 :     if ((event == NULL) || (event->opaque == NULL)) {
    2608           0 :         return PCMK_EXEC_UNKNOWN;
    2609             :     } else {
    2610           0 :         struct event_private *event_private = event->opaque;
    2611             : 
    2612           0 :         return event_private->result.execution_status;
    2613             :     }
    2614             : }
    2615             : 
    2616             : /*!
    2617             :  * \internal
    2618             :  * \brief Return the exit reason from an event notification
    2619             :  *
    2620             :  * \param[in] event  Event
    2621             :  *
    2622             :  * \return Exit reason from event
    2623             :  */
    2624             : const char *
    2625           0 : stonith__event_exit_reason(const stonith_event_t *event)
    2626             : {
    2627           0 :     if ((event == NULL) || (event->opaque == NULL)) {
    2628           0 :         return NULL;
    2629             :     } else {
    2630           0 :         struct event_private *event_private = event->opaque;
    2631             : 
    2632           0 :         return event_private->result.exit_reason;
    2633             :     }
    2634             : }
    2635             : 
    2636             : /*!
    2637             :  * \internal
    2638             :  * \brief Return a human-friendly description of a fencing event
    2639             :  *
    2640             :  * \param[in] event  Event to describe
    2641             :  *
    2642             :  * \return Newly allocated string with description of \p event
    2643             :  * \note The caller is responsible for freeing the return value.
    2644             :  *       This function asserts on memory errors and never returns NULL.
    2645             :  */
    2646             : char *
    2647           0 : stonith__event_description(const stonith_event_t *event)
    2648             : {
    2649             :     // Use somewhat readable defaults
    2650           0 :     const char *origin = pcmk__s(event->client_origin, "a client");
    2651           0 :     const char *origin_node = pcmk__s(event->origin, "a node");
    2652           0 :     const char *executioner = pcmk__s(event->executioner, "the cluster");
    2653           0 :     const char *device = pcmk__s(event->device, "unknown");
    2654           0 :     const char *action = pcmk__s(event->action, event->operation);
    2655           0 :     const char *target = pcmk__s(event->target, "no node");
    2656           0 :     const char *reason = stonith__event_exit_reason(event);
    2657             :     const char *status;
    2658             : 
    2659           0 :     if (action == NULL) {
    2660           0 :         action = "(unknown)";
    2661             :     }
    2662             : 
    2663           0 :     if (stonith__event_execution_status(event) != PCMK_EXEC_DONE) {
    2664           0 :         status = pcmk_exec_status_str(stonith__event_execution_status(event));
    2665           0 :     } else if (stonith__event_exit_status(event) != CRM_EX_OK) {
    2666           0 :         status = pcmk_exec_status_str(PCMK_EXEC_ERROR);
    2667             :     } else {
    2668           0 :         status = crm_exit_str(CRM_EX_OK);
    2669             :     }
    2670             : 
    2671           0 :     if (pcmk__str_eq(event->operation, PCMK__VALUE_ST_NOTIFY_HISTORY,
    2672             :                      pcmk__str_none)) {
    2673           0 :         return crm_strdup_printf("Fencing history may have changed");
    2674             : 
    2675           0 :     } else if (pcmk__str_eq(event->operation, STONITH_OP_DEVICE_ADD,
    2676             :                             pcmk__str_none)) {
    2677           0 :         return crm_strdup_printf("A fencing device (%s) was added", device);
    2678             : 
    2679           0 :     } else if (pcmk__str_eq(event->operation, STONITH_OP_DEVICE_DEL,
    2680             :                             pcmk__str_none)) {
    2681           0 :         return crm_strdup_printf("A fencing device (%s) was removed", device);
    2682             : 
    2683           0 :     } else if (pcmk__str_eq(event->operation, STONITH_OP_LEVEL_ADD,
    2684             :                             pcmk__str_none)) {
    2685           0 :         return crm_strdup_printf("A fencing topology level (%s) was added",
    2686             :                                  device);
    2687             : 
    2688           0 :     } else if (pcmk__str_eq(event->operation, STONITH_OP_LEVEL_DEL,
    2689             :                             pcmk__str_none)) {
    2690           0 :         return crm_strdup_printf("A fencing topology level (%s) was removed",
    2691             :                                  device);
    2692             :     }
    2693             : 
    2694             :     // event->operation should be PCMK__VALUE_ST_NOTIFY_FENCE at this point
    2695             : 
    2696           0 :     return crm_strdup_printf("Operation %s of %s by %s for %s@%s: %s%s%s%s (ref=%s)",
    2697             :                              action, target, executioner, origin, origin_node,
    2698             :                              status,
    2699             :                              ((reason == NULL)? "" : " ("), pcmk__s(reason, ""),
    2700             :                              ((reason == NULL)? "" : ")"),
    2701           0 :                              pcmk__s(event->id, "(none)"));
    2702             : }
    2703             : 
    2704             : 
    2705             : // Deprecated functions kept only for backward API compatibility
    2706             : // LCOV_EXCL_START
    2707             : 
    2708             : const char *get_stonith_provider(const char *agent, const char *provider);
    2709             : 
    2710             : const char *
    2711             : get_stonith_provider(const char *agent, const char *provider)
    2712             : {
    2713             :     return stonith_namespace2text(stonith_get_namespace(agent, provider));
    2714             : }
    2715             : 
    2716             : // LCOV_EXCL_STOP
    2717             : // End deprecated API

Generated by: LCOV version 1.14