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