app/shim: implement signal handler outcome in Go
	
		
			
	
		
	
	
		
	
		
			All checks were successful
		
		
	
	
		
			
				
	
				Test / Create distribution (push) Successful in 32s
				
			
		
			
				
	
				Test / Sandbox (push) Successful in 1m53s
				
			
		
			
				
	
				Test / Hakurei (push) Successful in 2m48s
				
			
		
			
				
	
				Test / Planterette (push) Successful in 3m48s
				
			
		
			
				
	
				Test / Sandbox (race detector) (push) Successful in 3m56s
				
			
		
			
				
	
				Test / Hakurei (race detector) (push) Successful in 4m27s
				
			
		
			
				
	
				Test / Flake checks (push) Successful in 1m13s
				
			
		
		
	
	
				
					
				
			
		
			All checks were successful
		
		
	
	Test / Create distribution (push) Successful in 32s
				
			Test / Sandbox (push) Successful in 1m53s
				
			Test / Hakurei (push) Successful in 2m48s
				
			Test / Planterette (push) Successful in 3m48s
				
			Test / Sandbox (race detector) (push) Successful in 3m56s
				
			Test / Hakurei (race detector) (push) Successful in 4m27s
				
			Test / Flake checks (push) Successful in 1m13s
				
			This needs to be done from the Go side eventually anyway to integrate the signal forwarding behaviour now supported by the container package. Signed-off-by: Ophestra <cat@gensokyo.uk>
This commit is contained in:
		
							parent
							
								
									a0f499e30a
								
							
						
					
					
						commit
						ddf48a6c22
					
				| @ -6,27 +6,43 @@ | ||||
| #include <unistd.h> | ||||
| 
 | ||||
| static pid_t hakurei_shim_param_ppid = -1; | ||||
| static int hakurei_shim_fd = -1; | ||||
| 
 | ||||
| // this cannot unblock hlog since Go code is not async-signal-safe
 | ||||
| static ssize_t hakurei_shim_write(const void *buf, size_t count) { | ||||
|   int savedErrno = errno; | ||||
|   ssize_t ret = write(hakurei_shim_fd, buf, count); | ||||
|   if (ret == -1 && errno != EAGAIN) | ||||
|     exit(EXIT_FAILURE); | ||||
|   errno = savedErrno; | ||||
|   return ret; | ||||
| } | ||||
| 
 | ||||
| /* see shim_linux.go for handling of the value */ | ||||
| static void hakurei_shim_sigaction(int sig, siginfo_t *si, void *ucontext) { | ||||
|   if (sig != SIGCONT || si == NULL) { | ||||
|     // unreachable
 | ||||
|     fprintf(stderr, "sigaction: sa_sigaction got invalid siginfo\n"); | ||||
|     /* unreachable */ | ||||
|     hakurei_shim_write("\2", 1); | ||||
|     return; | ||||
|   } | ||||
| 
 | ||||
|   // monitor requests shim exit
 | ||||
|   if (si->si_pid == hakurei_shim_param_ppid) | ||||
|     exit(254); | ||||
|   if (si->si_pid == hakurei_shim_param_ppid) { | ||||
|     /* monitor requests shim exit */ | ||||
|     hakurei_shim_write("\0", 1); | ||||
|     return; | ||||
|   } | ||||
| 
 | ||||
|   fprintf(stderr, "sigaction: got SIGCONT from process %d\n", si->si_pid); | ||||
|   /* unexpected si_pid */ | ||||
|   hakurei_shim_write("\3", 1); | ||||
| 
 | ||||
|   // shim orphaned before monitor delivers a signal
 | ||||
|   if (getppid() != hakurei_shim_param_ppid) | ||||
|     exit(3); | ||||
|     /* shim orphaned before monitor delivers a signal */ | ||||
|     hakurei_shim_write("\1", 1); | ||||
| } | ||||
| 
 | ||||
