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