dosage/dosagelib/singleton.py
2020-10-11 20:15:27 +02:00

84 lines
3 KiB
Python

# SPDX-License-Identifier: PSF-2.0
# Copied from: https://github.com/pycontribs/tendo
# Author: Sorin Sbarnea
# Changes: changed logging and formatting
import sys
import os
import errno
import tempfile
from .output import out
class SingleInstance(object):
"""
To prevent a script from running in parallel instantiate the
SingleInstance() class. If is there another instance
already running it will exit the application with the message
"Another instance is already running, quitting.",
returning an error code of -1.
>>> me = SingleInstance()
This is very useful to execute scripts by crontab that should only run
one at a time.
Note that this works by creating a lock file with a filename based
on the full path to the script file.
"""
def __init__(self, flavor_id="", exit_code=-1):
"""Create an exclusive lockfile or exit with an error and the given
exit code."""
self.initialized = False
scriptname = os.path.splitext(os.path.realpath(sys.argv[0]))[0]
lockname = scriptname.replace("/", "-").replace(":", "").replace("\\", "-")
if flavor_id:
lockname += "-%s" % flavor_id
lockname += '.lock'
tempdir = tempfile.gettempdir()
self.lockfile = os.path.normpath(os.path.join(tempdir, lockname))
out.debug("SingleInstance lockfile: " + self.lockfile)
if sys.platform == 'win32':
try:
# file already exists, try to remove it in case the previous
# execution was interrupted
if os.path.exists(self.lockfile):
os.unlink(self.lockfile)
self.fd = os.open(self.lockfile, os.O_CREAT | os.O_EXCL | os.O_RDWR)
except OSError:
type, e, tb = sys.exc_info()
if e.errno == errno.EACCES: # EACCES == 13
self.exit(exit_code)
raise
else: # non Windows
import fcntl
self.fp = open(self.lockfile, 'w')
try:
fcntl.lockf(self.fp, fcntl.LOCK_EX | fcntl.LOCK_NB)
# raises IOError on Python << 3.3, else OSError
except OSError:
self.exit(exit_code)
self.initialized = True
def exit(self, exit_code):
"""Exit with an error message and the given exit code."""
out.error("Another instance is already running, quitting.")
sys.exit(exit_code)
def __del__(self):
"""Remove the lock file."""
if not self.initialized:
return
try:
if sys.platform == 'win32':
if hasattr(self, 'fd'):
os.close(self.fd)
os.unlink(self.lockfile)
else:
import fcntl
fcntl.lockf(self.fp, fcntl.LOCK_UN)
if os.path.isfile(self.lockfile):
os.unlink(self.lockfile)
except Exception as e:
out.exception("could not remove lockfile: %s" % e)