This source file includes following definitions.
- set_result_from_svc_action
- log_action
- append_config_arg
- make_args
- stonith__destroy_action
- stonith__action_result
- stonith__action_create
- update_remaining_timeout
- stonith__result2rc
- stonith__legacy2status
- stonith__xe_set_result
- stonith__find_xe_with_result
- stonith__xe_get_result
- stonith_action_async_done
- stonith_action_async_forked
- internal_stonith_action_execute
- stonith__execute_async
- stonith__execute
1
2
3
4
5
6
7
8
9
10 #include <crm_internal.h>
11
12 #include <stdbool.h>
13 #include <stdlib.h>
14 #include <stdio.h>
15 #include <string.h>
16 #include <libgen.h>
17 #include <inttypes.h>
18 #include <sys/types.h>
19 #include <glib.h>
20
21 #include <crm/crm.h>
22 #include <crm/stonith-ng.h>
23 #include <crm/fencing/internal.h>
24 #include <crm/common/xml.h>
25 #include <crm/services_internal.h>
26
27 #include "fencing_private.h"
28
29 struct stonith_action_s {
30
31 char *agent;
32 char *action;
33 GHashTable *args;
34 int timeout;
35 bool async;
36 void *userdata;
37 void (*done_cb) (int pid, const pcmk__action_result_t *result,
38 void *user_data);
39 void (*fork_cb) (int pid, void *user_data);
40
41 svc_action_t *svc_action;
42
43
44 time_t initial_start_time;
45 int tries;
46 int remaining_timeout;
47 int max_retries;
48
49 int pid;
50 pcmk__action_result_t result;
51 };
52
53 static int internal_stonith_action_execute(stonith_action_t *action);
54 static void log_action(stonith_action_t *action, pid_t pid);
55
56
57
58
59
60
61
62
63 static void
64 set_result_from_svc_action(stonith_action_t *action, svc_action_t *svc_action)
65 {
66 pcmk__set_result(&(action->result), svc_action->rc, svc_action->status,
67 services__exit_reason(svc_action));
68 pcmk__set_result_output(&(action->result),
69 services__grab_stdout(svc_action),
70 services__grab_stderr(svc_action));
71 }
72
73 static void
74 log_action(stonith_action_t *action, pid_t pid)
75 {
76
77
78
79 if (action->result.action_stderr != NULL) {
80
81 char *prefix = crm_strdup_printf("%s[%d] stderr:", action->agent, pid);
82
83 crm_log_output(LOG_WARNING, prefix, action->result.action_stderr);
84 free(prefix);
85 }
86 }
87
88 static void
89 append_config_arg(gpointer key, gpointer value, gpointer user_data)
90 {
91
92
93
94
95
96
97 if (!pcmk__str_eq(key, STONITH_ATTR_ACTION_OP, pcmk__str_casei)
98 && !pcmk_stonith_param(key)
99 && (strstr(key, CRM_META) == NULL)
100 && !pcmk__str_eq(key, PCMK_XA_CRM_FEATURE_SET, pcmk__str_none)) {
101
102 crm_trace("Passing %s=%s with fence action",
103 (const char *) key, (const char *) (value? value : ""));
104 pcmk__insert_dup((GHashTable *) user_data, key, pcmk__s(value, ""));
105 }
106 }
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122 static GHashTable *
123 make_args(const char *agent, const char *action, const char *target,
124 uint32_t target_nodeid, GHashTable *device_args,
125 GHashTable *port_map, const char *host_arg)
126 {
127 GHashTable *arg_list = NULL;
128 const char *value = NULL;
129
130 CRM_CHECK(action != NULL, return NULL);
131
132 arg_list = pcmk__strkey_table(free, free);
133
134
135 if (device_args) {
136 char buffer[512];
137
138 snprintf(buffer, sizeof(buffer), "pcmk_%s_action", action);
139 value = g_hash_table_lookup(device_args, buffer);
140 if (value) {
141 crm_debug("Substituting '%s' for fence action %s targeting %s",
142 value, action, pcmk__s(target, "no node"));
143 action = value;
144 }
145 }
146
147
148 pcmk__insert_dup(arg_list, STONITH_ATTR_ACTION_OP, action);
149
150
151
152
153 if ((target != NULL) && (device_args != NULL)) {
154 const char *param = NULL;
155
156
157
158
159 pcmk__insert_dup(arg_list, "nodename", target);
160
161
162 if (target_nodeid != 0) {
163 char *nodeid = crm_strdup_printf("%" PRIu32, target_nodeid);
164
165
166 crm_info("Passing '%s' as nodeid with fence action '%s' targeting %s",
167 nodeid, action, pcmk__s(target, "no node"));
168 g_hash_table_insert(arg_list, strdup("nodeid"), nodeid);
169 }
170
171
172 param = g_hash_table_lookup(device_args, PCMK_STONITH_HOST_ARGUMENT);
173 if (param == NULL) {
174
175 param = host_arg;
176 }
177 if ((param != NULL)
178 && !pcmk__str_eq(agent, "fence_legacy", pcmk__str_none)
179 && !pcmk__str_eq(param, PCMK_VALUE_NONE, pcmk__str_casei)) {
180
181 value = g_hash_table_lookup(device_args, param);
182 if (pcmk__str_eq(value, "dynamic",
183 pcmk__str_casei|pcmk__str_null_matches)) {
184
185
186
187 const char *alias = NULL;
188
189 if (port_map) {
190 alias = g_hash_table_lookup(port_map, target);
191 }
192 if (alias == NULL) {
193 alias = target;
194 }
195 crm_debug("Passing %s='%s' with fence action %s targeting %s",
196 param, alias, action, pcmk__s(target, "no node"));
197 pcmk__insert_dup(arg_list, param, alias);
198 }
199 }
200 }
201
202 if (device_args) {
203 g_hash_table_foreach(device_args, append_config_arg, arg_list);
204 }
205
206 return arg_list;
207 }
208
209
210
211
212
213
214
215 void
216 stonith__destroy_action(stonith_action_t *action)
217 {
218 if (action) {
219 free(action->agent);
220 if (action->args) {
221 g_hash_table_destroy(action->args);
222 }
223 free(action->action);
224 if (action->svc_action) {
225 services_action_free(action->svc_action);
226 }
227 pcmk__reset_result(&(action->result));
228 free(action);
229 }
230 }
231
232
233
234
235
236
237
238
239
240 pcmk__action_result_t *
241 stonith__action_result(stonith_action_t *action)
242 {
243 return (action == NULL)? NULL : &(action->result);
244 }
245
246 #define FAILURE_MAX_RETRIES 2
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263 stonith_action_t *
264 stonith__action_create(const char *agent, const char *action_name,
265 const char *target, uint32_t target_nodeid,
266 int timeout_sec, GHashTable *device_args,
267 GHashTable *port_map, const char *host_arg)
268 {
269 stonith_action_t *action = pcmk__assert_alloc(1, sizeof(stonith_action_t));
270
271 action->args = make_args(agent, action_name, target, target_nodeid,
272 device_args, port_map, host_arg);
273 crm_debug("Preparing '%s' action targeting %s using agent %s",
274 action_name, pcmk__s(target, "no node"), agent);
275 action->agent = strdup(agent);
276 action->action = strdup(action_name);
277 action->timeout = action->remaining_timeout = timeout_sec;
278 action->max_retries = FAILURE_MAX_RETRIES;
279
280 pcmk__set_result(&(action->result), PCMK_OCF_UNKNOWN, PCMK_EXEC_UNKNOWN,
281 "Initialization bug in fencing library");
282
283 if (device_args) {
284 char buffer[512];
285 const char *value = NULL;
286
287 snprintf(buffer, sizeof(buffer), "pcmk_%s_retries", action_name);
288 value = g_hash_table_lookup(device_args, buffer);
289
290 if (value) {
291 action->max_retries = atoi(value);
292 }
293 }
294
295 return action;
296 }
297
298 static gboolean
299 update_remaining_timeout(stonith_action_t * action)
300 {
301 int diff = time(NULL) - action->initial_start_time;
302
303 if (action->tries >= action->max_retries) {
304 crm_info("Attempted to execute agent %s (%s) the maximum number of times (%d) allowed",
305 action->agent, action->action, action->max_retries);
306 action->remaining_timeout = 0;
307 } else if ((action->result.execution_status != PCMK_EXEC_TIMEOUT)
308 && (diff < (action->timeout * 0.7))) {
309
310
311 action->remaining_timeout = action->timeout - diff;
312 } else {
313 action->remaining_timeout = 0;
314 }
315 return action->remaining_timeout ? TRUE : FALSE;
316 }
317
318
319
320
321
322
323
324
325
326 int
327 stonith__result2rc(const pcmk__action_result_t *result)
328 {
329 if (pcmk__result_ok(result)) {
330 return pcmk_rc_ok;
331 }
332
333 switch (result->execution_status) {
334 case PCMK_EXEC_PENDING: return EINPROGRESS;
335 case PCMK_EXEC_CANCELLED: return ECANCELED;
336 case PCMK_EXEC_TIMEOUT: return ETIME;
337 case PCMK_EXEC_NOT_INSTALLED: return ENOENT;
338 case PCMK_EXEC_NOT_SUPPORTED: return EOPNOTSUPP;
339 case PCMK_EXEC_NOT_CONNECTED: return ENOTCONN;
340 case PCMK_EXEC_NO_FENCE_DEVICE: return ENODEV;
341 case PCMK_EXEC_NO_SECRETS: return EACCES;
342
343
344
345
346
347
348 case PCMK_EXEC_INVALID:
349 switch (result->exit_status) {
350 case CRM_EX_INVALID_PARAM: return EINVAL;
351 case CRM_EX_INSUFFICIENT_PRIV: return EACCES;
352 case CRM_EX_PROTOCOL: return EPROTO;
353
354
355
356
357
358
359 case CRM_EX_EXPIRED: return EHOSTUNREACH;
360 default: break;
361 }
362 break;
363
364 default:
365 break;
366 }
367
368
369
370 if (result->action_stderr == NULL) {
371 return ENODATA;
372
373 } else if (strcasestr(result->action_stderr, "timed out")
374 || strcasestr(result->action_stderr, "timeout")) {
375 return ETIME;
376
377 } else if (strcasestr(result->action_stderr, "unrecognised action")
378 || strcasestr(result->action_stderr, "unrecognized action")
379 || strcasestr(result->action_stderr, "unsupported action")) {
380 return EOPNOTSUPP;
381 }
382
383
384 return pcmk_rc_error;
385 }
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400 int
401 stonith__legacy2status(int rc)
402 {
403 if (rc >= 0) {
404 return PCMK_EXEC_DONE;
405 }
406 switch (-rc) {
407 case EACCES: return PCMK_EXEC_NO_SECRETS;
408 case ECANCELED: return PCMK_EXEC_CANCELLED;
409 case EHOSTUNREACH: return PCMK_EXEC_INVALID;
410 case EINPROGRESS: return PCMK_EXEC_PENDING;
411 case ENODEV: return PCMK_EXEC_NO_FENCE_DEVICE;
412 case ENOENT: return PCMK_EXEC_NOT_INSTALLED;
413 case ENOTCONN: return PCMK_EXEC_NOT_CONNECTED;
414 case EOPNOTSUPP: return PCMK_EXEC_NOT_SUPPORTED;
415 case EPROTO: return PCMK_EXEC_INVALID;
416 case EPROTONOSUPPORT: return PCMK_EXEC_NOT_SUPPORTED;
417 case ETIME: return PCMK_EXEC_TIMEOUT;
418 case ETIMEDOUT: return PCMK_EXEC_TIMEOUT;
419 default: return PCMK_EXEC_ERROR;
420 }
421 }
422
423
424
425
426
427
428
429
430 void
431 stonith__xe_set_result(xmlNode *xml, const pcmk__action_result_t *result)
432 {
433 int exit_status = CRM_EX_OK;
434 enum pcmk_exec_status execution_status = PCMK_EXEC_DONE;
435 const char *exit_reason = NULL;
436 const char *action_stdout = NULL;
437 int rc = pcmk_ok;
438
439 CRM_CHECK(xml != NULL, return);
440
441 if (result != NULL) {
442 exit_status = result->exit_status;
443 execution_status = result->execution_status;
444 exit_reason = result->exit_reason;
445 action_stdout = result->action_stdout;
446 rc = pcmk_rc2legacy(stonith__result2rc(result));
447 }
448
449 crm_xml_add_int(xml, PCMK__XA_OP_STATUS, (int) execution_status);
450 crm_xml_add_int(xml, PCMK__XA_RC_CODE, exit_status);
451 crm_xml_add(xml, PCMK_XA_EXIT_REASON, exit_reason);
452 crm_xml_add(xml, PCMK__XA_ST_OUTPUT, action_stdout);
453
454
455
456
457
458 crm_xml_add_int(xml, PCMK__XA_ST_RC, rc);
459 }
460
461
462
463
464
465
466
467
468
469 xmlNode *
470 stonith__find_xe_with_result(xmlNode *xml)
471 {
472 xmlNode *match = get_xpath_object("//@" PCMK__XA_RC_CODE, xml, LOG_NEVER);
473
474 if (match == NULL) {
475
476
477
478 match = get_xpath_object("//@" PCMK__XA_ST_RC, xml, LOG_ERR);
479 }
480 return match;
481 }
482
483
484
485
486
487
488
489
490 void
491 stonith__xe_get_result(const xmlNode *xml, pcmk__action_result_t *result)
492 {
493 int exit_status = CRM_EX_OK;
494 int execution_status = PCMK_EXEC_DONE;
495 const char *exit_reason = NULL;
496 char *action_stdout = NULL;
497
498 CRM_CHECK((xml != NULL) && (result != NULL), return);
499
500 exit_reason = crm_element_value(xml, PCMK_XA_EXIT_REASON);
501 action_stdout = crm_element_value_copy(xml, PCMK__XA_ST_OUTPUT);
502
503
504 if ((crm_element_value_int(xml, PCMK__XA_RC_CODE, &exit_status) < 0)
505 || (crm_element_value_int(xml, PCMK__XA_OP_STATUS,
506 &execution_status) < 0)) {
507 int rc = pcmk_ok;
508 exit_status = CRM_EX_ERROR;
509
510
511
512
513 if (crm_element_value_int(xml, PCMK__XA_ST_RC, &rc) == 0) {
514 if ((rc == pcmk_ok) || (rc == -EINPROGRESS)) {
515 exit_status = CRM_EX_OK;
516 }
517 execution_status = stonith__legacy2status(rc);
518 exit_reason = pcmk_strerror(rc);
519
520 } else {
521 execution_status = PCMK_EXEC_ERROR;
522 exit_reason = "Fencer reply contained neither a full result "
523 "nor a legacy return code (bug?)";
524 }
525 }
526 pcmk__set_result(result, exit_status, execution_status, exit_reason);
527 pcmk__set_result_output(result, action_stdout, NULL);
528 }
529
530 static void
531 stonith_action_async_done(svc_action_t *svc_action)
532 {
533 stonith_action_t *action = (stonith_action_t *) svc_action->cb_data;
534
535 set_result_from_svc_action(action, svc_action);
536 svc_action->params = NULL;
537 log_action(action, action->pid);
538
539 if (!pcmk__result_ok(&(action->result))
540 && update_remaining_timeout(action)) {
541
542 int rc = internal_stonith_action_execute(action);
543 if (rc == pcmk_ok) {
544 return;
545 }
546 }
547
548 if (action->done_cb) {
549 action->done_cb(action->pid, &(action->result), action->userdata);
550 }
551
552 action->svc_action = NULL;
553 stonith__destroy_action(action);
554 }
555
556 static void
557 stonith_action_async_forked(svc_action_t *svc_action)
558 {
559 stonith_action_t *action = (stonith_action_t *) svc_action->cb_data;
560
561 action->pid = svc_action->pid;
562 action->svc_action = svc_action;
563
564 if (action->fork_cb) {
565 (action->fork_cb) (svc_action->pid, action->userdata);
566 }
567
568 pcmk__set_result(&(action->result), PCMK_OCF_UNKNOWN, PCMK_EXEC_PENDING,
569 NULL);
570
571 crm_trace("Child process %d performing action '%s' successfully forked",
572 action->pid, action->action);
573 }
574
575 static int
576 internal_stonith_action_execute(stonith_action_t * action)
577 {
578 int rc = -EPROTO;
579 int is_retry = 0;
580 svc_action_t *svc_action = NULL;
581 static int stonith_sequence = 0;
582 char *buffer = NULL;
583
584 CRM_CHECK(action != NULL, return -EINVAL);
585
586 if ((action->action == NULL) || (action->args == NULL)
587 || (action->agent == NULL)) {
588 pcmk__set_result(&(action->result), PCMK_OCF_UNKNOWN_ERROR,
589 PCMK_EXEC_ERROR_FATAL, "Bug in fencing library");
590 return -EINVAL;
591 }
592
593 if (!action->tries) {
594 action->initial_start_time = time(NULL);
595 }
596 action->tries++;
597
598 if (action->tries > 1) {
599 crm_info("Attempt %d to execute %s (%s). remaining timeout is %d",
600 action->tries, action->agent, action->action, action->remaining_timeout);
601 is_retry = 1;
602 }
603
604 buffer = crm_strdup_printf(PCMK__FENCE_BINDIR "/%s",
605 basename(action->agent));
606 svc_action = services_action_create_generic(buffer, NULL);
607 free(buffer);
608
609 if (svc_action->rc != PCMK_OCF_UNKNOWN) {
610 set_result_from_svc_action(action, svc_action);
611 services_action_free(svc_action);
612 return -E2BIG;
613 }
614
615 svc_action->timeout = 1000 * action->remaining_timeout;
616 svc_action->standard = strdup(PCMK_RESOURCE_CLASS_STONITH);
617 svc_action->id = crm_strdup_printf("%s_%s_%dof%d", basename(action->agent),
618 action->action, action->tries,
619 action->max_retries);
620 svc_action->agent = strdup(action->agent);
621 svc_action->sequence = stonith_sequence++;
622 svc_action->params = action->args;
623 svc_action->cb_data = (void *) action;
624 svc_action->flags = pcmk__set_flags_as(__func__, __LINE__,
625 LOG_TRACE, "Action",
626 svc_action->id, svc_action->flags,
627 SVC_ACTION_NON_BLOCKED,
628 "SVC_ACTION_NON_BLOCKED");
629
630
631 if (is_retry) {
632 pcmk__reset_result(&(action->result));
633 sleep(1);
634 }
635
636 if (action->async) {
637
638 CRM_LOG_ASSERT(services_action_async_fork_notify(svc_action,
639 &stonith_action_async_done,
640 &stonith_action_async_forked));
641 return pcmk_ok;
642
643 } else if (services_action_sync(svc_action)) {
644 rc = pcmk_ok;
645
646 } else {
647 rc = -ECONNABORTED;
648 }
649
650 set_result_from_svc_action(action, svc_action);
651 svc_action->params = NULL;
652 services_action_free(svc_action);
653 return rc;
654 }
655
656
657
658
659
660
661
662
663
664
665
666
667 int
668 stonith__execute_async(stonith_action_t * action, void *userdata,
669 void (*done) (int pid,
670 const pcmk__action_result_t *result,
671 void *user_data),
672 void (*fork_cb) (int pid, void *user_data))
673 {
674 if (!action) {
675 return -EINVAL;
676 }
677
678 action->userdata = userdata;
679 action->done_cb = done;
680 action->fork_cb = fork_cb;
681 action->async = true;
682
683 return internal_stonith_action_execute(action);
684 }
685
686
687
688
689
690
691
692
693
694 int
695 stonith__execute(stonith_action_t *action)
696 {
697 int rc = pcmk_ok;
698
699 CRM_CHECK(action != NULL, return -EINVAL);
700
701
702 do {
703 rc = internal_stonith_action_execute(action);
704 } while ((rc != pcmk_ok) && update_remaining_timeout(action));
705
706 return rc;
707 }