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