Python service
Package¶
Easy Implementation of Background Services¶
This package makes it easy to write Unix services, i.e. background processes (“daemons”) that are controlled by a foreground application (e.g. a console script).
The package is built around the python-daemon module, which provides
the means for creating well-behaved daemon processes. The service
package adds a control infrastructure for easily starting, stopping,
querying and killing the background process from a foreground
application.
Installation¶
The service
package is available from PyPI and can be installed via
pip:
pip install service
Supported Python versions are 2.7 as well as 3.4 and later.
Quickstart¶
import logging
from logging.handlers import SysLogHandler
import time
from service import find_syslog, Service
class MyService(Service):
def __init__(self, *args, **kwargs):
super(MyService, self).__init__(*args, **kwargs)
self.logger.addHandler(SysLogHandler(address=find_syslog(),
facility=SysLogHandler.LOG_DAEMON))
self.logger.setLevel(logging.INFO)
def run(self):
while not self.got_sigterm():
self.logger.info("I'm working...")
time.sleep(5)
if __name__ == '__main__':
import sys
if len(sys.argv) != 2:
sys.exit('Syntax: %s COMMAND' % sys.argv[0])
cmd = sys.argv[1].lower()
service = MyService('my_service', pid_dir='/tmp')
if cmd == 'start':
service.start()
elif cmd == 'stop':
service.stop()
elif cmd == 'status':
if service.is_running():
print "Service is running."
else:
print "Service is not running."
else:
sys.exit('Unknown command "%s".' % cmd)
Control Interface¶
The Service
class has a dual interface: Some methods
control the daemon and are intended to be called from the controlling process
while others implement the actual daemon functionality or utilities for it.
The control methods are:
start()
to start the daemonstop()
to ask the daemon to stopkill()
to kill the daemonis_running()
to check whether the daemon is runningget_pid()
to get the daemon’s process IDsend_signal()
to send arbitrary signals to the daemon
Subclasses usually do not need to override any of these.
Daemon Functionality¶
To provide the actual daemon functionality, subclasses override
run()
, which is executed in a separate daemon process
when start()
is called. Once
run()
exits, the daemon process stops.
When stop()
is called, the SIGTERM signal is sent to
the daemon process, which can check for its reception using
got_sigterm()
or wait for it using
wait_for_sigterm()
.
Further signals to control the daemon can be specified using the signals
constructor argument. These signals can then be sent to the daemon process
using send_signal()
. The daemon process can use
got_signal()
,
wait_for_signal()
, and clear_signal()
to react to signals.
Logging¶
Instances of Service
provide a built-in logger via their
logger
attribute. By default the logger only has a
logging.NullHandler
attached, so all messages are discarded. Attach
your own handler to output log messages to files or syslog (see the handlers
provided by the logging
and logging.handlers
modules).
Any uncaught exceptions from run()
are automatically
logged via that logger. To avoid error messages during startup being lost make
sure to attach your logging handlers before calling
start()
.
If you want use syslog for logging take a look at
find_syslog()
, which provides a portable way of locating
syslog.
Preserving File Handles¶
By default, all open file handles are released by the daemon process. If you
need to preserve some of them add them to the
files_preserve
list attribute. Note that file
handles used by any built-in Python logging handlers attached to
logger
are automatically preserved.
Exiting the Service¶
From the outside, a service can be stopped gracefully by calling
stop()
or, as a last resort, by calling
kill()
.
From the inside, i.e. from within run()
, the easiest
way is to just return
from the method. From version 0.5 on you can also
call sys.exit
and it will be handled correctly (in earlier versions that
would prevent a correct clean up). Note that you should never use os._exit
,
since that skips all clean up.
API Reference¶
-
service.
find_syslog
()[source]¶ Find Syslog.
Returns Syslog’s location on the current system in a form that can be passed on to
logging.handlers.SysLogHandler
:handler = SysLogHandler(address=find_syslog(), facility=SysLogHandler.LOG_DAEMON)
-
class
service.
Service
[source]¶ A background service.
This class provides the basic framework for running and controlling a background daemon. This includes methods for starting the daemon (including things like proper setup of a detached deamon process), checking whether the daemon is running, asking the daemon to terminate and for killing the daemon should that become necessary.
-
logger
¶ A
logging.Logger
instance.
-
files_preserve
¶ A list of file handles that should be preserved by the daemon process. File handles of built-in Python logging handlers attached to
logger
are automatically preserved.
-
__init__
(name, pid_dir='/var/run', signals=None)[source]¶ Constructor.
name
is a string that identifies the daemon. The name is used for the name of the daemon process, the PID file and for the messages to syslog.pid_dir
is the directory in which the PID file is stored.signals
list of operating signals, that should be available for use withsend_signal()
,got_signal()
,wait_for_signal()
, andcheck_signal()
. Note that SIGTERM is always supported, and that SIGTTIN, SIGTTOU, and SIGTSTP are never supported.
-
clear_signal
(s)[source]¶ Clears the state of a signal.
The signal must have been enabled using the
signals
parameter ofService.__init__()
. Otherwise, aValueError
is raised.
-
got_signal
(s)[source]¶ Check if a signal was received.
The signal must have been enabled using the
signals
parameter ofService.__init__()
. Otherwise, aValueError
is raised.Returns
True
if the daemon process has received the signal (for example becausestop()
was called in case of SIGTERM, or becausesend_signal()
was used) andFalse
otherwise.Note
This function always returns
False
for enabled signals when it is not called from the daemon process.
-
got_sigterm
()[source]¶ Check if SIGTERM signal was received.
Returns
True
if the daemon process has received the SIGTERM signal (for example becausestop()
was called).Note
This function always returns
False
when it is not called from the daemon process.
-
kill
(block=False)[source]¶ Kill the daemon process.
Sends the SIGKILL signal to the daemon process, killing it. You probably want to try
stop()
first.If
block
is true then the call blocks until the daemon process has exited.block
can either beTrue
(in which case it blocks indefinitely) or a timeout in seconds.Returns
True
if the daemon process has (already) exited andFalse
otherwise.The PID file is always removed, whether the process has already exited or not. Note that this means that subsequent calls to
is_running()
andget_pid()
will behave as if the process has exited. If you need to be sure that the process has already exited, setblock
toTrue
.New in version 0.5.1: The
block
parameter
-
run
()[source]¶ Main daemon method.
This method is called once the daemon is initialized and running. Subclasses should override this method and provide the implementation of the daemon’s functionality. The default implementation does nothing and immediately returns.
Once this method returns the daemon process automatically exits. Typical implementations therefore contain some kind of loop.
The daemon may also be terminated by sending it the SIGTERM signal, in which case
run()
should terminate after performing any necessary clean up routines. You can usegot_sigterm()
andwait_for_sigterm()
to check whether SIGTERM has been received.
-
send_signal
(s)[source]¶ Send a signal to the daemon process.
The signal must have been enabled using the
signals
parameter ofService.__init__()
. Otherwise, aValueError
is raised.
-
start
(block=False)[source]¶ Start the daemon process.
The daemon process is started in the background and the calling process returns.
Once the daemon process is initialized it calls the
run()
method.If
block
is true then the call blocks until the daemon process has started.block
can either beTrue
(in which case it blocks indefinitely) or a timeout in seconds.The return value is
True
if the daemon process has been started andFalse
otherwise.New in version 0.3: The
block
parameter
-
stop
(block=False)[source]¶ Tell the daemon process to stop.
Sends the SIGTERM signal to the daemon process, requesting it to terminate.
If
block
is true then the call blocks until the daemon process has exited. This may take some time since the daemon process will complete its on-going backup activities before shutting down.block
can either beTrue
(in which case it blocks indefinitely) or a timeout in seconds.The return value is
True
if the daemon process has been stopped andFalse
otherwise.New in version 0.3: The
block
parameter
-
wait_for_signal
(s, timeout=None)[source]¶ Wait until a signal has been received.
The signal must have been enabled using the
signals
parameter ofService.__init__()
. Otherwise, aValueError
is raised.This function blocks until the daemon process has received the signal (for example because
stop()
was called in case of SIGTERM, or becausesend_signal()
was used).If
timeout
is given and notNone
it specifies a timeout for the block.The return value is
True
if the signal was received andFalse
otherwise (the latter occurs if a timeout was given and the signal was not received).Warning
This function blocks indefinitely (or until the given timeout) for enabled signals when it is not called from the daemon process.
-
wait_for_sigterm
(timeout=None)[source]¶ Wait until a SIGTERM signal has been received.
This function blocks until the daemon process has received the SIGTERM signal (for example because
stop()
was called).If
timeout
is given and notNone
it specifies a timeout for the block.The return value is
True
if SIGTERM was received andFalse
otherwise (the latter only occurs if a timeout was given and the signal was not received).Warning
This function blocks indefinitely (or until the given timeout) when it is not called from the daemon process.
-
Development¶
The code for this package can be found on GitHub. It is available under the MIT license.
Change Log¶
See the file CHANGELOG.md.