mirror of
https://github.com/SickGear/SickGear.git
synced 2024-12-01 00:43:37 +00:00
Fixed start/restart/shutdown issues including any issues with daemonizing.
This commit is contained in:
parent
130daf7d0a
commit
1fc909299d
6 changed files with 359 additions and 165 deletions
160
SickBeard.py
160
SickBeard.py
|
@ -19,7 +19,6 @@
|
||||||
|
|
||||||
# Check needed software dependencies to nudge users to fix their setup
|
# Check needed software dependencies to nudge users to fix their setup
|
||||||
from __future__ import with_statement
|
from __future__ import with_statement
|
||||||
import functools
|
|
||||||
|
|
||||||
import sys
|
import sys
|
||||||
import shutil
|
import shutil
|
||||||
|
@ -66,14 +65,21 @@ from sickbeard.databases.mainDB import MIN_DB_VERSION
|
||||||
from sickbeard.databases.mainDB import MAX_DB_VERSION
|
from sickbeard.databases.mainDB import MAX_DB_VERSION
|
||||||
|
|
||||||
from lib.configobj import ConfigObj
|
from lib.configobj import ConfigObj
|
||||||
|
|
||||||
from tornado.ioloop import IOLoop
|
from tornado.ioloop import IOLoop
|
||||||
|
from daemon import Daemon
|
||||||
|
|
||||||
signal.signal(signal.SIGINT, sickbeard.sig_handler)
|
signal.signal(signal.SIGINT, sickbeard.sig_handler)
|
||||||
signal.signal(signal.SIGTERM, sickbeard.sig_handler)
|
signal.signal(signal.SIGTERM, sickbeard.sig_handler)
|
||||||
|
|
||||||
throwaway = datetime.datetime.strptime('20110101', '%Y%m%d')
|
throwaway = datetime.datetime.strptime('20110101', '%Y%m%d')
|
||||||
|
|
||||||
|
restart = False
|
||||||
|
daemon = None
|
||||||
|
startPort = None
|
||||||
|
forceUpdate = None
|
||||||
|
noLaunch = None
|
||||||
|
web_options = None
|
||||||
|
|
||||||
def loadShowsFromDB():
|
def loadShowsFromDB():
|
||||||
"""
|
"""
|
||||||
Populates the showList with shows from the database
|
Populates the showList with shows from the database
|
||||||
|
@ -91,48 +97,11 @@ def loadShowsFromDB():
|
||||||
sickbeard.showList.append(curShow)
|
sickbeard.showList.append(curShow)
|
||||||
except Exception, e:
|
except Exception, e:
|
||||||
logger.log(
|
logger.log(
|
||||||
u"There was an error creating the show in " + sqlShow["location"] + ": " + str(e).decode('utf-8'),
|
u"There was an error creating the show in " + sqlShow["location"] + ": " + str(e).decode('utf-8'),
|
||||||
logger.ERROR)
|
logger.ERROR)
|
||||||
logger.log(traceback.format_exc(), logger.DEBUG)
|
logger.log(traceback.format_exc(), logger.DEBUG)
|
||||||
|
|
||||||
# TODO: update the existing shows if the showlist has something in it
|
# TODO: update the existing shows if the showlist has something in it
|
||||||
|
|
||||||
def daemonize():
|
|
||||||
try:
|
|
||||||
pid = os.fork()
|
|
||||||
if pid > 0:
|
|
||||||
sys.exit(0)
|
|
||||||
except OSError:
|
|
||||||
print "fork() failed"
|
|
||||||
sys.exit(1)
|
|
||||||
|
|
||||||
os.chdir(sickbeard.PROG_DIR)
|
|
||||||
os.setsid()
|
|
||||||
# Make sure I can read my own files and shut out others
|
|
||||||
prev= os.umask(0)
|
|
||||||
os.umask(prev and int('077',8))
|
|
||||||
|
|
||||||
try:
|
|
||||||
pid = os.fork()
|
|
||||||
if pid > 0:
|
|
||||||
sys.exit(0)
|
|
||||||
except OSError:
|
|
||||||
print "fork() failed"
|
|
||||||
sys.exit(1)
|
|
||||||
|
|
||||||
# Write pid
|
|
||||||
if sickbeard.CREATEPID:
|
|
||||||
pid = str(os.getpid())
|
|
||||||
logger.log(u"Writing PID: " + pid + " to " + str(sickbeard.PIDFILE))
|
|
||||||
try:
|
|
||||||
file(sickbeard.PIDFILE, 'w').write("%s\n" % pid)
|
|
||||||
except IOError, e:
|
|
||||||
logger.log_error_and_exit(
|
|
||||||
u"Unable to write PID file: " + sickbeard.PIDFILE + " Error: " + str(e.strerror) + " [" + str(
|
|
||||||
e.errno) + "]")
|
|
||||||
|
|
||||||
dev_null = file('/dev/null', 'r')
|
|
||||||
os.dup2(dev_null.fileno(), sys.stdin.fileno())
|
|
||||||
|
|
||||||
def restore(srcDir, dstDir):
|
def restore(srcDir, dstDir):
|
||||||
try:
|
try:
|
||||||
|
@ -153,6 +122,8 @@ def main():
|
||||||
TV for me
|
TV for me
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
global daemon, startPort, forceUpdate, noLaunch, web_options
|
||||||
|
|
||||||
# do some preliminary stuff
|
# do some preliminary stuff
|
||||||
sickbeard.MY_FULLNAME = os.path.normpath(os.path.abspath(__file__))
|
sickbeard.MY_FULLNAME = os.path.normpath(os.path.abspath(__file__))
|
||||||
sickbeard.MY_NAME = os.path.basename(sickbeard.MY_FULLNAME)
|
sickbeard.MY_NAME = os.path.basename(sickbeard.MY_FULLNAME)
|
||||||
|
@ -255,7 +226,7 @@ def main():
|
||||||
sys.exit("PID file: " + sickbeard.PIDFILE + " already exists. Exiting.")
|
sys.exit("PID file: " + sickbeard.PIDFILE + " already exists. Exiting.")
|
||||||
|
|
||||||
# The pidfile is only useful in daemon mode, make sure we can write the file properly
|
# The pidfile is only useful in daemon mode, make sure we can write the file properly
|
||||||
if sickbeard.CREATEPID and not sickbeard.restarted:
|
if sickbeard.CREATEPID:
|
||||||
if sickbeard.DAEMON:
|
if sickbeard.DAEMON:
|
||||||
pid_dir = os.path.dirname(sickbeard.PIDFILE)
|
pid_dir = os.path.dirname(sickbeard.PIDFILE)
|
||||||
if not os.access(pid_dir, os.F_OK):
|
if not os.access(pid_dir, os.F_OK):
|
||||||
|
@ -289,7 +260,8 @@ def main():
|
||||||
if os.path.isfile(sickbeard.CONFIG_FILE):
|
if os.path.isfile(sickbeard.CONFIG_FILE):
|
||||||
raise SystemExit("Config file '" + sickbeard.CONFIG_FILE + "' must be writeable.")
|
raise SystemExit("Config file '" + sickbeard.CONFIG_FILE + "' must be writeable.")
|
||||||
elif not os.access(os.path.dirname(sickbeard.CONFIG_FILE), os.W_OK):
|
elif not os.access(os.path.dirname(sickbeard.CONFIG_FILE), os.W_OK):
|
||||||
raise SystemExit("Config file root dir '" + os.path.dirname(sickbeard.CONFIG_FILE) + "' must be writeable.")
|
raise SystemExit(
|
||||||
|
"Config file root dir '" + os.path.dirname(sickbeard.CONFIG_FILE) + "' must be writeable.")
|
||||||
|
|
||||||
# Check if we need to perform a restore first
|
# Check if we need to perform a restore first
|
||||||
restoreDir = os.path.join(sickbeard.DATA_DIR, 'restore')
|
restoreDir = os.path.join(sickbeard.DATA_DIR, 'restore')
|
||||||
|
@ -327,12 +299,6 @@ def main():
|
||||||
# Initialize the config and our threads
|
# Initialize the config and our threads
|
||||||
sickbeard.initialize(consoleLogging=consoleLogging)
|
sickbeard.initialize(consoleLogging=consoleLogging)
|
||||||
|
|
||||||
if sickbeard.DAEMON:
|
|
||||||
daemonize()
|
|
||||||
|
|
||||||
# Use this PID for everything
|
|
||||||
sickbeard.PID = os.getpid()
|
|
||||||
|
|
||||||
if forcedPort:
|
if forcedPort:
|
||||||
logger.log(u"Forcing web server to port " + str(forcedPort))
|
logger.log(u"Forcing web server to port " + str(forcedPort))
|
||||||
startPort = forcedPort
|
startPort = forcedPort
|
||||||
|
@ -354,7 +320,8 @@ def main():
|
||||||
else:
|
else:
|
||||||
webhost = '0.0.0.0'
|
webhost = '0.0.0.0'
|
||||||
|
|
||||||
options = {
|
# web server options
|
||||||
|
web_options = {
|
||||||
'port': int(startPort),
|
'port': int(startPort),
|
||||||
'host': webhost,
|
'host': webhost,
|
||||||
'data_root': os.path.join(sickbeard.PROG_DIR, 'gui', sickbeard.GUI_NAME),
|
'data_root': os.path.join(sickbeard.PROG_DIR, 'gui', sickbeard.GUI_NAME),
|
||||||
|
@ -366,62 +333,71 @@ def main():
|
||||||
'handle_reverse_proxy': sickbeard.HANDLE_REVERSE_PROXY,
|
'handle_reverse_proxy': sickbeard.HANDLE_REVERSE_PROXY,
|
||||||
'https_cert': sickbeard.HTTPS_CERT,
|
'https_cert': sickbeard.HTTPS_CERT,
|
||||||
'https_key': sickbeard.HTTPS_KEY,
|
'https_key': sickbeard.HTTPS_KEY,
|
||||||
}
|
}
|
||||||
|
|
||||||
# init tornado
|
# Start SickRage
|
||||||
try:
|
if daemon and daemon.is_running():
|
||||||
webserveInit.initWebServer(options)
|
daemon.restart(daemonize=sickbeard.DAEMON)
|
||||||
except IOError:
|
else:
|
||||||
logger.log(u"Unable to start web server, is something else running on port %d?" % startPort, logger.ERROR)
|
daemon = SickRage(sickbeard.PIDFILE)
|
||||||
if sickbeard.LAUNCH_BROWSER and not sickbeard.DAEMON:
|
daemon.start(daemonize=sickbeard.DAEMON)
|
||||||
logger.log(u"Launching browser and exiting", logger.ERROR)
|
|
||||||
|
class SickRage(Daemon):
|
||||||
|
def run(self):
|
||||||
|
global restart, startPort, forceUpdate, noLaunch, web_options
|
||||||
|
|
||||||
|
# Use this PID for everything
|
||||||
|
sickbeard.PID = os.getpid()
|
||||||
|
|
||||||
|
try:
|
||||||
|
webserveInit.initWebServer(web_options)
|
||||||
|
except IOError:
|
||||||
|
logger.log(u"Unable to start web server, is something else running on port %d?" % startPort, logger.ERROR)
|
||||||
|
if sickbeard.LAUNCH_BROWSER and not sickbeard.DAEMON:
|
||||||
|
logger.log(u"Launching browser and exiting", logger.ERROR)
|
||||||
|
sickbeard.launchBrowser(startPort)
|
||||||
|
sys.exit()
|
||||||
|
|
||||||
|
# Build from the DB to start with
|
||||||
|
loadShowsFromDB()
|
||||||
|
|
||||||
|
# Fire up all our threads
|
||||||
|
sickbeard.start()
|
||||||
|
|
||||||
|
# Launch browser if we're supposed to
|
||||||
|
if sickbeard.LAUNCH_BROWSER and not noLaunch:
|
||||||
sickbeard.launchBrowser(startPort)
|
sickbeard.launchBrowser(startPort)
|
||||||
sys.exit()
|
|
||||||
|
|
||||||
# Build from the DB to start with
|
# Start an update if we're supposed to
|
||||||
loadShowsFromDB()
|
if forceUpdate or sickbeard.UPDATE_SHOWS_ON_START:
|
||||||
|
sickbeard.showUpdateScheduler.action.run(force=True) # @UndefinedVariable
|
||||||
|
|
||||||
# Fire up all our threads
|
if sickbeard.LAUNCH_BROWSER and not (noLaunch or sickbeard.DAEMON or restart):
|
||||||
sickbeard.start()
|
sickbeard.launchBrowser(startPort)
|
||||||
|
|
||||||
# Launch browser if we're supposed to
|
# reset this if sickrage was restarted
|
||||||
if sickbeard.LAUNCH_BROWSER and not noLaunch:
|
restart = False
|
||||||
sickbeard.launchBrowser(startPort)
|
|
||||||
|
|
||||||
# Start an update if we're supposed to
|
# start IO loop
|
||||||
if forceUpdate or sickbeard.UPDATE_SHOWS_ON_START:
|
IOLoop.current().start()
|
||||||
sickbeard.showUpdateScheduler.action.run(force=True) # @UndefinedVariable
|
|
||||||
|
|
||||||
# If we restarted then unset the restarted flag
|
# close IO loop
|
||||||
if sickbeard.restarted:
|
IOLoop.current().close(True)
|
||||||
sickbeard.restarted = False
|
|
||||||
|
|
||||||
# IOLoop
|
# stop all tasks
|
||||||
io_loop = IOLoop.current()
|
sickbeard.halt()
|
||||||
|
|
||||||
# Open browser window
|
# save all shows to DB
|
||||||
if sickbeard.LAUNCH_BROWSER and not (noLaunch or sickbeard.DAEMON or sickbeard.restarted):
|
sickbeard.saveAll()
|
||||||
io_loop.add_timeout(datetime.timedelta(seconds=5), functools.partial(sickbeard.launchBrowser, startPort))
|
|
||||||
|
|
||||||
# Start web server
|
|
||||||
io_loop.start()
|
|
||||||
|
|
||||||
# Save and restart/shutdown
|
|
||||||
sickbeard.saveAndShutdown()
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
if sys.hexversion >= 0x020600F0:
|
if sys.hexversion >= 0x020600F0:
|
||||||
freeze_support()
|
freeze_support()
|
||||||
|
|
||||||
while(True):
|
while(not sickbeard.shutdown):
|
||||||
main()
|
main()
|
||||||
|
|
||||||
# check if restart was requested
|
logger.log("SickRage is restarting, please stand by ...")
|
||||||
if not sickbeard.restarted:
|
restart = True
|
||||||
if sickbeard.CREATEPID:
|
|
||||||
logger.log(u"Removing pidfile " + str(sickbeard.PIDFILE))
|
|
||||||
sickbeard.remove_pid_file(sickbeard.PIDFILE)
|
|
||||||
break
|
|
||||||
|
|
||||||
# restart
|
logger.log("Goodbye ...")
|
||||||
logger.log("Restarting SickRage, please stand by...")
|
|
|
@ -1,30 +1,55 @@
|
||||||
if (sbHandleReverseProxy != "False" && sbHandleReverseProxy != 0)
|
if (sbHttpsEnabled != "False" && sbHttpsEnabled != 0) {
|
||||||
// Don't add the port to the url if using reverse proxy
|
var sb_base_url = 'https://' + sbHost + ':' + sbHttpPort + sbRoot;
|
||||||
if (sbHttpsEnabled != "False" && sbHttpsEnabled != 0)
|
} else {
|
||||||
var sb_base_url = 'https://'+sbHost+sbRoot;
|
var sb_base_url = 'http://' + sbHost + ':' + sbHttpPort + sbRoot;
|
||||||
else
|
}
|
||||||
var sb_base_url = 'http://'+sbHost+sbRoot;
|
|
||||||
else
|
|
||||||
if (sbHttpsEnabled != "False" && sbHttpsEnabled != 0)
|
|
||||||
var sb_base_url = 'https://'+sbHost+':'+sbHttpPort+sbRoot;
|
|
||||||
else
|
|
||||||
var sb_base_url = 'http://'+sbHost+':'+sbHttpPort+sbRoot;
|
|
||||||
|
|
||||||
var base_url = window.location.protocol+'//'+window.location.host+sbRoot;
|
var base_url = window.location.protocol + '//' + window.location.host + sbRoot;
|
||||||
var is_alive_url = sbRoot+'/home/is_alive';
|
var is_alive_url = sbRoot + '/home/is_alive/';
|
||||||
var timeout_id;
|
var timeout_id;
|
||||||
var restarted = '';
|
var current_pid = '';
|
||||||
var num_restart_waits = 0;
|
var num_restart_waits = 0;
|
||||||
|
|
||||||
function restartHandler() {
|
function is_alive() {
|
||||||
|
timeout_id = 0;
|
||||||
|
$.get(is_alive_url, function(data) {
|
||||||
|
|
||||||
|
// if it's still initalizing then just wait and try again
|
||||||
|
if (data.msg == 'nope') {
|
||||||
|
$('#shut_down_loading').hide();
|
||||||
|
$('#shut_down_success').show();
|
||||||
|
$('#restart_message').show();
|
||||||
|
setTimeout('is_alive()', 1000);
|
||||||
|
} else {
|
||||||
|
// if this is before we've even shut down then just try again later
|
||||||
|
if (current_pid == '' || data.msg == current_pid) {
|
||||||
|
current_pid = data.msg;
|
||||||
|
setTimeout(is_alive, 1000);
|
||||||
|
|
||||||
|
// if we're ready to go then redirect to new url
|
||||||
|
} else {
|
||||||
|
$('#restart_loading').hide();
|
||||||
|
$('#restart_success').show();
|
||||||
|
$('#refresh_message').show();
|
||||||
|
window.location = sb_base_url + '/home/';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, 'jsonp');
|
||||||
|
}
|
||||||
|
|
||||||
|
$(document).ready(function() {
|
||||||
|
|
||||||
|
is_alive();
|
||||||
|
|
||||||
|
$('#shut_down_message').ajaxError(function(e, jqxhr, settings, exception) {
|
||||||
num_restart_waits += 1;
|
num_restart_waits += 1;
|
||||||
|
|
||||||
$('#shut_down_loading').hide();
|
$('#shut_down_loading').hide();
|
||||||
$('#shut_down_success').show();
|
$('#shut_down_success').show();
|
||||||
$('#restart_message').show();
|
$('#restart_message').show();
|
||||||
is_alive_url = sb_base_url+'/home/is_alive';
|
is_alive_url = sb_base_url + '/home/is_alive/';
|
||||||
|
|
||||||
// if https is enabled or you are currently on https and the port or protocol changed just wait 5 seconds then redirect.
|
// if https is enabled or you are currently on https and the port or protocol changed just wait 5 seconds then redirect.
|
||||||
// This is because the ajax will fail if the cert is untrusted or the the http ajax requst from https will fail because of mixed content error.
|
// This is because the ajax will fail if the cert is untrusted or the the http ajax requst from https will fail because of mixed content error.
|
||||||
if ((sbHttpsEnabled != "False" && sbHttpsEnabled != 0) || window.location.protocol == "https:") {
|
if ((sbHttpsEnabled != "False" && sbHttpsEnabled != 0) || window.location.protocol == "https:") {
|
||||||
if (base_url != sb_base_url) {
|
if (base_url != sb_base_url) {
|
||||||
|
@ -34,16 +59,8 @@ function restartHandler() {
|
||||||
$('#restart_success').show();
|
$('#restart_success').show();
|
||||||
$('#refresh_message').show();
|
$('#refresh_message').show();
|
||||||
}, 3000);
|
}, 3000);
|
||||||
setTimeout("window.location = sb_base_url+'/home/'", 5000);
|
setTimeout("window.location = sb_base_url + '/home/'", 5000);
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
timeout_id = 1;
|
|
||||||
setTimeout(function(){
|
|
||||||
$('#restart_loading').hide();
|
|
||||||
$('#restart_success').show();
|
|
||||||
$('#refresh_message').show();
|
|
||||||
}, 3000);
|
|
||||||
setTimeout("window.location = sb_base_url+'/home/'", 5000);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// if it is taking forever just give up
|
// if it is taking forever just give up
|
||||||
|
@ -54,34 +71,9 @@ function restartHandler() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (timeout_id == 0)
|
if (timeout_id == 0) {
|
||||||
timeout_id = setTimeout('is_alive()', 1000);
|
timeout_id = setTimeout('is_alive()', 1000);
|
||||||
}
|
|
||||||
|
|
||||||
function is_alive() {
|
|
||||||
timeout_id = 0;
|
|
||||||
|
|
||||||
$.get(is_alive_url, function(data) {
|
|
||||||
|
|
||||||
// if it's still initalizing then just wait and try again
|
|
||||||
if (data.msg == 'nope') {
|
|
||||||
$('#shut_down_loading').hide();
|
|
||||||
$('#shut_down_success').show();
|
|
||||||
$('#restart_message').show();
|
|
||||||
setTimeout('is_alive()', 1000);
|
|
||||||
} else if (data.restarted == 'True') {
|
|
||||||
restartHandler();
|
|
||||||
} else {
|
|
||||||
// if this is before we've even shut down then just try again later
|
|
||||||
if (restarted == '' || data.restarted == restarted) {
|
|
||||||
restarted = data.restarted;
|
|
||||||
setTimeout(is_alive, 1000);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}, 'jsonp');
|
});
|
||||||
}
|
|
||||||
|
|
||||||
$(document).ready(function()
|
});
|
||||||
{
|
|
||||||
is_alive();
|
|
||||||
});
|
|
216
lib/daemon.py
Normal file
216
lib/daemon.py
Normal file
|
@ -0,0 +1,216 @@
|
||||||
|
'''
|
||||||
|
***
|
||||||
|
Modified generic daemon class
|
||||||
|
***
|
||||||
|
|
||||||
|
Author: http://www.jejik.com/articles/2007/02/
|
||||||
|
a_simple_unix_linux_daemon_in_python/www.boxedice.com
|
||||||
|
|
||||||
|
License: http://creativecommons.org/licenses/by-sa/3.0/
|
||||||
|
|
||||||
|
Changes: 23rd Jan 2009 (David Mytton <david@boxedice.com>)
|
||||||
|
- Replaced hard coded '/dev/null in __init__ with os.devnull
|
||||||
|
- Added OS check to conditionally remove code that doesn't
|
||||||
|
work on OS X
|
||||||
|
- Added output to console on completion
|
||||||
|
- Tidied up formatting
|
||||||
|
11th Mar 2009 (David Mytton <david@boxedice.com>)
|
||||||
|
- Fixed problem with daemon exiting on Python 2.4
|
||||||
|
(before SystemExit was part of the Exception base)
|
||||||
|
13th Aug 2010 (David Mytton <david@boxedice.com>
|
||||||
|
- Fixed unhandled exception if PID file is empty
|
||||||
|
'''
|
||||||
|
|
||||||
|
# Core modules
|
||||||
|
import atexit
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
import time
|
||||||
|
import signal
|
||||||
|
|
||||||
|
|
||||||
|
class Daemon(object):
|
||||||
|
"""
|
||||||
|
A generic daemon class.
|
||||||
|
|
||||||
|
Usage: subclass the Daemon class and override the run() method
|
||||||
|
"""
|
||||||
|
def __init__(self, pidfile, stdin=os.devnull,
|
||||||
|
stdout=os.devnull, stderr=os.devnull,
|
||||||
|
home_dir='.', umask=022, verbose=1):
|
||||||
|
self.stdin = stdin
|
||||||
|
self.stdout = stdout
|
||||||
|
self.stderr = stderr
|
||||||
|
self.pidfile = pidfile
|
||||||
|
self.home_dir = home_dir
|
||||||
|
self.verbose = verbose
|
||||||
|
self.umask = umask
|
||||||
|
self.daemon_alive = True
|
||||||
|
|
||||||
|
def daemonize(self):
|
||||||
|
"""
|
||||||
|
Do the UNIX double-fork magic, see Stevens' "Advanced
|
||||||
|
Programming in the UNIX Environment" for details (ISBN 0201563177)
|
||||||
|
http://www.erlenstar.demon.co.uk/unix/faq_2.html#SEC16
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
pid = os.fork()
|
||||||
|
if pid > 0:
|
||||||
|
# Exit first parent
|
||||||
|
sys.exit(0)
|
||||||
|
except OSError, e:
|
||||||
|
sys.stderr.write(
|
||||||
|
"fork #1 failed: %d (%s)\n" % (e.errno, e.strerror))
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
# Decouple from parent environment
|
||||||
|
os.chdir(self.home_dir)
|
||||||
|
os.setsid()
|
||||||
|
os.umask(self.umask)
|
||||||
|
|
||||||
|
# Do second fork
|
||||||
|
try:
|
||||||
|
pid = os.fork()
|
||||||
|
if pid > 0:
|
||||||
|
# Exit from second parent
|
||||||
|
sys.exit(0)
|
||||||
|
except OSError, e:
|
||||||
|
sys.stderr.write(
|
||||||
|
"fork #2 failed: %d (%s)\n" % (e.errno, e.strerror))
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
if sys.platform != 'darwin': # This block breaks on OS X
|
||||||
|
# Redirect standard file descriptors
|
||||||
|
sys.stdout.flush()
|
||||||
|
sys.stderr.flush()
|
||||||
|
si = file(self.stdin, 'r')
|
||||||
|
so = file(self.stdout, 'a+')
|
||||||
|
if self.stderr:
|
||||||
|
se = file(self.stderr, 'a+', 0)
|
||||||
|
else:
|
||||||
|
se = so
|
||||||
|
os.dup2(si.fileno(), sys.stdin.fileno())
|
||||||
|
os.dup2(so.fileno(), sys.stdout.fileno())
|
||||||
|
os.dup2(se.fileno(), sys.stderr.fileno())
|
||||||
|
|
||||||
|
def sigtermhandler(signum, frame):
|
||||||
|
self.daemon_alive = False
|
||||||
|
signal.signal(signal.SIGTERM, sigtermhandler)
|
||||||
|
signal.signal(signal.SIGINT, sigtermhandler)
|
||||||
|
|
||||||
|
if self.verbose >= 1:
|
||||||
|
print "Started"
|
||||||
|
|
||||||
|
# Write pidfile
|
||||||
|
atexit.register(
|
||||||
|
self.delpid) # Make sure pid file is removed if we quit
|
||||||
|
pid = str(os.getpid())
|
||||||
|
file(self.pidfile, 'w+').write("%s\n" % pid)
|
||||||
|
|
||||||
|
def delpid(self):
|
||||||
|
os.remove(self.pidfile)
|
||||||
|
|
||||||
|
def start(self, daemonize=True, *args, **kwargs):
|
||||||
|
"""
|
||||||
|
Start the daemon
|
||||||
|
"""
|
||||||
|
|
||||||
|
if daemonize:
|
||||||
|
if self.verbose >= 1:
|
||||||
|
print "Starting..."
|
||||||
|
|
||||||
|
# Check for a pidfile to see if the daemon already runs
|
||||||
|
try:
|
||||||
|
pf = file(self.pidfile, 'r')
|
||||||
|
pid = int(pf.read().strip())
|
||||||
|
pf.close()
|
||||||
|
except IOError:
|
||||||
|
pid = None
|
||||||
|
except SystemExit:
|
||||||
|
pid = None
|
||||||
|
|
||||||
|
if pid:
|
||||||
|
message = "pidfile %s already exists. Is it already running?\n"
|
||||||
|
sys.stderr.write(message % self.pidfile)
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
# Start the daemon
|
||||||
|
self.daemonize()
|
||||||
|
|
||||||
|
self.run(*args, **kwargs)
|
||||||
|
|
||||||
|
def stop(self, daemonize=True):
|
||||||
|
"""
|
||||||
|
Stop the daemon
|
||||||
|
"""
|
||||||
|
|
||||||
|
if not daemonize:
|
||||||
|
return
|
||||||
|
|
||||||
|
if self.verbose >= 1:
|
||||||
|
print "Stopping..."
|
||||||
|
|
||||||
|
# Get the pid from the pidfile
|
||||||
|
pid = self.get_pid()
|
||||||
|
|
||||||
|
if not pid:
|
||||||
|
message = "pidfile %s does not exist. Not running?\n"
|
||||||
|
sys.stderr.write(message % self.pidfile)
|
||||||
|
|
||||||
|
# Just to be sure. A ValueError might occur if the PID file is
|
||||||
|
# empty but does actually exist
|
||||||
|
if os.path.exists(self.pidfile):
|
||||||
|
os.remove(self.pidfile)
|
||||||
|
|
||||||
|
return # Not an error in a restart
|
||||||
|
|
||||||
|
# Try killing the daemon process
|
||||||
|
try:
|
||||||
|
i = 0
|
||||||
|
while 1:
|
||||||
|
os.kill(pid, signal.SIGTERM)
|
||||||
|
time.sleep(0.1)
|
||||||
|
i = i + 1
|
||||||
|
if i % 10 == 0:
|
||||||
|
os.kill(pid, signal.SIGHUP)
|
||||||
|
except OSError, err:
|
||||||
|
err = str(err)
|
||||||
|
if err.find("No such process") > 0:
|
||||||
|
if os.path.exists(self.pidfile):
|
||||||
|
os.remove(self.pidfile)
|
||||||
|
else:
|
||||||
|
print str(err)
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
if self.verbose >= 1:
|
||||||
|
print "Stopped"
|
||||||
|
|
||||||
|
def restart(self, daemonize=True):
|
||||||
|
"""
|
||||||
|
Restart the daemon
|
||||||
|
"""
|
||||||
|
self.stop(daemonize=daemonize)
|
||||||
|
self.start(daemonize=daemonize)
|
||||||
|
|
||||||
|
def get_pid(self):
|
||||||
|
try:
|
||||||
|
pf = file(self.pidfile, 'r')
|
||||||
|
pid = int(pf.read().strip())
|
||||||
|
pf.close()
|
||||||
|
except IOError:
|
||||||
|
pid = None
|
||||||
|
except SystemExit:
|
||||||
|
pid = None
|
||||||
|
return pid
|
||||||
|
|
||||||
|
def is_running(self):
|
||||||
|
pid = self.get_pid()
|
||||||
|
print(pid)
|
||||||
|
return pid and os.path.exists('/proc/%d' % pid)
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
"""
|
||||||
|
You should override this method when you subclass Daemon.
|
||||||
|
It will be called after the process has been
|
||||||
|
daemonized by start() or restart().
|
||||||
|
"""
|
|
@ -19,7 +19,6 @@
|
||||||
from __future__ import with_statement
|
from __future__ import with_statement
|
||||||
|
|
||||||
import webbrowser
|
import webbrowser
|
||||||
import time
|
|
||||||
import datetime
|
import datetime
|
||||||
import socket
|
import socket
|
||||||
import os
|
import os
|
||||||
|
@ -104,7 +103,7 @@ CUR_COMMIT_HASH = None
|
||||||
|
|
||||||
INIT_LOCK = Lock()
|
INIT_LOCK = Lock()
|
||||||
started = False
|
started = False
|
||||||
restarted = False
|
shutdown = False
|
||||||
|
|
||||||
ACTUAL_LOG_DIR = None
|
ACTUAL_LOG_DIR = None
|
||||||
LOG_DIR = None
|
LOG_DIR = None
|
||||||
|
@ -1284,9 +1283,12 @@ def remove_pid_file(PIDFILE):
|
||||||
|
|
||||||
|
|
||||||
def sig_handler(signum=None, frame=None):
|
def sig_handler(signum=None, frame=None):
|
||||||
|
global shutdown
|
||||||
|
|
||||||
if type(signum) != type(None):
|
if type(signum) != type(None):
|
||||||
logger.log(u"Signal %i caught, saving and exiting..." % int(signum))
|
logger.log(u"Signal %i caught, saving and exiting..." % int(signum))
|
||||||
webserveInit.shutdown()
|
shutdown = True
|
||||||
|
IOLoop.current().stop()
|
||||||
|
|
||||||
def saveAll():
|
def saveAll():
|
||||||
global showList
|
global showList
|
||||||
|
@ -1300,10 +1302,24 @@ def saveAll():
|
||||||
logger.log(u"Saving config file to disk")
|
logger.log(u"Saving config file to disk")
|
||||||
save_config()
|
save_config()
|
||||||
|
|
||||||
def saveAndShutdown():
|
def saveAndShutdown(restart=False):
|
||||||
|
global shutdown
|
||||||
|
|
||||||
|
if not restart:
|
||||||
|
shutdown = True
|
||||||
|
|
||||||
|
# stop tornado web server
|
||||||
|
webserveInit.server.stop()
|
||||||
|
|
||||||
|
# stop all tasks
|
||||||
halt()
|
halt()
|
||||||
|
|
||||||
|
# save all shows to db
|
||||||
saveAll()
|
saveAll()
|
||||||
|
|
||||||
|
#stop tornado io loop
|
||||||
|
IOLoop.current().stop()
|
||||||
|
|
||||||
def invoke_command(to_call, *args, **kwargs):
|
def invoke_command(to_call, *args, **kwargs):
|
||||||
|
|
||||||
def delegate():
|
def delegate():
|
||||||
|
@ -1319,21 +1335,18 @@ def invoke_restart(soft=True):
|
||||||
|
|
||||||
|
|
||||||
def invoke_shutdown():
|
def invoke_shutdown():
|
||||||
invoke_command(webserveInit.shutdown)
|
global shutdown
|
||||||
|
shutdown = True
|
||||||
|
invoke_command(IOLoop.current().stop)
|
||||||
|
|
||||||
def restart(soft=True):
|
def restart(soft=True):
|
||||||
global restarted
|
|
||||||
|
|
||||||
if soft:
|
if soft:
|
||||||
halt()
|
halt()
|
||||||
saveAll()
|
saveAll()
|
||||||
logger.log(u"Re-initializing all data")
|
logger.log(u"Re-initializing all data")
|
||||||
initialize()
|
initialize()
|
||||||
else:
|
else:
|
||||||
restarted=True
|
IOLoop.current().stop()
|
||||||
time.sleep(5)
|
|
||||||
webserveInit.shutdown()
|
|
||||||
|
|
||||||
|
|
||||||
def save_config():
|
def save_config():
|
||||||
|
|
|
@ -3099,9 +3099,9 @@ class Home(MainHandler):
|
||||||
|
|
||||||
if sickbeard.started:
|
if sickbeard.started:
|
||||||
return callback + '(' + json.dumps(
|
return callback + '(' + json.dumps(
|
||||||
{"msg": str(sickbeard.PID), "restarted": str(sickbeard.restarted)}) + ');'
|
{"msg": str(sickbeard.PID)}) + ');'
|
||||||
else:
|
else:
|
||||||
return callback + '(' + json.dumps({"msg": "nope", "restarted": str(sickbeard.restarted)}) + ');'
|
return callback + '(' + json.dumps({"msg": "nope"}) + ');'
|
||||||
|
|
||||||
|
|
||||||
def index(self, *args, **kwargs):
|
def index(self, *args, **kwargs):
|
||||||
|
@ -3424,7 +3424,6 @@ class Home(MainHandler):
|
||||||
|
|
||||||
return _munge(t)
|
return _munge(t)
|
||||||
|
|
||||||
|
|
||||||
def update(self, pid=None):
|
def update(self, pid=None):
|
||||||
|
|
||||||
if str(pid) != str(sickbeard.PID):
|
if str(pid) != str(sickbeard.PID):
|
||||||
|
|
|
@ -105,16 +105,14 @@ def initWebServer(options={}):
|
||||||
logger.log(u"Starting SickRage on " + protocol + "://" + str(options['host']) + ":" + str(
|
logger.log(u"Starting SickRage on " + protocol + "://" + str(options['host']) + ":" + str(
|
||||||
options['port']) + "/")
|
options['port']) + "/")
|
||||||
|
|
||||||
if not sickbeard.restarted:
|
server.listen(options['port'], options['host'])
|
||||||
server.listen(options['port'], options['host'])
|
|
||||||
|
|
||||||
def shutdown():
|
def shutdown():
|
||||||
global server
|
|
||||||
|
|
||||||
logger.log('Shutting down tornado io loop')
|
logger.log('Shutting down tornado IO loop')
|
||||||
try:
|
try:
|
||||||
IOLoop.current().stop()
|
IOLoop.current().stop()
|
||||||
except RuntimeError:
|
except RuntimeError:
|
||||||
pass
|
pass
|
||||||
except:
|
except:
|
||||||
logger.log('Failed shutting down tornado io loop: %s' % traceback.format_exc(), logger.ERROR)
|
logger.log('Failed shutting down tornado IO loop: %s' % traceback.format_exc(), logger.ERROR)
|
Loading…
Reference in a new issue