chiark / gitweb /
new persistent arrangements compile but do not work
authorian <ian>
Mon, 11 Feb 2008 03:24:41 +0000 (03:24 +0000)
committerian <ian>
Mon, 11 Feb 2008 03:24:41 +0000 (03:24 +0000)
16 files changed:
hostside/.cvsignore
hostside/Makefile
hostside/TODO
hostside/analyse-speeds
hostside/common.h
hostside/persist.c [new file with mode: 0644]
hostside/realtime.c
hostside/realtime.h
hostside/record-i.h [new file with mode: 0644]
hostside/record-l.l
hostside/record-y.y
hostside/record.c
hostside/record.h
hostside/safety.h
hostside/startup.c
hostside/utils.c

index db748bad9564151cdf1a2ec14ddbdd935dfa2d9a..024a957c6eafb41f5830643884ce10afcbcdd556 100644 (file)
@@ -18,3 +18,10 @@ stastate.h
 retransmit-table.h
 record-l.[ch]
 record-y.[ch]
+persist.lock
+persist.data.old
+persist.data
+persist.data.new
+persist.conv.old
+persist.conv
+persist.conv.new
index 7e3b8ae0159fe7f2832e5c68e621efe32905c514..9c75a7806e8bd6450e3d762752f5458392836c4d 100644 (file)
@@ -29,7 +29,7 @@ on-bessar:    $(TARGETS)
                RSYNC_RSH=fsh rsync $^ $(BESSAR)
 
 realtime:      realtime.o startup.o cdumgr.o safety.o trackloc.o       \
-                speed.o actual.o retransmit.o                          \
+                speed.o actual.o retransmit.o persist.o                \
                 cmdinput.o commands.o obc.o eventhelp.o                \
                 record.o record-l.o record-y.o                         \
                 utils.o serialio.o parseutils.o auproto-pic.o          \
index 1c8443e6bb59351a094f627e7bc11a5872ce3fff..7438dfd5fc8958569d47cfb8e135287b2db3749c 100644 (file)
@@ -14,3 +14,8 @@ things not yet considered at all in safety code
        min. curve specifications
 
 initialise safety_state with appropriate stuff
+
+
+
+make safety stop not be estop
+check safety stop polarity is enough to cope with that
index 22694dba6f81355a0be9aa00a53f99ab349e2d78..e4dc53c80c3b1dd517d0f89f32058ccb9a89f455 100755 (executable)
@@ -72,6 +72,7 @@ END
 
 perl <$o.table.tmp >$o.record.tmp -ne '
  BEGIN {
+       use IO::Handle;
        $nxt= 1;
        $max= 0;
        $speed[0]= 0;
@@ -106,6 +107,8 @@ perl <$o.table.tmp >$o.record.tmp -ne '
                        calcwait($step,'$deceltime')
                                or die $!;
        }
+       STDOUT->error and die $!;
+       print "end\n" or die $!;
  }
 '
 
index b44acbd2966764003d3e1e2f058d109ab2720139..c27ba145982900f02f2241c43974e625cf229739 100644 (file)
@@ -102,6 +102,8 @@ char *mstrdupl(const char *s, int l);
 char *mstrdup(const char *s);
 void mgettimeofday(struct timeval *tv);
 
+void mrename(const char *old, const char *new);
+
 void badusage(const char *why);
 
 #define massert(x) ((x) ? (void)0 : diem())
