From 669a8da54817bc80c22f2f921c1bd7b0c5846bb8 Mon Sep 17 00:00:00 2001 From: Elliot Wolk Date: Wed, 27 Mar 2024 13:33:03 -0400 Subject: [PATCH 1/2] fcrondyn: include seconds in fcrondyn -x ls as "YYYY-mm-dd HH:MM:SS" (#27) * fcrondyn: include seconds in `fcrondyn -x ls` as "YYYY-mm-dd HH:MM:SS" commit 739d6ebe60a405daadcb0f5fe59ed991cf36c48f Author: Elliot Wolk Date: 2011-10-04 03:37:10 -0400 added a script to install fcron, with numerous patches of my own design to make it work, and to include seconds in fcrondyn * fcrondyn: adjust SCHEDULE field header spacing --- fcrondyn_svr.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/fcrondyn_svr.c b/fcrondyn_svr.c index b27eae3..b5549e5 100644 --- a/fcrondyn_svr.c +++ b/fcrondyn_svr.c @@ -378,7 +378,7 @@ print_fields(int fd, unsigned char *details) char field_user[] = "|USER "; char field_rq[] = "|R&Q "; char field_options[] = "|OPTIONS "; - char field_schedule[] = "|SCHEDULE "; + char field_schedule[] = "|SCHEDULE "; char field_until[] = "|LAVG 1,5,15 UNTIL STRICT"; char field_pid[] = "|PID "; char field_index[] = "|INDEX"; @@ -455,9 +455,10 @@ print_line(int fd, struct cl_t *line, unsigned char *details, pid_t pid, ftime = localtime(&until); len += snprintf(buf + len, sizeof(buf) - len, - " %04d-%02d-%02d %02d:%02d %s", + " %04d-%02d-%02d %02d:%02d:%02d %s", (ftime->tm_year + 1900), (ftime->tm_mon + 1), ftime->tm_mday, ftime->tm_hour, ftime->tm_min, + ftime->tm_sec, (is_strict(line->cl_option)) ? "Y" : "N"); } else @@ -468,9 +469,9 @@ print_line(int fd, struct cl_t *line, unsigned char *details, pid_t pid, if (bit_test(details, FIELD_SCHEDULE)) { ftime = localtime(&(line->cl_nextexe)); len += - snprintf(buf + len, sizeof(buf) - len, "|%04d-%02d-%02d %02d:%02d", + snprintf(buf + len, sizeof(buf) - len, "|%04d-%02d-%02d %02d:%02d:%02d", (ftime->tm_year + 1900), (ftime->tm_mon + 1), - ftime->tm_mday, ftime->tm_hour, ftime->tm_min); + ftime->tm_mday, ftime->tm_hour, ftime->tm_min, ftime->tm_sec); } len += snprintf(buf + len, sizeof(buf) - len, "|%s\n", line->cl_shell); From f49f631186c5ef8881845f43d9e58214dfa897b6 Mon Sep 17 00:00:00 2001 From: Elliot Wolk Date: Mon, 1 Apr 2024 15:04:32 -0400 Subject: [PATCH 2/2] 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 --- config.h.in | 3 +++ configure.in | 42 ++++++++++++++++++++++++++++++++++++ fcronsighup.c | 59 ++++++++++++++++++++++++++++----------------------- 3 files changed, 78 insertions(+), 26 deletions(-) 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");