#!/usr/whatap/infra/lib/bin/python2.7


import os, time, sys, signal, Queue
from whatap.agent.conf import configure as config
import whatap.util.logging_util as logging_util

AliveSockAddr       = os.path.join(os.environ.get("WHATAP_HOME","/usr/whatap/infra/conf"),"whatap_infrad.sock")
KEEP_ALIVE_PROTOCOL = 0x5B9B

childPid = 0

def setPid():
    whatap_home = os.environ.get("WHATAP_HOME","/usr/whatap/infra/conf")
    if whatap_home:
        filepath = os.path.join(whatap_home,'whatap.pid')
        f = open(filepath,'w')
        f.write(str(os.getpid()))
        f.close()

import platform
import re

def is_fork():
    system = platform.system().lower()
    if system == "sunos" or system == "hp-ux":
        return True

    # Attempt to read the release information from /etc/redhat-release
    try:
        with open("/etc/redhat-release", "r") as file:
            release_info = file.read()
            
            # Check if the release information mentions CentOS and version 5
            if "CentOS" in release_info and re.search(r"release 5(\.\d+)?", release_info):
                return True
    except IOError:
        # Handle error (e.g., file not found) if /etc/redhat-release does not exist
        pass

    # Fallback check using platform module
    dist_name, version, id = platform.linux_distribution(full_distribution_name=0)
    return dist_name.lower() in ["centos", "redhat"] and version.startswith("5")


def setLogger(logfileName):
    conf = config.GetConfig()
    logPath = os.path.join(conf.logPath, logfileName)
    logSize = conf.logSize * 1024 * 1024 #MB
    logBackup = conf.logBackup
    logging_util.initLogger(logPath, isDebug = conf.Debug, logMaxBytes = logSize, logBackupCount = logBackup) 



def daemonize(uid, isfork=is_fork()):
    if isfork:
        if not hasattr ( os, 'fork'):
            return
        if os.fork():
            os._exit(0)
        os.setsid()
        os.setuid( uid )
        signal.signal(signal.SIGHUP, signal.SIG_IGN)

        if os.fork():
            os._exit(0)
        sys.stdin = open("/dev/null", "r")

    setPid()

    #deamon
    sys.stdout = open("/dev/null", "w")
    sys.stderr = open("/dev/null", "w")

    import whatap.util.thread_util as thread_util
    logging_util.isDebug = config.GetConfig().Debug
    from datetime import datetime, timedelta
    if config.GetConfig().HouseKeepEnabled:
        childHealthChannel = Queue.Queue()
        thread_util.async(listenChildAlive, childHealthChannel)
        time.sleep(1)
        def watchChildProcess():
            global childPid
            logging_util.debug("whatap.daemonize step -2 start forking child ")
            childProcess = forkChild()
            childPid = childProcess.pid
            logging_util.debug("whatap.daemonize step -3 child forked receiving child health  ")
            childHealthy = True
            childStarted = datetime.now()

            while childHealthy :
                logging_util.debug("whatap.daemonize step -3.1 wait health ")
                try:
                    receivedStatus = childHealthChannel.get(timeout = 180)
                    logging_util.debug("whatap.daemonize step -4 received child health: ", receivedStatus)
                    if not receivedStatus :
                        logging_util.error("whatap.daemonize step -5 received bad health: ", childProcess)
                        if childProcess :
                            os.kill(childProcess.pid, signal.SIGKILL)
                            childProcess.wait()
                            childHealthy = False

                except Queue.Empty, e:
                    logging_util.error("whatap.daemonize step -7 timeout childProcess: ", childProcess)
                    if childProcess:
                        os.kill(childProcess.pid, signal.SIGKILL)
                        childProcess.wait()
                        childHealthy = False
                    logging_util.debug("whatap.daemonize step -8 kill complete ")

                now = datetime.now()
                cooltimeThreshold = timedelta(hours = config.GetConfig().HouseKeepCooltime)
                if config.GetConfig().HouseKeepCooltime < 1:
                    cooltimeThreshold += timedelta(minutes = 1)

                logging_util.debug("whatap.daemonize step -9  ",
                    childHealthy, now - childStarted > cooltimeThreshold, now.hour, config.GetConfig().HouseKeepHour,
                    "kill switch:",
                    childHealthy and now- childStarted > cooltimeThreshold and now.hour == config.GetConfig().HouseKeepHour)
                if childHealthy and now - childStarted > cooltimeThreshold and now.hour == config.GetConfig().HouseKeepHour:
                    os.kill(childProcess.pid, signal.SIGKILL)
                    childProcess.wait()
                    childHealthy = False

            if childHealthy :
                time.sleep(30)
        def _loop():
            while True:
                watchChildProcess()
        thread_util.async(_loop)
    else:
        thread_util.async(boot.boot)

