Document more recent changes.
[rsync/rsync.git] / main.c
... / ...
CommitLineData
1/* -*- c-file-style: "linux" -*-
2
3 Copyright (C) 1996-2001 by Andrew Tridgell <tridge@samba.org>
4 Copyright (C) Paul Mackerras 1996
5 Copyright (C) 2001, 2002 by Martin Pool <mbp@samba.org>
6
7 This program is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2 of the License, or
10 (at your option) any later version.
11
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with this program; if not, write to the Free Software
19 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20*/
21
22#include "rsync.h"
23
24time_t starttime = 0;
25
26extern struct stats stats;
27extern int verbose;
28
29static void show_malloc_stats(void);
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 (do_stats) {
60 /* These come out from every process */
61 show_malloc_stats();
62 show_flist_stats();
63 }
64
65 if (am_daemon) {
66 log_exit(0, __FILE__, __LINE__);
67 if (f == -1 || !am_sender) return;
68 }
69
70 send_stats = verbose || (remote_version >= 20);
71 if (am_server) {
72 if (am_sender && send_stats) {
73 int64 w;
74 /* store total_written in a temporary
75 because write_longint changes it */
76 w = stats.total_written;
77 write_longint(f,stats.total_read);
78 write_longint(f,w);
79 write_longint(f,stats.total_size);
80 }
81 return;
82 }
83
84 /* this is the client */
85
86 if (!am_sender && send_stats) {
87 int64 r;
88 stats.total_written = read_longint(f);
89 /* store total_read in a temporary, read_longint changes it */
90 r = read_longint(f);
91 stats.total_size = read_longint(f);
92 stats.total_read = r;
93 }
94
95 if (do_stats) {
96 if (!am_sender && !send_stats) {
97 /* missing the bytes written by the generator */
98 rprintf(FINFO, "\nCannot show stats as receiver because remote protocol version is less than 20\n");
99 rprintf(FINFO, "Use --stats -v to show stats\n");
100 return;
101 }
102 rprintf(FINFO,"\nNumber of files: %d\n", stats.num_files);
103 rprintf(FINFO,"Number of files transferred: %d\n",
104 stats.num_transferred_files);
105 rprintf(FINFO,"Total file size: %.0f bytes\n",
106 (double)stats.total_size);
107 rprintf(FINFO,"Total transferred file size: %.0f bytes\n",
108 (double)stats.total_transferred_size);
109 rprintf(FINFO,"Literal data: %.0f bytes\n",
110 (double)stats.literal_data);
111 rprintf(FINFO,"Matched data: %.0f bytes\n",
112 (double)stats.matched_data);
113 rprintf(FINFO,"File list size: %d\n", stats.flist_size);
114 rprintf(FINFO,"Total bytes written: %.0f\n",
115 (double)stats.total_written);
116 rprintf(FINFO,"Total bytes read: %.0f\n\n",
117 (double)stats.total_read);
118 }
119
120 if (verbose || do_stats) {
121 rprintf(FINFO,"wrote %.0f bytes read %.0f bytes %.2f bytes/sec\n",
122 (double)stats.total_written,
123 (double)stats.total_read,
124 (stats.total_written+stats.total_read)/(0.5 + (t-starttime)));
125 rprintf(FINFO,"total size is %.0f speedup is %.2f\n",
126 (double)stats.total_size,
127 (1.0*stats.total_size)/(stats.total_written+stats.total_read));
128 }
129
130 fflush(stdout);
131 fflush(stderr);
132}
133
134
135/**
136 * If our C library can get malloc statistics, then show them to FINFO
137 **/
138static void show_malloc_stats(void)
139{
140#ifdef HAVE_MALLINFO
141 struct mallinfo mi;
142 extern int am_server;
143 extern int am_sender;
144 extern int am_daemon;
145
146 mi = mallinfo();
147
148 rprintf(FINFO, RSYNC_NAME "[%d] (%s%s%s) heap statistics:\n",
149 getpid(),
150 am_server ? "server " : "",
151 am_daemon ? "daemon " : "",
152 am_sender ? "sender" : "receiver");
153 rprintf(FINFO, " arena: %10d (bytes from sbrk)\n", mi.arena);
154 rprintf(FINFO, " ordblks: %10d (chunks not in use)\n", mi.ordblks);
155 rprintf(FINFO, " smblks: %10d\n", mi.smblks);
156 rprintf(FINFO, " hblks: %10d (chunks from mmap)\n", mi.hblks);
157 rprintf(FINFO, " hblkhd: %10d (bytes from mmap)\n", mi.hblkhd);
158 rprintf(FINFO, " usmblks: %10d\n", mi.usmblks);
159 rprintf(FINFO, " fsmblks: %10d\n", mi.fsmblks);
160 rprintf(FINFO, " uordblks: %10d (bytes used)\n", mi.uordblks);
161 rprintf(FINFO, " fordblks: %10d (bytes free)\n", mi.fordblks);
162 rprintf(FINFO, " keepcost: %10d (bytes in releasable chunk)\n", mi.keepcost);
163#endif /* HAVE_MALLINFO */
164}
165
166
167/* Start the remote shell. cmd may be NULL to use the default. */
168static pid_t do_cmd(char *cmd,char *machine,char *user,char *path,int *f_in,int *f_out)
169{
170 char *args[100];
171 int i,argc=0;
172 pid_t ret;
173 char *tok,*dir=NULL;
174 extern int local_server;
175 extern char *rsync_path;
176 extern int blocking_io;
177 extern int daemon_over_rsh;
178 extern int read_batch;
179
180 if (!read_batch && !local_server) {
181 if (!cmd)
182 cmd = getenv(RSYNC_RSH_ENV);
183 if (!cmd)
184 cmd = RSYNC_RSH;
185 cmd = strdup(cmd);
186 if (!cmd)
187 goto oom;
188
189 for (tok=strtok(cmd," ");tok;tok=strtok(NULL," ")) {
190 args[argc++] = tok;
191 }
192
193#if HAVE_REMSH
194 /* remsh (on HPUX) takes the arguments the other way around */
195 args[argc++] = machine;
196 if (user) {
197 args[argc++] = "-l";
198 args[argc++] = user;
199 }
200#else
201 if (user) {
202 args[argc++] = "-l";
203 args[argc++] = user;
204 }
205 args[argc++] = machine;
206#endif
207
208 args[argc++] = rsync_path;
209
210 if ((blocking_io == -1) && (strcmp(cmd, RSYNC_RSH) == 0))
211 blocking_io = 1;
212
213 server_options(args,&argc);
214
215 }
216
217 args[argc++] = ".";
218
219 if (!daemon_over_rsh && path && *path)
220 args[argc++] = path;
221
222 args[argc] = NULL;
223
224 if (verbose > 3) {
225 rprintf(FINFO,"cmd=");
226 for (i=0;i<argc;i++)
227 rprintf(FINFO,"%s ",args[i]);
228 rprintf(FINFO,"\n");
229 }
230
231 if (local_server) {
232 if (read_batch)
233 create_flist_from_batch(); /* sets batch_flist */
234 ret = local_child(argc, args, f_in, f_out, child_main);
235 } else {
236 ret = piped_child(args,f_in,f_out);
237 }
238
239 if (dir) free(dir);
240
241 return ret;
242
243oom:
244 out_of_memory("do_cmd");
245 return 0; /* not reached */
246}
247
248
249
250
251static char *get_local_name(struct file_list *flist,char *name)
252{
253 STRUCT_STAT st;
254 extern int orig_umask;
255
256 if (verbose > 2)
257 rprintf(FINFO,"get_local_name count=%d %s\n",
258 flist->count, NS(name));
259
260 if (!name)
261 return NULL;
262
263 if (do_stat(name,&st) == 0) {
264 if (S_ISDIR(st.st_mode)) {
265 if (!push_dir(name, 0)) {
266 rprintf(FERROR,"push_dir %s : %s (1)\n",
267 name,strerror(errno));
268 exit_cleanup(RERR_FILESELECT);
269 }
270 return NULL;
271 }
272 if (flist->count > 1) {
273 rprintf(FERROR,"ERROR: destination must be a directory when copying more than 1 file\n");
274 exit_cleanup(RERR_FILESELECT);
275 }
276 return name;
277 }
278
279 if (flist->count <= 1)
280 return name;
281
282 if (do_mkdir(name,0777 & ~orig_umask) != 0) {
283 rprintf(FERROR, RSYNC_NAME ": mkdir %s: %s\n",
284 name, strerror(errno));
285 exit_cleanup(RERR_FILEIO);
286 } else {
287 if (verbose > 0)
288 rprintf(FINFO,"created directory %s\n",name);
289 }
290
291 if (!push_dir(name, 0)) {
292 rprintf(FERROR, RSYNC_NAME ": push_dir %s: %s\n",
293 name, strerror(errno));
294 exit_cleanup(RERR_FILESELECT);
295 }
296
297 return NULL;
298}
299
300
301
302
303static void do_server_sender(int f_in, int f_out, int argc,char *argv[])
304{
305 int i;
306 struct file_list *flist;
307 char *dir = argv[0];
308 extern int relative_paths;
309 extern int recurse;
310 extern int remote_version;
311
312 if (verbose > 2)
313 rprintf(FINFO,"server_sender starting pid=%d\n",(int)getpid());
314
315 if (!relative_paths && !push_dir(dir, 0)) {
316 rprintf(FERROR,"push_dir %s: %s (3)\n",dir,strerror(errno));
317 exit_cleanup(RERR_FILESELECT);
318 }
319 argc--;
320 argv++;
321
322 if (strcmp(dir,".")) {
323 int l = strlen(dir);
324 if (strcmp(dir,"/") == 0)
325 l = 0;
326 for (i=0;i<argc;i++)
327 argv[i] += l+1;
328 }
329
330 if (argc == 0 && recurse) {
331 argc=1;
332 argv--;
333 argv[0] = ".";
334 }
335
336 flist = send_file_list(f_out,argc,argv);
337 if (!flist || flist->count == 0) {
338 exit_cleanup(0);
339 }
340
341 send_files(flist,f_out,f_in);
342 io_flush();
343 report(f_out);
344 if (remote_version >= 24) {
345 /* final goodbye message */
346 read_int(f_in);
347 }
348 io_flush();
349 exit_cleanup(0);
350}
351
352
353static int do_recv(int f_in,int f_out,struct file_list *flist,char *local_name)
354{
355 int pid;
356 int status=0;
357 int recv_pipe[2];
358 int error_pipe[2];
359 extern int preserve_hard_links;
360 extern int delete_after;
361 extern int recurse;
362 extern int delete_mode;
363 extern int remote_version;
364
365 if (preserve_hard_links)
366 init_hard_links(flist);
367
368 if (!delete_after) {
369 /* I moved this here from recv_files() to prevent a race condition */
370 if (recurse && delete_mode && !local_name && flist->count>0) {
371 delete_files(flist);
372 }
373 }
374
375 if (fd_pair(recv_pipe) < 0) {
376 rprintf(FERROR,"pipe failed in do_recv\n");
377 exit_cleanup(RERR_SOCKETIO);
378 }
379
380 if (fd_pair(error_pipe) < 0) {
381 rprintf(FERROR,"error pipe failed in do_recv\n");
382 exit_cleanup(RERR_SOCKETIO);
383 }
384
385 io_flush();
386
387 if ((pid=do_fork()) == 0) {
388 close(recv_pipe[0]);
389 close(error_pipe[0]);
390 if (f_in != f_out) close(f_out);
391
392 /* we can't let two processes write to the socket at one time */
393 io_multiplexing_close();
394
395 /* set place to send errors */
396 set_error_fd(error_pipe[1]);
397
398 recv_files(f_in,flist,local_name,recv_pipe[1]);
399 io_flush();
400 report(f_in);
401
402 write_int(recv_pipe[1],1);
403 close(recv_pipe[1]);
404 io_flush();
405 /* finally we go to sleep until our parent kills us
406 with a USR2 signal. We sleep for a short time as on
407 some OSes a signal won't interrupt a sleep! */
408 while (msleep(20))
409 ;
410 }
411
412 close(recv_pipe[1]);
413 close(error_pipe[1]);
414 if (f_in != f_out) close(f_in);
415
416 io_start_buffering(f_out);
417
418 io_set_error_fd(error_pipe[0]);
419
420 generate_files(f_out,flist,local_name,recv_pipe[0]);
421
422 read_int(recv_pipe[0]);
423 close(recv_pipe[0]);
424 if (remote_version >= 24) {
425 /* send a final goodbye message */
426 write_int(f_out, -1);
427 }
428 io_flush();
429
430 kill(pid, SIGUSR2);
431 wait_process(pid, &status);
432 return status;
433}
434
435
436static void do_server_recv(int f_in, int f_out, int argc,char *argv[])
437{
438 int status;
439 struct file_list *flist;
440 char *local_name=NULL;
441 char *dir = NULL;
442 extern int delete_mode;
443 extern int delete_excluded;
444 extern int am_daemon;
445 extern int module_id;
446 extern int am_sender;
447 extern int read_batch;
448 extern struct file_list *batch_flist;
449
450 if (verbose > 2)
451 rprintf(FINFO,"server_recv(%d) starting pid=%d\n",argc,(int)getpid());
452
453 if (am_daemon && lp_read_only(module_id) && !am_sender) {
454 rprintf(FERROR,"ERROR: module is read only\n");
455 exit_cleanup(RERR_SYNTAX);
456 return;
457 }
458
459
460 if (argc > 0) {
461 dir = argv[0];
462 argc--;
463 argv++;
464 if (!am_daemon && !push_dir(dir, 0)) {
465 rprintf(FERROR,"push_dir %s : %s (4)\n",
466 dir,strerror(errno));
467 exit_cleanup(RERR_FILESELECT);
468 }
469 }
470
471 if (delete_mode && !delete_excluded)
472 recv_exclude_list(f_in);
473
474 if (read_batch)
475 flist = batch_flist;
476 else
477 flist = recv_file_list(f_in);
478 if (!flist) {
479 rprintf(FERROR,"server_recv: recv_file_list error\n");
480 exit_cleanup(RERR_FILESELECT);
481 }
482
483 if (argc > 0) {
484 if (strcmp(dir,".")) {
485 argv[0] += strlen(dir);
486 if (argv[0][0] == '/') argv[0]++;
487 }
488 local_name = get_local_name(flist,argv[0]);
489 }
490
491 status = do_recv(f_in,f_out,flist,local_name);
492 exit_cleanup(status);
493}
494
495
496int child_main(int argc, char *argv[])
497{
498 start_server(STDIN_FILENO, STDOUT_FILENO, argc, argv);
499 return 0;
500}
501
502
503void start_server(int f_in, int f_out, int argc, char *argv[])
504{
505 extern int cvs_exclude;
506 extern int am_sender;
507 extern int remote_version;
508 extern int read_batch;
509
510 setup_protocol(f_out, f_in);
511
512 set_nonblocking(f_in);
513 set_nonblocking(f_out);
514
515 if (remote_version >= 23)
516 io_start_multiplex_out(f_out);
517
518 if (am_sender) {
519 if (!read_batch) {
520 recv_exclude_list(f_in);
521 if (cvs_exclude)
522 add_cvs_excludes();
523 }
524 do_server_sender(f_in, f_out, argc, argv);
525 } else {
526 do_server_recv(f_in, f_out, argc, argv);
527 }
528 exit_cleanup(0);
529}
530
531
532/*
533 * This is called once the connection has been negotiated. It is used
534 * for rsyncd, remote-shell, and local connections.
535 */
536int client_run(int f_in, int f_out, pid_t pid, int argc, char *argv[])
537{
538 struct file_list *flist = NULL;
539 int status = 0, status2 = 0;
540 char *local_name = NULL;
541 extern int am_sender;
542 extern int remote_version;
543 extern pid_t cleanup_child_pid;
544 extern int write_batch;
545 extern int read_batch;
546 extern struct file_list *batch_flist;
547
548 cleanup_child_pid = pid;
549 if (read_batch)
550 flist = batch_flist;
551
552 set_nonblocking(f_in);
553 set_nonblocking(f_out);
554
555 setup_protocol(f_out,f_in);
556
557 if (remote_version >= 23)
558 io_start_multiplex_in(f_in);
559
560 if (am_sender) {
561 extern int cvs_exclude;
562 extern int delete_mode;
563 extern int delete_excluded;
564 if (cvs_exclude)
565 add_cvs_excludes();
566 if (delete_mode && !delete_excluded)
567 send_exclude_list(f_out);
568 if (!read_batch) /* dw -- don't write to pipe */
569 flist = send_file_list(f_out,argc,argv);
570 if (verbose > 3)
571 rprintf(FINFO,"file list sent\n");
572
573 send_files(flist,f_out,f_in);
574 if (remote_version >= 24) {
575 /* final goodbye message */
576 read_int(f_in);
577 }
578 if (pid != -1) {
579 if (verbose > 3)
580 rprintf(FINFO,"client_run waiting on %d\n", (int) pid);
581 io_flush();
582 wait_process(pid, &status);
583 }
584 report(-1);
585 exit_cleanup(status);
586 }
587
588 if (argc == 0) {
589 extern int list_only;
590 list_only = 1;
591 }
592
593 if (!write_batch)
594 send_exclude_list(f_out);
595
596 flist = recv_file_list(f_in);
597 if (!flist || flist->count == 0) {
598 rprintf(FINFO, "client: nothing to do: "
599 "perhaps you need to specify some filenames or "
600 "the --recursive option?\n");
601 exit_cleanup(0);
602 }
603
604 local_name = get_local_name(flist,argv[0]);
605
606 status2 = do_recv(f_in,f_out,flist,local_name);
607
608 if (pid != -1) {
609 if (verbose > 3)
610 rprintf(FINFO,"client_run2 waiting on %d\n", (int) pid);
611 io_flush();
612 wait_process(pid, &status);
613 }
614
615 return MAX(status, status2);
616}
617
618static char *find_colon(char *s)
619{
620 char *p, *p2;
621
622 p = strchr(s,':');
623 if (!p) return NULL;
624
625 /* now check to see if there is a / in the string before the : - if there is then
626 discard the colon on the assumption that the : is part of a filename */
627 p2 = strchr(s,'/');
628 if (p2 && p2 < p) return NULL;
629
630 return p;
631}
632
633
634static int copy_argv (char *argv[])
635{
636 int i;
637
638 for (i = 0; argv[i]; i++) {
639 if (!(argv[i] = strdup(argv[i]))) {
640 rprintf (FERROR, "out of memory at %s(%d)\n",
641 __FILE__, __LINE__);
642 return RERR_MALLOC;
643 }
644 }
645
646 return 0;
647}
648
649
650/**
651 * Start a client for either type of remote connection. Work out
652 * whether the arguments request a remote shell or rsyncd connection,
653 * and call the appropriate connection function, then run_client.
654 *
655 * Calls either start_socket_client (for sockets) or do_cmd and
656 * client_run (for ssh).
657 **/
658static int start_client(int argc, char *argv[])
659{
660 char *p;
661 char *shell_machine = NULL;
662 char *shell_path = NULL;
663 char *shell_user = NULL;
664 int ret;
665 pid_t pid;
666 int f_in,f_out;
667 extern int local_server;
668 extern int am_sender;
669 extern char *shell_cmd;
670 extern int rsync_port;
671 extern int daemon_over_rsh;
672 extern int read_batch;
673 int rc;
674
675 /* Don't clobber argv[] so that ps(1) can still show the right
676 command line. */
677 if ((rc = copy_argv(argv)))
678 return rc;
679
680 /* rsync:// always uses rsync server over direct socket connection */
681 if (strncasecmp(URL_PREFIX, argv[0], strlen(URL_PREFIX)) == 0) {
682 char *host, *path;
683
684 host = argv[0] + strlen(URL_PREFIX);
685 p = strchr(host,'/');
686 if (p) {
687 *p = 0;
688 path = p+1;
689 } else {
690 path="";
691 }
692 p = strchr(host,':');
693 if (p) {
694 rsync_port = atoi(p+1);
695 *p = 0;
696 }
697 return start_socket_client(host, path, argc-1, argv+1);
698 }
699
700 if (!read_batch) {
701 p = find_colon(argv[0]);
702
703 if (p) {
704 if (p[1] == ':') { /* double colon */
705 *p = 0;
706 if (!shell_cmd) {
707 return start_socket_client(argv[0], p+2,
708 argc-1, argv+1);
709 }
710 p++;
711 daemon_over_rsh = 1;
712 }
713
714 if (argc < 1) {
715 usage(FERROR);
716 exit_cleanup(RERR_SYNTAX);
717 }
718
719 am_sender = 0;
720 *p = 0;
721 shell_machine = argv[0];
722 shell_path = p+1;
723 argc--;
724 argv++;
725 } else {
726 am_sender = 1;
727
728 p = find_colon(argv[argc-1]);
729 if (!p) {
730 local_server = 1;
731 } else if (p[1] == ':') { /* double colon */
732 *p = 0;
733 if (!shell_cmd) {
734 return start_socket_client(argv[argc-1], p+2,
735 argc-1, argv);
736 }
737 p++;
738 daemon_over_rsh = 1;
739 }
740
741 if (argc < 2) {
742 usage(FERROR);
743 exit_cleanup(RERR_SYNTAX);
744 }
745
746 if (local_server) {
747 shell_machine = NULL;
748 shell_path = argv[argc-1];
749 } else {
750 *p = 0;
751 shell_machine = argv[argc-1];
752 shell_path = p+1;
753 }
754 argc--;
755 }
756 } else {
757 am_sender = 1;
758 local_server = 1;
759 shell_path = argv[argc-1];
760 }
761
762 if (shell_machine) {
763 p = strchr(shell_machine,'@');
764 if (p) {
765 *p = 0;
766 shell_user = shell_machine;
767 shell_machine = p+1;
768 }
769 }
770
771 if (verbose > 3) {
772 rprintf(FINFO,"cmd=%s machine=%s user=%s path=%s\n",
773 shell_cmd?shell_cmd:"",
774 shell_machine?shell_machine:"",
775 shell_user?shell_user:"",
776 shell_path?shell_path:"");
777 }
778
779 if (!am_sender && argc > 1) {
780 usage(FERROR);
781 exit_cleanup(RERR_SYNTAX);
782 }
783
784 if (argc == 0 && !am_sender) {
785 extern int list_only;
786 list_only = 1;
787 }
788
789 pid = do_cmd(shell_cmd,shell_machine,shell_user,shell_path,
790 &f_in,&f_out);
791
792 /* if we're running an rsync server on the remote host over a
793 remote shell command, we need to do the RSYNCD protocol first */
794 if (daemon_over_rsh) {
795 int tmpret;
796 tmpret = start_inband_exchange(shell_user, shell_path,
797 f_in, f_out, argc);
798 if (tmpret < 0)
799 return tmpret;
800 }
801
802 ret = client_run(f_in, f_out, pid, argc, argv);
803
804 fflush(stdout);
805 fflush(stderr);
806
807 return ret;
808}
809
810
811static RETSIGTYPE sigusr1_handler(int UNUSED(val)) {
812 exit_cleanup(RERR_SIGNAL);
813}
814
815static RETSIGTYPE sigusr2_handler(int UNUSED(val)) {
816 extern int log_got_error;
817 if (log_got_error) _exit(RERR_PARTIAL);
818 _exit(0);
819}
820
821static RETSIGTYPE sigchld_handler(int UNUSED(val)) {
822#ifdef WNOHANG
823 while (waitpid(-1, NULL, WNOHANG) > 0) ;
824#endif
825}
826
827
828/**
829 * This routine catches signals and tries to send them to gdb.
830 *
831 * Because it's called from inside a signal handler it ought not to
832 * use too many library routines.
833 *
834 * @todo Perhaps use "screen -X" instead/as well, to help people
835 * debugging without easy access to X. Perhaps use an environment
836 * variable, or just call a script?
837 *
838 * @todo The /proc/ magic probably only works on Linux (and
839 * Solaris?) Can we be more portable?
840 **/
841#ifdef MAINTAINER_MODE
842const char *get_panic_action(void)
843{
844 const char *cmd_fmt = getenv("RSYNC_PANIC_ACTION");
845
846 if (cmd_fmt)
847 return cmd_fmt;
848 else
849 return "xterm -display :0 -T Panic -n Panic "
850 "-e gdb /proc/%d/exe %d";
851}
852
853
854/**
855 * Handle a fatal signal by launching a debugger, controlled by $RSYNC_PANIC_ACTION.
856 *
857 * This signal handler is only installed if we were configured with
858 * --enable-maintainer-mode. Perhaps it should always be on and we
859 * should just look at the environment variable, but I'm a bit leery
860 * of a signal sending us into a busy loop.
861 **/
862static RETSIGTYPE rsync_panic_handler(int UNUSED(whatsig))
863{
864 char cmd_buf[300];
865 int ret;
866
867 sprintf(cmd_buf, get_panic_action(),
868 getpid(), getpid());
869
870 /* Unless we failed to execute gdb, we allow the process to
871 * continue. I'm not sure if that's right. */
872 ret = system(cmd_buf);
873 if (ret)
874 _exit(ret);
875}
876#endif
877
878
879int main(int argc,char *argv[])
880{
881 extern int am_root;
882 extern int orig_umask;
883 extern int dry_run;
884 extern int am_daemon;
885 extern int am_server;
886 int ret;
887 extern int write_batch;
888 int orig_argc;
889 char **orig_argv;
890
891 orig_argc = argc;
892 orig_argv = argv;
893
894 signal(SIGUSR1, sigusr1_handler);
895 signal(SIGUSR2, sigusr2_handler);
896 signal(SIGCHLD, sigchld_handler);
897#ifdef MAINTAINER_MODE
898 signal(SIGSEGV, rsync_panic_handler);
899 signal(SIGFPE, rsync_panic_handler);
900 signal(SIGABRT, rsync_panic_handler);
901 signal(SIGBUS, rsync_panic_handler);
902#endif /* def MAINTAINER_MODE */
903
904 starttime = time(NULL);
905 am_root = (getuid() == 0);
906
907 memset(&stats, 0, sizeof(stats));
908
909 if (argc < 2) {
910 usage(FERROR);
911 exit_cleanup(RERR_SYNTAX);
912 }
913
914 /* we set a 0 umask so that correct file permissions can be
915 carried across */
916 orig_umask = (int)umask(0);
917
918 if (!parse_arguments(&argc, (const char ***) &argv, 1)) {
919 /* FIXME: We ought to call the same error-handling
920 * code here, rather than relying on getopt. */
921 option_error();
922 exit_cleanup(RERR_SYNTAX);
923 }
924
925 signal(SIGINT,SIGNAL_CAST sig_int);
926 signal(SIGHUP,SIGNAL_CAST sig_int);
927 signal(SIGTERM,SIGNAL_CAST sig_int);
928
929 /* Ignore SIGPIPE; we consistently check error codes and will
930 * see the EPIPE. */
931 signal(SIGPIPE, SIG_IGN);
932
933 /* Initialize push_dir here because on some old systems getcwd
934 (implemented by forking "pwd" and reading its output) doesn't
935 work when there are other child processes. Also, on all systems
936 that implement getcwd that way "pwd" can't be found after chroot. */
937 push_dir(NULL,0);
938
939 if (write_batch && !am_server) {
940 write_batch_argvs_file(orig_argc, orig_argv);
941 }
942
943 if (am_daemon && !am_server)
944 return daemon_main();
945
946 if (argc < 1) {
947 usage(FERROR);
948 exit_cleanup(RERR_SYNTAX);
949 }
950
951 if (dry_run)
952 verbose = MAX(verbose,1);
953
954#ifndef SUPPORT_LINKS
955 if (!am_server && preserve_links) {
956 rprintf(FERROR,"ERROR: symbolic links not supported\n");
957 exit_cleanup(RERR_UNSUPPORTED);
958 }
959#endif
960
961 if (am_server) {
962 set_nonblocking(STDIN_FILENO);
963 set_nonblocking(STDOUT_FILENO);
964 if (am_daemon)
965 return start_daemon(STDIN_FILENO, STDOUT_FILENO);
966 start_server(STDIN_FILENO, STDOUT_FILENO, argc, argv);
967 }
968
969 ret = start_client(argc, argv);
970 if (ret == -1)
971 exit_cleanup(RERR_STARTCLIENT);
972 else
973 exit_cleanup(ret);
974
975 exit(ret);
976 /* NOTREACHED */
977}