From: vados-dev <192440777+vados-dev@users.noreply.github.com> Date: Mon, 17 Aug 2026 18:15:05 +0000 (+0300) Subject: Autocommit in repo: ros.vados.ru, generated by owner: root, in 21:15:04 17.08.2026. X-Git-Url: https://git.vados.ru/gitweb.cgi?a=commitdiff_plain;h=98f85e73805147972b72801f41224da328402f41;p=Mikrotiks%2Fros.vados.ru.git Autocommit in repo: ros.vados.ru, generated by owner: root, in 21:15:04 17.08.2026. --- diff --git a/html/.#AM-Fetch-Generator.rsc b/html/.#AM-Fetch-Generator.rsc deleted file mode 120000 index af090d606..000000000 --- a/html/.#AM-Fetch-Generator.rsc +++ /dev/null @@ -1 +0,0 @@ -root@amster.3712 \ No newline at end of file diff --git a/html/AM-Fetch-Generator.rsc b/html/AM-Fetch-Generator.rsc index 03034e99c..e79b6c0c5 100644 --- a/html/AM-Fetch-Generator.rsc +++ b/html/AM-Fetch-Generator.rsc @@ -1,654 +1,218 @@ -#!rsc by Vados -# RouterOS script: AM-Backup-UpdateEmail -# Script comment: Update and Backup send notification to EMail -# Copyright (c) 2007-2026 Vados -# -# -# requires RouterOS, version>=6.43.7 -# requires device-mode, fetch -# -# Updated: 15/04/2025 -# Website: https://github.com/beeyev -# Notification e-mail (Make sure you have configured Email settings in Tools -> Email) -:local ExitOK false; -:onerror Err { - :global GlobalConfReady; :global GlobalFuncReady; - :retry { :if ($GlobalConfReady != true || $GlobalFuncReady != true) \ - do={ :error ("Global config and/or functions not ready."); }; } delay=500ms max=50; - :local ScriptName [ :jobname ]; - -:local IfThenElse do={:if ([ :tostr $1 ] = "true" || [ :tobool $1 ] = true) do={:return $2}; :return $3;} -:local EitherOr do={ - :if ([ :typeof $1 ] = "num") do={:if (($1 != 0) = true) do={:return $1} else={:return $2};} - :if ([ :typeof $1 ] = "time") do={:if (($1 > 0s) = true) do={:return $1} else={:return $2};} - :if ([ :len [ :tostr $1 ] ] > 0) do={:return $1} else={:return $2}; -} -:local ExitError do={ - :local ExitOK [ :tostr $1 ]; - :local Name [ :tostr $2 ]; - :local Error [ :tostr $3 ]; - :if ($ExitOK = "false") do={:log error ($Name [ $IfThenElse ([ :pick $Name 0 1 ] = "\$") \ - "Function" "Script" ] . " '" . $Name . "' exited with error" . \ - [ $IfThenElse (!($Error ~ "^(|true|false)\$")) (": " . $Error) "." ]);} -} - -:global LogPrint; -:global EditSchedulers; -:global DevInfoIncludeIP; - -:local SchedInterval "7d"; -:local SchedStartTime "03:41:27"; -:local SchedComment "Every 7 Day Check For Update and Backup send notification to EMail."; - -# Function to add or edit schedulers for scripts, to ensure they are set up correctly -# How to use: -# Call this function with the following parameters: -# 1. $1 - Scheduler name (usually same as script name) [*] -# 2. $2 - Interval (e.g. "1d" (default is "1d")) [**] -# 3. $3 - Start Time (e.g. "00:00:00", default is current time) [**] -# 4. $4 - Comment (optional, default: "Scheduler for script: ") [**] -# 5. $5 - On Event command (e.g. "/system/script { run ; }" (default is "/system/script { run $SchedName; }")) [**] -# 6. $6 - Policy (e.g. "ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon" (default is the same)) [**] -# 7. $7 - Start Date (e.g. "jan/01/1970", default is current date) [**] -# Notes: -# * - Scheduler name is required, and is used to identify the scheduler to create or edit. It is usually the same as the script name ($ScriptName). -# ** - If optional parameters are not provided, default values will be used. -# This allows for quick setup with minimal parameters, while still providing flexibility for customization. -# Example usage: -# :if ([ $EditSchedulers "my-script" ] = false) do={:set ExitOK false; :error true;} -# :if ([ $EditSchedulers "my-script" "1d" "03:41:27" "My custom scheduler comment" false false "jan/01/1970" ] = false) do={:set ExitOK false; :error true;} - -:set EditSchedulers do={ - :local SchedName [ :tostr $1 ]; - :if ([ :len $SchedName ] = 0) do={ :return false; } - :local Interval [ :tostr $2 ]; - :if ([ :len $Interval ] = 0) do={ :set Interval "1d"; } - :local StartTime [ :tostr $3 ]; - :if ([ :len $StartTime ] = 0) do={ :set StartTime [/system/clock/get time]; } - :local Comment [ :tostr $4 ]; - :if ([ :len $Comment ] = 0) do={ :set Comment "Scheduler for script: $SchedName"; } - :local OnEvent [ :tostr $5 ]; - :if ([ :len $OnEvent ] = 0) do={ :set OnEvent "/system/script { run $SchedName; }"; } - :local Policy [ :tostr $6 ]; - :if ([ :len $Policy ] = 0) do={ :set Policy "ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon"; } - :local StartDate [ :tostr $7 ]; - :if ([ :len $StartDate ] = 0) do={ :set StartDate [/system/clock/get date]; } - - :onerror Err { - # Check if scheduler exists, if not - create it - :if ([ :len [ /system/scheduler/find where name=$SchedName ] ] = 0) do={ - :log warning ($SchedName . "System Scheduler for script: " . $SchedName . " is NOT SET! Fix It Now!"); - /system/scheduler/add comment=$Comment interval=$Interval name=$SchedName on-event=$OnEvent policy=$Policy start-date=$StartDate start-time=$StartTime; - } - # Check if scheduler parameters are correct, if not - fix them - :if ([ /system/scheduler/get $SchedName comment ] != $Comment) do={ - /system/scheduler/set $SchedName comment=$Comment; - } - :if ([ /system/scheduler/get $SchedName interval ] != $Interval) do={ - /system/scheduler/set $SchedName interval=$Interval; - } - :if ([ /system/scheduler/get $SchedName on-event ] != $OnEvent) do={ - /system/scheduler/set $SchedName on-event=$OnEvent; - } - :if ([ /system/scheduler/get $SchedName policy ] != $Policy) do={ - /system/scheduler/set $SchedName policy=$Policy; - } - :if ([ /system/scheduler/get $SchedName start-date ] != $StartDate) do={ - /system/scheduler/set $SchedName start-date=$StartDate; - } - :if ([ /system/scheduler/get $SchedName start-time ] != $StartTime) do={ - /system/scheduler/set $SchedName start-time=$StartTime; - } - } do={:log error ($SchedName . "Failed to add/modify scheduler for '" . $SchedName . "' failed: " . $Err); :return false}; - :return true; -} - -:if ([ $EditSchedulers $ScriptName $SchedInterval $SchedStartTime $SchedComment ] = false) do={ - :log error ($ScriptName . "Failed to add/modify scheduler for " . $ScriptName . "!"); - :set ExitOK true; - :error true; -} - -# Local Function CheckOSUpdateMode -:local CheckOSUpdateMode; -:set CheckOSUpdateMode do={ - :local NewMode [ :tostr $1 ]; - :if ([ :len $NewMode ] = 0) do={ - :log error ($0 . "/nFunction /$CheckOSUpdateMode received empty value!"); - :return false; - :error true; - } - - :global BackupAndUpdateMode; - :global OldMode [ :tostr $BackupAndUpdateMode ]; - :if ($NewMode != $BackupAndUpdateMode && $NewMode = "updateOnce") do={ - :set BackupAndUpdateMode [ :tostr $NewMode ]; - :log info ($0 . "/nOk. /$BackupAndUpdateMode set to: '" . $NewMode . "'.\nYou're a good boy! ))\nI'm launching the update in 5 seconds."); - :delay 5; - /system/script { run $ScriptName; }; - } else={ - :log warning ($0 . "/nFunction /$CheckOSUpdateMode received value: '" . $NewMode . "', but is may be only \"updateOnce\"!"); - :return false; - } - :return true; - :error false; -} - -# :if ([ :len [ /system/scheduler/find where name="running-from-backup-partition" ] ] > 0) do={ -# $LogPrint warning $ScriptName ("Running from backup partition, refusing to act."); -# :set ExitOK true; -# :error false; -# } - -:global EmailGeneralTo; -:global BackupAndUpdateMode; -:global forceBackup; -:global BackupPassword; -:global BackupSens; -:global updateChannel; - -:global DevInfoIncludeIP; - -# Installs patch updates only (BackupAndUpdateMode = "osupdate"). -# Works for `stable` and `long-term` channels. -# Updates only if MAJOR.MINOR match (e.g. 6.43.2 > 6.43.6 allowed, 6.44.1 skipped). -# Sends info if a newer (non-patch) version is found. -:local installOnlyPatchUpdates false; -:local scriptVersion "26.04.30"; -:local copyrightUrl "https://www.github.com/vados-dev/"; -#Script messages prefix -:local SMP "BkpAndUpdate:"; - -:local exitErrorMessage "$SMP script stopped due to an error. Please check logs for more details."; -:log info "\n\n$SMP Script \"Mikrotik RouterOS automatic backup & update\" v.$scriptVersion started."; -:log info "$SMP Script Mode: `$BackupAndUpdateMode`, Update channel: `$updateChannel`, Force backup: `$forceBackup`, Install only patch updates: `$installOnlyPatchUpdates`"; - -## vv FUNCTIONS vv ## -:global WaitCheckUpdates; $WaitCheckUpdates; -:global MiniDateTimeStamp; -:local MDTSFix [$MiniDateTimeStamp]; -:global Identity; -:global IdentityShort; -:global devBoardName; -:global runningVersion; -:global runningChannel; -:global devModel; -:global devRbSerialNumber; -:global ROsVerAvail; -:global PkgUpdStatus; - -# Checks if two RouterOS version strings differ only by the patch version -# :put [$FuncIsPatchUpdateOnly "6.2.1" "6.2.4"] # Output: true -# :put [$FuncIsPatchUpdateOnly "6.2.1" "6.3.1"] # Output: false -:local FuncIsPatchUpdateOnly do={ - :local ver1 $1; - :local ver2 $2; - # Extract the major and minor components from a version - :local extractMajorMinor do={ - :local ver $1; - :local dot1 [:find $ver "."]; - :if ($dot1 = -1) do={:return $ver} - :local major [:pick $ver 0 $dot1]; - :local rest [:pick $ver ($dot1 + 1) [:len $ver]]; - :local dot2 [:find $rest "."]; - :local minor $rest; - :if ($dot2 >= 0) do={:set minor [:pick $rest 0 $dot2]} - :return ($major . "." . $minor); - } - -# Compare the major and minor components of both version strings -:if ([$extractMajorMinor $ver1] = [$extractMajorMinor $ver2]) do={:return true} - :return false; -} -# Creates backups and returns array of names -# Possible arguments: -# $1 - file name, without extension -# $2 - password (optional) -# $3 - sensitive data in config (optional, default: false) -# Example: -#:put [$FuncCreateBackups $backupName] -:local FuncCreateBackups do={ - :local backupName [ :tostr $1 ]; - :local BackPass $2; - :local BackSens $3; - - #Script messages prefix - :local SMP "BkpAndUpdate:"; - :local exitErrorMessage "$SMP script stopped due to an error. Please check logs for more details."; - :log info ("$SMP global function `FuncCreateBackups` started, input: `$backupName`"); - - # validate required parameter: backupName - :if ([:typeof $backupName] != "str" or [:len $backupName] = 0) do={ - :log error "$SMP parameter 'backupName' is required and must be a non-empty string"; -# :log warning "$SMP parameter '\$backupName' is required and must be a non-empty string.\nI set '\$backupName' to 'default-backup'"; -# :set $backupName "default-backup"; - :error $exitErrorMessage; - } - - :local backupFileSys "$backupName.backup"; - :local backupFileConfig "$backupName.rsc"; - :local backupNames {$backupFileSys;$backupFileConfig}; - - ## Perform system backup - :if ([:len $BackPass] = 0) do={ - :log info ("$SMP starting backup without password, backup name: `$backupName`"); - /system backup save dont-encrypt=yes name=$backupName; - } else={ - :log info ("$SMP starting backup with password, backup name: `$backupName`"); - /system backup save password=$BackPass name=$backupName; - } - :log info ("$SMP system backup created: `$backupFileSys`"); - ## Export config file - :if ($BackSens = true) do={ - :log info ("$SMP starting export config with sensitive data, backup name: `$backupName`"); - # Since RouterOS v7 it needs to be explicitly set that we want to export sensitive data - :if ([:pick [/system resource get version] 0 1] < 7) do={ - :execute "/export compact terse file=$backupName"; - } else={ - :execute "/export compact show-sensitive terse file=$backupName"; - } - } else={ - :log info ("$SMP starting export config without sensitive data, backup name: `$backupName`"); - /export compact hide-sensitive terse file=$backupName; - } - :log info ("$SMP Config export complete: `$backupFileConfig`"); - :log info ("$SMP Waiting a little to ensure backup files are written"); - :delay 20; - :if ([:len [/file find name=$backupFileSys]] > 0) do={ - :log info ("$SMP system backup file successfully saved to the file system: `$backupFileSys`"); - } else={ - :log error ("$SMP system backup was not created, file does not exist: `$backupFileSys`"); - :error $exitErrorMessage; - } - :if ([:len [/file find name=$backupFileConfig]] > 0) do={ - :log info ("$SMP config backup file successfully saved to the file system: `$backupFileConfig`"); - } else={ - :log error ("$SMP config backup was not created, file does not exist: `$backupFileConfig`"); - :error $exitErrorMessage; - } - :log info ("$SMP global function `FuncCreateBackups` finished. Created backups, system: `$backupFileSys`, config: `$backupFileConfig`") - :return $backupNames; -} -# Sends an email -# Parameters: -# $1 - to (email address) -# $2 - subject -# $3 - body -# $4 - file attachments (optional; pass "" if not needed) -# -# Example: -# $FuncSendEmailSafe "admin@domain.com" "Backup Done" "Backup complete." "backup1.backup" -:local FuncSendEmailSafe do={ - :global EmailGeneralTo; - :local emailSubject $2; - :local emailBody $3; - :local emailAttachments $4; - :local SMP "Bkp&Upd:"; - :local exitErrorMessage "$SMP script stopped due to an error. Please check logs for more details."; - :log info "$SMP Attempting to send email to `$EmailGeneralTo`"; - # SAFETY: wait for any previously queued email to finish - :local waitTimeoutPre 60; - :local waitCounterPre 0; - :while (([/tool e-mail get last-status] = "resolving-dns" or [/tool e-mail get last-status] = "in-progress")) do={ - :if ($waitCounterPre >= $waitTimeoutPre) do={ - :log error "$SMP Email send aborted: previous send did not complete after $waitTimeoutPre seconds"; - :error $exitErrorMessage; - } - :log info "$SMP Waiting for previous email to finish (status: $[/tool e-mail get last-status])..."; - :delay 1; - :set waitCounterPre ($waitCounterPre + 1); - } - # Send the email - :do { - /tool e-mail send to=$EmailGeneralTo subject=$emailSubject body=$emailBody file=$emailAttachments; - } on-error={ - :log error "$SMP Email send command failed to execute. Check logs and verify email settings."; - :error $exitErrorMessage; - } - # Wait for send status to change from "in-progress" / "resolving-dns" - :local waitTimeout 60; - :local waitCounter 0; - :local emailStatus ""; - :log info "$SMP Waiting for email to be sent, timeout in `$waitTimeout` seconds..."; - :while ($waitCounter < $waitTimeout) do={ - :set emailStatus [/tool e-mail get last-status]; - :if ($emailStatus != "in-progress" and $emailStatus != "resolving-dns") do={ - :log info "$SMP Email send status received: $emailStatus"; - # exit loop - :set waitCounter $waitTimeout; - } else={:delay 1; :set waitCounter ($waitCounter + 1)} - } - # Final decision based on last status - :if ($emailStatus = "succeeded") do={ - :log info "$SMP Email successfully sent to `$EmailGeneralTo`"; - } else={ - :log error "$SMP Email failed to send. Status: `$emailStatus`. Check logs for more details and verify email settings."; - :error $exitErrorMessage; - } -} -# Global variable to track current update step -# They need to be initialized here first to be available in the script -:global buGlobalVarTargetOsVersion; -:global buGlobalVarScriptStep; -:local scriptStep $buGlobalVarScriptStep; -:do { - /system/script/environment remove buGlobalVarScriptStep; -} on-error={} -:if ([:len $scriptStep] = 0) do={ - :set scriptStep 1; -} -## ^^ FUNCTIONS ^^ ## -# -# Initial validation -## Check email settings -:if ([:len $EmailGeneralTo] < 3) do={ - :log error ("$SMP Parameter `\$EmailGeneralTo` is not set, or contains invalid value. Script stopped."); - :error $exitErrorMessage; -} -# Values will be defined later in the script -:local emailServer ""; -:local emailFromAddress [/tool e-mail get from]; -:log info "$SMP Validating email settings..."; -:do { - :set emailServer [/tool e-mail get server]; -} on-error={ - # This is a workaround for the RouterOS v7.12 and older versions - :set emailServer [/tool e-mail get address]; -} -:if ($emailServer = "0.0.0.0") do={ - :log error ("$SMP Email server address is not correct: `$emailServer`, check `Tools -> Email`. Script stopped."); - :error $exitErrorMessage; -} -:if ([:len $emailFromAddress] < 3) do={ - :log error ("$SMP Email configuration FROM address is not correct: `$emailFromAddress`, check `Tools -> Email`. Script stopped."); - :error $exitErrorMessage; -} -# Script mode validation -:if ($BackupAndUpdateMode != "backup" and $BackupAndUpdateMode != "osupdate" and $BackupAndUpdateMode != "updateOnce" and $BackupAndUpdateMode != "osnotify") do={ - :log error ("$SMP Script parameter `\$BackupAndUpdateMode` is not set, or contains invalid value: `$BackupAndUpdateMode`. Script stopped."); - :error $exitErrorMessage; -} -# Update channel validation -:if ($updateChannel != "stable" and $updateChannel != "long-term" and $updateChannel != "testing" and $updateChannel != "development") do={ - :log error ("$SMP Script parameter `\$updateChannel` is not set, or contains invalid value: `$updateChannel`. Script stopped."); - :error $exitErrorMessage; -} -# Verify if script is set to install patch updates and if the update channel is valid -:if (($BackupAndUpdateMode = "osupdate" or $BackupAndUpdateMode = "updateOnce") and $installOnlyPatchUpdates = true) do={ - :if ($updateChannel != "stable" and $updateChannel != "long-term") do={ - :log error ("$SMP Patch-only updates enabled, but update channel `$updateChannel` is invalid. Only `stable` and `long-term` are supported. Script stopped"); - :error $exitErrorMessage; - } - - :if ($runningChannel != "stable" and $runningChannel != "long-term") do={ - :log error ("$SMP Script is set to install only patch updates, but the installed RouterOS version is not from `stable` or `long-term` channel: `$runningChannel`. Script stopped"); - :error $exitErrorMessage; - } -} -# - -:local rawTime [/system clock get time]; -:local rawDate [/system clock get date]; - -:local deviceOsVerAndChannelRunning [/system/resource/get version]; - -:local backupNameTemplate ("backup_v" . $runningVersion . "_" . $runningChannel . "_" . $MDTSFix); -:local backupNameBeforeUpdate ($backupNameTemplate . "_before_update"); -:local backupNameAfterUpdate ($backupNameTemplate . "_after_update"); - -## Email body template -:local mailSubjectPrefix "$SMP Device - `$IdentityShort`"; -:local mailBodyCopyright "Mikrotik RouterOS automatic backup & update (ver. $scriptVersion) \n$copyrightUrl"; -:local changelogUrl "Check RouterOS changelog: https://mikrotik.com/download/changelogs/"; -:local mailBodyDeviceInfo ""; -:set mailBodyDeviceInfo ($mailBodyDeviceInfo . "Device information:"); -:set mailBodyDeviceInfo ($mailBodyDeviceInfo . "\n---------------------"); -:set mailBodyDeviceInfo ($mailBodyDeviceInfo . "\nName: $Identity"); -:set mailBodyDeviceInfo ($mailBodyDeviceInfo . "\nModel: $devModel"); -:set mailBodyDeviceInfo ($mailBodyDeviceInfo . "\nBoard: $devBoardName"); -:set mailBodyDeviceInfo ($mailBodyDeviceInfo . "\nSerial number: $devRbSerialNumber"); -:set mailBodyDeviceInfo ($mailBodyDeviceInfo . "\nRouterOS version: v$deviceOsVerAndChannelRunning"); -:set mailBodyDeviceInfo ($mailBodyDeviceInfo . "\nBuild time: $[/system/resource/get build-time]"); -:set mailBodyDeviceInfo ($mailBodyDeviceInfo . "\nRouterboard FW Available: $ROsVerAvail"); -:set mailBodyDeviceInfo ($mailBodyDeviceInfo . "\nDevice date-time: $rawDate $rawTime ($[/system/clock/get time-zone-name ])"); -:set mailBodyDeviceInfo ($mailBodyDeviceInfo . "\nUptime: $[/system/resource/get uptime]"); -## IP address detection -:if ([ $DevInfoIncludeIP ] = true) do={ - :global GetPubIp; - :local PublicIpAddress [ $GetPubIp ]; - :set mailBodyDeviceInfo ($mailBodyDeviceInfo . "\nPublic IP address: " . $PublicIpAddress . "."); - :log info "$SMP Public IP address detected: $PublicIpAddress"; -} - -:local mailAttachments [:toarray ""]; -:if ($scriptStep = 1 or $scriptStep = 3) do={ - :if ($scriptStep = 3) do={ - :log info ("$SMP Waiting for one minute before continuing to the final step."); - :delay 1m; - } -} - -## STEP 1: Create backups, check for new RouterOS, and send email -## Steps 2â3 run only if auto-update is enabled and a new version is available -:if ($scriptStep = 1) do={ - :global BackupPassword; - :global BackupSens; - :local isNewOsUpdateAvailable false; - :local isLatestOsAlreadyInstalled true; - :local isOsNeedsToBeUpdated false; - :local isUpdateCheckSucceeded false; - :local isEmailNeedsToBeSent false; - :local mailSubjectPartAction ""; - :local mailPtBodyAction ""; - :local mailPtSubjectBackup ""; - :local mailPtBodyBackup ""; - # Checking for new version - :if ($BackupAndUpdateMode = "osupdate" or $BackupAndUpdateMode = "osnotify" or $BackupAndUpdateMode = "updateOnce") do={ - :log info ("$SMP Setting update channel to `$updateChannel`"); - /system/package/update/set channel=$updateChannel; - :log info ("$SMP Checking for new RouterOS version. Current installed version is: `$runningVersion`"); - $LogPrint debug $ScriptName ("Checking for updates..."); - -# /system/package/update/check-for-updates without-paging as-value; -# /system/package/update check-for-updates; - :delay 5s; - :set PkgUpdStatus [ /system/package/update/get status ]; - :if ($PkgUpdStatus = "New version is available") do={ - :set ROsVerAvail [/system/package/update/get latest-version]; - } - - :if ($PkgUpdStatus = "New version is available") do={ - :log info ("$SMP New RouterOS version is available: `$ROsVerAvail`"); - :set isNewOsUpdateAvailable true; - :set isLatestOsAlreadyInstalled false; - :set isUpdateCheckSucceeded true; - :set isEmailNeedsToBeSent true; - :set mailSubjectPartAction "New RouterOS available"; - :set mailPtBodyAction "New RouterOS version is available, current version: v$runningVersion, new version: v$ROsVerAvail. \n$changelogUrl"; - } else={ - :if ($PkgUpdStatus = "System is already up to date") do={ - :log info ("$SMP No new RouterOS version is available, the latest version is already installed: `v$runningVersion`"); - :set isUpdateCheckSucceeded true; - :set mailSubjectPartAction "No os update available"; - :set mailPtBodyAction "No new RouterOS version is available, the latest version is already installed: `v$runningVersion`"; - } else={ - :log error ("$SMP Failed to check for new RouterOS version. Package check status: `$PkgUpdStatus`"); - :set isEmailNeedsToBeSent true; - :set mailSubjectPartAction "Error unable to check new os version"; - :set mailPtBodyAction "An error occurred while checking for a new RouterOS version.\nStatus returned: `$PkgUpdStatus`\n\nPlease review the logs on the device for more details and verify internet connectivity."; - } - } - } - # Checking if the script needs to install new os version - :if (($BackupAndUpdateMode = "osupdate" or $BackupAndUpdateMode = "updateOnce") and $isNewOsUpdateAvailable = true) do={ - :if ($installOnlyPatchUpdates = true) do={ - :if ([$FuncIsPatchUpdateOnly $runningVersion $ROsVerAvail] = true) do={ - :log info "$SMP New RouterOS version is available, and it is a patch update. Current version: v$runningVersion, new version: v$ROsVerAvail"; - :set isOsNeedsToBeUpdated true; - } else={ - :log info "$SMP The script will not install this update, because it is not a patch update. Current version: v$runningVersion, new version: v$ROsVerAvail"; - :set mailPtBodyAction ($mailPtBodyAction . "\nThis update will not be installed, because the script is set to install only patch updates."); - } - } else={ - :set isOsNeedsToBeUpdated true; - } - } - # Checking If the script needs to create a backup - :if ($forceBackup = true or $BackupAndUpdateMode = "backup" or $isOsNeedsToBeUpdated = true) do={ - :log info ("$SMP Starting backup process."); - :set isEmailNeedsToBeSent true; - :local backupName $backupNameTemplate; - # This means it's the first step where we create a backup before the update process - :if ($isOsNeedsToBeUpdated = true) do={ - :set backupName $backupNameBeforeUpdate; - #Email body if the purpose of the script is to update the device - :set mailSubjectPartAction "Update preparation"; - :set mailPtBodyAction ($mailPtBodyAction . "\nThe update process for device '$Identity' is scheduled to upgrade RouterOS from version v.$runningVersion to version v.$ROsVerAvail (Update channel: $updateChannel)"); - :set mailPtBodyAction ($mailPtBodyAction . "\nPlease note: The update will proceed only after a successful backup."); - :set mailPtBodyAction ($mailPtBodyAction . "\nA final report with detailed information will be sent once the update process is completed."); - :set mailPtBodyAction ($mailPtBodyAction . "\nIf you do not receive a second email within the next 10 minutes, there may be an issue. Please check your device logs for further information."); - } - :do { - :set mailAttachments [$FuncCreateBackups $backupName $BackupPassword $BackupSens]; - :set mailPtSubjectBackup "Backup created"; - :set mailPtBodyBackup "System backups have been successfully created and attached to this email."; - } on-error={ - :set isOsNeedsToBeUpdated false; - :set mailPtSubjectBackup "Backup failed"; - :set mailPtBodyBackup "The script failed to create backups. Please check device logs for more details."; - :log warning "$SMP Backup creation failed. Update process will be canceled if automatic update is enabled"; - } - } - :if ($isEmailNeedsToBeSent = true) do={ - :log info "$SMP Preparing to send email..."; - :local mailStep1Subject $mailSubjectPrefix; - :local mailStep1Body ""; - # subject - :if ($mailSubjectPartAction != "") do={:set mailStep1Subject ($mailStep1Subject . " - " . $mailSubjectPartAction)} - :if ($mailPtSubjectBackup != "") do={:set mailStep1Subject ($mailStep1Subject . " - " . $mailPtSubjectBackup)} - # body - :if ($mailPtBodyAction != "") do={:set mailStep1Body ($mailStep1Body . $mailPtBodyAction . "\n\n")} - :if ($mailPtBodyBackup != "") do={:set mailStep1Body ($mailStep1Body . $mailPtBodyBackup . "\n\n")} - :set mailStep1Body ($mailStep1Body . $mailBodyDeviceInfo . "\n__________\n" . $mailBodyCopyright); - :set mailStep1Body ($mailStep1Body . "\n[*** TEST START ***]\n\$DeviceInfo: " . [ $DeviceInfo ] . "\n[*** TEST END ***]\n"); - # Send email with backups - :do {$FuncSendEmailSafe $EmailGeneralTo $mailStep1Subject $mailStep1Body $mailAttachments} on-error={ - :set isOsNeedsToBeUpdated false; - :log error "$SMP The script will not proceed with the update process, because the email was not sent."; - } - } - :if ([:len $mailAttachments] > 0) do={ - :log info "$SMP Cleaning up backup files from the file system..."; - /file remove $mailAttachments; - :delay 2s; - } - :if ($isOsNeedsToBeUpdated = true) do={ - :global OldMode; - :log info "$SMP everything is ready to install new RouterOS, going to start the update process and reboot the device."; - :do { - :local nextStep 2; - :if ($isCloudHostedRouter = true) do={ - :log info "$SMP The device is a cloud hosted router, the second step updating the Routerboard firmware will be skipped."; - :set nextStep 3; - } - :local scheduledCommand (":delay 5s; /system/scheduler remove BKPUPD-NEXT-BOOT-TASK; \ - :global buGlobalVarScriptStep $nextStep; :global buGlobalVarTargetOsVersion \"$ROsVerAvail\"; \ - :delay 10s; /system/script { run $ScriptName; }"); - /system/scheduler add name=BKPUPD-NEXT-BOOT-TASK on-event=$scheduledCommand start-time=startup interval=0; - /system/package/update install; - } on-error={ - # Failed to install new os version, remove the task and variables - :do { - /system/scheduler remove BKPUPD-NEXT-BOOT-TASK; - :set BackupAndUpdateMode $OldMode; - :delay 2; - :do { - :global buGlobalVarTargetOsVersion (a); - } on-error={ - :set $mailSubjectPrefix ($mailSubjectPrefix . "ERROR Remove global variable \$buGlobalVarTargetOsVersion failed!"); - } -# :if ([ :len $OldMode ] > 0) do={ -# :do { -# :global OldMode (a); -# } on-error={ -# :set $mailSubjectPrefix ($mailSubjectPrefix . " ERROR Remove global variable \$OldMode failed!"); -# } -# } - } on-error={ - :log error "$SMP Failed to install new RouterOS version. Please check device logs for more details. \ - \nAnd Failed to remove task and variables! Check it!"; - :set $mailSubjectPrefix ($mailSubjectPrefix . " ERROR Remove task and global variables!"); - } - :log error "$SMP Failed to install new RouterOS version. Please check device logs for more details."; - :local mailUpdateErrorSubject ($mailSubjectPrefix . " - Update failed"); - :local mailUpdateErrorBody "The script was unable to install new RouterOS version. Please check device logs for more details."; - # Send email with error - $FuncSendEmailSafe $EmailGeneralTo $mailUpdateErrorSubject $mailUpdateErrorBody ""; - :error $exitErrorMessage; - } - } -} -## STEP 2: (Post-reboot) Upgrade RouterBOARD firmware -## Runs only if auto-update is enabled and a new RouterOS version was found -:if ($scriptStep = 2) do={ - :log info "$SMP The script is in the second step, updating Routerboard firmware."; - :log info "$SMP Upgrading routerboard firmware from v.$deviceRbCurrentFw to v.$deviceRbUpgradeFw"; - /system routerboard upgrade; - :delay 2; - :log info "$SMP routerboard upgrade process was completed, going to reboot in a moment!"; - ## Set task to send final report on the next boot - /system scheduler add name=BKPUPD-NEXT-BOOT-TASK on-event=":delay 2; :global buGlobalVarScriptStep 3; \ - :global buGlobalVarTargetOsVersion \"$buGlobalVarTargetOsVersion\"; :delay 10s; /system/script { run $ScriptName; }; \ - :delay 5s; /system/scheduler remove BKPUPD-NEXT-BOOT-TASK;" start-time=startup interval=0; - :delay 2; - /system reboot; -} - -## STEP 3: Final report (after second reboot, with delay). -## Runs only if auto-update is enabled and a new RouterOS version was found. -:if ($scriptStep = 3) do={ - :log info ("$SMP The script is in the third step, sending final report."); - :local targetOsVersion $buGlobalVarTargetOsVersion; - :do { -# /system/script/environment remove [find name="buGlobalVarTargetOsVersion"]; - :global buGlobalVarTargetOsVersion (a); - } on-error={ - :set $mailSubjectPrefix ($mailSubjectPrefix . " ERROR Remove global variable \$buGlobalVarTargetOsVersion failed!"); - } - - :if ([ :len $OldMode ] != 0) do={ - :do { -# /system/script/environment remove [find name="OldMode"]; - :global OldMode (a); - } on-error={ - :set $mailSubjectPrefix ($mailSubjectPrefix . " ERROR! Remove global variable \$OldMode failed!"); - } - } - :if ([:len $targetOsVersion] = 0) do={ - :log warning "$SMP Something is wrong, the script was unable to get the target updated OS version from the global variable."; - } - :local mailStep3Subject $mailSubjectPrefix; - :local mailStep3Body ""; - :if ($targetOsVersion = $runningVersion) do={ - :log info "$SMP Successfully verified new RouterOS version: target: `$targetOsVersion`, current: `$runningVersion`"; - :set mailStep3Subject ($mailStep3Subject . " - Update completed - Backup created"); - :set mailStep3Body ($mailStep3Body . "RouterOS and routerboard upgrade process was completed"); - :set mailStep3Body ($mailStep3Body . "\nNew RouterOS version: v.$targetOsVersion, routerboard firmware: v.$deviceRbCurrentFw"); - :set mailStep3Body ($mailStep3Body . "\n$changelogUrl\nBackups of the upgraded system are in the attachment of this email.\n\n$mailBodyDeviceInfo\n\n$mailBodyCopyright"); - :set mailAttachments [$FuncCreateBackups $backupNameAfterUpdate $BackupPassword $BackupSens]; - } else={ - :log error "$SMP Failed to verify new RouterOS version: target: `$targetOsVersion`, current: `$runningVersion`"; - :set mailStep3Subject ($mailStep3Subject . " - Update failed"); - :set mailStep3Body ($mailStep3Body . "The script was unable to verify that the new RouterOS version was installed, target version: `$targetOsVersion`, current version: `$runningVersion`\nCheck device logs for more details.\n\n$mailBodyDeviceInfo\n\n$mailBodyCopyright"); - } - $FuncSendEmailSafe $EmailGeneralTo $mailStep3Subject $mailStep3Body $mailAttachments; - :if ([:len $mailAttachments] > 0) do={ - :log info "$SMP Cleaning up backup files from the file system..."; - /file remove $mailAttachments; - :delay 2; - } - :log info ("$SMP Final report email sent successfully, and the script has finished."); -} -:log info ("$SMP the script has finished, script step: `$scriptStep` \n\n"); - -} do={:global ExitError; $ExitError $ExitOK [ :jobname ] $Err} +#!rsc by Vados +# RouterOS script: AM-Fetch-Generator +# Script comment: Downloader for Amnezia generated scripts +# Copyright (c) 2007-2026 Vados +# +# +# requires RouterOS, version>=6.43.7 +# requires device-mode, fetch +# +# Updated: 17/08/2026 +# Website: https://github.com/vados-dev/ +#:local ExitOK false; +#:onerror Err { +# :global GlobalConfReady; :global GlobalFuncReady; +# :retry { :if ($GlobalConfReady != true || $GlobalFuncReady != true) \ +# do={ :error ("Global config and/or functions not ready."); }; } delay=500ms max=50; +# :local ScriptName [ :jobname ]; +# ============================================================ +# AWG Proxy -- MikroTik RouterOS (protocol: v2) +# 2026-07-21 10:33:39 +# ============================================================ + +# 0. Check prerequisites +:if ([:len [/system/package/find where name="container" disabled=no]] = 0) do={ + :put "Container package is not installed or disabled. Install it and reboot." + :error "Usage: /system/device-mode/update container=yes" +} + +# Check storage device +{ + :local targetDisk "usb1" + :local diskFree 0 + :do { :set diskFree [/disk/get [find where mount-point=$targetDisk] free] } on-error={} + :if ($diskFree = 0) do={ + :local baseDisk $targetDisk + :local dashPos [:find $targetDisk "-part"] + :if ([:typeof $dashPos] != "nil") do={ :set baseDisk [:pick $targetDisk 0 $dashPos] } + :if ([:len [/disk/find where slot=$baseDisk]] > 0) do={ + :put ("Device $baseDisk found but $targetDisk is not available.") + :put "Format $baseDisk as ext4? ALL DATA WILL BE ERASED!" + :put "Press 'y' to confirm, any other key to cancel:" + :local key [/terminal/inkey timeout=30] + :if ($key = 121 || $key = 89) do={ + :put "Formatting $baseDisk as ext4..." + /disk/format-drive $baseDisk file-system=ext4 label=$baseDisk + :delay 3s + :do { :set diskFree [/disk/get [find where mount-point=$targetDisk] free] } on-error={} + :if ($diskFree = 0) do={ + :put ("ERROR: $targetDisk not found after formatting.") + :put "Check mount point: /disk/print" + :error ("Mount point $targetDisk not found after formatting") + } + :put ("Formatted OK. Free: " . ($diskFree / 1048576) . " MB") + } else={ + :error "Formatting cancelled" + } + } else={ + :put ("ERROR: Storage device $targetDisk not found.") + :put "Check available disks: /disk/print" + :error ("Storage not found: " . $targetDisk) + } + } else={ + :put ("Storage OK: $targetDisk (" . ($diskFree / 1048576) . " MB free)") + } +} + +# 1. Uninstall script +/system/script/add name=awg2vds-uninstall comment=awg2vds source={ + :put "Uninstallimg AmneziaWG..." + :log info "Uninstallimg AmneziaWG..." + :local prevDNS "" + :do { :set prevDNS [/container/envs/get [find where list="awg2vds-env" key="AWG_PREV_DNS"] value] } on-error={} + :local prevTmpdir "" + :do { :set prevTmpdir [/container/envs/get [find where list="awg2vds-env" key="AWG_PREV_TMPDIR"] value] } on-error={} + /ip/route/remove [find where comment=awg2vds-tunnel] + /ip/route/remove [find where comment=awg2vds-dns] + /container/stop [find where interface=veth-awg2vds] + :delay 7s + /container/remove [find where interface=veth-awg2vds] + /container/envs/remove [find where list="awg2vds-env"] + :if ($prevDNS != "") do={ + :do { /ip/dns/set servers="$prevDNS" } on-error={} + :put ("DNS restored to: $prevDNS") + } + /container/config set tmpdir=$prevTmpdir + :put ("Container tmpdir restored to: $prevTmpdir") + /ip/firewall/nat/remove [find where out-interface="wg-awg2vds"] + /ip/firewall/nat/remove [find where src-address="172.20.200.68/30"] + /ip/address/remove [find where address="172.20.200.69/30"] + /interface/veth/remove [find where name="veth-awg2vds"] + /interface/wireguard/peers/remove [find where interface="wg-awg2vds"] + /ip/address/remove [find where interface="wg-awg2vds"] + /interface/wireguard/remove [find where name="wg-awg2vds"] + :do { /file/remove [find where name~"awg2vds.+tar"] } on-error={} + :do { /file/remove [find where name="usb1/awg2vds"] } on-error={} + :do { /file/remove [find where name="usb1/ru_ranges_timeout.rsc"] } on-error={} + :do { /file/remove [find where name~"usb1/pull"] } on-error={} + :put "Uninstall AmneziaWG Proxy complete!" + :log info "Uninstall AmneziaWG Proxy complete!" + /system/script/remove [find where name=awg2vds-uninstall] +} + +# 2. Network infrastructure +/interface/veth/add name=veth-awg2vds address=172.20.200.70/30 gateway=172.20.200.69 +/ip/address/add address=172.20.200.69/30 interface=veth-awg2vds +/ip/firewall/nat/add chain=srcnat action=masquerade src-address=172.20.200.68/30 + +# 3. WireGuard interface (MikroTik derives the public key automatically) +/interface/wireguard/add name=wg-awg2vds private-key="4LNSJJNEDeT2LFXkFx0XyrNp2Mp7YXlS8SawhqNfKGQ=" listen-port=26084 disabled=yes +/interface/wireguard/peers/add interface=wg-awg2vds public-key="qHFd72XDJJ5UrUZCZ0vRFQmzxP7eUYcOuaGL9ZbHtyg=" preshared-key="ckhzjCcr15ve+IeDRYveYITR7a/CMZCcOZ2Z0cABKD8=" endpoint-address=172.20.200.70 endpoint-port=51820 allowed-address=10.10.10.1/24,10.30.30.0/24,0.0.0.0/0 persistent-keepalive=10 +/ip/address/add address=10.10.10.2/24 interface=wg-awg2vds + +# 4. Container environment variables +/container/envs/add list=awg2vds-env key=AWG_LISTEN value=":51820" +/container/envs/add list=awg2vds-env key=AWG_REMOTE value="109.107.177.79:443" +/container/envs/add list=awg2vds-env key=AWG_JC value="6" +/container/envs/add list=awg2vds-env key=AWG_JMIN value="171" +/container/envs/add list=awg2vds-env key=AWG_JMAX value="481" +/container/envs/add list=awg2vds-env key=AWG_S1 value="50" +/container/envs/add list=awg2vds-env key=AWG_S2 value="139" +/container/envs/add list=awg2vds-env key=AWG_H1 value="1189447486-1189448396" +/container/envs/add list=awg2vds-env key=AWG_H2 value="538303610-538304276" +/container/envs/add list=awg2vds-env key=AWG_H3 value="101001960-101002598" +/container/envs/add list=awg2vds-env key=AWG_H4 value="1783887443-1783887851" +/container/envs/add list=awg2vds-env key=AWG_SERVER_PUB value="qHFd72XDJJ5UrUZCZ0vRFQmzxP7eUYcOuaGL9ZbHtyg=" +/container/envs/add list=awg2vds-env key=AWG_CLIENT_PUB value=[/interface/wireguard/get [find name=wg-awg2vds] public-key] +/container/envs/add list=awg2vds-env key=AWG_S3 value="17" +/container/envs/add list=awg2vds-env key=AWG_S4 value="6" +/container/envs/add list=awg2vds-env key=AWG_I1 value="" +/container/envs/add list=awg2vds-env key=AWG_I2 value="" +/container/envs/add list=awg2vds-env key=AWG_I3 value="" +/container/envs/add list=awg2vds-env key=AWG_I4 value="" +/container/envs/add list=awg2vds-env key=AWG_I5 value="" + +# 5. DNS +{ :local rawDNS [/ip/dns/get servers]; :local prevDNS ""; :foreach s in=[:toarray $rawDNS] do={ :if ([:len $prevDNS] > 0) do={ :set prevDNS "$prevDNS,$s" } else={ :set prevDNS $s } }; /container/envs/add list=awg2vds-env key=AWG_PREV_DNS value="$prevDNS" } +# NOTE: replaces existing DNS configuration +/ip/dns/set servers=10.10.10.1 +/ip/route/add dst-address=10.10.10.1/32 gateway=wg-awg2vds distance=1 comment=awg2vds-dns + +# 6. Download, create and start container +:local prevTmpdir [/container/config/get tmpdir] +/container/envs/add list=awg2vds-env key=AWG_PREV_TMPDIR value=$prevTmpdir +/container/config set tmpdir=usb1/pull memory-high=200M +{ + :local arch [/system/resource/get architecture-name] + :local ver [/system/resource/get version] + :local dotPos [:find $ver "."] + :local rest [:pick $ver ($dotPos + 1) [:len $ver]] + :local endPos [:find $rest "."] + :if ([:typeof $endPos] = "nil") do={ :set endPos [:find $rest " "] } + :local minor [:tonum [:pick $rest 0 $endPos]] + :local suffix "" + :if ($minor <= 20) do={ :set suffix "-7.20-Docker" } + :local file "" + :if ($arch = "arm64") do={ :set file ("awg-proxy-arm64" . $suffix . ".tar.gz") } + :if ($arch = "arm") do={ :set file ("awg-proxy-arm" . $suffix . ".tar.gz") } + :if ($arch ~ "x86") do={ :set file ("awg-proxy-amd64" . $suffix . ".tar.gz") } + :if ($file = "") do={ :error "Unsupported architecture: $arch" } + #:local url "https://github.com/timbrs/amneziawg-mikrotik-c/releases/latest/download/$file" + :local url "https://ros.vados.ru/amneziawg/img/$file" + :local filePath ("usb1/pull/" . $file) + :do { /file/make-directory usb1/pull } on-error={} + :if ([:len [/file/find where name=$filePath]] = 0) do={ + :local freeStorage 0 + :do { :set freeStorage [/disk/get [find where mount-point="usb1"] free] } on-error={} + :if ($freeStorage < 262144) do={ + :put ("WARNING: Low disk space (" . ($freeStorage / 1024) . "KB free). Need at least 256KB.") + :put "See: https://github.com/timbrs/amneziawg-mikrotik-c#insufficient-disk-space" + :error "Insufficient disk space" + } + :put ("Fetching: $url -> " . $filePath) + /tool/fetch url=$url dst-path=$filePath http-max-redirect-count=10 + } else={ + :put ("File already exists: " . $filePath) + } + /container/add file=$filePath interface=veth-awg2vds envlist=awg2vds-env hostname=awg2vds root-dir=usb1/awg2vds logging=no start-on-boot=yes comment=awg2vds + :if ($minor > 20) do={ [:parse "/container/set [find where interface=veth-awg2vds] shm-size=4M"] } + /file/remove $filePath + :local freeMem [/system/resource/get free-memory] + :if ($freeMem < 16777216) do={ + :put ("WARNING: Low memory (" . ($freeMem / 1048576) . "MB free). 16MB+ recommended.") + } + /container/start [find where interface=veth-awg2vds] + :do { /file/remove [find where name="console-dump.txt"] } on-error={} + :put "Waiting for container to start..." + :delay 5s + /interface/wireguard/enable wg-awg2vds + :put "WireGuard interface enabled" + :put "Installation complete!" + :put "Ping test: 10.10.10.1 via AmneziaWG tunnel. repeat 10 times" + /ping 10.10.10.1 count=10 interval=1 + :put "" + :put "======= NEXT STEPS =======" + :put "" + :put "Route specific traffic through the AmneziaWG tunnel:" + :put "" + :put " Route a single host:" + :put " /ip/route/add dst-address=8.8.8.8/32 gateway=wg-awg2vds comment=awg2vds-tunnel" + :put "" + :put " Route a subnet:" + :put " /ip/route/add dst-address=100.0.0.0/8 gateway=wg-awg2vds comment=awg2vds-tunnel" + :put "" + :put " Remove a route:" + :put " /ip/route/remove [find where comment=awg2vds-tunnel dst-address=8.8.8.8/32]" + :put "" + :put " Show all tunnel routes:" + :put " /ip/route/print where comment=awg2vds-tunnel" + :put "" + :put " Uninstall:" + :put " /system/script/run awg2vds-uninstall" + :put "=========================" +} + +# 7. Verification +# /container/print +# /interface/wireguard/print +# /interface/wireguard/peers/print diff --git a/html/AM-GlobalFunc.rsc b/html/AM-GlobalFunc.rsc index 127205b02..8f4876c9e 100644 --- a/html/AM-GlobalFunc.rsc +++ b/html/AM-GlobalFunc.rsc @@ -17,9 +17,9 @@ :global GlobalFuncReady false; # Git commit id & info, expected configuration version -:global CommitId "main/9348/4b652655"; +:global CommitId "main/9349/97bb2cdb"; :global CommitTAG "unknown"; -:global CommitInfo "Autocommit in repo: ros.vados.ru, generated by owner: root, in 21:10:04 17.08.2026."; +:global CommitInfo "Autocommit in repo: ros.vados.ru, generated by owner: root, in 21:15:04 17.08.2026."; # The variables are received right here. :global GlobalConfVersion 140;