~justin-fathomdb/nova/justinsb-openstack-api-volumes

« back to all changes in this revision

Viewing changes to vendor/python-daemon/daemon/daemon.py

  • Committer: Jesse Andrews
  • Date: 2010-05-28 06:05:26 UTC
  • Revision ID: git-v1:bf6e6e718cdc7488e2da87b21e258ccc065fe499
initial commit

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# -*- coding: utf-8 -*-
 
2
 
 
3
# daemon/daemon.py
 
4
# Part of python-daemon, an implementation of PEP 3143.
 
5
#
 
6
# Copyright © 2008–2010 Ben Finney <ben+python@benfinney.id.au>
 
7
# Copyright © 2007–2008 Robert Niederreiter, Jens Klein
 
8
# Copyright © 2004–2005 Chad J. Schroeder
 
9
# Copyright © 2003 Clark Evans
 
10
# Copyright © 2002 Noah Spurrier
 
11
# Copyright © 2001 Jürgen Hermann
 
12
#
 
13
# This is free software: you may copy, modify, and/or distribute this work
 
14
# under the terms of the Python Software Foundation License, version 2 or
 
15
# later as published by the Python Software Foundation.
 
16
# No warranty expressed or implied. See the file LICENSE.PSF-2 for details.
 
17
 
 
18
""" Daemon process behaviour.
 
19
    """
 
20
 
 
21
import os
 
22
import sys
 
23
import resource
 
24
import errno
 
25
import signal
 
26
import socket
 
27
import atexit
 
28
 
 
29
 
 
30
class DaemonError(Exception):
 
31
    """ Base exception class for errors from this module. """
 
32
 
 
33
 
 
34
class DaemonOSEnvironmentError(DaemonError, OSError):
 
35
    """ Exception raised when daemon OS environment setup receives error. """
 
36
 
 
37
 
 
38
class DaemonProcessDetachError(DaemonError, OSError):
 
39
    """ Exception raised when process detach fails. """
 
40
 
 
41
 
 
42
class DaemonContext(object):
 
