From 3039b71cb195a8b5830c0afd7e759bc2d28bb329 Mon Sep 17 00:00:00 2001 From: Ian Jackson Date: Sat, 20 Aug 2022 13:03:40 +0100 Subject: [PATCH] prefork-interp: clean up old sockets Signed-off-by: Ian Jackson --- cprogs/prefork-interp.c | 110 ++++++++++++++++++++++++++++++++++++++++ cprogs/prefork.c | 2 +- cprogs/prefork.h | 1 + 3 files changed, 112 insertions(+), 1 deletion(-) diff --git a/cprogs/prefork-interp.c b/cprogs/prefork-interp.c index 2a7fdc3..305314a 100644 --- a/cprogs/prefork-interp.c +++ b/cprogs/prefork-interp.c @@ -118,6 +118,8 @@ void fusagemessage(FILE *f) { } static int laundering; +static int max_sockets = 100; // maximum entries in the run dir is 2x this + static struct stat initial_stab; const struct cmdinfo cmdinfos[]= { @@ -174,6 +176,111 @@ static void propagate_exit_status(int status, const char *what) { die("%s failed with weird wait status %d 0x%x", what, status, status); } +typedef struct { + char *name_hash; + time_t atime; +} PrecleanEntry; + +static int preclean_entry_compar_name(const void *av, const void *bv) { + const PrecleanEntry *a = av; + const PrecleanEntry *b = bv; + return strcmp(a->name_hash, b->name_hash); +} + +static int preclean_entry_compar_atime(const void *av, const void *bv) { + const PrecleanEntry *ae = av; time_t a = ae->atime; + const PrecleanEntry *be = bv; time_t b = be->atime; + return (a > b ? +1 : + a < b ? -1 : 0); +} + +static time_t preclean_stat_atime(const char *s_path) { + struct stat stab; + int r= lstat(s_path, &stab); + if (r) { + if (errno!=ENOENT) diee("pre-cleanup: stat socket (%s)", s_path); + return 0; + } + return stab.st_atime; +} + +static void preclean(void) { + DIR *dir = opendir(run_base); + if (!dir) { + if (errno == ENOENT) return; + diee("pre-cleanup: open run dir (%s)", run_base); + } + + PrecleanEntry *entries=0; + size_t avail_entries=0; + size_t used_entries=0; + + struct dirent *de; + while ((errno = 0, de = readdir(dir))) { + char c0 = de->d_name[0]; + if (!(c0 == 'l' || c0 == 's')) continue; + char *name_hash = m_asprintf("%s", de->d_name+1); + char *s_path = m_asprintf("%s/s%s", run_base, name_hash); + time_t atime = preclean_stat_atime(s_path); + + if (avail_entries == used_entries) { + assert(avail_entries < INT_MAX / 4 / sizeof(PrecleanEntry)); + avail_entries <<= 1; + avail_entries += 10; + entries = realloc(entries, avail_entries * sizeof(PrecleanEntry)); + } + entries[used_entries].name_hash = name_hash; + entries[used_entries].atime = atime; + used_entries++; + } + if (errno) diee("pre-cleanup: read run dir (%s)", run_base); + + // First we dedupe (after sorting by path) + qsort(entries, used_entries, sizeof(PrecleanEntry), + preclean_entry_compar_name); + PrecleanEntry *p, *q; + for (p=entries, q=entries; p < entries + used_entries; p++) { + if (q > entries && !strcmp(p->name_hash, (q-1)->name_hash)) + continue; + *q++ = *p; + } + used_entries = q - entries; + + // Now maybe delete some things + // + // Actually this has an off-by-one error since we are about + // to create a socket, so the actual number of sockets is one more. + // But, *actually*, since there might be multiple of us running at once, + // we might have even more than that. This doesn't really matter. + if (used_entries > max_sockets) { + qsort(entries, used_entries, sizeof(PrecleanEntry), + preclean_entry_compar_atime); + for (p=entries; p < entries + max_sockets; p++) { + char *l_path = m_asprintf("%s/l%s", run_base, p->name_hash); + char *s_path = m_asprintf("%s/s%s", run_base, p->name_hash); + int lock_fd = flock_file(l_path); + // Recheck atime - we might have raced! + time_t atime = preclean_stat_atime(s_path); + if (atime == p->atime) { + // Raced. This will leave use deleting too few things. Whatever. + } else { + int r= unlink(s_path); + if (r && errno!=ENOENT) diee("preclean: delete stale (%s)", s_path); + r= unlink(l_path); + if (r) diee("preclean: delete stale lock (%s)", s_path); + // NB we don't hold the lock any more now. + } + close(lock_fd); + free(l_path); + free(s_path); + } + } + + for (p=entries; p < entries + used_entries; p++) + free(p->name_hash); + free(entries); +} + static __attribute((noreturn)) void die_data_overflow(void) { die("cannot handle data with length >2^32"); } @@ -470,6 +577,9 @@ static void connect_or_spawn(void) { call_sock = connect_existing(); if (call_sock) return; + // We're going to make a new one, so clean out old ones + preclean(); + int lockfd = acquire_lock(); call_sock = connect_existing(); if (call_sock) { close(lockfd); return; } diff --git a/cprogs/prefork.c b/cprogs/prefork.c index 12d6021..e25aadf 100644 --- a/cprogs/prefork.c +++ b/cprogs/prefork.c @@ -5,9 +5,9 @@ const char *interp, *ident, *script, *socket_path, *lock_path; bool logging; struct sha256_ctx identsc; +const char *run_base; static uid_t us; -static const char *run_base; static const char *run_base_mkdir_p; void common_diee(const char *m) { diee("%s", m); } diff --git a/cprogs/prefork.h b/cprogs/prefork.h index 11329ce..66ed5fc 100644 --- a/cprogs/prefork.h +++ b/cprogs/prefork.h @@ -37,6 +37,7 @@ extern const char *interp, *ident, *script, *socket_path, *lock_path; extern bool logging; extern struct sha256_ctx identsc; +extern const char *run_base; extern const char our_name[]; -- 2.30.2