/src/pacemaker/lib/common/mainloop.c
Line | Count | Source |
1 | | /* |
2 | | * Copyright 2004-2026 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 | | #include <errno.h> // errno, ESRCH, etc. |
13 | | #include <signal.h> // sigaction, NSIG, SIG_ERR |
14 | | #include <stdbool.h> // bool, false, true |
15 | | #include <stdint.h> // int32_t |
16 | | #include <stdio.h> // fprintf, stderr |
17 | | #include <stdlib.h> // free |
18 | | #include <string.h> // memset, strerror, strsignal |
19 | | #include <sys/types.h> // pid_t |
20 | | #include <sys/wait.h> // waitpid, WNOHANG, etc. |
21 | | #include <time.h> // time |
22 | | |
23 | | #include <glib.h> // g_*, G_*, etc. |
24 | | #include <qb/qbarray.h> // qb_array_* |
25 | | #include <qb/qbdefs.h> // QB_FALSE, QB_TRUE |
26 | | #include <qb/qbipc_common.h> // qb_ipc_type |
27 | | #include <qb/qbipcs.h> // qb_ipcs_* |
28 | | #include <qb/qblog.h> // QB_XS |
29 | | #include <qb/qbloop.h> // qb_loop_priority |
30 | | |
31 | | #include <crm/common/ipc.h> // crm_ipc_* |
32 | | #include <crm/common/logging.h> // crm_log_level, CRM_CHECK |
33 | | #include <crm/common/mainloop.h> // mainloop_* |
34 | | #include <crm/common/results.h> // pcmk_rc_*, pcmk_strerror |
35 | | |
36 | | struct trigger_s { |
37 | | GSource source; |
38 | | bool running; |
39 | | bool trigger; |
40 | | void *user_data; |
41 | | unsigned int id; |
42 | | }; |
43 | | |
44 | | static GList *child_list = NULL; |
45 | | static qb_array_t *gio_map = NULL; |
46 | | |
47 | | static gboolean |
48 | | crm_trigger_prepare(GSource *source, int *timeout) |
49 | 0 | { |
50 | 0 | crm_trigger_t *trig = (crm_trigger_t *) source; |
51 | | |
52 | | /* cluster-glue's FD and IPC related sources make use of |
53 | | * g_source_add_poll() but do not set a timeout in their prepare |
54 | | * functions |
55 | | * |
56 | | * This means mainloop's poll() will block until an event for one |
57 | | * of these sources occurs - any /other/ type of source, such as |
58 | | * this one or g_idle_*, that doesn't use g_source_add_poll() is |
59 | | * S-O-L and won't be processed until there is something fd-based |
60 | | * happens. |
61 | | * |
62 | | * Luckily the timeout we can set here affects all sources and |
63 | | * puts an upper limit on how long poll() can take. |
64 | | * |
65 | | * So unconditionally set a small-ish timeout, not too small that |
66 | | * we're in constant motion, which will act as an upper bound on |
67 | | * how long the signal handling might be delayed for. |
68 | | */ |
69 | 0 | *timeout = 500; /* Timeout in ms */ |
70 | |
|
71 | 0 | return trig->trigger; |
72 | 0 | } |
73 | | |
74 | | static gboolean |
75 | | crm_trigger_check(GSource * source) |
76 | 0 | { |
77 | 0 | crm_trigger_t *trig = (crm_trigger_t *) source; |
78 | |
|
79 | 0 | return trig->trigger; |
80 | 0 | } |
81 | | |
82 | | /*! |
83 | | * \internal |
84 | | * \brief GSource dispatch function for crm_trigger_t |
85 | | * |
86 | | * \param[in] source crm_trigger_t being dispatched |
87 | | * \param[in] callback Callback passed at source creation |
88 | | * \param[in,out] userdata User data passed at source creation |
89 | | * |
90 | | * \return G_SOURCE_REMOVE to remove source, G_SOURCE_CONTINUE to keep it |
91 | | */ |
92 | | static gboolean |
93 | | crm_trigger_dispatch(GSource *source, GSourceFunc callback, void *userdata) |
94 | 0 | { |
95 | 0 | gboolean rc = G_SOURCE_CONTINUE; |
96 | 0 | crm_trigger_t *trig = (crm_trigger_t *) source; |
97 | |
|
98 | 0 | if (trig->running) { |
99 | | /* Wait until the existing job is complete before starting the next one */ |
100 | 0 | return G_SOURCE_CONTINUE; |
101 | 0 | } |
102 | 0 | trig->trigger = false; |
103 | |
|
104 | 0 | if (callback) { |
105 | 0 | int callback_rc = callback(trig->user_data); |
106 | |
|
107 | 0 | if (callback_rc < 0) { |
108 | 0 | pcmk__trace("Trigger handler %p not yet complete", trig); |
109 | 0 | trig->running = true; |
110 | 0 | } else if (callback_rc == 0) { |
111 | 0 | rc = G_SOURCE_REMOVE; |
112 | 0 | } |
113 | 0 | } |
114 | 0 | return rc; |
115 | 0 | } |
116 | | |
117 | | static void |
118 | | crm_trigger_finalize(GSource * source) |
119 | 0 | { |
120 | 0 | pcmk__trace("Trigger %p destroyed", source); |
121 | 0 | } |
122 | | |
123 | | static GSourceFuncs crm_trigger_funcs = { |
124 | | crm_trigger_prepare, |
125 | | crm_trigger_check, |
126 | | crm_trigger_dispatch, |
127 | | crm_trigger_finalize, |
128 | | }; |
129 | | |
130 | | static crm_trigger_t * |
131 | | mainloop_setup_trigger(GSource * source, int priority, |
132 | | int (*dispatch)(void *user_data), void *userdata) |
133 | 0 | { |
134 | 0 | crm_trigger_t *trigger = NULL; |
135 | |
|
136 | 0 | trigger = (crm_trigger_t *) source; |
137 | |
|
138 | 0 | trigger->id = 0; |
139 | 0 | trigger->trigger = false; |
140 | 0 | trigger->user_data = userdata; |
141 | |
|
142 | 0 | if (dispatch) { |
143 | 0 | g_source_set_callback(source, dispatch, trigger, NULL); |
144 | 0 | } |
145 | |
|
146 | 0 | g_source_set_priority(source, priority); |
147 | 0 | g_source_set_can_recurse(source, FALSE); |
148 | |
|
149 | 0 | trigger->id = g_source_attach(source, NULL); |
150 | 0 | return trigger; |
151 | 0 | } |
152 | | |
153 | | void |
154 | | mainloop_trigger_complete(crm_trigger_t * trig) |
155 | 0 | { |
156 | 0 | pcmk__trace("Trigger handler %p complete", trig); |
157 | 0 | trig->running = false; |
158 | 0 | } |
159 | | |
160 | | /*! |
161 | | * \brief Create a trigger to be used as a mainloop source |
162 | | * |
163 | | * \param[in] priority Relative priority of source (lower number is higher priority) |
164 | | * \param[in] dispatch Trigger dispatch function (should return 0 to remove the |
165 | | * trigger from the mainloop, -1 if the trigger should be |
166 | | * kept but the job is still running and not complete, and |
167 | | * 1 if the trigger should be kept and the job is complete) |
168 | | * \param[in] userdata Pointer to pass to \p dispatch |
169 | | * |
170 | | * \return Newly allocated mainloop source for trigger |
171 | | */ |
172 | | crm_trigger_t * |
173 | | mainloop_add_trigger(int priority, int (*dispatch) (void *user_data), |
174 | | void *userdata) |
175 | 0 | { |
176 | 0 | GSource *source = NULL; |
177 | |
|
178 | 0 | pcmk__assert(sizeof(crm_trigger_t) > sizeof(GSource)); |
179 | 0 | source = g_source_new(&crm_trigger_funcs, sizeof(crm_trigger_t)); |
180 | |
|
181 | 0 | return mainloop_setup_trigger(source, priority, dispatch, userdata); |
182 | 0 | } |
183 | | |
184 | | void |
185 | | mainloop_set_trigger(crm_trigger_t * source) |
186 | 0 | { |
187 | 0 | if(source) { |
188 | 0 | source->trigger = true; |
189 | 0 | } |
190 | 0 | } |
191 | | |
192 | | gboolean |
193 | | mainloop_destroy_trigger(crm_trigger_t * source) |
194 | 0 | { |
195 | 0 | GSource *gs = NULL; |
196 | |
|
197 | 0 | if(source == NULL) { |
198 | 0 | return TRUE; |
199 | 0 | } |
200 | | |
201 | 0 | gs = (GSource *)source; |
202 | |
|
203 | 0 | g_source_destroy(gs); /* Remove from mainloop, ref_count-- */ |
204 | 0 | g_source_unref(gs); /* The caller no longer carries a reference to source |
205 | | * |
206 | | * At this point the source should be free'd, |
207 | | * unless we're currently processing said |
208 | | * source, in which case mainloop holds an |
209 | | * additional reference and it will be free'd |
210 | | * once our processing completes |
211 | | */ |
212 | 0 | return TRUE; |
213 | 0 | } |
214 | | |
215 | | // Define a custom glib source for signal handling |
216 | | |
217 | | // Data structure for custom glib source |
218 | | typedef struct { |
219 | | crm_trigger_t trigger; // trigger that invoked source (must be first) |
220 | | void (*handler) (int sig); // signal handler |
221 | | int signal; // signal that was received |
222 | | } crm_signal_t; |
223 | | |
224 | | // Table to associate signal handlers with signal numbers |
225 | | static crm_signal_t *crm_signals[NSIG]; |
226 | | |
227 | | /*! |
228 | | * \internal |
229 | | * \brief Dispatch an event from custom glib source for signals |
230 | | * |
231 | | * Given an signal event, clear the event trigger and call any registered |
232 | | * signal handler. |
233 | | * |
234 | | * \param[in] source glib source that triggered this dispatch |
235 | | * \param[in] callback (ignored) |
236 | | * \param[in] userdata (ignored) |
237 | | */ |
238 | | static gboolean |
239 | | crm_signal_dispatch(GSource *source, GSourceFunc callback, void *userdata) |
240 | 0 | { |
241 | 0 | crm_signal_t *sig = (crm_signal_t *) source; |
242 | |
|
243 | 0 | if(sig->signal != SIGCHLD) { |
244 | 0 | pcmk__notice("Caught '%s' signal " QB_XS " %d (%s handler)", |
245 | 0 | strsignal(sig->signal), sig->signal, |
246 | 0 | ((sig->handler != NULL)? "invoking" : "no")); |
247 | 0 | } |
248 | |
|
249 | 0 | sig->trigger.trigger = false; |
250 | 0 | if (sig->handler) { |
251 | 0 | sig->handler(sig->signal); |
252 | 0 | } |
253 | 0 | return TRUE; |
254 | 0 | } |
255 | | |
256 | | /*! |
257 | | * \internal |
258 | | * \brief Handle a signal by setting a trigger for signal source |
259 | | * |
260 | | * \param[in] sig Signal number that was received |
261 | | * |
262 | | * \note This is the true signal handler for the mainloop signal source, and |
263 | | * must be async-safe. |
264 | | */ |
265 | | static void |
266 | | mainloop_signal_handler(int sig) |
267 | 0 | { |
268 | 0 | if (sig > 0 && sig < NSIG && crm_signals[sig] != NULL) { |
269 | 0 | mainloop_set_trigger((crm_trigger_t *) crm_signals[sig]); |
270 | 0 | } |
271 | 0 | } |
272 | | |
273 | | // Functions implementing our custom glib source for signal handling |
274 | | static GSourceFuncs crm_signal_funcs = { |
275 | | crm_trigger_prepare, |
276 | | crm_trigger_check, |
277 | | crm_signal_dispatch, |
278 | | crm_trigger_finalize, |
279 | | }; |
280 | | |
281 | | /*! |
282 | | * \internal |
283 | | * \brief Set a true signal handler |
284 | | * |
285 | | * signal()-like interface to sigaction() |
286 | | * |
287 | | * \param[in] sig Signal number to register handler for |
288 | | * \param[in] dispatch Signal handler |
289 | | * |
290 | | * \return The previous value of the signal handler, or SIG_ERR on error |
291 | | * \note The dispatch function must be async-safe. |
292 | | */ |
293 | | sighandler_t |
294 | | crm_signal_handler(int sig, sighandler_t dispatch) |
295 | 0 | { |
296 | 0 | sigset_t mask; |
297 | 0 | struct sigaction sa; |
298 | 0 | struct sigaction old; |
299 | |
|
300 | 0 | if (sigemptyset(&mask) < 0) { |
301 | 0 | pcmk__err("Could not %sset handler for signal %d: %s", |
302 | 0 | ((dispatch == NULL)? "un" : ""), sig, strerror(errno)); |
303 | 0 | return SIG_ERR; |
304 | 0 | } |
305 | | |
306 | 0 | memset(&sa, 0, sizeof(struct sigaction)); |
307 | 0 | sa.sa_handler = dispatch; |
308 | 0 | sa.sa_flags = SA_RESTART; |
309 | 0 | sa.sa_mask = mask; |
310 | |
|
311 | 0 | if (sigaction(sig, &sa, &old) < 0) { |
312 | 0 | pcmk__err("Could not %sset handler for signal %d: %s", |
313 | 0 | ((dispatch == NULL)? "un" : ""), sig, strerror(errno)); |
314 | 0 | return SIG_ERR; |
315 | 0 | } |
316 | 0 | return old.sa_handler; |
317 | 0 | } |
318 | | |
319 | | static void |
320 | | mainloop_destroy_signal_entry(int sig) |
321 | 0 | { |
322 | 0 | crm_signal_t *tmp = crm_signals[sig]; |
323 | |
|
324 | 0 | if (tmp != NULL) { |
325 | 0 | crm_signals[sig] = NULL; |
326 | 0 | pcmk__trace("Unregistering mainloop handler for signal %d", sig); |
327 | 0 | mainloop_destroy_trigger((crm_trigger_t *) tmp); |
328 | 0 | } |
329 | 0 | } |
330 | | |
331 | | /*! |
332 | | * \internal |
333 | | * \brief Add a signal handler to a mainloop |
334 | | * |
335 | | * \param[in] sig Signal number to handle |
336 | | * \param[in] dispatch Signal handler function (\c NULL to ignore the signal) |
337 | | * |
338 | | * \note The true signal handler merely sets a mainloop trigger to call this |
339 | | * dispatch function via the mainloop. Therefore, the dispatch function |
340 | | * does not need to be async-safe. |
341 | | * \note The added signal handler gets freed by \c mainloop_cleanup() if it is |
342 | | * not freed manually using \c mainloop_destroy_signal(). |
343 | | */ |
344 | | gboolean |
345 | | mainloop_add_signal(int sig, void (*dispatch) (int sig)) |
346 | 0 | { |
347 | 0 | GSource *source = NULL; |
348 | 0 | int priority = G_PRIORITY_HIGH - 1; |
349 | |
|
350 | 0 | if (sig == SIGTERM) { |
351 | | /* TERM is higher priority than other signals, |
352 | | * signals are higher priority than other ipc. |
353 | | * Yes, minus: smaller is "higher" |
354 | | */ |
355 | 0 | priority--; |
356 | 0 | } |
357 | |
|
358 | 0 | if (sig >= NSIG || sig < 0) { |
359 | 0 | pcmk__err("Signal %d is out of range", sig); |
360 | 0 | return FALSE; |
361 | |
|
362 | 0 | } else if (crm_signals[sig] != NULL && crm_signals[sig]->handler == dispatch) { |
363 | 0 | pcmk__trace("Signal handler for %d is already installed", sig); |
364 | 0 | return TRUE; |
365 | |
|
366 | 0 | } else if (crm_signals[sig] != NULL) { |
367 | 0 | pcmk__err("Different signal handler for %d is already installed", sig); |
368 | 0 | return FALSE; |
369 | 0 | } |
370 | | |
371 | 0 | pcmk__assert(sizeof(crm_signal_t) > sizeof(GSource)); |
372 | 0 | source = g_source_new(&crm_signal_funcs, sizeof(crm_signal_t)); |
373 | |
|
374 | 0 | crm_signals[sig] = (crm_signal_t *) mainloop_setup_trigger(source, priority, NULL, NULL); |
375 | 0 | pcmk__assert(crm_signals[sig] != NULL); |
376 | |
|
377 | 0 | crm_signals[sig]->handler = dispatch; |
378 | 0 | crm_signals[sig]->signal = sig; |
379 | |
|
380 | 0 | if (crm_signal_handler(sig, mainloop_signal_handler) == SIG_ERR) { |
381 | 0 | mainloop_destroy_signal_entry(sig); |
382 | 0 | return FALSE; |
383 | 0 | } |
384 | | |
385 | 0 | return TRUE; |
386 | 0 | } |
387 | | |
388 | | gboolean |
389 | | mainloop_destroy_signal(int sig) |
390 | 0 | { |
391 | 0 | if (sig >= NSIG || sig < 0) { |
392 | 0 | pcmk__err("Signal %d is out of range", sig); |
393 | 0 | return FALSE; |
394 | |
|
395 | 0 | } else if (crm_signal_handler(sig, NULL) == SIG_ERR) { |
396 | | // Error already logged |
397 | 0 | return FALSE; |
398 | |
|
399 | 0 | } else if (crm_signals[sig] == NULL) { |
400 | 0 | return TRUE; |
401 | 0 | } |
402 | 0 | mainloop_destroy_signal_entry(sig); |
403 | 0 | return TRUE; |
404 | 0 | } |
405 | | |
406 | | /* |
407 | | * libqb... |
408 | | */ |
409 | | struct gio_to_qb_poll { |
410 | | int32_t is_used; |
411 | | unsigned int source; |
412 | | int32_t events; |
413 | | void *data; |
414 | | qb_ipcs_dispatch_fn_t fn; |
415 | | enum qb_loop_priority p; |
416 | | }; |
417 | | |
418 | | static gboolean |
419 | | gio_read_socket(GIOChannel * gio, GIOCondition condition, void *data) |
420 | 0 | { |
421 | 0 | struct gio_to_qb_poll *adaptor = (struct gio_to_qb_poll *)data; |
422 | 0 | int fd = g_io_channel_unix_get_fd(gio); |
423 | |
|
424 | 0 | pcmk__trace("%p.%d %d", data, fd, condition); |
425 | | |
426 | | /* if this assert get's hit, then there is a race condition between |
427 | | * when we destroy a fd and when mainloop actually gives it up */ |
428 | 0 | pcmk__assert(adaptor->is_used > 0); |
429 | |
|
430 | 0 | return (adaptor->fn(fd, condition, adaptor->data) == 0); |
431 | 0 | } |
432 | | |
433 | | static void |
434 | | gio_poll_destroy(void *data) |
435 | 0 | { |
436 | 0 | struct gio_to_qb_poll *adaptor = (struct gio_to_qb_poll *)data; |
437 | |
|
438 | 0 | adaptor->is_used--; |
439 | 0 | pcmk__assert(adaptor->is_used >= 0); |
440 | |
|
441 | 0 | if (adaptor->is_used == 0) { |
442 | 0 | pcmk__trace("Marking adaptor %p unused", adaptor); |
443 | 0 | adaptor->source = 0; |
444 | 0 | } |
445 | 0 | } |
446 | | |
447 | | /*! |
448 | | * \internal |
449 | | * \brief Convert libqb's poll priority into GLib's one |
450 | | * |
451 | | * \param[in] prio libqb's poll priority (#QB_LOOP_MED assumed as fallback) |
452 | | * |
453 | | * \return best matching GLib's priority |
454 | | */ |
455 | | static int |
456 | | conv_prio_libqb2glib(enum qb_loop_priority prio) |
457 | 0 | { |
458 | 0 | switch (prio) { |
459 | 0 | case QB_LOOP_LOW: return G_PRIORITY_LOW; |
460 | 0 | case QB_LOOP_HIGH: return G_PRIORITY_HIGH; |
461 | 0 | default: return G_PRIORITY_DEFAULT; // QB_LOOP_MED |
462 | 0 | } |
463 | 0 | } |
464 | | |
465 | | /*! |
466 | | * \internal |
467 | | * \brief Convert libqb's poll priority to rate limiting spec |
468 | | * |
469 | | * \param[in] prio libqb's poll priority (#QB_LOOP_MED assumed as fallback) |
470 | | * |
471 | | * \return best matching rate limiting spec |
472 | | * \note This is the inverse of libqb's qb_ipcs_request_rate_limit(). |
473 | | */ |
474 | | static enum qb_ipcs_rate_limit |
475 | | conv_libqb_prio2ratelimit(enum qb_loop_priority prio) |
476 | 0 | { |
477 | 0 | switch (prio) { |
478 | 0 | case QB_LOOP_LOW: return QB_IPCS_RATE_SLOW; |
479 | 0 | case QB_LOOP_HIGH: return QB_IPCS_RATE_FAST; |
480 | 0 | default: return QB_IPCS_RATE_NORMAL; // QB_LOOP_MED |
481 | 0 | } |
482 | 0 | } |
483 | | |
484 | | static int32_t |
485 | | gio_poll_dispatch_update(enum qb_loop_priority p, int32_t fd, int32_t evts, |
486 | | void *data, qb_ipcs_dispatch_fn_t fn, int32_t add) |
487 | 0 | { |
488 | 0 | struct gio_to_qb_poll *adaptor; |
489 | 0 | GIOChannel *channel; |
490 | 0 | int32_t res = 0; |
491 | |
|
492 | 0 | res = qb_array_index(gio_map, fd, (void **)&adaptor); |
493 | 0 | if (res < 0) { |
494 | 0 | pcmk__err("Array lookup failed for fd=%d: %d", fd, res); |
495 | 0 | return res; |
496 | 0 | } |
497 | | |
498 | 0 | pcmk__trace("Adding fd=%d to mainloop as adaptor %p", fd, adaptor); |
499 | | |
500 | 0 | if (add && adaptor->source) { |
501 | 0 | pcmk__err("Adaptor for descriptor %d is still in-use", fd); |
502 | 0 | return -EEXIST; |
503 | 0 | } |
504 | 0 | if (!add && !adaptor->is_used) { |
505 | 0 | pcmk__err("Adaptor for descriptor %d is not in-use", fd); |
506 | 0 | return -ENOENT; |
507 | 0 | } |
508 | | |
509 | | /* channel is created with ref_count = 1 */ |
510 | 0 | channel = g_io_channel_unix_new(fd); |
511 | 0 | if (!channel) { |
512 | 0 | pcmk__err("No memory left to add fd=%d", fd); |
513 | 0 | return -ENOMEM; |
514 | 0 | } |
515 | | |
516 | 0 | if (adaptor->source) { |
517 | 0 | g_source_remove(adaptor->source); |
518 | 0 | adaptor->source = 0; |
519 | 0 | } |
520 | | |
521 | | /* Because unlike the poll() API, glib doesn't tell us about HUPs by default */ |
522 | 0 | evts |= (G_IO_HUP | G_IO_NVAL | G_IO_ERR); |
523 | |
|
524 | 0 | adaptor->fn = fn; |
525 | 0 | adaptor->events = evts; |
526 | 0 | adaptor->data = data; |
527 | 0 | adaptor->p = p; |
528 | 0 | adaptor->is_used++; |
529 | 0 | adaptor->source = |
530 | 0 | g_io_add_watch_full(channel, conv_prio_libqb2glib(p), evts, |
531 | 0 | gio_read_socket, adaptor, gio_poll_destroy); |
532 | | |
533 | | /* Now that mainloop now holds a reference to channel, |
534 | | * thanks to g_io_add_watch_full(), drop ours from g_io_channel_unix_new(). |
535 | | * |
536 | | * This means that channel will be free'd by: |
537 | | * g_main_context_dispatch() |
538 | | * -> g_source_destroy_internal() |
539 | | * -> g_source_callback_unref() |
540 | | * shortly after gio_poll_destroy() completes |
541 | | */ |
542 | 0 | g_io_channel_unref(channel); |
543 | |
|
544 | 0 | pcmk__trace("Added to mainloop with gsource id=%d", adaptor->source); |
545 | 0 | if (adaptor->source > 0) { |
546 | 0 | return 0; |
547 | 0 | } |
548 | | |
549 | 0 | return -EINVAL; |
550 | 0 | } |
551 | | |
552 | | static int32_t |
553 | | gio_poll_dispatch_add(enum qb_loop_priority p, int32_t fd, int32_t evts, |
554 | | void *data, qb_ipcs_dispatch_fn_t fn) |
555 | 0 | { |
556 | 0 | return gio_poll_dispatch_update(p, fd, evts, data, fn, QB_TRUE); |
557 | 0 | } |
558 | | |
559 | | static int32_t |
560 | | gio_poll_dispatch_mod(enum qb_loop_priority p, int32_t fd, int32_t evts, |
561 | | void *data, qb_ipcs_dispatch_fn_t fn) |
562 | 0 | { |
563 | 0 | return gio_poll_dispatch_update(p, fd, evts, data, fn, QB_FALSE); |
564 | 0 | } |
565 | | |
566 | | static int32_t |
567 | | gio_poll_dispatch_del(int32_t fd) |
568 | 0 | { |
569 | 0 | struct gio_to_qb_poll *adaptor; |
570 | |
|
571 | 0 | pcmk__trace("Looking for fd=%d", fd); |
572 | 0 | if (qb_array_index(gio_map, fd, (void **)&adaptor) == 0) { |
573 | 0 | if (adaptor->source) { |
574 | 0 | g_source_remove(adaptor->source); |
575 | 0 | adaptor->source = 0; |
576 | 0 | } |
577 | 0 | } |
578 | 0 | return 0; |
579 | 0 | } |
580 | | |
581 | | struct qb_ipcs_poll_handlers gio_poll_funcs = { |
582 | | .job_add = NULL, |
583 | | .dispatch_add = gio_poll_dispatch_add, |
584 | | .dispatch_mod = gio_poll_dispatch_mod, |
585 | | .dispatch_del = gio_poll_dispatch_del, |
586 | | }; |
587 | | |
588 | | qb_ipcs_service_t * |
589 | | mainloop_add_ipc_server(const char *name, enum qb_ipc_type type, |
590 | | struct qb_ipcs_service_handlers *callbacks) |
591 | 0 | { |
592 | 0 | return mainloop_add_ipc_server_with_prio(name, type, callbacks, QB_LOOP_MED); |
593 | 0 | } |
594 | | |
595 | | qb_ipcs_service_t * |
596 | | mainloop_add_ipc_server_with_prio(const char *name, enum qb_ipc_type type, |
597 | | struct qb_ipcs_service_handlers *callbacks, |
598 | | enum qb_loop_priority prio) |
599 | 0 | { |
600 | 0 | int rc = 0; |
601 | 0 | qb_ipcs_service_t *server = NULL; |
602 | |
|
603 | 0 | if (gio_map == NULL) { |
604 | 0 | gio_map = qb_array_create_2(64, sizeof(struct gio_to_qb_poll), 1); |
605 | 0 | } |
606 | |
|
607 | 0 | server = qb_ipcs_create(name, 0, QB_IPC_SHM, callbacks); |
608 | |
|
609 | 0 | if (server == NULL) { |
610 | 0 | pcmk__err("Could not create %s IPC server: %s (%d)", name, |
611 | 0 | pcmk_rc_str(errno), errno); |
612 | 0 | return NULL; |
613 | 0 | } |
614 | | |
615 | 0 | if (prio != QB_LOOP_MED) { |
616 | 0 | qb_ipcs_request_rate_limit(server, conv_libqb_prio2ratelimit(prio)); |
617 | 0 | } |
618 | | |
619 | | // Enforce a minimum IPC buffer size on all clients |
620 | 0 | qb_ipcs_enforce_buffer_size(server, crm_ipc_default_buffer_size()); |
621 | 0 | qb_ipcs_poll_handlers_set(server, &gio_poll_funcs); |
622 | |
|
623 | 0 | rc = qb_ipcs_run(server); |
624 | 0 | if (rc < 0) { |
625 | 0 | pcmk__err("Could not start %s IPC server: %s (%d)", name, |
626 | 0 | pcmk_strerror(rc), rc); |
627 | 0 | return NULL; // qb_ipcs_run() destroys server on failure |
628 | 0 | } |
629 | | |
630 | 0 | return server; |
631 | 0 | } |
632 | | |
633 | | void |
634 | | mainloop_del_ipc_server(qb_ipcs_service_t * server) |
635 | 0 | { |
636 | 0 | if (server) { |
637 | 0 | qb_ipcs_destroy(server); |
638 | 0 | } |
639 | 0 | } |
640 | | |
641 | | /*! |
642 | | * \internal |
643 | | * \brief I/O watch callback function (GIOFunc) |
644 | | * |
645 | | * \param[in] gio I/O channel being watched |
646 | | * \param[in] condition I/O condition satisfied |
647 | | * \param[in] data User data passed when source was created |
648 | | * |
649 | | * \return G_SOURCE_REMOVE to remove source, G_SOURCE_CONTINUE to keep it |
650 | | */ |
651 | | static gboolean |
652 | | mainloop_gio_callback(GIOChannel *gio, GIOCondition condition, void *data) |
653 | 0 | { |
654 | 0 | gboolean rc = G_SOURCE_CONTINUE; |
655 | 0 | mainloop_io_t *client = data; |
656 | |
|
657 | 0 | pcmk__assert(client->fd == g_io_channel_unix_get_fd(gio)); |
658 | |
|
659 | 0 | if (condition & G_IO_IN) { |
660 | 0 | if (client->ipc) { |
661 | 0 | long read_rc = 0L; |
662 | 0 | int max = 10; |
663 | |
|
664 | 0 | do { |
665 | 0 | read_rc = crm_ipc_read(client->ipc); |
666 | 0 | if (read_rc <= 0) { |
667 | 0 | pcmk__trace("Could not read IPC message from %s: %s (%ld)", |
668 | 0 | client->name, pcmk_strerror(read_rc), read_rc); |
669 | | |
670 | 0 | if (read_rc == -EAGAIN) { |
671 | 0 | continue; |
672 | 0 | } |
673 | |
|
674 | 0 | } else if (client->dispatch_fn_ipc) { |
675 | 0 | const char *buffer = crm_ipc_buffer(client->ipc); |
676 | |
|
677 | 0 | pcmk__trace("New %ld-byte IPC message from %s after I/O " |
678 | 0 | "condition %d", |
679 | 0 | read_rc, client->name, (int) condition); |
680 | 0 | if (client->dispatch_fn_ipc(buffer, read_rc, client->userdata) < 0) { |
681 | 0 | pcmk__trace("Connection to %s no longer required", |
682 | 0 | client->name); |
683 | 0 | rc = G_SOURCE_REMOVE; |
684 | 0 | } |
685 | 0 | } |
686 | | |
687 | 0 | pcmk__ipc_free_client_buffer(client->ipc); |
688 | |
|
689 | 0 | } while ((rc == G_SOURCE_CONTINUE) && (--max > 0) |
690 | 0 | && ((read_rc > 0) || (read_rc == -EAGAIN))); |
691 | |
|
692 | 0 | } else { |
693 | 0 | pcmk__trace("New I/O event for %s after I/O condition %d", |
694 | 0 | client->name, (int) condition); |
695 | 0 | if (client->dispatch_fn_io) { |
696 | 0 | if (client->dispatch_fn_io(client->userdata) < 0) { |
697 | 0 | pcmk__trace("Connection to %s no longer required", |
698 | 0 | client->name); |
699 | 0 | rc = G_SOURCE_REMOVE; |
700 | 0 | } |
701 | 0 | } |
702 | 0 | } |
703 | 0 | } |
704 | | |
705 | 0 | if (client->ipc && !crm_ipc_connected(client->ipc)) { |
706 | 0 | pcmk__err("Connection to %s closed " QB_XS " client=%p condition=%d", |
707 | 0 | client->name, client, condition); |
708 | 0 | rc = G_SOURCE_REMOVE; |
709 | |
|
710 | 0 | } else if (condition & (G_IO_HUP | G_IO_NVAL | G_IO_ERR)) { |
711 | 0 | pcmk__trace("The connection %s[%p] has been closed (I/O condition=%d)", |
712 | 0 | client->name, client, condition); |
713 | 0 | rc = G_SOURCE_REMOVE; |
714 | |
|
715 | 0 | } else if ((condition & G_IO_IN) == 0) { |
716 | | /* |
717 | | #define GLIB_SYSDEF_POLLIN =1 |
718 | | #define GLIB_SYSDEF_POLLPRI =2 |
719 | | #define GLIB_SYSDEF_POLLOUT =4 |
720 | | #define GLIB_SYSDEF_POLLERR =8 |
721 | | #define GLIB_SYSDEF_POLLHUP =16 |
722 | | #define GLIB_SYSDEF_POLLNVAL =32 |
723 | | |
724 | | typedef enum |
725 | | { |
726 | | G_IO_IN GLIB_SYSDEF_POLLIN, |
727 | | G_IO_OUT GLIB_SYSDEF_POLLOUT, |
728 | | G_IO_PRI GLIB_SYSDEF_POLLPRI, |
729 | | G_IO_ERR GLIB_SYSDEF_POLLERR, |
730 | | G_IO_HUP GLIB_SYSDEF_POLLHUP, |
731 | | G_IO_NVAL GLIB_SYSDEF_POLLNVAL |
732 | | } GIOCondition; |
733 | | |
734 | | A bitwise combination representing a condition to watch for on an event source. |
735 | | |
736 | | G_IO_IN There is data to read. |
737 | | G_IO_OUT Data can be written (without blocking). |
738 | | G_IO_PRI There is urgent data to read. |
739 | | G_IO_ERR Error condition. |
740 | | G_IO_HUP Hung up (the connection has been broken, usually for pipes and sockets). |
741 | | G_IO_NVAL Invalid request. The file descriptor is not open. |
742 | | */ |
743 | 0 | pcmk__err("Strange condition: %d", condition); |
744 | 0 | } |
745 | | |
746 | | /* G_SOURCE_REMOVE results in mainloop_gio_destroy() being called |
747 | | * just before the source is removed from mainloop |
748 | | */ |
749 | 0 | return rc; |
750 | 0 | } |
751 | | |
752 | | static void |
753 | | mainloop_gio_destroy(void *c) |
754 | 0 | { |
755 | 0 | mainloop_io_t *client = c; |
756 | | |
757 | | /* client->source is valid but about to be destroyed (ref_count == 0) in |
758 | | * gmain.c. client->channel will still have ref_count > 0 (should be == 1). |
759 | | */ |
760 | 0 | pcmk__trace("Destroying client %s[%p]", client->name, client); |
761 | | |
762 | | // @TODO Would it be safe to call this immediately before crm_ipc_destroy()? |
763 | 0 | crm_ipc_close(client->ipc); |
764 | |
|
765 | 0 | if (client->destroy_fn != NULL) { |
766 | 0 | client->destroy_fn(client->userdata); |
767 | 0 | } |
768 | |
|
769 | 0 | crm_ipc_destroy(client->ipc); |
770 | |
|
771 | 0 | pcmk__trace("Destroyed client %s[%p]", client->name, client); |
772 | | |
773 | | /* This is the destructor corresponding to mainloop_add_fd(). There, |
774 | | * g_io_channel_unix_new() created client->channel and added a reference to |
775 | | * it, so we drop a reference here. The channel will be freed when the |
776 | | * reference count drops to zero. |
777 | | */ |
778 | 0 | g_io_channel_unref(client->channel); |
779 | |
|
780 | 0 | free(client->name); |
781 | 0 | free(client); |
782 | 0 | } |
783 | | |
784 | | /*! |
785 | | * \brief Connect to IPC and add it as a main loop source |
786 | | * |
787 | | * \param[in,out] ipc IPC connection to add |
788 | | * \param[in] priority Event source priority to use for connection |
789 | | * \param[in] userdata Data to register with callbacks |
790 | | * \param[in] callbacks Dispatch and destroy callbacks for connection |
791 | | * \param[out] source Newly allocated event source |
792 | | * |
793 | | * \return Standard Pacemaker return code |
794 | | * |
795 | | * \note On failure, the caller is still responsible for ipc. On success, the |
796 | | * caller should call mainloop_del_ipc_client() when source is no longer |
797 | | * needed, which will lead to the disconnection of the IPC later in the |
798 | | * main loop if it is connected. However the IPC disconnects, |
799 | | * mainloop_gio_destroy() will free ipc and source after calling the |
800 | | * destroy callback. |
801 | | */ |
802 | | int |
803 | | pcmk__add_mainloop_ipc(crm_ipc_t *ipc, int priority, void *userdata, |
804 | | const struct ipc_client_callbacks *callbacks, |
805 | | mainloop_io_t **source) |
806 | 0 | { |
807 | 0 | int rc = pcmk_rc_ok; |
808 | 0 | int fd = -1; |
809 | 0 | const char *ipc_name = NULL; |
810 | |
|
811 | 0 | CRM_CHECK((ipc != NULL) && (callbacks != NULL), return EINVAL); |
812 | | |
813 | 0 | ipc_name = pcmk__s(crm_ipc_name(ipc), "Pacemaker"); |
814 | 0 | rc = pcmk__connect_generic_ipc(ipc); |
815 | 0 | if (rc != pcmk_rc_ok) { |
816 | 0 | pcmk__debug("Connection to %s failed: %s", ipc_name, pcmk_rc_str(rc)); |
817 | 0 | return rc; |
818 | 0 | } |
819 | | |
820 | 0 | rc = pcmk__ipc_fd(ipc, &fd); |
821 | 0 | if (rc != pcmk_rc_ok) { |
822 | 0 | pcmk__debug("Could not obtain file descriptor for %s IPC: %s", ipc_name, |
823 | 0 | pcmk_rc_str(rc)); |
824 | 0 | crm_ipc_close(ipc); |
825 | 0 | return rc; |
826 | 0 | } |
827 | | |
828 | 0 | *source = mainloop_add_fd(ipc_name, priority, fd, userdata, NULL); |
829 | 0 | if (*source == NULL) { |
830 | 0 | rc = errno; |
831 | 0 | crm_ipc_close(ipc); |
832 | 0 | return rc; |
833 | 0 | } |
834 | | |
835 | 0 | (*source)->ipc = ipc; |
836 | 0 | (*source)->destroy_fn = callbacks->destroy; |
837 | 0 | (*source)->dispatch_fn_ipc = callbacks->dispatch; |
838 | 0 | return pcmk_rc_ok; |
839 | 0 | } |
840 | | |
841 | | mainloop_io_t * |
842 | | mainloop_add_ipc_client(const char *name, int priority, size_t max_size, |
843 | | void *userdata, struct ipc_client_callbacks *callbacks) |
844 | 0 | { |
845 | 0 | crm_ipc_t *ipc = crm_ipc_new(name, 0); |
846 | 0 | mainloop_io_t *source = NULL; |
847 | 0 | int rc = pcmk__add_mainloop_ipc(ipc, priority, userdata, callbacks, |
848 | 0 | &source); |
849 | |
|
850 | 0 | if (rc != pcmk_rc_ok) { |
851 | 0 | if (crm_log_level == PCMK__LOG_STDOUT) { |
852 | 0 | fprintf(stderr, "Connection to %s failed: %s", |
853 | 0 | name, pcmk_rc_str(rc)); |
854 | 0 | } |
855 | 0 | crm_ipc_destroy(ipc); |
856 | 0 | if (rc > 0) { |
857 | 0 | errno = rc; |
858 | 0 | } else { |
859 | 0 | errno = ENOTCONN; |
860 | 0 | } |
861 | 0 | return NULL; |
862 | 0 | } |
863 | 0 | return source; |
864 | 0 | } |
865 | | |
866 | | void |
867 | | mainloop_del_ipc_client(mainloop_io_t * client) |
868 | 0 | { |
869 | 0 | mainloop_del_fd(client); |
870 | 0 | } |
871 | | |
872 | | crm_ipc_t * |
873 | | mainloop_get_ipc_client(mainloop_io_t * client) |
874 | 0 | { |
875 | 0 | if (client) { |
876 | 0 | return client->ipc; |
877 | 0 | } |
878 | 0 | return NULL; |
879 | 0 | } |
880 | | |
881 | | mainloop_io_t * |
882 | | mainloop_add_fd(const char *name, int priority, int fd, void *userdata, |
883 | | struct mainloop_fd_callbacks * callbacks) |
884 | 0 | { |
885 | 0 | mainloop_io_t *client = NULL; |
886 | 0 | const GIOCondition condition = G_IO_IN|G_IO_HUP|G_IO_NVAL|G_IO_ERR; |
887 | |
|
888 | 0 | if (fd < 0) { |
889 | 0 | errno = EINVAL; |
890 | 0 | return NULL; |
891 | 0 | } |
892 | | |
893 | 0 | client = pcmk__assert_alloc(1, sizeof(mainloop_io_t)); |
894 | 0 | client->name = pcmk__str_copy(name); |
895 | 0 | client->userdata = userdata; |
896 | |
|
897 | 0 | if (callbacks != NULL) { |
898 | 0 | client->destroy_fn = callbacks->destroy; |
899 | 0 | client->dispatch_fn_io = callbacks->dispatch; |
900 | 0 | } |
901 | |
|
902 | 0 | client->fd = fd; |
903 | 0 | client->channel = g_io_channel_unix_new(fd); |
904 | 0 | client->source = g_io_add_watch_full(client->channel, priority, condition, |
905 | 0 | mainloop_gio_callback, client, |
906 | 0 | mainloop_gio_destroy); |
907 | |
|
908 | 0 | pcmk__trace("Added connection %d for %s[%p].%d", client->source, |
909 | 0 | client->name, client, fd); |
910 | 0 | return client; |
911 | 0 | } |
912 | | |
913 | | void |
914 | | mainloop_del_fd(mainloop_io_t *client) |
915 | 0 | { |
916 | 0 | if ((client == NULL) || (client->source == 0)) { |
917 | 0 | return; |
918 | 0 | } |
919 | | |
920 | 0 | pcmk__trace("Removing client %s[%p]", client->name, client); |
921 | | |
922 | | /* g_source_remove() marks the source as destroyed, unsets the source |
923 | | * callback (mainloop_gio_callback()), and destroys the callback data (the |
924 | | * client) via the notify function (mainloop_gio_destroy()). We can rely on |
925 | | * mainloop_gio_callback() not getting called again for this source, and on |
926 | | * the client being destroyed. |
927 | | */ |
928 | 0 | g_source_remove(client->source); |
929 | 0 | } |
930 | | |
931 | | /*! |
932 | | * \internal |
933 | | * \brief Send \c SIGKILL to a main loop child process or its process group |
934 | | * |
935 | | * If \p child->kill_group is set, kill the child's entire process group. |
936 | | * Otherwise, kill only the child process itself. |
937 | | * |
938 | | * \param[in] child Main loop child |
939 | | * |
940 | | * \return Standard Pacemaker return code (\c pcmk_rc_ok if \c kill() returns 0, |
941 | | * or \c errno after calling \c kill() otherwise) |
942 | | */ |
943 | | static int |
944 | | kill_child_pid(const pcmk__main_loop_child_t *child) |
945 | 0 | { |
946 | 0 | const pid_t pid = (child->kill_group? -child->pid : child->pid); |
947 | 0 | int rc = 0; |
948 | |
|
949 | 0 | pcmk__debug("Killing PID %lld", (long long) pid); |
950 | | |
951 | 0 | rc = kill(pid, SIGKILL); |
952 | 0 | if (rc == 0) { |
953 | 0 | return pcmk_rc_ok; |
954 | 0 | } |
955 | | |
956 | 0 | rc = errno; |
957 | 0 | if (rc == ESRCH) { |
958 | 0 | return rc; |
959 | 0 | } |
960 | | |
961 | 0 | pcmk__err("kill(%lld, KILL) failed for child '%s': %s", (long long) pid, |
962 | 0 | pcmk__s(child->desc, ""), strerror(rc)); |
963 | 0 | return rc; |
964 | 0 | } |
965 | | |
966 | | /*! |
967 | | * \internal |
968 | | * \brief Kill a child process after its timeout has expired |
969 | | * |
970 | | * \param[in,out] user_data Main loop child |
971 | | * (<tt>pcmk__main_loop_child_t *</tt>) |
972 | | * |
973 | | * \return \c G_SOURCE_REMOVE (to destroy the timeout that triggered this call) |
974 | | * |
975 | | * \note This is a \c GSourceFunc. |
976 | | */ |
977 | | static gboolean |
978 | | child_timeout_callback(void *user_data) |
979 | 0 | { |
980 | 0 | pcmk__main_loop_child_t *child = user_data; |
981 | 0 | int rc = pcmk_rc_ok; |
982 | 0 | const char *result_s = NULL; |
983 | |
|
984 | 0 | child->timer_id = 0; |
985 | 0 | child->timed_out = true; |
986 | |
|
987 | 0 | rc = kill_child_pid(child); |
988 | |
|
989 | 0 | switch (rc) { |
990 | 0 | case pcmk_rc_ok: |
991 | 0 | result_s = "was successfully killed"; |
992 | 0 | break; |
993 | | |
994 | 0 | case ESRCH: |
995 | 0 | result_s = "has already terminated"; |
996 | 0 | break; |
997 | | |
998 | 0 | default: |
999 | 0 | result_s = "could not be killed"; |
1000 | 0 | break; |
1001 | 0 | } |
1002 | | |
1003 | 0 | pcmk__debug("%s process (PID %lld) timed out and %s", child->desc, |
1004 | 0 | (long long) child->pid, result_s); |
1005 | 0 | return G_SOURCE_REMOVE; |
1006 | 0 | } |
1007 | | |
1008 | | /*! |
1009 | | * \internal |
1010 | | * \brief Free a main loop child |
1011 | | * |
1012 | | * If the child has an associated timer, remove it. |
1013 | | * |
1014 | | * \param[in,out] data Main loop child (<tt>pcmk__main_loop_child_t *</tt>) |
1015 | | * |
1016 | | * \note This does not free the child's \c user_data field. |
1017 | | * \note This is a \c GDestroyNotify. |
1018 | | */ |
1019 | | static void |
1020 | | free_main_loop_child(void *data) |
1021 | 0 | { |
1022 | 0 | pcmk__main_loop_child_t *child = data; |
1023 | |
|
1024 | 0 | if (child == NULL) { |
1025 | 0 | return; |
1026 | 0 | } |
1027 | | |
1028 | 0 | if (child->timer_id != 0) { |
1029 | 0 | pcmk__trace("Removing timer %u", child->timer_id); |
1030 | 0 | g_source_remove(child->timer_id); |
1031 | 0 | } |
1032 | | |
1033 | 0 | free(child->desc); |
1034 | 0 | free(child); |
1035 | 0 | } |
1036 | | |
1037 | | /*! |
1038 | | * \internal |
1039 | | * \brief Wait on a child process and free it if terminated |
1040 | | * |
1041 | | * If the child has terminated, call its exit callback if any, remove it from |
1042 | | * \c child_list, and free it. |
1043 | | * |
1044 | | * Likely bug: If the child object's \c pid field is nonpositive, then we wait |
1045 | | * on the corresponding process group as documented in the \c wait(2) man page. |
1046 | | * On success, call the exit callback using that PID (not the PID of the actual |
1047 | | * child process that changed state). Also remove the child object from |
1048 | | * \c child_list and free the child object, even though there may still be other |
1049 | | * child processes in the same process group that have not yet been waited on. |
1050 | | * This seems incorrect. However, nothing internal creates a child object with |
1051 | | * nonpositive PID, and the \c mainloop_child_add() documentation notes that |
1052 | | * nonpositive PIDs are not expected to work correctly. |
1053 | | * |
1054 | | * \param[in,out] link List element whose data is the child to wait for |
1055 | | * \param[in] no_hang If \c true, use the \c waitpid() \c WNOHANG option |
1056 | | * |
1057 | | * \return \c true if the child process (or a child process in the specified |
1058 | | * process group) has terminated, or \c false if the child process is |
1059 | | * still active or its state changed in an unexpected way |
1060 | | * |
1061 | | * \note Taking the list link rather than the child as an argument allows us to |
1062 | | * delete a terminated child from \c child_list in constant time. If we |
1063 | | * took the child, \c g_list_remove() would have to find the child in the |
1064 | | * list again before removing it. |
1065 | | */ |
1066 | | static bool |
1067 | | child_waitpid(GList *link, bool no_hang) |
1068 | 0 | { |
1069 | 0 | const int options = no_hang? WNOHANG : 0; |
1070 | |
|
1071 | 0 | pcmk__main_loop_child_t *child = NULL; |
1072 | 0 | pid_t rc = 0; |
1073 | 0 | int status = 0; |
1074 | |
|
1075 | 0 | int core = 0; |
1076 | 0 | int signo = 0; |
1077 | 0 | int exit_code = 0; |
1078 | |
|
1079 | 0 | pcmk__assert(link != NULL); |
1080 | 0 | child = link->data; |
1081 | |
|
1082 | 0 | rc = waitpid(child->pid, &status, options); |
1083 | |
|
1084 | 0 | if (rc == 0) { |
1085 | | // WNOHANG was specified and child->pid exists and has not changed state |
1086 | 0 | pcmk__trace("Child process %lld (%s) still active", |
1087 | 0 | (long long) child->pid, child->desc); |
1088 | 0 | return false; |
1089 | 0 | } |
1090 | | |
1091 | 0 | if (rc == -1) { |
1092 | 0 | if (errno == ECHILD) { |
1093 | | /* This situation should probably never happen in practice. Setting |
1094 | | * exit_code to 1 is misleading in that it indicates the child |
1095 | | * exited with code 1, and we don't know that to be true. We could |
1096 | | * add a pcmk__main_loop_child_t flag to indicate this case, but it |
1097 | | * doesn't seem worth it. |
1098 | | */ |
1099 | 0 | exit_code = 1; |
1100 | |
|
1101 | 0 | pcmk__err("Wait for child process %lld (%s) failed because process " |
1102 | 0 | "does not exist or is not our child", |
1103 | 0 | (long long) child->pid, child->desc); |
1104 | 0 | goto terminated; |
1105 | 0 | } |
1106 | | |
1107 | 0 | if (errno == EINTR) { |
1108 | 0 | pcmk__notice("Wait for child process %lld (%s) was interrupted by " |
1109 | 0 | "a signal", (long long) child->pid, child->desc); |
1110 | 0 | return false; |
1111 | 0 | } |
1112 | | |
1113 | 0 | pcmk__err("Bug: Wait for child process %lld (%s) failed: %s (waitpid() " |
1114 | 0 | "options: %#x)", (long long) child->pid, child->desc, |
1115 | 0 | strerror(errno), options); |
1116 | 0 | return false; |
1117 | 0 | } |
1118 | | |
1119 | | /* At this point, rc is the PID of a child whose state changed. If |
1120 | | * child->pid is positive, then rc == child->pid. Otherwise, rc is the PID |
1121 | | * of one of the child processes in the process group with ID -child->pid. |
1122 | | */ |
1123 | | |
1124 | 0 | if (rc != child->pid) { |
1125 | | /* @COMPAT Nothing internal creates a nonpositive child->pid, and the |
1126 | | * public Doxygen for mainloop_child_add() now notes that nonpositive |
1127 | | * PIDs are not expected to work correctly. |
1128 | | */ |
1129 | 0 | pcmk__trace("Child process %lld from group %lld (%s) terminated", |
1130 | 0 | (long long) rc, (long long) -child->pid, child->desc); |
1131 | 0 | goto terminated; |
1132 | 0 | } |
1133 | | |
1134 | 0 | if (WIFEXITED(status)) { |
1135 | 0 | exit_code = WEXITSTATUS(status); |
1136 | 0 | pcmk__trace("Child process %lld (%s) exited with status %d", |
1137 | 0 | (long long) child->pid, child->desc, exit_code); |
1138 | 0 | goto terminated; |
1139 | 0 | } |
1140 | | |
1141 | 0 | if (WIFSIGNALED(status)) { |
1142 | 0 | signo = WTERMSIG(status); |
1143 | 0 | pcmk__trace("Child process %lld (%s) was terminated by signal %d (%s)", |
1144 | 0 | (long long) child->pid, child->desc, signo, |
1145 | 0 | strsignal(signo)); |
1146 | | |
1147 | 0 | #ifdef WCOREDUMP |
1148 | 0 | if (WCOREDUMP(status)) { |
1149 | 0 | core = 1; |
1150 | 0 | pcmk__err("Child process %lld (%s) dumped core", |
1151 | 0 | (long long) child->pid, child->desc); |
1152 | 0 | } |
1153 | 0 | #endif // defined(WCOREDUMP) |
1154 | |
|
1155 | 0 | goto terminated; |
1156 | 0 | } |
1157 | | |
1158 | | /* We're not using the WUNTRACED or WCONTINUED options. If the process |
1159 | | * changed state, it should have either exited or been terminated by a |
1160 | | * signal. |
1161 | | */ |
1162 | 0 | CRM_CHECK(false, return false); |
1163 | | |
1164 | 0 | terminated: |
1165 | 0 | if (child->callback != NULL) { |
1166 | 0 | child->callback(child, core, signo, exit_code); |
1167 | 0 | } |
1168 | |
|
1169 | 0 | pcmk__trace("Removing terminated process %lld from child list", |
1170 | 0 | (long long) child->pid); |
1171 | 0 | child_list = g_list_delete_link(child_list, link); |
1172 | 0 | free_main_loop_child(child); |
1173 | |
|
1174 | 0 | return true; |
1175 | 0 | } |
1176 | | |
1177 | | /*! |
1178 | | * \internal |
1179 | | * \brief Free all main loop children whose processes have terminated |
1180 | | * |
1181 | | * If a child object's process has terminated, remove the child from |
1182 | | * \c child_list and free it. |
1183 | | * |
1184 | | * \param[in] signal Ignored |
1185 | | */ |
1186 | | static void |
1187 | | free_terminated_children(int signal) |
1188 | 0 | { |
1189 | 0 | GList *iter = child_list; |
1190 | |
|
1191 | 0 | while (iter != NULL) { |
1192 | 0 | GList *next = iter->next; |
1193 | |
|
1194 | 0 | child_waitpid(iter, true); |
1195 | 0 | iter = next; |
1196 | 0 | } |
1197 | 0 | } |
1198 | | |
1199 | | /*! |
1200 | | * \internal |
1201 | | * \brief Install the main loop \c SIGCHLD handler |
1202 | | * |
1203 | | * Install \c free_terminated_children() as the \c SIGCHLD handler, and call it |
1204 | | * for any children that terminated before the handler was installed. |
1205 | | * |
1206 | | * \param[in] user_data Ignored |
1207 | | * |
1208 | | * \return \c G_SOURCE_REMOVE (to destroy the timeout that triggered this call) |
1209 | | * |
1210 | | * \note This is a \c GSourceFunc. |
1211 | | */ |
1212 | | static gboolean |
1213 | | install_sigchld_handler(void *user_data) |
1214 | 0 | { |
1215 | 0 | pcmk__trace("Installing SIGCHLD handler"); |
1216 | | |
1217 | | // Do NOT use g_child_watch_add() and friends, since they rely on pthreads |
1218 | 0 | mainloop_add_signal(SIGCHLD, free_terminated_children); |
1219 | |
|
1220 | 0 | free_terminated_children(SIGCHLD); |
1221 | 0 | return G_SOURCE_REMOVE; |
1222 | 0 | } |
1223 | | |
1224 | | /*! |
1225 | | * \internal |
1226 | | * \brief Create a \c pcmk__main_loop_child_t object and add it to the main loop |
1227 | | * |
1228 | | * If the child process has not exited within \p timeout_ms, send it a |
1229 | | * \c SIGKILL signal. |
1230 | | * |
1231 | | * \param[in] pid Child PID |
1232 | | * \param[in] desc Description |
1233 | | * \param[in] timeout_ms Timeout in milliseconds |
1234 | | * \param[in] user_data User data |
1235 | | * \param[in] kill group If \c true, kill the child's entire process group on |
1236 | | * timeout; otherwise, kill only the child process |
1237 | | * \param[in] callback Function to call when the child process terminates |
1238 | | */ |
1239 | | void |
1240 | | pcmk__main_loop_child_create(pid_t pid, const char *desc, |
1241 | | unsigned int timeout_ms, void *user_data, |
1242 | | bool kill_group, |
1243 | | pcmk__main_loop_child_cb_t callback) |
1244 | 0 | { |
1245 | 0 | static bool need_init = true; |
1246 | |
|
1247 | 0 | pcmk__main_loop_child_t *child = NULL; |
1248 | |
|
1249 | 0 | pcmk__assert(pid > 0); |
1250 | |
|
1251 | 0 | child = pcmk__assert_alloc(1, sizeof(pcmk__main_loop_child_t)); |
1252 | 0 | child->pid = pid; |
1253 | 0 | child->desc = pcmk__str_copy(desc); |
1254 | 0 | child->user_data = user_data; |
1255 | 0 | child->kill_group = kill_group; |
1256 | 0 | child->callback = callback; |
1257 | |
|
1258 | 0 | if (timeout_ms > 0) { |
1259 | 0 | child->timer_id = pcmk__create_timer(timeout_ms, child_timeout_callback, |
1260 | 0 | child); |
1261 | 0 | } |
1262 | |
|
1263 | 0 | child_list = g_list_append(child_list, child); |
1264 | |
|
1265 | 0 | if (need_init) { |
1266 | | /* Invoke SIGCHLD processing from the main loop. This ensures that we |
1267 | | * don't add a child to the main loop and have the exit callback invoked |
1268 | | * for the child PID within the same call stack. |
1269 | | * |
1270 | | * @TODO Understand and document why this matters. |
1271 | | */ |
1272 | 0 | need_init = false; |
1273 | 0 | pcmk__create_timer(1, install_sigchld_handler, NULL); |
1274 | 0 | } |
1275 | 0 | } |
1276 | | |
1277 | | /*! |
1278 | | * \internal |
1279 | | * \brief Compare two mainloop child objects by PID |
1280 | | * |
1281 | | * \param[in] a First child to compare |
1282 | | * (<tt>const pcmk__main_loop_child_t *</tt>) |
1283 | | * \param[in] b Second child to compare |
1284 | | * (<tt>const pcmk__main_loop_child_t *</tt>) |
1285 | | * |
1286 | | * \retval -1 if \p a->pid is less than \p b->pid |
1287 | | * \retval 0 if \p a->pid is equal to \p b->pid |
1288 | | * \retval 1 if \p a->pid is greater than \p b->pid |
1289 | | * |
1290 | | * \note This is a \c GCompareFunc. |
1291 | | */ |
1292 | | static int |
1293 | | compare_children_by_pid(const void *a, const void *b) |
1294 | 0 | { |
1295 | 0 | const pcmk__main_loop_child_t *child1 = a; |
1296 | 0 | const pcmk__main_loop_child_t *child2 = b; |
1297 | |
|
1298 | 0 | if (child1->pid < child2->pid) { |
1299 | 0 | return -1; |
1300 | 0 | } |
1301 | | |
1302 | 0 | if (child1->pid > child2->pid) { |
1303 | 0 | return 1; |
1304 | 0 | } |
1305 | | |
1306 | 0 | return 0; |
1307 | 0 | } |
1308 | | |
1309 | | /*! |
1310 | | * \internal |
1311 | | * \brief Kill a child process tracked by the main loop |
1312 | | * |
1313 | | * If a process with PID \p pid is being tracked, send it a \c SIGKILL. |
1314 | | * |
1315 | | * If this function kills the child process successfully, remove the child from |
1316 | | * the tracking data structure and free the child. |
1317 | | * |
1318 | | * If the process is being tracked but no longer exists, don't remove or free |
1319 | | * the child yet. We will do this later when we receive a \c SIGCHLD for the |
1320 | | * child process. |
1321 | | * |
1322 | | * \param[in] pid Child PID |
1323 | | * |
1324 | | * \return \c true if the child with ID \p pid was being tracked and either this |
1325 | | * function killed the process successfully or the process has already |
1326 | | * terminated but we have not received a \c SIGCHLD for it; or \c false |
1327 | | * otherwise |
1328 | | */ |
1329 | | bool |
1330 | | pcmk__main_loop_child_kill(pid_t pid) |
1331 | 0 | { |
1332 | 0 | const pcmk__main_loop_child_t cmp_data = { .pid = pid }; |
1333 | 0 | GList *match = NULL; |
1334 | 0 | pcmk__main_loop_child_t *child = NULL; |
1335 | 0 | int rc = pcmk_rc_ok; |
1336 | 0 | bool no_hang = false; |
1337 | |
|
1338 | 0 | pcmk__assert(pid > 0); |
1339 | |
|
1340 | 0 | match = g_list_find_custom(child_list, &cmp_data, compare_children_by_pid); |
1341 | 0 | if (match == NULL) { |
1342 | 0 | return false; |
1343 | 0 | } |
1344 | | |
1345 | 0 | child = match->data; |
1346 | |
|
1347 | 0 | rc = kill_child_pid(child); |
1348 | 0 | if (rc == ESRCH) { |
1349 | | /* It's gone but hasn't shown up in waitpid() yet. Wait until we get |
1350 | | * SIGCHLD and let handler clean it up as normal (so we get the correct |
1351 | | * return code/status). The blocking alternative would be to call |
1352 | | * child_waitpid(iter, false). |
1353 | | */ |
1354 | 0 | pcmk__trace("Waiting for signal that child process %lld completed", |
1355 | 0 | (long long) child->pid); |
1356 | 0 | return true; |
1357 | 0 | } |
1358 | | |
1359 | 0 | if (rc != pcmk_rc_ok) { |
1360 | | /* If kill() failed for some other reason, set the WNOHANG flag, since |
1361 | | * we can't be certain what happened. |
1362 | | * |
1363 | | * If kill() succeeded, we don't need the WNOHANG flag because SIGKILL |
1364 | | * can't be blocked. |
1365 | | */ |
1366 | 0 | no_hang = true; |
1367 | 0 | } |
1368 | |
|
1369 | 0 | return child_waitpid(match, no_hang); |
1370 | 0 | } |
1371 | | |
1372 | | /*! |
1373 | | * \internal |
1374 | | * \brief Create a main loop timer |
1375 | | * |
1376 | | * \param[in] name Timer name prefix (for logging only) |
1377 | | * \param[in] interval_ms Timer interval |
1378 | | * \param[in] callback Function to call after \p interval_ms expires |
1379 | | * \param[in] user_data User data for \p callback |
1380 | | * |
1381 | | * \return Newly allocated main loop timer (guaranteed not to be \c NULL) |
1382 | | * |
1383 | | * \note The new timer's \c name string starts with the \p name argument and |
1384 | | * includes the timer's interval and address. |
1385 | | * \note The caller is responsible for freeing the return value using |
1386 | | * \c pcmk__main_loop_timer_free(). |
1387 | | */ |
1388 | | pcmk__main_loop_timer_t * |
1389 | | pcmk__main_loop_timer_new(const char *name, unsigned int interval_ms, |
1390 | | GSourceFunc callback, void *user_data) |
1391 | 0 | { |
1392 | 0 | pcmk__main_loop_timer_t *timer = NULL; |
1393 | 0 | pcmk__assert((name != NULL) && (callback != NULL)); |
1394 | |
|
1395 | 0 | timer = pcmk__assert_alloc(1, sizeof(pcmk__main_loop_timer_t)); |
1396 | 0 | timer->name = pcmk__assert_asprintf("%s-%u-%p", name, interval_ms, timer); |
1397 | 0 | timer->interval_ms = interval_ms; |
1398 | 0 | timer->cb = callback; |
1399 | 0 | timer->user_data = user_data; |
1400 | |
|
1401 | 0 | pcmk__trace("Created timer %s with data %p", timer->name, user_data); |
1402 | 0 | return timer; |
1403 | 0 | } |
1404 | | |
1405 | | /*! |
1406 | | * \internal |
1407 | | * \brief Check whether a main loop timer is running |
1408 | | * |
1409 | | * A timer is running if its \c id field is nonzero, meaning that it has an |
1410 | | * active \c GSource with that ID associated with it. |
1411 | | * |
1412 | | * \param[in] timer Main loop timer |
1413 | | * |
1414 | | * \return \c true if the timer is running, or \c false otherwise |
1415 | | */ |
1416 | | bool |
1417 | | pcmk__main_loop_timer_running(const pcmk__main_loop_timer_t *timer) |
1418 | 0 | { |
1419 | 0 | CRM_CHECK(timer != NULL, return false); |
1420 | | |
1421 | 0 | return (timer->source_id != 0); |
1422 | 0 | } |
1423 | | |
1424 | | /*! |
1425 | | * \internal |
1426 | | * \brief Stop a main loop timer |
1427 | | * |
1428 | | * Stopping a timer consists of removing its \c GSource and setting its \c id |
1429 | | * field to 0 (to indicate that it has no associated \c GSource). |
1430 | | * |
1431 | | * \param[in,out] timer Main loop timer |
1432 | | */ |
1433 | | void |
1434 | | pcmk__main_loop_timer_stop(pcmk__main_loop_timer_t *timer) |
1435 | 0 | { |
1436 | 0 | CRM_CHECK(timer != NULL, return); |
1437 | | |
1438 | 0 | if (!pcmk__main_loop_timer_running(timer)) { |
1439 | 0 | return; |
1440 | 0 | } |
1441 | | |
1442 | 0 | pcmk__trace("Stopping timer %s", timer->name); |
1443 | 0 | g_source_remove(timer->source_id); |
1444 | 0 | timer->source_id = 0; |
1445 | 0 | } |
1446 | | |
1447 | | /*! |
1448 | | * \internal |
1449 | | * \brief Run a main loop timer's callback |
1450 | | * |
1451 | | * If the callback returns \c G_SOURCE_REMOVE, set \p timer->source_id to 0 to |
1452 | | * indicate that the timer has no associated \c GSource. |
1453 | | * |
1454 | | * \param[in,out] user_data Main loop timer |
1455 | | * (<tt>pcmk__main_loop_timer_t *</tt>) |
1456 | | * |
1457 | | * \return The return value from \p timer->cb (\c G_SOURCE_CONTINUE to keep the |
1458 | | * timeout source, or \c G_SOURCE_REMOVE to remove it) |
1459 | | * |
1460 | | * \note This is a \c GSourceFunc. |
1461 | | */ |
1462 | | static gboolean |
1463 | | main_loop_timer_cb(void *user_data) |
1464 | 0 | { |
1465 | 0 | int id = 0; |
1466 | 0 | pcmk__main_loop_timer_t *timer = user_data; |
1467 | |
|
1468 | 0 | pcmk__assert((timer != NULL) && (timer->cb != NULL)); |
1469 | | |
1470 | | /* Ensure id is unset during callbacks so that |
1471 | | * pcmk__main_loop_timer_running() works as expected. |
1472 | | * |
1473 | | * @TODO Why is this necessary or desirable? |
1474 | | */ |
1475 | 0 | id = timer->source_id; |
1476 | 0 | timer->source_id = 0; |
1477 | |
|
1478 | 0 | pcmk__trace("Invoking callbacks for timer %s", timer->name); |
1479 | | |
1480 | | // G_SOURCE_REMOVE is false; G_SOURCE_CONTINUE is true |
1481 | 0 | if (!timer->cb(timer->user_data)) { |
1482 | 0 | pcmk__trace("Timer %s complete", timer->name); |
1483 | 0 | return G_SOURCE_REMOVE; |
1484 | 0 | } |
1485 | | |
1486 | 0 | timer->source_id = id; |
1487 | 0 | return G_SOURCE_CONTINUE; |
1488 | 0 | } |
1489 | | |
1490 | | /*! |
1491 | | * \internal |
1492 | | * \brief Start a main loop timer |
1493 | | * |
1494 | | * Starting a timer consists of: |
1495 | | * 1. stopping the timer if it's already running (by removing the associated |
1496 | | * \c GSource) |
1497 | | * 2. creating a new \c GSource using \p timer->interval_ms as the timeout (see |
1498 | | * \c pcmk__create_timer()) |
1499 | | * 3. assigning the new \c GSource ID to \p timer->source_id |
1500 | | * |
1501 | | * \param[in,out] timer Main loop timer |
1502 | | */ |
1503 | | void |
1504 | | pcmk__main_loop_timer_start(pcmk__main_loop_timer_t *timer) |
1505 | 0 | { |
1506 | 0 | CRM_CHECK((timer != NULL) |
1507 | 0 | && (timer->interval_ms > 0) |
1508 | 0 | && (timer->cb != NULL), |
1509 | 0 | return); |
1510 | | |
1511 | 0 | pcmk__main_loop_timer_stop(timer); |
1512 | |
|
1513 | 0 | pcmk__trace("Starting timer %s", timer->name); |
1514 | 0 | timer->source_id = pcmk__create_timer(timer->interval_ms, |
1515 | 0 | main_loop_timer_cb, timer); |
1516 | 0 | } |
1517 | | |
1518 | | /*! |
1519 | | * \internal |
1520 | | * \brief Free a main loop timer |
1521 | | * |
1522 | | * \param[in,out] timer Main loop timer |
1523 | | */ |
1524 | | void |
1525 | | pcmk__main_loop_timer_free(pcmk__main_loop_timer_t *timer) |
1526 | 0 | { |
1527 | 0 | if (timer == NULL) { |
1528 | 0 | return; |
1529 | 0 | } |
1530 | | |
1531 | 0 | pcmk__trace("Destroying timer %s", timer->name); |
1532 | 0 | pcmk__main_loop_timer_stop(timer); |
1533 | 0 | free(timer->name); |
1534 | 0 | free(timer); |
1535 | 0 | } |
1536 | | |
1537 | | /* |
1538 | | * Helpers to make sure certain events aren't lost at shutdown |
1539 | | */ |
1540 | | |
1541 | | static gboolean |
1542 | | drain_timeout_cb(void *user_data) |
1543 | 0 | { |
1544 | 0 | bool *timeout_popped = (bool*) user_data; |
1545 | |
|
1546 | 0 | *timeout_popped = TRUE; |
1547 | 0 | return G_SOURCE_REMOVE; |
1548 | 0 | } |
1549 | | |
1550 | | /*! |
1551 | | * \brief Drain some remaining main loop events then quit it |
1552 | | * |
1553 | | * \param[in,out] mloop Main loop to drain and quit |
1554 | | * \param[in] n Drain up to this many pending events |
1555 | | */ |
1556 | | void |
1557 | | pcmk_quit_main_loop(GMainLoop *mloop, unsigned int n) |
1558 | 0 | { |
1559 | 0 | if ((mloop != NULL) && g_main_loop_is_running(mloop)) { |
1560 | 0 | GMainContext *ctx = g_main_loop_get_context(mloop); |
1561 | | |
1562 | | /* Drain up to n events in case some memory clean-up is pending |
1563 | | * (helpful to reduce noise in valgrind output). |
1564 | | */ |
1565 | 0 | for (int i = 0; (i < n) && g_main_context_pending(ctx); ++i) { |
1566 | 0 | g_main_context_dispatch(ctx); |
1567 | 0 | } |
1568 | 0 | g_main_loop_quit(mloop); |
1569 | 0 | } |
1570 | 0 | } |
1571 | | |
1572 | | /*! |
1573 | | * \brief Process main loop events while a certain condition is met |
1574 | | * |
1575 | | * \param[in,out] mloop Main loop to process |
1576 | | * \param[in] timer_ms Don't process longer than this amount of time |
1577 | | * \param[in] check Function that returns true if events should be |
1578 | | * processed |
1579 | | * |
1580 | | * \note This function is intended to be called at shutdown if certain important |
1581 | | * events should not be missed. The caller would likely quit the main loop |
1582 | | * or exit after calling this function. The check() function will be |
1583 | | * passed the remaining timeout in milliseconds. |
1584 | | */ |
1585 | | void |
1586 | | pcmk_drain_main_loop(GMainLoop *mloop, unsigned int timer_ms, |
1587 | | bool (*check)(unsigned int)) |
1588 | 0 | { |
1589 | 0 | bool timeout_popped = FALSE; |
1590 | 0 | unsigned int timer = 0; |
1591 | 0 | GMainContext *ctx = NULL; |
1592 | |
|
1593 | 0 | CRM_CHECK(mloop && check, return); |
1594 | | |
1595 | 0 | ctx = g_main_loop_get_context(mloop); |
1596 | 0 | if (ctx) { |
1597 | 0 | time_t start_time = time(NULL); |
1598 | |
|
1599 | 0 | timer = pcmk__create_timer(timer_ms, drain_timeout_cb, &timeout_popped); |
1600 | 0 | while (!timeout_popped |
1601 | 0 | && check(timer_ms - (time(NULL) - start_time) * 1000)) { |
1602 | 0 | g_main_context_iteration(ctx, TRUE); |
1603 | 0 | } |
1604 | 0 | } |
1605 | 0 | if (!timeout_popped && (timer > 0)) { |
1606 | 0 | g_source_remove(timer); |
1607 | 0 | } |
1608 | 0 | } |
1609 | | |
1610 | | /*! |
1611 | | * \internal |
1612 | | * \brief Free data structures used for the mainloop |
1613 | | * |
1614 | | * \todo This is incomplete. Free other data structures created in this file. |
1615 | | */ |
1616 | | void |
1617 | | mainloop_cleanup(void) |
1618 | 0 | { |
1619 | 0 | g_list_free_full(child_list, free_main_loop_child); |
1620 | 0 | child_list = NULL; |
1621 | |
|
1622 | 0 | g_clear_pointer(&gio_map, qb_array_free); |
1623 | |
|
1624 | 0 | for (int sig = 0; sig < NSIG; ++sig) { |
1625 | 0 | mainloop_destroy_signal_entry(sig); |
1626 | 0 | } |
1627 | 0 | } |
1628 | | |
1629 | | // Deprecated functions kept only for backward API compatibility |
1630 | | // LCOV_EXCL_START |
1631 | | |
1632 | | #include <crm/common/mainloop_compat.h> |
1633 | | |
1634 | | void |
1635 | | mainloop_child_add_with_flags(pid_t pid, int timeout_ms, const char *desc, |
1636 | | void *user_data, |
1637 | | enum mainloop_child_flags flags, |
1638 | | void (*callback)(mainloop_child_t *child, int core, |
1639 | | int signo, int exit_code)) |
1640 | 0 | { |
1641 | 0 | static bool need_init = true; |
1642 | |
|
1643 | 0 | mainloop_child_t *child = pcmk__assert_alloc(1, sizeof(mainloop_child_t)); |
1644 | |
|
1645 | 0 | child->pid = pid; |
1646 | 0 | child->desc = pcmk__str_copy(desc); |
1647 | 0 | child->user_data = user_data; |
1648 | 0 | child->kill_group = !pcmk__is_set(flags, mainloop_leave_pid_group); |
1649 | 0 | child->callback = callback; |
1650 | |
|
1651 | 0 | if (timeout_ms > 0) { |
1652 | 0 | child->timer_id = pcmk__create_timer(timeout_ms, child_timeout_callback, |
1653 | 0 | child); |
1654 | 0 | } |
1655 | |
|
1656 | 0 | child_list = g_list_append(child_list, child); |
1657 | |
|
1658 | 0 | if (need_init) { |
1659 | 0 | need_init = false; |
1660 | 0 | pcmk__create_timer(1, install_sigchld_handler, NULL); |
1661 | 0 | } |
1662 | 0 | } |
1663 | | |
1664 | | void |
1665 | | mainloop_child_add(pid_t pid, int timeout_ms, const char *desc, void *user_data, |
1666 | | void (*callback)(mainloop_child_t *child, int core, |
1667 | | int signo, int exit_code)) |
1668 | 0 | { |
1669 | 0 | mainloop_child_add_with_flags(pid, timeout_ms, desc, user_data, 0, callback); |
1670 | 0 | } |
1671 | | |
1672 | | gboolean |
1673 | | mainloop_child_kill(pid_t pid) |
1674 | 0 | { |
1675 | 0 | const mainloop_child_t cmp_data = { .pid = pid }; |
1676 | 0 | GList *match = NULL; |
1677 | 0 | mainloop_child_t *child = NULL; |
1678 | 0 | int rc = pcmk_rc_ok; |
1679 | 0 | bool no_hang = false; |
1680 | |
|
1681 | 0 | match = g_list_find_custom(child_list, &cmp_data, compare_children_by_pid); |
1682 | 0 | if (match == NULL) { |
1683 | 0 | return FALSE; |
1684 | 0 | } |
1685 | | |
1686 | 0 | child = match->data; |
1687 | |
|
1688 | 0 | rc = kill_child_pid(child); |
1689 | 0 | if (rc == ESRCH) { |
1690 | 0 | pcmk__trace("Waiting for signal that child process %lld completed", |
1691 | 0 | (long long) child->pid); |
1692 | 0 | return TRUE; |
1693 | 0 | } |
1694 | | |
1695 | 0 | if (rc != pcmk_rc_ok) { |
1696 | 0 | no_hang = true; |
1697 | 0 | } |
1698 | |
|
1699 | 0 | return child_waitpid(match, no_hang)? TRUE : FALSE; |
1700 | 0 | } |
1701 | | |
1702 | | pid_t |
1703 | | mainloop_child_pid(mainloop_child_t *child) |
1704 | 0 | { |
1705 | 0 | return child->pid; |
1706 | 0 | } |
1707 | | |
1708 | | const char * |
1709 | | mainloop_child_name(mainloop_child_t *child) |
1710 | 0 | { |
1711 | 0 | return child->desc; |
1712 | 0 | } |
1713 | | |
1714 | | int |
1715 | | mainloop_child_timeout(mainloop_child_t *child) |
1716 | 0 | { |
1717 | 0 | return child->timed_out? TRUE : FALSE; |
1718 | 0 | } |
1719 | | |
1720 | | void * |
1721 | | mainloop_child_userdata(mainloop_child_t *child) |
1722 | 0 | { |
1723 | 0 | return child->user_data; |
1724 | 0 | } |
1725 | | |
1726 | | void |
1727 | | mainloop_clear_child_userdata(mainloop_child_t *child) |
1728 | 0 | { |
1729 | 0 | child->user_data = NULL; |
1730 | 0 | } |
1731 | | |
1732 | | struct mainloop_timer_s { |
1733 | | char *name; |
1734 | | unsigned int source_id; |
1735 | | unsigned int interval_ms; |
1736 | | bool repeat; |
1737 | | GSourceFunc cb; |
1738 | | void *user_data; |
1739 | | }; |
1740 | | |
1741 | | static gboolean |
1742 | | mainloop_timer_cb(void *user_data) |
1743 | 0 | { |
1744 | 0 | int id = 0; |
1745 | 0 | mainloop_timer_t *timer = user_data; |
1746 | |
|
1747 | 0 | pcmk__assert((timer != NULL) && (timer->cb != NULL)); |
1748 | |
|
1749 | 0 | id = timer->source_id; |
1750 | 0 | timer->source_id = 0; |
1751 | |
|
1752 | 0 | pcmk__trace("Invoking callbacks for timer %s", timer->name); |
1753 | | |
1754 | 0 | if (!timer->cb(timer->user_data)) { |
1755 | 0 | pcmk__trace("Timer %s complete", timer->name); |
1756 | 0 | return G_SOURCE_REMOVE; |
1757 | 0 | } |
1758 | | |
1759 | 0 | if (!timer->repeat) { |
1760 | 0 | return G_SOURCE_REMOVE; |
1761 | 0 | } |
1762 | | |
1763 | 0 | timer->source_id = id; |
1764 | 0 | return G_SOURCE_CONTINUE; |
1765 | 0 | } |
1766 | | |
1767 | | bool |
1768 | | mainloop_timer_running(mainloop_timer_t *timer) |
1769 | 0 | { |
1770 | 0 | return (timer != NULL) && (timer->source_id != 0); |
1771 | 0 | } |
1772 | | |
1773 | | void |
1774 | | mainloop_timer_start(mainloop_timer_t *timer) |
1775 | 0 | { |
1776 | 0 | mainloop_timer_stop(timer); |
1777 | |
|
1778 | 0 | if ((timer == NULL) || (timer->interval_ms == 0) || (timer->cb == NULL)) { |
1779 | 0 | return; |
1780 | 0 | } |
1781 | | |
1782 | 0 | pcmk__trace("Starting timer %s", timer->name); |
1783 | 0 | timer->source_id = pcmk__create_timer(timer->interval_ms, mainloop_timer_cb, |
1784 | 0 | timer); |
1785 | 0 | } |
1786 | | |
1787 | | void |
1788 | | mainloop_timer_stop(mainloop_timer_t *timer) |
1789 | 0 | { |
1790 | 0 | if ((timer == NULL) || (timer->source_id == 0)) { |
1791 | 0 | return; |
1792 | 0 | } |
1793 | | |
1794 | 0 | pcmk__trace("Stopping timer %s", timer->name); |
1795 | 0 | g_source_remove(timer->source_id); |
1796 | 0 | timer->source_id = 0; |
1797 | 0 | } |
1798 | | |
1799 | | unsigned int |
1800 | | mainloop_timer_set_period(mainloop_timer_t *timer, unsigned int interval_ms) |
1801 | 0 | { |
1802 | 0 | unsigned int last = 0; |
1803 | |
|
1804 | 0 | if (timer == NULL) { |
1805 | 0 | return 0; |
1806 | 0 | } |
1807 | | |
1808 | 0 | last = timer->interval_ms; |
1809 | 0 | timer->interval_ms = interval_ms; |
1810 | |
|
1811 | 0 | if ((timer->source_id != 0) && (timer->interval_ms != last)) { |
1812 | 0 | mainloop_timer_start(timer); |
1813 | 0 | } |
1814 | |
|
1815 | 0 | return last; |
1816 | 0 | } |
1817 | | |
1818 | | mainloop_timer_t * |
1819 | | mainloop_timer_add(const char *name, unsigned int interval_ms, bool repeat, |
1820 | | GSourceFunc cb, void *userdata) |
1821 | 0 | { |
1822 | 0 | mainloop_timer_t *timer = pcmk__assert_alloc(1, sizeof(mainloop_timer_t)); |
1823 | |
|
1824 | 0 | if (name != NULL) { |
1825 | 0 | timer->name = pcmk__assert_asprintf("%s-%u-%d", name, interval_ms, |
1826 | 0 | repeat); |
1827 | |
|
1828 | 0 | } else { |
1829 | 0 | timer->name = pcmk__assert_asprintf("%p-%u-%d", timer, interval_ms, |
1830 | 0 | repeat); |
1831 | 0 | } |
1832 | |
|
1833 | 0 | timer->interval_ms = interval_ms; |
1834 | 0 | timer->repeat = repeat; |
1835 | 0 | timer->cb = cb; |
1836 | 0 | timer->user_data = userdata; |
1837 | |
|
1838 | 0 | pcmk__trace("Created timer %s with %p", timer->name, userdata); |
1839 | 0 | return timer; |
1840 | 0 | } |
1841 | | |
1842 | | void |
1843 | | mainloop_timer_del(mainloop_timer_t *timer) |
1844 | 0 | { |
1845 | 0 | if (timer == NULL) { |
1846 | 0 | return; |
1847 | 0 | } |
1848 | | |
1849 | 0 | pcmk__trace("Destroying timer %s", timer->name); |
1850 | 0 | mainloop_timer_stop(timer); |
1851 | 0 | free(timer->name); |
1852 | 0 | free(timer); |
1853 | 0 | } |
1854 | | |
1855 | | // LCOV_EXCL_STOP |
1856 | | // End deprecated API |