Compare commits

...

304 commits

Author SHA1 Message Date
Christian Hesse
89175e511f accesslist-duplicates: print without paging 2025-07-07 18:38:21 +02:00
Christian Hesse
b068f86995 netwatch-dns: fix indention 2025-06-25 10:47:12 +02:00
Christian Hesse
d46574b4fe netwatch-dns: retry doh server...
... for more resilience on bad connectivity or saturated link.
2025-06-25 10:30:26 +02:00
Christian Hesse
c3010af4ed Merge branch 'file' into next 2025-06-23 08:52:58 +02:00
Christian Hesse
1307b8587e global-functions: $FileGet: mitigate race with file properties
RouterOS is suffering a race condition, where a file exists, but its
properties are not (yet) available. This is handled in $WaitForFile.

This passes an interval of zero to $WaitForFile, as does not wait for
the file to exist, but wants to avoid the race only.
2025-06-23 08:52:58 +02:00
Christian Hesse
6415849850 global-functions: $WaitForFile: (mostly) revert changes
This (mostly) reverts commits 0e00a228d6
and e08bb2192d.

This is required for RouterOS 7.20beta4. That fixed recursive find for
files, and (again, or still?) suffers timing (and thus racing) issues
getting file properties.

This breaks RouterOS 7.20beta2 again, so that specific version is not
supported. Just update...
2025-06-23 08:52:51 +02:00
Christian Hesse
95f8af6234 packages-update: convert to time before comparing...
... to avoid:

    packages-update: Script 'packages-update' exited with error: Script Error: cannot compare if truth value is more than or equal to ip address
2025-06-20 23:52:07 +02:00
Christian Hesse
de2a90d841 doc/fw-addr-lists: add a warning on possible subsystem crash 2025-06-11 17:47:40 +02:00
Christian Hesse
e2d3f0f073 fw-addr-lists: delay on possible scripting subsystem crash
This happens in :convert when a list is way too large.

Let's use $LogPrintOnce here. If the scripting subsystem really crashes
the message will be purged from $LogPrintOnceMessages anyway (as all
global variables are lost).
2025-06-11 17:46:09 +02:00
Christian Hesse
595b4aea9d capsman-download-packages: revert changes for "new functionality"
(Though we keep the quoting for type.)

Well, turned out this functionality is for `/file/print` only,
but does not work with `/file/find`. 🫣🥴

This reverts commit 15fd522d3d.
2025-06-11 17:46:09 +02:00
Christian Hesse
0de6d006ae update list of contributors 2025-06-04 22:30:51 +02:00
Christian Hesse
1f4bf9ee63 check-routeros-update: remove a stale scheduler 2025-06-04 22:30:51 +02:00
Christian Hesse
c3d3d61f92 packages-update: support deferred reboot with longer interval 2025-06-04 22:30:51 +02:00
Christian Hesse
6130c94cc1 Merge branch 'file' into next 2025-06-04 22:30:51 +02:00
Christian Hesse
15fd522d3d capsman-download-packages: adopt new functionality from file menu 2025-06-04 22:30:51 +02:00
Christian Hesse
5b15c82bb1 capsman-download-packages: use $FileGet ...
... to work around restrictions in new file handling.
2025-06-04 22:30:48 +02:00
Christian Hesse
30b80e903d telegram-chat: use $FileExists ...
... to work around restrictions in new file handling.
2025-06-04 22:30:45 +02:00
Christian Hesse
80aed200fd mod/ssh-keys-import: use $FileExists ...
... to work around restrictions in new file handling.
2025-06-04 22:30:42 +02:00
Christian Hesse
2d81984aed mod/notification-email: use $RmFile 2025-06-04 22:30:42 +02:00
Christian Hesse
e3284ca770 mod/notification-email: use $FileExists ...
... to work around restrictions in new file handling.
2025-06-04 22:30:39 +02:00
Christian Hesse
daee05dbd7 backup-email: add a comment why files are not removed 2025-06-04 22:30:39 +02:00
Christian Hesse
43bac7c33c backup-email: check for .conf file 2025-06-04 22:30:39 +02:00
Christian Hesse
a2f837be59 backup-email: use :retry and $FileExists ...
... to work around restrictions in new file handling.
2025-06-04 22:30:36 +02:00
Christian Hesse
8353a8547f global-functions: $DownloadPackage: use $FileExists ...
... to work around restrictions in new file handling.
2025-06-04 22:30:33 +02:00
Christian Hesse
cb984a5e52 global-functions: introduce $FileExists 2025-06-04 22:28:25 +02:00
Christian Hesse
0e00a228d6 global-functions: $WaitForFile: use :retry for simplification, ...
... and to work around restrictions in new file handling.
2025-06-04 22:28:20 +02:00
Christian Hesse
e08bb2192d global-functions: $WaitForFile: drop the workaround
This was fixed in RouterOS 7.18rc1, so should be ok to remove now.
2025-06-04 22:28:20 +02:00
Christian Hesse
fb8e616846 global-functions: $RmFile: use $FileGet ...
... to work around restrictions in new file handling.
2025-06-04 22:28:17 +02:00
Christian Hesse
d993495e44 global-functions: $RmDir: use $FileGet ...
... to work around restrictions in new file handling.
2025-06-04 22:28:14 +02:00
Christian Hesse
1e4f168735 global-functions: $MkDir: use $FileGet ...
... to work around restrictions in new file handling.
2025-06-04 22:28:11 +02:00
Christian Hesse
b70e6e7984 global-functions: introduce $FileGet 2025-06-03 12:29:42 +02:00
Christian Hesse
4bc3bf40e6 Merge branch 'builtin-certs' into next 2025-06-03 11:06:59 +02:00
Christian Hesse
d69b399572 INITIAL-COMMANDS: use builtin certificates if possible 2025-06-03 11:06:59 +02:00
Christian Hesse
bf684a7197 global-functions: $CertificateAvailable: try to use builtin certificates
The builtin certificates were introduced with RouterOS 7.19, so requires
this hacky :parse workaround.
2025-05-28 15:08:26 +02:00
Christian Hesse
d59c4aee26 README: add a paragraph and link to jump 2025-05-28 14:16:57 +02:00
Christian Hesse
3d3b270748 README: give a hint on builtin certificate store
I guess this should become the default any time in future...
2025-05-28 14:07:57 +02:00
Christian Hesse
6a49c483b6 telegram-chat: rename variable...
... to better describe the use.
2025-05-23 17:38:22 +02:00
Christian Hesse
c50acd697a telegram-chat: fix detection of replies 2025-05-23 17:36:56 +02:00
Christian Hesse
4bd7d44cd2 global-functions: $ScriptInstallUpdate: fix syntax error 2025-05-23 11:16:40 +02:00
Christian Hesse
86ad41f6b6 fix the trust state for certificates
RouterOS 7.19 is suffering an issue with certificate store, where the
trust state is not available correctly. This effects certificates
imported a long time ago, with RouterOS 7.4 or older.

Fixing trust state for all certificates by re-setting the trust state...

(Reported as SUP-188791...)
2025-05-23 10:51:04 +02:00
Christian Hesse
64fe923c5a DEBUG: fix typo 2025-05-22 10:13:52 +02:00
Christian Hesse
f1396b83aa telegram-chat: support debug output for updates in JSON 2025-05-22 10:13:52 +02:00
Christian Hesse
1b5dd1e1d3 telegram-chat: support trusted group chat ids
But be warned: Adding a person to a trusted group will give
her/him administrative control over the device(s) - without
changes on the device itself!

On the other hand... Removing permissions is easily done by removing
a person from a group.

Closes: https://github.com/eworm-de/routeros-scripts/issues/101
2025-05-22 10:13:52 +02:00
Christian Hesse
e78c71948d mod/notification-telegram: fix indention 2025-05-22 10:13:52 +02:00
Christian Hesse
ce129ee441 fw-addr-lists: for IPv6 the CIDR is always expected 2025-05-22 10:13:52 +02:00
Christian Hesse
2a95687477 mod/notification-email: add the link symbol 2025-05-22 10:13:52 +02:00
Christian Hesse
74dc809b98 mod/notification-email: properly truncate the body
Truned out that the size limit for e-mail message/body is anywhere just
below 64kB... So truncate  at about 62.000 bytes.
2025-05-22 10:13:52 +02:00
Christian Hesse
ff218e4ce5 mod/notification-email: add error handling when sending mail 2025-05-22 10:13:52 +02:00
Christian Hesse
bd3cc3bbd4 mod/inspectvar: replace only when matching 2025-05-22 10:13:52 +02:00
Christian Hesse
36f155ce62 mod/inspectvar: remove CR, replace LF 2025-05-22 10:13:52 +02:00
Christian Hesse
187735f35f mod/inspectvar: print the length for strings 2025-05-22 10:13:52 +02:00
Christian Hesse
3521995dbc mod/inspectvar: use $CharacterMultiply 2025-05-22 10:13:52 +02:00
Christian Hesse
1f4290f4be Merge branch 'onerror' into next 2025-05-22 10:13:52 +02:00
Christian Hesse
205bcce6f9 update-tunnelbroker: :do ... on-error=... -> :onerror ... do=... 2025-05-22 10:13:52 +02:00
Christian Hesse
10ac816348 unattended-lte-firmware-upgrade: :do ... on-error=... -> :onerror ... do=... 2025-05-22 10:13:52 +02:00
Christian Hesse
f22d46da96 telegram-chat: :do ... on-error=... -> :onerror ... do=... 2025-05-22 10:13:52 +02:00
Christian Hesse
d0adf9974e sms-forward: :do ... on-error=... -> :onerror ... do=... 2025-05-22 10:13:52 +02:00
Christian Hesse
fefe59751f netwatch-notify: :do ... on-error=... -> :onerror ... do=... 2025-05-22 10:13:52 +02:00
Christian Hesse
a523f97938 netwatch-dns: :do ... on-error=... -> :onerror ... do=... 2025-05-22 10:13:52 +02:00
Christian Hesse
c757064eb9 mod/ssh-keys-import: :do ... on-error=... -> ... well... - fix condition 🥴 2025-05-22 10:13:47 +02:00
Christian Hesse
9cd4ed08a4 mod/ssh-keys-import: :do ... on-error=... -> :onerror ... do=... 2025-05-21 22:12:25 +02:00
Christian Hesse
9ae21c00bd mod/scriptrunonce: :do ... on-error=... -> :onerror ... do=... 2025-05-21 22:12:25 +02:00
Christian Hesse
314e68e4cd mode-button: :do ... on-error=... -> :onerror ... do=... 2025-05-21 22:12:25 +02:00
Christian Hesse
e01873301f mod/notification-gotify: :do ... on-error=... -> :onerror ... do=... 2025-05-21 22:12:25 +02:00
Christian Hesse
3f092d3477 mod/notification-matrix: :do ... on-error=... -> :onerror ... do=... 2025-05-21 22:12:25 +02:00
Christian Hesse
2a7fd7ea53 mod/notification-ntfy: :do ... on-error=... -> :onerror ... do=... 2025-05-21 22:12:25 +02:00
Christian Hesse
5badafe109 mod/notification-telegram: :do ... on-error=... -> :onerror ... do=... 2025-05-21 22:12:25 +02:00
Christian Hesse
c1e6348bb9 lease-script: :do ... on-error=... -> :onerror ... do=... 2025-05-21 22:12:25 +02:00
Christian Hesse
d5f9ecebfb gps-track: :do ... on-error=... -> :onerror ... do=... 2025-05-21 22:12:25 +02:00
Christian Hesse
5c599beae1 global-functions: :do ... on-error=... -> :onerror ... do=... 2025-05-21 22:12:25 +02:00
Christian Hesse
8b1b73c936 global-functions: $ValidateSyntax: add debug output 2025-05-21 22:12:25 +02:00
Christian Hesse
f5c4378676 global-functions: $ValidateSyntax: :do ... on-error=... -> :onerror ... do=... 2025-05-21 22:12:25 +02:00
Christian Hesse
12926b7c42 global-functions: $ScriptInstallUpdate: :do ... on-error=... -> :onerror ... do=... 2025-05-21 22:12:25 +02:00
Christian Hesse
2317013121 global-functions: $RmFile: :do ... on-error=... -> :onerror ... do=... 2025-05-21 22:12:25 +02:00
Christian Hesse
86a8919ed2 global-functions: $RmDir: :do ... on-error=... -> :onerror ... do=... 2025-05-21 22:12:25 +02:00
Christian Hesse
09f9826760 global-functions: $MkDir: :do ... on-error=... -> :onerror ... do=... 2025-05-21 22:12:25 +02:00
Christian Hesse
7a1fef78a2 global-functions: $GetMacVendor: :do ... on-error=... -> :onerror ... do=... 2025-05-21 22:12:25 +02:00
Christian Hesse
a6b36dde7b global-functions: $FetchHuge: :do ... on-error=... -> :onerror ... do=... 2025-05-21 22:12:25 +02:00
Christian Hesse
5b89f3e425 global-functions: $DownloadPackage: :do ... on-error=... -> :onerror ... do=... 2025-05-21 22:12:25 +02:00
Christian Hesse
a9e7bb0a05 global-config: :do ... on-error=... -> :onerror ... do=... 2025-05-21 22:12:25 +02:00
Christian Hesse
b807fc9e90 fw-addr-lists: adopt changes in wording...
... to match the changes from previous commit.
2025-05-21 22:12:10 +02:00
Christian Hesse
142b0760b0 fw-addr-lists: :do ... on-error=... -> :onerror ... do=... 2025-05-21 22:05:35 +02:00
Christian Hesse
79f681b801 check-routeros-update: :do ... on-error=... -> :onerror ... do=... 2025-05-21 22:05:35 +02:00
Christian Hesse
1925d2847c check-lte-firmware-upgrade: :do ... on-error=... -> :onerror ... do=... 2025-05-21 22:05:35 +02:00
Christian Hesse
187aeba78b check-health: :do ... on-error=... -> :onerror ... do=... 2025-05-21 22:05:35 +02:00
Christian Hesse
dc2296d500 backup-upload: :do ... on-error=... -> :onerror ... do=... 2025-05-21 22:05:35 +02:00
Christian Hesse
c3fce37066 backup-partition: :do ... on-error=... -> :onerror ... do=... 2025-05-21 22:05:35 +02:00
Christian Hesse
6691e2e765 global-functions: $DownloadPackage: reverse failure logic...
... and do not retry.
2025-05-21 21:51:16 +02:00
Christian Hesse
01f3cb91e7 sms-forward: add error handling on message removal 2025-05-13 09:22:45 +02:00
Christian Hesse
9085780562 sms-forward: delay after removal...
... to give the SIM card some time to succeed. Removing several messages
one after another fails without this.
2025-05-13 09:22:45 +02:00
Christian Hesse
f095b581e7 global-functions: $ScriptInstallUpdate: set config and functions not ready...
... before reload. This should fix some corner cases where scripts ran
with broken configuration or functions.

