chiark / gitweb /
initial wip - before remove compression
authorianmdlvl <ianmdlvl>
Fri, 3 Oct 2008 20:17:40 +0000 (20:17 +0000)
committerianmdlvl <ianmdlvl>
Fri, 3 Oct 2008 20:17:40 +0000 (20:17 +0000)
cprogs/rcopy-repeatedly.c [new file with mode: 0644]

diff --git a/cprogs/rcopy-repeatedly.c b/cprogs/rcopy-repeatedly.c
new file mode 100644 (file)
index 0000000..489ab2d
--- /dev/null
@@ -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 (