LCOV - code coverage report
Current view: top level - services - services_linux.c (source / functions) Hit Total Coverage
Test: Pacemaker code coverage Lines: 0 543 0.0 %
Date: 2024-05-07 11:09:47 Functions: 0 35 0.0 %

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

Generated by: LCOV version 1.14