# Copyright 1999-2012. Parallels IP Holdings GmbH. All Rights Reserved.
import os
import sys
import statvfs
import errno
from shutil import copyfileobj
from os.path import isdir, join

try: import subprocess
except ImportError: import compat.subprocess as subprocess


mswindows = (sys.platform == "win32")


class PsException(Exception):
    def __init__(self, params):
        Exception.__init__(self, ' '.join(params))


def chown(path, user_name, group_name):
    if not mswindows:
        import pwd, grp
        uid = pwd.getpwnam(user_name)[2]
        gid = grp.getgrnam(group_name)[2]
        os.chown(path, uid, gid)


def kill(pid, sig):
    os_pid = int(pid)
    
    if mswindows:
        from win32api import OpenProcess, TerminateProcess
        from pywintypes import error
        try:
            handle = OpenProcess(1,0,os_pid)
        except error:
            return False
        try:
            terminate_result = TerminateProcess(handle,0)
        except error:
            return False
        return terminate_result != 0
    else:
        return os.kill(os_pid,sig)


def is_active(pid):
    os_pid = int(pid)
    
    if mswindows:
        from win32api import OpenProcess, CloseHandle
        from win32process import GetExitCodeProcess
        from pywintypes import error
        
        PROCESS_QUERY_INFORMATION = 1024
        try:
            handle = OpenProcess(PROCESS_QUERY_INFORMATION,0,os_pid)
        except error:
            return False
        if handle:
            STILL_ACTIVE = 259
            exitcode = GetExitCodeProcess(handle)
            CloseHandle(handle)
            if exitcode == STILL_ACTIVE:
                return True
        
        return False
    
    else:
        ps_params = ["ps", "-p", str(os_pid), "-o", "state"]
        try:
            ps = subprocess.Popen(ps_params,stdout=subprocess.PIPE)
            output = ps.communicate()[0]
            output_lines = output.splitlines()
            if len(output_lines) > 1 and not output_lines[1][0] in ["X", "Z"]:
                return True
            return False
        except OSError, e:
            if e.errno == errno.ENOEXEC:
                raise PsException(ps_params)
            raise


args_separator = ' '

# This is cross-platform realization for 'get_process_command_line' function
# Does not work with 64-bit Windows applications, that means you will always get empty string as command line for them 
def get_cmd(pid):
    os_pid = int(pid)
    
    if mswindows:
        from win32api import OpenProcess, CloseHandle
        from win32process import EnumProcessModules, GetModuleFileNameEx
        from pywintypes import error
        
        PROCESS_QUERY_INFORMATION = 1024
        PROCESS_VM_READ = 16
        
        try:
            handle = OpenProcess(PROCESS_QUERY_INFORMATION|PROCESS_VM_READ,0,os_pid)
        except error:
            return ''
        
        if not handle:
            return ''
        
        cmd = ''
        try:
            executable_module_handle = EnumProcessModules(handle)[0]
            try:
                cmd = GetModuleFileNameEx(handle,executable_module_handle)
            except error:
                pass
        except error:
            # EnumProcessModules raises ERROR_PARTIAL_COPY for 64-bit applications
            pass
        
        CloseHandle(handle)
        return cmd
    
    else:
        try:
            ps = open('/proc/%d/cmdline'%os_pid,'rb')
        except:
            return ''
        
        rawcmdline=[]
        cmdline=[]
        while True:
            try:
                onebyte = ps.read(1)
            except:
                return ''
            if onebyte:
                rawcmdline.append(onebyte)
            else:
                break
        
        try:
            ps.close()
        except:
            pass
        
        if len(rawcmdline) > 0 and rawcmdline[-1] == '\x00':
            rawcmdline.pop()
        
        for onechar in rawcmdline:
            if onechar == '\x00':
                onechar = args_separator
            cmdline.append(onechar)
        return unix_fs_decode(''.join(cmdline))


def createToFileSaver(outFileName):
    def fileSaver(infh):
        outfh = file(outFileName, "wb")
        try:
            copyfileobj(infh, outfh)
        finally:
            close_nothrow(infh)
            close_nothrow(outfh)
    return fileSaver


def saveToFile(outFileName, fh):
    createToFileSaver(outFileName)(fh)


def close_nothrow(fh):
    try:
        fh.close()
    except:
        pass


def unlink_nothrow(filename):
    try:
        os.unlink(filename)
    except OSError:
        pass


def getDirectorySize(dir_name):
    size = 0
    for root, dirs, files in os.walk(dir_name):
        size += sum([ os.path.getsize( os.path.join(root,name)) for name in files] )
    return size


def unlink_recursively(dirname):
    if isdir(dirname):
        names = os.listdir(dirname)
        for name in names:
            fullname = join(dirname, name)
            if isdir(fullname):
                unlink_recursively(fullname)
            else:
                unlink_nothrow(fullname)
        try:
            os.rmdir(dirname)
        except OSError:
            pass


