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