Currently our rsync does not follow the exit codes from rsync. Also the
error handling is complex because ERR() and ERRX() are not terminating the
process.

This diff tries to start cleaning up the mess a bit. Introduce some exit
codes to use and apply them in places where it is obvious.

The rsync server process should normally communicate errors back via the
rsync socket but there are some errors where this will most probably fail
as well. A good example are memory failures.

I used ERR_IPC as a kind of catchall for any system error that pops up
(fork, socketpair but also pledge, unveil).

The goal is to reduce the amount of ERR(), ERRX() and ERRX1() from rsync.
This should simplify the code base.

I also synced the mkpath() call with the one from mkdir and handle the
error now outside of the call. Again I think the result is cleaner.

OK?
-- 
:wq Claudio

Index: client.c
===================================================================
RCS file: /cvs/src/usr.bin/rsync/client.c,v
retrieving revision 1.15
diff -u -p -r1.15 client.c
--- client.c    8 May 2019 20:00:25 -0000       1.15
+++ client.c    7 May 2021 08:29:10 -0000
@@ -43,7 +43,7 @@ rsync_client(const struct opts *opts, in
 
        if (pledge("stdio unix rpath wpath cpath dpath fattr chown getpw 
unveil",
            NULL) == -1)
-               err(1, "pledge");
+               err(ERR_IPC, "pledge");
 
        memset(&sess, 0, sizeof(struct sess));
        sess.opts = opts;
Index: extern.h
===================================================================
RCS file: /cvs/src/usr.bin/rsync/extern.h,v
retrieving revision 1.36
diff -u -p -r1.36 extern.h
--- extern.h    31 Mar 2021 19:45:16 -0000      1.36
+++ extern.h    7 May 2021 08:22:32 -0000
@@ -42,6 +42,19 @@
 #define        CSUM_LENGTH_PHASE2 (16)
 
 /*
+ * Rsync error codes.
+ */
+#define ERR_SYNTAX     1
+#define ERR_PROTOCOL   2
+#define ERR_SOCK_IO    10
+#define ERR_FILE_IO    11
+#define ERR_WIREPROTO  12
+#define ERR_IPC                14      /* catchall for any kind of syscall 
error */
+#define ERR_TERMIMATED 16
+#define ERR_WAITPID    21
+#define ERR_NOMEM      22
+
+/*
  * Use this for --timeout.
  * All poll events will use it and catch time-outs.
  */
Index: fargs.c
===================================================================
RCS file: /cvs/src/usr.bin/rsync/fargs.c,v
retrieving revision 1.17
diff -u -p -r1.17 fargs.c
--- fargs.c     8 May 2019 20:00:25 -0000       1.17
+++ fargs.c     7 May 2021 08:19:29 -0000
@@ -17,6 +17,7 @@
 #include <sys/stat.h>
 
 #include <assert.h>
+#include <err.h>
 #include <stdint.h>
 #include <stdlib.h>
 #include <string.h>
@@ -51,7 +52,7 @@ fargs_cmdline(struct sess *sess, const s
                if (sess->opts->ssh_prog) {
                        ap = strdup(sess->opts->ssh_prog);
                        if (ap == NULL)
-                               goto out;
+                               err(ERR_NOMEM, NULL);
 
                        while ((arg = strsep(&ap, " \t")) != NULL) {
                                if (arg[0] == '\0') {
@@ -127,8 +128,4 @@ fargs_cmdline(struct sess *sess, const s
                addargs(&args, "%s", f->sink);
 
        return args.list;
-out:
-       freeargs(&args);
-       ERR("calloc");
-       return NULL;
 }
Index: main.c
===================================================================
RCS file: /cvs/src/usr.bin/rsync/main.c,v
retrieving revision 1.53
diff -u -p -r1.53 main.c
--- main.c      31 Mar 2021 19:45:16 -0000      1.53
+++ main.c      7 May 2021 08:30:18 -0000
@@ -83,18 +83,18 @@ fargs_parse(size_t argc, char *argv[], s
        /* Allocations. */
 
        if ((f = calloc(1, sizeof(struct fargs))) == NULL)
-               err(1, "calloc");
+               err(ERR_NOMEM, NULL);
 
        f->sourcesz = argc - 1;
        if ((f->sources = calloc(f->sourcesz, sizeof(char *))) == NULL)
-               err(1, "calloc");
+               err(ERR_NOMEM, NULL);
 
        for (i = 0; i < argc - 1; i++)
                if ((f->sources[i] = strdup(argv[i])) == NULL)
-                       err(1, "strdup");
+                       err(ERR_NOMEM, NULL);
 
        if ((f->sink = strdup(argv[i])) == NULL)
-               err(1, "strdup");
+               err(ERR_NOMEM, NULL);
 
        /*
         * Test files for its locality.
@@ -109,15 +109,16 @@ fargs_parse(size_t argc, char *argv[], s
        if (fargs_is_remote(f->sink)) {
                f->mode = FARGS_SENDER;
                if ((f->host = strdup(f->sink)) == NULL)
-                       err(1, "strdup");
+                       err(ERR_NOMEM, NULL);
        }
 
        if (fargs_is_remote(f->sources[0])) {
                if (f->host != NULL)
-                       errx(1, "both source and destination cannot be remote 
files");
+                       errx(ERR_SYNTAX, "both source and destination "
+                           "cannot be remote files");
                f->mode = FARGS_RECEIVER;
                if ((f->host = strdup(f->sources[0])) == NULL)
-                       err(1, "strdup");
+                       err(ERR_NOMEM, NULL);
        }
 
        if (f->host != NULL) {
@@ -127,7 +128,8 @@ fargs_parse(size_t argc, char *argv[], s
                        len = strlen(f->host) - 8 + 1;
                        memmove(f->host, f->host + 8, len);
                        if ((cp = strchr(f->host, '/')) == NULL)
-                               errx(1, "rsync protocol requires a module 
name");
+                               errx(ERR_SYNTAX,
+                                   "rsync protocol requires a module name");
                        *cp++ = '\0';
                        f->module = cp;
                        if ((cp = strchr(f->module, '/')) != NULL)
@@ -152,9 +154,9 @@ fargs_parse(size_t argc, char *argv[], s
                        }
                }
                if ((len = strlen(f->host)) == 0)
-                       errx(1, "empty remote host");
+                       errx(ERR_SYNTAX, "empty remote host");
                if (f->remote && strlen(f->module) == 0)
-                       errx(1, "empty remote module");
+                       errx(ERR_SYNTAX, "empty remote module");
        }
 
        /* Make sure we have the same "hostspec" for all files. */
@@ -164,7 +166,7 @@ fargs_parse(size_t argc, char *argv[], s
                        for (i = 0; i < f->sourcesz; i++) {
                                if (!fargs_is_remote(f->sources[i]))
                                        continue;
-                               errx(1,
+                               errx(ERR_SYNTAX,
                                    "remote file in list of local sources: %s",
                                    f->sources[i]);
                        }
@@ -174,20 +176,20 @@ fargs_parse(size_t argc, char *argv[], s
                                    !fargs_is_daemon(f->sources[i]))
                                        continue;
                                if (fargs_is_daemon(f->sources[i]))
-                                       errx(1, "remote daemon in list of "
-                                           "remote sources: %s",
-                                           f->sources[i]);
-                               errx(1, "local file in list of remote sources: 
%s",
-                                   f->sources[i]);
+                                       errx(ERR_SYNTAX,
+                                           "remote daemon in list of remote "
+                                           "sources: %s", f->sources[i]);
+                               errx(ERR_SYNTAX, "local file in list of "
+                                   "remote sources: %s", f->sources[i]);
                        }
        } else {
                if (f->mode != FARGS_RECEIVER)
-                       errx(1, "sender mode for remote "
+                       errx(ERR_SYNTAX, "sender mode for remote "
                                "daemon receivers not yet supported");
                for (i = 0; i < f->sourcesz; i++) {
                        if (fargs_is_daemon(f->sources[i]))
                                continue;
-                       errx(1, "non-remote daemon file "
+                       errx(ERR_SYNTAX, "non-remote daemon file "
                                "in list of remote daemon sources: "
                                "%s", f->sources[i]);
                }
@@ -233,7 +235,7 @@ fargs_parse(size_t argc, char *argv[], s
                                *ccp = '\0';
                        if (strncmp(cp, f->host, len) ||
                            (cp[len] != '/' && cp[len] != '\0'))
-                               errx(1, "different remote host: %s",
+                               errx(ERR_SYNTAX, "different remote host: %s",
                                    f->sources[i]);
                        memmove(f->sources[i],
                                f->sources[i] + len + 8 + 1,
@@ -246,7 +248,7 @@ fargs_parse(size_t argc, char *argv[], s
                        /* host::path */
                        if (strncmp(cp, f->host, len) ||
                            (cp[len] != ':' && cp[len] != '\0'))
-                               errx(1, "different remote host: %s",
+                               errx(ERR_SYNTAX, "different remote host: %s",
                                    f->sources[i]);
                        memmove(f->sources[i], f->sources[i] + len + 2,
                            j - len - 1);
@@ -257,7 +259,7 @@ fargs_parse(size_t argc, char *argv[], s
                        /* host:path */
                        if (strncmp(cp, f->host, len) ||
                            (cp[len] != ':' && cp[len] != '\0'))
-                               errx(1, "different remote host: %s",
+                               errx(ERR_SYNTAX, "different remote host: %s",
                                    f->sources[i]);
                        memmove(f->sources[i],
                                f->sources[i] + len + 1, j - len);
@@ -318,7 +320,7 @@ main(int argc, char *argv[])
 
        if (pledge("stdio unix rpath wpath cpath dpath inet fattr chown dns 
getpw proc exec unveil",
            NULL) == -1)
-               err(1, "pledge");
+               err(ERR_IPC, "pledge");
 
        memset(&opts, 0, sizeof(struct opts));
 
@@ -391,7 +393,8 @@ main(int argc, char *argv[])
                case 5:
                        poll_timeout = strtonum(optarg, 0, 60*60, &errstr);
                        if (errstr != NULL)
-                               errx(1, "timeout is %s: %s", errstr, optarg);
+                               errx(ERR_SYNTAX, "timeout is %s: %s",
+                                   errstr, optarg);
                        break;
                case 6:
                        opts.no_motd = 1;
@@ -461,43 +464,36 @@ main(int argc, char *argv[])
 
        if (pledge("stdio unix rpath wpath cpath dpath fattr chown getpw proc 
exec unveil",
            NULL) == -1)
-               err(1, "pledge");
+               err(ERR_IPC, "pledge");
 
        /* Create a bidirectional socket and start our child. */
 
        if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_NONBLOCK, 0, fds) == -1)
-               err(1, "socketpair");
+               err(ERR_IPC, "socketpair");
 
        switch ((child = fork())) {
        case -1:
-               err(1, "fork");
+               err(ERR_IPC, "fork");
        case 0:
                close(fds[0]);
                if (pledge("stdio exec", NULL) == -1)
-                       err(1, "pledge");
+                       err(ERR_IPC, "pledge");
 
                memset(&sess, 0, sizeof(struct sess));
                sess.opts = &opts;
 
-               if ((args = fargs_cmdline(&sess, fargs, NULL)) == NULL) {
-                       ERRX1("fargs_cmdline");
-                       _exit(1);
-               }
+               args = fargs_cmdline(&sess, fargs, NULL);
 
                for (i = 0; args[i] != NULL; i++)
                        LOG2("exec[%d] = %s", i, args[i]);
 
                /* Make sure the child's stdin is from the sender. */
-               if (dup2(fds[1], STDIN_FILENO) == -1) {
-                       ERR("dup2");
-                       _exit(1);
-               }
-               if (dup2(fds[1], STDOUT_FILENO) == -1) {
-                       ERR("dup2");
-                       _exit(1);
-               }
+               if (dup2(fds[1], STDIN_FILENO) == -1)
+                       err(ERR_IPC, "dup2");
+               if (dup2(fds[1], STDOUT_FILENO) == -1)
+                       err(ERR_IPC, "dup2");
                execvp(args[0], args);
-               _exit(1);
+               _exit(ERR_IPC);
                /* NOTREACHED */
        default:
                close(fds[1]);
@@ -511,7 +507,7 @@ main(int argc, char *argv[])
        close(fds[0]);
 
        if (waitpid(child, &st, 0) == -1)
-               err(1, "waitpid");
+               err(ERR_WAITPID, "waitpid");
 
        /*
         * If we don't already have an error (rc == 0), then inherit the
@@ -519,10 +515,14 @@ main(int argc, char *argv[])
         * If it hasn't exited, it overrides our return value.
         */
 
-       if (WIFEXITED(st) && rc == 0)
-               rc = WEXITSTATUS(st);
-       else if (!WIFEXITED(st))
-               rc = 1;
+       if (rc == 0) {
+               if (WIFEXITED(st))
+                       rc = WEXITSTATUS(st);
+               else if (WIFSIGNALED(st))
+                       rc = ERR_TERMIMATED;
+               else
+                       rc = ERR_WAITPID;
+       }
 
        exit(rc);
 usage:
@@ -532,5 +532,5 @@ usage:
            "[--rsync-path=program]\n\t[--timeout=seconds] [--version] "
             "source ... directory\n",
            getprogname());
-       exit(1);
+       exit(ERR_SYNTAX);
 }
Index: misc.c
===================================================================
RCS file: /cvs/src/usr.bin/rsync/misc.c,v
retrieving revision 1.2
diff -u -p -r1.2 misc.c
--- misc.c      22 Mar 2021 11:14:42 -0000      1.2
+++ misc.c      7 May 2021 08:19:17 -0000
@@ -45,7 +45,7 @@ addargs(arglist *args, const char *fmt, 
        r = vasprintf(&cp, fmt, ap);
        va_end(ap);
        if (r == -1)
-               err(1, "addargs: argument too long");
+               err(ERR_NOMEM, "addargs: argument too long");
 
        nalloc = args->nalloc;
        if (args->list == NULL) {
@@ -54,9 +54,10 @@ addargs(arglist *args, const char *fmt, 
        } else if (args->num+2 >= nalloc)
                nalloc *= 2;
 
-       args->list = recallocarray(args->list, args->nalloc, nalloc, 
sizeof(char *));
+       args->list = recallocarray(args->list, args->nalloc, nalloc,
+           sizeof(char *));
        if (!args->list)
-               err(1, "malloc");
+               err(ERR_NOMEM, NULL);
        args->nalloc = nalloc;
        args->list[args->num++] = cp;
        args->list[args->num] = NULL;
Index: mkpath.c
===================================================================
RCS file: /cvs/src/usr.bin/rsync/mkpath.c,v
retrieving revision 1.4
diff -u -p -r1.4 mkpath.c
--- mkpath.c    8 May 2019 21:30:11 -0000       1.4
+++ mkpath.c    7 May 2021 08:19:55 -0000
@@ -46,28 +46,34 @@ mkpath(char *path)
 {
        struct stat sb;
        char *slash;
-       int done = 0;
+       int done;
 
        slash = path;
 
-       while (!done) {
+       for (;;) {
                slash += strspn(slash, "/");
                slash += strcspn(slash, "/");
 
                done = (*slash == '\0');
                *slash = '\0';
 
-               if (stat(path, &sb)) {
-                       if (errno != ENOENT || (mkdir(path, 0777) &&
-                           errno != EEXIST)) {
-                               ERR("%s: stat", path);
+               if (mkdir(path, 0777) != 0) {
+                       int mkdir_errno = errno;
+
+                       if (stat(path, &sb) == -1) {
+                               /* Not there; use mkdir()s errno */
+                               errno = mkdir_errno;
+                               return (-1);
+                       }
+                       if (!S_ISDIR(sb.st_mode)) {
+                               /* Is there, but isn't a directory */
+                               errno = ENOTDIR;
                                return (-1);
                        }
-               } else if (!S_ISDIR(sb.st_mode)) {
-                       errno = ENOTDIR;
-                       ERR("%s: stat", path);
-                       return (-1);
                }
+
+               if (done)
+                       break;
 
                *slash = '/';
        }
Index: receiver.c
===================================================================
RCS file: /cvs/src/usr.bin/rsync/receiver.c,v
retrieving revision 1.26
diff -u -p -r1.26 receiver.c
--- receiver.c  6 May 2021 07:29:59 -0000       1.26
+++ receiver.c  7 May 2021 08:21:48 -0000
@@ -20,6 +20,7 @@
 #include <sys/stat.h>
 
 #include <assert.h>
+#include <err.h>
 #include <errno.h>
 #include <fcntl.h>
 #include <inttypes.h>
@@ -180,15 +181,12 @@ rsync_receiver(struct sess *sess, int fd
        struct upload   *ul = NULL;
        mode_t           oumask;
 
-       if (pledge("stdio unix rpath wpath cpath dpath fattr chown getpw 
unveil", NULL) == -1) {
-               ERR("pledge");
-               goto out;
-       }
+       if (pledge("stdio unix rpath wpath cpath dpath fattr chown getpw 
unveil", NULL) == -1)
+               err(ERR_IPC, "pledge");
 
        /* Client sends zero-length exclusions. */
 
-       if (!sess->opts->server &&
-            !io_write_int(sess, fdout, 0)) {
+       if (!sess->opts->server && !io_write_int(sess, fdout, 0)) {
                ERRX1("io_write_int");
                goto out;
        }
@@ -240,14 +238,10 @@ rsync_receiver(struct sess *sess, int fd
         */
 
        if (!sess->opts->dry_run) {
-               if ((tofree = strdup(root)) == NULL) {
-                       ERR("strdup");
-                       goto out;
-               } else if (mkpath(tofree) < 0) {
-                       ERRX1("%s: mkpath", root);
-                       free(tofree);
-                       goto out;
-               }
+               if ((tofree = strdup(root)) == NULL)
+                       err(ERR_NOMEM, NULL);
+               if (mkpath(tofree) < 0)
+                       err(ERR_FILE_IO, "%s: mkpath", tofree);
                free(tofree);
        }
 
@@ -260,10 +254,8 @@ rsync_receiver(struct sess *sess, int fd
 
        if (!sess->opts->dry_run) {
                dfd = open(root, O_RDONLY | O_DIRECTORY, 0);
-               if (dfd == -1) {
-                       ERR("%s: open", root);
-                       goto out;
-               }
+               if (dfd == -1)
+                       err(ERR_FILE_IO, "%s: open", root);
        }
 
        /*
@@ -285,13 +277,10 @@ rsync_receiver(struct sess *sess, int fd
         * writing into other parts of the file-system.
         */
 
-       if (unveil(root, "rwc") == -1) {
-               ERR("%s: unveil", root);
-               goto out;
-       } else if (unveil(NULL, NULL) == -1) {
-               ERR("%s: unveil", root);
-               goto out;
-       }
+       if (unveil(root, "rwc") == -1)
+               err(ERR_IPC, "%s: unveil", root);
+       if (unveil(NULL, NULL) == -1)
+               err(ERR_IPC, "unveil");
 
        /* If we have a local set, go for the deletion. */
 
Index: server.c
===================================================================
RCS file: /cvs/src/usr.bin/rsync/server.c,v
retrieving revision 1.12
diff -u -p -r1.12 server.c
--- server.c    8 May 2019 21:30:11 -0000       1.12
+++ server.c    7 May 2021 08:29:14 -0000
@@ -57,7 +57,7 @@ rsync_server(const struct opts *opts, si
 
        if (pledge("stdio unix rpath wpath cpath dpath fattr chown getpw 
unveil",
            NULL) == -1)
-               err(1, "pledge");
+               err(ERR_IPC, "pledge");
 
        memset(&sess, 0, sizeof(struct sess));
        sess.opts = opts;
Index: socket.c
===================================================================
RCS file: /cvs/src/usr.bin/rsync/socket.c,v
retrieving revision 1.29
diff -u -p -r1.29 socket.c
--- socket.c    31 Mar 2021 19:45:16 -0000      1.29
+++ socket.c    7 May 2021 08:29:20 -0000
@@ -281,7 +281,7 @@ rsync_connect(const struct opts *opts, i
 
        if (pledge("stdio unix rpath wpath cpath dpath inet fattr chown dns 
getpw unveil",
            NULL) == -1)
-               err(1, "pledge");
+               err(ERR_IPC, "pledge");
 
        memset(&sess, 0, sizeof(struct sess));
        sess.opts = opts;
@@ -365,7 +365,7 @@ rsync_socket(const struct opts *opts, in
 
        if (pledge("stdio unix rpath wpath cpath dpath fattr chown getpw 
unveil",
            NULL) == -1)
-               err(1, "pledge");
+               err(ERR_IPC, "pledge");
 
        memset(&sess, 0, sizeof(struct sess));
        sess.lver = RSYNC_PROTOCOL;
@@ -374,10 +374,7 @@ rsync_socket(const struct opts *opts, in
        assert(f->host != NULL);
        assert(f->module != NULL);
 
-       if ((args = fargs_cmdline(&sess, f, &skip)) == NULL) {
-               ERRX1("fargs_cmdline");
-               exit(1);
-       }
+       args = fargs_cmdline(&sess, f, &skip);
 
        /* Initiate with the rsyncd version and module request. */
 

Reply via email to