Allow --stats to work without -v.
[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
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
105 static 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
170 oom:
171         out_of_memory("do_cmd");
172         return 0; /* not reached */
173 }
174
175
176
177
178 static 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
229 static 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
277 static 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
325 static 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
370 void 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
392 int 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
452 static 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
467 static 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
578 static RETSIGTYPE sigusr1_handler(int val) {
579         exit_cleanup(RERR_SIGNAL);
580 }
581
582 int 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