#!/usr/bin/python
"""Service launcher that creates pidfiles and can redirect output to a file."""
import grp
import optparse
import os
import pwd
import re
import signal
import subprocess
import sys


def stop_service(pidfile):
    """
    Stop a service using a pidfile.

    Read the first line of a file for an integer that should refer to a pid,
    send a SIGTERM to that pid #.
       :param pidfile: file to read for the process id number
    """
    pidfh = open(pidfile)
    pid = int(pidfh.readline())
    os.kill(pid, signal.SIGTERM)


def start_service(cmd, pidfile, logfile=os.devnull, chdir=None):
    """
    Start cmd in the background and write the pid to pidfile.

    :param cmd: command to run with arguments
    :param pidfile: pidfile to write the pid to
    :param logfile: file to write stderr/stdout to
    :param chdir: Directory to change to before starting the application
    """
    logfh = open(logfile, 'a')
    pidfh = open(pidfile, 'w')
    proc = subprocess.Popen(cmd, stdout=logfh, stderr=logfh, cwd=chdir)
    pidfh.write(str(proc.pid))
    pidfh.close()


def get_user_name_id(user):
    """
    Get the user id # and name.

    :param user: integer or string containing either the uid #
        or a string username

    :return: a tuple of the user name, user id
    """
    if re.match('\d+', str(user)):
        pass_info = pwd.getpwuid(user)
    else:
        pass_info = pwd.getpwnam(user)

    return pass_info[0], pass_info[2]


def get_group_name_id(group):
    """
    Get the group id # and name

    :param group: integer or string containing either the uid #
        or a string username

    :return: a tuple of group name, group id
    """
    if re.match('\d+', str(group)):
        group_info = grp.getgrgid(group)
    else:
        group_info = grp.getgrnam(group)

    return group_info[0], group_info[2]


def set_group_user(group=None, user=None):
    """
    Set the group and user id if gid or uid is defined.

    :param group: Change the group id the program is run under
    :param user: Change the user id the program is run under
    """
    if group:
        _, gid = get_group_name_id(group)
        os.setgid(gid)
        os.setegid(gid)

    if user:
        username, uid = get_user_name_id(user)
        # Set environment for programs that use those to find running user
        for name in ('LOGNAME', 'USER', 'LNAME', 'USERNAME'):
            os.environ[name] = username
        os.setuid(uid)
        os.seteuid(uid)


def main():
    parser = optparse.OptionParser()
    parser.allow_interspersed_args = False
    parser.add_option('-l', '--logfile', action='store',
                      default=None,
                      help='File to redirect stdout to')
    parser.add_option('-c', '--chdir', action='store',
                      default=None,
                      help='Change to dir before starting the process')
    parser.add_option('-s', '--start-service', action='store_true',
                      default=False,
                      help='Start service')
    parser.add_option('-k', '--stop-service', action='store_true',
                      default=False,
                      help='Stop service')
    parser.add_option('-p', '--pidfile', action='store',
                      default=None,
                      help='Pid file location (Required)')
    parser.add_option('-u', '--chuid', action='store',
                      default=None,
                      help='UID to run process as')
    parser.add_option('-g', '--chgid', action='store',
                      default=None,
                      help='GID to run process as')

    options, args = parser.parse_args()

    if not options.pidfile:
        print 'A pidfile must always be supplied'
        parser.print_help()
        sys.exit(1)

    set_group_user(group=options.chgid, user=options.chuid)
    if options.start_service:
        start_service(args, options.pidfile, options.logfile, options.chdir)
    elif options.stop_service:
        stop_service(options.pidfile)
    else:
        print 'Nothing to do, you must specify to start or stop a service'
        parser.print_help()


if __name__ == '__main__':
    main()
