1// Copyright 2015 CoreOS, Inc.
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		syscall.Kill(pid, sig.(syscall.Signal))
72	}()
73}
74
75// Exit relays to os.Exit if no interrupt handlers are running, blocks otherwise.
76func Exit(code int) {
77	interruptExitMu.Lock()
78	os.Exit(code)
79}
80