github.com/nicocha30/gvisor-ligolo@v0.0.0-20230726075806-989fa2c0a413/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/nicocha30/gvisor-ligolo/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  }