#!/usr/bin/env python

# Deejayd, a media player daemon
# Copyright (C) 2007-2009 Mickael Royer <mickael.royer@gmail.com>
#                         Alexandre Rossi <alexandre.rossi@gmail.com>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License along
# with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.

"""
This is the script used to launch deejayd
"""

import errno, random, sys, os, pwd, grp, traceback
from optparse import OptionParser
from deejayd.ui.config import DeejaydConfig


INSTALL_BIN_DIR = os.path.dirname(__file__)
binsuffix = os.path.basename(INSTALL_BIN_DIR)
INSTALL_MODE = 'source'
if binsuffix == 'bin':
    # Deejayd is installed, and not in source tree
    INSTALL_MODE = 'installed'
    INSTALL_PREFIX = INSTALL_BIN_DIR[:-len(binsuffix)]

# init translation
######################################################
import gettext
from deejayd.ui.i18n import DeejaydTranslations
if INSTALL_MODE == 'source':
    LOCALES_PATH = os.path.join(INSTALL_BIN_DIR, '..', 'build', 'mo')
else:
    LOCALES_PATH = os.path.join(INSTALL_PREFIX, 'share', 'locale')
try:
    t = gettext.translation("deejayd", LOCALES_PATH, class_=DeejaydTranslations)
except IOError:
    t = DeejaydTranslations()
t.install()

############ Parse Option ############################
######################################################
usage = "usage: %prog [options]"
parser = OptionParser(usage=usage)

# List options
parser.set_defaults(pidfile="deejayd.pid",kill = False,daemon = True)

# The help option must be changed to comply with i18n.
parser.get_option('-h').help = _("Show this help message and exit.")

parser.add_option("-u","--uid",dest="uid",type="string",\
    help=_("The uid to run as"))
parser.add_option("-g","--gid",dest="gid",type="string",\
    help=_("The gid to run as (the first one), and the supplementary gids separated by commas."))
parser.add_option("-n","--nodaemon",action="store_false",dest="daemon",\
    help=_("No daemonize deejayd"))
parser.add_option("-l","--log-file",type="string",dest="logfile",\
    metavar="FILE", help=_("Specify the log file"))
parser.add_option("-w","--webui-log",type="string",dest="webui_logfile",\
    metavar="FILE", help=_("Specify the log file for the webui"))
parser.add_option("-p","--pid-file",type="string",dest="pidfile",\
    metavar="FILE", help=_("Specify the log file"))
parser.add_option("-c","--conf-file",type="string",dest="conffile",\
    metavar="FILE", help=_("Specify a custom conf file"))
parser.add_option("-k","--kill",action="store_true",dest="kill",\
    help=_("Kill the actual deejayd process"))
parser.add_option("-d", "--debug",
                  action="store_true", dest="debug",
                  help=_("Log more debug informations"))

(options, args) = parser.parse_args()
######################################################

# add custom config parms
if options.conffile:
    if os.path.isfile(options.conffile):
        DeejaydConfig.custom_conf = options.conffile
    else:
        sys.exit(_("The config file does not exist."))

if options.debug:
    DeejaydConfig().set('general', 'log', 'debug')


######################################################

def daemonize():
    # See http://www.erlenstar.demon.co.uk/unix/faq_toc.html#TOC16
    if os.fork():   # launch child and...
        os._exit(0) # kill off parent
    os.setsid()
    if os.fork():   # launch child and...
        os._exit(0) # kill off parent again.
    os.umask(077)
    null=os.open('/dev/null', os.O_RDWR)
    for i in range(3):
        try:
            os.dup2(null, i)
        except OSError, e:
            if e.errno != errno.EBADF:
                raise
    os.close(null)


def killDeejayd(pidfile):
    if os.path.exists(pidfile):
        try: pid = int(open(pidfile).read())
        except ValueError:
            sys.exit(_('Pidfile %s contains non-numeric value') % pidfile)
        try:
            os.kill(pid, 15)
            removePID(pidfile)
        except OSError, err:
            sys.exit(\
                _('Unable to stop deejayd : %s, are you sure it running ?')\
                 % (err,))
    else:
        sys.exit(_('no PidFile found, are you sure deejayd running ?'))


