diff options
109 files changed, 1699 insertions, 885 deletions
@@ -9,5 +9,8 @@ # html files (as generated from markdown) *.html +# checksums file as used by $ScriptInstallUpdate +checksums.json + # Mac OS X folder settings file .DS_Store diff --git a/BRANCHES.md b/BRANCHES.md index 8a0bdad..dc4f4ac 100644 --- a/BRANCHES.md +++ b/BRANCHES.md @@ -13,7 +13,7 @@ Installing from branches > ⚠️ **Warning**: Living on the edge? Great, read on! > If not: Please use the `main` branch and leave this page! -These scripts are developed in a [git](https://git-scm.com/) repository. +These scripts are developed in a [git ↗️](https://git-scm.com/) repository. Development and experimental branches are used to provide early access for specific changes. You can install scripts from these branches for testing. diff --git a/CERTIFICATES.md b/CERTIFICATES.md index 5432d78..69d6c18 100644 --- a/CERTIFICATES.md +++ b/CERTIFICATES.md @@ -21,7 +21,7 @@ first step of [installation](README.md#the-long-way-in-detail) is importing the certificate. The scripts can install additional certificates when required. This happens -from this repository if available, or from [mkcert.org](https://mkcert.org) +from this repository if available, or from [mkcert.org ↗️](https://mkcert.org) as a fallback. Get the certificate's CommonName @@ -29,7 +29,7 @@ Get the certificate's CommonName But how to determine what certificate may be required? Often easiest way is to use a desktop browser to get that information. This demonstration uses -[Mozilla Firefox](https://www.mozilla.org/firefox/). +[Mozilla Firefox ↗️](https://www.mozilla.org/firefox/). Let's assume we want to make sure the certificate for [git.eworm.de](https://git.eworm.de/) is available. Open that page in the @@ -74,6 +74,7 @@ See also * [Download, import and update firewall address-lists](doc/fw-addr-lists.md) * [Manage DNS and DoH servers from netwatch](doc/netwatch-dns.md) +* [Send notifications via Gotify](doc/mod/notification-gotify.md) * [Send notifications via Matrix](doc/mod/notification-matrix.md) * [Send notifications via Ntfy](doc/mod/notification-ntfy.md) diff --git a/CONTRIBUTIONS.md b/CONTRIBUTIONS.md index 0b35c40..00861c1 100644 --- a/CONTRIBUTIONS.md +++ b/CONTRIBUTIONS.md @@ -21,6 +21,8 @@ for details! * [Ben Harris](mailto:mail@bharr.is) (@bharrisau) * [Daniel Ziegenberg](mailto:daniel@ziegenberg.at) (@ziegenberg) * [Ignacio Serrano](mailto:ignic@ignic.com) (@ignic) +* [Ilya Kulakov](mailto:kulakov.ilya@gmail.com) (@Kentzo) +* [Leonardo David Monteiro](mailto:leo@cub3.xyz) (@leosfsm) * [Michael Gisbers](mailto:michael@gisbers.de) (@mgisbers) * [Miquel Bonastre](mailto:mbonastre@yahoo.com) (@mbonastre) * @netravnen @@ -30,9 +32,10 @@ for details! ## Donations Add yourself to the list, -[donate with PayPal](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=A4ZXBD6YS2W8J)! +[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) @@ -42,7 +42,21 @@ Other actions (`disk`, `email`, `remote` or `support`) can be used as well. I do not recommend using `echo` - use [debug output](#debug-output) instead. -Disable or remote that setting to restore regular logging. +Disable or remove that setting to restore regular logging. + +## Verbose output + +Specific scripts can generate huge amount of output. These do use a function +`$LogPrintVerbose`, which is declared, but has no code, intentionally. + +If you *really* want that output set the function to be the same as +`$LogPrint`: + + :set LogPrintVerbose $LogPrint; + +To revert that change just run: + + :set LogPrintVerbose; --- [⬅️ Go back to main README](README.md) diff --git a/INITIAL-COMMANDS.md b/INITIAL-COMMANDS.md index 424ef32..40f609b 100644 --- a/INITIAL-COMMANDS.md +++ b/INITIAL-COMMANDS.md @@ -10,30 +10,46 @@ Initial commands [⬅️ Go back to main README](README.md) -> ⚠️ **Warning**: These command are inteneded for initial setup. If you are +> ⚠️ **Warning**: These commands are intended for initial setup. If you are > not aware of the procedure please follow > [the long way in detail](README.md#the-long-way-in-detail). Run the complete base installation: { - /tool/fetch "https://git.eworm.de/cgit/routeros-scripts/plain/certs/ISRG-Root-X2.pem" dst-path="isrg-root-x2.pem" as-value; - :delay 1s; - /certificate/import file-name=isrg-root-x2.pem passphrase=""; - :if ([ :len [ /certificate/find where fingerprint="69729b8e15a86efc177a57afb7171dfc64add28c2fca8cf1507e34453ccb1470" ] ] != 1) do={ - :error "Something is wrong with your certificates!"; + :local BaseUrl "https://git.eworm.de/cgit/routeros-scripts/plain/"; + :local CertCommonName "ISRG Root X2"; + :local CertFileName "ISRG-Root-X2.pem"; + :local CertFingerprint "69729b8e15a86efc177a57afb7171dfc64add28c2fca8cf1507e34453ccb1470"; + + :if (!(([ /certificate/settings/get ]->"builtin-trust-anchors") = "trusted" && \ + [[ :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 ("https://git.eworm.de/cgit/routeros-scripts/plain/" . $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; }"; - :global CertificateNameByCN; - $CertificateNameByCN "ISRG Root X2"; + :if ([ :len [ /certificate/find where fingerprint=$CertFingerprint ] ] > 0) do={ + :put "Renaming certificate by its common-name..."; + :global CertificateNameByCN; + $CertificateNameByCN $CertFingerprint; + }; }; Then continue setup with @@ -9,7 +9,7 @@ WIFI = $(wildcard *.wifi.rsc) MARKDOWN = $(wildcard *.md doc/*.md doc/mod/*.md) HTML = $(MARKDOWN:.md=.html) -all: $(CAPSMAN) $(LOCAL) $(WIFI) $(HTML) +all: $(CAPSMAN) $(LOCAL) $(WIFI) $(HTML) checksums.json %.html: %.md Makefile markdown $< | sed 's/href="\([-_\./[:alnum:]]*\)\.md"/href="\1.html"/g' > $@ @@ -32,5 +32,8 @@ all: $(CAPSMAN) $(LOCAL) $(WIFI) $(HTML) -e '/^# !!/,/^# !!/c # !! Do not edit this file, it is generated from template!' \ < $< > $@ +checksums.json: contrib/checksums.sh *.rsc */*.rsc + contrib/checksums.sh + clean: - rm -f $(HTML) + rm -f $(HTML) checksums.json @@ -10,13 +10,14 @@ RouterOS Scripts  -[RouterOS](https://mikrotik.com/software) is the operating system developed -by [MikroTik](https://mikrotik.com/aboutus) for networking tasks. This -repository holds a number of [scripts](https://wiki.mikrotik.com/wiki/Manual:Scripting) +[RouterOS ↗️](https://mikrotik.com/software) is the operating system developed +by [MikroTik ↗️](https://mikrotik.com/aboutus) for networking tasks. This +repository holds a number of [scripts ↗️](https://wiki.mikrotik.com/wiki/Manual:Scripting) to manage RouterOS devices or extend their functionality. *Use at your own risk*, pay attention to -[license and warranty](#license-and-warranty)! +[license and warranty](#license-and-warranty), and +[disclaimer on external links](#disclaimer-on-external-links)! Requirements ------------ @@ -35,7 +36,7 @@ Specific scripts may require even newer RouterOS version. > running RouterOS v6 switch to `routeros-v6` branch! Starting with RouterOS 7.17 the -[device-mode](https://help.mikrotik.com/docs/spaces/ROS/pages/93749258/Device-mode) +[device-mode ↗️](https://help.mikrotik.com/docs/spaces/ROS/pages/93749258/Device-mode) has been extended to give more fine-grained control over what features are available. You need to enable `scheduler` and `fetch` at least, specific scripts may require additional features. @@ -61,9 +62,9 @@ First time users should take the long way 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) +RouterOS script distribution ↗️](https://www.youtube.com/watch?v=B9neG3oAhcY) including demonstation recorded live at [MUM Europe -2019](https://mum.mikrotik.com/2019/EU/) in Vienna. +2019 ↗️](https://mum.mikrotik.com/2019/EU/) in Vienna. > ⚠️ **Warning**: Some details changed. So see the presentation, then follow > the steps below for up-to-date commands. @@ -71,7 +72,15 @@ 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;` + +If you intend to download the scripts from a different location (for example from github.com) install the corresponding certificate chain. @@ -83,11 +92,11 @@ Note that the commands above do *not* verify server certificate, so if you want to be safe download with your workstations's browser and transfer the file to your MikroTik device. -* [ISRG Root X2](https://letsencrypt.org/certs/isrg-root-x2.pem) +* [ISRG Root X2 ↗️](https://letsencrypt.org/certs/isrg-root-x2.pem) Then we import the certificate. - /certificate/import file-name=isrg-root-x2.pem passphrase=""; + /certificate/import file-name="isrg-root-x2.pem" passphrase=""; Do not worry that the command is not shown - that happens because it contains a sensitive property, the passphrase. @@ -105,6 +114,8 @@ 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! @@ -122,6 +133,9 @@ And finally load configuration and functions and add the scheduler.  +> 💡️ **Hint**: You see complaints regarding syntax errors? Most likely the +> RouterOS on your device is too old. Check for updates! + ### Scheduled automatic updates The last step is optional: Add this scheduler **only** if you want the @@ -191,7 +205,7 @@ Scheduler and events -------------------- Most scripts are designed to run regularly from -[scheduler](https://wiki.mikrotik.com/wiki/Manual:System/Scheduler). We just +[scheduler ↗️](https://wiki.mikrotik.com/wiki/Manual:System/Scheduler). We just added `check-routeros-update`, so let's run it daily to make sure not to miss an update. @@ -214,60 +228,62 @@ There's much more to explore... Have fun! Available scripts ----------------- -* [Find and remove access list duplicates](doc/accesslist-duplicates.md) -* [Upload backup to Mikrotik cloud](doc/backup-cloud.md) -* [Send backup via e-mail](doc/backup-email.md) -* [Save configuration to fallback partition](doc/backup-partition.md) -* [Upload backup to server](doc/backup-upload.md) -* [Download packages for CAP upgrade from CAPsMAN](doc/capsman-download-packages.md) -* [Run rolling CAP upgrades from CAPsMAN](doc/capsman-rolling-upgrade.md) -* [Renew locally issued certificates](doc/certificate-renew-issued.md) -* [Renew certificates and notify on expiration](doc/check-certificates.md) -* [Notify about health state](doc/check-health.md) -* [Notify on LTE firmware upgrade](doc/check-lte-firmware-upgrade.md) -* [Notify on RouterOS update](doc/check-routeros-update.md) -* [Collect MAC addresses in wireless access list](doc/collect-wireless-mac.md) -* [Use wireless network with daily psk](doc/daily-psk.md) -* [Comment DHCP leases with info from access list](doc/dhcp-lease-comment.md) -* [Create DNS records for DHCP leases](doc/dhcp-to-dns.md) -* [Automatically upgrade firmware and reboot](doc/firmware-upgrade-reboot.md) -* [Download, import and update firewall address-lists](doc/fw-addr-lists.md) -* [Wait for global functions und modules](doc/global-wait.md) -* [Send GPS position to server](doc/gps-track.md) -* [Use WPA network with hotspot credentials](doc/hotspot-to-wpa.md) -* [Create DNS records for IPSec peers](doc/ipsec-to-dns.md) -* [Update configuration on IPv6 prefix change](doc/ipv6-update.md) -* [Manage IP addresses with bridge status](doc/ip-addr-bridge.md) -* [Run other scripts on DHCP lease](doc/lease-script.md) -* [Manage LEDs dark mode](doc/leds-mode.md) -* [Forward log messages via notification](doc/log-forward.md) -* [Mode button with multiple presses](doc/mode-button.md) -* [Manage DNS and DoH servers from netwatch](doc/netwatch-dns.md) -* [Notify on host up and down](doc/netwatch-notify.md) -* [Visualize OSPF state via LEDs](doc/ospf-to-leds.md) -* [Manage system update](doc/packages-update.md) -* [Run scripts on ppp connection](doc/ppp-on-up.md) -* [Act on received SMS](doc/sms-action.md) -* [Forward received SMS](doc/sms-forward.md) -* [Play Super Mario theme](doc/super-mario-theme.md) -* [Chat with your router and send commands via Telegram bot](doc/telegram-chat.md) -* [Install LTE firmware upgrade](doc/unattended-lte-firmware-upgrade.md) -* [Update GRE configuration with dynamic addresses](doc/update-gre-address.md) -* [Update tunnelbroker configuration](doc/update-tunnelbroker.md) +* [Find and remove access list duplicates](doc/accesslist-duplicates.md) (`accesslist-duplicates`) +* [Upload backup to Mikrotik cloud](doc/backup-cloud.md) (`backup-cloud`) +* [Send backup via e-mail](doc/backup-email.md) (`backup-email`) +* [Save configuration to fallback partition](doc/backup-partition.md) (`backup-partition`) +* [Upload backup to server](doc/backup-upload.md) (`backup-upload`) +* [Download packages for CAP upgrade from CAPsMAN](doc/capsman-download-packages.md) (`capsman-download-packages`) +* [Run rolling CAP upgrades from CAPsMAN](doc/capsman-rolling-upgrade.md) (`capsman-rolling-upgrade`) +* [Renew locally issued certificates](doc/certificate-renew-issued.md) (`certificate-renew-issued`) +* [Renew certificates and notify on expiration](doc/check-certificates.md) (`check-certificates`) +* [Notify about health state](doc/check-health.md) (`check-health`) +* [Notify on LTE firmware upgrade](doc/check-lte-firmware-upgrade.md) (`check-lte-firmware-upgrade`) +* [Check perpetual license on CHR](doc/check-perpetual-license.md) (`check-perpetual-license`) +* [Notify on RouterOS update](doc/check-routeros-update.md) (`check-routeros-update`) +* [Collect MAC addresses in wireless access list](doc/collect-wireless-mac.md) (`collect-wireless-mac`) +* [Use wireless network with daily psk](doc/daily-psk.md) (`daily-psk`) +* [Comment DHCP leases with info from access list](doc/dhcp-lease-comment.md) (`dhcp-lease-comment`) +* [Create DNS records for DHCP leases](doc/dhcp-to-dns.md) (`dhcp-to-dns`) +* [Automatically upgrade firmware and reboot](doc/firmware-upgrade-reboot.md) (`firmware-upgrade-reboot`) +* [Download, import and update firewall address-lists](doc/fw-addr-lists.md) (`fw-addr-lists`) +* [Wait for global functions und modules](doc/global-wait.md) (`global-wait`) +* [Send GPS position to server](doc/gps-track.md) (`gps-track`) +* [Use WPA network with hotspot credentials](doc/hotspot-to-wpa.md) (`hotspot-to-wpa` & `hotspot-to-wpa-cleanup`) +* [Create DNS records for IPSec peers](doc/ipsec-to-dns.md) (`ipsec-to-dns`) +* [Update configuration on IPv6 prefix change](doc/ipv6-update.md) (`ipv6-update`) +* [Manage IP addresses with bridge status](doc/ip-addr-bridge.md) (`ip-addr-bridge`) +* [Run other scripts on DHCP lease](doc/lease-script.md) (`lease-script`) +* [Manage LEDs dark mode](doc/leds-mode.md) (`leds-day-mode`, `leds-night-mode` & `leds-toggle-mode`) +* [Forward log messages via notification](doc/log-forward.md) (`log-forward`) +* [Mode button with multiple presses](doc/mode-button.md) (`mode-button`) +* [Manage DNS and DoH servers from netwatch](doc/netwatch-dns.md) (`netwatch-dns`) +* [Notify on host up and down](doc/netwatch-notify.md) (`netwatch-notify`) +* [Visualize OSPF state via LEDs](doc/ospf-to-leds.md) (`ospf-to-leds`) +* [Manage system update](doc/packages-update.md) (`packages-update`) +* [Run scripts on ppp connection](doc/ppp-on-up.md) (`ppp-on-up`) +* [Act on received SMS](doc/sms-action.md) (`sms-action`) +* [Forward received SMS](doc/sms-forward.md) (`sms-forward`) +* [Play Super Mario theme](doc/super-mario-theme.md) (`super-mario-theme`) +* [Chat with your router and send commands via Telegram bot](doc/telegram-chat.md) (`telegram-chat`) +* [Install LTE firmware upgrade](doc/unattended-lte-firmware-upgrade.md) (`unattended-lte-firmware-upgrade`) +* [Update GRE configuration with dynamic addresses](doc/update-gre-address.md) (`update-gre-address`) +* [Update tunnelbroker configuration](doc/update-tunnelbroker.md) (`update-tunnelbroker`) Available modules ----------------- -* [Manage ports in bridge](doc/mod/bridge-port-to.md) -* [Manage VLANs on bridge ports](doc/mod/bridge-port-vlan.md) -* [Inspect variables](doc/mod/inspectvar.md) -* [IP address calculation](doc/mod/ipcalc.md) -* [Send notifications via e-mail](doc/mod/notification-email.md) -* [Send notifications via Matrix](doc/mod/notification-matrix.md) -* [Send notifications via Ntfy](doc/mod/notification-ntfy.md) -* [Send notifications via Telegram](doc/mod/notification-telegram.md) -* [Download script and run it once](doc/mod/scriptrunonce.md) -* [Import ssh keys for public key authentication](doc/mod/ssh-keys-import.md) +* [Manage ports in bridge](doc/mod/bridge-port-to.md) (`mod/bridge-port-to`) +* [Manage VLANs on bridge ports](doc/mod/bridge-port-vlan.md) (`mod/bridge-port-vlan`) +* [Inspect variables](doc/mod/inspectvar.md) (`mod/inspectvar`) +* [IP address calculation](doc/mod/ipcalc.md) (`mod/ipcalc`) +* [Send notifications via e-mail](doc/mod/notification-email.md) (`mod/notification-email`) +* [Send notifications via Gotify](doc/mod/notification-gotify.md) (`mod/notification-gotify`) +* [Send notifications via Matrix](doc/mod/notification-matrix.md) (`mod/notification-matrix`) +* [Send notifications via Ntfy](doc/mod/notification-ntfy.md) (`mod/notification-ntfy`) +* [Send notifications via Telegram](doc/mod/notification-telegram.md) (`mod/notification-telegram`) +* [Download script and run it once](doc/mod/scriptrunonce.md) (`mod/scriptrunonce`) +* [Import ssh keys for public key authentication](doc/mod/ssh-keys-import.md) (`mod/ssh-keys-import`) Installing custom scripts & modules ----------------------------------- @@ -324,7 +340,7 @@ Possibly a scheduler and other configuration has to be removed as well. Contact ------- -We have a Telegram Group [RouterOS-Scripts](https://t.me/routeros_scripts)! +We have a Telegram Group [RouterOS-Scripts ↗️](https://t.me/routeros_scripts)! [](https://t.me/routeros_scripts) @@ -348,7 +364,7 @@ at github. This project is developed in private spare time and usage is free of charge for you. If you like the scripts and think this is of value for you or your business please consider to -[donate with PayPal](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=A4ZXBD6YS2W8J). +[donate with PayPal ↗️](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=A4ZXBD6YS2W8J). [](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=A4ZXBD6YS2W8J) @@ -367,6 +383,21 @@ but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the [GNU General Public License](COPYING.md) for more details. +Disclaimer on external links +---------------------------- + +Our website contains links to the websites of third parties ("external +links"). As the content of these websites is not under our control, we +cannot assume any liability for such external content. In all cases, the +provider of information of the linked websites is liable for the content +and accuracy of the information provided. At the point in time when the +links were placed, no infringements of the law were recognisable to us. +As soon as an infringement of the law becomes known to us, we will +immediately remove the link in question. + +> 💡️ **Hint**: All external links are marked with an arrow pointing +> diagonally in an up-right (or north-east) direction (↗️). + Upstream -------- diff --git a/accesslist-duplicates.capsman.rsc b/accesslist-duplicates.capsman.rsc index 27546c8..5e6cf0a 100644 --- a/accesslist-duplicates.capsman.rsc +++ b/accesslist-duplicates.capsman.rsc @@ -10,11 +10,11 @@ # # !! Do not edit this file, it is generated from template! -:global GlobalFunctionsReady; -:while ($GlobalFunctionsReady != true) do={ :delay 500ms; } - :local ExitOK false; -:do { +:onerror Err { + :global GlobalConfigReady; :global GlobalFunctionsReady; + :retry { :if ($GlobalConfigReady != true || $GlobalFunctionsReady != true) \ + do={ :error ("Global config and/or functions not ready."); }; } delay=500ms max=50; :local ScriptName [ :jobname ]; :local Seen ({}); @@ -32,6 +32,6 @@ } :set ($Seen->$Mac) 1; } -} on-error={ - :global ExitError; $ExitError $ExitOK [ :jobname ]; +} do={ + :global ExitError; $ExitError $ExitOK [ :jobname ] $Err; } diff --git a/accesslist-duplicates.local.rsc b/accesslist-duplicates.local.rsc index 589815d..a6b4f41 100644 --- a/accesslist-duplicates.local.rsc +++ b/accesslist-duplicates.local.rsc @@ -10,11 +10,11 @@ # # !! Do not edit this file, it is generated from template! -:global GlobalFunctionsReady; -:while ($GlobalFunctionsReady != true) do={ :delay 500ms; } - :local ExitOK false; -:do { +:onerror Err { + :global GlobalConfigReady; :global GlobalFunctionsReady; + :retry { :if ($GlobalConfigReady != true || $GlobalFunctionsReady != true) \ + do={ :error ("Global config and/or functions not ready."); }; } delay=500ms max=50; :local ScriptName [ :jobname ]; :local Seen ({}); @@ -32,6 +32,6 @@ } :set ($Seen->$Mac) 1; } -} on-error={ - :global ExitError; $ExitError $ExitOK [ :jobname ]; +} do={ + :global ExitError; $ExitError $ExitOK [ :jobname ] $Err; } diff --git a/accesslist-duplicates.template.rsc b/accesslist-duplicates.template.rsc index ccbca3d..e51198d 100644 --- a/accesslist-duplicates.template.rsc +++ b/accesslist-duplicates.template.rsc @@ -11,11 +11,11 @@ # !! This is just a template to generate the real script! # !! Pattern '%TEMPL%' is replaced, paths are filtered. -:global GlobalFunctionsReady; -:while ($GlobalFunctionsReady != true) do={ :delay 500ms; } - :local ExitOK false; -:do { +:onerror Err { + :global GlobalConfigReady; :global GlobalFunctionsReady; + :retry { :if ($GlobalConfigReady != true || $GlobalFunctionsReady != true) \ + do={ :error ("Global config and/or functions not ready."); }; } delay=500ms max=50; :local ScriptName [ :jobname ]; :local Seen ({}); @@ -41,6 +41,6 @@ } :set ($Seen->$Mac) 1; } -} on-error={ - :global ExitError; $ExitError $ExitOK [ :jobname ]; +} do={ + :global ExitError; $ExitError $ExitOK [ :jobname ] $Err; } diff --git a/accesslist-duplicates.wifi.rsc b/accesslist-duplicates.wifi.rsc index 527ebb4..cadacb6 100644 --- a/accesslist-duplicates.wifi.rsc +++ b/accesslist-duplicates.wifi.rsc @@ -10,11 +10,11 @@ # # !! Do not edit this file, it is generated from template! -:global GlobalFunctionsReady; -:while ($GlobalFunctionsReady != true) do={ :delay 500ms; } - :local ExitOK false; -:do { +:onerror Err { + :global GlobalConfigReady; :global GlobalFunctionsReady; + :retry { :if ($GlobalConfigReady != true || $GlobalFunctionsReady != true) \ + do={ :error ("Global config and/or functions not ready."); }; } delay=500ms max=50; :local ScriptName [ :jobname ]; :local Seen ({}); @@ -32,6 +32,6 @@ } :set ($Seen->$Mac) 1; } -} on-error={ - :global ExitError; $ExitError $ExitOK [ :jobname ]; +} do={ + :global ExitError; $ExitError $ExitOK [ :jobname ] $Err; } diff --git a/backup-cloud.rsc b/backup-cloud.rsc index c4e23b2..e41db27 100644 --- a/backup-cloud.rsc +++ b/backup-cloud.rsc @@ -9,11 +9,11 @@ # upload backup to MikroTik cloud # https://rsc.eworm.de/doc/backup-cloud.md -:global GlobalFunctionsReady; -:while ($GlobalFunctionsReady != true) do={ :delay 500ms; } - :local ExitOK false; -:do { +:onerror Err { + :global GlobalConfigReady; :global GlobalFunctionsReady; + :retry { :if ($GlobalConfigReady != true || $GlobalFunctionsReady != true) \ + do={ :error ("Global config and/or functions not ready."); }; } delay=500ms max=50; :local ScriptName [ :jobname ]; :global BackupRandomDelay; @@ -99,6 +99,6 @@ :set PackagesUpdateBackupFailure true; } $RmDir "tmpfs/backup-cloud"; -} on-error={ - :global ExitError; $ExitError $ExitOK [ :jobname ]; +} do={ + :global ExitError; $ExitError $ExitOK [ :jobname ] $Err; } diff --git a/backup-email.rsc b/backup-email.rsc index d097301..8015bea 100644 --- a/backup-email.rsc +++ b/backup-email.rsc @@ -9,11 +9,11 @@ # create and email backup and config file # https://rsc.eworm.de/doc/backup-email.md -:global GlobalFunctionsReady; -:while ($GlobalFunctionsReady != true) do={ :delay 500ms; } - :local ExitOK false; -:do { +:onerror Err { + :global GlobalConfigReady; :global GlobalFunctionsReady; + :retry { :if ($GlobalConfigReady != true || $GlobalFunctionsReady != true) \ + do={ :error ("Global config and/or functions not ready."); }; } delay=500ms max=50; :local ScriptName [ :jobname ]; :global BackupPassword; @@ -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; } -} on-error={ - :global ExitError; $ExitError $ExitOK [ :jobname ]; + # do not remove the files here, as the mail is still queued! +} do={ + :global ExitError; $ExitError $ExitOK [ :jobname ] $Err; } diff --git a/backup-partition.rsc b/backup-partition.rsc index 1f0cf2e..ae7ad03 100644 --- a/backup-partition.rsc +++ b/backup-partition.rsc @@ -10,11 +10,11 @@ # save configuration to fallback partition # https://rsc.eworm.de/doc/backup-partition.md -:global GlobalFunctionsReady; -:while ($GlobalFunctionsReady != true) do={ :delay 500ms; } - :local ExitOK false; -:do { +:onerror Err { + :global GlobalConfigReady; :global GlobalFunctionsReady; + :retry { :if ($GlobalConfigReady != true || $GlobalFunctionsReady != true) \ + do={ :error ("Global config and/or functions not ready."); }; } delay=500ms max=50; :local ScriptName [ :jobname ]; :global BackupPartitionCopyBeforeFeatureUpdate; @@ -32,14 +32,15 @@ :global LogPrint; - :do { + :onerror Err { /partitions/copy-to $FallbackTo; $LogPrint info $ScriptName ("Copied RouterOS to partition '" . $FallbackToName . "'."); - :return true; - } on-error={ - $LogPrint error $ScriptName ("Failed copying RouterOS to partition '" . $FallbackToName . "'!"); + } do={ + $LogPrint error $ScriptName ("Failed copying RouterOS to partition '" . \ + $FallbackToName . "': " . $Err); :return false; } + :return true; } :if ([ $ScriptLock $ScriptName ] = false) do={ @@ -107,20 +108,21 @@ } } - :do { + :onerror Err { /system/scheduler/add start-time=startup name="running-from-backup-partition" \ on-event=(":log warning (\"Running from partition '\" . " . \ "[ /partitions/get [ find where running ] name ] . \"'!\")"); /partitions/save-config-to $FallbackTo; /system/scheduler/remove "running-from-backup-partition"; $LogPrint info $ScriptName ("Saved configuration to partition '" . $FallbackToName . "'."); - } on-error={ + } do={ /system/scheduler/remove [ find where name="running-from-backup-partition" ]; - $LogPrint error $ScriptName ("Failed saving configuration to partition '" . $FallbackToName . "'!"); + $LogPrint error $ScriptName ("Failed saving configuration to partition '" . \ + $FallbackToName . "': " . $Err); :set PackagesUpdateBackupFailure true; :set ExitOK true; :error false; } -} on-error={ - :global ExitError; $ExitError $ExitOK [ :jobname ]; +} do={ + :global ExitError; $ExitError $ExitOK [ :jobname ] $Err; } diff --git a/backup-upload.rsc b/backup-upload.rsc index 14c3914..e6b9f92 100644 --- a/backup-upload.rsc +++ b/backup-upload.rsc @@ -10,11 +10,11 @@ # create and upload backup and config file # https://rsc.eworm.de/doc/backup-upload.md -:global GlobalFunctionsReady; -:while ($GlobalFunctionsReady != true) do={ :delay 500ms; } - :local ExitOK false; -:do { +:onerror Err { + :global GlobalConfigReady; :global GlobalFunctionsReady; + :retry { :if ($GlobalConfigReady != true || $GlobalFunctionsReady != true) \ + do={ :error ("Global config and/or functions not ready."); }; } delay=500ms max=50; :local ScriptName [ :jobname ]; :global BackupPassword; @@ -90,13 +90,13 @@ /system/backup/save encryption=aes-sha256 name=$FilePath password=$BackupPassword; $WaitForFile ($FilePath . ".backup"); - :do { + :onerror Err { /tool/fetch upload=yes url=($BackupUploadUrl . "/" . $FileName . ".backup") \ user=$BackupUploadUser password=$BackupUploadPass src-path=($FilePath . ".backup"); :set BackupFile [ /file/get ($FilePath . ".backup") ]; :set ($BackupFile->"name") ($FileName . ".backup"); - } on-error={ - $LogPrint error $ScriptName ("Uploading backup file failed!"); + } do={ + $LogPrint error $ScriptName ("Uploading backup file failed: " . $Err); :set BackupFile "failed"; :set Failed 1; } @@ -109,13 +109,13 @@ /export terse show-sensitive file=$FilePath; $WaitForFile ($FilePath . ".rsc"); - :do { + :onerror Err { /tool/fetch upload=yes url=($BackupUploadUrl . "/" . $FileName . ".rsc") \ user=$BackupUploadUser password=$BackupUploadPass src-path=($FilePath . ".rsc"); :set ExportFile [ /file/get ($FilePath . ".rsc") ]; :set ($ExportFile->"name") ($FileName . ".rsc"); - } on-error={ - $LogPrint error $ScriptName ("Uploading configuration export failed!"); + } do={ + $LogPrint error $ScriptName ("Uploading configuration export failed: " . $Err); :set ExportFile "failed"; :set Failed 1; } @@ -130,13 +130,13 @@ file=($FilePath . ".conf\00"); $WaitForFile ($FilePath . ".conf"); - :do { + :onerror Err { /tool/fetch upload=yes url=($BackupUploadUrl . "/" . $FileName . ".conf") \ user=$BackupUploadUser password=$BackupUploadPass src-path=($FilePath . ".conf"); :set ConfigFile [ /file/get ($FilePath . ".conf") ]; :set ($ConfigFile->"name") ($FileName . ".conf"); - } on-error={ - $LogPrint error $ScriptName ("Uploading global-config-overlay failed!"); + } do={ + $LogPrint error $ScriptName ("Uploading global-config-overlay failed: " . $Err); :set ConfigFile "failed"; :set Failed 1; } @@ -173,6 +173,6 @@ :set PackagesUpdateBackupFailure true; } $RmDir $DirName; -} on-error={ - :global ExitError; $ExitError $ExitOK [ :jobname ]; +} do={ + :global ExitError; $ExitError $ExitOK [ :jobname ] $Err; } diff --git a/capsman-download-packages.capsman.rsc b/capsman-download-packages.capsman.rsc index 4387cb1..2ea1667 100644 --- a/capsman-download-packages.capsman.rsc +++ b/capsman-download-packages.capsman.rsc @@ -11,15 +11,16 @@ # # !! Do not edit this file, it is generated from template! -:global GlobalFunctionsReady; -:while ($GlobalFunctionsReady != true) do={ :delay 500ms; } - :local ExitOK false; -:do { +:onerror Err { + :global GlobalConfigReady; :global GlobalFunctionsReady; + :retry { :if ($GlobalConfigReady != true || $GlobalFunctionsReady != true) \ + do={ :error ("Global config and/or functions not ready."); }; } delay=500ms max=50; :local ScriptName [ :jobname ]; :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={ @@ -62,11 +63,11 @@ :if ([ $DownloadPackage ($File->"package-name") $InstalledVersion \ ($File->"package-architecture") $PackagePath ] = true) do={ :set Updated true; - $RmFile $Package; + $RmFile ($File->"name"); } } - :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={ @@ -87,6 +88,6 @@ /caps-man/remote-cap/upgrade [ find where version!=$InstalledVersion ]; } } -} on-error={ - :global ExitError; $ExitError $ExitOK [ :jobname ]; +} do={ + :global ExitError; $ExitError $ExitOK [ :jobname ] $Err; } diff --git a/capsman-download-packages.template.rsc b/capsman-download-packages.template.rsc index 744494e..f95212a 100644 --- a/capsman-download-packages.template.rsc +++ b/capsman-download-packages.template.rsc @@ -12,15 +12,16 @@ # !! This is just a template to generate the real script! # !! Pattern '%TEMPL%' is replaced, paths are filtered. -:global GlobalFunctionsReady; -:while ($GlobalFunctionsReady != true) do={ :delay 500ms; } - :local ExitOK false; -:do { +:onerror Err { + :global GlobalConfigReady; :global GlobalFunctionsReady; + :retry { :if ($GlobalConfigReady != true || $GlobalFunctionsReady != true) \ + do={ :error ("Global config and/or functions not ready."); }; } delay=500ms max=50; :local ScriptName [ :jobname ]; :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={ @@ -64,11 +65,11 @@ :if ([ $DownloadPackage ($File->"package-name") $InstalledVersion \ ($File->"package-architecture") $PackagePath ] = true) do={ :set Updated true; - $RmFile $Package; + $RmFile ($File->"name"); } } - :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={ @@ -98,6 +99,6 @@ /interface/wifi/capsman/remote-cap/upgrade [ find where version!=$InstalledVersion ]; } } -} on-error={ - :global ExitError; $ExitError $ExitOK [ :jobname ]; +} do={ + :global ExitError; $ExitError $ExitOK [ :jobname ] $Err; } diff --git a/capsman-download-packages.wifi.rsc b/capsman-download-packages.wifi.rsc index a0c5e12..03fd9e7 100644 --- a/capsman-download-packages.wifi.rsc +++ b/capsman-download-packages.wifi.rsc @@ -11,15 +11,16 @@ # # !! Do not edit this file, it is generated from template! -:global GlobalFunctionsReady; -:while ($GlobalFunctionsReady != true) do={ :delay 500ms; } - :local ExitOK false; -:do { +:onerror Err { + :global GlobalConfigReady; :global GlobalFunctionsReady; + :retry { :if ($GlobalConfigReady != true || $GlobalFunctionsReady != true) \ + do={ :error ("Global config and/or functions not ready."); }; } delay=500ms max=50; :local ScriptName [ :jobname ]; :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={ @@ -62,11 +63,11 @@ :if ([ $DownloadPackage ($File->"package-name") $InstalledVersion \ ($File->"package-architecture") $PackagePath ] = true) do={ :set Updated true; - $RmFile $Package; + $RmFile ($File->"name"); } } - :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" }; @@ -89,6 +90,6 @@ /interface/wifi/capsman/remote-cap/upgrade [ find where version!=$InstalledVersion ]; } } -} on-error={ - :global ExitError; $ExitError $ExitOK [ :jobname ]; +} do={ + :global ExitError; $ExitError $ExitOK [ :jobname ] $Err; } diff --git a/capsman-rolling-upgrade.capsman.rsc b/capsman-rolling-upgrade.capsman.rsc index 791b3db..0d4114a 100644 --- a/capsman-rolling-upgrade.capsman.rsc +++ b/capsman-rolling-upgrade.capsman.rsc @@ -12,11 +12,11 @@ # # !! Do not edit this file, it is generated from template! -:global GlobalFunctionsReady; -:while ($GlobalFunctionsReady != true) do={ :delay 500ms; } - :local ExitOK false; -:do { +:onerror Err { + :global GlobalConfigReady; :global GlobalFunctionsReady; + :retry { :if ($GlobalConfigReady != true || $GlobalFunctionsReady != true) \ + do={ :error ("Global config and/or functions not ready."); }; } delay=500ms max=50; :local ScriptName [ :jobname ]; :global LogPrint; @@ -45,6 +45,6 @@ :delay ($Delay . "s"); } } -} on-error={ - :global ExitError; $ExitError $ExitOK [ :jobname ]; +} do={ + :global ExitError; $ExitError $ExitOK [ :jobname ] $Err; } diff --git a/capsman-rolling-upgrade.template.rsc b/capsman-rolling-upgrade.template.rsc index 0b1cc2b..690d73d 100644 --- a/capsman-rolling-upgrade.template.rsc +++ b/capsman-rolling-upgrade.template.rsc @@ -13,11 +13,11 @@ # !! This is just a template to generate the real script! # !! Pattern '%TEMPL%' is replaced, paths are filtered. -:global GlobalFunctionsReady; -:while ($GlobalFunctionsReady != true) do={ :delay 500ms; } - :local ExitOK false; -:do { +:onerror Err { + :global GlobalConfigReady; :global GlobalFunctionsReady; + :retry { :if ($GlobalConfigReady != true || $GlobalFunctionsReady != true) \ + do={ :error ("Global config and/or functions not ready."); }; } delay=500ms max=50; :local ScriptName [ :jobname ]; :global LogPrint; @@ -53,6 +53,6 @@ :delay ($Delay . "s"); } } -} on-error={ - :global ExitError; $ExitError $ExitOK [ :jobname ]; +} do={ + :global ExitError; $ExitError $ExitOK [ :jobname ] $Err; } diff --git a/capsman-rolling-upgrade.wifi.rsc b/capsman-rolling-upgrade.wifi.rsc index 4afdee2..8e32ab2 100644 --- a/capsman-rolling-upgrade.wifi.rsc +++ b/capsman-rolling-upgrade.wifi.rsc @@ -12,11 +12,11 @@ # # !! Do not edit this file, it is generated from template! -:global GlobalFunctionsReady; -:while ($GlobalFunctionsReady != true) do={ :delay 500ms; } - :local ExitOK false; -:do { +:onerror Err { + :global GlobalConfigReady; :global GlobalFunctionsReady; + :retry { :if ($GlobalConfigReady != true || $GlobalFunctionsReady != true) \ + do={ :error ("Global config and/or functions not ready."); }; } delay=500ms max=50; :local ScriptName [ :jobname ]; :global LogPrint; @@ -46,6 +46,6 @@ :delay ($Delay . "s"); } } -} on-error={ - :global ExitError; $ExitError $ExitOK [ :jobname ]; +} do={ + :global ExitError; $ExitError $ExitOK [ :jobname ] $Err; } diff --git a/certificate-renew-issued.rsc b/certificate-renew-issued.rsc index 91a48de..14917e4 100644 --- a/certificate-renew-issued.rsc +++ b/certificate-renew-issued.rsc @@ -8,11 +8,11 @@ # renew locally issued certificates # https://rsc.eworm.de/doc/certificate-renew-issued.md -:global GlobalFunctionsReady; -:while ($GlobalFunctionsReady != true) do={ :delay 500ms; } - :local ExitOK false; -:do { +:onerror Err { + :global GlobalConfigReady; :global GlobalFunctionsReady; + :retry { :if ($GlobalConfigReady != true || $GlobalFunctionsReady != true) \ + do={ :error ("Global config and/or functions not ready."); }; } delay=500ms max=50; :local ScriptName [ :jobname ]; :global CertIssuedExportPass; @@ -47,6 +47,6 @@ $LogPrint info $ScriptName ("Issued a new certificate for '" . $CertVal->"common-name" . "'."); } } -} on-error={ - :global ExitError; $ExitError $ExitOK [ :jobname ]; +} do={ + :global ExitError; $ExitError $ExitOK [ :jobname ] $Err; } diff --git a/certs/Makefile b/certs/Makefile index 4e252b4..3ccad6e 100644 --- a/certs/Makefile +++ b/certs/Makefile @@ -15,7 +15,7 @@ DOMAINS_DUAL = \ git.eworm.de/ISRG-Root-X2 \ lists.blocklist.de/Certum-Trusted-Network-CA \ matrix.org/GTS-Root-R4 \ - raw.githubusercontent.com/DigiCert-Global-Root-G2 \ + raw.githubusercontent.com/USERTrust-RSA-Certification-Authority \ rsc.eworm.de/ISRG-Root-X2 \ upgrade.mikrotik.com/ISRG-Root-X1 DOMAINS_IPV4 = \ diff --git a/certs/USERTrust-RSA-Certification-Authority.pem b/certs/USERTrust-RSA-Certification-Authority.pem new file mode 100644 index 0000000..0fbeef6 --- /dev/null +++ b/certs/USERTrust-RSA-Certification-Authority.pem @@ -0,0 +1,41 @@ +# Issuer: CN=USERTrust RSA Certification Authority O=The USERTRUST Network +# Subject: CN=USERTrust RSA Certification Authority O=The USERTRUST Network +# Label: "USERTrust RSA Certification Authority" +# Serial: 2645093764781058787591871645665788717 +# MD5 Fingerprint: 1b:fe:69:d1:91:b7:19:33:a3:72:a8:0f:e1:55:e5:b5 +# SHA1 Fingerprint: 2b:8f:1b:57:33:0d:bb:a2:d0:7a:6c:51:f7:0e:e9:0d:da:b9:ad:8e +# SHA256 Fingerprint: e7:93:c9:b0:2f:d8:aa:13:e2:1c:31:22:8a:cc:b0:81:19:64:3b:74:9c:89:89:64:b1:74:6d:46:c3:d4:cb:d2 +-----BEGIN CERTIFICATE----- +MIIF3jCCA8agAwIBAgIQAf1tMPyjylGoG7xkDjUDLTANBgkqhkiG9w0BAQwFADCB +iDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCk5ldyBKZXJzZXkxFDASBgNVBAcTC0pl +cnNleSBDaXR5MR4wHAYDVQQKExVUaGUgVVNFUlRSVVNUIE5ldHdvcmsxLjAsBgNV +BAMTJVVTRVJUcnVzdCBSU0EgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMTAw +MjAxMDAwMDAwWhcNMzgwMTE4MjM1OTU5WjCBiDELMAkGA1UEBhMCVVMxEzARBgNV +BAgTCk5ldyBKZXJzZXkxFDASBgNVBAcTC0plcnNleSBDaXR5MR4wHAYDVQQKExVU +aGUgVVNFUlRSVVNUIE5ldHdvcmsxLjAsBgNVBAMTJVVTRVJUcnVzdCBSU0EgQ2Vy +dGlmaWNhdGlvbiBBdXRob3JpdHkwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIK +AoICAQCAEmUXNg7D2wiz0KxXDXbtzSfTTK1Qg2HiqiBNCS1kCdzOiZ/MPans9s/B +3PHTsdZ7NygRK0faOca8Ohm0X6a9fZ2jY0K2dvKpOyuR+OJv0OwWIJAJPuLodMkY +tJHUYmTbf6MG8YgYapAiPLz+E/CHFHv25B+O1ORRxhFnRghRy4YUVD+8M/5+bJz/ +Fp0YvVGONaanZshyZ9shZrHUm3gDwFA66Mzw3LyeTP6vBZY1H1dat//O+T23LLb2 +VN3I5xI6Ta5MirdcmrS3ID3KfyI0rn47aGYBROcBTkZTmzNg95S+UzeQc0PzMsNT +79uq/nROacdrjGCT3sTHDN/hMq7MkztReJVni+49Vv4M0GkPGw/zJSZrM233bkf6 +c0Plfg6lZrEpfDKEY1WJxA3Bk1QwGROs0303p+tdOmw1XNtB1xLaqUkL39iAigmT +Yo61Zs8liM2EuLE/pDkP2QKe6xJMlXzzawWpXhaDzLhn4ugTncxbgtNMs+1b/97l +c6wjOy0AvzVVdAlJ2ElYGn+SNuZRkg7zJn0cTRe8yexDJtC/QV9AqURE9JnnV4ee +UB9XVKg+/XRjL7FQZQnmWEIuQxpMtPAlR1n6BB6T1CZGSlCBst6+eLf8ZxXhyVeE +Hg9j1uliutZfVS7qXMYoCAQlObgOK6nyTJccBz8NUvXt7y+CDwIDAQABo0IwQDAd +BgNVHQ4EFgQUU3m/WqorSs9UgOHYm8Cd8rIDZsswDgYDVR0PAQH/BAQDAgEGMA8G +A1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEMBQADggIBAFzUfA3P9wF9QZllDHPF +Up/L+M+ZBn8b2kMVn54CVVeWFPFSPCeHlCjtHzoBN6J2/FNQwISbxmtOuowhT6KO +VWKR82kV2LyI48SqC/3vqOlLVSoGIG1VeCkZ7l8wXEskEVX/JJpuXior7gtNn3/3 +ATiUFJVDBwn7YKnuHKsSjKCaXqeYalltiz8I+8jRRa8YFWSQEg9zKC7F4iRO/Fjs +8PRF/iKz6y+O0tlFYQXBl2+odnKPi4w2r78NBc5xjeambx9spnFixdjQg3IM8WcR +iQycE0xyNN+81XHfqnHd4blsjDwSXWXavVcStkNr/+XeTWYRUc+ZruwXtuhxkYze +Sf7dNXGiFSeUHM9h4ya7b6NnJSFd5t0dCy5oGzuCr+yDZ4XUmFF0sbmZgIn/f3gZ +XHlKYC6SQK5MNyosycdiyA5d9zZbyuAlJQG03RoHnHcAP9Dc1ew91Pq7P8yF1m9/ +qS3fuQL39ZeatTXaw2ewh0qpKJ4jjv9cJ2vhsE/zB+4ALtRZh8tSQZXq9EfX7mRB +VXyNWQKV3WKdwrnuWih0hKWbt5DHDAff9Yk2dDLWKMGwsAvgnEzDHNb842m1R0aB +L6KCq9NjRHDEjf8tM7qtj3u1cIiuPhnPQCjY/MiQu12ZIvVS5ljFH4gxQ+6IHdfG +jjxDah2nGN59PRbxYvnKkKj9 +-----END CERTIFICATE----- diff --git a/check-certificates.rsc b/check-certificates.rsc index 0907395..c10e33b 100644 --- a/check-certificates.rsc +++ b/check-certificates.rsc @@ -9,11 +9,11 @@ # check for certificate validity # https://rsc.eworm.de/doc/check-certificates.md -:global GlobalFunctionsReady; -:while ($GlobalFunctionsReady != true) do={ :delay 500ms; } - :local ExitOK false; -:do { +:onerror Err { + :global GlobalConfigReady; :global GlobalFunctionsReady; + :retry { :if ($GlobalConfigReady != true || $GlobalFunctionsReady != true) \ + do={ :error ("Global config and/or functions not ready."); }; } delay=500ms max=50; :local ScriptName [ :jobname ]; :global CertRenewTime; @@ -48,21 +48,26 @@ :global UrlEncode; :global WaitForFile; - :local Return false; + :foreach Type in={ "p12"; "pem" } do={ + :local CertFileName ([ $UrlEncode $FetchName ] . "." . $Type); + $LogPrint debug $ScriptName ("Trying type '" . $Type . "' for '" . $CertName . \ + "' (file '" . $CertFileName . "')..."); - :foreach Type in={ ".pem"; ".p12" } do={ - :local CertFileName ([ $UrlEncode $FetchName ] . $Type); :do { /tool/fetch check-certificate=yes-without-crl http-header-field=({ [ $FetchUserAgentStr $ScriptName ] }) \ ($CertRenewUrl . $CertFileName) dst-path=$CertFileName as-value; $WaitForFile $CertFileName; :local DecryptionFailed true; - :foreach PassPhrase in=$CertRenewPass do={ - :local Result [ /certificate/import file-name=$CertFileName passphrase=$PassPhrase as-value ]; - :if ($Result->"decryption-failures" = 0) do={ - :set DecryptionFailed false; - } + :foreach I,PassPhrase in=$CertRenewPass do={ + :do { + $LogPrint debug $ScriptName ("Trying " . $I . ". passphrase... "); + :local Result [ /certificate/import file-name=$CertFileName passphrase=$PassPhrase as-value ]; + :if ($Result->"decryption-failures" = 0) do={ + $LogPrint debug $ScriptName ("Success!"); + :set DecryptionFailed false; + } + } on-error={ } } $RmFile $CertFileName; @@ -77,13 +82,13 @@ $CertificateNameByCN [ /certificate/get $CertInChain common-name ]; } - :set Return true; + :return true; } on-error={ $LogPrint debug $ScriptName ("Could not download certificate file '" . $CertFileName . "'."); } } - :return $Return; + :return false; } :local FormatInfo do={ @@ -232,6 +237,6 @@ ", it is invalid after " . ($CertVal->"invalid-after") . "."); } } -} on-error={ - :global ExitError; $ExitError $ExitOK [ :jobname ]; +} do={ + :global ExitError; $ExitError $ExitOK [ :jobname ] $Err; } diff --git a/check-health.rsc b/check-health.rsc index f02a249..4cb9940 100644 --- a/check-health.rsc +++ b/check-health.rsc @@ -8,11 +8,11 @@ # check for RouterOS health state # https://rsc.eworm.de/doc/check-health.md -:global GlobalFunctionsReady; -:while ($GlobalFunctionsReady != true) do={ :delay 500ms; } - :local ExitOK false; -:do { +:onerror Err { + :global GlobalConfigReady; :global GlobalFunctionsReady; + :retry { :if ($GlobalConfigReady != true || $GlobalFunctionsReady != true) \ + do={ :error ("Global config and/or functions not ready."); }; } delay=500ms max=50; :local ScriptName [ :jobname ]; :global CheckHealthCPUUtilization; @@ -89,10 +89,10 @@ :foreach Plugin in=$Plugins do={ :local PluginVal [ /system/script/get $Plugin ]; :if ([ $ValidateSyntax ($PluginVal->"source") ] = true) do={ - :do { + :onerror Err { /system/script/run $Plugin; - } on-error={ - $LogPrint error $ScriptName ("Plugin '" . $ScriptVal->"name" . "' failed to run."); + } do={ + $LogPrint error $ScriptName ("Plugin '" . $ScriptVal->"name" . "' failed to run: " . $Err); } } else={ $LogPrint error $ScriptName ("Plugin '" . $ScriptVal->"name" . "' failed syntax validation, skipping."); @@ -105,6 +105,6 @@ } :set CheckHealthPlugins; -} on-error={ - :global ExitError; $ExitError $ExitOK [ :jobname ]; +} do={ + :global ExitError; $ExitError $ExitOK [ :jobname ] $Err; } diff --git a/check-lte-firmware-upgrade.rsc b/check-lte-firmware-upgrade.rsc index c5b6cb5..9f4b656 100644 --- a/check-lte-firmware-upgrade.rsc +++ b/check-lte-firmware-upgrade.rsc @@ -8,11 +8,11 @@ # check for LTE firmware upgrade, send notification # https://rsc.eworm.de/doc/check-lte-firmware-upgrade.md -:global GlobalFunctionsReady; -:while ($GlobalFunctionsReady != true) do={ :delay 500ms; } - :local ExitOK false; -:do { +:onerror Err { + :global GlobalConfigReady; :global GlobalFunctionsReady; + :retry { :if ($GlobalConfigReady != true || $GlobalFunctionsReady != true) \ + do={ :error ("Global config and/or functions not ready."); }; } delay=500ms max=50; :local ScriptName [ :jobname ]; :global SentLteFirmwareUpgradeNotification; @@ -45,12 +45,12 @@ :local IntName [ /interface/lte/get $Interface name ]; :local Firmware; :local Info; - :do { + :onerror Err { :set Firmware [ /interface/lte/firmware-upgrade $Interface as-value ]; :set Info [ /interface/lte/monitor $Interface once as-value ]; - } on-error={ + } do={ $LogPrint debug $ScriptName ("Could not get latest LTE firmware version for interface " . \ - $IntName . "."); + $IntName . ": " . $Err); :return false; } @@ -102,6 +102,6 @@ :foreach Interface in=[ /interface/lte/find ] do={ $CheckInterface $ScriptName $Interface; } -} on-error={ - :global ExitError; $ExitError $ExitOK [ :jobname ]; +} do={ + :global ExitError; $ExitError $ExitOK [ :jobname ] $Err; } diff --git a/check-perpetual-license.rsc b/check-perpetual-license.rsc new file mode 100644 index 0000000..c2f0dff --- /dev/null +++ b/check-perpetual-license.rsc @@ -0,0 +1,78 @@ +#!rsc by RouterOS +# RouterOS script: check-perpetual-license +# Copyright (c) 2025 Christian Hesse <mail@eworm.de> +# https://rsc.eworm.de/COPYING.md +# +# requires RouterOS, version=7.15 +# +# check perpetual license on CHR +# https://rsc.eworm.de/doc/check-perpetual-license.md + +:local ExitOK false; +:onerror Err { + :global GlobalConfigReady; :global GlobalFunctionsReady; + :retry { :if ($GlobalConfigReady != true || $GlobalFunctionsReady != true) \ + do={ :error ("Global config and/or functions not ready."); }; } delay=500ms max=50; + :local ScriptName [ :jobname ]; + + :global Identity; + :global SentCertificateNotification; + + :global LogPrint; + :global ScriptLock; + :global SendNotification2; + :global SymbolForNotification; + :global WaitFullyConnected; + + :if ([ $ScriptLock $ScriptName ] = false) do={ + :set ExitOK true; + :error false; + } + + $WaitFullyConnected; + + :local License [ /system/license/get ]; + :if ([ :typeof ($License->"deadline-at") ] != "str") do={ + $LogPrint info $ScriptName ("This device does not have a perpetual license."); + :set ExitOK true; + :error true; + } + + :if ([ :len ($License->"next-renewal-at") ] = 0 && ($License->"limited-upgrades") = true) do={ + $LogPrint warning $ScriptName ("Your license expired on " . ($License->"deadline-at") . "!"); + :if ($SentCertificateNotification != "expired") do={ + $SendNotification2 ({ origin=$ScriptName; \ + subject=([ $SymbolForNotification "warning-sign" ] . "License expired!"); \ + message=("Your license expired on " . ($License->"deadline-at") . \ + ", can no longer update RouterOS on " . $Identity . "...") }); + :set SentCertificateNotification "expired"; + } + :set ExitOK true; + :error true; + } + + :if ([ :totime ($License->"deadline-at") ] - 3w < [ :timestamp ]) do={ + $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!"); \ + message=("Your license failed to renew and is about to expire on " . \ + ($License->"deadline-at") . " on " . $Identity . "...") }); + :set SentCertificateNotification "warning"; + } + :set ExitOK true; + :error true; + } + + :if ([ :typeof $SentCertificateNotification ] = "str" && \ + [ :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"); \ + message=("Your license was successfully renewed on " . $Identity . \ + ". It is now valid until " . ($License->"deadline-at") . ".") }); + :set SentCertificateNotification; + } +} do={ + :global ExitError; $ExitError $ExitOK [ :jobname ] $Err; +} diff --git a/check-routeros-update.rsc b/check-routeros-update.rsc index 361be34..8b80dde 100644 --- a/check-routeros-update.rsc +++ b/check-routeros-update.rsc @@ -9,11 +9,11 @@ # check for RouterOS update, send notification and/or install # https://rsc.eworm.de/doc/check-routeros-update.md -:global GlobalFunctionsReady; -:while ($GlobalFunctionsReady != true) do={ :delay 500ms; } - :local ExitOK false; -:do { +:onerror Err { + :global GlobalConfigReady; :global GlobalFunctionsReady; + :retry { :if ($GlobalConfigReady != true || $GlobalFunctionsReady != true) \ + do={ :error ("Global config and/or functions not ready."); }; } delay=500ms max=50; :local ScriptName [ :jobname ]; :global Identity; @@ -28,6 +28,7 @@ :global EscapeForRegEx; :global FetchUserAgentStr; :global LogPrint; + :global RebootForUpdate; :global ScriptFromTerminal; :global ScriptLock; :global SendNotification2; @@ -62,8 +63,14 @@ $WaitFullyConnected; :if ([ :len [ /system/scheduler/find where name="_RebootForUpdate" ] ] > 0) do={ - :set ExitOK true; - :error "A reboot for update is already scheduled."; + :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..."); @@ -140,13 +147,13 @@ :if ([ :len $SafeUpdateUrl ] > 0) do={ :local Result; - :do { + :onerror Err { :set Result [ /tool/fetch check-certificate=yes-without-crl \ ($SafeUpdateUrl . $Update->"channel" . "?installed=" . $Update->"installed-version" . \ "&latest=" . $Update->"latest-version") http-header-field=({ [ $FetchUserAgentStr $ScriptName ] }) \ output=user as-value ]; - } on-error={ - $LogPrint warning $ScriptName ("Failed receiving safe version for " . $Update->"channel" . "."); + } do={ + $LogPrint warning $ScriptName ("Failed receiving safe version for " . $Update->"channel" . ": " . $Err); } :if ($Result->"status" = "finished" && $Result->"data" = $Update->"latest-version") do={ $LogPrint info $ScriptName ("Version " . $Update->"latest-version" . " is considered safe, updating..."); @@ -213,6 +220,6 @@ " is available for downgrade."); :set SentRouterosUpdateNotification ($Update->"latest-version"); } -} on-error={ - :global ExitError; $ExitError $ExitOK [ :jobname ]; +} do={ + :global ExitError; $ExitError $ExitOK [ :jobname ] $Err; } diff --git a/collect-wireless-mac.capsman.rsc b/collect-wireless-mac.capsman.rsc index 17e09e3..06b8d84 100644 --- a/collect-wireless-mac.capsman.rsc +++ b/collect-wireless-mac.capsman.rsc @@ -11,11 +11,11 @@ # # !! Do not edit this file, it is generated from template! -:global GlobalFunctionsReady; -:while ($GlobalFunctionsReady != true) do={ :delay 500ms; } - :local ExitOK false; -:do { +:onerror Err { + :global GlobalConfigReady; :global GlobalFunctionsReady; + :retry { :if ($GlobalConfigReady != true || $GlobalFunctionsReady != true) \ + do={ :error ("Global config and/or functions not ready."); }; } delay=500ms max=50; :local ScriptName [ :jobname ]; :global Identity; @@ -95,6 +95,6 @@ $LogPrint debug $ScriptName ("No mac address available... Ignoring."); } } -} on-error={ - :global ExitError; $ExitError $ExitOK [ :jobname ]; +} do={ + :global ExitError; $ExitError $ExitOK [ :jobname ] $Err; } diff --git a/collect-wireless-mac.local.rsc b/collect-wireless-mac.local.rsc index 4a38bfa..6716582 100644 --- a/collect-wireless-mac.local.rsc +++ b/collect-wireless-mac.local.rsc @@ -11,11 +11,11 @@ # # !! Do not edit this file, it is generated from template! -:global GlobalFunctionsReady; -:while ($GlobalFunctionsReady != true) do={ :delay 500ms; } - :local ExitOK false; -:do { +:onerror Err { + :global GlobalConfigReady; :global GlobalFunctionsReady; + :retry { :if ($GlobalConfigReady != true || $GlobalFunctionsReady != true) \ + do={ :error ("Global config and/or functions not ready."); }; } delay=500ms max=50; :local ScriptName [ :jobname ]; :global Identity; @@ -96,6 +96,6 @@ $LogPrint debug $ScriptName ("No mac address available... Ignoring."); } } -} on-error={ - :global ExitError; $ExitError $ExitOK [ :jobname ]; +} do={ + :global ExitError; $ExitError $ExitOK [ :jobname ] $Err; } diff --git a/collect-wireless-mac.template.rsc b/collect-wireless-mac.template.rsc index da901be..53e6b0a 100644 --- a/collect-wireless-mac.template.rsc +++ b/collect-wireless-mac.template.rsc @@ -12,11 +12,11 @@ # !! This is just a template to generate the real script! # !! Pattern '%TEMPL%' is replaced, paths are filtered. -:global GlobalFunctionsReady; -:while ($GlobalFunctionsReady != true) do={ :delay 500ms; } - :local ExitOK false; -:do { +:onerror Err { + :global GlobalConfigReady; :global GlobalFunctionsReady; + :retry { :if ($GlobalConfigReady != true || $GlobalFunctionsReady != true) \ + do={ :error ("Global config and/or functions not ready."); }; } delay=500ms max=50; :local ScriptName [ :jobname ]; :global Identity; @@ -113,6 +113,6 @@ $LogPrint debug $ScriptName ("No mac address available... Ignoring."); } } -} on-error={ - :global ExitError; $ExitError $ExitOK [ :jobname ]; +} do={ + :global ExitError; $ExitError $ExitOK [ :jobname ] $Err; } diff --git a/collect-wireless-mac.wifi.rsc b/collect-wireless-mac.wifi.rsc index cb217ce..43ac851 100644 --- a/collect-wireless-mac.wifi.rsc +++ b/collect-wireless-mac.wifi.rsc @@ -11,11 +11,11 @@ # # !! Do not edit this file, it is generated from template! -:global GlobalFunctionsReady; -:while ($GlobalFunctionsReady != true) do={ :delay 500ms; } - :local ExitOK false; -:do { +:onerror Err { + :global GlobalConfigReady; :global GlobalFunctionsReady; + :retry { :if ($GlobalConfigReady != true || $GlobalFunctionsReady != true) \ + do={ :error ("Global config and/or functions not ready."); }; } delay=500ms max=50; :local ScriptName [ :jobname ]; :global Identity; @@ -95,6 +95,6 @@ $LogPrint debug $ScriptName ("No mac address available... Ignoring."); } } -} on-error={ - :global ExitError; $ExitError $ExitOK [ :jobname ]; +} do={ + :global ExitError; $ExitError $ExitOK [ :jobname ] $Err; } diff --git a/contrib/checksums.sh b/contrib/checksums.sh new file mode 100755 index 0000000..b472b49 --- /dev/null +++ b/contrib/checksums.sh @@ -0,0 +1,9 @@ +#!/bin/sh + +# generate a checksums file as used by $ScriptInstallUpdate + +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' diff --git a/daily-psk.capsman.rsc b/daily-psk.capsman.rsc index 5672931..3ecd6b6 100644 --- a/daily-psk.capsman.rsc +++ b/daily-psk.capsman.rsc @@ -11,11 +11,11 @@ # # !! Do not edit this file, it is generated from template! -:global GlobalFunctionsReady; -:while ($GlobalFunctionsReady != true) do={ :delay 500ms; } - :local ExitOK false; -:do { +:onerror Err { + :global GlobalConfigReady; :global GlobalFunctionsReady; + :retry { :if ($GlobalConfigReady != true || $GlobalFunctionsReady != true) \ + do={ :error ("Global config and/or functions not ready."); }; } delay=500ms max=50; :local ScriptName [ :jobname ]; :global DailyPskMatchComment; @@ -91,6 +91,6 @@ } } } -} on-error={ - :global ExitError; $ExitError $ExitOK [ :jobname ]; +} do={ + :global ExitError; $ExitError $ExitOK [ :jobname ] $Err; } diff --git a/daily-psk.local.rsc b/daily-psk.local.rsc index 9dea469..d496350 100644 --- a/daily-psk.local.rsc +++ b/daily-psk.local.rsc @@ -11,11 +11,11 @@ # # !! Do not edit this file, it is generated from template! -:global GlobalFunctionsReady; -:while ($GlobalFunctionsReady != true) do={ :delay 500ms; } - :local ExitOK false; -:do { +:onerror Err { + :global GlobalConfigReady; :global GlobalFunctionsReady; + :retry { :if ($GlobalConfigReady != true || $GlobalFunctionsReady != true) \ + do={ :error ("Global config and/or functions not ready."); }; } delay=500ms max=50; :local ScriptName [ :jobname ]; :global DailyPskMatchComment; @@ -90,6 +90,6 @@ } } } -} on-error={ - :global ExitError; $ExitError $ExitOK [ :jobname ]; +} do={ + :global ExitError; $ExitError $ExitOK [ :jobname ] $Err; } diff --git a/daily-psk.template.rsc b/daily-psk.template.rsc index 8202eeb..5a1df2f 100644 --- a/daily-psk.template.rsc +++ b/daily-psk.template.rsc @@ -12,11 +12,11 @@ # !! This is just a template to generate the real script! # !! Pattern '%TEMPL%' is replaced, paths are filtered. -:global GlobalFunctionsReady; -:while ($GlobalFunctionsReady != true) do={ :delay 500ms; } - :local ExitOK false; -:do { +:onerror Err { + :global GlobalConfigReady; :global GlobalFunctionsReady; + :retry { :if ($GlobalConfigReady != true || $GlobalFunctionsReady != true) \ + do={ :error ("Global config and/or functions not ready."); }; } delay=500ms max=50; :local ScriptName [ :jobname ]; :global DailyPskMatchComment; @@ -106,6 +106,6 @@ } } } -} on-error={ - :global ExitError; $ExitError $ExitOK [ :jobname ]; +} do={ + :global ExitError; $ExitError $ExitOK [ :jobname ] $Err; } diff --git a/daily-psk.wifi.rsc b/daily-psk.wifi.rsc index 3de3c5b..c441e58 100644 --- a/daily-psk.wifi.rsc +++ b/daily-psk.wifi.rsc @@ -11,11 +11,11 @@ # # !! Do not edit this file, it is generated from template! -:global GlobalFunctionsReady; -:while ($GlobalFunctionsReady != true) do={ :delay 500ms; } - :local ExitOK false; -:do { +:onerror Err { + :global GlobalConfigReady; :global GlobalFunctionsReady; + :retry { :if ($GlobalConfigReady != true || $GlobalFunctionsReady != true) \ + do={ :error ("Global config and/or functions not ready."); }; } delay=500ms max=50; :local ScriptName [ :jobname ]; :global DailyPskMatchComment; @@ -91,6 +91,6 @@ } } } -} on-error={ - :global ExitError; $ExitError $ExitOK [ :jobname ]; +} do={ + :global ExitError; $ExitError $ExitOK [ :jobname ] $Err; } diff --git a/dhcp-lease-comment.capsman.rsc b/dhcp-lease-comment.capsman.rsc index 36b31c8..3615bb9 100644 --- a/dhcp-lease-comment.capsman.rsc +++ b/dhcp-lease-comment.capsman.rsc @@ -11,11 +11,11 @@ # # !! Do not edit this file, it is generated from template! -:global GlobalFunctionsReady; -:while ($GlobalFunctionsReady != true) do={ :delay 500ms; } - :local ExitOK false; -:do { +:onerror Err { + :global GlobalConfigReady; :global GlobalFunctionsReady; + :retry { :if ($GlobalConfigReady != true || $GlobalFunctionsReady != true) \ + do={ :error ("Global config and/or functions not ready."); }; } delay=500ms max=50; :local ScriptName [ :jobname ]; :global LogPrint; @@ -38,6 +38,6 @@ /ip/dhcp-server/lease/set comment=$NewComment $Lease; } } -} on-error={ - :global ExitError; $ExitError $ExitOK [ :jobname ]; +} do={ + :global ExitError; $ExitError $ExitOK [ :jobname ] $Err; } diff --git a/dhcp-lease-comment.local.rsc b/dhcp-lease-comment.local.rsc index 35dc6f6..9da5333 100644 --- a/dhcp-lease-comment.local.rsc +++ b/dhcp-lease-comment.local.rsc @@ -11,11 +11,11 @@ # # !! Do not edit this file, it is generated from template! -:global GlobalFunctionsReady; -:while ($GlobalFunctionsReady != true) do={ :delay 500ms; } - :local ExitOK false; -:do { +:onerror Err { + :global GlobalConfigReady; :global GlobalFunctionsReady; + :retry { :if ($GlobalConfigReady != true || $GlobalFunctionsReady != true) \ + do={ :error ("Global config and/or functions not ready."); }; } delay=500ms max=50; :local ScriptName [ :jobname ]; :global LogPrint; @@ -38,6 +38,6 @@ /ip/dhcp-server/lease/set comment=$NewComment $Lease; } } -} on-error={ - :global ExitError; $ExitError $ExitOK [ :jobname ]; +} do={ + :global ExitError; $ExitError $ExitOK [ :jobname ] $Err; } diff --git a/dhcp-lease-comment.template.rsc b/dhcp-lease-comment.template.rsc index 47a8554..62cace1 100644 --- a/dhcp-lease-comment.template.rsc +++ b/dhcp-lease-comment.template.rsc @@ -12,11 +12,11 @@ # !! This is just a template to generate the real script! # !! Pattern '%TEMPL%' is replaced, paths are filtered. -:global GlobalFunctionsReady; -:while ($GlobalFunctionsReady != true) do={ :delay 500ms; } - :local ExitOK false; -:do { +:onerror Err { + :global GlobalConfigReady; :global GlobalFunctionsReady; + :retry { :if ($GlobalConfigReady != true || $GlobalFunctionsReady != true) \ + do={ :error ("Global config and/or functions not ready."); }; } delay=500ms max=50; :local ScriptName [ :jobname ]; :global LogPrint; @@ -43,6 +43,6 @@ /ip/dhcp-server/lease/set comment=$NewComment $Lease; } } -} on-error={ - :global ExitError; $ExitError $ExitOK [ :jobname ]; +} do={ + :global ExitError; $ExitError $ExitOK [ :jobname ] $Err; } diff --git a/dhcp-lease-comment.wifi.rsc b/dhcp-lease-comment.wifi.rsc index e0f9785..667708c 100644 --- a/dhcp-lease-comment.wifi.rsc +++ b/dhcp-lease-comment.wifi.rsc @@ -11,11 +11,11 @@ # # !! Do not edit this file, it is generated from template! -:global GlobalFunctionsReady; -:while ($GlobalFunctionsReady != true) do={ :delay 500ms; } - :local ExitOK false; -:do { +:onerror Err { + :global GlobalConfigReady; :global GlobalFunctionsReady; + :retry { :if ($GlobalConfigReady != true || $GlobalFunctionsReady != true) \ + do={ :error ("Global config and/or functions not ready."); }; } delay=500ms max=50; :local ScriptName [ :jobname ]; :global LogPrint; @@ -38,6 +38,6 @@ /ip/dhcp-server/lease/set comment=$NewComment $Lease; } } -} on-error={ - :global ExitError; $ExitError $ExitOK [ :jobname ]; +} do={ + :global ExitError; $ExitError $ExitOK [ :jobname ] $Err; } diff --git a/dhcp-to-dns.rsc b/dhcp-to-dns.rsc index 9b94098..a9c91e1 100644 --- a/dhcp-to-dns.rsc +++ b/dhcp-to-dns.rsc @@ -9,11 +9,11 @@ # check DHCP leases and add/remove/update DNS entries # https://rsc.eworm.de/doc/dhcp-to-dns.md -:global GlobalFunctionsReady; -:while ($GlobalFunctionsReady != true) do={ :delay 500ms; } - :local ExitOK false; -:do { +:onerror Err { + :global GlobalConfigReady; :global GlobalFunctionsReady; + :retry { :if ($GlobalConfigReady != true || $GlobalFunctionsReady != true) \ + do={ :error ("Global config and/or functions not ready."); }; } delay=500ms max=50; :local ScriptName [ :jobname ]; :global Domain; @@ -125,6 +125,6 @@ $LogPrint debug $ScriptName ("No address available... Ignoring."); } } -} on-error={ - :global ExitError; $ExitError $ExitOK [ :jobname ]; +} do={ + :global ExitError; $ExitError $ExitOK [ :jobname ] $Err; } diff --git a/doc/backup-cloud.md b/doc/backup-cloud.md index 7286960..7d55d74 100644 --- a/doc/backup-cloud.md +++ b/doc/backup-cloud.md @@ -17,7 +17,7 @@ Description ----------- This script uploads -[binary backup to Mikrotik cloud](https://wiki.mikrotik.com/wiki/Manual:IP/Cloud#Backup). +[binary backup to Mikrotik cloud ↗️](https://wiki.mikrotik.com/wiki/Manual:IP/Cloud#Backup). > ⚠️ **Warning**: The used command can hit errors that a script can with > workaround only. A notification *should* be sent anyway. But it can result @@ -49,6 +49,7 @@ The configuration goes to `global-config-overlay`, these are the parameters: Also notification settings are required for [e-mail](mod/notification-email.md), +[gotify](mod/notification-gotify.md), [matrix](mod/notification-matrix.md), [ntfy](mod/notification-ntfy.md) and/or [telegram](mod/notification-telegram.md). diff --git a/doc/backup-partition.md b/doc/backup-partition.md index 9d615a5..50b8a09 100644 --- a/doc/backup-partition.md +++ b/doc/backup-partition.md @@ -17,7 +17,7 @@ Description ----------- This script saves the current configuration to fallback -[partition](https://wiki.mikrotik.com/wiki/Manual:Partitions). +[partition ↗️](https://wiki.mikrotik.com/wiki/Manual:Partitions). It can also copy-over the RouterOS installation when run interactively or just before a feature update. diff --git a/doc/backup-upload.md b/doc/backup-upload.md index 6a5b0e4..b4012c8 100644 --- a/doc/backup-upload.md +++ b/doc/backup-upload.md @@ -55,6 +55,7 @@ The configuration goes to `global-config-overlay`, these are the parameters: Also notification settings are required for [e-mail](mod/notification-email.md), +[gotify](mod/notification-gotify.md), [matrix](mod/notification-matrix.md), [ntfy](mod/notification-ntfy.md) and/or [telegram](mod/notification-telegram.md). diff --git a/doc/check-certificates.md b/doc/check-certificates.md index 4c144ba..a9426db 100644 --- a/doc/check-certificates.md +++ b/doc/check-certificates.md @@ -51,6 +51,7 @@ subject alternative name (aka *Subject Alt Name* or *SAN*) can be used. Also notification settings are required for [e-mail](mod/notification-email.md), +[gotify](mod/notification-gotify.md), [matrix](mod/notification-matrix.md), [ntfy](mod/notification-ntfy.md) and/or [telegram](mod/notification-telegram.md). diff --git a/doc/check-health.md b/doc/check-health.md index 7cf0c33..33847e3 100644 --- a/doc/check-health.md +++ b/doc/check-health.md @@ -113,6 +113,7 @@ The configuration goes to `global-config-overlay`, these are the parameters: Also notification settings are required for [e-mail](mod/notification-email.md), +[gotify](mod/notification-gotify.md), [matrix](mod/notification-matrix.md), [ntfy](mod/notification-ntfy.md) and/or [telegram](mod/notification-telegram.md). diff --git a/doc/check-lte-firmware-upgrade.md b/doc/check-lte-firmware-upgrade.md index 3693b71..a0c441e 100644 --- a/doc/check-lte-firmware-upgrade.md +++ b/doc/check-lte-firmware-upgrade.md @@ -44,7 +44,9 @@ Configuration Also notification settings are required for [e-mail](mod/notification-email.md), -[matrix](mod/notification-matrix.md) and/or +[gotify](mod/notification-gotify.md), +[matrix](mod/notification-matrix.md), +[ntfy](mod/notification-ntfy.md) and/or [telegram](mod/notification-telegram.md). See also diff --git a/doc/check-perpetual-license.d/notification.avif b/doc/check-perpetual-license.d/notification.avif Binary files differnew file mode 100644 index 0000000..70ca603 --- /dev/null +++ b/doc/check-perpetual-license.d/notification.avif diff --git a/doc/check-perpetual-license.md b/doc/check-perpetual-license.md new file mode 100644 index 0000000..0335fb5 --- /dev/null +++ b/doc/check-perpetual-license.md @@ -0,0 +1,71 @@ +Check perpetual license on CHR +============================== + +[](https://github.com/eworm-de/routeros-scripts/stargazers) +[](https://github.com/eworm-de/routeros-scripts/network) +[](https://github.com/eworm-de/routeros-scripts/watchers) +[](https://mikrotik.com/download/changelogs/) +[](https://t.me/routeros_scripts) +[](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=A4ZXBD6YS2W8J) + +[⬅️ Go back to main README](../README.md) + +> ℹ️ **Info**: This script can not be used on its own but requires the base +> installation. See [main README](../README.md) for details. + +Description +----------- + +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 + + + +Requirements and installation +----------------------------- + +Just install the script: + + $ScriptInstallUpdate check-perpetual-license; + +And add a scheduler for automatic update notification: + + /system/scheduler/add interval=1d name=check-perpetual-license on-event="/system/script/run check-perpetual-license;" start-time=startup; + +Configuration +------------- + +No extra configuration is required for this script, but notification +settings are required for +[e-mail](mod/notification-email.md), +[gotify](mod/notification-gotify.md), +[matrix](mod/notification-matrix.md), +[ntfy](mod/notification-ntfy.md) and/or +[telegram](mod/notification-telegram.md). + +Usage and invocation +-------------------- + +Be notified when run from scheduler or run it manually: + + /system/script/run check-perpetual-license; + +Tips & Tricks +------------- + +The script checks for full connectivity before acting, so scheduling at +startup is perfectly valid: + + /system/scheduler/add name=check-perpetual-license@startup on-event="/system/script/run check-perpetual-license;" start-time=startup; + +See also +-------- + +* [Notify on RouterOS update](check-routeros-update.md) + +--- +[⬅️ Go back to main README](../README.md) +[⬆️ Go back to top](#top) diff --git a/doc/check-routeros-update.md b/doc/check-routeros-update.md index 926b4aa..a45e075 100644 --- a/doc/check-routeros-update.md +++ b/doc/check-routeros-update.md @@ -30,8 +30,8 @@ automatically is supported. > ⚠️ **Warning**: Installing updates is important from a security point > 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 +> [changelog ↗️](https://mikrotik.com/download/changelogs/) and +> [forum ↗️](https://forum.mikrotik.com/viewforum.php?f=21) before deploying > to your production environment! Automatic updates should be handled > with care! @@ -73,6 +73,7 @@ The configuration goes to `global-config-overlay`, these are the parameters: Also notification settings are required for [e-mail](mod/notification-email.md), +[gotify](mod/notification-gotify.md), [matrix](mod/notification-matrix.md), [ntfy](mod/notification-ntfy.md) and/or [telegram](mod/notification-telegram.md). @@ -99,6 +100,7 @@ startup is perfectly valid: See also -------- +* [Check perpetual license on CHR](check-perpetual-license.md) * [Automatically upgrade firmware and reboot](firmware-upgrade-reboot.md) * [Manage system update](packages-update.md) diff --git a/doc/collect-wireless-mac.md b/doc/collect-wireless-mac.md index 0197522..2378fed 100644 --- a/doc/collect-wireless-mac.md +++ b/doc/collect-wireless-mac.md @@ -54,6 +54,7 @@ entries are to be added. Also notification settings are required for [e-mail](mod/notification-email.md), +[gotify](mod/notification-gotify.md), [matrix](mod/notification-matrix.md), [ntfy](mod/notification-ntfy.md) and/or [telegram](mod/notification-telegram.md). diff --git a/doc/daily-psk.md b/doc/daily-psk.md index 4a3de64..118d768 100644 --- a/doc/daily-psk.md +++ b/doc/daily-psk.md @@ -79,6 +79,7 @@ For legacy local interface: Also notification settings are required for [e-mail](mod/notification-email.md), +[gotify](mod/notification-gotify.md), [trix](mod/notification-matrix.md), [ntfy](mod/notification-ntfy.md) and/or [telegram](mod/notification-telegram.md). diff --git a/doc/fw-addr-lists.md b/doc/fw-addr-lists.md index cb560d7..46b80c2 100644 --- a/doc/fw-addr-lists.md +++ b/doc/fw-addr-lists.md @@ -19,10 +19,10 @@ Description This script downloads, imports and updates firewall address-lists. Its main purpose is to block attacking ip addresses, spam hosts, command-and-control servers and similar malicious entities. The default configuration contains a -[collective list by GitHub user @stamparm](https://github.com/stamparm/ipsum), -lists from [dshield.org](https://dshield.org/) and -[blocklist.de](https://www.blocklist.de/), and lists from -[spamhaus.org](https://spamhaus.org/) are prepared. +[collective list by GitHub user @stamparm ↗️](https://github.com/stamparm/ipsum), +lists from [dshield.org ↗️](https://dshield.org/) and +[blocklist.de ↗️](https://www.blocklist.de/), and lists from +[spamhaus.org ↗️](https://spamhaus.org/) are prepared. The address-lists are updated in place, so after initial import you will not see situation when the lists are not populated. @@ -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.md b/doc/log-forward.md index 3c19569..f6086c8 100644 --- a/doc/log-forward.md +++ b/doc/log-forward.md @@ -22,15 +22,15 @@ server (see `/system/logging`). This has some limitation, however: * does not work early after boot if network connectivity is not yet established, or breaks intermittently * lots of messages generate a flood of mails -* Matrix, Ntfy and Telegram are not supported +* Gotify, Matrix, Ntfy and Telegram are not supported The script works around the limitations, for example it does: * read from `/log`, including messages from early boot * skip multi-repeated messages * rate-limit itself to mitigate flooding -* forward via notification (which includes *e-mail*, *Matrix*, *Ntfy* and - *Telegram* when installed and configured, see below) +* forward via notification (which includes *e-mail*, *Gotify*, *Matrix*, + *Ntfy* and *Telegram* when installed and configured, see below) It is intended to be run periodically from scheduler, then collects new log messages and forwards them via notification. @@ -72,7 +72,7 @@ The configuration goes to `global-config-overlay`, these are the parameters: > your local `global-config-overlay` and modify it to your specific needs. These patterns are matched as -[regular expressions](https://wiki.mikrotik.com/wiki/Manual:Regular_Expressions). +[regular expressions ↗️](https://wiki.mikrotik.com/wiki/Manual:Regular_Expressions). To forward **all** (ignoring severity) log messages with topics `account` (which includes user logins) and `dhcp` you need something like: @@ -80,6 +80,7 @@ To forward **all** (ignoring severity) log messages with topics `account` Also notification settings are required for [e-mail](mod/notification-email.md), +[gotify](mod/notification-gotify.md), [matrix](mod/notification-matrix.md), [ntfy](mod/notification-ntfy.md) and/or [telegram](mod/notification-telegram.md). diff --git a/doc/mod/notification-email.md b/doc/mod/notification-email.md index 34d1c09..127bf96 100644 --- a/doc/mod/notification-email.md +++ b/doc/mod/notification-email.md @@ -32,7 +32,7 @@ Configuration ------------- Set up your device's -[e-mail settings](https://wiki.mikrotik.com/wiki/Manual:Tools/email). +[e-mail settings ↗️](https://wiki.mikrotik.com/wiki/Manual:Tools/email). Also make sure the device has correct time configured, best is to set up the ntp client. @@ -79,6 +79,7 @@ function available: See also -------- +* [Send notifications via Gotify](notification-gotify.md) * [Send notifications via Matrix](notification-matrix.md) * [Send notifications via Ntfy](notification-ntfy.md) * [Send notifications via Telegram](notification-telegram.md) diff --git a/doc/mod/notification-gotify.d/appsetup.avif b/doc/mod/notification-gotify.d/appsetup.avif Binary files differnew file mode 100644 index 0000000..58f57a8 --- /dev/null +++ b/doc/mod/notification-gotify.d/appsetup.avif diff --git a/doc/mod/notification-gotify.md b/doc/mod/notification-gotify.md new file mode 100644 index 0000000..6fce629 --- /dev/null +++ b/doc/mod/notification-gotify.md @@ -0,0 +1,97 @@ +Send notifications via Gotify +=========================== + +[](https://github.com/eworm-de/routeros-scripts/stargazers) +[](https://github.com/eworm-de/routeros-scripts/network) +[](https://github.com/eworm-de/routeros-scripts/watchers) +[](https://mikrotik.com/download/changelogs/) +[](https://t.me/routeros_scripts) +[](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=A4ZXBD6YS2W8J) + +[⬅️ Go back to main README](../../README.md) + +> ℹ️️ **Info**: This module can not be used on its own but requires the base +> installation. See [main README](../../README.md) for details. + +Description +----------- + +This module adds support for sending notifications via +[Gotify ↗️](https://gotify.net/). A queue is used to make sure +notifications are not lost on failure but sent later. + +Requirements and installation +----------------------------- + +Just install the module: + + $ScriptInstallUpdate mod/notification-gotify; + +Also deploy the [Gotify server ↗️](https://github.com/gotify/server) and +optionally install a Gotify client on your mobile device. + +Configuration +------------- + +Follow the [Installation ↗️](https://gotify.net/docs/install) instructions +and the [First Login ↗️](https://gotify.net/docs/first-login) setup. Once +you have a user and account you can start creating apps. Each app is an +independent notification feed for a device or application. + + + +On creation apps are assigned a *Token* for authentification, you will need +that in configuration. + +Edit `global-config-overlay`, add `GotifyServer` with your server address +(just the address, no protocol - `https://` is assumed) and `GotifyToken` +with the *Token* from your configured app on the Gotify server. Then reload +the configuration. + +> ℹ️ **Info**: Copy relevant configuration from +> [`global-config`](../../global-config.rsc) (the one without `-overlay`) to +> your local `global-config-overlay` and modify it to your specific needs. + +For a custom service installing an additional certificate may be required. +You may want to install that certificate manually, after finding the +[certificate name from browser](../../CERTIFICATES.md). + +Usage and invocation +-------------------- + +There's nothing special to do. Every script or function sending a notification +will now send it to your Gotify application feed. + +But of course you can use the function to send notifications directly. Give +it a try: + + $SendGotify "Subject..." "Body..."; + +Alternatively this sends a notification with all available and configured +methods: + + $SendNotification "Subject..." "Body..."; + +To use the functions in your own scripts you have to declare them first. +Place this before you call them: + + :global SendGotify; + :global SendNotification; + +In case there is a situation when the queue needs to be purged there is a +function available: + + $PurgeGotifyQueue; + +See also +-------- + +* [Certificate name from browser](../../CERTIFICATES.md) +* [Send notifications via e-mail](notification-email.md) +* [Send notifications via Matrix](notification-matrix.md) +* [Send notifications via Ntfy](notification-ntfy.md) +* [Send notifications via Telegram](notification-telegram.md) + +--- +[⬅️ Go back to main README](../../README.md) +[⬆️ Go back to top](#top) diff --git a/doc/mod/notification-matrix.md b/doc/mod/notification-matrix.md index 89c1b01..da6d6de 100644 --- a/doc/mod/notification-matrix.md +++ b/doc/mod/notification-matrix.md @@ -17,7 +17,7 @@ Description ----------- This module adds support for sending notifications via -[Matrix](https://matrix.org/) via client server api. A queue is used to +[Matrix ↗️](https://matrix.org/) via client server api. A queue is used to make sure notifications are not lost on failure but sent later. Requirements and installation @@ -131,6 +131,7 @@ See also * [Certificate name from browser](../../CERTIFICATES.md) * [Send notifications via e-mail](notification-email.md) +* [Send notifications via Gotify](notification-gotify.md) * [Send notifications via Ntfy](notification-ntfy.md) * [Send notifications via Telegram](notification-telegram.md) diff --git a/doc/mod/notification-ntfy.md b/doc/mod/notification-ntfy.md index 51756ac..993501d 100644 --- a/doc/mod/notification-ntfy.md +++ b/doc/mod/notification-ntfy.md @@ -17,7 +17,7 @@ Description ----------- This module adds support for sending notifications via -[Ntfy](https://ntfy.sh/). A queue is used to make sure +[Ntfy ↗️](https://ntfy.sh/). A queue is used to make sure notifications are not lost on failure but sent later. Requirements and installation @@ -28,7 +28,7 @@ Just install the module: $ScriptInstallUpdate mod/notification-ntfy; Also install the Ntfy app on your mobile device or use the -[web app](https://ntfy.sh/app) in a browser of your choice. +[web app ↗️](https://ntfy.sh/app) in a browser of your choice. Configuration ------------- @@ -90,6 +90,7 @@ See also * [Certificate name from browser](../../CERTIFICATES.md) * [Send notifications via e-mail](notification-email.md) +* [Send notifications via Gotify](notification-gotify.md) * [Send notifications via Matrix](notification-matrix.md) * [Send notifications via Telegram](notification-telegram.md) diff --git a/doc/mod/notification-telegram.d/getchatid.avif b/doc/mod/notification-telegram.d/getchatid.avif Binary files differnew file mode 100644 index 0000000..7792969 --- /dev/null +++ b/doc/mod/notification-telegram.d/getchatid.avif diff --git a/doc/mod/notification-telegram.md b/doc/mod/notification-telegram.md index b85d09c..804104f 100644 --- a/doc/mod/notification-telegram.md +++ b/doc/mod/notification-telegram.md @@ -17,7 +17,7 @@ Description ----------- This module adds support for sending notifications via -[Telegram](https://telegram.org/) via bot api. A queue is used to make sure +[Telegram ↗️](https://telegram.org/) via bot api. A queue is used to make sure notifications are not lost on failure but sent later. Requirements and installation @@ -33,19 +33,26 @@ and create an account. Configuration ------------- -Open Telegram, then start a chat with [BotFather](https://t.me/BotFather) and +Open Telegram, then start a chat with [BotFather ↗️](https://t.me/BotFather) and create your own bot:  -Now open a chat with your bot and start it by clicking the `START` button. +Set that token from *BotFather* (use your own!) to `TelegramTokenId`, for +now just temporarily: -Open just another chat with [GetIDs Bot](https://t.me/getidsbot), again start -with the `START` button. It will send you some information, including the -`id`, just below `You`. + :set TelegramTokenId "5214364459:AAHLwf1o7ybbKDo6pY24Kd2bZ5rjCakDXTc"; + +Now open a chat with your bot and start it by clicking the `START` button, +then send your first message. Any text will do. On your device run +`$GetTelegramChatId` to retrieve the chat id: + + $GetTelegramChatId; + + Finally edit `global-config-overlay`, add `TelegramTokenId` with the token -from *BotFather* and `TelegramChatId` with your id from *GetIDs Bot*. Then +from *BotFather* and `TelegramChatId` with your retrieved chat id. Then reload the configuration. > ℹ️ **Info**: Copy relevant configuration from @@ -54,9 +61,13 @@ reload the configuration. ### Notifications to a group -Sending notifications to a group is possible as well. Add your bot and the -*GetIDs Bot* to a group, then use the group's id (which starts with a dash) -for `TelegramChatId`. Then remove *GetIDs Bot* from group. +Sending notifications to a group is possible as well. Add your bot to a group +and make it an admin (required for read access!) and send a message and run +`$GetTelegramChatId` again. Then use that chat id (which starts with a dash) +for `TelegramChatId`. + +Groups can enable the `Topics` feature. Use `TelegramThreadId` to send to a +specific topic in a group. Usage and invocation -------------------- @@ -91,7 +102,7 @@ Tips & Tricks ### Set a profile photo 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. +chat with [BotFather ↗️](https://t.me/BotFather) and set it there.  @@ -104,6 +115,7 @@ See also * [Chat with your router and send commands via Telegram bot](../telegram-chat.md) * [Send notifications via e-mail](notification-email.md) +* [Send notifications via Gotify](notification-gotify.md) * [Send notifications via Matrix](notification-matrix.md) * [Send notifications via Ntfy](notification-ntfy.md) diff --git a/doc/netwatch-notify.md b/doc/netwatch-notify.md index 81adfe9..91c568f 100644 --- a/doc/netwatch-notify.md +++ b/doc/netwatch-notify.md @@ -47,6 +47,7 @@ The hosts to be checked have to be added to netwatch with specific comment: Also notification settings are required for [e-mail](mod/notification-email.md), +[gotify](mod/notification-gotify.md), [matrix](mod/notification-matrix.md), [ntfy](mod/notification-ntfy.md) and/or [telegram](mod/notification-telegram.md). 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.md b/doc/sms-forward.md index ccb6482..0c1317d 100644 --- a/doc/sms-forward.md +++ b/doc/sms-forward.md @@ -56,6 +56,7 @@ The configuration goes to `global-config-overlay`, this is the only parameter: Notification settings are required for [e-mail](mod/notification-email.md), +[gotify](mod/notification-gotify.md), [matrix](mod/notification-matrix.md), [ntfy](mod/notification-ntfy.md) and/or [telegram](mod/notification-telegram.md). diff --git a/firmware-upgrade-reboot.rsc b/firmware-upgrade-reboot.rsc index 86a9a8c..e3ca55b 100644 --- a/firmware-upgrade-reboot.rsc +++ b/firmware-upgrade-reboot.rsc @@ -8,11 +8,11 @@ # install firmware upgrade, and reboot # https://rsc.eworm.de/doc/firmware-upgrade-reboot.md -:global GlobalFunctionsReady; -:while ($GlobalFunctionsReady != true) do={ :delay 500ms; } - :local ExitOK false; -:do { +:onerror Err { + :global GlobalConfigReady; :global GlobalFunctionsReady; + :retry { :if ($GlobalConfigReady != true || $GlobalFunctionsReady != true) \ + do={ :error ("Global config and/or functions not ready."); }; } delay=500ms max=50; :local ScriptName [ :jobname ]; :global LogPrint; @@ -55,6 +55,6 @@ $LogPrint info $ScriptName ("Firmware upgrade successful, rebooting."); /system/reboot; -} on-error={ - :global ExitError; $ExitError $ExitOK [ :jobname ]; +} do={ + :global ExitError; $ExitError $ExitOK [ :jobname ] $Err; } diff --git a/fw-addr-lists.rsc b/fw-addr-lists.rsc index d41dc04..0c45f7e 100644 --- a/fw-addr-lists.rsc +++ b/fw-addr-lists.rsc @@ -8,11 +8,11 @@ # download, import and update firewall address-lists # https://rsc.eworm.de/doc/fw-addr-lists.md -:global GlobalFunctionsReady; -:while ($GlobalFunctionsReady != true) do={ :delay 500ms; } - :local ExitOK false; -:do { +:onerror Err { + :global GlobalConfigReady; :global GlobalFunctionsReady; + :retry { :if ($GlobalConfigReady != true || $GlobalFunctionsReady != true) \ + do={ :error ("Global config and/or functions not ready."); }; } delay=500ms max=50; :local ScriptName [ :jobname ]; :global FwAddrLists; @@ -24,6 +24,7 @@ :global HumanReadableNum; :global LogPrint; :global LogPrintOnce; + :global LogPrintVerbose; :global ScriptLock; :global WaitFullyConnected; @@ -36,12 +37,23 @@ } } + :local GetBranch do={ + :global EitherOr; + :return [ :pick [ :convert transform=md5 to=hex [ :pick $1 0 [ $EitherOr [ :find $1 "/" ] [ :len $1 ] ] ] ] 0 2 ]; + } + :if ([ $ScriptLock $ScriptName ] = false) do={ :set ExitOK true; :error false; } $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={ @@ -99,17 +111,24 @@ :set Address ([ :pick $Line 0 [ $FindDelim $Line ] ] . ($List->"cidr")); } :do { + :local Branch [ $GetBranch $Address ]; :if ($Address ~ "^[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}(/[0-9]{1,2})?\$") do={ - :set ($IPv4Addresses->$Address) $TimeOut; + :if ($Address ~ "/32\$") do={ + :set Address [ :pick $Address 0 ([ :len $Address ] - 3) ]; + } + :set ($IPv4Addresses->$Branch->$Address) $TimeOut; :error true; } :if ($Address ~ "^[0-9a-zA-Z]*:[0-9a-zA-Z:\\.]+(/[0-9]{1,3})?\$") do={ - :set ($IPv6Addresses->$Address) $TimeOut; + :if ([ :typeof [ :find $Address "/" ] ] = "nil") do={ + :set Address ($Address . "/128"); + } + :set ($IPv6Addresses->$Branch->$Address) $TimeOut; :error true; } :if ($Address ~ "^[\\.a-zA-Z0-9-]+\\.[a-zA-Z]{2,}\$") do={ - :set ($IPv4Addresses->$Address) $TimeOut; - :set ($IPv6Addresses->$Address) $TimeOut; + :set ($IPv4Addresses->$Branch->$Address) $TimeOut; + :set ($IPv6Addresses->$Branch->$Address) $TimeOut; :error true; } } on-error={ } @@ -119,16 +138,18 @@ :foreach Entry in=[ /ip/firewall/address-list/find where \ list=$FwListName comment=$ListComment ] do={ :local Address [ /ip/firewall/address-list/get $Entry address ]; - :if ([ :typeof ($IPv4Addresses->$Address) ] = "time") do={ - $LogPrint debug $ScriptName ("Renewing IPv4 address in list '" . $FwListName . \ - "' with " . ($IPv4Addresses->$Address) . ": " . $Address); - /ip/firewall/address-list/set $Entry timeout=($IPv4Addresses->$Address); - :set ($IPv4Addresses->$Address); + :local Branch [ $GetBranch $Address ]; + :local TimeOut ($IPv4Addresses->$Branch->$Address); + :if ([ :typeof $TimeOut ] = "time") do={ + $LogPrintVerbose debug $ScriptName ("Renewing IPv4 address " . $Address . \ + " in list '" . $FwListName . "' with " . $TimeOut . "."); + /ip/firewall/address-list/set $Entry timeout=$TimeOut; + :set ($IPv4Addresses->$Branch->$Address); :set CntRenew ($CntRenew + 1); } else={ :if ($Failure = false) do={ - $LogPrint debug $ScriptName ("Removing IPv4 address from list '" . $FwListName . \ - "': " . $Address); + $LogPrintVerbose debug $ScriptName ("Removing IPv4 address " . $Address . \ + " from list '" . $FwListName . "."); /ip/firewall/address-list/remove $Entry; :set CntRemove ($CntRemove + 1); } @@ -138,47 +159,53 @@ :foreach Entry in=[ /ipv6/firewall/address-list/find where \ list=$FwListName comment=$ListComment ] do={ :local Address [ /ipv6/firewall/address-list/get $Entry address ]; - :if ([ :typeof ($IPv6Addresses->$Address) ] = "time") do={ - $LogPrint debug $ScriptName ("Renewing IPv6 address in list '" . $FwListName . \ - "' with " . ($IPv6Addresses->$Address) . ": " . $Address); - /ipv6/firewall/address-list/set $Entry timeout=($IPv6Addresses->$Address); - :set ($IPv6Addresses->$Address); + :local Branch [ $GetBranch $Address ]; + :local TimeOut ($IPv6Addresses->$Branch->$Address); + :if ([ :typeof $TimeOut ] = "time") do={ + $LogPrintVerbose debug $ScriptName ("Renewing IPv6 address " . $Address . \ + " in list '" . $FwListName . "' with " . $TimeOut . "."); + /ipv6/firewall/address-list/set $Entry timeout=$TimeOut; + :set ($IPv6Addresses->$Branch->$Address); :set CntRenew ($CntRenew + 1); } else={ :if ($Failure = false) do={ - $LogPrint debug $ScriptName ("Removing IPv6 address from list '" . $FwListName . \ - "': " . $Address); + $LogPrintVerbose debug $ScriptName ("Removing IPv6 address " . $Address . \ + " from list '" . $FwListName ."."); /ipv6/firewall/address-list/remove $Entry; :set CntRemove ($CntRemove + 1); } } } - :foreach Address,Timeout in=$IPv4Addresses do={ - $LogPrint debug $ScriptName ("Adding IPv4 address to list '" . $FwListName . \ - "' with " . $Timeout . ": " . $Address); - :do { - /ip/firewall/address-list/add list=$FwListName comment=$ListComment \ - address=$Address timeout=$Timeout; - :set ($IPv4Addresses->$Address); - :set CntAdd ($CntAdd + 1); - } on-error={ - $LogPrint warning $ScriptName ("Failed to add IPv4 address to list '" . $FwListName . \ - "': " . $Address); + :foreach BranchName,Branch in=$IPv4Addresses do={ + $LogPrintVerbose debug $ScriptName ("Handling branch: " . $BranchName); + :foreach Address,Timeout in=$Branch do={ + $LogPrintVerbose debug $ScriptName ("Adding IPv4 address " . $Address . \ + " to list '" . $FwListName . "' with " . $Timeout . "."); + :onerror Err { + /ip/firewall/address-list/add list=$FwListName comment=$ListComment \ + address=$Address timeout=$Timeout; + :set CntAdd ($CntAdd + 1); + } do={ + $LogPrint warning $ScriptName ("Failed to add IPv4 address " . $Address . \ + " to list '" . $FwListName . "': " . $Err); + } } } - :foreach Address,Timeout in=$IPv6Addresses do={ - $LogPrint debug $ScriptName ("Adding IPv6 address to list '" . $FwListName . \ - "' with " . $Timeout . ": " . $Address); - :do { - /ipv6/firewall/address-list/add list=$FwListName comment=$ListComment \ - address=$Address timeout=$Timeout; - :set ($IPv6Addresses->$Address); - :set CntAdd ($CntAdd + 1); - } on-error={ - $LogPrint warning $ScriptName ("Failed to add IPv6 address to list '" . $FwListName . \ - "': " . $Address); + :foreach BranchName,Branch in=$IPv6Addresses do={ + $LogPrintVerbose debug $ScriptName ("Handling branch: " . $BranchName); + :foreach Address,Timeout in=$Branch do={ + $LogPrintVerbose debug $ScriptName ("Adding IPv6 address " . $Address . \ + " to list '" . $FwListName . "' with " . $Timeout . "."); + :onerror Err { + /ipv6/firewall/address-list/add list=$FwListName comment=$ListComment \ + address=$Address timeout=$Timeout; + :set CntAdd ($CntAdd + 1); + } do={ + $LogPrint warning $ScriptName ("Failed to add IPv6 address " . $Address . \ + " to list '" . $FwListName . "': " . $Err); + } } } @@ -188,6 +215,6 @@ " - renewed: " . [ $HumanReadableNum $CntRenew 1000 ] . \ " - removed: " . [ $HumanReadableNum $CntRemove 1000 ]); } -} on-error={ - :global ExitError; $ExitError $ExitOK [ :jobname ]; +} do={ + :global ExitError; $ExitError $ExitOK [ :jobname ] $Err; } diff --git a/global-config.rsc b/global-config.rsc index b364a25..86d528a 100644 --- a/global-config.rsc +++ b/global-config.rsc @@ -6,6 +6,12 @@ # global configuration # https://rsc.eworm.de/ +# Warning: Do *NOT* copy this line to overlay! +:global GlobalConfigReady false; +# || ... but +# \||/ start +# \/ here! + # Set this to 'true' to disable news and change notifications. :global NoNewsAndChangesNotification false; @@ -33,6 +39,8 @@ :global TelegramChatId ""; #:global TelegramTokenId "123456:ABCDEF-GHI"; #:global TelegramChatId "12345678"; +# Use this to send notifications to a specific topic in group. +:global TelegramThreadId ""; # Using telegram-chat you have to define trusted chat ids (not group ids!) # or user names. Groups allow to chat with devices simultaneously. #:global TelegramChatIdsTrusted { @@ -61,6 +69,12 @@ :global NtfyServerToken ""; :global NtfyTopic ""; +# You can send Gotify notifications. Configure these settings and +# install the module: +# $ScriptInstallUpdate mod/notification-gotify +:global GotifyServer ""; +:global GotifyToken ""; + # It is possible to override e-mail, Telegram, Matrix and Ntfy setting # for every script. This is done in arrays, where 'Override' is appended # to the variable name, like this: @@ -101,7 +115,7 @@ # cert="ISRG Root X2" }; { url="https://raw.githubusercontent.com/stamparm/ipsum/refs/heads/master/levels/4.txt"; # # higher level (decrease the numerical value) for more addresses, and vice versa - cert="DigiCert Global Root G2" }; + cert="USERTrust RSA Certification Authority" }; { url="https://www.dshield.org/block.txt"; cidr="/24"; cert="ISRG Root X1" }; { url="https://lists.blocklist.de/lists/strongips.txt"; @@ -256,14 +270,20 @@ "cert2-cn"="4n0th3r-s3cr3t"; }; +# /\ Warning: Do *NOT* copy +# /\7\ the code below to overlay! +# /_()_\ Things *will* break! +# # load custom settings from overlay and snippets -# Warning: Do *NOT* copy this code to overlay! :foreach Script in=([ /system/script/find where name="global-config-overlay" ], \ [ /system/script/find where name~"^global-config-overlay.d/" ]) do={ - :do { + :onerror Err { /system/script/run $Script; - } on-error={ + } do={ :log error ("Loading configuration from overlay or snippet " . \ - [ /system/script/get $Script name ] . " failed!"); + [ /system/script/get $Script name ] . " failed: " . $Err); } } + +# signal we are ready +:set GlobalConfigReady true; diff --git a/global-functions.rsc b/global-functions.rsc index 204f824..829cbf2 100644 --- a/global-functions.rsc +++ b/global-functions.rsc @@ -12,9 +12,10 @@ :local ScriptName [ :jobname ]; -# expected configuration version +# Git commit id & info, expected configuration version :global CommitId "unknown"; -:global ExpectedConfigVersion 133; +:global CommitInfo "unknown"; +:global ExpectedConfigVersion 138; # global variables not to be changed by user :global GlobalFunctionsReady false; @@ -37,6 +38,8 @@ :global ExitError; :global FetchHuge; :global FetchUserAgentStr; +:global FileExists; +:global FileGet; :global FormatLine; :global FormatMultiLines; :global GetMacVendor; @@ -54,6 +57,7 @@ :global IsTimeSync; :global LogPrint; :global LogPrintOnce; +:global LogPrintVerbose; :global MAX; :global MIN; :global MkDir; @@ -117,6 +121,11 @@ :return false; } + :if (([ /certificate/settings/get ]->"builtin-trust-anchors") = "trusted" && \ + [[ :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={ @@ -165,8 +174,8 @@ $LogPrint warning $0 ("Failed downloading certificate with CommonName '" . $CommonName . \ "' from repository! Trying fallback to mkcert.org..."); :do { - :if ([ $CertificateAvailable "ISRG Root X1" ] = false) do={ - $LogPrint error $0 ("Downloading required certificate failed."); + :if ([ :len [ /certificate/find where common-name="ISRG Root X1" ] ] = 0) do={ + $LogPrint error $0 ("Required certificate is not available."); :return false; } /tool/fetch check-certificate=yes-without-crl http-header-field=({ [ $FetchUserAgentStr $0 ] }) \ @@ -201,12 +210,19 @@ # name a certificate by its common-name :set CertificateNameByCN do={ - :local CommonName [ :tostr $1 ]; + :local Match [ :tostr $1 ]; :global CleanName; + :global LogPrint; - :local Cert [ /certificate/find where common-name=$CommonName ]; + :local Cert ([ /certificate/find where (common-name=$Match or fingerprint=$Match or name=$Match) ]->0); + :if ([ :len $Cert ] = 0) do={ + $LogPrint warning $0 ("No matching certificate found."); + :return false; + } + :local CommonName [ /certificate/get $Cert common-name ]; /certificate/set $Cert name=[ $CleanName $CommonName ]; + :return true; } # multiply given character(s) @@ -286,6 +302,7 @@ # get readable device info :set DeviceInfo do={ :global CommitId; + :global CommitInfo; :global ExpectedConfigVersion; :global Identity; @@ -307,16 +324,19 @@ ([ $FormatLine "Location" ($Snmp->"location") ] . "\n") ] . \ [ $IfThenElse ([ :len ($Snmp->"contact") ] > 0) \ ([ $FormatLine "Contact" ($Snmp->"contact") ] . "\n") ] . \ - [ $FormatLine "Board name" ($Resource->"board-name") ] . "\n" . \ - [ $FormatLine "Architecture" ($Resource->"architecture-name") ] . "\n" . \ + "Hardware:\n" . \ + [ $FormatLine " Board" ($Resource->"board-name") ] . "\n" . \ + [ $FormatLine " Arch" ($Resource->"architecture-name") ] . "\n" . \ [ $IfThenElse ($RouterBoard->"routerboard" = true) \ - ([ $FormatLine "Model" ($RouterBoard->"model") ] . \ + ([ $FormatLine " Model" ($RouterBoard->"model") ] . \ [ $IfThenElse ([ :len ($RouterBoard->"revision") ] > 0) \ (" " . $RouterBoard->"revision") ] . "\n" . \ - [ $FormatLine "Serial number" ($RouterBoard->"serial-number") ] . "\n") ] . \ - [ $IfThenElse ([ :len ($License->"level") ] > 0) \ - ([ $FormatLine "License" ($License->"level") ] . "\n") ] . \ + [ $FormatLine " Serial" ($RouterBoard->"serial-number") ] . "\n") ] . \ + [ $IfThenElse ([ :len ($License->"nlevel") ] > 0) \ + ([ $FormatLine " License" ("level " . ($License->"nlevel")) ] . "\n") ] . \ "RouterOS:\n" . \ + [ $IfThenElse ([ :len ($License->"level") ] > 0) \ + ([ $FormatLine " License" ("level " . ($License->"level")) ] . "\n") ] . \ [ $FormatLine " Channel" ($Update->"channel") ] . "\n" . \ [ $FormatLine " Installed" ($Update->"installed-version") ] . "\n" . \ [ $IfThenElse ([ :typeof ($Update->"latest-version") ] != "nothing" && \ @@ -327,7 +347,7 @@ ([ $FormatLine " Firmware" ($RouterBoard->"current-firmware") ] . "\n") ] . \ "RouterOS-Scripts:\n" . \ [ $IfThenElse ($CommitId != "unknown") \ - ([ $FormatLine " Commit" [ :pick $CommitId 0 8 ] ] . "\n") ] . \ + ([ $FormatLine " Commit" ($CommitInfo . "/" . [ :pick $CommitId 0 8 ]) ] . "\n") ] . \ [ $FormatLine " Version" $ExpectedConfigVersion ]); } @@ -345,6 +365,7 @@ :global CertificateAvailable; :global CleanFilePath; + :global FileExists; :global LogPrint; :global MkDir; :global RmFile; @@ -365,7 +386,7 @@ :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; } @@ -378,25 +399,22 @@ :local Url ("https://upgrade.mikrotik.com/routeros/" . $PkgVer . "/" . $PkgFile); $LogPrint info $0 ("Downloading package file '" . $PkgName . "'..."); $LogPrint debug $0 ("... from url: " . $Url); - :local Retry 3; - :while ($Retry > 0) do={ - :do { - /tool/fetch check-certificate=yes-without-crl $Url dst-path=$PkgDest; - $WaitForFile $PkgDest; - :if ([ /file/get [ find where name=$PkgDest ] type ] = "package") do={ - :return true; - } - } on-error={ - $LogPrint debug $0 ("Downloading package file failed."); - } + :onerror Err { + /tool/fetch check-certificate=yes-without-crl $Url dst-path=$PkgDest; + $WaitForFile $PkgDest; + } do={ + $LogPrint warning $0 ("Downloading package file '" . $PkgName . "' failed: " . $Err); + :return false; + } + :if ([ $FileExists $PkgDest "package" ] = false) do={ + $LogPrint warning $0 ("Downloaded file is not a package, removing."); $RmFile $PkgDest; - :set Retry ($Retry - 1); + :return false; } - $LogPrint warning $0 ("Downloading package file '" . $PkgName . "' failed."); - :return false; + :return true; } # return either first (if "true") or second @@ -439,13 +457,15 @@ :set ExitError do={ :local ExitOK [ :tostr $1 ]; :local Name [ :tostr $2 ]; + :local Error [ :tostr $3 ]; :global IfThenElse; :global LogPrint; :if ($ExitOK = "false") do={ $LogPrint error $Name ([ $IfThenElse ([ :pick $Name 0 1 ] = "\$") \ - "Function" "Script" ] . " '" . $Name . "' exited with error."); + "Function" "Script" ] . " '" . $Name . "' exited with error" . \ + [ $IfThenElse (!($Error ~ "^(|true|false)\$")) (": " . $Error) "." ]); } } @@ -474,14 +494,14 @@ } :local FileName ($DirName . "/" . [ $CleanName $0 ] . "-" . [ $GetRandom20CharAlNum ]); - :do { + :onerror Err { /tool/fetch check-certificate=$CheckCert $Url dst-path=$FileName \ http-header-field=({ [ $FetchUserAgentStr $ScriptName ] }) as-value; - } on-error={ + } do={ :if ([ $WaitForFile $FileName 500ms ] = true) do={ $RmFile $FileName; } - $LogPrint debug $0 ("Failed downloading from: " . $Url); + $LogPrint debug $0 ("Failed downloading from " . $Url . " - " . $Err); $RmDir $DirName; :return false; } @@ -512,6 +532,47 @@ $Resource->"architecture-name" . " " . $Caller . "/Fetch (https://rsc.eworm.de/)"); } +# 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 :set FormatLine do={ :local Key [ :tostr $1 ]; @@ -574,12 +635,12 @@ ("https://api.macvendors.com/" . [ :pick $Mac 0 8 ]) output=user as-value ]->"data"); :return $Vendor; } on-error={ - :do { + :onerror Err { /tool/fetch check-certificate=yes-without-crl ("https://api.macvendors.com/") \ output=none as-value; $LogPrint debug $0 ("The mac vendor is not known in database."); - } on-error={ - $LogPrint warning $0 ("Failed getting mac vendor."); + } do={ + $LogPrint warning $0 ("Failed getting mac vendor: " . $Err); } :return "unknown vendor"; } @@ -843,6 +904,9 @@ :return true; } +# The function $LogPrintVerbose is declared, but has no code, intentionally. +# https://rsc.eworm.de/DEBUG.md#verbose-output + # get max value :set MAX do={ :if ($1 > $2) do={ :return $1; } @@ -860,6 +924,7 @@ :local Path [ :tostr $1 ]; :global CleanFilePath; + :global FileGet; :global LogPrint; :global RmDir; :global WaitForFile; @@ -879,11 +944,11 @@ $LogPrint info $0 ("Creating disk of type tmpfs."); $RmDir "tmpfs"; - :do { + :onerror Err { /disk/add slot=tmpfs type=tmpfs tmpfs-max-size=([ /system/resource/get total-memory ] / 3); $WaitForFile "tmpfs"; - } on-error={ - $LogPrint warning $0 ("Creating disk of type tmpfs failed!"); + } do={ + $LogPrint warning $0 ("Creating disk of type tmpfs failed: " . $Err); :return false; } :return true; @@ -897,7 +962,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; } @@ -908,11 +974,11 @@ } } - :do { + :onerror Err { /file/add type="directory" name=$Path; $WaitForFile $Path; - } on-error={ - $LogPrint warning $0 ("Making directory '" . $Path . "' failed!"); + } do={ + $LogPrint warning $0 ("Making directory '" . $Path . "' failed: " . $Err); :return false; } @@ -1022,25 +1088,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; } - :do { - /file/remove $Dir; - } on-error={ - $LogPrint error $0 ("Removing directory '" . $DirName . "' (" . $Dir . ") failed."); + :if ($DirVal->"type" != "directory") do={ + $LogPrint error $0 ("Directory '" . $DirName . "' is not a directory."); + :return false; + } + + :onerror Err { + /file/remove $DirName; + } do={ + $LogPrint error $0 ("Removing directory '" . $DirName . "' failed: " . $Err); :return false; } :return true; @@ -1050,25 +1117,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!=file ] ] > 0) do={ - $LogPrint error $0 ("File '" . $FileName . "' is not a file."); - :return false; - } - - :local File [ /file/find where name=$FileName type=file ]; - :if ([ :len $File ] = 0) do={ + :local FileVal [ $FileGet $FileName ]; + :if ($FileVal = false) do={ $LogPrint debug $0 ("... which does not exist."); :return true; } - :do { - /file/remove $File; - } on-error={ - $LogPrint error $0 ("Removing file '" . $FileName . "' (" . $File . ") failed."); + :if ($FileVal->"type" = "directory" || $FileVal->"type" = "disk") do={ + $LogPrint error $0 ("File '" . $FileName . "' is not a file."); + :return false; + } + + :onerror Err { + /file/remove $FileName; + } do={ + $LogPrint error $0 ("Removing file '" . $FileName . "' failed: " . $Err); :return false; } :return true; @@ -1101,12 +1169,15 @@ } # install new scripts, update existing scripts -:set ScriptInstallUpdate do={ :do { +:set ScriptInstallUpdate do={ :onerror Err { :local Scripts [ :toarray $1 ]; :local NewComment [ :tostr $2 ]; :global CommitId; + :global CommitInfo; :global ExpectedConfigVersion; + :global GlobalConfigReady; + :global GlobalFunctionsReady; :global Identity; :global IDonate; :global NoNewsAndChangesNotification; @@ -1140,10 +1211,17 @@ :local CommitIdBefore $CommitId; :local ExpectedConfigVersionBefore $ExpectedConfigVersion; - :local ReloadGlobalFunctions false; - :local ReloadGlobalConfig false; + :local ReloadGlobal false; :local DeviceMode [ /system/device-mode/get ]; + :local CheckSums ({}); + :do { + :local Url ($ScriptUpdatesBaseUrl . "checksums.json" . $ScriptUpdatesUrlSuffix); + $LogPrint debug $0 ("Fetching checksums from url: " . $Url); + :set CheckSums [ :deserialize from=json ([ /tool/fetch check-certificate=yes-without-crl \ + http-header-field=({ [ $FetchUserAgentStr $0 ] }) $Url output=user as-value ]->"data") ]; + } on-error={ } + :foreach Script in=[ /system/script/find where source~"^#!rsc by RouterOS\r?\n" ] do={ :local ScriptVal [ /system/script/get $Script ]; :local ScriptInfo [ $ParseKeyValueStore ($ScriptVal->"comment") ]; @@ -1157,8 +1235,26 @@ } } - :if (!($ScriptInfo->"ignore" = true)) do={ - :do { + :do { + :if ($ScriptInfo->"ignore" = true) do={ + $LogPrint debug $0 ("Ignoring script '" . $ScriptVal->"name" . "', as requested."); + :error true; + } + + :local CheckSum ($CheckSums->($ScriptVal->"name")); + :if ([ :len ($ScriptInfo->"base-url") ] = 0 && [ :len ($ScriptInfo->"url-suffix") ] = 0 && \ + [ :convert transform=md5 to=hex [ :tolf ($ScriptVal->"source") ] ] = $CheckSum) do={ + $LogPrint debug $0 ("Checksum for script '" . $ScriptVal->"name" . "' matches, ignoring."); + :error true; + } + + :if ([ :len ($ScriptInfo->"certificate") ] > 0) do={ + :if ([ $CertificateAvailable ($ScriptInfo->"certificate") ] = false) do={ + $LogPrint warning $0 ("Downloading certificate failed, trying without."); + } + } + + :onerror Err { :local BaseUrl [ $EitherOr ($ScriptInfo->"base-url") $ScriptUpdatesBaseUrl ]; :local UrlSuffix [ $EitherOr ($ScriptInfo->"url-suffix") $ScriptUpdatesUrlSuffix ]; :local Url ($BaseUrl . $ScriptVal->"name" . ".rsc" . $UrlSuffix); @@ -1168,18 +1264,15 @@ :if ($Result->"status" = "finished") do={ :set SourceNew [ :tolf ($Result->"data") ]; } - } on-error={ + } do={ + $LogPrint warning $0 ("Failed fetching script '" . $ScriptVal->"name" . "': " . $Err); :if ($ScriptVal->"source" = "#!rsc by RouterOS\n") do={ - $LogPrint warning $0 ("Failed fetching script '" . $ScriptVal->"name" . \ - "', removing dummy. Typo on installation?"); + $LogPrint warning $0 ("Removing dummy. Typo on installation?"); /system/script/remove $Script; - } else={ - $LogPrint warning $0 ("Failed fetching script '" . $ScriptVal->"name" . "'!"); } + :error false; } - } - :do { :if ([ :len $SourceNew ] = 0) do={ $LogPrint debug $0 ("No update for script '" . $ScriptVal->"name" . "'."); :error false; @@ -1225,36 +1318,30 @@ $LogPrint info $0 ("Updating script: " . $ScriptVal->"name"); /system/script/set owner=($ScriptVal->"name") \ source=[ $IfThenElse ($ScriptUpdatesCRLF = true) $SourceCRLF $SourceNew ] $Script; - :if ($ScriptVal->"name" = "global-config") do={ - :set ReloadGlobalConfig true; - } - :if ($ScriptVal->"name" = "global-functions" || $ScriptVal->"name" ~ ("^mod/.")) do={ - :set ReloadGlobalFunctions true; + :if ($ScriptVal->"name" = "global-config" || \ + $ScriptVal->"name" = "global-functions" || \ + $ScriptVal->"name" ~ ("^mod/.")) do={ + :set ReloadGlobal true; } } on-error={ } } - :if ($ReloadGlobalFunctions = true) do={ - $LogPrint info $0 ("Reloading global functions."); - :do { - /system/script/run global-functions; - } on-error={ - $LogPrint error $0 ("Reloading global functions failed!"); - } - } + :if ($ReloadGlobal = true) do={ + $LogPrint info $0 ("Reloading global configuration and functions."); + :set GlobalConfigReady false; + :set GlobalFunctionsReady false; + :delay 1s; - :if ($ReloadGlobalConfig = true) do={ - $LogPrint info $0 ("Reloading global configuration."); - :do { + :onerror Err { /system/script/run global-config; - } on-error={ - $LogPrint error $0 ("Reloading global configuration failed!" . \ - " Syntax error or missing overlay?"); + /system/script/run global-functions; + } do={ + $LogPrint error $0 ("Reloading global configuration and functions failed! " . $Err); } } :if ($CommitId != "unknown" && $CommitIdBefore != $CommitId) do={ - $LogPrint info $0 ("Updated to commit id: " . [ :pick $CommitId 0 8 ]); + $LogPrint info $0 ("Updated to commit: " . $CommitInfo . "/" . [ :pick $CommitId 0 8 ]); } :if ($ExpectedConfigVersionBefore > $ExpectedConfigVersion) do={ @@ -1268,7 +1355,7 @@ :global GlobalConfigMigration; :local ChangeLogCode; - :do { + :onerror Err { :local Url ($ScriptUpdatesBaseUrl . "news-and-changes.rsc" . $ScriptUpdatesUrlSuffix); $LogPrint debug $0 ("Fetching news, changes and migration: " . $Url); :local Result [ /tool/fetch check-certificate=yes-without-crl \ @@ -1276,16 +1363,16 @@ :if ($Result->"status" = "finished") do={ :set ChangeLogCode ($Result->"data"); } - } on-error={ - $LogPrint warning $0 ("Failed fetching news, changes and migration!"); + } do={ + $LogPrint warning $0 ("Failed fetching news, changes and migration: " . $Err); } :if ([ :len $ChangeLogCode ] > 0) do={ :if ([ $ValidateSyntax $ChangeLogCode ] = true) do={ - :do { + :onerror Err { [ :parse $ChangeLogCode ]; - } on-error={ - $LogPrint warning $0 ("The changelog failed to run!"); + } do={ + $LogPrint warning $0 ("The changelog failed to run: " . $Err); } } else={ $LogPrint warning $0 ("The changelog failed syntax validation!"); @@ -1307,10 +1394,10 @@ } $LogPrint info $0 ("Applying migration for change " . $I . ": " . $Migration); - :do { + :onerror Err { [ :parse $Migration ]; - } on-error={ - $LogPrint warning $0 ("Migration code for change " . $I . " failed to run!"); + } do={ + $LogPrint warning $0 ("Migration code for change " . $I . " failed to run: " . $Err); } } on-error={ } } @@ -1352,14 +1439,14 @@ :set GlobalConfigChanges; :set GlobalConfigMigration; } -} on-error={ - :global ExitError; $ExitError false $0; +} do={ + :global ExitError; $ExitError false $0 $Err; } } # lock script against multiple invocation :set ScriptLock do={ - :local Script [ :tostr $1 ]; - :local WaitMax ([ :tonum $3 ] * 10); + :local Script [ :tostr $1 ]; + :local WaitMax [ :totime $2 ]; :global GetRandom20CharAlNum; :global IfThenElse; @@ -1448,6 +1535,10 @@ :set ($ScriptLockOrder->$Script) ({}); } + :if ([ :typeof $WaitMax ] = "nil" ) do={ + :set WaitMax 0s; + } + :if ([ :len [ /system/script/find where name=$Script ] ] = 0) do={ $LogPrint error $0 ("A script named '" . $Script . "' does not exist!"); :error false; @@ -1467,12 +1558,13 @@ :local MyTicket [ $GetRandom20CharAlNum 6 ]; $AddTicket $Script $MyTicket; - :local WaitCount 0; - :while ($WaitMax > $WaitCount && \ + :local WaitInterval ($WaitMax / 20); + :local WaitTime $WaitMax; + :while ($WaitTime > 0 && \ ([ $IsFirstTicket $Script $MyTicket ] = false || \ [ $TicketCount $Script ] < [ $JobCount $Script ])) do={ - :set WaitCount ($WaitCount + 1); - :delay 100ms; + :set WaitTime ($WaitTime - $WaitInterval); + :delay $WaitInterval; } :if ([ $IsFirstTicket $Script $MyTicket ] = true && \ @@ -1484,17 +1576,17 @@ $RemoveTicket $Script $MyTicket; $LogPrint debug $0 ("Script '" . $Script . "' started more than once" . \ - [ $IfThenElse ($WaitCount > 0) " and timed out waiting for lock" "" ] . "..."); + [ $IfThenElse ($WaitTime < $WaitMax) " and timed out waiting for lock" "" ] . "..."); :return false; } # send notification via NotificationFunctions - expects at least two string arguments -:set SendNotification do={ :do { +:set SendNotification do={ :onerror Err { :global SendNotification2; $SendNotification2 ({ origin=$0; subject=$1; message=$2; link=$3; silent=$4 }); -} on-error={ - :global ExitError; $ExitError false $0; +} do={ + :global ExitError; $ExitError false $0 $Err; } } # send notification via NotificationFunctions - expects one array argument @@ -1613,9 +1705,12 @@ :set ValidateSyntax do={ :local Code [ :tostr $1 ]; - :do { + :global LogPrint; + + :onerror Err { [ :parse (":local Validate do={\n" . $Code . "\n}") ]; - } on-error={ + } do={ + $LogPrint debug $0 ("Valdation failed: " . $Err); :return false; } :return true; @@ -1679,29 +1774,26 @@ :global CleanFilePath; :global EitherOr; - :global LogPrintOnce; :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 { + :if ([ :len [ /file/find where name=$FileName ] ] = 0) do={ + :error false; + } + } delay=$Delay max=10; + } on-error={ + :return false; } :while ([ :len [ /file/find where name=$FileName ] ] > 0) do={ :do { /file/get $FileName; :return true; - } on-error={ - $LogPrintOnce warning $0 \ - ("Hit the infamous file handling breakage (SUP-179200) introduced with RouterOS 7.18beta2..."); - } + } on-error={ } :delay $Delay; :set Delay ($Delay * 3 / 2); } @@ -1733,10 +1825,10 @@ :foreach Script in=[ /system/script/find where name ~ "^mod/." ] do={ :local ScriptVal [ /system/script/get $Script ]; :if ([ $ValidateSyntax ($ScriptVal->"source") ] = true) do={ - :do { + :onerror Err { /system/script/run $Script; - } on-error={ - $LogPrint error $0 ("Module '" . $ScriptVal->"name" . "' failed to run."); + } do={ + $LogPrint error $0 ("Module '" . $ScriptVal->"name" . "' failed to run: " . $Err); } } else={ $LogPrint error $0 ("Module '" . $ScriptVal->"name" . "' failed syntax validation, skipping."); diff --git a/global-wait.rsc b/global-wait.rsc index ca3fc0c..23b5629 100644 --- a/global-wait.rsc +++ b/global-wait.rsc @@ -8,5 +8,6 @@ # wait for global-functions to finish # https://rsc.eworm.de/doc/global-wait.md +:global GlobalConfigReady; :global GlobalFunctionsReady; -:while ($GlobalFunctionsReady != true) do={ :delay 500ms; } +:while ($GlobalConfigReady != true || $GlobalFunctionsReady != true) do={ :delay 500ms; } diff --git a/gps-track.rsc b/gps-track.rsc index dea56d2..6a090bf 100644 --- a/gps-track.rsc +++ b/gps-track.rsc @@ -9,11 +9,11 @@ # track gps data by sending json data to http server # https://rsc.eworm.de/doc/gps-track.md -:global GlobalFunctionsReady; -:while ($GlobalFunctionsReady != true) do={ :delay 500ms; } - :local ExitOK false; -:do { +:onerror Err { + :global GlobalConfigReady; :global GlobalFunctionsReady; + :retry { :if ($GlobalConfigReady != true || $GlobalFunctionsReady != true) \ + do={ :error ("Global config and/or functions not ready."); }; } delay=500ms max=50; :local ScriptName [ :jobname ]; :global GpsTrackUrl; @@ -34,7 +34,7 @@ :local Gps [ /system/gps/monitor once as-value ]; :if ($Gps->"valid" = true) do={ - :do { + :onerror Err { /tool/fetch check-certificate=yes-without-crl output=none http-method=post \ http-header-field=({ [ $FetchUserAgentStr $ScriptName ]; "Content-Type: application/json" }) \ http-data=[ :serialize to=json { "identity"=$Identity; \ @@ -42,12 +42,12 @@ $LogPrint debug $ScriptName ("Sending GPS data in " . $CoordinateFormat . " format: " . \ "lat: " . ($Gps->"latitude") . " " . \ "lon: " . ($Gps->"longitude")); - } on-error={ - $LogPrint warning $ScriptName ("Failed sending GPS data!"); + } do={ + $LogPrint warning $ScriptName ("Failed sending GPS data: " . $Err); } } else={ $LogPrint debug $ScriptName ("GPS data not valid."); } -} on-error={ - :global ExitError; $ExitError $ExitOK [ :jobname ]; +} do={ + :global ExitError; $ExitError $ExitOK [ :jobname ] $Err; } diff --git a/hotspot-to-wpa-cleanup.capsman.rsc b/hotspot-to-wpa-cleanup.capsman.rsc index 033d0e7..e4ac967 100644 --- a/hotspot-to-wpa-cleanup.capsman.rsc +++ b/hotspot-to-wpa-cleanup.capsman.rsc @@ -12,11 +12,11 @@ # # !! Do not edit this file, it is generated from template! -:global GlobalFunctionsReady; -:while ($GlobalFunctionsReady != true) do={ :delay 500ms; } - :local ExitOK false; -:do { +:onerror Err { + :global GlobalConfigReady; :global GlobalFunctionsReady; + :retry { :if ($GlobalConfigReady != true || $GlobalFunctionsReady != true) \ + do={ :error ("Global config and/or functions not ready."); }; } delay=500ms max=50; :local ScriptName [ :jobname ]; :global EitherOr; @@ -75,6 +75,6 @@ /ip/dhcp-server/lease/remove $Lease; } } -} on-error={ - :global ExitError; $ExitError $ExitOK [ :jobname ]; +} do={ + :global ExitError; $ExitError $ExitOK [ :jobname ] $Err; } diff --git a/hotspot-to-wpa-cleanup.template.rsc b/hotspot-to-wpa-cleanup.template.rsc index 0f8c490..d51e1d0 100644 --- a/hotspot-to-wpa-cleanup.template.rsc +++ b/hotspot-to-wpa-cleanup.template.rsc @@ -13,11 +13,11 @@ # !! This is just a template to generate the real script! # !! Pattern '%TEMPL%' is replaced, paths are filtered. -:global GlobalFunctionsReady; -:while ($GlobalFunctionsReady != true) do={ :delay 500ms; } - :local ExitOK false; -:do { +:onerror Err { + :global GlobalConfigReady; :global GlobalFunctionsReady; + :retry { :if ($GlobalConfigReady != true || $GlobalFunctionsReady != true) \ + do={ :error ("Global config and/or functions not ready."); }; } delay=500ms max=50; :local ScriptName [ :jobname ]; :global EitherOr; @@ -82,6 +82,6 @@ /ip/dhcp-server/lease/remove $Lease; } } -} on-error={ - :global ExitError; $ExitError $ExitOK [ :jobname ]; +} do={ + :global ExitError; $ExitError $ExitOK [ :jobname ] $Err; } diff --git a/hotspot-to-wpa-cleanup.wifi.rsc b/hotspot-to-wpa-cleanup.wifi.rsc index dfec697..8bb2631 100644 --- a/hotspot-to-wpa-cleanup.wifi.rsc +++ b/hotspot-to-wpa-cleanup.wifi.rsc @@ -12,11 +12,11 @@ # # !! Do not edit this file, it is generated from template! -:global GlobalFunctionsReady; -:while ($GlobalFunctionsReady != true) do={ :delay 500ms; } - :local ExitOK false; -:do { +:onerror Err { + :global GlobalConfigReady; :global GlobalFunctionsReady; + :retry { :if ($GlobalConfigReady != true || $GlobalFunctionsReady != true) \ + do={ :error ("Global config and/or functions not ready."); }; } delay=500ms max=50; :local ScriptName [ :jobname ]; :global EitherOr; @@ -75,6 +75,6 @@ /ip/dhcp-server/lease/remove $Lease; } } -} on-error={ - :global ExitError; $ExitError $ExitOK [ :jobname ]; +} do={ + :global ExitError; $ExitError $ExitOK [ :jobname ] $Err; } diff --git a/hotspot-to-wpa.capsman.rsc b/hotspot-to-wpa.capsman.rsc index 3f51475..8977cee 100644 --- a/hotspot-to-wpa.capsman.rsc +++ b/hotspot-to-wpa.capsman.rsc @@ -11,11 +11,11 @@ # # !! Do not edit this file, it is generated from template! -:global GlobalFunctionsReady; -:while ($GlobalFunctionsReady != true) do={ :delay 500ms; } - :local ExitOK false; -:do { +:onerror Err { + :global GlobalConfigReady; :global GlobalFunctionsReady; + :retry { :if ($GlobalConfigReady != true || $GlobalFunctionsReady != true) \ + do={ :error ("Global config and/or functions not ready."); }; } delay=500ms max=50; :local ScriptName [ :jobname ]; :global EitherOr; @@ -100,6 +100,6 @@ :delay 2s; /caps-man/access-list/set $Entry action=accept; -} on-error={ - :global ExitError; $ExitError $ExitOK [ :jobname ]; +} do={ + :global ExitError; $ExitError $ExitOK [ :jobname ] $Err; } diff --git a/hotspot-to-wpa.template.rsc b/hotspot-to-wpa.template.rsc index 068241d..c5d977d 100644 --- a/hotspot-to-wpa.template.rsc +++ b/hotspot-to-wpa.template.rsc @@ -12,11 +12,11 @@ # !! This is just a template to generate the real script! # !! Pattern '%TEMPL%' is replaced, paths are filtered. -:global GlobalFunctionsReady; -:while ($GlobalFunctionsReady != true) do={ :delay 500ms; } - :local ExitOK false; -:do { +:onerror Err { + :global GlobalConfigReady; :global GlobalFunctionsReady; + :retry { :if ($GlobalConfigReady != true || $GlobalFunctionsReady != true) \ + do={ :error ("Global config and/or functions not ready."); }; } delay=500ms max=50; :local ScriptName [ :jobname ]; :global EitherOr; @@ -120,6 +120,6 @@ :delay 2s; /caps-man/access-list/set $Entry action=accept; /interface/wifi/access-list/set $Entry action=accept; -} on-error={ - :global ExitError; $ExitError $ExitOK [ :jobname ]; +} do={ + :global ExitError; $ExitError $ExitOK [ :jobname ] $Err; } diff --git a/hotspot-to-wpa.wifi.rsc b/hotspot-to-wpa.wifi.rsc index cc5e2fc..6a97e46 100644 --- a/hotspot-to-wpa.wifi.rsc +++ b/hotspot-to-wpa.wifi.rsc @@ -11,11 +11,11 @@ # # !! Do not edit this file, it is generated from template! -:global GlobalFunctionsReady; -:while ($GlobalFunctionsReady != true) do={ :delay 500ms; } - :local ExitOK false; -:do { +:onerror Err { + :global GlobalConfigReady; :global GlobalFunctionsReady; + :retry { :if ($GlobalConfigReady != true || $GlobalFunctionsReady != true) \ + do={ :error ("Global config and/or functions not ready."); }; } delay=500ms max=50; :local ScriptName [ :jobname ]; :global EitherOr; @@ -97,6 +97,6 @@ :delay 2s; /interface/wifi/access-list/set $Entry action=accept; -} on-error={ - :global ExitError; $ExitError $ExitOK [ :jobname ]; +} do={ + :global ExitError; $ExitError $ExitOK [ :jobname ] $Err; } diff --git a/ipsec-to-dns.rsc b/ipsec-to-dns.rsc index 26dab0a..1b5ed13 100644 --- a/ipsec-to-dns.rsc +++ b/ipsec-to-dns.rsc @@ -9,11 +9,11 @@ # and add/remove/update DNS entries from IPSec mode-config # https://rsc.eworm.de/doc/ipsec-to-dns.md -:global GlobalFunctionsReady; -:while ($GlobalFunctionsReady != true) do={ :delay 500ms; } - :local ExitOK false; -:do { +:onerror Err { + :global GlobalConfigReady; :global GlobalFunctionsReady; + :retry { :if ($GlobalConfigReady != true || $GlobalFunctionsReady != true) \ + do={ :error ("Global config and/or functions not ready."); }; } delay=500ms max=50; :local ScriptName [ :jobname ]; :global Domain; @@ -79,6 +79,6 @@ /ip/dns/static/add name=$Fqdn address=($PeerVal->"dynamic-address") ttl=$Ttl comment=$Comment place-before=$PlaceBefore; } } -} on-error={ - :global ExitError; $ExitError $ExitOK [ :jobname ]; +} do={ + :global ExitError; $ExitError $ExitOK [ :jobname ] $Err; } diff --git a/ipv6-update.rsc b/ipv6-update.rsc index c6e3d65..580a426 100644 --- a/ipv6-update.rsc +++ b/ipv6-update.rsc @@ -8,11 +8,11 @@ # update firewall and dns settings on IPv6 prefix change # https://rsc.eworm.de/doc/ipv6-update.md -:global GlobalFunctionsReady; -:while ($GlobalFunctionsReady != true) do={ :delay 500ms; } - :local ExitOK false; -:do { +:onerror Err { + :global GlobalConfigReady; :global GlobalFunctionsReady; + :retry { :if ($GlobalConfigReady != true || $GlobalFunctionsReady != true) \ + do={ :error ("Global config and/or functions not ready."); }; } delay=500ms max=50; :local ScriptName [ :jobname ]; :global LogPrint; @@ -20,7 +20,9 @@ :global ScriptLock; :local NaAddress $"na-address"; + :local NaValid $"na-valid"; :local PdPrefix $"pd-prefix"; + :local PdValid $"pd-valid"; :if ([ $ScriptLock $ScriptName ] = false) do={ :set ExitOK true; @@ -33,12 +35,18 @@ :error false; } - :if ([ :typeof $PdPrefix ] = "nothing") do={ + :if ([ :typeof $PdPrefix ] = "nothing" || [ :typeof $PdValid ] = "nothing") do={ $LogPrint error $ScriptName ("This script is supposed to run from ipv6 dhcp-client."); :set ExitOK true; :error false; } + :if ($PdValid != 1) do={ + $LogPrint info $ScriptName ("The prefix " . $PdPrefix . " is no longer valid. Ignoring."); + :set ExitOK true; + :error false; + } + :local Pool [ /ipv6/pool/get [ find where prefix=$PdPrefix ] name ]; :if ([ :len [ /ipv6/firewall/address-list/find where comment=("ipv6-pool-" . $Pool) ] ] = 0) do={ /ipv6/firewall/address-list/add list=("ipv6-pool-" . $Pool) address=:: comment=("ipv6-pool-" . $Pool) dynamic=yes; @@ -94,6 +102,6 @@ } } } -} on-error={ - :global ExitError; $ExitError $ExitOK [ :jobname ]; +} do={ + :global ExitError; $ExitError $ExitOK [ :jobname ] $Err; } diff --git a/lease-script.rsc b/lease-script.rsc index bf27fda..ab44956 100644 --- a/lease-script.rsc +++ b/lease-script.rsc @@ -8,11 +8,11 @@ # run scripts on DHCP lease # https://rsc.eworm.de/doc/lease-script.md -:global GlobalFunctionsReady; -:while ($GlobalFunctionsReady != true) do={ :delay 500ms; } - :local ExitOK false; -:do { +:onerror Err { + :global GlobalConfigReady; :global GlobalFunctionsReady; + :retry { :if ($GlobalConfigReady != true || $GlobalFunctionsReady != true) \ + do={ :error ("Global config and/or functions not ready."); }; } delay=500ms max=50; :local ScriptName [ :jobname ]; :global Grep; @@ -53,13 +53,13 @@ } :foreach Order,Script in=$RunOrder do={ - :do { + :onerror Err { $LogPrint debug $ScriptName ("Running script with order " . $Order . ": " . $Script); /system/script/run $Script; - } on-error={ - $LogPrint warning $ScriptName ("Running script '" . $Script . "' failed!"); + } do={ + $LogPrint warning $ScriptName ("Running script '" . $Script . "' failed: " . $Err); } } -} on-error={ - :global ExitError; $ExitError $ExitOK [ :jobname ]; +} do={ + :global ExitError; $ExitError $ExitOK [ :jobname ] $Err; } diff --git a/log-forward.rsc b/log-forward.rsc index afeb3f2..be7eff7 100644 --- a/log-forward.rsc +++ b/log-forward.rsc @@ -8,11 +8,11 @@ # forward log messages via notification # https://rsc.eworm.de/doc/log-forward.md -:global GlobalFunctionsReady; -:while ($GlobalFunctionsReady != true) do={ :delay 500ms; } - :local ExitOK false; -:do { +:onerror Err { + :global GlobalConfigReady; :global GlobalFunctionsReady; + :retry { :if ($GlobalConfigReady != true || $GlobalFunctionsReady != true) \ + do={ :error ("Global config and/or functions not ready."); }; } delay=500ms max=50; :local ScriptName [ :jobname ]; :global Identity; @@ -108,6 +108,6 @@ :local LogAll [ /log/find ]; :set LogForwardLast ($LogAll->([ :len $LogAll ] - 1) ); -} on-error={ - :global ExitError; $ExitError $ExitOK [ :jobname ]; +} do={ + :global ExitError; $ExitError $ExitOK [ :jobname ] $Err; } Binary files differBinary files differdiff --git a/mod/bridge-port-to.rsc b/mod/bridge-port-to.rsc index 39a036e..93eedce 100644 --- a/mod/bridge-port-to.rsc +++ b/mod/bridge-port-to.rsc @@ -10,7 +10,7 @@ :global BridgePortTo; -:set BridgePortTo do={ :do { +:set BridgePortTo do={ :onerror Err { :local BridgePortTo [ :tostr $1 ]; :global IfThenElse; @@ -65,6 +65,6 @@ $LogPrint info $0 ("Re-enabling interfaces..."); /interface/ethernet/enable $InterfaceReEnable; } -} on-error={ - :global ExitError; $ExitError false $0; +} do={ + :global ExitError; $ExitError false $0 $Err; } } diff --git a/mod/bridge-port-vlan.rsc b/mod/bridge-port-vlan.rsc index 0eeb9b5..6deee99 100644 --- a/mod/bridge-port-vlan.rsc +++ b/mod/bridge-port-vlan.rsc @@ -10,7 +10,7 @@ :global BridgePortVlan; -:global BridgePortVlan do={ :do { +:global BridgePortVlan do={ :onerror Err { :local ConfigTo [ :tostr $1 ]; :global IfThenElse; @@ -74,6 +74,6 @@ $LogPrint info $0 ("Re-enabling interfaces..."); /interface/ethernet/enable $InterfaceReEnable; } -} on-error={ - :global ExitError; $ExitError false $0; +} do={ + :global ExitError; $ExitError false $0 $Err; } } diff --git a/mod/inspectvar.rsc b/mod/inspectvar.rsc index c861557..fc1b366 100644 --- a/mod/inspectvar.rsc +++ b/mod/inspectvar.rsc @@ -12,12 +12,12 @@ :global InspectVarReturn; # inspect variable and print on terminal -:set InspectVar do={ :do { +:set InspectVar do={ :onerror Err { :global InspectVarReturn; :put [ :tocrlf [ $InspectVarReturn $1 ] ]; -} on-error={ - :global ExitError; $ExitError false $0; +} do={ + :global ExitError; $ExitError false $0 $Err; } } # inspect variable and return formatted string @@ -25,6 +25,7 @@ :local Input $1; :local Level (0 + [ :tonum $2 ]); + :global CharacterReplace; :global IfThenElse; :global InspectVarReturn; @@ -33,14 +34,13 @@ :local Value [ :tostr $2 ]; :local Level [ :tonum $3 ]; - :local Indent ""; - :for I from=1 to=$Level step=1 do={ - :set Indent ($Indent . " "); - } - :return ($Indent . "-" . $Prefix . "-> " . $Value); + :global CharacterMultiply; + + :return ([ $CharacterMultiply " " $Level ] . "-" . $Prefix . "-> " . $Value); } :local TypeOf [ :typeof $Input ]; + :local Len [ :len $Input ]; :local Return [ $IndentReturn "type" $TypeOf $Level ]; :if ($TypeOf = "array") do={ @@ -50,6 +50,16 @@ [ $InspectVarReturn $Value ($Level + 2) ]); } } else={ + :if ($TypeOf = "str") do={ + :set $Return ($Return . "\n" . \ + [ $IndentReturn "len" $Len $Level ]); + :if ([ :typeof [ :find $Input ("\r") ] ] = "num") do={ + :set Input [ $CharacterReplace $Input ("\r") "" ]; + } + :if ([ :typeof [ :find $Input ("\n") ] ] = "num") do={ + :set Input [ $CharacterReplace $Input ("\n") " " ]; + } + } :if ($TypeOf != "nothing") do={ :set $Return ($Return . "\n" . \ [ $IndentReturn "value" [ $IfThenElse ([ :len $Input ] > 80) \ diff --git a/mod/ipcalc.rsc b/mod/ipcalc.rsc index 477cf4a..eacff6d 100644 --- a/mod/ipcalc.rsc +++ b/mod/ipcalc.rsc @@ -12,7 +12,7 @@ :global IPCalcReturn; # print netmask, network, min host, max host and broadcast -:set IPCalc do={ :do { +:set IPCalc do={ :onerror Err { :local Input [ :tostr $1 ]; :global FormatLine; @@ -27,8 +27,8 @@ [ $FormatLine "HostMin" ($Values->"hostmin") ] . "\n" . \ [ $FormatLine "HostMax" ($Values->"hostmax") ] . "\n" . \ [ $FormatLine "Broadcast" ($Values->"broadcast") ]) ]; -} on-error={ - :global ExitError; $ExitError false $0; +} do={ + :global ExitError; $ExitError false $0 $Err; } } # calculate and return netmask, network, min host, max host and broadcast diff --git a/mod/notification-email.rsc b/mod/notification-email.rsc index 7b89d98..ad9762a 100644 --- a/mod/notification-email.rsc +++ b/mod/notification-email.rsc @@ -35,14 +35,16 @@ } # flush e-mail queue -:set FlushEmailQueue do={ :do { +:set FlushEmailQueue do={ :onerror Err { :global EmailQueue; :global EitherOr; :global EMailGenerateFrom; + :global FileExists; :global IsDNSResolving; :global IsTimeSync; :global LogPrint; + :global RmFile; :local AllDone true; :local QueueLen [ :len $EmailQueue ]; @@ -89,35 +91,40 @@ :foreach Id,Message in=$EmailQueue do={ :if ([ :typeof $Message ] = "array" ) do={ - :local Attach ({}); :while ([ /tool/e-mail/get last-status ] = "in-progress") do={ :delay 1s; } - :foreach File in=[ :toarray [ $EitherOr ($Message->"attach") "" ] ] do={ - :if ([ :len [ /file/find where name=$File ] ] = 1) do={ - :set Attach ($Attach, $File); - } else={ - $LogPrint warning $0 ("File '" . $File . "' does not exist, can not attach."); + :onerror Err { + :local Attach ({}); + :foreach File in=[ :toarray [ $EitherOr ($Message->"attach") "" ] ] 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; - :local Wait true; - :do { - :delay 1s; - :local Status [ /tool/e-mail/get last-status ]; - :if ($Status = "succeeded") do={ - :set ($EmailQueue->$Id); - :set Wait false; - :if (($Message->"remove-attach") = true) do={ - :foreach File in=$Attach do={ - /file/remove $File; + /tool/e-mail/send from=[ $EMailGenerateFrom ] to=($Message->"to") cc=($Message->"cc") \ + subject=($Message->"subject") body=($Message->"body") file=$Attach; + :local Wait true; + :do { + :delay 1s; + :local Status [ /tool/e-mail/get last-status ]; + :if ($Status = "succeeded") do={ + :set ($EmailQueue->$Id); + :set Wait false; + :if (($Message->"remove-attach") = true) do={ + :foreach File in=$Attach do={ + $RmFile $File; + } } } - } - :if ($Status = "failed") do={ - :set AllDone false; - :set Wait false; - } - } while=($Wait = true); + :if ($Status = "failed") do={ + :set AllDone false; + :set Wait false; + } + } while=($Wait = true); + } do={ + $LogPrint warning $0 ("Sending queued mail failed: " . $Err); + :set AllDone false; + } } } @@ -135,8 +142,8 @@ /system/scheduler/set interval=(($SchedVal->"run-count") . "m") \ comment="Waiting for retry..." $Scheduler; -} on-error={ - :global ExitError; $ExitError false $0; +} do={ + :global ExitError; $ExitError false $0 $Err; } } # generate filter for log-forward @@ -176,6 +183,7 @@ :global IfThenElse; :global NotificationEMailSignature; :global NotificationEMailSubject; + :global SymbolForNotification; :local To [ $EitherOr ($EmailGeneralToOverride->($Notification->"origin")) $EmailGeneralTo ]; :local Cc [ $EitherOr ($EmailGeneralCcOverride->($Notification->"origin")) $EmailGeneralCc ]; @@ -188,13 +196,23 @@ :if ([ :typeof $EmailQueue ] = "nothing") do={ :set EmailQueue ({}); } + :local Truncated false; + :local Body ($Notification->"message"); + :if ([ :len $Body ] > 62000) do={ + :set Body ([ :pick $Body 0 62000 ] . "..."); + :set Truncated true; + } :local Signature [ $EitherOr [ $NotificationEMailSignature ] [ /system/note/get note ] ]; + :set Body ($Body . "\n" . \ + [ $IfThenElse ([ :len ($Notification->"link") ] > 0) \ + ("\n" . [ $SymbolForNotification "link" ] . ($Notification->"link")) ] . \ + [ $IfThenElse ($Truncated = true) ("\n" . [ $SymbolForNotification "scissors" ] . \ + "The message was too long and has been truncated!") ] . \ + [ $IfThenElse ([ :len $Signature ] > 0) ("\n-- \n" . $Signature) "" ]); :set ($EmailQueue->[ :len $EmailQueue ]) { to=$To; cc=$Cc; subject=[ $NotificationEMailSubject ($Notification->"subject") ]; - body=(($Notification->"message") . \ - [ $IfThenElse ([ :len ($Notification->"link") ] > 0) ("\n\n" . ($Notification->"link")) "" ] . \ - [ $IfThenElse ([ :len $Signature ] > 0) ("\n-- \n" . $Signature) "" ]); \ + body=$Body; \ attach=($Notification->"attach"); remove-attach=($Notification->"remove-attach") }; :if ([ :len [ /system/scheduler/find where name="_FlushEmailQueue" ] ] = 0) do={ /system/scheduler/add name="_FlushEmailQueue" interval=1s start-time=startup \ @@ -248,12 +266,12 @@ } # send notification via e-mail - expects at least two string arguments -:set SendEMail do={ :do { +:set SendEMail do={ :onerror Err { :global SendEMail2; $SendEMail2 ({ origin=$0; subject=$1; message=$2; link=$3 }); -} on-error={ - :global ExitError; $ExitError false $0; +} do={ + :global ExitError; $ExitError false $0 $Err; } } # send notification via e-mail - expects one array argument diff --git a/mod/notification-gotify.rsc b/mod/notification-gotify.rsc new file mode 100644 index 0000000..d8eafbe --- /dev/null +++ b/mod/notification-gotify.rsc @@ -0,0 +1,139 @@ +#!rsc by RouterOS +# RouterOS script: mod/notification-gotify +# Copyright (c) 2013-2025 Christian Hesse <mail@eworm.de> +# Leonardo David Monteiro <leo@cub3.xyz> +# https://rsc.eworm.de/COPYING.md +# +# requires RouterOS, version=7.15 +# requires device-mode, fetch, scheduler +# +# send notifications via Gotify (gotify.net) +# https://rsc.eworm.de/doc/mod/notification-gotify.md + +:global FlushGotifyQueue; +:global NotificationFunctions; +:global PurgeGotifyQueue; +:global SendGotify; +:global SendGotify2; + +# flush Gotify queue +:set FlushGotifyQueue do={ :onerror Err { + :global GotifyQueue; + + :global IsFullyConnected; + :global LogPrint; + + :if ([ $IsFullyConnected ] = false) do={ + $LogPrint debug $0 ("System is not fully connected, not flushing."); + :return false; + } + + :local AllDone true; + :local QueueLen [ :len $GotifyQueue ]; + + :if ([ :len [ /system/scheduler/find where name="_FlushGotifyQueue" ] ] > 0 && $QueueLen = 0) do={ + $LogPrint warning $0 ("Flushing Gotify messages from scheduler, but queue is empty."); + } + + :foreach Id,Message in=$GotifyQueue do={ + :if ([ :typeof $Message ] = "array" ) do={ + :onerror Err { + /tool/fetch check-certificate=yes-without-crl output=none http-method=post \ + http-header-field=($Message->"headers") http-data=[ :serialize to=json ($Message->"message") ] \ + ($Message->"url") as-value; + :set ($GotifyQueue->$Id); + } do={ + $LogPrint debug $0 ("Sending queued Gotify message failed: " . $Err); + :set AllDone false; + } + } + } + + :if ($AllDone = true && $QueueLen = [ :len $GotifyQueue ]) do={ + /system/scheduler/remove [ find where name="_FlushGotifyQueue" ]; + :set GotifyQueue; + } +} do={ + :global ExitError; $ExitError false $0 $Err; +} } + +# send notification via Gotify - expects one array argument +:set ($NotificationFunctions->"gotify") do={ + :local Notification $1; + + :global Identity; + :global IdentityExtra; + :global GotifyQueue; + :global GotifyServer; + :global GotifyServerOverride; + :global GotifyToken; + :global GotifyTokenOverride; + + :global EitherOr; + :global FetchUserAgentStr; + :global IfThenElse; + :global LogPrint; + :global SymbolForNotification; + + :local Server [ $EitherOr ($GotifyServerOverride->($Notification->"origin")) $GotifyServer ]; + :local Token [ $EitherOr ($GotifyTokenOverride->($Notification->"origin")) $GotifyToken ]; + + :if ([ :len $Token ] = 0) do={ + :return false; + } + + :local Url ("https://" . $Server . "/message"); + :local Headers ({ [ $FetchUserAgentStr ($Notification->"origin") ]; \ + ("X-Gotify-Key: " . $Token); "Content-Type: application/json" }); + :local Message ({ + "title"=("[" . $IdentityExtra . $Identity . "] " . ($Notification->"subject")); \ + "message"=(($Notification->"message") . "\n" . [ $IfThenElse ([ :len ($Notification->"link") ] > 0) \ + ("\n" . [ $SymbolForNotification "link" ] . ($Notification->"link")) ]); \ + "priority"=[ :tonum [ $IfThenElse ($Notification->"silent") 2 5 ] ] }); + + :onerror Err { + /tool/fetch check-certificate=yes-without-crl output=none http-method=post \ + http-header-field=$Headers http-data=[ :serialize to=json $Message ] $Url as-value; + } do={ + $LogPrint info $0 ("Failed sending Gotify notification: " . $Err . " - Queuing..."); + + :if ([ :typeof $GotifyQueue ] = "nothing") do={ + :set GotifyQueue ({}); + } + :set ($Message->"message") (($Notification->"message") . "\n" . \ + [ $SymbolForNotification "alarm-clock" ] . "This message was queued since " . \ + [ /system/clock/get date ] . " " . [ /system/clock/get time ] . " and may be obsolete."); + :set ($GotifyQueue->[ :len $GotifyQueue ]) \ + { url=$Url; headers=$Headers; message=$Message }; + :if ([ :len [ /system/scheduler/find where name="_FlushGotifyQueue" ] ] = 0) do={ + /system/scheduler/add name="_FlushGotifyQueue" interval=1m start-time=startup \ + on-event=(":global FlushGotifyQueue; \$FlushGotifyQueue;"); + } + } +} + +# purge the Gotify queue +:set PurgeGotifyQueue do={ + :global GotifyQueue; + + /system/scheduler/remove [ find where name="_FlushGotifyQueue" ]; + :set GotifyQueue; +} + +# send notification via Gotify - expects at least two string arguments +:set SendGotify do={ :onerror Err { + :global SendGotify2; + + $SendGotify2 ({ origin=$0; subject=$1; message=$2; link=$3; silent=$4 }); +} do={ + :global ExitError; $ExitError false $0 $Err; +} } + +# send notification via Gotify - expects one array argument +:set SendGotify2 do={ + :local Notification $1; + + :global NotificationFunctions; + + ($NotificationFunctions->"gotify") ("\$NotificationFunctions->\"gotify\"") $Notification; +} diff --git a/mod/notification-matrix.rsc b/mod/notification-matrix.rsc index e989ee0..e9b42a0 100644 --- a/mod/notification-matrix.rsc +++ b/mod/notification-matrix.rsc @@ -19,7 +19,7 @@ :global SetupMatrixJoinRoom; # flush Matrix queue -:set FlushMatrixQueue do={ :do { +:set FlushMatrixQueue do={ :onerror Err { :global MatrixQueue; :global IsFullyConnected; @@ -39,7 +39,7 @@ :foreach Id,Message in=$MatrixQueue do={ :if ([ :typeof $Message ] = "array" ) do={ - :do { + :onerror Err { /tool/fetch check-certificate=yes-without-crl output=none \ http-header-field=($Message->"headers") http-method=post \ http-data=[ :serialize to=json { "msgtype"="m.text"; "body"=($Message->"plain"); @@ -47,8 +47,8 @@ ("https://" . $Message->"homeserver" . "/_matrix/client/r0/rooms/" . $Message->"room" . \ "/send/m.room.message?access_token=" . $Message->"accesstoken") as-value; :set ($MatrixQueue->$Id); - } on-error={ - $LogPrint debug $0 ("Sending queued Matrix message failed."); + } do={ + $LogPrint debug $0 ("Sending queued Matrix message failed: " . $Err); :set AllDone false; } } @@ -58,8 +58,8 @@ /system/scheduler/remove [ find where name="_FlushMatrixQueue" ]; :set MatrixQueue; } -} on-error={ - :global ExitError; $ExitError false $0; +} do={ + :global ExitError; $ExitError false $0 $Err; } } # send notification via Matrix - expects one array argument @@ -129,15 +129,15 @@ [ $PrepareText $Label ] . "</a>"); } - :do { + :onerror Err { /tool/fetch check-certificate=yes-without-crl output=none \ http-header-field=$Headers http-method=post \ http-data=[ :serialize to=json { "msgtype"="m.text"; "body"=$Plain; "format"="org.matrix.custom.html"; "formatted_body"=$Formatted } ] \ ("https://" . $HomeServer . "/_matrix/client/r0/rooms/" . $Room . \ "/send/m.room.message?access_token=" . $AccessToken) as-value; - } on-error={ - $LogPrint info $0 ("Failed sending Matrix notification! Queuing..."); + } do={ + $LogPrint info $0 ("Failed sending Matrix notification: " . $Err . " - Queuing..."); :if ([ :typeof $MatrixQueue ] = "nothing") do={ :set MatrixQueue ({}); @@ -167,12 +167,12 @@ } # send notification via Matrix - expects at least two string arguments -:set SendMatrix do={ :do { +:set SendMatrix do={ :onerror Err { :global SendMatrix2; $SendMatrix2 ({ origin=$0; subject=$1; message=$2; link=$3 }); -} on-error={ - :global ExitError; $ExitError false $0; +} do={ + :global ExitError; $ExitError false $0 $Err; } } # send notification via Matrix - expects one array argument @@ -196,14 +196,14 @@ :global MatrixHomeServer; :local Domain [ :pick $User ([ :find $User ":" ] + 1) [ :len $User] ]; - :do { + :onerror Err { :local Data ([ /tool/fetch check-certificate=yes-without-crl output=user \ http-header-field=({ [ $FetchUserAgentStr $0 ] }) \ ("https://" . $Domain . "/.well-known/matrix/client") as-value ]->"data"); :set MatrixHomeServer ([ :deserialize from=json value=$Data ]->"m.homeserver"->"base_url"); $LogPrint debug $0 ("Home server is: " . $MatrixHomeServer); - } on-error={ - $LogPrint error $0 ("Failed getting home server!"); + } do={ + $LogPrint error $0 ("Failed getting home server: " . $Err); :return false; } @@ -211,27 +211,27 @@ :set MatrixHomeServer [ :pick $MatrixHomeServer 8 [ :len $MatrixHomeServer ] ]; } - :do { + :onerror Err { :local Data ([ /tool/fetch check-certificate=yes-without-crl output=user \ http-header-field=({ [ $FetchUserAgentStr $0 ] }) http-method=post \ http-data=[ :serialize to=json { "type"="m.login.password"; "user"=$User; "password"=$Pass } ] \ ("https://" . $MatrixHomeServer . "/_matrix/client/r0/login") as-value ]->"data"); :set MatrixAccessToken ([ :deserialize from=json value=$Data ]->"access_token"); $LogPrint debug $0 ("Access token is: " . $MatrixAccessToken); - } on-error={ - $LogPrint error $0 ("Failed logging in (and getting access token)!"); + } do={ + $LogPrint error $0 ("Failed logging in (and getting access token): " . $Err); :return false; } - :do { + :onerror Err { /system/script/remove [ find where name="global-config-overlay.d/mod/notification-matrix" ]; /system/script/add name="global-config-overlay.d/mod/notification-matrix" source=( \ "# configuration snippet: mod/notification-matrix\n\n" . \ ":global MatrixHomeServer \"" . $MatrixHomeServer . "\";\n" . \ ":global MatrixAccessToken \"" . $MatrixAccessToken . "\";\n"); $LogPrint info $0 ("Added configuration snippet. Now create and join a room, please!"); - } on-error={ - $LogPrint error $0 ("Failed adding configuration snippet!"); + } do={ + $LogPrint error $0 ("Failed adding configuration snippet: " . $Err); :return false; } } @@ -248,24 +248,24 @@ :global MatrixHomeServer; :global MatrixRoom; - :do { + :onerror Err { /tool/fetch check-certificate=yes-without-crl output=none \ http-header-field=({ [ $FetchUserAgentStr $0 ] }) http-method=post http-data="" \ ("https://" . $MatrixHomeServer . "/_matrix/client/r0/rooms/" . [ $UrlEncode $MatrixRoom ] . \ "/join?access_token=" . [ $UrlEncode $MatrixAccessToken ]) as-value; $LogPrint debug $0 ("Joined the room."); - } on-error={ - $LogPrint error $0 ("Failed joining the room!"); + } do={ + $LogPrint error $0 ("Failed joining the room: " . $Err); :return false; } - :do { + :onerror Err { :local Snippet [ /system/script/find where name="global-config-overlay.d/mod/notification-matrix" ]; /system/script/set $Snippet source=([ get $Snippet source ] . \ ":global MatrixRoom \"" . $MatrixRoom . "\";\n"); $LogPrint info $0 ("Appended configuration to configuration snippet. Please review!"); - } on-error={ - $LogPrint error $0 ("Failed appending configuration to snippet!"); + } do={ + $LogPrint error $0 ("Failed appending configuration to snippet: " . $Err); :return false; } } diff --git a/mod/notification-ntfy.rsc b/mod/notification-ntfy.rsc index aac6d6c..7114020 100644 --- a/mod/notification-ntfy.rsc +++ b/mod/notification-ntfy.rsc @@ -16,9 +16,8 @@ :global SendNtfy2; # flush ntfy queue -:set FlushNtfyQueue do={ :do { +:set FlushNtfyQueue do={ :onerror Err { :global NtfyQueue; - :global NtfyMessageIDs; :global IsFullyConnected; :global LogPrint; @@ -37,13 +36,13 @@ :foreach Id,Message in=$NtfyQueue do={ :if ([ :typeof $Message ] = "array" ) do={ - :do { + :onerror Err { /tool/fetch check-certificate=yes-without-crl output=none http-method=post \ http-header-field=($Message->"headers") http-data=($Message->"text") \ ($Message->"url") as-value; :set ($NtfyQueue->$Id); - } on-error={ - $LogPrint debug $0 ("Sending queued Ntfy message failed."); + } do={ + $LogPrint debug $0 ("Sending queued Ntfy message failed: " . $Err); :set AllDone false; } } @@ -53,8 +52,8 @@ /system/scheduler/remove [ find where name="_FlushNtfyQueue" ]; :set NtfyQueue; } -} on-error={ - :global ExitError; $ExitError false $0; +} do={ + :global ExitError; $ExitError false $0 $Err; } } # send notification via ntfy - expects one array argument @@ -108,7 +107,7 @@ :set Text ($Text . "\n" . [ $SymbolForNotification "link" ] . ($Notification->"link")); } - :do { + :onerror Err { :if ($Server = "ntfy.sh") do={ :if ([ $CertificateAvailable "ISRG Root X1" ] = false) do={ $LogPrint warning $0 ("Downloading required certificate failed."); @@ -117,8 +116,8 @@ } /tool/fetch check-certificate=yes-without-crl output=none http-method=post \ http-header-field=$Headers http-data=$Text $Url as-value; - } on-error={ - $LogPrint info $0 ("Failed sending ntfy notification! Queuing..."); + } do={ + $LogPrint info $0 ("Failed sending ntfy notification: " . $Err . " - Queuing..."); :if ([ :typeof $NtfyQueue ] = "nothing") do={ :set NtfyQueue ({}); @@ -144,12 +143,12 @@ } # send notification via ntfy - expects at least two string arguments -:set SendNtfy do={ :do { +:set SendNtfy do={ :onerror Err { :global SendNtfy2; $SendNtfy2 ({ origin=$0; subject=$1; message=$2; link=$3; silent=$4 }); -} on-error={ - :global ExitError; $ExitError false $0; +} do={ + :global ExitError; $ExitError false $0 $Err; } } # send notification via ntfy - expects one array argument diff --git a/mod/notification-telegram.rsc b/mod/notification-telegram.rsc index 23ef942..2eb90e1 100644 --- a/mod/notification-telegram.rsc +++ b/mod/notification-telegram.rsc @@ -10,19 +10,19 @@ # https://rsc.eworm.de/doc/mod/notification-telegram.md :global FlushTelegramQueue; +:global GetTelegramChatId; :global NotificationFunctions; :global PurgeTelegramQueue; :global SendTelegram; :global SendTelegram2; # flush telegram queue -:set FlushTelegramQueue do={ :do { +:set FlushTelegramQueue do={ :onerror Err { :global TelegramQueue; :global TelegramMessageIDs; :global IsFullyConnected; :global LogPrint; - :global UrlEncode; :if ([ $IsFullyConnected ] = false) do={ $LogPrint debug $0 ("System is not fully connected, not flushing."); @@ -38,16 +38,14 @@ :foreach Id,Message in=$TelegramQueue do={ :if ([ :typeof $Message ] = "array" ) do={ - :do { + :onerror Err { :local Data ([ /tool/fetch check-certificate=yes-without-crl output=user http-method=post \ ("https://api.telegram.org/bot" . ($Message->"tokenid") . "/sendMessage") \ - http-data=("chat_id=" . ($Message->"chatid") . "&disable_notification=" . ($Message->"silent") . \ - "&reply_to_message_id=" . ($Message->"replyto") . "&disable_web_page_preview=true" . \ - "&parse_mode=MarkdownV2&text=" . [ $UrlEncode ($Message->"text") ]) as-value ]->"data"); + http-data=($Message->"http-data") as-value ]->"data"); :set ($TelegramQueue->$Id); :set ($TelegramMessageIDs->[ :tostr ([ :deserialize from=json value=$Data ]->"result"->"message_id") ]) 1; - } on-error={ - $LogPrint debug $0 ("Sending queued Telegram message failed."); + } do={ + $LogPrint debug $0 ("Sending queued Telegram message failed: " . $Err); :set AllDone false; } } @@ -57,10 +55,49 @@ /system/scheduler/remove [ find where name="_FlushTelegramQueue" ]; :set TelegramQueue; } -} on-error={ - :global ExitError; $ExitError false $0; +} do={ + :global ExitError; $ExitError false $0 $Err; } } +# get the chat id +:set GetTelegramChatId do={ :onerror Err { + :global TelegramTokenId; + + :global CertificateAvailable; + :global LogPrint; + + :if ([ $CertificateAvailable "Go Daddy Root Certificate Authority - G2" ] = false) do={ + $LogPrint warning $0 ("Downloading required certificate failed."); + :return false; + } + + :local Data; + :onerror Err { + :set Data ([ /tool/fetch check-certificate=yes-without-crl output=user \ + ("https://api.telegram.org/bot" . $TelegramTokenId . "/getUpdates?offset=0" . \ + "&allowed_updates=%5B%22message%22%5D") as-value ]->"data"); + } do={ + $LogPrint warning $0 ("Fetching data failed: " . $Err); + :return false; + } + + :local JSON [ :deserialize from=json value=$Data ]; + :local Count [ :len ($JSON->"result") ]; + + :if ($Count = 0) do={ + $LogPrint info $0 ("No message received."); + :return false; + } + + :local Message ($JSON->"result"->($Count - 1)->"message"); + $LogPrint info $0 ("The chat id is: " . ($Message->"chat"->"id")); + :if (($Message->"is_topic_message") = true) do={ + $LogPrint info $0 ("The thread id is: " . ($Message->"message_thread_id")); + } +} do={ + :global ExitError; $ExitError false $0 $Err; +} } + # send notification via telegram - expects one array argument :set ($NotificationFunctions->"telegram") do={ :local Notification $1; @@ -71,6 +108,8 @@ :global TelegramChatIdOverride; :global TelegramMessageIDs; :global TelegramQueue; + :global TelegramThreadId; + :global TelegramThreadIdOverride; :global TelegramTokenId; :global TelegramTokenIdOverride; @@ -111,6 +150,9 @@ :local ChatId [ $EitherOr ($Notification->"chatid") \ [ $EitherOr ($TelegramChatIdOverride->($Notification->"origin")) $TelegramChatId ] ]; + :local ThreadId [ $EitherOr ($Notification->"threadid") \ + [ $EitherOr ($TelegramThreadIdOverride->($Notification->"origin")) \ + [ $IfThenElse ([ :len ($TelegramChatIdOverride->($Notification->"origin")) ] = 0) $TelegramThreadId ] ] ]; :local TokenId [ $EitherOr ($TelegramTokenIdOverride->($Notification->"origin")) $TelegramTokenId ]; :if ([ :len $TokenId ] = 0 || [ :len $ChatId ] = 0) do={ @@ -145,19 +187,20 @@ (($LenSum - [ :len $Text ]) * 100 / $LenSum) . "%_!") "plain" "_" ]); } - :do { + :local HTTPData ("chat_id=" . $ChatId . "&disable_notification=" . ($Notification->"silent") . \ + "&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={ $LogPrint warning $0 ("Downloading required certificate failed."); :error false; } :local Data ([ /tool/fetch check-certificate=yes-without-crl output=user http-method=post \ ("https://api.telegram.org/bot" . $TokenId . "/sendMessage") \ - http-data=("chat_id=" . $ChatId . "&disable_notification=" . ($Notification->"silent") . \ - "&reply_to_message_id=" . ($Notification->"replyto") . "&disable_web_page_preview=true" . \ - "&parse_mode=MarkdownV2&text=" . [ $UrlEncode $Text ]) as-value ]->"data"); + http-data=($HTTPData . "&text=" . [ $UrlEncode $Text ]) as-value ]->"data"); :set ($TelegramMessageIDs->[ :tostr ([ :deserialize from=json value=$Data ]->"result"->"message_id") ]) 1; - } on-error={ - $LogPrint info $0 ("Failed sending Telegram notification! Queuing..."); + } do={ + $LogPrint info $0 ("Failed sending Telegram notification: " . $Err . " - Queuing..."); :if ([ :typeof $TelegramQueue ] = "nothing") do={ :set TelegramQueue ({}); @@ -165,8 +208,8 @@ :set Text ($Text . "\n" . [ $SymbolForNotification "alarm-clock" ] . \ [ $EscapeMD ("This message was queued since _" . [ /system/clock/get date ] . \ " " . [ /system/clock/get time ] . "_ and may be obsolete.") "plain" "_" ]); - :set ($TelegramQueue->[ :len $TelegramQueue ]) { chatid=$ChatId; tokenid=$TokenId; - text=$Text; silent=($Notification->"silent"); replyto=($Notification->"replyto") }; + :set ($TelegramQueue->[ :len $TelegramQueue ]) { tokenid=$TokenId; + http-data=($HTTPData . "&text=" . [ $UrlEncode $Text ]) }; :if ([ :len [ /system/scheduler/find where name="_FlushTelegramQueue" ] ] = 0) do={ /system/scheduler/add name="_FlushTelegramQueue" interval=1m start-time=startup \ on-event=(":global FlushTelegramQueue; \$FlushTelegramQueue;"); @@ -183,12 +226,12 @@ } # send notification via telegram - expects at least two string arguments -:set SendTelegram do={ :do { +:set SendTelegram do={ :onerror Err { :global SendTelegram2; $SendTelegram2 ({ origin=$0; subject=$1; message=$2; link=$3; silent=$4 }); -} on-error={ - :global ExitError; $ExitError false $0; +} do={ + :global ExitError; $ExitError false $0 $Err; } } # send notification via telegram - expects one array argument diff --git a/mod/scriptrunonce.rsc b/mod/scriptrunonce.rsc index 7fcd5b5..1d6aaf1 100644 --- a/mod/scriptrunonce.rsc +++ b/mod/scriptrunonce.rsc @@ -11,7 +11,7 @@ :global ScriptRunOnce; # fetch and run script(s) once -:set ScriptRunOnce do={ :do { +:set ScriptRunOnce do={ :onerror Err { :local Scripts [ :toarray $1 ]; :global ScriptRunOnceBaseUrl; @@ -41,16 +41,16 @@ :return false; } - :do { + :onerror Err { $LogPrint info $0 ("Running script '" . $Script . "' now."); [ :parse $Source ]; - } on-error={ - $LogPrint warning $0 ("The script '" . $Script . "' failed to run!"); + } do={ + $LogPrint warning $0 ("The script '" . $Script . "' failed to run: " . $Err); :return false; } :return true; } -} on-error={ - :global ExitError; $ExitError false $0; +} do={ + :global ExitError; $ExitError false $0 $Err; } } diff --git a/mod/ssh-keys-import.rsc b/mod/ssh-keys-import.rsc index 2fae4b1..7bdc95d 100644 --- a/mod/ssh-keys-import.rsc +++ b/mod/ssh-keys-import.rsc @@ -12,7 +12,7 @@ :global SSHKeysImportFile; # import single key passed as string -:set SSHKeysImport do={ :do { +:set SSHKeysImport do={ :onerror Err { :local Key [ :tostr $1 ]; :local User [ :tostr $2 ]; @@ -55,26 +55,27 @@ /file/add name=$FileName contents=($Key . ", md5=" . $FingerPrintMD5); $WaitForFile $FileName; - :do { + :onerror Err { /user/ssh-keys/import public-key-file=$FileName user=$User; $LogPrint info $0 ("Imported ssh public key (" . $KeyVal->2 . ", " . $KeyVal->0 . ", " . \ "MD5:" . $FingerPrintMD5 . ") for user '" . $User . "'."); $RmDir "tmpfs/ssh-keys-import"; - } on-error={ - $LogPrint warning $0 ("Failed importing key."); + } do={ + $LogPrint warning $0 ("Failed importing key: " . $Err); $RmDir "tmpfs/ssh-keys-import"; :return false; } -} on-error={ - :global ExitError; $ExitError false $0; +} do={ + :global ExitError; $ExitError false $0 $Err; } } # import keys from a file -:set SSHKeysImportFile do={ :do { +:set SSHKeysImportFile do={ :onerror Err { :local FileName [ :tostr $1 ]; :local User [ :tostr $2 ]; :global EitherOr; + :global FileExists; :global LogPrint; :global ParseKeyValueStore; :global SSHKeysImport; @@ -84,8 +85,7 @@ :return false; } - :local File [ /file/find where name=$FileName ]; - :if ([ :len $File ] = 0) do={ + :if ([ $FileExists $FileName ] = true) do={ $LogPrint warning $0 ("File '" . $FileName . "' does not exist."); :return false; } @@ -94,9 +94,7 @@ :foreach KeyVal in=[ :deserialize $Keys delimiter=" " from=dsv options=dsv.plain ] do={ :local Continue false; :if ($KeyVal->0 = "ssh-ed25519" || $KeyVal->0 = "ssh-rsa") do={ - :do { - $SSHKeysImport ($KeyVal->0 . " " . $KeyVal->1 . " " . $KeyVal->2) $User; - } on-error={ + :if ([ $SSHKeysImport ($KeyVal->0 . " " . $KeyVal->1 . " " . $KeyVal->2) $User ] = false) do={ $LogPrint warning $0 ("Failed importing key for user '" . $User . "'."); } :set Continue true; @@ -109,6 +107,6 @@ $LogPrint warning $0 ("SSH key of type '" . $KeyVal->0 . "' is not supported."); } } -} on-error={ - :global ExitError; $ExitError false $0; +} do={ + :global ExitError; $ExitError false $0 $Err; } } diff --git a/mode-button.rsc b/mode-button.rsc index edc5f40..d82f899 100644 --- a/mode-button.rsc +++ b/mode-button.rsc @@ -9,11 +9,11 @@ # act on multiple mode and reset button presses # https://rsc.eworm.de/doc/mode-button.md -:global GlobalFunctionsReady; -:while ($GlobalFunctionsReady != true) do={ :delay 500ms; } - :local ExitOK false; -:do { +:onerror Err { + :global GlobalConfigReady; :global GlobalFunctionsReady; + :retry { :if ($GlobalConfigReady != true || $GlobalFunctionsReady != true) \ + do={ :error ("Global config and/or functions not ready."); }; } delay=500ms max=50; :local ScriptName [ :jobname ]; :global ModeButton; @@ -26,7 +26,7 @@ :if ([ :len $Scheduler ] = 0) do={ $LogPrint info $ScriptName ("Creating scheduler _ModeButtonScheduler, counting presses..."); - :global ModeButtonScheduler do={ :do { + :global ModeButtonScheduler do={ :onerror Err { :local FuncName $0; :global ModeButton; @@ -69,11 +69,11 @@ :delay 200ms; } - :do { + :onerror Err { [ :parse $Code ]; - } on-error={ + } do={ $LogPrint warning $FuncName \ - ("The code for " . $Count . " mode-button presses failed with runtime error!"); + ("The code for " . $Count . " mode-button presses failed with runtime error: " . $Err); } } else={ $LogPrint warning $FuncName \ @@ -82,8 +82,8 @@ } else={ $LogPrint info $FuncName ("No action defined for " . $Count . " mode-button presses."); } - } on-error={ - :global ExitError; $ExitError false $0; + } do={ + :global ExitError; $ExitError false $0 $Err; } } /system/scheduler/add name="_ModeButtonScheduler" \ on-event=":global ModeButtonScheduler; \$ModeButtonScheduler;" interval=3s; @@ -91,6 +91,6 @@ $LogPrint debug $ScriptName ("Updating scheduler _ModeButtonScheduler..."); /system/scheduler/set $Scheduler start-time=[ /system/clock/get time ]; } -} on-error={ - :global ExitError; $ExitError $ExitOK [ :jobname ]; +} do={ + :global ExitError; $ExitError $ExitOK [ :jobname ] $Err; } diff --git a/netwatch-dns.rsc b/netwatch-dns.rsc index 467d636..9e2f9bc 100644 --- a/netwatch-dns.rsc +++ b/netwatch-dns.rsc @@ -9,11 +9,11 @@ # monitor and manage dns/doh with netwatch # https://rsc.eworm.de/doc/netwatch-dns.md -:global GlobalFunctionsReady; -:while ($GlobalFunctionsReady != true) do={ :delay 500ms; } - :local ExitOK false; -:do { +:onerror Err { + :global GlobalConfigReady; :global GlobalFunctionsReady; + :retry { :if ($GlobalConfigReady != true || $GlobalFunctionsReady != true) \ + do={ :error ("Global config and/or functions not ready."); }; } delay=500ms max=50; :local ScriptName [ :jobname ]; :global CertificateAvailable; @@ -118,15 +118,17 @@ } :local Data false; - :do { - :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"); - } on-error={ - $LogPrint warning $ScriptName ("Request to DoH server failed (network or certificate issue): " . \ - ($DohServer->"doh-url")); + :onerror Err { + :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); } :if ($Data != false) do={ @@ -145,6 +147,6 @@ } } } -} on-error={ - :global ExitError; $ExitError $ExitOK [ :jobname ]; +} do={ + :global ExitError; $ExitError $ExitOK [ :jobname ] $Err; } diff --git a/netwatch-notify.rsc b/netwatch-notify.rsc index 8b05c5e..00f03cd 100644 --- a/netwatch-notify.rsc +++ b/netwatch-notify.rsc @@ -8,11 +8,11 @@ # monitor netwatch and send notifications # https://rsc.eworm.de/doc/netwatch-notify.md -:global GlobalFunctionsReady; -:while ($GlobalFunctionsReady != true) do={ :delay 500ms; } - :local ExitOK false; -:do { +:onerror Err { + :global GlobalConfigReady; :global GlobalFunctionsReady; + :retry { :if ($GlobalConfigReady != true || $GlobalFunctionsReady != true) \ + do={ :error ("Global config and/or functions not ready."); }; } delay=500ms max=50; :local ScriptName [ :jobname ]; :global NetwatchNotify; @@ -38,10 +38,10 @@ :global ValidateSyntax; :if ([ $ValidateSyntax $Hook ] = true) do={ - :do { + onerror Err { [ :parse $Hook ]; - } on-error={ - $LogPrint warning $ScriptName ("The " . $State . "-hook for " . $Type . " '" . $Name . "' failed to run."); + } do={ + $LogPrint warning $ScriptName ("The " . $State . "-hook for " . $Type . " '" . $Name . "' failed to run: " . $Err); :return ("The hook failed to run."); } } else={ @@ -61,15 +61,19 @@ :global GetRandom20CharAlNum; :local FwAddrList ($ScriptName . "-" . [ $GetRandom20CharAlNum ]); - /ip/firewall/address-list/add address=$Name list=$FwAddrList dynamic=yes timeout=1s; - :delay 20ms; - :if ([ :len [ /ip/firewall/address-list/find where list=$FwAddrList address=$Expected ] ] > 0) do={ - :return true; + :if ([ :typeof [ :toip $Expected ] ] = "ip") do={ + /ip/firewall/address-list/add address=$Name list=$FwAddrList dynamic=yes timeout=10s; + :delay 20ms; + :if ([ :len [ /ip/firewall/address-list/find where list=$FwAddrList address=$Expected ] ] > 0) do={ + :return true; + } } - /ipv6/firewall/address-list/add address=$Name list=$FwAddrList dynamic=yes timeout=1s; - :delay 20ms; - :if ([ :len [ /ipv6/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; + :delay 20ms; + :if ([ :len [ /ipv6/firewall/address-list/find where list=$FwAddrList address=$Expected ] ] > 0) do={ + :return true; + } } :return false; @@ -103,7 +107,7 @@ :if ([ :typeof ($HostInfo->"resolve") ] = "str") do={ :if ([ $IsDNSResolving ] = true) do={ - :do { + :onerror Err { :local Resolve [ :resolve type=[ $IfThenElse ([ :typeof ($HostVal->"host") ] = "ip") \ "ipv4" "ipv6" ] ($HostInfo->"resolve") ]; :if ($Resolve != $HostVal->"host") do={ @@ -117,13 +121,13 @@ :set ($HostVal->"status") "unknown"; } } - } on-error={ + } do={ :set ($Metric->"resolve-failcnt") ($Metric->"resolve-failcnt" + 1); :if ($Metric->"resolve-failcnt" = 3) do={ $LogPrint [ $IfThenElse ($HostInfo->"no-resolve-fail" != true) warning debug ] \ $ScriptName ("Resolving name '" . $HostInfo->"resolve" . [ $IfThenElse \ ($HostInfo->"resolve" != $HostInfo->"name") ("' for " . $Type . " '" . \ - $HostInfo->"name") "" ] . "' failed."); + $HostInfo->"name") "" ] . "' failed: " . $Err); } } } @@ -220,6 +224,6 @@ "since"=($Metric->"since") }; } } -} on-error={ - :global ExitError; $ExitError $ExitOK [ :jobname ]; +} do={ + :global ExitError; $ExitError $ExitOK [ :jobname ] $Err; } diff --git a/news-and-changes.rsc b/news-and-changes.rsc index 6f9e96e..dbfb1b9 100644 --- a/news-and-changes.rsc +++ b/news-and-changes.rsc @@ -58,6 +58,11 @@ 131="Enhanced certificate download to fallback to mkcert.org, so all (commonly trusted) root certificates are available now."; 132="Split off plugins from 'check-health', so the script works on all devices to monitor CPU and RAM. The supported plugins for sensors in hardware are installed automatically."; 133="Updated the default configuration for 'fw-addr-lists', deprecated lists were removed, a collective list was added."; + 134="Enhanced 'mod/notification-telegram' and 'telegram-chat' to support topics in groups."; + 135="Introduced helper function '\$GetTelegramChatId' for 'mod/notification-telegram' which helps retrieve information."; + 136="Introduced script 'check-perpetual-license' to check for license state on CHR."; + 137="Added support to send notifications via Gotify (gotify.net)."; + 138="RouterOS 7.19 is suffering an issue with certificate store. Fixing trust state for all certificates..."; }; # Migration steps to be applied on script updates @@ -67,4 +72,5 @@ 104=":global CharacterReplace; :global ScriptInstallUpdate; :foreach Script in={ \"capsman-download-packages\"; \"capsman-rolling-upgrade\"; \"hotspot-to-wpa\"; \"hotspot-to-wpa-cleanup\" } do={ /system/script/set name=(\$Script . \".capsman\") [ find where name=\$Script ]; :foreach Scheduler in=[ /system/scheduler/find where on-event~(\$Script . \"([^-.]|\\\$)\") ] do={ /system/scheduler/set \$Scheduler on-event=[ \$CharacterReplace [ get \$Scheduler on-event ] \$Script (\$Script . \".capsman\") ]; }; }; /ip/hotspot/user/profile/set on-login=\"hotspot-to-wpa.capsman\" [ find where on-login=\"hotspot-to-wpa\" ]; \$ScriptInstallUpdate;"; 111=":local Rec [ /ip/dns/static/find where comment~\"^managed by dhcp-to-dns for \" ]; :if ([ :len \$Rec ] > 0) do={ /ip/dns/static/remove \$Rec; /system/script/run dhcp-to-dns; }"; 132=":if ([ :len [ /system/script/find where name=\"check-health\" ] ] > 0) do={ :local Code \":local Install \\\"check-health\\\"; :if ([ :len [ /system/health/find where type=\\\"\\\" name~\\\"-state\\\\\\\$\\\" ] ] > 0) do={ :set Install (\\\$Install . \\\",check-health.d/state\\\"); }; :if ([ :len [ /system/health/find where type=\\\"C\\\" ] ] > 0) do={ :set Install (\\\$Install . \\\",check-health.d/temperature\\\"); }; :if ([ :len [ /system/health/find where type=\\\"V\\\" ] ] > 0) do={ :set Install (\\\$Install . \\\",check-health.d/voltage\\\"); }; :global ScriptInstallUpdate; \\\$ScriptInstallUpdate \\\$Install;\"; :global ValidateSyntax; :if ([ \$ValidateSyntax \$Code ] = true) do={ :do { [ :parse \$Code ]; } on-error={ }; }; }"; + 138="/certificate/set trusted=yes [ find where trusted=yes ];"; }; diff --git a/ospf-to-leds.rsc b/ospf-to-leds.rsc index a8662b3..26f8aa3 100644 --- a/ospf-to-leds.rsc +++ b/ospf-to-leds.rsc @@ -8,11 +8,11 @@ # visualize ospf instance state via leds # https://rsc.eworm.de/doc/ospf-to-leds.md -:global GlobalFunctionsReady; -:while ($GlobalFunctionsReady != true) do={ :delay 500ms; } - :local ExitOK false; -:do { +:onerror Err { + :global GlobalConfigReady; :global GlobalFunctionsReady; + :retry { :if ($GlobalConfigReady != true || $GlobalFunctionsReady != true) \ + do={ :error ("Global config and/or functions not ready."); }; } delay=500ms max=50; :local ScriptName [ :jobname ]; :global LogPrint; @@ -44,6 +44,6 @@ /system/leds/set type=off [ find where leds=$LED ]; } } -} on-error={ - :global ExitError; $ExitError $ExitOK [ :jobname ]; +} do={ + :global ExitError; $ExitError $ExitOK [ :jobname ] $Err; } diff --git a/packages-update.rsc b/packages-update.rsc index b11596e..d3140f2 100644 --- a/packages-update.rsc +++ b/packages-update.rsc @@ -9,11 +9,11 @@ # download packages and reboot for installation # https://rsc.eworm.de/doc/packages-update.md -:global GlobalFunctionsReady; -:while ($GlobalFunctionsReady != true) do={ :delay 500ms; } - :local ExitOK false; -:do { +:onerror Err { + :global GlobalConfigReady; :global GlobalFunctionsReady; + :retry { :if ($GlobalConfigReady != true || $GlobalFunctionsReady != true) \ + do={ :error ("Global config and/or functions not ready."); }; } delay=500ms max=50; :local ScriptName [ :jobname ]; :global BackupRandomDelay; @@ -31,19 +31,25 @@ :local Schedule do={ :local ScriptName [ :tostr $1 ]; + :global PackagesUpdateDeferReboot; + :global GetRandomNumber; + :global IfThenElse; :global LogPrint; :global RebootForUpdate do={ /system/reboot; } + :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 +159,7 @@ :error true; } } else={ - :if ($PackagesUpdateDeferReboot = true) do={ + :if ($PackagesUpdateDeferReboot = true || [ :totime $PackagesUpdateDeferReboot ] >= 1d) do={ $Schedule $ScriptName; :set ExitOK true; :error true; @@ -163,6 +169,6 @@ $LogPrint info $ScriptName ("Rebooting for update."); :delay 1s; /system/reboot; -} on-error={ - :global ExitError; $ExitError $ExitOK [ :jobname ]; +} do={ + :global ExitError; $ExitError $ExitOK [ :jobname ] $Err; } diff --git a/ppp-on-up.rsc b/ppp-on-up.rsc index e09bd9d..f16d73f 100644 --- a/ppp-on-up.rsc +++ b/ppp-on-up.rsc @@ -8,11 +8,11 @@ # run scripts on ppp up # https://rsc.eworm.de/doc/ppp-on-up.md -:global GlobalFunctionsReady; -:while ($GlobalFunctionsReady != true) do={ :delay 500ms; } - :local ExitOK false; -:do { +:onerror Err { + :global GlobalConfigReady; :global GlobalFunctionsReady; + :retry { :if ($GlobalConfigReady != true || $GlobalFunctionsReady != true) \ + do={ :error ("Global config and/or functions not ready."); }; } delay=500ms max=50; :local ScriptName [ :jobname ]; :global LogPrint; @@ -39,6 +39,6 @@ $LogPrint warning $ScriptName ("Running script '" . $ScriptName . "' failed!"); } } -} on-error={ - :global ExitError; $ExitError $ExitOK [ :jobname ]; +} do={ + :global ExitError; $ExitError $ExitOK [ :jobname ] $Err; } diff --git a/sms-action.rsc b/sms-action.rsc index 3c8307a..47e1922 100644 --- a/sms-action.rsc +++ b/sms-action.rsc @@ -8,11 +8,11 @@ # run action on received SMS # https://rsc.eworm.de/doc/sms-action.md -:global GlobalFunctionsReady; -:while ($GlobalFunctionsReady != true) do={ :delay 500ms; } - :local ExitOK false; -:do { +:onerror Err { + :global GlobalConfigReady; :global GlobalFunctionsReady; + :retry { :if ($GlobalConfigReady != true || $GlobalFunctionsReady != true) \ + do={ :error ("Global config and/or functions not ready."); }; } delay=500ms max=50; :local ScriptName [ :jobname ]; :global SmsAction; @@ -36,6 +36,6 @@ } else={ $LogPrint warning $ScriptName ("The code for action '" . $Action . "' failed syntax validation!"); } -} on-error={ - :global ExitError; $ExitError $ExitOK [ :jobname ]; +} do={ + :global ExitError; $ExitError $ExitOK [ :jobname ] $Err; } diff --git a/sms-forward.rsc b/sms-forward.rsc index 8169022..feb640e 100644 --- a/sms-forward.rsc +++ b/sms-forward.rsc @@ -9,11 +9,11 @@ # forward SMS to e-mail # https://rsc.eworm.de/doc/sms-forward.md -:global GlobalFunctionsReady; -:while ($GlobalFunctionsReady != true) do={ :delay 500ms; } - :local ExitOK false; -:do { +:onerror Err { + :global GlobalConfigReady; :global GlobalFunctionsReady; + :retry { :if ($GlobalConfigReady != true || $GlobalFunctionsReady != true) \ + do={ :error ("Global config and/or functions not ready."); }; } delay=500ms max=50; :local ScriptName [ :jobname ]; :global Identity; @@ -61,7 +61,12 @@ :if ($Phone = $Settings->"allowed-number" && \ ($SmsVal->"message")~("^:cmd " . $Settings->"secret" . " script ")) do={ $LogPrint debug $ScriptName ("Removing SMS, which started a script."); - /tool/sms/inbox/remove $Sms; + :onerror Err { + /tool/sms/inbox/remove $Sms; + :delay 50ms; + } do={ + $LogPrint warning $ScriptName ("Failed to remove message: " . $Err); + } } else={ :set Messages ($Messages . "\n\nOn " . $SmsVal->"timestamp" . \ " type " . $SmsVal->"type" . ":\n" . $SmsVal->"message"); @@ -69,12 +74,12 @@ :if ($Phone~($Hook->"allowed-number") && ($SmsVal->"message")~($Hook->"match")) do={ :if ([ $ValidateSyntax ($Hook->"command") ] = true) do={ $LogPrint info $ScriptName ("Running hook '" . $Hook->"match" . "': " . $Hook->"command"); - :do { + :onerror Err { :local Command [ :parse ($Hook->"command") ]; $Command Phone=$Phone Message=($SmsVal->"message"); :set Messages ($Messages . "\n\nRan hook '" . $Hook->"match" . "':\n" . $Hook->"command"); - } on-error={ - $LogPrint warning $ScriptName ("The code for hook '" . $Hook->"match" . "' failed to run!"); + } do={ + $LogPrint warning $ScriptName ("The code for hook '" . $Hook->"match" . "' failed to run: " . $Err); } } else={ $LogPrint warning $ScriptName ("The code for hook '" . $Hook->"match" . "' failed syntax validation!"); @@ -92,10 +97,15 @@ message=("Received " . [ $IfThenElse ($Count = 1) "this message" ("these " . $Count . " messages") ] . \ " by " . $Identity . " from " . $Phone . ":" . $Messages) }); :foreach Sms in=$Delete do={ - /tool/sms/inbox/remove $Sms; + :onerror Err { + /tool/sms/inbox/remove $Sms; + :delay 50ms; + } do={ + $LogPrint warning $ScriptName ("Failed to remove message: " . $Err); + } } } } -} on-error={ - :global ExitError; $ExitError $ExitOK [ :jobname ]; +} do={ + :global ExitError; $ExitError $ExitOK [ :jobname ] $Err; } diff --git a/telegram-chat.rsc b/telegram-chat.rsc index 10952a6..7f7b7a7 100644 --- a/telegram-chat.rsc +++ b/telegram-chat.rsc @@ -9,11 +9,11 @@ # use Telegram to chat with your Router and send commands # https://rsc.eworm.de/doc/telegram-chat.md -:global GlobalFunctionsReady; -:while ($GlobalFunctionsReady != true) do={ :delay 500ms; } - :local ExitOK false; -:do { +:onerror Err { + :global GlobalConfigReady; :global GlobalFunctionsReady; + :retry { :if ($GlobalConfigReady != true || $GlobalFunctionsReady != true) \ + do={ :error ("Global config and/or functions not ready."); }; } delay=500ms max=50; :local ScriptName [ :jobname ]; :global Identity; @@ -30,9 +30,11 @@ :global CertificateAvailable; :global EitherOr; :global EscapeForRegEx; + :global FileExists; :global GetRandom20CharAlNum; :global IfThenElse; :global LogPrint; + :global LogPrintVerbose; :global MAX; :global MIN; :global MkDir; @@ -70,14 +72,14 @@ :local Data false; :for I from=1 to=4 do={ :if ($Data = false) do={ - :do { + :onerror Err { :set Data ([ /tool/fetch check-certificate=yes-without-crl output=user \ ("https://api.telegram.org/bot" . $TelegramTokenId . "/getUpdates?offset=" . \ $TelegramChatOffset->0 . "&allowed_updates=%5B%22message%22%5D") as-value ]->"data"); :set TelegramRandomDelay [ $MAX 0 ($TelegramRandomDelay - 1) ]; - } on-error={ + } do={ :if ($I < 4) do={ - $LogPrint debug $ScriptName ("Fetch failed, " . $I . ". try."); + $LogPrint debug $ScriptName ("Fetch failed, " . $I . ". try: " . $Err); :set TelegramRandomDelay [ $MIN 15 ($TelegramRandomDelay + 5) ]; :delay (($I * $I) . "s"); } @@ -96,17 +98,22 @@ :local Uptime [ /system/resource/get uptime ]; :foreach Update in=($JSON->"result") do={ :set UpdateID ($Update->"update_id"); + $LogPrintVerbose debug $ScriptName ("Update " . $UpdateID . ": " . [ :serialize to=json $Update ]); + :local Message ($Update->"message"); - :local IsReply [ :len ($Message->"reply_to_message") ]; + :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; :local Chat ($Message->"chat"); :local From ($Message->"from"); :local Command ($Message->"text"); + :local ThreadId [ $IfThenElse ($Message->"is_topic_message") ($Message->"message_thread_id") "" ]; :foreach IdsTrusted in=($TelegramChatId, $TelegramChatIdsTrusted) do={ - :if ($From->"id" = $IdsTrusted || $From->"username" = $IdsTrusted) do={ + :if ($From->"id" = $IdsTrusted || \ + $From->"username" = $IdsTrusted || \ + $Chat->"id" = $IdsTrusted) do={ :set Trusted true; } } @@ -115,9 +122,11 @@ :local Done false; :if ($Command = "?") do={ $LogPrint info $ScriptName ("Sending notice for update " . $UpdateID . "."); - $SendTelegram2 ({ origin=$ScriptName; chatid=($Chat->"id"); silent=true; replyto=($Message->"message_id"); \ + $SendTelegram2 ({ origin=$ScriptName; chatid=($Chat->"id"); silent=true; \ + replyto=($Message->"message_id"); threadid=$ThreadId; \ subject=([ $SymbolForNotification "speech-balloon" ] . "Telegram Chat"); \ - message=("Online" . [ $IfThenElse $TelegramChatActive " (and active!)" ] . ", awaiting your commands!") }); + message=([ $IfThenElse ([ :len ($From->"first_name") ] > 0) ("Hello " . ($From->"first_name") . "!\n\n") ] . \ + "Online" . [ $IfThenElse $TelegramChatActive " (and active!)" ] . ", awaiting your commands!") }); :set Done true; } :if ($Done = false && [ :pick $Command 0 1 ] = "!") do={ @@ -130,7 +139,8 @@ " from update " . $UpdateID . "!"); :set Done true; } - :if ($Done = false && ($IsMyReply = 1 || ($IsReply = 0 && $TelegramChatActive = true)) && [ :len $Command ] > 0) do={ + :if ($Done = false && ($IsMyReply = 1 || ($IsAnyReply = false && \ + $TelegramChatActive = true)) && [ :len $Command ] > 0) do={ :if ([ $ValidateSyntax $Command ] = true) do={ :local State ""; :local File ("tmpfs/telegram-chat/" . [ $GetRandom20CharAlNum 6 ]); @@ -145,11 +155,12 @@ :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"); - $SendTelegram2 ({ origin=$ScriptName; chatid=($Chat->"id"); silent=true; replyto=($Message->"message_id"); \ + $SendTelegram2 ({ origin=$ScriptName; chatid=($Chat->"id"); silent=true; \ + replyto=($Message->"message_id"); threadid=$ThreadId; \ subject=([ $SymbolForNotification "speech-balloon" ] . "Telegram Chat"); \ message=([ $SymbolForNotification "gear" ] . "Command:\n" . $Command . "\n\n" . \ $State . [ $IfThenElse ([ :len $Content ] > 0) \ @@ -158,7 +169,8 @@ $RmDir "tmpfs/telegram-chat"; } else={ $LogPrint info $ScriptName ("The command from update " . $UpdateID . " failed syntax validation!"); - $SendTelegram2 ({ origin=$ScriptName; chatid=($Chat->"id"); silent=false; replyto=($Message->"message_id"); \ + $SendTelegram2 ({ origin=$ScriptName; chatid=($Chat->"id"); silent=false; \ + replyto=($Message->"message_id"); threadid=$ThreadId; \ subject=([ $SymbolForNotification "speech-balloon" ] . "Telegram Chat"); \ message=([ $SymbolForNotification "gear" ] . "Command:\n" . $Command . "\n\n" . \ [ $SymbolForNotification "cross-mark" ] . "The command failed syntax validation!") }); @@ -170,7 +182,8 @@ " (ID " . $From->"id" . ") in update " . $UpdateID . "!"); :if ($Command ~ ("^! *" . [ $EscapeForRegEx $Identity ] . "\$")) do={ $LogPrint warning $ScriptName $MessageText; - $SendTelegram2 ({ origin=$ScriptName; chatid=($Chat->"id"); silent=false; replyto=($Message->"message_id"); \ + $SendTelegram2 ({ origin=$ScriptName; chatid=($Chat->"id"); silent=false; \ + replyto=($Message->"message_id"); threadid=$ThreadId; \ subject=([ $SymbolForNotification "speech-balloon" ] . "Telegram Chat"); \ message=("You are not trusted.") }); } else={ @@ -183,6 +196,6 @@ } :set TelegramChatOffset ([ :pick $TelegramChatOffset 1 3 ], \ [ $IfThenElse ($UpdateID >= $TelegramChatOffset->2) ($UpdateID + 1) ($TelegramChatOffset->2) ]); -} on-error={ - :global ExitError; $ExitError $ExitOK [ :jobname ]; +} do={ + :global ExitError; $ExitError $ExitOK [ :jobname ] $Err; } diff --git a/unattended-lte-firmware-upgrade.rsc b/unattended-lte-firmware-upgrade.rsc index 83925fd..237c2d8 100644 --- a/unattended-lte-firmware-upgrade.rsc +++ b/unattended-lte-firmware-upgrade.rsc @@ -12,10 +12,10 @@ :foreach Interface in=[ /interface/lte/find where running ] do={ :local Firmware; :local IntName [ /interface/lte/get $Interface name ]; - :do { + :onerror Err { :set Firmware [ /interface/lte/firmware-upgrade $Interface as-value ]; - } on-error={ - :log debug ("Could not get latest LTE firmware version for interface " . $IntName . "."); + } do={ + :log debug ("Could not get latest LTE firmware version for interface " . $IntName . ": " . $Err); } :if ([ :typeof $Firmware ] = "array") do={ @@ -27,7 +27,7 @@ :set LTEFirmwareUpgrade; /system/scheduler/remove ($1 . "-firmware-upgrade"); - :do { + :onerror Err { /interface/lte/firmware-upgrade $1 upgrade=yes; :log info ("LTE firmware upgrade on '" . $1 . "' finished, waiting for reset."); :delay 240s; @@ -36,8 +36,8 @@ ($Firmware->"installed") != ($Firmware->"latest")) do={ :log warning ("LTE firmware versions still differ. Upgrade failed anyway?"); } - } on-error={ - :log error ("LTE firmware upgrade on '" . $1 . "' failed."); + } do={ + :log error ("LTE firmware upgrade on '" . $1 . "' failed: " . $Err); } } diff --git a/update-gre-address.rsc b/update-gre-address.rsc index cddfa92..dd7d63e 100644 --- a/update-gre-address.rsc +++ b/update-gre-address.rsc @@ -9,11 +9,11 @@ # ipsec remote peer # https://rsc.eworm.de/doc/update-gre-address.md -:global GlobalFunctionsReady; -:while ($GlobalFunctionsReady != true) do={ :delay 500ms; } - :local ExitOK false; -:do { +:onerror Err { + :global GlobalConfigReady; :global GlobalFunctionsReady; + :retry { :if ($GlobalConfigReady != true || $GlobalFunctionsReady != true) \ + do={ :error ("Global config and/or functions not ready."); }; } delay=500ms max=50; :local ScriptName [ :jobname ]; :global CharacterReplace; @@ -41,6 +41,6 @@ } } } -} on-error={ - :global ExitError; $ExitError $ExitOK [ :jobname ]; +} do={ + :global ExitError; $ExitError $ExitOK [ :jobname ] $Err; } diff --git a/update-tunnelbroker.rsc b/update-tunnelbroker.rsc index 45afa6f..9057e1e 100644 --- a/update-tunnelbroker.rsc +++ b/update-tunnelbroker.rsc @@ -11,11 +11,11 @@ # update local address of tunnelbroker interface # https://rsc.eworm.de/doc/update-tunnelbroker.md -:global GlobalFunctionsReady; -:while ($GlobalFunctionsReady != true) do={ :delay 500ms; } - :local ExitOK false; -:do { +:onerror Err { + :global GlobalConfigReady; :global GlobalFunctionsReady; + :retry { :if ($GlobalConfigReady != true || $GlobalFunctionsReady != true) \ + do={ :error ("Global config and/or functions not ready."); }; } delay=500ms max=50; :local ScriptName [ :jobname ]; :global CertificateAvailable; @@ -41,12 +41,12 @@ :for I from=2 to=0 do={ :if ($Data = false) do={ - :do { + :onerror Err { :set Data ([ /tool/fetch check-certificate=yes-without-crl \ ("https://ipv4.tunnelbroker.net/nic/update?hostname=" . $Comment->"id") \ user=($Comment->"user") password=($Comment->"pass") output=user as-value ]->"data"); - } on-error={ - $LogPrint debug $ScriptName ("Failed downloading, " . $I . " retries pending."); + } do={ + $LogPrint debug $ScriptName ("Failed downloading: " . $Err . " - " . $I . " retries pending."); :delay 2s; } } @@ -69,6 +69,6 @@ /interface/6to4/set $Interface local-address=$PublicAddress; } } -} on-error={ - :global ExitError; $ExitError $ExitOK [ :jobname ]; +} do={ + :global ExitError; $ExitError $ExitOK [ :jobname ] $Err; } |