Claudio Jeker([email protected]) on 2021.05.07 12:16:26 +0200:
> 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?
ok benno
> --
> :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. */
>
>