From: ian Date: Mon, 11 Feb 2008 03:24:41 +0000 (+0000) Subject: new persistent arrangements compile but do not work X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~ijackson/git?a=commitdiff_plain;h=564b7731ef9357c4c89b8e203f44e90b61f41e6f;p=trains.git new persistent arrangements compile but do not work --- diff --git a/hostside/.cvsignore b/hostside/.cvsignore index db748ba..024a957 100644 --- a/hostside/.cvsignore +++ b/hostside/.cvsignore @@ -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 diff --git a/hostside/Makefile b/hostside/Makefile index 7e3b8ae..9c75a78 100644 --- a/hostside/Makefile +++ b/hostside/Makefile @@ -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 \ diff --git a/hostside/TODO b/hostside/TODO index 1c8443e..7438dfd 100644 --- a/hostside/TODO +++ b/hostside/TODO @@ -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 diff --git a/hostside/analyse-speeds b/hostside/analyse-speeds index 22694db..e4dc53c 100755 --- a/hostside/analyse-speeds +++ b/hostside/analyse-speeds @@ -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 $!; } ' diff --git a/hostside/common.h b/hostside/common.h index b44acbd..c27ba14 100644 --- a/hostside/common.h +++ b/hostside/common.h @@ -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 index 0000000..1f5f07a --- /dev/null +++ b/hostside/persist.c @@ -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; ipname); + 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; segnowner); + + 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); +} diff --git a/hostside/realtime.c b/hostside/realtime.c index 4aad568..6cd3a2e 100644 --- a/hostside/realtime.c +++ b/hostside/realtime.c @@ -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); diff --git a/hostside/realtime.h b/hostside/realtime.h index 94413e8..786e30b 100644 --- a/hostside/realtime.h +++ b/hostside/realtime.h @@ -19,6 +19,13 @@ #include #include +#include +#include +#include + +#include +#include +#include #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 index 0000000..eddb680 --- /dev/null +++ b/hostside/record-i.h @@ -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*/ diff --git a/hostside/record-l.l b/hostside/record-l.l index 79afd95..68edcb3 100644 --- a/hostside/record-l.l +++ b/hostside/record-l.l @@ -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]; } diff --git a/hostside/record-y.y b/hostside/record-y.y index ff906fc..5a97fda 100644 --- a/hostside/record-y.y +++ b/hostside/record-y.y @@ -1,7 +1,7 @@ /* -*- fundamental -*- */ %{ -#include "record.h" +#include "record-i.h" #include "record-l.h" %} diff --git a/hostside/record.c b/hostside/record.c index 43448ee..67d34a1 100644 --- a/hostside/record.c +++ b/hostside/record.c @@ -7,7 +7,7 @@ * train at [-]:+- * train is ++ * train step = / - * seg has [-]|$ + * seg has [-] * seg at * * speed is in um/s, upwait and downwait are in us @@ -15,7 +15,7 @@ #include -#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; - imovposcomb= -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; + ipname= *trap= pname; + tra->addr= -1; + tra->head= -1; + } + } - trains= (void*)(segments + NUM_SEGMENTS); - for (i=0, tra=trains, trap=train_pnames; - ipname= *trap; - tra->addr= -1; - tra->head= -1; + ALLOC(segments, info_nsegments); + if (phase) + for (i=0, seg=segments, segi=info_segments; + imovposcomb= -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) { diff --git a/hostside/record.h b/hostside/record.h index 366bd29..cab5b15 100644 --- a/hostside/record.h +++ b/hostside/record.h @@ -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*/ diff --git a/hostside/safety.h b/hostside/safety.h index 61ca48e..a4806f9 100644 --- a/hostside/safety.h +++ b/hostside/safety.h @@ -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 */ diff --git a/hostside/startup.c b/hostside/startup.c index 955a443..25b6750 100644 --- a/hostside/startup.c +++ b/hostside/startup.c @@ -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); diff --git a/hostside/utils.c b/hostside/utils.c index 5e08244..f9671a1 100644 --- a/hostside/utils.c +++ b/hostside/utils.c @@ -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); +}