From b0663d04e48f7d9a9ccfc34b2bc2522288bc1e8a Mon Sep 17 00:00:00 2001 From: Tobias Waldekranz Date: Thu, 21 Dec 2017 11:10:47 +0100 Subject: [PATCH] cond: don't rely on mtime for condition management TIL, using mtimes for tracking event orderings is a monumentally bad idea (queue the nodding UNIX-beards). Mtimes are in wallclock time which is not necessarily monotonically increasing. A user may adjust the time, an NTP daemon will continously tune the clock and so on. Instead, store an explicit generation number in each condition file, which will be monotonically increased by finit on each reconf. --- src/cond-w.c | 51 +++++++++++++++++++++++++++++++++++++++++++++++---- src/cond.c | 37 +++++++++++++++++++++++-------------- src/cond.h | 2 ++ src/finit.c | 4 ++++ 4 files changed, 76 insertions(+), 18 deletions(-) diff --git a/src/cond-w.c b/src/cond-w.c index 8f77643f..8246f54d 100644 --- a/src/cond-w.c +++ b/src/cond-w.c @@ -25,16 +25,51 @@ #include #include #include -#include #include "finit.h" #include "cond.h" #include "service.h" +static int cond_set_gen(const char *path, unsigned int gen) +{ + FILE *fp; + int ret; + + fp = fopen(path, "w"); + if (!fp) + return -1; + + ret = fprintf(fp, "%u", gen); + fclose(fp); + + return (ret > 0) ? 0 : ret; +} + +static void cond_bump_reconf(void) +{ + unsigned int rgen; + + rgen = cond_get_gen(COND_RECONF); + /* COND_RECONF does not exist, cond_get_gen will return 0 + * meaning that rgen++ is always what we want. */ + rgen++; + + cond_set_gen(COND_RECONF, rgen); +} + int cond_set_path(const char *path, enum cond_state new) { char buf[MAX_ARG_LEN], *dir; enum cond_state old; + unsigned int rgen; + + _d("%s", path); + + rgen = cond_get_gen(COND_RECONF); + if (!rgen) { + _e("Unable to read configuration generation (%s)", path); + return -1; + } old = cond_get_path(path); @@ -50,8 +85,7 @@ int cond_set_path(const char *path, enum cond_state new) _pe("Failed creating dir '%s' for condition '%s'", dir, path); return 0; } - touch(path); - utimensat(0, path, NULL, 0); + cond_set_gen(path, rgen); break; case COND_OFF: @@ -121,8 +155,8 @@ void cond_clear(const char *name) void cond_reload(void) { _d(""); - cond_set_path(COND_RECONF, COND_ON); + cond_bump_reconf(); cond_update(NULL); } @@ -153,6 +187,15 @@ void cond_reassert(const char *type) nftw(cond_path(type), reassert, 20, FTW_DEPTH); } +void cond_init(void) +{ + if (makepath(COND_PATH) && errno != EEXIST) { + _pe("Failed creating condition base directory '%s'", COND_PATH); + return; + } + + cond_bump_reconf(); +} /** * Local Variables: diff --git a/src/cond.c b/src/cond.c index 98c3895b..d01fae2f 100644 --- a/src/cond.c +++ b/src/cond.c @@ -28,14 +28,6 @@ #include "cond.h" #include "service.h" -static inline int timespec_newer(const struct timespec *a, - const struct timespec *b) -{ - if (a->tv_sec != b->tv_sec) - return a->tv_sec > b->tv_sec; - - return a->tv_nsec >= b->tv_nsec; -} const char *condstr(enum cond_state s) { @@ -57,18 +49,35 @@ const char *cond_path(const char *name) return file; } +unsigned int cond_get_gen(const char *path) +{ + unsigned int gen; + FILE *fp; + int ret; + + fp = fopen(path, "r"); + if (!fp) + return 0; + + ret = fscanf(fp, "%u", &gen); + fclose(fp); + + return (ret == 1) ? gen : 0; +} + enum cond_state cond_get_path(const char *path) { - struct stat st, st_reconf; + int cgen, rgen; - if (stat(path, &st)) + rgen = cond_get_gen(COND_RECONF); + if (!rgen) return COND_OFF; - if (stat(COND_RECONF, &st_reconf) || - timespec_newer(&st.st_mtim, &st_reconf.st_mtim)) - return COND_ON; + cgen = cond_get_gen(path); + if (!cgen) + return COND_OFF; - return COND_FLUX; + return (cgen == rgen) ? COND_ON : COND_FLUX; } enum cond_state cond_get(const char *name) diff --git a/src/cond.h b/src/cond.h index f8c94f4e..c003cc1a 100644 --- a/src/cond.h +++ b/src/cond.h @@ -15,6 +15,7 @@ typedef enum cond_state { const char *condstr (enum cond_state s); const char *cond_path (const char *name); +unsigned int cond_get_gen (const char *path); enum cond_state cond_get_path(const char *path); enum cond_state cond_get (const char *name); enum cond_state cond_get_agg (const char *names); @@ -25,6 +26,7 @@ void cond_set (const char *name); void cond_clear (const char *name); void cond_reload (void); void cond_reassert(const char *pat); +void cond_init (void); #endif /* FINIT_COND_H_ */ diff --git a/src/finit.c b/src/finit.c index b8d445bb..27bd0c15 100644 --- a/src/finit.c +++ b/src/finit.c @@ -36,6 +36,7 @@ #include #include "finit.h" +#include "cond.h" #include "conf.h" #include "helpers.h" #include "private.h" @@ -475,6 +476,9 @@ int main(int argc, char* argv[]) /* Base FS up, enable standard SysV init signals */ sig_setup(&loop); + /* Bootstrap conditions */ + cond_init(); + /* * Reload all *.conf in /etc/finit.d, the mount command (above) * may have brought in new files (overlayfs).