Annotation of src/usr.bin/rsync/main.c, Revision 1.69
1.69 ! job 1: /* $OpenBSD: main.c,v 1.68 2023/04/28 10:24:38 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>
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.68 claudio 315: { "ignore-times", no_argument, NULL, 'I' },
1.63 deraadt 316: { "include", required_argument, NULL, OP_INCLUDE },
1.57 claudio 317: { "include-from", required_argument, NULL, OP_INCLUDE_FROM },
1.56 claudio 318: { "links", no_argument, &opts.preserve_links, 1 },
1.61 claudio 319: { "max-size", required_argument, NULL, OP_MAX_SIZE },
320: { "min-size", required_argument, NULL, OP_MIN_SIZE },
1.56 claudio 321: { "no-links", no_argument, &opts.preserve_links, 0 },
322: { "no-motd", no_argument, &opts.no_motd, 1 },
323: { "numeric-ids", no_argument, &opts.numeric_ids, 1 },
1.69 ! job 324: { "omit-dir-times", no_argument, &opts.ignore_dir_times, 1 },
1.56 claudio 325: { "owner", no_argument, &opts.preserve_uids, 1 },
326: { "no-owner", no_argument, &opts.preserve_uids, 0 },
327: { "perms", no_argument, &opts.preserve_perms, 1 },
328: { "no-perms", no_argument, &opts.preserve_perms, 0 },
329: { "port", required_argument, NULL, OP_PORT },
330: { "recursive", no_argument, &opts.recursive, 1 },
331: { "no-recursive", no_argument, &opts.recursive, 0 },
332: { "rsh", required_argument, NULL, 'e' },
333: { "rsync-path", required_argument, NULL, OP_RSYNCPATH },
334: { "sender", no_argument, &opts.sender, 1 },
335: { "server", no_argument, &opts.server, 1 },
1.68 claudio 336: { "size-only", no_argument, &opts.size_only, 1 },
1.56 claudio 337: { "specials", no_argument, &opts.specials, 1 },
338: { "no-specials", no_argument, &opts.specials, 0 },
339: { "timeout", required_argument, NULL, OP_TIMEOUT },
340: { "times", no_argument, &opts.preserve_times, 1 },
341: { "no-times", no_argument, &opts.preserve_times, 0 },
342: { "verbose", no_argument, &verbose, 1 },
343: { "no-verbose", no_argument, &verbose, 0 },
1.67 claudio 344: { "version", no_argument, NULL, 'V' },
1.56 claudio 345: { NULL, 0, NULL, 0 }
346: };
347:
1.1 benno 348: int
349: main(int argc, char *argv[])
350: {
1.2 benno 351: pid_t child;
1.60 claudio 352: int fds[2], sd = -1, rc, c, st, i, lidx;
353: size_t basedir_cnt = 0;
1.62 claudio 354: struct sess sess;
1.1 benno 355: struct fargs *fargs;
1.32 deraadt 356: char **args;
1.63 deraadt 357: const char *errstr;
1.57 claudio 358:
1.1 benno 359: /* Global pledge. */
360:
1.19 florian 361: if (pledge("stdio unix rpath wpath cpath dpath inet fattr chown dns getpw proc exec unveil",
1.6 deraadt 362: NULL) == -1)
1.54 claudio 363: err(ERR_IPC, "pledge");
1.1 benno 364:
1.62 claudio 365: opts.max_size = opts.min_size = -1;
366:
1.69 ! job 367: while ((c = getopt_long(argc, argv, "aDe:ghIlnOoprtVvxz", lopts, &lidx))
1.30 benno 368: != -1) {
1.1 benno 369: switch (c) {
1.19 florian 370: case 'D':
371: opts.devices = 1;
372: opts.specials = 1;
373: break;
374: case 'a':
375: opts.recursive = 1;
376: opts.preserve_links = 1;
377: opts.preserve_perms = 1;
378: opts.preserve_times = 1;
379: opts.preserve_gids = 1;
380: opts.preserve_uids = 1;
381: opts.devices = 1;
382: opts.specials = 1;
383: break;
1.1 benno 384: case 'e':
1.9 deraadt 385: opts.ssh_prog = optarg;
1.1 benno 386: break;
1.10 benno 387: case 'g':
388: opts.preserve_gids = 1;
389: break;
1.68 claudio 390: case 'I':
391: opts.ignore_times = 1;
392: break;
1.1 benno 393: case 'l':
394: opts.preserve_links = 1;
395: break;
396: case 'n':
397: opts.dry_run = 1;
398: break;
1.69 ! job 399: case 'O':
! 400: opts.ignore_dir_times = 1;
! 401: break;
1.13 florian 402: case 'o':
403: opts.preserve_uids = 1;
404: break;
1.1 benno 405: case 'p':
406: opts.preserve_perms = 1;
407: break;
408: case 'r':
409: opts.recursive = 1;
410: break;
411: case 't':
412: opts.preserve_times = 1;
413: break;
414: case 'v':
1.45 benno 415: verbose++;
1.1 benno 416: break;
1.67 claudio 417: case 'V':
418: fprintf(stderr, "openrsync: protocol version %u\n",
419: RSYNC_PROTOCOL);
420: exit(0);
1.44 bket 421: case 'x':
422: opts.one_file_system++;
423: break;
1.36 deraadt 424: case 'z':
425: fprintf(stderr, "%s: -z not supported yet\n", getprogname());
426: break;
1.1 benno 427: case 0:
428: /* Non-NULL flag values (e.g., --sender). */
429: break;
1.56 claudio 430: case OP_ADDRESS:
431: opts.address = optarg;
1.1 benno 432: break;
1.64 job 433: case OP_CONTIMEOUT:
434: poll_contimeout = strtonum(optarg, 0, 60*60, &errstr);
435: if (errstr != NULL)
436: errx(ERR_SYNTAX, "timeout is %s: %s",
437: errstr, optarg);
438: break;
1.56 claudio 439: case OP_PORT:
1.26 deraadt 440: opts.port = optarg;
441: break;
1.56 claudio 442: case OP_RSYNCPATH:
443: opts.rsync_path = optarg;
1.48 claudio 444: break;
1.56 claudio 445: case OP_TIMEOUT:
1.50 claudio 446: poll_timeout = strtonum(optarg, 0, 60*60, &errstr);
447: if (errstr != NULL)
1.54 claudio 448: errx(ERR_SYNTAX, "timeout is %s: %s",
449: errstr, optarg);
1.57 claudio 450: break;
451: case OP_EXCLUDE:
452: if (parse_rule(optarg, RULE_EXCLUDE) == -1)
453: errx(ERR_SYNTAX, "syntax error in exclude: %s",
454: optarg);
455: break;
456: case OP_INCLUDE:
457: if (parse_rule(optarg, RULE_INCLUDE) == -1)
458: errx(ERR_SYNTAX, "syntax error in include: %s",
459: optarg);
460: break;
461: case OP_EXCLUDE_FROM:
1.59 claudio 462: parse_file(optarg, RULE_EXCLUDE);
1.57 claudio 463: break;
464: case OP_INCLUDE_FROM:
1.59 claudio 465: parse_file(optarg, RULE_INCLUDE);
1.50 claudio 466: break;
1.60 claudio 467: case OP_COMP_DEST:
468: if (opts.alt_base_mode !=0 &&
469: opts.alt_base_mode != BASE_MODE_COMPARE) {
470: errx(1, "option --%s conflicts with %s",
471: lopts[lidx].name,
472: alt_base_mode(opts.alt_base_mode));
473: }
474: opts.alt_base_mode = BASE_MODE_COMPARE;
475: #if 0
476: goto basedir;
477: case OP_COPY_DEST:
478: if (opts.alt_base_mode !=0 &&
479: opts.alt_base_mode != BASE_MODE_COPY) {
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_COPY;
485: goto basedir;
486: case OP_LINK_DEST:
487: if (opts.alt_base_mode !=0 &&
488: opts.alt_base_mode != BASE_MODE_LINK) {
489: errx(1, "option --%s conflicts with %s",
490: lopts[lidx].name,
491: alt_base_mode(opts.alt_base_mode));
492: }
493: opts.alt_base_mode = BASE_MODE_LINK;
494:
495: basedir:
496: #endif
497: if (basedir_cnt >= MAX_BASEDIR)
498: errx(1, "too many --%s directories specified",
499: lopts[lidx].name);
500: opts.basedir[basedir_cnt++] = optarg;
1.61 claudio 501: break;
502: case OP_MAX_SIZE:
1.62 claudio 503: if (scan_scaled(optarg, &opts.max_size) == -1)
504: err(1, "bad max-size");
505: break;
1.61 claudio 506: case OP_MIN_SIZE:
1.62 claudio 507: if (scan_scaled(optarg, &opts.min_size) == -1)
508: err(1, "bad min-size");
1.60 claudio 509: break;
1.21 deraadt 510: case 'h':
1.1 benno 511: default:
512: goto usage;
513: }
514: }
515:
516: argc -= optind;
517: argv += optind;
518:
519: /* FIXME: reference implementation rsync accepts this. */
520:
521: if (argc < 2)
522: goto usage;
523:
1.26 deraadt 524: if (opts.port == NULL)
1.29 benno 525: opts.port = "rsync";
1.26 deraadt 526:
1.64 job 527: /* by default and for --contimeout=0 disable poll_contimeout */
528: if (poll_contimeout == 0)
529: poll_contimeout = -1;
530: else
531: poll_contimeout *= 1000;
532:
1.51 claudio 533: /* by default and for --timeout=0 disable poll_timeout */
534: if (poll_timeout == 0)
1.64 job 535: poll_timeout = -1;
536: else
1.51 claudio 537: poll_timeout *= 1000;
538:
1.1 benno 539: /*
540: * This is what happens when we're started with the "hidden"
541: * --server option, which is invoked for the rsync on the remote
542: * host by the parent.
543: */
544:
1.32 deraadt 545: if (opts.server)
546: exit(rsync_server(&opts, (size_t)argc, argv));
1.50 claudio 547:
1.1 benno 548: /*
549: * Now we know that we're the client on the local machine
550: * invoking rsync(1).
551: * At this point, we need to start the client and server
552: * initiation logic.
553: * The client is what we continue running on this host; the
554: * server is what we'll use to connect to the remote and
555: * invoke rsync with the --server option.
556: */
557:
1.26 deraadt 558: fargs = fargs_parse(argc, argv, &opts);
1.6 deraadt 559: assert(fargs != NULL);
1.1 benno 560:
561: /*
562: * If we're contacting an rsync:// daemon, then we don't need to
563: * fork, because we won't start a server ourselves.
1.41 naddy 564: * Route directly into the socket code, unless a remote shell
565: * has explicitly been specified.
1.1 benno 566: */
567:
1.41 naddy 568: if (fargs->remote && opts.ssh_prog == NULL) {
1.6 deraadt 569: assert(fargs->mode == FARGS_RECEIVER);
1.47 naddy 570: if ((rc = rsync_connect(&opts, &sd, fargs)) == 0) {
1.41 naddy 571: rc = rsync_socket(&opts, sd, fargs);
1.47 naddy 572: close(sd);
573: }
1.41 naddy 574: exit(rc);
1.1 benno 575: }
576:
577: /* Drop the dns/inet possibility. */
578:
1.19 florian 579: if (pledge("stdio unix rpath wpath cpath dpath fattr chown getpw proc exec unveil",
1.6 deraadt 580: NULL) == -1)
1.54 claudio 581: err(ERR_IPC, "pledge");
1.1 benno 582:
583: /* Create a bidirectional socket and start our child. */
584:
1.6 deraadt 585: if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_NONBLOCK, 0, fds) == -1)
1.54 claudio 586: err(ERR_IPC, "socketpair");
1.1 benno 587:
1.32 deraadt 588: switch ((child = fork())) {
589: case -1:
1.54 claudio 590: err(ERR_IPC, "fork");
1.32 deraadt 591: case 0:
1.1 benno 592: close(fds[0]);
1.32 deraadt 593: if (pledge("stdio exec", NULL) == -1)
1.54 claudio 594: err(ERR_IPC, "pledge");
1.32 deraadt 595:
596: memset(&sess, 0, sizeof(struct sess));
597: sess.opts = &opts;
1.1 benno 598:
1.54 claudio 599: args = fargs_cmdline(&sess, fargs, NULL);
1.1 benno 600:
1.32 deraadt 601: for (i = 0; args[i] != NULL; i++)
1.45 benno 602: LOG2("exec[%d] = %s", i, args[i]);
1.1 benno 603:
1.32 deraadt 604: /* Make sure the child's stdin is from the sender. */
1.54 claudio 605: if (dup2(fds[1], STDIN_FILENO) == -1)
606: err(ERR_IPC, "dup2");
607: if (dup2(fds[1], STDOUT_FILENO) == -1)
608: err(ERR_IPC, "dup2");
1.32 deraadt 609: execvp(args[0], args);
1.54 claudio 610: _exit(ERR_IPC);
1.1 benno 611: /* NOTREACHED */
1.32 deraadt 612: default:
613: close(fds[1]);
1.41 naddy 614: if (!fargs->remote)
615: rc = rsync_client(&opts, fds[0], fargs);
616: else
617: rc = rsync_socket(&opts, fds[0], fargs);
1.32 deraadt 618: break;
1.1 benno 619: }
620:
1.47 naddy 621: close(fds[0]);
1.1 benno 622:
1.6 deraadt 623: if (waitpid(child, &st, 0) == -1)
1.54 claudio 624: err(ERR_WAITPID, "waitpid");
1.34 deraadt 625:
626: /*
627: * If we don't already have an error (rc == 0), then inherit the
628: * error code of rsync_server() if it has exited.
629: * If it hasn't exited, it overrides our return value.
630: */
631:
1.54 claudio 632: if (rc == 0) {
633: if (WIFEXITED(st))
634: rc = WEXITSTATUS(st);
635: else if (WIFSIGNALED(st))
636: rc = ERR_TERMIMATED;
637: else
638: rc = ERR_WAITPID;
639: }
1.34 deraadt 640:
1.32 deraadt 641: exit(rc);
1.1 benno 642: usage:
1.42 schwarze 643: fprintf(stderr, "usage: %s"
1.69 ! job 644: " [-aDgIlnOoprtVvx] [-e program] [--address=sourceaddr]\n"
1.65 tb 645: "\t[--contimeout=seconds] [--compare-dest=dir] [--del] [--exclude]\n"
1.64 job 646: "\t[--exclude-from=file] [--include] [--include-from=file]\n"
647: "\t[--no-motd] [--numeric-ids] [--port=portnumber]\n"
1.68 claudio 648: "\t[--rsync-path=program] [--size-only] [--timeout=seconds]\n"
1.64 job 649: "\tsource ... directory\n",
1.32 deraadt 650: getprogname());
1.54 claudio 651: exit(ERR_SYNTAX);
1.1 benno 652: }