4 * - "chiark-realtime-replicator\n" [27 bytes]
5 * - length of declaration, as 4 hex digits, zero prefixed,
6 * and a space [5 bytes]. In this protocol version this
7 * will be "0002" but client _must_ parse it.
8 * server sends declaration
9 * - one of "u " or "d" [1 byte]
10 * - optionally, some more ascii text, reserved for future use
11 * must be ignored by client (but not sent by server)
12 * - a newline [1 byte]
15 * then for each update
17 * - 0x02 update using rle and 8-bit counts
18 * - zero or more repetitions of
19 * n single byte giving length of data same as last time
20 * d single byte giving length of data changed
22 * where n!=0 or d!=0 (and for first update, n==0)
24 * indicates file is complete and should be installed
26 * - 0x03 destination file should be deleted
27 * but note that contents must be retained by receiver
28 * as it may be used for rle updates
31 #define REPLMSG_GO 0x01
32 #define REPLMSG_RLE8 0x02
33 #define REPLMSG_RM 0x03
35 static void vdie(int ec, const char *fmt, const char *emstr, va_list al) {
36 fputs("realtime-replicator: ",stderr);
37 vfprintf(stderr,fmt,al);
38 if (emstr) fprintf(stderr,": %s",emstr);
42 static void die(int ec, const char *fmt, const char *emstr, ...) {
45 vdiee(ec,fmt,emstr,al);
48 static void diem(void) { die(16,"malloc failed",strerror(errno)); }
50 static void diee(const char *fmt, ...) {
51 const char *em= strerror(errno);
56 static void die_protocol(const char *fmt, ...) {
61 static void die_badrecv(FILE *f, const char *what) {
62 if (ferror(f)) diee("transmission failed while receiving %s", what);
63 if (feof(f)) die_protocol("receiver got unexpected EOF in %s", what);
67 static void receiver_write(const unsigned char *buf, int n,
68 FILE *newfile, const char *tmpfilename) {
71 r= fwrite(dbuf,1,n,newfile);
72 if (r != n) diee("failed to write temporary receiving file `%s'",
76 static void receiver(const char *filename, FILE *comms) {
77 FILE *lastfile=0, *newfile;
80 if (asprintf(&tmpfilename, ".realtime-replicator.#%s#")==-1) diem();
82 r= unlink(tmpfilename);
83 if (r && errno!=ENOENT)
84 diee("could not remove temporary receiving file `%s'", tmpfilename);
87 c= fgetc(comms); if (c==EOF) break;
91 newfile= fopen(tmpfilename, "w");
92 if (!newfile) diee("could not create temporary receiving file `%s'",
95 unsigned char lu[2], dbuf[255];
96 r= fread(lu,1,2,comms); if (r!=2) die_badrecv(comms,"RLE8 elem hdr");
97 if (!lu[0] && !lu[1]) break;
99 if (!lastfile) die_protocol("first RLE8 requests some previous");
100 r= fread(dbuf,1,lu[0],lastfile);
102 if (ferror(lastfile)) diee("could not read current (old) file"
104 assert(feof(lastfile));
105 die_protocol("RLE8 requests more previous than is available");
107 receiver_write(dbuf,lu[0],newfile,tmpfilename);
110 r= fread(dbuf,1,lu[1],comms);
111 if (r!=lu[1]) die_badrecv(comms,"RLE8 literal data");
112 receiver_write(dbuf,lu[1],newfile,tmpfilename);
115 if (fflush(newfile)) diee("could not flush temporary receiving file"
116 " `%s'", tmpfilename);
117 if (rename(tmpfilename, filename))
118 diee("could not install new version of destination file `%s'",
120 if (fclose(lastfile)) diee("failed to close old (now unlinked) file");
122 r= fseek(lastfile,0,SEEK_SET);
123 if (r) diee("failed to seek installed destination file `%s'", filename);
128 if (r && errno!=ENOENT)
129 diee("could not remove destination file `%s' (as instructed"
130 " by sender)", filename);
134 die_protocol("unknown transfer message code 0x%02x",c);
137 if (feof(comms)) return 0;
138 die_badrecv(comms,"transfer message code");
141 static void sender(const char *filename, FILE *comms,
142 unsigned long interval_usec) {
143 struct stat stab, laststab;
144 memset(&stab,0,sizeof(stab));
145 FILE *lastfile, *newfile;
149 r= stat(filename, &stab);
153 if (stab.st_dev == laststab.st_dev &&
154 stab.st_ino == laststab.st_ino) {
155 usleep(interval_usec);
158 newfile= fopen(filename, "r");
161 if (errno!=ENOENT) diee("could not access source file `%s'",filename);
163 usleep(interval_usec);
166 sender_sendbyte(REPLMSG_RM);
173 cl= nextchar(lastfile);
174 cn= nextchar(newfile);
177 if (!feof(lastfile)) {
179 if (ferror(lastfile))
180 diee("could not read old source file `%s'",filename);
181 assert(cl != EOF || feof(lastfile));
184 if (errno!=ENOENT) diee("could not open source file `%s'",filename);
192 assert(ferror(newfile));
195 r= fread(dbuf,1,lu[0],comms);
196 if (r!=lu[0]) die_badrecv(comms,
202 int main(int argc, const char **argv) {