43
    """ Context for turning the current program into a daemon process.
 
44
 
 
45
        A `DaemonContext` instance represents the behaviour settings and
 
46
        process context for the program when it becomes a daemon. The
 
47
        behaviour and environment is customised by setting options on the
 
48
        instance, before calling the `open` method.
 
49
 
 
50
        Each option can be passed as a keyword argument to the `DaemonContext`
 
51
        constructor, or subsequently altered by assigning to an attribute on
 
52
        the instance at any time prior to calling `open`. That is, for
 
53
        options named `wibble` and `wubble`, the following invocation::
 
54
 
 
55
            foo = daemon.DaemonContext(wibble=bar, wubble=baz)
 
56
            foo.open()
 
57
 
 
58
        is equivalent to::
 
59
 
 
60
            foo = daemon.DaemonContext()
 
61
            foo.wibble = bar
 
62
            foo.wubble = baz
 
63
            foo.open()
 
64
 
 
65
        The following options are defined.
 
66
 
 
67
        `files_preserve`
 
68
            :Default: ``None``
 
69
 
 
70
            List of files that should *not* be closed when starting the
 
71
            daemon. If ``None``, all open file descriptors will be closed.
 
72
 
 
73
            Elements of the list are file descriptors (as returned by a file
 
74
            object's `fileno()` method) or Python `file` objects. Each
 
75
            specifies a file that is not to be closed during daemon start.
 
76
 
 
77
        `chroot_directory`
 
78
            :Default: ``None``
 
79
 
 
80
            Full path to a directory to set as the effective root directory of
 
81
            the process. If ``None``, specifies that the root directory is not
 
82
            to be changed.
 
83
 
 
84
        `working_directory`
 
85
            :Default: ``'/'``
 
86
 
 
87
            Full path of the working directory to which the process should
 
88
            change on daemon start.
 
89
 
 
90
            Since a filesystem cannot be unmounted if a process has its
 
91
            current working directory on that filesystem, this should either
 
92
            be left at default or set to a directory that is a sensible “home
 
93
            directory” for the daemon while it is running.
 
94
 
 
95
        `umask`
 
96
            :Default: ``0``
 
97
 
 
98
            File access creation mask (“umask”) to set for the process on
 
99
            daemon start.
 
100
 
 
101
            Since a process inherits its umask from its parent process,
 
102
            starting the daemon will reset the umask to this value so that
 
103
            files are created by the daemon with access modes as it expects.
 
104
 
 
105
        `pidfile`
 
106
            :Default: ``None``
 
107
 
 
108
            Context manager for a PID lock file. When the daemon context opens
 
109
            and closes, it enters and exits the `pidfile` context manager.
 
110
 
 
111
        `detach_process`
 
112
            :Default: ``None``
 
113
 
 
114
            If ``True``, detach the process context when opening the daemon
 
115
            context; if ``False``, do not detach.
 
116
 
 
117
            If unspecified (``None``) during initialisation of the instance,
 
118
            this will be set to ``True`` by default, and ``False`` only if
 
119
            detaching the process is determined to be redundant; for example,
 
120
            in the case when the process was started by `init`, by `initd`, or
 
121
            by `inetd`.
 
122
 
 
123
        `signal_map`
 
124
            :Default: system-dependent
 
125
 
 
126
            Mapping from operating system signals to callback actions.
 
127
 
 
128
            The mapping is used when the daemon context opens, and determines
 
129
            the action for each signal's signal handler:
 
130
 
 
131
            * A value of ``None`` will ignore the signal (by setting the
 
132
              signal action to ``signal.SIG_IGN``).
 
133
 
 
134
            * A string value will be used as the name of an attribute on the
 
135
              ``DaemonContext`` instance. The attribute's value will be used
 
136
              as the action for the signal handler.
 
137
 
 
138
            * Any other value will be used as the action for the
 
139
              signal handler. See the ``signal.signal`` documentation
 
140
              for details of the signal handler interface.
 
141
 
 
142
            The default value depends on which signals are defined on the
 
143
            running system. Each item from the list below whose signal is
 
144
            actually defined in the ``signal`` module will appear in the
 
145
            default map:
 
146
 
 
147
            * ``signal.SIGTTIN``: ``None``
 
148
 
 
149
            * ``signal.SIGTTOU``: ``None``
 
150
 
 
151
            * ``signal.SIGTSTP``: ``None``
 
152
 
 
153
            * ``signal.SIGTERM``: ``'terminate'``
 
154
 
 
155
            Depending on how the program will interact with its child
 
156
            processes, it may need to specify a signal map that
 
157
            includes the ``signal.SIGCHLD`` signal (received when a
 
158
            child process exits). See the specific operating system's
 
159
            documentation for more detail on how to determine what
 
160
            circumstances dictate the need for signal handlers.
 
161
 
 
162
        `uid`
 
163
            :Default: ``os.getuid()``
 
164
 
 
165
        `gid`
 
166
            :Default: ``os.getgid()``
 
167
 
 
168
            The user ID (“UID”) value and group ID (“GID”) value to switch
 
169
            the process to on daemon start.
 
170
 
 
171
            The default values, the real UID and GID of the process, will
 
172
            relinquish any effective privilege elevation inherited by the
 
173
            process.
 
174
 
 
175
        `prevent_core`
 
176
            :Default: ``True``
 
177
 
 
178
            If true, prevents the generation of core files, in order to avoid
 
179
            leaking sensitive information from daemons run as `root`.
 
180
 
 
181
        `stdin`
 
182
            :Default: ``None``
 
183
 
 
184
        `stdout`
 
185
            :Default: ``None``
 
186
 
 
187
        `stderr`
 
188
            :Default: ``None``
 
189
 
 
190
            Each of `stdin`, `stdout`, and `stderr` is a file-like object
 
191
            which will be used as the new file for the standard I/O stream
 
192
            `sys.stdin`, `sys.stdout`, and `sys.stderr` respectively. The file
 
193
            should therefore be open, with a minimum of mode 'r' in the case
 
194
            of `stdin`, and mode 'w+' in the case of `stdout` and `stderr`.
 
195
 
 
196
            If the object has a `fileno()` method that returns a file
 
197
            descriptor, the corresponding file will be excluded from being
 
198
            closed during daemon start (that is, it will be treated as though
 
199
            it were listed in `files_preserve`).
 
200
 
 
201
            If ``None``, the corresponding system stream is re-bound to the
 
202
            file named by `os.devnull`.
 
203
 
 
204
        """
 
