[BACK]Return to main.c CVS log [TXT][DIR] Up to [local] / src / usr.bin / rsync

Annotation of src/usr.bin/rsync/main.c, Revision 1.29

1.29    ! benno       1: /*     $Id: main.c,v 1.28 2019/02/18 21:34:54 benno 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:
                     32: static void
                     33: fargs_free(struct fargs *p)
                     34: {
                     35:        size_t   i;
                     36:
1.6       deraadt    37:        if (p == NULL)
1.1       benno      38:                return;
                     39:
1.6       deraadt    40:        if (p->sources != NULL)
1.1       benno      41:                for (i = 0; i < p->sourcesz; i++)
                     42:                        free(p->sources[i]);
                     43:
                     44:        free(p->sources);
                     45:        free(p->sink);
                     46:        free(p->host);
                     47:        free(p);
                     48: }
                     49:
                     50: /*
                     51:  * A remote host is has a colon before the first path separator.
                     52:  * This works for rsh remote hosts (host:/foo/bar), implicit rsync
                     53:  * remote hosts (host::/foo/bar), and explicit (rsync://host/foo).
                     54:  * Return zero if local, non-zero if remote.
                     55:  */
                     56: static int
                     57: fargs_is_remote(const char *v)
                     58: {
                     59:        size_t   pos;
                     60:
                     61:        pos = strcspn(v, ":/");
1.6       deraadt    62:        return v[pos] == ':';
1.1       benno      63: }
                     64:
                     65: /*
                     66:  * Test whether a remote host is specifically an rsync daemon.
                     67:  * Return zero if not, non-zero if so.
                     68:  */
                     69: static int
                     70: fargs_is_daemon(const char *v)
                     71: {
                     72:        size_t   pos;
                     73:
1.6       deraadt    74:        if (strncasecmp(v, "rsync://", 8) == 0)
1.1       benno      75:                return 1;
                     76:
                     77:        pos = strcspn(v, ":/");
1.6       deraadt    78:        return v[pos] == ':' && v[pos + 1] == ':';
1.1       benno      79: }
                     80:
                     81: /*
                     82:  * Take the command-line filenames (e.g., rsync foo/ bar/ baz/) and
                     83:  * determine our operating mode.
                     84:  * For example, if the first argument is a remote file, this means that
                     85:  * we're going to transfer from the remote to the local.
                     86:  * We also make sure that the arguments are consistent, that is, if
                     87:  * we're going to transfer from the local to the remote, that no
                     88:  * filenames for the local transfer indicate remote hosts.
                     89:  * Always returns the parsed and sanitised options.
                     90:  */
                     91: static struct fargs *
