From 26cd324553b07672926a5a21fb07c3344c438516 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 3 Jun 2026 17:08:40 +0000 Subject: [PATCH 1/9] Bump actions/checkout from 6.0.2 to 6.0.3 in the all-actions group Bumps the all-actions group with 1 update: [actions/checkout](https://github.com/actions/checkout). Updates `actions/checkout` from 6.0.2 to 6.0.3 - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](https://github.com/actions/checkout/compare/v6.0.2...v6.0.3) --- updated-dependencies: - dependency-name: actions/checkout dependency-version: 6.0.3 dependency-type: direct:production update-type: version-update:semver-patch 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 0a398895..3000045d 100644 --- a/.github/workflows/Create-NewReleases.yml +++ b/.github/workflows/Create-NewReleases.yml @@ -15,7 +15,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout source code - uses: actions/checkout@v6.0.2 + uses: actions/checkout@v6.0.3 with: fetch-depth: 0 ref: 'main' # Ensure we're tagging the main branch after the merge From e4a99ae155b8ac66df19b81e2c2faf5529e7cb8e Mon Sep 17 00:00:00 2001 From: ExtremeFiretop Date: Wed, 10 Jun 2026 17:39:13 -0400 Subject: [PATCH 2/9] Suggestion to Resolve Duplicate Mount-points Suggestion to Resolve Duplicate Mount-points --- MerlinAU.sh | 173 +++++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 152 insertions(+), 21 deletions(-) diff --git a/MerlinAU.sh b/MerlinAU.sh index 237e1a54..958343aa 100644 --- a/MerlinAU.sh +++ b/MerlinAU.sh @@ -9,8 +9,8 @@ set -u ## Set version for each Production Release ## -readonly SCRIPT_VERSION=1.6.3 -readonly SCRIPT_VERSTAG="26052123" +readonly SCRIPT_VERSION=1.6.4 +readonly SCRIPT_VERSTAG="26061019" readonly SCRIPT_NAME="MerlinAU" ## Set to "master" for Production Releases ## SCRIPT_BRANCH="master" @@ -546,6 +546,12 @@ fwupMutexFLock_FD=576 fwupMutexFLock_FN="/tmp/var/${ScriptFNameTag}_FW_Update.FLock" fwupMutexFLock_OK=false #DO NOT have FLock# +##-------------------------------------## +## Added for initialization operations ## +##-------------------------------------## +initMutexFLock_FD=577 +initMutexFLock_FN="/tmp/var/${ScriptFNameTag}_Initialization.FLock" + _ReleaseMutexFLock_() { if [ $# -gt 0 ] && \ @@ -2223,7 +2229,7 @@ readonly POST_UPDATE_EMAIL_SCRIPT_HOOK="[ -x $ScriptFilePath ] && $POST_UPDATE_E _CleanUpOldLogFiles_() { [ ! -d "$FW_LOG_DIR" ] && return 1 - local numLogFiles topLogFile + local numLogFiles topFile savedTopFile numLogFiles="$(ls -1lt "$FW_LOG_DIR"/*.log 2>/dev/null | wc -l)" # Leave one log file (if any available) # @@ -2231,16 +2237,31 @@ _CleanUpOldLogFiles_() # Save the most recent log file # topFile="$(ls -1t "$FW_LOG_DIR"/*.log 2>/dev/null | head -n1)" - [ -n "$topFile" ] && mv -f "$topFile" "${topFile}.SAVED.TEMP.LOG" + + if [ -n "$topFile" ] + then + savedTopFile="${topFile}.SAVED.$$.TEMP.LOG" + + if ! mv -f "$topFile" "$savedTopFile" + then + return 1 + fi + fi # Delete logs older than 30 days # /usr/bin/find -L "$FW_LOG_DIR" -name '*.log' -mtime +30 -exec rm {} \; # Restore the most recent log file # - [ -n "$topFile" ] && mv -f "${topFile}.SAVED.TEMP.LOG" "$topFile" -} + if [ -n "$topFile" ] && [ -f "$savedTopFile" ] + then + if ! mv -f "$savedTopFile" "$topFile" + then + return 1 + fi + fi -_CleanUpOldLogFiles_ + return 0 +} ##----------------------------------------## ## Modified by Martinski W. [2024-Jan-27] ## @@ -2860,7 +2881,7 @@ _CurlFileDownload_() then return 1 fi local retCode=1 - local tempFilePathDL="${2}.DL.TMP" + local tempFilePathDL="${2}.DL.$$.TMP" local srceFilePathDL="${SCRIPT_URL_REPO}/$1" curl -LSs --retry 4 --retry-delay 5 --retry-connrefused \ @@ -2877,7 +2898,11 @@ _CurlFileDownload_() then updatedWebUIPage=true else updatedWebUIPage=false fi - mv -f "$tempFilePathDL" "$2" + if ! mv -f "$tempFilePathDL" "$2" + then + rm -f "$tempFilePathDL" + return 1 + fi retCode=0 fi @@ -12054,8 +12079,8 @@ _Gnuton_Check_Webs_Update_Script_() local theWebsUpdateFile="webs_update.sh" local fixedGnutonWebsUpdateFilePath="${SETTINGS_DIR}/$theWebsUpdateFile" - local dwnldGnutonWebsUpdateFilePath="${SETTINGS_DIR}/${theWebsUpdateFile}.GNUTON" - local localVersTag remoteVersTag + local dwnldGnutonWebsUpdateFilePath="${SETTINGS_DIR}/${theWebsUpdateFile}.GNUTON.$$" + local localVersTag remoteVersTag diffRC # Get local VERSTAG (if any) # localVersTag="$(_Get_GnutonWebUpdate_ScriptVersTag_ "$FW_UpdateCheckScript")" @@ -12064,35 +12089,137 @@ _Gnuton_Check_Webs_Update_Script_() # Get the fixed version of the script targeted for Gnuton F/W # if _CurlFileDownload_ "gnuton_webs_update.sh" "$dwnldGnutonWebsUpdateFilePath" then - chmod 755 "$dwnldGnutonWebsUpdateFilePath" + if ! chmod 755 "$dwnldGnutonWebsUpdateFilePath" + then + Say "${REDct}**ERROR**${NOct}: Unable to set permissions on the downloaded GNUton webs_update.sh file." + rm -f "$dwnldGnutonWebsUpdateFilePath" + return 1 + fi + remoteVersTag="$(_Get_GnutonWebUpdate_ScriptVersTag_ "$dwnldGnutonWebsUpdateFilePath")" [ -z "$remoteVersTag" ] && remoteVersTag=0 else - return 1 #NOT available so do nothing# + rm -f "$dwnldGnutonWebsUpdateFilePath" + return 1 + fi + + # Distinguish files that differ from an actual comparison error. + diff -q "$FW_UpdateCheckScript" "$dwnldGnutonWebsUpdateFilePath" >/dev/null 2>&1 + + diffRC=$? + + if [ "$diffRC" -gt 1 ] + then + Say "${REDct}**ERROR**${NOct}: Unable to compare the GNUton webs_update.sh files." + rm -f "$dwnldGnutonWebsUpdateFilePath" + return 1 fi # (Re)bind/mount only if remote is newer version OR files differ # if [ "$remoteVersTag" -gt "$localVersTag" ] || \ - ! diff -q "$FW_UpdateCheckScript" "$dwnldGnutonWebsUpdateFilePath" >/dev/null 2>&1 + [ "$diffRC" -eq 1 ] then - umount "$FW_UpdateCheckScript" 2>/dev/null - mv -f "$dwnldGnutonWebsUpdateFilePath" "$fixedGnutonWebsUpdateFilePath" - mount -o bind "$fixedGnutonWebsUpdateFilePath" "$FW_UpdateCheckScript" + # Remove all existing bind-mount layers, including duplicates. + while awk -v target="$FW_UpdateCheckScript" ' + $2 == target { found=1 } + END { exit !found } + ' /proc/mounts + do + if ! umount "$FW_UpdateCheckScript" + then + Say "${REDct}**ERROR**${NOct}: Unable to unmount \"$FW_UpdateCheckScript\"." + rm -f "$dwnldGnutonWebsUpdateFilePath" + return 1 + fi + done + + if ! mv -f "$dwnldGnutonWebsUpdateFilePath" "$fixedGnutonWebsUpdateFilePath" + then + Say "${REDct}**ERROR**${NOct}: Unable to install the fixed GNUton webs_update.sh file." + rm -f "$dwnldGnutonWebsUpdateFilePath" + return 1 + fi + + if [ ! -f "$fixedGnutonWebsUpdateFilePath" ] + then + Say "${REDct}**ERROR**${NOct}: GNUton webs_update.sh source file is missing before bind mount." + return 1 + fi + + if [ ! -e "$FW_UpdateCheckScript" ] + then + Say "${REDct}**ERROR**${NOct}: Bind-mount target \"$FW_UpdateCheckScript\" does not exist." + return 1 + fi + + if ! mount -o bind \ + "$fixedGnutonWebsUpdateFilePath" \ + "$FW_UpdateCheckScript" + then + Say "${REDct}**ERROR**${NOct}: Unable to bind-mount the fixed GNUton webs_update.sh file." + return 1 + fi + Say "${YLWct}Set up a fixed version of the \"${theWebsUpdateFile}\" script file.${NOct}" else rm -f "$dwnldGnutonWebsUpdateFilePath" fi + + return 0 } +##-------------------------------------## +## Initialization operations mutex ## +##-------------------------------------## +# Serialize startup-time operations that use shared log and GNUton files. +_RunLockedInitializationChecks_() +{ + local retCode=0 + + [ ! -d /tmp/var ] && mkdir -p /tmp/var + + # Open the dedicated lock file on its own file descriptor. + if ! eval "exec ${initMutexFLock_FD}>\"${initMutexFLock_FN}\"" + then + Say "${REDct}**ERROR**${NOct}: Unable to open the initialization lock file." + return 1 + fi + + # Use a blocking exclusive lock so concurrent MerlinAU processes wait. + if ! flock -x "$initMutexFLock_FD" 2>/dev/null + then + Say "${REDct}**ERROR**${NOct}: Unable to acquire the initialization lock." + eval "exec ${initMutexFLock_FD}>&-" + return 1 + fi + + # These operations access shared paths and must not overlap. + if [ -d "$FW_LOG_DIR" ] && \ + ! _CleanUpOldLogFiles_ + then + Say "${YLWct}WARNING:${NOct} Unable to clean up old firmware-update log files." + retCode=1 + fi + + checkWebsUpdateScriptForGnuton="$isGNUtonFW" + if ! _Gnuton_Check_Webs_Update_Script_ + then + Say "${YLWct}WARNING:${NOct} Unable to check or install the GNUton webs_update.sh patch." + retCode=1 + fi + + flock -u "$initMutexFLock_FD" 2>/dev/null + eval "exec ${initMutexFLock_FD}>&-" + + return "$retCode" +} + + if [ "$SCRIPT_BRANCH" = "master" ] then SCRIPT_VERS_INFO="" else SCRIPT_VERS_INFO="[$versionDev_TAG]" fi -## Set variable to 'false' to stop the check ## -checkWebsUpdateScriptForGnuton="$isGNUtonFW" -_Gnuton_Check_Webs_Update_Script_ - FW_InstalledVersion="$(_GetCurrentFWInstalledLongVersion_)" FW_InstalledVerStr="${GRNct}${FW_InstalledVersion}${NOct}" FW_NewUpdateVerInit=TBD @@ -12107,6 +12234,8 @@ then then Say "Exiting..." ; exit 1 fi + _RunLockedInitializationChecks_ + inMenuMode=true _DoInitializationStartup_ _CheckFor_VersionFile_ @@ -12134,6 +12263,8 @@ then if ! _AcquireLock_ cliOptsLock then Say "Exiting..." ; exit 1 ; fi + _RunLockedInitializationChecks_ + [ "$1" = "amtmupdate" ] && isVerbose=false inMenuMode=false From eb173d9f9590ffd1fd2db25788d96aa3448c3355 Mon Sep 17 00:00:00 2001 From: ExtremeFiretop Date: Wed, 10 Jun 2026 17:40:26 -0400 Subject: [PATCH 3/9] Update Version Update Version --- README.md | 4 ++-- version.txt | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 9f1dd278..6904f844 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # MerlinAU - AsusWRT-Merlin Firmware Auto Updater -## v1.6.3 -## 2026-May-22 +## v1.6.4 +## 2026-June-10 ## WebUI: image diff --git a/version.txt b/version.txt index 266146b8..9edc58bb 100644 --- a/version.txt +++ b/version.txt @@ -1 +1 @@ -1.6.3 +1.6.4 From a42d4acb376aef00a9feddcadcb1a2f7e79c72b8 Mon Sep 17 00:00:00 2001 From: ExtremeFiretop Date: Wed, 10 Jun 2026 17:42:29 -0400 Subject: [PATCH 4/9] Update Dates Update Dates --- MerlinAU.sh | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/MerlinAU.sh b/MerlinAU.sh index 958343aa..1df6fb2d 100644 --- a/MerlinAU.sh +++ b/MerlinAU.sh @@ -2223,9 +2223,9 @@ readonly POST_REBOOT_SCRIPT_HOOK="[ -x $ScriptFilePath ] && $POST_REBOOT_SCRIPT_ readonly POST_UPDATE_EMAIL_SCRIPT_JOB="$ScriptFilePath postUpdateEmail &" readonly POST_UPDATE_EMAIL_SCRIPT_HOOK="[ -x $ScriptFilePath ] && $POST_UPDATE_EMAIL_SCRIPT_JOB $hookScriptTagStr" -##-------------------------------------## -## Added by Martinski W. [2026-Feb-07] ## -##-------------------------------------## +##------------------------------------------## +## Modified by ExtremeFiretop [2026-Jun-10] ## +##------------------------------------------## _CleanUpOldLogFiles_() { [ ! -d "$FW_LOG_DIR" ] && return 1 @@ -12057,9 +12057,9 @@ _DoInitializationStartup_() _SetDefaultBuildType_ } -##----------------------------------------## -## Modified by Martinski W. [2025-Jul-29] ## -##----------------------------------------## +##------------------------------------------## +## Modified by ExtremeFiretop [2026-Jun-10] ## +##------------------------------------------## ####################################################################### # TEMPORARY hack to check if the Gnuton F/W built-in 'webs_update.sh' # script is the most recent version that includes required fixes. @@ -12168,9 +12168,9 @@ _Gnuton_Check_Webs_Update_Script_() return 0 } -##-------------------------------------## -## Initialization operations mutex ## -##-------------------------------------## +##---------------------------------------## +## Added by ExtremeFiretop [2026-Jun-10] ## +##---------------------------------------## # Serialize startup-time operations that use shared log and GNUton files. _RunLockedInitializationChecks_() { From b2476f9bef82a52881a00bcfa84b41d2bcde6eae Mon Sep 17 00:00:00 2001 From: Martinski4GitHub <119833648+Martinski4GitHub@users.noreply.github.com> Date: Thu, 11 Jun 2026 01:05:07 -0700 Subject: [PATCH 5/9] Code Improvements and Fine-Tuning - Replaced the newly-added FLock mechanism with the existing Lock mechanism already in place since it can handle a blocking lock to wait until the current process releases the lock. - Code improvements when mounting the GNUton-specific webs_update.sh file to make sure it's installed only if the F/W-installed version is older than the fixed version from MerlinAU. --- MerlinAU.sh | 156 +++++++++++++++++++++------------------------------- README.md | 2 +- 2 files changed, 63 insertions(+), 95 deletions(-) diff --git a/MerlinAU.sh b/MerlinAU.sh index 1df6fb2d..c3c06772 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-May-21 +# Last Modified: 2026-Jun-11 ################################################################### set -u ## Set version for each Production Release ## readonly SCRIPT_VERSION=1.6.4 -readonly SCRIPT_VERSTAG="26061019" +readonly SCRIPT_VERSTAG="26061100" readonly SCRIPT_NAME="MerlinAU" ## Set to "master" for Production Releases ## -SCRIPT_BRANCH="master" +SCRIPT_BRANCH="dev" ##----------------------------------------## ## Modified by Martinski W. [2024-Jul-03] ## @@ -413,7 +413,7 @@ _WaitForYESorNO_() ## Modified by Martinski W. [2025-Jan-11] ## ##----------------------------------------## readonly LockFilePath="/tmp/var/${ScriptFNameTag}.LOCK" -readonly LockTypeRegEx="(cliMenuLock|cliOptsLock|cliFileLock)" +readonly LockTypeRegEx="(cliInitLock|cliMenuLock|cliOptsLock|cliFileLock)" _FindLockFileTypes_() { grep -woE "$LockTypeRegEx" "$LockFilePath" | tr '\n' ' ' | sed 's/[ ]*$//' ; } @@ -434,7 +434,7 @@ _ReleaseLock_() fi [ -s "$LockFilePath" ] && return 0 fi - rm -f "$LockFilePath" + printf '' > "$LockFilePath" } ## Defaults ## @@ -445,7 +445,7 @@ LockFileMaxAgeSecs=600 #10-minutes# if [ $# -eq 0 ] || [ -z "$1" ] then #Interactive Mode# - LockMaxTimeoutSecs=3 + LockMaxTimeoutSecs=5 LockFileMaxAgeSecs=1200 else case "$1" in @@ -478,7 +478,7 @@ _AcquireLock_() _CreateLockFile_() { echo "$$|$lockTypeReq" > "$LockFilePath" ; } - if [ ! -f "$LockFilePath" ] + if [ ! -s "$LockFilePath" ] then _CreateLockFile_ ; return 0 fi @@ -546,12 +546,6 @@ fwupMutexFLock_FD=576 fwupMutexFLock_FN="/tmp/var/${ScriptFNameTag}_FW_Update.FLock" fwupMutexFLock_OK=false #DO NOT have FLock# -##-------------------------------------## -## Added for initialization operations ## -##-------------------------------------## -initMutexFLock_FD=577 -initMutexFLock_FN="/tmp/var/${ScriptFNameTag}_Initialization.FLock" - _ReleaseMutexFLock_() { if [ $# -gt 0 ] && \ @@ -779,7 +773,8 @@ Toggle_LEDs_PID="" # To enable/disable the built-in "F/W Update Check" # FW_UpdateCheckState="$(nvram get firmware_check_enable)" -FW_UpdateCheckScript="/usr/sbin/webs_update.sh" +readonly FW_WebsUpdateFile="webs_update.sh" +readonly FW_UpdateCheckScript="/usr/sbin/$FW_WebsUpdateFile" ##-------------------------------------## ## Added by Martinski W. [2023-Nov-24] ## @@ -2229,20 +2224,19 @@ readonly POST_UPDATE_EMAIL_SCRIPT_HOOK="[ -x $ScriptFilePath ] && $POST_UPDATE_E _CleanUpOldLogFiles_() { [ ! -d "$FW_LOG_DIR" ] && return 1 - local numLogFiles topFile savedTopFile + local numLogFiles topLogFile savedTopLogFile="" numLogFiles="$(ls -1lt "$FW_LOG_DIR"/*.log 2>/dev/null | wc -l)" # Leave one log file (if any available) # [ "$numLogFiles" -lt 2 ] && return 0 # Save the most recent log file # - topFile="$(ls -1t "$FW_LOG_DIR"/*.log 2>/dev/null | head -n1)" + topLogFile="$(ls -1t "$FW_LOG_DIR"/*.log 2>/dev/null | head -n1)" - if [ -n "$topFile" ] + if [ -n "$topLogFile" ] && [ -s "$topLogFile" ] then - savedTopFile="${topFile}.SAVED.$$.TEMP.LOG" - - if ! mv -f "$topFile" "$savedTopFile" + savedTopLogFile="${topLogFile}.SAVED.$$.TEMP.LOG" + if ! mv -f "$topLogFile" "$savedTopLogFile" then return 1 fi @@ -2252,14 +2246,15 @@ _CleanUpOldLogFiles_() /usr/bin/find -L "$FW_LOG_DIR" -name '*.log' -mtime +30 -exec rm {} \; # Restore the most recent log file # - if [ -n "$topFile" ] && [ -f "$savedTopFile" ] + if [ -n "$topLogFile" ] && \ + [ -n "$savedTopLogFile" ] && \ + [ -s "$savedTopLogFile" ] then - if ! mv -f "$savedTopFile" "$topFile" + if ! mv -f "$savedTopLogFile" "$topLogFile" then return 1 fi fi - return 0 } @@ -12057,9 +12052,9 @@ _DoInitializationStartup_() _SetDefaultBuildType_ } -##------------------------------------------## -## Modified by ExtremeFiretop [2026-Jun-10] ## -##------------------------------------------## +##----------------------------------------## +## Modified by Martinski W. [2026-Jun-11] ## +##----------------------------------------## ####################################################################### # TEMPORARY hack to check if the Gnuton F/W built-in 'webs_update.sh' # script is the most recent version that includes required fixes. @@ -12077,10 +12072,9 @@ _Gnuton_Check_Webs_Update_Script_() return 0 fi - local theWebsUpdateFile="webs_update.sh" - local fixedGnutonWebsUpdateFilePath="${SETTINGS_DIR}/$theWebsUpdateFile" - local dwnldGnutonWebsUpdateFilePath="${SETTINGS_DIR}/${theWebsUpdateFile}.GNUTON.$$" - local localVersTag remoteVersTag diffRC + local fixedGnutonWebsUpdateFilePath="${SETTINGS_DIR}/$FW_WebsUpdateFile" + local dwnldGnutonWebsUpdateFilePath="${SETTINGS_DIR}/${FW_WebsUpdateFile}.GNUTON.$$" + local localVersTag remoteVersTag diffRC mountPoint # Get local VERSTAG (if any) # localVersTag="$(_Get_GnutonWebUpdate_ScriptVersTag_ "$FW_UpdateCheckScript")" @@ -12091,11 +12085,10 @@ _Gnuton_Check_Webs_Update_Script_() then if ! chmod 755 "$dwnldGnutonWebsUpdateFilePath" then - Say "${REDct}**ERROR**${NOct}: Unable to set permissions on the downloaded GNUton webs_update.sh file." + Say "${REDct}**ERROR**${NOct}: Unable to set permissions on the downloaded GNUton \"${FW_WebsUpdateFile}\" file." rm -f "$dwnldGnutonWebsUpdateFilePath" return 1 fi - remoteVersTag="$(_Get_GnutonWebUpdate_ScriptVersTag_ "$dwnldGnutonWebsUpdateFilePath")" [ -z "$remoteVersTag" ] && remoteVersTag=0 else @@ -12103,118 +12096,88 @@ _Gnuton_Check_Webs_Update_Script_() return 1 fi - # Distinguish files that differ from an actual comparison error. + # Distinguish files that differ from an actual comparison error # diff -q "$FW_UpdateCheckScript" "$dwnldGnutonWebsUpdateFilePath" >/dev/null 2>&1 - - diffRC=$? + diffRC="$?" if [ "$diffRC" -gt 1 ] then - Say "${REDct}**ERROR**${NOct}: Unable to compare the GNUton webs_update.sh files." + Say "${REDct}**ERROR**${NOct}: Unable to compare the GNUton \"${FW_WebsUpdateFile}\" files." rm -f "$dwnldGnutonWebsUpdateFilePath" return 1 fi # (Re)bind/mount only if remote is newer version OR files differ # if [ "$remoteVersTag" -gt "$localVersTag" ] || \ - [ "$diffRC" -eq 1 ] + { [ "$remoteVersTag" -eq "$localVersTag" ] && [ "$diffRC" -ne 0 ] ; } then - # Remove all existing bind-mount layers, including duplicates. - while awk -v target="$FW_UpdateCheckScript" ' - $2 == target { found=1 } - END { exit !found } - ' /proc/mounts - do - if ! umount "$FW_UpdateCheckScript" - then - Say "${REDct}**ERROR**${NOct}: Unable to unmount \"$FW_UpdateCheckScript\"." - rm -f "$dwnldGnutonWebsUpdateFilePath" - return 1 - fi + for mountPoint in $(grep "$FW_UpdateCheckScript" /proc/mounts | awk -F' ' '{print $2}') + do umount "$FW_UpdateCheckScript" 2>/dev/null done + if grep -q "$FW_UpdateCheckScript" /proc/mounts + then + Say "${REDct}**ERROR**${NOct}: Unable to unmount \"$FW_UpdateCheckScript\"." + rm -f "$dwnldGnutonWebsUpdateFilePath" + return 1 + fi + if ! mv -f "$dwnldGnutonWebsUpdateFilePath" "$fixedGnutonWebsUpdateFilePath" then - Say "${REDct}**ERROR**${NOct}: Unable to install the fixed GNUton webs_update.sh file." + Say "${REDct}**ERROR**${NOct}: Unable to install the fixed GNUton \"${FW_WebsUpdateFile}\" file." rm -f "$dwnldGnutonWebsUpdateFilePath" return 1 fi - if [ ! -f "$fixedGnutonWebsUpdateFilePath" ] + if [ ! -s "$fixedGnutonWebsUpdateFilePath" ] then - Say "${REDct}**ERROR**${NOct}: GNUton webs_update.sh source file is missing before bind mount." + Say "${REDct}**ERROR**${NOct}: GNUton \"${FW_WebsUpdateFile}\" source file is missing before bind mount." return 1 fi - if [ ! -e "$FW_UpdateCheckScript" ] + if [ ! -s "$FW_UpdateCheckScript" ] then Say "${REDct}**ERROR**${NOct}: Bind-mount target \"$FW_UpdateCheckScript\" does not exist." return 1 fi - if ! mount -o bind \ - "$fixedGnutonWebsUpdateFilePath" \ - "$FW_UpdateCheckScript" + if ! mount -o bind "$fixedGnutonWebsUpdateFilePath" "$FW_UpdateCheckScript" then - Say "${REDct}**ERROR**${NOct}: Unable to bind-mount the fixed GNUton webs_update.sh file." + Say "${REDct}**ERROR**${NOct}: Unable to bind-mount the fixed GNUton \"${FW_WebsUpdateFile}\" file." return 1 fi - - Say "${YLWct}Set up a fixed version of the \"${theWebsUpdateFile}\" script file.${NOct}" + Say "${YLWct}Set up a fixed version of the \"${FW_WebsUpdateFile}\" script file.${NOct}" else rm -f "$dwnldGnutonWebsUpdateFilePath" fi - return 0 } -##---------------------------------------## -## Added by ExtremeFiretop [2026-Jun-10] ## -##---------------------------------------## -# Serialize startup-time operations that use shared log and GNUton files. +##----------------------------------------## +## Modified by Martinski W. [2026-Jun-11] ## +##----------------------------------------## +# Startup/Initialization operations done by possible concurrent processes # _RunLockedInitializationChecks_() { local retCode=0 - [ ! -d /tmp/var ] && mkdir -p /tmp/var - - # Open the dedicated lock file on its own file descriptor. - if ! eval "exec ${initMutexFLock_FD}>\"${initMutexFLock_FN}\"" - then - Say "${REDct}**ERROR**${NOct}: Unable to open the initialization lock file." - return 1 - fi - - # Use a blocking exclusive lock so concurrent MerlinAU processes wait. - if ! flock -x "$initMutexFLock_FD" 2>/dev/null - then - Say "${REDct}**ERROR**${NOct}: Unable to acquire the initialization lock." - eval "exec ${initMutexFLock_FD}>&-" - return 1 - fi - - # These operations access shared paths and must not overlap. - if [ -d "$FW_LOG_DIR" ] && \ - ! _CleanUpOldLogFiles_ + if ! _CleanUpOldLogFiles_ then Say "${YLWct}WARNING:${NOct} Unable to clean up old firmware-update log files." retCode=1 fi + # Set variable to 'false' to stop the check # checkWebsUpdateScriptForGnuton="$isGNUtonFW" if ! _Gnuton_Check_Webs_Update_Script_ then - Say "${YLWct}WARNING:${NOct} Unable to check or install the GNUton webs_update.sh patch." + Say "${YLWct}WARNING:${NOct} Unable to check or install the GNUton \"${FW_WebsUpdateFile}\" patch." retCode=1 fi - flock -u "$initMutexFLock_FD" 2>/dev/null - eval "exec ${initMutexFLock_FD}>&-" - return "$retCode" } - if [ "$SCRIPT_BRANCH" = "master" ] then SCRIPT_VERS_INFO="" else SCRIPT_VERS_INFO="[$versionDev_TAG]" @@ -12224,6 +12187,14 @@ FW_InstalledVersion="$(_GetCurrentFWInstalledLongVersion_)" FW_InstalledVerStr="${GRNct}${FW_InstalledVersion}${NOct}" FW_NewUpdateVerInit=TBD +if _AcquireLock_ cliInitLock +then + _RunLockedInitializationChecks_ + _ReleaseLock_ cliInitLock +else + Say "Exiting..." ; exit 1 +fi + ##----------------------------------------## ## Modified by Martinski W. [2025-Apr-07] ## ##----------------------------------------## @@ -12234,8 +12205,6 @@ then then Say "Exiting..." ; exit 1 fi - _RunLockedInitializationChecks_ - inMenuMode=true _DoInitializationStartup_ _CheckFor_VersionFile_ @@ -12261,9 +12230,8 @@ fi if [ $# -gt 0 ] then if ! _AcquireLock_ cliOptsLock - then Say "Exiting..." ; exit 1 ; fi - - _RunLockedInitializationChecks_ + then Say "Exiting..." ; exit 1 + fi [ "$1" = "amtmupdate" ] && isVerbose=false diff --git a/README.md b/README.md index 6904f844..de39449f 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # MerlinAU - AsusWRT-Merlin Firmware Auto Updater ## v1.6.4 -## 2026-June-10 +## 2026-June-11 ## WebUI: image From db8dad9ca1b982a8ad58c51a2578ca59b717bbb0 Mon Sep 17 00:00:00 2001 From: Martinski4GitHub <119833648+Martinski4GitHub@users.noreply.github.com> Date: Thu, 11 Jun 2026 03:10:29 -0700 Subject: [PATCH 6/9] Update MerlinAU.sh Actually I re-added the separate, dedicated FLOCK blocking mechanism to protect the initialization routines because it was a cleaner and leaner implementation for fast operations. --- MerlinAU.sh | 36 +++++++++++++++++++++++++++++------- 1 file changed, 29 insertions(+), 7 deletions(-) diff --git a/MerlinAU.sh b/MerlinAU.sh index c3c06772..81d9b858 100644 --- a/MerlinAU.sh +++ b/MerlinAU.sh @@ -539,6 +539,27 @@ _AcquireLock_() return "$retCode" } +##-------------------------------------## +## Added for initialization operations ## +##-------------------------------------## +initMutexFLock_FD=577 +initMutexFLock_FN="/tmp/var/${ScriptFNameTag}_Initialization.FLock" + +#--------------------------------------------------------------# +# This is a mutually exclusive, blocking FLOCK mechanism used +# to protect initialization routines when concurrent MerlinAU +# executions are running (e.g. called from services-start). +#--------------------------------------------------------------# +_AcquireInitMutexFLock_() +{ + eval exec "$initMutexFLock_FD>$initMutexFLock_FN" + flock -x "$initMutexFLock_FD" 2>/dev/null + return "$?" +} + +_ReleaseInitMutexFLock_() +{ flock -u "$initMutexFLock_FD" 2>/dev/null ; } + ##-------------------------------------## ## Added by Martinski W. [2026-Mar-18] ## ##-------------------------------------## @@ -2236,7 +2257,7 @@ _CleanUpOldLogFiles_() if [ -n "$topLogFile" ] && [ -s "$topLogFile" ] then savedTopLogFile="${topLogFile}.SAVED.$$.TEMP.LOG" - if ! mv -f "$topLogFile" "$savedTopLogFile" + if ! mv -f "$topLogFile" "$savedTopLogFile" 2>/dev/null then return 1 fi @@ -2250,7 +2271,7 @@ _CleanUpOldLogFiles_() [ -n "$savedTopLogFile" ] && \ [ -s "$savedTopLogFile" ] then - if ! mv -f "$savedTopLogFile" "$topLogFile" + if ! mv -f "$savedTopLogFile" "$topLogFile" 2>/dev/null then return 1 fi @@ -12083,7 +12104,7 @@ _Gnuton_Check_Webs_Update_Script_() # Get the fixed version of the script targeted for Gnuton F/W # if _CurlFileDownload_ "gnuton_webs_update.sh" "$dwnldGnutonWebsUpdateFilePath" then - if ! chmod 755 "$dwnldGnutonWebsUpdateFilePath" + if ! chmod 755 "$dwnldGnutonWebsUpdateFilePath" 2>/dev/null then Say "${REDct}**ERROR**${NOct}: Unable to set permissions on the downloaded GNUton \"${FW_WebsUpdateFile}\" file." rm -f "$dwnldGnutonWebsUpdateFilePath" @@ -12122,7 +12143,7 @@ _Gnuton_Check_Webs_Update_Script_() return 1 fi - if ! mv -f "$dwnldGnutonWebsUpdateFilePath" "$fixedGnutonWebsUpdateFilePath" + if ! mv -f "$dwnldGnutonWebsUpdateFilePath" "$fixedGnutonWebsUpdateFilePath" 2>/dev/null then Say "${REDct}**ERROR**${NOct}: Unable to install the fixed GNUton \"${FW_WebsUpdateFile}\" file." rm -f "$dwnldGnutonWebsUpdateFilePath" @@ -12187,12 +12208,13 @@ FW_InstalledVersion="$(_GetCurrentFWInstalledLongVersion_)" FW_InstalledVerStr="${GRNct}${FW_InstalledVersion}${NOct}" FW_NewUpdateVerInit=TBD -if _AcquireLock_ cliInitLock +if _AcquireInitMutexFLock_ then _RunLockedInitializationChecks_ - _ReleaseLock_ cliInitLock + _ReleaseInitMutexFLock_ else - Say "Exiting..." ; exit 1 + Say "${REDct}**ERROR**${NOct}: Unable to acquire the initialization lock. Exiting..." + exit 1 fi ##----------------------------------------## From e94a477cd25fb4b65953622abeeb2f6583d99c00 Mon Sep 17 00:00:00 2001 From: Martinski4GitHub <119833648+Martinski4GitHub@users.noreply.github.com> Date: Thu, 11 Jun 2026 04:06:56 -0700 Subject: [PATCH 7/9] Update MerlinAU.sh Make sure to close the FD when releasing the lock before continuing execution. --- MerlinAU.sh | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/MerlinAU.sh b/MerlinAU.sh index 81d9b858..f16055c1 100644 --- a/MerlinAU.sh +++ b/MerlinAU.sh @@ -552,13 +552,19 @@ initMutexFLock_FN="/tmp/var/${ScriptFNameTag}_Initialization.FLock" #--------------------------------------------------------------# _AcquireInitMutexFLock_() { - eval exec "$initMutexFLock_FD>$initMutexFLock_FN" - flock -x "$initMutexFLock_FD" 2>/dev/null - return "$?" + eval exec "${initMutexFLock_FD}>$initMutexFLock_FN" + if flock -x "$initMutexFLock_FD" 2>/dev/null + then return 0 + fi + eval exec "${initMutexFLock_FD}>&-" + return 1 } _ReleaseInitMutexFLock_() -{ flock -u "$initMutexFLock_FD" 2>/dev/null ; } +{ + flock -u "$initMutexFLock_FD" 2>/dev/null + eval exec "${initMutexFLock_FD}>&-" +} ##-------------------------------------## ## Added by Martinski W. [2026-Mar-18] ## From c70fb5016e29b7e77563f45ddea8fdc9ba13a94b Mon Sep 17 00:00:00 2001 From: Martinski4GitHub <119833648+Martinski4GitHub@users.noreply.github.com> Date: Thu, 11 Jun 2026 18:23:47 -0700 Subject: [PATCH 8/9] Remove unused parameter for "Lock Type" --- MerlinAU.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/MerlinAU.sh b/MerlinAU.sh index f16055c1..f0552434 100644 --- a/MerlinAU.sh +++ b/MerlinAU.sh @@ -10,7 +10,7 @@ set -u ## Set version for each Production Release ## readonly SCRIPT_VERSION=1.6.4 -readonly SCRIPT_VERSTAG="26061100" +readonly SCRIPT_VERSTAG="26061118" readonly SCRIPT_NAME="MerlinAU" ## Set to "master" for Production Releases ## SCRIPT_BRANCH="dev" @@ -413,7 +413,7 @@ _WaitForYESorNO_() ## Modified by Martinski W. [2025-Jan-11] ## ##----------------------------------------## readonly LockFilePath="/tmp/var/${ScriptFNameTag}.LOCK" -readonly LockTypeRegEx="(cliInitLock|cliMenuLock|cliOptsLock|cliFileLock)" +readonly LockTypeRegEx="(cliMenuLock|cliOptsLock|cliFileLock)" _FindLockFileTypes_() { grep -woE "$LockTypeRegEx" "$LockFilePath" | tr '\n' ' ' | sed 's/[ ]*$//' ; } From 7191a70257ef6a72f02b5cb78d89fd484f43fe05 Mon Sep 17 00:00:00 2001 From: Joel Samson Date: Sun, 21 Jun 2026 10:31:20 -0400 Subject: [PATCH 9/9] Update release date in README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index de39449f..26f58ecd 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # MerlinAU - AsusWRT-Merlin Firmware Auto Updater ## v1.6.4 -## 2026-June-11 +## 2026-June-21 ## WebUI: image