root/daemons/execd/execd_commands.c

/* [previous][next][first][last][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. time_is_set
  2. get_current_time
  3. time_diff_ms
  4. cmd_original_times
  5. action_matches
  6. log_finished
  7. log_execute
  8. normalize_action_name
  9. build_rsc_from_xml
  10. create_lrmd_cmd
  11. stop_recurring_timer
  12. free_lrmd_cmd
  13. stonith_recurring_op_helper
  14. start_recurring_timer
  15. start_delay_helper
  16. find_duplicate_action
  17. merge_recurring_duplicate
  18. schedule_lrmd_cmd
  19. create_lrmd_reply
  20. send_client_notify
  21. send_cmd_complete_notify
  22. send_generic_notify
  23. cmd_reset
  24. cmd_finalize
  25. notify_one_client
  26. notify_of_new_client
  27. client_disconnect_cleanup
  28. action_complete
  29. stonith_action_complete
  30. lrmd_stonith_callback
  31. stonith_connection_failed
  32. execd_stonith_start
  33. execd_stonith_stop
  34. execd_stonith_monitor
  35. execute_stonith_action
  36. execute_nonstonith_action
  37. execute_resource_action
  38. free_rsc
  39. process_lrmd_signon
  40. process_lrmd_rsc_register
  41. process_lrmd_get_rsc_info
  42. process_lrmd_rsc_unregister
  43. process_lrmd_rsc_exec
  44. cancel_op
  45. cancel_all_recurring
  46. process_lrmd_rsc_cancel
  47. add_recurring_op_xml
  48. process_lrmd_get_recurring
  49. process_lrmd_message

   1 /*
   2  * Copyright 2012-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 #include <crm/fencing/internal.h>
  12 
  13 #include <glib.h>
  14 
  15 // Check whether we have a high-resolution monotonic clock
  16 #undef PCMK__TIME_USE_CGT
  17 #if HAVE_DECL_CLOCK_MONOTONIC && defined(CLOCK_MONOTONIC)
  18 #  define PCMK__TIME_USE_CGT
  19 #  include <time.h>  /* clock_gettime */
  20 #endif
  21 
  22 #include <unistd.h>
  23 
  24 #include <crm/crm.h>
  25 #include <crm/fencing/internal.h>
  26 #include <crm/services.h>
  27 #include <crm/services_internal.h>
  28 #include <crm/common/mainloop.h>
  29 #include <crm/common/ipc.h>
  30 #include <crm/common/ipc_internal.h>
  31 #include <crm/common/xml.h>
  32 
  33 #include "pacemaker-execd.h"
  34 
  35 GHashTable *rsc_list = NULL;
  36 
  37 typedef struct lrmd_cmd_s {
  38     int timeout;
  39     guint interval_ms;
  40     int start_delay;
  41     int timeout_orig;
  42 
  43     int call_id;
  44 
  45     int call_opts;
  46     /* Timer ids, must be removed on cmd destruction. */
  47     int delay_id;
  48     int stonith_recurring_id;
  49 
  50     int rsc_deleted;
  51 
  52     int service_flags;
  53 
  54     char *client_id;
  55     char *origin;
  56     char *rsc_id;
  57     char *action;
  58     char *real_action;
  59     char *userdata_str;
  60 
  61     pcmk__action_result_t result;
  62 
  63     /* We can track operation queue time and run time, to be saved with the CIB
  64      * resource history (and displayed in cluster status). We need
  65      * high-resolution monotonic time for this purpose, so we use
  66      * clock_gettime(CLOCK_MONOTONIC, ...) (if available, otherwise this feature
  67      * is disabled).
  68      *
  69      * However, we also need epoch timestamps for recording the time the command
  70      * last ran and the time its return value last changed, for use in time
  71      * displays (as opposed to interval calculations). We keep time_t values for
  72      * this purpose.
  73      *
  74      * The last run time is used for both purposes, so we keep redundant
  75      * monotonic and epoch values for this. Technically the two could represent
  76      * different times, but since time_t has only second resolution and the
  77      * values are used for distinct purposes, that is not significant.
  78      */
  79 #ifdef PCMK__TIME_USE_CGT
  80     /* Recurring and systemd operations may involve more than one executor
  81      * command per operation, so they need info about the original and the most
  82      * recent.
  83      */
  84     struct timespec t_first_run;    // When op first ran
  85     struct timespec t_run;          // When op most recently ran
  86     struct timespec t_first_queue;  // When op was first queued
  87     struct timespec t_queue;        // When op was most recently queued
  88 #endif
  89     time_t epoch_last_run;          // Epoch timestamp of when op last ran
  90     time_t epoch_rcchange;          // Epoch timestamp of when rc last changed
  91 
  92     bool first_notify_sent;
  93     int last_notify_rc;
  94     int last_notify_op_status;
  95     int last_pid;
  96 
  97     GHashTable *params;
  98 } lrmd_cmd_t;
  99 
 100 static void cmd_finalize(lrmd_cmd_t * cmd, lrmd_rsc_t * rsc);
 101 static gboolean execute_resource_action(gpointer user_data);
 102 static void cancel_all_recurring(lrmd_rsc_t * rsc, const char *client_id);
 103 
 104 #ifdef PCMK__TIME_USE_CGT
 105 
 106 /*!
 107  * \internal
 108  * \brief Check whether a struct timespec has been set
 109  *
 110  * \param[in] timespec  Time to check
 111  *
 112  * \return true if timespec has been set (i.e. is nonzero), false otherwise
 113  */
 114 static inline bool
 115 time_is_set(const struct timespec *timespec)
     /* [previous][next][first][last][top][bottom][index][help] */
 116 {
 117     return (timespec != NULL) &&
 118            ((timespec->tv_sec != 0) || (timespec->tv_nsec != 0));
 119 }
 120 
 121 /*
 122  * \internal
 123  * \brief Set a timespec (and its original if unset) to the current time
 124  *
 125  * \param[out] t_current  Where to store current time
 126  * \param[out] t_orig     Where to copy t_current if unset
 127  */
 128 static void
 129 get_current_time(struct timespec *t_current, struct timespec *t_orig)
     /* [previous][next][first][last][top][bottom][index][help] */
 130 {
 131     clock_gettime(CLOCK_MONOTONIC, t_current);
 132     if ((t_orig != NULL) && !time_is_set(t_orig)) {
 133         *t_orig = *t_current;
 134     }
 135 }
 136 
 137 /*!
 138  * \internal
 139  * \brief Return difference between two times in milliseconds
 140  *
 141  * \param[in] now  More recent time (or NULL to use current time)
 142  * \param[in] old  Earlier time
 143  *
 144  * \return milliseconds difference (or 0 if old is NULL or unset)
 145  *
 146  * \note Can overflow on 32bit machines when the differences is around
 147  *       24 days or more.
 148  */
 149 static int
 150 time_diff_ms(const struct timespec *now, const struct timespec *old)
     /* [previous][next][first][last][top][bottom][index][help] */
 151 {
 152     int diff_ms = 0;
 153 
 154     if (time_is_set(old)) {
 155         struct timespec local_now = { 0, };
 156 
 157         if (now == NULL) {
 158             clock_gettime(CLOCK_MONOTONIC, &local_now);
 159             now = &local_now;
 160         }
 161         diff_ms = (now->tv_sec - old->tv_sec) * 1000
 162                   + (now->tv_nsec - old->tv_nsec) / 1000000;
 163     }
 164     return diff_ms;
 165 }
 166 
 167 /*!
 168  * \internal
 169  * \brief Reset a command's operation times to their original values.
 170  *
 171  * Reset a command's run and queued timestamps to the timestamps of the original
 172  * command, so we report the entire time since then and not just the time since
 173  * the most recent command (for recurring and systemd operations).
 174  *
 175  * \param[in,out] cmd  Executor command object to reset
 176  *
 177  * \note It's not obvious what the queued time should be for a systemd
 178  *       start/stop operation, which might go like this:
 179  *         initial command queued 5ms, runs 3s
 180  *         monitor command queued 10ms, runs 10s
 181  *         monitor command queued 10ms, runs 10s
 182  *       Is the queued time for that operation 5ms, 10ms or 25ms? The current
 183  *       implementation will report 5ms. If it's 25ms, then we need to
 184  *       subtract 20ms from the total exec time so as not to count it twice.
 185  *       We can implement that later if it matters to anyone ...
 186  */
 187 static void
 188 cmd_original_times(lrmd_cmd_t * cmd)
     /* [previous][next][first][last][top][bottom][index][help] */
 189 {
 190     cmd->t_run = cmd->t_first_run;
 191     cmd->t_queue = cmd->t_first_queue;
 192 }
 193 #endif
 194 
 195 static inline bool
 196 action_matches(const lrmd_cmd_t *cmd, const char *action, guint interval_ms)
     /* [previous][next][first][last][top][bottom][index][help] */
 197 {
 198     return (cmd->interval_ms == interval_ms)
 199            && pcmk__str_eq(cmd->action, action, pcmk__str_casei);
 200 }
 201 
 202 /*!
 203  * \internal
 204  * \brief Log the result of an asynchronous command
 205  *
 206  * \param[in] cmd            Command to log result for
 207  * \param[in] exec_time_ms   Execution time in milliseconds, if known
 208  * \param[in] queue_time_ms  Queue time in milliseconds, if known
 209  */
 210 static void
 211 log_finished(const lrmd_cmd_t *cmd, int exec_time_ms, int queue_time_ms)
     /* [previous][next][first][last][top][bottom][index][help] */
 212 {
 213     int log_level = LOG_INFO;
 214     GString *str = g_string_sized_new(100); // reasonable starting size
 215 
 216     if (pcmk__str_eq(cmd->action, PCMK_ACTION_MONITOR, pcmk__str_casei)) {
 217         log_level = LOG_DEBUG;
 218     }
 219 
 220     g_string_append_printf(str, "%s %s (call %d",
 221                            cmd->rsc_id, cmd->action, cmd->call_id);
 222     if (cmd->last_pid != 0) {
 223         g_string_append_printf(str, ", PID %d", cmd->last_pid);
 224     }
 225     if (cmd->result.execution_status == PCMK_EXEC_DONE) {
 226         g_string_append_printf(str, ") exited with status %d",
 227                                cmd->result.exit_status);
 228     } else {
 229         pcmk__g_strcat(str, ") could not be executed: ",
 230                        pcmk_exec_status_str(cmd->result.execution_status),
 231                        NULL);
 232     }
 233     if (cmd->result.exit_reason != NULL) {
 234         pcmk__g_strcat(str, " (", cmd->result.exit_reason, ")", NULL);
 235     }
 236 
 237 #ifdef PCMK__TIME_USE_CGT
 238     pcmk__g_strcat(str, " (execution time ",
 239                    pcmk__readable_interval(exec_time_ms), NULL);
 240     if (queue_time_ms > 0) {
 241         pcmk__g_strcat(str, " after being queued ",
 242                        pcmk__readable_interval(queue_time_ms), NULL);
 243     }
 244     g_string_append_c(str, ')');
 245 #endif
 246 
 247     do_crm_log(log_level, "%s", str->str);
 248     g_string_free(str, TRUE);
 249 }
 250 
 251 static void
 252 log_execute(lrmd_cmd_t * cmd)
     /* [previous][next][first][last][top][bottom][index][help] */
 253 {
 254     int log_level = LOG_INFO;
 255 
 256     if (pcmk__str_eq(cmd->action, PCMK_ACTION_MONITOR, pcmk__str_casei)) {
 257         log_level = LOG_DEBUG;
 258     }
 259 
 260     do_crm_log(log_level, "executing - rsc:%s action:%s call_id:%d",
 261                cmd->rsc_id, cmd->action, cmd->call_id);
 262 }
 263 
 264 static const char *
 265 normalize_action_name(lrmd_rsc_t * rsc, const char *action)
     /* [previous][next][first][last][top][bottom][index][help] */
 266 {
 267     if (pcmk__str_eq(action, PCMK_ACTION_MONITOR, pcmk__str_casei) &&
 268         pcmk_is_set(pcmk_get_ra_caps(rsc->class), pcmk_ra_cap_status)) {
 269         return PCMK_ACTION_STATUS;
 270     }
 271     return action;
 272 }
 273 
 274 static lrmd_rsc_t *
 275 build_rsc_from_xml(xmlNode * msg)
     /* [previous][next][first][last][top][bottom][index][help] */
 276 {
 277     xmlNode *rsc_xml = get_xpath_object("//" PCMK__XE_LRMD_RSC, msg, LOG_ERR);
 278     lrmd_rsc_t *rsc = NULL;
 279 
 280     rsc = pcmk__assert_alloc(1, sizeof(lrmd_rsc_t));
 281 
 282     crm_element_value_int(msg, PCMK__XA_LRMD_CALLOPT, &rsc->call_opts);
 283 
 284     rsc->rsc_id = crm_element_value_copy(rsc_xml, PCMK__XA_LRMD_RSC_ID);
 285     rsc->class = crm_element_value_copy(rsc_xml, PCMK__XA_LRMD_CLASS);
 286     rsc->provider = crm_element_value_copy(rsc_xml, PCMK__XA_LRMD_PROVIDER);
 287     rsc->type = crm_element_value_copy(rsc_xml, PCMK__XA_LRMD_TYPE);
 288     rsc->work = mainloop_add_trigger(G_PRIORITY_HIGH, execute_resource_action,
 289                                      rsc);
 290 
 291     // Initialize fence device probes (to return "not running")
 292     pcmk__set_result(&rsc->fence_probe_result, CRM_EX_ERROR,
 293                      PCMK_EXEC_NO_FENCE_DEVICE, NULL);
 294     return rsc;
 295 }
 296 
 297 static lrmd_cmd_t *
 298 create_lrmd_cmd(xmlNode *msg, pcmk__client_t *client)
     /* [previous][next][first][last][top][bottom][index][help] */
 299 {
 300     int call_options = 0;
 301     xmlNode *rsc_xml = get_xpath_object("//" PCMK__XE_LRMD_RSC, msg, LOG_ERR);
 302     lrmd_cmd_t *cmd = NULL;
 303 
 304     cmd = pcmk__assert_alloc(1, sizeof(lrmd_cmd_t));
 305 
 306     crm_element_value_int(msg, PCMK__XA_LRMD_CALLOPT, &call_options);
 307     cmd->call_opts = call_options;
 308     cmd->client_id = pcmk__str_copy(client->id);
 309 
 310     crm_element_value_int(msg, PCMK__XA_LRMD_CALLID, &cmd->call_id);
 311     crm_element_value_ms(rsc_xml, PCMK__XA_LRMD_RSC_INTERVAL,
 312                          &cmd->interval_ms);
 313     crm_element_value_int(rsc_xml, PCMK__XA_LRMD_TIMEOUT, &cmd->timeout);
 314     crm_element_value_int(rsc_xml, PCMK__XA_LRMD_RSC_START_DELAY,
 315                           &cmd->start_delay);
 316     cmd->timeout_orig = cmd->timeout;
 317 
 318     cmd->origin = crm_element_value_copy(rsc_xml, PCMK__XA_LRMD_ORIGIN);
 319     cmd->action = crm_element_value_copy(rsc_xml, PCMK__XA_LRMD_RSC_ACTION);
 320     cmd->userdata_str = crm_element_value_copy(rsc_xml,
 321                                                PCMK__XA_LRMD_RSC_USERDATA_STR);
 322     cmd->rsc_id = crm_element_value_copy(rsc_xml, PCMK__XA_LRMD_RSC_ID);
 323 
 324     cmd->params = xml2list(rsc_xml);
 325 
 326     if (pcmk__str_eq(g_hash_table_lookup(cmd->params, "CRM_meta_on_fail"),
 327                      PCMK_VALUE_BLOCK, pcmk__str_casei)) {
 328         crm_debug("Setting flag to leave pid group on timeout and "
 329                   "only kill action pid for " PCMK__OP_FMT,
 330                   cmd->rsc_id, cmd->action, cmd->interval_ms);
 331         cmd->service_flags = pcmk__set_flags_as(__func__, __LINE__,
 332                                                 LOG_TRACE, "Action",
 333                                                 cmd->action, 0,
 334                                                 SVC_ACTION_LEAVE_GROUP,
 335                                                 "SVC_ACTION_LEAVE_GROUP");
 336     }
 337     return cmd;
 338 }
 339 
 340 static void
 341 stop_recurring_timer(lrmd_cmd_t *cmd)
     /* [previous][next][first][last][top][bottom][index][help] */
 342 {
 343     if (cmd) {
 344         if (cmd->stonith_recurring_id) {
 345             g_source_remove(cmd->stonith_recurring_id);
 346         }
 347         cmd->stonith_recurring_id = 0;
 348     }
 349 }
 350 
 351 static void
 352 free_lrmd_cmd(lrmd_cmd_t * cmd)
     /* [previous][next][first][last][top][bottom][index][help] */
 353 {
 354     stop_recurring_timer(cmd);
 355     if (cmd->delay_id) {
 356         g_source_remove(cmd->delay_id);
 357     }
 358     if (cmd->params) {
 359         g_hash_table_destroy(cmd->params);
 360     }
 361     pcmk__reset_result(&(cmd->result));
 362     free(cmd->origin);
 363     free(cmd->action);
 364     free(cmd->real_action);
 365     free(cmd->userdata_str);
 366     free(cmd->rsc_id);
 367     free(cmd->client_id);
 368     free(cmd);
 369 }
 370 
 371 static gboolean
 372 stonith_recurring_op_helper(gpointer data)
     /* [previous][next][first][last][top][bottom][index][help] */
 373 {
 374     lrmd_cmd_t *cmd = data;
 375     lrmd_rsc_t *rsc;
 376 
 377     cmd->stonith_recurring_id = 0;
 378 
 379     if (!cmd->rsc_id) {
 380         return FALSE;
 381     }
 382 
 383     rsc = g_hash_table_lookup(rsc_list, cmd->rsc_id);
 384 
 385     pcmk__assert(rsc != NULL);
 386     /* take it out of recurring_ops list, and put it in the pending ops
 387      * to be executed */
 388     rsc->recurring_ops = g_list_remove(rsc->recurring_ops, cmd);
 389     rsc->pending_ops = g_list_append(rsc->pending_ops, cmd);
 390 #ifdef PCMK__TIME_USE_CGT
 391     get_current_time(&(cmd->t_queue), &(cmd->t_first_queue));
 392 #endif
 393     mainloop_set_trigger(rsc->work);
 394 
 395     return FALSE;
 396 }
 397 
 398 static inline void
 399 start_recurring_timer(lrmd_cmd_t *cmd)
     /* [previous][next][first][last][top][bottom][index][help] */
 400 {
 401     if (!cmd || (cmd->interval_ms <= 0)) {
 402         return;
 403     }
 404 
 405     cmd->stonith_recurring_id = pcmk__create_timer(cmd->interval_ms,
 406                                                    stonith_recurring_op_helper,
 407                                                    cmd);
 408 }
 409 
 410 static gboolean
 411 start_delay_helper(gpointer data)
     /* [previous][next][first][last][top][bottom][index][help] */
 412 {
 413     lrmd_cmd_t *cmd = data;
 414     lrmd_rsc_t *rsc = NULL;
 415 
 416     cmd->delay_id = 0;
 417     rsc = cmd->rsc_id ? g_hash_table_lookup(rsc_list, cmd->rsc_id) : NULL;
 418 
 419     if (rsc) {
 420         mainloop_set_trigger(rsc->work);
 421     }
 422 
 423     return FALSE;
 424 }
 425 
 426 /*!
 427  * \internal
 428  * \brief Check whether a list already contains the equivalent of a given action
 429  *
 430  * \param[in] action_list  List to search
 431  * \param[in] cmd          Action to search for
 432  */
 433 static lrmd_cmd_t *
 434 find_duplicate_action(const GList *action_list, const lrmd_cmd_t *cmd)
     /* [previous][next][first][last][top][bottom][index][help] */
 435 {
 436     for (const GList *item = action_list; item != NULL; item = item->next) {
 437         lrmd_cmd_t *dup = item->data;
 438 
 439         if (action_matches(cmd, dup->action, dup->interval_ms)) {
 440             return dup;
 441         }
 442     }
 443     return NULL;
 444 }
 445 
 446 static bool
 447 merge_recurring_duplicate(lrmd_rsc_t * rsc, lrmd_cmd_t * cmd)
     /* [previous][next][first][last][top][bottom][index][help] */
 448 {
 449     lrmd_cmd_t * dup = NULL;
 450     bool dup_pending = true;
 451 
 452     if (cmd->interval_ms == 0) {
 453         return false;
 454     }
 455 
 456     // Search for a duplicate of this action (in-flight or not)
 457     dup = find_duplicate_action(rsc->pending_ops, cmd);
 458     if (dup == NULL) {
 459         dup_pending = false;
 460         dup = find_duplicate_action(rsc->recurring_ops, cmd);
 461         if (dup == NULL) {
 462             return false;
 463         }
 464     }
 465 
 466     /* Do not merge fencing monitors marked for cancellation, so we can reply to
 467      * the cancellation separately.
 468      */
 469     if (pcmk__str_eq(rsc->class, PCMK_RESOURCE_CLASS_STONITH,
 470                      pcmk__str_casei)
 471         && (dup->result.execution_status == PCMK_EXEC_CANCELLED)) {
 472         return false;
 473     }
 474 
 475     /* This should not occur. If it does, we need to investigate how something
 476      * like this is possible in the controller.
 477      */
 478     crm_warn("Duplicate recurring op entry detected (" PCMK__OP_FMT
 479              "), merging with previous op entry",
 480              rsc->rsc_id, normalize_action_name(rsc, dup->action),
 481              dup->interval_ms);
 482 
 483     // Merge new action's call ID and user data into existing action
 484     dup->first_notify_sent = false;
 485     free(dup->userdata_str);
 486     dup->userdata_str = cmd->userdata_str;
 487     cmd->userdata_str = NULL;
 488     dup->call_id = cmd->call_id;
 489     free_lrmd_cmd(cmd);
 490     cmd = NULL;
 491 
 492     /* If dup is not pending, that means it has already executed at least once
 493      * and is waiting in the interval. In that case, stop waiting and initiate
 494      * a new instance now.
 495      */
 496     if (!dup_pending) {
 497         if (pcmk__str_eq(rsc->class, PCMK_RESOURCE_CLASS_STONITH,
 498                          pcmk__str_casei)) {
 499             stop_recurring_timer(dup);
 500             stonith_recurring_op_helper(dup);
 501         } else {
 502             services_action_kick(rsc->rsc_id,
 503                                  normalize_action_name(rsc, dup->action),
 504                                  dup->interval_ms);
 505         }
 506     }
 507     return true;
 508 }
 509 
 510 static void
 511 schedule_lrmd_cmd(lrmd_rsc_t * rsc, lrmd_cmd_t * cmd)
     /* [previous][next][first][last][top][bottom][index][help] */
 512 {
 513     CRM_CHECK(cmd != NULL, return);
 514     CRM_CHECK(rsc != NULL, return);
 515 
 516     crm_trace("Scheduling %s on %s", cmd->action, rsc->rsc_id);
 517 
 518     if (merge_recurring_duplicate(rsc, cmd)) {
 519         // Equivalent of cmd has already been scheduled
 520         return;
 521     }
 522 
 523     /* The controller expects the executor to automatically cancel
 524      * recurring operations before a resource stops.
 525      */
 526     if (pcmk__str_eq(cmd->action, PCMK_ACTION_STOP, pcmk__str_casei)) {
 527         cancel_all_recurring(rsc, NULL);
 528     }
 529 
 530     rsc->pending_ops = g_list_append(rsc->pending_ops, cmd);
 531 #ifdef PCMK__TIME_USE_CGT
 532     get_current_time(&(cmd->t_queue), &(cmd->t_first_queue));
 533 #endif
 534     mainloop_set_trigger(rsc->work);
 535 
 536     if (cmd->start_delay) {
 537         cmd->delay_id = pcmk__create_timer(cmd->start_delay, start_delay_helper, cmd);
 538     }
 539 }
 540 
 541 static xmlNode *
 542 create_lrmd_reply(const char *origin, int rc, int call_id)
     /* [previous][next][first][last][top][bottom][index][help] */
 543 {
 544     xmlNode *reply = pcmk__xe_create(NULL, PCMK__XE_LRMD_REPLY);
 545 
 546     crm_xml_add(reply, PCMK__XA_LRMD_ORIGIN, origin);
 547     crm_xml_add_int(reply, PCMK__XA_LRMD_RC, rc);
 548     crm_xml_add_int(reply, PCMK__XA_LRMD_CALLID, call_id);
 549     return reply;
 550 }
 551 
 552 static void
 553 send_client_notify(gpointer key, gpointer value, gpointer user_data)
     /* [previous][next][first][last][top][bottom][index][help] */
 554 {
 555     xmlNode *update_msg = user_data;
 556     pcmk__client_t *client = value;
 557     int rc;
 558     int log_level = LOG_WARNING;
 559     const char *msg = NULL;
 560 
 561     CRM_CHECK(client != NULL, return);
 562     if (client->name == NULL) {
 563         crm_trace("Skipping notification to client without name");
 564         return;
 565     }
 566     if (pcmk_is_set(client->flags, pcmk__client_to_proxy)) {
 567         /* We only want to notify clients of the executor IPC API. If we are
 568          * running as Pacemaker Remote, we may have clients proxied to other
 569          * IPC services in the cluster, so skip those.
 570          */
 571         crm_trace("Skipping executor API notification to client %s",
 572                   pcmk__client_name(client));
 573         return;
 574     }
 575 
 576     rc = lrmd_server_send_notify(client, update_msg);
 577     if (rc == pcmk_rc_ok) {
 578         return;
 579     }
 580 
 581     switch (rc) {
 582         case ENOTCONN:
 583         case EPIPE: // Client exited without waiting for notification
 584             log_level = LOG_INFO;
 585             msg = "Disconnected";
 586             break;
 587 
 588         default:
 589             msg = pcmk_rc_str(rc);
 590             break;
 591     }
 592     do_crm_log(log_level, "Could not notify client %s: %s " QB_XS " rc=%d",
 593                pcmk__client_name(client), msg, rc);
 594 }
 595 
 596 static void
 597 send_cmd_complete_notify(lrmd_cmd_t * cmd)
     /* [previous][next][first][last][top][bottom][index][help] */
 598 {
 599     xmlNode *notify = NULL;
 600     int exec_time = 0;
 601     int queue_time = 0;
 602 
 603 #ifdef PCMK__TIME_USE_CGT
 604     exec_time = time_diff_ms(NULL, &(cmd->t_run));
 605     queue_time = time_diff_ms(&cmd->t_run, &(cmd->t_queue));
 606 #endif
 607     log_finished(cmd, exec_time, queue_time);
 608 
 609     /* If the originator requested to be notified only for changes in recurring
 610      * operation results, skip the notification if the result hasn't changed.
 611      */
 612     if (cmd->first_notify_sent
 613         && pcmk_is_set(cmd->call_opts, lrmd_opt_notify_changes_only)
 614         && (cmd->last_notify_rc == cmd->result.exit_status)
 615         && (cmd->last_notify_op_status == cmd->result.execution_status)) {
 616         return;
 617     }
 618 
 619     cmd->first_notify_sent = true;
 620     cmd->last_notify_rc = cmd->result.exit_status;
 621     cmd->last_notify_op_status = cmd->result.execution_status;
 622 
 623     notify = pcmk__xe_create(NULL, PCMK__XE_LRMD_NOTIFY);
 624 
 625     crm_xml_add(notify, PCMK__XA_LRMD_ORIGIN, __func__);
 626     crm_xml_add_int(notify, PCMK__XA_LRMD_TIMEOUT, cmd->timeout);
 627     crm_xml_add_ms(notify, PCMK__XA_LRMD_RSC_INTERVAL, cmd->interval_ms);
 628     crm_xml_add_int(notify, PCMK__XA_LRMD_RSC_START_DELAY, cmd->start_delay);
 629     crm_xml_add_int(notify, PCMK__XA_LRMD_EXEC_RC, cmd->result.exit_status);
 630     crm_xml_add_int(notify, PCMK__XA_LRMD_EXEC_OP_STATUS,
 631                     cmd->result.execution_status);
 632     crm_xml_add_int(notify, PCMK__XA_LRMD_CALLID, cmd->call_id);
 633     crm_xml_add_int(notify, PCMK__XA_LRMD_RSC_DELETED, cmd->rsc_deleted);
 634 
 635     crm_xml_add_ll(notify, PCMK__XA_LRMD_RUN_TIME,
 636                    (long long) cmd->epoch_last_run);
 637     crm_xml_add_ll(notify, PCMK__XA_LRMD_RCCHANGE_TIME,
 638                    (long long) cmd->epoch_rcchange);
 639 #ifdef PCMK__TIME_USE_CGT
 640     crm_xml_add_int(notify, PCMK__XA_LRMD_EXEC_TIME, exec_time);
 641     crm_xml_add_int(notify, PCMK__XA_LRMD_QUEUE_TIME, queue_time);
 642 #endif
 643 
 644     crm_xml_add(notify, PCMK__XA_LRMD_OP, LRMD_OP_RSC_EXEC);
 645     crm_xml_add(notify, PCMK__XA_LRMD_RSC_ID, cmd->rsc_id);
 646     if(cmd->real_action) {
 647         crm_xml_add(notify, PCMK__XA_LRMD_RSC_ACTION, cmd->real_action);
 648     } else {
 649         crm_xml_add(notify, PCMK__XA_LRMD_RSC_ACTION, cmd->action);
 650     }
 651     crm_xml_add(notify, PCMK__XA_LRMD_RSC_USERDATA_STR, cmd->userdata_str);
 652     crm_xml_add(notify, PCMK__XA_LRMD_RSC_EXIT_REASON, cmd->result.exit_reason);
 653 
 654     if (cmd->result.action_stderr != NULL) {
 655         crm_xml_add(notify, PCMK__XA_LRMD_RSC_OUTPUT,
 656                     cmd->result.action_stderr);
 657 
 658     } else if (cmd->result.action_stdout != NULL) {
 659         crm_xml_add(notify, PCMK__XA_LRMD_RSC_OUTPUT,
 660                     cmd->result.action_stdout);
 661     }
 662 
 663     if (cmd->params) {
 664         char *key = NULL;
 665         char *value = NULL;
 666         GHashTableIter iter;
 667 
 668         xmlNode *args = pcmk__xe_create(notify, PCMK__XE_ATTRIBUTES);
 669 
 670         g_hash_table_iter_init(&iter, cmd->params);
 671         while (g_hash_table_iter_next(&iter, (gpointer *) & key, (gpointer *) & value)) {
 672             hash2smartfield((gpointer) key, (gpointer) value, args);
 673         }
 674     }
 675     if ((cmd->client_id != NULL)
 676         && pcmk_is_set(cmd->call_opts, lrmd_opt_notify_orig_only)) {
 677 
 678         pcmk__client_t *client = pcmk__find_client_by_id(cmd->client_id);
 679 
 680         if (client != NULL) {
 681             send_client_notify(client->id, client, notify);
 682         }
 683     } else {
 684         pcmk__foreach_ipc_client(send_client_notify, notify);
 685     }
 686 
 687     pcmk__xml_free(notify);
 688 }
 689 
 690 static void
 691 send_generic_notify(int rc, xmlNode * request)
     /* [previous][next][first][last][top][bottom][index][help] */
 692 {
 693     if (pcmk__ipc_client_count() != 0) {
 694         int call_id = 0;
 695         xmlNode *notify = NULL;
 696         xmlNode *rsc_xml = get_xpath_object("//" PCMK__XE_LRMD_RSC, request,
 697                                             LOG_ERR);
 698         const char *rsc_id = crm_element_value(rsc_xml, PCMK__XA_LRMD_RSC_ID);
 699         const char *op = crm_element_value(request, PCMK__XA_LRMD_OP);
 700 
 701         crm_element_value_int(request, PCMK__XA_LRMD_CALLID, &call_id);
 702 
 703         notify = pcmk__xe_create(NULL, PCMK__XE_LRMD_NOTIFY);
 704         crm_xml_add(notify, PCMK__XA_LRMD_ORIGIN, __func__);
 705         crm_xml_add_int(notify, PCMK__XA_LRMD_RC, rc);
 706         crm_xml_add_int(notify, PCMK__XA_LRMD_CALLID, call_id);
 707         crm_xml_add(notify, PCMK__XA_LRMD_OP, op);
 708         crm_xml_add(notify, PCMK__XA_LRMD_RSC_ID, rsc_id);
 709 
 710         pcmk__foreach_ipc_client(send_client_notify, notify);
 711 
 712         pcmk__xml_free(notify);
 713     }
 714 }
 715 
 716 static void
 717 cmd_reset(lrmd_cmd_t * cmd)
     /* [previous][next][first][last][top][bottom][index][help] */
 718 {
 719     cmd->last_pid = 0;
 720 #ifdef PCMK__TIME_USE_CGT
 721     memset(&cmd->t_run, 0, sizeof(cmd->t_run));
 722     memset(&cmd->t_queue, 0, sizeof(cmd->t_queue));
 723 #endif
 724     cmd->epoch_last_run = 0;
 725 
 726     pcmk__reset_result(&(cmd->result));
 727     cmd->result.execution_status = PCMK_EXEC_DONE;
 728 }
 729 
 730 static void
 731 cmd_finalize(lrmd_cmd_t * cmd, lrmd_rsc_t * rsc)
     /* [previous][next][first][last][top][bottom][index][help] */
 732 {
 733     crm_trace("Resource operation rsc:%s action:%s completed (%p %p)", cmd->rsc_id, cmd->action,
 734               rsc ? rsc->active : NULL, cmd);
 735 
 736     if (rsc && (rsc->active == cmd)) {
 737         rsc->active = NULL;
 738         mainloop_set_trigger(rsc->work);
 739     }
 740 
 741     if (!rsc) {
 742         cmd->rsc_deleted = 1;
 743     }
 744 
 745     /* reset original timeout so client notification has correct information */
 746     cmd->timeout = cmd->timeout_orig;
 747 
 748     send_cmd_complete_notify(cmd);
 749 
 750     if ((cmd->interval_ms != 0)
 751         && (cmd->result.execution_status == PCMK_EXEC_CANCELLED)) {
 752 
 753         if (rsc) {
 754             rsc->recurring_ops = g_list_remove(rsc->recurring_ops, cmd);
 755             rsc->pending_ops = g_list_remove(rsc->pending_ops, cmd);
 756         }
 757         free_lrmd_cmd(cmd);
 758     } else if (cmd->interval_ms == 0) {
 759         if (rsc) {
 760             rsc->pending_ops = g_list_remove(rsc->pending_ops, cmd);
 761         }
 762         free_lrmd_cmd(cmd);
 763     } else {
 764         /* Clear all the values pertaining just to the last iteration of a recurring op. */
 765         cmd_reset(cmd);
 766     }
 767 }
 768 
 769 struct notify_new_client_data {
 770     xmlNode *notify;
 771     pcmk__client_t *new_client;
 772 };
 773 
 774 static void
 775 notify_one_client(gpointer key, gpointer value, gpointer user_data)
     /* [previous][next][first][last][top][bottom][index][help] */
 776 {
 777     pcmk__client_t *client = value;
 778     struct notify_new_client_data *data = user_data;
 779 
 780     if (!pcmk__str_eq(client->id, data->new_client->id, pcmk__str_casei)) {
 781         send_client_notify(key, (gpointer) client, (gpointer) data->notify);
 782     }
 783 }
 784 
 785 void
 786 notify_of_new_client(pcmk__client_t *new_client)
     /* [previous][next][first][last][top][bottom][index][help] */
 787 {
 788     struct notify_new_client_data data;
 789 
 790     data.new_client = new_client;
 791     data.notify = pcmk__xe_create(NULL, PCMK__XE_LRMD_NOTIFY);
 792     crm_xml_add(data.notify, PCMK__XA_LRMD_ORIGIN, __func__);
 793     crm_xml_add(data.notify, PCMK__XA_LRMD_OP, LRMD_OP_NEW_CLIENT);
 794     pcmk__foreach_ipc_client(notify_one_client, &data);
 795     pcmk__xml_free(data.notify);
 796 }
 797 
 798 void
 799 client_disconnect_cleanup(const char *client_id)
     /* [previous][next][first][last][top][bottom][index][help] */
 800 {
 801     GHashTableIter iter;
 802     lrmd_rsc_t *rsc = NULL;
 803     char *key = NULL;
 804 
 805     g_hash_table_iter_init(&iter, rsc_list);
 806     while (g_hash_table_iter_next(&iter, (gpointer *) & key, (gpointer *) & rsc)) {
 807         if (pcmk_all_flags_set(rsc->call_opts, lrmd_opt_drop_recurring)) {
 808             /* This client is disconnecting, drop any recurring operations
 809              * it may have initiated on the resource */
 810             cancel_all_recurring(rsc, client_id);
 811         }
 812     }
 813 }
 814 
 815 static void
 816 action_complete(svc_action_t * action)
     /* [previous][next][first][last][top][bottom][index][help] */
 817 {
 818     lrmd_rsc_t *rsc;
 819     lrmd_cmd_t *cmd = action->cb_data;
 820     enum ocf_exitcode code;
 821 
 822 #ifdef PCMK__TIME_USE_CGT
 823     const char *rclass = NULL;
 824     bool goagain = false;
 825 #endif
 826 
 827     if (!cmd) {
 828         crm_err("Completed executor action (%s) does not match any known operations",
 829                 action->id);
 830         return;
 831     }
 832 
 833 #ifdef PCMK__TIME_USE_CGT
 834     if (cmd->result.exit_status != action->rc) {
 835         cmd->epoch_rcchange = time(NULL);
 836     }
 837 #endif
 838 
 839     cmd->last_pid = action->pid;
 840 
 841     // Cast variable instead of function return to keep compilers happy
 842     code = services_result2ocf(action->standard, cmd->action, action->rc);
 843     pcmk__set_result(&(cmd->result), (int) code,
 844                      action->status, services__exit_reason(action));
 845 
 846     rsc = cmd->rsc_id ? g_hash_table_lookup(rsc_list, cmd->rsc_id) : NULL;
 847 
 848 #ifdef PCMK__TIME_USE_CGT
 849     if (rsc != NULL) {
 850         rclass = rsc->class;
 851 #if PCMK__ENABLE_SERVICE
 852         if (pcmk__str_eq(rclass, PCMK_RESOURCE_CLASS_SERVICE,
 853                          pcmk__str_casei)) {
 854             rclass = resources_find_service_class(rsc->type);
 855         }
 856 #endif
 857     }
 858 
 859     if (pcmk__str_eq(rclass, PCMK_RESOURCE_CLASS_SYSTEMD, pcmk__str_casei)) {
 860         if (pcmk__result_ok(&(cmd->result))
 861             && pcmk__strcase_any_of(cmd->action, PCMK_ACTION_START,
 862                                     PCMK_ACTION_STOP, NULL)) {
 863             /* systemd returns from start and stop actions after the action
 864              * begins, not after it completes. We have to jump through a few
 865              * hoops so that we don't report 'complete' to the rest of pacemaker
 866              * until it's actually done.
 867              */
 868             goagain = true;
 869             cmd->real_action = cmd->action;
 870             cmd->action = pcmk__str_copy(PCMK_ACTION_MONITOR);
 871 
 872         } else if (cmd->real_action != NULL) {
 873             // This is follow-up monitor to check whether start/stop completed
 874             if (cmd->result.execution_status == PCMK_EXEC_PENDING) {
 875                 goagain = true;
 876 
 877             } else if (pcmk__result_ok(&(cmd->result))
 878                        && pcmk__str_eq(cmd->real_action, PCMK_ACTION_STOP,
 879                                        pcmk__str_casei)) {
 880                 goagain = true;
 881 
 882             } else {
 883                 int time_sum = time_diff_ms(NULL, &(cmd->t_first_run));
 884                 int timeout_left = cmd->timeout_orig - time_sum;
 885 
 886                 crm_debug("%s systemd %s is now complete (elapsed=%dms, "
 887                           "remaining=%dms): %s (%d)",
 888                           cmd->rsc_id, cmd->real_action, time_sum, timeout_left,
 889                           crm_exit_str(cmd->result.exit_status),
 890                           cmd->result.exit_status);
 891                 cmd_original_times(cmd);
 892 
 893                 // Monitors may return "not running", but start/stop shouldn't
 894                 if ((cmd->result.execution_status == PCMK_EXEC_DONE)
 895                     && (cmd->result.exit_status == PCMK_OCF_NOT_RUNNING)) {
 896 
 897                     if (pcmk__str_eq(cmd->real_action, PCMK_ACTION_START,
 898                                      pcmk__str_casei)) {
 899                         cmd->result.exit_status = PCMK_OCF_UNKNOWN_ERROR;
 900                     } else if (pcmk__str_eq(cmd->real_action, PCMK_ACTION_STOP,
 901                                             pcmk__str_casei)) {
 902                         cmd->result.exit_status = PCMK_OCF_OK;
 903                     }
 904                 }
 905             }
 906         }
 907     }
 908 #endif
 909 
 910 #ifdef PCMK__TIME_USE_CGT
 911     if (goagain) {
 912         int time_sum = time_diff_ms(NULL, &(cmd->t_first_run));
 913         int timeout_left = cmd->timeout_orig - time_sum;
 914         int delay = cmd->timeout_orig / 10;
 915 
 916         if(delay >= timeout_left && timeout_left > 20) {
 917             delay = timeout_left/2;
 918         }
 919 
 920         delay = QB_MIN(2000, delay);
 921         if (delay < timeout_left) {
 922             cmd->start_delay = delay;
 923             cmd->timeout = timeout_left;
 924 
 925             if (pcmk__result_ok(&(cmd->result))) {
 926                 crm_debug("%s %s may still be in progress: re-scheduling (elapsed=%dms, remaining=%dms, start_delay=%dms)",
 927                           cmd->rsc_id, cmd->real_action, time_sum, timeout_left, delay);
 928 
 929             } else if (cmd->result.execution_status == PCMK_EXEC_PENDING) {
 930                 crm_info("%s %s is still in progress: re-scheduling (elapsed=%dms, remaining=%dms, start_delay=%dms)",
 931                          cmd->rsc_id, cmd->action, time_sum, timeout_left, delay);
 932 
 933             } else {
 934                 crm_notice("%s %s failed: %s: Re-scheduling (remaining "
 935                            "timeout %s) " QB_XS
 936                            " exitstatus=%d elapsed=%dms start_delay=%dms)",
 937                            cmd->rsc_id, cmd->action,
 938                            crm_exit_str(cmd->result.exit_status),
 939                            pcmk__readable_interval(timeout_left),
 940                            cmd->result.exit_status, time_sum, delay);
 941             }
 942 
 943             cmd_reset(cmd);
 944             if(rsc) {
 945                 rsc->active = NULL;
 946             }
 947             schedule_lrmd_cmd(rsc, cmd);
 948 
 949             /* Don't finalize cmd, we're not done with it yet */
 950             return;
 951 
 952         } else {
 953             crm_notice("Giving up on %s %s (rc=%d): timeout (elapsed=%dms, remaining=%dms)",
 954                        cmd->rsc_id,
 955                        (cmd->real_action? cmd->real_action : cmd->action),
 956                        cmd->result.exit_status, time_sum, timeout_left);
 957             pcmk__set_result(&(cmd->result), PCMK_OCF_UNKNOWN_ERROR,
 958                              PCMK_EXEC_TIMEOUT,
 959                              "Investigate reason for timeout, and adjust "
 960                              "configured operation timeout if necessary");
 961             cmd_original_times(cmd);
 962         }
 963     }
 964 #endif
 965 
 966     pcmk__set_result_output(&(cmd->result), services__grab_stdout(action),
 967                             services__grab_stderr(action));
 968     cmd_finalize(cmd, rsc);
 969 }
 970 
 971 /*!
 972  * \internal
 973  * \brief Process the result of a fence device action (start, stop, or monitor)
 974  *
 975  * \param[in,out] cmd               Fence device action that completed
 976  * \param[in]     exit_status       Fencer API exit status for action
 977  * \param[in]     execution_status  Fencer API execution status for action
 978  * \param[in]     exit_reason       Human-friendly detail, if action failed
 979  */
 980 static void
 981 stonith_action_complete(lrmd_cmd_t *cmd, int exit_status,
     /* [previous][next][first][last][top][bottom][index][help] */
 982                         enum pcmk_exec_status execution_status,
 983                         const char *exit_reason)
 984 {
 985     // This can be NULL if resource was removed before command completed
 986     lrmd_rsc_t *rsc = g_hash_table_lookup(rsc_list, cmd->rsc_id);
 987 
 988     // Simplify fencer exit status to uniform exit status
 989     if (exit_status != CRM_EX_OK) {
 990         exit_status = PCMK_OCF_UNKNOWN_ERROR;
 991     }
 992 
 993     if (cmd->result.execution_status == PCMK_EXEC_CANCELLED) {
 994         /* An in-flight fence action was cancelled. The execution status is
 995          * already correct, so don't overwrite it.
 996          */
 997         execution_status = PCMK_EXEC_CANCELLED;
 998 
 999     } else {
1000         /* Some execution status codes have specific meanings for the fencer
1001          * that executor clients may not expect, so map them to a simple error
1002          * status.
1003          */
1004         switch (execution_status) {
1005             case PCMK_EXEC_NOT_CONNECTED:
1006             case PCMK_EXEC_INVALID:
1007                 execution_status = PCMK_EXEC_ERROR;
1008                 break;
1009 
1010             case PCMK_EXEC_NO_FENCE_DEVICE:
1011                 /* This should be possible only for probes in practice, but
1012                  * interpret for all actions to be safe.
1013                  */
1014                 if (pcmk__str_eq(cmd->action, PCMK_ACTION_MONITOR,
1015                                  pcmk__str_none)) {
1016                     exit_status = PCMK_OCF_NOT_RUNNING;
1017 
1018                 } else if (pcmk__str_eq(cmd->action, PCMK_ACTION_STOP,
1019                                         pcmk__str_none)) {
1020                     exit_status = PCMK_OCF_OK;
1021 
1022                 } else {
1023                     exit_status = PCMK_OCF_NOT_INSTALLED;
1024                 }
1025                 execution_status = PCMK_EXEC_ERROR;
1026                 break;
1027 
1028             case PCMK_EXEC_NOT_SUPPORTED:
1029                 exit_status = PCMK_OCF_UNIMPLEMENT_FEATURE;
1030                 break;
1031 
1032             default:
1033                 break;
1034         }
1035     }
1036 
1037     pcmk__set_result(&cmd->result, exit_status, execution_status, exit_reason);
1038 
1039     // Certain successful actions change the known state of the resource
1040     if ((rsc != NULL) && pcmk__result_ok(&(cmd->result))) {
1041 
1042         if (pcmk__str_eq(cmd->action, PCMK_ACTION_START, pcmk__str_casei)) {
1043             pcmk__set_result(&rsc->fence_probe_result, CRM_EX_OK,
1044                              PCMK_EXEC_DONE, NULL); // "running"
1045 
1046         } else if (pcmk__str_eq(cmd->action, PCMK_ACTION_STOP,
1047                                 pcmk__str_casei)) {
1048             pcmk__set_result(&rsc->fence_probe_result, CRM_EX_ERROR,
1049                              PCMK_EXEC_NO_FENCE_DEVICE, NULL); // "not running"
1050         }
1051     }
1052 
1053     /* The recurring timer should not be running at this point in any case, but
1054      * as a failsafe, stop it if it is.
1055      */
1056     stop_recurring_timer(cmd);
1057 
1058     /* Reschedule this command if appropriate. If a recurring command is *not*
1059      * rescheduled, its status must be PCMK_EXEC_CANCELLED, otherwise it will
1060      * not be removed from recurring_ops by cmd_finalize().
1061      */
1062     if (rsc && (cmd->interval_ms > 0)
1063         && (cmd->result.execution_status != PCMK_EXEC_CANCELLED)) {
1064         start_recurring_timer(cmd);
1065     }
1066 
1067     cmd_finalize(cmd, rsc);
1068 }
1069 
1070 static void
1071 lrmd_stonith_callback(stonith_t * stonith, stonith_callback_data_t * data)
     /* [previous][next][first][last][top][bottom][index][help] */
