From 89b014c9027106e6c261e4e9898e636c27b82919 Mon Sep 17 00:00:00 2001 From: vados-dev Date: Mon, 29 Jun 2026 05:20:03 +0300 Subject: [PATCH] Backup for MikroNUC created successfully at Jun 29 05:20:03. --- .../20260629/20260629052001-Binary.backup | Bin 0 -> 368677 bytes MikroNUC/20260629/20260629052001-Config.rsc | 4918 +++++++++++++++++ .../20260629/20260629052001-Diff-Config.rsc | 11 + 3 files changed, 4929 insertions(+) create mode 100644 MikroNUC/20260629/20260629052001-Binary.backup create mode 100644 MikroNUC/20260629/20260629052001-Config.rsc create mode 100644 MikroNUC/history/20260629/20260629052001-Diff-Config.rsc diff --git a/MikroNUC/20260629/20260629052001-Binary.backup b/MikroNUC/20260629/20260629052001-Binary.backup new file mode 100644 index 0000000000000000000000000000000000000000..01a2db3526874fe42d067cd2ab587490657f90c7 GIT binary patch literal 368677 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

1RqNI+T+F=hk&m@$(s5m!t0&Ft)Bh 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 literal 0 HcmV?d00001 diff --git a/MikroNUC/20260629/20260629052001-Config.rsc b/MikroNUC/20260629/20260629052001-Config.rsc new file mode 100644 index 0000000..7c87a59 --- /dev/null +++ b/MikroNUC/20260629/20260629052001-Config.rsc @@ -0,0 +1,4918 @@ +# 2026-06-29 05:20:02 by RouterOS 7.23.1 +# 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=br-awg +add name=bridge2 +/interface ethernet +set [ find default-name=ether1 ] disable-running-check=no +/interface veth +add address=172.19.77.106/30 container-mac-address=5E:0C:7C:E8:98:12 dhcp=no \ + gateway=172.19.77.105 gateway6="" mac-address=5E:0C:7C:E8:98:11 name=\ + veth-AWG-VDSina +add address=10.9.9.2/24 container-mac-address=7E:39:0C:32:E5:06 dhcp=no \ + gateway=10.9.9.1 gateway6="" mac-address=7E:39:0C:32:E5:05 name=veth-awg +/interface wireguard +add listen-port=13822 mtu=1420 name=wg-AWG-VDSina +/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 +/container +add envlists=awgui-env file=sata1/tmp/awg2-webui.tar hostname=awg2-webui \ + interface=veth-awg layer-dir=/sata1/docker/awg2-webu logging=yes name=\ + awg2-webui root-dir=/sata1/awg2-webui shm-size=4096.0KiB start-on-boot=\ + yes +/disk +add slot=ram tmpfs-max-size=100000000 type=tmpfs +/interface list +add include=dynamic,static name=LAN +add exclude=LAN name=WAN +add exclude=LAN include=WAN name=Internet +/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/700/f14f095d\";\r\ + \n:global CommitTAG \"unknown\";\r\ + \n:global CommitInfo \"Autocommit in repo: ros.vados.ru, generated by owne\ + r: root, in 13:30:03 22.06.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="#\ + \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\";\r\ + \n#\r\ + \n#root.mnuc.backuper\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\";\r\ + \n#:local Script AM-GlobalConfig;\r\ + \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\");\r\ + \n#/usb1/mikro_bak_rsa.pub\r\ + \n#:local FetchFile AM-DOHCert.rsc\r\ + \n\r\ + \n:global GlobalScriptsUrl \"https://ros.vados.ru/tmp/\";\r\ + \n:global MkDir;\r\ + \n\r\ + \n:local FetchFile Binary.backup\r\ + \n:local localDir \"[usb1]/scripts/\";\r\ + \n\r\ + \n\$MkDir \$localDir;\r\ + \n\r\ + \n/tool/fetch mode=https url=(\$GlobalScriptsUrl . \$FetchFile) dst-path=(\ + \$localDir . \$FetchFile);\r\ + \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" +/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="ssh,ftp,read,write,policy,!local,!telnet,!reboot,!tes\ + t,!winbox,!password,!web,!sniff,!sensitive,!api,!romon,!rest-api" +/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 auto-update=yes disk=*7 lan-bridge=*3 registry-mirrors="https://reg.vados.\ + ru:5000/:https://reg.vados.ru/,https://registry-1.docker.io" +/caps-man aaa +set called-format=ssid +/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 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 +/caps-man datapath +add bridge=*3 client-to-client-forwarding=yes local-forwarding=yes name=\ + xs-DataPath +add bridge=*3 client-to-client-forwarding=yes local-forwarding=yes name=\ + DataPath +/caps-man manager +set ca-certificate=CAPsMAN-CA-51B25E507D07 certificate=CAPsMAN-51B25E507D07 \ + enabled=yes require-peer-certificate=yes +/caps-man manager interface +set [ find default=yes ] forbid=yes +add disabled=no interface=*DF +/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 crl-store=system +/container config +set layer-dir=/sata1 memory-high=200.0MiB memory-max=4096.0MiB registry-url=\ + https://registry-1.docker.io tmpdir=/sata1/tmp username=amsternl +/container envs +add key=HANDSHAKE_TIMEOUT list=amneziawg value=60 +add key=LOCAL_NET list=amneziawg value=10.9.0.0/24 +add key=PING_TARGET list=amneziawg value=8.8.8.8 +add key=AWG_ENDPOINT list=awgui-env value=10.30.30.10:443 +add key=SECRET_KEY list=awgui-env value=RanDomSecret32SymboLs2325272931q +add key=WEB_PASS list=awgui-env value=zoomzoomrulezz +add key=WEB_USER list=awgui-env value=vados +/container mounts +add dst=/config list=awg-conf src=/sata1/amnezia/amneziawg +/ip smb +set enabled=no +/interface bridge port +add bridge=*FC interface=*FB +add bridge=br-awg interface=veth-awg +add bridge=bridge2 interface=ether1 +/ipv6 settings +# ipv6 neighbor configuration has changed, please restart the device in order to apply the new 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 +/interface wifi cap +set discovery-interfaces=*DF +/interface wifi capsman +set package-path=/tmpfs/capsman-packages upgrade-policy=none +/interface wireguard peers +add allowed-address=0.0.0.0/0 endpoint-address=172.19.77.106 endpoint-port=\ + 51820 interface=wg-AWG-VDSina name=peer19 persistent-keepalive=10s \ + public-key="o5IgCl1/G6xbl6j7sPaP4ZNYREeYOmqZjKbm+v0b9jU=" +/ip address +add address=10.30.30.10/24 interface=ether1 network=10.30.30.0 +add address=10.9.9.1/24 interface=br-awg network=10.9.9.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=109.107.177.79,8.8.4.4 \ + verify-doh-cert=yes +/ip firewall nat +add action=masquerade chain=srcnat src-address=172.19.77.104/30 +add action=masquerade chain=srcnat src-address=10.9.9.0/24 +# in/out-interface matcher not possible when interface (ether1) is slave - use master instead (bridge2) +add action=dst-nat chain=dstnat dst-port=443 in-interface=ether1 protocol=udp \ + to-addresses=10.9.9.2 to-ports=55055 +# in/out-interface matcher not possible when interface (ether1) is slave - use master instead (bridge2) +add action=dst-nat chain=dstnat dst-port=9443 in-interface=ether1 protocol=\ + tcp to-addresses=10.9.9.2 to-ports=443 +/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 telnet disabled=yes +set www disabled=yes +set reverse-proxy disabled=yes +set ssh port=2222 +set www-ssl port=4443 +set api disabled=yes +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-plain=no rest-secure=no scep-plain=no \ + webfig-plain=no webfig-secure=no +/ip ssh +set forwarding-enabled=both password-authentication=no \ + 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 +/system clock +set time-zone-autodetect=no 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 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 +/tool netwatch +add disabled=yes down-script=\ + ":local msg \"Host MikroYellow is DOWN\";\r\ + \n\$TGSendMessage \$msg;" host=10.30.30.86 http-codes="" interval=10s \ + test-script="" type=simple up-script=\ + ":global TGSendMessage;\r\ + \n\$TGSendMessage \"Host MikroYellow is UP\";" +/user aaa +set accounting=no diff --git a/MikroNUC/history/20260629/20260629052001-Diff-Config.rsc b/MikroNUC/history/20260629/20260629052001-Diff-Config.rsc new file mode 100644 index 0000000..a4e09b8 --- /dev/null +++ b/MikroNUC/history/20260629/20260629052001-Diff-Config.rsc @@ -0,0 +1,11 @@ +25d24 +< add name=bridge2 +4787d4785 +< add bridge=bridge2 interface=ether1 +4794,4795d4791 +< /interface list member +< add interface=ether1 list=WAN +4820d4815 +< # in/out-interface matcher not possible when interface (ether1) is slave - use master instead (bridge2) +4823d4817 +< # in/out-interface matcher not possible when interface (ether1) is slave - use master instead (bridge2) -- 2.52.0