From 7030f85484de891ed9e7188eb688f4ac38f0bff0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mattias=20Walstr=C3=B6m?= Date: Fri, 26 Jun 2026 10:21:38 +0200 Subject: [PATCH 1/2] confd: schedule: start crond only when a schedule is active MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit crond shipped auto-enabled in finit (symlinked into enabled/), so it ran on every boot. With no cron jobs /var/spool/cron/crontabs does not exist and 'crond -f' exits non-zero, which finit then crash-loops until it gives up ("Service crond keeps crashing, not restarting"). Ship crond available-only and let confd manage it: the schedule plugin already rebuilds the crontab on every change, so enable + touch crond when at least one consumer is active and disable it otherwise. No jobs, no crond, no crash loop. Signed-off-by: Mattias Walström --- package/confd/confd.mk | 2 ++ src/confd/src/schedule.c | 12 +++++++----- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/package/confd/confd.mk b/package/confd/confd.mk index 4ec5b5774..164f1591e 100644 --- a/package/confd/confd.mk +++ b/package/confd/confd.mk @@ -38,6 +38,8 @@ endif define CONFD_INSTALL_EXTRA for fn in confd.conf crond.conf resolvconf.conf; do \ cp $(CONFD_PKGDIR)/$$fn $(FINIT_D)/available/; \ + done + for fn in confd.conf resolvconf.conf; do \ ln -sf ../available/$$fn $(FINIT_D)/enabled/$$fn; \ done cp $(CONFD_PKGDIR)/tmpfiles.conf $(TARGET_DIR)/etc/tmpfiles.d/confd.conf diff --git a/src/confd/src/schedule.c b/src/confd/src/schedule.c index 1a67d43b3..76eb1365d 100644 --- a/src/confd/src/schedule.c +++ b/src/confd/src/schedule.c @@ -176,11 +176,13 @@ static void build_cron_expr(struct lyd_node *recurrence, char *expr, size_t sz) snprintf(expr, sz, "%s %s %s %s %s", min, hr, dom, mon, dow); } -static void reload_crond(void) +static void crond_apply(int active) { - char *args[] = { "pkill", "-HUP", "crond", NULL }; - - runbg(args, 0); + if (active) { + finit_enable("crond"); + } else { + finit_disable("crond"); + } } /* @@ -267,7 +269,7 @@ static void apply_schedules(struct lyd_node *config) out: fclose(fp); - reload_crond(); + crond_apply(count > 0); NOTE("schedule: %d active job(s) written to crontab", count); } From f866cf15b42fab6b48be5479bb56cb205a89122b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mattias=20Walstr=C3=B6m?= Date: Fri, 26 Jun 2026 11:23:01 +0200 Subject: [PATCH 2/2] confd: schedule: generate in change, apply in done MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Write the crontab to admin.next on change and promote it on done, drop it on abort. Signed-off-by: Mattias Walström --- src/confd/src/schedule.c | 55 ++++++++++++++++++++++++++++++---------- 1 file changed, 42 insertions(+), 13 deletions(-) diff --git a/src/confd/src/schedule.c b/src/confd/src/schedule.c index 76eb1365d..8efd8ecb0 100644 --- a/src/confd/src/schedule.c +++ b/src/confd/src/schedule.c @@ -11,7 +11,9 @@ #include "core.h" #define XPATH_BASE "/ietf-system:system/infix-schedule:schedules" -#define CRONTAB_FILE "/var/spool/cron/crontabs/admin" +#define CRONTAB_DIR "/var/spool/cron/crontabs" +#define CRONTAB_FILE CRONTAB_DIR "/admin" +#define CRONTAB_NEXT CRONTAB_DIR "/admin.next" /* Features register a consumer to run a command on a schedule. */ static const struct cron_consumer **consumers; @@ -219,21 +221,23 @@ static int schedule_to_cron(struct lyd_node *config, const char *name, } /* - * Rebuild the crontab from every registered consumer. Each consumer points - * at a feature container holding a schedule-ref; we resolve that to cron - * fields and emit one line running the consumer's own command. + * Generate the next crontab from every registered consumer. Each consumer + * points at a feature container holding a schedule-ref; we resolve that to + * cron fields and emit one line running the consumer's own command. Written + * to CRONTAB_NEXT, promoted in SR_EV_DONE. Returns the active job count; + * CRONTAB_NEXT is removed when there are none, so DONE knows to stop crond. */ -static void apply_schedules(struct lyd_node *config) +static int gen_schedules(struct lyd_node *config) { int count = 0; FILE *fp; size_t i; - makepath("/var/spool/cron/crontabs"); - fp = fopen(CRONTAB_FILE, "w"); + makepath(CRONTAB_DIR); + fp = fopen(CRONTAB_NEXT, "w"); if (!fp) { - ERROR("schedule: failed to open %s", CRONTAB_FILE); - return; + ERROR("schedule: failed to open %s", CRONTAB_NEXT); + return -1; } fprintf(fp, "# Managed by infix-schedule\n"); @@ -269,16 +273,41 @@ static void apply_schedules(struct lyd_node *config) out: fclose(fp); - crond_apply(count > 0); - NOTE("schedule: %d active job(s) written to crontab", count); + if (!count) + (void)remove(CRONTAB_NEXT); + + return count; } int schedule_change(sr_session_ctx_t *session, struct lyd_node *config, struct lyd_node *diff, sr_event_t event, struct confd *confd) { - if (event != SR_EV_DONE && event != SR_EV_ENABLED) + if (diff && !lydx_get_xpathf(diff, XPATH_BASE)) return SR_ERR_OK; - apply_schedules(config); + switch (event) { + case SR_EV_ENABLED: /* first time, on register */ + case SR_EV_CHANGE: /* regular change */ + gen_schedules(config); + break; + + case SR_EV_ABORT: /* user abort, or another plugin failed */ + (void)remove(CRONTAB_NEXT); + break; + + case SR_EV_DONE: + if (fexist(CRONTAB_NEXT)) { + (void)rename(CRONTAB_NEXT, CRONTAB_FILE); + crond_apply(1); + } else { + (void)remove(CRONTAB_FILE); + crond_apply(0); + } + break; + + default: + break; + } + return SR_ERR_OK; }