aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorOphestra <cat@gensokyo.uk>2025-07-29 03:06:49 +0900
committerOphestra <cat@gensokyo.uk>2025-07-29 03:06:49 +0900
commitf7bd28118c4ac34a9a444073b31ca8f2579188f3 (patch)
treed094700b4d367250e9de16226cc7cc49a0dc3b55
parent940ee00ffee9208a244a99ff085a981c1af1d7c4 (diff)
hst: configurable wait delay
This is useful for programs that take a long time to clean up. Signed-off-by: Ophestra <cat@gensokyo.uk>
-rw-r--r--cmd/hakurei/print_test.go6
-rw-r--r--hst/container.go8
-rw-r--r--hst/template.go24
-rw-r--r--hst/template_test.go2
-rw-r--r--internal/app/container_linux.go2
-rw-r--r--internal/app/errors.go (renamed from internal/app/errors_linux.go)0
-rw-r--r--internal/app/process_linux.go10
-rw-r--r--internal/app/seal_linux.go3
-rw-r--r--internal/app/shim_linux.go18
-rw-r--r--internal/app/strings.go (renamed from internal/app/strings_linux.go)0
-rw-r--r--nixos.nix2
-rw-r--r--options.nix12
-rw-r--r--test/configuration.nix2
13 files changed, 62 insertions, 27 deletions
diff --git a/cmd/hakurei/print_test.go b/cmd/hakurei/print_test.go
index b9d4bb27..efe6ffb8 100644
--- a/cmd/hakurei/print_test.go
+++ b/cmd/hakurei/print_test.go
@@ -256,7 +256,7 @@ App
],
"container": {
"hostname": "localhost",
- "immediate_termination": true,
+ "wait_delay": -1,
"seccomp_flags": 1,
"seccomp_presets": 1,
"seccomp_compat": true,
@@ -384,7 +384,7 @@ App
],
"container": {
"hostname": "localhost",
- "immediate_termination": true,
+ "wait_delay": -1,
"seccomp_flags": 1,
"seccomp_presets": 1,
"seccomp_compat": true,
@@ -566,7 +566,7 @@ func Test_printPs(t *testing.T) {
],
"container": {
"hostname": "localhost",
- "immediate_termination": true,
+ "wait_delay": -1,
"seccomp_flags": 1,
"seccomp_presets": 1,
"seccomp_compat": true,
diff --git a/hst/container.go b/hst/container.go
index 1ee9e839..c385344c 100644
--- a/hst/container.go
+++ b/hst/container.go
@@ -1,6 +1,8 @@
package hst
import (
+ "time"
+
"hakurei.app/container/seccomp"
)
@@ -10,8 +12,10 @@ type (
// container hostname
Hostname string `json:"hostname,omitempty"`
- // do not interrupt and wait for initial process during termination
- ImmediateTermination bool `json:"immediate_termination,omitempty"`
+ // duration to wait for after interrupting a container's initial process in nanoseconds;
+ // a negative value causes the container to be terminated immediately on cancellation
+ WaitDelay time.Duration `json:"wait_delay,omitempty"`
+
// extra seccomp flags
SeccompFlags seccomp.ExportFlag `json:"seccomp_flags"`
// extra seccomp presets
diff --git a/hst/template.go b/hst/template.go
index 60af2ebc..3ab9c277 100644
--- a/hst/template.go
+++ b/hst/template.go
@@ -57,18 +57,18 @@ func Template() *Config {
Groups: []string{"video", "dialout", "plugdev"},
Container: &ContainerConfig{
- Hostname: "localhost",
- Devel: true,
- Userns: true,
- Net: true,
- Device: true,
- ImmediateTermination: true,
- SeccompFlags: seccomp.AllowMultiarch,
- SeccompPresets: seccomp.PresetExt,
- SeccompCompat: true,
- Tty: true,
- Multiarch: true,
- MapRealUID: true,
+ Hostname: "localhost",
+ Devel: true,
+ Userns: true,
+ Net: true,
+ Device: true,
+ WaitDelay: -1,
+ SeccompFlags: seccomp.AllowMultiarch,
+ SeccompPresets: seccomp.PresetExt,
+ SeccompCompat: true,
+ Tty: true,
+ Multiarch: true,
+ MapRealUID: true,
// example API credentials pulled from Google Chrome
// DO NOT USE THESE IN A REAL BROWSER
Env: map[string]string{
diff --git a/hst/template_test.go b/hst/template_test.go
index a9b92262..8761b7d4 100644
--- a/hst/template_test.go
+++ b/hst/template_test.go
@@ -80,7 +80,7 @@ func TestTemplate(t *testing.T) {
],
"container": {
"hostname": "localhost",
- "immediate_termination": true,
+ "wait_delay": -1,
"seccomp_flags": 1,
"seccomp_presets": 1,
"seccomp_compat": true,
diff --git a/internal/app/container_linux.go b/internal/app/container_linux.go
index baea57d9..5efd3de7 100644
--- a/internal/app/container_linux.go
+++ b/internal/app/container_linux.go
@@ -35,7 +35,7 @@ func newContainer(s *hst.ContainerConfig, os sys.State, uid, gid *int) (*contain
// the container is canceled when shim is requested to exit or receives an interrupt or termination signal;
// this behaviour is implemented in the shim
- ForwardCancel: !s.ImmediateTermination,
+ ForwardCancel: s.WaitDelay >= 0,
}
{
diff --git a/internal/app/errors_linux.go b/internal/app/errors.go
index b9bd6f57..b9bd6f57 100644
--- a/internal/app/errors_linux.go
+++ b/internal/app/errors.go
diff --git a/internal/app/process_linux.go b/internal/app/process_linux.go
index 818c7d7b..516beb4b 100644
--- a/internal/app/process_linux.go
+++ b/internal/app/process_linux.go
@@ -123,7 +123,15 @@ func (seal *outcome) Run(rs *RunState) error {
// this prevents blocking forever on an early failure
waitErr, setupErr := make(chan error, 1), make(chan error, 1)
go func() { waitErr <- cmd.Wait(); cancel() }()
- go func() { setupErr <- e.Encode(&shimParams{os.Getpid(), seal.container, seal.user.data, hlog.Load()}) }()
+ go func() {
+ setupErr <- e.Encode(&shimParams{
+ os.Getpid(),
+ seal.waitDelay,
+ seal.container,
+ seal.user.data,
+ hlog.Load(),
+ })
+ }()
select {
case err := <-setupErr:
diff --git a/internal/app/seal_linux.go b/internal/app/seal_linux.go
index 40c68c1b..13257211 100644
--- a/internal/app/seal_linux.go
+++ b/internal/app/seal_linux.go
@@ -15,6 +15,7 @@ import (
"strings"
"sync/atomic"
"syscall"
+ "time"
"hakurei.app/container"
"hakurei.app/hst"
@@ -79,6 +80,7 @@ type outcome struct {
sys *system.I
ctx context.Context
+ waitDelay time.Duration
container *container.Params
env map[string]string
sync *os.File
@@ -281,6 +283,7 @@ func (seal *outcome) finalise(ctx context.Context, sys sys.State, config *hst.Co
var uid, gid int
var err error
seal.container, seal.env, err = newContainer(config.Container, sys, &uid, &gid)
+ seal.waitDelay = config.Container.WaitDelay
if err != nil {
return hlog.WrapErrSuffix(err,
"cannot initialise container configuration:")
diff --git a/internal/app/shim_linux.go b/internal/app/shim_linux.go
index c88c8672..e423b321 100644
--- a/internal/app/shim_linux.go
+++ b/internal/app/shim_linux.go
@@ -28,6 +28,10 @@ type shimParams struct {
// monitor pid, checked against ppid in signal handler
Monitor int
+ // duration to wait for after interrupting a container's initial process before the container is killed;
+ // zero value defaults to [DefaultShimWaitDelay], values exceeding [MaxShimWaitDelay] becomes [MaxShimWaitDelay]
+ WaitDelay time.Duration
+
// finalised container params
Container *container.Params
// path to outer home directory
@@ -43,9 +47,8 @@ const (
// ShimExitOrphan is returned when the shim is orphaned before monitor delivers a signal.
ShimExitOrphan = 3
- // ShimWaitDelay is the duration to wait after interrupting a container's initial process
- // before the container is fully killed off.
- ShimWaitDelay = 5 * time.Second
+ DefaultShimWaitDelay = 5 * time.Second
+ MaxShimWaitDelay = 30 * time.Second
)
// ShimMain is the main function of the shim process and runs as the unconstrained target user.
@@ -163,7 +166,14 @@ func ShimMain() {
z := container.New(ctx, name)
z.Params = *params.Container
z.Stdin, z.Stdout, z.Stderr = os.Stdin, os.Stdout, os.Stderr
- z.WaitDelay = ShimWaitDelay
+
+ z.WaitDelay = params.WaitDelay
+ if z.WaitDelay == 0 {
+ z.WaitDelay = DefaultShimWaitDelay
+ }
+ if z.WaitDelay > MaxShimWaitDelay {
+ z.WaitDelay = MaxShimWaitDelay
+ }
if err := z.Start(); err != nil {
hlog.PrintBaseError(err, "cannot start container:")
diff --git a/internal/app/strings_linux.go b/internal/app/strings.go
index e4465cee..e4465cee 100644
--- a/internal/app/strings_linux.go
+++ b/internal/app/strings.go
diff --git a/nixos.nix b/nixos.nix
index 919b2c96..bda29884 100644
--- a/nixos.nix
+++ b/nixos.nix
@@ -128,7 +128,7 @@ in
container = {
inherit (app)
- immediate_termination
+ wait_delay
devel
userns
net
diff --git a/options.nix b/options.nix
index 17c55372..90ad3565 100644
--- a/options.nix
+++ b/options.nix
@@ -76,6 +76,7 @@ in
type =
let
inherit (types)
+ int
ints
str
bool
@@ -195,7 +196,16 @@ in
'';
};
- immediate_termination = mkEnableOption "immediate termination of the container on interrupt";
+ wait_delay = mkOption {
+ type = nullOr int;
+ default = null;
+ description = ''
+ Duration to wait for after interrupting a container's initial process in nanoseconds.
+ A negative value causes the container to be terminated immediately on cancellation.
+ Setting this to null defaults to five seconds.
+ '';
+ };
+
devel = mkEnableOption "debugging-related kernel interfaces";
userns = mkEnableOption "user namespace creation";
tty = mkEnableOption "access to the controlling terminal";
diff --git a/test/configuration.nix b/test/configuration.nix
index 58bc19a0..e1fa892d 100644
--- a/test/configuration.nix
+++ b/test/configuration.nix
@@ -132,7 +132,7 @@
identity = 1;
shareUid = true;
verbose = true;
- immediate_termination = true;
+ wait_delay = -1;
share = pkgs.foot;
packages = [ ];
command = "foot";