Compare commits

..

No commits in common. "main" and "change-134" have entirely different histories.

109 changed files with 873 additions and 1674 deletions

3
.gitignore vendored
View file

@ -9,8 +9,5 @@
# html files (as generated from markdown) # html files (as generated from markdown)
*.html *.html
# checksums file as used by $ScriptInstallUpdate
checksums.json
# Mac OS X folder settings file # Mac OS X folder settings file
.DS_Store .DS_Store

View file

@ -13,7 +13,7 @@ Installing from branches
> ⚠️ **Warning**: Living on the edge? Great, read on! > ⚠️ **Warning**: Living on the edge? Great, read on!
> If not: Please use the `main` branch and leave this page! > 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 Development and experimental branches are used to provide early access
for specific changes. You can install scripts from these branches for specific changes. You can install scripts from these branches
for testing. for testing.

View file

@ -21,7 +21,7 @@ first step of [installation](README.md#the-long-way-in-detail) is importing
the certificate. the certificate.
The scripts can install additional certificates when required. This happens 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. as a fallback.
Get the certificate's CommonName 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 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 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 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 [git.eworm.de](https://git.eworm.de/) is available. Open that page in the
@ -74,7 +74,6 @@ See also
* [Download, import and update firewall address-lists](doc/fw-addr-lists.md) * [Download, import and update firewall address-lists](doc/fw-addr-lists.md)
* [Manage DNS and DoH servers from netwatch](doc/netwatch-dns.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 Matrix](doc/mod/notification-matrix.md)
* [Send notifications via Ntfy](doc/mod/notification-ntfy.md) * [Send notifications via Ntfy](doc/mod/notification-ntfy.md)

View file

@ -21,8 +21,6 @@ for details!
* [Ben Harris](mailto:mail@bharr.is) (@bharrisau) * [Ben Harris](mailto:mail@bharr.is) (@bharrisau)
* [Daniel Ziegenberg](mailto:daniel@ziegenberg.at) (@ziegenberg) * [Daniel Ziegenberg](mailto:daniel@ziegenberg.at) (@ziegenberg)
* [Ignacio Serrano](mailto:ignic@ignic.com) (@ignic) * [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) * [Michael Gisbers](mailto:michael@gisbers.de) (@mgisbers)
* [Miquel Bonastre](mailto:mbonastre@yahoo.com) (@mbonastre) * [Miquel Bonastre](mailto:mbonastre@yahoo.com) (@mbonastre)
* @netravnen * @netravnen
@ -32,10 +30,9 @@ for details!
## Donations ## Donations
Add yourself to the list, 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 * Abdul Mannan Abbasi
* Alex Maier
* Andrea Ruffini Perico * Andrea Ruffini Perico
* Andrew Cox * Andrew Cox
* Christoph Boss (@Kampfwurst) * Christoph Boss (@Kampfwurst)

View file

@ -42,21 +42,7 @@ Other actions (`disk`, `email`, `remote` or `support`) can be used as
well. I do not recommend using `echo` - use [debug output](#debug-output) well. I do not recommend using `echo` - use [debug output](#debug-output)
instead. instead.
Disable or remove that setting to restore regular logging. Disable or remote 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) [⬅️ Go back to main README](README.md)

View file

@ -10,46 +10,30 @@ Initial commands
[⬅️ Go back to main README](README.md) [⬅️ Go back to main README](README.md)
> ⚠️ **Warning**: These commands are intended for initial setup. If you are > ⚠️ **Warning**: These command are inteneded for initial setup. If you are
> not aware of the procedure please follow > not aware of the procedure please follow
> [the long way in detail](README.md#the-long-way-in-detail). > [the long way in detail](README.md#the-long-way-in-detail).
Run the complete base installation: Run the complete base installation:
{ {
:local BaseUrl "https://git.eworm.de/cgit/routeros-scripts/plain/"; /tool/fetch "https://git.eworm.de/cgit/routeros-scripts/plain/certs/ISRG-Root-X2.pem" dst-path="isrg-root-x2.pem" as-value;
:local CertCommonName "ISRG Root X2"; :delay 1s;
:local CertFileName "ISRG-Root-X2.pem"; /certificate/import file-name=isrg-root-x2.pem passphrase="";
:local CertFingerprint "69729b8e15a86efc177a57afb7171dfc64add28c2fca8cf1507e34453ccb1470"; :if ([ :len [ /certificate/find where fingerprint="69729b8e15a86efc177a57afb7171dfc64add28c2fca8cf1507e34453ccb1470" ] ] != 1) do={
:error "Something is wrong with your certificates!";
: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;
}; };
:put "Renaming global-config-overlay, if exists..."; :delay 1s;
/system/script/set name=("global-config-overlay-" . [ /system/clock/get date ] . "-" . [ /system/clock/get time ]) [ find where name="global-config-overlay" ]; /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={ :foreach Script in={ "global-config"; "global-config-overlay"; "global-functions" } do={
:put "Installing $Script...";
/system/script/remove [ find where name=$Script ]; /system/script/remove [ find where name=$Script ];
/system/script/add name=$Script owner=$Script source=([ /tool/fetch check-certificate=yes-without-crl ($BaseUrl . $Script . ".rsc") output=user as-value]->"data"); /system/script/add name=$Script owner=$Script source=([ /tool/fetch check-certificate=yes-without-crl ("https://git.eworm.de/cgit/routeros-scripts/plain/" . $Script . ".rsc") output=user as-value]->"data");
}; };
:put "Loading configuration and functions...";
/system/script { run global-config; run global-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/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; }"; /system/scheduler/add name="global-scripts" start-time=startup on-event="/system/script { run global-config; run global-functions; }";
:if ([ :len [ /certificate/find where fingerprint=$CertFingerprint ] ] > 0) do={ :global CertificateNameByCN;
:put "Renaming certificate by its common-name..."; $CertificateNameByCN "ISRG Root X2";
:global CertificateNameByCN;
$CertificateNameByCN $CertFingerprint;
};
}; };
Then continue setup with Then continue setup with

View file

@ -9,7 +9,7 @@ WIFI = $(wildcard *.wifi.rsc)
MARKDOWN = $(wildcard *.md doc/*.md doc/mod/*.md) MARKDOWN = $(wildcard *.md doc/*.md doc/mod/*.md)
HTML = $(MARKDOWN:.md=.html) HTML = $(MARKDOWN:.md=.html)
all: $(CAPSMAN) $(LOCAL) $(WIFI) $(HTML) checksums.json all: $(CAPSMAN) $(LOCAL) $(WIFI) $(HTML)
%.html: %.md Makefile %.html: %.md Makefile
markdown $< | sed 's/href="\([-_\./[:alnum:]]*\)\.md"/href="\1.html"/g' > $@ markdown $< | sed 's/href="\([-_\./[:alnum:]]*\)\.md"/href="\1.html"/g' > $@
@ -32,8 +32,5 @@ all: $(CAPSMAN) $(LOCAL) $(WIFI) $(HTML) checksums.json
-e '/^# !!/,/^# !!/c # !! Do not edit this file, it is generated from template!' \ -e '/^# !!/,/^# !!/c # !! Do not edit this file, it is generated from template!' \
< $< > $@ < $< > $@
checksums.json: contrib/checksums.sh *.rsc */*.rsc
contrib/checksums.sh
clean: clean:
rm -f $(HTML) checksums.json rm -f $(HTML)

157
README.md
View file

@ -10,14 +10,13 @@ RouterOS Scripts
![RouterOS Scripts Logo](logo.svg) ![RouterOS Scripts Logo](logo.svg)
[RouterOS ↗️](https://mikrotik.com/software) is the operating system developed [RouterOS](https://mikrotik.com/software) is the operating system developed
by [MikroTik ↗️](https://mikrotik.com/aboutus) for networking tasks. This by [MikroTik](https://mikrotik.com/aboutus) for networking tasks. This
repository holds a number of [scripts ↗️](https://wiki.mikrotik.com/wiki/Manual:Scripting) repository holds a number of [scripts](https://wiki.mikrotik.com/wiki/Manual:Scripting)
to manage RouterOS devices or extend their functionality. to manage RouterOS devices or extend their functionality.
*Use at your own risk*, pay attention to *Use at your own risk*, pay attention to
[license and warranty](#license-and-warranty), and [license and warranty](#license-and-warranty)!
[disclaimer on external links](#disclaimer-on-external-links)!
Requirements Requirements
------------ ------------
@ -36,7 +35,7 @@ Specific scripts may require even newer RouterOS version.
> running RouterOS v6 switch to `routeros-v6` branch! > running RouterOS v6 switch to `routeros-v6` branch!
Starting with RouterOS 7.17 the 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 has been extended to give more fine-grained control over what features are
available. You need to enable `scheduler` and `fetch` at least, specific available. You need to enable `scheduler` and `fetch` at least, specific
scripts may require additional features. scripts may require additional features.
@ -62,9 +61,9 @@ First time users should take the long way below.
### Live presentation ### Live presentation
Want to see it in action? I've had a presentation [Repository based 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 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 > ⚠️ **Warning**: Some details changed. So see the presentation, then follow
> the steps below for up-to-date commands. > the steps below for up-to-date commands.
@ -72,15 +71,7 @@ including demonstation recorded live at [MUM Europe
### The long way in detail ### The long way in detail
The update script does server certificate verification, so first step is to The update script does server certificate verification, so first step is to
download the certificates. download the certificates. If you intend to download the scripts from a
> 💡️ **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 different location (for example from github.com) install the corresponding
certificate chain. certificate chain.
@ -92,11 +83,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 want to be safe download with your workstations's browser and transfer the
file to your MikroTik device. 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. 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 Do not worry that the command is not shown - that happens because it contains
a sensitive property, the passphrase. a sensitive property, the passphrase.
@ -114,8 +105,6 @@ is shown.
Always make sure there are no certificates installed you do not know or want! 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 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 certificate's lifetime is checked with local time, so make sure the device's
date and time is set correctly! date and time is set correctly!
@ -133,9 +122,6 @@ And finally load configuration and functions and add the scheduler.
![screenshot: run and schedule scripts](README.d/05-run-and-schedule-scripts.avif) ![screenshot: run and schedule scripts](README.d/05-run-and-schedule-scripts.avif)
> 💡️ **Hint**: You see complaints regarding syntax errors? Most likely the
> RouterOS on your device is too old. Check for updates!
### Scheduled automatic updates ### Scheduled automatic updates
The last step is optional: Add this scheduler **only** if you want the The last step is optional: Add this scheduler **only** if you want the
@ -205,7 +191,7 @@ Scheduler and events
-------------------- --------------------
Most scripts are designed to run regularly from 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 added `check-routeros-update`, so let's run it daily to make sure not to
miss an update. miss an update.
@ -228,62 +214,60 @@ There's much more to explore... Have fun!
Available scripts Available scripts
----------------- -----------------
* [Find and remove access list duplicates](doc/accesslist-duplicates.md) (`accesslist-duplicates`) * [Find and remove access list duplicates](doc/accesslist-duplicates.md)
* [Upload backup to Mikrotik cloud](doc/backup-cloud.md) (`backup-cloud`) * [Upload backup to Mikrotik cloud](doc/backup-cloud.md)
* [Send backup via e-mail](doc/backup-email.md) (`backup-email`) * [Send backup via e-mail](doc/backup-email.md)
* [Save configuration to fallback partition](doc/backup-partition.md) (`backup-partition`) * [Save configuration to fallback partition](doc/backup-partition.md)
* [Upload backup to server](doc/backup-upload.md) (`backup-upload`) * [Upload backup to server](doc/backup-upload.md)
* [Download packages for CAP upgrade from CAPsMAN](doc/capsman-download-packages.md) (`capsman-download-packages`) * [Download packages for CAP upgrade from CAPsMAN](doc/capsman-download-packages.md)
* [Run rolling CAP upgrades from CAPsMAN](doc/capsman-rolling-upgrade.md) (`capsman-rolling-upgrade`) * [Run rolling CAP upgrades from CAPsMAN](doc/capsman-rolling-upgrade.md)
* [Renew locally issued certificates](doc/certificate-renew-issued.md) (`certificate-renew-issued`) * [Renew locally issued certificates](doc/certificate-renew-issued.md)
* [Renew certificates and notify on expiration](doc/check-certificates.md) (`check-certificates`) * [Renew certificates and notify on expiration](doc/check-certificates.md)
* [Notify about health state](doc/check-health.md) (`check-health`) * [Notify about health state](doc/check-health.md)
* [Notify on LTE firmware upgrade](doc/check-lte-firmware-upgrade.md) (`check-lte-firmware-upgrade`) * [Notify on LTE firmware upgrade](doc/check-lte-firmware-upgrade.md)
* [Check perpetual license on CHR](doc/check-perpetual-license.md) (`check-perpetual-license`) * [Notify on RouterOS update](doc/check-routeros-update.md)
* [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 MAC addresses in wireless access list](doc/collect-wireless-mac.md) (`collect-wireless-mac`) * [Use wireless network with daily psk](doc/daily-psk.md)
* [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)
* [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)
* [Create DNS records for DHCP leases](doc/dhcp-to-dns.md) (`dhcp-to-dns`) * [Automatically upgrade firmware and reboot](doc/firmware-upgrade-reboot.md)
* [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)
* [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)
* [Wait for global functions und modules](doc/global-wait.md) (`global-wait`) * [Send GPS position to server](doc/gps-track.md)
* [Send GPS position to server](doc/gps-track.md) (`gps-track`) * [Use WPA network with hotspot credentials](doc/hotspot-to-wpa.md)
* [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)
* [Create DNS records for IPSec peers](doc/ipsec-to-dns.md) (`ipsec-to-dns`) * [Update configuration on IPv6 prefix change](doc/ipv6-update.md)
* [Update configuration on IPv6 prefix change](doc/ipv6-update.md) (`ipv6-update`) * [Manage IP addresses with bridge status](doc/ip-addr-bridge.md)
* [Manage IP addresses with bridge status](doc/ip-addr-bridge.md) (`ip-addr-bridge`) * [Run other scripts on DHCP lease](doc/lease-script.md)
* [Run other scripts on DHCP lease](doc/lease-script.md) (`lease-script`) * [Manage LEDs dark mode](doc/leds-mode.md)
* [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)
* [Forward log messages via notification](doc/log-forward.md) (`log-forward`) * [Mode button with multiple presses](doc/mode-button.md)
* [Mode button with multiple presses](doc/mode-button.md) (`mode-button`) * [Manage DNS and DoH servers from netwatch](doc/netwatch-dns.md)
* [Manage DNS and DoH servers from netwatch](doc/netwatch-dns.md) (`netwatch-dns`) * [Notify on host up and down](doc/netwatch-notify.md)
* [Notify on host up and down](doc/netwatch-notify.md) (`netwatch-notify`) * [Visualize OSPF state via LEDs](doc/ospf-to-leds.md)
* [Visualize OSPF state via LEDs](doc/ospf-to-leds.md) (`ospf-to-leds`) * [Manage system update](doc/packages-update.md)
* [Manage system update](doc/packages-update.md) (`packages-update`) * [Run scripts on ppp connection](doc/ppp-on-up.md)
* [Run scripts on ppp connection](doc/ppp-on-up.md) (`ppp-on-up`) * [Act on received SMS](doc/sms-action.md)
* [Act on received SMS](doc/sms-action.md) (`sms-action`) * [Forward received SMS](doc/sms-forward.md)
* [Forward received SMS](doc/sms-forward.md) (`sms-forward`) * [Play Super Mario theme](doc/super-mario-theme.md)
* [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)
* [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)
* [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 configuration with dynamic addresses](doc/update-gre-address.md) (`update-gre-address`) * [Update tunnelbroker configuration](doc/update-tunnelbroker.md)
* [Update tunnelbroker configuration](doc/update-tunnelbroker.md) (`update-tunnelbroker`)
Available modules Available modules
----------------- -----------------
* [Manage ports in bridge](doc/mod/bridge-port-to.md) (`mod/bridge-port-to`) * [Manage ports in bridge](doc/mod/bridge-port-to.md)
* [Manage VLANs on bridge ports](doc/mod/bridge-port-vlan.md) (`mod/bridge-port-vlan`) * [Manage VLANs on bridge ports](doc/mod/bridge-port-vlan.md)
* [Inspect variables](doc/mod/inspectvar.md) (`mod/inspectvar`) * [Inspect variables](doc/mod/inspectvar.md)
* [IP address calculation](doc/mod/ipcalc.md) (`mod/ipcalc`) * [IP address calculation](doc/mod/ipcalc.md)
* [Send notifications via e-mail](doc/mod/notification-email.md) (`mod/notification-email`) * [Send notifications via e-mail](doc/mod/notification-email.md)
* [Send notifications via Gotify](doc/mod/notification-gotify.md) (`mod/notification-gotify`) * [Send notifications via Matrix](doc/mod/notification-matrix.md)
* [Send notifications via Matrix](doc/mod/notification-matrix.md) (`mod/notification-matrix`) * [Send notifications via Ntfy](doc/mod/notification-ntfy.md)
* [Send notifications via Ntfy](doc/mod/notification-ntfy.md) (`mod/notification-ntfy`) * [Send notifications via Telegram](doc/mod/notification-telegram.md)
* [Send notifications via Telegram](doc/mod/notification-telegram.md) (`mod/notification-telegram`) * [Download script and run it once](doc/mod/scriptrunonce.md)
* [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)
* [Import ssh keys for public key authentication](doc/mod/ssh-keys-import.md) (`mod/ssh-keys-import`)
Installing custom scripts & modules Installing custom scripts & modules
----------------------------------- -----------------------------------
@ -340,7 +324,7 @@ Possibly a scheduler and other configuration has to be removed as well.
Contact 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)!
[![RouterOS Scripts Telegram Group](README.d/telegram-group.avif)](https://t.me/routeros_scripts) [![RouterOS Scripts Telegram Group](README.d/telegram-group.avif)](https://t.me/routeros_scripts)
@ -364,7 +348,7 @@ at github.
This project is developed in private spare time and usage is free of charge 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 for you. If you like the scripts and think this is of value for you or your
business please consider to 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).
[![donate with PayPal](https://img.shields.io/badge/Like_it%3F-Donate!-orange?logo=githubsponsors&logoColor=orange&style=for-the-badge)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=A4ZXBD6YS2W8J) [![donate with PayPal](https://img.shields.io/badge/Like_it%3F-Donate!-orange?logo=githubsponsors&logoColor=orange&style=for-the-badge)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=A4ZXBD6YS2W8J)
@ -383,21 +367,6 @@ but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
[GNU General Public License](COPYING.md) for more details. [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 Upstream
-------- --------

View file

@ -10,11 +10,11 @@
# #
# !! Do not edit this file, it is generated from template! # !! Do not edit this file, it is generated from template!
:global GlobalFunctionsReady;
:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
:local ExitOK false; :local ExitOK false;
:onerror Err { :do {
: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 ScriptName [ :jobname ];
:local Seen ({}); :local Seen ({});
@ -22,7 +22,7 @@
:foreach AccList in=[ /caps-man/access-list/find where mac-address!="00:00:00:00:00:00" ] do={ :foreach AccList in=[ /caps-man/access-list/find where mac-address!="00:00:00:00:00:00" ] do={
:local Mac [ /caps-man/access-list/get $AccList mac-address ]; :local Mac [ /caps-man/access-list/get $AccList mac-address ];
:if ($Seen->$Mac = 1) do={ :if ($Seen->$Mac = 1) do={
/caps-man/access-list/print without-paging where mac-address=$Mac; /caps-man/access-list/print where mac-address=$Mac;
:local Remove [ :tonum [ /terminal/ask prompt="\nNumeric id to remove, any key to skip!" ] ]; :local Remove [ :tonum [ /terminal/ask prompt="\nNumeric id to remove, any key to skip!" ] ];
:if ([ :typeof $Remove ] = "num") do={ :if ([ :typeof $Remove ] = "num") do={
@ -32,6 +32,6 @@
} }
:set ($Seen->$Mac) 1; :set ($Seen->$Mac) 1;
} }
} do={ } on-error={
:global ExitError; $ExitError $ExitOK [ :jobname ] $Err; :global ExitError; $ExitError $ExitOK [ :jobname ];
} }

View file

@ -10,11 +10,11 @@
# #
# !! Do not edit this file, it is generated from template! # !! Do not edit this file, it is generated from template!
:global GlobalFunctionsReady;
:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
:local ExitOK false; :local ExitOK false;
:onerror Err { :do {
: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 ScriptName [ :jobname ];
:local Seen ({}); :local Seen ({});
@ -22,7 +22,7 @@
:foreach AccList in=[ /interface/wireless/access-list/find where mac-address!="00:00:00:00:00:00" ] do={ :foreach AccList in=[ /interface/wireless/access-list/find where mac-address!="00:00:00:00:00:00" ] do={
:local Mac [ /interface/wireless/access-list/get $AccList mac-address ]; :local Mac [ /interface/wireless/access-list/get $AccList mac-address ];
:if ($Seen->$Mac = 1) do={ :if ($Seen->$Mac = 1) do={
/interface/wireless/access-list/print without-paging where mac-address=$Mac; /interface/wireless/access-list/print where mac-address=$Mac;
:local Remove [ :tonum [ /terminal/ask prompt="\nNumeric id to remove, any key to skip!" ] ]; :local Remove [ :tonum [ /terminal/ask prompt="\nNumeric id to remove, any key to skip!" ] ];
:if ([ :typeof $Remove ] = "num") do={ :if ([ :typeof $Remove ] = "num") do={
@ -32,6 +32,6 @@
} }
:set ($Seen->$Mac) 1; :set ($Seen->$Mac) 1;
} }
} do={ } on-error={
:global ExitError; $ExitError $ExitOK [ :jobname ] $Err; :global ExitError; $ExitError $ExitOK [ :jobname ];
} }

View file

@ -11,11 +11,11 @@
# !! This is just a template to generate the real script! # !! This is just a template to generate the real script!
# !! Pattern '%TEMPL%' is replaced, paths are filtered. # !! Pattern '%TEMPL%' is replaced, paths are filtered.
:global GlobalFunctionsReady;
:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
:local ExitOK false; :local ExitOK false;
:onerror Err { :do {
: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 ScriptName [ :jobname ];
:local Seen ({}); :local Seen ({});
@ -27,9 +27,9 @@
:local Mac [ /interface/wifi/access-list/get $AccList mac-address ]; :local Mac [ /interface/wifi/access-list/get $AccList mac-address ];
:local Mac [ /interface/wireless/access-list/get $AccList mac-address ]; :local Mac [ /interface/wireless/access-list/get $AccList mac-address ];
:if ($Seen->$Mac = 1) do={ :if ($Seen->$Mac = 1) do={
/caps-man/access-list/print without-paging where mac-address=$Mac; /caps-man/access-list/print where mac-address=$Mac;
/interface/wifi/access-list/print without-paging where mac-address=$Mac; /interface/wifi/access-list/print where mac-address=$Mac;
/interface/wireless/access-list/print without-paging where mac-address=$Mac; /interface/wireless/access-list/print where mac-address=$Mac;
:local Remove [ :tonum [ /terminal/ask prompt="\nNumeric id to remove, any key to skip!" ] ]; :local Remove [ :tonum [ /terminal/ask prompt="\nNumeric id to remove, any key to skip!" ] ];
:if ([ :typeof $Remove ] = "num") do={ :if ([ :typeof $Remove ] = "num") do={
@ -41,6 +41,6 @@
} }
:set ($Seen->$Mac) 1; :set ($Seen->$Mac) 1;
} }
} do={ } on-error={
:global ExitError; $ExitError $ExitOK [ :jobname ] $Err; :global ExitError; $ExitError $ExitOK [ :jobname ];
} }

View file

@ -10,11 +10,11 @@
# #
# !! Do not edit this file, it is generated from template! # !! Do not edit this file, it is generated from template!
:global GlobalFunctionsReady;
:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
:local ExitOK false; :local ExitOK false;
:onerror Err { :do {
: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 ScriptName [ :jobname ];
:local Seen ({}); :local Seen ({});
@ -22,7 +22,7 @@
:foreach AccList in=[ /interface/wifi/access-list/find where mac-address!="00:00:00:00:00:00" ] do={ :foreach AccList in=[ /interface/wifi/access-list/find where mac-address!="00:00:00:00:00:00" ] do={
:local Mac [ /interface/wifi/access-list/get $AccList mac-address ]; :local Mac [ /interface/wifi/access-list/get $AccList mac-address ];
:if ($Seen->$Mac = 1) do={ :if ($Seen->$Mac = 1) do={
/interface/wifi/access-list/print without-paging where mac-address=$Mac; /interface/wifi/access-list/print where mac-address=$Mac;
:local Remove [ :tonum [ /terminal/ask prompt="\nNumeric id to remove, any key to skip!" ] ]; :local Remove [ :tonum [ /terminal/ask prompt="\nNumeric id to remove, any key to skip!" ] ];
:if ([ :typeof $Remove ] = "num") do={ :if ([ :typeof $Remove ] = "num") do={
@ -32,6 +32,6 @@
} }
:set ($Seen->$Mac) 1; :set ($Seen->$Mac) 1;
} }
} do={ } on-error={
:global ExitError; $ExitError $ExitOK [ :jobname ] $Err; :global ExitError; $ExitError $ExitOK [ :jobname ];
} }

View file

@ -9,11 +9,11 @@
# upload backup to MikroTik cloud # upload backup to MikroTik cloud
# https://rsc.eworm.de/doc/backup-cloud.md # https://rsc.eworm.de/doc/backup-cloud.md
:global GlobalFunctionsReady;
:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
:local ExitOK false; :local ExitOK false;
:onerror Err { :do {
: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 ScriptName [ :jobname ];
:global BackupRandomDelay; :global BackupRandomDelay;
@ -99,6 +99,6 @@
:set PackagesUpdateBackupFailure true; :set PackagesUpdateBackupFailure true;
} }
$RmDir "tmpfs/backup-cloud"; $RmDir "tmpfs/backup-cloud";
} do={ } on-error={
:global ExitError; $ExitError $ExitOK [ :jobname ] $Err; :global ExitError; $ExitError $ExitOK [ :jobname ];
} }

View file

@ -9,11 +9,11 @@
# create and email backup and config file # create and email backup and config file
# https://rsc.eworm.de/doc/backup-email.md # https://rsc.eworm.de/doc/backup-email.md
:global GlobalFunctionsReady;
:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
:local ExitOK false; :local ExitOK false;
:onerror Err { :do {
: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 ScriptName [ :jobname ];
:global BackupPassword; :global BackupPassword;
@ -27,7 +27,6 @@
:global CleanName; :global CleanName;
:global DeviceInfo; :global DeviceInfo;
:global FileExists;
:global FormatLine; :global FormatLine;
:global LogPrint; :global LogPrint;
:global MkDir; :global MkDir;
@ -125,19 +124,17 @@
attach=$Attach; remove-attach=true }); attach=$Attach; remove-attach=true });
# wait for the mail to be sent # wait for the mail to be sent
:do { :local I 0;
:retry { :while ([ :len [ /file/find where name ~ ($FilePath . "\\.(backup|rsc)\$") ] ] > 0) do={
:if ([ $FileExists ($FilePath . ".conf") ".conf file" ] = true || \ :if ($I >= 120) do={
[ $FileExists ($FilePath . ".backup") "backup" ] = true || \ $LogPrint warning $ScriptName ("Files are still available, sending e-mail failed.");
[ $FileExists ($FilePath . ".rsc") "script" ] = true) do={ :set PackagesUpdateBackupFailure true;
:error "Files are still available."; :set ExitOK true;
} :error false;
} delay=1s max=120; }
} on-error={ :delay 1s;
$LogPrint warning $ScriptName ("Files are still available, sending e-mail failed."); :set I ($I + 1);
:set PackagesUpdateBackupFailure true;
} }
# do not remove the files here, as the mail is still queued! } on-error={
} do={ :global ExitError; $ExitError $ExitOK [ :jobname ];
:global ExitError; $ExitError $ExitOK [ :jobname ] $Err;
} }

View file

@ -10,11 +10,11 @@
# save configuration to fallback partition # save configuration to fallback partition
# https://rsc.eworm.de/doc/backup-partition.md # https://rsc.eworm.de/doc/backup-partition.md
:global GlobalFunctionsReady;
:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
:local ExitOK false; :local ExitOK false;
:onerror Err { :do {
: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 ScriptName [ :jobname ];
:global BackupPartitionCopyBeforeFeatureUpdate; :global BackupPartitionCopyBeforeFeatureUpdate;
@ -32,15 +32,14 @@
:global LogPrint; :global LogPrint;
:onerror Err { :do {
/partitions/copy-to $FallbackTo; /partitions/copy-to $FallbackTo;
$LogPrint info $ScriptName ("Copied RouterOS to partition '" . $FallbackToName . "'."); $LogPrint info $ScriptName ("Copied RouterOS to partition '" . $FallbackToName . "'.");
} do={ :return true;
$LogPrint error $ScriptName ("Failed copying RouterOS to partition '" . \ } on-error={
$FallbackToName . "': " . $Err); $LogPrint error $ScriptName ("Failed copying RouterOS to partition '" . $FallbackToName . "'!");
:return false; :return false;
} }
:return true;
} }
:if ([ $ScriptLock $ScriptName ] = false) do={ :if ([ $ScriptLock $ScriptName ] = false) do={
@ -108,21 +107,20 @@
} }
} }
:onerror Err { :do {
/system/scheduler/add start-time=startup name="running-from-backup-partition" \ /system/scheduler/add start-time=startup name="running-from-backup-partition" \
on-event=(":log warning (\"Running from partition '\" . " . \ on-event=(":log warning (\"Running from partition '\" . " . \
"[ /partitions/get [ find where running ] name ] . \"'!\")"); "[ /partitions/get [ find where running ] name ] . \"'!\")");
/partitions/save-config-to $FallbackTo; /partitions/save-config-to $FallbackTo;
/system/scheduler/remove "running-from-backup-partition"; /system/scheduler/remove "running-from-backup-partition";
$LogPrint info $ScriptName ("Saved configuration to partition '" . $FallbackToName . "'."); $LogPrint info $ScriptName ("Saved configuration to partition '" . $FallbackToName . "'.");
} do={ } on-error={
/system/scheduler/remove [ find where name="running-from-backup-partition" ]; /system/scheduler/remove [ find where name="running-from-backup-partition" ];
$LogPrint error $ScriptName ("Failed saving configuration to partition '" . \ $LogPrint error $ScriptName ("Failed saving configuration to partition '" . $FallbackToName . "'!");
$FallbackToName . "': " . $Err);
:set PackagesUpdateBackupFailure true; :set PackagesUpdateBackupFailure true;
:set ExitOK true; :set ExitOK true;
:error false; :error false;
} }
} do={ } on-error={
:global ExitError; $ExitError $ExitOK [ :jobname ] $Err; :global ExitError; $ExitError $ExitOK [ :jobname ];
} }

View file

@ -10,11 +10,11 @@
# create and upload backup and config file # create and upload backup and config file
# https://rsc.eworm.de/doc/backup-upload.md # https://rsc.eworm.de/doc/backup-upload.md
:global GlobalFunctionsReady;
:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
:local ExitOK false; :local ExitOK false;
:onerror Err { :do {
: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 ScriptName [ :jobname ];
:global BackupPassword; :global BackupPassword;
@ -90,13 +90,13 @@
/system/backup/save encryption=aes-sha256 name=$FilePath password=$BackupPassword; /system/backup/save encryption=aes-sha256 name=$FilePath password=$BackupPassword;
$WaitForFile ($FilePath . ".backup"); $WaitForFile ($FilePath . ".backup");
:onerror Err { :do {
/tool/fetch upload=yes url=($BackupUploadUrl . "/" . $FileName . ".backup") \ /tool/fetch upload=yes url=($BackupUploadUrl . "/" . $FileName . ".backup") \
user=$BackupUploadUser password=$BackupUploadPass src-path=($FilePath . ".backup"); user=$BackupUploadUser password=$BackupUploadPass src-path=($FilePath . ".backup");
:set BackupFile [ /file/get ($FilePath . ".backup") ]; :set BackupFile [ /file/get ($FilePath . ".backup") ];
:set ($BackupFile->"name") ($FileName . ".backup"); :set ($BackupFile->"name") ($FileName . ".backup");
} do={ } on-error={
$LogPrint error $ScriptName ("Uploading backup file failed: " . $Err); $LogPrint error $ScriptName ("Uploading backup file failed!");
:set BackupFile "failed"; :set BackupFile "failed";
:set Failed 1; :set Failed 1;
} }
@ -109,13 +109,13 @@
/export terse show-sensitive file=$FilePath; /export terse show-sensitive file=$FilePath;
$WaitForFile ($FilePath . ".rsc"); $WaitForFile ($FilePath . ".rsc");
:onerror Err { :do {
/tool/fetch upload=yes url=($BackupUploadUrl . "/" . $FileName . ".rsc") \ /tool/fetch upload=yes url=($BackupUploadUrl . "/" . $FileName . ".rsc") \
user=$BackupUploadUser password=$BackupUploadPass src-path=($FilePath . ".rsc"); user=$BackupUploadUser password=$BackupUploadPass src-path=($FilePath . ".rsc");
:set ExportFile [ /file/get ($FilePath . ".rsc") ]; :set ExportFile [ /file/get ($FilePath . ".rsc") ];
:set ($ExportFile->"name") ($FileName . ".rsc"); :set ($ExportFile->"name") ($FileName . ".rsc");
} do={ } on-error={
$LogPrint error $ScriptName ("Uploading configuration export failed: " . $Err); $LogPrint error $ScriptName ("Uploading configuration export failed!");
:set ExportFile "failed"; :set ExportFile "failed";
:set Failed 1; :set Failed 1;
} }
@ -130,13 +130,13 @@
file=($FilePath . ".conf\00"); file=($FilePath . ".conf\00");
$WaitForFile ($FilePath . ".conf"); $WaitForFile ($FilePath . ".conf");
:onerror Err { :do {
/tool/fetch upload=yes url=($BackupUploadUrl . "/" . $FileName . ".conf") \ /tool/fetch upload=yes url=($BackupUploadUrl . "/" . $FileName . ".conf") \
user=$BackupUploadUser password=$BackupUploadPass src-path=($FilePath . ".conf"); user=$BackupUploadUser password=$BackupUploadPass src-path=($FilePath . ".conf");
:set ConfigFile [ /file/get ($FilePath . ".conf") ]; :set ConfigFile [ /file/get ($FilePath . ".conf") ];
:set ($ConfigFile->"name") ($FileName . ".conf"); :set ($ConfigFile->"name") ($FileName . ".conf");
} do={ } on-error={
$LogPrint error $ScriptName ("Uploading global-config-overlay failed: " . $Err); $LogPrint error $ScriptName ("Uploading global-config-overlay failed!");
:set ConfigFile "failed"; :set ConfigFile "failed";
:set Failed 1; :set Failed 1;
} }
@ -173,6 +173,6 @@
:set PackagesUpdateBackupFailure true; :set PackagesUpdateBackupFailure true;
} }
$RmDir $DirName; $RmDir $DirName;
} do={ } on-error={
:global ExitError; $ExitError $ExitOK [ :jobname ] $Err; :global ExitError; $ExitError $ExitOK [ :jobname ];
} }

View file

@ -11,16 +11,15 @@
# #
# !! Do not edit this file, it is generated from template! # !! Do not edit this file, it is generated from template!
:global GlobalFunctionsReady;
:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
:local ExitOK false; :local ExitOK false;
:onerror Err { :do {
: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 ScriptName [ :jobname ];
:global CleanFilePath; :global CleanFilePath;
:global DownloadPackage; :global DownloadPackage;
:global FileGet;
:global LogPrint; :global LogPrint;
:global MkDir; :global MkDir;
:global RmFile; :global RmFile;
@ -43,7 +42,7 @@
:error false; :error false;
} }
:if ([ $FileGet $PackagePath ] = false) do={ :if ([ :len [ /file/find where name=$PackagePath type="directory" ] ] = 0) do={
:if ([ $MkDir $PackagePath ] = false) do={ :if ([ $MkDir $PackagePath ] = false) do={
$LogPrint warning $ScriptName ("Creating directory at CAPsMAN package path (" . \ $LogPrint warning $ScriptName ("Creating directory at CAPsMAN package path (" . \
$PackagePath . ") failed!"); $PackagePath . ") failed!");
@ -54,7 +53,7 @@
"). Please place your packages!"); "). 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={ package-version!=$InstalledVersion name~("^" . $PackagePath) ] do={
:local File [ /file/get $Package ]; :local File [ /file/get $Package ];
:if ($File->"package-architecture" = "mips") do={ :if ($File->"package-architecture" = "mips") do={
@ -63,11 +62,11 @@
:if ([ $DownloadPackage ($File->"package-name") $InstalledVersion \ :if ([ $DownloadPackage ($File->"package-name") $InstalledVersion \
($File->"package-architecture") $PackagePath ] = true) do={ ($File->"package-architecture") $PackagePath ] = true) do={
:set Updated true; :set Updated true;
$RmFile ($File->"name"); $RmFile $Package;
} }
} }
: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."); $LogPrint info $ScriptName ("No packages available, downloading default set.");
:foreach Arch in={ "arm"; "mipsbe" } do={ :foreach Arch in={ "arm"; "mipsbe" } do={
:foreach Package in={ "routeros"; "wireless" } do={ :foreach Package in={ "routeros"; "wireless" } do={
@ -88,6 +87,6 @@
/caps-man/remote-cap/upgrade [ find where version!=$InstalledVersion ]; /caps-man/remote-cap/upgrade [ find where version!=$InstalledVersion ];
} }
} }
} do={ } on-error={
:global ExitError; $ExitError $ExitOK [ :jobname ] $Err; :global ExitError; $ExitError $ExitOK [ :jobname ];
} }

View file

@ -12,16 +12,15 @@
# !! This is just a template to generate the real script! # !! This is just a template to generate the real script!
# !! Pattern '%TEMPL%' is replaced, paths are filtered. # !! Pattern '%TEMPL%' is replaced, paths are filtered.
:global GlobalFunctionsReady;
:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
:local ExitOK false; :local ExitOK false;
:onerror Err { :do {
: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 ScriptName [ :jobname ];
:global CleanFilePath; :global CleanFilePath;
:global DownloadPackage; :global DownloadPackage;
:global FileGet;
:global LogPrint; :global LogPrint;
:global MkDir; :global MkDir;
:global RmFile; :global RmFile;
@ -45,7 +44,7 @@
:error false; :error false;
} }
:if ([ $FileGet $PackagePath ] = false) do={ :if ([ :len [ /file/find where name=$PackagePath type="directory" ] ] = 0) do={
:if ([ $MkDir $PackagePath ] = false) do={ :if ([ $MkDir $PackagePath ] = false) do={
$LogPrint warning $ScriptName ("Creating directory at CAPsMAN package path (" . \ $LogPrint warning $ScriptName ("Creating directory at CAPsMAN package path (" . \
$PackagePath . ") failed!"); $PackagePath . ") failed!");
@ -56,7 +55,7 @@
"). Please place your packages!"); "). 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={ package-version!=$InstalledVersion name~("^" . $PackagePath) ] do={
:local File [ /file/get $Package ]; :local File [ /file/get $Package ];
:if ($File->"package-architecture" = "mips") do={ :if ($File->"package-architecture" = "mips") do={
@ -65,11 +64,11 @@
:if ([ $DownloadPackage ($File->"package-name") $InstalledVersion \ :if ([ $DownloadPackage ($File->"package-name") $InstalledVersion \
($File->"package-architecture") $PackagePath ] = true) do={ ($File->"package-architecture") $PackagePath ] = true) do={
:set Updated true; :set Updated true;
$RmFile ($File->"name"); $RmFile $Package;
} }
} }
: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."); $LogPrint info $ScriptName ("No packages available, downloading default set.");
# NOT /interface/wifi/ # # NOT /interface/wifi/ #
:foreach Arch in={ "arm"; "mipsbe" } do={ :foreach Arch in={ "arm"; "mipsbe" } do={
@ -99,6 +98,6 @@
/interface/wifi/capsman/remote-cap/upgrade [ find where version!=$InstalledVersion ]; /interface/wifi/capsman/remote-cap/upgrade [ find where version!=$InstalledVersion ];
} }
} }
} do={ } on-error={
:global ExitError; $ExitError $ExitOK [ :jobname ] $Err; :global ExitError; $ExitError $ExitOK [ :jobname ];
} }

View file

@ -11,16 +11,15 @@
# #
# !! Do not edit this file, it is generated from template! # !! Do not edit this file, it is generated from template!
:global GlobalFunctionsReady;
:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
:local ExitOK false; :local ExitOK false;
:onerror Err { :do {
: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 ScriptName [ :jobname ];
:global CleanFilePath; :global CleanFilePath;
:global DownloadPackage; :global DownloadPackage;
:global FileGet;
:global LogPrint; :global LogPrint;
:global MkDir; :global MkDir;
:global RmFile; :global RmFile;
@ -43,7 +42,7 @@
:error false; :error false;
} }
:if ([ $FileGet $PackagePath ] = false) do={ :if ([ :len [ /file/find where name=$PackagePath type="directory" ] ] = 0) do={
:if ([ $MkDir $PackagePath ] = false) do={ :if ([ $MkDir $PackagePath ] = false) do={
$LogPrint warning $ScriptName ("Creating directory at CAPsMAN package path (" . \ $LogPrint warning $ScriptName ("Creating directory at CAPsMAN package path (" . \
$PackagePath . ") failed!"); $PackagePath . ") failed!");
@ -54,7 +53,7 @@
"). Please place your packages!"); "). 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={ package-version!=$InstalledVersion name~("^" . $PackagePath) ] do={
:local File [ /file/get $Package ]; :local File [ /file/get $Package ];
:if ($File->"package-architecture" = "mips") do={ :if ($File->"package-architecture" = "mips") do={
@ -63,11 +62,11 @@
:if ([ $DownloadPackage ($File->"package-name") $InstalledVersion \ :if ([ $DownloadPackage ($File->"package-name") $InstalledVersion \
($File->"package-architecture") $PackagePath ] = true) do={ ($File->"package-architecture") $PackagePath ] = true) do={
:set Updated true; :set Updated true;
$RmFile ($File->"name"); $RmFile $Package;
} }
} }
: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."); $LogPrint info $ScriptName ("No packages available, downloading default set.");
:foreach Arch in={ "arm"; "arm64" } do={ :foreach Arch in={ "arm"; "arm64" } do={
:local Packages { "arm"={ "routeros"; "wifi-qcom"; "wifi-qcom-ac" }; :local Packages { "arm"={ "routeros"; "wifi-qcom"; "wifi-qcom-ac" };
@ -90,6 +89,6 @@
/interface/wifi/capsman/remote-cap/upgrade [ find where version!=$InstalledVersion ]; /interface/wifi/capsman/remote-cap/upgrade [ find where version!=$InstalledVersion ];
} }
} }
} do={ } on-error={
:global ExitError; $ExitError $ExitOK [ :jobname ] $Err; :global ExitError; $ExitError $ExitOK [ :jobname ];
} }

View file

@ -12,11 +12,11 @@
# #
# !! Do not edit this file, it is generated from template! # !! Do not edit this file, it is generated from template!
:global GlobalFunctionsReady;
:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
:local ExitOK false; :local ExitOK false;
:onerror Err { :do {
: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 ScriptName [ :jobname ];
:global LogPrint; :global LogPrint;
@ -45,6 +45,6 @@
:delay ($Delay . "s"); :delay ($Delay . "s");
} }
} }
} do={ } on-error={
:global ExitError; $ExitError $ExitOK [ :jobname ] $Err; :global ExitError; $ExitError $ExitOK [ :jobname ];
} }

View file

@ -13,11 +13,11 @@
# !! This is just a template to generate the real script! # !! This is just a template to generate the real script!
# !! Pattern '%TEMPL%' is replaced, paths are filtered. # !! Pattern '%TEMPL%' is replaced, paths are filtered.
:global GlobalFunctionsReady;
:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
:local ExitOK false; :local ExitOK false;
:onerror Err { :do {
: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 ScriptName [ :jobname ];
:global LogPrint; :global LogPrint;
@ -53,6 +53,6 @@
:delay ($Delay . "s"); :delay ($Delay . "s");
} }
} }
} do={ } on-error={
:global ExitError; $ExitError $ExitOK [ :jobname ] $Err; :global ExitError; $ExitError $ExitOK [ :jobname ];
} }

