X-Git-Url: https://mattmccutchen.net/rsync/rsync.git/blobdiff_plain/6bf32edb8c335fac92ea6ab29de96cd59d986ade..0f78b81511be65d8fe21af1e6ac674f9e80ac29d:/main.c diff --git a/main.c b/main.c index f609bd7d..1237447d 100644 --- a/main.c +++ b/main.c @@ -1,23 +1,25 @@ -/* -*- c-file-style: "linux" -*- - - Copyright (C) 1996-2001 by Andrew Tridgell - Copyright (C) Paul Mackerras 1996 - Copyright (C) 2001, 2002 by Martin Pool - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. -*/ +/* + * The startup routines, including main(), for rsync. + * + * Copyright (C) 1996-2001 Andrew Tridgell + * Copyright (C) 1996 Paul Mackerras + * Copyright (C) 2001, 2002 Martin Pool + * Copyright (C) 2003, 2004, 2005, 2006 Wayne Davison + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ #include "rsync.h" #if defined CONFIG_LOCALE && defined HAVE_LOCALE_H @@ -40,8 +42,8 @@ extern int kluge_around_eof; extern int do_stats; extern int log_got_error; extern int module_id; -extern int orig_umask; extern int copy_links; +extern int copy_dirlinks; extern int keep_dirlinks; extern int preserve_hard_links; extern int protocol_version; @@ -62,12 +64,22 @@ extern char *shell_cmd; extern char *batch_name; int local_server = 0; +mode_t orig_umask = 0; struct file_list *the_file_list; /* There's probably never more than at most 2 outstanding child processes, * but set it higher, just in case. */ #define MAXCHILDPROCS 7 +#ifdef HAVE_SIGACTION +# ifdef HAVE_SIGPROCMASK +# define SIGACTMASK(n,h) SIGACTION(n,h), sigaddset(&sigmask,(n)) +# else +# define SIGACTMASK(n,h) SIGACTION(n,h) +# endif +static struct sigaction sigact; +#endif + struct pid_status { pid_t pid; int status; @@ -79,14 +91,14 @@ static int64 total_read, total_written; static void show_malloc_stats(void); /* Works like waitpid(), but if we already harvested the child pid in our - * sigchld_handler(), we succeed instead of returning an error. */ + * remember_children(), we succeed instead of returning an error. */ pid_t wait_process(pid_t pid, int *status_ptr, int flags) { pid_t waited_pid = waitpid(pid, status_ptr, flags); if (waited_pid == -1 && errno == ECHILD) { /* Status of requested child no longer available: check to - * see if it was processed by sigchld_handler(). */ + * see if it was processed by remember_children(). */ int cnt; for (cnt = 0; cnt < MAXCHILDPROCS; cnt++) { if (pid == pid_stat_table[cnt].pid) { @@ -179,7 +191,7 @@ static void handle_stats(int f) /* this is the client */ if (f < 0 && !am_sender) /* e.g. when we got an empty file list. */ - ; + ; else if (!am_sender) { /* Read the first two in opposite order because the meaning of * read/write swaps when switching from sender to receiver. */ @@ -393,7 +405,7 @@ static pid_t do_cmd(char *cmd, char *machine, char *user, char *path, if (verbose > 3) { for (i = 0; i < argc; i++) - rprintf(FINFO, "cmd[%d]=%s ", i, safe_fname(args[i])); + rprintf(FINFO, "cmd[%d]=%s ", i, args[i]); rprintf(FINFO, "\n"); } @@ -426,57 +438,117 @@ static pid_t do_cmd(char *cmd, char *machine, char *user, char *path, return 0; /* not reached */ } - -static char *get_local_name(struct file_list *flist,char *name) +/* The receiving side operates in one of two modes: + * + * 1. it receives any number of files into a destination directory, + * placing them according to their names in the file-list. + * + * 2. it receives a single file and saves it using the name in the + * destination path instead of its file-list name. This requires a + * "local name" for writing out the destination file. + * + * So, our task is to figure out what mode/local-name we need and return + * either a NULL for mode 1, or the local-name for mode 2. We also + * change directory if there are any path components in dest_path. */ +static char *get_local_name(struct file_list *flist, char *dest_path) { STRUCT_STAT st; - int e; + char *cp; - if (verbose > 2) - rprintf(FINFO,"get_local_name count=%d %s\n", - flist->count, NS(name)); + if (verbose > 2) { + rprintf(FINFO, "get_local_name count=%d %s\n", + flist->count, NS(dest_path)); + } - if (!name) + if (!dest_path || list_only) return NULL; - if (do_stat(name,&st) == 0) { + /* If the destination path refers to an existing directory, enter + * it and use mode 1. If there is something other than a directory + * at the destination path, we must be transferring one file + * (anything at the destination will be overwritten). */ + if (do_stat(dest_path, &st) == 0) { if (S_ISDIR(st.st_mode)) { - if (!push_dir(name)) { + if (!push_dir(dest_path)) { rsyserr(FERROR, errno, "push_dir#1 %s failed", - full_fname(name)); + full_fname(dest_path)); exit_cleanup(RERR_FILESELECT); } return NULL; } if (flist->count > 1) { - rprintf(FERROR,"ERROR: destination must be a directory when copying more than 1 file\n"); + rprintf(FERROR, + "ERROR: destination must be a directory when" + " copying more than 1 file\n"); + exit_cleanup(RERR_FILESELECT); + } + /* Caution: flist->count could be 0! */ + if (flist->count == 1 && S_ISDIR(flist->files[0]->mode)) { + rprintf(FERROR, + "ERROR: cannot overwrite non-directory" + " with a directory\n"); exit_cleanup(RERR_FILESELECT); } - return name; + } else if (errno != ENOENT) { + rsyserr(FERROR, errno, "cannot stat destination %s", + full_fname(dest_path)); + exit_cleanup(RERR_FILESELECT); } - if (flist->count <= 1 && ((e = strlen(name)) <= 1 || name[e-1] != '/')) - return name; + cp = strrchr(dest_path, '/'); - if (do_mkdir(name,0777 & ~orig_umask) != 0) { - rsyserr(FERROR, errno, "mkdir %s failed", full_fname(name)); - exit_cleanup(RERR_FILEIO); - } - if (verbose > 0) - rprintf(FINFO, "created directory %s\n", safe_fname(name)); + /* If the destination path ends in a slash or we are transferring + * multiple files, create a directory at the destination path, + * enter the new directory, and use mode 1. */ + if (flist->count > 1 || (cp && !cp[1])) { + /* Lop off the final slash (if any). */ + if (cp && !cp[1]) + *cp = '\0'; + + if (mkdir_defmode(dest_path) != 0) { + rsyserr(FERROR, errno, "mkdir %s failed", + full_fname(dest_path)); + exit_cleanup(RERR_FILEIO); + } + + if (verbose) + rprintf(FINFO, "created directory %s\n", dest_path); + + if (dry_run) { + /* Indicate that the destination directory doesn't + * really exist and return mode 1. */ + dry_run++; + return NULL; + } + + if (!push_dir(dest_path)) { + rsyserr(FERROR, errno, "push_dir#2 %s failed", + full_fname(dest_path)); + exit_cleanup(RERR_FILESELECT); + } - if (dry_run) { - dry_run++; return NULL; } - if (!push_dir(name)) { - rsyserr(FERROR, errno, "push_dir#2 %s failed", - full_fname(name)); + /* Otherwise, we are writing a single file, possibly on top of an + * existing non-directory. Change to the item's parent directory + * (if it has a path component), return the basename of the + * destination file as the local name, and use mode 2. */ + if (!cp) + return dest_path; + + if (cp == dest_path) + dest_path = "/"; + + *cp = '\0'; + if (!push_dir(dest_path)) { + rsyserr(FERROR, errno, "push_dir#3 %s failed", + full_fname(dest_path)); exit_cleanup(RERR_FILESELECT); } + *cp = '/'; - return NULL; + return cp + 1; } @@ -504,7 +576,7 @@ static void read_final_goodbye(int f_in, int f_out) } -static void do_server_sender(int f_in, int f_out, int argc,char *argv[]) +static void do_server_sender(int f_in, int f_out, int argc, char *argv[]) { struct file_list *flist; char *dir = argv[0]; @@ -567,7 +639,7 @@ static int do_recv(int f_in,int f_out,struct file_list *flist,char *local_name) /* The receiving side mustn't obey this, or an existing symlink that * points to an identical file won't be replaced by the referent. */ - copy_links = 0; + copy_links = copy_dirlinks = 0; if (preserve_hard_links) init_hard_links(); @@ -732,6 +804,9 @@ void start_server(int f_in, int f_out, int argc, char *argv[]) io_set_sock_fds(f_in, f_out); setup_protocol(f_out, f_in); +#if defined HAVE_ICONV_OPEN && defined HAVE_ICONV_H + setup_iconv(); +#endif if (protocol_version >= 23) io_start_multiplex_out(); @@ -768,6 +843,9 @@ int client_run(int f_in, int f_out, pid_t pid, int argc, char *argv[]) io_set_sock_fds(f_in, f_out); setup_protocol(f_out,f_in); +#if defined HAVE_ICONV_OPEN && defined HAVE_ICONV_H + setup_iconv(); +#endif if (protocol_version >= 23 && !read_batch) io_start_multiplex_in(); @@ -894,12 +972,12 @@ static int start_client(int argc, char *argv[]) return rc; if (!read_batch) { /* for read_batch, NO source is specified */ - argc--; shell_path = check_for_hostspec(argv[0], &shell_machine, &rsync_port); if (shell_path) { /* source is remote */ char *dummy1; int dummy2; - if (argc && check_for_hostspec(argv[argc], &dummy1, &dummy2)) { + if (--argc + && check_for_hostspec(argv[argc], &dummy1, &dummy2)) { rprintf(FERROR, "The source and destination cannot both be remote.\n"); exit_cleanup(RERR_SYNTAX); @@ -924,12 +1002,14 @@ static int start_client(int argc, char *argv[]) } else { /* source is local, check dest arg */ am_sender = 1; - if (argc < 1) { /* destination required */ - usage(FERROR); - exit_cleanup(RERR_SYNTAX); + if (argc > 1) + p = argv[--argc]; + else { + p = "."; + list_only = 1; } - shell_path = check_for_hostspec(argv[argc], &shell_machine, &rsync_port); + shell_path = check_for_hostspec(p, &shell_machine, &rsync_port); if (shell_path && filesfrom_host && *filesfrom_host && strcmp(filesfrom_host, shell_machine) != 0) { rprintf(FERROR, @@ -944,7 +1024,7 @@ static int start_client(int argc, char *argv[]) exit_cleanup(RERR_SYNTAX); } shell_machine = NULL; - shell_path = argv[argc]; + shell_path = p; } else if (rsync_port) { if (!shell_cmd) { return start_socket_client(shell_machine, @@ -974,10 +1054,10 @@ static int start_client(int argc, char *argv[]) if (verbose > 3) { rprintf(FINFO,"cmd=%s machine=%s user=%s path=%s\n", - shell_cmd ? safe_fname(shell_cmd) : "", - shell_machine ? safe_fname(shell_machine) : "", - shell_user ? safe_fname(shell_user) : "", - shell_path ? safe_fname(shell_path) : ""); + shell_cmd ? shell_cmd : "", + shell_machine ? shell_machine : "", + shell_user ? shell_user : "", + shell_path ? shell_path : ""); } /* for remote source, only single dest arg can remain ... */ @@ -1027,7 +1107,7 @@ static RETSIGTYPE sigusr2_handler(UNUSED(int val)) _exit(0); } -static RETSIGTYPE sigchld_handler(UNUSED(int val)) +RETSIGTYPE remember_children(UNUSED(int val)) { #ifdef WNOHANG int cnt, status; @@ -1048,7 +1128,9 @@ static RETSIGTYPE sigchld_handler(UNUSED(int val)) } } #endif - signal(SIGCHLD, sigchld_handler); +#ifndef HAVE_SIGACTION + signal(SIGCHLD, remember_children); +#endif } @@ -1108,16 +1190,23 @@ int main(int argc,char *argv[]) int ret; int orig_argc = argc; char **orig_argv = argv; +#ifdef HAVE_SIGACTION +# ifdef HAVE_SIGPROCMASK + sigset_t sigmask; - signal(SIGUSR1, sigusr1_handler); - signal(SIGUSR2, sigusr2_handler); - signal(SIGCHLD, sigchld_handler); + sigemptyset(&sigmask); +# endif + sigact.sa_flags = SA_NOCLDSTOP; +#endif + SIGACTMASK(SIGUSR1, sigusr1_handler); + SIGACTMASK(SIGUSR2, sigusr2_handler); + SIGACTMASK(SIGCHLD, remember_children); #ifdef MAINTAINER_MODE - signal(SIGSEGV, rsync_panic_handler); - signal(SIGFPE, rsync_panic_handler); - signal(SIGABRT, rsync_panic_handler); - signal(SIGBUS, rsync_panic_handler); -#endif /* def MAINTAINER_MODE */ + SIGACTMASK(SIGSEGV, rsync_panic_handler); + SIGACTMASK(SIGFPE, rsync_panic_handler); + SIGACTMASK(SIGABRT, rsync_panic_handler); + SIGACTMASK(SIGBUS, rsync_panic_handler); +#endif starttime = time(NULL); am_root = (MY_UID() == 0); @@ -1131,7 +1220,11 @@ int main(int argc,char *argv[]) /* we set a 0 umask so that correct file permissions can be * carried across */ - orig_umask = (int)umask(0); + orig_umask = umask(0); + +#if defined CONFIG_LOCALE && defined HAVE_SETLOCALE + setlocale(LC_CTYPE, ""); +#endif if (!parse_arguments(&argc, (const char ***) &argv, 1)) { /* FIXME: We ought to call the same error-handling @@ -1140,16 +1233,18 @@ int main(int argc,char *argv[]) exit_cleanup(RERR_SYNTAX); } - signal(SIGINT,SIGNAL_CAST sig_int); - signal(SIGHUP,SIGNAL_CAST sig_int); - signal(SIGTERM,SIGNAL_CAST sig_int); + SIGACTMASK(SIGINT, sig_int); + SIGACTMASK(SIGHUP, sig_int); + SIGACTMASK(SIGTERM, sig_int); +#if defined HAVE_SIGACTION && HAVE_SIGPROCMASK + sigprocmask(SIG_UNBLOCK, &sigmask, NULL); +#endif /* Ignore SIGPIPE; we consistently check error codes and will * see the EPIPE. */ - signal(SIGPIPE, SIG_IGN); - -#if defined CONFIG_LOCALE && defined HAVE_SETLOCALE - setlocale(LC_CTYPE, ""); + SIGACTION(SIGPIPE, SIG_IGN); +#ifdef SIGXFSZ + SIGACTION(SIGXFSZ, SIG_IGN); #endif /* Initialize push_dir here because on some old systems getcwd