pacemaker 3.0.1-16e74fc4da
Scalable High-Availability cluster resource manager
Loading...
Searching...
No Matches
services_linux.c
Go to the documentation of this file.
1/*
2 * Copyright 2010-2025 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 <sys/types.h>
13#include <sys/stat.h>
14#include <sys/wait.h>
15#include <errno.h>
16#include <unistd.h>
17#include <dirent.h>
18#include <grp.h>
19#include <string.h>
20#include <sys/time.h>
21#include <sys/resource.h>
22
23#include "crm/crm.h"
24#include "crm/common/mainloop.h"
25#include "crm/services.h"
27
28#include "services_private.h"
29
30static void close_pipe(int fildes[]);
31
32/* We have two alternative ways of handling SIGCHLD when synchronously waiting
33 * for spawned processes to complete. Both rely on polling a file descriptor to
34 * discover SIGCHLD events.
35 *
36 * If sys/signalfd.h is available (e.g. on Linux), we call signalfd() to
37 * generate the file descriptor. Otherwise, we use the "self-pipe trick"
38 * (opening a pipe and writing a byte to it when SIGCHLD is received).
39 */
40#ifdef HAVE_SYS_SIGNALFD_H
41
42// signalfd() implementation
43
44#include <sys/signalfd.h>
45
46// Everything needed to manage SIGCHLD handling
47struct sigchld_data_s {
48 sigset_t mask; // Signals to block now (including SIGCHLD)
49 sigset_t old_mask; // Previous set of blocked signals
50 bool ignored; // If SIGCHLD for another child has been ignored
51};
52
53// Initialize SIGCHLD data and prepare for use
54static bool
55sigchld_setup(struct sigchld_data_s *data)
56{
57 sigemptyset(&(data->mask));
58 sigaddset(&(data->mask), SIGCHLD);
59
60 sigemptyset(&(data->old_mask));
61
62 // Block SIGCHLD (saving previous set of blocked signals to restore later)
63 if (sigprocmask(SIG_BLOCK, &(data->mask), &(data->old_mask)) < 0) {
64 crm_info("Wait for child process completion failed: %s "
65 QB_XS " source=sigprocmask", pcmk_rc_str(errno));
66 return false;
67 }
68
69 data->ignored = false;
70
71 return true;
72}
73
74// Get a file descriptor suitable for polling for SIGCHLD events
75static int
76sigchld_open(struct sigchld_data_s *data)
77{
78 int fd;
79
80 CRM_CHECK(data != NULL, return -1);
81
82 fd = signalfd(-1, &(data->mask), SFD_NONBLOCK);
83 if (fd < 0) {
84 crm_info("Wait for child process completion failed: %s "
85 QB_XS " source=signalfd", pcmk_rc_str(errno));
86 }
87 return fd;
88}
89
90// Close a file descriptor returned by sigchld_open()
91static void
92sigchld_close(int fd)
93{
94 if (fd > 0) {
95 close(fd);
96 }
97}
98
99// Return true if SIGCHLD was received from polled fd
100static bool
101sigchld_received(int fd, int pid, struct sigchld_data_s *data)
102{
103 struct signalfd_siginfo fdsi;
104 ssize_t s;
105
106 if (fd < 0) {
107 return false;
108 }
109 s = read(fd, &fdsi, sizeof(struct signalfd_siginfo));
110 if (s != sizeof(struct signalfd_siginfo)) {
111 crm_info("Wait for child process completion failed: %s "
112 QB_XS " source=read", pcmk_rc_str(errno));
113
114 } else if (fdsi.ssi_signo == SIGCHLD) {
115 if (fdsi.ssi_pid == pid) {
116 return true;
117
118 } else {
119 /* This SIGCHLD is for another child. We have to ignore it here but
120 * will still need to resend it after this synchronous action has
121 * completed and SIGCHLD has been restored to be handled by the
122 * previous SIGCHLD handler, so that it will be handled.
123 */
124 data->ignored = true;
125 return false;
126 }
127 }
128 return false;
129}
130
131// Do anything needed after done waiting for SIGCHLD
132static void
133sigchld_cleanup(struct sigchld_data_s *data)
134{
135 // Restore the original set of blocked signals
136 if ((sigismember(&(data->old_mask), SIGCHLD) == 0)
137 && (sigprocmask(SIG_UNBLOCK, &(data->mask), NULL) < 0)) {
138 crm_warn("Could not clean up after child process completion: %s",
139 pcmk_rc_str(errno));
140 }
141
142 // Resend any ignored SIGCHLD for other children so that they'll be handled.
143 if (data->ignored && kill(getpid(), SIGCHLD) != 0) {
144 crm_warn("Could not resend ignored SIGCHLD to ourselves: %s",
145 pcmk_rc_str(errno));
146 }
147}
148
149#else // HAVE_SYS_SIGNALFD_H not defined
150
151// Self-pipe implementation (see above for function descriptions)
152
153struct sigchld_data_s {
154 int pipe_fd[2]; // Pipe file descriptors
155 struct sigaction sa; // Signal handling info (with SIGCHLD)
156 struct sigaction old_sa; // Previous signal handling info
157 bool ignored; // If SIGCHLD for another child has been ignored
158};
159
160// We need a global to use in the signal handler
161volatile struct sigchld_data_s *last_sigchld_data = NULL;
162
163static void
164sigchld_handler(void)
165{
166 // We received a SIGCHLD, so trigger pipe polling
167 if ((last_sigchld_data != NULL)
168 && (last_sigchld_data->pipe_fd[1] >= 0)
169 && (write(last_sigchld_data->pipe_fd[1], "", 1) == -1)) {
170 crm_info("Wait for child process completion failed: %s "
171 QB_XS " source=write", pcmk_rc_str(errno));
172 }
173}
174
175static bool
176sigchld_setup(struct sigchld_data_s *data)
177{
178 int rc;
179
180 data->pipe_fd[0] = data->pipe_fd[1] = -1;
181
182 if (pipe(data->pipe_fd) == -1) {
183 crm_info("Wait for child process completion failed: %s "
184 QB_XS " source=pipe", pcmk_rc_str(errno));
185 return false;
186 }
187
188 rc = pcmk__set_nonblocking(data->pipe_fd[0]);
189 if (rc != pcmk_rc_ok) {
190 crm_info("Could not set pipe input non-blocking: %s " QB_XS " rc=%d",
191 pcmk_rc_str(rc), rc);
192 }
193 rc = pcmk__set_nonblocking(data->pipe_fd[1]);
194 if (rc != pcmk_rc_ok) {
195 crm_info("Could not set pipe output non-blocking: %s " QB_XS " rc=%d",
196 pcmk_rc_str(rc), rc);
197 }
198
199 // Set SIGCHLD handler
200 data->sa.sa_handler = (sighandler_t) sigchld_handler;
201 data->sa.sa_flags = 0;
202 sigemptyset(&(data->sa.sa_mask));
203 if (sigaction(SIGCHLD, &(data->sa), &(data->old_sa)) < 0) {
204 crm_info("Wait for child process completion failed: %s "
205 QB_XS " source=sigaction", pcmk_rc_str(errno));
206 }
207
208 data->ignored = false;
209
210 // Remember data for use in signal handler
212 return true;
213}
214
215static int
216sigchld_open(struct sigchld_data_s *data)
217{
218 CRM_CHECK(data != NULL, return -1);
219 return data->pipe_fd[0];
220}
221
222static void
223sigchld_close(int fd)
224{
225 // Pipe will be closed in sigchld_cleanup()
226 return;
227}
228
229static bool
230sigchld_received(int fd, int pid, struct sigchld_data_s *data)
231{
232 char ch;
233
234 if (fd < 0) {
235 return false;
236 }
237
238 // Clear out the self-pipe
239 while (read(fd, &ch, 1) == 1) /*omit*/;
240 return true;
241}
242
243static void
244sigchld_cleanup(struct sigchld_data_s *data)
245{
246 // Restore the previous SIGCHLD handler
247 if (sigaction(SIGCHLD, &(data->old_sa), NULL) < 0) {
248 crm_warn("Could not clean up after child process completion: %s",
249 pcmk_rc_str(errno));
250 }
251
252 close_pipe(data->pipe_fd);
253
254 // Resend any ignored SIGCHLD for other children so that they'll be handled.
255 if (data->ignored && kill(getpid(), SIGCHLD) != 0) {
256 crm_warn("Could not resend ignored SIGCHLD to ourselves: %s",
257 pcmk_rc_str(errno));
258 }
259}
260
261#endif
262
269static void
270close_pipe(int fildes[])
271{
272 if (fildes[0] >= 0) {
273 close(fildes[0]);
274 fildes[0] = -1;
275 }
276 if (fildes[1] >= 0) {
277 close(fildes[1]);
278 fildes[1] = -1;
279 }
280}
281
282#define out_type(is_stderr) ((is_stderr)? "stderr" : "stdout")
283
284// Maximum number of bytes of stdout or stderr we'll accept
285#define MAX_OUTPUT (10 * 1024 * 1024)
286
287static gboolean
288svc_read_output(int fd, svc_action_t * op, bool is_stderr)
289{
290 char *data = NULL;
291 ssize_t rc = 0;
292 size_t len = 0;
293 size_t discarded = 0;
294 char buf[500];
295 static const size_t buf_read_len = sizeof(buf) - 1;
296
297 if (fd < 0) {
298 crm_trace("No fd for %s", op->id);
299 return FALSE;
300 }
301
302 if (is_stderr && op->stderr_data) {
303 len = strlen(op->stderr_data);
304 data = op->stderr_data;
305 crm_trace("Reading %s stderr into offset %zu", op->id, len);
306
307 } else if (is_stderr == FALSE && op->stdout_data) {
308 len = strlen(op->stdout_data);
309 data = op->stdout_data;
310 crm_trace("Reading %s stdout into offset %zu", op->id, len);
311
312 } else {
313 crm_trace("Reading %s %s", op->id, out_type(is_stderr));
314 }
315
316 do {
317 errno = 0;
318 rc = read(fd, buf, buf_read_len);
319 if (rc > 0) {
320 if (len < MAX_OUTPUT) {
321 buf[rc] = 0;
322 crm_trace("Received %zd bytes of %s %s: %.80s",
323 rc, op->id, out_type(is_stderr), buf);
324 data = pcmk__realloc(data, len + rc + 1);
325 strcpy(data + len, buf);
326 len += rc;
327 } else {
328 discarded += rc;
329 }
330
331 } else if (errno != EINTR) { // Fatal error or EOF
332 rc = 0;
333 break;
334 }
335 } while ((rc == buf_read_len) || (rc < 0));
336
337 if (discarded > 0) {
338 crm_warn("Truncated %s %s to %zu bytes (discarded %zu)",
339 op->id, out_type(is_stderr), len, discarded);
340 }
341
342 if (is_stderr) {
343 op->stderr_data = data;
344 } else {
345 op->stdout_data = data;
346 }
347
348 return rc != 0;
349}
350
351static int
352dispatch_stdout(gpointer userdata)
353{
354 svc_action_t *op = (svc_action_t *) userdata;
355
356 return svc_read_output(op->opaque->stdout_fd, op, FALSE);
357}
358
359static int
360dispatch_stderr(gpointer userdata)
361{
362 svc_action_t *op = (svc_action_t *) userdata;
363
364 return svc_read_output(op->opaque->stderr_fd, op, TRUE);
365}
366
367static void
368pipe_out_done(gpointer user_data)
369{
370 svc_action_t *op = (svc_action_t *) user_data;
371
372 crm_trace("%p", op);
373
374 op->opaque->stdout_gsource = NULL;
375 if (op->opaque->stdout_fd > STDOUT_FILENO) {
376 close(op->opaque->stdout_fd);
377 }
378 op->opaque->stdout_fd = -1;
379}
380
381static void
382pipe_err_done(gpointer user_data)
383{
384 svc_action_t *op = (svc_action_t *) user_data;
385
386 op->opaque->stderr_gsource = NULL;
387 if (op->opaque->stderr_fd > STDERR_FILENO) {
388 close(op->opaque->stderr_fd);
389 }
390 op->opaque->stderr_fd = -1;
391}
392
393static struct mainloop_fd_callbacks stdout_callbacks = {
394 .dispatch = dispatch_stdout,
395 .destroy = pipe_out_done,
396};
397
398static struct mainloop_fd_callbacks stderr_callbacks = {
399 .dispatch = dispatch_stderr,
400 .destroy = pipe_err_done,
401};
402
403static void
404set_ocf_env(const char *key, const char *value, gpointer user_data)
405{
406 if (setenv(key, value, 1) != 0) {
407 crm_perror(LOG_ERR, "setenv failed for key:%s and value:%s", key, value);
408 }
409}
410
411static void
412set_ocf_env_with_prefix(gpointer key, gpointer value, gpointer user_data)
413{
414 char buffer[500];
415
416 snprintf(buffer, sizeof(buffer), strcmp(key, "OCF_CHECK_LEVEL") != 0 ? "OCF_RESKEY_%s" : "%s", (char *)key);
417 set_ocf_env(buffer, value, user_data);
418}
419
420static void
421set_alert_env(gpointer key, gpointer value, gpointer user_data)
422{
423 int rc;
424
425 if (value != NULL) {
426 rc = setenv(key, value, 1);
427 } else {
428 rc = unsetenv(key);
429 }
430
431 if (rc < 0) {
432 crm_perror(LOG_ERR, "setenv %s=%s",
433 (char*)key, (value? (char*)value : ""));
434 } else {
435 crm_trace("setenv %s=%s", (char*)key, (value? (char*)value : ""));
436 }
437}
438
445static void
446add_action_env_vars(const svc_action_t *op)
447{
448 void (*env_setter)(gpointer, gpointer, gpointer) = NULL;
449 if (op->agent == NULL) {
450 env_setter = set_alert_env; /* we deal with alert handler */
451
452 } else if (pcmk__str_eq(op->standard, PCMK_RESOURCE_CLASS_OCF, pcmk__str_casei)) {
453 env_setter = set_ocf_env_with_prefix;
454 }
455
456 if (env_setter != NULL && op->params != NULL) {
457 g_hash_table_foreach(op->params, env_setter, NULL);
458 }
459
460 if (env_setter == NULL || env_setter == set_alert_env) {
461 return;
462 }
463
464 set_ocf_env("OCF_RA_VERSION_MAJOR", PCMK_OCF_MAJOR_VERSION, NULL);
465 set_ocf_env("OCF_RA_VERSION_MINOR", PCMK_OCF_MINOR_VERSION, NULL);
466 set_ocf_env("OCF_ROOT", PCMK_OCF_ROOT, NULL);
467 set_ocf_env("OCF_EXIT_REASON_PREFIX", PCMK_OCF_REASON_PREFIX, NULL);
468
469 if (op->rsc) {
470 set_ocf_env("OCF_RESOURCE_INSTANCE", op->rsc, NULL);
471 }
472
473 if (op->agent != NULL) {
474 set_ocf_env("OCF_RESOURCE_TYPE", op->agent, NULL);
475 }
476
477 /* Notes: this is not added to specification yet. Sept 10,2004 */
478 if (op->provider != NULL) {
479 set_ocf_env("OCF_RESOURCE_PROVIDER", op->provider, NULL);
480 }
481}
482
483static void
484pipe_in_single_parameter(gpointer key, gpointer value, gpointer user_data)
485{
486 svc_action_t *op = user_data;
487 char *buffer = crm_strdup_printf("%s=%s\n", (char *)key, (char *) value);
488 size_t len = strlen(buffer);
489 size_t total = 0;
490 ssize_t ret = 0;
491
492 do {
493 errno = 0;
494 ret = write(op->opaque->stdin_fd, buffer + total, len - total);
495 if (ret > 0) {
496 total += ret;
497 }
498 } while ((errno == EINTR) && (total < len));
499 free(buffer);
500}
501
508static void
509pipe_in_action_stdin_parameters(const svc_action_t *op)
510{
511 if (op->params) {
512 g_hash_table_foreach(op->params, pipe_in_single_parameter, (gpointer) op);
513 }
514}
515
516gboolean
518{
519 svc_action_t *op = data;
520
521 crm_debug("Scheduling another invocation of %s", op->id);
522
523 /* Clean out the old result */
524 free(op->stdout_data);
525 op->stdout_data = NULL;
526 free(op->stderr_data);
527 op->stderr_data = NULL;
528 op->opaque->repeat_timer = 0;
529
530 services_action_async(op, NULL);
531 return FALSE;
532}
533
552int
554{
555 CRM_CHECK((op != NULL) && !(op->synchronous), return EINVAL);
556
557 if (op->interval_ms != 0) {
558 // Recurring operations must be either cancelled or rescheduled
559 if (op->cancel) {
562 } else {
565 op);
566 }
567 }
568
569 if (op->opaque->callback != NULL) {
570 op->opaque->callback(op);
571 }
572
573 // Stop tracking the operation (as in-flight or blocked)
574 op->pid = 0;
576
577 if ((op->interval_ms != 0) && !(op->cancel)) {
578 // Do not free recurring actions (they will get freed when cancelled)
580 return EBUSY;
581 }
582
584 return pcmk_rc_ok;
585}
586
587static void
588close_op_input(svc_action_t *op)
589{
590 if (op->opaque->stdin_fd >= 0) {
591 close(op->opaque->stdin_fd);
592 }
593}
594
595static void
596finish_op_output(svc_action_t *op, bool is_stderr)
597{
598 mainloop_io_t **source;
599 int fd;
600
601 if (is_stderr) {
602 source = &(op->opaque->stderr_gsource);
603 fd = op->opaque->stderr_fd;
604 } else {
605 source = &(op->opaque->stdout_gsource);
606 fd = op->opaque->stdout_fd;
607 }
608
609 if (op->synchronous || *source) {
610 crm_trace("Finish reading %s[%d] %s",
611 op->id, op->pid, (is_stderr? "stderr" : "stdout"));
612 svc_read_output(fd, op, is_stderr);
613 if (op->synchronous) {
614 close(fd);
615 } else {
616 mainloop_del_fd(*source);
617 *source = NULL;
618 }
619 }
620}
621
622// Log an operation's stdout and stderr
623static void
624log_op_output(svc_action_t *op)
625{
626 char *prefix = crm_strdup_printf("%s[%d] error output", op->id, op->pid);
627
628 /* The library caller has better context to know how important the output
629 * is, so log it at info and debug severity here. They can log it again at
630 * higher severity if appropriate.
631 */
632 crm_log_output(LOG_INFO, prefix, op->stderr_data);
633 strcpy(prefix + strlen(prefix) - strlen("error output"), "output");
634 crm_log_output(LOG_DEBUG, prefix, op->stdout_data);
635 free(prefix);
636}
637
638// Truncate exit reasons at this many characters
639#define EXIT_REASON_MAX_LEN 128
640
641static void
642parse_exit_reason_from_stderr(svc_action_t *op)
643{
644 const char *reason_start = NULL;
645 const char *reason_end = NULL;
646 const int prefix_len = strlen(PCMK_OCF_REASON_PREFIX);
647
648 if ((op->stderr_data == NULL) ||
649 // Only OCF agents have exit reasons in stderr
650 !pcmk__str_eq(op->standard, PCMK_RESOURCE_CLASS_OCF, pcmk__str_none)) {
651 return;
652 }
653
654 // Find the last occurrence of the magic string indicating an exit reason
655 for (const char *cur = strstr(op->stderr_data, PCMK_OCF_REASON_PREFIX);
656 cur != NULL; cur = strstr(cur, PCMK_OCF_REASON_PREFIX)) {
657
658 cur += prefix_len; // Skip over magic string
659 reason_start = cur;
660 }
661
662 if ((reason_start == NULL) || (reason_start[0] == '\n')
663 || (reason_start[0] == '\0')) {
664 return; // No or empty exit reason
665 }
666
667 // Exit reason goes to end of line (or end of output)
668 reason_end = strchr(reason_start, '\n');
669 if (reason_end == NULL) {
670 reason_end = reason_start + strlen(reason_start);
671 }
672
673 // Limit size of exit reason to something reasonable
674 if (reason_end > (reason_start + EXIT_REASON_MAX_LEN)) {
675 reason_end = reason_start + EXIT_REASON_MAX_LEN;
676 }
677
678 free(op->opaque->exit_reason);
679 op->opaque->exit_reason = strndup(reason_start, reason_end - reason_start);
680}
681
692static void
693async_action_complete(mainloop_child_t *p, pid_t pid, int core, int signo,
694 int exitcode)
695{
697
699 CRM_CHECK(op->pid == pid,
701 PCMK_EXEC_ERROR, "Bug in mainloop handling");
702 return);
703
704 /* Depending on the priority the mainloop gives the stdout and stderr
705 * file descriptors, this function could be called before everything has
706 * been read from them, so force a final read now.
707 */
708 finish_op_output(op, true);
709 finish_op_output(op, false);
710
711 close_op_input(op);
712
713 if (signo == 0) {
714 crm_debug("%s[%d] exited with status %d", op->id, op->pid, exitcode);
715 services__set_result(op, exitcode, PCMK_EXEC_DONE, NULL);
716 log_op_output(op);
717 parse_exit_reason_from_stderr(op);
718
719 } else if (mainloop_child_timeout(p)) {
720 const char *kind = services__action_kind(op);
721
722 crm_info("%s %s[%d] timed out after %s",
723 kind, op->id, op->pid, pcmk__readable_interval(op->timeout));
726 "%s did not complete within %s",
728
729 } else if (op->cancel) {
730 /* If an in-flight recurring operation was killed because it was
731 * cancelled, don't treat that as a failure.
732 */
733 crm_info("%s[%d] terminated with signal %d (%s)",
734 op->id, op->pid, signo, strsignal(signo));
736
737 } else {
738 crm_info("%s[%d] terminated with signal %d (%s)",
739 op->id, op->pid, signo, strsignal(signo));
741 "%s interrupted by %s signal",
742 services__action_kind(op), strsignal(signo));
743 }
744
746}
747
761int
763{
764 if ((op == NULL) || (op->standard == NULL)) {
766 }
767
768#if PCMK__ENABLE_LSB
769 if (pcmk__str_eq(op->standard, PCMK_RESOURCE_CLASS_LSB, pcmk__str_casei)
770 && pcmk__str_eq(op->action, PCMK_ACTION_STATUS, pcmk__str_casei)) {
771
773 }
774#endif
775
777}
778
792int
794{
795 if ((op == NULL) || (op->standard == NULL)) {
797 }
798
799#if PCMK__ENABLE_LSB
800 if (pcmk__str_eq(op->standard, PCMK_RESOURCE_CLASS_LSB, pcmk__str_casei)
801 && pcmk__str_eq(op->action, PCMK_ACTION_STATUS, pcmk__str_casei)) {
802
804 }
805#endif
806
808}
809
823int
825{
826 if ((op == NULL) || (op->standard == NULL)) {
828 }
829
830#if PCMK__ENABLE_LSB
831 if (pcmk__str_eq(op->standard, PCMK_RESOURCE_CLASS_LSB, pcmk__str_casei)
832 && pcmk__str_eq(op->action, PCMK_ACTION_STATUS, pcmk__str_casei)) {
833
835 }
836#endif
837
839}
840
855int
857{
858 if ((op == NULL) || (op->standard == NULL)) {
860 }
861
862#if PCMK__ENABLE_LSB
863 if (pcmk__str_eq(op->standard, PCMK_RESOURCE_CLASS_LSB, pcmk__str_casei)
864 && pcmk__str_eq(op->action, PCMK_ACTION_STATUS, pcmk__str_casei)) {
865
867 }
868#endif
869
871}
872
873
883void
885{
886 const char *name = op->opaque->exec;
887
888 if (name == NULL) {
889 name = op->agent;
890 if (name == NULL) {
891 name = op->id;
892 }
893 }
894
895 switch (error) { /* see execve(2), stat(2) and fork(2) */
896 case ENOENT: /* No such file or directory */
897 case EISDIR: /* Is a directory */
898 case ENOTDIR: /* Path component is not a directory */
899 case EINVAL: /* Invalid executable format */
900 case ENOEXEC: /* Invalid executable format */
902 PCMK_EXEC_NOT_INSTALLED, "%s: %s",
903 name, pcmk_rc_str(error));
904 break;
905 case EACCES: /* permission denied (various errors) */
906 case EPERM: /* permission denied (various errors) */
908 PCMK_EXEC_ERROR, "%s: %s",
909 name, pcmk_rc_str(error));
910 break;
911 default:
914 }
915}
916
925static void
926exit_child(const svc_action_t *op, int exit_status, const char *exit_reason)
927{
928 if ((op != NULL) && (exit_reason != NULL)
929 && pcmk__str_eq(op->standard, PCMK_RESOURCE_CLASS_OCF,
931 fprintf(stderr, PCMK_OCF_REASON_PREFIX "%s\n", exit_reason);
932 }
934 _exit(exit_status);
935}
936
937static void
938action_launch_child(svc_action_t *op)
939{
940 int rc;
941
942 /* SIGPIPE is ignored (which is different from signal blocking) by the gnutls library.
943 * Depending on the libqb version in use, libqb may set SIGPIPE to be ignored as well.
944 * We do not want this to be inherited by the child process. By resetting this the signal
945 * to the default behavior, we avoid some potential odd problems that occur during OCF
946 * scripts when SIGPIPE is ignored by the environment. */
947 signal(SIGPIPE, SIG_DFL);
948
949 if (sched_getscheduler(0) != SCHED_OTHER) {
950 struct sched_param sp;
951
952 memset(&sp, 0, sizeof(sp));
953 sp.sched_priority = 0;
954
955 if (sched_setscheduler(0, SCHED_OTHER, &sp) == -1) {
956 crm_info("Could not reset scheduling policy for %s", op->id);
957 }
958 }
959
960 if (setpriority(PRIO_PROCESS, 0, 0) == -1) {
961 crm_info("Could not reset process priority for %s", op->id);
962 }
963
964 /* Man: The call setpgrp() is equivalent to setpgid(0,0)
965 * _and_ compiles on BSD variants too
966 * need to investigate if it works the same too.
967 */
968 setpgid(0, 0);
969
971
972 /* It would be nice if errors in this function could be reported as
973 * execution status (for example, PCMK_EXEC_NO_SECRETS for the secrets error
974 * below) instead of exit status. However, we've already forked, so
975 * exit status is all we have. At least for OCF actions, we can output an
976 * exit reason for the parent to parse.
977 *
978 * @TODO It might be better to substitute secrets in the parent before
979 * forking, so that if it fails, we can give a better message and result,
980 * and avoid the fork.
981 */
982
983#if PCMK__ENABLE_CIBSECRETS
984 rc = pcmk__substitute_secrets(op->rsc, op->params);
985 if (rc != pcmk_rc_ok) {
986 if (pcmk__str_eq(op->action, PCMK_ACTION_STOP, pcmk__str_casei)) {
987 crm_info("Proceeding with stop operation for %s "
988 "despite being unable to load CIB secrets (%s)",
989 op->rsc, pcmk_rc_str(rc));
990 } else {
991 crm_err("Considering %s unconfigured "
992 "because unable to load CIB secrets: %s",
993 op->rsc, pcmk_rc_str(rc));
994 exit_child(op, services__configuration_error(op, false),
995 "Unable to load CIB secrets");
996 }
997 }
998#endif
999
1000 add_action_env_vars(op);
1001
1002 /* Become the desired user */
1003 if (op->opaque->uid && (geteuid() == 0)) {
1004
1005 // If requested, set effective group
1006 if (op->opaque->gid && (setgid(op->opaque->gid) < 0)) {
1007 crm_err("Considering %s unauthorized because could not set "
1008 "child group to %d: %s",
1009 op->id, op->opaque->gid, strerror(errno));
1010 exit_child(op, services__authorization_error(op),
1011 "Could not set group for child process");
1012 }
1013
1014 // Erase supplementary group list
1015 // (We could do initgroups() if we kept a copy of the username)
1016 if (setgroups(0, NULL) < 0) {
1017 crm_err("Considering %s unauthorized because could not "
1018 "clear supplementary groups: %s", op->id, strerror(errno));
1019 exit_child(op, services__authorization_error(op),
1020 "Could not clear supplementary groups for child process");
1021 }
1022
1023 // Set effective user
1024 if (setuid(op->opaque->uid) < 0) {
1025 crm_err("Considering %s unauthorized because could not set user "
1026 "to %d: %s", op->id, op->opaque->uid, strerror(errno));
1027 exit_child(op, services__authorization_error(op),
1028 "Could not set user for child process");
1029 }
1030 }
1031
1032 // Execute the agent (doesn't return if successful)
1033 execvp(op->opaque->exec, op->opaque->args);
1034
1035 // An earlier stat() should have avoided most possible errors
1036 rc = errno;
1038 crm_err("Unable to execute %s: %s", op->id, strerror(rc));
1039 exit_child(op, op->rc, "Child process was unable to execute file");
1040}
1041
1049static void
1050wait_for_sync_result(svc_action_t *op, struct sigchld_data_s *data)
1051{
1052 int status = 0;
1053 int timeout = op->timeout;
1054 time_t start = time(NULL);
1055 struct pollfd fds[3];
1056 int wait_rc = 0;
1057 const char *wait_reason = NULL;
1058
1059 fds[0].fd = op->opaque->stdout_fd;
1060 fds[0].events = POLLIN;
1061 fds[0].revents = 0;
1062
1063 fds[1].fd = op->opaque->stderr_fd;
1064 fds[1].events = POLLIN;
1065 fds[1].revents = 0;
1066
1067 fds[2].fd = sigchld_open(data);
1068 fds[2].events = POLLIN;
1069 fds[2].revents = 0;
1070
1071 crm_trace("Waiting for %s[%d]", op->id, op->pid);
1072 do {
1073 int poll_rc = poll(fds, 3, timeout);
1074
1075 wait_reason = NULL;
1076
1077 if (poll_rc > 0) {
1078 if (fds[0].revents & POLLIN) {
1079 svc_read_output(op->opaque->stdout_fd, op, FALSE);
1080 }
1081
1082 if (fds[1].revents & POLLIN) {
1083 svc_read_output(op->opaque->stderr_fd, op, TRUE);
1084 }
1085
1086 if ((fds[2].revents & POLLIN)
1087 && sigchld_received(fds[2].fd, op->pid, data)) {
1088 wait_rc = waitpid(op->pid, &status, WNOHANG);
1089
1090 if ((wait_rc > 0) || ((wait_rc < 0) && (errno == ECHILD))) {
1091 // Child process exited or doesn't exist
1092 break;
1093
1094 } else if (wait_rc < 0) {
1095 wait_reason = pcmk_rc_str(errno);
1096 crm_info("Wait for completion of %s[%d] failed: %s "
1097 QB_XS " source=waitpid",
1098 op->id, op->pid, wait_reason);
1099 wait_rc = 0; // Act as if process is still running
1100
1101#ifndef HAVE_SYS_SIGNALFD_H
1102 } else {
1103 /* The child hasn't exited, so this SIGCHLD could be for
1104 * another child. We have to ignore it here but will still
1105 * need to resend it after this synchronous action has
1106 * completed and SIGCHLD has been restored to be handled by
1107 * the previous handler, so that it will be handled.
1108 */
1109 data->ignored = true;
1110#endif
1111 }
1112 }
1113
1114 } else if (poll_rc == 0) {
1115 // Poll timed out with no descriptors ready
1116 timeout = 0;
1117 break;
1118
1119 } else if ((poll_rc < 0) && (errno != EINTR)) {
1120 wait_reason = pcmk_rc_str(errno);
1121 crm_info("Wait for completion of %s[%d] failed: %s "
1122 QB_XS " source=poll", op->id, op->pid, wait_reason);
1123 break;
1124 }
1125
1126 timeout = op->timeout - (time(NULL) - start) * 1000;
1127
1128 } while ((op->timeout < 0 || timeout > 0));
1129
1130 crm_trace("Stopped waiting for %s[%d]", op->id, op->pid);
1131 finish_op_output(op, true);
1132 finish_op_output(op, false);
1133 close_op_input(op);
1134 sigchld_close(fds[2].fd);
1135
1136 if (wait_rc <= 0) {
1137
1138 if ((op->timeout > 0) && (timeout <= 0)) {
1141 "%s did not exit within specified timeout",
1143 crm_info("%s[%d] timed out after %dms",
1144 op->id, op->pid, op->timeout);
1145
1146 } else {
1148 PCMK_EXEC_ERROR, wait_reason);
1149 }
1150
1151 /* If only child hasn't been successfully waited for, yet.
1152 This is to limit killing wrong target a bit more. */
1153 if ((wait_rc == 0) && (waitpid(op->pid, &status, WNOHANG) == 0)) {
1154 if (kill(op->pid, SIGKILL)) {
1155 crm_warn("Could not kill rogue child %s[%d]: %s",
1156 op->id, op->pid, pcmk_rc_str(errno));
1157 }
1158 /* Safe to skip WNOHANG here as we sent non-ignorable signal. */
1159 while ((waitpid(op->pid, &status, 0) == (pid_t) -1)
1160 && (errno == EINTR)) {
1161 /* keep waiting */;
1162 }
1163 }
1164
1165 } else if (WIFEXITED(status)) {
1166 services__set_result(op, WEXITSTATUS(status), PCMK_EXEC_DONE, NULL);
1167 parse_exit_reason_from_stderr(op);
1168 crm_info("%s[%d] exited with status %d", op->id, op->pid, op->rc);
1169
1170 } else if (WIFSIGNALED(status)) {
1171 int signo = WTERMSIG(status);
1172
1174 PCMK_EXEC_ERROR, "%s interrupted by %s signal",
1175 services__action_kind(op), strsignal(signo));
1176 crm_info("%s[%d] terminated with signal %d (%s)",
1177 op->id, op->pid, signo, strsignal(signo));
1178
1179#ifdef WCOREDUMP
1180 if (WCOREDUMP(status)) {
1181 crm_warn("%s[%d] dumped core", op->id, op->pid);
1182 }
1183#endif
1184
1185 } else {
1186 // Shouldn't be possible to get here
1188 "Unable to wait for child to complete");
1189 }
1190}
1191
1208int
1210{
1211 int stdout_fd[2];
1212 int stderr_fd[2];
1213 int stdin_fd[2] = {-1, -1};
1214 int rc;
1215 struct stat st;
1216 struct sigchld_data_s data = { .ignored = false };
1217
1218 // Catch common failure conditions early
1219 if (stat(op->opaque->exec, &st) != 0) {
1220 rc = errno;
1221 crm_info("Cannot execute '%s': %s " QB_XS " stat rc=%d",
1222 op->opaque->exec, pcmk_rc_str(rc), rc);
1224 goto done;
1225 }
1226
1227 if (pipe(stdout_fd) < 0) {
1228 rc = errno;
1229 crm_info("Cannot execute '%s': %s " QB_XS " pipe(stdout) rc=%d",
1230 op->opaque->exec, pcmk_rc_str(rc), rc);
1232 goto done;
1233 }
1234
1235 if (pipe(stderr_fd) < 0) {
1236 rc = errno;
1237
1238 close_pipe(stdout_fd);
1239
1240 crm_info("Cannot execute '%s': %s " QB_XS " pipe(stderr) rc=%d",
1241 op->opaque->exec, pcmk_rc_str(rc), rc);
1243 goto done;
1244 }
1245
1247 if (pipe(stdin_fd) < 0) {
1248 rc = errno;
1249
1250 close_pipe(stdout_fd);
1251 close_pipe(stderr_fd);
1252
1253 crm_info("Cannot execute '%s': %s " QB_XS " pipe(stdin) rc=%d",
1254 op->opaque->exec, pcmk_rc_str(rc), rc);
1256 goto done;
1257 }
1258 }
1259
1260 if (op->synchronous && !sigchld_setup(&data)) {
1261 close_pipe(stdin_fd);
1262 close_pipe(stdout_fd);
1263 close_pipe(stderr_fd);
1264 sigchld_cleanup(&data);
1266 "Could not manage signals for child process");
1267 goto done;
1268 }
1269
1270 op->pid = fork();
1271 switch (op->pid) {
1272 case -1:
1273 rc = errno;
1274 close_pipe(stdin_fd);
1275 close_pipe(stdout_fd);
1276 close_pipe(stderr_fd);
1277
1278 crm_info("Cannot execute '%s': %s " QB_XS " fork rc=%d",
1279 op->opaque->exec, pcmk_rc_str(rc), rc);
1281 if (op->synchronous) {
1282 sigchld_cleanup(&data);
1283 }
1284 goto done;
1285 break;
1286
1287 case 0: /* Child */
1288 close(stdout_fd[0]);
1289 close(stderr_fd[0]);
1290 if (stdin_fd[1] >= 0) {
1291 close(stdin_fd[1]);
1292 }
1293 if (STDOUT_FILENO != stdout_fd[1]) {
1294 if (dup2(stdout_fd[1], STDOUT_FILENO) != STDOUT_FILENO) {
1295 crm_warn("Can't redirect output from '%s': %s "
1296 QB_XS " errno=%d",
1297 op->opaque->exec, pcmk_rc_str(errno), errno);
1298 }
1299 close(stdout_fd[1]);
1300 }
1301 if (STDERR_FILENO != stderr_fd[1]) {
1302 if (dup2(stderr_fd[1], STDERR_FILENO) != STDERR_FILENO) {
1303 crm_warn("Can't redirect error output from '%s': %s "
1304 QB_XS " errno=%d",
1305 op->opaque->exec, pcmk_rc_str(errno), errno);
1306 }
1307 close(stderr_fd[1]);
1308 }
1309 if ((stdin_fd[0] >= 0) &&
1310 (STDIN_FILENO != stdin_fd[0])) {
1311 if (dup2(stdin_fd[0], STDIN_FILENO) != STDIN_FILENO) {
1312 crm_warn("Can't redirect input to '%s': %s "
1313 QB_XS " errno=%d",
1314 op->opaque->exec, pcmk_rc_str(errno), errno);
1315 }
1316 close(stdin_fd[0]);
1317 }
1318
1319 if (op->synchronous) {
1320 sigchld_cleanup(&data);
1321 }
1322
1323 action_launch_child(op);
1324 pcmk__assert(false); // action_launch_child() should not return
1325 }
1326
1327 /* Only the parent reaches here */
1328 close(stdout_fd[1]);
1329 close(stderr_fd[1]);
1330 if (stdin_fd[0] >= 0) {
1331 close(stdin_fd[0]);
1332 }
1333
1334 op->opaque->stdout_fd = stdout_fd[0];
1336 if (rc != pcmk_rc_ok) {
1337 crm_info("Could not set '%s' output non-blocking: %s "
1338 QB_XS " rc=%d",
1339 op->opaque->exec, pcmk_rc_str(rc), rc);
1340 }
1341
1342 op->opaque->stderr_fd = stderr_fd[0];
1344 if (rc != pcmk_rc_ok) {
1345 crm_info("Could not set '%s' error output non-blocking: %s "
1346 QB_XS " rc=%d",
1347 op->opaque->exec, pcmk_rc_str(rc), rc);
1348 }
1349
1350 op->opaque->stdin_fd = stdin_fd[1];
1351 if (op->opaque->stdin_fd >= 0) {
1352 // using buffer behind non-blocking-fd here - that could be improved
1353 // as long as no other standard uses stdin_fd assume stonith
1355 if (rc != pcmk_rc_ok) {
1356 crm_info("Could not set '%s' input non-blocking: %s "
1357 QB_XS " fd=%d,rc=%d", op->opaque->exec,
1358 pcmk_rc_str(rc), op->opaque->stdin_fd, rc);
1359 }
1360 pipe_in_action_stdin_parameters(op);
1361 // as long as we are handling parameters directly in here just close
1362 close(op->opaque->stdin_fd);
1363 op->opaque->stdin_fd = -1;
1364 }
1365
1366 // after fds are setup properly and before we plug anything into mainloop
1367 if (op->opaque->fork_callback) {
1368 op->opaque->fork_callback(op);
1369 }
1370
1371 if (op->synchronous) {
1372 wait_for_sync_result(op, &data);
1373 sigchld_cleanup(&data);
1374 goto done;
1375 }
1376
1377 crm_trace("Waiting async for '%s'[%d]", op->opaque->exec, op->pid);
1378 mainloop_child_add_with_flags(op->pid, op->timeout, op->id, op,
1380 async_action_complete);
1381
1383 G_PRIORITY_LOW,
1384 op->opaque->stdout_fd, op,
1385 &stdout_callbacks);
1387 G_PRIORITY_LOW,
1388 op->opaque->stderr_fd, op,
1389 &stderr_callbacks);
1391 return pcmk_rc_ok;
1392
1393done:
1394 if (op->synchronous) {
1395 return (op->rc == PCMK_OCF_OK)? pcmk_rc_ok : pcmk_rc_error;
1396 } else {
1397 return services__finalize_async_op(op);
1398 }
1399}
1400
1401GList *
1402services_os_get_single_directory_list(const char *root, gboolean files, gboolean executable)
1403{
1404 GList *list = NULL;
1405 struct dirent **namelist = NULL;
1406 int entries = 0, lpc = 0;
1407 char buffer[PATH_MAX];
1408
1409 entries = scandir(root, &namelist, NULL, alphasort);
1410 if (entries <= 0) {
1411 return list;
1412 }
1413
1414 for (lpc = 0; lpc < entries; lpc++) {
1415 struct stat sb;
1416
1417 if ('.' == namelist[lpc]->d_name[0]) {
1418 free(namelist[lpc]);
1419 continue;
1420 }
1421
1422 snprintf(buffer, sizeof(buffer), "%s/%s", root, namelist[lpc]->d_name);
1423
1424 if (stat(buffer, &sb)) {
1425 continue;
1426 }
1427
1428 if (S_ISDIR(sb.st_mode)) {
1429 if (files) {
1430 free(namelist[lpc]);
1431 continue;
1432 }
1433
1434 } else if (S_ISREG(sb.st_mode)) {
1435 if (files == FALSE) {
1436 free(namelist[lpc]);
1437 continue;
1438
1439 } else if (executable
1440 && (sb.st_mode & S_IXUSR) == 0
1441 && (sb.st_mode & S_IXGRP) == 0 && (sb.st_mode & S_IXOTH) == 0) {
1442 free(namelist[lpc]);
1443 continue;
1444 }
1445 }
1446
1447 list = g_list_append(list, strdup(namelist[lpc]->d_name));
1448
1449 free(namelist[lpc]);
1450 }
1451
1452 free(namelist);
1453 return list;
1454}
1455
1456GList *
1457services_os_get_directory_list(const char *root, gboolean files, gboolean executable)
1458{
1459 GList *result = NULL;
1460 char *dirs = strdup(root);
1461 char *dir = NULL;
1462
1463 if (pcmk__str_empty(dirs)) {
1464 free(dirs);
1465 return result;
1466 }
1467
1468 for (dir = strtok(dirs, ":"); dir != NULL; dir = strtok(NULL, ":")) {
1469 GList *tmp = services_os_get_single_directory_list(dir, files, executable);
1470
1471 if (tmp) {
1472 result = g_list_concat(result, tmp);
1473 }
1474 }
1475
1476 free(dirs);
1477
1478 return result;
1479}
#define PCMK_ACTION_STATUS
Definition actions.h:64
#define PCMK_ACTION_STOP
Definition actions.h:66
uint32_t pcmk_get_ra_caps(const char *standard)
Get capabilities of a resource agent standard.
Definition agents.c:27
#define PCMK_OCF_MAJOR_VERSION
Definition agents.h:48
#define PCMK_RESOURCE_CLASS_OCF
Definition agents.h:27
@ pcmk_ra_cap_stdin
Definition agents.h:60
#define PCMK_OCF_MINOR_VERSION
Definition agents.h:49
#define PCMK_RESOURCE_CLASS_LSB
Definition agents.h:29
const char * name
Definition cib.c:26
int pcmk__substitute_secrets(const char *rsc_id, GHashTable *params)
guint pcmk__create_timer(guint interval_ms, GSourceFunc fn, gpointer data)
Definition utils.c:405
void pcmk_common_cleanup(void)
Free all memory used by libcrmcommon.
Definition utils.c:55
#define pcmk_is_set(g, f)
Convenience alias for pcmk_all_flags_set(), to check single flag.
Definition util.h:80
#define PCMK_OCF_ROOT
Definition config.h:454
char data[0]
Definition cpg.c:10
uint32_t pid
Definition cpg.c:1
A dumping ground.
int pcmk__set_nonblocking(int fd)
Definition io.c:525
void pcmk__close_fds_in_child(bool)
Definition io.c:566
const char * pcmk__readable_interval(guint interval_ms)
Definition iso8601.c:2210
#define crm_info(fmt, args...)
Definition logging.h:365
#define crm_warn(fmt, args...)
Definition logging.h:360
#define crm_log_output(level, prefix, output)
Definition logging.h:83
#define crm_perror(level, fmt, args...)
Send a system error message to both the log and stderr.
Definition logging.h:299
#define CRM_CHECK(expr, failure_action)
Definition logging.h:213
#define crm_debug(fmt, args...)
Definition logging.h:368
#define crm_err(fmt, args...)
Definition logging.h:357
#define crm_trace(fmt, args...)
Definition logging.h:370
Wrappers for and extensions to glib mainloop.
struct mainloop_child_s mainloop_child_t
Definition mainloop.h:42
int mainloop_child_timeout(mainloop_child_t *child)
Definition mainloop.c:1034
void mainloop_child_add_with_flags(pid_t pid, int timeout, const char *desc, void *userdata, enum mainloop_child_flags, void(*callback)(mainloop_child_t *p, pid_t pid, int core, int signo, int exitcode))
Definition mainloop.c:1258
void * mainloop_child_userdata(mainloop_child_t *child)
Definition mainloop.c:1040
void(* sighandler_t)(int)
Definition mainloop.h:61
void mainloop_del_fd(mainloop_io_t *client)
Definition mainloop.c:1006
struct mainloop_io_s mainloop_io_t
Definition mainloop.h:41
@ mainloop_leave_pid_group
Definition mainloop.h:35
void mainloop_clear_child_userdata(mainloop_child_t *child)
Definition mainloop.c:1046
mainloop_io_t * mainloop_add_fd(const char *name, int priority, int fd, void *userdata, struct mainloop_fd_callbacks *callbacks)
Definition mainloop.c:962
unsigned int timeout
Definition pcmk_fence.c:34
stonith_t * st
Definition pcmk_fence.c:30
pcmk__action_result_t result
Definition pcmk_fence.c:37
const char * pcmk_rc_str(int rc)
Get a user-friendly description of a return code.
Definition results.c:617
@ PCMK_OCF_INSUFFICIENT_PRIV
Insufficient privileges.
Definition results.h:181
@ PCMK_OCF_NOT_CONFIGURED
Parameter invalid (inherently)
Definition results.h:183
@ PCMK_OCF_NOT_INSTALLED
Dependencies not available locally.
Definition results.h:182
@ PCMK_OCF_UNKNOWN_ERROR
Unspecified error.
Definition results.h:177
@ PCMK_OCF_INVALID_PARAM
Parameter invalid (in local context)
Definition results.h:179
@ PCMK_OCF_OK
Success.
Definition results.h:174
@ pcmk_rc_ok
Definition results.h:159
@ pcmk_rc_error
Definition results.h:154
@ PCMK_EXEC_CANCELLED
Action was cancelled.
Definition results.h:312
@ PCMK_EXEC_NOT_INSTALLED
Agent or dependency not available locally.
Definition results.h:318
@ PCMK_EXEC_DONE
Action completed, result is known.
Definition results.h:311
@ PCMK_EXEC_ERROR
Execution failed, may be retried.
Definition results.h:315
@ PCMK_EXEC_TIMEOUT
Action did not complete in time.
Definition results.h:313
#define pcmk__assert(expr)
const char * services__action_kind(const svc_action_t *action)
Definition services.c:1313
gboolean cancel_recurring_action(svc_action_t *op)
Definition services.c:624
void services__set_cancelled(svc_action_t *action)
Definition services.c:1295
void services_untrack_op(const svc_action_t *op)
Definition services.c:835
void services_add_inflight_op(svc_action_t *op)
Definition services.c:814
Services API.
@ SVC_ACTION_LEAVE_GROUP
Definition services.h:80
@ PCMK_LSB_STATUS_NOT_INSTALLED
Definition services.h:74
@ PCMK_LSB_STATUS_INSUFFICIENT_PRIV
Definition services.h:75
@ PCMK_LSB_STATUS_UNKNOWN
Definition services.h:71
gboolean services_action_async(svc_action_t *op, void(*action_callback)(svc_action_t *))
Request asynchronous execution of an action.
Definition services.c:880
#define PCMK_OCF_REASON_PREFIX
Definition services.h:46
void services_action_free(svc_action_t *op)
Definition services.c:566
@ PCMK_LSB_NOT_CONFIGURED
Definition services.h:61
void services_action_cleanup(svc_action_t *op)
Definition services.c:492
void services__format_result(svc_action_t *action, int agent_status, enum pcmk_exec_status exec_status, const char *format,...) G_GNUC_PRINTF(4
void services__set_result(svc_action_t *action, int agent_status, enum pcmk_exec_status exec_status, const char *exit_reason)
Definition services.c:1218
volatile struct sigchld_data_s * last_sigchld_data
int services__authorization_error(const svc_action_t *op)
void services__handle_exec_error(svc_action_t *op, int error)
int services__execute_file(svc_action_t *op)
#define MAX_OUTPUT
#define out_type(is_stderr)
int services__not_installed_error(const svc_action_t *op)
int services__finalize_async_op(svc_action_t *op)
gboolean recurring_action_timer(gpointer data)
int services__configuration_error(const svc_action_t *op, bool is_fatal)
#define EXIT_REASON_MAX_LEN
GList * services_os_get_directory_list(const char *root, gboolean files, gboolean executable)
GList * services_os_get_single_directory_list(const char *root, gboolean files, gboolean executable)
int services__generic_error(const svc_action_t *op)
char * crm_strdup_printf(char const *format,...) G_GNUC_PRINTF(1
@ pcmk__str_none
@ pcmk__str_casei
char * args[MAX_ARGC]
void(* fork_callback)(svc_action_t *op)
mainloop_io_t * stdout_gsource
mainloop_io_t * stderr_gsource
void(* callback)(svc_action_t *op)
Object for executing external actions.
Definition services.h:99
char * id
Definition services.h:103
char * provider
Resource provider for resource actions that require it, otherwise NULL.
Definition services.h:118
char * agent
Resource agent name for resource actions, otherwise NULL.
Definition services.h:121
char * standard
Resource standard for resource actions, otherwise NULL.
Definition services.h:115
int rc
Exit status of action (set by library upon completion)
Definition services.h:132
char * rsc
XML ID of resource being executed for resource actions, otherwise NULL.
Definition services.h:106
char * action
Name of action being executed for resource actions, otherwise NULL.
Definition services.h:109
enum svc_action_flags flags
Flag group of enum svc_action_flags.
Definition services.h:153
char * stderr_data
Action stderr (set by library)
Definition services.h:154
GHashTable * params
Definition services.h:130
int synchronous
Definition services.h:150
int timeout
Action timeout (in milliseconds)
Definition services.h:123
char * stdout_data
Action stdout (set by library)
Definition services.h:155
guint interval_ms
Action interval for recurring resource actions, otherwise 0.
Definition services.h:112
svc_action_private_t * opaque
This field should be treated as internal to Pacemaker.
Definition services.h:159