diff --git a/hostside/persist.c b/hostside/persist.c
new file mode 100644 (file)
index 0000000..1f5f07a
--- /dev/null
@@ -0,0 +1,359 @@
+/*
+ * persist
+ * persistent state management.
+ */
+
+/*
+ * We use these files:
+ *      persist.lock             - protocol is as for with-lock-ex
+ *      persist.data[.new,.old]          - mmap, see record.c alloc
+ *      persist.conv[.new,.old]          - copy of our own convutable
+ *
+ *      persist.record     generated and updated automatically
+ *
+ * we mark the data files as executable, and then later insist on that,
+ * because we map them into our own address space and trust them completely
+ *
+ * Update protocol:
+ *
+ *   interpretation of the states      data conv
+ *                                      .o  .o
+ *
+ *     case A                          y ? y ?
+ *     case B                          ? y ? y         but not A
+ *     case C                          y ? ? y         but not A or B
+ *
+ *      (y means file exists, use this version; ? existence irrelevant)
+ *      (update protocol ignores .new files, which are used only for
+ *       atomic creation of actual files)
+ *
+ *                                    data conv
+ *   procedure for updating             .o  .o
+ *
+ *     normal state                    1 0 1 0         case A, 1
+ *     delete old converter            1 0 1 -         case A, 1
+ *     delete data.old                 1 - 1 -         case A, 1
+ *     rename converter -> .old        1 - 1 1         case A, 1
+ *      rename completes               1 - - 1         case C, 1
+ *     rename data -> .old             1 1 - 1         case B, 1
+ *      rename completes               - 1 - 1         case B, 1
+ *     create new data                 2 1 - 1         case B, 1
+ *     create new converter            2 1 2 1         case A, 2
+ *
+ *     (0, 1, 2 are successive versions; - is ENOENT)
+ */
+
+#include "realtime.h"
+
+const char *persist_fn= "persist";
+const char *persist_record_converted;
+
+static int fd= -1;
+static void *mapbase;
+static int datalen;
+
+/*---------- filename handling ----------*/
+
+#define FN(dcl,suffix) persist_fn_ephemeral("." #dcl "." #suffix)
+#define FN1(dcl)       persist_fn_ephemeral("." #dcl)
+
+#define PFES 20
+static const char *persist_fn_ephemeral(const char *suffix) {
+  static char *rr[PFES];
+  static int i;
+
+  i++;
+  i %= PFES;
+
+  free(rr[i]);
+  if (asprintf(&rr[i], "%s%s", persist_fn, suffix) <= 0)
+    diee("vasprintf failed for persist_fn");
+  return rr[i];
+}
+
+/*---------- utilities ----------*/
+
+static void unlink_or_enoent(const char *filename) {
+  int r;
+
+  r= unlink(filename);
+  if (r && errno != ENOENT) diee("unlink `%s'", filename);
+}
+
+static int fe(const char *fn) {
+  struct stat stab;
+  int r;
+
+  r= stat(fn,&stab);
+  if (r) {
+    if (errno==ENOENT) return 0;
+    else diee("failed stat to check for existence of `%s'", fn);
+  }
+
+  if (!S_ISREG(stab.st_mode))
+    die("checking for existence of `%s' but it is not a plain file", fn);
+
+  return 1;
+}
+
+/*---------- finding and interpreting of old persistent data ----------*/
+
+static int persist_convert(const char *data, const char *conv) {
+  int data_fd, newrecord_fd, status;
+  pid_t child, rpid;
+
+  if (!fe(conv)) return 0;
+
+  data_fd= open(data, O_RDONLY);
+  if (data_fd<0) {
+    if (errno==ENOENT) return 0;
+    else diee("persist data failed to check/open `%s'",data);
+  }
+
+  newrecord_fd= open(persist_record_converted, O_WRONLY|O_CREAT|O_TRUNC, 0666);
+  if (newrecord_fd<0)
+    diee("persist data failed to create new record");
+
+  child= fork();
+  if (child<0) diee("persist conversion: failed to fork");
+
+  if (!child) {
+    if (dup2(data_fd,0)) diee("persist child: failed to dup2 0");
+    if (dup2(newrecord_fd,1)) diee("persist child: failed to dup2 1");
+    execl(conv, conv, PERSIST_CONVERT_OPTION, (char*)0);
+    diee("persist child: failed to exec `%s'", conv);
+  }
+  
+  rpid= waitpid(child,&status,0);  if (rpid!=child) diee("waitpid");
+  if (WIFEXITED(status)) {
+    int st= WEXITSTATUS(status);
+    if (st) die("persist conversion exited with nonzero status %d",st);
+  } else if (WIFSIGNALED(status)) {
+    die("persist conversion died due to %s%s",
+       strsignal(WTERMSIG(status)),
+       WCOREDUMP(status) ? " (core dumped)" : "");
+  } else {
+    die("persist conversion failed with unexpected wait status 0x%x",status);
+  }
+
+  if (close(newrecord_fd)) diee("persist data close new record");
+  close(data_fd);
+
+  return 1;
+}
+
+static int try(const char *data, const char *conv) {
+  if (!persist_convert(data,conv)) return 0;
+  logmsg(0,0,0, "converted %s using %s",data,conv);
+  return 1;
+}
+
+void persist_entrails_interpret(void) {
+  /* creates persist_record_converted */
+  persist_record_converted= mstrdup(FN1(record));
+
+  try(FN1(data),    FN1(conv)) ||
+  try(FN(data,old), FN(conv,old)) ||
+  try(FN1(data),    FN(conv,old)) ||
+    (persist_record_converted= 0);
+}
+
+/*---------- installing of our data as the current one ----------*/
+
+void persist_install(void) {
+  FILE *src, *dst;
+  DIR *dir;
+  const char *dirname;
+  char *dirname_buf, *slash;
+  int c;
+
+  if (fd==-1) return;
+  
+  src= fopen("/proc/self/exe","rb");  if (!src) diee("open /proc/self/exe");
+
+  unlink_or_enoent(FN(conv,new));
+  dst= fopen(FN(conv,new),"wb");  if (!dst) diee("create persist new conv");
+
+  while ((c= getc(src)) != EOF)
+    if (putc(c,dst) == EOF) diee("write persist new conv");
+
+  if (ferror(src) || fclose(src)) diee("read /proc/self/exe");
+  if (ferror(dst) || fflush(dst) || fsync(fileno(dst)) || fclose(dst))
+    diee("finish writing persist new conv");
+
+  if (fsync(fd) || msync(mapbase,datalen,MS_SYNC) || fsync(fd))
+    diee("sync persist new data");
+
+  /* Now we have the .new's, but let's just check ... */
+  if (!persist_convert(FN(data,new),FN(conv,new)))
+    die("persist conversion claims .new's do not exist ?!");
+
+  dirname_buf= mstrdup(persist_fn);
+  slash= strrchr(dirname_buf, '/');
+  if (slash) do { *slash=0; } while (slash>dirname_buf && *--slash=='/');
+  dirname= slash ? dirname_buf : ".";
+  dir= opendir(dirname);
+  if (!dir) diee("opendir persist directory `%s'", dirname);
+
+  if (fe(FN1(data)) && fe(FN1(conv))) {        /* 1 ? 1 ?   A               */
+    unlink_or_enoent(FN(conv,old));            /* 1 ? 1 -   A               */
+    unlink_or_enoent(FN(data,old));            /* 1 - 1 -   A               */
+    mrename(FN1(conv),FN(conv,old));           /* 1 - 1 1   A               */
+                       /* rename completes        1 - - 1   C               */
+  }
+  /* we've converted A to C, so only B and C remain: */
+  if (fe(FN(data,old)) && fe(FN(conv,old))) {  /* ? 1 ? 1   B               */
+    unlink_or_enoent(FN1(data));               /* - 1 ? 1   B unlike C      */
+  }
+  /* B has been made not to look like C, so now only
+   * genuine C and unmistakeable B remains: */
+  if (fe(FN1(data)) && fe(FN(conv,old))) {     /* 1 ? ? 1   C               */
+    mrename(FN1(data),FN(data,old));           /* 1 1 ? 1   B               */
+                       /* rename completes        - 1 ? 1   B unlike A or C */
+  }
+  /* Just B now, ie we have */                 /* - 1 ? 1   B               */
+                                                          
+  unlink_or_enoent(FN1(conv));                 /* - 1 - 1   B               */
+                                                          
+  mrename(FN(data,new),FN1(data));             /* 2 1 - 1   B               */
+  mrename(FN(conv,new),FN1(conv));             /* 2 1 2 1   A               */
+
+  if (fsync(dirfd(dir))) diee("sync persist directory `%s'", dirname);
+
+  free(dirname_buf);
+  fd= -1; /* do not install it again */
+}
+
+/*---------- creation (and mmapping) of new persistent data ----------*/
+
+void *record_allocate(int datalen_spec) {
+  /* claims lock, allocates space for new data file */
+  int lockfd, r, i;
+  FILE *data;
+  struct flock fl;
+  struct stat buf_stat, buf_fstat;
+
+  assert(fd==-1);
+  datalen= datalen_spec;
+  
+  for (;;) {
+    lockfd= open(FN1(lock), O_RDWR|O_CREAT|O_TRUNC, 0660);
+    if (lockfd<0) diee("open new persist lock file");
+
+    memset(&fl,0,sizeof(fl));
+    fl.l_type= F_WRLCK;
+    fl.l_whence= SEEK_SET;
+    r= fcntl(lockfd, F_SETLK, &fl);
+    if (r<0) diee("claim persistent lock file");
+
+    r= stat(FN1(lock), &buf_stat);  if (r) diee("stat persistent lock");
+    r= fstat(lockfd, &buf_fstat);  if (r) diee("fstat persistent lock");
+    if (!(buf_stat.st_dev != buf_fstat.st_dev ||
+         buf_stat.st_ino != buf_fstat.st_ino))
+      break;
+
+    close(lockfd);
+  }
+
+  
+  unlink_or_enoent(FN(data,new));
+
+  fd= open(FN(data,new), O_RDWR|O_CREAT|O_TRUNC, 0777);
+  if (fd<0) diee("open new persist data file");
+  data= fdopen(fd, "w+");  if (!data) diee("fdopen new persist data file");
+
+  for (i=0; i<datalen; i++) putc(0x55,data);
+  if (ferror(data) || fflush(data)) diee("clear new persist data file");
+
+  fd= fileno(data);
+
+  mapbase= mmap(0, datalen, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
+  if (mapbase == MAP_FAILED) diee("could not write mmap persist data file");
+
+  return mapbase;
+}
+
+/*---------- reading and mapping of existing persistent data ----------*/
+
+static void phi_load(void *object, size_t sz, int *offset) {
+  size_t r;
+
+  while (*offset % sz) { getchar(); (*offset)++; }
+
+  r= fread(object,1,sz,stdin);
+  if (feof(stdin))  die("truncated persistent data header");
+  if (ferror(stdin)) diee("read persistent data header");
+  assert (r==sz);
+
+  *offset += sz;
+}
+
+static void phi_check(const void *expected, size_t sz, int *offset) {
+  Byte actual[sz];
+
+  phi_load(actual, sz, offset);
+  if (memcmp(actual, expected, sz)) die("header magic check failed");
+}
+  
+static void *persist_mapread(void) {
+  struct stat stab;
+  int offset=0, r;
+  void *rv;
+
+  r= fstat(0, &stab);  if (r) diee("could not fstat persist data file");
+  if (!(stab.st_mode & 0111)) die("persist data file is not executable");
+
+#define PHI_CHECK(x) phi_check(&(x), sizeof(x), &offset);
+#define PHI_LOAD(x) phi_load(&(x), sizeof(x), &offset);
+  DO_PERSIST_HEADER_ITEMS(PHI_CHECK, PHI_LOAD, PHI_LOAD)
+
+  rv= mmap(0, datalen, PROT_READ|PROT_WRITE, MAP_PRIVATE, 0,0);
+  if (rv == MAP_FAILED) diee("could not read mmap persist data file");
+  return rv;
+}
+
+void persist_entrails_run_converter(void) {
+  Train *tra; int tran;
+  Segment *seg; const SegmentInfo *segi; int segn;
+  void *realbase;
+  ptrdiff_t adjust;
+
+  realbase= persist_mapread();
+  adjust= (Byte*)realbase - (Byte*)mapbase;
+
+#define CP(lvalue) \
+  ((lvalue)= (lvalue) ? (void*)((Byte*)(lvalue) + adjust) : 0);
+
+#define PHI_IGNORE(x) /*nothing*/
+  DO_PERSIST_HEADER_ITEMS(PHI_IGNORE, PHI_IGNORE, CP)
+
+  for (tran=0, tra=trains; tran<n_trains; tran++, tra++) {
+    CP(tra->pname);
+    CP(tra->foredetect);
+    
+    if (!tra->pname || !tra->foredetect ||
+       !tra->foredetect->i || !tra->foredetect->i->pname)
+      continue;
+    printf("train %s at %s%s:%d+-%d\n",
+          tra->pname, tra->backwards ? "-" : "",
+          tra->foredetect->i->pname, tra->maxinto, tra->uncertainty);
+  }
+  for (segn=0, seg=segments, segi=info_segments; segn<info_nsegments;
+       segn++, seg++, segi++) {
+    CP(seg->owner);
+    
+    if (seg->i != segi || !segi->pname ||
+       !seg->owner || !seg->owner->pname)
+      continue;
+    printf("seg %s has %s%s\n",
+          segi->pname, seg->tr_backwards ? "-" : "", seg->owner->pname);
+  }
+  if (ferror(stdout) || fflush(stdout))
+    diee("entrails converter: stdout write error");
+
+  printf("end\n");
+  
+  if (ferror(stdout) || fclose(stdout))
+    diee("entrails converter: stdout write/close error");
+  exit(0);
+}
index 4aad568c79dfa5d04856cf98026b4dfd0754cacb..6cd3a2e60be3bf33f101e2cea92a4335a8164501 100644 (file)
@@ -245,12 +245,12 @@ void ouhex(const char *word, const Byte *command, int length) {
 }
  
 void die_vprintf_hook(const char *fmt, va_list al) {
-  ovprintf(UPO, fmt, al);
+  if (events) ovprintf(UPO, fmt, al);
 }
  
 void die_hook(void) {
   int e;
-  e= obc_tryflush(UPO);
+  e= events ? obc_tryflush(UPO) : 0;
   if (e) fprintf(stderr,"(unwritten command output: %s)\n",strerror(e));
 }
 
@@ -339,6 +339,10 @@ int main(int argc, const char **argv) {
   const char *arg;
   int r;
 
+  if (argv[0] && argv[1] && !strcmp(argv[1],PERSIST_CONVERT_OPTION))
+    /* do this before we call malloc so that MAP_FIXED is sure to work */
+    persist_entrails_run_converter();
+
   sys_events= oop_sys_new();  if (!sys_events) diee("oop_sys_new");
   events= oop_sys_source(sys_events);  massert(events);
 
@@ -353,11 +357,13 @@ int main(int argc, const char **argv) {
     arg++;
     switch (*arg++) {
     case 's': device= arg; break;
+    case 'p': persist_fn= arg; break;
     case 'v': picio_send_noise= atoi(arg); break;
     default: badusage("unknown option");
     }
   }
 
+  persist_entrails_interpret();
   records_parse(argv);
 
   serial_open(device);
index 94413e84f04678aadf4c1f44995aef5f0d4d618f..786e30be1ab788329b6b81d30d4c0d64d61fdf8e 100644 (file)
 
 #include <sys/types.h>
 #include <sys/time.h>
+#include <sys/stat.h>
+#include <sys/mman.h>
+#include <sys/wait.h>
+
+#include <unistd.h>
+#include <fcntl.h>
+#include <dirent.h>
 
 #include "../layout/layout-data.h"
 
@@ -83,9 +90,15 @@ void serial_moredata(PicInsn *buf);
 extern StartupState sta_state;
 extern const char *const stastatelist[];
 
-/*---------- from/for record.c ----------*/
+/*---------- from/for record.c and persist.c ----------*/
 
 void records_parse(const char **argv);
+void persist_entrails_interpret(void);
+void persist_entrails_run_converter(void);
+void persist_install(void);
+
+extern const char *persist_fn;
+extern const char *persist_record_converted;
 
 /*---------- from/for realtime.c ----------*/
 
@@ -104,6 +117,9 @@ int picinsn_polarity_testbit(const PicInsn *pi, const SegmentInfo *segi);
 
 void abandon_run(void);
 
+#include "record.h"
+
+#define PERSIST_CONVERT_OPTION "--persist-convert-entrails"
 
 #include "safety.h"
 
diff --git a/hostside/record-i.h b/hostside/record-i.h
new file mode 100644 (file)
index 0000000..eddb680
--- /dev/null
@@ -0,0 +1,27 @@
+/*
+ */
+
+#ifndef RECORD_I_H
+#define RECORD_I_H
+
+#include "record.h"
+#include "record-y.h"
+
+void record_train_at(Train *tra, int backw, Segment *seg, int maxi, int unc);
+void record_train_is(Train *tra, int addr, int head, int det, int tail);
+void record_train_step(Train *tra, int step, int speed, int upw, int downw);
+void record_train_step_count(void);
+void record_seg_has(Segment *seg, int backw, Train *tra);
+void record_seg_at(Segment *seg, const char *movposcomb_pname);
+
+Train *record_pname2train(const char *pname);
+Segment *record_pname2seg(const char *pname);
+char *record_tempzone_strdup(const char *s);
+void record_yyerror(const char *m);
+void record_tempzone_clear(void);
+
+int record_yyparse(void);
+int record_yylex(void);
+extern int record_yylineno;
+
+#endif /*RECORD_I_H*/
index 79afd955b0c7cb261602d706f46e83592973a375..68edcb3cb3e15dde504b9869566a6bd52353bd23 100644 (file)
@@ -1,7 +1,7 @@
 /* -*- fundamental -*- */
 
 %{
-#include "record.h"
+#include "record-i.h"
 %}
 
 %option warn
@@ -28,8 +28,8 @@ end           { STR END; }
 
 [A-Za-z][A-Za-z0-9_]+  { STR IDENT; }
 
-[0-9]{0,5}     { record_yylval.num= strtoul(yytext,0,10); return NUM; }
-[0-9]{6}       { record_yyerror("number too long"); }
+[0-9]{0,8}     { record_yylval.num= strtoul(yytext,0,10); return NUM; }
+[0-9]{9}       { record_yyerror("number too long"); }
 
 [-+:=~/]       { record_yylval.name= 0; return yytext[0]; }
 
index ff906fc7010db13d99e8e016bac01da434d95c30..5a97fdaea12c0a9abc60483ba6c4293e4e8b41c1 100644 (file)
@@ -1,7 +1,7 @@
 /* -*- fundamental -*- */
 
 %{
-#include "record.h"
+#include "record-i.h"
 #include "record-l.h"
 %}
 
index 43448ee8c9f9a860d17032415eda5dd5c84a59c0..67d34a13b8dc2769f2b506f758f0aa2651ea435a 100644 (file)
@@ -7,7 +7,7 @@
  *  train <trainpn> at [-]<foredetectpn>:<maxinto>+-<uncertainty>
  *  train <trainpn> is <addr> <head>+<detectable>+<tail>
  *  train <trainpn> step <step>=<speed> <upwait>/<downwait>
- *  seg <segpn> has [-]<ownerpn>|$
+ *  seg <segpn> has [-]<ownerpn>
  *  seg <segpn> at <movposcomb>
  *
  * speed is in um/s, upwait and downwait are in us
@@ -15,7 +15,7 @@
 
 #include <sys/mman.h>
 
-#include "record.h"
+#include "record-i.h"
 #include "record-l.h"
 
 /*---------- input and error handling ----------*/
@@ -220,45 +220,106 @@ static void sort_curves(void) {
   }
 }
 
-/*---------- entrypoint from main, and its subroutines ----------*/
+/*---------- persistent data file layout ----------*/
+
+static void *alloc_some(void *mapbase, int *offset, size_t sz, int count) {
+  void *r;
+  
+  while (*offset % sz) {
+    if (mapbase) ((Byte*)mapbase)[*offset]= 0xaa;
+    (*offset)++;
+  }
+  r= mapbase ? (Byte*)mapbase + *offset : 0;
+  *offset += sz*count;
+  return r;
+}
 
 static void alloc(void) {
   Train *tra; Segment *seg; const SegmentInfo *segi;
+  void *mapbase=0;
   char **trap;
-  int i;
-  
-  segments= mmap(0, sizeof(*segments)*NUM_SEGMENTS + sizeof(*trains)*n_trains,
-                PROT_READ|PROT_WRITE, MAP_SHARED|MAP_ANONYMOUS, -1,0);
-  if (!segments) diee("mmap for shared region");
+  int i, phase, offset, datalen=0;
+
+#define ALLOC(array,count) \
+  ((array)= alloc_some(mapbase,&offset,sizeof(*(array)),(count)))
+
+  for (phase=0; ; phase++) {
+    /* phase 0: count up how much space will be needed
+     * phase 1: fill in most of the details, leaving header blank
+     * phase 2: fill in the header, and then exit before doing rest
+     */
+    offset= 0;
+
+    if (phase==1)
+      mapbase= record_allocate(datalen);
+
+#define PHI_SAVE(x) {                          \
+      typeof(x) *p;                            \
+      ALLOC(p,1);                              \
+      if (phase==2)                            \
+        *p= (x);                               \
+    }
+#define PHI_DUMP(x) {                          \
+      Byte *p;                                 \
+      ALLOC(p,sizeof(x));                      \
+      if (phase==2)                            \
+        memcpy(p, &(x), sizeof(x));            \
+    }
 
-  for (i=0, seg=segments, segi=info_segments;
-       i<NUM_SEGMENTS;
-       i++, seg++, segi++) {
-    seg->movposcomb= -1;
-    seg->i= segi;
-  }
+    DO_PERSIST_HEADER_ITEMS(PHI_DUMP, PHI_SAVE, PHI_SAVE)
+    
+    if (phase==2)
+      break;
+
+    ALLOC(trains, n_trains);
+    for (i=0, tra=trains, trap=train_pnames;
+        i<n_trains;
+        i++, tra++, trap++) {
+      char *pname;
+      ALLOC(pname, strlen(*trap)+1);
+      if (phase) {
+       strcpy(pname, *trap);
+       free(*trap);
+       tra->pname= *trap= pname;
+       tra->addr= -1;
+       tra->head= -1;
+      }        
+    }
 
-  trains= (void*)(segments + NUM_SEGMENTS);
-  for (i=0, tra=trains, trap=train_pnames;
-       i<n_trains;
-       i++, tra++, trap++) {
-    tra->pname= *trap;
-    tra->addr= -1;
-    tra->head= -1;
+    ALLOC(segments, info_nsegments);
+    if (phase)
+      for (i=0, seg=segments, segi=info_segments;
+          i<NUM_SEGMENTS;
+          i++, seg++, segi++) {
+       seg->movposcomb= -1;
+       seg->i= segi;
+      }
+
+    if (phase==0)
+      datalen= offset;
   }
 
   curvebuf= mmalloc(sizeof(*curvebuf) * curvebufsz);
 }
