patches and low-level development discussion
 help / color / mirror / code / Atom feed
From: Alyssa Ross <hi@alyssa.is>
To: devel@spectrum-os.org
Cc: Yureka <yuka@yuka.dev>
Subject: [PATCH 7/8] release/checks/integration: extract library
Date: Fri, 13 Jun 2025 16:05:07 +0200	[thread overview]
Message-ID: <20250613140508.76998-8-hi@alyssa.is> (raw)
In-Reply-To: <20250613140508.76998-1-hi@alyssa.is>

This will ease writing more tests.  The drain stuff could probably
also be extracted, but the second test I'm currently writing won't use
it, so I'm leaving it for now.

Signed-off-by: Alyssa Ross <hi@alyssa.is>
---
 .../integration/{networking.c => lib.c}       | 139 +----------
 release/checks/integration/lib.h              |  16 ++
 release/checks/integration/meson.build        |   4 +-
 release/checks/integration/networking.c       | 216 +-----------------
 4 files changed, 26 insertions(+), 349 deletions(-)
 copy release/checks/integration/{networking.c => lib.c} (61%)
 create mode 100644 release/checks/integration/lib.h

diff --git a/release/checks/integration/networking.c b/release/checks/integration/lib.c
similarity index 61%
copy from release/checks/integration/networking.c
copy to release/checks/integration/lib.c
index 7331c4a..b0b4507 100644
--- a/release/checks/integration/networking.c
+++ b/release/checks/integration/lib.c
@@ -1,20 +1,15 @@
 // SPDX-License-Identifier: EUPL-1.2+
 // SPDX-FileCopyrightText: 2025 Alyssa Ross <hi@alyssa.is>
 
-#include <errno.h>
-#include <pthread.h>
+#include "lib.h"
+
 #include <sched.h>
 #include <signal.h>
-#include <stdint.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 #include <unistd.h>
 
-#include <arpa/inet.h>
-#include <net/if.h>
-
-#include <sys/ioctl.h>
 #include <sys/prctl.h>
 #include <sys/socket.h>
 #include <sys/un.h>
@@ -38,31 +33,6 @@ static void exit_on_sigchld(void)
 	exit(EXIT_FAILURE);
 }
 
-static void *drain(void *arg)
-{
-	int c;
-
-	while ((c = fgetc((FILE *)arg)) != EOF)
-		fputc(c, stderr);
-
-	if (ferror((FILE *)arg))
-		return (void *)(intptr_t)errno;
-
-	return nullptr;
-}
-
-static pthread_t start_drain(FILE *console)
-{
-	pthread_t thread;
-	int e;
-
-	if (!(e = pthread_create(&thread, nullptr, drain, console)))
-		return thread;
-
-	fprintf(stderr, "pthread_create: %s\n", strerror(e));
-	exit(EXIT_FAILURE);
-}
-
 static char *make_tmp_dir(void)
 {
 	char *dir, *run;
@@ -81,48 +51,6 @@ static char *make_tmp_dir(void)
 	return dir;
 }
 
-static int setup_server(void)
-{
-	int fd;
-	struct ifreq ifr;
-
-	struct sockaddr_in addr = {
-		.sin_family = AF_INET,
-		.sin_port = htons(1234),
-		.sin_addr = { .s_addr = htonl(INADDR_LOOPBACK) },
-	};
-
-	sprintf(ifr.ifr_name, "lo");
-
-	if ((fd = socket(AF_INET, SOCK_STREAM|SOCK_CLOEXEC, 0)) == -1) {
-		perror("socket");
-		exit(EXIT_FAILURE);
-	}
-
-	if (ioctl(fd, SIOCGIFFLAGS, &ifr) == -1) {
-		perror("SIOCGIFFLAGS");
-		exit(EXIT_FAILURE);
-	}
-
-	ifr.ifr_flags |= IFF_UP;
-	if (ioctl(fd, SIOCSIFFLAGS, &ifr) == -1) {
-		perror("SIOCSIFFLAGS");
-		exit(EXIT_FAILURE);
-	}
-
-	if (bind(fd, &addr, sizeof addr) == -1) {
-		perror("bind");
-		exit(EXIT_FAILURE);
-	}
-
-	if (listen(fd, 1) == -1) {
-		perror("listen");
-		exit(EXIT_FAILURE);
-	}
-
-	return fd;
-}
-
 static int setup_unix(const char *path)
 {
 	int r, fd;
@@ -176,15 +104,7 @@ static void wait_for_prompt(FILE *console)
 	exit(EXIT_FAILURE);
 }
 
