Hi folks, This is a patch for rsync 2.6.9 - I'm not sure if this is the proper vehicle for submitting them. It's been working great for us for months, and I hope it's seen as offering useful utility in general. Steve (who is not a subscriber to this list) --- Stephen J Friedl | Security Consultant | UNIX Wizard | +1 714 544-6561 www.unixwiz.net | Tustin, Calif. USA | Microsoft MVP | steve@unixwiz.net ---------------------------------------------------------------------------- Date: 2007/10/09 By: Stephen J. Friedl steve@unixwiz.net Purpose: This patch introduces a "nice = <N>" variable in the rsyncd.conf file so that the daemon will raise its nice value (lower CPU priority) by the given integer delta for a given rsync module. Background: When doing full-system backups with rsync across the data center, we use several techniques to avoid swamping the backed up machine when the rsync kicks in. We set a --bwlimit to cut down on the I/O, but we also wanted to lower the CPU priority as well. We've had real problems with the rsync backups adversely affecting production workloads, and though it's probably possible to build some kind of wrapper that nices the process priority up before execing the real daemon, that would apply the nice to all modules served by that daemon. That's not what we wanted. Using this: In an rsyncd.conf: [fullbackup] path = / ... nice = 10 This *adds* 10 to the current nice, which *lowers* the CPU priority. Though the nice(2) system call can take negative numbers to lower the nice/raise the priority, that's disallowed here. We only allow lowering of the CPU priority, never raising it. The patch: This was a pretty easy patch to make: I found where the variables were defined and simply added a new integral one in the pattern of the timeout variable. Not being terribly familiar with the internals of rsync, I was not absolutely sure that rsync forked a child for each client request, and since nice(2) is additive, doing this in the wrong place means that each client gets lower and lower priority. So I put the call right after the chroot(2) call - I was sure that *this* was not in a loop, and I wouldn't have to worry about it. The nice(2) system call has different return semantics across different operating systems (Linux returns 0 on success, BSD returns the new nice value), and there's no real point in checking for success or failure: nice() with positive values is supposed to work for everybody anyway. It might be possible to support *raising* CPU priority (via a negative nice), but this seems like an area rife with trouble for no real benefit. We'd have to coordinate lowering the nice before giving up root permissions, check for errors, probably get it wrong sometimes, and it seems so much easier to allow only the behavior which the patch intended to add anyway: lower the priority. The only thing I'm not completely sure about is how to best do logging. I have two calls to rprint(FLOG, ...): one is for an error condition (providing a negative nice), and the other is just an info notice. rprintf(FLOG, "WARNING: nice(%d) not allowed (must be positive) on module %s\n", niceval, name); rprintf(FLOG, "rsync set nice(%d) on module %s\n", niceval, name); The error should not be fatal, and neither of these should be routed to the client. This very patch has been in use on a dozen machines (Linux and FreeBSD) for more than two months, and we've verified that it really does increase the nice as requested. --- clientserver.c.orig 2007-08-19 07:36:59.000000000 +0000 +++ clientserver.c 2007-08-20 00:47:10.000000000 +0000 @@ -283,6 +283,7 @@ int ret, pre_exec_fd = -1; pid_t pre_exec_pid = 0; char *request = NULL; + int niceval; if (!allow_access(addr, host, lp_hosts_allow(i), lp_hosts_deny(i))) { rprintf(FLOG, "rsync denied on module %s from %s (%s)\n", @@ -559,6 +560,29 @@ am_root = (MY_UID() == 0); } + /* If the user provided a nice increment to lower the priority, + * do it here. Negative nice not allowed, and we rely on the OS + * to quietly limit the maximum nice (no point in us vetting it). + * + * The return value from nice(2) varies across operating systems + * (linux has 0=success, BSD return new nice value), so there is + * no point in checking for errors. + */ + if ( (niceval = lp_nice(i)) > 0 ) + { + rprintf(FLOG, "rsync set nice(%d) on module %s\n", niceval, name); + + nice(niceval); + } + else if ( niceval < 0 ) + { + rprintf(FLOG, "WARNING: nice(%d) not allowed (must be positive) on module %s\n", + niceval, name); + + /* but we keep going */ + } + + if (lp_temp_dir(i) && *lp_temp_dir(i)) { tmpdir = lp_temp_dir(i); if (strlen(tmpdir) >= MAXPATHLEN - 10) { --- loadparm.c.orig 2007-08-19 07:28:25.000000000 +0000 +++ loadparm.c 2007-08-19 07:39:00.000000000 +0000 @@ -149,6 +149,7 @@ int max_verbosity; int syslog_facility; int timeout; + int niceval; BOOL ignore_errors; BOOL ignore_nonreadable; @@ -196,6 +197,7 @@ /* max_verbosity; */ 1, /* syslog_facility; */ LOG_DAEMON, /* timeout; */ 0, + /* nice; */ 0, /* ignore_errors; */ False, /* ignore_nonreadable; */ False, @@ -327,6 +329,7 @@ {"syslog facility", P_ENUM, P_LOCAL, &sDefault.syslog_facility,enum_facilities,0}, {"temp dir", P_PATH, P_LOCAL, &sDefault.temp_dir, NULL,0}, {"timeout", P_INTEGER,P_LOCAL, &sDefault.timeout, NULL,0}, + {"nice", P_INTEGER,P_LOCAL, &sDefault.niceval, NULL,0}, {"transfer logging", P_BOOL, P_LOCAL, &sDefault.transfer_logging, NULL,0}, {"uid", P_STRING, P_LOCAL, &sDefault.uid, NULL,0}, {"use chroot", P_BOOL, P_LOCAL, &sDefault.use_chroot, NULL,0}, @@ -411,6 +414,7 @@ FN_LOCAL_INTEGER(lp_max_connections, max_connections) FN_LOCAL_INTEGER(lp_max_verbosity, max_verbosity) FN_LOCAL_INTEGER(lp_timeout, timeout) +FN_LOCAL_INTEGER(lp_nice, niceval) FN_LOCAL_BOOL(lp_ignore_errors, ignore_errors) FN_LOCAL_BOOL(lp_ignore_nonreadable, ignore_nonreadable) --- proto.h.orig 2007-08-19 07:41:19.000000000 +0000 +++ proto.h 2007-08-19 07:42:12.000000000 +0000 @@ -173,6 +173,7 @@ int lp_max_connections(int ); int lp_max_verbosity(int ); int lp_timeout(int ); +int lp_nice(int ); BOOL lp_ignore_errors(int ); BOOL lp_ignore_nonreadable(int ); BOOL lp_list(int ); --- rsyncd.conf.5.orig 2007-08-19 08:02:16.000000000 +0000 +++ rsyncd.conf.5 2007-08-20 00:31:23.000000000 +0000 @@ -573,6 +573,13 @@ of the patterns will not be compressed during transfer\&. .IP The default setting is \f(CW*\&.gz *\&.tgz *\&.zip *\&.z *\&.rpm *\&.deb *\&.iso *\&.bz2 *\&.tbz\fP +.IP +.IP "\fBnice\fP +This provides a positive nice(2) value that lowers the CPU priority of the +rsyncd process when servicing this module. This is useful for backups across +the network that are not time critical, and the lowered priority can help +avoid swamping a busy machine. Negative nice (to increase priority) are +specifically disallowed here. .IP .IP "\fBpre-xfer exec\fP, \fBpost-xfer exec\fP" You may specify a command to be run @@ -686,6 +693,12 @@ auth users = tridge, susan secrets file = /etc/rsyncd\&.secrets +[fullbackup] + path = / + read only = yes + nice = 5 + comment = Full system backup + .fi .PP