new error handling system
[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
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
23time_t starttime = 0;
24
25struct stats stats;
26
27extern int verbose;
28
29
30/****************************************************************************
31wait for a process to exit, calling io_flush while waiting
32****************************************************************************/
33void 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
48static 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/* TODO: When the shell exits, look at its return value, as this may
130 * well tell us if something went wrong in trying to connect to the
131 * remote machine. Although it doesn't seem to be specified anywhere,
132 * ssh and the shell seem to return these values:
133 *
134 * 124 if the command exited with status 255
135 * 125 if the command is killed by a signal
136 * 126 if the command cannot be run
137 * 127 if the command is not found
138 *
139 * and we could use this to give a better explanation if the remote
140 * command is not found.
141 */
142static int do_cmd(char *cmd,char *machine,char *user,char *path,int *f_in,int *f_out)
143{
144 char *args[100];
145 int i,argc=0, ret;
146 char *tok,*dir=NULL;
147 extern int local_server;
148 extern char *rsync_path;
149 extern int blocking_io;
150
151 if (!local_server) {
152 if (!cmd)
153 cmd = getenv(RSYNC_RSH_ENV);
154 if (!cmd)
155 cmd = RSYNC_RSH;
156 cmd = strdup(cmd);
157 if (!cmd)
158 goto oom;
159
160 for (tok=strtok(cmd," ");tok;tok=strtok(NULL," ")) {
161 args[argc++] = tok;
162 }
163
164#if HAVE_REMSH
165 /* remsh (on HPUX) takes the arguments the other way around */
166 args[argc++] = machine;
167 if (user) {
168 args[argc++] = "-l";
169 args[argc++] = user;
170 }
171#else
172 if (user) {
173 args[argc++] = "-l";
174 args[argc++] = user;
175 }
176 args[argc++] = machine;
177#endif
178
179 args[argc++] = rsync_path;
180
181 server_options(args,&argc);
182
183
184 if (strcmp(cmd, RSYNC_RSH) == 0) blocking_io = 1;
185 }
186
187 args[argc++] = ".";
188
189 if (path && *path)
190 args[argc++] = path;
191
192 args[argc] = NULL;
193
194 if (verbose > 3) {
195 rprintf(FINFO,"cmd=");
196 for (i=0;i<argc;i++)
197 rprintf(FINFO,"%s ",args[i]);
198 rprintf(FINFO,"\n");
199 }
200
201 if (local_server) {
202 ret = local_child(argc, args, f_in, f_out);
203 } else {
204 ret = piped_child(args,f_in,f_out);
205 }
206
207 if (dir) free(dir);
208
209 return ret;
210
211oom:
212 out_of_memory("do_cmd");
213 return 0; /* not reached */
214}
215
216
217
218
219static char *get_local_name(struct file_list *flist,char *name)
220{
221 STRUCT_STAT st;
222 extern int orig_umask;
223
224 if (verbose > 2)
225 rprintf(FINFO,"get_local_name count=%d %s\n",
226 flist->count, NS(name));
227
228 if (!name)
229 return NULL;
230
231 if (do_stat(name,&st) == 0) {
232 if (S_ISDIR(st.st_mode)) {
233 if (!push_dir(name, 0)) {
234 rprintf(FERROR,"push_dir %s : %s (1)\n",
235 name,strerror(errno));
236 exit_cleanup(RERR_FILESELECT);
237 }
238 return NULL;
239 }
240 if (flist->count > 1) {
241 rprintf(FERROR,"ERROR: destination must be a directory when copying more than 1 file\n");
242 exit_cleanup(RERR_FILESELECT);
243 }
244 return name;
245 }
246
247 if (flist->count <= 1)
248 return name;
249
250 if (do_mkdir(name,0777 & ~orig_umask) != 0) {
251 rprintf(FERROR,"mkdir %s : %s (1)\n",name,strerror(errno));
252 exit_cleanup(RERR_FILEIO);
253 } else {
254 if (verbose > 0)
255 rprintf(FINFO,"created directory %s\n",name);
256 }
257
258 if (!push_dir(name, 0)) {
259 rprintf(FERROR,"push_dir %s : %s (2)\n",
260 name,strerror(errno));
261 exit_cleanup(RERR_FILESELECT);
262 }
263
264 return NULL;
265}
266
267
268
269
270static void do_server_sender(int f_in, int f_out, int argc,char *argv[])
271{
272 int i;
273 struct file_list *flist;
274 char *dir = argv[0];
275 extern int relative_paths;
276 extern int recurse;
277 extern int remote_version;
278
279 if (verbose > 2)
280 rprintf(FINFO,"server_sender starting pid=%d\n",(int)getpid());
281
282 if (!relative_paths && !push_dir(dir, 0)) {
283 rprintf(FERROR,"push_dir %s: %s (3)\n",dir,strerror(errno));
284 exit_cleanup(RERR_FILESELECT);
285 }
286 argc--;
287 argv++;
288
289 if (strcmp(dir,".")) {
290 int l = strlen(dir);
291 if (strcmp(dir,"/") == 0)
292 l = 0;
293 for (i=0;i<argc;i++)
294 argv[i] += l+1;
295 }
296
297 if (argc == 0 && recurse) {
298 argc=1;
299 argv--;
300 argv[0] = ".";
301 }
302
303 flist = send_file_list(f_out,argc,argv);
304 if (!flist || flist->count == 0) {
305 exit_cleanup(0);
306 }
307
308 send_files(flist,f_out,f_in);
309 io_flush();
310 report(f_out);
311 if (remote_version >= 24) {
312 /* final goodbye message */
313 read_int(f_in);
314 }
315 io_flush();
316 exit_cleanup(0);
317}
318
319
320static int do_recv(int f_in,int f_out,struct file_list *flist,char *local_name)
321{
322 int pid;
323 int status=0;
324 int recv_pipe[2];
325 int error_pipe[2];
326 extern int preserve_hard_links;
327 extern int delete_after;
328 extern int recurse;
329 extern int delete_mode;
330 extern int remote_version;
331
332 if (preserve_hard_links)
333 init_hard_links(flist);
334
335 if (!delete_after) {
336 /* I moved this here from recv_files() to prevent a race condition */
337 if (recurse && delete_mode && !local_name && flist->count>0) {
338 delete_files(flist);
339 }
340 }
341
342 if (fd_pair(recv_pipe) < 0) {
343 rprintf(FERROR,"pipe failed in do_recv\n");
344 exit_cleanup(RERR_SOCKETIO);
345 }
346
347 if (fd_pair(error_pipe) < 0) {
348 rprintf(FERROR,"error pipe failed in do_recv\n");
349 exit_cleanup(RERR_SOCKETIO);
350 }
351
352 io_flush();
353
354 if ((pid=do_fork()) == 0) {
355 close(recv_pipe[0]);
356 close(error_pipe[0]);
357 if (f_in != f_out) close(f_out);
358
359 /* we can't let two processes write to the socket at one time */
360 io_multiplexing_close();
361
362 /* set place to send errors */
363 set_error_fd(error_pipe[1]);
364
365 recv_files(f_in,flist,local_name,recv_pipe[1]);
366 io_flush();
367 report(f_in);
368
369 write_int(recv_pipe[1],1);
370 close(recv_pipe[1]);
371 io_flush();
372 /* finally we go to sleep until our parent kills us
373 with a USR2 signal. We sleep for a short time as on
374 some OSes a signal won't interrupt a sleep! */
375 while (1) msleep(20);
376 }
377
378 close(recv_pipe[1]);
379 close(error_pipe[1]);
380 if (f_in != f_out) close(f_in);
381
382 io_start_buffering(f_out);
383
384 io_set_error_fd(error_pipe[0]);
385
386 generate_files(f_out,flist,local_name,recv_pipe[0]);
387
388 read_int(recv_pipe[0]);
389 close(recv_pipe[0]);
390 if (remote_version >= 24) {
391 /* send a final goodbye message */
392 write_int(f_out, -1);
393 }
394 io_flush();
395
396 kill(pid, SIGUSR2);
397 wait_process(pid, &status);
398 return status;
399}
400
401
402static void do_server_recv(int f_in, int f_out, int argc,char *argv[])
403{
404 int status;
405 struct file_list *flist;
406 char *local_name=NULL;
407 char *dir = NULL;
408 extern int delete_mode;
409 extern int delete_excluded;
410 extern int am_daemon;
411 extern int module_id;
412 extern int am_sender;
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 flist = recv_file_list(f_in);
439 if (!flist) {
440 rprintf(FERROR,"server_recv: recv_file_list error\n");
441 exit_cleanup(RERR_FILESELECT);
442 }
443
444 if (argc > 0) {
445 if (strcmp(dir,".")) {
446 argv[0] += strlen(dir);
447 if (argv[0][0] == '/') argv[0]++;
448 }
449 local_name = get_local_name(flist,argv[0]);
450 }
451
452 status = do_recv(f_in,f_out,flist,local_name);
453 exit_cleanup(status);
454}
455
456
457void start_server(int f_in, int f_out, int argc, char *argv[])
458{
459 extern int cvs_exclude;
460 extern int am_sender;
461 extern int remote_version;
462
463 setup_protocol(f_out, f_in);
464
465 set_nonblocking(f_in);
466 set_nonblocking(f_out);
467
468 if (remote_version >= 23)
469 io_start_multiplex_out(f_out);
470
471 if (am_sender) {
472 recv_exclude_list(f_in);
473 if (cvs_exclude)
474 add_cvs_excludes();
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 */
487int client_run(int f_in, int f_out, int 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
495 set_nonblocking(f_in);
496 set_nonblocking(f_out);
497
498 setup_protocol(f_out,f_in);
499
500 if (remote_version >= 23)
501 io_start_multiplex_in(f_in);
502
503 if (am_sender) {
504 extern int cvs_exclude;
505 extern int delete_mode;
506 extern int delete_excluded;
507 if (cvs_exclude)
508 add_cvs_excludes();
509 if (delete_mode && !delete_excluded)
510 send_exclude_list(f_out);
511 flist = send_file_list(f_out,argc,argv);
512 if (verbose > 3)
513 rprintf(FINFO,"file list sent\n");
514
515 send_files(flist,f_out,f_in);
516 if (pid != -1) {
517 if (verbose > 3)
518 rprintf(FINFO,"client_run waiting on %d\n",pid);
519 io_flush();
520 wait_process(pid, &status);
521 }
522 if (remote_version >= 24) {
523 /* final goodbye message */
524 read_int(f_in);
525 }
526 report(-1);
527 exit_cleanup(status);
528 }
529
530 if (argc == 0) {
531 extern int list_only;
532 list_only = 1;
533 }
534
535 send_exclude_list(f_out);
536
537 flist = recv_file_list(f_in);
538 if (!flist || flist->count == 0) {
539 rprintf(FINFO, "client: nothing to do: "
540 "perhaps you need to specify some filenames or "
541 "the --recursive option?\n");
542 exit_cleanup(0);
543 }
544
545 local_name = get_local_name(flist,argv[0]);
546
547 status2 = do_recv(f_in,f_out,flist,local_name);
548
549 if (pid != -1) {
550 if (verbose > 3)
551 rprintf(FINFO,"client_run2 waiting on %d\n",pid);
552 io_flush();
553 wait_process(pid, &status);
554 }
555
556 return MAX(status, status2);
557}
558
559static char *find_colon(char *s)
560{
561 char *p, *p2;
562
563 p = strchr(s,':');
564 if (!p) return NULL;
565
566 /* now check to see if there is a / in the string before the : - if there is then
567 discard the colon on the assumption that the : is part of a filename */
568 p2 = strchr(s,'/');
569 if (p2 && p2 < p) return NULL;
570
571 return p;
572}
573
574
575/*
576 * Start a client for either type of remote connection. Work out
577 * whether the arguments request a remote shell or rsyncd connection,
578 * and call the appropriate connection function, then run_client.
579 */
580static int start_client(int argc, char *argv[])
581{
582 char *p;
583 char *shell_machine = NULL;
584 char *shell_path = NULL;
585 char *shell_user = NULL;
586 int pid, ret;
587 int f_in,f_out;
588 extern int local_server;
589 extern int am_sender;
590 extern char *shell_cmd;
591 extern int rsync_port;
592 char *argv0 = strdup(argv[0]);
593
594 if (strncasecmp(URL_PREFIX, argv0, strlen(URL_PREFIX)) == 0) {
595 char *host, *path;
596
597 host = argv0 + strlen(URL_PREFIX);
598 p = strchr(host,'/');
599 if (p) {
600 *p = 0;
601 path = p+1;
602 } else {
603 path="";
604 }
605 p = strchr(host,':');
606 if (p) {
607 rsync_port = atoi(p+1);
608 *p = 0;
609 }
610 return start_socket_client(host, path, argc-1, argv+1);
611 }
612
613 p = find_colon(argv0);
614
615 if (p) {
616 if (p[1] == ':') {
617 *p = 0;
618 return start_socket_client(argv0, p+2, argc-1, argv+1);
619 }
620
621 if (argc < 1) {
622 usage(FERROR);
623 exit_cleanup(RERR_SYNTAX);
624 }
625
626 am_sender = 0;
627 *p = 0;
628 shell_machine = argv0;
629 shell_path = p+1;
630 argc--;
631 argv++;
632 } else {
633 am_sender = 1;
634
635 p = find_colon(argv[argc-1]);
636 if (!p) {
637 local_server = 1;
638 } else if (p[1] == ':') {
639 *p = 0;
640 return start_socket_client(argv[argc-1], p+2, argc-1, argv);
641 }
642
643 if (argc < 2) {
644 usage(FERROR);
645 exit_cleanup(RERR_SYNTAX);
646 }
647
648 if (local_server) {
649 shell_machine = NULL;
650 shell_path = argv[argc-1];
651 } else {
652 *p = 0;
653 shell_machine = argv[argc-1];
654 shell_path = p+1;
655 }
656 argc--;
657 }
658
659 if (shell_machine) {
660 p = strchr(shell_machine,'@');
661 if (p) {
662 *p = 0;
663 shell_user = shell_machine;
664 shell_machine = p+1;
665 }
666 }
667
668 if (verbose > 3) {
669 rprintf(FINFO,"cmd=%s machine=%s user=%s path=%s\n",
670 shell_cmd?shell_cmd:"",
671 shell_machine?shell_machine:"",
672 shell_user?shell_user:"",
673 shell_path?shell_path:"");
674 }
675
676 if (!am_sender && argc > 1) {
677 usage(FERROR);
678 exit_cleanup(RERR_SYNTAX);
679 }
680
681 if (argc == 0 && !am_sender) {
682 extern int list_only;
683 list_only = 1;
684 }
685
686 pid = do_cmd(shell_cmd,shell_machine,shell_user,shell_path,&f_in,&f_out);
687
688 ret = client_run(f_in, f_out, pid, argc, argv);
689
690 fflush(stdout);
691 fflush(stderr);
692
693 return ret;
694}
695
696
697static RETSIGTYPE sigusr1_handler(int val) {
698 exit_cleanup(RERR_SIGNAL);
699}
700
701static RETSIGTYPE sigusr2_handler(int val) {
702 extern int log_got_error;
703 if (log_got_error) _exit(RERR_FILEIO);
704 _exit(0);
705}
706
707int main(int argc,char *argv[])
708{
709 extern int am_root;
710 extern int orig_umask;
711 extern int dry_run;
712 extern int am_daemon;
713 extern int am_server;
714 int ret;
715
716 signal(SIGUSR1, sigusr1_handler);
717 signal(SIGUSR2, sigusr2_handler);
718
719 starttime = time(NULL);
720 am_root = (getuid() == 0);
721
722 memset(&stats, 0, sizeof(stats));
723
724 if (argc < 2) {
725 usage(FERROR);
726 exit_cleanup(RERR_SYNTAX);
727 }
728
729 /* we set a 0 umask so that correct file permissions can be
730 carried across */
731 orig_umask = (int)umask(0);
732
733 if (!parse_arguments(&argc, (const char ***) &argv, 1)) {
734 /* FIXME: We ought to call the same error-handling
735 * code here, rather than relying on getopt. */
736 option_error();
737 exit_cleanup(RERR_SYNTAX);
738 }
739
740 signal(SIGINT,SIGNAL_CAST sig_int);
741 signal(SIGPIPE,SIGNAL_CAST sig_int);
742 signal(SIGHUP,SIGNAL_CAST sig_int);
743 signal(SIGTERM,SIGNAL_CAST sig_int);
744
745 /* Initialize push_dir here because on some old systems getcwd
746 (implemented by forking "pwd" and reading its output) doesn't
747 work when there are other child processes. Also, on all systems
748 that implement getcwd that way "pwd" can't be found after chroot. */
749 push_dir(NULL,0);
750
751 if (am_daemon) {
752 return daemon_main();
753 }
754
755 if (argc < 1) {
756 usage(FERROR);
757 exit_cleanup(RERR_SYNTAX);
758 }
759
760 if (dry_run)
761 verbose = MAX(verbose,1);
762
763#ifndef SUPPORT_LINKS
764 if (!am_server && preserve_links) {
765 rprintf(FERROR,"ERROR: symbolic links not supported\n");
766 exit_cleanup(RERR_UNSUPPORTED);
767 }
768#endif
769
770 if (am_server) {
771 set_nonblocking(STDIN_FILENO);
772 set_nonblocking(STDOUT_FILENO);
773 start_server(STDIN_FILENO, STDOUT_FILENO, argc, argv);
774 }
775
776 ret = start_client(argc, argv);
777 exit_cleanup(ret);
778 return ret;
779}
780