View file

@ -12,11 +12,11 @@
# #
# !! Do not edit this file, it is generated from template! # !! Do not edit this file, it is generated from template!
:global GlobalFunctionsReady;
:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
:local ExitOK false; :local ExitOK false;
:onerror Err { :do {
: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 ScriptName [ :jobname ];
:global LogPrint; :global LogPrint;
@ -46,6 +46,6 @@
:delay ($Delay . "s"); :delay ($Delay . "s");
} }
} }
} do={ } on-error={
:global ExitError; $ExitError $ExitOK [ :jobname ] $Err; :global ExitError; $ExitError $ExitOK [ :jobname ];
} }

View file

@ -8,11 +8,11 @@
# renew locally issued certificates # renew locally issued certificates
# https://rsc.eworm.de/doc/certificate-renew-issued.md # https://rsc.eworm.de/doc/certificate-renew-issued.md
:global GlobalFunctionsReady;
:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
:local ExitOK false; :local ExitOK false;
:onerror Err { :do {
: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 ScriptName [ :jobname ];
:global CertIssuedExportPass; :global CertIssuedExportPass;
@ -47,6 +47,6 @@
$LogPrint info $ScriptName ("Issued a new certificate for '" . $CertVal->"common-name" . "'."); $LogPrint info $ScriptName ("Issued a new certificate for '" . $CertVal->"common-name" . "'.");
} }
} }
} do={ } on-error={
:global ExitError; $ExitError $ExitOK [ :jobname ] $Err; :global ExitError; $ExitError $ExitOK [ :jobname ];
} }

View file

