[openlmi-tools/f20] fix signal handling
Peter Hatina
phatina at fedoraproject.org
Mon Sep 22 07:09:45 UTC 2014
commit c5237b3957bd7d89cfab7c8743fec218c3f43943
Author: Peter Hatina <phatina at redhat.com>
Date: Mon Sep 22 08:28:36 2014 +0200
fix signal handling
openlmi-tools-02-signal.patch | 229 +++++++++++++++++++++++++++++++++++++++++
openlmi-tools.spec | 8 ++-
2 files changed, 236 insertions(+), 1 deletions(-)
---
diff --git a/openlmi-tools-02-signal.patch b/openlmi-tools-02-signal.patch
new file mode 100644
index 0000000..d202962
--- /dev/null
+++ b/openlmi-tools-02-signal.patch
@@ -0,0 +1,229 @@
+diff --git a/cli/lmi/shell/LMIMethod.py b/cli/lmi/shell/LMIMethod.py
+index dc1e8d1..9ce1f82 100644
+--- a/cli/lmi/shell/LMIMethod.py
++++ b/cli/lmi/shell/LMIMethod.py
+@@ -55,53 +55,95 @@ from LMIExceptions import LMIHandlerNamePatternError
+
+ logger = logging.getLogger(__name__)
+
+-class LMISignalHelper(object):
++
++class LMISignalHelperBase(object):
+ """
+- Helper class, which takes care of signal (de)registration and handling.
++ Base signal handling class.
+ """
+
+- _instance = None
+-
+- def __new__(cls):
+- if cls._instance is None:
+- cls._instance = super(LMISignalHelper, cls).__new__(cls)
+- LMISignalHelper.reset(cls._instance)
+- return cls._instance
++ @staticmethod
++ def signal(signo, handler):
++ """
++ Calls signal() for signo, handler and returns the old signal handler.
++ If signo is list of signals, the signal() call is applied for each
++ signo. If handler is also list, each signal from signo will be handled
++ by corresponding handler. In such case, tuple of previous handlers will
++ be returned.
++ """
++ if isinstance(signo, (list, tuple)):
++ if not isinstance(handler, (list, tuple)):
++ handler = [handler] * len(signo)
++ signo_handler = zip(signo, handler)
++ if not signo_handler:
++ return (None,) * len(signo)
++ old_handlers = []
++ for signal, handler in signo_handler:
++ old_handlers.append(
++ LMISignalHelperBase.signal_core(signal, handler))
++ return tuple(old_handlers)
++ else:
++ return LMISignalHelperBase.signal_core(signal, handler)
+
+- def reset(self):
++ @staticmethod
++ def signal_core(signo, handler):
+ """
+- Resets the single instance into default state.
++ Wrapper method for signal.signal(). In case of ValueError, it returns
++ None, old signal handler otherwise. If handler is None, default signal
++ handler is set for such signal.
+ """
+- self._handler_sigint = None
+- self._handler_sigterm = None
+- self._signal_handled = False
+- self._instance._callbacks = collections.OrderedDict()
++ try:
++ if handler is None:
++ handler = signal.SIG_DFL
++ return signal.signal(signo, handler)
++ except ValueError:
++ return None
++
++
++class LMIMethodSignalHelper(LMISignalHelperBase):
++ """
++ Helper class which takes care of signal (de)registration and handling.
++ """
++
++ INTERRUPT_SIGNALS = (
++ signal.SIGINT,
++ signal.SIGTERM)
++
++ def __init__(self):
++ super(LMIMethodSignalHelper, self).__init__()
++ self._signal_handled = False
++ self._signal_prev_handlers = (signal.SIG_DFL,) * 2
++ self._callbacks = collections.OrderedDict()
+
+ def signal_attach(self):
+ """
+- Registers *SIGINT* and *SIGTERM* signals to local handler in which, the flags for
+- each signal are modified, if such signal is caught.
++ Registers *SIGINT* and *SIGTERM* signals to local handler in which, the
++ flags for each signal are modified, if such signal is caught.
+ """
+- self._signal_handled = False
+- self._handler_sigint = signal.signal(signal.SIGINT, LMISignalHelper.__signal_handler)
+- self._handler_sigterm = signal.signal(signal.SIGTERM, LMISignalHelper.__signal_handler)
++ def handler(sig, action):
++ self.signal_handler(sig, action)
++
++ self._signal_handled = False
++ self._signal_prev_handlers = self.signal(
++ self.INTERRUPT_SIGNALS, handler)
+
+ def signal_detach(self):
+ """
+- Unregisters *SIGINT* and *SIGTERM* handler and removes all the attached callbacks.
++ Unregisters *SIGINT* and *SIGTERM* handler and removes all the attached
++ callbacks.
+ """
+- signal.signal(signal.SIGINT, self._handler_sigint)
+- signal.signal(signal.SIGTERM, self._handler_sigterm)
++ self.signal(self.INTERRUPT_SIGNALS, self._signal_prev_handlers)
+
+ def signal_handled(self):
+ """
+- :returns: True, if any of *SIGINT* or *SIGTERM* has been caught; False otherwise
++ :returns: True, if any of *SIGINT* or *SIGTERM* has been caught; \
++ False otherwise
+ """
+ return self._signal_handled
+
+ def callback_attach(self, cb_name, cb):
+ """
+- Registers a callback, which will be called when a *SIGINT* or *SIGTERM* is caught.
++ Registers a callback, which will be called when a *SIGINT* or *SIGTERM*
++ is caught.
+
+ :param string cb_name: callback name
+ :param cb: callable object, which takes zero arguments
+@@ -116,21 +158,18 @@ class LMISignalHelper(object):
+ """
+ self._callbacks.pop(cb_name)
+
+- @staticmethod
+- def __signal_handler(signo, frame):
++ def signal_handler(self, signo, frame):
+ """
+- Signal handler, which is called, when *SIGINT* and *SIGTERM* are sent to
+- the LMIShell.
++ Signal handler, which is called, when *SIGINT* and *SIGTERM* are sent
++ to the LMIShell.
+
+ :param int signo: signal number
+ :param frame: -- stack frame
+-
+- **NOTE:** see help(signal)
+ """
+- if signo in (signal.SIGINT, signal.SIGTERM):
+- LMISignalHelper._instance._signal_handled = True
+- for cb in LMISignalHelper._instance._callbacks.values():
+- cb()
++ if signo in self.INTERRUPT_SIGNALS:
++ self._signal_handled = True
++ [cb() for cb in self._callbacks.values()]
++
+
+ class LMIMethod(LMIWrapperBaseObject):
+ """
+@@ -350,13 +389,15 @@ class LMIMethod(LMIWrapperBaseObject):
+
+ # Register signal callback for SIGINT, SIGTERM with callback,
+ # which awakes waiting thread for immediate return.
+- LMISignalHelper().callback_attach("indication", lambda: LMIMethod.__wake(cond))
+- LMISignalHelper().signal_attach()
++ signal_helper = LMIMethodSignalHelper()
++ signal_helper.callback_attach(
++ "indication", lambda: LMIMethod.__wake(cond))
++ signal_helper.signal_attach()
+
+ # Wait for the job to finish
+ wake_cnt = 0
+ cond.acquire()
+- while not LMISignalHelper().signal_handled() and \
++ while not signal_helper.signal_handled() and \
+ not job_finished.value and \
+ not lmi_is_job_finished(job_inst):
+ cond.wait(LMIMethod._COND_WAIT_TIME)
+@@ -382,8 +423,8 @@ class LMIMethod(LMIWrapperBaseObject):
+ break
+
+ # Unregister signal handler
+- LMISignalHelper().signal_detach()
+- LMISignalHelper().callback_detach("indication")
++ signal_helper.signal_detach()
++ signal_helper.callback_detach("indication")
+
+ cond.release()
+
+@@ -393,7 +434,7 @@ class LMIMethod(LMIWrapperBaseObject):
+ self._conn._client._delete_instance(cim_handler.path)
+ if job_exception.value:
+ raise job_exception.value
+- if LMISignalHelper().signal_handled() and not job_finished.value:
++ if signal_helper.signal_handled() and not job_finished.value:
+ # We got SIGINT or SIGTERM, when waiting for the job, cancelling the job
+ logger.warn("Cancelling a job '%s'" % job_inst.Name)
+ job_inst.RequestStateChange(
+@@ -418,8 +459,10 @@ class LMIMethod(LMIWrapperBaseObject):
+
+ # Register signal callback for SIGINT, SIGTERM with callback,
+ # which awakes waiting thread for immediate return.
+- LMISignalHelper().callback_attach("polling", lambda: LMIMethod.__wake(cond))
+- LMISignalHelper().signal_attach()
++ signal_helper = LMIMethodSignalHelper()
++ signal_helper.callback_attach(
++ "polling", lambda: LMIMethod.__wake(cond))
++ signal_helper.signal_attach()
+
+ cond = threading.Condition()
+ cond.acquire()
+@@ -428,7 +471,7 @@ class LMIMethod(LMIWrapperBaseObject):
+
+ try:
+ sleep_time = 1
+- while not LMISignalHelper().signal_handled() and \
++ while not signal_helper.signal_handled() and \
+ not lmi_is_job_finished(job_inst):
+ # Sleep, a bit longer in every iteration
+ cond.wait(sleep_time)
+@@ -448,10 +491,10 @@ class LMIMethod(LMIWrapperBaseObject):
+ cond.release()
+
+ # Unregister signal handler and callback
+- LMISignalHelper().signal_detach()
+- LMISignalHelper().callback_detach("polling")
++ signal_helper.signal_detach()
++ signal_helper.callback_detach("polling")
+
+- if LMISignalHelper().signal_handled() and not lmi_is_job_finished(job_inst):
++ if signal_helper.signal_handled() and not lmi_is_job_finished(job_inst):
+ # We got SIGINT or SIGTERM, when waiting for the job, cancelling the job
+ logger.warn("Cancelling a job '%s'" % job_inst.Name)
+ job_inst.RequestStateChange(
diff --git a/openlmi-tools.spec b/openlmi-tools.spec
index 24ac307..6278fbd 100644
--- a/openlmi-tools.spec
+++ b/openlmi-tools.spec
@@ -1,12 +1,13 @@
Name: openlmi-tools
Version: 0.9.2
-Release: 2%{?dist}
+Release: 3%{?dist}
Summary: Set of CLI tools for Openlmi providers
License: GPLv2+
URL: http://fedorahosted.org/openlmi
Source0: http://fedorahosted.org/released/openlmi-tools/%{name}-%{version}.tar.gz
Patch0: openlmi-tools-01-logging.patch
+Patch1: openlmi-tools-02-signal.patch
BuildArch: noarch
BuildRequires: automake
@@ -44,6 +45,7 @@ Group: Documentation
%prep
%setup -q
%patch0 -p1 -b .logging
+%patch1 -p1 -b .signal
%build
make
@@ -116,6 +118,10 @@ install -m 644 cli/completion/_lmishell $zsh_comp_dir
%{_docdir}/%{name}-%{version}/html
%changelog
+* Mon Sep 22 2014 Peter Hatina <phatina at redhat.com> - 0.9.2-3
+- fix signal handling
+- Resolves: rhbz#1138573
+
* Wed Jul 16 2014 Peter Hatina <phatina at redhat.com> - 0.9.2-2
- fix unset logging handlers
- Resolves: rhbz#1119747
More information about the scm-commits
mailing list