This source file includes following definitions.
- stonith_text2namespace
- stonith_namespace2text
- stonith_get_namespace
- stonith__watchdog_fencing_enabled_for_node_api
- stonith__watchdog_fencing_enabled_for_node
- log_action
- foreach_notify_entry
- stonith_connection_destroy
- create_device_registration_xml
- stonith_api_register_device
- stonith_api_remove_device
- stonith_api_remove_level_full
- stonith_api_remove_level
- create_level_registration_xml
- stonith_api_register_level_full
- stonith_api_register_level
- append_config_arg
- make_args
- stonith__destroy_action
- stonith__action_result
- stonith_action_create
- update_remaining_timeout
- svc_action_to_errno
- stonith_action_async_done
- stonith_action_async_forked
- internal_stonith_action_execute
- stonith_action_execute_async
- stonith__execute
- stonith_api_device_list
- stonith_api_device_metadata
- stonith_api_query
- stonith_api_call
- stonith_api_list
- stonith_api_monitor
- stonith_api_status
- stonith_api_fence_with_delay
- stonith_api_fence
- stonith_api_confirm
- stonith_api_history
- stonith_history_free
- stonithlib_GCompareFunc
- stonith_create_op
- stonith_destroy_op_callback
- stonith_api_signoff
- stonith_api_del_callback
- invoke_callback
- stonith_perform_callback
- stonith_async_timeout_handler
- set_callback_timeout
- update_callback_timeout
- stonith_dispatch_internal
- stonith_api_signon
- stonith_set_notification
- stonith_api_add_notification
- stonith_api_del_notification
- stonith_api_add_callback
- stonith_dump_pending_op
- stonith_dump_pending_callbacks
- xml_to_event
- event_free
- stonith_send_notification
- stonith_send_command
- stonith_dispatch
- stonith_api_free
- stonith_api_delete
- stonith_api_validate
- stonith_api_new
- stonith_api_connect_retry
- stonith_key_value_add
- stonith_key_value_freeall
- stonith_api_kick
- stonith_api_time
- stonith_agent_exists
- stonith_action_str
- parse_list_line
- stonith__parse_targets
- stonith__later_succeeded
- stonith__sort_history
- stonith_op_state_str
- stonith__first_matching_event
- stonith__event_state_pending
- stonith__event_state_eq
- stonith__event_state_neq
- stonith__device_parameter_flags
- get_stonith_provider
1
2
3
4
5
6
7
8
9
10 #include <crm_internal.h>
11 #include <unistd.h>
12 #include <stdlib.h>
13 #include <stdio.h>
14 #include <stdbool.h>
15 #include <string.h>
16 #include <ctype.h>
17 #include <libgen.h>
18 #include <inttypes.h>
19
20 #include <sys/stat.h>
21 #include <sys/types.h>
22 #include <sys/wait.h>
23
24 #include <glib.h>
25
26 #include <crm/crm.h>
27 #include <crm/stonith-ng.h>
28 #include <crm/fencing/internal.h>
29 #include <crm/msg_xml.h>
30 #include <crm/common/xml.h>
31 #include <crm/common/xml_internal.h>
32
33 #include <crm/common/mainloop.h>
34
35 #include "fencing_private.h"
36
37 CRM_TRACE_INIT_DATA(stonith);
38
39 struct stonith_action_s {
40
41 char *agent;
42 char *action;
43 char *victim;
44 GHashTable *args;
45 int timeout;
46 int async;
47 void *userdata;
48 void (*done_cb) (int pid, int status, const char *output, void *user_data);
49 void (*fork_cb) (int pid, void *user_data);
50
51 svc_action_t *svc_action;
52
53
54 time_t initial_start_time;
55 int tries;
56 int remaining_timeout;
57 int max_retries;
58
59 int pid;
60 int rc;
61 char *output;
62 char *error;
63 };
64
65 typedef struct stonith_private_s {
66 char *token;
67 crm_ipc_t *ipc;
68 mainloop_io_t *source;
69 GHashTable *stonith_op_callback_table;
70 GList *notify_list;
71 int notify_refcnt;
72 bool notify_deletes;
73
74 void (*op_callback) (stonith_t * st, stonith_callback_data_t * data);
75
76 } stonith_private_t;
77
78 typedef struct stonith_notify_client_s {
79 const char *event;
80 const char *obj_id;
81 const char *obj_type;
82 void (*notify) (stonith_t * st, stonith_event_t * e);
83 bool delete;
84
85 } stonith_notify_client_t;
86
87 typedef struct stonith_callback_client_s {
88 void (*callback) (stonith_t * st, stonith_callback_data_t * data);
89 const char *id;
90 void *user_data;
91 gboolean only_success;
92 gboolean allow_timeout_updates;
93 struct timer_rec_s *timer;
94
95 } stonith_callback_client_t;
96
97 struct notify_blob_s {
98 stonith_t *stonith;
99 xmlNode *xml;
100 };
101
102 struct timer_rec_s {
103 int call_id;
104 int timeout;
105 guint ref;
106 stonith_t *stonith;
107 };
108
109 typedef int (*stonith_op_t) (const char *, int, const char *, xmlNode *,
110 xmlNode *, xmlNode *, xmlNode **, xmlNode **);
111
112 bool stonith_dispatch(stonith_t * st);
113 xmlNode *stonith_create_op(int call_id, const char *token, const char *op, xmlNode * data,
114 int call_options);
115 static int stonith_send_command(stonith_t *stonith, const char *op,
116 xmlNode *data, xmlNode **output_data,
117 int call_options, int timeout);
118
119 static void stonith_connection_destroy(gpointer user_data);
120 static void stonith_send_notification(gpointer data, gpointer user_data);
121 static int internal_stonith_action_execute(stonith_action_t * action);
122 static void log_action(stonith_action_t *action, pid_t pid);
123
124
125
126
127
128
129
130
131 enum stonith_namespace
132 stonith_text2namespace(const char *namespace_s)
133 {
134 if (pcmk__str_eq(namespace_s, "any", pcmk__str_null_matches)) {
135 return st_namespace_any;
136
137 } else if (!strcmp(namespace_s, "redhat")
138 || !strcmp(namespace_s, "stonith-ng")) {
139 return st_namespace_rhcs;
140
141 } else if (!strcmp(namespace_s, "internal")) {
142 return st_namespace_internal;
143
144 } else if (!strcmp(namespace_s, "heartbeat")) {
145 return st_namespace_lha;
146 }
147 return st_namespace_invalid;
148 }
149
150
151
152
153
154
155
156
157 const char *
158 stonith_namespace2text(enum stonith_namespace st_namespace)
159 {
160 switch (st_namespace) {
161 case st_namespace_any: return "any";
162 case st_namespace_rhcs: return "stonith-ng";
163 case st_namespace_internal: return "internal";
164 case st_namespace_lha: return "heartbeat";
165 default: break;
166 }
167 return "unsupported";
168 }
169
170
171
172
173
174
175
176
177
178 enum stonith_namespace
179 stonith_get_namespace(const char *agent, const char *namespace_s)
180 {
181 if (pcmk__str_eq(namespace_s, "internal", pcmk__str_casei)) {
182 return st_namespace_internal;
183 }
184
185 if (stonith__agent_is_rhcs(agent)) {
186 return st_namespace_rhcs;
187 }
188
189 #if HAVE_STONITH_STONITH_H
190 if (stonith__agent_is_lha(agent)) {
191 return st_namespace_lha;
192 }
193 #endif
194
195 crm_err("Unknown fence agent: %s", agent);
196 return st_namespace_invalid;
197 }
198
199 gboolean
200 stonith__watchdog_fencing_enabled_for_node_api(stonith_t *st, const char *node)
201 {
202 gboolean rv = FALSE;
203 stonith_t *stonith_api = st?st:stonith_api_new();
204 char *list = NULL;
205
206 if(stonith_api) {
207 if (stonith_api->state == stonith_disconnected) {
208 int rc = stonith_api->cmds->connect(stonith_api, "stonith-api", NULL);
209
210 if (rc != pcmk_ok) {
211 crm_err("Failed connecting to Stonith-API for watchdog-fencing-query.");
212 }
213 }
214
215 if (stonith_api->state != stonith_disconnected) {
216
217
218
219 int rc = stonith_api->cmds->list(stonith_api, st_opt_sync_call, STONITH_WATCHDOG_ID, &list, 0);
220 if ((rc != pcmk_ok) || (list == NULL)) {
221
222
223
224
225 crm_warn("watchdog-fencing-query failed");
226 } else if (list[0] == '\0') {
227 rv = TRUE;
228 } else {
229 GList *targets = stonith__parse_targets(list);
230 rv = pcmk__str_in_list(node, targets, pcmk__str_casei);
231 g_list_free_full(targets, free);
232 }
233 free(list);
234 if (!st) {
235
236
237
238 stonith_api->cmds->disconnect(stonith_api);
239 }
240 }
241
242 if (!st) {
243 stonith_api_delete(stonith_api);
244 }
245 } else {
246 crm_err("Stonith-API for watchdog-fencing-query couldn't be created.");
247 }
248 crm_trace("Pacemaker assumes node %s %sto do watchdog-fencing.",
249 node, rv?"":"not ");
250 return rv;
251 }
252
253 gboolean
254 stonith__watchdog_fencing_enabled_for_node(const char *node)
255 {
256 return stonith__watchdog_fencing_enabled_for_node_api(NULL, node);
257 }
258
259 static void
260 log_action(stonith_action_t *action, pid_t pid)
261 {
262 if (action->output) {
263
264 char *prefix = crm_strdup_printf("%s[%d] stdout:", action->agent, pid);
265
266 crm_log_output(LOG_TRACE, prefix, action->output);
267 free(prefix);
268 }
269
270 if (action->error) {
271
272 char *prefix = crm_strdup_printf("%s[%d] stderr:", action->agent, pid);
273
274 crm_log_output(LOG_WARNING, prefix, action->error);
275 free(prefix);
276 }
277 }
278
279
280
281
282
283 static void
284 foreach_notify_entry (stonith_private_t *private,
285 GFunc func,
286 gpointer user_data)
287 {
288 private->notify_refcnt++;
289 g_list_foreach(private->notify_list, func, user_data);
290 private->notify_refcnt--;
291 if ((private->notify_refcnt == 0) &&
292 private->notify_deletes) {
293 GList *list_item = private->notify_list;
294
295 private->notify_deletes = FALSE;
296 while (list_item != NULL)
297 {
298 stonith_notify_client_t *list_client = list_item->data;
299 GList *next = g_list_next(list_item);
300
301 if (list_client->delete) {
302 free(list_client);
303 private->notify_list =
304 g_list_delete_link(private->notify_list, list_item);
305 }
306 list_item = next;
307 }
308 }
309 }
310
311 static void
312 stonith_connection_destroy(gpointer user_data)
313 {
314 stonith_t *stonith = user_data;
315 stonith_private_t *native = NULL;
316 struct notify_blob_s blob;
317
318 crm_trace("Sending destroyed notification");
319 blob.stonith = stonith;
320 blob.xml = create_xml_node(NULL, "notify");
321
322 native = stonith->st_private;
323 native->ipc = NULL;
324 native->source = NULL;
325
326 free(native->token); native->token = NULL;
327 stonith->state = stonith_disconnected;
328 crm_xml_add(blob.xml, F_TYPE, T_STONITH_NOTIFY);
329 crm_xml_add(blob.xml, F_SUBTYPE, T_STONITH_NOTIFY_DISCONNECT);
330
331 foreach_notify_entry(native, stonith_send_notification, &blob);
332 free_xml(blob.xml);
333 }
334
335 xmlNode *
336 create_device_registration_xml(const char *id, enum stonith_namespace namespace,
337 const char *agent, stonith_key_value_t *params,
338 const char *rsc_provides)
339 {
340 xmlNode *data = create_xml_node(NULL, F_STONITH_DEVICE);
341 xmlNode *args = create_xml_node(data, XML_TAG_ATTRS);
342
343 #if HAVE_STONITH_STONITH_H
344 if (namespace == st_namespace_any) {
345 namespace = stonith_get_namespace(agent, NULL);
346 }
347 if (namespace == st_namespace_lha) {
348 hash2field((gpointer) "plugin", (gpointer) agent, args);
349 agent = "fence_legacy";
350 }
351 #endif
352
353 crm_xml_add(data, XML_ATTR_ID, id);
354 crm_xml_add(data, F_STONITH_ORIGIN, __func__);
355 crm_xml_add(data, "agent", agent);
356 if ((namespace != st_namespace_any) && (namespace != st_namespace_invalid)) {
357 crm_xml_add(data, "namespace", stonith_namespace2text(namespace));
358 }
359 if (rsc_provides) {
360 crm_xml_add(data, "rsc_provides", rsc_provides);
361 }
362
363 for (; params; params = params->next) {
364 hash2field((gpointer) params->key, (gpointer) params->value, args);
365 }
366
367 return data;
368 }
369
370 static int
371 stonith_api_register_device(stonith_t * st, int call_options,
372 const char *id, const char *namespace, const char *agent,
373 stonith_key_value_t * params)
374 {
375 int rc = 0;
376 xmlNode *data = NULL;
377
378 data = create_device_registration_xml(id, stonith_text2namespace(namespace),
379 agent, params, NULL);
380
381 rc = stonith_send_command(st, STONITH_OP_DEVICE_ADD, data, NULL, call_options, 0);
382 free_xml(data);
383
384 return rc;
385 }
386
387 static int
388 stonith_api_remove_device(stonith_t * st, int call_options, const char *name)
389 {
390 int rc = 0;
391 xmlNode *data = NULL;
392
393 data = create_xml_node(NULL, F_STONITH_DEVICE);
394 crm_xml_add(data, F_STONITH_ORIGIN, __func__);
395 crm_xml_add(data, XML_ATTR_ID, name);
396 rc = stonith_send_command(st, STONITH_OP_DEVICE_DEL, data, NULL, call_options, 0);
397 free_xml(data);
398
399 return rc;
400 }
401
402 static int
403 stonith_api_remove_level_full(stonith_t *st, int options,
404 const char *node, const char *pattern,
405 const char *attr, const char *value, int level)
406 {
407 int rc = 0;
408 xmlNode *data = NULL;
409
410 CRM_CHECK(node || pattern || (attr && value), return -EINVAL);
411
412 data = create_xml_node(NULL, XML_TAG_FENCING_LEVEL);
413 crm_xml_add(data, F_STONITH_ORIGIN, __func__);
414
415 if (node) {
416 crm_xml_add(data, XML_ATTR_STONITH_TARGET, node);
417
418 } else if (pattern) {
419 crm_xml_add(data, XML_ATTR_STONITH_TARGET_PATTERN, pattern);
420
421 } else {
422 crm_xml_add(data, XML_ATTR_STONITH_TARGET_ATTRIBUTE, attr);
423 crm_xml_add(data, XML_ATTR_STONITH_TARGET_VALUE, value);
424 }
425
426 crm_xml_add_int(data, XML_ATTR_STONITH_INDEX, level);
427 rc = stonith_send_command(st, STONITH_OP_LEVEL_DEL, data, NULL, options, 0);
428 free_xml(data);
429
430 return rc;
431 }
432
433 static int
434 stonith_api_remove_level(stonith_t * st, int options, const char *node, int level)
435 {
436 return stonith_api_remove_level_full(st, options, node,
437 NULL, NULL, NULL, level);
438 }
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455 xmlNode *
456 create_level_registration_xml(const char *node, const char *pattern,
457 const char *attr, const char *value,
458 int level, stonith_key_value_t *device_list)
459 {
460 size_t len = 0;
461 char *list = NULL;
462 xmlNode *data;
463
464 CRM_CHECK(node || pattern || (attr && value), return NULL);
465
466 data = create_xml_node(NULL, XML_TAG_FENCING_LEVEL);
467 CRM_CHECK(data, return NULL);
468
469 crm_xml_add(data, F_STONITH_ORIGIN, __func__);
470 crm_xml_add_int(data, XML_ATTR_ID, level);
471 crm_xml_add_int(data, XML_ATTR_STONITH_INDEX, level);
472
473 if (node) {
474 crm_xml_add(data, XML_ATTR_STONITH_TARGET, node);
475
476 } else if (pattern) {
477 crm_xml_add(data, XML_ATTR_STONITH_TARGET_PATTERN, pattern);
478
479 } else {
480 crm_xml_add(data, XML_ATTR_STONITH_TARGET_ATTRIBUTE, attr);
481 crm_xml_add(data, XML_ATTR_STONITH_TARGET_VALUE, value);
482 }
483
484
485
486 for (; device_list; device_list = device_list->next) {
487 pcmk__add_separated_word(&list, &len, device_list->value, ",");
488 }
489
490 crm_xml_add(data, XML_ATTR_STONITH_DEVICES, list);
491
492 free(list);
493 return data;
494 }
495
496 static int
497 stonith_api_register_level_full(stonith_t * st, int options, const char *node,
498 const char *pattern,
499 const char *attr, const char *value,
500 int level, stonith_key_value_t *device_list)
501 {
502 int rc = 0;
503 xmlNode *data = create_level_registration_xml(node, pattern, attr, value,
504 level, device_list);
505 CRM_CHECK(data != NULL, return -EINVAL);
506
507 rc = stonith_send_command(st, STONITH_OP_LEVEL_ADD, data, NULL, options, 0);
508 free_xml(data);
509
510 return rc;
511 }
512
513 static int
514 stonith_api_register_level(stonith_t * st, int options, const char *node, int level,
515 stonith_key_value_t * device_list)
516 {
517 return stonith_api_register_level_full(st, options, node, NULL, NULL, NULL,
518 level, device_list);
519 }
520
521 static void
522 append_config_arg(gpointer key, gpointer value, gpointer user_data)
523 {
524
525
526
527
528
529 if (!pcmk__str_eq(key, STONITH_ATTR_ACTION_OP, pcmk__str_casei)
530 && !pcmk_stonith_param(key)
531 && (strstr(key, CRM_META) == NULL)
532 && !pcmk__str_eq(key, "crm_feature_set", pcmk__str_casei)) {
533
534 crm_trace("Passing %s=%s with fence action",
535 (const char *) key, (const char *) (value? value : ""));
536 g_hash_table_insert((GHashTable *) user_data,
537 strdup(key), strdup(value? value : ""));
538 }
539 }
540
541 static GHashTable *
542 make_args(const char *agent, const char *action, const char *victim,
543 uint32_t victim_nodeid, GHashTable * device_args,
544 GHashTable * port_map, const char *host_arg)
545 {
546 GHashTable *arg_list = NULL;
547 const char *value = NULL;
548
549 CRM_CHECK(action != NULL, return NULL);
550
551 arg_list = pcmk__strkey_table(free, free);
552
553
554 if (device_args) {
555 char buffer[512];
556
557 snprintf(buffer, sizeof(buffer), "pcmk_%s_action", action);
558 value = g_hash_table_lookup(device_args, buffer);
559 if (value) {
560 crm_debug("Substituting '%s' for fence action %s targeting %s",
561 value, action, victim);
562 action = value;
563 }
564 }
565 g_hash_table_insert(arg_list, strdup(STONITH_ATTR_ACTION_OP),
566 strdup(action));
567
568
569
570
571 if (victim && device_args) {
572 const char *param = NULL;
573
574
575
576
577 g_hash_table_insert(arg_list, strdup("nodename"), strdup(victim));
578
579
580 if (victim_nodeid) {
581 char *nodeid = crm_strdup_printf("%" PRIu32, victim_nodeid);
582
583
584 crm_info("Passing '%s' as nodeid with fence action '%s' targeting %s",
585 nodeid, action, victim);
586 g_hash_table_insert(arg_list, strdup("nodeid"), nodeid);
587 }
588
589
590 param = g_hash_table_lookup(device_args, PCMK_STONITH_HOST_ARGUMENT);
591 if (!pcmk__str_eq(agent, "fence_legacy", pcmk__str_none)
592 && !pcmk__str_eq(param, "none", pcmk__str_casei)) {
593
594 if (param == NULL) {
595
596
597
598 param = (host_arg == NULL)? "port" : host_arg;
599 }
600 value = g_hash_table_lookup(device_args, param);
601
602 if (pcmk__str_eq(value, "dynamic",
603 pcmk__str_casei|pcmk__str_null_matches)) {
604
605
606
607 const char *alias = NULL;
608
609 if (port_map) {
610 alias = g_hash_table_lookup(port_map, victim);
611 }
612 if (alias == NULL) {
613 alias = victim;
614 }
615 crm_debug("Passing %s='%s' with fence action %s targeting %s",
616 param, alias, action, victim);
617 g_hash_table_insert(arg_list, strdup(param), strdup(alias));
618 }
619 }
620 }
621
622 if (device_args) {
623 g_hash_table_foreach(device_args, append_config_arg, arg_list);
624 }
625
626 return arg_list;
627 }
628
629
630
631
632
633
634
635 void
636 stonith__destroy_action(stonith_action_t *action)
637 {
638 if (action) {
639 free(action->agent);
640 if (action->args) {
641 g_hash_table_destroy(action->args);
642 }
643 free(action->action);
644 free(action->victim);
645 if (action->svc_action) {
646 services_action_free(action->svc_action);
647 }
648 free(action->output);
649 free(action->error);
650 free(action);
651 }
652 }
653
654
655
656
657
658
659
660
661
662
663
664
665
666 void
667 stonith__action_result(stonith_action_t *action, int *rc, char **output,
668 char **error_output)
669 {
670 if (rc) {
671 *rc = pcmk_ok;
672 }
673 if (output) {
674 *output = NULL;
675 }
676 if (error_output) {
677 *error_output = NULL;
678 }
679 if (action != NULL) {
680 if (rc) {
681 *rc = action->rc;
682 }
683 if (output && action->output) {
684 *output = action->output;
685 action->output = NULL;
686 }
687 if (error_output && action->error) {
688 *error_output = action->error;
689 action->error = NULL;
690 }
691 }
692 }
693
694 #define FAILURE_MAX_RETRIES 2
695 stonith_action_t *
696 stonith_action_create(const char *agent,
697 const char *_action,
698 const char *victim,
699 uint32_t victim_nodeid,
700 int timeout, GHashTable * device_args,
701 GHashTable * port_map, const char *host_arg)
702 {
703 stonith_action_t *action;
704
705 action = calloc(1, sizeof(stonith_action_t));
706 action->args = make_args(agent, _action, victim, victim_nodeid,
707 device_args, port_map, host_arg);
708 crm_debug("Preparing '%s' action for %s using agent %s",
709 _action, (victim? victim : "no target"), agent);
710 action->agent = strdup(agent);
711 action->action = strdup(_action);
712 if (victim) {
713 action->victim = strdup(victim);
714 }
715 action->timeout = action->remaining_timeout = timeout;
716 action->max_retries = FAILURE_MAX_RETRIES;
717
718 if (device_args) {
719 char buffer[512];
720 const char *value = NULL;
721
722 snprintf(buffer, sizeof(buffer), "pcmk_%s_retries", _action);
723 value = g_hash_table_lookup(device_args, buffer);
724
725 if (value) {
726 action->max_retries = atoi(value);
727 }
728 }
729
730 return action;
731 }
732
733 static gboolean
734 update_remaining_timeout(stonith_action_t * action)
735 {
736 int diff = time(NULL) - action->initial_start_time;
737
738 if (action->tries >= action->max_retries) {
739 crm_info("Attempted to execute agent %s (%s) the maximum number of times (%d) allowed",
740 action->agent, action->action, action->max_retries);
741 action->remaining_timeout = 0;
742 } else if ((action->rc != -ETIME) && diff < (action->timeout * 0.7)) {
743
744
745 action->remaining_timeout = action->timeout - diff;
746 } else {
747 action->remaining_timeout = 0;
748 }
749 return action->remaining_timeout ? TRUE : FALSE;
750 }
751
752 static int
753 svc_action_to_errno(svc_action_t *svc_action) {
754 int rv = pcmk_ok;
755
756 if (svc_action->status == PCMK_EXEC_TIMEOUT) {
757 rv = -ETIME;
758
759 } else if (svc_action->rc != PCMK_OCF_OK) {
760
761
762
763 if (svc_action->stderr_data == NULL) {
764 rv = -ENODATA;
765
766 } else if (strstr(svc_action->stderr_data, "imed out")) {
767
768 rv = -ETIME;
769
770 } else if (strstr(svc_action->stderr_data, "Unrecognised action")) {
771 rv = -EOPNOTSUPP;
772
773 } else {
774 rv = -pcmk_err_generic;
775 }
776 }
777 return rv;
778 }
779
780 static void
781 stonith_action_async_done(svc_action_t *svc_action)
782 {
783 stonith_action_t *action = (stonith_action_t *) svc_action->cb_data;
784
785 action->rc = svc_action_to_errno(svc_action);
786 action->output = svc_action->stdout_data;
787 svc_action->stdout_data = NULL;
788 action->error = svc_action->stderr_data;
789 svc_action->stderr_data = NULL;
790
791 svc_action->params = NULL;
792
793 crm_debug("Child process %d performing action '%s' exited with rc %d",
794 action->pid, action->action, svc_action->rc);
795
796 log_action(action, action->pid);
797
798 if (action->rc != pcmk_ok && update_remaining_timeout(action)) {
799 int rc = internal_stonith_action_execute(action);
800 if (rc == pcmk_ok) {
801 return;
802 }
803 }
804
805 if (action->done_cb) {
806 action->done_cb(action->pid, action->rc, action->output, action->userdata);
807 }
808
809 action->svc_action = NULL;
810 stonith__destroy_action(action);
811 }
812
813 static void
814 stonith_action_async_forked(svc_action_t *svc_action)
815 {
816 stonith_action_t *action = (stonith_action_t *) svc_action->cb_data;
817
818 action->pid = svc_action->pid;
819 action->svc_action = svc_action;
820
821 if (action->fork_cb) {
822 (action->fork_cb) (svc_action->pid, action->userdata);
823 }
824
825 crm_trace("Child process %d performing action '%s' successfully forked",
826 action->pid, action->action);
827 }
828
829 static int
830 internal_stonith_action_execute(stonith_action_t * action)
831 {
832 int rc = -EPROTO;
833 int is_retry = 0;
834 svc_action_t *svc_action = NULL;
835 static int stonith_sequence = 0;
836 char *buffer = NULL;
837
838 if ((action == NULL) || (action->action == NULL) || (action->args == NULL)
839 || (action->agent == NULL)) {
840 return -EPROTO;
841 }
842
843 if (!action->tries) {
844 action->initial_start_time = time(NULL);
845 }
846 action->tries++;
847
848 if (action->tries > 1) {
849 crm_info("Attempt %d to execute %s (%s). remaining timeout is %d",
850 action->tries, action->agent, action->action, action->remaining_timeout);
851 is_retry = 1;
852 }
853
854 buffer = crm_strdup_printf(PCMK__FENCE_BINDIR "/%s",
855 basename(action->agent));
856 svc_action = services_action_create_generic(buffer, NULL);
857 free(buffer);
858
859 if (svc_action->rc != PCMK_OCF_UNKNOWN) {
860 services_action_free(svc_action);
861 return -E2BIG;
862 }
863
864 svc_action->timeout = 1000 * action->remaining_timeout;
865 svc_action->standard = strdup(PCMK_RESOURCE_CLASS_STONITH);
866 svc_action->id = crm_strdup_printf("%s_%s_%d", basename(action->agent),
867 action->action, action->tries);
868 svc_action->agent = strdup(action->agent);
869 svc_action->sequence = stonith_sequence++;
870 svc_action->params = action->args;
871 svc_action->cb_data = (void *) action;
872 svc_action->flags = pcmk__set_flags_as(__func__, __LINE__,
873 LOG_TRACE, "Action",
874 svc_action->id, svc_action->flags,
875 SVC_ACTION_NON_BLOCKED,
876 "SVC_ACTION_NON_BLOCKED");
877
878
879 if (is_retry) {
880 free(action->output);
881 action->output = NULL;
882 free(action->error);
883 action->error = NULL;
884 sleep(1);
885 }
886
887 if (action->async) {
888
889 if (services_action_async_fork_notify(svc_action,
890 &stonith_action_async_done,
891 &stonith_action_async_forked)) {
892 return pcmk_ok;
893 }
894
895 } else if (services_action_sync(svc_action)) {
896 rc = pcmk_ok;
897 action->rc = svc_action_to_errno(svc_action);
898 action->output = svc_action->stdout_data;
899 svc_action->stdout_data = NULL;
900 action->error = svc_action->stderr_data;
901 svc_action->stderr_data = NULL;
902
903 } else {
904 action->rc = -ECONNABORTED;
905 rc = action->rc;
906 }
907
908 svc_action->params = NULL;
909 services_action_free(svc_action);
910 return rc;
911 }
912
913
914
915
916
917
918
919
920
921
922
923
924 int
925 stonith_action_execute_async(stonith_action_t * action,
926 void *userdata,
927 void (*done) (int pid, int rc, const char *output,
928 void *user_data),
929 void (*fork_cb) (int pid, void *user_data))
930 {
931 if (!action) {
932 return -EINVAL;
933 }
934
935 action->userdata = userdata;
936 action->done_cb = done;
937 action->fork_cb = fork_cb;
938 action->async = 1;
939
940 return internal_stonith_action_execute(action);
941 }
942
943
944
945
946
947
948
949
950
951 int
952 stonith__execute(stonith_action_t *action)
953 {
954 int rc = pcmk_ok;
955
956 CRM_CHECK(action != NULL, return -EINVAL);
957
958
959 do {
960 rc = internal_stonith_action_execute(action);
961 } while ((rc != pcmk_ok) && update_remaining_timeout(action));
962
963 return rc;
964 }
965
966 static int
967 stonith_api_device_list(stonith_t * stonith, int call_options, const char *namespace,
968 stonith_key_value_t ** devices, int timeout)
969 {
970 int count = 0;
971 enum stonith_namespace ns = stonith_text2namespace(namespace);
972
973 if (devices == NULL) {
974 crm_err("Parameter error: stonith_api_device_list");
975 return -EFAULT;
976 }
977
978 #if HAVE_STONITH_STONITH_H
979
980 if ((ns == st_namespace_any) || (ns == st_namespace_lha)) {
981 count += stonith__list_lha_agents(devices);
982 }
983 #endif
984
985
986 if ((ns == st_namespace_any) || (ns == st_namespace_rhcs)) {
987 count += stonith__list_rhcs_agents(devices);
988 }
989
990 return count;
991 }
992
993 static int
994 stonith_api_device_metadata(stonith_t * stonith, int call_options, const char *agent,
995 const char *namespace, char **output, int timeout)
996 {
997
998
999
1000
1001 enum stonith_namespace ns = stonith_get_namespace(agent, namespace);
1002
1003 crm_trace("Looking up metadata for %s agent %s",
1004 stonith_namespace2text(ns), agent);
1005
1006 switch (ns) {
1007 case st_namespace_rhcs:
1008 return stonith__rhcs_metadata(agent, timeout, output);
1009
1010 #if HAVE_STONITH_STONITH_H
1011 case st_namespace_lha:
1012 return stonith__lha_metadata(agent, timeout, output);
1013 #endif
1014
1015 default:
1016 crm_err("Can't get fence agent '%s' meta-data: No such agent",
1017 agent);
1018 break;
1019 }
1020 return -ENODEV;
1021 }
1022
1023 static int
1024 stonith_api_query(stonith_t * stonith, int call_options, const char *target,
1025 stonith_key_value_t ** devices, int timeout)
1026 {
1027 int rc = 0, lpc = 0, max = 0;
1028
1029 xmlNode *data = NULL;
1030 xmlNode *output = NULL;
1031 xmlXPathObjectPtr xpathObj = NULL;
1032
1033 CRM_CHECK(devices != NULL, return -EINVAL);
1034
1035 data = create_xml_node(NULL, F_STONITH_DEVICE);
1036 crm_xml_add(data, F_STONITH_ORIGIN, __func__);
1037 crm_xml_add(data, F_STONITH_TARGET, target);
1038 crm_xml_add(data, F_STONITH_ACTION, "off");
1039 rc = stonith_send_command(stonith, STONITH_OP_QUERY, data, &output, call_options, timeout);
1040
1041 if (rc < 0) {
1042 return rc;
1043 }
1044
1045 xpathObj = xpath_search(output, "//@agent");
1046 if (xpathObj) {
1047 max = numXpathResults(xpathObj);
1048
1049 for (lpc = 0; lpc < max; lpc++) {
1050 xmlNode *match = getXpathResult(xpathObj, lpc);
1051
1052 CRM_LOG_ASSERT(match != NULL);
1053 if(match != NULL) {
1054 xmlChar *match_path = xmlGetNodePath(match);
1055
1056 crm_info("%s[%d] = %s", "//@agent", lpc, match_path);
1057 free(match_path);
1058 *devices = stonith_key_value_add(*devices, NULL, crm_element_value(match, XML_ATTR_ID));
1059 }
1060 }
1061
1062 freeXpathObject(xpathObj);
1063 }
1064
1065 free_xml(output);
1066 free_xml(data);
1067 return max;
1068 }
1069
1070 static int
1071 stonith_api_call(stonith_t * stonith,
1072 int call_options,
1073 const char *id,
1074 const char *action, const char *victim, int timeout, xmlNode ** output)
1075 {
1076 int rc = 0;
1077 xmlNode *data = NULL;
1078
1079 data = create_xml_node(NULL, F_STONITH_DEVICE);
1080 crm_xml_add(data, F_STONITH_ORIGIN, __func__);
1081 crm_xml_add(data, F_STONITH_DEVICE, id);
1082 crm_xml_add(data, F_STONITH_ACTION, action);
1083 crm_xml_add(data, F_STONITH_TARGET, victim);
1084
1085 rc = stonith_send_command(stonith, STONITH_OP_EXEC, data, output, call_options, timeout);
1086 free_xml(data);
1087
1088 return rc;
1089 }
1090
1091 static int
1092 stonith_api_list(stonith_t * stonith, int call_options, const char *id, char **list_info,
1093 int timeout)
1094 {
1095 int rc;
1096 xmlNode *output = NULL;
1097
1098 rc = stonith_api_call(stonith, call_options, id, "list", NULL, timeout, &output);
1099
1100 if (output && list_info) {
1101 const char *list_str;
1102
1103 list_str = crm_element_value(output, "st_output");
1104
1105 if (list_str) {
1106 *list_info = strdup(list_str);
1107 }
1108 }
1109
1110 if (output) {
1111 free_xml(output);
1112 }
1113
1114 return rc;
1115 }
1116
1117 static int
1118 stonith_api_monitor(stonith_t * stonith, int call_options, const char *id, int timeout)
1119 {
1120 return stonith_api_call(stonith, call_options, id, "monitor", NULL, timeout, NULL);
1121 }
1122
1123 static int
1124 stonith_api_status(stonith_t * stonith, int call_options, const char *id, const char *port,
1125 int timeout)
1126 {
1127 return stonith_api_call(stonith, call_options, id, "status", port, timeout, NULL);
1128 }
1129
1130 static int
1131 stonith_api_fence_with_delay(stonith_t * stonith, int call_options, const char *node,
1132 const char *action, int timeout, int tolerance, int delay)
1133 {
1134 int rc = 0;
1135 xmlNode *data = NULL;
1136
1137 data = create_xml_node(NULL, __func__);
1138 crm_xml_add(data, F_STONITH_TARGET, node);
1139 crm_xml_add(data, F_STONITH_ACTION, action);
1140 crm_xml_add_int(data, F_STONITH_TIMEOUT, timeout);
1141 crm_xml_add_int(data, F_STONITH_TOLERANCE, tolerance);
1142 crm_xml_add_int(data, F_STONITH_DELAY, delay);
1143
1144 rc = stonith_send_command(stonith, STONITH_OP_FENCE, data, NULL, call_options, timeout);
1145 free_xml(data);
1146
1147 return rc;
1148 }
1149
1150 static int
1151 stonith_api_fence(stonith_t * stonith, int call_options, const char *node, const char *action,
1152 int timeout, int tolerance)
1153 {
1154 return stonith_api_fence_with_delay(stonith, call_options, node, action,
1155 timeout, tolerance, 0);
1156 }
1157
1158 static int
1159 stonith_api_confirm(stonith_t * stonith, int call_options, const char *target)
1160 {
1161 stonith__set_call_options(call_options, target, st_opt_manual_ack);
1162 return stonith_api_fence(stonith, call_options, target, "off", 0, 0);
1163 }
1164
1165 static int
1166 stonith_api_history(stonith_t * stonith, int call_options, const char *node,
1167 stonith_history_t ** history, int timeout)
1168 {
1169 int rc = 0;
1170 xmlNode *data = NULL;
1171 xmlNode *output = NULL;
1172 stonith_history_t *last = NULL;
1173
1174 *history = NULL;
1175
1176 if (node) {
1177 data = create_xml_node(NULL, __func__);
1178 crm_xml_add(data, F_STONITH_TARGET, node);
1179 }
1180
1181 stonith__set_call_options(call_options, node, st_opt_sync_call);
1182 rc = stonith_send_command(stonith, STONITH_OP_FENCE_HISTORY, data, &output,
1183 call_options, timeout);
1184 free_xml(data);
1185
1186 if (rc == 0) {
1187 xmlNode *op = NULL;
1188 xmlNode *reply = get_xpath_object("//" F_STONITH_HISTORY_LIST, output,
1189 LOG_NEVER);
1190
1191 for (op = pcmk__xml_first_child(reply); op != NULL;
1192 op = pcmk__xml_next(op)) {
1193 stonith_history_t *kvp;
1194 long long completed;
1195 long long completed_nsec = 0L;
1196
1197 kvp = calloc(1, sizeof(stonith_history_t));
1198 kvp->target = crm_element_value_copy(op, F_STONITH_TARGET);
1199 kvp->action = crm_element_value_copy(op, F_STONITH_ACTION);
1200 kvp->origin = crm_element_value_copy(op, F_STONITH_ORIGIN);
1201 kvp->delegate = crm_element_value_copy(op, F_STONITH_DELEGATE);
1202 kvp->client = crm_element_value_copy(op, F_STONITH_CLIENTNAME);
1203 crm_element_value_ll(op, F_STONITH_DATE, &completed);
1204 kvp->completed = (time_t) completed;
1205 crm_element_value_ll(op, F_STONITH_DATE_NSEC, &completed_nsec);
1206 kvp->completed_nsec = completed_nsec;
1207 crm_element_value_int(op, F_STONITH_STATE, &kvp->state);
1208
1209 if (last) {
1210 last->next = kvp;
1211 } else {
1212 *history = kvp;
1213 }
1214 last = kvp;
1215 }
1216 }
1217
1218 free_xml(output);
1219
1220 return rc;
1221 }
1222
1223 void stonith_history_free(stonith_history_t *history)
1224 {
1225 stonith_history_t *hp, *hp_old;
1226
1227 for (hp = history; hp; hp_old = hp, hp = hp->next, free(hp_old)) {
1228 free(hp->target);
1229 free(hp->action);
1230 free(hp->origin);
1231 free(hp->delegate);
1232 free(hp->client);
1233 }
1234 }
1235
1236 static gint
1237 stonithlib_GCompareFunc(gconstpointer a, gconstpointer b)
1238 {
1239 int rc = 0;
1240 const stonith_notify_client_t *a_client = a;
1241 const stonith_notify_client_t *b_client = b;
1242
1243 if (a_client->delete || b_client->delete) {
1244
1245 return -1;
1246 }
1247 CRM_CHECK(a_client->event != NULL && b_client->event != NULL, return 0);
1248 rc = strcmp(a_client->event, b_client->event);
1249 if (rc == 0) {
1250 if (a_client->notify == NULL || b_client->notify == NULL) {
1251 return 0;
1252
1253 } else if (a_client->notify == b_client->notify) {
1254 return 0;
1255
1256 } else if (((long)a_client->notify) < ((long)b_client->notify)) {
1257 crm_err("callbacks for %s are not equal: %p vs. %p",
1258 a_client->event, a_client->notify, b_client->notify);
1259 return -1;
1260 }
1261 crm_err("callbacks for %s are not equal: %p vs. %p",
1262 a_client->event, a_client->notify, b_client->notify);
1263 return 1;
1264 }
1265 return rc;
1266 }
1267
1268 xmlNode *
1269 stonith_create_op(int call_id, const char *token, const char *op, xmlNode * data, int call_options)
1270 {
1271 xmlNode *op_msg = create_xml_node(NULL, "stonith_command");
1272
1273 CRM_CHECK(op_msg != NULL, return NULL);
1274 CRM_CHECK(token != NULL, return NULL);
1275
1276 crm_xml_add(op_msg, F_XML_TAGNAME, "stonith_command");
1277
1278 crm_xml_add(op_msg, F_TYPE, T_STONITH_NG);
1279 crm_xml_add(op_msg, F_STONITH_CALLBACK_TOKEN, token);
1280 crm_xml_add(op_msg, F_STONITH_OPERATION, op);
1281 crm_xml_add_int(op_msg, F_STONITH_CALLID, call_id);
1282 crm_trace("Sending call options: %.8lx, %d", (long)call_options, call_options);
1283 crm_xml_add_int(op_msg, F_STONITH_CALLOPTS, call_options);
1284
1285 if (data != NULL) {
1286 add_message_xml(op_msg, F_STONITH_CALLDATA, data);
1287 }
1288
1289 return op_msg;
1290 }
1291
1292 static void
1293 stonith_destroy_op_callback(gpointer data)
1294 {
1295 stonith_callback_client_t *blob = data;
1296
1297 if (blob->timer && blob->timer->ref > 0) {
1298 g_source_remove(blob->timer->ref);
1299 }
1300 free(blob->timer);
1301 free(blob);
1302 }
1303
1304 static int
1305 stonith_api_signoff(stonith_t * stonith)
1306 {
1307 stonith_private_t *native = stonith->st_private;
1308
1309 crm_debug("Disconnecting from the fencer");
1310
1311 if (native->source != NULL) {
1312
1313 mainloop_del_ipc_client(native->source);
1314 native->source = NULL;
1315 native->ipc = NULL;
1316
1317 } else if (native->ipc) {
1318
1319 crm_ipc_t *ipc = native->ipc;
1320
1321 native->ipc = NULL;
1322 crm_ipc_close(ipc);
1323 crm_ipc_destroy(ipc);
1324 }
1325
1326 free(native->token); native->token = NULL;
1327 stonith->state = stonith_disconnected;
1328 return pcmk_ok;
1329 }
1330
1331 static int
1332 stonith_api_del_callback(stonith_t * stonith, int call_id, bool all_callbacks)
1333 {
1334 stonith_private_t *private = stonith->st_private;
1335
1336 if (all_callbacks) {
1337 private->op_callback = NULL;
1338 g_hash_table_destroy(private->stonith_op_callback_table);
1339 private->stonith_op_callback_table = pcmk__intkey_table(stonith_destroy_op_callback);
1340
1341 } else if (call_id == 0) {
1342 private->op_callback = NULL;
1343
1344 } else {
1345 pcmk__intkey_table_remove(private->stonith_op_callback_table, call_id);
1346 }
1347 return pcmk_ok;
1348 }
1349
1350 static void
1351 invoke_callback(stonith_t * st, int call_id, int rc, void *userdata,
1352 void (*callback) (stonith_t * st, stonith_callback_data_t * data))
1353 {
1354 stonith_callback_data_t data = { 0, };
1355
1356 data.call_id = call_id;
1357 data.rc = rc;
1358 data.userdata = userdata;
1359
1360 callback(st, &data);
1361 }
1362
1363 static void
1364 stonith_perform_callback(stonith_t * stonith, xmlNode * msg, int call_id, int rc)
1365 {
1366 stonith_private_t *private = NULL;
1367 stonith_callback_client_t *blob = NULL;
1368 stonith_callback_client_t local_blob;
1369
1370 CRM_CHECK(stonith != NULL, return);
1371 CRM_CHECK(stonith->st_private != NULL, return);
1372
1373 private = stonith->st_private;
1374
1375 local_blob.id = NULL;
1376 local_blob.callback = NULL;
1377 local_blob.user_data = NULL;
1378 local_blob.only_success = FALSE;
1379
1380 if (msg != NULL) {
1381 crm_element_value_int(msg, F_STONITH_RC, &rc);
1382 crm_element_value_int(msg, F_STONITH_CALLID, &call_id);
1383 }
1384
1385 CRM_CHECK(call_id > 0, crm_log_xml_err(msg, "Bad result"));
1386
1387 blob = pcmk__intkey_table_lookup(private->stonith_op_callback_table,
1388 call_id);
1389 if (blob != NULL) {
1390 local_blob = *blob;
1391 blob = NULL;
1392
1393 stonith_api_del_callback(stonith, call_id, FALSE);
1394
1395 } else {
1396 crm_trace("No callback found for call %d", call_id);
1397 local_blob.callback = NULL;
1398 }
1399
1400 if (local_blob.callback != NULL && (rc == pcmk_ok || local_blob.only_success == FALSE)) {
1401 crm_trace("Invoking callback %s for call %d", crm_str(local_blob.id), call_id);
1402 invoke_callback(stonith, call_id, rc, local_blob.user_data, local_blob.callback);
1403
1404 } else if (private->op_callback == NULL && rc != pcmk_ok) {
1405 crm_warn("Fencing command failed: %s", pcmk_strerror(rc));
1406 crm_log_xml_debug(msg, "Failed fence update");
1407 }
1408
1409 if (private->op_callback != NULL) {
1410 crm_trace("Invoking global callback for call %d", call_id);
1411 invoke_callback(stonith, call_id, rc, NULL, private->op_callback);
1412 }
1413 crm_trace("OP callback activated.");
1414 }
1415
1416 static gboolean
1417 stonith_async_timeout_handler(gpointer data)
1418 {
1419 struct timer_rec_s *timer = data;
1420
1421 crm_err("Async call %d timed out after %dms", timer->call_id, timer->timeout);
1422 stonith_perform_callback(timer->stonith, NULL, timer->call_id, -ETIME);
1423
1424
1425
1426
1427 return TRUE;
1428 }
1429
1430 static void
1431 set_callback_timeout(stonith_callback_client_t * callback, stonith_t * stonith, int call_id,
1432 int timeout)
1433 {
1434 struct timer_rec_s *async_timer = callback->timer;
1435
1436 if (timeout <= 0) {
1437 return;
1438 }
1439
1440 if (!async_timer) {
1441 async_timer = calloc(1, sizeof(struct timer_rec_s));
1442 callback->timer = async_timer;
1443 }
1444
1445 async_timer->stonith = stonith;
1446 async_timer->call_id = call_id;
1447
1448
1449
1450 async_timer->timeout = (timeout + 60) * 1000;
1451 if (async_timer->ref) {
1452 g_source_remove(async_timer->ref);
1453 }
1454 async_timer->ref =
1455 g_timeout_add(async_timer->timeout, stonith_async_timeout_handler, async_timer);
1456 }
1457
1458 static void
1459 update_callback_timeout(int call_id, int timeout, stonith_t * st)
1460 {
1461 stonith_callback_client_t *callback = NULL;
1462 stonith_private_t *private = st->st_private;
1463
1464 callback = pcmk__intkey_table_lookup(private->stonith_op_callback_table,
1465 call_id);
1466 if (!callback || !callback->allow_timeout_updates) {
1467 return;
1468 }
1469
1470 set_callback_timeout(callback, st, call_id, timeout);
1471 }
1472
1473 static int
1474 stonith_dispatch_internal(const char *buffer, ssize_t length, gpointer userdata)
1475 {
1476 const char *type = NULL;
1477 struct notify_blob_s blob;
1478
1479 stonith_t *st = userdata;
1480 stonith_private_t *private = NULL;
1481
1482 CRM_ASSERT(st != NULL);
1483 private = st->st_private;
1484
1485 blob.stonith = st;
1486 blob.xml = string2xml(buffer);
1487 if (blob.xml == NULL) {
1488 crm_warn("Received malformed message from fencer: %s", buffer);
1489 return 0;
1490 }
1491
1492
1493 type = crm_element_value(blob.xml, F_TYPE);
1494 crm_trace("Activating %s callbacks...", type);
1495
1496 if (pcmk__str_eq(type, T_STONITH_NG, pcmk__str_casei)) {
1497 stonith_perform_callback(st, blob.xml, 0, 0);
1498
1499 } else if (pcmk__str_eq(type, T_STONITH_NOTIFY, pcmk__str_casei)) {
1500 foreach_notify_entry(private, stonith_send_notification, &blob);
1501 } else if (pcmk__str_eq(type, T_STONITH_TIMEOUT_VALUE, pcmk__str_casei)) {
1502 int call_id = 0;
1503 int timeout = 0;
1504
1505 crm_element_value_int(blob.xml, F_STONITH_TIMEOUT, &timeout);
1506 crm_element_value_int(blob.xml, F_STONITH_CALLID, &call_id);
1507
1508 update_callback_timeout(call_id, timeout, st);
1509 } else {
1510 crm_err("Unknown message type: %s", type);
1511 crm_log_xml_warn(blob.xml, "BadReply");
1512 }
1513
1514 free_xml(blob.xml);
1515 return 1;
1516 }
1517
1518 static int
1519 stonith_api_signon(stonith_t * stonith, const char *name, int *stonith_fd)
1520 {
1521 int rc = pcmk_ok;
1522 stonith_private_t *native = NULL;
1523 const char *display_name = name? name : "client";
1524
1525 struct ipc_client_callbacks st_callbacks = {
1526 .dispatch = stonith_dispatch_internal,
1527 .destroy = stonith_connection_destroy
1528 };
1529
1530 CRM_CHECK(stonith != NULL, return -EINVAL);
1531
1532 native = stonith->st_private;
1533 CRM_ASSERT(native != NULL);
1534
1535 crm_debug("Attempting fencer connection by %s with%s mainloop",
1536 display_name, (stonith_fd? "out" : ""));
1537
1538 stonith->state = stonith_connected_command;
1539 if (stonith_fd) {
1540
1541 native->ipc = crm_ipc_new("stonith-ng", 0);
1542
1543 if (native->ipc && crm_ipc_connect(native->ipc)) {
1544 *stonith_fd = crm_ipc_get_fd(native->ipc);
1545 } else if (native->ipc) {
1546 crm_ipc_close(native->ipc);
1547 crm_ipc_destroy(native->ipc);
1548 native->ipc = NULL;
1549 }
1550
1551 } else {
1552
1553 native->source =
1554 mainloop_add_ipc_client("stonith-ng", G_PRIORITY_MEDIUM, 0, stonith, &st_callbacks);
1555 native->ipc = mainloop_get_ipc_client(native->source);
1556 }
1557
1558 if (native->ipc == NULL) {
1559 rc = -ENOTCONN;
1560 } else {
1561 xmlNode *reply = NULL;
1562 xmlNode *hello = create_xml_node(NULL, "stonith_command");
1563
1564 crm_xml_add(hello, F_TYPE, T_STONITH_NG);
1565 crm_xml_add(hello, F_STONITH_OPERATION, CRM_OP_REGISTER);
1566 crm_xml_add(hello, F_STONITH_CLIENTNAME, name);
1567 rc = crm_ipc_send(native->ipc, hello, crm_ipc_client_response, -1, &reply);
1568
1569 if (rc < 0) {
1570 crm_debug("Couldn't register with the fencer: %s "
1571 CRM_XS " rc=%d", pcmk_strerror(rc), rc);
1572 rc = -ECOMM;
1573
1574 } else if (reply == NULL) {
1575 crm_debug("Couldn't register with the fencer: no reply");
1576 rc = -EPROTO;
1577
1578 } else {
1579 const char *msg_type = crm_element_value(reply, F_STONITH_OPERATION);
1580
1581 native->token = crm_element_value_copy(reply, F_STONITH_CLIENTID);
1582 if (!pcmk__str_eq(msg_type, CRM_OP_REGISTER, pcmk__str_casei)) {
1583 crm_debug("Couldn't register with the fencer: invalid reply type '%s'",
1584 (msg_type? msg_type : "(missing)"));
1585 crm_log_xml_debug(reply, "Invalid fencer reply");
1586 rc = -EPROTO;
1587
1588 } else if (native->token == NULL) {
1589 crm_debug("Couldn't register with the fencer: no token in reply");
1590 crm_log_xml_debug(reply, "Invalid fencer reply");
1591 rc = -EPROTO;
1592
1593 } else {
1594 #if HAVE_MSGFROMIPC_TIMEOUT
1595 stonith->call_timeout = PCMK__IPC_TIMEOUT;
1596 #endif
1597 crm_debug("Connection to fencer by %s succeeded (registration token: %s)",
1598 display_name, native->token);
1599 rc = pcmk_ok;
1600 }
1601 }
1602
1603 free_xml(reply);
1604 free_xml(hello);
1605 }
1606
1607 if (rc != pcmk_ok) {
1608 crm_debug("Connection attempt to fencer by %s failed: %s "
1609 CRM_XS " rc=%d", display_name, pcmk_strerror(rc), rc);
1610 stonith->cmds->disconnect(stonith);
1611 }
1612 return rc;
1613 }
1614
1615 static int
1616 stonith_set_notification(stonith_t * stonith, const char *callback, int enabled)
1617 {
1618 int rc = pcmk_ok;
1619 xmlNode *notify_msg = create_xml_node(NULL, __func__);
1620 stonith_private_t *native = stonith->st_private;
1621
1622 if (stonith->state != stonith_disconnected) {
1623
1624 crm_xml_add(notify_msg, F_STONITH_OPERATION, T_STONITH_NOTIFY);
1625 if (enabled) {
1626 crm_xml_add(notify_msg, F_STONITH_NOTIFY_ACTIVATE, callback);
1627 } else {
1628 crm_xml_add(notify_msg, F_STONITH_NOTIFY_DEACTIVATE, callback);
1629 }
1630
1631 rc = crm_ipc_send(native->ipc, notify_msg, crm_ipc_client_response, -1, NULL);
1632 if (rc < 0) {
1633 crm_perror(LOG_DEBUG, "Couldn't register for fencing notifications: %d", rc);
1634 rc = -ECOMM;
1635 } else {
1636 rc = pcmk_ok;
1637 }
1638 }
1639
1640 free_xml(notify_msg);
1641 return rc;
1642 }
1643
1644 static int
1645 stonith_api_add_notification(stonith_t * stonith, const char *event,
1646 void (*callback) (stonith_t * stonith, stonith_event_t * e))
1647 {
1648 GList *list_item = NULL;
1649 stonith_notify_client_t *new_client = NULL;
1650 stonith_private_t *private = NULL;
1651
1652 private = stonith->st_private;
1653 crm_trace("Adding callback for %s events (%d)", event, g_list_length(private->notify_list));
1654
1655 new_client = calloc(1, sizeof(stonith_notify_client_t));
1656 new_client->event = event;
1657 new_client->notify = callback;
1658
1659 list_item = g_list_find_custom(private->notify_list, new_client, stonithlib_GCompareFunc);
1660
1661 if (list_item != NULL) {
1662 crm_warn("Callback already present");
1663 free(new_client);
1664 return -ENOTUNIQ;
1665
1666 } else {
1667 private->notify_list = g_list_append(private->notify_list, new_client);
1668
1669 stonith_set_notification(stonith, event, 1);
1670
1671 crm_trace("Callback added (%d)", g_list_length(private->notify_list));
1672 }
1673 return pcmk_ok;
1674 }
1675
1676 static int
1677 stonith_api_del_notification(stonith_t * stonith, const char *event)
1678 {
1679 GList *list_item = NULL;
1680 stonith_notify_client_t *new_client = NULL;
1681 stonith_private_t *private = NULL;
1682
1683 crm_debug("Removing callback for %s events", event);
1684
1685 private = stonith->st_private;
1686 new_client = calloc(1, sizeof(stonith_notify_client_t));
1687 new_client->event = event;
1688 new_client->notify = NULL;
1689
1690 list_item = g_list_find_custom(private->notify_list, new_client, stonithlib_GCompareFunc);
1691
1692 stonith_set_notification(stonith, event, 0);
1693
1694 if (list_item != NULL) {
1695 stonith_notify_client_t *list_client = list_item->data;
1696
1697 if (private->notify_refcnt) {
1698 list_client->delete = TRUE;
1699 private->notify_deletes = TRUE;
1700 } else {
1701 private->notify_list = g_list_remove(private->notify_list, list_client);
1702 free(list_client);
1703 }
1704
1705 crm_trace("Removed callback");
1706
1707 } else {
1708 crm_trace("Callback not present");
1709 }
1710 free(new_client);
1711 return pcmk_ok;
1712 }
1713
1714 static int
1715 stonith_api_add_callback(stonith_t * stonith, int call_id, int timeout, int options,
1716 void *user_data, const char *callback_name,
1717 void (*callback) (stonith_t * st, stonith_callback_data_t * data))
1718 {
1719 stonith_callback_client_t *blob = NULL;
1720 stonith_private_t *private = NULL;
1721
1722 CRM_CHECK(stonith != NULL, return -EINVAL);
1723 CRM_CHECK(stonith->st_private != NULL, return -EINVAL);
1724 private = stonith->st_private;
1725
1726 if (call_id == 0) {
1727 private->op_callback = callback;
1728
1729 } else if (call_id < 0) {
1730 if (!(options & st_opt_report_only_success)) {
1731 crm_trace("Call failed, calling %s: %s", callback_name, pcmk_strerror(call_id));
1732 invoke_callback(stonith, call_id, call_id, user_data, callback);
1733 } else {
1734 crm_warn("Fencer call failed: %s", pcmk_strerror(call_id));
1735 }
1736 return FALSE;
1737 }
1738
1739 blob = calloc(1, sizeof(stonith_callback_client_t));
1740 blob->id = callback_name;
1741 blob->only_success = (options & st_opt_report_only_success) ? TRUE : FALSE;
1742 blob->user_data = user_data;
1743 blob->callback = callback;
1744 blob->allow_timeout_updates = (options & st_opt_timeout_updates) ? TRUE : FALSE;
1745
1746 if (timeout > 0) {
1747 set_callback_timeout(blob, stonith, call_id, timeout);
1748 }
1749
1750 pcmk__intkey_table_insert(private->stonith_op_callback_table, call_id,
1751 blob);
1752 crm_trace("Added callback to %s for call %d", callback_name, call_id);
1753
1754 return TRUE;
1755 }
1756
1757 static void
1758 stonith_dump_pending_op(gpointer key, gpointer value, gpointer user_data)
1759 {
1760 int call = GPOINTER_TO_INT(key);
1761 stonith_callback_client_t *blob = value;
1762
1763 crm_debug("Call %d (%s): pending", call, crm_str(blob->id));
1764 }
1765
1766 void
1767 stonith_dump_pending_callbacks(stonith_t * stonith)
1768 {
1769 stonith_private_t *private = stonith->st_private;
1770
1771 if (private->stonith_op_callback_table == NULL) {
1772 return;
1773 }
1774 return g_hash_table_foreach(private->stonith_op_callback_table, stonith_dump_pending_op, NULL);
1775 }
1776
1777
1778
1779
1780
1781
1782
1783
1784
1785
1786
1787
1788
1789
1790
1791
1792
1793
1794
1795
1796 static stonith_event_t *
1797 xml_to_event(xmlNode * msg)
1798 {
1799 stonith_event_t *event = calloc(1, sizeof(stonith_event_t));
1800 const char *ntype = crm_element_value(msg, F_SUBTYPE);
1801 char *data_addr = crm_strdup_printf("//%s", ntype);
1802 xmlNode *data = get_xpath_object(data_addr, msg, LOG_DEBUG);
1803
1804 crm_log_xml_trace(msg, "stonith_notify");
1805
1806 crm_element_value_int(msg, F_STONITH_RC, &(event->result));
1807
1808 if (pcmk__str_eq(ntype, T_STONITH_NOTIFY_FENCE, pcmk__str_casei)) {
1809 event->operation = crm_element_value_copy(msg, F_STONITH_OPERATION);
1810
1811 if (data) {
1812 event->origin = crm_element_value_copy(data, F_STONITH_ORIGIN);
1813 event->action = crm_element_value_copy(data, F_STONITH_ACTION);
1814 event->target = crm_element_value_copy(data, F_STONITH_TARGET);
1815 event->executioner = crm_element_value_copy(data, F_STONITH_DELEGATE);
1816 event->id = crm_element_value_copy(data, F_STONITH_REMOTE_OP_ID);
1817 event->client_origin = crm_element_value_copy(data, F_STONITH_CLIENTNAME);
1818 event->device = crm_element_value_copy(data, F_STONITH_DEVICE);
1819
1820 } else {
1821 crm_err("No data for %s event", ntype);
1822 crm_log_xml_notice(msg, "BadEvent");
1823 }
1824 }
1825
1826 free(data_addr);
1827 return event;
1828 }
1829
1830 static void
1831 event_free(stonith_event_t * event)
1832 {
1833 free(event->id);
1834 free(event->type);
1835 free(event->message);
1836 free(event->operation);
1837 free(event->origin);
1838 free(event->action);
1839 free(event->target);
1840 free(event->executioner);
1841 free(event->device);
1842 free(event->client_origin);
1843 free(event);
1844 }
1845
1846 static void
1847 stonith_send_notification(gpointer data, gpointer user_data)
1848 {
1849 struct notify_blob_s *blob = user_data;
1850 stonith_notify_client_t *entry = data;
1851 stonith_event_t *st_event = NULL;
1852 const char *event = NULL;
1853
1854 if (blob->xml == NULL) {
1855 crm_warn("Skipping callback - NULL message");
1856 return;
1857 }
1858
1859 event = crm_element_value(blob->xml, F_SUBTYPE);
1860
1861 if (entry == NULL) {
1862 crm_warn("Skipping callback - NULL callback client");
1863 return;
1864
1865 } else if (entry->delete) {
1866 crm_trace("Skipping callback - marked for deletion");
1867 return;
1868
1869 } else if (entry->notify == NULL) {
1870 crm_warn("Skipping callback - NULL callback");
1871 return;
1872
1873 } else if (!pcmk__str_eq(entry->event, event, pcmk__str_casei)) {
1874 crm_trace("Skipping callback - event mismatch %p/%s vs. %s", entry, entry->event, event);
1875 return;
1876 }
1877
1878 st_event = xml_to_event(blob->xml);
1879
1880 crm_trace("Invoking callback for %p/%s event...", entry, event);
1881 entry->notify(blob->stonith, st_event);
1882 crm_trace("Callback invoked...");
1883
1884 event_free(st_event);
1885 }
1886
1887
1888
1889
1890
1891
1892
1893
1894
1895
1896
1897
1898
1899
1900
1901 static int
1902 stonith_send_command(stonith_t * stonith, const char *op, xmlNode * data, xmlNode ** output_data,
1903 int call_options, int timeout)
1904 {
1905 int rc = 0;
1906 int reply_id = -1;
1907
1908 xmlNode *op_msg = NULL;
1909 xmlNode *op_reply = NULL;
1910 stonith_private_t *native = NULL;
1911
1912 CRM_ASSERT(stonith && stonith->st_private && op);
1913 native = stonith->st_private;
1914
1915 if (output_data != NULL) {
1916 *output_data = NULL;
1917 }
1918
1919 if ((stonith->state == stonith_disconnected) || (native->token == NULL)) {
1920 return -ENOTCONN;
1921 }
1922
1923
1924
1925
1926
1927 stonith->call_id++;
1928 if (stonith->call_id < 1) {
1929 stonith->call_id = 1;
1930 }
1931
1932 op_msg = stonith_create_op(stonith->call_id, native->token, op, data, call_options);
1933 if (op_msg == NULL) {
1934 return -EINVAL;
1935 }
1936
1937 crm_xml_add_int(op_msg, F_STONITH_TIMEOUT, timeout);
1938 crm_trace("Sending %s message to fencer with timeout %ds", op, timeout);
1939
1940 if (data) {
1941 const char *delay_s = crm_element_value(data, F_STONITH_DELAY);
1942
1943 if (delay_s) {
1944 crm_xml_add(op_msg, F_STONITH_DELAY, delay_s);
1945 }
1946 }
1947
1948 {
1949 enum crm_ipc_flags ipc_flags = crm_ipc_flags_none;
1950
1951 if (call_options & st_opt_sync_call) {
1952 pcmk__set_ipc_flags(ipc_flags, "stonith command",
1953 crm_ipc_client_response);
1954 }
1955 rc = crm_ipc_send(native->ipc, op_msg, ipc_flags,
1956 1000 * (timeout + 60), &op_reply);
1957 }
1958 free_xml(op_msg);
1959
1960 if (rc < 0) {
1961 crm_perror(LOG_ERR, "Couldn't perform %s operation (timeout=%ds): %d", op, timeout, rc);
1962 rc = -ECOMM;
1963 goto done;
1964 }
1965
1966 crm_log_xml_trace(op_reply, "Reply");
1967
1968 if (!(call_options & st_opt_sync_call)) {
1969 crm_trace("Async call %d, returning", stonith->call_id);
1970 free_xml(op_reply);
1971 return stonith->call_id;
1972 }
1973
1974 rc = pcmk_ok;
1975 crm_element_value_int(op_reply, F_STONITH_CALLID, &reply_id);
1976
1977 if (reply_id == stonith->call_id) {
1978 crm_trace("Synchronous reply %d received", reply_id);
1979
1980 if (crm_element_value_int(op_reply, F_STONITH_RC, &rc) != 0) {
1981 rc = -ENOMSG;
1982 }
1983
1984 if ((call_options & st_opt_discard_reply) || output_data == NULL) {
1985 crm_trace("Discarding reply");
1986
1987 } else {
1988 *output_data = op_reply;
1989 op_reply = NULL;
1990 }
1991
1992 } else if (reply_id <= 0) {
1993 crm_err("Received bad reply: No id set");
1994 crm_log_xml_err(op_reply, "Bad reply");
1995 free_xml(op_reply);
1996 rc = -ENOMSG;
1997
1998 } else {
1999 crm_err("Received bad reply: %d (wanted %d)", reply_id, stonith->call_id);
2000 crm_log_xml_err(op_reply, "Old reply");
2001 free_xml(op_reply);
2002 rc = -ENOMSG;
2003 }
2004
2005 done:
2006 if (crm_ipc_connected(native->ipc) == FALSE) {
2007 crm_err("Fencer disconnected");
2008 free(native->token); native->token = NULL;
2009 stonith->state = stonith_disconnected;
2010 }
2011
2012 free_xml(op_reply);
2013 return rc;
2014 }
2015
2016
2017 bool
2018 stonith_dispatch(stonith_t * st)
2019 {
2020 gboolean stay_connected = TRUE;
2021 stonith_private_t *private = NULL;
2022
2023 CRM_ASSERT(st != NULL);
2024 private = st->st_private;
2025
2026 while (crm_ipc_ready(private->ipc)) {
2027
2028 if (crm_ipc_read(private->ipc) > 0) {
2029 const char *msg = crm_ipc_buffer(private->ipc);
2030
2031 stonith_dispatch_internal(msg, strlen(msg), st);
2032 }
2033
2034 if (crm_ipc_connected(private->ipc) == FALSE) {
2035 crm_err("Connection closed");
2036 stay_connected = FALSE;
2037 }
2038 }
2039
2040 return stay_connected;
2041 }
2042
2043 static int
2044 stonith_api_free(stonith_t * stonith)
2045 {
2046 int rc = pcmk_ok;
2047
2048 crm_trace("Destroying %p", stonith);
2049
2050 if (stonith->state != stonith_disconnected) {
2051 crm_trace("Disconnecting %p first", stonith);
2052 rc = stonith->cmds->disconnect(stonith);
2053 }
2054
2055 if (stonith->state == stonith_disconnected) {
2056 stonith_private_t *private = stonith->st_private;
2057
2058 crm_trace("Removing %d callbacks", g_hash_table_size(private->stonith_op_callback_table));
2059 g_hash_table_destroy(private->stonith_op_callback_table);
2060
2061 crm_trace("Destroying %d notification clients", g_list_length(private->notify_list));
2062 g_list_free_full(private->notify_list, free);
2063
2064 free(stonith->st_private);
2065 free(stonith->cmds);
2066 free(stonith);
2067
2068 } else {
2069 crm_err("Not free'ing active connection: %s (%d)", pcmk_strerror(rc), rc);
2070 }
2071
2072 return rc;
2073 }
2074
2075 void
2076 stonith_api_delete(stonith_t * stonith)
2077 {
2078 crm_trace("Destroying %p", stonith);
2079 if(stonith) {
2080 stonith->cmds->free(stonith);
2081 }
2082 }
2083
2084 static int
2085 stonith_api_validate(stonith_t *st, int call_options, const char *rsc_id,
2086 const char *namespace_s, const char *agent,
2087 stonith_key_value_t *params, int timeout, char **output,
2088 char **error_output)
2089 {
2090
2091
2092
2093
2094
2095 int rc = pcmk_ok;
2096
2097
2098
2099
2100
2101 const char *target = "node1";
2102 const char *host_arg = NULL;
2103
2104 GHashTable *params_table = pcmk__strkey_table(free, free);
2105
2106
2107 for (; params; params = params->next) {
2108 if (pcmk__str_eq(params->key, PCMK_STONITH_HOST_ARGUMENT,
2109 pcmk__str_casei)) {
2110 host_arg = params->value;
2111 }
2112 if (!pcmk_stonith_param(params->key)) {
2113 g_hash_table_insert(params_table, strdup(params->key),
2114 strdup(params->value));
2115 }
2116 }
2117
2118 #if SUPPORT_CIBSECRETS
2119 rc = pcmk__substitute_secrets(rsc_id, params_table);
2120 if (rc != pcmk_rc_ok) {
2121 crm_warn("Could not replace secret parameters for validation of %s: %s",
2122 agent, pcmk_rc_str(rc));
2123
2124 }
2125 #endif
2126
2127 if (output) {
2128 *output = NULL;
2129 }
2130 if (error_output) {
2131 *error_output = NULL;
2132 }
2133
2134 switch (stonith_get_namespace(agent, namespace_s)) {
2135 case st_namespace_rhcs:
2136 rc = stonith__rhcs_validate(st, call_options, target, agent,
2137 params_table, host_arg, timeout,
2138 output, error_output);
2139 break;
2140
2141 #if HAVE_STONITH_STONITH_H
2142 case st_namespace_lha:
2143 rc = stonith__lha_validate(st, call_options, target, agent,
2144 params_table, timeout, output,
2145 error_output);
2146 break;
2147 #endif
2148
2149 default:
2150 rc = -EINVAL;
2151 errno = EINVAL;
2152 crm_perror(LOG_ERR,
2153 "Agent %s not found or does not support validation",
2154 agent);
2155 break;
2156 }
2157 g_hash_table_destroy(params_table);
2158 return rc;
2159 }
2160
2161 stonith_t *
2162 stonith_api_new(void)
2163 {
2164 stonith_t *new_stonith = NULL;
2165 stonith_private_t *private = NULL;
2166
2167 new_stonith = calloc(1, sizeof(stonith_t));
2168 if (new_stonith == NULL) {
2169 return NULL;
2170 }
2171
2172 private = calloc(1, sizeof(stonith_private_t));
2173 if (private == NULL) {
2174 free(new_stonith);
2175 return NULL;
2176 }
2177 new_stonith->st_private = private;
2178
2179 private->stonith_op_callback_table = pcmk__intkey_table(stonith_destroy_op_callback);
2180 private->notify_list = NULL;
2181 private->notify_refcnt = 0;
2182 private->notify_deletes = FALSE;
2183
2184 new_stonith->call_id = 1;
2185 new_stonith->state = stonith_disconnected;
2186
2187 new_stonith->cmds = calloc(1, sizeof(stonith_api_operations_t));
2188 if (new_stonith->cmds == NULL) {
2189 free(new_stonith->st_private);
2190 free(new_stonith);
2191 return NULL;
2192 }
2193
2194
2195 new_stonith->cmds->free = stonith_api_free;
2196 new_stonith->cmds->connect = stonith_api_signon;
2197 new_stonith->cmds->disconnect = stonith_api_signoff;
2198
2199 new_stonith->cmds->list = stonith_api_list;
2200 new_stonith->cmds->monitor = stonith_api_monitor;
2201 new_stonith->cmds->status = stonith_api_status;
2202 new_stonith->cmds->fence = stonith_api_fence;
2203 new_stonith->cmds->fence_with_delay = stonith_api_fence_with_delay;
2204 new_stonith->cmds->confirm = stonith_api_confirm;
2205 new_stonith->cmds->history = stonith_api_history;
2206
2207 new_stonith->cmds->list_agents = stonith_api_device_list;
2208 new_stonith->cmds->metadata = stonith_api_device_metadata;
2209
2210 new_stonith->cmds->query = stonith_api_query;
2211 new_stonith->cmds->remove_device = stonith_api_remove_device;
2212 new_stonith->cmds->register_device = stonith_api_register_device;
2213
2214 new_stonith->cmds->remove_level = stonith_api_remove_level;
2215 new_stonith->cmds->remove_level_full = stonith_api_remove_level_full;
2216 new_stonith->cmds->register_level = stonith_api_register_level;
2217 new_stonith->cmds->register_level_full = stonith_api_register_level_full;
2218
2219 new_stonith->cmds->remove_callback = stonith_api_del_callback;
2220 new_stonith->cmds->register_callback = stonith_api_add_callback;
2221 new_stonith->cmds->remove_notification = stonith_api_del_notification;
2222 new_stonith->cmds->register_notification = stonith_api_add_notification;
2223
2224 new_stonith->cmds->validate = stonith_api_validate;
2225
2226
2227 return new_stonith;
2228 }
2229
2230
2231
2232
2233
2234
2235
2236
2237
2238
2239 int
2240 stonith_api_connect_retry(stonith_t *st, const char *name, int max_attempts)
2241 {
2242 int rc = -EINVAL;
2243
2244 for (int attempt = 1; attempt <= max_attempts; attempt++) {
2245 rc = st->cmds->connect(st, name, NULL);
2246 if (rc == pcmk_ok) {
2247 return pcmk_ok;
2248 } else if (attempt < max_attempts) {
2249 crm_notice("Fencer connection attempt %d of %d failed (retrying in 2s): %s "
2250 CRM_XS " rc=%d",
2251 attempt, max_attempts, pcmk_strerror(rc), rc);
2252 sleep(2);
2253 }
2254 }
2255 crm_notice("Could not connect to fencer: %s " CRM_XS " rc=%d",
2256 pcmk_strerror(rc), rc);
2257 return rc;
2258 }
2259
2260 stonith_key_value_t *
2261 stonith_key_value_add(stonith_key_value_t * head, const char *key, const char *value)
2262 {
2263 stonith_key_value_t *p, *end;
2264
2265 p = calloc(1, sizeof(stonith_key_value_t));
2266 if (key) {
2267 p->key = strdup(key);
2268 }
2269 if (value) {
2270 p->value = strdup(value);
2271 }
2272
2273 end = head;
2274 while (end && end->next) {
2275 end = end->next;
2276 }
2277
2278 if (end) {
2279 end->next = p;
2280 } else {
2281 head = p;
2282 }
2283
2284 return head;
2285 }
2286
2287 void
2288 stonith_key_value_freeall(stonith_key_value_t * head, int keys, int values)
2289 {
2290 stonith_key_value_t *p;
2291
2292 while (head) {
2293 p = head->next;
2294 if (keys) {
2295 free(head->key);
2296 }
2297 if (values) {
2298 free(head->value);
2299 }
2300 free(head);
2301 head = p;
2302 }
2303 }
2304
2305 #define api_log_open() openlog("stonith-api", LOG_CONS | LOG_NDELAY | LOG_PID, LOG_DAEMON)
2306 #define api_log(level, fmt, args...) syslog(level, "%s: "fmt, __func__, args)
2307
2308 int
2309 stonith_api_kick(uint32_t nodeid, const char *uname, int timeout, bool off)
2310 {
2311 int rc = pcmk_ok;
2312 stonith_t *st = stonith_api_new();
2313 const char *action = off? "off" : "reboot";
2314
2315 api_log_open();
2316 if (st == NULL) {
2317 api_log(LOG_ERR, "API initialization failed, could not kick (%s) node %u/%s",
2318 action, nodeid, uname);
2319 return -EPROTO;
2320 }
2321
2322 rc = st->cmds->connect(st, "stonith-api", NULL);
2323 if (rc != pcmk_ok) {
2324 api_log(LOG_ERR, "Connection failed, could not kick (%s) node %u/%s : %s (%d)",
2325 action, nodeid, uname, pcmk_strerror(rc), rc);
2326 } else {
2327 char *name = (uname == NULL)? pcmk__itoa(nodeid) : strdup(uname);
2328 int opts = 0;
2329
2330 stonith__set_call_options(opts, name,
2331 st_opt_sync_call|st_opt_allow_suicide);
2332 if ((uname == NULL) && (nodeid > 0)) {
2333 stonith__set_call_options(opts, name, st_opt_cs_nodeid);
2334 }
2335 rc = st->cmds->fence(st, opts, name, action, timeout, 0);
2336 free(name);
2337
2338 if (rc != pcmk_ok) {
2339 api_log(LOG_ERR, "Could not kick (%s) node %u/%s : %s (%d)",
2340 action, nodeid, uname, pcmk_strerror(rc), rc);
2341 } else {
2342 api_log(LOG_NOTICE, "Node %u/%s kicked: %s", nodeid, uname, action);
2343 }
2344 }
2345
2346 stonith_api_delete(st);
2347 return rc;
2348 }
2349
2350 time_t
2351 stonith_api_time(uint32_t nodeid, const char *uname, bool in_progress)
2352 {
2353 int rc = pcmk_ok;
2354 time_t when = 0;
2355 stonith_t *st = stonith_api_new();
2356 stonith_history_t *history = NULL, *hp = NULL;
2357
2358 if (st == NULL) {
2359 api_log(LOG_ERR, "Could not retrieve fence history for %u/%s: "
2360 "API initialization failed", nodeid, uname);
2361 return when;
2362 }
2363
2364 rc = st->cmds->connect(st, "stonith-api", NULL);
2365 if (rc != pcmk_ok) {
2366 api_log(LOG_NOTICE, "Connection failed: %s (%d)", pcmk_strerror(rc), rc);
2367 } else {
2368 int entries = 0;
2369 int progress = 0;
2370 int completed = 0;
2371 int opts = 0;
2372 char *name = (uname == NULL)? pcmk__itoa(nodeid) : strdup(uname);
2373
2374 stonith__set_call_options(opts, name, st_opt_sync_call);
2375 if ((uname == NULL) && (nodeid > 0)) {
2376 stonith__set_call_options(opts, name, st_opt_cs_nodeid);
2377 }
2378 rc = st->cmds->history(st, opts, name, &history, 120);
2379 free(name);
2380
2381 for (hp = history; hp; hp = hp->next) {
2382 entries++;
2383 if (in_progress) {
2384 progress++;
2385 if (hp->state != st_done && hp->state != st_failed) {
2386 when = time(NULL);
2387 }
2388
2389 } else if (hp->state == st_done) {
2390 completed++;
2391 if (hp->completed > when) {
2392 when = hp->completed;
2393 }
2394 }
2395 }
2396
2397 stonith_history_free(history);
2398
2399 if(rc == pcmk_ok) {
2400 api_log(LOG_INFO, "Found %d entries for %u/%s: %d in progress, %d completed", entries, nodeid, uname, progress, completed);
2401 } else {
2402 api_log(LOG_ERR, "Could not retrieve fence history for %u/%s: %s (%d)", nodeid, uname, pcmk_strerror(rc), rc);
2403 }
2404 }
2405
2406 stonith_api_delete(st);
2407
2408 if(when) {
2409 api_log(LOG_INFO, "Node %u/%s last kicked at: %ld", nodeid, uname, (long int)when);
2410 }
2411 return when;
2412 }
2413
2414 bool
2415 stonith_agent_exists(const char *agent, int timeout)
2416 {
2417 stonith_t *st = NULL;
2418 stonith_key_value_t *devices = NULL;
2419 stonith_key_value_t *dIter = NULL;
2420 bool rc = FALSE;
2421
2422 if (agent == NULL) {
2423 return rc;
2424 }
2425
2426 st = stonith_api_new();
2427 if (st == NULL) {
2428 crm_err("Could not list fence agents: API memory allocation failed");
2429 return FALSE;
2430 }
2431 st->cmds->list_agents(st, st_opt_sync_call, NULL, &devices, timeout == 0 ? 120 : timeout);
2432
2433 for (dIter = devices; dIter != NULL; dIter = dIter->next) {
2434 if (pcmk__str_eq(dIter->value, agent, pcmk__str_none)) {
2435 rc = TRUE;
2436 break;
2437 }
2438 }
2439
2440 stonith_key_value_freeall(devices, 1, 1);
2441 stonith_api_delete(st);
2442 return rc;
2443 }
2444
2445 const char *
2446 stonith_action_str(const char *action)
2447 {
2448 if (action == NULL) {
2449 return "fencing";
2450 } else if (!strcmp(action, "on")) {
2451 return "unfencing";
2452 } else if (!strcmp(action, "off")) {
2453 return "turning off";
2454 } else {
2455 return action;
2456 }
2457 }
2458
2459
2460
2461
2462
2463
2464
2465
2466
2467 static void
2468 parse_list_line(const char *line, int len, GList **output)
2469 {
2470 size_t i = 0;
2471 size_t entry_start = 0;
2472
2473
2474
2475
2476
2477 if (strstr(line, "invalid") || strstr(line, "variable")) {
2478 crm_debug("Skipping list output line: %s", line);
2479 return;
2480 }
2481
2482
2483 for (i = 0; i <= len; i++) {
2484
2485 if (isspace(line[i]) || (line[i] == ',') || (line[i] == ';')
2486 || (line[i] == '\0')) {
2487
2488
2489 int rc = 0;
2490 char *entry = NULL;
2491
2492 if (i == entry_start) {
2493
2494 entry_start = i + 1;
2495 continue;
2496 }
2497
2498 entry = calloc(i - entry_start + 1, sizeof(char));
2499 CRM_ASSERT(entry != NULL);
2500
2501
2502
2503
2504
2505 rc = sscanf(line + entry_start, "%[a-zA-Z0-9_-.]", entry);
2506 if (rc != 1) {
2507 crm_warn("Could not parse list output entry: %s "
2508 CRM_XS " entry_start=%d position=%d",
2509 line + entry_start, entry_start, i);
2510 free(entry);
2511
2512 } else if (pcmk__strcase_any_of(entry, "on", "off", NULL)) {
2513
2514
2515
2516
2517
2518
2519
2520
2521 free(entry);
2522
2523 } else {
2524
2525 *output = g_list_append(*output, entry);
2526 }
2527 entry_start = i + 1;
2528 }
2529 }
2530 }
2531
2532
2533
2534
2535
2536
2537
2538
2539
2540
2541
2542
2543
2544
2545
2546
2547
2548
2549
2550
2551
2552
2553 GList *
2554 stonith__parse_targets(const char *target_spec)
2555 {
2556 GList *targets = NULL;
2557
2558 if (target_spec != NULL) {
2559 size_t out_len = strlen(target_spec);
2560 size_t line_start = 0;
2561
2562 for (size_t i = 0; i <= out_len; ++i) {
2563 if ((target_spec[i] == '\n') || (target_spec[i] == '\0')
2564 || ((target_spec[i] == '\\') && (target_spec[i + 1] == 'n'))) {
2565
2566
2567 int len = i - line_start;
2568
2569 if (len > 0) {
2570 char *line = strndup(target_spec + line_start, len);
2571
2572 line[len] = '\0';
2573 parse_list_line(line, len, &targets);
2574 free(line);
2575 }
2576 if (target_spec[i] == '\\') {
2577 ++i;
2578 }
2579 line_start = i + 1;
2580 }
2581 }
2582 }
2583 return targets;
2584 }
2585
2586
2587
2588
2589
2590
2591
2592
2593 gboolean
2594 stonith__later_succeeded(stonith_history_t *event, stonith_history_t *top_history)
2595 {
2596 gboolean ret = FALSE;
2597
2598 for (stonith_history_t *prev_hp = top_history; prev_hp; prev_hp = prev_hp->next) {
2599 if (prev_hp == event) {
2600 break;
2601 }
2602
2603 if ((prev_hp->state == st_done) &&
2604 pcmk__str_eq(event->target, prev_hp->target, pcmk__str_casei) &&
2605 pcmk__str_eq(event->action, prev_hp->action, pcmk__str_casei) &&
2606 pcmk__str_eq(event->delegate, prev_hp->delegate, pcmk__str_casei) &&
2607 ((event->completed < prev_hp->completed) ||
2608 ((event->completed == prev_hp->completed) && (event->completed_nsec < prev_hp->completed_nsec)))) {
2609 ret = TRUE;
2610 break;
2611 }
2612 }
2613 return ret;
2614 }
2615
2616
2617
2618
2619
2620
2621
2622
2623
2624
2625 stonith_history_t *
2626 stonith__sort_history(stonith_history_t *history)
2627 {
2628 stonith_history_t *new = NULL, *pending = NULL, *hp, *np, *tmp;
2629
2630 for (hp = history; hp; ) {
2631 tmp = hp->next;
2632 if ((hp->state == st_done) || (hp->state == st_failed)) {
2633
2634 if ((!new) || (hp->completed > new->completed) ||
2635 ((hp->completed == new->completed) && (hp->completed_nsec > new->completed_nsec))) {
2636 hp->next = new;
2637 new = hp;
2638 } else {
2639 np = new;
2640 do {
2641 if ((!np->next) || (hp->completed > np->next->completed) ||
2642 ((hp->completed == np->next->completed) && (hp->completed_nsec > np->next->completed_nsec))) {
2643 hp->next = np->next;
2644 np->next = hp;
2645 break;
2646 }
2647 np = np->next;
2648 } while (1);
2649 }
2650 } else {
2651
2652 hp->next = pending;
2653 pending = hp;
2654 }
2655 hp = tmp;
2656 }
2657
2658
2659 if (pending) {
2660 stonith_history_t *last_pending = pending;
2661
2662 while (last_pending->next) {
2663 last_pending = last_pending->next;
2664 }
2665
2666 last_pending->next = new;
2667 new = pending;
2668 }
2669 return new;
2670 }
2671
2672
2673
2674
2675
2676
2677
2678
2679 const char *
2680 stonith_op_state_str(enum op_state state)
2681 {
2682 switch (state) {
2683 case st_query: return "querying";
2684 case st_exec: return "executing";
2685 case st_done: return "completed";
2686 case st_duplicate: return "duplicate";
2687 case st_failed: return "failed";
2688 }
2689 return "unknown";
2690 }
2691
2692 stonith_history_t *
2693 stonith__first_matching_event(stonith_history_t *history,
2694 bool (*matching_fn)(stonith_history_t *, void *),
2695 void *user_data)
2696 {
2697 for (stonith_history_t *hp = history; hp; hp = hp->next) {
2698 if (matching_fn(hp, user_data)) {
2699 return hp;
2700 }
2701 }
2702
2703 return NULL;
2704 }
2705
2706 bool
2707 stonith__event_state_pending(stonith_history_t *history, void *user_data)
2708 {
2709 return history->state != st_failed && history->state != st_done;
2710 }
2711
2712 bool
2713 stonith__event_state_eq(stonith_history_t *history, void *user_data)
2714 {
2715 return history->state == GPOINTER_TO_INT(user_data);
2716 }
2717
2718 bool
2719 stonith__event_state_neq(stonith_history_t *history, void *user_data)
2720 {
2721 return history->state != GPOINTER_TO_INT(user_data);
2722 }
2723
2724 void
2725 stonith__device_parameter_flags(uint32_t *device_flags, const char *device_name,
2726 xmlNode *metadata)
2727 {
2728 xmlXPathObjectPtr xpath = NULL;
2729 int max = 0;
2730 int lpc = 0;
2731
2732 CRM_CHECK((device_flags != NULL) && (metadata != NULL), return);
2733
2734 xpath = xpath_search(metadata, "//parameter");
2735 max = numXpathResults(xpath);
2736
2737 if (max <= 0) {
2738 freeXpathObject(xpath);
2739 return;
2740 }
2741
2742 for (lpc = 0; lpc < max; lpc++) {
2743 const char *parameter = NULL;
2744 xmlNode *match = getXpathResult(xpath, lpc);
2745
2746 CRM_LOG_ASSERT(match != NULL);
2747 if (match == NULL) {
2748 continue;
2749 }
2750
2751 parameter = crm_element_value(match, "name");
2752
2753 if (pcmk__str_eq(parameter, "plug", pcmk__str_casei)) {
2754 stonith__set_device_flags(*device_flags, device_name,
2755 st_device_supports_parameter_plug);
2756
2757 } else if (pcmk__str_eq(parameter, "port", pcmk__str_casei)) {
2758 stonith__set_device_flags(*device_flags, device_name,
2759 st_device_supports_parameter_port);
2760 }
2761 }
2762
2763 freeXpathObject(xpath);
2764 }
2765
2766
2767
2768
2769 const char *get_stonith_provider(const char *agent, const char *provider);
2770
2771 const char *
2772 get_stonith_provider(const char *agent, const char *provider)
2773 {
2774 return stonith_namespace2text(stonith_get_namespace(agent, provider));
2775 }
2776
2777
2778