Add --no-whole-file and --no-blocking-io options
[rsync/rsync.git] / main.c
1 /* -*- c-file-style: "linux" -*-
2    
3    Copyright (C) 1996-2001 by Andrew Tridgell <tridge@samba.org>
4    Copyright (C) Paul Mackerras 1996
5    Copyright (C) 2001, 2002 by Martin Pool <mbp@samba.org>
6    
7    This program is free software; you can redistribute it and/or modify
8    it under the terms of the GNU General Public License as published by
9    the Free Software Foundation; either version 2 of the License, or
10    (at your option) any later version.
11    
12    This program is distributed in the hope that it will be useful,
13    but WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15    GNU General Public License for more details.
16    
17    You should have received a copy of the GNU General Public License
18    along with this program; if not, write to the Free Software
19    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20 */
21
22 #include "rsync.h"
23
24 time_t starttime = 0;
25
26 struct stats stats;
27
28 extern int verbose;
29
30 static void show_malloc_stats(void);
31
32 /****************************************************************************
33 wait for a process to exit, calling io_flush while waiting
34 ****************************************************************************/
35 void wait_process(pid_t pid, int *status)
36 {
37         while (waitpid(pid, status, WNOHANG) == 0) {
38                 msleep(20);
39                 io_flush();
40         }
41         
42         /* TODO: If the child exited on a signal, then log an
43          * appropriate error message.  Perhaps we should also accept a
44          * message describing the purpose of the child.  Also indicate
45          * this to the caller so that thhey know something went
46          * wrong.  */
47         *status = WEXITSTATUS(*status);
48 }
49
50 static void report(int f)
51 {
52         time_t t = time(NULL);
53         extern int am_server;
54         extern int am_sender;
55         extern int am_daemon;
56         extern int do_stats;
57         extern int remote_version;
58         int send_stats;
59
60         if (do_stats) {
61                 /* These come out from every process */
62                 show_malloc_stats();
63                 show_flist_stats();
64         }
65
66         if (am_daemon) {
67                 log_exit(0, __FILE__, __LINE__);
68                 if (f == -1 || !am_sender) return;
69         }
70
71         send_stats = verbose || (remote_version >= 20);
72         if (am_server) {
73                 if (am_sender && send_stats) {
74                         int64 w;
75                         /* store total_written in a temporary
76                             because write_longint changes it */
77                         w = stats.total_written;
78                         write_longint(f,stats.total_read);
79                         write_longint(f,w);
80                         write_longint(f,stats.total_size);
81                 }
82                 return;
83         }
84
85         /* this is the client */
86             
87         if (!am_sender && send_stats) {
88                 int64 r;
89                 stats.total_written = read_longint(f);
90                 /* store total_read in a temporary, read_longint changes it */
91                 r = read_longint(f);
92                 stats.total_size = read_longint(f);
93                 stats.total_read = r;
94         }
95
96         if (do_stats) {
97                 if (!am_sender && !send_stats) {
98                     /* missing the bytes written by the generator */
99                     rprintf(FINFO, "\nCannot show stats as receiver because remote protocol version is less than 20\n");
100                     rprintf(FINFO, "Use --stats -v to show stats\n");
101                     return;
102                 }
103                 rprintf(FINFO,"\nNumber of files: %d\n", stats.num_files);
104                 rprintf(FINFO,"Number of files transferred: %d\n", 
105                        stats.num_transferred_files);
106                 rprintf(FINFO,"Total file size: %.0f bytes\n", 
107                        (double)stats.total_size);
108                 rprintf(FINFO,"Total transferred file size: %.0f bytes\n", 
109                        (double)stats.total_transferred_size);
110                 rprintf(FINFO,"Literal data: %.0f bytes\n", 
111                        (double)stats.literal_data);
112                 rprintf(FINFO,"Matched data: %.0f bytes\n", 
113                        (double)stats.matched_data);
114                 rprintf(FINFO,"File list size: %d\n", stats.flist_size);
115                 rprintf(FINFO,"Total bytes written: %.0f\n", 
116                        (double)stats.total_written);
117                 rprintf(FINFO,"Total bytes read: %.0f\n\n", 
118                        (double)stats.total_read);
119         }
120         
121         if (verbose || do_stats) {
122                 rprintf(FINFO,"wrote %.0f bytes  read %.0f bytes  %.2f bytes/sec\n",
123                        (double)stats.total_written,
124                        (double)stats.total_read,
125                        (stats.total_written+stats.total_read)/(0.5 + (t-starttime)));
126                 rprintf(FINFO,"total size is %.0f  speedup is %.2f\n",
127                        (double)stats.total_size,
128                        (1.0*stats.total_size)/(stats.total_written+stats.total_read));
129         }
130
131         fflush(stdout);
132         fflush(stderr);
133 }
134
135
136 /**
137  * If our C library can get malloc statistics, then show them to FINFO
138  **/
139 static void show_malloc_stats(void)
140 {
141 #ifdef HAVE_MALLINFO
142         struct mallinfo mi;
143         extern int am_server;
144         extern int am_sender;
145         extern int am_daemon;
146
147         mi = mallinfo();
148
149         rprintf(FINFO, RSYNC_NAME "[%d] (%s%s%s) heap statistics:\n",
150                 getpid(),
151                 am_server ? "server " : "",
152                 am_daemon ? "daemon " : "",
153                 am_sender ? "sender" : "receiver");
154         rprintf(FINFO, "  arena:     %10d   (bytes from sbrk)\n", mi.arena);
155         rprintf(FINFO, "  ordblks:   %10d   (chunks not in use)\n", mi.ordblks);
156         rprintf(FINFO, "  smblks:    %10d\n", mi.smblks);
157         rprintf(FINFO, "  hblks:     %10d   (chunks from mmap)\n", mi.hblks);
158         rprintf(FINFO, "  hblkhd:    %10d   (bytes from mmap)\n", mi.hblkhd);
159         rprintf(FINFO, "  usmblks:   %10d\n", mi.usmblks);
160         rprintf(FINFO, "  fsmblks:   %10d\n", mi.fsmblks);
161         rprintf(FINFO, "  uordblks:  %10d   (bytes used)\n", mi.uordblks);
162         rprintf(FINFO, "  fordblks:  %10d   (bytes free)\n", mi.fordblks);
163         rprintf(FINFO, "  keepcost:  %10d   (bytes in releasable chunk)\n", mi.keepcost);
164 #endif /* HAVE_MALLINFO */
165 }
166
167
168 /* Start the remote shell.   cmd may be NULL to use the default. */
169 static pid_t do_cmd(char *cmd,char *machine,char *user,char *path,int *f_in,int *f_out)
170 {
171         char *args[100];
172         int i,argc=0;
173         pid_t ret;
174         char *tok,*dir=NULL;
175         extern int local_server;
176         extern char *rsync_path;
177         extern int blocking_io;
178         extern int read_batch;
179
180         if (!read_batch && !local_server) { /* dw -- added read_batch */
181                 if (!cmd)
182                         cmd = getenv(RSYNC_RSH_ENV);
183                 if (!cmd)
184                         cmd = RSYNC_RSH;
185                 cmd = strdup(cmd);
186                 if (!cmd) 
187                         goto oom;
188
189                 for (tok=strtok(cmd," ");tok;tok=strtok(NULL," ")) {
190                         args[argc++] = tok;
191                 }
192
193 #if HAVE_REMSH
194                 /* remsh (on HPUX) takes the arguments the other way around */
195                 args[argc++] = machine;
196                 if (user) {
197                         args[argc++] = "-l";
198                         args[argc++] = user;
199                 }
200 #else
201                 if (user) {
202                         args[argc++] = "-l";
203                         args[argc++] = user;
204                 }
205                 args[argc++] = machine;
206 #endif
207
208                 args[argc++] = rsync_path;
209
210                 if ((blocking_io == -1) && (strcmp(cmd, RSYNC_RSH) == 0))
211                         blocking_io = 1;
212
213                 server_options(args,&argc);
214
215         }
216
217         args[argc++] = ".";
218
219         if (path && *path) 
220                 args[argc++] = path;
221
222         args[argc] = NULL;
223
224         if (verbose > 3) {
225                 rprintf(FINFO,"cmd=");
226                 for (i=0;i<argc;i++)
227                         rprintf(FINFO,"%s ",args[i]);
228                 rprintf(FINFO,"\n");
229         }
230
231         if (local_server) {
232                 if (read_batch)
233                     create_flist_from_batch();
234                 ret = local_child(argc, args, f_in, f_out);
235         } else {
236                 ret = piped_child(args,f_in,f_out);
237         }
238
239         if (dir) free(dir);
240
241         return ret;
242
243 oom:
244         out_of_memory("do_cmd");
245         return 0; /* not reached */
246 }
247
248
249
250
251 static char *get_local_name(struct file_list *flist,char *name)
252 {
253         STRUCT_STAT st;
254         extern int orig_umask;
255
256         if (verbose > 2)
257                 rprintf(FINFO,"get_local_name count=%d %s\n", 
258                         flist->count, NS(name));
259
260         if (!name) 
261                 return NULL;
262
263         if (do_stat(name,&st) == 0) {
264                 if (S_ISDIR(st.st_mode)) {
265                         if (!push_dir(name, 0)) {
266                                 rprintf(FERROR,"push_dir %s : %s (1)\n",
267                                         name,strerror(errno));
268                                 exit_cleanup(RERR_FILESELECT);
269                         }
270                         return NULL;
271                 }
272                 if (flist->count > 1) {
273                         rprintf(FERROR,"ERROR: destination must be a directory when copying more than 1 file\n");
274                         exit_cleanup(RERR_FILESELECT);
275                 }
276                 return name;
277         }
278
279         if (flist->count <= 1)
280                 return name;
281
282         if (do_mkdir(name,0777 & ~orig_umask) != 0) {
283                 rprintf(FERROR,"mkdir %s : %s (1)\n",name,strerror(errno));
284                 exit_cleanup(RERR_FILEIO);
285         } else {
286                 if (verbose > 0)
287                         rprintf(FINFO,"created directory %s\n",name);
288         }
289
290         if (!push_dir(name, 0)) {
291                 rprintf(FERROR,"push_dir %s : %s (2)\n",
292                         name,strerror(errno));
293                 exit_cleanup(RERR_FILESELECT);
294         }
295
296         return NULL;
297 }
298
299
300
301
302 static void do_server_sender(int f_in, int f_out, int argc,char *argv[])
303 {
304         int i;
305         struct file_list *flist;
306         char *dir = argv[0];
307         extern int relative_paths;
308         extern int recurse;
309         extern int remote_version;
310
311         if (verbose > 2)
312                 rprintf(FINFO,"server_sender starting pid=%d\n",(int)getpid());
313   
314         if (!relative_paths && !push_dir(dir, 0)) {
315                 rprintf(FERROR,"push_dir %s: %s (3)\n",dir,strerror(errno));
316                 exit_cleanup(RERR_FILESELECT);
317         }
318         argc--;
319         argv++;
320   
321         if (strcmp(dir,".")) {
322                 int l = strlen(dir);
323                 if (strcmp(dir,"/") == 0) 
324                         l = 0;
325                 for (i=0;i<argc;i++)
326                         argv[i] += l+1;
327         }
328
329         if (argc == 0 && recurse) {
330                 argc=1;
331                 argv--;
332                 argv[0] = ".";
333         }
334         
335         flist = send_file_list(f_out,argc,argv);
336         if (!flist || flist->count == 0) {
337                 exit_cleanup(0);
338         }
339
340         send_files(flist,f_out,f_in);
341         io_flush();
342         report(f_out);
343         if (remote_version >= 24) {
344                 /* final goodbye message */             
345                 read_int(f_in);
346         }
347         io_flush();
348         exit_cleanup(0);
349 }
350
351
352 static int do_recv(int f_in,int f_out,struct file_list *flist,char *local_name)
353 {
354         int pid;
355         int status=0;
356         int recv_pipe[2];
357         int error_pipe[2];
358         extern int preserve_hard_links;
359         extern int delete_after;
360         extern int recurse;
361         extern int delete_mode;
362         extern int remote_version;
363
364         if (preserve_hard_links)
365                 init_hard_links(flist);
366
367         if (!delete_after) {
368                 /* I moved this here from recv_files() to prevent a race condition */
369                 if (recurse && delete_mode && !local_name && flist->count>0) {
370                         delete_files(flist);
371                 }
372         }
373
374         if (fd_pair(recv_pipe) < 0) {
375                 rprintf(FERROR,"pipe failed in do_recv\n");
376                 exit_cleanup(RERR_SOCKETIO);
377         }
378
379         if (fd_pair(error_pipe) < 0) {
380                 rprintf(FERROR,"error pipe failed in do_recv\n");
381                 exit_cleanup(RERR_SOCKETIO);
382         }
383   
384         io_flush();
385
386         if ((pid=do_fork()) == 0) {
387                 close(recv_pipe[0]);
388                 close(error_pipe[0]);
389                 if (f_in != f_out) close(f_out);
390
391                 /* we can't let two processes write to the socket at one time */
392                 io_multiplexing_close();
393
394                 /* set place to send errors */
395                 set_error_fd(error_pipe[1]);
396
397                 recv_files(f_in,flist,local_name,recv_pipe[1]);
398                 io_flush();
399                 report(f_in);
400
401                 write_int(recv_pipe[1],1);
402                 close(recv_pipe[1]);
403                 io_flush();
404                 /* finally we go to sleep until our parent kills us
405                    with a USR2 signal. We sleep for a short time as on
406                    some OSes a signal won't interrupt a sleep! */
407                 while (msleep(20))
408                         ;
409         }
410
411         close(recv_pipe[1]);
412         close(error_pipe[1]);
413         if (f_in != f_out) close(f_in);
414
415         io_start_buffering(f_out);
416
417         io_set_error_fd(error_pipe[0]);
418
419         generate_files(f_out,flist,local_name,recv_pipe[0]);
420
421         read_int(recv_pipe[0]);
422         close(recv_pipe[0]);
423         if (remote_version >= 24) {
424                 /* send a final goodbye message */
425                 write_int(f_out, -1);
426         }
427         io_flush();
428
429         kill(pid, SIGUSR2);
430         wait_process(pid, &status);
431         return status;
432 }
433
434
435 static void do_server_recv(int f_in, int f_out, int argc,char *argv[])
436 {
437         int status;
438         struct file_list *flist;
439         char *local_name=NULL;
440         char *dir = NULL;
441         extern int delete_mode;
442         extern int delete_excluded;
443         extern int am_daemon;
444         extern int module_id;
445         extern int am_sender;
446         extern int read_batch;   /* dw */
447         extern struct file_list *batch_flist;  /* dw */
448
449         if (verbose > 2)
450                 rprintf(FINFO,"server_recv(%d) starting pid=%d\n",argc,(int)getpid());
451
452         if (am_daemon && lp_read_only(module_id) && !am_sender) {
453                 rprintf(FERROR,"ERROR: module is read only\n");
454                 exit_cleanup(RERR_SYNTAX);
455                 return;
456         }
457
458         
459         if (argc > 0) {
460                 dir = argv[0];
461                 argc--;
462                 argv++;
463                 if (!am_daemon && !push_dir(dir, 0)) {
464                         rprintf(FERROR,"push_dir %s : %s (4)\n",
465                                 dir,strerror(errno));
466                         exit_cleanup(RERR_FILESELECT);
467                 }    
468         }
469
470         if (delete_mode && !delete_excluded)
471                 recv_exclude_list(f_in);
472
473         if (read_batch) /*  dw  */
474             flist = batch_flist;
475         else
476             flist = recv_file_list(f_in);
477         if (!flist) {
478                 rprintf(FERROR,"server_recv: recv_file_list error\n");
479                 exit_cleanup(RERR_FILESELECT);
480         }
481         
482         if (argc > 0) {    
483                 if (strcmp(dir,".")) {
484                         argv[0] += strlen(dir);
485                         if (argv[0][0] == '/') argv[0]++;
486                 }
487                 local_name = get_local_name(flist,argv[0]);
488         }
489
490         status = do_recv(f_in,f_out,flist,local_name);
491         exit_cleanup(status);
492 }
493
494
495 void start_server(int f_in, int f_out, int argc, char *argv[])
496 {
497         extern int cvs_exclude;
498         extern int am_sender;
499         extern int remote_version;
500         extern int read_batch; /* dw */
501
502         setup_protocol(f_out, f_in);
503
504         set_nonblocking(f_in);
505         set_nonblocking(f_out);
506
507         if (remote_version >= 23)
508                 io_start_multiplex_out(f_out);
509
510         if (am_sender) {
511                 if (!read_batch) { /* dw */
512                     recv_exclude_list(f_in);
513                     if (cvs_exclude)
514                         add_cvs_excludes();
515                 }
516                 do_server_sender(f_in, f_out, argc, argv);
517         } else {
518                 do_server_recv(f_in, f_out, argc, argv);
519         }
520         exit_cleanup(0);
521 }
522
523
524 /*
525  * This is called once the connection has been negotiated.  It is used
526  * for rsyncd, remote-shell, and local connections.
527  */
528 int client_run(int f_in, int f_out, pid_t pid, int argc, char *argv[])
529 {
530         struct file_list *flist;
531         int status = 0, status2 = 0;
532         char *local_name = NULL;
533         extern int am_sender;
534         extern int remote_version;
535         extern pid_t cleanup_child_pid;
536         extern int write_batch; /* dw */
537         extern int read_batch; /* dw */
538         extern struct file_list *batch_flist; /*  dw */
539
540         cleanup_child_pid = pid;
541         if (read_batch)
542             flist = batch_flist;  /* dw */
543
544         set_nonblocking(f_in);
545         set_nonblocking(f_out);
546
547         setup_protocol(f_out,f_in);
548
549         if (remote_version >= 23)
550                 io_start_multiplex_in(f_in);
551         
552         if (am_sender) {
553                 extern int cvs_exclude;
554                 extern int delete_mode;
555                 extern int delete_excluded;
556                 if (cvs_exclude)
557                         add_cvs_excludes();
558                 if (delete_mode && !delete_excluded) 
559                         send_exclude_list(f_out);
560                 if (!read_batch) /*  dw -- don't write to pipe */
561                     flist = send_file_list(f_out,argc,argv);
562                 if (verbose > 3) 
563                         rprintf(FINFO,"file list sent\n");
564
565                 send_files(flist,f_out,f_in);
566                 if (remote_version >= 24) {
567                         /* final goodbye message */             
568                         read_int(f_in);
569                 }
570                 if (pid != -1) {
571                         if (verbose > 3)
572                                 rprintf(FINFO,"client_run waiting on %d\n", (int) pid);
573                         io_flush();
574                         wait_process(pid, &status);
575                 }
576                 report(-1);
577                 exit_cleanup(status);
578         }
579
580         if (argc == 0) {
581                 extern int list_only;
582                 list_only = 1;
583         }
584         
585         if (!write_batch) /* dw */
586             send_exclude_list(f_out);
587         
588         flist = recv_file_list(f_in);
589         if (!flist || flist->count == 0) {
590                 rprintf(FINFO, "client: nothing to do: "
591                         "perhaps you need to specify some filenames or "
592                         "the --recursive option?\n");
593                 exit_cleanup(0);
594         }
595         
596         local_name = get_local_name(flist,argv[0]);
597         
598         status2 = do_recv(f_in,f_out,flist,local_name);
599         
600         if (pid != -1) {
601                 if (verbose > 3)
602                         rprintf(FINFO,"client_run2 waiting on %d\n", (int) pid);
603                 io_flush();
604                 wait_process(pid, &status);
605         }
606         
607         return MAX(status, status2);
608 }
609
610 static char *find_colon(char *s)
611 {
612         char *p, *p2;
613
614         p = strchr(s,':');
615         if (!p) return NULL;
616         
617         /* now check to see if there is a / in the string before the : - if there is then
618            discard the colon on the assumption that the : is part of a filename */
619         p2 = strchr(s,'/');
620         if (p2 && p2 < p) return NULL;
621
622         return p;
623 }
624
625
626 static int copy_argv (char *argv[])
627 {
628         int i;
629
630         for (i = 0; argv[i]; i++) {
631                 if (!(argv[i] = strdup(argv[i]))) {
632                         rprintf (FERROR, "out of memory at %s(%d)\n",
633                                  __FILE__, __LINE__);
634                         return RERR_MALLOC;
635                 }
636         }
637
638         return 0;
639 }
640
641
642 /*
643  * Start a client for either type of remote connection.  Work out
644  * whether the arguments request a remote shell or rsyncd connection,
645  * and call the appropriate connection function, then run_client.
646  */
647 static int start_client(int argc, char *argv[])
648 {
649         char *p;
650         char *shell_machine = NULL;
651         char *shell_path = NULL;
652         char *shell_user = NULL;
653         int ret;
654         pid_t pid;
655         int f_in,f_out;
656         extern int local_server;
657         extern int am_sender;
658         extern char *shell_cmd;
659         extern int rsync_port;
660         extern int whole_file;
661         extern int read_batch;
662         int rc;
663
664         /* Don't clobber argv[] so that ps(1) can still show the right
665            command line. */
666         if ((rc = copy_argv (argv)))
667                 return rc;
668
669         if (strncasecmp(URL_PREFIX, argv[0], strlen(URL_PREFIX)) == 0) {
670                 char *host, *path;
671
672                 host = argv[0] + strlen(URL_PREFIX);
673                 p = strchr(host,'/');
674                 if (p) {
675                         *p = 0;
676                         path = p+1;
677                 } else {
678                         path="";
679                 }
680                 p = strchr(host,':');
681                 if (p) {
682                         rsync_port = atoi(p+1);
683                         *p = 0;
684                 }
685                 return start_socket_client(host, path, argc-1, argv+1);
686         }
687
688         if (!read_batch) { /* dw */
689             p = find_colon(argv[0]);
690
691         if (p) {
692                 if (p[1] == ':') {
693                         *p = 0;
694                         return start_socket_client(argv[0], p+2, argc-1, argv+1);
695                 }
696
697                 if (argc < 1) {
698                         usage(FERROR);
699                         exit_cleanup(RERR_SYNTAX);
700                 }
701
702                 am_sender = 0;
703                 *p = 0;
704                 shell_machine = argv[0];
705                 shell_path = p+1;
706                 argc--;
707                 argv++;
708         } else {
709                 am_sender = 1;
710
711                 p = find_colon(argv[argc-1]);
712                 if (!p) {
713                         local_server = 1;
714                         /* disable "rsync algorithm" when both sides local */
715                         if (whole_file == -1)
716                                 whole_file = 1;
717                 } else if (p[1] == ':') {
718                         *p = 0;
719                         return start_socket_client(argv[argc-1], p+2, argc-1, argv);
720                 }
721
722                 if (argc < 2) {
723                         usage(FERROR);
724                         exit_cleanup(RERR_SYNTAX);
725                 }
726                 
727                 if (local_server) {
728                         shell_machine = NULL;
729                         shell_path = argv[argc-1];
730                 } else {
731                         *p = 0;
732                         shell_machine = argv[argc-1];
733                         shell_path = p+1;
734                 }
735                 argc--;
736         }
737         } else {
738             am_sender = 1;  /*  dw */
739             local_server = 1;  /* dw */
740             shell_path = argv[argc-1];  /* dw */
741         }
742
743         if (shell_machine) {
744                 p = strchr(shell_machine,'@');
745                 if (p) {
746                         *p = 0;
747                         shell_user = shell_machine;
748                         shell_machine = p+1;
749                 }
750         }
751
752         if (verbose > 3) {
753                 rprintf(FINFO,"cmd=%s machine=%s user=%s path=%s\n",
754                         shell_cmd?shell_cmd:"",
755                         shell_machine?shell_machine:"",
756                         shell_user?shell_user:"",
757                         shell_path?shell_path:"");
758         }
759         
760         if (!am_sender && argc > 1) {
761                 usage(FERROR);
762                 exit_cleanup(RERR_SYNTAX);
763         }
764
765         if (argc == 0 && !am_sender) {
766                 extern int list_only;
767                 list_only = 1;
768         }
769         
770         pid = do_cmd(shell_cmd,shell_machine,shell_user,shell_path,&f_in,&f_out);
771         
772         ret = client_run(f_in, f_out, pid, argc, argv);
773
774         fflush(stdout);
775         fflush(stderr);
776
777         return ret;
778 }
779
780
781 static RETSIGTYPE sigusr1_handler(int val) {
782         exit_cleanup(RERR_SIGNAL);
783 }
784
785 static RETSIGTYPE sigusr2_handler(int val) {
786         extern int log_got_error;
787         if (log_got_error) _exit(RERR_PARTIAL);
788         _exit(0);
789 }
790
791 static RETSIGTYPE sigchld_handler(int val) {
792 #ifdef WNOHANG
793         while (waitpid(-1, NULL, WNOHANG) > 0) ;
794 #endif
795 }
796
797 int main(int argc,char *argv[])
798 {       
799         extern int am_root;
800         extern int orig_umask;
801         extern int dry_run;
802         extern int am_daemon;
803         extern int am_server;
804         int ret;
805         extern int read_batch;   /*  dw */
806         extern int write_batch;  /*  dw */
807         extern char *batch_ext;   /*  dw */
808         int orig_argc;  /* dw */
809         char **orig_argv;
810
811         orig_argc = argc;   /* dw */
812         orig_argv = argv;
813
814         signal(SIGUSR1, sigusr1_handler);
815         signal(SIGUSR2, sigusr2_handler);
816         signal(SIGCHLD, sigchld_handler);
817
818         starttime = time(NULL);
819         am_root = (getuid() == 0);
820
821         memset(&stats, 0, sizeof(stats));
822
823         if (argc < 2) {
824                 usage(FERROR);
825                 exit_cleanup(RERR_SYNTAX);
826         }
827
828         /* we set a 0 umask so that correct file permissions can be
829            carried across */
830         orig_umask = (int)umask(0);
831
832         if (!parse_arguments(&argc, (const char ***) &argv, 1)) {
833                 /* FIXME: We ought to call the same error-handling
834                  * code here, rather than relying on getopt. */
835                 option_error();
836                 exit_cleanup(RERR_SYNTAX);
837         }
838
839         signal(SIGINT,SIGNAL_CAST sig_int);
840         signal(SIGPIPE,SIGNAL_CAST sig_int);
841         signal(SIGHUP,SIGNAL_CAST sig_int);
842         signal(SIGTERM,SIGNAL_CAST sig_int);
843
844         /* Initialize push_dir here because on some old systems getcwd
845            (implemented by forking "pwd" and reading its output) doesn't
846            work when there are other child processes.  Also, on all systems
847            that implement getcwd that way "pwd" can't be found after chroot. */
848         push_dir(NULL,0);
849
850         if (write_batch) { /* dw */
851             create_batch_file_ext();
852             write_batch_argvs_file(orig_argc, orig_argv);
853         }
854
855         if (read_batch) { /* dw */
856             set_batch_file_ext(batch_ext);
857         }
858
859         if (am_daemon) {
860                 return daemon_main();
861         }
862
863         if (argc < 1) {
864                 usage(FERROR);
865                 exit_cleanup(RERR_SYNTAX);
866         }
867
868         if (dry_run)
869                 verbose = MAX(verbose,1);
870
871 #ifndef SUPPORT_LINKS
872         if (!am_server && preserve_links) {
873                 rprintf(FERROR,"ERROR: symbolic links not supported\n");
874                 exit_cleanup(RERR_UNSUPPORTED);
875         }
876 #endif
877
878         if (am_server) {
879                 set_nonblocking(STDIN_FILENO);
880                 set_nonblocking(STDOUT_FILENO);
881                 start_server(STDIN_FILENO, STDOUT_FILENO, argc, argv);
882         }
883
884         ret = start_client(argc, argv);
885         exit_cleanup(ret);
886         return ret;
887 }
888