Usage¶
Install & setup¶
What’s the point in an awesome module (which this is!) if you can’t use it? Nothing, that’s what!
mattdaemon is on pypi, so you just have to pip install mattdaemon
, and you’re good to import mattdaemon
.
Setup¶
mattdaemon is fairly simple to setup, all you have to do is subclass the mattdaemon.daemon
class, and override the mattdaemon.daemon.run()
method.
Here is how you’d make a simple daemon instance.
import mattdaemon
class MyDaemon(mattdaemon.daemon):
def run(self, *args, **kwargs):
# ... do something ...
# /tmp/my-daemon.pid is our PID file.
daem = MyDaemon("/tmp/my-daemon.pid")
Working with the daemon¶
Note
In these examples, daem
refers to the instance of the MyDaemon class, demonstrated above.
Note
Most of these are fairly simple to grasp; there isn’t a dying need to document them, it’s just nice to have.
stop¶
if daem.status():
daem.stop()
restart¶
Note
This literally just calls mattdaemon.daemon.stop()
and mattdaemon.daemon.start()
, in that order.
if daem.status():
daem.restart()
status¶
This will return a True
or False
value based on whether or not the daemon process is currently running.
if daem.status():
print 'daemon currently running!'
else:
print 'daemon not running!'
run¶
Note
You don’t actually call this; it’s called by the mattdaemon.daemon.start()
method.
Warning
If you don’t add the *args
and **kwargs
variables to the function, all sorts of hell might break loose.
This is the method you override to get your daemon calling what you want it to call.
import mattdaemon
class MyDaemon(mattdaemon.daemon):
def run(self, *args, **kwargs):
# ...
Daemonizing your app¶
Note
These are just some good ideas to keep in mind when creating your app with mattdaemon.
We don’t need no daemonization¶
Changed in version 1.1.0.
Note
Previous to 1.1.0
, you just pass daemonize=False
to mattdaemon.daemon.start()
if you don’t want to daemonize.
This was changed to make it easier to pass through variables.
If you’d like to debug your app, you’ll have to stop the daemonization from happening temporarily. If you’d like to do that, you can simply pass daemonize=False
through to the creation of your daemon instance, like so:
daem = MyDaemon("/tmp/my-daemon.pid", daemonize=False, **kw)
Pass through variables¶
New in version 1.1.0.
Note
This makes use of the argument lists you can use in Python.
If you know how many args are passed in, you can simply expand the *args
into variables.
If you want to pass through variables from the creation of your daemon to the mattdaemon.daemon.run()
method, you can easily do so by passing them into mattdaemon.daemon.start()
!
For example, say we want to pass some settings in from sys.argv
:
#...
class MyDaemon(mattdaemon.daemon):
def run(self, *args, **kwargs):
for count, thing in enumerate(args):
print '{0}. {1}'.format(count, thing)
for k, v in kwargs.items():
print '{0} = {1}'.format(k, v)
#...
daem.start(sys.argv[0], sys.argv[1], foo=sys.argv[2], bar=sys.argv[3])
If we call the script like this: python example.py gibson tree 50
, we’ll get the output:
$ python example.py gibson tree 50
0. example.py
1. gibson
foo = tree
bar = 50
There is no std*¶
Why would there be? Your daemon will be running in the background. If you need the user to enter information, perhaps in a loop, you’ll have to look for other ways of doing that.
By default, sys.stdin
, sys.stdout
and sys.stderr
are all redirected to /dev/null
, since they’re useless in a deamonized app.
What does this mean? If you want to log anything, you’ll need to use a dedicated logger, or provide a log file to your daemon instance (example below).
kw = {
"pidfile": "/tmp/my-daemon.pid",
"stdin": "/dev/null", # since we don't need it
"stdout": "/tmp/my-daemon.log",
"stderr": "/tmp/my-daemon.log"
}
daem = MyDaemon(**kw)
# daem.start(), whathaveyou
Don’t assume your working dir¶
Due to how daemonization works, the working directory is changed to /
, the root of the file system.
Because of this, you can’t assume any required files are relative, since they might not be.
# Relative, bad!
with open('relative/file', 'rb') as f:
# ...
# Absolute, good!
with open('/some/absolute/file', 'rb') as f:
# ...
To root or not to root¶
Note
The default for the root check is False
; as in, no, we don’t require root.
mattdaemon understands you, as well as your app. Careless users often run things with a higher privilege than they need. Do you need root to write some files to temp, or serve data on a high port? Nope. Do people do it anyway? They sure do!
Due to this, there is a built in root check. You can either tell the user that they do require root, or that they don’t (which, to be fair, you should be aiming for anyway). Why yes or no? If you don’t need root, you shouldn’t allow it. If you do need root, you should require it, since it will be needed at one point or another.
Root checks are simple, and can be controlled like such (with the root
keyword):
# Yes, we need root!
daem = MyDaemon("/tmp/my-daemon.pid", root=True)
By default, the check also requires --requires-root
to be in the arguments passed, so that the user acknowledges the use of root. This might not be what you want, so you can easily disable that, with the root_chk_argv
keyword.
# Yes, we need root!
# No, we don't care about --requires-root
daem = MyDaemon("/tmp/my-daemon.pid", root=True, root_chk_argv=False)
Handling SIGTERM¶
Warning
This only works in POSIX compliant operating systems, since it uses signals, and Windows doesn’t.
If you do anything with resources, be it an open port, file, network request, you should handle SIGTERM.
import signal
def my_handler(signum, frame):
print 'Received signal', signum, 'cleaning up resources to exit'
my_resource.close()
my_socket.close()
print 'done..'
# Register the handler.
signal.signal(signal.SIGTERM, my_handler)