1.26      deraadt    92: fargs_parse(size_t argc, char *argv[], struct opts *opts)
1.1       benno      93: {
                     94:        struct fargs    *f = NULL;
1.26      deraadt    95:        char            *cp, *ccp;
1.1       benno      96:        size_t           i, j, len = 0;
                     97:
                     98:        /* Allocations. */
                     99:
1.6       deraadt   100:        if ((f = calloc(1, sizeof(struct fargs))) == NULL)
1.29    ! benno     101:                err(1, "calloc");
1.1       benno     102:
                    103:        f->sourcesz = argc - 1;
1.6       deraadt   104:        if ((f->sources = calloc(f->sourcesz, sizeof(char *))) == NULL)
1.29    ! benno     105:                err(1, "calloc");
1.1       benno     106:
                    107:        for (i = 0; i < argc - 1; i++)
1.6       deraadt   108:                if ((f->sources[i] = strdup(argv[i])) == NULL)
1.29    ! benno     109:                        err(1, "strdup");
1.1       benno     110:
1.6       deraadt   111:        if ((f->sink = strdup(argv[i])) == NULL)
1.29    ! benno     112:                err(1, "strdup");
1.1       benno     113:
                    114:        /*
                    115:         * Test files for its locality.
                    116:         * If the last is a remote host, then we're sending from the
                    117:         * local to the remote host ("sender" mode).
                    118:         * If the first, remote to local ("receiver" mode).
                    119:         * If neither, a local transfer in sender style.
                    120:         */
                    121:
                    122:        f->mode = FARGS_SENDER;
                    123:
                    124:        if (fargs_is_remote(f->sink)) {
                    125:                f->mode = FARGS_SENDER;
1.6       deraadt   126:                if ((f->host = strdup(f->sink)) == NULL)
1.29    ! benno     127:                        err(1, "strdup");
1.1       benno     128:        }
                    129:
                    130:        if (fargs_is_remote(f->sources[0])) {
1.6       deraadt   131:                if (f->host != NULL)
1.29    ! benno     132:                        errx(1, "both source and "
1.1       benno     133:                                "destination cannot be remote files");
                    134:                f->mode = FARGS_RECEIVER;
1.6       deraadt   135:                if ((f->host = strdup(f->sources[0])) == NULL)
1.29    ! benno     136:                        err(1, "strdup");
1.1       benno     137:        }
                    138:
1.6       deraadt   139:        if (f->host != NULL) {
                    140:                if (strncasecmp(f->host, "rsync://", 8) == 0) {
1.29    ! benno     141:                        /* rsync://host/module[/path] */
1.1       benno     142:                        f->remote = 1;
                    143:                        len = strlen(f->host) - 8 + 1;
                    144:                        memmove(f->host, f->host + 8, len);
1.6       deraadt   145:                        if ((cp = strchr(f->host, '/')) == NULL)
1.29    ! benno     146:                                errx(1, "rsync protocol "
1.1       benno     147:                                        "requires a module name");
                    148:                        *cp++ = '\0';
                    149:                        f->module = cp;
1.6       deraadt   150:                        if ((cp = strchr(f->module, '/')) != NULL)
1.1       benno     151:                                *cp = '\0';
1.29    ! benno     152:                        if ((cp = strchr(f->host, ':'))) {
1.26      deraadt   153:                                /* host:port --> extract port */
                    154:                                *cp++ = '\0';
                    155:                                opts->port = cp;
                    156:                        }
1.1       benno     157:                } else {
                    158:                        /* host:[/path] */
                    159:                        cp = strchr(f->host, ':');
1.6       deraadt   160:                        assert(cp != NULL);
1.1       benno     161:                        *cp++ = '\0';
1.6       deraadt   162:                        if (*cp == ':') {
1.1       benno     163:                                /* host::module[/path] */
                    164:                                f->remote = 1;
                    165:                                f->module = ++cp;
                    166:                                cp = strchr(f->module, '/');
1.6       deraadt   167:                                if (cp != NULL)
1.1       benno     168:                                        *cp = '\0';
                    169:                        }
                    170:                }
1.6       deraadt   171:                if ((len = strlen(f->host)) == 0)
1.29    ! benno     172:                        errx(1, "empty remote host");
1.6       deraadt   173:                if (f->remote && strlen(f->module) == 0)
1.29    ! benno     174:                        errx(1, "empty remote module");
1.1       benno     175:        }
                    176:
                    177:        /* Make sure we have the same "hostspec" for all files. */
                    178:
1.4       deraadt   179:        if (!f->remote) {
1.6       deraadt   180:                if (f->mode == FARGS_SENDER)
1.1       benno     181:                        for (i = 0; i < f->sourcesz; i++) {
1.4       deraadt   182:                                if (!fargs_is_remote(f->sources[i]))
1.1       benno     183:                                        continue;
1.29    ! benno     184:                                errx(1, "remote file in "
1.1       benno     185:                                        "list of local sources: %s",
                    186:                                        f->sources[i]);
                    187:                        }
1.6       deraadt   188:                if (f->mode == FARGS_RECEIVER)
1.1       benno     189:                        for (i = 0; i < f->sourcesz; i++) {
                    190:                                if (fargs_is_remote(f->sources[i]) &&
1.4       deraadt   191:                                    !fargs_is_daemon(f->sources[i]))
1.1       benno     192:                                        continue;
                    193:                                if (fargs_is_daemon(f->sources[i]))
1.29    ! benno     194:                                        errx(1, "remote "
1.1       benno     195:                                                "daemon in list of "
                    196:                                                "remote sources: %s",
                    197:                                                f->sources[i]);
1.29    ! benno     198:                                errx(1, "local file in "
1.1       benno     199:                                        "list of remote sources: %s",
                    200:                                        f->sources[i]);
                    201:                        }
                    202:        } else {
1.6       deraadt   203:                if (f->mode != FARGS_RECEIVER)
1.29    ! benno     204:                        errx(1, "sender mode for remote "
1.1       benno     205:                                "daemon receivers not yet supported");
                    206:                for (i = 0; i < f->sourcesz; i++) {
                    207:                        if (fargs_is_daemon(f->sources[i]))
                    208:                                continue;
1.29    ! benno     209:                        errx(1, "non-remote daemon file "
1.1       benno     210:                                "in list of remote daemon sources: "
                    211:                                "%s", f->sources[i]);
                    212:                }
                    213:        }
                    214:
                    215:        /*
                    216:         * If we're not remote and a sender, strip our hostname.
                    217:         * Then exit if we're a sender or a local connection.
                    218:         */
                    219:
1.4       deraadt   220:        if (!f->remote) {
1.6       deraadt   221:                if (f->host == NULL)
1.1       benno     222:                        return f;
1.6       deraadt   223:                if (f->mode == FARGS_SENDER) {
                    224:                        assert(f->host != NULL);
1.1       benno     225:                        assert(len > 0);
                    226:                        j = strlen(f->sink);
                    227:                        memmove(f->sink, f->sink + len + 1, j - len);
                    228:                        return f;
1.6       deraadt   229:                } else if (f->mode != FARGS_RECEIVER)
1.1       benno     230:                        return f;
                    231:        }
                    232:
                    233:        /*
                    234:         * Now strip the hostnames from the remote host.
                    235:         *   rsync://host/module/path -> module/path
                    236:         *   host::module/path -> module/path
                    237:         *   host:path -> path
                    238:         * Also make sure that the remote hosts are the same.
                    239:         */
                    240:
1.6       deraadt   241:        assert(f->host != NULL);
1.1       benno     242:        assert(len > 0);
                    243:
                    244:        for (i = 0; i < f->sourcesz; i++) {
                    245:                cp = f->sources[i];
                    246:                j = strlen(cp);
                    247:                if (f->remote &&
1.6       deraadt   248:                    strncasecmp(cp, "rsync://", 8) == 0) {
1.1       benno     249:                        /* rsync://path */
                    250:                        cp += 8;
1.29    ! benno     251:                        if ((ccp = strchr(cp, ':')))    /* skip :port */
1.26      deraadt   252:                                *ccp = '\0';
1.1       benno     253:                        if (strncmp(cp, f->host, len) ||
1.11      benno     254:                            (cp[len] != '/' && cp[len] != '\0'))
1.29    ! benno     255:                                errx(1, "different remote "
1.1       benno     256:                                        "host: %s", f->sources[i]);
                    257:                        memmove(f->sources[i],
                    258:                                f->sources[i] + len + 8 + 1,
                    259:                                j - len - 8);
1.6       deraadt   260:                } else if (f->remote && strncmp(cp, "::", 2) == 0) {
1.1       benno     261:                        /* ::path */
                    262:                        memmove(f->sources[i],
                    263:                                f->sources[i] + 2, j - 1);
                    264:                } else if (f->remote) {
                    265:                        /* host::path */
                    266:                        if (strncmp(cp, f->host, len) ||
1.6       deraadt   267:                            (cp[len] != ':' && cp[len] != '\0'))
1.29    ! benno     268:                                errx(1, "different remote "
1.1       benno     269:                                        "host: %s", f->sources[i]);
1.11      benno     270:                        memmove(f->sources[i], f->sources[i] + len + 2,
                    271:                            j - len - 1);
1.6       deraadt   272:                } else if (cp[0] == ':') {
1.1       benno     273:                        /* :path */
                    274:                        memmove(f->sources[i], f->sources[i] + 1, j);
                    275:                } else {
                    276:                        /* host:path */
                    277:                        if (strncmp(cp, f->host, len) ||
1.6       deraadt   278:                            (cp[len] != ':' && cp[len] != '\0'))
1.29    ! benno     279:                                errx(1, "different remote "
1.1       benno     280:                                        "host: %s", f->sources[i]);
                    281:                        memmove(f->sources[i],
                    282:                                f->sources[i] + len + 1, j - len);
                    283:                }
                    284:        }
                    285:
                    286:        return f;
                    287: }
                    288:
                    289: int
                    290: main(int argc, char *argv[])
                    291: {
                    292:        struct opts      opts;
1.2       benno     293:        pid_t            child;
1.29    ! benno     294:        int              fds[2], rc = 0, c, st;
1.1       benno     295:        struct fargs    *fargs;
                    296:        struct option    lopts[] = {
1.26      deraadt   297:                { "port",       required_argument, NULL,                3 },
1.21      deraadt   298:                { "rsh",        required_argument, NULL,                'e' },
1.8       deraadt   299:                { "rsync-path", required_argument, NULL,                1 },
                    300:                { "sender",     no_argument,    &opts.sender,           1 },
                    301:                { "server",     no_argument,    &opts.server,           1 },
1.20      deraadt   302:                { "dry-run",    no_argument,    &opts.dry_run,          1 },
                    303:                { "version",    no_argument,    NULL,                   2 },
1.23      deraadt   304:                { "archive",    no_argument,    NULL,                   'a' },
1.21      deraadt   305:                { "help",       no_argument,    NULL,                   'h' },
1.20      deraadt   306:                { "delete",     no_argument,    &opts.del,              1 },
                    307:                { "no-delete",  no_argument,    &opts.del,              0 },
                    308:                { "devices",    no_argument,    &opts.devices,          1 },
                    309:                { "no-devices", no_argument,    &opts.devices,          0 },
                    310:                { "group",      no_argument,    &opts.preserve_gids,    1 },
                    311:                { "no-group",   no_argument,    &opts.preserve_gids,    0 },
1.8       deraadt   312:                { "links",      no_argument,    &opts.preserve_links,   1 },
1.20      deraadt   313:                { "no-links",   no_argument,    &opts.preserve_links,   0 },
                    314:                { "owner",      no_argument,    &opts.preserve_uids,    1 },
                    315:                { "no-owner",   no_argument,    &opts.preserve_uids,    0 },
1.8       deraadt   316:                { "perms",      no_argument,    &opts.preserve_perms,   1 },
1.20      deraadt   317:                { "no-perms",   no_argument,    &opts.preserve_perms,   0 },
1.8       deraadt   318:                { "recursive",  no_argument,    &opts.recursive,        1 },
1.20      deraadt   319:                { "no-recursive", no_argument,  &opts.recursive,        0 },
1.19      florian   320:                { "specials",   no_argument,    &opts.specials,         1 },
1.21      deraadt   321:                { "no-specials", no_argument,   &opts.specials,         0 },
1.20      deraadt   322:                { "times",      no_argument,    &opts.preserve_times,   1 },
                    323:                { "no-times",   no_argument,    &opts.preserve_times,   0 },
                    324:                { "verbose",    no_argument,    &opts.verbose,          1 },
                    325:                { "no-verbose", no_argument,    &opts.verbose,          0 },
1.8       deraadt   326:                { NULL,         0,              NULL,                   0 }};
1.1       benno     327:
                    328:        /* Global pledge. */
                    329:
1.19      florian   330:        if (pledge("stdio unix rpath wpath cpath dpath inet fattr chown dns getpw proc exec unveil",
1.6       deraadt   331:            NULL) == -1)
1.29    ! benno     332:                err(1, "pledge");
1.1       benno     333:
                    334:        memset(&opts, 0, sizeof(struct opts));
                    335:
1.21      deraadt   336:        while ((c = getopt_long(argc, argv, "Dae:ghlnoprtv", lopts, NULL)) != -1) {
1.1       benno     337:                switch (c) {
1.19      florian   338:                case 'D':
                    339:                        opts.devices = 1;
                    340:                        opts.specials = 1;
                    341:                        break;
                    342:                case 'a':
                    343:                        opts.recursive = 1;
                    344:                        opts.preserve_links = 1;
                    345:                        opts.preserve_perms = 1;
                    346:                        opts.preserve_times = 1;
                    347:                        opts.preserve_gids = 1;
                    348:                        opts.preserve_uids = 1;
                    349:                        opts.devices = 1;
                    350:                        opts.specials = 1;
                    351:                        break;
1.1       benno     352:                case 'e':
1.9       deraadt   353:                        opts.ssh_prog = optarg;
1.1       benno     354:                        break;
1.10      benno     355:                case 'g':
                    356:                        opts.preserve_gids = 1;
                    357:                        break;
1.1       benno     358:                case 'l':
                    359:                        opts.preserve_links = 1;
                    360:                        break;
                    361:                case 'n':
                    362:                        opts.dry_run = 1;
                    363:                        break;
1.13      florian   364:                case 'o':
                    365:                        opts.preserve_uids = 1;
                    366:                        break;
1.1       benno     367:                case 'p':
                    368:                        opts.preserve_perms = 1;
                    369:                        break;
                    370:                case 'r':
                    371:                        opts.recursive = 1;
                    372:                        break;
                    373:                case 't':
                    374:                        opts.preserve_times = 1;
                    375:                        break;
                    376:                case 'v':
                    377:                        opts.verbose++;
                    378:                        break;
                    379:                case 0:
                    380:                        /* Non-NULL flag values (e.g., --sender). */
                    381:                        break;
                    382:                case 1:
                    383:                        opts.rsync_path = optarg;
                    384:                        break;
1.17      deraadt   385:                case 2:
                    386:                        fprintf(stderr, "openrsync: protocol version %u\n",
                    387:                            RSYNC_PROTOCOL);
                    388:                        exit(0);
1.26      deraadt   389:                case 3:
                    390:                        opts.port = optarg;
                    391:                        break;
1.21      deraadt   392:                case 'h':
1.1       benno     393:                default:
                    394:                        goto usage;
                    395:                }
                    396:        }
                    397:
                    398:        argc -= optind;
                    399:        argv += optind;
                    400:
                    401:        /* FIXME: reference implementation rsync accepts this. */
                    402:
                    403:        if (argc < 2)
                    404:                goto usage;
                    405:
1.26      deraadt   406:        if (opts.port == NULL)
1.29    ! benno     407:                opts.port = "rsync";
1.26      deraadt   408:
1.1       benno     409:        /*
                    410:         * This is what happens when we're started with the "hidden"
                    411:         * --server option, which is invoked for the rsync on the remote
                    412:         * host by the parent.
                    413:         */
                    414:
                    415:        if (opts.server) {
1.19      florian   416:                if (pledge("stdio unix rpath wpath cpath dpath fattr chown getpw unveil", NULL) == -1)
1.29    ! benno     417:                        err(1, "pledge");
        !           418:                return rsync_server(&opts, (size_t)argc, argv);
1.1       benno     419:        }
                    420:
                    421:        /*
                    422:         * Now we know that we're the client on the local machine
                    423:         * invoking rsync(1).
                    424:         * At this point, we need to start the client and server
                    425:         * initiation logic.
                    426:         * The client is what we continue running on this host; the
                    427:         * server is what we'll use to connect to the remote and
                    428:         * invoke rsync with the --server option.
                    429:         */
                    430:
1.26      deraadt   431:        fargs = fargs_parse(argc, argv, &opts);
1.6       deraadt   432:        assert(fargs != NULL);
1.1       benno     433:
                    434:        /*
                    435:         * If we're contacting an rsync:// daemon, then we don't need to
                    436:         * fork, because we won't start a server ourselves.
                    437:         * Route directly into the socket code, in that case.
                    438:         */
                    439:
                    440:        if (fargs->remote) {
1.6       deraadt   441:                assert(fargs->mode == FARGS_RECEIVER);
1.19      florian   442:                if (pledge("stdio unix rpath wpath cpath dpath inet fattr chown dns getpw unveil",
1.6       deraadt   443:                    NULL) == -1)
1.29    ! benno     444:                        err(1, "pledge");
        !           445:                rc = rsync_socket(&opts, fargs);
1.1       benno     446:                fargs_free(fargs);
1.29    ! benno     447:                return rc;
1.1       benno     448:        }
                    449:
                    450:        /* Drop the dns/inet possibility. */
                    451:
1.19      florian   452:        if (pledge("stdio unix rpath wpath cpath dpath fattr chown getpw proc exec unveil",
1.6       deraadt   453:            NULL) == -1)
1.29    ! benno     454:                err(1, "pledge");
1.1       benno     455:
                    456:        /* Create a bidirectional socket and start our child. */
                    457:
1.6       deraadt   458:        if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_NONBLOCK, 0, fds) == -1)
1.29    ! benno     459:                err(1, "socketpair");
1.1       benno     460:
1.6       deraadt   461:        if ((child = fork()) == -1) {
1.1       benno     462:                close(fds[0]);
                    463:                close(fds[1]);
1.29    ! benno     464:                err(1, "fork");
1.1       benno     465:        }
                    466:
                    467:        /* Drop the fork possibility. */
                    468:
