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