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

          Line data    Source code
       1             : /*
       2             :  * Copyright 2004-2024 the Pacemaker project contributors
       3             :  *
       4             :  * The version control history for this file may have further details.
       5             :  *
       6             :  * This source code is licensed under the GNU Lesser General Public License
       7             :  * version 2.1 or later (LGPLv2.1+) WITHOUT ANY WARRANTY.
       8             :  */
       9             : 
      10             : #include <crm_internal.h>
      11             : 
      12             : #ifndef _GNU_SOURCE
      13             : #  define _GNU_SOURCE
      14             : #endif
      15             : 
      16             : #include <stdlib.h>
      17             : #include <string.h>
      18             : #include <signal.h>
      19             : #include <errno.h>
      20             : 
      21             : #include <sys/wait.h>
      22             : 
      23             : #include <crm/crm.h>
      24             : #include <crm/common/xml.h>
      25             : #include <crm/common/mainloop.h>
      26             : #include <crm/common/ipc_internal.h>
      27             : 
      28             : #include <qb/qbarray.h>
      29             : 
      30             : struct mainloop_child_s {
      31             :     pid_t pid;
      32             :     char *desc;
      33             :     unsigned timerid;
      34             :     gboolean timeout;
      35             :     void *privatedata;
      36             : 
      37             :     enum mainloop_child_flags flags;
      38             : 
      39             :     /* Called when a process dies */
      40             :     void (*callback) (mainloop_child_t * p, pid_t pid, int core, int signo, int exitcode);
      41             : };
      42             : 
      43             : struct trigger_s {
      44             :     GSource source;
      45             :     gboolean running;
      46             :     gboolean trigger;
      47             :     void *user_data;
      48             :     guint id;
      49             : 
      50             : };
      51             : 
      52             : struct mainloop_timer_s {
      53             :         guint id;
      54             :         guint period_ms;
      55             :         bool repeat;
      56             :         char *name;
      57             :         GSourceFunc cb;
      58             :         void *userdata;
      59             : };
      60             : 
      61             : static gboolean
      62           0 : crm_trigger_prepare(GSource * source, gint * timeout)
      63             : {
      64           0 :     crm_trigger_t *trig = (crm_trigger_t *) source;
      65             : 
      66             :     /* cluster-glue's FD and IPC related sources make use of
      67             :      * g_source_add_poll() but do not set a timeout in their prepare
      68             :      * functions
      69             :      *
      70             :      * This means mainloop's poll() will block until an event for one
      71             :      * of these sources occurs - any /other/ type of source, such as
      72             :      * this one or g_idle_*, that doesn't use g_source_add_poll() is
      73             :      * S-O-L and won't be processed until there is something fd-based
      74             :      * happens.
      75             :      *
      76             :      * Luckily the timeout we can set here affects all sources and
      77             :      * puts an upper limit on how long poll() can take.
      78             :      *
      79             :      * So unconditionally set a small-ish timeout, not too small that
      80             :      * we're in constant motion, which will act as an upper bound on
      81             :      * how long the signal handling might be delayed for.
      82             :      */
      83           0 :     *timeout = 500;             /* Timeout in ms */
      84             : 
      85           0 :     return trig->trigger;
      86             : }
      87             : 
      88             : static gboolean
      89           0 : crm_trigger_check(GSource * source)
      90             : {
      91           0 :     crm_trigger_t *trig = (crm_trigger_t *) source;
      92             : 
      93           0 :     return trig->trigger;
      94             : }
      95             : 
      96             : /*!
      97             :  * \internal
      98             :  * \brief GSource dispatch function for crm_trigger_t
      99             :  *
     100             :  * \param[in] source        crm_trigger_t being dispatched
     101             :  * \param[in] callback      Callback passed at source creation
     102             :  * \param[in,out] userdata  User data passed at source creation
     103             :  *
     104             :  * \return G_SOURCE_REMOVE to remove source, G_SOURCE_CONTINUE to keep it
     105             :  */
     106             : static gboolean
     107           0 : crm_trigger_dispatch(GSource *source, GSourceFunc callback, gpointer userdata)
     108             : {
     109           0 :     gboolean rc = G_SOURCE_CONTINUE;
     110           0 :     crm_trigger_t *trig = (crm_trigger_t *) source;
     111             : 
     112           0 :     if (trig->running) {
     113             :         /* Wait until the existing job is complete before starting the next one */
     114           0 :         return G_SOURCE_CONTINUE;
     115             :     }
     116           0 :     trig->trigger = FALSE;
     117             : 
     118           0 :     if (callback) {
     119           0 :         int callback_rc = callback(trig->user_data);
     120             : 
     121           0 :         if (callback_rc < 0) {
     122           0 :             crm_trace("Trigger handler %p not yet complete", trig);
     123           0 :             trig->running = TRUE;
     124           0 :         } else if (callback_rc == 0) {
     125           0 :             rc = G_SOURCE_REMOVE;
     126             :         }
     127             :     }
     128           0 :     return rc;
     129             : }
     130             : 
     131             : static void
     132           0 : crm_trigger_finalize(GSource * source)
     133             : {
     134           0 :     crm_trace("Trigger %p destroyed", source);
     135           0 : }
     136             : 
     137             : static GSourceFuncs crm_trigger_funcs = {
     138             :     crm_trigger_prepare,
     139             :     crm_trigger_check,
     140             :     crm_trigger_dispatch,
     141             :     crm_trigger_finalize,
     142             : };
     143             : 
     144             : static crm_trigger_t *
     145           0 : mainloop_setup_trigger(GSource * source, int priority, int (*dispatch) (gpointer user_data),
     146             :                        gpointer userdata)
     147             : {
     148           0 :     crm_trigger_t *trigger = NULL;
     149             : 
     150           0 :     trigger = (crm_trigger_t *) source;
     151             : 
     152           0 :     trigger->id = 0;
     153           0 :     trigger->trigger = FALSE;
     154           0 :     trigger->user_data = userdata;
     155             : 
     156           0 :     if (dispatch) {
     157           0 :         g_source_set_callback(source, dispatch, trigger, NULL);
     158             :     }
     159             : 
     160           0 :     g_source_set_priority(source, priority);
     161           0 :     g_source_set_can_recurse(source, FALSE);
     162             : 
     163           0 :     trigger->id = g_source_attach(source, NULL);
     164           0 :     return trigger;
     165             : }
     166             : 
     167             : void
     168           0 : mainloop_trigger_complete(crm_trigger_t * trig)
     169             : {
     170           0 :     crm_trace("Trigger handler %p complete", trig);
     171           0 :     trig->running = FALSE;
     172           0 : }
     173             : 
     174             : /*!
     175             :  * \brief Create a trigger to be used as a mainloop source
     176             :  *
     177             :  * \param[in] priority  Relative priority of source (lower number is higher priority)
     178             :  * \param[in] dispatch  Trigger dispatch function (should return 0 to remove the
     179             :  *                      trigger from the mainloop, -1 if the trigger should be
     180             :  *                      kept but the job is still running and not complete, and
     181             :  *                      1 if the trigger should be kept and the job is complete)
     182             :  * \param[in] userdata  Pointer to pass to \p dispatch
     183             :  *
     184             :  * \return Newly allocated mainloop source for trigger
     185             :  */
     186             : crm_trigger_t *
     187           0 : mainloop_add_trigger(int priority, int (*dispatch) (gpointer user_data),
     188             :                      gpointer userdata)
     189             : {
     190           0 :     GSource *source = NULL;
     191             : 
     192             :     CRM_ASSERT(sizeof(crm_trigger_t) > sizeof(GSource));
     193           0 :     source = g_source_new(&crm_trigger_funcs, sizeof(crm_trigger_t));
     194             : 
     195           0 :     return mainloop_setup_trigger(source, priority, dispatch, userdata);
     196             : }
     197             : 
     198             : void
     199           0 : mainloop_set_trigger(crm_trigger_t * source)
     200             : {
     201           0 :     if(source) {
     202           0 :         source->trigger = TRUE;
     203             :     }
     204           0 : }
     205             : 
     206             : gboolean
     207           0 : mainloop_destroy_trigger(crm_trigger_t * source)
     208             : {
     209           0 :     GSource *gs = NULL;
     210             : 
     211           0 :     if(source == NULL) {
     212           0 :         return TRUE;
     213             :     }
     214             : 
     215           0 :     gs = (GSource *)source;
     216             : 
     217           0 :     g_source_destroy(gs); /* Remove from mainloop, ref_count-- */
     218           0 :     g_source_unref(gs); /* The caller no longer carries a reference to source
     219             :                          *
     220             :                          * At this point the source should be free'd,
     221             :                          * unless we're currently processing said
     222             :                          * source, in which case mainloop holds an
     223             :                          * additional reference and it will be free'd
     224             :                          * once our processing completes
     225             :                          */
     226           0 :     return TRUE;
     227             : }
     228             : 
     229             : // Define a custom glib source for signal handling
     230             : 
     231             : // Data structure for custom glib source
     232             : typedef struct signal_s {
     233             :     crm_trigger_t trigger;      // trigger that invoked source (must be first)
     234             :     void (*handler) (int sig);  // signal handler
     235             :     int signal;                 // signal that was received
     236             : } crm_signal_t;
     237             : 
     238             : // Table to associate signal handlers with signal numbers
     239             : static crm_signal_t *crm_signals[NSIG];
     240             : 
     241             : /*!
     242             :  * \internal
     243             :  * \brief Dispatch an event from custom glib source for signals
     244             :  *
     245             :  * Given an signal event, clear the event trigger and call any registered
     246             :  * signal handler.
     247             :  *
     248             :  * \param[in] source    glib source that triggered this dispatch
     249             :  * \param[in] callback  (ignored)
     250             :  * \param[in] userdata  (ignored)
     251             :  */
     252             : static gboolean
     253           0 : crm_signal_dispatch(GSource *source, GSourceFunc callback, gpointer userdata)
     254             : {
     255           0 :     crm_signal_t *sig = (crm_signal_t *) source;
     256             : 
     257           0 :     if(sig->signal != SIGCHLD) {
     258           0 :         crm_notice("Caught '%s' signal "CRM_XS" %d (%s handler)",
     259             :                    strsignal(sig->signal), sig->signal,
     260             :                    (sig->handler? "invoking" : "no"));
     261             :     }
     262             : 
     263           0 :     sig->trigger.trigger = FALSE;
     264           0 :     if (sig->handler) {
     265           0 :         sig->handler(sig->signal);
     266             :     }
     267           0 :     return TRUE;
     268             : }
     269             : 
     270             : /*!
     271             :  * \internal
     272             :  * \brief Handle a signal by setting a trigger for signal source
     273             :  *
     274             :  * \param[in] sig  Signal number that was received
     275             :  *
     276             :  * \note This is the true signal handler for the mainloop signal source, and
     277             :  *       must be async-safe.
     278             :  */
     279             : static void
     280           0 : mainloop_signal_handler(int sig)
     281             : {
     282           0 :     if (sig > 0 && sig < NSIG && crm_signals[sig] != NULL) {
     283           0 :         mainloop_set_trigger((crm_trigger_t *) crm_signals[sig]);
     284             :     }
     285           0 : }
     286             : 
     287             : // Functions implementing our custom glib source for signal handling
     288             : static GSourceFuncs crm_signal_funcs = {
     289             :     crm_trigger_prepare,
     290             :     crm_trigger_check,
     291             :     crm_signal_dispatch,
     292             :     crm_trigger_finalize,
     293             : };
     294             : 
     295             : /*!
     296             :  * \internal
     297             :  * \brief Set a true signal handler
     298             :  *
     299             :  * signal()-like interface to sigaction()
     300             :  *
     301             :  * \param[in] sig       Signal number to register handler for
     302             :  * \param[in] dispatch  Signal handler
     303             :  *
     304             :  * \return The previous value of the signal handler, or SIG_ERR on error
     305             :  * \note The dispatch function must be async-safe.
     306             :  */
     307             : sighandler_t
     308           0 : crm_signal_handler(int sig, sighandler_t dispatch)
     309             : {
     310             :     sigset_t mask;
     311             :     struct sigaction sa;
     312             :     struct sigaction old;
     313             : 
     314           0 :     if (sigemptyset(&mask) < 0) {
     315           0 :         crm_err("Could not set handler for signal %d: %s",
     316             :                 sig, pcmk_rc_str(errno));
     317           0 :         return SIG_ERR;
     318             :     }
     319             : 
     320           0 :     memset(&sa, 0, sizeof(struct sigaction));
     321           0 :     sa.sa_handler = dispatch;
     322           0 :     sa.sa_flags = SA_RESTART;
     323           0 :     sa.sa_mask = mask;
     324             : 
     325           0 :     if (sigaction(sig, &sa, &old) < 0) {
     326           0 :         crm_err("Could not set handler for signal %d: %s",
     327             :                 sig, pcmk_rc_str(errno));
     328           0 :         return SIG_ERR;
     329             :     }
     330           0 :     return old.sa_handler;
     331             : }
     332             : 
     333             : static void
     334           0 : mainloop_destroy_signal_entry(int sig)
     335             : {
     336           0 :     crm_signal_t *tmp = crm_signals[sig];
     337             : 
     338           0 :     crm_signals[sig] = NULL;
     339             : 
     340           0 :     crm_trace("Destroying signal %d", sig);
     341           0 :     mainloop_destroy_trigger((crm_trigger_t *) tmp);
     342           0 : }
     343             : 
     344             : /*!
     345             :  * \internal
     346             :  * \brief Add a signal handler to a mainloop
     347             :  *
     348             :  * \param[in] sig       Signal number to handle
     349             :  * \param[in] dispatch  Signal handler function
     350             :  *
     351             :  * \note The true signal handler merely sets a mainloop trigger to call this
     352             :  *       dispatch function via the mainloop. Therefore, the dispatch function
     353             :  *       does not need to be async-safe.
     354             :  */
     355             : gboolean
     356           0 : mainloop_add_signal(int sig, void (*dispatch) (int sig))
     357             : {
     358           0 :     GSource *source = NULL;
     359           0 :     int priority = G_PRIORITY_HIGH - 1;
     360             : 
     361           0 :     if (sig == SIGTERM) {
     362             :         /* TERM is higher priority than other signals,
     363             :          *   signals are higher priority than other ipc.
     364             :          * Yes, minus: smaller is "higher"
     365             :          */
     366           0 :         priority--;
     367             :     }
     368             : 
     369           0 :     if (sig >= NSIG || sig < 0) {
     370           0 :         crm_err("Signal %d is out of range", sig);
     371           0 :         return FALSE;
     372             : 
     373           0 :     } else if (crm_signals[sig] != NULL && crm_signals[sig]->handler == dispatch) {
     374           0 :         crm_trace("Signal handler for %d is already installed", sig);
     375           0 :         return TRUE;
     376             : 
     377           0 :     } else if (crm_signals[sig] != NULL) {
     378           0 :         crm_err("Different signal handler for %d is already installed", sig);
     379           0 :         return FALSE;
     380             :     }
     381             : 
     382             :     CRM_ASSERT(sizeof(crm_signal_t) > sizeof(GSource));
     383           0 :     source = g_source_new(&crm_signal_funcs, sizeof(crm_signal_t));
     384             : 
     385           0 :     crm_signals[sig] = (crm_signal_t *) mainloop_setup_trigger(source, priority, NULL, NULL);
     386           0 :     CRM_ASSERT(crm_signals[sig] != NULL);
     387             : 
     388           0 :     crm_signals[sig]->handler = dispatch;
     389           0 :     crm_signals[sig]->signal = sig;
     390             : 
     391           0 :     if (crm_signal_handler(sig, mainloop_signal_handler) == SIG_ERR) {
     392           0 :         mainloop_destroy_signal_entry(sig);
     393           0 :         return FALSE;
     394             :     }
     395             : 
     396           0 :     return TRUE;
     397             : }
     398             : 
     399             : gboolean
     400           0 : mainloop_destroy_signal(int sig)
     401             : {
     402           0 :     if (sig >= NSIG || sig < 0) {
     403           0 :         crm_err("Signal %d is out of range", sig);
     404           0 :         return FALSE;
     405             : 
     406           0 :     } else if (crm_signal_handler(sig, NULL) == SIG_ERR) {
     407           0 :         crm_perror(LOG_ERR, "Could not uninstall signal handler for signal %d", sig);
     408           0 :         return FALSE;
     409             : 
     410           0 :     } else if (crm_signals[sig] == NULL) {
     411           0 :         return TRUE;
     412             :     }
     413           0 :     mainloop_destroy_signal_entry(sig);
     414           0 :     return TRUE;
     415             : }
     416             : 
     417             : static qb_array_t *gio_map = NULL;
     418             : 
     419             : void
     420           0 : mainloop_cleanup(void) 
     421             : {
     422           0 :     if (gio_map) {
     423           0 :         qb_array_free(gio_map);
     424             :     }
     425             : 
     426           0 :     for (int sig = 0; sig < NSIG; ++sig) {
     427           0 :         mainloop_destroy_signal_entry(sig);
     428             :     }
     429           0 : }
     430             : 
     431             : /*
     432             :  * libqb...
     433             :  */
     434             : struct gio_to_qb_poll {
     435             :     int32_t is_used;
     436             :     guint source;
     437             :     int32_t events;
     438             :     void *data;
     439             :     qb_ipcs_dispatch_fn_t fn;
     440             :     enum qb_loop_priority p;
     441             : };
     442             : 
     443             : static gboolean
     444           0 : gio_read_socket(GIOChannel * gio, GIOCondition condition, gpointer data)
     445             : {
     446           0 :     struct gio_to_qb_poll *adaptor = (struct gio_to_qb_poll *)data;
     447           0 :     gint fd = g_io_channel_unix_get_fd(gio);
     448             : 
     449           0 :     crm_trace("%p.%d %d", data, fd, condition);
     450             : 
     451             :     /* if this assert get's hit, then there is a race condition between
     452             :      * when we destroy a fd and when mainloop actually gives it up */
     453           0 :     CRM_ASSERT(adaptor->is_used > 0);
     454             : 
     455           0 :     return (adaptor->fn(fd, condition, adaptor->data) == 0);
     456             : }
     457             : 
     458             : static void
     459           0 : gio_poll_destroy(gpointer data)
     460             : {
     461           0 :     struct gio_to_qb_poll *adaptor = (struct gio_to_qb_poll *)data;
     462             : 
     463           0 :     adaptor->is_used--;
     464           0 :     CRM_ASSERT(adaptor->is_used >= 0);
     465             : 
     466           0 :     if (adaptor->is_used == 0) {
     467           0 :         crm_trace("Marking adaptor %p unused", adaptor);
     468           0 :         adaptor->source = 0;
     469             :     }
     470           0 : }
     471             : 
     472             : /*!
     473             :  * \internal
     474             :  * \brief Convert libqb's poll priority into GLib's one
     475             :  *
     476             :  * \param[in] prio  libqb's poll priority (#QB_LOOP_MED assumed as fallback)
     477             :  *
     478             :  * \return  best matching GLib's priority
     479             :  */
     480             : static gint
     481           0 : conv_prio_libqb2glib(enum qb_loop_priority prio)
     482             : {
     483           0 :     switch (prio) {
     484           0 :         case QB_LOOP_LOW:   return G_PRIORITY_LOW;
     485           0 :         case QB_LOOP_HIGH:  return G_PRIORITY_HIGH;
     486           0 :         default:            return G_PRIORITY_DEFAULT; // QB_LOOP_MED
     487             :     }
     488             : }
     489             : 
     490             : /*!
     491             :  * \internal
     492             :  * \brief Convert libqb's poll priority to rate limiting spec
     493             :  *
     494             :  * \param[in] prio  libqb's poll priority (#QB_LOOP_MED assumed as fallback)
     495             :  *
     496             :  * \return  best matching rate limiting spec
     497             :  * \note This is the inverse of libqb's qb_ipcs_request_rate_limit().
     498             :  */
     499             : static enum qb_ipcs_rate_limit
     500           0 : conv_libqb_prio2ratelimit(enum qb_loop_priority prio)
     501             : {
     502           0 :     switch (prio) {
     503           0 :         case QB_LOOP_LOW:   return QB_IPCS_RATE_SLOW;
     504           0 :         case QB_LOOP_HIGH:  return QB_IPCS_RATE_FAST;
     505           0 :         default:            return QB_IPCS_RATE_NORMAL; // QB_LOOP_MED
     506             :     }
     507             : }
     508             : 
     509             : static int32_t
     510           0 : gio_poll_dispatch_update(enum qb_loop_priority p, int32_t fd, int32_t evts,
     511             :                          void *data, qb_ipcs_dispatch_fn_t fn, int32_t add)
     512             : {
     513             :     struct gio_to_qb_poll *adaptor;
     514             :     GIOChannel *channel;
     515           0 :     int32_t res = 0;
     516             : 
     517           0 :     res = qb_array_index(gio_map, fd, (void **)&adaptor);
     518           0 :     if (res < 0) {
     519           0 :         crm_err("Array lookup failed for fd=%d: %d", fd, res);
     520           0 :         return res;
     521             :     }
     522             : 
     523           0 :     crm_trace("Adding fd=%d to mainloop as adaptor %p", fd, adaptor);
     524             : 
     525           0 :     if (add && adaptor->source) {
     526           0 :         crm_err("Adaptor for descriptor %d is still in-use", fd);
     527           0 :         return -EEXIST;
     528             :     }
     529           0 :     if (!add && !adaptor->is_used) {
     530           0 :         crm_err("Adaptor for descriptor %d is not in-use", fd);
     531           0 :         return -ENOENT;
     532             :     }
     533             : 
     534             :     /* channel is created with ref_count = 1 */
     535           0 :     channel = g_io_channel_unix_new(fd);
     536           0 :     if (!channel) {
     537           0 :         crm_err("No memory left to add fd=%d", fd);
     538           0 :         return -ENOMEM;
     539             :     }
     540             : 
     541           0 :     if (adaptor->source) {
     542           0 :         g_source_remove(adaptor->source);
     543           0 :         adaptor->source = 0;
     544             :     }
     545             : 
     546             :     /* Because unlike the poll() API, glib doesn't tell us about HUPs by default */
     547           0 :     evts |= (G_IO_HUP | G_IO_NVAL | G_IO_ERR);
     548             : 
     549           0 :     adaptor->fn = fn;
     550           0 :     adaptor->events = evts;
     551           0 :     adaptor->data = data;
     552           0 :     adaptor->p = p;
     553           0 :     adaptor->is_used++;
     554           0 :     adaptor->source =
     555           0 :         g_io_add_watch_full(channel, conv_prio_libqb2glib(p), evts,
     556             :                             gio_read_socket, adaptor, gio_poll_destroy);
     557             : 
     558             :     /* Now that mainloop now holds a reference to channel,
     559             :      * thanks to g_io_add_watch_full(), drop ours from g_io_channel_unix_new().
     560             :      *
     561             :      * This means that channel will be free'd by:
     562             :      * g_main_context_dispatch()
     563             :      *  -> g_source_destroy_internal()
     564             :      *      -> g_source_callback_unref()
     565             :      * shortly after gio_poll_destroy() completes
     566             :      */
     567           0 :     g_io_channel_unref(channel);
     568             : 
     569           0 :     crm_trace("Added to mainloop with gsource id=%d", adaptor->source);
     570           0 :     if (adaptor->source > 0) {
     571           0 :         return 0;
     572             :     }
     573             : 
     574           0 :     return -EINVAL;
     575             : }
     576             : 
     577             : static int32_t
     578           0 : gio_poll_dispatch_add(enum qb_loop_priority p, int32_t fd, int32_t evts,
     579             :                       void *data, qb_ipcs_dispatch_fn_t fn)
     580             : {
     581           0 :     return gio_poll_dispatch_update(p, fd, evts, data, fn, QB_TRUE);
     582             : }
     583             : 
     584             : static int32_t
     585           0 : gio_poll_dispatch_mod(enum qb_loop_priority p, int32_t fd, int32_t evts,
     586             :                       void *data, qb_ipcs_dispatch_fn_t fn)
     587             : {
     588           0 :     return gio_poll_dispatch_update(p, fd, evts, data, fn, QB_FALSE);
     589             : }
     590             : 
     591             : static int32_t
     592           0 : gio_poll_dispatch_del(int32_t fd)
     593             : {
     594             :     struct gio_to_qb_poll *adaptor;
     595             : 
     596           0 :     crm_trace("Looking for fd=%d", fd);
     597           0 :     if (qb_array_index(gio_map, fd, (void **)&adaptor) == 0) {
     598           0 :         if (adaptor->source) {
     599           0 :             g_source_remove(adaptor->source);
     600           0 :             adaptor->source = 0;
     601             :         }
     602             :     }
     603           0 :     return 0;
     604             : }
     605             : 
     606             : struct qb_ipcs_poll_handlers gio_poll_funcs = {
     607             :     .job_add = NULL,
     608             :     .dispatch_add = gio_poll_dispatch_add,
     609             :     .dispatch_mod = gio_poll_dispatch_mod,
     610             :     .dispatch_del = gio_poll_dispatch_del,
     611             : };
     612             : 
     613             : static enum qb_ipc_type
     614           0 : pick_ipc_type(enum qb_ipc_type requested)
     615             : {
     616           0 :     const char *env = pcmk__env_option(PCMK__ENV_IPC_TYPE);
     617             : 
     618           0 :     if (env && strcmp("shared-mem", env) == 0) {
     619           0 :         return QB_IPC_SHM;
     620           0 :     } else if (env && strcmp("socket", env) == 0) {
     621           0 :         return QB_IPC_SOCKET;
     622           0 :     } else if (env && strcmp("posix", env) == 0) {
     623           0 :         return QB_IPC_POSIX_MQ;
     624           0 :     } else if (env && strcmp("sysv", env) == 0) {
     625           0 :         return QB_IPC_SYSV_MQ;
     626           0 :     } else if (requested == QB_IPC_NATIVE) {
     627             :         /* We prefer shared memory because the server never blocks on
     628             :          * send.  If part of a message fits into the socket, libqb
     629             :          * needs to block until the remainder can be sent also.
     630             :          * Otherwise the client will wait forever for the remaining
     631             :          * bytes.
     632             :          */
     633           0 :         return QB_IPC_SHM;
     634             :     }
     635           0 :     return requested;
     636             : }
     637             : 
     638             : qb_ipcs_service_t *
     639           0 : mainloop_add_ipc_server(const char *name, enum qb_ipc_type type,
     640             :                         struct qb_ipcs_service_handlers *callbacks)
     641             : {
     642           0 :     return mainloop_add_ipc_server_with_prio(name, type, callbacks, QB_LOOP_MED);
     643             : }
     644             : 
     645             : qb_ipcs_service_t *
     646           0 : mainloop_add_ipc_server_with_prio(const char *name, enum qb_ipc_type type,
     647             :                                   struct qb_ipcs_service_handlers *callbacks,
     648             :                                   enum qb_loop_priority prio)
     649             : {
     650           0 :     int rc = 0;
     651           0 :     qb_ipcs_service_t *server = NULL;
     652             : 
     653           0 :     if (gio_map == NULL) {
     654           0 :         gio_map = qb_array_create_2(64, sizeof(struct gio_to_qb_poll), 1);
     655             :     }
     656             : 
     657           0 :     server = qb_ipcs_create(name, 0, pick_ipc_type(type), callbacks);
     658             : 
     659           0 :     if (server == NULL) {
     660           0 :         crm_err("Could not create %s IPC server: %s (%d)",
     661             :                 name, pcmk_rc_str(errno), errno);
     662           0 :         return NULL;
     663             :     }
     664             : 
     665           0 :     if (prio != QB_LOOP_MED) {
     666           0 :         qb_ipcs_request_rate_limit(server, conv_libqb_prio2ratelimit(prio));
     667             :     }
     668             : 
     669             :     /* All clients should use at least ipc_buffer_max as their buffer size */
     670           0 :     qb_ipcs_enforce_buffer_size(server, crm_ipc_default_buffer_size());
     671           0 :     qb_ipcs_poll_handlers_set(server, &gio_poll_funcs);
     672             : 
     673           0 :     rc = qb_ipcs_run(server);
     674           0 :     if (rc < 0) {
     675           0 :         crm_err("Could not start %s IPC server: %s (%d)", name, pcmk_strerror(rc), rc);
     676           0 :         return NULL; // qb_ipcs_run() destroys server on failure
     677             :     }
     678             : 
     679           0 :     return server;
     680             : }
     681             : 
     682             : void
     683           0 : mainloop_del_ipc_server(qb_ipcs_service_t * server)
     684             : {
     685           0 :     if (server) {
     686           0 :         qb_ipcs_destroy(server);
     687             :     }
     688           0 : }
     689             : 
     690             : struct mainloop_io_s {
     691             :     char *name;
     692             :     void *userdata;
     693             : 
     694             :     int fd;
     695             :     guint source;
     696             :     crm_ipc_t *ipc;
     697             :     GIOChannel *channel;
     698             : 
     699             :     int (*dispatch_fn_ipc) (const char *buffer, ssize_t length, gpointer userdata);
     700             :     int (*dispatch_fn_io) (gpointer userdata);
     701             :     void (*destroy_fn) (gpointer userdata);
     702             : 
     703             : };
     704             : 
     705             : /*!
     706             :  * \internal
     707             :  * \brief I/O watch callback function (GIOFunc)
     708             :  *
     709             :  * \param[in] gio        I/O channel being watched
     710             :  * \param[in] condition  I/O condition satisfied
     711             :  * \param[in] data       User data passed when source was created
     712             :  *
     713             :  * \return G_SOURCE_REMOVE to remove source, G_SOURCE_CONTINUE to keep it
     714             :  */
     715             : static gboolean
     716           0 : mainloop_gio_callback(GIOChannel *gio, GIOCondition condition, gpointer data)
     717             : {
     718           0 :     gboolean rc = G_SOURCE_CONTINUE;
     719           0 :     mainloop_io_t *client = data;
     720             : 
     721           0 :     CRM_ASSERT(client->fd == g_io_channel_unix_get_fd(gio));
     722             : 
     723           0 :     if (condition & G_IO_IN) {
     724           0 :         if (client->ipc) {
     725           0 :             long read_rc = 0L;
     726           0 :             int max = 10;
     727             : 
     728             :             do {
     729           0 :                 read_rc = crm_ipc_read(client->ipc);
     730           0 :                 if (read_rc <= 0) {
     731           0 :                     crm_trace("Could not read IPC message from %s: %s (%ld)",
     732             :                               client->name, pcmk_strerror(read_rc), read_rc);
     733             : 
     734           0 :                 } else if (client->dispatch_fn_ipc) {
     735           0 :                     const char *buffer = crm_ipc_buffer(client->ipc);
     736             : 
     737           0 :                     crm_trace("New %ld-byte IPC message from %s "
     738             :                               "after I/O condition %d",
     739             :                               read_rc, client->name, (int) condition);
     740           0 :                     if (client->dispatch_fn_ipc(buffer, read_rc, client->userdata) < 0) {
     741           0 :                         crm_trace("Connection to %s no longer required", client->name);
     742           0 :                         rc = G_SOURCE_REMOVE;
     743             :                     }
     744             :                 }
     745             : 
     746           0 :             } while ((rc == G_SOURCE_CONTINUE) && (read_rc > 0) && --max > 0);
     747             : 
     748             :         } else {
     749           0 :             crm_trace("New I/O event for %s after I/O condition %d",
     750             :                       client->name, (int) condition);
     751           0 :             if (client->dispatch_fn_io) {
     752           0 :                 if (client->dispatch_fn_io(client->userdata) < 0) {
     753           0 :                     crm_trace("Connection to %s no longer required", client->name);
     754           0 :                     rc = G_SOURCE_REMOVE;
     755             :                 }
     756             :             }
     757             :         }
     758             :     }
     759             : 
     760           0 :     if (client->ipc && !crm_ipc_connected(client->ipc)) {
     761           0 :         crm_err("Connection to %s closed " CRM_XS "client=%p condition=%d",
     762             :                 client->name, client, condition);
     763           0 :         rc = G_SOURCE_REMOVE;
     764             : 
     765           0 :     } else if (condition & (G_IO_HUP | G_IO_NVAL | G_IO_ERR)) {
     766           0 :         crm_trace("The connection %s[%p] has been closed (I/O condition=%d)",
     767             :                   client->name, client, condition);
     768           0 :         rc = G_SOURCE_REMOVE;
     769             : 
     770           0 :     } else if ((condition & G_IO_IN) == 0) {
     771             :         /*
     772             :            #define      GLIB_SYSDEF_POLLIN     =1
     773             :            #define      GLIB_SYSDEF_POLLPRI    =2
     774             :            #define      GLIB_SYSDEF_POLLOUT    =4
     775             :            #define      GLIB_SYSDEF_POLLERR    =8
     776             :            #define      GLIB_SYSDEF_POLLHUP    =16
     777             :            #define      GLIB_SYSDEF_POLLNVAL   =32
     778             : 
     779             :            typedef enum
     780             :            {
     781             :            G_IO_IN      GLIB_SYSDEF_POLLIN,
     782             :            G_IO_OUT     GLIB_SYSDEF_POLLOUT,
     783             :            G_IO_PRI     GLIB_SYSDEF_POLLPRI,
     784             :            G_IO_ERR     GLIB_SYSDEF_POLLERR,
     785             :            G_IO_HUP     GLIB_SYSDEF_POLLHUP,
     786             :            G_IO_NVAL    GLIB_SYSDEF_POLLNVAL
     787             :            } GIOCondition;
     788             : 
     789             :            A bitwise combination representing a condition to watch for on an event source.
     790             : 
     791             :            G_IO_IN      There is data to read.
     792             :            G_IO_OUT     Data can be written (without blocking).
     793             :            G_IO_PRI     There is urgent data to read.
     794             :            G_IO_ERR     Error condition.
     795             :            G_IO_HUP     Hung up (the connection has been broken, usually for pipes and sockets).
     796             :            G_IO_NVAL    Invalid request. The file descriptor is not open.
     797             :          */
     798           0 :         crm_err("Strange condition: %d", condition);
     799             :     }
     800             : 
     801             :     /* G_SOURCE_REMOVE results in mainloop_gio_destroy() being called
     802             :      * just before the source is removed from mainloop
     803             :      */
     804           0 :     return rc;
     805             : }
     806             : 
     807             : static void
     808           0 : mainloop_gio_destroy(gpointer c)
     809             : {
     810           0 :     mainloop_io_t *client = c;
     811           0 :     char *c_name = strdup(client->name);
     812             : 
     813             :     /* client->source is valid but about to be destroyed (ref_count == 0) in gmain.c
     814             :      * client->channel will still have ref_count > 0... should be == 1
     815             :      */
     816           0 :     crm_trace("Destroying client %s[%p]", c_name, c);
     817             : 
     818           0 :     if (client->ipc) {
     819           0 :         crm_ipc_close(client->ipc);
     820             :     }
     821             : 
     822           0 :     if (client->destroy_fn) {
     823           0 :         void (*destroy_fn) (gpointer userdata) = client->destroy_fn;
     824             : 
     825           0 :         client->destroy_fn = NULL;
     826           0 :         destroy_fn(client->userdata);
     827             :     }
     828             : 
     829           0 :     if (client->ipc) {
     830           0 :         crm_ipc_t *ipc = client->ipc;
     831             : 
     832           0 :         client->ipc = NULL;
     833           0 :         crm_ipc_destroy(ipc);
     834             :     }
     835             : 
     836           0 :     crm_trace("Destroyed client %s[%p]", c_name, c);
     837             : 
     838           0 :     free(client->name); client->name = NULL;
     839           0 :     free(client);
     840             : 
     841           0 :     free(c_name);
     842           0 : }
     843             : 
     844             : /*!
     845             :  * \brief Connect to IPC and add it as a main loop source
     846             :  *
     847             :  * \param[in,out] ipc        IPC connection to add
     848             :  * \param[in]     priority   Event source priority to use for connection
     849             :  * \param[in]     userdata   Data to register with callbacks
     850             :  * \param[in]     callbacks  Dispatch and destroy callbacks for connection
     851             :  * \param[out]    source     Newly allocated event source
     852             :  *
     853             :  * \return Standard Pacemaker return code
     854             :  *
     855             :  * \note On failure, the caller is still responsible for ipc. On success, the
     856             :  *       caller should call mainloop_del_ipc_client() when source is no longer
     857             :  *       needed, which will lead to the disconnection of the IPC later in the
     858             :  *       main loop if it is connected. However the IPC disconnects,
     859             :  *       mainloop_gio_destroy() will free ipc and source after calling the
     860             :  *       destroy callback.
     861             :  */
     862             : int
     863           0 : pcmk__add_mainloop_ipc(crm_ipc_t *ipc, int priority, void *userdata,
     864             :                        const struct ipc_client_callbacks *callbacks,
     865             :                        mainloop_io_t **source)
     866             : {
     867           0 :     int rc = pcmk_rc_ok;
     868           0 :     int fd = -1;
     869           0 :     const char *ipc_name = NULL;
     870             : 
     871           0 :     CRM_CHECK((ipc != NULL) && (callbacks != NULL), return EINVAL);
     872             : 
     873           0 :     ipc_name = pcmk__s(crm_ipc_name(ipc), "Pacemaker");
     874           0 :     rc = pcmk__connect_generic_ipc(ipc);
     875           0 :     if (rc != pcmk_rc_ok) {
     876           0 :         crm_debug("Connection to %s failed: %s", ipc_name, pcmk_rc_str(rc));
     877           0 :         return rc;
     878             :     }
     879             : 
     880           0 :     rc = pcmk__ipc_fd(ipc, &fd);
     881           0 :     if (rc != pcmk_rc_ok) {
     882           0 :         crm_debug("Could not obtain file descriptor for %s IPC: %s",
     883             :                   ipc_name, pcmk_rc_str(rc));
     884           0 :         crm_ipc_close(ipc);
     885           0 :         return rc;
     886             :     }
     887             : 
     888           0 :     *source = mainloop_add_fd(ipc_name, priority, fd, userdata, NULL);
     889           0 :     if (*source == NULL) {
     890           0 :         rc = errno;
     891           0 :         crm_ipc_close(ipc);
     892           0 :         return rc;
     893             :     }
     894             : 
     895           0 :     (*source)->ipc = ipc;
     896           0 :     (*source)->destroy_fn = callbacks->destroy;
     897           0 :     (*source)->dispatch_fn_ipc = callbacks->dispatch;
     898           0 :     return pcmk_rc_ok;
     899             : }
     900             : 
     901             : /*!
     902             :  * \brief Get period for mainloop timer
     903             :  *
     904             :  * \param[in]  timer      Timer
     905             :  *
     906             :  * \return Period in ms
     907             :  */
     908             : guint
     909           0 : pcmk__mainloop_timer_get_period(const mainloop_timer_t *timer)
     910             : {
     911           0 :     if (timer) {
     912           0 :         return timer->period_ms;
     913             :     }
     914           0 :     return 0;
     915             : }
     916             : 
     917             : mainloop_io_t *
     918           0 : mainloop_add_ipc_client(const char *name, int priority, size_t max_size,
     919             :                         void *userdata, struct ipc_client_callbacks *callbacks)
     920             : {
     921           0 :     crm_ipc_t *ipc = crm_ipc_new(name, max_size);
     922           0 :     mainloop_io_t *source = NULL;
     923           0 :     int rc = pcmk__add_mainloop_ipc(ipc, priority, userdata, callbacks,
     924             :                                     &source);
     925             : 
     926           0 :     if (rc != pcmk_rc_ok) {
     927           0 :         if (crm_log_level == LOG_STDOUT) {
     928           0 :             fprintf(stderr, "Connection to %s failed: %s",
     929             :                     name, pcmk_rc_str(rc));
     930             :         }
     931           0 :         crm_ipc_destroy(ipc);
     932           0 :         if (rc > 0) {
     933           0 :             errno = rc;
     934             :         } else {
     935           0 :             errno = ENOTCONN;
     936             :         }
     937           0 :         return NULL;
     938             :     }
     939           0 :     return source;
     940             : }
     941             : 
     942             : void
     943           0 : mainloop_del_ipc_client(mainloop_io_t * client)
     944             : {
     945           0 :     mainloop_del_fd(client);
     946           0 : }
     947             : 
     948             : crm_ipc_t *
     949           0 : mainloop_get_ipc_client(mainloop_io_t * client)
     950             : {
     951           0 :     if (client) {
     952           0 :         return client->ipc;
     953             :     }
     954           0 :     return NULL;
     955             : }
     956             : 
     957             : mainloop_io_t *
     958           0 : mainloop_add_fd(const char *name, int priority, int fd, void *userdata,
     959             :                 struct mainloop_fd_callbacks * callbacks)
     960             : {
     961           0 :     mainloop_io_t *client = NULL;
     962             : 
     963           0 :     if (fd >= 0) {
     964           0 :         client = calloc(1, sizeof(mainloop_io_t));
     965           0 :         if (client == NULL) {
     966           0 :             return NULL;
     967             :         }
     968           0 :         client->name = strdup(name);
     969           0 :         client->userdata = userdata;
     970             : 
     971           0 :         if (callbacks) {
     972           0 :             client->destroy_fn = callbacks->destroy;
     973           0 :             client->dispatch_fn_io = callbacks->dispatch;
     974             :         }
     975             : 
     976           0 :         client->fd = fd;
     977           0 :         client->channel = g_io_channel_unix_new(fd);
     978           0 :         client->source =
     979           0 :             g_io_add_watch_full(client->channel, priority,
     980             :                                 (G_IO_IN | G_IO_HUP | G_IO_NVAL | G_IO_ERR), mainloop_gio_callback,
     981             :                                 client, mainloop_gio_destroy);
     982             : 
     983             :         /* Now that mainloop now holds a reference to channel,
     984             :          * thanks to g_io_add_watch_full(), drop ours from g_io_channel_unix_new().
     985             :          *
     986             :          * This means that channel will be free'd by:
     987             :          * g_main_context_dispatch() or g_source_remove()
     988             :          *  -> g_source_destroy_internal()
     989             :          *      -> g_source_callback_unref()
     990             :          * shortly after mainloop_gio_destroy() completes
     991             :          */
     992           0 :         g_io_channel_unref(client->channel);
     993           0 :         crm_trace("Added connection %d for %s[%p].%d", client->source, client->name, client, fd);
     994             :     } else {
     995           0 :         errno = EINVAL;
     996             :     }
     997             : 
     998           0 :     return client;
     999             : }
    1000             : 
    1001             : void
    1002           0 : mainloop_del_fd(mainloop_io_t * client)
    1003             : {
    1004           0 :     if (client != NULL) {
    1005           0 :         crm_trace("Removing client %s[%p]", client->name, client);
    1006           0 :         if (client->source) {
    1007             :             /* Results in mainloop_gio_destroy() being called just
    1008             :              * before the source is removed from mainloop
    1009             :              */
    1010           0 :             g_source_remove(client->source);
    1011             :         }
    1012             :     }
    1013           0 : }
    1014             : 
    1015             : static GList *child_list = NULL;
    1016             : 
    1017             : pid_t
    1018           0 : mainloop_child_pid(mainloop_child_t * child)
    1019             : {
    1020           0 :     return child->pid;
    1021             : }
    1022             : 
    1023             : const char *
    1024           0 : mainloop_child_name(mainloop_child_t * child)
    1025             : {
    1026           0 :     return child->desc;
    1027             : }
    1028             : 
    1029             : int
    1030           0 : mainloop_child_timeout(mainloop_child_t * child)
    1031             : {
    1032           0 :     return child->timeout;
    1033             : }
    1034             : 
    1035             : void *
    1036           0 : mainloop_child_userdata(mainloop_child_t * child)
    1037             : {
    1038           0 :     return child->privatedata;
    1039             : }
    1040             : 
    1041             : void
    1042           0 : mainloop_clear_child_userdata(mainloop_child_t * child)
    1043             : {
    1044           0 :     child->privatedata = NULL;
    1045           0 : }
    1046             : 
    1047             : /* good function name */
    1048             : static void
    1049           0 : child_free(mainloop_child_t *child)
    1050             : {
    1051           0 :     if (child->timerid != 0) {
    1052           0 :         crm_trace("Removing timer %d", child->timerid);
    1053           0 :         g_source_remove(child->timerid);
    1054           0 :         child->timerid = 0;
    1055             :     }
    1056           0 :     free(child->desc);
    1057           0 :     free(child);
    1058           0 : }
    1059             : 
    1060             : /* terrible function name */
    1061             : static int
    1062           0 : child_kill_helper(mainloop_child_t *child)
    1063             : {
    1064             :     int rc;
    1065           0 :     if (child->flags & mainloop_leave_pid_group) {
    1066           0 :         crm_debug("Kill pid %d only. leave group intact.", child->pid);
    1067           0 :         rc = kill(child->pid, SIGKILL);
    1068             :     } else {
    1069           0 :         crm_debug("Kill pid %d's group", child->pid);
    1070           0 :         rc = kill(-child->pid, SIGKILL);
    1071             :     }
    1072             : 
    1073           0 :     if (rc < 0) {
    1074           0 :         if (errno != ESRCH) {
    1075           0 :             crm_perror(LOG_ERR, "kill(%d, KILL) failed", child->pid);
    1076             :         }
    1077           0 :         return -errno;
    1078             :     }
    1079           0 :     return 0;
    1080             : }
    1081             : 
    1082             : static gboolean
    1083           0 : child_timeout_callback(gpointer p)
    1084             : {
    1085           0 :     mainloop_child_t *child = p;
    1086           0 :     int rc = 0;
    1087             : 
    1088           0 :     child->timerid = 0;
    1089           0 :     if (child->timeout) {
    1090           0 :         crm_warn("%s process (PID %d) will not die!", child->desc, (int)child->pid);
    1091           0 :         return FALSE;
    1092             :     }
    1093             : 
    1094           0 :     rc = child_kill_helper(child);
    1095           0 :     if (rc == -ESRCH) {
    1096             :         /* Nothing left to do. pid doesn't exist */
    1097           0 :         return FALSE;
    1098             :     }
    1099             : 
    1100           0 :     child->timeout = TRUE;
    1101           0 :     crm_debug("%s process (PID %d) timed out", child->desc, (int)child->pid);
    1102             : 
    1103           0 :     child->timerid = g_timeout_add(5000, child_timeout_callback, child);
    1104           0 :     return FALSE;
    1105             : }
    1106             : 
    1107             : static bool
    1108           0 : child_waitpid(mainloop_child_t *child, int flags)
    1109             : {
    1110           0 :     int rc = 0;
    1111           0 :     int core = 0;
    1112           0 :     int signo = 0;
    1113           0 :     int status = 0;
    1114           0 :     int exitcode = 0;
    1115           0 :     bool callback_needed = true;
    1116             : 
    1117           0 :     rc = waitpid(child->pid, &status, flags);
    1118           0 :     if (rc == 0) { // WNOHANG in flags, and child status is not available
    1119           0 :         crm_trace("Child process %d (%s) still active",
    1120             :                   child->pid, child->desc);
    1121           0 :         callback_needed = false;
    1122             : 
    1123           0 :     } else if (rc != child->pid) {
    1124             :         /* According to POSIX, possible conditions:
    1125             :          * - child->pid was non-positive (process group or any child),
    1126             :          *   and rc is specific child
    1127             :          * - errno ECHILD (pid does not exist or is not child)
    1128             :          * - errno EINVAL (invalid flags)
    1129             :          * - errno EINTR (caller interrupted by signal)
    1130             :          *
    1131             :          * @TODO Handle these cases more specifically.
    1132             :          */
    1133           0 :         signo = SIGCHLD;
    1134           0 :         exitcode = 1;
    1135           0 :         crm_notice("Wait for child process %d (%s) interrupted: %s",
    1136             :                    child->pid, child->desc, pcmk_rc_str(errno));
    1137             : 
    1138           0 :     } else if (WIFEXITED(status)) {
    1139           0 :         exitcode = WEXITSTATUS(status);
    1140           0 :         crm_trace("Child process %d (%s) exited with status %d",
    1141             :                   child->pid, child->desc, exitcode);
    1142             : 
    1143           0 :     } else if (WIFSIGNALED(status)) {
    1144           0 :         signo = WTERMSIG(status);
    1145           0 :         crm_trace("Child process %d (%s) exited with signal %d (%s)",
    1146             :                   child->pid, child->desc, signo, strsignal(signo));
    1147             : 
    1148             : #ifdef WCOREDUMP // AIX, SunOS, maybe others
    1149           0 :     } else if (WCOREDUMP(status)) {
    1150           0 :         core = 1;
    1151           0 :         crm_err("Child process %d (%s) dumped core",
    1152             :                 child->pid, child->desc);
    1153             : #endif
    1154             : 
    1155             :     } else { // flags must contain WUNTRACED and/or WCONTINUED to reach this
    1156           0 :         crm_trace("Child process %d (%s) stopped or continued",
    1157             :                   child->pid, child->desc);
    1158           0 :         callback_needed = false;
    1159             :     }
    1160             : 
    1161           0 :     if (callback_needed && child->callback) {
    1162           0 :         child->callback(child, child->pid, core, signo, exitcode);
    1163             :     }
    1164           0 :     return callback_needed;
    1165             : }
    1166             : 
    1167             : static void
    1168           0 : child_death_dispatch(int signal)
    1169             : {
    1170           0 :     for (GList *iter = child_list; iter; ) {
    1171           0 :         GList *saved = iter;
    1172           0 :         mainloop_child_t *child = iter->data;
    1173             : 
    1174           0 :         iter = iter->next;
    1175           0 :         if (child_waitpid(child, WNOHANG)) {
    1176           0 :             crm_trace("Removing completed process %d from child list",
    1177             :                       child->pid);
    1178           0 :             child_list = g_list_remove_link(child_list, saved);
    1179           0 :             g_list_free(saved);
    1180           0 :             child_free(child);
    1181             :         }
    1182             :     }
    1183           0 : }
    1184             : 
    1185             : static gboolean
    1186           0 : child_signal_init(gpointer p)
    1187             : {
    1188           0 :     crm_trace("Installed SIGCHLD handler");
    1189             :     /* Do NOT use g_child_watch_add() and friends, they rely on pthreads */
    1190           0 :     mainloop_add_signal(SIGCHLD, child_death_dispatch);
    1191             : 
    1192             :     /* In case they terminated before the signal handler was installed */
    1193           0 :     child_death_dispatch(SIGCHLD);
    1194           0 :     return FALSE;
    1195             : }
    1196             : 
    1197             : gboolean
    1198           0 : mainloop_child_kill(pid_t pid)
    1199             : {
    1200             :     GList *iter;
    1201           0 :     mainloop_child_t *child = NULL;
    1202           0 :     mainloop_child_t *match = NULL;
    1203             :     /* It is impossible to block SIGKILL, this allows us to
    1204             :      * call waitpid without WNOHANG flag.*/
    1205           0 :     int waitflags = 0, rc = 0;
    1206             : 
    1207           0 :     for (iter = child_list; iter != NULL && match == NULL; iter = iter->next) {
    1208           0 :         child = iter->data;
    1209           0 :         if (pid == child->pid) {
    1210           0 :             match = child;
    1211             :         }
    1212             :     }
    1213             : 
    1214           0 :     if (match == NULL) {
    1215           0 :         return FALSE;
    1216             :     }
    1217             : 
    1218           0 :     rc = child_kill_helper(match);
    1219           0 :     if(rc == -ESRCH) {
    1220             :         /* It's gone, but hasn't shown up in waitpid() yet. Wait until we get
    1221             :          * SIGCHLD and let handler clean it up as normal (so we get the correct
    1222             :          * return code/status). The blocking alternative would be to call
    1223             :          * child_waitpid(match, 0).
    1224             :          */
    1225           0 :         crm_trace("Waiting for signal that child process %d completed",
    1226             :                   match->pid);
    1227           0 :         return TRUE;
    1228             : 
    1229           0 :     } else if(rc != 0) {
    1230             :         /* If KILL for some other reason set the WNOHANG flag since we
    1231             :          * can't be certain what happened.
    1232             :          */
    1233           0 :         waitflags = WNOHANG;
    1234             :     }
    1235             : 
    1236           0 :     if (!child_waitpid(match, waitflags)) {
    1237             :         /* not much we can do if this occurs */
    1238           0 :         return FALSE;
    1239             :     }
    1240             : 
    1241           0 :     child_list = g_list_remove(child_list, match);
    1242           0 :     child_free(match);
    1243           0 :     return TRUE;
    1244             : }
    1245             : 
    1246             : /* Create/Log a new tracked process
    1247             :  * To track a process group, use -pid
    1248             :  *
    1249             :  * @TODO Using a non-positive pid (i.e. any child, or process group) would
    1250             :  *       likely not be useful since we will free the child after the first
    1251             :  *       completed process.
    1252             :  */
    1253             : void
    1254           0 : mainloop_child_add_with_flags(pid_t pid, int timeout, const char *desc, void *privatedata, enum mainloop_child_flags flags, 
    1255             :                    void (*callback) (mainloop_child_t * p, pid_t pid, int core, int signo, int exitcode))
    1256             : {
    1257             :     static bool need_init = TRUE;
    1258           0 :     mainloop_child_t *child = pcmk__assert_alloc(1, sizeof(mainloop_child_t));
    1259             : 
    1260           0 :     child->pid = pid;
    1261           0 :     child->timerid = 0;
    1262           0 :     child->timeout = FALSE;
    1263           0 :     child->privatedata = privatedata;
    1264           0 :     child->callback = callback;
    1265           0 :     child->flags = flags;
    1266           0 :     child->desc = pcmk__str_copy(desc);
    1267             : 
    1268           0 :     if (timeout) {
    1269           0 :         child->timerid = g_timeout_add(timeout, child_timeout_callback, child);
    1270             :     }
    1271             : 
    1272           0 :     child_list = g_list_append(child_list, child);
    1273             : 
    1274           0 :     if(need_init) {
    1275           0 :         need_init = FALSE;
    1276             :         /* SIGCHLD processing has to be invoked from mainloop.
    1277             :          * We do not want it to be possible to both add a child pid
    1278             :          * to mainloop, and have the pid's exit callback invoked within
    1279             :          * the same callstack. */
    1280           0 :         g_timeout_add(1, child_signal_init, NULL);
    1281             :     }
    1282           0 : }
    1283             : 
    1284             : void
    1285           0 : mainloop_child_add(pid_t pid, int timeout, const char *desc, void *privatedata,
    1286             :                    void (*callback) (mainloop_child_t * p, pid_t pid, int core, int signo, int exitcode))
    1287             : {
    1288           0 :     mainloop_child_add_with_flags(pid, timeout, desc, privatedata, 0, callback);
    1289           0 : }
    1290             : 
    1291             : static gboolean
    1292           0 : mainloop_timer_cb(gpointer user_data)
    1293             : {
    1294           0 :     int id = 0;
    1295           0 :     bool repeat = FALSE;
    1296           0 :     struct mainloop_timer_s *t = user_data;
    1297             : 
    1298           0 :     CRM_ASSERT(t != NULL);
    1299             : 
    1300           0 :     id = t->id;
    1301           0 :     t->id = 0; /* Ensure it's unset during callbacks so that
    1302             :                 * mainloop_timer_running() works as expected
    1303             :                 */
    1304             : 
    1305           0 :     if(t->cb) {
    1306           0 :         crm_trace("Invoking callbacks for timer %s", t->name);
    1307           0 :         repeat = t->repeat;
    1308           0 :         if(t->cb(t->userdata) == FALSE) {
    1309           0 :             crm_trace("Timer %s complete", t->name);
    1310           0 :             repeat = FALSE;
    1311             :         }
    1312             :     }
    1313             : 
    1314           0 :     if(repeat) {
    1315             :         /* Restore if repeating */
    1316           0 :         t->id = id;
    1317             :     }
    1318             : 
    1319           0 :     return repeat;
    1320             : }
    1321             : 
    1322             : bool
    1323           0 : mainloop_timer_running(mainloop_timer_t *t)
    1324             : {
    1325           0 :     if(t && t->id != 0) {
    1326           0 :         return TRUE;
    1327             :     }
    1328           0 :     return FALSE;
    1329             : }
    1330             : 
    1331             : void
    1332           0 : mainloop_timer_start(mainloop_timer_t *t)
    1333             : {
    1334           0 :     mainloop_timer_stop(t);
    1335           0 :     if(t && t->period_ms > 0) {
    1336           0 :         crm_trace("Starting timer %s", t->name);
    1337           0 :         t->id = g_timeout_add(t->period_ms, mainloop_timer_cb, t);
    1338             :     }
    1339           0 : }
    1340             : 
    1341             : void
    1342           0 : mainloop_timer_stop(mainloop_timer_t *t)
    1343             : {
    1344           0 :     if(t && t->id != 0) {
    1345           0 :         crm_trace("Stopping timer %s", t->name);
    1346           0 :         g_source_remove(t->id);
    1347           0 :         t->id = 0;
    1348             :     }
    1349           0 : }
    1350             : 
    1351             : guint
    1352           0 : mainloop_timer_set_period(mainloop_timer_t *t, guint period_ms)
    1353             : {
    1354           0 :     guint last = 0;
    1355             : 
    1356           0 :     if(t) {
    1357           0 :         last = t->period_ms;
    1358           0 :         t->period_ms = period_ms;
    1359             :     }
    1360             : 
    1361           0 :     if(t && t->id != 0 && last != t->period_ms) {
    1362           0 :         mainloop_timer_start(t);
    1363             :     }
    1364           0 :     return last;
    1365             : }
    1366             : 
    1367             : mainloop_timer_t *
    1368           0 : mainloop_timer_add(const char *name, guint period_ms, bool repeat, GSourceFunc cb, void *userdata)
    1369             : {
    1370           0 :     mainloop_timer_t *t = pcmk__assert_alloc(1, sizeof(mainloop_timer_t));
    1371             : 
    1372           0 :     if (name != NULL) {
    1373           0 :         t->name = crm_strdup_printf("%s-%u-%d", name, period_ms, repeat);
    1374             :     } else {
    1375           0 :         t->name = crm_strdup_printf("%p-%u-%d", t, period_ms, repeat);
    1376             :     }
    1377           0 :     t->id = 0;
    1378           0 :     t->period_ms = period_ms;
    1379           0 :     t->repeat = repeat;
    1380           0 :     t->cb = cb;
    1381           0 :     t->userdata = userdata;
    1382           0 :     crm_trace("Created timer %s with %p %p", t->name, userdata, t->userdata);
    1383           0 :     return t;
    1384             : }
    1385             : 
    1386             : void
    1387           0 : mainloop_timer_del(mainloop_timer_t *t)
    1388             : {
    1389           0 :     if(t) {
    1390           0 :         crm_trace("Destroying timer %s", t->name);
    1391           0 :         mainloop_timer_stop(t);
    1392           0 :         free(t->name);
    1393           0 :         free(t);
    1394             :     }
    1395           0 : }
    1396             : 
    1397             : /*
    1398             :  * Helpers to make sure certain events aren't lost at shutdown
    1399             :  */
    1400             : 
    1401             : static gboolean
    1402           0 : drain_timeout_cb(gpointer user_data)
    1403             : {
    1404           0 :     bool *timeout_popped = (bool*) user_data;
    1405             : 
    1406           0 :     *timeout_popped = TRUE;
    1407           0 :     return FALSE;
    1408             : }
    1409             : 
    1410             : /*!
    1411             :  * \brief Drain some remaining main loop events then quit it
    1412             :  *
    1413             :  * \param[in,out] mloop  Main loop to drain and quit
    1414             :  * \param[in]     n      Drain up to this many pending events
    1415             :  */
    1416             : void
    1417           0 : pcmk_quit_main_loop(GMainLoop *mloop, unsigned int n)
    1418             : {
    1419           0 :     if ((mloop != NULL) && g_main_loop_is_running(mloop)) {
    1420           0 :         GMainContext *ctx = g_main_loop_get_context(mloop);
    1421             : 
    1422             :         /* Drain up to n events in case some memory clean-up is pending
    1423             :          * (helpful to reduce noise in valgrind output).
    1424             :          */
    1425           0 :         for (int i = 0; (i < n) && g_main_context_pending(ctx); ++i) {
    1426           0 :             g_main_context_dispatch(ctx);
    1427             :         }
    1428           0 :         g_main_loop_quit(mloop);
    1429             :     }
    1430           0 : }
    1431             : 
    1432             : /*!
    1433             :  * \brief Process main loop events while a certain condition is met
    1434             :  *
    1435             :  * \param[in,out] mloop     Main loop to process
    1436             :  * \param[in]     timer_ms  Don't process longer than this amount of time
    1437             :  * \param[in]     check     Function that returns true if events should be
    1438             :  *                          processed
    1439             :  *
    1440             :  * \note This function is intended to be called at shutdown if certain important
    1441             :  *       events should not be missed. The caller would likely quit the main loop
    1442             :  *       or exit after calling this function. The check() function will be
    1443             :  *       passed the remaining timeout in milliseconds.
    1444             :  */
    1445             : void
    1446           0 : pcmk_drain_main_loop(GMainLoop *mloop, guint timer_ms, bool (*check)(guint))
    1447             : {
    1448           0 :     bool timeout_popped = FALSE;
    1449           0 :     guint timer = 0;
    1450           0 :     GMainContext *ctx = NULL;
    1451             : 
    1452           0 :     CRM_CHECK(mloop && check, return);
    1453             : 
    1454           0 :     ctx = g_main_loop_get_context(mloop);
    1455           0 :     if (ctx) {
    1456           0 :         time_t start_time = time(NULL);
    1457             : 
    1458           0 :         timer = g_timeout_add(timer_ms, drain_timeout_cb, &timeout_popped);
    1459           0 :         while (!timeout_popped
    1460           0 :                && check(timer_ms - (time(NULL) - start_time) * 1000)) {
    1461           0 :             g_main_context_iteration(ctx, TRUE);
    1462             :         }
    1463             :     }
    1464           0 :     if (!timeout_popped && (timer > 0)) {
    1465           0 :         g_source_remove(timer);
    1466             :     }
    1467             : }
    1468             : 
    1469             : // Deprecated functions kept only for backward API compatibility
    1470             : // LCOV_EXCL_START
    1471             : 
    1472             : #include <crm/common/mainloop_compat.h>
    1473             : 
    1474             : gboolean
    1475             : crm_signal(int sig, void (*dispatch) (int sig))
    1476             : {
    1477             :     return crm_signal_handler(sig, dispatch) != SIG_ERR;
    1478             : }
    1479             : 
    1480             : // LCOV_EXCL_STOP
    1481             : // End deprecated API

Generated by: LCOV version 1.14