Also do reload both - configuration and functions - to simplify the
function.
2025-05-12 12:09:24 +02:00
Christian Hesse
661aad522b global-config: add another visual hint 2025-05-12 12:09:24 +02:00
Christian Hesse
4561b17dbf global-config: add another comment with visual hint 2025-05-12 12:09:24 +02:00
Christian Hesse
39295b4954 Merge branch 'global-config-ready' into next 2025-05-08 09:51:08 +02:00
Christian Hesse
0f58cecc0f global-wait: check for global config to be ready 2025-05-08 09:51:08 +02:00
Christian Hesse
f2dbb9eff3 update-tunnelbroker: check for global config to be ready 2025-05-08 09:51:08 +02:00
Christian Hesse
b4d80d3b17 update-gre-address: check for global config to be ready 2025-05-08 09:51:08 +02:00
Christian Hesse
9b5419b939 telegram-chat: check for global config to be ready 2025-05-08 09:51:08 +02:00
Christian Hesse
15248ffd39 sms-forward: check for global config to be ready 2025-05-08 09:51:08 +02:00
Christian Hesse
6270ac45d8 sms-action: check for global config to be ready 2025-05-08 09:51:08 +02:00
Christian Hesse
194698f53f ppp-on-up: check for global config to be ready 2025-05-08 09:51:08 +02:00
Christian Hesse
38ec392a5f packages-update: check for global config to be ready 2025-05-08 09:51:08 +02:00
Christian Hesse
aaecd1b457 ospf-to-leds: check for global config to be ready 2025-05-08 09:51:08 +02:00
Christian Hesse
5b40380cd2 netwatch-notify: check for global config to be ready 2025-05-08 09:51:08 +02:00
Christian Hesse
2d39dbdf7c netwatch-dns: check for global config to be ready 2025-05-08 09:51:08 +02:00
Christian Hesse
bc1c238158 mode-button: check for global config to be ready 2025-05-08 09:51:08 +02:00
Christian Hesse
160effd91b log-forward: check for global config to be ready 2025-05-08 09:51:08 +02:00
Christian Hesse
10bd9a1cb5 lease-script: check for global config to be ready 2025-05-08 09:51:08 +02:00
Christian Hesse
59f9a1187f ipv6-update: check for global config to be ready 2025-05-08 09:51:08 +02:00
Christian Hesse
ce29fcf49e ipsec-to-dns: check for global config to be ready 2025-05-08 09:51:08 +02:00
Christian Hesse
6a9fced721 hotspot-to-wpa: check for global config to be ready 2025-05-08 09:51:08 +02:00
Christian Hesse
713e63da87 hotspot-to-wpa-cleanup: check for global config to be ready 2025-05-08 09:51:08 +02:00
Christian Hesse
148892694f gps-track: check for global config to be ready 2025-05-08 09:51:08 +02:00
Christian Hesse
9e70bca30b fw-addr-lists: check for global config to be ready 2025-05-08 09:51:08 +02:00
Christian Hesse
a68763c4dd firmware-upgrade-reboot: check for global config to be ready 2025-05-08 09:51:08 +02:00
Christian Hesse
857993cf90 dhcp-to-dns: check for global config to be ready 2025-05-08 09:51:08 +02:00
Christian Hesse
ad6ad5bfcc dhcp-lease-comment: check for global config to be ready 2025-05-08 09:51:08 +02:00
Christian Hesse
be0e9be1ba Merge branch 'global-functions-fail' into next 2025-05-08 09:51:07 +02:00
Christian Hesse
b19c448f78 daily-psk: check for global config to be ready 2025-05-08 09:51:07 +02:00
Christian Hesse
2afe734bc3 Merge branch 'onerror-for-outer-block' into next 2025-05-08 09:51:07 +02:00
Christian Hesse
6040f70b19 update-tunnelbroker: fail if global functions do not become ready 2025-05-08 09:51:07 +02:00
Christian Hesse
286ed96cc3 collect-wireless-mac: check for global config to be ready 2025-05-08 09:51:07 +02:00
Christian Hesse
af40ae82ed mod/ssh-keys-import: use :onerror for outer block 2025-05-08 09:51:07 +02:00
Christian Hesse
87edd0bd29 update-gre-address: fail if global functions do not become ready 2025-05-08 09:51:07 +02:00
Christian Hesse
59c66de690 check-routeros-update: check for global config to be ready 2025-05-08 09:51:07 +02:00
Christian Hesse
5958851bd7 mod/scriptrunonce: use :onerror for outer block 2025-05-08 09:51:07 +02:00
Christian Hesse
35bce7c5eb telegram-chat: fail if global functions do not become ready 2025-05-08 09:51:07 +02:00
Christian Hesse
78463f469b check-perpetual-license: check for global config to be ready 2025-05-08 09:51:07 +02:00
Christian Hesse
f7ba78702f mod/notification-telegram: use :onerror for outer block 2025-05-08 09:51:07 +02:00
Christian Hesse
24ea16cf91 sms-forward: fail if global functions do not become ready 2025-05-08 09:51:07 +02:00
Christian Hesse
506acb748b check-lte-firmware-upgrade: check for global config to be ready 2025-05-08 09:51:07 +02:00
Christian Hesse
e3a15cc6b8 mod/notification-ntfy: use :onerror for outer block 2025-05-08 09:51:07 +02:00
Christian Hesse
0ef8888412 sms-action: fail if global functions do not become ready 2025-05-08 09:51:07 +02:00
Christian Hesse
1936dcb563 check-health: check for global config to be ready 2025-05-08 09:51:07 +02:00
Christian Hesse
d865deb8a8 mod/notification-matrix: use :onerror for outer block 2025-05-08 09:51:07 +02:00
Christian Hesse
7b624faaa1 ppp-on-up: fail if global functions do not become ready 2025-05-08 09:51:07 +02:00
Christian Hesse
4954a88695 check-certificates: check for global config to be ready 2025-05-08 09:51:07 +02:00
Christian Hesse
9d845d40f0 mod/notification-gotify: use :onerror for outer block 2025-05-08 09:51:07 +02:00
Christian Hesse
4358dabb19 packages-update: fail if global functions do not become ready 2025-05-08 09:51:07 +02:00
Christian Hesse
ef1a402c4e certificate-renew-issued: check for global config to be ready 2025-05-08 09:51:07 +02:00
Christian Hesse
862830b341 mod/notification-email: use :onerror for outer block 2025-05-08 09:51:07 +02:00
Christian Hesse
ff812dd192 ospf-to-leds: fail if global functions do not become ready 2025-05-08 09:51:07 +02:00
Christian Hesse
40b19a179d capsman-rolling-upgrade: check for global config to be ready 2025-05-08 09:51:07 +02:00
Christian Hesse
8cddc63767 mod/ipcalc: use :onerror for outer block 2025-05-08 09:51:07 +02:00
Christian Hesse
446aa3d7ff netwatch-notify: fail if global functions do not become ready 2025-05-08 09:51:07 +02:00
Christian Hesse
12037ff6e6 capsman-download-packages: check for global config to be ready 2025-05-08 09:51:07 +02:00
Christian Hesse
9b558ff8a1 mod/inspectvar: use :onerror for outer block 2025-05-08 09:51:07 +02:00
Christian Hesse
2374be9a21 netwatch-dns: fail if global functions do not become ready 2025-05-08 09:51:07 +02:00
Christian Hesse
27fc07e940 backup-upload: check for global config to be ready 2025-05-08 09:51:07 +02:00
Christian Hesse
d5141f43e5 mod/bridge-port-vlan: use :onerror for outer block 2025-05-08 09:51:07 +02:00
Christian Hesse
63606ad25c mode-button: fail if global functions do not become ready 2025-05-08 09:51:07 +02:00
Christian Hesse
200167fa38 backup-partition: check for global config to be ready 2025-05-08 09:51:07 +02:00
Christian Hesse
02986eb77a mod/bridge-port-to: use :onerror for outer block 2025-05-08 09:51:07 +02:00
Christian Hesse
a6ab95ccef log-forward: fail if global functions do not become ready 2025-05-08 09:51:07 +02:00
Christian Hesse
37a6d4cc43 backup-email: check for global config to be ready 2025-05-08 09:51:07 +02:00
Christian Hesse
d2af793683 update-tunnelbroker: use :onerror for outer block 2025-05-08 09:51:07 +02:00
Christian Hesse
829fce789b lease-script: fail if global functions do not become ready 2025-05-08 09:51:07 +02:00
Christian Hesse
d57916fc26 backup-cloud: check for global config to be ready 2025-05-08 09:51:07 +02:00
Christian Hesse
fe3d9f8418 update-gre-address: use :onerror for outer block 2025-05-08 09:51:07 +02:00
Christian Hesse
41b705aa5a ipv6-update: fail if global functions do not become ready 2025-05-08 09:51:07 +02:00
Christian Hesse
60c5e84401 accesslist-duplicates: check for global config to be ready 2025-05-08 09:51:07 +02:00
Christian Hesse
3ad8cafc7a telegram-chat: use :onerror for outer block 2025-05-08 09:51:07 +02:00
Christian Hesse
83cf003f62 ipsec-to-dns: fail if global functions do not become ready 2025-05-08 09:51:07 +02:00
Christian Hesse
bcdb74f31d global-config: introduce a state variable 2025-05-08 09:51:07 +02:00
Christian Hesse
aae9b4d4e3 sms-forward: use :onerror for outer block 2025-05-08 09:51:07 +02:00
Christian Hesse
057bbb8481 hotspot-to-wpa: fail if global functions do not become ready 2025-05-08 09:51:07 +02:00
Christian Hesse
3eec13b8b5 sms-action: use :onerror for outer block 2025-05-08 09:51:07 +02:00
Christian Hesse
76778f0431 hotspot-to-wpa-cleanup: fail if global functions do not become ready 2025-05-08 09:51:07 +02:00
Christian Hesse
aeccd36f16 ppp-on-up: use :onerror for outer block 2025-05-08 09:51:07 +02:00
Christian Hesse
925e7f869e gps-track: fail if global functions do not become ready 2025-05-08 09:51:07 +02:00
Christian Hesse
9a5a2aec4f packages-update: use :onerror for outer block 2025-05-08 09:51:07 +02:00
Christian Hesse
4b92181f2e fw-addr-lists: fail if global functions do not become ready 2025-05-08 09:51:07 +02:00
Christian Hesse
c720eadd2c ospf-to-leds: use :onerror for outer block 2025-05-08 09:51:07 +02:00
Christian Hesse
4d1349125d firmware-upgrade-reboot: fail if global functions do not become ready 2025-05-08 09:51:07 +02:00
Christian Hesse
0df4170a2e netwatch-notify: use :onerror for outer block 2025-05-08 09:51:07 +02:00
Christian Hesse
a308d4269e dhcp-to-dns: fail if global functions do not become ready 2025-05-08 09:51:07 +02:00
Christian Hesse
2d41ad718d netwatch-dns: use :onerror for outer block 2025-05-08 09:51:07 +02:00
Christian Hesse
a556f4c398 dhcp-lease-comment: fail if global functions do not become ready 2025-05-08 09:51:07 +02:00
Christian Hesse
ff03f49724 mode-button: use :onerror for outer block 2025-05-08 09:51:07 +02:00
Christian Hesse
8418d58ff0 daily-psk: fail if global functions do not become ready 2025-05-08 09:51:07 +02:00
Christian Hesse
2712bcb44d collect-wireless-mac: fail if global functions do not become ready 2025-05-08 09:51:07 +02:00
Christian Hesse
69b2af4bd7 check-routeros-update: fail if global functions do not become ready 2025-05-08 09:51:07 +02:00
Christian Hesse
08d78224a0 check-perpetual-license: fail if global functions do not become ready 2025-05-08 09:51:07 +02:00
Christian Hesse
8f1ac6fa17 check-lte-firmware-upgrade: fail if global functions do not become ready 2025-05-08 09:51:07 +02:00
Christian Hesse
96cbb32a4f check-health: fail if global functions do not become ready 2025-05-08 09:51:07 +02:00
Christian Hesse
9b811e1ed2 check-certificates: fail if global functions do not become ready 2025-05-08 09:51:07 +02:00
Christian Hesse
b4188ab1a2 certificate-renew-issued: fail if global functions do not become ready 2025-05-08 09:51:07 +02:00
Christian Hesse
de5aab12ae capsman-rolling-upgrade: fail if global functions do not become ready 2025-05-08 09:51:07 +02:00
Christian Hesse
33393798b1 capsman-download-packages: fail if global functions do not become ready 2025-05-08 09:51:07 +02:00
Christian Hesse
9423d8019a backup-upload: fail if global functions do not become ready 2025-05-08 09:51:07 +02:00
Christian Hesse
bf1b3e3c5e backup-partition: fail if global functions do not become ready 2025-05-08 09:51:07 +02:00
Christian Hesse
64ec962bb6 backup-email: fail if global functions do not become ready 2025-05-08 09:51:07 +02:00
Christian Hesse
7d4e4ec273 backup-cloud: fail if global functions do not become ready 2025-05-08 09:51:07 +02:00
Christian Hesse
9acf9781cb accesslist-duplicates: fail if global functions do not become ready 2025-05-08 09:51:07 +02:00
Christian Hesse
c643069b3e log-forward: use :onerror for outer block 2025-05-06 09:55:07 +02:00
Christian Hesse
da93138017 lease-script: use :onerror for outer block 2025-05-06 09:55:04 +02:00
Christian Hesse
39ab19b272 ipv6-update: use :onerror for outer block 2025-05-06 09:55:01 +02:00
Christian Hesse
1075b6b24e ipsec-to-dns: use :onerror for outer block 2025-05-06 09:54:57 +02:00
Christian Hesse
9ae733a167 hotspot-to-wpa: use :onerror for outer block 2025-05-06 09:54:53 +02:00
Christian Hesse
143b8a8ddc hotspot-to-wpa-cleanup: use :onerror for outer block 2025-05-06 09:54:48 +02:00
Christian Hesse
d609da0041 gps-track: use :onerror for outer block 2025-05-06 09:54:45 +02:00
Christian Hesse
0e93d8ca66 fw-addr-lists: use :onerror for outer block 2025-05-06 09:54:42 +02:00
Christian Hesse
d99380443b firmware-upgrade-reboot: use :onerror for outer block 2025-05-06 09:54:38 +02:00
Christian Hesse
8ed83a311d dhcp-to-dns: use :onerror for outer block 2025-05-06 09:54:35 +02:00
Christian Hesse
5dc6b712e1 dhcp-lease-comment: use :onerror for outer block 2025-05-06 09:54:29 +02:00
Christian Hesse
55e07a8a42 daily-psk: use :onerror for outer block 2025-05-06 09:54:24 +02:00
Christian Hesse
7d8e9fda29 collect-wireless-mac: use :onerror for outer block 2025-05-06 09:54:18 +02:00
Christian Hesse
472dc9289d check-routeros-update: use :onerror for outer block 2025-05-06 09:54:14 +02:00
Christian Hesse
f5c33a9cfb check-perpetual-license: use :onerror for outer block 2025-05-06 09:54:10 +02:00
Christian Hesse
b959f2d941 check-lte-firmware-upgrade: use :onerror for outer block 2025-05-06 09:54:06 +02:00
Christian Hesse
051a1d95e5 check-health: use :onerror for outer block 2025-05-06 09:54:02 +02:00
Christian Hesse
e44a5384b7 check-certificates: use :onerror for outer block 2025-05-06 09:53:59 +02:00
Christian Hesse
009516dbd4 certificate-renew-issued: use :onerror for outer block 2025-05-06 09:53:54 +02:00
Christian Hesse
54a3012e89 capsman-rolling-upgrade: use :onerror for outer block 2025-05-06 09:53:46 +02:00
Christian Hesse
e0ba2c8282 capsman-download-packages: use :onerror for outer block 2025-05-06 09:53:37 +02:00
Christian Hesse
0a9acab040 backup-upload: use :onerror for outer block 2025-05-06 09:53:34 +02:00
Christian Hesse
34c052c5f1 backup-partition: use :onerror for outer block 2025-05-06 09:53:31 +02:00
Christian Hesse
953daca8ac backup-email: use :onerror for outer block 2025-05-06 09:53:28 +02:00
Christian Hesse
02a205b14e backup-cloud: use :onerror for outer block 2025-05-06 09:53:24 +02:00
Christian Hesse
b9faeb86b9 accesslist-duplicates: use :onerror for outer block 2025-05-06 09:53:19 +02:00
Christian Hesse
41bf9677db global-functions: use :onerror for outer block 2025-05-06 09:50:20 +02:00
Christian Hesse
90f61d3d75 global-functions: $ExitError: support to pass in error message 2025-05-06 09:43:57 +02:00
Christian Hesse
ccfe1a781e check-routeros-update: log and print info with scheduler 2025-05-06 09:43:57 +02:00
Christian Hesse
154fb43800 mod/notification-ntfy: drop unused variable 2025-05-03 22:11:22 +02:00
Christian Hesse
fa83c76be1 doc/check-lte-firmware-upgrade: add missing reference for ntfy 2025-05-03 22:11:22 +02:00
Christian Hesse
07541a3cbc update list of contributors 2025-05-03 22:11:22 +02:00
Leonardo David Monteiro
0717ebfbd5 introduce mod/notification-gotify...
... for sending notifications via Gotify (https://gotify.net).

Closes: https://github.com/eworm-de/routeros-scripts/pull/92

Co-authored-by: Christian Hesse <mail@eworm.de>
2025-05-03 22:11:22 +02:00
Christian Hesse
507a520994 Merge branch 'external-links' into next 2025-04-29 10:53:26 +02:00
Christian Hesse
fe64918118 README: add disclaimer on external links 2025-04-29 10:53:26 +02:00
Christian Hesse
3822887ba2 doc/mod/notification-telegram: mark all external links 2025-04-24 23:24:53 +02:00
Christian Hesse
c69b13c879 doc/mod/notification-notify: mark all external links 2025-04-24 23:24:50 +02:00
Christian Hesse
183b16d83c doc/mod/notification-matrix: mark all external links 2025-04-24 23:24:47 +02:00
Christian Hesse
0eaefcdc72 doc/mod/notification-email: mark all external links 2025-04-24 23:24:43 +02:00
Christian Hesse
2fa044972c doc/log-forward: mark all external links 2025-04-24 23:24:39 +02:00
Christian Hesse
3788a3e286 doc/fw-addr-lists: mark all external links 2025-04-24 23:24:36 +02:00
Christian Hesse
34eac64f33 doc/check-routeros-update: mark all external links 2025-04-24 23:24:32 +02:00
Christian Hesse
4118f53aae doc/backup-partition: mark all external links 2025-04-24 23:24:28 +02:00
Christian Hesse
b12f8a3974 doc/backup-cloud: mark all external links 2025-04-24 23:24:25 +02:00
Christian Hesse
a1437a4c83 CONTRIBUTIONS: mark all external links 2025-04-24 23:24:25 +02:00
Christian Hesse
8328400e87 CERTIFICATES: mark all external links 2025-04-24 23:24:25 +02:00
Christian Hesse
158230070f BRANCHES: mark all external links 2025-04-24 23:24:25 +02:00
Christian Hesse
405c329f39 README: mark all external links 2025-04-24 23:24:25 +02:00
Christian Hesse
b78df91b23 README: and another hint on RouterOS version 2025-04-17 22:22:05 +02:00
Christian Hesse
29bcd191ee global-functions: $ScriptInstallUpdate: support downloading certificate...
... for individual scripts. Just add it in comment with
"certificate=...". This also works on installtion:

$ScriptInstallUpdate new-script "base-url=..., certificate=...";

Closes: https://github.com/eworm-de/routeros-scripts/pull/97
2025-04-17 10:48:45 +02:00
Christian Hesse
d80f43a1c8 INITIAL-COMMANDS: fix typos 2025-04-14 11:19:46 +02:00
Miquel Bonastre
390e3653d7 INITIAL-COMMANDS: add status output
Co-authored-by: Christian Hesse <mail@eworm.de>
2025-04-14 11:19:42 +02:00
Miquel Bonastre
75163f0d3c INITIAL-COMMANDS: support installation from custom server
Closes: https://github.com/eworm-de/routeros-scripts/pull/96

Co-authored-by: Christian Hesse <mail@eworm.de>
2025-04-14 11:19:24 +02:00
Christian Hesse
d4b5e1f5e7 global-functions: $CertificateNameByCN: warn with no match 2025-04-10 11:08:43 +02:00
Christian Hesse
c823ff87ed global-functions: $CertificateNameByCN: return false without match...
... and return true on success.
2025-04-08 16:09:53 +02:00
Christian Hesse
44fa91f5c4 global-functions: $CertificateNameByCN: pick the first match only 2025-04-08 16:09:53 +02:00
Christian Hesse
e36613608c global-functions: $CertificateNameByCN: support matching by fingerprint and name 2025-04-08 16:09:53 +02:00
Christian Hesse
019e10e190 global-functions: $CertificateDownload: no infinite loop
We can not call $CertificateAvailable here, as that will most likely
cause an infinite loop. After all that's the certificate mkcert.org is
using. And it *is* available in this repository.
2025-04-08 16:09:53 +02:00
Christian Hesse
314ba5796d global-functions: $ScriptLock: increase interval with wait time
Inspired by: https://github.com/eworm-de/routeros-scripts/issues/95#issuecomment-2773513467
2025-04-08 16:09:53 +02:00
Christian Hesse
67e7b11aa7 update list of contributors 2025-04-08 09:13:59 +02:00
Ilya Kulakov
27987a0d7c global-functions: $ScriptLock: fix second parameter
This broke with 1e8918fdaa5a30393e2004d1f5e4dff458936b67...

Fixes: https://github.com/eworm-de/routeros-scripts/issues/95
2025-04-08 09:11:45 +02:00
Christian Hesse
2cc47f56b9 README: give the script names 2025-04-02 11:32:24 +02:00
Christian Hesse
b560ea4b7d logo: strip some bytes...
... by just re-compressing/re-encoding.
2025-04-01 17:39:50 +02:00
Christian Hesse
cdfb086b49 mod/notification-telegram: fix override quirk
IDs for chat and thread can be overridden. Overriding the chat probably
makes the thread invalid - so ignore that then.
2025-04-01 17:39:50 +02:00
Christian Hesse
3c30276e23 check-routeros-update: split off check-perpetual-license...
... and also add documentation, screenshot, etc.
2025-04-01 17:39:50 +02:00
Christian Hesse
16c9ce437e check-routeros-update: send notification on renewed license...
... when a warning has been sent before.
2025-03-27 22:26:39 +01:00
Christian Hesse
6909514692 check-routeros-update: send certificate warning just once...
... and another one once expired.
2025-03-27 10:27:54 +01:00
Christian Hesse
f8c3659f6a support creating the checksums file 2025-03-27 10:27:54 +01:00
Christian Hesse
ce39b79f69 capsman-download-packages: fix parameter for $RmFile
The function can not handle ids, we have to pass a name instead.
2025-03-13 11:50:38 +01:00
Christian Hesse
20bf609c44 check-routeros-update: fix condition for license check
Turns out that `next-renewal-at` is moved forward when renewal failed,
so it never matches the criteria. Just start complaining three weeks
before deadline.
2025-03-13 10:51:39 +01:00
Christian Hesse
b63e0fcb2f netwatch-notify: check matching address type only 2025-03-12 11:26:22 +01:00
Christian Hesse
1555426687 netwatch-notify: increase the timeout even more
This interacts with the number of addresses in the address-list. Having
a lot of addresses there (for exemple from script 'fw-addr-lists' 😜)
makes the 'find' take longer. We have to make sure that 'find' succeeds
before the address times out.

As this does not hurt... Let's just bump to 10 seconds to be safe.
2025-03-12 11:18:18 +01:00
Christian Hesse
97b99316b2 netwatch-notify: increase timeout...
... as a timeout of one second expires immediately. 🤨
2025-03-12 10:31:11 +01:00
Christian Hesse
788400c458 fw-addr-lists: raw.githubusercontent.com requires 'USERTrust RSA Certification Authority' now 2025-03-11 15:51:25 +01:00
Christian Hesse
eb59dd21ca check-routeros-update: check perpetual license...
... as these have to be renewed and can expire.
2025-03-11 15:51:25 +01:00
Christian Hesse
79a4b369cb Merge branch 'fw-addr-lists' into next 2025-03-11 15:51:25 +01:00
Christian Hesse
f0e6cbcfe1 fw-addr-lists: get branch from calculated checksum
The addresses were spread very uneven before.

Let's calculate a checksum, and take the first two characters of that.
The addresses are now spread evenly on 256 branches (0x00 to 0xff).
2025-03-11 15:51:10 +01:00
Christian Hesse
d71ea804b0 fw-addr-lists: two characters for branch
Using one character for IPv4 is ok (1 to 9), but IPv6 global unicase
(2000::/3) results in just two different characters (2 and 3).

So let's use first two characters...
2025-03-11 14:20:50 +01:00
Christian Hesse
e148df9e57 fw-addr-lists: put addresses into "branches"...
... effectively adding another layer and some complexity, but:
The addresses are sorted inside the array, and sorting less addresses in
a branch saves a lot of processing power. So this is a lot faster now...
2025-03-11 14:20:44 +01:00
Christian Hesse
2f55bfaf00 fw-addr-lists: strip cidr for host addresses
This makes sure the addresses match later when we read them from
address-list for renew.
2025-03-11 14:19:10 +01:00
Christian Hesse
ea6de35699 fw-addr-lists: do not clean up
Cleanup is important on renew (so the script does not attempt to re-add),
but we do not care here.
2025-03-11 14:13:55 +01:00
Christian Hesse
fb343c99e3 fw-addr-lists: put timeout into variable 2025-03-06 22:59:31 +01:00
Christian Hesse
7be26a0712 DEBUG: add info on $LogPrintVerbose 2025-03-06 15:59:44 +01:00
Christian Hesse
6d718ec987 fw-addr-lists: use $LogPrintVerbose ...
... to reduce debug output and speed up execution.
2025-03-06 15:59:44 +01:00
Christian Hesse
e341e1c30c global-functions: introduce $LogPrintVerbose ...
... which is a declared function, but has no code, intentionally. It can be
called as a no-op by default.

If you want this output set the function to be the same as $LogPrint:

    :set LogPrintVerbose $LogPrint;
2025-03-06 15:59:11 +01:00
Christian Hesse
b43b1b3955 Merge branch 'checksums' into next 2025-03-06 10:43:15 +01:00
Christian Hesse
1b46a5fd9b global-functions: $ScriptInstallUpdate: checksum only for same source
So ignore if script is fetched from different base or with different
suffix.
2025-03-06 10:43:13 +01:00
Christian Hesse
b13360e4b8 global-functions: $ScriptInstallUpdate: simplify check
This one should suffice...
2025-03-06 10:42:52 +01:00
Christian Hesse
c9de6d8579 global-functions: $ScriptInstallUpdate: put checksum into variable 2025-03-06 10:42:27 +01:00
Christian Hesse
10374afc18 global-functions: $ScriptInstallUpdate: support checksums for CRLF scripts 2025-03-06 10:42:00 +01:00
Christian Hesse
0c1d96f89d global-functions: $ScriptInstallUpdate: get and compare checksums
The file 'checksums.json' is generated when deploying to my web
server... This should speed up the update a lot as it reduces downloads
to a minimum. 🎉😁
2025-03-06 10:41:28 +01:00
Christian Hesse
3ccaafd1b3 global-functions: $ScriptInstallUpdate: move code into block 2025-03-05 01:15:22 +01:00
Christian Hesse
469f783a92 ipv6-update: check for availability of both variables 2025-03-03 09:12:43 +01:00
Christian Hesse
33c02e0609 ipv6-update: ignore if prefix is no longer valid 2025-03-03 09:10:54 +01:00
Christian Hesse
6331505dbe Merge branch 'quote-file-name' into next
This is required with RouterOS 7.18 now...

Well, probably the change was introduced with one of the
beta versions...
2025-02-27 10:52:52 +01:00
Christian Hesse
0c4fb42616 mod/notification-telegram: $GetTelegramChatId: give thead id...
... if message was sent to group's topic.
2025-02-27 10:52:52 +01:00
Christian Hesse
f5189b8bd7 INITIAL-COMMANDS: quote the certificate file name 2025-02-27 10:52:52 +01:00
Christian Hesse
e2fe653035 mod/notification-telegram: $GetTelegramChatId: use last message 2025-02-27 10:52:52 +01:00
Christian Hesse
b11be59b08 README: quote the certificate file name 2025-02-27 10:52:52 +01:00
Christian Hesse
24de060904 Merge branch 'check-certificates' into next 2025-02-27 10:52:19 +01:00
Christian Hesse
14195c51ca check-certificates: try PKCS#12 before PEM...
... as that is more likely to have a private key.

Is that true? 🤨
2025-02-26 18:25:58 +01:00
Christian Hesse
e833dfcf25 check-certificates: simplify return from function...
... and also break earch on success.
2025-02-26 18:05:32 +01:00
Christian Hesse
512c54bd59 check-certificates: ... and even more 2025-02-26 18:05:32 +01:00
Christian Hesse
3d40b4419d check-certificates: add more debug output 2025-02-26 18:03:45 +01:00
Christian Hesse
a6d4e7e82c check-certificates: drop dot from type...
... and add it in file name.
2025-02-26 18:03:17 +01:00
Christian Hesse
f6c2225f68 check-certificates: catch and ignore import error
Hmm... 🤨 When was that runtime error introduced? I *think* it
worked before.
2025-02-26 13:57:51 +01:00
Christian Hesse
53b13b295a mod/notification-telegram: introduce $GetTelegramChatId 2025-02-25 22:37:30 +01:00
Christian Hesse
4eafcaa3ac telegram-chat: say hello when awaiting commands 2025-02-25 22:16:48 +01:00
Christian Hesse
c33eb41c9c global-functions: $DeviceInfo: add license level, re-order 2025-02-25 17:55:26 +01:00
Christian Hesse
78f9687558 Merge branch 'telegram-topics' into next 2025-02-25 17:55:26 +01:00
109 changed files with 1676 additions and 875 deletions

3
.gitignore vendored
View file

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

View file

@ -13,7 +13,7 @@ Installing from branches
> ⚠️ **Warning**: Living on the edge? Great, read on!
> If not: Please use the `main` branch and leave this page!
These scripts are developed in a [git](https://git-scm.com/) repository.
These scripts are developed in a [git ↗️](https://git-scm.com/) repository.
Development and experimental branches are used to provide early access
for specific changes. You can install scripts from these branches
for testing.

View file

@ -21,7 +21,7 @@ first step of [installation](README.md#the-long-way-in-detail) is importing
the certificate.
The scripts can install additional certificates when required. This happens
from this repository if available, or from [mkcert.org](https://mkcert.org)
from this repository if available, or from [mkcert.org ↗️](https://mkcert.org)
as a fallback.
Get the certificate's CommonName
@ -29,7 +29,7 @@ Get the certificate's CommonName
But how to determine what certificate may be required? Often easiest way
is to use a desktop browser to get that information. This demonstration uses
[Mozilla Firefox](https://www.mozilla.org/firefox/).
[Mozilla Firefox ↗️](https://www.mozilla.org/firefox/).
Let's assume we want to make sure the certificate for
[git.eworm.de](https://git.eworm.de/) is available. Open that page in the
@ -74,6 +74,7 @@ See also
* [Download, import and update firewall address-lists](doc/fw-addr-lists.md)
* [Manage DNS and DoH servers from netwatch](doc/netwatch-dns.md)
* [Send notifications via Gotify](doc/mod/notification-gotify.md)
* [Send notifications via Matrix](doc/mod/notification-matrix.md)
* [Send notifications via Ntfy](doc/mod/notification-ntfy.md)

View file

@ -21,6 +21,8 @@ for details!
* [Ben Harris](mailto:mail@bharr.is) (@bharrisau)
* [Daniel Ziegenberg](mailto:daniel@ziegenberg.at) (@ziegenberg)
* [Ignacio Serrano](mailto:ignic@ignic.com) (@ignic)
* [Ilya Kulakov](mailto:kulakov.ilya@gmail.com) (@Kentzo)
* [Leonardo David Monteiro](mailto:leo@cub3.xyz) (@leosfsm)
* [Michael Gisbers](mailto:michael@gisbers.de) (@mgisbers)
* [Miquel Bonastre](mailto:mbonastre@yahoo.com) (@mbonastre)
* @netravnen
@ -30,9 +32,10 @@ for details!
## Donations
Add yourself to the list,
[donate with PayPal](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=A4ZXBD6YS2W8J)!
[donate with PayPal ↗️](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=A4ZXBD6YS2W8J)!
* Abdul Mannan Abbasi
* Alex Maier
* Andrea Ruffini Perico
* Andrew Cox
* Christoph Boss (@Kampfwurst)

View file

@ -42,7 +42,21 @@ Other actions (`disk`, `email`, `remote` or `support`) can be used as
well. I do not recommend using `echo` - use [debug output](#debug-output)
instead.
Disable or remote that setting to restore regular logging.
Disable or remove that setting to restore regular logging.
## Verbose output
Specific scripts can generate huge amount of output. These do use a function
`$LogPrintVerbose`, which is declared, but has no code, intentionally.
If you *really* want that output set the function to be the same as
`$LogPrint`:
:set LogPrintVerbose $LogPrint;
To revert that change just run:
:set LogPrintVerbose;
---
[⬅️ Go back to main README](README.md)

View file

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

View file

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

157
README.md
View file

@ -10,13 +10,14 @@ RouterOS Scripts
![RouterOS Scripts Logo](logo.svg)
[RouterOS](https://mikrotik.com/software) is the operating system developed
by [MikroTik](https://mikrotik.com/aboutus) for networking tasks. This
repository holds a number of [scripts](https://wiki.mikrotik.com/wiki/Manual:Scripting)
[RouterOS ↗️](https://mikrotik.com/software) is the operating system developed
by [MikroTik ↗️](https://mikrotik.com/aboutus) for networking tasks. This
repository holds a number of [scripts ↗️](https://wiki.mikrotik.com/wiki/Manual:Scripting)
to manage RouterOS devices or extend their functionality.
*Use at your own risk*, pay attention to
[license and warranty](#license-and-warranty)!
[license and warranty](#license-and-warranty), and
[disclaimer on external links](#disclaimer-on-external-links)!
Requirements
------------
@ -35,7 +36,7 @@ Specific scripts may require even newer RouterOS version.
> running RouterOS v6 switch to `routeros-v6` branch!
Starting with RouterOS 7.17 the
[device-mode](https://help.mikrotik.com/docs/spaces/ROS/pages/93749258/Device-mode)
[device-mode ↗️](https://help.mikrotik.com/docs/spaces/ROS/pages/93749258/Device-mode)
has been extended to give more fine-grained control over what features are
available. You need to enable `scheduler` and `fetch` at least, specific
scripts may require additional features.
@ -61,9 +62,9 @@ First time users should take the long way below.
### Live presentation
Want to see it in action? I've had a presentation [Repository based
RouterOS script distribution](https://www.youtube.com/watch?v=B9neG3oAhcY)
RouterOS script distribution ↗️](https://www.youtube.com/watch?v=B9neG3oAhcY)
including demonstation recorded live at [MUM Europe
2019](https://mum.mikrotik.com/2019/EU/) in Vienna.
2019 ↗️](https://mum.mikrotik.com/2019/EU/) in Vienna.
> ⚠️ **Warning**: Some details changed. So see the presentation, then follow
> the steps below for up-to-date commands.
@ -71,7 +72,15 @@ including demonstation recorded live at [MUM Europe
### The long way in detail
The update script does server certificate verification, so first step is to
download the certificates. If you intend to download the scripts from a
download the certificates.
> 💡️ **Hint**: RouterOS 7.19 comes with a builtin certificate store. You
> can skip the steps regarding certificate download and import and jump
> to [installation of scripts](#installation-of-scripts) if you set the
> trust for these builtin trust anchors:
> `/certificate/settings/set builtin-trust-anchors=trusted;`
If you intend to download the scripts from a
different location (for example from github.com) install the corresponding
certificate chain.
@ -83,11 +92,11 @@ Note that the commands above do *not* verify server certificate, so if you
want to be safe download with your workstations's browser and transfer the
file to your MikroTik device.
* [ISRG Root X2](https://letsencrypt.org/certs/isrg-root-x2.pem)
* [ISRG Root X2 ↗️](https://letsencrypt.org/certs/isrg-root-x2.pem)
Then we import the certificate.
/certificate/import file-name=isrg-root-x2.pem passphrase="";
/certificate/import file-name="isrg-root-x2.pem" passphrase="";
Do not worry that the command is not shown - that happens because it contains
a sensitive property, the passphrase.
@ -105,6 +114,8 @@ is shown.
Always make sure there are no certificates installed you do not know or want!
#### Installation of scripts
All following commands will verify the server certificate. For validity the
certificate's lifetime is checked with local time, so make sure the device's
date and time is set correctly!
@ -122,6 +133,9 @@ And finally load configuration and functions and add the scheduler.
![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
The last step is optional: Add this scheduler **only** if you want the
@ -191,7 +205,7 @@ Scheduler and events
--------------------
Most scripts are designed to run regularly from
[scheduler](https://wiki.mikrotik.com/wiki/Manual:System/Scheduler). We just
[scheduler ↗️](https://wiki.mikrotik.com/wiki/Manual:System/Scheduler). We just
added `check-routeros-update`, so let's run it daily to make sure not to
miss an update.
@ -214,60 +228,62 @@ There's much more to explore... Have fun!
Available scripts
-----------------
* [Find and remove access list duplicates](doc/accesslist-duplicates.md)
* [Upload backup to Mikrotik cloud](doc/backup-cloud.md)
* [Send backup via e-mail](doc/backup-email.md)
* [Save configuration to fallback partition](doc/backup-partition.md)
* [Upload backup to server](doc/backup-upload.md)
* [Download packages for CAP upgrade from CAPsMAN](doc/capsman-download-packages.md)
* [Run rolling CAP upgrades from CAPsMAN](doc/capsman-rolling-upgrade.md)
* [Renew locally issued certificates](doc/certificate-renew-issued.md)
* [Renew certificates and notify on expiration](doc/check-certificates.md)
* [Notify about health state](doc/check-health.md)
* [Notify on LTE firmware upgrade](doc/check-lte-firmware-upgrade.md)
* [Notify on RouterOS update](doc/check-routeros-update.md)
* [Collect MAC addresses in wireless access list](doc/collect-wireless-mac.md)
* [Use wireless network with daily psk](doc/daily-psk.md)
* [Comment DHCP leases with info from access list](doc/dhcp-lease-comment.md)
* [Create DNS records for DHCP leases](doc/dhcp-to-dns.md)
* [Automatically upgrade firmware and reboot](doc/firmware-upgrade-reboot.md)
* [Download, import and update firewall address-lists](doc/fw-addr-lists.md)
* [Wait for global functions und modules](doc/global-wait.md)
* [Send GPS position to server](doc/gps-track.md)
* [Use WPA network with hotspot credentials](doc/hotspot-to-wpa.md)
* [Create DNS records for IPSec peers](doc/ipsec-to-dns.md)
* [Update configuration on IPv6 prefix change](doc/ipv6-update.md)
* [Manage IP addresses with bridge status](doc/ip-addr-bridge.md)
* [Run other scripts on DHCP lease](doc/lease-script.md)
* [Manage LEDs dark mode](doc/leds-mode.md)
* [Forward log messages via notification](doc/log-forward.md)
* [Mode button with multiple presses](doc/mode-button.md)
* [Manage DNS and DoH servers from netwatch](doc/netwatch-dns.md)
* [Notify on host up and down](doc/netwatch-notify.md)
* [Visualize OSPF state via LEDs](doc/ospf-to-leds.md)
* [Manage system update](doc/packages-update.md)
* [Run scripts on ppp connection](doc/ppp-on-up.md)
* [Act on received SMS](doc/sms-action.md)
* [Forward received SMS](doc/sms-forward.md)
* [Play Super Mario theme](doc/super-mario-theme.md)
* [Chat with your router and send commands via Telegram bot](doc/telegram-chat.md)
* [Install LTE firmware upgrade](doc/unattended-lte-firmware-upgrade.md)
* [Update GRE configuration with dynamic addresses](doc/update-gre-address.md)
* [Update tunnelbroker configuration](doc/update-tunnelbroker.md)
* [Find and remove access list duplicates](doc/accesslist-duplicates.md) (`accesslist-duplicates`)
* [Upload backup to Mikrotik cloud](doc/backup-cloud.md) (`backup-cloud`)
* [Send backup via e-mail](doc/backup-email.md) (`backup-email`)
* [Save configuration to fallback partition](doc/backup-partition.md) (`backup-partition`)
* [Upload backup to server](doc/backup-upload.md) (`backup-upload`)
* [Download packages for CAP upgrade from CAPsMAN](doc/capsman-download-packages.md) (`capsman-download-packages`)
* [Run rolling CAP upgrades from CAPsMAN](doc/capsman-rolling-upgrade.md) (`capsman-rolling-upgrade`)
* [Renew locally issued certificates](doc/certificate-renew-issued.md) (`certificate-renew-issued`)
* [Renew certificates and notify on expiration](doc/check-certificates.md) (`check-certificates`)
* [Notify about health state](doc/check-health.md) (`check-health`)
* [Notify on LTE firmware upgrade](doc/check-lte-firmware-upgrade.md) (`check-lte-firmware-upgrade`)
* [Check perpetual license on CHR](doc/check-perpetual-license.md) (`check-perpetual-license`)
* [Notify on RouterOS update](doc/check-routeros-update.md) (`check-routeros-update`)
* [Collect MAC addresses in wireless access list](doc/collect-wireless-mac.md) (`collect-wireless-mac`)
* [Use wireless network with daily psk](doc/daily-psk.md) (`daily-psk`)
* [Comment DHCP leases with info from access list](doc/dhcp-lease-comment.md) (`dhcp-lease-comment`)
* [Create DNS records for DHCP leases](doc/dhcp-to-dns.md) (`dhcp-to-dns`)
* [Automatically upgrade firmware and reboot](doc/firmware-upgrade-reboot.md) (`firmware-upgrade-reboot`)
* [Download, import and update firewall address-lists](doc/fw-addr-lists.md) (`fw-addr-lists`)
* [Wait for global functions und modules](doc/global-wait.md) (`global-wait`)
* [Send GPS position to server](doc/gps-track.md) (`gps-track`)
* [Use WPA network with hotspot credentials](doc/hotspot-to-wpa.md) (`hotspot-to-wpa` & `hotspot-to-wpa-cleanup`)
* [Create DNS records for IPSec peers](doc/ipsec-to-dns.md) (`ipsec-to-dns`)
* [Update configuration on IPv6 prefix change](doc/ipv6-update.md) (`ipv6-update`)
* [Manage IP addresses with bridge status](doc/ip-addr-bridge.md) (`ip-addr-bridge`)
* [Run other scripts on DHCP lease](doc/lease-script.md) (`lease-script`)
* [Manage LEDs dark mode](doc/leds-mode.md) (`leds-day-mode`, `leds-night-mode` & `leds-toggle-mode`)
* [Forward log messages via notification](doc/log-forward.md) (`log-forward`)
* [Mode button with multiple presses](doc/mode-button.md) (`mode-button`)
* [Manage DNS and DoH servers from netwatch](doc/netwatch-dns.md) (`netwatch-dns`)
* [Notify on host up and down](doc/netwatch-notify.md) (`netwatch-notify`)
* [Visualize OSPF state via LEDs](doc/ospf-to-leds.md) (`ospf-to-leds`)
* [Manage system update](doc/packages-update.md) (`packages-update`)
* [Run scripts on ppp connection](doc/ppp-on-up.md) (`ppp-on-up`)
* [Act on received SMS](doc/sms-action.md) (`sms-action`)
* [Forward received SMS](doc/sms-forward.md) (`sms-forward`)
* [Play Super Mario theme](doc/super-mario-theme.md) (`super-mario-theme`)
* [Chat with your router and send commands via Telegram bot](doc/telegram-chat.md) (`telegram-chat`)
* [Install LTE firmware upgrade](doc/unattended-lte-firmware-upgrade.md) (`unattended-lte-firmware-upgrade`)
* [Update GRE configuration with dynamic addresses](doc/update-gre-address.md) (`update-gre-address`)
* [Update tunnelbroker configuration](doc/update-tunnelbroker.md) (`update-tunnelbroker`)
Available modules
-----------------
* [Manage ports in bridge](doc/mod/bridge-port-to.md)
* [Manage VLANs on bridge ports](doc/mod/bridge-port-vlan.md)
* [Inspect variables](doc/mod/inspectvar.md)
* [IP address calculation](doc/mod/ipcalc.md)
* [Send notifications via e-mail](doc/mod/notification-email.md)
* [Send notifications via Matrix](doc/mod/notification-matrix.md)
* [Send notifications via Ntfy](doc/mod/notification-ntfy.md)
* [Send notifications via Telegram](doc/mod/notification-telegram.md)
* [Download script and run it once](doc/mod/scriptrunonce.md)
* [Import ssh keys for public key authentication](doc/mod/ssh-keys-import.md)
* [Manage ports in bridge](doc/mod/bridge-port-to.md) (`mod/bridge-port-to`)
* [Manage VLANs on bridge ports](doc/mod/bridge-port-vlan.md) (`mod/bridge-port-vlan`)
* [Inspect variables](doc/mod/inspectvar.md) (`mod/inspectvar`)
* [IP address calculation](doc/mod/ipcalc.md) (`mod/ipcalc`)
* [Send notifications via e-mail](doc/mod/notification-email.md) (`mod/notification-email`)
* [Send notifications via Gotify](doc/mod/notification-gotify.md) (`mod/notification-gotify`)
* [Send notifications via Matrix](doc/mod/notification-matrix.md) (`mod/notification-matrix`)
* [Send notifications via Ntfy](doc/mod/notification-ntfy.md) (`mod/notification-ntfy`)
* [Send notifications via Telegram](doc/mod/notification-telegram.md) (`mod/notification-telegram`)
* [Download script and run it once](doc/mod/scriptrunonce.md) (`mod/scriptrunonce`)
* [Import ssh keys for public key authentication](doc/mod/ssh-keys-import.md) (`mod/ssh-keys-import`)
Installing custom scripts & modules
-----------------------------------
@ -324,7 +340,7 @@ Possibly a scheduler and other configuration has to be removed as well.
Contact
-------
We have a Telegram Group [RouterOS-Scripts](https://t.me/routeros_scripts)!
We have a Telegram Group [RouterOS-Scripts ↗️](https://t.me/routeros_scripts)!
[![RouterOS Scripts Telegram Group](README.d/telegram-group.avif)](https://t.me/routeros_scripts)
@ -348,7 +364,7 @@ at github.
This project is developed in private spare time and usage is free of charge
for you. If you like the scripts and think this is of value for you or your
business please consider to
[donate with PayPal](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=A4ZXBD6YS2W8J).
[donate with PayPal ↗️](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=A4ZXBD6YS2W8J).
[![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)
@ -367,6 +383,21 @@ but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
[GNU General Public License](COPYING.md) for more details.
Disclaimer on external links
----------------------------
Our website contains links to the websites of third parties ("external
links"). As the content of these websites is not under our control, we
cannot assume any liability for such external content. In all cases, the
provider of information of the linked websites is liable for the content
and accuracy of the information provided. At the point in time when the
links were placed, no infringements of the law were recognisable to us.
As soon as an infringement of the law becomes known to us, we will
immediately remove the link in question.
> 💡️ **Hint**: All external links are marked with an arrow pointing
> diagonally in an up-right (or north-east) direction (↗️).
Upstream
--------

View file

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

View file

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

View file

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

View file

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

View file

@ -9,11 +9,11 @@
# upload backup to MikroTik cloud
# https://rsc.eworm.de/doc/backup-cloud.md
:global GlobalFunctionsReady;
:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
:local ExitOK false;
:do {
:onerror Err {
:global GlobalConfigReady; :global GlobalFunctionsReady;
:retry { :if ($GlobalConfigReady != true || $GlobalFunctionsReady != true) \
do={ :error ("Global config and/or functions not ready."); }; } delay=500ms max=50;
:local ScriptName [ :jobname ];
:global BackupRandomDelay;
@ -99,6 +99,6 @@
:set PackagesUpdateBackupFailure true;
}
$RmDir "tmpfs/backup-cloud";
} on-error={
:global ExitError; $ExitError $ExitOK [ :jobname ];
} do={
:global ExitError; $ExitError $ExitOK [ :jobname ] $Err;
}

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -12,11 +12,11 @@
#
# !! Do not edit this file, it is generated from template!
:global GlobalFunctionsReady;
:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
:local ExitOK false;
:do {
:onerror Err {
:global GlobalConfigReady; :global GlobalFunctionsReady;
:retry { :if ($GlobalConfigReady != true || $GlobalFunctionsReady != true) \
do={ :error ("Global config and/or functions not ready."); }; } delay=500ms max=50;
:local ScriptName [ :jobname ];
:global LogPrint;
@ -45,6 +45,6 @@
:delay ($Delay . "s");
}
}
} on-error={
:global ExitError; $ExitError $ExitOK [ :jobname ];
} do={
:global ExitError; $ExitError $ExitOK [ :jobname ] $Err;
}

View file

@ -13,11 +13,11 @@
# !! This is just a template to generate the real script!
# !! Pattern '%TEMPL%' is replaced, paths are filtered.
:global GlobalFunctionsReady;
:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
:local ExitOK false;
:do {
:onerror Err {
:global GlobalConfigReady; :global GlobalFunctionsReady;
:retry { :if ($GlobalConfigReady != true || $GlobalFunctionsReady != true) \
do={ :error ("Global config and/or functions not ready."); }; } delay=500ms max=50;
:local ScriptName [ :jobname ];
:global LogPrint;
@ -53,6 +53,6 @@
:delay ($Delay . "s");
}
}
} on-error={
:global ExitError; $ExitError $ExitOK [ :jobname ];
} do={
:global ExitError; $ExitError $ExitOK [ :jobname ] $Err;
}

View file

@ -12,11 +12,11 @@
#
# !! Do not edit this file, it is generated from template!
:global GlobalFunctionsReady;
:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
:local ExitOK false;
:do {
:onerror Err {
:global GlobalConfigReady; :global GlobalFunctionsReady;
:retry { :if ($GlobalConfigReady != true || $GlobalFunctionsReady != true) \
do={ :error ("Global config and/or functions not ready."); }; } delay=500ms max=50;
:local ScriptName [ :jobname ];
:global LogPrint;
@ -46,6 +46,6 @@
:delay ($Delay . "s");
}
}
} on-error={
:global ExitError; $ExitError $ExitOK [ :jobname ];
} do={
:global ExitError; $ExitError $ExitOK [ :jobname ] $Err;
}

View file

@ -8,11 +8,11 @@
# renew locally issued certificates
# https://rsc.eworm.de/doc/certificate-renew-issued.md
:global GlobalFunctionsReady;
:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
:local ExitOK false;
:do {
:onerror Err {
:global GlobalConfigReady; :global GlobalFunctionsReady;
:retry { :if ($GlobalConfigReady != true || $GlobalFunctionsReady != true) \
do={ :error ("Global config and/or functions not ready."); }; } delay=500ms max=50;
:local ScriptName [ :jobname ];
:global CertIssuedExportPass;
@ -47,6 +47,6 @@
$LogPrint info $ScriptName ("Issued a new certificate for '" . $CertVal->"common-name" . "'.");
}
}
} on-error={
:global ExitError; $ExitError $ExitOK [ :jobname ];
} do={
:global ExitError; $ExitError $ExitOK [ :jobname ] $Err;
}

View file

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

View file

@ -0,0 +1,41 @@
# Issuer: CN=USERTrust RSA Certification Authority O=The USERTRUST Network
# Subject: CN=USERTrust RSA Certification Authority O=The USERTRUST Network
# Label: "USERTrust RSA Certification Authority"
# Serial: 2645093764781058787591871645665788717
# MD5 Fingerprint: 1b:fe:69:d1:91:b7:19:33:a3:72:a8:0f:e1:55:e5:b5
# SHA1 Fingerprint: 2b:8f:1b:57:33:0d:bb:a2:d0:7a:6c:51:f7:0e:e9:0d:da:b9:ad:8e
# SHA256 Fingerprint: e7:93:c9:b0:2f:d8:aa:13:e2:1c:31:22:8a:cc:b0:81:19:64:3b:74:9c:89:89:64:b1:74:6d:46:c3:d4:cb:d2
-----BEGIN CERTIFICATE-----
MIIF3jCCA8agAwIBAgIQAf1tMPyjylGoG7xkDjUDLTANBgkqhkiG9w0BAQwFADCB
iDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCk5ldyBKZXJzZXkxFDASBgNVBAcTC0pl
cnNleSBDaXR5MR4wHAYDVQQKExVUaGUgVVNFUlRSVVNUIE5ldHdvcmsxLjAsBgNV
BAMTJVVTRVJUcnVzdCBSU0EgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMTAw
MjAxMDAwMDAwWhcNMzgwMTE4MjM1OTU5WjCBiDELMAkGA1UEBhMCVVMxEzARBgNV
BAgTCk5ldyBKZXJzZXkxFDASBgNVBAcTC0plcnNleSBDaXR5MR4wHAYDVQQKExVU
aGUgVVNFUlRSVVNUIE5ldHdvcmsxLjAsBgNVBAMTJVVTRVJUcnVzdCBSU0EgQ2Vy
dGlmaWNhdGlvbiBBdXRob3JpdHkwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIK
AoICAQCAEmUXNg7D2wiz0KxXDXbtzSfTTK1Qg2HiqiBNCS1kCdzOiZ/MPans9s/B
3PHTsdZ7NygRK0faOca8Ohm0X6a9fZ2jY0K2dvKpOyuR+OJv0OwWIJAJPuLodMkY
tJHUYmTbf6MG8YgYapAiPLz+E/CHFHv25B+O1ORRxhFnRghRy4YUVD+8M/5+bJz/
Fp0YvVGONaanZshyZ9shZrHUm3gDwFA66Mzw3LyeTP6vBZY1H1dat//O+T23LLb2
VN3I5xI6Ta5MirdcmrS3ID3KfyI0rn47aGYBROcBTkZTmzNg95S+UzeQc0PzMsNT
79uq/nROacdrjGCT3sTHDN/hMq7MkztReJVni+49Vv4M0GkPGw/zJSZrM233bkf6
c0Plfg6lZrEpfDKEY1WJxA3Bk1QwGROs0303p+tdOmw1XNtB1xLaqUkL39iAigmT
Yo61Zs8liM2EuLE/pDkP2QKe6xJMlXzzawWpXhaDzLhn4ugTncxbgtNMs+1b/97l
c6wjOy0AvzVVdAlJ2ElYGn+SNuZRkg7zJn0cTRe8yexDJtC/QV9AqURE9JnnV4ee
UB9XVKg+/XRjL7FQZQnmWEIuQxpMtPAlR1n6BB6T1CZGSlCBst6+eLf8ZxXhyVeE
Hg9j1uliutZfVS7qXMYoCAQlObgOK6nyTJccBz8NUvXt7y+CDwIDAQABo0IwQDAd
BgNVHQ4EFgQUU3m/WqorSs9UgOHYm8Cd8rIDZsswDgYDVR0PAQH/BAQDAgEGMA8G
A1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEMBQADggIBAFzUfA3P9wF9QZllDHPF
Up/L+M+ZBn8b2kMVn54CVVeWFPFSPCeHlCjtHzoBN6J2/FNQwISbxmtOuowhT6KO
VWKR82kV2LyI48SqC/3vqOlLVSoGIG1VeCkZ7l8wXEskEVX/JJpuXior7gtNn3/3
ATiUFJVDBwn7YKnuHKsSjKCaXqeYalltiz8I+8jRRa8YFWSQEg9zKC7F4iRO/Fjs
8PRF/iKz6y+O0tlFYQXBl2+odnKPi4w2r78NBc5xjeambx9spnFixdjQg3IM8WcR
iQycE0xyNN+81XHfqnHd4blsjDwSXWXavVcStkNr/+XeTWYRUc+ZruwXtuhxkYze
Sf7dNXGiFSeUHM9h4ya7b6NnJSFd5t0dCy5oGzuCr+yDZ4XUmFF0sbmZgIn/f3gZ
XHlKYC6SQK5MNyosycdiyA5d9zZbyuAlJQG03RoHnHcAP9Dc1ew91Pq7P8yF1m9/
qS3fuQL39ZeatTXaw2ewh0qpKJ4jjv9cJ2vhsE/zB+4ALtRZh8tSQZXq9EfX7mRB
VXyNWQKV3WKdwrnuWih0hKWbt5DHDAff9Yk2dDLWKMGwsAvgnEzDHNb842m1R0aB
L6KCq9NjRHDEjf8tM7qtj3u1cIiuPhnPQCjY/MiQu12ZIvVS5ljFH4gxQ+6IHdfG
jjxDah2nGN59PRbxYvnKkKj9
-----END CERTIFICATE-----

View file

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

View file

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

View file

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

View file

@ -0,0 +1,78 @@
#!rsc by RouterOS
# RouterOS script: check-perpetual-license
# Copyright (c) 2025 Christian Hesse <mail@eworm.de>
# https://rsc.eworm.de/COPYING.md
#
# requires RouterOS, version=7.15
#
# check perpetual license on CHR
# https://rsc.eworm.de/doc/check-perpetual-license.md
:local ExitOK false;
:onerror Err {
:global GlobalConfigReady; :global GlobalFunctionsReady;
:retry { :if ($GlobalConfigReady != true || $GlobalFunctionsReady != true) \
do={ :error ("Global config and/or functions not ready."); }; } delay=500ms max=50;
:local ScriptName [ :jobname ];
:global Identity;
:global SentCertificateNotification;
:global LogPrint;
:global ScriptLock;
:global SendNotification2;
:global SymbolForNotification;
:global WaitFullyConnected;
:if ([ $ScriptLock $ScriptName ] = false) do={
:set ExitOK true;
:error false;
}
$WaitFullyConnected;
:local License [ /system/license/get ];
:if ([ :typeof ($License->"deadline-at") ] != "str") do={
$LogPrint info $ScriptName ("This device does not have a perpetual license.");
:set ExitOK true;
:error true;
}
:if ([ :len ($License->"next-renewal-at") ] = 0 && ($License->"limited-upgrades") = true) do={
$LogPrint warning $ScriptName ("Your license expired on " . ($License->"deadline-at") . "!");
:if ($SentCertificateNotification != "expired") do={
$SendNotification2 ({ origin=$ScriptName; \
subject=([ $SymbolForNotification "warning-sign" ] . "License expired!"); \
message=("Your license expired on " . ($License->"deadline-at") . \
", can no longer update RouterOS on " . $Identity . "...") });
:set SentCertificateNotification "expired";
}
:set ExitOK true;
:error true;
}
:if ([ :totime ($License->"deadline-at") ] - 3w < [ :timestamp ]) do={
$LogPrint warning $ScriptName ("Your license will expire on " . ($License->"deadline-at") . "!");
:if ($SentCertificateNotification != "warning") do={
$SendNotification2 ({ origin=$ScriptName; \
subject=([ $SymbolForNotification "warning-sign" ] . "License about to expire!"); \
message=("Your license failed to renew and is about to expire on " . \
($License->"deadline-at") . " on " . $Identity . "...") });
:set SentCertificateNotification "warning";
}
:set ExitOK true;
:error true;
}
:if ([ :typeof $SentCertificateNotification ] = "str" && \
[ :totime ($License->"deadline-at") ] - 4w > [ :timestamp ]) do={
$LogPrint info $ScriptName ("Your license was successfully renewed.");
$SendNotification2 ({ origin=$ScriptName; \
subject=([ $SymbolForNotification "white-heavy-check-mark" ] . "License renewed"); \
message=("Your license was successfully renewed on " . $Identity . \
". It is now valid until " . ($License->"deadline-at") . ".") });
:set SentCertificateNotification;
}
} do={
:global ExitError; $ExitError $ExitOK [ :jobname ] $Err;
}

View file

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

View file

@ -11,11 +11,11 @@
#
# !! Do not edit this file, it is generated from template!
:global GlobalFunctionsReady;
:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
:local ExitOK false;
:do {
:onerror Err {
:global GlobalConfigReady; :global GlobalFunctionsReady;
:retry { :if ($GlobalConfigReady != true || $GlobalFunctionsReady != true) \
do={ :error ("Global config and/or functions not ready."); }; } delay=500ms max=50;
:local ScriptName [ :jobname ];
:global Identity;
@ -95,6 +95,6 @@
$LogPrint debug $ScriptName ("No mac address available... Ignoring.");
}
}
} on-error={
:global ExitError; $ExitError $ExitOK [ :jobname ];
} do={
:global ExitError; $ExitError $ExitOK [ :jobname ] $Err;
}

View file

@ -11,11 +11,11 @@
#
# !! Do not edit this file, it is generated from template!
:global GlobalFunctionsReady;
:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
:local ExitOK false;
:do {
:onerror Err {
:global GlobalConfigReady; :global GlobalFunctionsReady;
:retry { :if ($GlobalConfigReady != true || $GlobalFunctionsReady != true) \
do={ :error ("Global config and/or functions not ready."); }; } delay=500ms max=50;
:local ScriptName [ :jobname ];
:global Identity;
@ -96,6 +96,6 @@
$LogPrint debug $ScriptName ("No mac address available... Ignoring.");
}
}
} on-error={
:global ExitError; $ExitError $ExitOK [ :jobname ];
} do={
:global ExitError; $ExitError $ExitOK [ :jobname ] $Err;
}

View file

@ -12,11 +12,11 @@
# !! This is just a template to generate the real script!
# !! Pattern '%TEMPL%' is replaced, paths are filtered.
:global GlobalFunctionsReady;
:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
:local ExitOK false;
:do {
:onerror Err {
:global GlobalConfigReady; :global GlobalFunctionsReady;
:retry { :if ($GlobalConfigReady != true || $GlobalFunctionsReady != true) \
do={ :error ("Global config and/or functions not ready."); }; } delay=500ms max=50;
:local ScriptName [ :jobname ];
:global Identity;
@ -113,6 +113,6 @@
$LogPrint debug $ScriptName ("No mac address available... Ignoring.");
}
}
} on-error={
:global ExitError; $ExitError $ExitOK [ :jobname ];
} do={
:global ExitError; $ExitError $ExitOK [ :jobname ] $Err;
}

View file

@ -11,11 +11,11 @@
#
# !! Do not edit this file, it is generated from template!
:global GlobalFunctionsReady;
:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
:local ExitOK false;
:do {
:onerror Err {
:global GlobalConfigReady; :global GlobalFunctionsReady;
:retry { :if ($GlobalConfigReady != true || $GlobalFunctionsReady != true) \
do={ :error ("Global config and/or functions not ready."); }; } delay=500ms max=50;
:local ScriptName [ :jobname ];
:global Identity;
@ -95,6 +95,6 @@
$LogPrint debug $ScriptName ("No mac address available... Ignoring.");
}
}
} on-error={
:global ExitError; $ExitError $ExitOK [ :jobname ];
} do={
:global ExitError; $ExitError $ExitOK [ :jobname ] $Err;
}

9
contrib/checksums.sh Executable file
View file

@ -0,0 +1,9 @@
#!/bin/sh
# generate a checksums file as used by $ScriptInstallUpdate
set -e
md5sum $(find -name '*.rsc' | sort) | \
sed -e "s| \./||" -e 's|.rsc$||' | \
jq --raw-input --null-input '[ inputs | split (" ") | { (.[1]): (.[0]) }] | add' > 'checksums.json'

View file

@ -11,11 +11,11 @@
#
# !! Do not edit this file, it is generated from template!
:global GlobalFunctionsReady;
:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
:local ExitOK false;
:do {
:onerror Err {
:global GlobalConfigReady; :global GlobalFunctionsReady;
:retry { :if ($GlobalConfigReady != true || $GlobalFunctionsReady != true) \
do={ :error ("Global config and/or functions not ready."); }; } delay=500ms max=50;
:local ScriptName [ :jobname ];
:global DailyPskMatchComment;
@ -91,6 +91,6 @@
}
}
}
} on-error={
:global ExitError; $ExitError $ExitOK [ :jobname ];
} do={
:global ExitError; $ExitError $ExitOK [ :jobname ] $Err;
}

View file

@ -11,11 +11,11 @@
#
# !! Do not edit this file, it is generated from template!
:global GlobalFunctionsReady;
:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
:local ExitOK false;
:do {
:onerror Err {
:global GlobalConfigReady; :global GlobalFunctionsReady;
:retry { :if ($GlobalConfigReady != true || $GlobalFunctionsReady != true) \
do={ :error ("Global config and/or functions not ready."); }; } delay=500ms max=50;
:local ScriptName [ :jobname ];
:global DailyPskMatchComment;
@ -90,6 +90,6 @@
}
}
}
} on-error={
:global ExitError; $ExitError $ExitOK [ :jobname ];
} do={
:global ExitError; $ExitError $ExitOK [ :jobname ] $Err;
}

View file

@ -12,11 +12,11 @@
# !! This is just a template to generate the real script!
# !! Pattern '%TEMPL%' is replaced, paths are filtered.
:global GlobalFunctionsReady;
:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
:local ExitOK false;
:do {
:onerror Err {
:global GlobalConfigReady; :global GlobalFunctionsReady;
:retry { :if ($GlobalConfigReady != true || $GlobalFunctionsReady != true) \
do={ :error ("Global config and/or functions not ready."); }; } delay=500ms max=50;
:local ScriptName [ :jobname ];
:global DailyPskMatchComment;
@ -106,6 +106,6 @@
}
}
}
} on-error={
:global ExitError; $ExitError $ExitOK [ :jobname ];
} do={
:global ExitError; $ExitError $ExitOK [ :jobname ] $Err;
}

View file

@ -11,11 +11,11 @@
#
# !! Do not edit this file, it is generated from template!
:global GlobalFunctionsReady;
:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
:local ExitOK false;
:do {
:onerror Err {
:global GlobalConfigReady; :global GlobalFunctionsReady;
:retry { :if ($GlobalConfigReady != true || $GlobalFunctionsReady != true) \
do={ :error ("Global config and/or functions not ready."); }; } delay=500ms max=50;
:local ScriptName [ :jobname ];
:global DailyPskMatchComment;
@ -91,6 +91,6 @@
}
}
}
} on-error={
:global ExitError; $ExitError $ExitOK [ :jobname ];
} do={
:global ExitError; $ExitError $ExitOK [ :jobname ] $Err;
}

View file

@ -11,11 +11,11 @@
#
# !! Do not edit this file, it is generated from template!
:global GlobalFunctionsReady;
:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
:local ExitOK false;
:do {
:onerror Err {
:global GlobalConfigReady; :global GlobalFunctionsReady;
:retry { :if ($GlobalConfigReady != true || $GlobalFunctionsReady != true) \
do={ :error ("Global config and/or functions not ready."); }; } delay=500ms max=50;
:local ScriptName [ :jobname ];
:global LogPrint;
@ -38,6 +38,6 @@
/ip/dhcp-server/lease/set comment=$NewComment $Lease;
}
}
} on-error={
:global ExitError; $ExitError $ExitOK [ :jobname ];
} do={
:global ExitError; $ExitError $ExitOK [ :jobname ] $Err;
}

View file

@ -11,11 +11,11 @@
#
# !! Do not edit this file, it is generated from template!
:global GlobalFunctionsReady;
:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
:local ExitOK false;
:do {
:onerror Err {
:global GlobalConfigReady; :global GlobalFunctionsReady;
:retry { :if ($GlobalConfigReady != true || $GlobalFunctionsReady != true) \
do={ :error ("Global config and/or functions not ready."); }; } delay=500ms max=50;
:local ScriptName [ :jobname ];
:global LogPrint;
@ -38,6 +38,6 @@
/ip/dhcp-server/lease/set comment=$NewComment $Lease;
}
}
} on-error={
:global ExitError; $ExitError $ExitOK [ :jobname ];
} do={
:global ExitError; $ExitError $ExitOK [ :jobname ] $Err;
}

View file

@ -12,11 +12,11 @@
# !! This is just a template to generate the real script!
# !! Pattern '%TEMPL%' is replaced, paths are filtered.
:global GlobalFunctionsReady;
:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
:local ExitOK false;
:do {
:onerror Err {
:global GlobalConfigReady; :global GlobalFunctionsReady;
:retry { :if ($GlobalConfigReady != true || $GlobalFunctionsReady != true) \
do={ :error ("Global config and/or functions not ready."); }; } delay=500ms max=50;
:local ScriptName [ :jobname ];
:global LogPrint;
@ -43,6 +43,6 @@
/ip/dhcp-server/lease/set comment=$NewComment $Lease;
}
}
} on-error={
:global ExitError; $ExitError $ExitOK [ :jobname ];
} do={
:global ExitError; $ExitError $ExitOK [ :jobname ] $Err;
}

View file

@ -11,11 +11,11 @@
#
# !! Do not edit this file, it is generated from template!
:global GlobalFunctionsReady;
:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
:local ExitOK false;
:do {
:onerror Err {
:global GlobalConfigReady; :global GlobalFunctionsReady;
:retry { :if ($GlobalConfigReady != true || $GlobalFunctionsReady != true) \
do={ :error ("Global config and/or functions not ready."); }; } delay=500ms max=50;
:local ScriptName [ :jobname ];
:global LogPrint;
@ -38,6 +38,6 @@
/ip/dhcp-server/lease/set comment=$NewComment $Lease;
}
}
} on-error={
:global ExitError; $ExitError $ExitOK [ :jobname ];
} do={
:global ExitError; $ExitError $ExitOK [ :jobname ] $Err;
}

View file

@ -9,11 +9,11 @@
# check DHCP leases and add/remove/update DNS entries
# https://rsc.eworm.de/doc/dhcp-to-dns.md
:global GlobalFunctionsReady;
:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
:local ExitOK false;
:do {
:onerror Err {
:global GlobalConfigReady; :global GlobalFunctionsReady;
:retry { :if ($GlobalConfigReady != true || $GlobalFunctionsReady != true) \
do={ :error ("Global config and/or functions not ready."); }; } delay=500ms max=50;
:local ScriptName [ :jobname ];
:global Domain;
@ -125,6 +125,6 @@
$LogPrint debug $ScriptName ("No address available... Ignoring.");
}
}
} on-error={
:global ExitError; $ExitError $ExitOK [ :jobname ];
} do={
:global ExitError; $ExitError $ExitOK [ :jobname ] $Err;
}

View file

@ -17,7 +17,7 @@ Description
-----------
This script uploads
[binary backup to Mikrotik cloud](https://wiki.mikrotik.com/wiki/Manual:IP/Cloud#Backup).
[binary backup to Mikrotik cloud ↗️](https://wiki.mikrotik.com/wiki/Manual:IP/Cloud#Backup).
> ⚠️ **Warning**: The used command can hit errors that a script can with
> workaround only. A notification *should* be sent anyway. But it can result
@ -49,6 +49,7 @@ The configuration goes to `global-config-overlay`, these are the parameters:
Also notification settings are required for
[e-mail](mod/notification-email.md),
[gotify](mod/notification-gotify.md),
[matrix](mod/notification-matrix.md),
[ntfy](mod/notification-ntfy.md) and/or
[telegram](mod/notification-telegram.md).

View file

@ -17,7 +17,7 @@ Description
-----------
This script saves the current configuration to fallback
[partition](https://wiki.mikrotik.com/wiki/Manual:Partitions).
[partition ↗️](https://wiki.mikrotik.com/wiki/Manual:Partitions).
It can also copy-over the RouterOS installation when run interactively
or just before a feature update.

View file

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

View file

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

View file

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

View file

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

View file

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

View file

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

View file

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

View file

@ -19,10 +19,10 @@ Description
This script downloads, imports and updates firewall address-lists. Its main
purpose is to block attacking ip addresses, spam hosts, command-and-control
servers and similar malicious entities. The default configuration contains a
[collective list by GitHub user @stamparm](https://github.com/stamparm/ipsum),
lists from [dshield.org](https://dshield.org/) and
[blocklist.de](https://www.blocklist.de/), and lists from
[spamhaus.org](https://spamhaus.org/) are prepared.
[collective list by GitHub user @stamparm ↗️](https://github.com/stamparm/ipsum),
lists from [dshield.org ↗️](https://dshield.org/) and
[blocklist.de ↗️](https://www.blocklist.de/), and lists from
[spamhaus.org ↗️](https://spamhaus.org/) are prepared.
The address-lists are updated in place, so after initial import you will not
see situation when the lists are not populated.
@ -32,7 +32,10 @@ certificate is checked.
> ⚠️ **Warning**: The script does not limit the size of a list, but keep in
> mind that huge lists can exhaust your device's resources (RAM and CPU),
> and may take a long time to process.
> and may take a long time to process.
> Even crashes for the complete scripting (and CLI) subsystem are possible.
> This should be logged accordingly with warnings when global functions are
> reloaded from scheduler.
Requirements and installation
-----------------------------

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

View file

@ -32,7 +32,7 @@ Configuration
-------------
Set up your device's
[e-mail settings](https://wiki.mikrotik.com/wiki/Manual:Tools/email).
[e-mail settings ↗️](https://wiki.mikrotik.com/wiki/Manual:Tools/email).
Also make sure the device has correct time configured, best is to set up
the ntp client.
@ -79,6 +79,7 @@ function available:
See also
--------
* [Send notifications via Gotify](notification-gotify.md)
* [Send notifications via Matrix](notification-matrix.md)
* [Send notifications via Ntfy](notification-ntfy.md)
* [Send notifications via Telegram](notification-telegram.md)

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

View file

@ -0,0 +1,97 @@
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
[Matrix](https://matrix.org/) via client server api. A queue is used to
[Matrix ↗️](https://matrix.org/) via client server api. A queue is used to
make sure notifications are not lost on failure but sent later.
Requirements and installation
@ -131,6 +131,7 @@ See also
* [Certificate name from browser](../../CERTIFICATES.md)
* [Send notifications via e-mail](notification-email.md)
* [Send notifications via Gotify](notification-gotify.md)
* [Send notifications via Ntfy](notification-ntfy.md)
* [Send notifications via Telegram](notification-telegram.md)

View file

@ -17,7 +17,7 @@ Description
-----------
This module adds support for sending notifications via
[Ntfy](https://ntfy.sh/). A queue is used to make sure
[Ntfy ↗️](https://ntfy.sh/). A queue is used to make sure
notifications are not lost on failure but sent later.
Requirements and installation
@ -28,7 +28,7 @@ Just install the module:
$ScriptInstallUpdate mod/notification-ntfy;
Also install the Ntfy app on your mobile device or use the
[web app](https://ntfy.sh/app) in a browser of your choice.
[web app ↗️](https://ntfy.sh/app) in a browser of your choice.
Configuration
-------------
@ -90,6 +90,7 @@ See also
* [Certificate name from browser](../../CERTIFICATES.md)
* [Send notifications via e-mail](notification-email.md)
* [Send notifications via Gotify](notification-gotify.md)
* [Send notifications via Matrix](notification-matrix.md)
* [Send notifications via Telegram](notification-telegram.md)

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

View file

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

View file

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

View file

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

View file

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

View file

@ -8,11 +8,11 @@
# install firmware upgrade, and reboot
# https://rsc.eworm.de/doc/firmware-upgrade-reboot.md
:global GlobalFunctionsReady;
:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
:local ExitOK false;
:do {
:onerror Err {
:global GlobalConfigReady; :global GlobalFunctionsReady;
:retry { :if ($GlobalConfigReady != true || $GlobalFunctionsReady != true) \
do={ :error ("Global config and/or functions not ready."); }; } delay=500ms max=50;
:local ScriptName [ :jobname ];
:global LogPrint;
@ -55,6 +55,6 @@
$LogPrint info $ScriptName ("Firmware upgrade successful, rebooting.");
/system/reboot;
} on-error={
:global ExitError; $ExitError $ExitOK [ :jobname ];
} do={
:global ExitError; $ExitError $ExitOK [ :jobname ] $Err;
}

View file

@ -8,11 +8,11 @@
# download, import and update firewall address-lists
# https://rsc.eworm.de/doc/fw-addr-lists.md
:global GlobalFunctionsReady;
:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
:local ExitOK false;
:do {
:onerror Err {
:global GlobalConfigReady; :global GlobalFunctionsReady;
:retry { :if ($GlobalConfigReady != true || $GlobalFunctionsReady != true) \
do={ :error ("Global config and/or functions not ready."); }; } delay=500ms max=50;
:local ScriptName [ :jobname ];
:global FwAddrLists;
@ -24,6 +24,7 @@
:global HumanReadableNum;
:global LogPrint;
:global LogPrintOnce;
:global LogPrintVerbose;
:global ScriptLock;
:global WaitFullyConnected;
@ -36,12 +37,23 @@
}
}
:local GetBranch do={
:global EitherOr;
:return [ :pick [ :convert transform=md5 to=hex [ :pick $1 0 [ $EitherOr [ :find $1 "/" ] [ :len $1 ] ] ] ] 0 2 ];
}
:if ([ $ScriptLock $ScriptName ] = false) do={
:set ExitOK true;
:error false;
}
$WaitFullyConnected;
:if ([ :len [ /log/find where topics=({"script"; "warning"}) \
message=("\$LogPrintOnce: The message is already in log, scripting subsystem may have crashed before!") ] ] > 0) do={
$LogPrintOnce warning $ScriptName ("Scripting subsystem may have crashed, possibly caused by us. Delaying!");
:delay 5m;
}
:local ListComment ("managed by " . $ScriptName);
:foreach FwListName,FwList in=$FwAddrLists do={
@ -99,17 +111,24 @@
:set Address ([ :pick $Line 0 [ $FindDelim $Line ] ] . ($List->"cidr"));
}
:do {
:local Branch [ $GetBranch $Address ];
:if ($Address ~ "^[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}(/[0-9]{1,2})?\$") do={
:set ($IPv4Addresses->$Address) $TimeOut;
:if ($Address ~ "/32\$") do={
:set Address [ :pick $Address 0 ([ :len $Address ] - 3) ];
}
:set ($IPv4Addresses->$Branch->$Address) $TimeOut;
:error true;
}
:if ($Address ~ "^[0-9a-zA-Z]*:[0-9a-zA-Z:\\.]+(/[0-9]{1,3})?\$") do={
:set ($IPv6Addresses->$Address) $TimeOut;
:if ([ :typeof [ :find $Address "/" ] ] = "nil") do={
:set Address ($Address . "/128");
}
:set ($IPv6Addresses->$Branch->$Address) $TimeOut;
:error true;
}
:if ($Address ~ "^[\\.a-zA-Z0-9-]+\\.[a-zA-Z]{2,}\$") do={
:set ($IPv4Addresses->$Address) $TimeOut;
:set ($IPv6Addresses->$Address) $TimeOut;
:set ($IPv4Addresses->$Branch->$Address) $TimeOut;
:set ($IPv6Addresses->$Branch->$Address) $TimeOut;
:error true;
}
} on-error={ }
@ -119,16 +138,18 @@
:foreach Entry in=[ /ip/firewall/address-list/find where \
list=$FwListName comment=$ListComment ] do={
:local Address [ /ip/firewall/address-list/get $Entry address ];
:if ([ :typeof ($IPv4Addresses->$Address) ] = "time") do={
$LogPrint debug $ScriptName ("Renewing IPv4 address in list '" . $FwListName . \
"' with " . ($IPv4Addresses->$Address) . ": " . $Address);
/ip/firewall/address-list/set $Entry timeout=($IPv4Addresses->$Address);
:set ($IPv4Addresses->$Address);
:local Branch [ $GetBranch $Address ];
:local TimeOut ($IPv4Addresses->$Branch->$Address);
:if ([ :typeof $TimeOut ] = "time") do={
$LogPrintVerbose debug $ScriptName ("Renewing IPv4 address " . $Address . \
" in list '" . $FwListName . "' with " . $TimeOut . ".");
/ip/firewall/address-list/set $Entry timeout=$TimeOut;
:set ($IPv4Addresses->$Branch->$Address);
:set CntRenew ($CntRenew + 1);
} else={
:if ($Failure = false) do={
$LogPrint debug $ScriptName ("Removing IPv4 address from list '" . $FwListName . \
"': " . $Address);
$LogPrintVerbose debug $ScriptName ("Removing IPv4 address " . $Address . \
" from list '" . $FwListName . ".");
/ip/firewall/address-list/remove $Entry;
:set CntRemove ($CntRemove + 1);
}
@ -138,47 +159,53 @@
:foreach Entry in=[ /ipv6/firewall/address-list/find where \
list=$FwListName comment=$ListComment ] do={
:local Address [ /ipv6/firewall/address-list/get $Entry address ];
:if ([ :typeof ($IPv6Addresses->$Address) ] = "time") do={
$LogPrint debug $ScriptName ("Renewing IPv6 address in list '" . $FwListName . \
"' with " . ($IPv6Addresses->$Address) . ": " . $Address);
/ipv6/firewall/address-list/set $Entry timeout=($IPv6Addresses->$Address);
:set ($IPv6Addresses->$Address);
:local Branch [ $GetBranch $Address ];
:local TimeOut ($IPv6Addresses->$Branch->$Address);
:if ([ :typeof $TimeOut ] = "time") do={
$LogPrintVerbose debug $ScriptName ("Renewing IPv6 address " . $Address . \
" in list '" . $FwListName . "' with " . $TimeOut . ".");
/ipv6/firewall/address-list/set $Entry timeout=$TimeOut;
:set ($IPv6Addresses->$Branch->$Address);
:set CntRenew ($CntRenew + 1);
} else={
:if ($Failure = false) do={
$LogPrint debug $ScriptName ("Removing IPv6 address from list '" . $FwListName . \
"': " . $Address);
$LogPrintVerbose debug $ScriptName ("Removing IPv6 address " . $Address . \
" from list '" . $FwListName .".");
/ipv6/firewall/address-list/remove $Entry;
:set CntRemove ($CntRemove + 1);
}
}
}
:foreach Address,Timeout in=$IPv4Addresses do={
$LogPrint debug $ScriptName ("Adding IPv4 address to list '" . $FwListName . \
"' with " . $Timeout . ": " . $Address);
:do {
/ip/firewall/address-list/add list=$FwListName comment=$ListComment \
address=$Address timeout=$Timeout;
:set ($IPv4Addresses->$Address);
:set CntAdd ($CntAdd + 1);
} on-error={
$LogPrint warning $ScriptName ("Failed to add IPv4 address to list '" . $FwListName . \
"': " . $Address);
:foreach BranchName,Branch in=$IPv4Addresses do={
$LogPrintVerbose debug $ScriptName ("Handling branch: " . $BranchName);
:foreach Address,Timeout in=$Branch do={
$LogPrintVerbose debug $ScriptName ("Adding IPv4 address " . $Address . \
" to list '" . $FwListName . "' with " . $Timeout . ".");
:onerror Err {
/ip/firewall/address-list/add list=$FwListName comment=$ListComment \
address=$Address timeout=$Timeout;
:set CntAdd ($CntAdd + 1);
} do={
$LogPrint warning $ScriptName ("Failed to add IPv4 address " . $Address . \
" to list '" . $FwListName . "': " . $Err);
}
}
}
:foreach Address,Timeout in=$IPv6Addresses do={
$LogPrint debug $ScriptName ("Adding IPv6 address to list '" . $FwListName . \
"' with " . $Timeout . ": " . $Address);
:do {
/ipv6/firewall/address-list/add list=$FwListName comment=$ListComment \
address=$Address timeout=$Timeout;
:set ($IPv6Addresses->$Address);
:set CntAdd ($CntAdd + 1);
} on-error={
$LogPrint warning $ScriptName ("Failed to add IPv6 address to list '" . $FwListName . \
"': " . $Address);
:foreach BranchName,Branch in=$IPv6Addresses do={
$LogPrintVerbose debug $ScriptName ("Handling branch: " . $BranchName);
:foreach Address,Timeout in=$Branch do={
$LogPrintVerbose debug $ScriptName ("Adding IPv6 address " . $Address . \
" to list '" . $FwListName . "' with " . $Timeout . ".");
:onerror Err {
/ipv6/firewall/address-list/add list=$FwListName comment=$ListComment \
address=$Address timeout=$Timeout;
:set CntAdd ($CntAdd + 1);
} do={
$LogPrint warning $ScriptName ("Failed to add IPv6 address " . $Address . \
" to list '" . $FwListName . "': " . $Err);
}
}
}
@ -188,6 +215,6 @@
" - renewed: " . [ $HumanReadableNum $CntRenew 1000 ] . \
" - removed: " . [ $HumanReadableNum $CntRemove 1000 ]);
}
} on-error={
:global ExitError; $ExitError $ExitOK [ :jobname ];
} do={
:global ExitError; $ExitError $ExitOK [ :jobname ] $Err;
}

View file

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

View file

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

View file

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

View file

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

View file

@ -12,11 +12,11 @@
#
# !! Do not edit this file, it is generated from template!
:global GlobalFunctionsReady;
:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
:local ExitOK false;
:do {
:onerror Err {
:global GlobalConfigReady; :global GlobalFunctionsReady;
:retry { :if ($GlobalConfigReady != true || $GlobalFunctionsReady != true) \
do={ :error ("Global config and/or functions not ready."); }; } delay=500ms max=50;
:local ScriptName [ :jobname ];
:global EitherOr;
@ -75,6 +75,6 @@
/ip/dhcp-server/lease/remove $Lease;
}
}
} on-error={
:global ExitError; $ExitError $ExitOK [ :jobname ];
} do={
:global ExitError; $ExitError $ExitOK [ :jobname ] $Err;
}

View file

@ -13,11 +13,11 @@
# !! This is just a template to generate the real script!
# !! Pattern '%TEMPL%' is replaced, paths are filtered.
:global GlobalFunctionsReady;
:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
:local ExitOK false;
:do {
:onerror Err {
:global GlobalConfigReady; :global GlobalFunctionsReady;
:retry { :if ($GlobalConfigReady != true || $GlobalFunctionsReady != true) \
do={ :error ("Global config and/or functions not ready."); }; } delay=500ms max=50;
:local ScriptName [ :jobname ];
:global EitherOr;
@ -82,6 +82,6 @@
/ip/dhcp-server/lease/remove $Lease;
}
}
} on-error={
:global ExitError; $ExitError $ExitOK [ :jobname ];
} do={
:global ExitError; $ExitError $ExitOK [ :jobname ] $Err;
}

View file

@ -12,11 +12,11 @@
#
# !! Do not edit this file, it is generated from template!
:global GlobalFunctionsReady;
:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
:local ExitOK false;
:do {
:onerror Err {
:global GlobalConfigReady; :global GlobalFunctionsReady;
:retry { :if ($GlobalConfigReady != true || $GlobalFunctionsReady != true) \
do={ :error ("Global config and/or functions not ready."); }; } delay=500ms max=50;
:local ScriptName [ :jobname ];
:global EitherOr;
@ -75,6 +75,6 @@
/ip/dhcp-server/lease/remove $Lease;
}
}
} on-error={
:global ExitError; $ExitError $ExitOK [ :jobname ];
} do={
:global ExitError; $ExitError $ExitOK [ :jobname ] $Err;
}

View file

@ -11,11 +11,11 @@
#
# !! Do not edit this file, it is generated from template!
:global GlobalFunctionsReady;
:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
:local ExitOK false;
:do {
:onerror Err {
:global GlobalConfigReady; :global GlobalFunctionsReady;
:retry { :if ($GlobalConfigReady != true || $GlobalFunctionsReady != true) \
do={ :error ("Global config and/or functions not ready."); }; } delay=500ms max=50;
:local ScriptName [ :jobname ];
:global EitherOr;
@ -100,6 +100,6 @@
:delay 2s;
/caps-man/access-list/set $Entry action=accept;
} on-error={
:global ExitError; $ExitError $ExitOK [ :jobname ];
} do={
:global ExitError; $ExitError $ExitOK [ :jobname ] $Err;
}

View file

@ -12,11 +12,11 @@
# !! This is just a template to generate the real script!
# !! Pattern '%TEMPL%' is replaced, paths are filtered.
:global GlobalFunctionsReady;
:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
:local ExitOK false;
:do {
:onerror Err {
:global GlobalConfigReady; :global GlobalFunctionsReady;
:retry { :if ($GlobalConfigReady != true || $GlobalFunctionsReady != true) \
do={ :error ("Global config and/or functions not ready."); }; } delay=500ms max=50;
:local ScriptName [ :jobname ];
:global EitherOr;
@ -120,6 +120,6 @@
:delay 2s;
/caps-man/access-list/set $Entry action=accept;
/interface/wifi/access-list/set $Entry action=accept;
} on-error={
:global ExitError; $ExitError $ExitOK [ :jobname ];
} do={
:global ExitError; $ExitError $ExitOK [ :jobname ] $Err;
}

View file

@ -11,11 +11,11 @@
#
# !! Do not edit this file, it is generated from template!
:global GlobalFunctionsReady;
:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
:local ExitOK false;
:do {
:onerror Err {
:global GlobalConfigReady; :global GlobalFunctionsReady;
:retry { :if ($GlobalConfigReady != true || $GlobalFunctionsReady != true) \
do={ :error ("Global config and/or functions not ready."); }; } delay=500ms max=50;
:local ScriptName [ :jobname ];
:global EitherOr;
@ -97,6 +97,6 @@
:delay 2s;
/interface/wifi/access-list/set $Entry action=accept;
} on-error={
:global ExitError; $ExitError $ExitOK [ :jobname ];
} do={
:global ExitError; $ExitError $ExitOK [ :jobname ] $Err;
}

View file

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

View file

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

View file

@ -8,11 +8,11 @@
# run scripts on DHCP lease
# https://rsc.eworm.de/doc/lease-script.md
:global GlobalFunctionsReady;
:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
:local ExitOK false;
:do {
:onerror Err {
:global GlobalConfigReady; :global GlobalFunctionsReady;
:retry { :if ($GlobalConfigReady != true || $GlobalFunctionsReady != true) \
do={ :error ("Global config and/or functions not ready."); }; } delay=500ms max=50;
:local ScriptName [ :jobname ];
:global Grep;
@ -53,13 +53,13 @@
}
:foreach Order,Script in=$RunOrder do={
:do {
:onerror Err {
$LogPrint debug $ScriptName ("Running script with order " . $Order . ": " . $Script);
/system/script/run $Script;
} on-error={
$LogPrint warning $ScriptName ("Running script '" . $Script . "' failed!");
} do={
$LogPrint warning $ScriptName ("Running script '" . $Script . "' failed: " . $Err);
}
}
} on-error={
:global ExitError; $ExitError $ExitOK [ :jobname ];
} do={
:global ExitError; $ExitError $ExitOK [ :jobname ] $Err;
}

View file

@ -8,11 +8,11 @@
# forward log messages via notification
# https://rsc.eworm.de/doc/log-forward.md
:global GlobalFunctionsReady;
:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
:local ExitOK false;
:do {
:onerror Err {
:global GlobalConfigReady; :global GlobalFunctionsReady;
:retry { :if ($GlobalConfigReady != true || $GlobalFunctionsReady != true) \
do={ :error ("Global config and/or functions not ready."); }; } delay=500ms max=50;
:local ScriptName [ :jobname ];
:global Identity;
@ -108,6 +108,6 @@
:local LogAll [ /log/find ];
:set LogForwardLast ($LogAll->([ :len $LogAll ] - 1) );
} on-error={
:global ExitError; $ExitError $ExitOK [ :jobname ];
} do={
:global ExitError; $ExitError $ExitOK [ :jobname ] $Err;
}

BIN
logo.avif

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2 KiB

After

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

View file

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

View file

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

View file

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

View file

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

139
mod/notification-gotify.rsc Normal file
View file

@ -0,0 +1,139 @@
#!rsc by RouterOS
# RouterOS script: mod/notification-gotify
# Copyright (c) 2013-2025 Christian Hesse <mail@eworm.de>
# Leonardo David Monteiro <leo@cub3.xyz>
# https://rsc.eworm.de/COPYING.md
#
# requires RouterOS, version=7.15
# requires device-mode, fetch, scheduler
#
# send notifications via Gotify (gotify.net)
# https://rsc.eworm.de/doc/mod/notification-gotify.md
:global FlushGotifyQueue;
:global NotificationFunctions;
:global PurgeGotifyQueue;
:global SendGotify;
:global SendGotify2;
# flush Gotify queue
:set FlushGotifyQueue do={ :onerror Err {
:global GotifyQueue;
:global IsFullyConnected;
:global LogPrint;
:if ([ $IsFullyConnected ] = false) do={
$LogPrint debug $0 ("System is not fully connected, not flushing.");
:return false;
}
:local AllDone true;
:local QueueLen [ :len $GotifyQueue ];
:if ([ :len [ /system/scheduler/find where name="_FlushGotifyQueue" ] ] > 0 && $QueueLen = 0) do={
$LogPrint warning $0 ("Flushing Gotify messages from scheduler, but queue is empty.");
}
:foreach Id,Message in=$GotifyQueue do={
:if ([ :typeof $Message ] = "array" ) do={
:onerror Err {
/tool/fetch check-certificate=yes-without-crl output=none http-method=post \
http-header-field=($Message->"headers") http-data=[ :serialize to=json ($Message->"message") ] \
($Message->"url") as-value;
:set ($GotifyQueue->$Id);
} do={
$LogPrint debug $0 ("Sending queued Gotify message failed: " . $Err);
:set AllDone false;
}
}
}
:if ($AllDone = true && $QueueLen = [ :len $GotifyQueue ]) do={
/system/scheduler/remove [ find where name="_FlushGotifyQueue" ];
:set GotifyQueue;
}
} do={
:global ExitError; $ExitError false $0 $Err;
} }
# send notification via Gotify - expects one array argument
:set ($NotificationFunctions->"gotify") do={
:local Notification $1;
:global Identity;
:global IdentityExtra;
:global GotifyQueue;
:global GotifyServer;
:global GotifyServerOverride;
:global GotifyToken;
:global GotifyTokenOverride;
:global EitherOr;
:global FetchUserAgentStr;
:global IfThenElse;
:global LogPrint;
:global SymbolForNotification;
:local Server [ $EitherOr ($GotifyServerOverride->($Notification->"origin")) $GotifyServer ];
:local Token [ $EitherOr ($GotifyTokenOverride->($Notification->"origin")) $GotifyToken ];
:if ([ :len $Token ] = 0) do={
:return false;
}
:local Url ("https://" . $Server . "/message");
:local Headers ({ [ $FetchUserAgentStr ($Notification->"origin") ]; \
("X-Gotify-Key: " . $Token); "Content-Type: application/json" });
:local Message ({
"title"=("[" . $IdentityExtra . $Identity . "] " . ($Notification->"subject")); \
"message"=(($Notification->"message") . "\n" . [ $IfThenElse ([ :len ($Notification->"link") ] > 0) \
("\n" . [ $SymbolForNotification "link" ] . ($Notification->"link")) ]); \
"priority"=[ :tonum [ $IfThenElse ($Notification->"silent") 2 5 ] ] });
:onerror Err {
/tool/fetch check-certificate=yes-without-crl output=none http-method=post \
http-header-field=$Headers http-data=[ :serialize to=json $Message ] $Url as-value;
} do={
$LogPrint info $0 ("Failed sending Gotify notification: " . $Err . " - Queuing...");
:if ([ :typeof $GotifyQueue ] = "nothing") do={
:set GotifyQueue ({});
}
:set ($Message->"message") (($Notification->"message") . "\n" . \
[ $SymbolForNotification "alarm-clock" ] . "This message was queued since " . \
[ /system/clock/get date ] . " " . [ /system/clock/get time ] . " and may be obsolete.");
:set ($GotifyQueue->[ :len $GotifyQueue ]) \
{ url=$Url; headers=$Headers; message=$Message };
:if ([ :len [ /system/scheduler/find where name="_FlushGotifyQueue" ] ] = 0) do={
/system/scheduler/add name="_FlushGotifyQueue" interval=1m start-time=startup \
on-event=(":global FlushGotifyQueue; \$FlushGotifyQueue;");
}
}
}
# purge the Gotify queue
:set PurgeGotifyQueue do={
:global GotifyQueue;
/system/scheduler/remove [ find where name="_FlushGotifyQueue" ];
:set GotifyQueue;
}
# send notification via Gotify - expects at least two string arguments
:set SendGotify do={ :onerror Err {
:global SendGotify2;
$SendGotify2 ({ origin=$0; subject=$1; message=$2; link=$3; silent=$4 });
} do={
:global ExitError; $ExitError false $0 $Err;
} }
# send notification via Gotify - expects one array argument
:set SendGotify2 do={
:local Notification $1;
:global NotificationFunctions;
($NotificationFunctions->"gotify") ("\$NotificationFunctions->\"gotify\"") $Notification;
}

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -59,6 +59,10 @@
132="Split off plugins from 'check-health', so the script works on all devices to monitor CPU and RAM. The supported plugins for sensors in hardware are installed automatically.";
133="Updated the default configuration for 'fw-addr-lists', deprecated lists were removed, a collective list was added.";
134="Enhanced 'mod/notification-telegram' and 'telegram-chat' to support topics in groups.";
135="Introduced helper function '\$GetTelegramChatId' for 'mod/notification-telegram' which helps retrieve information.";
136="Introduced script 'check-perpetual-license' to check for license state on CHR.";
137="Added support to send notifications via Gotify (gotify.net).";
138="RouterOS 7.19 is suffering an issue with certificate store. Fixing trust state for all certificates...";
};
# Migration steps to be applied on script updates
@ -68,4 +72,5 @@
104=":global CharacterReplace; :global ScriptInstallUpdate; :foreach Script in={ \"capsman-download-packages\"; \"capsman-rolling-upgrade\"; \"hotspot-to-wpa\"; \"hotspot-to-wpa-cleanup\" } do={ /system/script/set name=(\$Script . \".capsman\") [ find where name=\$Script ]; :foreach Scheduler in=[ /system/scheduler/find where on-event~(\$Script . \"([^-.]|\\\$)\") ] do={ /system/scheduler/set \$Scheduler on-event=[ \$CharacterReplace [ get \$Scheduler on-event ] \$Script (\$Script . \".capsman\") ]; }; }; /ip/hotspot/user/profile/set on-login=\"hotspot-to-wpa.capsman\" [ find where on-login=\"hotspot-to-wpa\" ]; \$ScriptInstallUpdate;";
111=":local Rec [ /ip/dns/static/find where comment~\"^managed by dhcp-to-dns for \" ]; :if ([ :len \$Rec ] > 0) do={ /ip/dns/static/remove \$Rec; /system/script/run dhcp-to-dns; }";
132=":if ([ :len [ /system/script/find where name=\"check-health\" ] ] > 0) do={ :local Code \":local Install \\\"check-health\\\"; :if ([ :len [ /system/health/find where type=\\\"\\\" name~\\\"-state\\\\\\\$\\\" ] ] > 0) do={ :set Install (\\\$Install . \\\",check-health.d/state\\\"); }; :if ([ :len [ /system/health/find where type=\\\"C\\\" ] ] > 0) do={ :set Install (\\\$Install . \\\",check-health.d/temperature\\\"); }; :if ([ :len [ /system/health/find where type=\\\"V\\\" ] ] > 0) do={ :set Install (\\\$Install . \\\",check-health.d/voltage\\\"); }; :global ScriptInstallUpdate; \\\$ScriptInstallUpdate \\\$Install;\"; :global ValidateSyntax; :if ([ \$ValidateSyntax \$Code ] = true) do={ :do { [ :parse \$Code ]; } on-error={ }; }; }";
138="/certificate/set trusted=yes [ find where trusted=yes ];";
};

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