mirror of
https://github.com/RsyncProject/rsync.git
synced 2026-03-10 02:16:35 -04:00
Add --early-input=FILE option.
This commit is contained in:
3
NEWS.md
3
NEWS.md
@@ -23,6 +23,9 @@ Protocol: 31 (unchanged)
|
||||
|
||||
### ENHANCEMENTS:
|
||||
|
||||
- Added `--early-input=FILE` option that allows the client to send some
|
||||
data to the "early exec" daemon script on its stdin.
|
||||
|
||||
- Added "atimes" to the capabilities list that `--version` outputs.
|
||||
|
||||
- Mention either "default protect-args" or "optional protect-args" in the
|
||||
|
||||
126
clientserver.c
126
clientserver.c
@@ -54,6 +54,7 @@ extern char *config_file;
|
||||
extern char *logfile_format;
|
||||
extern char *files_from;
|
||||
extern char *tmpdir;
|
||||
extern char *early_input_file;
|
||||
extern struct chmod_mode_struct *chmod_modes;
|
||||
extern filter_rule_list daemon_filter_list;
|
||||
#ifdef ICONV_OPTION
|
||||
@@ -67,8 +68,13 @@ char *auth_user;
|
||||
int read_only = 0;
|
||||
int module_id = -1;
|
||||
int pid_file_fd = -1;
|
||||
int early_input_len = 0;
|
||||
char *early_input = NULL;
|
||||
struct chmod_mode_struct *daemon_chmod_modes;
|
||||
|
||||
#define EARLY_INPUT_CMD "#early_input="
|
||||
#define EARLY_INPUT_CMDLEN (sizeof EARLY_INPUT_CMD - 1)
|
||||
|
||||
/* module_dirlen is the length of the module_dir string when in daemon
|
||||
* mode and module_dir is not "/"; otherwise 0. (Note that a chroot-
|
||||
* enabled module can have a non-"/" module_dir these days.) */
|
||||
@@ -144,14 +150,12 @@ static int exchange_protocols(int f_in, int f_out, char *buf, size_t bufsiz, int
|
||||
#else
|
||||
int our_sub = 0;
|
||||
#endif
|
||||
char *motd;
|
||||
|
||||
io_printf(f_out, "@RSYNCD: %d.%d\n", protocol_version, our_sub);
|
||||
|
||||
if (!am_client) {
|
||||
motd = lp_motd_file();
|
||||
char *motd = lp_motd_file();
|
||||
if (motd && *motd) {
|
||||
FILE *f = fopen(motd,"r");
|
||||
FILE *f = fopen(motd, "r");
|
||||
while (f && !feof(f)) {
|
||||
int len = fread(buf, 1, bufsiz - 1, f);
|
||||
if (len > 0)
|
||||
@@ -245,6 +249,36 @@ int start_inband_exchange(int f_in, int f_out, const char *user, int argc, char
|
||||
if (exchange_protocols(f_in, f_out, line, sizeof line, 1) < 0)
|
||||
return -1;
|
||||
|
||||
if (early_input_file) {
|
||||
STRUCT_STAT st;
|
||||
FILE *f = fopen(early_input_file, "rb");
|
||||
if (!f || do_fstat(fileno(f), &st) < 0) {
|
||||
rsyserr(FERROR, errno, "failed to open %s", early_input_file);
|
||||
return -1;
|
||||
}
|
||||
early_input_len = st.st_size;
|
||||
if (early_input_len >= (int)sizeof line) {
|
||||
rprintf(FERROR, "%s is >= %d bytes.\n", early_input_file, (int)sizeof line);
|
||||
return -1;
|
||||
}
|
||||
if (early_input_len > 0) {
|
||||
io_printf(f_out, EARLY_INPUT_CMD "%d\n", early_input_len);
|
||||
while (early_input_len > 0) {
|
||||
int len;
|
||||
if (feof(f)) {
|
||||
rprintf(FERROR, "Early EOF in %s\n", early_input_file);
|
||||
return -1;
|
||||
}
|
||||
len = fread(line, 1, early_input_len / 2 + 1, f);
|
||||
if (len > 0) {
|
||||
write_buf(f_out, line, len);
|
||||
early_input_len -= len;
|
||||
}
|
||||
}
|
||||
}
|
||||
fclose(f);
|
||||
}
|
||||
|
||||
/* 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 */
|
||||
@@ -397,7 +431,7 @@ static pid_t start_pre_exec(const char *cmd, int *arg_fd_ptr, int *error_fd_ptr)
|
||||
int arg_fds[2], error_fds[2], arg_fd;
|
||||
pid_t pid;
|
||||
|
||||
if ((error_fd_ptr && pipe(error_fds) < 0) || (arg_fd_ptr && pipe(arg_fds) < 0) || (pid = fork()) < 0)
|
||||
if ((error_fd_ptr && pipe(error_fds) < 0) || pipe(arg_fds) < 0 || (pid = fork()) < 0)
|
||||
return (pid_t)-1;
|
||||
|
||||
if (pid == 0) {
|
||||
@@ -409,37 +443,35 @@ static pid_t start_pre_exec(const char *cmd, int *arg_fd_ptr, int *error_fd_ptr)
|
||||
set_blocking(error_fds[1]);
|
||||
}
|
||||
|
||||
if (arg_fd_ptr) {
|
||||
close(arg_fds[1]);
|
||||
arg_fd = arg_fds[0];
|
||||
set_blocking(arg_fd);
|
||||
close(arg_fds[1]);
|
||||
arg_fd = arg_fds[0];
|
||||
set_blocking(arg_fd);
|
||||
|
||||
len = read_arg_from_pipe(arg_fd, buf, BIGPATHBUFLEN);
|
||||
if (len <= 0)
|
||||
_exit(1);
|
||||
set_env_str("RSYNC_REQUEST", buf);
|
||||
|
||||
for (j = 0; ; j++) {
|
||||
char *p;
|
||||
len = read_arg_from_pipe(arg_fd, buf, BIGPATHBUFLEN);
|
||||
if (len <= 0)
|
||||
if (len <= 0) {
|
||||
if (!len)
|
||||
break;
|
||||
_exit(1);
|
||||
set_env_str("RSYNC_REQUEST", buf);
|
||||
|
||||
for (j = 0; ; j++) {
|
||||
char *p;
|
||||
len = read_arg_from_pipe(arg_fd, buf, BIGPATHBUFLEN);
|
||||
if (len <= 0) {
|
||||
if (!len)
|
||||
break;
|
||||
_exit(1);
|
||||
}
|
||||
if (asprintf(&p, "RSYNC_ARG%d=%s", j, buf) >= 0)
|
||||
putenv(p);
|
||||
}
|
||||
close(arg_fd);
|
||||
if (asprintf(&p, "RSYNC_ARG%d=%s", j, buf) >= 0)
|
||||
putenv(p);
|
||||
}
|
||||
|
||||
dup2(arg_fd, STDIN_FILENO);
|
||||
close(arg_fd);
|
||||
|
||||
if (error_fd_ptr) {
|
||||
dup2(error_fds[1], STDOUT_FILENO);
|
||||
close(error_fds[1]);
|
||||
}
|
||||
|
||||
close(STDIN_FILENO);
|
||||
|
||||
status = shell_exec(cmd);
|
||||
|
||||
if (!WIFEXITED(status))
|
||||
@@ -453,16 +485,14 @@ static pid_t start_pre_exec(const char *cmd, int *arg_fd_ptr, int *error_fd_ptr)
|
||||
set_blocking(error_fds[0]);
|
||||
}
|
||||
|
||||
if (arg_fd_ptr) {
|
||||
close(arg_fds[0]);
|
||||
arg_fd = *arg_fd_ptr = arg_fds[1];
|
||||
set_blocking(arg_fd);
|
||||
}
|
||||
close(arg_fds[0]);
|
||||
arg_fd = *arg_fd_ptr = arg_fds[1];
|
||||
set_blocking(arg_fd);
|
||||
|
||||
return pid;
|
||||
}
|
||||
|
||||
static void write_pre_exec_args(int write_fd, char *request, char **early_argv, char **argv)
|
||||
static void write_pre_exec_args(int write_fd, char *request, char **early_argv, char **argv, int am_early)
|
||||
{
|
||||
int j = 0;
|
||||
|
||||
@@ -475,10 +505,15 @@ static void write_pre_exec_args(int write_fd, char *request, char **early_argv,
|
||||
write_buf(write_fd, *early_argv, strlen(*early_argv)+1);
|
||||
j = 1; /* Skip arg0 name in argv. */
|
||||
}
|
||||
for ( ; argv[j]; j++)
|
||||
write_buf(write_fd, argv[j], strlen(argv[j])+1);
|
||||
if (argv) {
|
||||
for ( ; argv[j]; j++)
|
||||
write_buf(write_fd, argv[j], strlen(argv[j])+1);
|
||||
}
|
||||
write_byte(write_fd, 0);
|
||||
|
||||
if (am_early && early_input_len)
|
||||
write_buf(write_fd, early_input, early_input_len);
|
||||
|
||||
close(write_fd);
|
||||
}
|
||||
|
||||
@@ -812,12 +847,14 @@ static int rsync_module(int f_in, int f_out, int i, const char *addr, const char
|
||||
/* For early exec, fork a child process to run the indicated
|
||||
* command and wait for it to exit. */
|
||||
if (*lp_early_exec(i)) {
|
||||
pid_t pid = start_pre_exec(lp_early_exec(i), NULL, NULL);
|
||||
int arg_fd;
|
||||
pid_t pid = start_pre_exec(lp_early_exec(i), &arg_fd, NULL);
|
||||
if (pid == (pid_t)-1) {
|
||||
rsyserr(FLOG, errno, "early exec preparation failed");
|
||||
io_printf(f_out, "@ERROR: early exec preparation failed\n");
|
||||
return -1;
|
||||
}
|
||||
write_pre_exec_args(arg_fd, NULL, NULL, NULL, 1);
|
||||
if (finish_pre_exec("early exec", pid, -1) != NULL) {
|
||||
rsyserr(FLOG, errno, "early exec failed");
|
||||
io_printf(f_out, "@ERROR: early exec failed\n");
|
||||
@@ -839,6 +876,11 @@ static int rsync_module(int f_in, int f_out, int i, const char *addr, const char
|
||||
}
|
||||
#endif
|
||||
|
||||
if (early_input) {
|
||||
free(early_input);
|
||||
early_input = NULL;
|
||||
}
|
||||
|
||||
if (use_chroot) {
|
||||
/*
|
||||
* XXX: The 'use chroot' flag is a fairly reliable
|
||||
@@ -953,7 +995,7 @@ static int rsync_module(int f_in, int f_out, int i, const char *addr, const char
|
||||
msgs2stderr = 0; /* A non-rsh-run daemon doesn't have stderr for msgs. */
|
||||
|
||||
if (pre_exec_pid) {
|
||||
write_pre_exec_args(pre_exec_arg_fd, request, orig_early_argv, orig_argv);
|
||||
write_pre_exec_args(pre_exec_arg_fd, request, orig_early_argv, orig_argv, 0);
|
||||
err_msg = finish_pre_exec("pre-xfer exec", pre_exec_pid, pre_exec_error_fd);
|
||||
}
|
||||
|
||||
@@ -1185,6 +1227,20 @@ int start_daemon(int f_in, int f_out)
|
||||
if (!read_line_old(f_in, line, sizeof line, 0))
|
||||
return -1;
|
||||
|
||||
if (strncmp(line, EARLY_INPUT_CMD, EARLY_INPUT_CMDLEN) == 0) {
|
||||
early_input_len = strtol(line + EARLY_INPUT_CMDLEN, NULL, 10);
|
||||
if (early_input_len <= 0 || early_input_len >= BIGPATHBUFLEN) {
|
||||
io_printf(f_out, "@ERROR: invalid early_input length\n");
|
||||
return -1;
|
||||
}
|
||||
if (!(early_input = new_array(char, early_input_len)))
|
||||
out_of_memory("exchange_protocols");
|
||||
read_buf(f_in, early_input, early_input_len);
|
||||
|
||||
if (!read_line_old(f_in, line, sizeof line, 0))
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!*line || strcmp(line, "#list") == 0) {
|
||||
rprintf(FLOG, "module-list request from %s (%s)\n",
|
||||
host, addr);
|
||||
|
||||
@@ -170,6 +170,7 @@ char *logfile_name = NULL;
|
||||
char *logfile_format = NULL;
|
||||
char *stdout_format = NULL;
|
||||
char *password_file = NULL;
|
||||
char *early_input_file = NULL;
|
||||
char *rsync_path = RSYNC_PATH;
|
||||
char *backup_dir = NULL;
|
||||
char backup_dir_buf[MAXPATHLEN];
|
||||
@@ -996,6 +997,7 @@ static struct poptOption long_options[] = {
|
||||
{"port", 0, POPT_ARG_INT, &rsync_port, 0, 0, 0 },
|
||||
{"sockopts", 0, POPT_ARG_STRING, &sockopts, 0, 0, 0 },
|
||||
{"password-file", 0, POPT_ARG_STRING, &password_file, 0, 0, 0 },
|
||||
{"early-input", 0, POPT_ARG_STRING, &early_input_file, 0, 0, 0 },
|
||||
{"blocking-io", 0, POPT_ARG_VAL, &blocking_io, 1, 0, 0 },
|
||||
{"no-blocking-io", 0, POPT_ARG_VAL, &blocking_io, 0, 0, 0 },
|
||||
{"outbuf", 0, POPT_ARG_STRING, &outbuf_mode, 0, 0, 0 },
|
||||
|
||||
10
rsync.1.md
10
rsync.1.md
@@ -452,6 +452,7 @@ detailed description below for a complete description.
|
||||
--log-file=FILE log what we're doing to the specified FILE
|
||||
--log-file-format=FMT log updates using the specified FMT
|
||||
--password-file=FILE read daemon-access password from FILE
|
||||
--early-input=FILE use FILE for daemon's early exec input
|
||||
--list-only list the files instead of copying them
|
||||
--bwlimit=RATE limit socket I/O bandwidth
|
||||
--write-batch=FILE write a batched update to FILE
|
||||
@@ -2977,6 +2978,15 @@ your home directory (remove the '=' for that).
|
||||
authentication (i.e. if you have also specified a password in the daemon's
|
||||
config file).
|
||||
|
||||
0. `--early-input=FILE`
|
||||
|
||||
This option allows rsync to send up to 5K of data to the "early exec"
|
||||
script on its stdin. One possible use of this data is to give the script a
|
||||
secret that can be used to mount an encrypted filesystem (which you should
|
||||
unmount in the the "post-xfer exec" script).
|
||||
|
||||
The daemon must be at least version 3.2.1.
|
||||
|
||||
0. `--list-only`
|
||||
|
||||
This option will cause the source files to be listed instead of
|
||||
|
||||
Reference in New Issue
Block a user