damn.
[rsync/rsync.git] / main.c
... / ...
CommitLineData
1/*
2 Copyright (C) Andrew Tridgell 1996
3 Copyright (C) Paul Mackerras 1996
4
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18*/
19
20#include "rsync.h"
21
22time_t starttime = 0;
23
24struct stats stats;
25
26extern int verbose;
27
28static void report(int f)
29{
30 time_t t = time(NULL);
31 extern int am_server;
32 extern int am_sender;
33 extern int am_daemon;
34 extern int do_stats;
35 extern int remote_version;
36 int send_stats;
37
38 if (am_daemon) {
39 log_exit(0, __FILE__, __LINE__);
40 if (f == -1 || !am_sender) return;
41 }
42
43 send_stats = verbose || (remote_version >= 20);
44 if (am_server) {
45 if (am_sender && send_stats) {
46 int64 w;
47 /* store total_written in a temporary
48 because write_longint changes it */
49 w = stats.total_written;
50 write_longint(f,stats.total_read);
51 write_longint(f,w);
52 write_longint(f,stats.total_size);
53 }
54 return;
55 }
56
57 /* this is the client */
58
59 if (!am_sender && send_stats) {
60 int64 r;
61 stats.total_written = read_longint(f);
62 /* store total_read in a temporary, read_longint changes it */
63 r = read_longint(f);
64 stats.total_size = read_longint(f);
65 stats.total_read = r;
66 }
67
68 if (do_stats) {
69 if (!am_sender && !send_stats) {
70 /* missing the bytes written by the generator */
71 rprintf(FINFO, "\nCannot show stats as receiver because remote protocol version is less than 20\n");
72 rprintf(FINFO, "Use --stats -v to show stats\n");
73 return;
74 }
75 rprintf(FINFO,"\nNumber of files: %d\n", stats.num_files);
76 rprintf(FINFO,"Number of files transferred: %d\n",
77 stats.num_transferred_files);
78 rprintf(FINFO,"Total file size: %.0f bytes\n",
79 (double)stats.total_size);
80 rprintf(FINFO,"Total transferred file size: %.0f bytes\n",
81 (double)stats.total_transferred_size);
82 rprintf(FINFO,"Literal data: %.0f bytes\n",
83 (double)stats.literal_data);
84 rprintf(FINFO,"Matched data: %.0f bytes\n",
85 (double)stats.matched_data);
86 rprintf(FINFO,"File list size: %d\n", stats.flist_size);
87 rprintf(FINFO,"Total bytes written: %.0f\n",
88 (double)stats.total_written);
89 rprintf(FINFO,"Total bytes read: %.0f\n\n",
90 (double)stats.total_read);
91 }
92
93 if (verbose || do_stats) {
94 rprintf(FINFO,"wrote %.0f bytes read %.0f bytes %.2f bytes/sec\n",
95 (double)stats.total_written,
96 (double)stats.total_read,
97 (stats.total_written+stats.total_read)/(0.5 + (t-starttime)));
98 rprintf(FINFO,"total size is %.0f speedup is %.2f\n",
99 (double)stats.total_size,
100 (1.0*stats.total_size)/(stats.total_written+stats.total_read));
101 }
102
103 fflush(stdout);
104 fflush(stderr);
105}
106
107
108static int do_cmd(char *cmd,char *machine,char *user,char *path,int *f_in,int *f_out)
109{
110 char *args[100];
111 int i,argc=0, ret;
112 char *tok,*dir=NULL;
113 extern int local_server;
114 extern char *rsync_path;
115
116 if (!local_server) {
117 if (!cmd)
118 cmd = getenv(RSYNC_RSH_ENV);
119 if (!cmd)
120 cmd = RSYNC_RSH;
121 cmd = strdup(cmd);
122 if (!cmd)
123 goto oom;
124
125 for (tok=strtok(cmd," ");tok;tok=strtok(NULL," ")) {
126 args[argc++] = tok;
127 }
128
129#if HAVE_REMSH
130 /* remsh (on HPUX) takes the arguments the other way around */
131 args[argc++] = machine;
132 if (user) {
133 args[argc++] = "-l";
134 args[argc++] = user;
135 }
136#else
137 if (user) {
138 args[argc++] = "-l";
139 args[argc++] = user;
140 }
141 args[argc++] = machine;
142#endif
143
144 args[argc++] = rsync_path;
145
146 server_options(args,&argc);
147 }
148
149 args[argc++] = ".";
150
151 if (path && *path)
152 args[argc++] = path;
153
154 args[argc] = NULL;
155
156 if (verbose > 3) {
157 rprintf(FINFO,"cmd=");
158 for (i=0;i<argc;i++)
159 rprintf(FINFO,"%s ",args[i]);
160 rprintf(FINFO,"\n");
161 }
162
163 if (local_server) {
164 ret = local_child(argc, args, f_in, f_out);
165 } else {
166 ret = piped_child(args,f_in,f_out);
167 }
168
169 if (dir) free(dir);
170
171 return ret;
172
173oom:
174 out_of_memory("do_cmd");
175 return 0; /* not reached */
176}
177
178
179
180
181static char *get_local_name(struct file_list *flist,char *name)
182{
183 STRUCT_STAT st;
184 extern int orig_umask;
185
186 if (verbose > 2)
187 rprintf(FINFO,"get_local_name count=%d %s\n",
188 flist->count, NS(name));
189
190 if (!name)
191 return NULL;
192
193 if (do_stat(name,&st) == 0) {
194 if (S_ISDIR(st.st_mode)) {
195 if (!push_dir(name, 0)) {
196 rprintf(FERROR,"push_dir %s : %s (1)\n",
197 name,strerror(errno));
198 exit_cleanup(RERR_FILESELECT);
199 }
200 return NULL;
201 }
202 if (flist->count > 1) {
203 rprintf(FERROR,"ERROR: destination must be a directory when copying more than 1 file\n");
204 exit_cleanup(RERR_FILESELECT);
205 }
206 return name;
207 }
208
209 if (flist->count <= 1)
210 return name;
211
212 if (do_mkdir(name,0777 & ~orig_umask) != 0) {
213 rprintf(FERROR,"mkdir %s : %s (1)\n",name,strerror(errno));
214 exit_cleanup(RERR_FILEIO);
215 } else {
216 if (verbose > 0)
217 rprintf(FINFO,"created directory %s\n",name);
218 }
219
220 if (!push_dir(name, 0)) {
221 rprintf(FERROR,"push_dir %s : %s (2)\n",
222 name,strerror(errno));
223 exit_cleanup(RERR_FILESELECT);
224 }
225
226 return NULL;
227}
228
229
230
231
232static void do_server_sender(int f_in, int f_out, int argc,char *argv[])
233{
234 int i;
235 struct file_list *flist;
236 char *dir = argv[0];
237 extern int relative_paths;
238 extern int recurse;
239
240 if (verbose > 2)
241 rprintf(FINFO,"server_sender starting pid=%d\n",(int)getpid());
242
243 if (!relative_paths && !push_dir(dir, 0)) {
244 rprintf(FERROR,"push_dir %s: %s (3)\n",dir,strerror(errno));
245 exit_cleanup(RERR_FILESELECT);
246 }
247 argc--;
248 argv++;
249
250 if (strcmp(dir,".")) {
251 int l = strlen(dir);
252 if (strcmp(dir,"/") == 0)
253 l = 0;
254 for (i=0;i<argc;i++)
255 argv[i] += l+1;
256 }
257
258 if (argc == 0 && recurse) {
259 argc=1;
260 argv--;
261 argv[0] = ".";
262 }
263
264 flist = send_file_list(f_out,argc,argv);
265 if (!flist || flist->count == 0) {
266 exit_cleanup(0);
267 }
268
269 send_files(flist,f_out,f_in);
270 report(f_out);
271 io_flush();
272 exit_cleanup(0);
273}
274
275
276static int do_recv(int f_in,int f_out,struct file_list *flist,char *local_name)
277{
278 int pid;
279 int status=0;
280 int recv_pipe[2];
281 int error_pipe[2];
282 extern int preserve_hard_links;
283 extern int delete_after;
284 extern int recurse;
285 extern int delete_mode;
286 extern int remote_version;
287
288 if (preserve_hard_links)
289 init_hard_links(flist);
290
291 if (!delete_after) {
292 /* I moved this here from recv_files() to prevent a race condition */
293 if (recurse && delete_mode && !local_name && flist->count>0) {
294 delete_files(flist);
295 }
296 }
297
298 if (fd_pair(recv_pipe) < 0) {
299 rprintf(FERROR,"pipe failed in do_recv\n");
300 exit_cleanup(RERR_SOCKETIO);
301 }
302
303 if (fd_pair(error_pipe) < 0) {
304 rprintf(FERROR,"error pipe failed in do_recv\n");
305 exit_cleanup(RERR_SOCKETIO);
306 }
307
308 io_flush();
309
310 if ((pid=do_fork()) == 0) {
311 close(recv_pipe[0]);
312 close(error_pipe[0]);
313 if (f_in != f_out) close(f_out);
314
315 /* we can't let two processes write to the socket at one time */
316 io_multiplexing_close();
317
318 /* set place to send errors */
319 set_error_fd(error_pipe[1]);
320
321 recv_files(f_in,flist,local_name,recv_pipe[1]);
322 report(f_in);
323
324 write_int(recv_pipe[1],-1);
325 io_flush();
326 _exit(0);
327 }
328
329 close(recv_pipe[1]);
330 close(error_pipe[1]);
331 io_close_input(f_in);
332 if (f_in != f_out) close(f_in);
333
334 io_start_buffering(f_out);
335
336 io_set_error_fd(error_pipe[0]);
337
338 generate_files(f_out,flist,local_name,recv_pipe[0]);
339
340 read_int(recv_pipe[1]);
341 if (remote_version >= 24) {
342 /* send a final goodbye message */
343 write_int(f_out, -1);
344 }
345 io_flush();
346
347 wait_process(pid, &status);
348 return status;
349}
350
351
352static void do_server_recv(int f_in, int f_out, int argc,char *argv[])
353{
354 int status;
355 struct file_list *flist;
356 char *local_name=NULL;
357 char *dir = NULL;
358 extern int delete_mode;
359 extern int delete_excluded;
360 extern int am_daemon;
361 extern int module_id;
362 extern int am_sender;
363
364 if (verbose > 2)
365 rprintf(FINFO,"server_recv(%d) starting pid=%d\n",argc,(int)getpid());
366
367 if (am_daemon && lp_read_only(module_id) && !am_sender) {
368 rprintf(FERROR,"ERROR: module is read only\n");
369 exit_cleanup(RERR_SYNTAX);
370 return;
371 }
372
373
374 if (argc > 0) {
375 dir = argv[0];
376 argc--;
377 argv++;
378 if (!am_daemon && !push_dir(dir, 0)) {
379 rprintf(FERROR,"push_dir %s : %s (4)\n",
380 dir,strerror(errno));
381 exit_cleanup(RERR_FILESELECT);
382 }
383 }
384
385 if (delete_mode && !delete_excluded)
386 recv_exclude_list(f_in);
387
388 flist = recv_file_list(f_in);
389 if (!flist) {
390 rprintf(FERROR,"server_recv: recv_file_list error\n");
391 exit_cleanup(RERR_FILESELECT);
392 }
393
394 if (argc > 0) {
395 if (strcmp(dir,".")) {
396 argv[0] += strlen(dir);
397 if (argv[0][0] == '/') argv[0]++;
398 }
399 local_name = get_local_name(flist,argv[0]);
400 }
401
402 status = do_recv(f_in,f_out,flist,local_name);
403 exit_cleanup(status);
404}
405
406
407void start_server(int f_in, int f_out, int argc, char *argv[])
408{
409 extern int cvs_exclude;
410 extern int am_sender;
411 extern int remote_version;
412
413 setup_protocol(f_out, f_in);
414
415 if (remote_version >= 23)
416 io_start_multiplex_out(f_out);
417
418 if (am_sender) {
419 recv_exclude_list(f_in);
420 if (cvs_exclude)
421 add_cvs_excludes();
422 do_server_sender(f_in, f_out, argc, argv);
423 } else {
424 do_server_recv(f_in, f_out, argc, argv);
425 }
426 exit_cleanup(0);
427}
428
429int client_run(int f_in, int f_out, int pid, int argc, char *argv[])
430{
431 struct file_list *flist;
432 int status = 0, status2 = 0;
433 char *local_name = NULL;
434 extern int am_sender;
435 extern int list_only;
436 extern int remote_version;
437
438 setup_protocol(f_out,f_in);
439
440 if (remote_version >= 23)
441 io_start_multiplex_in(f_in);
442
443 if (am_sender) {
444 extern int cvs_exclude;
445 extern int delete_mode;
446 extern int delete_excluded;
447 if (cvs_exclude)
448 add_cvs_excludes();
449 if (delete_mode && !delete_excluded)
450 send_exclude_list(f_out);
451 flist = send_file_list(f_out,argc,argv);
452 if (verbose > 3)
453 rprintf(FINFO,"file list sent\n");
454
455 send_files(flist,f_out,f_in);
456 if (pid != -1) {
457 if (verbose > 3)
458 rprintf(FINFO,"client_run waiting on %d\n",pid);
459 io_flush();
460 wait_process(pid, &status);
461 }
462 report(-1);
463 if (remote_version >= 24) {
464 /* final goodbye message */
465 read_int(f_in);
466 }
467 exit_cleanup(status);
468 }
469
470 if (argc == 0) list_only = 1;
471
472 send_exclude_list(f_out);
473
474 flist = recv_file_list(f_in);
475 if (!flist || flist->count == 0) {
476 rprintf(FINFO,"client: nothing to do\n");
477 exit_cleanup(0);
478 }
479
480 local_name = get_local_name(flist,argv[0]);
481
482 status2 = do_recv(f_in,f_out,flist,local_name);
483
484 if (pid != -1) {
485 if (verbose > 3)
486 rprintf(FINFO,"client_run2 waiting on %d\n",pid);
487 io_flush();
488 wait_process(pid, &status);
489 }
490
491 return status | status2;
492}
493
494static char *find_colon(char *s)
495{
496 char *p, *p2;
497
498 p = strchr(s,':');
499 if (!p) return NULL;
500
501 /* now check to see if there is a / in the string before the : - if there is then
502 discard the colon on the assumption that the : is part of a filename */
503 p2 = strchr(s,'/');
504 if (p2 && p2 < p) return NULL;
505
506 return p;
507}
508
509static int start_client(int argc, char *argv[])
510{
511 char *p;
512 char *shell_machine = NULL;
513 char *shell_path = NULL;
514 char *shell_user = NULL;
515 int pid, ret;
516 int f_in,f_out;
517 extern int local_server;
518 extern int am_sender;
519 extern char *shell_cmd;
520 extern int rsync_port;
521
522 if (strncasecmp(URL_PREFIX, argv[0], strlen(URL_PREFIX)) == 0) {
523 char *host, *path;
524
525 host = argv[0] + strlen(URL_PREFIX);
526 p = strchr(host,'/');
527 if (p) {
528 *p = 0;
529 path = p+1;
530 } else {
531 path="";
532 }
533 p = strchr(host,':');
534 if (p) {
535 rsync_port = atoi(p+1);
536 *p = 0;
537 }
538 return start_socket_client(host, path, argc-1, argv+1);
539 }
540
541 p = find_colon(argv[0]);
542
543 if (p) {
544 if (p[1] == ':') {
545 *p = 0;
546 return start_socket_client(argv[0], p+2, argc-1, argv+1);
547 }
548
549 if (argc < 1) {
550 usage(FERROR);
551 exit_cleanup(RERR_SYNTAX);
552 }
553
554 am_sender = 0;
555 *p = 0;
556 shell_machine = argv[0];
557 shell_path = p+1;
558 argc--;
559 argv++;
560 } else {
561 am_sender = 1;
562
563 p = find_colon(argv[argc-1]);
564 if (!p) {
565 local_server = 1;
566 } else if (p[1] == ':') {
567 *p = 0;
568 return start_socket_client(argv[argc-1], p+2, argc-1, argv);
569 }
570
571 if (argc < 2) {
572 usage(FERROR);
573 exit_cleanup(RERR_SYNTAX);
574 }
575
576 if (local_server) {
577 shell_machine = NULL;
578 shell_path = argv[argc-1];
579 } else {
580 *p = 0;
581 shell_machine = argv[argc-1];
582 shell_path = p+1;
583 }
584 argc--;
585 }
586
587 if (shell_machine) {
588 p = strchr(shell_machine,'@');
589 if (p) {
590 *p = 0;
591 shell_user = shell_machine;
592 shell_machine = p+1;
593 }
594 }
595
596 if (verbose > 3) {
597 rprintf(FINFO,"cmd=%s machine=%s user=%s path=%s\n",
598 shell_cmd?shell_cmd:"",
599 shell_machine?shell_machine:"",
600 shell_user?shell_user:"",
601 shell_path?shell_path:"");
602 }
603
604 if (!am_sender && argc > 1) {
605 usage(FERROR);
606 exit_cleanup(RERR_SYNTAX);
607 }
608
609 pid = do_cmd(shell_cmd,shell_machine,shell_user,shell_path,&f_in,&f_out);
610
611 ret = client_run(f_in, f_out, pid, argc, argv);
612
613 fflush(stdout);
614 fflush(stderr);
615
616 return ret;
617}
618
619
620static RETSIGTYPE sigusr1_handler(int val) {
621 exit_cleanup(RERR_SIGNAL);
622}
623
624int main(int argc,char *argv[])
625{
626 extern int am_root;
627 extern int orig_umask;
628 extern int dry_run;
629 extern int am_daemon;
630 extern int am_server;
631
632 signal(SIGUSR1, sigusr1_handler);
633
634 starttime = time(NULL);
635 am_root = (getuid() == 0);
636
637 memset(&stats, 0, sizeof(stats));
638
639 if (argc < 2) {
640 usage(FERROR);
641 exit_cleanup(RERR_SYNTAX);
642 }
643
644 /* we set a 0 umask so that correct file permissions can be
645 carried across */
646 orig_umask = (int)umask(0);
647
648 if (!parse_arguments(argc, argv, 1)) {
649 exit_cleanup(RERR_SYNTAX);
650 }
651
652 argc -= optind;
653 argv += optind;
654 optind = 0;
655
656 signal(SIGCHLD,SIG_IGN);
657 signal(SIGINT,SIGNAL_CAST sig_int);
658 signal(SIGPIPE,SIGNAL_CAST sig_int);
659 signal(SIGHUP,SIGNAL_CAST sig_int);
660 signal(SIGTERM,SIGNAL_CAST sig_int);
661
662 /* Initialize push_dir here because on some old systems getcwd
663 (implemented by forking "pwd" and reading its output) doesn't
664 work when there are other child processes. Also, on all systems
665 that implement getcwd that way "pwd" can't be found after chroot. */
666 push_dir(NULL,0);
667
668 if (am_daemon) {
669 return daemon_main();
670 }
671
672 if (argc < 1) {
673 usage(FERROR);
674 exit_cleanup(RERR_SYNTAX);
675 }
676
677 if (dry_run)
678 verbose = MAX(verbose,1);
679
680#ifndef SUPPORT_LINKS
681 if (!am_server && preserve_links) {
682 rprintf(FERROR,"ERROR: symbolic links not supported\n");
683 exit_cleanup(RERR_UNSUPPORTED);
684 }
685#endif
686
687 if (am_server) {
688 start_server(STDIN_FILENO, STDOUT_FILENO, argc, argv);
689 }
690
691 return start_client(argc, argv);
692}
693