#
# Copyright 2001 by Object Craft P/L, Melbourne, Australia.
#
# LICENCE - see LICENCE file distributed with this software for details.
#
import sys
import os
from errno import *
import signal
import time


class PidFileError(Exception):
    pass


class PidFile:

    def __init__(self, pidfilename):
        self._pid_filename = pidfilename

    def getpid(self):
        try:
            pid = open(self._pid_filename,"r").readline()
        except IOError, (eno, estr):
            if eno == ENOENT:
                return 0
            raise PidFileError("%s: %s" % (self._pid_filename, estr))
        try:
            pid = int(pid)
        except ValueError:
            raise PidFileError("%s: unknown format" % \
                               self._pid_filename)
        if pid <= 1:
            raise PidFileError("%s: unknown format" % \
                               self._pid_filename)
        try:
            os.kill(pid,0)
        except OSError, (eno, estr):
            if eno == ESRCH:
                # Stale lock file, remove and sleep to avoid creation race
                sys.stderr.write("Removing state lock for pid %d\n" % pid)
                os.unlink(self._pid_filename)
                time.sleep(2)
                return 0
            raise PidFileError("pid %d: %s" % (pid, estr))

        return pid

    def is_running(self):
        return (self.getpid() != 0)

    def kill(self, sig = signal.SIGTERM):
        pid = self.getpid()
        if pid:
            os.kill(pid, sig)

    def start(self, pid = None):
        if pid is None:
            pid = os.getpid()

        daemon_pid = self.getpid()
        if daemon_pid:
            raise PidFileError("daemon already running, pid %d" % daemon_pid)

        # We use fd ops because we want O_EXCL semantics on the created file
        try:
            pid_fd = os.open(self._pid_filename, 
                             os.O_WRONLY | os.O_CREAT | os.O_EXCL, 0644)
        except OSError, (eno, estr):
            raise PidFileError("can't create pid file %s: %s" %\
                               (self._pid_filename, estr))
        os.write(pid_fd, "%d\n" % pid)
        os.close(pid_fd)

    def stop(self):
        pid = self.getpid()
        if pid != os.getpid():
            raise PidFileError("Pid file doesn't belong to us - pid %d" % pid)
        try:
            os.unlink(self._pid_filename)
        except:
            pass