-struct config {
-	const char *run_qemu;
-
-	struct {
-		const char *efi, *img, *user_data;
-	} drives;
-};
-
-static FILE *start_qemu(struct config c)
+FILE *start_qemu(struct config c)
 {
 	FILE *console;
 	struct utsname u;
@@ -259,36 +179,6 @@ static FILE *start_qemu(struct config c)
 	return console;
 }
 
-static void expect_connection(int listener)
-{
-	int conn_fd;
-	FILE *conn;
-	char msg[7];
-	size_t len;
-
-	fputs("waiting for server connection\n", stderr);
-	if ((conn_fd = accept(listener, nullptr, nullptr)) == -1) {
-		perror("accept");
-		exit(EXIT_FAILURE);
-	}
-	fputs("accepted connection!\n", stderr);
-	if (!(conn = fdopen(conn_fd, "r"))) {
-		perror("fdopen(server connection)");
-		exit(EXIT_FAILURE);
-	}
-
-	len = fread(msg, 1, sizeof msg, conn);
-	if (len != 6 || memcmp("hello\n", msg, 6)) {
-		if (ferror(conn))
-			perror("fread(server connection)");
-		else
-			fprintf(stderr, "unexpected connection data: %.*s",
-			        (int)len, msg);
-		exit(EXIT_FAILURE);
-	}
-	fclose(conn);
-}
-
 int main(int argc, char *argv[])
 {
 	exit_on_sigchld();
@@ -323,26 +213,5 @@ int main(int argc, char *argv[])
 		exit(EXIT_FAILURE);
 	}
 
-	int server = setup_server();
-
-	FILE *console = start_qemu(c);
-
-	if (fputs("set -euxo pipefail\n"
-	          "mkdir /run/mnt\n"
-	          "mount \"$(findfs UUID=a7834806-2f82-4faf-8ac4-4f8fd8a474ca)\" /run/mnt\n"
-	          "s6-rc -bu change vmm-env\n"
-	          "vm-import user /run/mnt/vms\n"
-	          "vm-start user.nc\n"
-	          "tail -Fc +0 /run/log/current /run/user.nc.log\r\n",
-	          console) == EOF) {
-		fputs("error writing to console\n", stderr);
-		exit(EXIT_FAILURE);
-	}
-	if (fflush(console) == EOF) {
-		perror("fflush");
-		exit(EXIT_FAILURE);
-	}
-	start_drain(console);
-
-	expect_connection(server);
+	test(c);
 }
