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