From: Theo Buehler Subject: Re: rpki-client: normalize 'buildtime' in the output To: Job Snijders Cc: tech@openbsd.org Date: Thu, 30 Oct 2025 21:39:01 +0100 On Thu, Oct 30, 2025 at 07:40:36PM +0000, Job Snijders wrote: > Hello, > > In some of my analytics pipelines it became a nuisance how outputs from > the same validation run could have slightly different timestamps, > because of slight delays in I/O. > > With the below diff the outputs generated from the same validated data > will have the same timestamp, and the modification time on disk will > also reflect this timestamp. > > OK? The output.c changes except the one in outputheader() are largely unrelated and should be done separately. The rest is ok with me. > > Kind regards, > > Job > > Index: ccr.c > =================================================================== > RCS file: /cvs/src/usr.sbin/rpki-client/ccr.c,v > diff -u -p -r1.26 ccr.c > --- ccr.c 18 Oct 2025 08:12:32 -0000 1.26 > +++ ccr.c 30 Oct 2025 19:36:21 -0000 > @@ -603,7 +603,6 @@ static CanonicalCacheRepresentation * > generate_ccr(struct validation_data *vd) > { > CanonicalCacheRepresentation *ccr = NULL; > - time_t now = get_current_time(); > > if ((ccr = CanonicalCacheRepresentation_new()) == NULL) > errx(1, "CanonicalCacheRepresentation_new"); > @@ -612,7 +611,7 @@ generate_ccr(struct validation_data *vd) > if ((ccr->hashAlg = OBJ_nid2obj(NID_sha256)) == NULL) > errx(1, "OBJ_nid2obj"); > > - if (ASN1_GENERALIZEDTIME_set(ccr->producedAt, now) == NULL) > + if (ASN1_GENERALIZEDTIME_set(ccr->producedAt, vd->buildtime) == NULL) > errx(1, "ASN1_GENERALIZEDTIME_set"); > > if ((ccr->mfts = generate_manifeststate(vd)) == NULL) > Index: extern.h > =================================================================== > RCS file: /cvs/src/usr.sbin/rpki-client/extern.h,v > diff -u -p -r1.265 extern.h > --- extern.h 16 Oct 2025 06:46:31 -0000 1.265 > +++ extern.h 30 Oct 2025 19:36:21 -0000 > @@ -512,6 +512,7 @@ struct ccr { > }; > > struct validation_data { > + time_t buildtime; > struct vrp_tree vrps; > struct brk_tree brks; > struct vap_tree vaps; > Index: main.c > =================================================================== > RCS file: /cvs/src/usr.sbin/rpki-client/main.c,v > diff -u -p -r1.300 main.c > --- main.c 17 Oct 2025 08:09:21 -0000 1.300 > +++ main.c 30 Oct 2025 19:36:21 -0000 > @@ -1553,6 +1553,8 @@ main(int argc, char *argv[]) > timespecadd(&stats.system_time, &ts, &stats.system_time); > } > > + vd.buildtime = get_current_time(); > + > /* change working directory to the output directory */ > if (fchdir(outdirfd) == -1) > err(1, "fchdir output dir"); > Index: output-bird.c > =================================================================== > RCS file: /cvs/src/usr.sbin/rpki-client/output-bird.c,v > diff -u -p -r1.25 output-bird.c > --- output-bird.c 23 Aug 2025 09:13:14 -0000 1.25 > +++ output-bird.c 30 Oct 2025 19:36:21 -0000 > @@ -25,7 +25,6 @@ output_bird(FILE *out, struct validation > { > struct vrp *v; > struct vap *vap; > - time_t now = get_current_time(); > size_t i; > > if (fprintf(out, "# For BIRD %s\n#\n", excludeaspa ? "2" : "2.16+") < 0) > @@ -35,7 +34,8 @@ output_bird(FILE *out, struct validation > return -1; > > if (fprintf(out, "\ndefine force_roa_table_update = %lld;\n\n" > - "roa4 table ROAS4;\nroa6 table ROAS6;\n", (long long)now) < 0) > + "roa4 table ROAS4;\nroa6 table ROAS6;\n", > + (long long)vd->buildtime) < 0) > return -1; > > if (!excludeaspa) { > Index: output-json.c > =================================================================== > RCS file: /cvs/src/usr.sbin/rpki-client/output-json.c,v > diff -u -p -r1.57 output-json.c > --- output-json.c 17 Sep 2025 12:10:08 -0000 1.57 > +++ output-json.c 30 Oct 2025 19:36:21 -0000 > @@ -28,11 +28,9 @@ outputheader_json(struct validation_data > { > char hn[NI_MAXHOST], tbuf[26]; > struct tm *tp; > - time_t t; > int i; > > - time(&t); > - tp = gmtime(&t); > + tp = gmtime(&vd->buildtime); > strftime(tbuf, sizeof tbuf, "%FT%TZ", tp); > > gethostname(hn, sizeof hn); > Index: output.c > =================================================================== > RCS file: /cvs/src/usr.sbin/rpki-client/output.c,v > diff -u -p -r1.42 output.c > --- output.c 23 Aug 2025 09:13:14 -0000 1.42 > +++ output.c 30 Oct 2025 19:36:21 -0000 > @@ -76,7 +76,7 @@ static const struct outputs { > > static FILE *output_createtmp(char *); > static void output_cleantmp(void); > -static int output_finish(FILE *); > +static int output_finish(FILE *, time_t); > static void sig_handler(int); > static void set_signal_handler(void); > > @@ -152,7 +152,7 @@ outputfiles(struct validation_data *vd, > rc = 1; > continue; > } > - if (output_finish(fout) != 0) { > + if (output_finish(fout, vd->buildtime) != 0) { > warn("finish for %s format failed", outputs[i].name); > output_cleantmp(); > rc = 1; > @@ -187,12 +187,23 @@ output_createtmp(char *name) > } > > static int > -output_finish(FILE *out) > +output_finish(FILE *out, time_t buildtime) > { > + struct timespec ts[2]; > + > if (fclose(out) != 0) > return -1; > + > + ts[0].tv_nsec = UTIME_OMIT; > + ts[1].tv_sec = buildtime; > + ts[1].tv_nsec = 0; > + > + if (utimensat(AT_FDCWD, output_tmpname, ts, 0) == -1) > + return -1; > + > if (rename(output_tmpname, output_name) == -1) > return -1; > + > output_tmpname[0] = '\0'; > return 0; > } > @@ -243,11 +254,9 @@ outputheader(FILE *out, struct validatio > { > char hn[NI_MAXHOST], tbuf[80]; > struct tm *tp; > - time_t t; > int i; > > - time(&t); > - tp = gmtime(&t); > + tp = gmtime(&vd->buildtime); > strftime(tbuf, sizeof tbuf, "%a %b %e %H:%M:%S UTC %Y", tp); > > gethostname(hn, sizeof hn); >