1// Copyright 2015 The etcd 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//go:build !windows && !plan9 16// +build !windows,!plan9 17 18package osutil 19 20import ( 21 "os" 22 "os/signal" 23 "sync" 24 "syscall" 25 26 "go.uber.org/zap" 27) 28 29// InterruptHandler is a function that is called on receiving a 30// SIGTERM or SIGINT signal. 31type InterruptHandler func() 32 33var ( 34 interruptRegisterMu, interruptExitMu sync.Mutex 35 // interruptHandlers holds all registered InterruptHandlers in order 36 // they will be executed. 37 interruptHandlers = []InterruptHandler{} 38) 39 40// RegisterInterruptHandler registers a new InterruptHandler. Handlers registered 41// after interrupt handing was initiated will not be executed. 42func RegisterInterruptHandler(h InterruptHandler) { 43 interruptRegisterMu.Lock() 44 defer interruptRegisterMu.Unlock() 45 interruptHandlers = append(interruptHandlers, h) 46} 47 48// HandleInterrupts calls the handler functions on receiving a SIGINT or SIGTERM. 49func HandleInterrupts(lg *zap.Logger) { 50 notifier := make(chan os.Signal, 1) 51 signal.Notify(notifier, syscall.SIGINT, syscall.SIGTERM) 52 53 go func() { 54 sig := <-notifier 55 56 interruptRegisterMu.Lock() 57 ihs := make([]InterruptHandler, len(interruptHandlers)) 58 copy(ihs, interruptHandlers) 59 interruptRegisterMu.Unlock() 60 61 interruptExitMu.Lock() 62 63 if lg != nil { 64 lg.Info("received signal; shutting down", zap.String("signal", sig.String())) 65 } 66 67 for _, h := range ihs { 68 h() 69 } 70 signal.Stop(notifier) 71 pid := syscall.Getpid() 72 // exit directly if it is the "init" process, since the kernel will not help to kill pid 1. 73 if pid == 1 { 74 os.Exit(0) 75 } 76 setDflSignal(sig.(syscall.Signal)) 77 syscall.Kill(pid, sig.(syscall.Signal)) 78 }() 79} 80 81// Exit relays to os.Exit if no interrupt handlers are running, blocks otherwise. 82func Exit(code int) { 83 interruptExitMu.Lock() 84 os.Exit(code) 85} 86