1072 {
1073     if ((data == NULL) || (data->userdata == NULL)) {
1074         crm_err("Ignoring fence action result: "
1075                 "Invalid callback arguments (bug?)");
1076     } else {
1077         stonith_action_complete((lrmd_cmd_t *) data->userdata,
1078                                 stonith__exit_status(data),
1079                                 stonith__execution_status(data),
1080                                 stonith__exit_reason(data));
1081     }
1082 }
1083 
1084 void
1085 stonith_connection_failed(void)
     /* [previous][next][first][last][top][bottom][index][help] */
1086 {
1087     GHashTableIter iter;
1088     lrmd_rsc_t *rsc = NULL;
1089 
1090     crm_warn("Connection to fencer lost (any pending operations for "
1091              "fence devices will be considered failed)");
1092 
1093     g_hash_table_iter_init(&iter, rsc_list);
1094     while (g_hash_table_iter_next(&iter, NULL, (gpointer *) &rsc)) {
1095         if (!pcmk__str_eq(rsc->class, PCMK_RESOURCE_CLASS_STONITH,
1096                           pcmk__str_none)) {
1097             continue;
1098         }
1099 
1100         /* If we registered this fence device, we don't know whether the
1101          * fencer still has the registration or not. Cause future probes to
1102          * return an error until the resource is stopped or started
1103          * successfully. This is especially important if the controller also
1104          * went away (possibly due to a cluster layer restart) and won't
1105          * receive our client notification of any monitors finalized below.
1106          */
1107         if (rsc->fence_probe_result.execution_status == PCMK_EXEC_DONE) {
1108             pcmk__set_result(&rsc->fence_probe_result, CRM_EX_ERROR,
1109                              PCMK_EXEC_NOT_CONNECTED,
1110                              "Lost connection to fencer");
1111         }
1112 
1113         // Consider any active, pending, or recurring operations as failed
1114 
1115         for (GList *op = rsc->recurring_ops; op != NULL; op = op->next) {
1116             lrmd_cmd_t *cmd = op->data;
1117 
1118             /* This won't free a recurring op but instead restart its timer.
1119              * If cmd is rsc->active, this will set rsc->active to NULL, so we
1120              * don't have to worry about finalizing it a second time below.
1121              */
1122             stonith_action_complete(cmd,
1123                                     CRM_EX_ERROR, PCMK_EXEC_NOT_CONNECTED,
1124                                     "Lost connection to fencer");
1125         }
1126 
1127         if (rsc->active != NULL) {
1128             rsc->pending_ops = g_list_prepend(rsc->pending_ops, rsc->active);
1129         }
1130         while (rsc->pending_ops != NULL) {
1131             // This will free the op and remove it from rsc->pending_ops
1132             stonith_action_complete((lrmd_cmd_t *) rsc->pending_ops->data,
1133                                     CRM_EX_ERROR, PCMK_EXEC_NOT_CONNECTED,
1134                                     "Lost connection to fencer");
1135         }
1136     }
1137 }
1138 
1139 /*!
1140  * \internal
1141  * \brief Execute a stonith resource "start" action
1142  *
1143  * Start a stonith resource by registering it with the fencer.
1144  * (Stonith agents don't have a start command.)
1145  *
1146  * \param[in,out] stonith_api  Connection to fencer
1147  * \param[in]     rsc          Stonith resource to start
1148  * \param[in]     cmd          Start command to execute
1149  *
1150  * \return pcmk_ok on success, -errno otherwise
1151  */
1152 static int
1153 execd_stonith_start(stonith_t *stonith_api, const lrmd_rsc_t *rsc,
     /* [previous][next][first][last][top][bottom][index][help] */
1154                     const lrmd_cmd_t *cmd)
1155 {
1156     char *key = NULL;
1157     char *value = NULL;
1158     stonith_key_value_t *device_params = NULL;
1159     int rc = pcmk_ok;
1160 
1161     // Convert command parameters to stonith API key/values
1162     if (cmd->params) {
1163         GHashTableIter iter;
1164 
1165         g_hash_table_iter_init(&iter, cmd->params);
1166         while (g_hash_table_iter_next(&iter, (gpointer *) & key, (gpointer *) & value)) {
1167             device_params = stonith_key_value_add(device_params, key, value);
1168         }
1169     }
1170 
1171     /* The fencer will automatically register devices via CIB notifications
1172      * when the CIB changes, but to avoid a possible race condition between
1173      * the fencer receiving the notification and the executor requesting that
1174      * resource, the executor registers the device as well. The fencer knows how
1175      * to handle duplicate registrations.
1176      */
1177     rc = stonith_api->cmds->register_device(stonith_api, st_opt_sync_call,
1178                                             cmd->rsc_id, rsc->provider,
1179                                             rsc->type, device_params);
1180 
1181     stonith_key_value_freeall(device_params, 1, 1);
1182     return rc;
1183 }
1184 
1185 /*!
1186  * \internal
1187  * \brief Execute a stonith resource "stop" action
1188  *
1189  * Stop a stonith resource by unregistering it with the fencer.
1190  * (Stonith agents don't have a stop command.)
1191  *
1192  * \param[in,out] stonith_api  Connection to fencer
1193  * \param[in]     rsc          Stonith resource to stop
1194  *
1195  * \return pcmk_ok on success, -errno otherwise
1196  */
1197 static inline int
1198 execd_stonith_stop(stonith_t *stonith_api, const lrmd_rsc_t *rsc)
     /* [previous][next][first][last][top][bottom][index][help] */
