-/* -*- c-file-style: "linux" -*-
+/*
+ * Command-line (and received via daemon-socket) option parsing.
*
- * Copyright (C) 1998-2001 by Andrew Tridgell <tridge@samba.org>
- * Copyright (C) 2000, 2001, 2002 by Martin Pool <mbp@samba.org>
+ * Copyright (C) 1998-2001 Andrew Tridgell <tridge@samba.org>
+ * Copyright (C) 2000, 2001, 2002 Martin Pool <mbp@samba.org>
+ * Copyright (C) 2002, 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
* 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.
+ * 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.,
+ * 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "rsync.h"
int append_mode = 0;
int keep_dirlinks = 0;
+int copy_dirlinks = 0;
int copy_links = 0;
int preserve_links = 0;
int preserve_hard_links = 0;
int dry_run = 0;
int do_xfers = 1;
int ignore_times = 0;
-int saw_delete_opt = 0;
-int saw_delete_excluded_opt = 0;
int delete_mode = 0;
int delete_during = 0;
int delete_before = 0;
int am_sender = 0;
int am_generator = 0;
int am_starting_up = 1;
-int orig_umask = 0;
int relative_paths = -1;
int implied_dirs = 1;
int numeric_ids = 0;
+int allow_8bit_chars = 0;
int force_delete = 0;
int io_timeout = 0;
int allowed_lull = 0;
char *basis_dir[MAX_BASIS_DIRS+1];
char *config_file = NULL;
char *shell_cmd = NULL;
-char *log_format = NULL;
+char *logfile_name = NULL;
+char *logfile_format = NULL;
+char *stdout_format = NULL;
char *password_file = NULL;
char *rsync_path = RSYNC_PATH;
char *backup_dir = NULL;
int verbose = 0;
int quiet = 0;
int log_before_transfer = 0;
-int log_format_has_i = 0;
-int log_format_has_o_or_i = 0;
+int stdout_format_has_i = 0;
+int stdout_format_has_o_or_i = 0;
+int logfile_format_has_i = 0;
+int logfile_format_has_o_or_i = 0;
int always_checksum = 0;
int list_only = 0;
rprintf(F," -L, --copy-links transform symlink into referent file/dir\n");
rprintf(F," --copy-unsafe-links only \"unsafe\" symlinks are transformed\n");
rprintf(F," --safe-links ignore symlinks that point outside the source tree\n");
- rprintf(F," -H, --hard-links preserve hard links\n");
+ rprintf(F," -k, --copy-dirlinks transform symlink to a dir into referent dir\n");
rprintf(F," -K, --keep-dirlinks treat symlinked dir on receiver as dir\n");
+ rprintf(F," -H, --hard-links preserve hard links\n");
rprintf(F," -p, --perms preserve permissions\n");
rprintf(F," -E, --executability preserve the file's executability\n");
rprintf(F," --chmod=CHMOD change destination permissions\n");
rprintf(F," -B, --block-size=SIZE force a fixed checksum block-size\n");
rprintf(F," -e, --rsh=COMMAND specify the remote shell to use\n");
rprintf(F," --rsync-path=PROGRAM specify the rsync to run on the remote machine\n");
- rprintf(F," --existing ignore non-existing files on receiving side\n");
- rprintf(F," --ignore-existing ignore files that already exist on receiving side\n");
- rprintf(F," --remove-sent-files sent files/symlinks are removed from sending side\n");
+ rprintf(F," --existing skip creating new files on receiver\n");
+ rprintf(F," --ignore-existing skip updating files that already exist on receiver\n");
+ rprintf(F," --remove-sent-files sender removes successfully sent files (non-dirs)\n");
rprintf(F," --del an alias for --delete-during\n");
- rprintf(F," --delete delete files that don't exist on the sending side\n");
+ rprintf(F," --delete delete extraneous files from destination dirs\n");
rprintf(F," --delete-before receiver deletes before transfer (default)\n");
rprintf(F," --delete-during receiver deletes during transfer, not before\n");
rprintf(F," --delete-after receiver deletes after transfer, not before\n");
- rprintf(F," --delete-excluded also delete excluded files on the receiving side\n");
+ rprintf(F," --delete-excluded also delete excluded files from destination dirs\n");
rprintf(F," --ignore-errors delete even if there are I/O errors\n");
rprintf(F," --force force deletion of directories even if not empty\n");
rprintf(F," --max-delete=NUM don't delete more than NUM files\n");
rprintf(F," --sockopts=OPTIONS specify custom TCP options\n");
rprintf(F," --blocking-io use blocking I/O for the remote shell\n");
rprintf(F," --stats give some file-transfer stats\n");
+ rprintf(F," -8, --8-bit-output leave high-bit chars unescaped in output\n");
rprintf(F," -h, --human-readable output numbers in a human-readable format\n");
- rprintf(F," --si like human-readable, but use powers of 1000\n");
rprintf(F," --progress show progress during transfer\n");
rprintf(F," -P same as --partial --progress\n");
rprintf(F," -i, --itemize-changes output a change-summary for all updates\n");
- rprintf(F," --log-format=FORMAT output filenames using the specified format\n");
+ rprintf(F," --out-format=FORMAT output updates using the specified FORMAT\n");
+ rprintf(F," --log-file=FILE log what we're doing to the specified FILE\n");
+ rprintf(F," --log-file-format=FMT log updates using the specified FMT\n");
rprintf(F," --password-file=FILE read password from FILE\n");
rprintf(F," --list-only list the files instead of copying them\n");
rprintf(F," --bwlimit=KBPS limit I/O bandwidth; KBytes per second\n");
rprintf(F," -6, --ipv6 prefer IPv6\n");
#endif
rprintf(F," --version print version number\n");
- rprintf(F," --help show this help screen\n");
+ rprintf(F,"(-h) --help show this help (-h works with no other options)\n");
rprintf(F,"\nUse \"rsync --daemon --help\" to see the daemon-mode command-line options.\n");
rprintf(F,"Please see the rsync(1) and rsyncd.conf(5) man pages for full documentation.\n");
{"no-v", 0, POPT_ARG_VAL, &verbose, 0, 0, 0 },
{"quiet", 'q', POPT_ARG_NONE, 0, 'q', 0, 0 },
{"stats", 0, POPT_ARG_NONE, &do_stats, 0, 0, 0 },
- {"human-readable", 'h', POPT_ARG_VAL, &human_readable, 1, 0, 0},
- {"si", 0, POPT_ARG_VAL, &human_readable, 2, 0, 0},
+ {"human-readable", 'h', POPT_ARG_NONE, 0, 'h', 0, 0},
{"dry-run", 'n', POPT_ARG_NONE, &dry_run, 0, 0, 0 },
{"archive", 'a', POPT_ARG_NONE, 0, 'a', 0, 0 },
{"recursive", 'r', POPT_ARG_VAL, &recurse, 2, 0, 0 },
{"copy-links", 'L', POPT_ARG_NONE, ©_links, 0, 0, 0 },
{"copy-unsafe-links",0, POPT_ARG_NONE, ©_unsafe_links, 0, 0, 0 },
{"safe-links", 0, POPT_ARG_NONE, &safe_symlinks, 0, 0, 0 },
+ {"copy-dirlinks", 'k', POPT_ARG_NONE, ©_dirlinks, 0, 0, 0 },
{"keep-dirlinks", 'K', POPT_ARG_NONE, &keep_dirlinks, 0, 0, 0 },
- {"hard-links", 'H', POPT_ARG_NONE, &preserve_hard_links, 0, 0, 0 },
+ {"hard-links", 'H', POPT_ARG_VAL, &preserve_hard_links, 1, 0, 0 },
+ {"no-hard-links", 0, POPT_ARG_VAL, &preserve_hard_links, 0, 0, 0 },
+ {"no-H", 0, POPT_ARG_VAL, &preserve_hard_links, 0, 0, 0 },
{"relative", 'R', POPT_ARG_VAL, &relative_paths, 1, 0, 0 },
{"no-relative", 0, POPT_ARG_VAL, &relative_paths, 0, 0, 0 },
{"no-R", 0, POPT_ARG_VAL, &relative_paths, 0, 0, 0 },
+ {"implied-dirs", 0, POPT_ARG_VAL, &implied_dirs, 1, 0, 0 },
{"no-implied-dirs", 0, POPT_ARG_VAL, &implied_dirs, 0, 0, 0 },
{"chmod", 0, POPT_ARG_STRING, 0, OPT_CHMOD, 0, 0 },
{"ignore-times", 'I', POPT_ARG_NONE, &ignore_times, 0, 0, 0 },
{"partial-dir", 0, POPT_ARG_STRING, &partial_dir, 0, 0, 0 },
{"delay-updates", 0, POPT_ARG_NONE, &delay_updates, 0, 0, 0 },
{"prune-empty-dirs",'m', POPT_ARG_NONE, &prune_empty_dirs, 0, 0, 0 },
- {"log-format", 0, POPT_ARG_STRING, &log_format, 0, 0, 0 },
+ {"log-file", 0, POPT_ARG_STRING, &logfile_name, 0, 0, 0 },
+ {"log-file-format", 0, POPT_ARG_STRING, &logfile_format, 0, 0, 0 },
+ {"out-format", 0, POPT_ARG_STRING, &stdout_format, 0, 0, 0 },
+ {"log-format", 0, POPT_ARG_STRING, &stdout_format, 0, 0, 0 }, /* DEPRECATED */
{"itemize-changes", 'i', POPT_ARG_NONE, 0, 'i', 0, 0 },
{"bwlimit", 0, POPT_ARG_INT, &bwlimit, 0, 0, 0 },
{"backup", 'b', POPT_ARG_NONE, &make_backups, 0, 0, 0 },
{"ipv4", '4', POPT_ARG_VAL, &default_af_hint, AF_INET, 0, 0 },
{"ipv6", '6', POPT_ARG_VAL, &default_af_hint, AF_INET6, 0, 0 },
#endif
+ {"8-bit-output", '8', POPT_ARG_NONE, &allow_8bit_chars, 0, 0, 0 },
{"address", 0, POPT_ARG_STRING, &bind_address, 0, 0, 0 },
{"port", 0, POPT_ARG_INT, &rsync_port, 0, 0, 0 },
{"sockopts", 0, POPT_ARG_STRING, &sockopts, 0, 0, 0 },
rprintf(F," --config=FILE specify alternate rsyncd.conf file\n");
rprintf(F," --no-detach do not detach from the parent\n");
rprintf(F," --port=PORT listen on alternate port number\n");
+ rprintf(F," --log-file=FILE override the \"log file\" setting\n");
rprintf(F," --sockopts=OPTIONS specify custom TCP options\n");
rprintf(F," -v, --verbose increase verbosity\n");
#ifdef INET6
{"ipv6", '6', POPT_ARG_VAL, &default_af_hint, AF_INET6, 0, 0 },
#endif
{"detach", 0, POPT_ARG_VAL, &no_detach, 0, 0, 0 },
+ {"log-file", 0, POPT_ARG_STRING, &logfile_name, 0, 0, 0 },
{"no-detach", 0, POPT_ARG_VAL, &no_detach, 1, 0, 0 },
{"port", 0, POPT_ARG_INT, &rsync_port, 0, 0, 0 },
{"sockopts", 0, POPT_ARG_STRING, &sockopts, 0, 0, 0 },
}
rprintf(FERROR, RSYNC_NAME ": %s", err_buf);
+ msleep(20);
}
case OPT_EXCLUDE_FROM:
case OPT_INCLUDE_FROM:
arg = poptGetOptArg(pc);
- if (sanitize_paths)
- arg = sanitize_path(NULL, arg, NULL, 0);
+ if (sanitize_paths) {
+ arg = sanitize_path(NULL, arg, NULL, 0, NULL);
+ die_on_unsafe_path((char*)arg, 0);
+ }
if (server_filter_list.head) {
- char *cp = (char *)arg;
+ char *cp = strdup(arg);
+ if (!cp)
+ out_of_memory("parse_arguments");
if (!*cp)
goto options_rejected;
clean_fname(cp, 1);
if (check_filter(&server_filter_list, cp, 0) < 0)
goto options_rejected;
+ free(cp);
}
parse_filter_file(&filter_list, arg,
opt == OPT_INCLUDE_FROM ? MATCHFLG_INCLUDE : 0,
preserve_devices = preserve_specials = 0;
break;
+ case 'h':
+ human_readable++;
+ break;
+
case 'i':
itemize_changes++;
break;
break;
case OPT_LINK_DEST:
-#ifdef HAVE_LINK
+#ifdef SUPPORT_HARD_LINKS
link_dest = 1;
dest_option = "--link-dest";
goto set_dest_dir;
MAX_BASIS_DIRS, dest_option);
return 0;
}
- arg = poptGetOptArg(pc);
- if (sanitize_paths)
- arg = sanitize_path(NULL, arg, NULL, 0);
- basis_dir[basis_dir_cnt++] = (char *)arg;
+ /* We defer sanitizing this arg until we know what
+ * our destination directory is going to be. */
+ basis_dir[basis_dir_cnt++] = (char *)poptGetOptArg(pc);
break;
case OPT_CHMOD:
}
if (human_readable && *argc == 2) {
+ /* Allow the old meaning of 'h' (--help) on its own. */
usage(FINFO);
exit_cleanup(0);
}
}
delete_mode = delete_before = 1;
}
- saw_delete_opt = delete_mode;
- saw_delete_excluded_opt = delete_excluded;
- if (!xfer_dirs) {
- delete_before = delete_during = delete_after = 0;
- delete_mode = delete_excluded = 0;
+ if (!xfer_dirs && delete_mode) {
+ snprintf(err_buf, sizeof err_buf,
+ "--delete does not work without -r or -d.\n");
+ return 0;
}
if (delete_mode && refused_delete) {
if (sanitize_paths) {
int i;
for (i = *argc; i-- > 0; )
- (*argv)[i] = sanitize_path(NULL, (*argv)[i], "", 0);
- if (tmpdir)
- tmpdir = sanitize_path(NULL, tmpdir, NULL, 0);
- if (partial_dir)
- partial_dir = sanitize_path(NULL, partial_dir, NULL, 0);
- if (backup_dir)
- backup_dir = sanitize_path(NULL, backup_dir, NULL, 0);
+ (*argv)[i] = sanitize_path(NULL, (*argv)[i], "", 0, NULL);
+ if (tmpdir) {
+ tmpdir = sanitize_path(NULL, tmpdir, NULL, 0, NULL);
+ die_on_unsafe_path(tmpdir, 0);
+ }
+ if (backup_dir) {
+ backup_dir = sanitize_path(NULL, backup_dir, NULL, 0, NULL);
+ die_on_unsafe_path(backup_dir, 0);
+ }
}
if (server_filter_list.head && !am_sender) {
struct filter_list_struct *elp = &server_filter_list;
- int i;
if (tmpdir) {
if (!*tmpdir)
goto options_rejected;
if (check_filter(elp, tmpdir, 1) < 0)
goto options_rejected;
}
- if (partial_dir && *partial_dir) {
- clean_fname(partial_dir, 1);
- if (check_filter(elp, partial_dir, 1) < 0)
- goto options_rejected;
- }
- for (i = 0; i < basis_dir_cnt; i++) {
- if (!*basis_dir[i])
- goto options_rejected;
- clean_fname(basis_dir[i], 1);
- if (check_filter(elp, basis_dir[i], 1) < 0)
- goto options_rejected;
- }
if (backup_dir) {
if (!*backup_dir)
goto options_rejected;
if (make_backups && !backup_dir)
omit_dir_times = 1;
- if (log_format) {
- if (am_server && log_format_has(log_format, 'I'))
- log_format_has_i = 2;
- else if (log_format_has(log_format, 'i'))
- log_format_has_i = itemize_changes | 1;
- if (!log_format_has(log_format, 'b')
- && !log_format_has(log_format, 'c'))
+ if (stdout_format) {
+ if (am_server && log_format_has(stdout_format, 'I'))
+ stdout_format_has_i = 2;
+ else if (log_format_has(stdout_format, 'i'))
+ stdout_format_has_i = itemize_changes | 1;
+ if (!log_format_has(stdout_format, 'b')
+ && !log_format_has(stdout_format, 'c'))
log_before_transfer = !am_server;
} else if (itemize_changes) {
- log_format = "%i %n%L";
- log_format_has_i = itemize_changes;
+ stdout_format = "%i %n%L";
+ stdout_format_has_i = itemize_changes;
log_before_transfer = !am_server;
}
- if ((do_progress || dry_run) && !verbose && !log_before_transfer
- && !am_server)
+ if (do_progress && !verbose && !log_before_transfer && !am_server)
verbose = 1;
if (dry_run)
set_io_timeout(io_timeout);
- if (verbose && !log_format) {
- log_format = "%n%L";
+ if (verbose && !stdout_format) {
+ stdout_format = "%n%L";
log_before_transfer = !am_server;
}
- if (log_format_has_i || log_format_has(log_format, 'o'))
- log_format_has_o_or_i = 1;
+ if (stdout_format_has_i || log_format_has(stdout_format, 'o'))
+ stdout_format_has_o_or_i = 1;
+
+ if (am_daemon)
+ logfile_name = NULL;
+ else if (logfile_name) {
+ if (!logfile_format) {
+ logfile_format = "%i %n%L (!)";
+ logfile_format_has_i = logfile_format_has_o_or_i = 1;
+ } else {
+ if (log_format_has(logfile_format, 'i'))
+ stdout_format_has_i = 2;
+ if (logfile_format_has_i || log_format_has(logfile_format, 'o'))
+ logfile_format_has_o_or_i = 1;
+ }
+ log_init();
+ }
if (daemon_bwlimit && (!bwlimit || bwlimit > daemon_bwlimit))
bwlimit = daemon_bwlimit;
return 0;
#endif
} else {
- if (keep_partial && !partial_dir) {
+ if (keep_partial && !partial_dir && !am_server) {
if ((arg = getenv("RSYNC_PARTIAL_DIR")) != NULL && *arg)
partial_dir = strdup(arg);
}
clean_fname(partial_dir, 1);
if (!*partial_dir || strcmp(partial_dir, ".") == 0)
partial_dir = NULL;
- else if (*partial_dir != '/'
- && check_filter(&filter_list, partial_dir, 1) == 0) {
+ else if (*partial_dir != '/') {
parse_rule(&filter_list, partial_dir,
MATCHFLG_NO_PREFIXES|MATCHFLG_DIRECTORY, 0);
}
}
} else {
if (sanitize_paths)
- files_from = sanitize_path(NULL, files_from, NULL, 0);
+ files_from = sanitize_path(NULL, files_from, NULL, 0, NULL);
if (server_filter_list.head) {
if (!*files_from)
goto options_rejected;
argstr[x++] = 'b';
if (update_only)
argstr[x++] = 'u';
- if (!do_xfers) /* NOT "dry_run"! */
+ if (!do_xfers) /* Note: NOT "dry_run"! */
argstr[x++] = 'n';
if (preserve_links)
argstr[x++] = 'l';
- if (copy_links)
- argstr[x++] = 'L';
if (xfer_dirs > (recurse || !delete_mode || !am_sender))
argstr[x++] = 'd';
if (am_sender) {
argstr[x++] = 'm';
if (omit_dir_times == 2)
argstr[x++] = 'O';
+ } else {
+ if (copy_links)
+ argstr[x++] = 'L';
+ if (copy_dirlinks)
+ argstr[x++] = 'k';
}
if (whole_file > 0)
/* The server side doesn't use our log-format, but in certain
* circumstances they need to know a little about the option. */
- if (log_format && am_sender) {
- if (log_format_has_i > 1)
+ if (stdout_format && am_sender) {
+ /* Use --log-format, not --out-format, for compatibility. */
+ if (stdout_format_has_i > 1)
args[ac++] = "--log-format=%i%I";
- else if (log_format_has_i)
+ else if (stdout_format_has_i)
args[ac++] = "--log-format=%i";
- else if (log_format_has_o_or_i)
+ else if (stdout_format_has_o_or_i)
args[ac++] = "--log-format=%o";
else if (!verbose)
args[ac++] = "--log-format=X";
args[ac++] = "--only-write-batch=X";
if (am_root > 1)
args[ac++] = "--super";
+ if (size_only)
+ args[ac++] = "--size-only";
}
- if (size_only)
- args[ac++] = "--size-only";
-
if (modify_window_set) {
if (asprintf(&arg, "--modify-window=%d", modify_window) < 0)
goto oom;