def removePID(pidfile):
    try: os.unlink(pidfile)
    except OSError, e:
        if e.errno == errno.EACCES or e.errno == errno.EPERM:
            sys.exit(_("Unable to remove pid file : %s") % (e,))


def setEnv(options):
    # Init random generator
    random.seed()

    if options.daemon:
        daemonize()
    # Store the pid
    removePID(options.pidfile)
    open(options.pidfile,'wb').write(str(os.getpid()))

    if options.gid:
        gids = options.gid.split(',')
        ngids = []
        for gid in gids:
            try: ngid = int(gid)
            except ValueError:
                ngid = grp.getgrnam(gid)[2]
            ngids.append(ngid)
        try:
            os.setgroups(ngids)
            os.setgid(ngids[0])
        except OSError:
            sys.exit(_("Unable to change gid of the process"))
    if options.uid:
        try: uid = int(options.uid)
        except ValueError:
            uid = pwd.getpwnam(options.uid)[2]
        try:
            os.setuid(uid)
        except OSError:
            sys.exit(_("Unable to change uid of the process"))


def startLog(options):
    from twisted.python import log
    import deejayd.ui.log

    log_file_name = None
    if options.logfile:
        log_file_name = options.logfile
    elif options.daemon:
        log_file_name = 'deejayd.log'

    if log_file_name:
        flo = deejayd.ui.log.SignaledFileLogObserver(log_file_name)
        log.startLoggingWithObserver(flo.emit)
    else:
        log.startLogging(sys.stdout)


# Start
if __name__ == "__main__":
    if options.kill:
        killDeejayd(options.pidfile)
        sys.exit()

    ###################
    # install reactor
    ##################
    config = DeejaydConfig()
    media_backend = config.get("general", "media_backend")
    if media_backend == "gstreamer":
        # Install glib2 reactor
        from twisted.internet import glib2reactor
        glib2reactor.install()

    from twisted.internet import reactor
    # use twisted loop for kaa
    import kaa
    kaa.main.select_notifier('twisted')
    ###############################################################

    setEnv(options)
    startLog(options)

    from twisted.internet.error import CannotListenError
    from twisted.python import log

    # start core
    try:
        from deejayd.core import DeejayDaemonCore
        deejayd_core = DeejayDaemonCore(config)
    except Exception, ex:
        from deejayd.utils import str_encode
        log.err(\
            _("Unable to launch deejayd core, see traceback for more details"))
        log.msg(str_encode(traceback.format_exc()))
        sys.exit(1)

    service = False
    # net service
    if config.getboolean("net","enabled"):
        service = True
        from deejayd.net.protocol import DeejaydFactory

        factory = DeejaydFactory(deejayd_core)
        port = config.getint("net", "port")
        for bind_address in config.get_bind_addresses('net'):
            try: reactor.listenTCP(port, factory, interface=bind_address)
            except CannotListenError, err:
                deejayd_core.close()
                log.err(str(err))
                sys.exit(1)

    # webui service
    if config.getboolean("webui","enabled"):
        try:
            from deejayd import webui
        except ImportError:
            log.err(_("Webui does not seem to be installed, disabling."))
            config.set("webui", "enabled", False)
        else:
            service = True
            htdocs_dir = None
            if INSTALL_MODE == 'source':
                htdocs_dir = os.path.abspath(os.path.join(INSTALL_BIN_DIR, '..',
                                                          'data', 'htdocs'))
            elif INSTALL_MODE == 'installed':
                htdocs_dir = os.path.abspath(os.path.join(INSTALL_PREFIX,
                                                          'share', 'deejayd',
                                                          'htdocs'))
            try:
                site = webui.init(deejayd_core, config,
                                  options.webui_logfile, htdocs_dir)
            except webui.DeejaydWebError, err:
                deejayd_core.close()
                log.err(err)
                sys.exit(1)

            port = config.getint("webui","port")
            for bind_address in config.get_bind_addresses('webui'):
                try: reactor.listenTCP(port, site, interface=bind_address)
                except CannotListenError, err:
                    deejayd_core.close()
                    log.err(str(err))
                    sys.exit(1)

    # launch reactor
    if not service:
        deejayd_core.close()
        log.err(_("No service has been activated"))
        sys.exit(1)
    reactor.addSystemEventTrigger('after','shutdown',deejayd_core.close)
    reactor.run()

# vim: ts=4 sw=4 expandtab
