From: Elliot Wolk Date: Mon, 1 Apr 2024 19:04:32 +0000 (-0400) Subject: Add compilation option to allow faster or even instant reload for non-root users... X-Git-Tag: ver3_3_2~8^2 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=f49f631186c5ef8881845f43d9e58214dfa897b6;p=thirdparty%2Ffcron.git Add compilation option to allow faster or even instant reload for non-root users (#26) * fcrontab: refactor sig_daemon() reload delay, add seconds to msg * configure: add opt to decrease or remove non-root tab reload * fcrontab: enforce max-fcrontab-delay-seconds in delay for non-root user * configure: rename max-fcrontab-load => max-fcrontab-reload * configure: expand usage for max-delay-reload * fcrontab: refactor fcrontab reload delay calculation for clarity --- diff --git a/config.h.in b/config.h.in index 61df7b0..5d73f2a 100644 --- a/config.h.in +++ b/config.h.in @@ -177,6 +177,9 @@ #undef FOREGROUND #undef RUN_NON_PRIVILEGED +/* 0 for no delay for any user, 60s is the default */ +#undef MAX_FCRONTAB_RELOAD_DELAY_SECONDS + /* Define if we should use sete[ug]id() funcs */ #undef USE_SETE_ID diff --git a/configure.in b/configure.in index 87bd2b8..6fc44e1 100644 --- a/configure.in +++ b/configure.in @@ -476,6 +476,33 @@ WARNING : AC_MSG_RESULT(no) ) +max_fcrontab_reload_delay_seconds=60 +AC_MSG_CHECKING(max fcrontab reload delay seconds) +AC_ARG_WITH(max-fcrontab-reload-delay-seconds, +[ --with-max-fcrontab-reload-delay-seconds=INTEGER + The maximum delay, in seconds, before (re)loading the fcrontab as a non-root user + (there is never any delay for root). By default, the delay to (re)load the fcrontab + for a non-root user is at least one full second and at most sixty seconds, + depending on circumstances when the (re)load happens. + A value of 0 for this argument means no delay for any user, ever. + A value between 1 and 59 (inclusive) will ensure that the non-root delay is always at least 1s, + and never more than the given number of seconds. + A value greater than or equal to 60 has no effect.], +[ + case "$withval" in + ("" | *[!0123456789]*) + AC_MSG_ERROR(Invalid argument : please use a non-negative integer.) + ;; + *) + max_fcrontab_reload_delay_seconds="$withval" + ;; + esac + + AC_MSG_RESULT([$withval]) +]) +MAX_FCRONTAB_RELOAD_DELAY_SECONDS="$max_fcrontab_reload_delay_seconds" +AC_SUBST(MAX_FCRONTAB_RELOAD_DELAY_SECONDS) +AC_DEFINE_UNQUOTED([MAX_FCRONTAB_RELOAD_DELAY_SECONDS], [$max_fcrontab_reload_delay_seconds]) if test "$fcrondyn" = ""; then dnl As it stands gettimeofday() is required to have fcrondyn @@ -1096,6 +1123,21 @@ else echo "no" fi +echo "Max fcrontab reload delay seconds : $max_fcrontab_reload_delay_seconds" + +if test "$max_fcrontab_reload_delay_seconds" -lt "1" ; then + AC_MSG_WARN([ + +!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! +WARNING: +This option --with-max-fcrontab-reload-delay-seconds=0, allows a + non-privileged user to immediately (re)load fcrontab. +This allows the possibility of SIGHUP denial of service to block the daemon. +!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + + ]) +fi + echo -n "Load average support : " if test "$getloadavg" -eq 1 -o -n "$proc"; then echo "yes" diff --git a/fcronsighup.c b/fcronsighup.c index 2bc1b28..c75041b 100644 --- a/fcronsighup.c +++ b/fcronsighup.c @@ -86,37 +86,44 @@ sig_daemon(void) /* SIGHUP is sent once 10s before the next minute to avoid * some bad users to block daemon by sending it SIGHUP all the time */ { - /* we don't need to make root wait */ - if (uid != rootuid) { - time_t t = 0; - int sl = 0; + int max_delay_s = 60; +#ifdef MAX_FCRONTAB_RELOAD_DELAY_SECONDS + max_delay_s = MAX_FCRONTAB_RELOAD_DELAY_SECONDS; +#endif + if (uid == rootuid) { + /* we don't need to make root wait */ + max_delay_s = 0; + } + + if (max_delay_s > 0) { + time_t now_epoch = 0; + int delay_s = 0; + time_t *target_time_epoch = NULL; + struct tm *target_time_tm = NULL; FILE *fp = NULL; int fd = 0; - struct tm *tm = NULL; char sigfile[PATH_LEN]; - char buf[PATH_LEN]; sigfile[0] = '\0'; - t = time(NULL); - tm = localtime(&t); - - if ((sl = 60 - (t % 60) - 10) < 0) { - if ((tm->tm_min = tm->tm_min + 2) >= 60) { - tm->tm_hour++; - tm->tm_min -= 60; - } - snprintf(buf, sizeof(buf), "%02d:%02d", tm->tm_hour, tm->tm_min); - sl = 60 - (t % 60) + 50; + now_epoch = time(NULL); + + if (now_epoch % 60 < 50) { + /* clocktime is < ##:##:50, so target 10s before the end of the current minute */ + delay_s = 50 - (now_epoch % 60); + } else { + /* clocktime is >= ##:##:50, so target 10s before the end of the next minute */ + delay_s = 50 + (60 - (now_epoch % 60)); } - else { - if (++tm->tm_min >= 60) { - tm->tm_hour++; - tm->tm_min -= 60; - } - snprintf(buf, sizeof(buf), "%02d:%02d", tm->tm_hour, tm->tm_min); + + if (delay_s > max_delay_s) { + delay_s = max_delay_s; } - fprintf(stderr, "Modifications will be taken into account" - " at %s.\n", buf); + + target_time_epoch = now_epoch + delay_s; + target_time_tm = localtime(&target_time_epoch); + + fprintf(stderr, "Modifications will be taken into account at %02d:%02d:%02d.\n", + target_time_tm->tm_hour, target_time_tm->tm_min, target_time_tm->tm_sec); /* if fcrontabs is too long, snprintf will not be able to add "/fcrontab.sig" * string at the end of sigfile */ @@ -167,7 +174,7 @@ sig_daemon(void) } #endif /* ! HAVE_FLOCK */ - sleep(sl); + sleep(delay_s); /* also closes the underlying file descriptor fd: */ xfclose_check(&fp, sigfile); @@ -178,7 +185,7 @@ sig_daemon(void) error_e("Could not remove %s"); } else - /* we are root */ + /* we are root, or config MAX_FCRONTAB_RELOAD_DELAY_SECONDS=0 is set */ fprintf(stderr, "Modifications will be taken into account" " right now.\n");