diff --git a/daemons/attrd/attrd_attributes.c b/daemons/attrd/attrd_attributes.c index 34440904af5..318b4d67d43 100644 --- a/daemons/attrd/attrd_attributes.c +++ b/daemons/attrd/attrd_attributes.c @@ -30,6 +30,8 @@ attrd_create_attribute(xmlNode *xml) const char *dampen_s = pcmk__xe_get(xml, PCMK__XA_ATTR_DAMPENING); attribute_t *a = NULL; + pcmk__assert(name != NULL); + if (set_type == NULL) { set_type = PCMK_XE_INSTANCE_ATTRIBUTES; } @@ -97,7 +99,7 @@ attrd_update_dampening(attribute_t *a, xmlNode *xml, const char *attr) } if (a->timeout_ms != dampen) { - mainloop_timer_del(a->timer); + pcmk__main_loop_timer_free(a->timer); a->timeout_ms = (int) QB_MIN(dampen, INT_MAX); if (dampen > 0) { a->timer = attrd_add_timer(attr, a->timeout_ms, a); diff --git a/daemons/attrd/attrd_cib.c b/daemons/attrd/attrd_cib.c index e33d525fd41..31f86f68060 100644 --- a/daemons/attrd/attrd_cib.c +++ b/daemons/attrd/attrd_cib.c @@ -240,7 +240,7 @@ attribute_timer_cb(void *data) attribute_t *a = data; pcmk__trace("Dampen interval expired for %s", a->id); attrd_write_or_elect_attribute(a); - return FALSE; + return G_SOURCE_REMOVE; } static void @@ -270,7 +270,7 @@ attrd_cib_callback(xmlNode *msg, int call_id, int rc, xmlNode *output, void *use last_cib_op_done = call_id; if (a->timer && !a->timeout_ms) { // Remove temporary dampening for failed writes - g_clear_pointer(&a->timer, mainloop_timer_del); + g_clear_pointer(&a->timer, pcmk__main_loop_timer_free); } break; @@ -319,10 +319,10 @@ attrd_cib_callback(xmlNode *msg, int call_id, int rc, xmlNode *output, void *use */ } else if (a->timer) { // Attribute has a dampening value, so use that as delay - if (!mainloop_timer_running(a->timer)) { + if (!pcmk__main_loop_timer_running(a->timer)) { pcmk__trace("Delayed re-attempted write for %s by %s", name, pcmk__readable_interval(a->timeout_ms)); - mainloop_timer_start(a->timer); + pcmk__main_loop_timer_start(a->timer); } } else { /* Set a temporary dampening of 2 seconds (timer will continue @@ -330,7 +330,7 @@ attrd_cib_callback(xmlNode *msg, int call_id, int rc, xmlNode *output, void *use * write succeeds). */ a->timer = attrd_add_timer(a->id, 2000, a); - mainloop_timer_start(a->timer); + pcmk__main_loop_timer_start(a->timer); } } } @@ -489,10 +489,10 @@ set_alert_attribute_value(GHashTable *t, attribute_value_t *v) g_hash_table_replace(t, a_v->nodename, a_v); } -mainloop_timer_t * +pcmk__main_loop_timer_t * attrd_add_timer(const char *id, int timeout_ms, attribute_t *attr) { - return mainloop_timer_add(id, timeout_ms, FALSE, attribute_timer_cb, attr); + return pcmk__main_loop_timer_new(id, timeout_ms, attribute_timer_cb, attr); } /*! @@ -536,9 +536,9 @@ write_attribute(attribute_t *a, bool ignore_delay) a->id, a->update); goto done; - } else if (mainloop_timer_running(a->timer)) { + } else if (pcmk__main_loop_timer_running(a->timer)) { if (ignore_delay) { - mainloop_timer_stop(a->timer); + pcmk__main_loop_timer_stop(a->timer); pcmk__debug("Overriding '%s' write delay", a->id); } else { pcmk__info("Delaying write of '%s'", a->id); diff --git a/daemons/attrd/attrd_corosync.c b/daemons/attrd/attrd_corosync.c index 20519e0911d..e89b1fca5e1 100644 --- a/daemons/attrd/attrd_corosync.c +++ b/daemons/attrd/attrd_corosync.c @@ -361,7 +361,7 @@ update_attr_on_host(attribute_t *a, const pcmk__node_status_t *peer, if (a->timeout_ms && a->timer) { pcmk__trace("Delaying write of %s %s for dampening", attr, pcmk__readable_interval(a->timeout_ms)); - mainloop_timer_start(a->timer); + pcmk__main_loop_timer_start(a->timer); } else { attrd_write_or_elect_attribute(a); } diff --git a/daemons/attrd/attrd_sync.c b/daemons/attrd/attrd_sync.c index f9c7f3f9bba..2ae497c93d5 100644 --- a/daemons/attrd/attrd_sync.c +++ b/daemons/attrd/attrd_sync.c @@ -66,7 +66,7 @@ struct confirmation_action { * \brief A timer that will be used to remove the client should it time out * before receiving all confirmations */ - mainloop_timer_t *timer; + pcmk__main_loop_timer_t *timer; /*! * \brief A function to run when all confirmations have been received @@ -341,7 +341,7 @@ free_action(void *data) { struct confirmation_action *action = (struct confirmation_action *) data; g_list_free_full(action->respondents, free); - mainloop_timer_del(action->timer); + pcmk__main_loop_timer_free(action->timer); pcmk__xml_free(action->xml); free(action->client_id); free(action); @@ -513,8 +513,9 @@ attrd_expect_confirmations(pcmk__request_t *request, attrd_confirmation_action_f action->ipc_id = request->ipc_id; action->flags = request->flags; - action->timer = mainloop_timer_add(NULL, 15000, FALSE, confirmation_timeout_cb, action); - mainloop_timer_start(action->timer); + action->timer = pcmk__main_loop_timer_new("attrd_confirmation", 15000, + confirmation_timeout_cb, action); + pcmk__main_loop_timer_start(action->timer); pcmk__intkey_table_insert(expected_confirmations, callid, action); pcmk__trace("Callid %d now waiting on %u confirmations", callid, diff --git a/daemons/attrd/attrd_utils.c b/daemons/attrd/attrd_utils.c index 3025f197e76..b63d2b29a01 100644 --- a/daemons/attrd/attrd_utils.c +++ b/daemons/attrd/attrd_utils.c @@ -213,7 +213,7 @@ attrd_free_attribute(void *data) free(a->set_type); free(a->user); - mainloop_timer_del(a->timer); + pcmk__main_loop_timer_free(a->timer); g_hash_table_destroy(a->values); free(a); diff --git a/daemons/attrd/pacemaker-attrd.h b/daemons/attrd/pacemaker-attrd.h index 8d9b50ad408..1c6d2c5508b 100644 --- a/daemons/attrd/pacemaker-attrd.h +++ b/daemons/attrd/pacemaker-attrd.h @@ -141,7 +141,7 @@ typedef struct { int timeout_ms; // How long to wait for more changes before writing uint32_t flags; // Group of enum attrd_attr_flags GHashTable *values; // Key: node name, value: attribute_value_t - mainloop_timer_t *timer; // Timer to use for timeout_ms + pcmk__main_loop_timer_t *timer; // Timer to use for timeout_ms } attribute_t; #define attrd_set_attr_flags(attr, flags_to_set) do { \ @@ -236,7 +236,8 @@ extern int minimum_protocol_version; void attrd_remove_peer_protocol_ver(const char *host); void attrd_update_minimum_protocol_ver(const char *host, const char *value); -mainloop_timer_t *attrd_add_timer(const char *id, int timeout_ms, attribute_t *attr); +pcmk__main_loop_timer_t *attrd_add_timer(const char *id, int timeout_ms, + attribute_t *attr); void attrd_unregister_handlers(void); void attrd_handle_request(pcmk__request_t *request); diff --git a/daemons/based/based_callbacks.c b/daemons/based/based_callbacks.c index 71679d43275..44f6ea2f1d8 100644 --- a/daemons/based/based_callbacks.c +++ b/daemons/based/based_callbacks.c @@ -34,7 +34,7 @@ #include "pacemaker-based.h" -static mainloop_timer_t *digest_timer = NULL; +static pcmk__main_loop_timer_t *digest_timer = NULL; static long long ping_seq = 0; static char *ping_digest = NULL; static bool ping_modified_since = false; @@ -102,8 +102,8 @@ void based_callbacks_init(void) { if (digest_timer == NULL) { - digest_timer = mainloop_timer_add("based_digest_timer", 5000, false, - digest_timer_cb, NULL); + digest_timer = pcmk__main_loop_timer_new("based_digest_timer", 5000, + digest_timer_cb, NULL); } } @@ -114,7 +114,7 @@ based_callbacks_init(void) void based_callbacks_cleanup(void) { - g_clear_pointer(&digest_timer, mainloop_timer_del); + g_clear_pointer(&digest_timer, pcmk__main_loop_timer_free); g_clear_pointer(&ping_digest, free); } @@ -566,7 +566,7 @@ based_perform_op_rw(xmlNode *request, const cib__operation_t *operation, ping_modified_since = true; } - mainloop_timer_start(digest_timer); + pcmk__main_loop_timer_start(digest_timer); done: if (!pcmk__any_flags_set(call_options, diff --git a/daemons/based/based_io.c b/daemons/based/based_io.c index 5e6a12153ab..60f541eb8bb 100644 --- a/daemons/based/based_io.c +++ b/daemons/based/based_io.c @@ -45,13 +45,13 @@ static crm_trigger_t *write_trigger = NULL; * \internal * \brief Process the exit status of a child forked from \c write_cib_async() * - * \param[in] child Mainloop child data + * \param[in] child Main loop child * \param[in] core If set to 1, the child process dumped core * \param[in] signo Signal that the child process exited with * \param[in] exit_code Child process's exit code */ static void -write_cib_cb(mainloop_child_t *child, int core, int signo, int exit_code) +write_cib_cb(pcmk__main_loop_child_t *child, int core, int signo, int exit_code) { const char *error = "Could not write CIB to disk"; @@ -113,7 +113,8 @@ write_cib_async(void *user_data) if (pid > 0) { // Parent - mainloop_child_add(pid, 0, "disk-writer", NULL, write_cib_cb); + pcmk__main_loop_child_create(pid, "disk-writer", 0, NULL, true, + write_cib_cb); if (blackbox_state == QB_LOG_STATE_ENABLED) { qb_log_ctl(QB_LOG_BLACKBOX, QB_LOG_CONF_ENABLED, QB_TRUE); diff --git a/daemons/controld/controld_control.c b/daemons/controld/controld_control.c index 5d876436276..1dcc4fe8323 100644 --- a/daemons/controld/controld_control.c +++ b/daemons/controld/controld_control.c @@ -295,9 +295,7 @@ do_startup(long long action, enum crmd_fsa_cause cause, controld_execd_state_table_init(); controld_remote_proxy_table_init(); - if (!controld_init_fsa_timers()) { - register_fsa_error(I_ERROR, msg_data); - } + controld_init_fsa_timers(); } // \return libqb error code (0 on success, -errno on error) diff --git a/daemons/controld/controld_election.c b/daemons/controld/controld_election.c index 43d5f0f7e5f..57b4191f9e1 100644 --- a/daemons/controld/controld_election.c +++ b/daemons/controld/controld_election.c @@ -45,7 +45,7 @@ controld_configure_election(GHashTable *options) unsigned int interval_ms = 0; pcmk_parse_interval_spec(value, &interval_ms); - election_timeout_set_period(controld_globals.cluster, interval_ms); + election_timeout_set_interval(controld_globals.cluster, interval_ms); } void diff --git a/daemons/controld/controld_fencing.c b/daemons/controld/controld_fencing.c index c3df6d5d2d4..41d8d068a1c 100644 --- a/daemons/controld/controld_fencing.c +++ b/daemons/controld/controld_fencing.c @@ -386,7 +386,7 @@ controld_execute_fencing_cleanup(void) */ static stonith_t *fencer_api = NULL; -static mainloop_timer_t *controld_fencer_connect_timer = NULL; +static pcmk__main_loop_timer_t *controld_fencer_connect_timer = NULL; static char *te_client_id = NULL; static bool @@ -443,8 +443,8 @@ destroy_fencer_connection(stonith_t *st, stonith_event_t *e) if (pcmk__is_set(controld_globals.fsa_input_register, R_ST_REQUIRED)) { pcmk__err("Lost fencer connection (will attempt to reconnect)"); - if (!mainloop_timer_running(controld_fencer_connect_timer)) { - mainloop_timer_start(controld_fencer_connect_timer); + if (!pcmk__main_loop_timer_running(controld_fencer_connect_timer)) { + pcmk__main_loop_timer_start(controld_fencer_connect_timer); } } else { pcmk__info("Disconnected from fencer"); @@ -674,9 +674,9 @@ controld_timer_fencer_connect(void *user_data) if (controld_fencer_connect_timer == NULL) { controld_fencer_connect_timer = - mainloop_timer_add("controld_fencer_connect", 1000, - TRUE, controld_timer_fencer_connect, - GINT_TO_POINTER(TRUE)); + pcmk__main_loop_timer_new("controld_fencer_connect", 1000, + controld_timer_fencer_connect, + GINT_TO_POINTER(TRUE)); } if (rc != pcmk_ok) { @@ -686,8 +686,8 @@ controld_timer_fencer_connect(void *user_data) QB_XS " rc=%d", pcmk_strerror(rc), rc); - if (!mainloop_timer_running(controld_fencer_connect_timer)) { - mainloop_timer_start(controld_fencer_connect_timer); + if (!pcmk__main_loop_timer_running(controld_fencer_connect_timer)) { + pcmk__main_loop_timer_start(controld_fencer_connect_timer); } return G_SOURCE_CONTINUE; @@ -735,7 +735,8 @@ controld_disconnect_fencer(bool destroy) g_clear_pointer(&fencer_api, fencer_api->cmds->free); } - g_clear_pointer(&controld_fencer_connect_timer, mainloop_timer_del); + g_clear_pointer(&controld_fencer_connect_timer, + pcmk__main_loop_timer_free); g_clear_pointer(&te_client_id, free); } } @@ -1011,19 +1012,21 @@ controld_validate_fencing_watchdog_timeout(const char *value) */ static crm_trigger_t *fencing_history_sync_trigger = NULL; -static mainloop_timer_t *fencing_history_sync_timer_short = NULL; -static mainloop_timer_t *fencing_history_sync_timer_long = NULL; +static pcmk__main_loop_timer_t *fencing_history_sync_timer_short = NULL; +static pcmk__main_loop_timer_t *fencing_history_sync_timer_long = NULL; void controld_cleanup_fencing_history_sync(stonith_t *st, bool free_timers) { if (free_timers) { - g_clear_pointer(&fencing_history_sync_timer_short, mainloop_timer_del); - g_clear_pointer(&fencing_history_sync_timer_long, mainloop_timer_del); + g_clear_pointer(&fencing_history_sync_timer_short, + pcmk__main_loop_timer_free); + g_clear_pointer(&fencing_history_sync_timer_long, + pcmk__main_loop_timer_free); } else { - mainloop_timer_stop(fencing_history_sync_timer_short); - mainloop_timer_stop(fencing_history_sync_timer_long); + pcmk__main_loop_timer_stop(fencing_history_sync_timer_short); + pcmk__main_loop_timer_stop(fencing_history_sync_timer_long); } if (st) { @@ -1042,7 +1045,7 @@ static gboolean fencing_history_sync_set_trigger(void *user_data) { mainloop_set_trigger(fencing_history_sync_trigger); - return FALSE; + return G_SOURCE_REMOVE; } void @@ -1074,24 +1077,24 @@ controld_trigger_fencing_history_sync(bool long_timeout) if (long_timeout) { if (fencing_history_sync_timer_long == NULL) { fencing_history_sync_timer_long = - mainloop_timer_add("history_sync_long", 30000, - FALSE, fencing_history_sync_set_trigger, - NULL); + pcmk__main_loop_timer_new("history_sync_long", 30000, + fencing_history_sync_set_trigger, + NULL); } pcmk__info("Fence history will be synchronized cluster-wide within 30 " "seconds"); - mainloop_timer_start(fencing_history_sync_timer_long); + pcmk__main_loop_timer_start(fencing_history_sync_timer_long); } else { if (fencing_history_sync_timer_short == NULL) { fencing_history_sync_timer_short = - mainloop_timer_add("history_sync_short", 5000, - FALSE, fencing_history_sync_set_trigger, - NULL); + pcmk__main_loop_timer_new("history_sync_short", 5000, + fencing_history_sync_set_trigger, + NULL); } pcmk__info("Fence history will be synchronized cluster-wide within 5 " "seconds"); - mainloop_timer_start(fencing_history_sync_timer_short); + pcmk__main_loop_timer_start(fencing_history_sync_timer_short); } } diff --git a/daemons/controld/controld_remote_ra.c b/daemons/controld/controld_remote_ra.c index 1c2f0a89106..36d071dbdad 100644 --- a/daemons/controld/controld_remote_ra.c +++ b/daemons/controld/controld_remote_ra.c @@ -139,7 +139,8 @@ recurring_helper(void *data) ra_data->cmds = g_list_append(ra_data->cmds, cmd); mainloop_set_trigger(ra_data->work); } - return FALSE; + + return G_SOURCE_REMOVE; } static gboolean @@ -155,7 +156,8 @@ start_delay_helper(void *data) mainloop_set_trigger(ra_data->work); } - return FALSE; + + return G_SOURCE_REMOVE; } static bool @@ -471,11 +473,11 @@ retry_start_cmd_cb(void *data) int remaining = 0; if (!ra_data || !ra_data->cur_cmd) { - return FALSE; + return G_SOURCE_REMOVE; } cmd = ra_data->cur_cmd; if (!pcmk__is_up_action(cmd->action)) { - return FALSE; + return G_SOURCE_REMOVE; } remaining = remaining_timeout_sec(cmd); @@ -499,7 +501,7 @@ retry_start_cmd_cb(void *data) /* wait for connection event */ } - return FALSE; + return G_SOURCE_REMOVE; } @@ -517,7 +519,7 @@ connection_takeover_timeout_cb(void *data) handle_remote_ra_stop(lrm_state, cmd); free_cmd(cmd); - return FALSE; + return G_SOURCE_REMOVE; } static gboolean @@ -552,7 +554,7 @@ monitor_timeout_cb(void *data) // @TODO Should we move this before reporting the result above? lrm_state_disconnect(lrm_state); } - return FALSE; + return G_SOURCE_REMOVE; } static void diff --git a/daemons/controld/controld_schedulerd.c b/daemons/controld/controld_schedulerd.c index 3f7d3301ba3..7eba4cb4e90 100644 --- a/daemons/controld/controld_schedulerd.c +++ b/daemons/controld/controld_schedulerd.c @@ -25,7 +25,7 @@ static pcmk_ipc_api_t *schedulerd_api = NULL; -static mainloop_timer_t *controld_cib_retry_timer = NULL; +static pcmk__main_loop_timer_t *controld_cib_retry_timer = NULL; /*! * \internal @@ -235,7 +235,7 @@ do_pe_control(long long action, enum crmd_fsa_cause cause, } static int fsa_pe_query = 0; -static mainloop_timer_t *controld_sched_timer = NULL; +static pcmk__main_loop_timer_t *controld_sched_timer = NULL; // @TODO Make this a configurable cluster option if there's demand for it #define SCHED_TIMEOUT_MS (120000) @@ -246,7 +246,7 @@ static mainloop_timer_t *controld_sched_timer = NULL; * * \param[in] user_data Ignored * - * \return FALSE (indicating that timer should not be restarted) + * \return \c G_SOURCE_REMOVE (indicating that timer should not be restarted) */ static gboolean controld_sched_timeout(void *user_data) @@ -264,7 +264,8 @@ controld_sched_timeout(void *user_data) */ crmd_exit(CRM_EX_FATAL); } - return FALSE; + + return G_SOURCE_REMOVE; } void @@ -275,7 +276,8 @@ controld_stop_sched_timer(void) pcmk__trace("Stopping timer for scheduler reply %s", controld_globals.fsa_pe_ref); } - mainloop_timer_stop(controld_sched_timer); + + pcmk__main_loop_timer_stop(controld_sched_timer); } /*! @@ -291,12 +293,14 @@ controld_expect_sched_reply(char *ref) { if (ref) { if (controld_sched_timer == NULL) { - controld_sched_timer = mainloop_timer_add("scheduler_reply_timer", - SCHED_TIMEOUT_MS, FALSE, - controld_sched_timeout, - NULL); + controld_sched_timer = + pcmk__main_loop_timer_new("scheduler_reply_timer", + SCHED_TIMEOUT_MS, + controld_sched_timeout, NULL); } - mainloop_timer_start(controld_sched_timer); + + pcmk__main_loop_timer_start(controld_sched_timer); + } else { controld_stop_sched_timer(); } @@ -311,7 +315,7 @@ controld_expect_sched_reply(char *ref) void controld_free_sched_timer(void) { - g_clear_pointer(&controld_sched_timer, mainloop_timer_del); + g_clear_pointer(&controld_sched_timer, pcmk__main_loop_timer_free); } // A_PE_INVOKE @@ -450,7 +454,7 @@ sleep_timer(void *data) { controld_set_fsa_action_flags(A_PE_INVOKE); controld_trigger_fsa(); - g_clear_pointer(&controld_cib_retry_timer, mainloop_timer_del); + g_clear_pointer(&controld_cib_retry_timer, pcmk__main_loop_timer_free); return G_SOURCE_REMOVE; } @@ -487,9 +491,9 @@ do_pe_invoke_callback(xmlNode * msg, int call_id, int rc, xmlNode * output, void pcmk__debug("Re-asking for the CIB: %d other peer updates still " "pending", (num_cib_op_callbacks() - 1)); - controld_cib_retry_timer = mainloop_timer_add("cib_retry", 1000, false, - sleep_timer, NULL); - mainloop_timer_start(controld_cib_retry_timer); + controld_cib_retry_timer = pcmk__main_loop_timer_new("cib_retry", 1000, + sleep_timer, NULL); + pcmk__main_loop_timer_start(controld_cib_retry_timer); return; } diff --git a/daemons/controld/controld_te_actions.c b/daemons/controld/controld_te_actions.c index befce6d4ff1..dabc6dd672b 100644 --- a/daemons/controld/controld_te_actions.c +++ b/daemons/controld/controld_te_actions.c @@ -728,7 +728,7 @@ notify_crmd(pcmk__graph_t *graph) case pcmk__graph_restart: type = "restart"; if (controld_globals.fsa_state == S_TRANSITION_ENGINE) { - if (controld_get_period_transition_timer() > 0) { + if (controld_get_interval_transition_timer() > 0) { controld_stop_transition_timer(); controld_start_transition_timer(); } else { diff --git a/daemons/controld/controld_te_callbacks.c b/daemons/controld/controld_te_callbacks.c index 3be6c120ce7..217183f6d68 100644 --- a/daemons/controld/controld_te_callbacks.c +++ b/daemons/controld/controld_te_callbacks.c @@ -480,7 +480,7 @@ cib_action_updated(xmlNode * msg, int call_id, int rc, xmlNode * output, void *u * * \param[in,out] data Pointer to graph action * - * \return FALSE (indicating that source should be not be re-added) + * \return \c G_SOURCE_REMOVE (indicating that source should be not be re-added) */ gboolean action_timer_callback(void *data) @@ -490,7 +490,7 @@ action_timer_callback(void *data) const char *on_node = NULL; const char *via_node = NULL; - CRM_CHECK(data != NULL, return FALSE); + CRM_CHECK(data != NULL, return G_SOURCE_REMOVE); stop_te_timer(action); @@ -527,5 +527,5 @@ action_timer_callback(void *data) } } - return FALSE; + return G_SOURCE_REMOVE; } diff --git a/daemons/controld/controld_te_utils.c b/daemons/controld/controld_te_utils.c index 1e04bde99fa..05558238ea9 100644 --- a/daemons/controld/controld_te_utils.c +++ b/daemons/controld/controld_te_utils.c @@ -139,8 +139,9 @@ abort_timer_popped(void *data) abort_transition(abort_timer->priority, abort_timer->action, abort_timer->text, NULL); } + abort_timer->id = 0; - return FALSE; // do not immediately reschedule timer + return G_SOURCE_REMOVE; // do not immediately reschedule timer } /*! @@ -183,12 +184,12 @@ node_pending_timer_popped(void *key) struct abort_timer_s *node_pending_timer = NULL; if (node_pending_timers == NULL) { - return FALSE; + return G_SOURCE_REMOVE; } node_pending_timer = g_hash_table_lookup(node_pending_timers, key); if (node_pending_timer == NULL) { - return FALSE; + return G_SOURCE_REMOVE; } pcmk__warn("Node with " PCMK_XA_ID " '%s' pending timed out (%us) on " @@ -201,7 +202,7 @@ node_pending_timer_popped(void *key) g_hash_table_remove(node_pending_timers, key); - return FALSE; // do not reschedule timer + return G_SOURCE_REMOVE; // do not reschedule timer } static void @@ -490,7 +491,7 @@ abort_transition_graph(int abort_priority, enum pcmk__graph_next abort_action, done: if (controld_globals.transition_graph->complete) { - if (controld_get_period_transition_timer() > 0) { + if (controld_get_interval_transition_timer() > 0) { controld_stop_transition_timer(); controld_start_transition_timer(); } else { diff --git a/daemons/controld/controld_throttle.c b/daemons/controld/controld_throttle.c index 05a67207009..ad838aef84a 100644 --- a/daemons/controld/controld_throttle.c +++ b/daemons/controld/controld_throttle.c @@ -47,7 +47,7 @@ static float throttle_load_target = 0.0; #define THROTTLE_FACTOR_HIGH 2.0 static GHashTable *throttle_records = NULL; -static mainloop_timer_t *throttle_timer = NULL; +static pcmk__main_loop_timer_t *throttle_timer = NULL; static const char * load2str(enum throttle_state_e mode) @@ -202,7 +202,7 @@ static gboolean throttle_timer_cb(void *data) { throttle_send_command(throttle_mode()); - return TRUE; + return G_SOURCE_CONTINUE; } static void @@ -262,11 +262,12 @@ throttle_init(void) { if(throttle_records == NULL) { throttle_records = pcmk__strkey_table(NULL, throttle_record_free); - throttle_timer = mainloop_timer_add("throttle", 30 * 1000, TRUE, throttle_timer_cb, NULL); + throttle_timer = pcmk__main_loop_timer_new("throttle", (30 * 1000), + throttle_timer_cb, NULL); } throttle_update_job_max(NULL); - mainloop_timer_start(throttle_timer); + pcmk__main_loop_timer_start(throttle_timer); } /*! @@ -291,7 +292,7 @@ controld_configure_throttle(GHashTable *options) void throttle_fini(void) { - g_clear_pointer(&throttle_timer, mainloop_timer_del); + g_clear_pointer(&throttle_timer, pcmk__main_loop_timer_free); g_clear_pointer(&throttle_records, g_hash_table_destroy); } diff --git a/daemons/controld/controld_timers.c b/daemons/controld/controld_timers.c index 90138ee716c..099077d011d 100644 --- a/daemons/controld/controld_timers.c +++ b/daemons/controld/controld_timers.c @@ -20,12 +20,12 @@ //! FSA mainloop timer type typedef struct { - unsigned int source_id; //!< Timer source ID - unsigned int period_ms; //!< Timer period - enum crmd_fsa_input fsa_input; //!< Input to register if timer pops - gboolean (*callback)(void *data); //!< What do if timer pops - bool log_error; //!< Timer popping indicates error - int counter; //!< For detecting loops + const char *desc; //!< Description + unsigned int source_id; //!< Timer source ID + unsigned int interval_ms; //!< Timer interval + enum crmd_fsa_input fsa_input; //!< Input to register if timer pops + bool log_error; //!< Timer popping indicates error + int counter; //!< For detecting loops } fsa_timer_t; //! Wait before retrying a failed cib or executor connection @@ -52,34 +52,6 @@ fsa_timer_t *shutdown_escalation_timer = NULL; //! Cluster recheck interval (from configuration) static unsigned int recheck_interval_ms = 0; -static const char * -get_timer_desc(fsa_timer_t * timer) -{ - if (timer == election_timer) { - return "Election Trigger"; - - } else if (timer == shutdown_escalation_timer) { - return "Shutdown Escalation"; - - } else if (timer == integration_timer) { - return "Integration Timer"; - - } else if (timer == finalization_timer) { - return "Finalization Timer"; - - } else if (timer == transition_timer) { - return "New Transition Timer"; - - } else if (timer == wait_timer) { - return "Wait Timer"; - - } else if (timer == recheck_timer) { - return "Cluster Recheck Timer"; - - } - return "Unknown Timer"; -} - /*! * \internal * \brief Stop an FSA timer @@ -95,20 +67,76 @@ controld_stop_timer(fsa_timer_t *timer) if (timer->source_id != 0) { pcmk__trace("Stopping %s (would inject %s if popped after %ums, " - "src=%d)", - get_timer_desc(timer), fsa_input2string(timer->fsa_input), - timer->period_ms, timer->source_id); + "src=%d)", timer->desc, fsa_input2string(timer->fsa_input), + timer->interval_ms, timer->source_id); g_source_remove(timer->source_id); timer->source_id = 0; return true; } pcmk__trace("%s already stopped (would inject %s if popped after %ums)", - get_timer_desc(timer), fsa_input2string(timer->fsa_input), - timer->period_ms); + timer->desc, fsa_input2string(timer->fsa_input), + timer->interval_ms); return false; } +static gboolean +crm_timer_popped(void *data) +{ + fsa_timer_t *timer = (fsa_timer_t *) data; + + if (timer->log_error) { + pcmk__err("%s just popped in state %s! " QB_XS " input=%s time=%ums", + timer->desc, fsa_state2string(controld_globals.fsa_state), + fsa_input2string(timer->fsa_input), timer->interval_ms); + } else { + pcmk__info("%s just popped " QB_XS " input=%s time=%ums", timer->desc, + fsa_input2string(timer->fsa_input), timer->interval_ms); + timer->counter++; + } + + if ((timer == election_timer) && (election_timer->counter > 5)) { + pcmk__notice("We appear to be in an election loop, something may be " + "wrong"); + crm_write_blackbox(0, NULL); + election_timer->counter = 0; + } + + controld_stop_timer(timer); // Make timer _not_ go off again + + if (timer->fsa_input == I_INTEGRATED) { + pcmk__info("Welcomed: %d, Integrated: %d", + crmd_join_phase_count(controld_join_welcomed), + crmd_join_phase_count(controld_join_integrated)); + if (crmd_join_phase_count(controld_join_welcomed) == 0) { + // If we don't even have ourselves, start again + register_fsa_error(I_ELECTION, NULL); + + } else { + controld_fsa_prepend(C_TIMER_POPPED, timer->fsa_input, NULL); + } + + } else if ((timer == recheck_timer) + && (controld_globals.fsa_state != S_IDLE)) { + pcmk__debug("Discarding %s event in state: %s", + fsa_input2string(timer->fsa_input), + fsa_state2string(controld_globals.fsa_state)); + + } else if ((timer == finalization_timer) + && (controld_globals.fsa_state != S_FINALIZE_JOIN)) { + pcmk__debug("Discarding %s event in state: %s", + fsa_input2string(timer->fsa_input), + fsa_state2string(controld_globals.fsa_state)); + + } else if (timer->fsa_input != I_NULL) { + controld_fsa_append(C_TIMER_POPPED, timer->fsa_input, NULL); + } + + controld_trigger_fsa(); + + return G_SOURCE_CONTINUE; +} + /*! * \internal * \brief Start an FSA timer @@ -118,17 +146,18 @@ controld_stop_timer(fsa_timer_t *timer) static void controld_start_timer(fsa_timer_t *timer) { - if (timer->source_id == 0 && timer->period_ms > 0) { - timer->source_id = pcmk__create_timer(timer->period_ms, timer->callback, timer); + if ((timer->source_id == 0) && (timer->interval_ms > 0)) { + timer->source_id = pcmk__create_timer(timer->interval_ms, + crm_timer_popped, timer); pcmk__assert(timer->source_id != 0); pcmk__debug("Started %s (inject %s if pops after %ums, source=%d)", - get_timer_desc(timer), fsa_input2string(timer->fsa_input), - timer->period_ms, timer->source_id); + timer->desc, fsa_input2string(timer->fsa_input), + timer->interval_ms, timer->source_id); } else { pcmk__debug("%s already running (inject %s if pops after %ums, " - "source=%d)", - get_timer_desc(timer), fsa_input2string(timer->fsa_input), - timer->period_ms, timer->source_id); + "source=%d)", timer->desc, + fsa_input2string(timer->fsa_input), timer->interval_ms, + timer->source_id); } } @@ -192,99 +221,55 @@ do_timer_control(long long action, enum crmd_fsa_cause cause, } } -static gboolean -crm_timer_popped(void *data) +/*! + * \internal + * \brief Create an FSA timer + * + * \param[in] desc Description + * \param[in] interval_ms Timer interval in milliseconds + * \param[in] fsa_input FSA input to register if the timer pops + * \param[in] log_error If \c true, log an error if the timer pops + * + * \return Newly allocated FSA timer (guaranteed not to be \c NULL) + * + * \note The caller is responsible for freeing the return value using + * \c free_fsa_timer(). + */ +static fsa_timer_t * +new_fsa_timer(const char *desc, unsigned int interval_ms, + enum crmd_fsa_input fsa_input, bool log_error) { - fsa_timer_t *timer = (fsa_timer_t *) data; - - if (timer->log_error) { - pcmk__err("%s just popped in state %s! " QB_XS " input=%s time=%ums", - get_timer_desc(timer), - fsa_state2string(controld_globals.fsa_state), - fsa_input2string(timer->fsa_input), timer->period_ms); - } else { - pcmk__info("%s just popped " QB_XS " input=%s time=%ums", - get_timer_desc(timer), fsa_input2string(timer->fsa_input), - timer->period_ms); - timer->counter++; - } - - if ((timer == election_timer) && (election_timer->counter > 5)) { - pcmk__notice("We appear to be in an election loop, something may be " - "wrong"); - crm_write_blackbox(0, NULL); - election_timer->counter = 0; - } - - controld_stop_timer(timer); // Make timer _not_ go off again - - if (timer->fsa_input == I_INTEGRATED) { - pcmk__info("Welcomed: %d, Integrated: %d", - crmd_join_phase_count(controld_join_welcomed), - crmd_join_phase_count(controld_join_integrated)); - if (crmd_join_phase_count(controld_join_welcomed) == 0) { - // If we don't even have ourselves, start again - register_fsa_error(I_ELECTION, NULL); - - } else { - controld_fsa_prepend(C_TIMER_POPPED, timer->fsa_input, NULL); - } + fsa_timer_t *timer = pcmk__assert_alloc(1, sizeof(fsa_timer_t)); - } else if ((timer == recheck_timer) - && (controld_globals.fsa_state != S_IDLE)) { - pcmk__debug("Discarding %s event in state: %s", - fsa_input2string(timer->fsa_input), - fsa_state2string(controld_globals.fsa_state)); + timer->desc = desc; + timer->interval_ms = interval_ms; + timer->fsa_input = fsa_input; + timer->log_error = log_error; - } else if ((timer == finalization_timer) - && (controld_globals.fsa_state != S_FINALIZE_JOIN)) { - pcmk__debug("Discarding %s event in state: %s", - fsa_input2string(timer->fsa_input), - fsa_state2string(controld_globals.fsa_state)); + return timer; +} - } else if (timer->fsa_input != I_NULL) { - controld_fsa_append(C_TIMER_POPPED, timer->fsa_input, NULL); +/*! + * \internal + * \return Free an FSA timer + * + * \param[in,out] timer FSA timer + */ +static void +free_fsa_timer(fsa_timer_t *timer) +{ + if (timer == NULL) { + return; } - controld_trigger_fsa(); - - return TRUE; + controld_stop_timer(timer); + free(timer); } -bool +void controld_init_fsa_timers(void) { - transition_timer = pcmk__assert_alloc(1, sizeof(fsa_timer_t)); - integration_timer = pcmk__assert_alloc(1, sizeof(fsa_timer_t)); - finalization_timer = pcmk__assert_alloc(1, sizeof(fsa_timer_t)); - election_timer = pcmk__assert_alloc(1, sizeof(fsa_timer_t)); - shutdown_escalation_timer = pcmk__assert_alloc(1, sizeof(fsa_timer_t)); - wait_timer = pcmk__assert_alloc(1, sizeof(fsa_timer_t)); - recheck_timer = pcmk__assert_alloc(1, sizeof(fsa_timer_t)); - - election_timer->source_id = 0; - election_timer->period_ms = 0; - election_timer->fsa_input = I_DC_TIMEOUT; - election_timer->callback = crm_timer_popped; - election_timer->log_error = FALSE; - - transition_timer->source_id = 0; - transition_timer->period_ms = 0; - transition_timer->fsa_input = I_PE_CALC; - transition_timer->callback = crm_timer_popped; - transition_timer->log_error = FALSE; - - integration_timer->source_id = 0; - integration_timer->period_ms = 0; - integration_timer->fsa_input = I_INTEGRATED; - integration_timer->callback = crm_timer_popped; - integration_timer->log_error = TRUE; - - finalization_timer->source_id = 0; - finalization_timer->period_ms = 0; - finalization_timer->fsa_input = I_FINALIZED; - finalization_timer->callback = crm_timer_popped; - finalization_timer->log_error = FALSE; + election_timer = new_fsa_timer("Election Trigger", 0, I_DC_TIMEOUT, false); /* We can't use I_FINALIZED here, because that creates a bug in the join * process where a joining node can be stuck in S_PENDING while we think it @@ -295,27 +280,21 @@ controld_init_fsa_timers(void) * not, we can avoid this causing an election/join loop, in the integration * phase. */ - finalization_timer->fsa_input = I_ELECTION; - - shutdown_escalation_timer->source_id = 0; - shutdown_escalation_timer->period_ms = 0; - shutdown_escalation_timer->fsa_input = I_STOP; - shutdown_escalation_timer->callback = crm_timer_popped; - shutdown_escalation_timer->log_error = TRUE; - - wait_timer->source_id = 0; - wait_timer->period_ms = 2000; - wait_timer->fsa_input = I_NULL; - wait_timer->callback = crm_timer_popped; - wait_timer->log_error = FALSE; - - recheck_timer->source_id = 0; - recheck_timer->period_ms = 0; - recheck_timer->fsa_input = I_PE_CALC; - recheck_timer->callback = crm_timer_popped; - recheck_timer->log_error = FALSE; - - return TRUE; + finalization_timer = new_fsa_timer("Finalization Timer", 0, I_ELECTION, + false); + + integration_timer = new_fsa_timer("Integration Timer", 0, I_INTEGRATED, + true); + + recheck_timer = new_fsa_timer("Cluster Recheck Timer", 0, I_PE_CALC, false); + + shutdown_escalation_timer = new_fsa_timer("Shutdown Escalation", 0, I_STOP, + true); + + transition_timer = new_fsa_timer("New Transition Timer", 0, I_PE_CALC, + false); + + wait_timer = new_fsa_timer("Wait Timer", 2000, I_NULL, false); } /*! @@ -331,26 +310,25 @@ controld_configure_fsa_timers(GHashTable *options) // Election timer value = g_hash_table_lookup(options, PCMK_OPT_DC_DEADTIME); - pcmk_parse_interval_spec(value, &election_timer->period_ms); + pcmk_parse_interval_spec(value, &election_timer->interval_ms); // Integration timer value = g_hash_table_lookup(options, PCMK_OPT_JOIN_INTEGRATION_TIMEOUT); - pcmk_parse_interval_spec(value, &integration_timer->period_ms); + pcmk_parse_interval_spec(value, &integration_timer->interval_ms); // Finalization timer value = g_hash_table_lookup(options, PCMK_OPT_JOIN_FINALIZATION_TIMEOUT); - pcmk_parse_interval_spec(value, &finalization_timer->period_ms); + pcmk_parse_interval_spec(value, &finalization_timer->interval_ms); // Shutdown escalation timer value = g_hash_table_lookup(options, PCMK_OPT_SHUTDOWN_ESCALATION); - pcmk_parse_interval_spec(value, &shutdown_escalation_timer->period_ms); + pcmk_parse_interval_spec(value, &shutdown_escalation_timer->interval_ms); pcmk__debug("Shutdown escalation occurs if DC has not responded to request " - "in %ums", - shutdown_escalation_timer->period_ms); + "in %ums", shutdown_escalation_timer->interval_ms); // Transition timer value = g_hash_table_lookup(options, PCMK_OPT_TRANSITION_DELAY); - pcmk_parse_interval_spec(value, &transition_timer->period_ms); + pcmk_parse_interval_spec(value, &transition_timer->interval_ms); // Recheck interval value = g_hash_table_lookup(options, PCMK_OPT_CLUSTER_RECHECK_INTERVAL); @@ -362,21 +340,13 @@ controld_configure_fsa_timers(GHashTable *options) void controld_free_fsa_timers(void) { - controld_stop_timer(transition_timer); - controld_stop_timer(integration_timer); - controld_stop_timer(finalization_timer); - controld_stop_timer(election_timer); - controld_stop_timer(shutdown_escalation_timer); - controld_stop_timer(wait_timer); - controld_stop_timer(recheck_timer); - - g_clear_pointer(&transition_timer, free); - g_clear_pointer(&integration_timer, free); - g_clear_pointer(&finalization_timer, free); - g_clear_pointer(&election_timer, free); - g_clear_pointer(&shutdown_escalation_timer, free); - g_clear_pointer(&wait_timer, free); - g_clear_pointer(&recheck_timer, free); + g_clear_pointer(&election_timer, free_fsa_timer); + g_clear_pointer(&finalization_timer, free_fsa_timer); + g_clear_pointer(&integration_timer, free_fsa_timer); + g_clear_pointer(&recheck_timer, free_fsa_timer); + g_clear_pointer(&shutdown_escalation_timer, free_fsa_timer); + g_clear_pointer(&transition_timer, free_fsa_timer); + g_clear_pointer(&wait_timer, free_fsa_timer); } /*! @@ -387,7 +357,7 @@ controld_free_fsa_timers(void) bool controld_is_started_transition_timer(void) { - return (transition_timer->period_ms > 0) + return (transition_timer->interval_ms > 0) && (transition_timer->source_id != 0); } @@ -399,7 +369,7 @@ void controld_start_recheck_timer(void) { // Default to recheck interval configured in CIB (if any) - unsigned int period_ms = recheck_interval_ms; + unsigned int interval_ms = recheck_interval_ms; // If scheduler supplied a "recheck by" time, check whether that's sooner if (controld_globals.transition_graph->recheck_by > 0) { @@ -408,19 +378,20 @@ controld_start_recheck_timer(void) if (diff_seconds < 1) { // We're already past the desired time - period_ms = 500; + interval_ms = 500; } else { - period_ms = (unsigned int) QB_MIN(UINT_MAX, diff_seconds * 1000LL); + interval_ms = (unsigned int) QB_MIN(UINT_MAX, + (diff_seconds * 1000LL)); } // Use "recheck by" only if it's sooner than interval from CIB - if (period_ms > recheck_interval_ms) { - period_ms = recheck_interval_ms; + if (interval_ms > recheck_interval_ms) { + interval_ms = recheck_interval_ms; } } - if (period_ms > 0) { - recheck_timer->period_ms = period_ms; + if (interval_ms > 0) { + recheck_timer->interval_ms = interval_ms; controld_start_timer(recheck_timer); } } @@ -448,13 +419,15 @@ controld_stop_recheck_timer(void) } /*! - * \brief Get the transition timer's configured period - * \return The transition_timer's period + * \internal + * \brief Get the transition timer's configured interval + * + * \return The transition_timer's interval */ unsigned int -controld_get_period_transition_timer(void) +controld_get_interval_transition_timer(void) { - return transition_timer->period_ms; + return transition_timer->interval_ms; } /*! @@ -493,17 +466,17 @@ controld_start_transition_timer(void) * \internal * \brief Start the countdown sequence for a shutdown * - * \param[in] default_period_ms Period to use if the shutdown escalation - * timer's period is 0 + * \param[in] default_interval_ms Interval to use if the shutdown escalation + * timer's interval is 0 */ void -controld_shutdown_start_countdown(unsigned int default_period_ms) +controld_shutdown_start_countdown(unsigned int default_interval_ms) { - if (shutdown_escalation_timer->period_ms == 0) { - shutdown_escalation_timer->period_ms = default_period_ms; + if (shutdown_escalation_timer->interval_ms == 0) { + shutdown_escalation_timer->interval_ms = default_interval_ms; } pcmk__notice("Initiating controller shutdown sequence " QB_XS " limit=%ums", - shutdown_escalation_timer->period_ms); + shutdown_escalation_timer->interval_ms); controld_start_timer(shutdown_escalation_timer); } diff --git a/daemons/controld/controld_timers.h b/daemons/controld/controld_timers.h index 0baf5311ee4..e824f73cf70 100644 --- a/daemons/controld/controld_timers.h +++ b/daemons/controld/controld_timers.h @@ -14,7 +14,7 @@ # include // GHashTable # include // crmd_fsa_input -bool controld_init_fsa_timers(void); +void controld_init_fsa_timers(void); void controld_free_fsa_timers(void); void controld_configure_fsa_timers(GHashTable *options); @@ -27,7 +27,7 @@ void controld_start_wait_timer(void); bool controld_is_started_transition_timer(void); -unsigned int controld_get_period_transition_timer(void); +unsigned int controld_get_interval_transition_timer(void); void controld_reset_counter_election_timer(void); diff --git a/daemons/execd/cts-exec-helper.c b/daemons/execd/cts-exec-helper.c index 8508da6ef51..d5a46fb257d 100644 --- a/daemons/execd/cts-exec-helper.c +++ b/daemons/execd/cts-exec-helper.c @@ -223,7 +223,7 @@ timeout_err(void *data) { print_result("LISTEN EVENT FAILURE - timeout occurred, never found"); test_exit(CRM_EX_TIMEOUT); - return FALSE; + return G_SOURCE_REMOVE; } static void diff --git a/daemons/execd/execd_commands.c b/daemons/execd/execd_commands.c index 89d77d6ec08..67e8ab4057d 100644 --- a/daemons/execd/execd_commands.c +++ b/daemons/execd/execd_commands.c @@ -388,7 +388,7 @@ stonith_recurring_op_helper(void *data) cmd->stonith_recurring_id = 0; if (!cmd->rsc_id) { - return FALSE; + return G_SOURCE_REMOVE; } rsc = g_hash_table_lookup(rsc_list, cmd->rsc_id); @@ -403,7 +403,7 @@ stonith_recurring_op_helper(void *data) #endif mainloop_set_trigger(rsc->work); - return FALSE; + return G_SOURCE_REMOVE; } static inline void @@ -431,7 +431,7 @@ start_delay_helper(void *data) mainloop_set_trigger(rsc->work); } - return FALSE; + return G_SOURCE_REMOVE; } /*! diff --git a/daemons/execd/remoted_schemas.c b/daemons/execd/remoted_schemas.c index da4fece9267..68baae6bfe8 100644 --- a/daemons/execd/remoted_schemas.c +++ b/daemons/execd/remoted_schemas.c @@ -201,7 +201,7 @@ get_schema_files(void) * saving them to disk. */ static void -get_schema_files_complete(mainloop_child_t *p, int core, int signo, +get_schema_files_complete(pcmk__main_loop_child_t *p, int core, int signo, int exitcode) { const char *errmsg = "Could not load additional schema files"; @@ -248,7 +248,7 @@ remoted_request_cib_schema_files(void) * directory. */ if (schema_fetch_pid != 0) { - if (mainloop_child_kill(schema_fetch_pid) == FALSE) { + if (!pcmk__main_loop_child_kill(schema_fetch_pid)) { pcmk__warn("Unable to kill pre-existing schema-fetch process"); return; } @@ -287,9 +287,10 @@ remoted_request_cib_schema_files(void) default: /* parent */ schema_fetch_pid = pid; - mainloop_child_add_with_flags(pid, 5 * 60 * 1000, "schema-fetch", NULL, - mainloop_leave_pid_group, - get_schema_files_complete); + + // Five-minute timeout + pcmk__main_loop_child_create(pid, "schema-fetch", 300000, NULL, + false, get_schema_files_complete); break; } } diff --git a/daemons/execd/remoted_tls.c b/daemons/execd/remoted_tls.c index af28efc657c..98bd27cebb1 100644 --- a/daemons/execd/remoted_tls.c +++ b/daemons/execd/remoted_tls.c @@ -195,13 +195,13 @@ lrmd_auth_timeout_cb(void *data) client->remote->auth_timeout = 0; if (pcmk__is_set(client->flags, pcmk__client_tls_handshake_complete)) { - return FALSE; + return G_SOURCE_REMOVE; } g_clear_pointer(&client->remote->source, mainloop_del_fd); pcmk__err("Remote client authentication timed out"); - return FALSE; + return G_SOURCE_REMOVE; } // Dispatch callback for remote server socket diff --git a/daemons/fenced/fenced_commands.c b/daemons/fenced/fenced_commands.c index b6dd543f5fc..82c30700cdd 100644 --- a/daemons/fenced/fenced_commands.c +++ b/daemons/fenced/fenced_commands.c @@ -629,7 +629,6 @@ static int get_agent_metadata_cb(void *data) { fenced_device_t *device = data; - unsigned int period_ms = 0; int rc = get_agent_metadata(device->agent, &device->agent_metadata); if (rc == pcmk_rc_ok) { @@ -643,11 +642,14 @@ get_agent_metadata_cb(void *data) } if (rc == EAGAIN) { - period_ms = pcmk__mainloop_timer_get_period(device->timer); - if (period_ms < 160 * 1000) { - mainloop_timer_set_period(device->timer, 2 * period_ms); + if (device->timer->interval_ms < (160 * 1000)) { + device->timer->interval_ms *= 2; } + /* @FIXME Does the updated interval even take effect? G_SOURCE_CONTINUE + * tells main_loop_timer_cb() to keep the existing GSource. It seems as + * if that GSource would still use the old interval. + */ return G_SOURCE_CONTINUE; } @@ -810,7 +812,7 @@ start_delay_helper(void *data) mainloop_set_trigger(device->work); } - return FALSE; + return G_SOURCE_REMOVE; } static void @@ -909,8 +911,8 @@ free_device(void *data) g_list_free_full(device->targets, free); if (device->timer != NULL) { - mainloop_timer_stop(device->timer); - mainloop_timer_del(device->timer); + pcmk__main_loop_timer_stop(device->timer); + pcmk__main_loop_timer_free(device->timer); } mainloop_destroy_trigger(device->work); @@ -1193,13 +1195,14 @@ build_device_from_xml(const xmlNode *dev) } else if (rc == EAGAIN) { if (device->timer == NULL) { - device->timer = mainloop_timer_add("get_agent_metadata", 10 * 1000, - TRUE, get_agent_metadata_cb, - device); + device->timer = pcmk__main_loop_timer_new("get_agent_metadata", + (10 * 1000), + get_agent_metadata_cb, + device); } - if (!mainloop_timer_running(device->timer)) { - mainloop_timer_start(device->timer); + if (!pcmk__main_loop_timer_running(device->timer)) { + pcmk__main_loop_timer_start(device->timer); } } diff --git a/daemons/fenced/pacemaker-fenced.h b/daemons/fenced/pacemaker-fenced.h index 6002c2f3601..810b3f9fc9c 100644 --- a/daemons/fenced/pacemaker-fenced.h +++ b/daemons/fenced/pacemaker-fenced.h @@ -143,7 +143,7 @@ typedef struct { GHashTable *params; GHashTable *aliases; GList *pending_ops; - mainloop_timer_t *timer; + pcmk__main_loop_timer_t *timer; crm_trigger_t *work; xmlNode *agent_metadata; const char *default_host_arg; diff --git a/daemons/pacemakerd/pcmkd_corosync.c b/daemons/pacemakerd/pcmkd_corosync.c index e085789d0cc..1eac33b0c22 100644 --- a/daemons/pacemakerd/pcmkd_corosync.c +++ b/daemons/pacemakerd/pcmkd_corosync.c @@ -34,7 +34,7 @@ #include "pcmkd_corosync.h" static corosync_cfg_handle_t cfg_handle = 0; -static mainloop_timer_t *reconnect_timer = NULL; +static pcmk__main_loop_timer_t *reconnect_timer = NULL; /* =::=::=::= CFG - Shutdown stuff =::=::=::= */ @@ -96,7 +96,7 @@ static gboolean cluster_reconnect_cb(void *data) { if (cluster_connect_cfg()) { - g_clear_pointer(&reconnect_timer, mainloop_timer_del); + g_clear_pointer(&reconnect_timer, pcmk__main_loop_timer_free); pcmk__notice("Cluster reconnect succeeded"); pacemakerd_read_config(); restart_cluster_subdaemons(); @@ -120,8 +120,10 @@ cfg_connection_destroy(void *user_data) "reattempted once per second)"); corosync_cfg_finalize(cfg_handle); cfg_handle = 0; - reconnect_timer = mainloop_timer_add("corosync reconnect", 1000, TRUE, cluster_reconnect_cb, NULL); - mainloop_timer_start(reconnect_timer); + reconnect_timer = pcmk__main_loop_timer_new("pcmkd_corosync_reconnect", + 1000, cluster_reconnect_cb, + NULL); + pcmk__main_loop_timer_start(reconnect_timer); } void @@ -132,7 +134,7 @@ cluster_disconnect_cfg(void) /* The mainloop should be gone by this point, so this isn't necessary, but * cleaning up memory should make valgrind happier. */ - g_clear_pointer(&reconnect_timer, mainloop_timer_del); + g_clear_pointer(&reconnect_timer, pcmk__main_loop_timer_free); } #define cs_repeat(counter, max, code) do { \ diff --git a/daemons/pacemakerd/pcmkd_subdaemons.c b/daemons/pacemakerd/pcmkd_subdaemons.c index 63152d29df0..b29af46948e 100644 --- a/daemons/pacemakerd/pcmkd_subdaemons.c +++ b/daemons/pacemakerd/pcmkd_subdaemons.c @@ -104,7 +104,7 @@ static bool fatal_error = false; static int child_liveness(pcmkd_child_t *child); static gboolean escalate_shutdown(void *data); static int start_child(pcmkd_child_t *child); -static void pcmk_child_exit(mainloop_child_t *p, int core, int signo, +static void pcmk_child_exit(pcmk__main_loop_child_t *p, int core, int signo, int exitcode); static void pcmk_process_exit(pcmkd_child_t *child); static gboolean pcmk_shutdown_worker(void *user_data); @@ -254,30 +254,28 @@ escalate_shutdown(void *data) } static void -pcmk_child_exit(mainloop_child_t *p, int core, int signo, int exitcode) +pcmk_child_exit(pcmk__main_loop_child_t *p, int core, int signo, int exitcode) { - pcmkd_child_t *child = mainloop_child_userdata(p); - const char *name = mainloop_child_name(p); + pcmkd_child_t *child = p->user_data; if (signo) { // cts-lab looks for this message do_crm_log(((signo == SIGKILL)? LOG_WARNING : LOG_ERR), - "%s[%d] terminated with signal %d (%s)%s", - name, p->pid, signo, strsignal(signo), - (core? " and dumped core" : "")); + "%s[%d] terminated with signal %d (%s)%s", p->desc, p->pid, + signo, strsignal(signo), (core? " and dumped core" : "")); pcmk_process_exit(child); return; } switch(exitcode) { case CRM_EX_OK: - pcmk__info("%s[%d] exited with status %d (%s)", name, p->pid, + pcmk__info("%s[%d] exited with status %d (%s)", p->desc, p->pid, exitcode, crm_exit_str(exitcode)); break; case CRM_EX_FATAL: pcmk__warn("Shutting cluster down because %s[%d] had fatal failure", - name, p->pid); + p->desc, p->pid); child->flags &= ~child_respawn; fatal_error = true; pcmk_shutdown(SIGTERM); @@ -290,7 +288,7 @@ pcmk_child_exit(mainloop_child_t *p, int core, int signo, int exitcode) child->flags &= ~child_respawn; fatal_error = true; msg = pcmk__assert_asprintf("Subdaemon %s[%d] requested panic", - name, p->pid); + p->desc, p->pid); pcmk__panic(msg); // Should never get here @@ -301,7 +299,7 @@ pcmk_child_exit(mainloop_child_t *p, int core, int signo, int exitcode) default: // cts-lab looks for this message - pcmk__err("%s[%d] exited with status %d (%s)", name, p->pid, + pcmk__err("%s[%d] exited with status %d (%s)", p->desc, p->pid, exitcode, crm_exit_str(exitcode)); break; } @@ -479,7 +477,8 @@ start_child(pcmkd_child_t * child) valgrind_s = " (valgrind enabled: " PCMK__VALGRIND_EXEC ")"; } - mainloop_child_add(child->pid, 0, name, child, pcmk_child_exit); + pcmk__main_loop_child_create(child->pid, name, 0, child, true, + pcmk_child_exit); pcmk__info("Forked process %lld using user %lld (%s) and group %lld " "for subdaemon %s%s", diff --git a/include/crm/cluster/election_internal.h b/include/crm/cluster/election_internal.h index 4825f8e103e..8e60b62824b 100644 --- a/include/crm/cluster/election_internal.h +++ b/include/crm/cluster/election_internal.h @@ -73,8 +73,8 @@ enum election_result { void election_reset(pcmk_cluster_t *cluster); void election_init(pcmk_cluster_t *cluster, void (*cb)(pcmk_cluster_t *)); -void election_timeout_set_period(pcmk_cluster_t *cluster, - unsigned int period_ms); +void election_timeout_set_interval(pcmk_cluster_t *cluster, + unsigned int interval_ms); void election_timeout_stop(pcmk_cluster_t *cluster); void election_vote(pcmk_cluster_t *cluster); diff --git a/include/crm/common/Makefile.am b/include/crm/common/Makefile.am index cf66a79f240..708190bb2d7 100644 --- a/include/crm/common/Makefile.am +++ b/include/crm/common/Makefile.am @@ -27,6 +27,7 @@ header_HEADERS += iso8601_compat.h header_HEADERS += logging.h header_HEADERS += logging_compat.h header_HEADERS += mainloop.h +header_HEADERS += mainloop_compat.h header_HEADERS += nodes.h header_HEADERS += nvpair.h header_HEADERS += nvpair_compat.h diff --git a/include/crm/common/mainloop.h b/include/crm/common/mainloop.h index 17a0ea1cc12..c62dc61fd65 100644 --- a/include/crm/common/mainloop.h +++ b/include/crm/common/mainloop.h @@ -12,9 +12,9 @@ #include // bool #include // sighandler_t -#include // pid_t, ssize_t +#include // ssize_t -#include // gboolean, GSourceFunc, GMainLoop +#include // gboolean, GMainLoop #include // qb_ipcs_service_t, etc. #include @@ -30,23 +30,10 @@ extern "C" { * \ingroup core */ -enum mainloop_child_flags { - /* don't kill pid group on timeout, only kill the pid */ - mainloop_leave_pid_group = 0x01, -}; - // NOTE: sbd (as of at least 1.5.2) uses this typedef struct trigger_s crm_trigger_t; typedef struct mainloop_io_s mainloop_io_t; -typedef struct mainloop_child_s mainloop_child_t; - -// NOTE: sbd (as of at least 1.5.2) uses this -typedef struct mainloop_timer_s mainloop_timer_t; - -//! \deprecated This has been for internal use only since its creation. -typedef void (*pcmk__mainloop_child_exit_fn_t)(mainloop_child_t *p, int core, - int signo, int exitcode); void mainloop_cleanup(void); @@ -73,24 +60,6 @@ gboolean mainloop_add_signal(int sig, void (*dispatch) (int sig)); gboolean mainloop_destroy_signal(int sig); -bool mainloop_timer_running(mainloop_timer_t *t); - -// NOTE: sbd (as of at least 1.5.2) uses this -void mainloop_timer_start(mainloop_timer_t *t); - -// NOTE: sbd (as of at least 1.5.2) uses this -void mainloop_timer_stop(mainloop_timer_t *t); - -unsigned int mainloop_timer_set_period(mainloop_timer_t *t, - unsigned int period_ms); - -// NOTE: sbd (as of at least 1.5.2) uses this -mainloop_timer_t *mainloop_timer_add(const char *name, unsigned int period_ms, - bool repeat, GSourceFunc cb, - void *userdata); - -void mainloop_timer_del(mainloop_timer_t *t); - struct ipc_client_callbacks { /*! * \brief Dispatch function for an IPC connection used as mainloop source @@ -171,26 +140,6 @@ mainloop_io_t *mainloop_add_fd(const char *name, int priority, int fd, void *use void mainloop_del_fd(mainloop_io_t * client); -/* - * Create a new tracked process - * To track a process group, use -pid - */ -void mainloop_child_add(pid_t pid, int timeout, const char *desc, - void *userdata, - pcmk__mainloop_child_exit_fn_t exit_fn); - -void mainloop_child_add_with_flags(pid_t pid, int timeout, const char *desc, - void *userdata, enum mainloop_child_flags, - pcmk__mainloop_child_exit_fn_t exit_fn); - -void *mainloop_child_userdata(mainloop_child_t * child); -int mainloop_child_timeout(mainloop_child_t * child); -const char *mainloop_child_name(mainloop_child_t * child); - -pid_t mainloop_child_pid(mainloop_child_t * child); -void mainloop_clear_child_userdata(mainloop_child_t * child); -gboolean mainloop_child_kill(pid_t pid); - void pcmk_quit_main_loop(GMainLoop *mloop, unsigned int n); void pcmk_drain_main_loop(GMainLoop *mloop, unsigned int timer_ms, bool (*check)(unsigned int)); @@ -201,4 +150,8 @@ void pcmk_drain_main_loop(GMainLoop *mloop, unsigned int timer_ms, } #endif +#if !defined(PCMK_ALLOW_DEPRECATED) || (PCMK_ALLOW_DEPRECATED == 1) +#include +#endif // !defined(PCMK_ALLOW_DEPRECATED) || (PCMK_ALLOW_DEPRECATED == 1) + #endif diff --git a/include/crm/common/mainloop_compat.h b/include/crm/common/mainloop_compat.h new file mode 100644 index 00000000000..dd9232bf2d7 --- /dev/null +++ b/include/crm/common/mainloop_compat.h @@ -0,0 +1,104 @@ +/* + * Copyright 2009-2026 the Pacemaker project contributors + * + * The version control history for this file may have further details. + * + * This source code is licensed under the GNU Lesser General Public License + * version 2.1 or later (LGPLv2.1+) WITHOUT ANY WARRANTY. + */ + +#ifndef PCMK__CRM_COMMON_MAINLOOP_COMPAT__H +#define PCMK__CRM_COMMON_MAINLOOP_COMPAT__H + +#include // bool +#include // pid_t + +#include // gboolean, GSourceFunc + +#include // mainloop_* + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \file + * \brief Deprecated Pacemaker main event loop API + * \ingroup core + * \deprecated Do not include this header directly. The time APIs in this + * header, and the header itself, will be removed in a future + * release. + */ + +//! \deprecated Do not use +typedef struct mainloop_child_s mainloop_child_t; + +//! \deprecated Do not use +enum mainloop_child_flags { + mainloop_leave_pid_group = 0x01, +}; + +//! \deprecated Do not use +void mainloop_child_add_with_flags(pid_t pid, int timeout_ms, const char *desc, + void *user_data, enum mainloop_child_flags, + void (*callback)(mainloop_child_t *child, + int core, int signo, + int exit_code)); + +//! \deprecated Do not use +void mainloop_child_add(pid_t pid, int timeout_ms, const char *desc, + void *user_data, + void (*callback)(mainloop_child_t *child, int core, + int signo, int exit_code)); + +//! \deprecated Do not use +gboolean mainloop_child_kill(pid_t pid); + +//! \deprecated Do not use +pid_t mainloop_child_pid(mainloop_child_t *child); + +//! \deprecated Do not use +const char *mainloop_child_name(mainloop_child_t *child); + +//! \deprecated Do not use +int mainloop_child_timeout(mainloop_child_t *child); + +//! \deprecated Do not use +void *mainloop_child_userdata(mainloop_child_t *child); + +//! \deprecated Do not use +void mainloop_clear_child_userdata(mainloop_child_t *child); + +// NOTE: sbd (as of at least 1.5.2) uses this +//! \deprecated Do not use +typedef struct mainloop_timer_s mainloop_timer_t; + +//! \deprecated Do not use +bool mainloop_timer_running(mainloop_timer_t *timer); + +// NOTE: sbd (as of at least 1.5.2) uses this +//! \deprecated Do not use +void mainloop_timer_start(mainloop_timer_t *timer); + +// NOTE: sbd (as of at least 1.5.2) uses this +//! \deprecated Do not use +void mainloop_timer_stop(mainloop_timer_t *timer); + +//! \deprecated Do not use +unsigned int mainloop_timer_set_period(mainloop_timer_t *timer, + unsigned int interval_ms); + +// NOTE: sbd (as of at least 1.5.2) uses this +//! \deprecated Do not use +mainloop_timer_t *mainloop_timer_add(const char *name, unsigned int interval_ms, + bool repeat, GSourceFunc cb, + void *userdata); + +//! \deprecated Do not use +void mainloop_timer_del(mainloop_timer_t *timer); + +#ifdef __cplusplus +} +#endif + +#endif // PCMK__CRM_COMMON_MAINLOOP_COMPAT__H diff --git a/include/crm/common/mainloop_internal.h b/include/crm/common/mainloop_internal.h index 70cf3da79bb..21e19bf9352 100644 --- a/include/crm/common/mainloop_internal.h +++ b/include/crm/common/mainloop_internal.h @@ -14,10 +14,9 @@ #ifndef PCMK__CRM_COMMON_MAINLOOP_INTERNAL__H #define PCMK__CRM_COMMON_MAINLOOP_INTERNAL__H +#include // bool #include // pid_t -#include // gboolean - #include // crm_ipc_t #include // ipc_client_callbacks, mainloop_* @@ -25,19 +24,71 @@ extern "C" { #endif +/* Forward-declare because pcmk__main_loop_child_cb_t takes a + * (pcmk__main_loop_child_t *) argument + */ +typedef struct mainloop_child_s pcmk__main_loop_child_t; + +/*! + * \internal + * \brief Callback function called when a child process terminates + */ +typedef void (*pcmk__main_loop_child_cb_t)(pcmk__main_loop_child_t *child, + int core, int signo, int exit_code); + +/*! + * \internal + * \brief Info about a child process tracked by a main event loop + */ struct mainloop_child_s { - pid_t pid; - char *desc; - unsigned timerid; - gboolean timeout; - void *privatedata; + /* @COMPAT Drop "struct mainloop_child_s" when we drop it from + * mainloop_compat.h + */ + pid_t pid; //!< Child PID + char *desc; //!< Description + unsigned int timer_id; //!< ID of timer for child timeout + bool timed_out; //!< Whether the child has timed out + void *user_data; //!< User data - enum mainloop_child_flags flags; + /*! + * If \c true, kill the child's entire process group on timeout. + * If \c false, kill only the child process. + */ + bool kill_group; - /* Called when a process dies */ - pcmk__mainloop_child_exit_fn_t exit_fn; + //! Callback function called when the child terminates + pcmk__main_loop_child_cb_t callback; }; +/*! + * \internal + * \brief Main loop timer + * + * This is an abstraction for a \c GSource that's added using \c g_timeout_add() + * or \c g_timeout_add_seconds(). + * + * A given source has an unsigned ID. This ID identifies the source to the main + * loop and allows it to be removed by \c g_source_remove(). + * + * To stop a timeout using GLib primitives, we remove the source. At that point, + * if we saved the source ID, it's no longer valid. If we want to start the + * timeout again using the same interval, callback function, and user data, we + * must pass all of those again to \c g_timeout_add() or similar. + * + * This structure facilitates timeout management and reuse. It encapsulates the + * timeout interval, callback function, and user data; a timer name for logging + * purposes; and the ID of the associated source if the timer is running. The ID + * is set to 0 if the timer is not running, indicating that there's no + * associated source. + */ +typedef struct { + char *name; //!< Timer name (for logging only) + unsigned int source_id; //!< Source ID (0 indicates timer not running) + unsigned int interval_ms; //!< Interval in milliseconds + GSourceFunc cb; //!< Callback called every \c interval_ms + void *user_data; //!< User data passed to \c cb +} pcmk__main_loop_timer_t; + struct mainloop_io_s { char *name; void *userdata; @@ -52,10 +103,24 @@ struct mainloop_io_s { void (*destroy_fn)(void *user_data); }; +void pcmk__main_loop_child_create(pid_t pid, const char *desc, + unsigned int timeout_ms, void *user_data, + bool kill_group, + pcmk__main_loop_child_cb_t callback); +bool pcmk__main_loop_child_kill(pid_t pid); + int pcmk__add_mainloop_ipc(crm_ipc_t *ipc, int priority, void *userdata, const struct ipc_client_callbacks *callbacks, mainloop_io_t **source); -unsigned int pcmk__mainloop_timer_get_period(const mainloop_timer_t *timer); + +pcmk__main_loop_timer_t *pcmk__main_loop_timer_new(const char *name, + unsigned int interval_ms, + GSourceFunc callback, + void *user_data); +bool pcmk__main_loop_timer_running(const pcmk__main_loop_timer_t *timer); +void pcmk__main_loop_timer_stop(pcmk__main_loop_timer_t *timer); +void pcmk__main_loop_timer_start(pcmk__main_loop_timer_t *timer); +void pcmk__main_loop_timer_free(pcmk__main_loop_timer_t *timer); #ifdef __cplusplus } diff --git a/lib/cluster/cpg.c b/lib/cluster/cpg.c index ad314112ac6..58ae8978510 100644 --- a/lib/cluster/cpg.c +++ b/lib/cluster/cpg.c @@ -181,14 +181,17 @@ pcmk__cpg_local_nodeid(cpg_handle_t handle) * * \param[in] data CPG handle * - * \return FALSE (to indicate to glib that timer should not be removed) + * \return \c G_SOURCE_REMOVE (to indicate to glib that timer should not be + * removed) + * + * \note Return value description seems inverted */ static gboolean crm_cs_flush_cb(void *data) { cs_message_timer = 0; crm_cs_flush(data); - return FALSE; + return G_SOURCE_REMOVE; } // Send no more than this many CPG messages in one flush diff --git a/lib/cluster/election.c b/lib/cluster/election.c index db44bfdfccd..64ea1c38f08 100644 --- a/lib/cluster/election.c +++ b/lib/cluster/election.c @@ -28,7 +28,7 @@ struct pcmk__election { unsigned int count; // How many times local node has voted void (*cb)(pcmk_cluster_t *); // Function to call if election is won GHashTable *voted; // Key = node name, value = how node voted - mainloop_timer_t *timeout; // When to abort if all votes not received + pcmk__main_loop_timer_t *timeout; // When to abort if all votes not received int election_wins; // Track wins, for storm detection bool wrote_blackbox; // Write a storm blackbox at most once time_t expires; // When storm detection period ends @@ -53,7 +53,7 @@ election_timer_cb(void *user_data) pcmk__info("Declaring local node as winner after election timed out"); election_complete(cluster); - return FALSE; + return G_SOURCE_REMOVE; } /*! @@ -98,11 +98,9 @@ election_init(pcmk_cluster_t *cluster, void (*cb)(pcmk_cluster_t *)) cluster->priv->election = pcmk__assert_alloc(1, sizeof(pcmk__election_t)); cluster->priv->election->cb = cb; - cluster->priv->election->timeout = mainloop_timer_add(name, - ELECTION_TIMEOUT_MS, - FALSE, - election_timer_cb, - cluster); + cluster->priv->election->timeout = + pcmk__main_loop_timer_new(name, ELECTION_TIMEOUT_MS, election_timer_cb, + cluster); } /*! @@ -136,7 +134,7 @@ election_reset(pcmk_cluster_t *cluster) { if ((cluster != NULL) && (cluster->priv->election != NULL)) { pcmk__trace("Resetting election"); - mainloop_timer_stop(cluster->priv->election->timeout); + pcmk__main_loop_timer_stop(cluster->priv->election->timeout); g_clear_pointer(&cluster->priv->election->voted, g_hash_table_destroy); } } @@ -156,7 +154,7 @@ election_fini(pcmk_cluster_t *cluster) if ((cluster != NULL) && (cluster->priv->election != NULL)) { election_reset(cluster); pcmk__trace("Destroying election"); - mainloop_timer_del(cluster->priv->election->timeout); + pcmk__main_loop_timer_free(cluster->priv->election->timeout); g_clear_pointer(&cluster->priv->election, free); } } @@ -164,7 +162,7 @@ election_fini(pcmk_cluster_t *cluster) static void election_timeout_start(pcmk_cluster_t *cluster) { - mainloop_timer_start(cluster->priv->election->timeout); + pcmk__main_loop_timer_start(cluster->priv->election->timeout); } /*! @@ -177,7 +175,7 @@ void election_timeout_stop(pcmk_cluster_t *cluster) { if ((cluster != NULL) && (cluster->priv->election != NULL)) { - mainloop_timer_stop(cluster->priv->election->timeout); + pcmk__main_loop_timer_stop(cluster->priv->election->timeout); } } @@ -185,14 +183,31 @@ election_timeout_stop(pcmk_cluster_t *cluster) * \internal * \brief Change an election's timeout (restarting timer if running) * - * \param[in,out] cluster Cluster with election - * \param[in] period New timeout + * \param[in,out] cluster Cluster with election + * \param[in] interval_ms New timer interval in milliseconds */ void -election_timeout_set_period(pcmk_cluster_t *cluster, unsigned int period) +election_timeout_set_interval(pcmk_cluster_t *cluster, unsigned int interval_ms) { - CRM_CHECK((cluster != NULL) && (cluster->priv->election != NULL), return); - mainloop_timer_set_period(cluster->priv->election->timeout, period); + pcmk__main_loop_timer_t *timer = NULL; + + CRM_CHECK((cluster != NULL) + && (cluster->priv->election != NULL) + && (cluster->priv->election->timeout != NULL), + return); + + timer = cluster->priv->election->timeout; + + if (timer->interval_ms == interval_ms) { + return; + } + + timer->interval_ms = interval_ms; + + if (pcmk__main_loop_timer_running(timer)) { + // Restart the timer using the new interval if it changed + pcmk__main_loop_timer_start(timer); + } } static int diff --git a/lib/common/ipc_server.c b/lib/common/ipc_server.c index ec92392918f..d3796edade3 100644 --- a/lib/common/ipc_server.c +++ b/lib/common/ipc_server.c @@ -482,7 +482,7 @@ crm_ipcs_flush_events_cb(void *data) c->event_timer = 0; crm_ipcs_flush_events(c); - return FALSE; + return G_SOURCE_REMOVE; } /*! diff --git a/lib/common/mainloop.c b/lib/common/mainloop.c index 823c202f27c..f980c9193b1 100644 --- a/lib/common/mainloop.c +++ b/lib/common/mainloop.c @@ -25,37 +25,15 @@ struct trigger_s { GSource source; - gboolean running; - gboolean trigger; + bool running; + bool trigger; void *user_data; unsigned int id; }; -struct mainloop_timer_s { - unsigned int id; - unsigned int period_ms; - bool repeat; - char *name; - GSourceFunc cb; - void *userdata; -}; - static GList *child_list = NULL; static qb_array_t *gio_map = NULL; -static void -child_free(mainloop_child_t *child) -{ - if (child->timerid != 0) { - pcmk__trace("Removing timer %d", child->timerid); - g_source_remove(child->timerid); - child->timerid = 0; - } - - free(child->desc); - free(child); -} - static gboolean crm_trigger_prepare(GSource *source, int *timeout) { @@ -111,14 +89,14 @@ crm_trigger_dispatch(GSource *source, GSourceFunc callback, void *userdata) /* Wait until the existing job is complete before starting the next one */ return G_SOURCE_CONTINUE; } - trig->trigger = FALSE; + trig->trigger = false; if (callback) { int callback_rc = callback(trig->user_data); if (callback_rc < 0) { pcmk__trace("Trigger handler %p not yet complete", trig); - trig->running = TRUE; + trig->running = true; } else if (callback_rc == 0) { rc = G_SOURCE_REMOVE; } @@ -148,7 +126,7 @@ mainloop_setup_trigger(GSource * source, int priority, trigger = (crm_trigger_t *) source; trigger->id = 0; - trigger->trigger = FALSE; + trigger->trigger = false; trigger->user_data = userdata; if (dispatch) { @@ -166,7 +144,7 @@ void mainloop_trigger_complete(crm_trigger_t * trig) { pcmk__trace("Trigger handler %p complete", trig); - trig->running = FALSE; + trig->running = false; } /*! @@ -197,7 +175,7 @@ void mainloop_set_trigger(crm_trigger_t * source) { if(source) { - source->trigger = TRUE; + source->trigger = true; } } @@ -258,7 +236,7 @@ crm_signal_dispatch(GSource *source, GSourceFunc callback, void *userdata) ((sig->handler != NULL)? "invoking" : "no")); } - sig->trigger.trigger = FALSE; + sig->trigger.trigger = false; if (sig->handler) { sig->handler(sig->signal); } @@ -415,25 +393,6 @@ mainloop_destroy_signal(int sig) return TRUE; } -/*! - * \internal - * \brief Free data structures used for the mainloop - * - * \todo This is incomplete. Free other data structures created in this file. - */ -void -mainloop_cleanup(void) -{ - g_list_free_full(child_list, (GDestroyNotify) child_free); - child_list = NULL; - - g_clear_pointer(&gio_map, qb_array_free); - - for (int sig = 0; sig < NSIG; ++sig) { - mainloop_destroy_signal_entry(sig); - } -} - /* * libqb... */ @@ -869,22 +828,6 @@ pcmk__add_mainloop_ipc(crm_ipc_t *ipc, int priority, void *userdata, return pcmk_rc_ok; } -/*! - * \brief Get period for mainloop timer - * - * \param[in] timer Timer - * - * \return Period in ms - */ -unsigned int -pcmk__mainloop_timer_get_period(const mainloop_timer_t *timer) -{ - if (timer) { - return timer->period_ms; - } - return 0; -} - mainloop_io_t * mainloop_add_ipc_client(const char *name, int priority, size_t max_size, void *userdata, struct ipc_client_callbacks *callbacks) @@ -975,52 +918,27 @@ mainloop_del_fd(mainloop_io_t *client) g_source_remove(client->source); } -pid_t -mainloop_child_pid(mainloop_child_t * child) -{ - return child->pid; -} - -const char * -mainloop_child_name(mainloop_child_t * child) -{ - return child->desc; -} - -int -mainloop_child_timeout(mainloop_child_t * child) -{ - return child->timeout; -} - -void * -mainloop_child_userdata(mainloop_child_t * child) -{ - return child->privatedata; -} - -void -mainloop_clear_child_userdata(mainloop_child_t * child) -{ - child->privatedata = NULL; -} - +/*! + * \internal + * \brief Send \c SIGKILL to a main loop child process or its process group + * + * If \p child->kill_group is set, kill the child's entire process group. + * Otherwise, kill only the child process itself. + * + * \param[in] child Main loop child + * + * \return Standard Pacemaker return code (\c pcmk_rc_ok if \c kill() returns 0, + * or \c errno after calling \c kill() otherwise) + */ static int -child_kill_helper(const mainloop_child_t *child) +kill_child_pid(const pcmk__main_loop_child_t *child) { + const pid_t pid = (child->kill_group? -child->pid : child->pid); int rc = 0; - if (pcmk__is_set(child->flags, mainloop_leave_pid_group)) { - pcmk__debug("Killing PID %lld only. Leaving its process group intact.", - (long long) child->pid); - rc = kill(child->pid, SIGKILL); - - } else { - pcmk__debug("Killing PID %lld's entire process group", - (long long) child->pid); - rc = kill(-child->pid, SIGKILL); - } + pcmk__debug("Killing PID %lld", (long long) pid); + rc = kill(pid, SIGKILL); if (rc == 0) { return pcmk_rc_ok; } @@ -1030,334 +948,575 @@ child_kill_helper(const mainloop_child_t *child) return rc; } - pcmk__err("kill(%lld, KILL) failed: %s", (long long) child->pid, - strerror(rc)); + pcmk__err("kill(%lld, KILL) failed for child '%s': %s", (long long) pid, + pcmk__s(child->desc, ""), strerror(rc)); return rc; } +/*! + * \internal + * \brief Kill a child process after its timeout has expired + * + * \param[in,out] user_data Main loop child + * (pcmk__main_loop_child_t *) + * + * \return \c G_SOURCE_REMOVE (to destroy the timeout that triggered this call) + * + * \note This is a \c GSourceFunc. + */ static gboolean -child_timeout_callback(void *p) +child_timeout_callback(void *user_data) { - mainloop_child_t *child = p; + pcmk__main_loop_child_t *child = user_data; int rc = pcmk_rc_ok; + const char *result_s = NULL; - child->timerid = 0; - if (child->timeout) { - pcmk__warn("%s process (PID %lld) will not die!", child->desc, - (long long) child->pid); - return FALSE; + child->timer_id = 0; + child->timed_out = true; + + rc = kill_child_pid(child); + + switch (rc) { + case pcmk_rc_ok: + result_s = "was successfully killed"; + break; + + case ESRCH: + result_s = "has already terminated"; + break; + + default: + result_s = "could not be killed"; + break; } - rc = child_kill_helper(child); - if (rc == ESRCH) { - /* Nothing left to do. pid doesn't exist */ - return FALSE; + pcmk__debug("%s process (PID %lld) timed out and %s", child->desc, + (long long) child->pid, result_s); + return G_SOURCE_REMOVE; +} + +/*! + * \internal + * \brief Free a main loop child + * + * If the child has an associated timer, remove it. + * + * \param[in,out] data Main loop child (pcmk__main_loop_child_t *) + * + * \note This does not free the child's \c user_data field. + * \note This is a \c GDestroyNotify. + */ +static void +free_main_loop_child(void *data) +{ + pcmk__main_loop_child_t *child = data; + + if (child == NULL) { + return; } - child->timeout = TRUE; - pcmk__debug("%s process (PID %lld) timed out", child->desc, - (long long) child->pid); + if (child->timer_id != 0) { + pcmk__trace("Removing timer %u", child->timer_id); + g_source_remove(child->timer_id); + } - child->timerid = pcmk__create_timer(5000, child_timeout_callback, child); - return FALSE; + free(child->desc); + free(child); } +/*! + * \internal + * \brief Wait on a child process and free it if terminated + * + * If the child has terminated, call its exit callback if any, remove it from + * \c child_list, and free it. + * + * Likely bug: If the child object's \c pid field is nonpositive, then we wait + * on the corresponding process group as documented in the \c wait(2) man page. + * On success, call the exit callback using that PID (not the PID of the actual + * child process that changed state). Also remove the child object from + * \c child_list and free the child object, even though there may still be other + * child processes in the same process group that have not yet been waited on. + * This seems incorrect. However, nothing internal creates a child object with + * nonpositive PID, and the \c mainloop_child_add() documentation notes that + * nonpositive PIDs are not expected to work correctly. + * + * \param[in,out] link List element whose data is the child to wait for + * \param[in] no_hang If \c true, use the \c waitpid() \c WNOHANG option + * + * \return \c true if the child process (or a child process in the specified + * process group) has terminated, or \c false if the child process is + * still active or its state changed in an unexpected way + * + * \note Taking the list link rather than the child as an argument allows us to + * delete a terminated child from \c child_list in constant time. If we + * took the child, \c g_list_remove() would have to find the child in the + * list again before removing it. + */ static bool -child_waitpid(mainloop_child_t *child, int flags) +child_waitpid(GList *link, bool no_hang) { - int rc = 0; + const int options = no_hang? WNOHANG : 0; + + pcmk__main_loop_child_t *child = NULL; + pid_t rc = 0; + int status = 0; + int core = 0; int signo = 0; - int status = 0; - int exitcode = 0; + int exit_code = 0; - rc = waitpid(child->pid, &status, flags); + pcmk__assert(link != NULL); + child = link->data; - if (rc == 0) { // WNOHANG in flags, and child status is not available + rc = waitpid(child->pid, &status, options); + + if (rc == 0) { + // WNOHANG was specified and child->pid exists and has not changed state pcmk__trace("Child process %lld (%s) still active", (long long) child->pid, child->desc); return false; } + if (rc == -1) { + if (errno == ECHILD) { + /* This situation should probably never happen in practice. Setting + * exit_code to 1 is misleading in that it indicates the child + * exited with code 1, and we don't know that to be true. We could + * add a pcmk__main_loop_child_t flag to indicate this case, but it + * doesn't seem worth it. + */ + exit_code = 1; + + pcmk__err("Wait for child process %lld (%s) failed because process " + "does not exist or is not our child", + (long long) child->pid, child->desc); + goto terminated; + } + + if (errno == EINTR) { + pcmk__notice("Wait for child process %lld (%s) was interrupted by " + "a signal", (long long) child->pid, child->desc); + return false; + } + + pcmk__err("Bug: Wait for child process %lld (%s) failed: %s (waitpid() " + "options: %#x)", (long long) child->pid, child->desc, + strerror(errno), options); + return false; + } + + /* At this point, rc is the PID of a child whose state changed. If + * child->pid is positive, then rc == child->pid. Otherwise, rc is the PID + * of one of the child processes in the process group with ID -child->pid. + */ + if (rc != child->pid) { - /* According to POSIX, possible conditions: - * - child->pid was non-positive (process group or any child), - * and rc is specific child - * - errno ECHILD (pid does not exist or is not child) - * - errno EINVAL (invalid flags) - * - errno EINTR (caller interrupted by signal) - * - * @TODO Handle these cases more specifically. + /* @COMPAT Nothing internal creates a nonpositive child->pid, and the + * public Doxygen for mainloop_child_add() now notes that nonpositive + * PIDs are not expected to work correctly. */ - signo = SIGCHLD; - exitcode = 1; - pcmk__notice("Wait for child process %lld (%s) interrupted: %s", - (long long) child->pid, child->desc, strerror(errno)); + pcmk__trace("Child process %lld from group %lld (%s) terminated", + (long long) rc, (long long) -child->pid, child->desc); + goto terminated; + } - } else if (WIFEXITED(status)) { - exitcode = WEXITSTATUS(status); + if (WIFEXITED(status)) { + exit_code = WEXITSTATUS(status); pcmk__trace("Child process %lld (%s) exited with status %d", - (long long) child->pid, child->desc, exitcode); + (long long) child->pid, child->desc, exit_code); + goto terminated; + } - } else if (WIFSIGNALED(status)) { + if (WIFSIGNALED(status)) { signo = WTERMSIG(status); - pcmk__trace("Child process %lld (%s) exited with signal %d (%s)", + pcmk__trace("Child process %lld (%s) was terminated by signal %d (%s)", (long long) child->pid, child->desc, signo, strsignal(signo)); -#ifdef WCOREDUMP // AIX, SunOS, maybe others - } else if (WCOREDUMP(status)) { - core = 1; - pcmk__err("Child process %lld (%s) dumped core", (long long) child->pid, - child->desc); -#endif +#ifdef WCOREDUMP + if (WCOREDUMP(status)) { + core = 1; + pcmk__err("Child process %lld (%s) dumped core", + (long long) child->pid, child->desc); + } +#endif // defined(WCOREDUMP) - } else { // flags must contain WUNTRACED and/or WCONTINUED to reach this - pcmk__trace("Child process %lld (%s) stopped or continued", - (long long) child->pid, child->desc); - return false; + goto terminated; } - if (child->exit_fn != NULL) { - child->exit_fn(child, core, signo, exitcode); + /* We're not using the WUNTRACED or WCONTINUED options. If the process + * changed state, it should have either exited or been terminated by a + * signal. + */ + CRM_CHECK(false, return false); + +terminated: + if (child->callback != NULL) { + child->callback(child, core, signo, exit_code); } + pcmk__trace("Removing terminated process %lld from child list", + (long long) child->pid); + child_list = g_list_delete_link(child_list, link); + free_main_loop_child(child); + return true; } +/*! + * \internal + * \brief Free all main loop children whose processes have terminated + * + * If a child object's process has terminated, remove the child from + * \c child_list and free it. + * + * \param[in] signal Ignored + */ static void -child_death_dispatch(int signal) -{ - for (GList *iter = child_list; iter; ) { - GList *saved = iter; - mainloop_child_t *child = iter->data; - - iter = iter->next; - if (child_waitpid(child, WNOHANG)) { - pcmk__trace("Removing completed process %lld from child list", - (long long) child->pid); - child_list = g_list_remove_link(child_list, saved); - g_list_free(saved); - child_free(child); - } +free_terminated_children(int signal) +{ + GList *iter = child_list; + + while (iter != NULL) { + GList *next = iter->next; + + child_waitpid(iter, true); + iter = next; } } +/*! + * \internal + * \brief Install the main loop \c SIGCHLD handler + * + * Install \c free_terminated_children() as the \c SIGCHLD handler, and call it + * for any children that terminated before the handler was installed. + * + * \param[in] user_data Ignored + * + * \return \c G_SOURCE_REMOVE (to destroy the timeout that triggered this call) + * + * \note This is a \c GSourceFunc. + */ static gboolean -child_signal_init(void *p) +install_sigchld_handler(void *user_data) { - pcmk__trace("Installed SIGCHLD handler"); - /* Do NOT use g_child_watch_add() and friends, they rely on pthreads */ - mainloop_add_signal(SIGCHLD, child_death_dispatch); + pcmk__trace("Installing SIGCHLD handler"); - /* In case they terminated before the signal handler was installed */ - child_death_dispatch(SIGCHLD); - return FALSE; + // Do NOT use g_child_watch_add() and friends, since they rely on pthreads + mainloop_add_signal(SIGCHLD, free_terminated_children); + + free_terminated_children(SIGCHLD); + return G_SOURCE_REMOVE; } -gboolean -mainloop_child_kill(pid_t pid) +/*! + * \internal + * \brief Create a \c pcmk__main_loop_child_t object and add it to the main loop + * + * If the child process has not exited within \p timeout_ms, send it a + * \c SIGKILL signal. + * + * \param[in] pid Child PID + * \param[in] desc Description + * \param[in] timeout_ms Timeout in milliseconds + * \param[in] user_data User data + * \param[in] kill group If \c true, kill the child's entire process group on + * timeout; otherwise, kill only the child process + * \param[in] callback Function to call when the child process terminates + */ +void +pcmk__main_loop_child_create(pid_t pid, const char *desc, + unsigned int timeout_ms, void *user_data, + bool kill_group, + pcmk__main_loop_child_cb_t callback) { - GList *iter; - mainloop_child_t *child = NULL; - mainloop_child_t *match = NULL; - /* It is impossible to block SIGKILL, this allows us to - * call waitpid without WNOHANG flag.*/ - int waitflags = 0; - int rc = pcmk_rc_ok; + static bool need_init = true; - for (iter = child_list; iter != NULL && match == NULL; iter = iter->next) { - child = iter->data; - if (pid == child->pid) { - match = child; - } + pcmk__main_loop_child_t *child = NULL; + + pcmk__assert(pid > 0); + + child = pcmk__assert_alloc(1, sizeof(pcmk__main_loop_child_t)); + child->pid = pid; + child->desc = pcmk__str_copy(desc); + child->timer_id = pcmk__create_timer(timeout_ms, child_timeout_callback, + child); + child->user_data = user_data; + child->kill_group = kill_group; + child->callback = callback; + + child_list = g_list_append(child_list, child); + + if (need_init) { + /* Invoke SIGCHLD processing from the main loop. This ensures that we + * don't add a child to the main loop and have the exit callback invoked + * for the child PID within the same call stack. + * + * @TODO Understand and document why this matters. + */ + need_init = false; + pcmk__create_timer(1, install_sigchld_handler, NULL); + } +} + +/*! + * \internal + * \brief Compare two mainloop child objects by PID + * + * \param[in] a First child to compare + * (const pcmk__main_loop_child_t *) + * \param[in] b Second child to compare + * (const pcmk__main_loop_child_t *) + * + * \retval -1 if \p a->pid is less than \p b->pid + * \retval 0 if \p a->pid is equal to \p b->pid + * \retval 1 if \p a->pid is greater than \p b->pid + * + * \note This is a \c GCompareFunc. + */ +static int +compare_children_by_pid(const void *a, const void *b) +{ + const pcmk__main_loop_child_t *child1 = a; + const pcmk__main_loop_child_t *child2 = b; + + if (child1->pid < child2->pid) { + return -1; + } + + if (child1->pid > child2->pid) { + return 1; } + return 0; +} + +/*! + * \internal + * \brief Kill a child process tracked by the main loop + * + * If a process with PID \p pid is being tracked, send it a \c SIGKILL. + * + * If this function kills the child process successfully, remove the child from + * the tracking data structure and free the child. + * + * If the process is being tracked but no longer exists, don't remove or free + * the child yet. We will do this later when we receive a \c SIGCHLD for the + * child process. + * + * \param[in] pid Child PID + * + * \return \c true if the child with ID \p pid was being tracked and either this + * function killed the process successfully or the process has already + * terminated but we have not received a \c SIGCHLD for it; or \c false + * otherwise + */ +bool +pcmk__main_loop_child_kill(pid_t pid) +{ + const pcmk__main_loop_child_t cmp_data = { .pid = pid }; + GList *match = NULL; + pcmk__main_loop_child_t *child = NULL; + int rc = pcmk_rc_ok; + bool no_hang = false; + + pcmk__assert(pid > 0); + + match = g_list_find_custom(child_list, &cmp_data, compare_children_by_pid); if (match == NULL) { - return FALSE; + return false; } - rc = child_kill_helper(match); + child = match->data; + + rc = kill_child_pid(child); if (rc == ESRCH) { - /* It's gone, but hasn't shown up in waitpid() yet. Wait until we get + /* It's gone but hasn't shown up in waitpid() yet. Wait until we get * SIGCHLD and let handler clean it up as normal (so we get the correct * return code/status). The blocking alternative would be to call - * child_waitpid(match, 0). + * child_waitpid(iter, false). */ pcmk__trace("Waiting for signal that child process %lld completed", - (long long) match->pid); - return TRUE; + (long long) child->pid); + return true; } if (rc != pcmk_rc_ok) { /* If kill() failed for some other reason, set the WNOHANG flag, since * we can't be certain what happened. + * + * If kill() succeeded, we don't need the WNOHANG flag because SIGKILL + * can't be blocked. */ - waitflags = WNOHANG; + no_hang = true; } - if (!child_waitpid(match, waitflags)) { - /* not much we can do if this occurs */ - return FALSE; - } - - child_list = g_list_remove(child_list, match); - child_free(match); - return TRUE; + return child_waitpid(match, no_hang); } -/* Create/Log a new tracked process - * To track a process group, use -pid +/*! + * \internal + * \brief Create a main loop timer * - * @TODO Using a non-positive pid (i.e. any child, or process group) would - * likely not be useful since we will free the child after the first - * completed process. + * \param[in] name Timer name prefix (for logging only) + * \param[in] interval_ms Timer interval + * \param[in] callback Function to call after \p interval_ms expires + * \param[in] user_data User data for \p callback + * + * \return Newly allocated main loop timer (guaranteed not to be \c NULL) + * + * \note The new timer's \c name string starts with the \p name argument and + * includes the timer's interval and address. + * \note The caller is responsible for freeing the return value using + * \c pcmk__main_loop_timer_free(). */ -void -mainloop_child_add_with_flags(pid_t pid, int timeout, const char *desc, - void *privatedata, - enum mainloop_child_flags flags, - pcmk__mainloop_child_exit_fn_t exit_fn) +pcmk__main_loop_timer_t * +pcmk__main_loop_timer_new(const char *name, unsigned int interval_ms, + GSourceFunc callback, void *user_data) { - static bool need_init = TRUE; - mainloop_child_t *child = pcmk__assert_alloc(1, sizeof(mainloop_child_t)); + pcmk__main_loop_timer_t *timer = NULL; + pcmk__assert((name != NULL) && (callback != NULL)); - child->pid = pid; - child->timerid = 0; - child->timeout = FALSE; - child->privatedata = privatedata; - child->exit_fn = exit_fn; - child->flags = flags; - child->desc = pcmk__str_copy(desc); + timer = pcmk__assert_alloc(1, sizeof(pcmk__main_loop_timer_t)); + timer->name = pcmk__assert_asprintf("%s-%u-%p", name, interval_ms, timer); + timer->interval_ms = interval_ms; + timer->cb = callback; + timer->user_data = user_data; - if (timeout) { - child->timerid = pcmk__create_timer(timeout, child_timeout_callback, child); - } + pcmk__trace("Created timer %s with data %p", timer->name, user_data); + return timer; +} - child_list = g_list_append(child_list, child); +/*! + * \internal + * \brief Check whether a main loop timer is running + * + * A timer is running if its \c id field is nonzero, meaning that it has an + * active \c GSource with that ID associated with it. + * + * \param[in] timer Main loop timer + * + * \return \c true if the timer is running, or \c false otherwise + */ +bool +pcmk__main_loop_timer_running(const pcmk__main_loop_timer_t *timer) +{ + CRM_CHECK(timer != NULL, return false); - if(need_init) { - need_init = FALSE; - /* SIGCHLD processing has to be invoked from mainloop. - * We do not want it to be possible to both add a child pid - * to mainloop, and have the pid's exit callback invoked within - * the same callstack. */ - pcmk__create_timer(1, child_signal_init, NULL); - } + return (timer->source_id != 0); } +/*! + * \internal + * \brief Stop a main loop timer + * + * Stopping a timer consists of removing its \c GSource and setting its \c id + * field to 0 (to indicate that it has no associated \c GSource). + * + * \param[in,out] timer Main loop timer + */ void -mainloop_child_add(pid_t pid, int timeout, const char *desc, void *privatedata, - pcmk__mainloop_child_exit_fn_t exit_fn) +pcmk__main_loop_timer_stop(pcmk__main_loop_timer_t *timer) { - mainloop_child_add_with_flags(pid, timeout, desc, privatedata, 0, exit_fn); + if (!pcmk__main_loop_timer_running(timer)) { + return; + } + + pcmk__trace("Stopping timer %s", timer->name); + g_source_remove(timer->source_id); + timer->source_id = 0; } +/*! + * \internal + * \brief Run a main loop timer's callback + * + * If the callback returns \c G_SOURCE_REMOVE, set \p timer->source_id to 0 to + * indicate that the timer has no associated \c GSource. + * + * \param[in,out] user_data Main loop timer + * (pcmk__main_loop_timer_t *) + * + * \return The return value from \p timer->cb (\c G_SOURCE_CONTINUE to keep the + * timeout source, or \c G_SOURCE_REMOVE to remove it) + * + * \note This is a \c GSourceFunc. + */ static gboolean -mainloop_timer_cb(void *user_data) +main_loop_timer_cb(void *user_data) { int id = 0; - bool repeat = FALSE; - struct mainloop_timer_s *t = user_data; - - pcmk__assert(t != NULL); - - id = t->id; - t->id = 0; /* Ensure it's unset during callbacks so that - * mainloop_timer_running() works as expected - */ - - if(t->cb) { - pcmk__trace("Invoking callbacks for timer %s", t->name); - repeat = t->repeat; - if(t->cb(t->userdata) == FALSE) { - pcmk__trace("Timer %s complete", t->name); - repeat = FALSE; - } - } + pcmk__main_loop_timer_t *timer = user_data; - if(repeat) { - /* Restore if repeating */ - t->id = id; - } + pcmk__assert((timer != NULL) && (timer->cb != NULL)); - return repeat; -} + /* Ensure id is unset during callbacks so that + * pcmk__main_loop_timer_running() works as expected. + * + * @TODO Why is this necessary or desirable? + */ + id = timer->source_id; + timer->source_id = 0; -bool -mainloop_timer_running(mainloop_timer_t *t) -{ - if(t && t->id != 0) { - return TRUE; - } - return FALSE; -} + pcmk__trace("Invoking callbacks for timer %s", timer->name); -void -mainloop_timer_start(mainloop_timer_t *t) -{ - mainloop_timer_stop(t); - if(t && t->period_ms > 0) { - pcmk__trace("Starting timer %s", t->name); - t->id = pcmk__create_timer(t->period_ms, mainloop_timer_cb, t); + // G_SOURCE_REMOVE is false; G_SOURCE_CONTINUE is true + if (!timer->cb(timer->user_data)) { + pcmk__trace("Timer %s complete", timer->name); + return G_SOURCE_REMOVE; } -} -void -mainloop_timer_stop(mainloop_timer_t *t) -{ - if(t && t->id != 0) { - pcmk__trace("Stopping timer %s", t->name); - g_source_remove(t->id); - t->id = 0; - } + timer->source_id = id; + return G_SOURCE_CONTINUE; } -unsigned int -mainloop_timer_set_period(mainloop_timer_t *t, unsigned int period_ms) +/*! + * \internal + * \brief Start a main loop timer + * + * Starting a timer consists of: + * 1. stopping the timer if it's already running (by removing the associated + * \c GSource) + * 2. creating a new \c GSource using \p timer->interval_ms as the timeout (see + * \c pcmk__create_timer()) + * 3. assigning the new \c GSource ID to \p timer->source_id + * + * \param[in,out] timer Main loop timer + */ +void +pcmk__main_loop_timer_start(pcmk__main_loop_timer_t *timer) { - unsigned int last = 0; - - if(t) { - last = t->period_ms; - t->period_ms = period_ms; - } - - if(t && t->id != 0 && last != t->period_ms) { - mainloop_timer_start(t); - } - return last; -} + CRM_CHECK((timer != NULL) + && (timer->interval_ms > 0) + && (timer->cb != NULL), + return); -mainloop_timer_t * -mainloop_timer_add(const char *name, unsigned int period_ms, bool repeat, - GSourceFunc cb, void *userdata) -{ - mainloop_timer_t *t = pcmk__assert_alloc(1, sizeof(mainloop_timer_t)); + pcmk__main_loop_timer_stop(timer); - if (name != NULL) { - t->name = pcmk__assert_asprintf("%s-%u-%d", name, period_ms, repeat); - } else { - t->name = pcmk__assert_asprintf("%p-%u-%d", t, period_ms, repeat); - } - t->id = 0; - t->period_ms = period_ms; - t->repeat = repeat; - t->cb = cb; - t->userdata = userdata; - pcmk__trace("Created timer %s with %p %p", t->name, userdata, t->userdata); - return t; + pcmk__trace("Starting timer %s", timer->name); + timer->source_id = pcmk__create_timer(timer->interval_ms, + main_loop_timer_cb, timer); } +/*! + * \internal + * \brief Free a main loop timer + * + * \param[in,out] timer Main loop timer + */ void -mainloop_timer_del(mainloop_timer_t *t) +pcmk__main_loop_timer_free(pcmk__main_loop_timer_t *timer) { - if(t) { - pcmk__trace("Destroying timer %s", t->name); - mainloop_timer_stop(t); - free(t->name); - free(t); + if (timer == NULL) { + return; } + + pcmk__trace("Destroying timer %s", timer->name); + pcmk__main_loop_timer_stop(timer); + free(timer->name); + free(timer); } /* @@ -1370,7 +1529,7 @@ drain_timeout_cb(void *user_data) bool *timeout_popped = (bool*) user_data; *timeout_popped = TRUE; - return FALSE; + return G_SOURCE_REMOVE; } /*! @@ -1432,3 +1591,251 @@ pcmk_drain_main_loop(GMainLoop *mloop, unsigned int timer_ms, g_source_remove(timer); } } + +/*! + * \internal + * \brief Free data structures used for the mainloop + * + * \todo This is incomplete. Free other data structures created in this file. + */ +void +mainloop_cleanup(void) +{ + g_list_free_full(child_list, free_main_loop_child); + child_list = NULL; + + g_clear_pointer(&gio_map, qb_array_free); + + for (int sig = 0; sig < NSIG; ++sig) { + mainloop_destroy_signal_entry(sig); + } +} + +// Deprecated functions kept only for backward API compatibility +// LCOV_EXCL_START + +#include + +void +mainloop_child_add_with_flags(pid_t pid, int timeout_ms, const char *desc, + void *user_data, + enum mainloop_child_flags flags, + void (*callback)(mainloop_child_t *child, int core, + int signo, int exit_code)) +{ + static bool need_init = true; + + mainloop_child_t *child = pcmk__assert_alloc(1, sizeof(mainloop_child_t)); + + child->pid = pid; + child->desc = pcmk__str_copy(desc); + child->user_data = user_data; + child->kill_group = !pcmk__is_set(flags, mainloop_leave_pid_group); + child->callback = callback; + + if (timeout_ms > 0) { + child->timer_id = pcmk__create_timer(timeout_ms, child_timeout_callback, + child); + } + + child_list = g_list_append(child_list, child); + + if (need_init) { + need_init = false; + pcmk__create_timer(1, install_sigchld_handler, NULL); + } +} + +void +mainloop_child_add(pid_t pid, int timeout_ms, const char *desc, void *user_data, + void (*callback)(mainloop_child_t *child, int core, + int signo, int exit_code)) +{ + mainloop_child_add_with_flags(pid, timeout_ms, desc, user_data, 0, callback); +} + +gboolean +mainloop_child_kill(pid_t pid) +{ + const mainloop_child_t cmp_data = { .pid = pid }; + GList *match = NULL; + mainloop_child_t *child = NULL; + int rc = pcmk_rc_ok; + bool no_hang = false; + + match = g_list_find_custom(child_list, &cmp_data, compare_children_by_pid); + if (match == NULL) { + return FALSE; + } + + child = match->data; + + rc = kill_child_pid(child); + if (rc == ESRCH) { + pcmk__trace("Waiting for signal that child process %lld completed", + (long long) child->pid); + return TRUE; + } + + if (rc != pcmk_rc_ok) { + no_hang = true; + } + + return child_waitpid(match, no_hang)? TRUE : FALSE; +} + +pid_t +mainloop_child_pid(mainloop_child_t *child) +{ + return child->pid; +} + +const char * +mainloop_child_name(mainloop_child_t *child) +{ + return child->desc; +} + +int +mainloop_child_timeout(mainloop_child_t *child) +{ + return child->timed_out? TRUE : FALSE; +} + +void * +mainloop_child_userdata(mainloop_child_t *child) +{ + return child->user_data; +} + +void +mainloop_clear_child_userdata(mainloop_child_t *child) +{ + child->user_data = NULL; +} + +struct mainloop_timer_s { + char *name; + unsigned int source_id; + unsigned int interval_ms; + gboolean repeat; + GSourceFunc cb; + void *user_data; +}; + +static gboolean +mainloop_timer_cb(void *user_data) +{ + int id = 0; + mainloop_timer_t *timer = user_data; + + pcmk__assert((timer != NULL) && (timer->cb != NULL)); + + id = timer->source_id; + timer->source_id = 0; + + pcmk__trace("Invoking callbacks for timer %s", timer->name); + + if (!timer->cb(timer->user_data)) { + pcmk__trace("Timer %s complete", timer->name); + return G_SOURCE_REMOVE; + } + + if (!timer->repeat) { + return G_SOURCE_REMOVE; + } + + timer->source_id = id; + return G_SOURCE_CONTINUE; +} + +bool +mainloop_timer_running(mainloop_timer_t *timer) +{ + return (timer != NULL) && (timer->source_id != 0); +} + +void +mainloop_timer_start(mainloop_timer_t *timer) +{ + mainloop_timer_stop(timer); + + if ((timer == NULL) || (timer->interval_ms == 0) || (timer->cb == NULL)) { + return; + } + + pcmk__trace("Starting timer %s", timer->name); + timer->source_id = pcmk__create_timer(timer->interval_ms, mainloop_timer_cb, + timer); +} + +void +mainloop_timer_stop(mainloop_timer_t *timer) +{ + if ((timer == NULL) || (timer->source_id == 0)) { + return; + } + + pcmk__trace("Stopping timer %s", timer->name); + g_source_remove(timer->source_id); + timer->source_id = 0; +} + +unsigned int +mainloop_timer_set_period(mainloop_timer_t *timer, unsigned int interval_ms) +{ + unsigned int last = 0; + + if (timer == NULL) { + return 0; + } + + last = timer->interval_ms; + timer->interval_ms = interval_ms; + + if ((timer->source_id != 0) && (timer->interval_ms != last)) { + mainloop_timer_start(timer); + } + + return last; +} + +mainloop_timer_t * +mainloop_timer_add(const char *name, unsigned int interval_ms, bool repeat, + GSourceFunc cb, void *userdata) +{ + mainloop_timer_t *timer = pcmk__assert_alloc(1, sizeof(mainloop_timer_t)); + + if (name != NULL) { + timer->name = pcmk__assert_asprintf("%s-%u-%d", name, interval_ms, + repeat); + + } else { + timer->name = pcmk__assert_asprintf("%p-%u-%d", timer, interval_ms, + repeat); + } + + timer->interval_ms = interval_ms; + timer->repeat = repeat; + timer->cb = cb; + timer->user_data = userdata; + + pcmk__trace("Created timer %s with %p", timer->name, userdata); + return timer; +} + +void +mainloop_timer_del(mainloop_timer_t *timer) +{ + if (timer == NULL) { + return; + } + + pcmk__trace("Destroying timer %s", timer->name); + mainloop_timer_stop(timer); + free(timer->name); + free(timer); +} + +// LCOV_EXCL_STOP +// End deprecated API diff --git a/lib/common/remote.c b/lib/common/remote.c index 1846b8fc775..c6af9c9f9ba 100644 --- a/lib/common/remote.c +++ b/lib/common/remote.c @@ -785,7 +785,9 @@ struct tcp_async_cb_data { void (*callback) (void *userdata, int rc, int sock); }; -// \return TRUE if timer should be rescheduled, FALSE otherwise +/* \return G_SOURCE_CONTINUE if timer should be rescheduled, G_SOURCE_REMOVED + * otherwise + */ static gboolean check_connect_finished(void *userdata) { @@ -811,7 +813,7 @@ check_connect_finished(void *userdata) rc = errno; if ((rc == EINTR) || (rc == EAGAIN)) { if ((time(NULL) - cb_data->start) < pcmk__timeout_ms2s(cb_data->timeout_ms)) { - return TRUE; // There is time left, so reschedule timer + return G_SOURCE_CONTINUE; // There is time left, so reschedule timer } else { rc = ETIMEDOUT; } @@ -821,7 +823,7 @@ check_connect_finished(void *userdata) } else if (rc == 0) { // select() timeout if ((time(NULL) - cb_data->start) < pcmk__timeout_ms2s(cb_data->timeout_ms)) { - return TRUE; // There is time left, so reschedule timer + return G_SOURCE_CONTINUE; // There is time left, so reschedule timer } pcmk__debug("Timed out while waiting for socket %d connection success", cb_data->sock); @@ -868,7 +870,7 @@ check_connect_finished(void *userdata) cb_data->callback(cb_data->userdata, rc, cb_data->sock); } free(cb_data); - return FALSE; // Do not reschedule timer + return G_SOURCE_REMOVE; // Do not reschedule timer } /*! diff --git a/lib/fencing/st_client.c b/lib/fencing/st_client.c index 691e353c16a..fae37900e64 100644 --- a/lib/fencing/st_client.c +++ b/lib/fencing/st_client.c @@ -1022,10 +1022,10 @@ stonith_async_timeout_handler(void *data) timer->timeout); invoke_registered_callbacks(timer->stonith, NULL, timer->call_id); - /* Always return TRUE, never remove the handler - * We do that in stonith_del_callback() + /* Always return G_SOURCE_CONTINUE, never remove the handler. + * We do that in stonith_del_callback(). */ - return TRUE; + return G_SOURCE_CONTINUE; } static void diff --git a/lib/services/dbus.c b/lib/services/dbus.c index a21da13e599..99d961d79a7 100644 --- a/lib/services/dbus.c +++ b/lib/services/dbus.c @@ -205,7 +205,7 @@ timer_popped(void *data) pcmk__debug("%dms DBus timer expired", dbus_timeout_get_interval((DBusTimeout *) data)); dbus_timeout_handle(data); - return FALSE; + return G_SOURCE_REMOVE; } static dbus_bool_t diff --git a/lib/services/services.c b/lib/services/services.c index c6a3019452e..06abcd76d2f 100644 --- a/lib/services/services.c +++ b/lib/services/services.c @@ -649,7 +649,7 @@ gboolean services_action_cancel(const char *name, const char *action, unsigned int interval_ms) { - gboolean cancelled = FALSE; + bool cancelled = false; char *id = pcmk__op_key(name, action, interval_ms); svc_action_t *op = NULL; @@ -674,12 +674,13 @@ services_action_cancel(const char *name, const char *action, */ if (op->pid != 0) { pcmk__info("Terminating in-flight op %s[%d] early because it was " - "cancelled", - id, op->pid); - cancelled = mainloop_child_kill(op->pid); - if (cancelled == FALSE) { + "cancelled", id, op->pid); + + cancelled = pcmk__main_loop_child_kill(op->pid); + if (!cancelled) { pcmk__err("Termination of %s[%d] failed", id, op->pid); } + goto done; } @@ -707,12 +708,12 @@ services_action_cancel(const char *name, const char *action, blocked_ops = g_list_remove(blocked_ops, op); services_action_free(op); - cancelled = TRUE; + cancelled = true; // @TODO Initiate handle_blocked_ops() asynchronously done: free(id); - return cancelled; + return cancelled? TRUE : FALSE; } gboolean diff --git a/lib/services/services_linux.c b/lib/services/services_linux.c index 6ae1605ebbc..80fd416e1b5 100644 --- a/lib/services/services_linux.c +++ b/lib/services/services_linux.c @@ -560,7 +560,7 @@ recurring_action_timer(void *data) op->opaque->repeat_timer = 0; services_action_async(op, NULL); - return FALSE; + return G_SOURCE_REMOVE; } /*! @@ -706,11 +706,13 @@ parse_exit_reason_from_stderr(svc_action_t *op) * \param[in] exitcode Exit status of child process */ static void -async_action_complete(mainloop_child_t *p, int core, int signo, int exitcode) +async_action_complete(pcmk__main_loop_child_t *p, int core, int signo, + int exitcode) { - svc_action_t *op = mainloop_child_userdata(p); + svc_action_t *op = p->user_data; + + p->user_data = NULL; - mainloop_clear_child_userdata(p); CRM_CHECK(op->pid == p->pid, services__set_result(op, services__generic_error(op), PCMK_EXEC_ERROR, "Bug in mainloop handling"); @@ -731,7 +733,7 @@ async_action_complete(mainloop_child_t *p, int core, int signo, int exitcode) log_op_output(op); parse_exit_reason_from_stderr(op); - } else if (mainloop_child_timeout(p)) { + } else if (p->timed_out) { const char *kind = services__action_kind(op); pcmk__info("%s %s[%d] timed out after %s", kind, op->id, op->pid, @@ -1393,14 +1395,11 @@ services__execute_file(svc_action_t *op) } pcmk__trace("Waiting async for '%s'[%d]", op->opaque->exec, op->pid); - if (pcmk__is_set(op->flags, SVC_ACTION_LEAVE_GROUP)) { - mainloop_child_add_with_flags(op->pid, op->timeout, op->id, op, - mainloop_leave_pid_group, - async_action_complete); - } else { - mainloop_child_add_with_flags(op->pid, op->timeout, op->id, op, 0, - async_action_complete); - } + + pcmk__main_loop_child_create(op->pid, op->id, op->timeout, op, + !pcmk__is_set(op->flags, + SVC_ACTION_LEAVE_GROUP), + async_action_complete); op->opaque->stdout_gsource = mainloop_add_fd(op->id, G_PRIORITY_LOW, diff --git a/lib/services/systemd.c b/lib/services/systemd.c index 8f20d08d4b9..93ffe306579 100644 --- a/lib/services/systemd.c +++ b/lib/services/systemd.c @@ -1363,7 +1363,7 @@ systemd_timeout_callback(void *p) } else { services__finalize_async_op(op); } - return FALSE; + return G_SOURCE_REMOVE; } /*! diff --git a/tools/crm_mon.c b/tools/crm_mon.c index 3610870a9dd..b75b0329d86 100644 --- a/tools/crm_mon.c +++ b/tools/crm_mon.c @@ -67,7 +67,7 @@ static mon_output_format_t output_format = mon_output_unset; static GIOChannel *io_channel = NULL; static GMainLoop *mainloop = NULL; static unsigned int reconnect_timer = 0; -static mainloop_timer_t *refresh_timer = NULL; +static pcmk__main_loop_timer_t *refresh_timer = NULL; static enum pcmk_pacemakerd_state pcmkd_state = pcmk_pacemakerd_state_invalid; static cib_t *cib = NULL; @@ -831,7 +831,7 @@ mon_cib_connection_destroy(void *user_data) if (refresh_timer != NULL) { /* we'll trigger a refresh after reconnect */ - mainloop_timer_stop(refresh_timer); + pcmk__main_loop_timer_stop(refresh_timer); } if (reconnect_timer) { /* we'll trigger a new reconnect-timeout at the end */ @@ -1864,7 +1864,7 @@ static gboolean mon_trigger_refresh(void *user_data) { mainloop_set_trigger((crm_trigger_t *) refresh_trigger); - return FALSE; + return G_SOURCE_REMOVE; } static int @@ -2115,12 +2115,13 @@ refresh_after_event(gboolean data_updated, gboolean enforce) } if(refresh_timer == NULL) { - refresh_timer = mainloop_timer_add("refresh", 2000, FALSE, mon_trigger_refresh, NULL); + refresh_timer = pcmk__main_loop_timer_new("refresh", 2000, + mon_trigger_refresh, NULL); } if (reconnect_timer > 0) { /* we will receive a refresh request after successful reconnect */ - mainloop_timer_stop(refresh_timer); + pcmk__main_loop_timer_stop(refresh_timer); return; } @@ -2134,11 +2135,11 @@ refresh_after_event(gboolean data_updated, gboolean enforce) ((now - last_refresh) > pcmk__timeout_ms2s(options.reconnect_ms)) || updates >= 10) { mainloop_set_trigger((crm_trigger_t *) refresh_trigger); - mainloop_timer_stop(refresh_timer); + pcmk__main_loop_timer_stop(refresh_timer); updates = 0; } else { - mainloop_timer_start(refresh_timer); + pcmk__main_loop_timer_start(refresh_timer); } } diff --git a/tools/crm_resource.c b/tools/crm_resource.c index 8c2d165b19d..e4f08f36f6d 100644 --- a/tools/crm_resource.c +++ b/tools/crm_resource.c @@ -212,7 +212,7 @@ resource_ipc_timeout(void *data) _("Aborting because no messages received in %d seconds"), MESSAGE_TIMEOUT_S); quit_main_loop(CRM_EX_TIMEOUT); - return FALSE; + return G_SOURCE_REMOVE; } static void