This source file includes following definitions.
- is_action_required
- get_action_delay_max
- get_action_delay_base
- get_action_timeout
- cmd_device
- fenced_device_reboot_action
- fenced_device_supports_on
- free_async_command
- create_async_command
- get_action_limit
- get_active_cmds
- fork_cb
- get_agent_metadata_cb
- report_internal_result
- stonith_device_execute
- stonith_device_dispatch
- start_delay_helper
- schedule_stonith_command
- free_device
- free_device_list
- init_device_list
- build_port_aliases
- free_metadata_cache
- init_metadata_cache
- get_agent_metadata
- is_nodeid_required
- read_action_metadata
- map_action
- xml2device_params
- target_list_type
- build_device_from_xml
- schedule_internal_command
- status_search_cb
- dynamic_list_search_cb
- device_params_diff
- device_has_duplicate
- stonith_device_register
- stonith_device_remove
- count_active_levels
- free_topology_entry
- free_topology_list
- init_topology_list
- stonith_level_key
- unpack_level_kind
- parse_device_list
- unpack_level_request
- fenced_register_level
- fenced_unregister_level
- list_to_string
- execute_agent_action
- search_devices_record_result
- localhost_is_eligible
- localhost_is_eligible_with_remap
- can_fence_host_with_device
- search_devices
- get_capable_devices
- add_action_specific_attributes
- add_disallowed
- add_action_reply
- stonith_send_reply
- stonith_query_capable_device_cb
- log_async_result
- send_async_reply
- cancel_stonith_command
- reply_to_duplicates
- next_required_device
- st_child_done
- sort_device_priority
- stonith_fence_get_devices_cb
- fence_locally
- fenced_construct_reply
- construct_async_reply
- fencing_peer_active
- set_fencing_completed
- check_alternate_host
- remove_relay_op
- is_privileged
- handle_register_request
- handle_agent_request
- handle_update_timeout_request
- handle_query_request
- handle_notify_request
- handle_relay_request
- handle_fence_request
- handle_history_request
- handle_device_add_request
- handle_device_delete_request
- handle_level_add_request
- handle_level_delete_request
- handle_cache_request
- handle_unknown_request
- fenced_register_handlers
- fenced_unregister_handlers
- handle_request
- handle_reply
- stonith_command
1
2
3
4
5
6
7
8
9
10 #include <crm_internal.h>
11
12 #include <sys/param.h>
13 #include <stdio.h>
14 #include <sys/types.h>
15 #include <sys/wait.h>
16 #include <sys/stat.h>
17 #include <unistd.h>
18 #include <sys/utsname.h>
19
20 #include <stdlib.h>
21 #include <errno.h>
22 #include <fcntl.h>
23 #include <ctype.h>
24
25 #include <crm/crm.h>
26 #include <crm/common/ipc.h>
27 #include <crm/common/ipc_internal.h>
28 #include <crm/cluster/internal.h>
29 #include <crm/common/mainloop.h>
30
31 #include <crm/stonith-ng.h>
32 #include <crm/fencing/internal.h>
33 #include <crm/common/xml.h>
34
35 #include <pacemaker-fenced.h>
36
37 GHashTable *device_list = NULL;
38 GHashTable *topology = NULL;
39 static GList *cmd_list = NULL;
40
41 static GHashTable *fenced_handlers = NULL;
42
43 struct device_search_s {
44
45 char *host;
46
47 char *action;
48
49 int per_device_timeout;
50
51 int replies_needed;
52
53 int replies_received;
54
55 bool allow_self;
56
57
58 void *user_data;
59
60 void (*callback) (GList * devices, void *user_data);
61
62 GList *capable;
63
64 uint32_t support_action_only;
65 };
66
67 static gboolean stonith_device_dispatch(gpointer user_data);
68 static void st_child_done(int pid, const pcmk__action_result_t *result,
69 void *user_data);
70
71 static void search_devices_record_result(struct device_search_s *search, const char *device,
72 gboolean can_fence);
73
74 static int get_agent_metadata(const char *agent, xmlNode **metadata);
75 static void read_action_metadata(stonith_device_t *device);
76 static enum fenced_target_by unpack_level_kind(const xmlNode *level);
77
78 typedef struct async_command_s {
79
80 int id;
81 int pid;
82 int fd_stdout;
83 uint32_t options;
84 int default_timeout;
85 int timeout;
86
87 int start_delay;
88 int delay_id;
89
90 char *op;
91 char *origin;
92 char *client;
93 char *client_name;
94 char *remote_op_id;
95
96 char *target;
97 uint32_t target_nodeid;
98 char *action;
99 char *device;
100
101 GList *device_list;
102 GList *next_device_iter;
103
104 void *internal_user_data;
105 void (*done_cb) (int pid, const pcmk__action_result_t *result,
106 void *user_data);
107 guint timer_sigterm;
108 guint timer_sigkill;
109
110
111 int last_timeout_signo;
112
113 stonith_device_t *active_on;
114 stonith_device_t *activating_on;
115 } async_command_t;
116
117 static xmlNode *construct_async_reply(const async_command_t *cmd,
118 const pcmk__action_result_t *result);
119
120 static gboolean
121 is_action_required(const char *action, const stonith_device_t *device)
122 {
123 return (device != NULL) && device->automatic_unfencing
124 && pcmk__str_eq(action, PCMK_ACTION_ON, pcmk__str_none);
125 }
126
127 static int
128 get_action_delay_max(const stonith_device_t *device, const char *action)
129 {
130 const char *value = NULL;
131 guint delay_max = 0U;
132
133 if (!pcmk__is_fencing_action(action)) {
134 return 0;
135 }
136
137 value = g_hash_table_lookup(device->params, PCMK_STONITH_DELAY_MAX);
138 if (value) {
139 pcmk_parse_interval_spec(value, &delay_max);
140 delay_max /= 1000;
141 }
142
143 return (int) delay_max;
144 }
145
146 static int
147 get_action_delay_base(const stonith_device_t *device, const char *action,
148 const char *target)
149 {
150 char *hash_value = NULL;
151 guint delay_base = 0U;
152
153 if (!pcmk__is_fencing_action(action)) {
154 return 0;
155 }
156
157 hash_value = g_hash_table_lookup(device->params, PCMK_STONITH_DELAY_BASE);
158
159 if (hash_value) {
160 char *value = pcmk__str_copy(hash_value);
161 char *valptr = value;
162
163 if (target != NULL) {
164 for (char *val = strtok(value, "; \t"); val != NULL; val = strtok(NULL, "; \t")) {
165 char *mapval = strchr(val, ':');
166
167 if (mapval == NULL || mapval[1] == 0) {
168 crm_err("pcmk_delay_base: empty value in mapping", val);
169 continue;
170 }
171
172 if (mapval != val && strncasecmp(target, val, (size_t)(mapval - val)) == 0) {
173 value = mapval + 1;
174 crm_debug("pcmk_delay_base mapped to %s for %s",
175 value, target);
176 break;
177 }
178 }
179 }
180
181 if (strchr(value, ':') == 0) {
182 pcmk_parse_interval_spec(value, &delay_base);
183 delay_base /= 1000;
184 }
185
186 free(valptr);
187 }
188
189 return (int) delay_base;
190 }
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212 static int
213 get_action_timeout(const stonith_device_t *device, const char *action,
214 int default_timeout)
215 {
216 if (action && device && device->params) {
217 char buffer[64] = { 0, };
218 const char *value = NULL;
219
220
221
222
223 if (pcmk__str_eq(action, PCMK_ACTION_REBOOT, pcmk__str_none)
224 && !pcmk_is_set(device->flags, st_device_supports_reboot)) {
225 crm_trace("%s doesn't support reboot, using timeout for off instead",
226 device->id);
227 action = PCMK_ACTION_OFF;
228 }
229
230
231 snprintf(buffer, sizeof(buffer), "pcmk_%s_timeout", action);
232 value = g_hash_table_lookup(device->params, buffer);
233 if (value) {
234 long long timeout_ms = crm_get_msec(value);
235 return (int) QB_MIN(timeout_ms / 1000, INT_MAX);
236 }
237 }
238 return default_timeout;
239 }
240
241
242
243
244
245
246
247
248
249 static stonith_device_t *
250 cmd_device(const async_command_t *cmd)
251 {
252 if ((cmd == NULL) || (cmd->device == NULL) || (device_list == NULL)) {
253 return NULL;
254 }
255 return g_hash_table_lookup(device_list, cmd->device);
256 }
257
258
259
260
261
262
263
264
265
266 const char *
267 fenced_device_reboot_action(const char *device_id)
268 {
269 const char *action = NULL;
270
271 if ((device_list != NULL) && (device_id != NULL)) {
272 stonith_device_t *device = g_hash_table_lookup(device_list, device_id);
273
274 if ((device != NULL) && (device->params != NULL)) {
275 action = g_hash_table_lookup(device->params, "pcmk_reboot_action");
276 }
277 }
278 return pcmk__s(action, PCMK_ACTION_REBOOT);
279 }
280
281
282
283
284
285
286
287
288
289 bool
290 fenced_device_supports_on(const char *device_id)
291 {
292 if ((device_list != NULL) && (device_id != NULL)) {
293 stonith_device_t *device = g_hash_table_lookup(device_list, device_id);
294
295 if (device != NULL) {
296 return pcmk_is_set(device->flags, st_device_supports_on);
297 }
298 }
299 return false;
300 }
301
302 static void
303 free_async_command(async_command_t * cmd)
304 {
305 if (!cmd) {
306 return;
307 }
308
309 if (cmd->delay_id) {
310 g_source_remove(cmd->delay_id);
311 }
312
313 cmd_list = g_list_remove(cmd_list, cmd);
314
315 g_list_free_full(cmd->device_list, free);
316 free(cmd->device);
317 free(cmd->action);
318 free(cmd->target);
319 free(cmd->remote_op_id);
320 free(cmd->client);
321 free(cmd->client_name);
322 free(cmd->origin);
323 free(cmd->op);
324 free(cmd);
325 }
326
327
328
329
330
331
332
333
334
335
336
337
338 static async_command_t *
339 create_async_command(xmlNode *msg)
340 {
341 xmlNode *op = NULL;
342 async_command_t *cmd = NULL;
343 int rc = pcmk_rc_ok;
344
345 if (msg == NULL) {
346 return NULL;
347 }
348
349 op = get_xpath_object("//@" PCMK__XE_ST_DEVICE_ACTION, msg, LOG_ERR);
350 if (op == NULL) {
351 return NULL;
352 }
353
354 cmd = pcmk__assert_alloc(1, sizeof(async_command_t));
355
356
357 cmd->action = crm_element_value_copy(op, PCMK__XA_ST_DEVICE_ACTION);
358 cmd->op = crm_element_value_copy(msg, PCMK__XA_ST_OP);
359 cmd->client = crm_element_value_copy(msg, PCMK__XA_ST_CLIENTID);
360 if ((cmd->action == NULL) || (cmd->op == NULL) || (cmd->client == NULL)) {
361 free_async_command(cmd);
362 return NULL;
363 }
364
365 crm_element_value_int(msg, PCMK__XA_ST_CALLID, &(cmd->id));
366 crm_element_value_int(msg, PCMK__XA_ST_DELAY, &(cmd->start_delay));
367 crm_element_value_int(msg, PCMK__XA_ST_TIMEOUT, &(cmd->default_timeout));
368 cmd->timeout = cmd->default_timeout;
369
370 rc = pcmk__xe_get_flags(msg, PCMK__XA_ST_CALLOPT, &(cmd->options),
371 st_opt_none);
372 if (rc != pcmk_rc_ok) {
373 crm_warn("Couldn't parse options from request: %s", pcmk_rc_str(rc));
374 }
375
376 cmd->origin = crm_element_value_copy(msg, PCMK__XA_SRC);
377 cmd->remote_op_id = crm_element_value_copy(msg, PCMK__XA_ST_REMOTE_OP);
378 cmd->client_name = crm_element_value_copy(msg, PCMK__XA_ST_CLIENTNAME);
379 cmd->target = crm_element_value_copy(op, PCMK__XA_ST_TARGET);
380 cmd->device = crm_element_value_copy(op, PCMK__XA_ST_DEVICE_ID);
381
382 cmd->done_cb = st_child_done;
383
384
385 cmd_list = g_list_append(cmd_list, cmd);
386
387 return cmd;
388 }
389
390 static int
391 get_action_limit(stonith_device_t * device)
392 {
393 const char *value = NULL;
394 int action_limit = 1;
395
396 value = g_hash_table_lookup(device->params, PCMK_STONITH_ACTION_LIMIT);
397 if ((value == NULL)
398 || (pcmk__scan_min_int(value, &action_limit, INT_MIN) != pcmk_rc_ok)
399 || (action_limit == 0)) {
400 action_limit = 1;
401 }
402 return action_limit;
403 }
404
405 static int
406 get_active_cmds(stonith_device_t * device)
407 {
408 int counter = 0;
409 GList *gIter = NULL;
410 GList *gIterNext = NULL;
411
412 CRM_CHECK(device != NULL, return 0);
413
414 for (gIter = cmd_list; gIter != NULL; gIter = gIterNext) {
415 async_command_t *cmd = gIter->data;
416
417 gIterNext = gIter->next;
418
419 if (cmd->active_on == device) {
420 counter++;
421 }
422 }
423
424 return counter;
425 }
426
427 static void
428 fork_cb(int pid, void *user_data)
429 {
430 async_command_t *cmd = (async_command_t *) user_data;
431 stonith_device_t * device =
432
433
434
435 cmd->activating_on?cmd->activating_on:cmd->active_on;
436
437 pcmk__assert(device != NULL);
438 crm_debug("Operation '%s' [%d]%s%s using %s now running with %ds timeout",
439 cmd->action, pid,
440 ((cmd->target == NULL)? "" : " targeting "),
441 pcmk__s(cmd->target, ""), device->id, cmd->timeout);
442 cmd->active_on = device;
443 cmd->activating_on = NULL;
444 }
445
446 static int
447 get_agent_metadata_cb(gpointer data) {
448 stonith_device_t *device = data;
449 guint period_ms;
450
451 switch (get_agent_metadata(device->agent, &device->agent_metadata)) {
452 case pcmk_rc_ok:
453 if (device->agent_metadata) {
454 read_action_metadata(device);
455 stonith__device_parameter_flags(&(device->flags), device->id,
456 device->agent_metadata);
457 }
458 return G_SOURCE_REMOVE;
459
460 case EAGAIN:
461 period_ms = pcmk__mainloop_timer_get_period(device->timer);
462 if (period_ms < 160 * 1000) {
463 mainloop_timer_set_period(device->timer, 2 * period_ms);
464 }
465 return G_SOURCE_CONTINUE;
466
467 default:
468 return G_SOURCE_REMOVE;
469 }
470 }
471
472
473
474
475
476
477
478
479
480
481 static void
482 report_internal_result(async_command_t *cmd, int exit_status,
483 int execution_status, const char *exit_reason)
484 {
485 pcmk__action_result_t result = PCMK__UNKNOWN_RESULT;
486
487 pcmk__set_result(&result, exit_status, execution_status, exit_reason);
488 cmd->done_cb(0, &result, cmd);
489 pcmk__reset_result(&result);
490 }
491
492 static gboolean
493 stonith_device_execute(stonith_device_t * device)
494 {
495 int exec_rc = 0;
496 const char *action_str = NULL;
497 const char *host_arg = NULL;
498 async_command_t *cmd = NULL;
499 stonith_action_t *action = NULL;
500 int active_cmds = 0;
501 int action_limit = 0;
502 GList *gIter = NULL;
503 GList *gIterNext = NULL;
504
505 CRM_CHECK(device != NULL, return FALSE);
506
507 active_cmds = get_active_cmds(device);
508 action_limit = get_action_limit(device);
509 if (action_limit > -1 && active_cmds >= action_limit) {
510 crm_trace("%s is over its action limit of %d (%u active action%s)",
511 device->id, action_limit, active_cmds,
512 pcmk__plural_s(active_cmds));
513 return TRUE;
514 }
515
516 for (gIter = device->pending_ops; gIter != NULL; gIter = gIterNext) {
517 async_command_t *pending_op = gIter->data;
518
519 gIterNext = gIter->next;
520
521 if (pending_op && pending_op->delay_id) {
522 crm_trace("Operation '%s'%s%s using %s was asked to run too early, "
523 "waiting for start delay of %ds",
524 pending_op->action,
525 ((pending_op->target == NULL)? "" : " targeting "),
526 pcmk__s(pending_op->target, ""),
527 device->id, pending_op->start_delay);
528 continue;
529 }
530
531 device->pending_ops = g_list_remove_link(device->pending_ops, gIter);
532 g_list_free_1(gIter);
533
534 cmd = pending_op;
535 break;
536 }
537
538 if (cmd == NULL) {
539 crm_trace("No actions using %s are needed", device->id);
540 return TRUE;
541 }
542
543 if (pcmk__str_any_of(device->agent, STONITH_WATCHDOG_AGENT,
544 STONITH_WATCHDOG_AGENT_INTERNAL, NULL)) {
545 if (pcmk__is_fencing_action(cmd->action)) {
546 if (node_does_watchdog_fencing(stonith_our_uname)) {
547 pcmk__panic(__func__);
548 goto done;
549 }
550 } else {
551 crm_info("Faking success for %s watchdog operation", cmd->action);
552 report_internal_result(cmd, CRM_EX_OK, PCMK_EXEC_DONE, NULL);
553 goto done;
554 }
555 }
556
557 #if SUPPORT_CIBSECRETS
558 exec_rc = pcmk__substitute_secrets(device->id, device->params);
559 if (exec_rc != pcmk_rc_ok) {
560 if (pcmk__str_eq(cmd->action, PCMK_ACTION_STOP, pcmk__str_none)) {
561 crm_info("Proceeding with stop operation for %s "
562 "despite being unable to load CIB secrets (%s)",
563 device->id, pcmk_rc_str(exec_rc));
564 } else {
565 crm_err("Considering %s unconfigured "
566 "because unable to load CIB secrets: %s",
567 device->id, pcmk_rc_str(exec_rc));
568 report_internal_result(cmd, CRM_EX_ERROR, PCMK_EXEC_NO_SECRETS,
569 "Failed to get CIB secrets");
570 goto done;
571 }
572 }
573 #endif
574
575 action_str = cmd->action;
576 if (pcmk__str_eq(cmd->action, PCMK_ACTION_REBOOT, pcmk__str_none)
577 && !pcmk_is_set(device->flags, st_device_supports_reboot)) {
578
579 crm_notice("Remapping 'reboot' action%s%s using %s to 'off' "
580 "because agent '%s' does not support reboot",
581 ((cmd->target == NULL)? "" : " targeting "),
582 pcmk__s(cmd->target, ""), device->id, device->agent);
583 action_str = PCMK_ACTION_OFF;
584 }
585
586 if (pcmk_is_set(device->flags, st_device_supports_parameter_port)) {
587 host_arg = "port";
588
589 } else if (pcmk_is_set(device->flags, st_device_supports_parameter_plug)) {
590 host_arg = "plug";
591 }
592
593 action = stonith__action_create(device->agent, action_str, cmd->target,
594 cmd->target_nodeid, cmd->timeout,
595 device->params, device->aliases, host_arg);
596
597
598
599 cmd->activating_on = device;
600 exec_rc = stonith__execute_async(action, (void *)cmd, cmd->done_cb,
601 fork_cb);
602 if (exec_rc < 0) {
603 cmd->activating_on = NULL;
604 cmd->done_cb(0, stonith__action_result(action), cmd);
605 stonith__destroy_action(action);
606 }
607
608 done:
609
610
611
612 if (device->pending_ops) {
613 mainloop_set_trigger(device->work);
614 }
615 return TRUE;
616 }
617
618 static gboolean
619 stonith_device_dispatch(gpointer user_data)
620 {
621 return stonith_device_execute(user_data);
622 }
623
624 static gboolean
625 start_delay_helper(gpointer data)
626 {
627 async_command_t *cmd = data;
628 stonith_device_t *device = cmd_device(cmd);
629
630 cmd->delay_id = 0;
631 if (device) {
632 mainloop_set_trigger(device->work);
633 }
634
635 return FALSE;
636 }
637
638 static void
639 schedule_stonith_command(async_command_t * cmd, stonith_device_t * device)
640 {
641 int delay_max = 0;
642 int delay_base = 0;
643 int requested_delay = cmd->start_delay;
644
645 CRM_CHECK(cmd != NULL, return);
646 CRM_CHECK(device != NULL, return);
647
648 if (cmd->device) {
649 free(cmd->device);
650 }
651
652 if (device->include_nodeid && (cmd->target != NULL)) {
653 crm_node_t *node = pcmk__get_node(0, cmd->target, NULL,
654 pcmk__node_search_cluster_member);
655
656 cmd->target_nodeid = node->id;
657 }
658
659 cmd->device = pcmk__str_copy(device->id);
660 cmd->timeout = get_action_timeout(device, cmd->action, cmd->default_timeout);
661
662 if (cmd->remote_op_id) {
663 crm_debug("Scheduling '%s' action%s%s using %s for remote peer %s "
664 "with op id %.8s and timeout %ds",
665 cmd->action,
666 (cmd->target == NULL)? "" : " targeting ",
667 pcmk__s(cmd->target, ""),
668 device->id, cmd->origin, cmd->remote_op_id, cmd->timeout);
669 } else {
670 crm_debug("Scheduling '%s' action%s%s using %s for %s with timeout %ds",
671 cmd->action,
672 (cmd->target == NULL)? "" : " targeting ",
673 pcmk__s(cmd->target, ""),
674 device->id, cmd->client, cmd->timeout);
675 }
676
677 device->pending_ops = g_list_append(device->pending_ops, cmd);
678 mainloop_set_trigger(device->work);
679
680
681 if (requested_delay < 0) {
682 return;
683 }
684
685 delay_max = get_action_delay_max(device, cmd->action);
686 delay_base = get_action_delay_base(device, cmd->action, cmd->target);
687 if (delay_max == 0) {
688 delay_max = delay_base;
689 }
690 if (delay_max < delay_base) {
691 crm_warn(PCMK_STONITH_DELAY_BASE " (%ds) is larger than "
692 PCMK_STONITH_DELAY_MAX " (%ds) for %s using %s "
693 "(limiting to maximum delay)",
694 delay_base, delay_max, cmd->action, device->id);
695 delay_base = delay_max;
696 }
697 if (delay_max > 0) {
698
699 cmd->start_delay +=
700 ((delay_max != delay_base)?(rand() % (delay_max - delay_base)):0)
701 + delay_base;
702 }
703
704 if (cmd->start_delay > 0) {
705 crm_notice("Delaying '%s' action%s%s using %s for %ds " CRM_XS
706 " timeout=%ds requested_delay=%ds base=%ds max=%ds",
707 cmd->action,
708 (cmd->target == NULL)? "" : " targeting ",
709 pcmk__s(cmd->target, ""),
710 device->id, cmd->start_delay, cmd->timeout,
711 requested_delay, delay_base, delay_max);
712 cmd->delay_id =
713 g_timeout_add_seconds(cmd->start_delay, start_delay_helper, cmd);
714 }
715 }
716
717 static void
718 free_device(gpointer data)
719 {
720 GList *gIter = NULL;
721 stonith_device_t *device = data;
722
723 g_hash_table_destroy(device->params);
724 g_hash_table_destroy(device->aliases);
725
726 for (gIter = device->pending_ops; gIter != NULL; gIter = gIter->next) {
727 async_command_t *cmd = gIter->data;
728
729 crm_warn("Removal of device '%s' purged operation '%s'", device->id, cmd->action);
730 report_internal_result(cmd, CRM_EX_ERROR, PCMK_EXEC_NO_FENCE_DEVICE,
731 "Device was removed before action could be executed");
732 }
733 g_list_free(device->pending_ops);
734
735 g_list_free_full(device->targets, free);
736
737 if (device->timer) {
738 mainloop_timer_stop(device->timer);
739 mainloop_timer_del(device->timer);
740 }
741
742 mainloop_destroy_trigger(device->work);
743
744 free_xml(device->agent_metadata);
745 free(device->namespace);
746 if (device->on_target_actions != NULL) {
747 g_string_free(device->on_target_actions, TRUE);
748 }
749 free(device->agent);
750 free(device->id);
751 free(device);
752 }
753
754 void free_device_list(void)
755 {
756 if (device_list != NULL) {
757 g_hash_table_destroy(device_list);
758 device_list = NULL;
759 }
760 }
761
762 void
763 init_device_list(void)
764 {
765 if (device_list == NULL) {
766 device_list = pcmk__strkey_table(NULL, free_device);
767 }
768 }
769
770 static GHashTable *
771 build_port_aliases(const char *hostmap, GList ** targets)
772 {
773 char *name = NULL;
774 int last = 0, lpc = 0, max = 0, added = 0;
775 GHashTable *aliases = pcmk__strikey_table(free, free);
776
777 if (hostmap == NULL) {
778 return aliases;
779 }
780
781 max = strlen(hostmap);
782 for (; lpc <= max; lpc++) {
783 switch (hostmap[lpc]) {
784
785 case '\\':
786 lpc++;
787 break;
788
789
790 case '=':
791 case ':':
792 if (lpc > last) {
793 free(name);
794 name = pcmk__assert_alloc(1, 1 + lpc - last);
795 memcpy(name, hostmap + last, lpc - last);
796 }
797 last = lpc + 1;
798 break;
799
800
801
802 case 0:
803 case ';':
804 case ' ':
805 case '\t':
806 if (name) {
807 char *value = NULL;
808 int k = 0;
809
810 value = pcmk__assert_alloc(1, 1 + lpc - last);
811 memcpy(value, hostmap + last, lpc - last);
812
813 for (int i = 0; value[i] != '\0'; i++) {
814 if (value[i] != '\\') {
815 value[k++] = value[i];
816 }
817 }
818 value[k] = '\0';
819
820 crm_debug("Adding alias '%s'='%s'", name, value);
821 g_hash_table_replace(aliases, name, value);
822 if (targets) {
823 *targets = g_list_append(*targets, pcmk__str_copy(value));
824 }
825 value = NULL;
826 name = NULL;
827 added++;
828
829 } else if (lpc > last) {
830 crm_debug("Parse error at offset %d near '%s'", lpc - last, hostmap + last);
831 }
832
833 last = lpc + 1;
834 break;
835 }
836
837 if (hostmap[lpc] == 0) {
838 break;
839 }
840 }
841
842 if (added == 0) {
843 crm_info("No host mappings detected in '%s'", hostmap);
844 }
845
846 free(name);
847 return aliases;
848 }
849
850 GHashTable *metadata_cache = NULL;
851
852 void
853 free_metadata_cache(void) {
854 if (metadata_cache != NULL) {
855 g_hash_table_destroy(metadata_cache);
856 metadata_cache = NULL;
857 }
858 }
859
860 static void
861 init_metadata_cache(void) {
862 if (metadata_cache == NULL) {
863 metadata_cache = pcmk__strkey_table(free, free);
864 }
865 }
866
867 int
868 get_agent_metadata(const char *agent, xmlNode ** metadata)
869 {
870 char *buffer = NULL;
871
872 if (metadata == NULL) {
873 return EINVAL;
874 }
875 *metadata = NULL;
876 if (pcmk__str_eq(agent, STONITH_WATCHDOG_AGENT_INTERNAL, pcmk__str_none)) {
877 return pcmk_rc_ok;
878 }
879 init_metadata_cache();
880 buffer = g_hash_table_lookup(metadata_cache, agent);
881 if (buffer == NULL) {
882 stonith_t *st = stonith_api_new();
883 int rc;
884
885 if (st == NULL) {
886 crm_warn("Could not get agent meta-data: "
887 "API memory allocation failed");
888 return EAGAIN;
889 }
890 rc = st->cmds->metadata(st, st_opt_sync_call, agent,
891 NULL, &buffer, 10);
892 stonith_api_delete(st);
893 if (rc || !buffer) {
894 crm_err("Could not retrieve metadata for fencing agent %s", agent);
895 return EAGAIN;
896 }
897 g_hash_table_replace(metadata_cache, pcmk__str_copy(agent), buffer);
898 }
899
900 *metadata = pcmk__xml_parse(buffer);
901 return pcmk_rc_ok;
902 }
903
904 static gboolean
905 is_nodeid_required(xmlNode * xml)
906 {
907 xmlXPathObjectPtr xpath = NULL;
908
909 if (stand_alone) {
910 return FALSE;
911 }
912
913 if (!xml) {
914 return FALSE;
915 }
916
917 xpath = xpath_search(xml,
918 "//" PCMK_XE_PARAMETER "[@" PCMK_XA_NAME "='nodeid']");
919 if (numXpathResults(xpath) <= 0) {
920 freeXpathObject(xpath);
921 return FALSE;
922 }
923
924 freeXpathObject(xpath);
925 return TRUE;
926 }
927
928 static void
929 read_action_metadata(stonith_device_t *device)
930 {
931 xmlXPathObjectPtr xpath = NULL;
932 int max = 0;
933 int lpc = 0;
934
935 if (device->agent_metadata == NULL) {
936 return;
937 }
938
939 xpath = xpath_search(device->agent_metadata, "//action");
940 max = numXpathResults(xpath);
941
942 if (max <= 0) {
943 freeXpathObject(xpath);
944 return;
945 }
946
947 for (lpc = 0; lpc < max; lpc++) {
948 const char *action = NULL;
949 xmlNode *match = getXpathResult(xpath, lpc);
950
951 CRM_LOG_ASSERT(match != NULL);
952 if(match == NULL) { continue; };
953
954 action = crm_element_value(match, PCMK_XA_NAME);
955
956 if (pcmk__str_eq(action, PCMK_ACTION_LIST, pcmk__str_none)) {
957 stonith__set_device_flags(device->flags, device->id,
958 st_device_supports_list);
959 } else if (pcmk__str_eq(action, PCMK_ACTION_STATUS, pcmk__str_none)) {
960 stonith__set_device_flags(device->flags, device->id,
961 st_device_supports_status);
962 } else if (pcmk__str_eq(action, PCMK_ACTION_REBOOT, pcmk__str_none)) {
963 stonith__set_device_flags(device->flags, device->id,
964 st_device_supports_reboot);
965 } else if (pcmk__str_eq(action, PCMK_ACTION_ON, pcmk__str_none)) {
966
967
968
969
970
971
972 if (pcmk__xe_attr_is_true(match, PCMK_XA_AUTOMATIC)
973 || pcmk__xe_attr_is_true(match, PCMK__XA_REQUIRED)) {
974 device->automatic_unfencing = TRUE;
975 }
976 stonith__set_device_flags(device->flags, device->id,
977 st_device_supports_on);
978 }
979
980 if ((action != NULL)
981 && pcmk__xe_attr_is_true(match, PCMK_XA_ON_TARGET)) {
982
983 pcmk__add_word(&(device->on_target_actions), 64, action);
984 }
985 }
986
987 freeXpathObject(xpath);
988 }
989
990
991
992
993
994
995
996
997
998 static void
999 map_action(GHashTable *params, const char *action, const char *value)
1000 {
1001 char *key = crm_strdup_printf("pcmk_%s_action", action);
1002
1003 if (g_hash_table_lookup(params, key)) {
1004 crm_warn("Ignoring %s='%s', see %s instead",
1005 STONITH_ATTR_ACTION_OP, value, key);
1006 free(key);
1007 } else {
1008 crm_warn("Mapping %s='%s' to %s='%s'",
1009 STONITH_ATTR_ACTION_OP, value, key, value);
1010 g_hash_table_insert(params, key, pcmk__str_copy(value));
1011 }
1012 }
1013
1014
1015
1016
1017
1018
1019
1020
1021 static GHashTable *
1022 xml2device_params(const char *name, const xmlNode *dev)
1023 {
1024 GHashTable *params = xml2list(dev);
1025 const char *value;
1026
1027
1028
1029
1030
1031 value = g_hash_table_lookup(params, STONITH_ATTR_ACTION_OP);
1032 if (value != NULL) {
1033 crm_warn("%s has '%s' parameter, which should never be specified in configuration",
1034 name, STONITH_ATTR_ACTION_OP);
1035
1036 if (*value == '\0') {
1037 crm_warn("Ignoring empty '%s' parameter", STONITH_ATTR_ACTION_OP);
1038
1039 } else if (strcmp(value, PCMK_ACTION_REBOOT) == 0) {
1040 crm_warn("Ignoring %s='reboot' (see " PCMK_OPT_STONITH_ACTION
1041 " cluster property instead)",
1042 STONITH_ATTR_ACTION_OP);
1043
1044 } else if (strcmp(value, PCMK_ACTION_OFF) == 0) {
1045 map_action(params, PCMK_ACTION_REBOOT, value);
1046
1047 } else {
1048 map_action(params, PCMK_ACTION_OFF, value);
1049 map_action(params, PCMK_ACTION_REBOOT, value);
1050 }
1051
1052 g_hash_table_remove(params, STONITH_ATTR_ACTION_OP);
1053 }
1054
1055 return params;
1056 }
1057
1058 static const char *
1059 target_list_type(stonith_device_t * dev)
1060 {
1061 const char *check_type = NULL;
1062
1063 check_type = g_hash_table_lookup(dev->params, PCMK_STONITH_HOST_CHECK);
1064
1065 if (check_type == NULL) {
1066
1067 if (g_hash_table_lookup(dev->params, PCMK_STONITH_HOST_LIST)) {
1068 check_type = PCMK_VALUE_STATIC_LIST;
1069 } else if (g_hash_table_lookup(dev->params, PCMK_STONITH_HOST_MAP)) {
1070 check_type = PCMK_VALUE_STATIC_LIST;
1071 } else if (pcmk_is_set(dev->flags, st_device_supports_list)) {
1072 check_type = PCMK_VALUE_DYNAMIC_LIST;
1073 } else if (pcmk_is_set(dev->flags, st_device_supports_status)) {
1074 check_type = PCMK_VALUE_STATUS;
1075 } else {
1076 check_type = PCMK_VALUE_NONE;
1077 }
1078 }
1079
1080 return check_type;
1081 }
1082
1083 static stonith_device_t *
1084 build_device_from_xml(xmlNode *dev)
1085 {
1086 const char *value;
1087 stonith_device_t *device = NULL;
1088 char *agent = crm_element_value_copy(dev, PCMK_XA_AGENT);
1089
1090 CRM_CHECK(agent != NULL, return device);
1091
1092 device = pcmk__assert_alloc(1, sizeof(stonith_device_t));
1093
1094 device->id = crm_element_value_copy(dev, PCMK_XA_ID);
1095 device->agent = agent;
1096 device->namespace = crm_element_value_copy(dev, PCMK__XA_NAMESPACE);
1097 device->params = xml2device_params(device->id, dev);
1098
1099 value = g_hash_table_lookup(device->params, PCMK_STONITH_HOST_LIST);
1100 if (value) {
1101 device->targets = stonith__parse_targets(value);
1102 }
1103
1104 value = g_hash_table_lookup(device->params, PCMK_STONITH_HOST_MAP);
1105 device->aliases = build_port_aliases(value, &(device->targets));
1106
1107 value = target_list_type(device);
1108 if (!pcmk__str_eq(value, PCMK_VALUE_STATIC_LIST, pcmk__str_casei)
1109 && (device->targets != NULL)) {
1110
1111
1112 g_list_free_full(device->targets, free);
1113 device->targets = NULL;
1114 }
1115 switch (get_agent_metadata(device->agent, &device->agent_metadata)) {
1116 case pcmk_rc_ok:
1117 if (device->agent_metadata) {
1118 read_action_metadata(device);
1119 stonith__device_parameter_flags(&(device->flags), device->id,
1120 device->agent_metadata);
1121 }
1122 break;
1123
1124 case EAGAIN:
1125 if (device->timer == NULL) {
1126 device->timer = mainloop_timer_add("get_agent_metadata", 10 * 1000,
1127 TRUE, get_agent_metadata_cb, device);
1128 }
1129 if (!mainloop_timer_running(device->timer)) {
1130 mainloop_timer_start(device->timer);
1131 }
1132 break;
1133
1134 default:
1135 break;
1136 }
1137
1138 value = g_hash_table_lookup(device->params, "nodeid");
1139 if (!value) {
1140 device->include_nodeid = is_nodeid_required(device->agent_metadata);
1141 }
1142
1143 value = crm_element_value(dev, PCMK__XA_RSC_PROVIDES);
1144 if (pcmk__str_eq(value, PCMK_VALUE_UNFENCING, pcmk__str_casei)) {
1145 device->automatic_unfencing = TRUE;
1146 }
1147
1148 if (is_action_required(PCMK_ACTION_ON, device)) {
1149 crm_info("Fencing device '%s' requires unfencing", device->id);
1150 }
1151
1152 if (device->on_target_actions != NULL) {
1153 crm_info("Fencing device '%s' requires actions (%s) to be executed "
1154 "on target", device->id,
1155 (const char *) device->on_target_actions->str);
1156 }
1157
1158 device->work = mainloop_add_trigger(G_PRIORITY_HIGH, stonith_device_dispatch, device);
1159
1160
1161 return device;
1162 }
1163
1164 static void
1165 schedule_internal_command(const char *origin,
1166 stonith_device_t * device,
1167 const char *action,
1168 const char *target,
1169 int timeout,
1170 void *internal_user_data,
1171 void (*done_cb) (int pid,
1172 const pcmk__action_result_t *result,
1173 void *user_data))
1174 {
1175 async_command_t *cmd = NULL;
1176
1177 cmd = pcmk__assert_alloc(1, sizeof(async_command_t));
1178
1179 cmd->id = -1;
1180 cmd->default_timeout = timeout ? timeout : 60;
1181 cmd->timeout = cmd->default_timeout;
1182 cmd->action = pcmk__str_copy(action);
1183 cmd->target = pcmk__str_copy(target);
1184 cmd->device = pcmk__str_copy(device->id);
1185 cmd->origin = pcmk__str_copy(origin);
1186 cmd->client = pcmk__str_copy(crm_system_name);
1187 cmd->client_name = pcmk__str_copy(crm_system_name);
1188
1189 cmd->internal_user_data = internal_user_data;
1190 cmd->done_cb = done_cb;
1191
1192 schedule_stonith_command(cmd, device);
1193 }
1194
1195
1196 enum fence_status_code {
1197 fence_status_invalid = -1,
1198 fence_status_active = 0,
1199 fence_status_unknown = 1,
1200 fence_status_inactive = 2,
1201 };
1202
1203 static void
1204 status_search_cb(int pid, const pcmk__action_result_t *result, void *user_data)
1205 {
1206 async_command_t *cmd = user_data;
1207 struct device_search_s *search = cmd->internal_user_data;
1208 stonith_device_t *dev = cmd_device(cmd);
1209 gboolean can = FALSE;
1210
1211 free_async_command(cmd);
1212
1213 if (!dev) {
1214 search_devices_record_result(search, NULL, FALSE);
1215 return;
1216 }
1217
1218 mainloop_set_trigger(dev->work);
1219
1220 if (result->execution_status != PCMK_EXEC_DONE) {
1221 crm_warn("Assuming %s cannot fence %s "
1222 "because status could not be executed: %s%s%s%s",
1223 dev->id, search->host,
1224 pcmk_exec_status_str(result->execution_status),
1225 ((result->exit_reason == NULL)? "" : " ("),
1226 ((result->exit_reason == NULL)? "" : result->exit_reason),
1227 ((result->exit_reason == NULL)? "" : ")"));
1228 search_devices_record_result(search, dev->id, FALSE);
1229 return;
1230 }
1231
1232 switch (result->exit_status) {
1233 case fence_status_unknown:
1234 crm_trace("%s reported it cannot fence %s", dev->id, search->host);
1235 break;
1236
1237 case fence_status_active:
1238 case fence_status_inactive:
1239 crm_trace("%s reported it can fence %s", dev->id, search->host);
1240 can = TRUE;
1241 break;
1242
1243 default:
1244 crm_warn("Assuming %s cannot fence %s "
1245 "(status returned unknown code %d)",
1246 dev->id, search->host, result->exit_status);
1247 break;
1248 }
1249 search_devices_record_result(search, dev->id, can);
1250 }
1251
1252 static void
1253 dynamic_list_search_cb(int pid, const pcmk__action_result_t *result,
1254 void *user_data)
1255 {
1256 async_command_t *cmd = user_data;
1257 struct device_search_s *search = cmd->internal_user_data;
1258 stonith_device_t *dev = cmd_device(cmd);
1259 gboolean can_fence = FALSE;
1260
1261 free_async_command(cmd);
1262
1263
1264
1265
1266
1267
1268 if (!dev) {
1269 search_devices_record_result(search, NULL, FALSE);
1270 return;
1271 }
1272
1273 mainloop_set_trigger(dev->work);
1274
1275 if (pcmk__result_ok(result)) {
1276 crm_info("Refreshing target list for %s", dev->id);
1277 g_list_free_full(dev->targets, free);
1278 dev->targets = stonith__parse_targets(result->action_stdout);
1279 dev->targets_age = time(NULL);
1280
1281 } else if (dev->targets != NULL) {
1282 if (result->execution_status == PCMK_EXEC_DONE) {
1283 crm_info("Reusing most recent target list for %s "
1284 "because list returned error code %d",
1285 dev->id, result->exit_status);
1286 } else {
1287 crm_info("Reusing most recent target list for %s "
1288 "because list could not be executed: %s%s%s%s",
1289 dev->id, pcmk_exec_status_str(result->execution_status),
1290 ((result->exit_reason == NULL)? "" : " ("),
1291 ((result->exit_reason == NULL)? "" : result->exit_reason),
1292 ((result->exit_reason == NULL)? "" : ")"));
1293 }
1294
1295 } else {
1296 if (result->execution_status == PCMK_EXEC_DONE) {
1297 crm_warn("Assuming %s cannot fence %s "
1298 "because list returned error code %d",
1299 dev->id, search->host, result->exit_status);
1300 } else {
1301 crm_warn("Assuming %s cannot fence %s "
1302 "because list could not be executed: %s%s%s%s",
1303 dev->id, search->host,
1304 pcmk_exec_status_str(result->execution_status),
1305 ((result->exit_reason == NULL)? "" : " ("),
1306 ((result->exit_reason == NULL)? "" : result->exit_reason),
1307 ((result->exit_reason == NULL)? "" : ")"));
1308 }
1309
1310
1311
1312
1313 if (g_hash_table_lookup(dev->params, PCMK_STONITH_HOST_CHECK) == NULL) {
1314 crm_notice("Switching to pcmk_host_check='status' for %s", dev->id);
1315 pcmk__insert_dup(dev->params, PCMK_STONITH_HOST_CHECK,
1316 PCMK_VALUE_STATUS);
1317 }
1318 }
1319
1320 if (dev->targets) {
1321 const char *alias = g_hash_table_lookup(dev->aliases, search->host);
1322
1323 if (!alias) {
1324 alias = search->host;
1325 }
1326 if (pcmk__str_in_list(alias, dev->targets, pcmk__str_casei)) {
1327 can_fence = TRUE;
1328 }
1329 }
1330 search_devices_record_result(search, dev->id, can_fence);
1331 }
1332
1333
1334
1335
1336
1337 static int
1338 device_params_diff(GHashTable *first, GHashTable *second) {
1339 char *key = NULL;
1340 char *value = NULL;
1341 GHashTableIter gIter;
1342
1343 g_hash_table_iter_init(&gIter, first);
1344 while (g_hash_table_iter_next(&gIter, (void **)&key, (void **)&value)) {
1345
1346 if(strstr(key, "CRM_meta") == key) {
1347 continue;
1348 } else if (strcmp(key, PCMK_XA_CRM_FEATURE_SET) == 0) {
1349 continue;
1350 } else {
1351 char *other_value = g_hash_table_lookup(second, key);
1352
1353 if (!other_value || !pcmk__str_eq(other_value, value, pcmk__str_casei)) {
1354 crm_trace("Different value for %s: %s != %s", key, other_value, value);
1355 return 1;
1356 }
1357 }
1358 }
1359
1360 return 0;
1361 }
1362
1363
1364
1365
1366
1367 static stonith_device_t *
1368 device_has_duplicate(const stonith_device_t *device)
1369 {
1370 stonith_device_t *dup = g_hash_table_lookup(device_list, device->id);
1371
1372 if (!dup) {
1373 crm_trace("No match for %s", device->id);
1374 return NULL;
1375
1376 } else if (!pcmk__str_eq(dup->agent, device->agent, pcmk__str_casei)) {
1377 crm_trace("Different agent: %s != %s", dup->agent, device->agent);
1378 return NULL;
1379 }
1380
1381
1382 if (device_params_diff(device->params, dup->params) ||
1383 device_params_diff(dup->params, device->params)) {
1384 return NULL;
1385 }
1386
1387 crm_trace("Match");
1388 return dup;
1389 }
1390
1391 int
1392 stonith_device_register(xmlNode *dev, gboolean from_cib)
1393 {
1394 stonith_device_t *dup = NULL;
1395 stonith_device_t *device = build_device_from_xml(dev);
1396 guint ndevices = 0;
1397 int rv = pcmk_ok;
1398
1399 CRM_CHECK(device != NULL, return -ENOMEM);
1400
1401
1402 if (pcmk__str_eq(device->id, STONITH_WATCHDOG_ID, pcmk__str_none) ||
1403 pcmk__str_any_of(device->agent, STONITH_WATCHDOG_AGENT,
1404 STONITH_WATCHDOG_AGENT_INTERNAL, NULL)) do {
1405 if (stonith_watchdog_timeout_ms <= 0) {
1406 crm_err("Ignoring watchdog fence device without "
1407 PCMK_OPT_STONITH_WATCHDOG_TIMEOUT " set.");
1408 rv = -ENODEV;
1409
1410 } else if (!pcmk__str_any_of(device->agent, STONITH_WATCHDOG_AGENT,
1411 STONITH_WATCHDOG_AGENT_INTERNAL, NULL)) {
1412 crm_err("Ignoring watchdog fence device with unknown "
1413 "agent '%s' unequal '" STONITH_WATCHDOG_AGENT "'.",
1414 device->agent?device->agent:"");
1415 rv = -ENODEV;
1416
1417 } else if (!pcmk__str_eq(device->id, STONITH_WATCHDOG_ID,
1418 pcmk__str_none)) {
1419 crm_err("Ignoring watchdog fence device "
1420 "named %s !='"STONITH_WATCHDOG_ID"'.",
1421 device->id?device->id:"");
1422 rv = -ENODEV;
1423
1424 } else {
1425 if (pcmk__str_eq(device->agent, STONITH_WATCHDOG_AGENT,
1426 pcmk__str_none)) {
1427
1428
1429
1430 g_list_free_full(stonith_watchdog_targets, free);
1431 stonith_watchdog_targets = device->targets;
1432 device->targets = NULL;
1433 }
1434 if (node_does_watchdog_fencing(stonith_our_uname)) {
1435 g_list_free_full(device->targets, free);
1436 device->targets = stonith__parse_targets(stonith_our_uname);
1437 pcmk__insert_dup(device->params,
1438 PCMK_STONITH_HOST_LIST, stonith_our_uname);
1439
1440 break;
1441 }
1442
1443 crm_debug("Skip registration of watchdog fence device on node not in host-list.");
1444
1445 device->targets = NULL;
1446 stonith_device_remove(device->id, from_cib);
1447 }
1448 free_device(device);
1449 return rv;
1450 } while (0);
1451
1452 dup = device_has_duplicate(device);
1453 if (dup) {
1454 ndevices = g_hash_table_size(device_list);
1455 crm_debug("Device '%s' already in device list (%d active device%s)",
1456 device->id, ndevices, pcmk__plural_s(ndevices));
1457 free_device(device);
1458 device = dup;
1459 dup = g_hash_table_lookup(device_list, device->id);
1460 dup->dirty = FALSE;
1461
1462 } else {
1463 stonith_device_t *old = g_hash_table_lookup(device_list, device->id);
1464
1465 if (from_cib && old && old->api_registered) {
1466
1467
1468
1469
1470 crm_info("Overwriting existing entry for %s from CIB", device->id);
1471 device->pending_ops = old->pending_ops;
1472 device->api_registered = TRUE;
1473 old->pending_ops = NULL;
1474 if (device->pending_ops) {
1475 mainloop_set_trigger(device->work);
1476 }
1477 }
1478 g_hash_table_replace(device_list, device->id, device);
1479
1480 ndevices = g_hash_table_size(device_list);
1481 crm_notice("Added '%s' to device list (%d active device%s)",
1482 device->id, ndevices, pcmk__plural_s(ndevices));
1483 }
1484
1485 if (from_cib) {
1486 device->cib_registered = TRUE;
1487 } else {
1488 device->api_registered = TRUE;
1489 }
1490
1491 return pcmk_ok;
1492 }
1493
1494 void
1495 stonith_device_remove(const char *id, bool from_cib)
1496 {
1497 stonith_device_t *device = g_hash_table_lookup(device_list, id);
1498 guint ndevices = 0;
1499
1500 if (!device) {
1501 ndevices = g_hash_table_size(device_list);
1502 crm_info("Device '%s' not found (%d active device%s)",
1503 id, ndevices, pcmk__plural_s(ndevices));
1504 return;
1505 }
1506
1507 if (from_cib) {
1508 device->cib_registered = FALSE;
1509 } else {
1510 device->verified = FALSE;
1511 device->api_registered = FALSE;
1512 }
1513
1514 if (!device->cib_registered && !device->api_registered) {
1515 g_hash_table_remove(device_list, id);
1516 ndevices = g_hash_table_size(device_list);
1517 crm_info("Removed '%s' from device list (%d active device%s)",
1518 id, ndevices, pcmk__plural_s(ndevices));
1519 } else {
1520 crm_trace("Not removing '%s' from device list (%d active) because "
1521 "still registered via:%s%s",
1522 id, g_hash_table_size(device_list),
1523 (device->cib_registered? " cib" : ""),
1524 (device->api_registered? " api" : ""));
1525 }
1526 }
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537 static int
1538 count_active_levels(const stonith_topology_t *tp)
1539 {
1540 int lpc = 0;
1541 int count = 0;
1542
1543 for (lpc = 0; lpc < ST__LEVEL_COUNT; lpc++) {
1544 if (tp->levels[lpc] != NULL) {
1545 count++;
1546 }
1547 }
1548 return count;
1549 }
1550
1551 static void
1552 free_topology_entry(gpointer data)
1553 {
1554 stonith_topology_t *tp = data;
1555
1556 int lpc = 0;
1557
1558 for (lpc = 0; lpc < ST__LEVEL_COUNT; lpc++) {
1559 if (tp->levels[lpc] != NULL) {
1560 g_list_free_full(tp->levels[lpc], free);
1561 }
1562 }
1563 free(tp->target);
1564 free(tp->target_value);
1565 free(tp->target_pattern);
1566 free(tp->target_attribute);
1567 free(tp);
1568 }
1569
1570 void
1571 free_topology_list(void)
1572 {
1573 if (topology != NULL) {
1574 g_hash_table_destroy(topology);
1575 topology = NULL;
1576 }
1577 }
1578
1579 void
1580 init_topology_list(void)
1581 {
1582 if (topology == NULL) {
1583 topology = pcmk__strkey_table(NULL, free_topology_entry);
1584 }
1585 }
1586
1587 char *
1588 stonith_level_key(const xmlNode *level, enum fenced_target_by mode)
1589 {
1590 if (mode == fenced_target_by_unknown) {
1591 mode = unpack_level_kind(level);
1592 }
1593 switch (mode) {
1594 case fenced_target_by_name:
1595 return crm_element_value_copy(level, PCMK_XA_TARGET);
1596
1597 case fenced_target_by_pattern:
1598 return crm_element_value_copy(level, PCMK_XA_TARGET_PATTERN);
1599
1600 case fenced_target_by_attribute:
1601 return crm_strdup_printf("%s=%s",
1602 crm_element_value(level, PCMK_XA_TARGET_ATTRIBUTE),
1603 crm_element_value(level, PCMK_XA_TARGET_VALUE));
1604
1605 default:
1606 return crm_strdup_printf("unknown-%s", pcmk__xe_id(level));
1607 }
1608 }
1609
1610
1611
1612
1613
1614
1615
1616
1617
1618 static enum fenced_target_by
1619 unpack_level_kind(const xmlNode *level)
1620 {
1621 if (crm_element_value(level, PCMK_XA_TARGET) != NULL) {
1622 return fenced_target_by_name;
1623 }
1624 if (crm_element_value(level, PCMK_XA_TARGET_PATTERN) != NULL) {
1625 return fenced_target_by_pattern;
1626 }
1627 if (!stand_alone
1628 && (crm_element_value(level, PCMK_XA_TARGET_ATTRIBUTE) != NULL)
1629 && (crm_element_value(level, PCMK_XA_TARGET_VALUE) != NULL)) {
1630 return fenced_target_by_attribute;
1631 }
1632 return fenced_target_by_unknown;
1633 }
1634
1635 static stonith_key_value_t *
1636 parse_device_list(const char *devices)
1637 {
1638 int lpc = 0;
1639 int max = 0;
1640 int last = 0;
1641 stonith_key_value_t *output = NULL;
1642
1643 if (devices == NULL) {
1644 return output;
1645 }
1646
1647 max = strlen(devices);
1648 for (lpc = 0; lpc <= max; lpc++) {
1649 if (devices[lpc] == ',' || devices[lpc] == 0) {
1650 char *line = strndup(devices + last, lpc - last);
1651
1652 output = stonith_key_value_add(output, NULL, line);
1653 free(line);
1654
1655 last = lpc + 1;
1656 }
1657 }
1658
1659 return output;
1660 }
1661
1662
1663
1664
1665
1666
1667
1668
1669
1670
1671
1672
1673
1674
1675 static xmlNode *
1676 unpack_level_request(xmlNode *xml, enum fenced_target_by *mode, char **target,
1677 int *id, char **desc)
1678 {
1679 enum fenced_target_by local_mode = fenced_target_by_unknown;
1680 char *local_target = NULL;
1681 int local_id = 0;
1682
1683
1684
1685
1686
1687 if ((xml != NULL) && !pcmk__xe_is(xml, PCMK_XE_FENCING_LEVEL)) {
1688 xml = get_xpath_object("//" PCMK_XE_FENCING_LEVEL, xml, LOG_WARNING);
1689 }
1690
1691 if (xml == NULL) {
1692 if (desc != NULL) {
1693 *desc = crm_strdup_printf("missing");
1694 }
1695 } else {
1696 local_mode = unpack_level_kind(xml);
1697 local_target = stonith_level_key(xml, local_mode);
1698 crm_element_value_int(xml, PCMK_XA_INDEX, &local_id);
1699 if (desc != NULL) {
1700 *desc = crm_strdup_printf("%s[%d]", local_target, local_id);
1701 }
1702 }
1703
1704 if (mode != NULL) {
1705 *mode = local_mode;
1706 }
1707 if (id != NULL) {
1708 *id = local_id;
1709 }
1710
1711 if (target != NULL) {
1712 *target = local_target;
1713 } else {
1714 free(local_target);
1715 }
1716
1717 return xml;
1718 }
1719
1720
1721
1722
1723
1724
1725
1726
1727
1728
1729
1730
1731
1732
1733 void
1734 fenced_register_level(xmlNode *msg, char **desc, pcmk__action_result_t *result)
1735 {
1736 int id = 0;
1737 xmlNode *level;
1738 enum fenced_target_by mode;
1739 char *target;
1740
1741 stonith_topology_t *tp;
1742 stonith_key_value_t *dIter = NULL;
1743 stonith_key_value_t *devices = NULL;
1744
1745 CRM_CHECK((msg != NULL) && (result != NULL), return);
1746
1747 level = unpack_level_request(msg, &mode, &target, &id, desc);
1748 if (level == NULL) {
1749 fenced_set_protocol_error(result);
1750 return;
1751 }
1752
1753
1754 if (pcmk__str_empty(pcmk__xe_id(level))) {
1755 crm_warn("Ignoring registration for topology level without ID");
1756 free(target);
1757 crm_log_xml_trace(level, "Bad level");
1758 pcmk__format_result(result, CRM_EX_INVALID_PARAM, PCMK_EXEC_INVALID,
1759 "Topology level is invalid without ID");
1760 return;
1761 }
1762
1763
1764 if (mode == fenced_target_by_unknown) {
1765 crm_warn("Ignoring registration for topology level '%s' "
1766 "without valid target", pcmk__xe_id(level));
1767 free(target);
1768 crm_log_xml_trace(level, "Bad level");
1769 pcmk__format_result(result, CRM_EX_INVALID_PARAM, PCMK_EXEC_INVALID,
1770 "Invalid target for topology level '%s'",
1771 pcmk__xe_id(level));
1772 return;
1773 }
1774
1775
1776 if ((id < ST__LEVEL_MIN) || (id > ST__LEVEL_MAX)) {
1777 crm_warn("Ignoring topology registration for %s with invalid level %d",
1778 target, id);
1779 free(target);
1780 crm_log_xml_trace(level, "Bad level");
1781 pcmk__format_result(result, CRM_EX_INVALID_PARAM, PCMK_EXEC_INVALID,
1782 "Invalid level number '%s' for topology level '%s'",
1783 pcmk__s(crm_element_value(level, PCMK_XA_INDEX),
1784 ""),
1785 pcmk__xe_id(level));
1786 return;
1787 }
1788
1789
1790 tp = g_hash_table_lookup(topology, target);
1791 if (tp == NULL) {
1792 tp = pcmk__assert_alloc(1, sizeof(stonith_topology_t));
1793
1794 tp->kind = mode;
1795 tp->target = target;
1796 tp->target_value = crm_element_value_copy(level, PCMK_XA_TARGET_VALUE);
1797 tp->target_pattern = crm_element_value_copy(level,
1798 PCMK_XA_TARGET_PATTERN);
1799 tp->target_attribute = crm_element_value_copy(level,
1800 PCMK_XA_TARGET_ATTRIBUTE);
1801
1802 g_hash_table_replace(topology, tp->target, tp);
1803 crm_trace("Added %s (%d) to the topology (%d active entries)",
1804 target, (int) mode, g_hash_table_size(topology));
1805 } else {
1806 free(target);
1807 }
1808
1809 if (tp->levels[id] != NULL) {
1810 crm_info("Adding to the existing %s[%d] topology entry",
1811 tp->target, id);
1812 }
1813
1814 devices = parse_device_list(crm_element_value(level, PCMK_XA_DEVICES));
1815 for (dIter = devices; dIter; dIter = dIter->next) {
1816 const char *device = dIter->value;
1817
1818 crm_trace("Adding device '%s' for %s[%d]", device, tp->target, id);
1819 tp->levels[id] = g_list_append(tp->levels[id], pcmk__str_copy(device));
1820 }
1821 stonith_key_value_freeall(devices, 1, 1);
1822
1823 {
1824 int nlevels = count_active_levels(tp);
1825
1826 crm_info("Target %s has %d active fencing level%s",
1827 tp->target, nlevels, pcmk__plural_s(nlevels));
1828 }
1829
1830 pcmk__set_result(result, CRM_EX_OK, PCMK_EXEC_DONE, NULL);
1831 }
1832
1833
1834
1835
1836
1837
1838
1839
1840
1841
1842
1843
1844
1845 void
1846 fenced_unregister_level(xmlNode *msg, char **desc,
1847 pcmk__action_result_t *result)
1848 {
1849 int id = -1;
1850 stonith_topology_t *tp;
1851 char *target;
1852 xmlNode *level = NULL;
1853
1854 CRM_CHECK(result != NULL, return);
1855
1856 level = unpack_level_request(msg, NULL, &target, &id, desc);
1857 if (level == NULL) {
1858 fenced_set_protocol_error(result);
1859 return;
1860 }
1861
1862
1863 if ((id < 0) || (id >= ST__LEVEL_COUNT)) {
1864 crm_warn("Ignoring topology unregistration for %s with invalid level %d",
1865 target, id);
1866 free(target);
1867 crm_log_xml_trace(level, "Bad level");
1868 pcmk__format_result(result, CRM_EX_INVALID_PARAM, PCMK_EXEC_INVALID,
1869 "Invalid level number '%s' for topology level %s",
1870 pcmk__s(crm_element_value(level, PCMK_XA_INDEX),
1871 "<null>"),
1872
1873
1874 pcmk__s(pcmk__xe_id(level), ""));
1875 return;
1876 }
1877
1878 tp = g_hash_table_lookup(topology, target);
1879 if (tp == NULL) {
1880 guint nentries = g_hash_table_size(topology);
1881
1882 crm_info("No fencing topology found for %s (%d active %s)",
1883 target, nentries,
1884 pcmk__plural_alt(nentries, "entry", "entries"));
1885
1886 } else if (id == 0 && g_hash_table_remove(topology, target)) {
1887 guint nentries = g_hash_table_size(topology);
1888
1889 crm_info("Removed all fencing topology entries related to %s "
1890 "(%d active %s remaining)", target, nentries,
1891 pcmk__plural_alt(nentries, "entry", "entries"));
1892
1893 } else if (tp->levels[id] != NULL) {
1894 guint nlevels;
1895
1896 g_list_free_full(tp->levels[id], free);
1897 tp->levels[id] = NULL;
1898
1899 nlevels = count_active_levels(tp);
1900 crm_info("Removed level %d from fencing topology for %s "
1901 "(%d active level%s remaining)",
1902 id, target, nlevels, pcmk__plural_s(nlevels));
1903 }
1904
1905 free(target);
1906 pcmk__set_result(result, CRM_EX_OK, PCMK_EXEC_DONE, NULL);
1907 }
1908
1909 static char *
1910 list_to_string(GList *list, const char *delim, gboolean terminate_with_delim)
1911 {
1912 int max = g_list_length(list);
1913 size_t delim_len = delim?strlen(delim):0;
1914 size_t alloc_size = 1 + (max?((max-1+(terminate_with_delim?1:0))*delim_len):0);
1915 char *rv;
1916 GList *gIter;
1917
1918 char *pos = NULL;
1919 const char *lead_delim = "";
1920
1921 for (gIter = list; gIter != NULL; gIter = gIter->next) {
1922 const char *value = (const char *) gIter->data;
1923
1924 alloc_size += strlen(value);
1925 }
1926
1927 rv = pcmk__assert_alloc(alloc_size, sizeof(char));
1928 pos = rv;
1929
1930 for (gIter = list; gIter != NULL; gIter = gIter->next) {
1931 const char *value = (const char *) gIter->data;
1932
1933 pos = &pos[sprintf(pos, "%s%s", lead_delim, value)];
1934 lead_delim = delim;
1935 }
1936
1937 if (max && terminate_with_delim) {
1938 sprintf(pos, "%s", delim);
1939 }
1940
1941 return rv;
1942 }
1943
1944
1945
1946
1947
1948
1949
1950
1951
1952
1953
1954
1955
1956
1957
1958
1959 static void
1960 execute_agent_action(xmlNode *msg, pcmk__action_result_t *result)
1961 {
1962 xmlNode *dev = get_xpath_object("//" PCMK__XE_ST_DEVICE_ID, msg, LOG_ERR);
1963 xmlNode *op = get_xpath_object("//@" PCMK__XE_ST_DEVICE_ACTION, msg,
1964 LOG_ERR);
1965 const char *id = crm_element_value(dev, PCMK__XA_ST_DEVICE_ID);
1966 const char *action = crm_element_value(op, PCMK__XA_ST_DEVICE_ACTION);
1967 async_command_t *cmd = NULL;
1968 stonith_device_t *device = NULL;
1969
1970 if ((id == NULL) || (action == NULL)) {
1971 crm_info("Malformed API action request: device %s, action %s",
1972 (id? id : "not specified"),
1973 (action? action : "not specified"));
1974 fenced_set_protocol_error(result);
1975 return;
1976 }
1977
1978 if (pcmk__str_eq(id, STONITH_WATCHDOG_ID, pcmk__str_none)) {
1979
1980 if (stonith_watchdog_timeout_ms <= 0) {
1981 pcmk__set_result(result, CRM_EX_ERROR, PCMK_EXEC_NO_FENCE_DEVICE,
1982 "Watchdog fence device not configured");
1983 return;
1984
1985 } else if (pcmk__str_eq(action, PCMK_ACTION_LIST, pcmk__str_none)) {
1986 pcmk__set_result(result, CRM_EX_OK, PCMK_EXEC_DONE, NULL);
1987 pcmk__set_result_output(result,
1988 list_to_string(stonith_watchdog_targets,
1989 "\n", TRUE),
1990 NULL);
1991 return;
1992
1993 } else if (pcmk__str_eq(action, PCMK_ACTION_MONITOR, pcmk__str_none)) {
1994 pcmk__set_result(result, CRM_EX_OK, PCMK_EXEC_DONE, NULL);
1995 return;
1996 }
1997 }
1998
1999 device = g_hash_table_lookup(device_list, id);
2000 if (device == NULL) {
2001 crm_info("Ignoring API '%s' action request because device %s not found",
2002 action, id);
2003 pcmk__format_result(result, CRM_EX_ERROR, PCMK_EXEC_NO_FENCE_DEVICE,
2004 "'%s' not found", id);
2005 return;
2006
2007 } else if (!device->api_registered
2008 && (strcmp(action, PCMK_ACTION_MONITOR) == 0)) {
2009
2010 crm_info("Ignoring API '%s' action request because device %s not active",
2011 action, id);
2012 pcmk__format_result(result, CRM_EX_ERROR, PCMK_EXEC_NO_FENCE_DEVICE,
2013 "'%s' not active", id);
2014 return;
2015 }
2016
2017 cmd = create_async_command(msg);
2018 if (cmd == NULL) {
2019 crm_log_xml_warn(msg, "invalid");
2020 fenced_set_protocol_error(result);
2021 return;
2022 }
2023
2024 schedule_stonith_command(cmd, device);
2025 pcmk__set_result(result, CRM_EX_OK, PCMK_EXEC_PENDING, NULL);
2026 }
2027
2028 static void
2029 search_devices_record_result(struct device_search_s *search, const char *device, gboolean can_fence)
2030 {
2031 search->replies_received++;
2032 if (can_fence && device) {
2033 if (search->support_action_only != st_device_supports_none) {
2034 stonith_device_t *dev = g_hash_table_lookup(device_list, device);
2035 if (dev && !pcmk_is_set(dev->flags, search->support_action_only)) {
2036 return;
2037 }
2038 }
2039 search->capable = g_list_append(search->capable,
2040 pcmk__str_copy(device));
2041 }
2042
2043 if (search->replies_needed == search->replies_received) {
2044
2045 guint ndevices = g_list_length(search->capable);
2046
2047 crm_debug("Search found %d device%s that can perform '%s' targeting %s",
2048 ndevices, pcmk__plural_s(ndevices),
2049 (search->action? search->action : "unknown action"),
2050 (search->host? search->host : "any node"));
2051
2052 search->callback(search->capable, search->user_data);
2053 free(search->host);
2054 free(search->action);
2055 free(search);
2056 }
2057 }
2058
2059
2060
2061
2062
2063
2064
2065
2066
2067
2068
2069
2070 static gboolean
2071 localhost_is_eligible(const stonith_device_t *device, const char *action,
2072 const char *target, gboolean allow_self)
2073 {
2074 gboolean localhost_is_target = pcmk__str_eq(target, stonith_our_uname,
2075 pcmk__str_casei);
2076
2077 if ((device != NULL) && (action != NULL)
2078 && (device->on_target_actions != NULL)
2079 && (strstr((const char*) device->on_target_actions->str,
2080 action) != NULL)) {
2081
2082 if (!localhost_is_target) {
2083 crm_trace("Operation '%s' using %s can only be executed for local "
2084 "host, not %s", action, device->id, target);
2085 return FALSE;
2086 }
2087
2088 } else if (localhost_is_target && !allow_self) {
2089 crm_trace("'%s' operation does not support self-fencing", action);
2090 return FALSE;
2091 }
2092 return TRUE;
2093 }
2094
2095
2096
2097
2098
2099
2100
2101
2102
2103
2104
2105
2106
2107 static bool
2108 localhost_is_eligible_with_remap(const stonith_device_t *device,
2109 const char *action, const char *target,
2110 gboolean allow_self)
2111 {
2112
2113 if (localhost_is_eligible(device, action, target, allow_self)) {
2114 return true;
2115 }
2116
2117
2118
2119 if (pcmk__str_eq(action, PCMK_ACTION_REBOOT, pcmk__str_none)) {
2120
2121
2122
2123
2124
2125 if (localhost_is_eligible(device, PCMK_ACTION_OFF, target, allow_self)
2126 || localhost_is_eligible(device, PCMK_ACTION_ON, target, FALSE)) {
2127 return true;
2128 }
2129 }
2130
2131 return false;
2132 }
2133
2134 static void
2135 can_fence_host_with_device(stonith_device_t *dev,
2136 struct device_search_s *search)
2137 {
2138 gboolean can = FALSE;
2139 const char *check_type = "Internal bug";
2140 const char *target = NULL;
2141 const char *alias = NULL;
2142 const char *dev_id = "Unspecified device";
2143 const char *action = (search == NULL)? NULL : search->action;
2144
2145 CRM_CHECK((dev != NULL) && (action != NULL), goto search_report_results);
2146
2147 if (dev->id != NULL) {
2148 dev_id = dev->id;
2149 }
2150
2151 target = search->host;
2152 if (target == NULL) {
2153 can = TRUE;
2154 check_type = "No target";
2155 goto search_report_results;
2156 }
2157
2158
2159
2160
2161 if (pcmk__str_eq(action, PCMK_ACTION_ON, pcmk__str_none)
2162 && !pcmk_is_set(dev->flags, st_device_supports_on)) {
2163 check_type = "Agent does not support 'on'";
2164 goto search_report_results;
2165
2166 } else if (!localhost_is_eligible_with_remap(dev, action, target,
2167 search->allow_self)) {
2168 check_type = "This node is not allowed to execute action";
2169 goto search_report_results;
2170 }
2171
2172
2173 check_type = target_list_type(dev);
2174 alias = g_hash_table_lookup(dev->aliases, target);
2175 if (pcmk__str_eq(check_type, PCMK_VALUE_NONE, pcmk__str_casei)) {
2176 can = TRUE;
2177
2178 } else if (pcmk__str_eq(check_type, PCMK_VALUE_STATIC_LIST,
2179 pcmk__str_casei)) {
2180
2181 if (pcmk__str_in_list(target, dev->targets, pcmk__str_casei)) {
2182 can = TRUE;
2183 } else if (g_hash_table_lookup(dev->params, PCMK_STONITH_HOST_MAP)
2184 && g_hash_table_lookup(dev->aliases, target)) {
2185 can = TRUE;
2186 }
2187
2188 } else if (pcmk__str_eq(check_type, PCMK_VALUE_DYNAMIC_LIST,
2189 pcmk__str_casei)) {
2190 time_t now = time(NULL);
2191
2192 if (dev->targets == NULL || dev->targets_age + 60 < now) {
2193 int device_timeout = get_action_timeout(dev, PCMK_ACTION_LIST,
2194 search->per_device_timeout);
2195
2196 if (device_timeout > search->per_device_timeout) {
2197 crm_notice("Since the pcmk_list_timeout (%ds) parameter of %s "
2198 "is larger than " PCMK_OPT_STONITH_TIMEOUT
2199 " (%ds), timeout may occur",
2200 device_timeout, dev_id, search->per_device_timeout);
2201 }
2202
2203 crm_trace("Running '%s' to check whether %s is eligible to fence %s (%s)",
2204 check_type, dev_id, target, action);
2205
2206 schedule_internal_command(__func__, dev, PCMK_ACTION_LIST, NULL,
2207 search->per_device_timeout, search, dynamic_list_search_cb);
2208
2209
2210 return;
2211 }
2212
2213 if (pcmk__str_in_list(((alias == NULL)? target : alias), dev->targets,
2214 pcmk__str_casei)) {
2215 can = TRUE;
2216 }
2217
2218 } else if (pcmk__str_eq(check_type, PCMK_VALUE_STATUS, pcmk__str_casei)) {
2219 int device_timeout = get_action_timeout(dev, check_type, search->per_device_timeout);
2220
2221 if (device_timeout > search->per_device_timeout) {
2222 crm_notice("Since the pcmk_status_timeout (%ds) parameter of %s is "
2223 "larger than " PCMK_OPT_STONITH_TIMEOUT " (%ds), "
2224 "timeout may occur",
2225 device_timeout, dev_id, search->per_device_timeout);
2226 }
2227
2228 crm_trace("Running '%s' to check whether %s is eligible to fence %s (%s)",
2229 check_type, dev_id, target, action);
2230 schedule_internal_command(__func__, dev, PCMK_ACTION_STATUS, target,
2231 search->per_device_timeout, search, status_search_cb);
2232
2233 return;
2234 } else {
2235 crm_err("Invalid value for " PCMK_STONITH_HOST_CHECK ": %s", check_type);
2236 check_type = "Invalid " PCMK_STONITH_HOST_CHECK;
2237 }
2238
2239 search_report_results:
2240 crm_info("%s is%s eligible to fence (%s) %s%s%s%s: %s",
2241 dev_id, (can? "" : " not"), pcmk__s(action, "unspecified action"),
2242 pcmk__s(target, "unspecified target"),
2243 (alias == NULL)? "" : " (as '", pcmk__s(alias, ""),
2244 (alias == NULL)? "" : "')", check_type);
2245 search_devices_record_result(search, ((dev == NULL)? NULL : dev_id), can);
2246 }
2247
2248 static void
2249 search_devices(gpointer key, gpointer value, gpointer user_data)
2250 {
2251 stonith_device_t *dev = value;
2252 struct device_search_s *search = user_data;
2253
2254 can_fence_host_with_device(dev, search);
2255 }
2256
2257 #define DEFAULT_QUERY_TIMEOUT 20
2258 static void
2259 get_capable_devices(const char *host, const char *action, int timeout,
2260 bool allow_self, void *user_data,
2261 void (*callback) (GList * devices, void *user_data),
2262 uint32_t support_action_only)
2263 {
2264 struct device_search_s *search;
2265 guint ndevices = g_hash_table_size(device_list);
2266
2267 if (ndevices == 0) {
2268 callback(NULL, user_data);
2269 return;
2270 }
2271
2272 search = pcmk__assert_alloc(1, sizeof(struct device_search_s));
2273
2274 search->host = pcmk__str_copy(host);
2275 search->action = pcmk__str_copy(action);
2276 search->per_device_timeout = timeout;
2277 search->allow_self = allow_self;
2278 search->callback = callback;
2279 search->user_data = user_data;
2280 search->support_action_only = support_action_only;
2281
2282
2283
2284
2285 search->replies_needed = ndevices;
2286
2287 crm_debug("Searching %d device%s to see which can execute '%s' targeting %s",
2288 ndevices, pcmk__plural_s(ndevices),
2289 (search->action? search->action : "unknown action"),
2290 (search->host? search->host : "any node"));
2291 g_hash_table_foreach(device_list, search_devices, search);
2292 }
2293
2294 struct st_query_data {
2295 xmlNode *reply;
2296 char *remote_peer;
2297 char *client_id;
2298 char *target;
2299 char *action;
2300 int call_options;
2301 };
2302
2303
2304
2305
2306
2307
2308
2309
2310
2311
2312 static void
2313 add_action_specific_attributes(xmlNode *xml, const char *action,
2314 const stonith_device_t *device,
2315 const char *target)
2316 {
2317 int action_specific_timeout;
2318 int delay_max;
2319 int delay_base;
2320
2321 CRM_CHECK(xml && action && device, return);
2322
2323
2324 if (is_action_required(action, device)) {
2325 crm_trace("Action '%s' is required using %s", action, device->id);
2326 crm_xml_add_int(xml, PCMK__XA_ST_REQUIRED, 1);
2327 }
2328
2329
2330 action_specific_timeout = get_action_timeout(device, action, 0);
2331 if (action_specific_timeout) {
2332 crm_trace("Action '%s' has timeout %ds using %s",
2333 action, action_specific_timeout, device->id);
2334 crm_xml_add_int(xml, PCMK__XA_ST_ACTION_TIMEOUT,
2335 action_specific_timeout);
2336 }
2337
2338 delay_max = get_action_delay_max(device, action);
2339 if (delay_max > 0) {
2340 crm_trace("Action '%s' has maximum random delay %ds using %s",
2341 action, delay_max, device->id);
2342 crm_xml_add_int(xml, PCMK__XA_ST_DELAY_MAX, delay_max);
2343 }
2344
2345 delay_base = get_action_delay_base(device, action, target);
2346 if (delay_base > 0) {
2347 crm_xml_add_int(xml, PCMK__XA_ST_DELAY_BASE, delay_base);
2348 }
2349
2350 if ((delay_max > 0) && (delay_base == 0)) {
2351 crm_trace("Action '%s' has maximum random delay %ds using %s",
2352 action, delay_max, device->id);
2353 } else if ((delay_max == 0) && (delay_base > 0)) {
2354 crm_trace("Action '%s' has a static delay of %ds using %s",
2355 action, delay_base, device->id);
2356 } else if ((delay_max > 0) && (delay_base > 0)) {
2357 crm_trace("Action '%s' has a minimum delay of %ds and a randomly chosen "
2358 "maximum delay of %ds using %s",
2359 action, delay_base, delay_max, device->id);
2360 }
2361 }
2362
2363
2364
2365
2366
2367
2368
2369
2370
2371
2372
2373 static void
2374 add_disallowed(xmlNode *xml, const char *action, const stonith_device_t *device,
2375 const char *target, gboolean allow_self)
2376 {
2377 if (!localhost_is_eligible(device, action, target, allow_self)) {
2378 crm_trace("Action '%s' using %s is disallowed for local host",
2379 action, device->id);
2380 pcmk__xe_set_bool_attr(xml, PCMK__XA_ST_ACTION_DISALLOWED, true);
2381 }
2382 }
2383
2384
2385
2386
2387
2388
2389
2390
2391
2392
2393
2394 static void
2395 add_action_reply(xmlNode *xml, const char *action,
2396 const stonith_device_t *device, const char *target,
2397 gboolean allow_self)
2398 {
2399 xmlNode *child = pcmk__xe_create(xml, PCMK__XE_ST_DEVICE_ACTION);
2400
2401 crm_xml_add(child, PCMK_XA_ID, action);
2402 add_action_specific_attributes(child, action, device, target);
2403 add_disallowed(child, action, device, target, allow_self);
2404 }
2405
2406
2407
2408
2409
2410
2411
2412
2413
2414
2415 static void
2416 stonith_send_reply(const xmlNode *reply, int call_options,
2417 const char *remote_peer, pcmk__client_t *client)
2418 {
2419 CRM_CHECK((reply != NULL) && ((remote_peer != NULL) || (client != NULL)),
2420 return);
2421
2422 if (remote_peer == NULL) {
2423 do_local_reply(reply, client, call_options);
2424 } else {
2425 const crm_node_t *node =
2426 pcmk__get_node(0, remote_peer, NULL,
2427 pcmk__node_search_cluster_member);
2428
2429 pcmk__cluster_send_message(node, crm_msg_stonith_ng, reply);
2430 }
2431 }
2432
2433 static void
2434 stonith_query_capable_device_cb(GList * devices, void *user_data)
2435 {
2436 struct st_query_data *query = user_data;
2437 int available_devices = 0;
2438 xmlNode *wrapper = NULL;
2439 xmlNode *list = NULL;
2440 GList *lpc = NULL;
2441 pcmk__client_t *client = NULL;
2442
2443 if (query->client_id != NULL) {
2444 client = pcmk__find_client_by_id(query->client_id);
2445 if ((client == NULL) && (query->remote_peer == NULL)) {
2446 crm_trace("Skipping reply to %s: no longer a client",
2447 query->client_id);
2448 goto done;
2449 }
2450 }
2451
2452
2453 wrapper = pcmk__xe_create(query->reply, PCMK__XE_ST_CALLDATA);
2454 list = pcmk__xe_create(wrapper, __func__);
2455 crm_xml_add(list, PCMK__XA_ST_TARGET, query->target);
2456
2457 for (lpc = devices; lpc != NULL; lpc = lpc->next) {
2458 stonith_device_t *device = g_hash_table_lookup(device_list, lpc->data);
2459 const char *action = query->action;
2460 xmlNode *dev = NULL;
2461
2462 if (!device) {
2463
2464
2465 continue;
2466 }
2467
2468 available_devices++;
2469
2470 dev = pcmk__xe_create(list, PCMK__XE_ST_DEVICE_ID);
2471 crm_xml_add(dev, PCMK_XA_ID, device->id);
2472 crm_xml_add(dev, PCMK__XA_NAMESPACE, device->namespace);
2473 crm_xml_add(dev, PCMK_XA_AGENT, device->agent);
2474
2475
2476 crm_xml_add_int(dev, PCMK__XA_ST_MONITOR_VERIFIED, device->verified);
2477
2478 crm_xml_add_int(dev, PCMK__XA_ST_DEVICE_SUPPORT_FLAGS, device->flags);
2479
2480
2481
2482
2483 if (!pcmk_is_set(device->flags, st_device_supports_reboot)
2484 && pcmk__str_eq(query->action, PCMK_ACTION_REBOOT,
2485 pcmk__str_none)) {
2486 crm_trace("%s doesn't support reboot, using values for off instead",
2487 device->id);
2488 action = PCMK_ACTION_OFF;
2489 }
2490
2491
2492 add_action_specific_attributes(dev, action, device, query->target);
2493 if (pcmk__str_eq(query->action, PCMK_ACTION_REBOOT, pcmk__str_none)) {
2494
2495
2496
2497
2498
2499
2500
2501
2502
2503
2504
2505 add_disallowed(dev, action, device, query->target,
2506 pcmk_is_set(query->call_options,
2507 st_opt_allow_self_fencing));
2508 add_action_reply(dev, PCMK_ACTION_OFF, device, query->target,
2509 pcmk_is_set(query->call_options,
2510 st_opt_allow_self_fencing));
2511 add_action_reply(dev, PCMK_ACTION_ON, device, query->target, FALSE);
2512 }
2513
2514
2515 if (query->target == NULL) {
2516 xmlNode *attrs = pcmk__xe_create(dev, PCMK__XE_ATTRIBUTES);
2517
2518 g_hash_table_foreach(device->params, hash2field, attrs);
2519 }
2520 }
2521
2522 crm_xml_add_int(list, PCMK__XA_ST_AVAILABLE_DEVICES, available_devices);
2523 if (query->target) {
2524 crm_debug("Found %d matching device%s for target '%s'",
2525 available_devices, pcmk__plural_s(available_devices),
2526 query->target);
2527 } else {
2528 crm_debug("%d device%s installed",
2529 available_devices, pcmk__plural_s(available_devices));
2530 }
2531
2532 crm_log_xml_trace(list, "query-result");
2533
2534 stonith_send_reply(query->reply, query->call_options, query->remote_peer,
2535 client);
2536
2537 done:
2538 free_xml(query->reply);
2539 free(query->remote_peer);
2540 free(query->client_id);
2541 free(query->target);
2542 free(query->action);
2543 free(query);
2544 g_list_free_full(devices, free);
2545 }
2546
2547
2548
2549
2550
2551
2552
2553
2554
2555
2556
2557 static void
2558 log_async_result(const async_command_t *cmd,
2559 const pcmk__action_result_t *result,
2560 int pid, const char *next, bool op_merged)
2561 {
2562 int log_level = LOG_ERR;
2563 int output_log_level = LOG_NEVER;
2564 guint devices_remaining = g_list_length(cmd->next_device_iter);
2565
2566 GString *msg = g_string_sized_new(80);
2567
2568
2569 if (pcmk__result_ok(result)) {
2570 log_level = (cmd->target == NULL)? LOG_DEBUG : LOG_NOTICE;
2571 if ((result->action_stdout != NULL)
2572 && !pcmk__str_eq(cmd->action, PCMK_ACTION_METADATA,
2573 pcmk__str_none)) {
2574 output_log_level = LOG_DEBUG;
2575 }
2576 next = NULL;
2577 } else {
2578 log_level = (cmd->target == NULL)? LOG_NOTICE : LOG_ERR;
2579 if ((result->action_stdout != NULL)
2580 && !pcmk__str_eq(cmd->action, PCMK_ACTION_METADATA,
2581 pcmk__str_none)) {
2582 output_log_level = LOG_WARNING;
2583 }
2584 }
2585
2586
2587 pcmk__g_strcat(msg, "Operation '", cmd->action, "' ", NULL);
2588 if (pid != 0) {
2589 g_string_append_printf(msg, "[%d] ", pid);
2590 }
2591 if (cmd->target != NULL) {
2592 pcmk__g_strcat(msg, "targeting ", cmd->target, " ", NULL);
2593 }
2594 if (cmd->device != NULL) {
2595 pcmk__g_strcat(msg, "using ", cmd->device, " ", NULL);
2596 }
2597
2598
2599 if (result->execution_status == PCMK_EXEC_DONE) {
2600 g_string_append_printf(msg, "returned %d", result->exit_status);
2601 } else {
2602 pcmk__g_strcat(msg, "could not be executed: ",
2603 pcmk_exec_status_str(result->execution_status), NULL);
2604 }
2605
2606
2607 if (result->exit_reason != NULL) {
2608 pcmk__g_strcat(msg, " (", result->exit_reason, ")", NULL);
2609 }
2610 if (next != NULL) {
2611 pcmk__g_strcat(msg, ", retrying with ", next, NULL);
2612 }
2613 if (devices_remaining > 0) {
2614 g_string_append_printf(msg, " (%u device%s remaining)",
2615 (unsigned int) devices_remaining,
2616 pcmk__plural_s(devices_remaining));
2617 }
2618 g_string_append_printf(msg, " " CRM_XS " %scall %d from %s",
2619 (op_merged? "merged " : ""), cmd->id,
2620 cmd->client_name);
2621
2622
2623 do_crm_log(log_level, "%s", msg->str);
2624 g_string_free(msg, TRUE);
2625
2626
2627 if (output_log_level != LOG_NEVER) {
2628 char *prefix = crm_strdup_printf("%s[%d]", cmd->device, pid);
2629
2630 crm_log_output(output_log_level, prefix, result->action_stdout);
2631 free(prefix);
2632 }
2633 }
2634
2635
2636
2637
2638
2639
2640
2641
2642
2643
2644 static void
2645 send_async_reply(const async_command_t *cmd, const pcmk__action_result_t *result,
2646 int pid, bool merged)
2647 {
2648 xmlNode *reply = NULL;
2649 pcmk__client_t *client = NULL;
2650
2651 CRM_CHECK((cmd != NULL) && (result != NULL), return);
2652
2653 log_async_result(cmd, result, pid, NULL, merged);
2654
2655 if (cmd->client != NULL) {
2656 client = pcmk__find_client_by_id(cmd->client);
2657 if ((client == NULL) && (cmd->origin == NULL)) {
2658 crm_trace("Skipping reply to %s: no longer a client", cmd->client);
2659 return;
2660 }
2661 }
2662
2663 reply = construct_async_reply(cmd, result);
2664 if (merged) {
2665 pcmk__xe_set_bool_attr(reply, PCMK__XA_ST_OP_MERGED, true);
2666 }
2667
2668 if (!stand_alone && pcmk__is_fencing_action(cmd->action)
2669 && pcmk__str_eq(cmd->origin, cmd->target, pcmk__str_casei)) {
2670
2671
2672
2673 crm_trace("Broadcast '%s' result for %s (target was also originator)",
2674 cmd->action, cmd->target);
2675 crm_xml_add(reply, PCMK__XA_SUBT, PCMK__VALUE_BROADCAST);
2676 crm_xml_add(reply, PCMK__XA_ST_OP, STONITH_OP_NOTIFY);
2677 pcmk__cluster_send_message(NULL, crm_msg_stonith_ng, reply);
2678 } else {
2679
2680 stonith_send_reply(reply, cmd->options, cmd->origin, client);
2681 }
2682
2683 crm_log_xml_trace(reply, "Reply");
2684 free_xml(reply);
2685
2686 if (stand_alone) {
2687
2688 xmlNode *notify_data = pcmk__xe_create(NULL, PCMK__XE_ST_NOTIFY_FENCE);
2689
2690 stonith__xe_set_result(notify_data, result);
2691 crm_xml_add(notify_data, PCMK__XA_ST_TARGET, cmd->target);
2692 crm_xml_add(notify_data, PCMK__XA_ST_OP, cmd->op);
2693 crm_xml_add(notify_data, PCMK__XA_ST_DELEGATE, "localhost");
2694 crm_xml_add(notify_data, PCMK__XA_ST_DEVICE_ID, cmd->device);
2695 crm_xml_add(notify_data, PCMK__XA_ST_REMOTE_OP, cmd->remote_op_id);
2696 crm_xml_add(notify_data, PCMK__XA_ST_ORIGIN, cmd->client);
2697
2698 fenced_send_notification(PCMK__VALUE_ST_NOTIFY_FENCE, result,
2699 notify_data);
2700 fenced_send_notification(PCMK__VALUE_ST_NOTIFY_HISTORY, NULL, NULL);
2701 }
2702 }
2703
2704 static void
2705 cancel_stonith_command(async_command_t * cmd)
2706 {
2707 stonith_device_t *device = cmd_device(cmd);
2708
2709 if (device) {
2710 crm_trace("Cancel scheduled '%s' action using %s",
2711 cmd->action, device->id);
2712 device->pending_ops = g_list_remove(device->pending_ops, cmd);
2713 }
2714 }
2715
2716
2717
2718
2719
2720
2721
2722
2723
2724
2725
2726
2727
2728
2729
2730
2731
2732
2733
2734
2735 static void
2736 reply_to_duplicates(async_command_t *cmd, const pcmk__action_result_t *result,
2737 int pid)
2738 {
2739 GList *next = NULL;
2740
2741 for (GList *iter = cmd_list; iter != NULL; iter = next) {
2742 async_command_t *cmd_other = iter->data;
2743
2744 next = iter->next;
2745
2746 if (cmd == cmd_other) {
2747 continue;
2748 }
2749
2750
2751
2752
2753
2754
2755
2756 if (pcmk__str_eq(cmd->client, cmd_other->client, pcmk__str_casei) ||
2757 !pcmk__str_eq(cmd->target, cmd_other->target, pcmk__str_casei) ||
2758 !pcmk__str_eq(cmd->action, cmd_other->action, pcmk__str_none) ||
2759 !pcmk__str_eq(cmd->device, cmd_other->device, pcmk__str_casei)) {
2760
2761 continue;
2762 }
2763
2764 crm_notice("Merging fencing action '%s'%s%s originating from "
2765 "client %s with identical fencing request from client %s",
2766 cmd_other->action,
2767 (cmd_other->target == NULL)? "" : " targeting ",
2768 pcmk__s(cmd_other->target, ""), cmd_other->client_name,
2769 cmd->client_name);
2770
2771
2772 cmd_list = g_list_remove_link(cmd_list, iter);
2773 send_async_reply(cmd_other, result, pid, true);
2774 cancel_stonith_command(cmd_other);
2775
2776 free_async_command(cmd_other);
2777 g_list_free_1(iter);
2778 }
2779 }
2780
2781
2782
2783
2784
2785
2786
2787
2788
2789 static stonith_device_t *
2790 next_required_device(async_command_t *cmd)
2791 {
2792 for (GList *iter = cmd->next_device_iter; iter != NULL; iter = iter->next) {
2793 stonith_device_t *next_device = g_hash_table_lookup(device_list,
2794 iter->data);
2795
2796 if (is_action_required(cmd->action, next_device)) {
2797
2798
2799
2800 cmd->next_device_iter = iter->next;
2801 return next_device;
2802 }
2803 }
2804 return NULL;
2805 }
2806
2807 static void
2808 st_child_done(int pid, const pcmk__action_result_t *result, void *user_data)
2809 {
2810 async_command_t *cmd = user_data;
2811
2812 stonith_device_t *device = NULL;
2813 stonith_device_t *next_device = NULL;
2814
2815 CRM_CHECK(cmd != NULL, return);
2816
2817 device = cmd_device(cmd);
2818 cmd->active_on = NULL;
2819
2820
2821 if (device) {
2822 if (!device->verified && pcmk__result_ok(result)
2823 && pcmk__strcase_any_of(cmd->action, PCMK_ACTION_LIST,
2824 PCMK_ACTION_MONITOR, PCMK_ACTION_STATUS,
2825 NULL)) {
2826
2827 device->verified = TRUE;
2828 }
2829
2830 mainloop_set_trigger(device->work);
2831 }
2832
2833 if (pcmk__result_ok(result)) {
2834 next_device = next_required_device(cmd);
2835
2836 } else if ((cmd->next_device_iter != NULL)
2837 && !is_action_required(cmd->action, device)) {
2838
2839
2840 next_device = g_hash_table_lookup(device_list,
2841 cmd->next_device_iter->data);
2842 cmd->next_device_iter = cmd->next_device_iter->next;
2843 }
2844
2845 if (next_device == NULL) {
2846 send_async_reply(cmd, result, pid, false);
2847 if (pcmk__result_ok(result)) {
2848 reply_to_duplicates(cmd, result, pid);
2849 }
2850 free_async_command(cmd);
2851
2852 } else {
2853 log_async_result(cmd, result, pid, next_device->id, false);
2854 schedule_stonith_command(cmd, next_device);
2855 }
2856 }
2857
2858 static gint
2859 sort_device_priority(gconstpointer a, gconstpointer b)
2860 {
2861 const stonith_device_t *dev_a = a;
2862 const stonith_device_t *dev_b = b;
2863
2864 if (dev_a->priority > dev_b->priority) {
2865 return -1;
2866 } else if (dev_a->priority < dev_b->priority) {
2867 return 1;
2868 }
2869 return 0;
2870 }
2871
2872 static void
2873 stonith_fence_get_devices_cb(GList * devices, void *user_data)
2874 {
2875 async_command_t *cmd = user_data;
2876 stonith_device_t *device = NULL;
2877 guint ndevices = g_list_length(devices);
2878
2879 crm_info("Found %d matching device%s for target '%s'",
2880 ndevices, pcmk__plural_s(ndevices), cmd->target);
2881
2882 if (devices != NULL) {
2883
2884 devices = g_list_sort(devices, sort_device_priority);
2885 device = g_hash_table_lookup(device_list, devices->data);
2886 }
2887
2888 if (device == NULL) {
2889 pcmk__action_result_t result = PCMK__UNKNOWN_RESULT;
2890
2891 pcmk__format_result(&result, CRM_EX_ERROR, PCMK_EXEC_NO_FENCE_DEVICE,
2892 "No device configured for target '%s'",
2893 cmd->target);
2894 send_async_reply(cmd, &result, 0, false);
2895 pcmk__reset_result(&result);
2896 free_async_command(cmd);
2897 g_list_free_full(devices, free);
2898
2899 } else {
2900 cmd->device_list = devices;
2901 cmd->next_device_iter = devices->next;
2902 schedule_stonith_command(cmd, device);
2903 }
2904 }
2905
2906
2907
2908
2909
2910
2911
2912
2913 static void
2914 fence_locally(xmlNode *msg, pcmk__action_result_t *result)
2915 {
2916 const char *device_id = NULL;
2917 stonith_device_t *device = NULL;
2918 async_command_t *cmd = NULL;
2919 xmlNode *dev = NULL;
2920
2921 CRM_CHECK((msg != NULL) && (result != NULL), return);
2922
2923 dev = get_xpath_object("//@" PCMK__XA_ST_TARGET, msg, LOG_ERR);
2924
2925 cmd = create_async_command(msg);
2926 if (cmd == NULL) {
2927 crm_log_xml_warn(msg, "invalid");
2928 fenced_set_protocol_error(result);
2929 return;
2930 }
2931
2932 device_id = crm_element_value(dev, PCMK__XA_ST_DEVICE_ID);
2933 if (device_id != NULL) {
2934 device = g_hash_table_lookup(device_list, device_id);
2935 if (device == NULL) {
2936 crm_err("Requested device '%s' is not available", device_id);
2937 pcmk__format_result(result, CRM_EX_ERROR, PCMK_EXEC_NO_FENCE_DEVICE,
2938 "Requested device '%s' not found", device_id);
2939 return;
2940 }
2941 schedule_stonith_command(cmd, device);
2942
2943 } else {
2944 const char *host = crm_element_value(dev, PCMK__XA_ST_TARGET);
2945
2946 if (pcmk_is_set(cmd->options, st_opt_cs_nodeid)) {
2947 int nodeid = 0;
2948 crm_node_t *node = NULL;
2949
2950 pcmk__scan_min_int(host, &nodeid, 0);
2951 node = pcmk__search_node_caches(nodeid, NULL,
2952 pcmk__node_search_any
2953 |pcmk__node_search_cluster_cib);
2954 if (node != NULL) {
2955 host = node->uname;
2956 }
2957 }
2958
2959
2960 get_capable_devices(host, cmd->action, cmd->default_timeout,
2961 TRUE, cmd, stonith_fence_get_devices_cb,
2962 fenced_support_flag(cmd->action));
2963 }
2964
2965 pcmk__set_result(result, CRM_EX_OK, PCMK_EXEC_PENDING, NULL);
2966 }
2967
2968
2969
2970
2971
2972
2973
2974
2975
2976
2977
2978
2979
2980
2981
2982 xmlNode *
2983 fenced_construct_reply(const xmlNode *request, xmlNode *data,
2984 const pcmk__action_result_t *result)
2985 {
2986 xmlNode *reply = NULL;
2987
2988 reply = pcmk__xe_create(NULL, PCMK__XE_ST_REPLY);
2989
2990 crm_xml_add(reply, PCMK__XA_ST_ORIGIN, __func__);
2991 crm_xml_add(reply, PCMK__XA_T, PCMK__VALUE_STONITH_NG);
2992 stonith__xe_set_result(reply, result);
2993
2994 if (request == NULL) {
2995
2996
2997
2998
2999
3000
3001 crm_warn("Missing request information for client notifications for "
3002 "operation with result '%s' (initiated before we came up?)",
3003 pcmk_exec_status_str(result->execution_status));
3004
3005 } else {
3006 const char *name = NULL;
3007 const char *value = NULL;
3008
3009
3010 const char *names[] = {
3011 PCMK__XA_ST_OP,
3012 PCMK__XA_ST_CALLID,
3013 PCMK__XA_ST_CLIENTID,
3014 PCMK__XA_ST_CLIENTNAME,
3015 PCMK__XA_ST_REMOTE_OP,
3016 PCMK__XA_ST_CALLOPT,
3017 };
3018
3019 for (int lpc = 0; lpc < PCMK__NELEM(names); lpc++) {
3020 name = names[lpc];
3021 value = crm_element_value(request, name);
3022 crm_xml_add(reply, name, value);
3023 }
3024 if (data != NULL) {
3025 xmlNode *wrapper = pcmk__xe_create(reply, PCMK__XE_ST_CALLDATA);
3026
3027 pcmk__xml_copy(wrapper, data);
3028 }
3029 }
3030 return reply;
3031 }
3032
3033
3034
3035
3036
3037
3038
3039
3040 static xmlNode *
3041 construct_async_reply(const async_command_t *cmd,
3042 const pcmk__action_result_t *result)
3043 {
3044 xmlNode *reply = pcmk__xe_create(NULL, PCMK__XE_ST_REPLY);
3045
3046 crm_xml_add(reply, PCMK__XA_ST_ORIGIN, __func__);
3047 crm_xml_add(reply, PCMK__XA_T, PCMK__VALUE_STONITH_NG);
3048 crm_xml_add(reply, PCMK__XA_ST_OP, cmd->op);
3049 crm_xml_add(reply, PCMK__XA_ST_DEVICE_ID, cmd->device);
3050 crm_xml_add(reply, PCMK__XA_ST_REMOTE_OP, cmd->remote_op_id);
3051 crm_xml_add(reply, PCMK__XA_ST_CLIENTID, cmd->client);
3052 crm_xml_add(reply, PCMK__XA_ST_CLIENTNAME, cmd->client_name);
3053 crm_xml_add(reply, PCMK__XA_ST_TARGET, cmd->target);
3054 crm_xml_add(reply, PCMK__XA_ST_DEVICE_ACTION, cmd->op);
3055 crm_xml_add(reply, PCMK__XA_ST_ORIGIN, cmd->origin);
3056 crm_xml_add_int(reply, PCMK__XA_ST_CALLID, cmd->id);
3057 crm_xml_add_int(reply, PCMK__XA_ST_CALLOPT, cmd->options);
3058
3059 stonith__xe_set_result(reply, result);
3060 return reply;
3061 }
3062
3063 bool fencing_peer_active(crm_node_t *peer)
3064 {
3065 if (peer == NULL) {
3066 return FALSE;
3067 } else if (peer->uname == NULL) {
3068 return FALSE;
3069 } else if (pcmk_is_set(peer->processes, crm_get_cluster_proc())) {
3070 return TRUE;
3071 }
3072 return FALSE;
3073 }
3074
3075 void
3076 set_fencing_completed(remote_fencing_op_t *op)
3077 {
3078 struct timespec tv;
3079
3080 qb_util_timespec_from_epoch_get(&tv);
3081 op->completed = tv.tv_sec;
3082 op->completed_nsec = tv.tv_nsec;
3083 }
3084
3085
3086
3087
3088
3089
3090
3091
3092
3093
3094 static const char *
3095 check_alternate_host(const char *target)
3096 {
3097 if (pcmk__str_eq(target, stonith_our_uname, pcmk__str_casei)) {
3098 GHashTableIter gIter;
3099 crm_node_t *entry = NULL;
3100
3101 g_hash_table_iter_init(&gIter, crm_peer_cache);
3102 while (g_hash_table_iter_next(&gIter, NULL, (void **)&entry)) {
3103 if (fencing_peer_active(entry)
3104 && !pcmk__str_eq(entry->uname, target, pcmk__str_casei)) {
3105 crm_notice("Forwarding self-fencing request to %s",
3106 entry->uname);
3107 return entry->uname;
3108 }
3109 }
3110 crm_warn("Will handle own fencing because no peer can");
3111 }
3112 return NULL;
3113 }
3114
3115 static void
3116 remove_relay_op(xmlNode * request)
3117 {
3118 xmlNode *dev = get_xpath_object("//@" PCMK__XE_ST_DEVICE_ACTION, request,
3119 LOG_TRACE);
3120 const char *relay_op_id = NULL;
3121 const char *op_id = NULL;
3122 const char *client_name = NULL;
3123 const char *target = NULL;
3124 remote_fencing_op_t *relay_op = NULL;
3125
3126 if (dev) {
3127 target = crm_element_value(dev, PCMK__XA_ST_TARGET);
3128 }
3129
3130 relay_op_id = crm_element_value(request, PCMK__XA_ST_REMOTE_OP_RELAY);
3131 op_id = crm_element_value(request, PCMK__XA_ST_REMOTE_OP);
3132 client_name = crm_element_value(request, PCMK__XA_ST_CLIENTNAME);
3133
3134
3135 if (relay_op_id && target && pcmk__str_eq(target, stonith_our_uname, pcmk__str_casei)) {
3136 relay_op = g_hash_table_lookup(stonith_remote_op_list, relay_op_id);
3137
3138 if (relay_op) {
3139 GHashTableIter iter;
3140 remote_fencing_op_t *list_op = NULL;
3141 g_hash_table_iter_init(&iter, stonith_remote_op_list);
3142
3143
3144 while (g_hash_table_iter_next(&iter, NULL, (void **)&list_op)) {
3145 GList *dup_iter = NULL;
3146 if (list_op != relay_op) {
3147 for (dup_iter = list_op->duplicates; dup_iter != NULL; dup_iter = dup_iter->next) {
3148 remote_fencing_op_t *other = dup_iter->data;
3149 if (other == relay_op) {
3150 other->duplicates = g_list_remove(other->duplicates, relay_op);
3151 break;
3152 }
3153 }
3154 }
3155 }
3156 crm_debug("Deleting relay op %s ('%s'%s%s for %s), "
3157 "replaced by op %s ('%s'%s%s for %s)",
3158 relay_op->id, relay_op->action,
3159 (relay_op->target == NULL)? "" : " targeting ",
3160 pcmk__s(relay_op->target, ""),
3161 relay_op->client_name, op_id, relay_op->action,
3162 (target == NULL)? "" : " targeting ", pcmk__s(target, ""),
3163 client_name);
3164
3165 g_hash_table_remove(stonith_remote_op_list, relay_op_id);
3166 }
3167 }
3168 }
3169
3170
3171
3172
3173
3174
3175
3176
3177
3178
3179
3180
3181
3182
3183
3184 static inline bool
3185 is_privileged(const pcmk__client_t *c, const char *op)
3186 {
3187 if ((c == NULL) || pcmk_is_set(c->flags, pcmk__client_privileged)) {
3188 return true;
3189 } else {
3190 crm_warn("Rejecting IPC request '%s' from unprivileged client %s",
3191 pcmk__s(op, ""), pcmk__client_name(c));
3192 return false;
3193 }
3194 }
3195
3196
3197 static xmlNode *
3198 handle_register_request(pcmk__request_t *request)
3199 {
3200 xmlNode *reply = pcmk__xe_create(NULL, "reply");
3201
3202 pcmk__assert(request->ipc_client != NULL);
3203 crm_xml_add(reply, PCMK__XA_ST_OP, CRM_OP_REGISTER);
3204 crm_xml_add(reply, PCMK__XA_ST_CLIENTID, request->ipc_client->id);
3205 pcmk__set_result(&request->result, CRM_EX_OK, PCMK_EXEC_DONE, NULL);
3206 pcmk__set_request_flags(request, pcmk__request_reuse_options);
3207 return reply;
3208 }
3209
3210
3211 static xmlNode *
3212 handle_agent_request(pcmk__request_t *request)
3213 {
3214 execute_agent_action(request->xml, &request->result);
3215 if (request->result.execution_status == PCMK_EXEC_PENDING) {
3216 return NULL;
3217 }
3218 return fenced_construct_reply(request->xml, NULL, &request->result);
3219 }
3220
3221
3222 static xmlNode *
3223 handle_update_timeout_request(pcmk__request_t *request)
3224 {
3225 const char *call_id = crm_element_value(request->xml, PCMK__XA_ST_CALLID);
3226 const char *client_id = crm_element_value(request->xml,
3227 PCMK__XA_ST_CLIENTID);
3228 int op_timeout = 0;
3229
3230 crm_element_value_int(request->xml, PCMK__XA_ST_TIMEOUT, &op_timeout);
3231 do_stonith_async_timeout_update(client_id, call_id, op_timeout);
3232 pcmk__set_result(&request->result, CRM_EX_OK, PCMK_EXEC_DONE, NULL);
3233 return NULL;
3234 }
3235
3236
3237 static xmlNode *
3238 handle_query_request(pcmk__request_t *request)
3239 {
3240 int timeout = 0;
3241 xmlNode *dev = NULL;
3242 const char *action = NULL;
3243 const char *target = NULL;
3244 const char *client_id = crm_element_value(request->xml,
3245 PCMK__XA_ST_CLIENTID);
3246 struct st_query_data *query = NULL;
3247
3248 if (request->peer != NULL) {
3249
3250 create_remote_stonith_op(client_id, request->xml, TRUE);
3251 }
3252
3253
3254 remove_relay_op(request->xml);
3255
3256 pcmk__set_result(&request->result, CRM_EX_OK, PCMK_EXEC_DONE, NULL);
3257
3258 dev = get_xpath_object("//@" PCMK__XE_ST_DEVICE_ACTION, request->xml,
3259 LOG_NEVER);
3260 if (dev != NULL) {
3261 const char *device = crm_element_value(dev, PCMK__XA_ST_DEVICE_ID);
3262
3263 if (pcmk__str_eq(device, "manual_ack", pcmk__str_casei)) {
3264 return NULL;
3265 }
3266 target = crm_element_value(dev, PCMK__XA_ST_TARGET);
3267 action = crm_element_value(dev, PCMK__XA_ST_DEVICE_ACTION);
3268 }
3269
3270 crm_log_xml_trace(request->xml, "Query");
3271
3272 query = pcmk__assert_alloc(1, sizeof(struct st_query_data));
3273
3274 query->reply = fenced_construct_reply(request->xml, NULL, &request->result);
3275 query->remote_peer = pcmk__str_copy(request->peer);
3276 query->client_id = pcmk__str_copy(client_id);
3277 query->target = pcmk__str_copy(target);
3278 query->action = pcmk__str_copy(action);
3279 query->call_options = request->call_options;
3280
3281 crm_element_value_int(request->xml, PCMK__XA_ST_TIMEOUT, &timeout);
3282 get_capable_devices(target, action, timeout,
3283 pcmk_is_set(query->call_options,
3284 st_opt_allow_self_fencing),
3285 query, stonith_query_capable_device_cb, st_device_supports_none);
3286 return NULL;
3287 }
3288
3289
3290 static xmlNode *
3291 handle_notify_request(pcmk__request_t *request)
3292 {
3293 const char *flag_name = NULL;
3294
3295 pcmk__assert(request->ipc_client != NULL);
3296 flag_name = crm_element_value(request->xml, PCMK__XA_ST_NOTIFY_ACTIVATE);
3297 if (flag_name != NULL) {
3298 crm_debug("Enabling %s callbacks for client %s",
3299 flag_name, pcmk__request_origin(request));
3300 pcmk__set_client_flags(request->ipc_client, get_stonith_flag(flag_name));
3301 }
3302
3303 flag_name = crm_element_value(request->xml, PCMK__XA_ST_NOTIFY_DEACTIVATE);
3304 if (flag_name != NULL) {
3305 crm_debug("Disabling %s callbacks for client %s",
3306 flag_name, pcmk__request_origin(request));
3307 pcmk__clear_client_flags(request->ipc_client,
3308 get_stonith_flag(flag_name));
3309 }
3310
3311 pcmk__set_result(&request->result, CRM_EX_OK, PCMK_EXEC_DONE, NULL);
3312 pcmk__set_request_flags(request, pcmk__request_reuse_options);
3313
3314 return pcmk__ipc_create_ack(request->ipc_flags, PCMK__XE_ACK, NULL,
3315 CRM_EX_OK);
3316 }
3317
3318
3319 static xmlNode *
3320 handle_relay_request(pcmk__request_t *request)
3321 {
3322 xmlNode *dev = get_xpath_object("//@" PCMK__XA_ST_TARGET, request->xml,
3323 LOG_TRACE);
3324
3325 crm_notice("Received forwarded fencing request from "
3326 "%s %s to fence (%s) peer %s",
3327 pcmk__request_origin_type(request),
3328 pcmk__request_origin(request),
3329 crm_element_value(dev, PCMK__XA_ST_DEVICE_ACTION),
3330 crm_element_value(dev, PCMK__XA_ST_TARGET));
3331
3332 if (initiate_remote_stonith_op(NULL, request->xml, FALSE) == NULL) {
3333 fenced_set_protocol_error(&request->result);
3334 return fenced_construct_reply(request->xml, NULL, &request->result);
3335 }
3336
3337 pcmk__set_result(&request->result, CRM_EX_OK, PCMK_EXEC_PENDING, NULL);
3338 return NULL;
3339 }
3340
3341
3342 static xmlNode *
3343 handle_fence_request(pcmk__request_t *request)
3344 {
3345 if ((request->peer != NULL) || stand_alone) {
3346 fence_locally(request->xml, &request->result);
3347
3348 } else if (pcmk_is_set(request->call_options, st_opt_manual_ack)) {
3349 switch (fenced_handle_manual_confirmation(request->ipc_client,
3350 request->xml)) {
3351 case pcmk_rc_ok:
3352 pcmk__set_result(&request->result, CRM_EX_OK, PCMK_EXEC_DONE,
3353 NULL);
3354 break;
3355 case EINPROGRESS:
3356 pcmk__set_result(&request->result, CRM_EX_OK, PCMK_EXEC_PENDING,
3357 NULL);
3358 break;
3359 default:
3360 fenced_set_protocol_error(&request->result);
3361 break;
3362 }
3363
3364 } else {
3365 const char *alternate_host = NULL;
3366 xmlNode *dev = get_xpath_object("//@" PCMK__XA_ST_TARGET, request->xml,
3367 LOG_TRACE);
3368 const char *target = crm_element_value(dev, PCMK__XA_ST_TARGET);
3369 const char *action = crm_element_value(dev, PCMK__XA_ST_DEVICE_ACTION);
3370 const char *device = crm_element_value(dev, PCMK__XA_ST_DEVICE_ID);
3371
3372 if (request->ipc_client != NULL) {
3373 int tolerance = 0;
3374
3375 crm_notice("Client %s wants to fence (%s) %s using %s",
3376 pcmk__request_origin(request), action,
3377 target, (device? device : "any device"));
3378 crm_element_value_int(dev, PCMK__XA_ST_TOLERANCE, &tolerance);
3379 if (stonith_check_fence_tolerance(tolerance, target, action)) {
3380 pcmk__set_result(&request->result, CRM_EX_OK, PCMK_EXEC_DONE,
3381 NULL);
3382 return fenced_construct_reply(request->xml, NULL,
3383 &request->result);
3384 }
3385 alternate_host = check_alternate_host(target);
3386
3387 } else {
3388 crm_notice("Peer %s wants to fence (%s) '%s' with device '%s'",
3389 request->peer, action, target,
3390 (device == NULL)? "(any)" : device);
3391 }
3392
3393 if (alternate_host != NULL) {
3394 const char *client_id = NULL;
3395 remote_fencing_op_t *op = NULL;
3396 crm_node_t *node = pcmk__get_node(0, alternate_host, NULL,
3397 pcmk__node_search_cluster_member);
3398
3399 if (request->ipc_client->id == 0) {
3400 client_id = crm_element_value(request->xml,
3401 PCMK__XA_ST_CLIENTID);
3402 } else {
3403 client_id = request->ipc_client->id;
3404 }
3405
3406
3407
3408
3409
3410 op = create_remote_stonith_op(client_id, request->xml, FALSE);
3411
3412 crm_xml_add(request->xml, PCMK__XA_ST_OP, STONITH_OP_RELAY);
3413 crm_xml_add(request->xml, PCMK__XA_ST_CLIENTID,
3414 request->ipc_client->id);
3415 crm_xml_add(request->xml, PCMK__XA_ST_REMOTE_OP, op->id);
3416 pcmk__cluster_send_message(node, crm_msg_stonith_ng, request->xml);
3417 pcmk__set_result(&request->result, CRM_EX_OK, PCMK_EXEC_PENDING,
3418 NULL);
3419
3420 } else if (initiate_remote_stonith_op(request->ipc_client, request->xml,
3421 FALSE) == NULL) {
3422 fenced_set_protocol_error(&request->result);
3423
3424 } else {
3425 pcmk__set_result(&request->result, CRM_EX_OK, PCMK_EXEC_PENDING,
3426 NULL);
3427 }
3428 }
3429
3430 if (request->result.execution_status == PCMK_EXEC_PENDING) {
3431 return NULL;
3432 }
3433 return fenced_construct_reply(request->xml, NULL, &request->result);
3434 }
3435
3436
3437 static xmlNode *
3438 handle_history_request(pcmk__request_t *request)
3439 {
3440 xmlNode *reply = NULL;
3441 xmlNode *data = NULL;
3442
3443 stonith_fence_history(request->xml, &data, request->peer,
3444 request->call_options);
3445 pcmk__set_result(&request->result, CRM_EX_OK, PCMK_EXEC_DONE, NULL);
3446 if (!pcmk_is_set(request->call_options, st_opt_discard_reply)) {
3447
3448
3449
3450 reply = fenced_construct_reply(request->xml, data, &request->result);
3451 }
3452 free_xml(data);
3453 return reply;
3454 }
3455
3456
3457 static xmlNode *
3458 handle_device_add_request(pcmk__request_t *request)
3459 {
3460 const char *op = crm_element_value(request->xml, PCMK__XA_ST_OP);
3461 xmlNode *dev = get_xpath_object("//" PCMK__XE_ST_DEVICE_ID, request->xml,
3462 LOG_ERR);
3463
3464 if (is_privileged(request->ipc_client, op)) {
3465 int rc = stonith_device_register(dev, FALSE);
3466
3467 pcmk__set_result(&request->result,
3468 ((rc == pcmk_ok)? CRM_EX_OK : CRM_EX_ERROR),
3469 stonith__legacy2status(rc),
3470 ((rc == pcmk_ok)? NULL : pcmk_strerror(rc)));
3471 } else {
3472 pcmk__set_result(&request->result, CRM_EX_INSUFFICIENT_PRIV,
3473 PCMK_EXEC_INVALID,
3474 "Unprivileged users must register device via CIB");
3475 }
3476 fenced_send_config_notification(op, &request->result,
3477 (dev == NULL)? NULL : pcmk__xe_id(dev));
3478 return fenced_construct_reply(request->xml, NULL, &request->result);
3479 }
3480
3481
3482 static xmlNode *
3483 handle_device_delete_request(pcmk__request_t *request)
3484 {
3485 xmlNode *dev = get_xpath_object("//" PCMK__XE_ST_DEVICE_ID, request->xml,
3486 LOG_ERR);
3487 const char *device_id = crm_element_value(dev, PCMK_XA_ID);
3488 const char *op = crm_element_value(request->xml, PCMK__XA_ST_OP);
3489
3490 if (is_privileged(request->ipc_client, op)) {
3491 stonith_device_remove(device_id, false);
3492 pcmk__set_result(&request->result, CRM_EX_OK, PCMK_EXEC_DONE, NULL);
3493 } else {
3494 pcmk__set_result(&request->result, CRM_EX_INSUFFICIENT_PRIV,
3495 PCMK_EXEC_INVALID,
3496 "Unprivileged users must delete device via CIB");
3497 }
3498 fenced_send_config_notification(op, &request->result, device_id);
3499 return fenced_construct_reply(request->xml, NULL, &request->result);
3500 }
3501
3502
3503 static xmlNode *
3504 handle_level_add_request(pcmk__request_t *request)
3505 {
3506 char *desc = NULL;
3507 const char *op = crm_element_value(request->xml, PCMK__XA_ST_OP);
3508
3509 if (is_privileged(request->ipc_client, op)) {
3510 fenced_register_level(request->xml, &desc, &request->result);
3511 } else {
3512 unpack_level_request(request->xml, NULL, NULL, NULL, &desc);
3513 pcmk__set_result(&request->result, CRM_EX_INSUFFICIENT_PRIV,
3514 PCMK_EXEC_INVALID,
3515 "Unprivileged users must add level via CIB");
3516 }
3517 fenced_send_config_notification(op, &request->result, desc);
3518 free(desc);
3519 return fenced_construct_reply(request->xml, NULL, &request->result);
3520 }
3521
3522
3523 static xmlNode *
3524 handle_level_delete_request(pcmk__request_t *request)
3525 {
3526 char *desc = NULL;
3527 const char *op = crm_element_value(request->xml, PCMK__XA_ST_OP);
3528
3529 if (is_privileged(request->ipc_client, op)) {
3530 fenced_unregister_level(request->xml, &desc, &request->result);
3531 } else {
3532 unpack_level_request(request->xml, NULL, NULL, NULL, &desc);
3533 pcmk__set_result(&request->result, CRM_EX_INSUFFICIENT_PRIV,
3534 PCMK_EXEC_INVALID,
3535 "Unprivileged users must delete level via CIB");
3536 }
3537 fenced_send_config_notification(op, &request->result, desc);
3538 free(desc);
3539 return fenced_construct_reply(request->xml, NULL, &request->result);
3540 }
3541
3542
3543 static xmlNode *
3544 handle_cache_request(pcmk__request_t *request)
3545 {
3546 int node_id = 0;
3547 const char *name = NULL;
3548
3549 crm_element_value_int(request->xml, PCMK_XA_ID, &node_id);
3550 name = crm_element_value(request->xml, PCMK_XA_UNAME);
3551 pcmk__cluster_forget_cluster_node(node_id, name);
3552 pcmk__set_result(&request->result, CRM_EX_OK, PCMK_EXEC_DONE, NULL);
3553 return NULL;
3554 }
3555
3556 static xmlNode *
3557 handle_unknown_request(pcmk__request_t *request)
3558 {
3559 crm_err("Unknown IPC request %s from %s %s",
3560 request->op, pcmk__request_origin_type(request),
3561 pcmk__request_origin(request));
3562 pcmk__format_result(&request->result, CRM_EX_PROTOCOL, PCMK_EXEC_INVALID,
3563 "Unknown IPC request type '%s' (bug?)", request->op);
3564 return fenced_construct_reply(request->xml, NULL, &request->result);
3565 }
3566
3567 static void
3568 fenced_register_handlers(void)
3569 {
3570 pcmk__server_command_t handlers[] = {
3571 { CRM_OP_REGISTER, handle_register_request },
3572 { STONITH_OP_EXEC, handle_agent_request },
3573 { STONITH_OP_TIMEOUT_UPDATE, handle_update_timeout_request },
3574 { STONITH_OP_QUERY, handle_query_request },
3575 { STONITH_OP_NOTIFY, handle_notify_request },
3576 { STONITH_OP_RELAY, handle_relay_request },
3577 { STONITH_OP_FENCE, handle_fence_request },
3578 { STONITH_OP_FENCE_HISTORY, handle_history_request },
3579 { STONITH_OP_DEVICE_ADD, handle_device_add_request },
3580 { STONITH_OP_DEVICE_DEL, handle_device_delete_request },
3581 { STONITH_OP_LEVEL_ADD, handle_level_add_request },
3582 { STONITH_OP_LEVEL_DEL, handle_level_delete_request },
3583 { CRM_OP_RM_NODE_CACHE, handle_cache_request },
3584 { NULL, handle_unknown_request },
3585 };
3586
3587 fenced_handlers = pcmk__register_handlers(handlers);
3588 }
3589
3590 void
3591 fenced_unregister_handlers(void)
3592 {
3593 if (fenced_handlers != NULL) {
3594 g_hash_table_destroy(fenced_handlers);
3595 fenced_handlers = NULL;
3596 }
3597 }
3598
3599 static void
3600 handle_request(pcmk__request_t *request)
3601 {
3602 xmlNode *reply = NULL;
3603 const char *reason = NULL;
3604
3605 if (fenced_handlers == NULL) {
3606 fenced_register_handlers();
3607 }
3608 reply = pcmk__process_request(request, fenced_handlers);
3609 if (reply != NULL) {
3610 if (pcmk_is_set(request->flags, pcmk__request_reuse_options)
3611 && (request->ipc_client != NULL)) {
3612
3613
3614
3615
3616 pcmk__ipc_send_xml(request->ipc_client, request->ipc_id, reply,
3617 request->ipc_flags);
3618 request->ipc_client->request_id = 0;
3619 } else {
3620 stonith_send_reply(reply, request->call_options,
3621 request->peer, request->ipc_client);
3622 }
3623 free_xml(reply);
3624 }
3625
3626 reason = request->result.exit_reason;
3627 crm_debug("Processed %s request from %s %s: %s%s%s%s",
3628 request->op, pcmk__request_origin_type(request),
3629 pcmk__request_origin(request),
3630 pcmk_exec_status_str(request->result.execution_status),
3631 (reason == NULL)? "" : " (",
3632 (reason == NULL)? "" : reason,
3633 (reason == NULL)? "" : ")");
3634 }
3635
3636 static void
3637 handle_reply(pcmk__client_t *client, xmlNode *request, const char *remote_peer)
3638 {
3639
3640 char *op = crm_element_value_copy(request, PCMK__XA_ST_OP);
3641
3642 if (pcmk__str_eq(op, STONITH_OP_QUERY, pcmk__str_none)) {
3643 process_remote_stonith_query(request);
3644
3645 } else if (pcmk__str_any_of(op, STONITH_OP_NOTIFY, STONITH_OP_FENCE,
3646 NULL)) {
3647 fenced_process_fencing_reply(request);
3648
3649 } else {
3650 crm_err("Ignoring unknown %s reply from %s %s",
3651 pcmk__s(op, "untyped"), ((client == NULL)? "peer" : "client"),
3652 ((client == NULL)? remote_peer : pcmk__client_name(client)));
3653 crm_log_xml_warn(request, "UnknownOp");
3654 free(op);
3655 return;
3656 }
3657 crm_debug("Processed %s reply from %s %s",
3658 op, ((client == NULL)? "peer" : "client"),
3659 ((client == NULL)? remote_peer : pcmk__client_name(client)));
3660 free(op);
3661 }
3662
3663
3664
3665
3666
3667
3668
3669
3670
3671
3672
3673 void
3674 stonith_command(pcmk__client_t *client, uint32_t id, uint32_t flags,
3675 xmlNode *message, const char *remote_peer)
3676 {
3677 uint32_t call_options = st_opt_none;
3678 int rc = pcmk_rc_ok;
3679 bool is_reply = false;
3680
3681 CRM_CHECK(message != NULL, return);
3682
3683 if (get_xpath_object("//" PCMK__XE_ST_REPLY, message, LOG_NEVER) != NULL) {
3684 is_reply = true;
3685 }
3686
3687 rc = pcmk__xe_get_flags(message, PCMK__XA_ST_CALLOPT, &call_options,
3688 st_opt_none);
3689 if (rc != pcmk_rc_ok) {
3690 crm_warn("Couldn't parse options from message: %s", pcmk_rc_str(rc));
3691 }
3692
3693 crm_debug("Processing %ssynchronous %s %s %u from %s %s",
3694 pcmk_is_set(call_options, st_opt_sync_call)? "" : "a",
3695 crm_element_value(message, PCMK__XA_ST_OP),
3696 (is_reply? "reply" : "request"), id,
3697 ((client == NULL)? "peer" : "client"),
3698 ((client == NULL)? remote_peer : pcmk__client_name(client)));
3699
3700 if (pcmk_is_set(call_options, st_opt_sync_call)) {
3701 pcmk__assert((client == NULL) || (client->request_id == id));
3702 }
3703
3704 if (is_reply) {
3705 handle_reply(client, message, remote_peer);
3706 } else {
3707 pcmk__request_t request = {
3708 .ipc_client = client,
3709 .ipc_id = id,
3710 .ipc_flags = flags,
3711 .peer = remote_peer,
3712 .xml = message,
3713 .call_options = call_options,
3714 .result = PCMK__UNKNOWN_RESULT,
3715 };
3716
3717 request.op = crm_element_value_copy(request.xml, PCMK__XA_ST_OP);
3718 CRM_CHECK(request.op != NULL, return);
3719
3720 if (pcmk_is_set(request.call_options, st_opt_sync_call)) {
3721 pcmk__set_request_flags(&request, pcmk__request_sync);
3722 }
3723
3724 handle_request(&request);
3725 pcmk__reset_request(&request);
3726 }
3727 }