1199 {
1200     /* @TODO Failure would indicate a problem communicating with fencer;
1201      * perhaps we should try reconnecting and retrying a few times?
1202      */
1203     return stonith_api->cmds->remove_device(stonith_api, st_opt_sync_call,
1204                                             rsc->rsc_id);
1205 }
1206 
1207 /*!
1208  * \internal
1209  * \brief Initiate a stonith resource agent recurring "monitor" action
1210  *
1211  * \param[in,out] stonith_api  Connection to fencer
1212  * \param[in,out] rsc          Stonith resource to monitor
1213  * \param[in]     cmd          Monitor command being executed
1214  *
1215  * \return pcmk_ok if monitor was successfully initiated, -errno otherwise
1216  */
1217 static inline int
1218 execd_stonith_monitor(stonith_t *stonith_api, lrmd_rsc_t *rsc, lrmd_cmd_t *cmd)
     /* [previous][next][first][last][top][bottom][index][help] */
1219 {
1220     int rc = stonith_api->cmds->monitor(stonith_api, 0, cmd->rsc_id,
1221                                         pcmk__timeout_ms2s(cmd->timeout));
1222 
1223     rc = stonith_api->cmds->register_callback(stonith_api, rc, 0, 0, cmd,
1224                                               "lrmd_stonith_callback",
1225                                               lrmd_stonith_callback);
1226     if (rc == TRUE) {
1227         rsc->active = cmd;
1228         rc = pcmk_ok;
1229     } else {
1230         rc = -pcmk_err_generic;
1231     }
1232     return rc;
1233 }
1234 
1235 static void
1236 execute_stonith_action(lrmd_rsc_t *rsc, lrmd_cmd_t *cmd)
     /* [previous][next][first][last][top][bottom][index][help] */
