|
|
|
#!/usr/bin/env python
|
|
|
|
from logging import handlers
|
|
|
|
from os.path import dirname
|
|
|
|
import logging
|
|
|
|
import os
|
|
|
|
import select
|
|
|
|
import signal
|
Default exception handler no longer assumes all remaining exceptions take (errno, string) pairs.
Previously any exception that made it all the way up to the default
exception handler would be expected to take (errno, string) pairs,
as is the python standard for exceptions thrown by system calls.
All exceptions that don't take enough arguments throw a ValueError.
Based on the errno tested, it appears that this code
is meant to silently ignore when a socket receives a SIGINT (from
e.g. a timeout.) This seems to be the only instance where handling
EINTR in this manner is desired - though having this bubble up this
far seems odd.
The existing code would also handle any other EINTR, though, which
includes those raised by OSError, WindowsError, and
anything that subclasses EnvironmentError, barring KeyboardError
because it is handled separately. This is a bug as there is already
some use of the signals module elsewhere in CouchPotato.py to trap
SIGINT and SIGTERM outside of system calls, and most of these other
EINTRs should be handled by code lower down the stack.
A default exception handler is also added, so that unhandled
exceptions will be logged, and raised.
13 years ago
|
|
|
import socket
|
|
|
|
import subprocess
|
|
|
|
import sys
|
|
|
|
import traceback
|
|
|
|
import time
|
|
|
|
|
|
|
|
# Root path
|
|
|
|
base_path = dirname(os.path.abspath(__file__))
|
|
|
|
|
|
|
|
# Insert local directories into path
|
|
|
|
sys.path.insert(0, os.path.join(base_path, 'libs'))
|
|
|
|
|
|
|
|
from couchpotato.environment import Env
|
|
|
|
from couchpotato.core.helpers.variable import getDataDir
|
|
|
|
|
|
|
|
class Loader(object):
|
|
|
|
|
|
|
|
do_restart = False
|
|
|
|
|
|
|
|
def __init__(self):
|
|
|
|
|
|
|
|
# Get options via arg
|
|
|
|
from couchpotato.runner import getOptions
|
|
|
|
self.options = getOptions(base_path, sys.argv[1:])
|
|
|
|
|
|
|
|
# Load settings
|
|
|
|
settings = Env.get('settings')
|
|
|
|
settings.setFile(self.options.config_file)
|
|
|
|
|
|
|
|
# Create data dir if needed
|
|
|
|
if self.options.data_dir:
|
|
|
|
self.data_dir = self.options.data_dir
|
|
|
|
else:
|
|
|
|
self.data_dir = os.path.expanduser(Env.setting('data_dir'))
|
|
|
|
|
|
|
|
if self.data_dir == '':
|
|
|
|
self.data_dir = getDataDir()
|
|
|
|
|
|
|
|
if not os.path.isdir(self.data_dir):
|
|
|
|
os.makedirs(self.data_dir)
|
|
|
|
|
|
|
|
# Create logging dir
|
|
|
|
self.log_dir = os.path.join(self.data_dir, 'logs');
|
|
|
|
if not os.path.isdir(self.log_dir):
|
|
|
|
os.mkdir(self.log_dir)
|
|
|
|
|
|
|
|
# Logging
|
|
|
|
from couchpotato.core.logger import CPLog
|
|
|
|
self.log = CPLog(__name__)
|
|
|
|
|
|
|
|
formatter = logging.Formatter('%(asctime)s %(levelname)s %(message)s', '%H:%M:%S')
|
|
|
|
hdlr = handlers.RotatingFileHandler(os.path.join(self.log_dir, 'error.log'), 'a', 500000, 10)
|
|
|
|
hdlr.setLevel(logging.CRITICAL)
|
|
|
|
hdlr.setFormatter(formatter)
|
|
|
|
self.log.logger.addHandler(hdlr)
|
|
|
|
|
|
|
|
def addSignals(self):
|
|
|
|
|
|
|
|
signal.signal(signal.SIGINT, self.onExit)
|
|
|
|
signal.signal(signal.SIGTERM, lambda signum, stack_frame: sys.exit(1))
|
|
|
|
|
|
|
|
from couchpotato.core.event import addEvent
|
|
|
|
addEvent('app.after_shutdown', self.afterShutdown)
|
|
|
|
|
|
|
|
def afterShutdown(self, restart):
|
|
|
|
self.do_restart = restart
|
|
|
|
|
|
|
|
def onExit(self, signal, frame):
|
|
|
|
from couchpotato.core.event import fireEvent
|
|
|
|
fireEvent('app.crappy_shutdown', single = True)
|
|
|
|
|
|
|
|
def run(self):
|
|
|
|
|
|
|
|
self.addSignals()
|
|
|
|
|
|
|
|
from couchpotato.runner import runCouchPotato
|
|
|
|
runCouchPotato(self.options, base_path, sys.argv[1:], data_dir = self.data_dir, log_dir = self.log_dir, Env = Env)
|
|
|
|
|
|
|
|
if self.do_restart:
|
|
|
|
self.restart()
|
|
|
|
|
|
|
|
def restart(self):
|
|
|
|
try:
|
|
|
|
# remove old pidfile first
|
|
|
|
try:
|
|
|
|
if self.runAsDaemon():
|
|
|
|
try: self.daemon.stop()
|
|
|
|
except: pass
|
|
|
|
except:
|
|
|
|
self.log.critical(traceback.format_exc())
|
|
|
|
|
|
|
|
# Release log files and shutdown logger
|
|
|
|
logging.shutdown()
|
|
|
|
time.sleep(3)
|
|
|
|
|
|
|
|
args = [sys.executable] + [os.path.join(base_path, os.path.basename(__file__))] + sys.argv[1:]
|
|
|
|
subprocess.Popen(args)
|
|
|
|
except:
|
|
|
|
self.log.critical(traceback.format_exc())
|
|
|
|
|
|
|
|
def daemonize(self):
|
|
|
|
|
|
|
|
if self.runAsDaemon():
|
|
|
|
try:
|
|
|
|
from daemon import Daemon
|
|
|
|
self.daemon = Daemon(self.options.pid_file)
|
|
|
|
self.daemon.daemonize()
|
|
|
|
except SystemExit:
|
|
|
|
raise
|
|
|
|
except:
|
|
|
|
self.log.critical(traceback.format_exc())
|
|
|
|
|
|
|
|
def runAsDaemon(self):
|
|
|
|
return self.options.daemon and self.options.pid_file
|
|
|
|
|
|
|
|
|
|
|
|
if __name__ == '__main__':
|
|
|
|
l = None
|
|
|
|
try:
|
|
|
|
l = Loader()
|
|
|
|
l.daemonize()
|
|
|
|
l.run()
|
|
|
|
except KeyboardInterrupt:
|
|
|
|
pass
|
|
|
|
except select.error:
|
|
|
|
pass
|
|
|
|
except SystemExit:
|
|
|
|
raise
|
Default exception handler no longer assumes all remaining exceptions take (errno, string) pairs.
Previously any exception that made it all the way up to the default
exception handler would be expected to take (errno, string) pairs,
as is the python standard for exceptions thrown by system calls.
All exceptions that don't take enough arguments throw a ValueError.
Based on the errno tested, it appears that this code
is meant to silently ignore when a socket receives a SIGINT (from
e.g. a timeout.) This seems to be the only instance where handling
EINTR in this manner is desired - though having this bubble up this
far seems odd.
The existing code would also handle any other EINTR, though, which
includes those raised by OSError, WindowsError, and
anything that subclasses EnvironmentError, barring KeyboardError
because it is handled separately. This is a bug as there is already
some use of the signals module elsewhere in CouchPotato.py to trap
SIGINT and SIGTERM outside of system calls, and most of these other
EINTRs should be handled by code lower down the stack.
A default exception handler is also added, so that unhandled
exceptions will be logged, and raised.
13 years ago
|
|
|
except socket.error as (nr, msg):
|
|
|
|
# log when socket receives SIGINT, but continue.
|
|
|
|
# previous code would have skipped over other types of IO errors too.
|
|
|
|
if nr != 4:
|
|
|
|
try:
|
|
|
|
l.log.critical(traceback.format_exc())
|
|
|
|
except:
|
|
|
|
print traceback.format_exc()
|
Default exception handler no longer assumes all remaining exceptions take (errno, string) pairs.
Previously any exception that made it all the way up to the default
exception handler would be expected to take (errno, string) pairs,
as is the python standard for exceptions thrown by system calls.
All exceptions that don't take enough arguments throw a ValueError.
Based on the errno tested, it appears that this code
is meant to silently ignore when a socket receives a SIGINT (from
e.g. a timeout.) This seems to be the only instance where handling
EINTR in this manner is desired - though having this bubble up this
far seems odd.
The existing code would also handle any other EINTR, though, which
includes those raised by OSError, WindowsError, and
anything that subclasses EnvironmentError, barring KeyboardError
because it is handled separately. This is a bug as there is already
some use of the signals module elsewhere in CouchPotato.py to trap
SIGINT and SIGTERM outside of system calls, and most of these other
EINTRs should be handled by code lower down the stack.
A default exception handler is also added, so that unhandled
exceptions will be logged, and raised.
13 years ago
|
|
|
raise
|
|
|
|
except:
|
|
|
|
try:
|
|
|
|
# if this fails we will have two tracebacks
|
|
|
|
# one for failing to log, and one for the exception that got us here.
|
|
|
|
if l:
|
|
|
|
l.log.critical(traceback.format_exc())
|
|
|
|
else:
|
|
|
|
print traceback.format_exc()
|
Default exception handler no longer assumes all remaining exceptions take (errno, string) pairs.
Previously any exception that made it all the way up to the default
exception handler would be expected to take (errno, string) pairs,
as is the python standard for exceptions thrown by system calls.
All exceptions that don't take enough arguments throw a ValueError.
Based on the errno tested, it appears that this code
is meant to silently ignore when a socket receives a SIGINT (from
e.g. a timeout.) This seems to be the only instance where handling
EINTR in this manner is desired - though having this bubble up this
far seems odd.
The existing code would also handle any other EINTR, though, which
includes those raised by OSError, WindowsError, and
anything that subclasses EnvironmentError, barring KeyboardError
because it is handled separately. This is a bug as there is already
some use of the signals module elsewhere in CouchPotato.py to trap
SIGINT and SIGTERM outside of system calls, and most of these other
EINTRs should be handled by code lower down the stack.
A default exception handler is also added, so that unhandled
exceptions will be logged, and raised.
13 years ago
|
|
|
except:
|
|
|
|
print traceback.format_exc()
|
|
|
|
raise
|