diff --git a/CERTIFICATES.md b/CERTIFICATES.md index 69d6c18..0e0a867 100644 --- a/CERTIFICATES.md +++ b/CERTIFICATES.md @@ -61,7 +61,7 @@ Import a certificate by CommonName Running the function `$CertificateAvailable` with that name as parameter makes sure the certificate is available in the device's store: - $CertificateAvailable "ISRG Root X2"; + $CertificateAvailable "ISRG Root X2" "fetch"; If the certificate is actually available already nothing happens, and there is no output. Otherwise the certificate is downloaded and imported. diff --git a/CONTRIBUTIONS.md b/CONTRIBUTIONS.md index 5bf5d08..00861c1 100644 --- a/CONTRIBUTIONS.md +++ b/CONTRIBUTIONS.md @@ -35,6 +35,7 @@ Add yourself to the list, [donate with PayPal â†—ī¸](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=A4ZXBD6YS2W8J)! * Abdul Mannan Abbasi +* Alex Maier * Andrea Ruffini Perico * Andrew Cox * Christoph Boss (@Kampfwurst) diff --git a/INITIAL-COMMANDS.md b/INITIAL-COMMANDS.md index 79773bd..e580bc5 100644 --- a/INITIAL-COMMANDS.md +++ b/INITIAL-COMMANDS.md @@ -17,33 +17,39 @@ Initial commands Run the complete base installation: { - :local BaseUrl "https://git.eworm.de/cgit/routeros-scripts/plain/"; + :local BaseUrl "https://rsc.eworm.de/main/"; + :local CertCommonName "ISRG Root X2"; :local CertFileName "ISRG-Root-X2.pem"; :local CertFingerprint "69729b8e15a86efc177a57afb7171dfc64add28c2fca8cf1507e34453ccb1470"; - :put "Importing certificate..."; - /tool/fetch ($BaseUrl . "certs/" . $CertFileName) dst-path=$CertFileName as-value; - :delay 1s; - /certificate/import file-name=$CertFileName passphrase=""; - :if ([ :len [ /certificate/find where fingerprint=$CertFingerprint ] ] != 1) do={ - :error "Something is wrong with your certificates!"; + :local CertSettings [ /certificate/settings/get ]; + :if (!((($CertSettings->"builtin-trust-anchors") = "trusted" || \ + ($CertSettings->"builtin-trust-store") ~ "fetch" || \ + ($CertSettings->"builtin-trust-store") = "all") && \ + [[ :parse (":return [ :len [ /certificate/builtin/find where common-name=\"" . $CertCommonName . "\" ] ]") ]] > 0)) do={ + :put "Importing certificate..."; + /tool/fetch ($BaseUrl . "certs/" . $CertFileName) dst-path=$CertFileName as-value; + :delay 1s; + /certificate/import file-name=$CertFileName passphrase=""; + :if ([ :len [ /certificate/find where fingerprint=$CertFingerprint ] ] != 1) do={ + :error "Something is wrong with your certificates!"; + }; + :delay 1s; }; - :delay 1s; :put "Renaming global-config-overlay, if exists..."; /system/script/set name=("global-config-overlay-" . [ /system/clock/get date ] . "-" . [ /system/clock/get time ]) [ find where name="global-config-overlay" ]; :foreach Script in={ "global-config"; "global-config-overlay"; "global-functions" } do={ :put "Installing $Script..."; /system/script/remove [ find where name=$Script ]; - /system/script/add name=$Script owner=$Script source=([ /tool/fetch check-certificate=yes-without-crl ($BaseUrl . $Script . ".rsc") output=user as-value]->"data"); + /system/script/add name=$Script owner=$Script source=([ /tool/fetch check-certificate=yes-without-crl ($BaseUrl . $Script . ".rsc") output=user as-value ]->"data"); }; :put "Loading configuration and functions..."; /system/script { run global-config; run global-functions; }; - :put "Scheduling to load configuration and functions..."; - /system/scheduler/remove [ find where name="global-scripts" ]; - /system/scheduler/add name="global-scripts" start-time=startup on-event="/system/script { run global-config; run global-functions; }"; - :put "Renaming certificate by its common-name..."; - :global CertificateNameByCN; - $CertificateNameByCN $CertFingerprint; + :if ([ :len [ /certificate/find where fingerprint=$CertFingerprint ] ] > 0) do={ + :put "Renaming certificate by its common-name..."; + :global CertificateNameByCN; + $CertificateNameByCN $CertFingerprint; + }; }; Then continue setup with diff --git a/Makefile b/Makefile index 8951741..5db0a30 100644 --- a/Makefile +++ b/Makefile @@ -2,38 +2,45 @@ # template scripts -> final scripts # markdown files -> html files -CAPSMAN = $(wildcard *.capsman.rsc) -LOCAL = $(wildcard *.local.rsc) -WIFI = $(wildcard *.wifi.rsc) +ALL_RSC := $(wildcard *.rsc */*.rsc) +GEN_RSC := $(wildcard *.capsman.rsc *.local.rsc *.wifi.rsc) -MARKDOWN = $(wildcard *.md doc/*.md doc/mod/*.md) -HTML = $(MARKDOWN:.md=.html) +MARKDOWN := $(wildcard *.md doc/*.md doc/mod/*.md) +HTML := $(MARKDOWN:.md=.html) -all: $(CAPSMAN) $(LOCAL) $(WIFI) $(HTML) checksums.json +DATE ?= $(shell date --rfc-email) +VERSION ?= $(shell git symbolic-ref --short HEAD 2>/dev/null)/$(shell git rev-list --count HEAD 2>/dev/null)/$(shell git rev-parse --short=8 HEAD 2>/dev/null) +export DATE VERSION -%.html: %.md Makefile - markdown $< | sed 's/href="\([-_\./[:alnum:]]*\)\.md"/href="\1.html"/g' > $@ +.PHONY: all checksums commitinfo docs rsc clean -%.capsman.rsc: %.template.rsc Makefile - sed -e '/\/interface\/wifi\//d' -e '/\/interface\/wireless\//d' -e 's|%TEMPL%|.capsman|' \ - -e '/^# NOT \/caps-man\/ #$$/,/^# NOT \/caps-man\/ #$$/d' \ - -e '/^# !!/,/^# !!/c # !! Do not edit this file, it is generated from template!' \ - < $< > $@ +all: checksums docs rsc -%.local.rsc: %.template.rsc Makefile - sed -e '/\/caps-man\//d' -e '/\/interface\/wifi\//d' -e 's|%TEMPL%|.local|' \ - -e '/^# NOT \/interface\/wireless\/ #$$/,/^# NOT \/interface\/wireless\/ #$$/d' \ - -e '/^# !!/,/^# !!/c # !! Do not edit this file, it is generated from template!' \ - < $< > $@ +checksums: checksums.json -%.wifi.rsc: %.template.rsc Makefile - sed -e '/\/caps-man\//d' -e '/\/interface\/wireless\//d' -e 's|%TEMPL%|.wifi|' \ - -e '/^# NOT \/interface\/wifi\/ #$$/,/^# NOT \/interface\/wifi\/ #$$/d' \ - -e '/^# !!/,/^# !!/c # !! Do not edit this file, it is generated from template!' \ - < $< > $@ +checksums.json: contrib/checksums.sh $(ALL_RSC) + contrib/checksums.sh > $@ -checksums.json: contrib/checksums.sh *.rsc */*.rsc - contrib/checksums.sh +commitinfo: global-functions.rsc + contrib/commitinfo.sh $< > $<~ + mv $<~ $< + +docs: $(HTML) + +%.html: %.md general/style.css contrib/html.sh contrib/html.sh.d/head.html contrib/html.sh.d/foot.html + contrib/html.sh $< > $@ + +rsc: $(GEN_RSC) + +%.capsman.rsc: %.template.rsc contrib/template-capsman.sh + contrib/template-capsman.sh $< > $@ + +%.local.rsc: %.template.rsc contrib/template-local.sh + contrib/template-local.sh $< > $@ + +%.wifi.rsc: %.template.rsc contrib/template-wifi.sh + contrib/template-wifi.sh $< > $@ clean: rm -f $(HTML) checksums.json + make -C contrib/ clean diff --git a/README.d/01-download-certs.avif b/README.d/01-download-certs.avif index d41ca05..f2afeb5 100644 Binary files a/README.d/01-download-certs.avif and b/README.d/01-download-certs.avif differ diff --git a/README.d/02-import-certs.avif b/README.d/02-import-certs.avif index bf7d577..b31343c 100644 Binary files a/README.d/02-import-certs.avif and b/README.d/02-import-certs.avif differ diff --git a/README.d/03-check-certs.avif b/README.d/03-check-certs.avif index 4717b3e..1f03ad2 100644 Binary files a/README.d/03-check-certs.avif and b/README.d/03-check-certs.avif differ diff --git a/README.d/04-import-scripts.avif b/README.d/04-import-scripts.avif index 53439e4..c09949a 100644 Binary files a/README.d/04-import-scripts.avif and b/README.d/04-import-scripts.avif differ diff --git a/README.d/05-run-and-schedule-scripts.avif b/README.d/05-run-and-schedule-scripts.avif deleted file mode 100644 index 37e1173..0000000 Binary files a/README.d/05-run-and-schedule-scripts.avif and /dev/null differ diff --git a/README.d/05-run-scripts.avif b/README.d/05-run-scripts.avif new file mode 100644 index 0000000..12d812c Binary files /dev/null and b/README.d/05-run-scripts.avif differ diff --git a/README.d/06-schedule-update.avif b/README.d/06-schedule-update.avif index 7c96f3a..158e13f 100644 Binary files a/README.d/06-schedule-update.avif and b/README.d/06-schedule-update.avif differ diff --git a/README.d/07-edit-global-config-overlay.avif b/README.d/07-edit-global-config-overlay.avif index f87fda8..9a5b903 100644 Binary files a/README.d/07-edit-global-config-overlay.avif and b/README.d/07-edit-global-config-overlay.avif differ diff --git a/README.d/08-apply-configuration.avif b/README.d/08-apply-configuration.avif index b66af1a..ab22cae 100644 Binary files a/README.d/08-apply-configuration.avif and b/README.d/08-apply-configuration.avif differ diff --git a/README.d/09-update-scripts.avif b/README.d/09-update-scripts.avif index f549fef..e713ac2 100644 Binary files a/README.d/09-update-scripts.avif and b/README.d/09-update-scripts.avif differ diff --git a/README.d/10-install-scripts.avif b/README.d/10-install-scripts.avif index 00225b1..cf26b16 100644 Binary files a/README.d/10-install-scripts.avif and b/README.d/10-install-scripts.avif differ diff --git a/README.d/11-schedule-script.avif b/README.d/11-schedule-script.avif index d6eb0f8..558614f 100644 Binary files a/README.d/11-schedule-script.avif and b/README.d/11-schedule-script.avif differ diff --git a/README.d/12-setup-lease-script.avif b/README.d/12-setup-lease-script.avif index fb4024e..2a8bcb2 100644 Binary files a/README.d/12-setup-lease-script.avif and b/README.d/12-setup-lease-script.avif differ diff --git a/README.d/13-install-custom-script.avif b/README.d/13-install-custom-script.avif index 2f01c43..221b84e 100644 Binary files a/README.d/13-install-custom-script.avif and b/README.d/13-install-custom-script.avif differ diff --git a/README.d/14-remove-script.avif b/README.d/14-remove-script.avif index a5c7daf..3e4c105 100644 Binary files a/README.d/14-remove-script.avif and b/README.d/14-remove-script.avif differ diff --git a/README.d/notification-news-and-changes.avif b/README.d/notification-news-and-changes.avif index d91b8a0..d2e8aa7 100644 Binary files a/README.d/notification-news-and-changes.avif and b/README.d/notification-news-and-changes.avif differ diff --git a/README.md b/README.md index 2a8b2ce..d562ada 100644 --- a/README.md +++ b/README.md @@ -55,15 +55,18 @@ Initial setup ### Get me ready! If you know how things work just copy and paste the -[initial commands](INITIAL-COMMANDS.md). Remember to edit and rerun +[initial commands](INITIAL-COMMANDS.md). These also support fixing an +existing but broken installation. Remember to edit and rerun `global-config-overlay`! -First time users should take the long way below. + +> đŸ’Ąī¸ **Hint**: First time users should take +> [the long way in detail](#the-long-way-in-detail) below. ### Live presentation Want to see it in action? I've had a presentation [Repository based RouterOS script distribution â†—ī¸](https://www.youtube.com/watch?v=B9neG3oAhcY) -including demonstation recorded live at [MUM Europe +including demonstration recorded live at [MUM Europe 2019 â†—ī¸](https://mum.mikrotik.com/2019/EU/) in Vienna. > âš ī¸ **Warning**: Some details changed. So see the presentation, then follow @@ -72,11 +75,22 @@ including demonstation recorded live at [MUM Europe ### The long way in detail The update script does server certificate verification, so first step is to -download the certificates. If you intend to download the scripts from a +download the certificates. + +> đŸ’Ąī¸ **Hint**: RouterOS 7.19 comes with a builtin certificate store. You +> can skip the steps regarding certificate download and import and jump +> to [installation of scripts](#installation-of-scripts) if you set the +> trust for these builtin trust anchors: +> `/certificate/settings/set builtin-trust-anchors=trusted;` +> With RouterOS 7.21 the functionality was changed. Set this at minimum, +> but make sure not to drop other targets: +> `/certificate/settings/set builtin-trust-store=fetch;` + +If you intend to download the scripts from a different location (for example from github.com) install the corresponding certificate chain. - /tool/fetch "https://git.eworm.de/cgit/routeros-scripts/plain/certs/ISRG-Root-X2.pem" dst-path="isrg-root-x2.pem"; + /tool/fetch "https://rsc.eworm.de/main/certs/ISRG-Root-X2.pem" dst-path="isrg-root-x2.pem"; ![screenshot: download certs](README.d/01-download-certs.avif) @@ -106,22 +120,24 @@ is shown. Always make sure there are no certificates installed you do not know or want! +#### Installation of scripts + All following commands will verify the server certificate. For validity the certificate's lifetime is checked with local time, so make sure the device's date and time is set correctly! Now let's download the main scripts and add them in configuration on the fly. - :foreach Script in={ "global-config"; "global-config-overlay"; "global-functions" } do={ /system/script/add name=$Script owner=$Script source=([ /tool/fetch check-certificate=yes-without-crl ("https://git.eworm.de/cgit/routeros-scripts/plain/" . $Script . ".rsc") output=user as-value]->"data"); }; + :foreach Script in={ "global-config"; "global-config-overlay"; "global-functions" } do={ /system/script/add name=$Script owner=$Script source=([ /tool/fetch check-certificate=yes-without-crl ("https://rsc.eworm.de/main/" . $Script . ".rsc") output=user as-value ]->"data"); }; ![screenshot: import scripts](README.d/04-import-scripts.avif) -And finally load configuration and functions and add the scheduler. +And finally run configuration and functions. This will also add the +scheduler for loading at system startup automatically. /system/script { run global-config; run global-functions; }; - /system/scheduler/add name="global-scripts" start-time=startup on-event="/system/script { run global-config; run global-functions; }"; -![screenshot: run and schedule scripts](README.d/05-run-and-schedule-scripts.avif) +![screenshot: run scripts](README.d/05-run-scripts.avif) > đŸ’Ąī¸ **Hint**: You see complaints regarding syntax errors? Most likely the > RouterOS on your device is too old. Check for updates! @@ -163,7 +179,7 @@ This last step is required when ever you make changes to your configuration. > â„šī¸ **Info**: It is recommended to edit the configuration using the command > line interface. If using Winbox on Windows OS, the line endings may be -> missing. To fix this run: +> missing. To fix this run: > `/system/script/set source=[ :tocrlf [ get global-config-overlay source ] ] global-config-overlay;` Updating scripts @@ -209,7 +225,7 @@ cleanup add a scheduler entry. $ScriptInstallUpdate dhcp-to-dns,lease-script; /ip/dhcp-server/set lease-script=lease-script [ find ]; - /system/scheduler/add name="dhcp-to-dns" interval=5m on-event="/system/script/run dhcp-to-dns;"; + /system/scheduler/add name="dhcp-to-dns" interval=5m start-time=startup on-event="/system/script/run dhcp-to-dns;"; ![screenshot: setup lease script](README.d/12-setup-lease-script.avif) @@ -391,14 +407,15 @@ immediately remove the link in question. Upstream -------- -[![upstream](README.d/upstream.png)](https://rsc.eworm.de/) +[rsc.eworm.de](https://rsc.eworm.de/) -URL: -[GitHub.com](https://github.com/eworm-de/routeros-scripts#routeros-scripts) +[![upstream](general/qr-code.png)](https://rsc.eworm.de/) -Mirror: -[eworm.de](https://git.eworm.de/cgit/routeros-scripts/about/) -[GitLab.com](https://gitlab.com/eworm-de/routeros-scripts#routeros-scripts) +### Code hosting + +* [git.eworm.de](https://git.eworm.de/cgit/routeros-scripts/about/) +* [GitHub.com](https://github.com/eworm-de/routeros-scripts#routeros-scripts) +* [GitLab.com](https://gitlab.com/eworm-de/routeros-scripts#routeros-scripts) --- [âŦ†ī¸ Go back to top](#top) diff --git a/accesslist-duplicates.capsman.rsc b/accesslist-duplicates.capsman.rsc index 5e6cf0a..1da0366 100644 --- a/accesslist-duplicates.capsman.rsc +++ b/accesslist-duplicates.capsman.rsc @@ -22,7 +22,7 @@ :foreach AccList in=[ /caps-man/access-list/find where mac-address!="00:00:00:00:00:00" ] do={ :local Mac [ /caps-man/access-list/get $AccList mac-address ]; :if ($Seen->$Mac = 1) do={ - /caps-man/access-list/print where mac-address=$Mac; + /caps-man/access-list/print without-paging where mac-address=$Mac; :local Remove [ :tonum [ /terminal/ask prompt="\nNumeric id to remove, any key to skip!" ] ]; :if ([ :typeof $Remove ] = "num") do={ diff --git a/accesslist-duplicates.local.rsc b/accesslist-duplicates.local.rsc index a6b4f41..03a9724 100644 --- a/accesslist-duplicates.local.rsc +++ b/accesslist-duplicates.local.rsc @@ -22,7 +22,7 @@ :foreach AccList in=[ /interface/wireless/access-list/find where mac-address!="00:00:00:00:00:00" ] do={ :local Mac [ /interface/wireless/access-list/get $AccList mac-address ]; :if ($Seen->$Mac = 1) do={ - /interface/wireless/access-list/print where mac-address=$Mac; + /interface/wireless/access-list/print without-paging where mac-address=$Mac; :local Remove [ :tonum [ /terminal/ask prompt="\nNumeric id to remove, any key to skip!" ] ]; :if ([ :typeof $Remove ] = "num") do={ diff --git a/accesslist-duplicates.template.rsc b/accesslist-duplicates.template.rsc index e51198d..bf23147 100644 --- a/accesslist-duplicates.template.rsc +++ b/accesslist-duplicates.template.rsc @@ -27,9 +27,9 @@ :local Mac [ /interface/wifi/access-list/get $AccList mac-address ]; :local Mac [ /interface/wireless/access-list/get $AccList mac-address ]; :if ($Seen->$Mac = 1) do={ - /caps-man/access-list/print where mac-address=$Mac; - /interface/wifi/access-list/print where mac-address=$Mac; - /interface/wireless/access-list/print where mac-address=$Mac; + /caps-man/access-list/print without-paging where mac-address=$Mac; + /interface/wifi/access-list/print without-paging where mac-address=$Mac; + /interface/wireless/access-list/print without-paging where mac-address=$Mac; :local Remove [ :tonum [ /terminal/ask prompt="\nNumeric id to remove, any key to skip!" ] ]; :if ([ :typeof $Remove ] = "num") do={ diff --git a/accesslist-duplicates.wifi.rsc b/accesslist-duplicates.wifi.rsc index cadacb6..528a73c 100644 --- a/accesslist-duplicates.wifi.rsc +++ b/accesslist-duplicates.wifi.rsc @@ -22,7 +22,7 @@ :foreach AccList in=[ /interface/wifi/access-list/find where mac-address!="00:00:00:00:00:00" ] do={ :local Mac [ /interface/wifi/access-list/get $AccList mac-address ]; :if ($Seen->$Mac = 1) do={ - /interface/wifi/access-list/print where mac-address=$Mac; + /interface/wifi/access-list/print without-paging where mac-address=$Mac; :local Remove [ :tonum [ /terminal/ask prompt="\nNumeric id to remove, any key to skip!" ] ]; :if ([ :typeof $Remove ] = "num") do={ diff --git a/backup-email.rsc b/backup-email.rsc index 632d2e6..8015bea 100644 --- a/backup-email.rsc +++ b/backup-email.rsc @@ -27,6 +27,7 @@ :global CleanName; :global DeviceInfo; + :global FileExists; :global FormatLine; :global LogPrint; :global MkDir; @@ -124,17 +125,19 @@ attach=$Attach; remove-attach=true }); # wait for the mail to be sent - :local I 0; - :while ([ :len [ /file/find where name ~ ($FilePath . "\\.(backup|rsc)\$") ] ] > 0) do={ - :if ($I >= 120) do={ - $LogPrint warning $ScriptName ("Files are still available, sending e-mail failed."); - :set PackagesUpdateBackupFailure true; - :set ExitOK true; - :error false; - } - :delay 1s; - :set I ($I + 1); + :do { + :retry { + :if ([ $FileExists ($FilePath . ".conf") ".conf file" ] = true || \ + [ $FileExists ($FilePath . ".backup") "backup" ] = true || \ + [ $FileExists ($FilePath . ".rsc") "script" ] = true) do={ + :error "Files are still available."; + } + } delay=1s max=120; + } on-error={ + $LogPrint warning $ScriptName ("Files are still available, sending e-mail failed."); + :set PackagesUpdateBackupFailure true; } + # do not remove the files here, as the mail is still queued! } do={ :global ExitError; $ExitError $ExitOK [ :jobname ] $Err; } diff --git a/capsman-download-packages.capsman.rsc b/capsman-download-packages.capsman.rsc index cab1e4c..2ea1667 100644 --- a/capsman-download-packages.capsman.rsc +++ b/capsman-download-packages.capsman.rsc @@ -20,6 +20,7 @@ :global CleanFilePath; :global DownloadPackage; + :global FileGet; :global LogPrint; :global MkDir; :global RmFile; @@ -42,7 +43,7 @@ :error false; } - :if ([ :len [ /file/find where name=$PackagePath type="directory" ] ] = 0) do={ + :if ([ $FileGet $PackagePath ] = false) do={ :if ([ $MkDir $PackagePath ] = false) do={ $LogPrint warning $ScriptName ("Creating directory at CAPsMAN package path (" . \ $PackagePath . ") failed!"); @@ -53,7 +54,7 @@ "). Please place your packages!"); } - :foreach Package in=[ /file/find where type=package \ + :foreach Package in=[ /file/find where type="package" \ package-version!=$InstalledVersion name~("^" . $PackagePath) ] do={ :local File [ /file/get $Package ]; :if ($File->"package-architecture" = "mips") do={ @@ -66,7 +67,7 @@ } } - :if ([ :len [ /file/find where type=package name~("^" . $PackagePath) ] ] = 0) do={ + :if ([ :len [ /file/find where type="package" name~("^" . $PackagePath) ] ] = 0) do={ $LogPrint info $ScriptName ("No packages available, downloading default set."); :foreach Arch in={ "arm"; "mipsbe" } do={ :foreach Package in={ "routeros"; "wireless" } do={ diff --git a/capsman-download-packages.template.rsc b/capsman-download-packages.template.rsc index ea41120..f95212a 100644 --- a/capsman-download-packages.template.rsc +++ b/capsman-download-packages.template.rsc @@ -21,6 +21,7 @@ :global CleanFilePath; :global DownloadPackage; + :global FileGet; :global LogPrint; :global MkDir; :global RmFile; @@ -44,7 +45,7 @@ :error false; } - :if ([ :len [ /file/find where name=$PackagePath type="directory" ] ] = 0) do={ + :if ([ $FileGet $PackagePath ] = false) do={ :if ([ $MkDir $PackagePath ] = false) do={ $LogPrint warning $ScriptName ("Creating directory at CAPsMAN package path (" . \ $PackagePath . ") failed!"); @@ -55,7 +56,7 @@ "). Please place your packages!"); } - :foreach Package in=[ /file/find where type=package \ + :foreach Package in=[ /file/find where type="package" \ package-version!=$InstalledVersion name~("^" . $PackagePath) ] do={ :local File [ /file/get $Package ]; :if ($File->"package-architecture" = "mips") do={ @@ -68,7 +69,7 @@ } } - :if ([ :len [ /file/find where type=package name~("^" . $PackagePath) ] ] = 0) do={ + :if ([ :len [ /file/find where type="package" name~("^" . $PackagePath) ] ] = 0) do={ $LogPrint info $ScriptName ("No packages available, downloading default set."); # NOT /interface/wifi/ # :foreach Arch in={ "arm"; "mipsbe" } do={ diff --git a/capsman-download-packages.wifi.rsc b/capsman-download-packages.wifi.rsc index a810356..03fd9e7 100644 --- a/capsman-download-packages.wifi.rsc +++ b/capsman-download-packages.wifi.rsc @@ -20,6 +20,7 @@ :global CleanFilePath; :global DownloadPackage; + :global FileGet; :global LogPrint; :global MkDir; :global RmFile; @@ -42,7 +43,7 @@ :error false; } - :if ([ :len [ /file/find where name=$PackagePath type="directory" ] ] = 0) do={ + :if ([ $FileGet $PackagePath ] = false) do={ :if ([ $MkDir $PackagePath ] = false) do={ $LogPrint warning $ScriptName ("Creating directory at CAPsMAN package path (" . \ $PackagePath . ") failed!"); @@ -53,7 +54,7 @@ "). Please place your packages!"); } - :foreach Package in=[ /file/find where type=package \ + :foreach Package in=[ /file/find where type="package" \ package-version!=$InstalledVersion name~("^" . $PackagePath) ] do={ :local File [ /file/get $Package ]; :if ($File->"package-architecture" = "mips") do={ @@ -66,7 +67,7 @@ } } - :if ([ :len [ /file/find where type=package name~("^" . $PackagePath) ] ] = 0) do={ + :if ([ :len [ /file/find where type="package" name~("^" . $PackagePath) ] ] = 0) do={ $LogPrint info $ScriptName ("No packages available, downloading default set."); :foreach Arch in={ "arm"; "arm64" } do={ :local Packages { "arm"={ "routeros"; "wifi-qcom"; "wifi-qcom-ac" }; diff --git a/check-certificates.rsc b/check-certificates.rsc index c10e33b..93641ee 100644 --- a/check-certificates.rsc +++ b/check-certificates.rsc @@ -21,7 +21,7 @@ :global CertWarnTime; :global Identity; - :global CertificateAvailable + :global CertificateAvailable; :global EscapeForRegEx; :global IfThenElse; :global LogPrint; @@ -189,7 +189,7 @@ fingerprint!=[ :tostr ($CertVal->"fingerprint") ] expires-after>$CertRenewTime ]; :local CertNewVal [ /certificate/get $CertNew ]; - :if ([ $CertificateAvailable ([ $ParseKeyValueStore ($CertNewVal->"issuer") ]->"CN") ] = false) do={ + :if ([ $CertificateAvailable ([ $ParseKeyValueStore ($CertNewVal->"issuer") ]->"CN") "fetch" ] = false) do={ $LogPrint warning $ScriptName ("The certificate chain is not available!"); } @@ -231,7 +231,7 @@ :local State [ $IfThenElse (($CertVal->"expired") = true) "expired" "is about to expire" ]; $SendNotification2 ({ origin=$ScriptName; \ - subject=([ $SymbolForNotification "warning-sign" ] . "Certificate warning: " . ($CertVal->"name")); \ + subject=([ $SymbolForNotification "lock-with-ink-pen,warning-sign" ] . "Certificate warning: " . ($CertVal->"name")); \ message=("A certificate on " . $Identity . " " . $State . ".\n\n" . [ $FormatInfo $Cert ]) }); $LogPrint info $ScriptName ("The certificate '" . ($CertVal->"name") . "' " . $State . \ ", it is invalid after " . ($CertVal->"invalid-after") . "."); diff --git a/check-health.d/state.rsc b/check-health.d/state.rsc index 2991935..5e4cbb8 100644 --- a/check-health.d/state.rsc +++ b/check-health.d/state.rsc @@ -11,7 +11,8 @@ :global CheckHealthPlugins; :set ($CheckHealthPlugins->[ :jobname ]) do={ - :local FuncName [ :tostr $0 ]; + :local FuncName [ :tostr $0 ]; + :local ScriptName [ :tostr $1 ]; :global CheckHealthLast; :global Identity; @@ -32,13 +33,13 @@ :if ([ :typeof ($CheckHealthLast->$Name) ] != "nothing") do={ :if ($CheckHealthLast->$Name = "ok" && \ $Value != "ok") do={ - $SendNotification2 ({ origin=$FuncName; \ + $SendNotification2 ({ origin=$ScriptName; \ subject=([ $SymbolForNotification "cross-mark" ] . "Health warning: " . $Name); \ message=("The device '" . $Name . "' on " . $Identity . " failed!") }); } :if ($CheckHealthLast->$Name != "ok" && \ $Value = "ok") do={ - $SendNotification2 ({ origin=$FuncName; \ + $SendNotification2 ({ origin=$ScriptName; \ subject=([ $SymbolForNotification "white-heavy-check-mark" ] . "Health recovery: " . $Name); \ message=("The device '" . $Name . "' on " . $Identity . " recovered!") }); } diff --git a/check-health.d/temperature.rsc b/check-health.d/temperature.rsc index a2f632d..9f3f1a5 100644 --- a/check-health.d/temperature.rsc +++ b/check-health.d/temperature.rsc @@ -11,7 +11,8 @@ :global CheckHealthPlugins; :set ($CheckHealthPlugins->[ :jobname ]) do={ - :local FuncName [ :tostr $0 ]; + :local FuncName [ :tostr $0 ]; + :local ScriptName [ :tostr $1 ]; :global CheckHealthLast; :global CheckHealthTemperature; @@ -54,7 +55,7 @@ } :if ($Value > $CheckHealthTemperature->$Name && \ $CheckHealthTemperatureNotified->$Name != true) do={ - $SendNotification2 ({ origin=$FuncName; \ + $SendNotification2 ({ origin=$ScriptName; \ subject=([ $SymbolForNotification "fire" ] . "Health warning: " . $Name); \ message=("The " . $Name . " on " . $Identity . " is above threshold: " . \ $Value . "\C2\B0" . "C") }); @@ -62,7 +63,7 @@ } :if ($Value <= ($CheckHealthTemperature->$Name - $CheckHealthTemperatureDeviation) && \ $CheckHealthTemperatureNotified->$Name = true) do={ - $SendNotification2 ({ origin=$FuncName; \ + $SendNotification2 ({ origin=$ScriptName; \ subject=([ $SymbolForNotification "white-heavy-check-mark" ] . "Health recovery: " . $Name); \ message=("The " . $Name . " on " . $Identity . " dropped below threshold: " . \ $Value . "\C2\B0" . "C") }); diff --git a/check-health.d/voltage.rsc b/check-health.d/voltage.rsc index 9071c88..5891fb4 100644 --- a/check-health.d/voltage.rsc +++ b/check-health.d/voltage.rsc @@ -11,7 +11,8 @@ :global CheckHealthPlugins; :set ($CheckHealthPlugins->[ :jobname ]) do={ - :local FuncName [ :tostr $0 ]; + :local FuncName [ :tostr $0 ]; + :local ScriptName [ :tostr $1 ]; :global CheckHealthLast; :global CheckHealthVoltageLow; @@ -39,7 +40,7 @@ :if ($NumLast * (100 + $CheckHealthVoltagePercent) < $NumCurr * 100 || \ $NumLast * 100 > $NumCurr * (100 + $CheckHealthVoltagePercent)) do={ - $SendNotification2 ({ origin=$FuncName; \ + $SendNotification2 ({ origin=$ScriptName; \ subject=([ $SymbolForNotification ("high-voltage-sign,chart-" . [ $IfThenElse ($NumLast < \ $NumCurr) "in" "de" ] . "creasing") ] . "Health warning: " . $Name); \ message=("The " . $Name . " on " . $Identity . " jumped more than " . $CheckHealthVoltagePercent . "%.\n\n" . \ @@ -47,12 +48,12 @@ [ $FormatLine "new value" ($Value . " V") 12 ]) }); } else={ :if ($NumCurr <= $CheckHealthVoltageLow && $NumLast > $CheckHealthVoltageLow) do={ - $SendNotification2 ({ origin=$FuncName; \ + $SendNotification2 ({ origin=$ScriptName; \ subject=([ $SymbolForNotification "high-voltage-sign,chart-decreasing" ] . "Health warning: Low " . $Name); \ message=("The " . $Name . " on " . $Identity . " dropped to " . $Value . " V below hard limit.") }); } :if ($NumCurr > $CheckHealthVoltageLow && $NumLast <= $CheckHealthVoltageLow) do={ - $SendNotification2 ({ origin=$FuncName; \ + $SendNotification2 ({ origin=$ScriptName; \ subject=([ $SymbolForNotification "high-voltage-sign,chart-increasing" ] . "Health recovery: Low " . $Name); \ message=("The " . $Name . " on " . $Identity . " recovered to " . $Value . " V above hard limit.") }); } diff --git a/check-health.rsc b/check-health.rsc index 4cb9940..760636d 100644 --- a/check-health.rsc +++ b/check-health.rsc @@ -92,16 +92,16 @@ :onerror Err { /system/script/run $Plugin; } do={ - $LogPrint error $ScriptName ("Plugin '" . $ScriptVal->"name" . "' failed to run: " . $Err); + $LogPrint error $ScriptName ("Plugin '" . $PluginVal->"name" . "' failed to run: " . $Err); } } else={ - $LogPrint error $ScriptName ("Plugin '" . $ScriptVal->"name" . "' failed syntax validation, skipping."); + $LogPrint error $ScriptName ("Plugin '" . $PluginVal->"name" . "' failed syntax validation, skipping."); } } :foreach PluginName,Discard in=$CheckHealthPlugins do={ ($CheckHealthPlugins->$PluginName) \ - ("\$CheckHealthPlugins->\"" . $PluginName . "\""); + ("\$CheckHealthPlugins->\"" . $PluginName . "\"") $ScriptName; } :set CheckHealthPlugins; diff --git a/check-perpetual-license.rsc b/check-perpetual-license.rsc index c2f0dff..a88498d 100644 --- a/check-perpetual-license.rsc +++ b/check-perpetual-license.rsc @@ -42,7 +42,7 @@ $LogPrint warning $ScriptName ("Your license expired on " . ($License->"deadline-at") . "!"); :if ($SentCertificateNotification != "expired") do={ $SendNotification2 ({ origin=$ScriptName; \ - subject=([ $SymbolForNotification "warning-sign" ] . "License expired!"); \ + subject=([ $SymbolForNotification "scroll,warning-sign" ] . "License expired!"); \ message=("Your license expired on " . ($License->"deadline-at") . \ ", can no longer update RouterOS on " . $Identity . "...") }); :set SentCertificateNotification "expired"; @@ -55,7 +55,7 @@ $LogPrint warning $ScriptName ("Your license will expire on " . ($License->"deadline-at") . "!"); :if ($SentCertificateNotification != "warning") do={ $SendNotification2 ({ origin=$ScriptName; \ - subject=([ $SymbolForNotification "warning-sign" ] . "License about to expire!"); \ + subject=([ $SymbolForNotification "scroll,warning-sign" ] . "License about to expire!"); \ message=("Your license failed to renew and is about to expire on " . \ ($License->"deadline-at") . " on " . $Identity . "...") }); :set SentCertificateNotification "warning"; @@ -68,7 +68,7 @@ [ :totime ($License->"deadline-at") ] - 4w > [ :timestamp ]) do={ $LogPrint info $ScriptName ("Your license was successfully renewed."); $SendNotification2 ({ origin=$ScriptName; \ - subject=([ $SymbolForNotification "white-heavy-check-mark" ] . "License renewed"); \ + subject=([ $SymbolForNotification "scroll,white-heavy-check-mark" ] . "License renewed"); \ message=("Your license was successfully renewed on " . $Identity . \ ". It is now valid until " . ($License->"deadline-at") . ".") }); :set SentCertificateNotification; diff --git a/check-routeros-update.rsc b/check-routeros-update.rsc index e28a019..8b80dde 100644 --- a/check-routeros-update.rsc +++ b/check-routeros-update.rsc @@ -28,6 +28,7 @@ :global EscapeForRegEx; :global FetchUserAgentStr; :global LogPrint; + :global RebootForUpdate; :global ScriptFromTerminal; :global ScriptLock; :global SendNotification2; @@ -62,9 +63,14 @@ $WaitFullyConnected; :if ([ :len [ /system/scheduler/find where name="_RebootForUpdate" ] ] > 0) do={ - $LogPrint info $ScriptName ("A reboot for update is already scheduled."); - :set ExitOK true; - :error false; + :if ([ :typeof $RebootForUpdate ] = "nothing") do={ + $LogPrint info $ScriptName ("Found a stale scheduler for reboot, removing."); + /system/scheduler/remove "_RebootForUpdate"; + } else={ + $LogPrint info $ScriptName ("A reboot for update is already scheduled."); + :set ExitOK true; + :error false; + } } $LogPrint debug $ScriptName ("Checking for updates..."); diff --git a/contrib/Makefile b/contrib/Makefile new file mode 100644 index 0000000..e755a1d --- /dev/null +++ b/contrib/Makefile @@ -0,0 +1,17 @@ +# Makefile + +HTML := $(shell grep -xl '' *.html) + +.PHONY: all docs clean + +all: docs + +badges.html: badges.md + markdown $< > $@ + +docs: static-html.sh $(HTML) badges.html + ./static-html.sh $(HTML) + +clean: + rm -f badges.html + git checkout HEAD -- $(HTML) diff --git a/contrib/badges.md b/contrib/badges.md new file mode 100644 index 0000000..24bd205 --- /dev/null +++ b/contrib/badges.md @@ -0,0 +1,6 @@ +[![GitHub stars](https://img.shields.io/github/stars/eworm-de/routeros-scripts?logo=GitHub&style=flat&color=red)](https://github.com/eworm-de/routeros-scripts/stargazers) +[![GitHub forks](https://img.shields.io/github/forks/eworm-de/routeros-scripts?logo=GitHub&style=flat&color=green)](https://github.com/eworm-de/routeros-scripts/network) +[![GitHub watchers](https://img.shields.io/github/watchers/eworm-de/routeros-scripts?logo=GitHub&style=flat&color=blue)](https://github.com/eworm-de/routeros-scripts/watchers) +[![required RouterOS version](https://img.shields.io/badge/RouterOS-7.15-yellow?style=flat)](https://mikrotik.com/download/changelogs/) +[![Telegram group @routeros_scripts](https://img.shields.io/badge/Telegram-%40routeros__scripts-%2326A5E4?logo=telegram&style=flat)](https://t.me/routeros_scripts) +[![donate with PayPal](https://img.shields.io/badge/Like_it%3F-Donate!-orange?logo=githubsponsors&logoColor=orange&style=flat)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=A4ZXBD6YS2W8J) diff --git a/contrib/checksums.sh b/contrib/checksums.sh index b472b49..ab4e973 100755 --- a/contrib/checksums.sh +++ b/contrib/checksums.sh @@ -6,4 +6,4 @@ set -e md5sum $(find -name '*.rsc' | sort) | \ sed -e "s| \./||" -e 's|.rsc$||' | \ - jq --raw-input --null-input '[ inputs | split (" ") | { (.[1]): (.[0]) }] | add' > 'checksums.json' + jq --raw-input --null-input '[ inputs | split (" ") | { (.[1]): (.[0]) }] | add' diff --git a/contrib/commitinfo.sh b/contrib/commitinfo.sh new file mode 100755 index 0000000..21faf9f --- /dev/null +++ b/contrib/commitinfo.sh @@ -0,0 +1,6 @@ +#!/bin/sh + +sed \ + -e "/^:global CommitId/c :global CommitId \"${COMMITID:-unknown}\";" \ + -e "/^:global CommitInfo/c :global CommitInfo \"${COMMITINFO:-unknown}\";" \ + < "${1}" diff --git a/contrib/html.sh b/contrib/html.sh new file mode 100755 index 0000000..03eba23 --- /dev/null +++ b/contrib/html.sh @@ -0,0 +1,23 @@ +#!/bin/sh + +set -e + +RELTO="$(dirname "${1}")" + +sed \ + -e "s|__TITLE__|$(head -n1 "${1}")|" \ + -e "s|__GENERAL__|$(realpath --relative-to="${RELTO}" general/)|" \ + -e "s|__ROOT__|$(realpath --relative-to="${RELTO}" ./)|" \ + < "${0}.d/head.html" + +markdown -f toc,idanchor "${1}" | sed \ + -e 's/href="\([-_\./[:alnum:]]*\)\.md\(#[-[:alnum:]]*\)\?"/href="\1.html\2"/g' \ + -e '/| id="\L\1">|' \ + -e '//s|pre|pre class="code" onclick="CopyToClipboard(this)"|g' \ + -e '/The above link may be broken on code hosting sites/s|blockquote|blockquote style="display: none;"|' + +sed \ + -e "s|__DATE__|${DATE:-$(date --rfc-email)}|" \ + -e "s|__VERSION__|${VERSION:-unknown}|" \ + < "${0}.d/foot.html" diff --git a/contrib/html.sh.d/foot.html b/contrib/html.sh.d/foot.html new file mode 100644 index 0000000..d304152 --- /dev/null +++ b/contrib/html.sh.d/foot.html @@ -0,0 +1,5 @@ + +

