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