daemon.py 5.24 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
#
#

# Copyright (C) 2006, 2007, 2008 Google Inc.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program 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
# General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
# 02110-1301, USA.


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


import select
import signal
import errno

from ganeti import utils


class Mainloop(object):
  """Generic mainloop for daemons

  """
  def __init__(self):
37
38
39
40
41
42
    """Constructs a new Mainloop instance.

    """
    self._io_wait = {}
    self._io_wait_add = []
    self._io_wait_remove = []
43
44
    self._signal_wait = []

45
46
47
48
49
50
51
52
53
54
55
56
  def Run(self, handle_sigchld=True, handle_sigterm=True, stop_on_empty=False):
    """Runs the mainloop.

    @type handle_sigchld: bool
    @param handle_sigchld: Whether to install handler for SIGCHLD
    @type handle_sigterm: bool
    @param handle_sigterm: Whether to install handler for SIGTERM
    @type stop_on_empty: bool
    @param stop_on_empty: Whether to stop mainloop once all I/O waiters
                          unregistered

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

59
60
61
62
63
    # Setup signal handlers
    if handle_sigchld:
      sigchld_handler = utils.SignalHandler([signal.SIGCHLD])
    else:
      sigchld_handler = None
64
    try:
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
      if handle_sigterm:
        sigterm_handler = utils.SignalHandler([signal.SIGTERM])
      else:
        sigterm_handler = None

      try:
        running = True

        # Start actual main loop
        while running:
          # Entries could be added again afterwards, hence removing first
          if self._io_wait_remove:
            for fd in self._io_wait_remove:
              try:
                poller.unregister(fd)
              except KeyError:
                pass
              try:
                del self._io_wait[fd]
              except KeyError:
                pass
            self._io_wait_remove = []

          # Add new entries
          if self._io_wait_add:
            for (owner, fd, conditions) in self._io_wait_add:
              self._io_wait[fd] = owner
              poller.register(fd, conditions)
            self._io_wait_add = []

          # Stop if nothing is listening anymore
          if stop_on_empty and not self._io_wait:
            break

          # Wait for I/O events
          try:
            io_events = poller.poll()
          except select.error, err:
            # EINTR can happen when signals are sent
            if err.args and err.args[0] in (errno.EINTR,):
              io_events = None
            else:
              raise

          if io_events:
            # Check for I/O events
            for (evfd, evcond) in io_events:
              owner = self._io_wait.get(evfd, None)
              if owner:
                owner.OnIO(evfd, evcond)

          # Check whether signal was raised
          if sigchld_handler and sigchld_handler.called:
            self._CallSignalWaiters(signal.SIGCHLD)
            sigchld_handler.Clear()

          if sigterm_handler and sigterm_handler.called:
            self._CallSignalWaiters(signal.SIGTERM)
            running = False
            sigterm_handler.Clear()
      finally:
        # Restore signal handlers
        if sigterm_handler:
          sigterm_handler.Reset()
129
    finally:
130
131
      if sigchld_handler:
        sigchld_handler.Reset()
Guido Trotter's avatar
Guido Trotter committed
132

133
134
135
136
137
138
139
140
141
  def _CallSignalWaiters(self, signum):
    """Calls all signal waiters for a certain signal.

    @type signum: int
    @param signum: Signal number

    """
    for owner in self._signal_wait:
      owner.OnSignal(signal.SIGCHLD)
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156

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

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

    @type owner: instance
    @param owner: Receiver
    @type fd: int
    @param fd: File descriptor
    @type condition: int
    @param condition: ORed field of conditions to be notified
                      (see select module)

    """
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
    # select.Poller also supports file() like objects, but we don't.
    assert isinstance(fd, (int, long)), \
      "Only integers are supported for file descriptors"

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

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

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

    @type fd: int
    @param fd: File descriptor

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

    self._io_wait_remove.append(fd)
177
178
179
180
181
182
183
184
185
186
187

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

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

    @type owner: instance
    @param owner: Receiver

    """
    self._signal_wait.append(owner)