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