Changed --stats implementation to work without -v in only these two
[rsync/rsync.git] / main.c
... / ...
CommitLineData
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
22time_t starttime = 0;
23
24struct stats stats;
25
26extern int csum_length;
27
28extern int verbose;
29
30static void report(int f)
31{
32 time_t t = time(NULL);
33 extern int am_server;
34 extern int am_sender;
35 extern int am_daemon;
36 extern int do_stats;
37 extern int remote_version;
38 int send_stats;
39
40 if (am_daemon) {
41 log_exit(0, __FILE__, __LINE__);
42 if (f == -1 || !am_sender) return;
43 }
44
45 send_stats = verbose ||
46 ((remote_version >= 20) && (PROTOCOL_VERSION >= 20));
47 if (am_server) {
48 if (am_sender && send_stats) {
49 int64 w;
50 /* store total_written in a temporary
51 because write_longint changes it */
52 w = stats.total_written;
53 write_longint(f,stats.total_read);
54 write_longint(f,w);
55 write_longint(f,stats.total_size);
56 }
57 return;
58 }
59
60 /* this is the client */
61
62 if (!am_sender && send_stats) {
63 int64 r;
64 stats.total_written = read_longint(f);
65 /* store total_read in a temporary, read_longint changes it */
66 r = read_longint(f);
67 stats.total_size = read_longint(f);
68 stats.total_read = r;
69 }
70
71 if (do_stats) {
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 }
78 rprintf(FINFO,"\nNumber of files: %d\n", stats.num_files);
79 rprintf(FINFO,"Number of files transferred: %d\n",
80 stats.num_transferred_files);
81 rprintf(FINFO,"Total file size: %.0f bytes\n",
82 (double)stats.total_size);
83 rprintf(FINFO,"Total transferred file size: %.0f bytes\n",
84 (double)stats.total_transferred_size);
85 rprintf(FINFO,"Literal data: %.0f bytes\n",
86 (double)stats.literal_data);
87 rprintf(FINFO,"Matched data: %.0f bytes\n",
88 (double)stats.matched_data);
89 rprintf(FINFO,"File list size: %d\n", stats.flist_size);
90 rprintf(FINFO,"Total bytes written: %.0f\n",
91 (double)stats.total_written);
92 rprintf(FINFO,"Total bytes read: %.0f\n\n",
93 (double)stats.total_read);
94 }
95
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 }
105
106 fflush(stdout);
107 fflush(stderr);
108}
109
110
111static int do_cmd(char *cmd,char *machine,char *user,char *path,int *f_in,int *f_out)
112{
113 char *args[100];
114 int i,argc=0, ret;
115 char *tok,*dir=NULL;
116 extern int local_server;
117 extern char *rsync_path;
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 }
131
132#if HAVE_REMSH
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 }
139#else
140 if (user) {
141 args[argc++] = "-l";
142 args[argc++] = user;
143 }
144 args[argc++] = machine;
145#endif
146
147 args[argc++] = rsync_path;
148
149 server_options(args,&argc);
150 }
151
152 args[argc++] = ".";
153
154 if (path && *path)
155 args[argc++] = path;
156
157 args[argc] = NULL;
158
159 if (verbose > 3) {
160 rprintf(FINFO,"cmd=");
161 for (i=0;i<argc;i++)
162 rprintf(FINFO,"%s ",args[i]);
163 rprintf(FINFO,"\n");
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 }
171
172 if (dir) free(dir);
173
174 return ret;
175
176oom:
177 out_of_memory("do_cmd");
178 return 0; /* not reached */
179}
180
181
182
183
184static char *get_local_name(struct file_list *flist,char *name)
185{
186 STRUCT_STAT st;
187 extern int orig_umask;
188
189 if (verbose > 2)
190 rprintf(FINFO,"get_local_name count=%d %s\n",
191 flist->count, NS(name));
192
193 if (!name)
194 return NULL;
195
196 if (do_stat(name,&st) == 0) {
197 if (S_ISDIR(st.st_mode)) {
198 if (!push_dir(name, 0)) {
199 rprintf(FERROR,"push_dir %s : %s (1)\n",
200 name,strerror(errno));
201 exit_cleanup(RERR_FILESELECT);
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");
207 exit_cleanup(RERR_FILESELECT);
208 }
209 return name;
210 }
211
212 if (flist->count == 1)
213 return name;
214
215 if (do_mkdir(name,0777 & ~orig_umask) != 0) {
216 rprintf(FERROR,"mkdir %s : %s (1)\n",name,strerror(errno));
217 exit_cleanup(RERR_FILEIO);
218 } else {
219 if (verbose > 0)
220 rprintf(FINFO,"created directory %s\n",name);
221 }
222
223 if (!push_dir(name, 0)) {
224 rprintf(FERROR,"push_dir %s : %s (2)\n",
225 name,strerror(errno));
226 exit_cleanup(RERR_FILESELECT);
227 }
228
229 return NULL;
230}
231
232
233
234
235static void do_server_sender(int f_in, int f_out, int argc,char *argv[])
236{
237 int i;
238 struct file_list *flist;
239 char *dir = argv[0];
240 extern int relative_paths;
241 extern int recurse;
242
243 if (verbose > 2)
244 rprintf(FINFO,"server_sender starting pid=%d\n",(int)getpid());
245
246 if (!relative_paths && !push_dir(dir, 0)) {
247 rprintf(FERROR,"push_dir %s: %s (3)\n",dir,strerror(errno));
248 exit_cleanup(RERR_FILESELECT);
249 }
250 argc--;
251 argv++;
252
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 }
260
261 if (argc == 0 && recurse) {
262 argc=1;
263 argv--;
264 argv[0] = ".";
265 }
266
267 set_nonblocking(f_out);
268 if (f_in != f_out)
269 set_nonblocking(f_in);
270
271 flist = send_file_list(f_out,argc,argv);
272 if (!flist || flist->count == 0) {
273 exit_cleanup(0);
274 }
275
276 send_files(flist,f_out,f_in);
277 report(f_out);
278 io_flush();
279 exit_cleanup(0);
280}
281
282
283static int do_recv(int f_in,int f_out,struct file_list *flist,char *local_name)
284{
285 int pid;
286 int status=0;
287 int recv_pipe[2];
288 extern int preserve_hard_links;
289
290 if (preserve_hard_links)
291 init_hard_links(flist);
292
293 if (pipe(recv_pipe) < 0) {
294 rprintf(FERROR,"pipe failed in do_recv\n");
295 exit_cleanup(RERR_SOCKETIO);
296 }
297
298 io_flush();
299
300 if ((pid=do_fork()) == 0) {
301 close(recv_pipe[0]);
302 if (f_in != f_out) close(f_out);
303
304 set_nonblocking(f_in);
305 set_nonblocking(recv_pipe[1]);
306
307 recv_files(f_in,flist,local_name,recv_pipe[1]);
308 report(f_in);
309
310 io_flush();
311 _exit(0);
312 }
313
314 close(recv_pipe[1]);
315 io_close_input(f_in);
316 if (f_in != f_out) close(f_in);
317
318 set_nonblocking(f_out);
319 set_nonblocking(recv_pipe[0]);
320
321 io_start_buffering(f_out);
322
323 generate_files(f_out,flist,local_name,recv_pipe[0]);
324
325 io_flush();
326 waitpid(pid, &status, 0);
327 return status;
328}
329
330
331static void do_server_recv(int f_in, int f_out, int argc,char *argv[])
332{
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;
339
340 if (verbose > 2)
341 rprintf(FINFO,"server_recv(%d) starting pid=%d\n",argc,(int)getpid());
342
343 if (argc > 0) {
344 dir = argv[0];
345 argc--;
346 argv++;
347 if (!am_daemon && !push_dir(dir, 0)) {
348 rprintf(FERROR,"push_dir %s : %s (4)\n",
349 dir,strerror(errno));
350 exit_cleanup(RERR_FILESELECT);
351 }
352 }
353
354 if (delete_mode)
355 recv_exclude_list(f_in);
356
357 flist = recv_file_list(f_in);
358 if (!flist || flist->count == 0) {
359 rprintf(FERROR,"server_recv: nothing to do\n");
360 exit_cleanup(RERR_FILESELECT);
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 }
370
371 status = do_recv(f_in,f_out,flist,local_name);
372 exit_cleanup(status);
373}
374
375
376void start_server(int f_in, int f_out, int argc, char *argv[])
377{
378 extern int cvs_exclude;
379 extern int am_sender;
380
381 set_nonblocking(f_out);
382 if (f_in != f_out)
383 set_nonblocking(f_in);
384
385 setup_protocol(f_out, f_in);
386
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);
396}
397
398int client_run(int f_in, int f_out, int pid, int argc, char *argv[])
399{
400 struct file_list *flist;
401 int status = 0, status2 = 0;
402 char *local_name = NULL;
403 extern int am_sender;
404 extern int list_only;
405
406 setup_protocol(f_out,f_in);
407
408 if (am_sender) {
409 extern int cvs_exclude;
410 extern int delete_mode;
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");
418
419 set_nonblocking(f_out);
420 if (f_in != f_out)
421 set_nonblocking(f_in);
422
423 send_files(flist,f_out,f_in);
424 if (pid != -1) {
425 if (verbose > 3)
426 rprintf(FINFO,"client_run waiting on %d\n",pid);
427 io_flush();
428 waitpid(pid, &status, 0);
429 }
430 report(-1);
431 exit_cleanup(status);
432 }
433
434 if (argc == 0) list_only = 1;
435
436 send_exclude_list(f_out);
437
438 flist = recv_file_list(f_in);
439 if (!flist || flist->count == 0) {
440 rprintf(FINFO,"client: nothing to do\n");
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
448 if (pid != -1) {
449 if (verbose > 3)
450 rprintf(FINFO,"client_run2 waiting on %d\n",pid);
451 io_flush();
452 waitpid(pid, &status, 0);
453 }
454
455 return status | status2;
456}
457
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}
472
473static int start_client(int argc, char *argv[])
474{
475 char *p;
476 char *shell_machine = NULL;
477 char *shell_path = NULL;
478 char *shell_user = NULL;
479 int pid, ret;
480 int f_in,f_out;
481 extern int local_server;
482 extern int am_sender;
483 extern char *shell_cmd;
484 extern int rsync_port;
485
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 }
497 p = strchr(host,':');
498 if (p) {
499 rsync_port = atoi(p+1);
500 *p = 0;
501 }
502 return start_socket_client(host, path, argc-1, argv+1);
503 }
504
505 p = find_colon(argv[0]);
506
507 if (p) {
508 if (p[1] == ':') {
509 *p = 0;
510 return start_socket_client(argv[0], p+2, argc-1, argv+1);
511 }
512
513 if (argc < 1) {
514 usage(FERROR);
515 exit_cleanup(RERR_SYNTAX);
516 }
517
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;
526
527 p = find_colon(argv[argc-1]);
528 if (!p) {
529 local_server = 1;
530 } else if (p[1] == ':') {
531 *p = 0;
532 return start_socket_client(argv[argc-1], p+2, argc-1, argv);
533 }
534
535 if (argc < 2) {
536 usage(FERROR);
537 exit_cleanup(RERR_SYNTAX);
538 }
539
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) {
561 rprintf(FINFO,"cmd=%s machine=%s user=%s path=%s\n",
562 shell_cmd?shell_cmd:"",
563 shell_machine?shell_machine:"",
564 shell_user?shell_user:"",
565 shell_path?shell_path:"");
566 }
567
568 if (!am_sender && argc > 1) {
569 usage(FERROR);
570 exit_cleanup(RERR_SYNTAX);
571 }
572
573 pid = do_cmd(shell_cmd,shell_machine,shell_user,shell_path,&f_in,&f_out);
574
575 ret = client_run(f_in, f_out, pid, argc, argv);
576
577 fflush(stdout);
578 fflush(stderr);
579
580 return ret;
581}
582
583
584static RETSIGTYPE sigusr1_handler(int val) {
585 exit_cleanup(RERR_SIGNAL);
586}
587
588int main(int argc,char *argv[])
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;
595
596 signal(SIGUSR1, sigusr1_handler);
597
598 starttime = time(NULL);
599 am_root = (getuid() == 0);
600
601 memset(&stats, 0, sizeof(stats));
602
603 if (argc < 2) {
604 usage(FERROR);
605 exit_cleanup(RERR_SYNTAX);
606 }
607
608 /* we set a 0 umask so that correct file permissions can be
609 carried across */
610 orig_umask = (int)umask(0);
611
612 if (!parse_arguments(argc, argv, 1)) {
613 exit_cleanup(RERR_SYNTAX);
614 }
615
616 argc -= optind;
617 argv += optind;
618 optind = 0;
619
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);
624 signal(SIGTERM,SIGNAL_CAST sig_int);
625
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
632 if (am_daemon) {
633 return daemon_main();
634 }
635
636 if (argc < 1) {
637 usage(FERROR);
638 exit_cleanup(RERR_SYNTAX);
639 }
640
641 if (dry_run)
642 verbose = MAX(verbose,1);
643
644#ifndef SUPPORT_LINKS
645 if (!am_server && preserve_links) {
646 rprintf(FERROR,"ERROR: symbolic links not supported\n");
647 exit_cleanup(RERR_UNSUPPORTED);
648 }
649#endif
650
651 if (am_server) {
652 start_server(STDIN_FILENO, STDOUT_FILENO, argc, argv);
653 }
654
655 return start_client(argc, argv);
656}
657