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