Here's a new version of my rsync-server-over-remote-shell patch:

- diffs (-u) against 2.5.0 

- syntax for running rsync server over remote shell is

rsync [options] -e ssh source [user@]host::module[/dest]
rsync [options] -e ssh [user@]host::module[/source] dest

  If you need different usernames at the rsyncd and ssh levels, you
  can do

rsync [options] -e "ssh -l ssh-user" source rsyncd-user@host::module[/path]

- rsync_module() now uses the SSH_CLIENT environment variable to get
  an IP address.  As Martin mentioned, there is the potential for
  spoofing here, if the client can control what the SSH_CLIENT
  environment variable contains.  (However, if the client can control
  the environment on the server side, it can do pretty much whatever
  it wants anyway....)  If you're using this in a locked-down scheme
  with single-use SSH keys, then there's no way for the client to do
  anything to the environment, and it should be safe.

Cheers --

					JD Paul
					jdpaul@interstel.net

P.S.  Question -- is the enum { } of all the OPT_ variables in
options.c necessary any more?  It looks like it's not.

On Tue, 20 Nov 2001, JD Paul wrote:

> Hi --
> 
> I've made the changes to my code changes and the new diffs (against
> 2.4.6 again) are included below.
> 
> Syntax for running rsync server over a remote shell (e.g. ssh) is now:
> 
> rsync [options] -e ssh source [user@]host::module[/dest]
> rsync [options] -e ssh [user@]host::module[/source] dest
> 
> Cheers --
> 
> 					JD
> 					jdpaul@interstel.net
> 

-------------------------<cut here>-----------------------------------


Index: authenticate.c
===================================================================
RCS file: /juno/repository/usr/local/pkg/rsync/authenticate.c,v
retrieving revision 1.1.1.5
retrieving revision 1.4
diff -u -b -r1.1.1.5 -r1.4
--- authenticate.c	2001/12/03 23:48:45	1.1.1.5
+++ authenticate.c	2001/12/04 00:10:38	1.4
@@ -203,7 +203,7 @@
 
    otherwise return username
 */
