X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~ian/git?a=blobdiff_plain;f=cprogs%2Frcopy-repeatedly.c;fp=cprogs%2Frcopy-repeatedly.c;h=489ab2daf5ec09e15cc2ae0e8d567fb09de6d36c;hb=b04c264af5257661e52015a8895f81046a39e8d8;hp=0000000000000000000000000000000000000000;hpb=9532ae8327cb78467546612f3008e467d092d987;p=chiark-utils.git diff --git a/cprogs/rcopy-repeatedly.c b/cprogs/rcopy-repeatedly.c new file mode 100644 index 0000000..489ab2d --- /dev/null +++ b/cprogs/rcopy-repeatedly.c @@ -0,0 +1,203 @@ +/* + * protocol is: + * server sends banner + * - "chiark-realtime-replicator\n" [27 bytes] + * - length of declaration, as 4 hex digits, zero prefixed, + * and a space [5 bytes]. In this protocol version this + * will be "0002" but client _must_ parse it. + * server sends declaration + * - one of "u " or "d" [1 byte] + * - optionally, some more ascii text, reserved for future use + * must be ignored by client (but not sent by server) + * - a newline [1 byte] + * client sends + * - 0x01 go + * then for each update + * sender sends + * - 0x02 update using rle and 8-bit counts + * - zero or more repetitions of + * n single byte giving length of data same as last time + * d single byte giving length of data changed + * ... d bytes of data + * where n!=0 or d!=0 (and for first update, n==0) + * - 0x00 0x00 + * indicates file is complete and should be installed + * or server may send + * - 0x03 destination file should be deleted + * but note that contents must be retained by receiver + * as it may be used for rle updates + */ + +#define REPLMSG_GO 0x01 +#define REPLMSG_RLE8 0x02 +#define REPLMSG_RM 0x03 + +static void vdie(int ec, const char *fmt, const char *emstr, va_list al) { + fputs("realtime-replicator: ",stderr); + vfprintf(stderr,fmt,al); + if (emstr) fprintf(stderr,": %s",emstr); + fputc('\n',stderr); + exit(ec); +} +static void die(int ec, const char *fmt, const char *emstr, ...) { + va_arg al; + va_start(al,fmt); + vdiee(ec,fmt,emstr,al); +} + +static void diem(void) { die(16,"malloc failed",strerror(errno)); } + +static void diee(const char *fmt, ...) { + const char *em= strerror(errno); + va_arg al; + va_start(al,fmt); + diee(8,fmt,em,al); +} +static void die_protocol(const char *fmt, ...) { + va_arg al; + va_start(al,fmt); + diee(12,fmt,0,al); + +static void die_badrecv(FILE *f, const char *what) { + if (ferror(f)) diee("transmission failed while receiving %s", what); + if (feof(f)) die_protocol("receiver got unexpected EOF in %s", what); + abort(); +} + +static void receiver_write(const unsigned char *buf, int n, + FILE *newfile, const char *tmpfilename) { + int r; + + r= fwrite(dbuf,1,n,newfile); + if (r != n) diee("failed to write temporary receiving file `%s'", + tmpfilename); +} + +static void receiver(const char *filename, FILE *comms) { + FILE *lastfile=0, *newfile; + char *tmpfilename; + + if (asprintf(&tmpfilename, ".realtime-replicator.#%s#")==-1) diem(); + + r= unlink(tmpfilename); + if (r && errno!=ENOENT) + diee("could not remove temporary receiving file `%s'", tmpfilename); + + for (;;) { + c= fgetc(comms); if (c==EOF) break; + switch (c) { + + case REPLMSG_RLE8: + newfile= fopen(tmpfilename, "w"); + if (!newfile) diee("could not create temporary receiving file `%s'", + tmpfilename); + for (;;) { + unsigned char lu[2], dbuf[255]; + r= fread(lu,1,2,comms); if (r!=2) die_badrecv(comms,"RLE8 elem hdr"); + if (!lu[0] && !lu[1]) break; + if (lu[0]) { + if (!lastfile) die_protocol("first RLE8 requests some previous"); + r= fread(dbuf,1,lu[0],lastfile); + if (r!=lu[0]) { + if (ferror(lastfile)) diee("could not read current (old) file" + " `%s'", filename); + assert(feof(lastfile)); + die_protocol("RLE8 requests more previous than is available"); + } + receiver_write(dbuf,lu[0],newfile,tmpfilename); + } + if (lu[1]) { + r= fread(dbuf,1,lu[1],comms); + if (r!=lu[1]) die_badrecv(comms,"RLE8 literal data"); + receiver_write(dbuf,lu[1],newfile,tmpfilename); + } + } + if (fflush(newfile)) diee("could not flush temporary receiving file" + " `%s'", tmpfilename); + if (rename(tmpfilename, filename)) + diee("could not install new version of destination file `%s'", + filename); + if (fclose(lastfile)) diee("failed to close old (now unlinked) file"); + lastfile= newfile; + r= fseek(lastfile,0,SEEK_SET); + if (r) diee("failed to seek installed destination file `%s'", filename); + continue; + + case REPLMSG_RM: + r= unlink(filename); + if (r && errno!=ENOENT) + diee("could not remove destination file `%s' (as instructed" + " by sender)", filename); + continue; + + default: + die_protocol("unknown transfer message code 0x%02x",c); + } + } + if (feof(comms)) return 0; + die_badrecv(comms,"transfer message code"); +} + + static void sender(const char *filename, FILE *comms, + unsigned long interval_usec) { + struct stat stab, laststab; + memset(&stab,0,sizeof(stab)); + FILE *lastfile, *newfile; + int told_removed= 0; + + for (;;) { + r= stat(filename, &stab); + if (r) { + newfile= 0; + } else { + if (stab.st_dev == laststab.st_dev && + stab.st_ino == laststab.st_ino) { + usleep(interval_usec); + continue; + } + newfile= fopen(filename, "r"); + } + if (!newfile) { + if (errno!=ENOENT) diee("could not access source file `%s'",filename); + if (told_removed) { + usleep(interval_usec); + continue; + } + sender_sendbyte(REPLMSG_RM); + continue; + } + + for (;;) { + n= 0; + while (n<255) { + cl= nextchar(lastfile); + cn= nextchar(newfile); + if (cl!=cn) { + + if (!feof(lastfile)) { + cl= fgetc(lastfile); + if (ferror(lastfile)) + diee("could not read old source file `%s'",filename); + assert(cl != EOF || feof(lastfile)); + } + + if (errno!=ENOENT) diee("could not open source file `%s'",filename); + sender_send + + if (r) +} + +static void sender( + + assert(ferror(newfile)); + + + r= fread(dbuf,1,lu[0],comms); + if (r!=lu[0]) die_badrecv(comms, + + + c= fgetc(comms); + + +int main(int argc, const char **argv) { + for (