root/lib/common/mainloop.c

/* [previous][next][first][last][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. crm_trigger_prepare
  2. crm_trigger_check
  3. crm_trigger_dispatch
  4. crm_trigger_finalize
  5. mainloop_setup_trigger
  6. mainloop_trigger_complete
  7. mainloop_add_trigger
  8. mainloop_set_trigger
  9. mainloop_destroy_trigger
  10. crm_signal_dispatch
  11. mainloop_signal_handler
  12. crm_signal_handler
  13. mainloop_destroy_signal_entry
  14. mainloop_add_signal
  15. mainloop_destroy_signal
  16. mainloop_cleanup
  17. gio_read_socket
  18. gio_poll_destroy
  19. conv_prio_libqb2glib
  20. conv_libqb_prio2ratelimit
  21. gio_poll_dispatch_update
  22. gio_poll_dispatch_add
  23. gio_poll_dispatch_mod
  24. gio_poll_dispatch_del
  25. pick_ipc_type
  26. mainloop_add_ipc_server
  27. mainloop_add_ipc_server_with_prio
  28. mainloop_del_ipc_server
  29. mainloop_gio_callback
  30. mainloop_gio_destroy
  31. pcmk__add_mainloop_ipc
  32. pcmk__mainloop_timer_get_period
  33. mainloop_add_ipc_client
  34. mainloop_del_ipc_client
  35. mainloop_get_ipc_client
  36. mainloop_add_fd
  37. mainloop_del_fd
  38. mainloop_child_pid
  39. mainloop_child_name
  40. mainloop_child_timeout
  41. mainloop_child_userdata
  42. mainloop_clear_child_userdata
  43. child_free
  44. child_kill_helper
  45. child_timeout_callback
  46. child_waitpid
  47. child_death_dispatch
  48. child_signal_init
  49. mainloop_child_kill
  50. mainloop_child_add_with_flags
  51. mainloop_child_add
  52. mainloop_timer_cb
  53. mainloop_timer_running
  54. mainloop_timer_start
  55. mainloop_timer_stop
  56. mainloop_timer_set_period
  57. mainloop_timer_add
  58. mainloop_timer_del
  59. drain_timeout_cb
  60. pcmk_quit_main_loop
  61. pcmk_drain_main_loop
  62. crm_signal

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

/* [previous][next][first][last][top][bottom][index][help] */