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