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, status;
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)) 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 rpid= waitpid(child,&status,0); if (rpid!=child) diee("waitpid");
128 if (WIFEXITED(status)) {
129 int st= WEXITSTATUS(status);
130 if (st) die("persist conversion exited with nonzero status %d",st);
131 } else if (WIFSIGNALED(status)) {
132 die("persist conversion died due to %s%s",
133 strsignal(WTERMSIG(status)),
134 WCOREDUMP(status) ? " (core dumped)" : "");
136 die("persist conversion failed with unexpected wait status 0x%x",status);
139 if (close(newrecord_fd)) diee("persist data close new record");
145 static int try(const char *data, const char *conv) {
146 if (!persist_convert(data,conv)) return 0;
147 logmsg(0,0,0, "converted %s using %s",data,conv);
151 void persist_entrails_interpret(void) {
152 /* creates persist_record_converted */
153 persist_record_converted= mstrdup(FN1(record));
155 try(FN1(data), FN1(conv)) ||
156 try(FN(data,old), FN(conv,old)) ||
157 try(FN1(data), FN(conv,old)) ||
158 (free(persist_record_converted),
159 persist_record_converted=0);
162 /*---------- stupid mmap workaround ----------*/
164 static Byte resaddrbuf[1024*1024];
165 static long pagesize;
166 static unsigned long resaddruse;
168 #define RESADDR_DIEFMT "(persist mapping parameters:" \
169 " %p+0x%lx%%0x%lx->%p+0x%lx)"
170 #define RESADDR_DIEARGS resaddrbuf,(unsigned long)sizeof(resaddrbuf), \
171 pagesize, mapbase,resaddruse
173 static void resaddrdiee(const char *why) {
174 diee("%s " RESADDR_DIEFMT, why, RESADDR_DIEARGS);
177 void persist_map_veryearly(void) {
180 errno= 0; pagesize= sysconf(_SC_PAGE_SIZE);
181 if (pagesize<=0) diee("could not find pagesize");
183 if (pagesize & (pagesize-1)) return;
184 if (pagesize > sizeof(resaddrbuf)/2) return;
186 mapbase= (void*)(((unsigned long)resaddrbuf + pagesize - 1) &
188 resaddruse= sizeof(resaddrbuf) - pagesize;
190 r= mprotect(mapbase,resaddruse,PROT_READ);
191 if (r) resaddrdiee("mprotect reserve buffer");
194 static void mapmem(int fd, int datalen, int prot) {
199 die("inappropriate combination of sizes " RESADDR_DIEFMT, RESADDR_DIEARGS);
201 if (datalen > resaddruse)
202 die("data length %d too large " RESADDR_DIEFMT, datalen, RESADDR_DIEARGS);
204 r= munmap(mapbase, resaddruse);
205 if (r) resaddrdiee("munmap reserve buffer");
207 rv= mmap(mapbase, datalen, prot, MAP_SHARED|MAP_FIXED, fd, 0);
208 if (rv == MAP_FAILED)
209 resaddrdiee(prot==(PROT_READ|PROT_WRITE) ? "map data rw" :
210 prot==PROT_READ ? "map data ro" : "map data badly");
212 assert(rv == mapbase);
215 /*---------- installing of our data as the current one ----------*/
217 void persist_install(void) {
221 char *dirname_buf, *slash;
226 src= fopen("/proc/self/exe","rb"); if (!src) diee("open /proc/self/exe");
228 unlink_or_enoent(FN(conv,new));
229 dst= fopen(FN(conv,new),"wb"); if (!dst) diee("create persist new conv");
231 while ((c= getc(src)) != EOF)
232 if (putc(c,dst) == EOF) diee("write persist new conv");
234 if (ferror(src) || fclose(src)) diee("read /proc/self/exe");
235 if (ferror(dst) || fflush(dst) || fsync(fileno(dst)) || fclose(dst))
236 diee("finish writing persist new conv");
238 if (fsync(fd) || msync(mapbase,datalen,MS_SYNC) || fsync(fd))
239 diee("sync persist new data");
241 /* Now we have the .new's, but let's just check ... */
242 if (!persist_convert(FN(data,new),FN(conv,new)))
243 die("persist conversion claims .new's do not exist ?!");
245 dirname_buf= mstrdup(persist_fn);
246 slash= strrchr(dirname_buf, '/');
247 if (slash) do { *slash=0; } while (slash>dirname_buf && *--slash=='/');
248 dirname= slash ? dirname_buf : ".";
249 dir= opendir(dirname);
250 if (!dir) diee("opendir persist directory `%s'", dirname);
252 if (fe(FN1(data)) && fe(FN1(conv))) { /* 1 ? 1 ? A */
253 unlink_or_enoent(FN(conv,old)); /* 1 ? 1 - A */
254 unlink_or_enoent(FN(data,old)); /* 1 - 1 - A */
255 mrename(FN1(conv),FN(conv,old)); /* 1 - 1 1 A */
256 /* rename completes 1 - - 1 C */
258 /* we've converted A to C, so only B and C remain: */
259 if (fe(FN(data,old)) && fe(FN(conv,old))) { /* ? 1 ? 1 B */
260 unlink_or_enoent(FN1(data)); /* - 1 ? 1 B unlike C */
262 /* B has been made not to look like C, so now only
263 * genuine C and unmistakeable B remains: */
264 if (fe(FN1(data)) && fe(FN(conv,old))) { /* 1 ? ? 1 C */
265 mrename(FN1(data),FN(data,old)); /* 1 1 ? 1 B */
266 /* rename completes - 1 ? 1 B unlike A or C */
268 /* Just B now, ie we have */ /* - 1 ? 1 B */
270 unlink_or_enoent(FN1(conv)); /* - 1 - 1 B */
272 mrename(FN(data,new),FN1(data)); /* 2 1 - 1 B */
273 mrename(FN(conv,new),FN1(conv)); /* 2 1 2 1 A */
275 if (fsync(dirfd(dir))) diee("sync persist directory `%s'", dirname);
278 fd= -1; /* do not install it again */
281 /*---------- creation (and mmapping) of new persistent data ----------*/
283 void *record_allocate(int datalen_spec) {
284 /* claims lock, allocates space for new data file */
288 struct stat buf_stat, buf_fstat;
291 datalen= datalen_spec;
294 lockfd= open(FN1(lock), O_RDWR|O_CREAT|O_TRUNC, 0660);
295 if (lockfd<0) diee("open new persist lock file");
297 memset(&fl,0,sizeof(fl));
299 fl.l_whence= SEEK_SET;
300 r= fcntl(lockfd, F_SETLK, &fl);
301 if (r<0) diee("claim persistent lock file");
303 r= stat(FN1(lock), &buf_stat); if (r) diee("stat persistent lock");
304 r= fstat(lockfd, &buf_fstat); if (r) diee("fstat persistent lock");
305 if (!(buf_stat.st_dev != buf_fstat.st_dev ||
306 buf_stat.st_ino != buf_fstat.st_ino))
313 unlink_or_enoent(FN(data,new));
315 fd= open(FN(data,new), O_RDWR|O_CREAT|O_TRUNC, 0777);
316 if (fd<0) diee("open new persist data file");
317 data= fdopen(fd, "w+"); if (!data) diee("fdopen new persist data file");
319 for (i=0; i<datalen; i++) putc(0x55,data);
320 if (ferror(data) || fflush(data)) diee("clear new persist data file");
324 mapmem(fd, datalen, PROT_READ|PROT_WRITE);
329 /*---------- reading and mapping of existing persistent data ----------*/
331 static void phi_load(void *object, size_t sz, int *offset) {
334 while (*offset % sz) { getchar(); (*offset)++; }
336 r= fread(object,1,sz,stdin);
337 if (feof(stdin)) die("truncated persistent data header");
338 if (ferror(stdin)) diee("read persistent data header");
344 static void phi_check(const void *expected, size_t sz, int *offset) {
347 phi_load(actual, sz, offset);
348 if (memcmp(actual, expected, sz)) die("header magic check failed");
351 static void persist_mapread(void) {
355 r= fstat(0, &stab); if (r) diee("could not fstat persist data file");
356 if (!(stab.st_mode & 0111)) die("persist data file is not executable");
358 #define PHI_CHECK(x) phi_check(&(x), sizeof(x), &offset);
359 #define PHI_LOAD(x) phi_load(&(x), sizeof(x), &offset);
360 DO_PERSIST_HEADER_ITEMS(PHI_CHECK, PHI_LOAD, PHI_LOAD)
362 mapmem(0, datalen, PROT_READ);
365 void persist_entrails_run_converter(void) {
372 if (!tra->pname || !tra->foredetect ||
373 !tra->foredetect->i || !tra->foredetect->i->pname)
375 printf("train %s at %s%s:%d+-%d\n",
376 tra->pname, tra->backwards ? "-" : "",
377 tra->foredetect->i->pname, tra->maxinto, tra->uncertainty);
380 if (seg->i != segi || !segi->pname ||
381 !seg->owner || !seg->owner->pname)
383 printf("seg %s has %s%s\n",
384 segi->pname, seg->tr_backwards ? "-" : "", seg->owner->pname);
386 if (ferror(stdout) || fflush(stdout))
387 diee("entrails converter: stdout write error");
391 if (ferror(stdout) || fclose(stdout))
392 diee("entrails converter: stdout write/close error");