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