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 * - 0x03 destination file should be deleted
18 * but note that contents must be retained by receiver
19 * as it may be used for rle updates
20 * - 0x04 complete new destination file follows, 64-bit length
21 * l 8 bytes big endian length
23 * receiver must then reply with 0x01 GO
26 #define REPLMSG_GO 0x01
27 #define REPLMSG_RLE8 0x02
28 #define REPLMSG_RM 0x03
29 #define REPLMSG_FILE64 0x04
31 static FILE *commsi, *commso;
32 static long interval_usec;
33 static int bytes_per_sec_log2;
35 static void vdie(int ec, int eno, const char *fmt, va_list al) {
36 fputs("realtime-replicator: ",stderr);
37 vfprintf(stderr,fmt,al);
38 if (eno!=-1) fprintf(stderr,": %s",strerror(eno));
42 static void die(int ec, int eno, const char *fmt, ...) {
48 static void diem(void) { die(16,errno,"malloc failed"); }
50 static void diee(const char *fmt, ...) {
55 static void die_protocol(const char *fmt, ...) {
60 static void die_badrecv(const char *what) {
61 if (ferror(commsi)) diee("communication failed while receiving %s", what);
62 if (feof(commsi)) die_protocol("receiver got unexpected EOF in %s", what);
65 static void die_badsend(void) {
66 diee("transmission failed");
69 static void receiver_write(const unsigned char *buf, int n,
70 FILE *newfile, const char *tmpfilename) {
73 r= fwrite(dbuf,1,n,newfile);
74 if (r != n) diee("failed to write temporary receiving file `%s'",
78 typedef static void copyfile_die_fn(FILE *f, const char *xi);
80 static void copyfile(FILE *sf, copyfile_die_fn *sdie, const char *sxi
81 FILE *df, copyfile_die_fn *ddie, const char *dxi,
86 fixme adjustable chunk size
89 now= l < sizeof(buf) ? l : sizeof(buf);
93 r= fread(buf,1,now,sf); if (r!=now) sdie(sf,sxi);
94 r= fwrite(buf,1,now,df); if (r!=now) ddie(df,dxi);
99 static void copydie_tmpwrite(FILE *f, const char *tmpfilename) {
100 diee("write failed to temporary receiving file `%s'", tmpfilename);
102 static void copydie_commsi(FILE *f, const char *what) {
105 static void copydie_commso(FILE *f, const char *what) {
109 static void receiver(const char *filename) {
113 if (asprintf(&tmpfilename, ".realtime-replicator.#%s#")==-1) diem();
115 r= unlink(tmpfilename);
116 if (r && errno!=ENOENT)
117 diee("could not remove temporary receiving file `%s'", tmpfilename);
126 if (ferror(commsi)) die_badrecv("transfer message code");
127 assert(feof(commsi));
131 newfile= fopen(tmpfilename, "w");
132 if (!newfile) diee("could not create temporary receiving file `%s'",
135 r= fread(lbuf,1,8,commsi); if (r!=8) die_badrecv("FILE64 l");
138 (lbuf[0] << 28 << 28) |
139 (lbuf[1] << 24 << 24) |
140 (lbuf[2] << 16 << 24) |
141 (lbuf[3] << 8 << 24) |
147 copyfile(commsi, copydie_commsi,"FILE64 file data",
148 newfile, copydie_tmpwrite,tmpfilename,
151 if (fclose(newfile)) diee("could not flush and close temporary"
152 " receiving file `%s'", tmpfilename);
153 if (rename(tmpfilename, filename))
154 diee("could not install new version of destination file `%s'",
157 sendbyte(REPLMSG_GO);
160 die_protocol("unknown transfer message code 0x%02x",c);
166 static void sender(const char *filename) {
172 f= fopen(filename, "r");
174 if (errno!=ENOENT) diee("could not access source file `%s'",filename);
176 usleep(interval_usec);
180 sendbyte(REPLMSG_RM);
184 r= fstat(fileno(f),&stab);
185 if (r) diee("could not fstat source file `%s'",filename);
187 if (!S_ISREG(stab.st_mode))
188 die(12,-1,"source file `%s' is not a plain file",filename);
192 stab.st_size >> 28 >> 28,
193 stab.st_size >> 24 >> 24,
194 stab.st_size >> 16 >> 24,
195 stab.st_size >> 8 >> 24,
202 r= fwrite(hbuf,1,9,commso); if (r!=9) die_badsend();
205 copyfile(f, copydie_inputfile,filename,
206 commso, copydie_commso,0);
210 if (fclose(f)) diee("couldn't close source file `%s'",filename);
212 c= fgetc(commsi); if (c==EOF) die_badrecv("ack");
213 if (c!=REPLMSG_GO) die_protocol("got %#02x instead of GO",c);
215 usleep(interval_usec);
219 int main(int argc, const char **argv) {