1237 {
1238     int rc = 0;
1239     bool do_monitor = FALSE;
1240 
1241     stonith_t *stonith_api = get_stonith_connection();
1242 
1243     if (pcmk__str_eq(cmd->action, PCMK_ACTION_MONITOR, pcmk__str_casei)
1244         && (cmd->interval_ms == 0)) {
1245         // Probes don't require a fencer connection
1246         stonith_action_complete(cmd, rsc->fence_probe_result.exit_status,
1247                                 rsc->fence_probe_result.execution_status,
1248                                 rsc->fence_probe_result.exit_reason);
1249         return;
1250 
1251     } else if (stonith_api == NULL) {
1252         stonith_action_complete(cmd, PCMK_OCF_UNKNOWN_ERROR,
1253                                 PCMK_EXEC_NOT_CONNECTED,
1254                                 "No connection to fencer");
1255         return;
1256 
1257     } else if (pcmk__str_eq(cmd->action, PCMK_ACTION_START, pcmk__str_casei)) {
1258         rc = execd_stonith_start(stonith_api, rsc, cmd);
1259         if (rc == pcmk_ok) {
1260             do_monitor = TRUE;
1261         }
1262 
1263     } else if (pcmk__str_eq(cmd->action, PCMK_ACTION_STOP, pcmk__str_casei)) {
1264         rc = execd_stonith_stop(stonith_api, rsc);
1265 
1266     } else if (pcmk__str_eq(cmd->action, PCMK_ACTION_MONITOR,
1267                             pcmk__str_casei)) {
1268         do_monitor = TRUE;
1269 
1270     } else {
1271         stonith_action_complete(cmd, PCMK_OCF_UNIMPLEMENT_FEATURE,
1272                                 PCMK_EXEC_ERROR,
1273                                 "Invalid fence device action (bug?)");
1274         return;
1275     }
1276 
1277     if (do_monitor) {
1278         rc = execd_stonith_monitor(stonith_api, rsc, cmd);
1279         if (rc == pcmk_ok) {
1280             // Don't clean up yet, we will find out result of the monitor later
1281             return;
1282         }
1283     }
1284 
1285     stonith_action_complete(cmd,
1286                             ((rc == pcmk_ok)? CRM_EX_OK : CRM_EX_ERROR),
1287                             stonith__legacy2status(rc),
1288                             ((rc == -pcmk_err_generic)? NULL : pcmk_strerror(rc)));
1289 }
1290 
1291 static void
1292 execute_nonstonith_action(lrmd_rsc_t *rsc, lrmd_cmd_t *cmd)
     /* [previous][next][first][last][top][bottom][index][help] */