| void hakurei_shim_setup_cont_signal(pid_t ppid) { | ||||
| void hakurei_shim_setup_cont_signal(pid_t ppid, int fd) { | ||||
|   if (hakurei_shim_param_ppid != -1 || hakurei_shim_fd != -1) | ||||
|     *(int *)NULL = 0; /* unreachable */ | ||||
| 
 | ||||
|   struct sigaction new_action = {0}, old_action = {0}; | ||||
|   if (sigaction(SIGCONT, NULL, &old_action) != 0) | ||||
|     return; | ||||
| @ -45,4 +61,5 @@ void hakurei_shim_setup_cont_signal(pid_t ppid) { | ||||
| 
 | ||||
|   errno = 0; | ||||
|   hakurei_shim_param_ppid = ppid; | ||||
|   hakurei_shim_fd = fd; | ||||
| } | ||||
|  | ||||
| @ -1,3 +1,3 @@ | ||||
| #include <signal.h> | ||||
| 
 | ||||
| void hakurei_shim_setup_cont_signal(pid_t ppid); | ||||
| void hakurei_shim_setup_cont_signal(pid_t ppid, int fd); | ||||
|  | ||||
| @ -3,10 +3,12 @@ package app | ||||
| import ( | ||||
| 	"context" | ||||
| 	"errors" | ||||
| 	"io" | ||||
| 	"log" | ||||
| 	"os" | ||||
| 	"os/exec" | ||||
| 	"os/signal" | ||||
| 	"runtime" | ||||
| 	"syscall" | ||||
| 	"time" | ||||
| 
 | ||||
| @ -34,6 +36,13 @@ type shimParams struct { | ||||
| 	Verbose bool | ||||
| } | ||||
| 
 | ||||
| const ( | ||||
| 	// ShimExitRequest is returned when the monitor process requests shim exit. | ||||
| 	ShimExitRequest = 254 | ||||
| 	// ShimExitOrphan is returned when the shim is orphaned before monitor delivers a signal. | ||||
| 	ShimExitOrphan = 3 | ||||
| ) | ||||
| 
 | ||||
| // ShimMain is the main function of the shim process and runs as the unconstrained target user. | ||||
| func ShimMain() { | ||||
| 	hlog.Prepare("shim") | ||||
| @ -58,18 +67,55 @@ func ShimMain() { | ||||
| 	} else { | ||||
| 		internal.InstallOutput(params.Verbose) | ||||
| 		closeSetup = f | ||||
| 
 | ||||
| 		// the Go runtime does not expose siginfo_t so SIGCONT is handled in C to check si_pid | ||||
| 		if _, err = C.hakurei_shim_setup_cont_signal(C.pid_t(params.Monitor)); err != nil { | ||||
| 			log.Fatalf("cannot install SIGCONT handler: %v", err) | ||||
| 		} | ||||
| 
 | ||||
| 		// pdeath_signal delivery is checked as if the dying process called kill(2), see kernel/exit.c | ||||
| 		if _, _, errno := syscall.Syscall(syscall.SYS_PRCTL, syscall.PR_SET_PDEATHSIG, uintptr(syscall.SIGCONT), 0); errno != 0 { | ||||
| 			log.Fatalf("cannot set parent-death signal: %v", errno) | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	var signalPipe io.ReadCloser | ||||
| 	// the Go runtime does not expose siginfo_t so SIGCONT is handled in C to check si_pid | ||||
| 	if r, w, err := os.Pipe(); err != nil { | ||||
| 		log.Fatalf("cannot pipe: %v", err) | ||||
| 	} else if _, err = C.hakurei_shim_setup_cont_signal(C.pid_t(params.Monitor), C.int(w.Fd())); err != nil { | ||||
| 		log.Fatalf("cannot install SIGCONT handler: %v", err) | ||||
| 	} else { | ||||
| 		defer runtime.KeepAlive(w) | ||||
| 		signalPipe = r | ||||
| 	} | ||||
| 
 | ||||
| 	// pdeath_signal delivery is checked as if the dying process called kill(2), see kernel/exit.c | ||||
| 	if _, _, errno := syscall.Syscall(syscall.SYS_PRCTL, syscall.PR_SET_PDEATHSIG, uintptr(syscall.SIGCONT), 0); errno != 0 { | ||||
| 		log.Fatalf("cannot set parent-death signal: %v", errno) | ||||
| 	} | ||||
| 
 | ||||
| 	// signal handler outcome | ||||
| 	go func() { | ||||
| 		buf := make([]byte, 1) | ||||
| 		for { | ||||
| 			if _, err := signalPipe.Read(buf); err != nil { | ||||
| 				log.Fatalf("cannot read from signal pipe: %v", err) | ||||
| 			} | ||||
| 
 | ||||
| 			switch buf[0] { | ||||
| 			case 0: | ||||
| 				hlog.Resume() | ||||
| 				os.Exit(ShimExitRequest) | ||||
| 				return | ||||
| 
 | ||||
| 			case 1: | ||||
| 				hlog.BeforeExit() | ||||
| 				os.Exit(ShimExitOrphan) | ||||
| 				return | ||||
| 
 | ||||
| 			case 2: | ||||
| 				log.Println("sa_sigaction got invalid siginfo") | ||||
| 
 | ||||
| 			case 3: | ||||
| 				log.Println("got SIGCONT from unexpected process") | ||||
| 
 | ||||
| 			default: | ||||
| 				log.Fatalf("got invalid message %d from signal handler", buf[0]) | ||||
| 			} | ||||
| 		} | ||||
| 	}() | ||||
| 
 | ||||
| 	if params.Container == nil || params.Container.Ops == nil { | ||||
| 		log.Fatal("invalid container params") | ||||
| 	} | ||||
|  | ||||
| @ -181,6 +181,15 @@ 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}") | ||||
| 
 | ||||
| # Check shim SIGCONT from unexpected process behaviour: | ||||
| swaymsg("exec sh -c 'ne-foot &> /tmp/shim-cont-unexpected-pid'") | ||||
| wait_for_window(f"u0_a{aid(0)}@machine") | ||||
| machine.succeed("pkill -CONT -f 'hakurei shim'") | ||||
| machine.succeed("pkill -INT -f 'hakurei -v app '") | ||||
| machine.wait_until_fails("pgrep foot", timeout=5) | ||||
| machine.wait_for_file("/tmp/shim-cont-unexpected-pid") | ||||
| print(machine.succeed('grep "shim: got SIGCONT from unexpected process$" /tmp/shim-cont-unexpected-pid')) | ||||
| 
 | ||||
| # Start app (foot) with Wayland enablement: | ||||
| swaymsg("exec ne-foot") | ||||
| wait_for_window(f"u0_a{aid(0)}@machine") | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user