diff --git a/plugin/source/dynamix.unraid.net/etc/rc.d/rc.flash_backup b/plugin/source/dynamix.unraid.net/etc/rc.d/rc.flash_backup
new file mode 100755
index 000000000..6093c5f51
--- /dev/null
+++ b/plugin/source/dynamix.unraid.net/etc/rc.d/rc.flash_backup
@@ -0,0 +1,205 @@
+#!/bin/bash
+# This file is /etc/rc.d/rc.flash_backup
+# use at queue "f" for flash backup
+QUEUE=" -q f "
+TASKNAME="/etc/rc.d/rc.flash_backup watch"
+TASKACTION="/usr/local/emhttp/plugins/dynamix.my.servers/scripts/UpdateFlashBackup update"
+last=$(date +%s)
+# set GIT_OPTIONAL_LOCKS=0 globally to reduce/eliminate writes to /boot
+export GIT_OPTIONAL_LOCKS=0
+
+FAST=1 # 1 second delay when waiting for git
+SLOW=10 # 10 second delay when waiting for git
+# wait for existing git commands to complete
+# $1 is the time in seconds to sleep when waiting. SLOW or FAST
+_waitforgit() {
+ while [[ $(pgrep -f '^git -C /boot' -c) -ne 0 ]]; do
+ sleep "$1"
+ done
+}
+# log to syslog, then wait for existing git commands to complete
+# $1 is the time in seconds to sleep when waiting. SLOW or FAST
+_waitforgitlog() {
+ if [[ $(pgrep -f '^git -C /boot' -c) -ne 0 ]]; then
+ logger "waiting for current backup to complete" --tag flash_backup
+ _waitforgit "$1"
+ fi
+}
+status() {
+ _connected && CONNECTED="system is connected to Unraid Connect Cloud." || CONNECTED="system is not connected to Unraid Connect Cloud."
+ if _watching; then
+ echo "flash backup monitor is running. ${CONNECTED}"
+ _hasqueue && echo "changes detected, backup queued."
+ exit 0
+ else
+ if _enabled; then
+ echo "flash backup is enabled but the monitor is not running. ${CONNECTED}"
+ else
+ echo "flash backup is disabled so the monitor is disabled. ${CONNECTED}"
+ fi
+ exit 1
+ fi
+}
+start() {
+ _start
+ exit 0
+}
+stop() {
+ _stop
+ exit 0
+}
+reload() {
+ _start
+ sleep 1
+ status
+}
+_start() {
+ # Note: can start if not signed in, but watcher loop will not process until signed in
+ # only run if flash_backup is enabled
+ if ! _enabled; then
+ logger "flash backup disabled, exiting" --tag flash_backup
+ exit 1
+ fi
+ _stop
+ # start watcher loop as background process
+ exec ${TASKNAME} &>/dev/null &
+}
+_stop() {
+ if _watching; then
+ logger "stop watching for file changes" --tag flash_backup
+ # terminate watcher loop/process
+ pkill --full "${TASKNAME}" &>/dev/null
+ fi
+ # do not flush. better to have unsaved changes than to corrupt the backup during shutdown
+ # note that an existing git process could still be running
+}
+flush() {
+ # remove any queued jobs
+ _removequeue
+ # wait for existing git commands to finish before flushing
+ _waitforgitlog "${FAST}"
+ logger "flush: ${TASKACTION}" --tag flash_backup
+ # push any changes ad-hoc
+ # shellcheck disable=SC2086
+ echo "${TASKACTION}_nolimit &>/dev/null" | at ${QUEUE} -M now &>/dev/null
+}
+_watching() {
+ local flash_backup_pid
+ flash_backup_pid=$(pgrep --full "${TASKNAME}")
+ if [[ ${flash_backup_pid} ]]; then
+ return 0
+ fi
+ return 1
+}
+_watch() {
+ # safely clean up git *.lock files
+ _clearlocks
+ # flush: this will ensure we start with a clean repo
+ flush
+ logger "start watching for file changes" --tag flash_backup
+ # start watcher loop
+ while true; do
+ # wait for system to be connected to Unraid Connect Cloud, then process flash backups
+ _connected && _f1
+ sleep 60
+ done
+}
+_f1() {
+ # wait for existing git commands to finish before checking for updates
+ _waitforgit "${SLOW}"
+ if [ "$(git -C /boot status -s)" ]; then
+ _hasqueue || _f2
+ elif _haserror && _beenawhile; then
+ # we are in an error state and it has been 3 hours since we last tried submitting. run the task now.
+ _runtaskaction
+ fi
+}
+_f2() {
+ if ! _haserror || [[ $(($(date +"%M") % 10)) -eq 0 ]]; then
+ logger "adding task: ${TASKACTION}" --tag flash_backup
+ fi
+ sed -i "s@uptodate=yes@uptodate=no@" /var/local/emhttp/flashbackup.ini &>/dev/null
+ _runtaskaction
+}
+_hasqueue() {
+ # returns false if the queue is empty, true otherwise
+ # shellcheck disable=SC2086
+ if [ -z "$(atq ${QUEUE})" ]; then
+ return 1
+ fi
+ return 0
+}
+_removequeue() {
+ # delete any at jobs in queue f
+ # @TODO shellcheck SC2162
+ # shellcheck disable=SC2086
+ atq ${QUEUE} | while read line; do
+ id=$(echo ${line} | cut -d " " -f 1)
+ atrm ${id}
+ done
+}
+_runtaskaction() {
+ # shellcheck disable=SC2086
+ echo "${TASKACTION} &>/dev/null" | at ${QUEUE} -M now +1 minute &>/dev/null
+ last=$(date +%s)
+}
+_enabled() {
+ local output
+ output=$(git -C /boot config --get remote.origin.url 2>&1)
+ if [[ "${output}" == *"backup.unraid.net"* ]]; then
+ return 0
+ fi
+ return 1
+}
+_connected() {
+ CFG=/var/local/emhttp/myservers.cfg
+ [[ ! -f "${CFG}" ]] && return 1
+ # shellcheck disable=SC1090
+ source <(sed -nr '/\[connectionStatus\]/,/\[/{/minigraph/p}' "${CFG}" 2>/dev/null)
+ if [[ -z "${minigraph}" || "${minigraph}" != "CONNECTED" ]]; then
+ return 1
+ fi
+ return 0
+}
+_haserror() {
+ errorstring=$(awk -F "=" '/error/ {print $2}' /var/local/emhttp/flashbackup.ini 2>&1 || echo '')
+ if [ ${#errorstring} -le 2 ]; then
+ return 1
+ fi
+ return 0
+}
+_beenawhile() {
+ now=$(date +%s)
+ age=$((now - last))
+ maxage=$((3 * 60 * 60)) # three hours
+ [[ $age -gt $maxage ]] && return 0
+ return 1
+}
+# wait for git commands to end, then delete any stale lock files
+_clearlocks() {
+ _waitforgitlog "${FAST}"
+ find /boot/.git -type f -name '*.lock' -delete
+}
+case "$1" in
+'status')
+ status
+ ;;
+'start')
+ start
+ ;;
+'stop')
+ stop
+ ;;
+'reload')
+ reload
+ ;;
+'flush')
+ flush
+ ;;
+'watch')
+ _watch
+ ;;
+*)
+ echo "usage $0 status|start|stop|reload|flush"
+ ;;
+esac
diff --git a/plugin/source/dynamix.unraid.net/etc/rc.d/rc.unraid-api b/plugin/source/dynamix.unraid.net/etc/rc.d/rc.unraid-api
new file mode 100755
index 000000000..f80837ba8
--- /dev/null
+++ b/plugin/source/dynamix.unraid.net/etc/rc.d/rc.unraid-api
@@ -0,0 +1,182 @@
+#!/bin/bash
+# unraid-api-handler
+flash="/boot/config/plugins/dynamix.my.servers"
+[[ ! -d "${flash}" ]] && echo "Please reinstall the Unraid Connect plugin" && exit 1
+[[ ! -f "${flash}/env" ]] && echo 'env=production' >"${flash}/env"
+# define env to avoid shellcheck SC2154. Will be overridden by the source command below
+env=production
+# shellcheck disable=SC1091
+source "${flash}/env"
+api_base_directory="/usr/local/bin"
+
+# Only allow specific envs
+if [ "${env}" != "staging" ] && [ "${env}" != "production" ]; then
+ echo "\"${env}\" is an unsupported env. Please use \"staging\" or \"production\"."
+ exit 1
+fi
+
+switchenv() {
+ stop
+ # Get current environment from file
+ local envFile="${flash}/env"
+ local currentEnv
+ currentEnv=$(
+ # shellcheck disable=SC1090
+ source "${envFile}"
+ echo "${env}"
+ )
+
+ if [[ "${currentEnv}" = "production" ]]; then
+ echo "Switching from production to staging"
+ echo 'env="staging"' >"${envFile}"
+ cp "${api_base_directory}/unraid-api/.env.staging" "${api_base_directory}/unraid-api/.env"
+ elif [[ "${currentEnv}" = "staging" ]]; then
+ echo "Switching from staging to production"
+ echo 'env="production"' >"${envFile}"
+ cp "${api_base_directory}/unraid-api/.env.production" "${api_base_directory}/unraid-api/.env"
+ fi
+ echo "Run \"unraid-api start\" to start the API."
+}
+raiseloglevel() {
+ kill -s SIGUSR2 "$(pidof unraid-api)"
+}
+lowerloglevel() {
+ kill -s SIGUSR1 "$(pidof unraid-api)"
+}
+status() {
+ LOG_TYPE=raw "${api_base_directory}/unraid-api/unraid-api" status
+}
+start() {
+ LOG_TYPE=raw "${api_base_directory}/unraid-api/unraid-api" start 2>&1 | logger &
+}
+report() {
+ LOG_TYPE=raw "${api_base_directory}/unraid-api/unraid-api" report "$1" "$2"
+}
+startdebug() {
+ LOG_CONTEXT=true LOG_STACKTRACE=true LOG_TRACING=true LOG_LEVEL=debug "${api_base_directory}/unraid-api/unraid-api" start --debug
+}
+stop() {
+ LOG_TYPE=raw "${api_base_directory}/unraid-api/unraid-api" stop 2>/dev/null
+}
+reload() {
+ LOG_TYPE=raw "${api_base_directory}/unraid-api/unraid-api" restart
+}
+_install() {
+ # process file from commandline
+ if [[ -n "$1" ]]; then
+ file=$(realpath "${flash}/$1")
+ if [[ "${file}" == "${flash}"* ]] && [[ "${file}" == *".tgz" || "${file}" == *".zip" ]] && [[ -f "${file}" ]]; then
+ [[ "${file}" == *".tgz" ]] && ext=tgz || ext=zip
+ echo "installing $1"
+ cp "${file}" "${flash}/unraid-api.${ext}"
+ else
+ echo "invalid installation file: $1"
+ exit 1
+ fi
+ fi
+
+ # If this was downloaded from a Github action it'll be a zip with a tgz inside
+ # Let's extract the tgz and rename it for the next step
+ if [[ -f "${flash}/unraid-api.zip" ]]; then
+ for f in ${flash}/unraid-api.zip; do unzip -p "${f}" >"${flash}/${f%.zip}.tgz"; done
+ rm -f "${flash}/unraid-api.zip"
+ fi
+
+ # Ensure installation tgz exists
+ [[ ! -f "${flash}/unraid-api.tgz" ]] && echo "Please reinstall the Unraid Connect plugin" && exit 1
+
+ # Stop old process
+ [[ -f "${api_base_directory}/unraid-api/unraid-api" ]] && stop
+
+ # Install unraid-api
+ rm -rf "${api_base_directory}/unraid-api"
+ mkdir -p "${api_base_directory}/unraid-api"
+ tar -C "${api_base_directory}/unraid-api" -xzf "${flash}/unraid-api.tgz" --strip 1
+
+ # Reset permissions
+ rm -f "${flash}/data/permissions.json"
+
+ # Copy env file
+ cp "${api_base_directory}/unraid-api/.env.${env}" "${api_base_directory}/unraid-api/.env"
+
+ # Copy wc files from flash
+ if [ -f "${flash}/webComps/unraid.min.js" ]; then
+ rm -rf /usr/local/emhttp/webGui/webComps
+ mkdir -p /usr/local/emhttp/webGui/webComps
+ cp ${flash}/webComps/* /usr/local/emhttp/webGui/webComps
+ else
+ # not fatal, previous version of unraid.min.js should still exist in /usr/local/emhttp/webGui/webComps
+ echo "Note: ${flash}/webComps/unraid.min.js is missing"
+ fi
+
+ # bail if expected file does not exist
+ [[ ! -f "${api_base_directory}/unraid-api/unraid-api" ]] && echo "unraid-api install failed" && exit 1
+}
+install() {
+ # Install the files
+ _install "$1"
+
+ # if nginx is running, start the api. if not, it will be started by rc.nginx
+ if /etc/rc.d/rc.nginx status &>/dev/null; then
+ # Start new process
+ start
+ # Note: do not run another unraid-api command until you see "UNRAID API started successfully!" in syslog
+ sleep 3
+ echo "unraid-api installed and started"
+ else
+ echo "unraid-api installed"
+ fi
+ exit 0
+}
+uninstall() {
+ # Stop old process
+ [[ -f "${api_base_directory}/unraid-api/unraid-api" ]] && stop
+
+ # Remove all unraid-api files
+ rm -rf "${api_base_directory}/unraid-api"
+ rm -f /var/run/unraid-api.sock
+}
+case "$1" in
+'status')
+ status
+ ;;
+'start')
+ start
+ ;;
+'report')
+ report "$2" "$3"
+ ;;
+'switch-env')
+ switchenv
+ ;;
+'start-debug')
+ startdebug
+ ;;
+'raise-log-level')
+ raiseloglevel
+ ;;
+'lower-log-level')
+ lowerloglevel
+ ;;
+'stop')
+ stop
+ ;;
+'reload')
+ reload
+ ;;
+'restart')
+ reload
+ ;;
+'install')
+ install "$2"
+ ;;
+'_install')
+ _install "$2"
+ ;;
+'uninstall')
+ uninstall
+ ;;
+*)
+ echo "usage $0 status|start|report|switch-env|start-debug|raise-log-level|lower-log-level|stop|reload|install|uninstall"
+ ;;
+esac
diff --git a/plugin/source/dynamix.unraid.net/usr/local/emhttp/plugins/dynamix.my.servers/Connect.page b/plugin/source/dynamix.unraid.net/usr/local/emhttp/plugins/dynamix.my.servers/Connect.page
index 8f66ebc9c..a6e70f557 100644
--- a/plugin/source/dynamix.unraid.net/usr/local/emhttp/plugins/dynamix.my.servers/Connect.page
+++ b/plugin/source/dynamix.unraid.net/usr/local/emhttp/plugins/dynamix.my.servers/Connect.page
@@ -522,7 +522,7 @@ _(Allow Remote Access)_:
: _(Disabled until you have signed in)_
-: _(Disabled until connected to Unraid Connect Cloud)_
+: _(Disabled until connected to Unraid Connect Cloud - try reloading the page)_
: _(Disabled until you Provision a myunraid.net SSL Cert)_
@@ -627,8 +627,8 @@ _(Enable Transparent 2FA for Local Access)_:
_(Flash backup)_:
: _(Disabled until you have signed in)_
-
-: _(Disabled until connected to Unraid Connect Cloud)_
+
+: _(Disabled until connected to Unraid Connect Cloud - try reloading the page)_
: _(Loading)_
diff --git a/plugin/source/dynamix.unraid.net/usr/local/emhttp/plugins/dynamix.my.servers/include/UpdateFlashBackup.php b/plugin/source/dynamix.unraid.net/usr/local/emhttp/plugins/dynamix.my.servers/include/UpdateFlashBackup.php
index 613dcbcc1..2c42f38a1 100644
--- a/plugin/source/dynamix.unraid.net/usr/local/emhttp/plugins/dynamix.my.servers/include/UpdateFlashBackup.php
+++ b/plugin/source/dynamix.unraid.net/usr/local/emhttp/plugins/dynamix.my.servers/include/UpdateFlashBackup.php
@@ -70,8 +70,8 @@ function save_flash_backup_state($loading='') {
rename($flashbackup_tmp, $flashbackup_ini);
}
-function load_flash_backup_state() {
- global $arrState,$flashbackup_ini,$isRegistered;
+function default_flash_backup_state() {
+ global $arrState;
$arrState = [
'activated' => 'no',
@@ -80,6 +80,12 @@ function load_flash_backup_state() {
'error' => '',
'remoteerror' => ''
];
+}
+
+function load_flash_backup_state() {
+ global $arrState,$flashbackup_ini,$isRegistered;
+
+ default_flash_backup_state();
$arrNewState = (file_exists($flashbackup_ini)) ? @parse_ini_file($flashbackup_ini) : [];
if ($arrNewState) {
@@ -277,7 +283,14 @@ if ($pgrep_output[0] != "0") {
// check if signed-in
if (!$isRegistered) {
- response_complete(406, array('error' => 'Must be signed in to My Servers to use Flash Backup'));
+ default_flash_backup_state();
+ response_complete(406, array('error' => 'Must be signed in to Unraid Connect to use Flash Backup'));
+}
+
+// check if connected to Unraid Connect Cloud
+if (!$isConnected) {
+ default_flash_backup_state();
+ response_complete(406, array('error' => 'Must be connected to Unraid Connect Cloud to use Flash Backup'));
}
// keyfile
@@ -540,7 +553,7 @@ if (empty($SSH_PORT)) {
} else {
$arrState['loading'] = '';
if (stripos(implode($ssh_output),'permission denied') !== false) {
- $arrState['error'] = ($isConnected) ? 'Permission Denied' : 'Permission Denied, ensure you are connected to My Servers Cloud';
+ $arrState['error'] = ($isConnected) ? 'Permission Denied' : 'Permission Denied, ensure you are connected to Unraid Connect Cloud';
} else {
$arrState['error'] = 'Unable to connect to backup.unraid.net:22';
}
@@ -654,7 +667,7 @@ if ($command == 'update' || $command == 'activate') {
if ($return_var != 0) {
// check for permission denied
if (stripos(implode($push_output),'permission denied') !== false) {
- $arrState['error'] = ($isConnected) ? 'Permission Denied' : 'Permission Denied, ensure you are connected to My Servers Cloud';
+ $arrState['error'] = ($isConnected) ? 'Permission Denied' : 'Permission Denied, ensure you are connected to Unraid Connect Cloud';
} elseif (stripos(implode($push_output),'fatal: loose object') !== false && stripos(implode($push_output),'is corrupt') !== false) {
// detect corruption #2
$arrState['error'] = 'Error: Backup corrupted';