1293 {
1294     svc_action_t *action = NULL;
1295     GHashTable *params_copy = NULL;
1296 
1297     pcmk__assert((rsc != NULL) && (cmd != NULL));
1298 
1299     crm_trace("Creating action, resource:%s action:%s class:%s provider:%s agent:%s",
1300               rsc->rsc_id, cmd->action, rsc->class, rsc->provider, rsc->type);
1301 
1302     params_copy = pcmk__str_table_dup(cmd->params);
1303 
1304     action = services__create_resource_action(rsc->rsc_id, rsc->class, rsc->provider,
1305                                      rsc->type,
1306                                      normalize_action_name(rsc, cmd->action),
1307                                      cmd->interval_ms, cmd->timeout,
1308                                      params_copy, cmd->service_flags);
1309 
1310     if (action == NULL) {
1311         pcmk__set_result(&(cmd->result), PCMK_OCF_UNKNOWN_ERROR,
1312                          PCMK_EXEC_ERROR, strerror(ENOMEM));
1313         cmd_finalize(cmd, rsc);
1314         return;
1315     }
1316 
1317     if (action->rc != PCMK_OCF_UNKNOWN) {
1318         pcmk__set_result(&(cmd->result), action->rc, action->status,
1319                          services__exit_reason(action));
1320         services_action_free(action);
1321         cmd_finalize(cmd, rsc);
1322         return;
1323     }
1324 
1325     action->cb_data = cmd;
1326 
1327     if (services_action_async(action, action_complete)) {
1328         /* The services library has taken responsibility for the action. It
1329          * could be pending, blocked, or merged into a duplicate recurring
1330          * action, in which case the action callback (action_complete())
1331          * will be called when the action completes, otherwise the callback has
1332          * already been called.
1333          *
1334          * action_complete() calls cmd_finalize() which can free cmd, so cmd
1335          * cannot be used here.
1336          */
1337     } else {
1338         /* This is a recurring action that is not being cancelled and could not
1339          * be initiated. It has been rescheduled, and the action callback
1340          * (action_complete()) has been called, which in this case has already
1341          * called cmd_finalize(), which in this case should only reset (not
1342          * free) cmd.
1343          */
1344 
1345         pcmk__set_result(&(cmd->result), action->rc, action->status,
1346                          services__exit_reason(action));
1347         services_action_free(action);
1348     }
1349 }
1350 
1351 static gboolean
1352 execute_resource_action(gpointer user_data)
     /* [previous][next][first][last][top][bottom][index][help] */
