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
|