mirror of
https://github.com/Tautulli/Tautulli.git
synced 2025-07-10 23:42:37 -07:00
Run script in a new thread with timeout
* Also fixes script output not sent to logger
This commit is contained in:
parent
0b10e68c60
commit
93a1d9c164
2 changed files with 54 additions and 24 deletions
|
@ -509,6 +509,7 @@ _CONFIG_DEFINITIONS = {
|
|||
'SCRIPTS_ON_PMSUPDATE_SCRIPT': (unicode, 'Scripts', ''),
|
||||
'SCRIPTS_ON_CONCURRENT_SCRIPT': (unicode, 'Scripts', ''),
|
||||
'SCRIPTS_ON_NEWDEVICE_SCRIPT': (unicode, 'Scripts', ''),
|
||||
'SCRIPTS_TIMEOUT': (int, 'Scripts', 30),
|
||||
'TELEGRAM_BOT_TOKEN': (str, 'Telegram', ''),
|
||||
'TELEGRAM_ENABLED': (int, 'Telegram', 0),
|
||||
'TELEGRAM_CHAT_ID': (str, 'Telegram', ''),
|
||||
|
|
|
@ -26,6 +26,7 @@ import requests
|
|||
import shlex
|
||||
import smtplib
|
||||
import subprocess
|
||||
import threading
|
||||
import time
|
||||
import urllib
|
||||
from urllib import urlencode
|
||||
|
@ -2116,6 +2117,7 @@ class Scripts(object):
|
|||
def __init__(self, **kwargs):
|
||||
self.script_exts = ('.bat', '.cmd', '.exe', '.php', '.pl', '.ps1', '.py', '.pyw', '.rb', '.sh')
|
||||
self.script_folder = plexpy.CONFIG.SCRIPTS_FOLDER
|
||||
self.script_timeout = plexpy.CONFIG.SCRIPTS_TIMEOUT
|
||||
self.scripts = {'play': plexpy.CONFIG.SCRIPTS_ON_PLAY_SCRIPT,
|
||||
'stop': plexpy.CONFIG.SCRIPTS_ON_STOP_SCRIPT,
|
||||
'pause': plexpy.CONFIG.SCRIPTS_ON_PAUSE_SCRIPT,
|
||||
|
@ -2160,6 +2162,48 @@ class Scripts(object):
|
|||
|
||||
return scripts
|
||||
|
||||
def run_script(self, script):
|
||||
output = error = ''
|
||||
try:
|
||||
def kill_script(process):
|
||||
logger.warn(u"PlexPy Notifiers :: Script exceeded timeout limit of %d seconds. "
|
||||
"Script killed." % self.script_timeout)
|
||||
process.kill()
|
||||
self.script_killed = True
|
||||
|
||||
process = subprocess.Popen(script,
|
||||
stdin=subprocess.PIPE,
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE,
|
||||
cwd=plexpy.CONFIG.SCRIPTS_FOLDER)
|
||||
|
||||
timer = threading.Timer(self.script_timeout, kill_script, (process,))
|
||||
self.script_killed = False
|
||||
|
||||
try:
|
||||
timer.start()
|
||||
output, error = process.communicate()
|
||||
status = process.returncode
|
||||
finally:
|
||||
timer.cancel()
|
||||
|
||||
except OSError as e:
|
||||
logger.error(u"PlexPy Notifiers :: Failed to run script: %s" % e)
|
||||
return False
|
||||
|
||||
if error:
|
||||
err = '\n '.join([l for l in error.splitlines()])
|
||||
logger.error(u"PlexPy Notifiers :: Script error: \n %s" % err)
|
||||
return False
|
||||
|
||||
if output:
|
||||
out = '\n '.join([l for l in output.splitlines()])
|
||||
logger.debug(u"PlexPy Notifiers :: Script returned: \n %s" % out)
|
||||
|
||||
if not self.script_killed:
|
||||
logger.info(u"PlexPy Notifiers :: Script notification sent.")
|
||||
return True
|
||||
|
||||
def notify(self, subject='', message='', notify_action='', script_args=None, *args, **kwargs):
|
||||
"""
|
||||
Args:
|
||||
|
@ -2231,31 +2275,10 @@ class Scripts(object):
|
|||
script.extend(script_args)
|
||||
|
||||
logger.debug(u"PlexPy Notifiers :: Full script is: %s" % script)
|
||||
logger.debug(u"PlexPy Notifiers :: Executing script in a new thread.")
|
||||
thread = threading.Thread(target=self.run_script, args=(script,)).start()
|
||||
|
||||
try:
|
||||
p = subprocess.Popen(script, stdin=subprocess.PIPE,
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.STDOUT,
|
||||
cwd=plexpy.CONFIG.SCRIPTS_FOLDER)
|
||||
|
||||
out, error = p.communicate()
|
||||
status = p.returncode
|
||||
|
||||
if out and status:
|
||||
out = out.strip()
|
||||
logger.debug(u"PlexPy Notifiers :: Script returned: %s" % out)
|
||||
|
||||
if error:
|
||||
error = error.strip()
|
||||
logger.error(u"PlexPy Notifiers :: Script error: %s" % error)
|
||||
return False
|
||||
else:
|
||||
logger.info(u"PlexPy Notifiers :: Script notification sent.")
|
||||
return True
|
||||
|
||||
except OSError as e:
|
||||
logger.error(u"PlexPy Notifiers :: Failed to run script: %s" % e)
|
||||
return False
|
||||
return True
|
||||
|
||||
def return_config_options(self):
|
||||
config_option = [{'label': 'Supported File Types',
|
||||
|
@ -2365,6 +2388,12 @@ class Scripts(object):
|
|||
'description': 'Choose the script for user new device.',
|
||||
'input_type': 'select',
|
||||
'select_options': self.list_scripts()
|
||||
},
|
||||
{'label': 'Script Timeout',
|
||||
'value': self.script_timeout,
|
||||
'name': 'scripts_timeout',
|
||||
'description': 'The number of seconds to wait before killing the script.',
|
||||
'input_type': 'number'
|
||||
}
|
||||
]
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue