# Copyright (C) 2013 Science and Technology Facilities Council.
#
# 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 3 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, see <http://www.gnu.org/licenses/>.

# hashlib replaced md5 in Python 2.5
try:
    from hashlib import md5
except ImportError:
    from md5 import md5
import os
import os.path

def pidfile_write(pidfile, pid):
    """Attempt to write a key for the given process into the file specified."""

    f = None

    try:
        try:
            f = open(pidfile, 'w')
            f.write(_get_process_key(pid))

        except IOError:
            pass

    finally:
        if f is not None:
            f.close()

def pidfile_running(pidfile):
    """Read the pidfile specified and check if the process is running."""

    f = None

    try:
        try:
            f = open(pidfile)
            return _check_process_key(f.read().strip())

        except IOError:
            return False

    finally:
        if f is not None:
            f.close()

def pidfile_delete(pidfile):
    """Attempt to delete the specified pidfile."""

    try:
        os.unlink(pidfile)

    except OSError:
        pass

def _get_process_key(pid):
    """Generate a string uniquely identifying a processess.

    If the cmdline file for this process can be read, then
    return a string containg the PID and the MD5 digest of that
    file.

    Otherwise just return a string containing the PID."""

    if not isinstance(pid, int):
        raise Exception('Process ID is not an integer.')

    f = None

    try:
        try:
            f = open(os.path.join('/proc', str(pid), 'cmdline'), 'rb')
            h = md5(f.read(1024))

        except IOError:
            h = None

    finally:
        if f is not None:
            f.close()

    if h is None:
        return str(pid)

    else:
        return str(pid) + ' ' + h.hexdigest()

def _check_process_key(key):
    """Check a process key generated by _get_process_key().

    If the key contains spaces, the PID is extracted from the
    first word and the key is compared to the output
    of _get_process_key().

    Otherwise take the key to be a plain PID and check that the
    process is still running."""

    if ' ' in key:
        (pid, hash) = key.split(' ')

        try:
            return _get_process_key(int(pid)) == key

        except ValueError:
            return False

    else:
        try:
            pid = int(key)

        except ValueError:
            return False

        try:
            os.kill(pid, 0)
            return True

        except OSError:
            return False


