public inbox for passt-dev@passt.top
 help / color / mirror / code / Atom feed
* [PATCH] pasta: Add --pass-fds option to preserve specific file descriptors
@ 2026-07-16 19:03 Richard Lawrence
  2026-07-17  6:14 ` David Gibson
  0 siblings, 1 reply; 2+ messages in thread
From: Richard Lawrence @ 2026-07-16 19:03 UTC (permalink / raw)
  To: passt-dev; +Cc: jbash, Richard Lawrence

Introduce a new long-only command line option '--pass-fds' for
pasta mode. This option accepts a comma-separated list of file
descriptor numbers. It parses, validates, sorts, and deduplicates
these file descriptors, and then skips closing them by calling
close_range() on the gaps between them inside close_open_files().

The option is documented in the usage help and the passt.1 man page.

Co-authored-by: rarensu <40872193+rarensu@users.noreply.github.com>
Co-authored-by: google-labs-jules[bot] <161369871+google-labs-jules[bot]@users.noreply.github.com>
Signed-off-by: Richard Lawrence <rlawrence@tamu.edu>
---
 conf.c  |  9 +++++-
 passt.1 |  5 ++++
 util.c  | 93 ++++++++++++++++++++++++++++++++++++++++++++++++++-------
 3 files changed, 96 insertions(+), 11 deletions(-)

diff --git a/conf.c b/conf.c
index 5b6cc2b..0032d02 100644
--- a/conf.c
+++ b/conf.c
@@ -732,7 +732,9 @@ pasta_opts:
 		"			Don't copy all addresses to namespace\n"
 		"  --ns-mac-addr ADDR	Set MAC address on tap interface\n"
 		"  --no-splice		Disable inbound socket splicing\n"
-		"  --splice-only	Only enable loopback forwarding\n");
+		"  --splice-only	Only enable loopback forwarding\n"
+		"  --pass-fds FDS	Comma-separated list of fds to pass to\n"
+		"			the spawned command\n");
 
 	passt_exit(status);
 }
@@ -1316,6 +1318,7 @@ void conf(struct ctx *c, int argc, char **argv)
 		{"stats", required_argument,		NULL,		31 },
 		{"conf-path",	required_argument,	NULL,		'c' },
 		{"chroot-fallback", no_argument,	NULL, 		32 },
+		{"pass-fds",	required_argument,	NULL,		33 },
 		{ 0 },
 	};
 	const char *optstring = "+dqfel:hs:c:F:I:p:P:m:a:n:M:g:i:o:D:S:H:461t:u:T:U:";
@@ -1557,6 +1560,10 @@ void conf(struct ctx *c, int argc, char **argv)
 		case 32:
 			c->chroot_fallback = true;
 			break;
+		case 33:
+			if (c->mode != MODE_PASTA)
+				die("--pass-fds is for pasta mode only");
+			break;
 		case 'd':
 			c->debug = 1;
 			c->quiet = 0;
diff --git a/passt.1 b/passt.1
index 1570f6a..93c9b24 100644
--- a/passt.1
+++ b/passt.1
@@ -755,6 +755,11 @@ of local traffic in pasta\fR in the \fBNOTES\fR for more details.
 Do not create a tap device in the namespace. In this mode, \fIpasta\fR only
 forwards loopback traffic between namespaces.
 
+.TP
+.BR \-\-pass\-fds " " \fIfds\fR
+Pass a comma-separated list of file descriptors to the spawned command.
+These file descriptors will be kept open.
+
 .SH EXAMPLES
 
 .SS \fBpasta
diff --git a/util.c b/util.c
index 4bc5d6f..c73fd74 100644
--- a/util.c
+++ b/util.c
@@ -932,29 +932,102 @@ const char *str_ee_origin(const struct sock_extended_err *ee)
 void close_open_files(int argc, char **argv)
 {
 	const struct option optfd[] = { { "fd", required_argument, NULL, 'F' },
+					{ "pass-fds", required_argument, NULL, 33 },
 					{ 0 },
 				      };
 	long fd = -1;
-	int name, rc;
+	const char *pass_fds_arg = NULL;
+	int name, rc = 0;
+	int old_opterr = opterr;
 
+	opterr = 0;
 	do {
 		name = getopt_long(argc, argv, "-:F:", optfd, NULL);
 
 		if (name == 'F')
 			fd = conf_tap_fd(optarg);
+		else if (name == 33)
+			pass_fds_arg = optarg;
 	} while (name != -1);
+	opterr = old_opterr;
 
-	if (fd == -1) {
-		rc = close_range(STDERR_FILENO + 1, ~0U, CLOSE_RANGE_UNSHARE);
-	} else if (fd == STDERR_FILENO + 1) { /* Still a single range */
-		rc = close_range(STDERR_FILENO + 2, ~0U, CLOSE_RANGE_UNSHARE);
-	} else {
-		rc = close_range(STDERR_FILENO + 1, fd - 1,
-				 CLOSE_RANGE_UNSHARE);
-		if (!rc)
-			rc = close_range(fd + 1, ~0U, CLOSE_RANGE_UNSHARE);
+	#define MAX_PASS_FDS 1024
+	unsigned int keep_fds[MAX_PASS_FDS];
+	int keep_fds_cnt = 0;
+
+	if (fd != -1 && fd > STDERR_FILENO) {
+		keep_fds[keep_fds_cnt++] = (unsigned int)fd;
+	}
+
+	if (pass_fds_arg) {
+		const char *p = pass_fds_arg;
+		while (*p) {
+			char *endptr;
+			unsigned long val = strtoul(p, &endptr, 10);
+			if (p == endptr) {
+				die("Invalid --pass-fds option: %s", pass_fds_arg);
+			}
+			if (val > INT_MAX || val <= STDERR_FILENO) {
+				die("Invalid file descriptor in --pass-fds: %lu", val);
+			}
+			if (keep_fds_cnt >= MAX_PASS_FDS) {
+				die("Too many file descriptors in --pass-fds");
+			}
+			keep_fds[keep_fds_cnt++] = (unsigned int)val;
+			if (*endptr == ',') {
+				p = endptr + 1;
+				if (*p == '\0') {
+					die("Invalid --pass-fds option: trailing comma");
+				}
+			} else if (*endptr == '\0') {
+				p = endptr;
+			} else {
+				die("Invalid character in --pass-fds option: %s", pass_fds_arg);
+			}
+		}
 	}
 
+	/* Sort keep_fds in ascending order */
+	for (int i = 1; i < keep_fds_cnt; i++) {
+		unsigned int key = keep_fds[i];
+		int j = i - 1;
+		while (j >= 0 && keep_fds[j] > key) {
+			keep_fds[j + 1] = keep_fds[j];
+			j--;
+		}
+		keep_fds[j + 1] = key;
+	}
+
+	/* Remove duplicates */
+	int write_idx = 0;
+	for (int i = 0; i < keep_fds_cnt; i++) {
+		if (i == 0 || keep_fds[i] != keep_fds[i - 1]) {
+			keep_fds[write_idx++] = keep_fds[i];
+		}
+	}
+	keep_fds_cnt = write_idx;
+
+	/* Close ranges */
+	unsigned int current = STDERR_FILENO + 1;
+	for (int i = 0; i < keep_fds_cnt; i++) {
+		unsigned int f = keep_fds[i];
+		if (current < f) {
+			int call_rc = close_range(current, f - 1, CLOSE_RANGE_UNSHARE);
+			if (call_rc != 0)
+				rc = call_rc;
+		}
+		current = f + 1;
+	}
+	if (keep_fds_cnt == 0 || keep_fds[keep_fds_cnt - 1] < ~0U) {
+		int call_rc = close_range(current, ~0U, CLOSE_RANGE_UNSHARE);
+		if (call_rc != 0)
+			rc = call_rc;
+	}
+
+	/* Note: We assume that the parent process did not set FD_CLOEXEC on the
+	 * passed file descriptors, so we do not explicitly clear it here.
+	 */
+
 	if (rc) {
 		if (errno == ENOSYS || errno == EINVAL) {
 			/* This probably means close_range() or the
-- 
2.52.0


^ permalink raw reply	[flat|nested] 2+ messages in thread

end of thread, other threads:[~2026-07-17  6:14 UTC | newest]

Thread overview: 2+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-07-16 19:03 [PATCH] pasta: Add --pass-fds option to preserve specific file descriptors Richard Lawrence
2026-07-17  6:14 ` David Gibson

Code repositories for project(s) associated with this public inbox

	https://passt.top/passt

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 IMAP folder(s).