def free_bytes(path):
    if mswindows:
        from win32file import GetDiskFreeSpace
        sectorsPerCluster, bytesPerSector, numFreeClusters, totalNumClusters = GetDiskFreeSpace(os.path.splitdrive(path)[0] + "\\")
        sectorsPerCluster = long(sectorsPerCluster)
        bytesPerSector = long(bytesPerSector)
        numFreeClusters = long(numFreeClusters)
        return numFreeClusters * sectorsPerCluster * bytesPerSector
    else:
        stats = os.statvfs(path)
        return stats[statvfs.F_BSIZE] * stats[statvfs.F_BAVAIL]


def win_fs_encode(item):
    if isinstance(item, unicode):
        return item.encode('mbcs')
    else:
        return item


def unix_fs_encode(item):
    if isinstance(item, unicode):
        return item.encode('utf-8')
    else:
        return item


def fs_compliant_encode(item):
    if mswindows:
        fs_compliant_encode = win_fs_encode
    else:
        fs_compliant_encode = unix_fs_encode
    return fs_compliant_encode(item)


def win_fs_decode(item):
    return unicode(item, 'mbcs')


def unix_fs_decode(item):
    return unicode(item, 'utf-8')


def fs_compliant_decode(item):
    if mswindows:
        fs_compliant_decode = win_fs_decode
    else:
        fs_compliant_decode = unix_fs_decode
    return fs_compliant_decode(item)


if mswindows:
    from win32file import CreateFile, LockFileEx, UnlockFileEx, CloseHandle
    from win32con import GENERIC_READ, GENERIC_WRITE, FILE_SHARE_READ, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, LOCKFILE_EXCLUSIVE_LOCK, LOCKFILE_FAIL_IMMEDIATELY
    from pywintypes import error, OVERLAPPED
    from win32security import SECURITY_ATTRIBUTES
else:
    from fcntl import LOCK_EX, LOCK_SH, LOCK_NB, LOCK_UN, lockf


class Interlock:
    def __init__(self,file):
        self.__file = file
        self.__locked = False
        self.__hfile = None
        self.__ov = None

    def __del__(self):
        if self.__locked:
            self.unlock()

    def lock(self):
        if mswindows:
            secur_att = SECURITY_ATTRIBUTES()
            secur_att.Initialize()
            try:
                self.__hfile = CreateFile(self.__file,GENERIC_READ|GENERIC_WRITE,FILE_SHARE_READ,secur_att,OPEN_ALWAYS,FILE_ATTRIBUTE_NORMAL,0)
                lock_flags = LOCKFILE_EXCLUSIVE_LOCK | LOCKFILE_FAIL_IMMEDIATELY
                self.__ov = OVERLAPPED()
                LockFileEx(self.__hfile, lock_flags, 0, -0x7fff0000, self.__ov)
                self.__locked = True
                return True
            except error:
                return False
        else:
            try:
                self.__hfile = open(self.__file,'w')
                lock_flags = LOCK_EX | LOCK_NB
                lockf(self.__hfile.fileno(), lock_flags)
                return True
            except IOError:
                return False
        return False


    def unlock(self):
        if self.__locked:
            if mswindows:
                try:
                    UnlockFileEx(self.__hfile, 0, -0x7fff0000, self.__ov)
                    self.__hfile.Close()
                except error:
                    pass
            else:
                lock_flags = LOCK_UN
                lockf(self.__hfile.fileno(), lock_flags)
            
            self.__locked = False
            unlink_nothrow(self.__file)

    def is_locked(self):
        if mswindows:
            if os.access(self.__file, os.F_OK):
                if os.access(self.__file, os.R_OK):
                    return False
                else:
                    return True
            else:
                return False
        else:
            with open(self.__file, 'w') as hfile:
                try:
                    lock_flags = LOCK_EX | LOCK_NB
                    lockf(hfile.fileno(), lock_flags)
                    return False
                except IOError, e:
                    if e.errno == errno.EACCES or e.errno == errno.EAGAIN:
                        return True
                    raise



usage = """Usage: %prog <status|kill|get_cmd> <%pid%>


Warning: Improper use of 'kill' command can damage your OS environment!
"""

def main():
    if not len(sys.argv) == 3:
        print usage
        sys.exit(1)
    command = sys.argv[1]
    pid = sys.argv[2]
    if command not in ['status','kill','get_cmd']:
        print usage
        sys.exit(1)
    if command == 'kill':
        result = kill(pid,0)
        print "kill result: %s" % result 
    elif command == 'status':
        if is_active(pid):
            print "process %s is active" % pid
        else:
            print "process %s is not running" % pid
    else:
        if is_active(pid):
            print "process cmd: %s" % get_cmd(pid)
        else:
            print "process %s is not running" % pid 

if __name__ == '__main__':
    main()

# Local Variables:
# mode: python
# indent-tabs-mode: nil
# tab-width: 4
# End:
