Commit 14fc787a authored by Antoine Millet's avatar Antoine Millet
Browse files

Implemented proper daemonization in server.

parent e48633b8
Loading
Loading
Loading
Loading
+86 −22
Original line number Diff line number Diff line
#!/usr/bin/env python
#coding=utf8

import os
import sys
import atexit
import logging
import logging.handlers
import ConfigParser
import signal
from optparse import OptionParser
from pwd import getpwnam
from grp import getgrnam

from daemon import DaemonContext
from daemon.pidlockfile import PIDLockFile

from ccserver.ccserver import CCServer
from ccserver import __version__

import os
try:
    import daemon
    DAEMONIZE = True
except ImportError:
    DAEMONIZE = False


DEFAULT_CONFIG_FILE = '/etc/cc-server.conf'
DEFAULT_PID_FILE = '/var/run/cc-server.pid'
DEFAULT_UMASK = 0o0177
DEFAULT_CONFIGURATION = {
    'daemonize': False,
    'user': '',
    'group': '',
    'pidfile': '',
    'umask': '0177',
    'port': 1984,
    'verbosity': 0,
    'account_db': None, # None = mandatory option
@@ -44,9 +50,14 @@ def run_server(options):

    logger = logging.getLogger()
    logger.setLevel(level)

    if options['stdout']:
        handler = logging.StreamHandler()
    else:
        facility = logging.handlers.SysLogHandler.LOG_DAEMON
        handler = logging.handlers.SysLogHandler(address='/dev/log',
                                                 facility=facility)

    fmt = logging.Formatter('cc-server: %(levelname)s %(message)s')
    handler.setFormatter(fmt)
    logger.addHandler(handler)
@@ -79,10 +90,19 @@ if __name__ == '__main__':
    op = OptionParser(version='%%prog v%s' % __version__)
    op.add_option('-c', '--config', default=DEFAULT_CONFIG_FILE,
                  help='configuration file (default: %default)')
    op.add_option('-d', '--daemonize', default=False, action='store_true',
                  help='run as daemon and write pid file')
    op.add_option('-p', '--pid-file', default=DEFAULT_PID_FILE,
                  help='pid file (default: %default)')

    op.add_option('-d', '--daemonize', action='store_true',
                  help='run as a daemon')
    op.add_option('-f', '--foreground', action='store_false', dest='daemonize',
                  help='don\'t run as a daemon')

    op.add_option('-p', '--pidfile', help='write pidfile to the path')
    op.add_option('-k', '--umask', help='set the umask of the process')
    op.add_option('-u', '--user', help='run as user')
    op.add_option('-g', '--group', help='run as group')

    op.add_option('-s', '--stdout', action='store_true', default=False,
                  help='log in stdout instead of syslog')

    cliopts, args = op.parse_args()

@@ -106,13 +126,57 @@ if __name__ == '__main__':
            else:
                options[opt] = default

    if cliopts.daemonize:
        if DAEMONIZE:
            with daemon.DaemonContext(stderr=sys.stderr):
                run_server(options)
    # Merge cli options and .conf file options:
    for opt in ('daemonize', 'pidfile', 'umask', 'user', 'group', 'stdout'):
        if getattr(cliopts, opt) is not None:
            options[opt] = getattr(cliopts, opt)

    # Create option set for the daemonization process:
    daemon_opts = {}

    daemonize = options['daemonize']
    if isinstance(daemonize, str):
        if daemonize in ('yes', 'true'):
            daemonize = True
        else:
            daemonize = False
    daemon_opts['detach_process'] = daemonize

    daemon_opts['umask'] = int(options['umask'], 8)

    if options['user']:
        if options['user'].isdigit():
            daemon_opts['uid'] = int(options['user'])
        else:
            daemon_opts['uid'] = getpwnam(options['user']).pw_uid

    if options['group']:
        if options['group'].isdigit():
            daemon_opts['gid'] = int(options['group'])
        else:
            sys.stderr.write(('You must install python-daemon to handle the '
                              'daemonization of the process.\n'))
            sys.exit(2)
            daemon_opts['gid'] = getgrnam(options['group']).gr_gid

    if not daemonize:
        daemon_opts['stderr'] = sys.stderr
        daemon_opts['stdout'] = sys.stderr

    # I've to write myself the pidfile because the daemon library write it
    # after the privilege downgrade:
    if options['pidfile']:
        pidfile = open(cliopts.pidfile, 'w')
        daemon_opts['files_preserve'] = [pidfile]
    else:
        pidfile = None

    with DaemonContext(**daemon_opts):
        if pidfile is not None:
            pidfile.write('%s' % os.getpid())
            pidfile.flush()

            @atexit.register
            def clean_pidfile():
                pidfile.seek(0)
                pidfile.truncate()
                pidfile.flush()

        run_server(options)
+3 −8
Original line number Diff line number Diff line
@@ -17,12 +17,8 @@ PATH=/sbin:/usr/sbin:/bin:/usr/bin
DESC="CloudControl server"
NAME=cc-server
DAEMON=/usr/bin/cc-server
DAEMON_ARGS=""
PIDFILE=/var/run/$NAME.pid

# Defaults:
USER=cc-server
GROUP=cc-server
DAEMON_OPTS="-d -p $PIDFILE"

# Exit if the package is not installed
[ -x "$DAEMON" ] || exit 0
@@ -53,10 +49,9 @@ VERBOSE=yes
#
do_start()
{
    start-stop-daemon --start --quiet --background --name $NAME -k 0177 \
    start-stop-daemon --start --quiet --background --name $NAME \
            --exec $(readlink -f $(which python)) --test > /dev/null || return 2
    start-stop-daemon --start --quiet --background --make-pidfile \
            --pidfile $PIDFILE --chuid $USER:$GROUP --exec $DAEMON \
    start-stop-daemon --start --quiet --pidfile $PIDFILE --exec $DAEMON \
            -- $DAEMON_OPTS || return 1
}