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