RouterOS Scripts documentation generated on __DATE__ for __VERSION__
+Copyright © 2013-2025 Christian Hesse <mail@eworm.de>

+ + diff --git a/contrib/html.sh.d/head.html b/contrib/html.sh.d/head.html new file mode 100644 index 0000000..656a63c --- /dev/null +++ b/contrib/html.sh.d/head.html @@ -0,0 +1,16 @@ + + +RouterOS Scripts :: __TITLE__ + + + + + + + + + +
eworm on meadowQR code: rsc.eworm.deRouterOS Scripts
+ a collection of scripts for MikroTik RouterOS
+
+ diff --git a/contrib/logo-color.d/style.css b/contrib/logo-color.d/style.css deleted file mode 100644 index eb2ec6a..0000000 --- a/contrib/logo-color.d/style.css +++ /dev/null @@ -1,5 +0,0 @@ -body { - font-family: fira-sans, sans-serif; - font-size: 10pt; - background-color: transparent; -} diff --git a/contrib/logo-color.html b/contrib/logo-color.html index 17942ce..e5bfb71 100644 --- a/contrib/logo-color.html +++ b/contrib/logo-color.html @@ -1,14 +1,30 @@ - - - - -RouterOS-Scripts Logo Color Changer - + + + +RouterOS Scripts :: Logo Color Changer + + - - + -

