moved file deletion to before the fork() to prevent a race condition
[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         extern int delete_after;
284         extern int recurse;
285         extern int delete_mode;
286
287         if (preserve_hard_links)
288                 init_hard_links(flist);
289
290         if (!delete_after) {
291                 /* I moved this here from recv_files() to prevent a race condition */
292                 if (recurse && delete_mode && !local_name && flist->count>0) {
293                         delete_files(flist);
294                 }
295         }
296
297         if (pipe(recv_pipe) < 0) {
298                 rprintf(FERROR,"pipe failed in do_recv\n");
299                 exit_cleanup(RERR_SOCKETIO);
300         }
301
302         if (pipe(error_pipe) < 0) {
303                 rprintf(FERROR,"error pipe failed in do_recv\n");
304                 exit_cleanup(RERR_SOCKETIO);
305         }
306   
307         io_flush();
308
309         if ((pid=do_fork()) == 0) {
310                 close(recv_pipe[0]);
311                 close(error_pipe[0]);
312                 if (f_in != f_out) close(f_out);
313
314                 /* we can't let two processes write to the socket at one time */
315                 io_multiplexing_close();
316
317                 /* set place to send errors */
318                 set_error_fd(error_pipe[1]);
319
320                 recv_files(f_in,flist,local_name,recv_pipe[1]);
321                 report(f_in);
322
323                 io_flush();
324                 _exit(0);
325         }
326
327         close(recv_pipe[1]);
328         close(error_pipe[1]);
329         io_close_input(f_in);
330         if (f_in != f_out) close(f_in);
331
332         io_start_buffering(f_out);
333
334         io_set_error_fd(error_pipe[0]);
335
336         generate_files(f_out,flist,local_name,recv_pipe[0]);
337
338         io_flush();
339         wait_process(pid, &status);
340         return status;
341 }
342
343
344 static void do_server_recv(int f_in, int f_out, int argc,char *argv[])
345 {
346         int status;
347         struct file_list *flist;
348         char *local_name=NULL;
349         char *dir = NULL;
350         extern int delete_mode;
351         extern int delete_excluded;
352         extern int am_daemon;
353
354         if (verbose > 2)
355                 rprintf(FINFO,"server_recv(%d) starting pid=%d\n",argc,(int)getpid());
356         
357         if (argc > 0) {
358                 dir = argv[0];
359                 argc--;
360                 argv++;
361                 if (!am_daemon && !push_dir(dir, 0)) {
362                         rprintf(FERROR,"push_dir %s : %s (4)\n",
363                                 dir,strerror(errno));
364                         exit_cleanup(RERR_FILESELECT);
365                 }    
366         }
367
368         if (delete_mode && !delete_excluded)
369                 recv_exclude_list(f_in);
370
371         flist = recv_file_list(f_in);
372         if (!flist) {
373                 rprintf(FERROR,"server_recv: recv_file_list error\n");
374                 exit_cleanup(RERR_FILESELECT);
375         }
376         
377         if (argc > 0) {    
378                 if (strcmp(dir,".")) {
379                         argv[0] += strlen(dir);
380                         if (argv[0][0] == '/') argv[0]++;
381                 }
382                 local_name = get_local_name(flist,argv[0]);
383         }
384
385         status = do_recv(f_in,f_out,flist,local_name);
386         exit_cleanup(status);
387 }
388
389
390 void start_server(int f_in, int f_out, int argc, char *argv[])
391 {
392         extern int cvs_exclude;
393         extern int am_sender;
394
395         setup_protocol(f_out, f_in);
396
397         if (am_sender) {
398                 recv_exclude_list(f_in);
399                 if (cvs_exclude)
400                         add_cvs_excludes();
401                 do_server_sender(f_in, f_out, argc, argv);
402         } else {
403                 do_server_recv(f_in, f_out, argc, argv);
404         }
405         exit_cleanup(0);
406 }
407
408 int client_run(int f_in, int f_out, int pid, int argc, char *argv[])
409 {
410         struct file_list *flist;
411         int status = 0, status2 = 0;
412         char *local_name = NULL;
413         extern int am_sender;
414         extern int list_only;
415
416         setup_protocol(f_out,f_in);
417         
418         if (am_sender) {
419                 extern int cvs_exclude;
420                 extern int delete_mode;
421                 extern int delete_excluded;
422                 if (cvs_exclude)
423                         add_cvs_excludes();
424                 if (delete_mode && !delete_excluded) 
425                         send_exclude_list(f_out);
426                 flist = send_file_list(f_out,argc,argv);
427                 if (verbose > 3) 
428                         rprintf(FINFO,"file list sent\n");
429
430                 send_files(flist,f_out,f_in);
431                 if (pid != -1) {
432                         if (verbose > 3)
433                                 rprintf(FINFO,"client_run waiting on %d\n",pid);
434                         io_flush();
435                         wait_process(pid, &status);
436                 }
437                 report(-1);
438                 exit_cleanup(status);
439         }
440
441         if (argc == 0) list_only = 1;
442         
443         send_exclude_list(f_out);
444         
445         flist = recv_file_list(f_in);
446         if (!flist || flist->count == 0) {
447                 rprintf(FINFO,"client: nothing to do\n");
448                 exit_cleanup(0);
449         }
450         
451         local_name = get_local_name(flist,argv[0]);
452         
453         status2 = do_recv(f_in,f_out,flist,local_name);
454         
455         if (pid != -1) {
456                 if (verbose > 3)
457                         rprintf(FINFO,"client_run2 waiting on %d\n",pid);
458                 io_flush();
459                 wait_process(pid, &status);
460         }
461         
462         return status | status2;
463 }
464
465 static char *find_colon(char *s)
466 {
467         char *p, *p2;
468
469         p = strchr(s,':');
470         if (!p) return NULL;
471         
472         /* now check to see if there is a / in the string before the : - if there is then
473            discard the colon on the assumption that the : is part of a filename */
474         p2 = strchr(s,'/');
475         if (p2 && p2 < p) return NULL;
476
477         return p;
478 }
479
480 static int start_client(int argc, char *argv[])
481 {
482         char *p;
483         char *shell_machine = NULL;
484         char *shell_path = NULL;
485         char *shell_user = NULL;
486         int pid, ret;
487         int f_in,f_out;
488         extern int local_server;
489         extern int am_sender;
490         extern char *shell_cmd;
491         extern int rsync_port;
492
493         if (strncasecmp(URL_PREFIX, argv[0], strlen(URL_PREFIX)) == 0) {
494                 char *host, *path;
495
496                 host = argv[0] + strlen(URL_PREFIX);
497                 p = strchr(host,'/');
498                 if (p) {
499                         *p = 0;
500                         path = p+1;
501                 } else {
502                         path="";
503                 }
504                 p = strchr(host,':');
505                 if (p) {
506                         rsync_port = atoi(p+1);
507                         *p = 0;
508                 }
509                 return start_socket_client(host, path, argc-1, argv+1);
510         }
511
512         p = find_colon(argv[0]);
513
514         if (p) {
515                 if (p[1] == ':') {
516                         *p = 0;
517                         return start_socket_client(argv[0], p+2, argc-1, argv+1);
518                 }
519
520                 if (argc < 1) {
521                         usage(FERROR);
522                         exit_cleanup(RERR_SYNTAX);
523                 }
524
525                 am_sender = 0;
526                 *p = 0;
527                 shell_machine = argv[0];
528                 shell_path = p+1;
529                 argc--;
530                 argv++;
531         } else {
532                 am_sender = 1;
533
534                 p = find_colon(argv[argc-1]);
535                 if (!p) {
536                         local_server = 1;
537                 } else if (p[1] == ':') {
538                         *p = 0;
539                         return start_socket_client(argv[argc-1], p+2, argc-1, argv);
540                 }
541
542                 if (argc < 2) {
543                         usage(FERROR);
544                         exit_cleanup(RERR_SYNTAX);
545                 }
546                 
547                 if (local_server) {
548                         shell_machine = NULL;
549                         shell_path = argv[argc-1];
550                 } else {
551                         *p = 0;
552                         shell_machine = argv[argc-1];
553                         shell_path = p+1;
554                 }
555                 argc--;
556         }
557         
558         if (shell_machine) {
559                 p = strchr(shell_machine,'@');
560                 if (p) {
561                         *p = 0;
562                         shell_user = shell_machine;
563                         shell_machine = p+1;
564                 }
565         }
566
567         if (verbose > 3) {
568                 rprintf(FINFO,"cmd=%s machine=%s user=%s path=%s\n",
569                         shell_cmd?shell_cmd:"",
570                         shell_machine?shell_machine:"",
571                         shell_user?shell_user:"",
572                         shell_path?shell_path:"");
573         }
574         
575         if (!am_sender && argc > 1) {
576                 usage(FERROR);
577                 exit_cleanup(RERR_SYNTAX);
578         }
579         
580         pid = do_cmd(shell_cmd,shell_machine,shell_user,shell_path,&f_in,&f_out);
581         
582         ret = client_run(f_in, f_out, pid, argc, argv);
583
584         fflush(stdout);
585         fflush(stderr);
586
587         return ret;
588 }
589
590
591 static RETSIGTYPE sigusr1_handler(int val) {
592         exit_cleanup(RERR_SIGNAL);
593 }
594
595 int main(int argc,char *argv[])
596 {       
597         extern int am_root;
598         extern int orig_umask;
599         extern int dry_run;
600         extern int am_daemon;
601         extern int am_server;
602
603         signal(SIGUSR1, sigusr1_handler);
604
605         starttime = time(NULL);
606         am_root = (getuid() == 0);
607
608         memset(&stats, 0, sizeof(stats));
609
610         if (argc < 2) {
611                 usage(FERROR);
612                 exit_cleanup(RERR_SYNTAX);
613         }
614
615         /* we set a 0 umask so that correct file permissions can be
616            carried across */
617         orig_umask = (int)umask(0);
618
619         if (!parse_arguments(argc, argv, 1)) {
620                 exit_cleanup(RERR_SYNTAX);
621         }
622
623         argc -= optind;
624         argv += optind;
625         optind = 0;
626
627         signal(SIGCHLD,SIG_IGN);
628         signal(SIGINT,SIGNAL_CAST sig_int);
629         signal(SIGPIPE,SIGNAL_CAST sig_int);
630         signal(SIGHUP,SIGNAL_CAST sig_int);
631         signal(SIGTERM,SIGNAL_CAST sig_int);
632
633         /* Initialize push_dir here because on some old systems getcwd
634            (implemented by forking "pwd" and reading its output) doesn't
635            work when there are other child processes.  Also, on all systems
636            that implement getcwd that way "pwd" can't be found after chroot. */
637         push_dir(NULL,0);
638
639         if (am_daemon) {
640                 return daemon_main();
641         }
642
643         if (argc < 1) {
644                 usage(FERROR);
645                 exit_cleanup(RERR_SYNTAX);
646         }
647
648         if (dry_run)
649                 verbose = MAX(verbose,1);
650
651 #ifndef SUPPORT_LINKS
652         if (!am_server && preserve_links) {
653                 rprintf(FERROR,"ERROR: symbolic links not supported\n");
654                 exit_cleanup(RERR_UNSUPPORTED);
655         }
656 #endif
657
658         if (am_server) {
659                 start_server(STDIN_FILENO, STDOUT_FILENO, argc, argv);
660         }
661
662         return start_client(argc, argv);
663 }
664