Statistics
| Branch: | Tag: | Revision:

root / lib / daemon.py @ b14b975f

History | View | Annotate | Download (5.2 kB)

1
#
2
#
3

    
4
# Copyright (C) 2006, 2007, 2008 Google Inc.
5
#
6
# This program is free software; you can redistribute it and/or modify
7
# it under the terms of the GNU General Public License as published by
8
# the Free Software Foundation; either version 2 of the License, or
9
# (at your option) any later version.
10
#
11
# This program is distributed in the hope that it will be useful, but
12
# WITHOUT ANY WARRANTY; without even the implied warranty of
13
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14
# General Public License for more details.
15
#
16
# You should have received a copy of the GNU General Public License
17
# along with this program; if not, write to the Free Software
18
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19
# 02110-1301, USA.
20

    
21

    
22
"""Module with helper classes and functions for daemons"""
23

    
24

    
25
import select
26
import signal
27
import errno
28

    
29
from ganeti import utils
30

    
31

    
32
class Mainloop(object):
33
  """Generic mainloop for daemons
34

35
  """
36
  def __init__(self):
37
    """Constructs a new Mainloop instance.
38

39
    """
40
    self._io_wait = {}
41
    self._io_wait_add = []
42
    self._io_wait_remove = []
43
    self._signal_wait = []
44

    
45
  def Run(self, handle_sigchld=True, handle_sigterm=True, stop_on_empty=False):
46
    """Runs the mainloop.
47

48
    @type handle_sigchld: bool
49
    @param handle_sigchld: Whether to install handler for SIGCHLD
50
    @type handle_sigterm: bool
51
    @param handle_sigterm: Whether to install handler for SIGTERM
52
    @type stop_on_empty: bool
53
    @param stop_on_empty: Whether to stop mainloop once all I/O waiters
54
                          unregistered
55

56
    """
57
    poller = select.poll()
58

    
59
    # Setup signal handlers
60
    if handle_sigchld:
61
      sigchld_handler = utils.SignalHandler([signal.SIGCHLD])
62
    else:
63
      sigchld_handler = None
64
    try:
65
      if handle_sigterm:
66
        sigterm_handler = utils.SignalHandler([signal.SIGTERM])
67
      else:
68
        sigterm_handler = None
69

    
70
      try:
71
        running = True
72

    
73
        # Start actual main loop
74
        while running:
75
          # Entries could be added again afterwards, hence removing first
76
          if self._io_wait_remove:
77
            for fd in self._io_wait_remove:
78
              try:
79
                poller.unregister(fd)
80
              except KeyError:
81
                pass
82
              try:
83
                del self._io_wait[fd]
84
              except KeyError:
85
                pass
86
            self._io_wait_remove = []
87

    
88
          # Add new entries
89
          if self._io_wait_add:
90
            for (owner, fd, conditions) in self._io_wait_add:
91
              self._io_wait[fd] = owner
92
              poller.register(fd, conditions)
93
            self._io_wait_add = []
94

    
95
          # Stop if nothing is listening anymore
96
          if stop_on_empty and not self._io_wait:
97
            break
98

    
99
          # Wait for I/O events
100
          try:
101
            io_events = poller.poll()
102
          except select.error, err:
103
            # EINTR can happen when signals are sent
104
            if err.args and err.args[0] in (errno.EINTR,):
105
              io_events = None
106
            else:
107
              raise
108

    
109
          if io_events:
110
            # Check for I/O events
111
            for (evfd, evcond) in io_events:
112
              owner = self._io_wait.get(evfd, None)
113
              if owner:
114
                owner.OnIO(evfd, evcond)
115

    
116
          # Check whether signal was raised
117
          if sigchld_handler and sigchld_handler.called:
118
            self._CallSignalWaiters(signal.SIGCHLD)
119
            sigchld_handler.Clear()
120

    
121
          if sigterm_handler and sigterm_handler.called:
122
            self._CallSignalWaiters(signal.SIGTERM)
123
            running = False
124
            sigterm_handler.Clear()
125
      finally:
126
        # Restore signal handlers
127
        if sigterm_handler:
128
          sigterm_handler.Reset()
129
    finally:
130
      if sigchld_handler:
131
        sigchld_handler.Reset()
132

    
133
  def _CallSignalWaiters(self, signum):
134
    """Calls all signal waiters for a certain signal.
135

136
    @type signum: int
137
    @param signum: Signal number
138

139
    """
140
    for owner in self._signal_wait:
141
      owner.OnSignal(signal.SIGCHLD)
142

    
143
  def RegisterIO(self, owner, fd, condition):
144
    """Registers a receiver for I/O notifications
145

146
    The receiver must support a "OnIO(self, fd, conditions)" function.
147

148
    @type owner: instance
149
    @param owner: Receiver
150
    @type fd: int
151
    @param fd: File descriptor
152
    @type condition: int
153
    @param condition: ORed field of conditions to be notified
154
                      (see select module)
155

156
    """
157
    # select.Poller also supports file() like objects, but we don't.
158
    assert isinstance(fd, (int, long)), \
159
      "Only integers are supported for file descriptors"
160

    
161
    self._io_wait_add.append((owner, fd, condition))
162

    
163
  def UnregisterIO(self, fd):
164
    """Unregister a file descriptor.
165

166
    It'll be unregistered the next time the mainloop checks for it.
167

168
    @type fd: int
169
    @param fd: File descriptor
170

171
    """
172
    # select.Poller also supports file() like objects, but we don't.
173
    assert isinstance(fd, (int, long)), \
174
      "Only integers are supported for file descriptors"
175

    
176
    self._io_wait_remove.append(fd)
177

    
178
  def RegisterSignal(self, owner):
179
    """Registers a receiver for signal notifications
180

181
    The receiver must support a "OnSignal(self, signum)" function.
182

183
    @type owner: instance
184
    @param owner: Receiver
185

186
    """
187
    self._signal_wait.append(owner)