version 1.28, 2019/02/18 21:34:54 |
version 1.29, 2019/02/18 21:55:27 |
|
|
/* Allocations. */ |
/* Allocations. */ |
|
|
if ((f = calloc(1, sizeof(struct fargs))) == NULL) |
if ((f = calloc(1, sizeof(struct fargs))) == NULL) |
err(EXIT_FAILURE, "calloc"); |
err(1, "calloc"); |
|
|
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(EXIT_FAILURE, "calloc"); |
err(1, "calloc"); |
|
|
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(EXIT_FAILURE, "strdup"); |
err(1, "strdup"); |
|
|
if ((f->sink = strdup(argv[i])) == NULL) |
if ((f->sink = strdup(argv[i])) == NULL) |
err(EXIT_FAILURE, "strdup"); |
err(1, "strdup"); |
|
|
/* |
/* |
* 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(EXIT_FAILURE, "strdup"); |
err(1, "strdup"); |
} |
} |
|
|
if (fargs_is_remote(f->sources[0])) { |
if (fargs_is_remote(f->sources[0])) { |
if (f->host != NULL) |
if (f->host != NULL) |
errx(EXIT_FAILURE, "both source and " |
errx(1, "both source and " |
"destination cannot be remote files"); |
"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(EXIT_FAILURE, "strdup"); |
err(1, "strdup"); |
} |
} |
|
|
if (f->host != NULL) { |
if (f->host != NULL) { |
if (strncasecmp(f->host, "rsync://", 8) == 0) { |
if (strncasecmp(f->host, "rsync://", 8) == 0) { |
/* rsync://host[:port]/module[/path] */ |
/* rsync://host/module[/path] */ |
f->remote = 1; |
f->remote = 1; |
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(EXIT_FAILURE, "rsync protocol " |
errx(1, "rsync protocol " |
"requires a module name"); |
"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) |
*cp = '\0'; |
*cp = '\0'; |
if ((cp = strchr(f->host, ':')) != NULL) { |
if ((cp = strchr(f->host, ':'))) { |
/* host:port --> extract port */ |
/* host:port --> extract port */ |
*cp++ = '\0'; |
*cp++ = '\0'; |
opts->port = cp; |
opts->port = cp; |
|
|
} |
} |
} |
} |
if ((len = strlen(f->host)) == 0) |
if ((len = strlen(f->host)) == 0) |
errx(EXIT_FAILURE, "empty remote host"); |
errx(1, "empty remote host"); |
if (f->remote && strlen(f->module) == 0) |
if (f->remote && strlen(f->module) == 0) |
errx(EXIT_FAILURE, "empty remote module"); |
errx(1, "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(EXIT_FAILURE, "remote file in " |
errx(1, "remote file in " |
"list of local sources: %s", |
"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(EXIT_FAILURE, "remote " |
errx(1, "remote " |
"daemon in list of " |
"daemon in list of " |
"remote sources: %s", |
"remote sources: %s", |
f->sources[i]); |
f->sources[i]); |
errx(EXIT_FAILURE, "local file in " |
errx(1, "local file in " |
"list of remote sources: %s", |
"list of remote sources: %s", |
f->sources[i]); |
f->sources[i]); |
} |
} |
} else { |
} else { |
if (f->mode != FARGS_RECEIVER) |
if (f->mode != FARGS_RECEIVER) |
errx(EXIT_FAILURE, "sender mode for remote " |
errx(1, "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(EXIT_FAILURE, "non-remote daemon file " |
errx(1, "non-remote daemon file " |
"in list of remote daemon sources: " |
"in list of remote daemon sources: " |
"%s", f->sources[i]); |
"%s", f->sources[i]); |
} |
} |
|
|
strncasecmp(cp, "rsync://", 8) == 0) { |
strncasecmp(cp, "rsync://", 8) == 0) { |
/* rsync://path */ |
/* rsync://path */ |
cp += 8; |
cp += 8; |
|
if ((ccp = strchr(cp, ':'))) /* skip :port */ |
/* |
|
* FIXME: broken. |
|
* URIs can allow colons too. |
|
* Fix this after merge. |
|
*/ |
|
|
|
if ((ccp = strchr(cp, ':')) != NULL) /* skip :port */ |
|
*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(EXIT_FAILURE, "different remote " |
errx(1, "different remote " |
"host: %s", f->sources[i]); |
"host: %s", 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(EXIT_FAILURE, "different remote " |
errx(1, "different remote " |
"host: %s", f->sources[i]); |
"host: %s", 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(EXIT_FAILURE, "different remote " |
errx(1, "different remote " |
"host: %s", f->sources[i]); |
"host: %s", 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); |
|
|
{ |
{ |
struct opts opts; |
struct opts opts; |
pid_t child; |
pid_t child; |
int fds[2], c, st; |
int fds[2], rc = 0, c, st; |
struct fargs *fargs; |
struct fargs *fargs; |
struct option lopts[] = { |
struct option lopts[] = { |
{ "port", required_argument, NULL, 3 }, |
{ "port", required_argument, NULL, 3 }, |
|
|
|
|
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(EXIT_FAILURE, "pledge"); |
err(1, "pledge"); |
|
|
memset(&opts, 0, sizeof(struct opts)); |
memset(&opts, 0, sizeof(struct opts)); |
|
|
|
|
break; |
break; |
case 'e': |
case 'e': |
opts.ssh_prog = optarg; |
opts.ssh_prog = optarg; |
/* Ignore. */ |
|
break; |
break; |
case 'g': |
case 'g': |
opts.preserve_gids = 1; |
opts.preserve_gids = 1; |
|
|
goto usage; |
goto usage; |
|
|
if (opts.port == NULL) |
if (opts.port == NULL) |
opts.port = RSYNC_SERVICE; |
opts.port = "rsync"; |
|
|
/* |
/* |
* This is what happens when we're started with the "hidden" |
* This is what happens when we're started with the "hidden" |
|
|
|
|
if (opts.server) { |
if (opts.server) { |
if (pledge("stdio unix rpath wpath cpath dpath fattr chown getpw unveil", NULL) == -1) |
if (pledge("stdio unix rpath wpath cpath dpath fattr chown getpw unveil", NULL) == -1) |
err(EXIT_FAILURE, "pledge"); |
err(1, "pledge"); |
c = rsync_server(&opts, (size_t)argc, argv); |
return rsync_server(&opts, (size_t)argc, argv); |
return c ? EXIT_SUCCESS : EXIT_FAILURE; |
|
} |
} |
|
|
/* |
/* |
|
|
assert(fargs->mode == FARGS_RECEIVER); |
assert(fargs->mode == FARGS_RECEIVER); |
if (pledge("stdio unix rpath wpath cpath dpath inet fattr chown dns getpw unveil", |
if (pledge("stdio unix rpath wpath cpath dpath inet fattr chown dns getpw unveil", |
NULL) == -1) |
NULL) == -1) |
err(EXIT_FAILURE, "pledge"); |
err(1, "pledge"); |
c = rsync_socket(&opts, fargs); |
rc = rsync_socket(&opts, fargs); |
fargs_free(fargs); |
fargs_free(fargs); |
return c ? EXIT_SUCCESS : EXIT_FAILURE; |
return rc; |
} |
} |
|
|
/* Drop the dns/inet possibility. */ |
/* Drop the dns/inet possibility. */ |
|
|
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(EXIT_FAILURE, "pledge"); |
err(1, "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(EXIT_FAILURE, "socketpair"); |
err(1, "socketpair"); |
|
|
if ((child = fork()) == -1) { |
if ((child = fork()) == -1) { |
close(fds[0]); |
close(fds[0]); |
close(fds[1]); |
close(fds[1]); |
err(EXIT_FAILURE, "fork"); |
err(1, "fork"); |
} |
} |
|
|
/* Drop the fork possibility. */ |
/* Drop the fork possibility. */ |
|
|
if (pledge("stdio unix rpath wpath cpath dpath fattr chown getpw exec unveil", NULL) == -1) |
if (pledge("stdio unix rpath wpath cpath dpath fattr chown getpw exec unveil", NULL) == -1) |
err(EXIT_FAILURE, "pledge"); |
err(1, "pledge"); |
|
|
if (child == 0) { |
if (child == 0) { |
close(fds[0]); |
close(fds[0]); |
fds[0] = -1; |
fds[0] = -1; |
if (pledge("stdio exec", NULL) == -1) |
if (pledge("stdio exec", NULL) == -1) |
err(EXIT_FAILURE, "pledge"); |
err(1, "pledge"); |
rsync_child(&opts, fds[1], fargs); |
rsync_child(&opts, fds[1], fargs); |
/* NOTREACHED */ |
/* NOTREACHED */ |
} |
} |
|
|
close(fds[1]); |
close(fds[1]); |
fds[1] = -1; |
fds[1] = -1; |
if (pledge("stdio unix rpath wpath cpath dpath fattr chown getpw unveil", NULL) == -1) |
if (pledge("stdio unix rpath wpath cpath dpath fattr chown getpw unveil", NULL) == -1) |
err(EXIT_FAILURE, "pledge"); |
err(1, "pledge"); |
c = rsync_client(&opts, fds[0], fargs); |
rc = rsync_client(&opts, fds[0], fargs); |
fargs_free(fargs); |
fargs_free(fargs); |
|
|
/* |
/* |
|
|
* So close the connection here so that they don't hang. |
* So close the connection here so that they don't hang. |
*/ |
*/ |
|
|
if (!c) { |
if (!rc) { |
close(fds[0]); |
close(fds[0]); |
fds[0] = -1; |
fds[0] = -1; |
} |
} |
|
|
if (waitpid(child, &st, 0) == -1) |
if (waitpid(child, &st, 0) == -1) |
err(EXIT_FAILURE, "waitpid"); |
err(1, "waitpid"); |
if (!(WIFEXITED(st) && WEXITSTATUS(st) == EXIT_SUCCESS)) |
if (!(WIFEXITED(st) && WEXITSTATUS(st) == 0)) |
c = 0; |
rc = 0; |
|
|
if (fds[0] != -1) |
if (fds[0] != -1) |
close(fds[0]); |
close(fds[0]); |
return c ? EXIT_SUCCESS : EXIT_FAILURE; |
return rc; |
usage: |
usage: |
fprintf(stderr, "usage: %s [-Daghlnoprtv] " |
fprintf(stderr, "usage: %s [-Daglnoprtv] " |
"[-e ssh-prog] [--delete] " |
"[-e ssh-prog] [--delete] [--rsync-path=prog] src ... dst\n", |
"[--port=port] [--rsync-path=prog] src ... dst\n", |
|
getprogname()); |
getprogname()); |
return EXIT_FAILURE; |
return 1; |
} |
} |