@ -15,7 +15,7 @@ DOMAINS_DUAL = \
git.eworm.de/ISRG-Root-X2 \ git.eworm.de/ISRG-Root-X2 \
lists.blocklist.de/Certum-Trusted-Network-CA \ lists.blocklist.de/Certum-Trusted-Network-CA \
matrix.org/GTS-Root-R4 \ matrix.org/GTS-Root-R4 \
raw.githubusercontent.com/USERTrust-RSA-Certification-Authority \ raw.githubusercontent.com/DigiCert-Global-Root-G2 \
rsc.eworm.de/ISRG-Root-X2 \ rsc.eworm.de/ISRG-Root-X2 \
upgrade.mikrotik.com/ISRG-Root-X1 upgrade.mikrotik.com/ISRG-Root-X1
DOMAINS_IPV4 = \ DOMAINS_IPV4 = \

View file

@ -1,41 +0,0 @@
# 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-----

View file

@ -9,11 +9,11 @@
# check for certificate validity # check for certificate validity
# https://rsc.eworm.de/doc/check-certificates.md # https://rsc.eworm.de/doc/check-certificates.md
:global GlobalFunctionsReady;
:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
:local ExitOK false; :local ExitOK false;
:onerror Err { :do {
: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 ScriptName [ :jobname ];
:global CertRenewTime; :global CertRenewTime;
@ -48,26 +48,21 @@
:global UrlEncode; :global UrlEncode;
:global WaitForFile; :global WaitForFile;
:foreach Type in={ "p12"; "pem" } do={ :local Return false;
: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 { :do {
/tool/fetch check-certificate=yes-without-crl http-header-field=({ [ $FetchUserAgentStr $ScriptName ] }) \ /tool/fetch check-certificate=yes-without-crl http-header-field=({ [ $FetchUserAgentStr $ScriptName ] }) \
($CertRenewUrl . $CertFileName) dst-path=$CertFileName as-value; ($CertRenewUrl . $CertFileName) dst-path=$CertFileName as-value;
$WaitForFile $CertFileName; $WaitForFile $CertFileName;
:local DecryptionFailed true; :local DecryptionFailed true;
:foreach I,PassPhrase in=$CertRenewPass do={ :foreach PassPhrase in=$CertRenewPass do={
:do { :local Result [ /certificate/import file-name=$CertFileName passphrase=$PassPhrase as-value ];
$LogPrint debug $ScriptName ("Trying " . $I . ". passphrase... "); :if ($Result->"decryption-failures" = 0) do={
:local Result [ /certificate/import file-name=$CertFileName passphrase=$PassPhrase as-value ]; :set DecryptionFailed false;
:if ($Result->"decryption-failures" = 0) do={ }
$LogPrint debug $ScriptName ("Success!");
:set DecryptionFailed false;
}
} on-error={ }
} }
$RmFile $CertFileName; $RmFile $CertFileName;
@ -82,13 +77,13 @@
$CertificateNameByCN [ /certificate/get $CertInChain common-name ]; $CertificateNameByCN [ /certificate/get $CertInChain common-name ];
} }
:return true; :set Return true;
} on-error={ } on-error={
$LogPrint debug $ScriptName ("Could not download certificate file '" . $CertFileName . "'."); $LogPrint debug $ScriptName ("Could not download certificate file '" . $CertFileName . "'.");
} }
} }
:return false; :return $Return;
} }
:local FormatInfo do={ :local FormatInfo do={
@ -237,6 +232,6 @@
", it is invalid after " . ($CertVal->"invalid-after") . "."); ", it is invalid after " . ($CertVal->"invalid-after") . ".");
} }
} }
} do={ } on-error={
:global ExitError; $ExitError $ExitOK [ :jobname ] $Err; :global ExitError; $ExitError $ExitOK [ :jobname ];
} }

View file

@ -8,11 +8,11 @@
# check for RouterOS health state # check for RouterOS health state
# https://rsc.eworm.de/doc/check-health.md # https://rsc.eworm.de/doc/check-health.md
:global GlobalFunctionsReady;
:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
:local ExitOK false; :local ExitOK false;
:onerror Err { :do {
: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 ScriptName [ :jobname ];
:global CheckHealthCPUUtilization; :global CheckHealthCPUUtilization;
@ -89,10 +89,10 @@
:foreach Plugin in=$Plugins do={ :foreach Plugin in=$Plugins do={
:local PluginVal [ /system/script/get $Plugin ]; :local PluginVal [ /system/script/get $Plugin ];
:if ([ $ValidateSyntax ($PluginVal->"source") ] = true) do={ :if ([ $ValidateSyntax ($PluginVal->"source") ] = true) do={
:onerror Err { :do {
/system/script/run $Plugin; /system/script/run $Plugin;
} do={ } on-error={
$LogPrint error $ScriptName ("Plugin '" . $ScriptVal->"name" . "' failed to run: " . $Err); $LogPrint error $ScriptName ("Plugin '" . $ScriptVal->"name" . "' failed to run.");
} }
} else={ } else={
$LogPrint error $ScriptName ("Plugin '" . $ScriptVal->"name" . "' failed syntax validation, skipping."); $LogPrint error $ScriptName ("Plugin '" . $ScriptVal->"name" . "' failed syntax validation, skipping.");
@ -105,6 +105,6 @@
} }
:set CheckHealthPlugins; :set CheckHealthPlugins;
} do={ } on-error={
:global ExitError; $ExitError $ExitOK [ :jobname ] $Err; :global ExitError; $ExitError $ExitOK [ :jobname ];
} }

View file

@ -8,11 +8,11 @@
# check for LTE firmware upgrade, send notification # check for LTE firmware upgrade, send notification
# https://rsc.eworm.de/doc/check-lte-firmware-upgrade.md # https://rsc.eworm.de/doc/check-lte-firmware-upgrade.md
:global GlobalFunctionsReady;
:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
:local ExitOK false; :local ExitOK false;
:onerror Err { :do {
: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 ScriptName [ :jobname ];
:global SentLteFirmwareUpgradeNotification; :global SentLteFirmwareUpgradeNotification;
@ -45,12 +45,12 @@
:local IntName [ /interface/lte/get $Interface name ]; :local IntName [ /interface/lte/get $Interface name ];
:local Firmware; :local Firmware;
:local Info; :local Info;
:onerror Err { :do {
:set Firmware [ /interface/lte/firmware-upgrade $Interface as-value ]; :set Firmware [ /interface/lte/firmware-upgrade $Interface as-value ];
:set Info [ /interface/lte/monitor $Interface once as-value ]; :set Info [ /interface/lte/monitor $Interface once as-value ];
} do={ } on-error={
$LogPrint debug $ScriptName ("Could not get latest LTE firmware version for interface " . \ $LogPrint debug $ScriptName ("Could not get latest LTE firmware version for interface " . \
$IntName . ": " . $Err); $IntName . ".");
:return false; :return false;
} }
@ -102,6 +102,6 @@
:foreach Interface in=[ /interface/lte/find ] do={ :foreach Interface in=[ /interface/lte/find ] do={
$CheckInterface $ScriptName $Interface; $CheckInterface $ScriptName $Interface;
} }
} do={ } on-error={
:global ExitError; $ExitError $ExitOK [ :jobname ] $Err; :global ExitError; $ExitError $ExitOK [ :jobname ];
} }

View file

@ -1,78 +0,0 @@
#!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;
}

View file

@ -9,11 +9,11 @@
# check for RouterOS update, send notification and/or install # check for RouterOS update, send notification and/or install
# https://rsc.eworm.de/doc/check-routeros-update.md # https://rsc.eworm.de/doc/check-routeros-update.md
:global GlobalFunctionsReady;
:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
:local ExitOK false; :local ExitOK false;
:onerror Err { :do {
: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 ScriptName [ :jobname ];
:global Identity; :global Identity;
@ -28,7 +28,6 @@
:global EscapeForRegEx; :global EscapeForRegEx;
:global FetchUserAgentStr; :global FetchUserAgentStr;
:global LogPrint; :global LogPrint;
:global RebootForUpdate;
:global ScriptFromTerminal; :global ScriptFromTerminal;
:global ScriptLock; :global ScriptLock;
:global SendNotification2; :global SendNotification2;
@ -63,14 +62,8 @@
$WaitFullyConnected; $WaitFullyConnected;
:if ([ :len [ /system/scheduler/find where name="_RebootForUpdate" ] ] > 0) do={ :if ([ :len [ /system/scheduler/find where name="_RebootForUpdate" ] ] > 0) do={
:if ([ :typeof $RebootForUpdate ] = "nothing") do={ :set ExitOK true;
$LogPrint info $ScriptName ("Found a stale scheduler for reboot, removing."); :error "A reboot for update is already scheduled.";
/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..."); $LogPrint debug $ScriptName ("Checking for updates...");
@ -147,13 +140,13 @@
:if ([ :len $SafeUpdateUrl ] > 0) do={ :if ([ :len $SafeUpdateUrl ] > 0) do={
:local Result; :local Result;
:onerror Err { :do {
:set Result [ /tool/fetch check-certificate=yes-without-crl \ :set Result [ /tool/fetch check-certificate=yes-without-crl \
($SafeUpdateUrl . $Update->"channel" . "?installed=" . $Update->"installed-version" . \ ($SafeUpdateUrl . $Update->"channel" . "?installed=" . $Update->"installed-version" . \
"&latest=" . $Update->"latest-version") http-header-field=({ [ $FetchUserAgentStr $ScriptName ] }) \ "&latest=" . $Update->"latest-version") http-header-field=({ [ $FetchUserAgentStr $ScriptName ] }) \
output=user as-value ]; output=user as-value ];
} do={ } on-error={
$LogPrint warning $ScriptName ("Failed receiving safe version for " . $Update->"channel" . ": " . $Err); $LogPrint warning $ScriptName ("Failed receiving safe version for " . $Update->"channel" . ".");
} }
:if ($Result->"status" = "finished" && $Result->"data" = $Update->"latest-version") do={ :if ($Result->"status" = "finished" && $Result->"data" = $Update->"latest-version") do={
$LogPrint info $ScriptName ("Version " . $Update->"latest-version" . " is considered safe, updating..."); $LogPrint info $ScriptName ("Version " . $Update->"latest-version" . " is considered safe, updating...");
@ -220,6 +213,6 @@
" is available for downgrade."); " is available for downgrade.");
:set SentRouterosUpdateNotification ($Update->"latest-version"); :set SentRouterosUpdateNotification ($Update->"latest-version");
} }
} do={ } on-error={
:global ExitError; $ExitError $ExitOK [ :jobname ] $Err; :global ExitError; $ExitError $ExitOK [ :jobname ];
} }

View file

@ -11,11 +11,11 @@
# #
# !! Do not edit this file, it is generated from template! # !! Do not edit this file, it is generated from template!
:global GlobalFunctionsReady;
:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
:local ExitOK false; :local ExitOK false;
:onerror Err { :do {
: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 ScriptName [ :jobname ];
:global Identity; :global Identity;
@ -95,6 +95,6 @@
$LogPrint debug $ScriptName ("No mac address available... Ignoring."); $LogPrint debug $ScriptName ("No mac address available... Ignoring.");
} }
} }
} do={ } on-error={
:global ExitError; $ExitError $ExitOK [ :jobname ] $Err; :global ExitError; $ExitError $ExitOK [ :jobname ];
} }

View file

@ -11,11 +11,11 @@
# #
# !! Do not edit this file, it is generated from template! # !! Do not edit this file, it is generated from template!
:global GlobalFunctionsReady;
:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
:local ExitOK false; :local ExitOK false;
:onerror Err { :do {
: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 ScriptName [ :jobname ];
:global Identity; :global Identity;
@ -96,6 +96,6 @@
$LogPrint debug $ScriptName ("No mac address available... Ignoring."); $LogPrint debug $ScriptName ("No mac address available... Ignoring.");
} }
} }
} do={ } on-error={
:global ExitError; $ExitError $ExitOK [ :jobname ] $Err; :global ExitError; $ExitError $ExitOK [ :jobname ];
} }

View file

@ -12,11 +12,11 @@
# !! This is just a template to generate the real script! # !! This is just a template to generate the real script!
# !! Pattern '%TEMPL%' is replaced, paths are filtered. # !! Pattern '%TEMPL%' is replaced, paths are filtered.
:global GlobalFunctionsReady;
:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
:local ExitOK false; :local ExitOK false;
:onerror Err { :do {
: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 ScriptName [ :jobname ];
:global Identity; :global Identity;
@ -113,6 +113,6 @@
$LogPrint debug $ScriptName ("No mac address available... Ignoring."); $LogPrint debug $ScriptName ("No mac address available... Ignoring.");
} }
} }
} do={ } on-error={
:global ExitError; $ExitError $ExitOK [ :jobname ] $Err; :global ExitError; $ExitError $ExitOK [ :jobname ];
} }

View file

@ -11,11 +11,11 @@
# #
# !! Do not edit this file, it is generated from template! # !! Do not edit this file, it is generated from template!
:global GlobalFunctionsReady;
:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
:local ExitOK false; :local ExitOK false;
:onerror Err { :do {
: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 ScriptName [ :jobname ];
:global Identity; :global Identity;
@ -95,6 +95,6 @@
$LogPrint debug $ScriptName ("No mac address available... Ignoring."); $LogPrint debug $ScriptName ("No mac address available... Ignoring.");
} }
} }
} do={ } on-error={
:global ExitError; $ExitError $ExitOK [ :jobname ] $Err; :global ExitError; $ExitError $ExitOK [ :jobname ];
} }

View file

@ -1,9 +0,0 @@
#!/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'

View file

@ -11,11 +11,11 @@
# #
# !! Do not edit this file, it is generated from template! # !! Do not edit this file, it is generated from template!
:global GlobalFunctionsReady;
:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
:local ExitOK false; :local ExitOK false;
:onerror Err { :do {
: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 ScriptName [ :jobname ];
:global DailyPskMatchComment; :global DailyPskMatchComment;
@ -91,6 +91,6 @@
} }
} }
} }
} do={ } on-error={
:global ExitError; $ExitError $ExitOK [ :jobname ] $Err; :global ExitError; $ExitError $ExitOK [ :jobname ];
} }

View file

@ -11,11 +11,11 @@
# #
# !! Do not edit this file, it is generated from template! # !! Do not edit this file, it is generated from template!
:global GlobalFunctionsReady;
:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
:local ExitOK false; :local ExitOK false;
:onerror Err { :do {
: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 ScriptName [ :jobname ];
:global DailyPskMatchComment; :global DailyPskMatchComment;
@ -90,6 +90,6 @@
} }
} }
} }
} do={ } on-error={
:global ExitError; $ExitError $ExitOK [ :jobname ] $Err; :global ExitError; $ExitError $ExitOK [ :jobname ];
} }

View file

@ -12,11 +12,11 @@
# !! This is just a template to generate the real script! # !! This is just a template to generate the real script!
# !! Pattern '%TEMPL%' is replaced, paths are filtered. # !! Pattern '%TEMPL%' is replaced, paths are filtered.
:global GlobalFunctionsReady;
:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
:local ExitOK false; :local ExitOK false;
:onerror Err { :do {
: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 ScriptName [ :jobname ];
:global DailyPskMatchComment; :global DailyPskMatchComment;
@ -106,6 +106,6 @@
} }
} }
} }
} do={ } on-error={
:global ExitError; $ExitError $ExitOK [ :jobname ] $Err; :global ExitError; $ExitError $ExitOK [ :jobname ];
} }

View file

@ -11,11 +11,11 @@
# #
# !! Do not edit this file, it is generated from template! # !! Do not edit this file, it is generated from template!
:global GlobalFunctionsReady;
:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
:local ExitOK false; :local ExitOK false;
:onerror Err { :do {
: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 ScriptName [ :jobname ];
:global DailyPskMatchComment; :global DailyPskMatchComment;
@ -91,6 +91,6 @@
} }
} }
} }
} do={ } on-error={
:global ExitError; $ExitError $ExitOK [ :jobname ] $Err; :global ExitError; $ExitError $ExitOK [ :jobname ];
} }

View file

@ -11,11 +11,11 @@
# #
# !! Do not edit this file, it is generated from template! # !! Do not edit this file, it is generated from template!
:global GlobalFunctionsReady;
:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
:local ExitOK false; :local ExitOK false;
:onerror Err { :do {
: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 ScriptName [ :jobname ];
:global LogPrint; :global LogPrint;
@ -38,6 +38,6 @@
/ip/dhcp-server/lease/set comment=$NewComment $Lease; /ip/dhcp-server/lease/set comment=$NewComment $Lease;
} }
} }
} do={ } on-error={
:global ExitError; $ExitError $ExitOK [ :jobname ] $Err; :global ExitError; $ExitError $ExitOK [ :jobname ];
} }

View file

@ -11,11 +11,11 @@
# #
# !! Do not edit this file, it is generated from template! # !! Do not edit this file, it is generated from template!
:global GlobalFunctionsReady;
:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
:local ExitOK false; :local ExitOK false;
:onerror Err { :do {
: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 ScriptName [ :jobname ];
:global LogPrint; :global LogPrint;
@ -38,6 +38,6 @@
/ip/dhcp-server/lease/set comment=$NewComment $Lease; /ip/dhcp-server/lease/set comment=$NewComment $Lease;
} }
} }
} do={ } on-error={
:global ExitError; $ExitError $ExitOK [ :jobname ] $Err; :global ExitError; $ExitError $ExitOK [ :jobname ];
} }

View file

@ -12,11 +12,11 @@
# !! This is just a template to generate the real script! # !! This is just a template to generate the real script!
# !! Pattern '%TEMPL%' is replaced, paths are filtered. # !! Pattern '%TEMPL%' is replaced, paths are filtered.
:global GlobalFunctionsReady;
:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
:local ExitOK false; :local ExitOK false;
:onerror Err { :do {
: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 ScriptName [ :jobname ];
:global LogPrint; :global LogPrint;
@ -43,6 +43,6 @@
/ip/dhcp-server/lease/set comment=$NewComment $Lease; /ip/dhcp-server/lease/set comment=$NewComment $Lease;
} }
} }
} do={ } on-error={
:global ExitError; $ExitError $ExitOK [ :jobname ] $Err; :global ExitError; $ExitError $ExitOK [ :jobname ];
} }

View file

@ -11,11 +11,11 @@
# #
# !! Do not edit this file, it is generated from template! # !! Do not edit this file, it is generated from template!
:global GlobalFunctionsReady;
:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
:local ExitOK false; :local ExitOK false;
:onerror Err { :do {
: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 ScriptName [ :jobname ];
:global LogPrint; :global LogPrint;
@ -38,6 +38,6 @@
/ip/dhcp-server/lease/set comment=$NewComment $Lease; /ip/dhcp-server/lease/set comment=$NewComment $Lease;
} }
} }
} do={ } on-error={
:global ExitError; $ExitError $ExitOK [ :jobname ] $Err; :global ExitError; $ExitError $ExitOK [ :jobname ];
} }

View file

@ -9,11 +9,11 @@
# check DHCP leases and add/remove/update DNS entries # check DHCP leases and add/remove/update DNS entries
# https://rsc.eworm.de/doc/dhcp-to-dns.md # https://rsc.eworm.de/doc/dhcp-to-dns.md
:global GlobalFunctionsReady;
:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
:local ExitOK false; :local ExitOK false;
:onerror Err { :do {
: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 ScriptName [ :jobname ];
:global Domain; :global Domain;
@ -125,6 +125,6 @@
$LogPrint debug $ScriptName ("No address available... Ignoring."); $LogPrint debug $ScriptName ("No address available... Ignoring.");
} }
} }
} do={ } on-error={
:global ExitError; $ExitError $ExitOK [ :jobname ] $Err; :global ExitError; $ExitError $ExitOK [ :jobname ];
} }

View file

@ -17,7 +17,7 @@ Description
----------- -----------
This script uploads 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 > ⚠️ **Warning**: The used command can hit errors that a script can with
> workaround only. A notification *should* be sent anyway. But it can result > workaround only. A notification *should* be sent anyway. But it can result
@ -49,7 +49,6 @@ The configuration goes to `global-config-overlay`, these are the parameters:
Also notification settings are required for Also notification settings are required for
[e-mail](mod/notification-email.md), [e-mail](mod/notification-email.md),
[gotify](mod/notification-gotify.md),
[matrix](mod/notification-matrix.md), [matrix](mod/notification-matrix.md),
[ntfy](mod/notification-ntfy.md) and/or [ntfy](mod/notification-ntfy.md) and/or
[telegram](mod/notification-telegram.md). [telegram](mod/notification-telegram.md).

View file

@ -17,7 +17,7 @@ Description
----------- -----------
This script saves the current configuration to fallback 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 It can also copy-over the RouterOS installation when run interactively
or just before a feature update. or just before a feature update.

View file

@ -55,7 +55,6 @@ The configuration goes to `global-config-overlay`, these are the parameters:
Also notification settings are required for Also notification settings are required for
[e-mail](mod/notification-email.md), [e-mail](mod/notification-email.md),
[gotify](mod/notification-gotify.md),
[matrix](mod/notification-matrix.md), [matrix](mod/notification-matrix.md),
[ntfy](mod/notification-ntfy.md) and/or [ntfy](mod/notification-ntfy.md) and/or
[telegram](mod/notification-telegram.md). [telegram](mod/notification-telegram.md).

View file

@ -51,7 +51,6 @@ subject alternative name (aka *Subject Alt Name* or *SAN*) can be used.
Also notification settings are required for Also notification settings are required for
[e-mail](mod/notification-email.md), [e-mail](mod/notification-email.md),
[gotify](mod/notification-gotify.md),
[matrix](mod/notification-matrix.md), [matrix](mod/notification-matrix.md),
[ntfy](mod/notification-ntfy.md) and/or [ntfy](mod/notification-ntfy.md) and/or
[telegram](mod/notification-telegram.md). [telegram](mod/notification-telegram.md).

View file

@ -113,7 +113,6 @@ The configuration goes to `global-config-overlay`, these are the parameters:
Also notification settings are required for Also notification settings are required for
[e-mail](mod/notification-email.md), [e-mail](mod/notification-email.md),
[gotify](mod/notification-gotify.md),
[matrix](mod/notification-matrix.md), [matrix](mod/notification-matrix.md),
[ntfy](mod/notification-ntfy.md) and/or [ntfy](mod/notification-ntfy.md) and/or
[telegram](mod/notification-telegram.md). [telegram](mod/notification-telegram.md).

View file

@ -44,9 +44,7 @@ Configuration
Also notification settings are required for Also notification settings are required for
[e-mail](mod/notification-email.md), [e-mail](mod/notification-email.md),
[gotify](mod/notification-gotify.md), [matrix](mod/notification-matrix.md) and/or
[matrix](mod/notification-matrix.md),
[ntfy](mod/notification-ntfy.md) and/or
[telegram](mod/notification-telegram.md). [telegram](mod/notification-telegram.md).
See also See also

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.9 KiB

View file

