Init
This commit is contained in:
235
neko_daemonizer_dante/daemonizer/_signals_unix.py
Normal file
235
neko_daemonizer_dante/daemonizer/_signals_unix.py
Normal file
@@ -0,0 +1,235 @@
|
||||
'''
|
||||
LICENSING
|
||||
-------------------------------------------------
|
||||
|
||||
daemoniker: Cross-platform daemonization tools.
|
||||
Copyright (C) 2016 Muterra, Inc.
|
||||
|
||||
Contributors
|
||||
------------
|
||||
Nick Badger
|
||||
badg@muterra.io | badg@nickbadger.com | nickbadger.com
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, write to the
|
||||
Free Software Foundation, Inc.,
|
||||
51 Franklin Street,
|
||||
Fifth Floor,
|
||||
Boston, MA 02110-1301 USA
|
||||
|
||||
------------------------------------------------------
|
||||
'''
|
||||
|
||||
# Global dependencies
|
||||
import os
|
||||
import sys
|
||||
import signal
|
||||
import logging
|
||||
import atexit
|
||||
import traceback
|
||||
import shutil
|
||||
|
||||
# Intra-package dependencies
|
||||
from .utils import platform_specificker
|
||||
from .utils import default_to
|
||||
|
||||
from ._signals_common import _SighandlerCore
|
||||
|
||||
from .exceptions import DaemonikerSignal
|
||||
from .exceptions import SignalError
|
||||
from .exceptions import SIGINT
|
||||
from .exceptions import SIGTERM
|
||||
from .exceptions import SIGABRT
|
||||
|
||||
_SUPPORTED_PLATFORM = platform_specificker(
|
||||
linux_choice = True,
|
||||
win_choice = False,
|
||||
cygwin_choice = False,
|
||||
osx_choice = True,
|
||||
# Dunno if this is a good idea but might as well try
|
||||
other_choice = True
|
||||
)
|
||||
|
||||
if _SUPPORTED_PLATFORM:
|
||||
import fcntl
|
||||
import pwd
|
||||
import grp
|
||||
import resource
|
||||
|
||||
|
||||
# ###############################################
|
||||
# Boilerplate
|
||||
# ###############################################
|
||||
|
||||
|
||||
import logging
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
# Control * imports.
|
||||
__all__ = [
|
||||
# 'Inquisitor',
|
||||
]
|
||||
|
||||
|
||||
# ###############################################
|
||||
# Library
|
||||
# ###############################################
|
||||
|
||||
|
||||
def _restore_any_previous_handler(signum, maybe_handler, force_clear=False):
|
||||
''' Makes sure that a previous handler was actually set, and then
|
||||
restores it.
|
||||
|
||||
maybe_handler is the cached potential handler. If it's the constant
|
||||
(ZeroDivisionError) we've been using to denote nothingness, then
|
||||
maybe_handler will either:
|
||||
|
||||
1. do nothing, if force_clear == False
|
||||
2. restore signal.SIG_DEFL, if force_clear == True
|
||||
'''
|
||||
# Nope, wasn't previously set
|
||||
if maybe_handler == ZeroDivisionError:
|
||||
# Restore the default if we're forcing it
|
||||
if force_clear:
|
||||
signal.signal(signum, signal.SIG_DFL)
|
||||
# (Do nothing otherwise)
|
||||
|
||||
# It was previously set, so re-set it.
|
||||
else:
|
||||
signal.signal(signum, maybe_handler)
|
||||
|
||||
|
||||
class SignalHandler1(_SighandlerCore):
|
||||
''' Signal handling system using lightweight wrapper around built-in
|
||||
signal.signal handling.
|
||||
'''
|
||||
def __init__(self, pid_file, sigint=None, sigterm=None, sigabrt=None):
|
||||
''' Creates a signal handler, using the passed callables. None
|
||||
will assign the default handler (raise in main). passing
|
||||
IGNORE_SIGNAL constant will result in the signal being noop'd.
|
||||
'''
|
||||
self.sigint = sigint
|
||||
self.sigterm = sigterm
|
||||
self.sigabrt = sigabrt
|
||||
|
||||
# Yeah, except this isn't used at all (just here for cross-platform
|
||||
# consistency)
|
||||
self._pidfile = pid_file
|
||||
|
||||
# Assign these to impossible values so we can compare against them
|
||||
# later, when deciding whether or not to restore a previous handler.
|
||||
# Don't use None, in case it gets used to denote a default somewhere.
|
||||
# This is deliberately unconventional.
|
||||
self._old_sigint = ZeroDivisionError
|
||||
self._old_sigterm = ZeroDivisionError
|
||||
self._old_sigabrt = ZeroDivisionError
|
||||
|
||||
self._running = False
|
||||
|
||||
def start(self):
|
||||
''' Starts signal handling.
|
||||
'''
|
||||
if self._running:
|
||||
raise RuntimeError('SignalHandler is already running.')
|
||||
|
||||
# Initialize stuff to values that are impossible for signal.signal.
|
||||
# Don't use None, in case it gets used to denote a default somewhere.
|
||||
# This is deliberately unconventional.
|
||||
old_sigint = ZeroDivisionError
|
||||
old_sigterm = ZeroDivisionError
|
||||
old_sigabrt = ZeroDivisionError
|
||||
|
||||
try:
|
||||
# First we need to make closures around all of our attributes, so
|
||||
# they can be updated after we start listening to signals
|
||||
def sigint_closure(signum, frame):
|
||||
return self.sigint(signum)
|
||||
def sigterm_closure(signum, frame):
|
||||
return self.sigterm(signum)
|
||||
def sigabrt_closure(signum, frame):
|
||||
return self.sigabrt(signum)
|
||||
|
||||
# Now simply register those with signal.signal
|
||||
old_sigint = signal.signal(signal.SIGINT, sigint_closure)
|
||||
old_sigterm = signal.signal(signal.SIGTERM, sigterm_closure)
|
||||
old_sigabrt = signal.signal(signal.SIGABRT, sigabrt_closure)
|
||||
|
||||
# If that fails, restore previous state and reraise
|
||||
except:
|
||||
_restore_any_previous_handler(signal.SIGINT, old_sigint)
|
||||
_restore_any_previous_handler(signal.SIGTERM, old_sigterm)
|
||||
_restore_any_previous_handler(signal.SIGABRT, old_sigabrt)
|
||||
raise
|
||||
|
||||
# If that succeeds, set self._running and cache old handlers
|
||||
else:
|
||||
self._old_sigint = old_sigint
|
||||
self._old_sigterm = old_sigterm
|
||||
self._old_sigabrt = old_sigabrt
|
||||
self._running = True
|
||||
|
||||
def stop(self):
|
||||
''' Stops signal handling, returning all signal handlers to
|
||||
their previous handlers, or restoring their defaults if
|
||||
something went fishy.
|
||||
'''
|
||||
try:
|
||||
_restore_any_previous_handler(
|
||||
signal.SIGINT,
|
||||
self._old_sigint,
|
||||
force_clear = True
|
||||
)
|
||||
_restore_any_previous_handler(
|
||||
signal.SIGTERM,
|
||||
self._old_sigterm,
|
||||
force_clear = True
|
||||
)
|
||||
_restore_any_previous_handler(
|
||||
signal.SIGABRT,
|
||||
self._old_sigabrt,
|
||||
force_clear = True
|
||||
)
|
||||
|
||||
# If we get an exception there, just force restoring all defaults and
|
||||
# reraise
|
||||
except:
|
||||
signal.signal(signal.SIGINT, signal.SIG_DFL)
|
||||
signal.signal(signal.SIGTERM, signal.SIG_DFL)
|
||||
signal.signal(signal.SIGABRT, signal.SIG_DFL)
|
||||
raise
|
||||
|
||||
finally:
|
||||
# See notes above re: using ZeroDivisionError instead of None
|
||||
self._old_sigint = ZeroDivisionError
|
||||
self._old_sigterm = ZeroDivisionError
|
||||
self._old_sigabrt = ZeroDivisionError
|
||||
self._running = False
|
||||
|
||||
@staticmethod
|
||||
def _default_handler(signum, *args):
|
||||
''' The default signal handler for Unix.
|
||||
'''
|
||||
# Just parallel the sighandlers that are available in Windows, because
|
||||
# it is definitely the limiting factor here
|
||||
sigs = {
|
||||
signal.SIGABRT: SIGABRT,
|
||||
signal.SIGINT: SIGINT,
|
||||
signal.SIGTERM: SIGTERM,
|
||||
}
|
||||
|
||||
try:
|
||||
exc = sigs[signum]
|
||||
except KeyError:
|
||||
exc = DaemonikerSignal
|
||||
|
||||
raise exc()
|
||||
Reference in New Issue
Block a user