205
 
 
206
    def __init__(
 
207
        self,
 
208
        chroot_directory=None,
 
209
        working_directory='/',
 
210
        umask=0,
 
211
        uid=None,
 
212
        gid=None,
 
213
        prevent_core=True,
 
214
        detach_process=None,
 
215
        files_preserve=None,
 
216
        pidfile=None,
 
217
        stdin=None,
 
218
        stdout=None,
 
219
        stderr=None,
 
220
        signal_map=None,
 
221
        ):
 
222
        """ Set up a new instance. """
 
223
        self.chroot_directory = chroot_directory
 
224
        self.working_directory = working_directory
 
225
        self.umask = umask
 
226
        self.prevent_core = prevent_core
 
227
        self.files_preserve = files_preserve
 
228
        self.pidfile = pidfile
 
229
        self.stdin = stdin
 
230
        self.stdout = stdout
 
231
        self.stderr = stderr
 
232
 
 
233
        if uid is None:
 
234
            uid = os.getuid()
 
235
        self.uid = uid
 
236
        if gid is None:
 
237
            gid = os.getgid()
 
238
        self.gid = gid
 
239
 
 
240
        if detach_process is None:
 
241
            detach_process = is_detach_process_context_required()
 
242
        self.detach_process = detach_process
 
243
 
 
244
        if signal_map is None:
 
245
            signal_map = make_default_signal_map()
 
246
        self.signal_map = signal_map
 
247
 
 
248
        self._is_open = False
 
249
 
 
250
    @property
 
251
    def is_open(self):
 
252
        """ ``True`` if the instance is currently open. """
 
253
        return self._is_open
 
254
 
 
255
    def open(self):
 
256
        """ Become a daemon process.
 
257
            :Return: ``None``
 
258
 
 
259
            Open the daemon context, turning the current program into a daemon
 
260
            process. This performs the following steps:
 
261
 
 
262
            * If this instance's `is_open` property is true, return
 
263
              immediately. This makes it safe to call `open` multiple times on
 
264
              an instance.
 
265
 
 
266
            * If the `prevent_core` attribute is true, set the resource limits
 
267
              for the process to prevent any core dump from the process.
 
268
 
 
269
            * If the `chroot_directory` attribute is not ``None``, set the
 
270
              effective root directory of the process to that directory (via
 
271
              `os.chroot`).
 
272
 
 
273
              This allows running the daemon process inside a “chroot gaol”
 
274
              as a means of limiting the system's exposure to rogue behaviour
 
275
              by the process. Note that the specified directory needs to
 
276
              already be set up for this purpose.
 
277
 
 
278
            * Set the process UID and GID to the `uid` and `gid` attribute
 
279
              values.
 
280
 
 
281
            * Close all open file descriptors. This excludes those listed in
 
282
              the `files_preserve` attribute, and those that correspond to the
 
283
              `stdin`, `stdout`, or `stderr` attributes.
 
284
 
 
285
            * Change current working directory to the path specified by the
 
286
              `working_directory` attribute.
 
287
 
 
288
            * Reset the file access creation mask to the value specified by
 
289
              the `umask` attribute.
 
290
 
 
291
            * If the `detach_process` option is true, detach the current
 
292
              process into its own process group, and disassociate from any
 
293
              controlling terminal.
 
294
 
 
295
            * Set signal handlers as specified by the `signal_map` attribute.
 
296
 
 
297
            * If any of the attributes `stdin`, `stdout`, `stderr` are not
 
298
              ``None``, bind the system streams `sys.stdin`, `sys.stdout`,
 
299
              and/or `sys.stderr` to the files represented by the
 
300
              corresponding attributes. Where the attribute has a file
 
301
              descriptor, the descriptor is duplicated (instead of re-binding
 
302
              the name).
 
303
 
 
304
            * If the `pidfile` attribute is not ``None``, enter its context
 
305
              manager.
 
306
 
 
307
            * Mark this instance as open (for the purpose of future `open` and
 
308
              `close` calls).
 
309
 
 
310
            * Register the `close` method to be called during Python's exit
 
311
              processing.
 
312
 
 
313
            When the function returns, the running program is a daemon
 
314
            process.
 
315
 
 
316
            """
 
