Changed --stats implementation to work without -v in only these two
[rsync/rsync.git] / main.c
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
22 time_t starttime = 0;
23
24 struct stats stats;
25
26 extern int csum_length;
27
28 extern int verbose;
29
30 static 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
111 static 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
176 oom:
177         out_of_memory("do_cmd");
178         return 0; /* not reached */
179 }
180
181
182
183
184 static 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
235 static 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
283 static 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
331 static 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
376 void 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
398 int 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
458 static 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
473 static 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
584 static RETSIGTYPE sigusr1_handler(int val) {
585         exit_cleanup(RERR_SIGNAL);
586 }
587
588 int 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