Factor out code to grow the file list into a common location.
[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 }
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 **/
138static 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. */
168static 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
241oom:
242 out_of_memory("do_cmd");
243 return 0; /* not reached */
244}
245
246
247
248
249static 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
300static 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
350static 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
433static 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
493void 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 */
526int 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
608static 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
624static 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 */
645static 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
778static RETSIGTYPE sigusr1_handler(int val) {
779 exit_cleanup(RERR_SIGNAL);
780}
781
782static RETSIGTYPE sigusr2_handler(int val) {
783 extern int log_got_error;
784 if (log_got_error) _exit(RERR_PARTIAL);
785 _exit(0);
786}
787
788static RETSIGTYPE sigchld_handler(int val) {
789#ifdef WNOHANG
790 while (waitpid(-1, NULL, WNOHANG) > 0) ;
791#endif
792}
793
794int 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 char **orig_argv;
807
808 orig_argc = argc; /* dw */
809 orig_argv = argv;
810
811 signal(SIGUSR1, sigusr1_handler);
812 signal(SIGUSR2, sigusr2_handler);
813 signal(SIGCHLD, sigchld_handler);
814
815 starttime = time(NULL);
816 am_root = (getuid() == 0);
817
818 memset(&stats, 0, sizeof(stats));
819
820 if (argc < 2) {
821 usage(FERROR);
822 exit_cleanup(RERR_SYNTAX);
823 }
824
825 /* we set a 0 umask so that correct file permissions can be
826 carried across */
827 orig_umask = (int)umask(0);
828
829 if (!parse_arguments(&argc, (const char ***) &argv, 1)) {
830 /* FIXME: We ought to call the same error-handling
831 * code here, rather than relying on getopt. */
832 option_error();
833 exit_cleanup(RERR_SYNTAX);
834 }
835
836 signal(SIGINT,SIGNAL_CAST sig_int);
837 signal(SIGPIPE,SIGNAL_CAST sig_int);
838 signal(SIGHUP,SIGNAL_CAST sig_int);
839 signal(SIGTERM,SIGNAL_CAST sig_int);
840
841 /* Initialize push_dir here because on some old systems getcwd
842 (implemented by forking "pwd" and reading its output) doesn't
843 work when there are other child processes. Also, on all systems
844 that implement getcwd that way "pwd" can't be found after chroot. */
845 push_dir(NULL,0);
846
847 if (write_batch) { /* dw */
848 create_batch_file_ext();
849 write_batch_argvs_file(orig_argc, orig_argv);
850 }
851
852 if (read_batch) { /* dw */
853 set_batch_file_ext(batch_ext);
854 }
855
856 if (am_daemon) {
857 return daemon_main();
858 }
859
860 if (argc < 1) {
861 usage(FERROR);
862 exit_cleanup(RERR_SYNTAX);
863 }
864
865 if (dry_run)
866 verbose = MAX(verbose,1);
867
868#ifndef SUPPORT_LINKS
869 if (!am_server && preserve_links) {
870 rprintf(FERROR,"ERROR: symbolic links not supported\n");
871 exit_cleanup(RERR_UNSUPPORTED);
872 }
873#endif
874
875 if (am_server) {
876 set_nonblocking(STDIN_FILENO);
877 set_nonblocking(STDOUT_FILENO);
878 start_server(STDIN_FILENO, STDOUT_FILENO, argc, argv);
879 }
880
881 ret = start_client(argc, argv);
882 exit_cleanup(ret);
883 return ret;
884}
885