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