From 8d8aab8ac0002a1161b992c75c1cb0fa546ee3aa Mon Sep 17 00:00:00 2001 From: Martinski4GitHub <119833648+Martinski4GitHub@users.noreply.github.com> Date: Sun, 22 Feb 2026 04:37:00 -0800 Subject: [PATCH 01/10] Support for New AMTM Functionality - Initial code to support automatic script updates via AMTM. --- MerlinAU.sh | 155 ++++++++++++++++++++++++++++++++++++---------------- README.md | 4 +- 2 files changed, 109 insertions(+), 50 deletions(-) diff --git a/MerlinAU.sh b/MerlinAU.sh index ec89df1..5bbf8c1 100644 --- a/MerlinAU.sh +++ b/MerlinAU.sh @@ -4,16 +4,16 @@ # # Original Creation Date: 2023-Oct-01 by @ExtremeFiretop. # Official Co-Author: @Martinski W. - Date: 2023-Nov-01 -# Last Modified: 2026-Feb-07 +# Last Modified: 2026-Feb-22 ################################################################### set -u ## Set version for each Production Release ## -readonly SCRIPT_VERSION=1.5.9 -readonly SCRIPT_VERSTAG="26020700" +readonly SCRIPT_VERSION=1.5.10 +readonly SCRIPT_VERSTAG="26022202" readonly SCRIPT_NAME="MerlinAU" ## Set to "master" for Production Releases ## -SCRIPT_BRANCH="master" +SCRIPT_BRANCH="dev" ##----------------------------------------## ## Modified by Martinski W. [2024-Jul-03] ## @@ -52,6 +52,9 @@ scriptUpdateNotify=0 routerModelCheckFailed=false offlineUpdateTrigger=false +# To support automatic script updates from AMTM # +doScriptUpdateFromAMTM=true + ##----------------------------------------## ## Modified by Martinski W. [2025-Feb-18] ## ##----------------------------------------## @@ -169,6 +172,7 @@ fi ##----------------------------------------## inMenuMode=true webguiMode=false +isVerbose=false isInteractive=false FlashStarted=false MerlinChangeLogURL="" @@ -273,7 +277,10 @@ routerLoginFailureMsg="Please try the following: to restrict access to the router webGUI from the router's IP address [${GRNct}${mainLAN_IPaddr}${NOct}]. 3. Confirm your password via the \"Set Router Login Password\" option from the Main Menu." -[ -t 0 ] && ! tty | grep -qwi "NOT" && isInteractive=true +if [ -t 0 ] && ! tty | grep -qwi "NOT" +then + isInteractive=true ; isVerbose=true +fi ##----------------------------------------## ## Modified by Martinski W. [2023-Dec-23] ## @@ -321,13 +328,23 @@ _UserLogMsg_() fi } +##-------------------------------------## +## Added by Martinski W. [2026-Feb-22] ## +##-------------------------------------## +DoPrintf() +{ + if "$isInteractive" && "$isVerbose" + then printf "$@" + fi +} + ##----------------------------------------## -## Modified by Martinski W. [2025-May-05] ## +## Modified by Martinski W. [2026-Feb-22] ## ##----------------------------------------## Say() { local logMsg - "$isInteractive" && printf "${1}\n" + DoPrintf "${1}\n" # Remove all "color escape sequences" from the system log file entries # logMsg="$(echo "$1" | \ sed 's/\\e\[[0-1]m//g; s/\\e\[[3-4][0-9]m//g; s/\\e\[[0-1];[3-4][0-9]m//g; s/\\e\[30;10[1-9]m//g; s/\\n/ /g')" @@ -335,9 +352,9 @@ Say() printf "$logMsg" | logger -t "${SCRIPT_NAME}_[$$]" } -##----------------------------------------------## -## Added/Modified by Martinski W. [2023-Nov-20] ## -##----------------------------------------------## +##----------------------------------------## +## Modified by Martinski W. [2023-Nov-20] ## +##----------------------------------------## _WaitForEnterKey_() { ! "$isInteractive" && return 0 @@ -2912,9 +2929,9 @@ _CheckNewScriptMinFWBeforeUpdate_() return 0 } -##-------------------------------------------## -## Modified by ExtremeFiretop [2026-Jan-23] ## -##-------------------------------------------## +##----------------------------------------## +## Modified by Martinski W. [2026-Feb-22] ## +##----------------------------------------## _SCRIPT_UPDATE_() { local current_version @@ -2964,9 +2981,15 @@ _SCRIPT_UPDATE_() _SendEMailNotification_ SUCCESS_SCRIPT_UPDATE_STATUS fi sleep 1 - _ReleaseLock_ - exec "$ScriptFilePath" - exit 0 + if [ $# -lt 2 ] || [ -z "$2" ] + then + _ReleaseLock_ + exec "$ScriptFilePath" + exit 0 + elif [ "$2" = "unattended" ] + then + return 0 + fi else if ! "$isInteractive" then @@ -3067,6 +3090,32 @@ _SCRIPT_UPDATE_() fi } +##-------------------------------------## +## Added by Martinski W. [2026-Feb-22] ## +##-------------------------------------## +ScriptUpdateFromAMTM() +{ + local retCode=1 + + ScriptAutoUpdateSetting="$(Get_Custom_Setting Allow_Script_Auto_Update)" + if ! "$doScriptUpdateFromAMTM" || \ + [ "$ScriptAutoUpdateSetting" = "ENABLED" ] + then + printf "Automatic script updates via AMTM are currently disabled.\n\n" + return 1 + fi + if [ $# -gt 0 ] && [ "$1" = "check" ] + then return 0 + fi + if _AcquireLock_ cliFileLock + then + _SCRIPT_UPDATE_ force unattended + retCode="$?" + _ReleaseLock_ cliFileLock + fi + return "$retCode" +} + ##------------------------------------------## ## Modified by ExtremeFiretop [2025-May-10] ## ##------------------------------------------## @@ -6568,14 +6617,14 @@ _DelFWAutoUpdateCronJob_() if _CheckFWAutoUpdateCronJobExists_ ANY then retCode=1 - printf "${REDct}**ERROR**${NOct}: Failed to remove cron job [${GRNct}${CRON_JOB_TAG}${NOct}].\n" + DoPrintf "${REDct}**ERROR**${NOct}: Failed to remove cron job [${GRNct}${CRON_JOB_TAG}${NOct}].\n" else retCode=0 - printf "Cron job '${GRNct}${CRON_JOB_TAG}${NOct}' was removed successfully.\n" + DoPrintf "Cron job '${GRNct}${CRON_JOB_TAG}${NOct}' was removed successfully.\n" fi else retCode=0 - printf "Cron job '${GRNct}${CRON_JOB_TAG}${NOct}' does not exist.\n" + DoPrintf "Cron job '${GRNct}${CRON_JOB_TAG}${NOct}' does not exist.\n" fi return "$retCode" } @@ -6672,14 +6721,14 @@ _DelScriptAutoUpdateCronJob_() if eval $cronListCmd | grep -qE "$SCRIPT_UP_CRON_JOB_RUN #${SCRIPT_UP_CRON_JOB_TAG}#$" then retCode=1 - printf "${REDct}**ERROR**${NOct}: Failed to remove cron job [${GRNct}${SCRIPT_UP_CRON_JOB_TAG}${NOct}].\n" + DoPrintf "${REDct}**ERROR**${NOct}: Failed to remove cron job [${GRNct}${SCRIPT_UP_CRON_JOB_TAG}${NOct}].\n" else retCode=0 - printf "Cron job '${GRNct}${SCRIPT_UP_CRON_JOB_TAG}${NOct}' was removed successfully.\n" + DoPrintf "Cron job '${GRNct}${SCRIPT_UP_CRON_JOB_TAG}${NOct}' was removed successfully.\n" fi else retCode=0 - printf "Cron job '${GRNct}${SCRIPT_UP_CRON_JOB_TAG}${NOct}' does not exist.\n" + DoPrintf "Cron job '${GRNct}${SCRIPT_UP_CRON_JOB_TAG}${NOct}' does not exist.\n" fi return "$retCode" } @@ -9573,7 +9622,7 @@ Please manually update to version ${GRNct}${MinSupportedFirmwareVers}${NOct} or _CheckFirmwareMD5_ retCode="$?" else - retCode=0 # Skip if the MD5 file does not exist + retCode=0 # Skip if the MD5 file does NOT exist # fi else if "$isGNUtonFW" @@ -9920,7 +9969,7 @@ _DelFWAutoUpdateHook_() Say "F/W Update cron job hook was deleted successfully from '$hookScriptFile' script." fi else - printf "F/W Update cron job hook does not exist in '$hookScriptFile' script.\n" + DoPrintf "F/W Update cron job hook does not exist in '$hookScriptFile' script.\n" fi } @@ -10004,7 +10053,7 @@ _DelScriptAutoUpdateHook_() Say "ScriptAU cron job hook was deleted successfully from '$hookScriptFile' script." fi else - printf "ScriptAU cron job hook does not exist in '$hookScriptFile' script.\n" + DoPrintf "ScriptAU cron job hook does not exist in '$hookScriptFile' script.\n" fi } @@ -10582,13 +10631,14 @@ _EnableFWAutoUpdateChecks_() } ##----------------------------------------## -## Modified by Martinski W. [2025-Sep-01] ## +## Modified by Martinski W. [2026-Feb-22] ## ##----------------------------------------## _ConfirmCronJobForFWAutoUpdates_() { if [ $# -gt 0 ] && [ -n "$1" ] && \ echo "$1" | grep -qE "^(install|startup|uninstall)$" - then return 1 ; fi + then return 1 + fi # Check if the PREVIOUS Cron Job ID already exists # if eval $cronListCmd | grep -qE "$CRON_JOB_RUN #${CRON_JOB_TAG_OLD}#$" @@ -10618,17 +10668,17 @@ _ConfirmCronJobForFWAutoUpdates_() then if ! _CheckFWAutoUpdateCronJobExists_ then - printf "Auto-enabling cron job '${GRNct}${CRON_JOB_TAG}${NOct}'...\n" + DoPrintf "Auto-enabling cron job '${GRNct}${CRON_JOB_TAG}${NOct}'...\n" if _AddFWAutoUpdateCronJob_ then - printf "Cron job '${GRNct}${CRON_JOB_TAG}${NOct}' was added successfully.\n" + DoPrintf "Cron job '${GRNct}${CRON_JOB_TAG}${NOct}' was added successfully.\n" cronSchedStrHR="$(_TranslateCronSchedHR_ "$FW_UpdateCronJobSchedule")" - printf "Job Schedule: ${GRNct}${cronSchedStrHR}${NOct}\n" + DoPrintf "Job Schedule: ${GRNct}${cronSchedStrHR}${NOct}\n" else - printf "${REDct}**ERROR**${NOct}: Failed to add the cron job [${CRON_JOB_TAG}].\n" + DoPrintf "${REDct}**ERROR**${NOct}: Failed to add the cron job [${CRON_JOB_TAG}].\n" fi else - printf "Cron job '${GRNct}${CRON_JOB_TAG}${NOct}' already exists.\n" + DoPrintf "Cron job '${GRNct}${CRON_JOB_TAG}${NOct}' already exists.\n" fi _EnableFWAutoUpdateChecks_ @@ -10637,32 +10687,34 @@ _ConfirmCronJobForFWAutoUpdates_() then if ! _CheckFWAutoUpdateCronJobExists_ then - _ShowLogo_ - printf "Do you want to enable automatic firmware update checks?\n" - printf "This will create a CRON job to check for updates regularly.\n" - printf "The CRON can be disabled at any time via the main menu.\n" - - if _WaitForYESorNO_ + if "$isInteractive" && "$isVerbose" + then + _ShowLogo_ + printf "Do you want to enable automatic firmware update checks?\n" + printf "This will create a CRON job to check for updates regularly.\n" + printf "The CRON can be disabled at any time via the main menu.\n" + fi + if "$isVerbose" && _WaitForYESorNO_ then - printf "Adding '${GRNct}${CRON_JOB_TAG}${NOct}' cron job...\n" + DoPrintf "Adding '${GRNct}${CRON_JOB_TAG}${NOct}' cron job...\n" if _AddFWAutoUpdateCronJob_ then - printf "Cron job '${GRNct}${CRON_JOB_TAG}${NOct}' was added successfully.\n" + DoPrintf "Cron job '${GRNct}${CRON_JOB_TAG}${NOct}' was added successfully.\n" cronSchedStrHR="$(_TranslateCronSchedHR_ "$FW_UpdateCronJobSchedule")" - printf "Job Schedule: ${GRNct}${cronSchedStrHR}${NOct}\n" + DoPrintf "Job Schedule: ${GRNct}${cronSchedStrHR}${NOct}\n" else - printf "${REDct}**ERROR**${NOct}: Failed to add the cron job [${CRON_JOB_TAG}].\n" + DoPrintf "${REDct}**ERROR**${NOct}: Failed to add the cron job [${CRON_JOB_TAG}].\n" fi _EnableFWAutoUpdateChecks_ else # User said NO -> disable checks # - printf "Automatic firmware update checks will be ${REDct}DISABLED${NOct}.\n" - printf "You can enable this feature later via the main menu.\n" + DoPrintf "Automatic firmware update checks will be ${REDct}DISABLED${NOct}.\n" + DoPrintf "You can enable this feature later via the main menu.\n" _DisableFWAutoUpdateChecks_ fi _WaitForEnterKey_ else - printf "Cron job '${GRNct}${CRON_JOB_TAG}${NOct}' already exists.\n" + DoPrintf "Cron job '${GRNct}${CRON_JOB_TAG}${NOct}' already exists.\n" Update_Custom_Settings "FW_Update_Check" "ENABLED" _AddFWAutoUpdateHook_ runfwUpdateCheck=true @@ -10671,12 +10723,12 @@ _ConfirmCronJobForFWAutoUpdates_() # 3) "DISABLED": Perform the disable steps (same as _Toggle_FW_UpdateCheckSetting_) # elif [ "$fwUpdateCheckState" = "DISABLED" ] then - printf "Firmware update checks have been ${REDct}DISABLED${NOct}.\n" + DoPrintf "Firmware update checks have been ${REDct}DISABLED${NOct}.\n" _DisableFWAutoUpdateChecks_ # 4) Unknown/fallback -> treat as DISABLED # else - printf "Unknown FW_Update_Check value: '%s'. Disabling firmware checks.\n" "$fwUpdateCheckState" + DoPrintf "Unknown FW_Update_Check value: '%s'. Disabling firmware checks.\n" "$fwUpdateCheckState" _DisableFWAutoUpdateChecks_ fi @@ -11745,13 +11797,15 @@ then fi ##----------------------------------------## -## Modified by Martinski W. [2025-May-11] ## +## Modified by Martinski W. [2026-Feb-22] ## ##----------------------------------------## if [ $# -gt 0 ] then if ! _AcquireLock_ cliOptsLock then Say "Exiting..." ; exit 1 ; fi + [ "$1" = "amtmupdate" ] && isVerbose=false + inMenuMode=false _DoInitializationStartup_ "$1" _ConfirmCronJobForFWAutoUpdates_ "$1" @@ -11792,6 +11846,11 @@ then _ReleaseLock_ cliFileLock fi ;; + amtmupdate) + shift + ScriptUpdateFromAMTM "$@" + _DoExit_ "$?" + ;; develop) _ChangeToDev_ ;; stable) _ChangeToStable_ diff --git a/README.md b/README.md index 0d7037c..cff1961 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # MerlinAU - AsusWRT-Merlin Firmware Auto Updater -## v1.5.9 -## 2026-Feb-07 +## v1.5.10 +## 2026-Feb-22 ## WebUI: ![image](https://github.com/user-attachments/assets/9c1dff99-9c13-491b-a7fa-aff924d5f02e) From 4a10e65a4d7da30eb529f61a463cc192992a164e Mon Sep 17 00:00:00 2001 From: Joel Samson Date: Mon, 23 Feb 2026 08:19:07 -0500 Subject: [PATCH 02/10] Bump version from 1.5.9 to 1.5.10 Bump version from 1.5.9 to 1.5.10 --- version.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/version.txt b/version.txt index 2b26b8d..f0ed379 100644 --- a/version.txt +++ b/version.txt @@ -1 +1 @@ -1.5.9 +1.5.10 From 9c98095596a40540468206a45336e24aa5c8646d Mon Sep 17 00:00:00 2001 From: ExtremeFiretop Date: Tue, 24 Feb 2026 09:36:42 -0500 Subject: [PATCH 03/10] Update Node Email Titles Update Node Email Titles --- MerlinAU.sh | 6 +++--- README.md | 4 ++-- version.txt | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/MerlinAU.sh b/MerlinAU.sh index 5bbf8c1..83f78e3 100644 --- a/MerlinAU.sh +++ b/MerlinAU.sh @@ -9,8 +9,8 @@ set -u ## Set version for each Production Release ## -readonly SCRIPT_VERSION=1.5.10 -readonly SCRIPT_VERSTAG="26022202" +readonly SCRIPT_VERSION=1.6.0 +readonly SCRIPT_VERSTAG="26022409" readonly SCRIPT_NAME="MerlinAU" ## Set to "master" for Production Releases ## SCRIPT_BRANCH="dev" @@ -3227,7 +3227,7 @@ _CreateEMailContent_() then subjectStrTag="Script Update Status" fi if [ -s "$tempNodeEMailList" ] - then subjectStr="$subjectStrTag for $node_lan_hostname" + then subjectStr="$subjectStrTag for AiMesh Node(s)" else subjectStr="$subjectStrTag for $MODEL_ID" fi diff --git a/README.md b/README.md index cff1961..6e1a402 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # MerlinAU - AsusWRT-Merlin Firmware Auto Updater -## v1.5.10 -## 2026-Feb-22 +## v1.6.0 +## 2026-Feb-24 ## WebUI: ![image](https://github.com/user-attachments/assets/9c1dff99-9c13-491b-a7fa-aff924d5f02e) diff --git a/version.txt b/version.txt index f0ed379..dc1e644 100644 --- a/version.txt +++ b/version.txt @@ -1 +1 @@ -1.5.10 +1.6.0 From 59dda9178c344ac263934fe9b0a3247217acabe0 Mon Sep 17 00:00:00 2001 From: ExtremeFiretop Date: Tue, 24 Feb 2026 09:47:16 -0500 Subject: [PATCH 04/10] Changing Title Changing Title --- MerlinAU.sh | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/MerlinAU.sh b/MerlinAU.sh index 83f78e3..cd2fc89 100644 --- a/MerlinAU.sh +++ b/MerlinAU.sh @@ -3218,11 +3218,13 @@ _CreateEMailContent_() if [ $# -eq 0 ] || [ -z "$1" ] ; then return 1 ; fi local fwInstalledVersion fwNewUpdateVersion local savedInstalledVersion savedNewUpdateVersion - local subjectStr emailBodyTitle="" release_version + local subjectStrTag subjectStr emailBodyTitle="" release_version rm -f "$tempEMailContent" "$tempEMailBodyMsg" - local subjectStrTag="F/W Update Status" + if [ -s "$tempNodeEMailList" ] + then subjectStrTag="F/W Update Available" + else subjectStrTag="F/W Update Status" if echo "$1" | grep -q '._SCRIPT_UPDATE_.' then subjectStrTag="Script Update Status" fi From 4022300c1663ec565c7c2e7b1b42bae2a8140579 Mon Sep 17 00:00:00 2001 From: Joel Samson Date: Tue, 24 Feb 2026 15:37:01 -0500 Subject: [PATCH 05/10] Fix missing typo Fix missing typo --- MerlinAU.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/MerlinAU.sh b/MerlinAU.sh index cd2fc89..1c77e14 100644 --- a/MerlinAU.sh +++ b/MerlinAU.sh @@ -3225,6 +3225,7 @@ _CreateEMailContent_() if [ -s "$tempNodeEMailList" ] then subjectStrTag="F/W Update Available" else subjectStrTag="F/W Update Status" + fi if echo "$1" | grep -q '._SCRIPT_UPDATE_.' then subjectStrTag="Script Update Status" fi From 1d4923cb5458312a32933a90595f89eecf136340 Mon Sep 17 00:00:00 2001 From: Martinski4GitHub <119833648+Martinski4GitHub@users.noreply.github.com> Date: Sun, 15 Mar 2026 09:47:05 -0700 Subject: [PATCH 06/10] Message Improvement Added syslog message to show the add-on version number at startup. --- MerlinAU.sh | 5 +++-- README.md | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/MerlinAU.sh b/MerlinAU.sh index 1c77e14..77cf357 100644 --- a/MerlinAU.sh +++ b/MerlinAU.sh @@ -4,13 +4,13 @@ # # Original Creation Date: 2023-Oct-01 by @ExtremeFiretop. # Official Co-Author: @Martinski W. - Date: 2023-Nov-01 -# Last Modified: 2026-Feb-22 +# Last Modified: 2026-Mar-15 ################################################################### set -u ## Set version for each Production Release ## readonly SCRIPT_VERSION=1.6.0 -readonly SCRIPT_VERSTAG="26022409" +readonly SCRIPT_VERSTAG="26031508" readonly SCRIPT_NAME="MerlinAU" ## Set to "master" for Production Releases ## SCRIPT_BRANCH="dev" @@ -10113,6 +10113,7 @@ _CheckForMinimumRequirements_() ##-------------------------------------## _DoStartupInit_() { + Say "$SCRIPT_NAME $SCRIPT_VERSION starting up" _CreateDirPaths_ _InitCustomDefaultsConfig_ _InitCustomUserSettings_ diff --git a/README.md b/README.md index 6e1a402..818b0bb 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # MerlinAU - AsusWRT-Merlin Firmware Auto Updater ## v1.6.0 -## 2026-Feb-24 +## 2026-Mar-15 ## WebUI: ![image](https://github.com/user-attachments/assets/9c1dff99-9c13-491b-a7fa-aff924d5f02e) From 3d26ca6adf90658c9f90b9e8f4997c97e64a585e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 16 Mar 2026 23:43:48 +0000 Subject: [PATCH 07/10] Bump softprops/action-gh-release in the all-actions group Bumps the all-actions group with 1 update: [softprops/action-gh-release](https://github.com/softprops/action-gh-release). Updates `softprops/action-gh-release` from 2.5.0 to 2.6.1 - [Release notes](https://github.com/softprops/action-gh-release/releases) - [Changelog](https://github.com/softprops/action-gh-release/blob/master/CHANGELOG.md) - [Commits](https://github.com/softprops/action-gh-release/compare/v2.5.0...v2.6.1) --- updated-dependencies: - dependency-name: softprops/action-gh-release dependency-version: 2.6.1 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: all-actions ... Signed-off-by: dependabot[bot] --- .github/workflows/Create-NewReleases.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/Create-NewReleases.yml b/.github/workflows/Create-NewReleases.yml index 3a72afc..af4cf2f 100644 --- a/.github/workflows/Create-NewReleases.yml +++ b/.github/workflows/Create-NewReleases.yml @@ -85,7 +85,7 @@ jobs: git push origin ${{ steps.nextver.outputs.tag }} - name: Create Release with Automated Release Notes - uses: softprops/action-gh-release@v2.5.0 + uses: softprops/action-gh-release@v2.6.1 with: token: ${{ secrets.GITHUB_TOKEN }} tag_name: ${{ steps.nextver.outputs.tag }} From 0d318c5da8baea50bf66733f1d419341b4b9e937 Mon Sep 17 00:00:00 2001 From: Martinski4GitHub <119833648+Martinski4GitHub@users.noreply.github.com> Date: Tue, 17 Mar 2026 00:45:13 -0700 Subject: [PATCH 08/10] Maximum Lock Timeout for AMTM Call Set Maximum LOCK Timeout to short value so that the AMTM call returns quickly when LOCK already exists. --- MerlinAU.sh | 20 +++++++++++++------- README.md | 2 +- 2 files changed, 14 insertions(+), 8 deletions(-) diff --git a/MerlinAU.sh b/MerlinAU.sh index 77cf357..5ddaf63 100644 --- a/MerlinAU.sh +++ b/MerlinAU.sh @@ -4,13 +4,13 @@ # # Original Creation Date: 2023-Oct-01 by @ExtremeFiretop. # Official Co-Author: @Martinski W. - Date: 2023-Nov-01 -# Last Modified: 2026-Mar-15 +# Last Modified: 2026-Mar-17 ################################################################### set -u ## Set version for each Production Release ## readonly SCRIPT_VERSION=1.6.0 -readonly SCRIPT_VERSTAG="26031508" +readonly SCRIPT_VERSTAG="26031700" readonly SCRIPT_NAME="MerlinAU" ## Set to "master" for Production Releases ## SCRIPT_BRANCH="dev" @@ -429,6 +429,7 @@ _ReleaseLock_() } ## Defaults ## +LockSleepDelaySecs=5 LockMaxTimeoutSecs=120 LockFileMaxAgeSecs=600 #10-minutes# @@ -439,8 +440,9 @@ then LockFileMaxAgeSecs=1200 else case "$1" in - run_now|resetLockFile) - LockMaxTimeoutSecs=3 + run_now|amtmupdate|resetLockFile) + LockSleepDelaySecs=2 + LockMaxTimeoutSecs=1 LockFileMaxAgeSecs=1200 ;; startup|addCronJob) @@ -457,7 +459,7 @@ _AcquireLock_() { local retCode waitTimeoutSecs local lockFileSecs ageOfLockSecs oldPID - local lockTypeReq lockTypeFound + local lockTypeReq lockTypeFound savedVerbose if [ $# -gt 0 ] && [ -n "$1" ] then lockTypeReq="$1" @@ -474,6 +476,8 @@ _AcquireLock_() retCode=1 lockTypeFound="" waitTimeoutSecs=0 + savedVerbose="$isVerbose" + isVerbose=true while true do @@ -515,13 +519,15 @@ _AcquireLock_() then Say "Lock Found [$lockTypeFound: $ageOfLockSecs secs]. Waiting for script [PID=$oldPID] to exit [Timer: $waitTimeoutSecs secs]" fi - sleep 5 - waitTimeoutSecs="$((waitTimeoutSecs + 5))" + sleep "$LockSleepDelaySecs" + waitTimeoutSecs="$((waitTimeoutSecs + LockSleepDelaySecs))" else Say "${REDct}**ERROR**${NOct}: The shell script ${ScriptFileName} [PID=$oldPID] is already running [$lockTypeFound: $ageOfLockSecs secs]" retCode=1 ; break fi done + + isVerbose="$savedVerbose" return "$retCode" } diff --git a/README.md b/README.md index 818b0bb..c1eb7be 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # MerlinAU - AsusWRT-Merlin Firmware Auto Updater ## v1.6.0 -## 2026-Mar-15 +## 2026-Mar-17 ## WebUI: ![image](https://github.com/user-attachments/assets/9c1dff99-9c13-491b-a7fa-aff924d5f02e) From 35a4b9a860b138bcba36d5c67fd256fe8d724139 Mon Sep 17 00:00:00 2001 From: Martinski4GitHub <119833648+Martinski4GitHub@users.noreply.github.com> Date: Wed, 18 Mar 2026 01:54:22 -0700 Subject: [PATCH 09/10] Added F/W Update Lock File Added a mutually exclusive, non-blocking FLOCK mechanism in MerlinAU so that whenever AMTM initiates script updates or MerlinAU starts an F/W Update, each one can check the FLOCK mutex to make sure only one of them can perform its intended operation without "stepping on each other's toes" and avoid any possible corruption of a script update. --- MerlinAU.sh | 87 +++++++++++++++++++++++++++++++++++++++++++++++------ README.md | 2 +- 2 files changed, 79 insertions(+), 10 deletions(-) diff --git a/MerlinAU.sh b/MerlinAU.sh index 5ddaf63..85f19b9 100644 --- a/MerlinAU.sh +++ b/MerlinAU.sh @@ -4,13 +4,13 @@ # # Original Creation Date: 2023-Oct-01 by @ExtremeFiretop. # Official Co-Author: @Martinski W. - Date: 2023-Nov-01 -# Last Modified: 2026-Mar-17 +# Last Modified: 2026-Mar-18 ################################################################### set -u ## Set version for each Production Release ## readonly SCRIPT_VERSION=1.6.0 -readonly SCRIPT_VERSTAG="26031700" +readonly SCRIPT_VERSTAG="26031800" readonly SCRIPT_NAME="MerlinAU" ## Set to "master" for Production Releases ## SCRIPT_BRANCH="dev" @@ -476,8 +476,7 @@ _AcquireLock_() retCode=1 lockTypeFound="" waitTimeoutSecs=0 - savedVerbose="$isVerbose" - isVerbose=true + savedVerbose="$isVerbose" ; isVerbose=true while true do @@ -531,6 +530,66 @@ _AcquireLock_() return "$retCode" } +##-------------------------------------## +## Added by Martinski W. [2026-Mar-18] ## +##-------------------------------------## +fwupMutexFLock_FD=576 +fwupMutexFLock_FN="/tmp/var/${ScriptFNameTag}_FW_Update.FLock" + +_ReleaseMutexFLock_() +{ + printf '' > "$fwupMutexFLock_FN" + flock -u "$fwupMutexFLock_FD" 2>/dev/null +} + +##-------------------------------------## +## Added by Martinski W. [2026-Mar-18] ## +##-------------------------------------## +#--------------------------------------------------------------# +# This is a mutually exclusive, non-blocking FLOCK mechanism +# to be used when MerlinAU is perforning a F/W Update so that +# AMTM can check and prevent running automatic script updates +# while the F/W Update is in progress. +#--------------------------------------------------------------# +_AcquireMutexFLock_() +{ + local retCode savedVerbose + local procInfo procName="" procIDno="" procIDof="" + + savedVerbose="$isVerbose" ; isVerbose=true + + if [ -s "$fwupMutexFLock_FN" ] + then + procInfo="$(head -n1 "$fwupMutexFLock_FN")" + procName="$(echo "$procInfo" | cut -d'|' -f1)" + procIDno="$(echo "$procInfo" | cut -d'|' -f2)" + if [ -n "$procName" ] && [ -n "$procIDno" ] + then procIDof="$(pidof "$procName")" + fi + if [ -z "$procIDof" ] || ! echo "$procIDof" | grep -qow "$procIDno" + then + Say "Stale F/W Update Lock Found. Resetting lock file..." + _ReleaseMutexFLock_ + fi + fi + + [ ! -s "$fwupMutexFLock_FN" ] && \ + eval exec "$fwupMutexFLock_FD>$fwupMutexFLock_FN" + + if flock -x -n "$fwupMutexFLock_FD" 2>/dev/null + then + printf "$(basename "$0")|$$\n" > "$fwupMutexFLock_FN" + retCode=0 + else + procInfo="$(head -n1 "$fwupMutexFLock_FN")" + Say "${REDct}**ERROR**${NOct}: Another process [$procInfo] has the F/W Update Lock file." + retCode=1 + fi + + isVerbose="$savedVerbose" + return "$retCode" +} + ##-------------------------------------## ## Added by Martinski W. [2025-Sep-01] ## ##-------------------------------------## @@ -9126,10 +9185,12 @@ _RunOfflineUpdateNow_() FW_DL_FPATH="${FW_ZIP_DIR}/${FW_FileName}.${extension}" _GnutonBuildSelection_ fi - if _AcquireLock_ cliFileLock + if _AcquireLock_ cliFileLock && \ + _AcquireMutexFLock_ then _RunFirmwareUpdateNow_ _ReleaseLock_ cliFileLock + _ReleaseMutexFLock_ fi _ClearOfflineUpdateState_ else @@ -9953,10 +10014,12 @@ _PostRebootRunNow_() Say "END of $logMsg [$curWaitDelaySecs sec.]" sleep 30 ## Let's wait a bit & proceed ## - if _AcquireLock_ cliFileLock + if _AcquireLock_ cliFileLock && \ + _AcquireMutexFLock_ then _RunFirmwareUpdateNow_ _ReleaseLock_ cliFileLock + _ReleaseMutexFLock_ fi } @@ -11615,10 +11678,12 @@ _MainMenu_() HIDE_ROUTER_SECTION=true fi ;; - 1) if _AcquireLock_ cliFileLock + 1) if _AcquireLock_ cliFileLock && \ + _AcquireMutexFLock_ then _RunFirmwareUpdateNow_ _ReleaseLock_ cliFileLock + _ReleaseMutexFLock_ FlashStarted=false fi ;; @@ -11822,10 +11887,12 @@ then case "$1" in run_now) - if _AcquireLock_ cliFileLock + if _AcquireLock_ cliFileLock && \ + _AcquireMutexFLock_ then _RunFirmwareUpdateNow_ _ReleaseLock_ cliFileLock + _ReleaseMutexFLock_ fi ;; processNodes) _ProcessMeshNodes_ false @@ -11914,7 +11981,8 @@ then ;; "${SCRIPT_NAME}checkfwupdate" | \ "${SCRIPT_NAME}checkfwupdate_bypassDays") - if _AcquireLock_ cliFileLock + if _AcquireLock_ cliFileLock && \ + _AcquireMutexFLock_ then if [ "$3" = "${SCRIPT_NAME}checkfwupdate_bypassDays" ] then bypassPostponedDays=true @@ -11923,6 +11991,7 @@ then webguiMode=true _RunFirmwareUpdateNow_ _ReleaseLock_ cliFileLock + _ReleaseMutexFLock_ fi ;; "${SCRIPT_NAME}scrptupdate" | \ diff --git a/README.md b/README.md index c1eb7be..133867b 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # MerlinAU - AsusWRT-Merlin Firmware Auto Updater ## v1.6.0 -## 2026-Mar-17 +## 2026-Mar-18 ## WebUI: ![image](https://github.com/user-attachments/assets/9c1dff99-9c13-491b-a7fa-aff924d5f02e) From 4bb2e9996f0056aa9b9f94e29f8c28d906dc837e Mon Sep 17 00:00:00 2001 From: Martinski4GitHub <119833648+Martinski4GitHub@users.noreply.github.com> Date: Wed, 18 Mar 2026 23:48:04 -0700 Subject: [PATCH 10/10] Fine-Tuning & Improvements WRT the FLock Just some code improvements and fine-tuning WRT the FLock mechanism. --- MerlinAU.sh | 42 ++++++++++++++++++++++++++++++------------ README.md | 2 +- 2 files changed, 31 insertions(+), 13 deletions(-) diff --git a/MerlinAU.sh b/MerlinAU.sh index 85f19b9..e5bd398 100644 --- a/MerlinAU.sh +++ b/MerlinAU.sh @@ -10,7 +10,7 @@ set -u ## Set version for each Production Release ## readonly SCRIPT_VERSION=1.6.0 -readonly SCRIPT_VERSTAG="26031800" +readonly SCRIPT_VERSTAG="26031823" readonly SCRIPT_NAME="MerlinAU" ## Set to "master" for Production Releases ## SCRIPT_BRANCH="dev" @@ -409,7 +409,7 @@ readonly LockTypeRegEx="(cliMenuLock|cliOptsLock|cliFileLock)" _FindLockFileTypes_() { grep -woE "$LockTypeRegEx" "$LockFilePath" | tr '\n' ' ' | sed 's/[ ]*$//' ; } -_ReleaseLock_() +_ReleaseLock_() { local lockType if [ $# -eq 0 ] || [ -z "$1" ] @@ -421,7 +421,7 @@ _ReleaseLock_() then if [ -z "$lockType" ] then sed -i "/^$$|/d" "$LockFilePath" - else sed -i "/.*|${1}$/d" "$LockFilePath" + else sed -i "/^$$|${1}$/d" "$LockFilePath" fi [ -s "$LockFilePath" ] && return 0 fi @@ -535,11 +535,19 @@ _AcquireLock_() ##-------------------------------------## fwupMutexFLock_FD=576 fwupMutexFLock_FN="/tmp/var/${ScriptFNameTag}_FW_Update.FLock" +fwupMutexFLock_OK=false #DO NOT have FLock# _ReleaseMutexFLock_() { + if [ $# -gt 0 ] && \ + [ "$1" = "checkLockOK" ] && \ + [ "$fwupMutexFLock_OK" != "true" ] + then return 0 + fi + printf '' > "$fwupMutexFLock_FN" flock -u "$fwupMutexFLock_FD" 2>/dev/null + fwupMutexFLock_OK=false } ##-------------------------------------## @@ -554,7 +562,7 @@ _ReleaseMutexFLock_() _AcquireMutexFLock_() { local retCode savedVerbose - local procInfo procName="" procIDno="" procIDof="" + local procInfo procName procIDno procIDof="" savedVerbose="$isVerbose" ; isVerbose=true @@ -566,7 +574,8 @@ _AcquireMutexFLock_() if [ -n "$procName" ] && [ -n "$procIDno" ] then procIDof="$(pidof "$procName")" fi - if [ -z "$procIDof" ] || ! echo "$procIDof" | grep -qow "$procIDno" + if [ -z "$procIDof" ] || \ + ! echo "$procIDof" | grep -qow "$procIDno" then Say "Stale F/W Update Lock Found. Resetting lock file..." _ReleaseMutexFLock_ @@ -579,11 +588,14 @@ _AcquireMutexFLock_() if flock -x -n "$fwupMutexFLock_FD" 2>/dev/null then printf "$(basename "$0")|$$\n" > "$fwupMutexFLock_FN" - retCode=0 + retCode=0 ; fwupMutexFLock_OK=true else procInfo="$(head -n1 "$fwupMutexFLock_FN")" + if [ -n "$procInfo" ] + then procInfo="$(echo "$procInfo" | sed 's/|/, PID=/')" + fi Say "${REDct}**ERROR**${NOct}: Another process [$procInfo] has the F/W Update Lock file." - retCode=1 + retCode=1 ; fwupMutexFLock_OK=false fi isVerbose="$savedVerbose" @@ -603,7 +615,9 @@ _DoExit_() { local exitCode=0 [ $# -gt 0 ] && [ -n "$1" ] && exitCode="$1" - _ReleaseLock_ ; exit "$exitCode" + _ReleaseLock_ + _ReleaseMutexFLock_ checkLockOK + exit "$exitCode" } ##-------------------------------------## @@ -9189,9 +9203,11 @@ _RunOfflineUpdateNow_() _AcquireMutexFLock_ then _RunFirmwareUpdateNow_ - _ReleaseLock_ cliFileLock - _ReleaseMutexFLock_ + else + _WaitForEnterKey_ fi + _ReleaseLock_ cliFileLock + _ReleaseMutexFLock_ checkLockOK _ClearOfflineUpdateState_ else _ClearOfflineUpdateState_ 1 @@ -11682,10 +11698,12 @@ _MainMenu_() _AcquireMutexFLock_ then _RunFirmwareUpdateNow_ - _ReleaseLock_ cliFileLock - _ReleaseMutexFLock_ FlashStarted=false + else + _WaitForEnterKey_ fi + _ReleaseLock_ cliFileLock + _ReleaseMutexFLock_ checkLockOK ;; 2) _GetLoginCredentials_ ;; diff --git a/README.md b/README.md index 133867b..6aa619f 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # MerlinAU - AsusWRT-Merlin Firmware Auto Updater ## v1.6.0 -## 2026-Mar-18 +## 2026-Mar-19 ## WebUI: ![image](https://github.com/user-attachments/assets/9c1dff99-9c13-491b-a7fa-aff924d5f02e)