@ -1,71 +0,0 @@
Check perpetual license on CHR
==============================
[![GitHub stars](https://img.shields.io/github/stars/eworm-de/routeros-scripts?logo=GitHub&style=flat&color=red)](https://github.com/eworm-de/routeros-scripts/stargazers)
[![GitHub forks](https://img.shields.io/github/forks/eworm-de/routeros-scripts?logo=GitHub&style=flat&color=green)](https://github.com/eworm-de/routeros-scripts/network)
[![GitHub watchers](https://img.shields.io/github/watchers/eworm-de/routeros-scripts?logo=GitHub&style=flat&color=blue)](https://github.com/eworm-de/routeros-scripts/watchers)
[![required RouterOS version](https://img.shields.io/badge/RouterOS-7.15-yellow?style=flat)](https://mikrotik.com/download/changelogs/)
[![Telegram group @routeros_scripts](https://img.shields.io/badge/Telegram-%40routeros__scripts-%2326A5E4?logo=telegram&style=flat)](https://t.me/routeros_scripts)
[![donate with PayPal](https://img.shields.io/badge/Like_it%3F-Donate!-orange?logo=githubsponsors&logoColor=orange&style=flat)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=A4ZXBD6YS2W8J)
[⬅️ 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
![check-perpetual-license notification](check-perpetual-license.d/notification.avif)
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)

View file

@ -30,8 +30,8 @@ automatically is supported.
> ⚠️ **Warning**: Installing updates is important from a security point > ⚠️ **Warning**: Installing updates is important from a security point
> of view. At the same time it can be source of serve breakage. So test > of view. At the same time it can be source of serve breakage. So test
> versions in lab and read > versions in lab and read
> [changelog ↗️](https://mikrotik.com/download/changelogs/) and > [changelog](https://mikrotik.com/download/changelogs/) and
> [forum ↗️](https://forum.mikrotik.com/viewforum.php?f=21) before deploying > [forum](https://forum.mikrotik.com/viewforum.php?f=21) before deploying
> to your production environment! Automatic updates should be handled > to your production environment! Automatic updates should be handled
> with care! > with care!
@ -73,7 +73,6 @@ The configuration goes to `global-config-overlay`, these are the parameters:
Also notification settings are required for Also notification settings are required for
[e-mail](mod/notification-email.md), [e-mail](mod/notification-email.md),
[gotify](mod/notification-gotify.md),
[matrix](mod/notification-matrix.md), [matrix](mod/notification-matrix.md),
[ntfy](mod/notification-ntfy.md) and/or [ntfy](mod/notification-ntfy.md) and/or
[telegram](mod/notification-telegram.md). [telegram](mod/notification-telegram.md).
@ -100,7 +99,6 @@ startup is perfectly valid:
See also See also
-------- --------
* [Check perpetual license on CHR](check-perpetual-license.md)
* [Automatically upgrade firmware and reboot](firmware-upgrade-reboot.md) * [Automatically upgrade firmware and reboot](firmware-upgrade-reboot.md)
* [Manage system update](packages-update.md) * [Manage system update](packages-update.md)

View file

@ -54,7 +54,6 @@ entries are to be added.
Also notification settings are required for Also notification settings are required for
[e-mail](mod/notification-email.md), [e-mail](mod/notification-email.md),
[gotify](mod/notification-gotify.md),
[matrix](mod/notification-matrix.md), [matrix](mod/notification-matrix.md),
[ntfy](mod/notification-ntfy.md) and/or [ntfy](mod/notification-ntfy.md) and/or
[telegram](mod/notification-telegram.md). [telegram](mod/notification-telegram.md).

View file

@ -79,7 +79,6 @@ For legacy local interface:
Also notification settings are required for Also notification settings are required for
[e-mail](mod/notification-email.md), [e-mail](mod/notification-email.md),
[gotify](mod/notification-gotify.md),
[trix](mod/notification-matrix.md), [trix](mod/notification-matrix.md),
[ntfy](mod/notification-ntfy.md) and/or [ntfy](mod/notification-ntfy.md) and/or
[telegram](mod/notification-telegram.md). [telegram](mod/notification-telegram.md).

View file

@ -19,10 +19,10 @@ Description
This script downloads, imports and updates firewall address-lists. Its main This script downloads, imports and updates firewall address-lists. Its main
purpose is to block attacking ip addresses, spam hosts, command-and-control purpose is to block attacking ip addresses, spam hosts, command-and-control
servers and similar malicious entities. The default configuration contains a servers and similar malicious entities. The default configuration contains a
[collective list by GitHub user @stamparm ↗️](https://github.com/stamparm/ipsum), [collective list by GitHub user @stamparm](https://github.com/stamparm/ipsum),
lists from [dshield.org ↗️](https://dshield.org/) and lists from [dshield.org](https://dshield.org/) and
[blocklist.de ↗️](https://www.blocklist.de/), and lists from [blocklist.de](https://www.blocklist.de/), and lists from
[spamhaus.org ↗️](https://spamhaus.org/) are prepared. [spamhaus.org](https://spamhaus.org/) are prepared.
The address-lists are updated in place, so after initial import you will not The address-lists are updated in place, so after initial import you will not
see situation when the lists are not populated. see situation when the lists are not populated.
@ -33,9 +33,6 @@ certificate is checked.
> ⚠️ **Warning**: The script does not limit the size of a list, but keep in > ⚠️ **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), > 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 Requirements and installation
----------------------------- -----------------------------

View file

@ -22,15 +22,15 @@ server (see `/system/logging`). This has some limitation, however:
* does not work early after boot if network connectivity is not * does not work early after boot if network connectivity is not
yet established, or breaks intermittently yet established, or breaks intermittently
* lots of messages generate a flood of mails * lots of messages generate a flood of mails
* Gotify, Matrix, Ntfy and Telegram are not supported * Matrix, Ntfy and Telegram are not supported
The script works around the limitations, for example it does: The script works around the limitations, for example it does:
* read from `/log`, including messages from early boot * read from `/log`, including messages from early boot
* skip multi-repeated messages * skip multi-repeated messages
* rate-limit itself to mitigate flooding * rate-limit itself to mitigate flooding
* forward via notification (which includes *e-mail*, *Gotify*, *Matrix*, * forward via notification (which includes *e-mail*, *Matrix*, *Ntfy* and
*Ntfy* and *Telegram* when installed and configured, see below) *Telegram* when installed and configured, see below)
It is intended to be run periodically from scheduler, then collects new It is intended to be run periodically from scheduler, then collects new
log messages and forwards them via notification. 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. > your local `global-config-overlay` and modify it to your specific needs.
These patterns are matched as 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` To forward **all** (ignoring severity) log messages with topics `account`
(which includes user logins) and `dhcp` you need something like: (which includes user logins) and `dhcp` you need something like:
@ -80,7 +80,6 @@ To forward **all** (ignoring severity) log messages with topics `account`
Also notification settings are required for Also notification settings are required for
[e-mail](mod/notification-email.md), [e-mail](mod/notification-email.md),
[gotify](mod/notification-gotify.md),
[matrix](mod/notification-matrix.md), [matrix](mod/notification-matrix.md),
[ntfy](mod/notification-ntfy.md) and/or [ntfy](mod/notification-ntfy.md) and/or
[telegram](mod/notification-telegram.md). [telegram](mod/notification-telegram.md).

View file

@ -32,7 +32,7 @@ Configuration
------------- -------------
Set up your device's 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 Also make sure the device has correct time configured, best is to set up
the ntp client. the ntp client.
@ -79,7 +79,6 @@ function available:
See also See also
-------- --------
* [Send notifications via Gotify](notification-gotify.md)
* [Send notifications via Matrix](notification-matrix.md) * [Send notifications via Matrix](notification-matrix.md)
* [Send notifications via Ntfy](notification-ntfy.md) * [Send notifications via Ntfy](notification-ntfy.md)
* [Send notifications via Telegram](notification-telegram.md) * [Send notifications via Telegram](notification-telegram.md)

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

View file

@ -1,97 +0,0 @@
Send notifications via Gotify
===========================
[![GitHub stars](https://img.shields.io/github/stars/eworm-de/routeros-scripts?logo=GitHub&style=flat&color=red)](https://github.com/eworm-de/routeros-scripts/stargazers)
[![GitHub forks](https://img.shields.io/github/forks/eworm-de/routeros-scripts?logo=GitHub&style=flat&color=green)](https://github.com/eworm-de/routeros-scripts/network)
[![GitHub watchers](https://img.shields.io/github/watchers/eworm-de/routeros-scripts?logo=GitHub&style=flat&color=blue)](https://github.com/eworm-de/routeros-scripts/watchers)
[![required RouterOS version](https://img.shields.io/badge/RouterOS-7.15-yellow?style=flat)](https://mikrotik.com/download/changelogs/)
[![Telegram group @routeros_scripts](https://img.shields.io/badge/Telegram-%40routeros__scripts-%2326A5E4?logo=telegram&style=flat)](https://t.me/routeros_scripts)
[![donate with PayPal](https://img.shields.io/badge/Like_it%3F-Donate!-orange?logo=githubsponsors&logoColor=orange&style=flat)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=A4ZXBD6YS2W8J)
[⬅️ 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.
![Create new app](notification-gotify.d/appsetup.avif)
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)

View file

@ -17,7 +17,7 @@ Description
----------- -----------
This module adds support for sending notifications via 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. make sure notifications are not lost on failure but sent later.
Requirements and installation Requirements and installation
@ -131,7 +131,6 @@ See also
* [Certificate name from browser](../../CERTIFICATES.md) * [Certificate name from browser](../../CERTIFICATES.md)
* [Send notifications via e-mail](notification-email.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 Ntfy](notification-ntfy.md)
* [Send notifications via Telegram](notification-telegram.md) * [Send notifications via Telegram](notification-telegram.md)

View file

@ -17,7 +17,7 @@ Description
----------- -----------
This module adds support for sending notifications via 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. notifications are not lost on failure but sent later.
Requirements and installation Requirements and installation
@ -28,7 +28,7 @@ Just install the module:
$ScriptInstallUpdate mod/notification-ntfy; $ScriptInstallUpdate mod/notification-ntfy;
Also install the Ntfy app on your mobile device or use the 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 Configuration
------------- -------------
@ -90,7 +90,6 @@ See also
* [Certificate name from browser](../../CERTIFICATES.md) * [Certificate name from browser](../../CERTIFICATES.md)
* [Send notifications via e-mail](notification-email.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 Matrix](notification-matrix.md)
* [Send notifications via Telegram](notification-telegram.md) * [Send notifications via Telegram](notification-telegram.md)

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.8 KiB

View file

@ -17,7 +17,7 @@ Description
----------- -----------
This module adds support for sending notifications via 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. notifications are not lost on failure but sent later.
Requirements and installation Requirements and installation
@ -33,26 +33,19 @@ and create an account.
Configuration 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: create your own bot:
![create new bot](notification-telegram.d/newbot.avif) ![create new bot](notification-telegram.d/newbot.avif)
Set that token from *BotFather* (use your own!) to `TelegramTokenId`, for Now open a chat with your bot and start it by clicking the `START` button.
now just temporarily:
:set TelegramTokenId "5214364459:AAHLwf1o7ybbKDo6pY24Kd2bZ5rjCakDXTc"; 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
Now open a chat with your bot and start it by clicking the `START` button, `id`, just below `You`.
then send your first message. Any text will do. On your device run
`$GetTelegramChatId` to retrieve the chat id:
$GetTelegramChatId;
![get chat id](notification-telegram.d/getchatid.avif)
Finally edit `global-config-overlay`, add `TelegramTokenId` with the token Finally edit `global-config-overlay`, add `TelegramTokenId` with the token
from *BotFather* and `TelegramChatId` with your retrieved chat id. Then from *BotFather* and `TelegramChatId` with your id from *GetIDs Bot*. Then
reload the configuration. reload the configuration.
> **Info**: Copy relevant configuration from > **Info**: Copy relevant configuration from
@ -61,10 +54,9 @@ reload the configuration.
### Notifications to a group ### Notifications to a group
Sending notifications to a group is possible as well. Add your bot to a group Sending notifications to a group is possible as well. Add your bot and the
and make it an admin (required for read access!) and send a message and run *GetIDs Bot* to a group, then use the group's id (which starts with a dash)
`$GetTelegramChatId` again. Then use that chat id (which starts with a dash) for `TelegramChatId`. Then remove *GetIDs Bot* from group.
for `TelegramChatId`.
Groups can enable the `Topics` feature. Use `TelegramThreadId` to send to a Groups can enable the `Topics` feature. Use `TelegramThreadId` to send to a
specific topic in a group. specific topic in a group.
@ -102,7 +94,7 @@ Tips & Tricks
### Set a profile photo ### Set a profile photo
You can use a profile photo for your bot to make it recognizable. Open the 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.
![set profile photo](notification-telegram.d/setuserpic.avif) ![set profile photo](notification-telegram.d/setuserpic.avif)
@ -115,7 +107,6 @@ See also
* [Chat with your router and send commands via Telegram bot](../telegram-chat.md) * [Chat with your router and send commands via Telegram bot](../telegram-chat.md)
* [Send notifications via e-mail](notification-email.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 Matrix](notification-matrix.md)
* [Send notifications via Ntfy](notification-ntfy.md) * [Send notifications via Ntfy](notification-ntfy.md)

View file

@ -47,7 +47,6 @@ The hosts to be checked have to be added to netwatch with specific comment:
Also notification settings are required for Also notification settings are required for
[e-mail](mod/notification-email.md), [e-mail](mod/notification-email.md),
[gotify](mod/notification-gotify.md),
[matrix](mod/notification-matrix.md), [matrix](mod/notification-matrix.md),
[ntfy](mod/notification-ntfy.md) and/or [ntfy](mod/notification-ntfy.md) and/or
[telegram](mod/notification-telegram.md). [telegram](mod/notification-telegram.md).

View file

@ -46,8 +46,8 @@ Configuration
The configuration goes to `global-config-overlay`, this is the only parameter: The configuration goes to `global-config-overlay`, this is the only parameter:
* `PackagesUpdateDeferReboot`: defer the reboot for night (between 3 AM and * `PackagesUpdateDeferReboot`: defer the reboot for night (between 3 AM
5 AM), use a numerical value in days suffixed with a `d` to defer further and 5 AM)
By modifying the scheduler's `start-time` you can force the reboot at By modifying the scheduler's `start-time` you can force the reboot at
different time. different time.

View file

@ -56,7 +56,6 @@ The configuration goes to `global-config-overlay`, this is the only parameter:
Notification settings are required for Notification settings are required for
[e-mail](mod/notification-email.md), [e-mail](mod/notification-email.md),
[gotify](mod/notification-gotify.md),
[matrix](mod/notification-matrix.md), [matrix](mod/notification-matrix.md),
[ntfy](mod/notification-ntfy.md) and/or [ntfy](mod/notification-ntfy.md) and/or
[telegram](mod/notification-telegram.md). [telegram](mod/notification-telegram.md).

View file

@ -8,11 +8,11 @@
# install firmware upgrade, and reboot # install firmware upgrade, and reboot
# https://rsc.eworm.de/doc/firmware-upgrade-reboot.md # https://rsc.eworm.de/doc/firmware-upgrade-reboot.md
:global GlobalFunctionsReady;
:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
:local ExitOK false; :local ExitOK false;
:onerror Err { :do {
: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 ScriptName [ :jobname ];
:global LogPrint; :global LogPrint;
@ -55,6 +55,6 @@
$LogPrint info $ScriptName ("Firmware upgrade successful, rebooting."); $LogPrint info $ScriptName ("Firmware upgrade successful, rebooting.");
/system/reboot; /system/reboot;
} do={ } on-error={
:global ExitError; $ExitError $ExitOK [ :jobname ] $Err; :global ExitError; $ExitError $ExitOK [ :jobname ];
} }

View file

@ -8,11 +8,11 @@
# download, import and update firewall address-lists # download, import and update firewall address-lists
# https://rsc.eworm.de/doc/fw-addr-lists.md # https://rsc.eworm.de/doc/fw-addr-lists.md
:global GlobalFunctionsReady;
:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
:local ExitOK false; :local ExitOK false;
:onerror Err { :do {
: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 ScriptName [ :jobname ];
:global FwAddrLists; :global FwAddrLists;
@ -24,7 +24,6 @@
:global HumanReadableNum; :global HumanReadableNum;
:global LogPrint; :global LogPrint;
:global LogPrintOnce; :global LogPrintOnce;
:global LogPrintVerbose;
:global ScriptLock; :global ScriptLock;
:global WaitFullyConnected; :global WaitFullyConnected;
@ -37,23 +36,12 @@
} }
} }
: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={ :if ([ $ScriptLock $ScriptName ] = false) do={
:set ExitOK true; :set ExitOK true;
:error false; :error false;
} }
$WaitFullyConnected; $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); :local ListComment ("managed by " . $ScriptName);
:foreach FwListName,FwList in=$FwAddrLists do={ :foreach FwListName,FwList in=$FwAddrLists do={
@ -111,24 +99,17 @@
:set Address ([ :pick $Line 0 [ $FindDelim $Line ] ] . ($List->"cidr")); :set Address ([ :pick $Line 0 [ $FindDelim $Line ] ] . ($List->"cidr"));
} }
:do { :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={ :if ($Address ~ "^[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}(/[0-9]{1,2})?\$") do={
:if ($Address ~ "/32\$") do={ :set ($IPv4Addresses->$Address) $TimeOut;
:set Address [ :pick $Address 0 ([ :len $Address ] - 3) ];
}
:set ($IPv4Addresses->$Branch->$Address) $TimeOut;
:error true; :error true;
} }
:if ($Address ~ "^[0-9a-zA-Z]*:[0-9a-zA-Z:\\.]+(/[0-9]{1,3})?\$") do={ :if ($Address ~ "^[0-9a-zA-Z]*:[0-9a-zA-Z:\\.]+(/[0-9]{1,3})?\$") do={
:if ([ :typeof [ :find $Address "/" ] ] = "nil") do={ :set ($IPv6Addresses->$Address) $TimeOut;
:set Address ($Address . "/128");
}
:set ($IPv6Addresses->$Branch->$Address) $TimeOut;
:error true; :error true;
} }
:if ($Address ~ "^[\\.a-zA-Z0-9-]+\\.[a-zA-Z]{2,}\$") do={ :if ($Address ~ "^[\\.a-zA-Z0-9-]+\\.[a-zA-Z]{2,}\$") do={
:set ($IPv4Addresses->$Branch->$Address) $TimeOut; :set ($IPv4Addresses->$Address) $TimeOut;
:set ($IPv6Addresses->$Branch->$Address) $TimeOut; :set ($IPv6Addresses->$Address) $TimeOut;
:error true; :error true;
} }
} on-error={ } } on-error={ }
@ -138,18 +119,16 @@
:foreach Entry in=[ /ip/firewall/address-list/find where \ :foreach Entry in=[ /ip/firewall/address-list/find where \
list=$FwListName comment=$ListComment ] do={ list=$FwListName comment=$ListComment ] do={
:local Address [ /ip/firewall/address-list/get $Entry address ]; :local Address [ /ip/firewall/address-list/get $Entry address ];
:local Branch [ $GetBranch $Address ]; :if ([ :typeof ($IPv4Addresses->$Address) ] = "time") do={
:local TimeOut ($IPv4Addresses->$Branch->$Address); $LogPrint debug $ScriptName ("Renewing IPv4 address in list '" . $FwListName . \
:if ([ :typeof $TimeOut ] = "time") do={ "' with " . ($IPv4Addresses->$Address) . ": " . $Address);
$LogPrintVerbose debug $ScriptName ("Renewing IPv4 address " . $Address . \ /ip/firewall/address-list/set $Entry timeout=($IPv4Addresses->$Address);
" in list '" . $FwListName . "' with " . $TimeOut . "."); :set ($IPv4Addresses->$Address);
/ip/firewall/address-list/set $Entry timeout=$TimeOut;
:set ($IPv4Addresses->$Branch->$Address);
:set CntRenew ($CntRenew + 1); :set CntRenew ($CntRenew + 1);
} else={ } else={
:if ($Failure = false) do={ :if ($Failure = false) do={
$LogPrintVerbose debug $ScriptName ("Removing IPv4 address " . $Address . \ $LogPrint debug $ScriptName ("Removing IPv4 address from list '" . $FwListName . \
" from list '" . $FwListName . "."); "': " . $Address);
/ip/firewall/address-list/remove $Entry; /ip/firewall/address-list/remove $Entry;
:set CntRemove ($CntRemove + 1); :set CntRemove ($CntRemove + 1);
} }
@ -159,53 +138,47 @@
:foreach Entry in=[ /ipv6/firewall/address-list/find where \ :foreach Entry in=[ /ipv6/firewall/address-list/find where \
list=$FwListName comment=$ListComment ] do={ list=$FwListName comment=$ListComment ] do={
:local Address [ /ipv6/firewall/address-list/get $Entry address ]; :local Address [ /ipv6/firewall/address-list/get $Entry address ];
:local Branch [ $GetBranch $Address ]; :if ([ :typeof ($IPv6Addresses->$Address) ] = "time") do={
:local TimeOut ($IPv6Addresses->$Branch->$Address); $LogPrint debug $ScriptName ("Renewing IPv6 address in list '" . $FwListName . \
:if ([ :typeof $TimeOut ] = "time") do={ "' with " . ($IPv6Addresses->$Address) . ": " . $Address);
$LogPrintVerbose debug $ScriptName ("Renewing IPv6 address " . $Address . \ /ipv6/firewall/address-list/set $Entry timeout=($IPv6Addresses->$Address);
" in list '" . $FwListName . "' with " . $TimeOut . "."); :set ($IPv6Addresses->$Address);
/ipv6/firewall/address-list/set $Entry timeout=$TimeOut;
:set ($IPv6Addresses->$Branch->$Address);
:set CntRenew ($CntRenew + 1); :set CntRenew ($CntRenew + 1);
} else={ } else={
:if ($Failure = false) do={ :if ($Failure = false) do={
$LogPrintVerbose debug $ScriptName ("Removing IPv6 address " . $Address . \ $LogPrint debug $ScriptName ("Removing IPv6 address from list '" . $FwListName . \
" from list '" . $FwListName ."."); "': " . $Address);
/ipv6/firewall/address-list/remove $Entry; /ipv6/firewall/address-list/remove $Entry;
:set CntRemove ($CntRemove + 1); :set CntRemove ($CntRemove + 1);
} }
} }
} }
:foreach BranchName,Branch in=$IPv4Addresses do={ :foreach Address,Timeout in=$IPv4Addresses do={
$LogPrintVerbose debug $ScriptName ("Handling branch: " . $BranchName); $LogPrint debug $ScriptName ("Adding IPv4 address to list '" . $FwListName . \
:foreach Address,Timeout in=$Branch do={ "' with " . $Timeout . ": " . $Address);
$LogPrintVerbose debug $ScriptName ("Adding IPv4 address " . $Address . \ :do {
" to list '" . $FwListName . "' with " . $Timeout . "."); /ip/firewall/address-list/add list=$FwListName comment=$ListComment \
:onerror Err { address=$Address timeout=$Timeout;
/ip/firewall/address-list/add list=$FwListName comment=$ListComment \ :set ($IPv4Addresses->$Address);
address=$Address timeout=$Timeout; :set CntAdd ($CntAdd + 1);
:set CntAdd ($CntAdd + 1); } on-error={
} do={ $LogPrint warning $ScriptName ("Failed to add IPv4 address to list '" . $FwListName . \
$LogPrint warning $ScriptName ("Failed to add IPv4 address " . $Address . \ "': " . $Address);
" to list '" . $FwListName . "': " . $Err);
}
} }
} }
:foreach BranchName,Branch in=$IPv6Addresses do={ :foreach Address,Timeout in=$IPv6Addresses do={
$LogPrintVerbose debug $ScriptName ("Handling branch: " . $BranchName); $LogPrint debug $ScriptName ("Adding IPv6 address to list '" . $FwListName . \
:foreach Address,Timeout in=$Branch do={ "' with " . $Timeout . ": " . $Address);
$LogPrintVerbose debug $ScriptName ("Adding IPv6 address " . $Address . \ :do {
" to list '" . $FwListName . "' with " . $Timeout . "."); /ipv6/firewall/address-list/add list=$FwListName comment=$ListComment \
:onerror Err { address=$Address timeout=$Timeout;
/ipv6/firewall/address-list/add list=$FwListName comment=$ListComment \ :set ($IPv6Addresses->$Address);
address=$Address timeout=$Timeout; :set CntAdd ($CntAdd + 1);
:set CntAdd ($CntAdd + 1); } on-error={
} do={ $LogPrint warning $ScriptName ("Failed to add IPv6 address to list '" . $FwListName . \
$LogPrint warning $ScriptName ("Failed to add IPv6 address " . $Address . \ "': " . $Address);
" to list '" . $FwListName . "': " . $Err);
}
} }
} }
@ -215,6 +188,6 @@
" - renewed: " . [ $HumanReadableNum $CntRenew 1000 ] . \ " - renewed: " . [ $HumanReadableNum $CntRenew 1000 ] . \
" - removed: " . [ $HumanReadableNum $CntRemove 1000 ]); " - removed: " . [ $HumanReadableNum $CntRemove 1000 ]);
} }
} do={ } on-error={
:global ExitError; $ExitError $ExitOK [ :jobname ] $Err; :global ExitError; $ExitError $ExitOK [ :jobname ];
} }

View file

@ -6,12 +6,6 @@
# global configuration # global configuration
# https://rsc.eworm.de/ # 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. # Set this to 'true' to disable news and change notifications.
:global NoNewsAndChangesNotification false; :global NoNewsAndChangesNotification false;
@ -69,12 +63,6 @@
:global NtfyServerToken ""; :global NtfyServerToken "";
:global NtfyTopic ""; :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 # It is possible to override e-mail, Telegram, Matrix and Ntfy setting
# for every script. This is done in arrays, where 'Override' is appended # for every script. This is done in arrays, where 'Override' is appended
# to the variable name, like this: # to the variable name, like this:
@ -115,7 +103,7 @@
# cert="ISRG Root X2" }; # cert="ISRG Root X2" };
{ url="https://raw.githubusercontent.com/stamparm/ipsum/refs/heads/master/levels/4.txt"; { 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 # # higher level (decrease the numerical value) for more addresses, and vice versa
cert="USERTrust RSA Certification Authority" }; cert="DigiCert Global Root G2" };
{ url="https://www.dshield.org/block.txt"; cidr="/24"; { url="https://www.dshield.org/block.txt"; cidr="/24";
cert="ISRG Root X1" }; cert="ISRG Root X1" };
{ url="https://lists.blocklist.de/lists/strongips.txt"; { url="https://lists.blocklist.de/lists/strongips.txt";
@ -270,20 +258,14 @@
"cert2-cn"="4n0th3r-s3cr3t"; "cert2-cn"="4n0th3r-s3cr3t";
}; };
# /\ Warning: Do *NOT* copy
# /\7\ the code below to overlay!
# /_()_\ Things *will* break!
#
# load custom settings from overlay and snippets # 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" ], \ :foreach Script in=([ /system/script/find where name="global-config-overlay" ], \
[ /system/script/find where name~"^global-config-overlay.d/" ]) do={ [ /system/script/find where name~"^global-config-overlay.d/" ]) do={
:onerror Err { :do {
/system/script/run $Script; /system/script/run $Script;
} do={ } on-error={
:log error ("Loading configuration from overlay or snippet " . \ :log error ("Loading configuration from overlay or snippet " . \
[ /system/script/get $Script name ] . " failed: " . $Err); [ /system/script/get $Script name ] . " failed!");
} }
} }
# signal we are ready
:set GlobalConfigReady true;

View file

@ -15,7 +15,7 @@
# Git commit id & info, expected configuration version # Git commit id & info, expected configuration version
:global CommitId "unknown"; :global CommitId "unknown";
:global CommitInfo "unknown"; :global CommitInfo "unknown";
:global ExpectedConfigVersion 138; :global ExpectedConfigVersion 134;
# global variables not to be changed by user # global variables not to be changed by user
:global GlobalFunctionsReady false; :global GlobalFunctionsReady false;
@ -38,8 +38,6 @@
:global ExitError; :global ExitError;
:global FetchHuge; :global FetchHuge;
:global FetchUserAgentStr; :global FetchUserAgentStr;
:global FileExists;
:global FileGet;
:global FormatLine; :global FormatLine;
:global FormatMultiLines; :global FormatMultiLines;
:global GetMacVendor; :global GetMacVendor;
@ -57,7 +55,6 @@
:global IsTimeSync; :global IsTimeSync;
:global LogPrint; :global LogPrint;
:global LogPrintOnce; :global LogPrintOnce;
:global LogPrintVerbose;
:global MAX; :global MAX;
:global MIN; :global MIN;
:global MkDir; :global MkDir;
@ -121,11 +118,6 @@
:return false; :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={ :if ([ :len [ /certificate/find where common-name=$CommonName ] ] = 0) do={
$LogPrint info $0 ("Certificate with CommonName '" . $CommonName . "' not available."); $LogPrint info $0 ("Certificate with CommonName '" . $CommonName . "' not available.");
:if ([ $CertificateDownload $CommonName ] = false) do={ :if ([ $CertificateDownload $CommonName ] = false) do={
@ -174,8 +166,8 @@
$LogPrint warning $0 ("Failed downloading certificate with CommonName '" . $CommonName . \ $LogPrint warning $0 ("Failed downloading certificate with CommonName '" . $CommonName . \
"' from repository! Trying fallback to mkcert.org..."); "' from repository! Trying fallback to mkcert.org...");
:do { :do {
:if ([ :len [ /certificate/find where common-name="ISRG Root X1" ] ] = 0) do={ :if ([ $CertificateAvailable "ISRG Root X1" ] = false) do={
$LogPrint error $0 ("Required certificate is not available."); $LogPrint error $0 ("Downloading required certificate failed.");
:return false; :return false;
} }
/tool/fetch check-certificate=yes-without-crl http-header-field=({ [ $FetchUserAgentStr $0 ] }) \ /tool/fetch check-certificate=yes-without-crl http-header-field=({ [ $FetchUserAgentStr $0 ] }) \
@ -210,19 +202,12 @@
# name a certificate by its common-name # name a certificate by its common-name
:set CertificateNameByCN do={ :set CertificateNameByCN do={
:local Match [ :tostr $1 ]; :local CommonName [ :tostr $1 ];
:global CleanName; :global CleanName;
:global LogPrint;
:local Cert ([ /certificate/find where (common-name=$Match or fingerprint=$Match or name=$Match) ]->0); :local Cert [ /certificate/find where common-name=$CommonName ];
: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 ]; /certificate/set $Cert name=[ $CleanName $CommonName ];
:return true;
} }
# multiply given character(s) # multiply given character(s)
@ -324,19 +309,16 @@
([ $FormatLine "Location" ($Snmp->"location") ] . "\n") ] . \ ([ $FormatLine "Location" ($Snmp->"location") ] . "\n") ] . \
[ $IfThenElse ([ :len ($Snmp->"contact") ] > 0) \ [ $IfThenElse ([ :len ($Snmp->"contact") ] > 0) \
([ $FormatLine "Contact" ($Snmp->"contact") ] . "\n") ] . \ ([ $FormatLine "Contact" ($Snmp->"contact") ] . "\n") ] . \
"Hardware:\n" . \ [ $FormatLine "Board name" ($Resource->"board-name") ] . "\n" . \
[ $FormatLine " Board" ($Resource->"board-name") ] . "\n" . \ [ $FormatLine "Architecture" ($Resource->"architecture-name") ] . "\n" . \
[ $FormatLine " Arch" ($Resource->"architecture-name") ] . "\n" . \
[ $IfThenElse ($RouterBoard->"routerboard" = true) \ [ $IfThenElse ($RouterBoard->"routerboard" = true) \
([ $FormatLine " Model" ($RouterBoard->"model") ] . \ ([ $FormatLine "Model" ($RouterBoard->"model") ] . \
[ $IfThenElse ([ :len ($RouterBoard->"revision") ] > 0) \ [ $IfThenElse ([ :len ($RouterBoard->"revision") ] > 0) \
(" " . $RouterBoard->"revision") ] . "\n" . \ (" " . $RouterBoard->"revision") ] . "\n" . \
[ $FormatLine " Serial" ($RouterBoard->"serial-number") ] . "\n") ] . \ [ $FormatLine "Serial number" ($RouterBoard->"serial-number") ] . "\n") ] . \
[ $IfThenElse ([ :len ($License->"nlevel") ] > 0) \
([ $FormatLine " License" ("level " . ($License->"nlevel")) ] . "\n") ] . \
"RouterOS:\n" . \
[ $IfThenElse ([ :len ($License->"level") ] > 0) \ [ $IfThenElse ([ :len ($License->"level") ] > 0) \
([ $FormatLine " License" ("level " . ($License->"level")) ] . "\n") ] . \ ([ $FormatLine "License" ($License->"level") ] . "\n") ] . \
"RouterOS:\n" . \
[ $FormatLine " Channel" ($Update->"channel") ] . "\n" . \ [ $FormatLine " Channel" ($Update->"channel") ] . "\n" . \
[ $FormatLine " Installed" ($Update->"installed-version") ] . "\n" . \ [ $FormatLine " Installed" ($Update->"installed-version") ] . "\n" . \
[ $IfThenElse ([ :typeof ($Update->"latest-version") ] != "nothing" && \ [ $IfThenElse ([ :typeof ($Update->"latest-version") ] != "nothing" && \
@ -365,7 +347,6 @@
:global CertificateAvailable; :global CertificateAvailable;
:global CleanFilePath; :global CleanFilePath;
:global FileExists;
:global LogPrint; :global LogPrint;
:global MkDir; :global MkDir;
:global RmFile; :global RmFile;
@ -386,7 +367,7 @@
:return false; :return false;
} }
:if ([ $FileExists $PkgDest "package" ] = true) do={ :if ([ :len [ /file/find where name=$PkgDest type="package" ] ] > 0) do={
$LogPrint info $0 ("Package file " . $PkgName . " already exists."); $LogPrint info $0 ("Package file " . $PkgName . " already exists.");
:return true; :return true;
} }
@ -399,22 +380,25 @@
:local Url ("https://upgrade.mikrotik.com/routeros/" . $PkgVer . "/" . $PkgFile); :local Url ("https://upgrade.mikrotik.com/routeros/" . $PkgVer . "/" . $PkgFile);
$LogPrint info $0 ("Downloading package file '" . $PkgName . "'..."); $LogPrint info $0 ("Downloading package file '" . $PkgName . "'...");
$LogPrint debug $0 ("... from url: " . $Url); $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;
:onerror Err { :if ([ /file/get [ find where name=$PkgDest ] type ] = "package") do={
/tool/fetch check-certificate=yes-without-crl $Url dst-path=$PkgDest; :return true;
$WaitForFile $PkgDest; }
} do={ } on-error={
$LogPrint warning $0 ("Downloading package file '" . $PkgName . "' failed: " . $Err); $LogPrint debug $0 ("Downloading package file failed.");
:return false; }
}
:if ([ $FileExists $PkgDest "package" ] = false) do={
$LogPrint warning $0 ("Downloaded file is not a package, removing.");
$RmFile $PkgDest; $RmFile $PkgDest;
:return false; :set Retry ($Retry - 1);
} }
:return true; $LogPrint warning $0 ("Downloading package file '" . $PkgName . "' failed.");
:return false;
} }
# return either first (if "true") or second # return either first (if "true") or second
@ -457,15 +441,13 @@
:set ExitError do={ :set ExitError do={
:local ExitOK [ :tostr $1 ]; :local ExitOK [ :tostr $1 ];
:local Name [ :tostr $2 ]; :local Name [ :tostr $2 ];
:local Error [ :tostr $3 ];
:global IfThenElse; :global IfThenElse;
:global LogPrint; :global LogPrint;
:if ($ExitOK = "false") do={ :if ($ExitOK = "false") do={
$LogPrint error $Name ([ $IfThenElse ([ :pick $Name 0 1 ] = "\$") \ $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) "." ]);
} }
} }
@ -494,14 +476,14 @@
} }
:local FileName ($DirName . "/" . [ $CleanName $0 ] . "-" . [ $GetRandom20CharAlNum ]); :local FileName ($DirName . "/" . [ $CleanName $0 ] . "-" . [ $GetRandom20CharAlNum ]);
:onerror Err { :do {
/tool/fetch check-certificate=$CheckCert $Url dst-path=$FileName \ /tool/fetch check-certificate=$CheckCert $Url dst-path=$FileName \
http-header-field=({ [ $FetchUserAgentStr $ScriptName ] }) as-value; http-header-field=({ [ $FetchUserAgentStr $ScriptName ] }) as-value;
} do={ } on-error={
:if ([ $WaitForFile $FileName 500ms ] = true) do={ :if ([ $WaitForFile $FileName 500ms ] = true) do={
$RmFile $FileName; $RmFile $FileName;
} }
$LogPrint debug $0 ("Failed downloading from " . $Url . " - " . $Err); $LogPrint debug $0 ("Failed downloading from: " . $Url);
$RmDir $DirName; $RmDir $DirName;
:return false; :return false;
} }
@ -532,47 +514,6 @@
$Resource->"architecture-name" . " " . $Caller . "/Fetch (https://rsc.eworm.de/)"); $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 # format a line for output
:set FormatLine do={ :set FormatLine do={
:local Key [ :tostr $1 ]; :local Key [ :tostr $1 ];
@ -635,12 +576,12 @@
("https://api.macvendors.com/" . [ :pick $Mac 0 8 ]) output=user as-value ]->"data"); ("https://api.macvendors.com/" . [ :pick $Mac 0 8 ]) output=user as-value ]->"data");
:return $Vendor; :return $Vendor;
} on-error={ } on-error={
:onerror Err { :do {
/tool/fetch check-certificate=yes-without-crl ("https://api.macvendors.com/") \ /tool/fetch check-certificate=yes-without-crl ("https://api.macvendors.com/") \
output=none as-value; output=none as-value;
$LogPrint debug $0 ("The mac vendor is not known in database."); $LogPrint debug $0 ("The mac vendor is not known in database.");
} do={ } on-error={
$LogPrint warning $0 ("Failed getting mac vendor: " . $Err); $LogPrint warning $0 ("Failed getting mac vendor.");
} }
:return "unknown vendor"; :return "unknown vendor";
} }
@ -904,9 +845,6 @@
:return true; :return true;
} }
# The function $LogPrintVerbose is declared, but has no code, intentionally.
# https://rsc.eworm.de/DEBUG.md#verbose-output
# get max value # get max value
:set MAX do={ :set MAX do={
:if ($1 > $2) do={ :return $1; } :if ($1 > $2) do={ :return $1; }
@ -924,7 +862,6 @@
:local Path [ :tostr $1 ]; :local Path [ :tostr $1 ];
:global CleanFilePath; :global CleanFilePath;
:global FileGet;
:global LogPrint; :global LogPrint;
:global RmDir; :global RmDir;
:global WaitForFile; :global WaitForFile;
@ -944,11 +881,11 @@
$LogPrint info $0 ("Creating disk of type tmpfs."); $LogPrint info $0 ("Creating disk of type tmpfs.");
$RmDir "tmpfs"; $RmDir "tmpfs";
:onerror Err { :do {
/disk/add slot=tmpfs type=tmpfs tmpfs-max-size=([ /system/resource/get total-memory ] / 3); /disk/add slot=tmpfs type=tmpfs tmpfs-max-size=([ /system/resource/get total-memory ] / 3);
$WaitForFile "tmpfs"; $WaitForFile "tmpfs";
} do={ } on-error={
$LogPrint warning $0 ("Creating disk of type tmpfs failed: " . $Err); $LogPrint warning $0 ("Creating disk of type tmpfs failed!");
:return false; :return false;
} }
:return true; :return true;
@ -962,8 +899,7 @@
$LogPrint debug $0 ("Making directory: " . $Path); $LogPrint debug $0 ("Making directory: " . $Path);
:local PathVal [ $FileGet $Path ]; :if ([ :len [ /file/find where name=$Path type="directory" ] ] = 1) do={
:if ($PathVal->"type" = "directory") do={
$LogPrint debug $0 ("... which already exists."); $LogPrint debug $0 ("... which already exists.");
:return true; :return true;
} }
@ -974,11 +910,11 @@
} }
} }
:onerror Err { :do {
/file/add type="directory" name=$Path; /file/add type="directory" name=$Path;
$WaitForFile $Path; $WaitForFile $Path;
} do={ } on-error={
$LogPrint warning $0 ("Making directory '" . $Path . "' failed: " . $Err); $LogPrint warning $0 ("Making directory '" . $Path . "' failed!");
:return false; :return false;
} }
@ -1088,26 +1024,25 @@
:set RmDir do={ :set RmDir do={
:local DirName [ :tostr $1 ]; :local DirName [ :tostr $1 ];
:global FileGet;
:global LogPrint; :global LogPrint;
$LogPrint debug $0 ("Removing directory: ". $DirName); $LogPrint debug $0 ("Removing directory: ". $DirName);
:local DirVal [ $FileGet $DirName ]; :if ([ :len [ /file/find where name=$DirName type!=directory ] ] > 0) do={
:if ($DirVal = false) do={
$LogPrint debug $0 ("... which does not exist.");
:return true;
}
:if ($DirVal->"type" != "directory") do={
$LogPrint error $0 ("Directory '" . $DirName . "' is not a directory."); $LogPrint error $0 ("Directory '" . $DirName . "' is not a directory.");
:return false; :return false;
} }
:onerror Err { :local Dir [ /file/find where name=$DirName type=directory ];
/file/remove $DirName; :if ([ :len $Dir ] = 0) do={
} do={ $LogPrint debug $0 ("... which does not exist.");
$LogPrint error $0 ("Removing directory '" . $DirName . "' failed: " . $Err); :return true;
}
:do {
/file/remove $Dir;
} on-error={
$LogPrint error $0 ("Removing directory '" . $DirName . "' (" . $Dir . ") failed.");
:return false; :return false;
} }
:return true; :return true;
@ -1117,26 +1052,25 @@
:set RmFile do={ :set RmFile do={
:local FileName [ :tostr $1 ]; :local FileName [ :tostr $1 ];
:global FileGet;
:global LogPrint; :global LogPrint;
$LogPrint debug $0 ("Removing file: ". $FileName); $LogPrint debug $0 ("Removing file: ". $FileName);
:local FileVal [ $FileGet $FileName ]; :if ([ :len [ /file/find where name=$FileName (type=directory or type=disk) ] ] > 0) do={
:if ($FileVal = false) do={
$LogPrint debug $0 ("... which does not exist.");
:return true;
}
:if ($FileVal->"type" = "directory" || $FileVal->"type" = "disk") do={
$LogPrint error $0 ("File '" . $FileName . "' is not a file."); $LogPrint error $0 ("File '" . $FileName . "' is not a file.");
:return false; :return false;
} }
:onerror Err { :local File [ /file/find where name=$FileName !(type=directory or type=disk) ];
/file/remove $FileName; :if ([ :len $File ] = 0) do={
} do={ $LogPrint debug $0 ("... which does not exist.");
$LogPrint error $0 ("Removing file '" . $FileName . "' failed: " . $Err); :return true;
}
:do {
/file/remove $File;
} on-error={
$LogPrint error $0 ("Removing file '" . $FileName . "' (" . $File . ") failed.");
:return false; :return false;
} }
:return true; :return true;
@ -1169,15 +1103,13 @@
} }
# install new scripts, update existing scripts # install new scripts, update existing scripts
:set ScriptInstallUpdate do={ :onerror Err { :set ScriptInstallUpdate do={ :do {
:local Scripts [ :toarray $1 ]; :local Scripts [ :toarray $1 ];
:local NewComment [ :tostr $2 ]; :local NewComment [ :tostr $2 ];
:global CommitId; :global CommitId;
:global CommitInfo; :global CommitInfo;
:global ExpectedConfigVersion; :global ExpectedConfigVersion;
:global GlobalConfigReady;
:global GlobalFunctionsReady;
:global Identity; :global Identity;
:global IDonate; :global IDonate;
:global NoNewsAndChangesNotification; :global NoNewsAndChangesNotification;
@ -1211,17 +1143,10 @@
:local CommitIdBefore $CommitId; :local CommitIdBefore $CommitId;
:local ExpectedConfigVersionBefore $ExpectedConfigVersion; :local ExpectedConfigVersionBefore $ExpectedConfigVersion;
:local ReloadGlobal false; :local ReloadGlobalFunctions false;
:local ReloadGlobalConfig false;
:local DeviceMode [ /system/device-mode/get ]; :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={ :foreach Script in=[ /system/script/find where source~"^#!rsc by RouterOS\r?\n" ] do={
:local ScriptVal [ /system/script/get $Script ]; :local ScriptVal [ /system/script/get $Script ];
:local ScriptInfo [ $ParseKeyValueStore ($ScriptVal->"comment") ]; :local ScriptInfo [ $ParseKeyValueStore ($ScriptVal->"comment") ];
@ -1235,26 +1160,8 @@
} }
} }
:do { :if (!($ScriptInfo->"ignore" = true)) do={
:if ($ScriptInfo->"ignore" = true) do={ :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 BaseUrl [ $EitherOr ($ScriptInfo->"base-url") $ScriptUpdatesBaseUrl ];
:local UrlSuffix [ $EitherOr ($ScriptInfo->"url-suffix") $ScriptUpdatesUrlSuffix ]; :local UrlSuffix [ $EitherOr ($ScriptInfo->"url-suffix") $ScriptUpdatesUrlSuffix ];
:local Url ($BaseUrl . $ScriptVal->"name" . ".rsc" . $UrlSuffix); :local Url ($BaseUrl . $ScriptVal->"name" . ".rsc" . $UrlSuffix);
@ -1264,15 +1171,18 @@
:if ($Result->"status" = "finished") do={ :if ($Result->"status" = "finished") do={
:set SourceNew [ :tolf ($Result->"data") ]; :set SourceNew [ :tolf ($Result->"data") ];
} }
} do={ } on-error={
$LogPrint warning $0 ("Failed fetching script '" . $ScriptVal->"name" . "': " . $Err);
:if ($ScriptVal->"source" = "#!rsc by RouterOS\n") do={ :if ($ScriptVal->"source" = "#!rsc by RouterOS\n") do={
$LogPrint warning $0 ("Removing dummy. Typo on installation?"); $LogPrint warning $0 ("Failed fetching script '" . $ScriptVal->"name" . \
"', removing dummy. Typo on installation?");
/system/script/remove $Script; /system/script/remove $Script;
} else={
$LogPrint warning $0 ("Failed fetching script '" . $ScriptVal->"name" . "'!");
} }
:error false;
} }
}
:do {
:if ([ :len $SourceNew ] = 0) do={ :if ([ :len $SourceNew ] = 0) do={
$LogPrint debug $0 ("No update for script '" . $ScriptVal->"name" . "'."); $LogPrint debug $0 ("No update for script '" . $ScriptVal->"name" . "'.");
:error false; :error false;
@ -1318,25 +1228,31 @@
$LogPrint info $0 ("Updating script: " . $ScriptVal->"name"); $LogPrint info $0 ("Updating script: " . $ScriptVal->"name");
/system/script/set owner=($ScriptVal->"name") \ /system/script/set owner=($ScriptVal->"name") \
source=[ $IfThenElse ($ScriptUpdatesCRLF = true) $SourceCRLF $SourceNew ] $Script; source=[ $IfThenElse ($ScriptUpdatesCRLF = true) $SourceCRLF $SourceNew ] $Script;
:if ($ScriptVal->"name" = "global-config" || \ :if ($ScriptVal->"name" = "global-config") do={
$ScriptVal->"name" = "global-functions" || \ :set ReloadGlobalConfig true;
$ScriptVal->"name" ~ ("^mod/.")) do={ }
:set ReloadGlobal true; :if ($ScriptVal->"name" = "global-functions" || $ScriptVal->"name" ~ ("^mod/.")) do={
:set ReloadGlobalFunctions true;
} }
} on-error={ } } on-error={ }
} }
:if ($ReloadGlobal = true) do={ :if ($ReloadGlobalFunctions = true) do={
$LogPrint info $0 ("Reloading global configuration and functions."); $LogPrint info $0 ("Reloading global functions.");
:set GlobalConfigReady false; :do {
:set GlobalFunctionsReady false;
:delay 1s;
:onerror Err {
/system/script/run global-config;
/system/script/run global-functions; /system/script/run global-functions;
} do={ } on-error={
$LogPrint error $0 ("Reloading global configuration and functions failed! " . $Err); $LogPrint error $0 ("Reloading global functions failed!");
}
}
:if ($ReloadGlobalConfig = true) do={
$LogPrint info $0 ("Reloading global configuration.");
:do {
/system/script/run global-config;
} on-error={
$LogPrint error $0 ("Reloading global configuration failed!" . \
" Syntax error or missing overlay?");
} }
} }
@ -1355,7 +1271,7 @@
:global GlobalConfigMigration; :global GlobalConfigMigration;
:local ChangeLogCode; :local ChangeLogCode;
:onerror Err { :do {
:local Url ($ScriptUpdatesBaseUrl . "news-and-changes.rsc" . $ScriptUpdatesUrlSuffix); :local Url ($ScriptUpdatesBaseUrl . "news-and-changes.rsc" . $ScriptUpdatesUrlSuffix);
$LogPrint debug $0 ("Fetching news, changes and migration: " . $Url); $LogPrint debug $0 ("Fetching news, changes and migration: " . $Url);
:local Result [ /tool/fetch check-certificate=yes-without-crl \ :local Result [ /tool/fetch check-certificate=yes-without-crl \
@ -1363,16 +1279,16 @@
:if ($Result->"status" = "finished") do={ :if ($Result->"status" = "finished") do={
:set ChangeLogCode ($Result->"data"); :set ChangeLogCode ($Result->"data");
} }
} do={ } on-error={
$LogPrint warning $0 ("Failed fetching news, changes and migration: " . $Err); $LogPrint warning $0 ("Failed fetching news, changes and migration!");
} }
:if ([ :len $ChangeLogCode ] > 0) do={ :if ([ :len $ChangeLogCode ] > 0) do={
:if ([ $ValidateSyntax $ChangeLogCode ] = true) do={ :if ([ $ValidateSyntax $ChangeLogCode ] = true) do={
:onerror Err { :do {
[ :parse $ChangeLogCode ]; [ :parse $ChangeLogCode ];
} do={ } on-error={
$LogPrint warning $0 ("The changelog failed to run: " . $Err); $LogPrint warning $0 ("The changelog failed to run!");
} }
} else={ } else={
$LogPrint warning $0 ("The changelog failed syntax validation!"); $LogPrint warning $0 ("The changelog failed syntax validation!");
@ -1394,10 +1310,10 @@
} }
$LogPrint info $0 ("Applying migration for change " . $I . ": " . $Migration); $LogPrint info $0 ("Applying migration for change " . $I . ": " . $Migration);
:onerror Err { :do {
[ :parse $Migration ]; [ :parse $Migration ];
} do={ } on-error={
$LogPrint warning $0 ("Migration code for change " . $I . " failed to run: " . $Err); $LogPrint warning $0 ("Migration code for change " . $I . " failed to run!");
} }
} on-error={ } } on-error={ }
} }
@ -1439,14 +1355,14 @@
:set GlobalConfigChanges; :set GlobalConfigChanges;
:set GlobalConfigMigration; :set GlobalConfigMigration;
} }
} do={ } on-error={
:global ExitError; $ExitError false $0 $Err; :global ExitError; $ExitError false $0;
} } } }
# lock script against multiple invocation # lock script against multiple invocation
:set ScriptLock do={ :set ScriptLock do={
:local Script [ :tostr $1 ]; :local Script [ :tostr $1 ];
:local WaitMax [ :totime $2 ]; :local WaitMax ([ :tonum $3 ] * 10);
:global GetRandom20CharAlNum; :global GetRandom20CharAlNum;
:global IfThenElse; :global IfThenElse;
@ -1535,10 +1451,6 @@
:set ($ScriptLockOrder->$Script) ({}); :set ($ScriptLockOrder->$Script) ({});
} }
:if ([ :typeof $WaitMax ] = "nil" ) do={
:set WaitMax 0s;
}
:if ([ :len [ /system/script/find where name=$Script ] ] = 0) do={ :if ([ :len [ /system/script/find where name=$Script ] ] = 0) do={
$LogPrint error $0 ("A script named '" . $Script . "' does not exist!"); $LogPrint error $0 ("A script named '" . $Script . "' does not exist!");
:error false; :error false;
@ -1558,13 +1470,12 @@
:local MyTicket [ $GetRandom20CharAlNum 6 ]; :local MyTicket [ $GetRandom20CharAlNum 6 ];
$AddTicket $Script $MyTicket; $AddTicket $Script $MyTicket;
:local WaitInterval ($WaitMax / 20); :local WaitCount 0;
:local WaitTime $WaitMax; :while ($WaitMax > $WaitCount && \
:while ($WaitTime > 0 && \
([ $IsFirstTicket $Script $MyTicket ] = false || \ ([ $IsFirstTicket $Script $MyTicket ] = false || \
[ $TicketCount $Script ] < [ $JobCount $Script ])) do={ [ $TicketCount $Script ] < [ $JobCount $Script ])) do={
:set WaitTime ($WaitTime - $WaitInterval); :set WaitCount ($WaitCount + 1);
:delay $WaitInterval; :delay 100ms;
} }
:if ([ $IsFirstTicket $Script $MyTicket ] = true && \ :if ([ $IsFirstTicket $Script $MyTicket ] = true && \
@ -1576,17 +1487,17 @@
$RemoveTicket $Script $MyTicket; $RemoveTicket $Script $MyTicket;
$LogPrint debug $0 ("Script '" . $Script . "' started more than once" . \ $LogPrint debug $0 ("Script '" . $Script . "' started more than once" . \
[ $IfThenElse ($WaitTime < $WaitMax) " and timed out waiting for lock" "" ] . "..."); [ $IfThenElse ($WaitCount > 0) " and timed out waiting for lock" "" ] . "...");
:return false; :return false;
} }
# send notification via NotificationFunctions - expects at least two string arguments # send notification via NotificationFunctions - expects at least two string arguments
:set SendNotification do={ :onerror Err { :set SendNotification do={ :do {
:global SendNotification2; :global SendNotification2;
$SendNotification2 ({ origin=$0; subject=$1; message=$2; link=$3; silent=$4 }); $SendNotification2 ({ origin=$0; subject=$1; message=$2; link=$3; silent=$4 });
} do={ } on-error={
:global ExitError; $ExitError false $0 $Err; :global ExitError; $ExitError false $0;
} } } }
# send notification via NotificationFunctions - expects one array argument # send notification via NotificationFunctions - expects one array argument
@ -1705,12 +1616,9 @@
:set ValidateSyntax do={ :set ValidateSyntax do={
:local Code [ :tostr $1 ]; :local Code [ :tostr $1 ];
:global LogPrint; :do {
:onerror Err {
[ :parse (":local Validate do={\n" . $Code . "\n}") ]; [ :parse (":local Validate do={\n" . $Code . "\n}") ];
} do={ } on-error={
$LogPrint debug $0 ("Valdation failed: " . $Err);
:return false; :return false;
} }
:return true; :return true;
@ -1777,16 +1685,15 @@
:global MAX; :global MAX;
:set FileName [ $CleanFilePath $FileName ]; :set FileName [ $CleanFilePath $FileName ];
:local Delay ([ $MAX [ $EitherOr $WaitTime 2s ] 100ms ] / 9); :local I 1;
:local Delay ([ $MAX [ $EitherOr $WaitTime 2s ] 100ms ] / 10);
:do { :while ([ :len [ /file/find where name=$FileName ] ] = 0) do={
:retry { :if ($I >= 10) do={
:if ([ :len [ /file/find where name=$FileName ] ] = 0) do={ :return false;
:error false; }
} :delay $Delay;
} delay=$Delay max=10; :set I ($I + 1);
} on-error={
:return false;
} }
:while ([ :len [ /file/find where name=$FileName ] ] > 0) do={ :while ([ :len [ /file/find where name=$FileName ] ] > 0) do={
@ -1825,10 +1732,10 @@
:foreach Script in=[ /system/script/find where name ~ "^mod/." ] do={ :foreach Script in=[ /system/script/find where name ~ "^mod/." ] do={
:local ScriptVal [ /system/script/get $Script ]; :local ScriptVal [ /system/script/get $Script ];
:if ([ $ValidateSyntax ($ScriptVal->"source") ] = true) do={ :if ([ $ValidateSyntax ($ScriptVal->"source") ] = true) do={
:onerror Err { :do {
/system/script/run $Script; /system/script/run $Script;
} do={ } on-error={
$LogPrint error $0 ("Module '" . $ScriptVal->"name" . "' failed to run: " . $Err); $LogPrint error $0 ("Module '" . $ScriptVal->"name" . "' failed to run.");
} }
} else={ } else={
$LogPrint error $0 ("Module '" . $ScriptVal->"name" . "' failed syntax validation, skipping."); $LogPrint error $0 ("Module '" . $ScriptVal->"name" . "' failed syntax validation, skipping.");

View file

@ -8,6 +8,5 @@
# wait for global-functions to finish # wait for global-functions to finish
# https://rsc.eworm.de/doc/global-wait.md # https://rsc.eworm.de/doc/global-wait.md
:global GlobalConfigReady;
:global GlobalFunctionsReady; :global GlobalFunctionsReady;
:while ($GlobalConfigReady != true || $GlobalFunctionsReady != true) do={ :delay 500ms; } :while ($GlobalFunctionsReady != true) do={ :delay 500ms; }

View file

@ -9,11 +9,11 @@
# track gps data by sending json data to http server # track gps data by sending json data to http server
# https://rsc.eworm.de/doc/gps-track.md # https://rsc.eworm.de/doc/gps-track.md
:global GlobalFunctionsReady;
:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
:local ExitOK false; :local ExitOK false;
:onerror Err { :do {
: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 ScriptName [ :jobname ];
:global GpsTrackUrl; :global GpsTrackUrl;
@ -34,7 +34,7 @@
:local Gps [ /system/gps/monitor once as-value ]; :local Gps [ /system/gps/monitor once as-value ];
:if ($Gps->"valid" = true) do={ :if ($Gps->"valid" = true) do={
:onerror Err { :do {
/tool/fetch check-certificate=yes-without-crl output=none http-method=post \ /tool/fetch check-certificate=yes-without-crl output=none http-method=post \
http-header-field=({ [ $FetchUserAgentStr $ScriptName ]; "Content-Type: application/json" }) \ http-header-field=({ [ $FetchUserAgentStr $ScriptName ]; "Content-Type: application/json" }) \
http-data=[ :serialize to=json { "identity"=$Identity; \ http-data=[ :serialize to=json { "identity"=$Identity; \
@ -42,12 +42,12 @@
$LogPrint debug $ScriptName ("Sending GPS data in " . $CoordinateFormat . " format: " . \ $LogPrint debug $ScriptName ("Sending GPS data in " . $CoordinateFormat . " format: " . \
"lat: " . ($Gps->"latitude") . " " . \ "lat: " . ($Gps->"latitude") . " " . \
"lon: " . ($Gps->"longitude")); "lon: " . ($Gps->"longitude"));
} do={ } on-error={
$LogPrint warning $ScriptName ("Failed sending GPS data: " . $Err); $LogPrint warning $ScriptName ("Failed sending GPS data!");
} }
} else={ } else={
$LogPrint debug $ScriptName ("GPS data not valid."); $LogPrint debug $ScriptName ("GPS data not valid.");
} }
} do={ } on-error={
:global ExitError; $ExitError $ExitOK [ :jobname ] $Err; :global ExitError; $ExitError $ExitOK [ :jobname ];
} }

View file

@ -12,11 +12,11 @@
# #
# !! Do not edit this file, it is generated from template! # !! Do not edit this file, it is generated from template!
:global GlobalFunctionsReady;
:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
:local ExitOK false; :local ExitOK false;
:onerror Err { :do {
: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 ScriptName [ :jobname ];
:global EitherOr; :global EitherOr;
@ -75,6 +75,6 @@
/ip/dhcp-server/lease/remove $Lease; /ip/dhcp-server/lease/remove $Lease;
} }
} }
} do={ } on-error={
:global ExitError; $ExitError $ExitOK [ :jobname ] $Err; :global ExitError; $ExitError $ExitOK [ :jobname ];
} }

View file

@ -13,11 +13,11 @@
# !! This is just a template to generate the real script! # !! This is just a template to generate the real script!
# !! Pattern '%TEMPL%' is replaced, paths are filtered. # !! Pattern '%TEMPL%' is replaced, paths are filtered.
:global GlobalFunctionsReady;
:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
:local ExitOK false; :local ExitOK false;
:onerror Err { :do {
: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 ScriptName [ :jobname ];
:global EitherOr; :global EitherOr;
@ -82,6 +82,6 @@
/ip/dhcp-server/lease/remove $Lease; /ip/dhcp-server/lease/remove $Lease;
} }
} }
} do={ } on-error={
:global ExitError; $ExitError $ExitOK [ :jobname ] $Err; :global ExitError; $ExitError $ExitOK [ :jobname ];
} }

View file

@ -12,11 +12,11 @@
# #
# !! Do not edit this file, it is generated from template! # !! Do not edit this file, it is generated from template!
:global GlobalFunctionsReady;
:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
:local ExitOK false; :local ExitOK false;
:onerror Err { :do {
: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 ScriptName [ :jobname ];
:global EitherOr; :global EitherOr;
@ -75,6 +75,6 @@
/ip/dhcp-server/lease/remove $Lease; /ip/dhcp-server/lease/remove $Lease;
} }
} }
} do={ } on-error={
:global ExitError; $ExitError $ExitOK [ :jobname ] $Err; :global ExitError; $ExitError $ExitOK [ :jobname ];
} }

View file

@ -11,11 +11,11 @@
# #
# !! Do not edit this file, it is generated from template! # !! Do not edit this file, it is generated from template!
:global GlobalFunctionsReady;
:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
:local ExitOK false; :local ExitOK false;
:onerror Err { :do {
: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 ScriptName [ :jobname ];
:global EitherOr; :global EitherOr;
@ -100,6 +100,6 @@
:delay 2s; :delay 2s;
/caps-man/access-list/set $Entry action=accept; /caps-man/access-list/set $Entry action=accept;
} do={ } on-error={
:global ExitError; $ExitError $ExitOK [ :jobname ] $Err; :global ExitError; $ExitError $ExitOK [ :jobname ];
} }

View file

@ -12,11 +12,11 @@
# !! This is just a template to generate the real script! # !! This is just a template to generate the real script!
# !! Pattern '%TEMPL%' is replaced, paths are filtered. # !! Pattern '%TEMPL%' is replaced, paths are filtered.
:global GlobalFunctionsReady;
:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
:local ExitOK false; :local ExitOK false;
:onerror Err { :do {
: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 ScriptName [ :jobname ];
:global EitherOr; :global EitherOr;
@ -120,6 +120,6 @@
:delay 2s; :delay 2s;
/caps-man/access-list/set $Entry action=accept; /caps-man/access-list/set $Entry action=accept;
/interface/wifi/access-list/set $Entry action=accept; /interface/wifi/access-list/set $Entry action=accept;
} do={ } on-error={
:global ExitError; $ExitError $ExitOK [ :jobname ] $Err; :global ExitError; $ExitError $ExitOK [ :jobname ];
} }

View file

@ -11,11 +11,11 @@
# #
# !! Do not edit this file, it is generated from template! # !! Do not edit this file, it is generated from template!
:global GlobalFunctionsReady;
:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
:local ExitOK false; :local ExitOK false;
:onerror Err { :do {
: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 ScriptName [ :jobname ];
:global EitherOr; :global EitherOr;
@ -97,6 +97,6 @@
:delay 2s; :delay 2s;
/interface/wifi/access-list/set $Entry action=accept; /interface/wifi/access-list/set $Entry action=accept;
} do={ } on-error={
:global ExitError; $ExitError $ExitOK [ :jobname ] $Err; :global ExitError; $ExitError $ExitOK [ :jobname ];
} }

View file

@ -9,11 +9,11 @@
# and add/remove/update DNS entries from IPSec mode-config # and add/remove/update DNS entries from IPSec mode-config
# https://rsc.eworm.de/doc/ipsec-to-dns.md # https://rsc.eworm.de/doc/ipsec-to-dns.md
:global GlobalFunctionsReady;
:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
:local ExitOK false; :local ExitOK false;
:onerror Err { :do {
: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 ScriptName [ :jobname ];
:global Domain; :global Domain;
@ -79,6 +79,6 @@
/ip/dns/static/add name=$Fqdn address=($PeerVal->"dynamic-address") ttl=$Ttl comment=$Comment place-before=$PlaceBefore; /ip/dns/static/add name=$Fqdn address=($PeerVal->"dynamic-address") ttl=$Ttl comment=$Comment place-before=$PlaceBefore;
} }
} }
} do={ } on-error={
:global ExitError; $ExitError $ExitOK [ :jobname ] $Err; :global ExitError; $ExitError $ExitOK [ :jobname ];
} }

View file

@ -8,11 +8,11 @@
# update firewall and dns settings on IPv6 prefix change # update firewall and dns settings on IPv6 prefix change
# https://rsc.eworm.de/doc/ipv6-update.md # https://rsc.eworm.de/doc/ipv6-update.md
:global GlobalFunctionsReady;
:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
:local ExitOK false; :local ExitOK false;
:onerror Err { :do {
: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 ScriptName [ :jobname ];
:global LogPrint; :global LogPrint;
@ -20,9 +20,7 @@
:global ScriptLock; :global ScriptLock;
:local NaAddress $"na-address"; :local NaAddress $"na-address";
:local NaValid $"na-valid";
:local PdPrefix $"pd-prefix"; :local PdPrefix $"pd-prefix";
:local PdValid $"pd-valid";
:if ([ $ScriptLock $ScriptName ] = false) do={ :if ([ $ScriptLock $ScriptName ] = false) do={
:set ExitOK true; :set ExitOK true;
@ -35,18 +33,12 @@
:error false; :error false;
} }
:if ([ :typeof $PdPrefix ] = "nothing" || [ :typeof $PdValid ] = "nothing") do={ :if ([ :typeof $PdPrefix ] = "nothing") do={
$LogPrint error $ScriptName ("This script is supposed to run from ipv6 dhcp-client."); $LogPrint error $ScriptName ("This script is supposed to run from ipv6 dhcp-client.");
:set ExitOK true; :set ExitOK true;
:error false; :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 ]; :local Pool [ /ipv6/pool/get [ find where prefix=$PdPrefix ] name ];
:if ([ :len [ /ipv6/firewall/address-list/find where comment=("ipv6-pool-" . $Pool) ] ] = 0) do={ :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; /ipv6/firewall/address-list/add list=("ipv6-pool-" . $Pool) address=:: comment=("ipv6-pool-" . $Pool) dynamic=yes;
@ -102,6 +94,6 @@
} }
} }
} }
} do={ } on-error={
:global ExitError; $ExitError $ExitOK [ :jobname ] $Err; :global ExitError; $ExitError $ExitOK [ :jobname ];
} }

View file

@ -8,11 +8,11 @@
# run scripts on DHCP lease # run scripts on DHCP lease
# https://rsc.eworm.de/doc/lease-script.md # https://rsc.eworm.de/doc/lease-script.md
:global GlobalFunctionsReady;
:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
:local ExitOK false; :local ExitOK false;
:onerror Err { :do {
: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 ScriptName [ :jobname ];
:global Grep; :global Grep;
@ -53,13 +53,13 @@
} }
:foreach Order,Script in=$RunOrder do={ :foreach Order,Script in=$RunOrder do={
:onerror Err { :do {
$LogPrint debug $ScriptName ("Running script with order " . $Order . ": " . $Script); $LogPrint debug $ScriptName ("Running script with order " . $Order . ": " . $Script);
/system/script/run $Script; /system/script/run $Script;
} do={ } on-error={
$LogPrint warning $ScriptName ("Running script '" . $Script . "' failed: " . $Err); $LogPrint warning $ScriptName ("Running script '" . $Script . "' failed!");
} }
} }
} do={ } on-error={
:global ExitError; $ExitError $ExitOK [ :jobname ] $Err; :global ExitError; $ExitError $ExitOK [ :jobname ];
} }

View file

@ -8,11 +8,11 @@
# forward log messages via notification # forward log messages via notification
# https://rsc.eworm.de/doc/log-forward.md # https://rsc.eworm.de/doc/log-forward.md
:global GlobalFunctionsReady;
:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
:local ExitOK false; :local ExitOK false;
:onerror Err { :do {
: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 ScriptName [ :jobname ];
:global Identity; :global Identity;
@ -108,6 +108,6 @@
:local LogAll [ /log/find ]; :local LogAll [ /log/find ];
:set LogForwardLast ($LogAll->([ :len $LogAll ] - 1) ); :set LogForwardLast ($LogAll->([ :len $LogAll ] - 1) );
} do={ } on-error={
:global ExitError; $ExitError $ExitOK [ :jobname ] $Err; :global ExitError; $ExitError $ExitOK [ :jobname ];
} }

BIN
logo.avif

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 KiB

After

Width:  |  Height:  |  Size: 2 KiB

Before After
Before After

BIN
logo.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.3 KiB

After

Width:  |  Height:  |  Size: 4.3 KiB

Before After
Before After

View file

@ -10,7 +10,7 @@
:global BridgePortTo; :global BridgePortTo;
:set BridgePortTo do={ :onerror Err { :set BridgePortTo do={ :do {
:local BridgePortTo [ :tostr $1 ]; :local BridgePortTo [ :tostr $1 ];
:global IfThenElse; :global IfThenElse;
@ -65,6 +65,6 @@
$LogPrint info $0 ("Re-enabling interfaces..."); $LogPrint info $0 ("Re-enabling interfaces...");
/interface/ethernet/enable $InterfaceReEnable; /interface/ethernet/enable $InterfaceReEnable;
} }
} do={ } on-error={
:global ExitError; $ExitError false $0 $Err; :global ExitError; $ExitError false $0;
} } } }

View file

@ -10,7 +10,7 @@
:global BridgePortVlan; :global BridgePortVlan;
:global BridgePortVlan do={ :onerror Err { :global BridgePortVlan do={ :do {
:local ConfigTo [ :tostr $1 ]; :local ConfigTo [ :tostr $1 ];
:global IfThenElse; :global IfThenElse;
@ -74,6 +74,6 @@
$LogPrint info $0 ("Re-enabling interfaces..."); $LogPrint info $0 ("Re-enabling interfaces...");
/interface/ethernet/enable $InterfaceReEnable; /interface/ethernet/enable $InterfaceReEnable;
} }
} do={ } on-error={
:global ExitError; $ExitError false $0 $Err; :global ExitError; $ExitError false $0;
} } } }

View file

@ -12,12 +12,12 @@
:global InspectVarReturn; :global InspectVarReturn;
# inspect variable and print on terminal # inspect variable and print on terminal
:set InspectVar do={ :onerror Err { :set InspectVar do={ :do {
:global InspectVarReturn; :global InspectVarReturn;
:put [ :tocrlf [ $InspectVarReturn $1 ] ]; :put [ :tocrlf [ $InspectVarReturn $1 ] ];
} do={ } on-error={
:global ExitError; $ExitError false $0 $Err; :global ExitError; $ExitError false $0;
} } } }
# inspect variable and return formatted string # inspect variable and return formatted string
@ -25,7 +25,6 @@
:local Input $1; :local Input $1;
:local Level (0 + [ :tonum $2 ]); :local Level (0 + [ :tonum $2 ]);
:global CharacterReplace;
:global IfThenElse; :global IfThenElse;
:global InspectVarReturn; :global InspectVarReturn;
@ -34,13 +33,14 @@
:local Value [ :tostr $2 ]; :local Value [ :tostr $2 ];
:local Level [ :tonum $3 ]; :local Level [ :tonum $3 ];
:global CharacterMultiply; :local Indent "";
:for I from=1 to=$Level step=1 do={
:return ([ $CharacterMultiply " " $Level ] . "-" . $Prefix . "-> " . $Value); :set Indent ($Indent . " ");
}
:return ($Indent . "-" . $Prefix . "-> " . $Value);
} }
:local TypeOf [ :typeof $Input ]; :local TypeOf [ :typeof $Input ];
:local Len [ :len $Input ];
:local Return [ $IndentReturn "type" $TypeOf $Level ]; :local Return [ $IndentReturn "type" $TypeOf $Level ];
:if ($TypeOf = "array") do={ :if ($TypeOf = "array") do={
@ -50,16 +50,6 @@
[ $InspectVarReturn $Value ($Level + 2) ]); [ $InspectVarReturn $Value ($Level + 2) ]);
} }
} else={ } 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={ :if ($TypeOf != "nothing") do={
:set $Return ($Return . "\n" . \ :set $Return ($Return . "\n" . \
[ $IndentReturn "value" [ $IfThenElse ([ :len $Input ] > 80) \ [ $IndentReturn "value" [ $IfThenElse ([ :len $Input ] > 80) \

View file

@ -12,7 +12,7 @@
:global IPCalcReturn; :global IPCalcReturn;
# print netmask, network, min host, max host and broadcast # print netmask, network, min host, max host and broadcast
:set IPCalc do={ :onerror Err { :set IPCalc do={ :do {
:local Input [ :tostr $1 ]; :local Input [ :tostr $1 ];
:global FormatLine; :global FormatLine;
@ -27,8 +27,8 @@
[ $FormatLine "HostMin" ($Values->"hostmin") ] . "\n" . \ [ $FormatLine "HostMin" ($Values->"hostmin") ] . "\n" . \
[ $FormatLine "HostMax" ($Values->"hostmax") ] . "\n" . \ [ $FormatLine "HostMax" ($Values->"hostmax") ] . "\n" . \
[ $FormatLine "Broadcast" ($Values->"broadcast") ]) ]; [ $FormatLine "Broadcast" ($Values->"broadcast") ]) ];
} do={ } on-error={
:global ExitError; $ExitError false $0 $Err; :global ExitError; $ExitError false $0;
} } } }
# calculate and return netmask, network, min host, max host and broadcast # calculate and return netmask, network, min host, max host and broadcast

View file

@ -35,16 +35,14 @@
} }
# flush e-mail queue # flush e-mail queue
:set FlushEmailQueue do={ :onerror Err { :set FlushEmailQueue do={ :do {
:global EmailQueue; :global EmailQueue;
:global EitherOr; :global EitherOr;
:global EMailGenerateFrom; :global EMailGenerateFrom;
:global FileExists;
:global IsDNSResolving; :global IsDNSResolving;
:global IsTimeSync; :global IsTimeSync;
:global LogPrint; :global LogPrint;
:global RmFile;
:local AllDone true; :local AllDone true;
:local QueueLen [ :len $EmailQueue ]; :local QueueLen [ :len $EmailQueue ];
@ -91,40 +89,35 @@
:foreach Id,Message in=$EmailQueue do={ :foreach Id,Message in=$EmailQueue do={
:if ([ :typeof $Message ] = "array" ) do={ :if ([ :typeof $Message ] = "array" ) do={
:local Attach ({});
:while ([ /tool/e-mail/get last-status ] = "in-progress") do={ :delay 1s; } :while ([ /tool/e-mail/get last-status ] = "in-progress") do={ :delay 1s; }
:onerror Err { :foreach File in=[ :toarray [ $EitherOr ($Message->"attach") "" ] ] do={
:local Attach ({}); :if ([ :len [ /file/find where name=$File ] ] = 1) do={
:foreach File in=[ :toarray [ $EitherOr ($Message->"attach") "" ] ] do={ :set Attach ($Attach, $File);
:if ([ $FileExists $File ] = true) do={ } else={
:set Attach ($Attach, $File); $LogPrint warning $0 ("File '" . $File . "' does not exist, can not attach.");
} 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; /tool/e-mail/send from=[ $EMailGenerateFrom ] to=($Message->"to") cc=($Message->"cc") \
:local Wait true; subject=($Message->"subject") body=($Message->"body") file=$Attach;
:do { :local Wait true;
:delay 1s; :do {
:local Status [ /tool/e-mail/get last-status ]; :delay 1s;
:if ($Status = "succeeded") do={ :local Status [ /tool/e-mail/get last-status ];
:set ($EmailQueue->$Id); :if ($Status = "succeeded") do={
:set Wait false; :set ($EmailQueue->$Id);
:if (($Message->"remove-attach") = true) do={ :set Wait false;
:foreach File in=$Attach do={ :if (($Message->"remove-attach") = true) do={
$RmFile $File; :foreach File in=$Attach do={
} /file/remove $File;
} }
} }
:if ($Status = "failed") do={ }
:set AllDone false; :if ($Status = "failed") do={
:set Wait false; :set AllDone false;
} :set Wait false;
} while=($Wait = true); }
} do={ } while=($Wait = true);
$LogPrint warning $0 ("Sending queued mail failed: " . $Err);
:set AllDone false;
}
} }
} }
@ -142,8 +135,8 @@
/system/scheduler/set interval=(($SchedVal->"run-count") . "m") \ /system/scheduler/set interval=(($SchedVal->"run-count") . "m") \
comment="Waiting for retry..." $Scheduler; comment="Waiting for retry..." $Scheduler;
} do={ } on-error={
:global ExitError; $ExitError false $0 $Err; :global ExitError; $ExitError false $0;
} } } }
# generate filter for log-forward # generate filter for log-forward
@ -183,7 +176,6 @@
:global IfThenElse; :global IfThenElse;
:global NotificationEMailSignature; :global NotificationEMailSignature;
:global NotificationEMailSubject; :global NotificationEMailSubject;
:global SymbolForNotification;
:local To [ $EitherOr ($EmailGeneralToOverride->($Notification->"origin")) $EmailGeneralTo ]; :local To [ $EitherOr ($EmailGeneralToOverride->($Notification->"origin")) $EmailGeneralTo ];
:local Cc [ $EitherOr ($EmailGeneralCcOverride->($Notification->"origin")) $EmailGeneralCc ]; :local Cc [ $EitherOr ($EmailGeneralCcOverride->($Notification->"origin")) $EmailGeneralCc ];
@ -196,23 +188,13 @@
:if ([ :typeof $EmailQueue ] = "nothing") do={ :if ([ :typeof $EmailQueue ] = "nothing") do={
:set EmailQueue ({}); :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 ] ]; :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 ]) { :set ($EmailQueue->[ :len $EmailQueue ]) {
to=$To; cc=$Cc; to=$To; cc=$Cc;
subject=[ $NotificationEMailSubject ($Notification->"subject") ]; subject=[ $NotificationEMailSubject ($Notification->"subject") ];
body=$Body; \ body=(($Notification->"message") . \
[ $IfThenElse ([ :len ($Notification->"link") ] > 0) ("\n\n" . ($Notification->"link")) "" ] . \
[ $IfThenElse ([ :len $Signature ] > 0) ("\n-- \n" . $Signature) "" ]); \
attach=($Notification->"attach"); remove-attach=($Notification->"remove-attach") }; attach=($Notification->"attach"); remove-attach=($Notification->"remove-attach") };
:if ([ :len [ /system/scheduler/find where name="_FlushEmailQueue" ] ] = 0) do={ :if ([ :len [ /system/scheduler/find where name="_FlushEmailQueue" ] ] = 0) do={
/system/scheduler/add name="_FlushEmailQueue" interval=1s start-time=startup \ /system/scheduler/add name="_FlushEmailQueue" interval=1s start-time=startup \
@ -266,12 +248,12 @@
} }
# send notification via e-mail - expects at least two string arguments # send notification via e-mail - expects at least two string arguments
:set SendEMail do={ :onerror Err { :set SendEMail do={ :do {
:global SendEMail2; :global SendEMail2;
$SendEMail2 ({ origin=$0; subject=$1; message=$2; link=$3 }); $SendEMail2 ({ origin=$0; subject=$1; message=$2; link=$3 });
} do={ } on-error={
:global ExitError; $ExitError false $0 $Err; :global ExitError; $ExitError false $0;
} } } }
# send notification via e-mail - expects one array argument # send notification via e-mail - expects one array argument

View file

@ -1,139 +0,0 @@
#!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;
}

View file

@ -19,7 +19,7 @@
:global SetupMatrixJoinRoom; :global SetupMatrixJoinRoom;
# flush Matrix queue # flush Matrix queue
:set FlushMatrixQueue do={ :onerror Err { :set FlushMatrixQueue do={ :do {
:global MatrixQueue; :global MatrixQueue;
:global IsFullyConnected; :global IsFullyConnected;
@ -39,7 +39,7 @@
:foreach Id,Message in=$MatrixQueue do={ :foreach Id,Message in=$MatrixQueue do={
:if ([ :typeof $Message ] = "array" ) do={ :if ([ :typeof $Message ] = "array" ) do={
:onerror Err { :do {
/tool/fetch check-certificate=yes-without-crl output=none \ /tool/fetch check-certificate=yes-without-crl output=none \
http-header-field=($Message->"headers") http-method=post \ http-header-field=($Message->"headers") http-method=post \
http-data=[ :serialize to=json { "msgtype"="m.text"; "body"=($Message->"plain"); http-data=[ :serialize to=json { "msgtype"="m.text"; "body"=($Message->"plain");
@ -47,8 +47,8 @@
("https://" . $Message->"homeserver" . "/_matrix/client/r0/rooms/" . $Message->"room" . \ ("https://" . $Message->"homeserver" . "/_matrix/client/r0/rooms/" . $Message->"room" . \
"/send/m.room.message?access_token=" . $Message->"accesstoken") as-value; "/send/m.room.message?access_token=" . $Message->"accesstoken") as-value;
:set ($MatrixQueue->$Id); :set ($MatrixQueue->$Id);
} do={ } on-error={
$LogPrint debug $0 ("Sending queued Matrix message failed: " . $Err); $LogPrint debug $0 ("Sending queued Matrix message failed.");
:set AllDone false; :set AllDone false;
} }
} }
@ -58,8 +58,8 @@
/system/scheduler/remove [ find where name="_FlushMatrixQueue" ]; /system/scheduler/remove [ find where name="_FlushMatrixQueue" ];
:set MatrixQueue; :set MatrixQueue;
} }
} do={ } on-error={
:global ExitError; $ExitError false $0 $Err; :global ExitError; $ExitError false $0;
} } } }
# send notification via Matrix - expects one array argument # send notification via Matrix - expects one array argument
@ -129,15 +129,15 @@
[ $PrepareText $Label ] . "</a>"); [ $PrepareText $Label ] . "</a>");
} }
:onerror Err { :do {
/tool/fetch check-certificate=yes-without-crl output=none \ /tool/fetch check-certificate=yes-without-crl output=none \
http-header-field=$Headers http-method=post \ http-header-field=$Headers http-method=post \
http-data=[ :serialize to=json { "msgtype"="m.text"; "body"=$Plain; http-data=[ :serialize to=json { "msgtype"="m.text"; "body"=$Plain;
"format"="org.matrix.custom.html"; "formatted_body"=$Formatted } ] \ "format"="org.matrix.custom.html"; "formatted_body"=$Formatted } ] \
("https://" . $HomeServer . "/_matrix/client/r0/rooms/" . $Room . \ ("https://" . $HomeServer . "/_matrix/client/r0/rooms/" . $Room . \
"/send/m.room.message?access_token=" . $AccessToken) as-value; "/send/m.room.message?access_token=" . $AccessToken) as-value;
} do={ } on-error={
$LogPrint info $0 ("Failed sending Matrix notification: " . $Err . " - Queuing..."); $LogPrint info $0 ("Failed sending Matrix notification! Queuing...");
:if ([ :typeof $MatrixQueue ] = "nothing") do={ :if ([ :typeof $MatrixQueue ] = "nothing") do={
:set MatrixQueue ({}); :set MatrixQueue ({});
@ -167,12 +167,12 @@
} }
# send notification via Matrix - expects at least two string arguments # send notification via Matrix - expects at least two string arguments
:set SendMatrix do={ :onerror Err { :set SendMatrix do={ :do {
:global SendMatrix2; :global SendMatrix2;
$SendMatrix2 ({ origin=$0; subject=$1; message=$2; link=$3 }); $SendMatrix2 ({ origin=$0; subject=$1; message=$2; link=$3 });
} do={ } on-error={
:global ExitError; $ExitError false $0 $Err; :global ExitError; $ExitError false $0;
} } } }
# send notification via Matrix - expects one array argument # send notification via Matrix - expects one array argument
@ -196,14 +196,14 @@
:global MatrixHomeServer; :global MatrixHomeServer;
:local Domain [ :pick $User ([ :find $User ":" ] + 1) [ :len $User] ]; :local Domain [ :pick $User ([ :find $User ":" ] + 1) [ :len $User] ];
:onerror Err { :do {
:local Data ([ /tool/fetch check-certificate=yes-without-crl output=user \ :local Data ([ /tool/fetch check-certificate=yes-without-crl output=user \
http-header-field=({ [ $FetchUserAgentStr $0 ] }) \ http-header-field=({ [ $FetchUserAgentStr $0 ] }) \
("https://" . $Domain . "/.well-known/matrix/client") as-value ]->"data"); ("https://" . $Domain . "/.well-known/matrix/client") as-value ]->"data");
:set MatrixHomeServer ([ :deserialize from=json value=$Data ]->"m.homeserver"->"base_url"); :set MatrixHomeServer ([ :deserialize from=json value=$Data ]->"m.homeserver"->"base_url");
$LogPrint debug $0 ("Home server is: " . $MatrixHomeServer); $LogPrint debug $0 ("Home server is: " . $MatrixHomeServer);
} do={ } on-error={
$LogPrint error $0 ("Failed getting home server: " . $Err); $LogPrint error $0 ("Failed getting home server!");
:return false; :return false;
} }
@ -211,27 +211,27 @@
:set MatrixHomeServer [ :pick $MatrixHomeServer 8 [ :len $MatrixHomeServer ] ]; :set MatrixHomeServer [ :pick $MatrixHomeServer 8 [ :len $MatrixHomeServer ] ];
} }
:onerror Err { :do {
:local Data ([ /tool/fetch check-certificate=yes-without-crl output=user \ :local Data ([ /tool/fetch check-certificate=yes-without-crl output=user \
http-header-field=({ [ $FetchUserAgentStr $0 ] }) http-method=post \ http-header-field=({ [ $FetchUserAgentStr $0 ] }) http-method=post \
http-data=[ :serialize to=json { "type"="m.login.password"; "user"=$User; "password"=$Pass } ] \ http-data=[ :serialize to=json { "type"="m.login.password"; "user"=$User; "password"=$Pass } ] \
("https://" . $MatrixHomeServer . "/_matrix/client/r0/login") as-value ]->"data"); ("https://" . $MatrixHomeServer . "/_matrix/client/r0/login") as-value ]->"data");
:set MatrixAccessToken ([ :deserialize from=json value=$Data ]->"access_token"); :set MatrixAccessToken ([ :deserialize from=json value=$Data ]->"access_token");
$LogPrint debug $0 ("Access token is: " . $MatrixAccessToken); $LogPrint debug $0 ("Access token is: " . $MatrixAccessToken);
} do={ } on-error={
$LogPrint error $0 ("Failed logging in (and getting access token): " . $Err); $LogPrint error $0 ("Failed logging in (and getting access token)!");
:return false; :return false;
} }
:onerror Err { :do {
/system/script/remove [ find where name="global-config-overlay.d/mod/notification-matrix" ]; /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=( \ /system/script/add name="global-config-overlay.d/mod/notification-matrix" source=( \
"# configuration snippet: mod/notification-matrix\n\n" . \ "# configuration snippet: mod/notification-matrix\n\n" . \
":global MatrixHomeServer \"" . $MatrixHomeServer . "\";\n" . \ ":global MatrixHomeServer \"" . $MatrixHomeServer . "\";\n" . \
":global MatrixAccessToken \"" . $MatrixAccessToken . "\";\n"); ":global MatrixAccessToken \"" . $MatrixAccessToken . "\";\n");
$LogPrint info $0 ("Added configuration snippet. Now create and join a room, please!"); $LogPrint info $0 ("Added configuration snippet. Now create and join a room, please!");
} do={ } on-error={
$LogPrint error $0 ("Failed adding configuration snippet: " . $Err); $LogPrint error $0 ("Failed adding configuration snippet!");
:return false; :return false;
} }
} }
@ -248,24 +248,24 @@
:global MatrixHomeServer; :global MatrixHomeServer;
:global MatrixRoom; :global MatrixRoom;
:onerror Err { :do {
/tool/fetch check-certificate=yes-without-crl output=none \ /tool/fetch check-certificate=yes-without-crl output=none \
http-header-field=({ [ $FetchUserAgentStr $0 ] }) http-method=post http-data="" \ http-header-field=({ [ $FetchUserAgentStr $0 ] }) http-method=post http-data="" \
("https://" . $MatrixHomeServer . "/_matrix/client/r0/rooms/" . [ $UrlEncode $MatrixRoom ] . \ ("https://" . $MatrixHomeServer . "/_matrix/client/r0/rooms/" . [ $UrlEncode $MatrixRoom ] . \
"/join?access_token=" . [ $UrlEncode $MatrixAccessToken ]) as-value; "/join?access_token=" . [ $UrlEncode $MatrixAccessToken ]) as-value;
$LogPrint debug $0 ("Joined the room."); $LogPrint debug $0 ("Joined the room.");
} do={ } on-error={
$LogPrint error $0 ("Failed joining the room: " . $Err); $LogPrint error $0 ("Failed joining the room!");
:return false; :return false;
} }
:onerror Err { :do {
:local Snippet [ /system/script/find where name="global-config-overlay.d/mod/notification-matrix" ]; :local Snippet [ /system/script/find where name="global-config-overlay.d/mod/notification-matrix" ];
/system/script/set $Snippet source=([ get $Snippet source ] . \ /system/script/set $Snippet source=([ get $Snippet source ] . \
":global MatrixRoom \"" . $MatrixRoom . "\";\n"); ":global MatrixRoom \"" . $MatrixRoom . "\";\n");
$LogPrint info $0 ("Appended configuration to configuration snippet. Please review!"); $LogPrint info $0 ("Appended configuration to configuration snippet. Please review!");
} do={ } on-error={
$LogPrint error $0 ("Failed appending configuration to snippet: " . $Err); $LogPrint error $0 ("Failed appending configuration to snippet!");
:return false; :return false;
} }
} }

View file

@ -16,8 +16,9 @@
:global SendNtfy2; :global SendNtfy2;
# flush ntfy queue # flush ntfy queue
:set FlushNtfyQueue do={ :onerror Err { :set FlushNtfyQueue do={ :do {
:global NtfyQueue; :global NtfyQueue;
:global NtfyMessageIDs;
:global IsFullyConnected; :global IsFullyConnected;
:global LogPrint; :global LogPrint;
@ -36,13 +37,13 @@
:foreach Id,Message in=$NtfyQueue do={ :foreach Id,Message in=$NtfyQueue do={
:if ([ :typeof $Message ] = "array" ) do={ :if ([ :typeof $Message ] = "array" ) do={
:onerror Err { :do {
/tool/fetch check-certificate=yes-without-crl output=none http-method=post \ /tool/fetch check-certificate=yes-without-crl output=none http-method=post \
http-header-field=($Message->"headers") http-data=($Message->"text") \ http-header-field=($Message->"headers") http-data=($Message->"text") \
($Message->"url") as-value; ($Message->"url") as-value;
:set ($NtfyQueue->$Id); :set ($NtfyQueue->$Id);
} do={ } on-error={
$LogPrint debug $0 ("Sending queued Ntfy message failed: " . $Err); $LogPrint debug $0 ("Sending queued Ntfy message failed.");
:set AllDone false; :set AllDone false;
} }
} }
@ -52,8 +53,8 @@
/system/scheduler/remove [ find where name="_FlushNtfyQueue" ]; /system/scheduler/remove [ find where name="_FlushNtfyQueue" ];
:set NtfyQueue; :set NtfyQueue;
} }
} do={ } on-error={
:global ExitError; $ExitError false $0 $Err; :global ExitError; $ExitError false $0;
} } } }
# send notification via ntfy - expects one array argument # send notification via ntfy - expects one array argument
@ -107,7 +108,7 @@
:set Text ($Text . "\n" . [ $SymbolForNotification "link" ] . ($Notification->"link")); :set Text ($Text . "\n" . [ $SymbolForNotification "link" ] . ($Notification->"link"));
} }
:onerror Err { :do {
:if ($Server = "ntfy.sh") do={ :if ($Server = "ntfy.sh") do={
:if ([ $CertificateAvailable "ISRG Root X1" ] = false) do={ :if ([ $CertificateAvailable "ISRG Root X1" ] = false) do={
$LogPrint warning $0 ("Downloading required certificate failed."); $LogPrint warning $0 ("Downloading required certificate failed.");
@ -116,8 +117,8 @@
} }
/tool/fetch check-certificate=yes-without-crl output=none http-method=post \ /tool/fetch check-certificate=yes-without-crl output=none http-method=post \
http-header-field=$Headers http-data=$Text $Url as-value; http-header-field=$Headers http-data=$Text $Url as-value;
} do={ } on-error={
$LogPrint info $0 ("Failed sending ntfy notification: " . $Err . " - Queuing..."); $LogPrint info $0 ("Failed sending ntfy notification! Queuing...");
:if ([ :typeof $NtfyQueue ] = "nothing") do={ :if ([ :typeof $NtfyQueue ] = "nothing") do={
:set NtfyQueue ({}); :set NtfyQueue ({});
@ -143,12 +144,12 @@
} }
# send notification via ntfy - expects at least two string arguments # send notification via ntfy - expects at least two string arguments
:set SendNtfy do={ :onerror Err { :set SendNtfy do={ :do {
:global SendNtfy2; :global SendNtfy2;
$SendNtfy2 ({ origin=$0; subject=$1; message=$2; link=$3; silent=$4 }); $SendNtfy2 ({ origin=$0; subject=$1; message=$2; link=$3; silent=$4 });
} do={ } on-error={
:global ExitError; $ExitError false $0 $Err; :global ExitError; $ExitError false $0;
} } } }
# send notification via ntfy - expects one array argument # send notification via ntfy - expects one array argument

View file

@ -10,14 +10,13 @@
# https://rsc.eworm.de/doc/mod/notification-telegram.md # https://rsc.eworm.de/doc/mod/notification-telegram.md
:global FlushTelegramQueue; :global FlushTelegramQueue;
:global GetTelegramChatId;
:global NotificationFunctions; :global NotificationFunctions;
:global PurgeTelegramQueue; :global PurgeTelegramQueue;
:global SendTelegram; :global SendTelegram;
:global SendTelegram2; :global SendTelegram2;
# flush telegram queue # flush telegram queue
:set FlushTelegramQueue do={ :onerror Err { :set FlushTelegramQueue do={ :do {
:global TelegramQueue; :global TelegramQueue;
:global TelegramMessageIDs; :global TelegramMessageIDs;
@ -38,14 +37,14 @@
:foreach Id,Message in=$TelegramQueue do={ :foreach Id,Message in=$TelegramQueue do={
:if ([ :typeof $Message ] = "array" ) do={ :if ([ :typeof $Message ] = "array" ) do={
:onerror Err { :do {
:local Data ([ /tool/fetch check-certificate=yes-without-crl output=user http-method=post \ :local Data ([ /tool/fetch check-certificate=yes-without-crl output=user http-method=post \
("https://api.telegram.org/bot" . ($Message->"tokenid") . "/sendMessage") \ ("https://api.telegram.org/bot" . ($Message->"tokenid") . "/sendMessage") \
http-data=($Message->"http-data") as-value ]->"data"); http-data=($Message->"http-data") as-value ]->"data");
:set ($TelegramQueue->$Id); :set ($TelegramQueue->$Id);
:set ($TelegramMessageIDs->[ :tostr ([ :deserialize from=json value=$Data ]->"result"->"message_id") ]) 1; :set ($TelegramMessageIDs->[ :tostr ([ :deserialize from=json value=$Data ]->"result"->"message_id") ]) 1;
} do={ } on-error={
$LogPrint debug $0 ("Sending queued Telegram message failed: " . $Err); $LogPrint debug $0 ("Sending queued Telegram message failed.");
:set AllDone false; :set AllDone false;
} }
} }
@ -55,47 +54,8 @@
/system/scheduler/remove [ find where name="_FlushTelegramQueue" ]; /system/scheduler/remove [ find where name="_FlushTelegramQueue" ];
:set TelegramQueue; :set TelegramQueue;
} }
} do={ } on-error={
:global ExitError; $ExitError false $0 $Err; :global ExitError; $ExitError false $0;
} }
# 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 # send notification via telegram - expects one array argument
@ -151,8 +111,7 @@
:local ChatId [ $EitherOr ($Notification->"chatid") \ :local ChatId [ $EitherOr ($Notification->"chatid") \
[ $EitherOr ($TelegramChatIdOverride->($Notification->"origin")) $TelegramChatId ] ]; [ $EitherOr ($TelegramChatIdOverride->($Notification->"origin")) $TelegramChatId ] ];
:local ThreadId [ $EitherOr ($Notification->"threadid") \ :local ThreadId [ $EitherOr ($Notification->"threadid") \
[ $EitherOr ($TelegramThreadIdOverride->($Notification->"origin")) \ [ $EitherOr ($TelegramThreadIdOverride->($Notification->"origin")) $TelegramThreadId ] ];
[ $IfThenElse ([ :len ($TelegramChatIdOverride->($Notification->"origin")) ] = 0) $TelegramThreadId ] ] ];
:local TokenId [ $EitherOr ($TelegramTokenIdOverride->($Notification->"origin")) $TelegramTokenId ]; :local TokenId [ $EitherOr ($TelegramTokenIdOverride->($Notification->"origin")) $TelegramTokenId ];
:if ([ :len $TokenId ] = 0 || [ :len $ChatId ] = 0) do={ :if ([ :len $TokenId ] = 0 || [ :len $ChatId ] = 0) do={
@ -189,8 +148,8 @@
:local HTTPData ("chat_id=" . $ChatId . "&disable_notification=" . ($Notification->"silent") . \ :local HTTPData ("chat_id=" . $ChatId . "&disable_notification=" . ($Notification->"silent") . \
"&reply_to_message_id=" . ($Notification->"replyto") . "&message_thread_id=" . $ThreadId . \ "&reply_to_message_id=" . ($Notification->"replyto") . "&message_thread_id=" . $ThreadId . \
"&disable_web_page_preview=true&parse_mode=MarkdownV2"); "&disable_web_page_preview=true&parse_mode=MarkdownV2");
:onerror Err { :do {
:if ([ $CertificateAvailable "Go Daddy Root Certificate Authority - G2" ] = false) do={ :if ([ $CertificateAvailable "Go Daddy Root Certificate Authority - G2" ] = false) do={
$LogPrint warning $0 ("Downloading required certificate failed."); $LogPrint warning $0 ("Downloading required certificate failed.");
:error false; :error false;
@ -199,8 +158,8 @@
("https://api.telegram.org/bot" . $TokenId . "/sendMessage") \ ("https://api.telegram.org/bot" . $TokenId . "/sendMessage") \
http-data=($HTTPData . "&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; :set ($TelegramMessageIDs->[ :tostr ([ :deserialize from=json value=$Data ]->"result"->"message_id") ]) 1;
} do={ } on-error={
$LogPrint info $0 ("Failed sending Telegram notification: " . $Err . " - Queuing..."); $LogPrint info $0 ("Failed sending Telegram notification! Queuing...");
:if ([ :typeof $TelegramQueue ] = "nothing") do={ :if ([ :typeof $TelegramQueue ] = "nothing") do={
:set TelegramQueue ({}); :set TelegramQueue ({});
@ -226,12 +185,12 @@
} }
# send notification via telegram - expects at least two string arguments # send notification via telegram - expects at least two string arguments
:set SendTelegram do={ :onerror Err { :set SendTelegram do={ :do {
:global SendTelegram2; :global SendTelegram2;
$SendTelegram2 ({ origin=$0; subject=$1; message=$2; link=$3; silent=$4 }); $SendTelegram2 ({ origin=$0; subject=$1; message=$2; link=$3; silent=$4 });
} do={ } on-error={
:global ExitError; $ExitError false $0 $Err; :global ExitError; $ExitError false $0;
} } } }
# send notification via telegram - expects one array argument # send notification via telegram - expects one array argument

View file

@ -11,7 +11,7 @@
:global ScriptRunOnce; :global ScriptRunOnce;
# fetch and run script(s) once # fetch and run script(s) once
:set ScriptRunOnce do={ :onerror Err { :set ScriptRunOnce do={ :do {
:local Scripts [ :toarray $1 ]; :local Scripts [ :toarray $1 ];
:global ScriptRunOnceBaseUrl; :global ScriptRunOnceBaseUrl;
@ -41,16 +41,16 @@
:return false; :return false;
} }
:onerror Err { :do {
$LogPrint info $0 ("Running script '" . $Script . "' now."); $LogPrint info $0 ("Running script '" . $Script . "' now.");
[ :parse $Source ]; [ :parse $Source ];
} do={ } on-error={
$LogPrint warning $0 ("The script '" . $Script . "' failed to run: " . $Err); $LogPrint warning $0 ("The script '" . $Script . "' failed to run!");
:return false; :return false;
} }
:return true; :return true;
} }
} do={ } on-error={
:global ExitError; $ExitError false $0 $Err; :global ExitError; $ExitError false $0;
} } } }

View file

@ -12,7 +12,7 @@
:global SSHKeysImportFile; :global SSHKeysImportFile;
# import single key passed as string # import single key passed as string
:set SSHKeysImport do={ :onerror Err { :set SSHKeysImport do={ :do {
:local Key [ :tostr $1 ]; :local Key [ :tostr $1 ];
:local User [ :tostr $2 ]; :local User [ :tostr $2 ];
@ -55,27 +55,26 @@
/file/add name=$FileName contents=($Key . ", md5=" . $FingerPrintMD5); /file/add name=$FileName contents=($Key . ", md5=" . $FingerPrintMD5);
$WaitForFile $FileName; $WaitForFile $FileName;
:onerror Err { :do {
/user/ssh-keys/import public-key-file=$FileName user=$User; /user/ssh-keys/import public-key-file=$FileName user=$User;
$LogPrint info $0 ("Imported ssh public key (" . $KeyVal->2 . ", " . $KeyVal->0 . ", " . \ $LogPrint info $0 ("Imported ssh public key (" . $KeyVal->2 . ", " . $KeyVal->0 . ", " . \
"MD5:" . $FingerPrintMD5 . ") for user '" . $User . "'."); "MD5:" . $FingerPrintMD5 . ") for user '" . $User . "'.");
$RmDir "tmpfs/ssh-keys-import"; $RmDir "tmpfs/ssh-keys-import";
} do={ } on-error={
$LogPrint warning $0 ("Failed importing key: " . $Err); $LogPrint warning $0 ("Failed importing key.");
$RmDir "tmpfs/ssh-keys-import"; $RmDir "tmpfs/ssh-keys-import";
:return false; :return false;
} }
} do={ } on-error={
:global ExitError; $ExitError false $0 $Err; :global ExitError; $ExitError false $0;
} } } }
# import keys from a file # import keys from a file
:set SSHKeysImportFile do={ :onerror Err { :set SSHKeysImportFile do={ :do {
:local FileName [ :tostr $1 ]; :local FileName [ :tostr $1 ];
:local User [ :tostr $2 ]; :local User [ :tostr $2 ];
:global EitherOr; :global EitherOr;
:global FileExists;
:global LogPrint; :global LogPrint;
:global ParseKeyValueStore; :global ParseKeyValueStore;
:global SSHKeysImport; :global SSHKeysImport;
@ -85,7 +84,8 @@
:return false; :return false;
} }
:if ([ $FileExists $FileName ] = true) do={ :local File [ /file/find where name=$FileName ];
:if ([ :len $File ] = 0) do={
$LogPrint warning $0 ("File '" . $FileName . "' does not exist."); $LogPrint warning $0 ("File '" . $FileName . "' does not exist.");
:return false; :return false;
} }
@ -94,7 +94,9 @@
:foreach KeyVal in=[ :deserialize $Keys delimiter=" " from=dsv options=dsv.plain ] do={ :foreach KeyVal in=[ :deserialize $Keys delimiter=" " from=dsv options=dsv.plain ] do={
:local Continue false; :local Continue false;
:if ($KeyVal->0 = "ssh-ed25519" || $KeyVal->0 = "ssh-rsa") do={ :if ($KeyVal->0 = "ssh-ed25519" || $KeyVal->0 = "ssh-rsa") do={
:if ([ $SSHKeysImport ($KeyVal->0 . " " . $KeyVal->1 . " " . $KeyVal->2) $User ] = false) do={ :do {
$SSHKeysImport ($KeyVal->0 . " " . $KeyVal->1 . " " . $KeyVal->2) $User;
} on-error={
$LogPrint warning $0 ("Failed importing key for user '" . $User . "'."); $LogPrint warning $0 ("Failed importing key for user '" . $User . "'.");
} }
:set Continue true; :set Continue true;
@ -107,6 +109,6 @@
$LogPrint warning $0 ("SSH key of type '" . $KeyVal->0 . "' is not supported."); $LogPrint warning $0 ("SSH key of type '" . $KeyVal->0 . "' is not supported.");
} }
} }
} do={ } on-error={
:global ExitError; $ExitError false $0 $Err; :global ExitError; $ExitError false $0;
} } } }

View file

@ -9,11 +9,11 @@
# act on multiple mode and reset button presses # act on multiple mode and reset button presses
# https://rsc.eworm.de/doc/mode-button.md # https://rsc.eworm.de/doc/mode-button.md
:global GlobalFunctionsReady;
:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
:local ExitOK false; :local ExitOK false;
:onerror Err { :do {
: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 ScriptName [ :jobname ];
:global ModeButton; :global ModeButton;
@ -26,7 +26,7 @@
:if ([ :len $Scheduler ] = 0) do={ :if ([ :len $Scheduler ] = 0) do={
$LogPrint info $ScriptName ("Creating scheduler _ModeButtonScheduler, counting presses..."); $LogPrint info $ScriptName ("Creating scheduler _ModeButtonScheduler, counting presses...");
:global ModeButtonScheduler do={ :onerror Err { :global ModeButtonScheduler do={ :do {
:local FuncName $0; :local FuncName $0;
:global ModeButton; :global ModeButton;
@ -69,11 +69,11 @@
:delay 200ms; :delay 200ms;
} }
:onerror Err { :do {
[ :parse $Code ]; [ :parse $Code ];
} do={ } on-error={
$LogPrint warning $FuncName \ $LogPrint warning $FuncName \
("The code for " . $Count . " mode-button presses failed with runtime error: " . $Err); ("The code for " . $Count . " mode-button presses failed with runtime error!");
} }
} else={ } else={
$LogPrint warning $FuncName \ $LogPrint warning $FuncName \
@ -82,8 +82,8 @@
} else={ } else={
$LogPrint info $FuncName ("No action defined for " . $Count . " mode-button presses."); $LogPrint info $FuncName ("No action defined for " . $Count . " mode-button presses.");
} }
} do={ } on-error={
:global ExitError; $ExitError false $0 $Err; :global ExitError; $ExitError false $0;
} } } }
/system/scheduler/add name="_ModeButtonScheduler" \ /system/scheduler/add name="_ModeButtonScheduler" \
on-event=":global ModeButtonScheduler; \$ModeButtonScheduler;" interval=3s; on-event=":global ModeButtonScheduler; \$ModeButtonScheduler;" interval=3s;
@ -91,6 +91,6 @@
$LogPrint debug $ScriptName ("Updating scheduler _ModeButtonScheduler..."); $LogPrint debug $ScriptName ("Updating scheduler _ModeButtonScheduler...");
/system/scheduler/set $Scheduler start-time=[ /system/clock/get time ]; /system/scheduler/set $Scheduler start-time=[ /system/clock/get time ];
} }
} do={ } on-error={
:global ExitError; $ExitError $ExitOK [ :jobname ] $Err; :global ExitError; $ExitError $ExitOK [ :jobname ];
} }

View file

@ -9,11 +9,11 @@
# monitor and manage dns/doh with netwatch # monitor and manage dns/doh with netwatch
# https://rsc.eworm.de/doc/netwatch-dns.md # https://rsc.eworm.de/doc/netwatch-dns.md
:global GlobalFunctionsReady;
:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
:local ExitOK false; :local ExitOK false;
:onerror Err { :do {
: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 ScriptName [ :jobname ];
:global CertificateAvailable; :global CertificateAvailable;
@ -118,17 +118,15 @@
} }
:local Data false; :local Data false;
:onerror Err { :do {
:retry { :set Data ([ /tool/fetch check-certificate=yes-without-crl output=user \
:set Data ([ /tool/fetch check-certificate=yes-without-crl output=user \ http-header-field=({ "accept: application/dns-message" }) \
http-header-field=({ "accept: application/dns-message" }) \ url=(($DohServer->"doh-url") . "?dns=" . [ :convert to=base64 ([ :rndstr length=2 ] . \
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" . \
"\01\00" . "\00\01" . "\00\00" . "\00\00" . "\00\00" . "\09doh-check\05eworm\02de\00" . \ "\00\10" . "\00\01") ]) as-value ]->"data");
"\00\10" . "\00\01") ]) as-value ]->"data"); } on-error={
} delay=1s max=3; $LogPrint warning $ScriptName ("Request to DoH server failed (network or certificate issue): " . \
} do={ ($DohServer->"doh-url"));
$LogPrint warning $ScriptName ("Request to DoH server " . ($DohServer->"doh-url") . \
" failed: " . $Err);
} }
:if ($Data != false) do={ :if ($Data != false) do={
@ -147,6 +145,6 @@
} }
} }
} }
} do={ } on-error={
:global ExitError; $ExitError $ExitOK [ :jobname ] $Err; :global ExitError; $ExitError $ExitOK [ :jobname ];
} }

View file

@ -8,11 +8,11 @@
# monitor netwatch and send notifications # monitor netwatch and send notifications
# https://rsc.eworm.de/doc/netwatch-notify.md # https://rsc.eworm.de/doc/netwatch-notify.md
:global GlobalFunctionsReady;
:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
:local ExitOK false; :local ExitOK false;
:onerror Err { :do {
: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 ScriptName [ :jobname ];
:global NetwatchNotify; :global NetwatchNotify;
@ -38,10 +38,10 @@
:global ValidateSyntax; :global ValidateSyntax;
:if ([ $ValidateSyntax $Hook ] = true) do={ :if ([ $ValidateSyntax $Hook ] = true) do={
onerror Err { :do {
[ :parse $Hook ]; [ :parse $Hook ];
} do={ } on-error={
$LogPrint warning $ScriptName ("The " . $State . "-hook for " . $Type . " '" . $Name . "' failed to run: " . $Err); $LogPrint warning $ScriptName ("The " . $State . "-hook for " . $Type . " '" . $Name . "' failed to run.");
:return ("The hook failed to run."); :return ("The hook failed to run.");
} }
} else={ } else={
@ -61,19 +61,15 @@
:global GetRandom20CharAlNum; :global GetRandom20CharAlNum;
:local FwAddrList ($ScriptName . "-" . [ $GetRandom20CharAlNum ]); :local FwAddrList ($ScriptName . "-" . [ $GetRandom20CharAlNum ]);
:if ([ :typeof [ :toip $Expected ] ] = "ip") do={ /ip/firewall/address-list/add address=$Name list=$FwAddrList dynamic=yes timeout=1s;
/ip/firewall/address-list/add address=$Name list=$FwAddrList dynamic=yes timeout=10s; :delay 20ms;
:delay 20ms; :if ([ :len [ /ip/firewall/address-list/find where list=$FwAddrList address=$Expected ] ] > 0) do={
:if ([ :len [ /ip/firewall/address-list/find where list=$FwAddrList address=$Expected ] ] > 0) do={ :return true;
:return true;
}
} }
:if ([ :typeof [ :toip6 $Expected ] ] = "ip6") do={ /ipv6/firewall/address-list/add address=$Name list=$FwAddrList dynamic=yes timeout=1s;
/ipv6/firewall/address-list/add address=$Name list=$FwAddrList dynamic=yes timeout=10s; :delay 20ms;
:delay 20ms; :if ([ :len [ /ipv6/firewall/address-list/find where list=$FwAddrList address=$Expected ] ] > 0) do={
:if ([ :len [ /ipv6/firewall/address-list/find where list=$FwAddrList address=$Expected ] ] > 0) do={ :return true;
:return true;
}
} }
:return false; :return false;
@ -107,7 +103,7 @@
:if ([ :typeof ($HostInfo->"resolve") ] = "str") do={ :if ([ :typeof ($HostInfo->"resolve") ] = "str") do={
:if ([ $IsDNSResolving ] = true) do={ :if ([ $IsDNSResolving ] = true) do={
:onerror Err { :do {
:local Resolve [ :resolve type=[ $IfThenElse ([ :typeof ($HostVal->"host") ] = "ip") \ :local Resolve [ :resolve type=[ $IfThenElse ([ :typeof ($HostVal->"host") ] = "ip") \
"ipv4" "ipv6" ] ($HostInfo->"resolve") ]; "ipv4" "ipv6" ] ($HostInfo->"resolve") ];
:if ($Resolve != $HostVal->"host") do={ :if ($Resolve != $HostVal->"host") do={
@ -121,13 +117,13 @@
:set ($HostVal->"status") "unknown"; :set ($HostVal->"status") "unknown";
} }
} }
} do={ } on-error={
:set ($Metric->"resolve-failcnt") ($Metric->"resolve-failcnt" + 1); :set ($Metric->"resolve-failcnt") ($Metric->"resolve-failcnt" + 1);
:if ($Metric->"resolve-failcnt" = 3) do={ :if ($Metric->"resolve-failcnt" = 3) do={
$LogPrint [ $IfThenElse ($HostInfo->"no-resolve-fail" != true) warning debug ] \ $LogPrint [ $IfThenElse ($HostInfo->"no-resolve-fail" != true) warning debug ] \
$ScriptName ("Resolving name '" . $HostInfo->"resolve" . [ $IfThenElse \ $ScriptName ("Resolving name '" . $HostInfo->"resolve" . [ $IfThenElse \
($HostInfo->"resolve" != $HostInfo->"name") ("' for " . $Type . " '" . \ ($HostInfo->"resolve" != $HostInfo->"name") ("' for " . $Type . " '" . \
$HostInfo->"name") "" ] . "' failed: " . $Err); $HostInfo->"name") "" ] . "' failed.");
} }
} }
} }
@ -224,6 +220,6 @@
"since"=($Metric->"since") }; "since"=($Metric->"since") };
} }
} }
} do={ } on-error={
:global ExitError; $ExitError $ExitOK [ :jobname ] $Err; :global ExitError; $ExitError $ExitOK [ :jobname ];
} }

View file

@ -59,10 +59,6 @@
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."; 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."; 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."; 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 # Migration steps to be applied on script updates
@ -72,5 +68,4 @@
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;"; 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; }"; 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={ }; }; }"; 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 ];";
}; };

Some files were not shown because too many files have changed in this diff Show more