Some demon_log_* variables changed into logfile_* variables that are
[rsync/rsync.git] / options.c
index d7c7cbe..38bf257 100644 (file)
--- a/options.c
+++ b/options.c
@@ -1,7 +1,9 @@
-/*  -*- 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
@@ -13,9 +15,9 @@
  * 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"
@@ -41,6 +43,7 @@ int whole_file = -1;
 
 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;
@@ -57,8 +60,6 @@ int cvs_exclude = 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;
@@ -75,7 +76,6 @@ int am_server = 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;
@@ -148,6 +148,8 @@ 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 *password_file = NULL;
 char *rsync_path = RSYNC_PATH;
 char *backup_dir = NULL;
@@ -164,7 +166,9 @@ int verbose = 0;
 int quiet = 0;
 int log_before_transfer = 0;
 int log_format_has_i = 0;
+int logfile_format_has_i = 0;
 int log_format_has_o_or_i = 0;
+int logfile_format_has_o_or_i = 0;
 int always_checksum = 0;
 int list_only = 0;
 
@@ -290,8 +294,9 @@ void usage(enum logcode F)
   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");
@@ -310,15 +315,15 @@ void usage(enum logcode F)
   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");
@@ -360,6 +365,7 @@ void usage(enum logcode F)
   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-file=FILE         output what we're doing to a log file\n");
   rprintf(F,"     --log-format=FORMAT     output filenames using the specified format\n");
   rprintf(F,"     --password-file=FILE    read password from FILE\n");
   rprintf(F,"     --list-only             list the files instead of copying them\n");
@@ -434,11 +440,15 @@ static struct poptOption long_options[] = {
   {"copy-links",      'L', POPT_ARG_NONE,   &copy_links, 0, 0, 0 },
   {"copy-unsafe-links",0,  POPT_ARG_NONE,   &copy_unsafe_links, 0, 0, 0 },
   {"safe-links",       0,  POPT_ARG_NONE,   &safe_symlinks, 0, 0, 0 },
+  {"copy-dirlinks",   'k', POPT_ARG_NONE,   &copy_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 },
@@ -489,6 +499,7 @@ static struct poptOption long_options[] = {
   {"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-file",         0,  POPT_ARG_STRING, &logfile_name, 0, 0, 0 },
   {"log-format",       0,  POPT_ARG_STRING, &log_format, 0, 0, 0 },
   {"itemize-changes", 'i', POPT_ARG_NONE,   0, 'i', 0, 0 },
   {"bwlimit",          0,  POPT_ARG_INT,    &bwlimit, 0, 0, 0 },
@@ -539,6 +550,7 @@ static void daemon_usage(enum logcode F)
   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
@@ -562,6 +574,7 @@ static struct poptOption long_daemon_options[] = {
   {"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 },
@@ -592,6 +605,7 @@ void option_error(void)
        }
 
        rprintf(FERROR, RSYNC_NAME ": %s", err_buf);
+       msleep(20);
 }
 
 
@@ -884,15 +898,20 @@ int parse_arguments(int *argc, const char ***argv, int frommain)
                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,
@@ -1016,7 +1035,7 @@ int parse_arguments(int *argc, const char ***argv, int frommain)
                        break;
 
                case OPT_LINK_DEST:
-#ifdef HAVE_LINK
+#ifdef SUPPORT_HARD_LINKS
                        link_dest = 1;
                        dest_option = "--link-dest";
                        goto set_dest_dir;
@@ -1042,10 +1061,9 @@ int parse_arguments(int *argc, const char ***argv, int frommain)
                                        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:
@@ -1172,11 +1190,10 @@ int parse_arguments(int *argc, const char ***argv, int frommain)
                }
                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) {
@@ -1201,17 +1218,18 @@ int parse_arguments(int *argc, const char ***argv, int frommain)
        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;
@@ -1219,18 +1237,6 @@ int parse_arguments(int *argc, const char ***argv, int frommain)
                        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;
@@ -1308,6 +1314,21 @@ int parse_arguments(int *argc, const char ***argv, int frommain)
        if (log_format_has_i || log_format_has(log_format, 'o'))
                log_format_has_o_or_i = 1;
 
+       if (am_daemon)
+               logfile_name = NULL;
+       else if (logfile_name) {
+               if (am_server) {
+                       logfile_format = "%i %n%L";
+                       logfile_format_has_i = logfile_format_has_o_or_i = 1;
+               } else if (log_format) {
+                       logfile_format = log_format;
+                       logfile_format_has_i = log_format_has_i;
+                       logfile_format_has_o_or_i = log_format_has_o_or_i;
+               }
+               log_before_transfer = !am_server;
+               log_init();
+       }
+
        if (daemon_bwlimit && (!bwlimit || bwlimit > daemon_bwlimit))
                bwlimit = daemon_bwlimit;
        if (bwlimit) {
@@ -1411,7 +1432,7 @@ int parse_arguments(int *argc, const char ***argv, int frommain)
                        }
                } 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;
@@ -1477,12 +1498,10 @@ void server_options(char **args,int *argc)
                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) {
@@ -1492,6 +1511,11 @@ void server_options(char **args,int *argc)
                        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)