pacemaker 2.1.1-77db578727
Scalable High-Availability cluster resource manager
Loading...
Searching...
No Matches
mainloop.c
Go to the documentation of this file.
1/*
2 * Copyright 2004-2021 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_strerror(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_strerror(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 return NULL;
707 }
708
709 return server;
710}
711
712void
713mainloop_del_ipc_server(qb_ipcs_service_t * server)
714{
715 if (server) {
716 qb_ipcs_destroy(server);
717 }
718}
719
720struct mainloop_io_s {
721 char *name;
722 void *userdata;
723
724 int fd;
725 guint source;
726 crm_ipc_t *ipc;
727 GIOChannel *channel;
728
729 int (*dispatch_fn_ipc) (const char *buffer, ssize_t length, gpointer userdata);
730 int (*dispatch_fn_io) (gpointer userdata);
731 void (*destroy_fn) (gpointer userdata);
732
733};
734
745static gboolean
746mainloop_gio_callback(GIOChannel * gio, GIOCondition condition, gpointer data)
747{
748 gboolean rc = G_SOURCE_CONTINUE;
749 mainloop_io_t *client = data;
750
751 CRM_ASSERT(client->fd == g_io_channel_unix_get_fd(gio));
752
753 if (condition & G_IO_IN) {
754 if (client->ipc) {
755 long read_rc = 0L;
756 int max = 10;
757
758 do {
759 read_rc = crm_ipc_read(client->ipc);
760 if (read_rc <= 0) {
761 crm_trace("Could not read IPC message from %s: %s (%ld)",
762 client->name, pcmk_strerror(read_rc), read_rc);
763
764 } else if (client->dispatch_fn_ipc) {
765 const char *buffer = crm_ipc_buffer(client->ipc);
766
767 crm_trace("New %ld-byte IPC message from %s "
768 "after I/O condition %d",
769 read_rc, client->name, (int) condition);
770 if (client->dispatch_fn_ipc(buffer, read_rc, client->userdata) < 0) {
771 crm_trace("Connection to %s no longer required", client->name);
772 rc = G_SOURCE_REMOVE;
773 }
774 }
775
776 } while ((rc == G_SOURCE_CONTINUE) && (read_rc > 0) && --max > 0);
777
778 } else {
779 crm_trace("New I/O event for %s after I/O condition %d",
780 client->name, (int) condition);
781 if (client->dispatch_fn_io) {
782 if (client->dispatch_fn_io(client->userdata) < 0) {
783 crm_trace("Connection to %s no longer required", client->name);
784 rc = G_SOURCE_REMOVE;
785 }
786 }
787 }
788 }
789
790 if (client->ipc && crm_ipc_connected(client->ipc) == FALSE) {
791 crm_err("Connection to %s closed " CRM_XS "client=%p condition=%d",
792 client->name, client, condition);
793 rc = G_SOURCE_REMOVE;
794
795 } else if (condition & (G_IO_HUP | G_IO_NVAL | G_IO_ERR)) {
796 crm_trace("The connection %s[%p] has been closed (I/O condition=%d)",
797 client->name, client, condition);
798 rc = G_SOURCE_REMOVE;
799
800 } else if ((condition & G_IO_IN) == 0) {
801 /*
802 #define GLIB_SYSDEF_POLLIN =1
803 #define GLIB_SYSDEF_POLLPRI =2
804 #define GLIB_SYSDEF_POLLOUT =4
805 #define GLIB_SYSDEF_POLLERR =8
806 #define GLIB_SYSDEF_POLLHUP =16
807 #define GLIB_SYSDEF_POLLNVAL =32
808
809 typedef enum
810 {
811 G_IO_IN GLIB_SYSDEF_POLLIN,
812 G_IO_OUT GLIB_SYSDEF_POLLOUT,
813 G_IO_PRI GLIB_SYSDEF_POLLPRI,
814 G_IO_ERR GLIB_SYSDEF_POLLERR,
815 G_IO_HUP GLIB_SYSDEF_POLLHUP,
816 G_IO_NVAL GLIB_SYSDEF_POLLNVAL
817 } GIOCondition;
818
819 A bitwise combination representing a condition to watch for on an event source.
820
821 G_IO_IN There is data to read.
822 G_IO_OUT Data can be written (without blocking).
823 G_IO_PRI There is urgent data to read.
824 G_IO_ERR Error condition.
825 G_IO_HUP Hung up (the connection has been broken, usually for pipes and sockets).
826 G_IO_NVAL Invalid request. The file descriptor is not open.
827 */
828 crm_err("Strange condition: %d", condition);
829 }
830
831 /* G_SOURCE_REMOVE results in mainloop_gio_destroy() being called
832 * just before the source is removed from mainloop
833 */
834 return rc;
835}
836
837static void
838mainloop_gio_destroy(gpointer c)
839{
840 mainloop_io_t *client = c;
841 char *c_name = strdup(client->name);
842
843 /* client->source is valid but about to be destroyed (ref_count == 0) in gmain.c
844 * client->channel will still have ref_count > 0... should be == 1
845 */
846 crm_trace("Destroying client %s[%p]", c_name, c);
847
848 if (client->ipc) {
849 crm_ipc_close(client->ipc);
850 }
851
852 if (client->destroy_fn) {
853 void (*destroy_fn) (gpointer userdata) = client->destroy_fn;
854
855 client->destroy_fn = NULL;
856 destroy_fn(client->userdata);
857 }
858
859 if (client->ipc) {
860 crm_ipc_t *ipc = client->ipc;
861
862 client->ipc = NULL;
863 crm_ipc_destroy(ipc);
864 }
865
866 crm_trace("Destroyed client %s[%p]", c_name, c);
867
868 free(client->name); client->name = NULL;
869 free(client);
870
871 free(c_name);
872}
873
892int
893pcmk__add_mainloop_ipc(crm_ipc_t *ipc, int priority, void *userdata,
894 struct ipc_client_callbacks *callbacks,
895 mainloop_io_t **source)
896{
897 CRM_CHECK((ipc != NULL) && (callbacks != NULL), return EINVAL);
898
899 if (!crm_ipc_connect(ipc)) {
900 int rc = errno;
901 crm_debug("Connection to %s failed: %d", crm_ipc_name(ipc), errno);
902 return rc;
903 }
904 *source = mainloop_add_fd(crm_ipc_name(ipc), priority, crm_ipc_get_fd(ipc),
905 userdata, NULL);
906 if (*source == NULL) {
907 int rc = errno;
908
909 crm_ipc_close(ipc);
910 return rc;
911 }
912 (*source)->ipc = ipc;
913 (*source)->destroy_fn = callbacks->destroy;
914 (*source)->dispatch_fn_ipc = callbacks->dispatch;
915 return pcmk_rc_ok;
916}
917
925guint
927{
928 if (timer) {
929 return timer->period_ms;
930 }
931 return 0;
932}
933
935mainloop_add_ipc_client(const char *name, int priority, size_t max_size,
936 void *userdata, struct ipc_client_callbacks *callbacks)
937{
938 crm_ipc_t *ipc = crm_ipc_new(name, max_size);
939 mainloop_io_t *source = NULL;
940 int rc = pcmk__add_mainloop_ipc(ipc, priority, userdata, callbacks,
941 &source);
942
943 if (rc != pcmk_rc_ok) {
944 if (crm_log_level == LOG_STDOUT) {
945 fprintf(stderr, "Connection to %s failed: %s",
947 }
948 crm_ipc_destroy(ipc);
949 if (rc > 0) {
950 errno = rc;
951 } else {
952 errno = ENOTCONN;
953 }
954 return NULL;
955 }
956 return source;
957}
958
959void
964
965crm_ipc_t *
967{
968 if (client) {
969 return client->ipc;
970 }
971 return NULL;
972}
973
975mainloop_add_fd(const char *name, int priority, int fd, void *userdata,
976 struct mainloop_fd_callbacks * callbacks)
977{
978 mainloop_io_t *client = NULL;
979
980 if (fd >= 0) {
981 client = calloc(1, sizeof(mainloop_io_t));
982 if (client == NULL) {
983 return NULL;
984 }
985 client->name = strdup(name);
986 client->userdata = userdata;
987
988 if (callbacks) {
989 client->destroy_fn = callbacks->destroy;
990 client->dispatch_fn_io = callbacks->dispatch;
991 }
992
993 client->fd = fd;
994 client->channel = g_io_channel_unix_new(fd);
995 client->source =
996 g_io_add_watch_full(client->channel, priority,
997 (G_IO_IN | G_IO_HUP | G_IO_NVAL | G_IO_ERR), mainloop_gio_callback,
998 client, mainloop_gio_destroy);
999
1000 /* Now that mainloop now holds a reference to channel,
1001 * thanks to g_io_add_watch_full(), drop ours from g_io_channel_unix_new().
1002 *
1003 * This means that channel will be free'd by:
1004 * g_main_context_dispatch() or g_source_remove()
1005 * -> g_source_destroy_internal()
1006 * -> g_source_callback_unref()
1007 * shortly after mainloop_gio_destroy() completes
1008 */
1009 g_io_channel_unref(client->channel);
1010 crm_trace("Added connection %d for %s[%p].%d", client->source, client->name, client, fd);
1011 } else {
1012 errno = EINVAL;
1013 }
1014
1015 return client;
1016}
1017
1018void
1020{
1021 if (client != NULL) {
1022 crm_trace("Removing client %s[%p]", client->name, client);
1023 if (client->source) {
1024 /* Results in mainloop_gio_destroy() being called just
1025 * before the source is removed from mainloop
1026 */
1027 g_source_remove(client->source);
1028 }
1029 }
1030}
1031
1032static GList *child_list = NULL;
1033
1034pid_t
1036{
1037 return child->pid;
1038}
1039
1040const char *
1042{
1043 return child->desc;
1044}
1045
1046int
1048{
1049 return child->timeout;
1050}
1051
1052void *
1054{
1055 return child->privatedata;
1056}
1057
1058void
1060{
1061 child->privatedata = NULL;
1062}
1063
1064/* good function name */
1065static void
1066child_free(mainloop_child_t *child)
1067{
1068 if (child->timerid != 0) {
1069 crm_trace("Removing timer %d", child->timerid);
1070 g_source_remove(child->timerid);
1071 child->timerid = 0;
1072 }
1073 free(child->desc);
1074 free(child);
1075}
1076
1077/* terrible function name */
1078static int
1079child_kill_helper(mainloop_child_t *child)
1080{
1081 int rc;
1082 if (child->flags & mainloop_leave_pid_group) {
1083 crm_debug("Kill pid %d only. leave group intact.", child->pid);
1084 rc = kill(child->pid, SIGKILL);
1085 } else {
1086 crm_debug("Kill pid %d's group", child->pid);
1087 rc = kill(-child->pid, SIGKILL);
1088 }
1089
1090 if (rc < 0) {
1091 if (errno != ESRCH) {
1092 crm_perror(LOG_ERR, "kill(%d, KILL) failed", child->pid);
1093 }
1094 return -errno;
1095 }
1096 return 0;
1097}
1098
1099static gboolean
1100child_timeout_callback(gpointer p)
1101{
1102 mainloop_child_t *child = p;
1103 int rc = 0;
1104
1105 child->timerid = 0;
1106 if (child->timeout) {
1107 crm_crit("%s process (PID %d) will not die!", child->desc, (int)child->pid);
1108 return FALSE;
1109 }
1110
1111 rc = child_kill_helper(child);
1112 if (rc == -ESRCH) {
1113 /* Nothing left to do. pid doesn't exist */
1114 return FALSE;
1115 }
1116
1117 child->timeout = TRUE;
1118 crm_warn("%s process (PID %d) timed out", child->desc, (int)child->pid);
1119
1120 child->timerid = g_timeout_add(5000, child_timeout_callback, child);
1121 return FALSE;
1122}
1123
1124static bool
1125child_waitpid(mainloop_child_t *child, int flags)
1126{
1127 int rc = 0;
1128 int core = 0;
1129 int signo = 0;
1130 int status = 0;
1131 int exitcode = 0;
1132 bool callback_needed = true;
1133
1134 rc = waitpid(child->pid, &status, flags);
1135 if (rc == 0) { // WNOHANG in flags, and child status is not available
1136 crm_trace("Child process %d (%s) still active",
1137 child->pid, child->desc);
1138 callback_needed = false;
1139
1140 } else if (rc != child->pid) {
1141 /* According to POSIX, possible conditions:
1142 * - child->pid was non-positive (process group or any child),
1143 * and rc is specific child
1144 * - errno ECHILD (pid does not exist or is not child)
1145 * - errno EINVAL (invalid flags)
1146 * - errno EINTR (caller interrupted by signal)
1147 *
1148 * @TODO Handle these cases more specifically.
1149 */
1150 signo = SIGCHLD;
1151 exitcode = 1;
1152 crm_notice("Wait for child process %d (%s) interrupted: %s",
1153 child->pid, child->desc, pcmk_strerror(errno));
1154
1155 } else if (WIFEXITED(status)) {
1156 exitcode = WEXITSTATUS(status);
1157 crm_trace("Child process %d (%s) exited with status %d",
1158 child->pid, child->desc, exitcode);
1159
1160 } else if (WIFSIGNALED(status)) {
1161 signo = WTERMSIG(status);
1162 crm_trace("Child process %d (%s) exited with signal %d (%s)",
1163 child->pid, child->desc, signo, strsignal(signo));
1164
1165#ifdef WCOREDUMP // AIX, SunOS, maybe others
1166 } else if (WCOREDUMP(status)) {
1167 core = 1;
1168 crm_err("Child process %d (%s) dumped core",
1169 child->pid, child->desc);
1170#endif
1171
1172 } else { // flags must contain WUNTRACED and/or WCONTINUED to reach this
1173 crm_trace("Child process %d (%s) stopped or continued",
1174 child->pid, child->desc);
1175 callback_needed = false;
1176 }
1177
1178 if (callback_needed && child->callback) {
1179 child->callback(child, child->pid, core, signo, exitcode);
1180 }
1181 return callback_needed;
1182}
1183
1184static void
1185child_death_dispatch(int signal)
1186{
1187 for (GList *iter = child_list; iter; ) {
1188 GList *saved = iter;
1189 mainloop_child_t *child = iter->data;
1190
1191 iter = iter->next;
1192 if (child_waitpid(child, WNOHANG)) {
1193 crm_trace("Removing completed process %d from child list",
1194 child->pid);
1195 child_list = g_list_remove_link(child_list, saved);
1196 g_list_free(saved);
1197 child_free(child);
1198 }
1199 }
1200}
1201
1202static gboolean
1203child_signal_init(gpointer p)
1204{
1205 crm_trace("Installed SIGCHLD handler");
1206 /* Do NOT use g_child_watch_add() and friends, they rely on pthreads */
1207 mainloop_add_signal(SIGCHLD, child_death_dispatch);
1208
1209 /* In case they terminated before the signal handler was installed */
1210 child_death_dispatch(SIGCHLD);
1211 return FALSE;
1212}
1213
1214gboolean
1216{
1217 GList *iter;
1218 mainloop_child_t *child = NULL;
1219 mainloop_child_t *match = NULL;
1220 /* It is impossible to block SIGKILL, this allows us to
1221 * call waitpid without WNOHANG flag.*/
1222 int waitflags = 0, rc = 0;
1223
1224 for (iter = child_list; iter != NULL && match == NULL; iter = iter->next) {
1225 child = iter->data;
1226 if (pid == child->pid) {
1227 match = child;
1228 }
1229 }
1230
1231 if (match == NULL) {
1232 return FALSE;
1233 }
1234
1235 rc = child_kill_helper(match);
1236 if(rc == -ESRCH) {
1237 /* It's gone, but hasn't shown up in waitpid() yet. Wait until we get
1238 * SIGCHLD and let handler clean it up as normal (so we get the correct
1239 * return code/status). The blocking alternative would be to call
1240 * child_waitpid(match, 0).
1241 */
1242 crm_trace("Waiting for signal that child process %d completed",
1243 match->pid);
1244 return TRUE;
1245
1246 } else if(rc != 0) {
1247 /* If KILL for some other reason set the WNOHANG flag since we
1248 * can't be certain what happened.
1249 */
1250 waitflags = WNOHANG;
1251 }
1252
1253 if (!child_waitpid(match, waitflags)) {
1254 /* not much we can do if this occurs */
1255 return FALSE;
1256 }
1257
1258 child_list = g_list_remove(child_list, match);
1259 child_free(match);
1260 return TRUE;
1261}
1262
1263/* Create/Log a new tracked process
1264 * To track a process group, use -pid
1265 *
1266 * @TODO Using a non-positive pid (i.e. any child, or process group) would
1267 * likely not be useful since we will free the child after the first
1268 * completed process.
1269 */
1270void
1271mainloop_child_add_with_flags(pid_t pid, int timeout, const char *desc, void *privatedata, enum mainloop_child_flags flags,
1272 void (*callback) (mainloop_child_t * p, pid_t pid, int core, int signo, int exitcode))
1273{
1274 static bool need_init = TRUE;
1275 mainloop_child_t *child = g_new(mainloop_child_t, 1);
1276
1277 child->pid = pid;
1278 child->timerid = 0;
1279 child->timeout = FALSE;
1280 child->privatedata = privatedata;
1281 child->callback = callback;
1282 child->flags = flags;
1283
1284 if(desc) {
1285 child->desc = strdup(desc);
1286 }
1287
1288 if (timeout) {
1289 child->timerid = g_timeout_add(timeout, child_timeout_callback, child);
1290 }
1291
1292 child_list = g_list_append(child_list, child);
1293
1294 if(need_init) {
1295 need_init = FALSE;
1296 /* SIGCHLD processing has to be invoked from mainloop.
1297 * We do not want it to be possible to both add a child pid
1298 * to mainloop, and have the pid's exit callback invoked within
1299 * the same callstack. */
1300 g_timeout_add(1, child_signal_init, NULL);
1301 }
1302}
1303
1304void
1305mainloop_child_add(pid_t pid, int timeout, const char *desc, void *privatedata,
1306 void (*callback) (mainloop_child_t * p, pid_t pid, int core, int signo, int exitcode))
1307{
1308 mainloop_child_add_with_flags(pid, timeout, desc, privatedata, 0, callback);
1309}
1310
1311static gboolean
1312mainloop_timer_cb(gpointer user_data)
1313{
1314 int id = 0;
1315 bool repeat = FALSE;
1316 struct mainloop_timer_s *t = user_data;
1317
1318 CRM_ASSERT(t != NULL);
1319
1320 id = t->id;
1321 t->id = 0; /* Ensure it's unset during callbacks so that
1322 * mainloop_timer_running() works as expected
1323 */
1324
1325 if(t->cb) {
1326 crm_trace("Invoking callbacks for timer %s", t->name);
1327 repeat = t->repeat;
1328 if(t->cb(t->userdata) == FALSE) {
1329 crm_trace("Timer %s complete", t->name);
1330 repeat = FALSE;
1331 }
1332 }
1333
1334 if(repeat) {
1335 /* Restore if repeating */
1336 t->id = id;
1337 }
1338
1339 return repeat;
1340}
1341
1342bool
1344{
1345 if(t && t->id != 0) {
1346 return TRUE;
1347 }
1348 return FALSE;
1349}
1350
1351void
1353{
1355 if(t && t->period_ms > 0) {
1356 crm_trace("Starting timer %s", t->name);
1357 t->id = g_timeout_add(t->period_ms, mainloop_timer_cb, t);
1358 }
1359}
1360
1361void
1363{
1364 if(t && t->id != 0) {
1365 crm_trace("Stopping timer %s", t->name);
1366 g_source_remove(t->id);
1367 t->id = 0;
1368 }
1369}
1370
1371guint
1373{
1374 guint last = 0;
1375
1376 if(t) {
1377 last = t->period_ms;
1378 t->period_ms = period_ms;
1379 }
1380
1381 if(t && t->id != 0 && last != t->period_ms) {
1383 }
1384 return last;
1385}
1386
1388mainloop_timer_add(const char *name, guint period_ms, bool repeat, GSourceFunc cb, void *userdata)
1389{
1390 mainloop_timer_t *t = calloc(1, sizeof(mainloop_timer_t));
1391
1392 if(t) {
1393 if(name) {
1394 t->name = crm_strdup_printf("%s-%u-%d", name, period_ms, repeat);
1395 } else {
1396 t->name = crm_strdup_printf("%p-%u-%d", t, period_ms, repeat);
1397 }
1398 t->id = 0;
1399 t->period_ms = period_ms;
1400 t->repeat = repeat;
1401 t->cb = cb;
1402 t->userdata = userdata;
1403 crm_trace("Created timer %s with %p %p", t->name, userdata, t->userdata);
1404 }
1405 return t;
1406}
1407
1408void
1410{
1411 if(t) {
1412 crm_trace("Destroying timer %s", t->name);
1414 free(t->name);
1415 free(t);
1416 }
1417}
1418
1419/*
1420 * Helpers to make sure certain events aren't lost at shutdown
1421 */
1422
1423static gboolean
1424drain_timeout_cb(gpointer user_data)
1425{
1426 bool *timeout_popped = (bool*) user_data;
1427
1428 *timeout_popped = TRUE;
1429 return FALSE;
1430}
1431
1438void
1439pcmk_quit_main_loop(GMainLoop *mloop, unsigned int n)
1440{
1441 if ((mloop != NULL) && g_main_loop_is_running(mloop)) {
1442 GMainContext *ctx = g_main_loop_get_context(mloop);
1443
1444 /* Drain up to n events in case some memory clean-up is pending
1445 * (helpful to reduce noise in valgrind output).
1446 */
1447 for (int i = 0; (i < n) && g_main_context_pending(ctx); ++i) {
1448 g_main_context_dispatch(ctx);
1449 }
1450 g_main_loop_quit(mloop);
1451 }
1452}
1453
1466void
1467pcmk_drain_main_loop(GMainLoop *mloop, guint timer_ms, bool (*check)(guint))
1468{
1469 bool timeout_popped = FALSE;
1470 guint timer = 0;
1471 GMainContext *ctx = NULL;
1472
1473 CRM_CHECK(mloop && check, return);
1474
1475 ctx = g_main_loop_get_context(mloop);
1476 if (ctx) {
1477 time_t start_time = time(NULL);
1478
1479 timer = g_timeout_add(timer_ms, drain_timeout_cb, &timeout_popped);
1480 while (!timeout_popped
1481 && check(timer_ms - (time(NULL) - start_time) * 1000)) {
1482 g_main_context_iteration(ctx, TRUE);
1483 }
1484 }
1485 if (!timeout_popped && (timer > 0)) {
1486 g_source_remove(timer);
1487 }
1488}
1489
1490// Deprecated functions kept only for backward API compatibility
1491
1493
1494gboolean
1495crm_signal(int sig, void (*dispatch) (int sig))
1496{
1497 return crm_signal_handler(sig, dispatch) != SIG_ERR;
1498}
1499
1500// End deprecated API
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:875
const char * crm_ipc_name(crm_ipc_t *client)
long crm_ipc_read(crm_ipc_t *client)
int crm_ipc_get_fd(crm_ipc_t *client)
Definition ipc_client.c:901
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:915
bool crm_ipc_connect(crm_ipc_t *client)
Establish an IPC connection to a Pacemaker component.
Definition ipc_client.c:792
void crm_ipc_close(crm_ipc_t *client)
Definition ipc_client.c:862
const char * crm_ipc_buffer(crm_ipc_t *client)
struct crm_ipc_s crm_ipc_t
Definition ipc.h:162
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:745
#define crm_warn(fmt, args...)
Definition logging.h:351
#define CRM_XS
Definition logging.h:54
#define crm_crit(fmt, args...)
Definition logging.h:349
#define LOG_STDOUT
Definition logging.h:41
#define crm_notice(fmt, args...)
Definition logging.h:352
#define crm_perror(level, fmt, args...)
Send a system error message to both the log and stderr.
Definition logging.h:301
#define CRM_CHECK(expr, failure_action)
Definition logging.h:218
#define crm_debug(fmt, args...)
Definition logging.h:355
#define crm_err(fmt, args...)
Definition logging.h:350
unsigned int crm_log_level
Definition logging.c:45
#define crm_trace(fmt, args...)
Definition logging.h:356
void pcmk_quit_main_loop(GMainLoop *mloop, unsigned int n)
Drain some remaining main loop events then quit it.
Definition mainloop.c:1439
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:1047
pid_t mainloop_child_pid(mainloop_child_t *child)
Definition mainloop.c:1035
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:1372
void * mainloop_child_userdata(mainloop_child_t *child)
Definition mainloop.c:1053
crm_ipc_t * mainloop_get_ipc_client(mainloop_io_t *client)
Definition mainloop.c:966
bool mainloop_timer_running(mainloop_timer_t *t)
Definition mainloop.c:1343
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:1388
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:935
void mainloop_trigger_complete(crm_trigger_t *trig)
Definition mainloop.c:168
void mainloop_timer_del(mainloop_timer_t *t)
Definition mainloop.c:1409
guint pcmk__mainloop_timer_get_period(mainloop_timer_t *timer)
Get period for mainloop timer.
Definition mainloop.c:926
void mainloop_timer_start(mainloop_timer_t *t)
Definition mainloop.c:1352
void mainloop_del_fd(mainloop_io_t *client)
Definition mainloop.c:1019
void mainloop_del_ipc_server(qb_ipcs_service_t *server)
Definition mainloop.c:713
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:1305
gboolean crm_signal(int sig, void(*dispatch)(int sig))
Definition mainloop.c:1495
void mainloop_timer_stop(mainloop_timer_t *t)
Definition mainloop.c:1362
gboolean mainloop_child_kill(pid_t pid)
Definition mainloop.c:1215
void mainloop_del_ipc_client(mainloop_io_t *client)
Definition mainloop.c:960
void mainloop_clear_child_userdata(mainloop_child_t *child)
Definition mainloop.c:1059
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:975
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:1467
const char * mainloop_child_name(mainloop_child_t *child)
Definition mainloop.c:1041
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:1271
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:893
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:33
struct mainloop_timer_s mainloop_timer_t
Definition mainloop.h:34
void(* sighandler_t)(int)
Definition mainloop.h:48
struct mainloop_io_s mainloop_io_t
Definition mainloop.h:32
mainloop_child_flags
Definition mainloop.h:26
@ mainloop_leave_pid_group
Definition mainloop.h:28
struct trigger_s crm_trigger_t
Definition mainloop.h:31
Deprecated Pacemaker mainloop API.
unsigned int timeout
Definition pcmk_fence.c:32
char * name
Definition pcmk_fence.c:31
int rc
Definition pcmk_fence.c:35
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:420
@ pcmk_rc_ok
Definition results.h:142
void(* destroy)(gpointer userdata)
Destroy function for mainloop IPC connection client data.
Definition mainloop.h:90
int(* dispatch)(const char *buffer, ssize_t length, gpointer userdata)
Dispatch function for an IPC connection used as mainloop source.
Definition mainloop.h:83
int(* dispatch)(gpointer userdata)
Dispatch function for mainloop file descriptor with data ready.
Definition mainloop.h:137
void(* destroy)(gpointer userdata)
Destroy function for mainloop file descriptor client data.
Definition mainloop.h:144
Wrappers for and extensions to libxml2.