From: Arran Cudbard-Bell Date: Tue, 28 Jul 2026 01:07:22 +0000 (-0600) Subject: listen: disarm the timers when closing a virtual listener X-Git-Url: http://git.ipfire.org/gitweb/index.cgi?a=commitdiff_plain;h=HEAD;p=thirdparty%2Ffreeradius-server.git listen: disarm the timers when closing a virtual listener proto_load_step and proto_cron_crontab drive themselves from timers rather than from their fd, and neither had a close callback, so nothing stopped them when the listener was closed. The load generator kept manufacturing packets and handing them to fr_network_send_request() after the network had signalled the workers to close, which the assert added there caught. Both now close the fd and remove their timer. Of the remaining app_io modules with timers, proto_detail_file, proto_detail_work and proto_ldap_sync_ldap have a close callback that does not obviously disarm anything, and are worth a look. --- diff --git a/src/listen/cron/proto_cron_crontab.c b/src/listen/cron/proto_cron_crontab.c index 7459bd891bb..4d1bfabfee1 100644 --- a/src/listen/cron/proto_cron_crontab.c +++ b/src/listen/cron/proto_cron_crontab.c @@ -653,6 +653,31 @@ use_time: fr_network_listen_read(thread->nr, thread->parent); } +/** Close a virtual listener + * + * The fd only exists to bootstrap the listener, so closing it mostly means + * removing the timer that runs the jobs. + * + * @param[in] li the listener + * @return + * - 0 on success. + * - -1 if the timer could not be deleted. The fd is closed either way. + */ +static int mod_close(fr_listen_t *li) +{ + proto_cron_crontab_thread_t *thread = talloc_get_type_abort(li->thread_instance, proto_cron_crontab_thread_t); + int ret = 0; + + if (thread->ev && (fr_timer_delete(&thread->ev) < 0)) { + PERROR("Failed deleting cron timer"); + ret = -1; + } + + close(li->fd); + + return ret; +} + /** Set the event list for a new socket * * @param[in] li the listener @@ -748,6 +773,7 @@ fr_app_io_t proto_cron_crontab = { .track_duplicates = false, .open = mod_open, + .close = mod_close, .read = mod_read, .write = mod_write, .event_list_set = mod_event_list_set, diff --git a/src/listen/load/proto_load_step.c b/src/listen/load/proto_load_step.c index 763d361486f..60f8f613668 100644 --- a/src/listen/load/proto_load_step.c +++ b/src/listen/load/proto_load_step.c @@ -356,6 +356,31 @@ static int mod_decode(void const *instance, request_t *request, UNUSED uint8_t * return 0; } +/** Close a virtual listener + * + * The fd only exists to bootstrap the listener, so closing it mostly means + * removing the timer that drives the load generator. + * + * @param[in] li the listener + * @return + * - 0 on success. + * - -1 if the generator could not be stopped. The fd is closed either way. + */ +static int mod_close(fr_listen_t *li) +{ + proto_load_step_thread_t *thread = talloc_get_type_abort(li->thread_instance, proto_load_step_thread_t); + int ret = 0; + + if (thread->l && (fr_load_generator_stop(thread->l) < 0)) { + PERROR("Failed stopping load generator"); + ret = -1; + } + + close(li->fd); + + return ret; +} + /** Set the event list for a new socket * * @param[in] li the listener @@ -506,6 +531,7 @@ fr_app_io_t proto_load_step = { .track_duplicates = false, .open = mod_open, + .close = mod_close, .read = mod_read, .write = mod_write, .event_list_set = mod_event_list_set,