+static void server(const char *filename) {
+ int c, l;
+ char buf[2];
+
+ udchar= server_upcopy?'u':'d';
+
+ commsi= stdin;
+ commso= stdout;
+ l= generate_declaration();
+ fprintf(commso, "%s%04x\n", banner, l);
+ mfwritecommso(mainbuf, l);
+ send_flush();
+
+ c= fgetc(commsi);
+ if (c==EOF) {
+ if (feof(commsi)) exit(14);
+ assert(ferror(commsi)); die_badrecv("initial START message");
+ }
+ if (c!=REPLMSG_START) die_protocol("initial START was %#02x instead",c);
+
+ mfreadcommsi(buf,2,"START l");
+ l= (buf[0] << 8) | buf[1];
+
+ read_declaration(l);
+
+ if (server_upcopy)
+ sender(filename);
+ else
+ receiver(filename);
+}
+
+static void client(void) {
+ int uppipe[2], downpipe[2], r;
+ pid_t child;
+ FileSpecification *remotespec;
+ const char *remotemode;
+
+ mpipe(uppipe);
+ mpipe(downpipe);
+
+ if (srcspec.userhost) {
+ udchar= 'u';
+ remotespec= &srcspec;
+ remotemode= "--sender";
+ } else {
+ udchar= 'd';
+ remotespec= &dstspec;
+ remotemode= "--receiver";
+ }
+
+ sargs= xrealloc(sargs, sizeof(*sargs) * (7 + nsargs));
+ memmove(sargs+5, sargs, sizeof(*sargs) * nsargs);
+ sargs[0]= rsh_program;
+ sargs[1]= remotespec->userhost;
+ sargs[2]= rcopy_repeatedly_program;
+ sargs[3]= remotemode;
+ sargs[4]= "--";
+ sargs[5+nsargs]= remotespec->path;
+ sargs[6+nsargs]= 0;
+
+ child= fork();
+ if (child==-1) diee("fork failed");
+ if (!child) {
+ mdup2(downpipe[0],0);
+ mdup2(uppipe[1],1);
+ close(uppipe[0]); close(downpipe[0]);
+ close(uppipe[1]); close(downpipe[1]);
+
+ execvp(rsh_program, (char**)sargs);
+ diee("failed to execute rsh program `%s'",rsh_program);
+ }
+
+ commso= fdopen(downpipe[1],"wb");
+ commsi= fdopen(uppipe[0],"rb");
+ if (!commso || !commsi) diee("fdopen failed");
+ close(downpipe[0]);
+ close(uppipe[1]);
+
+ char banbuf[sizeof(banner)-1 + 5 + 1];
+ r= fread(banbuf,1,sizeof(banbuf)-1,commsi);
+ if (ferror(commsi)) die_badrecv("read banner");
+
+ if (r!=sizeof(banbuf)-1 ||
+ memcmp(banbuf,banner,sizeof(banner)-1) ||
+ banbuf[sizeof(banner)-1 + 4] != '\n') {
+ const char **sap;
+ int count=0;
+ for (count=0, sap=sargs; *sap; sap++) count+= strlen(*sap)+1;
+ char *cmdline= xmalloc(count+1);
+ cmdline[0]=' ';
+ for (sap=sargs; *sap; sap++) {
+ strcat(cmdline," ");
+ strcat(cmdline,*sap);
+ }
+
+ die(8,0,-1,"did not receive banner as expected -"
+ " shell dirty? ssh broken?\n"
+ " try running\n"
+ " %s\n"
+ " and expect the first line to be\n"
+ " %s",
+ cmdline, banner);
+ }
+
+ banbuf[sizeof(banbuf)-1]= 0;
+ char *ep;
+ long decllen= strtoul(banbuf + sizeof(banner)-1, &ep, 16);
+ if (ep != banbuf + sizeof(banner)-1 + 4)
+ die_protocol("declaration length syntax error");
+
+ read_declaration(decllen);
+
+ int l= generate_declaration();
+ sendbyte(REPLMSG_START);
+ sendbyte((l >> 8) & 0x0ff);
+ sendbyte( l & 0x0ff);
+ mfwritecommso(mainbuf,l);
+
+ if (remotespec==&srcspec)
+ receiver(dstspec.path);
+ else
+ sender(srcspec.path);
+}
+
+static void parse_file_specification(FileSpecification *fs, const char *arg,
+ const char *what) {
+ const char *colon;
+
+ if (!arg) badusage("too few arguments - missing %s\n",what);
+
+ for (colon=arg; ; colon++) {
+ if (!*colon || *colon=='/') {
+ fs->userhost=0;
+ fs->path= arg;
+ return;
+ }
+ if (*colon==':') {
+ char *uh= xmalloc(colon-arg + 1);
+ memcpy(uh,arg, colon-arg); uh[colon-arg]= 0;
+ fs->userhost= uh;
+ fs->path= colon+1;
+ return;
+ }
+ }
+}
+
+int main(int argc, const char *const *argv) {
+ setvbuf(stderr,0,_IOLBF,BUFSIZ);
+
+ myopt(&argv, cmdinfos);
+
+ if (!rsh_program) rsh_program= getenv("RCOPY_REPEATEDLY_RSH");
+ if (!rsh_program) rsh_program= getenv("RSYNC_RSH");
+ if (!rsh_program) rsh_program= "ssh";
+
+ if (txblocksz<1) badusage("transmit block size must be at least 1");
+ if (min_interval_usec<0) badusage("minimum update interval may not be -ve");
+
+ if (server_upcopy>=0) {
+ if (!argv[0] || argv[1])
+ badusage("server mode must have just the filename as non-option arg");
+ server(argv[0]);
+ } else {
+ parse_file_specification(&srcspec, argv[0], "source");
+ parse_file_specification(&dstspec, argv[1], "destination");
+ if (argv[2]) badusage("too many non-option arguments");
+ if (!!srcspec.userhost == !!dstspec.userhost)
+ badusage("need exactly one remote file argument");
+ client();
+ }
+ return 0;
+}