#!/usr/bin/python3
# -*- coding: utf-8; -*-
#
# (c) 2008-2010 Mandriva, http://www.mandriva.com/
# (c) 2016-2017 Mandriva, http://www.siveo.net
#
# $Id$
#
# This file is part of Pulse 2, http://pulse2.mandriva.org
#
# Pulse 2 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.
#
# Pulse 2 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 Pulse 2.  If not, see <http://www.gnu.org/licenses/>.

"""
Binary file for the inventory server.
"""

import os
import os.path
from resource import RLIMIT_NOFILE, RLIM_INFINITY, getrlimit
import sys
import getopt
import logging
import logging.config
import multiprocessing as mp

from mmc.site import mmcconfdir, localstatedir
from mmc.core.log import ColoredFormatter
from pulse2.version import getVersion, getRevision
from pulse2.inventoryserver.server import InventoryGetService
from pulse2.inventoryserver.config import Pulse2OcsserverConfigParser

# Shared return state, so that father can know if children goes wrong
_shared_state = mp.Value('i', 0)
_lock = mp.Lock()

def running(daemonize, inifile, noconsoledebug = True):
    config = Pulse2OcsserverConfigParser()
    config.setup(inifile)

    # Set umask and effective UID and GID values
    os.umask(config.umask)
    os.setegid(config.daemon_group)
    os.seteuid(config.daemon_user)

    # Create log dir if it doesn't exist
    # TODO: should parse conf to know where are logs, if any
    os.system('mkdir -p %s/log/mmc' % localstatedir)

    # if we daemonize, we start by that before trying to initialise the service
    if daemonize:
        _lock.acquire()
        daemon(config, inifile)

    # Load logging configuration after daemonization, else inventory server
    # may crash
    logging.config.fileConfig(inifile)
    logger = logging.getLogger()
    # Log to stderr when not in daemon mode
    if not daemonize:
        if noconsoledebug:
            hdlr2 = logging.StreamHandler()
            hdlr2.setFormatter(ColoredFormatter("%(levelname)-18s %(message)s"))
            logger.addHandler(hdlr2)

    logger.info("Inventory server version('%s') build('%s')" % (str(getVersion()), str(getRevision())))

    if InventoryGetService().initialise(config):
        ret = 0
    else:
        ret = 1

    if daemonize:
        _shared_state.value = ret
        _lock.release()

    if ret == 0:
        InventoryGetService().run()

    return ret

def daemon(config, inifile):
    """
    daemonize pulse2-inventory-server

    @param pidfile: path to pid file
    @type pidfile: str
    """
    pidfile = config.pidfile

    # Test if pulse2-inventory-server has been already launched in daemon mode
    if os.path.isfile(pidfile):
        print pidfile+" pid already exist. Maybe pulse2-inventory-server is already running\n"
        print "use /etc/init.d script to stop and relaunch it"
        sys.exit(0)

    # do the UNIX double-fork magic, see Stevens' "Advanced
    # Programming in the UNIX Environment" for details (ISBN 0201563177)
    try:
        pid = os.fork()
        if pid > 0:
            _lock.acquire()
            # exit first parent
            sys.exit(_shared_state.value)
    except OSError, e:
        print >>sys.stderr, "fork #1 failed: %d (%s)" % (e.errno, e.strerror)
        sys.exit(1)

    # decouple from parent environment
    os.close(sys.stdin.fileno())
    os.close(sys.stdout.fileno())
    os.close(sys.stderr.fileno())
    os.open(os.devnull, os.O_RDWR)
    os.dup2(sys.stdin.fileno(), sys.stdout.fileno())
    os.dup2(sys.stdout.fileno(), sys.stderr.fileno())
    os.chdir("/")
    os.setsid()

    if (hasattr(os, "devnull")):
        REDIRECT_TO = os.devnull
    else:
        REDIRECT_TO = "/dev/null"

    maxfd = getrlimit(RLIMIT_NOFILE)[1]
    if maxfd == RLIM_INFINITY:
        maxfd = 1024

    for fd in range(0, maxfd):
        try:
            os.close(fd)
        except OSError:
            pass

    os.open(REDIRECT_TO, os.O_RDWR)
    os.dup2(0, 1)
    os.dup2(0, 2)

    # do second fork
    try:
        pid = os.fork()
        if pid > 0:
            # exit from second parent
            os.seteuid(0)
            os.setegid(0)
            os.system("echo " + str(pid) + " > " + pidfile)
            sys.exit(0)
    except OSError, e:
        print >>sys.stderr, "fork #2 failed: %d (%s)" % (e.errno, e.strerror)
        _shared_state.value = 1
        _lock.release()
        sys.exit(1)



if __name__ == "__main__":
    inifile = mmcconfdir + "/pulse2/inventory-server/inventory-server.ini"
    try:
        opts, suivarg = getopt.getopt(sys.argv[1:], "f:ds")
    except getopt.GetoptError:
        print "Error while trying to get options."
        sys.exit(2)
    daemonize = True
    debugtoconsole = True
    for option, argument in opts:
        if option == "-f":
            inifile = argument
        elif option == "-d":
            daemonize = False
        elif option == "-s":
            daemonize = False
            debugtoconsole = False

    if not os.path.exists(inifile):
        print "File '%s' does not exist." % inifile
        sys.exit(3)

    # Start the daemon main loop
    sys.exit(running(daemonize, inifile,debugtoconsole))
