app: integrate interrupt forwarding
	
		
			
	
		
	
	
		
	
		
			All checks were successful
		
		
	
	
		
			
				
	
				Test / Create distribution (push) Successful in 32s
				
			
		
			
				
	
				Test / Sandbox (push) Successful in 1m58s
				
			
		
			
				
	
				Test / Hakurei (push) Successful in 2m53s
				
			
		
			
				
	
				Test / Sandbox (race detector) (push) Successful in 3m53s
				
			
		
			
				
	
				Test / Planterette (push) Successful in 3m53s
				
			
		
			
				
	
				Test / Hakurei (race detector) (push) Successful in 4m31s
				
			
		
			
				
	
				Test / Flake checks (push) Successful in 1m19s
				
			
		
		
	
	
				
					
				
			
		
			All checks were successful
		
		
	
	Test / Create distribution (push) Successful in 32s
				
			Test / Sandbox (push) Successful in 1m58s
				
			Test / Hakurei (push) Successful in 2m53s
				
			Test / Sandbox (race detector) (push) Successful in 3m53s
				
			Test / Planterette (push) Successful in 3m53s
				
			Test / Hakurei (race detector) (push) Successful in 4m31s
				
			Test / Flake checks (push) Successful in 1m19s
				
			This significantly increases usability of command line tools running through hakurei. Signed-off-by: Ophestra <cat@gensokyo.uk>
This commit is contained in:
		
							parent
							
								
									ddf48a6c22
								
							
						
					
					
						commit
						b43d104680
					
				| @ -256,8 +256,10 @@ App | ||||