def listenChildAlive(badHealthChannel ):
    import socket
    import whatap.util.logging_util as logging_util
    while True:
        logging_util.debug("listenChildAlive step -1")
        if os.path.exists(AliveSockAddr):
            os.unlink(AliveSockAddr)
        alive_sock_path= os.path.split(AliveSockAddr)[0]
        if not os.path.exists(alive_sock_path):
            os.makedirs(alive_sock_path)
        logging_util.debug("listenChildAlive step -2")
        l = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
        l.bind(AliveSockAddr)
        l.listen(1)

        while True:
            conn = None
            try:
                conn, _ = l.accept()
            except Exception, e:
                logging_util.debugStack(e)
                if conn:
                    conn.close()
                time.sleep(3)

            logging_util.debug("listenChildAlive step -6")

            keepAlive(conn, badHealthChannel)
            logging_util.debug("listenChildAlive step -7")

def keepAlive(conn, healthChannel ):
    import whatap.util.logging_util as logging_util
    try:
        _keepAlive(conn, healthChannel)
    except Exception, e:

        logging_util.debugStack(e)
        return
    finally:
        if conn :
            conn.close()

def _keepAlive(conn, healthChannel):
    from whatap.io.data_inputx import DataInputX
    import whatap.util.logging_util as logging_util
    from whatap.value.map_value import MapValue

    reader = DataInputX(conn)
    while True:
        logging_util.debug("whatap.keepAlive step -1 ")

        protocol = reader.readShort()
        if protocol != KEEP_ALIVE_PROTOCOL:
            logging_util.debug("KeepAlive protocol protocol:", protocol)
            return

        logging_util.debug("whatap.keepAlive step -2 ")
        msgbytes = reader.readIntBytes(2048)

        datainputx = DataInputX(msgbytes)
        msgmap = MapValue()
        msgmap.read(datainputx)
        logging_util.debug("whatap.keepAlive step -3 ", msgmap.getText("Health"))
        if msgmap.getText("Health") != "OK":
            healthChannel.put(False)
            return

        healthChannel.put(True)
        logging_util.debug("whatap.keepAlive step -4 ")

def forkChild():
    import whatap.util.logging_util as logging_util
    import whatap.util.process_util as process_util
    import os,sys

    logging_util.debug("whatap.forkChild step -1 ")
    execPath = sys.argv[0]
    logging_util.debug("whatap.forkChild step -1.1 ", execPath)
    p = process_util.executeCommandSimple(command = (execPath, "foreground"))
    logging_util.debug("whatap.forkChild step -3 ")

    return p


