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