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> | #include <unistd.h> | ||||||
| 
 | 
 | ||||||
| static pid_t hakurei_shim_param_ppid = -1; | 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) { | static void hakurei_shim_sigaction(int sig, siginfo_t *si, void *ucontext) { | ||||||
|   if (sig != SIGCONT || si == NULL) { |   if (sig != SIGCONT || si == NULL) { | ||||||
|     // unreachable
 |     /* unreachable */ | ||||||
|     fprintf(stderr, "sigaction: sa_sigaction got invalid siginfo\n"); |     hakurei_shim_write("\2", 1); | ||||||
|     return; |     return; | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   // monitor requests shim exit
 |   if (si->si_pid == hakurei_shim_param_ppid) { | ||||||
|   if (si->si_pid == hakurei_shim_param_ppid) |     /* monitor requests shim exit */ | ||||||
|     exit(254); |     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) |   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}; |   struct sigaction new_action = {0}, old_action = {0}; | ||||||
|   if (sigaction(SIGCONT, NULL, &old_action) != 0) |   if (sigaction(SIGCONT, NULL, &old_action) != 0) | ||||||
|     return; |     return; | ||||||
| @ -45,4 +61,5 @@ void hakurei_shim_setup_cont_signal(pid_t ppid) { | |||||||
| 
 | 
 | ||||||
|   errno = 0; |   errno = 0; | ||||||
|   hakurei_shim_param_ppid = ppid; |   hakurei_shim_param_ppid = ppid; | ||||||
|  |   hakurei_shim_fd = fd; | ||||||
| } | } | ||||||
|  | |||||||
| @ -1,3 +1,3 @@ | |||||||
| #include <signal.h> | #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 ( | import ( | ||||||
| 	"context" | 	"context" | ||||||
| 	"errors" | 	"errors" | ||||||
|  | 	"io" | ||||||
| 	"log" | 	"log" | ||||||
| 	"os" | 	"os" | ||||||
| 	"os/exec" | 	"os/exec" | ||||||
| 	"os/signal" | 	"os/signal" | ||||||
|  | 	"runtime" | ||||||
| 	"syscall" | 	"syscall" | ||||||
| 	"time" | 	"time" | ||||||
| 
 | 
 | ||||||
| @ -34,6 +36,13 @@ type shimParams struct { | |||||||
| 	Verbose bool | 	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. | // ShimMain is the main function of the shim process and runs as the unconstrained target user. | ||||||
| func ShimMain() { | func ShimMain() { | ||||||
| 	hlog.Prepare("shim") | 	hlog.Prepare("shim") | ||||||
| @ -58,18 +67,55 @@ func ShimMain() { | |||||||
| 	} else { | 	} else { | ||||||
| 		internal.InstallOutput(params.Verbose) | 		internal.InstallOutput(params.Verbose) | ||||||
| 		closeSetup = f | 		closeSetup = f | ||||||
|  | 	} | ||||||
| 
 | 
 | ||||||
|  | 	var signalPipe io.ReadCloser | ||||||
| 	// the Go runtime does not expose siginfo_t so SIGCONT is handled in C to check si_pid | 	// 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 { | 	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) | 		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 | 	// 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 { | 	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) | 		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 { | 	if params.Container == nil || params.Container.Ops == nil { | ||||||
| 		log.Fatal("invalid container params") | 		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: | if interrupt_exit_code != 254: | ||||||
|     raise Exception(f"unexpected exit code {interrupt_exit_code}") |     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: | # Start app (foot) with Wayland enablement: | ||||||
| swaymsg("exec ne-foot") | swaymsg("exec ne-foot") | ||||||
| wait_for_window(f"u0_a{aid(0)}@machine") | wait_for_window(f"u0_a{aid(0)}@machine") | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user