317
        if self.is_open:
 
318
            return
 
319
 
 
320
        if self.chroot_directory is not None:
 
321
            change_root_directory(self.chroot_directory)
 
322
 
 
323
        if self.prevent_core:
 
324
            prevent_core_dump()
 
325
 
 
326
        change_file_creation_mask(self.umask)
 
327
        change_working_directory(self.working_directory)
 
328
        change_process_owner(self.uid, self.gid)
 
329
 
 
330
        if self.detach_process:
 
331
            detach_process_context()
 
332
 
 
333
        signal_handler_map = self._make_signal_handler_map()
 
334
        set_signal_handlers(signal_handler_map)
 
335
 
 
336
        exclude_fds = self._get_exclude_file_descriptors()
 
337
        close_all_open_files(exclude=exclude_fds)
 
338
 
 
339
        redirect_stream(sys.stdin, self.stdin)
 
340
        redirect_stream(sys.stdout, self.stdout)
 
341
        redirect_stream(sys.stderr, self.stderr)
 
342
 
 
343
        if self.pidfile is not None:
 
344
            self.pidfile.__enter__()
 
345
 
 
346
        self._is_open = True
 
347
 
 
348
        register_atexit_function(self.close)
 
349
 
 
350
    def __enter__(self):
 
351
        """ Context manager entry point. """
 
352
        self.open()
 
353
        return self
 
354
 
 
355
    def close(self):
 
356
        """ Exit the daemon process context.
 
357
            :Return: ``None``
 
358
 
 
359
            Close the daemon context. This performs the following steps:
 
360
 
 
361
            * If this instance's `is_open` property is false, return
 
362
              immediately. This makes it safe to call `close` multiple times
 
363
              on an instance.
 
364
 
 
365
            * If the `pidfile` attribute is not ``None``, exit its context
 
366
              manager.
 
367
 
 
368
            * Mark this instance as closed (for the purpose of future `open`
 
369
              and `close` calls).
 
370
 
 
371
            """
 
372
        if not self.is_open:
 
373
            return
 
374
 
 
375
        if self.pidfile is not None:
 
376
            # Follow the interface for telling a context manager to exit,
 
377
            # <URL:http://docs.python.org/library/stdtypes.html#typecontextmanager>.
 
378
            self.pidfile.__exit__(None, None, None)
 
379
 
 
380
        self._is_open = False
 
381
 
 
382
    def __exit__(self, exc_type, exc_value, traceback):
 
383
        """ Context manager exit point. """
 
384
        self.close()
 
385
 
 
386
    def terminate(self, signal_number, stack_frame):
 
387
        """ Signal handler for end-process signals.
 
388
            :Return: ``None``
 
389
 
 
390
            Signal handler for the ``signal.SIGTERM`` signal. Performs the
 
391
            following step:
 
392
 
 
393
            * Raise a ``SystemExit`` exception explaining the signal.
 
394
 
 
395
            """
 
396
        exception = SystemExit(
 
397
            "Terminating on signal %(signal_number)r"
 
398
                % vars())
 
399
        raise exception
 
400
 
 
401
    def _get_exclude_file_descriptors(self):
 
402
        """ Return the set of file descriptors to exclude closing.
 
403
 
 
404
            Returns a set containing the file descriptors for the
 
405
            items in `files_preserve`, and also each of `stdin`,
 
406
            `stdout`, and `stderr`:
 
407
 
 
408
            * If the item is ``None``, it is omitted from the return
 
409
              set.
 
410
 
 
411
            * If the item has a ``fileno()`` method, that method's
 
412
              return value is in the return set.
 
413
 
 
414
            * Otherwise, the item is in the return set verbatim.
 
415
 
 
416
            """
 
417
        files_preserve = self.files_preserve
 
418
        if files_preserve is None:
 
419
            files_preserve = []
 
420
        files_preserve.extend(
 
421
            item for item in [self.stdin, self.stdout, self.stderr]
 
422
            if hasattr(item, 'fileno'))
 
