From 52de7681cd9dcb8fca86c074b0b89efe27edbd93 Mon Sep 17 00:00:00 2001 From: Sergio Lopez Date: Tue, 18 Nov 2025 10:19:54 +0100 Subject: [PATCH 1/5] handlers: add new hook to close fds Some handlers may need to preserve more fds than those related to stdio. Let's allow them to provide their own hook for doing that. Signed-off-by: Sergio Lopez --- libocispec | 2 +- src/libcrun/container.c | 12 +++++++++++- src/libcrun/custom-handler.h | 3 +++ 3 files changed, 15 insertions(+), 2 deletions(-) diff --git a/libocispec b/libocispec index bf749566cd..552ccbbad3 160000 --- a/libocispec +++ b/libocispec @@ -1 +1 @@ -Subproject commit bf749566cda632fb2f5dcf9c4eb5bbec71ac7d5f +Subproject commit 552ccbbad3aaff8e07e8fbad210ec3b4c9c95a66 diff --git a/src/libcrun/container.c b/src/libcrun/container.c index 16af03b45f..396aef8b42 100644 --- a/src/libcrun/container.c +++ b/src/libcrun/container.c @@ -1681,7 +1681,17 @@ container_init (void *args, char *notify_socket, int sync_socket, libcrun_error_ This is a best effort operation, because the seccomp filter is already in place and it could stop some syscalls used by mark_or_close_fds_ge_than. */ - ret = mark_or_close_fds_ge_than (entrypoint_args->container, entrypoint_args->context->preserve_fds + 3, true, err); + if (entrypoint_args->custom_handler->vtable->close_fds) + { + ret = entrypoint_args->custom_handler->vtable->close_fds (entrypoint_args->custom_handler->cookie, + entrypoint_args->container, + entrypoint_args->context->preserve_fds, + err); + } + else + { + ret = mark_or_close_fds_ge_than (entrypoint_args->container, entrypoint_args->context->preserve_fds + 3, true, err); + } if (UNLIKELY (ret < 0)) crun_error_release (err); diff --git a/src/libcrun/custom-handler.h b/src/libcrun/custom-handler.h index 9351daa5be..7536191f1b 100644 --- a/src/libcrun/custom-handler.h +++ b/src/libcrun/custom-handler.h @@ -48,6 +48,9 @@ struct custom_handler_s int (*modify_oci_configuration) (void *cookie, libcrun_context_t *context, runtime_spec_schema_config_schema *def, libcrun_error_t *err); + + int (*close_fds) (void *cookie, libcrun_container_t *container, + int preserve_fds, libcrun_error_t *err); }; struct custom_handler_manager_s; From f18efff8569aac2e3e763bef009b7c2a2b0ee357 Mon Sep 17 00:00:00 2001 From: Sergio Lopez Date: Thu, 19 Feb 2026 13:02:12 +0100 Subject: [PATCH 2/5] krun: process the vm configuration earlier Some decisions, such as whether we need to start passt or not, need to happen before we switch to the container's mount namespace. With this change we move processing the VM configuration to the HANDLER_CONFIGURE_BEFORE_USERNS phase, so we can have the information early enough. Signed-off-by: Sergio Lopez --- src/libcrun/handlers/krun.c | 42 ++++++++++++++++++++++--------------- 1 file changed, 25 insertions(+), 17 deletions(-) diff --git a/src/libcrun/handlers/krun.c b/src/libcrun/handlers/krun.c index d7acdb150a..2ab0e29101 100644 --- a/src/libcrun/handlers/krun.c +++ b/src/libcrun/handlers/krun.c @@ -80,6 +80,8 @@ struct krun_config int32_t ctx_id_awsnitro; bool has_kvm; bool has_awsnitro; + yajl_val config_tree; + bool use_passt; }; /* libkrun handler. */ @@ -170,20 +172,30 @@ libkrun_enable_virtio_gpu (struct krun_config *kconf) } static int -libkrun_read_vm_config (yajl_val *config_tree, libcrun_error_t *err) +libkrun_read_vm_config (struct krun_config *kconf, int rootfsfd, const char *rootfs, libcrun_error_t *err) { int ret; cleanup_free char *config = NULL; + cleanup_close int fd = -1; struct parser_context ctx = { 0, stderr }; - if (access (KRUN_VM_FILE, F_OK) != 0) - return 0; + fd = safe_openat (rootfsfd, rootfs, KRUN_VM_FILE, O_PATH | O_NOFOLLOW, 0, err); + if (fd < 0) + { + // The configuration file is optional, don't generate an error if it's missing. + if (errno == ENOENT) + { + crun_error_release (err); + return 0; + } + return fd; + } - ret = read_all_file (KRUN_VM_FILE, &config, NULL, err); + ret = read_all_fd (fd, "krun configuration file", &config, NULL, err); if (UNLIKELY (ret < 0)) return ret; - ret = parse_json_file (config_tree, config, &ctx, err); + ret = parse_json_file (&kconf->config_tree, config, &ctx, err); if (UNLIKELY (ret < 0)) return ret; @@ -373,17 +385,8 @@ libkrun_exec (void *cookie, libcrun_container_t *container, const char *pathname cpu_set_t set; libcrun_error_t err; bool configured = false; - yajl_val config_tree = NULL; - - ret = libkrun_read_vm_config (&config_tree, &err); - if (UNLIKELY (ret < 0)) - { - int errcode = crun_error_get_errno (&err); - crun_error_release (&err); - error (EXIT_FAILURE, errcode, "libkrun VM config exists, but unable to parse"); - } - ret = libkrun_configure_flavor (cookie, &config_tree, container, &err); + ret = libkrun_configure_flavor (cookie, &kconf->config_tree, container, &err); if (UNLIKELY (ret < 0)) { int errcode = crun_error_get_errno (&err); @@ -462,7 +465,7 @@ libkrun_exec (void *cookie, libcrun_container_t *container, const char *pathname error (EXIT_FAILURE, -ret, "could not set enclave execution arguments"); } - ret = libkrun_configure_vm (ctx_id, handle, &configured, &config_tree, container, &err); + ret = libkrun_configure_vm (ctx_id, handle, &configured, &kconf->config_tree, container, &err); if (UNLIKELY (ret)) { int errcode = crun_error_get_errno (&err); @@ -506,7 +509,7 @@ libkrun_exec (void *cookie, libcrun_container_t *container, const char *pathname } } - yajl_tree_free (config_tree); + yajl_tree_free (kconf->config_tree); ret = krun_start_enter (ctx_id); if (UNLIKELY (ret < 0)) @@ -571,6 +574,10 @@ libkrun_configure_container (void *cookie, enum handler_configure_phase phase, ret = safe_write (fd, KRUN_CONFIG_FILE, config, config_size, err); if (UNLIKELY (ret < 0)) return ret; + + ret = libkrun_read_vm_config (kconf, rootfsfd, rootfs, err); + if (UNLIKELY (ret < 0)) + return ret; } if (phase != HANDLER_CONFIGURE_AFTER_MOUNTS) @@ -664,6 +671,7 @@ libkrun_load (void **cookie, libcrun_error_t *err) kconf = malloc (sizeof (struct krun_config)); if (kconf == NULL) return crun_make_error (err, 0, "could not allocate memory for krun_config"); + memset (kconf, 0, sizeof (struct krun_config)); kconf->handle = dlopen (libkrun_so, RTLD_NOW); kconf->handle_sev = dlopen (libkrun_sev_so, RTLD_NOW); From a21e93040d94d33d8685e077e3dd3e430016016f Mon Sep 17 00:00:00 2001 From: Sergio Lopez Date: Thu, 19 Feb 2026 13:24:01 +0100 Subject: [PATCH 3/5] krun: consolidate configuration in configure_vm Since the introduction of libkrun_configure_vm, the legacy path was only walked on very exceptional situations. Let's consolidate both configuration mechanisms in libkrun_configure_vm. While there, make GPU a configurable option, instead of relying in heuristics. Signed-off-by: Sergio Lopez --- src/libcrun/handlers/krun.c | 97 +++++++++++++++---------------------- 1 file changed, 39 insertions(+), 58 deletions(-) diff --git a/src/libcrun/handlers/krun.c b/src/libcrun/handlers/krun.c index 2ab0e29101..ca7f372662 100644 --- a/src/libcrun/handlers/krun.c +++ b/src/libcrun/handlers/krun.c @@ -43,9 +43,6 @@ /* libkrun has a hard-limit of 16 vCPUs per microVM. */ #define LIBKRUN_MAX_VCPUS 16 -/* If the user doesn't configure the vCPU count, fallback to this value. */ -#define LIBKRUN_DEFAULT_VCPUS 1 - /* If the user doesn't configure the RAM amount, fallback to this value. */ #define LIBKRUN_DEFAULT_RAM_MIB 1024 @@ -154,7 +151,7 @@ libkrun_configure_kernel (uint32_t ctx_id, void *handle, yajl_val *config_tree, } static int -libkrun_enable_virtio_gpu (struct krun_config *kconf) +libkrun_enable_virtio_gpu (struct krun_config *kconf, uint32_t virgl_flags) { int32_t (*krun_set_gpu_options) (uint32_t ctx_id, uint32_t virgl_flags); krun_set_gpu_options = dlsym (kconf->handle, "krun_set_gpu_options"); @@ -163,11 +160,6 @@ libkrun_enable_virtio_gpu (struct krun_config *kconf) if (krun_set_gpu_options == NULL) return 0; - uint32_t virgl_flags = VIRGLRENDERER_NO_VIRGL | /* do not expose OpenGL */ - VIRGLRENDERER_RENDER_SERVER | /* start a render server and move GPU rendering to the render server */ - VIRGLRENDERER_VENUS | /* enable venus renderer */ - VIRGLRENDERER_THREAD_SYNC | /* wait for sync objects in thread rather than polling */ - VIRGLRENDERER_USE_ASYNC_FENCE_CB; /* used in conjunction with VIRGLRENDERER_THREAD_SYNC */ return krun_set_gpu_options (kconf->ctx_id, virgl_flags); } @@ -245,20 +237,35 @@ libkrun_parse_resource_configuration (yajl_val *config_tree, libcrun_container_t } static int -libkrun_configure_vm (uint32_t ctx_id, void *handle, bool *configured, yajl_val *config_tree, libcrun_container_t *container, libcrun_error_t *err) +libkrun_configure_vm (uint32_t ctx_id, void *handle, struct krun_config *kconf, libcrun_container_t *container, libcrun_error_t *err) { + runtime_spec_schema_config_schema *def = container->container_def; int32_t (*krun_set_vm_config) (uint32_t ctx_id, uint8_t num_vcpus, uint32_t ram_mib); - int cpus, ram_mib, ret; + int cpus, ram_mib, gpu_flags, ret; + cpu_set_t set; const char *path_cpus[] = { "cpus", (const char *) 0 }; const char *path_ram_mib[] = { "ram_mib", (const char *) 0 }; + const char *path_gpu_flags[] = { "gpu_flags", (const char *) 0 }; - cpus = libkrun_parse_resource_configuration (config_tree, container, "krun.cpus", path_cpus); + cpus = libkrun_parse_resource_configuration (&kconf->config_tree, container, "krun.cpus", path_cpus); if (cpus <= 0) - cpus = LIBKRUN_DEFAULT_VCPUS; + { + CPU_ZERO (&set); + if (sched_getaffinity (getpid (), sizeof (set), &set) == 0) + cpus = MIN (CPU_COUNT (&set), LIBKRUN_MAX_VCPUS); + else + cpus = 1; + } - ram_mib = libkrun_parse_resource_configuration (config_tree, container, "krun.ram_mib", path_ram_mib); + ram_mib = libkrun_parse_resource_configuration (&kconf->config_tree, container, "krun.ram_mib", path_ram_mib); if (ram_mib <= 0) - ram_mib = LIBKRUN_DEFAULT_RAM_MIB; + { + if (def && def->linux && def->linux->resources && def->linux->resources->memory + && def->linux->resources->memory->limit_present) + ram_mib = def->linux->resources->memory->limit / (1024 * 1024); + else + ram_mib = LIBKRUN_DEFAULT_RAM_MIB; + } krun_set_vm_config = dlsym (handle, "krun_set_vm_config"); @@ -269,19 +276,31 @@ libkrun_configure_vm (uint32_t ctx_id, void *handle, bool *configured, yajl_val if (UNLIKELY (ret < 0)) return crun_make_error (err, -ret, "could not set krun vm configuration"); - if (*config_tree != NULL) + gpu_flags = libkrun_parse_resource_configuration (&kconf->config_tree, container, "krun.gpu_flags", path_gpu_flags); + if (gpu_flags > 0) + { + if (access ("/dev/dri", F_OK) != 0) + return crun_make_error (err, errno, "gpu requested but /dev/dri is not available"); + + if (access ("/usr/libexec/virgl_render_server", F_OK) != 0) + return crun_make_error (err, errno, "gpu requested but virgl_render_server is not available"); + + ret = libkrun_enable_virtio_gpu (kconf, gpu_flags); + if (UNLIKELY (ret < 0)) + return crun_make_error (err, -ret, "could not enable virtio gpu"); + } + + if (kconf->config_tree != NULL) { /* Try to configure an external kernel. If the configuration file doesn't * specify a kernel, libkrun automatically fall back to using libkrunfw, * if the library is present and was loaded while creating the context. */ - ret = libkrun_configure_kernel (ctx_id, handle, config_tree, err); + ret = libkrun_configure_kernel (ctx_id, handle, &kconf->config_tree, err); if (UNLIKELY (ret)) return ret; } - *configured = true; - return 0; } @@ -380,11 +399,8 @@ libkrun_exec (void *cookie, libcrun_container_t *container, const char *pathname const char *const argv[], const char *const envp[]); struct krun_config *kconf = (struct krun_config *) cookie; void *handle; - uint32_t num_vcpus, ram_mib; int32_t ctx_id, ret; - cpu_set_t set; libcrun_error_t err; - bool configured = false; ret = libkrun_configure_flavor (cookie, &kconf->config_tree, container, &err); if (UNLIKELY (ret < 0)) @@ -465,7 +481,7 @@ libkrun_exec (void *cookie, libcrun_container_t *container, const char *pathname error (EXIT_FAILURE, -ret, "could not set enclave execution arguments"); } - ret = libkrun_configure_vm (ctx_id, handle, &configured, &kconf->config_tree, container, &err); + ret = libkrun_configure_vm (ctx_id, handle, kconf, container, &err); if (UNLIKELY (ret)) { int errcode = crun_error_get_errno (&err); @@ -474,41 +490,6 @@ libkrun_exec (void *cookie, libcrun_container_t *container, const char *pathname error (EXIT_FAILURE, errcode, "could not configure krun vm"); } - /* If we couldn't configure the microVM using KRUN_VM_FILE, fall back to the - * legacy configuration logic. - */ - if (! configured) - { - /* If sched_getaffinity fails, default to 1 vcpu. */ - num_vcpus = 1; - /* If no memory limit is specified, default to 2G. */ - ram_mib = 2 * 1024; - - if (def && def->linux && def->linux->resources && def->linux->resources->memory - && def->linux->resources->memory->limit_present) - ram_mib = def->linux->resources->memory->limit / (1024 * 1024); - - CPU_ZERO (&set); - if (sched_getaffinity (getpid (), sizeof (set), &set) == 0) - num_vcpus = MIN (CPU_COUNT (&set), LIBKRUN_MAX_VCPUS); - - krun_set_vm_config = dlsym (handle, "krun_set_vm_config"); - - if (krun_set_vm_config == NULL) - error (EXIT_FAILURE, 0, "could not find symbol in `libkrun.so`"); - - ret = krun_set_vm_config (ctx_id, num_vcpus, ram_mib); - if (UNLIKELY (ret < 0)) - error (EXIT_FAILURE, -ret, "could not set krun vm configuration"); - - if (access ("/dev/dri", F_OK) == 0 && access ("/usr/libexec/virgl_render_server", F_OK) == 0) - { - ret = libkrun_enable_virtio_gpu (kconf); - if (UNLIKELY (ret < 0)) - error (EXIT_FAILURE, -ret, "could not enable virtio gpu"); - } - } - yajl_tree_free (kconf->config_tree); ret = krun_start_enter (ctx_id); From 675d09645ca5e17104135f377df74151fcfa89c3 Mon Sep 17 00:00:00 2001 From: Sergio Lopez Date: Tue, 10 Mar 2026 19:26:35 +0100 Subject: [PATCH 4/5] krun: ignore RAM configurations below 128MB The minimum viable amount for RAM for a microVM is 128MB, ignore configurations asking for less than that. Signed-off-by: Sergio Lopez --- src/libcrun/handlers/krun.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/libcrun/handlers/krun.c b/src/libcrun/handlers/krun.c index ca7f372662..25511b1cb1 100644 --- a/src/libcrun/handlers/krun.c +++ b/src/libcrun/handlers/krun.c @@ -46,6 +46,9 @@ /* If the user doesn't configure the RAM amount, fallback to this value. */ #define LIBKRUN_DEFAULT_RAM_MIB 1024 +/* The minimum amount of RAM for a viable microVM is 128 MB. */ +#define LIBKRUN_MINIMUM_RAM_MIB 128 + /* crun dumps the container configuration into this file, which will be read by * libkrun to set up the environment for the workload inside the microVM. */ @@ -258,7 +261,7 @@ libkrun_configure_vm (uint32_t ctx_id, void *handle, struct krun_config *kconf, } ram_mib = libkrun_parse_resource_configuration (&kconf->config_tree, container, "krun.ram_mib", path_ram_mib); - if (ram_mib <= 0) + if (ram_mib <= LIBKRUN_MINIMUM_RAM_MIB) { if (def && def->linux && def->linux->resources && def->linux->resources->memory && def->linux->resources->memory->limit_present) From 51d3b56ce3f079ea6c694927d0a95da780630879 Mon Sep 17 00:00:00 2001 From: Sergio Lopez Date: Tue, 18 Nov 2025 18:45:00 +0100 Subject: [PATCH 5/5] krun: implement support for passt networking Introduce a new configuration flag (krun.use_passt) that enables users to request the microVM to use passt based networking instead of TSI. Right now, the container needs to configure the network by itself, but the next version of libkrun will embed a DHCP client in init.c Signed-off-by: Sergio Lopez --- src/libcrun/handlers/krun.c | 119 +++++++++++++++++++++++++++++++++++- 1 file changed, 118 insertions(+), 1 deletion(-) diff --git a/src/libcrun/handlers/krun.c b/src/libcrun/handlers/krun.c index 25511b1cb1..8d132d6c32 100644 --- a/src/libcrun/handlers/krun.c +++ b/src/libcrun/handlers/krun.c @@ -24,6 +24,7 @@ #include "../linux.h" #include #include +#include #include #include #include @@ -68,6 +69,9 @@ #define KRUN_FLAVOR_AWS_NITRO "aws-nitro" #define KRUN_FLAVOR_SEV "sev" +#define PASST_FD_PARENT 0 +#define PASST_FD_CHILD 1 + struct krun_config { void *handle; @@ -80,6 +84,7 @@ struct krun_config int32_t ctx_id_awsnitro; bool has_kvm; bool has_awsnitro; + int passt_fds[2]; yajl_val config_tree; bool use_passt; }; @@ -221,7 +226,7 @@ libkrun_parse_resource_configuration (yajl_val *config_tree, libcrun_container_t /* Annotations value is not a valid integer. */ error (EXIT_FAILURE, 0, "krun annotation %s value cannot be converted to an integer", annotation); - if (val <= 0) + if (val < 0) error (EXIT_FAILURE, 0, "krun annotation %s value must be a positive integer", annotation); return val; @@ -244,6 +249,7 @@ libkrun_configure_vm (uint32_t ctx_id, void *handle, struct krun_config *kconf, { runtime_spec_schema_config_schema *def = container->container_def; int32_t (*krun_set_vm_config) (uint32_t ctx_id, uint8_t num_vcpus, uint32_t ram_mib); + int32_t (*krun_add_net_unixstream) (uint32_t ctx_id, const char *c_path, int fd, uint8_t *const c_mac, uint32_t features, uint32_t flags); int cpus, ram_mib, gpu_flags, ret; cpu_set_t set; const char *path_cpus[] = { "cpus", (const char *) 0 }; @@ -293,6 +299,16 @@ libkrun_configure_vm (uint32_t ctx_id, void *handle, struct krun_config *kconf, return crun_make_error (err, -ret, "could not enable virtio gpu"); } + if (kconf->use_passt) + { + krun_add_net_unixstream = dlsym (handle, "krun_add_net_unixstream"); + + uint8_t mac[] = { 0x5a, 0x94, 0xef, 0xe4, 0x0c, 0xee }; + ret = krun_add_net_unixstream (ctx_id, NULL, kconf->passt_fds[PASST_FD_PARENT], &mac[0], COMPAT_NET_FEATURES, 0); + if (UNLIKELY (ret < 0)) + error (EXIT_FAILURE, -ret, "could not set krun net configuration"); + } + if (kconf->config_tree != NULL) { /* Try to configure an external kernel. If the configuration file doesn't @@ -502,6 +518,73 @@ libkrun_exec (void *cookie, libcrun_container_t *container, const char *pathname return ret; } +static int +libkrun_start_passt (void *cookie, libcrun_container_t *container) +{ + struct krun_config *kconf = (struct krun_config *) cookie; + const char *path_use_passt[] = { "use_passt", (const char *) 0 }; + pid_t pid; + char fd_as_str[16]; + int use_passt; + int status; + int null; + int ret; + + use_passt = libkrun_parse_resource_configuration (&kconf->config_tree, container, "krun.use_passt", path_use_passt); + if (use_passt > 0) + kconf->use_passt = 1; + else + return 0; + + ret = socketpair (AF_UNIX, SOCK_STREAM, 0, kconf->passt_fds); + if (UNLIKELY (ret < 0)) + return ret; + snprintf (fd_as_str, sizeof (fd_as_str), "%d", kconf->passt_fds[PASST_FD_CHILD]); + + char *const argv[] = { + (char *) "passt", + (char *) "--no-dhcp-dns", + (char *) "-t", + (char *) "all", + (char *) "-u", + (char *) "all", + (char *) "--fd", + fd_as_str, + NULL + }; + + pid = fork (); + if (pid < 0) + return pid; + else if (pid == 0) + { + close (kconf->passt_fds[PASST_FD_PARENT]); + + null = open ("/dev/null", O_WRONLY); + if (null == -1) + _exit (EXIT_FAILURE); + + // Redirect passt's stdout and stderr to /dev/null, as closing them here + // instead will cause passt to exit with an error. + dup2 (null, STDOUT_FILENO); + dup2 (null, STDERR_FILENO); + close (null); + + execvp ("passt", argv); + // Only reachable on error. + _exit (EXIT_FAILURE); + } + + close (kconf->passt_fds[PASST_FD_CHILD]); + + // Wait for passt to daemonize itself. + waitpid (pid, &status, 0); + if (! (WIFEXITED (status)) || WEXITSTATUS (status) != 0) + return -1; + + return 0; +} + /* libkrun_create_kvm_device: explicitly adds kvm device. */ static int libkrun_configure_container (void *cookie, enum handler_configure_phase phase, @@ -567,6 +650,10 @@ libkrun_configure_container (void *cookie, enum handler_configure_phase phase, if (phase != HANDLER_CONFIGURE_AFTER_MOUNTS) return 0; + ret = libkrun_start_passt (cookie, container); + if (UNLIKELY (ret < 0)) + return crun_make_error (err, errno, "start passt"); + /* Do nothing if /dev/kvm is already present in spec */ for (i = 0; i < def->linux->devices_len; i++) { @@ -838,6 +925,35 @@ libkrun_modify_oci_configuration (void *cookie arg_unused, libcrun_context_t *co return 0; } +static int +libkrun_close_fds (void *cookie, libcrun_container_t *container, int preserve_fds, libcrun_error_t *err) +{ + struct krun_config *kconf = (struct krun_config *) cookie; + int first_fd_to_close = preserve_fds + 3; + int passt_fd; + int i; + + if (kconf->use_passt) + { + passt_fd = kconf->passt_fds[PASST_FD_PARENT]; + + if (first_fd_to_close <= passt_fd) + { + for (i = first_fd_to_close; i < passt_fd; i++) + { + // If we're closing proc_fd, make sure to invalidate it. + if (i == container->proc_fd) + container->proc_fd = -1; + close (i); + } + + first_fd_to_close = passt_fd + 1; + } + } + + return mark_or_close_fds_ge_than (container, first_fd_to_close, true, err); +} + struct custom_handler_s handler_libkrun = { .name = "krun", .alias = NULL, @@ -847,6 +963,7 @@ struct custom_handler_s handler_libkrun = { .run_func = libkrun_exec, .configure_container = libkrun_configure_container, .modify_oci_configuration = libkrun_modify_oci_configuration, + .close_fds = libkrun_close_fds, }; #endif