import subprocess, shlex
import time, os, errno
from StringIO import StringIO
import whatap.util.logging_util as logging_util
import threading, signal

def executeCmd(command='getconf CLK_TCK'):
    process = subprocess.Popen(shlex.split(command), stdout=subprocess.PIPE, stderr=subprocess.PIPE)
    buf = StringIO()
    while True:
        output = process.stdout.readline()
        if output == '' and process.poll() is not None:
            break
        buf.write(output)

    return buf.getvalue()

def executeCmdEx(command=None, uid=None, gid= None):
    if uid and gid:
        def preexec_fn():
            os.setgid(uid)
            os.setuid(gid)
        process = subprocess.Popen(shlex.split(command), stdout=subprocess.PIPE, stderr=subprocess.PIPE, preexec_fn= preexec_fn)
    else:
        process = subprocess.Popen(shlex.split(command), stdout=subprocess.PIPE, stderr=subprocess.PIPE)
    buf = StringIO()
    while True:
        output = process.stdout.readline()
        return_code = process.poll()
        if output == '' and return_code is not None:
            break
        buf.write(output)

    return return_code, buf.getvalue()

def executeCommandSimple(command=[], output= None):
    process = subprocess.Popen(command, stdout=output, stderr=output)
    return process

def executeCommandShell(command):
    process = subprocess.Popen(command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)

    stdout, _ = process.communicate()

    return stdout.decode('utf-8').strip()

def _killProcessGroup(process, command):
    # SERVER-1847 Cause C: escalate SIGTERM -> SIGKILL on the process group so
    # a timed-out command (and any children it spawned) is torn down instead
    # of accumulating. killpg is used because the child is a session leader
    # (preexec_fn=os.setsid), so its pgid equals its pid.
    try:
        pgid = os.getpgid(process.pid)
    except Exception:
        pgid = process.pid

    for sig in (signal.SIGTERM, signal.SIGKILL):
        try:
            os.killpg(pgid, sig)
        except OSError, e:
            if e.errno == errno.ESRCH:
                # process group already gone; nothing left to kill
                return
            logging_util.error("excute command kill signal fail : ", command, e)
        except Exception, e:
            logging_util.error("excute command kill signal fail : ", command, e)
        # brief grace period before escalating to SIGKILL
        time.sleep(0.2)


def executeCommandArgsWithTimeout(args, timeout = 5):
    """Run args (a list) without a shell, with the same timeout handling.

    SERVER-1987: building a command string and running it with shell=True lets
    any byte in an interpolated value be read as shell syntax. A collector that
    returned uninitialised memory as an interface name turned a stray '>' into
    file creation in the working directory, and any other metacharacter would
    have run as root. Passing a list keeps values as arguments.
    """
    process = subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.PIPE, preexec_fn=os.setsid)
    return _communicateWithTimeout(process, args, timeout)


def executeCommandShellWithTimeout(command, timeout = 5):
    process = subprocess.Popen(command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, preexec_fn=os.setsid)
    return _communicateWithTimeout(process, command, timeout)


def _communicateWithTimeout(process, command, timeout):
    stdout = [None]

    def target():
        stdout[0], _ = process.communicate()

    thread = threading.Thread(target=target)
    thread.setDaemon(True)
    thread.start()

    thread.join(timeout)
    if thread.is_alive():
        logging_util.error("excute command timeout error : ", command)
        _killProcessGroup(process, command)
        thread.join(timeout)
        if thread.is_alive():
            # communicate() is still blocked (typically a process stuck in
            # uninterruptible I/O on a hung SAN, which even SIGKILL cannot
            # interrupt until the I/O returns). Give up on the abandoned daemon
            # thread instead of joining forever; that thread stays the sole
            # owner of the child and reaps it (via Popen.wait -> waitpid) once
            # the process finally dies, so we do not add a second, racing
            # waitpid here.
            logging_util.error("excute command kill fail : ", command)
        return ""

    if stdout[0] == None:
        logging_util.error("excute command runtime error : ", command, process.returncode)
        return ""

    return stdout[0].decode('utf-8', 'replace').strip()


def initPollCommand():
    #kill old poll process pid
    pass

# TODO pid manager 
def pollCommand(command):
    process = subprocess.Popen(shlex.split(command), stdout=subprocess.PIPE, stderr=subprocess.PIPE, preexec_fn = os.setsid)

    return process



def getSimpleDict(command=None):
    started=time.time()*1000
    process = subprocess.Popen(shlex.split(command), stdout=subprocess.PIPE)
    result = {}
    while True:
        output = process.stdout.readline().strip()
        #print 'getVmStat', output
        if not output and process.poll() is not None:
            break
        if output:
            #print 'getVmStat', output
            k, v = output.split()
            result[k] = v

    return result

def fixByteUnit(src):
    unit = {'k': 1024,'m': 1024*1024, 'g': 1024*1024*1024, 't': 1024*1024*1024*1024}
    try:
        src = int(float(src))
        return src
    except ValueError, e:
        src = int(float(src[:-1])) * unit[src[-1].lower()]

        return src


