pacemaker  2.0.3-4b1f869f0f
Scalable High-Availability cluster resource manager
services_linux.c
Go to the documentation of this file.
1 /*
2  * Copyright 2010-2019 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 #ifndef _GNU_SOURCE
13 # define _GNU_SOURCE
14 #endif
15 
16 #include <sys/types.h>
17 #include <sys/stat.h>
18 #include <sys/wait.h>
19 #include <errno.h>
20 #include <unistd.h>
21 #include <dirent.h>
22 #include <grp.h>
23 #include <string.h>
24 #include <sys/time.h>
25 #include <sys/resource.h>
26 
27 #include "crm/crm.h"
28 #include "crm/common/mainloop.h"
29 #include "crm/services.h"
30 
31 #include "services_private.h"
32 
33 #if SUPPORT_CIBSECRETS
34 # include "crm/common/cib_secrets.h"
35 #endif
36 
37 static void close_pipe(int fildes[]);
38 
39 /* We have two alternative ways of handling SIGCHLD when synchronously waiting
40  * for spawned processes to complete. Both rely on polling a file descriptor to
41  * discover SIGCHLD events.
42  *
43  * If sys/signalfd.h is available (e.g. on Linux), we call signalfd() to
44  * generate the file descriptor. Otherwise, we use the "self-pipe trick"
45  * (opening a pipe and writing a byte to it when SIGCHLD is received).
46  */
47 #ifdef HAVE_SYS_SIGNALFD_H
48 
49 // signalfd() implementation
50 
51 #include <sys/signalfd.h>
52 
53 // Everything needed to manage SIGCHLD handling
54 struct sigchld_data_s {
55  sigset_t mask; // Signals to block now (including SIGCHLD)
56  sigset_t old_mask; // Previous set of blocked signals
57 };
58 
59 // Initialize SIGCHLD data and prepare for use
60 static bool
61 sigchld_setup(struct sigchld_data_s *data)
62 {
63  sigemptyset(&(data->mask));
64  sigaddset(&(data->mask), SIGCHLD);
65 
66  sigemptyset(&(data->old_mask));
67 
68  // Block SIGCHLD (saving previous set of blocked signals to restore later)
69  if (sigprocmask(SIG_BLOCK, &(data->mask), &(data->old_mask)) < 0) {
70  crm_err("Wait for child process completion failed: %s "
71  CRM_XS " source=sigprocmask", pcmk_strerror(errno));
72  return false;
73  }
74  return true;
75 }
76 
77 // Get a file descriptor suitable for polling for SIGCHLD events
78 static int
79 sigchld_open(struct sigchld_data_s *data)
80 {
81  int fd;
82 
83  CRM_CHECK(data != NULL, return -1);
84 
85  fd = signalfd(-1, &(data->mask), SFD_NONBLOCK);
86  if (fd < 0) {
87  crm_err("Wait for child process completion failed: %s "
88  CRM_XS " source=signalfd", pcmk_strerror(errno));
89  }
90  return fd;
91 }
92 
93 // Close a file descriptor returned by sigchld_open()
94 static void
95 sigchld_close(int fd)
96 {
97  if (fd > 0) {
98  close(fd);
99  }
100 }
101 
102 // Return true if SIGCHLD was received from polled fd
103 static bool
104 sigchld_received(int fd)
105 {
106  struct signalfd_siginfo fdsi;
107  ssize_t s;
108 
109  if (fd < 0) {
110  return false;
111  }
112  s = read(fd, &fdsi, sizeof(struct signalfd_siginfo));
113  if (s != sizeof(struct signalfd_siginfo)) {
114  crm_err("Wait for child process completion failed: %s "
115  CRM_XS " source=read", pcmk_strerror(errno));
116 
117  } else if (fdsi.ssi_signo == SIGCHLD) {
118  return true;
119  }
120  return false;
121 }
122 
123 // Do anything needed after done waiting for SIGCHLD
124 static void
125 sigchld_cleanup(struct sigchld_data_s *data)
126 {
127  // Restore the original set of blocked signals
128  if ((sigismember(&(data->old_mask), SIGCHLD) == 0)
129  && (sigprocmask(SIG_UNBLOCK, &(data->mask), NULL) < 0)) {
130  crm_warn("Could not clean up after child process completion: %s",
131  pcmk_strerror(errno));
132  }
133 }
134 
135 #else // HAVE_SYS_SIGNALFD_H not defined
136 
137 // Self-pipe implementation (see above for function descriptions)
138 
139 struct sigchld_data_s {
140  int pipe_fd[2]; // Pipe file descriptors
141  struct sigaction sa; // Signal handling info (with SIGCHLD)
142  struct sigaction old_sa; // Previous signal handling info
143 };
144 
145 // We need a global to use in the signal handler
146 volatile struct sigchld_data_s *last_sigchld_data = NULL;
147 
148 static void
149 sigchld_handler()
150 {
151  // We received a SIGCHLD, so trigger pipe polling
152  if ((last_sigchld_data != NULL)
153  && (last_sigchld_data->pipe_fd[1] >= 0)
154  && (write(last_sigchld_data->pipe_fd[1], "", 1) == -1)) {
155  crm_err("Wait for child process completion failed: %s "
156  CRM_XS " source=write", pcmk_strerror(errno));
157  }
158 }
159 
160 static bool
161 sigchld_setup(struct sigchld_data_s *data)
162 {
163  int rc;
164 
165  data->pipe_fd[0] = data->pipe_fd[1] = -1;
166 
167  if (pipe(data->pipe_fd) == -1) {
168  crm_err("Wait for child process completion failed: %s "
169  CRM_XS " source=pipe", pcmk_strerror(errno));
170  return false;
171  }
172 
173  rc = crm_set_nonblocking(data->pipe_fd[0]);
174  if (rc < 0) {
175  crm_warn("Could not set pipe input non-blocking: %s " CRM_XS " rc=%d",
176  pcmk_strerror(rc), rc);
177  }
178  rc = crm_set_nonblocking(data->pipe_fd[1]);
179  if (rc < 0) {
180  crm_warn("Could not set pipe output non-blocking: %s " CRM_XS " rc=%d",
181  pcmk_strerror(rc), rc);
182  }
183 
184  // Set SIGCHLD handler
185  data->sa.sa_handler = sigchld_handler;
186  data->sa.sa_flags = 0;
187  sigemptyset(&(data->sa.sa_mask));
188  if (sigaction(SIGCHLD, &(data->sa), &(data->old_sa)) < 0) {
189  crm_err("Wait for child process completion failed: %s "
190  CRM_XS " source=sigaction", pcmk_strerror(errno));
191  }
192 
193  // Remember data for use in signal handler
195  return true;
196 }
197 
198 static int
199 sigchld_open(struct sigchld_data_s *data)
200 {
201  CRM_CHECK(data != NULL, return -1);
202  return data->pipe_fd[0];
203 }
204 
205 static void
206 sigchld_close(int fd)
207 {
208  // Pipe will be closed in sigchld_cleanup()
209  return;
210 }
211 
212 static bool
213 sigchld_received(int fd)
214 {
215  char ch;
216 
217  if (fd < 0) {
218  return false;
219  }
220 
221  // Clear out the self-pipe
222  while (read(fd, &ch, 1) == 1) /*omit*/;
223  return true;
224 }
225 
226 static void
227 sigchld_cleanup(struct sigchld_data_s *data)
228 {
229  // Restore the previous SIGCHLD handler
230  if (sigaction(SIGCHLD, &(data->old_sa), NULL) < 0) {
231  crm_warn("Could not clean up after child process completion: %s",
232  pcmk_strerror(errno));
233  }
234 
235  close_pipe(data->pipe_fd);
236 }
237 
238 #endif
239 
246 static void
247 close_pipe(int fildes[])
248 {
249  if (fildes[0] >= 0) {
250  close(fildes[0]);
251  fildes[0] = -1;
252  }
253  if (fildes[1] >= 0) {
254  close(fildes[1]);
255  fildes[1] = -1;
256  }
257 }
258 
259 static gboolean
260 svc_read_output(int fd, svc_action_t * op, bool is_stderr)
261 {
262  char *data = NULL;
263  int rc = 0, len = 0;
264  char buf[500];
265  static const size_t buf_read_len = sizeof(buf) - 1;
266 
267 
268  if (fd < 0) {
269  crm_trace("No fd for %s", op->id);
270  return FALSE;
271  }
272 
273  if (is_stderr && op->stderr_data) {
274  len = strlen(op->stderr_data);
275  data = op->stderr_data;
276  crm_trace("Reading %s stderr into offset %d", op->id, len);
277 
278  } else if (is_stderr == FALSE && op->stdout_data) {
279  len = strlen(op->stdout_data);
280  data = op->stdout_data;
281  crm_trace("Reading %s stdout into offset %d", op->id, len);
282 
283  } else {
284  crm_trace("Reading %s %s into offset %d", op->id, is_stderr?"stderr":"stdout", len);
285  }
286 
287  do {
288  rc = read(fd, buf, buf_read_len);
289  if (rc > 0) {
290  buf[rc] = 0;
291  crm_trace("Got %d chars: %.80s", rc, buf);
292  data = realloc_safe(data, len + rc + 1);
293  len += sprintf(data + len, "%s", buf);
294 
295  } else if (errno != EINTR) {
296  /* error or EOF
297  * Cleanup happens in pipe_done()
298  */
299  rc = FALSE;
300  break;
301  }
302 
303  } while (rc == buf_read_len || rc < 0);
304 
305  if (is_stderr) {
306  op->stderr_data = data;
307  } else {
308  op->stdout_data = data;
309  }
310 
311  return rc;
312 }
313 
314 static int
315 dispatch_stdout(gpointer userdata)
316 {
317  svc_action_t *op = (svc_action_t *) userdata;
318 
319  return svc_read_output(op->opaque->stdout_fd, op, FALSE);
320 }
321 
322 static int
323 dispatch_stderr(gpointer userdata)
324 {
325  svc_action_t *op = (svc_action_t *) userdata;
326 
327  return svc_read_output(op->opaque->stderr_fd, op, TRUE);
328 }
329 
330 static void
331 pipe_out_done(gpointer user_data)
332 {
333  svc_action_t *op = (svc_action_t *) user_data;
334 
335  crm_trace("%p", op);
336 
337  op->opaque->stdout_gsource = NULL;
338  if (op->opaque->stdout_fd > STDOUT_FILENO) {
339  close(op->opaque->stdout_fd);
340  }
341  op->opaque->stdout_fd = -1;
342 }
343 
344 static void
345 pipe_err_done(gpointer user_data)
346 {
347  svc_action_t *op = (svc_action_t *) user_data;
348 
349  op->opaque->stderr_gsource = NULL;
350  if (op->opaque->stderr_fd > STDERR_FILENO) {
351  close(op->opaque->stderr_fd);
352  }
353  op->opaque->stderr_fd = -1;
354 }
355 
356 static struct mainloop_fd_callbacks stdout_callbacks = {
357  .dispatch = dispatch_stdout,
358  .destroy = pipe_out_done,
359 };
360 
361 static struct mainloop_fd_callbacks stderr_callbacks = {
362  .dispatch = dispatch_stderr,
363  .destroy = pipe_err_done,
364 };
365 
366 static void
367 set_ocf_env(const char *key, const char *value, gpointer user_data)
368 {
369  if (setenv(key, value, 1) != 0) {
370  crm_perror(LOG_ERR, "setenv failed for key:%s and value:%s", key, value);
371  }
372 }
373 
374 static void
375 set_ocf_env_with_prefix(gpointer key, gpointer value, gpointer user_data)
376 {
377  char buffer[500];
378 
379  snprintf(buffer, sizeof(buffer), "OCF_RESKEY_%s", (char *)key);
380  set_ocf_env(buffer, value, user_data);
381 }
382 
383 static void
384 set_alert_env(gpointer key, gpointer value, gpointer user_data)
385 {
386  int rc;
387 
388  if (value != NULL) {
389  rc = setenv(key, value, 1);
390  } else {
391  rc = unsetenv(key);
392  }
393 
394  if (rc < 0) {
395  crm_perror(LOG_ERR, "setenv %s=%s",
396  (char*)key, (value? (char*)value : ""));
397  } else {
398  crm_trace("setenv %s=%s", (char*)key, (value? (char*)value : ""));
399  }
400 }
401 
408 static void
409 add_action_env_vars(const svc_action_t *op)
410 {
411  void (*env_setter)(gpointer, gpointer, gpointer) = NULL;
412  if (op->agent == NULL) {
413  env_setter = set_alert_env; /* we deal with alert handler */
414 
415  } else if (safe_str_eq(op->standard, PCMK_RESOURCE_CLASS_OCF)) {
416  env_setter = set_ocf_env_with_prefix;
417  }
418 
419  if (env_setter != NULL && op->params != NULL) {
420  g_hash_table_foreach(op->params, env_setter, NULL);
421  }
422 
423  if (env_setter == NULL || env_setter == set_alert_env) {
424  return;
425  }
426 
427  set_ocf_env("OCF_RA_VERSION_MAJOR", "1", NULL);
428  set_ocf_env("OCF_RA_VERSION_MINOR", "0", NULL);
429  set_ocf_env("OCF_ROOT", OCF_ROOT_DIR, NULL);
430  set_ocf_env("OCF_EXIT_REASON_PREFIX", PCMK_OCF_REASON_PREFIX, NULL);
431 
432  if (op->rsc) {
433  set_ocf_env("OCF_RESOURCE_INSTANCE", op->rsc, NULL);
434  }
435 
436  if (op->agent != NULL) {
437  set_ocf_env("OCF_RESOURCE_TYPE", op->agent, NULL);
438  }
439 
440  /* Notes: this is not added to specification yet. Sept 10,2004 */
441  if (op->provider != NULL) {
442  set_ocf_env("OCF_RESOURCE_PROVIDER", op->provider, NULL);
443  }
444 }
445 
446 static void
447 pipe_in_single_parameter(gpointer key, gpointer value, gpointer user_data)
448 {
449  svc_action_t *op = user_data;
450  char *buffer = crm_strdup_printf("%s=%s\n", (char *)key, (char *) value);
451  int ret, total = 0, len = strlen(buffer);
452 
453  do {
454  errno = 0;
455  ret = write(op->opaque->stdin_fd, buffer + total, len - total);
456  if (ret > 0) {
457  total += ret;
458  }
459 
460  } while ((errno == EINTR) && (total < len));
461  free(buffer);
462 }
463 
470 static void
471 pipe_in_action_stdin_parameters(const svc_action_t *op)
472 {
473  crm_debug("sending args");
474  if (op->params) {
475  g_hash_table_foreach(op->params, pipe_in_single_parameter, (gpointer) op);
476  }
477 }
478 
479 gboolean
481 {
482  svc_action_t *op = data;
483 
484  crm_debug("Scheduling another invocation of %s", op->id);
485 
486  /* Clean out the old result */
487  free(op->stdout_data);
488  op->stdout_data = NULL;
489  free(op->stderr_data);
490  op->stderr_data = NULL;
491  op->opaque->repeat_timer = 0;
492 
493  services_action_async(op, NULL);
494  return FALSE;
495 }
496 
497 /* Returns FALSE if 'op' should be free'd by the caller */
498 gboolean
500 {
501  int recurring = 0;
502 
503  if (op->interval_ms) {
504  if (op->cancel) {
507  } else {
508  recurring = 1;
509  op->opaque->repeat_timer = g_timeout_add(op->interval_ms,
510  recurring_action_timer, (void *)op);
511  }
512  }
513 
514  if (op->opaque->callback) {
515  op->opaque->callback(op);
516  }
517 
518  op->pid = 0;
519 
521 
522  if (!recurring && op->synchronous == FALSE) {
523  /*
524  * If this is a recurring action, do not free explicitly.
525  * It will get freed whenever the action gets cancelled.
526  */
528  return TRUE;
529  }
530 
532  return FALSE;
533 }
534 
535 static void
536 close_op_input(svc_action_t *op)
537 {
538  if (op->opaque->stdin_fd >= 0) {
539  close(op->opaque->stdin_fd);
540  }
541 }
542 
543 static void
544 finish_op_output(svc_action_t *op, bool is_stderr)
545 {
546  mainloop_io_t **source;
547  int fd;
548 
549  if (is_stderr) {
550  source = &(op->opaque->stderr_gsource);
551  fd = op->opaque->stderr_fd;
552  } else {
553  source = &(op->opaque->stdout_gsource);
554  fd = op->opaque->stdout_fd;
555  }
556 
557  if (op->synchronous || *source) {
558  crm_trace("Finish reading %s[%d] %s",
559  op->id, op->pid, (is_stderr? "stdout" : "stderr"));
560  svc_read_output(fd, op, is_stderr);
561  if (op->synchronous) {
562  close(fd);
563  } else {
564  mainloop_del_fd(*source);
565  *source = NULL;
566  }
567  }
568 }
569 
570 // Log an operation's stdout and stderr
571 static void
572 log_op_output(svc_action_t *op)
573 {
574  char *prefix = crm_strdup_printf("%s[%d] error output", op->id, op->pid);
575 
576  crm_log_output(LOG_NOTICE, prefix, op->stderr_data);
577  strcpy(prefix + strlen(prefix) - strlen("error output"), "output");
578  crm_log_output(LOG_DEBUG, prefix, op->stdout_data);
579  free(prefix);
580 }
581 
582 static void
583 operation_finished(mainloop_child_t * p, pid_t pid, int core, int signo, int exitcode)
584 {
586 
588  CRM_ASSERT(op->pid == pid);
589 
590  /* Depending on the priority the mainloop gives the stdout and stderr
591  * file descriptors, this function could be called before everything has
592  * been read from them, so force a final read now.
593  */
594  finish_op_output(op, true);
595  finish_op_output(op, false);
596 
597  close_op_input(op);
598 
599  if (signo == 0) {
600  crm_debug("%s[%d] exited with status %d", op->id, op->pid, exitcode);
601  op->status = PCMK_LRM_OP_DONE;
602  op->rc = exitcode;
603 
604  } else if (mainloop_child_timeout(p)) {
605  crm_warn("%s[%d] timed out after %dms", op->id, op->pid, op->timeout);
607  op->rc = PCMK_OCF_TIMEOUT;
608 
609  } else if (op->cancel) {
610  /* If an in-flight recurring operation was killed because it was
611  * cancelled, don't treat that as a failure.
612  */
613  crm_info("%s[%d] terminated with signal: %s " CRM_XS " (%d)",
614  op->id, op->pid, strsignal(signo), signo);
616  op->rc = PCMK_OCF_OK;
617 
618  } else {
619  crm_warn("%s[%d] terminated with signal: %s " CRM_XS " (%d)",
620  op->id, op->pid, strsignal(signo), signo);
622  op->rc = PCMK_OCF_SIGNAL;
623  }
624 
625  log_op_output(op);
626  operation_finalize(op);
627 }
628 
638 static void
639 services_handle_exec_error(svc_action_t * op, int error)
640 {
641  int rc_not_installed, rc_insufficient_priv, rc_exec_error;
642 
643  /* Mimic the return codes for each standard as that's what we'll convert back from in get_uniform_rc() */
645  && safe_str_eq(op->action, "status")) {
646 
647  rc_not_installed = PCMK_LSB_STATUS_NOT_INSTALLED;
648  rc_insufficient_priv = PCMK_LSB_STATUS_INSUFFICIENT_PRIV;
649  rc_exec_error = PCMK_LSB_STATUS_UNKNOWN;
650 
651 #if SUPPORT_NAGIOS
653  rc_not_installed = NAGIOS_NOT_INSTALLED;
654  rc_insufficient_priv = NAGIOS_INSUFFICIENT_PRIV;
655  rc_exec_error = PCMK_OCF_EXEC_ERROR;
656 #endif
657 
658  } else {
659  rc_not_installed = PCMK_OCF_NOT_INSTALLED;
660  rc_insufficient_priv = PCMK_OCF_INSUFFICIENT_PRIV;
661  rc_exec_error = PCMK_OCF_EXEC_ERROR;
662  }
663 
664  switch (error) { /* see execve(2), stat(2) and fork(2) */
665  case ENOENT: /* No such file or directory */
666  case EISDIR: /* Is a directory */
667  case ENOTDIR: /* Path component is not a directory */
668  case EINVAL: /* Invalid executable format */
669  case ENOEXEC: /* Invalid executable format */
670  op->rc = rc_not_installed;
672  break;
673  case EACCES: /* permission denied (various errors) */
674  case EPERM: /* permission denied (various errors) */
675  op->rc = rc_insufficient_priv;
677  break;
678  default:
679  op->rc = rc_exec_error;
681  }
682 }
683 
684 static void
685 action_launch_child(svc_action_t *op)
686 {
687  /* SIGPIPE is ignored (which is different from signal blocking) by the gnutls library.
688  * Depending on the libqb version in use, libqb may set SIGPIPE to be ignored as well.
689  * We do not want this to be inherited by the child process. By resetting this the signal
690  * to the default behavior, we avoid some potential odd problems that occur during OCF
691  * scripts when SIGPIPE is ignored by the environment. */
692  signal(SIGPIPE, SIG_DFL);
693 
694 #if defined(HAVE_SCHED_SETSCHEDULER)
695  if (sched_getscheduler(0) != SCHED_OTHER) {
696  struct sched_param sp;
697 
698  memset(&sp, 0, sizeof(sp));
699  sp.sched_priority = 0;
700 
701  if (sched_setscheduler(0, SCHED_OTHER, &sp) == -1) {
702  crm_perror(LOG_ERR, "Could not reset scheduling policy to SCHED_OTHER for %s", op->id);
703  }
704  }
705 #endif
706  if (setpriority(PRIO_PROCESS, 0, 0) == -1) {
707  crm_perror(LOG_ERR, "Could not reset process priority to 0 for %s", op->id);
708  }
709 
710  /* Man: The call setpgrp() is equivalent to setpgid(0,0)
711  * _and_ compiles on BSD variants too
712  * need to investigate if it works the same too.
713  */
714  setpgid(0, 0);
715 
717 
718 #if SUPPORT_CIBSECRETS
719  if (replace_secret_params(op->rsc, op->params) < 0) {
720  /* replacing secrets failed! */
721  if (safe_str_eq(op->action,"stop")) {
722  /* don't fail on stop! */
723  crm_info("proceeding with the stop operation for %s", op->rsc);
724 
725  } else {
726  crm_err("failed to get secrets for %s, "
727  "considering resource not configured", op->rsc);
729  }
730  }
731 #endif
732 
733  add_action_env_vars(op);
734 
735  /* Become the desired user */
736  if (op->opaque->uid && (geteuid() == 0)) {
737 
738  // If requested, set effective group
739  if (op->opaque->gid && (setgid(op->opaque->gid) < 0)) {
740  crm_perror(LOG_ERR, "Could not set child group to %d", op->opaque->gid);
742  }
743 
744  // Erase supplementary group list
745  // (We could do initgroups() if we kept a copy of the username)
746  if (setgroups(0, NULL) < 0) {
747  crm_perror(LOG_ERR, "Could not set child groups");
749  }
750 
751  // Set effective user
752  if (setuid(op->opaque->uid) < 0) {
753  crm_perror(LOG_ERR, "setting user to %d", op->opaque->uid);
755  }
756  }
757 
758  /* execute the RA */
759  execvp(op->opaque->exec, op->opaque->args);
760 
761  /* Most cases should have been already handled by stat() */
762  services_handle_exec_error(op, errno);
763 
764  _exit(op->rc);
765 }
766 
767 static void
768 action_synced_wait(svc_action_t *op, struct sigchld_data_s *data)
769 {
770  int status = 0;
771  int timeout = op->timeout;
772  time_t start = -1;
773  struct pollfd fds[3];
774  int wait_rc = 0;
775 
776  fds[0].fd = op->opaque->stdout_fd;
777  fds[0].events = POLLIN;
778  fds[0].revents = 0;
779 
780  fds[1].fd = op->opaque->stderr_fd;
781  fds[1].events = POLLIN;
782  fds[1].revents = 0;
783 
784  fds[2].fd = sigchld_open(data);
785  fds[2].events = POLLIN;
786  fds[2].revents = 0;
787 
788  crm_trace("Waiting for %s[%d]", op->id, op->pid);
789  start = time(NULL);
790  do {
791  int poll_rc = poll(fds, 3, timeout);
792 
793  if (poll_rc > 0) {
794  if (fds[0].revents & POLLIN) {
795  svc_read_output(op->opaque->stdout_fd, op, FALSE);
796  }
797 
798  if (fds[1].revents & POLLIN) {
799  svc_read_output(op->opaque->stderr_fd, op, TRUE);
800  }
801 
802  if ((fds[2].revents & POLLIN) && sigchld_received(fds[2].fd)) {
803  wait_rc = waitpid(op->pid, &status, WNOHANG);
804 
805  if ((wait_rc > 0) || ((wait_rc < 0) && (errno == ECHILD))) {
806  // Child process exited or doesn't exist
807  break;
808 
809  } else if (wait_rc < 0) {
810  crm_warn("Wait for completion of %s[%d] failed: %s "
811  CRM_XS " source=waitpid",
812  op->id, op->pid, pcmk_strerror(errno));
813  wait_rc = 0; // Act as if process is still running
814  }
815  }
816 
817  } else if (poll_rc == 0) {
818  // Poll timed out with no descriptors ready
819  timeout = 0;
820  break;
821 
822  } else if ((poll_rc < 0) && (errno != EINTR)) {
823  crm_err("Wait for completion of %s[%d] failed: %s "
824  CRM_XS " source=poll",
825  op->id, op->pid, pcmk_strerror(errno));
826  break;
827  }
828 
829  timeout = op->timeout - (time(NULL) - start) * 1000;
830 
831  } while ((op->timeout < 0 || timeout > 0));
832 
833  crm_trace("Stopped waiting for %s[%d]", op->id, op->pid);
834  if (wait_rc <= 0) {
836 
837  if (op->timeout > 0 && timeout <= 0) {
839  crm_warn("%s[%d] timed out after %dms",
840  op->id, op->pid, op->timeout);
841 
842  } else {
844  }
845 
846  /* If only child hasn't been successfully waited for, yet.
847  This is to limit killing wrong target a bit more. */
848  if (wait_rc == 0 && waitpid(op->pid, &status, WNOHANG) == 0) {
849  if (kill(op->pid, SIGKILL)) {
850  crm_warn("Could not kill rogue child %s[%d]: %s",
851  op->id, op->pid, pcmk_strerror(errno));
852  }
853  /* Safe to skip WNOHANG here as we sent non-ignorable signal. */
854  while (waitpid(op->pid, &status, 0) == (pid_t) -1 && errno == EINTR) /*omit*/;
855  }
856 
857  } else if (WIFEXITED(status)) {
858  op->status = PCMK_LRM_OP_DONE;
859  op->rc = WEXITSTATUS(status);
860  crm_info("%s[%d] exited with status %d", op->id, op->pid, op->rc);
861 
862  } else if (WIFSIGNALED(status)) {
863  int signo = WTERMSIG(status);
864 
866  crm_err("%s[%d] terminated with signal: %s " CRM_XS " (%d)",
867  op->id, op->pid, strsignal(signo), signo);
868  }
869 #ifdef WCOREDUMP
870  if (WCOREDUMP(status)) {
871  crm_err("%s[%d] dumped core", op->id, op->pid);
872  }
873 #endif
874 
875  finish_op_output(op, true);
876  finish_op_output(op, false);
877  close_op_input(op);
878  sigchld_close(fds[2].fd);
879 }
880 
881 /* For an asynchronous 'op', returns FALSE if 'op' should be free'd by the caller */
882 /* For a synchronous 'op', returns FALSE if 'op' fails */
883 gboolean
885 {
886  int stdout_fd[2];
887  int stderr_fd[2];
888  int stdin_fd[2] = {-1, -1};
889  int rc;
890  struct stat st;
891  struct sigchld_data_s data;
892 
893  /* Fail fast */
894  if(stat(op->opaque->exec, &st) != 0) {
895  rc = errno;
896  crm_warn("Cannot execute '%s': %s " CRM_XS " stat rc=%d",
897  op->opaque->exec, pcmk_strerror(rc), rc);
898  services_handle_exec_error(op, rc);
899  if (!op->synchronous) {
900  return operation_finalize(op);
901  }
902  return FALSE;
903  }
904 
905  if (pipe(stdout_fd) < 0) {
906  rc = errno;
907  crm_err("Cannot execute '%s': %s " CRM_XS " pipe(stdout) rc=%d",
908  op->opaque->exec, pcmk_strerror(rc), rc);
909  services_handle_exec_error(op, rc);
910  if (!op->synchronous) {
911  return operation_finalize(op);
912  }
913  return FALSE;
914  }
915 
916  if (pipe(stderr_fd) < 0) {
917  rc = errno;
918 
919  close_pipe(stdout_fd);
920 
921  crm_err("Cannot execute '%s': %s " CRM_XS " pipe(stderr) rc=%d",
922  op->opaque->exec, pcmk_strerror(rc), rc);
923  services_handle_exec_error(op, rc);
924  if (!op->synchronous) {
925  return operation_finalize(op);
926  }
927  return FALSE;
928  }
929 
931  if (pipe(stdin_fd) < 0) {
932  rc = errno;
933 
934  close_pipe(stdout_fd);
935  close_pipe(stderr_fd);
936 
937  crm_err("Cannot execute '%s': %s " CRM_XS " pipe(stdin) rc=%d",
938  op->opaque->exec, pcmk_strerror(rc), rc);
939  services_handle_exec_error(op, rc);
940  if (!op->synchronous) {
941  return operation_finalize(op);
942  }
943  return FALSE;
944  }
945  }
946 
947  if (op->synchronous && !sigchld_setup(&data)) {
948  close_pipe(stdin_fd);
949  close_pipe(stdout_fd);
950  close_pipe(stderr_fd);
951  sigchld_cleanup(&data);
952  return FALSE;
953  }
954 
955  op->pid = fork();
956  switch (op->pid) {
957  case -1:
958  rc = errno;
959  close_pipe(stdin_fd);
960  close_pipe(stdout_fd);
961  close_pipe(stderr_fd);
962 
963  crm_err("Cannot execute '%s': %s " CRM_XS " fork rc=%d",
964  op->opaque->exec, pcmk_strerror(rc), rc);
965  services_handle_exec_error(op, rc);
966  if (!op->synchronous) {
967  return operation_finalize(op);
968  }
969 
970  sigchld_cleanup(&data);
971  return FALSE;
972 
973  case 0: /* Child */
974  close(stdout_fd[0]);
975  close(stderr_fd[0]);
976  if (stdin_fd[1] >= 0) {
977  close(stdin_fd[1]);
978  }
979  if (STDOUT_FILENO != stdout_fd[1]) {
980  if (dup2(stdout_fd[1], STDOUT_FILENO) != STDOUT_FILENO) {
981  crm_warn("Can't redirect output from '%s': %s "
982  CRM_XS " errno=%d",
983  op->opaque->exec, pcmk_strerror(errno), errno);
984  }
985  close(stdout_fd[1]);
986  }
987  if (STDERR_FILENO != stderr_fd[1]) {
988  if (dup2(stderr_fd[1], STDERR_FILENO) != STDERR_FILENO) {
989  crm_warn("Can't redirect error output from '%s': %s "
990  CRM_XS " errno=%d",
991  op->opaque->exec, pcmk_strerror(errno), errno);
992  }
993  close(stderr_fd[1]);
994  }
995  if ((stdin_fd[0] >= 0) &&
996  (STDIN_FILENO != stdin_fd[0])) {
997  if (dup2(stdin_fd[0], STDIN_FILENO) != STDIN_FILENO) {
998  crm_warn("Can't redirect input to '%s': %s "
999  CRM_XS " errno=%d",
1000  op->opaque->exec, pcmk_strerror(errno), errno);
1001  }
1002  close(stdin_fd[0]);
1003  }
1004 
1005  if (op->synchronous) {
1006  sigchld_cleanup(&data);
1007  }
1008 
1009  action_launch_child(op);
1010  CRM_ASSERT(0); /* action_launch_child is effectively noreturn */
1011  }
1012 
1013  /* Only the parent reaches here */
1014  close(stdout_fd[1]);
1015  close(stderr_fd[1]);
1016  if (stdin_fd[0] >= 0) {
1017  close(stdin_fd[0]);
1018  }
1019 
1020  op->opaque->stdout_fd = stdout_fd[0];
1022  if (rc < 0) {
1023  crm_warn("Could not set '%s' output non-blocking: %s "
1024  CRM_XS " rc=%d",
1025  op->opaque->exec, pcmk_strerror(rc), rc);
1026  }
1027 
1028  op->opaque->stderr_fd = stderr_fd[0];
1030  if (rc < 0) {
1031  crm_warn("Could not set '%s' error output non-blocking: %s "
1032  CRM_XS " rc=%d",
1033  op->opaque->exec, pcmk_strerror(rc), rc);
1034  }
1035 
1036  op->opaque->stdin_fd = stdin_fd[1];
1037  if (op->opaque->stdin_fd >= 0) {
1038  // using buffer behind non-blocking-fd here - that could be improved
1039  // as long as no other standard uses stdin_fd assume stonith
1040  rc = crm_set_nonblocking(op->opaque->stdin_fd);
1041  if (rc < 0) {
1042  crm_warn("Could not set '%s' input non-blocking: %s "
1043  CRM_XS " fd=%d,rc=%d", op->opaque->exec,
1044  pcmk_strerror(rc), op->opaque->stdin_fd, rc);
1045  }
1046  pipe_in_action_stdin_parameters(op);
1047  // as long as we are handling parameters directly in here just close
1048  close(op->opaque->stdin_fd);
1049  op->opaque->stdin_fd = -1;
1050  }
1051 
1052  // after fds are setup properly and before we plug anything into mainloop
1053  if (op->opaque->fork_callback) {
1054  op->opaque->fork_callback(op);
1055  }
1056 
1057  if (op->synchronous) {
1058  action_synced_wait(op, &data);
1059  sigchld_cleanup(&data);
1060  } else {
1061  crm_trace("Waiting async for '%s'[%d]", op->opaque->exec, op->pid);
1063  op->timeout,
1064  op->id,
1065  op,
1067  operation_finished);
1068 
1069 
1071  G_PRIORITY_LOW,
1072  op->opaque->stdout_fd, op, &stdout_callbacks);
1073 
1075  G_PRIORITY_LOW,
1076  op->opaque->stderr_fd, op, &stderr_callbacks);
1077 
1079  }
1080 
1081  return TRUE;
1082 }
1083 
1084 GList *
1085 services_os_get_directory_list(const char *root, gboolean files, gboolean executable)
1086 {
1087  GList *list = NULL;
1088  struct dirent **namelist;
1089  int entries = 0, lpc = 0;
1090  char buffer[PATH_MAX];
1091 
1092  entries = scandir(root, &namelist, NULL, alphasort);
1093  if (entries <= 0) {
1094  return list;
1095  }
1096 
1097  for (lpc = 0; lpc < entries; lpc++) {
1098  struct stat sb;
1099 
1100  if ('.' == namelist[lpc]->d_name[0]) {
1101  free(namelist[lpc]);
1102  continue;
1103  }
1104 
1105  snprintf(buffer, sizeof(buffer), "%s/%s", root, namelist[lpc]->d_name);
1106 
1107  if (stat(buffer, &sb)) {
1108  continue;
1109  }
1110 
1111  if (S_ISDIR(sb.st_mode)) {
1112  if (files) {
1113  free(namelist[lpc]);
1114  continue;
1115  }
1116 
1117  } else if (S_ISREG(sb.st_mode)) {
1118  if (files == FALSE) {
1119  free(namelist[lpc]);
1120  continue;
1121 
1122  } else if (executable
1123  && (sb.st_mode & S_IXUSR) == 0
1124  && (sb.st_mode & S_IXGRP) == 0 && (sb.st_mode & S_IXOTH) == 0) {
1125  free(namelist[lpc]);
1126  continue;
1127  }
1128  }
1129 
1130  list = g_list_append(list, strdup(namelist[lpc]->d_name));
1131 
1132  free(namelist[lpc]);
1133  }
1134 
1135  free(namelist);
1136  return list;
1137 }
1138 
1139 GList *
1141 {
1142  return get_directory_list(OCF_ROOT_DIR "/resource.d", FALSE, TRUE);
1143 }
1144 
1145 GList *
1146 resources_os_list_ocf_agents(const char *provider)
1147 {
1148  GList *gIter = NULL;
1149  GList *result = NULL;
1150  GList *providers = NULL;
1151 
1152  if (provider) {
1153  char buffer[500];
1154 
1155  snprintf(buffer, sizeof(buffer), "%s/resource.d/%s", OCF_ROOT_DIR, provider);
1156  return get_directory_list(buffer, TRUE, TRUE);
1157  }
1158 
1159  providers = resources_os_list_ocf_providers();
1160  for (gIter = providers; gIter != NULL; gIter = gIter->next) {
1161  GList *tmp1 = result;
1162  GList *tmp2 = resources_os_list_ocf_agents(gIter->data);
1163 
1164  if (tmp2) {
1165  result = g_list_concat(tmp1, tmp2);
1166  }
1167  }
1168  g_list_free_full(providers, free);
1169  return result;
1170 }
1171 
1172 gboolean
1173 services__ocf_agent_exists(const char *provider, const char *agent)
1174 {
1175  char *buf = NULL;
1176  gboolean rc = FALSE;
1177  struct stat st;
1178 
1179  if (provider == NULL || agent == NULL) {
1180  return rc;
1181  }
1182 
1183  buf = crm_strdup_printf(OCF_ROOT_DIR "/resource.d/%s/%s", provider, agent);
1184  if (stat(buf, &st) == 0) {
1185  rc = TRUE;
1186  }
1187 
1188  free(buf);
1189  return rc;
1190 }
1191 
1192 #if SUPPORT_NAGIOS
1193 GList *
1195 {
1196  GList *plugin_list = NULL;
1197  GList *result = NULL;
1198  GList *gIter = NULL;
1199 
1200  plugin_list = get_directory_list(NAGIOS_PLUGIN_DIR, TRUE, TRUE);
1201 
1202  /* Make sure both the plugin and its metadata exist */
1203  for (gIter = plugin_list; gIter != NULL; gIter = gIter->next) {
1204  const char *plugin = gIter->data;
1205  char *metadata = crm_strdup_printf(NAGIOS_METADATA_DIR "/%s.xml", plugin);
1206  struct stat st;
1207 
1208  if (stat(metadata, &st) == 0) {
1209  result = g_list_append(result, strdup(plugin));
1210  }
1211 
1212  free(metadata);
1213  }
1214  g_list_free_full(plugin_list, free);
1215  return result;
1216 }
1217 
1218 gboolean
1219 services__nagios_agent_exists(const char *name)
1220 {
1221  char *buf = NULL;
1222  gboolean rc = FALSE;
1223  struct stat st;
1224 
1225  if (name == NULL) {
1226  return rc;
1227  }
1228 
1229  buf = crm_strdup_printf(NAGIOS_PLUGIN_DIR "/%s", name);
1230  if (stat(buf, &st) == 0) {
1231  rc = TRUE;
1232  }
1233 
1234  free(buf);
1235  return rc;
1236 }
1237 #endif
services_action_free
void services_action_free(svc_action_t *op)
Definition: services.c:465
PCMK_LSB_STATUS_INSUFFICIENT_PRIV
@ PCMK_LSB_STATUS_INSUFFICIENT_PRIV
Definition: services.h:83
svc_action_s::opaque
svc_action_private_t * opaque
Definition: services.h:187
replace_secret_params
int replace_secret_params(const char *rsc_id, GHashTable *params)
Definition: cib_secrets.c:90
svc_action_private_s::stdin_fd
int stdin_fd
Definition: services_private.h:36
svc_action_private_s::gid
gid_t gid
Definition: services_private.h:24
svc_action_private_s::uid
uid_t uid
Definition: services_private.h:23
svc_action_s::provider
char * provider
Definition: services.h:158
alphasort
int alphasort(const void *dirent1, const void *dirent2)
data
char data[0]
Definition: internal.h:12
PCMK_LRM_OP_ERROR
@ PCMK_LRM_OP_ERROR
Definition: services.h:125
mainloop_add_fd
mainloop_io_t * mainloop_add_fd(const char *name, int priority, int fd, void *userdata, struct mainloop_fd_callbacks *callbacks)
Definition: mainloop.c:941
svc_action_s::flags
enum svc_action_flags flags
Definition: services.h:174
svc_action_s::action
char * action
Definition: services.h:154
services__nagios_agent_exists
G_GNUC_INTERNAL gboolean services__nagios_agent_exists(const char *agent)
PCMK_RESOURCE_CLASS_STONITH
#define PCMK_RESOURCE_CLASS_STONITH
Definition: services.h:49
PCMK_LRM_OP_CANCELLED
@ PCMK_LRM_OP_CANCELLED
Definition: services.h:122
mainloop_child_userdata
void * mainloop_child_userdata(mainloop_child_t *child)
Definition: mainloop.c:1019
services_private.h
mainloop_fd_callbacks
Definition: mainloop.h:115
PCMK_RESOURCE_CLASS_OCF
#define PCMK_RESOURCE_CLASS_OCF
Definition: services.h:43
NAGIOS_NOT_INSTALLED
@ NAGIOS_NOT_INSTALLED
Definition: services.h:141
services__ocf_agent_exists
gboolean services__ocf_agent_exists(const char *provider, const char *agent)
Definition: services_linux.c:1173
svc_action_private_s::args
char * args[MAX_ARGC]
Definition: services_private.h:21
pcmk_strerror
const char * pcmk_strerror(int rc)
Definition: results.c:188
PCMK_OCF_TIMEOUT
@ PCMK_OCF_TIMEOUT
Definition: services.h:114
crm_log_output
#define crm_log_output(level, prefix, output)
Definition: logging.h:85
CRM_CHECK
#define CRM_CHECK(expr, failure_action)
Definition: logging.h:157
OCF_ROOT_DIR
#define OCF_ROOT_DIR
Definition: services.h:30
crm_err
#define crm_err(fmt, args...)
Definition: logging.h:241
PCMK_OCF_SIGNAL
@ PCMK_OCF_SIGNAL
Definition: services.h:110
crm_trace
#define crm_trace(fmt, args...)
Definition: logging.h:247
safe_str_eq
#define safe_str_eq(a, b)
Definition: util.h:61
resources_os_list_ocf_agents
GList * resources_os_list_ocf_agents(const char *provider)
Definition: services_linux.c:1146
crm_warn
#define crm_warn(fmt, args...)
Definition: logging.h:242
services_os_get_directory_list
GList * services_os_get_directory_list(const char *root, gboolean files, gboolean executable)
Definition: services_linux.c:1085
svc_action_private_s::exec
char * exec
Definition: services_private.h:20
mainloop_child_timeout
int mainloop_child_timeout(mainloop_child_t *child)
Definition: mainloop.c:1013
svc_action_s::id
char * id
Definition: services.h:152
setenv
int setenv(const char *name, const char *value, int why)
svc_action_s::stderr_data
char * stderr_data
Definition: services.h:176
PCMK_LRM_OP_TIMEOUT
@ PCMK_LRM_OP_TIMEOUT
Definition: services.h:123
mainloop.h
Wrappers for and extensions to glib mainloop.
SVC_ACTION_LEAVE_GROUP
@ SVC_ACTION_LEAVE_GROUP
Definition: services.h:146
NAGIOS_PLUGIN_DIR
#define NAGIOS_PLUGIN_DIR
Definition: config.h:493
PCMK_LSB_STATUS_UNKNOWN
@ PCMK_LSB_STATUS_UNKNOWN
Definition: services.h:79
svc_action_s::params
GHashTable * params
Definition: services.h:162
services_os_action_execute
gboolean services_os_action_execute(svc_action_t *op)
Definition: services_linux.c:884
svc_action_private_s::stderr_fd
int stderr_fd
Definition: services_private.h:30
svc_action_private_s::stdout_fd
int stdout_fd
Definition: services_private.h:33
PCMK_RESOURCE_CLASS_NAGIOS
#define PCMK_RESOURCE_CLASS_NAGIOS
Definition: services.h:48
svc_action_s
Definition: services.h:151
crm_info
#define crm_info(fmt, args...)
Definition: logging.h:244
svc_action_s::timeout
int timeout
Definition: services.h:161
CRM_XS
#define CRM_XS
Definition: logging.h:34
resources_os_list_ocf_providers
GList * resources_os_list_ocf_providers(void)
Definition: services_linux.c:1140
svc_action_private_s::stdout_gsource
mainloop_io_t * stdout_gsource
Definition: services_private.h:34
crm_strdup_printf
char * crm_strdup_printf(char const *format,...) __attribute__((__format__(__printf__
svc_action_s::stdout_data
char * stdout_data
Definition: services.h:177
crm_debug
#define crm_debug(fmt, args...)
Definition: logging.h:246
PCMK_OCF_INSUFFICIENT_PRIV
@ PCMK_OCF_INSUFFICIENT_PRIV
Definition: services.h:94
resources_os_list_nagios_agents
G_GNUC_INTERNAL GList * resources_os_list_nagios_agents(void)
pid
uint32_t pid
Definition: internal.h:3
PCMK_OCF_EXEC_ERROR
@ PCMK_OCF_EXEC_ERROR
Definition: services.h:108
PCMK_LRM_OP_NOT_INSTALLED
@ PCMK_LRM_OP_NOT_INSTALLED
Definition: services.h:128
mainloop_leave_pid_group
@ mainloop_leave_pid_group
Definition: mainloop.h:28
svc_action_s::pid
int pid
Definition: services.h:168
cancel_recurring_action
gboolean cancel_recurring_action(svc_action_t *op)
Definition: services.c:517
svc_action_private_s::fork_callback
void(* fork_callback)(svc_action_t *op)
Definition: services_private.h:28
svc_action_private_s::repeat_timer
guint repeat_timer
Definition: services_private.h:26
PCMK_OCF_UNKNOWN_ERROR
@ PCMK_OCF_UNKNOWN_ERROR
Definition: services.h:91
NAGIOS_METADATA_DIR
#define NAGIOS_METADATA_DIR
Definition: config.h:490
cib_secrets.h
svc_action_private_s::callback
void(* callback)(svc_action_t *op)
Definition: services_private.h:27
mainloop_clear_child_userdata
void mainloop_clear_child_userdata(mainloop_child_t *child)
Definition: mainloop.c:1025
pcmk__close_fds_in_child
void pcmk__close_fds_in_child(bool)
Definition: io.c:517
mainloop_fd_callbacks::dispatch
int(* dispatch)(gpointer userdata)
Definition: mainloop.h:116
crm_set_nonblocking
int crm_set_nonblocking(int fd)
Definition: io.c:485
last_sigchld_data
volatile struct sigchld_data_s * last_sigchld_data
Definition: services_linux.c:146
PCMK_LSB_STATUS_NOT_INSTALLED
@ PCMK_LSB_STATUS_NOT_INSTALLED
Definition: services.h:82
mainloop_child_t
struct mainloop_child_s mainloop_child_t
Definition: mainloop.h:33
crm_perror
#define crm_perror(level, fmt, args...)
Log a system error message.
Definition: logging.h:219
mainloop_del_fd
void mainloop_del_fd(mainloop_io_t *client)
Definition: mainloop.c:985
PCMK_OCF_NOT_CONFIGURED
@ PCMK_OCF_NOT_CONFIGURED
Definition: services.h:96
services_add_inflight_op
void services_add_inflight_op(svc_action_t *op)
Definition: services.c:701
get_directory_list
GList * get_directory_list(const char *root, gboolean files, gboolean executable)
Get a list of files or directories in a given path.
Definition: services.c:956
mainloop_io_t
struct mainloop_io_s mainloop_io_t
Definition: mainloop.h:32
PCMK_LRM_OP_DONE
@ PCMK_LRM_OP_DONE
Definition: services.h:121
svc_action_s::interval_ms
guint interval_ms
Definition: services.h:155
services.h
Services API.
svc_action_s::cancel
int cancel
Definition: services.h:169
mainloop_child_add_with_flags
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:1237
services_action_cleanup
void services_action_cleanup(svc_action_t *op)
Definition: services.c:426
services_action_async
gboolean services_action_async(svc_action_t *op, void(*action_callback)(svc_action_t *))
Definition: services.c:765
NAGIOS_INSUFFICIENT_PRIV
@ NAGIOS_INSUFFICIENT_PRIV
Definition: services.h:140
PCMK_OCF_OK
@ PCMK_OCF_OK
Definition: services.h:90
services_untrack_op
void services_untrack_op(svc_action_t *op)
Definition: services.c:722
CRM_ASSERT
#define CRM_ASSERT(expr)
Definition: results.h:42
recurring_action_timer
gboolean recurring_action_timer(gpointer data)
Definition: services_linux.c:480
svc_action_s::status
int status
Definition: services.h:170
svc_action_s::agent
char * agent
Definition: services.h:159
svc_action_private_s::stderr_gsource
mainloop_io_t * stderr_gsource
Definition: services_private.h:31
svc_action_s::synchronous
int synchronous
Definition: services.h:173
PCMK_OCF_REASON_PREFIX
#define PCMK_OCF_REASON_PREFIX
Definition: services.h:55
svc_action_s::rc
int rc
Definition: services.h:167
operation_finalize
gboolean operation_finalize(svc_action_t *op)
Definition: services_linux.c:499
PCMK_OCF_NOT_INSTALLED
@ PCMK_OCF_NOT_INSTALLED
Definition: services.h:95
PCMK_RESOURCE_CLASS_LSB
#define PCMK_RESOURCE_CLASS_LSB
Definition: services.h:45
svc_action_s::standard
char * standard
Definition: services.h:157
crm_internal.h
crm.h
A dumping ground.
svc_action_s::rsc
char * rsc
Definition: services.h:153