|     ], | ||||
|     "container": { | ||||
|       "hostname": "localhost", | ||||
|       "immediate_termination": true, | ||||
|       "seccomp_flags": 1, | ||||
|       "seccomp_presets": 1, | ||||
|       "seccomp_compat": true, | ||||
|       "devel": true, | ||||
|       "userns": true, | ||||
|       "net": true, | ||||
| @ -382,8 +384,10 @@ App | ||||
|   ], | ||||
|   "container": { | ||||
|     "hostname": "localhost", | ||||
|     "immediate_termination": true, | ||||
|     "seccomp_flags": 1, | ||||
|     "seccomp_presets": 1, | ||||
|     "seccomp_compat": true, | ||||
|     "devel": true, | ||||
|     "userns": true, | ||||
|     "net": true, | ||||
| @ -562,8 +566,10 @@ func Test_printPs(t *testing.T) { | ||||
|       ], | ||||
|       "container": { | ||||
|         "hostname": "localhost", | ||||
|         "immediate_termination": true, | ||||
|         "seccomp_flags": 1, | ||||
|         "seccomp_presets": 1, | ||||
|         "seccomp_compat": true, | ||||
|         "devel": true, | ||||
|         "userns": true, | ||||
|         "net": true, | ||||
|  | ||||
| @ -10,6 +10,8 @@ type ( | ||||
| 		// container hostname | ||||
| 		Hostname string `json:"hostname,omitempty"` | ||||
| 
 | ||||
| 		// do not interrupt and wait for initial process during termination | ||||
| 		ImmediateTermination bool `json:"immediate_termination,omitempty"` | ||||
| 		// extra seccomp flags | ||||
| 		SeccompFlags seccomp.ExportFlag `json:"seccomp_flags"` | ||||
| 		// extra seccomp presets | ||||
|  | ||||
| @ -62,8 +62,10 @@ func Template() *Config { | ||||
| 			Userns:               true, | ||||
| 			Net:                  true, | ||||
| 			Device:               true, | ||||
| 			ImmediateTermination: true, | ||||
| 			SeccompFlags:         seccomp.AllowMultiarch, | ||||
| 			SeccompPresets:       seccomp.PresetExt, | ||||
| 			SeccompCompat:        true, | ||||
| 			Tty:                  true, | ||||
| 			Multiarch:            true, | ||||
| 			MapRealUID:           true, | ||||
|  | ||||
| @ -80,8 +80,10 @@ func TestTemplate(t *testing.T) { | ||||
| 	], | ||||
| 	"container": { | ||||
| 		"hostname": "localhost", | ||||
| 		"immediate_termination": true, | ||||
| 		"seccomp_flags": 1, | ||||
| 		"seccomp_presets": 1, | ||||
| 		"seccomp_compat": true, | ||||
| 		"devel": true, | ||||
| 		"userns": true, | ||||
| 		"net": true, | ||||
|  | ||||
| @ -144,6 +144,7 @@ var testCasesNixos = []sealTestCase{ | ||||
| 				Tmpfs("/var/run/nscd", 8192, 0755), | ||||
| 			SeccompPresets: seccomp.PresetExt | seccomp.PresetDenyTTY | seccomp.PresetDenyDevel, | ||||
| 			HostNet:        true, | ||||
| 			ForwardCancel:  true, | ||||
| 		}, | ||||
| 	}, | ||||
| } | ||||
|  | ||||
| @ -71,6 +71,7 @@ var testCasesPd = []sealTestCase{ | ||||
| 			SeccompPresets: seccomp.PresetExt | seccomp.PresetDenyDevel, | ||||
| 			HostNet:        true, | ||||
| 			RetainSession:  true, | ||||
| 			ForwardCancel:  true, | ||||
| 		}, | ||||
| 	}, | ||||
| 	{ | ||||
| @ -220,6 +221,7 @@ var testCasesPd = []sealTestCase{ | ||||
| 			SeccompPresets: seccomp.PresetExt | seccomp.PresetDenyDevel, | ||||
| 			HostNet:        true, | ||||
| 			RetainSession:  true, | ||||
| 			ForwardCancel:  true, | ||||
| 		}, | ||||
| 	}, | ||||
| } | ||||
|  | ||||
| @ -32,6 +32,10 @@ func newContainer(s *hst.ContainerConfig, os sys.State, uid, gid *int) (*contain | ||||
| 		SeccompPresets: s.SeccompPresets, | ||||
| 		RetainSession:  s.Tty, | ||||
| 		HostNet:        s.Net, | ||||
| 
 | ||||
| 		// 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, | ||||
| 	} | ||||
| 
 | ||||
| 	{ | ||||
|  | ||||
| @ -9,6 +9,7 @@ import ( | ||||
| 	"os/exec" | ||||
| 	"os/signal" | ||||
| 	"runtime" | ||||
| 	"sync/atomic" | ||||
| 	"syscall" | ||||
| 	"time" | ||||
| 
 | ||||
| @ -41,6 +42,10 @@ const ( | ||||
| 	ShimExitRequest = 254 | ||||
| 	// 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 | ||||
| ) | ||||
| 
 | ||||
| // ShimMain is the main function of the shim process and runs as the unconstrained target user. | ||||
| @ -86,6 +91,7 @@ func ShimMain() { | ||||
| 	} | ||||
| 
 | ||||
| 	// signal handler outcome | ||||
| 	var cancelContainer atomic.Pointer[context.CancelFunc] | ||||
| 	go func() { | ||||
| 		buf := make([]byte, 1) | ||||
| 		for { | ||||
| @ -94,23 +100,30 @@ func ShimMain() { | ||||
| 			} | ||||
| 
 | ||||
| 			switch buf[0] { | ||||
| 			case 0: | ||||
| 			case 0: // got SIGCONT from monitor: shim exit requested | ||||
| 				if fp := cancelContainer.Load(); params.Container.ForwardCancel && fp != nil && *fp != nil { | ||||
| 					(*fp)() | ||||
| 					// shim now bound by ShimWaitDelay, implemented below | ||||
| 					continue | ||||
| 				} | ||||
| 
 | ||||
| 				// setup has not completed, terminate immediately | ||||
| 				hlog.Resume() | ||||
| 				os.Exit(ShimExitRequest) | ||||
| 				return | ||||
| 
 | ||||
| 			case 1: | ||||
| 			case 1: // got SIGCONT after adoption: monitor died before delivering signal | ||||
| 				hlog.BeforeExit() | ||||
| 				os.Exit(ShimExitOrphan) | ||||
| 				return | ||||
| 
 | ||||
| 			case 2: | ||||
| 			case 2: // unreachable | ||||
| 				log.Println("sa_sigaction got invalid siginfo") | ||||
| 
 | ||||
| 			case 3: | ||||
| 			case 3: // got SIGCONT from unexpected process: hopefully the terminal driver | ||||
| 				log.Println("got SIGCONT from unexpected process") | ||||
| 
 | ||||
| 			default: | ||||
| 			default: // unreachable | ||||
| 				log.Fatalf("got invalid message %d from signal handler", buf[0]) | ||||
| 			} | ||||
| 		} | ||||
| @ -146,12 +159,11 @@ func ShimMain() { | ||||
| 		name = params.Container.Args[0] | ||||
| 	} | ||||
| 	ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM) | ||||
| 	defer stop() // unreachable | ||||
| 	cancelContainer.Store(&stop) | ||||
| 	z := container.New(ctx, name) | ||||
| 	z.Params = *params.Container | ||||
| 	z.Stdin, z.Stdout, z.Stderr = os.Stdin, os.Stdout, os.Stderr | ||||
| 	z.Cancel = func(cmd *exec.Cmd) error { return cmd.Process.Signal(os.Interrupt) } | ||||
| 	z.WaitDelay = 2 * time.Second | ||||
| 	z.WaitDelay = ShimWaitDelay | ||||
| 
 | ||||
| 	if err := z.Start(); err != nil { | ||||
| 		hlog.PrintBaseError(err, "cannot start container:") | ||||
|  | ||||
| @ -128,6 +128,7 @@ in | ||||
| 
 | ||||
|                     container = { | ||||
|                       inherit (app) | ||||
|                         immediate_termination | ||||
|                         devel | ||||
|                         userns | ||||
|                         net | ||||
|  | ||||
| @ -195,6 +195,7 @@ in | ||||
|                 ''; | ||||
|               }; | ||||
| 
 | ||||
|               immediate_termination = mkEnableOption "immediate termination of the container on interrupt"; | ||||
|               devel = mkEnableOption "debugging-related kernel interfaces"; | ||||
|               userns = mkEnableOption "user namespace creation"; | ||||
|               tty = mkEnableOption "access to the controlling terminal"; | ||||
|  | ||||
| @ -127,6 +127,21 @@ | ||||
|         }; | ||||
|       }; | ||||
| 
 | ||||
|       "cat.gensokyo.extern.foot.noEnablements.immediate" = { | ||||
|         name = "ne-foot-immediate"; | ||||
|         identity = 1; | ||||
|         shareUid = true; | ||||
|         verbose = true; | ||||
|         immediate_termination = true; | ||||
|         share = pkgs.foot; | ||||
|         packages = [ ]; | ||||
|         command = "foot"; | ||||
|         capability = { | ||||
|           dbus = false; | ||||
|           pulse = false; | ||||
|         }; | ||||
|       }; | ||||
| 
 | ||||
|       "cat.gensokyo.extern.foot.pulseaudio" = { | ||||
|         name = "pa-foot"; | ||||
|         identity = 2; | ||||
|  | ||||
							
								
								
									
										10
									
								
								test/test.py
									
									
									
									
									
								
							
							
						
						
									
										10
									
								
								test/test.py
									
									
									
									
									
								
							| @ -178,6 +178,16 @@ machine.succeed("pkill -INT -f 'hakurei -v app '") | ||||
| machine.wait_until_fails("pgrep foot", timeout=5) | ||||
| machine.wait_for_file("/tmp/monitor-exit-code") | ||||
| interrupt_exit_code = int(machine.succeed("cat /tmp/monitor-exit-code")) | ||||
| if interrupt_exit_code != 230: | ||||
|     raise Exception(f"unexpected exit code {interrupt_exit_code}") | ||||
| 
 | ||||
| # Check interrupt shim behaviour immediate termination: | ||||
| swaymsg("exec sh -c 'ne-foot-immediate; echo -n $? > /tmp/monitor-exit-code'") | ||||
| wait_for_window(f"u0_a{aid(0)}@machine") | ||||
| machine.succeed("pkill -INT -f 'hakurei -v app '") | ||||
| machine.wait_until_fails("pgrep foot", timeout=5) | ||||
| machine.wait_for_file("/tmp/monitor-exit-code") | ||||
| interrupt_exit_code = int(machine.succeed("cat /tmp/monitor-exit-code")) | ||||
| if interrupt_exit_code != 254: | ||||
|     raise Exception(f"unexpected exit code {interrupt_exit_code}") | ||||
| 
 | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user