version 1.53, 2021/03/31 19:45:16 |
version 1.53.2.1, 2021/11/09 13:41:24 |
|
|
#include <stdlib.h> |
#include <stdlib.h> |
#include <string.h> |
#include <string.h> |
#include <unistd.h> |
#include <unistd.h> |
|
#include <util.h> |
|
|
#include "extern.h" |
#include "extern.h" |
|
|
|
|
/* Allocations. */ |
/* Allocations. */ |
|
|
if ((f = calloc(1, sizeof(struct fargs))) == NULL) |
if ((f = calloc(1, sizeof(struct fargs))) == NULL) |
err(1, "calloc"); |
err(ERR_NOMEM, NULL); |
|
|
f->sourcesz = argc - 1; |
f->sourcesz = argc - 1; |
if ((f->sources = calloc(f->sourcesz, sizeof(char *))) == NULL) |
if ((f->sources = calloc(f->sourcesz, sizeof(char *))) == NULL) |
err(1, "calloc"); |
err(ERR_NOMEM, NULL); |
|
|
for (i = 0; i < argc - 1; i++) |
for (i = 0; i < argc - 1; i++) |
if ((f->sources[i] = strdup(argv[i])) == NULL) |
if ((f->sources[i] = strdup(argv[i])) == NULL) |
err(1, "strdup"); |
err(ERR_NOMEM, NULL); |
|
|
if ((f->sink = strdup(argv[i])) == NULL) |
if ((f->sink = strdup(argv[i])) == NULL) |
err(1, "strdup"); |
err(ERR_NOMEM, NULL); |
|
|
/* |
/* |
* Test files for its locality. |
* Test files for its locality. |
|
|
if (fargs_is_remote(f->sink)) { |
if (fargs_is_remote(f->sink)) { |
f->mode = FARGS_SENDER; |
f->mode = FARGS_SENDER; |
if ((f->host = strdup(f->sink)) == NULL) |
if ((f->host = strdup(f->sink)) == NULL) |
err(1, "strdup"); |
err(ERR_NOMEM, NULL); |
} |
} |
|
|
if (fargs_is_remote(f->sources[0])) { |
if (fargs_is_remote(f->sources[0])) { |
if (f->host != NULL) |
if (f->host != NULL) |
errx(1, "both source and destination cannot be remote files"); |
errx(ERR_SYNTAX, "both source and destination " |
|
"cannot be remote files"); |
f->mode = FARGS_RECEIVER; |
f->mode = FARGS_RECEIVER; |
if ((f->host = strdup(f->sources[0])) == NULL) |
if ((f->host = strdup(f->sources[0])) == NULL) |
err(1, "strdup"); |
err(ERR_NOMEM, NULL); |
} |
} |
|
|
if (f->host != NULL) { |
if (f->host != NULL) { |
|
|
len = strlen(f->host) - 8 + 1; |
len = strlen(f->host) - 8 + 1; |
memmove(f->host, f->host + 8, len); |
memmove(f->host, f->host + 8, len); |
if ((cp = strchr(f->host, '/')) == NULL) |
if ((cp = strchr(f->host, '/')) == NULL) |
errx(1, "rsync protocol requires a module name"); |
errx(ERR_SYNTAX, |
|
"rsync protocol requires a module name"); |
*cp++ = '\0'; |
*cp++ = '\0'; |
f->module = cp; |
f->module = cp; |
if ((cp = strchr(f->module, '/')) != NULL) |
if ((cp = strchr(f->module, '/')) != NULL) |
|
|
} |
} |
} |
} |
if ((len = strlen(f->host)) == 0) |
if ((len = strlen(f->host)) == 0) |
errx(1, "empty remote host"); |
errx(ERR_SYNTAX, "empty remote host"); |
if (f->remote && strlen(f->module) == 0) |
if (f->remote && strlen(f->module) == 0) |
errx(1, "empty remote module"); |
errx(ERR_SYNTAX, "empty remote module"); |
} |
} |
|
|
/* Make sure we have the same "hostspec" for all files. */ |
/* Make sure we have the same "hostspec" for all files. */ |
|
|
for (i = 0; i < f->sourcesz; i++) { |
for (i = 0; i < f->sourcesz; i++) { |
if (!fargs_is_remote(f->sources[i])) |
if (!fargs_is_remote(f->sources[i])) |
continue; |
continue; |
errx(1, |
errx(ERR_SYNTAX, |
"remote file in list of local sources: %s", |
"remote file in list of local sources: %s", |
f->sources[i]); |
f->sources[i]); |
} |
} |
|
|
!fargs_is_daemon(f->sources[i])) |
!fargs_is_daemon(f->sources[i])) |
continue; |
continue; |
if (fargs_is_daemon(f->sources[i])) |
if (fargs_is_daemon(f->sources[i])) |
errx(1, "remote daemon in list of " |
errx(ERR_SYNTAX, |
"remote sources: %s", |
"remote daemon in list of remote " |
f->sources[i]); |
"sources: %s", f->sources[i]); |
errx(1, "local file in list of remote sources: %s", |
errx(ERR_SYNTAX, "local file in list of " |
f->sources[i]); |
"remote sources: %s", f->sources[i]); |
} |
} |
} else { |
} else { |
if (f->mode != FARGS_RECEIVER) |
if (f->mode != FARGS_RECEIVER) |
errx(1, "sender mode for remote " |
errx(ERR_SYNTAX, "sender mode for remote " |
"daemon receivers not yet supported"); |
"daemon receivers not yet supported"); |
for (i = 0; i < f->sourcesz; i++) { |
for (i = 0; i < f->sourcesz; i++) { |
if (fargs_is_daemon(f->sources[i])) |
if (fargs_is_daemon(f->sources[i])) |
continue; |
continue; |
errx(1, "non-remote daemon file " |
errx(ERR_SYNTAX, "non-remote daemon file " |
"in list of remote daemon sources: " |
"in list of remote daemon sources: " |
"%s", f->sources[i]); |
"%s", f->sources[i]); |
} |
} |
|
|
*ccp = '\0'; |
*ccp = '\0'; |
if (strncmp(cp, f->host, len) || |
if (strncmp(cp, f->host, len) || |
(cp[len] != '/' && cp[len] != '\0')) |
(cp[len] != '/' && cp[len] != '\0')) |
errx(1, "different remote host: %s", |
errx(ERR_SYNTAX, "different remote host: %s", |
f->sources[i]); |
f->sources[i]); |
memmove(f->sources[i], |
memmove(f->sources[i], |
f->sources[i] + len + 8 + 1, |
f->sources[i] + len + 8 + 1, |
|
|
/* host::path */ |
/* host::path */ |
if (strncmp(cp, f->host, len) || |
if (strncmp(cp, f->host, len) || |
(cp[len] != ':' && cp[len] != '\0')) |
(cp[len] != ':' && cp[len] != '\0')) |
errx(1, "different remote host: %s", |
errx(ERR_SYNTAX, "different remote host: %s", |
f->sources[i]); |
f->sources[i]); |
memmove(f->sources[i], f->sources[i] + len + 2, |
memmove(f->sources[i], f->sources[i] + len + 2, |
j - len - 1); |
j - len - 1); |
|
|
/* host:path */ |
/* host:path */ |
if (strncmp(cp, f->host, len) || |
if (strncmp(cp, f->host, len) || |
(cp[len] != ':' && cp[len] != '\0')) |
(cp[len] != ':' && cp[len] != '\0')) |
errx(1, "different remote host: %s", |
errx(ERR_SYNTAX, "different remote host: %s", |
f->sources[i]); |
f->sources[i]); |
memmove(f->sources[i], |
memmove(f->sources[i], |
f->sources[i] + len + 1, j - len); |
f->sources[i] + len + 1, j - len); |
|
|
return f; |
return f; |
} |
} |
|
|
|
static struct opts opts; |
|
|
|
#define OP_ADDRESS 1000 |
|
#define OP_PORT 1001 |
|
#define OP_RSYNCPATH 1002 |
|
#define OP_TIMEOUT 1003 |
|
#define OP_VERSION 1004 |
|
#define OP_EXCLUDE 1005 |
|
#define OP_INCLUDE 1006 |
|
#define OP_EXCLUDE_FROM 1007 |
|
#define OP_INCLUDE_FROM 1008 |
|
#define OP_COMP_DEST 1009 |
|
#define OP_COPY_DEST 1010 |
|
#define OP_LINK_DEST 1011 |
|
#define OP_MAX_SIZE 1012 |
|
#define OP_MIN_SIZE 1013 |
|
|
|
const struct option lopts[] = { |
|
{ "address", required_argument, NULL, OP_ADDRESS }, |
|
{ "archive", no_argument, NULL, 'a' }, |
|
{ "compare-dest", required_argument, NULL, OP_COMP_DEST }, |
|
#if 0 |
|
{ "copy-dest", required_argument, NULL, OP_COPY_DEST }, |
|
{ "link-dest", required_argument, NULL, OP_LINK_DEST }, |
|
#endif |
|
{ "compress", no_argument, NULL, 'z' }, |
|
{ "del", no_argument, &opts.del, 1 }, |
|
{ "delete", no_argument, &opts.del, 1 }, |
|
{ "devices", no_argument, &opts.devices, 1 }, |
|
{ "no-devices", no_argument, &opts.devices, 0 }, |
|
{ "dry-run", no_argument, &opts.dry_run, 1 }, |
|
{ "exclude", required_argument, NULL, OP_EXCLUDE }, |
|
{ "exclude-from", required_argument, NULL, OP_EXCLUDE_FROM }, |
|
{ "group", no_argument, &opts.preserve_gids, 1 }, |
|
{ "no-group", no_argument, &opts.preserve_gids, 0 }, |
|
{ "help", no_argument, NULL, 'h' }, |
|
{ "include", required_argument, NULL, OP_INCLUDE }, |
|
{ "include-from", required_argument, NULL, OP_INCLUDE_FROM }, |
|
{ "links", no_argument, &opts.preserve_links, 1 }, |
|
{ "max-size", required_argument, NULL, OP_MAX_SIZE }, |
|
{ "min-size", required_argument, NULL, OP_MIN_SIZE }, |
|
{ "no-links", no_argument, &opts.preserve_links, 0 }, |
|
{ "no-motd", no_argument, &opts.no_motd, 1 }, |
|
{ "numeric-ids", no_argument, &opts.numeric_ids, 1 }, |
|
{ "owner", no_argument, &opts.preserve_uids, 1 }, |
|
{ "no-owner", no_argument, &opts.preserve_uids, 0 }, |
|
{ "perms", no_argument, &opts.preserve_perms, 1 }, |
|
{ "no-perms", no_argument, &opts.preserve_perms, 0 }, |
|
{ "port", required_argument, NULL, OP_PORT }, |
|
{ "recursive", no_argument, &opts.recursive, 1 }, |
|
{ "no-recursive", no_argument, &opts.recursive, 0 }, |
|
{ "rsh", required_argument, NULL, 'e' }, |
|
{ "rsync-path", required_argument, NULL, OP_RSYNCPATH }, |
|
{ "sender", no_argument, &opts.sender, 1 }, |
|
{ "server", no_argument, &opts.server, 1 }, |
|
{ "specials", no_argument, &opts.specials, 1 }, |
|
{ "no-specials", no_argument, &opts.specials, 0 }, |
|
{ "timeout", required_argument, NULL, OP_TIMEOUT }, |
|
{ "times", no_argument, &opts.preserve_times, 1 }, |
|
{ "no-times", no_argument, &opts.preserve_times, 0 }, |
|
{ "verbose", no_argument, &verbose, 1 }, |
|
{ "no-verbose", no_argument, &verbose, 0 }, |
|
{ "version", no_argument, NULL, OP_VERSION }, |
|
{ NULL, 0, NULL, 0 } |
|
}; |
|
|
int |
int |
main(int argc, char *argv[]) |
main(int argc, char *argv[]) |
{ |
{ |
struct opts opts; |
|
pid_t child; |
pid_t child; |
int fds[2], sd = -1, rc, c, st, i; |
int fds[2], sd = -1, rc, c, st, i, lidx; |
struct sess sess; |
size_t basedir_cnt = 0; |
|
struct sess sess; |
struct fargs *fargs; |
struct fargs *fargs; |
char **args; |
char **args; |
const char *errstr; |
const char *errstr; |
const struct option lopts[] = { |
|
{ "port", required_argument, NULL, 3 }, |
|
{ "rsh", required_argument, NULL, 'e' }, |
|
{ "rsync-path", required_argument, NULL, 1 }, |
|
{ "sender", no_argument, &opts.sender, 1 }, |
|
{ "server", no_argument, &opts.server, 1 }, |
|
{ "dry-run", no_argument, &opts.dry_run, 1 }, |
|
{ "version", no_argument, NULL, 2 }, |
|
{ "archive", no_argument, NULL, 'a' }, |
|
{ "help", no_argument, NULL, 'h' }, |
|
{ "compress", no_argument, NULL, 'z' }, |
|
{ "del", no_argument, &opts.del, 1 }, |
|
{ "delete", no_argument, &opts.del, 1 }, |
|
{ "devices", no_argument, &opts.devices, 1 }, |
|
{ "no-devices", no_argument, &opts.devices, 0 }, |
|
{ "group", no_argument, &opts.preserve_gids, 1 }, |
|
{ "no-group", no_argument, &opts.preserve_gids, 0 }, |
|
{ "links", no_argument, &opts.preserve_links, 1 }, |
|
{ "no-links", no_argument, &opts.preserve_links, 0 }, |
|
{ "owner", no_argument, &opts.preserve_uids, 1 }, |
|
{ "no-owner", no_argument, &opts.preserve_uids, 0 }, |
|
{ "perms", no_argument, &opts.preserve_perms, 1 }, |
|
{ "no-perms", no_argument, &opts.preserve_perms, 0 }, |
|
{ "numeric-ids", no_argument, &opts.numeric_ids, 1 }, |
|
{ "recursive", no_argument, &opts.recursive, 1 }, |
|
{ "no-recursive", no_argument, &opts.recursive, 0 }, |
|
{ "specials", no_argument, &opts.specials, 1 }, |
|
{ "no-specials", no_argument, &opts.specials, 0 }, |
|
{ "timeout", required_argument, NULL, 5 }, |
|
{ "times", no_argument, &opts.preserve_times, 1 }, |
|
{ "no-times", no_argument, &opts.preserve_times, 0 }, |
|
{ "verbose", no_argument, &verbose, 1 }, |
|
{ "no-verbose", no_argument, &verbose, 0 }, |
|
{ "address", required_argument, NULL, 4 }, |
|
{ "no-motd", no_argument, NULL, 6 }, |
|
{ NULL, 0, NULL, 0 }}; |
|
|
|
/* Global pledge. */ |
/* Global pledge. */ |
|
|
if (pledge("stdio unix rpath wpath cpath dpath inet fattr chown dns getpw proc exec unveil", |
if (pledge("stdio unix rpath wpath cpath dpath inet fattr chown dns getpw proc exec unveil", |
NULL) == -1) |
NULL) == -1) |
err(1, "pledge"); |
err(ERR_IPC, "pledge"); |
|
|
memset(&opts, 0, sizeof(struct opts)); |
opts.max_size = opts.min_size = -1; |
|
|
while ((c = getopt_long(argc, argv, "Dae:ghlnoprtvxz", lopts, NULL)) |
while ((c = getopt_long(argc, argv, "Dae:ghlnoprtvxz", lopts, &lidx)) |
!= -1) { |
!= -1) { |
switch (c) { |
switch (c) { |
case 'D': |
case 'D': |
|
|
case 0: |
case 0: |
/* Non-NULL flag values (e.g., --sender). */ |
/* Non-NULL flag values (e.g., --sender). */ |
break; |
break; |
case 1: |
case OP_ADDRESS: |
opts.rsync_path = optarg; |
opts.address = optarg; |
break; |
break; |
case 2: |
case OP_PORT: |
fprintf(stderr, "openrsync: protocol version %u\n", |
|
RSYNC_PROTOCOL); |
|
exit(0); |
|
case 3: |
|
opts.port = optarg; |
opts.port = optarg; |
break; |
break; |
case 4: |
case OP_RSYNCPATH: |
opts.address = optarg; |
opts.rsync_path = optarg; |
break; |
break; |
case 5: |
case OP_TIMEOUT: |
poll_timeout = strtonum(optarg, 0, 60*60, &errstr); |
poll_timeout = strtonum(optarg, 0, 60*60, &errstr); |
if (errstr != NULL) |
if (errstr != NULL) |
errx(1, "timeout is %s: %s", errstr, optarg); |
errx(ERR_SYNTAX, "timeout is %s: %s", |
|
errstr, optarg); |
break; |
break; |
case 6: |
case OP_EXCLUDE: |
opts.no_motd = 1; |
if (parse_rule(optarg, RULE_EXCLUDE) == -1) |
|
errx(ERR_SYNTAX, "syntax error in exclude: %s", |
|
optarg); |
break; |
break; |
|
case OP_INCLUDE: |
|
if (parse_rule(optarg, RULE_INCLUDE) == -1) |
|
errx(ERR_SYNTAX, "syntax error in include: %s", |
|
optarg); |
|
break; |
|
case OP_EXCLUDE_FROM: |
|
parse_file(optarg, RULE_EXCLUDE); |
|
break; |
|
case OP_INCLUDE_FROM: |
|
parse_file(optarg, RULE_INCLUDE); |
|
break; |
|
case OP_COMP_DEST: |
|
if (opts.alt_base_mode !=0 && |
|
opts.alt_base_mode != BASE_MODE_COMPARE) { |
|
errx(1, "option --%s conflicts with %s", |
|
lopts[lidx].name, |
|
alt_base_mode(opts.alt_base_mode)); |
|
} |
|
opts.alt_base_mode = BASE_MODE_COMPARE; |
|
#if 0 |
|
goto basedir; |
|
case OP_COPY_DEST: |
|
if (opts.alt_base_mode !=0 && |
|
opts.alt_base_mode != BASE_MODE_COPY) { |
|
errx(1, "option --%s conflicts with %s", |
|
lopts[lidx].name, |
|
alt_base_mode(opts.alt_base_mode)); |
|
} |
|
opts.alt_base_mode = BASE_MODE_COPY; |
|
goto basedir; |
|
case OP_LINK_DEST: |
|
if (opts.alt_base_mode !=0 && |
|
opts.alt_base_mode != BASE_MODE_LINK) { |
|
errx(1, "option --%s conflicts with %s", |
|
lopts[lidx].name, |
|
alt_base_mode(opts.alt_base_mode)); |
|
} |
|
opts.alt_base_mode = BASE_MODE_LINK; |
|
|
|
basedir: |
|
#endif |
|
if (basedir_cnt >= MAX_BASEDIR) |
|
errx(1, "too many --%s directories specified", |
|
lopts[lidx].name); |
|
opts.basedir[basedir_cnt++] = optarg; |
|
break; |
|
case OP_MAX_SIZE: |
|
if (scan_scaled(optarg, &opts.max_size) == -1) |
|
err(1, "bad max-size"); |
|
break; |
|
case OP_MIN_SIZE: |
|
if (scan_scaled(optarg, &opts.min_size) == -1) |
|
err(1, "bad min-size"); |
|
break; |
|
case OP_VERSION: |
|
fprintf(stderr, "openrsync: protocol version %u\n", |
|
RSYNC_PROTOCOL); |
|
exit(0); |
case 'h': |
case 'h': |
default: |
default: |
goto usage; |
goto usage; |
|
|
|
|
/* by default and for --timeout=0 disable poll_timeout */ |
/* by default and for --timeout=0 disable poll_timeout */ |
if (poll_timeout == 0) |
if (poll_timeout == 0) |
poll_timeout = -1; |
poll_timeout = -1; else |
else |
|
poll_timeout *= 1000; |
poll_timeout *= 1000; |
|
|
/* |
/* |
|
|
|
|
if (pledge("stdio unix rpath wpath cpath dpath fattr chown getpw proc exec unveil", |
if (pledge("stdio unix rpath wpath cpath dpath fattr chown getpw proc exec unveil", |
NULL) == -1) |
NULL) == -1) |
err(1, "pledge"); |
err(ERR_IPC, "pledge"); |
|
|
/* Create a bidirectional socket and start our child. */ |
/* Create a bidirectional socket and start our child. */ |
|
|
if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_NONBLOCK, 0, fds) == -1) |
if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_NONBLOCK, 0, fds) == -1) |
err(1, "socketpair"); |
err(ERR_IPC, "socketpair"); |
|
|
switch ((child = fork())) { |
switch ((child = fork())) { |
case -1: |
case -1: |
err(1, "fork"); |
err(ERR_IPC, "fork"); |
case 0: |
case 0: |
close(fds[0]); |
close(fds[0]); |
if (pledge("stdio exec", NULL) == -1) |
if (pledge("stdio exec", NULL) == -1) |
err(1, "pledge"); |
err(ERR_IPC, "pledge"); |
|
|
memset(&sess, 0, sizeof(struct sess)); |
memset(&sess, 0, sizeof(struct sess)); |
sess.opts = &opts; |
sess.opts = &opts; |
|
|
if ((args = fargs_cmdline(&sess, fargs, NULL)) == NULL) { |
args = fargs_cmdline(&sess, fargs, NULL); |
ERRX1("fargs_cmdline"); |
|
_exit(1); |
|
} |
|
|
|
for (i = 0; args[i] != NULL; i++) |
for (i = 0; args[i] != NULL; i++) |
LOG2("exec[%d] = %s", i, args[i]); |
LOG2("exec[%d] = %s", i, args[i]); |
|
|
/* Make sure the child's stdin is from the sender. */ |
/* Make sure the child's stdin is from the sender. */ |
if (dup2(fds[1], STDIN_FILENO) == -1) { |
if (dup2(fds[1], STDIN_FILENO) == -1) |
ERR("dup2"); |
err(ERR_IPC, "dup2"); |
_exit(1); |
if (dup2(fds[1], STDOUT_FILENO) == -1) |
} |
err(ERR_IPC, "dup2"); |
if (dup2(fds[1], STDOUT_FILENO) == -1) { |
|
ERR("dup2"); |
|
_exit(1); |
|
} |
|
execvp(args[0], args); |
execvp(args[0], args); |
_exit(1); |
_exit(ERR_IPC); |
/* NOTREACHED */ |
/* NOTREACHED */ |
default: |
default: |
close(fds[1]); |
close(fds[1]); |
|
|
close(fds[0]); |
close(fds[0]); |
|
|
if (waitpid(child, &st, 0) == -1) |
if (waitpid(child, &st, 0) == -1) |
err(1, "waitpid"); |
err(ERR_WAITPID, "waitpid"); |
|
|
/* |
/* |
* If we don't already have an error (rc == 0), then inherit the |
* If we don't already have an error (rc == 0), then inherit the |
|
|
* If it hasn't exited, it overrides our return value. |
* If it hasn't exited, it overrides our return value. |
*/ |
*/ |
|
|
if (WIFEXITED(st) && rc == 0) |
if (rc == 0) { |
rc = WEXITSTATUS(st); |
if (WIFEXITED(st)) |
else if (!WIFEXITED(st)) |
rc = WEXITSTATUS(st); |
rc = 1; |
else if (WIFSIGNALED(st)) |
|
rc = ERR_TERMIMATED; |
|
else |
|
rc = ERR_WAITPID; |
|
} |
|
|
exit(rc); |
exit(rc); |
usage: |
usage: |
fprintf(stderr, "usage: %s" |
fprintf(stderr, "usage: %s" |
" [-aDglnoprtvx] [-e program] [--address=sourceaddr] [--del]\n" |
" [-aDglnoprtvx] [-e program] [--address=sourceaddr]\n" |
"\t[--no-motd] [--numeric-ids] [--port=portnumber] " |
"\t[--compare-dest=dir] [--del] [--exclude] [--exclude-from=file]\n" |
"[--rsync-path=program]\n\t[--timeout=seconds] [--version] " |
"\t[--include] [--include-from=file] [--no-motd] [--numeric-ids]\n" |
"source ... directory\n", |
"\t[--port=portnumber] [--rsync-path=program] [--timeout=seconds]\n" |
|
"\t[--version] source ... directory\n", |
getprogname()); |
getprogname()); |
exit(1); |
exit(ERR_SYNTAX); |
} |
} |