+  
+/*---------- entrypoint from main, and its subroutines ----------*/
 
-static void parse_pass(const char **argv) {
+static void parse_file(const char *why) {
   FILE *f;
-  
-  while ((filename= *argv++)) {
-    f= fopen(filename,"r");
-    if (!f) diee("config: cannot open input file: %s", filename);
-    record_yyrestart(f);
-    record_yyparse();
-  }
+
+  f= fopen(filename,"r");
+  if (!f) diee("config: cannot open %s: %s", why, filename);
+  record_yyrestart(f);
+  record_yyparse();
+}
+
+static void parse_pass(const char **argv) {
+  while ((filename= *argv++))
+    parse_file("commandline-specified record file");
+
+  filename= persist_record_converted;
+  if (filename)
+    parse_file("converted persistent data file");
 }
 
 void records_parse(const char **argv) {
index 366bd29f47e6e29c6241b1077ae2657fd8f469d6..cab5b1537c7e41299bdfdf23543a3517a31b69c4 100644 (file)
@@ -5,23 +5,19 @@
 #define RECORD_H
 
 #include "realtime.h"
-#include "record-y.h"
 
-void record_train_at(Train *tra, int backw, Segment *seg, int maxi, int unc);
-void record_train_is(Train *tra, int addr, int head, int det, int tail);
-void record_train_step(Train *tra, int step, int speed, int upw, int downw);
-void record_train_step_count(void);
-void record_seg_has(Segment *seg, int backw, Train *tra);
-void record_seg_at(Segment *seg, const char *movposcomb_pname);
-
-Train *record_pname2train(const char *pname);
-Segment *record_pname2seg(const char *pname);
-char *record_tempzone_strdup(const char *s);
-void record_yyerror(const char *m);
-void record_tempzone_clear(void);
+/* record.[ch] can be used in programs using persist.c, for the full
+ * works.  Alternatively for use in some other program, just provide
+ * a persist_allocate which calls malloc:
+ */
+void *record_allocate(int length);
 
-int record_yyparse(void);
-int record_yylex(void);
-extern int record_yylineno;
+#define DO_PERSIST_HEADER_ITEMS(cnst,num,ptr)  \
+  cnst("#! /dev/enoent/trains-image\n")                \
+  ptr(mapbase)                                 \
+  num(datalen)                                 \
+  ptr(trains)                                  \
+  num(n_trains)                                        \
+  ptr(segments)
 
 #endif /*RECORD_H*/
index 61ca48e9e52df62fbe77c41c28da78ff4d52052c..a4806f92cce55f37d49754558c740d58aaf4e9af 100644 (file)
@@ -43,7 +43,7 @@ struct Train {
   Distance head, detectable, tail;
 
   /* Location: */
-  Segment *foredetect;     /* train's detectable part is at most maxinto   */
+  struct Segment *foredetect; /* train's detectable part is at most maxinto */
   Distance maxinto, uncertainty;   /*   into foredetect but train may be   */
   unsigned                         /*   uncertainty less far advanced      */
     backwards:1, /* train is moving backwards wrt its own front and back */
index 955a443c68aeaf4d19f48d80664b4722fa47ec7b..25b67505db8d707952b7f26c5263a66b0d844731 100644 (file)
@@ -79,7 +79,7 @@ static void sta_goto(StartupState new_state) {
   case Sta_Fault:                                                  break;
   case Sta_Settling:                        enco_pic_off(&piob);   break;
   case Sta_Resolving:                       enco_pic_on(&piob);    break;
-  case Sta_Run:         retransmit_start();                        break;
+  case Sta_Run:   persist_install(); retransmit_start();           break;
   }
   if (piob.l) serial_transmit(&piob);
 
index 5e0824432d014d31ed90ab4403cc5f3678d752e3..f9671a118a8d90af09938ecb0528a43ebf4d4f4a 100644 (file)
@@ -76,3 +76,8 @@ void mgettimeofday(struct timeval *tv) {
   r= gettimeofday(tv,0);
   if (r) diee("gettimeofday failed");
 }
+
+void mrename(const char *old, const char *new) {
+  if (rename(old,new))
+    diee("failed to rename `%s' to `%s'", old,new);
+}