Only print the command used to open connections with -vv, not just -v.
[rsync/rsync.git] / main.c
... / ...
CommitLineData
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
24time_t starttime = 0;
25
26struct stats stats;
27
28extern int verbose;
29
30static void show_malloc_stats(void);
31
32/****************************************************************************
33wait for a process to exit, calling io_flush while waiting
34****************************************************************************/
35void 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
50static 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 **/
139static 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. */
169static 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
242oom:
243 out_of_memory("do_cmd");
244 return 0; /* not reached */
245}
246
247
248
249
250static 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
301static 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
351static 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
434static 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
494void 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 */
527int 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
609static 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
625static 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 */
646static 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
779static RETSIGTYPE sigusr1_handler(int val) {
780 exit_cleanup(RERR_SIGNAL);
781}
782
783static RETSIGTYPE sigusr2_handler(int val) {
784 extern int log_got_error;
785 if (log_got_error) _exit(RERR_PARTIAL);
786 _exit(0);
787}
788
789static RETSIGTYPE sigchld_handler(int val) {
790#ifdef WNOHANG
791 while (waitpid(-1, NULL, WNOHANG) > 0) ;
792#endif
793}
794
795int 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