From 047928085f8e03573cc3a6218870adeda391d1ac Mon Sep 17 00:00:00 2001 From: Michael Mess Date: Sat, 30 May 2026 09:19:12 +0200 Subject: [PATCH 01/24] Added methods renice_me() and ionice_me() to reduce priority --- niceness.c | 48 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) create mode 100644 niceness.c diff --git a/niceness.c b/niceness.c new file mode 100644 index 000000000..f1625f061 --- /dev/null +++ b/niceness.c @@ -0,0 +1,48 @@ +/* + * Renice or ionice the current process to reduce its impact on the system + * + * Copyright (C) 2026 Michael Mess + * + * 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 3 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, visit the http://fsf.org website. + */ + +#include "rsync.h" + +void renice_me() +{ +#ifdef SUPPORT_RENICE + int which = PRIO_PROCESS; // who specifies a Process ID + int who = 0; // 0 means current process + int prio = 19; // lowest CPU Priority + int result = setpriority(which, who, prio); + if ( result < 0 ) { + // Failed to set priority, TODO: log or inform user, but can be ignored (it's just not so nice). + } +#endif +} + +void ionice_me() +{ +#ifdef SUPPORT_IONICE + int which = IOPRIO_WHO_PROCESS; // who specifies a Process ID + int who = 0; // 0 means current process + int class = IOPRIO_CLASS_IDLE; + int data = 0; // Ignored when using the IOPRIO_CLASS_IDLE class + int ioprio = IOPRIO_PRIO_VALUE(class, data); + int result = syscall(SYS_ioprio_set, which, who, ioprio); + if ( result < 0 ) { + // Failed to set priority, TODO: log or inform user, but can be ignored (it's just not so ionice). + } +#endif +} From 5450a109bfe3b344233f9f4e9d82f33addddb291 Mon Sep 17 00:00:00 2001 From: Michael Mess Date: Sat, 30 May 2026 11:31:09 +0200 Subject: [PATCH 02/24] configure.ac: Check for sys/resource.h linux/ioprio.h sys/syscall.h header files. --- configure.ac | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index cda60405b..2ff1f92be 100644 --- a/configure.ac +++ b/configure.ac @@ -13,7 +13,7 @@ AC_CHECK_HEADERS(sys/fcntl.h sys/select.h fcntl.h sys/time.h sys/unistd.h \ sys/acl.h acl/libacl.h attr/xattr.h sys/xattr.h sys/extattr.h dl.h \ popt.h popt/popt.h linux/falloc.h netinet/in_systm.h netgroup.h \ zlib.h xxhash.h openssl/md4.h openssl/md5.h zstd.h lz4.h sys/file.h \ - bsd/string.h) + bsd/string.h sys/resource.h linux/ioprio.h sys/syscall.h) AC_CHECK_HEADERS([netinet/ip.h], [], [], [[#include ]]) AC_HEADER_MAJOR_FIXED From 0981aa14a985636d25c76ab592cb4ac00b5f5b6e Mon Sep 17 00:00:00 2001 From: Michael Mess Date: Sat, 30 May 2026 11:32:20 +0200 Subject: [PATCH 03/24] Added niceness.o to Makefile.in for build --- Makefile.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile.in b/Makefile.in index 5216fb2f7..d697e8992 100644 --- a/Makefile.in +++ b/Makefile.in @@ -46,7 +46,7 @@ zlib_OBJS=zlib/deflate.o zlib/inffast.o zlib/inflate.o zlib/inftrees.o \ OBJS1=flist.o rsync.o generator.o receiver.o cleanup.o sender.o exclude.o \ util1.o util2.o main.o checksum.o match.o syscall.o android.o log.o backup.o delete.o OBJS2=options.o io.o compat.o hlink.o token.o uidlist.o socket.o hashtable.o \ - usage.o fileio.o batch.o clientname.o chmod.o acls.o xattrs.o + usage.o fileio.o batch.o clientname.o chmod.o acls.o xattrs.o niceness.o OBJS3=progress.o pipe.o @MD5_ASM@ @ROLL_SIMD@ @ROLL_ASM@ DAEMON_OBJ = params.o loadparm.o clientserver.o access.o connection.o authenticate.o popt_OBJS= popt/popt.o popt/poptconfig.o \ From 2f4fd540c176651d9931e32722e143967153e090 Mon Sep 17 00:00:00 2001 From: Michael Mess Date: Sat, 30 May 2026 13:33:39 +0200 Subject: [PATCH 04/24] Define SUPPORT_RENICE and SUPPORT_IONICE if the necessary header files exist --- rsync.h | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/rsync.h b/rsync.h index cdc2d2c0d..cd6225c23 100644 --- a/rsync.h +++ b/rsync.h @@ -1510,3 +1510,13 @@ const char *get_panic_action(void); #elif defined HAVE_MALLINFO #define MEM_ALLOC_INFO mallinfo #endif + + /* Some header files needed to be nice ;) */ +#ifdef HAVE_SYS_RESOURCE_H +#define SUPPORT_RENICE +#endif + +#if defined HAVE_LINUX_IOPRIO_H && defined HAVE_SYS_SYSCALL_H +#define SUPPORT_IONICE +#endif + From 1b35d19654c8997e550c45f786fddfed4f2f040f Mon Sep 17 00:00:00 2001 From: Michael Mess Date: Sat, 30 May 2026 13:37:09 +0200 Subject: [PATCH 05/24] Added includes --- rsync.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/rsync.h b/rsync.h index cd6225c23..0b4a2f769 100644 --- a/rsync.h +++ b/rsync.h @@ -1514,9 +1514,12 @@ const char *get_panic_action(void); /* Some header files needed to be nice ;) */ #ifdef HAVE_SYS_RESOURCE_H #define SUPPORT_RENICE +#include #endif #if defined HAVE_LINUX_IOPRIO_H && defined HAVE_SYS_SYSCALL_H #define SUPPORT_IONICE +#include +#include #endif From 9bfa9f0b9e1bbfee966b8078915f12b3bab2a975 Mon Sep 17 00:00:00 2001 From: Michael Mess Date: Sat, 30 May 2026 14:00:02 +0200 Subject: [PATCH 06/24] Ensure that rsync.h definitions are included only once --- rsync.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/rsync.h b/rsync.h index 0b4a2f769..0c9e935ec 100644 --- a/rsync.h +++ b/rsync.h @@ -18,6 +18,9 @@ * with this program; if not, visit the http://fsf.org website. */ +#ifndef RSYNC_H // Ensure that this header file is never included more than once, thus avoiding errors with duplicate definitions +#define RSYNC_H 1 + #define False 0 #define True 1 #define Unset (-1) /* Our BOOL values are always an int. */ @@ -1523,3 +1526,4 @@ const char *get_panic_action(void); #include #endif +#endif // ifndef RSYNC_H From 185169d24eb80214d3cd1e3c50ab38f0d6b8dc1a Mon Sep 17 00:00:00 2001 From: Michael Mess Date: Sat, 30 May 2026 20:59:45 +0200 Subject: [PATCH 07/24] Option parsing for nice and ionice and remote argument --- options.c | 54 +++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 53 insertions(+), 1 deletion(-) diff --git a/options.c b/options.c index 8568af2b2..ff952e4b9 100644 --- a/options.c +++ b/options.c @@ -35,6 +35,12 @@ extern filter_rule_list daemon_filter_list; int make_backups = 0; +/* If set, be nice on the local or remote site */ +int nice_local = 0; +int nice_remote = 0; +int ionice_local = 0; +int ionice_remote = 0; + /** * If 1, send the whole file as literal data rather than trying to * create an incremental diff. @@ -594,7 +600,7 @@ enum {OPT_SERVER = 1000, OPT_DAEMON, OPT_SENDER, OPT_EXCLUDE, OPT_EXCLUDE_FROM, OPT_NO_D, OPT_APPEND, OPT_NO_ICONV, OPT_INFO, OPT_DEBUG, OPT_BLOCK_SIZE, OPT_USERMAP, OPT_GROUPMAP, OPT_CHOWN, OPT_BWLIMIT, OPT_STDERR, OPT_OLD_COMPRESS, OPT_NEW_COMPRESS, OPT_NO_COMPRESS, OPT_OLD_ARGS, - OPT_STOP_AFTER, OPT_STOP_AT, + OPT_STOP_AFTER, OPT_STOP_AT, OPT_NICE, OPT_NO_NICE, OPT_IONICE, OPT_NO_IONICE, OPT_REFUSED_BASE = 9000}; static struct poptOption long_options[] = { @@ -845,6 +851,19 @@ static struct poptOption long_options[] = { {"remote-option", 'M', POPT_ARG_STRING, 0, 'M', 0, 0 }, {"protocol", 0, POPT_ARG_INT, &protocol_version, 0, 0, 0 }, {"checksum-seed", 0, POPT_ARG_INT, &checksum_seed, 0, 0, 0 }, + {"nice-local", 0, POPT_ARG_VAL, &nice_local, 1, 0, 0 }, + {"nice-remote", 0, POPT_ARG_VAL, &nice_remote, 1, 0, 0 }, + {"ionice-local", 0, POPT_ARG_VAL, &ionice_local, 1, 0, 0 }, + {"ionice-remote", 0, POPT_ARG_VAL, &ionice_remote, 1, 0, 0 }, + {"no-nice-local", 0, POPT_ARG_VAL, &nice_local, 0, 0, 0 }, + {"no-nice-remote", 0, POPT_ARG_VAL, &nice_remote, 0, 0, 0 }, + {"no-ionice-local", 0, POPT_ARG_VAL, &ionice_local, 0, 0, 0 }, + {"no-ionice-remote", 0, POPT_ARG_VAL, &ionice_remote, 0, 0, 0 }, + {"nice", 0, POPT_ARG_NONE, 0, OPT_NICE, 0, 0 }, + {"ionice", 0, POPT_ARG_NONE, 0, OPT_IONICE, 0, 0 }, + {"no-nice", 0, POPT_ARG_NONE, 0, OPT_NO_NICE, 0, 0 }, + {"no-ionice", 0, POPT_ARG_NONE, 0, OPT_NO_IONICE, 0, 0 }, + {"nice-and-ionice", 'Q', POPT_ARG_NONE, 0, 'Q', 0, 0 }, {"server", 0, POPT_ARG_NONE, 0, OPT_SERVER, 0, 0 }, {"sender", 0, POPT_ARG_NONE, 0, OPT_SENDER, 0, 0 }, /* All the following options switch us into daemon-mode option-parsing. */ @@ -1597,6 +1616,33 @@ int parse_arguments(int *argc_p, const char ***argv_p) quiet++; break; + case 'Q': + nice_local = 1; + nice_remote = 1; + ionice_local = 1; + ionice_remote = 1; + break; + + case OPT_NICE: + nice_local = 1; + nice_remote = 1; + break; + + case OPT_IONICE: + ionice_local = 1; + ionice_remote = 1; + break; + + case OPT_NO_NICE: + nice_local = 0; + nice_remote = 0; + break; + + case OPT_NO_IONICE: + ionice_local = 0; + ionice_remote = 0; + break; + case 'x': one_file_system++; break; @@ -2995,6 +3041,12 @@ void server_options(char **args, int *argc_p) if (mkpath_dest_arg && am_sender) args[ac++] = "--mkpath"; + if (nice_remote) + args[ac++] = "--nice-local"; + + if (ionice_remote) + args[ac++] = "--ionice-local"; + if (ac > MAX_SERVER_ARGS) { /* Not possible... */ rprintf(FERROR, "argc overflow in server_options().\n"); exit_cleanup(RERR_MALLOC); From cb8af0356a845fa91c696e9820e6f6177aa8c072 Mon Sep 17 00:00:00 2001 From: Michael Mess Date: Sat, 30 May 2026 21:29:39 +0200 Subject: [PATCH 08/24] rsync --help --- rsync.1.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/rsync.1.md b/rsync.1.md index fdf0b2e95..72c06a82d 100644 --- a/rsync.1.md +++ b/rsync.1.md @@ -565,6 +565,13 @@ has its own detailed description later in this manpage. --checksum-seed=NUM set block/file checksum seed (advanced) --ipv4, -4 prefer IPv4 --ipv6, -6 prefer IPv6 +--nice-local renice local process to low priority +--nice-remote renice remote process to low priority +--ionice-local ionice local process to low priority +--ionice-remote ionice remote process to low priority +--nice, --ionice set local and remote process to low priority +--no-nice, ... prepending "no-" turns the given option off +--nice-and-ionice, -Q same as --nice --ionice --version, -V print the version + other info and exit --help, -h (*) show this help (* -h is help only on its own) ``` From e805671e1c7e3430e1ea9bb16d79f5067dc24f05 Mon Sep 17 00:00:00 2001 From: Michael Mess Date: Sat, 30 May 2026 21:40:33 +0200 Subject: [PATCH 09/24] rsync help for daemon process --- rsync.1.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/rsync.1.md b/rsync.1.md index 72c06a82d..96bc9ff98 100644 --- a/rsync.1.md +++ b/rsync.1.md @@ -592,6 +592,9 @@ accepted: --log-file=FILE override the "log file" setting --log-file-format=FMT override the "log format" setting --sockopts=OPTIONS specify custom TCP options +--nice-local renice local daemon process to low priority +--ionice-local ionice local daemon process to low priority + They are given at client site as (io)nice-remote --verbose, -v increase verbosity --ipv4, -4 prefer IPv4 --ipv6, -6 prefer IPv6 From 46a62d27e077de6465b6e416522787afac694401 Mon Sep 17 00:00:00 2001 From: Michael Mess Date: Sat, 30 May 2026 21:49:41 +0200 Subject: [PATCH 10/24] Set renice or ionice locally --- main.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/main.c b/main.c index 9b52bbe6a..b1205c3cf 100644 --- a/main.c +++ b/main.c @@ -107,6 +107,8 @@ extern char backup_dir_buf[MAXPATHLEN]; extern char *basis_dir[MAX_BASIS_DIRS+1]; extern struct file_list *first_flist; extern filter_rule_list daemon_filter_list, implied_filter_list; +extern int nice_local; +extern int ionice_local; uid_t our_uid; gid_t our_gid; @@ -1844,6 +1846,14 @@ int main(int argc,char *argv[]) if (write_batch < 0) dry_run = 1; + if (nice_local) { + renice_me(); + } + + if (ionice_local) { + ionice_me(); + } + if (am_server) { #ifdef ICONV_CONST setup_iconv(); From 9ad614884fb7b7e523aa628a625f0194f58020f4 Mon Sep 17 00:00:00 2001 From: Michael Mess Date: Sun, 31 May 2026 09:39:02 +0200 Subject: [PATCH 11/24] Added debug/warning messages for renice/ionice --- configure.ac | 2 ++ niceness.c | 22 ++++++++++++++++++++-- 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/configure.ac b/configure.ac index 2ff1f92be..f6cfef994 100644 --- a/configure.ac +++ b/configure.ac @@ -1479,6 +1479,8 @@ case "$CC" in ;; esac +AC_DEFINE_UNQUOTED([COMPILE_TARGET], ["$host"], [String describing the compiled target OS and architecture]) + AC_CONFIG_FILES([Makefile lib/dummy zlib/dummy popt/dummy shconfig]) AC_OUTPUT diff --git a/niceness.c b/niceness.c index f1625f061..37bbb08b0 100644 --- a/niceness.c +++ b/niceness.c @@ -19,6 +19,8 @@ #include "rsync.h" +extern int am_server; + void renice_me() { #ifdef SUPPORT_RENICE @@ -27,8 +29,16 @@ void renice_me() int prio = 19; // lowest CPU Priority int result = setpriority(which, who, prio); if ( result < 0 ) { - // Failed to set priority, TODO: log or inform user, but can be ignored (it's just not so nice). + // Failed to set priority, inform user, but can be ignored (it's just not so nice). + rprintf(FWARNING, "renice rejected by OS (%s version %s)\n", + RSYNC_NAME, rsync_version()); + } else { + if (DEBUG_GTE(CMD, 1)) + rprintf(FINFO, "successfully reniced %s\n", am_server ? "server" : "client"); } +#else + rprintf(FWARNING, "renice not supported for %s (%s: %s version %s)\n", + COMPILE_TARGET, am_server ? "server" : "client", RSYNC_NAME, rsync_version()); #endif } @@ -42,7 +52,15 @@ void ionice_me() int ioprio = IOPRIO_PRIO_VALUE(class, data); int result = syscall(SYS_ioprio_set, which, who, ioprio); if ( result < 0 ) { - // Failed to set priority, TODO: log or inform user, but can be ignored (it's just not so ionice). + // Failed to set priority, inform user, but can be ignored (it's just not so ionice). + rprintf(FWARNING, "ionice rejected by OS (%s version %s)\n", + RSYNC_NAME, rsync_version()); + } else { + if (DEBUG_GTE(CMD, 1)) + rprintf(FINFO, "successfully ioniced %s\n", am_server ? "server" : "client"); } +#else + rprintf(FWARNING, "ionice not supported for %s (%s: %s version %s)\n", + COMPILE_TARGET, am_server ? "server" : "client", RSYNC_NAME, rsync_version()); #endif } From 1ae1b117d2fea0d1efe8ef2d4dbef39d12abdb72 Mon Sep 17 00:00:00 2001 From: Michael Mess Date: Mon, 1 Jun 2026 17:21:49 +0200 Subject: [PATCH 12/24] Added some files generated by unit test cases to .gitignore. --- .gitignore | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/.gitignore b/.gitignore index 59a6d09eb..964463b1b 100644 --- a/.gitignore +++ b/.gitignore @@ -60,3 +60,11 @@ aclocal.m4 .deps /*.exe *.dSYM/ +simdtest +t_chmod_secure +t_secure_relpath +testsuite/__pycache__/ +testsuite/chown-fake_test.py +testsuite/devices-fake_test.py +testsuite/exclude-lsh_test.py +testsuite/xattrs-hlink_test.py From 7448a895fbddd808fae357c0dd72ac7de07356d5 Mon Sep 17 00:00:00 2001 From: Michael Mess Date: Mon, 1 Jun 2026 18:25:17 +0200 Subject: [PATCH 13/24] Turned nice_local and nice_remote from a simple on/off switch to a value for the nice level --- main.c | 2 +- niceness.c | 8 ++++++-- options.c | 45 +++++++++++++++++++++++++++++++++++++-------- 3 files changed, 44 insertions(+), 11 deletions(-) diff --git a/main.c b/main.c index b1205c3cf..7622b9202 100644 --- a/main.c +++ b/main.c @@ -1847,7 +1847,7 @@ int main(int argc,char *argv[]) dry_run = 1; if (nice_local) { - renice_me(); + renice_me(nice_local); } if (ionice_local) { diff --git a/niceness.c b/niceness.c index 37bbb08b0..a13c3aea6 100644 --- a/niceness.c +++ b/niceness.c @@ -21,12 +21,16 @@ extern int am_server; -void renice_me() +int get_renice_default_prio() +{ + return 19; // lowest CPU Priority +} + +void renice_me(int prio) { #ifdef SUPPORT_RENICE int which = PRIO_PROCESS; // who specifies a Process ID int who = 0; // 0 means current process - int prio = 19; // lowest CPU Priority int result = setpriority(which, who, prio); if ( result < 0 ) { // Failed to set priority, inform user, but can be ignored (it's just not so nice). diff --git a/options.c b/options.c index ff952e4b9..bf1b5a647 100644 --- a/options.c +++ b/options.c @@ -35,9 +35,16 @@ extern filter_rule_list daemon_filter_list; int make_backups = 0; -/* If set, be nice on the local or remote site */ +/* If set to other than 0, be nice on the local or remote site + * Contains the nice priority to be set on the process, or 0=feature is turned off, no priority will be set. + * I have to admit, that it is not possible to set a nice value of 0 with this code, + * but in most cases 0 should be the priority of the newly started rsync process already anyway. + */ int nice_local = 0; int nice_remote = 0; +/* If set to other than 0, be ionice on the local or remote site + * Currently only idle is supported for ionice when turned on. + */ int ionice_local = 0; int ionice_remote = 0; @@ -600,7 +607,8 @@ enum {OPT_SERVER = 1000, OPT_DAEMON, OPT_SENDER, OPT_EXCLUDE, OPT_EXCLUDE_FROM, OPT_NO_D, OPT_APPEND, OPT_NO_ICONV, OPT_INFO, OPT_DEBUG, OPT_BLOCK_SIZE, OPT_USERMAP, OPT_GROUPMAP, OPT_CHOWN, OPT_BWLIMIT, OPT_STDERR, OPT_OLD_COMPRESS, OPT_NEW_COMPRESS, OPT_NO_COMPRESS, OPT_OLD_ARGS, - OPT_STOP_AFTER, OPT_STOP_AT, OPT_NICE, OPT_NO_NICE, OPT_IONICE, OPT_NO_IONICE, + OPT_STOP_AFTER, OPT_STOP_AT, OPT_NICE, OPT_NO_NICE, OPT_IONICE, OPT_NO_IONICE, + OPT_NICE_LOCAL, OPT_NICE_REMOTE, OPT_REFUSED_BASE = 9000}; static struct poptOption long_options[] = { @@ -851,8 +859,8 @@ static struct poptOption long_options[] = { {"remote-option", 'M', POPT_ARG_STRING, 0, 'M', 0, 0 }, {"protocol", 0, POPT_ARG_INT, &protocol_version, 0, 0, 0 }, {"checksum-seed", 0, POPT_ARG_INT, &checksum_seed, 0, 0, 0 }, - {"nice-local", 0, POPT_ARG_VAL, &nice_local, 1, 0, 0 }, - {"nice-remote", 0, POPT_ARG_VAL, &nice_remote, 1, 0, 0 }, + {"nice-local", 0, POPT_ARG_NONE, 0, OPT_NICE_LOCAL, 0, 0 }, + {"nice-remote", 0, POPT_ARG_NONE, 0, OPT_NICE_REMOTE, 0, 0 }, {"ionice-local", 0, POPT_ARG_VAL, &ionice_local, 1, 0, 0 }, {"ionice-remote", 0, POPT_ARG_VAL, &ionice_remote, 1, 0, 0 }, {"no-nice-local", 0, POPT_ARG_VAL, &nice_local, 0, 0, 0 }, @@ -1384,6 +1392,7 @@ int parse_arguments(int *argc_p, const char ***argv_p) int argc = *argc_p; int opt, want_dest_type; int orig_protect_args = protect_args; + int default_nice_prio = get_renice_default_prio(); // default nice priority to be used, if no explicit priority is given. if (argc == 0) { strlcpy(err_buf, "argc is zero!\n", sizeof err_buf); @@ -1617,15 +1626,35 @@ int parse_arguments(int *argc_p, const char ***argv_p) break; case 'Q': - nice_local = 1; - nice_remote = 1; + /* + * Turn nice on with default prio when it was turned off, but do not overwrite any previously set nice value. + */ + nice_local = nice_local==0 ? default_nice_prio : nice_local; + nice_remote = nice_remote==0 ? default_nice_prio : nice_remote; ionice_local = 1; ionice_remote = 1; break; case OPT_NICE: - nice_local = 1; - nice_remote = 1; + /* + * Turn nice on with default prio when it was turned off, but do not overwrite any previously set nice value. + */ + nice_local = nice_local==0 ? default_nice_prio : nice_local; + nice_remote = nice_remote==0 ? default_nice_prio : nice_remote; + break; + + case OPT_NICE_LOCAL: + /* + * Turn nice on with default prio when it was turned off, but do not overwrite any previously set nice value. + */ + nice_local = nice_local==0 ? default_nice_prio : nice_local; + break; + + case OPT_NICE_REMOTE: + /* + * Turn nice on with default prio when it was turned off, but do not overwrite any previously set nice value. + */ + nice_remote = nice_remote==0 ? default_nice_prio : nice_remote; break; case OPT_IONICE: From b8ff5eddb8c9396cd4bb4e45f7166a5b48afd10d Mon Sep 17 00:00:00 2001 From: Michael Mess Date: Tue, 2 Jun 2026 00:16:20 +0200 Subject: [PATCH 14/24] prepare command line option --nice-local for remote rsync with and without given nice value (nice-remote) --- options.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/options.c b/options.c index bf1b5a647..5cda7685f 100644 --- a/options.c +++ b/options.c @@ -860,7 +860,9 @@ static struct poptOption long_options[] = { {"protocol", 0, POPT_ARG_INT, &protocol_version, 0, 0, 0 }, {"checksum-seed", 0, POPT_ARG_INT, &checksum_seed, 0, 0, 0 }, {"nice-local", 0, POPT_ARG_NONE, 0, OPT_NICE_LOCAL, 0, 0 }, + {"nice-local-value", 0, POPT_ARG_INT, &nice_local, 0, 0, 0 }, {"nice-remote", 0, POPT_ARG_NONE, 0, OPT_NICE_REMOTE, 0, 0 }, + {"nice-remote-value",0, POPT_ARG_INT, &nice_remote, 0, 0, 0 }, {"ionice-local", 0, POPT_ARG_VAL, &ionice_local, 1, 0, 0 }, {"ionice-remote", 0, POPT_ARG_VAL, &ionice_remote, 1, 0, 0 }, {"no-nice-local", 0, POPT_ARG_VAL, &nice_local, 0, 0, 0 }, @@ -3070,8 +3072,15 @@ void server_options(char **args, int *argc_p) if (mkpath_dest_arg && am_sender) args[ac++] = "--mkpath"; - if (nice_remote) - args[ac++] = "--nice-local"; + if (nice_remote) { + if (get_renice_default_prio()==nice_remote) { + args[ac++] = "--nice-local"; + } else { + if (asprintf(&arg, "--nice-local-value=%d", nice_remote) < 0) + goto oom; + args[ac++] = arg; + } + } if (ionice_remote) args[ac++] = "--ionice-local"; From fd83f1a715fc18b22f16bbe9cf6d93a9c9434554 Mon Sep 17 00:00:00 2001 From: Michael Mess Date: Tue, 2 Jun 2026 00:29:05 +0200 Subject: [PATCH 15/24] Renice: Print more details about new priority and error on failure --- niceness.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/niceness.c b/niceness.c index a13c3aea6..3f5b0fcb5 100644 --- a/niceness.c +++ b/niceness.c @@ -34,11 +34,11 @@ void renice_me(int prio) int result = setpriority(which, who, prio); if ( result < 0 ) { // Failed to set priority, inform user, but can be ignored (it's just not so nice). - rprintf(FWARNING, "renice rejected by OS (%s version %s)\n", - RSYNC_NAME, rsync_version()); + rprintf(FWARNING, "renice to %d rejected by OS (%s version %s): %s\n", + prio, RSYNC_NAME, rsync_version(), strerror(errno)); } else { if (DEBUG_GTE(CMD, 1)) - rprintf(FINFO, "successfully reniced %s\n", am_server ? "server" : "client"); + rprintf(FINFO, "successfully reniced %s to new priority %d\n", am_server ? "server" : "client", prio); } #else rprintf(FWARNING, "renice not supported for %s (%s: %s version %s)\n", @@ -57,7 +57,7 @@ void ionice_me() int result = syscall(SYS_ioprio_set, which, who, ioprio); if ( result < 0 ) { // Failed to set priority, inform user, but can be ignored (it's just not so ionice). - rprintf(FWARNING, "ionice rejected by OS (%s version %s)\n", + rprintf(FWARNING, "ionice rejected by OS (%s version %s)\n", RSYNC_NAME, rsync_version()); } else { if (DEBUG_GTE(CMD, 1)) From 9e2721a61d78286b7b2a8ff352c01ed314895088 Mon Sep 17 00:00:00 2001 From: Michael Mess Date: Tue, 2 Jun 2026 17:39:18 +0200 Subject: [PATCH 16/24] Implemented --nice-value option --- options.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/options.c b/options.c index 5cda7685f..302612258 100644 --- a/options.c +++ b/options.c @@ -608,7 +608,7 @@ enum {OPT_SERVER = 1000, OPT_DAEMON, OPT_SENDER, OPT_EXCLUDE, OPT_EXCLUDE_FROM, OPT_USERMAP, OPT_GROUPMAP, OPT_CHOWN, OPT_BWLIMIT, OPT_STDERR, OPT_OLD_COMPRESS, OPT_NEW_COMPRESS, OPT_NO_COMPRESS, OPT_OLD_ARGS, OPT_STOP_AFTER, OPT_STOP_AT, OPT_NICE, OPT_NO_NICE, OPT_IONICE, OPT_NO_IONICE, - OPT_NICE_LOCAL, OPT_NICE_REMOTE, + OPT_NICE_LOCAL, OPT_NICE_REMOTE, OPT_NICE_VALUE, OPT_REFUSED_BASE = 9000}; static struct poptOption long_options[] = { @@ -870,6 +870,7 @@ static struct poptOption long_options[] = { {"no-ionice-local", 0, POPT_ARG_VAL, &ionice_local, 0, 0, 0 }, {"no-ionice-remote", 0, POPT_ARG_VAL, &ionice_remote, 0, 0, 0 }, {"nice", 0, POPT_ARG_NONE, 0, OPT_NICE, 0, 0 }, + {"nice-value", 0, POPT_ARG_INT, &nice_local, OPT_NICE_VALUE, 0, 0 }, {"ionice", 0, POPT_ARG_NONE, 0, OPT_IONICE, 0, 0 }, {"no-nice", 0, POPT_ARG_NONE, 0, OPT_NO_NICE, 0, 0 }, {"no-ionice", 0, POPT_ARG_NONE, 0, OPT_NO_IONICE, 0, 0 }, @@ -1645,6 +1646,13 @@ int parse_arguments(int *argc_p, const char ***argv_p) nice_remote = nice_remote==0 ? default_nice_prio : nice_remote; break; + case OPT_NICE_VALUE: + /* + * POPT writes to nice_local. We have to copy the value from nice-local to nice-remote here. + */ + nice_remote = nice_local; + break; + case OPT_NICE_LOCAL: /* * Turn nice on with default prio when it was turned off, but do not overwrite any previously set nice value. From f73a4a02466a77232b0101b20bd426ef687dbf76 Mon Sep 17 00:00:00 2001 From: Michael Mess Date: Tue, 2 Jun 2026 18:15:46 +0200 Subject: [PATCH 17/24] Updated help --- rsync.1.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/rsync.1.md b/rsync.1.md index 96bc9ff98..4613a66e9 100644 --- a/rsync.1.md +++ b/rsync.1.md @@ -570,6 +570,9 @@ has its own detailed description later in this manpage. --ionice-local ionice local process to low priority --ionice-remote ionice remote process to low priority --nice, --ionice set local and remote process to low priority +--nice-local-value=10 renice local process to given priority +--nice-remote-value=10 renice remote process to given priority +--nice-value=10 renice local+remote process to given priority --no-nice, ... prepending "no-" turns the given option off --nice-and-ionice, -Q same as --nice --ionice --version, -V print the version + other info and exit From bad595104cffecde6ae480abc5dc82abe829cb3e Mon Sep 17 00:00:00 2001 From: Michael Mess Date: Sat, 13 Jun 2026 14:21:44 +0200 Subject: [PATCH 18/24] Test that the nice/ionice value is correctly handed over to remote command --- testsuite/niceness_test.py | 192 +++++++++++++++++++++++++++++++++++++ 1 file changed, 192 insertions(+) create mode 100644 testsuite/niceness_test.py diff --git a/testsuite/niceness_test.py b/testsuite/niceness_test.py new file mode 100644 index 000000000..b0cc210df --- /dev/null +++ b/testsuite/niceness_test.py @@ -0,0 +1,192 @@ +#!/usr/bin/env python3 +# Python handling of niceness options like --nice or --ionice. +# +# Foundational smoke test: --version / --info=help / --debug=help all +# work with --nice or --ionice without crash, +# plus test passing the correct options to the remote command + +import os + +from rsyncfns import ( + FROMDIR, RSYNC, SRCDIR, TODIR, + checkit, run_rsync, test_fail, +) + +# Helper function +def fail(msg: str) -> 'None': + print('### TEST FAILURE ### ' + msg + '\n') + test_fail(msg) + +# Basic help dumps must not crash when using any niceness option. +if run_rsync('-Q', '--version', check=False).returncode != 0: + fail('-Q --version output failed') +if run_rsync('-Q', '--info=help', check=False).returncode != 0: + fail('-Q --info=help output failed') +if run_rsync('-Q', '--debug=help', check=False).returncode != 0: + fail('-Q --debug=help output failed') + + +def test_remote_args(testname: str, args: list, expected_remote_args: list) -> None: + print('### TEST BEGIN ### ' + testname + '\n') + probe = run_rsync(*['-nvv', '--rsh=echo', *args], check=False, capture_output=True) + if probe.returncode == 0: + fail('The command should not succeed here.') + actual_remote_args = 'not found' + for remote_line in probe.stdout.splitlines(): + if 'opening connection using' in remote_line: + actual_remote_args = remote_line.strip() + else: + print('ignoring line: ' + remote_line) + if 'not found' == actual_remote_args: + fail('No remote shell command has been started or it was not logged.') + for string in expected_remote_args: + if string[0] == '!': + substring = string[1:] + if (substring+' ') in actual_remote_args: + fail('Remote command line contains unexpected ' + substring + ': ' + actual_remote_args) + else: + if not (string+' ') in actual_remote_args: + fail('Remote command line did not contain expected ' + string + ': ' + actual_remote_args) + print('### TEST END ### ' + testname + '\n') + +test_remote_args('Check with no flags', [f'localhost:{FROMDIR}', TODIR], ["--server", "--sender", "!--blahblah", + "!--nice", + "!--nice-local", + "!--nice-local-value=4", + "!--nice-remote", + "!--nice-remote-value=6", + "!--ionice", + "!--ionice-local", + "!--ionice-remote", + "!-Q" + ]) + +test_remote_args('Check with -Q', ['-Q', f'localhost:{FROMDIR}', TODIR], ["--server", "--sender", "!--blahblah", + "!--nice", + "--nice-local", + "!--nice-local-value=4", + "!--nice-remote", + "!--nice-remote-value=6", + "!--ionice", + "--ionice-local", + "!--ionice-remote", + "!-Q" + ]) + +test_remote_args('Check with --nice', ['--nice', f'localhost:{FROMDIR}', TODIR], ["--server", "--sender", "!--blahblah", + "!--nice", + "--nice-local", + "!--nice-local-value=4", + "!--nice-remote", + "!--nice-remote-value=6", + "!--ionice", + "!--ionice-local", + "!--ionice-remote", + "!-Q" + ]) + +test_remote_args('Check with --nice-local', ['--nice-local', f'localhost:{FROMDIR}', TODIR], ["--server", "--sender", "!--blahblah", + "!--nice", + "!--nice-local", + "!--nice-local-value=4", + "!--nice-remote", + "!--nice-remote-value=6", + "!--ionice", + "!--ionice-local", + "!--ionice-remote", + "!-Q" + ]) + +test_remote_args('Check with --nice-remote', ['--nice-remote', f'localhost:{FROMDIR}', TODIR], ["--server", "--sender", "!--blahblah", + "!--nice", + "--nice-local", + "!--nice-local-value=4", + "!--nice-remote", + "!--nice-remote-value=6", + "!--ionice", + "!--ionice-local", + "!--ionice-remote", + "!-Q" + ]) + +test_remote_args('Check with --ionice', ['--ionice', f'localhost:{FROMDIR}', TODIR], ["--server", "--sender", "!--blahblah", + "!--nice", + "!--nice-local", + "!--nice-local-value=4", + "!--nice-remote", + "!--nice-remote-value=6", + "!--ionice", + "--ionice-local", + "!--ionice-remote", + "!-Q" + ]) + +test_remote_args('Check with --ionice-local', ['--ionice-local', f'localhost:{FROMDIR}', TODIR], ["--server", "--sender", "!--blahblah", + "!--nice", + "!--nice-local", + "!--nice-local-value=4", + "!--nice-remote", + "!--nice-remote-value=6", + "!--ionice", + "!--ionice-local", + "!--ionice-remote", + "!-Q" + ]) + +test_remote_args('Check with --ionice-remote', ['--ionice-remote', f'localhost:{FROMDIR}', TODIR], ["--server", "--sender", "!--blahblah", + "!--nice", + "!--nice-local", + "!--nice-local-value=4", + "!--nice-remote", + "!--nice-remote-value=6", + "!--ionice", + "--ionice-local", + "!--ionice-remote", + "!-Q" + ]) + +test_remote_args('Check with --nice-local-value=4', ['--nice-local-value=4', f'localhost:{FROMDIR}', TODIR], ["--server", "--sender", "!--blahblah", + "!--nice", + "!--nice-local", + "!--nice-local-value=4", + "!--nice-local-value=6", + "!--nice-remote", + "!--nice-remote-value=4", + "!--nice-remote-value=6", + "!--ionice", + "!--ionice-local", + "!--ionice-remote", + "!-Q" + ]) + +test_remote_args('Check with --nice-remote-value=6', ['--nice-remote-value=6', f'localhost:{FROMDIR}', TODIR], ["--server", "--sender", "!--blahblah", + "!--nice", + "!--nice-local", + "!--nice-local-value=4", + "--nice-local-value=6", + "!--nice-remote", + "!--nice-remote-value=4", + "!--nice-remote-value=6", + "!--ionice", + "!--ionice-local", + "!--ionice-remote", + "!-Q" + ]) + + +test_remote_args('Check with --nice-value=6', ['--nice-value=6', f'localhost:{FROMDIR}', TODIR], ["--server", "--sender", "!--blahblah", + "!--nice", + "!--nice-local", + "!--nice-local-value=4", + "--nice-local-value=6", + "!--nice-remote", + "!--nice-remote-value=4", + "!--nice-remote-value=6", + "!--ionice", + "!--ionice-local", + "!--ionice-remote", + "!-Q" + ]) + + + From 6436031e08e59ec2baee8544bf68d86a68b60bf9 Mon Sep 17 00:00:00 2001 From: Michael Mess Date: Sun, 14 Jun 2026 23:27:34 +0200 Subject: [PATCH 19/24] Added description and disabled test case for OpenBSD, because there the test case seems not to do what is expected --- testsuite/niceness_test.py | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/testsuite/niceness_test.py b/testsuite/niceness_test.py index b0cc210df..c4c9fc12d 100644 --- a/testsuite/niceness_test.py +++ b/testsuite/niceness_test.py @@ -6,12 +6,19 @@ # plus test passing the correct options to the remote command import os +import platform from rsyncfns import ( FROMDIR, RSYNC, SRCDIR, TODIR, checkit, run_rsync, test_fail, ) +if platform.system() in ('OpenBSD'): + test_skipped( + f"test fails with 'ssh exited with code 1' instead of ignoring the expected error." + f"support not available on {platform.system()}" + ) + # Helper function def fail(msg: str) -> 'None': print('### TEST FAILURE ### ' + msg + '\n') @@ -25,7 +32,15 @@ def fail(msg: str) -> 'None': if run_rsync('-Q', '--debug=help', check=False).returncode != 0: fail('-Q --debug=help output failed') - +# We just want to test which parameters are handed over to the remote, +# but do not really want to run rsync on the remote site to copy files. +# For this we add "--rsh=echo" to replace ssh for the remote site with echo. +# Thus no remote connection is actually created, instead the echo will just +# exit quickly without any further rsync communication, letting the local +# rsync process fail fast, not wasting much time for the test execution. +# It is expected that the local rsync process will exit here with non zero. +# We then just check the log output here for any options that may or may not +# have been given for the remote rsync process by our local rsync process. def test_remote_args(testname: str, args: list, expected_remote_args: list) -> None: print('### TEST BEGIN ### ' + testname + '\n') probe = run_rsync(*['-nvv', '--rsh=echo', *args], check=False, capture_output=True) From a2a0d2d5a8c6160c45cb351dbf78a4dec73eaa6d Mon Sep 17 00:00:00 2001 From: Michael Mess Date: Sun, 14 Jun 2026 23:36:57 +0200 Subject: [PATCH 20/24] Added missing import for test_skipped --- testsuite/niceness_test.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/testsuite/niceness_test.py b/testsuite/niceness_test.py index c4c9fc12d..085bd1ab9 100644 --- a/testsuite/niceness_test.py +++ b/testsuite/niceness_test.py @@ -10,7 +10,7 @@ from rsyncfns import ( FROMDIR, RSYNC, SRCDIR, TODIR, - checkit, run_rsync, test_fail, + checkit, run_rsync, test_fail, test_skipped ) if platform.system() in ('OpenBSD'): From f9ff2262ad5659863e0cf71f7fc8b0627420b39f Mon Sep 17 00:00:00 2001 From: Michael Mess Date: Tue, 16 Jun 2026 02:49:33 +0200 Subject: [PATCH 21/24] Reworked option parsing to have only the --nice option with argument parsing for setting the nice/ionice values for local and/or remote --- niceness.c | 148 ++++++++++++++++++++++++++++++++++++- options.c | 89 +++++++--------------- testsuite/niceness_test.py | 71 ++++++++++-------- 3 files changed, 215 insertions(+), 93 deletions(-) diff --git a/niceness.c b/niceness.c index 3f5b0fcb5..1ab8bcba2 100644 --- a/niceness.c +++ b/niceness.c @@ -21,6 +21,152 @@ extern int am_server; + +/* + * Try to parse the location and set the variables isLocal and isRemote. + * Return the pointer to the string after the found string. + */ +char *parse_location(char *str, int *isLocal, int *isRemote) +{ + if (strncmp(str, "local", 5) == 0) + { + *isLocal=1; + *isRemote=0; + return str+5; + } + if (strncmp(str, "remote", 6) == 0) + { + *isLocal=0; + *isRemote=1; + return str+6; + } + if (strncmp(str, "all", 3) == 0) + { + *isLocal=1; + *isRemote=1; + return str+3; + } + *isLocal=0; + *isRemote=0; + return str; +} + +char *parse_nice_value(char *str, int *nice_value) +{ + if (!str || !str[0]) return 0; // ERROR + char *pointer = str; + long nice_long = strtol(str, &pointer, 10); + if (nice_long<-20 || nice_long>20) return 0; + *nice_value=(int)nice_long; + return pointer; +} + +char *parse_ionice_value(char *str, int *ionice_value) +{ + if (strncmp(str, "none", 3) == 0) + { + *ionice_value=0; + return str+4; + } + if (strncmp(str, "idle", 3) == 0) + { + *ionice_value=1; + return str+4; + } + *ionice_value=0; + return str; +} + +char *parse_nice_and_ionice_values(char *str, int *nice_value, int *ionice_value) +{ + char *pointer = parse_ionice_value(str, ionice_value); + if (*ionice_value) { + *nice_value=0; + return pointer; + } + pointer = parse_nice_value(pointer, nice_value); + if (pointer == 0) return 0; // ERROR + if (pointer[0] == '/') // nice/ionice value + { + pointer++; + pointer = parse_ionice_value(pointer, ionice_value); + } + return pointer; +} + +/* + * parse the configuration string and return a pointer to the still not processed remainder of the configuration string + */ +char *parse_setting(char *config_str, int *nice_local, int *ionice_local, int *nice_remote, int *ionice_remote) +{ + int isLocal=0; + int isRemote=0; + char *pointer=parse_location(config_str, &isLocal, &isRemote); + if (isLocal || isRemote) + { /* Location "local" or "remote" or "all" found */ + if (pointer[0] == ':') + { /* Location followed by specific setting */ + pointer++; /* Skip colon */ + /* Read nice/ionice value */ + int nice_value=0; + int ionice_value=0; + pointer = parse_nice_and_ionice_values(pointer, &nice_value, &ionice_value); + if (isLocal) { + *nice_local=nice_value; + *ionice_local=ionice_value; + } + if (isRemote) { + *nice_remote=nice_value; + *ionice_remote=ionice_value; + } + return pointer; + } else { + if (!pointer[0] || pointer[0] == ',') + { /* End of string or comma */ + /* Location without specific setting, use default */ + int nice_default=get_renice_default_prio(); + int ionice_default=1; + if (isLocal) { + *nice_local=nice_default; + *ionice_local=ionice_default; + } + if (isRemote) { + *nice_remote=nice_default; + *ionice_remote=ionice_default; + } + return pointer; + } else + { /* Unexpected characters */ + return 0; // ERROR + } + } + } else { + /* Read nice/ionice value */ + int nice_value=0; + int ionice_value=0; + pointer = parse_nice_and_ionice_values(pointer, &nice_value, &ionice_value); + *nice_local=nice_value; + *ionice_local=ionice_value; + *nice_remote=nice_value; + *ionice_remote=ionice_value; + return pointer; + } + return 0; // ERROR +} + +int parse_nice(const char *config_str, int *nice_local, int *ionice_local, int *nice_remote, int *ionice_remote) +{ + char *str = (char *) config_str; + while (str && str[0]) { // string is valid and not empty + str=parse_setting(str, nice_local, ionice_local, nice_remote, ionice_remote); + if (!str) return 0; // ERROR returned by parser + if (!str[0]) return 1; // End of string - everything has been parsed - finished successfully! + if (str[0] != ',') return 0; // ERROR: Unexpected character, we expect a comma here as separator. + str++; + } + return 0; // string was empty or not valid (maybe even after a comma) +} + int get_renice_default_prio() { return 19; // lowest CPU Priority @@ -61,7 +207,7 @@ void ionice_me() RSYNC_NAME, rsync_version()); } else { if (DEBUG_GTE(CMD, 1)) - rprintf(FINFO, "successfully ioniced %s\n", am_server ? "server" : "client"); + rprintf(FINFO, "successfully ioniced %s to new priority idle\n", am_server ? "server" : "client"); } #else rprintf(FWARNING, "ionice not supported for %s (%s: %s version %s)\n", diff --git a/options.c b/options.c index 302612258..45f7210f9 100644 --- a/options.c +++ b/options.c @@ -607,8 +607,7 @@ enum {OPT_SERVER = 1000, OPT_DAEMON, OPT_SENDER, OPT_EXCLUDE, OPT_EXCLUDE_FROM, OPT_NO_D, OPT_APPEND, OPT_NO_ICONV, OPT_INFO, OPT_DEBUG, OPT_BLOCK_SIZE, OPT_USERMAP, OPT_GROUPMAP, OPT_CHOWN, OPT_BWLIMIT, OPT_STDERR, OPT_OLD_COMPRESS, OPT_NEW_COMPRESS, OPT_NO_COMPRESS, OPT_OLD_ARGS, - OPT_STOP_AFTER, OPT_STOP_AT, OPT_NICE, OPT_NO_NICE, OPT_IONICE, OPT_NO_IONICE, - OPT_NICE_LOCAL, OPT_NICE_REMOTE, OPT_NICE_VALUE, + OPT_STOP_AFTER, OPT_STOP_AT, OPT_NICE, OPT_NO_NICE, OPT_REFUSED_BASE = 9000}; static struct poptOption long_options[] = { @@ -859,22 +858,9 @@ static struct poptOption long_options[] = { {"remote-option", 'M', POPT_ARG_STRING, 0, 'M', 0, 0 }, {"protocol", 0, POPT_ARG_INT, &protocol_version, 0, 0, 0 }, {"checksum-seed", 0, POPT_ARG_INT, &checksum_seed, 0, 0, 0 }, - {"nice-local", 0, POPT_ARG_NONE, 0, OPT_NICE_LOCAL, 0, 0 }, - {"nice-local-value", 0, POPT_ARG_INT, &nice_local, 0, 0, 0 }, - {"nice-remote", 0, POPT_ARG_NONE, 0, OPT_NICE_REMOTE, 0, 0 }, - {"nice-remote-value",0, POPT_ARG_INT, &nice_remote, 0, 0, 0 }, - {"ionice-local", 0, POPT_ARG_VAL, &ionice_local, 1, 0, 0 }, - {"ionice-remote", 0, POPT_ARG_VAL, &ionice_remote, 1, 0, 0 }, - {"no-nice-local", 0, POPT_ARG_VAL, &nice_local, 0, 0, 0 }, - {"no-nice-remote", 0, POPT_ARG_VAL, &nice_remote, 0, 0, 0 }, - {"no-ionice-local", 0, POPT_ARG_VAL, &ionice_local, 0, 0, 0 }, - {"no-ionice-remote", 0, POPT_ARG_VAL, &ionice_remote, 0, 0, 0 }, - {"nice", 0, POPT_ARG_NONE, 0, OPT_NICE, 0, 0 }, - {"nice-value", 0, POPT_ARG_INT, &nice_local, OPT_NICE_VALUE, 0, 0 }, - {"ionice", 0, POPT_ARG_NONE, 0, OPT_IONICE, 0, 0 }, + {"nice", 0, POPT_ARG_STRING, 0, OPT_NICE, 0, 0 }, {"no-nice", 0, POPT_ARG_NONE, 0, OPT_NO_NICE, 0, 0 }, - {"no-ionice", 0, POPT_ARG_NONE, 0, OPT_NO_IONICE, 0, 0 }, - {"nice-and-ionice", 'Q', POPT_ARG_NONE, 0, 'Q', 0, 0 }, + {0, 'Q', POPT_ARG_NONE, 0, 'Q', 0, 0 }, {"server", 0, POPT_ARG_NONE, 0, OPT_SERVER, 0, 0 }, {"sender", 0, POPT_ARG_NONE, 0, OPT_SENDER, 0, 0 }, /* All the following options switch us into daemon-mode option-parsing. */ @@ -1630,55 +1616,38 @@ int parse_arguments(int *argc_p, const char ***argv_p) case 'Q': /* - * Turn nice on with default prio when it was turned off, but do not overwrite any previously set nice value. + * Turn nice on with default prio and turn ionice on (idle). */ - nice_local = nice_local==0 ? default_nice_prio : nice_local; - nice_remote = nice_remote==0 ? default_nice_prio : nice_remote; + nice_local = default_nice_prio; + nice_remote = default_nice_prio; ionice_local = 1; ionice_remote = 1; break; case OPT_NICE: /* - * Turn nice on with default prio when it was turned off, but do not overwrite any previously set nice value. + * Parse parameters for nice and set the following fields: + - nice_local + - ionice_local + - nice_remote + - ionice_remote */ - nice_local = nice_local==0 ? default_nice_prio : nice_local; - nice_remote = nice_remote==0 ? default_nice_prio : nice_remote; - break; - - case OPT_NICE_VALUE: - /* - * POPT writes to nice_local. We have to copy the value from nice-local to nice-remote here. - */ - nice_remote = nice_local; - break; - - case OPT_NICE_LOCAL: - /* - * Turn nice on with default prio when it was turned off, but do not overwrite any previously set nice value. - */ - nice_local = nice_local==0 ? default_nice_prio : nice_local; + arg = poptGetOptArg(pc); + if (!parse_nice(arg, &nice_local, &ionice_local, &nice_remote, &ionice_remote)) { + snprintf(err_buf, sizeof err_buf, + "Invalid argument passed to --nice (%s)\n", + arg); + goto cleanup; + } break; - case OPT_NICE_REMOTE: + case OPT_NO_NICE: /* - * Turn nice on with default prio when it was turned off, but do not overwrite any previously set nice value. + * Turn off nice and ionice */ - nice_remote = nice_remote==0 ? default_nice_prio : nice_remote; - break; - - case OPT_IONICE: - ionice_local = 1; - ionice_remote = 1; - break; - - case OPT_NO_NICE: nice_local = 0; - nice_remote = 0; - break; - - case OPT_NO_IONICE: ionice_local = 0; + nice_remote = 0; ionice_remote = 0; break; @@ -3080,19 +3049,13 @@ void server_options(char **args, int *argc_p) if (mkpath_dest_arg && am_sender) args[ac++] = "--mkpath"; - if (nice_remote) { - if (get_renice_default_prio()==nice_remote) { - args[ac++] = "--nice-local"; - } else { - if (asprintf(&arg, "--nice-local-value=%d", nice_remote) < 0) - goto oom; - args[ac++] = arg; - } + if (nice_remote || ionice_remote) { + char *ionice_str = ionice_remote > 0 ? "/idle":""; + if (asprintf(&arg, "--nice=local:%d%s", nice_remote, ionice_str) < 0) + goto oom; + args[ac++] = arg; } - if (ionice_remote) - args[ac++] = "--ionice-local"; - if (ac > MAX_SERVER_ARGS) { /* Not possible... */ rprintf(FERROR, "argc overflow in server_options().\n"); exit_cleanup(RERR_MALLOC); diff --git a/testsuite/niceness_test.py b/testsuite/niceness_test.py index 085bd1ab9..42928b439 100644 --- a/testsuite/niceness_test.py +++ b/testsuite/niceness_test.py @@ -41,30 +41,43 @@ def fail(msg: str) -> 'None': # It is expected that the local rsync process will exit here with non zero. # We then just check the log output here for any options that may or may not # have been given for the remote rsync process by our local rsync process. -def test_remote_args(testname: str, args: list, expected_remote_args: list) -> None: +def test_remote_args(testname: str, args: list, expected_nice_value: str, expected_ionice_value: str, expected_remote_args: list) -> None: print('### TEST BEGIN ### ' + testname + '\n') probe = run_rsync(*['-nvv', '--rsh=echo', *args], check=False, capture_output=True) if probe.returncode == 0: fail('The command should not succeed here.') actual_remote_args = 'not found' + nice_value="" + ionice_value="" for remote_line in probe.stdout.splitlines(): if 'opening connection using' in remote_line: actual_remote_args = remote_line.strip() + elif 'client to new priority' in remote_line: + if 'reniced' in remote_line: + nice_value = remote_line.split("to new priority", 1)[1].strip() + print("Local nice value: "+nice_value); + else: + ionice_value = remote_line.split("to new priority", 1)[1].strip() + print("Local ionice value: "+ionice_value); else: print('ignoring line: ' + remote_line) if 'not found' == actual_remote_args: fail('No remote shell command has been started or it was not logged.') + if not nice_value == expected_nice_value: + fail('Local nice value does not match: expected: '+expected_nice_value+", but was: "+nice_value) + if not ionice_value == expected_ionice_value: + fail('Local ionice value does not match: expected: '+expected_ionice_value+", but was: "+ionice_value) for string in expected_remote_args: if string[0] == '!': substring = string[1:] - if (substring+' ') in actual_remote_args: + if (substring+' ') in actual_remote_args or ('\"'+substring+'\"') in actual_remote_args: fail('Remote command line contains unexpected ' + substring + ': ' + actual_remote_args) else: - if not (string+' ') in actual_remote_args: + if not ((string+' ') in actual_remote_args or ('\"'+string+'\"') in actual_remote_args): fail('Remote command line did not contain expected ' + string + ': ' + actual_remote_args) print('### TEST END ### ' + testname + '\n') -test_remote_args('Check with no flags', [f'localhost:{FROMDIR}', TODIR], ["--server", "--sender", "!--blahblah", +test_remote_args('Check with no flags', [f'localhost:{FROMDIR}', TODIR], "", "", ["--server", "--sender", "!--blahblah", "!--nice", "!--nice-local", "!--nice-local-value=4", @@ -76,21 +89,21 @@ def test_remote_args(testname: str, args: list, expected_remote_args: list) -> N "!-Q" ]) -test_remote_args('Check with -Q', ['-Q', f'localhost:{FROMDIR}', TODIR], ["--server", "--sender", "!--blahblah", +test_remote_args('Check with -Q', ['-Q', f'localhost:{FROMDIR}', TODIR], "19", "idle", ["--server", "--sender", "!--blahblah", "!--nice", - "--nice-local", + "--nice=local:19/idle", "!--nice-local-value=4", "!--nice-remote", "!--nice-remote-value=6", "!--ionice", - "--ionice-local", + "!--ionice-local", "!--ionice-remote", "!-Q" ]) -test_remote_args('Check with --nice', ['--nice', f'localhost:{FROMDIR}', TODIR], ["--server", "--sender", "!--blahblah", +test_remote_args('Check with --nice=all', ['--nice=all', f'localhost:{FROMDIR}', TODIR], "19", "idle", ["--server", "--sender", "!--blahblah", "!--nice", - "--nice-local", + "--nice=local:19/idle", "!--nice-local-value=4", "!--nice-remote", "!--nice-remote-value=6", @@ -100,9 +113,9 @@ def test_remote_args(testname: str, args: list, expected_remote_args: list) -> N "!-Q" ]) -test_remote_args('Check with --nice-local', ['--nice-local', f'localhost:{FROMDIR}', TODIR], ["--server", "--sender", "!--blahblah", +test_remote_args('Check with --nice=local', ['--nice=local', f'localhost:{FROMDIR}', TODIR], "19", "idle", ["--server", "--sender", "!--blahblah", "!--nice", - "!--nice-local", + "!--nice=local:19/idle", "!--nice-local-value=4", "!--nice-remote", "!--nice-remote-value=6", @@ -112,9 +125,9 @@ def test_remote_args(testname: str, args: list, expected_remote_args: list) -> N "!-Q" ]) -test_remote_args('Check with --nice-remote', ['--nice-remote', f'localhost:{FROMDIR}', TODIR], ["--server", "--sender", "!--blahblah", +test_remote_args('Check with --nice=remote', ['--nice=remote', f'localhost:{FROMDIR}', TODIR], "", "", ["--server", "--sender", "!--blahblah", "!--nice", - "--nice-local", + "--nice=local:19/idle", "!--nice-local-value=4", "!--nice-remote", "!--nice-remote-value=6", @@ -124,21 +137,21 @@ def test_remote_args(testname: str, args: list, expected_remote_args: list) -> N "!-Q" ]) -test_remote_args('Check with --ionice', ['--ionice', f'localhost:{FROMDIR}', TODIR], ["--server", "--sender", "!--blahblah", +test_remote_args('Check with --nice=idle', ['--nice=idle', f'localhost:{FROMDIR}', TODIR], "", "idle", ["--server", "--sender", "!--blahblah", "!--nice", - "!--nice-local", + "--nice=local:0/idle", "!--nice-local-value=4", "!--nice-remote", "!--nice-remote-value=6", "!--ionice", - "--ionice-local", + "!--ionice-local", "!--ionice-remote", "!-Q" ]) -test_remote_args('Check with --ionice-local', ['--ionice-local', f'localhost:{FROMDIR}', TODIR], ["--server", "--sender", "!--blahblah", +test_remote_args('Check with --nice=local:idle', ['--nice=local:idle', f'localhost:{FROMDIR}', TODIR], "", "idle", ["--server", "--sender", "!--blahblah", "!--nice", - "!--nice-local", + "!--nice=local:19/idle", "!--nice-local-value=4", "!--nice-remote", "!--nice-remote-value=6", @@ -148,21 +161,21 @@ def test_remote_args(testname: str, args: list, expected_remote_args: list) -> N "!-Q" ]) -test_remote_args('Check with --ionice-remote', ['--ionice-remote', f'localhost:{FROMDIR}', TODIR], ["--server", "--sender", "!--blahblah", +test_remote_args('Check with --nice=remote:idle', ['--nice=remote:idle', f'localhost:{FROMDIR}', TODIR], "", "", ["--server", "--sender", "!--blahblah", "!--nice", - "!--nice-local", + "--nice=local:0/idle", "!--nice-local-value=4", "!--nice-remote", "!--nice-remote-value=6", "!--ionice", - "--ionice-local", + "!--ionice-local", "!--ionice-remote", "!-Q" ]) -test_remote_args('Check with --nice-local-value=4', ['--nice-local-value=4', f'localhost:{FROMDIR}', TODIR], ["--server", "--sender", "!--blahblah", +test_remote_args('Check with --nice=local:4', ['--nice=local:4', f'localhost:{FROMDIR}', TODIR], "4", "", ["--server", "--sender", "!--blahblah", "!--nice", - "!--nice-local", + "!--nice=local:4", "!--nice-local-value=4", "!--nice-local-value=6", "!--nice-remote", @@ -174,11 +187,11 @@ def test_remote_args(testname: str, args: list, expected_remote_args: list) -> N "!-Q" ]) -test_remote_args('Check with --nice-remote-value=6', ['--nice-remote-value=6', f'localhost:{FROMDIR}', TODIR], ["--server", "--sender", "!--blahblah", +test_remote_args('Check with --nice=remote:6', ['--nice=remote:6', f'localhost:{FROMDIR}', TODIR], "", "", ["--server", "--sender", "!--blahblah", "!--nice", - "!--nice-local", + "--nice=local:6", "!--nice-local-value=4", - "--nice-local-value=6", + "!--nice-local-value=6", "!--nice-remote", "!--nice-remote-value=4", "!--nice-remote-value=6", @@ -189,11 +202,11 @@ def test_remote_args(testname: str, args: list, expected_remote_args: list) -> N ]) -test_remote_args('Check with --nice-value=6', ['--nice-value=6', f'localhost:{FROMDIR}', TODIR], ["--server", "--sender", "!--blahblah", +test_remote_args('Check with --nice=6', ['--nice=6', f'localhost:{FROMDIR}', TODIR], "6", "", ["--server", "--sender", "!--blahblah", "!--nice", - "!--nice-local", + "--nice=local:6", "!--nice-local-value=4", - "--nice-local-value=6", + "!--nice-local-value=6", "!--nice-remote", "!--nice-remote-value=4", "!--nice-remote-value=6", From 9677caa7ad1d4f9f4dc10167c184e0d2970151b9 Mon Sep 17 00:00:00 2001 From: Michael Mess Date: Wed, 17 Jun 2026 01:25:12 +0200 Subject: [PATCH 22/24] Added enum for IONICE values --- niceness.c | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/niceness.c b/niceness.c index 1ab8bcba2..732c31cbd 100644 --- a/niceness.c +++ b/niceness.c @@ -21,6 +21,36 @@ extern int am_server; +/* + * Enum for ionice values. + * Constants with negative numbers need root permission, others can be set by any user. + * RT_X is realtime priority + * BE_X is best effort with the given level + * IDLE means the process gets served only when no other processes are using disk io. + * NONE means best effort with level calculated by the formula (cpu_nice + 20) / 5 + * and is the default that does not need to be set. + */ +enum Ionice_Values { + RT_0 =-8, + RT_1 =-7, + RT_2 =-6, + RT_3 =-5, + RT_4 =-4, + RT_5 =-3, + RT_6 =-2, + RT_7 =-1, + NONE = 0, + BE_0 = 1, + BE_1 = 2, + BE_2 = 3, + BE_3 = 4, + BE_4 = 5, + BE_5 = 6, + BE_6 = 7, + BE_7 = 8, + IDLE = 9 +}; + /* * Try to parse the location and set the variables isLocal and isRemote. From 450400ee25243cde1e3d69396fc25f662c4f6c18 Mon Sep 17 00:00:00 2001 From: Michael Mess Date: Wed, 17 Jun 2026 01:33:40 +0200 Subject: [PATCH 23/24] Use constants to support all possible IONICE values --- main.c | 2 +- niceness.c | 112 ++++++++++++++++++++++++------ options.c | 10 +-- testsuite/niceness_test.py | 135 ++++++++++++++++++++++++++++++++++++- 4 files changed, 232 insertions(+), 27 deletions(-) diff --git a/main.c b/main.c index 7622b9202..8eed586de 100644 --- a/main.c +++ b/main.c @@ -1851,7 +1851,7 @@ int main(int argc,char *argv[]) } if (ionice_local) { - ionice_me(); + ionice_me(ionice_local); } if (am_server) { diff --git a/niceness.c b/niceness.c index 732c31cbd..bdb32f462 100644 --- a/niceness.c +++ b/niceness.c @@ -51,6 +51,56 @@ enum Ionice_Values { IDLE = 9 }; +/* + * String representation of ionice. All strings have to be 4 characters long. + */ +const char *Ionice_ValueStrings[] = { + "rt_0", + "rt_1", + "rt_2", + "rt_3", + "rt_4", + "rt_5", + "rt_6", + "rt_7", + "none", + "be_0", + "be_1", + "be_2", + "be_3", + "be_4", + "be_5", + "be_6", + "be_7", + "idle", + 0 +}; + +/* + * Get the string representation for the given ionice_value + */ +const char *intToIoniceValueString(int ionice_value) +{ + if (ionice_value<-8) return 0; + if (ionice_value>9) return 0; + return Ionice_ValueStrings[ionice_value+8]; +} + +/* + * Parse string into ionice_value. Returns 1 on success, 0 otherwise. + */ +int ioniceStringToInt(char * string, int *ionice_value) +{ + for (int i=0; Ionice_ValueStrings[i]; i++) + { + if (strncasecmp(string, Ionice_ValueStrings[i], 4) == 0) // All values are 4 chars long + { + *ionice_value=i-8; + return 1; + } + } + return 0; +} /* * Try to parse the location and set the variables isLocal and isRemote. @@ -58,19 +108,19 @@ enum Ionice_Values { */ char *parse_location(char *str, int *isLocal, int *isRemote) { - if (strncmp(str, "local", 5) == 0) + if (strncasecmp(str, "local", 5) == 0) { *isLocal=1; *isRemote=0; return str+5; } - if (strncmp(str, "remote", 6) == 0) + if (strncasecmp(str, "remote", 6) == 0) { *isLocal=0; *isRemote=1; return str+6; } - if (strncmp(str, "all", 3) == 0) + if (strncasecmp(str, "all", 3) == 0) { *isLocal=1; *isRemote=1; @@ -93,14 +143,7 @@ char *parse_nice_value(char *str, int *nice_value) char *parse_ionice_value(char *str, int *ionice_value) { - if (strncmp(str, "none", 3) == 0) - { - *ionice_value=0; - return str+4; - } - if (strncmp(str, "idle", 3) == 0) - { - *ionice_value=1; + if (ioniceStringToInt(str, ionice_value)) { return str+4; } *ionice_value=0; @@ -110,7 +153,8 @@ char *parse_ionice_value(char *str, int *ionice_value) char *parse_nice_and_ionice_values(char *str, int *nice_value, int *ionice_value) { char *pointer = parse_ionice_value(str, ionice_value); - if (*ionice_value) { + if (pointer != str) // ionice value has been given standalone, no nice + { *nice_value=0; return pointer; } @@ -155,7 +199,7 @@ char *parse_setting(char *config_str, int *nice_local, int *ionice_local, int *n { /* End of string or comma */ /* Location without specific setting, use default */ int nice_default=get_renice_default_prio(); - int ionice_default=1; + int ionice_default=get_ionice_default_prio(); if (isLocal) { *nice_local=nice_default; *ionice_local=ionice_default; @@ -202,6 +246,11 @@ int get_renice_default_prio() return 19; // lowest CPU Priority } +int get_ionice_default_prio() +{ + return IDLE; // lowest IO Priority +} + void renice_me(int prio) { #ifdef SUPPORT_RENICE @@ -210,8 +259,8 @@ void renice_me(int prio) int result = setpriority(which, who, prio); if ( result < 0 ) { // Failed to set priority, inform user, but can be ignored (it's just not so nice). - rprintf(FWARNING, "renice to %d rejected by OS (%s version %s): %s\n", - prio, RSYNC_NAME, rsync_version(), strerror(errno)); + rprintf(FWARNING, "renice %s to new priority %d failed (%s version %s): %s\n", + am_server ? "server" : "client", prio, RSYNC_NAME, rsync_version(), strerror(errno)); } else { if (DEBUG_GTE(CMD, 1)) rprintf(FINFO, "successfully reniced %s to new priority %d\n", am_server ? "server" : "client", prio); @@ -222,22 +271,43 @@ void renice_me(int prio) #endif } -void ionice_me() +void ionice_me(int ionice_value) { #ifdef SUPPORT_IONICE int which = IOPRIO_WHO_PROCESS; // who specifies a Process ID int who = 0; // 0 means current process - int class = IOPRIO_CLASS_IDLE; - int data = 0; // Ignored when using the IOPRIO_CLASS_IDLE class + char *ionice_string = intToIoniceValueString(ionice_value); + int class; + int data; // Ignored when using the IOPRIO_CLASS_IDLE or IOPRIO_CLASS_NONE class + switch (ionice_string[0]) + { + case 'r': // Realtime + class = IOPRIO_CLASS_RT; + data = ionice_string[3]-'0'; // rt_X: X -> data + break; + case 'b': // Best effort + class = IOPRIO_CLASS_BE; + data = ionice_string[3]-'0'; // be_X: X -> data + break; + case 'i': // Idle + class = IOPRIO_CLASS_IDLE; + data = 0; + break; + case 'n': // None + default: + class = IOPRIO_CLASS_NONE; + data = 0; + break; + } int ioprio = IOPRIO_PRIO_VALUE(class, data); int result = syscall(SYS_ioprio_set, which, who, ioprio); if ( result < 0 ) { // Failed to set priority, inform user, but can be ignored (it's just not so ionice). - rprintf(FWARNING, "ionice rejected by OS (%s version %s)\n", - RSYNC_NAME, rsync_version()); + rprintf(FWARNING, "ionice %s to new priority %s failed (%s version %s): %s\n", + am_server ? "server" : "client", ionice_string, RSYNC_NAME, rsync_version(), strerror(errno)); } else { if (DEBUG_GTE(CMD, 1)) - rprintf(FINFO, "successfully ioniced %s to new priority idle\n", am_server ? "server" : "client"); + rprintf(FINFO, "successfully ioniced %s to new priority %s\n", am_server ? "server" : "client", ionice_string); } #else rprintf(FWARNING, "ionice not supported for %s (%s: %s version %s)\n", diff --git a/options.c b/options.c index 45f7210f9..9d38fd5b8 100644 --- a/options.c +++ b/options.c @@ -1382,6 +1382,7 @@ int parse_arguments(int *argc_p, const char ***argv_p) int opt, want_dest_type; int orig_protect_args = protect_args; int default_nice_prio = get_renice_default_prio(); // default nice priority to be used, if no explicit priority is given. + int default_ionice_prio = get_ionice_default_prio(); // default ionice priority to be used, if no explicit priority is given. if (argc == 0) { strlcpy(err_buf, "argc is zero!\n", sizeof err_buf); @@ -1620,8 +1621,8 @@ int parse_arguments(int *argc_p, const char ***argv_p) */ nice_local = default_nice_prio; nice_remote = default_nice_prio; - ionice_local = 1; - ionice_remote = 1; + ionice_local = default_ionice_prio; + ionice_remote = default_ionice_prio; break; case OPT_NICE: @@ -3050,8 +3051,9 @@ void server_options(char **args, int *argc_p) args[ac++] = "--mkpath"; if (nice_remote || ionice_remote) { - char *ionice_str = ionice_remote > 0 ? "/idle":""; - if (asprintf(&arg, "--nice=local:%d%s", nice_remote, ionice_str) < 0) + char *ionice_str = ionice_remote ? intToIoniceValueString(ionice_remote):""; + char *slash_str = ionice_remote ? "/":""; + if (asprintf(&arg, "--nice=local:%d%s%s", nice_remote, slash_str, ionice_str) < 0) goto oom; args[ac++] = arg; } diff --git a/testsuite/niceness_test.py b/testsuite/niceness_test.py index 42928b439..b49ebdc4c 100644 --- a/testsuite/niceness_test.py +++ b/testsuite/niceness_test.py @@ -53,7 +53,8 @@ def test_remote_args(testname: str, args: list, expected_nice_value: str, expect if 'opening connection using' in remote_line: actual_remote_args = remote_line.strip() elif 'client to new priority' in remote_line: - if 'reniced' in remote_line: + print("stdout: Found client info: "+remote_line); + if 'renice' in remote_line: nice_value = remote_line.split("to new priority", 1)[1].strip() print("Local nice value: "+nice_value); else: @@ -61,6 +62,15 @@ def test_remote_args(testname: str, args: list, expected_nice_value: str, expect print("Local ionice value: "+ionice_value); else: print('ignoring line: ' + remote_line) + for remote_line in probe.stderr.splitlines(): + if 'client to new priority' in remote_line: + print("stderr: Found client info: "+remote_line); + if 'renice' in remote_line: + nice_value = remote_line.split("to new priority", 1)[1].split("failed", 1)[0].strip() + print("Local nice value: "+nice_value); + else: + ionice_value = remote_line.split("to new priority", 1)[1].split("failed", 1)[0].strip() + print("Local ionice value: "+ionice_value); if 'not found' == actual_remote_args: fail('No remote shell command has been started or it was not logged.') if not nice_value == expected_nice_value: @@ -149,6 +159,90 @@ def test_remote_args(testname: str, args: list, expected_nice_value: str, expect "!-Q" ]) +test_remote_args('Check with --nice=be_2', ['--nice=be_2', f'localhost:{FROMDIR}', TODIR], "", "be_2", ["--server", "--sender", "!--blahblah", + "!--nice", + "--nice=local:0/be_2", + "!--nice-local-value=4", + "!--nice-remote", + "!--nice-remote-value=6", + "!--ionice", + "!--ionice-local", + "!--ionice-remote", + "!-Q" + ]) + +test_remote_args('Check with --nice=be_5', ['--nice=be_5', f'localhost:{FROMDIR}', TODIR], "", "be_5", ["--server", "--sender", "!--blahblah", + "!--nice", + "--nice=local:0/be_5", + "!--nice-local-value=4", + "!--nice-remote", + "!--nice-remote-value=6", + "!--ionice", + "!--ionice-local", + "!--ionice-remote", + "!-Q" + ]) + +test_remote_args('Check with --nice=rt_3', ['--nice=rt_3', f'localhost:{FROMDIR}', TODIR], "", "rt_3", ["--server", "--sender", "!--blahblah", + "!--nice", + "--nice=local:0/rt_3", + "!--nice-local-value=4", + "!--nice-remote", + "!--nice-remote-value=6", + "!--ionice", + "!--ionice-local", + "!--ionice-remote", + "!-Q" + ]) + +test_remote_args('Check with --nice=rt_7', ['--nice=rt_7', f'localhost:{FROMDIR}', TODIR], "", "rt_7", ["--server", "--sender", "!--blahblah", + "!--nice", + "--nice=local:0/rt_7", + "!--nice-local-value=4", + "!--nice-remote", + "!--nice-remote-value=6", + "!--ionice", + "!--ionice-local", + "!--ionice-remote", + "!-Q" + ]) + +test_remote_args('Check with --nice=RT_0', ['--nice=RT_0', f'localhost:{FROMDIR}', TODIR], "", "rt_0", ["--server", "--sender", "!--blahblah", + "!--nice", + "--nice=local:0/rt_0", + "!--nice-local-value=4", + "!--nice-remote", + "!--nice-remote-value=6", + "!--ionice", + "!--ionice-local", + "!--ionice-remote", + "!-Q" + ]) + +test_remote_args('Check with --nice=none', ['--nice=none', f'localhost:{FROMDIR}', TODIR], "", "", ["--server", "--sender", "!--blahblah", + "!--nice", + "!--nice=local:0/none", + "!--nice-local-value=4", + "!--nice-remote", + "!--nice-remote-value=6", + "!--ionice", + "!--ionice-local", + "!--ionice-remote", + "!-Q" + ]) + +test_remote_args('Check with --nice=NONE', ['--nice=NONE', f'localhost:{FROMDIR}', TODIR], "", "", ["--server", "--sender", "!--blahblah", + "!--nice", + "!--nice=local:0/none", + "!--nice-local-value=4", + "!--nice-remote", + "!--nice-remote-value=6", + "!--ionice", + "!--ionice-local", + "!--ionice-remote", + "!-Q" + ]) + test_remote_args('Check with --nice=local:idle', ['--nice=local:idle', f'localhost:{FROMDIR}', TODIR], "", "idle", ["--server", "--sender", "!--blahblah", "!--nice", "!--nice=local:19/idle", @@ -217,4 +311,43 @@ def test_remote_args(testname: str, args: list, expected_nice_value: str, expect ]) +test_remote_args('Check with --nice=-2/IDLE', ['--nice=-2/IDLE', f'localhost:{FROMDIR}', TODIR], "-2", "idle", ["--server", "--sender", "!--blahblah", + "!--nice", + "--nice=local:-2/idle", + "!--nice-local-value=4", + "!--nice-local-value=6", + "!--nice-remote", + "!--nice-remote-value=4", + "!--nice-remote-value=6", + "!--ionice", + "!--ionice-local", + "!--ionice-remote", + "!-Q" + ]) + +test_remote_args('Check with --nice=ALL:-2/IDLE', ['--nice=ALL:-2/IDLE', f'localhost:{FROMDIR}', TODIR], "-2", "idle", ["--server", "--sender", "!--blahblah", + "!--nice", + "--nice=local:-2/idle", + "!--nice-local-value=4", + "!--nice-local-value=6", + "!--nice-remote", + "!--nice-remote-value=4", + "!--nice-remote-value=6", + "!--ionice", + "!--ionice-local", + "!--ionice-remote", + "!-Q" + ]) + +test_remote_args('Check with --nice=local:10/BE_0,remote:19/IDLE', ['--nice=local:10/BE_0,remote:19/IDLE', f'localhost:{FROMDIR}', TODIR], "10", "be_0", ["--server", "--sender", "!--blahblah", + "!--nice", + "--nice=local:19/idle", + "!--nice-local-value=4", + "!--nice-remote", + "!--nice-remote-value=6", + "!--ionice", + "!--ionice-local", + "!--ionice-remote", + "!-Q" + ]) From 1ef416f3096d772d47d78f636d5402d0a08d3c81 Mon Sep 17 00:00:00 2001 From: Michael Mess Date: Thu, 18 Jun 2026 01:58:42 +0200 Subject: [PATCH 24/24] Updated documentation --- rsync.1.md | 91 ++++++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 78 insertions(+), 13 deletions(-) diff --git a/rsync.1.md b/rsync.1.md index 4613a66e9..e4929dda4 100644 --- a/rsync.1.md +++ b/rsync.1.md @@ -565,16 +565,15 @@ has its own detailed description later in this manpage. --checksum-seed=NUM set block/file checksum seed (advanced) --ipv4, -4 prefer IPv4 --ipv6, -6 prefer IPv6 ---nice-local renice local process to low priority ---nice-remote renice remote process to low priority ---ionice-local ionice local process to low priority ---ionice-remote ionice remote process to low priority ---nice, --ionice set local and remote process to low priority ---nice-local-value=10 renice local process to given priority ---nice-remote-value=10 renice remote process to given priority ---nice-value=10 renice local+remote process to given priority ---no-nice, ... prepending "no-" turns the given option off ---nice-and-ionice, -Q same as --nice --ionice +--nice=(LOCATION|[LOCATION:]SETTING)[,...] Set prio of the rsync process + LOCATION: local|remote|all + SETTING: NICESETTING|IONICESETTING|NICESETTING/IONICESETTING + NICESETTING: -20...19 + IONICESETTING: RT_0|RT_1|RT_2|RT_3|RT_4|RT_5|RT_6|RT_7|NONE| + BE_0|BE_1|BE_2|BE_3|BE_4|BE_5|BE_6|BE_7|IDLE +--no-nice prepending "no-" turns the option off +--Q same as --nice=all:19/idle + LOCATION and IONICESETTING are not case sensitive --version, -V print the version + other info and exit --help, -h (*) show this help (* -h is help only on its own) ``` @@ -595,9 +594,7 @@ accepted: --log-file=FILE override the "log file" setting --log-file-format=FMT override the "log format" setting --sockopts=OPTIONS specify custom TCP options ---nice-local renice local daemon process to low priority ---ionice-local ionice local daemon process to low priority - They are given at client site as (io)nice-remote +--nice=... set priority of the local daemon process --verbose, -v increase verbosity --ipv4, -4 prefer IPv4 --ipv6, -6 prefer IPv6 @@ -3838,6 +3835,74 @@ expand it. user wants a more random checksum seed. Setting NUM to 0 causes rsync to use the default of **time**() for checksum seed. +0. `--nice=(LOCATION|[LOCATION:]SETTING)[,...]` + + LOCATION: `LOCAL`|`REMOTE`|`ALL` + + - `LOCAL` is the local rsync process that has been started by the user/script/etc. + + - `REMOTE` it the remote rsync process that has been started via SSH on the remote system + + - `ALL` means both, local and remote. + The constants are not case sensitive, thus e.g. `REMOTE` and `remote` are the same value. + If omitted, the default is: all + + SETTING: NICESETTING|IONICESETTING|NICESETTING/IONICESETTING + - If the whole setting is omitted, the default is `19/IDLE` + + NICESETTING: `-20`...`19` + - Higher number means lower priority, negative numbers require root permissions + - If not specified in the setting, the default is `0`. + - No system call is made to set the priority `0`. + - If the setting fails, e.g. due to the lack of permission to set negative nice + priority, a warning is issued and rsync will continue doing its work. + + IONICESETTING: RT_0|RT_1|RT_2|RT_3|RT_4|RT_5|RT_6|RT_7|NONE|BE_0|BE_1|BE_2|BE_3|BE_4|BE_5|BE_6|BE_7|IDLE + - `RT_X` is realtime priority, need root permission + - `BE_X` is best effort with the given level + - `IDLE` means the process gets served only when no other processes are using disk io. + - `NONE` means best effort with level calculated by the formula (cpu_nice + 20) / 5 + and is the default that does not need to be set. + - The constants are not case sensitive, thus e.g. `IDLE` and `idle` are the same value. + - If not specified in the setting, the default is `NONE`. + - No system call is made to set the priority `NONE`. + - If the setting fails, e.g. due to the lack of permission to set realtime + priority, a warning is issued and rsync will continue doing its work. + + Examples: + + Specifying a setting for both, local and remote: + --nice=19/idle = --nice=all:19/idle + --nice=19 = --nice=all:19 (no ionice -> NONE => only nice will be set) + --nice=idle = --nice=all:idle (no nice -> 0 => only ionice will be set) + --nice=0 = --nice=all:0 (nice value 0 and no ionice -> 0/NONE => none of them is set) + + Specifying a setting for the given location: + --nice=local:10 + --nice=remote:idle + --nice=local:19,remote:5 + --nice=local:19/idle,remote:5 + --nice=local:19/be_0,remote:idle + + No specific setting means default 19/idle: + --nice=local = --nice=local:19/idle + --nice=remote = --nice=remote:19/idle + --nice=all = --nice=19/idle + -Q = --nice=19/idle + --nice=local,remote + + Any specific setting set before may be overwritten by the following setting, e. g.: + --nice=local:5 --nice=all + --nice=local:6/idle,all + --nice=local:5 --nice=remote:6 -Q + would result in setting the default 19/idle for both, local and remote. + + nice and ionice are always set as a pair, even when only one of them is specified. + --nice=remote:5 --nice=remote:idle + therefore would result in an unset nice value (`5` will be overwritten with default `0`) + and effectively only ionice value `idle` beeing applied. + + ## DAEMON OPTIONS The options allowed when starting an rsync daemon are as follows: