Drop dead variables introduced in rsync+ patch.
[rsync/rsync.git] / main.c
1 /* -*- c-file-style: "linux" -*-
2    
3    Copyright (C) 1996-2001 by Andrew Tridgell <tridge@samba.org>
4    Copyright (C) Paul Mackerras 1996
5    
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
323         if (preserve_hard_links)
324                 init_hard_links(flist);
325
326         if (!delete_after) {
327                 /* I moved this here from recv_files() to prevent a race condition */
328                 if (recurse && delete_mode && !local_name && flist->count>0) {
329                         delete_files(flist);
330                 }
331         }
332
333         if (fd_pair(recv_pipe) < 0) {
334                 rprintf(FERROR,"pipe failed in do_recv\n");
335                 exit_cleanup(RERR_SOCKETIO);
336         }
337
338         if (fd_pair(error_pipe) < 0) {
339                 rprintf(FERROR,"error pipe failed in do_recv\n");
340                 exit_cleanup(RERR_SOCKETIO);
341         }
342   
343         io_flush();
344
345         if ((pid=do_fork()) == 0) {
346                 close(recv_pipe[0]);
347                 close(error_pipe[0]);
348                 if (f_in != f_out) close(f_out);
349
350                 /* we can't let two processes write to the socket at one time */
351                 io_multiplexing_close();
352
353                 /* set place to send errors */
354                 set_error_fd(error_pipe[1]);
355
356                 recv_files(f_in,flist,local_name,recv_pipe[1]);
357                 io_flush();
358                 report(f_in);
359
360                 write_int(recv_pipe[1],1);
361                 close(recv_pipe[1]);
362                 io_flush();
363                 /* finally we go to sleep until our parent kills us
364                    with a USR2 signal. We sleep for a short time as on
365                    some OSes a signal won't interrupt a sleep! */
366                 while (msleep(20))
367                         ;
368         }
369
370         close(recv_pipe[1]);
371         close(error_pipe[1]);
372         if (f_in != f_out) close(f_in);
373
374         io_start_buffering(f_out);
375
376         io_set_error_fd(error_pipe[0]);
377
378         generate_files(f_out,flist,local_name,recv_pipe[0]);
379
380         read_int(recv_pipe[0]);
381         close(recv_pipe[0]);
382         if (remote_version >= 24) {
383                 /* send a final goodbye message */
384                 write_int(f_out, -1);
385         }
386         io_flush();
387
388         kill(pid, SIGUSR2);
389         wait_process(pid, &status);
390         return status;
391 }
392
393
394 static void do_server_recv(int f_in, int f_out, int argc,char *argv[])
395 {
396         int status;
397         struct file_list *flist;
398         char *local_name=NULL;
399         char *dir = NULL;
400         extern int delete_mode;
401         extern int delete_excluded;
402         extern int am_daemon;
403         extern int module_id;
404         extern int am_sender;
405         extern int read_batch;   /* dw */
406         extern struct file_list *batch_flist;  /* dw */
407
408         if (verbose > 2)
409                 rprintf(FINFO,"server_recv(%d) starting pid=%d\n",argc,(int)getpid());
410
411         if (am_daemon && lp_read_only(module_id) && !am_sender) {
412                 rprintf(FERROR,"ERROR: module is read only\n");
413                 exit_cleanup(RERR_SYNTAX);
414                 return;
415         }
416
417         
418         if (argc > 0) {
419                 dir = argv[0];
420                 argc--;
421                 argv++;
422                 if (!am_daemon && !push_dir(dir, 0)) {
423                         rprintf(FERROR,"push_dir %s : %s (4)\n",
424                                 dir,strerror(errno));
425                         exit_cleanup(RERR_FILESELECT);
426                 }    
427         }
428
429         if (delete_mode && !delete_excluded)
430                 recv_exclude_list(f_in);
431
432         if (read_batch) /*  dw  */
433             flist = batch_flist;
434         else
435             flist = recv_file_list(f_in);
436         if (!flist) {
437                 rprintf(FERROR,"server_recv: recv_file_list error\n");
438                 exit_cleanup(RERR_FILESELECT);
439         }
440         
441         if (argc > 0) {    
442                 if (strcmp(dir,".")) {
443                         argv[0] += strlen(dir);
444                         if (argv[0][0] == '/') argv[0]++;
445                 }
446                 local_name = get_local_name(flist,argv[0]);
447         }
448
449         status = do_recv(f_in,f_out,flist,local_name);
450         exit_cleanup(status);
451 }
452
453
454 void start_server(int f_in, int f_out, int argc, char *argv[])
455 {
456         extern int cvs_exclude;
457         extern int am_sender;
458         extern int remote_version;
459         extern int read_batch; /* dw */
460
461         setup_protocol(f_out, f_in);
462
463         set_nonblocking(f_in);
464         set_nonblocking(f_out);
465
466         if (remote_version >= 23)
467                 io_start_multiplex_out(f_out);
468
469         if (am_sender) {
470                 if (!read_batch) { /* dw */
471                     recv_exclude_list(f_in);
472                     if (cvs_exclude)
473                         add_cvs_excludes();
474                 }
475                 do_server_sender(f_in, f_out, argc, argv);
476         } else {
477                 do_server_recv(f_in, f_out, argc, argv);
478         }
479         exit_cleanup(0);
480 }
481
482
483 /*
484  * This is called once the connection has been negotiated.  It is used
485  * for rsyncd, remote-shell, and local connections.
486  */
487 int client_run(int f_in, int f_out, pid_t pid, int argc, char *argv[])
488 {
489         struct file_list *flist;
490         int status = 0, status2 = 0;
491         char *local_name = NULL;
492         extern int am_sender;
493         extern int remote_version;
494         extern pid_t cleanup_child_pid;
495         extern int write_batch; /* dw */
496         extern int read_batch; /* dw */
497         extern struct file_list *batch_flist; /*  dw */
498
499         cleanup_child_pid = pid;
500         if (read_batch)
501             flist = batch_flist;  /* dw */
502
503         set_nonblocking(f_in);
504         set_nonblocking(f_out);
505
506         setup_protocol(f_out,f_in);
507
508         if (remote_version >= 23)
509                 io_start_multiplex_in(f_in);
510         
511         if (am_sender) {
512                 extern int cvs_exclude;
513                 extern int delete_mode;
514                 extern int delete_excluded;
515                 if (cvs_exclude)
516                         add_cvs_excludes();
517                 if (delete_mode && !delete_excluded) 
518                         send_exclude_list(f_out);
519                 if (!read_batch) /*  dw -- don't write to pipe */
520                     flist = send_file_list(f_out,argc,argv);
521                 if (verbose > 3) 
522                         rprintf(FINFO,"file list sent\n");
523
524                 send_files(flist,f_out,f_in);
525                 if (remote_version >= 24) {
526                         /* final goodbye message */             
527                         read_int(f_in);
528                 }
529                 if (pid != -1) {
530                         if (verbose > 3)
531                                 rprintf(FINFO,"client_run waiting on %d\n",pid);
532                         io_flush();
533                         wait_process(pid, &status);
534                 }
535                 report(-1);
536                 exit_cleanup(status);
537         }
538
539         if (argc == 0) {
540                 extern int list_only;
541                 list_only = 1;
542         }
543         
544         if (!write_batch) /* dw */
545             send_exclude_list(f_out);
546         
547         flist = recv_file_list(f_in);
548         if (!flist || flist->count == 0) {
549                 rprintf(FINFO, "client: nothing to do: "
550                         "perhaps you need to specify some filenames or "
551                         "the --recursive option?\n");
552                 exit_cleanup(0);
553         }
554         
555         local_name = get_local_name(flist,argv[0]);
556         
557         status2 = do_recv(f_in,f_out,flist,local_name);
558         
559         if (pid != -1) {
560                 if (verbose > 3)
561                         rprintf(FINFO,"client_run2 waiting on %d\n",pid);
562                 io_flush();
563                 wait_process(pid, &status);
564         }
565         
566         return MAX(status, status2);
567 }
568
569 static char *find_colon(char *s)
570 {
571         char *p, *p2;
572
573         p = strchr(s,':');
574         if (!p) return NULL;
575         
576         /* now check to see if there is a / in the string before the : - if there is then
577            discard the colon on the assumption that the : is part of a filename */
578         p2 = strchr(s,'/');
579         if (p2 && p2 < p) return NULL;
580
581         return p;
582 }
583
584
585 /*
586  * Start a client for either type of remote connection.  Work out
587  * whether the arguments request a remote shell or rsyncd connection,
588  * and call the appropriate connection function, then run_client.
589  */
590 static int start_client(int argc, char *argv[])
591 {
592         char *p;
593         char *shell_machine = NULL;
594         char *shell_path = NULL;
595         char *shell_user = NULL;
596         int ret;
597         pid_t pid;
598         int f_in,f_out;
599         extern int local_server;
600         extern int am_sender;
601         extern char *shell_cmd;
602         extern int rsync_port;
603         extern int whole_file;
604         char *argv0 = strdup(argv[0]);
605         extern int read_batch;
606
607         if (strncasecmp(URL_PREFIX, argv0, strlen(URL_PREFIX)) == 0) {
608                 char *host, *path;
609
610                 host = argv0 + strlen(URL_PREFIX);
611                 p = strchr(host,'/');
612                 if (p) {
613                         *p = 0;
614                         path = p+1;
615                 } else {
616                         path="";
617                 }
618                 p = strchr(host,':');
619                 if (p) {
620                         rsync_port = atoi(p+1);
621                         *p = 0;
622                 }
623                 return start_socket_client(host, path, argc-1, argv+1);
624         }
625
626         if (!read_batch) { /* dw */
627             p = find_colon(argv[0]);
628
629         if (p) {
630                 if (p[1] == ':') {
631                         *p = 0;
632                         return start_socket_client(argv0, p+2, argc-1, argv+1);
633                 }
634
635                 if (argc < 1) {
636                         usage(FERROR);
637                         exit_cleanup(RERR_SYNTAX);
638                 }
639
640                 am_sender = 0;
641                 *p = 0;
642                 shell_machine = argv0;
643                 shell_path = p+1;
644                 argc--;
645                 argv++;
646         } else {
647                 am_sender = 1;
648
649                 p = find_colon(argv[argc-1]);
650                 if (!p) {
651                         local_server = 1;
652                         /* disable "rsync algorithm" when both sides local */
653                         whole_file = 1;
654                 } else if (p[1] == ':') {
655                         *p = 0;
656                         return start_socket_client(argv[argc-1], p+2, argc-1, argv);
657                 }
658
659                 if (argc < 2) {
660                         usage(FERROR);
661                         exit_cleanup(RERR_SYNTAX);
662                 }
663                 
664                 if (local_server) {
665                         shell_machine = NULL;
666                         shell_path = argv[argc-1];
667                 } else {
668                         *p = 0;
669                         shell_machine = argv[argc-1];
670                         shell_path = p+1;
671                 }
672                 argc--;
673         }
674         } else {
675             am_sender = 1;  /*  dw */
676             local_server = 1;  /* dw */
677             shell_path = argv[argc-1];  /* dw */
678         }
679
680         if (shell_machine) {
681                 p = strchr(shell_machine,'@');
682                 if (p) {
683                         *p = 0;
684                         shell_user = shell_machine;
685                         shell_machine = p+1;
686                 }
687         }
688
689         if (verbose > 3) {
690                 rprintf(FINFO,"cmd=%s machine=%s user=%s path=%s\n",
691                         shell_cmd?shell_cmd:"",
692                         shell_machine?shell_machine:"",
693                         shell_user?shell_user:"",
694                         shell_path?shell_path:"");
695         }
696         
697         if (!am_sender && argc > 1) {
698                 usage(FERROR);
699                 exit_cleanup(RERR_SYNTAX);
700         }
701
702         if (argc == 0 && !am_sender) {
703                 extern int list_only;
704                 list_only = 1;
705         }
706         
707         pid = do_cmd(shell_cmd,shell_machine,shell_user,shell_path,&f_in,&f_out);
708         
709         ret = client_run(f_in, f_out, pid, argc, argv);
710
711         fflush(stdout);
712         fflush(stderr);
713
714         return ret;
715 }
716
717
718 static RETSIGTYPE sigusr1_handler(int val) {
719         exit_cleanup(RERR_SIGNAL);
720 }
721
722 static RETSIGTYPE sigusr2_handler(int val) {
723         extern int log_got_error;
724         if (log_got_error) _exit(RERR_PARTIAL);
725         _exit(0);
726 }
727
728 static RETSIGTYPE sigchld_handler(int val) {
729 #ifdef WNOHANG
730         while (waitpid(-1, NULL, WNOHANG) > 0) ;
731 #endif
732 }
733
734 int main(int argc,char *argv[])
735 {       
736         extern int am_root;
737         extern int orig_umask;
738         extern int dry_run;
739         extern int am_daemon;
740         extern int am_server;
741         int ret;
742         extern int read_batch;   /*  dw */
743         extern int write_batch;  /*  dw */
744         extern char *batch_ext;   /*  dw */
745         int orig_argc;  /* dw */
746
747         orig_argc = argc;   /* dw */
748
749         signal(SIGUSR1, sigusr1_handler);
750         signal(SIGUSR2, sigusr2_handler);
751         signal(SIGCHLD, sigchld_handler);
752
753         starttime = time(NULL);
754         am_root = (getuid() == 0);
755
756         memset(&stats, 0, sizeof(stats));
757
758         if (argc < 2) {
759                 usage(FERROR);
760                 exit_cleanup(RERR_SYNTAX);
761         }
762
763         /* we set a 0 umask so that correct file permissions can be
764            carried across */
765         orig_umask = (int)umask(0);
766
767         if (!parse_arguments(&argc, (const char ***) &argv, 1)) {
768                 /* FIXME: We ought to call the same error-handling
769                  * code here, rather than relying on getopt. */
770                 option_error();
771                 exit_cleanup(RERR_SYNTAX);
772         }
773
774         signal(SIGINT,SIGNAL_CAST sig_int);
775         signal(SIGPIPE,SIGNAL_CAST sig_int);
776         signal(SIGHUP,SIGNAL_CAST sig_int);
777         signal(SIGTERM,SIGNAL_CAST sig_int);
778
779         /* Initialize push_dir here because on some old systems getcwd
780            (implemented by forking "pwd" and reading its output) doesn't
781            work when there are other child processes.  Also, on all systems
782            that implement getcwd that way "pwd" can't be found after chroot. */
783         push_dir(NULL,0);
784
785         if (write_batch) { /* dw */
786             create_batch_file_ext();
787             write_batch_argvs_file(orig_argc, argc, argv);
788         }
789
790         if (read_batch) { /* dw */
791             set_batch_file_ext(batch_ext);
792         }
793
794         if (am_daemon) {
795                 return daemon_main();
796         }
797
798         if (argc < 1) {
799                 usage(FERROR);
800                 exit_cleanup(RERR_SYNTAX);
801         }
802
803         if (dry_run)
804                 verbose = MAX(verbose,1);
805
806 #ifndef SUPPORT_LINKS
807         if (!am_server && preserve_links) {
808                 rprintf(FERROR,"ERROR: symbolic links not supported\n");
809                 exit_cleanup(RERR_UNSUPPORTED);
810         }
811 #endif
812
813         if (am_server) {
814                 set_nonblocking(STDIN_FILENO);
815                 set_nonblocking(STDOUT_FILENO);
816                 start_server(STDIN_FILENO, STDOUT_FILENO, argc, argv);
817         }
818
819         ret = start_client(argc, argv);
820         exit_cleanup(ret);
821         return ret;
822 }
823