the signed/unsigned change seems to have caused a logic bug on some
[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                 server_options(args,&argc);
211
212
213                 if (strcmp(cmd, RSYNC_RSH) == 0) blocking_io = 1;
214         }
215
216         args[argc++] = ".";
217
218         if (path && *path) 
219                 args[argc++] = path;
220
221         args[argc] = NULL;
222
223         if (verbose > 3) {
224                 rprintf(FINFO,"cmd=");
225                 for (i=0;i<argc;i++)
226                         rprintf(FINFO,"%s ",args[i]);
227                 rprintf(FINFO,"\n");
228         }
229
230         if (local_server) {
231                 if (read_batch)
232                     create_flist_from_batch();
233                 ret = local_child(argc, args, f_in, f_out);
234         } else {
235                 ret = piped_child(args,f_in,f_out);
236         }
237
238         if (dir) free(dir);
239
240         return ret;
241
242 oom:
243         out_of_memory("do_cmd");
244         return 0; /* not reached */
245 }
246
247
248
249
250 static char *get_local_name(struct file_list *flist,char *name)
251 {
252         STRUCT_STAT st;
253         extern int orig_umask;
254
255         if (verbose > 2)
256                 rprintf(FINFO,"get_local_name count=%d %s\n", 
257                         flist->count, NS(name));
258
259         if (!name) 
260                 return NULL;
261
262         if (do_stat(name,&st) == 0) {
263                 if (S_ISDIR(st.st_mode)) {
264                         if (!push_dir(name, 0)) {
265                                 rprintf(FERROR,"push_dir %s : %s (1)\n",
266                                         name,strerror(errno));
267                                 exit_cleanup(RERR_FILESELECT);
268                         }
269                         return NULL;
270                 }
271                 if (flist->count > 1) {
272                         rprintf(FERROR,"ERROR: destination must be a directory when copying more than 1 file\n");
273                         exit_cleanup(RERR_FILESELECT);
274                 }
275                 return name;
276         }
277
278         if (flist->count <= 1)
279                 return name;
280
281         if (do_mkdir(name,0777 & ~orig_umask) != 0) {
282                 rprintf(FERROR,"mkdir %s : %s (1)\n",name,strerror(errno));
283                 exit_cleanup(RERR_FILEIO);
284         } else {
285                 if (verbose > 0)
286                         rprintf(FINFO,"created directory %s\n",name);
287         }
288
289         if (!push_dir(name, 0)) {
290                 rprintf(FERROR,"push_dir %s : %s (2)\n",
291                         name,strerror(errno));
292                 exit_cleanup(RERR_FILESELECT);
293         }
294
295         return NULL;
296 }
297
298
299
300
301 static void do_server_sender(int f_in, int f_out, int argc,char *argv[])
302 {
303         int i;
304         struct file_list *flist;
305         char *dir = argv[0];
306         extern int relative_paths;
307         extern int recurse;
308         extern int remote_version;
309
310         if (verbose > 2)
311                 rprintf(FINFO,"server_sender starting pid=%d\n",(int)getpid());
312   
313         if (!relative_paths && !push_dir(dir, 0)) {
314                 rprintf(FERROR,"push_dir %s: %s (3)\n",dir,strerror(errno));
315                 exit_cleanup(RERR_FILESELECT);
316         }
317         argc--;
318         argv++;
319   
320         if (strcmp(dir,".")) {
321                 int l = strlen(dir);
322                 if (strcmp(dir,"/") == 0) 
323                         l = 0;
324                 for (i=0;i<argc;i++)
325                         argv[i] += l+1;
326         }
327
328         if (argc == 0 && recurse) {
329                 argc=1;
330                 argv--;
331                 argv[0] = ".";
332         }
333         
334         flist = send_file_list(f_out,argc,argv);
335         if (!flist || flist->count == 0) {
336                 exit_cleanup(0);
337         }
338
339         send_files(flist,f_out,f_in);
340         io_flush();
341         report(f_out);
342         if (remote_version >= 24) {
343                 /* final goodbye message */             
344                 read_int(f_in);
345         }
346         io_flush();
347         exit_cleanup(0);
348 }
349
350
351 static int do_recv(int f_in,int f_out,struct file_list *flist,char *local_name)
352 {
353         int pid;
354         int status=0;
355         int recv_pipe[2];
356         int error_pipe[2];
357         extern int preserve_hard_links;
358         extern int delete_after;
359         extern int recurse;
360         extern int delete_mode;
361         extern int remote_version;
362
363         if (preserve_hard_links)
364                 init_hard_links(flist);
365
366         if (!delete_after) {
367                 /* I moved this here from recv_files() to prevent a race condition */
368                 if (recurse && delete_mode && !local_name && flist->count>0) {
369                         delete_files(flist);
370                 }
371         }
372
373         if (fd_pair(recv_pipe) < 0) {
374                 rprintf(FERROR,"pipe failed in do_recv\n");
375                 exit_cleanup(RERR_SOCKETIO);
376         }
377
378         if (fd_pair(error_pipe) < 0) {
379                 rprintf(FERROR,"error pipe failed in do_recv\n");
380                 exit_cleanup(RERR_SOCKETIO);
381         }
382   
383         io_flush();
384
385         if ((pid=do_fork()) == 0) {
386                 close(recv_pipe[0]);
387                 close(error_pipe[0]);
388                 if (f_in != f_out) close(f_out);
389
390                 /* we can't let two processes write to the socket at one time */
391                 io_multiplexing_close();
392
393                 /* set place to send errors */
394                 set_error_fd(error_pipe[1]);
395
396                 recv_files(f_in,flist,local_name,recv_pipe[1]);
397                 io_flush();
398                 report(f_in);
399
400                 write_int(recv_pipe[1],1);
401                 close(recv_pipe[1]);
402                 io_flush();
403                 /* finally we go to sleep until our parent kills us
404                    with a USR2 signal. We sleep for a short time as on
405                    some OSes a signal won't interrupt a sleep! */
406                 while (msleep(20))
407                         ;
408         }
409
410         close(recv_pipe[1]);
411         close(error_pipe[1]);
412         if (f_in != f_out) close(f_in);
413
414         io_start_buffering(f_out);
415
416         io_set_error_fd(error_pipe[0]);
417
418         generate_files(f_out,flist,local_name,recv_pipe[0]);
419
420         read_int(recv_pipe[0]);
421         close(recv_pipe[0]);
422         if (remote_version >= 24) {
423                 /* send a final goodbye message */
424                 write_int(f_out, -1);
425         }
426         io_flush();
427
428         kill(pid, SIGUSR2);
429         wait_process(pid, &status);
430         return status;
431 }
432
433
434 static void do_server_recv(int f_in, int f_out, int argc,char *argv[])
435 {
436         int status;
437         struct file_list *flist;
438         char *local_name=NULL;
439         char *dir = NULL;
440         extern int delete_mode;
441         extern int delete_excluded;
442         extern int am_daemon;
443         extern int module_id;
444         extern int am_sender;
445         extern int read_batch;   /* dw */
446         extern struct file_list *batch_flist;  /* dw */
447
448         if (verbose > 2)
449                 rprintf(FINFO,"server_recv(%d) starting pid=%d\n",argc,(int)getpid());
450
451         if (am_daemon && lp_read_only(module_id) && !am_sender) {
452                 rprintf(FERROR,"ERROR: module is read only\n");
453                 exit_cleanup(RERR_SYNTAX);
454                 return;
455         }
456
457         
458         if (argc > 0) {
459                 dir = argv[0];
460                 argc--;
461                 argv++;
462                 if (!am_daemon && !push_dir(dir, 0)) {
463                         rprintf(FERROR,"push_dir %s : %s (4)\n",
464                                 dir,strerror(errno));
465                         exit_cleanup(RERR_FILESELECT);
466                 }    
467         }
468
469         if (delete_mode && !delete_excluded)
470                 recv_exclude_list(f_in);
471
472         if (read_batch) /*  dw  */
473             flist = batch_flist;
474         else
475             flist = recv_file_list(f_in);
476         if (!flist) {
477                 rprintf(FERROR,"server_recv: recv_file_list error\n");
478                 exit_cleanup(RERR_FILESELECT);
479         }
480         
481         if (argc > 0) {    
482                 if (strcmp(dir,".")) {
483                         argv[0] += strlen(dir);
484                         if (argv[0][0] == '/') argv[0]++;
485                 }
486                 local_name = get_local_name(flist,argv[0]);
487         }
488
489         status = do_recv(f_in,f_out,flist,local_name);
490         exit_cleanup(status);
491 }
492
493
494 void start_server(int f_in, int f_out, int argc, char *argv[])
495 {
496         extern int cvs_exclude;
497         extern int am_sender;
498         extern int remote_version;
499         extern int read_batch; /* dw */
500
501         setup_protocol(f_out, f_in);
502
503         set_nonblocking(f_in);
504         set_nonblocking(f_out);
505
506         if (remote_version >= 23)
507                 io_start_multiplex_out(f_out);
508
509         if (am_sender) {
510                 if (!read_batch) { /* dw */
511                     recv_exclude_list(f_in);
512                     if (cvs_exclude)
513                         add_cvs_excludes();
514                 }
515                 do_server_sender(f_in, f_out, argc, argv);
516         } else {
517                 do_server_recv(f_in, f_out, argc, argv);
518         }
519         exit_cleanup(0);
520 }
521
522
523 /*
524  * This is called once the connection has been negotiated.  It is used
525  * for rsyncd, remote-shell, and local connections.
526  */
527 int client_run(int f_in, int f_out, pid_t pid, int argc, char *argv[])
528 {
529         struct file_list *flist;
530         int status = 0, status2 = 0;
531         char *local_name = NULL;
532         extern int am_sender;
533         extern int remote_version;
534         extern pid_t cleanup_child_pid;
535         extern int write_batch; /* dw */
536         extern int read_batch; /* dw */
537         extern struct file_list *batch_flist; /*  dw */
538
539         cleanup_child_pid = pid;
540         if (read_batch)
541             flist = batch_flist;  /* dw */
542
543         set_nonblocking(f_in);
544         set_nonblocking(f_out);
545
546         setup_protocol(f_out,f_in);
547
548         if (remote_version >= 23)
549                 io_start_multiplex_in(f_in);
550         
551         if (am_sender) {
552                 extern int cvs_exclude;
553                 extern int delete_mode;
554                 extern int delete_excluded;
555                 if (cvs_exclude)
556                         add_cvs_excludes();
557                 if (delete_mode && !delete_excluded) 
558                         send_exclude_list(f_out);
559                 if (!read_batch) /*  dw -- don't write to pipe */
560                     flist = send_file_list(f_out,argc,argv);
561                 if (verbose > 3) 
562                         rprintf(FINFO,"file list sent\n");
563
564                 send_files(flist,f_out,f_in);
565                 if (remote_version >= 24) {
566                         /* final goodbye message */             
567                         read_int(f_in);
568                 }
569                 if (pid != -1) {
570                         if (verbose > 3)
571                                 rprintf(FINFO,"client_run waiting on %d\n", (int) pid);
572                         io_flush();
573                         wait_process(pid, &status);
574                 }
575                 report(-1);
576                 exit_cleanup(status);
577         }
578
579         if (argc == 0) {
580                 extern int list_only;
581                 list_only = 1;
582         }
583         
584         if (!write_batch) /* dw */
585             send_exclude_list(f_out);
586         
587         flist = recv_file_list(f_in);
588         if (!flist || flist->count == 0) {
589                 rprintf(FINFO, "client: nothing to do: "
590                         "perhaps you need to specify some filenames or "
591                         "the --recursive option?\n");
592                 exit_cleanup(0);
593         }
594         
595         local_name = get_local_name(flist,argv[0]);
596         
597         status2 = do_recv(f_in,f_out,flist,local_name);
598         
599         if (pid != -1) {
600                 if (verbose > 3)
601                         rprintf(FINFO,"client_run2 waiting on %d\n", (int) pid);
602                 io_flush();
603                 wait_process(pid, &status);
604         }
605         
606         return MAX(status, status2);
607 }
608
609 static char *find_colon(char *s)
610 {
611         char *p, *p2;
612
613         p = strchr(s,':');
614         if (!p) return NULL;
615         
616         /* now check to see if there is a / in the string before the : - if there is then
617            discard the colon on the assumption that the : is part of a filename */
618         p2 = strchr(s,'/');
619         if (p2 && p2 < p) return NULL;
620
621         return p;
622 }
623
624
625 static int copy_argv (char *argv[])
626 {
627         int i;
628
629         for (i = 0; argv[i]; i++) {
630                 if (!(argv[i] = strdup(argv[i]))) {
631                         rprintf (FERROR, "out of memory at %s(%d)\n",
632                                  __FILE__, __LINE__);
633                         return RERR_MALLOC;
634                 }
635         }
636
637         return 0;
638 }
639
640
641 /*
642  * Start a client for either type of remote connection.  Work out
643  * whether the arguments request a remote shell or rsyncd connection,
644  * and call the appropriate connection function, then run_client.
645  */
646 static int start_client(int argc, char *argv[])
647 {
648         char *p;
649         char *shell_machine = NULL;
650         char *shell_path = NULL;
651         char *shell_user = NULL;
652         int ret;
653         pid_t pid;
654         int f_in,f_out;
655         extern int local_server;
656         extern int am_sender;
657         extern char *shell_cmd;
658         extern int rsync_port;
659         extern int whole_file;
660         extern int read_batch;
661         int rc;
662
663         /* Don't clobber argv[] so that ps(1) can still show the right
664            command line. */
665         if ((rc = copy_argv (argv)))
666                 return rc;
667
668         if (strncasecmp(URL_PREFIX, argv[0], strlen(URL_PREFIX)) == 0) {
669                 char *host, *path;
670
671                 host = argv[0] + strlen(URL_PREFIX);
672                 p = strchr(host,'/');
673                 if (p) {
674                         *p = 0;
675                         path = p+1;
676                 } else {
677                         path="";
678                 }
679                 p = strchr(host,':');
680                 if (p) {
681                         rsync_port = atoi(p+1);
682                         *p = 0;
683                 }
684                 return start_socket_client(host, path, argc-1, argv+1);
685         }
686
687         if (!read_batch) { /* dw */
688             p = find_colon(argv[0]);
689
690         if (p) {
691                 if (p[1] == ':') {
692                         *p = 0;
693                         return start_socket_client(argv[0], p+2, argc-1, argv+1);
694                 }
695
696                 if (argc < 1) {
697                         usage(FERROR);
698                         exit_cleanup(RERR_SYNTAX);
699                 }
700
701                 am_sender = 0;
702                 *p = 0;
703                 shell_machine = argv[0];
704                 shell_path = p+1;
705                 argc--;
706                 argv++;
707         } else {
708                 am_sender = 1;
709
710                 p = find_colon(argv[argc-1]);
711                 if (!p) {
712                         local_server = 1;
713                         /* disable "rsync algorithm" when both sides local */
714                         whole_file = 1;
715                 } else if (p[1] == ':') {
716                         *p = 0;
717                         return start_socket_client(argv[argc-1], p+2, argc-1, argv);
718                 }
719
720                 if (argc < 2) {
721                         usage(FERROR);
722                         exit_cleanup(RERR_SYNTAX);
723                 }
724                 
725                 if (local_server) {
726                         shell_machine = NULL;
727                         shell_path = argv[argc-1];
728                 } else {
729                         *p = 0;
730                         shell_machine = argv[argc-1];
731                         shell_path = p+1;
732                 }
733                 argc--;
734         }
735         } else {
736             am_sender = 1;  /*  dw */
737             local_server = 1;  /* dw */
738             shell_path = argv[argc-1];  /* dw */
739         }
740
741         if (shell_machine) {
742                 p = strchr(shell_machine,'@');
743                 if (p) {
744                         *p = 0;
745                         shell_user = shell_machine;
746                         shell_machine = p+1;
747                 }
748         }
749
750         if (verbose > 3) {
751                 rprintf(FINFO,"cmd=%s machine=%s user=%s path=%s\n",
752                         shell_cmd?shell_cmd:"",
753                         shell_machine?shell_machine:"",
754                         shell_user?shell_user:"",
755                         shell_path?shell_path:"");
756         }
757         
758         if (!am_sender && argc > 1) {
759                 usage(FERROR);
760                 exit_cleanup(RERR_SYNTAX);
761         }
762
763         if (argc == 0 && !am_sender) {
764                 extern int list_only;
765                 list_only = 1;
766         }
767         
768         pid = do_cmd(shell_cmd,shell_machine,shell_user,shell_path,&f_in,&f_out);
769         
770         ret = client_run(f_in, f_out, pid, argc, argv);
771
772         fflush(stdout);
773         fflush(stderr);
774
775         return ret;
776 }
777
778
779 static RETSIGTYPE sigusr1_handler(int val) {
780         exit_cleanup(RERR_SIGNAL);
781 }
782
783 static RETSIGTYPE sigusr2_handler(int val) {
784         extern int log_got_error;
785         if (log_got_error) _exit(RERR_PARTIAL);
786         _exit(0);
787 }
788
789 static RETSIGTYPE sigchld_handler(int val) {
790 #ifdef WNOHANG
791         while (waitpid(-1, NULL, WNOHANG) > 0) ;
792 #endif
793 }
794
795 int main(int argc,char *argv[])
796 {       
797         extern int am_root;
798         extern int orig_umask;
799         extern int dry_run;
800         extern int am_daemon;
801         extern int am_server;
802         int ret;
803         extern int read_batch;   /*  dw */
804         extern int write_batch;  /*  dw */
805         extern char *batch_ext;   /*  dw */
806         int orig_argc;  /* dw */
807         char **orig_argv;
808
809         orig_argc = argc;   /* dw */
810         orig_argv = argv;
811
812         signal(SIGUSR1, sigusr1_handler);
813         signal(SIGUSR2, sigusr2_handler);
814         signal(SIGCHLD, sigchld_handler);
815
816         starttime = time(NULL);
817         am_root = (getuid() == 0);
818
819         memset(&stats, 0, sizeof(stats));
820
821         if (argc < 2) {
822                 usage(FERROR);
823                 exit_cleanup(RERR_SYNTAX);
824         }
825
826         /* we set a 0 umask so that correct file permissions can be
827            carried across */
828         orig_umask = (int)umask(0);
829
830         if (!parse_arguments(&argc, (const char ***) &argv, 1)) {
831                 /* FIXME: We ought to call the same error-handling
832                  * code here, rather than relying on getopt. */
833                 option_error();
834                 exit_cleanup(RERR_SYNTAX);
835         }
836
837         signal(SIGINT,SIGNAL_CAST sig_int);
838         signal(SIGPIPE,SIGNAL_CAST sig_int);
839         signal(SIGHUP,SIGNAL_CAST sig_int);
840         signal(SIGTERM,SIGNAL_CAST sig_int);
841
842         /* Initialize push_dir here because on some old systems getcwd
843            (implemented by forking "pwd" and reading its output) doesn't
844            work when there are other child processes.  Also, on all systems
845            that implement getcwd that way "pwd" can't be found after chroot. */
846         push_dir(NULL,0);
847
848         if (write_batch) { /* dw */
849             create_batch_file_ext();
850             write_batch_argvs_file(orig_argc, orig_argv);
851         }
852
853         if (read_batch) { /* dw */
854             set_batch_file_ext(batch_ext);
855         }
856
857         if (am_daemon) {
858                 return daemon_main();
859         }
860
861         if (argc < 1) {
862                 usage(FERROR);
863                 exit_cleanup(RERR_SYNTAX);
864         }
865
866         if (dry_run)
867                 verbose = MAX(verbose,1);
868
869 #ifndef SUPPORT_LINKS
870         if (!am_server && preserve_links) {
871                 rprintf(FERROR,"ERROR: symbolic links not supported\n");
872                 exit_cleanup(RERR_UNSUPPORTED);
873         }
874 #endif
875
876         if (am_server) {
877                 set_nonblocking(STDIN_FILENO);
878                 set_nonblocking(STDOUT_FILENO);
879                 start_server(STDIN_FILENO, STDOUT_FILENO, argc, argv);
880         }
881
882         ret = start_client(argc, argv);
883         exit_cleanup(ret);
884         return ret;
885 }
886