+ if (fold) fclose(fold);
+ fold= 0;
+
+ r= fstat(fileno(f),&stab);
+ if (r) diee("could not fstat source file `%s'",filename);
+
+ if (!S_ISREG(stab.st_mode))
+ die(8,0,-1,"source file `%s' is not a plain file",filename);
+
+ uint8_t hbuf[9]= {
+ REPLMSG_FILE64,
+ stab.st_size >> 28 >> 28,
+ stab.st_size >> 24 >> 24,
+ stab.st_size >> 16 >> 24,
+ stab.st_size >> 8 >> 24,
+ stab.st_size >> 24,
+ stab.st_size >> 16,
+ stab.st_size >> 8,
+ stab.st_size
+ };
+
+ bandlimit_sendstart();
+
+ mfwritecommso(hbuf,9);
+
+ copyfile(f, copydie_inputfile,filename,
+ commso, copydie_commso,0,
+ stab.st_size, 1);
+
+ send_flush();
+
+ c= fgetc(commsi); if (c==EOF) die_badrecv("ack");
+ if (c!=REPLMSG_ACK) die_protocol("got %#02x instead of ACK",c);
+
+ bandlimit_sendend(stab.st_size, &interval_usec);
+
+ fold= f;
+ told= told_file;
+ }
+}
+
+typedef struct {
+ const char *userhost, *path;
+} FileSpecification;
+
+static FileSpecification srcspec, dstspec;
+
+static void of__server(const struct cmdinfo *ci, const char *val) {
+ int ncount= nsargs + 1 + !!val;
+ sargs= xrealloc(sargs, sizeof(*sargs) * ncount);
+ sargs[nsargs++]= ci->olong;
+ if (val)
+ sargs[nsargs++]= val;
+}
+
+static int of__server_int(const struct cmdinfo *ci, const char *val) {
+ of__server(ci,val);
+ long v;
+ char *ep;
+ errno= 0; v= strtol(val,&ep,10);
+ if (!*val || *ep || errno || v<INT_MIN || v>INT_MAX)
+ badusage("bad integer argument `%s' for --%s",val,ci->olong);
+ return v;
+}
+
+static void of_help(const struct cmdinfo *ci, const char *val) {
+ usagemessage();
+ if (ferror(stdout)) diee("could not write usage message to stdout");
+ exit(0);
+}
+
+static void of_bw(const struct cmdinfo *ci, const char *val) {
+ int pct= of__server_int(ci,val);
+ if (pct<1 || pct>100)
+ badusage("bandwidth percentage must be between 1 and 100 inclusive");
+ *(double*)ci->parg= pct * 0.01;
+}
+
+static void of_server_int(const struct cmdinfo *ci, const char *val) {
+ *(int*)ci->parg= of__server_int(ci,val);
+}
+
+void usagemessage(void) {
+ printf(
+ "usage: rcopy-repeatedly [<options>] <file> <file>\n"
+ " <file> may be <local-file> or [<user>@]<host>:<file>\n"
+ " exactly one of each of the two forms must be provided\n"
+ " a file is taken as remote if it has a : before the first /\n"
+ "general options:\n"
+ " --help\n"
+ " --quiet | -q\n"
+ "options for bandwidth (and cpu time) control:\n"
+ " --max-bandwidth-percent (default %d)\n"
+ " --tx-block-size (default/max %d)\n"
+ " --min-interval-usec (default %d)\n"
+ "options for finding programs:\n"
+ " --rcopy-repeatedly (default: rcopy-repeatedly)\n"
+ " --rsh-program (default: $RCOPY_REPEATEDLY_RSH or $RSYNC_RSH or ssh)\n"
+ "options passed to server side via ssh:\n"
+ " --receiver --sender, bandwidth control options\n",
+ (int)(max_bw_prop*100), (int)sizeof(mainbuf), min_interval_usec);
+}
+
+static const struct cmdinfo cmdinfos[]= {
+ { "help", .call= of_help },
+ { "max-bandwidth-percent", 0,1,.call=of_bw,.parg=&max_bw_prop },
+ { "tx-block-size",0, 1,.call=of_server_int, .parg=&txblocksz },
+ { "min-interval-usec",0, 1,.call=of_server_int, .parg=&min_interval_usec },
+ { "rcopy-repeatedly",0, 1, .sassignto=&rcopy_repeatedly_program },
+ { "rsh-program",0, 1, .sassignto=&rsh_program },
+ { "quiet",'q', .iassignto= &verbose, .arg=0 },
+ { "receiver", .iassignto= &server_upcopy, .arg=0 },
+ { "sender", .iassignto= &server_upcopy, .arg=1 },
+ { 0 }
+};
+
+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);