423
        exclude_descriptors = set()
 
424
        for item in files_preserve:
 
425
            if item is None:
 
426
                continue
 
427
            if hasattr(item, 'fileno'):
 
428
                exclude_descriptors.add(item.fileno())
 
429
            else:
 
430
                exclude_descriptors.add(item)
 
431
        return exclude_descriptors
 
432
 
 
433
    def _make_signal_handler(self, target):
 
434
        """ Make the signal handler for a specified target object.
 
435
 
 
436
            If `target` is ``None``, returns ``signal.SIG_IGN``. If
 
437
            `target` is a string, returns the attribute of this
 
438
            instance named by that string. Otherwise, returns `target`
 
439
            itself.
 
440
 
 
441
            """
 
442
        if target is None:
 
443
            result = signal.SIG_IGN
 
444
        elif isinstance(target, basestring):
 
445
            name = target
 
446
            result = getattr(self, name)
 
447
        else:
 
448
            result = target
 
449
 
 
450
        return result
 
451
 
 
452
    def _make_signal_handler_map(self):
 
453
        """ Make the map from signals to handlers for this instance.
 
454
 
 
455
            Constructs a map from signal numbers to handlers for this
 
456
            context instance, suitable for passing to
 
457
            `set_signal_handlers`.
 
458
 
 
459
            """
 
460
        signal_handler_map = dict(
 
461
            (signal_number, self._make_signal_handler(target))
 
462
            for (signal_number, target) in self.signal_map.items())
 
463
        return signal_handler_map
 
464
 
 
465
 
 
466
def change_working_directory(directory):
 
467
    """ Change the working directory of this process.
 
468
        """
 
469
    try:
 
470
        os.chdir(directory)
 
471
    except Exception, exc:
 
472
        error = DaemonOSEnvironmentError(
 
473
            "Unable to change working directory (%(exc)s)"
 
474
            % vars())
 
475
        raise error
 
476
 
 
477
 
 
478
def change_root_directory(directory):
 
479
    """ Change the root directory of this process.
 
480
 
 
481
        Sets the current working directory, then the process root
 
482
        directory, to the specified `directory`. Requires appropriate
 
483
        OS privileges for this process.
 
484
 
 
485
        """
 
486
    try:
 
487
        os.chdir(directory)
 
488
        os.chroot(directory)
 
489
    except Exception, exc:
 
490
        error = DaemonOSEnvironmentError(
 
491
            "Unable to change root directory (%(exc)s)"
 
492
            % vars())
 
493
        raise error
 
494
 
 
495
 
 
496
def change_file_creation_mask(mask):
 
497
    """ Change the file creation mask for this process.
 
498
        """
 
499
    try:
 
500
        os.umask(mask)
 
501
    except Exception, exc:
 
502
        error = DaemonOSEnvironmentError(
 
503
            "Unable to change file creation mask (%(exc)s)"
 
504
            % vars())
 
505
        raise error
 
506
 
 
507
 
 
508
def change_process_owner(uid, gid):
 
509
    """ Change the owning UID and GID of this process.
 
510
 
 
511
        Sets the GID then the UID of the process (in that order, to
 
512
        avoid permission errors) to the specified `gid` and `uid`
 
513
        values. Requires appropriate OS privileges for this process.
 
514
 
 
515
        """
 
516
    try:
 
517
        os.setgid(gid)
 
518
        os.setuid(uid)
 
519
    except Exception, exc:
 
520
        error = DaemonOSEnvironmentError(
 
521
            "Unable to change file creation mask (%(exc)s)"
 
522
            % vars())
 
523
        raise error
 
524
 
 
525
 
 
526
def prevent_core_dump():
 
527
    """ Prevent this process from generating a core dump.
 
528
 
 
529
        Sets the soft and hard limits for core dump size to zero. On
 
530
        Unix, this prevents the process from creating core dump
 
531
        altogether.
 
532
 
 
533
        """
 
534
    core_resource = resource.RLIMIT_CORE
 
535
 
 
536
    try:
 
537
        # Ensure the resource limit exists on this platform, by requesting
 
538
        # its current value
 
539
        core_limit_prev = resource.getrlimit(core_resource)
 
540
    except ValueError, exc:
 
