From: vados-dev <192440777+vados-dev@users.noreply.github.com> Date: Thu, 12 Mar 2026 09:29:19 +0000 (+0300) Subject: Backup for MikroNUC sucessfully creatd at Mar 12 12:29:19. X-Git-Url: https://git.vados.ru/gitweb.cgi?a=commitdiff_plain;h=a9f95b608f349bf44a703e8a1604257753db29e0;p=Mikrotiks%2Fbackups.git Backup for MikroNUC sucessfully creatd at Mar 12 12:29:19. --- diff --git a/MikroNUC/20260312/20260312122915-Config.rsc b/MikroNUC/20260312/20260312122915-Config.rsc new file mode 100644 index 0000000..e69de29 diff --git a/MikroNUC/history/20260312/20260312122915-Diff-Config.rsc b/MikroNUC/history/20260312/20260312122915-Diff-Config.rsc new file mode 100644 index 0000000..0296874 --- /dev/null +++ b/MikroNUC/history/20260312/20260312122915-Diff-Config.rsc @@ -0,0 +1,2780 @@ +0a1,2779 +> # 2026-03-12 11:32:28 by RouterOS 7.21.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=Bridge-DOC +> add name=Bridge-LAN port-cost-mode=short +> /interface ethernet +> set [ find default-name=ether1 ] disable-running-check=no name=ether2 +> /interface veth +> add address=172.18.20.6/30 container-mac-address=00:00:00:00:00:00 dhcp=no \ +> gateway=172.18.20.5 gateway6="" name=docker-xray-vless-veth +> add address=10.10.10.10/24 container-mac-address=5C:26:FE:05:6F:12 dhcp=no \ +> gateway=10.10.10.1 gateway6="" mac-address=5C:26:FE:05:6F:11 name=veth1 +> /caps-man datapath +> add bridge=Bridge-LAN client-to-client-forwarding=yes local-forwarding=yes \ +> name=xs-DataPath +> add bridge=Bridge-LAN client-to-client-forwarding=yes local-forwarding=yes \ +> name=DataPath +> /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 +> /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 +> /disk +> add slot=ramstorage tmpfs-max-size=100000000 type=tmpfs +> add slot=tmpfs tmpfs-max-size=715827882 type=tmpfs +> /interface list +> add include=dynamic,static name=LAN +> /interface wireless security-profiles +> set [ find default=yes ] supplicant-identity=MikroMIC +> /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 +> /user group +> add name=backup policy="telnet,ssh,ftp,read,write,policy,test,sniff,sensitive,\ +> !local,!reboot,!winbox,!password,!web,!api,!romon,!rest-api" +> /app settings +> set auto-update=yes disk=sata1 lan-bridge=Bridge-LAN 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 manager +> # bad package path +> set ca-certificate=CAPsMAN-CA-A2A7DF3D5EE6 certificate=CAPsMAN-A2A7DF3D5EE6 \ +> enabled=yes package-path=/tmpfs/capsman-packages +> /caps-man manager interface +> set [ find default=yes ] forbid=yes +> add disabled=no interface=Bridge-LAN +> /caps-man provisioning +> add action=create-dynamic-enabled hw-supported-modes=g,gn,b \ +> master-configuration=2G name-format=prefix-identity name-prefix=2G +> add action=create-dynamic-enabled hw-supported-modes=an,ac,a \ +> master-configuration=5G name-format=prefix-identity name-prefix=5G +> add action=create-dynamic-enabled disabled=yes hw-supported-modes=ac,an,a \ +> master-configuration=2G name-format=prefix-identity name-prefix=5ghz-ac +> add action=create-dynamic-enabled disabled=yes hw-supported-modes=an \ +> master-configuration=5G name-format=prefix-identity name-prefix=5ghz-an +> /certificate settings +> set builtin-trust-store=fetch crl-store=system +> /container config +> set layer-dir=/sata1 registry-url=https://reg.vados.ru tmpdir=/ramstorage \ +> username=amster-dev +> /container envs +> add key=ENV list=carkeeperbot_envs value=/root/.ashrc +> add key=PATH list=carkeeperbot_envs value=\ +> /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin +> add key=PORT list=carkeeperbot_envs value="\${SSH_PORT}" +> add key=SSH_PORT list=carkeeperbot_envs value=8222 +> /container mounts +> add dst=/app list=carkeeper_app src=/sata1/carkeeper/app +> add dst=/configs list=carkeeper_configs src=/sata1/carkeeper/configs +> add dst=/root/.ashrc list=carkeeper_configs/.profile src=\ +> /sata1/carkeeper/configs/.profile +> add dst=/etc/php82/php.ini list=carkeeper_configs/php.ini src=\ +> /sata1/carkeeper/configs/php.ini +> add dst=/etc/ssh/sshd_config list=carkeeper_configs/sshd_config src=\ +> /sata1/carkeeper/configs/sshd_config +> add dst=/scripts list=carkeeper_scripts src=/sata1/carkeeper/scripts +> add dst=/var/sync list=carkeeper_sync src=/sata1/carkeeper/sync +> add dst=/var/tmp list=carkeeper_tmp src=/sata1/carkeeper/tmp +> /interface bridge port +> add bridge=Bridge-LAN interface=ether2 +> add bridge=Bridge-DOC interface=veth1 +> add bridge=Bridge-LAN interface=LAN +> add bridge=Bridge-LAN interface=dynamic +> /ipv6 settings +> set disable-ipv6=yes disable-link-local-address=yes +> /interface detect-internet +> set detect-interface-list=LAN internet-interface-list=LAN lan-interface-list=\ +> LAN wan-interface-list=LAN +> /interface wifi capsman +> set package-path=/tmpfs/capsman-packages upgrade-policy=none +> /ip address +> add address=10.30.30.10/24 interface=Bridge-LAN network=10.30.30.0 +> add address=10.10.10.1/24 interface=Bridge-DOC network=10.10.10.0 +> add address=172.18.20.5/30 interface=docker-xray-vless-veth network=\ +> 172.18.20.4 +> /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=Bridge-LAN \ +> lease-script=lease-script lease-time=10m name=nucDHCP server-address=\ +> 10.30.30.10 +> /ip dns +> set doh-max-concurrent-queries=100 doh-max-server-connections=20 servers=\ +> 10.30.30.81 verify-doh-cert=yes +> /ip firewall filter +> add action=accept chain=input +> add action=accept chain=output +> add action=accept chain=forward comment="Comment of the rule" disabled=yes +> /ip firewall mangle +> # docker-xray-vless-veth not ready +> add action=change-mss chain=forward new-mss=1360 out-interface=\ +> docker-xray-vless-veth protocol=tcp tcp-flags=syn tcp-mss=1420-65535 +> /ip firewall nat +> # docker-xray-vless-veth not ready +> add action=masquerade chain=srcnat out-interface=docker-xray-vless-veth +> /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 disabled=no dst-address=0.0.0.0/0 gateway=10.30.30.81 routing-table=main +> /ip service +> set ftp disabled=yes +> set www 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 allow-disable-external-interface=yes enabled=yes +> /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 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="Backup Every Week" interval=1w name=BackupEveryWeek on-event=\ +> "/system/script { run BackupAndUpdate; }" policy=\ +> ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \ +> start-date=2025-07-07 start-time=04:41:27 +> add comment="Setup Global Environment for scripts" name=Amster-SetGlobalEnv \ +> on-event=\ +> "/system/script { run Amster-GlobalConfig; run Amster-GlobalFunc; }" \ +> policy=ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \ +> start-time=startup +> add comment="Every Day Check For Update Scripts" interval=1d name=\ +> Amster-GetInstallUpdate on-event=":global getInstUpd; \$getInstUpd;" \ +> policy=ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \ +> start-time=startup +> add comment="Every Day Check For Update Scripts" interval=1d name=\ +> ScriptInstallUpdate on-event=\ +> ":global ScriptInstallUpdate; \$ScriptInstallUpdate;" policy=\ +> ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \ +> start-time=startup +> add comment="Setup Global Environment for scripts" 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" interval=1d name=\ +> AM-ScriptInstallUpdate on-event=\ +> ":global ScriptInstallUpdate; \$ScriptInstallUpdate;" policy=\ +> ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \ +> start-time=startup +> /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 \"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 scrypt 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 scrypt 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#:foreach Script in=[ /system/script/find where source~\"^#!rsc by Vados\ +> \" ] do={\r\ +> \n# if (\$ScriptUpdatesCRLF = true) do={\r\ +> \n# /system/script/set source=[ :tocrlf [ get \$Script source ] ] \$Scri\ +> pt;\r\ +> \n# } else={\r\ +> \n# /system/script/set source=[ :tolf [ get \$Script source ] ] \$Script\ +> ;\r\ +> \n# }\r\ +> \n#}\r\ +> \n# /import verbose=no \$ScriptName\r\ +> \n:global SetupScript [ :jobname ];\r\ +> \n:global RemoveMe;\r\ +> \n:global InitSetup true;\r\ +> \n:global tmpfs true;\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 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 :local CertSettings [ /certificate/settings/get ];\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 \r\ +> \n /tool/fetch (\$GlobalScriptsUrl . \"certs/\" . \$CertFileName) dst-p\ +> ath=\$CertFileName as-value;\r\ +> \n :delay 1s;\r\ +> \n /certificate/import file-name=\$CertFileName passphrase=\"\";\r\ +> \n :if ([ :len [ /certificate/find where fingerprint=\$CertFingerprint \ +> ] ] != 1) do={\r\ +> \n :error \"Something is wrong with your certificates!\";\r\ +> \n };\r\ +> \n :delay 1s;\r\ +> \n };\r\ +> \n :put \"Thr tmpfs variable is true. Checking for tmpfs/scripts director\ +> y...\";\r\ +> \n :if (\$tmpfs = true && [/file find name=\"tmpfs/scripts\"] != \"\") do\ +> ={\r\ +> \n :log debug (\$0 . \"Directory tmpfs/scripts exsist.\");\r\ +> \n :return \$true;\r\ +> \n } else={\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 :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 :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 GlobalConfReady false;\r\ +> \n# \\/ start edit\r\ +> \n#\r\ +> \n# Global Url For Amster scripts to fetch\r\ +> \n# most be defined in Amster-Init-Script\r\ +> \n:global GlobalScriptsUrl \"https://ros.vados.ru/\";\r\ +> \n:global ScriptsUrlSuffix \"\";\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\"; #: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# 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#\r\ +> \n#\r\ +> \n# requires RouterOS, version=7.21\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/80/71f092a6\";\r\ +> \n:global CommitTAG \"unknown\";\r\ +> \n:global CommitInfo \"Autocommit in repo: ros.vados.ru, generated by owne\ +> r: root, in 14:51:02 09.03.2026.\";\r\ +> \n\r\ +> \n# The variables are received right here.\r\ +> \n:global GlobalConfVersion 138;\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 GlobalEnvRemove;\r\ +> \n:global MiniDateTimeStamp;\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 ExitError;\r\ +> \n:global CleanFilePath;\r\ +> \n:global CleanName;\r\ +> \n:global CharacterReplace;\r\ +> \n:global CharacterMultiply;\r\ +> \n:global EitherOr;\r\ +> \n:global FormatLine;\r\ +> \n:global GetRandom20CharAlNum;\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 WaitDNSResolving;\r\ +> \n:global WaitFullyConnected;\r\ +> \n:global WaitTimeSync;\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 FetchLargeFiles;\r\ +> \n:global FetchUserAgentStr;\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 LogPrint;\r\ +> \n :global updateChannel;\r\ +> \n :global PkgUpdStatus;\r\ +> \n \$LogPrint debug \$0 (\"\\\$isInfo = \" . \$isInfo); \r\ +> \n :local CurrentChannel [ /system/package/update/get channel ];\r\ +> \n :if (\$CurrentChannel != \$updateChannel) do={\r\ +> \n :retry command={/system/package/update set channel=\$updateChannel} \ +> \_delay=300ms max=5 on-error={};\r\ +> \n \$LogPrint debug \$0 (\"Set updates channel to: \$updateChannel\");\ +> \r\ +> \n }\r\ +> \n \r\ +> \n :local Update [ /system/package/update/get ];\r\ +> \n :local Channel (\$Update->\"channel\");\r\ +> \n \$LogPrint debug \$0 (\"Update Channel: \" . \$Channel . \".\");\r\ +> \n \r\ +> \n :local StatusPkg \"undefined\";\r\ +> \n \$LogPrint debug \$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 \$LogPrint debug \$0 (\"\\\$StatusPkg = \" . \$StatusPkg);\r\ +> \n :if (\$StatusPkg = \"New version is available\") do={ :set PkgUpdStatu\ +> s true; } else={ :set PkgUpdStatus false; }\r\ +> \n \$LogPrint debug \$0 (\"\\\$PkgUpdStatus = \" . \$PkgUpdStatus);\r\ +> \n :if (\$isInfo != false || \$PkgUpdStatus = true) do={\r\ +> \n \$LogPrint debug \$0 (\"\\\$PkgUpdStatus = \" . \$PkgUpdStatus);\r\ +> \n :if ([:len \$arrDeviceInfo] = 0) do={\r\ +> \n :global arrDeviceInfo ({});\r\ +> \n :local arrDeviceInfo ({\"section\":{\"key\":\"\"; \"value\":\"\";};}\ +> );\r\ +> \n }\r\ +> \n # else={:local arrDeviceInfo ({})}\r\ +> \n :retry command={:set (\$arrDeviceInfo->\"devUpdates\") [[ :parse \"/\ +> system/package/update/get\" ]]} delay=500ms max=5 on-error={:log error (\"\ +> Error\")};\r\ +> \n #:set (\$arrDeviceInfo->\"devUpdates\") {\"devCurrentChannel\"; \$Cu\ +> rrentChannel;};\r\ +> \n #:set (\$arrDeviceInfo->\"devUpdates\") {\"devCurrentVersion\"; (\$U\ +> pdate->\"installed-version\");};\r\ +> \n #:set (\$arrDeviceInfo->\"devUpdates\") {\"devCurrentChannelVersion\ +> \"; [/system/resource/get version];};\r\ +> \n #:set (\$arrDeviceInfo->\"devUpdates\") {\"devUpdateChannel\"; [\$up\ +> dateChannel];};\r\ +> \n #:set (\$arrDeviceInfo->\"devUpdates\") {\"devUpdateVersion\"; (\$Up\ +> date->\"latest-version\");};\r\ +> \n } else={\r\ +> \n :return \$PkgUpdStatus;\r\ +> \n }\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\r\ +> \n :global devBoardName;\r\ +> \n :global devModel;\r\ +> \n \r\ +> \n :global arrDeviceInfo [:toarray {\"section\"={\"key\"=\"\"; \"value\"\ +> =\"\";} }];\r\ +> \n #:do {:set (\$arrDeviceInfo->\"Updates\") [[ :parse \"/system/package\ +> /update/get\" ]]} on-error={}\r\ +> \n :global devRbRevision;\r\ +> \n :global devRbSerialNumber;\r\ +> \n :global devRbCurrentFw;\r\ +> \n :global devRbUpgradeFw;\r\ +> \n \r\ +> \n :global devRunning;\r\ +> \n :global devCurArch;\r\ +> \n :global ROsVerCurrent;\r\ +> \n :global ROsChannelCurrent;\r\ +> \n :global ROsVerAvail;\r\ +> \n\r\ +> \n :global ParseCustom;\r\ +> \n :global runningChannelVersion;\r\ +> \n :global runningChannel;\r\ +> \n :global runningVersion;\r\ +> \n :global devBuildTime;\r\ +> \n :global GetPubIp;\r\ +> \n \r\ +> \n :local License [/system/license/get];\r\ +> \n :local Resource [/system/resource/get];\r\ +> \n :set devBuildTime (\$Resource->\"build-time\");\r\ +> \n :set runningChannelVersion (\$Resource->\"version\");\r\ +> \n :set runningChannel [\$ParseCustom \$runningChannelVersion \"(\" \")\ +> \" 1];\r\ +> \n :set runningVersion [\$ParseCustom \$runningChannelVersion \" \"];\r\ +> \n \r\ +> \n :do {\r\ +> \n :if ([:pick \$devBoardName 0 3] != \"CHR\" or [:pick \$devBoardName 0\ +> \_3] != \"x86\") do={\r\ +> \n :local RouterBoard;\r\ +> \n :do {:set RouterBoard [[ :parse \"/system/routerboard/get\" ]]} on\ +> -error={}\r\ +> \n :set devModel (\$RouterBoard->\"model\");\r\ +> \n :set devRbRevision (\$RouterBoard->\"revision\");\r\ +> \n :set devRbSerialNumber (\$RouterBoard->\"serial-number\");\r\ +> \n #:set devRbCurrentFw (\$RouterBoard->\"current-firmware\");\r\ +> \n #:set devRbUpgradeFw (\$RouterBoard->\"upgrade-firmware\");\r\ +> \n :if (\$devCurArch = \"mipsbe\" || \$devCurArch = \"arm\") do={\r\ +> \n :global devRunning [ /system/device-mode/get mode ];\r\ +> \n } else={\r\ +> \n :global devRunning [/system/device-mode/get running];}\r\ +> \n } else={\r\ +> \n :set devModel \$devBoardName;\r\ +> \n \r\ +> \n :set devRbRevision false;\r\ +> \n :set devRbSerialNumber false;\r\ +> \n# :set devRbCurrentFw false;\r\ +> \n# :set devRbUpgradeFw false;\r\ +> \n #:set devCurrentFw \$runningVersion;#:set devUpgradeFw \$ROsVerAvail\ +> ;\r\ +> \n :set devRunning [/system/device-mode/get running];\r\ +> \n }\r\ +> \n } on-error={\r\ +> \n \$LogPrint error \$0 (\"Error in Function getDevice!\");\r\ +> \n :return false;\r\ +> \n }\r\ +> \n:return true;\r\ +> \n}\r\ +> \n\r\ +> \n:set DeviceInfo do={\r\ +> \n :global WaitArrDeviceInfo;\r\ +> \n # \$WaitDeviceInfo;\r\ +> \n :global devBoardName;\r\ +> \n :global devCurArch;\r\ +> \n :global devModel;\r\ +> \n :global devRunning;\r\ +> \n :global devRbRevision;\r\ +> \n :global devRbSerialNumber;\r\ +> \n :global PiblicIpAddress;\r\ +> \n\r\ +> \n :global GlobalConfVersion;\r\ +> \n :global Identity;\r\ +> \n :global IfThenElse;\r\ +> \n :global FormatLine;\r\ +> \n :local Return;\r\ +> \n :local License [/system/license/get];\r\ +> \n :local Resource [/system/resource/get];\r\ +> \n :local Snmp [ /snmp/get ];\r\ +> \n :local Update [ /system/package/update/get ];\r\ +> \n \r\ +> \n :set Return ([ \$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:\" . \$devBoardName ] . \"\\n\" . \\\r\ +> \n [ \$FormatLine \" Arch:\" . \$devCurArch ] . \"\\n\" . \\\r\ +> \n [ \$FormatLine \" Model:\" \$devModel ] . \\\r\ +> \n [ \$IfThenElse ([ :len \$devRbRevision ] > 0) \\\r\ +> \n (\" \" . \$devRbRevision) ] . \"\\n\" . \\\r\ +> \n [ \$IfThenElse ([ :len \$devRbSerialNumber ] > 0) \\\r\ +> \n ([ \$FormatLine \" Serial:\" . \$devRbSerialNumber ] . \"\\n\"\ +> ) ] . \\\r\ +> \n [ \$IfThenElse ([ :len (\$License->\"nlevel\") ] > 0) \\\r\ +> \n ([ \$FormatLine \" License:\" (\"level \" . (\$License->\"nleve\ +> l\")) ] . \"\\n\") ] . \"RouterOS:\\n\" . \\\r\ +> \n [ \$IfThenElse ([ :len (\$License->\"level\") ] > 0) \\\r\ +> \n ([ \$FormatLine \" License:\" (\"level \" . (\$License->\"level\ +> \")) ] . \"\\n\") ] . \\\r\ +> \n [ \$FormatLine \"Running Channel\" \$runningChannel ] . \"\\n\" . \\\ +> \r\ +> \n [ \$FormatLine \" Installed:\" (\$Update->\"installed-version\") \ +> ] . \"\\n\" . \\\r\ +> \n [ \$IfThenElse ([ :typeof (\$Update->\"latest-version\") ] != \"noth\ +> ing\" && \\\r\ +> \n \$Update->\"installed-version\" != \$Update->\"latest-version\")\ +> \_\\\r\ +> \n ([ \$FormatLine \" Available\" (\$Update->\"latest-version\") ]\ +> \_. \"\\n\") ] . \\\r\ +> \n [ \$IfThenElse (\$RouterBoard->\"routerboard\" = true && \\\r\ +> \n \$RouterBoard->\"current-firmware\" != \$RouterBoard->\"upgrade-\ +> firmware\") \\\r\ +> \n ([ \$FormatLine \" Firmware\" (\$RouterBoard->\"current-firmwar\ +> e\") ] . \"\\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: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: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 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# !!! The function is not ready !!!\r\ +> \n:global FetchHttpInfo do={\r\ +> \n :local Mac [ :tostr \$1 ];\r\ +> \n :global CertificateAvailable;\r\ +> \n :global IsMacLocallyAdministered;\r\ +> \n :global LogPrint;\r\ +> \n :local Url \"https://api.macvendors.com/\";\r\ +> \n :if ([ \$IsMacLocallyAdministered \$Mac ] = true) do={:return \"locall\ +> y administered\";}\r\ +> \n :do {\r\ +> \n :if ([ \$CertificateAvailable \"GTS Root R4\" \"fetch\" ] = false) d\ +> o={\r\ +> \n \$LogPrint warning \$0 (\"Downloading required certificate failed.\ +> \");\r\ +> \n :error false;\r\ +> \n }\r\ +> \n :local Vendor ([ /tool/fetch \r\ +> \n (\$Url . [ :pick \$Mac 0 8 ]) output=user as-value ]->\"data\");\r\ +> \n :return \$Vendor;\r\ +> \n } on-error={\r\ +> \n :onerror Err {\r\ +> \n /tool/fetch check-certificate=yes-without-crl (\$Url) output=none \ +> as-value;\r\ +> \n \$LogPrint debug \$0 (\"The mac vendor is not known in database.\"\ +> );\r\ +> \n } do={ \$LogPrint warning \$0 (\"Failed getting mac vendor: \" . \$E\ +> rr); }\r\ +> \n :return \"unknown vendor\";\r\ +> \n }\r\ +> \n}\r\ +> \n\r\ +> \n:set GetPubIp do={\r\ +> \n :global DetectIpAddrDefault;\r\ +> \n :global DetectIpAddrFallback;\r\ +> \n :global LogPrint;\r\ +> \n \r\ +> \n :local PubIpAddr false;\r\ +> \n :local Url \$DetectIpAddrDefault;\r\ +> \n\r\ +> \n# :retry command={abc} delay=1 max=2 on-error={:put \"got error\"} \r\ +> \n :do {\r\ +> \n \$LogPrint debug \$0 (\"Trying to detect public IP using default s\ +> ervice: \" . \$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={\$LogPrint error [ :jobname ] (\"Fail\ +> ed to detect public IP using default service: \" . \$Url . \".\")}\r\ +> \n #:error \"Failed to detect public IP using default service: \$Url\ +> .\"};\r\ +> \n } on-error={\r\ +> \n \$LogPrint error \$0 (\"Failed to detect public IP using default s\ +> ervice: \" . \$Url . \".\");\r\ +> \n :set Url \$DetectIpAddrFallBack;\r\ +> \n \$LogPrint debug \$0 (\"Trying to detect public IP using fallback \ +> service: \" . \$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={\$LogPrint error [ :jobname ] (\"Fai\ +> led to detect public IP using fallback service: \" . \$Url . \".\")}\r\ +> \n };\r\ +> \n :if (\$PubIpAddr = false) do={\r\ +> \n :set PubIpAddr \"not-detected\";\r\ +> \n } else={\r\ +> \n :set PubIpAddr [ :pick \$PubIpAddr 0 15];\r\ +> \n }\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 :global CertificateDownload;\r\ +> \n :global EitherOr;\r\ +> \n :global LogPrint;\r\ +> \n :global ParseKeyValueStore;\r\ +> \n :set UseFor [ \$EitherOr \$UseFor \"undefined\" ];\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 \$LogPrint 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 \$LogPrint 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 \$LogPrint info \$0 (\"Certificate with CommonName '\" . \$CommonNam\ +> e . \"' not available.\");\r\ +> \n :if ([ \$CertificateDownload \$CommonName ] = false) do={:return fal\ +> se}\r\ +> \n }\r\ +> \n :if ([ :len [ /certificate/find where common-name=\$CommonName ] ] > 1\ +> ) do={\r\ +> \n \$LogPrint 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 \$LogPrint info \$0 (\"Certificate chain for '\" . \$CommonName . \ +> \"' is incomplete, missing '\" . \$IssuerCN . \"'.\");\r\ +> \n :if ([ \$CertificateDownload \$IssuerCN ] = false) do={:return fal\ +> se}\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 GlobalScriptsUrl;\r\ +> \n :global GlobalScriptsUrlSuffix;\r\ +> \n :global CertificateNameByCN;\r\ +> \n :global CleanName;\r\ +> \n :global FetchUserAgentStr;\r\ +> \n :global LogPrint;\r\ +> \n :global RmFile;\r\ +> \n :global WaitForFile;\r\ +> \n \r\ +> \n \$LogPrint info \$0 (\"Downloading and importing certificate with Comm\ +> onName '\" . \$CommonName . \"'.\");\r\ +> \n \r\ +> \n :local FileName ([ \$CleanName \$CommonName ] . \".pem\");\r\ +> \n :do {\r\ +> \n /tool/fetch check-certificate=yes-without-crl http-header-field=({ [\ +> \_\$FetchUserAgentStr \$0 ] }) \\\r\ +> \n (\$GlobalScriptsUrl . \"certs/\" . \$FileName . \$GlobalScriptsUrl\ +> Suffix) dst-path=\$FileName as-value;\r\ +> \n \$WaitForFile \$FileName;\r\ +> \n } on-error={\r\ +> \n \$LogPrint warning \$0 (\"Failed downloading certificate with Common\ +> Name '\" . \$CommonName . \\\r\ +> \n \"' from repository! Trying fallback to mkcert.org...\");\r\ +> \n :do {\r\ +> \n :if ([ :len [ /certificate/find where common-name=\"ISRG Root X1\"\ +> \_] ] = 0) do={\r\ +> \n \$LogPrint error \$0 (\"Required certificate is not available.\"\ +> );\r\ +> \n :return false;\r\ +> \n }\r\ +> \n /tool/fetch check-certificate=yes-without-crl http-header-field=({\ +> \_[ \$FetchUserAgentStr \$0 ] }) \\\r\ +> \n \"https://mkcert.org/generate/\" http-data=[ :serialize to=json \ +> ({ \$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 \$LogPrint warning \$0 (\"Failed downloading certificate with Comm\ +> onName '\" . \$CommonName . \"'!\");\r\ +> \n :return false;\r\ +> \n }\r\ +> \n }\r\ +> \n\r\ +> \n /certificate/import file-name=\$FileName passphrase=\"\" as-value;\r\ +> \n :delay 1s;\r\ +> \n \$RmFile \$FileName;\r\ +> \n\r\ +> \n :if ([ :len [ /certificate/find where common-name=\$CommonName ] ] = 0\ +> ) do={\r\ +> \n /certificate/remove [ find where name~(\"^\" . \$FileName . \"_[0-9]\ +> +\\\$\") ];\r\ +> \n \$LogPrint warning \$0 (\"Certificate with CommonName '\" . \$Common\ +> Name . \"' 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 :global LogPrint;\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 \$LogPrint 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 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 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 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={:set NotificationMessage (\$NotificationMessage . \"\\n\\nNe\ +> ws and changes are not available.\")}\r\ +> \n\r\ +> \n :set GlobalConfChanges;\r\ +> \n :set GlobalConfMigration;\r\ +> \n }\r\ +> \n} do={:global ExitOnError; \$ExitOnError \$0 \$Err}\r\ +> \n}\r\ +> \n\r\ +> \n:set FetchUserAgentStr do={\r\ +> \n :local Caller [ :tostr \$1 ];\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 (https://amster.vados.ru/; main/3419/3ee\ +> 19780)\");\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 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 GetRandomNumber do={\r\ +> \n :global EitherOr;\r\ +> \n :return [ :rndnum from=0 to=[ \$EitherOr [ :tonum \$1 ] 4294967295 ] ]\ +> \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: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 :global LogPrint;\r\ +> \n :if (\$ExitOK = \"false\") do={\r\ +> \n \$LogPrint 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 MiniDateTimeStamp do={\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 :return ([\$yyyy] . [\$MM] . [\$dd] . [\$HH] . [\$mm] . [\$ss]);\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={:return false}\r\ +> \n :local FileVal false;\r\ +> \n :do {: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 :global LogPrint;\r\ +> \n \$LogPrint debug \$0 (\"Removing directory: \". \$DirName);\r\ +> \n :local DirVal [ \$FileGet \$DirName ];\r\ +> \n :if (\$DirVal = false) do={\$LogPrint debug \$0 (\"... which does not \ +> exist.\"); :return true};\r\ +> \n :if (\$DirVal->\"type\" != \"directory\") do={\$LogPrint error \$0 (\"\ +> Directory '\" . \$DirName . \"' is not a directory.\"); :return false};\r\ +> \n :onerror Err {/file/remove \$DirName} do={\$LogPrint error \$0 (\"Remo\ +> ving directory '\" . \$DirName . \"' failed: \" . \$Err); :return false};\ +> \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 :global LogPrint;\r\ +> \n \$LogPrint debug \$0 (\"Removing file: \". \$FileName);\r\ +> \n :local FileVal [ \$FileGet \$FileName ];\r\ +> \n :if (\$FileVal = false) do={\$LogPrint debug \$0 (\"... which does not\ +> \_exist.\"); :return true}\r\ +> \n :if (\$FileVal->\"type\" = \"directory\" || \$FileVal->\"type\" = \"di\ +> sk\") do={\r\ +> \n \$LogPrint error \$0 (\"File '\" . \$FileName . \"' is not a file.\"\ +> ); :return false}\r\ +> \n :onerror Err {\r\ +> \n /file/remove \$FileName;\r\ +> \n } do={\$LogPrint error \$0 (\"Removing file '\" . \$FileName . \"' fai\ +> led: \" . \$Err); :return false}\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 :global LogPrint;\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 \$LogPrint info \$0 (\"The tmpfs is disabled, enabling.\");\r\ +> \n /disk/enable \$TmpFs;\r\ +> \n }\r\ +> \n :return true;\r\ +> \n }\r\ +> \n \$LogPrint 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 \$LogPrint 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 \$LogPrint debug \$0 (\"Making directory: \" . \$Path);\r\ +> \n :local PathVal [ \$FileGet \$Path ];\r\ +> \n :if (\$PathVal->\"type\" = \"directory\") do={\r\ +> \n \$LogPrint 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 \$LogPrint 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 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 [ :len\ +> \_\$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 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 LogPrint;\r\ +> \n :global VersionToNum;\r\ +> \n :if (!(\$Required ~ \"^\\\\d+\\\\.\\\\d+((alpha|beta|rc|\\\\.)\\\\d+|)\ +> \\\$\")) do={\r\ +> \n \$LogPrint 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 \$LogPrint warning \$0 (\"This \" . [ \$IfThenElse ([ :pick \$Call\ +> er 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: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: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 LogPrint;\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 \$LogPrint error \$0 (\"A script named '\" . \$Script . \"' does not\ +> \_exist!\");\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 \$LogPrint error \$0 (\"More tickets than running scripts '\" . \$Sc\ +> ript . \"', 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: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: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:set SymbolByUnicodeName do={\r\ +> \n :local Name [ :tostr \$1 ];\r\ +> \n :global EitherOr;\r\ +> \n :global LogPrint;\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 \$LogPrint 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: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: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: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:set IsDNSResolving do={\r\ +> \n :do {:resolve \"api.telegram.org\"} on-error={:return false}\r\ +> \n :return true;\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: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# read parts of a large file\r\ +> \n:set FetchLargeFiles do={\r\ +> \n :local ScriptName [:tostr \$1];\r\ +> \n :local fetchUrl [:tostr \$2];\r\ +> \n :local CheckCert [:tostr \$3];\r\ +> \n :local fetchMode [:tostr \$4];\r\ +> \n :local fetchMethod [:tostr \$5];\r\ +> \n :local FetchString \"undefined\";\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 #:set fetchMode [ \$IfThenElse (\$fetchMode != \"false\") [:tostr (\"m\ +> ode=\" . \$fetchMode)] ];\r\ +> \n #:set fetchMethod [\$IfThenElse (\$fetchMethod != \"false\") [:tostr (\ +> \"http-method=\\\"\" . \$fetchMethod . \"\\\"\") ]];\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 :local FileName (\$DirName . \"/\" . [ \$CleanName \$0 ] . \"-\" . [ \ +> \$GetRandom20CharAlNum ]);\r\ +> \n #/tool/fetch check-certificate=\$CheckCert \$Url dst-path=\$FileName h\ +> ttp-header-field=({ [ \$FetchUserAgentStr \$ScriptName ] }) as-value;\r\ +> \n #:log warning (\"\\\$fUrl =\" . \$2 . ({[ \$fMode ]}));\r\ +> \n :onerror Err {\r\ +> \n :set FetchString (\"/tool/fetch \" . \\\r\ +> \n [\$IfThenElse ([\$fetchMethod] != \"false\") (\"http-method=\\\"\"\ +> \_. \$fetchMethod . \"\\\" \")] . \\\r\ +> \n \"check-certificate=\" . \$CheckCert . \" url=(\\\"\" . \$fetchUrl\ +> \_. \"\\\") \" . \\\r\ +> \n [\$IfThenElse ([\$fetchMode] != \"false\") (\"mode=\" . \$fetchMod\ +> e . \" \")] . \\\r\ +> \n http-header-field=({ [ \$FetchUserAgentStr \$ScriptName ] }) as-va\ +> lue);\r\ +> \n :log warning (\"\\\$FetchSring = \" . \$FetchString);\r\ +> \n :put [\$FetchString];\r\ +> \n #:put [/tool/fetch \$fetchMode check-certificate=\$CheckCert url=(\$\ +> fetchUrl) dst-path=(\$FileName) http-header-field=({ [ \$FetchUserAgentStr\ +> \_\$ScriptName ] }) as-value];\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 \" . \$fetchUrl . \"\ +> \_- \" . \$Err);\r\ +> \n \$RmDir \$DirName;\r\ +> \n :return false;\r\ +> \n }\r\ +> \n :if ([ \$WaitForFile \$FileName 5s ] = false) do={\r\ +> \n \$LogPrint debug \$0 (\"The file downloaded from \" . \$fetchUrl . \ +> \" did not show up.\");\r\ +> \n :return false;\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# Global Environment clear. Use: \"\$GlobalEnvRemove True;\".\r\ +> \n:set GlobalEnvRemove do={\r\ +> \n :local doRmEnv [ :tostr \$1 ];\r\ +> \n :global GlobalScriptsUrl;\r\ +> \n :local ScriptsUrl;\r\ +> \n :global LogPrint;\r\ +> \n :if (\$doRmEnv != \"True\") do={\r\ +> \n \$LogPrint error \"GlobalEnvRemove\" (\"This script is needed \\\"T\ +> rue\\\" for confirm to remove global environment variables!\");\r\ +> \n :return [];\r\ +> \n :error true; \r\ +> \n } else={\r\ +> \n :set ScriptsUrl \$GlobalScriptsUrl;\r\ +> \n :delay 5;\r\ +> \n :foreach var in=[/system/script/environment print as-value] do={/sy\ +> stem/script/environment remove (\$var->\".id\")}\r\ +> \n :delay 5;\r\ +> \n :global GlobalScriptsUrl \$ScriptsUrl;\r\ +> \n \$LogPrint warning \"GlobalEnvRemove\" (\"Done!\");\r\ +> \n :return [];\r\ +> \n :error false;\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 \"Setup Global Environment for scripts\";\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#:local Resource [ /system/resource/get ];\r\ +> \n#:log info (\$ScriptName . \" Loaded on \" . \$Resource->\"board-name\" \ +> . \" with RouterOS \" . \$Resource->\"version\" . \" and Architecture \" .\ +> \".\");\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# signal we are ready\r\ +> \n:set GlobalFuncReady true;\r\ +> \n:set ExitOK true;\r\ +> \n:log warning (\"This script \$ScriptName Loaded on \" . \$devBoardName .\ +> \_\"\\nRouterOS: \" . \$devCurVersion . \"\\nArchitecture: \" . \$devCurAr\ +> ch . \"\\nIdentity: \" . \$Identity);\r\ +> \n} do={:global ExitOnError; \$ExitOnError \$0 \$Err;}\r\ +> \n\r\ +> \n#} do={:global ExitError; \$ExitError \$ExitOK [ :jobname ] \$Err}\r\ +> \n# log the successful load of the script, or an error if it failed\r\ +> \n#:if ([\$GlobalFuncReady] = true && [\$GlobalConfReady] = true) do={\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\r\ +> \n#root.mnuc.backuper\r\ +> \n:global GlobalScriptsUrl \"https://ros.vados.ru/\";\r\ +> \n/tool/fetch mode=https url=(\$GlobalScriptsUrl . \".bak/mikro_bak_rsa@10\ +> .30.30.10.pub\") dst-path=\"/mikro_bak_rsa@10.30.30.10.pub\";\r\ +> \n#/usb1/mikro_bak_rsa.pub" +> 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" +> /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