Daemon

Introduction

The Daemon Extension enables applications to easily perform standard daemonization operations.

Features

  • Configurable runtime user and group

  • Adds the --daemon command line option

  • Add app.daemonize() function to trigger daemon functionality where necessary (either in a cement pre_run hook or an application controller sub-command, etc)

  • Manages a PID file including cleanup on app.close()

API References:

Requirements

  • No external dependencies

Platform Support

  • Unix/Linux

  • macOS

Configuration

The daemon extension is configurable with the following settings under a [daemon] section in the application configuration:

Setting

Description

user

The user name the process runs as. Default: os.getlogin()

group

The group name the process runs as. Default: the primary group of the user

dir

The directory that the process runs in. Default: /

pid_file

The filesystem path to store the PID (Process ID) file. Default: None

umask

The UMASK value to pass to os.umask(). Default: 0

Configurations can be passed as defaults to App:

from cement import App, init_defaults

DEFAULTS = init_defaults('myapp', 'daemon')
DEFAULTS['daemon']['user'] = 'myuser'
DEFAULTS['daemon']['group'] = 'mygroup'
DEFAULTS['daemon']['dir'] = '/var/lib/myapp/'
DEFAULTS['daemon']['pid_file'] = '/var/run/myapp/myapp.pid'
DEFAULTS['daemon']['umask'] = 0

class MyApp(App):
    class Meta:
        label = 'myapp'
        config_defaults = DEFAULTS

Application defaults are then overridden by configurations parsed via a [demon] config section in any of the applications configuration paths. An example configuration block would look like:

[daemon]
user = myuser
group = mygroup
dir = /var/lib/myapp/
pid_file = /var/run/myapp/myapp.pid
umask = 0

Usage

The following example shows how to add the daemon extension, as well as trigger daemon functionality before app.run() is called.

from time import sleep
from cement import App

class MyApp(App):
    class Meta:
        label = 'myapp'
        extensions = ['daemon']

with MyApp() as app:
    app.daemonize()
    app.run()

    count = 0
    while True:
        count = count + 1
        print('Iteration: %s' % count)
        sleep(10)

Some applications may prefer to only daemonize certain sub-commands rather than the entire parent application. For example:

from cement import App, Controller, ex

class Base(Controller):
    class Meta:
        label = 'base'

    @ex(help="run the daemon command.")
    def run_forever(self):
        from time import sleep
        self.app.daemonize()

        count = 0
        while True:
            count = count + 1
            print(count)
            sleep(10)

class MyApp(App):
    class Meta:
        label = 'myapp'
        handlers = [Base]
        extensions = ['daemon']

with MyApp() as app:
    app.run()

By default, even after app.daemonize() is called… the application will continue to run in the foreground, but will still manage the pid and user/group switching. To detach a process and send it to the background you simply pass the --daemon option at command line.

$ python example.py --daemon

$ ps -x | grep example
37421 ??         0:00.01 python example2.py --daemon
37452 ttys000    0:00.00 grep example

Daemonizing Without Commandline Option

Some use cases might require daemonizing the process without having to always pass the --daemon option, or where passing the option might be redundant. You can work around that programatically by simply overriding the daemon argument value in order to force daemonization even if --daemon wasn’t passed.

app.pargs.daemon = True
app.daemonize()

Note that this would only work after arguments have been parsed (i.e. after app.run() is called).

Last updated