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