Merged in brunnels updated initscript/pidfile handling & adehub fix in base.html

This commit is contained in:
rembo10
2012-12-25 01:01:50 -05:00
6 changed files with 451 additions and 273 deletions
+4
View File
@@ -51,3 +51,7 @@ obj/
[Rr]elease*/ [Rr]elease*/
_ReSharper*/ _ReSharper*/
[Tt]est[Rr]esult* [Tt]est[Rr]esult*
/cache
/logs
.project
.pydevproject
+26 -4
View File
@@ -16,6 +16,7 @@
import os, sys, locale import os, sys, locale
import time import time
import signal
from lib.configobj import ConfigObj from lib.configobj import ConfigObj
@@ -28,6 +29,9 @@ try:
except ImportError: except ImportError:
import lib.argparse as argparse import lib.argparse as argparse
signal.signal(signal.SIGINT, headphones.sig_handler)
signal.signal(signal.SIGTERM, headphones.sig_handler)
def main(): def main():
@@ -74,10 +78,28 @@ def main():
headphones.VERBOSE = 0 headphones.VERBOSE = 0
if args.daemon: if args.daemon:
headphones.DAEMON=True if sys.platform == 'win32':
headphones.VERBOSE = 0 print "Daemonize not supported under Windows, starting normally"
if args.pidfile : else:
headphones.PIDFILE = args.pidfile 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: if args.datadir:
headphones.DATA_DIR = args.datadir headphones.DATA_DIR = args.datadir
+1 -1
View File
@@ -36,7 +36,7 @@
</div> </div>
% elif headphones.CURRENT_VERSION != headphones.LATEST_VERSION and headphones.INSTALL_TYPE != 'win': % elif headphones.CURRENT_VERSION != headphones.LATEST_VERSION and headphones.INSTALL_TYPE != 'win':
<div id="updatebar"> <div id="updatebar">
A <a href="https://github.com/rembo10/headphones/compare/${headphones.CURRENT_VERSION}...${headphones.LATEST_VERSION}"> newer version</a> is available. You're ${headphones.COMMITS_BEHIND} commits behind. <a href="update">Update</a> or <a href="#" onclick="$('#updatebar').slideUp('slow');">Close</a> A <a href="https://github.com/${headphones.GIT_USER}/headphones/compare/${headphones.CURRENT_VERSION}...${headphones.LATEST_VERSION}"> newer version</a> is available. You're ${headphones.COMMITS_BEHIND} commits behind. <a href="update">Update</a> or <a href="#" onclick="$('#updatebar').slideUp('slow');">Close</a>
</div> </div>
% endif % endif
+36 -23
View File
@@ -41,6 +41,7 @@ SYS_ENCODING = None
VERBOSE = 1 VERBOSE = 1
DAEMON = False DAEMON = False
CREATEPID = False
PIDFILE= None PIDFILE= None
SCHED = Scheduler() SCHED = Scheduler()
@@ -74,6 +75,8 @@ API_ENABLED = False
API_KEY = None API_KEY = None
GIT_PATH = None GIT_PATH = None
GIT_USER = None
GIT_BRANCH =None
INSTALL_TYPE = None INSTALL_TYPE = None
CURRENT_VERSION = None CURRENT_VERSION = None
LATEST_VERSION = None LATEST_VERSION = None
@@ -264,7 +267,7 @@ def initialize():
with INIT_LOCK: with INIT_LOCK:
global __INITIALIZED__, FULL_PATH, PROG_DIR, VERBOSE, DAEMON, SYS_PLATFORM, DATA_DIR, CONFIG_FILE, CFG, CONFIG_VERSION, LOG_DIR, CACHE_DIR, \ global __INITIALIZED__, FULL_PATH, PROG_DIR, VERBOSE, DAEMON, SYS_PLATFORM, DATA_DIR, CONFIG_FILE, CFG, CONFIG_VERSION, LOG_DIR, CACHE_DIR, \
HTTP_PORT, HTTP_HOST, HTTP_USERNAME, HTTP_PASSWORD, HTTP_ROOT, HTTP_PROXY, LAUNCH_BROWSER, API_ENABLED, API_KEY, GIT_PATH, \ HTTP_PORT, HTTP_HOST, HTTP_USERNAME, HTTP_PASSWORD, HTTP_ROOT, HTTP_PROXY, LAUNCH_BROWSER, API_ENABLED, API_KEY, GIT_PATH, GIT_USER, GIT_BRANCH, \
CURRENT_VERSION, LATEST_VERSION, CHECK_GITHUB, CHECK_GITHUB_ON_STARTUP, CHECK_GITHUB_INTERVAL, MUSIC_DIR, DESTINATION_DIR, \ CURRENT_VERSION, LATEST_VERSION, CHECK_GITHUB, CHECK_GITHUB_ON_STARTUP, CHECK_GITHUB_INTERVAL, MUSIC_DIR, DESTINATION_DIR, \
LOSSLESS_DESTINATION_DIR, PREFERRED_QUALITY, PREFERRED_BITRATE, DETECT_BITRATE, ADD_ARTISTS, CORRECT_METADATA, MOVE_FILES, \ LOSSLESS_DESTINATION_DIR, PREFERRED_QUALITY, PREFERRED_BITRATE, DETECT_BITRATE, ADD_ARTISTS, CORRECT_METADATA, MOVE_FILES, \
RENAME_FILES, FOLDER_FORMAT, FILE_FORMAT, CLEANUP_FILES, INCLUDE_EXTRAS, EXTRAS, AUTOWANT_UPCOMING, AUTOWANT_ALL, KEEP_TORRENT_FILES, \ RENAME_FILES, FOLDER_FORMAT, FILE_FORMAT, CLEANUP_FILES, INCLUDE_EXTRAS, EXTRAS, AUTOWANT_UPCOMING, AUTOWANT_ALL, KEEP_TORRENT_FILES, \
@@ -322,6 +325,8 @@ def initialize():
API_ENABLED = bool(check_setting_int(CFG, 'General', 'api_enabled', 0)) API_ENABLED = bool(check_setting_int(CFG, 'General', 'api_enabled', 0))
API_KEY = check_setting_str(CFG, 'General', 'api_key', '') API_KEY = check_setting_str(CFG, 'General', 'api_key', '')
GIT_PATH = check_setting_str(CFG, 'General', 'git_path', '') GIT_PATH = check_setting_str(CFG, 'General', 'git_path', '')
GIT_USER = check_setting_str(CFG, 'General', 'git_user', 'rembo10')
GIT_BRANCH = check_setting_str(CFG, 'General', 'git_branch', 'master')
LOG_DIR = check_setting_str(CFG, 'General', 'log_dir', '') LOG_DIR = check_setting_str(CFG, 'General', 'log_dir', '')
CACHE_DIR = check_setting_str(CFG, 'General', 'cache_dir', '') CACHE_DIR = check_setting_str(CFG, 'General', 'cache_dir', '')
@@ -575,29 +580,28 @@ def daemonize():
# Do first fork # Do first fork
try: try:
pid = os.fork() pid = os.fork() # @UndefinedVariable - only available in UNIX
if pid == 0: if pid != 0:
pass sys.exit(0)
else:
# Exit the parent process
logger.debug('Forking once...')
os._exit(0)
except OSError, e: 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() os.setsid()
# Do second fork # Make sure I can read my own files and shut out others
try: prev = os.umask(0) # @UndefinedVariable - only available in UNIX
pid = os.fork() os.umask(prev and int('077', 8))
if pid > 0:
logger.debug('Forking twice...')
os._exit(0) # Exit second parent process
except OSError, e:
sys.exit("2nd fork failed: %s [%d]" % (e.strerror, e.errno))
os.chdir("/") # Make the child a session-leader by detaching from the terminal
os.umask(0) try:
pid = os.fork() # @UndefinedVariable - only available in UNIX
if pid != 0:
sys.exit(0)
except OSError, e:
raise RuntimeError("2nd fork failed: %s [%d]" % (e.strerror, e.errno))
dev_null = file('/dev/null', 'r')
os.dup2(dev_null.fileno(), sys.stdin.fileno())
si = open('/dev/null', "r") si = open('/dev/null', "r")
so = open('/dev/null', "a+") so = open('/dev/null', "a+")
@@ -607,10 +611,11 @@ def daemonize():
os.dup2(so.fileno(), sys.stdout.fileno()) os.dup2(so.fileno(), sys.stdout.fileno())
os.dup2(se.fileno(), sys.stderr.fileno()) os.dup2(se.fileno(), sys.stderr.fileno())
pid = os.getpid() pid = str(os.getpid())
logger.info('Daemonized to PID: %s' % pid) 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) file(PIDFILE, 'w').write("%s\n" % pid)
def launch_browser(host, port, root): def launch_browser(host, port, root):
@@ -642,6 +647,8 @@ def config_write():
new_config['General']['log_dir'] = LOG_DIR new_config['General']['log_dir'] = LOG_DIR
new_config['General']['cache_dir'] = CACHE_DIR new_config['General']['cache_dir'] = CACHE_DIR
new_config['General']['git_path'] = GIT_PATH new_config['General']['git_path'] = GIT_PATH
new_config['General']['git_user'] = GIT_USER
new_config['General']['git_branch'] = GIT_BRANCH
new_config['General']['check_github'] = int(CHECK_GITHUB) new_config['General']['check_github'] = int(CHECK_GITHUB)
new_config['General']['check_github_on_startup'] = int(CHECK_GITHUB_ON_STARTUP) new_config['General']['check_github_on_startup'] = int(CHECK_GITHUB_ON_STARTUP)
@@ -824,6 +831,11 @@ def start():
started = True 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(): def dbcheck():
conn=sqlite3.connect(DB_FILE) conn=sqlite3.connect(DB_FILE)
@@ -1020,6 +1032,7 @@ def shutdown(restart=False, update=False):
if not restart and not update: if not restart and not update:
logger.info('Headphones is shutting down...') logger.info('Headphones is shutting down...')
if update: if update:
logger.info('Headphones is updating...') logger.info('Headphones is updating...')
try: try:
@@ -1027,7 +1040,7 @@ def shutdown(restart=False, update=False):
except Exception, e: except Exception, e:
logger.warn('Headphones failed to update: %s. Restarting.' % e) logger.warn('Headphones failed to update: %s. Restarting.' % e)
if PIDFILE : if CREATEPID :
logger.info ('Removing pidfile %s' % PIDFILE) logger.info ('Removing pidfile %s' % PIDFILE)
os.remove(PIDFILE) os.remove(PIDFILE)
+7 -9
View File
@@ -17,12 +17,10 @@ import platform, subprocess, re, os, urllib2, tarfile
import headphones import headphones
from headphones import logger, version from headphones import logger, version
from headphones.exceptions import ex
import lib.simplejson as simplejson import lib.simplejson as simplejson
user = "rembo10"
branch = "master"
def runGit(args): def runGit(args):
if headphones.GIT_PATH: if headphones.GIT_PATH:
@@ -107,8 +105,8 @@ def getVersion():
def checkGithub(): def checkGithub():
# Get the latest commit available from github # Get the latest commit available from github
url = 'https://api.github.com/repos/%s/headphones/commits/%s' % (user, branch) url = 'https://api.github.com/repos/%s/headphones/commits/%s' % (headphones.GIT_USER, headphones.GIT_BRANCH)
logger.info ('Retrieving latest version information from github') logger.info('Retrieving latest version information from github')
try: try:
result = urllib2.urlopen(url).read() result = urllib2.urlopen(url).read()
git = simplejson.JSONDecoder().decode(result) git = simplejson.JSONDecoder().decode(result)
@@ -121,7 +119,7 @@ def checkGithub():
# See how many commits behind we are # See how many commits behind we are
if headphones.CURRENT_VERSION: if headphones.CURRENT_VERSION:
logger.info('Comparing currently installed version with latest github version') logger.info('Comparing currently installed version with latest github version')
url = 'https://api.github.com/repos/%s/headphones/compare/%s...%s' % (user, headphones.CURRENT_VERSION, headphones.LATEST_VERSION) url = 'https://api.github.com/repos/%s/headphones/compare/%s...%s' % (headphones.GIT_USER, headphones.CURRENT_VERSION, headphones.LATEST_VERSION)
try: try:
result = urllib2.urlopen(url).read() result = urllib2.urlopen(url).read()
@@ -171,7 +169,7 @@ def update():
else: else:
tar_download_url = 'https://github.com/%s/headphones/tarball/%s' % (user, branch) tar_download_url = 'https://github.com/%s/headphones/tarball/%s' % (headphones.GIT_USER, headphones.GIT_BRANCH)
update_dir = os.path.join(headphones.PROG_DIR, 'update') update_dir = os.path.join(headphones.PROG_DIR, 'update')
version_path = os.path.join(headphones.PROG_DIR, 'version.txt') version_path = os.path.join(headphones.PROG_DIR, 'version.txt')
@@ -204,7 +202,7 @@ def update():
# Find update dir name # Find update dir name
update_dir_contents = [x for x in os.listdir(update_dir) if os.path.isdir(os.path.join(update_dir, x))] update_dir_contents = [x for x in os.listdir(update_dir) if os.path.isdir(os.path.join(update_dir, x))]
if len(update_dir_contents) != 1: if len(update_dir_contents) != 1:
logger.error(u"Invalid update data, update failed: "+str(update_dir_contents)) logger.error("Invalid update data, update failed: "+str(update_dir_contents))
return return
content_dir = os.path.join(update_dir, update_dir_contents[0]) content_dir = os.path.join(update_dir, update_dir_contents[0])
@@ -225,5 +223,5 @@ def update():
ver_file.write(headphones.LATEST_VERSION) ver_file.write(headphones.LATEST_VERSION)
ver_file.close() ver_file.close()
except IOError, e: except IOError, e:
logger.error(u"Unable to write current version to version.txt, update not complete: "+ex(e)) logger.error("Unable to write current version to version.txt, update not complete: "+ex(e))
return return
Regular → Executable
+175 -34
View File
@@ -1,5 +1,44 @@
#! /bin/sh #!/bin/sh
#
## Don't edit this file
## Edit user configuation in /etc/default/headphones to change
##
## Make sure init script is executable
## sudo chmod +x /opt/headphones/init.ubuntu
##
## Install the init script
## sudo ln -s /opt/headphones/init.ubuntu /etc/init.d/headphones
##
## Create the headphones daemon user:
## sudo adduser --system --no-create-home headphones
##
## Make sure /opt/headphones is owned by the headphones user
## sudo chown headphones:nogroup -R /opt/headphones
##
## Touch the default file to stop the warning message when starting
## sudo touch /etc/default/headphones
##
## To start Headphones automatically
## sudo update-rc.d headphones defaults
##
## To start/stop/restart Headphones
## sudo service headphones start
## sudo service headphones stop
## sudo service headphones restart
##
## HP_USER= #$RUN_AS, username to run headphones under, the default is headphones
## HP_HOME= #$APP_PATH, the location of Headphones.py, the default is /opt/headphones
## HP_DATA= #$DATA_DIR, the location of headphones.db, cache, logs, the default is /opt/headphones
## HP_PIDFILE= #$PID_FILE, the location of headphones.pid, the default is /var/run/headphones/headphones.pid
## PYTHON_BIN= #$DAEMON, the location of the python binary, the default is /usr/bin/python
## HP_OPTS= #$EXTRA_DAEMON_OPTS, extra cli option for headphones, i.e. " --config=/home/headphones/config.ini"
## SSD_OPTS= #$EXTRA_SSD_OPTS, extra start-stop-daemon option like " --group=users"
## HP_PORT= #$PORT_OPTS, hardcoded port for the webserver, overrides value in config.ini
##
## EXAMPLE if want to run as different user
## add HP_USER=username to /etc/default/headphones
## otherwise default headphones is used
#
### BEGIN INIT INFO ### BEGIN INIT INFO
# Provides: headphones # Provides: headphones
# Required-Start: $local_fs $network $remote_fs # Required-Start: $local_fs $network $remote_fs
@@ -12,50 +51,152 @@
# Description: starts instance of Headphones using start-stop-daemon # Description: starts instance of Headphones using start-stop-daemon
### END INIT INFO ### END INIT INFO
############### EDIT ME ################## # Script name
# path to app
APP_PATH=/usr/local/sbin/headphones
# path to python bin
DAEMON=/usr/bin/python
# startup args
DAEMON_OPTS=" Headphones.py -q"
# script name
NAME=headphones NAME=headphones
# app name # App name
DESC=headphones DESC=Headphones
# user SETTINGS_LOADED=FALSE
RUN_AS=root
PID_FILE=/var/run/headphones.pid . /lib/lsb/init-functions
############### END EDIT ME ################## # Source Headphones configuration
if [ -f /etc/default/headphones ]; then
SETTINGS=/etc/default/headphones
else
log_warning_msg "/etc/default/headphones not found using default settings.";
fi
test -x $DAEMON || exit 0 check_retval() {
if [ $? -eq 0 ]; then
log_end_msg 0
return 0
else
log_end_msg 1
exit 1
fi
}
set -e load_settings() {
if [ $SETTINGS_LOADED != "TRUE" ]; then
. $SETTINGS
## The defaults
# Run as username
RUN_AS=${HP_USER-headphones}
# Path to app HP_HOME=path_to_app_Headphones.py
APP_PATH=${HP_HOME-/opt/headphones}
# Data directory where headphones.db, cache and logs are stored
DATA_DIR=${HP_DATA-/opt/headphones}
# Path to store PID file
PID_FILE=${HP_PIDFILE-/var/run/headphones/headphones.pid}
# Path to python bin
DAEMON=${PYTHON_BIN-/usr/bin/python}
# Extra daemon option like: HP_OPTS=" --config=/home/headphones/config.ini"
EXTRA_DAEMON_OPTS=${HP_OPTS-}
# Extra start-stop-daemon option like START_OPTS=" --group=users"
EXTRA_SSD_OPTS=${SSD_OPTS-}
# Hardcoded port to run on, overrides config.ini settings
[ -n "$HP_PORT" ] && {
PORT_OPTS=" --port=${HP_PORT} "
}
DAEMON_OPTS=" Headphones.py --quiet --daemon --nolaunch --pidfile=${PID_FILE} --datadir=${DATA_DIR} ${PORT_OPTS}${EXTRA_DAEMON_OPTS}"
SETTINGS_LOADED=TRUE
fi
[ -x $DAEMON ] || {
log_warning_msg "$DESC: Can't execute daemon, aborting. See $DAEMON";
return 1;}
return 0
}
load_settings || exit 0
is_running () {
# returns 1 when running, else 0.
if [ -e $PID_FILE ]; then
PID=`cat $PID_FILE`
RET=$?
[ $RET -gt 1 ] && exit 1 || return $RET
else
return 1
fi
}
handle_pid () {
PID_PATH=`dirname $PID_FILE`
[ -d $PID_PATH ] || mkdir -p $PID_PATH && chown -R $RUN_AS $PID_PATH > /dev/null || {
log_warning_msg "$DESC: Could not create $PID_FILE, See $SETTINGS, aborting.";
return 1;}
if [ -e $PID_FILE ]; then
PID=`cat $PID_FILE`
if ! kill -0 $PID > /dev/null 2>&1; then
log_warning_msg "Removing stale $PID_FILE"
rm $PID_FILE
fi
fi
}
handle_datadir () {
[ -d $DATA_DIR ] || mkdir -p $DATA_DIR && chown -R $RUN_AS $DATA_DIR > /dev/null || {
log_warning_msg "$DESC: Could not create $DATA_DIR, See $SETTINGS, aborting.";
return 1;}
}
handle_updates () {
chown -R $RUN_AS $APP_PATH > /dev/null || {
log_warning_msg "$DESC: $APP_PATH not writable by $RUN_AS for web-updates";
return 0; }
}
start_headphones () {
handle_pid
handle_datadir
handle_updates
if ! is_running; then
log_daemon_msg "Starting $DESC"
start-stop-daemon -o -d $APP_PATH -c $RUN_AS --start $EXTRA_SSD_OPTS --pidfile $PID_FILE --exec $DAEMON -- $DAEMON_OPTS
check_retval
else
log_success_msg "$DESC: already running (pid $PID)"
fi
}
stop_headphones () {
if is_running; then
log_daemon_msg "Stopping $DESC"
start-stop-daemon -o --stop --pidfile $PID_FILE --retry 15
check_retval
else
log_success_msg "$DESC: not running"
fi
}
case "$1" in case "$1" in
start) start)
echo "Starting $DESC" start_headphones
start-stop-daemon -d $APP_PATH -c $RUN_AS --start --background --pidfile $PID_FILE --make-pidfile --exec $DAEMON -- $DAEMON_OPTS
;; ;;
stop) stop)
echo "Stopping $DESC" stop_headphones
start-stop-daemon --stop --pidfile $PID_FILE
;; ;;
restart|force-reload)
restart|force-reload) stop_headphones
echo "Restarting $DESC" start_headphones
start-stop-daemon --stop --pidfile $PID_FILE
sleep 15
start-stop-daemon -d $APP_PATH -c $RUN_AS --start --background --pidfile $PID_FILE --make-pidfile --exec $DAEMON -- $DAEMON_OPTS
;; ;;
*) *)
N=/etc/init.d/$NAME N=/etc/init.d/$NAME
echo "Usage: $N {start|stop|restart|force-reload}" >&2 echo "Usage: $N {start|stop|restart|force-reload}" >&2
exit 1 exit 1