Line data Source code
1 : /*
2 : * Copyright 2004-2024 the Pacemaker project contributors
3 : *
4 : * The version control history for this file may have further details.
5 : *
6 : * This source code is licensed under the GNU Lesser General Public License
7 : * version 2.1 or later (LGPLv2.1+) WITHOUT ANY WARRANTY.
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 : /*! user defined data */
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 : /*! internal timing information */
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 : * \internal
58 : * \brief Set an action's result based on services library result
59 : *
60 : * \param[in,out] action Fence action to set result for
61 : * \param[in,out] svc_action Service action to get result from
62 : */
63 : static void
64 0 : set_result_from_svc_action(stonith_action_t *action, svc_action_t *svc_action)
65 : {
66 0 : pcmk__set_result(&(action->result), svc_action->rc, svc_action->status,
67 : services__exit_reason(svc_action));
68 0 : pcmk__set_result_output(&(action->result),
69 : services__grab_stdout(svc_action),
70 : services__grab_stderr(svc_action));
71 0 : }
72 :
73 : static void
74 0 : log_action(stonith_action_t *action, pid_t pid)
75 : {
76 : /* The services library has already logged the output at info or debug
77 : * level, so just raise to warning for stderr.
78 : */
79 0 : if (action->result.action_stderr != NULL) {
80 : /* Logging the whole string confuses syslog when the string is xml */
81 0 : char *prefix = crm_strdup_printf("%s[%d] stderr:", action->agent, pid);
82 :
83 0 : crm_log_output(LOG_WARNING, prefix, action->result.action_stderr);
84 0 : free(prefix);
85 : }
86 0 : }
87 :
88 : static void
89 0 : append_config_arg(gpointer key, gpointer value, gpointer user_data)
90 : {
91 : /* The fencer will filter "action" out when it registers the device,
92 : * but ignore it here in case any external API users don't.
93 : *
94 : * Also filter out parameters handled directly by Pacemaker.
95 : */
96 0 : if (!pcmk__str_eq(key, STONITH_ATTR_ACTION_OP, pcmk__str_casei)
97 0 : && !pcmk_stonith_param(key)
98 0 : && (strstr(key, CRM_META) == NULL)
99 0 : && !pcmk__str_eq(key, PCMK_XA_CRM_FEATURE_SET, pcmk__str_none)) {
100 :
101 0 : crm_trace("Passing %s=%s with fence action",
102 : (const char *) key, (const char *) (value? value : ""));
103 0 : pcmk__insert_dup((GHashTable *) user_data, key, pcmk__s(value, ""));
104 : }
105 0 : }
106 :
107 : /*!
108 : * \internal
109 : * \brief Create a table of arguments for a fencing action
110 : *
111 : * \param[in] agent Fencing agent name
112 : * \param[in] action Name of fencing action
113 : * \param[in] target Name of target node for fencing action
114 : * \param[in] target_nodeid Node ID of target node for fencing action
115 : * \param[in] device_args Fence device parameters
116 : * \param[in] port_map Target node-to-port mapping for fence device
117 : * \param[in] host_arg Argument name for passing target
118 : *
119 : * \return Newly created hash table of arguments for fencing action
120 : */
121 : static GHashTable *
122 0 : make_args(const char *agent, const char *action, const char *target,
123 : uint32_t target_nodeid, GHashTable *device_args,
124 : GHashTable *port_map, const char *host_arg)
125 : {
126 0 : GHashTable *arg_list = NULL;
127 0 : const char *value = NULL;
128 :
129 0 : CRM_CHECK(action != NULL, return NULL);
130 :
131 0 : arg_list = pcmk__strkey_table(free, free);
132 :
133 : // Add action to arguments (using an alias if requested)
134 0 : if (device_args) {
135 : char buffer[512];
136 :
137 0 : snprintf(buffer, sizeof(buffer), "pcmk_%s_action", action);
138 0 : value = g_hash_table_lookup(device_args, buffer);
139 0 : if (value) {
140 0 : crm_debug("Substituting '%s' for fence action %s targeting %s",
141 : value, action, pcmk__s(target, "no node"));
142 0 : action = value;
143 : }
144 : }
145 0 : pcmk__insert_dup(arg_list, STONITH_ATTR_ACTION_OP, action);
146 :
147 : /* If this is a fencing operation against another node, add more standard
148 : * arguments.
149 : */
150 0 : if ((target != NULL) && (device_args != NULL)) {
151 0 : const char *param = NULL;
152 :
153 : /* Always pass the target's name, per
154 : * https://github.com/ClusterLabs/fence-agents/blob/main/doc/FenceAgentAPI.md
155 : */
156 0 : pcmk__insert_dup(arg_list, "nodename", target);
157 :
158 : // If the target's node ID was specified, pass it, too
159 0 : if (target_nodeid != 0) {
160 0 : char *nodeid = crm_strdup_printf("%" PRIu32, target_nodeid);
161 :
162 : // cts-fencing looks for this log message
163 0 : crm_info("Passing '%s' as nodeid with fence action '%s' targeting %s",
164 : nodeid, action, pcmk__s(target, "no node"));
165 0 : g_hash_table_insert(arg_list, strdup("nodeid"), nodeid);
166 : }
167 :
168 : // Check whether target must be specified in some other way
169 0 : param = g_hash_table_lookup(device_args, PCMK_STONITH_HOST_ARGUMENT);
170 0 : if (!pcmk__str_eq(agent, "fence_legacy", pcmk__str_none)
171 0 : && !pcmk__str_eq(param, PCMK_VALUE_NONE, pcmk__str_casei)) {
172 :
173 0 : if (param == NULL) {
174 : /* Use the caller's default for pcmk_host_argument, or "port" if
175 : * none was given
176 : */
177 0 : param = (host_arg == NULL)? "port" : host_arg;
178 : }
179 0 : value = g_hash_table_lookup(device_args, param);
180 :
181 0 : if (pcmk__str_eq(value, "dynamic",
182 : pcmk__str_casei|pcmk__str_null_matches)) {
183 : /* If the host argument was "dynamic" or not explicitly specified,
184 : * add it with the target
185 : */
186 0 : const char *alias = NULL;
187 :
188 0 : if (port_map) {
189 0 : alias = g_hash_table_lookup(port_map, target);
190 : }
191 0 : if (alias == NULL) {
192 0 : alias = target;
193 : }
194 0 : crm_debug("Passing %s='%s' with fence action %s targeting %s",
195 : param, alias, action, pcmk__s(target, "no node"));
196 0 : pcmk__insert_dup(arg_list, param, alias);
197 : }
198 : }
199 : }
200 :
201 0 : if (device_args) {
202 0 : g_hash_table_foreach(device_args, append_config_arg, arg_list);
203 : }
204 :
205 0 : return arg_list;
206 : }
207 :
208 : /*!
209 : * \internal
210 : * \brief Free all memory used by a stonith action
211 : *
212 : * \param[in,out] action Action to free
213 : */
214 : void
215 0 : stonith__destroy_action(stonith_action_t *action)
216 : {
217 0 : if (action) {
218 0 : free(action->agent);
219 0 : if (action->args) {
220 0 : g_hash_table_destroy(action->args);
221 : }
222 0 : free(action->action);
223 0 : if (action->svc_action) {
224 0 : services_action_free(action->svc_action);
225 : }
226 0 : pcmk__reset_result(&(action->result));
227 0 : free(action);
228 : }
229 0 : }
230 :
231 : /*!
232 : * \internal
233 : * \brief Get the result of an executed stonith action
234 : *
235 : * \param[in] action Executed action
236 : *
237 : * \return Pointer to action's result (or NULL if \p action is NULL)
238 : */
239 : pcmk__action_result_t *
240 0 : stonith__action_result(stonith_action_t *action)
241 : {
242 0 : return (action == NULL)? NULL : &(action->result);
243 : }
244 :
245 : #define FAILURE_MAX_RETRIES 2
246 :
247 : /*!
248 : * \internal
249 : * \brief Create a new fencing action to be executed
250 : *
251 : * \param[in] agent Fence agent to use
252 : * \param[in] action_name Fencing action to be executed
253 : * \param[in] target Name of target of fencing action (if known)
254 : * \param[in] target_nodeid Node ID of target of fencing action (if known)
255 : * \param[in] timeout_sec Timeout to be used when executing action
256 : * \param[in] device_args Parameters to pass to fence agent
257 : * \param[in] port_map Mapping of target names to device ports
258 : * \param[in] host_arg Agent parameter used to pass target name
259 : *
260 : * \return Newly created fencing action (asserts on error, never NULL)
261 : */
262 : stonith_action_t *
263 0 : stonith__action_create(const char *agent, const char *action_name,
264 : const char *target, uint32_t target_nodeid,
265 : int timeout_sec, GHashTable *device_args,
266 : GHashTable *port_map, const char *host_arg)
267 : {
268 0 : stonith_action_t *action = pcmk__assert_alloc(1, sizeof(stonith_action_t));
269 :
270 0 : action->args = make_args(agent, action_name, target, target_nodeid,
271 : device_args, port_map, host_arg);
272 0 : crm_debug("Preparing '%s' action targeting %s using agent %s",
273 : action_name, pcmk__s(target, "no node"), agent);
274 0 : action->agent = strdup(agent);
275 0 : action->action = strdup(action_name);
276 0 : action->timeout = action->remaining_timeout = timeout_sec;
277 0 : action->max_retries = FAILURE_MAX_RETRIES;
278 :
279 0 : pcmk__set_result(&(action->result), PCMK_OCF_UNKNOWN, PCMK_EXEC_UNKNOWN,
280 : "Initialization bug in fencing library");
281 :
282 0 : if (device_args) {
283 : char buffer[512];
284 0 : const char *value = NULL;
285 :
286 0 : snprintf(buffer, sizeof(buffer), "pcmk_%s_retries", action_name);
287 0 : value = g_hash_table_lookup(device_args, buffer);
288 :
289 0 : if (value) {
290 0 : action->max_retries = atoi(value);
291 : }
292 : }
293 :
294 0 : return action;
295 : }
296 :
297 : static gboolean
298 0 : update_remaining_timeout(stonith_action_t * action)
299 : {
300 0 : int diff = time(NULL) - action->initial_start_time;
301 :
302 0 : if (action->tries >= action->max_retries) {
303 0 : crm_info("Attempted to execute agent %s (%s) the maximum number of times (%d) allowed",
304 : action->agent, action->action, action->max_retries);
305 0 : action->remaining_timeout = 0;
306 0 : } else if ((action->result.execution_status != PCMK_EXEC_TIMEOUT)
307 0 : && (diff < (action->timeout * 0.7))) {
308 : /* only set remaining timeout period if there is 30%
309 : * or greater of the original timeout period left */
310 0 : action->remaining_timeout = action->timeout - diff;
311 : } else {
312 0 : action->remaining_timeout = 0;
313 : }
314 0 : return action->remaining_timeout ? TRUE : FALSE;
315 : }
316 :
317 : /*!
318 : * \internal
319 : * \brief Map a fencing action result to a standard return code
320 : *
321 : * \param[in] result Fencing action result to map
322 : *
323 : * \return Standard Pacemaker return code that best corresponds to \p result
324 : */
325 : int
326 0 : stonith__result2rc(const pcmk__action_result_t *result)
327 : {
328 0 : if (pcmk__result_ok(result)) {
329 0 : return pcmk_rc_ok;
330 : }
331 :
332 0 : switch (result->execution_status) {
333 0 : case PCMK_EXEC_PENDING: return EINPROGRESS;
334 0 : case PCMK_EXEC_CANCELLED: return ECANCELED;
335 0 : case PCMK_EXEC_TIMEOUT: return ETIME;
336 0 : case PCMK_EXEC_NOT_INSTALLED: return ENOENT;
337 0 : case PCMK_EXEC_NOT_SUPPORTED: return EOPNOTSUPP;
338 0 : case PCMK_EXEC_NOT_CONNECTED: return ENOTCONN;
339 0 : case PCMK_EXEC_NO_FENCE_DEVICE: return ENODEV;
340 0 : case PCMK_EXEC_NO_SECRETS: return EACCES;
341 :
342 : /* For the fencing API, PCMK_EXEC_INVALID is used with fencer API
343 : * operations that don't involve executing an agent (for example,
344 : * registering devices). This allows us to use the CRM_EX_* codes in the
345 : * exit status for finer-grained responses.
346 : */
347 0 : case PCMK_EXEC_INVALID:
348 0 : switch (result->exit_status) {
349 0 : case CRM_EX_INVALID_PARAM: return EINVAL;
350 0 : case CRM_EX_INSUFFICIENT_PRIV: return EACCES;
351 0 : case CRM_EX_PROTOCOL: return EPROTO;
352 :
353 : /* CRM_EX_EXPIRED is used for orphaned fencing operations left
354 : * over from a previous instance of the fencer. For API backward
355 : * compatibility, this is mapped to the previously used code for
356 : * this case, EHOSTUNREACH.
357 : */
358 0 : case CRM_EX_EXPIRED: return EHOSTUNREACH;
359 0 : default: break;
360 : }
361 0 : break;
362 :
363 0 : default:
364 0 : break;
365 : }
366 :
367 : // Try to provide useful error code based on result's error output
368 :
369 0 : if (result->action_stderr == NULL) {
370 0 : return ENODATA;
371 :
372 0 : } else if (strcasestr(result->action_stderr, "timed out")
373 0 : || strcasestr(result->action_stderr, "timeout")) {
374 0 : return ETIME;
375 :
376 0 : } else if (strcasestr(result->action_stderr, "unrecognised action")
377 0 : || strcasestr(result->action_stderr, "unrecognized action")
378 0 : || strcasestr(result->action_stderr, "unsupported action")) {
379 0 : return EOPNOTSUPP;
380 : }
381 :
382 : // Oh well, we tried
383 0 : return pcmk_rc_error;
384 : }
385 :
386 : /*!
387 : * \internal
388 : * \brief Determine execution status equivalent of legacy fencer return code
389 : *
390 : * Fence action notifications, and fence action callbacks from older fencers
391 : * (<=2.1.2) in a rolling upgrade, will have only a legacy return code. Map this
392 : * to an execution status as best as possible (essentially, the inverse of
393 : * stonith__result2rc()).
394 : *
395 : * \param[in] rc Legacy return code from fencer
396 : *
397 : * \return Execution status best corresponding to \p rc
398 : */
399 : int
400 0 : stonith__legacy2status(int rc)
401 : {
402 0 : if (rc >= 0) {
403 0 : return PCMK_EXEC_DONE;
404 : }
405 0 : switch (-rc) {
406 0 : case EACCES: return PCMK_EXEC_NO_SECRETS;
407 0 : case ECANCELED: return PCMK_EXEC_CANCELLED;
408 0 : case EHOSTUNREACH: return PCMK_EXEC_INVALID;
409 0 : case EINPROGRESS: return PCMK_EXEC_PENDING;
410 0 : case ENODEV: return PCMK_EXEC_NO_FENCE_DEVICE;
411 0 : case ENOENT: return PCMK_EXEC_NOT_INSTALLED;
412 0 : case ENOTCONN: return PCMK_EXEC_NOT_CONNECTED;
413 0 : case EOPNOTSUPP: return PCMK_EXEC_NOT_SUPPORTED;
414 0 : case EPROTO: return PCMK_EXEC_INVALID;
415 0 : case EPROTONOSUPPORT: return PCMK_EXEC_NOT_SUPPORTED;
416 0 : case ETIME: return PCMK_EXEC_TIMEOUT;
417 0 : case ETIMEDOUT: return PCMK_EXEC_TIMEOUT;
418 0 : default: return PCMK_EXEC_ERROR;
419 : }
420 : }
421 :
422 : /*!
423 : * \internal
424 : * \brief Add a fencing result to an XML element as attributes
425 : *
426 : * \param[in,out] xml XML element to add result to
427 : * \param[in] result Fencing result to add (assume success if NULL)
428 : */
429 : void
430 0 : stonith__xe_set_result(xmlNode *xml, const pcmk__action_result_t *result)
431 : {
432 0 : int exit_status = CRM_EX_OK;
433 0 : enum pcmk_exec_status execution_status = PCMK_EXEC_DONE;
434 0 : const char *exit_reason = NULL;
435 0 : const char *action_stdout = NULL;
436 0 : int rc = pcmk_ok;
437 :
438 0 : CRM_CHECK(xml != NULL, return);
439 :
440 0 : if (result != NULL) {
441 0 : exit_status = result->exit_status;
442 0 : execution_status = result->execution_status;
443 0 : exit_reason = result->exit_reason;
444 0 : action_stdout = result->action_stdout;
445 0 : rc = pcmk_rc2legacy(stonith__result2rc(result));
446 : }
447 :
448 0 : crm_xml_add_int(xml, PCMK__XA_OP_STATUS, (int) execution_status);
449 0 : crm_xml_add_int(xml, PCMK__XA_RC_CODE, exit_status);
450 0 : crm_xml_add(xml, PCMK_XA_EXIT_REASON, exit_reason);
451 0 : crm_xml_add(xml, PCMK__XA_ST_OUTPUT, action_stdout);
452 :
453 : /* @COMPAT Peers in rolling upgrades, Pacemaker Remote nodes, and external
454 : * code that use libstonithd <=2.1.2 don't check for the full result, and
455 : * need a legacy return code instead.
456 : */
457 0 : crm_xml_add_int(xml, PCMK__XA_ST_RC, rc);
458 : }
459 :
460 : /*!
461 : * \internal
462 : * \brief Find a fencing result beneath an XML element
463 : *
464 : * \param[in] xml XML element to search
465 : *
466 : * \return \p xml or descendant of it that contains a fencing result, else NULL
467 : */
468 : xmlNode *
469 0 : stonith__find_xe_with_result(xmlNode *xml)
470 : {
471 0 : xmlNode *match = get_xpath_object("//@" PCMK__XA_RC_CODE, xml, LOG_NEVER);
472 :
473 0 : if (match == NULL) {
474 : /* @COMPAT Peers <=2.1.2 in a rolling upgrade provide only a legacy
475 : * return code, not a full result, so check for that.
476 : */
477 0 : match = get_xpath_object("//@" PCMK__XA_ST_RC, xml, LOG_ERR);
478 : }
479 0 : return match;
480 : }
481 :
482 : /*!
483 : * \internal
484 : * \brief Get a fencing result from an XML element's attributes
485 : *
486 : * \param[in] xml XML element with fencing result
487 : * \param[out] result Where to store fencing result
488 : */
489 : void
490 0 : stonith__xe_get_result(const xmlNode *xml, pcmk__action_result_t *result)
491 : {
492 0 : int exit_status = CRM_EX_OK;
493 0 : int execution_status = PCMK_EXEC_DONE;
494 0 : const char *exit_reason = NULL;
495 0 : char *action_stdout = NULL;
496 :
497 0 : CRM_CHECK((xml != NULL) && (result != NULL), return);
498 :
499 0 : exit_reason = crm_element_value(xml, PCMK_XA_EXIT_REASON);
500 0 : action_stdout = crm_element_value_copy(xml, PCMK__XA_ST_OUTPUT);
501 :
502 : // A result must include an exit status and execution status
503 0 : if ((crm_element_value_int(xml, PCMK__XA_RC_CODE, &exit_status) < 0)
504 0 : || (crm_element_value_int(xml, PCMK__XA_OP_STATUS,
505 : &execution_status) < 0)) {
506 0 : int rc = pcmk_ok;
507 0 : exit_status = CRM_EX_ERROR;
508 :
509 : /* @COMPAT Peers <=2.1.2 in rolling upgrades provide only a legacy
510 : * return code, not a full result, so check for that.
511 : */
512 0 : if (crm_element_value_int(xml, PCMK__XA_ST_RC, &rc) == 0) {
513 0 : if ((rc == pcmk_ok) || (rc == -EINPROGRESS)) {
514 0 : exit_status = CRM_EX_OK;
515 : }
516 0 : execution_status = stonith__legacy2status(rc);
517 0 : exit_reason = pcmk_strerror(rc);
518 :
519 : } else {
520 0 : execution_status = PCMK_EXEC_ERROR;
521 0 : exit_reason = "Fencer reply contained neither a full result "
522 : "nor a legacy return code (bug?)";
523 : }
524 : }
525 0 : pcmk__set_result(result, exit_status, execution_status, exit_reason);
526 0 : pcmk__set_result_output(result, action_stdout, NULL);
527 : }
528 :
529 : static void
530 0 : stonith_action_async_done(svc_action_t *svc_action)
531 : {
532 0 : stonith_action_t *action = (stonith_action_t *) svc_action->cb_data;
533 :
534 0 : set_result_from_svc_action(action, svc_action);
535 0 : svc_action->params = NULL;
536 0 : log_action(action, action->pid);
537 :
538 0 : if (!pcmk__result_ok(&(action->result))
539 0 : && update_remaining_timeout(action)) {
540 :
541 0 : int rc = internal_stonith_action_execute(action);
542 0 : if (rc == pcmk_ok) {
543 0 : return;
544 : }
545 : }
546 :
547 0 : if (action->done_cb) {
548 0 : action->done_cb(action->pid, &(action->result), action->userdata);
549 : }
550 :
551 0 : action->svc_action = NULL; // don't remove our caller
552 0 : stonith__destroy_action(action);
553 : }
554 :
555 : static void
556 0 : stonith_action_async_forked(svc_action_t *svc_action)
557 : {
558 0 : stonith_action_t *action = (stonith_action_t *) svc_action->cb_data;
559 :
560 0 : action->pid = svc_action->pid;
561 0 : action->svc_action = svc_action;
562 :
563 0 : if (action->fork_cb) {
564 0 : (action->fork_cb) (svc_action->pid, action->userdata);
565 : }
566 :
567 0 : pcmk__set_result(&(action->result), PCMK_OCF_UNKNOWN, PCMK_EXEC_PENDING,
568 : NULL);
569 :
570 0 : crm_trace("Child process %d performing action '%s' successfully forked",
571 : action->pid, action->action);
572 0 : }
573 :
574 : static int
575 0 : internal_stonith_action_execute(stonith_action_t * action)
576 : {
577 0 : int rc = -EPROTO;
578 0 : int is_retry = 0;
579 0 : svc_action_t *svc_action = NULL;
580 : static int stonith_sequence = 0;
581 0 : char *buffer = NULL;
582 :
583 0 : CRM_CHECK(action != NULL, return -EINVAL);
584 :
585 0 : if ((action->action == NULL) || (action->args == NULL)
586 0 : || (action->agent == NULL)) {
587 0 : pcmk__set_result(&(action->result), PCMK_OCF_UNKNOWN_ERROR,
588 : PCMK_EXEC_ERROR_FATAL, "Bug in fencing library");
589 0 : return -EINVAL;
590 : }
591 :
592 0 : if (!action->tries) {
593 0 : action->initial_start_time = time(NULL);
594 : }
595 0 : action->tries++;
596 :
597 0 : if (action->tries > 1) {
598 0 : crm_info("Attempt %d to execute %s (%s). remaining timeout is %d",
599 : action->tries, action->agent, action->action, action->remaining_timeout);
600 0 : is_retry = 1;
601 : }
602 :
603 0 : buffer = crm_strdup_printf(PCMK__FENCE_BINDIR "/%s",
604 : basename(action->agent));
605 0 : svc_action = services_action_create_generic(buffer, NULL);
606 0 : free(buffer);
607 :
608 0 : if (svc_action->rc != PCMK_OCF_UNKNOWN) {
609 0 : set_result_from_svc_action(action, svc_action);
610 0 : services_action_free(svc_action);
611 0 : return -E2BIG;
612 : }
613 :
614 0 : svc_action->timeout = 1000 * action->remaining_timeout;
615 0 : svc_action->standard = strdup(PCMK_RESOURCE_CLASS_STONITH);
616 0 : svc_action->id = crm_strdup_printf("%s_%s_%dof%d", basename(action->agent),
617 : action->action, action->tries,
618 : action->max_retries);
619 0 : svc_action->agent = strdup(action->agent);
620 0 : svc_action->sequence = stonith_sequence++;
621 0 : svc_action->params = action->args;
622 0 : svc_action->cb_data = (void *) action;
623 0 : svc_action->flags = pcmk__set_flags_as(__func__, __LINE__,
624 : LOG_TRACE, "Action",
625 0 : svc_action->id, svc_action->flags,
626 : SVC_ACTION_NON_BLOCKED,
627 : "SVC_ACTION_NON_BLOCKED");
628 :
629 : /* keep retries from executing out of control and free previous results */
630 0 : if (is_retry) {
631 0 : pcmk__reset_result(&(action->result));
632 0 : sleep(1);
633 : }
634 :
635 0 : if (action->async) {
636 : // We never create a recurring action, so this should always return TRUE
637 0 : CRM_LOG_ASSERT(services_action_async_fork_notify(svc_action,
638 : &stonith_action_async_done,
639 : &stonith_action_async_forked));
640 0 : return pcmk_ok;
641 :
642 0 : } else if (services_action_sync(svc_action)) { // sync success
643 0 : rc = pcmk_ok;
644 :
645 : } else { // sync failure
646 0 : rc = -ECONNABORTED;
647 : }
648 :
649 0 : set_result_from_svc_action(action, svc_action);
650 0 : svc_action->params = NULL;
651 0 : services_action_free(svc_action);
652 0 : return rc;
653 : }
654 :
655 : /*!
656 : * \internal
657 : * \brief Kick off execution of an async stonith action
658 : *
659 : * \param[in,out] action Action to be executed
660 : * \param[in,out] userdata Datapointer to be passed to callbacks
661 : * \param[in] done Callback to notify action has failed/succeeded
662 : * \param[in] fork_callback Callback to notify successful fork of child
663 : *
664 : * \return pcmk_ok if ownership of action has been taken, -errno otherwise
665 : */
666 : int
667 0 : stonith__execute_async(stonith_action_t * action, void *userdata,
668 : void (*done) (int pid,
669 : const pcmk__action_result_t *result,
670 : void *user_data),
671 : void (*fork_cb) (int pid, void *user_data))
672 : {
673 0 : if (!action) {
674 0 : return -EINVAL;
675 : }
676 :
677 0 : action->userdata = userdata;
678 0 : action->done_cb = done;
679 0 : action->fork_cb = fork_cb;
680 0 : action->async = true;
681 :
682 0 : return internal_stonith_action_execute(action);
683 : }
684 :
685 : /*!
686 : * \internal
687 : * \brief Execute a stonith action
688 : *
689 : * \param[in,out] action Action to execute
690 : *
691 : * \return pcmk_ok on success, -errno otherwise
692 : */
693 : int
694 0 : stonith__execute(stonith_action_t *action)
695 : {
696 0 : int rc = pcmk_ok;
697 :
698 0 : CRM_CHECK(action != NULL, return -EINVAL);
699 :
700 : // Keep trying until success, max retries, or timeout
701 : do {
702 0 : rc = internal_stonith_action_execute(action);
703 0 : } while ((rc != pcmk_ok) && update_remaining_timeout(action));
704 :
705 0 : return rc;
706 : }
|