541
        error = DaemonOSEnvironmentError(
 
542
            "System does not support RLIMIT_CORE resource limit (%(exc)s)"
 
543
            % vars())
 
544
        raise error
 
545
 
 
546
    # Set hard and soft limits to zero, i.e. no core dump at all
 
547
    core_limit = (0, 0)
 
548
    resource.setrlimit(core_resource, core_limit)
 
549
 
 
550
 
 
551
def detach_process_context():
 
552
    """ Detach the process context from parent and session.
 
553
 
 
554
        Detach from the parent process and session group, allowing the
 
555
        parent to exit while this process continues running.
 
556
 
 
557
        Reference: “Advanced Programming in the Unix Environment”,
 
558
        section 13.3, by W. Richard Stevens, published 1993 by
 
559
        Addison-Wesley.
 
560
    
 
561
        """
 
562
 
 
563
    def fork_then_exit_parent(error_message):
 
564
        """ Fork a child process, then exit the parent process.
 
565
 
 
566
            If the fork fails, raise a ``DaemonProcessDetachError``
 
567
            with ``error_message``.
 
568
 
 
569
            """
 
570
        try:
 
571
            pid = os.fork()
 
572
            if pid > 0:
 
573
                os._exit(0)
 
574
        except OSError, exc:
 
575
            exc_errno = exc.errno
 
576
            exc_strerror = exc.strerror
 
577
            error = DaemonProcessDetachError(
 
578
                "%(error_message)s: [%(exc_errno)d] %(exc_strerror)s" % vars())
 
579
            raise error
 
580
 
 
581
    fork_then_exit_parent(error_message="Failed first fork")
 
582
    os.setsid()
 
583
    fork_then_exit_parent(error_message="Failed second fork")
 
584
 
 
585
 
 
586
def is_process_started_by_init():
 
587
    """ Determine if the current process is started by `init`.
 
588
 
 
589
        The `init` process has the process ID of 1; if that is our
 
590
        parent process ID, return ``True``, otherwise ``False``.
 
591
    
 
592
        """
 
593
    result = False
 
594
 
 
595
    init_pid = 1
 
596
    if os.getppid() == init_pid:
 
597
        result = True
 
598
 
 
599
    return result
 
600
 
 
601
 
 
602
def is_socket(fd):
 
603
    """ Determine if the file descriptor is a socket.
 
604
 
 
605
        Return ``False`` if querying the socket type of `fd` raises an
 
606
        error; otherwise return ``True``.
 
607
 
 
608
        """
 
609
    result = False
 
610
 
 
611
    file_socket = socket.fromfd(fd, socket.AF_INET, socket.SOCK_RAW)
 
612
 
 
613
    try:
 
614
        socket_type = file_socket.getsockopt(
 
615
            socket.SOL_SOCKET, socket.SO_TYPE)
 
616
    except socket.error, exc:
 
617
        exc_errno = exc.args[0]
 
618
        if exc_errno == errno.ENOTSOCK:
 
619
            # Socket operation on non-socket
 
620
            pass
 
621
        else:
 
622
            # Some other socket error
 
623
            result = True
 
624
    else:
 
625
        # No error getting socket type
 
626
        result = True
 
627
 
 
628
    return result
 
629
 
 
630
 
 
631
def is_process_started_by_superserver():
 
632
    """ Determine if the current process is started by the superserver.
 
633
 
 
634
        The internet superserver creates a network socket, and
 
635
        attaches it to the standard streams of the child process. If
 
636
        that is the case for this process, return ``True``, otherwise
 
637
        ``False``.
 
638
    
 
639
        """
 
640
    result = False
 
641
 
 
642
    stdin_fd = sys.__stdin__.fileno()
 
643
    if is_socket(stdin_fd):
 
644
        result = True
 
645
 
 
646
    return result
 
647
 
 
648
 
 
649
def is_detach_process_context_required():
 
650
    """ Determine whether detaching process context is required.
 
651
 
 
652
        Return ``True`` if the process environment indicates the
 
653
        process is already detached:
 
654
 
 
655
        * Process was started by `init`; or
 
656
 
 
657
        * Process was started by `inetd`.
 
658
 
 
659
        """
 