1353 {
1354     lrmd_rsc_t *rsc = (lrmd_rsc_t *) user_data;
1355     lrmd_cmd_t *cmd = NULL;
1356 
1357     CRM_CHECK(rsc != NULL, return FALSE);
1358 
1359     if (rsc->active) {
1360         crm_trace("%s is still active", rsc->rsc_id);
1361         return TRUE;
1362     }
1363 
1364     if (rsc->pending_ops) {
1365         GList *first = rsc->pending_ops;
1366 
1367         cmd = first->data;
1368         if (cmd->delay_id) {
1369             crm_trace
1370                 ("Command %s %s was asked to run too early, waiting for start_delay timeout of %dms",
1371                  cmd->rsc_id, cmd->action, cmd->start_delay);
1372             return TRUE;
1373         }
1374         rsc->pending_ops = g_list_remove_link(rsc->pending_ops, first);
1375         g_list_free_1(first);
1376 
1377 #ifdef PCMK__TIME_USE_CGT
1378         get_current_time(&(cmd->t_run), &(cmd->t_first_run));
1379 #endif
1380         cmd->epoch_last_run = time(NULL);
1381     }
1382 
1383     if (!cmd) {
1384         crm_trace("Nothing further to do for %s", rsc->rsc_id);
1385         return TRUE;
1386     }
1387 
1388     rsc->active = cmd;          /* only one op at a time for a rsc */
1389     if (cmd->interval_ms) {
1390         rsc->recurring_ops = g_list_append(rsc->recurring_ops, cmd);
1391     }
1392 
1393     log_execute(cmd);
1394 
1395     if (pcmk__str_eq(rsc->class, PCMK_RESOURCE_CLASS_STONITH, pcmk__str_casei)) {
1396         execute_stonith_action(rsc, cmd);
1397     } else {
1398         execute_nonstonith_action(rsc, cmd);
1399     }
1400 
1401     return TRUE;
1402 }
1403 
1404 void
1405 free_rsc(gpointer data)
     /* [previous][next][first][last][top][bottom][index][help] */
