1 /* -*- c-file-style: "linux" -*-
3 Copyright (C) 1996-2001 by Andrew Tridgell
4 Copyright (C) Paul Mackerras 1996
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.
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.
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.
30 /****************************************************************************
31 wait for a process to exit, calling io_flush while waiting
32 ****************************************************************************/
33 void wait_process(pid_t pid
, int *status
)
35 while (waitpid(pid
, status
, WNOHANG
) == 0) {
39 *status
= WEXITSTATUS(*status
);
42 static void report(int f
)
44 time_t t
= time(NULL
);
49 extern int remote_version
;
53 log_exit(0, __FILE__
, __LINE__
);
54 if (f
== -1 || !am_sender
) return;
57 send_stats
= verbose
|| (remote_version
>= 20);
59 if (am_sender
&& send_stats
) {
61 /* store total_written in a temporary
62 because write_longint changes it */
63 w
= stats
.total_written
;
64 write_longint(f
,stats
.total_read
);
66 write_longint(f
,stats
.total_size
);
71 /* this is the client */
73 if (!am_sender
&& send_stats
) {
75 stats
.total_written
= read_longint(f
);
76 /* store total_read in a temporary, read_longint changes it */
78 stats
.total_size
= read_longint(f
);
83 if (!am_sender
&& !send_stats
) {
84 /* missing the bytes written by the generator */
85 rprintf(FINFO
, "\nCannot show stats as receiver because remote protocol version is less than 20\n");
86 rprintf(FINFO
, "Use --stats -v to show stats\n");
89 rprintf(FINFO
,"\nNumber of files: %d\n", stats
.num_files
);
90 rprintf(FINFO
,"Number of files transferred: %d\n",
91 stats
.num_transferred_files
);
92 rprintf(FINFO
,"Total file size: %.0f bytes\n",
93 (double)stats
.total_size
);
94 rprintf(FINFO
,"Total transferred file size: %.0f bytes\n",
95 (double)stats
.total_transferred_size
);
96 rprintf(FINFO
,"Literal data: %.0f bytes\n",
97 (double)stats
.literal_data
);
98 rprintf(FINFO
,"Matched data: %.0f bytes\n",
99 (double)stats
.matched_data
);
100 rprintf(FINFO
,"File list size: %d\n", stats
.flist_size
);
101 rprintf(FINFO
,"Total bytes written: %.0f\n",
102 (double)stats
.total_written
);
103 rprintf(FINFO
,"Total bytes read: %.0f\n\n",
104 (double)stats
.total_read
);
107 if (verbose
|| do_stats
) {
108 rprintf(FINFO
,"wrote %.0f bytes read %.0f bytes %.2f bytes/sec\n",
109 (double)stats
.total_written
,
110 (double)stats
.total_read
,
111 (stats
.total_written
+stats
.total_read
)/(0.5 + (t
-starttime
)));
112 rprintf(FINFO
,"total size is %.0f speedup is %.2f\n",
113 (double)stats
.total_size
,
114 (1.0*stats
.total_size
)/(stats
.total_written
+stats
.total_read
));
122 /* Start the remote shell. */
123 /* TODO: When the shell exits, look at its return value, as this may
124 * well tell us if something went wrong in trying to connect to the
125 * remote machine. Although it doesn't seem to be specified anywhere,
126 * ssh and the shell seem to return these values:
128 * 124 if the command exited with status 255
129 * 125 if the command is killed by a signal
130 * 126 if the command cannot be run
131 * 127 if the command is not found
133 * and we could use this to give a better explanation if the remote
134 * command is not found.
136 static int do_cmd(char *cmd
,char *machine
,char *user
,char *path
,int *f_in
,int *f_out
)
141 extern int local_server
;
142 extern char *rsync_path
;
143 extern int blocking_io
;
147 cmd
= getenv(RSYNC_RSH_ENV
);
154 for (tok
=strtok(cmd
," ");tok
;tok
=strtok(NULL
," ")) {
159 /* remsh (on HPUX) takes the arguments the other way around */
160 args
[argc
++] = machine
;
170 args
[argc
++] = machine
;
173 args
[argc
++] = rsync_path
;
175 server_options(args
,&argc
);
178 if (strcmp(cmd
, RSYNC_RSH
) == 0) blocking_io
= 1;
189 rprintf(FINFO
,"cmd=");
191 rprintf(FINFO
,"%s ",args
[i
]);
196 ret
= local_child(argc
, args
, f_in
, f_out
);
198 ret
= piped_child(args
,f_in
,f_out
);
206 out_of_memory("do_cmd");
207 return 0; /* not reached */
213 static char *get_local_name(struct file_list
*flist
,char *name
)
216 extern int orig_umask
;
219 rprintf(FINFO
,"get_local_name count=%d %s\n",
220 flist
->count
, NS(name
));
225 if (do_stat(name
,&st
) == 0) {
226 if (S_ISDIR(st
.st_mode
)) {
227 if (!push_dir(name
, 0)) {
228 rprintf(FERROR
,"push_dir %s : %s (1)\n",
229 name
,strerror(errno
));
230 exit_cleanup(RERR_FILESELECT
);
234 if (flist
->count
> 1) {
235 rprintf(FERROR
,"ERROR: destination must be a directory when copying more than 1 file\n");
236 exit_cleanup(RERR_FILESELECT
);
241 if (flist
->count
<= 1)
244 if (do_mkdir(name
,0777 & ~orig_umask
) != 0) {
245 rprintf(FERROR
,"mkdir %s : %s (1)\n",name
,strerror(errno
));
246 exit_cleanup(RERR_FILEIO
);
249 rprintf(FINFO
,"created directory %s\n",name
);
252 if (!push_dir(name
, 0)) {
253 rprintf(FERROR
,"push_dir %s : %s (2)\n",
254 name
,strerror(errno
));
255 exit_cleanup(RERR_FILESELECT
);
264 static void do_server_sender(int f_in
, int f_out
, int argc
,char *argv
[])
267 struct file_list
*flist
;
269 extern int relative_paths
;
271 extern int remote_version
;
274 rprintf(FINFO
,"server_sender starting pid=%d\n",(int)getpid());
276 if (!relative_paths
&& !push_dir(dir
, 0)) {
277 rprintf(FERROR
,"push_dir %s: %s (3)\n",dir
,strerror(errno
));
278 exit_cleanup(RERR_FILESELECT
);
283 if (strcmp(dir
,".")) {
285 if (strcmp(dir
,"/") == 0)
291 if (argc
== 0 && recurse
) {
297 flist
= send_file_list(f_out
,argc
,argv
);
298 if (!flist
|| flist
->count
== 0) {
302 send_files(flist
,f_out
,f_in
);
305 if (remote_version
>= 24) {
306 /* final goodbye message */
314 static int do_recv(int f_in
,int f_out
,struct file_list
*flist
,char *local_name
)
320 extern int preserve_hard_links
;
321 extern int delete_after
;
323 extern int delete_mode
;
324 extern int remote_version
;
326 if (preserve_hard_links
)
327 init_hard_links(flist
);
330 /* I moved this here from recv_files() to prevent a race condition */
331 if (recurse
&& delete_mode
&& !local_name
&& flist
->count
>0) {
336 if (fd_pair(recv_pipe
) < 0) {
337 rprintf(FERROR
,"pipe failed in do_recv\n");
338 exit_cleanup(RERR_SOCKETIO
);
341 if (fd_pair(error_pipe
) < 0) {
342 rprintf(FERROR
,"error pipe failed in do_recv\n");
343 exit_cleanup(RERR_SOCKETIO
);
348 if ((pid
=do_fork()) == 0) {
350 close(error_pipe
[0]);
351 if (f_in
!= f_out
) close(f_out
);
353 /* we can't let two processes write to the socket at one time */
354 io_multiplexing_close();
356 /* set place to send errors */
357 set_error_fd(error_pipe
[1]);
359 recv_files(f_in
,flist
,local_name
,recv_pipe
[1]);
363 write_int(recv_pipe
[1],1);
366 /* finally we go to sleep until our parent kills us
367 with a USR2 signal. We sleep for a short time as on
368 some OSes a signal won't interrupt a sleep! */
369 while (1) msleep(20);
373 close(error_pipe
[1]);
374 if (f_in
!= f_out
) close(f_in
);
376 io_start_buffering(f_out
);
378 io_set_error_fd(error_pipe
[0]);
380 generate_files(f_out
,flist
,local_name
,recv_pipe
[0]);
382 read_int(recv_pipe
[0]);
384 if (remote_version
>= 24) {
385 /* send a final goodbye message */
386 write_int(f_out
, -1);
391 wait_process(pid
, &status
);
396 static void do_server_recv(int f_in
, int f_out
, int argc
,char *argv
[])
399 struct file_list
*flist
;
400 char *local_name
=NULL
;
402 extern int delete_mode
;
403 extern int delete_excluded
;
404 extern int am_daemon
;
405 extern int module_id
;
406 extern int am_sender
;
409 rprintf(FINFO
,"server_recv(%d) starting pid=%d\n",argc
,(int)getpid());
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
);
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
);
429 if (delete_mode
&& !delete_excluded
)
430 recv_exclude_list(f_in
);
432 flist
= recv_file_list(f_in
);
434 rprintf(FERROR
,"server_recv: recv_file_list error\n");
435 exit_cleanup(RERR_FILESELECT
);
439 if (strcmp(dir
,".")) {
440 argv
[0] += strlen(dir
);
441 if (argv
[0][0] == '/') argv
[0]++;
443 local_name
= get_local_name(flist
,argv
[0]);
446 status
= do_recv(f_in
,f_out
,flist
,local_name
);
447 exit_cleanup(status
);
451 void start_server(int f_in
, int f_out
, int argc
, char *argv
[])
453 extern int cvs_exclude
;
454 extern int am_sender
;
455 extern int remote_version
;
457 setup_protocol(f_out
, f_in
);
459 set_nonblocking(f_in
);
460 set_nonblocking(f_out
);
462 if (remote_version
>= 23)
463 io_start_multiplex_out(f_out
);
466 recv_exclude_list(f_in
);
469 do_server_sender(f_in
, f_out
, argc
, argv
);
471 do_server_recv(f_in
, f_out
, argc
, argv
);
478 * This is called once the connection has been negotiated. It is used
479 * for rsyncd, remote-shell, and local connections.
481 int client_run(int f_in
, int f_out
, int pid
, int argc
, char *argv
[])
483 struct file_list
*flist
;
484 int status
= 0, status2
= 0;
485 char *local_name
= NULL
;
486 extern int am_sender
;
487 extern int remote_version
;
489 set_nonblocking(f_in
);
490 set_nonblocking(f_out
);
492 setup_protocol(f_out
,f_in
);
494 if (remote_version
>= 23)
495 io_start_multiplex_in(f_in
);
498 extern int cvs_exclude
;
499 extern int delete_mode
;
500 extern int delete_excluded
;
503 if (delete_mode
&& !delete_excluded
)
504 send_exclude_list(f_out
);
505 flist
= send_file_list(f_out
,argc
,argv
);
507 rprintf(FINFO
,"file list sent\n");
509 send_files(flist
,f_out
,f_in
);
512 rprintf(FINFO
,"client_run waiting on %d\n",pid
);
514 wait_process(pid
, &status
);
516 if (remote_version
>= 24) {
517 /* final goodbye message */
521 exit_cleanup(status
);
525 extern int list_only
;
529 send_exclude_list(f_out
);
531 flist
= recv_file_list(f_in
);
532 if (!flist
|| flist
->count
== 0) {
533 rprintf(FINFO
, "client: nothing to do: "
534 "perhaps you need to specify some filenames or "
535 "the --recursive option?\n");
539 local_name
= get_local_name(flist
,argv
[0]);
541 status2
= do_recv(f_in
,f_out
,flist
,local_name
);
545 rprintf(FINFO
,"client_run2 waiting on %d\n",pid
);
547 wait_process(pid
, &status
);
550 return status
| status2
;
553 static char *find_colon(char *s
)
560 /* now check to see if there is a / in the string before the : - if there is then
561 discard the colon on the assumption that the : is part of a filename */
563 if (p2
&& p2
< p
) return NULL
;
570 * Start a client for either type of remote connection. Work out
571 * whether the arguments request a remote shell or rsyncd connection,
572 * and call the appropriate connection function, then run_client.
574 static int start_client(int argc
, char *argv
[])
577 char *shell_machine
= NULL
;
578 char *shell_path
= NULL
;
579 char *shell_user
= NULL
;
582 extern int local_server
;
583 extern int am_sender
;
584 extern char *shell_cmd
;
585 extern int rsync_port
;
586 char *argv0
= strdup(argv
[0]);
588 if (strncasecmp(URL_PREFIX
, argv0
, strlen(URL_PREFIX
)) == 0) {
591 host
= argv0
+ strlen(URL_PREFIX
);
592 p
= strchr(host
,'/');
599 p
= strchr(host
,':');
601 rsync_port
= atoi(p
+1);
604 return start_socket_client(host
, path
, argc
-1, argv
+1);
607 p
= find_colon(argv0
);
612 return start_socket_client(argv0
, p
+2, argc
-1, argv
+1);
617 exit_cleanup(RERR_SYNTAX
);
622 shell_machine
= argv0
;
629 p
= find_colon(argv
[argc
-1]);
632 } else if (p
[1] == ':') {
634 return start_socket_client(argv
[argc
-1], p
+2, argc
-1, argv
);
639 exit_cleanup(RERR_SYNTAX
);
643 shell_machine
= NULL
;
644 shell_path
= argv
[argc
-1];
647 shell_machine
= argv
[argc
-1];
654 p
= strchr(shell_machine
,'@');
657 shell_user
= shell_machine
;
663 rprintf(FINFO
,"cmd=%s machine=%s user=%s path=%s\n",
664 shell_cmd
?shell_cmd
:"",
665 shell_machine
?shell_machine
:"",
666 shell_user
?shell_user
:"",
667 shell_path
?shell_path
:"");
670 if (!am_sender
&& argc
> 1) {
672 exit_cleanup(RERR_SYNTAX
);
675 if (argc
== 0 && !am_sender
) {
676 extern int list_only
;
680 pid
= do_cmd(shell_cmd
,shell_machine
,shell_user
,shell_path
,&f_in
,&f_out
);
682 ret
= client_run(f_in
, f_out
, pid
, argc
, argv
);
691 static RETSIGTYPE
sigusr1_handler(int val
) {
692 exit_cleanup(RERR_SIGNAL
);
695 static RETSIGTYPE
sigusr2_handler(int val
) {
699 int main(int argc
,char *argv
[])
702 extern int orig_umask
;
704 extern int am_daemon
;
705 extern int am_server
;
707 signal(SIGUSR1
, sigusr1_handler
);
708 signal(SIGUSR2
, sigusr2_handler
);
710 starttime
= time(NULL
);
711 am_root
= (getuid() == 0);
713 memset(&stats
, 0, sizeof(stats
));
717 exit_cleanup(RERR_SYNTAX
);
720 /* we set a 0 umask so that correct file permissions can be
722 orig_umask
= (int)umask(0);
724 if (!parse_arguments(argc
, argv
, 1)) {
725 /* FIXME: We ought to call the same error-handling
726 * code here, rather than relying on getopt. */
727 /* option_error(); */
728 exit_cleanup(RERR_SYNTAX
);
735 signal(SIGCHLD
,SIG_IGN
);
736 signal(SIGINT
,SIGNAL_CAST sig_int
);
737 signal(SIGPIPE
,SIGNAL_CAST sig_int
);
738 signal(SIGHUP
,SIGNAL_CAST sig_int
);
739 signal(SIGTERM
,SIGNAL_CAST sig_int
);
741 /* Initialize push_dir here because on some old systems getcwd
742 (implemented by forking "pwd" and reading its output) doesn't
743 work when there are other child processes. Also, on all systems
744 that implement getcwd that way "pwd" can't be found after chroot. */
748 return daemon_main();
753 exit_cleanup(RERR_SYNTAX
);
757 verbose
= MAX(verbose
,1);
759 #ifndef SUPPORT_LINKS
760 if (!am_server
&& preserve_links
) {
761 rprintf(FERROR
,"ERROR: symbolic links not supported\n");
762 exit_cleanup(RERR_UNSUPPORTED
);
767 set_nonblocking(STDIN_FILENO
);
768 set_nonblocking(STDOUT_FILENO
);
769 start_server(STDIN_FILENO
, STDOUT_FILENO
, argc
, argv
);
772 return start_client(argc
, argv
);