// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-FileCopyrightText: 2021 Alyssa Ross #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include "config.h" #include "exec.h" #include "log.h" noreturn static void ex_usage(void) { if (verbosity) fprintf(stderr, "Usage: %s [ -1 ] [ -q | -Q | -v ] cid port prog...\n", program_invocation_short_name); exit(EX_USAGE); } int main(int argc, char *argv[]) { int opt; bool alloc_failed = false; size_t binder_opts_len = 0, daemon_opts_len = 0; char *binder_opts = NULL, *daemon_opts = NULL; // The heavy lifting here is done by vsockserver-socketbinder // and vsockserverd. All this program does is munge argv // appropriately to set the right options for each of those // programs, and then exec into vsockserver-socketbinder. // If allocation fails, we need to keep going until after // we've parsed the arguments, so we know what our verbosity // setting is, and consequently whether we should print an // error message about the allocation failure. alloc_failed |= argz_add(&binder_opts, &binder_opts_len, BINDIR "/vsockserver-socketbinder") || argz_add(&daemon_opts, &daemon_opts_len, BINDIR "/vsockserverd"); while ((opt = getopt(argc, argv, "+1qQv")) != -1) { char *arg = NULL; switch (opt) { case '1': case 'q': case 'Q': case 'v': set_verbosity(opt); alloc_failed |= asprintf(&arg, "-%c", opt) == -1 || argz_add(&daemon_opts, &daemon_opts_len, arg); free(arg); break; default: ex_usage(); } } // Now that verbosity is set, we can whether we were sitting // on an allocation failure. if (alloc_failed) diee(EX_OSERR, "malloc"); // Now we don't have to keep checking alloc_failed before // doing anything, and we can deal with allocation failures // after this point by just terminating immediately. // Check there are `cid' and `port' arguments to pass to // vsockserver-socketbinder, and at least one `prog' argument // to pass to vsockserverd. if (optind > argc - 3) ex_usage(); // Add `cid' and `port' arguments to binder options. if (argz_add(&binder_opts, &binder_opts_len, "--") || argz_add(&binder_opts, &binder_opts_len, argv[optind++]) || argz_add(&binder_opts, &binder_opts_len, argv[optind++])) diee(EX_OSERR, "malloc"); // Add all of daemon_opts onto the end of binder_opts. It's // okay to multiply to find the size because if it would // overflow calloc would have failed earlier. if (argz_append(&binder_opts, &binder_opts_len, daemon_opts, daemon_opts_len)) diee(EX_OSERR, "malloc"); free(daemon_opts); // Append `prog' to binder_opts. Technically this should be // part of daemon_opts, but then we'd just be copying it into // one place to immediately copy it elsewhere. for (int i = optind; i < argc; i++) if (argz_add(&binder_opts, &binder_opts_len, argv[i])) diee(EX_OSERR, "malloc"); if (verbosity == all) { char *opt = 0; // Log the full argv before we exec it. fprintf(stderr, "%s: executing", program_invocation_short_name); while ((opt = argz_next(binder_opts, binder_opts_len, opt))) fprintf(stderr, " %s", opt); fputc('\n', stderr); } execzp(binder_opts, binder_opts, binder_opts_len); diee(EX_OSERR, "execvp"); }