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