f2edc8598aba8d50ab898cd475d75b01f45712e8
[sliver-openvswitch.git] / python / ovs / fatal_signal.py
1 # Copyright (c) 2010, 2011 Nicira Networks
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 import atexit
16 import logging
17 import os
18 import signal
19
20 _hooks = []
21
22
23 def add_hook(hook, cancel, run_at_exit):
24     _init()
25     _hooks.append((hook, cancel, run_at_exit))
26
27
28 def fork():
29     """Clears all of the fatal signal hooks without executing them.  If any of
30     the hooks passed a 'cancel' function to add_hook(), then those functions
31     will be called, allowing them to free resources, etc.
32
33     Following a fork, one of the resulting processes can call this function to
34     allow it to terminate without calling the hooks registered before calling
35     this function.  New hooks registered after calling this function will take
36     effect normally."""
37     global _hooks
38     for hook, cancel, run_at_exit in _hooks:
39         if cancel:
40             cancel()
41
42     _hooks = []
43 \f
44 _added_hook = False
45 _files = {}
46
47
48 def add_file_to_unlink(file):
49     """Registers 'file' to be unlinked when the program terminates via
50     sys.exit() or a fatal signal."""
51     global _added_hook
52     if not _added_hook:
53         _added_hook = True
54         add_hook(_unlink_files, _cancel_files, True)
55     _files[file] = None
56
57
58 def remove_file_to_unlink(file):
59     """Unregisters 'file' from being unlinked when the program terminates via
60     sys.exit() or a fatal signal."""
61     if file in _files:
62         del _files[file]
63
64
65 def unlink_file_now(file):
66     """Like fatal_signal_remove_file_to_unlink(), but also unlinks 'file'.
67     Returns 0 if successful, otherwise a positive errno value."""
68     error = _unlink(file)
69     if error:
70         logging.warning("could not unlink \"%s\" (%s)"
71                         % (file, os.strerror(error)))
72     remove_file_to_unlink(file)
73     return error
74
75
76 def _unlink_files():
77     for file_ in _files:
78         _unlink(file_)
79
80
81 def _cancel_files():
82     global _added_hook
83     global _files
84     _added_hook = False
85     _files = {}
86
87
88 def _unlink(file_):
89     try:
90         os.unlink(file_)
91         return 0
92     except OSError, e:
93         return e.errno
94
95 \f
96 def _signal_handler(signr, _):
97     _call_hooks(signr)
98
99     # Re-raise the signal with the default handling so that the program
100     # termination status reflects that we were killed by this signal.
101     signal.signal(signr, signal.SIG_DFL)
102     os.kill(os.getpid(), signr)
103
104
105 def _atexit_handler():
106     _call_hooks(0)
107
108
109 recurse = False
110
111
112 def _call_hooks(signr):
113     global recurse
114     if recurse:
115         return
116     recurse = True
117
118     for hook, cancel, run_at_exit in _hooks:
119         if signr != 0 or run_at_exit:
120             hook()
121
122
123 _inited = False
124
125
126 def _init():
127     global _inited
128     if not _inited:
129         _inited = True
130
131         for signr in (signal.SIGTERM, signal.SIGINT,
132                       signal.SIGHUP, signal.SIGALRM):
133             if signal.getsignal(signr) == signal.SIG_DFL:
134                 signal.signal(signr, _signal_handler)
135         atexit.register(_atexit_handler)