Improved pidfile handling (from sickbeard)

Added signal watch to shutdown properly and remove pidfile (from
sickbeard)
This commit is contained in:
Benjamin Runnels
2012-12-17 10:14:46 -06:00
parent 4bc40a3218
commit f10b38c418
2 changed files with 75 additions and 54 deletions
+48 -26
View File
@@ -16,6 +16,7 @@
import os, sys, locale
import time
import signal
from lib.configobj import ConfigObj
@@ -27,7 +28,10 @@ try:
import argparse
except ImportError:
import lib.argparse as argparse
signal.signal(signal.SIGINT, headphones.sig_handler)
signal.signal(signal.SIGTERM, headphones.sig_handler)
def main():
@@ -36,10 +40,10 @@ def main():
headphones.FULL_PATH = os.path.abspath(sys.executable)
else:
headphones.FULL_PATH = os.path.abspath(__file__)
headphones.PROG_DIR = os.path.dirname(headphones.FULL_PATH)
headphones.ARGS = sys.argv[1:]
# From sickbeard
headphones.SYS_PLATFORM = sys.platform
headphones.SYS_ENCODING = None
@@ -53,7 +57,7 @@ def main():
# for OSes that are poorly configured I'll just force UTF-8
if not headphones.SYS_ENCODING or headphones.SYS_ENCODING in ('ANSI_X3.4-1968', 'US-ASCII', 'ASCII'):
headphones.SYS_ENCODING = 'UTF-8'
# Set up and gather command line arguments
parser = argparse.ArgumentParser(description='Music add-on for SABnzbd+')
@@ -65,55 +69,73 @@ def main():
parser.add_argument('--config', help='Specify a config file to use')
parser.add_argument('--nolaunch', action='store_true', help='Prevent browser from launching on startup')
parser.add_argument('--pidfile', help='Create a pid file (only relevant when running as a daemon)')
args = parser.parse_args()
if args.verbose:
headphones.VERBOSE = 2
elif args.quiet:
headphones.VERBOSE = 0
if args.daemon:
headphones.DAEMON=True
headphones.VERBOSE = 0
if args.pidfile :
headphones.PIDFILE = args.pidfile
if sys.platform == 'win32':
print "Daemonize not supported under Windows, starting normally"
else:
headphones.DAEMON=True
headphones.VERBOSE = False
if args.pidfile:
headphones.PIDFILE = str(args.pidfile)
# If the pidfile already exists, headphones may still be running, so exit
if os.path.exists(headphones.PIDFILE):
sys.exit("PID file '" + headphones.PIDFILE + "' already exists. Exiting.")
# The pidfile is only useful in daemon mode, make sure we can write the file properly
if headphones.DAEMON:
headphones.CREATEPID = True
try:
file(headphones.PIDFILE, 'w').write("pid\n")
except IOError, e:
raise SystemExit("Unable to write PID file: %s [%d]" % (e.strerror, e.errno))
else:
logger.warn("Not running in daemon mode. PID file creation disabled.")
if args.datadir:
headphones.DATA_DIR = args.datadir
else:
headphones.DATA_DIR = headphones.PROG_DIR
if args.config:
headphones.CONFIG_FILE = args.config
else:
headphones.CONFIG_FILE = os.path.join(headphones.DATA_DIR, 'config.ini')
# Try to create the DATA_DIR if it doesn't exist
if not os.path.exists(headphones.DATA_DIR):
try:
os.makedirs(headphones.DATA_DIR)
except OSError:
raise SystemExit('Could not create data directory: ' + headphones.DATA_DIR + '. Exiting....')
# Make sure the DATA_DIR is writeable
if not os.access(headphones.DATA_DIR, os.W_OK):
raise SystemExit('Cannot write to the data directory: ' + headphones.DATA_DIR + '. Exiting...')
# Put the database in the DATA_DIR
headphones.DB_FILE = os.path.join(headphones.DATA_DIR, 'headphones.db')
headphones.CFG = ConfigObj(headphones.CONFIG_FILE, encoding='utf-8')
# Read config & start logging
headphones.initialize()
if headphones.DAEMON:
if sys.platform == "win32":
print "Daemonize not supported under Windows, starting normally"
else:
headphones.daemonize()
#configure the connection to the musicbrainz database
headphones.mb.startmb()
@@ -123,8 +145,8 @@ def main():
logger.info('Starting Headphones on forced port: %i' % http_port)
else:
http_port = int(headphones.HTTP_PORT)
# Try to start the server.
# Try to start the server.
webstart.initialize({
'http_port': http_port,
'http_host': headphones.HTTP_HOST,
@@ -133,15 +155,15 @@ def main():
'http_username': headphones.HTTP_USERNAME,
'http_password': headphones.HTTP_PASSWORD,
})
logger.info('Starting Headphones on port: %i' % http_port)
if headphones.LAUNCH_BROWSER and not args.nolaunch:
headphones.launch_browser(headphones.HTTP_HOST, http_port, headphones.HTTP_ROOT)
# Start the background threads
headphones.start()
while True:
if not headphones.SIGNAL:
try:
@@ -156,9 +178,9 @@ def main():
headphones.shutdown(restart=True)
else:
headphones.shutdown(restart=True, update=True)
headphones.SIGNAL = None
return
if __name__ == "__main__":
+27 -28
View File
@@ -41,6 +41,7 @@ SYS_ENCODING = None
VERBOSE = 1
DAEMON = False
CREATEPID = False
PIDFILE= None
SCHED = Scheduler()
@@ -577,42 +578,34 @@ def daemonize():
# Do first fork
try:
pid = os.fork()
if pid == 0:
pass
else:
# Exit the parent process
logger.debug('Forking once...')
os._exit(0)
pid = os.fork() # @UndefinedVariable - only available in UNIX
if pid != 0:
sys.exit(0)
except OSError, e:
sys.exit("1st fork failed: %s [%d]" % (e.strerror, e.errno))
raise RuntimeError("1st fork failed: %s [%d]" % (e.strerror, e.errno))
os.setsid()
# Do second fork
# Make sure I can read my own files and shut out others
prev = os.umask(0) # @UndefinedVariable - only available in UNIX
os.umask(prev and int('077', 8))
# Make the child a session-leader by detaching from the terminal
try:
pid = os.fork()
if pid > 0:
logger.debug('Forking twice...')
os._exit(0) # Exit second parent process
pid = os.fork() # @UndefinedVariable - only available in UNIX
if pid != 0:
sys.exit(0)
except OSError, e:
sys.exit("2nd fork failed: %s [%d]" % (e.strerror, e.errno))
raise RuntimeError("2nd fork failed: %s [%d]" % (e.strerror, e.errno))
os.chdir("/")
os.umask(0)
dev_null = file('/dev/null', 'r')
os.dup2(dev_null.fileno(), sys.stdin.fileno())
si = open('/dev/null', "r")
so = open('/dev/null', "a+")
se = open('/dev/null', "a+")
os.dup2(si.fileno(), sys.stdin.fileno())
os.dup2(so.fileno(), sys.stdout.fileno())
os.dup2(se.fileno(), sys.stderr.fileno())
pid = os.getpid()
pid = str(os.getpid())
logger.info('Daemonized to PID: %s' % pid)
if PIDFILE:
logger.info('Writing PID %s to %s' % (pid, PIDFILE))
if CREATEPID:
logger.info("Writing PID " + pid + " to " + str(PIDFILE))
file(PIDFILE, 'w').write("%s\n" % pid)
def launch_browser(host, port, root):
@@ -827,6 +820,11 @@ def start():
started = True
def sig_handler(signum=None, frame=None):
if type(signum) != type(None):
logger.info("Signal %i caught, saving and exiting..." % int(signum))
shutdown()
def dbcheck():
conn=sqlite3.connect(DB_FILE)
@@ -1013,6 +1011,7 @@ def shutdown(restart=False, update=False):
if not restart and not update:
logger.info('Headphones is shutting down...')
if update:
logger.info('Headphones is updating...')
try:
@@ -1020,7 +1019,7 @@ def shutdown(restart=False, update=False):
except Exception, e:
logger.warn('Headphones failed to update: %s. Restarting.' % e)
if PIDFILE :
if CREATEPID :
logger.info ('Removing pidfile %s' % PIDFILE)
os.remove(PIDFILE)