diff --git a/back/pialert.py b/back/pialert.py
index df256da0..65938e37 100755
--- a/back/pialert.py
+++ b/back/pialert.py
@@ -270,10 +270,13 @@ def ccd(key, default, config, name, inputtype, options, group, events=[], desc =
# use existing value if already supplied, otehrwise default value is used
if key in config:
- result = config[key]
+ result = config[key]
global mySettings
+ if inputtype == 'text':
+ result = result.replace('\'', "_single_quote_")
+
mySettings.append((key, name, desc, inputtype, options, regex, str(result), group, str(events)))
return result
@@ -283,7 +286,7 @@ def ccd(key, default, config, name, inputtype, options, group, events=[], desc =
def importConfigs ():
# Specify globals so they can be overwritten with the new config
- global lastTimeImported, mySettings, plugins
+ global lastTimeImported, mySettings, plugins, plugins_once_run
# General
global ENABLE_ARPSCAN, SCAN_SUBNETS, PRINT_LOG, TIMEZONE, PIALERT_WEB_PROTECTION, PIALERT_WEB_PASSWORD, INCLUDED_SECTIONS, SCAN_CYCLE_MINUTES, DAYS_TO_KEEP_EVENTS, REPORT_DASHBOARD_URL, DIG_GET_IP_ARG, UI_LANG
# Email
@@ -454,26 +457,26 @@ def importConfigs ():
collect_lang_strings(plugin, pref)
for set in plugin["settings"]:
- setType = set["type"]
+ setFunction = set["function"]
# Setting code name / key
- key = pref + "_" + setType
+ key = pref + "_" + setFunction
- v = ccd(key, set["default_value"], c_d, set["name"][0]["string"], get_form_control(set), str(set["options"]), pref)
+ v = ccd(key, set["default_value"], c_d, set["name"][0]["string"], set["type"] , str(set["options"]), pref)
# Save the user defined value into the object
set["value"] = v
# Setup schedules
- if setType == 'RUN_SCHD':
+ if setFunction == 'RUN_SCHD':
newSchedule = Cron(v).schedule(start_date=datetime.datetime.now(tz))
mySchedules.append(schedule_class(pref, newSchedule, newSchedule.next(), False))
# Collect settings related language strings
- collect_lang_strings(set, pref + "_" + set["type"])
+ collect_lang_strings(set, pref + "_" + set["function"])
# -----------------
# Plugins END
-
+ plugins_once_run = False
# Insert settings into the DB
sql.execute ("DELETE FROM Settings")
@@ -3509,6 +3512,17 @@ def handle_test(testType):
file_print('[', timeNow(), '] END Test: ', testType)
+#-------------------------------------------------------------------------------
+# Return whole setting touple
+def get_setting(key):
+ result = None
+ # mySettings.append((key, name, desc, inputtype, options, regex, str(result), group, str(events)))
+ for set in mySettings:
+ if set[0] == key:
+ result = set
+
+ return result
+
#-------------------------------------------------------------------------------
def isNewVersion():
global newVersionAvailable
@@ -3580,23 +3594,6 @@ def import_language_string(code, key, value, extra = ""):
commitDB ()
-#-------------------------------------------------------------------------------
-def get_form_control(setting):
-
- type = setting["type"]
-
- if type in ['RUN']:
- return 'selecttext'
- if type in ['ENABLE', 'FORCE_REPORT']:
- return 'boolean'
- if type in ['TIMEOUT', 'RUN_TIMEOUT']:
- return 'integer'
- if type in ['WATCH']:
- return 'multiselect'
- if type in ['LIST']:
- return 'list'
-
- return 'text'
#-------------------------------------------------------------------------------
def custom_plugin_decoder(pluginDict):
@@ -3614,7 +3611,7 @@ def run_plugin_scripts(runType):
shouldRun = False
set = get_plugin_setting(plugin, "RUN")
- if set['value'] == runType:
+ if set != None and set['value'] == runType:
if runType != "schedule":
shouldRun = True
elif runType == "schedule":
@@ -3634,16 +3631,155 @@ def run_plugin_scripts(runType):
print_plugin_info(plugin, ['display_name'])
file_print(' [Plugins] CMD: ', get_plugin_setting(plugin, "CMD")["value"])
+ execute_plugin(plugin)
#-------------------------------------------------------------------------------
-def get_plugin_setting(plugin, key):
+# Executes the plugin command specified in the setting with the function specified as CMD
+def execute_plugin(plugin):
+
+ # ------- necessary settings check --------
+ set = get_plugin_setting(plugin, "CMD")
+
+ # handle missing "function":"CMD" setting
+ if set == None:
+ return
+
+ set_CMD = set["value"]
+
+ set = get_plugin_setting(plugin, "RUN_TIMEOUT")
+
+ # handle missing "function":"RUN_TIMEOUT" setting
+ if set == None:
+ return
- for set in plugin['settings']:
- if set["type"] == key:
- return set
+ set_RUN_TIMEOUT = set["value"]
+
+ # Prepare custom params
+ params = []
+
+ if "params" in plugin:
+ for param in plugin["params"]:
+ resolved = ""
+
+ # Get setting value
+ if param["type"] == "setting":
+ resolved = get_setting(param["value"])
+
+ if resolved != None:
+ resolved = plugin_param_from_glob_set(resolved)
+
+ # TODO HERE
+ # if param["type"] == "sql":
+ # resolved = get_sql(param["value"])
+
+ if resolved == None:
+ file_print(' [Plugins] The parameter "name":"', param["name"], '" was resolved as None')
+
+ else:
+ params.append( [param["name"], resolved] )
+
+
+ # ------- prepare params --------
+ # prepare command from plugin settings, custom parameters TODO HERE
+ command = resolve_wildcards(set_CMD, params).split()
+
+ # Execute command
+ file_print(' [Plugins] Executing: ', set_CMD)
+
+ try:
+ # try runnning a subprocess with a forced timeout in case the subprocess hangs
+ output = subprocess.check_output (command, universal_newlines=True, stderr=subprocess.STDOUT, timeout=(set_RUN_TIMEOUT))
+ except subprocess.CalledProcessError as e:
+ # An error occured, handle it
+ file_print(e.output)
+ file_print(' [Plugins] Error - enable PRINT_LOG and check logs')
+ except subprocess.TimeoutExpired as timeErr:
+ file_print(' [Plugins] TIMEOUT - the process forcefully terminated as timeout reached')
+
+
+ # check the last run output
+ f = open(pluginsPath + '/' + plugin["code_name"] + '/last_result.log', 'r+')
+ newLines = f.read().split('\n')
+ f.close()
+
+ # cleanup - select only lines containing a separator to filter out unnecessary data
+ newLines = list(filter(lambda x: '|' in x, newLines))
+
+ if len(newLines) == 0: # check if the subprocess failed / there was no valid output
+ file_print(' [Plugins] No output received from the plugin - enable PRINT_LOG and check logs')
+ return
+ else:
+ file_print('[', timeNow(), '] [Plugins]: SUCCESS, received ', len(newLines), ' entries')
+
+ # regular logging
+ for line in newLines:
+ append_line_to_file (pluginsPath + '/plugin.log', line +'\n')
+
+ # build SQL query parameters to insert into the DB
+ params = []
+
+ for line in newLines:
+ columns = line.split("|")
+ # There has to be always 8 columns
+ if len(columns) == 8:
+ params.append((plugin["unique_prefix"], columns[0], columns[1], columns[2], columns[3], columns[4], columns[5], columns[6], columns[7]))
+ else:
+ file_print(' [Plugins]: Skipped invalid line in the output: ', line)
+
+ if len(params) > 0:
+ sql.executemany ("""INSERT INTO Plugins_State ("Plugin", "Object_PrimaryID", "Object_SecondaryID", "DateTime", "Watched_Value1", "Watched_Value2", "Watched_Value3", "Watched_Value4", "Extra") VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)""", params)
+ commitDB ()
#-------------------------------------------------------------------------------
+# Flattens a setting to make it passable to a script
+def resolve_wildcards(command, params):
+ for param in params:
+ command = command.replace('{' + param[0] + '}', param[1])
+ return command
+
+#-------------------------------------------------------------------------------
+# Flattens a setting to make it passable to a script
+def plugin_param_from_glob_set(globalSetting):
+
+ setVal = globalSetting[6]
+ setTyp = globalSetting[3]
+
+
+ noConversion = ['text', 'integer', 'boolean', 'password', 'readonly', 'selectinteger', 'selecttext' ]
+ arrayConversion = ['multiselect', 'list']
+
+ if setTyp in noConversion:
+ return setVal
+
+ if setTyp in arrayConversion:
+ tmp = ''
+
+ tmp = setVal[:-1] # remove last bracket
+ tmp = tmp[1:] # remove first bracket
+ tmp = tmp.replace("'","").replace(' ','')
+
+ return tmp
+
+
+#-------------------------------------------------------------------------------
+# Gets the whole setting object
+def get_plugin_setting(plugin, function_key):
+
+ result = None
+
+ for set in plugin['settings']:
+ if set["function"] == function_key:
+ result = set
+
+ if result == None:
+ file_print(' [Plugins] Setting with "function":"', function_key, '" is missing in plugin: ', get_plugin_string(plugin, 'display_name'))
+
+ return result
+
+
+#-------------------------------------------------------------------------------
+# Get localized string value on the top JSON depth, not recursive
def get_plugin_string(props, el):
result = ''
diff --git a/front/php/server/util.php b/front/php/server/util.php
index 145f27ba..577d442c 100755
--- a/front/php/server/util.php
+++ b/front/php/server/util.php
@@ -276,7 +276,8 @@ function saveSettings()
{
if($setting[2] == 'text' or $setting[2] == 'password' or $setting[2] == 'readonly' or $setting[2] == 'selecttext')
{
- $txt = $txt.$setting[1]."='".$setting[3]."'\n" ;
+ $val = encode_single_quotes($setting[3]);
+ $txt = $txt.$setting[1]."='".$val."'\n" ;
} elseif($setting[2] == 'integer' or $setting[2] == 'selectinteger')
{
$txt = $txt.$setting[1]."=".$setting[3]."\n" ;
@@ -290,12 +291,18 @@ function saveSettings()
$txt = $txt.$setting[1]."=".$val."\n" ;
}elseif($setting[2] == 'multiselect' or $setting[2] == 'subnets' or $setting[2] == 'list')
{
- $temp = '[';
- foreach($setting[3] as $val)
- {
- $temp = $temp."'". $val."',";
+ $temp = '[';
+
+ if (count($setting) > 3 && is_array( $setting[3]) == True){
+ foreach($setting[3] as $val)
+ {
+ $temp = $temp."'". encode_single_quotes($val)."',";
+ }
+
+ $temp = substr_replace($temp, "", -1); // remove last comma ','
}
- $temp = substr_replace($temp, "", -1).']'; // close brackets and remove last comma ','
+
+ $temp = $temp.']'; // close brackets
$txt = $txt.$setting[1]."=".$temp."\n" ;
}
}
@@ -321,8 +328,6 @@ function saveSettings()
}
// -------------------------------------------------------------------------------------------
-
-
function getString ($codeName, $default) {
$result = lang($codeName);
@@ -337,6 +342,16 @@ function getString ($codeName, $default) {
// -------------------------------------------------------------------------------------------
+
+function encode_single_quotes ($val) {
+
+ $result = str_replace ('\'','_single_quote_',$val);
+
+ return $result;
+}
+
+// -------------------------------------------------------------------------------------------
+
function getDateFromPeriod () {
$period = $_REQUEST['period'];
return '"'. date ('Y-m-d', strtotime ('+1 day -'. $period) ) .'"';
diff --git a/front/plugins/website_monitoring/README.md b/front/plugins/website_monitor/README.md
similarity index 100%
rename from front/plugins/website_monitoring/README.md
rename to front/plugins/website_monitor/README.md
diff --git a/front/plugins/website_monitoring/config.json b/front/plugins/website_monitor/config.json
similarity index 79%
rename from front/plugins/website_monitoring/config.json
rename to front/plugins/website_monitor/config.json
index 29ec2757..8a14d609 100755
--- a/front/plugins/website_monitoring/config.json
+++ b/front/plugins/website_monitor/config.json
@@ -20,9 +20,14 @@
"value" : "SELECT dev_MAC from DEVICES"
},
{
- "name" : "sites",
+ "name" : "urls",
"type" : "setting",
- "value" : "WEBMON_LIST"
+ "value" : "WEBMON_urls_to_check"
+ },
+ {
+ "name" : "internet_ip",
+ "type" : "setting",
+ "value" : "WEBMON_SQL_internet_ip"
}],
"database_column_aliases":{
"Plugins_Events":{
@@ -50,7 +55,8 @@
},
"settings":[
{
- "type": "RUN",
+ "function": "RUN",
+ "type": "selecttext",
"default_value":"disabled",
"options": ["disabled", "once", "schedule", "always_after_scan", "on_new_device"],
"localized": ["name", "description"],
@@ -64,7 +70,23 @@
}]
},
{
- "type": "FORCE_REPORT",
+ "function": "CMD",
+ "type": "text",
+ "default_value":"python3 /home/pi/pialert/front/plugins/website_monitor/script.py urls={urls}",
+ "options": [],
+ "localized": ["name", "description"],
+ "name" : [{
+ "language_code":"en_us",
+ "string" : "Command"
+ }],
+ "description": [{
+ "language_code":"en_us",
+ "string" : "Comamnd to run"
+ }]
+ },
+ {
+ "function": "FORCE_REPORT",
+ "type": "boolean",
"default_value": false,
"options": [],
"localized": ["name", "description"],
@@ -83,7 +105,8 @@
},
{
- "type": "RUN_SCHD",
+ "function": "RUN_SCHD",
+ "type": "text",
"default_value":"0 2 * * *",
"options": [],
"localized": ["name", "description"],
@@ -97,7 +120,8 @@
}]
},
{
- "type": "API_SQL",
+ "function": "API_SQL",
+ "type": "text",
"default_value":"SELECT * FROM plugin_website_monitor",
"options": [],
"localized": ["name", "description"],
@@ -111,7 +135,8 @@
}]
},
{
- "type": "RUN_TIMEOUT",
+ "function": "RUN_TIMEOUT",
+ "type": "integer",
"default_value":5,
"options": [],
"localized": ["name", "description"],
@@ -125,7 +150,8 @@
}]
},
{
- "type": "WATCH",
+ "function": "WATCH",
+ "type": "multiselect",
"default_value":["Watched_Value1"],
"options": ["Watched_Value1","Watched_Value2","Watched_Value3","Watched_Value4"],
"localized": ["name", "description"],
@@ -139,22 +165,9 @@
}]
},
{
- "type": "CMD",
- "default_value":"python3 script.py",
- "options": [],
- "localized": ["name", "description"],
- "name" : [{
- "language_code":"en_us",
- "string" : "Command"
- }],
- "description": [{
- "language_code":"en_us",
- "string" : "Change the dig utility arguments if you have issues resolving your Internet IP. Arguments are added at the end of the following command: dig +short ."
- }]
- },
- {
- "type": "LIST",
- "default_value":"",
+ "function": "urls_to_check",
+ "type": "list",
+ "default_value":[],
"options": [],
"localized": ["name", "description"],
"name" : [{
@@ -163,7 +176,22 @@
}],
"description": [{
"language_code":"en_us",
- "string" : "Change the dig utility arguments if you have issues resolving your Internet IP. Arguments are added at the end of the following command: dig +short ."
+ "string" : "Services to watch. Enter full URL, e.g. https://google.com."
+ }]
+ },
+ {
+ "function": "SQL_internet_ip",
+ "type": "readonly",
+ "default_value":"SELECT dev_LastIP FROM Devices WHERE dev_MAC = 'Internet'",
+ "options": [],
+ "localized": ["name", "description"],
+ "name" : [{
+ "language_code":"en_us",
+ "string" : "Helper variable"
+ }],
+ "description": [{
+ "language_code":"en_us",
+ "string" : "Getting the IP address of the Router / Internet"
}]
}
diff --git a/front/plugins/website_monitoring/script.py b/front/plugins/website_monitor/script.py
similarity index 98%
rename from front/plugins/website_monitoring/script.py
rename to front/plugins/website_monitor/script.py
index d5f42de3..b0e7d5f0 100755
--- a/front/plugins/website_monitoring/script.py
+++ b/front/plugins/website_monitor/script.py
@@ -1,7 +1,7 @@
#!/usr/bin/env python
# Based on the work of https://github.com/leiweibau/Pi.Alert
-# /home/pi/pialert/front/plugins/website_monitoring/script.py urls=http://google.com,http://bing.com
+# /home/pi/pialert/front/plugins/website_monitor/script.py urls=http://google.com,http://bing.com
from __future__ import unicode_literals
from time import sleep, time, strftime
import requests
diff --git a/front/plugins/website_monitoring/last_result.log b/front/plugins/website_monitoring/last_result.log
deleted file mode 100755
index d3e80925..00000000
--- a/front/plugins/website_monitoring/last_result.log
+++ /dev/null
@@ -1,2 +0,0 @@
-http://google.com|null|2023-02-05 15:23:04|200|0.407871|null|null|null
-http://bing.com|null|2023-02-05 15:23:04|200|0.196052|null|null|null
diff --git a/front/plugins/website_monitoring/script.log b/front/plugins/website_monitoring/script.log
deleted file mode 100755
index 2f3baa62..00000000
--- a/front/plugins/website_monitoring/script.log
+++ /dev/null
@@ -1,13 +0,0 @@
-Pi.Alert [Prototype]:
----------------------------------------------------------
-Current User: root
-
-Monitor Web-Services
-Timestamp: 2023-02-05 15:23:03
-
-Start Services Monitoring
-
-| Timestamp | URL | StatusCode | ResponseTime |
------------------------------------------------
-2023-02-05 15:23:04 | http://google.com | 200 | 0.407871
-2023-02-05 15:23:04 | http://bing.com | 200 | 0.196052