Annotation of src/usr.bin/rsync/main.c, Revision 1.53.2.1
1.53.2.1! benno 1: /* $Id: main.c,v 1.53 2021/03/31 19:45:16 job Exp $ */
1.1 benno 2: /*
3: * Copyright (c) 2019 Kristaps Dzonsons <kristaps@bsd.lv>
4: *
5: * Permission to use, copy, modify, and distribute this software for any
6: * purpose with or without fee is hereby granted, provided that the above
7: * copyright notice and this permission notice appear in all copies.
8: *
9: * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10: * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11: * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12: * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13: * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14: * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15: * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16: */
17: #include <sys/stat.h>
18: #include <sys/socket.h>
19: #include <sys/wait.h>
20:
21: #include <assert.h>
22: #include <err.h>
23: #include <getopt.h>
24: #include <stdint.h>
25: #include <stdio.h>
26: #include <stdlib.h>
27: #include <string.h>
28: #include <unistd.h>
1.53.2.1! benno 29: #include <util.h>
1.1 benno 30:
31: #include "extern.h"
32:
1.45 benno 33: int verbose;
1.50 claudio 34: int poll_timeout;
1.45 benno 35:
1.1 benno 36: /*
37: * A remote host is has a colon before the first path separator.
38: * This works for rsh remote hosts (host:/foo/bar), implicit rsync
39: * remote hosts (host::/foo/bar), and explicit (rsync://host/foo).
40: * Return zero if local, non-zero if remote.
41: */
42: static int
43: fargs_is_remote(const char *v)
44: {
45: size_t pos;
46:
47: pos = strcspn(v, ":/");
1.6 deraadt 48: return v[pos] == ':';
1.1 benno 49: }
50:
51: /*
52: * Test whether a remote host is specifically an rsync daemon.
53: * Return zero if not, non-zero if so.
54: */
55: static int
56: fargs_is_daemon(const char *v)
57: {
58: size_t pos;
59:
1.6 deraadt 60: if (strncasecmp(v, "rsync://", 8) == 0)
1.1 benno 61: return 1;
62:
63: pos = strcspn(v, ":/");
1.6 deraadt 64: return v[pos] == ':' && v[pos + 1] == ':';
1.1 benno 65: }
66:
67: /*
68: * Take the command-line filenames (e.g., rsync foo/ bar/ baz/) and
69: * determine our operating mode.
70: * For example, if the first argument is a remote file, this means that
71: * we're going to transfer from the remote to the local.
72: * We also make sure that the arguments are consistent, that is, if
73: * we're going to transfer from the local to the remote, that no
74: * filenames for the local transfer indicate remote hosts.
75: * Always returns the parsed and sanitised options.
76: */
77: static struct fargs *
1.26 deraadt 78: fargs_parse(size_t argc, char *argv[], struct opts *opts)
1.1 benno 79: {
80: struct fargs *f = NULL;
1.26 deraadt 81: char *cp, *ccp;
1.1 benno 82: size_t i, j, len = 0;
83:
84: /* Allocations. */
85:
1.6 deraadt 86: if ((f = calloc(1, sizeof(struct fargs))) == NULL)
1.53.2.1! benno 87: err(ERR_NOMEM, NULL);
1.1 benno 88:
89: f->sourcesz = argc - 1;
1.6 deraadt 90: if ((f->sources = calloc(f->sourcesz, sizeof(char *))) == NULL)
1.53.2.1! benno 91: err(ERR_NOMEM, NULL);
1.1 benno 92:
93: for (i = 0; i < argc - 1; i++)
1.6 deraadt 94: if ((f->sources[i] = strdup(argv[i])) == NULL)
1.53.2.1! benno 95: err(ERR_NOMEM, NULL);
1.1 benno 96:
1.6 deraadt 97: if ((f->sink = strdup(argv[i])) == NULL)
1.53.2.1! benno 98: err(ERR_NOMEM, NULL);
1.1 benno 99:
100: /*
101: * Test files for its locality.
102: * If the last is a remote host, then we're sending from the
103: * local to the remote host ("sender" mode).
104: * If the first, remote to local ("receiver" mode).
105: * If neither, a local transfer in sender style.
106: */
107:
108: f->mode = FARGS_SENDER;
109:
110: if (fargs_is_remote(f->sink)) {
111: f->mode = FARGS_SENDER;
1.6 deraadt 112: if ((f->host = strdup(f->sink)) == NULL)
1.53.2.1! benno 113: err(ERR_NOMEM, NULL);
1.1 benno 114: }
115:
116: if (fargs_is_remote(f->sources[0])) {
1.6 deraadt 117: if (f->host != NULL)
1.53.2.1! benno 118: errx(ERR_SYNTAX, "both source and destination "
! 119: "cannot be remote files");
1.1 benno 120: f->mode = FARGS_RECEIVER;
1.6 deraadt 121: if ((f->host = strdup(f->sources[0])) == NULL)
1.53.2.1! benno 122: err(ERR_NOMEM, NULL);
1.1 benno 123: }
124:
1.6 deraadt 125: if (f->host != NULL) {
126: if (strncasecmp(f->host, "rsync://", 8) == 0) {
1.30 benno 127: /* rsync://host[:port]/module[/path] */
1.1 benno 128: f->remote = 1;
129: len = strlen(f->host) - 8 + 1;
130: memmove(f->host, f->host + 8, len);
1.6 deraadt 131: if ((cp = strchr(f->host, '/')) == NULL)
1.53.2.1! benno 132: errx(ERR_SYNTAX,
! 133: "rsync protocol requires a module name");
1.1 benno 134: *cp++ = '\0';
135: f->module = cp;
1.6 deraadt 136: if ((cp = strchr(f->module, '/')) != NULL)
1.1 benno 137: *cp = '\0';
1.52 claudio 138: if ((cp = strchr(f->host, ':')) != NULL) {
1.26 deraadt 139: /* host:port --> extract port */
140: *cp++ = '\0';
141: opts->port = cp;
142: }
1.1 benno 143: } else {
144: /* host:[/path] */
145: cp = strchr(f->host, ':');
1.6 deraadt 146: assert(cp != NULL);
1.1 benno 147: *cp++ = '\0';
1.6 deraadt 148: if (*cp == ':') {
1.1 benno 149: /* host::module[/path] */
150: f->remote = 1;
151: f->module = ++cp;
152: cp = strchr(f->module, '/');
1.6 deraadt 153: if (cp != NULL)
1.1 benno 154: *cp = '\0';
155: }
156: }
1.6 deraadt 157: if ((len = strlen(f->host)) == 0)
1.53.2.1! benno 158: errx(ERR_SYNTAX, "empty remote host");
1.6 deraadt 159: if (f->remote && strlen(f->module) == 0)
1.53.2.1! benno 160: errx(ERR_SYNTAX, "empty remote module");
1.1 benno 161: }
162:
163: /* Make sure we have the same "hostspec" for all files. */
164:
1.4 deraadt 165: if (!f->remote) {
1.6 deraadt 166: if (f->mode == FARGS_SENDER)
1.1 benno 167: for (i = 0; i < f->sourcesz; i++) {
1.4 deraadt 168: if (!fargs_is_remote(f->sources[i]))
1.1 benno 169: continue;
1.53.2.1! benno 170: errx(ERR_SYNTAX,
1.35 deraadt 171: "remote file in list of local sources: %s",
172: f->sources[i]);
1.1 benno 173: }
1.6 deraadt 174: if (f->mode == FARGS_RECEIVER)
1.1 benno 175: for (i = 0; i < f->sourcesz; i++) {
176: if (fargs_is_remote(f->sources[i]) &&
1.4 deraadt 177: !fargs_is_daemon(f->sources[i]))
1.1 benno 178: continue;
179: if (fargs_is_daemon(f->sources[i]))
1.53.2.1! benno 180: errx(ERR_SYNTAX,
! 181: "remote daemon in list of remote "
! 182: "sources: %s", f->sources[i]);
! 183: errx(ERR_SYNTAX, "local file in list of "
! 184: "remote sources: %s", f->sources[i]);
1.1 benno 185: }
186: } else {
1.6 deraadt 187: if (f->mode != FARGS_RECEIVER)
1.53.2.1! benno 188: errx(ERR_SYNTAX, "sender mode for remote "
1.1 benno 189: "daemon receivers not yet supported");
190: for (i = 0; i < f->sourcesz; i++) {
191: if (fargs_is_daemon(f->sources[i]))
192: continue;
1.53.2.1! benno 193: errx(ERR_SYNTAX, "non-remote daemon file "
1.1 benno 194: "in list of remote daemon sources: "
195: "%s", f->sources[i]);
196: }
197: }
198:
199: /*
200: * If we're not remote and a sender, strip our hostname.
201: * Then exit if we're a sender or a local connection.
202: */
203:
1.4 deraadt 204: if (!f->remote) {
1.6 deraadt 205: if (f->host == NULL)
1.1 benno 206: return f;
1.6 deraadt 207: if (f->mode == FARGS_SENDER) {
208: assert(f->host != NULL);
1.1 benno 209: assert(len > 0);
210: j = strlen(f->sink);
211: memmove(f->sink, f->sink + len + 1, j - len);
212: return f;
1.6 deraadt 213: } else if (f->mode != FARGS_RECEIVER)
1.1 benno 214: return f;
215: }
216:
217: /*
218: * Now strip the hostnames from the remote host.
219: * rsync://host/module/path -> module/path
220: * host::module/path -> module/path
221: * host:path -> path
222: * Also make sure that the remote hosts are the same.
223: */
224:
1.6 deraadt 225: assert(f->host != NULL);
1.1 benno 226: assert(len > 0);
227:
228: for (i = 0; i < f->sourcesz; i++) {
229: cp = f->sources[i];
230: j = strlen(cp);
231: if (f->remote &&
1.6 deraadt 232: strncasecmp(cp, "rsync://", 8) == 0) {
1.1 benno 233: /* rsync://path */
234: cp += 8;
1.29 benno 235: if ((ccp = strchr(cp, ':'))) /* skip :port */
1.26 deraadt 236: *ccp = '\0';
1.1 benno 237: if (strncmp(cp, f->host, len) ||
1.11 benno 238: (cp[len] != '/' && cp[len] != '\0'))
1.53.2.1! benno 239: errx(ERR_SYNTAX, "different remote host: %s",
1.35 deraadt 240: f->sources[i]);
1.1 benno 241: memmove(f->sources[i],
242: f->sources[i] + len + 8 + 1,
243: j - len - 8);
1.6 deraadt 244: } else if (f->remote && strncmp(cp, "::", 2) == 0) {
1.1 benno 245: /* ::path */
246: memmove(f->sources[i],
247: f->sources[i] + 2, j - 1);
248: } else if (f->remote) {
249: /* host::path */
250: if (strncmp(cp, f->host, len) ||
1.6 deraadt 251: (cp[len] != ':' && cp[len] != '\0'))
1.53.2.1! benno 252: errx(ERR_SYNTAX, "different remote host: %s",
1.35 deraadt 253: f->sources[i]);
1.11 benno 254: memmove(f->sources[i], f->sources[i] + len + 2,
255: j - len - 1);
1.6 deraadt 256: } else if (cp[0] == ':') {
1.1 benno 257: /* :path */
258: memmove(f->sources[i], f->sources[i] + 1, j);
259: } else {
260: /* host:path */
261: if (strncmp(cp, f->host, len) ||
1.6 deraadt 262: (cp[len] != ':' && cp[len] != '\0'))
1.53.2.1! benno 263: errx(ERR_SYNTAX, "different remote host: %s",
1.35 deraadt 264: f->sources[i]);
1.1 benno 265: memmove(f->sources[i],
266: f->sources[i] + len + 1, j - len);
267: }
268: }
269:
270: return f;
271: }
272:
1.53.2.1! benno 273: static struct opts opts;
! 274:
! 275: #define OP_ADDRESS 1000
! 276: #define OP_PORT 1001
! 277: #define OP_RSYNCPATH 1002
! 278: #define OP_TIMEOUT 1003
! 279: #define OP_VERSION 1004
! 280: #define OP_EXCLUDE 1005
! 281: #define OP_INCLUDE 1006
! 282: #define OP_EXCLUDE_FROM 1007
! 283: #define OP_INCLUDE_FROM 1008
! 284: #define OP_COMP_DEST 1009
! 285: #define OP_COPY_DEST 1010
! 286: #define OP_LINK_DEST 1011
! 287: #define OP_MAX_SIZE 1012
! 288: #define OP_MIN_SIZE 1013
! 289:
! 290: const struct option lopts[] = {
! 291: { "address", required_argument, NULL, OP_ADDRESS },
! 292: { "archive", no_argument, NULL, 'a' },
! 293: { "compare-dest", required_argument, NULL, OP_COMP_DEST },
! 294: #if 0
! 295: { "copy-dest", required_argument, NULL, OP_COPY_DEST },
! 296: { "link-dest", required_argument, NULL, OP_LINK_DEST },
! 297: #endif
! 298: { "compress", no_argument, NULL, 'z' },
! 299: { "del", no_argument, &opts.del, 1 },
! 300: { "delete", no_argument, &opts.del, 1 },
! 301: { "devices", no_argument, &opts.devices, 1 },
! 302: { "no-devices", no_argument, &opts.devices, 0 },
! 303: { "dry-run", no_argument, &opts.dry_run, 1 },
! 304: { "exclude", required_argument, NULL, OP_EXCLUDE },
! 305: { "exclude-from", required_argument, NULL, OP_EXCLUDE_FROM },
! 306: { "group", no_argument, &opts.preserve_gids, 1 },
! 307: { "no-group", no_argument, &opts.preserve_gids, 0 },
! 308: { "help", no_argument, NULL, 'h' },
! 309: { "include", required_argument, NULL, OP_INCLUDE },
! 310: { "include-from", required_argument, NULL, OP_INCLUDE_FROM },
! 311: { "links", no_argument, &opts.preserve_links, 1 },
! 312: { "max-size", required_argument, NULL, OP_MAX_SIZE },
! 313: { "min-size", required_argument, NULL, OP_MIN_SIZE },
! 314: { "no-links", no_argument, &opts.preserve_links, 0 },
! 315: { "no-motd", no_argument, &opts.no_motd, 1 },
! 316: { "numeric-ids", no_argument, &opts.numeric_ids, 1 },
! 317: { "owner", no_argument, &opts.preserve_uids, 1 },
! 318: { "no-owner", no_argument, &opts.preserve_uids, 0 },
! 319: { "perms", no_argument, &opts.preserve_perms, 1 },
! 320: { "no-perms", no_argument, &opts.preserve_perms, 0 },
! 321: { "port", required_argument, NULL, OP_PORT },
! 322: { "recursive", no_argument, &opts.recursive, 1 },
! 323: { "no-recursive", no_argument, &opts.recursive, 0 },
! 324: { "rsh", required_argument, NULL, 'e' },
! 325: { "rsync-path", required_argument, NULL, OP_RSYNCPATH },
! 326: { "sender", no_argument, &opts.sender, 1 },
! 327: { "server", no_argument, &opts.server, 1 },
! 328: { "specials", no_argument, &opts.specials, 1 },
! 329: { "no-specials", no_argument, &opts.specials, 0 },
! 330: { "timeout", required_argument, NULL, OP_TIMEOUT },
! 331: { "times", no_argument, &opts.preserve_times, 1 },
! 332: { "no-times", no_argument, &opts.preserve_times, 0 },
! 333: { "verbose", no_argument, &verbose, 1 },
! 334: { "no-verbose", no_argument, &verbose, 0 },
! 335: { "version", no_argument, NULL, OP_VERSION },
! 336: { NULL, 0, NULL, 0 }
! 337: };
! 338:
1.1 benno 339: int
340: main(int argc, char *argv[])
341: {
1.2 benno 342: pid_t child;
1.53.2.1! benno 343: int fds[2], sd = -1, rc, c, st, i, lidx;
! 344: size_t basedir_cnt = 0;
! 345: struct sess sess;
1.1 benno 346: struct fargs *fargs;
1.32 deraadt 347: char **args;
1.53.2.1! benno 348: const char *errstr;
1.1 benno 349:
350: /* Global pledge. */
351:
1.19 florian 352: if (pledge("stdio unix rpath wpath cpath dpath inet fattr chown dns getpw proc exec unveil",
1.6 deraadt 353: NULL) == -1)
1.53.2.1! benno 354: err(ERR_IPC, "pledge");
1.1 benno 355:
1.53.2.1! benno 356: opts.max_size = opts.min_size = -1;
1.1 benno 357:
1.53.2.1! benno 358: while ((c = getopt_long(argc, argv, "Dae:ghlnoprtvxz", lopts, &lidx))
1.30 benno 359: != -1) {
1.1 benno 360: switch (c) {
1.19 florian 361: case 'D':
362: opts.devices = 1;
363: opts.specials = 1;
364: break;
365: case 'a':
366: opts.recursive = 1;
367: opts.preserve_links = 1;
368: opts.preserve_perms = 1;
369: opts.preserve_times = 1;
370: opts.preserve_gids = 1;
371: opts.preserve_uids = 1;
372: opts.devices = 1;
373: opts.specials = 1;
374: break;
1.1 benno 375: case 'e':
1.9 deraadt 376: opts.ssh_prog = optarg;
1.1 benno 377: break;
1.10 benno 378: case 'g':
379: opts.preserve_gids = 1;
380: break;
1.1 benno 381: case 'l':
382: opts.preserve_links = 1;
383: break;
384: case 'n':
385: opts.dry_run = 1;
386: break;
1.13 florian 387: case 'o':
388: opts.preserve_uids = 1;
389: break;
1.1 benno 390: case 'p':
391: opts.preserve_perms = 1;
392: break;
393: case 'r':
394: opts.recursive = 1;
395: break;
396: case 't':
397: opts.preserve_times = 1;
398: break;
399: case 'v':
1.45 benno 400: verbose++;
1.1 benno 401: break;
1.44 bket 402: case 'x':
403: opts.one_file_system++;
404: break;
1.36 deraadt 405: case 'z':
406: fprintf(stderr, "%s: -z not supported yet\n", getprogname());
407: break;
1.1 benno 408: case 0:
409: /* Non-NULL flag values (e.g., --sender). */
410: break;
1.53.2.1! benno 411: case OP_ADDRESS:
! 412: opts.address = optarg;
1.1 benno 413: break;
1.53.2.1! benno 414: case OP_PORT:
1.26 deraadt 415: opts.port = optarg;
416: break;
1.53.2.1! benno 417: case OP_RSYNCPATH:
! 418: opts.rsync_path = optarg;
1.48 claudio 419: break;
1.53.2.1! benno 420: case OP_TIMEOUT:
1.50 claudio 421: poll_timeout = strtonum(optarg, 0, 60*60, &errstr);
422: if (errstr != NULL)
1.53.2.1! benno 423: errx(ERR_SYNTAX, "timeout is %s: %s",
! 424: errstr, optarg);
1.50 claudio 425: break;
1.53.2.1! benno 426: case OP_EXCLUDE:
! 427: if (parse_rule(optarg, RULE_EXCLUDE) == -1)
! 428: errx(ERR_SYNTAX, "syntax error in exclude: %s",
! 429: optarg);
! 430: break;
! 431: case OP_INCLUDE:
! 432: if (parse_rule(optarg, RULE_INCLUDE) == -1)
! 433: errx(ERR_SYNTAX, "syntax error in include: %s",
! 434: optarg);
! 435: break;
! 436: case OP_EXCLUDE_FROM:
! 437: parse_file(optarg, RULE_EXCLUDE);
! 438: break;
! 439: case OP_INCLUDE_FROM:
! 440: parse_file(optarg, RULE_INCLUDE);
! 441: break;
! 442: case OP_COMP_DEST:
! 443: if (opts.alt_base_mode !=0 &&
! 444: opts.alt_base_mode != BASE_MODE_COMPARE) {
! 445: errx(1, "option --%s conflicts with %s",
! 446: lopts[lidx].name,
! 447: alt_base_mode(opts.alt_base_mode));
! 448: }
! 449: opts.alt_base_mode = BASE_MODE_COMPARE;
! 450: #if 0
! 451: goto basedir;
! 452: case OP_COPY_DEST:
! 453: if (opts.alt_base_mode !=0 &&
! 454: opts.alt_base_mode != BASE_MODE_COPY) {
! 455: errx(1, "option --%s conflicts with %s",
! 456: lopts[lidx].name,
! 457: alt_base_mode(opts.alt_base_mode));
! 458: }
! 459: opts.alt_base_mode = BASE_MODE_COPY;
! 460: goto basedir;
! 461: case OP_LINK_DEST:
! 462: if (opts.alt_base_mode !=0 &&
! 463: opts.alt_base_mode != BASE_MODE_LINK) {
! 464: errx(1, "option --%s conflicts with %s",
! 465: lopts[lidx].name,
! 466: alt_base_mode(opts.alt_base_mode));
! 467: }
! 468: opts.alt_base_mode = BASE_MODE_LINK;
! 469:
! 470: basedir:
! 471: #endif
! 472: if (basedir_cnt >= MAX_BASEDIR)
! 473: errx(1, "too many --%s directories specified",
! 474: lopts[lidx].name);
! 475: opts.basedir[basedir_cnt++] = optarg;
! 476: break;
! 477: case OP_MAX_SIZE:
! 478: if (scan_scaled(optarg, &opts.max_size) == -1)
! 479: err(1, "bad max-size");
! 480: break;
! 481: case OP_MIN_SIZE:
! 482: if (scan_scaled(optarg, &opts.min_size) == -1)
! 483: err(1, "bad min-size");
1.53 job 484: break;
1.53.2.1! benno 485: case OP_VERSION:
! 486: fprintf(stderr, "openrsync: protocol version %u\n",
! 487: RSYNC_PROTOCOL);
! 488: exit(0);
1.21 deraadt 489: case 'h':
1.1 benno 490: default:
491: goto usage;
492: }
493: }
494:
495: argc -= optind;
496: argv += optind;
497:
498: /* FIXME: reference implementation rsync accepts this. */
499:
500: if (argc < 2)
501: goto usage;
502:
1.26 deraadt 503: if (opts.port == NULL)
1.29 benno 504: opts.port = "rsync";
1.26 deraadt 505:
1.51 claudio 506: /* by default and for --timeout=0 disable poll_timeout */
507: if (poll_timeout == 0)
1.53.2.1! benno 508: poll_timeout = -1; else
1.51 claudio 509: poll_timeout *= 1000;
510:
1.1 benno 511: /*
512: * This is what happens when we're started with the "hidden"
513: * --server option, which is invoked for the rsync on the remote
514: * host by the parent.
515: */
516:
1.32 deraadt 517: if (opts.server)
518: exit(rsync_server(&opts, (size_t)argc, argv));
1.50 claudio 519:
1.1 benno 520: /*
521: * Now we know that we're the client on the local machine
522: * invoking rsync(1).
523: * At this point, we need to start the client and server
524: * initiation logic.
525: * The client is what we continue running on this host; the
526: * server is what we'll use to connect to the remote and
527: * invoke rsync with the --server option.
528: */
529:
1.26 deraadt 530: fargs = fargs_parse(argc, argv, &opts);
1.6 deraadt 531: assert(fargs != NULL);
1.1 benno 532:
533: /*
534: * If we're contacting an rsync:// daemon, then we don't need to
535: * fork, because we won't start a server ourselves.
1.41 naddy 536: * Route directly into the socket code, unless a remote shell
537: * has explicitly been specified.
1.1 benno 538: */
539:
1.41 naddy 540: if (fargs->remote && opts.ssh_prog == NULL) {
1.6 deraadt 541: assert(fargs->mode == FARGS_RECEIVER);
1.47 naddy 542: if ((rc = rsync_connect(&opts, &sd, fargs)) == 0) {
1.41 naddy 543: rc = rsync_socket(&opts, sd, fargs);
1.47 naddy 544: close(sd);
545: }
1.41 naddy 546: exit(rc);
1.1 benno 547: }
548:
549: /* Drop the dns/inet possibility. */
550:
1.19 florian 551: if (pledge("stdio unix rpath wpath cpath dpath fattr chown getpw proc exec unveil",
1.6 deraadt 552: NULL) == -1)
1.53.2.1! benno 553: err(ERR_IPC, "pledge");
1.1 benno 554:
555: /* Create a bidirectional socket and start our child. */
556:
1.6 deraadt 557: if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_NONBLOCK, 0, fds) == -1)
1.53.2.1! benno 558: err(ERR_IPC, "socketpair");
1.1 benno 559:
1.32 deraadt 560: switch ((child = fork())) {
561: case -1:
1.53.2.1! benno 562: err(ERR_IPC, "fork");
1.32 deraadt 563: case 0:
1.1 benno 564: close(fds[0]);
1.32 deraadt 565: if (pledge("stdio exec", NULL) == -1)
1.53.2.1! benno 566: err(ERR_IPC, "pledge");
1.32 deraadt 567:
568: memset(&sess, 0, sizeof(struct sess));
569: sess.opts = &opts;
1.1 benno 570:
1.53.2.1! benno 571: args = fargs_cmdline(&sess, fargs, NULL);
1.1 benno 572:
1.32 deraadt 573: for (i = 0; args[i] != NULL; i++)
1.45 benno 574: LOG2("exec[%d] = %s", i, args[i]);
1.1 benno 575:
1.32 deraadt 576: /* Make sure the child's stdin is from the sender. */
1.53.2.1! benno 577: if (dup2(fds[1], STDIN_FILENO) == -1)
! 578: err(ERR_IPC, "dup2");
! 579: if (dup2(fds[1], STDOUT_FILENO) == -1)
! 580: err(ERR_IPC, "dup2");
1.32 deraadt 581: execvp(args[0], args);
1.53.2.1! benno 582: _exit(ERR_IPC);
1.1 benno 583: /* NOTREACHED */
1.32 deraadt 584: default:
585: close(fds[1]);
1.41 naddy 586: if (!fargs->remote)
587: rc = rsync_client(&opts, fds[0], fargs);
588: else
589: rc = rsync_socket(&opts, fds[0], fargs);
1.32 deraadt 590: break;
1.1 benno 591: }
592:
1.47 naddy 593: close(fds[0]);
1.1 benno 594:
1.6 deraadt 595: if (waitpid(child, &st, 0) == -1)
1.53.2.1! benno 596: err(ERR_WAITPID, "waitpid");
1.34 deraadt 597:
598: /*
599: * If we don't already have an error (rc == 0), then inherit the
600: * error code of rsync_server() if it has exited.
601: * If it hasn't exited, it overrides our return value.
602: */
603:
1.53.2.1! benno 604: if (rc == 0) {
! 605: if (WIFEXITED(st))
! 606: rc = WEXITSTATUS(st);
! 607: else if (WIFSIGNALED(st))
! 608: rc = ERR_TERMIMATED;
! 609: else
! 610: rc = ERR_WAITPID;
! 611: }
1.34 deraadt 612:
1.32 deraadt 613: exit(rc);
1.1 benno 614: usage:
1.42 schwarze 615: fprintf(stderr, "usage: %s"
1.53.2.1! benno 616: " [-aDglnoprtvx] [-e program] [--address=sourceaddr]\n"
! 617: "\t[--compare-dest=dir] [--del] [--exclude] [--exclude-from=file]\n"
! 618: "\t[--include] [--include-from=file] [--no-motd] [--numeric-ids]\n"
! 619: "\t[--port=portnumber] [--rsync-path=program] [--timeout=seconds]\n"
! 620: "\t[--version] source ... directory\n",
1.32 deraadt 621: getprogname());
1.53.2.1! benno 622: exit(ERR_SYNTAX);
1.1 benno 623: }