github.com/sagernet/gvisor@v0.0.0-20240428053021-e691de28565f/pkg/sighandling/sighandling.go (about) 1 // Copyright 2018 The gVisor Authors. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 // Package sighandling contains helpers for handling signals to applications. 16 package sighandling 17 18 import ( 19 "os" 20 "os/signal" 21 "reflect" 22 23 "golang.org/x/sys/unix" 24 "github.com/sagernet/gvisor/pkg/abi/linux" 25 ) 26 27 // numSignals is the number of normal (non-realtime) signals on Linux. 28 const numSignals = 32 29 30 // handleSignals listens for incoming signals and calls the given handler 31 // function. 32 // 33 // It stops when the stop channel is closed. The done channel is closed once it 34 // will no longer deliver signals to k. 35 func handleSignals(sigchans []chan os.Signal, handler func(linux.Signal), stop, done chan struct{}) { 36 // Build a select case. 37 sc := []reflect.SelectCase{{Dir: reflect.SelectRecv, Chan: reflect.ValueOf(stop)}} 38 for _, sigchan := range sigchans { 39 sc = append(sc, reflect.SelectCase{Dir: reflect.SelectRecv, Chan: reflect.ValueOf(sigchan)}) 40 } 41 42 for { 43 // Wait for a notification. 44 index, _, ok := reflect.Select(sc) 45 46 // Was it the stop channel? 47 if index == 0 { 48 if !ok { 49 // Stop forwarding and notify that it's done. 50 close(done) 51 return 52 } 53 continue 54 } 55 56 // How about a different close? 57 if !ok { 58 panic("signal channel closed unexpectedly") 59 } 60 61 // Otherwise, it was a signal on channel N. Index 0 represents the stop 62 // channel, so index N represents the channel for signal N. 63 handler(linux.Signal(index)) 64 } 65 } 66 67 // StartSignalForwarding ensures that synchronous signals are passed to the 68 // given handler function and returns a callback that stops signal delivery. 69 // 70 // Note that this function permanently takes over signal handling. After the 71 // stop callback, signals revert to the default Go runtime behavior, which 72 // cannot be overridden with external calls to signal.Notify. 73 func StartSignalForwarding(handler func(linux.Signal)) func() { 74 stop := make(chan struct{}) 75 done := make(chan struct{}) 76 77 // Register individual channels. One channel per standard signal is 78 // required as os.Notify() is non-blocking and may drop signals. To avoid 79 // this, standard signals have to be queued separately. Channel size 1 is 80 // enough for standard signals as their semantics allow de-duplication. 81 // 82 // External real-time signals are not supported. We rely on the go-runtime 83 // for their handling. 84 // 85 // We do not forward some signals that are likely induced by the behavior 86 // of the forwarding process. 87 var sigchans []chan os.Signal 88 for sig := 1; sig <= numSignals+1; sig++ { 89 sigchan := make(chan os.Signal, 1) 90 sigchans = append(sigchans, sigchan) 91 92 // SIGURG is used by Go's runtime scheduler. 93 if sig == int(linux.SIGURG) { 94 continue 95 } 96 // SIGPIPE is received when sending to disconnected host pipes/sockets. 97 if sig == int(linux.SIGPIPE) { 98 continue 99 } 100 // SIGCHLD is received when a child of the forwarding process exits. 101 if sig == int(linux.SIGCHLD) { 102 continue 103 } 104 signal.Notify(sigchan, unix.Signal(sig)) 105 } 106 // Start up our listener. 107 go handleSignals(sigchans, handler, stop, done) // S/R-SAFE: synchronized by Kernel.extMu. 108 109 return func() { 110 close(stop) 111 <-done 112 } 113 }