From b35d0d8e9ae9c5407c9f781b545f8a66b9caa9d0 Mon Sep 17 00:00:00 2001 From: Martin Pool Date: Mon, 8 Apr 2002 04:10:20 +0000 Subject: [PATCH] Split code out into separate files and remove some global variables to reduce symbol dependencies between files and therefore make it easier to write unit tests. The diff is large, but the actual code changes are pretty small. --- .cvsignore | 1 + Makefile.in | 18 ++++++- clientserver.c | 2 +- generator.c | 3 +- log.c | 1 + main.c | 3 +- options.c | 1 + pipe.c | 127 +++++++++++++++++++++++++++++++++++++++++++++++++ progress.c | 96 +++++++++++++++++++++++++++++++++++++ receiver.c | 10 ++-- 10 files changed, 252 insertions(+), 10 deletions(-) create mode 100644 pipe.c create mode 100644 progress.c diff --git a/.cvsignore b/.cvsignore index a4cd5802..cda7639f 100644 --- a/.cvsignore +++ b/.cvsignore @@ -8,6 +8,7 @@ config.log config.status conftest.c conftest.log +dox gmon.out rsync shconfig diff --git a/Makefile.in b/Makefile.in index 4fcf2c15..535dacea 100644 --- a/Makefile.in +++ b/Makefile.in @@ -32,15 +32,16 @@ ZLIBOBJ=zlib/deflate.o zlib/infblock.o zlib/infcodes.o zlib/inffast.o \ OBJS1=rsync.o generator.o receiver.o cleanup.o sender.o exclude.o util.o main.o checksum.o match.o syscall.o log.o backup.o OBJS2=options.o flist.o io.o compat.o hlink.o token.o uidlist.o socket.o fileio.o batch.o \ clientname.o +OBJS3=progress.o pipe.o DAEMON_OBJ = params.o loadparm.o clientserver.o access.o connection.o authenticate.o popt_OBJS=popt/findme.o popt/popt.o popt/poptconfig.o \ popt/popthelp.o popt/poptparse.o -OBJS=$(OBJS1) $(OBJS2) $(DAEMON_OBJ) $(LIBOBJ) $(ZLIBOBJ) @BUILD_POPT@ +OBJS=$(OBJS1) $(OBJS2) $(OBJS3) $(DAEMON_OBJ) $(LIBOBJ) $(ZLIBOBJ) @BUILD_POPT@ TLS_OBJ = tls.o syscall.o lib/permstring.o # Programs we must have to run the test cases -CHECK_PROGS = rsync tls getgroups trimslash +CHECK_PROGS = rsync tls getgroups trimslash t_unsafe # note that the -I. is needed to handle config.h when using VPATH .c.o: @@ -79,6 +80,10 @@ TRIMSLASH_OBJ = trimslash.o syscall.o trimslash: $(TRIMSLASH_OBJ) $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $(TRIMSLASH_OBJ) $(LIBS) +T_UNSAFE_OBJ = t_unsafe.o syscall.o util.o t_stub.o lib/compat.o +t_unsafe: $(T_UNSAFE_OBJ) + $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $(T_UNSAFE_OBJ) $(LIBS) + # I don't like these rules because CVS can skew the timestamps and # produce spurious warnings, and also make "make install" fail if the # source directory can no longer be found. Since we don't rebuild @@ -171,3 +176,12 @@ rsync.ps: rsync.dvi rsync.pdf: doc/rsync.texinfo texi2dvi -o $@ --pdf $< + + +doxygen: + cd $(srcdir) && rm dox/html/* && doxygen + +# for maintainers only +doxygen-upload: + rsync -avzv $(srcdir)/dox/html/ --delete \ + samba.org:/home/httpd/html/rsync/doxygen/head/ diff --git a/clientserver.c b/clientserver.c index 40a43768..9cda2a0b 100644 --- a/clientserver.c +++ b/clientserver.c @@ -27,7 +27,7 @@ extern int read_only; extern int verbose; extern int rsync_port; char *auth_user; -int sanitize_paths = 0; +extern int sanitize_paths; /** * Run a client connected to an rsyncd. The alternative to this diff --git a/generator.c b/generator.c index c1b94fd8..8e4ede09 100644 --- a/generator.c +++ b/generator.c @@ -239,6 +239,7 @@ void recv_generator(char *fname,struct file_list *flist,int i,int f_out) extern int list_only; extern int preserve_perms; extern int only_existing; + extern int orig_umask; if (list_only) return; @@ -281,7 +282,7 @@ void recv_generator(char *fname,struct file_list *flist,int i,int f_out) } if (statret != 0 && do_mkdir(fname,file->mode) != 0 && errno != EEXIST) { if (!(relative_paths && errno==ENOENT && - create_directory_path(fname)==0 && + create_directory_path(fname, orig_umask)==0 && do_mkdir(fname,file->mode)==0)) { rprintf(FERROR, RSYNC_NAME ": recv_generator: mkdir \"%s\": %s (2)\n", fname,strerror(errno)); diff --git a/log.c b/log.c index 411889c1..e79bc220 100644 --- a/log.c +++ b/log.c @@ -30,6 +30,7 @@ static char *logfname; static FILE *logfile; static int log_error_fd = -1; +struct stats stats; int log_got_error=0; diff --git a/main.c b/main.c index 86eca2ca..b946e754 100644 --- a/main.c +++ b/main.c @@ -23,8 +23,7 @@ time_t starttime = 0; -struct stats stats; - +extern struct stats stats; extern int verbose; static void show_malloc_stats(void); diff --git a/options.c b/options.c index 22d332c9..a928316b 100644 --- a/options.c +++ b/options.c @@ -88,6 +88,7 @@ int modify_window=0; #endif int blocking_io=-1; + /** Network address family. **/ #ifdef INET6 int default_af_hint = 0; /* Any protocol */ diff --git a/pipe.c b/pipe.c new file mode 100644 index 00000000..e2e88ec3 --- /dev/null +++ b/pipe.c @@ -0,0 +1,127 @@ +#include "rsync.h" + +/** + * Create a child connected to use on stdin/stdout. + * + * This is derived from CVS code + * + * Note that in the child STDIN is set to blocking and STDOUT + * is set to non-blocking. This is necessary as rsh relies on stdin being blocking + * and ssh relies on stdout being non-blocking + * + * If blocking_io is set then use blocking io on both fds. That can be + * used to cope with badly broken rsh implementations like the one on + * Solaris. + **/ +pid_t piped_child(char **command, int *f_in, int *f_out) +{ + pid_t pid; + int to_child_pipe[2]; + int from_child_pipe[2]; + extern int blocking_io; + + if (verbose >= 2) { + print_child_argv(command); + } + + if (fd_pair(to_child_pipe) < 0 || fd_pair(from_child_pipe) < 0) { + rprintf(FERROR, "pipe: %s\n", strerror(errno)); + exit_cleanup(RERR_IPC); + } + + + pid = do_fork(); + if (pid == -1) { + rprintf(FERROR, "fork: %s\n", strerror(errno)); + exit_cleanup(RERR_IPC); + } + + if (pid == 0) { + extern int orig_umask; + if (dup2(to_child_pipe[0], STDIN_FILENO) < 0 || + close(to_child_pipe[1]) < 0 || + close(from_child_pipe[0]) < 0 || + dup2(from_child_pipe[1], STDOUT_FILENO) < 0) { + rprintf(FERROR, "Failed to dup/close : %s\n", + strerror(errno)); + exit_cleanup(RERR_IPC); + } + if (to_child_pipe[0] != STDIN_FILENO) + close(to_child_pipe[0]); + if (from_child_pipe[1] != STDOUT_FILENO) + close(from_child_pipe[1]); + umask(orig_umask); + set_blocking(STDIN_FILENO); + if (blocking_io) { + set_blocking(STDOUT_FILENO); + } + execvp(command[0], command); + rprintf(FERROR, "Failed to exec %s : %s\n", + command[0], strerror(errno)); + exit_cleanup(RERR_IPC); + } + + if (close(from_child_pipe[1]) < 0 || close(to_child_pipe[0]) < 0) { + rprintf(FERROR, "Failed to close : %s\n", strerror(errno)); + exit_cleanup(RERR_IPC); + } + + *f_in = from_child_pipe[0]; + *f_out = to_child_pipe[1]; + + return pid; +} + +pid_t local_child(int argc, char **argv,int *f_in,int *f_out, + int (*child_main)(int, char **)) +{ + pid_t pid; + int to_child_pipe[2]; + int from_child_pipe[2]; + extern int read_batch; /* dw */ + + if (fd_pair(to_child_pipe) < 0 || + fd_pair(from_child_pipe) < 0) { + rprintf(FERROR,"pipe: %s\n",strerror(errno)); + exit_cleanup(RERR_IPC); + } + + + pid = do_fork(); + if (pid == -1) { + rprintf(FERROR,"fork: %s\n",strerror(errno)); + exit_cleanup(RERR_IPC); + } + + if (pid == 0) { + extern int am_sender; + extern int am_server; + + am_sender = read_batch ? 0 : !am_sender; + am_server = 1; + + if (dup2(to_child_pipe[0], STDIN_FILENO) < 0 || + close(to_child_pipe[1]) < 0 || + close(from_child_pipe[0]) < 0 || + dup2(from_child_pipe[1], STDOUT_FILENO) < 0) { + rprintf(FERROR,"Failed to dup/close : %s\n",strerror(errno)); + exit_cleanup(RERR_IPC); + } + if (to_child_pipe[0] != STDIN_FILENO) close(to_child_pipe[0]); + if (from_child_pipe[1] != STDOUT_FILENO) close(from_child_pipe[1]); + child_main(argc, argv); + } + + if (close(from_child_pipe[1]) < 0 || + close(to_child_pipe[0]) < 0) { + rprintf(FERROR,"Failed to close : %s\n",strerror(errno)); + exit_cleanup(RERR_IPC); + } + + *f_in = from_child_pipe[0]; + *f_out = to_child_pipe[1]; + + return pid; +} + + diff --git a/progress.c b/progress.c new file mode 100644 index 00000000..004b5d1b --- /dev/null +++ b/progress.c @@ -0,0 +1,96 @@ +#include "rsync.h" + +static OFF_T last_ofs; +static struct timeval print_time; +static struct timeval start_time; +static OFF_T start_ofs; + +static unsigned long msdiff(struct timeval *t1, struct timeval *t2) +{ + return (t2->tv_sec - t1->tv_sec) * 1000 + + (t2->tv_usec - t1->tv_usec) / 1000; +} + + +/** + * @param ofs Current position in file + * @param size Total size of file + * @param is_last True if this is the last time progress will be + * printed for this file, so we should output a newline. (Not + * necessarily the same as all bytes being received.) + **/ +static void rprint_progress(OFF_T ofs, OFF_T size, struct timeval *now, + int is_last) +{ + int pct = (ofs == size) ? 100 : (int)((100.0*ofs)/size); + unsigned long diff = msdiff(&start_time, now); + double rate = diff ? (double) (ofs-start_ofs) * 1000.0 / diff / 1024.0 : 0; + const char *units; + /* If we've finished transferring this file, show the time taken; + * otherwise show expected time to complete. That's kind of + * inconsistent, but people can probably cope. Hopefully we'll + * get more consistent and complete progress reporting soon. -- + * mbp */ + double remain = is_last + ? (double) diff / 1000.0 + : rate ? (double) (size-ofs) / rate / 1000.0 : 0.0; + int remain_h, remain_m, remain_s; + + if (rate > 1024*1024) { + rate /= 1024.0 * 1024.0; + units = "GB/s"; + } else if (rate > 1024) { + rate /= 1024.0; + units = "MB/s"; + } else { + units = "kB/s"; + } + + remain_s = (int) remain % 60; + remain_m = (int) (remain / 60.0) % 60; + remain_h = (int) (remain / 3600.0); + + rprintf(FINFO, "%12.0f %3d%% %7.2f%s %4d:%02d:%02d%s", + (double) ofs, pct, rate, units, + remain_h, remain_m, remain_s, + is_last ? "\n" : "\r"); +} + +void end_progress(OFF_T size) +{ + extern int do_progress, am_server; + + if (do_progress && !am_server) { + struct timeval now; + gettimeofday(&now, NULL); + rprint_progress(size, size, &now, True); + } + last_ofs = 0; + start_ofs = 0; + print_time.tv_sec = print_time.tv_usec = 0; + start_time.tv_sec = start_time.tv_usec = 0; +} + +void show_progress(OFF_T ofs, OFF_T size) +{ + extern int do_progress, am_server; + struct timeval now; + + gettimeofday(&now, NULL); + + if (!start_time.tv_sec && !start_time.tv_usec) { + start_time.tv_sec = now.tv_sec; + start_time.tv_usec = now.tv_usec; + start_ofs = ofs; + } + + if (do_progress + && !am_server + && ofs > last_ofs + 1000 + && msdiff(&print_time, &now) > 250) { + rprint_progress(ofs, size, &now, False); + last_ofs = ofs; + print_time.tv_sec = now.tv_sec; + print_time.tv_usec = now.tv_usec; + } +} diff --git a/receiver.c b/receiver.c index dbd8bfdb..14282338 100644 --- a/receiver.c +++ b/receiver.c @@ -297,9 +297,10 @@ static int receive_data(int f_in,struct map_struct *buf,int fd,char *fname, } -/* main routine for receiver process. Receiver process runs on the - same host as the generator process. */ - +/** + * main routine for receiver process. + * + * Receiver process runs on the same host as the generator process. */ int recv_files(int f_in,struct file_list *flist,char *local_name,int f_gen) { int fd1,fd2; @@ -317,6 +318,7 @@ int recv_files(int f_in,struct file_list *flist,char *local_name,int f_gen) extern struct stats stats; extern int preserve_perms; extern int delete_after; + extern int orig_umask; struct stats initial_stats; if (verbose > 2) { @@ -434,7 +436,7 @@ int recv_files(int f_in,struct file_list *flist,char *local_name,int f_gen) because their information should have been previously transferred, but that may not be the case with -R */ if (fd2 == -1 && relative_paths && errno == ENOENT && - create_directory_path(fnametmp) == 0) { + create_directory_path(fnametmp, orig_umask) == 0) { strlcpy(fnametmp, template, sizeof(fnametmp)); fd2 = do_mkstemp(fnametmp, file->mode & INITACCESSPERMS); } -- 2.34.1