1406 {
1407     GList *gIter = NULL;
1408     lrmd_rsc_t *rsc = data;
1409     int is_stonith = pcmk__str_eq(rsc->class, PCMK_RESOURCE_CLASS_STONITH,
1410                                   pcmk__str_casei);
1411 
1412     gIter = rsc->pending_ops;
1413     while (gIter != NULL) {
1414         GList *next = gIter->next;
1415         lrmd_cmd_t *cmd = gIter->data;
1416 
1417         /* command was never executed */
1418         cmd->result.execution_status = PCMK_EXEC_CANCELLED;
1419         cmd_finalize(cmd, NULL);
1420 
1421         gIter = next;
1422     }
1423     /* frees list, but not list elements. */
1424     g_list_free(rsc->pending_ops);
1425 
1426     gIter = rsc->recurring_ops;
1427     while (gIter != NULL) {
1428         GList *next = gIter->next;
1429         lrmd_cmd_t *cmd = gIter->data;
1430 
1431         if (is_stonith) {
1432             cmd->result.execution_status = PCMK_EXEC_CANCELLED;
1433             /* If a stonith command is in-flight, just mark it as cancelled;
1434              * it is not safe to finalize/free the cmd until the stonith api
1435              * says it has either completed or timed out.
1436              */
1437             if (rsc->active != cmd) {
1438                 cmd_finalize(cmd, NULL);
1439             }
1440         } else {
1441             /* This command is already handed off to service library,
1442              * let service library cancel it and tell us via the callback
1443              * when it is cancelled. The rsc can be safely destroyed
1444              * even if we are waiting for the cancel result */
1445             services_action_cancel(rsc->rsc_id,
1446                                    normalize_action_name(rsc, cmd->action),
1447                                    cmd->interval_ms);
1448         }
1449 
1450         gIter = next;
1451     }
1452     /* frees list, but not list elements. */
1453     g_list_free(rsc->recurring_ops);
1454 
1455     free(rsc->rsc_id);
1456     free(rsc->class);
1457     free(rsc->provider);
1458     free(rsc->type);
1459     mainloop_destroy_trigger(rsc->work);
1460 
1461     free(rsc);
1462 }
1463 
1464 static int
1465 process_lrmd_signon(pcmk__client_t *client, xmlNode *request, int call_id,
     /* [previous][next][first][last][top][bottom][index][help] */
1466                     xmlNode **reply)
1467 {
1468     int rc = pcmk_ok;
1469     time_t now = time(NULL);
1470     const char *protocol_version =
1471         crm_element_value(request, PCMK__XA_LRMD_PROTOCOL_VERSION);
1472     const char *start_state = pcmk__env_option(PCMK__ENV_NODE_START_STATE);
1473 
1474     if (compare_version(protocol_version, LRMD_COMPATIBLE_PROTOCOL) < 0) {
1475         crm_err("Cluster API version must be greater than or equal to %s, not %s",
1476                 LRMD_COMPATIBLE_PROTOCOL, protocol_version);
1477         rc = -EPROTO;
1478     }
1479 
1480     if (pcmk__xe_attr_is_true(request, PCMK__XA_LRMD_IS_IPC_PROVIDER)) {
1481 #ifdef PCMK__COMPILE_REMOTE
1482         if ((client->remote != NULL)
1483             && pcmk_is_set(client->flags,
1484                            pcmk__client_tls_handshake_complete)) {
1485             const char *op = crm_element_value(request, PCMK__XA_LRMD_OP);
1486 
1487             // This is a remote connection from a cluster node's controller
1488             ipc_proxy_add_provider(client);
1489 
1490             /* If this was a register operation, also ask for new schema files but
1491              * only if it's supported by the protocol version.
1492              */
1493             if (pcmk__str_eq(op, CRM_OP_REGISTER, pcmk__str_none) &&
1494                 LRMD_SUPPORTS_SCHEMA_XFER(protocol_version)) {
1495                 remoted_request_cib_schema_files();
1496             }
1497         } else {
1498             rc = -EACCES;
1499         }
1500 #else
1501         rc = -EPROTONOSUPPORT;
1502 #endif
1503     }
1504 
1505     *reply = create_lrmd_reply(__func__, rc, call_id);
1506     crm_xml_add(*reply, PCMK__XA_LRMD_OP, CRM_OP_REGISTER);
1507     crm_xml_add(*reply, PCMK__XA_LRMD_CLIENTID, client->id);
1508     crm_xml_add(*reply, PCMK__XA_LRMD_PROTOCOL_VERSION, LRMD_PROTOCOL_VERSION);
1509     crm_xml_add_ll(*reply, PCMK__XA_UPTIME, now - start_time);
1510 
1511     if (start_state) {
1512         crm_xml_add(*reply, PCMK__XA_NODE_START_STATE, start_state);
1513     }
1514 
1515     return rc;
1516 }
1517 
1518 static int
1519 process_lrmd_rsc_register(pcmk__client_t *client, uint32_t id, xmlNode *request)
     /* [previous][next][first][last][top][bottom][index][help] */
1520 {
1521     int rc = pcmk_ok;
1522     lrmd_rsc_t *rsc = build_rsc_from_xml(request);
1523     lrmd_rsc_t *dup = g_hash_table_lookup(rsc_list, rsc->rsc_id);
1524 
1525     if (dup &&
1526         pcmk__str_eq(rsc->class, dup->class, pcmk__str_casei) &&
1527         pcmk__str_eq(rsc->provider, dup->provider, pcmk__str_casei) && pcmk__str_eq(rsc->type, dup->type, pcmk__str_casei)) {
1528 
1529         crm_notice("Ignoring duplicate registration of '%s'", rsc->rsc_id);
1530         free_rsc(rsc);
1531         return rc;
1532     }
1533 
1534     g_hash_table_replace(rsc_list, rsc->rsc_id, rsc);
1535     crm_info("Cached agent information for '%s'", rsc->rsc_id);
1536     return rc;
1537 }
1538 
1539 static xmlNode *
1540 process_lrmd_get_rsc_info(xmlNode *request, int call_id)
     /* [previous][next][first][last][top][bottom][index][help] */
1541 {
1542     int rc = pcmk_ok;
1543     xmlNode *rsc_xml = get_xpath_object("//" PCMK__XE_LRMD_RSC, request,
1544                                         LOG_ERR);
1545     const char *rsc_id = crm_element_value(rsc_xml, PCMK__XA_LRMD_RSC_ID);
1546     xmlNode *reply = NULL;
1547     lrmd_rsc_t *rsc = NULL;
1548 
1549     if (rsc_id == NULL) {
1550         rc = -ENODEV;
1551     } else {
1552         rsc = g_hash_table_lookup(rsc_list, rsc_id);
1553         if (rsc == NULL) {
1554             crm_info("Agent information for '%s' not in cache", rsc_id);
1555             rc = -ENODEV;
1556         }
1557     }
1558 
1559     reply = create_lrmd_reply(__func__, rc, call_id);
1560     if (rsc) {
1561         crm_xml_add(reply, PCMK__XA_LRMD_RSC_ID, rsc->rsc_id);
1562         crm_xml_add(reply, PCMK__XA_LRMD_CLASS, rsc->class);
1563         crm_xml_add(reply, PCMK__XA_LRMD_PROVIDER, rsc->provider);
1564         crm_xml_add(reply, PCMK__XA_LRMD_TYPE, rsc->type);
1565     }
1566     return reply;
1567 }
1568 
1569 static int
1570 process_lrmd_rsc_unregister(pcmk__client_t *client, uint32_t id,
     /* [previous][next][first][last][top][bottom][index][help] */
1571                             xmlNode *request)
1572 {
1573     int rc = pcmk_ok;
1574     lrmd_rsc_t *rsc = NULL;
1575     xmlNode *rsc_xml = get_xpath_object("//" PCMK__XE_LRMD_RSC, request,
1576                                         LOG_ERR);
1577     const char *rsc_id = crm_element_value(rsc_xml, PCMK__XA_LRMD_RSC_ID);
1578 
1579     if (!rsc_id) {
1580         return -ENODEV;
1581     }
1582 
1583     rsc = g_hash_table_lookup(rsc_list, rsc_id);
1584     if (rsc == NULL) {
1585         crm_info("Ignoring unregistration of resource '%s', which is not registered",
1586                  rsc_id);
1587         return pcmk_ok;
1588     }
1589 
1590     if (rsc->active) {
1591         /* let the caller know there are still active ops on this rsc to watch for */
1592         crm_trace("Operation (%p) still in progress for unregistered resource %s",
1593                   rsc->active, rsc_id);
1594         rc = -EINPROGRESS;
1595     }
1596 
1597     g_hash_table_remove(rsc_list, rsc_id);
1598 
1599     return rc;
1600 }
1601 
1602 static int
1603 process_lrmd_rsc_exec(pcmk__client_t *client, uint32_t id, xmlNode *request)
     /* [previous][next][first][last][top][bottom][index][help] */
1604 {
1605     lrmd_rsc_t *rsc = NULL;
1606     lrmd_cmd_t *cmd = NULL;
1607     xmlNode *rsc_xml = get_xpath_object("//" PCMK__XE_LRMD_RSC, request,
1608                                         LOG_ERR);
1609     const char *rsc_id = crm_element_value(rsc_xml, PCMK__XA_LRMD_RSC_ID);
1610     int call_id;
1611 
1612     if (!rsc_id) {
1613         return -EINVAL;
1614     }
1615     if (!(rsc = g_hash_table_lookup(rsc_list, rsc_id))) {
1616         crm_info("Resource '%s' not found (%d active resources)",
1617                  rsc_id, g_hash_table_size(rsc_list));
1618         return -ENODEV;
1619     }
1620 
1621     cmd = create_lrmd_cmd(request, client);
1622     call_id = cmd->call_id;
1623 
1624     /* Don't reference cmd after handing it off to be scheduled.
1625      * The cmd could get merged and freed. */
1626     schedule_lrmd_cmd(rsc, cmd);
1627 
1628     return call_id;
1629 }
1630 
1631 static int
1632 cancel_op(const char *rsc_id, const char *action, guint interval_ms)
     /* [previous][next][first][last][top][bottom][index][help] */
1633 {
1634     GList *gIter = NULL;
1635     lrmd_rsc_t *rsc = g_hash_table_lookup(rsc_list, rsc_id);
1636 
1637     /* How to cancel an action.
1638      * 1. Check pending ops list, if it hasn't been handed off
1639      *    to the service library or stonith recurring list remove
1640      *    it there and that will stop it.
1641      * 2. If it isn't in the pending ops list, then it's either a
1642      *    recurring op in the stonith recurring list, or the service
1643      *    library's recurring list.  Stop it there
1644      * 3. If not found in any lists, then this operation has either
1645      *    been executed already and is not a recurring operation, or
1646      *    never existed.
1647      */
1648     if (!rsc) {
1649         return -ENODEV;
1650     }
1651 
1652     for (gIter = rsc->pending_ops; gIter != NULL; gIter = gIter->next) {
1653         lrmd_cmd_t *cmd = gIter->data;
1654 
1655         if (action_matches(cmd, action, interval_ms)) {
1656             cmd->result.execution_status = PCMK_EXEC_CANCELLED;
1657             cmd_finalize(cmd, rsc);
1658             return pcmk_ok;
1659         }
1660     }
1661 
1662     if (pcmk__str_eq(rsc->class, PCMK_RESOURCE_CLASS_STONITH, pcmk__str_casei)) {
1663         /* The service library does not handle stonith operations.
1664          * We have to handle recurring stonith operations ourselves. */
1665         for (gIter = rsc->recurring_ops; gIter != NULL; gIter = gIter->next) {
1666             lrmd_cmd_t *cmd = gIter->data;
1667 
1668             if (action_matches(cmd, action, interval_ms)) {
1669                 cmd->result.execution_status = PCMK_EXEC_CANCELLED;
1670                 if (rsc->active != cmd) {
1671                     cmd_finalize(cmd, rsc);
1672                 }
1673                 return pcmk_ok;
1674             }
1675         }
1676     } else if (services_action_cancel(rsc_id,
1677                                       normalize_action_name(rsc, action),
1678                                       interval_ms) == TRUE) {
1679         /* The service library will tell the action_complete callback function
1680          * this action was cancelled, which will destroy the cmd and remove
1681          * it from the recurring_op list. Do not do that in this function
1682          * if the service library says it cancelled it. */
1683         return pcmk_ok;
1684     }
1685 
1686     return -EOPNOTSUPP;
1687 }
1688 
1689 static void
1690 cancel_all_recurring(lrmd_rsc_t * rsc, const char *client_id)
     /* [previous][next][first][last][top][bottom][index][help] */
