#!/www/python/bin/python

"""$URL: svn+ssh://svn.mems-exchange.org/repos/trunk/dulcinea/bin/site $
$Id: site 24740 2004-07-21 01:03:00Z dbinger $

The site command script.
This controls the starting and stopping of durus, scgi, and apache servers.
"""
import os
import os.path
import sys
import signal
import errno
import time
from dulcinea import site_util

config = site_util.get_config()
start_script_dir = config.defaults().get('start-script-directory', '')

valid_actions = ['start', 'stop', 'restart', 'status', 'help']
valid_sites = site_util.list_sites()
assert valid_sites, "You must have at least one site defined"
valid_daemons = ['durus', 'apache', 'scgi']


def usage(msg=None):
    def alts(lst):
        return ' | '.join(lst)
    if msg:
        sys.stderr.write('error: %s\n' % msg)
    sys.stderr.write('Usage: %s [ <action> | <site> | <daemon ]*\n' %
                     sys.argv[0])
    sys.stderr.write('where:\n')
    sys.stderr.write('       <action> = %s\n' % alts(valid_actions))
    sys.stderr.write('           default is status\n')
    sys.stderr.write('       <site> = %s\n' % alts(valid_sites))
    sys.stderr.write('           to specify some subset of all sites\n')
    sys.stderr.write('           default is all sites\n')
    sys.stderr.write('       <daemon> = %s\n' % alts(valid_daemons))
    sys.stderr.write('           to specify some subset of all daemons\n')
    sys.stderr.write('           default is all daemons\n')
    sys.exit(1)

def is_running(pid):
    """Return true if program with 'pid' exists."""
    # Currently Linux specific.
    return os.path.exists('/proc/%s' % pid)

def write(*args):
    sys.stdout.write(' '.join(args))
    sys.stdout.flush()

def sanitize_environment(env):
    if env is None:
        env = {}
    for name in ['PATH', 'LOGNAME']:
        if name not in env:
            env[name] = os.environ[name]
    site_conf = os.environ.get('SITE_CONF')
    if site_conf:
        env['SITE_CONF'] = site_conf
    return env

def fork_child(exe, args=(), env=None, wd=None):
    pid = os.fork()
    if pid == 0:
        for i in range(4, 256):
            try:
                os.close(i)
            except:
                pass
        if wd:
            os.chdir(wd)
        env = sanitize_environment(env)
        try:
            os.execve(exe, [exe] + map(str, args), env)
        except OSError, err:
            sys.stderr.write("couldn't exec %s: %s\n" % (exe, err.strerror))
        except:
            sys.stderr.write("couldn't exec %s\n" % exe)
        os._exit(2)
    (pid, status) = os.wait()
    if status:
        print >>sys.stderr, "%s returned error status %s" % (exe, status)


def start_daemon(daemon, site='', exe=None, args=None, env=None):
    pid_file = site_util.get_pid_file_name(daemon, site)
    if os.path.isfile(pid_file):
        pid = open(pid_file).read().strip()
        if is_running(pid):
            write('\n%s %s appears to be running, not starting.\n' % (
                site, daemon))
            return
        else:
            write('\nPID file for %s %s appears obsolete, removing it.\n' % (
                site, daemon))
            os.unlink(pid_file)
    fork_child(exe, args, env, os.path.dirname(pid_file))

def kill_daemon (daemon, site):
    pid_file = site_util.get_pid_file_name(daemon, site)
    pid = open(pid_file).read().strip()
    try:
        os.kill(int(pid), signal.SIGTERM)
    except OSError, e:
        if errno.ESRCH == e.errno:
            write('%s %s (%s) process not found.' % (daemon, site, pid))
        else:
            write('\nkill failed: %s %s (%s)\n' % (daemon, site, pid))
            write('%s\n' % os.strerror(e.errno))
    else:
        for i in range(10):
            if not is_running(pid):
                break
            time.sleep(1)
        else:
            write('\nkill failed: %s %s (%s)\n' % (daemon, site, pid))
    if os.path.exists(pid_file):
	os.unlink(pid_file)