-char *auth_server(int fd, int module, char *addr, char *leader)
+char *auth_server(int f_in, int f_out, int module, char *addr, char *leader)
 {
 	char *users = lp_auth_users(module);
 	char challenge[16];
@@ -222,9 +222,9 @@
 	
 	base64_encode(challenge, 16, b64_challenge);
 
-	io_printf(fd,"%s%s\n", leader, b64_challenge);
+	io_printf(f_out,"%s%s\n", leader, b64_challenge);
 
-	if (!read_line(fd, line, sizeof(line)-1)) {
+	if (!read_line(f_in, line, sizeof(line)-1)) {
 		return NULL;
 	}
 
Index: clientserver.c
===================================================================
RCS file: /juno/repository/usr/local/pkg/rsync/clientserver.c,v
retrieving revision 1.1.1.5
retrieving revision 1.11
diff -u -b -r1.1.1.5 -r1.11
--- clientserver.c	2001/12/03 23:48:48	1.1.1.5
+++ clientserver.c	2001/12/04 04:51:10	1.11
@@ -35,34 +35,22 @@
  */
 int start_socket_client(char *host, char *path, int argc, char *argv[])
 {
-	int fd, i;
-	char *sargs[MAX_ARGS];
-	int sargc=0;
-	char line[MAXPATHLEN];
+	int fd;
+	int ret;
 	char *p, *user=NULL;
 	extern int remote_version;
 	extern int am_sender;
 	extern char *shell_cmd;
-	extern int kludge_around_eof;
 	extern char *bind_address;
        
 	if (argc == 0 && !am_sender) {
 		extern int list_only;
 		list_only = 1;
 	}
-
-        /* This is just a friendliness enhancement: if the connection
-         * is to an rsyncd then there is no point specifying the -e option.
-         * Note that this is only set if the -e was explicitly specified,
-         * not if the environment variable just happens to be set.
-         * See http://lists.samba.org/pipermail/rsync/2000-September/002744.html
-         */
-        if (shell_cmd) {
-                rprintf(FERROR, "WARNING: --rsh or -e option ignored when "
-                        "connecting to rsync daemon\n");
-                /* continue */
-        }
         
+	/* this is redundant with code in start_inband_exchange(), but
+	   this short-circuits a problem before we open a socket, and 
+	   the extra check won't hurt */
 	if (*path == '/') {
 		rprintf(FERROR,"ERROR: The remote path must start with a module name not a /\n");
 		return -1;
@@ -75,15 +63,46 @@
 		*p = 0;
 	}
 
-	if (!user) user = getenv("USER");
-	if (!user) user = getenv("LOGNAME");
-
 	fd = open_socket_out_wrapped (host, rsync_port, bind_address,
 				      global_opts.af_hint);
 	if (fd == -1) {
 		exit_cleanup(RERR_SOCKETIO);
 	}
 	
+	ret = start_inband_exchange(user, path, fd, fd, argc, argv);
+
+	return (ret < 0 ) ? ret : client_run(fd, fd, -1, argc, argv);
+}
+
+int start_inband_exchange(char *user, char *path, int f_in, int f_out, int argc, char *argv[])
+{
+	int i;
+	char *sargs[MAX_ARGS];
+	int sargc = 0;
+	char line[MAXPATHLEN];
+	char *p;
+	extern int remote_version;
+	extern int kludge_around_eof;
+	extern int am_sender;
+	extern int daemon_over_rsh;
+
+	if (argc == 0 && !am_sender) {
+		extern int list_only;
+		list_only = 1;
+	}
+
+	if (*path == '/') {
+		rprintf(FERROR,"ERROR: The remote path must start with a module name\n");
+		return -1;
+	}
+
+	if (!user) user = getenv("USER");
+	if (!user) user = getenv("LOGNAME");
+
+	/* set daemon_over_rsh to false since we need to build the 
+	   true set of args passed through the rsh/ssh connection; 
+	   this is a no-op for direct-socket-connection mode */
+	daemon_over_rsh = 0;
 	server_options(sargs,&sargc);
 
 	sargs[sargc++] = ".";
@@ -93,9 +112,9 @@
 
 	sargs[sargc] = NULL;
 
-	io_printf(fd,"@RSYNCD: %d\n", PROTOCOL_VERSION);
+	io_printf(f_out,"@RSYNCD: %d\n", PROTOCOL_VERSION);
 
-	if (!read_line(fd, line, sizeof(line)-1)) {
+	if (!read_line(f_in, line, sizeof(line)-1)) {
 		return -1;
 	}
 
@@ -105,7 +124,7 @@
 
 	p = strchr(path,'/');
 	if (p) *p = 0;
-	io_printf(fd,"%s\n",path);
+	io_printf(f_out,"%s\n",path);
 	if (p) *p = '/';
 
 	/* Old servers may just drop the connection here,
@@ -113,12 +132,12 @@
 	kludge_around_eof = remote_version < 25;
 
 	while (1) {
-		if (!read_line(fd, line, sizeof(line)-1)) {
+		if (!read_line(f_in, line, sizeof(line)-1)) {
 			return -1;
 		}
 
 		if (strncmp(line,"@RSYNCD: AUTHREQD ",18) == 0) {
-			auth_client(fd, user, line+18);
+			auth_client(f_out, user, line+18);
 			continue;
 		}
 
@@ -131,69 +150,97 @@
 	kludge_around_eof = False;
 
 	for (i=0;i<sargc;i++) {
-		io_printf(fd,"%s\n", sargs[i]);
+		io_printf(f_out,"%s\n", sargs[i]);
 	}
-	io_printf(fd,"\n");
+	io_printf(f_out,"\n");
 
 	if (remote_version < 23) {
 		if (remote_version == 22 || (remote_version > 17 && !am_sender))
-			io_start_multiplex_in(fd);
+			io_start_multiplex_in(f_in);
 	}
 
-	return client_run(fd, fd, -1, argc, argv);
-}
+	return 0;
 
+}
 
 
-static int rsync_module(int fd, int i)
+static int rsync_module(int f_in, int f_out, int i)
 {
 	int argc=0;
 	char *argv[MAX_ARGS];
 	char **argp;
 	char line[MAXPATHLEN];
+	char addr_buf[128];
 	uid_t uid = (uid_t)-2;  /* canonically "nobody" */
 	gid_t gid = (gid_t)-2;
 	char *p;
-	char *addr = client_addr(fd);
-	char *host = client_name(fd);
+	char *addr;
+	char *host;
 	char *name = lp_name(i);
 	int use_chroot = lp_use_chroot(i);
 	int start_glob=0;
 	int ret;
 	char *request=NULL;
 	extern int am_sender;
+	extern int am_server;
+	extern int am_daemon;
 	extern int remote_version;
 	extern int am_root;
 
+	if (is_a_socket(f_in)) {
+		addr = client_addr(f_in);
+		host = client_name(f_in);
+	} else {
+		char *ssh_client = getenv("SSH_CLIENT");
+		if (ssh_client) {
+			strlcpy(addr_buf, ssh_client, sizeof(addr_buf));
+			/* truncate SSH_CLIENT to just IP address */
+			p = strchr(addr_buf, ' ');
+			if (p) {
+				*p = '\0';
+			}
+			addr = addr_buf;
+			host = "remote.shell.connection";
+		} else {
+			addr = "0.0.0.0";
+			host = "remote.shell.connection";
+		}
+	}
+
 	if (!allow_access(addr, host, lp_hosts_allow(i), lp_hosts_deny(i))) {
 		rprintf(FERROR,"rsync denied on module %s from %s (%s)\n",
-			name, client_name(fd), client_addr(fd));
-		io_printf(fd,"@ERROR: access denied to %s from %s (%s)\n",
-			  name, client_name(fd), client_addr(fd));
+			name, host, addr);
+		io_printf(f_out,"@ERROR: access denied to %s from %s (%s)\n",
+			  name, host, addr);
 		return -1;
 	}
 
+	if (am_daemon && am_server) {
+		rprintf(FINFO, "rsync allowed access on module %s from %s (%s)\n",
+			name, host, addr);
+	}
+
 	if (!claim_connection(lp_lock_file(i), lp_max_connections(i))) {
 		if (errno) {
 			rprintf(FERROR,"failed to open lock file %s : %s\n",
 				lp_lock_file(i), strerror(errno));
-			io_printf(fd,"@ERROR: failed to open lock file %s : %s\n",
+			io_printf(f_out,"@ERROR: failed to open lock file %s : %s\n",
 				  lp_lock_file(i), strerror(errno));
 		} else {
 			rprintf(FERROR,"max connections (%d) reached\n",
 				lp_max_connections(i));
-			io_printf(fd,"@ERROR: max connections (%d) reached - try again later\n", lp_max_connections(i));
+			io_printf(f_out,"@ERROR: max connections (%d) reached - try again later\n", lp_max_connections(i));
 		}
 		return -1;
 	}
 
 	
-	auth_user = auth_server(fd, i, addr, "@RSYNCD: AUTHREQD ");
+	auth_user = auth_server(f_in, f_out, i, addr, "@RSYNCD: AUTHREQD ");
 
 	if (!auth_user) {
 		rprintf(FERROR,"auth failed on module %s from %s (%s)\n",
-			name, client_name(fd), client_addr(fd));
-		io_printf(fd,"@ERROR: auth failed on module %s\n",name);
+			name, host, addr);
+		io_printf(f_out,"@ERROR: auth failed on module %s\n",name);
 		return -1;		
 	}
 
@@ -206,7 +253,7 @@
 		if (!name_to_uid(p, &uid)) {
 			if (!isdigit(*p)) {
 				rprintf(FERROR,"Invalid uid %s\n", p);
-				io_printf(fd,"@ERROR: invalid uid %s\n", p);
+				io_printf(f_out,"@ERROR: invalid uid\n");
 				return -1;
 			} 
 			uid = atoi(p);
@@ -216,7 +263,7 @@
 		if (!name_to_gid(p, &gid)) {
 			if (!isdigit(*p)) {
 				rprintf(FERROR,"Invalid gid %s\n", p);
-				io_printf(fd,"@ERROR: invalid gid %s\n", p);
+				io_printf(f_out,"@ERROR: invalid gid\n");
 				return -1;
 			} 
 			gid = atoi(p);
@@ -259,20 +306,20 @@
 		 */
 		if (chroot(lp_path(i))) {
 			rsyserr(FERROR, errno, "chroot %s failed", lp_path(i));
-			io_printf(fd,"@ERROR: chroot failed\n");
+			io_printf(f_out,"@ERROR: chroot failed\n");
 			return -1;
 		}
 
 		if (!push_dir("/", 0)) {
                         rsyserr(FERROR, errno, "chdir %s failed\n", lp_path(i));
-			io_printf(fd,"@ERROR: chdir failed\n");
+			io_printf(f_out,"@ERROR: chdir failed\n");
 			return -1;
 		}
 
 	} else {
 		if (!push_dir(lp_path(i), 0)) {
 			rsyserr(FERROR, errno, "chdir %s failed\n", lp_path(i));
-			io_printf(fd,"@ERROR: chdir failed\n");
+			io_printf(f_out,"@ERROR: chdir failed\n");
 			return -1;
 		}
 		sanitize_paths = 1;
@@ -281,25 +328,25 @@
 	if (am_root) {
 		if (setgid(gid)) {
 			rsyserr(FERROR, errno, "setgid %d failed", (int) gid);
-			io_printf(fd,"@ERROR: setgid failed\n");
+			io_printf(f_out,"@ERROR: setgid failed\n");
 			return -1;
 		}
 
 		if (setuid(uid)) {
 			rsyserr(FERROR, errno, "setuid %d failed", (int) uid);
-			io_printf(fd,"@ERROR: setuid failed\n");
+			io_printf(f_out,"@ERROR: setuid failed\n");
 			return -1;
 		}
 
 		am_root = (getuid() == 0);
 	}
 
-	io_printf(fd,"@RSYNCD: OK\n");
+	io_printf(f_out,"@RSYNCD: OK\n");
 
 	argv[argc++] = "rsyncd";
 
 	while (1) {
-		if (!read_line(fd, line, sizeof(line)-1)) {
+		if (!read_line(f_in, line, sizeof(line)-1)) {
 			return -1;
 		}
 
@@ -367,7 +414,7 @@
 
 	if (remote_version < 23) {
 		if (remote_version == 22 || (remote_version > 17 && am_sender))
-			io_start_multiplex_out(fd);
+			io_start_multiplex_out(f_out);
 	}
         
         /* For later protocol versions, we don't start multiplexing
@@ -388,7 +435,7 @@
 		io_timeout = lp_timeout(i);
 	}
 
-	start_server(fd, fd, argc, argp);
+	start_server(f_in, f_out, argc, argp);
 
 	return 0;
 }
@@ -409,26 +456,29 @@
 		io_printf(fd,"@RSYNCD: EXIT\n");
 }
 
-/* this is called when a socket connection is established to a client
+/* this is called when a connection is established to a client
    and we want to start talking. The setup of the system is done from
    here */
-static int start_daemon(int fd)
+int start_daemon(int f_in, int f_out)
 {
 	char line[200];
 	char *motd;
 	int i = -1;
 	extern char *config_file;
 	extern int remote_version;
+	extern int am_server;
 
 	if (!lp_load(config_file, 0)) {
 		exit_cleanup(RERR_SYNTAX);
 	}
 
-	set_socket_options(fd,"SO_KEEPALIVE");
-	set_socket_options(fd,lp_socket_options());
-	set_nonblocking(fd);
+	if ( !am_server ) {
+		set_socket_options(f_in,"SO_KEEPALIVE");
+		set_socket_options(f_in,lp_socket_options());
+		set_nonblocking(f_in);
+	}
 
-	io_printf(fd,"@RSYNCD: %d\n", PROTOCOL_VERSION);
+	io_printf(f_out,"@RSYNCD: %d\n", PROTOCOL_VERSION);
 
 	motd = lp_motd_file();
 	if (motd && *motd) {
@@ -437,50 +487,49 @@
 			int len = fread(line, 1, sizeof(line)-1, f);
 			if (len > 0) {
 				line[len] = 0;
-				io_printf(fd,"%s", line);
+				io_printf(f_out,"%s", line);
 			}
 		}
 		if (f) fclose(f);
-		io_printf(fd,"\n");
+		io_printf(f_out,"\n");
 	}
 
-	if (!read_line(fd, line, sizeof(line)-1)) {
+	if (!read_line(f_in, line, sizeof(line)-1)) {
 		return -1;
 	}
 
 	if (sscanf(line,"@RSYNCD: %d", &remote_version) != 1) {
-		io_printf(fd,"@ERROR: protocol startup error\n");
+		io_printf(f_out,"@ERROR: protocol startup error\n");
 		return -1;
 	}	
 
 	while (i == -1) {
 		line[0] = 0;
-		if (!read_line(fd, line, sizeof(line)-1)) {
+		if (!read_line(f_in, line, sizeof(line)-1)) {
 			return -1;
 		}
 
 		if (!*line || strcmp(line,"#list")==0) {
-			send_listing(fd);
+			send_listing(f_out);
 			return -1;
 		} 
 
 		if (*line == '#') {
 			/* it's some sort of command that I don't understand */
-			io_printf(fd,"@ERROR: Unknown command '%s'\n", line);
+			io_printf(f_out,"@ERROR: Unknown command '%s'\n", line);
 			return -1;
 		}
 
 		i = lp_number(line);
 		if (i == -1) {
-			io_printf(fd,"@ERROR: Unknown module '%s'\n", line);
+			io_printf(f_out,"@ERROR: Unknown module '%s'\n", line);
 			return -1;
 		}
 	}
 
-	return rsync_module(fd, i);
+	return rsync_module(f_in, f_out, i);
 }
 
-
 int daemon_main(void)
 {
 	extern char *config_file;
@@ -498,7 +547,7 @@
 			open("/dev/null", O_RDWR);
 		}
 
-		return start_daemon(STDIN_FILENO);
+		return start_daemon(STDIN_FILENO, STDIN_FILENO);
 	}
 
 	become_daemon();
Index: main.c
===================================================================
RCS file: /juno/repository/usr/local/pkg/rsync/main.c,v
retrieving revision 1.1.1.7
retrieving revision 1.7
diff -u -b -r1.1.1.7 -r1.7
--- main.c	2001/12/03 23:48:56	1.1.1.7
+++ main.c	2001/12/04 00:10:38	1.7
@@ -133,9 +133,11 @@
 	int i,argc=0;
 	pid_t ret;
 	char *tok,*dir=NULL;
+	int dash_l_set = 0;
 	extern int local_server;
 	extern char *rsync_path;
 	extern int blocking_io;
+	extern int daemon_over_rsh;
 	extern int read_batch;
 
 	if (!read_batch && !local_server) { /* dw -- added read_batch */
@@ -151,15 +153,23 @@
 			args[argc++] = tok;
 		}
 
+		/* check to see if we've already been given '-l user' in 
+		   the remote-shell command */
+		for (i=0;i<(argc-1);i++) {
+			if (!strcmp("-l", args[i]) && (args[i+1][0] != '-')) {
+				dash_l_set = 1;
+			}
+		}
+
 #if HAVE_REMSH
 		/* remsh (on HPUX) takes the arguments the other way around */
 		args[argc++] = machine;
-		if (user) {
+		if (user && !(daemon_over_rsh && dash_l_set)) {
 			args[argc++] = "-l";
 			args[argc++] = user;
 		}
 #else
-		if (user) {
+		if (user && !(daemon_over_rsh && dash_l_set)) {
 			args[argc++] = "-l";
 			args[argc++] = user;
 		}
@@ -176,8 +186,10 @@
 
 	args[argc++] = ".";
 
+	if (!daemon_over_rsh) {
 	if (path && *path) 
 		args[argc++] = path;
+	}
 
 	args[argc] = NULL;
 
@@ -617,15 +629,17 @@
 	extern int am_sender;
 	extern char *shell_cmd;
 	extern int rsync_port;
+	extern int daemon_over_rsh;
 	extern int whole_file;
 	extern int read_batch;
 	int rc;
 
 	/* Don't clobber argv[] so that ps(1) can still show the right
            command line. */
-	if ((rc = copy_argv (argv)))
+	if ((rc = copy_argv(argv)))
 		return rc;
 
+	/* rsync:// always uses rsync server over direct socket connection */
 	if (strncasecmp(URL_PREFIX, argv[0], strlen(URL_PREFIX)) == 0) {
 		char *host, *path;
 
@@ -651,7 +665,13 @@
 	if (p) {
 		if (p[1] == ':') {
 			*p = 0;
-			return start_socket_client(argv[0], p+2, argc-1, argv+1);
+			if (!shell_cmd) {
+				return start_socket_client(argv[0], p+2, 
+							   argc-1, argv+1);
+			} else {
+				++p;
+				daemon_over_rsh = 1;
+			}
 		}
 
 		if (argc < 1) {
@@ -675,7 +695,13 @@
 			whole_file = 1;
 		} else if (p[1] == ':') {
 			*p = 0;
-			return start_socket_client(argv[argc-1], p+2, argc-1, argv);
+			if (!shell_cmd) {
+				return start_socket_client(argv[argc-1], p+2, 
+							   argc-1, argv);
+			} else {
+				++p;
+				daemon_over_rsh = 1;
+			}
 		}
 
 		if (argc < 2) {
@@ -726,8 +752,20 @@
 		list_only = 1;
 	}
 	
-	pid = do_cmd(shell_cmd,shell_machine,shell_user,shell_path,&f_in,&f_out);
+	pid = do_cmd(shell_cmd,shell_machine,shell_user,shell_path,
+	             &f_in,&f_out);
 	
+	/* if we're running an rsync server on the remote host over a
+	   remote shell command, we need to do the RSYNCD protocol first */
+	if (daemon_over_rsh) {
+		int tmpret;
+		tmpret = start_inband_exchange(shell_user, shell_path,
+					       f_in, f_out, argc, argv);
+		if ( tmpret < 0 ) {
+			return tmpret;
+		}
+	}
+
 	ret = client_run(f_in, f_out, pid, argc, argv);
 
 	fflush(stdout);
@@ -813,7 +851,7 @@
 	    set_batch_file_ext(batch_ext);
 	}
 
-	if (am_daemon) {
+	if (am_daemon && !am_server) {
 		return daemon_main();
 	}
 
@@ -835,7 +873,11 @@
 	if (am_server) {
 		set_nonblocking(STDIN_FILENO);
 		set_nonblocking(STDOUT_FILENO);
+		if (am_daemon) {
+			return start_daemon(STDIN_FILENO, STDOUT_FILENO);
+		} else {
 		start_server(STDIN_FILENO, STDOUT_FILENO, argc, argv);
+		}
 	}
 
 	ret = start_client(argc, argv);
Index: options.c
===================================================================
RCS file: /juno/repository/usr/local/pkg/rsync/options.c,v
retrieving revision 1.1.1.5
retrieving revision 1.11
diff -u -b -r1.1.1.5 -r1.11
--- options.c	2001/12/03 23:48:57	1.1.1.5
+++ options.c	2001/12/04 05:30:27	1.11
@@ -21,6 +21,7 @@
 #include "rsync.h"
 #include "popt.h"
 
+
 int make_backups = 0;
 int whole_file = 0;
 int copy_links = 0;
@@ -53,6 +54,7 @@
 int module_id = -1;
 int am_server = 0;
 int am_sender = 0;
+int daemon_over_rsh = 0;
 int recurse = 0;
 int am_daemon=0;
 int do_stats=0;
@@ -535,6 +537,13 @@
 	int i, x;
 
 	args[ac++] = "--server";
+
+	if (daemon_over_rsh) {
+		args[ac++] = "--daemon";
+		*argc = ac;
+		/* if we're passing --daemon, we're done */
+		return;
+	}
 
 	if (!am_sender)
 		args[ac++] = "--sender";
Index: proto.h
===================================================================
RCS file: /juno/repository/usr/local/pkg/rsync/proto.h,v
retrieving revision 1.1.1.7
retrieving revision 1.5
diff -u -b -r1.1.1.7 -r1.5
--- proto.h	2001/12/03 23:48:57	1.1.1.7
+++ proto.h	2001/12/04 00:10:38	1.5
@@ -1,7 +1,7 @@
 /* This file is automatically generated with "make proto". DO NOT EDIT */
 
 int allow_access(char *addr, char *host, char *allow_list, char *deny_list);
-char *auth_server(int fd, int module, char *addr, char *leader);
+char *auth_server(int f_in, int f_out, int module, char *addr, char *leader);
 void auth_client(int fd, char *user, char *challenge);
 int make_backup(char *fname);
 void create_batch_file_ext();
@@ -37,6 +37,8 @@
 		 struct map_struct *buf, int fd1, int fd2);
 void cleanup_set_pid(int pid);
 int start_socket_client(char *host, char *path, int argc, char *argv[]);
+int start_inband_exchange(char *user, char *path, int f_in, int f_out, int argc, char *argv[]);
+int start_daemon(int f_in, int f_out);
 int daemon_main(void);
 void setup_protocol(int f_out,int f_in);
 int claim_connection(char *fname,int max_connections);
@@ -173,7 +175,7 @@
 			     const char *bind_address,
 			     int af_hint);
 int is_a_socket(int fd);
-void start_accept_loop(int port, int (*fn)(int ));
+void start_accept_loop(int port, int (*fn)(int, int));
 void set_socket_options(int fd, char *options);
 void become_daemon(void);
 char *client_addr(int fd);
Index: rsync.1
===================================================================
RCS file: /juno/repository/usr/local/pkg/rsync/rsync.1,v
retrieving revision 1.1.1.7
retrieving revision 1.9
diff -u -b -r1.1.1.7 -r1.9
--- rsync.1	2001/12/03 23:48:58	1.1.1.7
+++ rsync.1	2001/12/04 05:02:26	1.9
@@ -47,7 +47,7 @@
 .PP 
 .SH "GENERAL" 
 .PP 
-There are six different ways of using rsync\&. They are:
+There are eight different ways of using rsync\&. They are:
 .PP 
 .IP o 
 for copying local files\&. This is invoked when neither
@@ -75,6 +75,20 @@
 separator\&. 
 .IP 
 .IP o 
+for copying from a remote machine using a remote shell
+program as the transport, using rsync server on the remote
+machine\&.  This is invoked when the source path contains a ::
+separator and the --rsh=COMMAND (aka "-e COMMAND") option is
+also provided\&.
+.IP 
+.IP o 
+for copying from the local machine to a remote machine
+using a remote shell program as the transport, using rsync
+server on the remote machine\&.  This is invoked when the
+destination path contains a :: separator and the
+--rsh=COMMMAND option is also provided\&.
+.IP 
+.IP o 
 for listing files on a remote machine\&. This is done the
 same way as rsync transfers except that you leave off the
 local destination\&.  
@@ -139,7 +153,7 @@
 using the --delete option\&.
 .PP 
 You can also use rsync in local-only mode, where both the source and
-destination don\&'t have a \&':\&' in the name\&. In this case it behaves like
+destination don\'t have a \':\' in the name\&. In this case it behaves like
 an improved copy command\&.
 .PP 
 .RS 
@@ -155,7 +169,7 @@
 transport\&. In this case you will connect to a remote rsync server
 running on TCP port 873\&. 
 .PP 
-You may establish the connetcion via a web proxy by setting the
+You may establish the connection via a web proxy by setting the
 environment variable RSYNC_PROXY to a hostname:port pair pointing to
 your web proxy\&. Note that your web proxy must allow proxying to port
 873, this must be configured in your proxy servers ruleset\&.
@@ -188,17 +202,70 @@
 WARNING: On some systems environment variables are visible to all
 users\&. On those systems using --password-file is recommended\&.
 .PP 
+.SH "CONNECTING TO AN RSYNC SERVER OVER A REMOTE SHELL PROGRAM" 
+.PP 
+It is sometimes useful to be able to set up file transfers using rsync
+server capabilities on the remote machine, while still using rsh or
+ssh for transport\&.  This is especially useful when you want to connect
+to a remote machine via ssh (for encryption or to get through a
+firewall), but you still want to have access to the rsync server
+features (see RUNNING AN RSYNC SERVER OVER A REMOTE SHELL PROGRAM,
+below)\&.  
+.PP 
+From the user\'s perspective, using rsync in this way is the same as
+using it to connect to an rsync server, except that you must
+explicitly set the remote shell program on the command line with
+--rsh=COMMAND\&.  (Setting RSYNC_RSH in the environment will not turn on
+this functionality\&.)
+.PP 
+In order to distinguish between the remote-shell user and the rsync
+server user, you can use \'-l user\' on your remote-shell command:
+.PP 
+.RS 
+rsync -av --rsh="ssh -l ssh-user" rsync-user@host::module[/path] local-path
+.RE 
+.PP 
+The "ssh-user" will be used at the ssh level; the "rsync-user" will be
+used to check against the rsyncd\&.conf on the remote host\&.
+.PP 
 .SH "RUNNING AN RSYNC SERVER" 
 .PP 
 An rsync server is configured using a config file which by default is
 called /etc/rsyncd\&.conf\&. Please see the rsyncd\&.conf(5) man page for more
 information\&. 
 .PP 
+.SH "RUNNING AN RSYNC SERVER OVER A REMOTE SHELL PROGRAM" 
+.PP 
+See the rsyncd\&.conf(5) man page for full information on the rsync
+server configuration file\&.  
+.PP 
+Several configuration options will not be available unless the remote
+user is root (e\&.g\&. chroot, setuid/setgid, etc\&.)\&.  There is no need to
+configure inetd or the services map to include the rsync server port
+if you run an rsync server only via a remote shell program\&.
+.PP 
+To run an rsync server out of a single-use ssh key, use the
+"command=\fICOMMAND\fP" syntax in the remote user\'s
+authorized_keys entry, where command would be
+.PP 
+.RS 
+rsync --server --daemon \&.
+.RE 
+.PP 
+NOTE: rsync\'s argument parsing expects the trailing "\&.", so make sure
+that it\'s there\&.  If you want to use a rsyncd\&.conf(5)-style
+configuration file other than /etc/rsyncd\&.conf, you can added a
+--config-file option to the \fIcommand\fP:
+.PP 
+.RS 
+rsync --server --daemon --config-file=\fIfile\fP \&.
+.RE 
+.PP 
 .SH "EXAMPLES" 
 .PP 
 Here are some examples of how I use rsync\&.
 .PP 
-To backup my wife\&'s home directory, which consists of large MS Word
+To backup my wife\'s home directory, which consists of large MS Word
 files and mail folders, I use a cron job that runs
 .PP 
 .RS 
@@ -214,7 +281,7 @@
 .RS 
 get:
 .br 
-rsync -avuzb --exclude \&'*~\&' samba:samba/ \&.
+rsync -avuzb --exclude \'*~\' samba:samba/ \&.
 .PP 
 put:
 .br 
@@ -225,7 +292,7 @@
 .PP 
 this allows me to sync with a CVS directory at the other end of the
 link\&. I then do cvs operations on the remote machine, which saves a
-lot of time as the remote cvs protocol isn\&'t very efficient\&.
+lot of time as the remote cvs protocol isn\'t very efficient\&.
 .PP 
 I mirror a directory between my "old" and "new" ftp sites with the
 command
@@ -254,7 +321,7 @@
  -b, --backup                make backups (default ~ suffix)
      --backup-dir            make backups into this directory
      --suffix=SUFFIX         override backup suffix
- -u, --update                update only (don\&'t overwrite newer files)
+ -u, --update                update only (don\'t overwrite newer files)
  -l, --links                 preserve soft links
  -L, --copy-links            treat soft links like regular files
      --copy-unsafe-links     copy links outside the source tree
@@ -268,22 +335,22 @@
  -S, --sparse                handle sparse files efficiently
  -n, --dry-run               show what would have been transferred
  -W, --whole-file            copy whole files, no incremental checks
- -x, --one-file-system       don\&'t cross filesystem boundaries
+ -x, --one-file-system       don\'t cross filesystem boundaries
  -B, --block-size=SIZE       checksum blocking size (default 700)
  -e, --rsh=COMMAND           specify rsh replacement
      --rsync-path=PATH       specify path to rsync on the remote machine
  -C, --cvs-exclude           auto ignore files in the same way CVS does
      --existing              only update files that already exist
-     --delete                delete files that don\&'t exist on the sending side
+     --delete                delete files that don\'t exist on the sending side
      --delete-excluded       also delete excluded files on the receiving side
      --delete-after          delete after transferring, not before
      --ignore-errors         delete even if there are IO errors
-     --max-delete=NUM        don\&'t delete more than NUM files
+     --max-delete=NUM        don\'t delete more than NUM files
      --partial               keep partially transferred files
      --force                 force deletion of directories even if not empty
-     --numeric-ids           don\&'t map uid/gid values by user/group name
+     --numeric-ids           don\'t map uid/gid values by user/group name
      --timeout=TIME          set IO timeout in seconds
- -I, --ignore-times          don\&'t exclude files that match length and time
+ -I, --ignore-times          don\'t exclude files that match length and time
      --size-only             only use file size when determining if a file should be transferred
      --modify-window=NUM     Timestamp window (seconds) for file match (default=0)
  -T  --temp-dir=DIR          create temporary files in directory DIR
@@ -292,8 +359,8 @@
  -z, --compress              compress file data
      --exclude=PATTERN       exclude files matching PATTERN
      --exclude-from=FILE     exclude patterns listed in FILE
-     --include=PATTERN       don\&'t exclude files matching PATTERN
-     --include-from=FILE     don\&'t exclude patterns listed in FILE
+     --include=PATTERN       don\'t exclude files matching PATTERN
+     --include-from=FILE     don\'t exclude patterns listed in FILE
      --version               print version number
      --daemon                run as a rsync daemon
      --address               bind to the specified address
@@ -320,7 +387,7 @@
 rsync uses the GNU long options package\&. Many of the command line
 options have two variants, one short and one long\&.  These are shown
 below, separated by commas\&. Some options only have a long variant\&.
-The \&'=\&' for options that take a parameter is optional; whitespace
+The \'=\' for options that take a parameter is optional; whitespace
 can be used instead\&.
 .PP 
 .IP "\fB-h, --help\fP" 
@@ -384,7 +451,7 @@
 .IP 
 .IP "\fB-r, --recursive\fP" 
 This tells rsync to copy directories
-recursively\&. If you don\&'t specify this then rsync won\&'t copy
+recursively\&. If you don\'t specify this then rsync won\'t copy
 directories at all\&.
 .IP 
 .IP "\fB-R, --relative\fP" 
@@ -502,7 +569,7 @@
 option is not used, the optimization that excludes files that have not been
 modified cannot be effective; in other words, a missing -t or -a will
 cause the next transfer to behave as if it used -I, and all files will have
-their checksums compared and show up in log messages even if they haven\&'t
+their checksums compared and show up in log messages even if they haven\'t
 changed\&.
 .IP 
 .IP "\fB-n, --dry-run\fP" 
@@ -513,8 +580,8 @@
 Try to handle sparse files efficiently so they take
 up less space on the destination\&.
 .IP 
-NOTE: Don\&'t use this option when the destination is a Solaris "tmpfs"
-filesystem\&. It doesn\&'t seem to handle seeks over null regions
+NOTE: Don\'t use this option when the destination is a Solaris "tmpfs"
+filesystem\&. It doesn\'t seem to handle seeks over null regions
 correctly and ends up corrupting the files\&.
 .IP 
 .IP "\fB-x, --one-file-system\fP" 
@@ -533,14 +600,14 @@
 .IP 
 .IP "\fB--delete\fP" 
 This tells rsync to delete any files on the receiving
-side that aren\&'t on the sending side\&.   Files that are excluded from
+side that aren\'t on the sending side\&.   Files that are excluded from
 transfer are excluded from being deleted unless you use --delete-excluded\&.
 .IP 
 This option has no effect if directory recursion is not selected\&.
 .IP 
 This option can be dangerous if used incorrectly!  It is a very good idea
 to run first using the dry run option (-n) to see what files would be
-deleted to make sure important files aren\&'t listed\&.
+deleted to make sure important files aren\'t listed\&.
 .IP 
 If the sending side detects any IO errors then the deletion of any
 files at the destination will be automatically disabled\&. This is to
@@ -582,14 +649,29 @@
 remote copies of rsync\&. By default, rsync will use rsh, but you may
 like to instead use ssh because of its high security\&.
 .IP 
+If this option is used with \fB[user@]host::module/path\fP, then the
+remote shell \fICOMMMAND\fP will be used to run an rsync server on the
+remote host, and all data will be transmitted through that remote
+shell connection, rather than through a direct socket connection to a
+running rsync server on the remote host\&.
+.IP 
 You can also choose the remote shell program using the RSYNC_RSH
 environment variable\&.
 .IP 
+In either case, rsync will tokenize the remote-shell command, so you
+can use constructions like
+.IP 
+.RS 
+rsync [options] --rsh="ssh -l user -i identity" source dest
+.RE 
+.IP 
+to control things at a fine level\&.
+.IP 
 See also the --blocking-io option which is affected by this option\&.
 .IP 
 .IP "\fB--rsync-path=PATH\fP" 
 Use this to specify the path to the copy of
-rsync on the remote machine\&. Useful when it\&'s not in your path\&. Note
+rsync on the remote machine\&. Useful when it\'s not in your path\&. Note
 that this is the full path to the binary, not just the directory that
 the binary is in\&.
 .IP 
@@ -608,7 +690,7 @@
 This option is similar to the --exclude
 option, but instead it adds all exclude patterns listed in the file
 FILE to the exclude list\&.  Blank lines in FILE and lines starting with
-\&';\&' or \&'#\&' are ignored\&.
+\';\' or \'#\' are ignored\&.
 .IP 
 .IP "\fB--include=PATTERN\fP" 
 This option tells rsync to not exclude the
@@ -624,7 +706,7 @@
 .IP 
 .IP "\fB-C, --cvs-exclude\fP" 
 This is a useful shorthand for excluding a
-broad range of files that you often don\&'t want to transfer between
+broad range of files that you often don\'t want to transfer between
 systems\&. It uses the same algorithm that CVS uses to determine if
 a file should be ignored\&.
 .IP 
@@ -676,7 +758,7 @@
 flash-cutover when all files have been successfully transferred (for
 example by moving directories around and removing the old directory,
 although this requires also doing the transfer with -I to avoid skipping
-files that haven\&'t changed)\&.  This option increases the usefulness of
+files that haven\'t changed)\&.  This option increases the usefulness of
 --partial because partially transferred files will remain in the new
 temporary destination until they have a chance to be completed\&.  If DIR is
 a relative path, it is relative to the destination directory\&.
@@ -741,7 +823,7 @@
 a remote shell transport\&.  If -e or --rsh are not specified or are set to
 the default "rsh", this defaults to blocking IO, otherwise it defaults to
 non-blocking IO\&.  You may find the --blocking-io option is needed for some
-remote shells that can\&'t handle non-blocking IO\&.  Ssh prefers blocking IO\&.
+remote shells that can\'t handle non-blocking IO\&.  Ssh prefers blocking IO\&.
 .IP 
 .IP "\fB--log-format=FORMAT\fP" 
 This allows you to specify exactly what the
@@ -847,7 +929,7 @@
 .IP o 
 if the pattern contains a / (not counting a trailing /) then it
 is matched against the full filename, including any leading
-directory\&. If the pattern doesn\&'t contain a / then it is matched
+directory\&. If the pattern doesn\'t contain a / then it is matched
 only against the final component of the filename\&.  Again, remember
 that the algorithm is applied recursively so "full filename" can 
 actually be any portion of a path\&.
@@ -869,12 +951,12 @@
 The +/- rules are most useful in exclude lists, allowing you to have a
 single exclude list that contains both include and exclude options\&.
 .PP 
-If you end an exclude list with --exclude \&'*\&', note that since the
+If you end an exclude list with --exclude \'*\', note that since the
 algorithm is applied recursively that unless you explicitly include
 parent directories of files you want to include then the algorithm
 will stop at the parent directories and never see the files below
-them\&.  To include all directories, use --include \&'*/\&' before the
---exclude \&'*\&'\&.
+them\&.  To include all directories, use --include \'*/\' before the
+--exclude \'*\'\&.
 .PP 
 Here are some exclude/include examples:
 .PP 
@@ -1020,7 +1102,7 @@
 are used to determine the default username sent to a rsync server\&.
 .IP 
 .IP "\fBHOME\fP" 
-The HOME environment variable is used to find the user\&'s
+The HOME environment variable is used to find the user\'s
 default \&.cvsignore file\&.
 .IP 
 .PP 
@@ -1070,7 +1152,7 @@
 .SH "THANKS" 
 .PP 
 Thanks to Richard Brent, Brendan Mackay, Bill Waite, Stephen Rothwell
-and David Bell for helpful suggestions and testing of rsync\&. I\&'ve
+and David Bell for helpful suggestions and testing of rsync\&. I\'ve
 probably missed some people, my apologies if I have\&.
 .PP 
 .SH "AUTHOR" 
Index: rsync.yo
===================================================================
RCS file: /juno/repository/usr/local/pkg/rsync/rsync.yo,v
retrieving revision 1.1.1.5
retrieving revision 1.4
diff -u -b -r1.1.1.5 -r1.4
--- rsync.yo	2001/12/03 23:49:01	1.1.1.5
+++ rsync.yo	2001/12/04 00:10:39	1.4
@@ -42,7 +42,7 @@
 
 manpagesection(GENERAL)
 
-There are six different ways of using rsync. They are:
+There are eight different ways of using rsync. They are:
 
 itemize(
 	it() for copying local files. This is invoked when neither
@@ -65,6 +65,18 @@
 	server. This is invoked when the destination path contains a ::
 	separator. 
 
+	it() for copying from a remote machine using a remote shell
+	program as the transport, using rsync server on the remote
+	machine.  This is invoked when the source path contains a ::
+	separator and the --rsh=COMMAND (aka "-e COMMAND") option is
+	also provided.
+
+	it() for copying from the local machine to a remote machine
+	using a remote shell program as the transport, using rsync
+	server on the remote machine.  This is invoked when the
+	destination path contains a :: separator and the
+	--rsh=COMMMAND option is also provided.
+
 	it() for listing files on a remote machine. This is done the
 	same way as rsync transfers except that you leave off the
 	local destination.  
@@ -139,7 +151,7 @@
 transport. In this case you will connect to a remote rsync server
 running on TCP port 873. 
 
-You may establish the connetcion via a web proxy by setting the
+You may establish the connection via a web proxy by setting the
 environment variable RSYNC_PROXY to a hostname:port pair pointing to
 your web proxy. Note that your web proxy must allow proxying to port
 873, this must be configured in your proxy servers ruleset.
@@ -170,12 +182,59 @@
 WARNING: On some systems environment variables are visible to all
 users. On those systems using --password-file is recommended.
 
+manpagesection(CONNECTING TO AN RSYNC SERVER OVER A REMOTE SHELL PROGRAM)
+
+It is sometimes useful to be able to set up file transfers using rsync
+server capabilities on the remote machine, while still using rsh or
+ssh for transport.  This is especially useful when you want to connect
+to a remote machine via ssh (for encryption or to get through a
+firewall), but you still want to have access to the rsync server
+features (see RUNNING AN RSYNC SERVER OVER A REMOTE SHELL PROGRAM,
+below).  
+
+From the user's perspective, using rsync in this way is the same as
+using it to connect to an rsync server, except that you must
+explicitly set the remote shell program on the command line with
+--rsh=COMMAND.  (Setting RSYNC_RSH in the environment will not turn on
+this functionality.)
+
+In order to distinguish between the remote-shell user and the rsync
+server user, you can use '-l user' on your remote-shell command:
+
+quote(rsync -av --rsh="ssh -l ssh-user" rsync-user@host::module[/path] local-path)
+
+The "ssh-user" will be used at the ssh level; the "rsync-user" will be
+used to check against the rsyncd.conf on the remote host.
+
 manpagesection(RUNNING AN RSYNC SERVER)
 
 An rsync server is configured using a config file which by default is
 called /etc/rsyncd.conf. Please see the rsyncd.conf(5) man page for more
 information. 
 
+manpagesection(RUNNING AN RSYNC SERVER OVER A REMOTE SHELL PROGRAM)
+
+See the rsyncd.conf(5) man page for full information on the rsync
+server configuration file.  
+
+Several configuration options will not be available unless the remote
+user is root (e.g. chroot, setuid/setgid, etc.).  There is no need to
+configure inetd or the services map to include the rsync server port
+if you run an rsync server only via a remote shell program.
+
+To run an rsync server out of a single-use ssh key, use the
+"command=em(COMMAND)" syntax in the remote user's
+authorized_keys entry, where command would be
+
+quote(rsync --server --daemon .)
+
+NOTE: rsync's argument parsing expects the trailing ".", so make sure
+that it's there.  If you want to use a rsyncd.conf(5)-style
+configuration file other than /etc/rsyncd.conf, you can added a
+--config-file option to the em(command):
+
+quote(rsync --server --daemon --config-file=em(file) .)
+
 manpagesection(EXAMPLES)
 
 Here are some examples of how I use rsync.
@@ -501,8 +560,21 @@
 remote copies of rsync. By default, rsync will use rsh, but you may
 like to instead use ssh because of its high security.
 
+If this option is used with bf([user@]host::module/path), then the
+remote shell em(COMMMAND) will be used to run an rsync server on the
+remote host, and all data will be transmitted through that remote
+shell connection, rather than through a direct socket connection to a
+running rsync server on the remote host.
+
 You can also choose the remote shell program using the RSYNC_RSH
 environment variable.
+
+In either case, rsync will tokenize the remote-shell command, so you
+can use constructions like
+
+quote(rsync [options] --rsh="ssh -l user -i identity" source dest)
+
+to control things at a fine level.
 
 See also the --blocking-io option which is affected by this option.
 
Index: rsyncd.conf.5
===================================================================
RCS file: /juno/repository/usr/local/pkg/rsync/rsyncd.conf.5,v
retrieving revision 1.1.1.5
retrieving revision 1.3
diff -u -b -r1.1.1.5 -r1.3
--- rsyncd.conf.5	2001/12/03 23:48:52	1.1.1.5
+++ rsyncd.conf.5	2001/12/04 00:10:39	1.3
@@ -238,6 +238,11 @@
 "secrets file" option\&. The default is for all users to be able to
 connect without a password (this is called "anonymous rsync")\&.
 .IP 
+See also the \fBCONNECTING TO AN RSYNC SERVER OVER A REMOTE SHELL
+PROGRAM\fP section in rsync(1) for information on how handle an
+rsyncd\&.conf-level username that differs from the remote-shell-level
+username when using a remote shell to connect to a rsync server\&.
+.IP 
 .IP "\fBsecrets file\fP" 
 The "secrets file" option specifies the name of
 a file that contains the username:password pairs used for
Index: rsyncd.conf.yo
===================================================================
RCS file: /juno/repository/usr/local/pkg/rsync/rsyncd.conf.yo,v
retrieving revision 1.1.1.5
retrieving revision 1.3
diff -u -b -r1.1.1.5 -r1.3
--- rsyncd.conf.yo	2001/12/03 23:49:02	1.1.1.5
+++ rsyncd.conf.yo	2001/12/04 00:10:39	1.3
@@ -219,6 +219,11 @@
 "secrets file" option. The default is for all users to be able to
 connect without a password (this is called "anonymous rsync").
 
+See also the bf(CONNECTING TO AN RSYNC SERVER OVER A REMOTE SHELL
+PROGRAM) section in rsync(1) for information on how handle an
+rsyncd.conf-level username that differs from the remote-shell-level
+username when using a remote shell to connect to a rsync server.
+
 dit(bf(secrets file)) The "secrets file" option specifies the name of
 a file that contains the username:password pairs used for
 authenticating this module. This file is only consulted if the "auth
Index: socket.c
===================================================================
RCS file: /juno/repository/usr/local/pkg/rsync/socket.c,v
retrieving revision 1.1.1.5
retrieving revision 1.3
diff -u -b -r1.1.1.5 -r1.3
--- socket.c	2001/12/03 23:49:04	1.1.1.5
+++ socket.c	2001/12/04 00:10:39	1.3
@@ -349,7 +349,7 @@
 }
 
 
-void start_accept_loop(int port, int (*fn)(int ))
+void start_accept_loop(int port, int (*fn)(int, int))
 {
 	int s;
 	extern char *bind_address;
@@ -407,7 +407,7 @@
 			/* open log file in child before possibly giving
 			   up privileges  */
 			log_open();
-			_exit(fn(fd));
+			_exit(fn(fd, fd));
 		}
 
 		close(fd);


