Update copyright
[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 (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 **/
134static 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. */
157static 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
230oom:
231 out_of_memory("do_cmd");
232 return 0; /* not reached */
233}
234
235
236
237
238static 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
289static 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
339static 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
422static 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
482void 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 */
515int 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
597static 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
613static 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 */
634static 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
767static RETSIGTYPE sigusr1_handler(int val) {
768 exit_cleanup(RERR_SIGNAL);
769}
770
771static RETSIGTYPE sigusr2_handler(int val) {
772 extern int log_got_error;
773 if (log_got_error) _exit(RERR_PARTIAL);
774 _exit(0);
775}
776
777static RETSIGTYPE sigchld_handler(int val) {
778#ifdef WNOHANG
779 while (waitpid(-1, NULL, WNOHANG) > 0) ;
780#endif
781}
782
783int 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