[Python-checkins] r70467 - peps/trunk/pep-3143.txt

brett.cannon python-checkins at python.org
Thu Mar 19 03:14:42 CET 2009


Author: brett.cannon
Date: Thu Mar 19 03:14:42 2009
New Revision: 70467

Log:
Add PEP 3143: Standard daemon process library by Ben Finney.

Added:
   peps/trunk/pep-3143.txt

Added: peps/trunk/pep-3143.txt
==============================================================================
--- (empty file)
+++ peps/trunk/pep-3143.txt	Thu Mar 19 03:14:42 2009
@@ -0,0 +1,585 @@
+PEP:               3143
+Title:             Standard daemon process library
+Version:           $Revision: 1.1 $
+Last-Modified:     $Date: 2009-03-19 12:51 $
+Author:            Ben Finney <ben+python at benfinney.id.au>
+Status:            Draft
+Type:              Standards Track
+Content-Type:      text/x-rst
+Created:           2009-01-26
+Python-Version:    3.2
+Post-History:
+
+
+========
+Abstract
+========
+
+Writing a program to become a well-behaved Unix daemon is somewhat
+complex and tricky to get right, yet the steps are largely similar for
+any daemon regardless of what else the program may need to do.
+
+This PEP introduces a package to the Python standard library that
+provides a simple interface to the task of becoming a daemon process.
+
+
+..  contents::
+..
+    Table of Contents: 
+    Abstract
+    Specification
+      Example usage
+      Interface
+      ``DaemonContext`` objects
+    Motivation
+    Rationale
+      Correct daemon behaviour
+      A daemon is not a service
+    Reference Implementation
+      Other daemon implementations
+    References
+    Copyright
+
+
+=============
+Specification
+=============
+
+Example usage
+=============
+
+Simple example of direct `DaemonContext` usage::
+
+    import daemon
+
+    from spam import do_main_program
+
+    with daemon.DaemonContext() as daemon_context:
+        do_main_program()
+
+More complex example usage::
+
+    import os
+    import grp
+    import signal
+    import daemon
+    import lockfile
+
+    from spam import (
+        initial_program_setup,
+        do_main_program,
+        program_cleanup,
+        reload_program_config,
+        )
+
+    context = daemon.DaemonContext(
+        working_directory='/var/lib/foo',
+        umask=0o002,
+        pidfile=lockfile.FileLock('/var/run/spam.pid'),
+        )
+
+    context.signal_map = {
+        signal.SIGTERM: program_cleanup,
+        signal.SIGHUP: 'terminate',
+        signal.SIGUSR1: reload_program_config,
+        }
+
+    mail_gid = grp.getgrnam('mail').gr_gid
+    context.gid = mail_gid
+
+    important_file = open('spam.data', 'w')
+    interesting_file = open('eggs.data', 'w')
+    context.files_preserve = [important_file, interesting_file]
+
+    initial_program_setup()
+
+    with context:
+        do_main_program()
+
+
+Interface
+=========
+
+A new package, `daemon`, is added to the standard library.
+
+A class, `DaemonContext`, is defined to represent the settings and
+process context for the program running as a daemon process.
+
+
+``DaemonContext`` objects
+=========================
+
+A `DaemonContext` instance represents the behaviour settings and
+process context for the program when it becomes a daemon. The
+behaviour and environment is customised by setting options on the
+instance, before calling the `open` method.
+
+Each option can be passed as a keyword argument to the `DaemonContext`
+constructor, or subsequently altered by assigning to an attribute on
+the instance at any time prior to calling `open`. That is, for
+options named `wibble` and `wubble`, the following invocation::
+
+    foo = daemon.DaemonContext(wibble=bar, wubble=baz)
+    foo.open()
+
+is equivalent to::
+
+    foo = daemon.DaemonContext()
+    foo.wibble = bar
+    foo.wubble = baz
+    foo.open()
+
+The following options are defined.
+
+`files_preserve`
+    :Default: ``None``
+
+    List of files that should *not* be closed when starting the
+    daemon. If ``None``, all open file descriptors will be closed.
+
+    Elements of the list are file descriptors (as returned by a file
+    object's `fileno()` method) or Python `file` objects. Each
+    specifies a file that is not to be closed during daemon start.
+
+`chroot_directory`
+    :Default: ``None``
+
+    Full path to a directory to set as the effective root directory of
+    the process. If ``None``, specifies that the root directory is not
+    to be changed.
+
+`working_directory`
+    :Default: ``'/'``
+
+    Full path of the working directory to which the process should
+    change on daemon start.
+
+    Since a filesystem cannot be unmounted if a process has its
+    current working directory on that filesystem, this should either
+    be left at default or set to a directory that is a sensible “home
+    directory” for the daemon while it is running.
+
+`umask`
+    :Default: ``0``
+
+    File access creation mask (“umask”) to set for the process on
+    daemon start.
+
+    Since a process inherits its umask from its parent process,
+    starting the daemon will reset the umask to this value so that
+    files are created by the daemon with access modes as it expects.
+
+`pidfile`
+    :Default: ``None``
+
+    Context manager for a PID lock file. When the daemon context opens
+    and closes, it enters and exits the `pidfile` context manager.
+
+`detach_process`
+    :Default: ``None``
+
+    If ``True``, detach the process context when opening the daemon
+    context; if ``False``, do not detach.
+
+    If unspecified (``None``) during initialisation of the instance,
+    this will be set to ``True`` by default, and ``False`` only if
+    detaching the process is determined to be redundant; for example,
+    in the case when the process was started by `init`, by `initd`, or
+    by `inetd`.
+
+`signal_map`
+    :Default: system-dependent
+
+    Mapping from operating system signals to callback actions.
+
+    The mapping is used when the daemon context opens, and determines
+    the action for each signal's signal handler:
+
+    * A value of ``None`` will ignore the signal (by setting the
+      signal action to ``signal.SIG_IGN``).
+
+    * A string value will be used as the name of an attribute on the
+      ``DaemonContext`` instance. The attribute's value will be used
+      as the action for the signal handler.
+
+    * Any other value will be used as the action for the signal
+      handler.
+
+    The default value depends on which signals are defined on the
+    running system. Each item from the list below whose signal is
+    actually defined in the ``signal`` module will appear in the
+    default map:
+
+    * ``signal.SIGCLD``: ``None``
+
+    * ``signal.SIGTTIN``: ``None``
+
+    * ``signal.SIGTTOU``: ``None``
+
+    * ``signal.SIGTSTP``: ``None``
+
+    * ``signal.SIGTERM``: ``'terminate'``
+
+`uid`
+    :Default: ``os.getuid()``
+
+`gid`
+    :Default: ``os.getgid()``
+
+    The user ID (“UID”) value and group ID (“GID”) value to switch
+    the process to on daemon start.
+
+    The default values, the real UID and GID of the process, will
+    relinquish any effective privilege elevation inherited by the
+    process.
+
+`prevent_core`
+    :Default: ``True``
+
+    If true, prevents the generation of core files, in order to avoid
+    leaking sensitive information from daemons run as `root`.
+
+`stdin`
+    :Default: ``None``
+
+`stdout`
+    :Default: ``None``
+
+`stderr`
+    :Default: ``None``
+
+    Each of `stdin`, `stdout`, and `stderr` is a file-like object
+    which will be used as the new file for the standard I/O stream
+    `sys.stdin`, `sys.stdout`, and `sys.stderr` respectively. The file
+    should therefore be open, with a minimum of mode 'r' in the case
+    of `stdin`, and mode 'w+' in the case of `stdout` and `stderr`.
+
+    If the object has a `fileno()` method that returns a file
+    descriptor, the corresponding file will be excluded from being
+    closed during daemon start (that is, it will be treated as though
+    it were listed in `files_preserve`).
+
+    If ``None``, the corresponding system stream is re-bound to the
+    file named by `os.devnull`.
+
+
+The following methods are defined.
+
+`open()`
+    :Return: ``None``
+
+    Open the daemon context, turning the current program into a daemon
+    process. This performs the following steps:
+
+    * If the `prevent_core` attribute is true, set the resource limits
+      for the process to prevent any core dump from the process.
+
+    * If the `chroot_directory` attribute is not ``None``, set the
+      effective root directory of the process to that directory (via
+      `os.chroot`).
+
+      This allows running the daemon process inside a “chroot gaol”
+      as a means of limiting the system's exposure to rogue behaviour
+      by the process. Note that the specified directory needs to
+      already be set up for this purpose.
+
+    * Set the process UID and GID to the `uid` and `gid` attribute
+      values.
+
+    * Close all open file descriptors. This excludes those listed in
+      the `files_preserve` attribute, and those that correspond to the
+      `stdin`, `stdout`, or `stderr` attributes.
+
+    * Change current working directory to the path specified by the
+      `working_directory` attribute.
+
+    * Reset the file access creation mask to the value specified by
+      the `umask` attribute.
+
+    * If the `detach_process` option is true, detach the current
+      process into its own process group, and disassociate from any
+      controlling terminal.
+
+    * Set signal handlers as specified by the `signal_map` attribute.
+
+    * If any of the attributes `stdin`, `stdout`, `stderr` are not
+      ``None``, bind the system streams `sys.stdin`, `sys.stdout`,
+      and/or `sys.stderr` to the files represented by the
+      corresponding attributes. Where the attribute has a file
+      descriptor, the descriptor is duplicated (instead of re-binding
+      the name).
+
+    * If the `pidfile` attribute is not ``None``, enter its context
+      manager.
+
+    When the function returns, the running program is a daemon
+    process.
+
+`close()`
+    :Return: ``None``
+
+    Close the daemon context. This does nothing by default, but may be
+    overridden by a derived class.
+
+`terminate(signal_number, stack_frame)`
+    :Return: ``None``
+
+    Signal handler for the ``signal.SIGTERM`` signal. Performs the
+    following steps:
+
+    * If the `pidfile` attribute is not ``None``, exit its context
+      manager.
+
+    * Call the `close()` method.
+
+    * Raise a ``SystemExit`` exception.
+
+The class also implements the context manager protocol via
+``__enter__`` and ``__exit__`` methods.
+
+`__enter__()`
+    :Return: The ``DaemonContext`` instance
+
+    Call the instance's `open()` method, then return the instance.
+
+`__exit__(exc_type, exc_value, exc_traceback)`
+    :Return: ``True`` or ``False`` as defined by the context manager
+        protocol
+
+    Call the instance's `close()` method, then return ``True`` if the
+    exception was handled or ``False`` if it was not.
+
+
+==========
+Motivation
+==========
+
+The majority of programs written to be Unix daemons either implement
+behaviour very similar to that in the `specification`_, or are
+poorly-behaved daemons by the `correct daemon behaviour`_.
+
+Since these steps should be much the same in most implementations but
+are very particular and easy to omit or implement incorrectly, they
+are a prime target for a standard well-tested implementation in the
+standard library.
+
+
+=========
+Rationale
+=========
+
+Correct daemon behaviour
+========================
+
+According to Stevens in [stevens]_ §2.6, a program should perform the
+following steps to become a Unix daemon process.
+
+* Close all open file descriptors.
+
+* Change current working directory.
+
+* Reset the file access creation mask.
+
+* Run in the background.
+
+* Disassociate from process group.
+
+* Ignore terminal I/O signals.
+
+* Disassociate from control terminal.
+
+* Don't reacquire a control terminal.
+
+* Correctly handle the following circumstances:
+
+  * Started by System V `init` process.
+
+  * Daemon termination by ``SIGTERM`` signal.
+
+  * Children generate ``SIGCLD`` signal.
+
+The `daemon` tool [slack-daemon]_ lists (in its summary of features)
+behaviour that should be performed when turning a program into a
+well-behaved Unix daemon process. It differs from this PEP's intent in
+that it invokes a *separate* program as a daemon process. The
+following features are appropriate for a daemon that starts itself
+once the program is already running:
+
+* Sets up the correct process context for a daemon.
+
+* Behaves sensibly when started by `initd(8)` or `inetd(8)`.
+
+* Revokes any suid or sgid privileges to reduce security risks in case
+  daemon is incorrectly installed with special privileges.
+
+* Prevents the generation of core files to prevent leaking sensitive
+  information from daemons run as root (optional).
+
+* Names the daemon by creating and locking a PID file to guarantee
+  that only one daemon with the given name can execute at any given
+  time (optional).
+
+* Sets the user and group under which to run the daemon (optional,
+  root only).
+
+* Creates a chroot gaol (optional, root only).
+
+* Captures the daemon's stdout and stderr and directs them to syslog
+  (optional).
+
+A daemon is not a service
+=========================
+
+This PEP addresses only Unix-style daemons, for which the above
+correct behaviour is relevant, as opposed to comparable behaviours on
+other operating systems.
+
+There is a related concept in many systems, called a “service”. A
+service differs from the model in this PEP, in that rather than having
+the *current* program continue to run as a daemon process, a service
+starts an *additional* process to run in the background, and the
+current process communicates with that additional process via some
+defined channels.
+
+The Unix-style daemon model in this PEP can be used, among other
+things, to implement the background-process part of a service; but
+this PEP does not address the other aspects of setting up and managing
+a service.
+
+
+========================
+Reference Implementation
+========================
+
+The `python-daemon` package [python-daemon]_.
+
+Other daemon implementations
+============================
+
+Prior to this PEP, several existing third-party Python libraries or
+tools implemented some of this PEP's `correct daemon behaviour`_.
+
+The `reference implementation`_ is a fairly direct successor from the
+following implementations:
+
+* Many good ideas were contributed by the community to Python cookbook
+  recipes #66012 [cookbook-66012]_ and #278731 [cookbook-278731]_.
+
+* The `bda.daemon` library [bda.daemon]_ is an implementation of
+  [cookbook-66012]_. It is the predecessor of [python-daemon]_.
+
+Other Python daemon implementations that differ from this PEP:
+
+* The `zdaemon` tool [zdaemon]_ was written for the Zope project. Like
+  [slack-daemon]_, it differs from this specification because it is
+  used to run another program as a daemon process.
+
+* The Python library `daemon` [clapper-daemon]_ is (according to its
+  homepage) no longer maintained. As of version 1.0.1, it implements
+  the basic steps from [stevens]_.
+
+* The `daemonize` library [seutter-daemonize]_ also implements the
+  basic steps from [stevens]_.
+
+* Ray Burr's `daemon.py` module [burr-daemon]_ provides the [stevens]_
+  procedure as well as PID file handling and redirection of output to
+  syslog.
+
+* Twisted [twisted]_ includes, perhaps unsurprisingly, an
+  implementation of a process daemonisation API that is integrated
+  with the rest of the Twisted framework; it differs significantly
+  from the API in this PEP.
+
+* The Python `initd` library [dagitses-initd]_, which uses
+  [clapper-daemon]_, implements an equivalent of Unix `initd(8)` for
+  controlling a daemon process.
+
+
+==========
+References
+==========
+
+..  [stevens]
+
+    `Unix Network Programming`, W. Richard Stevens, 1994 Prentice
+    Hall.
+
+..  [slack-daemon]
+
+    The (non-Python) “libslack” implementation of a `daemon` tool
+    `<http://www.libslack.org/daemon/>`_ by “raf” <raf at raf.org>.
+
+..  [python-daemon]
+
+    The `python-daemon` library
+    `<http://pypi.python.org/pypi/python-daemon/>`_ by Ben Finney et
+    al.
+
+..  [cookbook-66012]
+
+    Python Cookbook recipe 66012, “Fork a daemon process on Unix”
+    `<http://code.activestate.com/recipes/66012/>`_.
+
+..  [cookbook-278731]
+
+    Python Cookbook recipe 278731, “Creating a daemon the Python way”
+    `<http://code.activestate.com/recipes/278731/>`_.
+
+..  [bda.daemon]
+
+    The `bda.daemon` library
+    `<http://pypi.python.org/pypi/bda.daemon/>`_ by Robert
+    Niederreiter et al.
+
+..  [zdaemon]
+
+    The `zdaemon` tool `<http://pypi.python.org/pypi/zdaemon/>`_ by
+    Guido van Rossum et al.
+
+..  [clapper-daemon]
+
+    The `daemon` library `<http://pypi.python.org/pypi/daemon/>`_ by
+    Brian Clapper.
+
+..  [seutter-daemonize]
+
+    The `daemonize` library `<http://daemonize.sourceforge.net/>`_ by
+    Jerry Seutter.
+
+..  [burr-daemon]
+
+    The `daemon.py` module
+    `<http://www.nightmare.com/~ryb/code/daemon.py>`_ by Ray Burr.
+
+..  [twisted]
+
+    The `Twisted` application framework
+    `<http://pypi.python.org/pypi/Twisted/>`_ by Glyph Lefkowitz et
+    al.
+
+..  [dagitses-initd]
+
+    The Python `initd` library `<http://pypi.python.org/pypi/initd/>`_
+    by Michael Andreas Dagitses.
+
+
+=========
+Copyright
+=========
+
+This work is hereby placed in the public domain. To the extent that
+placing a work in the public domain is not legally possible, the
+copyright holder hereby grants to all recipients of this work all
+rights and freedoms that would otherwise be restricted by copyright.
+
+
+..
+    Local variables:
+    mode: rst
+    coding: utf-8
+    time-stamp-start: "^:Last-Modified:[         ]+"
+    time-stamp-end: "$"
+    time-stamp-line-limit: 20
+    time-stamp-format: "%:y-%02m-%02d %02H:%02M"
+    End:
+    vim: filetype=rst fileencoding=utf-8 :


More information about the Python-checkins mailing list