github.com/decred/dcrlnd@v0.7.6/signal/signal.go (about)

     1  // Copyright (c) 2013-2017 The btcsuite developers
     2  // Copyright (c) 2015-2016 The Decred developers
     3  // Heavily inspired by https://github.com/decred/dcrd/blob/master/signal.go
     4  // Copyright (C) 2015-2017 The Lightning Network Developers
     5  
     6  package signal
     7  
     8  import (
     9  	"errors"
    10  	"fmt"
    11  	"os"
    12  	"os/signal"
    13  	"sync/atomic"
    14  	"syscall"
    15  
    16  	"github.com/coreos/go-systemd/daemon"
    17  )
    18  
    19  var (
    20  	// started indicates whether we have started our main interrupt handler.
    21  	// This field should be used atomically.
    22  	started int32
    23  )
    24  
    25  // systemdNotifyReady notifies systemd about LND being ready, logs the result of
    26  // the operation or possible error. Besides logging, systemd being unavailable
    27  // is ignored.
    28  func systemdNotifyReady() error {
    29  	notified, err := daemon.SdNotify(false, daemon.SdNotifyReady)
    30  	if err != nil {
    31  		err := fmt.Errorf("failed to notify systemd %v (if you aren't "+
    32  			"running systemd clear the environment variable "+
    33  			"NOTIFY_SOCKET)", err)
    34  		log.Error(err)
    35  
    36  		// The SdNotify doc says it's common to ignore the
    37  		// error. We don't want to ignore it because if someone
    38  		// set up systemd to wait for initialization other
    39  		// processes would get stuck.
    40  		return err
    41  	}
    42  	if notified {
    43  		log.Info("Systemd was notified about our readiness")
    44  	} else {
    45  		log.Info("We're not running within systemd")
    46  	}
    47  	return nil
    48  }
    49  
    50  // systemdNotifyStop notifies systemd that LND is stopping and logs error if
    51  // the notification failed. It also logs if the notification was actually sent.
    52  // Systemd being unavailable is intentionally ignored.
    53  func systemdNotifyStop() {
    54  	notified, err := daemon.SdNotify(false, daemon.SdNotifyStopping)
    55  
    56  	// Just log - we're stopping anyway.
    57  	if err != nil {
    58  		log.Errorf("Failed to notify systemd: %v", err)
    59  	}
    60  	if notified {
    61  		log.Infof("Systemd was notified about stopping")
    62  	}
    63  }
    64  
    65  // Notifier handles notifications about status of LND.
    66  type Notifier struct {
    67  	// notifiedReady remembers whether Ready was sent to avoid sending it
    68  	// multiple times.
    69  	notifiedReady bool
    70  }
    71  
    72  // NotifyReady notifies other applications that RPC is ready.
    73  func (notifier *Notifier) NotifyReady(walletUnlocked bool) error {
    74  	if !notifier.notifiedReady {
    75  		err := systemdNotifyReady()
    76  		if err != nil {
    77  			return err
    78  		}
    79  		notifier.notifiedReady = true
    80  	}
    81  	if walletUnlocked {
    82  		_, _ = daemon.SdNotify(false, "STATUS=Wallet unlocked")
    83  	} else {
    84  		_, _ = daemon.SdNotify(false, "STATUS=Wallet locked")
    85  	}
    86  
    87  	return nil
    88  }
    89  
    90  // notifyStop notifies other applications that LND is stopping.
    91  func (notifier *Notifier) notifyStop() {
    92  	systemdNotifyStop()
    93  }
    94  
    95  // Interceptor contains channels and methods regarding application shutdown
    96  // and interrupt signals.
    97  type Interceptor struct {
    98  	// interruptChannel is used to receive SIGINT (Ctrl+C) signals.
    99  	interruptChannel chan os.Signal
   100  
   101  	// shutdownChannel is closed once the main interrupt handler exits.
   102  	shutdownChannel chan struct{}
   103  
   104  	// shutdownRequestChannel is used to request the daemon to shutdown
   105  	// gracefully, similar to when receiving SIGINT.
   106  	shutdownRequestChannel chan struct{}
   107  
   108  	// quit is closed when instructing the main interrupt handler to exit.
   109  	// Note that to avoid losing notifications, only shutdown func may
   110  	// close this channel.
   111  	quit chan struct{}
   112  
   113  	// Notifier handles sending shutdown notifications.
   114  	Notifier Notifier
   115  }
   116  
   117  // Intercept starts the interception of interrupt signals and returns an `Interceptor` instance.
   118  // Note that any previous active interceptor must be stopped before a new one can be created.
   119  func Intercept() (Interceptor, error) {
   120  	if !atomic.CompareAndSwapInt32(&started, 0, 1) {
   121  		return Interceptor{}, errors.New("intercept already started")
   122  	}
   123  
   124  	channels := Interceptor{
   125  		interruptChannel:       make(chan os.Signal, 1),
   126  		shutdownChannel:        make(chan struct{}),
   127  		shutdownRequestChannel: make(chan struct{}),
   128  		quit:                   make(chan struct{}),
   129  	}
   130  
   131  	signalsToCatch := []os.Signal{
   132  		os.Interrupt,
   133  		os.Kill,
   134  		syscall.SIGTERM,
   135  		syscall.SIGQUIT,
   136  	}
   137  	signal.Notify(channels.interruptChannel, signalsToCatch...)
   138  	go channels.mainInterruptHandler()
   139  
   140  	return channels, nil
   141  }
   142  
   143  // InterceptNoSignal returns an interceptor that does not listen to process
   144  // signals and relies on calls to ShutdownRequest to terminate.
   145  func InterceptNoSignal() Interceptor {
   146  	channels := Interceptor{
   147  		interruptChannel:       make(chan os.Signal, 1),
   148  		shutdownChannel:        make(chan struct{}),
   149  		shutdownRequestChannel: make(chan struct{}),
   150  		quit:                   make(chan struct{}),
   151  	}
   152  
   153  	go func() {
   154  		<-channels.shutdownRequestChannel
   155  		log.Infof("Received shutdown request.")
   156  		close(channels.quit)
   157  		close(channels.shutdownChannel)
   158  	}()
   159  
   160  	return channels
   161  }
   162  
   163  // mainInterruptHandler listens for SIGINT (Ctrl+C) signals on the
   164  // interruptChannel and shutdown requests on the shutdownRequestChannel, and
   165  // invokes the registered interruptCallbacks accordingly. It also listens for
   166  // callback registration.
   167  // It must be run as a goroutine.
   168  func (c *Interceptor) mainInterruptHandler() {
   169  	defer atomic.StoreInt32(&started, 0)
   170  	// isShutdown is a flag which is used to indicate whether or not
   171  	// the shutdown signal has already been received and hence any future
   172  	// attempts to add a new interrupt handler should invoke them
   173  	// immediately.
   174  	var isShutdown bool
   175  
   176  	// shutdown invokes the registered interrupt handlers, then signals the
   177  	// shutdownChannel.
   178  	shutdown := func() {
   179  		// Ignore more than one shutdown signal.
   180  		if isShutdown {
   181  			log.Infof("Already shutting down...")
   182  			return
   183  		}
   184  		isShutdown = true
   185  		log.Infof("Shutting down...")
   186  		c.Notifier.notifyStop()
   187  
   188  		// Signal the main interrupt handler to exit, and stop accept
   189  		// post-facto requests.
   190  		close(c.quit)
   191  	}
   192  
   193  	for {
   194  		select {
   195  		case signal := <-c.interruptChannel:
   196  			log.Infof("Received %v", signal)
   197  			shutdown()
   198  
   199  		case <-c.shutdownRequestChannel:
   200  			log.Infof("Received shutdown request.")
   201  			shutdown()
   202  
   203  		case <-c.quit:
   204  			log.Infof("Gracefully shutting down.")
   205  			close(c.shutdownChannel)
   206  			signal.Stop(c.interruptChannel)
   207  			return
   208  		}
   209  	}
   210  }
   211  
   212  // Listening returns true if the main interrupt handler has been started, and
   213  // has not been killed.
   214  func (c *Interceptor) Listening() bool {
   215  	// If our started field is not set, we are not yet listening for
   216  	// interrupts.
   217  	if atomic.LoadInt32(&started) != 1 {
   218  		return false
   219  	}
   220  
   221  	// If we have started our main goroutine, we check whether we have
   222  	// stopped it yet.
   223  	return c.Alive()
   224  }
   225  
   226  // Alive returns true if the main interrupt handler has not been killed.
   227  func (c *Interceptor) Alive() bool {
   228  	select {
   229  	case <-c.quit:
   230  		return false
   231  	default:
   232  		return true
   233  	}
   234  }
   235  
   236  // RequestShutdown initiates a graceful shutdown from the application.
   237  func (c *Interceptor) RequestShutdown() {
   238  	select {
   239  	case c.shutdownRequestChannel <- struct{}{}:
   240  	case <-c.quit:
   241  	}
   242  }
   243  
   244  // ShutdownChannel returns the channel that will be closed once the main
   245  // interrupt handler has exited.
   246  func (c *Interceptor) ShutdownChannel() <-chan struct{} {
   247  	return c.shutdownChannel
   248  }