RouterOS-Scripts Logo Color Changer

+ + + + +
eworm on meadowQR code: rsc.eworm.deRouterOS Scripts
+ a collection of scripts for MikroTik RouterOS
+
+ +

Logo Color Changer

+ + + +

âŦ…ī¸ Go back to main README

+ +

đŸ’Ąī¸ Hint: This site or links +on it may be broken on code hosting sites. Use +Logo Color Changer +instead.

You want the logo for your own notifications? But you joined the Telegram Group and want @@ -24,17 +40,23 @@ something that differentiates? Color it!

Then right-click, click "Take Screenshot" and finally select the logo and download it.

-

Screenshot Browser 01 -Screenshot Browser 02 -Screenshot Browser 03

+

Screenshot Browser 01

+

Screenshot Browser 02

+

Screenshot Browser 03

(This example is with Firefox. The workflow for other browsers may differ.)

See how to -Set +Set a profile photo for your Telegram bot.

- - +
+ +

âŦ…ī¸ Go back to main README
+âŦ†ī¸ Go back to top

+ +

Copyright © 2013-2025 Christian Hesse <mail@eworm.de>

+ + diff --git a/contrib/notification.d/style.css b/contrib/notification.d/style.css deleted file mode 100644 index 648ea23..0000000 --- a/contrib/notification.d/style.css +++ /dev/null @@ -1,36 +0,0 @@ -body { - font-family: fira-sans, sans-serif; - font-size: 10pt; - background-color: transparent; -} -div.notification { - position: relative; - float: right; - width: 600px; - border: 3px outset #6c5d53; - /* border-radius: 5px; */ - padding: 10px; - background-color: #e6e6e6; -} -div.content { - padding-left: 60px; -} -img.logo { - float: left; - border-radius: 50%; -} -p.heading { - margin: 0px; - font-weight: bold; - text-decoration: underline; -} -p.hint { - display: none; -} -pre { - font-family: fira-mono, monospace; - white-space: pre-wrap; -} -span.link { - color: #863600; -} diff --git a/contrib/notification.html b/contrib/notification.html index 7875036..baa659c 100644 --- a/contrib/notification.html +++ b/contrib/notification.html @@ -1,35 +1,57 @@ - - - - -RouterOS-Scripts Notification Generator - - - - + + + +RouterOS Scripts :: Notification Generator + + + + -

RouterOS-Scripts Notification Generator

+ + + + +
eworm on meadowQR code: rsc.eworm.deRouterOS Scripts
+ a collection of scripts for MikroTik RouterOS
+
+ +

Notification Generator

+ + + +

âŦ…ī¸ Go back to main README

+ +

đŸ’Ąī¸ Hint: This site or links +on it may be broken on code hosting sites. Use +Notification Generator +instead.

[MikroTik] â„šī¸ Subject

Message
- -

⏰ This message was queued since oct/18/2022 18:30:48 and may be obsolete.

-

âœ‚ī¸ The message was too long and has been truncated, cut off 13%!

+ +

⏰ This message was queued since 2025-10-29 16:06:18 and may be obsolete.

+

âœ‚ī¸ The message was too long and has been truncated, cut off 13%!

Hostname:

Subject:

Message:

-

Show link:

-

Queued since

+

Show link:

+

Queued since

Cut-off with percent

Then right-click, click "Take Screenshot" and finally select the notification and download it.

- - +
+ +

âŦ…ī¸ Go back to main README
+âŦ†ī¸ Go back to top

+ +

Copyright © 2013-2025 Christian Hesse <mail@eworm.de>

+ + diff --git a/contrib/static-html.sh b/contrib/static-html.sh new file mode 100755 index 0000000..7acf104 --- /dev/null +++ b/contrib/static-html.sh @@ -0,0 +1,10 @@ +#!/bin/sh + +set -e + +sed -i \ + -e '/href=/s|\.md|\.html|' \ + -e '/blockquote/s|/\* display \*/|display: none;|' \ + -e '//r badges.html' \ + -e '//d' \ + "${@}" diff --git a/contrib/template-capsman.sh b/contrib/template-capsman.sh new file mode 100755 index 0000000..5771b53 --- /dev/null +++ b/contrib/template-capsman.sh @@ -0,0 +1,11 @@ +#!/bin/sh + +set -e + +sed \ + -e '/\/interface\/wifi\//d' \ + -e '/\/interface\/wireless\//d' \ + -e 's|%TEMPL%|.capsman|' \ + -e '/^# NOT \/caps-man\/ #$/,/^# NOT \/caps-man\/ #$/d' \ + -e '/^# !!/,/^# !!/c # !! Do not edit this file, it is generated from template!' \ + < "${1}" diff --git a/contrib/template-local.sh b/contrib/template-local.sh new file mode 100755 index 0000000..bc5b327 --- /dev/null +++ b/contrib/template-local.sh @@ -0,0 +1,11 @@ +#!/bin/sh + +set -e + +sed \ + -e '/\/caps-man\//d' \ + -e '/\/interface\/wifi\//d' \ + -e 's|%TEMPL%|.local|' \ + -e '/^# NOT \/interface\/wireless\/ #$/,/^# NOT \/interface\/wireless\/ #$/d' \ + -e '/^# !!/,/^# !!/c # !! Do not edit this file, it is generated from template!' \ + < "${1}" diff --git a/contrib/template-wifi.sh b/contrib/template-wifi.sh new file mode 100755 index 0000000..5e297d9 --- /dev/null +++ b/contrib/template-wifi.sh @@ -0,0 +1,11 @@ +#!/bin/sh + +set -e + +sed \ + -e '/\/caps-man\//d' \ + -e '/\/interface\/wireless\//d' \ + -e 's|%TEMPL%|.wifi|' \ + -e '/^# NOT \/interface\/wifi\/ #$/,/^# NOT \/interface\/wifi\/ #$/d' \ + -e '/^# !!/,/^# !!/c # !! Do not edit this file, it is generated from template!' \ + < "${1}" diff --git a/doc/accesslist-duplicates.d/01-example.avif b/doc/accesslist-duplicates.d/01-example.avif index 11b3fc5..d290a77 100644 Binary files a/doc/accesslist-duplicates.d/01-example.avif and b/doc/accesslist-duplicates.d/01-example.avif differ diff --git a/doc/backup-cloud.d/notification.avif b/doc/backup-cloud.d/notification.avif index e533908..5918a62 100644 Binary files a/doc/backup-cloud.d/notification.avif and b/doc/backup-cloud.d/notification.avif differ diff --git a/doc/backup-upload.d/notification.avif b/doc/backup-upload.d/notification.avif index 83cfb18..3bcc1f4 100644 Binary files a/doc/backup-upload.d/notification.avif and b/doc/backup-upload.d/notification.avif differ diff --git a/doc/check-certificates.d/notification-01-warn.avif b/doc/check-certificates.d/notification-01-warn.avif new file mode 100644 index 0000000..e32e54b Binary files /dev/null and b/doc/check-certificates.d/notification-01-warn.avif differ diff --git a/doc/check-certificates.d/notification-02-renew.avif b/doc/check-certificates.d/notification-02-renew.avif new file mode 100644 index 0000000..9ff1400 Binary files /dev/null and b/doc/check-certificates.d/notification-02-renew.avif differ diff --git a/doc/check-certificates.d/notification.avif b/doc/check-certificates.d/notification.avif deleted file mode 100644 index 7c250da..0000000 Binary files a/doc/check-certificates.d/notification.avif and /dev/null differ diff --git a/doc/check-certificates.md b/doc/check-certificates.md index a9426db..b8fece0 100644 --- a/doc/check-certificates.md +++ b/doc/check-certificates.md @@ -19,9 +19,10 @@ Description This script tries to download and renew certificates, then notifies about certificates that are still about to expire. -### Sample notification +### Sample notifications -![check-certificates notification](check-certificates.d/notification.avif) +![check-certificates notification warning](check-certificates.d/notification-01-warn.avif) +![check-certificates notification renew](check-certificates.d/notification-02-renew.avif) Requirements and installation ----------------------------- diff --git a/doc/check-health.d/notification-01-cpu-utilization-high.avif b/doc/check-health.d/notification-01-cpu-utilization-high.avif index 326e7fe..7fbce3c 100644 Binary files a/doc/check-health.d/notification-01-cpu-utilization-high.avif and b/doc/check-health.d/notification-01-cpu-utilization-high.avif differ diff --git a/doc/check-health.d/notification-02-cpu-utilization-ok.avif b/doc/check-health.d/notification-02-cpu-utilization-ok.avif index 811ccd7..f8e91a3 100644 Binary files a/doc/check-health.d/notification-02-cpu-utilization-ok.avif and b/doc/check-health.d/notification-02-cpu-utilization-ok.avif differ diff --git a/doc/check-health.d/notification-03-ram-utilization-high.avif b/doc/check-health.d/notification-03-ram-utilization-high.avif index 59155c5..9015b4a 100644 Binary files a/doc/check-health.d/notification-03-ram-utilization-high.avif and b/doc/check-health.d/notification-03-ram-utilization-high.avif differ diff --git a/doc/check-health.d/notification-04-ram-utilization-ok.avif b/doc/check-health.d/notification-04-ram-utilization-ok.avif index d995b9a..8f949ec 100644 Binary files a/doc/check-health.d/notification-04-ram-utilization-ok.avif and b/doc/check-health.d/notification-04-ram-utilization-ok.avif differ diff --git a/doc/check-health.d/notification-05-voltage.avif b/doc/check-health.d/notification-05-voltage.avif index 17a385b..9ef2d94 100644 Binary files a/doc/check-health.d/notification-05-voltage.avif and b/doc/check-health.d/notification-05-voltage.avif differ diff --git a/doc/check-health.d/notification-06-temperature-high.avif b/doc/check-health.d/notification-06-temperature-high.avif index 60d3802..908e8f2 100644 Binary files a/doc/check-health.d/notification-06-temperature-high.avif and b/doc/check-health.d/notification-06-temperature-high.avif differ diff --git a/doc/check-health.d/notification-07-temperature-ok.avif b/doc/check-health.d/notification-07-temperature-ok.avif index 4afed02..8f509e8 100644 Binary files a/doc/check-health.d/notification-07-temperature-ok.avif and b/doc/check-health.d/notification-07-temperature-ok.avif differ diff --git a/doc/check-health.d/notification-08-state-fail.avif b/doc/check-health.d/notification-08-state-fail.avif index ad049ac..8ba7bb9 100644 Binary files a/doc/check-health.d/notification-08-state-fail.avif and b/doc/check-health.d/notification-08-state-fail.avif differ diff --git a/doc/check-health.d/notification-09-state-ok.avif b/doc/check-health.d/notification-09-state-ok.avif index 26f5a74..9197741 100644 Binary files a/doc/check-health.d/notification-09-state-ok.avif and b/doc/check-health.d/notification-09-state-ok.avif differ diff --git a/doc/check-lte-firmware-upgrade.d/notification.avif b/doc/check-lte-firmware-upgrade.d/notification.avif index c440da5..2c2f692 100644 Binary files a/doc/check-lte-firmware-upgrade.d/notification.avif and b/doc/check-lte-firmware-upgrade.d/notification.avif differ diff --git a/doc/check-perpetual-license.d/notification-01-warn.avif b/doc/check-perpetual-license.d/notification-01-warn.avif new file mode 100644 index 0000000..aa36e45 Binary files /dev/null and b/doc/check-perpetual-license.d/notification-01-warn.avif differ diff --git a/doc/check-perpetual-license.d/notification-02-renew.avif b/doc/check-perpetual-license.d/notification-02-renew.avif new file mode 100644 index 0000000..471a22e Binary files /dev/null and b/doc/check-perpetual-license.d/notification-02-renew.avif differ diff --git a/doc/check-perpetual-license.d/notification.avif b/doc/check-perpetual-license.d/notification.avif deleted file mode 100644 index 70ca603..0000000 Binary files a/doc/check-perpetual-license.d/notification.avif and /dev/null differ diff --git a/doc/check-perpetual-license.md b/doc/check-perpetual-license.md index 0335fb5..a32c4c3 100644 --- a/doc/check-perpetual-license.md +++ b/doc/check-perpetual-license.md @@ -20,9 +20,10 @@ On *Cloud Hosted Router* (*CHR*) the licensing is perpetual: Buy once, use forever - but it needs regular renewal. This script checks licensing state and sends a notification to warn before expiration. -### Sample notification +### Sample notifications -![check-perpetual-license notification](check-perpetual-license.d/notification.avif) +![check-perpetual-license notification warn](check-perpetual-license.d/notification-01-warn.avif) +![check-perpetual-license notification renew](check-perpetual-license.d/notification-02-renew.avif) Requirements and installation ----------------------------- diff --git a/doc/check-routeros-update.d/notification-01-found.avif b/doc/check-routeros-update.d/notification-01-found.avif new file mode 100644 index 0000000..54dd2df Binary files /dev/null and b/doc/check-routeros-update.d/notification-01-found.avif differ diff --git a/doc/check-routeros-update.d/notification-02-neighbor.avif b/doc/check-routeros-update.d/notification-02-neighbor.avif new file mode 100644 index 0000000..db4e980 Binary files /dev/null and b/doc/check-routeros-update.d/notification-02-neighbor.avif differ diff --git a/doc/check-routeros-update.d/notification.avif b/doc/check-routeros-update.d/notification.avif deleted file mode 100644 index 50317cf..0000000 Binary files a/doc/check-routeros-update.d/notification.avif and /dev/null differ diff --git a/doc/check-routeros-update.d/terminal.avif b/doc/check-routeros-update.d/terminal.avif new file mode 100644 index 0000000..af95059 Binary files /dev/null and b/doc/check-routeros-update.d/terminal.avif differ diff --git a/doc/check-routeros-update.md b/doc/check-routeros-update.md index a45e075..029182d 100644 --- a/doc/check-routeros-update.md +++ b/doc/check-routeros-update.md @@ -31,13 +31,14 @@ automatically is supported. > of view. At the same time it can be source of serve breakage. So test > versions in lab and read > [changelog â†—ī¸](https://mikrotik.com/download/changelogs/) and -> [forum â†—ī¸](https://forum.mikrotik.com/viewforum.php?f=21) before deploying +> [forum â†—ī¸](https://forum.mikrotik.com/c/announcements/5) before deploying > to your production environment! Automatic updates should be handled > with care! -### Sample notification +### Sample notifications -![check-routeros-update notification](check-routeros-update.d/notification.avif) +![check-routeros-update notification found](check-routeros-update.d/notification-01-found.avif) +![check-routeros-update notification neighbor](check-routeros-update.d/notification-02-neighbor.avif) Requirements and installation ----------------------------- @@ -87,6 +88,8 @@ Be notified when run from scheduler or run it manually: If an update is found you can install it right away. +![Terminal](check-routeros-update.d/terminal.avif) + Installing script [packages-update](packages-update.md) gives extra options. Tips & Tricks diff --git a/doc/collect-wireless-mac.d/notification.avif b/doc/collect-wireless-mac.d/notification.avif index a2833f0..4b21b2f 100644 Binary files a/doc/collect-wireless-mac.d/notification.avif and b/doc/collect-wireless-mac.d/notification.avif differ diff --git a/doc/daily-psk.d/notification.avif b/doc/daily-psk.d/notification.avif index dd0b1b6..79cfc3e 100644 Binary files a/doc/daily-psk.d/notification.avif and b/doc/daily-psk.d/notification.avif differ diff --git a/doc/fw-addr-lists.md b/doc/fw-addr-lists.md index d09383b..46b80c2 100644 --- a/doc/fw-addr-lists.md +++ b/doc/fw-addr-lists.md @@ -32,7 +32,10 @@ certificate is checked. > âš ī¸ **Warning**: The script does not limit the size of a list, but keep in > mind that huge lists can exhaust your device's resources (RAM and CPU), -> and may take a long time to process. +> and may take a long time to process. +> Even crashes for the complete scripting (and CLI) subsystem are possible. +> This should be logged accordingly with warnings when global functions are +> reloaded from scheduler. Requirements and installation ----------------------------- diff --git a/doc/log-forward.d/notification-01-info.avif b/doc/log-forward.d/notification-01-info.avif new file mode 100644 index 0000000..6d25467 Binary files /dev/null and b/doc/log-forward.d/notification-01-info.avif differ diff --git a/doc/log-forward.d/notification-02-warn.avif b/doc/log-forward.d/notification-02-warn.avif new file mode 100644 index 0000000..ff61c0c Binary files /dev/null and b/doc/log-forward.d/notification-02-warn.avif differ diff --git a/doc/log-forward.d/notification.avif b/doc/log-forward.d/notification.avif deleted file mode 100644 index a0f9ab3..0000000 Binary files a/doc/log-forward.d/notification.avif and /dev/null differ diff --git a/doc/log-forward.md b/doc/log-forward.md index f6086c8..d5a2f13 100644 --- a/doc/log-forward.md +++ b/doc/log-forward.md @@ -35,9 +35,10 @@ The script works around the limitations, for example it does: It is intended to be run periodically from scheduler, then collects new log messages and forwards them via notification. -### Sample notification +### Sample notifications -![log-forward notification](log-forward.d/notification.avif) +![log-forward notification info](log-forward.d/notification-01-info.avif) +![log-forward notification warn](log-forward.d/notification-02-warn.avif) Requirements and installation ----------------------------- diff --git a/doc/mod/inspectvar.d/01-inspectvar.avif b/doc/mod/inspectvar.d/01-inspectvar.avif new file mode 100644 index 0000000..1be1358 Binary files /dev/null and b/doc/mod/inspectvar.d/01-inspectvar.avif differ diff --git a/doc/mod/inspectvar.d/inspectvar.avif b/doc/mod/inspectvar.d/inspectvar.avif deleted file mode 100644 index f1da1d4..0000000 Binary files a/doc/mod/inspectvar.d/inspectvar.avif and /dev/null differ diff --git a/doc/mod/inspectvar.md b/doc/mod/inspectvar.md index 7daba15..e5c5629 100644 --- a/doc/mod/inspectvar.md +++ b/doc/mod/inspectvar.md @@ -33,7 +33,7 @@ Call the function `$InspectVar` with a variable as parameter: $InspectVar $ModeButton; -![InspectVar](inspectvar.d/inspectvar.avif) +![InspectVar](inspectvar.d/01-inspectvar.avif) --- [âŦ…ī¸ Go back to main README](../../README.md) diff --git a/doc/mod/ipcalc.d/01-ipcalc.avif b/doc/mod/ipcalc.d/01-ipcalc.avif new file mode 100644 index 0000000..aaad616 Binary files /dev/null and b/doc/mod/ipcalc.d/01-ipcalc.avif differ diff --git a/doc/mod/ipcalc.d/02-ipcalcreturn.avif b/doc/mod/ipcalc.d/02-ipcalcreturn.avif new file mode 100644 index 0000000..2459eaf Binary files /dev/null and b/doc/mod/ipcalc.d/02-ipcalcreturn.avif differ diff --git a/doc/mod/ipcalc.d/ipcalc.avif b/doc/mod/ipcalc.d/ipcalc.avif deleted file mode 100644 index fe726e8..0000000 Binary files a/doc/mod/ipcalc.d/ipcalc.avif and /dev/null differ diff --git a/doc/mod/ipcalc.d/ipcalcreturn.avif b/doc/mod/ipcalc.d/ipcalcreturn.avif deleted file mode 100644 index 5e4dd57..0000000 Binary files a/doc/mod/ipcalc.d/ipcalcreturn.avif and /dev/null differ diff --git a/doc/mod/ipcalc.md b/doc/mod/ipcalc.md index c07853e..f8d407c 100644 --- a/doc/mod/ipcalc.md +++ b/doc/mod/ipcalc.md @@ -43,7 +43,7 @@ It expects an IP address in CIDR notation as argument. $IPCalc 192.168.88.1/24; -![IPCalc](ipcalc.d/ipcalc.avif) +![IPCalc](ipcalc.d/01-ipcalc.avif) ### IPCalcReturn @@ -53,7 +53,7 @@ the information in a named array. :put ([ $IPCalcReturn 192.168.88.1/24 ]->"broadcast"); -![IPCalcReturn](ipcalc.d/ipcalcreturn.avif) +![IPCalcReturn](ipcalc.d/02-ipcalcreturn.avif) --- [âŦ…ī¸ Go back to main README](../../README.md) diff --git a/doc/mod/notification-matrix.d/01-authenticate.avif b/doc/mod/notification-matrix.d/01-authenticate.avif index b897943..d5b8025 100644 Binary files a/doc/mod/notification-matrix.d/01-authenticate.avif and b/doc/mod/notification-matrix.d/01-authenticate.avif differ diff --git a/doc/mod/notification-matrix.d/02-join-room.avif b/doc/mod/notification-matrix.d/02-join-room.avif index ad99ffd..20c4e79 100644 Binary files a/doc/mod/notification-matrix.d/02-join-room.avif and b/doc/mod/notification-matrix.d/02-join-room.avif differ diff --git a/doc/mod/notification-matrix.md b/doc/mod/notification-matrix.md index da6d6de..ad4cf4f 100644 --- a/doc/mod/notification-matrix.md +++ b/doc/mod/notification-matrix.md @@ -49,7 +49,7 @@ your server in device's certificate store. The example below is for `matrix.org`, which uses a trust chain from *Google Trust Services*. Run this to import the required certificate: - $CertificateAvailable "GTS Root R4"; + $CertificateAvailable "GTS Root R4" "fetch"; Replace the CA certificate name with what ever is needed for your server. You may want to find the diff --git a/doc/mod/notification-telegram.d/newbot.avif b/doc/mod/notification-telegram.d/01-newbot.avif similarity index 100% rename from doc/mod/notification-telegram.d/newbot.avif rename to doc/mod/notification-telegram.d/01-newbot.avif diff --git a/doc/mod/notification-telegram.d/02-getchatid.avif b/doc/mod/notification-telegram.d/02-getchatid.avif new file mode 100644 index 0000000..0112449 Binary files /dev/null and b/doc/mod/notification-telegram.d/02-getchatid.avif differ diff --git a/doc/mod/notification-telegram.d/setuserpic.avif b/doc/mod/notification-telegram.d/03-setuserpic.avif similarity index 100% rename from doc/mod/notification-telegram.d/setuserpic.avif rename to doc/mod/notification-telegram.d/03-setuserpic.avif diff --git a/doc/mod/notification-telegram.d/getchatid.avif b/doc/mod/notification-telegram.d/getchatid.avif deleted file mode 100644 index 7792969..0000000 Binary files a/doc/mod/notification-telegram.d/getchatid.avif and /dev/null differ diff --git a/doc/mod/notification-telegram.md b/doc/mod/notification-telegram.md index 804104f..d33f6fc 100644 --- a/doc/mod/notification-telegram.md +++ b/doc/mod/notification-telegram.md @@ -36,7 +36,7 @@ Configuration Open Telegram, then start a chat with [BotFather â†—ī¸](https://t.me/BotFather) and create your own bot: -![create new bot](notification-telegram.d/newbot.avif) +![create new bot](notification-telegram.d/01-newbot.avif) Set that token from *BotFather* (use your own!) to `TelegramTokenId`, for now just temporarily: @@ -49,7 +49,7 @@ then send your first message. Any text will do. On your device run $GetTelegramChatId; -![get chat id](notification-telegram.d/getchatid.avif) +![get chat id](notification-telegram.d/02-getchatid.avif) Finally edit `global-config-overlay`, add `TelegramTokenId` with the token from *BotFather* and `TelegramChatId` with your retrieved chat id. Then @@ -104,12 +104,15 @@ Tips & Tricks You can use a profile photo for your bot to make it recognizable. Open the chat with [BotFather â†—ī¸](https://t.me/BotFather) and set it there. -![set profile photo](notification-telegram.d/setuserpic.avif) +![set profile photo](notification-telegram.d/03-setuserpic.avif) -Have a look at my -[RouterOS-Scripts Logo Color Changer](https://git.eworm.de/cgit/routeros-scripts/plain/contrib/logo-color.html) +Have a look at my [Logo Color Changer](../../contrib/logo-color.html) to create a colored version of this scripts' logo. +> đŸ’Ąī¸ **Hint**: The above link may be broken on code hosting sites. +> Use [Logo Color Changer](https://rsc.eworm.de/main/contrib/logo-color.html) +> instead. + See also -------- diff --git a/doc/mod/scriptrunonce.d/01-scriptrunonce.avif b/doc/mod/scriptrunonce.d/01-scriptrunonce.avif new file mode 100644 index 0000000..461c398 Binary files /dev/null and b/doc/mod/scriptrunonce.d/01-scriptrunonce.avif differ diff --git a/doc/mod/scriptrunonce.d/scriptrunonce.avif b/doc/mod/scriptrunonce.d/scriptrunonce.avif deleted file mode 100644 index 27ccd41..0000000 Binary files a/doc/mod/scriptrunonce.d/scriptrunonce.avif and /dev/null differ diff --git a/doc/mod/scriptrunonce.md b/doc/mod/scriptrunonce.md index 955d12e..db861bb 100644 --- a/doc/mod/scriptrunonce.md +++ b/doc/mod/scriptrunonce.md @@ -48,9 +48,9 @@ Usage and invocation The function `$ScriptRunOnce` expects an URL (or name if `ScriptRunOnceBaseUrl` is given) pointing to a script as parameter. - $ScriptRunOnce https://git.eworm.de/cgit/routeros-scripts/plain/doc/mod/scriptrunonce.d/hello-world.rsc; + $ScriptRunOnce https://rsc.eworm.de/main/doc/mod/scriptrunonce.d/hello-world.rsc; -![ScriptRunOnce](scriptrunonce.d/scriptrunonce.avif) +![ScriptRunOnce](scriptrunonce.d/01-scriptrunonce.avif) Giving multiple scripts is possible, separated by comma. diff --git a/doc/mod/ssh-keys-import.md b/doc/mod/ssh-keys-import.md index 344f4bc..abf4d39 100644 --- a/doc/mod/ssh-keys-import.md +++ b/doc/mod/ssh-keys-import.md @@ -38,8 +38,9 @@ import that key: $SSHKeysImport "ssh-rsa AAAAB3Nza...QYZk8= user" admin; The third part of the key (`user` in this example) is inherited as -`key-owner` in RouterOS. Also the `MD5` fingerprint is recorded, this helps -to audit and verify the available keys. +`key-owner` in RouterOS (or `info` starting with RouterOS 7.21beta2). Also +the `MD5` fingerprint is recorded, this helps to audit and verify the +available keys. > â„šī¸ī¸ **Info**: Use `ssh-keygen` to show a fingerprint of an existing public > key file: `ssh-keygen -l -E md5 -f ~/.ssh/id_ed25519.pub` @@ -50,12 +51,10 @@ The functions `$SSHKeysImportFile` can read an `authorized_keys`-style file and import all the keys. The user given to the function can be overwritting from comments in the file. Create a file `keys.pub` with this content: -``` -ssh-ed25519 AAAAC3Nza...3OcN8A user@client -ssh-rsa AAAAB3Nza...ozyts= worker@station -# user=example -ssh-rsa AAAAB3Nza...GXQVk= person@host -``` + ssh-ed25519 AAAAC3Nza...3OcN8A user@client + ssh-rsa AAAAB3Nza...ozyts= worker@station + # user=example + ssh-rsa AAAAB3Nza...GXQVk= person@host Then import it with: diff --git a/doc/mode-button.md b/doc/mode-button.md index be15bc9..af95ddb 100644 --- a/doc/mode-button.md +++ b/doc/mode-button.md @@ -26,17 +26,15 @@ can configure the reset button to act the same, see Copy this code to terminal to check: -``` -:if ([ :len [ /system/routerboard/mode-button/print as-value ] ] > 0) do={ - :put "Mode button is supported."; -} else={ - :if ([ :len [ /system/routerboard/reset-button/print as-value ] ] > 0) do={ - :put "Mode button is not supported, but reset button is."; - } else={ - :put "Neither mode button nor reset button is supported."; - } -} -``` + :if ([ :len [ /system/routerboard/mode-button/print as-value ] ] > 0) do={ + :put "Mode button is supported."; + } else={ + :if ([ :len [ /system/routerboard/reset-button/print as-value ] ] > 0) do={ + :put "Mode button is not supported, but reset button is."; + } else={ + :put "Neither mode button nor reset button is supported."; + } + } Requirements and installation ----------------------------- diff --git a/doc/netwatch-notify.d/notification-01-down.avif b/doc/netwatch-notify.d/notification-01-down.avif index 894fb23..2257a0d 100644 Binary files a/doc/netwatch-notify.d/notification-01-down.avif and b/doc/netwatch-notify.d/notification-01-down.avif differ diff --git a/doc/netwatch-notify.d/notification-02-up.avif b/doc/netwatch-notify.d/notification-02-up.avif index 9021a93..4147cb7 100644 Binary files a/doc/netwatch-notify.d/notification-02-up.avif and b/doc/netwatch-notify.d/notification-02-up.avif differ diff --git a/doc/packages-update.md b/doc/packages-update.md index 75225fe..a0a1795 100644 --- a/doc/packages-update.md +++ b/doc/packages-update.md @@ -46,8 +46,8 @@ Configuration The configuration goes to `global-config-overlay`, this is the only parameter: -* `PackagesUpdateDeferReboot`: defer the reboot for night (between 3 AM - and 5 AM) +* `PackagesUpdateDeferReboot`: defer the reboot for night (between 3 AM and + 5 AM), use a numerical value in days suffixed with a `d` to defer further By modifying the scheduler's `start-time` you can force the reboot at different time. diff --git a/doc/sms-forward.d/notification.avif b/doc/sms-forward.d/notification.avif index 01eb7ba..14764a3 100644 Binary files a/doc/sms-forward.d/notification.avif and b/doc/sms-forward.d/notification.avif differ diff --git a/fw-addr-lists.d/allow b/fw-addr-lists.d/allow index 8b59ed7..8c4ca3c 100644 --- a/fw-addr-lists.d/allow +++ b/fw-addr-lists.d/allow @@ -1,3 +1,3 @@ # an ip address list for use with fw-addr-lists script -# https://git.eworm.de/cgit/routeros-scripts/about/doc/fw-addr-lists.md -git.eworm.de +# https://rsc.eworm.de/doc/fw-addr-lists.md +rsc.eworm.de diff --git a/fw-addr-lists.d/block b/fw-addr-lists.d/block index 5e9fef2..86a6c62 100644 --- a/fw-addr-lists.d/block +++ b/fw-addr-lists.d/block @@ -1,5 +1,5 @@ # an ip address list for use with fw-addr-lists script -# https://git.eworm.de/cgit/routeros-scripts/about/doc/fw-addr-lists.md +# https://rsc.eworm.de/doc/fw-addr-lists.md # example.net 93.184.216.34 diff --git a/fw-addr-lists.rsc b/fw-addr-lists.rsc index efd37db..e5a71aa 100644 --- a/fw-addr-lists.rsc +++ b/fw-addr-lists.rsc @@ -22,9 +22,12 @@ :global EitherOr; :global FetchHuge; :global HumanReadableNum; + :global IfThenElse; :global LogPrint; :global LogPrintOnce; :global LogPrintVerbose; + :global NetMask4; + :global NetMask6; :global ScriptLock; :global WaitFullyConnected; @@ -48,6 +51,12 @@ } $WaitFullyConnected; + :if ([ :len [ /log/find where topics=({"script"; "warning"}) \ + message=("\$LogPrintOnce: The message is already in log, scripting subsystem may have crashed before!") ] ] > 0) do={ + $LogPrintOnce warning $ScriptName ("Scripting subsystem may have crashed, possibly caused by us. Delaying!"); + :delay 5m; + } + :local ListComment ("managed by " . $ScriptName); :foreach FwListName,FwList in=$FwAddrLists do={ @@ -65,7 +74,7 @@ :if ([ :len ($List->"cert") ] > 0) do={ :set CheckCertificate true; - :if ([ $CertificateAvailable ($List->"cert") ] = false) do={ + :if ([ $CertificateAvailable ($List->"cert") "fetch" ] = false) do={ $LogPrint warning $ScriptName ("Downloading required certificate (" . $FwListName . \ " / " . $List->"url" . ") failed, trying anyway."); } @@ -97,7 +106,7 @@ :foreach Line in=[ :deserialize $Data delimiter="\n" from=dsv options=dsv.plain ] do={ :set Line ($Line->0); :local Address; - :if ([ :pick $Line 0 1 ] = "{") do={ + :if ([ :pick $Line 0 1 ] = "{" && [ :pick $Line ([ :len $Line ] - 1) ] = "}") do={ :do { :set Address [ :tostr ([ :deserialize from=json $Line ]->"cidr") ]; } on-error={ } @@ -105,22 +114,35 @@ :set Address ([ :pick $Line 0 [ $FindDelim $Line ] ] . ($List->"cidr")); } :do { - :local Branch [ $GetBranch $Address ]; + :local Branch; :if ($Address ~ "^[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}(/[0-9]{1,2})?\$") do={ - :if ($Address ~ "/32\$") do={ - :set Address [ :pick $Address 0 ([ :len $Address ] - 3) ]; + :local Net $Address; + :local CIDR 32; + :local Slash [ :find $Address "/" ]; + :if ([ :typeof $Slash ] = "num") do={ + :set Net [ :toip [ :pick $Address 0 $Slash ] ] + :set CIDR [ :pick $Address ($Slash + 1) [ :len $Address ] ]; + :set Address [ :tostr (([ :toip $Net ] & [ $NetMask4 $CIDR ]) . [ $IfThenElse ($CIDR < 32) ("/" . $CIDR) ]) ]; } + :set Branch [ $GetBranch $Address ]; :set ($IPv4Addresses->$Branch->$Address) $TimeOut; :error true; } :if ($Address ~ "^[0-9a-zA-Z]*:[0-9a-zA-Z:\\.]+(/[0-9]{1,3})?\$") do={ - :if ([ :typeof [ :find $Address "/" ] ] = "nil") do={ - :set Address ($Address . "/128"); + :local Net $Address; + :local CIDR 128; + :local Slash [ :find $Address "/" ]; + :if ([ :typeof $Slash ] = "num") do={ + :set Net [ :toip6 [ :pick $Address 0 $Slash ] ] + :set CIDR [ :pick $Address ($Slash + 1) [ :len $Address ] ]; } + :set Address (([ :toip6 $Net ] & [ $NetMask6 $CIDR ]) . "/" . $CIDR); + :set Branch [ $GetBranch $Address ]; :set ($IPv6Addresses->$Branch->$Address) $TimeOut; :error true; } :if ($Address ~ "^[\\.a-zA-Z0-9-]+\\.[a-zA-Z]{2,}\$") do={ + :set Branch [ $GetBranch $Address ]; :set ($IPv4Addresses->$Branch->$Address) $TimeOut; :set ($IPv6Addresses->$Branch->$Address) $TimeOut; :error true; diff --git a/general/clipboard.js b/general/clipboard.js new file mode 100644 index 0000000..cbcfa4a --- /dev/null +++ b/general/clipboard.js @@ -0,0 +1,8 @@ +/* copy code to clipboard */ +function CopyToClipboard(element) { + element.style.filter = 'invert(1)'; + navigator.clipboard.writeText(element.firstElementChild.textContent); + setTimeout(function() { + element.style.filter = 'invert(0)'; + }, 100); +} diff --git a/general/eworm-meadow.avif b/general/eworm-meadow.avif new file mode 100644 index 0000000..f592d59 Binary files /dev/null and b/general/eworm-meadow.avif differ diff --git a/README.d/upstream.png b/general/qr-code.png similarity index 100% rename from README.d/upstream.png rename to general/qr-code.png diff --git a/general/style.css b/general/style.css new file mode 100644 index 0000000..c3e8690 --- /dev/null +++ b/general/style.css @@ -0,0 +1,108 @@ +/* stylesheet for RouterOS Scripts */ +body { + background-color: transparent; + font-family: fira-sans, sans-serif; + font-size: 10pt; + line-height: 1.6; +} +h2 { + border-bottom: 1px solid #ccc; + color: #000; +} +a { + text-decoration: none; +} +a:hover { + text-decoration: underline; +} +blockquote { + border-left: 4px solid #ccc; + padding: 0 10px; + color: #555; +} +code { + margin: 0 2px; + padding: 2px 5px; + border: 1px solid #ccc; + background-color: #f8f8f8; + border-radius: 3px; +} +div.notification { + position: relative; + float: none; + width: 600px; + border: 3px outset #6c5d53; + /* border-radius: 5px; */ + padding: 10px; + background-color: #e6e6e6; +} +div.content { + padding-left: 60px; +} +hr { + clear: both; +} +img.logo { + float: left; + /* border-radius: 50%; */ +} +p.foot { + color: #777; + text-align: center; +} +p.heading { + font-size: 120%; + margin: 0px; + font-weight: bold; + text-decoration: underline; +} +p.hint { + display: none; +} +pre { + font-family: fira-mono, monospace; + white-space: pre-wrap; +} +pre.code { + background-color: #f8f8f8; + border: 1px solid #ccc; + overflow: auto; + padding: 6px 10px; + border-radius: 3px; +} +pre code { + margin: 0; + padding: 0; + border: 0; +} +pre.code::before { + content: "📋 Copy!"; + float: right; + border: 1px solid #ccc; + border-radius: 3px; +} +span.link { + color: #863600; +} +td.head { + line-height: 1.2; + padding: 0 2em; +} +td.head .top { + font-size: 250%; + font-weight: bold; +} +td.head .bottom { + font-size: 125%; + color: #555; +} +@media only screen and (orientation: landscape) { + body { + margin-left: 10vw; + margin-right: 10vw; + } + div.notification { + float: right; + margin: 10px; + } +} diff --git a/global-config-overlay.rsc b/global-config-overlay.rsc index 9afaceb..88bb4d9 100644 --- a/global-config-overlay.rsc +++ b/global-config-overlay.rsc @@ -6,7 +6,7 @@ # https://rsc.eworm.de/#editing-configuration # Copy relevant configuration from global-config, paste and modify it here. -# https://rsc.eworm.de/global-config.rsc +# https://rsc.eworm.de/main/global-config.rsc # End of global-config-overlay diff --git a/global-functions.rsc b/global-functions.rsc index ef849e0..75eccb9 100644 --- a/global-functions.rsc +++ b/global-functions.rsc @@ -38,6 +38,8 @@ :global ExitError; :global FetchHuge; :global FetchUserAgentStr; +:global FileExists; +:global FileGet; :global FormatLine; :global FormatMultiLines; :global GetMacVendor; @@ -59,6 +61,8 @@ :global MAX; :global MIN; :global MkDir; +:global NetMask4; +:global NetMask6; :global NotificationFunctions; :global ParseDate; :global ParseKeyValueStore; @@ -102,11 +106,15 @@ # check and download required certificate :set CertificateAvailable do={ :local CommonName [ :tostr $1 ]; + :local UseFor [ :tostr $2 ]; :global CertificateDownload; + :global EitherOr; :global LogPrint; :global ParseKeyValueStore; + :set UseFor [ $EitherOr $UseFor "undefined" ]; + :if ([ /system/resource/get free-hdd-space ] < 8388608 && \ [ /certificate/settings/get crl-download ] = true && \ [ /certificate/settings/get crl-store ] = "system") do={ @@ -119,6 +127,14 @@ :return false; } + :local CertSettings [ /certificate/settings/get ]; + :if ((($CertSettings->"builtin-trust-anchors") = "trusted" || \ + ($CertSettings->"builtin-trust-store") ~ $UseFor || \ + ($CertSettings->"builtin-trust-store") = "all") && \ + [[ :parse (":return [ :len [ /certificate/builtin/find where common-name=\"" . $CommonName . "\" ] ]") ]] > 0) do={ + :return true; + } + :if ([ :len [ /certificate/find where common-name=$CommonName ] ] = 0) do={ $LogPrint info $0 ("Certificate with CommonName '" . $CommonName . "' not available."); :if ([ $CertificateDownload $CommonName ] = false) do={ @@ -126,6 +142,11 @@ } } + :if ([ :len [ /certificate/find where common-name=$CommonName ] ] > 1) do={ + $LogPrint info $0 ("There are " . $CertCount . " Certificates with CommonName '" . $CommonName . "'. Should be ok."); + :return true; + } + :local CertVal [ /certificate/get [ find where common-name=$CommonName ] ]; :while (($CertVal->"akid") != "" && ($CertVal->"akid") != ($CertVal->"skid")) do={ :if ([ :len [ /certificate/find where skid=($CertVal->"akid") ] ] = 0) do={ @@ -147,7 +168,6 @@ :global ScriptUpdatesBaseUrl; :global ScriptUpdatesUrlSuffix; - :global CertificateAvailable; :global CertificateNameByCN; :global CleanName; :global FetchUserAgentStr; @@ -358,6 +378,7 @@ :global CertificateAvailable; :global CleanFilePath; + :global FileExists; :global LogPrint; :global MkDir; :global RmFile; @@ -378,12 +399,12 @@ :return false; } - :if ([ :len [ /file/find where name=$PkgDest type="package" ] ] > 0) do={ + :if ([ $FileExists $PkgDest "package" ] = true) do={ $LogPrint info $0 ("Package file " . $PkgName . " already exists."); :return true; } - :if ([ $CertificateAvailable "ISRG Root X1" ] = false) do={ + :if ([ $CertificateAvailable "ISRG Root X1" "fetch" ] = false) do={ $LogPrint error $0 ("Downloading required certificate failed."); :return false; } @@ -400,7 +421,7 @@ :return false; } - :if ([ /file/get [ find where name=$PkgDest ] type ] != "package") do={ + :if ([ $FileExists $PkgDest "package" ] = false) do={ $LogPrint warning $0 ("Downloaded file is not a package, removing."); $RmFile $PkgDest; :return false; @@ -452,7 +473,7 @@ :local Error [ :tostr $3 ]; :global IfThenElse; - :global LogPrint; + :global LogPrint; :if ($ExitOK = "false") do={ $LogPrint error $Name ([ $IfThenElse ([ :pick $Name 0 1 ] = "\$") \ @@ -518,10 +539,57 @@ :set FetchUserAgentStr do={ :local Caller [ :tostr $1 ]; + :global CommitId; + :global CommitInfo; + + :global IfThenElse; + :local Resource [ /system/resource/get ]; - :return ("User-Agent: Mikrotik/" . $Resource->"version" . " " . \ - $Resource->"architecture-name" . " " . $Caller . "/Fetch (https://rsc.eworm.de/)"); + :return ("User-Agent: Mikrotik/" . $Resource->"version" . " " . $Resource->"architecture-name" . \ + " " . $Caller . "/Fetch (https://rsc.eworm.de/" . [ $IfThenElse ($CommitId != "unknown") \ + ("; " . $CommitInfo . "/" . [ :pick $CommitId 0 8 ]) ] . ")"); +} + +# check for existence of file, optionally with type +:set FileExists do={ + :local FileName [ :tostr $1 ]; + :local Type [ :tostr $2 ]; + + :global FileGet; + + :local FileVal [ $FileGet $FileName ]; + :if ($FileVal = false) do={ + :return false; + } + + :if ([ :len ($FileVal->"size") ] = 0) do={ + :return false; + } + + :if ([ :len $Type ] = 0 || $FileVal->"type" = $Type) do={ + :return true; + } + + :return false; +} + +# get file properties in array, or false on error +:set FileGet do={ + :local FileName [ :tostr $1 ]; + + :global WaitForFile; + + :if ([ $WaitForFile $FileName 0s ] = false) do={ + :return false; + } + + :local FileVal false; + :do { + :set FileVal [ /file/get $FileName ]; + } on-error={ } + + :return $FileVal; } # format a line for output @@ -578,7 +646,7 @@ } :do { - :if ([ $CertificateAvailable "GTS Root R4" ] = false) do={ + :if ([ $CertificateAvailable "GTS Root R4" "fetch" ] = false) do={ $LogPrint warning $0 ("Downloading required certificate failed."); :error false; } @@ -875,6 +943,7 @@ :local Path [ :tostr $1 ]; :global CleanFilePath; + :global FileGet; :global LogPrint; :global RmDir; :global WaitForFile; @@ -912,7 +981,8 @@ $LogPrint debug $0 ("Making directory: " . $Path); - :if ([ :len [ /file/find where name=$Path type="directory" ] ] = 1) do={ + :local PathVal [ $FileGet $Path ]; + :if ($PathVal->"type" = "directory") do={ $LogPrint debug $0 ("... which already exists."); :return true; } @@ -934,6 +1004,43 @@ :return true; } +# return an IPv4 netmask for CIDR +:set NetMask4 do={ + :local CIDR [ :tonum $1 ]; + + :return ((255.255.255.255 << (32 - $CIDR)) & 255.255.255.255); +} + +# return an IPv6 netmask for CIDR +:set NetMask6 do={ + :local FuncName $0; + :local CIDR [ :tostr $1 ]; + + :global IfThenElse; + :global MAX; + :global MIN; + + :global NetMask6Cache; + + :if ([ :typeof ($NetMask6Cache->$CIDR) ] = "ip6") do={ + :return ($NetMask6Cache->$CIDR); + } + + :if ([ :typeof $NetMask6Cache ] = "nothing") do={ + :set NetMask6Cache ({}); + } + + :local Mask ""; + :for I from=0 to=7 do={ + :set Mask ($Mask . \ + [ :convert from=num to=hex (0xffff - (0xffff >> [ :tonum [ $MIN [ $MAX ($CIDR - (16 * $I)) 0 ] 16 ] ])) ] . \ + [ $IfThenElse ($I < 7) ":" ]); + } + :set Mask [ :toip6 $Mask ]; + :set ($NetMask6Cache->$CIDR) $Mask; + :return $Mask; +} + # prepare NotificationFunctions array :if ([ :typeof $NotificationFunctions ] != "array") do={ :set NotificationFunctions ({}); @@ -1037,25 +1144,26 @@ :set RmDir do={ :local DirName [ :tostr $1 ]; + :global FileGet; :global LogPrint; $LogPrint debug $0 ("Removing directory: ". $DirName); - :if ([ :len [ /file/find where name=$DirName type!=directory ] ] > 0) do={ - $LogPrint error $0 ("Directory '" . $DirName . "' is not a directory."); - :return false; - } - - :local Dir [ /file/find where name=$DirName type=directory ]; - :if ([ :len $Dir ] = 0) do={ + :local DirVal [ $FileGet $DirName ]; + :if ($DirVal = false) do={ $LogPrint debug $0 ("... which does not exist."); :return true; } + :if ($DirVal->"type" != "directory") do={ + $LogPrint error $0 ("Directory '" . $DirName . "' is not a directory."); + :return false; + } + :onerror Err { - /file/remove $Dir; + /file/remove $DirName; } do={ - $LogPrint error $0 ("Removing directory '" . $DirName . "' (" . $Dir . ") failed: " . $Err); + $LogPrint error $0 ("Removing directory '" . $DirName . "' failed: " . $Err); :return false; } :return true; @@ -1065,25 +1173,26 @@ :set RmFile do={ :local FileName [ :tostr $1 ]; + :global FileGet; :global LogPrint; $LogPrint debug $0 ("Removing file: ". $FileName); - :if ([ :len [ /file/find where name=$FileName (type=directory or type=disk) ] ] > 0) do={ - $LogPrint error $0 ("File '" . $FileName . "' is not a file."); - :return false; - } - - :local File [ /file/find where name=$FileName !(type=directory or type=disk) ]; - :if ([ :len $File ] = 0) do={ + :local FileVal [ $FileGet $FileName ]; + :if ($FileVal = false) do={ $LogPrint debug $0 ("... which does not exist."); :return true; } + :if ($FileVal->"type" = "directory" || $FileVal->"type" = "disk") do={ + $LogPrint error $0 ("File '" . $FileName . "' is not a file."); + :return false; + } + :onerror Err { - /file/remove $File; + /file/remove $FileName; } do={ - $LogPrint error $0 ("Removing file '" . $FileName . "' (" . $File . ") failed: " . $Err); + $LogPrint error $0 ("Removing file '" . $FileName . "' failed: " . $Err); :return false; } :return true; @@ -1145,7 +1254,7 @@ :global SymbolForNotification; :global ValidateSyntax; - :if ([ $CertificateAvailable "ISRG Root X2" ] = false) do={ + :if ([ $CertificateAvailable "ISRG Root X2" "fetch" ] = false) do={ $LogPrint warning $0 ("Downloading certificate failed, trying without."); } @@ -1196,7 +1305,7 @@ } :if ([ :len ($ScriptInfo->"certificate") ] > 0) do={ - :if ([ $CertificateAvailable ($ScriptInfo->"certificate") ] = false) do={ + :if ([ $CertificateAvailable ($ScriptInfo->"certificate") "fetch" ] = false) do={ $LogPrint warning $0 ("Downloading certificate failed, trying without."); } } @@ -1212,10 +1321,20 @@ :set SourceNew [ :tolf ($Result->"data") ]; } } do={ - $LogPrint warning $0 ("Failed fetching script '" . $ScriptVal->"name" . . "': " . $Err); + $LogPrint warning $0 ("Failed fetching script '" . $ScriptVal->"name" . "': " . $Err); + :if ($Err != "Fetch failed with status 404") do={ + :error false; + } + :if ($ScriptVal->"source" = "#!rsc by RouterOS\n") do={ $LogPrint warning $0 ("Removing dummy. Typo on installation?"); /system/script/remove $Script; + :error false; + } + :if ([ :len ($ScriptInfo->"base-url") ] = 0 && [ :len ($ScriptInfo->"url-suffix") ] = 0 && \ + [ :len $CheckSum ] = 0) do={ + $LogPrintOnce warning $0 \ + ("Added the script manually? Skip updates with 'ignore=true' in comment."); } :error false; } @@ -1553,9 +1672,12 @@ :set SymbolByUnicodeName do={ :local Name [ :tostr $1 ]; + :global EitherOr; :global LogPrintOnce; - :local Symbols { + :global SymbolsExtra; + + :local Symbols ({ "abacus"="\F0\9F\A7\AE"; "alarm-clock"="\E2\8F\B0"; "arrow-down"="\E2\AC\87"; @@ -1582,6 +1704,7 @@ "mobile-phone"="\F0\9F\93\B1"; "pushpin"="\F0\9F\93\8C"; "scissors"="\E2\9C\82"; + "scroll"="\F0\9F\93\9C"; "smiley-partying-face"="\F0\9F\A5\B3"; "smiley-smiling-face"="\E2\98\BA"; "smiley-winking-face-with-tongue"="\F0\9F\98\9C"; @@ -1590,14 +1713,20 @@ "star"="\E2\AD\90"; "warning-sign"="\E2\9A\A0"; "white-heavy-check-mark"="\E2\9C\85" - } + }, $SymbolsExtra); + + :local Magic [ :pick [ /system/clock/get date ] 4 10 ]; + :local Special { + "large-orange-circle-04-01"="\F0\9F\8D\8A"; + "large-orange-circle-10-31"="\F0\9F\8E\83"; + "large-red-circle-04-01"="\F0\9F\8D\92" }; :if ([ :len ($Symbols->$Name) ] = 0) do={ $LogPrintOnce warning $0 ("No symbol available for name '" . $Name . "'!"); :return ""; } - :return (($Symbols->$Name) . "\EF\B8\8F"); + :return ([ $EitherOr ($Special->($Name . $Magic)) ($Symbols->$Name) ] . "\EF\B8\8F"); } # return symbol for notification @@ -1724,16 +1853,14 @@ :global MAX; :set FileName [ $CleanFilePath $FileName ]; - :local I 1; - :local Delay ([ $MAX [ $EitherOr $WaitTime 2s ] 100ms ] / 10); + :local Delay ([ $MAX [ $EitherOr $WaitTime 2s ] 100ms ] / 9); - :while ([ :len [ /file/find where name=$FileName ] ] = 0) do={ - :if ($I >= 10) do={ - :return false; - } - :delay $Delay; - :set I ($I + 1); - } + :do { + :retry { + /file/get $FileName; + :return true; + } delay=$Delay max=10; + } on-error={ } :while ([ :len [ /file/find where name=$FileName ] ] > 0) do={ :do { @@ -1781,6 +1908,15 @@ } } +# add (and fix) global scripts scheduler +:local OnEvent "/system/script { run global-config; run global-functions; }"; +:if ([ :len [ /system/scheduler/find where name="global-scripts" ] ] = 0) do={ + /system/scheduler/add name="global-scripts" start-time=startup; +} +:if ([ /system/scheduler/get "global-scripts" on-event ] != $OnEvent) do={ + /system/scheduler/set "global-scripts" on-event=$OnEvent; +} + # Log success :local Resource [ /system/resource/get ]; $LogPrintOnce info $ScriptName ("Loaded on " . $Resource->"board-name" . \ diff --git a/log-forward.rsc b/log-forward.rsc index be7eff7..3d7d054 100644 --- a/log-forward.rsc +++ b/log-forward.rsc @@ -95,7 +95,7 @@ :set LogForwardRateLimit ($LogForwardRateLimit + 10); $SendNotification2 ({ origin=$ScriptName; \ - subject=([ $SymbolForNotification [ $IfThenElse ($Warning = true) "warning-sign" "memo" ] ] . \ + subject=([ $SymbolForNotification ("memo" . [ $IfThenElse ($Warning = true) ",warning-sign" ]) ] . \ "Log Forwarding"); \ message=("The log on " . $Identity . " contains " . [ $IfThenElse ($Count = 1) "this message" \ ("these " . $Count . " messages") ] . " after " . [ /system/resource/get uptime ] . " uptime." . \ diff --git a/mod/ipcalc.rsc b/mod/ipcalc.rsc index eacff6d..d65d472 100644 --- a/mod/ipcalc.rsc +++ b/mod/ipcalc.rsc @@ -34,20 +34,34 @@ # calculate and return netmask, network, min host, max host and broadcast :set IPCalcReturn do={ :local Input [ :tostr $1 ]; - :local Address [ :toip [ :pick $Input 0 [ :find $Input "/" ] ] ]; - :local Bits [ :tonum [ :pick $Input ([ :find $Input "/" ] + 1) [ :len $Input ] ] ]; - :local Mask ((255.255.255.255 << (32 - $Bits)) & 255.255.255.255); - :local Return { + :global NetMask4; + :global NetMask6; + + :local Address [ :pick $Input 0 [ :find $Input "/" ] ]; + :local Bits [ :tonum [ :pick $Input ([ :find $Input "/" ] + 1) [ :len $Input ] ] ]; + :local Mask; + :local One; + :if ([ :typeof [ :toip $Address ] ] = "ip") do={ + :set Address [ :toip $Address ]; + :set Mask [ $NetMask4 $Bits ]; + :set One 0.0.0.1; + } else={ + :set Address [ :toip6 $Address ]; + :set Mask [ $NetMask6 $Bits ]; + :set One ::1; + } + + :local Return ({ "address"=$Address; "netmask"=$Mask; "networkaddress"=($Address & $Mask); "networkbits"=$Bits; "network"=(($Address & $Mask) . "/" . $Bits); - "hostmin"=(($Address & $Mask) | 0.0.0.1); - "hostmax"=(($Address | ~$Mask) ^ 0.0.0.1); + "hostmin"=(($Address & $Mask) | $One); + "hostmax"=(($Address | ~$Mask) ^ $One); "broadcast"=($Address | ~$Mask); - } + }); :return $Return; } diff --git a/mod/notification-email.rsc b/mod/notification-email.rsc index 5293766..668daba 100644 --- a/mod/notification-email.rsc +++ b/mod/notification-email.rsc @@ -40,9 +40,11 @@ :global EitherOr; :global EMailGenerateFrom; + :global FileExists; :global IsDNSResolving; :global IsTimeSync; :global LogPrint; + :global RmFile; :local AllDone true; :local QueueLen [ :len $EmailQueue ]; @@ -93,14 +95,17 @@ :onerror Err { :local Attach ({}); :foreach File in=[ :toarray [ $EitherOr ($Message->"attach") "" ] ] do={ - :if ([ :len [ /file/find where name=$File ] ] = 1) do={ + :if ([ $FileExists $File ] = true) do={ :set Attach ($Attach, $File); } else={ $LogPrint warning $0 ("File '" . $File . "' does not exist, can not attach."); } } - /tool/e-mail/send from=[ $EMailGenerateFrom ] to=($Message->"to") cc=($Message->"cc") \ - subject=($Message->"subject") body=($Message->"body") file=$Attach; + :do { + /tool/e-mail/send from=[ $EMailGenerateFrom ] to=($Message->"to") \ + cc=($Message->"cc") subject=($Message->"subject") \ + body=($Message->"body") file=$Attach; + } on-error={ } :local Wait true; :do { :delay 1s; @@ -110,7 +115,7 @@ :set Wait false; :if (($Message->"remove-attach") = true) do={ :foreach File in=$Attach do={ - /file/remove $File; + $RmFile $File; } } } @@ -127,7 +132,7 @@ } :if ($AllDone = true && $QueueLen = [ :len $EmailQueue ]) do={ - /system/scheduler/remove $Scheduler; + /system/scheduler/remove [ find where name="_FlushEmailQueue" ]; :set EmailQueue; :return true; } diff --git a/mod/notification-ntfy.rsc b/mod/notification-ntfy.rsc index 7114020..dd10812 100644 --- a/mod/notification-ntfy.rsc +++ b/mod/notification-ntfy.rsc @@ -109,7 +109,7 @@ :onerror Err { :if ($Server = "ntfy.sh") do={ - :if ([ $CertificateAvailable "ISRG Root X1" ] = false) do={ + :if ([ $CertificateAvailable "ISRG Root X1" "fetch" ] = false) do={ $LogPrint warning $0 ("Downloading required certificate failed."); :error false; } diff --git a/mod/notification-telegram.rsc b/mod/notification-telegram.rsc index 2eb90e1..b1996a3 100644 --- a/mod/notification-telegram.rsc +++ b/mod/notification-telegram.rsc @@ -21,6 +21,7 @@ :global TelegramQueue; :global TelegramMessageIDs; + :global CertificateAvailable; :global IsFullyConnected; :global LogPrint; @@ -29,6 +30,11 @@ :return false; } + :if ([ $CertificateAvailable "Go Daddy Root Certificate Authority - G2" "fetch" ] = false) do={ + $LogPrint warning $0 ("Downloading required certificate failed."); + :return false; + } + :local AllDone true; :local QueueLen [ :len $TelegramQueue ]; @@ -66,7 +72,7 @@ :global CertificateAvailable; :global LogPrint; - :if ([ $CertificateAvailable "Go Daddy Root Certificate Authority - G2" ] = false) do={ + :if ([ $CertificateAvailable "Go Daddy Root Certificate Authority - G2" "fetch" ] = false) do={ $LogPrint warning $0 ("Downloading required certificate failed."); :return false; } @@ -191,7 +197,7 @@ "&reply_to_message_id=" . ($Notification->"replyto") . "&message_thread_id=" . $ThreadId . \ "&disable_web_page_preview=true&parse_mode=MarkdownV2"); :onerror Err { - :if ([ $CertificateAvailable "Go Daddy Root Certificate Authority - G2" ] = false) do={ + :if ([ $CertificateAvailable "Go Daddy Root Certificate Authority - G2" "fetch" ] = false) do={ $LogPrint warning $0 ("Downloading required certificate failed."); :error false; } diff --git a/mod/ssh-keys-import.rsc b/mod/ssh-keys-import.rsc index 9467525..8bea64e 100644 --- a/mod/ssh-keys-import.rsc +++ b/mod/ssh-keys-import.rsc @@ -40,7 +40,9 @@ :local FingerPrintMD5 [ :convert from=base64 transform=md5 to=hex ($KeyVal->1) ]; - :if ([ :len [ /user/ssh-keys/find where user=$User key-owner~("\\bmd5=" . $FingerPrintMD5 . "\\b") ] ] > 0) do={ + :local RegEx ("\\bmd5=" . $FingerPrintMD5 . "\\b"); + :if ([ :len [ /user/ssh-keys/find where user=$User \ + (key-owner~$RegEx or info~$RegEx) ] ] > 0) do={ $LogPrint warning $0 ("The ssh public key (MD5:" . $FingerPrintMD5 . \ ") is already available for user '" . $User . "'."); :return false; @@ -75,6 +77,7 @@ :local User [ :tostr $2 ]; :global EitherOr; + :global FileExists; :global LogPrint; :global ParseKeyValueStore; :global SSHKeysImport; @@ -84,8 +87,7 @@ :return false; } - :local File [ /file/find where name=$FileName ]; - :if ([ :len $File ] = 0) do={ + :if ([ $FileExists $FileName ] = false) do={ $LogPrint warning $0 ("File '" . $FileName . "' does not exist."); :return false; } diff --git a/netwatch-dns.rsc b/netwatch-dns.rsc index 4fa076a..eee5f85 100644 --- a/netwatch-dns.rsc +++ b/netwatch-dns.rsc @@ -112,18 +112,20 @@ :foreach DohServer in=$DohServers do={ :if ([ :len ($DohServer->"doh-cert") ] > 0) do={ - :if ([ $CertificateAvailable ($DohServer->"doh-cert") ] = false) do={ + :if ([ $CertificateAvailable ($DohServer->"doh-cert") "dns" ] = false) do={ $LogPrint warning $ScriptName ("Downloading certificate failed, trying without."); } } :local Data false; :onerror Err { - :set Data ([ /tool/fetch check-certificate=yes-without-crl output=user \ - http-header-field=({ "accept: application/dns-message" }) \ - url=(($DohServer->"doh-url") . "?dns=" . [ :convert to=base64 ([ :rndstr length=2 ] . \ - "\01\00" . "\00\01" . "\00\00" . "\00\00" . "\00\00" . "\09doh-check\05eworm\02de\00" . \ - "\00\10" . "\00\01") ]) as-value ]->"data"); + :retry { + :set Data ([ /tool/fetch check-certificate=yes-without-crl output=user \ + http-header-field=({ "accept: application/dns-message" }) \ + url=(($DohServer->"doh-url") . "?dns=" . [ :convert to=base64 ([ :rndstr length=2 ] . \ + "\01\00" . "\00\01" . "\00\00" . "\00\00" . "\00\00" . "\09doh-check\05eworm\02de\00" . \ + "\00\10" . "\00\01") ]) as-value ]->"data"); + } delay=1s max=3; } do={ $LogPrint warning $ScriptName ("Request to DoH server " . ($DohServer->"doh-url") . \ " failed: " . $Err); diff --git a/netwatch-notify.rsc b/netwatch-notify.rsc index 00f03cd..1fcc203 100644 --- a/netwatch-notify.rsc +++ b/netwatch-notify.rsc @@ -62,14 +62,14 @@ :local FwAddrList ($ScriptName . "-" . [ $GetRandom20CharAlNum ]); :if ([ :typeof [ :toip $Expected ] ] = "ip") do={ - /ip/firewall/address-list/add address=$Name list=$FwAddrList dynamic=yes timeout=10s; + /ip/firewall/address-list/add address=$Name list=$FwAddrList dynamic=yes timeout=30s; :delay 20ms; :if ([ :len [ /ip/firewall/address-list/find where list=$FwAddrList address=$Expected ] ] > 0) do={ :return true; } } :if ([ :typeof [ :toip6 $Expected ] ] = "ip6") do={ - /ipv6/firewall/address-list/add address=$Name list=$FwAddrList dynamic=yes timeout=10s; + /ipv6/firewall/address-list/add address=$Name list=$FwAddrList dynamic=yes timeout=30s; :delay 20ms; :if ([ :len [ /ipv6/firewall/address-list/find where list=$FwAddrList address=$Expected ] ] > 0) do={ :return true; @@ -127,7 +127,7 @@ $LogPrint [ $IfThenElse ($HostInfo->"no-resolve-fail" != true) warning debug ] \ $ScriptName ("Resolving name '" . $HostInfo->"resolve" . [ $IfThenElse \ ($HostInfo->"resolve" != $HostInfo->"name") ("' for " . $Type . " '" . \ - $HostInfo->"name") "" ] . "' failed: " . $Err); + $HostInfo->"name") "" ] . "' failed third time: " . $Err); } } } diff --git a/packages-update.rsc b/packages-update.rsc index 4fde131..379e818 100644 --- a/packages-update.rsc +++ b/packages-update.rsc @@ -31,19 +31,30 @@ :local Schedule do={ :local ScriptName [ :tostr $1 ]; + :global PackagesUpdateDeferReboot; + :global GetRandomNumber; + :global IfThenElse; :global LogPrint; :global RebootForUpdate do={ /system/reboot; } + :if ([ :len [ /system/scheduler/find where name="_RebootForUpdate" ] ] > 0) do={ + $LogPrint warning $ScriptName ("Scheduler for reboot already exists."); + :return false; + } + + :local Interval [ $IfThenElse ([ :totime $PackagesUpdateDeferReboot ] >= 1d) \ + $PackagesUpdateDeferReboot 1d ]; :local StartTime [ :tostr [ :totime (10800 + [ $GetRandomNumber 7200 ]) ] ]; - /system/scheduler/add name="_RebootForUpdate" start-time=$StartTime interval=1d \ + /system/scheduler/add name="_RebootForUpdate" start-time=$StartTime interval=$Interval \ on-event=("/system/scheduler/remove \"_RebootForUpdate\"; " . \ ":global RebootForUpdate; \$RebootForUpdate;"); $LogPrint info $ScriptName ("Scheduled reboot for update at " . $StartTime . \ - " local time (" . [ /system/clock/get time-zone-name ] . ")."); + " local time (" . [ /system/clock/get time-zone-name ] . ")" . \ + [ $IfThenElse ($Interval > 1d) (" deferred by " . $Interval) ] . "."); :return true; } @@ -153,7 +164,7 @@ :error true; } } else={ - :if ($PackagesUpdateDeferReboot = true) do={ + :if ($PackagesUpdateDeferReboot = true || [ :totime $PackagesUpdateDeferReboot ] >= 1d) do={ $Schedule $ScriptName; :set ExitOK true; :error true; diff --git a/sms-forward.rsc b/sms-forward.rsc index feb640e..161ae6b 100644 --- a/sms-forward.rsc +++ b/sms-forward.rsc @@ -68,8 +68,8 @@ $LogPrint warning $ScriptName ("Failed to remove message: " . $Err); } } else={ - :set Messages ($Messages . "\n\nOn " . $SmsVal->"timestamp" . \ - " type " . $SmsVal->"type" . ":\n" . $SmsVal->"message"); + :set Messages ($Messages . "\n\n" . [ $SymbolForNotification "incoming-envelope" ] . \ + "On " . $SmsVal->"timestamp" . " type " . $SmsVal->"type" . ":\n" . $SmsVal->"message"); :foreach Hook in=$SmsForwardHooks do={ :if ($Phone~($Hook->"allowed-number") && ($SmsVal->"message")~($Hook->"match")) do={ :if ([ $ValidateSyntax ($Hook->"command") ] = true) do={ diff --git a/telegram-chat.rsc b/telegram-chat.rsc index 277c40f..54872fb 100644 --- a/telegram-chat.rsc +++ b/telegram-chat.rsc @@ -30,6 +30,7 @@ :global CertificateAvailable; :global EitherOr; :global EscapeForRegEx; + :global FileExists; :global GetRandom20CharAlNum; :global IfThenElse; :global LogPrint; @@ -60,7 +61,7 @@ :set TelegramRandomDelay 0; } - :if ([ $CertificateAvailable "Go Daddy Root Certificate Authority - G2" ] = false) do={ + :if ([ $CertificateAvailable "Go Daddy Root Certificate Authority - G2" "fetch" ] = false) do={ $LogPrint warning $ScriptName ("Downloading required certificate failed."); :set ExitOK true; :error false; @@ -100,7 +101,7 @@ $LogPrintVerbose debug $ScriptName ("Update " . $UpdateID . ": " . [ :serialize to=json $Update ]); :local Message ($Update->"message"); - :local IsReply ([ :typeof ($Message->"reply_to_message") ] = "string"); + :local IsAnyReply ([ :typeof ($Message->"reply_to_message") ] = "array"); :local IsMyReply ($TelegramMessageIDs->[ :tostr ($Message->"reply_to_message"->"message_id") ]); :if (($IsMyReply = 1 || $TelegramChatOffset->0 > 0 || $Uptime > 5m) && $UpdateID >= $TelegramChatOffset->2) do={ :local Trusted false; @@ -138,7 +139,7 @@ " from update " . $UpdateID . "!"); :set Done true; } - :if ($Done = false && ($IsMyReply = 1 || ($IsReply = false && \ + :if ($Done = false && ($IsMyReply = 1 || ($IsAnyReply = false && \ $TelegramChatActive = true)) && [ :len $Command ] > 0) do={ :if ([ $ValidateSyntax $Command ] = true) do={ :local State ""; @@ -154,7 +155,7 @@ :if ([ $WaitForFile ($File . ".done") [ $EitherOr $TelegramChatRunTime 20s ] ] = false) do={ :set State ([ $SymbolForNotification "warning-sign" ] . "The command did not finish, still running in background.\n\n"); } - :if ([ :len [ /file/find where name=($File . ".failed") ] ] > 0) do={ + :if ([ $FileExists ($File . ".failed") ] = true) do={ :set State ([ $SymbolForNotification "cross-mark" ] . "The command failed with an error!\n\n"); } :local Content ([ /file/read chunk-size=32768 file=$File as-value ]->"data"); diff --git a/update-tunnelbroker.rsc b/update-tunnelbroker.rsc index 9057e1e..5372f4c 100644 --- a/update-tunnelbroker.rsc +++ b/update-tunnelbroker.rsc @@ -28,7 +28,7 @@ :error false; } - :if ([ $CertificateAvailable "Starfield Root Certificate Authority - G2" ] = false) do={ + :if ([ $CertificateAvailable "Starfield Root Certificate Authority - G2" "fetch" ] = false) do={ $LogPrint error $ScriptName ("Downloading required certificate failed."); :set ExitOK true; :error false;