Changed --stats implementation to work without -v in only these two
[rsync/rsync.git] / main.c
CommitLineData
c627d613
AT
1/*
2 Copyright (C) Andrew Tridgell 1996
3 Copyright (C) Paul Mackerras 1996
4
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18*/
19
20#include "rsync.h"
21
f0fca04e 22time_t starttime = 0;
a800434a
AT
23
24struct stats stats;
5d6bcd44 25
34ccb63e 26extern int csum_length;
c627d613 27
7a6421fa 28extern int verbose;
c627d613
AT
29
30static void report(int f)
31{
7a6421fa
AT
32 time_t t = time(NULL);
33 extern int am_server;
34 extern int am_sender;
248fbb8c 35 extern int am_daemon;
a800434a 36 extern int do_stats;
17d31b38
DD
37 extern int remote_version;
38 int send_stats;
7a6421fa 39
248fbb8c 40 if (am_daemon) {
a9766ef1 41 log_exit(0, __FILE__, __LINE__);
7b372642 42 if (f == -1 || !am_sender) return;
248fbb8c
AT
43 }
44
17d31b38
DD
45 send_stats = verbose ||
46 ((remote_version >= 20) && (PROTOCOL_VERSION >= 20));
e19452a9 47 if (am_server) {
17d31b38 48 if (am_sender && send_stats) {
23c5aef1
DD
49 int64 w;
50 /* store total_written in a temporary
51 because write_longint changes it */
52 w = stats.total_written;
e19452a9 53 write_longint(f,stats.total_read);
23c5aef1 54 write_longint(f,w);
e19452a9
DD
55 write_longint(f,stats.total_size);
56 }
7a6421fa
AT
57 return;
58 }
e19452a9
DD
59
60 /* this is the client */
61
17d31b38 62 if (!am_sender && send_stats) {
23c5aef1 63 int64 r;
a800434a 64 stats.total_written = read_longint(f);
23c5aef1
DD
65 /* store total_read in a temporary, read_longint changes it */
66 r = read_longint(f);
a800434a 67 stats.total_size = read_longint(f);
23c5aef1 68 stats.total_read = r;
a800434a
AT
69 }
70
71 if (do_stats) {
17d31b38
DD
72 if (!am_sender && !send_stats) {
73 /* missing the bytes written by the generator */
74 rprintf(FINFO, "\nCannot show stats as receiver because protocol version is less than 20\n");
75 rprintf(FINFO, "Use --stats -v to show stats\n");
76 return;
77 }
1f658d42
AT
78 rprintf(FINFO,"\nNumber of files: %d\n", stats.num_files);
79 rprintf(FINFO,"Number of files transferred: %d\n",
a800434a 80 stats.num_transferred_files);
1f658d42 81 rprintf(FINFO,"Total file size: %.0f bytes\n",
a800434a 82 (double)stats.total_size);
1f658d42 83 rprintf(FINFO,"Total transferred file size: %.0f bytes\n",
a800434a 84 (double)stats.total_transferred_size);
1f658d42 85 rprintf(FINFO,"Literal data: %.0f bytes\n",
a800434a 86 (double)stats.literal_data);
1f658d42 87 rprintf(FINFO,"Matched data: %.0f bytes\n",
a800434a 88 (double)stats.matched_data);
1f658d42
AT
89 rprintf(FINFO,"File list size: %d\n", stats.flist_size);
90 rprintf(FINFO,"Total bytes written: %.0f\n",
a800434a 91 (double)stats.total_written);
1f658d42 92 rprintf(FINFO,"Total bytes read: %.0f\n\n",
a800434a 93 (double)stats.total_read);
7a6421fa
AT
94 }
95
e19452a9
DD
96 if (verbose || do_stats) {
97 rprintf(FINFO,"wrote %.0f bytes read %.0f bytes %.2f bytes/sec\n",
98 (double)stats.total_written,
99 (double)stats.total_read,
100 (stats.total_written+stats.total_read)/(0.5 + (t-starttime)));
101 rprintf(FINFO,"total size is %.0f speedup is %.2f\n",
102 (double)stats.total_size,
103 (1.0*stats.total_size)/(stats.total_written+stats.total_read));
104 }
fc8a6b97
AT
105
106 fflush(stdout);
107 fflush(stderr);
c627d613
AT
108}
109
110
e3cd198f 111static int do_cmd(char *cmd,char *machine,char *user,char *path,int *f_in,int *f_out)
c627d613 112{
366345fe
AT
113 char *args[100];
114 int i,argc=0, ret;
115 char *tok,*dir=NULL;
7a6421fa
AT
116 extern int local_server;
117 extern char *rsync_path;
366345fe
AT
118
119 if (!local_server) {
120 if (!cmd)
121 cmd = getenv(RSYNC_RSH_ENV);
122 if (!cmd)
123 cmd = RSYNC_RSH;
124 cmd = strdup(cmd);
125 if (!cmd)
126 goto oom;
127
128 for (tok=strtok(cmd," ");tok;tok=strtok(NULL," ")) {
129 args[argc++] = tok;
130 }
c627d613 131
7b8356d0 132#if HAVE_REMSH
366345fe
AT
133 /* remsh (on HPUX) takes the arguments the other way around */
134 args[argc++] = machine;
135 if (user) {
136 args[argc++] = "-l";
137 args[argc++] = user;
138 }
7b8356d0 139#else
366345fe
AT
140 if (user) {
141 args[argc++] = "-l";
142 args[argc++] = user;
143 }
144 args[argc++] = machine;
7b8356d0 145#endif
c627d613 146
366345fe 147 args[argc++] = rsync_path;
c627d613 148
366345fe
AT
149 server_options(args,&argc);
150 }
c627d613 151
366345fe 152 args[argc++] = ".";
76076c4b 153
366345fe
AT
154 if (path && *path)
155 args[argc++] = path;
c627d613 156
366345fe 157 args[argc] = NULL;
c627d613 158
366345fe 159 if (verbose > 3) {
9486289c 160 rprintf(FINFO,"cmd=");
366345fe 161 for (i=0;i<argc;i++)
9486289c
AT
162 rprintf(FINFO,"%s ",args[i]);
163 rprintf(FINFO,"\n");
366345fe
AT
164 }
165
166 if (local_server) {
167 ret = local_child(argc, args, f_in, f_out);
168 } else {
169 ret = piped_child(args,f_in,f_out);
170 }
c627d613 171
366345fe 172 if (dir) free(dir);
82306bf6 173
366345fe 174 return ret;
c627d613
AT
175
176oom:
366345fe
AT
177 out_of_memory("do_cmd");
178 return 0; /* not reached */
c627d613
AT
179}
180
181
182
183
184static char *get_local_name(struct file_list *flist,char *name)
185{
7a6421fa
AT
186 STRUCT_STAT st;
187 extern int orig_umask;
c627d613 188
c95da96a
AT
189 if (verbose > 2)
190 rprintf(FINFO,"get_local_name count=%d %s\n",
1f0610ef
DD
191 flist->count, NS(name));
192
193 if (!name)
194 return NULL;
c95da96a 195
1ff5450d
AT
196 if (do_stat(name,&st) == 0) {
197 if (S_ISDIR(st.st_mode)) {
5243c216
AT
198 if (!push_dir(name, 0)) {
199 rprintf(FERROR,"push_dir %s : %s (1)\n",
1ff5450d 200 name,strerror(errno));
65417579 201 exit_cleanup(RERR_FILESELECT);
1ff5450d
AT
202 }
203 return NULL;
204 }
205 if (flist->count > 1) {
206 rprintf(FERROR,"ERROR: destination must be a directory when copying more than 1 file\n");
65417579 207 exit_cleanup(RERR_FILESELECT);
1ff5450d
AT
208 }
209 return name;
210 }
211
212 if (flist->count == 1)
213 return name;
214
1ff5450d
AT
215 if (do_mkdir(name,0777 & ~orig_umask) != 0) {
216 rprintf(FERROR,"mkdir %s : %s (1)\n",name,strerror(errno));
65417579 217 exit_cleanup(RERR_FILEIO);
1ff5450d 218 } else {
b536f47e
AT
219 if (verbose > 0)
220 rprintf(FINFO,"created directory %s\n",name);
1ff5450d
AT
221 }
222
5243c216
AT
223 if (!push_dir(name, 0)) {
224 rprintf(FERROR,"push_dir %s : %s (2)\n",
225 name,strerror(errno));
65417579 226 exit_cleanup(RERR_FILESELECT);
1ff5450d
AT
227 }
228
229 return NULL;
c627d613
AT
230}
231
232
233
234
9486289c 235static void do_server_sender(int f_in, int f_out, int argc,char *argv[])
c627d613 236{
7a6421fa
AT
237 int i;
238 struct file_list *flist;
239 char *dir = argv[0];
240 extern int relative_paths;
7a6421fa 241 extern int recurse;
c627d613 242
7a6421fa
AT
243 if (verbose > 2)
244 rprintf(FINFO,"server_sender starting pid=%d\n",(int)getpid());
c627d613 245
5243c216
AT
246 if (!relative_paths && !push_dir(dir, 0)) {
247 rprintf(FERROR,"push_dir %s: %s (3)\n",dir,strerror(errno));
65417579 248 exit_cleanup(RERR_FILESELECT);
7a6421fa
AT
249 }
250 argc--;
251 argv++;
c627d613 252
7a6421fa
AT
253 if (strcmp(dir,".")) {
254 int l = strlen(dir);
255 if (strcmp(dir,"/") == 0)
256 l = 0;
257 for (i=0;i<argc;i++)
258 argv[i] += l+1;
259 }
c627d613 260
7a6421fa
AT
261 if (argc == 0 && recurse) {
262 argc=1;
263 argv--;
264 argv[0] = ".";
265 }
266
3eb38818
AT
267 set_nonblocking(f_out);
268 if (f_in != f_out)
269 set_nonblocking(f_in);
270
7a6421fa 271 flist = send_file_list(f_out,argc,argv);
8d9dc9f9
AT
272 if (!flist || flist->count == 0) {
273 exit_cleanup(0);
274 }
275
7a6421fa
AT
276 send_files(flist,f_out,f_in);
277 report(f_out);
8d9dc9f9 278 io_flush();
7a6421fa 279 exit_cleanup(0);
c627d613
AT
280}
281
282
dc5ddbcc
AT
283static int do_recv(int f_in,int f_out,struct file_list *flist,char *local_name)
284{
d186eb1a
AT
285 int pid;
286 int status=0;
287 int recv_pipe[2];
288 extern int preserve_hard_links;
dc5ddbcc 289
d186eb1a
AT
290 if (preserve_hard_links)
291 init_hard_links(flist);
dc5ddbcc 292
d186eb1a
AT
293 if (pipe(recv_pipe) < 0) {
294 rprintf(FERROR,"pipe failed in do_recv\n");
65417579 295 exit_cleanup(RERR_SOCKETIO);
d186eb1a 296 }
c6e7fcb4 297
8d9dc9f9 298 io_flush();
c6e7fcb4 299
d186eb1a 300 if ((pid=do_fork()) == 0) {
e08c9610
AT
301 close(recv_pipe[0]);
302 if (f_in != f_out) close(f_out);
303
e1b3d5c4 304 set_nonblocking(f_in);
3eb38818 305 set_nonblocking(recv_pipe[1]);
e1b3d5c4 306
e08c9610 307 recv_files(f_in,flist,local_name,recv_pipe[1]);
ba5e128d 308 report(f_in);
e08c9610 309
8d9dc9f9
AT
310 io_flush();
311 _exit(0);
d186eb1a 312 }
dc5ddbcc 313
e08c9610
AT
314 close(recv_pipe[1]);
315 io_close_input(f_in);
316 if (f_in != f_out) close(f_in);
e1b3d5c4
AT
317
318 set_nonblocking(f_out);
3eb38818 319 set_nonblocking(recv_pipe[0]);
e1b3d5c4 320
b3e10ed7
AT
321 io_start_buffering(f_out);
322
e08c9610 323 generate_files(f_out,flist,local_name,recv_pipe[0]);
8d9dc9f9
AT
324
325 io_flush();
d186eb1a 326 waitpid(pid, &status, 0);
d186eb1a 327 return status;
dc5ddbcc
AT
328}
329
c627d613 330
9486289c 331static void do_server_recv(int f_in, int f_out, int argc,char *argv[])
c627d613 332{
7a6421fa
AT
333 int status;
334 struct file_list *flist;
335 char *local_name=NULL;
336 char *dir = NULL;
337 extern int delete_mode;
338 extern int am_daemon;
f0fca04e 339
7a6421fa
AT
340 if (verbose > 2)
341 rprintf(FINFO,"server_recv(%d) starting pid=%d\n",argc,(int)getpid());
342
7a6421fa
AT
343 if (argc > 0) {
344 dir = argv[0];
345 argc--;
346 argv++;
5243c216
AT
347 if (!am_daemon && !push_dir(dir, 0)) {
348 rprintf(FERROR,"push_dir %s : %s (4)\n",
7a6421fa 349 dir,strerror(errno));
65417579 350 exit_cleanup(RERR_FILESELECT);
7a6421fa
AT
351 }
352 }
c627d613 353
7a6421fa
AT
354 if (delete_mode)
355 recv_exclude_list(f_in);
c627d613 356
7a6421fa
AT
357 flist = recv_file_list(f_in);
358 if (!flist || flist->count == 0) {
8d9dc9f9 359 rprintf(FERROR,"server_recv: nothing to do\n");
65417579 360 exit_cleanup(RERR_FILESELECT);
7a6421fa
AT
361 }
362
363 if (argc > 0) {
364 if (strcmp(dir,".")) {
365 argv[0] += strlen(dir);
366 if (argv[0][0] == '/') argv[0]++;
367 }
368 local_name = get_local_name(flist,argv[0]);
369 }
c627d613 370
7a6421fa
AT
371 status = do_recv(f_in,f_out,flist,local_name);
372 exit_cleanup(status);
c627d613
AT
373}
374
375
9486289c 376void start_server(int f_in, int f_out, int argc, char *argv[])
366345fe 377{
7a6421fa
AT
378 extern int cvs_exclude;
379 extern int am_sender;
380
3eb38818
AT
381 set_nonblocking(f_out);
382 if (f_in != f_out)
383 set_nonblocking(f_in);
384
7a6421fa 385 setup_protocol(f_out, f_in);
3eb38818 386
7a6421fa
AT
387 if (am_sender) {
388 recv_exclude_list(f_in);
389 if (cvs_exclude)
390 add_cvs_excludes();
391 do_server_sender(f_in, f_out, argc, argv);
392 } else {
393 do_server_recv(f_in, f_out, argc, argv);
394 }
395 exit_cleanup(0);
366345fe
AT
396}
397
3591c066 398int client_run(int f_in, int f_out, int pid, int argc, char *argv[])
9486289c
AT
399{
400 struct file_list *flist;
401 int status = 0, status2 = 0;
402 char *local_name = NULL;
7a6421fa 403 extern int am_sender;
f7632fc6 404 extern int list_only;
9486289c
AT
405
406 setup_protocol(f_out,f_in);
407
408 if (am_sender) {
7a6421fa
AT
409 extern int cvs_exclude;
410 extern int delete_mode;
9486289c
AT
411 if (cvs_exclude)
412 add_cvs_excludes();
413 if (delete_mode)
414 send_exclude_list(f_out);
415 flist = send_file_list(f_out,argc,argv);
416 if (verbose > 3)
417 rprintf(FINFO,"file list sent\n");
e1b3d5c4
AT
418
419 set_nonblocking(f_out);
420 if (f_in != f_out)
421 set_nonblocking(f_in);
422
9486289c
AT
423 send_files(flist,f_out,f_in);
424 if (pid != -1) {
425 if (verbose > 3)
8d9dc9f9
AT
426 rprintf(FINFO,"client_run waiting on %d\n",pid);
427 io_flush();
9486289c
AT
428 waitpid(pid, &status, 0);
429 }
430 report(-1);
431 exit_cleanup(status);
432 }
f7632fc6
AT
433
434 if (argc == 0) list_only = 1;
9486289c
AT
435
436 send_exclude_list(f_out);
437
438 flist = recv_file_list(f_in);
439 if (!flist || flist->count == 0) {
8d9dc9f9 440 rprintf(FINFO,"client: nothing to do\n");
9486289c
AT
441 exit_cleanup(0);
442 }
443
444 local_name = get_local_name(flist,argv[0]);
445
446 status2 = do_recv(f_in,f_out,flist,local_name);
447
9486289c 448 if (pid != -1) {
8d9dc9f9
AT
449 if (verbose > 3)
450 rprintf(FINFO,"client_run2 waiting on %d\n",pid);
451 io_flush();
9486289c
AT
452 waitpid(pid, &status, 0);
453 }
454
455 return status | status2;
456}
457
ca6c93f8
AT
458static char *find_colon(char *s)
459{
460 char *p, *p2;
461
462 p = strchr(s,':');
463 if (!p) return NULL;
464
465 /* now check to see if there is a / in the string before the : - if there is then
466 discard the colon on the assumption that the : is part of a filename */
467 p2 = strchr(s,'/');
468 if (p2 && p2 < p) return NULL;
469
470 return p;
471}
9486289c 472
fc8a6b97 473static int start_client(int argc, char *argv[])
5d6bcd44
AT
474{
475 char *p;
476 char *shell_machine = NULL;
477 char *shell_path = NULL;
478 char *shell_user = NULL;
fc8a6b97 479 int pid, ret;
5d6bcd44 480 int f_in,f_out;
7a6421fa
AT
481 extern int local_server;
482 extern int am_sender;
483 extern char *shell_cmd;
2acf81eb 484 extern int rsync_port;
5d6bcd44 485
f7632fc6
AT
486 if (strncasecmp(URL_PREFIX, argv[0], strlen(URL_PREFIX)) == 0) {
487 char *host, *path;
488
489 host = argv[0] + strlen(URL_PREFIX);
490 p = strchr(host,'/');
491 if (p) {
492 *p = 0;
493 path = p+1;
494 } else {
495 path="";
496 }
2acf81eb
DD
497 p = strchr(host,':');
498 if (p) {
499 rsync_port = atoi(p+1);
500 *p = 0;
501 }
f7632fc6
AT
502 return start_socket_client(host, path, argc-1, argv+1);
503 }
504
ca6c93f8 505 p = find_colon(argv[0]);
5d6bcd44
AT
506
507 if (p) {
9486289c
AT
508 if (p[1] == ':') {
509 *p = 0;
510 return start_socket_client(argv[0], p+2, argc-1, argv+1);
511 }
3591c066 512
f7632fc6 513 if (argc < 1) {
3591c066 514 usage(FERROR);
65417579 515 exit_cleanup(RERR_SYNTAX);
3591c066
AT
516 }
517
5d6bcd44
AT
518 am_sender = 0;
519 *p = 0;
520 shell_machine = argv[0];
521 shell_path = p+1;
522 argc--;
523 argv++;
524 } else {
525 am_sender = 1;
9486289c 526
ca6c93f8 527 p = find_colon(argv[argc-1]);
5d6bcd44
AT
528 if (!p) {
529 local_server = 1;
9486289c
AT
530 } else if (p[1] == ':') {
531 *p = 0;
532 return start_socket_client(argv[argc-1], p+2, argc-1, argv);
5d6bcd44 533 }
3591c066
AT
534
535 if (argc < 2) {
536 usage(FERROR);
65417579 537 exit_cleanup(RERR_SYNTAX);
3591c066 538 }
9486289c 539
5d6bcd44
AT
540 if (local_server) {
541 shell_machine = NULL;
542 shell_path = argv[argc-1];
543 } else {
544 *p = 0;
545 shell_machine = argv[argc-1];
546 shell_path = p+1;
547 }
548 argc--;
549 }
550
551 if (shell_machine) {
552 p = strchr(shell_machine,'@');
553 if (p) {
554 *p = 0;
555 shell_user = shell_machine;
556 shell_machine = p+1;
557 }
558 }
559
560 if (verbose > 3) {
9486289c 561 rprintf(FINFO,"cmd=%s machine=%s user=%s path=%s\n",
5d6bcd44
AT
562 shell_cmd?shell_cmd:"",
563 shell_machine?shell_machine:"",
564 shell_user?shell_user:"",
565 shell_path?shell_path:"");
566 }
567
f7632fc6 568 if (!am_sender && argc > 1) {
5d6bcd44 569 usage(FERROR);
65417579 570 exit_cleanup(RERR_SYNTAX);
5d6bcd44
AT
571 }
572
573 pid = do_cmd(shell_cmd,shell_machine,shell_user,shell_path,&f_in,&f_out);
574
fc8a6b97
AT
575 ret = client_run(f_in, f_out, pid, argc, argv);
576
577 fflush(stdout);
578 fflush(stderr);
579
580 return ret;
5d6bcd44
AT
581}
582
366345fe 583
6e4fb64e 584static RETSIGTYPE sigusr1_handler(int val) {
65417579 585 exit_cleanup(RERR_SIGNAL);
82306bf6
AT
586}
587
5d6bcd44 588int main(int argc,char *argv[])
7a6421fa
AT
589{
590 extern int am_root;
591 extern int orig_umask;
592 extern int dry_run;
593 extern int am_daemon;
594 extern int am_server;
5d6bcd44 595
7a6421fa 596 signal(SIGUSR1, sigusr1_handler);
5d6bcd44 597
7a6421fa
AT
598 starttime = time(NULL);
599 am_root = (getuid() == 0);
c627d613 600
a800434a
AT
601 memset(&stats, 0, sizeof(stats));
602
df5e03da
AT
603 if (argc < 2) {
604 usage(FERROR);
65417579 605 exit_cleanup(RERR_SYNTAX);
df5e03da
AT
606 }
607
7a6421fa
AT
608 /* we set a 0 umask so that correct file permissions can be
609 carried across */
610 orig_umask = (int)umask(0);
5d6bcd44 611
b86f0cef 612 if (!parse_arguments(argc, argv, 1)) {
65417579 613 exit_cleanup(RERR_SYNTAX);
b11ed3b1 614 }
5d6bcd44 615
7a6421fa
AT
616 argc -= optind;
617 argv += optind;
618 optind = 0;
c627d613 619
7a6421fa
AT
620 signal(SIGCHLD,SIG_IGN);
621 signal(SIGINT,SIGNAL_CAST sig_int);
622 signal(SIGPIPE,SIGNAL_CAST sig_int);
623 signal(SIGHUP,SIGNAL_CAST sig_int);
8638dd48 624 signal(SIGTERM,SIGNAL_CAST sig_int);
6b83141d 625
c226b7c2
DD
626 /* Initialize push_dir here because on some old systems getcwd
627 (implemented by forking "pwd" and reading its output) doesn't
628 work when there are other child processes. Also, on all systems
629 that implement getcwd that way "pwd" can't be found after chroot. */
630 push_dir(NULL,0);
631
7a6421fa
AT
632 if (am_daemon) {
633 return daemon_main();
634 }
f0fca04e 635
08ac228f
AT
636 if (argc < 1) {
637 usage(FERROR);
65417579 638 exit_cleanup(RERR_SYNTAX);
08ac228f
AT
639 }
640
7a6421fa
AT
641 if (dry_run)
642 verbose = MAX(verbose,1);
c627d613 643
cbbe4892 644#ifndef SUPPORT_LINKS
7a6421fa
AT
645 if (!am_server && preserve_links) {
646 rprintf(FERROR,"ERROR: symbolic links not supported\n");
65417579 647 exit_cleanup(RERR_UNSUPPORTED);
7a6421fa 648 }
cbbe4892
AT
649#endif
650
7a6421fa
AT
651 if (am_server) {
652 start_server(STDIN_FILENO, STDOUT_FILENO, argc, argv);
653 }
c627d613 654
7a6421fa 655 return start_client(argc, argv);
c627d613 656}
82306bf6 657