1691 {
1692     GList *cmd_list = NULL;
1693     GList *cmd_iter = NULL;
1694 
1695     /* Notice a copy of each list is created when concat is called.
1696      * This prevents odd behavior from occurring when the cmd_list
1697      * is iterated through later on.  It is possible the cancel_op
1698      * function may end up modifying the recurring_ops and pending_ops
1699      * lists.  If we did not copy those lists, our cmd_list iteration
1700      * could get messed up.*/
1701     if (rsc->recurring_ops) {
1702         cmd_list = g_list_concat(cmd_list, g_list_copy(rsc->recurring_ops));
1703     }
1704     if (rsc->pending_ops) {
1705         cmd_list = g_list_concat(cmd_list, g_list_copy(rsc->pending_ops));
1706     }
1707     if (!cmd_list) {
1708         return;
1709     }
1710 
1711     for (cmd_iter = cmd_list; cmd_iter; cmd_iter = cmd_iter->next) {
1712         lrmd_cmd_t *cmd = cmd_iter->data;
1713 
1714         if (cmd->interval_ms == 0) {
1715             continue;
1716         }
1717 
1718         if (client_id && !pcmk__str_eq(cmd->client_id, client_id, pcmk__str_casei)) {
1719             continue;
1720         }
1721 
1722         cancel_op(rsc->rsc_id, cmd->action, cmd->interval_ms);
1723     }
1724     /* frees only the copied list data, not the cmds */
1725     g_list_free(cmd_list);
1726 }
1727 
1728 static int
1729 process_lrmd_rsc_cancel(pcmk__client_t *client, uint32_t id, xmlNode *request)
     /* [previous][next][first][last][top][bottom][index][help] */
1730 {
1731     xmlNode *rsc_xml = get_xpath_object("//" PCMK__XE_LRMD_RSC, request,
1732                                         LOG_ERR);
1733     const char *rsc_id = crm_element_value(rsc_xml, PCMK__XA_LRMD_RSC_ID);
1734     const char *action = crm_element_value(rsc_xml, PCMK__XA_LRMD_RSC_ACTION);
1735     guint interval_ms = 0;
1736 
1737     crm_element_value_ms(rsc_xml, PCMK__XA_LRMD_RSC_INTERVAL, &interval_ms);
1738 
1739     if (!rsc_id || !action) {
1740         return -EINVAL;
1741     }
1742 
1743     return cancel_op(rsc_id, action, interval_ms);
1744 }
1745 
1746 static void
1747 add_recurring_op_xml(xmlNode *reply, lrmd_rsc_t *rsc)
     /* [previous][next][first][last][top][bottom][index][help] */
1748 {
1749     xmlNode *rsc_xml = pcmk__xe_create(reply, PCMK__XE_LRMD_RSC);
1750 
1751     crm_xml_add(rsc_xml, PCMK__XA_LRMD_RSC_ID, rsc->rsc_id);
1752     for (GList *item = rsc->recurring_ops; item != NULL; item = item->next) {
1753         lrmd_cmd_t *cmd = item->data;
1754         xmlNode *op_xml = pcmk__xe_create(rsc_xml, PCMK__XE_LRMD_RSC_OP);
1755 
1756         crm_xml_add(op_xml, PCMK__XA_LRMD_RSC_ACTION,
1757                     pcmk__s(cmd->real_action, cmd->action));
1758         crm_xml_add_ms(op_xml, PCMK__XA_LRMD_RSC_INTERVAL, cmd->interval_ms);
1759         crm_xml_add_int(op_xml, PCMK__XA_LRMD_TIMEOUT, cmd->timeout_orig);
1760     }
1761 }
1762 
1763 static xmlNode *
1764 process_lrmd_get_recurring(xmlNode *request, int call_id)
     /* [previous][next][first][last][top][bottom][index][help] */
1765 {
1766     int rc = pcmk_ok;
1767     const char *rsc_id = NULL;
1768     lrmd_rsc_t *rsc = NULL;
1769     xmlNode *reply = NULL;
1770     xmlNode *rsc_xml = NULL;
1771 
1772     // Resource ID is optional
1773     rsc_xml = pcmk__xe_first_child(request, PCMK__XE_LRMD_CALLDATA, NULL, NULL);
1774     if (rsc_xml) {
1775         rsc_xml = pcmk__xe_first_child(rsc_xml, PCMK__XE_LRMD_RSC, NULL, NULL);
1776     }
1777     if (rsc_xml) {
1778         rsc_id = crm_element_value(rsc_xml, PCMK__XA_LRMD_RSC_ID);
1779     }
1780 
1781     // If resource ID is specified, resource must exist
1782     if (rsc_id != NULL) {
1783         rsc = g_hash_table_lookup(rsc_list, rsc_id);
1784         if (rsc == NULL) {
1785             crm_info("Resource '%s' not found (%d active resources)",
1786                      rsc_id, g_hash_table_size(rsc_list));
1787             rc = -ENODEV;
1788         }
1789     }
1790 
1791     reply = create_lrmd_reply(__func__, rc, call_id);
1792 
1793     // If resource ID is not specified, check all resources
1794     if (rsc_id == NULL) {
1795         GHashTableIter iter;
1796         char *key = NULL;
1797 
1798         g_hash_table_iter_init(&iter, rsc_list);
1799         while (g_hash_table_iter_next(&iter, (gpointer *) &key,
1800                                       (gpointer *) &rsc)) {
1801             add_recurring_op_xml(reply, rsc);
1802         }
1803     } else if (rsc) {
1804         add_recurring_op_xml(reply, rsc);
1805     }
1806     return reply;
1807 }
1808 
1809 void
1810 process_lrmd_message(pcmk__client_t *client, uint32_t id, xmlNode *request)
     /* [previous][next][first][last][top][bottom][index][help] */
1811 {
1812     int rc = pcmk_ok;
1813     int call_id = 0;
1814     const char *op = crm_element_value(request, PCMK__XA_LRMD_OP);
1815     int do_reply = 0;
1816     int do_notify = 0;
1817     xmlNode *reply = NULL;
1818 
1819     /* Certain IPC commands may be done only by privileged users (i.e. root or
1820      * hacluster), because they would otherwise provide a means of bypassing
1821      * ACLs.
1822      */
1823     bool allowed = pcmk_is_set(client->flags, pcmk__client_privileged);
1824 
1825     crm_trace("Processing %s operation from %s", op, client->id);
1826     crm_element_value_int(request, PCMK__XA_LRMD_CALLID, &call_id);
1827 
1828     if (pcmk__str_eq(op, CRM_OP_IPC_FWD, pcmk__str_none)) {
1829 #ifdef PCMK__COMPILE_REMOTE
1830         if (allowed) {
1831             ipc_proxy_forward_client(client, request);
1832         } else {
1833             rc = -EACCES;
1834         }
1835 #else
1836         rc = -EPROTONOSUPPORT;
1837 #endif
1838         do_reply = 1;
1839     } else if (pcmk__str_eq(op, CRM_OP_REGISTER, pcmk__str_none)) {
1840         rc = process_lrmd_signon(client, request, call_id, &reply);
1841         do_reply = 1;
1842     } else if (pcmk__str_eq(op, LRMD_OP_RSC_REG, pcmk__str_none)) {
1843         if (allowed) {
1844             rc = process_lrmd_rsc_register(client, id, request);
1845             do_notify = 1;
1846         } else {
1847             rc = -EACCES;
1848         }
1849         do_reply = 1;
1850     } else if (pcmk__str_eq(op, LRMD_OP_RSC_INFO, pcmk__str_none)) {
1851         if (allowed) {
1852             reply = process_lrmd_get_rsc_info(request, call_id);
1853         } else {
1854             rc = -EACCES;
1855         }
1856         do_reply = 1;
1857     } else if (pcmk__str_eq(op, LRMD_OP_RSC_UNREG, pcmk__str_none)) {
1858         if (allowed) {
1859             rc = process_lrmd_rsc_unregister(client, id, request);
1860             /* don't notify anyone about failed un-registers */
1861             if (rc == pcmk_ok || rc == -EINPROGRESS) {
1862                 do_notify = 1;
1863             }
1864         } else {
1865             rc = -EACCES;
1866         }
1867         do_reply = 1;
1868     } else if (pcmk__str_eq(op, LRMD_OP_RSC_EXEC, pcmk__str_none)) {
1869         if (allowed) {
1870             rc = process_lrmd_rsc_exec(client, id, request);
1871         } else {
1872             rc = -EACCES;
1873         }
1874         do_reply = 1;
1875     } else if (pcmk__str_eq(op, LRMD_OP_RSC_CANCEL, pcmk__str_none)) {
1876         if (allowed) {
1877             rc = process_lrmd_rsc_cancel(client, id, request);
1878         } else {
1879             rc = -EACCES;
1880         }
1881         do_reply = 1;
1882     } else if (pcmk__str_eq(op, LRMD_OP_POKE, pcmk__str_none)) {
1883         do_notify = 1;
1884         do_reply = 1;
1885     } else if (pcmk__str_eq(op, LRMD_OP_CHECK, pcmk__str_none)) {
1886         if (allowed) {
1887             xmlNode *wrapper = pcmk__xe_first_child(request,
1888                                                     PCMK__XE_LRMD_CALLDATA,
1889                                                     NULL, NULL);
1890             xmlNode *data = pcmk__xe_first_child(wrapper, NULL, NULL, NULL);
1891 
1892             const char *timeout = NULL;
1893 
1894             CRM_LOG_ASSERT(data != NULL);
1895             timeout = crm_element_value(data, PCMK__XA_LRMD_WATCHDOG);
1896             pcmk__valid_stonith_watchdog_timeout(timeout);
1897         } else {
1898             rc = -EACCES;
1899         }
1900     } else if (pcmk__str_eq(op, LRMD_OP_ALERT_EXEC, pcmk__str_none)) {
1901         if (allowed) {
1902             rc = process_lrmd_alert_exec(client, id, request);
1903         } else {
1904             rc = -EACCES;
1905         }
1906         do_reply = 1;
1907     } else if (pcmk__str_eq(op, LRMD_OP_GET_RECURRING, pcmk__str_none)) {
1908         if (allowed) {
1909             reply = process_lrmd_get_recurring(request, call_id);
1910         } else {
1911             rc = -EACCES;
1912         }
1913         do_reply = 1;
1914     } else {
1915         rc = -EOPNOTSUPP;
1916         do_reply = 1;
1917         crm_err("Unknown IPC request '%s' from client %s",
1918                 op, pcmk__client_name(client));
1919     }
1920 
1921     if (rc == -EACCES) {
1922         crm_warn("Rejecting IPC request '%s' from unprivileged client %s",
1923                  op, pcmk__client_name(client));
1924     }
1925 
1926     crm_debug("Processed %s operation from %s: rc=%d, reply=%d, notify=%d",
1927               op, client->id, rc, do_reply, do_notify);
1928 
1929     if (do_reply) {
1930         int send_rc = pcmk_rc_ok;
1931 
1932         if (reply == NULL) {
1933             reply = create_lrmd_reply(__func__, rc, call_id);
1934         }
1935         send_rc = lrmd_server_send_reply(client, id, reply);
1936         pcmk__xml_free(reply);
1937         if (send_rc != pcmk_rc_ok) {
1938             crm_warn("Reply to client %s failed: %s " QB_XS " rc=%d",
1939                      pcmk__client_name(client), pcmk_rc_str(send_rc), send_rc);
1940         }
1941     }
1942 
1943     if (do_notify) {
1944         send_generic_notify(rc, request);
1945     }
1946 }

/* [previous][next][first][last][top][bottom][index][help] */