diff --git a/release/checks/integration/lib.h b/release/checks/integration/lib.h
new file mode 100644
index 0000000..959573d
--- /dev/null
+++ b/release/checks/integration/lib.h
@@ -0,0 +1,16 @@
+// SPDX-License-Identifier: EUPL-1.2+
+// SPDX-FileCopyrightText: 2025 Alyssa Ross <hi@alyssa.is>
+
+#include <stdio.h>
+
+struct config {
+	const char *run_qemu;
+
+	struct {
+		const char *efi, *img, *user_data;
+	} drives;
+};
+
+extern void test(struct config);
+
+FILE *start_qemu(struct config config);
diff --git a/release/checks/integration/meson.build b/release/checks/integration/meson.build
index 3f273b4..7edc314 100644
--- a/release/checks/integration/meson.build
+++ b/release/checks/integration/meson.build
@@ -7,7 +7,9 @@ project('spectrum-integration-tests', 'c',
 
 add_project_arguments('-D_GNU_SOURCE', language : 'c')
 
-test('networking', executable('networking', 'networking.c'),
+lib = static_library('spectrum-integration-test', 'lib.c')
+
+test('networking', executable('networking', 'networking.c', link_with : lib),
   args : [
     find_program('../../../scripts/run-qemu.sh'),
     get_option('efi'),
diff --git a/release/checks/integration/networking.c b/release/checks/integration/networking.c
index 7331c4a..f550edc 100644
--- a/release/checks/integration/networking.c
+++ b/release/checks/integration/networking.c
@@ -1,42 +1,18 @@
 // SPDX-License-Identifier: EUPL-1.2+
 // SPDX-FileCopyrightText: 2025 Alyssa Ross <hi@alyssa.is>
 
+#include "lib.h"
+
 #include <errno.h>
 #include <pthread.h>
-#include <sched.h>
-#include <signal.h>
 #include <stdint.h>
-#include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
-#include <unistd.h>
 
 #include <arpa/inet.h>
 #include <net/if.h>
 
 #include <sys/ioctl.h>
-#include <sys/prctl.h>
-#include <sys/socket.h>
-#include <sys/un.h>
-#include <sys/utsname.h>
-
-static void chld_handler(int)
-{
-	exit(EXIT_FAILURE);
-}
-
-static void exit_on_sigchld(void)
-{
-	struct sigaction sa = {
-		.sa_handler = chld_handler,
-		.sa_flags = SA_NOCLDSTOP,
-	};
-	if (!sigaction(SIGCHLD, &sa, nullptr))
-		return;
-
-	perror("sigaction");
-	exit(EXIT_FAILURE);
-}
 
 static void *drain(void *arg)
 {
@@ -63,24 +39,6 @@ static pthread_t start_drain(FILE *console)
 	exit(EXIT_FAILURE);
 }
 
-static char *make_tmp_dir(void)
-{
-	char *dir, *run;
-	if (!(run = secure_getenv("XDG_RUNTIME_DIR"))) {
-		fputs("warning: XDG_RUNTIME_DIR unset\n", stderr);
-		run = "/tmp";
-	}
-	if (asprintf(&dir, "%s/spectrum-test.XXXXXX", run) == -1) {
-		perror("asprintf");
-		exit(EXIT_FAILURE);
-	}
-	if (!mkdtemp(dir)) {
-		perror("mkdtemp");
-		exit(EXIT_FAILURE);
-	}
-	return dir;
-}
-
 static int setup_server(void)
 {
 	int fd;
@@ -123,142 +81,6 @@ static int setup_server(void)
 	return fd;
 }
 
-static int setup_unix(const char *path)
-{
-	int r, fd;
-	struct sockaddr_un addr = { .sun_family = AF_UNIX };
-
-	r = snprintf(addr.sun_path, sizeof addr.sun_path, "%s", path);
-	if (r >= sizeof addr.sun_path) {
-		fputs("XDG_RUNTIME_DIR too long\n", stderr);
-		exit(EXIT_FAILURE);
-	}
-
-	if (r < 0) {
-		fputs("snprintf error\n", stderr);
-		exit(EXIT_FAILURE);
-	}
-
-	if ((fd = socket(AF_UNIX, SOCK_STREAM|SOCK_CLOEXEC, 0)) == -1) {
-		perror("socket");
-		exit(EXIT_FAILURE);
-	}
-
-	if (bind(fd, &addr, sizeof addr) == -1) {
-		perror("bind");
-		exit(EXIT_FAILURE);
-	}
-
-	if (listen(fd, 1) == -1) {
-		perror("listen");
-		exit(EXIT_FAILURE);
-	}
-
-	return fd;
-}
-
-static void wait_for_prompt(FILE *console)
-{
-	char *needle = "~ # ";
-	size_t i = 0;
-	int c;
-
-	fputs("waiting for character\n", stderr);
-
-	while ((c = fgetc(console)) != EOF) {
-		fputc(c, stderr);
-		i = c == needle[i] ? i + 1 : 0;
-		if (!needle[i])
-			return;
-	}
-
-	fputs("unexpected EOF from console\n", stderr);
-	exit(EXIT_FAILURE);
-}
-
-struct config {
-	const char *run_qemu;
-
-	struct {
-		const char *efi, *img, *user_data;
-	} drives;
-};
-
-static FILE *start_qemu(struct config c)
-{
-	FILE *console;
-	struct utsname u;
-	int console_listener, console_conn;
-	char *arch, *args[] = {
-		(char *)c.run_qemu,
-		"-drive", nullptr,
-		"-drive", nullptr,
-		"-drive", nullptr,
-		"-m", "4G",
-		"-nodefaults",
-		"-machine", "virtualization=on",
-		"-cpu", "max",
-		"-device", "virtio-keyboard",
-		"-device", "virtio-mouse",
-		"-device", "virtio-gpu",
-		"-netdev", "user,id=net0",
-		"-device", "e1000e,netdev=net0",
-		"-monitor", "vc",
-		"-vga", "none",
-		"-smbios", "type=11,value=io.systemd.stub.kernel-cmdline-extra=console=ttyS0",
-		"-serial", "unix:console",
-		nullptr,
-	};
-	char **efi_arg = &args[2], **img_arg = &args[4],
-	     **user_data_arg = &args[6];
-
-	if (!(arch = getenv("ARCH"))) {
-		uname(&u);
-		arch = u.machine;
-	}
-	if (strcmp(arch, "x86_64"))
-		args[sizeof args / sizeof *args - 3] = nullptr;
-
-	console_listener = setup_unix("console");
-
-	switch (fork()) {
-	case -1:
-		perror("fork");
-		exit(EXIT_FAILURE);
-	case 0:
-		if (prctl(PR_SET_PDEATHSIG, SIGTERM) == -1) {
-			perror("prctl");
-			exit(EXIT_FAILURE);
-		}
-
-		if (asprintf(efi_arg, "file=%s,format=raw,if=pflash,readonly=true", c.drives.efi) == -1 ||
-		    asprintf(img_arg, "file=%s,format=raw,if=virtio,readonly=true", c.drives.img) == -1 ||
-		    asprintf(user_data_arg, "file=%s,format=raw,if=virtio,readonly=true", c.drives.user_data) == -1) {
-			perror("asprintf");
-			exit(EXIT_FAILURE);
-		}
-
-		execv(c.run_qemu, args);
-		perror("execv");
-		exit(EXIT_FAILURE);
-	}
-
-	if ((console_conn = accept(console_listener, nullptr, nullptr)) == -1) {
-		perror("accept");
-		exit(EXIT_FAILURE);
-	}
-	if (!(console = fdopen(console_conn, "a+"))) {
-		perror("fdopen");
-		exit(EXIT_FAILURE);
-	}
-
-	fputs("waiting for console prompt\n", stderr);
-
-	wait_for_prompt(console);
-
-	return console;
-}
-
 static void expect_connection(int listener)
 {
 	int conn_fd;
@@ -289,40 +111,8 @@ static void expect_connection(int listener)
 	fclose(conn);
 }
 
-int main(int argc, char *argv[])
+void test(struct config c)
 {
-	exit_on_sigchld();
-
-	if (argc != 5) {
-		fputs("Usage: test efi spectrum user_data\n", stderr);
-		exit(EXIT_FAILURE);
-	}
-
-	struct config c = {
-		.run_qemu = argv[1],
-		.drives = {
-			.efi = argv[2],
-			.img = argv[3],
-			.user_data = argv[4],
-		},
-	};
-
-	if (strchr(c.drives.efi, ',') || strchr(c.drives.img, ',') ||
-	    strchr(c.drives.user_data, ',')) {
-		fputs("arguments contain commas\n", stderr);
-		exit(EXIT_FAILURE);
-	}
-
-	if (unshare(CLONE_NEWUSER|CLONE_NEWNET) == -1) {
-		perror("unshare");
-		exit(EXIT_FAILURE);
-	}
-
-	if (chdir(make_tmp_dir()) == -1) {
-		perror("chdir");
-		exit(EXIT_FAILURE);
-	}
-
 	int server = setup_server();
 
 	FILE *console = start_qemu(c);
-- 
2.49.0


  parent reply	other threads:[~2025-06-13 14:05 UTC|newest]

Thread overview: 15+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2025-06-13 14:05 [PATCH 0/8] Change serial getty instances dynamically Alyssa Ross
2025-06-13 14:05 ` [PATCH 1/8] host/rootfs: poll active consoles for serial getty Alyssa Ross
2025-06-28 19:54   ` Alyssa Ross
2025-06-13 14:05 ` [PATCH 2/8] release/checks/integration: init from networking Alyssa Ross
2025-06-28 19:54   ` Alyssa Ross
2025-06-13 14:05 ` [PATCH 3/8] release/checks/integration: use default Meson timeout Alyssa Ross
2025-06-13 14:05 ` [PATCH 4/8] release/checks/integration: extract config struct Alyssa Ross
2025-06-28 19:54   ` Alyssa Ross
2025-06-13 14:05 ` [PATCH 5/8] release/checks/integration: run from tmpdir Alyssa Ross
2025-06-13 14:05 ` [PATCH 6/8] release/checks/integration: name QEMU arg pointers Alyssa Ross
2025-06-28 19:54   ` Alyssa Ross
2025-06-13 14:05 ` Alyssa Ross [this message]
2025-06-28 19:54   ` [PATCH 7/8] release/checks/integration: extract library Alyssa Ross
2025-06-13 14:05 ` [PATCH 8/8] release/checks/integration: test late serial Alyssa Ross
2025-06-28 19:54   ` Alyssa Ross

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20250613140508.76998-8-hi@alyssa.is \
    --to=hi@alyssa.is \
    --cc=devel@spectrum-os.org \
    --cc=yuka@yuka.dev \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
Code repositories for project(s) associated with this public inbox

	https://spectrum-os.org/git/crosvm
	https://spectrum-os.org/git/doc
	https://spectrum-os.org/git/mktuntap
	https://spectrum-os.org/git/nixpkgs
	https://spectrum-os.org/git/spectrum
	https://spectrum-os.org/git/ucspi-vsock
	https://spectrum-os.org/git/www

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).