def stop_daemon (daemon, site='', script='', args=(), env=None):
    pid_file = site_util.get_pid_file_name(daemon, site)
    if os.path.isfile(pid_file):
        if script:
            fork_child(script, args, env)
            os.unlink(pid_file)
        else:
            kill_daemon(daemon, site)
    else:
        write('\n%s %s does not appear to be running.\n' % (site, daemon))

def start (daemon, site=None):
    start_daemon(daemon, site,
                 os.path.join(start_script_dir, 'start-%s.py' % daemon),
                 (site,))

def start_durus (sites):
    for site in sites:
        if not config.has_option(site, 'durus-address'):
            continue
        if site_util.is_local(config.get(site, 'durus-address')):
            write(' ' + site)
            start_daemon('durus', site,
                         os.path.join(start_script_dir, 'start-durus.py'),
                         (site, 'start'))

def stop_durus (sites):
    for site in sites:
        if not config.has_option(site, 'durus-address'):
            continue
        if site_util.is_local(config.get(site, 'durus-address')):
            write(' ' + site)
            stop_daemon('durus', site,
                        os.path.join(start_script_dir, 'start-durus.py'),
                        (site, 'stop'))

def start_scgi (sites):
    for site in sites:
        if not config.has_option(site, 'scgi-address'):
            continue
        if site_util.is_local(config.get(site, 'scgi-address')):
            write(' ' + site)
            start('scgi', site)

def stop_scgi (sites):
    for site in sites:
        if not config.has_option(site, 'scgi-address'):
            continue
        if site_util.is_local(config.get(site, 'scgi-address')):
            write(' ' + site)
            stop_daemon('scgi', site)

def _apache_required(sites):
    for site in sites:
        if (config.has_option(site, 'http-address') or
            config.has_option(site, 'https-address')):
            return True
    return False

def start_apache(sites):
    start('apache')

def stop_apache(sites):
    stop_daemon('apache')

def show_status():
    cmd = 'ps xo pid,command | grep "\\(start-\||scgi\\|durus\\|apache\\|httpd\\)" | grep -v grep'
    os.system(cmd)

def main():
    action = 'status'
    for act in valid_actions:
        if act in sys.argv:
            action = act
            break

    sites = ([ site for site in valid_sites if site in sys.argv ] or
             valid_sites)
    if site_util.any_live_sites():
        # Exclude all sites that aren't live
        sites = [site for site in sites if site_util.is_live(site)]
    elif site_util.any_staging_sites():
        # Exclude all sites that aren't staging
        sites = [site for site in sites if site_util.is_staging(site)]
    if not _apache_required(sites):
        valid_daemons.remove('apache')
    daemons = ([ daemon for daemon in valid_daemons if daemon in sys.argv ] or
               valid_daemons)


    def start_daemons():
        if 'durus' in daemons:
            write('Starting Durus servers:')
            start_durus(sites)
            write('\n')
        if 'scgi' in daemons:
            write('Starting SCGI servers:')
            start_scgi(sites)
            write('\n')
        if 'apache' in daemons:
            write('Starting Apache:')
            start_apache(sites)
            write('done\n')

    def stop_daemons():
        if 'apache' in daemons:
            write('Stopping Apache:')
            stop_apache(sites)
            write('done\n')
        if 'scgi' in daemons:
            write('Stopping SCGI servers:')
            stop_scgi(sites)
            write('\n')
        if 'durus' in daemons:
            write('Stopping Durus servers:')
            stop_durus(sites)
            write('\n')

    if action == 'start':
        start_daemons()

    elif action == 'stop':
        stop_daemons()

    elif action == 'restart':
        stop_daemons()
        start_daemons()

    elif action == 'status':
        show_status()

    elif action == 'help':
        usage()

main()
