3 * persistent state management.
8 * persist.lock - protocol is as for with-lock-ex
9 * persist.data[.new,.old] - mmap, see record.c alloc
10 * persist.conv[.new,.old] - copy of our own convutable
12 * persist.record generated and updated automatically
14 * we mark the data files as executable, and then later insist on that,
15 * because we map them into our own address space and trust them completely
19 * interpretation of the states data conv
23 * case B ? y ? y but not A
24 * case C y ? ? y but not A or B
26 * (y means file exists, use this version; ? existence irrelevant)
27 * (update protocol ignores .new files, which are used only for
28 * atomic creation of actual files)
31 * procedure for updating .o .o
33 * normal state 1 0 1 0 case A, 1
34 * delete old converter 1 0 1 - case A, 1
35 * delete data.old 1 - 1 - case A, 1
36 * rename converter -> .old 1 - 1 1 case A, 1
37 * rename completes 1 - - 1 case C, 1
38 * rename data -> .old 1 1 - 1 case B, 1
39 * rename completes - 1 - 1 case B, 1
40 * create new data 2 1 - 1 case B, 1
41 * create new converter 2 1 2 1 case A, 2
43 * (0, 1, 2 are successive versions; - is ENOENT)
48 const char *persist_fn= "+persist";
49 char *persist_record_converted;
55 /*---------- filename handling ----------*/
57 #define FN(dcl,suffix) persist_fn_ephemeral("." #dcl "." #suffix)
58 #define FN1(dcl) persist_fn_ephemeral("." #dcl)
61 static const char *persist_fn_ephemeral(const char *suffix) {
62 static char *rr[PFES];
69 if (asprintf(&rr[i], "%s%s", persist_fn, suffix) <= 0)
70 diee("vasprintf failed for persist_fn");
74 /*---------- utilities ----------*/
76 static void unlink_or_enoent(const char *filename) {
80 if (r && errno != ENOENT) diee("unlink `%s'", filename);
83 static int fe(const char *fn) {
89 if (errno==ENOENT) return 0;
90 else diee("failed stat to check for existence of `%s'", fn);
93 if (!S_ISREG(stab.st_mode))
94 die("checking for existence of `%s' but it is not a plain file", fn);
99 /*---------- finding and interpreting of old persistent data ----------*/
101 static int persist_convert(const char *data, const char *conv) {
102 int data_fd, newrecord_fd;
105 if (!fe(conv)) return 0;
107 data_fd= open(data, O_RDONLY);
109 if (errno==ENOENT) return 0;
110 else diee("persist data failed to check/open `%s'",data);
113 newrecord_fd= open(persist_record_converted, O_WRONLY|O_CREAT|O_TRUNC, 0666);
115 diee("persist data failed to create new record");
118 if (child<0) diee("persist conversion: failed to fork");
121 if (dup2(data_fd,0)) diee("persist child: failed to dup2 0");
122 if (dup2(newrecord_fd,1)!=1) diee("persist child: failed to dup2 1");
123 execl(conv, conv, PERSIST_CONVERT_OPTION, (char*)0);
124 diee("persist child: failed to exec `%s'", conv);
127 mwaitpid(child, "persist conversion");
129 if (close(newrecord_fd)) diee("persist data close new record");
135 static int try(const char *data, const char *conv) {
136 if (!persist_convert(data,conv)) return 0;
137 ouprintf("info : converted %s using %s\n",data,conv);
141 void persist_entrails_interpret(void) {
142 /* creates persist_record_converted */
143 if (!persist_fn[0]) return;
144 assert(!persist_record_converted);
145 persist_record_converted= mstrdup(FN1(record));
148 try(FN1(data), FN1(conv))) return;
149 if (try(FN(data,old), FN(conv,old))) return;
152 try(FN1(data), FN1(conv))) return;
154 if (try(FN1(data), FN(conv,old))) return;
156 free(persist_record_converted);
157 persist_record_converted= 0;
160 /*---------- stupid mmap workaround ----------*/
162 static Byte resaddrbuf[1024*1024];
163 static long pagesize;
164 static unsigned long resaddruse;
166 #define RESADDR_DIEFMT "(persist mapping parameters:" \
167 " %p+0x%lx%%0x%lx->%p+0x%lx)"
168 #define RESADDR_DIEARGS resaddrbuf,(unsigned long)sizeof(resaddrbuf), \
169 pagesize, mapbase,resaddruse
171 static void resaddrdiee(const char *why) {
172 diee("%s " RESADDR_DIEFMT, why, RESADDR_DIEARGS);
175 void persist_map_veryearly(void) {
178 errno= 0; pagesize= sysconf(_SC_PAGE_SIZE);
179 if (pagesize<=0) diee("could not find pagesize");
181 if (pagesize & (pagesize-1)) return;
182 if (pagesize > sizeof(resaddrbuf)/2) return;
184 mapbase= (void*)(((unsigned long)resaddrbuf + pagesize - 1) &
186 resaddruse= sizeof(resaddrbuf) - pagesize;
188 r= mprotect(mapbase,resaddruse,PROT_READ);
189 if (r) resaddrdiee("mprotect reserve buffer");
192 static void mapmem(int fd, int datalen, int prot) {
197 die("inappropriate combination of sizes " RESADDR_DIEFMT, RESADDR_DIEARGS);
199 if (datalen > resaddruse)
200 die("data length %d too large " RESADDR_DIEFMT, datalen, RESADDR_DIEARGS);
202 r= munmap(mapbase, resaddruse);
203 if (r) resaddrdiee("munmap reserve buffer");
205 rv= mmap(mapbase, datalen, prot, MAP_SHARED|MAP_FIXED, fd, 0);
206 if (rv == MAP_FAILED)
207 resaddrdiee(prot==(PROT_READ|PROT_WRITE) ? "map data rw" :
208 prot==PROT_READ ? "map data ro" : "map data badly");
210 assert(rv == mapbase);
213 /*---------- installing of our data as the current one ----------*/
215 void persist_install(void) {
216 const char *devnull= "/dev/null";
220 char *dirname_buf, *slash;
224 if (!persist_fn[0]) return;
226 src= fopen("/proc/self/exe","rb"); if (!src) diee("open /proc/self/exe");
228 unlink_or_enoent(FN(conv,new));
229 dst_fd= open(FN(conv,new), O_WRONLY|O_CREAT|O_TRUNC, 0777);
230 if (dst_fd<0) diee("create persist new conv");
231 dst= fdopen(dst_fd,"wb"); if (!dst) diee("fdopen persist new conv");
233 while ((c= getc(src)) != EOF)
234 if (putc(c,dst) == EOF) diee("write persist new conv");
236 if (ferror(src) || fclose(src)) diee("read /proc/self/exe");
237 if (ferror(dst) || fflush(dst) || fsync(fileno(dst)) || fclose(dst))
238 diee("finish writing persist new conv");
240 if (fsync(fd) || msync(mapbase,datalen,MS_SYNC) || fsync(fd))
241 diee("sync persist new data");
243 /* Now we have the .new's, but let's just check ... */
244 persist_record_converted= (char*)devnull;
245 if (!persist_convert(FN(data,new),FN(conv,new)))
246 die("persist conversion claims .new's do not exist ?!");
247 assert(persist_record_converted == devnull);
248 persist_record_converted= 0;
250 dirname_buf= mstrdup(persist_fn);
251 slash= strrchr(dirname_buf, '/');
252 if (slash) do { *slash=0; } while (slash>dirname_buf && *--slash=='/');
253 dirname= slash ? dirname_buf : ".";
254 dir= opendir(dirname);
255 if (!dir) diee("opendir persist directory `%s'", dirname);
257 if (fe(FN1(data)) && fe(FN1(conv))) { /* 1 ? 1 ? A */
258 unlink_or_enoent(FN(conv,old)); /* 1 ? 1 - A */
259 unlink_or_enoent(FN(data,old)); /* 1 - 1 - A */
260 mrename(FN1(conv),FN(conv,old)); /* 1 - 1 1 A */
261 /* rename completes 1 - - 1 C */
263 /* we've converted A to C, so only B and C remain: */
264 if (fe(FN(data,old)) && fe(FN(conv,old))) { /* ? 1 ? 1 B */
265 unlink_or_enoent(FN1(data)); /* - 1 ? 1 B unlike C */
267 /* B has been made not to look like C, so now only
268 * genuine C and unmistakeable B remains: */
269 if (fe(FN1(data)) && fe(FN(conv,old))) { /* 1 ? ? 1 C */
270 mrename(FN1(data),FN(data,old)); /* 1 1 ? 1 B */
271 /* rename completes - 1 ? 1 B unlike A or C */
273 /* Just B now, ie we have */ /* - 1 ? 1 B */
275 unlink_or_enoent(FN1(conv)); /* - 1 - 1 B */
277 mrename(FN(data,new),FN1(data)); /* 2 1 - 1 B */
278 mrename(FN(conv,new),FN1(conv)); /* 2 1 2 1 A */
280 if (fsync(dirfd(dir))) diee("sync persist directory `%s'", dirname);
283 fd= -1; /* do not install it again */
286 /*---------- creation (and mmapping) of new persistent data ----------*/
288 void *record_allocate(int datalen_spec) {
289 /* claims lock, allocates space for new data file */
293 struct stat buf_stat, buf_fstat;
296 datalen= datalen_spec;
299 lockfd= open(FN1(lock), O_RDWR|O_CREAT|O_TRUNC, 0660);
300 if (lockfd<0) diee("open new persist lock file");
302 memset(&fl,0,sizeof(fl));
304 fl.l_whence= SEEK_SET;
305 r= fcntl(lockfd, F_SETLK, &fl);
306 if (r<0) diee("claim persistent lock file");
308 r= stat(FN1(lock), &buf_stat); if (r) diee("stat persistent lock");
309 r= fstat(lockfd, &buf_fstat); if (r) diee("fstat persistent lock");
310 if (!(buf_stat.st_dev != buf_fstat.st_dev ||
311 buf_stat.st_ino != buf_fstat.st_ino))
318 unlink_or_enoent(FN(data,new));
320 fd= open(FN(data,new), O_RDWR|O_CREAT|O_TRUNC, 0777);
321 if (fd<0) diee("open new persist data file");
322 data= fdopen(fd, "w+"); if (!data) diee("fdopen new persist data file");
324 for (i=0; i<datalen; i++) putc(0x55,data);
325 if (ferror(data) || fflush(data)) diee("clear new persist data file");
329 mapmem(fd, datalen, PROT_READ|PROT_WRITE);
334 /*---------- reading and mapping of existing persistent data ----------*/
336 static void phi_load(void *object, size_t sz, int *offset) {
339 while (*offset % sz) { getchar(); (*offset)++; }
341 r= fread(object,1,sz,stdin);
342 if (feof(stdin)) die("truncated persistent data header");
343 if (ferror(stdin)) diee("read persistent data header");
349 static void phi_check(const void *expected, size_t sz,
350 int *offset, const char *what) {
353 phi_load(actual, sz, offset);
354 if (memcmp(actual, expected, sz))
355 die("header magic check failed, in `%s'", what);
358 static void persist_mapread(void) {
362 r= fstat(0, &stab); if (r) diee("could not fstat persist data file");
363 if (!(stab.st_mode & 0111)) die("persist data file is not executable");
365 #define PHI_CHECK(x) phi_check(&(x), sizeof(x), &offset, STR(x));
366 #define PHI_LOAD(x) phi_load(&(x), sizeof(x), &offset);
367 DO_PERSIST_HEADER_ITEMS(PHI_CHECK, PHI_LOAD, PHI_LOAD)
369 mapmem(0, datalen, PROT_READ);
372 #define SANE_SEGMENT(seg)
374 void persist_entrails_run_converter(void) {
381 if (seg->i != segi || !segi->pname ||
382 !seg->owner || !seg->owner->pname)
384 printf("seg %s has %s%s%s\n", segi->pname,
385 (seg->tr_backwards ^ seg->owner->backwards) ? "-" : "",
387 seg->seg_inverted ? " inverted" : "");
389 if (segi->n_poscombs>1 &&
391 (report= seg->movposcomb) >=0 &&
392 report < segi->n_poscombs)
393 printf("seg %s at %s\n", segi->pname, segi->poscombs[report].pname);
395 if (ferror(stdout) || fflush(stdout))
396 diee("entrails converter: stdout write error");
400 if (ferror(stdout) || fclose(stdout))
401 diee("entrails converter: stdout write/close error");