1.19      florian   469:        if (pledge("stdio unix rpath wpath cpath dpath fattr chown getpw exec unveil", NULL) == -1)
1.29    ! benno     470:                err(1, "pledge");
1.1       benno     471:
1.6       deraadt   472:        if (child == 0) {
1.1       benno     473:                close(fds[0]);
                    474:                fds[0] = -1;
1.6       deraadt   475:                if (pledge("stdio exec", NULL) == -1)
1.29    ! benno     476:                        err(1, "pledge");
1.1       benno     477:                rsync_child(&opts, fds[1], fargs);
                    478:                /* NOTREACHED */
                    479:        }
                    480:
                    481:        close(fds[1]);
                    482:        fds[1] = -1;
1.19      florian   483:        if (pledge("stdio unix rpath wpath cpath dpath fattr chown getpw unveil", NULL) == -1)
1.29    ! benno     484:                err(1, "pledge");
        !           485:        rc = rsync_client(&opts, fds[0], fargs);
1.1       benno     486:        fargs_free(fargs);
                    487:
                    488:        /*
                    489:         * If the client has an error and exits, the server may be
                    490:         * sitting around waiting to get data while we waitpid().
                    491:         * So close the connection here so that they don't hang.
                    492:         */
                    493:
1.29    ! benno     494:        if (!rc) {
1.1       benno     495:                close(fds[0]);
                    496:                fds[0] = -1;
                    497:        }
                    498:
1.6       deraadt   499:        if (waitpid(child, &st, 0) == -1)
1.29    ! benno     500:                err(1, "waitpid");
        !           501:        if (!(WIFEXITED(st) && WEXITSTATUS(st) == 0))
        !           502:                rc = 0;
1.1       benno     503:
1.6       deraadt   504:        if (fds[0] != -1)
1.1       benno     505:                close(fds[0]);
1.29    ! benno     506:        return rc;
1.1       benno     507: usage:
1.29    ! benno     508:        fprintf(stderr, "usage: %s [-Daglnoprtv] "
        !           509:                "[-e ssh-prog] [--delete] [--rsync-path=prog] src ... dst\n",
1.1       benno     510:                getprogname());
1.29    ! benno     511:        return 1;
1.1       benno     512: }