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