1# Copyright (c) 2010, 2011 Nicira, 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 15import atexit 16import os 17import signal 18import sys 19 20import ovs.vlog 21 22_hooks = [] 23vlog = ovs.vlog.Vlog("fatal-signal") 24 25 26def add_hook(hook, cancel, run_at_exit): 27 _init() 28 _hooks.append((hook, cancel, run_at_exit)) 29 30 31def fork(): 32 """Clears all of the fatal signal hooks without executing them. If any of 33 the hooks passed a 'cancel' function to add_hook(), then those functions 34 will be called, allowing them to free resources, etc. 35 36 Following a fork, one of the resulting processes can call this function to 37 allow it to terminate without calling the hooks registered before calling 38 this function. New hooks registered after calling this function will take 39 effect normally.""" 40 global _hooks 41 for hook, cancel, run_at_exit in _hooks: 42 if cancel: 43 cancel() 44 45 _hooks = [] 46 47 48_added_hook = False 49_files = {} 50 51 52def add_file_to_unlink(file): 53 """Registers 'file' to be unlinked when the program terminates via 54 sys.exit() or a fatal signal.""" 55 global _added_hook 56 if not _added_hook: 57 _added_hook = True 58 add_hook(_unlink_files, _cancel_files, True) 59 _files[file] = None 60 61 62def add_file_to_close_and_unlink(file, fd=None): 63 """Registers 'file' to be unlinked when the program terminates via 64 sys.exit() or a fatal signal and the 'fd' to be closed. On Windows a file 65 cannot be removed while it is open for writing.""" 66 global _added_hook 67 if not _added_hook: 68 _added_hook = True 69 add_hook(_unlink_files, _cancel_files, True) 70 _files[file] = fd 71 72 73def remove_file_to_unlink(file): 74 """Unregisters 'file' from being unlinked when the program terminates via 75 sys.exit() or a fatal signal.""" 76 if file in _files: 77 del _files[file] 78 79 80def unlink_file_now(file): 81 """Like fatal_signal_remove_file_to_unlink(), but also unlinks 'file'. 82 Returns 0 if successful, otherwise a positive errno value.""" 83 error = _unlink(file) 84 if error: 85 vlog.warn("could not unlink \"%s\" (%s)" % (file, os.strerror(error))) 86 remove_file_to_unlink(file) 87 return error 88 89 90def _unlink_files(): 91 for file_ in _files: 92 if sys.platform == "win32" and _files[file_]: 93 _files[file_].close() 94 _unlink(file_) 95 96 97def _cancel_files(): 98 global _added_hook 99 global _files 100 _added_hook = False 101 _files = {} 102 103 104def _unlink(file_): 105 try: 106 os.unlink(file_) 107 return 0 108 except OSError as e: 109 return e.errno 110 111 112def _signal_handler(signr, _): 113 _call_hooks(signr) 114 115 # Re-raise the signal with the default handling so that the program 116 # termination status reflects that we were killed by this signal. 117 signal.signal(signr, signal.SIG_DFL) 118 os.kill(os.getpid(), signr) 119 120 121def _atexit_handler(): 122 _call_hooks(0) 123 124 125recurse = False 126 127 128def _call_hooks(signr): 129 global recurse 130 if recurse: 131 return 132 recurse = True 133 134 for hook, cancel, run_at_exit in _hooks: 135 if signr != 0 or run_at_exit: 136 hook() 137 138 139_inited = False 140 141 142def _init(): 143 global _inited 144 if not _inited: 145 _inited = True 146 if sys.platform == "win32": 147 signals = [signal.SIGTERM, signal.SIGINT] 148 else: 149 signals = [signal.SIGTERM, signal.SIGINT, signal.SIGHUP, 150 signal.SIGALRM] 151 152 for signr in signals: 153 if signal.getsignal(signr) == signal.SIG_DFL: 154 signal.signal(signr, _signal_handler) 155 atexit.register(_atexit_handler) 156 157 158def signal_alarm(timeout): 159 if sys.platform == "win32": 160 import os 161 import time 162 import threading 163 164 class Alarm (threading.Thread): 165 def __init__(self, timeout): 166 super(Alarm, self).__init__() 167 self.timeout = timeout 168 self.setDaemon(True) 169 170 def run(self): 171 time.sleep(self.timeout) 172 os._exit(1) 173 174 alarm = Alarm(timeout) 175 alarm.start() 176 else: 177 signal.alarm(timeout) 178