def keepAliveSender():
    import whatap.util.logging_util as logging_util
    from whatap.io.data_outputx import DataOutputX
    from whatap.value.map_value import MapValue
    import whatap.agent.boot.boot as boot
    import socket

    while True:
        logging_util.debug("whatap.keepAliveSender step -1 ")

        serverconn = None
        while not serverconn:
            logging_util.debug("whatap.keepAliveSender step -1.1 ")
            s = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
            try:
                s.connect(AliveSockAddr)
                s.settimeout(30)
            except socket.error, msg:
                logging_util.debug("whatap.keepAliveSender step -2 ", msg)
                time.sleep(3)
                continue

            serverconn = s
        try:
            logging_util.debug("whatap.keepAliveSender step -3 ")
            while True:
                msgmap = MapValue()
                check = boot.isOK()
                logging_util.debug("whatap.keepAliveSender step -4 boot.isOK(): ", check)

                if check:
                    msgmap.putString("Health", "OK")
                else:
                    logging_util.error("HealthCheck Fail")
                    msgmap.putString("Health", "PROBLEM")

                dout = DataOutputX()
                dout.writeShort(KEEP_ALIVE_PROTOCOL)
                doutx = DataOutputX()
                msgmap.write(doutx)
                dout.writeIntBytes(doutx.toByteArray())
                try:
                    serverconn.sendall(dout.toByteArray())
                except Exception, e:
                    logging_util.debugStack(e)
                    break
                logging_util.debug("whatap.keepAliveSender step -5 ")
                time.sleep(60 )
        finally:
            if serverconn:
                serverconn.close()
        time.sleep(10 )


def exitOnStdinClose():
    import whatap.util.logging_util as logging_util
    logging_util.debug("whatap.exitOnStdinClose scanning stdin step -1 ")
    ppid = os.getppid()
    while True:
        if ppid != os.getppid():
            logging_util.debug("whatap.exitOnStdinClose scanning stdin step -2 ", ppid, os.getppid())
            os._exit(0)

        time.sleep(0.05)

def printusage():
    print "Usage"
    print "%s " % ( sys.argv[0] )

def signal_handler(sig, frame):
    import whatap.util.logging_util as logging_util
    import whatap.agent.boot.boot as boot
    logging_util.debug("whatap.signal step : ", sig, frame)
    if childPid != 0:
        os.kill(childPid, signal.SIGKILL)
        time.sleep(5)
    sys.exit(0)

def main():
    import argparse
    import whatap.agent.boot.boot as boot
    import whatap.util.logging_util as logging_util

    signal.signal(signal.SIGINT, signal_handler)
    signal.signal(signal.SIGTERM, signal_handler)
    signal.signal(signal.SIGSEGV, signal_handler)
    signal.signal(signal.SIGABRT, signal_handler)

    parser = argparse.ArgumentParser(description='Whatap Server Health Monitoring')
    parser.add_argument('cmd', metavar='cmd', type=str,
                        help='command', nargs='?')
    parser.add_argument('--user', metavar='user', type=str,
                        help='user to run custom script')

    args = parser.parse_args()

    import whatap.agent.conf.configure as conf
    conf.entrypoint_path = os.path.split(os.path.abspath(__file__))[0]
    conf.GetConfig()


    if args.cmd:
        import whatap.util.thread_util as thread_util
        import whatap.util.logging_util as logging_util
        if args.cmd == 'debug':
            boot.boot()
        elif args.cmd == 'foreground':
            setLogger("whatap_infra_foreground.log")
            thread_util.async(boot.boot)
            thread_util.async(exitOnStdinClose)
            thread_util.async(keepAliveSender)
        elif args.cmd == 'init-script':
            if args.user:
                conf.updateScripts(args.user)
                import whatap.agentless.checkmain as checkmain
                conf.GetConfig().load()
                checkmain.testRun()
            else:
                print("--user is required")
            return
    else:
        setLogger("whatap_infra.log")
        daemonize( os.getuid() )

    logging_util.debug("whatap_infrad.main ", conf.entrypoint_path)

    while conf.GetConfig().Enabled:
        time.sleep(1)

    print( 'agent disabled from service.whatap.io  exiting')

if __name__ == '__main__':
    try:
        main()
    except KeyboardInterrupt, e:
        sys.exit(0)
