From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.4 (2020-01-24) on atuin.qyliss.net X-Spam-Level: X-Spam-Status: No, score=-4.5 required=3.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,HEADER_FROM_DIFFERENT_DOMAINS, MAILING_LIST_MULTI,RCVD_IN_DNSWL_LOW,RCVD_IN_MSPIKE_H3, RCVD_IN_MSPIKE_WL,SPF_HELO_PASS autolearn=unavailable autolearn_force=no version=3.4.4 Received: by atuin.qyliss.net (Postfix, from userid 496) id 61ED48686; Fri, 19 Mar 2021 03:17:48 +0000 (UTC) Received: from [127.0.0.1] (localhost [IPv6:::1]) by atuin.qyliss.net (Postfix) with ESMTP id 372FB855F; Fri, 19 Mar 2021 03:17:32 +0000 (UTC) Received: by atuin.qyliss.net (Postfix, from userid 496) id 94CDF85F8; Fri, 19 Mar 2021 03:17:30 +0000 (UTC) Received: from out4-smtp.messagingengine.com (out4-smtp.messagingengine.com [66.111.4.28]) by atuin.qyliss.net (Postfix) with ESMTPS id 4626485F6 for ; Fri, 19 Mar 2021 03:17:27 +0000 (UTC) Received: from compute4.internal (compute4.nyi.internal [10.202.2.44]) by mailout.nyi.internal (Postfix) with ESMTP id 48E145C00FF for ; Thu, 18 Mar 2021 23:17:26 -0400 (EDT) Received: from mailfrontend2 ([10.202.2.163]) by compute4.internal (MEProxy); Thu, 18 Mar 2021 23:17:26 -0400 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=alyssa.is; h= from:to:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; s=fm2; bh=shsW+e+5GSEDW QqG+/2aDh99FfJwWk+HEdjo65y/Ij8=; b=Iuy9o+eoFdwXpTBvD00eyzLh3PGoU 43cZXJCd8TCtjAxwbiADIR97V+9ifNTwHmtxSRO4EF+5CJh9Hq93Mp5/Ceo3lslr PpiMOfWMcz8a3w/WD8LvzrvdIvj6j/SqGT7V8Lhv239Cnfc7obxMDN/fxxW3zOth zhbVoJgHsRYlPIuqKSwyV+LkO1FUwzNT+wKmeWwn2h9Mi8amBv2nUoIBBtVVc7Wr F3314SuDs7tmThIbzVTYUhWg+9OIc12Ou8bzox5msWkQwveoVn70irNIFsAY1kCi MNRVg++eswCS7+WDQBGJML1atah/3u742sE/pbjD7fCO7uNW0VLyPdimg== DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d= messagingengine.com; h=content-transfer-encoding:date:from :in-reply-to:message-id:mime-version:references:subject:to :x-me-proxy:x-me-proxy:x-me-sender:x-me-sender:x-sasl-enc; s= fm2; bh=shsW+e+5GSEDWQqG+/2aDh99FfJwWk+HEdjo65y/Ij8=; b=wdTZSAKg JI6+c27LoBAoTMDgJZZQ+Bu7yQrlTyJpbi8XblEKpS2E4DqJ4peY2tXzNpx1WSUn oTEYgc+SpWiqa5E+3GfNUGe+madVljmQQgNO4v30vmDm98IJ+Qa/yLyzcoBSObug I4Nyy7qMwltOE+gKjA0ubGCNCxaaP4SinEcTe/HJCx5C5rdjsaF1EYRHgA/o9Jlo ABm8RvAk3ABX+OCZBIc24LKTEAZ1QllLdd8QSnMh84rQB130JlXoRJ6AB/1+ARHs nOpBViEKjYDHeHvNH7N9Fj3scFch1CvHBD13pS/0QDAwKX9SvShXh3uVRcyUU5TM IPsKptAvfDCLog== X-ME-Sender: X-ME-Proxy-Cause: gggruggvucftvghtrhhoucdtuddrgeduledrudefjedgheeiucetufdoteggodetrfdotf fvucfrrhhofhhilhgvmecuhfgrshhtofgrihhlpdfqfgfvpdfurfetoffkrfgpnffqhgen uceurghilhhouhhtmecufedttdenucenucfjughrpefhvffufffkofgjfhgggfestdekre dtredttdenucfhrhhomheptehlhihsshgrucftohhsshcuoehhihesrghlhihsshgrrdhi sheqnecuggftrfgrthhtvghrnhepueelieejffdvheeigeeffeeugeffhfehhfekudfgte duledtvdehvdfhtddtfeelnecuffhomhgrihhnpehskhgrrhhnvghtrdhorhhgnecukfhp peekgedrudekgedrvdefvddrudektdenucevlhhushhtvghrufhiiigvpedtnecurfgrrh grmhepmhgrihhlfhhrohhmpehqhihlihhsshesgidvvddtrdhqhihlihhsshdrnhgvth X-ME-Proxy: Received: from x220.qyliss.net (p54b8e8b4.dip0.t-ipconnect.de [84.184.232.180]) by mail.messagingengine.com (Postfix) with ESMTPA id 692111080057 for ; Thu, 18 Mar 2021 23:17:25 -0400 (EDT) Received: by x220.qyliss.net (Postfix, from userid 1000) id 762722000; Fri, 19 Mar 2021 03:17:24 +0000 (UTC) From: Alyssa Ross To: devel@spectrum-os.org Subject: [PATCH ucspi-vsock 7/7] Extract vsockserver-socketbinder and vsockserverd Date: Fri, 19 Mar 2021 03:17:13 +0000 Message-Id: <20210319031713.23600-1-hi@alyssa.is> X-Mailer: git-send-email 2.30.0 In-Reply-To: <20210319025349.8839-2-hi@alyssa.is> References: <20210319025349.8839-2-hi@alyssa.is> MIME-Version: 1.0 Content-Transfer-Encoding: 8bit Message-ID-Hash: 7ZPQOWGJAEIJQJAZXYZTNALGM3ZNYS7Q X-Message-ID-Hash: 7ZPQOWGJAEIJQJAZXYZTNALGM3ZNYS7Q X-MailFrom: qyliss@x220.qyliss.net X-Mailman-Rule-Misses: dmarc-mitigation; no-senders; approved; emergency; loop; banned-address; member-moderation; header-match-config-1; nonmember-moderation; administrivia; implicit-dest; max-recipients; max-size; news-moderation; no-subject; suspicious-header X-Mailman-Version: 3.3.1 Precedence: list List-Id: Patches and low-level development discussion Archived-At: List-Archive: List-Help: List-Post: List-Subscribe: List-Unsubscribe: vsockserver-socketbinder creates and listens on a socket, and vsockserverd accepts connections and sets up file descriptors. vsockserver previously did both of these things in one big program, but now it just sets up a command line to run vsockserver-socketbinder followed by vsockserverd. Having two seperate programs allows one program to be used in situations where the other is not suitable (e.g. using vsockserver-socketbinder to create a socket in situations where accept behaviour more complex than vsockserverd can provide is required). This design is taken from s6[1], which uses the same design for its s6-ipcserver, s6-ipcserver-socketbinder, and s6-ipcserverd programs. [1]: https://skarnet.org/software/s6/ --- Because vsockserver.c is completely rewritten in this patch, the diff generated by git was a bit confusing, so the diff here has been lovingly hand-edited to group the hunks together more and make it easy to see the new code all at once. Being able to do this is one of the nice things about email patches. ;) The new implementations are much more thoroughly commented. Think that's my new style. :) .gitignore | 2 + Makefile.in | 14 ++- vsockserver-socketbinder.c | 86 ++++++++++++++++++ vsockserver.c | 149 ++++++++++++++------------------ vsockserver.c => vsockserverd.c | 65 ++++++-------- 5 files changed, 186 insertions(+), 130 deletions(-) create mode 100644 vsockserver-socketbinder.c copy vsockserver.c => vsockserverd.c (64%) diff --git a/.gitignore b/.gitignore index 89a0408..d29518a 100644 --- a/.gitignore +++ b/.gitignore @@ -4,6 +4,8 @@ *.o *.tmp vsockclient +vsockserver-socketbinder +vsockserverd vsockserver config.h Makefile diff --git a/Makefile.in b/Makefile.in index 3260e85..0e65f1f 100644 --- a/Makefile.in +++ b/Makefile.in @@ -10,7 +10,7 @@ INSTALL_PROGRAM = $(INSTALL) prefix = @PREFIX@ bindir = @BINDIR@ -PROGRAMS = vsockclient vsockserver +PROGRAMS = vsockclient vsockserver-socketbinder vsockserverd vsockserver all: $(PROGRAMS) .PHONY: all @@ -26,11 +26,17 @@ config.h: configure vsockclient: vsockclient.o env.o log.o num.o vsock.o $(CC) $(LDFLAGS) -o $@ $@.o env.o log.o num.o vsock.o $(LDLIBS) -vsockserver: vsockserver.o env.o log.o num.o vsock.o - $(CC) $(LDFLAGS) -o $@ $@.o env.o log.o num.o vsock.o $(LDLIBS) +vsockserver-socketbinder: vsockserver-socketbinder.o log.o num.o vsock.o + $(CC) $(LDFLAGS) -o $@ $@.o log.o num.o vsock.o $(LDLIBS) +vsockserverd: vsockserverd.o env.o log.o vsock.o + $(CC) $(LDFLAGS) -o $@ $@.o env.o log.o vsock.o $(LDLIBS) +vsockserver: vsockserver.o exec.o log.o + $(CC) $(LDFLAGS) -o $@ $@.o exec.o log.o $(LDLIBS) vsockclient.o: env.h log.h num.h vsock.h -vsockserver.o: env.h log.h num.h vsock.h +vsockserver-socketbinder.o: log.h num.h vsock.h +vsockserverd.o: env.h log.h vsock.h +vsockserver.o: config.h exec.h log.h clean: rm -f $(PROGRAMS) *.o diff --git a/vsockserver-socketbinder.c b/vsockserver-socketbinder.c new file mode 100644 index 0000000..598c01c --- /dev/null +++ b/vsockserver-socketbinder.c @@ -0,0 +1,86 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +// SPDX-FileCopyrightText: 2020-2021 Alyssa Ross + +#define _GNU_SOURCE + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "log.h" +#include "num.h" +#include "vsock.h" + +noreturn static void ex_usage(void) +{ + if (verbosity) + fprintf(stderr, "Usage: %s cid port prog...\n", + program_invocation_short_name); + exit(EX_USAGE); +} + +int main(int argc, char *argv[]) +{ + int opt, fd; + uint32_t cid, port; + + // A skeleton of an option parser to reject any options that + // are given, so we can add options in the future without + // worrying about breaking backwards compatibility because + // they were previously interpreted as a first argument. + while ((opt = getopt(argc, argv, "+")) != -1) { + switch (opt) { + default: + ex_usage(); + } + } + + // Check there are enough positional arguments (two for the + // address and at least one to exec into). + if (optind > argc - 3) + ex_usage(); + + // Parse the `cid' argument. + if (!strcmp(argv[optind], "-1")) + cid = VMADDR_CID_ANY; + else if (getu32(argv[optind], 0, UINT32_MAX, &cid)) + ex_usage(); + optind++; + + // Parse the `port' argument. + if (!strcmp(argv[optind], "-1")) + port = VMADDR_PORT_ANY; + else if (getu32(argv[optind], 0, UINT32_MAX, &port)) + ex_usage(); + optind++; + + // Set up the socket. + fd = socket(AF_VSOCK, SOCK_STREAM, 0); + if (fd == -1) + diee(EX_OSERR, "socket"); + if (fcntl(fd, F_SETFL, O_NONBLOCK) == -1) + diee(EX_OSERR, "fcntl"); + if (vsock_bind(fd, cid, port) == -1) + diee(EX_OSERR, "bind"); + if (listen(fd ,40) == -1) + diee(EX_OSERR, "listen"); + + // Place the socket at stdout. + if (dup2(fd, STDIN_FILENO) == -1) + diee(EX_OSERR, "dup2"); + if (fd != STDIN_FILENO) + close(fd); + + // Finally, exec into `prog'. + execvp(argv[optind], &argv[optind]); + diee(EX_OSERR, "execvp"); +} diff --git a/vsockserver.c b/vsockserver.c index 43307d2..f740a8a 100644 --- a/vsockserver.c +++ b/vsockserver.c @@ -1,28 +1,20 @@ // SPDX-License-Identifier: GPL-2.0-or-later -// SPDX-FileCopyrightText: 2020-2021 Alyssa Ross +// SPDX-FileCopyrightText: 2021 Alyssa Ross #define _GNU_SOURCE +#include #include -#include -#include -#include #include #include #include #include -#include -#include -#include #include #include -#include - -#include "env.h" +#include "config.h" +#include "exec.h" #include "log.h" -#include "num.h" -#include "vsock.h" noreturn static void ex_usage(void) { @@ -34,102 +26,87 @@ noreturn static void ex_usage(void) int main(int argc, char *argv[]) { - bool notify = false; int opt; - pid_t child; - uint32_t lcid, lport, rcid, rport; + 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': - notify = true; - break; 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 enough positional arguments (two for the - // address and at least one to exec into). + // 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(); - if (!strcmp(argv[optind], "-1")) - lcid = VMADDR_CID_ANY; - else if (getu32(argv[optind], 0, UINT32_MAX, &lcid)) - ex_usage(); - optind++; - - if (!strcmp(argv[optind], "-1")) - lport = VMADDR_PORT_ANY; - else if (getu32(argv[optind], 0, UINT32_MAX, &lport)) - ex_usage(); - optind++; - - int fd = socket(AF_VSOCK, SOCK_STREAM, 0); - if (fd == -1) - diee(EX_OSERR, "socket"); - - if (fcntl(fd, F_SETFL, O_NONBLOCK) == -1) - diee(EX_OSERR, "fcntl"); - - if (vsock_bind(fd, lcid, lport) == -1) - diee(EX_OSERR, "bind"); - - if (listen(fd, 40) == -1) - diee(EX_OSERR, "listen"); - - if (lport == VMADDR_PORT_ANY) - if (vsock_get_cid_and_port(fd, NULL, &lport) == -1) - diee(EX_OSERR, "getsockname"); - - if (notify) { - printf("%" PRIu32 "\n", lport); - fclose(stdout); - } - - setenvf("VSOCKLOCALCID", 1, "%" PRIu32, lcid); - setenvf("VSOCKLOCALPORT", 1, "%" PRIu32, lport); - - ilog("listening as %" PRIu32 " on port %" PRIu32, lcid, lport); - - struct pollfd poll_fd = { .fd = fd, .events = POLL_IN, .revents = 0 }; - while (poll(&poll_fd, 1, -1) != -1) { - // On Linux, conn will be blocking. On other - // platforms, this may not be the case. If other - // platforms are to be supported, we'd probably want - // to set O_NONBLOCK here. - int conn = vsock_accept(fd, &rcid, &rport); - if (conn == -1) - diee(EX_OSERR, "accept"); - - setenvf("VSOCKREMOTECID", 1, "%" PRIu32, rcid); - setenvf("VSOCKREMOTEPORT", 1, "%" PRIu32, rport); - - ilog("connection from %" PRIu32 " port %" PRIu32, rcid, rport); - - switch (child = fork()) { - case -1: diee(EX_OSERR, "fork"); - case 0: - if (dup2(conn, STDIN_FILENO) == -1) - diee(EX_OSERR, "dup2"); - if (dup2(conn, STDOUT_FILENO) == -1) - diee(EX_OSERR, "dup2"); - if (conn != STDIN_FILENO && conn != STDOUT_FILENO) - close(conn); - execvp(argv[optind], &argv[optind]); - diee(EX_OSERR, "exec"); - } - - if (waitpid(child, NULL, 0) == -1) - diee(EX_OSERR, "waitpid"); - - close(conn); - } - diee(EX_OSERR, "poll"); + // 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; + + // 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"); } diff --git a/vsockserver.c b/vsockserverd.c similarity index 64% copy from vsockserver.c copy to vsockserverd.c index 43307d2..1abfff9 100644 --- a/vsockserver.c +++ b/vsockserverd.c @@ -4,30 +4,25 @@ #define _GNU_SOURCE #include -#include #include #include #include +#include #include #include #include -#include -#include #include #include #include -#include - #include "env.h" #include "log.h" -#include "num.h" #include "vsock.h" noreturn static void ex_usage(void) { if (verbosity) - fprintf(stderr, "Usage: %s [ -1 ] [ -q | -Q | -v ] cid port prog...\n", + fprintf(stderr, "Usage: %s [ -1 ] [ -q | -Q | -v ] prog...\n", program_invocation_short_name); exit(EX_USAGE); } @@ -38,6 +33,8 @@ int main(int argc, char *argv[]) int opt; pid_t child; uint32_t lcid, lport, rcid, rport; + struct pollfd poll_fd = + { .fd = STDIN_FILENO, .events = POLLIN, .revents = 0 }; while ((opt = getopt(argc, argv, "+1qQv")) != -1) { switch (opt) { @@ -54,60 +51,42 @@ int main(int argc, char *argv[]) } } - // Check there are enough positional arguments (two for the - // address and at least one to exec into). - if (optind > argc - 3) + // Check that there is something for us to exec into. + if (optind > argc - 1) ex_usage(); - if (!strcmp(argv[optind], "-1")) - lcid = VMADDR_CID_ANY; - else if (getu32(argv[optind], 0, UINT32_MAX, &lcid)) - ex_usage(); - optind++; - - if (!strcmp(argv[optind], "-1")) - lport = VMADDR_PORT_ANY; - else if (getu32(argv[optind], 0, UINT32_MAX, &lport)) - ex_usage(); - optind++; - - int fd = socket(AF_VSOCK, SOCK_STREAM, 0); - if (fd == -1) - diee(EX_OSERR, "socket"); - - if (fcntl(fd, F_SETFL, O_NONBLOCK) == -1) - diee(EX_OSERR, "fcntl"); - - if (vsock_bind(fd, lcid, lport) == -1) - diee(EX_OSERR, "bind"); - - if (listen(fd, 40) == -1) - diee(EX_OSERR, "listen"); - - if (lport == VMADDR_PORT_ANY) - if (vsock_get_cid_and_port(fd, NULL, &lport) == -1) - diee(EX_OSERR, "getsockname"); + // Find out the CID and port of the socket. + if (vsock_get_cid_and_port(STDIN_FILENO, &lcid, &lport)) { + if (errno == ENOTSOCK || errno == EPROTOTYPE) + die(EX_USAGE, "stdin is not an AF_VSOCK socket"); + diee(EX_OSERR, "getsockname"); + } + // Notify the user of our port and indicate readiness. if (notify) { printf("%" PRIu32 "\n", lport); fclose(stdout); } + // Set our CID and port as environment variables so `prog' can + // know them. setenvf("VSOCKLOCALCID", 1, "%" PRIu32, lcid); setenvf("VSOCKLOCALPORT", 1, "%" PRIu32, lport); ilog("listening as %" PRIu32 " on port %" PRIu32, lcid, lport); - struct pollfd poll_fd = { .fd = fd, .events = POLL_IN, .revents = 0 }; + // Wait for a connection. while (poll(&poll_fd, 1, -1) != -1) { // On Linux, conn will be blocking. On other // platforms, this may not be the case. If other // platforms are to be supported, we'd probably want // to set O_NONBLOCK here. - int conn = vsock_accept(fd, &rcid, &rport); + int conn = vsock_accept(STDIN_FILENO, &rcid, &rport); if (conn == -1) diee(EX_OSERR, "accept"); + // Set the client's CID and port as environment + // variables so `prog' can know them. setenvf("VSOCKREMOTECID", 1, "%" PRIu32, rcid); setenvf("VSOCKREMOTEPORT", 1, "%" PRIu32, rport); @@ -116,12 +95,18 @@ int main(int argc, char *argv[]) switch (child = fork()) { case -1: diee(EX_OSERR, "fork"); case 0: + // Set up the connection socket on prog's + // stdin and stdout. This has the happy side + // effect of closing the listening socket in + // the child, since it's also on stdin. if (dup2(conn, STDIN_FILENO) == -1) diee(EX_OSERR, "dup2"); if (dup2(conn, STDOUT_FILENO) == -1) diee(EX_OSERR, "dup2"); if (conn != STDIN_FILENO && conn != STDOUT_FILENO) close(conn); + + // exec into `prog'. execvp(argv[optind], &argv[optind]); diee(EX_OSERR, "exec"); } -- 2.30.0