From fd2f8afb42e5546d3a487ed9d0355f952b5315e9 Mon Sep 17 00:00:00 2001 From: vados-dev Date: Mon, 17 Aug 2026 10:20:03 +0300 Subject: [PATCH] Backup for MikroNUC created successfully at Aug 17 10:20:03. --- .../20260817/20260817102001-Binary.backup | Bin 0 -> 441653 bytes MikroNUC/20260817/20260817102001-Config.rsc | 5536 +++++++++++++++++ .../20260817/20260817102001-Diff-Config.rsc | 13 + 3 files changed, 5549 insertions(+) create mode 100644 MikroNUC/20260817/20260817102001-Binary.backup create mode 100644 MikroNUC/20260817/20260817102001-Config.rsc create mode 100644 MikroNUC/history/20260817/20260817102001-Diff-Config.rsc diff --git a/MikroNUC/20260817/20260817102001-Binary.backup b/MikroNUC/20260817/20260817102001-Binary.backup new file mode 100644 index 0000000000000000000000000000000000000000..19ab909d10847f48accdda4054701142d4d61597 GIT binary patch literal 441653 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%0DJFp+K(-BBb5(di$Z8oG>?>n~Rl*3`i#-0)C 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

6*T8 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 Cq{O)Z literal 0 HcmV?d00001 diff --git a/MikroNUC/20260817/20260817102001-Config.rsc b/MikroNUC/20260817/20260817102001-Config.rsc new file mode 100644 index 0000000..6615400 --- /dev/null +++ b/MikroNUC/20260817/20260817102001-Config.rsc @@ -0,0 +1,5536 @@ +# 2026-08-17 10:20:02 by RouterOS 7.23.3 +# system id = NmJcEM+8XAP +# +/caps-man channel +add band=2ghz-g/n extension-channel=disabled frequency=2422 name=2G-3 \ + reselect-interval=20m save-selected=yes +add band=5ghz-a/n/ac extension-channel=XXXX frequency=5660 name=5G-5660 \ + save-selected=yes +add band=5ghz-a/n/ac extension-channel=XXXXXXXX frequency=5160,5200,5280 \ + name=5G-5160-5280 reselect-interval=30m save-selected=yes +add band=5ghz-n/ac extension-channel=XXXX frequency=5180 name=5G-36 \ + reselect-interval=30m save-selected=yes +add band=5ghz-n/ac extension-channel=XXXX frequency=5200 name=5G-40 \ + reselect-interval=30m save-selected=yes +add band=5ghz-n/ac extension-channel=XXXX frequency=5220 name=5G-44 \ + reselect-interval=30m save-selected=yes +add band=5ghz-n/ac extension-channel=XXXX frequency=5240 name=5G-48 \ + reselect-interval=30m save-selected=yes +add band=2ghz-g/n extension-channel=disabled frequency=2442 name=2G-7 \ + reselect-interval=20m save-selected=yes +add band=2ghz-g/n extension-channel=disabled frequency=2462 name=2G-11 \ + reselect-interval=20m save-selected=yes +/interface bridge +add name=Bridge-LAN +/interface ethernet +set [ find default-name=ether1 ] disable-running-check=no +/caps-man security +add authentication-types=wpa2-psk encryption=aes-ccm group-encryption=aes-ccm \ + group-key-update=30m name=xs-Sec +add authentication-types=wpa2-psk encryption=aes-ccm group-encryption=aes-ccm \ + group-key-update=30m name=WPA2 +/disk +add file-path=/sata1/swap file-size=2046.9MiB slot=file-sata1-swap swap=yes \ + type=file +add slot=ram tmpfs-max-size=100000000 type=tmpfs +add slot=tmpfs tmpfs-max-size=715827882 type=tmpfs +/interface list +add include=dynamic,static name=LAN +add exclude=LAN name=WAN +add exclude=LAN include=WAN name=Internet +/caps-man datapath +add bridge=Bridge-LAN client-to-client-forwarding=yes interface-list=LAN \ + local-forwarding=yes name=xs-DataPath +add bridge=Bridge-LAN client-to-client-forwarding=yes interface-list=LAN \ + local-forwarding=yes name=DataPath +/caps-man configuration +add channel.band=2ghz-b/g/n country=no_country_set datapath=xs-DataPath mode=\ + ap name=xs-2G rx-chains=0,1,2,3 security=xs-Sec ssid=vados tx-chains=\ + 0,1,2,3 +add channel.band=5ghz-a/n/ac country=no_country_set datapath=xs-DataPath \ + installation=any mode=ap name=xs-5G rx-chains=0,1,2,3 security=xs-Sec \ + ssid=vados5g tx-chains=0,1,2,3 +add channel.band=2ghz-b/g/n .control-channel-width=20mhz .extension-channel=\ + XX .frequency=2422,2442,2462 .reselect-interval=20m country=belarus \ + datapath=DataPath datapath.client-to-client-forwarding=yes \ + .local-forwarding=yes hw-protection-mode=rts-cts installation=any name=2G \ + rx-chains=0,1,2,3 security=WPA2 ssid=vados tx-chains=0,1,2,3 +add channel.band=5ghz-a/n/ac .control-channel-width=20mhz .extension-channel=\ + XXXX .frequency=5180,5200,5220,5240,5260 .reselect-interval=20m \ + .skip-dfs-channels=yes country=belarus datapath=DataPath \ + datapath.client-to-client-forwarding=yes .local-forwarding=yes \ + hw-protection-mode=rts-cts installation=any name=5G security=WPA2 ssid=\ + vados5g +/interface wireless security-profiles +set [ find default=yes ] supplicant-identity=MikroMIC +/iot wiliot servers +set *1 address=mqtt.us-east-2.prod.wiliot.cloud name="Wiliot US East" +/ip dhcp-server option +add code=12 name=HostName +add code=119 force=yes name=domain-search-list value="0x03'WORKGROUP'0x00" +/ip dhcp-server option sets +add name=P1505n options=HostName +add name=StartLife1 options=HostName +add name=amsterDev options=HostName +add name=MiTV options=HostName +add name=miller options=HostName +add name=amster options=HostName +add name=garage options=HostName +add name=domain-search-set options=domain-search-list +/ip pool +add name=dhcp ranges=10.30.30.150-10.30.30.200 +/system logging action +add email-start-tls=yes email-to=vados@vados.ru name=email target=email +/system script +add dont-require-permissions=no name=test owner=vados policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="#\ + \r\ + \n#:local ScriptName [ :jobname ];\r\ + \n:local expSrc;\r\ + \n:local FileName;\r\ + \n:local FilePath;\r\ + \n:local DirPath \"tmpfs/\";\r\ + \n:global WaitForFile;\r\ + \n:global IfThenElse;\r\ + \n\r\ + \n:foreach Script in=([ /system/script find where name~\"Amster-GlobalConf\ + *\" ]) do={\r\ + \n:local ScriptVal [ /system/script/get \$Script ];\r\ + \n\r\ + \n:local expName [ (\$ScriptVal->\"name\") ];\r\ + \n#[/system/script get \$expScript name];\r\ + \n:delay 2s;\r\ + \n\r\ + \n:set expSrc [/system/script print where name=\$expName];\r\ + \n/system/script print where name=\$expName;\r\ + \n#[:put [/system/script get \$expScript source]];\r\ + \n:put [\$expSrc];\r\ + \n:delay 2s;\r\ + \n:set FileName \$expName;\r\ + \n:set FilePath (\$DirPath . \$FileName);\r\ + \n\r\ + \n#:log warning \"\\n----------------------\\n\\\$expSrc = \$expSrc\\n____\ + ______________________________\";\r\ + \n:log info \"\\n\\\$FileName = '\$FileName'\";\r\ + \n:log info \"\\n\\\$FilePath = \$FilePath\";\r\ + \n#:log warning {/system/script/get source=[/system/script/get \$expScript\ + \_source];};\r\ + \n\r\ + \n#\$[/system script get [/system script find name=\"\$FileName\"] source]\ + \r\ + \n\r\ + \n#:local gettingScript \"\$FileName\";\r\ + \n#:execute script=\"[[:parse \\\"[:parse { :put (\"\$[/system script get \ + [/system script find name=\$FileName] source]\"); } \\\"]]\"; \\\r\ + \n\r\ + \n#:execute script={:put \"[:parse \\\"[:parse [/system/script get [/syste\ + m/script find name=\$gettingScript] source];\\\"]]\";} \\\r\ + \n# file=(\$FilePath . \".rsc\\00\");\r\ + \n:do {\r\ + \n:local fName (\$FilePath . \".rsc\");\r\ + \n#:delay 3s;\r\ + \n#/file add name=\$fName;\r\ + \n:put [:tostr [\$expSrc]];\r\ + \n/file set \$fName contents=[:put [\$expSrc]];\r\ + \n:delay 3s;\r\ + \n\$WaitForFile \$fName;\r\ + \n(\$FilePath . \".rsc\");\r\ + \n#:delay 2s;\r\ + \n\r\ + \n} on-error={\r\ + \n:log error \"\\nERROR \\\$FilePath = \$FilePath\";\r\ + \n}\r\ + \n#\$WaitForFile (\$FilePath . \".rsc\");\r\ + \n#:execute script={ :put [/system/script get [:put \$FileName] source]; }\ + ;\r\ + \n#/system/script export \$Sscript file=\$FilePath;\r\ + \n#/system/script/export [:put [/system/script/get \$expScript source]] fi\ + le=\$FilePath;\r\ + \n#:execute script={ :put [/system/script/get \$expScript source]; } \\\r\ + \n\r\ + \n#:log warning \"\\n\\\$expScript = \$expScript\";\r\ + \n# \\\r\ + \n# file=(\$FilePath . \".rsc\\00\");\r\ + \n#script={:put [/system/script/get \$FileName source];} file=(\$FilePath\ + \_. \".rsc\\00\");\r\ + \n#/system script export \$src file=(\$FilePath . \$FileName);\r\ + \n}\r\ + \n\r\ + \n:log warning \"\\nComplete!\";\r\ + \n\r\ + \n # :execute script={ :put [ /system/script/get Amster-GlobalConfig sou\ + rce ]; } \\\r\ + \n # file=(\$FilePath . \".conf\\00\");\r\ + \n # \$WaitForFile (\$FilePath . \".conf\");\r\ + \n # :onerror Err {\r\ + \n # /tool/fetch upload=yes url=(\$BackupUploadUrl . \"/\" . \$FileName\ + \_. \".conf\") \\\r\ + \n # user=\$BackupUploadUser password=\$BackupUploadPass src-path=(\ + \$FilePath . \".conf\");\r\ + \n # :set ConfigFile [ /file/get (\$FilePath . \".conf\") ];\r\ + \n # :set (\$ConfigFile->\"name\") (\$FileName . \".conf\");\r\ + \n " +add dont-require-permissions=no name=test1 owner=vados policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="\ + \r\ + \n#:local runningVer [:find [/system/resource/get version] \"(\"];\r\ + \n\r\ + \n:local runningChannel;\r\ + \n:local runningVersion;\r\ + \n:local ParseCustom;\r\ + \n\r\ + \n:set ParseCustom do={\r\ + \n :local Str [ :tostr \$1 ];\r\ + \n :local Start [:find [ \$Str ] [:tostr \$2]];\r\ + \n :local End [ :tostr \$3 ];\r\ + \n :local Num [ :tonum \$4 ];\r\ + \n :local Return;\r\ + \n\r\ + \n :if ([:len \$Num] = 0) do={:set Num \"0\"}\r\ + \n :local rest [:pick \$Str (\$Start+\$Num) [:len \$Str]]; \r\ + \n :if ([:len \$End ] = 0) do={:set Return [:pick \$Str \$Num \$Start]} e\ + lse={\r\ + \n :set End [:find \$rest \$End];\r\ + \n :set Return [:pick \$rest 0 \$End];\r\ + \n }\r\ + \n:return \$Return;\r\ + \n}\r\ + \n \r\ + \n:global runningChannel [\$ParseCustom [/system/resource/get version] \"(\ + \" \")\" 1];\r\ + \n:log warning [\$runningChannel];\r\ + \n:set runningVersion [\$ParseCustom [/system/resource/get version] \" \" \ + \")\" 2];\r\ + \n:log warning [\$runningVersion];\r\ + \n\r\ + \n\r\ + \n\r\ + \n# :if ([:len \$open] = 0) do={\r\ + \n# :log error \"\$SMP Could not extract installed OS channel from vers\ + ion string: `\$runningOsAndChannel`.\";\r\ + \n# :error \"\$SMP error, check logs\";\r\ + \n# }\r\ + \n# :local rest [:pick \$runningOsAndChannel (\$open+1) [:len \$runningOs\ + AndChannel]];\r\ + \n# :local close [:find \$rest \")\"];\r\ + \n# :local channel [:pick \$rest 0 \$close];\r\ + \n# :return \$channel;\r\ + \n#}" +add dont-require-permissions=no name=01-Amster-Init-Script owner=vados \ + policy=ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \ + source="# Amster Initial Script\r\ + \n#\r\ + \n#:global GlobalEnvRemove true; /system/script {run Amster-GlobalEnvRemov\ + e; };\r\ + \n#:delay 1s;\r\ + \n:#global ReloadGlobal true; \r\ + \n\r\ + \n:global AmScriptsUrl \"https://ros.vados.ru/\";\r\ + \n:global defOwner \"vados\";\r\ + \n:global AmFirstRun;\r\ + \n\r\ + \n:set AmFirstRun do={\r\ + \n :global AmScriptsUrl;\r\ + \n :global defOwner;\r\ + \n #:global ParseKeyValueStore;\r\ + \n #:local AmUpdCRLF true;\r\ + \n #:local IfThenElse;\r\ + \n #:set IfThenElse do={:if ([ :tostr \$1 ] = \"true\" || [ :tobool \$1\ + \_] = true) do={:return \$2}; :return \$3}\r\ + \n #/certificate/settings/set builtin-trust-store=fetch;\r\ + \n #/tool/fetch \"\$scriptsUrl/certs/Root-YE.pem\" dst-path=\"root-ye.p\ + em\";\r\ + \n #/certificate/import file-name=\"root-ye.pem\" passphrase=\"\";\r\ + \n # For basic verification we rename the certificate and print it by f\ + ingerprint. Make sure exactly this one certificate (\"Root-YE\") is shown.\ + \r\ + \n #/certificate/set name=\"Root-YE\" [ find where common-name=\"Root Y\ + E\" ];\r\ + \n #/certificate/print proplist=name,fingerprint where fingerprint=\"e1\ + 4ffcad5b0025731006caa43a121a22d8e9700f4fb9cf852f02a708aa5d5666\";\r\ + \n #:log info \"\\nAmScriptsUrl = \$AmScriptsUrl\";\r\ + \n :local Scripts {\r\ + \n \"Amster-GlobalConfig\"\r\ + \n \"Amster-GlobalFunc\";\r\ + \n# \"Amster-SFTP-backup\"\r\ + \n# \"Amster-BkpEmail-and-upd\";\r\ + \n };\r\ + \n# /system/script/set owner=(\$ScriptVal->\"name\") \\\r\ + \n# source=[ \$IfThenElse (\$ScriptUpdatesCRLF = true) \$SourceCRLF \$S\ + ourceNew ] \$Script;\r\ + \n :foreach Script in=\$Scripts do={\r\ + \n :put \"Installing \$Script...\";\r\ + \n /system/script/remove [ find where name=\$Script ];\r\ + \n /system/script/add name=\$Script owner=\$defOwner source=([ /too\ + l/fetch (\"\$AmScriptsUrl\" . \$Script . \".rsc\") output=user as-value ]-\ + >\"data\");\r\ + \n /system/script/set source=[ :tocrlf [ get \$Script source ] ] \$\ + Script;\r\ + \n };\r\ + \n /system/script { run Amster-GlobalConfig; run Amster-GlobalFunc; }\r\ + \n}\r\ + \n:global AmUpdRun;\r\ + \n:set AmUpdRun do={\r\ + \n #:local UpdScript [ :tostr \$1 ];\r\ + \n #:global ParseKeyValueStore;\r\ + \n :global AmScriptsUrl;\r\ + \n :global defOwner;\r\ + \n :local Scripts {\r\ + \n# \"Amster-GlobalConfig\"\r\ + \n# \"Amster-GlobalFunc\"\r\ + \n# \"Amster-GlobalRemove\"\r\ + \n \"Amster-backup-SFTP\"\r\ + \n \"Amster-SFTP-backup\"\r\ + \n \"Amster-BkpEmail-and-upd\"\r\ + \n \"Amster-TG-Bot\"\r\ + \n \"Amster-TG-Notification\";\r\ + \n };\r\ + \n :foreach Script in=\$Scripts do={\r\ + \n :put \"updating \$Script...\";\r\ + \n /system/script/remove [ find where name=\$Script ];\r\ + \n /system/script/add name=\$Script owner=\$defOwner source=([ /too\ + l/fetch (\"\$AmScriptsUrl\" . \$Script . \".rsc\") output=user as-value ]-\ + >\"data\");\r\ + \n /system/script/set source=[ :tocrlf [ get \$Script source ] ] \$\ + Script;\r\ + \n };\r\ + \n# /system/script/add name=\$Script owner=\$AmOwner source=([ /tool/fe\ + tch check-certificate=yes-without-crl (\"\$AmScriptsUrl\" . \$Script . \".\ + rsc\") output=user as-value ]->\"data\"); };\r\ + \n}\r\ + \n\r\ + \n\$AmFirstRun;\r\ + \n#\$AmUpdRun;\r\ + \n\r\ + \n#\$setUpdExist;\r\ + \n#:global setUpdExist;\r\ + \n#:global ParseKeyValueStore;\r\ + \n#:set setUpdExist do={\r\ + \n#:do {\r\ + \n#:global ParseKeyValueStore;\r\ + \n# :foreach Script in=[ /system/script/find where source~\"^#!rsc by Vad\ + os\\r\?\\n#comment=*\" ] do={\r\ + \n# :local ScriptVal [ /system/script/get \$Script ];\r\ + \n# :local ScriptInfo [ \$ParseKeyValueStore (\$ScriptVal->\"#comment\"\ + ) ];\r\ + \n# :local scriptName (\$ScriptVal->\"name\");\r\ + \n#:log warning (\"\\n\\\$scriptName = \". \$scriptName);\r\ + \n#: log warning (\"\\n\\\$ScriptInfo = \". \$ScriptInfo);\r\ + \n# }\r\ + \n#} on-error={}\r\ + \n#}\r\ + \n#\$name;\$owner;\$policy;\$dont-require-permissions;\$last-started;\$run\ + -count;\$source;\$invalid;\$comment;\$.id;\$.nextid;\$.dead;\$.about;\r\ + \n#;(evl / (evl /docommand=;(evl / (evl /foreachcounter=\$Script;do=;(evl \ + / (evl /localname=\$ScriptVal;value=(evl (evl /system/script/getnumber=\$S\ + cript))) (evl /localname=\$ScriptInfo;value=(evl (<%% \$ParseKeyValueStore\ + \_(> \$ParseKeyValueStore);(-> \$ScriptVal comment)))) (evl /localname=\$s\ + criptName) (evl /setname=\$scriptName;value=(-> #\$ScriptVal name)) (evl /\ + log/errormessage=(. \r\ + \n# \$ ScriptInfo = \$ScriptInfo)));in=(evl (evl /system/script/findwhere\ + =\$name;\$owner;\$policy;\$dont-require-permissions;\$last-started;\$run-c\ + ount;\$source;\$invalid;\$comment;\$.id;\$.nextid;\$.dead;\$.about;(~ \$so\ + urce (. ^#!rsc by Vados \r\ + \n# \? \r\ + \n#));5))));on-error=;(evl /)))\r\ + \n" +add dont-require-permissions=no name="Download cAP's Packages" owner=vados \ + policy=ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \ + source="# +------------------------------+\r\ + \n# | |\r\ + \n# | Mikrotik RouterOS Scripts |\r\ + \n# | _Download cAP's Packages_ |\r\ + \n# | |\r\ + \n# +------------------------------+\r\ + \n# https://github.com/sebastian13/mikrotik-routeros-scripts\r\ + \n#\r\ + \n\r\ + \n# Set a directory path providing the required space\r\ + \n# Add a microSD card https://wiki.mikrotik.com/wiki/Manual:System/Disks\ + \r\ + \n# or setup a RAM-Disk via '/disk/add type=tmpfs tmpfs-max-size=20M slot=\ + ram'\r\ + \n:local dir \"/tmpfs/capsman-packages\";\r\ + \n\r\ + \n\r\ + \n# Set Package Path if required, set policy to none if using 'upgrade-cap\ + s' script\r\ + \n/interface/wifi/capsman/set package-path=\$dir upgrade-policy=none;\r\ + \n/caps-man/manager/set package-path=\$dir upgrade-policy=none;\r\ + \n#require-same-version;\r\ + \n\r\ + \n:local installed [/system package get value-name=version [find where nam\ + e=\"routeros\"]]\r\ + \n\r\ + \n# Delete old downloads\r\ + \n# /file/remove [ find type=package name~\"capsman*\" ]\r\ + \n\r\ + \n# Starting from RouterOS version 7.13, drivers for older wireless and 60\ + GHz interfaces are\r\ + \n# part of the separate \"wireless\" package. The existing \"wifiwave2\" \ + package has been divided\r\ + \n# into distinct packages: \"wifi-qcom\" and \"wifi-qcom-ac\".\r\ + \n# RouterOS >=7.12 is required to upgrade to 7.13.\r\ + \n# https://forum.mikrotik.com/viewtopic.php\?t=202423\r\ + \n\r\ + \n:foreach architecture in={ \"arm\";\r\ + \n# \"arm64\";\r\ + \n \"x86_64\";\r\ + \n \"mipsbe\"} do={\r\ + \n :foreach package in={ \"routeros\";\r\ + \n# \"wifi-qcom-ac\";\r\ + \n# \"wifi-qcom\";\r\ + \n \"container\";\r\ + \n \"wireless\"} do={ :do {\r\ + \n :put \"\"\r\ + \n :put \"Download \$package \$installed for \$architecture\"\r\ + \n /tool fetch mode=https \\\r\ + \n url=\"https://download.mikrotik.com/routeros/\$installed/\$packa\ + ge-\$installed-\$architecture.npk\" \\\r\ + \n dst-path=\"\$dir/\$package-\$installed-\$architecture.npk\"\r\ + \n } on-error={ :put \"Error downloading the file. On a 404, note t\ + hat the package may not exist for the specified architecture!\" }\r\ + \n }\r\ + \n}\r\ + \n" +add comment="Script for setup routeros script system" \ + dont-require-permissions=no name=AM-setup owner=AM-setup policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="#\ + !rsc by Vados\r\ + \n# RouterOS script: AM-setup\r\ + \n# Script comment: Script for setup routeros script system\r\ + \n# Copyright (c) 2005-2026 Vados \r\ + \n#\r\ + \n#\r\ + \n# requires RouterOS, version=7.19\r\ + \n# requires device-mode, fetch\r\ + \n#\r\ + \n# :local ScriptName [ :jobname ];\r\ + \n# Additional commands:\r\ + \n#\r\ + \n# For scripts:\r\ + \n#:global NewScript \"script_name\"; \r\ + \n#:global GlobalScriptsUrl \"https://ros.vados.ru/\";\r\ + \n#/system/script/add name=\$NewScript owner=\$NewScript source=([/tool/fe\ + tch check-certificate=yes-without-crl (\$GlobalScriptsUrl . \$ScriptAdd . \ + \".rsc\") output=user as-value ]->\"data\");\r\ + \n#:global NewScript (a);\r\ + \n#:global ScriptInstallUpdate; \$ScriptInstallUpdate;\r\ + \n#\r\ + \n# For run from terminal:\r\ + \n#/system/script/add name=AM-setup owner=AM-Setup source=([/tool/fetch ch\ + eck-certificate=yes-without-crl \"https://ros.vados.ru/AM-setup.rsc\" outp\ + ut=user as-value ]->\"data\");\r\ + \n#\r\ + \n\r\ + \n:global ScriptUpdatesCRLF \"LF\";\r\ + \n#:if (\$ScriptUpdatesCRLF != false) do={\r\ + \n# :foreach Script in=[ /system/script/find where source~\"^#!rsc by Vad\ + os\" ] do={\r\ + \n# if (\$ScriptUpdatesCRLF = true) do={\r\ + \n# /system/script/set source=[ :tocrlf [ get \$Script source ] ] \$S\ + cript;\r\ + \n# } else={\r\ + \n# /system/script/set source=[ :tolf [ get \$Script source ] ] \$Scr\ + ipt;\r\ + \n# }\r\ + \n# }\r\ + \n#}\r\ + \n\r\ + \n# /import verbose=no \$ScriptName\r\ + \n:global Identity [/system/identity/get name];\r\ + \n:global SetupScript [ :jobname ];\r\ + \n:global RemoveMe true;\r\ + \n:global RunOnce (a);\r\ + \n:global InitSetup true;\r\ + \n:global tmpfs false;\r\ + \n\r\ + \n{\r\ + \n :local RmMe [:tobool \$1];\r\ + \n\r\ + \n :global GlobalScriptsUrl;\r\ + \n :global SetupScript;\r\ + \n :global RemoveMe;\r\ + \n :global InitSetup;\r\ + \n :global tmpfs;\r\ + \n\r\ + \n :local UrlSuffix \$Identity;\r\ + \n :local UrlDir \"certs/\";\r\ + \n :local CertUrl (\$GlobalScriptsUrl . \$UrlDir . \$UrlSuffix .\"/\"); \ + \r\ + \n :local CertCommonName \"Root YE\";\r\ + \n :local CertFileName \"Root-YE.pem\";\r\ + \n :local CertFingerprint \"e14ffcad5b0025731006caa43a121a22d8e9700f4fb9c\ + f852f02a708aa5d5666\";\r\ + \n\r\ + \n :if (\$SetupScript = false) do={\r\ + \n :global SetupScript \"AM-setup\";\r\ + \n }\r\ + \n :if (\$RemoveMe != false) do={\r\ + \n :set RemoveMe true;\r\ + \n }\r\ + \n\r\ + \n :log warning (\"\\n\\\$GlobalScriptsUrl = \" . \$GlobalScriptsUrl);\r\ + \n \r\ + \n\r\ + \n :if (!(((\$CertSettings->\"builtin-trust-anchors\") = \"trusted\" ||\ + \_\\\r\ + \n (\$CertSettings->\"builtin-trust-store\") ~ \"fetch\" || \\\r\ + \n (\$CertSettings->\"builtin-trust-store\") = \"all\") && \\\r\ + \n [ :len [ /certificate/builtin/find where common-name=\$CertComm\ + onName ] ] > 0)) do={\r\ + \n :put \"Importing certificate...\";\r\ + \n /tool/fetch (\$CertUrl . \$CertFileName) dst-path=\$CertFileName\ + \_as-value;\r\ + \n :delay 1s;\r\ + \n /certificate/import file-name=\$CertFileName passphrase=\"\";\r\ + \n /certificate/set name=\"Root-YE\" [ find where common-name=\"Roo\ + t YE\" ];\r\ + \n :delay 1s;\r\ + \n };\r\ + \n :local CertSettings [ /certificate/settings/get ];\r\ + \n :if ([ :len [ /certificate/find where fingerprint=\$CertFingerprint ] \ + ] != 1) do={\r\ + \n :error \"Something is wrong with your certificates!\";\r\ + \n };\r\ + \n\r\ + \n:local MkTmpfs do={\r\ + \n :local TmpFs [ /disk/find where slot=tmpfs type=tmpfs ];\r\ + \n :if ([ :len \$TmpFs ] = 1) do={\r\ + \n :if ([ /disk/get \$TmpFs disabled ] = true) do={\r\ + \n :log info (\"The tmpfs is disabled, enabling.\");\r\ + \n /disk/enable \$TmpFs;\r\ + \n }\r\ + \n :return true;\r\ + \n }\r\ + \n :log info (\"Try to removing directory 'tmpfs'.\");\r\ + \n :do {\r\ + \n :retry {\r\ + \n /file/remove tmpfs;\r\ + \n :return true;\r\ + \n } delay=100ms max=10;\r\ + \n } on-error={ }\r\ + \n# \$RmDir \"tmpfs\";\r\ + \n :onerror Err {\r\ + \n /disk/add slot=tmpfs type=tmpfs tmpfs-max-size=([ /system/resource/g\ + et total-memory ] / 3);\r\ + \n :do {\r\ + \n :retry {\r\ + \n /file/get tmpfs;\r\ + \n :return true;\r\ + \n } delay=222ms max=10;\r\ + \n } on-error={ }\r\ + \n } do={\r\ + \n :log warning (\"Creating disk of type tmpfs failed: \" . \$Err);\r\ + \n :return false;\r\ + \n }\r\ + \n# :return true;\r\ + \n}\r\ + \n:if (\$tmpfs = true) do={\r\ + \n :put \"Tmpfs variable is true. Checking for tmpfs/scripts directory...\ + \";\r\ + \n :if ([/file find name=\"tmpfs/scripts\"] != \"\") do={\r\ + \n :log info (\$0 . \"Directory tmpfs/scripts exsist.\");\r\ + \n :return \$true;\r\ + \n } else={\r\ + \n :log warning (\$0 . \"Directory tmpfs/scripts does't exsist.\");\r\ + \n :if ([ \$MkTmpfs ] = false) do={\r\ + \n :log error (\$0 . \"Directory tmpfs is not created!\");\r\ + \n :return false;\r\ + \n } else={\r\ + \n :onerror Err {\r\ + \n /file/add type=\"directory\" name=/tmpfs/scripts;\r\ + \n :do {\r\ + \n :retry {\r\ + \n /file/get /tmpfs/scripts;\r\ + \n :return true;\r\ + \n } delay=100ms max=10;\r\ + \n } on-error={ }\r\ + \n } do={\r\ + \n :log warning (\$0 . \"Making directory 'scripts' failed: \" . \$\ + Err);\r\ + \n :return false;\r\ + \n }\r\ + \n# :return true;\r\ + \n# [ /file/get \$FileName ]\r\ + \n }\r\ + \n# :log error (\$0 . \"Directory tmpfs/scripts does not exsist. Remove\ + \_global variable tmpfs!\");\r\ + \n# :global tmpfs (a);\r\ + \n# return false;\r\ + \n }\r\ + \n}\r\ + \n :put \"Renaming AM-GlobalConfig, if exists...\";\r\ + \n :local ConfOldName (\"AM-GlobalConfig-\" . [ /system/clock/get date ] \ + . [ /system/clock/get time ] . \".rsc\");\r\ + \n /system/script/set name=\$ConfOldName [ find where name=\"AM-GlobalCon\ + fig\" ];\r\ + \n :foreach Script in={ \"AM-GlobalConfig\"; \"AM-GlobalFunc\" } do={\r\ + \n :put \"Installing \$Script...\";\r\ + \n /system/script/remove [ find where name=\$Script ];\r\ + \n /system/script/add name=\$Script owner=\$Script source=([ /tool/fetc\ + h check-certificate=yes-without-crl url=(\$GlobalScriptsUrl . \$Script . \ + \".rsc\") output=user as-value ]->\"data\");\r\ + \n \r\ + \n /system/script run \$Script;\r\ + \n :delay 2s;\r\ + \n };\r\ + \n\r\ + \n :local Script \"mod/AM-Certs-By-Ident\";\r\ + \n \r\ + \n :put \"Installing \$Script...\";\r\ + \n /system/script/remove [ find where name=\$Script ];\r\ + \n /system/script/add name=\$Script owner=\$Script source=([ /tool/fetch \ + check-certificate=yes-without-crl url=(\$GlobalScriptsUrl . \$Script . \".\ + rsc\") output=user as-value ]->\"data\");\r\ + \n /system/script run \$Script;\r\ + \n :global tmpAddCerts;\r\ + \n :while ([ \$tmpAddCerts ] = true) do={\r\ + \n :delay 1s;\r\ + \n }\r\ + \n :delay 1s;\r\ + \n :put \"Removing \$Script...\";\r\ + \n /system/script/remove [ find where name=\$Script ];\r\ + \n\r\ + \n :if ([ :len [ /certificate/find where fingerprint=\$CertFingerprint ] \ + ] > 0) do={\r\ + \n :put \"Renaming certificate by its common-name...\";\r\ + \n :global CertificateNameByCN;\r\ + \n \$CertificateNameByCN \$CertFingerprint;\r\ + \n };\r\ + \n\r\ + \n :if (\$RemoveMe = true) do={\r\ + \n :put \"Add Scheduler for remove this script...\";\r\ + \n :local OnEvent (\":delay 30s;\\n\" . \"/system/script/remove [ find \ + where name=\\\"\$SetupScript\\\" ];\\n\" . \\\r\ + \n \":delay 2s;\\n\" . \"/system/scheduler/remove [ find where name=\\\ + \"_RemoveSetup\\\" ];\\n\" . \\\r\ + \n \":delay 2s;\\n\" . \":global RemoveMe (a);\\n\");\r\ + \n :local Comment \"_RemoveSetup tmp scheduler for remove setup script.\ + \";\r\ + \n /system/scheduler/add name=\"_RemoveSetup\" comment=\$Comment start-\ + time=startup interval=30s on-event=\$OnEvent;\r\ + \n :log warning (\$0 . \"\\nI've done everything, and now I'm tired..\ + . I'm leaving.\\nThere will be an error below, this is normal.\");\r\ + \n }\r\ + \n :delay 1;\r\ + \n :put \"Loading configuration and functions...\";\r\ + \n :do { \r\ + \n :global ScriptInstallUpdate; \$ScriptInstallUpdate;\r\ + \n :put \"Normal removing temporary global environment variables.\";\ + \r\ + \n :global SetupScript (a);\r\ + \n :global InitSetup (a);\r\ + \n :global RemoveMe (a);\r\ + \n } on-error={\r\ + \n :local ErrorMsg \"Loading configuration and functions error!\";\r\ + \n :log error (\$0 . \$ErrorMsg . :error);\r\ + \n :error \$ErrorMsg;\r\ + \n }\r\ + \n}\r\ + \n" +add comment="Global Config Script" dont-require-permissions=no name=\ + AM-GlobalConfig owner=AM-GlobalConfig policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="#\ + !rsc by Vados\r\ + \n# RouterOS script: AM-GlobalConfig\r\ + \n# Script comment: Global Config Script\r\ + \n# Copyright (c) 2007-2026 Vados \r\ + \n#\r\ + \n#\r\ + \n# Warning: Dont touch this line!\r\ + \n:global Identity [/system/identity/get name];\r\ + \n:global GlobalConfReady false;\r\ + \n#\r\ + \n#\r\ + \n# Global Url For Amster scripts to fetch\r\ + \n# most be defined in AM-Init-Script or AM-setup scripts\r\ + \n:global GlobalScriptsUrl \"https://ros.vados.ru/\";\r\ + \n:global ScriptsUrlSuffix (a);\r\ + \n:global CheckSumsVerify true;\r\ + \n\r\ + \n:global ScriptUpdatesCRLF true;\r\ + \n:global CommentsInScripts true;\r\ + \n\r\ + \n# Debug output:\r\ + \n:global PrintDebug true; #false;\r\ + \n\r\ + \n# Debug output for specific script:\r\ + \n#:set (\$PrintDebugOverride->\"AM-GlobalFunc\") true;\r\ + \n\r\ + \n# Debug logs\r\ + \n# Other actions (disk, email, remote or support) can be used as well.\r\ + \n# I do not recommend using echo - use debug output instead (https://rsc.\ + eworm.de/main/DEBUG.html#debug-output).\r\ + \n#/system/logging/add topics=script,debug action=memory;\r\ + \n\r\ + \n:global MikrotikUpgradeUrl \"https://upgrade.mikrotik.com/routeros\";\r\ + \n:global Domain \"WORKGROUP\";\r\ + \n\r\ + \n:global EmailGeneralTo \"vados@vados.ru\";\r\ + \n#:global EmailGeneralCc \"\";\r\ + \n\r\ + \n# \$ScriptInstallUpdate mod/notification-telegram\r\ + \n:global TelegramTokenId \"8390868324:AAG3_d1hfVR2M6pGAgDK6ZvhOe4J97nyopw\ + \";\r\ + \n:global TelegramChatId \"-1003631309063\"; #\"454403445\";\r\ + \n# Use this to send notifications to a specific topic in group.\r\ + \n#:global TelegramThreadId \"30\";\r\ + \n# Using telegram-chat you have to define trusted chat ids (not group ids\ + !)\r\ + \n# or user names. Groups allow to chat with devices simultaneously.\r\ + \n:global TelegramChatIdsTrusted {\r\ + \n \"454403445\"\r\ + \n \"-1003631309063\"\r\ + \n \"-5124482807\"\r\ + \n \"1087968824\";\r\ + \n};\r\ + \n# \"-5294757340\";\r\ + \n\r\ + \n:global TelegramChatGroups \"(all)\";\r\ + \n#:global TelegramChatGroups \"(all|home|office)\";\r\ + \n\r\ + \n:global NotificationFunctions {\r\ + \n \"email\"\r\ + \n \"telegram\";\r\ + \n};\r\ + \n# Toggle this to disable symbols in notifications.\r\ + \n:global NotificationsWithSymbols true;\r\ + \n:global TerminalColorOutput true;\r\ + \n\r\ + \n:global DetectIpAddrDefault \"https://ipv4.mikrotik.ovh/\";\r\ + \n:global DetectIpAddrFallback \"https://api.ipify.org/\";\r\ + \n\r\ + \n## Update channel. Possible values: stable, long-term, testing, developm\ + ent\r\ + \n:global updateChannel \"stable\";\r\ + \n\r\ + \n:global CertRenewUrl (\$GlobalScriptsUrl . \"certs/\" . \$Identity . \"/\ + \");\r\ + \n:global CertRenewTime 2w;\r\ + \n:global CertRenewPass (a); #{\r\ + \n# \"\";\r\ + \n# \"v3ry-s3cr3t\";\r\ + \n# \"4n0th3r-s3cr3t\";\r\ + \n#};\r\ + \n:global CertWarnTime 1w;\r\ + \n:global CertIssuedExportPass (a); # {\r\ + \n# \"\";\r\ + \n# \"cert1-cn\"=\"v3ry-s3cr3t\";\r\ + \n# \"cert2-cn\"=\"4n0th3r-s3cr3t\";\r\ + \n#};\r\ + \n\r\ + \n# Script BackupAndUpdate mode, possible values: backup,\r\ + \n# updateOnce - Update once as \"osnotify\" and back \"osnotify\" mode (i\ + n terminal run :set OSUpdateMode updateOnce; \$OSUpdateMode;). \r\ + \n# osupdate - Update if available and creates backups before/after update\ + \_(ignores `forceBackup`)\r\ + \n# Set `forceBackup` to true to always create backups, even\ + \_without updates\r\ + \n# osnotify - Set `forceBackup` to always create backups on every run\r\ + \n:global DevInfoIncludeIP true;\r\ + \n:global BackupAndUpdateMode \"osnotify\"\r\ + \n\r\ + \n:global forceBackup true;\r\ + \n:global BackupPassword \"Peskar55\";\r\ + \n\r\ + \n# SFTP Backup Variables\r\ + \n:global BackupRandomDelay 0;\r\ + \n:global BackupUploadUrl \"sftp://amster.vados.ru/upload/\";\r\ + \n:global BackupUploadUser \$Identity;\r\ + \n:global BackupUploadPass \"root.mnuc.backuper\";\r\ + \n\r\ + \n:global BackupSendBinary true;\r\ + \n:global BackupSendExport true;\r\ + \n:global BackupSendGlobalConfig true;\r\ + \n:global BackupSendScripts true;\r\ + \n\r\ + \n# Remove local file after uploading if no errors\r\ + \n:global BackupRmLocal true;\r\ + \n\r\ + \n# Local Backups Root Directory \r\ + \n:global BackupLocalRoot \"tmpfs\";\r\ + \n\r\ + \n# Encrypt Backup\r\ + \n:global BackupEncrypt true;\r\ + \n\r\ + \n# Sensitive information in Backups\r\ + \n:global BackupSens true;\r\ + \n\r\ + \n# User Export\r\ + \n:global BackupUser true;\r\ + \n\r\ + \n# License Export (not for CHR, will silently skip)\r\ + \n:global BackupLicense false;\r\ + \n\r\ + \n# SSH Keys\r\ + \n:global BackupSshKeys true;\r\ + \n\r\ + \n# Certificate Export\r\ + \n:global BackupCerts true;\r\ + \n\r\ + \n# Certificate Password\r\ + \n:global BackupCertsPasswd \"\";\r\ + \n\r\ + \n# User-Manager Export\r\ + \n:global BackupUserMan false;\r\ + \n\r\ + \n# The Dude Export\r\ + \n:global BackupDude false;\r\ + \n\r\ + \n# User Files to export, comma-separated string or array of strings\r\ + \n# User Files are not removed on backup\r\ + \n# Any directory paths will be removed (/ -> _) on remote file\r\ + \n# Nonpresent files are silently skipped\r\ + \n:global BackupUserList \"autosupout.rif,autosupout.old.rif\";\r\ + \n\r\ + \n# load functions and any custom scripts\r\ + \n# [ /system/script/find where name=\"global-config-ov\ + erlay\" ], \\\r\ + \n# [ /system/script/find where name~\"^global-config-o\ + verlay\\\\.d/.\" ]\r\ + \n#\r\ + \n#:foreach Script in=([ /system/script/find where name=\"Amster-TGglobalV\ + ars\" ], [ /system/script/find where name=\"Amster-TGglobalFunc\" ]) do={\ + \r\ + \n# :onerror Err {\r\ + \n# /system/script/run \$Script;\r\ + \n# } do={\r\ + \n# :log error (\"Loading configuration from configs and functions\"\ + \_. [ /system/script/get \$Script name ] . \" failed: \" . \$Err);\r\ + \n# }\r\ + \n#}\r\ + \n#:log info \"\\ndevMode = \$devMode\";\r\ + \n#:log info \"\\ndevRunning = \$devRunning\";\r\ + \n:foreach Script in=[ /system/script/find where source~\"^#!rsc by Vados \ + GlobalRUN\\r\?\\n\" ] do={\r\ + \n :onerror Err {\r\ + \n /system/script/run \$Script;\r\ + \n } do={\r\ + \n :log error (\"Run script \" . [ /system/script/get \$Script name \ + ] . \" width marker \\\" GlobalRUN\\\" failed: \" . \$Err);\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n# signal we are ready\r\ + \n:set GlobalConfReady true;\r\ + \n:while (\$GlobalConfReady != true) do={:delay 500ms};\r\ + \n" +add comment="Global functions" dont-require-permissions=no name=AM-GlobalFunc \ + owner=AM-GlobalFunc policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="#\ + !rsc by Vados\r\ + \n# RouterOS script: AM-GglobalFunc\r\ + \n# Script comment: Global functions\r\ + \n# Copyright (c) 2005-2026 Vados \r\ + \n#\r\ + \n#\r\ + \n# requires RouterOS, version=7.19\r\ + \n# requires device-mode, fetch, scheduler\r\ + \n:local ExitOK false;\r\ + \n:local ScriptName [ :jobname ];\r\ + \n#:global checkRunJob \$SriptName;\r\ + \n#:global fixCRLF;\r\ + \n#:if (\$fixCRLF != false) do={\r\ + \n#:foreach fixScript in=([ /system/script/find where name~\"*\" ]) do={/s\ + ystem/script/set source=[ :tocrlf [ get \$fixScript source ] ] \$fixScript\ + }\r\ + \n#:if (\$fixCRLF != \"LF\") do={ /system/script/set source=[ :tocrlf [ g\ + et \$fixScript source ] ] \$fixScript};};\r\ + \n#}\r\ + \n:global GlobalFuncReady false;\r\ + \n\r\ + \n# Git commit id & info, expected configuration version\r\ + \n:global CommitId \"main/729/f42d43ae\";\r\ + \n:global CommitTAG \"unknown\";\r\ + \n:global CommitInfo \"Autocommit in repo: ros.vados.ru, generated by owne\ + r: root, in 00:05:03 01.07.2026.\";\r\ + \n\r\ + \n# The variables are received right here.\r\ + \n:global GlobalConfVersion 140;\r\ + \n\r\ + \n:global SumsDirAndFileName \"tmp/checksums.json\";\r\ + \n\r\ + \n:global Identity [/system/identity/get name];\r\ + \n:global IdentityShort [:pick \$Identity 0 18];\r\ + \n\r\ + \n:global devBoardName [/system/resource/get board-name];\r\ + \n:global devMode [/system/device-mode/get mode];\r\ + \n:global devCurArch [/system/resource/get architecture-name];\r\ + \n:global devCurVersion [/system/resource/get version];\r\ + \n\r\ + \n# Variables defined in AM-GlobalConfig.rsc\r\ + \n:global TerminalColorOutput;\r\ + \n:global CommentsInScripts;\r\ + \n:global DetectIpAddrDefault;\r\ + \n:global DetectIpAddrFallback;\r\ + \n:global updateChannel;\r\ + \n:global Domain;\r\ + \n:global EmailGeneralTo;\r\ + \n:global BackupAndUpdateMode;\r\ + \n\r\ + \n# Single functions, without nested functions.\r\ + \n:global MiniDateTimeStamp;\r\ + \n:global MicroDateTimeStamp;\r\ + \n\r\ + \n# Include globals \$PrintDebug and \$PrintDebugOverride\r\ + \n:global LogPrint;\r\ + \n\r\ + \n# DeviceInfo and Updates\r\ + \n:global IsCheckUpdates;\r\ + \n:global WaitCheckUpdates;\r\ + \n:global GetPubIp;\r\ + \n\r\ + \n:global GetArrDeviceInfo;\r\ + \n:global WaitArrDeviceInfo;\r\ + \n\r\ + \n:global devModel;\r\ + \n:global devRunningMode;\r\ + \n\r\ + \n:global devRbRevision;\r\ + \n:global devRbSerialNumber;\r\ + \n:global devRbVerAvail;\r\ + \n\r\ + \n:global runningChannelVersion;\r\ + \n:global runningChannel;\r\ + \n:global runningVersion;\r\ + \n\r\ + \n:global devBuildTime;\r\ + \n:global ROsVerAvail;\r\ + \n:global PkgUpdStatus;\r\ + \n:global currentUpdateChannel;\r\ + \n\r\ + \n:global ScriptInstallUpdate;\r\ + \n\r\ + \n:global DeviceInfo;\r\ + \n\r\ + \n:global OldMode;\r\ + \n\r\ + \n:global ParseKeyValueStore;\r\ + \n:global RandomDelay;\r\ + \n:global GetRandomNumber;\r\ + \n:global EscapeForRegEx;\r\ + \n:global CharacterMultiply;\r\ + \n:global CharacterReplace;\r\ + \n:global CleanFilePath;\r\ + \n:global CleanName;\r\ + \n\r\ + \n:global CommitBrief;\r\ + \n\r\ + \n:global EitherOr;\r\ + \n:global FormatLine;\r\ + \n:global GetRandom20CharAlNum;\r\ + \n:global GetRandom20CharHex;\r\ + \n:global GetRandomNumber;\r\ + \n:global Grep;\r\ + \n:global IfThenElse;\r\ + \n:global MAX;\r\ + \n:global MIN;\r\ + \n:global HumanReadableNum;\r\ + \n\r\ + \n:global IsFullyConnected;\r\ + \n:global IsDNSResolving;\r\ + \n:global IsTimeSync;\r\ + \n:global IsTimeSyncCached;\r\ + \n:global IsTimeSyncResetNtp;\r\ + \n\r\ + \n:global MkDir;\r\ + \n:global RmDir;\r\ + \n:global RmFile;\r\ + \n:global FileGet;\r\ + \n:global Unix2Dos;\r\ + \n:global UrlEncode;\r\ + \n:global WaitForFile;\r\ + \n:global ScriptLock;\r\ + \n:global SendTelegram;\r\ + \n:global NotificationFunctions;\r\ + \n:global SendNotification;\r\ + \n:global SendNotification2;\r\ + \n:global SymbolForNotification;\r\ + \n:global SymbolByUnicodeName;\r\ + \n:global ValidateSyntax;\r\ + \n:global CheckSumsVerify;\r\ + \n:global GlobalConfChanges;\r\ + \n:global GlobalConfMigration;\r\ + \n:global VersionToNum;\r\ + \n:global RequiredRouterOS;\r\ + \n:global ExitError;\r\ + \n:global ExitOnError;\r\ + \n\r\ + \n:global FetchHugeFiles;\r\ + \n:global FetchUserAgentStr;\r\ + \n:global FileExists;\r\ + \n:global CertificateAvailable;\r\ + \n:global CertificateDownload;\r\ + \n:global CertificateNameByCN;\r\ + \n:global CharacterMultiply;\r\ + \n\r\ + \n:global InspectVar;\r\ + \n:global InspectVarRet;\r\ + \n:global ParseCustom;\r\ + \n\r\ + \n:set IsCheckUpdates do={\r\ + \n :local isInfo [:tobool \$1];\r\ + \n :global updateChannel;\r\ + \n :global PkgUpdStatus;\r\ + \n :global currentUpdateChannel;\r\ + \n :global ROsVerAvail;\r\ + \n :global arrDeviceInfo;\r\ + \n :log info (\$0 . \" \\\$isInfo = \" . \$isInfo); \r\ + \n :local CurrentChannel [ /system/package/update/get channel ];\r\ + \n :if ([ :len \$updateChannel ] > 0 && \$CurrentChannel != \$updateChann\ + el) do={\r\ + \n :retry command={/system/package/update set channel=\$updateChannel} \ + \_delay=300ms max=5 on-error={};\r\ + \n :log info (\$0 . \" Set updates channel to: \$updateChannel\");\r\ + \n }\r\ + \n :local Update [ /system/package/update/get ];\r\ + \n :local Channel [ :tostr (\$Update->\"channel\") ];\r\ + \n :set currentUpdateChannel \$Channel;\r\ + \n :log info (\$0 . \" Update Channel: \" . \$Channel . \".\");\r\ + \n :local StatusPkg \"undefined\";\r\ + \n :log info (\$0 . \" Checking for updates...\");\r\ + \n :retry command={:set \$StatusPkg ([/system/package/update/check-for-up\ + dates without-paging as-value]->\"status\")} \\\r\ + \n delay=300ms max=5 on-error={};\r\ + \n :log info (\$0 . \" \\\$StatusPkg = \" . \$StatusPkg);\r\ + \n :if (\$StatusPkg = \"New version is available\") do={ :set PkgUpdStatu\ + s true; } else={ :set PkgUpdStatus false; }\r\ + \n :set ROsVerAvail [ :tostr [ /system/package/update/get latest-version \ + ] ];\r\ + \n :log info (\$0 . \" \\\$PkgUpdStatus = \" . \$PkgUpdStatus);\r\ + \n :if (\$isInfo != false || \$PkgUpdStatus = true) do={\r\ + \n :log info (\$0 . \" \\\$PkgUpdStatus = \" . \$PkgUpdStatus);\r\ + \n :if ([ :typeof \$arrDeviceInfo ] != \"array\") do={\r\ + \n :set arrDeviceInfo [:toarray \$arrDeviceInfo];\r\ + \n }\r\ + \n :retry command={:set (\$arrDeviceInfo->\"devUpdates\") [ /system/pac\ + kage/update/get ]} delay=500ms max=5 on-error={:log error (\"Error\")};\r\ + \n :return true;\r\ + \n } else={\r\ + \n :return \$PkgUpdStatus;\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n:set WaitCheckUpdates do={\r\ + \n :global IsCheckUpdates;\r\ + \n :global PkgUpdStatus;\r\ + \n :global currentUpdateChannel;\r\ + \n :global ROsVerAvail;\r\ + \n :global arrDeviceInfo;\r\ + \n :local Status \"\";\r\ + \n :local Update;\r\ + \n :local WaitCount 0;\r\ + \n :if ([ \$IsCheckUpdates true ] = false) do={\r\ + \n :return false;\r\ + \n }\r\ + \n :set Status [ :tostr [ /system/package/update/get status ] ];\r\ + \n :while ((\$Status = \"\" || \$Status = \"finding out latest version...\ + \") && \$WaitCount < 60) do={\r\ + \n :delay 1s;\r\ + \n :set WaitCount (\$WaitCount + 1);\r\ + \n :set Status [ :tostr [ /system/package/update/get status ] ];\r\ + \n }\r\ + \n :set Update [ /system/package/update/get ];\r\ + \n :set currentUpdateChannel [ :tostr (\$Update->\"channel\") ];\r\ + \n :set ROsVerAvail [ :tostr (\$Update->\"latest-version\") ];\r\ + \n :if ((\$Update->\"status\") = \"New version is available\") do={\r\ + \n :set PkgUpdStatus true;\r\ + \n } else={\r\ + \n :set PkgUpdStatus false;\r\ + \n }\r\ + \n :if ([ :typeof \$arrDeviceInfo ] != \"array\") do={\r\ + \n :set arrDeviceInfo [:toarray \$arrDeviceInfo];\r\ + \n }\r\ + \n :set (\$arrDeviceInfo->\"devUpdates\") \$Update;\r\ + \n :if (\$Status = \"\" || \$Status = \"finding out latest version...\") \ + do={\r\ + \n :return false;\r\ + \n }\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set WaitArrDeviceInfo do={\r\ + \n :global GetArrDeviceInfo;\r\ + \n :while ([ \$GetArrDeviceInfo ] = false) do={\r\ + \n :delay 1s;\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n:set GetArrDeviceInfo do={\r\ + \n :global LogPrint;\r\ + \n :global arrDeviceInfo;\r\ + \n :global devBoardName;\r\ + \n :global devModel;\r\ + \n :global devRunning;\r\ + \n :global devCurArch;\r\ + \n :global devRbRevision;\r\ + \n :global devRbSerialNumber;\r\ + \n :global devBuildTime;\r\ + \n :global runningChannelVersion;\r\ + \n :global runningChannel;\r\ + \n :global runningVersion;\r\ + \n :global currentUpdateChannel;\r\ + \n :global ROsVerAvail;\r\ + \n\r\ + \n :local DeviceInfo;\r\ + \n :local Resource [/system/resource/get];\r\ + \n :local Update [ /system/package/update/get ];\r\ + \n :local VersionString [ :tostr (\$Resource->\"version\") ];\r\ + \n :local ChannelStart [ :find \$VersionString \" (\" ];\r\ + \n :local RouterBoard;\r\ + \n :local IsRouterBoard false;\r\ + \n :local RunningMode \"\";\r\ + \n\r\ + \n :set devCurArch [ :tostr (\$Resource->\"architecture-name\") ];\r\ + \n :set devBuildTime [ :tostr (\$Resource->\"build-time\") ];\r\ + \n :set runningChannelVersion \$VersionString;\r\ + \n :set runningVersion \$VersionString;\r\ + \n :set runningChannel \"\";\r\ + \n\r\ + \n :if ([ :typeof \$ChannelStart ] = \"num\" && \$ChannelStart >= 0) do=\ + {\r\ + \n :set runningVersion [ :pick \$VersionString 0 \$ChannelStart ];\r\ + \n :local ChannelRest [ :pick \$VersionString (\$ChannelStart + 2) [ :\ + len \$VersionString ] ];\r\ + \n :local ChannelEnd [ :find \$ChannelRest \")\" ];\r\ + \n :if ([ :typeof \$ChannelEnd ] = \"num\" && \$ChannelEnd >= 0) do={\ + \r\ + \n :set runningChannel [ :pick \$ChannelRest 0 \$ChannelEnd ];\r\ + \n }\r\ + \n }\r\ + \n\r\ + \n :if ([:pick \$devBoardName 0 3] != \"CHR\" or [:pick \$devBoardName 0\ + \_3] != \"x86\") do={\r\ + \n :do {\r\ + \n :set RouterBoard [[ :parse \"/system/routerboard/get\" ]];\r\ + \n# :set RouterBoard [ /system/routerboard/get ];\r\ + \n :if ((\$RouterBoard->\"routerboard\") = true) do={\r\ + \n :set IsRouterBoard true;\r\ + \n }\r\ + \n } on-error={}\r\ + \n }\r\ + \n\r\ + \n :do {\r\ + \n :set RunningMode [ /system/device-mode/get mode ];\r\ + \n } on-error={\r\ + \n :do {\r\ + \n :set RunningMode [ /system/device-mode/get running ];\r\ + \n } on-error={\r\ + \n :set RunningMode \"\";\r\ + \n }\r\ + \n }\r\ + \n :set devRunning \$RunningMode;\r\ + \n\r\ + \n :if (\$IsRouterBoard = true) do={\r\ + \n :set devModel [ :tostr (\$RouterBoard->\"model\") ];\r\ + \n :set devRbRevision [ :tostr (\$RouterBoard->\"revision\") ];\r\ + \n :set devRbSerialNumber [ :tostr (\$RouterBoard->\"serial-number\") \ + ];\r\ + \n } else={\r\ + \n :set devModel \$devBoardName;\r\ + \n :set devRbRevision \"\";\r\ + \n :set devRbSerialNumber \"\";\r\ + \n }\r\ + \n\r\ + \n :set currentUpdateChannel [ :tostr (\$Update->\"channel\") ];\r\ + \n :set ROsVerAvail [ :tostr (\$Update->\"latest-version\") ];\r\ + \n\r\ + \n :set (\$DeviceInfo->\"board-name\") \$devBoardName;\r\ + \n :set (\$DeviceInfo->\"architecture-name\") \$devCurArch;\r\ + \n :set (\$DeviceInfo->\"model\") \$devModel;\r\ + \n :set (\$DeviceInfo->\"revision\") \$devRbRevision;\r\ + \n :set (\$DeviceInfo->\"serial-number\") \$devRbSerialNumber;\r\ + \n :set (\$DeviceInfo->\"running-mode\") \$devRunning;\r\ + \n :set (\$DeviceInfo->\"build-time\") \$devBuildTime;\r\ + \n :set (\$DeviceInfo->\"running-channel-version\") \$runningChannelVers\ + ion;\r\ + \n :set (\$DeviceInfo->\"running-version\") \$runningVersion;\r\ + \n :set (\$DeviceInfo->\"running-channel\") \$runningChannel;\r\ + \n :set (\$DeviceInfo->\"routerboard\") \$IsRouterBoard;\r\ + \n :set (\$DeviceInfo->\"current-firmware\") [ :tostr (\$RouterBoard->\"\ + current-firmware\") ];\r\ + \n :set (\$DeviceInfo->\"upgrade-firmware\") [ :tostr (\$RouterBoard->\"\ + upgrade-firmware\") ];\r\ + \n :set (\$DeviceInfo->\"package-channel\") \$currentUpdateChannel;\r\ + \n :set (\$DeviceInfo->\"installed-version\") [ :tostr (\$Update->\"inst\ + alled-version\") ];\r\ + \n :set (\$DeviceInfo->\"latest-version\") \$ROsVerAvail;\r\ + \n :set (\$DeviceInfo->\"update-status\") [ :tostr (\$Update->\"status\"\ + ) ];\r\ + \n\r\ + \n :set arrDeviceInfo \$DeviceInfo;\r\ + \n :return \$DeviceInfo;\r\ + \n}\r\ + \n\r\ + \n:set DeviceInfo do={\r\ + \n :global GetArrDeviceInfo;\r\ + \n :global GlobalConfVersion;\r\ + \n :global Identity;\r\ + \n :global IfThenElse;\r\ + \n :global FormatLine;\r\ + \n :global CommitBrief;\r\ + \n\r\ + \n :local License [/system/license/get];\r\ + \n :local Snmp [ /snmp/get ];\r\ + \n :local Info [ \$GetArrDeviceInfo ];\r\ + \n\r\ + \n :if ([ :typeof \$Info ] != \"array\") do={\r\ + \n :return false;\r\ + \n }\r\ + \n\r\ + \n :return ( \\\r\ + \n [ \$FormatLine \"Hostname\" \$Identity ] . \"\\n\" . \\\r\ + \n [ \$IfThenElse ([ :len (\$Snmp->\"location\") ] > 0) \\\r\ + \n ([ \$FormatLine \"Location\" (\$Snmp->\"location\") ] . \"\\n\") ]\ + \_. \\\r\ + \n [ \$IfThenElse ([ :len (\$Snmp->\"contact\") ] > 0) \\\r\ + \n ([ \$FormatLine \"Contact\" (\$Snmp->\"contact\") ] . \"\\n\") ] .\ + \_\\\r\ + \n \"Hardware:\\n\" . \\\r\ + \n [ \$FormatLine \" Board\" (\$Info->\"board-name\") ] . \"\\n\" . \ + \\\r\ + \n [ \$FormatLine \" Arch\" (\$Info->\"architecture-name\") ] . \"\\\ + n\" . \\\r\ + \n [ \$FormatLine \" Model\" (\$Info->\"model\") ] . \\\r\ + \n [ \$IfThenElse ([ :len (\$Info->\"revision\") ] > 0) \\\r\ + \n (\" \" . (\$Info->\"revision\")) ] . \"\\n\" . \\\r\ + \n [ \$IfThenElse ([ :len (\$Info->\"serial-number\") ] > 0) \\\r\ + \n ([ \$FormatLine \" Serial\" (\$Info->\"serial-number\") ] . \"\ + \\n\") ] . \\\r\ + \n [ \$IfThenElse ([ :len (\$License->\"nlevel\") ] > 0) \\\r\ + \n ([ \$FormatLine \" License\" (\"level \" . (\$License->\"nlevel\ + \")) ] . \"\\n\") ] . \\\r\ + \n \"RouterOS:\\n\" . \\\r\ + \n [ \$IfThenElse ([ :len (\$License->\"level\") ] > 0) \\\r\ + \n ([ \$FormatLine \" License\" (\"level \" . (\$License->\"level\ + \")) ] . \"\\n\") ] . \\\r\ + \n [ \$FormatLine \" Channel\" (\$Info->\"package-channel\") ] . \"\ + \\n\" . \\\r\ + \n [ \$FormatLine \" Installed\" (\$Info->\"installed-version\") ] .\ + \_\"\\n\" . \\\r\ + \n [ \$IfThenElse ([ :len (\$Info->\"running-channel\") ] > 0) \\\r\ + \n ([ \$FormatLine \" Running\" ((\$Info->\"running-version\") . \ + \" (\" . (\$Info->\"running-channel\") . \")\") ] . \"\\n\") \\\r\ + \n ([ \$FormatLine \" Running\" (\$Info->\"running-version\") ] . \ + \"\\n\") ] . \\\r\ + \n [ \$IfThenElse ([ :len (\$Info->\"latest-version\") ] > 0 && \\\r\ + \n (\$Info->\"installed-version\") != (\$Info->\"latest-version\"))\ + \_\\\r\ + \n ([ \$FormatLine \" Available\" (\$Info->\"latest-version\") ] .\ + \_\"\\n\") ] . \\\r\ + \n [ \$IfThenElse ((\$Info->\"routerboard\") = true && \\\r\ + \n (\$Info->\"current-firmware\") != (\$Info->\"upgrade-firmware\")\ + ) \\\r\ + \n ([ \$FormatLine \" Firmware\" ((\$Info->\"current-firmware\") .\ + \_\" -> \" . (\$Info->\"upgrade-firmware\")) ] . \"\\n\") ] . \\\r\ + \n \"RouterOS-Scripts:\\n\" . \\\r\ + \n [ \$FormatLine \" Commit\" [ \$CommitBrief ] ] . \"\\n\" . \\\r\ + \n [ \$FormatLine \" Version\" \$GlobalConfVersion ]);\r\ + \n}\r\ + \n\r\ + \n:if ([ :typeof \$NotificationFunctions ] != \"array\") do={:set Notifica\ + tionFunctions ({})}\r\ + \n\r\ + \n:set GetRandom20CharAlNum do={\r\ + \n :global EitherOr;\r\ + \n :return [ :rndstr length=[ \$EitherOr [ :tonum \$1 ] 20 ] from=\"ABCDE\ + FGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789\" ];\r\ + \n}\r\ + \n\r\ + \n:set GetRandom20CharHex do={:global EitherOr; :return [ :rndstr length=[\ + \_\$EitherOr [ :tonum \$1 ] 20 ] from=\"0123456789abcdef\" ]; }\r\ + \n:set GetRandomNumber do={:global EitherOr; :return [ :rndnum from=0 to=[\ + \_\$EitherOr [ :tonum \$1 ] 4294967295 ] ]; }\r\ + \n\r\ + \n:global checkRunJob;\r\ + \n:set checkRunJob do={\r\ + \n :local Script [ :tostr \$1 ];\r\ + \n :global ScriptLock; \r\ + \n :if ([ \$ScriptLock \$Script ] = false) do={:return false}\r\ + \n :foreach Job in=[ /system/script/job/find where script=\$Script ] do={\ + :set Job [ /system/script/job/get \$Job ];\r\ + \n :while ([ :typeof (\$Job->\"parent\") ] = \"id\") do={:set Job [ /sy\ + stem/script/job/get [ find where .id=(\$Job->\"parent\") ] ]}\r\ + \n :if ((\$Job->\"type\") = \"login\") do={:return true};}; :return fa\ + lse; \r\ + \n}\r\ + \n\r\ + \n:set InspectVar do={\r\ + \n :global InspectVarRet;\r\ + \n :put [ :tocrlf [ \$InspectVarRet \$1 ] ];\r\ + \n}\r\ + \n\r\ + \n:set CommitBrief do={\r\ + \n :global CommitId;\r\ + \n :global CommitInfo;\r\ + \n :if (\$CommitId = \"unknown\") do={\r\ + \n :return \"unknown\";\r\ + \n }\r\ + \n :return (\$CommitInfo . \"/\" . [ :pick \$CommitId 0 8 ]);\r\ + \n}\r\ + \n\r\ + \n:set InspectVarRet do={\r\ + \n :local Input \$1;\r\ + \n :local Level (0 + [ :tonum \$2 ]);\r\ + \n :global CharacterReplace;\r\ + \n :global IfThenElse;\r\ + \n :global InspectVarRet;\r\ + \n :local IndentReturn do={\r\ + \n :local Prefix [ :tostr \$1 ];\r\ + \n :local Value [ :tostr \$2 ];\r\ + \n :local Level [ :tonum \$3 ];\r\ + \n :global CharacterMultiply;\r\ + \n :return ([ \$CharacterMultiply \" \" (\$Level * 2) ] . \"-\" . \$Pre\ + fix . \"-> \" . \$Value);\r\ + \n }\r\ + \n :local TypeOf [ :typeof \$Input ];\r\ + \n :local Len [ :len \$Input ];\r\ + \n :local Return [ \$IndentReturn \"type\" \$TypeOf \$Level ];\r\ + \n :if (\$TypeOf = \"array\") do={\r\ + \n :foreach Key,Value in=\$Input do={\r\ + \n :set \$Return (\$Return . \"\\n\" . \\\r\ + \n [ \$IndentReturn \"key\" \$Key (\$Level + 1) ] . \"\\n\" . \\\r\ + \n [ \$InspectVarRet \$Value (\$Level + 2) ]);\r\ + \n }\r\ + \n } else={\r\ + \n :if (\$TypeOf = \"str\") do={\r\ + \n :set \$Return (\$Return . \"\\n\" . \\\r\ + \n [ \$IndentReturn \"len\" \$Len \$Level ]);\r\ + \n :if ([ :typeof [ :find \$Input (\"\\r\") ] ] = \"num\") do={\r\ + \n :set Input [ \$CharacterReplace \$Input (\"\\r\") \"\" ];\r\ + \n }\r\ + \n :if ([ :typeof [ :find \$Input (\"\\n\") ] ] = \"num\") do={\r\ + \n :set Input [ \$CharacterReplace \$Input (\"\\n\") \" \" ];\r\ + \n }\r\ + \n }\r\ + \n :if (\$TypeOf != \"nothing\") do={\r\ + \n :set \$Return (\$Return . \"\\n\" . \\\r\ + \n [ \$IndentReturn \"value\" [ \$IfThenElse ([ :len \$Input ] > 80)\ + \_\\\r\ + \n ([ :pick \$Input 0 77 ] . \"...\") \$Input ] \$Level ]);\r\ + \n }\r\ + \n }\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set GetPubIp do={\r\ + \n :global DetectIpAddrDefault;\r\ + \n :global DetectIpAddrFallback;\r\ + \n\r\ + \n :local PubIpAddr false;\r\ + \n :local Url \$DetectIpAddrDefault;\r\ + \n :local JobName [ :jobname ];\r\ + \n\r\ + \n :do {\r\ + \n :log debug (\$0 . \" Trying to detect public IP using default serv\ + ice: \" . \$Url . \"...\");\r\ + \n :retry command={:set PubIpAddr ([ /tool/fetch http-method=\"get\"\ + \_check-certificate=yes-without-crl \$Url output=user as-value ]->\"data\"\ + )} \\\r\ + \n delay=50ms max=5 on-error={:log error (\$JobName . \" Failed to \ + detect public IP using default service: \" . \$Url . \".\")}\r\ + \n } on-error={\r\ + \n :log error (\$0 . \" Failed to detect public IP using default serv\ + ice: \" . \$Url . \".\");\r\ + \n :set Url \$DetectIpAddrFallBack;\r\ + \n :log debug (\$0 . \" Trying to detect public IP using fallback ser\ + vice: \" . \$Url . \"...\");\r\ + \n :retry command={:set PubIpAddr ([ /tool/fetch http-method=\"get\"\ + \_check-certificate=yes-without-crl \$Url output=user as-value ]->\"data\"\ + )} \\\r\ + \n delay=200ms max=5 on-error={:log error (\$JobName . \" Failed to\ + \_detect public IP using fallback service: \" . \$Url . \".\")}\r\ + \n };\r\ + \n :local IpLen ([ :len \$PubIpAddr ] -1);\r\ + \n :if (\$PubIpAddr = false) do={:set PubIpAddr \"not-detected\"} else=\ + {:set PubIpAddr [:pick \$PubIpAddr 0 \$IpLen]}\r\ + \n :return \$PubIpAddr;\r\ + \n}\r\ + \n\r\ + \n:set FormatLine do={\r\ + \n :local Key [ :tostr \$1 ];\r\ + \n :local Value [ :tostr \$2 ];\r\ + \n :local Indent [ :tonum \$3 ];\r\ + \n :local Spaces;\r\ + \n :local Return \"\";\r\ + \n :global CharacterMultiply;\r\ + \n :global EitherOr;\r\ + \n :set Indent [ \$EitherOr \$Indent 16 ];\r\ + \n :local Spaces [ \$CharacterMultiply \" \" \$Indent ];\r\ + \n :if ([ :len \$Key ] > 0) do={ :set Return (\$Key . \":\")}\r\ + \n :if ([ :len \$Key ] > (\$Indent - 2)) do={:set Return (\$Return . \"\\\ + n\" . [ :pick \$Spaces 0 \$Indent ] . \$Value);\r\ + \n } else={:set Return (\$Return . [ :pick \$Spaces 0 (\$Indent - [ :len \ + \$Return ]) ] . \$Value)}\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set ParseCustom do={\r\ + \n :local Str [ :tostr \$1 ];\r\ + \n :local Start [:find [ \$Str ] [:tostr \$2]];\r\ + \n :local End [ :tostr \$3 ];\r\ + \n :local Num [ :tonum \$4 ];\r\ + \n :local Return;\r\ + \n :if ([:len \$Num] = 0) do={:set Num \"0\"}\r\ + \n :local rest [:pick \$Str (\$Start+\$Num) [:len \$Str]]; \r\ + \n :if ([:len \$End ] = 0) do={:set Return [:pick \$Str \$Num \$Start]} e\ + lse={\r\ + \n :set End [:find \$rest \$End];\r\ + \n :set Return [:pick \$rest 0 \$End];\r\ + \n }\r\ + \n:return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set LogPrint do={\r\ + \n :local Severity [ :tostr \$1 ];\r\ + \n :local Name [ :tostr \$2 ];\r\ + \n :local Message [ :tostr \$3 ];\r\ + \n :global PrintDebug;\r\ + \n :global PrintDebugOverride;\r\ + \n :global EitherOr;\r\ + \n :local Debug [ \$EitherOr (\$PrintDebugOverride->\$Name) \$PrintDebug \ + ];\r\ + \n :local PrintSeverity do={\r\ + \n :global TerminalColorOutput;\r\ + \n :if (\$TerminalColorOutput != true) do={:return \$1}\r\ + \n :local Color { debug=96; info=97; warning=93; error=91 };\r\ + \n :return (\"\\1B[\" . \$Color->\$1 . \"m\" . \$1 . \"\\1B[0m\");\r\ + \n}\r\ + \n :local Log ([ \$EitherOr \$Name \"\" ] . \": \" . \$Message);\ + \r\ + \n :if (\$Severity ~ (\"^(custom|debug|error|info)\\\$\")) do={\r\ + \n :if (\$Severity = \"debug\") do={:log debug \$Log}\r\ + \n :if (\$Severity = \"error\") do={:log error \$Log}\r\ + \n :if (\$Severity = \"info\" ) do={:log info \$Log}\r\ + \n } else={:log warning \$Log; :set Severity \"warning\"}\r\ + \n :if (\$Severity != \"debug\" || \$Debug = true) do={:put ([ \$PrintSev\ + erity \$Severity ] . \": \" . \$Message)}\r\ + \n}\r\ + \n\r\ + \n:set CertificateAvailable do={\r\ + \n :local CommonName [ :tostr \$1 ];\r\ + \n :local UseFor [ :tostr \$2 ];\r\ + \n\r\ + \n :global CertificateDownload;\r\ + \n :global ParseKeyValueStore;\r\ + \n\r\ + \n :if ([ :len \$UseFor ] = 0) do={\r\ + \n :log warning (\$0 . \"The intended use is undefined!\");\r\ + \n :set UseFor \"undefined\";\r\ + \n }\r\ + \n :if ([ /system/resource/get free-hdd-space ] < 8388608 && \\\r\ + \n [ /certificate/settings/get crl-download ] = true && \\\r\ + \n [ /certificate/settings/get crl-store ] = \"system\") do={\r\ + \n :log warning (\$0 . \"This system has low free flash space but \" . \ + \\\r\ + \n \"is configured to download certificate CRLs to system!\");\r\ + \n }\r\ + \n :if ([ :len \$CommonName ] = 0) do={\r\ + \n :log warning (\$0 . \"No CommonName given!\");\r\ + \n :return false;\r\ + \n }\r\ + \n :local CertSettings [ /certificate/settings/get ];\r\ + \n :if (((\$CertSettings->\"builtin-trust-anchors\") = \"trusted\" || \\\ + \r\ + \n (\$CertSettings->\"builtin-trust-store\") ~ \$UseFor || \\\r\ + \n (\$CertSettings->\"builtin-trust-store\") = \"all\") && \\\r\ + \n [ :len [ /certificate/builtin/find where common-name=\$CommonName\ + \_] ] > 0) do={\r\ + \n :return true;\r\ + \n }\r\ + \n :if ([ :len [ /certificate/find where common-name=\$CommonName ] ] = 0\ + ) do={\r\ + \n :log info (\$0 . \"Certificate with CommonName '\" . \$CommonName . \ + \"' not available.\");\r\ + \n :if ([ \$CertificateDownload \$CommonName ] = false) do={\r\ + \n :return false;\r\ + \n }\r\ + \n }\r\ + \n :if ([ :len [ /certificate/find where common-name=\$CommonName ] ] > 1\ + ) do={\r\ + \n :log info (\$0 . \"There are \" . \$CertCount . \" Certificates with\ + \_CommonName '\" . \$CommonName . \"'. Should be ok.\");\r\ + \n :return true;\r\ + \n }\r\ + \n :local CertVal [ /certificate/get [ find where common-name=\$CommonNam\ + e ] ];\r\ + \n :while ((\$CertVal->\"akid\") != \"\" && (\$CertVal->\"akid\") != (\$C\ + ertVal->\"skid\")) do={\r\ + \n :if ([ :len [ /certificate/find where skid=(\$CertVal->\"akid\") ] ]\ + \_= 0) do={\r\ + \n :local IssuerCN ([ \$ParseKeyValueStore (\$CertVal->\"issuer\") ]-\ + >\"CN\");\r\ + \n :log info (\$0 . \"Certificate chain for '\" . \$CommonName . \"' \ + is incomplete, missing '\" . \$IssuerCN . \"'.\");\r\ + \n :if ([ \$CertificateDownload \$IssuerCN ] = false) do={\r\ + \n :return false;\r\ + \n }\r\ + \n }\r\ + \n :set CertVal [ /certificate/get [ find where skid=(\$CertVal->\"akid\ + \") ] ];\r\ + \n }\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set CertificateDownload do={\r\ + \n :local CommonName [ :tostr \$1 ];\r\ + \n :global ScriptUpdatesBaseUrl;\r\ + \n :global ScriptUpdatesUrlSuffix;\r\ + \n :global CertificateNameByCN;\r\ + \n :global CleanName;\r\ + \n :global FetchUserAgentStr;\r\ + \n :global RmFile;\r\ + \n :global WaitForFile;\r\ + \n\r\ + \n :log info (\$0 . \"Downloading and importing certificate with \" . \\\ + \r\ + \n \"CommonName '\" . \$CommonName . \"'.\");\r\ + \n :local FileName ([ \$CleanName \$CommonName ] . \".pem\");\r\ + \n :do {\r\ + \n /tool/fetch check-certificate=yes-without-crl mode=https http-header\ + -field=({ [ \$FetchUserAgentStr \$0 ] }) \\\r\ + \n (\$ScriptUpdatesBaseUrl . \"certs/\" . \$FileName . \$ScriptUpdate\ + sUrlSuffix) \\\r\ + \n dst-path=\$FileName as-value;\r\ + \n \$WaitForFile \$FileName;\r\ + \n } on-error={\r\ + \n :log warning (\$0 . \" Failed downloading certificate with CommonNam\ + e '\" . \$CommonName . \\\r\ + \n \"' from repository! Trying fallback to mkcert.org...\");\r\ + \n :do {\r\ + \n :local CertSettings [ /certificate/settings/get ];\r\ + \n :if ([ :len [ /certificate/find where common-name=\"ISRG Root X1\"\ + \_] ] = 0 && \\\r\ + \n !(((\$CertSettings->\"builtin-trust-anchors\") = \"trusted\" \ + || \\\r\ + \n (\$CertSettings->\"builtin-trust-store\") ~ \"fetch\" || \ + \\\r\ + \n (\$CertSettings->\"builtin-trust-store\") = \"all\") && \\\ + \r\ + \n [ :len [ /certificate/builtin/find where common-name=\"ISRG\ + \_Root X1\" ] ] > 0)) do={\r\ + \n :log error (\$0 . \" Required certificate is not available.\");\ + \r\ + \n :return false;\r\ + \n }\r\ + \n /tool/fetch check-certificate=yes-without-crl mode=https http-head\ + er-field=({ [ \$FetchUserAgentStr \$0 ] }) \\\r\ + \n url=\"https://mkcert.org/generate/\" http-data=[ :serialize to=jso\ + n ({ \$CommonName }) ] \\\r\ + \n dst-path=\$FileName as-value;\r\ + \n \$WaitForFile \$FileName;\r\ + \n :if ([ /file/get \$FileName size ] = 0) do={\r\ + \n \$RmFile \$FileName;\r\ + \n :error false;\r\ + \n }\r\ + \n } on-error={\r\ + \n :log error (\$0 . \" Failed downloading certificate with CommonNam\ + e '\" . \$CommonName . \"'!\");\r\ + \n :return false;\r\ + \n }\r\ + \n }\r\ + \n /certificate/import file-name=\$FileName passphrase=\"\" as-value;\r\ + \n :delay 1s;\r\ + \n \$RmFile \$FileName;\r\ + \n :if ([ :len [ /certificate/find where common-name=\$CommonName ] ] = 0\ + ) do={\r\ + \n /certificate/remove [ find where name~(\"^\" . \$FileName . \"_[0-9]\ + +\\\$\") ];\r\ + \n :log warning (\$0 . \" Certificate with CommonName '\" . \$CommonNam\ + e . \"' still unavailable!\");\r\ + \n :return false;\r\ + \n }\r\ + \n :foreach Cert in=[ /certificate/find where name~(\"^\" . \$FileName . \ + \"_[0-9]+\\\$\") ] do={\r\ + \n \$CertificateNameByCN [ /certificate/get \$Cert common-name ];\r\ + \n }\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set CertificateNameByCN do={\r\ + \n :local Match [ :tostr \$1 ];\r\ + \n :global CleanName;\r\ + \n :local Cert ([ /certificate/find where (common-name=\$Match or fingerp\ + rint=\$Match or name=\$Match) ]->0);\r\ + \n :if ([ :len \$Cert ] = 0) do={\r\ + \n :log warning (\$0 .\" No matching certificate found.\");\r\ + \n :return false;\r\ + \n }\r\ + \n :local CommonName [ /certificate/get \$Cert common-name ];\r\ + \n /certificate/set \$Cert name=[ \$CleanName \$CommonName ];\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set CharacterMultiply do={\r\ + \n :local Str [ :tostr \$1 ];\r\ + \n :local Num [ :tonum \$2 ];\r\ + \n :if (\$Num = 0) do={:return \"\"}\r\ + \n :local Return \"\";\r\ + \n :for I from=1 to=\$Num do={:set Return (\$Return . \$Str)}\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set FileExists do={\r\ + \n :local FileName [ :tostr \$1 ];\r\ + \n :local Type [ :tostr \$2 ];\r\ + \n :global FileGet;\r\ + \n :local FileVal [ \$FileGet \$FileName ];\r\ + \n :if (\$FileVal = false) do={ :return false; }\r\ + \n :if ([ :len (\$FileVal->\"size\") ] = 0) do={ :return false; }\r\ + \n :if ([ :len \$Type ] = 0 || \$FileVal->\"type\" = \$Type) do={ :return\ + \_true; }\r\ + \n :return false;\r\ + \n}\r\ + \n\r\ + \n:set Grep do={\r\ + \n :local Input ([ :tostr \$1 ] . \"\\n\");\r\ + \n :local Pattern [ :tostr \$2 ];\r\ + \n :if ([ :typeof [ :find \$Input \$Pattern ] ] = \"nil\") do={:return []\ + }\r\ + \n :do {\r\ + \n :local Line [ :pick \$Input 0 [ :find \$Input \"\\n\" ] ];\r\ + \n :if ([ :typeof [ :find \$Line \$Pattern ] ] = \"num\") do={:return \ + \$Line}\r\ + \n :set Input [ :pick \$Input ([ :find \$Input \"\\n\" ] + 1) [ :len \$\ + Input ] ];\r\ + \n } while=([ :len \$Input ] > 0);\r\ + \n :return [];\r\ + \n}\r\ + \n\r\ + \n:set HumanReadableNum do={\r\ + \n :local Input [ :tonum \$1 ];\r\ + \n :local Base [ :tonum \$2 ];\r\ + \n :global EitherOr;\r\ + \n :global IfThenElse;\r\ + \n :local Prefix \"kMGTPE\";\r\ + \n :local Pow 1;\r\ + \n :set Base [ \$EitherOr \$Base 1024 ];\r\ + \n :local Bin [ \$IfThenElse (\$Base = 1024) \"i\" \"\" ];\r\ + \n :if (\$Input < \$Base) do={:return \$Input}\r\ + \n :for I from=0 to=[ :len \$Prefix ] do={\r\ + \n :set Pow (\$Pow * \$Base);\r\ + \n :if (\$Input / \$Base < \$Pow) do={\r\ + \n :set Prefix [ :pick \$Prefix \$I ];\r\ + \n :local Tmp1 (\$Input * 100 / \$Pow);\r\ + \n :local Tmp2 (\$Tmp1 / 100);\r\ + \n :if (\$Tmp2 >= 100) do={:return (\$Tmp2 . \$Prefix . \$Bin)}\r\ + \n :return (\$Tmp2 . \".\" . \\\r\ + \n [ :pick \$Tmp1 [ :len \$Tmp2 ] ([ :len \$Tmp1 ] - [ :len \$Tmp\ + 2 ] + 1) ] . \\\r\ + \n \$Prefix . \$Bin);\r\ + \n }\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n:set CleanFilePath do={\r\ + \n :local Path [ :tostr \$1 ];\r\ + \n :global CharacterReplace;\r\ + \n :while (\$Path ~ \"//\") do={ :set \$Path [ \$CharacterReplace \$Path \ + \"//\" \"/\" ]; }\r\ + \n :if ([ :pick \$Path 0 ] = \"/\") do={ :set Path [ :pick \$Path 1 [ :le\ + n \$Path ] ]; }\r\ + \n :if ([ :pick \$Path ([ :len \$Path ] - 1) ] = \"/\") do={ :set Path [ \ + :pick \$Path 0 ([ :len \$Path ] - 1) ]; }\r\ + \n :return \$Path;\r\ + \n}\r\ + \n\r\ + \n:set CleanName do={\r\ + \n :local Input [ :tostr \$1 ];\r\ + \n :local Return \"\";\r\ + \n :for I from=0 to=([ :len \$Input ] - 1) do={\r\ + \n :local Char [ :pick \$Input \$I ];\r\ + \n :if ([ :typeof [ find \"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqr\ + stuvwxyz0123456789\" \$Char ] ] = \"nil\") do={\r\ + \n :do {:if ([ :len \$Return ] = 0) do={:error true};\r\ + \n :if ([ :pick \$Return ([ :len \$Return ] - 1) ] = \"-\") do={:er\ + ror true}; :set Char \"-\"} on-error={:set Char \"\"}; \r\ + \n };\r\ + \n :set Return (\$Return . \$Char)};\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set RandomDelay do={\r\ + \n :local Time [ :tonum \$1 ];\r\ + \n :local Unit [ :tostr \$2 ];\r\ + \n :global EitherOr;\r\ + \n :global GetRandomNumber;\r\ + \n :global MAX;\r\ + \n :if (\$Time = 0) do={:return false}\r\ + \n :delay ([ \$MAX 10 [ \$GetRandomNumber ([ :tonsec [ :totime (\$Time . \ + [ \$EitherOr \$Unit \"s\" ]) ] ] / 1000000) ] ] . \"ms\");\r\ + \n}\r\ + \n\r\ + \n:set EscapeForRegEx do={\r\ + \n :local Input [ :tostr \$1 ];\r\ + \n :if ([ :len \$Input ] = 0) do={:return \"\"}\r\ + \n :local Return \"\";\r\ + \n :local Chars (\"^.[]\\\$()|*+\?{}\\\\\");\r\ + \n :for I from=0 to=([ :len \$Input ] - 1) do={\r\ + \n :local Char [ :pick \$Input \$I ];\r\ + \n :if ([ :find \$Chars \$Char ]) do={:set Char (\"\\\\\" . \$Char)}\r\ + \n :set Return (\$Return . \$Char);\r\ + \n }\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set ExitError do={\r\ + \n :local ExitOK [ :tostr \$1 ];\r\ + \n :local Name [ :tostr \$2 ];\r\ + \n :local Error [ :tostr \$3 ];\r\ + \n :global IfThenElse;\r\ + \n :if (\$ExitOK = \"false\") do={\r\ + \n :log error (\$Name [ \$IfThenElse ([ :pick \$Name 0 1 ] = \"\\\$\") \ + \\\r\ + \n \"Function\" \"Script\" ] . \" '\" . \$Name . \"' exited with er\ + ror\" . \\\r\ + \n [ \$IfThenElse (!(\$Error ~ \"^(|true|false)\\\$\")) (\": \" . \ + \$Error) \".\" ]);\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n:set ExitOnError do={\r\ + \n :local Name [ :tostr \$1 ];\r\ + \n :local Error [ :tostr \$2 ];\r\ + \n :global IfThenElse;\r\ + \n :log error (\$Name . \" \" . [ \$IfThenElse ([ :pick \$Name 0 1 ] = \"\ + \\\$\") \\\r\ + \n \"Function\" \"Script\" ] . \" '\" . \$Name . \"' exited with erro\ + r\" . \\\r\ + \n [ \$IfThenElse (!(\$Error ~ \"^(|true|false)\\\$\")) (\": \" . \$E\ + rror) \".\" ]);\r\ + \n}\r\ + \n\r\ + \n:set MiniDateTimeStamp do={\r\ + \n :local mikro [ :tobool \$1 ];\r\ + \n /system/clock;\r\ + \n :local vdate [get date];\r\ + \n :local vtime [get time];\r\ + \n :local vdoff [:toarray \"0,4,5,7,8,10\"];\r\ + \n :local MM [:pick \$vdate (\$vdoff->2) (\$vdoff->3)];\r\ + \n :local M [:tonum \$MM];\r\ + \n :if (\$vdate ~ \".../../....\") do={\r\ + \n :set vdoff [:toarray \"7,11,1,3,4,6\"];\r\ + \n :set M ([:find \"xxanebarprayunulugepctovecANEBARPRAYUNULUGE\ + PCTOVEC\" [:pick \$vdate (\$vdoff->2) (\$vdoff->3)] -1] / 2);\r\ + \n :if (\$M>12) do={:set M (\$M - 12)}\r\ + \n :set MM [:pick (100 + \$M) 1 3];\r\ + \n }\r\ + \n :local yyyy [:pick \$vdate (\$vdoff->0) (\$vdoff->1)];\r\ + \n :local dd [:pick \$vdate (\$vdoff->4) (\$vdoff->5)];\r\ + \n :local HH [:pick \$vtime 0 2];\r\ + \n :local mm [:pick \$vtime 3 5];\r\ + \n :local ss [:pick \$vtime 6 8];\r\ + \n :local Return;\r\ + \n :if (\$mikro = true) do={\r\ + \n :set Return ([:pick \$yyyy 2 4] . [:pick \$dd 0 1] . [:pick \$HH 0\ + \_1] . [:pick \$mm 3 4] . [:pick \$ss 6 7]);\r\ + \n } else={\r\ + \n :set Return ([\$yyyy] . [\$MM] . [\$dd] . [\$HH] . [\$mm] . [\$ss]);\ + \r\ + \n }\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set WaitForFile do={\r\ + \n :local FileName [ :tostr \$1 ];\r\ + \n :local WaitTime [ :totime \$2 ];\r\ + \n :global CleanFilePath;\r\ + \n :global EitherOr;\r\ + \n :global MAX;\r\ + \n :set FileName [ \$CleanFilePath \$FileName ];\r\ + \n :local Delay ([ \$MAX [ \$EitherOr \$WaitTime 2s ] 100ms ] / 9);\r\ + \n :do {\r\ + \n :retry {\r\ + \n /file/get \$FileName;\r\ + \n :return true;\r\ + \n } delay=\$Delay max=10;\r\ + \n } on-error={ }\r\ + \n :while ([ :len [ /file/find where name=\$FileName ] ] > 0) do={\r\ + \n :do {\r\ + \n /file/get \$FileName;\r\ + \n :return true;\r\ + \n } on-error={ }\r\ + \n :delay \$Delay;\r\ + \n :set Delay (\$Delay * 3 / 2);\r\ + \n }\r\ + \n :return false;\r\ + \n}\r\ + \n\r\ + \n:set FileGet do={\r\ + \n :local FileName [ :tostr \$1 ];\r\ + \n :global WaitForFile;\r\ + \n :if ([ \$WaitForFile \$FileName 0s ] = false) do={\r\ + \n :return false;\r\ + \n }\r\ + \n :local FileVal false;\r\ + \n :do {\r\ + \n :set FileVal [ /file/get \$FileName ];\r\ + \n } on-error={ }\r\ + \n :return \$FileVal;\r\ + \n}\r\ + \n\r\ + \n:set RmDir do={\r\ + \n :local DirName [ :tostr \$1 ];\r\ + \n :global FileGet;\r\ + \n :log debug (\$0 . \" Removing directory: \". \$DirName);\r\ + \n :local DirVal [ \$FileGet \$DirName ];\r\ + \n :if (\$DirVal = false) do={\r\ + \n :log debug (\$0 . \" ... which does not exist.\");\r\ + \n :return true;\r\ + \n };\r\ + \n :if (\$DirVal->\"type\" != \"directory\") do={\r\ + \n :log error (\$0 . \" Directory '\" . \$DirName . \"' is not a direct\ + ory.\");\r\ + \n :return false;\r\ + \n };\r\ + \n :onerror Err {\r\ + \n /file/remove \$DirName;\r\ + \n } do={\r\ + \n :log error (\$0 . \" Removing directory '\" . \$DirName . \"' failed\ + : \" . \$Err);\r\ + \n :return false;\r\ + \n };\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set RmFile do={\r\ + \n :local FileName [ :tostr \$1 ];\r\ + \n :global FileGet;\r\ + \n :log debug (\$0 . \" Removing file: \". \$FileName);\r\ + \n :local FileVal [ \$FileGet \$FileName ];\r\ + \n :if (\$FileVal = false) do={\r\ + \n :log debug (\$0 . \" ... which does not exist.\");\r\ + \n :return true;\r\ + \n }\r\ + \n :if (\$FileVal->\"type\" = \"directory\" || \$FileVal->\"type\" = \"di\ + sk\") do={\r\ + \n :log error (\$0 . \" File '\" . \$FileName . \"' is not a file.\");\ + \r\ + \n :return false;\r\ + \n }\r\ + \n :onerror Err {\r\ + \n /file/remove \$FileName;\r\ + \n } do={\r\ + \n :log error (\$0 . \" Removing file '\" . \$FileName . \"' failed: \"\ + \_. \$Err); :return false;\r\ + \n }\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set MkDir do={\r\ + \n :local Path [ :tostr \$1 ];\r\ + \n :global CleanFilePath;\r\ + \n :global FileGet;\r\ + \n :global RmDir;\r\ + \n :global WaitForFile;\r\ + \n\r\ + \n :local MkTmpfs do={\r\ + \n :global WaitForFile;\r\ + \n :local TmpFs [ /disk/find where slot=tmpfs type=tmpfs ];\r\ + \n :if ([ :len \$TmpFs ] = 1) do={\r\ + \n :if ([ /disk/get \$TmpFs disabled ] = true) do={\r\ + \n :log info (\$0 . \" The tmpfs is disabled, enabling.\");\r\ + \n /disk/enable \$TmpFs;\r\ + \n }\r\ + \n :return true;\r\ + \n }\r\ + \n :log info (\$0 . \" Creating disk of type tmpfs.\");\r\ + \n \$RmDir \"tmpfs\";\r\ + \n :onerror Err {\r\ + \n /disk/add slot=tmpfs type=tmpfs tmpfs-max-size=([ /system/resource\ + /get total-memory ] / 3);\r\ + \n \$WaitForFile \"tmpfs\";\r\ + \n } do={\r\ + \n :log warning (\$0 . \" Creating disk of type tmpfs failed: \" . \$\ + Err);\r\ + \n :return false;\r\ + \n }\r\ + \n :return true;\r\ + \n }\r\ + \n :set Path [ \$CleanFilePath \$Path ];\r\ + \n :if (\$Path = \"\") do={\r\ + \n :return true;\r\ + \n }\r\ + \n :log debug (\$0 . \" Making directory: \" . \$Path);\r\ + \n :local PathVal [ \$FileGet \$Path ];\r\ + \n :if (\$PathVal->\"type\" = \"directory\") do={\r\ + \n :log debug (\$0 . \" ... which already exists.\");\r\ + \n :return true;\r\ + \n }\r\ + \n :if ([ :pick \$Path 0 5 ] = \"tmpfs\") do={\r\ + \n :if ([ \$MkTmpfs ] = false) do={\r\ + \n :return false;\r\ + \n }\r\ + \n }\r\ + \n :onerror Err {\r\ + \n /file/add type=\"directory\" name=\$Path;\r\ + \n \$WaitForFile \$Path;\r\ + \n } do={\r\ + \n :log warning (\$0 . \" Making directory '\" . \$Path . \"' failed: \ + \" . \$Err);\r\ + \n :return false;\r\ + \n }\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set MAX do={:if (\$1 > \$2) do={:return \$1}\r\ + \n :return \$2;\r\ + \n}\r\ + \n:set MIN do={:if (\$1 < \$2) do={:return \$1}\r\ + \n :return \$2;\r\ + \n}\r\ + \n\r\ + \n:set CharacterMultiply do={\r\ + \n :local Str [ :tostr \$1 ];\r\ + \n :local Num [ :tonum \$2 ];\r\ + \n :if (\$Num = 0) do={:return \"\"}\r\ + \n :local Return \"\";\r\ + \n :for I from=1 to=\$Num do={:set Return (\$Return . \$Str)}\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set CharacterReplace do={\r\ + \n :local String [ :tostr \$1 ];\r\ + \n :local ReplaceFrom [ :tostr \$2 ];\r\ + \n :local ReplaceWith [ :tostr \$3 ];\r\ + \n :local Return \"\";\r\ + \n :if (\$ReplaceFrom = \"\") do={:return \$String}\r\ + \n :while ([ :typeof [ :find \$String \$ReplaceFrom ] ] != \"nil\") do={\ + \r\ + \n :local Pos [ :find \$String \$ReplaceFrom ];\r\ + \n :set Return (\$Return . [ :pick \$String 0 \$Pos ] . \$ReplaceWith);\ + \r\ + \n :set String [ :pick \$String (\$Pos + [ :len \$ReplaceFrom ]) [ :len\ + \_\$String ] ];\r\ + \n }\r\ + \n :return (\$Return . \$String);\r\ + \n}\r\ + \n\r\ + \n:set IfThenElse do={\r\ + \n :if ([ :tostr \$1 ] = \"true\" || [ :tobool \$1 ] = true) do={:return \ + \$2};\r\ + \n :return \$3;\r\ + \n}\r\ + \n\r\ + \n:set EitherOr do={\r\ + \n :global IfThenElse;\r\ + \n :if ([ :typeof \$1 ] = \"num\") do={:return [ \$IfThenElse (\$1 != 0) \ + \$1 \$2 ]}\r\ + \n :if ([ :typeof \$1 ] = \"time\") do={:return [ \$IfThenElse (\$1 > 0s)\ + \_\$1 \$2 ]}\r\ + \n# this works for boolean values, literal ones with parentheses\r\ + \n :return [ \$IfThenElse ([ :len [ :tostr \$1 ] ] > 0) \$1 \$2 ];\r\ + \n}\r\ + \n\r\ + \n:set ParseKeyValueStore do={\r\ + \n :local Source \$1;\r\ + \n :if ([ :pick \$Source 0 1 ] = \"{\") do={\r\ + \n :do {\r\ + \n :return [ :deserialize from=json \$Source ];\r\ + \n } on-error={ }\r\ + \n } \r\ + \n :if ([ :typeof \$Source ] != \"array\") do={:set Source [ :tostr \$1 ]\ + }\r\ + \n :local Result ({});\r\ + \n :foreach KeyValue in=[ :toarray \$Source ] do={\r\ + \n :if ([ :find \$KeyValue \"=\" ]) do={\r\ + \n :local Key [ :pick \$KeyValue 0 [ :find \$KeyValue \"=\" ] ];\r\ + \n :local Value [ :pick \$KeyValue ([ :find \$KeyValue \"=\" ] + 1) [\ + \_:len \$KeyValue ] ];\r\ + \n :if (\$Value=\"true\") do={:set Value true}\r\ + \n :if (\$Value=\"false\") do={:set Value false}\r\ + \n :set (\$Result->\$Key) \$Value;\r\ + \n } else={:set (\$Result->\$KeyValue) true}\r\ + \n }\r\ + \n :return \$Result;\r\ + \n}\r\ + \n:set RequiredRouterOS do={\r\ + \n :local Caller [ :tostr \$1 ];\r\ + \n :local Required [ :tostr \$2 ];\r\ + \n :local Warn [ :tostr \$3 ];\r\ + \n :global IfThenElse;\r\ + \n :global VersionToNum;\r\ + \n :if (!(\$Required ~ \"^\\\\d+\\\\.\\\\d+((alpha|beta|rc|\\\\.)\\\\d+|)\ + \\\$\")) do={\r\ + \n :log error (\$0 . \" No valid RouterOS version: \" . \$Required);\r\ + \n :return false;\r\ + \n }\r\ + \n :if ([ \$VersionToNum \$Required ] > [ \$VersionToNum [ /system/packag\ + e/update/get installed-version ] ]) do={\r\ + \n :if (\$Warn = \"true\") do={\r\ + \n :log warning (\$0 . \" This \" . [ \$IfThenElse ([ :pick \$Caller \ + 0 ] = (\"\\\$\")) \"function\" \"script\" ] . \\\r\ + \n \" '\" . \$Caller . \"' (at least specific functionality) requir\ + es RouterOS \" . \$Required . \". Please update!\");\r\ + \n }\r\ + \n :return false;\r\ + \n }\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set ValidateSyntax do={\r\ + \n :local Code [ :tostr \$1 ];\r\ + \n :global LogPrint;\r\ + \n :onerror Err {\r\ + \n [ :parse (\":local Validate do={\\n\" . \$Code . \"\\n}\") ];\r\ + \n } do={\r\ + \n \$LogPrint debug \$0 (\"Valdation failed: \" . \$Err);\r\ + \n :return false;\r\ + \n }\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set VersionToNum do={\r\ + \n :local Input [ :tostr \$1 ];\r\ + \n :local Multi 0x1000000;\r\ + \n :local Return 0;\r\ + \n :global CharacterReplace;\r\ + \n :set Input [ \$CharacterReplace \$Input \".\" \",\" ];\r\ + \n :foreach I in={ \"zero\"; \"alpha\"; \"beta\"; \"rc\" } do={\r\ + \n :set Input [ \$CharacterReplace \$Input \$I (\",\" . \$I . \",\") ];\ + \r\ + \n }\r\ + \n :foreach Value in=([ :toarray \$Input ], 0) do={\r\ + \n :local Num [ :tonum \$Value ];\r\ + \n :if (\$Multi = 0x100) do={\r\ + \n :if ([ :typeof \$Num ] = \"num\") do={\r\ + \n :set Return (\$Return + 0xff00);\r\ + \n :set Multi (\$Multi / 0x100);\r\ + \n } else={\r\ + \n :if (\$Value = \"zero\") do={ }\r\ + \n :if (\$Value = \"alpha\") do={ :set Return (\$Return + 0x3f00); \ + }\r\ + \n :if (\$Value = \"beta\") do={ :set Return (\$Return + 0x5f00); }\ + \r\ + \n :if (\$Value = \"rc\") do={ :set Return (\$Return + 0x7f00); }\r\ + \n }\r\ + \n }\r\ + \n :if ([ :typeof \$Num ] = \"num\") do={ :set Return (\$Return + (\$Va\ + lue * \$Multi)); }\r\ + \n :set Multi (\$Multi / 0x100);\r\ + \n }\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set ScriptLock do={\r\ + \n :local Script [ :tostr \$1 ];\r\ + \n :local WaitMax [ :totime \$2 ];\r\ + \n :global GetRandom20CharAlNum;\r\ + \n :global IfThenElse;\r\ + \n :global ScriptLockOrder;\r\ + \n :if ([ :typeof \$ScriptLockOrder ] = \"nothing\") do={\r\ + \n :set ScriptLockOrder ({});\r\ + \n }\r\ + \n :if ([ :typeof (\$ScriptLockOrder->\$Script) ] = \"nothing\") do={\r\ + \n :set (\$ScriptLockOrder->\$Script) ({});\r\ + \n }\r\ + \n :local JobCount do={\r\ + \n :local Script [ :tostr \$1 ];\r\ + \n :return [ :len [ /system/script/job/find where script=\$Script ] ];\ + \r\ + \n }\r\ + \n :local TicketCount do={\r\ + \n :local Script [ :tostr \$1 ];\r\ + \n :global ScriptLockOrder;\r\ + \n :local Count 0;\r\ + \n :foreach Ticket in=(\$ScriptLockOrder->\$Script) do={\r\ + \n :if ([ :typeof \$Ticket ] != \"nothing\") do={:set Count (\$Count \ + + 1)}\r\ + \n }\r\ + \n :return \$Count;\r\ + \n }\r\ + \n :local IsFirstTicket do={\r\ + \n :local Script [ :tostr \$1 ];\r\ + \n :local Check [ :tostr \$2 ];\r\ + \n :global ScriptLockOrder;\r\ + \n :foreach Ticket in=(\$ScriptLockOrder->\$Script) do={\r\ + \n :if (\$Ticket = \$Check) do={:return true}\r\ + \n :if ([ :typeof \$Ticket ] != \"nothing\" && \$Ticket != \$Check) d\ + o={:return false}\r\ + \n }\r\ + \n :return false;\r\ + \n }\r\ + \n :local AddTicket do={\r\ + \n :local Script [ :tostr \$1 ];\r\ + \n :local Add [ :tostr \$2 ];\r\ + \n :global ScriptLockOrder;\r\ + \n :while (true) do={\r\ + \n :local Pos [ :len (\$ScriptLockOrder->\$Script) ];\r\ + \n :set (\$ScriptLockOrder->\$Script->\$Pos) \$Add;\r\ + \n :delay 10ms;\r\ + \n :if ((\$ScriptLockOrder->\$Script->\$Pos) = \$Add) do={:return tru\ + e}\r\ + \n }\r\ + \n }\r\ + \n :local RemoveTicket do={\r\ + \n :local Script [ :tostr \$1 ];\r\ + \n :local Remove [ :tostr \$2 ];\r\ + \n :global ScriptLockOrder;\r\ + \n :foreach Id,Ticket in=(\$ScriptLockOrder->\$Script) do={\r\ + \n :while ((\$ScriptLockOrder->\$Script->\$Id) = \$Remove) do={\r\ + \n :set (\$ScriptLockOrder->\$Script->\$Id);\r\ + \n :delay 10ms;\r\ + \n }\r\ + \n }\r\ + \n }\r\ + \n :local CleanupTickets do={\r\ + \n :local Script [ :tostr \$1 ];\r\ + \n :global ScriptLockOrder;\r\ + \n :foreach Ticket in=(\$ScriptLockOrder->\$Script) do={\r\ + \n :if ([ :typeof \$Ticket ] != \"nothing\") do={:return false}\r\ + \n }\r\ + \n :set (\$ScriptLockOrder->\$Script) ({});\r\ + \n }\r\ + \n :if ([ :typeof \$WaitMax ] = \"nil\" ) do={:set WaitMax 0s}\r\ + \n :if ([ :len [ /system/script/find where name=\$Script ] ] = 0) do={\r\ + \n :log error (\$0 . \" A script named '\" . \$Script . \"' does not ex\ + ist!\");\r\ + \n :error false;\r\ + \n }\r\ + \n :if ([ \$JobCount \$Script ] = 0) do={\r\ + \n \$LogPrint error \$0 (\"No script '\" . \$Script . \"' is running!\"\ + );\r\ + \n :error false;\r\ + \n }\r\ + \n :if ([ \$TicketCount \$Script ] >= [ \$JobCount \$Script ]) do={\r\ + \n :log error (\$0 . \" More tickets than running scripts '\" . \$Scrip\ + t . \"', resetting!\");\r\ + \n :set (\$ScriptLockOrder->\$Script) ({});\r\ + \n /system/script/job/remove [ find where script=\$Script ];\r\ + \n }\r\ + \n :local MyTicket [ \$GetRandom20CharAlNum 6 ];\r\ + \n \$AddTicket \$Script \$MyTicket;\r\ + \n :local WaitInterval (\$WaitMax / 20);\r\ + \n :local WaitTime \$WaitMax;\r\ + \n :while (\$WaitTime > 0 && \\\r\ + \n ([ \$IsFirstTicket \$Script \$MyTicket ] = false || \\\r\ + \n [ \$TicketCount \$Script ] < [ \$JobCount \$Script ])) do={\r\ + \n :set WaitTime (\$WaitTime - \$WaitInterval);\r\ + \n :delay \$WaitInterval;\r\ + \n }\r\ + \n :if ([ \$IsFirstTicket \$Script \$MyTicket ] = true && \\\r\ + \n [ \$TicketCount \$Script ] = [ \$JobCount \$Script ]) do={\r\ + \n \$RemoveTicket \$Script \$MyTicket;\r\ + \n \$CleanupTickets \$Script;\r\ + \n :return true;\r\ + \n }\r\ + \n \$RemoveTicket \$Script \$MyTicket;\r\ + \n \$LogPrint debug \$0 (\"Script '\" . \$Script . \"' started more than \ + once\" . \\\r\ + \n [ \$IfThenElse (\$WaitTime < \$WaitMax) \" and timed out waiting for\ + \_lock\" \"\" ] . \"...\");\r\ + \n :return false;\r\ + \n}\r\ + \n\r\ + \n:set SendNotification do={ :onerror Err {\r\ + \n :global SendNotification2;\r\ + \n \$SendNotification2 ({ origin=\$0; subject=\$1; message=\$2; link=\$3;\ + \_silent=\$4 });\r\ + \n} do={:global ExitOnError; \$ExitOnError \$0 \$Err}\r\ + \n}\r\ + \n\r\ + \n:set SendNotification2 do={\r\ + \n :local Notification \$1;\r\ + \n :global NotificationFunctions;\r\ + \n :foreach FunctionName,Discard in=\$NotificationFunctions do={\r\ + \n (\$NotificationFunctions->\$FunctionName) (\"\\\$NotificationFunctio\ + ns->\\\"\" . \$FunctionName . \"\\\"\") \$Notification;\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n:set SymbolByUnicodeName do={\r\ + \n :local Name [ :tostr \$1 ];\r\ + \n :global EitherOr;\r\ + \n :global SymbolsExtra;\r\ + \n :local Symbols ({\r\ + \n \"abacus\"=\"\\F0\\9F\\A7\\AE\";\r\ + \n \"alarm-clock\"=\"\\E2\\8F\\B0\";\r\ + \n \"arrow-down\"=\"\\E2\\AC\\87\";\r\ + \n \"arrow-up\"=\"\\E2\\AC\\86\";\r\ + \n \"calendar\"=\"\\F0\\9F\\93\\85\";\r\ + \n \"card-file-box\"=\"\\F0\\9F\\97\\83\";\r\ + \n \"chart-decreasing\"=\"\\F0\\9F\\93\\89\";\r\ + \n \"chart-increasing\"=\"\\F0\\9F\\93\\88\";\r\ + \n \"cloud\"=\"\\E2\\98\\81\";\r\ + \n \"cross-mark\"=\"\\E2\\9D\\8C\";\r\ + \n \"earth\"=\"\\F0\\9F\\8C\\8D\";\r\ + \n \"fire\"=\"\\F0\\9F\\94\\A5\";\r\ + \n \"floppy-disk\"=\"\\F0\\9F\\92\\BE\";\r\ + \n \"gear\"=\"\\E2\\9A\\99\";\r\ + \n \"heart\"=\"\\E2\\99\\A5\";\r\ + \n \"high-voltage-sign\"=\"\\E2\\9A\\A1\";\r\ + \n \"incoming-envelope\"=\"\\F0\\9F\\93\\A8\";\r\ + \n \"information\"=\"\\E2\\84\\B9\";\r\ + \n \"large-orange-circle\"=\"\\F0\\9F\\9F\\A0\";\r\ + \n \"large-red-circle\"=\"\\F0\\9F\\94\\B4\";\r\ + \n \"link\"=\"\\F0\\9F\\94\\97\";\r\ + \n \"lock-with-ink-pen\"=\"\\F0\\9F\\94\\8F\";\r\ + \n \"memo\"=\"\\F0\\9F\\93\\9D\";\r\ + \n \"mobile-phone\"=\"\\F0\\9F\\93\\B1\";\r\ + \n \"pushpin\"=\"\\F0\\9F\\93\\8C\";\r\ + \n \"scissors\"=\"\\E2\\9C\\82\";\r\ + \n \"scroll\"=\"\\F0\\9F\\93\\9C\";\r\ + \n \"smiley-partying-face\"=\"\\F0\\9F\\A5\\B3\";\r\ + \n \"smiley-smiling-face\"=\"\\E2\\98\\BA\";\r\ + \n \"smiley-winking-face-with-tongue\"=\"\\F0\\9F\\98\\9C\";\r\ + \n \"sparkles\"=\"\\E2\\9C\\A8\";\r\ + \n \"speech-balloon\"=\"\\F0\\9F\\92\\AC\";\r\ + \n \"star\"=\"\\E2\\AD\\90\";\r\ + \n \"warning-sign\"=\"\\E2\\9A\\A0\";\r\ + \n \"white-heavy-check-mark\"=\"\\E2\\9C\\85\"\r\ + \n }, \$SymbolsExtra);\r\ + \n :local Magic [ :pick [ /system/clock/get date ] 4 10 ];\r\ + \n :local Special {\r\ + \n \"information-04-01\"=\"\\F0\\9F\\9A\\BB\";\r\ + \n \"large-orange-circle-04-01\"=\"\\F0\\9F\\8D\\8A\";\r\ + \n \"large-orange-circle-10-31\"=\"\\F0\\9F\\8E\\83\";\r\ + \n \"large-red-circle-04-01\"=\"\\F0\\9F\\8D\\92\" };\r\ + \n :if ([ :len (\$Symbols->\$Name) ] = 0) do={\r\ + \n :log warning (\$0 . \"No symbol available for name '\" . \$Name . \"\ + '!\");\r\ + \n :return \"\";\r\ + \n }\r\ + \n :return ([ \$EitherOr (\$Special->(\$Name . \$Magic)) (\$Symbols->\$Na\ + me) ] . \"\\EF\\B8\\8F\");\r\ + \n}\r\ + \n\r\ + \n:set SymbolForNotification do={\r\ + \n :global NotificationsWithSymbols;\r\ + \n :global SymbolByUnicodeName;\r\ + \n :global IfThenElse;\r\ + \n :if (\$NotificationsWithSymbols != true) do={:return [ \$IfThenElse ([\ + \_:len \$2 ] > 0) ([ :tostr \$2 ] . \" \") \"\" ]}\r\ + \n :local Return \"\";\r\ + \n :foreach Symbol in=[ :toarray \$1 ] do={:set Return (\$Return . [ \$Sy\ + mbolByUnicodeName \$Symbol ])}\r\ + \n :return (\$Return . \" \");\r\ + \n}\r\ + \n\r\ + \n:set Unix2Dos do={:return [ :tocrlf [ :tostr \$1 ] ]}\r\ + \n:set UrlEncode do={\r\ + \n :local Input [ :tostr \$1 ];\r\ + \n :if ([ :len \$Input ] = 0) do={:return \"\"}\r\ + \n :local Return \"\";\r\ + \n :local Chars (\"\\n\\r !\\\"#\\\$%&'()*+,:;<=>\?@[\\\\]^`{|}~\");\r\ + \n :local Subs { \"%0A\"; \"%0D\"; \"%20\"; \"%21\"; \"%22\"; \"%23\"; \"\ + %24\"; \"%25\"; \"%26\"; \"%27\";\r\ + \n \"%28\"; \"%29\"; \"%2A\"; \"%2B\"; \"%2C\"; \"%3A\"; \"%3B\"; \ + \"%3C\"; \"%3D\"; \"%3E\"; \"%3F\";\r\ + \n \"%40\"; \"%5B\"; \"%5C\"; \"%5D\"; \"%5E\"; \"%60\"; \"%7B\"; \ + \"%7C\"; \"%7D\"; \"%7E\" };\r\ + \n :for I from=0 to=([ :len \$Input ] - 1) do={\r\ + \n :local Char [ :pick \$Input \$I ];\r\ + \n :local Replace [ :find \$Chars \$Char ];\r\ + \n :if ([ :typeof \$Replace ] = \"num\") do={:set Char (\$Subs->\$Repla\ + ce)}\r\ + \n :set Return (\$Return . \$Char);\r\ + \n }\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set IsDNSResolving do={\r\ + \n :do {:resolve \"api.telegram.org\"} on-error={:return false}\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set IsFullyConnected do={\r\ + \n :global IsDNSResolving;\r\ + \n :global IsTimeSync;\r\ + \n :if ([ \$IsDNSResolving ] = false) do={:return false};\r\ + \n :if ([ \$IsTimeSync ] = false) do={:return false};\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set IsTimeSync do={\r\ + \n :global IsTimeSyncCached;\r\ + \n :global IsTimeSyncResetNtp;\r\ + \n :if (\$IsTimeSyncCached = true) do={:return true}\r\ + \n :if ([ /system/ntp/client/get enabled ] = true) do={\r\ + \n :if ([ /system/ntp/client/get status ] = \"synchronized\") do={\r\ + \n :set IsTimeSyncCached true;\r\ + \n :return true;\r\ + \n }\r\ + \n :local Uptime [ /system/resource/get uptime ];\r\ + \n :if ([ :typeof \$IsTimeSyncResetNtp ] = \"nothing\") do={\r\ + \n :set IsTimeSyncResetNtp \$Uptime;\r\ + \n }\r\ + \n :if (\$Uptime - \$IsTimeSyncResetNtp < 3m) do={:return false}\r\ + \n :log warning \"\$0 The ntp client is configured, but did not sync.\"\ + ;\r\ + \n :set IsTimeSyncResetNtp \$Uptime;\r\ + \n /system/ntp/client/set enabled=no;\r\ + \n :delay 20ms;\r\ + \n /system/ntp/client/set enabled=yes;\r\ + \n :return false;\r\ + \n }\r\ + \n :if ([ /system/license/get ]->\"level\" = \"free\" || \\\r\ + \n [ /system/resource/get ]->\"board-name\" = \"x86\") do={\r\ + \n :log debug \"\$0 No ntp client configured, relying on RTC for CHR fr\ + ee license and x86.\";\r\ + \n :return true;\r\ + \n }\r\ + \n :if ([ /ip/cloud/get update-time ] = true) do={\r\ + \n :if ([ :typeof [ /ip/cloud/get public-address ] ] = \"ip\") do={\r\ + \n :set IsTimeSyncCached true;\r\ + \n :return true;\r\ + \n }\r\ + \n :return false;\r\ + \n }\r\ + \n :log debug \"\$0 No time source configured! Returning gracefully...\";\ + \r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set FetchHugeFiles do={\r\ + \n :local ScriptName [ :tostr \$1 ];\r\ + \n :local Url [ :tostr \$2 ];\r\ + \n :local CheckCert [ :tostr \$3 ];\r\ + \n\r\ + \n :global CleanName;\r\ + \n :global FetchUserAgentStr;\r\ + \n :global GetRandom20CharAlNum;\r\ + \n :global IfThenElse;\r\ + \n :global LogPrint;\r\ + \n :global MkDir;\r\ + \n :global RmDir;\r\ + \n :global RmFile;\r\ + \n :global WaitForFile;\r\ + \n\r\ + \n :set CheckCert [ \$IfThenElse (\$CheckCert = \"false\") \"no\" \"yes-w\ + ithout-crl\" ];\r\ + \n\r\ + \n :local DirName (\"tmpfs/\" . [ \$CleanName \$ScriptName ]);\r\ + \n :if ([ \$MkDir \$DirName ] = false) do={\r\ + \n \$LogPrint error \$0 (\"Failed creating directory!\");\r\ + \n :return false;\r\ + \n }\r\ + \n\r\ + \n :local FileName (\$DirName . \"/\" . [ \$CleanName \$0 ] . \"-\" . [ \ + \$GetRandom20CharAlNum ]);\r\ + \n :onerror Err {\r\ + \n /tool/fetch check-certificate=\$CheckCert \$Url dst-path=\$FileName \ + \\\r\ + \n http-header-field=({ [ \$FetchUserAgentStr \$ScriptName ] }) as-va\ + lue;\r\ + \n } do={\r\ + \n :if ([ \$WaitForFile \$FileName 500ms ] = true) do={\r\ + \n \$RmFile \$FileName;\r\ + \n }\r\ + \n \$LogPrint debug \$0 (\"Failed downloading from \" . \$Url . \" - \"\ + \_. \$Err);\r\ + \n \$RmDir \$DirName;\r\ + \n :return false;\r\ + \n }\r\ + \n\r\ + \n :if ([ \$WaitForFile \$FileName 5s ] = false) do={\r\ + \n \$LogPrint debug \$0 (\"The file downloaded from \" . \$Url . \" did\ + \_not show up.\");\r\ + \n :return false;\r\ + \n }\r\ + \n\r\ + \n :local FileSize [ /file/get \$FileName size ];\r\ + \n :local Return \"\";\r\ + \n :local VarSize 0;\r\ + \n :while (\$VarSize != \$FileSize) do={\r\ + \n :set Return (\$Return . ([ /file/read offset=\$VarSize chunk-size=32\ + 768 file=\$FileName as-value ]->\"data\"));\r\ + \n :set FileSize [ /file/get \$FileName size ];\r\ + \n :set VarSize [ :len \$Return ];\r\ + \n :if (\$VarSize > \$FileSize) do={\r\ + \n :delay 100ms;\r\ + \n }\r\ + \n }\r\ + \n \$RmDir \$DirName;\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set FetchUserAgentStr do={\r\ + \n :local Caller [ :tostr \$1 ];\r\ + \n :global GlobalScriptsUrl;\r\ + \n :global CommitBrief;\r\ + \n :global IfThenElse;\r\ + \n :local Resource [ /system/resource/get ];\r\ + \n :return (\"User-Agent: Mikrotik/\" . \$Resource->\"version\" . \" \" .\ + \_\$Resource->\"architecture-name\" . \\\r\ + \n \" \" . \$Caller . \"/Fetch (\" . \$GlobalScriptsUrl . \"; \" . [ \$\ + CommitBrief ] . \")\");\r\ + \n}\r\ + \n\r\ + \n:set ScriptInstallUpdate do={ :onerror Err {\r\ + \n :local Scripts [ :toarray \$1 ];\r\ + \n :local NewComment [ :tostr \$2 ];\r\ + \n :global InitSetup;\r\ + \n\r\ + \n :global CommitId;\r\ + \n :global ScriptUpdatesCRLF;\r\ + \n :global GlobalConfVersion;\r\ + \n :global Identity;\r\ + \n :global CertificateAvailable;\r\ + \n :global CheckSumsVerify;\r\ + \n :global GlobalScriptsUrl;\r\ + \n :global SumsDirAndFileName;\r\ + \n :global GlobalScriptsUrlSuffix;\r\ + \n :global EitherOr;\r\ + \n :global FetchUserAgentStr;\r\ + \n :global Grep;\r\ + \n :global IfThenElse;\r\ + \n :global LogPrint;\r\ + \n :global ParseKeyValueStore;\r\ + \n :global RequiredRouterOS;\r\ + \n :global SendNotification2;\r\ + \n :global SymbolForNotification;\r\ + \n :global ValidateSyntax;\r\ + \n :global CommentsInScripts;\r\ + \n\r\ + \n :if ([ \$CertificateAvailable \"Root YE\" \"fetch\" ] = false) do={\r\ + \n \$LogPrint warning \$0 (\"Downloading certificate failed, trying wit\ + hout.\");\r\ + \n }\r\ + \n\r\ + \n :foreach Script in=\$Scripts do={\r\ + \n :if ([ :len [ /system/script/find where name=\$Script ] ] = 0) do={\ + \r\ + \n \$LogPrint info \$0 (\"Adding new script: \" . \$Script);\r\ + \n /system/script/add name=\$Script owner=\$Script source=\"#!rsc by \ + Vados\\n\" comment=\$NewComment;\r\ + \n }\r\ + \n }\r\ + \n\r\ + \n :local CommitIdBefore \$CommitId;\r\ + \n :local GlobalConfVersionBefore \$GlobalConfVersion;\r\ + \n :local ReloadGlobal false;\r\ + \n :local DeviceMode [ /system/device-mode/get ];\r\ + \n\r\ + \n :if (\$CheckSumsVerify = true) do={\r\ + \n :local CheckSums ({});\r\ + \n :do {\r\ + \n :local Url (\$GlobalScriptsUrl . \$SumsDirAndFileName . \$GlobalScr\ + iptsUrlSuffix);\r\ + \n \$LogPrint debug \$0 (\"Fetching checksums from url: \" . \$Url);\ + \r\ + \n :set CheckSums [ :deserialize from=json ([ /tool/fetch check-certi\ + ficate=yes-without-crl \\\r\ + \n http-header-field=({ [ \$FetchUserAgentStr \$0 ] }) \$Url output\ + =user as-value ]->\"data\") ];\r\ + \n } on-error={ \$LogPrint warning \$0 (\"Err = \" . \$Err); }\r\ + \n }\r\ + \n\r\ + \n :foreach Script in=[ /system/script/find where source~\"^#!rsc by Vado\ + s\" ] do={\r\ + \n :local ScriptVal [ /system/script/get \$Script ];\r\ + \n :local ScriptInfo [ \$ParseKeyValueStore (\$ScriptVal->\"comment\") \ + ];\r\ + \n :local SourceNew;\r\ + \n\r\ + \n :foreach Scheduler in=[ /system/scheduler/find where on-event~(\"\\\ + \\b\" . \$ScriptVal->\"name\" . \"\\\\b\") ] do={\r\ + \n :local SchedulerVal [ /system/scheduler/get \$Scheduler ];\r\ + \n :if (\$ScriptVal->\"policy\" != \$SchedulerVal->\"policy\") do={\r\ + \n \$LogPrint warning \$0 (\"Policies differ for script '\" . \$Scr\ + iptVal->\"name\" . \"' and its scheduler '\" . \$SchedulerVal->\"name\" . \ + \"'!\");\r\ + \n }\r\ + \n }\r\ + \n :do {\r\ + \n :if (\$ScriptInfo->\"ignore\" = true) do={\r\ + \n \$LogPrint debug \$0 (\"Ignoring script '\" . \$ScriptVal->\"nam\ + e\" . \"', as requested.\");\r\ + \n :error true;\r\ + \n }\r\ + \n :if (\$CheckSumsVerify = true) do={\r\ + \n :local CheckSum (\$CheckSums->(\$ScriptVal->\"name\"));\r\ + \n :if ([ :len (\$ScriptInfo->\"base-url\") ] = 0 && [ :len (\$Scri\ + ptInfo->\"url-suffix\") ] = 0 && \\\r\ + \n [ :convert transform=md5 to=hex [ :tolf (\$ScriptVal->\"sou\ + rce\") ] ] = \$CheckSum) do={\r\ + \n \$LogPrint debug \$0 (\"Checksum for script '\" . \$ScriptVal-\ + >\"name\" . \"' matches, ignoring.\");\r\ + \n :error true;\r\ + \n }\r\ + \n }\r\ + \n :if ([ :len (\$ScriptInfo->\"certificate\") ] > 0) do={\r\ + \n :if ([ \$CertificateAvailable (\$ScriptInfo->\"certificate\") \"\ + fetch\" ] = false) do={\r\ + \n \$LogPrint warning \$0 (\"Downloading certificate failed, tryi\ + ng without.\");\r\ + \n }\r\ + \n }\r\ + \n :onerror Err {\r\ + \n :local BaseUrl [ \$EitherOr (\$ScriptInfo->\"base-url\") \$Globa\ + lScriptsUrl ];\r\ + \n :local UrlSuffix [ \$EitherOr (\$ScriptInfo->\"url-suffix\") \$G\ + lobalScriptsUrlSuffix ];\r\ + \n :local Url (\$BaseUrl . \$ScriptVal->\"name\" . \".rsc\" . \$Url\ + Suffix);\r\ + \n \$LogPrint warning \$0 (\"Fetching script '\" . (\$ScriptVal->\"\ + name\") . \"' from url: \" . \$Url);\r\ + \n :local Result [ /tool/fetch check-certificate=yes-without-crl \\\ + \r\ + \n http-header-field=({ [ \$FetchUserAgentStr \$0 ] }) \$Url outp\ + ut=user as-value ];\r\ + \n :if (\$Result->\"status\" = \"finished\") do={\r\ + \n :set SourceNew [ :tolf (\$Result->\"data\") ];\r\ + \n } \r\ + \n } do={\r\ + \n \$LogPrint warning \$0 (\"Failed fetching script '\" . \$ScriptV\ + al->\"name\" . \"': \" . \$Err);\r\ + \n :if (\$Err != \"Fetch failed with status 404\") do={\r\ + \n :error false;\r\ + \n }\r\ + \n :if (\$ScriptVal->\"source\" = \"#!rsc by Vados\") do={\r\ + \n \$LogPrint warning \$0 (\"Removing dummy. Typo on installation\ + \?\");\r\ + \n /system/script/remove \$Script;\r\ + \n :error false;\r\ + \n }\r\ + \n :if (\$CheckSumsVerify = true) do={\r\ + \n :if ([ :len (\$ScriptInfo->\"base-url\") ] = 0 && [ :len (\$Sc\ + riptInfo->\"url-suffix\") ] = 0 && \\\r\ + \n [ :len \$CheckSum ] = 0) do={\r\ + \n \$LogPrint warning \$0 (\"Added the script manually\? Skip u\ + pdates with 'ignore=true' in comment.\");\r\ + \n }\r\ + \n }\r\ + \n :error false;\r\ + \n }\r\ + \n\r\ + \n :if ([ :len \$SourceNew ] = 0) do={\r\ + \n \$LogPrint debug \$0 (\"No update for script '\" . (\$ScriptVal-\ + >\"name\") . \"'.\");\r\ + \n :error false;\r\ + \n }\r\ + \n\r\ + \n :local SourceCRLF [ :tocrlf \$SourceNew ];\r\ + \n :if (\$InitSetup != true && ((\$ScriptVal->\"name\") != \"AM-Globa\ + lConfig\" || (\$ScriptVal->\"name\") != \"AM-GlobalFunc\")) do={\r\ + \n :if (\$SourceNew = (\$ScriptVal->\"source\") || \$SourceCRLF = (\ + \$ScriptVal->\"source\")) do={\r\ + \n \$LogPrint debug \$0 (\"Script '\" . (\$ScriptVal->\"name\") \ + . \"' source did not change. End of lines set to CRLF.\");\r\ + \n if (\$ScriptUpdatesCRLF = false) do={\r\ + \n :put [ :tolf \$SourceNew ];\r\ + \n \$LogPrint debug \$0 (\"Script '\" . (\$ScriptVal->\"name\"\ + ) . \"' source update end of lines to LF.\");\r\ + \n }\r\ + \n :error false;\r\ + \n }\r\ + \n }\r\ + \n\r\ + \n :if ([ :pick \$SourceNew 0 14 ] != \"#!rsc by Vados\") do={\r\ + \n \$LogPrint warning \$0 (\"Looks like new script '\" . \$ScriptVa\ + l->\"name\" . \"' is not valid (missing shebang = '\$shebang') Ignoring!\"\ + );\r\ + \n :error false;\r\ + \n }\r\ + \n\r\ + \n :if (\$CommentsInScripts = true) do={\r\ + \n :local CommentLine [ \$Grep \$SourceNew (\"\\23 Script comment:\ + \_\") ];\r\ + \n :if ([ :len \$CommentLine ] = 0) do={\r\ + \n \$LogPrint debug \$0 (\"The search in new script '\" . (\$Scr\ + iptVal->\"name\") . \"' for comment in script body, is not found result Ig\ + noring!\");\r\ + \n :error false;\r\ + \n } else={\r\ + \n :if ([ :len (\$ScriptVal->\"comment\")] = 0 || [ :len (\$Scri\ + ptVal->\"comment\")] != [ :pick \$CommentLine ([ :find \$CommentLine \":\"\ + ] + 2) [ :len \$CommentLine] ]) do={\r\ + \n \$LogPrint debug \$0 (\"Exsiting comment for script '\" . (\ + \$ScriptVal->\"name\") . \"' is '\" . (\$ScriptVal->\"comment\") . \"'.\")\ + ;\r\ + \n :set (\$ScriptVal->\"comment\") [ :pick \$CommentLine ([ :f\ + ind \$CommentLine \":\"] + 2) [ :len \$CommentLine] ];\r\ + \n \$LogPrint debug \$0 (\"New comment (\" . (\$ScriptVal->\"c\ + omment\") . \") for script '\" . (\$ScriptVal->\"name\") . \"' has been ad\ + ded!\");\r\ + \n } else={\r\ + \n \$LogPrint debug \$0 (\"Comment for script '\" . (\$ScriptV\ + al->\"name\") . \"' did not change.\");\r\ + \n :error false;\r\ + \n }\r\ + \n } \r\ + \n }\r\ + \n\r\ + \n :local RequiredROS ([ \$ParseKeyValueStore [ \$Grep \$SourceNew (\ + \"\\23 requires RouterOS, \") ] ]->version);\r\ + \n :if ([ \$RequiredRouterOS \$0 [ \$EitherOr \$RequiredROS \"0.0\" ]\ + \_false ] = false) do={\r\ + \n \$LogPrint warning \$0 (\"The script '\" . (\$ScriptVal->\"name\ + \") . \"' requires RouterOS \" . \\\r\ + \n \$RequiredROS . \", which is not met by your installation. I\ + gnoring!\");\r\ + \n :error false;\r\ + \n }\r\ + \n :local RequiredDM [ \$ParseKeyValueStore [ \$Grep \$SourceNew (\"\ + \\23 requires device-mode, \") ] ];\r\ + \n :local MissingDM ({});\r\ + \n :foreach Feature,Value in=\$RequiredDM do={\r\ + \n :if ([ :typeof (\$DeviceMode->\$Feature) ] = \"bool\" && (\$Devi\ + ceMode->\$Feature) = false) do={\r\ + \n :set MissingDM (\$MissingDM, \$Feature);\r\ + \n }\r\ + \n }\r\ + \n :if ([ :len \$MissingDM ] > 0) do={\r\ + \n \$LogPrint warning \$0 (\"The script '\" . (\$ScriptVal->\"name\ + \") . \"' requires disabled \" . \\\r\ + \n \"device-mode features (\" . [ :tostr \$MissingDM ] . \"). I\ + gnoring!\");\r\ + \n :error false;\r\ + \n }\r\ + \n\r\ + \n :if ([ \$ValidateSyntax \$SourceNew ] = false) do={\r\ + \n \$LogPrint warning \$0 (\"Syntax validation for script '\" . (\$\ + ScriptVal->\"name\") . \"' failed! Ignoring!\");\r\ + \n :error false;\r\ + \n }\r\ + \n \r\ + \n :log info (\"Updating script: \" . (\$ScriptVal->\"name\") . \" Sc\ + ript comment: \" . (\$ScriptVal->\"comment\"));\r\ + \n\r\ + \n /system/script/set owner=(\$ScriptVal->\"name\") comment=(\$Script\ + Val->\"comment\") \\\r\ + \n source=[ \$IfThenElse (\$ScriptUpdatesCRLF = true) \$SourceCRL\ + F \$SourceNew ] \$Script;\r\ + \n :if (\$ScriptVal->\"name\" = \"AM-GlobalConfig\" || \\\r\ + \n \$ScriptVal->\"name\" = \"AM-GlobalFunc\" || \\\r\ + \n \$ScriptVal->\"name\" ~ (\"^(AM-GlobalFunc\\\\.d|mod)/.\")) d\ + o={\r\ + \n :set ReloadGlobal true;\r\ + \n }\r\ + \n } on-error={ }\r\ + \n }\r\ + \n :if (\$ReloadGlobal = true) do={\r\ + \n \$LogPrint info \$0 (\"Reloading global configuration and functions.\ + \");\r\ + \n :onerror Err {\r\ + \n :global InitSetup (a);\r\ + \n /system/script {run AM-GlobalConfig; run AM-GlobalFunc;};\r\ + \n } do={\$LogPrint error \$0 (\"Reloading global configuration and fun\ + ctions failed! \" . \$Err)}\r\ + \n }\r\ + \n :if (\$GlobalConfVersionBefore > \$GlobalConfVersion) do={\r\ + \n \$LogPrint warning \$0 (\"The configuration version decreased from \ + \" . \\\r\ + \n \$GlobalConfVersionBefore . \" to \" . \$GlobalConfVersion . \\\r\ + \n \". Installed an older version\?\");\r\ + \n }\r\ + \n :if (\$GlobalConfVersionBefore < \$GlobalConfVersion) do={\r\ + \n :global GlobalConfChanges;\r\ + \n :global GlobalConfMigration;\r\ + \n :local ChangeLogCode;\r\ + \n :if ([ :len \$ChangeLogCode ] > 0) do={\r\ + \n :if ([ \$ValidateSyntax \$ChangeLogCode ] = true) do={\r\ + \n :onerror Err {\r\ + \n [ :parse \$ChangeLogCode ];\r\ + \n } do={\$LogPrint warning \$0 (\"The changelog failed to run: \" \ + . \$Err)}\r\ + \n } else={\$LogPrint warning \$0 (\"The changelog failed syntax vali\ + dation!\")}\r\ + \n }\r\ + \n :if ([ :len \$GlobalConfMigration ] > 0) do={\r\ + \n :for I from=(\$GlobalConfVersionBefore + 1) to=\$GlobalConfVersion\ + \_do={\r\ + \n :local Migration (\$GlobalConfMigration->[ :tostr \$I ]);\r\ + \n :do {\r\ + \n :if ([ :typeof \$Migration ] != \"str\") do={\r\ + \n \$LogPrint debug \$0 (\"Migration code for change \" . \$I .\ + \_\" is not available.\");\r\ + \n :error false;\r\ + \n }\r\ + \n :if ([ \$ValidateSyntax \$Migration ] = false) do={\r\ + \n \$LogPrint warning \$0 (\"Migration code for change \" . \$I\ + \_. \" failed syntax validation!\");\r\ + \n :error false}\r\ + \n \$LogPrint info \$0 (\"Applying migration for change \" . \$I \ + . \": \" . \$Migration);\r\ + \n :onerror Err {\r\ + \n [ :parse \$Migration ];\r\ + \n } do={\$LogPrint warning \$0 (\"Migration code for change \" .\ + \_\$I . \" failed to run: \" . \$Err)}\r\ + \n } on-error={ }\r\ + \n }\r\ + \n }\r\ + \n :local NotificationMessage (\"The configuration version on \" . \$Id\ + entity . \" increased \" . \\\r\ + \n \"to \" . \$GlobalConfVersion . \", current configuration may nee\ + d modification. \" . \\\r\ + \n \"Please review and update global-config-overlay, then re-run glo\ + bal-config.\");\r\ + \n \$LogPrint info \$0 (\$NotificationMessage);\r\ + \n :if ([ :len \$GlobalConfChanges ] > 0) do={\r\ + \n :set NotificationMessage (\$NotificationMessage . \"\\n\\nChanges:\ + \");\r\ + \n :for I from=(\$GlobalConfVersionBefore + 1) to=\$GlobalConfVersion\ + \_do={\r\ + \n :local Change (\$GlobalConfChanges->[ :tostr \$I ]);\r\ + \n :set NotificationMessage (\$NotificationMessage . \"\\n \" . \\\ + \r\ + \n [ \$SymbolForNotification \"pushpin\" \"*\" ] . \$Change);\r\ + \n \$LogPrint info \$0 (\"Change \" . \$I . \": \" . \$Change);\r\ + \n }\r\ + \n } else={\r\ + \n :set NotificationMessage (\$NotificationMessage . \"\\n\\nNews and\ + \_changes are not available.\");\r\ + \n\r\ + \n }\r\ + \n\r\ + \n :if ([ :len \$GlobalConfChanges ] > 0) do={ :set GlobalConfChanges; \ + } else={ :global GlobalConfChanges (a); }\r\ + \n :if ([ :len \$GlobalConfMigration ] > 0) do={ :set GlobalConfMigrati\ + on; } else={ :global GlobalConfMigration (a); }\r\ + \n }\r\ + \n} do={\r\ + \n :global ExitOnError; \$ExitOnError \$0 \$Err;\r\ + \n} }\r\ + \n\r\ + \n# load modules\r\ + \n:foreach Script in=[ /system/script/find where name ~ \"^(global-functio\ + ns\\\\.d|mod)/.\" ] do={\r\ + \n :local ScriptVal [ /system/script/get \$Script ];\r\ + \n :if ([ \$ValidateSyntax (\$ScriptVal->\"source\") ] = true) do={\r\ + \n :onerror Err {\r\ + \n /system/script/run \$Script;\r\ + \n } do={\r\ + \n \$LogPrint error \$0 (\"Module '\" . \$ScriptVal->\"name\" . \"' f\ + ailed to run: \" . \$Err);\r\ + \n }\r\ + \n } else={\r\ + \n \$LogPrint error \$0 (\"Module '\" . \$ScriptVal->\"name\" . \"' fai\ + led syntax validation, skipping.\");\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n# Add and fix global scheduler\r\ + \n:put \"Add or/and fix Global scheduler...\";\r\ + \n:local OnEvent \"/system/script { run AM-GlobalConfig; run AM-GlobalFunc\ + ; }\";\r\ + \n:local Comment \"Every Day Set Global Environment Variables\";\r\ + \n:if ([ :len [ /system/scheduler/find where name=\"AM-SetGlobalEnv\" ] ] \ + = 0) do={\r\ + \n /system/scheduler/add name=\"AM-SetGlobalEnv\" comment=\$Comment on-ev\ + ent=\$OnEvent start-time=startup;\r\ + \n}\r\ + \n:if ([ /system/scheduler/get \"AM-SetGlobalEnv\" comment ] != \$Comment)\ + \_do={\r\ + \n /system/scheduler/set \"AM-SetGlobalEnv\" comment=\$Comment;\r\ + \n}\r\ + \n:if ([ /system/scheduler/get \"AM-SetGlobalEnv\" on-event ] != \$OnEvent\ + ) do={\r\ + \n /system/scheduler/set \"AM-SetGlobalEnv\" on-event=\$OnEvent;\r\ + \n}\r\ + \n# Add Update Script scheduler\r\ + \n:put \"Add or/and fix Update Script scheduler\";\r\ + \n:set OnEvent \":global ScriptInstallUpdate; \\\$ScriptInstallUpdate;\";\ + \r\ + \n:set Comment \"Every Day Check For Update Scripts\";\r\ + \n:if ([ :len [ /system/scheduler/find where name=\"AM-ScriptInstallUpdate\ + \" ] ] = 0) do={\r\ + \n/system/scheduler/add name=\"AM-ScriptInstallUpdate\" comment=\$Comment \ + start-time=startup interval=1d on-event=\$OnEvent;\r\ + \n}\r\ + \n\r\ + \n:onerror Err {\r\ + \n :global GlobalConfReady;\r\ + \n :retry { :if (\$GlobalConfReady != true) do={ :error (\"Global configs\ + \_not ready.\"); }; } delay=100ms max=5;\r\ + \n\r\ + \n:log warning (\"This script \$ScriptName Loaded \" . \\\r\ + \n [ \$IfThenElse (\$CommitId != \"unknown\") ([ \$CommitBrief ] . \" \ + \") ] . \\\r\ + \n \"on \" . \$devBoardName . \"\\nRouterOS: \" . \$devCurVersion . \"\ + \\nArchitecture: \" . \$devCurArch . \"\\nIdentity: \" . \$Identity);\r\ + \n # signal we are ready\r\ + \n:set GlobalFuncReady true;\r\ + \n:set ExitOK true;\r\ + \n\r\ + \n:log info (\$0 . \"Global configs ready!\");\r\ + \n} do={\r\ + \n :global ExitOnError; \$ExitOnError \$0 \$Err;\r\ + \n}\r\ + \n" +add dont-require-permissions=no name=fetchCustom owner=vados policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="#\ + \n\r\ + \n#/tool/fetch mode=https url=(\$GlobalScriptsUrl . \".bak/mikro_bak_rsa@1\ + 0.30.30.10.pub\") dst-path=\"/mikro_bak_rsa@10.30.30.10.pub\";\ + \n\r\ + \n\r\ + \n#\r\ + \n\ + \n#root.mnuc.backuper\ + \n\ + \n\r\ + \n\r\ + \n#/tool/fetch mode=https url=(\$GlobalScriptsUrl . \".bak/mikro_bak_rsa@1\ + 0.30.30.81.pub\") dst-path=\"/mikro_bak_rsa@10.30.30.81.pub\";\ + \n\r\ + \n\r\ + \n#:local Script AM-GlobalConfig;\r\ + \n\ + \n#/system/script/add name=\$Script owner=\$Script source=([ /tool/fetch c\ + heck-certificate=yes-without-crl url=(\$GlobalScriptsUrl . \$Script . \".r\ + sc\") output=user as-value ]->\"data\");\ + \n\r\ + \n#/usb1/mikro_bak_rsa.pub\r\ + \n\r\ + \n\ + \n#:local FetchFile AM-DOHCert.rsc\ + \n\ + \n\r\ + \n\r\ + \n:global GlobalScriptsUrl \"https://ros.vados.ru/tmp/\";\r\ + \n\ + \n:global MkDir;\ + \n\r\ + \n\ + \n:local FetchFile Manager@id_rsa.pub\ + \n:local localDir \"/\";\r\ + \n\r\ + \n\ + \n\ + \n\$MkDir \$localDir;\ + \n\ + \n\r\ + \n\r\ + \n/tool/fetch mode=https url=(\$GlobalScriptsUrl . \$FetchFile) dst-path=(\ + \$localDir . \$FetchFile);\ + \n" +add dont-require-permissions=yes name=MkTmpfs owner=vados policy=\ + ftp,read,write,policy,test,sniff,sensitive source=\ + ":global MkDir;\r\ + \n\r\ + \n\$MkDir /usb1/tmpfs;" +add dont-require-permissions=no name=CreateContainer owner=vados policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source=":\ + local contIF \"veth1\";\r\ + \n:local contIP \"10.10.10.10/24\";\r\ + \n:local contGW \"10.10.10.1\";\r\ + \n#/interface/veth/add name=\$contIF address=\$contIP gateway=\$contGW;\r\ + \n:local contBridgeIF \"Bridge-DOC\";\r\ + \n:local contBridgeIP \"10.10.10.1/24\";\r\ + \n/interface/bridge/add name=\$contBridgeIF;\r\ + \n/ip/address/add address=\$contBridgeIP interface=\$contBridgeIF;\r\ + \n/interface/bridge/port/add bridge=\$contBridgeIF interface=\$contIF;\r\ + \n/container/config/set ram-high=768M registry-url=https://registry-1.dock\ + er.io tmpdir=sata1/tmp\r\ + \n" +add dont-require-permissions=no name=carkeeperbot owner=vados policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="/\ + container/mounts/add list=carkeeper_configs/.profile src=sata1/carkeeper/c\ + onfigs/.profile dst=/root/.ashrc;\r\ + \n/container/mounts/add list=carkeeper_configs/php.ini src=sata1/carkeeper\ + /configs/php.ini dst=/etc/php82/php.ini;\r\ + \n#/container/mounts/add list=carkeeper_configs src=sata1/carkeeper/config\ + s dst=/configs;\r\ + \n/container/mounts/add list=carkeeper_app src=sata1/carkeeper/app dst=/ap\ + p;\r\ + \n/container/mounts/add list=carkeeper_scripts src=sata1/carkeeper/scripts\ + \_dst=/scripts;\r\ + \n/container/mounts/add list=carkeeper_configs/sshd_config src=sata1/carke\ + eper/configs/sshd_config dst=/etc/ssh/sshd_config;\r\ + \n\r\ + \n/container/mounts/add list=carkeeper_tmp src=sata1/carkeeper/tmp dst=/va\ + r/tmp;\r\ + \n/container/mounts/add list=carkeeper_sync src=sata1/carkeeper/sync dst=/\ + var/sync;\r\ + \n" +add comment="Enable or disable ip addresses based on bridge port state" \ + dont-require-permissions=no name=mod/AM-IPbridge owner=mod/AM-IPbridge \ + policy=ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \ + source="#!rsc by Vados\r\ + \n# RouterOS script: mod/AM-IPbridge\r\ + \n# Script comment: Enable or disable ip addresses based on bridge port st\ + ate\r\ + \n# Copyright (c) 2005-2026 Vados \r\ + \n#\r\ + \n#\r\ + \n# requires RouterOS, version=7.19\r\ + \n# requires device-mode, fetch\r\ + \n#\r\ + \n:local ScriptName [ :jobname ];\r\ + \n\r\ + \n# Add this Script scheduler\r\ + \n:if ([ :len [ /system/scheduler/find where name=\$ScriptName ] ] = 0) do\ + ={\r\ + \n:log warning (\"SystemScheduler for script: \" . \$ScriptName . \" is NO\ + T SET! Fix It Now!\");\r\ + \n:local OnEvent \"/system/script/run \$ScriptName;\";\r\ + \n:local Comment \"Every Day Check For Fake Bridges\";\r\ + \n/system/scheduler/add name=\$ScriptName comment=\$Comment start-time=sta\ + rtup interval=1d on-event=\$OnEvent;\r\ + \n}\r\ + \n\r\ + \n:foreach Bridge in=[ /interface/bridge/find ] do={\r\ + \n :local BrName [ /interface/bridge/get \$Bridge name ];\r\ + \n :if ([ :len [ /interface/bridge/port/find where bridge=\$BrName ] ] > \ + 0) do={\r\ + \n :if ([ :len [ /interface/bridge/port/find where bridge=\$BrName and \ + inactive=no ] ] = 0) do={\r\ + \n /ip/address/disable [ find where !dynamic interface=\$BrName ];\r\ + \n } else={\r\ + \n /ip/address/enable [ find where !dynamic interface=\$BrName ];\r\ + \n }\r\ + \n }\r\ + \n}\r\ + \n" +add comment="Use Telegram to chat with your Router and send commands" \ + dont-require-permissions=no name=mod/AM-TG-Bot owner=mod/AM-TG-Bot \ + policy=ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \ + source="#!rsc by Vados\r\ + \n# RouterOS script: mod/AM-TG-Bot\r\ + \n# Script comment: Use Telegram to chat with your Router and send command\ + s\r\ + \n# Copyright (c) 2007-2026 Vados \r\ + \n#\r\ + \n#\r\ + \n# requires RouterOS, version=7.19\r\ + \n# requires device-mode, fetch\r\ + \n:local ExitOK false;\r\ + \n:onerror Err {\r\ + \n :global GlobalConfReady; :global GlobalFuncReady;\r\ + \n :retry { :if (\$GlobalConfReady != true || \$GlobalFuncReady != true) \ + \\\r\ + \n do={ :error (\"Global config and/or functions not ready.\"); }; } \ + delay=500ms max=50;\r\ + \n :local ScriptName [ :jobname ];\r\ + \n\r\ + \n :global Identity;\r\ + \n :global TelegramChatActive;\r\ + \n :global TelegramChatGroups;\r\ + \n :global TelegramChatId;\r\ + \n :global TelegramChatIdsTrusted;\r\ + \n :global TelegramChatOffset;\r\ + \n :global TelegramChatRunTime;\r\ + \n :global TelegramMessageIDs;\r\ + \n :global TelegramRandomDelay;\r\ + \n :global TelegramTokenId;\r\ + \n\r\ + \n :global CertificateAvailable;\r\ + \n :global EitherOr;\r\ + \n :global EscapeForRegEx;\r\ + \n :global FileExists;\r\ + \n :global GetRandom20CharAlNum;\r\ + \n :global IfThenElse;\r\ + \n :global LogPrint;\r\ + \n :global LogPrintVerbose;\r\ + \n :global MAX;\r\ + \n :global MIN;\r\ + \n :global MkDir;\r\ + \n :global RandomDelay;\r\ + \n :global RmDir;\r\ + \n :global ScriptLock;\r\ + \n :global SendTelegram2;\r\ + \n :global SymbolForNotification;\r\ + \n :global ValidateSyntax;\r\ + \n :global WaitForFile;\r\ + \n :global WaitFullyConnected;\r\ + \n\r\ + \n :if ([ \$ScriptLock \$ScriptName ] = false) do={\r\ + \n :set ExitOK true;\r\ + \n :error false;\r\ + \n }\r\ + \n\r\ + \n \$WaitFullyConnected;\r\ + \n\r\ + \n :if ([ :len [ /system/scheduler/find where name=\$ScriptName ] ] = 0) \ + do={\r\ + \n /system/scheduler/add start-time=startup interval=30s name=\$ScriptN\ + ame on-event=\"/system/script/run \$ScriptName;\" comment=\"Telegram Chat \ + Bot\";\r\ + \n }\r\ + \n :if ([ :typeof \$TelegramChatOffset ] != \"array\") do={\r\ + \n :set TelegramChatOffset { 0; 0; 0 };\r\ + \n }\r\ + \n :if ([ :typeof \$TelegramRandomDelay ] != \"num\") do={\r\ + \n :set TelegramRandomDelay 0;\r\ + \n }\r\ + \n\r\ + \n :if ([ \$CertificateAvailable \"Go Daddy Root Certificate Authority - \ + G2\" \"fetch\" ] = false) do={\r\ + \n \$LogPrint warning \$ScriptName (\"Downloading required certificate \ + failed.\");\r\ + \n :set ExitOK true;\r\ + \n :error false;\r\ + \n }\r\ + \n\r\ + \n \$RandomDelay \$TelegramRandomDelay;\r\ + \n\r\ + \n :local Data false;\r\ + \n :for I from=1 to=4 do={\r\ + \n :if (\$Data = false) do={\r\ + \n :onerror Err {\r\ + \n :set Data ([ /tool/fetch check-certificate=yes-without-crl outpu\ + t=user \\\r\ + \n (\"https://api.telegram.org/bot\" . \$TelegramTokenId . \"/get\ + Updates\?offset=\" . \\\r\ + \n \$TelegramChatOffset->0 . \"&allowed_updates=%5B%22message%22%\ + 5D\") as-value ]->\"data\");\r\ + \n :set TelegramRandomDelay [ \$MAX 0 (\$TelegramRandomDelay - 1) ]\ + ;\r\ + \n } do={\r\ + \n :if (\$I < 4) do={\r\ + \n \$LogPrint debug \$ScriptName (\"Fetch failed, \" . \$I . \". \ + try: \" . \$Err);\r\ + \n :set TelegramRandomDelay [ \$MIN 15 (\$TelegramRandomDelay + 5\ + ) ];\r\ + \n :delay ((\$I * \$I) . \"s\");\r\ + \n }\r\ + \n }\r\ + \n }\r\ + \n }\r\ + \n\r\ + \n :if (\$Data = false) do={\r\ + \n \$LogPrint warning \$ScriptName (\"Failed getting updates.\");\r\ + \n :set ExitOK true;\r\ + \n :error false;\r\ + \n }\r\ + \n\r\ + \n :local JSON [ :deserialize from=json value=\$Data ];\r\ + \n :local UpdateID 0;\r\ + \n :local Uptime [ /system/resource/get uptime ];\r\ + \n :foreach Update in=(\$JSON->\"result\") do={\r\ + \n :set UpdateID (\$Update->\"update_id\");\r\ + \n \$LogPrintVerbose debug \$ScriptName (\"Update \" . \$UpdateID . \":\ + \_\" . [ :serialize to=json \$Update ]);\r\ + \n\r\ + \n :local Message (\$Update->\"message\");\r\ + \n :local IsAnyReply ([ :typeof (\$Message->\"reply_to_message\") ] = \ + \"array\");\r\ + \n :local IsMyReply (\$TelegramMessageIDs->[ :tostr (\$Message->\"reply\ + _to_message\"->\"message_id\") ]);\r\ + \n :if ((\$IsMyReply = 1 || \$TelegramChatOffset->0 > 0 || \$Uptime > 5\ + m) && \$UpdateID >= \$TelegramChatOffset->2) do={\r\ + \n :local Trusted false;\r\ + \n :local Chat (\$Message->\"chat\");\r\ + \n :local From (\$Message->\"from\");\r\ + \n :local Command (\$Message->\"text\");\r\ + \n :local ThreadId [ \$IfThenElse (\$Message->\"is_topic_message\") (\ + \$Message->\"message_thread_id\") \"\" ];\r\ + \n\r\ + \n :foreach IdsTrusted in=(\$TelegramChatId, \$TelegramChatIdsTrusted\ + ) do={\r\ + \n :if (\$From->\"id\" = \$IdsTrusted || \\\r\ + \n \$From->\"username\" = \$IdsTrusted || \\\r\ + \n \$Chat->\"id\" = \$IdsTrusted) do={\r\ + \n :set Trusted true;\r\ + \n }\r\ + \n }\r\ + \n\r\ + \n :if (\$Trusted = true) do={\r\ + \n :local Done false;\r\ + \n :if (\$Command = \"\?\") do={\r\ + \n \$LogPrint info \$ScriptName (\"Sending notice for update \" .\ + \_\$UpdateID . \".\");\r\ + \n \$SendTelegram2 ({ origin=\$ScriptName; chatid=(\$Chat->\"id\"\ + ); silent=true; \\\r\ + \n replyto=(\$Message->\"message_id\"); threadid=\$ThreadId; \\\ + \r\ + \n subject=([ \$SymbolForNotification \"speech-balloon\" ] . \"\ + Telegram Chat\"); \\\r\ + \n message=([ \$IfThenElse ([ :len (\$From->\"first_name\") ] >\ + \_0) (\"Hello \" . (\$From->\"first_name\") . \"!\\n\\n\") ] . \\\r\ + \n \"Online\" . [ \$IfThenElse \$TelegramChatActive \" (and a\ + ctive!)\" ] . \", awaiting your commands!\") });\r\ + \n :set Done true;\r\ + \n }\r\ + \n :if (\$Done = false && [ :pick \$Command 0 1 ] = \"!\") do={\r\ + \n :if (\$Command ~ (\"^! *(\" . [ \$EscapeForRegEx \$Identity ] \ + . \"|@\" . \$TelegramChatGroups . \")\\\$\")) do={\r\ + \n :set TelegramChatActive true;\r\ + \n } else={\r\ + \n :set TelegramChatActive false;\r\ + \n }\r\ + \n \$LogPrint info \$ScriptName (\"Now \" . [ \$IfThenElse \$Tele\ + gramChatActive \"active\" \"passive\" ] . \\\r\ + \n \" from update \" . \$UpdateID . \"!\");\r\ + \n :set Done true;\r\ + \n }\r\ + \n :if (\$Done = false && (\$IsMyReply = 1 || (\$IsAnyReply = false\ + \_&& \\\r\ + \n \$TelegramChatActive = true)) && [ :len \$Command ] > 0) do\ + ={\r\ + \n :if ([ \$ValidateSyntax \$Command ] = true) do={\r\ + \n :local State \"\";\r\ + \n :local File (\"tmpfs/telegram-chat/\" . [ \$GetRandom20CharA\ + lNum 6 ]);\r\ + \n :if ([ \$MkDir \"tmpfs/telegram-chat\" ] = false) do={\r\ + \n \$LogPrint error \$ScriptName (\"Failed creating directory\ + !\");\r\ + \n :set ExitOK true;\r\ + \n :error false;\r\ + \n }\r\ + \n \$LogPrint info \$ScriptName (\"Running command from update \ + \" . \$UpdateID . \": \" . \$Command);\r\ + \n :execute script=(\":do {\\n\" . \$Command . \"\\n} on-error=\ + { /file/add name=\\\"\" . \$File . \".failed\\\" };\" . \\\r\ + \n \"/file/add name=\\\"\" . \$File . \".done\\\"\") file=(\$\ + File . \"\\00\");\r\ + \n :if ([ \$WaitForFile (\$File . \".done\") [ \$EitherOr \$Tel\ + egramChatRunTime 20s ] ] = false) do={\r\ + \n :set State ([ \$SymbolForNotification \"warning-sign\" ] .\ + \_\"The command did not finish, still running in background.\\n\\n\");\r\ + \n }\r\ + \n :if ([ \$FileExists (\$File . \".failed\") ] = true) do={\r\ + \n :set State ([ \$SymbolForNotification \"cross-mark\" ] . \ + \"The command failed with an error!\\n\\n\");\r\ + \n }\r\ + \n :local Content ([ /file/read chunk-size=32768 file=\$File as\ + -value ]->\"data\");\r\ + \n \$SendTelegram2 ({ origin=\$ScriptName; chatid=(\$Chat->\"id\ + \"); silent=true; \\\r\ + \n replyto=(\$Message->\"message_id\"); threadid=\$ThreadId; \ + \\\r\ + \n subject=([ \$SymbolForNotification \"speech-balloon\" ] . \ + \"Telegram Chat\"); \\\r\ + \n message=([ \$SymbolForNotification \"gear\" ] . \"Command:\ + \\n\" . \$Command . \"\\n\\n\" . \\\r\ + \n \$State . [ \$IfThenElse ([ :len \$Content ] > 0) \\\r\ + \n ([ \$SymbolForNotification \"memo\" ] . \"Output:\\n\" .\ + \_\$Content) \\\r\ + \n ([ \$SymbolForNotification \"memo\" ] . \"No output.\") \ + ]) });\r\ + \n \$RmDir \"tmpfs/telegram-chat\";\r\ + \n } else={\r\ + \n \$LogPrint info \$ScriptName (\"The command from update \" .\ + \_\$UpdateID . \" failed syntax validation!\");\r\ + \n \$SendTelegram2 ({ origin=\$ScriptName; chatid=(\$Chat->\"id\ + \"); silent=false; \\\r\ + \n replyto=(\$Message->\"message_id\"); threadid=\$ThreadId; \ + \\\r\ + \n subject=([ \$SymbolForNotification \"speech-balloon\" ] . \ + \"Telegram Chat\"); \\\r\ + \n message=([ \$SymbolForNotification \"gear\" ] . \"Command:\ + \\n\" . \$Command . \"\\n\\n\" . \\\r\ + \n [ \$SymbolForNotification \"cross-mark\" ] . \"The comma\ + nd failed syntax validation!\") });\r\ + \n }\r\ + \n }\r\ + \n } else={\r\ + \n :local MessageText (\"Received a message from untrusted contact \ + \" . \\\r\ + \n [ \$IfThenElse ([ :len (\$From->\"username\") ] = 0) \"without\ + \_username\" (\"'\" . \$From->\"username\" . \"'\") ] . \\\r\ + \n \" (ID \" . \$From->\"id\" . \") in update \" . \$UpdateID . \ + \"!\");\r\ + \n :if (\$Command ~ (\"^! *\" . [ \$EscapeForRegEx \$Identity ] . \ + \"\\\$\")) do={\r\ + \n \$LogPrint warning \$ScriptName \$MessageText;\r\ + \n \$SendTelegram2 ({ origin=\$ScriptName; chatid=(\$Chat->\"id\"\ + ); silent=false; \\\r\ + \n replyto=(\$Message->\"message_id\"); threadid=\$ThreadId; \\\ + \r\ + \n subject=([ \$SymbolForNotification \"speech-balloon\" ] . \"\ + Telegram Chat\"); \\\r\ + \n message=(\"You are not trusted.\") });\r\ + \n } else={\r\ + \n \$LogPrint info \$ScriptName \$MessageText;\r\ + \n }\r\ + \n }\r\ + \n } else={\r\ + \n \$LogPrint debug \$ScriptName (\"Already handled update \" . \$Upd\ + ateID . \".\");\r\ + \n }\r\ + \n }\r\ + \n :set TelegramChatOffset ([ :pick \$TelegramChatOffset 1 3 ], \\\r\ + \n [ \$IfThenElse (\$UpdateID >= \$TelegramChatOffset->2) (\$UpdateID +\ + \_1) (\$TelegramChatOffset->2) ]);\r\ + \n} do={\r\ + \n :global ExitError; \$ExitError \$ExitOK [ :jobname ] \$Err;\r\ + \n}\r\ + \n" +add comment="Script send Notifications in Telegram" dont-require-permissions=\ + no name=mod/AM-TG-Notifications owner=mod/AM-TG-Notifications policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="#\ + !rsc by Vados\r\ + \n# RouterOS script: mod/AM-TG-Notifications\r\ + \n# Script comment: Script send Notifications in Telegram\r\ + \n# Copyright (c) 2007-2026 Vados \r\ + \n#\r\ + \n#\r\ + \n# requires RouterOS, version=7.19\r\ + \n# requires device-mode, fetch, scheduler\r\ + \n:global FlushTelegramQueue;\r\ + \n:global GetTelegramChatId;\r\ + \n:global NotificationFunctions;\r\ + \n:global PurgeTelegramQueue;\r\ + \n:global SendTelegram;\r\ + \n:global SendTelegram2;\r\ + \n# flush telegram queue\r\ + \n:set FlushTelegramQueue do={ :onerror Err {\r\ + \n :global TelegramQueue;\r\ + \n :global TelegramMessageIDs;\r\ + \n :global CertificateAvailable;\r\ + \n :global IsFullyConnected;\r\ + \n :global LogPrint;\r\ + \n :if ([ \$IsFullyConnected ] = false) do={\r\ + \n \$LogPrint debug \$0 (\"System is not fully connected, not flushing.\ + \");\r\ + \n :return false;\r\ + \n }\r\ + \n :if ([ \$CertificateAvailable \"Go Daddy Root Certificate Authority - \ + G2\" \"fetch\" ] = false) do={\r\ + \n \$LogPrint warning \$0 (\"Downloading required certificate failed.\"\ + );\r\ + \n :return false;\r\ + \n }\r\ + \n :local AllDone true;\r\ + \n :local QueueLen [ :len \$TelegramQueue ];\r\ + \n :if ([ :len [ /system/scheduler/find where name=\"_FlushTelegramQueue\ + \" ] ] > 0 && \$QueueLen = 0) do={\r\ + \n \$LogPrint warning \$0 (\"Flushing Telegram messages from scheduler,\ + \_but queue is empty.\");\r\ + \n }\r\ + \n :foreach Id,Message in=\$TelegramQueue do={\r\ + \n :if ([ :typeof \$Message ] = \"array\" ) do={\r\ + \n :onerror Err {\r\ + \n :local Data ([ /tool/fetch check-certificate=yes-without-crl out\ + put=user http-method=post \\\r\ + \n (\"https://api.telegram.org/bot\" . (\$Message->\"tokenid\") .\ + \_\"/sendMessage\") \\\r\ + \n http-data=(\$Message->\"http-data\") as-value ]->\"data\");\r\ + \n :set (\$TelegramQueue->\$Id);\r\ + \n :set (\$TelegramMessageIDs->[ :tostr ([ :deserialize from=json v\ + alue=\$Data ]->\"result\"->\"message_id\") ]) 1;\r\ + \n } do={\r\ + \n \$LogPrint debug \$0 (\"Sending queued Telegram message failed: \ + \" . \$Err);\r\ + \n :set AllDone false;\r\ + \n }\r\ + \n }\r\ + \n }\r\ + \n :if (\$AllDone = true && \$QueueLen = [ :len \$TelegramQueue ]) do={\r\ + \n /system/scheduler/remove [ find where name=\"_FlushTelegramQueue\" ]\ + ;\r\ + \n :set TelegramQueue;\r\ + \n }\r\ + \n} do={\r\ + \n :global ExitOnError; \$ExitOnError \$0 \$Err;\r\ + \n} }\r\ + \n# get the chat id\r\ + \n:set GetTelegramChatId do={ :onerror Err {\r\ + \n :global TelegramTokenId;\r\ + \n :global CertificateAvailable;\r\ + \n :global LogPrint;\r\ + \n :if ([ \$CertificateAvailable \"Go Daddy Root Certificate Authority - \ + G2\" \"fetch\" ] = false) do={\r\ + \n \$LogPrint warning \$0 (\"Downloading required certificate failed.\"\ + );\r\ + \n :return false;\r\ + \n }\r\ + \n :local Data;\r\ + \n :onerror Err {\r\ + \n :set Data ([ /tool/fetch check-certificate=yes-without-crl output=us\ + er \\\r\ + \n (\"https://api.telegram.org/bot\" . \$TelegramTokenId . \"/getUpd\ + ates\?offset=0\" . \\\r\ + \n \"&allowed_updates=%5B%22message%22%5D\") as-value ]->\"data\");\ + \r\ + \n } do={\r\ + \n \$LogPrint warning \$0 (\"Fetching data failed: \" . \$Err);\r\ + \n :return false;\r\ + \n }\r\ + \n :local JSON [ :deserialize from=json value=\$Data ];\r\ + \n :local Count [ :len (\$JSON->\"result\") ];\r\ + \n :if (\$Count = 0) do={\r\ + \n \$LogPrint info \$0 (\"No message received.\");\r\ + \n :return false;\r\ + \n }\r\ + \n :local Message (\$JSON->\"result\"->(\$Count - 1)->\"message\");\r\ + \n \$LogPrint info \$0 (\"The chat id is: \" . (\$Message->\"chat\"->\"id\ + \"));\r\ + \n :if ((\$Message->\"is_topic_message\") = true) do={\r\ + \n \$LogPrint info \$0 (\"The thread id is: \" . (\$Message->\"message_\ + thread_id\"));\r\ + \n }\r\ + \n} do={\r\ + \n :global ExitOnError; \$ExitOnError \$0 \$Err;\r\ + \n} }\r\ + \n# send notification via telegram - expects one array argument\r\ + \n:set (\$NotificationFunctions->\"telegram\") do={\r\ + \n :local Notification \$1;\r\ + \n :global Identity;\r\ + \n :global IdentityExtra;\r\ + \n :global TelegramChatId;\r\ + \n :global TelegramChatIdOverride;\r\ + \n :global TelegramMessageIDs;\r\ + \n :global TelegramQueue;\r\ + \n :global TelegramThreadId;\r\ + \n :global TelegramThreadIdOverride;\r\ + \n :global TelegramTokenId;\r\ + \n :global TelegramTokenIdOverride;\r\ + \n :global CertificateAvailable;\r\ + \n :global CharacterReplace;\r\ + \n :global EitherOr;\r\ + \n :global IfThenElse;\r\ + \n :global LogPrint;\r\ + \n :global ProtocolStrip;\r\ + \n :global SymbolForNotification;\r\ + \n :global UrlEncode;\r\ + \n :local EscapeMD do={\r\ + \n :local Text [ :tostr \$1 ];\r\ + \n :local Mode [ :tostr \$2 ];\r\ + \n :local Excl [ :tostr \$3 ];\r\ + \n :global CharacterReplace;\r\ + \n :global IfThenElse;\r\ + \n :local Chars {\r\ + \n \"body\"={ \"\\\\\"; \"`\" };\r\ + \n \"plain\"={ \"_\"; \"*\"; \"[\"; \"]\"; \"(\"; \")\"; \"~\"; \"`\"\ + ; \">\";\r\ + \n \"#\"; \"+\"; \"-\"; \"=\"; \"|\"; \"{\"; \"}\"; \".\"; \ + \"!\" };\r\ + \n }\r\ + \n :foreach Char in=(\$Chars->\$Mode) do={\r\ + \n :if ([ :typeof [ :find \$Excl \$Char ] ] = \"nil\") do={\r\ + \n :set Text [ \$CharacterReplace \$Text \$Char (\"\\\\\" . \$Char)\ + \_];\r\ + \n }\r\ + \n }\r\ + \n :if (\$Mode = \"body\") do={:return (\"```\\n\" . \$Text . \"\\n```\ + \")}\r\ + \n :return \$Text;\r\ + \n }\r\ + \n :local ChatId [ \$EitherOr (\$Notification->\"chatid\") \\\r\ + \n [ \$EitherOr (\$TelegramChatIdOverride->(\$Notification->\"origin\")\ + ) \$TelegramChatId ] ];\r\ + \n :local ThreadId [ \$EitherOr (\$Notification->\"threadid\") \\\r\ + \n [ \$EitherOr (\$TelegramThreadIdOverride->(\$Notification->\"origin\ + \")) \\\r\ + \n [ \$IfThenElse ([ :len (\$TelegramChatIdOverride->(\$Notification->\ + \"origin\")) ] = 0) \$TelegramThreadId ] ] ];\r\ + \n :local TokenId [ \$EitherOr (\$TelegramTokenIdOverride->(\$Notificatio\ + n->\"origin\")) \$TelegramTokenId ];\r\ + \n :if ([ :len \$TokenId ] = 0 || [ :len \$ChatId ] = 0) do={:return fals\ + e}\r\ + \n :if ([ :typeof \$TelegramMessageIDs ] = \"nothing\") do={:set Telegram\ + MessageIDs ({})}\r\ + \n :local Truncated false;\r\ + \n :local Text (\"*__\" . [ \$EscapeMD (\"[\" . \$IdentityExtra . \$Ident\ + ity . \"] \" . \\\r\ + \n (\$Notification->\"subject\")) \"plain\" ] . \"__*\\n\\n\");\r\ + \n :local LenSubject [ :len \$Text ];\r\ + \n :local LenMessage [ :len (\$Notification->\"message\") ];\r\ + \n :local LenLink ([ :len (\$Notification->\"link\") ] * 2);\r\ + \n :local LenSum (\$LenSubject + \$LenMessage + \$LenLink);\r\ + \n :if (\$LenSum > 3968) do={\r\ + \n :set Text (\$Text . [ \$EscapeMD ([ :pick (\$Notification->\"message\ + \") 0 (3840 - \$LenSubject - \$LenLink) ] . \"...\") \"body\" ]);\r\ + \n :set Truncated true;\r\ + \n } else={:set Text (\$Text . [ \$EscapeMD (\$Notification->\"message\")\ + \_\"body\" ])}\r\ + \n :if (\$LenLink > 0) do={\r\ + \n :set Text (\$Text . \"\\n\" . [ \$SymbolForNotification \"link\" ] .\ + \_\\\r\ + \n \"[\" . [ \$EscapeMD [ \$ProtocolStrip (\$Notification->\"link\") \ + ] \"plain\" ] . \"]\" . \\\r\ + \n \"(\" . [ \$EscapeMD (\$Notification->\"link\") \"plain\" ] . \")\ + \");\r\ + \n }\r\ + \n :if (\$Truncated = true) do={\r\ + \n :set Text (\$Text . \"\\n\" . [ \$SymbolForNotification \"scissors\"\ + \_] . \\\r\ + \n [ \$EscapeMD (\"The message was too long and has been truncated, c\ + ut off _\" . \\\r\ + \n ((\$LenSum - [ :len \$Text ]) * 100 / \$LenSum) . \"%_!\") \"plain\ + \" \"_\" ]);\r\ + \n }\r\ + \n :local HTTPData (\"chat_id=\" . \$ChatId . \"&disable_notification=\" \ + . (\$Notification->\"silent\") . \\\r\ + \n \"&reply_to_message_id=\" . (\$Notification->\"replyto\") . \"&mes\ + sage_thread_id=\" . \$ThreadId . \\\r\ + \n \"&disable_web_page_preview=true&parse_mode=MarkdownV2\");\r\ + \n :onerror Err {\r\ + \n :if ([ \$CertificateAvailable \"Go Daddy Root Certificate Authority \ + - G2\" \"fetch\" ] = false) do={\r\ + \n \$LogPrint warning \$0 (\"Downloading required certificate failed.\ + \");\r\ + \n :error false;\r\ + \n }\r\ + \n :local Data ([ /tool/fetch check-certificate=yes-without-crl output=\ + user http-method=post \\\r\ + \n (\"https://api.telegram.org/bot\" . \$TokenId . \"/sendMessage\") \ + \\\r\ + \n http-data=(\$HTTPData . \"&text=\" . [ \$UrlEncode \$Text ]) as-va\ + lue ]->\"data\");\r\ + \n :set (\$TelegramMessageIDs->[ :tostr ([ :deserialize from=json value\ + =\$Data ]->\"result\"->\"message_id\") ]) 1;\r\ + \n } do={\r\ + \n \$LogPrint info \$0 (\"Failed sending Telegram notification: \" . \$\ + Err . \" - Queuing...\");\r\ + \n :if ([ :typeof \$TelegramQueue ] = \"nothing\") do={:set TelegramQue\ + ue ({})}\r\ + \n :set Text (\$Text . \"\\n\" . [ \$SymbolForNotification \"alarm-cloc\ + k\" ] . \\\r\ + \n [ \$EscapeMD (\"This message was queued since _\" . [ /system/cloc\ + k/get date ] . \\\r\ + \n \" \" . [ /system/clock/get time ] . \"_ and may be obsolete.\") \ + \"plain\" \"_\" ]);\r\ + \n :set (\$TelegramQueue->[ :len \$TelegramQueue ]) { tokenid=\$TokenId\ + ;\r\ + \n http-data=(\$HTTPData . \"&text=\" . [ \$UrlEncode \$Text ]) };\r\ + \n :if ([ :len [ /system/scheduler/find where name=\"_FlushTelegramQueu\ + e\" ] ] = 0) do={\r\ + \n /system/scheduler/add name=\"_FlushTelegramQueue\" interval=1m sta\ + rt-time=startup \\\r\ + \n on-event=(\":global FlushTelegramQueue; \\\$FlushTelegramQueue;\ + \");\r\ + \n }\r\ + \n }\r\ + \n}\r\ + \n# purge the Telegram queue\r\ + \n:set PurgeTelegramQueue do={\r\ + \n :global TelegramQueue;\r\ + \n /system/scheduler/remove [ find where name=\"_FlushTelegramQueue\" ];\ + \r\ + \n :set TelegramQueue;\r\ + \n}\r\ + \n# send notification via telegram - expects at least two string arguments\ + \r\ + \n:set SendTelegram do={ :onerror Err {\r\ + \n :global SendTelegram2;\r\ + \n \$SendTelegram2 ({ origin=\$0; subject=\$1; message=\$2; link=\$3; sil\ + ent=\$4 });\r\ + \n} do={\r\ + \n :global ExitOnError; \$ExitOnError \$0 \$Err;\r\ + \n} }\r\ + \n# send notification via telegram - expects one array argument\r\ + \n:set SendTelegram2 do={\r\ + \n :local Notification \$1;\r\ + \n :global NotificationFunctions;\r\ + \n (\$NotificationFunctions->\"telegram\") (\"\\\$NotificationFunctions->\ + \\\"telegram\\\"\") \$Notification;\r\ + \n}\r\ + \n" +add dont-require-permissions=no name=testIfThenElseAndEitherOr owner=vados \ + policy=ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \ + source=":local ExitOK false;\r\ + \n:local ScriptName [ :jobname ];\r\ + \n\r\ + \n:local ExitError;\r\ + \n#:local IfThenElse;\r\ + \n#:local EitherOr;\r\ + \n\r\ + \n:local one 11;\r\ + \n:local two 8;\r\ + \n#:log warning ([ \$IfThenElse (\$one != 0) \$one \$two ]);\r\ + \n:local Test; # [ :totime \"22s\" ];\r\ + \n\r\ + \n:local IfThenElse do={:if ([ :tostr \$1 ] = \"true\" || [ :tobool \$1 ] \ + = true) do={:return \$2}; :return \$3;}\r\ + \n:local EitherOr do={\r\ + \n :if ([ :typeof \$1 ] = \"num\") do={:if ((\$1 != 0) = true) do={:retur\ + n \$1} else={:return \$2};}\r\ + \n :if ([ :typeof \$1 ] = \"time\") do={:if ((\$1 > 0s) = true) do={:retu\ + rn \$1} else={:return \$2};}\r\ + \n :if ([ :len [ :tostr \$1 ] ] > 0) do={:return \$1} else={:return \$2};\ + \r\ + \n}\r\ + \n\r\ + \n:log warning ([ \$IfThenElse (\$one != 0) \$one \$two ]);\r\ + \n\r\ + \n:set Test [ \$EitherOr \$Test \"\" ];\r\ + \n:log warning (\"\\\$ Test = \" . \$Test);\r\ + \n\r\ + \n:set ExitError do={\r\ + \n :local ExitOK [ :tostr \$1 ];\r\ + \n :local Name [ :tostr \$2 ];\r\ + \n :local Error [ :tostr \$3 ];\r\ + \n :local IfThenElse;\r\ + \n :if (\$ExitOK = \"false\") do={\r\ + \n :log error (\$Name [ \$IfThenElse ([ :pick \$Name 0 1 ] = \"\\\$\") \ + \\\r\ + \n \"Function\" \"Script\" ] . \" '\" . \$Name . \"' exited with er\ + ror\" . \\\r\ + \n [ \$IfThenElse (!(\$Error ~ \"^(|true|false)\\\$\")) (\": \" . \ + \$Error) \".\" ]);\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n\r\ + \n#:log warning ([ \$IfThenElse (\$one != 0) \$one \$two ]);\r\ + \n\r\ + \n:set ExitOK true;\r\ + \n\$ExitError \$ExitOK \$ScriptName \"Err test custom\";" +add comment="Update and Backup send notification to EMail" \ + dont-require-permissions=no name=AM-Backup-UpdateEmail owner=\ + AM-Backup-UpdateEmail policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="#\ + !rsc by Vados\r\ + \n# RouterOS script: AM-Backup-UpdateEmail\r\ + \n# Script comment: Update and Backup send notification to EMail\r\ + \n# Copyright (c) 2007-2026 Vados \r\ + \n#\r\ + \n#\r\ + \n# requires RouterOS, version>=6.43.7\r\ + \n# requires device-mode, fetch\r\ + \n#\r\ + \n# Updated: 15/04/2025\r\ + \n# Website: https://github.com/beeyev\r\ + \n# Notification e-mail (Make sure you have configured Email settings in T\ + ools -> Email)\r\ + \n:local ExitOK false;\r\ + \n:onerror Err {\r\ + \n :global GlobalConfReady; :global GlobalFuncReady;\r\ + \n :retry { :if (\$GlobalConfReady != true || \$GlobalFuncReady != true) \ + \\\r\ + \n do={ :error (\"Global config and/or functions not ready.\"); }; } \ + delay=500ms max=50;\r\ + \n :local ScriptName [ :jobname ];\r\ + \n\r\ + \n:local IfThenElse do={:if ([ :tostr \$1 ] = \"true\" || [ :tobool \$1 ] \ + = true) do={:return \$2}; :return \$3;}\r\ + \n:local EitherOr do={\r\ + \n :if ([ :typeof \$1 ] = \"num\") do={:if ((\$1 != 0) = true) do={:retur\ + n \$1} else={:return \$2};}\r\ + \n :if ([ :typeof \$1 ] = \"time\") do={:if ((\$1 > 0s) = true) do={:retu\ + rn \$1} else={:return \$2};}\r\ + \n :if ([ :len [ :tostr \$1 ] ] > 0) do={:return \$1} else={:return \$2};\ + \r\ + \n}\r\ + \n:local ExitError do={\r\ + \n :local ExitOK [ :tostr \$1 ];\r\ + \n :local Name [ :tostr \$2 ];\r\ + \n :local Error [ :tostr \$3 ];\r\ + \n :if (\$ExitOK = \"false\") do={:log error (\$Name [ \$IfThenElse ([ :p\ + ick \$Name 0 1 ] = \"\\\$\") \\\r\ + \n \"Function\" \"Script\" ] . \" '\" . \$Name . \"' exited with er\ + ror\" . \\\r\ + \n [ \$IfThenElse (!(\$Error ~ \"^(|true|false)\\\$\")) (\": \" . \ + \$Error) \".\" ]);}\r\ + \n}\r\ + \n\r\ + \n:global LogPrint;\r\ + \n:global EditSchedulers;\r\ + \n:global DevInfoIncludeIP;\r\ + \n\r\ + \n:local SchedInterval \"7d\";\r\ + \n:local SchedStartTime \"03:41:27\";\r\ + \n:local SchedComment \"Every 7 Day Check For Update and Backup send notif\ + ication to EMail.\";\r\ + \n\r\ + \n# Function to add or edit schedulers for scripts, to ensure they are set\ + \_up correctly\r\ + \n# How to use:\r\ + \n# Call this function with the following parameters:\r\ + \n# 1. \$1 - Scheduler name (usually same as script name) [*]\r\ + \n# 2. \$2 - Interval (e.g. \"1d\" (default is \"1d\")) [**]\r\ + \n# 3. \$3 - Start Time (e.g. \"00:00:00\", default is current time) [**]\ + \r\ + \n# 4. \$4 - Comment (optional, default: \"Scheduler for script: \")\ + \_[**]\r\ + \n# 5. \$5 - On Event command (e.g. \"/system/script { run ; }\" (de\ + fault is \"/system/script { run \$SchedName; }\")) [**]\r\ + \n# 6. \$6 - Policy (e.g. \"ftp,reboot,read,write,policy,test,password,sni\ + ff,sensitive,romon\" (default is the same)) [**]\r\ + \n# 7. \$7 - Start Date (e.g. \"jan/01/1970\", default is current date) [*\ + *]\r\ + \n# Notes:\r\ + \n# * - Scheduler name is required, and is used to identify the scheduler\ + \_to create or edit. It is usually the same as the script name (\$ScriptNa\ + me).\r\ + \n# ** - If optional parameters are not provided, default values will be u\ + sed.\r\ + \n# This allows for quick setup with minimal parameters, while still \ + providing flexibility for customization.\r\ + \n# Example usage:\r\ + \n# :if ([ \$EditSchedulers \"my-script\" ] = false) do={:set ExitOK false\ + ; :error true;}\r\ + \n# :if ([ \$EditSchedulers \"my-script\" \"1d\" \"03:41:27\" \"My custom \ + scheduler comment\" false false \"jan/01/1970\" ] = false) do={:set ExitOK\ + \_false; :error true;}\r\ + \n\r\ + \n:set EditSchedulers do={\r\ + \n :local SchedName [ :tostr \$1 ];\r\ + \n :if ([ :len \$SchedName ] = 0) do={ :return false; }\r\ + \n :local Interval [ :tostr \$2 ];\r\ + \n :if ([ :len \$Interval ] = 0) do={ :set Interval \"1d\"; }\r\ + \n :local StartTime [ :tostr \$3 ];\r\ + \n :if ([ :len \$StartTime ] = 0) do={ :set StartTime [/system/clock/get \ + time]; }\r\ + \n :local Comment [ :tostr \$4 ];\r\ + \n :if ([ :len \$Comment ] = 0) do={ :set Comment \"Scheduler for script:\ + \_\$SchedName\"; }\r\ + \n :local OnEvent [ :tostr \$5 ];\r\ + \n :if ([ :len \$OnEvent ] = 0) do={ :set OnEvent \"/system/script { run \ + \$SchedName; }\"; }\r\ + \n :local Policy [ :tostr \$6 ];\r\ + \n :if ([ :len \$Policy ] = 0) do={ :set Policy \"ftp,reboot,read,write,p\ + olicy,test,password,sniff,sensitive,romon\"; }\r\ + \n :local StartDate [ :tostr \$7 ];\r\ + \n :if ([ :len \$StartDate ] = 0) do={ :set StartDate [/system/clock/get \ + date]; }\r\ + \n\r\ + \n :onerror Err {\r\ + \n # Check if scheduler exists, if not - create it\r\ + \n :if ([ :len [ /system/scheduler/find where name=\$SchedName ] ] = 0)\ + \_do={\r\ + \n :log warning (\$SchedName . \"System Scheduler for script: \" . \$\ + SchedName . \" is NOT SET! Fix It Now!\");\r\ + \n /system/scheduler/add comment=\$Comment interval=\$Interval name=\ + \$SchedName on-event=\$OnEvent policy=\$Policy start-date=\$StartDate star\ + t-time=\$StartTime;\r\ + \n }\r\ + \n # Check if scheduler parameters are correct, if not - fix them\r\ + \n :if ([ /system/scheduler/get \$SchedName comment ] != \$Comment) do=\ + {\r\ + \n /system/scheduler/set \$SchedName comment=\$Comment;\r\ + \n }\r\ + \n :if ([ /system/scheduler/get \$SchedName interval ] != \$Interval) d\ + o={\r\ + \n /system/scheduler/set \$SchedName interval=\$Interval;\r\ + \n }\r\ + \n :if ([ /system/scheduler/get \$SchedName on-event ] != \$OnEvent) do\ + ={\r\ + \n /system/scheduler/set \$SchedName on-event=\$OnEvent;\r\ + \n }\r\ + \n :if ([ /system/scheduler/get \$SchedName policy ] != \$Policy) do={\ + \r\ + \n /system/scheduler/set \$SchedName policy=\$Policy;\r\ + \n }\r\ + \n :if ([ /system/scheduler/get \$SchedName start-date ] != \$StartDate\ + ) do={\r\ + \n /system/scheduler/set \$SchedName start-date=\$StartDate;\r\ + \n }\r\ + \n :if ([ /system/scheduler/get \$SchedName start-time ] != \$StartTime\ + ) do={\r\ + \n /system/scheduler/set \$SchedName start-time=\$StartTime;\r\ + \n }\r\ + \n } do={:log error (\$SchedName . \"Failed to add/modify scheduler for '\ + \" . \$SchedName . \"' failed: \" . \$Err); :return false};\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:if ([ \$EditSchedulers \$ScriptName \$SchedInterval \$SchedStartTime \$\ + SchedComment ] = false) do={\r\ + \n :log error (\$ScriptName . \"Failed to add/modify scheduler for \" . \ + \$ScriptName . \"!\");\r\ + \n :set ExitOK true;\r\ + \n :error true;\r\ + \n}\r\ + \n\r\ + \n# Local Function CheckOSUpdateMode\r\ + \n:local CheckOSUpdateMode;\r\ + \n:set CheckOSUpdateMode do={\r\ + \n :local NewMode [ :tostr \$1 ];\r\ + \n :if ([ :len \$NewMode ] = 0) do={\r\ + \n :log error (\$0 . \"/nFunction /\$CheckOSUpdateMode received empty v\ + alue!\");\r\ + \n :return false;\r\ + \n :error true;\r\ + \n }\r\ + \n\r\ + \n :global BackupAndUpdateMode;\r\ + \n :global OldMode [ :tostr \$BackupAndUpdateMode ];\r\ + \n :if (\$NewMode != \$BackupAndUpdateMode && \$NewMode = \"updateOnce\")\ + \_do={\r\ + \n :set BackupAndUpdateMode [ :tostr \$NewMode ];\r\ + \n :log info (\$0 . \"/nOk. /\$BackupAndUpdateMode set to: '\" . \$NewM\ + ode . \"'.\\nYou're a good boy! ))\\nI'm launching the update in 5 seconds\ + .\");\r\ + \n :delay 5;\r\ + \n /system/script { run \$ScriptName; };\r\ + \n } else={\r\ + \n :log warning (\$0 . \"/nFunction /\$CheckOSUpdateMode received value: \ + '\" . \$NewMode . \"', but is may be only \\\"updateOnce\\\"!\");\r\ + \n :return false;\r\ + \n }\r\ + \n :return true;\r\ + \n :error false;\r\ + \n}\r\ + \n\r\ + \n# :if ([ :len [ /system/scheduler/find where name=\"running-from-backup\ + -partition\" ] ] > 0) do={\r\ + \n# \$LogPrint warning \$ScriptName (\"Running from backup partition, r\ + efusing to act.\");\r\ + \n# :set ExitOK true;\r\ + \n# :error false;\r\ + \n# }\r\ + \n\r\ + \n:global EmailGeneralTo;\r\ + \n:global BackupAndUpdateMode;\r\ + \n:global forceBackup;\r\ + \n:global BackupPassword;\r\ + \n:global BackupSens;\r\ + \n:global updateChannel;\r\ + \n\r\ + \n:global DevInfoIncludeIP;\r\ + \n\r\ + \n# Installs patch updates only (BackupAndUpdateMode = \"osupdate\").\r\ + \n# Works for `stable` and `long-term` channels.\r\ + \n# Updates only if MAJOR.MINOR match (e.g. 6.43.2 > 6.43.6 allowed, 6.44.\ + 1 skipped).\r\ + \n# Sends info if a newer (non-patch) version is found.\r\ + \n:local installOnlyPatchUpdates false;\r\ + \n:local scriptVersion \"26.04.30\";\r\ + \n:local copyrightUrl \"https://www.github.com/vados-dev/\";\r\ + \n#Script messages prefix\r\ + \n:local SMP \"BkpAndUpdate:\";\r\ + \n\r\ + \n:local exitErrorMessage \"\$SMP script stopped due to an error. Please c\ + heck logs for more details.\";\r\ + \n:log info \"\\n\\n\$SMP Script \\\"Mikrotik RouterOS automatic backup & \ + update\\\" v.\$scriptVersion started.\";\r\ + \n:log info \"\$SMP Script Mode: `\$BackupAndUpdateMode`, Update channel: \ + `\$updateChannel`, Force backup: `\$forceBackup`, Install only patch updat\ + es: `\$installOnlyPatchUpdates`\";\r\ + \n\r\ + \n## vv FUNCTIONS vv ##\r\ + \n:global WaitCheckUpdates; \$WaitCheckUpdates;\r\ + \n:global MiniDateTimeStamp;\r\ + \n:local MDTSFix [\$MiniDateTimeStamp];\r\ + \n:global Identity;\r\ + \n:global IdentityShort;\r\ + \n:global devBoardName;\r\ + \n:global runningVersion;\r\ + \n:global runningChannel;\r\ + \n:global devModel;\r\ + \n:global devRbSerialNumber;\r\ + \n:global ROsVerAvail;\r\ + \n:global PkgUpdStatus;\r\ + \n\r\ + \n# Checks if two RouterOS version strings differ only by the patch versio\ + n\r\ + \n# :put [\$FuncIsPatchUpdateOnly \"6.2.1\" \"6.2.4\"] # Output: true\r\ + \n# :put [\$FuncIsPatchUpdateOnly \"6.2.1\" \"6.3.1\"] # Output: false\r\ + \n:local FuncIsPatchUpdateOnly do={\r\ + \n :local ver1 \$1;\r\ + \n :local ver2 \$2;\r\ + \n # Extract the major and minor components from a version\r\ + \n :local extractMajorMinor do={\r\ + \n :local ver \$1;\r\ + \n :local dot1 [:find \$ver \".\"];\r\ + \n :if (\$dot1 = -1) do={:return \$ver}\r\ + \n :local major [:pick \$ver 0 \$dot1];\r\ + \n :local rest [:pick \$ver (\$dot1 + 1) [:len \$ver]];\r\ + \n :local dot2 [:find \$rest \".\"];\r\ + \n :local minor \$rest;\r\ + \n :if (\$dot2 >= 0) do={:set minor [:pick \$rest 0 \$dot2]}\r\ + \n :return (\$major . \".\" . \$minor);\r\ + \n }\r\ + \n\r\ + \n# Compare the major and minor components of both version strings\r\ + \n:if ([\$extractMajorMinor \$ver1] = [\$extractMajorMinor \$ver2]) do={:r\ + eturn true}\r\ + \n :return false;\r\ + \n}\r\ + \n# Creates backups and returns array of names\r\ + \n# Possible arguments:\r\ + \n# \$1 - file name, without extension\r\ + \n# \$2 - password (optional)\r\ + \n# \$3 - sensitive data in config (optional, default: false)\r\ + \n# Example:\r\ + \n#:put [\$FuncCreateBackups \$backupName]\r\ + \n:local FuncCreateBackups do={\r\ + \n :local backupName [ :tostr \$1 ];\r\ + \n :local BackPass \$2;\r\ + \n :local BackSens \$3;\r\ + \n\r\ + \n #Script messages prefix\r\ + \n :local SMP \"BkpAndUpdate:\";\r\ + \n :local exitErrorMessage \"\$SMP script stopped due to an error. Please\ + \_check logs for more details.\";\r\ + \n :log info (\"\$SMP global function `FuncCreateBackups` started, input:\ + \_`\$backupName`\");\r\ + \n\r\ + \n # validate required parameter: backupName\r\ + \n :if ([:typeof \$backupName] != \"str\" or [:len \$backupName] = 0) do=\ + {\r\ + \n :log error \"\$SMP parameter 'backupName' is required and must be a \ + non-empty string\";\r\ + \n# :log warning \"\$SMP parameter '\\\$backupName' is required and mus\ + t be a non-empty string.\\nI set '\\\$backupName' to 'default-backup'\";\r\ + \n# :set \$backupName \"default-backup\";\r\ + \n :error \$exitErrorMessage;\r\ + \n }\r\ + \n\r\ + \n :local backupFileSys \"\$backupName.backup\";\r\ + \n :local backupFileConfig \"\$backupName.rsc\";\r\ + \n :local backupNames {\$backupFileSys;\$backupFileConfig};\r\ + \n\r\ + \n ## Perform system backup\r\ + \n :if ([:len \$BackPass] = 0) do={\r\ + \n :log info (\"\$SMP starting backup without password, backup name: `\ + \$backupName`\");\r\ + \n /system backup save dont-encrypt=yes name=\$backupName;\r\ + \n } else={\r\ + \n :log info (\"\$SMP starting backup with password, backup name: `\$ba\ + ckupName`\");\r\ + \n /system backup save password=\$BackPass name=\$backupName;\r\ + \n }\r\ + \n :log info (\"\$SMP system backup created: `\$backupFileSys`\");\r\ + \n ## Export config file\r\ + \n :if (\$BackSens = true) do={\r\ + \n :log info (\"\$SMP starting export config with sensitive data, backu\ + p name: `\$backupName`\");\r\ + \n # Since RouterOS v7 it needs to be explicitly set that we want to ex\ + port sensitive data\r\ + \n :if ([:pick [/system resource get version] 0 1] < 7) do={\r\ + \n :execute \"/export compact terse file=\$backupName\";\r\ + \n } else={\r\ + \n :execute \"/export compact show-sensitive terse file=\$backupName\ + \";\r\ + \n }\r\ + \n } else={\r\ + \n :log info (\"\$SMP starting export config without sensitive data, ba\ + ckup name: `\$backupName`\");\r\ + \n /export compact hide-sensitive terse file=\$backupName;\r\ + \n }\r\ + \n :log info (\"\$SMP Config export complete: `\$backupFileConfig`\");\r\ + \n :log info (\"\$SMP Waiting a little to ensure backup files are written\ + \");\r\ + \n :delay 20;\r\ + \n :if ([:len [/file find name=\$backupFileSys]] > 0) do={\r\ + \n :log info (\"\$SMP system backup file successfully saved to the file\ + \_system: `\$backupFileSys`\");\r\ + \n } else={\r\ + \n :log error (\"\$SMP system backup was not created, file does not exi\ + st: `\$backupFileSys`\");\r\ + \n :error \$exitErrorMessage;\r\ + \n }\r\ + \n :if ([:len [/file find name=\$backupFileConfig]] > 0) do={\r\ + \n :log info (\"\$SMP config backup file successfully saved to the file\ + \_system: `\$backupFileConfig`\");\r\ + \n } else={\r\ + \n :log error (\"\$SMP config backup was not created, file does not exi\ + st: `\$backupFileConfig`\");\r\ + \n :error \$exitErrorMessage;\r\ + \n }\r\ + \n :log info (\"\$SMP global function `FuncCreateBackups` finished. Creat\ + ed backups, system: `\$backupFileSys`, config: `\$backupFileConfig`\")\r\ + \n :return \$backupNames;\r\ + \n}\r\ + \n# Sends an email\r\ + \n# Parameters:\r\ + \n# \$1 - to (email address)\r\ + \n# \$2 - subject\r\ + \n# \$3 - body\r\ + \n# \$4 - file attachments (optional; pass \"\" if not needed)\r\ + \n#\r\ + \n# Example:\r\ + \n# \$FuncSendEmailSafe \"admin@domain.com\" \"Backup Done\" \"Backup comp\ + lete.\" \"backup1.backup\"\r\ + \n:local FuncSendEmailSafe do={\r\ + \n :global EmailGeneralTo;\r\ + \n :local emailSubject \$2;\r\ + \n :local emailBody \$3;\r\ + \n :local emailAttachments \$4;\r\ + \n :local SMP \"Bkp&Upd:\";\r\ + \n :local exitErrorMessage \"\$SMP script stopped due to an error. Please\ + \_check logs for more details.\";\r\ + \n :log info \"\$SMP Attempting to send email to `\$EmailGeneralTo`\";\r\ + \n # SAFETY: wait for any previously queued email to finish\r\ + \n :local waitTimeoutPre 60;\r\ + \n :local waitCounterPre 0;\r\ + \n :while (([/tool e-mail get last-status] = \"resolving-dns\" or [/tool \ + e-mail get last-status] = \"in-progress\")) do={\r\ + \n :if (\$waitCounterPre >= \$waitTimeoutPre) do={\r\ + \n :log error \"\$SMP Email send aborted: previous send did not compl\ + ete after \$waitTimeoutPre seconds\";\r\ + \n :error \$exitErrorMessage;\r\ + \n }\r\ + \n :log info \"\$SMP Waiting for previous email to finish (status: \$[/\ + tool e-mail get last-status])...\";\r\ + \n :delay 1; \r\ + \n :set waitCounterPre (\$waitCounterPre + 1);\r\ + \n }\r\ + \n # Send the email\r\ + \n :do {\r\ + \n /tool e-mail send to=\$EmailGeneralTo subject=\$emailSubject body=\$\ + emailBody file=\$emailAttachments;\r\ + \n } on-error={\r\ + \n :log error \"\$SMP Email send command failed to execute. Check logs \ + and verify email settings.\";\r\ + \n :error \$exitErrorMessage;\r\ + \n }\r\ + \n # Wait for send status to change from \"in-progress\" / \"resolving-dn\ + s\"\r\ + \n :local waitTimeout 60;\r\ + \n :local waitCounter 0;\r\ + \n :local emailStatus \"\";\r\ + \n :log info \"\$SMP Waiting for email to be sent, timeout in `\$waitTime\ + out` seconds...\";\r\ + \n :while (\$waitCounter < \$waitTimeout) do={\r\ + \n :set emailStatus [/tool e-mail get last-status];\r\ + \n :if (\$emailStatus != \"in-progress\" and \$emailStatus != \"resolvi\ + ng-dns\") do={\r\ + \n :log info \"\$SMP Email send status received: \$emailStatus\";\r\ + \n # exit loop\r\ + \n :set waitCounter \$waitTimeout;\r\ + \n } else={:delay 1; :set waitCounter (\$waitCounter + 1)}\r\ + \n }\r\ + \n # Final decision based on last status\r\ + \n :if (\$emailStatus = \"succeeded\") do={\r\ + \n :log info \"\$SMP Email successfully sent to `\$EmailGeneralTo`\";\ + \r\ + \n } else={\r\ + \n :log error \"\$SMP Email failed to send. Status: `\$emailStatus`. Ch\ + eck logs for more details and verify email settings.\";\r\ + \n :error \$exitErrorMessage;\r\ + \n }\r\ + \n}\r\ + \n# Global variable to track current update step\r\ + \n# They need to be initialized here first to be available in the script\r\ + \n:global buGlobalVarTargetOsVersion;\r\ + \n:global buGlobalVarScriptStep;\r\ + \n:local scriptStep \$buGlobalVarScriptStep;\r\ + \n:do {\r\ + \n /system/script/environment remove buGlobalVarScriptStep;\r\ + \n} on-error={}\r\ + \n:if ([:len \$scriptStep] = 0) do={\r\ + \n :set scriptStep 1;\r\ + \n}\r\ + \n## ^^ FUNCTIONS ^^ ##\r\ + \n#\r\ + \n# Initial validation\r\ + \n## Check email settings\r\ + \n:if ([:len \$EmailGeneralTo] < 3) do={\r\ + \n :log error (\"\$SMP Parameter `\\\$EmailGeneralTo` is not set, or cont\ + ains invalid value. Script stopped.\");\r\ + \n :error \$exitErrorMessage;\r\ + \n}\r\ + \n# Values will be defined later in the script\r\ + \n:local emailServer \"\";\r\ + \n:local emailFromAddress [/tool e-mail get from];\r\ + \n:log info \"\$SMP Validating email settings...\";\r\ + \n:do {\r\ + \n :set emailServer [/tool e-mail get server];\r\ + \n} on-error={\r\ + \n # This is a workaround for the RouterOS v7.12 and older versions\r\ + \n :set emailServer [/tool e-mail get address];\r\ + \n}\r\ + \n:if (\$emailServer = \"0.0.0.0\") do={\r\ + \n :log error (\"\$SMP Email server address is not correct: `\$emailServe\ + r`, check `Tools -> Email`. Script stopped.\");\r\ + \n :error \$exitErrorMessage;\r\ + \n}\r\ + \n:if ([:len \$emailFromAddress] < 3) do={\r\ + \n :log error (\"\$SMP Email configuration FROM address is not correct: `\ + \$emailFromAddress`, check `Tools -> Email`. Script stopped.\");\r\ + \n :error \$exitErrorMessage;\r\ + \n}\r\ + \n# Script mode validation\r\ + \n:if (\$BackupAndUpdateMode != \"backup\" and \$BackupAndUpdateMode != \"\ + osupdate\" and \$BackupAndUpdateMode != \"updateOnce\" and \$BackupAndUpda\ + teMode != \"osnotify\") do={\r\ + \n :log error (\"\$SMP Script parameter `\\\$BackupAndUpdateMode` is not \ + set, or contains invalid value: `\$BackupAndUpdateMode`. Script stopped.\"\ + );\r\ + \n :error \$exitErrorMessage;\r\ + \n}\r\ + \n# Update channel validation\r\ + \n:if (\$updateChannel != \"stable\" and \$updateChannel != \"long-term\" \ + and \$updateChannel != \"testing\" and \$updateChannel != \"development\")\ + \_do={\r\ + \n :log error (\"\$SMP Script parameter `\\\$updateChannel` is not set, o\ + r contains invalid value: `\$updateChannel`. Script stopped.\");\r\ + \n :error \$exitErrorMessage;\r\ + \n}\r\ + \n# Verify if script is set to install patch updates and if the update cha\ + nnel is valid\r\ + \n:if ((\$BackupAndUpdateMode = \"osupdate\" or \$BackupAndUpdateMode = \"\ + updateOnce\") and \$installOnlyPatchUpdates = true) do={\r\ + \n :if (\$updateChannel != \"stable\" and \$updateChannel != \"long-term\ + \") do={\r\ + \n :log error (\"\$SMP Patch-only updates enabled, but update channel `\ + \$updateChannel` is invalid. Only `stable` and `long-term` are supported. \ + Script stopped\");\r\ + \n :error \$exitErrorMessage;\r\ + \n }\r\ + \n \r\ + \n :if (\$runningChannel != \"stable\" and \$runningChannel != \"long-ter\ + m\") do={\r\ + \n :log error (\"\$SMP Script is set to install only patch updates, but\ + \_the installed RouterOS version is not from `stable` or `long-term` chann\ + el: `\$runningChannel`. Script stopped\");\r\ + \n :error \$exitErrorMessage;\r\ + \n }\r\ + \n}\r\ + \n#\r\ + \n\r\ + \n:local rawTime [/system clock get time];\r\ + \n:local rawDate [/system clock get date];\r\ + \n\r\ + \n:local deviceOsVerAndChannelRunning [/system/resource/get version];\r\ + \n\r\ + \n:local backupNameTemplate (\"backup_v\" . \$runningVersion . \"_\" .\ + \_\$runningChannel . \"_\" . \$MDTSFix);\r\ + \n:local backupNameBeforeUpdate (\$backupNameTemplate . \"_before_update\"\ + );\r\ + \n:local backupNameAfterUpdate (\$backupNameTemplate . \"_after_update\")\ + ;\r\ + \n\r\ + \n## Email body template\r\ + \n:local mailSubjectPrefix \"\$SMP Device - `\$IdentityShort`\";\r\ + \n:local mailBodyCopyright \"Mikrotik RouterOS automatic backup & update \ + (ver. \$scriptVersion) \\n\$copyrightUrl\";\r\ + \n:local changelogUrl \"Check RouterOS changelog: https://mikrotik.com\ + /download/changelogs/\";\r\ + \n:local mailBodyDeviceInfo \"\";\r\ + \n:set mailBodyDeviceInfo (\$mailBodyDeviceInfo . \"Device information:\")\ + ;\r\ + \n:set mailBodyDeviceInfo (\$mailBodyDeviceInfo . \"\\n-------------------\ + --\");\r\ + \n:set mailBodyDeviceInfo (\$mailBodyDeviceInfo . \"\\nName: \$Identity\")\ + ;\r\ + \n:set mailBodyDeviceInfo (\$mailBodyDeviceInfo . \"\\nModel: \$devModel\"\ + );\r\ + \n:set mailBodyDeviceInfo (\$mailBodyDeviceInfo . \"\\nBoard: \$devBoardNa\ + me\");\r\ + \n:set mailBodyDeviceInfo (\$mailBodyDeviceInfo . \"\\nSerial number: \$de\ + vRbSerialNumber\");\r\ + \n:set mailBodyDeviceInfo (\$mailBodyDeviceInfo . \"\\nRouterOS version: v\ + \$deviceOsVerAndChannelRunning\");\r\ + \n:set mailBodyDeviceInfo (\$mailBodyDeviceInfo . \"\\nBuild time: \$[/sys\ + tem/resource/get build-time]\");\r\ + \n:set mailBodyDeviceInfo (\$mailBodyDeviceInfo . \"\\nRouterboard FW Avai\ + lable: \$ROsVerAvail\");\r\ + \n:set mailBodyDeviceInfo (\$mailBodyDeviceInfo . \"\\nDevice date-time: \ + \$rawDate \$rawTime (\$[/system/clock/get time-zone-name ])\");\r\ + \n:set mailBodyDeviceInfo (\$mailBodyDeviceInfo . \"\\nUptime: \$[/system/\ + resource/get uptime]\");\r\ + \n## IP address detection\r\ + \n:if ([ \$DevInfoIncludeIP ] = true) do={\r\ + \n :global GetPubIp;\r\ + \n :local PublicIpAddress [ \$GetPubIp ];\r\ + \n :set mailBodyDeviceInfo (\$mailBodyDeviceInfo . \"\\nPublic IP address\ + : \" . \$PublicIpAddress . \".\");\r\ + \n :log info \"\$SMP Public IP address detected: \$PublicIpAddress\";\r\ + \n} \r\ + \n\r\ + \n:local mailAttachments [:toarray \"\"];\r\ + \n:if (\$scriptStep = 1 or \$scriptStep = 3) do={\r\ + \n :if (\$scriptStep = 3) do={\r\ + \n :log info (\"\$SMP Waiting for one minute before continuing to the f\ + inal step.\");\r\ + \n :delay 1m;\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n## STEP 1: Create backups, check for new RouterOS, and send email\r\ + \n## Steps 2\C3\A23 run only if auto-update is enabled and a new version i\ + s available\r\ + \n:if (\$scriptStep = 1) do={\r\ + \n :global BackupPassword;\r\ + \n :global BackupSens;\r\ + \n :local isNewOsUpdateAvailable false;\r\ + \n :local isLatestOsAlreadyInstalled true;\r\ + \n :local isOsNeedsToBeUpdated false;\r\ + \n :local isUpdateCheckSucceeded false;\r\ + \n :local isEmailNeedsToBeSent false;\r\ + \n :local mailSubjectPartAction \"\";\r\ + \n :local mailPtBodyAction \"\";\r\ + \n :local mailPtSubjectBackup \"\";\r\ + \n :local mailPtBodyBackup \"\";\r\ + \n # Checking for new version\r\ + \n :if (\$BackupAndUpdateMode = \"osupdate\" or \$BackupAndUpdateMode = \ + \"osnotify\" or \$BackupAndUpdateMode = \"updateOnce\") do={\r\ + \n :log info (\"\$SMP Setting update channel to `\$updateChannel`\");\r\ + \n /system/package/update/set channel=\$updateChannel;\r\ + \n :log info (\"\$SMP Checking for new RouterOS version. Current instal\ + led version is: `\$runningVersion`\");\r\ + \n \$LogPrint debug \$ScriptName (\"Checking for updates...\");\r\ + \n \r\ + \n# /system/package/update/check-for-updates without-paging as-value;\ + \r\ + \n# /system/package/update check-for-updates;\r\ + \n :delay 5s;\r\ + \n :set PkgUpdStatus [ /system/package/update/get status ];\r\ + \n :if (\$PkgUpdStatus = \"New version is available\") do={\r\ + \n :set ROsVerAvail [/system/package/update/get latest-version];\r\ + \n }\r\ + \n\r\ + \n :if (\$PkgUpdStatus = \"New version is available\") do={\r\ + \n :log info (\"\$SMP New RouterOS version is available: `\$ROsVerAva\ + il`\");\r\ + \n :set isNewOsUpdateAvailable true;\r\ + \n :set isLatestOsAlreadyInstalled false;\r\ + \n :set isUpdateCheckSucceeded true;\r\ + \n :set isEmailNeedsToBeSent true;\r\ + \n :set mailSubjectPartAction \"New RouterOS available\";\r\ + \n :set mailPtBodyAction \"New RouterOS version is available, curren\ + t version: v\$runningVersion, new version: v\$ROsVerAvail. \\n\$changelogU\ + rl\";\r\ + \n } else={\r\ + \n :if (\$PkgUpdStatus = \"System is already up to date\") do={\r\ + \n :log info (\"\$SMP No new RouterOS version is available, the lat\ + est version is already installed: `v\$runningVersion`\");\r\ + \n :set isUpdateCheckSucceeded true;\r\ + \n :set mailSubjectPartAction \"No os update available\";\r\ + \n :set mailPtBodyAction \"No new RouterOS version is available, t\ + he latest version is already installed: `v\$runningVersion`\";\r\ + \n } else={\r\ + \n :log error (\"\$SMP Failed to check for new RouterOS version. Pa\ + ckage check status: `\$PkgUpdStatus`\");\r\ + \n :set isEmailNeedsToBeSent true;\r\ + \n :set mailSubjectPartAction \"Error unable to check new os versio\ + n\";\r\ + \n :set mailPtBodyAction \"An error occurred while checking for a \ + new RouterOS version.\\nStatus returned: `\$PkgUpdStatus`\\n\\nPlease revi\ + ew the logs on the device for more details and verify internet connectivit\ + y.\";\r\ + \n }\r\ + \n }\r\ + \n }\r\ + \n # Checking if the script needs to install new os version\r\ + \n :if ((\$BackupAndUpdateMode = \"osupdate\" or \$BackupAndUpdateMode = \ + \"updateOnce\") and \$isNewOsUpdateAvailable = true) do={\r\ + \n :if (\$installOnlyPatchUpdates = true) do={\r\ + \n :if ([\$FuncIsPatchUpdateOnly \$runningVersion \$ROsVerAvail] = tr\ + ue) do={\r\ + \n :log info \"\$SMP New RouterOS version is available, and it is a\ + \_patch update. Current version: v\$runningVersion, new version: v\$ROsVer\ + Avail\";\r\ + \n :set isOsNeedsToBeUpdated true;\r\ + \n } else={\r\ + \n :log info \"\$SMP The script will not install this update, becau\ + se it is not a patch update. Current version: v\$runningVersion, new versi\ + on: v\$ROsVerAvail\";\r\ + \n :set mailPtBodyAction (\$mailPtBodyAction . \"\\nThis update wil\ + l not be installed, because the script is set to install only patch update\ + s.\");\r\ + \n }\r\ + \n } else={\r\ + \n :set isOsNeedsToBeUpdated true;\r\ + \n }\r\ + \n }\r\ + \n # Checking If the script needs to create a backup\r\ + \n :if (\$forceBackup = true or \$BackupAndUpdateMode = \"backup\" or \$i\ + sOsNeedsToBeUpdated = true) do={\r\ + \n :log info (\"\$SMP Starting backup process.\");\r\ + \n :set isEmailNeedsToBeSent true;\r\ + \n :local backupName \$backupNameTemplate;\r\ + \n # This means it's the first step where we create a backup before the\ + \_update process\r\ + \n :if (\$isOsNeedsToBeUpdated = true) do={\r\ + \n :set backupName \$backupNameBeforeUpdate;\r\ + \n #Email body if the purpose of the script is to update the device\r\ + \n :set mailSubjectPartAction \"Update preparation\";\r\ + \n :set mailPtBodyAction (\$mailPtBodyAction . \"\\nThe update proces\ + s for device '\$Identity' is scheduled to upgrade RouterOS from version v.\ + \$runningVersion to version v.\$ROsVerAvail (Update channel: \$updateChann\ + el)\");\r\ + \n :set mailPtBodyAction (\$mailPtBodyAction . \"\\nPlease note: The \ + update will proceed only after a successful backup.\");\r\ + \n :set mailPtBodyAction (\$mailPtBodyAction . \"\\nA final report wi\ + th detailed information will be sent once the update process is completed.\ + \");\r\ + \n :set mailPtBodyAction (\$mailPtBodyAction . \"\\nIf you do not rec\ + eive a second email within the next 10 minutes, there may be an issue. Ple\ + ase check your device logs for further information.\");\r\ + \n }\r\ + \n :do {\r\ + \n :set mailAttachments [\$FuncCreateBackups \$backupName \$BackupPas\ + sword \$BackupSens];\r\ + \n :set mailPtSubjectBackup \"Backup created\";\r\ + \n :set mailPtBodyBackup \"System backups have been successfully crea\ + ted and attached to this email.\";\r\ + \n } on-error={\r\ + \n :set isOsNeedsToBeUpdated false;\r\ + \n :set mailPtSubjectBackup \"Backup failed\";\r\ + \n :set mailPtBodyBackup \"The script failed to create backups. Pleas\ + e check device logs for more details.\";\r\ + \n :log warning \"\$SMP Backup creation failed. Update process will b\ + e canceled if automatic update is enabled\";\r\ + \n }\r\ + \n }\r\ + \n :if (\$isEmailNeedsToBeSent = true) do={\r\ + \n :log info \"\$SMP Preparing to send email...\";\r\ + \n :local mailStep1Subject \$mailSubjectPrefix;\r\ + \n :local mailStep1Body \"\";\r\ + \n # subject\r\ + \n :if (\$mailSubjectPartAction != \"\") do={:set mailStep1Subject (\$\ + mailStep1Subject . \" - \" . \$mailSubjectPartAction)}\r\ + \n :if (\$mailPtSubjectBackup != \"\") do={:set mailStep1Subject (\$ma\ + ilStep1Subject . \" - \" . \$mailPtSubjectBackup)}\r\ + \n # body\r\ + \n :if (\$mailPtBodyAction != \"\") do={:set mailStep1Body (\$mailStep1\ + Body . \$mailPtBodyAction . \"\\n\\n\")}\r\ + \n :if (\$mailPtBodyBackup != \"\") do={:set mailStep1Body (\$mailStep1\ + Body . \$mailPtBodyBackup . \"\\n\\n\")}\r\ + \n :set mailStep1Body (\$mailStep1Body . \$mailBodyDeviceInfo . \"\\n__\ + ________\\n\" . \$mailBodyCopyright);\r\ + \n :set mailStep1Body (\$mailStep1Body . \"\\n[*** TEST START ***]\\n\\\ + \$DeviceInfo: \" . [ \$DeviceInfo ] . \"\\n[*** TEST END ***]\\n\");\r\ + \n # Send email with backups\r\ + \n :do {\$FuncSendEmailSafe \$EmailGeneralTo \$mailStep1Subject \$mailS\ + tep1Body \$mailAttachments} on-error={\r\ + \n :set isOsNeedsToBeUpdated false;\r\ + \n :log error \"\$SMP The script will not proceed with the update pro\ + cess, because the email was not sent.\";\r\ + \n }\r\ + \n }\r\ + \n :if ([:len \$mailAttachments] > 0) do={\r\ + \n :log info \"\$SMP Cleaning up backup files from the file system...\"\ + ;\r\ + \n /file remove \$mailAttachments;\r\ + \n :delay 2s;\r\ + \n }\r\ + \n :if (\$isOsNeedsToBeUpdated = true) do={\r\ + \n :global OldMode;\r\ + \n :log info \"\$SMP everything is ready to install new RouterOS, going\ + \_to start the update process and reboot the device.\";\r\ + \n :do {\r\ + \n :local nextStep 2;\r\ + \n :if (\$isCloudHostedRouter = true) do={\r\ + \n :log info \"\$SMP The device is a cloud hosted router, the secon\ + d step updating the Routerboard firmware will be skipped.\";\r\ + \n :set nextStep 3;\r\ + \n }\r\ + \n :local scheduledCommand (\":delay 5s; /system/scheduler remove BKP\ + UPD-NEXT-BOOT-TASK; \\\r\ + \n :global buGlobalVarScriptStep \$nextStep; :global buGlobalVarTarg\ + etOsVersion \\\"\$ROsVerAvail\\\"; \\\r\ + \n :delay 10s; /system/script { run \$ScriptName; }\");\r\ + \n /system/scheduler add name=BKPUPD-NEXT-BOOT-TASK on-event=\$schedu\ + ledCommand start-time=startup interval=0;\r\ + \n /system/package/update install;\r\ + \n } on-error={\r\ + \n # Failed to install new os version, remove the task and variables\ + \r\ + \n :do {\r\ + \n /system/scheduler remove BKPUPD-NEXT-BOOT-TASK;\r\ + \n :set BackupAndUpdateMode \$OldMode;\r\ + \n :delay 2;\r\ + \n :do {\r\ + \n :global buGlobalVarTargetOsVersion (a);\r\ + \n } on-error={\r\ + \n :set \$mailSubjectPrefix (\$mailSubjectPrefix . \"ERROR Remove\ + \_global variable \\\$buGlobalVarTargetOsVersion failed!\");\r\ + \n }\r\ + \n# :if ([ :len \$OldMode ] > 0) do={\r\ + \n# :do {\r\ + \n# :global OldMode (a);\r\ + \n# } on-error={\r\ + \n# :set \$mailSubjectPrefix (\$mailSubjectPrefix . \" ERROR Re\ + move global variable \\\$OldMode failed!\");\r\ + \n# }\r\ + \n# }\r\ + \n } on-error={\r\ + \n :log error \"\$SMP Failed to install new RouterOS version. Pleas\ + e check device logs for more details. \\\r\ + \n \\nAnd Failed to remove task and variables! Check it!\";\r\ + \n :set \$mailSubjectPrefix (\$mailSubjectPrefix . \" ERROR Remove \ + task and global variables!\");\r\ + \n }\r\ + \n :log error \"\$SMP Failed to install new RouterOS version. Please \ + check device logs for more details.\";\r\ + \n :local mailUpdateErrorSubject (\$mailSubjectPrefix . \" - Update f\ + ailed\");\r\ + \n :local mailUpdateErrorBody \"The script was unable to install new \ + RouterOS version. Please check device logs for more details.\";\r\ + \n # Send email with error\r\ + \n \$FuncSendEmailSafe \$EmailGeneralTo \$mailUpdateErrorSubject \$ma\ + ilUpdateErrorBody \"\";\r\ + \n :error \$exitErrorMessage;\r\ + \n }\r\ + \n }\r\ + \n}\r\ + \n## STEP 2: (Post-reboot) Upgrade RouterBOARD firmware\r\ + \n## Runs only if auto-update is enabled and a new RouterOS version was fo\ + und\r\ + \n:if (\$scriptStep = 2) do={\r\ + \n :log info \"\$SMP The script is in the second step, updating Routerboa\ + rd firmware.\";\r\ + \n :log info \"\$SMP Upgrading routerboard firmware from v.\$deviceRbCurr\ + entFw to v.\$deviceRbUpgradeFw\";\r\ + \n /system routerboard upgrade;\r\ + \n :delay 2;\r\ + \n :log info \"\$SMP routerboard upgrade process was completed, going to \ + reboot in a moment!\";\r\ + \n ## Set task to send final report on the next boot\r\ + \n /system scheduler add name=BKPUPD-NEXT-BOOT-TASK on-event=\":delay 2; \ + :global buGlobalVarScriptStep 3; \\\r\ + \n :global buGlobalVarTargetOsVersion \\\"\$buGlobalVarTargetOsVersion\\\ + \"; :delay 10s; /system/script { run \$ScriptName; }; \\ \r\ + \n :delay 5s; /system/scheduler remove BKPUPD-NEXT-BOOT-TASK;\" start-ti\ + me=startup interval=0;\r\ + \n :delay 2;\r\ + \n /system reboot;\r\ + \n}\r\ + \n\r\ + \n## STEP 3: Final report (after second reboot, with delay).\r\ + \n## Runs only if auto-update is enabled and a new RouterOS version was fo\ + und.\r\ + \n:if (\$scriptStep = 3) do={\r\ + \n :log info (\"\$SMP The script is in the third step, sending final repo\ + rt.\");\r\ + \n :local targetOsVersion \$buGlobalVarTargetOsVersion;\r\ + \n :do {\r\ + \n# /system/script/environment remove [find name=\"buGlobalVarTargetOsV\ + ersion\"];\r\ + \n :global buGlobalVarTargetOsVersion (a);\r\ + \n } on-error={\r\ + \n :set \$mailSubjectPrefix (\$mailSubjectPrefix . \" ERROR Remove glob\ + al variable \\\$buGlobalVarTargetOsVersion failed!\");\r\ + \n }\r\ + \n\r\ + \n :if ([ :len \$OldMode ] != 0) do={\r\ + \n :do {\r\ + \n# /system/script/environment remove [find name=\"OldMode\"];\r\ + \n :global OldMode (a);\r\ + \n } on-error={\r\ + \n :set \$mailSubjectPrefix (\$mailSubjectPrefix . \" ERROR! Remove\ + \_global variable \\\$OldMode failed!\");\r\ + \n }\r\ + \n }\r\ + \n :if ([:len \$targetOsVersion] = 0) do={\r\ + \n :log warning \"\$SMP Something is wrong, the script was unable to ge\ + t the target updated OS version from the global variable.\";\r\ + \n }\r\ + \n :local mailStep3Subject \$mailSubjectPrefix;\r\ + \n :local mailStep3Body \"\";\r\ + \n :if (\$targetOsVersion = \$runningVersion) do={\r\ + \n :log info \"\$SMP Successfully verified new RouterOS version: target\ + : `\$targetOsVersion`, current: `\$runningVersion`\";\r\ + \n :set mailStep3Subject (\$mailStep3Subject . \" - Update completed - \ + Backup created\");\r\ + \n :set mailStep3Body (\$mailStep3Body . \"RouterOS and routerboard upg\ + rade process was completed\");\r\ + \n :set mailStep3Body (\$mailStep3Body . \"\\nNew RouterOS version: v.\ + \$targetOsVersion, routerboard firmware: v.\$deviceRbCurrentFw\");\r\ + \n :set mailStep3Body (\$mailStep3Body . \"\\n\$changelogUrl\\nBackups \ + of the upgraded system are in the attachment of this email.\\n\\n\$mailBod\ + yDeviceInfo\\n\\n\$mailBodyCopyright\");\r\ + \n :set mailAttachments [\$FuncCreateBackups \$backupNameAfterUpdate \$\ + BackupPassword \$BackupSens];\r\ + \n } else={\r\ + \n :log error \"\$SMP Failed to verify new RouterOS version: target: `\ + \$targetOsVersion`, current: `\$runningVersion`\";\r\ + \n :set mailStep3Subject (\$mailStep3Subject . \" - Update failed\");\r\ + \n :set mailStep3Body (\$mailStep3Body . \"The script was unable to ver\ + ify that the new RouterOS version was installed, target version: `\$target\ + OsVersion`, current version: `\$runningVersion`\\nCheck device logs for mo\ + re details.\\n\\n\$mailBodyDeviceInfo\\n\\n\$mailBodyCopyright\");\r\ + \n }\r\ + \n \$FuncSendEmailSafe \$EmailGeneralTo \$mailStep3Subject \$mailStep3Bod\ + y \$mailAttachments;\r\ + \n :if ([:len \$mailAttachments] > 0) do={\r\ + \n :log info \"\$SMP Cleaning up backup files from the file system...\"\ + ;\r\ + \n /file remove \$mailAttachments;\r\ + \n :delay 2;\r\ + \n }\r\ + \n :log info (\"\$SMP Final report email sent successfully, and the scrip\ + t has finished.\");\r\ + \n}\r\ + \n:log info (\"\$SMP the script has finished, script step: `\$scriptStep` \ + \\n\\n\");\r\ + \n\r\ + \n} do={:global ExitError; \$ExitError \$ExitOK [ :jobname ] \$Err}\r\ + \n" +add dont-require-permissions=no name=testGetPubIP owner=vados policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source=":\ + global DevInfoIncludeIP;\r\ + \n:global CharacterReplace;\r\ + \n:global EscapeForRegEx;\r\ + \n:global UrlEncode;\r\ + \n\r\ + \n:if ([ \$DevInfoIncludeIP ] = true) do={\r\ + \n :global GetPubIp;\r\ + \n :local Char (\"\\n\\r\");\r\ + \n :local PublicIpAddress [\$GetPubIp];\r\ + \n# :if ([ :pick \$PublicIpAddress ([ :len \$PublicIpAddress ] - 2) ] = \ + \"\\n\") do={:error true}; :set error falseon-error={:set PublicIpAddress \ + (\$PublicIpAddress . Char)};\r\ + \n#:if ([ :pick \$PublicIpAddress ([ :len \$PublicIpAddress ] - 1) ] = \"/\ + n\") do={\r\ + \n#:log warning \"N is = //n\"; \r\ + \n# :set PublicIpAddress [ \$CharacterReplace \$PublicIpAddress \$Char \"\ + \" ];\r\ + \n#}\r\ + \n# :set PublicIpAddress [ \$UrlEncode \$PublicIpAddress ];\r\ + \n#:local IpLen ([ :len \$PublicIpAddress ] -1);\r\ + \n#:set PublicIpAddress [:pick \$PublicIpAddress 0 \$IpLen];\r\ + \n :log warning \"Public IP address detected: \$PublicIpAddress , len = \ + \$IpLen\";\r\ + \n}\r\ + \n" +add comment="download, import and update firewall address-lists" \ + dont-require-permissions=no name=AM-FwAddrLists owner=AM-FwAddrLists \ + policy=ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \ + source="#!rsc by Vados\r\ + \n# RouterOS script: AM-FwAddrLists\r\ + \n# Script comment: download, import and update firewall address-lists\r\ + \n# Copyright (c) 2007-2026 Vados \r\ + \n#\r\ + \n#\r\ + \n# requires RouterOS, version=7.19\r\ + \n# https://ros.vados.ru/doc/fw-addr-lists.md\r\ + \n:local ExitOK false;\r\ + \n:onerror Err {\r\ + \n :global GlobalConfReady; :global GlobalFuncReady;\r\ + \n :retry { :if (\$GlobalConfReady != true || \$GlobalFuncReady != true) \ + \\\r\ + \n do={ :error (\"Global config and/or functions not ready.\"); }; } \ + delay=500ms max=50;\r\ + \n :local ScriptName [ :jobname ];\r\ + \n\r\ + \n :global GlobalScriptsUrl;\r\ + \n :local ListsDirName (\"fw-addr-lists.d/\" . [ /system identity get nam\ + e ] . \"/\");\r\ + \n :local ListsUrl (\$GlobalScriptsUrl . \$ListsDirName);\r\ + \n :local ListsIndex \"index.lst\";\r\ + \n :local AddrListsTimeOut 1d;\r\ + \n :local CheckCerts false;\r\ + \n\r\ + \n :global CertificateAvailable;\r\ + \n :global EitherOr;\r\ + \n :global FetchHugeFiles;\r\ + \n :global HumanReadableNum;\r\ + \n :global IfThenElse;\r\ + \n :global LogPrint;\r\ + \n :global NetMask4;\r\ + \n :global NetMask6;\r\ + \n :global ScriptLock;\r\ + \n \r\ + \n :local FindDelim do={\r\ + \n :local ValidChars \"0123456789.:/ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghi\ + jklmnopqrstuvwxyz-\";\r\ + \n :for I from=0 to=[ :len \$1 ] do={\r\ + \n :if ([ :typeof [ :find \$ValidChars [ :pick (\$1 . \" \") \$I ] ] \ + ] != \"num\") do={\r\ + \n :return \$I;\r\ + \n }\r\ + \n }\r\ + \n }\r\ + \n\r\ + \n :local GetBranch do={\r\ + \n :global EitherOr;\r\ + \n :return [ :pick [ :convert transform=md5 to=hex [ :pick \$1 0 [ \$Ei\ + therOr [ :find \$1 \"/\" ] [ :len \$1 ] ] ] ] 0 2 ];\r\ + \n }\r\ + \n \r\ + \n :if ([ \$ScriptLock \$ScriptName ] = false) do={\r\ + \n :set ExitOK true;\r\ + \n :error false;\r\ + \n }\r\ + \n\r\ + \n :local ListComment (\"managed by \" . \$ScriptName);\r\ + \n :local Url (\$ListsUrl . \$ListsIndex);\r\ + \n :local tmpIndex false;\r\ + \n\r\ + \n :for I from=1 to=5 do={\r\ + \n :if (\$tmpIndex = false) do={\r\ + \n :set tmpIndex [ :tolf [ \$FetchHugeFiles \$ScriptName \$Url \$Chec\ + kCerts ] ];\r\ + \n :if (\$tmpIndex = false && \$I < 5) do={\r\ + \n :log warning (\$ScriptName . \" Failed downloading address-list \ + index, \" . \$I . \". try from: \" . \$Url);\r\ + \n :delay ((\$I * \$I) . \"s\");\r\ + \n }\r\ + \n }\r\ + \n }\r\ + \n :if (\$tmpIndex = false) do={\r\ + \n :log warning (\$ScriptName . \" Failed downloading address-list inde\ + x from: \" . \$Url);\r\ + \n :set tmpIndex \"\";\r\ + \n }\r\ + \n\r\ + \n :foreach List in=[ :deserialize \$tmpIndex delimiter=\"\\n\" from=dsv \ + options=dsv.plain ] do={\r\ + \n# :set List (\$List->0);\r\ + \n :local ListName;\r\ + \n :local Delim [ \$FindDelim \$List ];\r\ + \n :if (\$Delim = 0) do={ :set List \"\"; };\r\ + \n :log warning (\$ScriptName . \" Parsing address-list index entry: \"\ + \_. \$List);\r\ + \n :if ([ :len \$List ] = 0) do={\r\ + \n :log error (\$ScriptName . \" Failed parsing address-list index en\ + try: \" . \$List);\r\ + \n } else={\r\ + \n :set ListName [ :pick \$List 0 \$Delim ];\r\ + \n }\r\ + \n :log warning (\$ScriptName . \" Parsing address-list index entry: \"\ + \_. \$ListName . \" - Ok.\");\r\ + \n :local ListUrl (\$ListsUrl . \$ListName);\r\ + \n :local CntAdd 0;\r\ + \n :local CntRenew 0;\r\ + \n :local CntRemove 0;\r\ + \n :local IPv4Addresses ({});\r\ + \n :local IPv6Addresses ({});\r\ + \n :local Failure false;\r\ + \n :local Data false;\r\ + \n :local TimeOut \$AddrListTimeOut;\r\ + \n\r\ + \n :for I from=1 to=5 do={\r\ + \n :if (\$Data = false) do={\r\ + \n :set Data [ :tolf [ \$FetchHugeFiles \$ScriptName \$ListUrl \$Ch\ + eckCerts ] ];\r\ + \n :if (\$Data = false && \$I < 5) do={\r\ + \n :log warning (\$ScriptName . \" Failed downloading for list '\ + \" . \$ListName . \"', \" . \$I . \". try from: \" . \$ListUrl);\r\ + \n :delay ((\$I * \$I) . \"s\");\r\ + \n }\r\ + \n }\r\ + \n }\r\ + \n\r\ + \n :if (\$Data = false) do={\r\ + \n :set Data \"\";\r\ + \n :set Failure true;\r\ + \n :log warning (\$ScriptName . \" Failed downloading for list '\" . \ + \$ListName . \"' from: \" . \$ListUrl);\r\ + \n } else={\r\ + \n :log warning (\$ScriptName . \" Downloaded \" . [ \$HumanReadableN\ + um [ :len \$Data ] 1024 ] . \"B for list '\" . \$ListName . \"' from: \" .\ + \_\$ListUrl);\r\ + \n }\r\ + \n\r\ + \n :foreach Line in=[ :deserialize \$Data delimiter=\"\\n\" from=dsv op\ + tions=dsv.plain ] do={\r\ + \n :set Line (\$Line->0);\r\ + \n :local Address;\r\ + \n :if ([ :pick \$Line 0 1 ] = \"{\" && [ :pick \$Line ([ :len \$Line\ + \_] - 1) ] = \"}\") do={\r\ + \n :do {\r\ + \n :set Address [ :tostr ([ :deserialize from=json \$Line ]->\"ci\ + dr\") ];\r\ + \n } on-error={ }\r\ + \n } else={\r\ + \n :set Address ([ :pick \$Line 0 [ \$FindDelim \$Line ] ] . (\$L\ + ist->\"cidr\"));\r\ + \n }\r\ + \n :do {\r\ + \n :local Branch;\r\ + \n :if (\$Address ~ \"^[0-9]{1,3}\\\\.[0-9]{1,3}\\\\.[0-9]{1,3}\\\\\ + .[0-9]{1,3}(/[0-9]{1,2})\?\\\$\") do={\r\ + \n :local Net \$Address;\r\ + \n :local CIDR 32;\r\ + \n :local Slash [ :find \$Address \"/\" ];\r\ + \n :if ([ :typeof \$Slash ] = \"num\") do={\r\ + \n :set Net [ :toip [ :pick \$Address 0 \$Slash ] ]\r\ + \n :set CIDR [ :pick \$Address (\$Slash + 1) [ :len \$Address ]\ + \_];\r\ + \n :set Address [ :tostr (([ :toip \$Net ] & [ \$NetMask4 \$CID\ + R ]) . [ \$IfThenElse (\$CIDR < 32) (\"/\" . \$CIDR) ]) ];\r\ + \n }\r\ + \n :set Branch [ \$GetBranch \$Address ];\r\ + \n :set (\$IPv4Addresses->\$Branch->\$Address) \$TimeOut;\r\ + \n }\r\ + \n :if (\$Address ~ \"^[0-9a-zA-Z]*:[0-9a-zA-Z:\\\\.]+(/[0-9]{1,3})\ + \?\\\$\") do={\r\ + \n :local Net \$Address;\r\ + \n :local CIDR 128;\r\ + \n :local Slash [ :find \$Address \"/\" ];\r\ + \n :if ([ :typeof \$Slash ] = \"num\") do={\r\ + \n :set Net [ :toip6 [ :pick \$Address 0 \$Slash ] ]\r\ + \n :set CIDR [ :pick \$Address (\$Slash + 1) [ :len \$Address ]\ + \_];\r\ + \n }\r\ + \n :set Address (([ :toip6 \$Net ] & [ \$NetMask6 \$CIDR ]) . \"/\ + \" . \$CIDR);\r\ + \n :set Branch [ \$GetBranch \$Address ];\r\ + \n :set (\$IPv6Addresses->\$Branch->\$Address) \$TimeOut;\r\ + \n }\r\ + \n :if (\$Address ~ \"^[\\\\.a-zA-Z0-9-]+\\\\.[a-zA-Z]{2,}\\\$\") d\ + o={\r\ + \n :set Branch [ \$GetBranch \$Address ];\r\ + \n :set (\$IPv4Addresses->\$Branch->\$Address) \$TimeOut;\r\ + \n :set (\$IPv6Addresses->\$Branch->\$Address) \$TimeOut;\r\ + \n }\r\ + \n } on-error={ }\r\ + \n }\r\ + \n \r\ + \n\r\ + \n :foreach Entry in=[ /ip/firewall/address-list/find where \\\r\ + \n list=\$ListName comment=\$ListComment ] do={\r\ + \n :local Address [ /ip/firewall/address-list/get \$Entry address ];\ + \r\ + \n :local Branch [ \$GetBranch \$Address ];\r\ + \n :local TimeOut (\$IPv4Addresses->\$Branch->\$Address);\r\ + \n :if ([ :typeof \$TimeOut ] = \"time\") do={\r\ + \n :log warning (\$ScriptName . \" Renewing IPv4 address \" . \$Add\ + ress . \\\r\ + \n \" in list '\" . \$ListName . \"' with \" . \$TimeOut . \".\ + \");\r\ + \n /ip/firewall/address-list/set \$Entry timeout=\$TimeOut;\r\ + \n :set (\$IPv4Addresses->\$Branch->\$Address);\r\ + \n :set CntRenew (\$CntRenew + 1);\r\ + \n } else={\r\ + \n :if (\$Failure = false) do={\r\ + \n :log warning (\$ScriptName . \" Removing IPv4 address \" . \$A\ + ddress . \\\r\ + \n \" from list '\" . \$ListName . \".\");\r\ + \n /ip/firewall/address-list/remove \$Entry;\r\ + \n :set CntRemove (\$CntRemove + 1);\r\ + \n }\r\ + \n }\r\ + \n }\r\ + \n\r\ + \n :foreach Entry in=[ /ipv6/firewall/address-list/find where \\\r\ + \n list=\$ListName comment=\$ListComment ] do={\r\ + \n :local Address [ /ipv6/firewall/address-list/get \$Entry address ]\ + ;\r\ + \n :local Branch [ \$GetBranch \$Address ];\r\ + \n :local TimeOut (\$IPv6Addresses->\$Branch->\$Address);\r\ + \n :if ([ :typeof \$TimeOut ] = \"time\") do={\r\ + \n :log warning (\$ScriptName . \" Renewing IPv6 address \" . \$Add\ + ress . \\\r\ + \n \" in list '\" . \$ListName . \"' with \" . \$TimeOut . \".\ + \");\r\ + \n /ipv6/firewall/address-list/set \$Entry timeout=\$TimeOut;\r\ + \n :set (\$IPv6Addresses->\$Branch->\$Address);\r\ + \n :set CntRenew (\$CntRenew + 1);\r\ + \n } else={\r\ + \n :if (\$Failure = false) do={\r\ + \n :log warning (\$ScriptName . \" Removing IPv6 address \" . \$A\ + ddress . \\\r\ + \n \" from list '\" . \$ListName . \".\");\r\ + \n /ipv6/firewall/address-list/remove \$Entry;\r\ + \n :set CntRemove (\$CntRemove + 1);\r\ + \n }\r\ + \n }\r\ + \n }\r\ + \n\r\ + \n :foreach BranchName,Branch in=\$IPv4Addresses do={\r\ + \n :log warning (\$ScriptName . \" Handling branch: \" . \$BranchName\ + );\r\ + \n :foreach Address,Timeout in=\$Branch do={\r\ + \n :log warning (\$ScriptName . \" Adding IPv4 address \" . \$Addre\ + ss . \\\r\ + \n \" to list '\" . \$ListName . \"' with \" . \$Timeout . \".\ + \");\r\ + \n :onerror Err {\r\ + \n /ip/firewall/address-list/add list=\$ListName comment=\$ListCo\ + mment \\\r\ + \n address=\$Address timeout=\$Timeout;\r\ + \n :set CntAdd (\$CntAdd + 1);\r\ + \n } do={\r\ + \n :log warning (\$ScriptName . \" Failed to add IPv4 address \" \ + . \$Address . \\\r\ + \n \" to list '\" . \$ListName . \"': \" . \$Err);\r\ + \n }\r\ + \n }\r\ + \n }\r\ + \n\r\ + \n :foreach BranchName,Branch in=\$IPv6Addresses do={\r\ + \n :log warning (\$ScriptName . \" Handling branch: \" . \$BranchName\ + );\r\ + \n :foreach Address,Timeout in=\$Branch do={\r\ + \n :log warning (\$ScriptName . \" Adding IPv6 address \" . \$Addre\ + ss . \\\r\ + \n \" to list '\" . \$ListName . \"' with \" . \$Timeout . \".\ + \");\r\ + \n :onerror Err {\r\ + \n /ipv6/firewall/address-list/add list=\$ListName comment=\$List\ + Comment \\\r\ + \n address=\$Address timeout=\$Timeout;\r\ + \n :set CntAdd (\$CntAdd + 1);\r\ + \n } do={\r\ + \n :log warning (\$ScriptName . \" Failed to add IPv6 address \" \ + . \$Address . \\\r\ + \n \" to list '\" . \$ListName . \"': \" . \$Err);\r\ + \n }\r\ + \n }\r\ + \n }\r\ + \n\r\ + \n :log info (\$ScriptName . \" list: \" . \$ListName . \\\r\ + \n \" (\" . [ \$HumanReadableNum (\$CntAdd + \$CntRenew) 1000 ] . \ + \")\" . \\\r\ + \n \" -- added: \" . [ \$HumanReadableNum \$CntAdd 1000 ] . \\\r\ + \n \" - renewed: \" . [ \$HumanReadableNum \$CntRenew 1000 ] . \\\r\ + \n \" - removed: \" . [ \$HumanReadableNum \$CntRemove 1000 ]);\r\ + \n }\r\ + \n} do={\r\ + \n :global ExitError; \$ExitError \$ExitOK [ :jobname ] \$Err;\r\ + \n}\r\ + \n" +add dont-require-permissions=no name=awg-env owner=vados policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="/\ + container/envs/add list=awg-proxy-env key=AWG_LISTEN value=\":55055\"\ + \n/container/envs/add list=awg-proxy-env key=AWG_REMOTE value=\"109.107.17\ + 7.79:55055\"\ + \n/container/envs/add list=awg-proxy-env key=AWG_JC value=\"9\"\ + \n/container/envs/add list=awg-proxy-env key=AWG_JMIN value=\"105\"\ + \n/container/envs/add list=awg-proxy-env key=AWG_JMAX value=\"920\"\ + \n/container/envs/add list=awg-proxy-env key=AWG_S1 value=\"90\"\ + \n/container/envs/add list=awg-proxy-env key=AWG_S2 value=\"136\"\ + \n/container/envs/add list=awg-proxy-env key=AWG_S3 value=\"45\"\ + \n/container/envs/add list=awg-proxy-env key=AWG_S4 value=\"27\"\ + \n/container/envs/add list=awg-proxy-env key=AWG_H1 value=\"472437748-4724\ + 38637\"\ + \n/container/envs/add list=awg-proxy-env key=AWG_H2 value=\"707808381-7078\ + 09250\"\ + \n/container/envs/add list=awg-proxy-env key=AWG_H3 value=\"1609078552-160\ + 9079495\"\ + \n/container/envs/add list=awg-proxy-env key=AWG_H4 value=\"1012329484-101\ + 2329741\"\ + \n/container/envs/add list=awg-proxy-env key=AWG_I1 value=\"\"\ + \n/container/envs/add list=awg-proxy-env key=AWG_I2 value=\"\"\ + \n/container/envs/add list=awg-proxy-env key=AWG_I3 value=\"\ + \"\ + \n/container/envs/add list=awg-proxy-env key=AWG_I4 value=\"\"\ + \n/container/envs/add list=awg-proxy-env key=AWG_I5 value=\"\"\ + \n/container/envs/add list=awg-proxy-env key=AWG_SERVER_PUB value=\"o5IgCl\ + 1/G6xbl6j7sPaP4ZNYREeYOmqZjKbm+v0b9jU=\"\ + \n/container/envs/add list=awg-proxy-env key=AWG_CLIENT_PUB value=[/interf\ + ace/wireguard/get [find name=wg-awg-proxy] public-key]" +add dont-require-permissions=no name=awg-container-add owner=vados policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="/\ + container/add file=sata1/tmp/awg-proxy-amd64.tar.gz interface=veth-awg-pro\ + xy envlist=awg-proxy-env \\\ + \n hostname=awg-proxy root-dir=sata1/awg-proxy logging=yes shm-size=4M \ + start-on-boot=yes\ + \n/container/start [find where tag~\"awg-proxy\"]" +add dont-require-permissions=yes name=awg2-webui owner=vados policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source=":\ + put \"Download image, and save him to usb1/tmp\"\ + \n/tool/fetch url=\"https://ros.vados.ru/docker/awg2-webui-chr.tar\" dst-p\ + ath=sata1/tmp/awg2-webui.tar\ + \n\ + \n:put \"1. Create veth interface\"\ + \n/interface veth add name=veth-awg address=10.9.9.2/24 gateway=10.9.9.1\ + \n\ + \n:put \"2. Create bridge and add veth\"\ + \n/interface bridge add name=br-awg\ + \n/interface bridge port add bridge=br-awg interface=veth-awg\ + \n\ + \n:put \"3. Assign IP to bridge\"\ + \n/ip address add address=10.9.9.1/24 interface=br-awg\ + \n\ + \n:put \"4. NAT: masquerade container traffic\"\ + \n/ip firewall nat add chain=srcnat src-address=10.9.9.0/24 action=masquer\ + ade\ + \n\ + \n:put \"5. Port forward: UDP 443 \? container (AmneziaWG QUIC mimicry)\"\ + \n/ip firewall nat add chain=dstnat protocol=udp dst-port=443 action=dst-n\ + at to-addresses=10.9.9.2 to-ports=55055 in-interface=ether1\ + \n\ + \n:put \"6. Port forward: HTTPS 9443 \? container Web UI\"\ + \n/ip firewall nat add chain=dstnat protocol=tcp dst-port=9443 action=dst-\ + nat to-addresses=10.9.9.2 to-ports=443 in-interface=ether1\ + \n\ + \n:put \"Create environment list..\"\ + \n/container envs add list=awgui-env key=WEB_USER value=vados\ + \n/container envs add list=awgui-env key=WEB_PASS value=zoomzoomrulezz\ + \n/container envs add list=awgui-env key=SECRET_KEY value=RanDomSecret32Sy\ + mboLs2325272931q\ + \n/container envs add list=awgui-env key=AWG_ENDPOINT value=10.30.30.10:44\ + 3\ + \n\ + \n:put \"Load and start container...\"\ + \n/container add file=sata1/tmp/awg2-webui.tar interface=veth-awg hostname\ + =awg2-webui \\\ + \nroot-dir=sata1/awg2-webui layer-dir=sata1/docker/awg2-webu/ envlist=awgu\ + i-env start-on-boot=yes logging=yes shm-size=4M\ + \n\ + \n:put \"Start...\"\ + \n/container start 0\ + \n\ + \n:put \"Verify...\"\ + \n/container print\ + \n\ + \n:put \"Should show: status=running\"\ + \n\ + \n/log print where topics~\"container\"\ + \n\ + \n:put \"Should show: AWG started, nginx running\"\ + \n" +add dont-require-permissions=no name=awg-proxy-go-Client owner=vados policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="\ + \n:put \"[::::::::::::::: START :::::::::::::::]\"\ + \n:put \"\"\ + \n:put \"Add ram disk tmpfs\"\ + \n#/disk add type=tmpfs tmpfs-max-size=100M slot=ram\ + \n\ + \n:put \"veth and network:\"\ + \n /interface/bridge add name=docker port-cost-mode=short\ + \n /ip/address/add address=192.168.100.1/24 interface=docker\ + \n\ + \n /interface/veth add address=192.168.100.3/24 gateway=192.168.100.1 name\ + =awg-proxy-go-client\ + \n /interface/bridge/port add bridge=docker interface=awg-proxy-go-client\ + \n\ + \n:put \"add config-client.json\"\ + \n/file/add name=sata1/mounts/awg-proxy-go/config-client.json\ + \n/container/mounts/add src=sata1/mounts/awg-proxy-go/config-client.json d\ + st=/app/config.json list=awgcfg-client\ + \n\ + \n#:put \"Download image, and save him to sata1/tmp/awgproxy-0.0.1-0034-am\ + d64.tar.gz\"\ + \n#/tool/fetch url=\"https://ros.vados.ru/amneziawg/img/awgproxy-0.0.1-003\ + 4-amd64.tar.gz\" dst-#path=\"tmp/awgproxy-0.0.1-0034-amd64.tar.gz\"\ + \n\ + \n:put \"Add container\"\ + \n/container/add file=\"usb1/dst/awgproxy-0.0.1-0036-amd64.tar\" interface\ + =awg-proxy-go-client hostname=awg-proxy-go-client root-dir=sata1/awg-proxy\ + -client logging=yes start-on-boot=yes workdir=sata1/docker/ name=\"awg-pro\ + xy-go-client\" mountlists=awgcfg-client\ + \n\ + \n:put \"Start container\"\ + \n#/container/start awg-proxy-go-client\ + \n\ + \n:put \"Add user:\"\ + \n /user/add address=192.168.100.2 group=write name=awg-proxy-go password=\ + peskar55\ + \n\ + \n:put \"Add forwarding port web-ui\"\ + \n /ip firewall nat add action=dst-nat chain=dstnat dst-port=8089 in-inter\ + face=ether1 protocol=tcp to-addresses=192.168.100.3 to-port=8088\ + \n\ + \n:put \"Add native WireGuard-server wg-awg, add address.\"\ + \n /interface/wireguard/add name=wg-awg-client\ + \n /ip/address/add address=172.17.0.1/24 interface=wg-awg-client\ + \n\ + \n:put \"\"\ + \n:put \"[::::::::::::::: END :::::::::::::::]\"\ + \n" +add comment=awg2wg dont-require-permissions=no name=awg2wg-uninstall owner=\ + Manager policy=ftp,read,write,policy source="\r\ + \n :put \"Uninstalling AmneziaWG...\"\r\ + \n :log info \"Uninstalling AmneziaWG...\"\r\ + \n /ip/route/remove [find where comment=awg2wg-tunnel]\r\ + \n :do { /tool/netwatch/remove [find where comment=Monitor-AWG2WG] } on-e\ + rror={}\r\ + \n :do { /ip/route/remove [find where comment=Monitor-AWG2WG] } on-error=\ + {}\r\ + \n /container/stop [find where interface=veth-awg2wg]\r\ + \n :delay 7s\r\ + \n /interface/list/member/remove [find where interface=wg-awg2wg]\r\ + \n /container/remove [find where interface=veth-awg2wg]\r\ + \n /container/envs/remove [find where list=\"awg2wg-env\"]\r\ + \n /ip/firewall/nat/remove [find where out-interface=\"wg-awg2wg\"]\r\ + \n /ip/address/remove [find where address=\"172.20.0.1/30\"]\r\ + \n /interface/veth/remove [find where name=\"veth-awg2wg\"]\r\ + \n /interface/wireguard/peers/remove [find where interface=\"wg-awg2wg\"]\ + \r\ + \n /ip/address/remove [find where interface=\"wg-awg2wg\"]\r\ + \n /interface/wireguard/remove [find where name=\"wg-awg2wg\"]\r\ + \n :do { /file/remove [find where name~\"awg2wg.+tar\"] } on-error={}\r\ + \n :do { /file/remove [find where name=\"disk1/awg2wg\"] } on-error={}\r\ + \n :put \"Uninstall AmneziaWG Proxy complete!\"\r\ + \n :log info \"Uninstall AmneziaWG Proxy complete!\"\r\ + \n /system/script/remove [find where name=awg2wg-uninstall]\r\ + \n" +add dont-require-permissions=no name=amneziawg-mikrotik-c owner=vados policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="\ + \n:global GlobalScriptsUrl \"https://ros.vados.ru/\";\ + \n:global imagesPath \"amneziawg/img/\";\ + \n:local Image \"awg-proxy-amd64-7.20-Docker.tar.gz\";\ + \n\ + \n:global DstDir \"sata1/dst/\";\ + \n\ + \n:local Url=(\$GlobalScriptsUrl . \$ImagesPath . \$Image);\ + \n\ + \n\ + \n\ + \n\ + \n\ + \n\ + \n\ + \n\ + \n\ + \n\ + \n\ + \n\ + \n\ + \n\ + \n\ + \n\ + \n\ + \n\ + \n\ + \n\ + \n" +add comment=Generator dont-require-permissions=no name=awg-VDSina owner=vados \ + policy=ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \ + source="# ============================================================\ + \n# AWG Proxy -- MikroTik RouterOS (protocol: v2)\ + \n# 2026-06-30 22:08:12\ + \n# ============================================================\ + \n\ + \n# 0. Check prerequisites\ + \n:if ([:len [/system/package/find where name=\"container\" disabled=no]] \ + = 0) do={\ + \n :put \"Container package is not installed or disabled. Install it and \ + reboot.\"\ + \n :error \"Usage: /system/device-mode/update container=yes\"\ + \n}\ + \n\ + \n# Auto-detect storage\ + \n:global awgDisk \"disk1\"\ + \n{\ + \n :local usbFree 0\ + \n :do { :set usbFree [/disk/get [find where mount-point=\"usb1\"] free] \ + } on-error={}\ + \n :if (\$usbFree > 0) do={\ + \n :set awgDisk \"usb1\"\ + \n :put (\"Using USB: usb1 (\" . (\$usbFree / 1048576) . \" MB free)\")\ + \n } else={\ + \n :if ([:len [/disk/find where slot=\"usb1\"]] > 0) do={\ + \n :put \"USB found but not mounted. Format as ext4\?\"\ + \n :put \"Press 'y' to confirm:\"\ + \n :local key [/terminal/inkey timeout=30]\ + \n :if (\$key = 121 || \$key = 89) do={\ + \n /disk/format-drive usb1 file-system=ext4 label=usb1\ + \n :delay 3s\ + \n :do { :set usbFree [/disk/get [find where mount-point=\"usb1\"] \ + free] } on-error={}\ + \n :if (\$usbFree > 0) do={\ + \n :set awgDisk \"usb1\"\ + \n } else={ :put \"Format failed, using disk1\" }\ + \n } else={ :put \"Using disk1\" }\ + \n } else={ :put \"No USB, using disk1\" }\ + \n }\ + \n}\ + \n\ + \n# 1. Uninstall script\ + \n/system/script/add name=awg-VDSina-uninstall comment=awg-VDSina source={\ + \n :put \"Uninstallimg AmneziaWG...\"\ + \n :log info \"Uninstallimg AmneziaWG...\"\ + \n :local prevDNS \"\"\ + \n :do { :set prevDNS [/container/envs/get [find where list=\"awg-VDSina-\ + env\" key=\"AWG_PREV_DNS\"] value] } on-error={}\ + \n :local storageDisk \"disk1\"\ + \n :do { :set storageDisk [/container/envs/get [find where list=\"awg-VDS\ + ina-env\" key=\"AWG_STORAGE\"] value] } on-error={}\ + \n :local prevTmpdir \"\"\ + \n :if (\$storageDisk != \"disk1\") do={\ + \n :do { :set prevTmpdir [/container/envs/get [find where list=\"awg-VD\ + Sina-env\" key=\"AWG_PREV_TMPDIR\"] value] } on-error={}\ + \n }\ + \n /ip/route/remove [find where comment=awg-VDSina-tunnel]\ + \n /ip/route/remove [find where comment=awg-VDSina-dns]\ + \n /container/stop [find where interface=veth-awg-VDSina]\ + \n :delay 7s\ + \n /container/remove [find where interface=veth-awg-VDSina]\ + \n /container/envs/remove [find where list=\"awg-VDSina-env\"]\ + \n :if (\$prevDNS != \"\") do={\ + \n :do { /ip/dns/set servers=\"\$prevDNS\" } on-error={}\ + \n :put (\"DNS restored to: \$prevDNS\")\ + \n }\ + \n :if (\$storageDisk != \"disk1\") do={\ + \n /container/config set tmpdir=\$prevTmpdir\ + \n :put (\"Container tmpdir restored to: \$prevTmpdir\")\ + \n }\ + \n /ip/firewall/nat/remove [find where out-interface=\"wg-awg-VDSina\"]\ + \n /ip/firewall/nat/remove [find where src-address=\"172.19.75.208/30\"]\ + \n /ip/address/remove [find where address=\"172.19.75.209/30\"]\ + \n /interface/veth/remove [find where name=\"veth-awg-VDSina\"]\ + \n /interface/wireguard/peers/remove [find where interface=\"wg-awg-VDSin\ + a\"]\ + \n /ip/address/remove [find where interface=\"wg-awg-VDSina\"]\ + \n /interface/wireguard/remove [find where name=\"wg-awg-VDSina\"]\ + \n :do { /file/remove [find where name~\"awg-VDSina.+tar\"] } on-error={}\ + \n :do { /file/remove [find where name=(\$storageDisk . \"/awg-VDSina\")]\ + \_} on-error={}\ + \n :do { /file/remove [find where name=(\$storageDisk . \"/ru_ranges_time\ + out.rsc\")] } on-error={}\ + \n :if (\$storageDisk != \"disk1\") do={\ + \n :do { /file/remove [find where name~(\$storageDisk . \"/pull\")] } o\ + n-error={}\ + \n }\ + \n :put \"Uninstall AmneziaWG Proxy complete!\"\ + \n :log info \"Uninstall AmneziaWG Proxy complete!\"\ + \n /system/script/remove [find where name=awg-VDSina-uninstall]\ + \n}\ + \n\ + \n# 2. Network infrastructure\ + \n/interface/veth/add name=veth-awg-VDSina address=172.19.75.210/30 gatewa\ + y=172.19.75.209\ + \n/ip/address/add address=172.19.75.209/30 interface=veth-awg-VDSina\ + \n/ip/firewall/nat/add chain=srcnat action=masquerade src-address=172.19.7\ + 5.208/30\ + \n\ + \n# 3. WireGuard interface (MikroTik derives the public key automatically)\ + \n/interface/wireguard/add name=wg-awg-VDSina private-key=\"4CHyNhbOs923/E\ + yNDguMXJXdghL2I3UyzXPwrANVgHs=\" listen-port=14710 disabled=yes\ + \n/interface/wireguard/peers/add interface=wg-awg-VDSina public-key=\"o5Ig\ + Cl1/G6xbl6j7sPaP4ZNYREeYOmqZjKbm+v0b9jU=\" preshared-key=\"RNvF7T1cg5tyXcY\ + QnD9N5BEp6e20vVJQTv0GMjyvHU0=\" endpoint-address=172.19.75.210 endpoint-po\ + rt=51820 allowed-address=0.0.0.0/0 persistent-keepalive=10\ + \n/ip/address/add address=10.9.0.3/32 interface=wg-awg-VDSina\ + \n\ + \n# 4. Container environment variables\ + \n/container/envs/add list=awg-VDSina-env key=AWG_LISTEN value=\":51820\"\ + \n/container/envs/add list=awg-VDSina-env key=AWG_REMOTE value=\"109.107.1\ + 77.79:55055\"\ + \n/container/envs/add list=awg-VDSina-env key=AWG_JC value=\"9\"\ + \n/container/envs/add list=awg-VDSina-env key=AWG_JMIN value=\"105\"\ + \n/container/envs/add list=awg-VDSina-env key=AWG_JMAX value=\"920\"\ + \n/container/envs/add list=awg-VDSina-env key=AWG_S1 value=\"90\"\ + \n/container/envs/add list=awg-VDSina-env key=AWG_S2 value=\"136\"\ + \n/container/envs/add list=awg-VDSina-env key=AWG_H1 value=\"472437748-472\ + 438637\"\ + \n/container/envs/add list=awg-VDSina-env key=AWG_H2 value=\"707808381-707\ + 809250\"\ + \n/container/envs/add list=awg-VDSina-env key=AWG_H3 value=\"1609078552-16\ + 09079495\"\ + \n/container/envs/add list=awg-VDSina-env key=AWG_H4 value=\"1012329484-10\ + 12329741\"\ + \n/container/envs/add list=awg-VDSina-env key=AWG_SERVER_PUB value=\"o5IgC\ + l1/G6xbl6j7sPaP4ZNYREeYOmqZjKbm+v0b9jU=\"\ + \n/container/envs/add list=awg-VDSina-env key=AWG_CLIENT_PUB value=[/inter\ + face/wireguard/get [find name=wg-awg-VDSina] public-key]\ + \n/container/envs/add list=awg-VDSina-env key=AWG_S3 value=\"45\"\ + \n/container/envs/add list=awg-VDSina-env key=AWG_S4 value=\"27\"\ + \n/container/envs/add list=awg-VDSina-env key=AWG_I1 value=\"\"\ + \n/container/envs/add list=awg-VDSina-env key=AWG_I2 value=\"\"\ + \n/container/envs/add list=awg-VDSina-env key=AWG_I3 value=\"\ + \"\ + \n/container/envs/add list=awg-VDSina-env key=AWG_I4 value=\"\"\ + \n/container/envs/add list=awg-VDSina-env key=AWG_I5 value=\"\"\ + \n\ + \n# 5. DNS\ + \n{ :local rawDNS [/ip/dns/get servers]; :local prevDNS \"\"; :foreach s i\ + n=[:toarray \$rawDNS] do={ :if ([:len \$prevDNS] > 0) do={ :set prevDNS \"\ + \$prevDNS,\$s\" } else={ :set prevDNS \$s } }; /container/envs/add list=aw\ + g-VDSina-env key=AWG_PREV_DNS value=\"\$prevDNS\" }\ + \n# NOTE: replaces existing DNS configuration\ + \n/ip/dns/set servers=109.107.177.79\ + \n/ip/route/add dst-address=109.107.177.79/32 gateway=wg-awg-VDSina distan\ + ce=1 comment=awg-VDSina-dns\ + \n\ + \n# 6. Download, create and start container\ + \n/container/envs/add list=awg-VDSina-env key=AWG_STORAGE value=\$awgDisk\ + \n:if (\$awgDisk != \"disk1\") do={\ + \n :local tmpPath (\$awgDisk . \"/pull\")\ + \n /container/envs/add list=awg-VDSina-env key=AWG_PREV_TMPDIR value=[/co\ + ntainer/config/get tmpdir]\ + \n /container/config set tmpdir=\$tmpPath memory-high=200M\ + \n}\ + \n{\ + \n :local arch [/system/resource/get architecture-name]\ + \n :local ver [/system/resource/get version]\ + \n :local dotPos [:find \$ver \".\"]\ + \n :local rest [:pick \$ver (\$dotPos + 1) [:len \$ver]]\ + \n :local endPos [:find \$rest \".\"]\ + \n :if ([:typeof \$endPos] = \"nil\") do={ :set endPos [:find \$rest \" \ + \"] }\ + \n :local minor [:tonum [:pick \$rest 0 \$endPos]]\ + \n :local suffix \"\"\ + \n :if (\$minor <= 20) do={ :set suffix \"-7.20-Docker\" }\ + \n :local file \"\"\ + \n :if (\$arch = \"arm64\") do={ :set file (\"awg-proxy-arm64\" . \$suffi\ + x . \".tar.gz\") }\ + \n :if (\$arch = \"arm\") do={ :set file (\"awg-proxy-arm\" . \$suffix . \ + \".tar.gz\") }\ + \n :if (\$arch ~ \"x86\") do={ :set file (\"awg-proxy-amd64\" . \$suffix \ + . \".tar.gz\") }\ + \n :if (\$file = \"\") do={ :error \"Unsupported architecture: \$arch\" }\ + \n :local url \"https://github.com/timbrs/amneziawg-mikrotik-c/releases/l\ + atest/download/\$file\"\ + \n :local filePath \$file\ + \n :if (\$awgDisk != \"disk1\") do={\ + \n :set filePath (\$awgDisk . \"/pull/\" . \$file)\ + \n :do { /file/make-directory (\$awgDisk . \"/pull\") } on-error={}\ + \n }\ + \n :if ([:len [/file/find where name=\$filePath]] = 0) do={\ + \n :local freeStorage 0\ + \n :if (\$awgDisk = \"disk1\") do={\ + \n :set freeStorage [/system/resource/get free-hdd-space]\ + \n } else={\ + \n :do { :set freeStorage [/disk/get [find where mount-point=\$awgDis\ + k] free] } on-error={}\ + \n }\ + \n :if (\$freeStorage < 262144) do={\ + \n :put (\"WARNING: Low disk space (\" . (\$freeStorage / 1024) . \"K\ + B free). Need at least 256KB.\")\ + \n :put \"See: https://github.com/timbrs/amneziawg-mikrotik-c#insuffi\ + cient-disk-space\"\ + \n :error \"Insufficient disk space\"\ + \n }\ + \n :put (\"Fetching: \$url -> \" . \$filePath)\ + \n /tool/fetch url=\$url dst-path=\$filePath http-max-redirect-count=10\ + \n } else={\ + \n :put (\"File already exists: \" . \$filePath)\ + \n }\ + \n :local rootDir (\$awgDisk . \"/awg-VDSina\")\ + \n /container/add file=\$filePath interface=veth-awg-VDSina envlist=awg-V\ + DSina-env hostname=awg-VDSina root-dir=\$rootDir logging=no start-on-boot=\ + yes comment=awg-VDSina\ + \n :if (\$minor > 20) do={ [:parse \"/container/set [find where interface\ + =veth-awg-VDSina] shm-size=4M\"] }\ + \n /file/remove \$filePath\ + \n :local freeMem [/system/resource/get free-memory]\ + \n :if (\$freeMem < 16777216) do={\ + \n :put (\"WARNING: Low memory (\" . (\$freeMem / 1048576) . \"MB free)\ + . 16MB+ recommended.\")\ + \n }\ + \n /container/start [find where interface=veth-awg-VDSina]\ + \n :do { /file/remove [find where name=\"console-dump.txt\"] } on-error={\ + }\ + \n :put \"Waiting for container to start...\"\ + \n :delay 5s\ + \n /interface/wireguard/enable wg-awg-VDSina\ + \n :put \"WireGuard interface enabled\"\ + \n :put \"Installation complete!\"\ + \n :put \"Ping test: 109.107.177.79 via AmneziaWG tunnel. repeat 10 times\ + \"\ + \n /ping 109.107.177.79 count=10 interval=1\ + \n :put \"\"\ + \n :put \"======= NEXT STEPS =======\"\ + \n :put \"\"\ + \n :put \"Route specific traffic through the AmneziaWG tunnel:\"\ + \n :put \"\"\ + \n :put \" Route a single host:\"\ + \n :put \" /ip/route/add dst-address=8.8.8.8/32 gateway=wg-awg-VDSina \ + comment=awg-VDSina-tunnel\"\ + \n :put \"\"\ + \n :put \" Route a subnet:\"\ + \n :put \" /ip/route/add dst-address=100.0.0.0/8 gateway=wg-awg-VDSina\ + \_comment=awg-VDSina-tunnel\"\ + \n :put \"\"\ + \n :put \" Remove a route:\"\ + \n :put \" /ip/route/remove [find where comment=awg-VDSina-tunnel dst-\ + address=8.8.8.8/32]\"\ + \n :put \"\"\ + \n :put \" Show all tunnel routes:\"\ + \n :put \" /ip/route/print where comment=awg-VDSina-tunnel\"\ + \n :put \"\"\ + \n :put \" Uninstall:\"\ + \n :put \" /system/script/run awg-VDSina-uninstall\"\ + \n :put \"=========================\"\ + \n}\ + \n\ + \n# 7. Verification\ + \n# /container/print\ + \n# /interface/wireguard/print\ + \n# /interface/wireguard/peers/print\ + \n" +add comment="wg2awg GH repo in client mode" dont-require-permissions=no name=\ + "wg2awg client" owner=vados policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="\ + \n:local Arch \"amd64\";\ + \n:local Version \"v0.0.1\";\ + \n:local Name wg2awg;\ + \n:local WorkDisk \"sata1\";\ + \n:local WorkDir (\$WorkDisk . \"/\" . \$Name);\ + \n:local Bridge (\"Bridge-\" . \$Name);\ + \n:local VetchMTU \"1280\";\ + \n:local Vetch (\"vetch-\" . \$Name);\ + \n:local VetchNet \"10.9.0.0\";\ + \n:local VetchGate \"10.9.0.1\";\ + \n:local VetchAddr \"10.9.0.3/32\";\ + \n:local Env (\$Name . \"-env\");\ + \n:local ConfDir (\"/etc/\" . \$Name . \"/\");\ + \n:local ConfFile (\$Name . \".conf\");\ + \n:local Mounts (\$WorkDisk . \"/mounts/\" . \$Name . \"/\");\ + \n:local RootDir (\$WorkDisk . \"/\" . \$Name);\ + \n:local Registry \"https://registry-1.docker.io\";\ + \n:local FetchFile (\$Name . \"-\" . \$Arch . \"-oci.tar\");\ + \n:local FetchUrl (\"https://github.com/vados-dev/wg2awg/releases/download\ + /\" . \$Version . \"/\" . \$FetchFile);\ + \n:local FetchDir (\$WorkDisk . \"/dst/\");\ + \n:local AwgListen \":44044\";\ + \n:local AwgRemote \"109.107.177.79\";\ + \n\ + \n#:put \"\"\ + \n#put \"Add ram disk tmpfs\"\ + \n#/disk add type=tmpfs tmpfs-max-size=100M slot=ram\ + \n\ + \n\ + \n:log warning \"[::::::::::::::: START :::::::::::::::]\"\ + \n:log warning \"Add veth and nat...\";\ + \n\ + \n/interface/veth/add name=\$Vetch address=\$VetchAddr gateway=\$VetchGate\ + ;\ + \n\ + \n:do {\ + \n:log warning (\"Remove interface: name=\" . \$Vetch . \", address=\" . \ + \$VetchAddr . \", gateway=\" . \$VetchGate);\ + \n/interface/veth/remove [find name=\$Vetch];\ + \n:log warning (\"Add interface: name=\" . \$Vetch . \", address=\" . \$Ve\ + tchAddr . \", gateway=\" . \$VetchGate);\ + \n/interface/veth/add name=\$Vetch address=\$VetchAddr gateway=\$VetchGate\ + ;\ + \n:log warning (\"Remove ip address: address=\" . \$VetchGate . \", interf\ + ace=\" . \$Vetch);\ + \n/ip/address/remove [find address=\$VetchGate interface=\$Vetch];\ + \n:log warning (\"Add ip address: address=\" . \$VetchGate . \", interface\ + =\" . \$Vetch);\ + \n/ip/address/add address=\$VetchGate interface=\$Vetch;\ + \n:log warning (\"Remove firewall nat rule: chain=srcnat action=masquerade\ + \_src-address=\" . \$VetchNet . \"/24\");\ + \n/ip/firewall/nat/remove [find chain=srcnat action=masquerade src-address\ + =(\$VetchNet . \"/24\")];\ + \n:log warning (\"Add firewall nat rule: chain=srcnat action=masquerade sr\ + c-address=\" . \$VetchNet . \"/24\");\ + \n/ip/firewall/nat/add chain=srcnat action=masquerade src-address=(\$VethN\ + et . \"/24\");\ + \n} on-error={}\ + \n\ + \n#:do {\ + \n#/file add type=directory name=\$Mounts\ + \n#} on-error={}\ + \n\ + \n\ + \n:do {\ + \n:log warning (\"Remove WorkDir: \" . \$WorkDir . \"...\");\ + \n/file remove [find name=\$WorkDir];\ + \n:log warning (\"Add WorkDir: \" . \$WorkDir . \"...\");\ + \n/file add type=directory name=\$WorkDir;\ + \n:log warning (\"Remove config file \" . \$Mounts . \$ConfFile . \"...\")\ + ;\ + \n/file remove [find name=(\$Mounts . \$ConfFile)];\ + \n:log warning (\"Add config file \" . \$Mounts . \$ConfFile . \"...\");\ + \n/file add type=file name=(\$Mounts . \$ConfFile) contents=\"[Interface]\ + \nPrivateKey = 4CHyNhbOs923/EyNDguMXJXdghL2I3UyzXPwrANVgHs=\ + \nAddress = \$VetchAddr\ + \nMTU = \$VetchMTU\ + \nDNS = 109.107.177.79\ + \nPreUp = ip route add 109.107.177.79/32 via \$VetchGate dev \$Vetch\ + \nPostDown = ip route del 109.107.177.79/32 via \$VetchGate dev \$Vetch\ + \nJc = 9\ + \nJmin = 105\ + \nJmax = 920\ + \nS1 = 90\ + \nS2 = 136\ + \nS3 = 45\ + \nS4 = 27\ + \nH1 = 472437748-472438637\ + \nH2 = 707808381-707809250\ + \nH3 = 1609078552-1609079495\ + \nH4 = 1012329484-1012329741\ + \nI1 = <\ + r 4>\ + \nI2 = \ + \nI3 = \ + \nI4 = \ + \nI5 = \ + \n\ + \n[Peer]\ + \nPublicKey = o5IgCl1/G6xbl6j7sPaP4ZNYREeYOmqZjKbm+v0b9jU=\ + \nAllowedIPs = 0.0.0.0/0\ + \nEndpoint = \$AwgRemote\ + \nPersistentKeepalive = 10\ + \nPresharedKey = RNvF7T1cg5tyXcYQnD9N5BEp6e20vVJQTv0GMjyvHU0=\";\ + \n} on-error={}\ + \n\ + \n\ + \n:do {\ + \n:log warning (\"Remove mounts list \$Env...\");\ + \n/container/mounts/remove [find list=\$Env];\ + \n:log warning (\"Add mounts list \$Env...\");\ + \n/container/mounts/add src=(\$Mounts . \$ConfFile) dst=(\$ConfDir . \$Con\ + fFile) list=\$Env mode=ro;\ + \n:log warning (\"Remove Environment keys.\");\ + \n/container/envs/remove [find list=\$Env];\ + \n:log warning (\"Add Environment keys.\");\ + \n/container/envs/add list=\$Env key=AWG_CONFIG value=(\$ConfDir . \$ConfF\ + ile);\ + \n/container/envs/add list=\$Env key=AWG_LISTEN value=\$AwgListen\ + \n/container/envs/add list=\$Env key=AWG_REMOTE value=\$AwgRemote\ + \n/container/envs/add list=\$Env key=AWG_MODE value=client\ + \n} on-error={}\ + \n\ + \n:do {\ + \n:log warning (\"Remove \" . \$WorkDisk . \"/tmp\");\ + \n/file remove [find name=(\$WorkDisk . \"/tmp\")];\ + \n:log warning (\"Add \" . \$WorkDisk . \"/tmp\");\ + \n/file add type=directory name=(\$WorkDisk . \"/tmp\");\ + \n:log warning (\"Container add tmpdir \" . \$WorkDisk . \"/tmp\");\ + \n/container config set tmpdir=(\$WorkDisk . \"/tmp\");\ + \n} on-error={}\ + \n\ + \n:do {\ + \n:log warning (\"Remove \" . \$FetchDir . \$FetchFile . \".\");\ + \n/file remove [find name=(\$FetchDir . \$FetchFile)];\ + \n:log warning (\"Download image from \" . \$FetchUrl . \" and save him to\ + \_\" . \$FetchDir . \$FetchFile . \".\");\ + \n/tool/fetch url=\$FetchUrl dst-path=(\$FetchDir . \$FetchFile);\ + \n} on-error={}\ + \n\ + \n#:log warning (\"Container set registry \" . \$Registry . \" tmpdir= \" \ + . \$WorkDisk . \"/tmp\");\ + \n#/container/config/set registry-url=\$Registry tmpdir=(\$WorkDisk . \"/t\ + mp\");\ + \n\ + \n#:do {\ + \n:log warning (\"Container add width: file=\" . \$FetchDir . \$FetchFile \ + . \", interface=\" . \$Vetch . \", hostname=\" . \$Name . \", mountlists=\ + \" . \$Env . \", envlist=\" . \$Env . \", root-dir=\" . \$RootDir . \", wo\ + rkdir=\" . \$WorkDir . \", name=\" . \$Name . \", logging=yes, start-on-bo\ + ot=yes\");\ + \n/container/add file=(\$FetchDir . \$FetchFile) interface=\$Vetch hostnam\ + e=\$Name mountlists=\$Env envlist=\$Env root-dir=\$RootDir workdir=\$WorkD\ + ir name=\$Name logging=yes start-on-boot=yes;\ + \n#} on-error={}\ + \n\ + \n\ + \n#/container/start [find where interface=\$Vetch];\ + \n\ + \n#/interface/bridge add name=\$Bridge port-cost-mode=short;\ + \n#/ip/address/add address=192.168.100.1/24 interface=\$Bridge;\ + \n\ + \n#/interface/veth add address=192.168.100.3/24 gateway=192.168.100.1 name\ + =awg-proxy-go-client\ + \n#/interface/bridge/port add bridge=docker interface=awg-proxy-go-client\ + \n\ + \n#:put \"add config-client.json\"\ + \n#/file/add name=sata1/mounts/awg-proxy-go/config-client.json\ + \n#/container/mounts/add src=sata1/mounts/awg-proxy-go/config-client.json \ + dst=/app/config.json list=awgcfg-client\ + \n\ + \n#:put \"Download image, and save him to sata1/tmp/awgproxy-0.0.1-0034-am\ + d64.tar.gz\"\ + \n#/tool/fetch url=\"https://ros.vados.ru/amneziawg/img/awgproxy-0.0.1-003\ + 4-amd64.tar.gz\" dst-#path=\"tmp/awgproxy-0.0.1-0034-amd64.tar.gz\"\ + \n\ + \n#:put \"Add container\"\ + \n#/container/add file=\"usb1/dst/awgproxy-0.0.1-0036-amd64.tar\" interfac\ + e=awg-proxy-go-client hostname=awg-proxy-go-client root-dir=sata1/awg-prox\ + y-client logging=yes start-on-boot=yes workdir=sata1/docker/ name=\"awg-pr\ + oxy-go-client\" mountlists=awgcfg-client\ + \n\ + \n#:put \"Start container\"\ + \n#/container/start awg-proxy-go-client\ + \n\ + \n#:put \"Add user:\"\ + \n#/user/add address=192.168.100.2 group=write name=awg-proxy-go password=\ + peskar55\ + \n\ + \n#:put \"Add forwarding port web-ui\"\ + \n#/ip firewall nat add action=dst-nat chain=dstnat dst-port=8089 in-inter\ + face=ether1 protocol=tcp to-addresses=192.168.100.3 to-port=8088\ + \n\ + \n#:put \"Add native WireGuard-server wg-awg, add address.\"\ + \n#/interface/wireguard/add name=wg-awg-client\ + \n#/ip/address/add address=172.17.0.1/24 interface=wg-awg-client\ + \n\ + \n:put \"\"\ + \n:put \"[::::::::::::::: END :::::::::::::::]\"\ + \n" +add dont-require-permissions=no name=tmp_print owner=vados policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="/\ + interface/wireguard/add name=wg-VDSina private-key=\"4CHyNhbOs923/EyNDguMX\ + JXdghL2I3UyzXPwrANVgHs=\" listen-port=21641 \ + \n :put \"======= AWG PROXY SETUP =======\"\ + \n :put \"\"\ + \n :put \"Run awg-proxy on your external device (10.30.30.33).\"\ + \n :put \"\"\ + \n :put \"1. Download the binary for your platform:\"\ + \n :put \" Linux arm64: https://github.com/timbrs/amneziawg-mikrotik-c/\ + releases/latest/download/awg-proxy-linux-arm64\"\ + \n :put \" Linux arm: https://github.com/timbrs/amneziawg-mikrotik-c/\ + releases/latest/download/awg-proxy-linux-arm\"\ + \n :put \" Linux amd64: https://github.com/timbrs/amneziawg-mikrotik-c/\ + releases/latest/download/awg-proxy-linux-amd64\"\ + \n :put \"\"\ + \n :put \"2. Set environment variables and run:\"\ + \n :put \"export AWG_LISTEN=\\\":51820\\\"\"\ + \n :put \"export AWG_REMOTE=\\\"109.107.177.79:55055\\\"\"\ + \n :put \"export AWG_JC=\\\"9\\\"\"\ + \n :put \"export AWG_JMIN=\\\"105\\\"\"\ + \n :put \"export AWG_JMAX=\\\"920\\\"\"\ + \n :put \"export AWG_S1=\\\"90\\\"\"\ + \n :put \"export AWG_S2=\\\"136\\\"\"\ + \n :put \"export AWG_H1=\\\"472437748-472438637\\\"\"\ + \n :put \"export AWG_H2=\\\"707808381-707809250\\\"\"\ + \n :put \"export AWG_H3=\\\"1609078552-1609079495\\\"\"\ + \n :put \"export AWG_H4=\\\"1012329484-1012329741\\\"\"\ + \n :put \"export AWG_SERVER_PUB=\\\"o5IgCl1/G6xbl6j7sPaP4ZNYREeYOmqZjKbm+\ + v0b9jU=\\\"\"\ + \n :put (\"export AWG_CLIENT_PUB=\\\"\" . [/interface/wireguard/get [find\ + \_name=wg-VDSina] public-key] . \"\\\"\")\ + \n :put \"export AWG_S3=\\\"45\\\"\"\ + \n :put \"export AWG_S4=\\\"27\\\"\"\ + \n :put \"export AWG_I1=\\\"\\\"\"\ + \n :put \"export AWG_I2=\\\"\\\"\"\ + \n :put \"export AWG_I3=\\\"\\\"\"\ + \n :put \"export AWG_I4=\\\"\\\"\"\ + \n :put \"export AWG_I5=\\\"\\\"\"\ + \n :put \"\"\ + \n :put \"chmod +x awg-proxy-linux-* && ./awg-proxy-linux-\"\ + \n :put \"\"\ + \n :put \"3. (Optional) Create a systemd service for auto-start.\"\ + \n :put \"\"\ + \n :put \"Route specific traffic through the AmneziaWG tunnel:\"\ + \n :put \"\"\ + \n :put \" Route a single host:\"\ + \n :put \" /ip/route/add dst-address=8.8.8.8/32 gateway=wg-VDSina comm\ + ent=VDSina-tunnel\"\ + \n :put \"\"\ + \n :put \" Route a subnet:\"\ + \n :put \" /ip/route/add dst-address=100.0.0.0/8 gateway=wg-VDSina com\ + ment=VDSina-tunnel\"\ + \n :put \"\"\ + \n :put \" Remove a route:\"\ + \n :put \" /ip/route/remove [find where comment=VDSina-tunnel dst-addr\ + ess=8.8.8.8/32]\"\ + \n :put \"\"\ + \n :put \" Show all tunnel routes:\"\ + \n :put \" /ip/route/print where comment=VDSina-tunnel\"\ + \n :put \"\"\ + \n :put \" Uninstall:\"\ + \n :put \" /system/script/run VDSina-uninstall\"\ + \n :put \"================================\"" +/user group +add name=backup policy="telnet,ssh,ftp,read,write,policy,test,sniff,sensitive,\ + !local,!reboot,!winbox,!password,!web,!api,!romon,!rest-api" +add name=manager policy="local,ssh,ftp,read,write,policy,api,rest-api,!telnet,\ + !reboot,!test,!winbox,!password,!web,!sniff,!sensitive,!romon" +/app +set cinny firewall-redirects=8094:80:tcp:web +set goaway container-command-lines=goaway:none:docker.io/pommee/goaway:latest +set home-assistant container-command-lines=\ + home-assistant:none:lscr.io/linuxserver/homeassistant +set n8n firewall-redirects=5678:5678:tcp:web +set nextcloud container-command-lines="db:none:docker.io/postgres:17,redis:non\ + e:docker.io/valkey/valkey:/bin/sh -c 'valkey-server --port 6379 --appendon\ + ly yes --requirepass \$VALKEY_PASSWORD',server:none:docker.io/nextcloud:ap\ + ache" +set pihole environment="pihole:FTLCONF_dns_listeningMode=all,pihole:FTLCONF_we\ + bserver_api_password=password" +set redlib firewall-redirects=8087:8080:tcp:web +set solr container-command-lines=solr:none:docker.io/solr:latest +set uptime-kuma container-command-lines=\ + uptime-kuma:none:docker.io/louislam/uptime-kuma:1 +/app settings +set disk=*7 lan-bridge=Bridge-LAN registry-mirrors="https://reg.vados.ru/:http\ + s://reg.vados.ru/,https://registry-1.docker.io:https://registry-1.docker.i\ + o,https://ghcr.io:https://ghcr.io" show-in-webfig=no +/caps-man aaa +set called-format=ssid interim-update=20m20s mac-caching=16m40s +/caps-man access-list +add action=reject allow-signal-out-of-range=10s disabled=no interface=all \ + signal-range=-120..-80 ssid-regexp="" +add action=accept allow-signal-out-of-range=10s disabled=no interface=all \ + signal-range=-79..120 ssid-regexp="" +/caps-man manager +set ca-certificate=CAPsMAN-CA-2C900E832E04 certificate=CAPsMAN-2C900E832E04 \ + enabled=yes +/caps-man manager interface +set [ find default=yes ] forbid=yes +add disabled=no interface=Bridge-LAN +/caps-man provisioning +add action=create-dynamic-enabled hw-supported-modes=g,gn,b \ + master-configuration=2G name-format=prefix-identity name-prefix=2G +add action=create-dynamic-enabled hw-supported-modes=an,ac,a \ + master-configuration=5G name-format=prefix-identity name-prefix=5G +add action=create-dynamic-enabled disabled=yes hw-supported-modes=ac,an,a \ + master-configuration=2G name-format=prefix-identity name-prefix=5ghz-ac +add action=create-dynamic-enabled disabled=yes hw-supported-modes=an \ + master-configuration=5G name-format=prefix-identity name-prefix=5ghz-an +/certificate settings +set builtin-trust-store=untrusted +/container config +set layer-dir=/sata1/docker/layers memory-high=200.0MiB memory-max=4096.0MiB \ + registry-url=https://registry-1.docker.io tmpdir=/sata1/tmp username=\ + amsternl +/container envs +add key=AWG_CONFIG list=wg2awg-env value=/etc/wg2awg/wg2awg.conf +add key=AWG_LISTEN list=wg2awg-env value=:44044 +add key=AWG_MODE list=wg2awg-env value=client +add key=AWG_REMOTE list=wg2awg-env value=109.107.177.79 +/container mounts +add dst=/etc/wg2awg/wg2awg.conf list=wg2awg-env mode=ro src=\ + /sata1/mounts/wg2awg/wg2awg.conf +/ip smb +set enabled=no +/interface bridge port +add bridge=Bridge-LAN interface=LAN +/ipv6 settings +set allow-fast-path=no disable-ipv6=yes disable-link-local-address=yes +/interface detect-internet +set detect-interface-list=WAN internet-interface-list=Internet \ + lan-interface-list=LAN wan-interface-list=WAN +/interface list member +add interface=ether1 list=WAN +add interface=Bridge-LAN list=LAN +/interface wifi cap +set discovery-interfaces=*DF +/interface wifi capsman +set ca-certificate=auto certificate=auto interfaces=Bridge-LAN package-path=\ + /tmpfs/capsman-packages require-peer-certificate=no upgrade-policy=none +/ip address +add address=10.30.30.10/24 interface=Bridge-LAN network=10.30.30.0 +/ip dhcp-client +add disabled=yes interface=ether1 name=client1 +/ip dhcp-server +add add-arp=yes address-pool=dhcp always-broadcast=yes bootp-support=none \ + dhcp-option-set=domain-search-set disabled=yes interface=*3 lease-script=\ + lease-script lease-time=10m name=nucDHCP server-address=10.30.30.10 +/ip dns +set allow-remote-requests=yes doh-max-concurrent-queries=100 \ + doh-max-server-connections=20 servers=10.30.30.33,109.107.177.79 \ + verify-doh-cert=yes +/ip firewall service-port +set ftp disabled=yes +set tftp disabled=yes +set sip disabled=yes +set pptp disabled=yes +set rtsp disabled=no +/ip route +add check-gateway=none disabled=no distance=1 dst-address=0.0.0.0/0 gateway=\ + 10.30.30.81 routing-table=main scope=30 target-scope=10 +/ip service +set ftp disabled=yes +set reverse-proxy disabled=yes +set ssh port=2222 +set www-ssl port=4443 +set api-ssl disabled=yes +/ip service webserver +set acme-plain=no crl-plain=no graphs-plain=no graphs-secure=no index-plain=\ + no index-secure=no rest-secure=no scep-plain=no webfig-plain=no \ + webfig-secure=no +/ip ssh +set forwarding-enabled=both publickey-authentication-options=verify-required \ + strong-crypto=yes +/ip upnp +set enabled=yes +/ip upnp interfaces +add interface=*3 type=external +add interface=*B5 type=internal +/ipv6 firewall address-list +add address=2400:cb00::/32 list=CLOUDFLARE-IP-Address +add address=2606:4700::/32 list=CLOUDFLARE-IP-Address +add address=2803:f800::/32 list=CLOUDFLARE-IP-Address +add address=2405:b500::/32 list=CLOUDFLARE-IP-Address +add address=2405:8100::/32 list=CLOUDFLARE-IP-Address +add address=2a06:98c0::/29 list=CLOUDFLARE-IP-Address +add address=2c0f:f248::/32 list=CLOUDFLARE-IP-Address +/special-login +add port=serial0 +/system clock +set time-zone-name=Europe/Moscow +/system gps +set set-system-time=yes +/system identity +set name=MikroNUC +/system note +set show-at-login=no +/system ntp client +set enabled=yes +/system ntp server +set enabled=yes manycast=yes multicast=yes +/system ntp client servers +add address=10.30.30.81 +add address=0.ru.pool.ntp.org +add address=1.ru.pool.ntp.org +add address=2.ru.pool.ntp.org +add address=3.ru.pool.ntp.org +/system package update +set check-certificate=yes-without-crl ip-version=ipv4 +/system scheduler +add comment="Every Day Set Global Environment Variables" disabled=yes name=\ + AM-SetGlobalEnv on-event=\ + "/system/script { run AM-GlobalConfig; run AM-GlobalFunc; }" policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \ + start-time=startup +add comment="Every Day Check For Update Scripts" disabled=yes interval=1d \ + name=AM-ScriptInstallUpdate on-event=\ + ":global ScriptInstallUpdate; \$ScriptInstallUpdate;" policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \ + start-time=startup +add comment="Every Day Check For Fake Bridges" disabled=yes interval=1d name=\ + mod/AM-IPbridge on-event="/system/script/run mod/AM-IPbridge;" policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \ + start-time=startup +add comment="Telegram Chat Bot" disabled=yes interval=30s name=mod/AM-TG-Bot \ + on-event="/system/script/run mod/AM-TG-Bot;" policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \ + start-time=startup +add comment=\ + "Every 7 Day Check For Update and Backup send notification to EMail." \ + disabled=yes interval=1w name=AM-Backup-UpdateEmail on-event=\ + "/system/script { run AM-Backup-UpdateEmail; }" policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \ + start-date=2026-06-25 start-time=03:41:27 +/tool e-mail +set certificate-verification=no from=MikroNUC port=587 \ + server=smtp.mail.me.com tls=starttls user=vadossio@icloud.com +/user aaa +set accounting=no diff --git a/MikroNUC/history/20260817/20260817102001-Diff-Config.rsc b/MikroNUC/history/20260817/20260817102001-Diff-Config.rsc new file mode 100644 index 0000000..8fd83d3 --- /dev/null +++ b/MikroNUC/history/20260817/20260817102001-Diff-Config.rsc @@ -0,0 +1,13 @@ +5386c5386 +< set called-format=ssid interim-update=20m20s mac-caching=16m40s +--- +> set interim-update=20m20s mac-caching=16m40s +5393,5394c5393,5397 +< set ca-certificate=CAPsMAN-CA-2C900E832E04 certificate=CAPsMAN-2C900E832E04 \ +< enabled=yes +--- +> # bad package path +> set ca-certificate=WiFi-CAPsMAN-CA-6D4D5AB6A085 certificate=\ +> WiFi-CAPsMAN-6D4D5AB6A085 enabled=yes package-path=\ +> /tmpfs/capsman-packages require-peer-certificate=yes upgrade-policy=\ +> require-same-version -- 2.52.0