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