660
    result = True
 
661
    if is_process_started_by_init() or is_process_started_by_superserver():
 
662
        result = False
 
663
 
 
664
    return result
 
665
 
 
666
 
 
667
def close_file_descriptor_if_open(fd):
 
668
    """ Close a file descriptor if already open.
 
669
 
 
670
        Close the file descriptor `fd`, suppressing an error in the
 
671
        case the file was not open.
 
672
 
 
673
        """
 
674
    try:
 
675
        os.close(fd)
 
676
    except OSError, exc:
 
677
        if exc.errno == errno.EBADF:
 
678
            # File descriptor was not open
 
679
            pass
 
680
        else:
 
681
            error = DaemonOSEnvironmentError(
 
682
                "Failed to close file descriptor %(fd)d"
 
683
                " (%(exc)s)"
 
684
                % vars())
 
685
            raise error
 
686
 
 
687
 
 
688
MAXFD = 2048
 
689
 
 
690
def get_maximum_file_descriptors():
 
691
    """ Return the maximum number of open file descriptors for this process.
 
692
 
 
693
        Return the process hard resource limit of maximum number of
 
694
        open file descriptors. If the limit is “infinity”, a default
 
695
        value of ``MAXFD`` is returned.
 
696
 
 
697
        """
 
698
    limits = resource.getrlimit(resource.RLIMIT_NOFILE)
 
699
    result = limits[1]
 
700
    if result == resource.RLIM_INFINITY:
 
701
        result = MAXFD
 
702
    return result
 
703
 
 
704
 
 
705
def close_all_open_files(exclude=set()):
 
706
    """ Close all open file descriptors.
 
707
 
 
708
        Closes every file descriptor (if open) of this process. If
 
709
        specified, `exclude` is a set of file descriptors to *not*
 
710
        close.
 
711
 
 
712
        """
 
713
    maxfd = get_maximum_file_descriptors()
 
714
    for fd in reversed(range(maxfd)):
 
715
        if fd not in exclude:
 
716
            close_file_descriptor_if_open(fd)
 
717
 
 
718
 
 
719
def redirect_stream(system_stream, target_stream):
 
720
    """ Redirect a system stream to a specified file.
 
721
 
 
722
        `system_stream` is a standard system stream such as
 
723
        ``sys.stdout``. `target_stream` is an open file object that
 
724
        should replace the corresponding system stream object.
 
725
 
 
726
        If `target_stream` is ``None``, defaults to opening the
 
727
        operating system's null device and using its file descriptor.
 
728
 
 
729
        """
 
730
    if target_stream is None:
 
731
        target_fd = os.open(os.devnull, os.O_RDWR)
 
732
    else:
 
733
        target_fd = target_stream.fileno()
 
734
    os.dup2(target_fd, system_stream.fileno())
 
735
 
 
736
 
 
737
def make_default_signal_map():
 
738
    """ Make the default signal map for this system.
 
739
 
 
740
        The signals available differ by system. The map will not
 
741
        contain any signals not defined on the running system.
 
742
 
 
743
        """
 
744
    name_map = {
 
745
        'SIGTSTP': None,
 
746
        'SIGTTIN': None,
 
747
        'SIGTTOU': None,
 
748
        'SIGTERM': 'terminate',
 
749
        }
 
750
    signal_map = dict(
 
751
        (getattr(signal, name), target)
 
752
        for (name, target) in name_map.items()
 
753
        if hasattr(signal, name))
 
754
 
 
755
    return signal_map
 
756
 
 
757
 
 
758
def set_signal_handlers(signal_handler_map):
 
759
    """ Set the signal handlers as specified.
 
760
 
 
761
        The `signal_handler_map` argument is a map from signal number
 
762
        to signal handler. See the `signal` module for details.
 
763
 
 
764
        """
 
765
    for (signal_number, handler) in signal_handler_map.items():
 
766
        signal.signal(signal_number, handler)
 
767
 
 
768
 
 
769
def register_atexit_function(func):
 
770
    """ Register a function for processing at program exit.
 
771
 
 
772
        The function `func` is registered for a call with no arguments
 
773
        at program exit.
 
774
 
 
775
        """
 
776
    atexit.register(func)