aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--INITIAL-COMMANDS.md2
-rw-r--r--Makefile14
-rw-r--r--README.md6
-rw-r--r--accesslist-duplicates.capsman41
-rw-r--r--accesslist-duplicates.capsman.rsc42
-rw-r--r--accesslist-duplicates.local41
-rw-r--r--accesslist-duplicates.local.rsc42
-rw-r--r--accesslist-duplicates.template.rsc (renamed from accesslist-duplicates.template)0
-rw-r--r--backup-cloud57
-rw-r--r--backup-cloud.rsc58
-rw-r--r--backup-email106
-rw-r--r--backup-email.rsc107
-rw-r--r--backup-partition35
-rw-r--r--backup-partition.rsc36
-rw-r--r--backup-upload128
-rw-r--r--backup-upload.rsc129
-rw-r--r--capsman-download-packages85
-rw-r--r--capsman-download-packages.rsc86
-rw-r--r--capsman-rolling-upgrade35
-rw-r--r--capsman-rolling-upgrade.rsc36
-rw-r--r--certificate-renew-issued37
-rw-r--r--certificate-renew-issued.rsc38
-rw-r--r--check-certificates137
-rw-r--r--check-certificates.rsc138
-rw-r--r--check-health166
-rw-r--r--check-health.rsc167
-rw-r--r--check-lte-firmware-upgrade81
-rw-r--r--check-lte-firmware-upgrade.rsc82
-rw-r--r--check-routeros-update146
-rw-r--r--check-routeros-update.rsc147
-rw-r--r--collect-wireless-mac.capsman84
-rw-r--r--collect-wireless-mac.capsman.rsc85
-rw-r--r--collect-wireless-mac.local85
-rw-r--r--collect-wireless-mac.local.rsc86
-rw-r--r--collect-wireless-mac.template.rsc (renamed from collect-wireless-mac.template)0
-rw-r--r--daily-psk.capsman95
-rw-r--r--daily-psk.capsman.rsc96
-rw-r--r--daily-psk.local94
-rw-r--r--daily-psk.local.rsc95
-rw-r--r--daily-psk.template.rsc (renamed from daily-psk.template)0
-rw-r--r--dhcp-lease-comment.capsman29
-rw-r--r--dhcp-lease-comment.capsman.rsc30
-rw-r--r--dhcp-lease-comment.local29
-rw-r--r--dhcp-lease-comment.local.rsc30
-rw-r--r--dhcp-lease-comment.template.rsc (renamed from dhcp-lease-comment.template)0
-rw-r--r--dhcp-to-dns96
-rw-r--r--dhcp-to-dns.rsc97
-rw-r--r--doc/mod/scriptrunonce.md4
-rw-r--r--firmware-upgrade-reboot41
-rw-r--r--firmware-upgrade-reboot.rsc42
-rw-r--r--global-config219
-rw-r--r--global-config-overlay.rsc (renamed from global-config-overlay)0
-rw-r--r--global-config.changes2
-rw-r--r--global-config.rsc220
-rw-r--r--global-functions1059
-rw-r--r--global-functions.rsc1292
-rw-r--r--global-wait10
-rw-r--r--global-wait.rsc11
-rw-r--r--gps-track33
-rw-r--r--gps-track.rsc34
-rw-r--r--hotspot-to-wpa71
-rw-r--r--hotspot-to-wpa-cleanup50
-rw-r--r--hotspot-to-wpa-cleanup.rsc51
-rw-r--r--hotspot-to-wpa.rsc72
-rw-r--r--ip-addr-bridge17
-rw-r--r--ip-addr-bridge.rsc18
-rw-r--r--ipsec-to-dns68
-rw-r--r--ipsec-to-dns.rsc69
-rw-r--r--ipv6-update75
-rw-r--r--ipv6-update.rsc76
-rw-r--r--lease-script50
-rw-r--r--lease-script.rsc51
-rw-r--r--leds-day-mode8
-rw-r--r--leds-day-mode.rsc9
-rw-r--r--leds-night-mode8
-rw-r--r--leds-night-mode.rsc9
-rw-r--r--leds-toggle-mode12
-rw-r--r--leds-toggle-mode.rsc13
-rw-r--r--log-forward89
-rw-r--r--log-forward.rsc90
-rw-r--r--mod/bridge-port-to64
-rw-r--r--mod/bridge-port-to.rsc65
-rw-r--r--mod/bridge-port-vlan72
-rw-r--r--mod/bridge-port-vlan.rsc73
-rw-r--r--mod/inspectvar55
-rw-r--r--mod/inspectvar.rsc54
-rw-r--r--mod/ipcalc47
-rw-r--r--mod/ipcalc.rsc46
-rw-r--r--mod/notification-email207
-rw-r--r--mod/notification-email.rsc206
-rw-r--r--mod/notification-matrix166
-rw-r--r--mod/notification-matrix.rsc165
-rw-r--r--mod/notification-telegram177
-rw-r--r--mod/notification-telegram.rsc176
-rw-r--r--mod/scriptrunonce47
-rw-r--r--mod/scriptrunonce.rsc46
-rw-r--r--mode-button75
-rw-r--r--mode-button.rsc76
-rw-r--r--netwatch-dns93
-rw-r--r--netwatch-dns.rsc94
-rw-r--r--netwatch-notify185
-rw-r--r--netwatch-notify.rsc186
-rw-r--r--news-and-changes.rsc16
-rw-r--r--ospf-to-leds34
-rw-r--r--ospf-to-leds.rsc35
-rw-r--r--packages-update97
-rw-r--r--packages-update.rsc98
-rw-r--r--ppp-on-up33
-rw-r--r--ppp-on-up.rsc34
-rw-r--r--sms-action30
-rw-r--r--sms-action.rsc31
-rw-r--r--sms-forward83
-rw-r--r--sms-forward.rsc84
-rw-r--r--ssh-keys-import10
-rw-r--r--ssh-keys-import.rsc11
-rw-r--r--super-mario-theme68
-rw-r--r--super-mario-theme.rsc69
-rw-r--r--telegram-chat149
-rw-r--r--telegram-chat.rsc150
-rw-r--r--unattended-lte-firmware-upgrade44
-rw-r--r--unattended-lte-firmware-upgrade.rsc45
-rw-r--r--update-gre-address31
-rw-r--r--update-gre-address.rsc32
-rw-r--r--update-tunnelbroker54
-rw-r--r--update-tunnelbroker.rsc55
125 files changed, 5622 insertions, 5175 deletions
diff --git a/INITIAL-COMMANDS.md b/INITIAL-COMMANDS.md
index a31112a..a53ae0f 100644
--- a/INITIAL-COMMANDS.md
+++ b/INITIAL-COMMANDS.md
@@ -19,7 +19,7 @@ Run the complete base installation:
/file/remove "letsencrypt-R3.pem";
:delay 1s;
:foreach Script in={ "global-config"; "global-config-overlay"; "global-functions" } do={
- /system/script/add name=$Script source=([ /tool/fetch check-certificate=yes-without-crl ("https://git.eworm.de/cgit/routeros-scripts/plain/" . $Script) output=user as-value]->"data");
+ /system/script/add name=$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 { run global-config; run global-functions; };
/system/scheduler/add name="global-scripts" start-time=startup on-event="/system/script { run global-config; run global-functions; }";
diff --git a/Makefile b/Makefile
index b0737ab..8c9bcf5 100644
--- a/Makefile
+++ b/Makefile
@@ -2,9 +2,9 @@
# template scripts -> final scripts
# markdown files -> html files
-TEMPLATE = $(wildcard *.template)
-CAPSMAN = $(TEMPLATE:.template=.capsman)
-LOCAL = $(TEMPLATE:.template=.local)
+TEMPLATE = $(wildcard *.template.rsc)
+CAPSMAN = $(TEMPLATE:.template.rsc=.capsman.rsc)
+LOCAL = $(TEMPLATE:.template.rsc=.local.rsc)
MARKDOWN = $(wildcard *.md doc/*.md doc/mod/*.md)
HTML = $(MARKDOWN:.md=.html)
@@ -14,13 +14,13 @@ all: $(CAPSMAN) $(LOCAL) $(HTML)
%.html: %.md Makefile
markdown $< | sed 's/href="\([-_\./[:alnum:]]*\)\.md"/href="\1.html"/g' > $@
-%.local: %.template Makefile
- sed -e '/\/caps-man/d' -e 's|%PATH%|interface\/wireless|' -e 's|%TEMPL%|$(suffix $@)|' \
+%.local.rsc: %.template.rsc Makefile
+ sed -e '/\/caps-man/d' -e 's|%PATH%|interface\/wireless|' -e 's|%TEMPL%|.local|' \
-e '/^# !!/,/^# !!/c # !! Do not edit this file, it is generated from template!' \
< $< > $@
-%.capsman: %.template Makefile
- sed -e '/\/interface\/wireless/d' -e 's/%PATH%/caps-man/' -e 's/%TEMPL%/$(suffix $@)/' \
+%.capsman.rsc: %.template.rsc Makefile
+ sed -e '/\/interface\/wireless/d' -e 's|%PATH%|caps-man|' -e 's|%TEMPL%|.capsman|' \
-e '/^# !!/,/^# !!/c # !! Do not edit this file, it is generated from template!' \
< $< > $@
diff --git a/README.md b/README.md
index 2a8405f..9c3f703 100644
--- a/README.md
+++ b/README.md
@@ -87,7 +87,7 @@ date and time is set correctly!
Now let's download the main scripts and add them in configuration on the fly.
- :foreach Script in={ "global-config"; "global-config-overlay"; "global-functions" } do={ /system/script/add name=$Script source=([ /tool/fetch check-certificate=yes-without-crl ("https://git.eworm.de/cgit/routeros-scripts/plain/" . $Script) output=user as-value]->"data"); };
+ :foreach Script in={ "global-config"; "global-config-overlay"; "global-functions" } do={ /system/script/add name=$Script source=([ /tool/fetch check-certificate=yes-without-crl ("https://git.eworm.de/cgit/routeros-scripts/plain/" . $Script . ".rsc") output=user as-value]->"data"); };
![screenshot: import scripts](README.d/04-import-scripts.avif)
@@ -112,7 +112,7 @@ Editing configuration
The configuration needs to be tweaked for your needs. Edit
`global-config-overlay`, copy relevant configuration from
-[`global-config`](global-config) (the one without `-overlay`).
+[`global-config`](global-config.rsc) (the one without `-overlay`).
Save changes and exit with `Ctrl-o`.
/system/script/edit global-config-overlay source;
@@ -247,7 +247,7 @@ still use my scripts to manage and deploy yours, by specifying `base-url`
This will fetch and install a script `hello-world.rsc` from the given url:
- $ScriptInstallUpdate hello-world.rsc "base-url=https://git.eworm.de/cgit/routeros-scripts-custom/plain/";
+ $ScriptInstallUpdate hello-world "base-url=https://git.eworm.de/cgit/routeros-scripts-custom/plain/";
![screenshot: install custom script](README.d/13-install-custom-script.avif)
diff --git a/accesslist-duplicates.capsman b/accesslist-duplicates.capsman
index 8831cc6..2da00ca 100644
--- a/accesslist-duplicates.capsman
+++ b/accesslist-duplicates.capsman
@@ -1,42 +1,3 @@
#!rsc by RouterOS
-# RouterOS script: accesslist-duplicates.capsman
-# Copyright (c) 2018-2023 Christian Hesse <mail@eworm.de>
-# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md
#
-# print duplicate antries in wireless access list
-# https://git.eworm.de/cgit/routeros-scripts/about/doc/accesslist-duplicates.md
-#
-# !! Do not edit this file, it is generated from template!
-
-:local 0 "accesslist-duplicates.capsman";
-:global GlobalFunctionsReady;
-:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
-
-:global Read;
-
-:local Seen ({});
-:local Shown ({});
-
-: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 ];
- :foreach SeenMac in=$Seen do={
- :if ($SeenMac = $Mac) do={
- :local Skip 0;
- :foreach ShownMac in=$Shown do={
- :if ($ShownMac = $Mac) do={ :set Skip 1; }
- }
- :if ($Skip = 0) do={
- /caps-man/access-list/print where mac-address=$Mac;
- :set Shown ($Shown, $Mac);
-
- :put "\nNumeric id to remove, any key to skip!";
- :local Remove [ :tonum [ $Read ] ];
- :if ([ :typeof $Remove ] = "num") do={
- :put ("Removing numeric id " . $Remove . "...\n");
- /caps-man/access-list/remove $Remove;
- }
- }
- }
- }
- :set Seen ($Seen, $Mac);
-}
+# dummy for migration
diff --git a/accesslist-duplicates.capsman.rsc b/accesslist-duplicates.capsman.rsc
new file mode 100644
index 0000000..8831cc6
--- /dev/null
+++ b/accesslist-duplicates.capsman.rsc
@@ -0,0 +1,42 @@
+#!rsc by RouterOS
+# RouterOS script: accesslist-duplicates.capsman
+# Copyright (c) 2018-2023 Christian Hesse <mail@eworm.de>
+# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md
+#
+# print duplicate antries in wireless access list
+# https://git.eworm.de/cgit/routeros-scripts/about/doc/accesslist-duplicates.md
+#
+# !! Do not edit this file, it is generated from template!
+
+:local 0 "accesslist-duplicates.capsman";
+:global GlobalFunctionsReady;
+:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
+
+:global Read;
+
+:local Seen ({});
+:local Shown ({});
+
+: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 ];
+ :foreach SeenMac in=$Seen do={
+ :if ($SeenMac = $Mac) do={
+ :local Skip 0;
+ :foreach ShownMac in=$Shown do={
+ :if ($ShownMac = $Mac) do={ :set Skip 1; }
+ }
+ :if ($Skip = 0) do={
+ /caps-man/access-list/print where mac-address=$Mac;
+ :set Shown ($Shown, $Mac);
+
+ :put "\nNumeric id to remove, any key to skip!";
+ :local Remove [ :tonum [ $Read ] ];
+ :if ([ :typeof $Remove ] = "num") do={
+ :put ("Removing numeric id " . $Remove . "...\n");
+ /caps-man/access-list/remove $Remove;
+ }
+ }
+ }
+ }
+ :set Seen ($Seen, $Mac);
+}
diff --git a/accesslist-duplicates.local b/accesslist-duplicates.local
index d4b8867..2da00ca 100644
--- a/accesslist-duplicates.local
+++ b/accesslist-duplicates.local
@@ -1,42 +1,3 @@
#!rsc by RouterOS
-# RouterOS script: accesslist-duplicates.local
-# Copyright (c) 2018-2023 Christian Hesse <mail@eworm.de>
-# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md
#
-# print duplicate antries in wireless access list
-# https://git.eworm.de/cgit/routeros-scripts/about/doc/accesslist-duplicates.md
-#
-# !! Do not edit this file, it is generated from template!
-
-:local 0 "accesslist-duplicates.local";
-:global GlobalFunctionsReady;
-:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
-
-:global Read;
-
-:local Seen ({});
-:local Shown ({});
-
-: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 ];
- :foreach SeenMac in=$Seen do={
- :if ($SeenMac = $Mac) do={
- :local Skip 0;
- :foreach ShownMac in=$Shown do={
- :if ($ShownMac = $Mac) do={ :set Skip 1; }
- }
- :if ($Skip = 0) do={
- /interface/wireless/access-list/print where mac-address=$Mac;
- :set Shown ($Shown, $Mac);
-
- :put "\nNumeric id to remove, any key to skip!";
- :local Remove [ :tonum [ $Read ] ];
- :if ([ :typeof $Remove ] = "num") do={
- :put ("Removing numeric id " . $Remove . "...\n");
- /interface/wireless/access-list/remove $Remove;
- }
- }
- }
- }
- :set Seen ($Seen, $Mac);
-}
+# dummy for migration
diff --git a/accesslist-duplicates.local.rsc b/accesslist-duplicates.local.rsc
new file mode 100644
index 0000000..d4b8867
--- /dev/null
+++ b/accesslist-duplicates.local.rsc
@@ -0,0 +1,42 @@
+#!rsc by RouterOS
+# RouterOS script: accesslist-duplicates.local
+# Copyright (c) 2018-2023 Christian Hesse <mail@eworm.de>
+# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md
+#
+# print duplicate antries in wireless access list
+# https://git.eworm.de/cgit/routeros-scripts/about/doc/accesslist-duplicates.md
+#
+# !! Do not edit this file, it is generated from template!
+
+:local 0 "accesslist-duplicates.local";
+:global GlobalFunctionsReady;
+:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
+
+:global Read;
+
+:local Seen ({});
+:local Shown ({});
+
+: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 ];
+ :foreach SeenMac in=$Seen do={
+ :if ($SeenMac = $Mac) do={
+ :local Skip 0;
+ :foreach ShownMac in=$Shown do={
+ :if ($ShownMac = $Mac) do={ :set Skip 1; }
+ }
+ :if ($Skip = 0) do={
+ /interface/wireless/access-list/print where mac-address=$Mac;
+ :set Shown ($Shown, $Mac);
+
+ :put "\nNumeric id to remove, any key to skip!";
+ :local Remove [ :tonum [ $Read ] ];
+ :if ([ :typeof $Remove ] = "num") do={
+ :put ("Removing numeric id " . $Remove . "...\n");
+ /interface/wireless/access-list/remove $Remove;
+ }
+ }
+ }
+ }
+ :set Seen ($Seen, $Mac);
+}
diff --git a/accesslist-duplicates.template b/accesslist-duplicates.template.rsc
index 80c47a9..80c47a9 100644
--- a/accesslist-duplicates.template
+++ b/accesslist-duplicates.template.rsc
diff --git a/backup-cloud b/backup-cloud
index b75d5cb..2da00ca 100644
--- a/backup-cloud
+++ b/backup-cloud
@@ -1,58 +1,3 @@
#!rsc by RouterOS
-# RouterOS script: backup-cloud
-# Copyright (c) 2013-2023 Christian Hesse <mail@eworm.de>
-# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md
#
-# provides: backup-script
-#
-# upload backup to MikroTik cloud
-# https://git.eworm.de/cgit/routeros-scripts/about/doc/backup-cloud.md
-
-:local 0 "backup-cloud";
-:global GlobalFunctionsReady;
-:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
-
-:global BackupPassword;
-:global BackupRandomDelay;
-:global Identity;
-
-:global DeviceInfo;
-:global LogPrintExit2;
-:global RandomDelay;
-:global ScriptFromTerminal;
-:global SendNotification2;
-:global SymbolForNotification;
-:global WaitFullyConnected;
-
-$WaitFullyConnected;
-
-:if ([ $ScriptFromTerminal $0 ] = false && $BackupRandomDelay > 0) do={
- $RandomDelay $BackupRandomDelay;
-}
-
-:do {
- # we are not interested in output, but print is
- # required to fetch information from cloud
- /system/backup/cloud/print as-value;
- :if ([ :len [ /system/backup/cloud/find ] ] > 0) do={
- /system/backup/cloud/upload-file action=create-and-upload \
- password=$BackupPassword replace=[ get ([ find ]->0) name ];
- } else={
- /system/backup/cloud/upload-file action=create-and-upload \
- password=$BackupPassword;
- }
- :local Cloud [ /system/backup/cloud/get ([ find ]->0) ];
-
- $SendNotification2 ({ origin=$0; \
- subject=([ $SymbolForNotification "floppy-disk,cloud" ] . "Cloud backup"); \
- message=("Uploaded backup for " . $Identity . " to cloud.\n\n" . \
- [ $DeviceInfo ] . "\n\n" . \
- "Name: " . $Cloud->"name" . "\n" . \
- "Size: " . $Cloud->"size" . " B (" . ($Cloud->"size" / 1024) . " KiB)\n" . \
- "Download key: " . $Cloud->"secret-download-key"); silent=true });
-} on-error={
- $SendNotification2 ({ origin=$0; \
- subject=([ $SymbolForNotification "floppy-disk,warning-sign" ] . "Cloud backup failed"); \
- message=("Failed uploading backup for " . $Identity . " to cloud!\n\n" . [ $DeviceInfo ]) });
- $LogPrintExit2 error $0 ("Failed uploading backup for " . $Identity . " to cloud!") true;
-}
+# dummy for migration
diff --git a/backup-cloud.rsc b/backup-cloud.rsc
new file mode 100644
index 0000000..b75d5cb
--- /dev/null
+++ b/backup-cloud.rsc
@@ -0,0 +1,58 @@
+#!rsc by RouterOS
+# RouterOS script: backup-cloud
+# Copyright (c) 2013-2023 Christian Hesse <mail@eworm.de>
+# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md
+#
+# provides: backup-script
+#
+# upload backup to MikroTik cloud
+# https://git.eworm.de/cgit/routeros-scripts/about/doc/backup-cloud.md
+
+:local 0 "backup-cloud";
+:global GlobalFunctionsReady;
+:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
+
+:global BackupPassword;
+:global BackupRandomDelay;
+:global Identity;
+
+:global DeviceInfo;
+:global LogPrintExit2;
+:global RandomDelay;
+:global ScriptFromTerminal;
+:global SendNotification2;
+:global SymbolForNotification;
+:global WaitFullyConnected;
+
+$WaitFullyConnected;
+
+:if ([ $ScriptFromTerminal $0 ] = false && $BackupRandomDelay > 0) do={
+ $RandomDelay $BackupRandomDelay;
+}
+
+:do {
+ # we are not interested in output, but print is
+ # required to fetch information from cloud
+ /system/backup/cloud/print as-value;
+ :if ([ :len [ /system/backup/cloud/find ] ] > 0) do={
+ /system/backup/cloud/upload-file action=create-and-upload \
+ password=$BackupPassword replace=[ get ([ find ]->0) name ];
+ } else={
+ /system/backup/cloud/upload-file action=create-and-upload \
+ password=$BackupPassword;
+ }
+ :local Cloud [ /system/backup/cloud/get ([ find ]->0) ];
+
+ $SendNotification2 ({ origin=$0; \
+ subject=([ $SymbolForNotification "floppy-disk,cloud" ] . "Cloud backup"); \
+ message=("Uploaded backup for " . $Identity . " to cloud.\n\n" . \
+ [ $DeviceInfo ] . "\n\n" . \
+ "Name: " . $Cloud->"name" . "\n" . \
+ "Size: " . $Cloud->"size" . " B (" . ($Cloud->"size" / 1024) . " KiB)\n" . \
+ "Download key: " . $Cloud->"secret-download-key"); silent=true });
+} on-error={
+ $SendNotification2 ({ origin=$0; \
+ subject=([ $SymbolForNotification "floppy-disk,warning-sign" ] . "Cloud backup failed"); \
+ message=("Failed uploading backup for " . $Identity . " to cloud!\n\n" . [ $DeviceInfo ]) });
+ $LogPrintExit2 error $0 ("Failed uploading backup for " . $Identity . " to cloud!") true;
+}
diff --git a/backup-email b/backup-email
index ba12494..2da00ca 100644
--- a/backup-email
+++ b/backup-email
@@ -1,107 +1,3 @@
#!rsc by RouterOS
-# RouterOS script: backup-email
-# Copyright (c) 2013-2023 Christian Hesse <mail@eworm.de>
-# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md
#
-# provides: backup-script
-#
-# create and email backup and config file
-# https://git.eworm.de/cgit/routeros-scripts/about/doc/backup-email.md
-
-:local 0 "backup-email";
-:global GlobalFunctionsReady;
-:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
-
-:global BackupPassword;
-:global BackupRandomDelay;
-:global BackupSendBinary;
-:global BackupSendExport;
-:global BackupSendGlobalConfig;
-:global Domain;
-:global Identity;
-
-:global CharacterReplace;
-:global DeviceInfo;
-:global LogPrintExit2;
-:global MkDir;
-:global RandomDelay;
-:global ScriptFromTerminal;
-:global SendEMail2;
-:global SymbolForNotification;
-:global WaitForFile;
-:global WaitFullyConnected;
-
-:if ([ :typeof $SendEMail2 ] = "nothing") do={
- $LogPrintExit2 error $0 ("The module for sending notifications via e-mail is not installed.") true;
-}
-
-:if ($BackupSendBinary != true && \
- $BackupSendExport != true) do={
- $LogPrintExit2 error $0 ("Configured to send neither backup nor config export.") true;
-}
-
-$WaitFullyConnected;
-
-:if ([ $ScriptFromTerminal $0 ] = false && $BackupRandomDelay > 0) do={
- $RandomDelay $BackupRandomDelay;
-}
-
-# filename based on identity
-:local DirName ("tmpfs/" . $0);
-:local FileName [ $CharacterReplace ($Identity . "." . $Domain) "." "_" ];
-:local FilePath ($DirName . "/" . $FileName);
-:local BackupFile "none";
-:local ExportFile "none";
-:local ConfigFile "none";
-:local Attach ({});
-
-:if ([ $MkDir $DirName ] = false) do={
- $LogPrintExit2 error $0 ("Failed creating directory!") true;
-}
-
-# binary backup
-:if ($BackupSendBinary = true) do={
- /system/backup/save encryption=aes-sha256 name=$FilePath password=$BackupPassword;
- $WaitForFile ($FilePath . ".backup");
- :set BackupFile ($FileName . ".backup");
- :set Attach ($Attach, ($FilePath . ".backup"));
-}
-
-# create configuration export
-:if ($BackupSendExport = true) do={
- /export terse show-sensitive file=$FilePath;
- $WaitForFile ($FilePath . ".rsc");
- :set ExportFile ($FileName . ".rsc");
- :set Attach ($Attach, ($FilePath . ".rsc"));
-}
-
-# global-config-overlay
-:if ($BackupSendGlobalConfig = true) do={
- :execute script={ :put [ /system/script/get global-config-overlay source ]; } \
- file=($FilePath . ".conf");
- $WaitForFile ($FilePath . ".conf.txt");
- :set ConfigFile ($FileName . ".conf.txt");
- :set Attach ($Attach, ($FilePath . ".conf.txt"));
-}
-
-# send email with status and files
-$SendEMail2 ({ origin=$0; \
- subject=([ $SymbolForNotification "floppy-disk,incoming-envelope" ] . \
- "Backup & Config"); \
- message=("See attached files for backup and config export for " . \
- $Identity . ".\n\n" . \
- [ $DeviceInfo ] . "\n\n" . \
- "Backup file: " . $BackupFile . "\n" . \
- "Export file: " . $ExportFile . "\n" . \
- "Config file: " . $ConfigFile); \
- 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={
- $LogPrintExit2 warning $0 ("Files are still available, sending e-mail failed.") true;
- }
- :delay 1s;
- :set I ($I + 1);
-}
+# dummy for migration
diff --git a/backup-email.rsc b/backup-email.rsc
new file mode 100644
index 0000000..ba12494
--- /dev/null
+++ b/backup-email.rsc
@@ -0,0 +1,107 @@
+#!rsc by RouterOS
+# RouterOS script: backup-email
+# Copyright (c) 2013-2023 Christian Hesse <mail@eworm.de>
+# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md
+#
+# provides: backup-script
+#
+# create and email backup and config file
+# https://git.eworm.de/cgit/routeros-scripts/about/doc/backup-email.md
+
+:local 0 "backup-email";
+:global GlobalFunctionsReady;
+:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
+
+:global BackupPassword;
+:global BackupRandomDelay;
+:global BackupSendBinary;
+:global BackupSendExport;
+:global BackupSendGlobalConfig;
+:global Domain;
+:global Identity;
+
+:global CharacterReplace;
+:global DeviceInfo;
+:global LogPrintExit2;
+:global MkDir;
+:global RandomDelay;
+:global ScriptFromTerminal;
+:global SendEMail2;
+:global SymbolForNotification;
+:global WaitForFile;
+:global WaitFullyConnected;
+
+:if ([ :typeof $SendEMail2 ] = "nothing") do={
+ $LogPrintExit2 error $0 ("The module for sending notifications via e-mail is not installed.") true;
+}
+
+:if ($BackupSendBinary != true && \
+ $BackupSendExport != true) do={
+ $LogPrintExit2 error $0 ("Configured to send neither backup nor config export.") true;
+}
+
+$WaitFullyConnected;
+
+:if ([ $ScriptFromTerminal $0 ] = false && $BackupRandomDelay > 0) do={
+ $RandomDelay $BackupRandomDelay;
+}
+
+# filename based on identity
+:local DirName ("tmpfs/" . $0);
+:local FileName [ $CharacterReplace ($Identity . "." . $Domain) "." "_" ];
+:local FilePath ($DirName . "/" . $FileName);
+:local BackupFile "none";
+:local ExportFile "none";
+:local ConfigFile "none";
+:local Attach ({});
+
+:if ([ $MkDir $DirName ] = false) do={
+ $LogPrintExit2 error $0 ("Failed creating directory!") true;
+}
+
+# binary backup
+:if ($BackupSendBinary = true) do={
+ /system/backup/save encryption=aes-sha256 name=$FilePath password=$BackupPassword;
+ $WaitForFile ($FilePath . ".backup");
+ :set BackupFile ($FileName . ".backup");
+ :set Attach ($Attach, ($FilePath . ".backup"));
+}
+
+# create configuration export
+:if ($BackupSendExport = true) do={
+ /export terse show-sensitive file=$FilePath;
+ $WaitForFile ($FilePath . ".rsc");
+ :set ExportFile ($FileName . ".rsc");
+ :set Attach ($Attach, ($FilePath . ".rsc"));
+}
+
+# global-config-overlay
+:if ($BackupSendGlobalConfig = true) do={
+ :execute script={ :put [ /system/script/get global-config-overlay source ]; } \
+ file=($FilePath . ".conf");
+ $WaitForFile ($FilePath . ".conf.txt");
+ :set ConfigFile ($FileName . ".conf.txt");
+ :set Attach ($Attach, ($FilePath . ".conf.txt"));
+}
+
+# send email with status and files
+$SendEMail2 ({ origin=$0; \
+ subject=([ $SymbolForNotification "floppy-disk,incoming-envelope" ] . \
+ "Backup & Config"); \
+ message=("See attached files for backup and config export for " . \
+ $Identity . ".\n\n" . \
+ [ $DeviceInfo ] . "\n\n" . \
+ "Backup file: " . $BackupFile . "\n" . \
+ "Export file: " . $ExportFile . "\n" . \
+ "Config file: " . $ConfigFile); \
+ 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={
+ $LogPrintExit2 warning $0 ("Files are still available, sending e-mail failed.") true;
+ }
+ :delay 1s;
+ :set I ($I + 1);
+}
diff --git a/backup-partition b/backup-partition
index 824cb7e..2da00ca 100644
--- a/backup-partition
+++ b/backup-partition
@@ -1,36 +1,3 @@
#!rsc by RouterOS
-# RouterOS script: backup-partition
-# Copyright (c) 2022-2023 Christian Hesse <mail@eworm.de>
-# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md
#
-# provides: backup-script
-#
-# save configuration to fallback partition
-# https://git.eworm.de/cgit/routeros-scripts/about/doc/backup-partition.md
-
-:local 0 "backup-partition";
-:global GlobalFunctionsReady;
-:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
-
-:global LogPrintExit2;
-
-:if ([ :len [ /partitions/find ] ] < 2) do={
- $LogPrintExit2 error $0 ("Device does not have a fallback partition.") true;
-}
-
-:local ActiveRunning [ /partitions/find where active running ];
-
-:if ([ :len $ActiveRunning ] < 1) do={
- $LogPrintExit2 error $0 ("Device is not running from active partition.") true;
-}
-
-:local ActiveRunningVar [ /partitions/get $ActiveRunning ];
-
-:do {
- /partitions/save-config-to ($ActiveRunningVar->"fallback-to");
- $LogPrintExit2 info $0 ("Saved configuration to partition '" . \
- ($ActiveRunningVar->"fallback-to") . "'.") false;
-} on-error={
- $LogPrintExit2 error $0 ("Failed saving configuration to partition '" . \
- ($ActiveRunningVar->"fallback-to") . "'!") true;
-}
+# dummy for migration
diff --git a/backup-partition.rsc b/backup-partition.rsc
new file mode 100644
index 0000000..824cb7e
--- /dev/null
+++ b/backup-partition.rsc
@@ -0,0 +1,36 @@
+#!rsc by RouterOS
+# RouterOS script: backup-partition
+# Copyright (c) 2022-2023 Christian Hesse <mail@eworm.de>
+# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md
+#
+# provides: backup-script
+#
+# save configuration to fallback partition
+# https://git.eworm.de/cgit/routeros-scripts/about/doc/backup-partition.md
+
+:local 0 "backup-partition";
+:global GlobalFunctionsReady;
+:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
+
+:global LogPrintExit2;
+
+:if ([ :len [ /partitions/find ] ] < 2) do={
+ $LogPrintExit2 error $0 ("Device does not have a fallback partition.") true;
+}
+
+:local ActiveRunning [ /partitions/find where active running ];
+
+:if ([ :len $ActiveRunning ] < 1) do={
+ $LogPrintExit2 error $0 ("Device is not running from active partition.") true;
+}
+
+:local ActiveRunningVar [ /partitions/get $ActiveRunning ];
+
+:do {
+ /partitions/save-config-to ($ActiveRunningVar->"fallback-to");
+ $LogPrintExit2 info $0 ("Saved configuration to partition '" . \
+ ($ActiveRunningVar->"fallback-to") . "'.") false;
+} on-error={
+ $LogPrintExit2 error $0 ("Failed saving configuration to partition '" . \
+ ($ActiveRunningVar->"fallback-to") . "'!") true;
+}
diff --git a/backup-upload b/backup-upload
index 4c09d4a..2da00ca 100644
--- a/backup-upload
+++ b/backup-upload
@@ -1,129 +1,3 @@
#!rsc by RouterOS
-# RouterOS script: backup-upload
-# Copyright (c) 2013-2023 Christian Hesse <mail@eworm.de>
-# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md
#
-# provides: backup-script
-#
-# create and upload backup and config file
-# https://git.eworm.de/cgit/routeros-scripts/about/doc/backup-upload.md
-
-:local 0 "backup-upload";
-:global GlobalFunctionsReady;
-:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
-
-:global BackupPassword;
-:global BackupRandomDelay;
-:global BackupSendBinary;
-:global BackupSendExport;
-:global BackupSendGlobalConfig;
-:global BackupUploadPass;
-:global BackupUploadUrl;
-:global BackupUploadUser;
-:global Domain;
-:global Identity;
-
-:global CharacterReplace;
-:global DeviceInfo;
-:global IfThenElse;
-:global LogPrintExit2;
-:global MkDir;
-:global RandomDelay;
-:global ScriptFromTerminal;
-:global SendNotification2;
-:global SymbolForNotification;
-:global WaitForFile;
-:global WaitFullyConnected;
-
-:if ($BackupSendBinary != true && \
- $BackupSendExport != true) do={
- $LogPrintExit2 error $0 ("Configured to send neither backup nor config export.") true;
-}
-
-$WaitFullyConnected;
-
-:if ([ $ScriptFromTerminal $0 ] = false && $BackupRandomDelay > 0) do={
- $RandomDelay $BackupRandomDelay;
-}
-
-# filename based on identity
-:local DirName ("tmpfs/" . $0);
-:local FileName [ $CharacterReplace ($Identity . "." . $Domain) "." "_" ];
-:local FilePath ($DirName . "/" . $FileName);
-:local BackupFile "none";
-:local ExportFile "none";
-:local ConfigFile "none";
-:local Failed 0;
-
-:if ([ $MkDir $DirName ] = false) do={
- $LogPrintExit2 error $0 ("Failed creating directory!") true;
-}
-
-# binary backup
-:if ($BackupSendBinary = true) do={
- /system/backup/save encryption=aes-sha256 name=$FilePath password=$BackupPassword;
- $WaitForFile ($FilePath . ".backup");
-
- :do {
- /tool/fetch upload=yes url=($BackupUploadUrl . "/" . $FileName . ".backup") \
- user=$BackupUploadUser password=$BackupUploadPass src-path=($FilePath . ".backup");
- :set BackupFile ($FileName . ".backup");
- } on-error={
- $LogPrintExit2 error $0 ("Uploading backup file failed!") false;
- :set BackupFile "failed";
- :set Failed 1;
- }
-
- /file/remove ($FilePath . ".backup");
-}
-
-# create configuration export
-:if ($BackupSendExport = true) do={
- /export terse show-sensitive file=$FilePath;
- $WaitForFile ($FilePath . ".rsc");
-
- :do {
- /tool/fetch upload=yes url=($BackupUploadUrl . "/" . $FileName . ".rsc") \
- user=$BackupUploadUser password=$BackupUploadPass src-path=($FilePath . ".rsc");
- :set ExportFile ($FileName . ".rsc");
- } on-error={
- $LogPrintExit2 error $0 ("Uploading configuration export failed!") false;
- :set ExportFile "failed";
- :set Failed 1;
- }
-
- /file/remove ($FilePath . ".rsc");
-}
-
-# global-config-overlay
-:if ($BackupSendGlobalConfig = true) do={
- :execute script={ :put [ /system/script/get global-config-overlay source ]; } \
- file=($FilePath . ".conf");
- $WaitForFile ($FilePath . ".conf.txt");
-
- :do {
- /tool/fetch upload=yes url=($BackupUploadUrl . "/" . $FileName . ".conf") \
- user=$BackupUploadUser password=$BackupUploadPass src-path=($FilePath . ".conf.txt");
- :set ConfigFile ($FileName . ".conf");
- } on-error={
- $LogPrintExit2 error $0 ("Uploading global-config-overlay failed!") false;
- :set ConfigFile "failed";
- :set Failed 1;
- }
-
- /file/remove ($FilePath . ".conf.txt");
-}
-
-$SendNotification2 ({ origin=$0; \
- subject=[ $IfThenElse ($Failed > 0) \
- ([ $SymbolForNotification "floppy-disk,warning-sign" ] . "Backup & Config upload with failure") \
- ([ $SymbolForNotification "floppy-disk,up-arrow" ] . "Backup & Config upload") ]; \
- message=("Backup and config export upload for " . $Identity . ".\n\n" . \
- [ $DeviceInfo ] . "\n\n" . \
- "Backup file: " . $BackupFile . "\n" . \
- "Export file: " . $ExportFile . "\n" . \
- "Config file: " . $ConfigFile); silent=true });
-
-:if ($Failed = 1) do={
- :error "An error occured!";
-}
+# dummy for migration
diff --git a/backup-upload.rsc b/backup-upload.rsc
new file mode 100644
index 0000000..4c09d4a
--- /dev/null
+++ b/backup-upload.rsc
@@ -0,0 +1,129 @@
+#!rsc by RouterOS
+# RouterOS script: backup-upload
+# Copyright (c) 2013-2023 Christian Hesse <mail@eworm.de>
+# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md
+#
+# provides: backup-script
+#
+# create and upload backup and config file
+# https://git.eworm.de/cgit/routeros-scripts/about/doc/backup-upload.md
+
+:local 0 "backup-upload";
+:global GlobalFunctionsReady;
+:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
+
+:global BackupPassword;
+:global BackupRandomDelay;
+:global BackupSendBinary;
+:global BackupSendExport;
+:global BackupSendGlobalConfig;
+:global BackupUploadPass;
+:global BackupUploadUrl;
+:global BackupUploadUser;
+:global Domain;
+:global Identity;
+
+:global CharacterReplace;
+:global DeviceInfo;
+:global IfThenElse;
+:global LogPrintExit2;
+:global MkDir;
+:global RandomDelay;
+:global ScriptFromTerminal;
+:global SendNotification2;
+:global SymbolForNotification;
+:global WaitForFile;
+:global WaitFullyConnected;
+
+:if ($BackupSendBinary != true && \
+ $BackupSendExport != true) do={
+ $LogPrintExit2 error $0 ("Configured to send neither backup nor config export.") true;
+}
+
+$WaitFullyConnected;
+
+:if ([ $ScriptFromTerminal $0 ] = false && $BackupRandomDelay > 0) do={
+ $RandomDelay $BackupRandomDelay;
+}
+
+# filename based on identity
+:local DirName ("tmpfs/" . $0);
+:local FileName [ $CharacterReplace ($Identity . "." . $Domain) "." "_" ];
+:local FilePath ($DirName . "/" . $FileName);
+:local BackupFile "none";
+:local ExportFile "none";
+:local ConfigFile "none";
+:local Failed 0;
+
+:if ([ $MkDir $DirName ] = false) do={
+ $LogPrintExit2 error $0 ("Failed creating directory!") true;
+}
+
+# binary backup
+:if ($BackupSendBinary = true) do={
+ /system/backup/save encryption=aes-sha256 name=$FilePath password=$BackupPassword;
+ $WaitForFile ($FilePath . ".backup");
+
+ :do {
+ /tool/fetch upload=yes url=($BackupUploadUrl . "/" . $FileName . ".backup") \
+ user=$BackupUploadUser password=$BackupUploadPass src-path=($FilePath . ".backup");
+ :set BackupFile ($FileName . ".backup");
+ } on-error={
+ $LogPrintExit2 error $0 ("Uploading backup file failed!") false;
+ :set BackupFile "failed";
+ :set Failed 1;
+ }
+
+ /file/remove ($FilePath . ".backup");
+}
+
+# create configuration export
+:if ($BackupSendExport = true) do={
+ /export terse show-sensitive file=$FilePath;
+ $WaitForFile ($FilePath . ".rsc");
+
+ :do {
+ /tool/fetch upload=yes url=($BackupUploadUrl . "/" . $FileName . ".rsc") \
+ user=$BackupUploadUser password=$BackupUploadPass src-path=($FilePath . ".rsc");
+ :set ExportFile ($FileName . ".rsc");
+ } on-error={
+ $LogPrintExit2 error $0 ("Uploading configuration export failed!") false;
+ :set ExportFile "failed";
+ :set Failed 1;
+ }
+
+ /file/remove ($FilePath . ".rsc");
+}
+
+# global-config-overlay
+:if ($BackupSendGlobalConfig = true) do={
+ :execute script={ :put [ /system/script/get global-config-overlay source ]; } \
+ file=($FilePath . ".conf");
+ $WaitForFile ($FilePath . ".conf.txt");
+
+ :do {
+ /tool/fetch upload=yes url=($BackupUploadUrl . "/" . $FileName . ".conf") \
+ user=$BackupUploadUser password=$BackupUploadPass src-path=($FilePath . ".conf.txt");
+ :set ConfigFile ($FileName . ".conf");
+ } on-error={
+ $LogPrintExit2 error $0 ("Uploading global-config-overlay failed!") false;
+ :set ConfigFile "failed";
+ :set Failed 1;
+ }
+
+ /file/remove ($FilePath . ".conf.txt");
+}
+
+$SendNotification2 ({ origin=$0; \
+ subject=[ $IfThenElse ($Failed > 0) \
+ ([ $SymbolForNotification "floppy-disk,warning-sign" ] . "Backup & Config upload with failure") \
+ ([ $SymbolForNotification "floppy-disk,up-arrow" ] . "Backup & Config upload") ]; \
+ message=("Backup and config export upload for " . $Identity . ".\n\n" . \
+ [ $DeviceInfo ] . "\n\n" . \
+ "Backup file: " . $BackupFile . "\n" . \
+ "Export file: " . $ExportFile . "\n" . \
+ "Config file: " . $ConfigFile); silent=true });
+
+:if ($Failed = 1) do={
+ :error "An error occured!";
+}
diff --git a/capsman-download-packages b/capsman-download-packages
index 08edd59..2da00ca 100644
--- a/capsman-download-packages
+++ b/capsman-download-packages
@@ -1,86 +1,3 @@
#!rsc by RouterOS
-# RouterOS script: capsman-download-packages
-# Copyright (c) 2018-2023 Christian Hesse <mail@eworm.de>
-# Michael Gisbers <michael@gisbers.de>
-# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md
#
-# download and cleanup packages for CAP installation from CAPsMAN
-# https://git.eworm.de/cgit/routeros-scripts/about/doc/capsman-download-packages.md
-
-:local 0 "capsman-download-packages";
-:global GlobalFunctionsReady;
-:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
-
-:global CleanFilePath;
-:global DownloadPackage;
-:global LogPrintExit2;
-:global MkDir;
-:global ScriptLock;
-:global WaitFullyConnected;
-
-$ScriptLock $0;
-$WaitFullyConnected;
-
-:local PackagePath [ $CleanFilePath [ /caps-man/manager/get package-path ] ];
-:local InstalledVersion [ /system/package/update/get installed-version ];
-:local Updated false;
-
-:if ([ :len $PackagePath ] = 0) do={
- $LogPrintExit2 warning $0 ("The CAPsMAN package path is not defined, can not download packages.") true;
-}
-
-:if ([ :len [ /file/find where name=$PackagePath type="directory" ] ] = 0) do={
- :if ([ $MkDir $PackagePath ] = false) do={
- $LogPrintExit2 warning $0 ("Creating directory at CAPsMAN package path (" . \
- $PackagePath . ") failed!") true;
- }
- $LogPrintExit2 info $0 ("Created directory at CAPsMAN package path (" . $PackagePath . \
- "). Please place your packages!") false;
-}
-
-: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={
- :set ($File->"package-architecture") "mipsbe";
- }
- :if ([ $DownloadPackage ($File->"package-name") $InstalledVersion \
- ($File->"package-architecture") $PackagePath ] = true) do={
- :set Updated true;
- /file/remove $Package;
- }
-}
-
-:if ([ :len [ /system/logging/find where topics~"error" !(topics~"!error") \
- !(topics~"!caps") action=memory !disabled !invalid ] ] < 1) do={
- $LogPrintExit2 warning $0 ("Looks like error messages for 'caps' are not sent to memory. " . \
- "Probably can not download packages automatically.") false;
-} else={
- :if ($Updated = false && [ /system/resource/get uptime ] < 2m) do={
- $LogPrintExit2 info $0 ("No packages downloaded, yet. Delaying for logs.") false;
- :delay 2m;
- }
-}
-
-:foreach Log in=[ /log/find where topics=({"caps"; "error"}) \
- message~("upgrade status: failed, failed to download file '.*-" . $InstalledVersion . \
- "-.*\\.npk', no such file") ] do={
- :local Message [ /log/get $Log message ];
- :local Package [ :pick $Message \
- ([ :find $Message "'" ] + 1) \
- [ :find $Message ("-" . $InstalledVersion . "-") ] ];
- :local Arch [ :pick $Message \
- ([ :find $Message ("-" . $InstalledVersion . "-") ] + 2 + [ :len $InstalledVersion ]) \
- [ :find $Message ".npk" ] ];
- :if ([ $DownloadPackage $Package $InstalledVersion $Arch $PackagePath ] = true) do={
- :set Updated true;
- }
-}
-
-:if ($Updated = true) do={
- :if ([ :len [ /system/script/find where name="capsman-rolling-upgrade" ] ] > 0) do={
- /system/script/run capsman-rolling-upgrade;
- } else={
- /caps-man/remote-cap/upgrade [ find where version!=$InstalledVersion ];
- }
-}
+# dummy for migration
diff --git a/capsman-download-packages.rsc b/capsman-download-packages.rsc
new file mode 100644
index 0000000..08edd59
--- /dev/null
+++ b/capsman-download-packages.rsc
@@ -0,0 +1,86 @@
+#!rsc by RouterOS
+# RouterOS script: capsman-download-packages
+# Copyright (c) 2018-2023 Christian Hesse <mail@eworm.de>
+# Michael Gisbers <michael@gisbers.de>
+# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md
+#
+# download and cleanup packages for CAP installation from CAPsMAN
+# https://git.eworm.de/cgit/routeros-scripts/about/doc/capsman-download-packages.md
+
+:local 0 "capsman-download-packages";
+:global GlobalFunctionsReady;
+:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
+
+:global CleanFilePath;
+:global DownloadPackage;
+:global LogPrintExit2;
+:global MkDir;
+:global ScriptLock;
+:global WaitFullyConnected;
+
+$ScriptLock $0;
+$WaitFullyConnected;
+
+:local PackagePath [ $CleanFilePath [ /caps-man/manager/get package-path ] ];
+:local InstalledVersion [ /system/package/update/get installed-version ];
+:local Updated false;
+
+:if ([ :len $PackagePath ] = 0) do={
+ $LogPrintExit2 warning $0 ("The CAPsMAN package path is not defined, can not download packages.") true;
+}
+
+:if ([ :len [ /file/find where name=$PackagePath type="directory" ] ] = 0) do={
+ :if ([ $MkDir $PackagePath ] = false) do={
+ $LogPrintExit2 warning $0 ("Creating directory at CAPsMAN package path (" . \
+ $PackagePath . ") failed!") true;
+ }
+ $LogPrintExit2 info $0 ("Created directory at CAPsMAN package path (" . $PackagePath . \
+ "). Please place your packages!") false;
+}
+
+: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={
+ :set ($File->"package-architecture") "mipsbe";
+ }
+ :if ([ $DownloadPackage ($File->"package-name") $InstalledVersion \
+ ($File->"package-architecture") $PackagePath ] = true) do={
+ :set Updated true;
+ /file/remove $Package;
+ }
+}
+
+:if ([ :len [ /system/logging/find where topics~"error" !(topics~"!error") \
+ !(topics~"!caps") action=memory !disabled !invalid ] ] < 1) do={
+ $LogPrintExit2 warning $0 ("Looks like error messages for 'caps' are not sent to memory. " . \
+ "Probably can not download packages automatically.") false;
+} else={
+ :if ($Updated = false && [ /system/resource/get uptime ] < 2m) do={
+ $LogPrintExit2 info $0 ("No packages downloaded, yet. Delaying for logs.") false;
+ :delay 2m;
+ }
+}
+
+:foreach Log in=[ /log/find where topics=({"caps"; "error"}) \
+ message~("upgrade status: failed, failed to download file '.*-" . $InstalledVersion . \
+ "-.*\\.npk', no such file") ] do={
+ :local Message [ /log/get $Log message ];
+ :local Package [ :pick $Message \
+ ([ :find $Message "'" ] + 1) \
+ [ :find $Message ("-" . $InstalledVersion . "-") ] ];
+ :local Arch [ :pick $Message \
+ ([ :find $Message ("-" . $InstalledVersion . "-") ] + 2 + [ :len $InstalledVersion ]) \
+ [ :find $Message ".npk" ] ];
+ :if ([ $DownloadPackage $Package $InstalledVersion $Arch $PackagePath ] = true) do={
+ :set Updated true;
+ }
+}
+
+:if ($Updated = true) do={
+ :if ([ :len [ /system/script/find where name="capsman-rolling-upgrade" ] ] > 0) do={
+ /system/script/run capsman-rolling-upgrade;
+ } else={
+ /caps-man/remote-cap/upgrade [ find where version!=$InstalledVersion ];
+ }
+}
diff --git a/capsman-rolling-upgrade b/capsman-rolling-upgrade
index 1f4a51c..2da00ca 100644
--- a/capsman-rolling-upgrade
+++ b/capsman-rolling-upgrade
@@ -1,36 +1,3 @@
#!rsc by RouterOS
-# RouterOS script: capsman-rolling-upgrade
-# Copyright (c) 2018-2023 Christian Hesse <mail@eworm.de>
-# Michael Gisbers <michael@gisbers.de>
-# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md
#
-# upgrade CAPs one after another
-# https://git.eworm.de/cgit/routeros-scripts/about/doc/capsman-rolling-upgrade.md
-
-:local 0 "capsman-rolling-upgrade";
-:global GlobalFunctionsReady;
-:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
-
-:global LogPrintExit2;
-:global ScriptLock;
-
-$ScriptLock $0;
-
-:local InstalledVersion [ /system/package/update/get installed-version ];
-
-:local RemoteCapCount [ :len [ /caps-man/remote-cap/find ] ];
-:if ($RemoteCapCount > 0) do={
- :local Delay (600 / $RemoteCapCount);
- :if ($Delay > 120) do={ :set Delay 120; }
- :foreach RemoteCap in=[ /caps-man/remote-cap/find where version!=$InstalledVersion ] do={
- :local RemoteCapVal [ /caps-man/remote-cap/get $RemoteCap ];
- :if ([ :len $RemoteCapVal ] > 1) do={
- $LogPrintExit2 info $0 ("Starting upgrade for " . $RemoteCapVal->"name" . \
- " (" . $RemoteCapVal->"identity" . ")...") false;
- /caps-man/remote-cap/upgrade $RemoteCap;
- } else={
- $LogPrintExit2 warning $0 ("Remote CAP vanished, skipping upgrade.") false;
- }
- :delay ($Delay . "s");
- }
-}
+# dummy for migration
diff --git a/capsman-rolling-upgrade.rsc b/capsman-rolling-upgrade.rsc
new file mode 100644
index 0000000..1f4a51c
--- /dev/null
+++ b/capsman-rolling-upgrade.rsc
@@ -0,0 +1,36 @@
+#!rsc by RouterOS
+# RouterOS script: capsman-rolling-upgrade
+# Copyright (c) 2018-2023 Christian Hesse <mail@eworm.de>
+# Michael Gisbers <michael@gisbers.de>
+# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md
+#
+# upgrade CAPs one after another
+# https://git.eworm.de/cgit/routeros-scripts/about/doc/capsman-rolling-upgrade.md
+
+:local 0 "capsman-rolling-upgrade";
+:global GlobalFunctionsReady;
+:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
+
+:global LogPrintExit2;
+:global ScriptLock;
+
+$ScriptLock $0;
+
+:local InstalledVersion [ /system/package/update/get installed-version ];
+
+:local RemoteCapCount [ :len [ /caps-man/remote-cap/find ] ];
+:if ($RemoteCapCount > 0) do={
+ :local Delay (600 / $RemoteCapCount);
+ :if ($Delay > 120) do={ :set Delay 120; }
+ :foreach RemoteCap in=[ /caps-man/remote-cap/find where version!=$InstalledVersion ] do={
+ :local RemoteCapVal [ /caps-man/remote-cap/get $RemoteCap ];
+ :if ([ :len $RemoteCapVal ] > 1) do={
+ $LogPrintExit2 info $0 ("Starting upgrade for " . $RemoteCapVal->"name" . \
+ " (" . $RemoteCapVal->"identity" . ")...") false;
+ /caps-man/remote-cap/upgrade $RemoteCap;
+ } else={
+ $LogPrintExit2 warning $0 ("Remote CAP vanished, skipping upgrade.") false;
+ }
+ :delay ($Delay . "s");
+ }
+}
diff --git a/certificate-renew-issued b/certificate-renew-issued
index c297b15..2da00ca 100644
--- a/certificate-renew-issued
+++ b/certificate-renew-issued
@@ -1,38 +1,3 @@
#!rsc by RouterOS
-# RouterOS script: certificate-renew-issued
-# Copyright (c) 2019-2023 Christian Hesse <mail@eworm.de>
-# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md
#
-# renew locally issued certificates
-# https://git.eworm.de/cgit/routeros-scripts/about/doc/certificate-renew-issued.md
-
-:local 0 "certificate-renew-issued";
-:global GlobalFunctionsReady;
-:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
-
-:global CertIssuedExportPass;
-
-:global LogPrintExit2;
-:global MkDir;
-
-:foreach Cert in=[ /certificate/find where issued expires-after<3w ] do={
- :local CertVal [ /certificate/get $Cert ];
- /certificate/issued-revoke $Cert;
- /certificate/set name=($CertVal->"name" . "-revoked-" . [ /system/clock/get date ]) $Cert;
- /certificate/add name=($CertVal->"name") common-name=($CertVal->"common-name") \
- key-usage=($CertVal->"key-usage") subject-alt-name=($CertVal->"subject-alt-name");
- /certificate/sign ($CertVal->"name") ca=($CertVal->"ca");
- :if ([ :typeof ($CertIssuedExportPass->($CertVal->"common-name")) ] = "str") do={
- :if ([ $MkDir "cert-issued" ] = true) do={
- /certificate/export-certificate ($CertVal->"name") type=pkcs12 \
- file-name=("cert-issued/" . $CertVal->"common-name") \
- export-passphrase=($CertIssuedExportPass->($CertVal->"common-name"));
- $LogPrintExit2 info $0 ("Issued a new certificate for \"" . $CertVal->"common-name" . \
- "\", exported to \"cert-issued/" . $CertVal->"common-name" . ".p12\".") false;
- } else={
- $LogPrintExit2 warning $0 ("Failed creating directory, not exporting certificate.") false;
- }
- } else={
- $LogPrintExit2 info $0 ("Issued a new certificate for \"" . $CertVal->"common-name" . "\".") false;
- }
-}
+# dummy for migration
diff --git a/certificate-renew-issued.rsc b/certificate-renew-issued.rsc
new file mode 100644
index 0000000..c297b15
--- /dev/null
+++ b/certificate-renew-issued.rsc
@@ -0,0 +1,38 @@
+#!rsc by RouterOS
+# RouterOS script: certificate-renew-issued
+# Copyright (c) 2019-2023 Christian Hesse <mail@eworm.de>
+# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md
+#
+# renew locally issued certificates
+# https://git.eworm.de/cgit/routeros-scripts/about/doc/certificate-renew-issued.md
+
+:local 0 "certificate-renew-issued";
+:global GlobalFunctionsReady;
+:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
+
+:global CertIssuedExportPass;
+
+:global LogPrintExit2;
+:global MkDir;
+
+:foreach Cert in=[ /certificate/find where issued expires-after<3w ] do={
+ :local CertVal [ /certificate/get $Cert ];
+ /certificate/issued-revoke $Cert;
+ /certificate/set name=($CertVal->"name" . "-revoked-" . [ /system/clock/get date ]) $Cert;
+ /certificate/add name=($CertVal->"name") common-name=($CertVal->"common-name") \
+ key-usage=($CertVal->"key-usage") subject-alt-name=($CertVal->"subject-alt-name");
+ /certificate/sign ($CertVal->"name") ca=($CertVal->"ca");
+ :if ([ :typeof ($CertIssuedExportPass->($CertVal->"common-name")) ] = "str") do={
+ :if ([ $MkDir "cert-issued" ] = true) do={
+ /certificate/export-certificate ($CertVal->"name") type=pkcs12 \
+ file-name=("cert-issued/" . $CertVal->"common-name") \
+ export-passphrase=($CertIssuedExportPass->($CertVal->"common-name"));
+ $LogPrintExit2 info $0 ("Issued a new certificate for \"" . $CertVal->"common-name" . \
+ "\", exported to \"cert-issued/" . $CertVal->"common-name" . ".p12\".") false;
+ } else={
+ $LogPrintExit2 warning $0 ("Failed creating directory, not exporting certificate.") false;
+ }
+ } else={
+ $LogPrintExit2 info $0 ("Issued a new certificate for \"" . $CertVal->"common-name" . "\".") false;
+ }
+}
diff --git a/check-certificates b/check-certificates
index 8a06f8b..2da00ca 100644
--- a/check-certificates
+++ b/check-certificates
@@ -1,138 +1,3 @@
#!rsc by RouterOS
-# RouterOS script: check-certificates
-# Copyright (c) 2013-2023 Christian Hesse <mail@eworm.de>
-# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md
#
-# check for certificate validity
-# https://git.eworm.de/cgit/routeros-scripts/about/doc/check-certificates.md
-
-:local 0 "check-certificates";
-:global GlobalFunctionsReady;
-:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
-
-:global CertRenewPass;
-:global CertRenewTime;
-:global CertRenewUrl;
-:global CertWarnTime;
-:global Identity;
-
-:global CertificateAvailable
-:global CertificateNameByCN;
-:global IfThenElse;
-:global LogPrintExit2;
-:global ParseKeyValueStore;
-:global SendNotification2;
-:global SymbolForNotification;
-:global UrlEncode;
-:global WaitForFile;
-:global WaitFullyConnected;
-
-:local FormatExpire do={
- :global CharacterReplace;
- :return [ $CharacterReplace [ $CharacterReplace [ :tostr $1 ] "w" "w " ] "d" "d " ];
-}
-
-$WaitFullyConnected;
-
-:foreach Cert in=[ /certificate/find where !revoked !ca !scep-url expires-after<$CertRenewTime ] do={
- :local CertVal [ /certificate/get $Cert ];
-
- :do {
- :if ([ :len $CertRenewUrl ] = 0) do={
- $LogPrintExit2 info $0 ("No CertRenewUrl given.") true;
- }
- $LogPrintExit2 info $0 ("Attempting to renew certificate " . ($CertVal->"name") . ".") false;
-
- :foreach Type in={ ".pem"; ".p12" } do={
- :local CertFileName ([ $UrlEncode ($CertVal->"common-name") ] . $Type);
- :do {
- /tool/fetch check-certificate=yes-without-crl \
- ($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;
- }
- }
- /file/remove [ find where name=$CertFileName ];
-
- :if ($DecryptionFailed = true) do={
- $LogPrintExit2 warning $0 ("Decryption failed for certificate file " . $CertFileName) false;
- }
-
- :foreach CertInChain in=[ /certificate/find where name~("^" . $CertFileName . "_[0-9]+\$") common-name!=($CertVal->"common-name") ] do={
- $CertificateNameByCN [ /certificate/get $CertInChain common-name ];
- }
- } on-error={
- $LogPrintExit2 debug $0 ("Could not download certificate file " . $CertFileName) false;
- }
- }
-
- :local CertNew [ /certificate/find where common-name=($CertVal->"common-name") fingerprint!=[ :tostr ($CertVal->"fingerprint") ] expires-after>$CertRenewTime ];
- :local CertNewVal [ /certificate/get $CertNew ];
-
- :if ([ $CertificateAvailable ([ $ParseKeyValueStore ($CertNewVal->"issuer") ]->"CN") ] = false) do={
- $LogPrintExit2 warning $0 ("The certificate chain is not available!") false;
- }
-
- :if ($Cert != $CertNew) do={
- $LogPrintExit2 debug $0 ("Certificate '" . $CertVal->"name" . "' was not updated, but replaced.") false;
-
- :if (($CertVal->"private-key") = true && ($CertVal->"private-key") != ($CertNewVal->"private-key")) do={
- /certificate/remove $CertNew;
- $LogPrintExit2 warning $0 ("Old certificate '" . ($CertVal->"name") . "' has a private key, new certificate does not. Aborting renew.") true;
- }
-
- /ip/service/set certificate=($CertNewVal->"name") [ find where certificate=($CertVal->"name") ];
-
- /ip/ipsec/identity/set certificate=($CertNewVal->"name") [ find where certificate=($CertVal->"name") ];
- /ip/ipsec/identity/set remote-certificate=($CertNewVal->"name") [ find where remote-certificate=($CertVal->"name") ];
-
- /ip/hotspot/profile/set ssl-certificate=($CertNewVal->"name") [ find where ssl-certificate=($CertVal->"name") ];
-
- /certificate/remove $Cert;
- /certificate/set $CertNew name=($CertVal->"name");
- }
-
- $SendNotification2 ({ origin=$0; \
- subject=([ $SymbolForNotification "lock-with-ink-pen" ] . "Certificate renewed"); \
- message=("A certificate on " . $Identity . " has been renewed.\n\n" . \
- "Name: " . ($CertVal->"name") . "\n" . \
- "CommonName: " . ($CertNewVal->"common-name") . "\n" . \
- "Private key: " . [ $IfThenElse (($CertNewVal->"private-key") = true) "available" "missing" ] . "\n" . \
- "Fingerprint: " . ($CertNewVal->"fingerprint") . "\n" . \
- "Issuer: " . ([ $ParseKeyValueStore ($CertNewVal->"issuer") ]->"CN") . "\n" . \
- "Validity: " . ($CertNewVal->"invalid-before") . " to " . ($CertNewVal->"invalid-after") . "\n" . \
- "Expires in: " . [ $FormatExpire ($CertNewVal->"expires-after") ]); silent=true });
- $LogPrintExit2 info $0 ("The certificate " . ($CertVal->"name") . " has been renewed.") false;
- } on-error={
- $LogPrintExit2 debug $0 ("Could not renew certificate " . ($CertVal->"name") . ".") false;
- }
-}
-
-:foreach Cert in=[ /certificate/find where !revoked !scep-url !(expires-after=[]) \
- expires-after<$CertWarnTime !(fingerprint=[]) ] do={
- :local CertVal [ /certificate/get $Cert ];
-
- :if ([ :len [ /certificate/scep-server/find where ca-cert=($CertVal->"ca") ] ] > 0) do={
- $LogPrintExit2 debug $0 ("Certificate \"" . ($CertVal->"name") . "\" is handled by SCEP, skipping.") false;
- } else={
- :local State [ $IfThenElse (($CertVal->"expired") = true) "expired" "is about to expire" ];
-
- $SendNotification2 ({ origin=$0; \
- subject=([ $SymbolForNotification "warning-sign" ] . "Certificate warning!"); \
- message=("A certificate on " . $Identity . " " . $State . ".\n\n" . \
- "Name: " . ($CertVal->"name") . "\n" . \
- "CommonName: " . ($CertVal->"common-name") . "\n" . \
- "Private key: " . [ $IfThenElse (($CertVal->"private-key") = true) "available" "missing" ] . "\n" . \
- "Fingerprint: " . ($CertVal->"fingerprint") . "\n" . \
- "Issuer: " . ($CertVal->"ca") . ([ $ParseKeyValueStore ($CertVal->"issuer") ]->"CN") . "\n" . \
- "Validity: " . ($CertVal->"invalid-before") . " to " . ($CertVal->"invalid-after") . "\n" . \
- "Expires in: " . [ $IfThenElse (($CertVal->"expired") = true) "expired" [ $FormatExpire ($CertVal->"expires-after") ] ]) });
- $LogPrintExit2 info $0 ("The certificate " . ($CertVal->"name") . " " . $State . \
- ", it is invalid after " . ($CertVal->"invalid-after") . ".") false;
- }
-}
+# dummy for migration
diff --git a/check-certificates.rsc b/check-certificates.rsc
new file mode 100644
index 0000000..8a06f8b
--- /dev/null
+++ b/check-certificates.rsc
@@ -0,0 +1,138 @@
+#!rsc by RouterOS
+# RouterOS script: check-certificates
+# Copyright (c) 2013-2023 Christian Hesse <mail@eworm.de>
+# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md
+#
+# check for certificate validity
+# https://git.eworm.de/cgit/routeros-scripts/about/doc/check-certificates.md
+
+:local 0 "check-certificates";
+:global GlobalFunctionsReady;
+:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
+
+:global CertRenewPass;
+:global CertRenewTime;
+:global CertRenewUrl;
+:global CertWarnTime;
+:global Identity;
+
+:global CertificateAvailable
+:global CertificateNameByCN;
+:global IfThenElse;
+:global LogPrintExit2;
+:global ParseKeyValueStore;
+:global SendNotification2;
+:global SymbolForNotification;
+:global UrlEncode;
+:global WaitForFile;
+:global WaitFullyConnected;
+
+:local FormatExpire do={
+ :global CharacterReplace;
+ :return [ $CharacterReplace [ $CharacterReplace [ :tostr $1 ] "w" "w " ] "d" "d " ];
+}
+
+$WaitFullyConnected;
+
+:foreach Cert in=[ /certificate/find where !revoked !ca !scep-url expires-after<$CertRenewTime ] do={
+ :local CertVal [ /certificate/get $Cert ];
+
+ :do {
+ :if ([ :len $CertRenewUrl ] = 0) do={
+ $LogPrintExit2 info $0 ("No CertRenewUrl given.") true;
+ }
+ $LogPrintExit2 info $0 ("Attempting to renew certificate " . ($CertVal->"name") . ".") false;
+
+ :foreach Type in={ ".pem"; ".p12" } do={
+ :local CertFileName ([ $UrlEncode ($CertVal->"common-name") ] . $Type);
+ :do {
+ /tool/fetch check-certificate=yes-without-crl \
+ ($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;
+ }
+ }
+ /file/remove [ find where name=$CertFileName ];
+
+ :if ($DecryptionFailed = true) do={
+ $LogPrintExit2 warning $0 ("Decryption failed for certificate file " . $CertFileName) false;
+ }
+
+ :foreach CertInChain in=[ /certificate/find where name~("^" . $CertFileName . "_[0-9]+\$") common-name!=($CertVal->"common-name") ] do={
+ $CertificateNameByCN [ /certificate/get $CertInChain common-name ];
+ }
+ } on-error={
+ $LogPrintExit2 debug $0 ("Could not download certificate file " . $CertFileName) false;
+ }
+ }
+
+ :local CertNew [ /certificate/find where common-name=($CertVal->"common-name") fingerprint!=[ :tostr ($CertVal->"fingerprint") ] expires-after>$CertRenewTime ];
+ :local CertNewVal [ /certificate/get $CertNew ];
+
+ :if ([ $CertificateAvailable ([ $ParseKeyValueStore ($CertNewVal->"issuer") ]->"CN") ] = false) do={
+ $LogPrintExit2 warning $0 ("The certificate chain is not available!") false;
+ }
+
+ :if ($Cert != $CertNew) do={
+ $LogPrintExit2 debug $0 ("Certificate '" . $CertVal->"name" . "' was not updated, but replaced.") false;
+
+ :if (($CertVal->"private-key") = true && ($CertVal->"private-key") != ($CertNewVal->"private-key")) do={
+ /certificate/remove $CertNew;
+ $LogPrintExit2 warning $0 ("Old certificate '" . ($CertVal->"name") . "' has a private key, new certificate does not. Aborting renew.") true;
+ }
+
+ /ip/service/set certificate=($CertNewVal->"name") [ find where certificate=($CertVal->"name") ];
+
+ /ip/ipsec/identity/set certificate=($CertNewVal->"name") [ find where certificate=($CertVal->"name") ];
+ /ip/ipsec/identity/set remote-certificate=($CertNewVal->"name") [ find where remote-certificate=($CertVal->"name") ];
+
+ /ip/hotspot/profile/set ssl-certificate=($CertNewVal->"name") [ find where ssl-certificate=($CertVal->"name") ];
+
+ /certificate/remove $Cert;
+ /certificate/set $CertNew name=($CertVal->"name");
+ }
+
+ $SendNotification2 ({ origin=$0; \
+ subject=([ $SymbolForNotification "lock-with-ink-pen" ] . "Certificate renewed"); \
+ message=("A certificate on " . $Identity . " has been renewed.\n\n" . \
+ "Name: " . ($CertVal->"name") . "\n" . \
+ "CommonName: " . ($CertNewVal->"common-name") . "\n" . \
+ "Private key: " . [ $IfThenElse (($CertNewVal->"private-key") = true) "available" "missing" ] . "\n" . \
+ "Fingerprint: " . ($CertNewVal->"fingerprint") . "\n" . \
+ "Issuer: " . ([ $ParseKeyValueStore ($CertNewVal->"issuer") ]->"CN") . "\n" . \
+ "Validity: " . ($CertNewVal->"invalid-before") . " to " . ($CertNewVal->"invalid-after") . "\n" . \
+ "Expires in: " . [ $FormatExpire ($CertNewVal->"expires-after") ]); silent=true });
+ $LogPrintExit2 info $0 ("The certificate " . ($CertVal->"name") . " has been renewed.") false;
+ } on-error={
+ $LogPrintExit2 debug $0 ("Could not renew certificate " . ($CertVal->"name") . ".") false;
+ }
+}
+
+:foreach Cert in=[ /certificate/find where !revoked !scep-url !(expires-after=[]) \
+ expires-after<$CertWarnTime !(fingerprint=[]) ] do={
+ :local CertVal [ /certificate/get $Cert ];
+
+ :if ([ :len [ /certificate/scep-server/find where ca-cert=($CertVal->"ca") ] ] > 0) do={
+ $LogPrintExit2 debug $0 ("Certificate \"" . ($CertVal->"name") . "\" is handled by SCEP, skipping.") false;
+ } else={
+ :local State [ $IfThenElse (($CertVal->"expired") = true) "expired" "is about to expire" ];
+
+ $SendNotification2 ({ origin=$0; \
+ subject=([ $SymbolForNotification "warning-sign" ] . "Certificate warning!"); \
+ message=("A certificate on " . $Identity . " " . $State . ".\n\n" . \
+ "Name: " . ($CertVal->"name") . "\n" . \
+ "CommonName: " . ($CertVal->"common-name") . "\n" . \
+ "Private key: " . [ $IfThenElse (($CertVal->"private-key") = true) "available" "missing" ] . "\n" . \
+ "Fingerprint: " . ($CertVal->"fingerprint") . "\n" . \
+ "Issuer: " . ($CertVal->"ca") . ([ $ParseKeyValueStore ($CertVal->"issuer") ]->"CN") . "\n" . \
+ "Validity: " . ($CertVal->"invalid-before") . " to " . ($CertVal->"invalid-after") . "\n" . \
+ "Expires in: " . [ $IfThenElse (($CertVal->"expired") = true) "expired" [ $FormatExpire ($CertVal->"expires-after") ] ]) });
+ $LogPrintExit2 info $0 ("The certificate " . ($CertVal->"name") . " " . $State . \
+ ", it is invalid after " . ($CertVal->"invalid-after") . ".") false;
+ }
+}
diff --git a/check-health b/check-health
index e208bac..2da00ca 100644
--- a/check-health
+++ b/check-health
@@ -1,167 +1,3 @@
#!rsc by RouterOS
-# RouterOS script: check-health
-# Copyright (c) 2019-2023 Christian Hesse <mail@eworm.de>
-# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md
#
-# check for RouterOS health state
-# https://git.eworm.de/cgit/routeros-scripts/about/doc/check-health.md
-
-:local 0 "check-health";
-:global GlobalFunctionsReady;
-:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
-
-:global CheckHealthCPUUtilization;
-:global CheckHealthCPUUtilizationNotified;
-:global CheckHealthFreeRAMNotified;
-:global CheckHealthLast;
-:global CheckHealthTemperature;
-:global CheckHealthTemperatureDeviation;
-:global CheckHealthTemperatureNotified;
-:global CheckHealthVoltageLow;
-:global CheckHealthVoltagePercent;
-:global Identity;
-
-:global IfThenElse;
-:global LogPrintExit2;
-:global ScriptLock;
-:global SendNotification2;
-:global SymbolForNotification;
-
-:local TempToNum do={
- :global CharacterReplace;
- :local T [ :toarray [ $CharacterReplace $1 "." "," ] ];
- :return ($T->0 * 10 + $T->1);
-}
-
-$ScriptLock $0;
-
-:local Resource [ /system/resource/get ];
-
-:set CheckHealthCPUUtilization (($CheckHealthCPUUtilization * 4 + ($Resource->"cpu-load") * 10) / 5);
-:if ($CheckHealthCPUUtilization > 750 && $CheckHealthCPUUtilizationNotified != true) do={
- $SendNotification2 ({ origin=$0; \
- subject=([ $SymbolForNotification "abacus,chart-increasing" ] . "Health warning: CPU utilization"); \
- message=("The average CPU utilization on " . $Identity . " is at " . ($CheckHealthCPUUtilization / 10) . "%!") });
- :set CheckHealthCPUUtilizationNotified true;
-}
-:if ($CheckHealthCPUUtilization < 650 && $CheckHealthCPUUtilizationNotified = true) do={
- $SendNotification2 ({ origin=$0; \
- subject=([ $SymbolForNotification "abacus,chart-decreasing" ] . "Health recovery: CPU utilization"); \
- message=("The average CPU utilization on " . $Identity . " decreased to " . ($CheckHealthCPUUtilization / 10) . "%.") });
- :set CheckHealthCPUUtilizationNotified false;
-}
-
-:local CheckHealthFreeRAM ($Resource->"free-memory" * 100 / $Resource->"total-memory");
-:if ($CheckHealthFreeRAM < 20 && $CheckHealthFreeRAMNotified != true) do={
- $SendNotification2 ({ origin=$0; \
- subject=([ $SymbolForNotification "card-file-box,chart-decreasing" ] . "Health warning: free RAM"); \
- message=("The available free RAM on " . $Identity . " is at " . $CheckHealthFreeRAM . "% (" . \
- ($Resource->"free-memory" / 1024 / 1024) . "MiB)!") });
- :set CheckHealthFreeRAMNotified true;
-}
-:if ($CheckHealthFreeRAM > 30 && $CheckHealthFreeRAMNotified = true) do={
- $SendNotification2 ({ origin=$0; \
- subject=([ $SymbolForNotification "card-file-box,chart-increasing" ] . "Health recovery: free RAM"); \
- message=("The available free RAM on " . $Identity . " increased to " . $CheckHealthFreeRAM . "% (" . \
- ($Resource->"free-memory" / 1024 / 1024) . "MiB).") });
- :set CheckHealthFreeRAMNotified false;
-}
-
-:if ([ :len [ /system/health/find ] ] = 0) do={
- $LogPrintExit2 debug $0 ("Your device does not provide any health values.") true;
-}
-
-:if ([ :typeof $CheckHealthLast ] != "array") do={
- :set CheckHealthLast ({});
-}
-:if ([ :typeof $CheckHealthTemperatureNotified ] != "array") do={
- :set CheckHealthTemperatureNotified ({});
-}
-
-
-:foreach Voltage in=[ /system/health/find where type="V" ] do={
- :local Name [ /system/health/get $Voltage name ];
- :local Value [ /system/health/get $Voltage value ];
-
- :if ([ :typeof ($CheckHealthLast->$Name) ] != "nothing") do={
- :local NumCurr [ $TempToNum $Value ];
- :local NumLast [ $TempToNum ($CheckHealthLast->$Name) ];
-
- :if ($NumLast * (100 + $CheckHealthVoltagePercent) < $NumCurr * 100 || \
- $NumLast * 100 > $NumCurr * (100 + $CheckHealthVoltagePercent)) do={
- $SendNotification2 ({ origin=$0; \
- subject=([ $SymbolForNotification ("high-voltage-sign,chart-" . [ $IfThenElse ($NumLast < \
- $NumCurr) "in" "de" ] . "creasing") ] . "Health warning: " . $Name); \
- message=("The " . $Name . " on " . $Identity . " jumped more than " . $CheckHealthVoltagePercent . "%.\n\n" . \
- "old value: " . ($CheckHealthLast->$Name) . " V\n" . \
- "new value: " . $Value . " V") });
- } else={
- :if ($NumCurr <= $CheckHealthVoltageLow && $NumLast > $CheckHealthVoltageLow) do={
- $SendNotification2 ({ origin=$0; \
- subject=([ $SymbolForNotification "high-voltage-sign,chart-decreasing" ] . "Health warning: Low " . $Name); \
- message=("The " . $Name . " on " . $Identity . " dropped to " . $Value . " V below hard limit.") });
- }
- :if ($NumCurr > $CheckHealthVoltageLow && $NumLast <= $CheckHealthVoltageLow) do={
- $SendNotification2 ({ origin=$0; \
- subject=([ $SymbolForNotification "high-voltage-sign,chart-increasing" ] . "Health recovery: Low " . $Name); \
- message=("The " . $Name . " on " . $Identity . " recovered to " . $Value . " V above hard limit.") });
- }
- }
- }
- :set ($CheckHealthLast->$Name) $Value;
-}
-
-:foreach PSU in=[ /system/health/find where name~"^psu.*-state\$" ] do={
- :local Name [ /system/health/get $PSU name ];
- :local Value [ /system/health/get $PSU value ];
-
- :if ([ :typeof ($CheckHealthLast->$Name) ] != "nothing") do={
- :if ($CheckHealthLast->$Name = "ok" && \
- $Value != "ok") do={
- $SendNotification2 ({ origin=$0; \
- subject=([ $SymbolForNotification "cross-mark" ] . "Health warning: " . $Name); \
- message=("The power supply unit '" . $Name . "' on " . $Identity . " failed!") });
- }
- :if ($CheckHealthLast->$Name != "ok" && \
- $Value = "ok") do={
- $SendNotification2 ({ origin=$0; \
- subject=([ $SymbolForNotification "white-heavy-check-mark" ] . "Health recovery: " . $Name); \
- message=("The power supply unit '" . $Name . "' on " . $Identity . " recovered!") });
- }
- }
- :set ($CheckHealthLast->$Name) $Value;
-}
-
-:foreach Temperature in=[ /system/health/find where type="C" ] do={
- :local Name [ /system/health/get $Temperature name ];
- :local Value [ /system/health/get $Temperature value ];
-
- :if ([ :typeof ($CheckHealthLast->$Name) ] != "nothing") do={
- :if ([ :typeof ($CheckHealthTemperature->$Name) ] != "num" ) do={
- $LogPrintExit2 info $0 ("No threshold given for " . $Name . ", assuming 50C.") false;
- :set ($CheckHealthTemperature->$Name) 50;
- }
- :local Validate [ /system/health/get [ find where name=$Name ] value ];
- :while ($Value != $Validate) do={
- :set Value $Validate;
- :set Validate [ /system/health/get [ find where name=$Name ] value ];
- }
- :if ($Value > $CheckHealthTemperature->$Name && \
- $CheckHealthTemperatureNotified->$Name != true) do={
- $SendNotification2 ({ origin=$0; \
- subject=([ $SymbolForNotification "fire" ] . "Health warning: " . $Name); \
- message=("The " . $Name . " on " . $Identity . " is above threshold: " . \
- $Value . "\C2\B0" . "C") });
- :set ($CheckHealthTemperatureNotified->$Name) true;
- }
- :if ($Value <= ($CheckHealthTemperature->$Name - $CheckHealthTemperatureDeviation) && \
- $CheckHealthTemperatureNotified->$Name = true) do={
- $SendNotification2 ({ origin=$0; \
- subject=([ $SymbolForNotification "white-heavy-check-mark" ] . "Health recovery: " . $Name); \
- message=("The " . $Name . " on " . $Identity . " dropped below threshold: " . \
- $Value . "\C2\B0" . "C") });
- :set ($CheckHealthTemperatureNotified->$Name) false;
- }
- }
- :set ($CheckHealthLast->$Name) $Value;
-}
+# dummy for migration
diff --git a/check-health.rsc b/check-health.rsc
new file mode 100644
index 0000000..e208bac
--- /dev/null
+++ b/check-health.rsc
@@ -0,0 +1,167 @@
+#!rsc by RouterOS
+# RouterOS script: check-health
+# Copyright (c) 2019-2023 Christian Hesse <mail@eworm.de>
+# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md
+#
+# check for RouterOS health state
+# https://git.eworm.de/cgit/routeros-scripts/about/doc/check-health.md
+
+:local 0 "check-health";
+:global GlobalFunctionsReady;
+:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
+
+:global CheckHealthCPUUtilization;
+:global CheckHealthCPUUtilizationNotified;
+:global CheckHealthFreeRAMNotified;
+:global CheckHealthLast;
+:global CheckHealthTemperature;
+:global CheckHealthTemperatureDeviation;
+:global CheckHealthTemperatureNotified;
+:global CheckHealthVoltageLow;
+:global CheckHealthVoltagePercent;
+:global Identity;
+
+:global IfThenElse;
+:global LogPrintExit2;
+:global ScriptLock;
+:global SendNotification2;
+:global SymbolForNotification;
+
+:local TempToNum do={
+ :global CharacterReplace;
+ :local T [ :toarray [ $CharacterReplace $1 "." "," ] ];
+ :return ($T->0 * 10 + $T->1);
+}
+
+$ScriptLock $0;
+
+:local Resource [ /system/resource/get ];
+
+:set CheckHealthCPUUtilization (($CheckHealthCPUUtilization * 4 + ($Resource->"cpu-load") * 10) / 5);
+:if ($CheckHealthCPUUtilization > 750 && $CheckHealthCPUUtilizationNotified != true) do={
+ $SendNotification2 ({ origin=$0; \
+ subject=([ $SymbolForNotification "abacus,chart-increasing" ] . "Health warning: CPU utilization"); \
+ message=("The average CPU utilization on " . $Identity . " is at " . ($CheckHealthCPUUtilization / 10) . "%!") });
+ :set CheckHealthCPUUtilizationNotified true;
+}
+:if ($CheckHealthCPUUtilization < 650 && $CheckHealthCPUUtilizationNotified = true) do={
+ $SendNotification2 ({ origin=$0; \
+ subject=([ $SymbolForNotification "abacus,chart-decreasing" ] . "Health recovery: CPU utilization"); \
+ message=("The average CPU utilization on " . $Identity . " decreased to " . ($CheckHealthCPUUtilization / 10) . "%.") });
+ :set CheckHealthCPUUtilizationNotified false;
+}
+
+:local CheckHealthFreeRAM ($Resource->"free-memory" * 100 / $Resource->"total-memory");
+:if ($CheckHealthFreeRAM < 20 && $CheckHealthFreeRAMNotified != true) do={
+ $SendNotification2 ({ origin=$0; \
+ subject=([ $SymbolForNotification "card-file-box,chart-decreasing" ] . "Health warning: free RAM"); \
+ message=("The available free RAM on " . $Identity . " is at " . $CheckHealthFreeRAM . "% (" . \
+ ($Resource->"free-memory" / 1024 / 1024) . "MiB)!") });
+ :set CheckHealthFreeRAMNotified true;
+}
+:if ($CheckHealthFreeRAM > 30 && $CheckHealthFreeRAMNotified = true) do={
+ $SendNotification2 ({ origin=$0; \
+ subject=([ $SymbolForNotification "card-file-box,chart-increasing" ] . "Health recovery: free RAM"); \
+ message=("The available free RAM on " . $Identity . " increased to " . $CheckHealthFreeRAM . "% (" . \
+ ($Resource->"free-memory" / 1024 / 1024) . "MiB).") });
+ :set CheckHealthFreeRAMNotified false;
+}
+
+:if ([ :len [ /system/health/find ] ] = 0) do={
+ $LogPrintExit2 debug $0 ("Your device does not provide any health values.") true;
+}
+
+:if ([ :typeof $CheckHealthLast ] != "array") do={
+ :set CheckHealthLast ({});
+}
+:if ([ :typeof $CheckHealthTemperatureNotified ] != "array") do={
+ :set CheckHealthTemperatureNotified ({});
+}
+
+
+:foreach Voltage in=[ /system/health/find where type="V" ] do={
+ :local Name [ /system/health/get $Voltage name ];
+ :local Value [ /system/health/get $Voltage value ];
+
+ :if ([ :typeof ($CheckHealthLast->$Name) ] != "nothing") do={
+ :local NumCurr [ $TempToNum $Value ];
+ :local NumLast [ $TempToNum ($CheckHealthLast->$Name) ];
+
+ :if ($NumLast * (100 + $CheckHealthVoltagePercent) < $NumCurr * 100 || \
+ $NumLast * 100 > $NumCurr * (100 + $CheckHealthVoltagePercent)) do={
+ $SendNotification2 ({ origin=$0; \
+ subject=([ $SymbolForNotification ("high-voltage-sign,chart-" . [ $IfThenElse ($NumLast < \
+ $NumCurr) "in" "de" ] . "creasing") ] . "Health warning: " . $Name); \
+ message=("The " . $Name . " on " . $Identity . " jumped more than " . $CheckHealthVoltagePercent . "%.\n\n" . \
+ "old value: " . ($CheckHealthLast->$Name) . " V\n" . \
+ "new value: " . $Value . " V") });
+ } else={
+ :if ($NumCurr <= $CheckHealthVoltageLow && $NumLast > $CheckHealthVoltageLow) do={
+ $SendNotification2 ({ origin=$0; \
+ subject=([ $SymbolForNotification "high-voltage-sign,chart-decreasing" ] . "Health warning: Low " . $Name); \
+ message=("The " . $Name . " on " . $Identity . " dropped to " . $Value . " V below hard limit.") });
+ }
+ :if ($NumCurr > $CheckHealthVoltageLow && $NumLast <= $CheckHealthVoltageLow) do={
+ $SendNotification2 ({ origin=$0; \
+ subject=([ $SymbolForNotification "high-voltage-sign,chart-increasing" ] . "Health recovery: Low " . $Name); \
+ message=("The " . $Name . " on " . $Identity . " recovered to " . $Value . " V above hard limit.") });
+ }
+ }
+ }
+ :set ($CheckHealthLast->$Name) $Value;
+}
+
+:foreach PSU in=[ /system/health/find where name~"^psu.*-state\$" ] do={
+ :local Name [ /system/health/get $PSU name ];
+ :local Value [ /system/health/get $PSU value ];
+
+ :if ([ :typeof ($CheckHealthLast->$Name) ] != "nothing") do={
+ :if ($CheckHealthLast->$Name = "ok" && \
+ $Value != "ok") do={
+ $SendNotification2 ({ origin=$0; \
+ subject=([ $SymbolForNotification "cross-mark" ] . "Health warning: " . $Name); \
+ message=("The power supply unit '" . $Name . "' on " . $Identity . " failed!") });
+ }
+ :if ($CheckHealthLast->$Name != "ok" && \
+ $Value = "ok") do={
+ $SendNotification2 ({ origin=$0; \
+ subject=([ $SymbolForNotification "white-heavy-check-mark" ] . "Health recovery: " . $Name); \
+ message=("The power supply unit '" . $Name . "' on " . $Identity . " recovered!") });
+ }
+ }
+ :set ($CheckHealthLast->$Name) $Value;
+}
+
+:foreach Temperature in=[ /system/health/find where type="C" ] do={
+ :local Name [ /system/health/get $Temperature name ];
+ :local Value [ /system/health/get $Temperature value ];
+
+ :if ([ :typeof ($CheckHealthLast->$Name) ] != "nothing") do={
+ :if ([ :typeof ($CheckHealthTemperature->$Name) ] != "num" ) do={
+ $LogPrintExit2 info $0 ("No threshold given for " . $Name . ", assuming 50C.") false;
+ :set ($CheckHealthTemperature->$Name) 50;
+ }
+ :local Validate [ /system/health/get [ find where name=$Name ] value ];
+ :while ($Value != $Validate) do={
+ :set Value $Validate;
+ :set Validate [ /system/health/get [ find where name=$Name ] value ];
+ }
+ :if ($Value > $CheckHealthTemperature->$Name && \
+ $CheckHealthTemperatureNotified->$Name != true) do={
+ $SendNotification2 ({ origin=$0; \
+ subject=([ $SymbolForNotification "fire" ] . "Health warning: " . $Name); \
+ message=("The " . $Name . " on " . $Identity . " is above threshold: " . \
+ $Value . "\C2\B0" . "C") });
+ :set ($CheckHealthTemperatureNotified->$Name) true;
+ }
+ :if ($Value <= ($CheckHealthTemperature->$Name - $CheckHealthTemperatureDeviation) && \
+ $CheckHealthTemperatureNotified->$Name = true) do={
+ $SendNotification2 ({ origin=$0; \
+ subject=([ $SymbolForNotification "white-heavy-check-mark" ] . "Health recovery: " . $Name); \
+ message=("The " . $Name . " on " . $Identity . " dropped below threshold: " . \
+ $Value . "\C2\B0" . "C") });
+ :set ($CheckHealthTemperatureNotified->$Name) false;
+ }
+ }
+ :set ($CheckHealthLast->$Name) $Value;
+}
diff --git a/check-lte-firmware-upgrade b/check-lte-firmware-upgrade
index 02e864b..2da00ca 100644
--- a/check-lte-firmware-upgrade
+++ b/check-lte-firmware-upgrade
@@ -1,82 +1,3 @@
#!rsc by RouterOS
-# RouterOS script: check-lte-firmware-upgrade
-# Copyright (c) 2018-2023 Christian Hesse <mail@eworm.de>
-# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md
#
-# check for LTE firmware upgrade, send notification
-# https://git.eworm.de/cgit/routeros-scripts/about/doc/check-lte-firmware-upgrade.md
-
-:local 0 "check-lte-firmware-upgrade";
-:global GlobalFunctionsReady;
-:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
-
-:global SentLteFirmwareUpgradeNotification;
-
-:if ([ :typeof $SentLteFirmwareUpgradeNotification ] != "array") do={
- :global SentLteFirmwareUpgradeNotification ({});
-}
-
-:local CheckInterface do={
- :local Interface $1;
-
- :global Identity;
- :global SentLteFirmwareUpgradeNotification;
-
- :global CharacterReplace;
- :global LogPrintExit2;
- :global ScriptFromTerminal;
- :global SendNotification2;
- :global SymbolForNotification;
-
- :local IntName [ /interface/lte/get $Interface name ];
- :local Firmware;
- :local Info;
- :do {
- :set Firmware [ /interface/lte/firmware-upgrade $Interface once as-value ];
- :set Info [ /interface/lte/monitor $Interface once as-value ];
- } on-error={
- $LogPrintExit2 debug $0 ("Could not get latest LTE firmware version for interface " . \
- $IntName . ".") false;
- :return false;
- }
-
- :if (($Firmware->"installed") = ($Firmware->"latest")) do={
- :if ([ $ScriptFromTerminal $0 ] = true) do={
- $LogPrintExit2 info $0 ("No firmware upgrade available for LTE interface " . $IntName . ".") false;
- }
- :return true;
- }
-
- :if ([ $ScriptFromTerminal $0 ] = true && \
- [ :len [ /system/script/find where name="unattended-lte-firmware-upgrade" ] ] > 0) do={
- :put ("Do you want to start unattended lte firmware upgrade for interface " . $IntName . "? [y/N]");
- :if (([ /terminal/inkey timeout=60 ] % 32) = 25) do={
- /system/script/run unattended-lte-firmware-upgrade;
- $LogPrintExit2 info $0 ("Scheduled lte firmware upgrade for interface " . $IntName . "...") false;
- :return true;
- } else={
- :put "Canceled...";
- }
- }
-
- :if (($SentLteFirmwareUpgradeNotification->$IntName) = ($Firmware->"latest")) do={
- $LogPrintExit2 debug $0 ("Already sent the LTE firmware upgrade notification for version " . \
- ($Firmware->"latest") . ".") false;
- :return false;
- }
-
- $LogPrintExit2 info $0 ("A new firmware version " . ($Firmware->"latest") . " is available for " . \
- "LTE interface " . $IntName . ".") false;
- $SendNotification2 ({ origin=$0; \
- subject=([ $SymbolForNotification "sparkles" ] . "LTE firmware upgrade"); \
- message=("A new firmware version " . ($Firmware->"latest") . " is available for " . \
- "LTE interface " . $IntName . " on " . $Identity . ".\n\n" . \
- "Interface: " . [ $CharacterReplace ($Info->"manufacturer" . " " . $Info->"model") ("\"") "" ] . "\n" . \
- "Installed: " . ($Firmware->"installed") . "\n" . \
- "Available: " . ($Firmware->"latest")); silent=true });
- :set ($SentLteFirmwareUpgradeNotification->$IntName) ($Firmware->"latest");
-}
-
-:foreach Interface in=[ /interface/lte/find ] do={
- $CheckInterface $Interface;
-}
+# dummy for migration
diff --git a/check-lte-firmware-upgrade.rsc b/check-lte-firmware-upgrade.rsc
new file mode 100644
index 0000000..02e864b
--- /dev/null
+++ b/check-lte-firmware-upgrade.rsc
@@ -0,0 +1,82 @@
+#!rsc by RouterOS
+# RouterOS script: check-lte-firmware-upgrade
+# Copyright (c) 2018-2023 Christian Hesse <mail@eworm.de>
+# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md
+#
+# check for LTE firmware upgrade, send notification
+# https://git.eworm.de/cgit/routeros-scripts/about/doc/check-lte-firmware-upgrade.md
+
+:local 0 "check-lte-firmware-upgrade";
+:global GlobalFunctionsReady;
+:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
+
+:global SentLteFirmwareUpgradeNotification;
+
+:if ([ :typeof $SentLteFirmwareUpgradeNotification ] != "array") do={
+ :global SentLteFirmwareUpgradeNotification ({});
+}
+
+:local CheckInterface do={
+ :local Interface $1;
+
+ :global Identity;
+ :global SentLteFirmwareUpgradeNotification;
+
+ :global CharacterReplace;
+ :global LogPrintExit2;
+ :global ScriptFromTerminal;
+ :global SendNotification2;
+ :global SymbolForNotification;
+
+ :local IntName [ /interface/lte/get $Interface name ];
+ :local Firmware;
+ :local Info;
+ :do {
+ :set Firmware [ /interface/lte/firmware-upgrade $Interface once as-value ];
+ :set Info [ /interface/lte/monitor $Interface once as-value ];
+ } on-error={
+ $LogPrintExit2 debug $0 ("Could not get latest LTE firmware version for interface " . \
+ $IntName . ".") false;
+ :return false;
+ }
+
+ :if (($Firmware->"installed") = ($Firmware->"latest")) do={
+ :if ([ $ScriptFromTerminal $0 ] = true) do={
+ $LogPrintExit2 info $0 ("No firmware upgrade available for LTE interface " . $IntName . ".") false;
+ }
+ :return true;
+ }
+
+ :if ([ $ScriptFromTerminal $0 ] = true && \
+ [ :len [ /system/script/find where name="unattended-lte-firmware-upgrade" ] ] > 0) do={
+ :put ("Do you want to start unattended lte firmware upgrade for interface " . $IntName . "? [y/N]");
+ :if (([ /terminal/inkey timeout=60 ] % 32) = 25) do={
+ /system/script/run unattended-lte-firmware-upgrade;
+ $LogPrintExit2 info $0 ("Scheduled lte firmware upgrade for interface " . $IntName . "...") false;
+ :return true;
+ } else={
+ :put "Canceled...";
+ }
+ }
+
+ :if (($SentLteFirmwareUpgradeNotification->$IntName) = ($Firmware->"latest")) do={
+ $LogPrintExit2 debug $0 ("Already sent the LTE firmware upgrade notification for version " . \
+ ($Firmware->"latest") . ".") false;
+ :return false;
+ }
+
+ $LogPrintExit2 info $0 ("A new firmware version " . ($Firmware->"latest") . " is available for " . \
+ "LTE interface " . $IntName . ".") false;
+ $SendNotification2 ({ origin=$0; \
+ subject=([ $SymbolForNotification "sparkles" ] . "LTE firmware upgrade"); \
+ message=("A new firmware version " . ($Firmware->"latest") . " is available for " . \
+ "LTE interface " . $IntName . " on " . $Identity . ".\n\n" . \
+ "Interface: " . [ $CharacterReplace ($Info->"manufacturer" . " " . $Info->"model") ("\"") "" ] . "\n" . \
+ "Installed: " . ($Firmware->"installed") . "\n" . \
+ "Available: " . ($Firmware->"latest")); silent=true });
+ :set ($SentLteFirmwareUpgradeNotification->$IntName) ($Firmware->"latest");
+}
+
+:foreach Interface in=[ /interface/lte/find ] do={
+ $CheckInterface $Interface;
+}
diff --git a/check-routeros-update b/check-routeros-update
index d1c82c5..2da00ca 100644
--- a/check-routeros-update
+++ b/check-routeros-update
@@ -1,147 +1,3 @@
#!rsc by RouterOS
-# RouterOS script: check-routeros-update
-# Copyright (c) 2013-2023 Christian Hesse <mail@eworm.de>
-# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md
#
-# check for RouterOS update, send notification and/or install
-# https://git.eworm.de/cgit/routeros-scripts/about/doc/check-routeros-update.md
-
-:local 0 "check-routeros-update";
-:global GlobalFunctionsReady;
-:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
-
-:global Identity;
-:global SafeUpdateAll;
-:global SafeUpdateNeighbor;
-:global SafeUpdatePatch;
-:global SafeUpdateUrl;
-:global SentRouterosUpdateNotification;
-
-:global DeviceInfo;
-:global LogPrintExit2;
-:global ScriptFromTerminal;
-:global ScriptLock;
-:global SendNotification2;
-:global SymbolForNotification;
-:global VersionToNum;
-:global WaitFullyConnected;
-
-:local DoUpdate do={
- :if ([ :len [ /system/script/find where name="packages-update" ] ] > 0) do={
- /system/script/run packages-update;
- } else={
- /system/package/update/install without-paging;
- }
- :error "Waiting for system to reboot.";
-}
-
-$ScriptLock $0;
-
-$WaitFullyConnected;
-
-:if ([ :len [ /system/scheduler/find where name="reboot-for-update" ] ] > 0) do={
- :error "A reboot for update is already scheduled.";
-}
-
-$LogPrintExit2 debug $0 ("Checking for updates...") false;
-/system/package/update/check-for-updates without-paging as-value;
-:local Update [ /system/package/update/get ];
-
-:if ([ $ScriptFromTerminal $0 ] = true && ($Update->"installed-version") = ($Update->"latest-version")) do={
- $LogPrintExit2 info $0 ("System is already up to date.") true;
-}
-
-:local NumInstalled [ $VersionToNum ($Update->"installed-version") ];
-:local NumLatest [ $VersionToNum ($Update->"latest-version") ];
-:local Link ("https://mikrotik.com/download/changelogs/" . $Update->"channel" . "-release-tree");
-
-:if ($NumLatest < 117505792) do={
- $LogPrintExit2 info $0 ("The version '" . ($Update->"latest-version") . "' is not a valid version.") true;
-}
-
-:if ($NumInstalled < $NumLatest) do={
- :if ($SafeUpdateAll ~ "^YES,? ?PLEASE!?\$") do={
- $LogPrintExit2 info $0 ("Installing ALL versions automatically, including " . \
- $Update->"latest-version" . "...") false;
- $SendNotification2 ({ origin=$0; \
- subject=([ $SymbolForNotification "sparkles" ] . "RouterOS update"); \
- message=("Installing ALL versions automatically, including " . $Update->"latest-version" . \
- "... Updating on " . $Identity . "..."); link=$Link; silent=true });
- $DoUpdate;
- }
-
- :if ($SafeUpdatePatch = true && ($NumInstalled & 0xffff0000) = ($NumLatest & 0xffff0000)) do={
- $LogPrintExit2 info $0 ("Version " . $Update->"latest-version" . " is a patch release, updating...") false;
- $SendNotification2 ({ origin=$0; \
- subject=([ $SymbolForNotification "sparkles" ] . "RouterOS update"); \
- message=("Version " . $Update->"latest-version" . " is a patch update for " . $Update->"channel" . \
- ", updating on " . $Identity . "..."); link=$Link; silent=true });
- $DoUpdate;
- }
-
- :if ($SafeUpdateNeighbor = true && [ :len [ /ip/neighbor/find where \
- version=($Update->"latest-version" . " (" . $Update->"channel" . ")") ] ] > 0) do={
- $LogPrintExit2 info $0 ("Seen a neighbor running version " . $Update->"latest-version" . ", updating...") false;
- $SendNotification2 ({ origin=$0; \
- subject=([ $SymbolForNotification "sparkles" ] . "RouterOS update"); \
- message=("Seen a neighbor running version " . $Update->"latest-version" . " from " . $Update->"channel" . \
- ", updating on " . $Identity . "..."); link=$Link; silent=true });
- $DoUpdate;
- }
-
- :if ([ :len $SafeUpdateUrl ] > 0) do={
- :local Result;
- :do {
- :set Result [ /tool/fetch check-certificate=yes-without-crl \
- ($SafeUpdateUrl . $Update->"channel" . "?installed=" . $Update->"installed-version" . \
- "&latest=" . $Update->"latest-version") output=user as-value ];
- } on-error={
- $LogPrintExit2 warning $0 ("Failed receiving safe version for " . $Update->"channel" . ".") false;
- }
- :if ($Result->"status" = "finished" && $Result->"data" = $Update->"latest-version") do={
- $LogPrintExit2 info $0 ("Version " . $Update->"latest-version" . " is considered safe, updating...") false;
- $SendNotification2 ({ origin=$0; \
- subject=([ $SymbolForNotification "sparkles" ] . "RouterOS update"); \
- message=("Version " . $Update->"latest-version" . " is considered safe for " . $Update->"channel" . \
- ", updating on " . $Identity . "..."); link=$Link; silent=true });
- $DoUpdate;
- }
- }
-
- :if ([ $ScriptFromTerminal $0 ] = true) do={
- :put ("Do you want to install RouterOS version " . $Update->"latest-version" . "? [y/N]");
- :if (([ /terminal/inkey timeout=60 ] % 32) = 25) do={
- $DoUpdate;
- } else={
- :put "Canceled...";
- }
- }
-
- :if ($SentRouterosUpdateNotification = $Update->"latest-version") do={
- $LogPrintExit2 info $0 ("Already sent the RouterOS update notification for version " . \
- $Update->"latest-version" . ".") true;
- }
-
- $SendNotification2 ({ origin=$0; \
- subject=([ $SymbolForNotification "sparkles" ] . "RouterOS update"); \
- message=("A new RouterOS version " . ($Update->"latest-version") . \
- " is available for " . $Identity . ".\n\n" . \
- [ $DeviceInfo ]); link=$Link; silent=true });
- :set SentRouterosUpdateNotification ($Update->"latest-version");
-}
-
-:if ($NumInstalled > $NumLatest) do={
- :if ($SentRouterosUpdateNotification = $Update->"latest-version") do={
- $LogPrintExit2 info $0 ("Already sent the RouterOS downgrade notification for version " . \
- $Update->"latest-version" . ".") true;
- }
-
- $SendNotification2 ({ origin=$0; \
- subject=([ $SymbolForNotification "warning-sign" ] . "RouterOS version"); \
- message=("A different RouterOS version " . ($Update->"latest-version") . \
- " is available for " . $Identity . ", but it is a downgrade.\n\n" . \
- [ $DeviceInfo ]); link=$Link; silent=true });
- $LogPrintExit2 info $0 ("A different RouterOS version " . ($Update->"latest-version") . \
- " is available for downgrade.") false;
- :set SentRouterosUpdateNotification ($Update->"latest-version");
-}
+# dummy for migration
diff --git a/check-routeros-update.rsc b/check-routeros-update.rsc
new file mode 100644
index 0000000..d1c82c5
--- /dev/null
+++ b/check-routeros-update.rsc
@@ -0,0 +1,147 @@
+#!rsc by RouterOS
+# RouterOS script: check-routeros-update
+# Copyright (c) 2013-2023 Christian Hesse <mail@eworm.de>
+# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md
+#
+# check for RouterOS update, send notification and/or install
+# https://git.eworm.de/cgit/routeros-scripts/about/doc/check-routeros-update.md
+
+:local 0 "check-routeros-update";
+:global GlobalFunctionsReady;
+:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
+
+:global Identity;
+:global SafeUpdateAll;
+:global SafeUpdateNeighbor;
+:global SafeUpdatePatch;
+:global SafeUpdateUrl;
+:global SentRouterosUpdateNotification;
+
+:global DeviceInfo;
+:global LogPrintExit2;
+:global ScriptFromTerminal;
+:global ScriptLock;
+:global SendNotification2;
+:global SymbolForNotification;
+:global VersionToNum;
+:global WaitFullyConnected;
+
+:local DoUpdate do={
+ :if ([ :len [ /system/script/find where name="packages-update" ] ] > 0) do={
+ /system/script/run packages-update;
+ } else={
+ /system/package/update/install without-paging;
+ }
+ :error "Waiting for system to reboot.";
+}
+
+$ScriptLock $0;
+
+$WaitFullyConnected;
+
+:if ([ :len [ /system/scheduler/find where name="reboot-for-update" ] ] > 0) do={
+ :error "A reboot for update is already scheduled.";
+}
+
+$LogPrintExit2 debug $0 ("Checking for updates...") false;
+/system/package/update/check-for-updates without-paging as-value;
+:local Update [ /system/package/update/get ];
+
+:if ([ $ScriptFromTerminal $0 ] = true && ($Update->"installed-version") = ($Update->"latest-version")) do={
+ $LogPrintExit2 info $0 ("System is already up to date.") true;
+}
+
+:local NumInstalled [ $VersionToNum ($Update->"installed-version") ];
+:local NumLatest [ $VersionToNum ($Update->"latest-version") ];
+:local Link ("https://mikrotik.com/download/changelogs/" . $Update->"channel" . "-release-tree");
+
+:if ($NumLatest < 117505792) do={
+ $LogPrintExit2 info $0 ("The version '" . ($Update->"latest-version") . "' is not a valid version.") true;
+}
+
+:if ($NumInstalled < $NumLatest) do={
+ :if ($SafeUpdateAll ~ "^YES,? ?PLEASE!?\$") do={
+ $LogPrintExit2 info $0 ("Installing ALL versions automatically, including " . \
+ $Update->"latest-version" . "...") false;
+ $SendNotification2 ({ origin=$0; \
+ subject=([ $SymbolForNotification "sparkles" ] . "RouterOS update"); \
+ message=("Installing ALL versions automatically, including " . $Update->"latest-version" . \
+ "... Updating on " . $Identity . "..."); link=$Link; silent=true });
+ $DoUpdate;
+ }
+
+ :if ($SafeUpdatePatch = true && ($NumInstalled & 0xffff0000) = ($NumLatest & 0xffff0000)) do={
+ $LogPrintExit2 info $0 ("Version " . $Update->"latest-version" . " is a patch release, updating...") false;
+ $SendNotification2 ({ origin=$0; \
+ subject=([ $SymbolForNotification "sparkles" ] . "RouterOS update"); \
+ message=("Version " . $Update->"latest-version" . " is a patch update for " . $Update->"channel" . \
+ ", updating on " . $Identity . "..."); link=$Link; silent=true });
+ $DoUpdate;
+ }
+
+ :if ($SafeUpdateNeighbor = true && [ :len [ /ip/neighbor/find where \
+ version=($Update->"latest-version" . " (" . $Update->"channel" . ")") ] ] > 0) do={
+ $LogPrintExit2 info $0 ("Seen a neighbor running version " . $Update->"latest-version" . ", updating...") false;
+ $SendNotification2 ({ origin=$0; \
+ subject=([ $SymbolForNotification "sparkles" ] . "RouterOS update"); \
+ message=("Seen a neighbor running version " . $Update->"latest-version" . " from " . $Update->"channel" . \
+ ", updating on " . $Identity . "..."); link=$Link; silent=true });
+ $DoUpdate;
+ }
+
+ :if ([ :len $SafeUpdateUrl ] > 0) do={
+ :local Result;
+ :do {
+ :set Result [ /tool/fetch check-certificate=yes-without-crl \
+ ($SafeUpdateUrl . $Update->"channel" . "?installed=" . $Update->"installed-version" . \
+ "&latest=" . $Update->"latest-version") output=user as-value ];
+ } on-error={
+ $LogPrintExit2 warning $0 ("Failed receiving safe version for " . $Update->"channel" . ".") false;
+ }
+ :if ($Result->"status" = "finished" && $Result->"data" = $Update->"latest-version") do={
+ $LogPrintExit2 info $0 ("Version " . $Update->"latest-version" . " is considered safe, updating...") false;
+ $SendNotification2 ({ origin=$0; \
+ subject=([ $SymbolForNotification "sparkles" ] . "RouterOS update"); \
+ message=("Version " . $Update->"latest-version" . " is considered safe for " . $Update->"channel" . \
+ ", updating on " . $Identity . "..."); link=$Link; silent=true });
+ $DoUpdate;
+ }
+ }
+
+ :if ([ $ScriptFromTerminal $0 ] = true) do={
+ :put ("Do you want to install RouterOS version " . $Update->"latest-version" . "? [y/N]");
+ :if (([ /terminal/inkey timeout=60 ] % 32) = 25) do={
+ $DoUpdate;
+ } else={
+ :put "Canceled...";
+ }
+ }
+
+ :if ($SentRouterosUpdateNotification = $Update->"latest-version") do={
+ $LogPrintExit2 info $0 ("Already sent the RouterOS update notification for version " . \
+ $Update->"latest-version" . ".") true;
+ }
+
+ $SendNotification2 ({ origin=$0; \
+ subject=([ $SymbolForNotification "sparkles" ] . "RouterOS update"); \
+ message=("A new RouterOS version " . ($Update->"latest-version") . \
+ " is available for " . $Identity . ".\n\n" . \
+ [ $DeviceInfo ]); link=$Link; silent=true });
+ :set SentRouterosUpdateNotification ($Update->"latest-version");
+}
+
+:if ($NumInstalled > $NumLatest) do={
+ :if ($SentRouterosUpdateNotification = $Update->"latest-version") do={
+ $LogPrintExit2 info $0 ("Already sent the RouterOS downgrade notification for version " . \
+ $Update->"latest-version" . ".") true;
+ }
+
+ $SendNotification2 ({ origin=$0; \
+ subject=([ $SymbolForNotification "warning-sign" ] . "RouterOS version"); \
+ message=("A different RouterOS version " . ($Update->"latest-version") . \
+ " is available for " . $Identity . ", but it is a downgrade.\n\n" . \
+ [ $DeviceInfo ]); link=$Link; silent=true });
+ $LogPrintExit2 info $0 ("A different RouterOS version " . ($Update->"latest-version") . \
+ " is available for downgrade.") false;
+ :set SentRouterosUpdateNotification ($Update->"latest-version");
+}
diff --git a/collect-wireless-mac.capsman b/collect-wireless-mac.capsman
index e814fa9..2da00ca 100644
--- a/collect-wireless-mac.capsman
+++ b/collect-wireless-mac.capsman
@@ -1,85 +1,3 @@
#!rsc by RouterOS
-# RouterOS script: collect-wireless-mac.capsman
-# Copyright (c) 2013-2023 Christian Hesse <mail@eworm.de>
-# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md
#
-# collect wireless mac adresses in access list
-# https://git.eworm.de/cgit/routeros-scripts/about/doc/collect-wireless-mac.md
-#
-# provides: lease-script, order=40
-#
-# !! Do not edit this file, it is generated from template!
-
-:local 0 "collect-wireless-mac.capsman";
-:global GlobalFunctionsReady;
-:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
-
-:global Identity;
-
-:global EitherOr;
-:global GetMacVendor;
-:global LogPrintExit2;
-:global ScriptLock;
-:global SendNotification2;
-:global SymbolForNotification;
-
-$ScriptLock $0 false 10;
-
-:if ([ :len [ /caps-man/access-list/find where comment="--- collected above ---" disabled ] ] = 0) do={
- /caps-man/access-list/add comment="--- collected above ---" disabled=yes;
- $LogPrintExit2 warning $0 ("Added disabled access-list entry with comment '--- collected above ---'.") false;
-}
-:local PlaceBefore ([ /caps-man/access-list/find where comment="--- collected above ---" disabled ]->0);
-
-:foreach Reg in=[ /caps-man/registration-table/find ] do={
- :local RegVal;
- :do {
- :set RegVal [ /caps-man/registration-table/get $Reg ];
- } on-error={
- $LogPrintExit2 debug $0 ("Device already gone... Ignoring.") false;
- }
-
- :if ([ :len ($RegVal->"mac-address") ] > 0) do={
- :local AccessList ([ /caps-man/access-list/find where mac-address=($RegVal->"mac-address") ]->0);
- :if ([ :len $AccessList ] > 0) do={
- $LogPrintExit2 debug $0 ("MAC address " . $RegVal->"mac-address" . " already known: " . \
- [ /caps-man/access-list/get $AccessList comment ]) false;
- }
-
- :if ([ :len $AccessList ] = 0) do={
- :local Address "no dhcp lease";
- :local DnsName "no dhcp lease";
- :local HostName "no dhcp lease";
- :local Lease ([ /ip/dhcp-server/lease/find where mac-address=($RegVal->"mac-address") dynamic=yes status=bound ]->0);
- :if ([ :len $Lease ] > 0) do={
- :set Address [ /ip/dhcp-server/lease/get $Lease address ];
- :set HostName [ $EitherOr [ /ip/dhcp-server/lease/get $Lease host-name ] "no hostname" ];
- :set DnsName "no dns name";
- :local DnsRec ([ /ip/dns/static/find where address=$Address ]->0);
- :if ([ :len $DnsRec ] > 0) do={
- :set DnsName [ /ip/dns/static/get $DnsRec name ];
- }
- }
- :local DateTime ([ /system/clock/get date ] . " " . [ /system/clock/get time ]);
- :local Vendor [ $GetMacVendor ($RegVal->"mac-address") ];
- :local Message ("MAC address " . $RegVal->"mac-address" . " (" . $Vendor . ", " . $HostName . ") " . \
- "first seen on " . $DateTime . " connected to SSID " . $RegVal->"ssid" . ", interface " . $RegVal->"interface");
- $LogPrintExit2 info $0 $Message false;
- /caps-man/access-list/add place-before=$PlaceBefore comment=$Message mac-address=($RegVal->"mac-address") disabled=yes;
- $SendNotification2 ({ origin=$0; \
- subject=([ $SymbolForNotification "mobile-phone" ] . $RegVal->"mac-address" . " connected to " . $RegVal->"ssid"); \
- message=("A device with unknown MAC address connected to " . $RegVal->"ssid" . " on " . $Identity . ".\n\n" . \
- "Controller: " . $Identity . "\n" . \
- "Interface: " . $RegVal->"interface" . "\n" . \
- "SSID: " . $RegVal->"ssid" . "\n" . \
- "MAC: " . $RegVal->"mac-address" . "\n" . \
- "Vendor: " . $Vendor . "\n" . \
- "Hostname: " . $HostName . "\n" . \
- "Address: " . $Address . "\n" . \
- "DNS name: " . $DnsName . "\n" . \
- "Date: " . $DateTime) });
- }
- } else={
- $LogPrintExit2 debug $0 ("No mac address available... Ignoring.") false;
- }
-}
+# dummy for migration
diff --git a/collect-wireless-mac.capsman.rsc b/collect-wireless-mac.capsman.rsc
new file mode 100644
index 0000000..e814fa9
--- /dev/null
+++ b/collect-wireless-mac.capsman.rsc
@@ -0,0 +1,85 @@
+#!rsc by RouterOS
+# RouterOS script: collect-wireless-mac.capsman
+# Copyright (c) 2013-2023 Christian Hesse <mail@eworm.de>
+# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md
+#
+# collect wireless mac adresses in access list
+# https://git.eworm.de/cgit/routeros-scripts/about/doc/collect-wireless-mac.md
+#
+# provides: lease-script, order=40
+#
+# !! Do not edit this file, it is generated from template!
+
+:local 0 "collect-wireless-mac.capsman";
+:global GlobalFunctionsReady;
+:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
+
+:global Identity;
+
+:global EitherOr;
+:global GetMacVendor;
+:global LogPrintExit2;
+:global ScriptLock;
+:global SendNotification2;
+:global SymbolForNotification;
+
+$ScriptLock $0 false 10;
+
+:if ([ :len [ /caps-man/access-list/find where comment="--- collected above ---" disabled ] ] = 0) do={
+ /caps-man/access-list/add comment="--- collected above ---" disabled=yes;
+ $LogPrintExit2 warning $0 ("Added disabled access-list entry with comment '--- collected above ---'.") false;
+}
+:local PlaceBefore ([ /caps-man/access-list/find where comment="--- collected above ---" disabled ]->0);
+
+:foreach Reg in=[ /caps-man/registration-table/find ] do={
+ :local RegVal;
+ :do {
+ :set RegVal [ /caps-man/registration-table/get $Reg ];
+ } on-error={
+ $LogPrintExit2 debug $0 ("Device already gone... Ignoring.") false;
+ }
+
+ :if ([ :len ($RegVal->"mac-address") ] > 0) do={
+ :local AccessList ([ /caps-man/access-list/find where mac-address=($RegVal->"mac-address") ]->0);
+ :if ([ :len $AccessList ] > 0) do={
+ $LogPrintExit2 debug $0 ("MAC address " . $RegVal->"mac-address" . " already known: " . \
+ [ /caps-man/access-list/get $AccessList comment ]) false;
+ }
+
+ :if ([ :len $AccessList ] = 0) do={
+ :local Address "no dhcp lease";
+ :local DnsName "no dhcp lease";
+ :local HostName "no dhcp lease";
+ :local Lease ([ /ip/dhcp-server/lease/find where mac-address=($RegVal->"mac-address") dynamic=yes status=bound ]->0);
+ :if ([ :len $Lease ] > 0) do={
+ :set Address [ /ip/dhcp-server/lease/get $Lease address ];
+ :set HostName [ $EitherOr [ /ip/dhcp-server/lease/get $Lease host-name ] "no hostname" ];
+ :set DnsName "no dns name";
+ :local DnsRec ([ /ip/dns/static/find where address=$Address ]->0);
+ :if ([ :len $DnsRec ] > 0) do={
+ :set DnsName [ /ip/dns/static/get $DnsRec name ];
+ }
+ }
+ :local DateTime ([ /system/clock/get date ] . " " . [ /system/clock/get time ]);
+ :local Vendor [ $GetMacVendor ($RegVal->"mac-address") ];
+ :local Message ("MAC address " . $RegVal->"mac-address" . " (" . $Vendor . ", " . $HostName . ") " . \
+ "first seen on " . $DateTime . " connected to SSID " . $RegVal->"ssid" . ", interface " . $RegVal->"interface");
+ $LogPrintExit2 info $0 $Message false;
+ /caps-man/access-list/add place-before=$PlaceBefore comment=$Message mac-address=($RegVal->"mac-address") disabled=yes;
+ $SendNotification2 ({ origin=$0; \
+ subject=([ $SymbolForNotification "mobile-phone" ] . $RegVal->"mac-address" . " connected to " . $RegVal->"ssid"); \
+ message=("A device with unknown MAC address connected to " . $RegVal->"ssid" . " on " . $Identity . ".\n\n" . \
+ "Controller: " . $Identity . "\n" . \
+ "Interface: " . $RegVal->"interface" . "\n" . \
+ "SSID: " . $RegVal->"ssid" . "\n" . \
+ "MAC: " . $RegVal->"mac-address" . "\n" . \
+ "Vendor: " . $Vendor . "\n" . \
+ "Hostname: " . $HostName . "\n" . \
+ "Address: " . $Address . "\n" . \
+ "DNS name: " . $DnsName . "\n" . \
+ "Date: " . $DateTime) });
+ }
+ } else={
+ $LogPrintExit2 debug $0 ("No mac address available... Ignoring.") false;
+ }
+}
diff --git a/collect-wireless-mac.local b/collect-wireless-mac.local
index ee07f54..2da00ca 100644
--- a/collect-wireless-mac.local
+++ b/collect-wireless-mac.local
@@ -1,86 +1,3 @@
#!rsc by RouterOS
-# RouterOS script: collect-wireless-mac.local
-# Copyright (c) 2013-2023 Christian Hesse <mail@eworm.de>
-# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md
#
-# collect wireless mac adresses in access list
-# https://git.eworm.de/cgit/routeros-scripts/about/doc/collect-wireless-mac.md
-#
-# provides: lease-script, order=40
-#
-# !! Do not edit this file, it is generated from template!
-
-:local 0 "collect-wireless-mac.local";
-:global GlobalFunctionsReady;
-:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
-
-:global Identity;
-
-:global EitherOr;
-:global GetMacVendor;
-:global LogPrintExit2;
-:global ScriptLock;
-:global SendNotification2;
-:global SymbolForNotification;
-
-$ScriptLock $0 false 10;
-
-:if ([ :len [ /interface/wireless/access-list/find where comment="--- collected above ---" disabled ] ] = 0) do={
- /interface/wireless/access-list/add comment="--- collected above ---" disabled=yes;
- $LogPrintExit2 warning $0 ("Added disabled access-list entry with comment '--- collected above ---'.") false;
-}
-:local PlaceBefore ([ /interface/wireless/access-list/find where comment="--- collected above ---" disabled ]->0);
-
-:foreach Reg in=[ /interface/wireless/registration-table/find ] do={
- :local RegVal;
- :do {
- :set RegVal [ /interface/wireless/registration-table/get $Reg ];
- } on-error={
- $LogPrintExit2 debug $0 ("Device already gone... Ignoring.") false;
- }
-
- :if ([ :len ($RegVal->"mac-address") ] > 0) do={
- :local AccessList ([ /interface/wireless/access-list/find where mac-address=($RegVal->"mac-address") ]->0);
- :if ([ :len $AccessList ] > 0) do={
- $LogPrintExit2 debug $0 ("MAC address " . $RegVal->"mac-address" . " already known: " . \
- [ /interface/wireless/access-list/get $AccessList comment ]) false;
- }
-
- :if ([ :len $AccessList ] = 0) do={
- :local Address "no dhcp lease";
- :local DnsName "no dhcp lease";
- :local HostName "no dhcp lease";
- :local Lease ([ /ip/dhcp-server/lease/find where mac-address=($RegVal->"mac-address") dynamic=yes status=bound ]->0);
- :if ([ :len $Lease ] > 0) do={
- :set Address [ /ip/dhcp-server/lease/get $Lease address ];
- :set HostName [ $EitherOr [ /ip/dhcp-server/lease/get $Lease host-name ] "no hostname" ];
- :set DnsName "no dns name";
- :local DnsRec ([ /ip/dns/static/find where address=$Address ]->0);
- :if ([ :len $DnsRec ] > 0) do={
- :set DnsName [ /ip/dns/static/get $DnsRec name ];
- }
- }
- :set ($RegVal->"ssid") [ /interface/wireless/get [ find where name=($RegVal->"interface") ] ssid ];
- :local DateTime ([ /system/clock/get date ] . " " . [ /system/clock/get time ]);
- :local Vendor [ $GetMacVendor ($RegVal->"mac-address") ];
- :local Message ("MAC address " . $RegVal->"mac-address" . " (" . $Vendor . ", " . $HostName . ") " . \
- "first seen on " . $DateTime . " connected to SSID " . $RegVal->"ssid" . ", interface " . $RegVal->"interface");
- $LogPrintExit2 info $0 $Message false;
- /interface/wireless/access-list/add place-before=$PlaceBefore comment=$Message mac-address=($RegVal->"mac-address") disabled=yes;
- $SendNotification2 ({ origin=$0; \
- subject=([ $SymbolForNotification "mobile-phone" ] . $RegVal->"mac-address" . " connected to " . $RegVal->"ssid"); \
- message=("A device with unknown MAC address connected to " . $RegVal->"ssid" . " on " . $Identity . ".\n\n" . \
- "Controller: " . $Identity . "\n" . \
- "Interface: " . $RegVal->"interface" . "\n" . \
- "SSID: " . $RegVal->"ssid" . "\n" . \
- "MAC: " . $RegVal->"mac-address" . "\n" . \
- "Vendor: " . $Vendor . "\n" . \
- "Hostname: " . $HostName . "\n" . \
- "Address: " . $Address . "\n" . \
- "DNS name: " . $DnsName . "\n" . \
- "Date: " . $DateTime) });
- }
- } else={
- $LogPrintExit2 debug $0 ("No mac address available... Ignoring.") false;
- }
-}
+# dummy for migration
diff --git a/collect-wireless-mac.local.rsc b/collect-wireless-mac.local.rsc
new file mode 100644
index 0000000..ee07f54
--- /dev/null
+++ b/collect-wireless-mac.local.rsc
@@ -0,0 +1,86 @@
+#!rsc by RouterOS
+# RouterOS script: collect-wireless-mac.local
+# Copyright (c) 2013-2023 Christian Hesse <mail@eworm.de>
+# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md
+#
+# collect wireless mac adresses in access list
+# https://git.eworm.de/cgit/routeros-scripts/about/doc/collect-wireless-mac.md
+#
+# provides: lease-script, order=40
+#
+# !! Do not edit this file, it is generated from template!
+
+:local 0 "collect-wireless-mac.local";
+:global GlobalFunctionsReady;
+:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
+
+:global Identity;
+
+:global EitherOr;
+:global GetMacVendor;
+:global LogPrintExit2;
+:global ScriptLock;
+:global SendNotification2;
+:global SymbolForNotification;
+
+$ScriptLock $0 false 10;
+
+:if ([ :len [ /interface/wireless/access-list/find where comment="--- collected above ---" disabled ] ] = 0) do={
+ /interface/wireless/access-list/add comment="--- collected above ---" disabled=yes;
+ $LogPrintExit2 warning $0 ("Added disabled access-list entry with comment '--- collected above ---'.") false;
+}
+:local PlaceBefore ([ /interface/wireless/access-list/find where comment="--- collected above ---" disabled ]->0);
+
+:foreach Reg in=[ /interface/wireless/registration-table/find ] do={
+ :local RegVal;
+ :do {
+ :set RegVal [ /interface/wireless/registration-table/get $Reg ];
+ } on-error={
+ $LogPrintExit2 debug $0 ("Device already gone... Ignoring.") false;
+ }
+
+ :if ([ :len ($RegVal->"mac-address") ] > 0) do={
+ :local AccessList ([ /interface/wireless/access-list/find where mac-address=($RegVal->"mac-address") ]->0);
+ :if ([ :len $AccessList ] > 0) do={
+ $LogPrintExit2 debug $0 ("MAC address " . $RegVal->"mac-address" . " already known: " . \
+ [ /interface/wireless/access-list/get $AccessList comment ]) false;
+ }
+
+ :if ([ :len $AccessList ] = 0) do={
+ :local Address "no dhcp lease";
+ :local DnsName "no dhcp lease";
+ :local HostName "no dhcp lease";
+ :local Lease ([ /ip/dhcp-server/lease/find where mac-address=($RegVal->"mac-address") dynamic=yes status=bound ]->0);
+ :if ([ :len $Lease ] > 0) do={
+ :set Address [ /ip/dhcp-server/lease/get $Lease address ];
+ :set HostName [ $EitherOr [ /ip/dhcp-server/lease/get $Lease host-name ] "no hostname" ];
+ :set DnsName "no dns name";
+ :local DnsRec ([ /ip/dns/static/find where address=$Address ]->0);
+ :if ([ :len $DnsRec ] > 0) do={
+ :set DnsName [ /ip/dns/static/get $DnsRec name ];
+ }
+ }
+ :set ($RegVal->"ssid") [ /interface/wireless/get [ find where name=($RegVal->"interface") ] ssid ];
+ :local DateTime ([ /system/clock/get date ] . " " . [ /system/clock/get time ]);
+ :local Vendor [ $GetMacVendor ($RegVal->"mac-address") ];
+ :local Message ("MAC address " . $RegVal->"mac-address" . " (" . $Vendor . ", " . $HostName . ") " . \
+ "first seen on " . $DateTime . " connected to SSID " . $RegVal->"ssid" . ", interface " . $RegVal->"interface");
+ $LogPrintExit2 info $0 $Message false;
+ /interface/wireless/access-list/add place-before=$PlaceBefore comment=$Message mac-address=($RegVal->"mac-address") disabled=yes;
+ $SendNotification2 ({ origin=$0; \
+ subject=([ $SymbolForNotification "mobile-phone" ] . $RegVal->"mac-address" . " connected to " . $RegVal->"ssid"); \
+ message=("A device with unknown MAC address connected to " . $RegVal->"ssid" . " on " . $Identity . ".\n\n" . \
+ "Controller: " . $Identity . "\n" . \
+ "Interface: " . $RegVal->"interface" . "\n" . \
+ "SSID: " . $RegVal->"ssid" . "\n" . \
+ "MAC: " . $RegVal->"mac-address" . "\n" . \
+ "Vendor: " . $Vendor . "\n" . \
+ "Hostname: " . $HostName . "\n" . \
+ "Address: " . $Address . "\n" . \
+ "DNS name: " . $DnsName . "\n" . \
+ "Date: " . $DateTime) });
+ }
+ } else={
+ $LogPrintExit2 debug $0 ("No mac address available... Ignoring.") false;
+ }
+}
diff --git a/collect-wireless-mac.template b/collect-wireless-mac.template.rsc
index c315385..c315385 100644
--- a/collect-wireless-mac.template
+++ b/collect-wireless-mac.template.rsc
diff --git a/daily-psk.capsman b/daily-psk.capsman
index 17a09e1..2da00ca 100644
--- a/daily-psk.capsman
+++ b/daily-psk.capsman
@@ -1,96 +1,3 @@
#!rsc by RouterOS
-# RouterOS script: daily-psk.capsman
-# Copyright (c) 2013-2023 Christian Hesse <mail@eworm.de>
-# Michael Gisbers <michael@gisbers.de>
-# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md
#
-# update daily PSK (pre shared key)
-# https://git.eworm.de/cgit/routeros-scripts/about/doc/daily-psk.md
-#
-# !! Do not edit this file, it is generated from template!
-
-:local 0 "daily-psk.capsman";
-:global GlobalFunctionsReady;
-:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
-
-:global DailyPskMatchComment;
-:global DailyPskQrCodeUrl;
-:global Identity;
-
-:global LogPrintExit2;
-:global SendNotification2;
-:global SymbolForNotification;
-:global UrlEncode;
-:global WaitForFile;
-:global WaitFullyConnected;
-
-$WaitFullyConnected;
-
-# return pseudo-random string for PSK
-:local GeneratePSK do={
- :local Date [ :tostr $1 ];
-
- :global DailyPskSecrets;
-
- :local Months { "jan"; "feb"; "mar"; "apr"; "may"; "jun";
- "jul"; "aug"; "sep"; "oct"; "nov"; "dec" };
-
- :local Month [ :pick $Date 0 3 ];
- :local Day [ :tonum [ :pick $Date 4 6 ] ];
- :local Year [ :pick $Date 7 11 ];
-
- :for MIndex from=0 to=[ :len $Months ] do={
- :if ($Months->$MIndex = $Month) do={
- :set Month ($MIndex + 1);
- }
- }
-
- :local A ((14 - $Month) / 12);
- :local B ($Year - $A);
- :local C ($Month + 12 * $A - 2);
- :local WeekDay (7000 + $Day + $B + ($B / 4) - ($B / 100) + ($B / 400) + ((31 * $C) / 12));
- :set WeekDay ($WeekDay - (($WeekDay / 7) * 7));
-
- :return (($DailyPskSecrets->0->($Day - 1)) . \
- ($DailyPskSecrets->1->($Month - 1)) . \
- ($DailyPskSecrets->2->$WeekDay));
-}
-
-:local Seen ({});
-:local Date [ /system/clock/get date ];
-:local NewPsk [ $GeneratePSK $Date ];
-
-:foreach AccList in=[ /caps-man/access-list/find where comment~$DailyPskMatchComment ] do={
- :local SsidRegExp [ /caps-man/access-list/get $AccList ssid-regexp ];
- :local Configuration ([ /caps-man/configuration/find where ssid~$SsidRegExp ]->0);
- :local Ssid [ /caps-man/configuration/get $Configuration ssid ];
- :local OldPsk [ /caps-man/access-list/get $AccList private-passphrase ];
- :local Skip 0;
-
- :if ($NewPsk != $OldPsk) do={
- $LogPrintExit2 info $0 ("Updating daily PSK for " . $Ssid . " to " . $NewPsk . " (was " . $OldPsk . ")") false;
- /caps-man/access-list/set $AccList private-passphrase=$NewPsk;
-
- :if ([ :len [ /caps-man/actual-interface-configuration/find where configuration.ssid=$Ssid !disabled ] ] > 0) do={
- :foreach SeenSsid in=$Seen do={
- :if ($SeenSsid = $Ssid) do={
- $LogPrintExit2 debug $0 ("Already sent a mail for SSID " . $Ssid . ", skipping.") false;
- :set Skip 1;
- }
- }
-
- :if ($Skip = 0) do={
- :set Seen ($Seen, $Ssid);
- :local Link ($DailyPskQrCodeUrl . \
- "?scale=8&level=1&ssid=" . [ $UrlEncode $Ssid ] . "&pass=" . [ $UrlEncode $NewPsk ]);
- $SendNotification2 ({ origin=$0; \
- subject=([ $SymbolForNotification "calendar" ] . "daily PSK " . $Ssid); \
- message=("This is the daily PSK on " . $Identity . ":\n\n" . \
- "SSID: " . $Ssid . "\n" . \
- "PSK: " . $NewPsk . "\n" . \
- "Date: " . $Date . "\n\n" . \
- "A client device specific rule must not exist!"); link=$Link });
- }
- }
- }
-}
+# dummy for migration
diff --git a/daily-psk.capsman.rsc b/daily-psk.capsman.rsc
new file mode 100644
index 0000000..17a09e1
--- /dev/null
+++ b/daily-psk.capsman.rsc
@@ -0,0 +1,96 @@
+#!rsc by RouterOS
+# RouterOS script: daily-psk.capsman
+# Copyright (c) 2013-2023 Christian Hesse <mail@eworm.de>
+# Michael Gisbers <michael@gisbers.de>
+# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md
+#
+# update daily PSK (pre shared key)
+# https://git.eworm.de/cgit/routeros-scripts/about/doc/daily-psk.md
+#
+# !! Do not edit this file, it is generated from template!
+
+:local 0 "daily-psk.capsman";
+:global GlobalFunctionsReady;
+:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
+
+:global DailyPskMatchComment;
+:global DailyPskQrCodeUrl;
+:global Identity;
+
+:global LogPrintExit2;
+:global SendNotification2;
+:global SymbolForNotification;
+:global UrlEncode;
+:global WaitForFile;
+:global WaitFullyConnected;
+
+$WaitFullyConnected;
+
+# return pseudo-random string for PSK
+:local GeneratePSK do={
+ :local Date [ :tostr $1 ];
+
+ :global DailyPskSecrets;
+
+ :local Months { "jan"; "feb"; "mar"; "apr"; "may"; "jun";
+ "jul"; "aug"; "sep"; "oct"; "nov"; "dec" };
+
+ :local Month [ :pick $Date 0 3 ];
+ :local Day [ :tonum [ :pick $Date 4 6 ] ];
+ :local Year [ :pick $Date 7 11 ];
+
+ :for MIndex from=0 to=[ :len $Months ] do={
+ :if ($Months->$MIndex = $Month) do={
+ :set Month ($MIndex + 1);
+ }
+ }
+
+ :local A ((14 - $Month) / 12);
+ :local B ($Year - $A);
+ :local C ($Month + 12 * $A - 2);
+ :local WeekDay (7000 + $Day + $B + ($B / 4) - ($B / 100) + ($B / 400) + ((31 * $C) / 12));
+ :set WeekDay ($WeekDay - (($WeekDay / 7) * 7));
+
+ :return (($DailyPskSecrets->0->($Day - 1)) . \
+ ($DailyPskSecrets->1->($Month - 1)) . \
+ ($DailyPskSecrets->2->$WeekDay));
+}
+
+:local Seen ({});
+:local Date [ /system/clock/get date ];
+:local NewPsk [ $GeneratePSK $Date ];
+
+:foreach AccList in=[ /caps-man/access-list/find where comment~$DailyPskMatchComment ] do={
+ :local SsidRegExp [ /caps-man/access-list/get $AccList ssid-regexp ];
+ :local Configuration ([ /caps-man/configuration/find where ssid~$SsidRegExp ]->0);
+ :local Ssid [ /caps-man/configuration/get $Configuration ssid ];
+ :local OldPsk [ /caps-man/access-list/get $AccList private-passphrase ];
+ :local Skip 0;
+
+ :if ($NewPsk != $OldPsk) do={
+ $LogPrintExit2 info $0 ("Updating daily PSK for " . $Ssid . " to " . $NewPsk . " (was " . $OldPsk . ")") false;
+ /caps-man/access-list/set $AccList private-passphrase=$NewPsk;
+
+ :if ([ :len [ /caps-man/actual-interface-configuration/find where configuration.ssid=$Ssid !disabled ] ] > 0) do={
+ :foreach SeenSsid in=$Seen do={
+ :if ($SeenSsid = $Ssid) do={
+ $LogPrintExit2 debug $0 ("Already sent a mail for SSID " . $Ssid . ", skipping.") false;
+ :set Skip 1;
+ }
+ }
+
+ :if ($Skip = 0) do={
+ :set Seen ($Seen, $Ssid);
+ :local Link ($DailyPskQrCodeUrl . \
+ "?scale=8&level=1&ssid=" . [ $UrlEncode $Ssid ] . "&pass=" . [ $UrlEncode $NewPsk ]);
+ $SendNotification2 ({ origin=$0; \
+ subject=([ $SymbolForNotification "calendar" ] . "daily PSK " . $Ssid); \
+ message=("This is the daily PSK on " . $Identity . ":\n\n" . \
+ "SSID: " . $Ssid . "\n" . \
+ "PSK: " . $NewPsk . "\n" . \
+ "Date: " . $Date . "\n\n" . \
+ "A client device specific rule must not exist!"); link=$Link });
+ }
+ }
+ }
+}
diff --git a/daily-psk.local b/daily-psk.local
index 17a60f7..2da00ca 100644
--- a/daily-psk.local
+++ b/daily-psk.local
@@ -1,95 +1,3 @@
#!rsc by RouterOS
-# RouterOS script: daily-psk.local
-# Copyright (c) 2013-2023 Christian Hesse <mail@eworm.de>
-# Michael Gisbers <michael@gisbers.de>
-# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md
#
-# update daily PSK (pre shared key)
-# https://git.eworm.de/cgit/routeros-scripts/about/doc/daily-psk.md
-#
-# !! Do not edit this file, it is generated from template!
-
-:local 0 "daily-psk.local";
-:global GlobalFunctionsReady;
-:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
-
-:global DailyPskMatchComment;
-:global DailyPskQrCodeUrl;
-:global Identity;
-
-:global LogPrintExit2;
-:global SendNotification2;
-:global SymbolForNotification;
-:global UrlEncode;
-:global WaitForFile;
-:global WaitFullyConnected;
-
-$WaitFullyConnected;
-
-# return pseudo-random string for PSK
-:local GeneratePSK do={
- :local Date [ :tostr $1 ];
-
- :global DailyPskSecrets;
-
- :local Months { "jan"; "feb"; "mar"; "apr"; "may"; "jun";
- "jul"; "aug"; "sep"; "oct"; "nov"; "dec" };
-
- :local Month [ :pick $Date 0 3 ];
- :local Day [ :tonum [ :pick $Date 4 6 ] ];
- :local Year [ :pick $Date 7 11 ];
-
- :for MIndex from=0 to=[ :len $Months ] do={
- :if ($Months->$MIndex = $Month) do={
- :set Month ($MIndex + 1);
- }
- }
-
- :local A ((14 - $Month) / 12);
- :local B ($Year - $A);
- :local C ($Month + 12 * $A - 2);
- :local WeekDay (7000 + $Day + $B + ($B / 4) - ($B / 100) + ($B / 400) + ((31 * $C) / 12));
- :set WeekDay ($WeekDay - (($WeekDay / 7) * 7));
-
- :return (($DailyPskSecrets->0->($Day - 1)) . \
- ($DailyPskSecrets->1->($Month - 1)) . \
- ($DailyPskSecrets->2->$WeekDay));
-}
-
-:local Seen ({});
-:local Date [ /system/clock/get date ];
-:local NewPsk [ $GeneratePSK $Date ];
-
-:foreach AccList in=[ /interface/wireless/access-list/find where comment~$DailyPskMatchComment ] do={
- :local IntName [ /interface/wireless/access-list/get $AccList interface ];
- :local Ssid [ /interface/wireless/get $IntName ssid ];
- :local OldPsk [ /interface/wireless/access-list/get $AccList private-pre-shared-key ];
- :local Skip 0;
-
- :if ($NewPsk != $OldPsk) do={
- $LogPrintExit2 info $0 ("Updating daily PSK for " . $Ssid . " to " . $NewPsk . " (was " . $OldPsk . ")") false;
- /interface/wireless/access-list/set $AccList private-pre-shared-key=$NewPsk;
-
- :if ([ :len [ /interface/wireless/find where name=$IntName !disabled ] ] = 1) do={
- :foreach SeenSsid in=$Seen do={
- :if ($SeenSsid = $Ssid) do={
- $LogPrintExit2 debug $0 ("Already sent a mail for SSID " . $Ssid . ", skipping.") false;
- :set Skip 1;
- }
- }
-
- :if ($Skip = 0) do={
- :set Seen ($Seen, $Ssid);
- :local Link ($DailyPskQrCodeUrl . \
- "?scale=8&level=1&ssid=" . [ $UrlEncode $Ssid ] . "&pass=" . [ $UrlEncode $NewPsk ]);
- $SendNotification2 ({ origin=$0; \
- subject=([ $SymbolForNotification "calendar" ] . "daily PSK " . $Ssid); \
- message=("This is the daily PSK on " . $Identity . ":\n\n" . \
- "SSID: " . $Ssid . "\n" . \
- "PSK: " . $NewPsk . "\n" . \
- "Date: " . $Date . "\n\n" . \
- "A client device specific rule must not exist!"); link=$Link });
- }
- }
- }
-}
+# dummy for migration
diff --git a/daily-psk.local.rsc b/daily-psk.local.rsc
new file mode 100644
index 0000000..17a60f7
--- /dev/null
+++ b/daily-psk.local.rsc
@@ -0,0 +1,95 @@
+#!rsc by RouterOS
+# RouterOS script: daily-psk.local
+# Copyright (c) 2013-2023 Christian Hesse <mail@eworm.de>
+# Michael Gisbers <michael@gisbers.de>
+# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md
+#
+# update daily PSK (pre shared key)
+# https://git.eworm.de/cgit/routeros-scripts/about/doc/daily-psk.md
+#
+# !! Do not edit this file, it is generated from template!
+
+:local 0 "daily-psk.local";
+:global GlobalFunctionsReady;
+:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
+
+:global DailyPskMatchComment;
+:global DailyPskQrCodeUrl;
+:global Identity;
+
+:global LogPrintExit2;
+:global SendNotification2;
+:global SymbolForNotification;
+:global UrlEncode;
+:global WaitForFile;
+:global WaitFullyConnected;
+
+$WaitFullyConnected;
+
+# return pseudo-random string for PSK
+:local GeneratePSK do={
+ :local Date [ :tostr $1 ];
+
+ :global DailyPskSecrets;
+
+ :local Months { "jan"; "feb"; "mar"; "apr"; "may"; "jun";
+ "jul"; "aug"; "sep"; "oct"; "nov"; "dec" };
+
+ :local Month [ :pick $Date 0 3 ];
+ :local Day [ :tonum [ :pick $Date 4 6 ] ];
+ :local Year [ :pick $Date 7 11 ];
+
+ :for MIndex from=0 to=[ :len $Months ] do={
+ :if ($Months->$MIndex = $Month) do={
+ :set Month ($MIndex + 1);
+ }
+ }
+
+ :local A ((14 - $Month) / 12);
+ :local B ($Year - $A);
+ :local C ($Month + 12 * $A - 2);
+ :local WeekDay (7000 + $Day + $B + ($B / 4) - ($B / 100) + ($B / 400) + ((31 * $C) / 12));
+ :set WeekDay ($WeekDay - (($WeekDay / 7) * 7));
+
+ :return (($DailyPskSecrets->0->($Day - 1)) . \
+ ($DailyPskSecrets->1->($Month - 1)) . \
+ ($DailyPskSecrets->2->$WeekDay));
+}
+
+:local Seen ({});
+:local Date [ /system/clock/get date ];
+:local NewPsk [ $GeneratePSK $Date ];
+
+:foreach AccList in=[ /interface/wireless/access-list/find where comment~$DailyPskMatchComment ] do={
+ :local IntName [ /interface/wireless/access-list/get $AccList interface ];
+ :local Ssid [ /interface/wireless/get $IntName ssid ];
+ :local OldPsk [ /interface/wireless/access-list/get $AccList private-pre-shared-key ];
+ :local Skip 0;
+
+ :if ($NewPsk != $OldPsk) do={
+ $LogPrintExit2 info $0 ("Updating daily PSK for " . $Ssid . " to " . $NewPsk . " (was " . $OldPsk . ")") false;
+ /interface/wireless/access-list/set $AccList private-pre-shared-key=$NewPsk;
+
+ :if ([ :len [ /interface/wireless/find where name=$IntName !disabled ] ] = 1) do={
+ :foreach SeenSsid in=$Seen do={
+ :if ($SeenSsid = $Ssid) do={
+ $LogPrintExit2 debug $0 ("Already sent a mail for SSID " . $Ssid . ", skipping.") false;
+ :set Skip 1;
+ }
+ }
+
+ :if ($Skip = 0) do={
+ :set Seen ($Seen, $Ssid);
+ :local Link ($DailyPskQrCodeUrl . \
+ "?scale=8&level=1&ssid=" . [ $UrlEncode $Ssid ] . "&pass=" . [ $UrlEncode $NewPsk ]);
+ $SendNotification2 ({ origin=$0; \
+ subject=([ $SymbolForNotification "calendar" ] . "daily PSK " . $Ssid); \
+ message=("This is the daily PSK on " . $Identity . ":\n\n" . \
+ "SSID: " . $Ssid . "\n" . \
+ "PSK: " . $NewPsk . "\n" . \
+ "Date: " . $Date . "\n\n" . \
+ "A client device specific rule must not exist!"); link=$Link });
+ }
+ }
+ }
+}
diff --git a/daily-psk.template b/daily-psk.template.rsc
index 7e41a1f..7e41a1f 100644
--- a/daily-psk.template
+++ b/daily-psk.template.rsc
diff --git a/dhcp-lease-comment.capsman b/dhcp-lease-comment.capsman
index 55b76bf..2da00ca 100644
--- a/dhcp-lease-comment.capsman
+++ b/dhcp-lease-comment.capsman
@@ -1,30 +1,3 @@
#!rsc by RouterOS
-# RouterOS script: dhcp-lease-comment.capsman
-# Copyright (c) 2013-2023 Christian Hesse <mail@eworm.de>
-# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md
#
-# provides: lease-script, order=60
-#
-# update dhcp-server lease comment with infos from access-list
-# https://git.eworm.de/cgit/routeros-scripts/about/doc/dhcp-lease-comment.md
-#
-# !! Do not edit this file, it is generated from template!
-
-:local 0 "dhcp-lease-comment.capsman";
-:global GlobalFunctionsReady;
-:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
-
-:global LogPrintExit2;
-
-:foreach Lease in=[ /ip/dhcp-server/lease/find where dynamic=yes status=bound ] do={
- :local LeaseVal [ /ip/dhcp-server/lease/get $Lease ];
- :local NewComment;
- :local AccessList ([ /caps-man/access-list/find where mac-address=($LeaseVal->"mac-address") ]->0);
- :if ([ :len $AccessList ] > 0) do={
- :set NewComment [ /caps-man/access-list/get $AccessList comment ];
- }
- :if ([ :len $NewComment ] != 0 && $LeaseVal->"comment" != $NewComment) do={
- $LogPrintExit2 info $0 ("Updating comment for DHCP lease " . $LeaseVal->"mac-address" . ": " . $NewComment) false;
- /ip/dhcp-server/lease/set comment=$NewComment $Lease;
- }
-}
+# dummy for migration
diff --git a/dhcp-lease-comment.capsman.rsc b/dhcp-lease-comment.capsman.rsc
new file mode 100644
index 0000000..55b76bf
--- /dev/null
+++ b/dhcp-lease-comment.capsman.rsc
@@ -0,0 +1,30 @@
+#!rsc by RouterOS
+# RouterOS script: dhcp-lease-comment.capsman
+# Copyright (c) 2013-2023 Christian Hesse <mail@eworm.de>
+# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md
+#
+# provides: lease-script, order=60
+#
+# update dhcp-server lease comment with infos from access-list
+# https://git.eworm.de/cgit/routeros-scripts/about/doc/dhcp-lease-comment.md
+#
+# !! Do not edit this file, it is generated from template!
+
+:local 0 "dhcp-lease-comment.capsman";
+:global GlobalFunctionsReady;
+:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
+
+:global LogPrintExit2;
+
+:foreach Lease in=[ /ip/dhcp-server/lease/find where dynamic=yes status=bound ] do={
+ :local LeaseVal [ /ip/dhcp-server/lease/get $Lease ];
+ :local NewComment;
+ :local AccessList ([ /caps-man/access-list/find where mac-address=($LeaseVal->"mac-address") ]->0);
+ :if ([ :len $AccessList ] > 0) do={
+ :set NewComment [ /caps-man/access-list/get $AccessList comment ];
+ }
+ :if ([ :len $NewComment ] != 0 && $LeaseVal->"comment" != $NewComment) do={
+ $LogPrintExit2 info $0 ("Updating comment for DHCP lease " . $LeaseVal->"mac-address" . ": " . $NewComment) false;
+ /ip/dhcp-server/lease/set comment=$NewComment $Lease;
+ }
+}
diff --git a/dhcp-lease-comment.local b/dhcp-lease-comment.local
index 3f4d6c5..2da00ca 100644
--- a/dhcp-lease-comment.local
+++ b/dhcp-lease-comment.local
@@ -1,30 +1,3 @@
#!rsc by RouterOS
-# RouterOS script: dhcp-lease-comment.local
-# Copyright (c) 2013-2023 Christian Hesse <mail@eworm.de>
-# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md
#
-# provides: lease-script, order=60
-#
-# update dhcp-server lease comment with infos from access-list
-# https://git.eworm.de/cgit/routeros-scripts/about/doc/dhcp-lease-comment.md
-#
-# !! Do not edit this file, it is generated from template!
-
-:local 0 "dhcp-lease-comment.local";
-:global GlobalFunctionsReady;
-:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
-
-:global LogPrintExit2;
-
-:foreach Lease in=[ /ip/dhcp-server/lease/find where dynamic=yes status=bound ] do={
- :local LeaseVal [ /ip/dhcp-server/lease/get $Lease ];
- :local NewComment;
- :local AccessList ([ /interface/wireless/access-list/find where mac-address=($LeaseVal->"mac-address") ]->0);
- :if ([ :len $AccessList ] > 0) do={
- :set NewComment [ /interface/wireless/access-list/get $AccessList comment ];
- }
- :if ([ :len $NewComment ] != 0 && $LeaseVal->"comment" != $NewComment) do={
- $LogPrintExit2 info $0 ("Updating comment for DHCP lease " . $LeaseVal->"mac-address" . ": " . $NewComment) false;
- /ip/dhcp-server/lease/set comment=$NewComment $Lease;
- }
-}
+# dummy for migration
diff --git a/dhcp-lease-comment.local.rsc b/dhcp-lease-comment.local.rsc
new file mode 100644
index 0000000..3f4d6c5
--- /dev/null
+++ b/dhcp-lease-comment.local.rsc
@@ -0,0 +1,30 @@
+#!rsc by RouterOS
+# RouterOS script: dhcp-lease-comment.local
+# Copyright (c) 2013-2023 Christian Hesse <mail@eworm.de>
+# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md
+#
+# provides: lease-script, order=60
+#
+# update dhcp-server lease comment with infos from access-list
+# https://git.eworm.de/cgit/routeros-scripts/about/doc/dhcp-lease-comment.md
+#
+# !! Do not edit this file, it is generated from template!
+
+:local 0 "dhcp-lease-comment.local";
+:global GlobalFunctionsReady;
+:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
+
+:global LogPrintExit2;
+
+:foreach Lease in=[ /ip/dhcp-server/lease/find where dynamic=yes status=bound ] do={
+ :local LeaseVal [ /ip/dhcp-server/lease/get $Lease ];
+ :local NewComment;
+ :local AccessList ([ /interface/wireless/access-list/find where mac-address=($LeaseVal->"mac-address") ]->0);
+ :if ([ :len $AccessList ] > 0) do={
+ :set NewComment [ /interface/wireless/access-list/get $AccessList comment ];
+ }
+ :if ([ :len $NewComment ] != 0 && $LeaseVal->"comment" != $NewComment) do={
+ $LogPrintExit2 info $0 ("Updating comment for DHCP lease " . $LeaseVal->"mac-address" . ": " . $NewComment) false;
+ /ip/dhcp-server/lease/set comment=$NewComment $Lease;
+ }
+}
diff --git a/dhcp-lease-comment.template b/dhcp-lease-comment.template.rsc
index 9de5f97..9de5f97 100644
--- a/dhcp-lease-comment.template
+++ b/dhcp-lease-comment.template.rsc
diff --git a/dhcp-to-dns b/dhcp-to-dns
index 48f96b2..2da00ca 100644
--- a/dhcp-to-dns
+++ b/dhcp-to-dns
@@ -1,97 +1,3 @@
#!rsc by RouterOS
-# RouterOS script: dhcp-to-dns
-# Copyright (c) 2013-2023 Christian Hesse <mail@eworm.de>
-# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md
#
-# provides: lease-script, order=20
-#
-# check DHCP leases and add/remove/update DNS entries
-# https://git.eworm.de/cgit/routeros-scripts/about/doc/dhcp-to-dns.md
-
-:local 0 "dhcp-to-dns";
-:global GlobalFunctionsReady;
-:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
-
-:global Domain;
-:global HostNameInZone;
-:global Identity;
-:global PrefixInZone;
-:global ServerNameInZone;
-
-:global CharacterReplace;
-:global IfThenElse;
-:global LogPrintExit2;
-:global ScriptLock;
-
-$ScriptLock $0 false 10;
-
-:local Zone \
- ([ $IfThenElse ($PrefixInZone = true) "dhcp." ] . \
- [ $IfThenElse ($HostNameInZone = true) ($Identity . ".") ] . $Domain);
-:local Ttl 5m;
-:local CommentPrefix ("managed by " . $0 . " for ");
-:local CommentString ("--- " . $0 . " above ---");
-
-:if ([ :len [ /ip/dns/static/find where comment=$CommentString name=- type=NXDOMAIN disabled ] ] = 0) do={
- /ip/dns/static/add comment=$CommentString name=- type=NXDOMAIN disabled=yes;
- $LogPrintExit2 warning $0 ("Added disabled static dns record with comment '" . $CommentString . "'.") false;
-}
-:local PlaceBefore ([ /ip/dns/static/find where comment=$CommentString name=- type=NXDOMAIN disabled ]->0);
-
-:foreach DnsRecord in=[ /ip/dns/static/find where comment ~ $CommentPrefix ] do={
- :local DnsRecordVal [ /ip/dns/static/get $DnsRecord ];
- :local MacAddress [ $CharacterReplace ($DnsRecordVal->"comment") $CommentPrefix "" ];
- :if ([ :len [ /ip/dhcp-server/lease/find where mac-address=$MacAddress address=($DnsRecordVal->"address") status=bound ] ] > 0) do={
- $LogPrintExit2 debug $0 ("Lease for " . $MacAddress . " (" . $DnsRecordVal->"name" . ") still exists. Not deleting DNS entry.") false;
- } else={
- :local Found false;
- $LogPrintExit2 info $0 ("Lease expired for " . $MacAddress . " (" . $DnsRecordVal->"name" . "), deleting DNS entry.") false;
- /ip/dns/static/remove $DnsRecord;
- }
-}
-
-:foreach Lease in=[ /ip/dhcp-server/lease/find where status=bound ] do={
- :local LeaseVal;
- :do {
- :set LeaseVal [ /ip/dhcp-server/lease/get $Lease ];
- } on-error={
- $LogPrintExit2 debug $0 ("A lease just vanished, ignoring.") false;
- }
-
- :if ([ :len ($LeaseVal->"address") ] > 0) do={
- :local Comment ($CommentPrefix . $LeaseVal->"mac-address");
- :local HostName [ $IfThenElse ([ :len ($LeaseVal->"host-name") ] = 0) \
- [ $CharacterReplace ($LeaseVal->"mac-address") ":" "-" ] \
- [ $CharacterReplace ($LeaseVal->"host-name") " " "" ] ];
-
- :local Fqdn ($HostName . "." . [ $IfThenElse ($ServerNameInZone = true) ($LeaseVal->"server" . ".") ] . $Zone);
- :local DnsRecord [ /ip/dns/static/find where name=$Fqdn ];
- :if ([ :len $DnsRecord ] > 0) do={
- :local DnsIp [ /ip/dns/static/get $DnsRecord address ];
-
- :local DupMacLeases [ /ip/dhcp-server/lease/find where mac-address=($LeaseVal->"mac-address") status=bound ];
- :if ([ :len $DupMacLeases ] > 1) do={
- :set ($LeaseVal->"address") [ /ip/dhcp-server/lease/get ($DupMacLeases->([ :len $DupMacLeases ] - 1)) address ];
- }
-
- :if ([ :len ($LeaseVal->"host-name") ] > 0) do={
- :local HostNameLeases [ /ip/dhcp-server/lease/find where host-name=($LeaseVal->"host-name") status=bound ];
- :if ([ :len $HostNameLeases ] > 1) do={
- :set ($LeaseVal->"address") [ /ip/dhcp-server/lease/get ($HostNameLeases->0) address ];
- }
- }
-
- :if ($DnsIp = $LeaseVal->"address") do={
- $LogPrintExit2 debug $0 ("DNS entry for " . $Fqdn . " does not need updating.") false;
- } else={
- $LogPrintExit2 info $0 ("Replacing DNS entry for " . $Fqdn . ", new address is " . $LeaseVal->"address" . ".") false;
- /ip/dns/static/set name=$Fqdn address=($LeaseVal->"address") ttl=$Ttl comment=$Comment $DnsRecord;
- }
- } else={
- $LogPrintExit2 info $0 ("Adding new DNS entry for " . $Fqdn . ", address is " . $LeaseVal->"address" . ".") false;
- /ip/dns/static/add name=$Fqdn address=($LeaseVal->"address") ttl=$Ttl comment=$Comment place-before=$PlaceBefore;
- }
- } else={
- $LogPrintExit2 debug $0 ("No address available... Ignoring.") false;
- }
-}
+# dummy for migration
diff --git a/dhcp-to-dns.rsc b/dhcp-to-dns.rsc
new file mode 100644
index 0000000..48f96b2
--- /dev/null
+++ b/dhcp-to-dns.rsc
@@ -0,0 +1,97 @@
+#!rsc by RouterOS
+# RouterOS script: dhcp-to-dns
+# Copyright (c) 2013-2023 Christian Hesse <mail@eworm.de>
+# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md
+#
+# provides: lease-script, order=20
+#
+# check DHCP leases and add/remove/update DNS entries
+# https://git.eworm.de/cgit/routeros-scripts/about/doc/dhcp-to-dns.md
+
+:local 0 "dhcp-to-dns";
+:global GlobalFunctionsReady;
+:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
+
+:global Domain;
+:global HostNameInZone;
+:global Identity;
+:global PrefixInZone;
+:global ServerNameInZone;
+
+:global CharacterReplace;
+:global IfThenElse;
+:global LogPrintExit2;
+:global ScriptLock;
+
+$ScriptLock $0 false 10;
+
+:local Zone \
+ ([ $IfThenElse ($PrefixInZone = true) "dhcp." ] . \
+ [ $IfThenElse ($HostNameInZone = true) ($Identity . ".") ] . $Domain);
+:local Ttl 5m;
+:local CommentPrefix ("managed by " . $0 . " for ");
+:local CommentString ("--- " . $0 . " above ---");
+
+:if ([ :len [ /ip/dns/static/find where comment=$CommentString name=- type=NXDOMAIN disabled ] ] = 0) do={
+ /ip/dns/static/add comment=$CommentString name=- type=NXDOMAIN disabled=yes;
+ $LogPrintExit2 warning $0 ("Added disabled static dns record with comment '" . $CommentString . "'.") false;
+}
+:local PlaceBefore ([ /ip/dns/static/find where comment=$CommentString name=- type=NXDOMAIN disabled ]->0);
+
+:foreach DnsRecord in=[ /ip/dns/static/find where comment ~ $CommentPrefix ] do={
+ :local DnsRecordVal [ /ip/dns/static/get $DnsRecord ];
+ :local MacAddress [ $CharacterReplace ($DnsRecordVal->"comment") $CommentPrefix "" ];
+ :if ([ :len [ /ip/dhcp-server/lease/find where mac-address=$MacAddress address=($DnsRecordVal->"address") status=bound ] ] > 0) do={
+ $LogPrintExit2 debug $0 ("Lease for " . $MacAddress . " (" . $DnsRecordVal->"name" . ") still exists. Not deleting DNS entry.") false;
+ } else={
+ :local Found false;
+ $LogPrintExit2 info $0 ("Lease expired for " . $MacAddress . " (" . $DnsRecordVal->"name" . "), deleting DNS entry.") false;
+ /ip/dns/static/remove $DnsRecord;
+ }
+}
+
+:foreach Lease in=[ /ip/dhcp-server/lease/find where status=bound ] do={
+ :local LeaseVal;
+ :do {
+ :set LeaseVal [ /ip/dhcp-server/lease/get $Lease ];
+ } on-error={
+ $LogPrintExit2 debug $0 ("A lease just vanished, ignoring.") false;
+ }
+
+ :if ([ :len ($LeaseVal->"address") ] > 0) do={
+ :local Comment ($CommentPrefix . $LeaseVal->"mac-address");
+ :local HostName [ $IfThenElse ([ :len ($LeaseVal->"host-name") ] = 0) \
+ [ $CharacterReplace ($LeaseVal->"mac-address") ":" "-" ] \
+ [ $CharacterReplace ($LeaseVal->"host-name") " " "" ] ];
+
+ :local Fqdn ($HostName . "." . [ $IfThenElse ($ServerNameInZone = true) ($LeaseVal->"server" . ".") ] . $Zone);
+ :local DnsRecord [ /ip/dns/static/find where name=$Fqdn ];
+ :if ([ :len $DnsRecord ] > 0) do={
+ :local DnsIp [ /ip/dns/static/get $DnsRecord address ];
+
+ :local DupMacLeases [ /ip/dhcp-server/lease/find where mac-address=($LeaseVal->"mac-address") status=bound ];
+ :if ([ :len $DupMacLeases ] > 1) do={
+ :set ($LeaseVal->"address") [ /ip/dhcp-server/lease/get ($DupMacLeases->([ :len $DupMacLeases ] - 1)) address ];
+ }
+
+ :if ([ :len ($LeaseVal->"host-name") ] > 0) do={
+ :local HostNameLeases [ /ip/dhcp-server/lease/find where host-name=($LeaseVal->"host-name") status=bound ];
+ :if ([ :len $HostNameLeases ] > 1) do={
+ :set ($LeaseVal->"address") [ /ip/dhcp-server/lease/get ($HostNameLeases->0) address ];
+ }
+ }
+
+ :if ($DnsIp = $LeaseVal->"address") do={
+ $LogPrintExit2 debug $0 ("DNS entry for " . $Fqdn . " does not need updating.") false;
+ } else={
+ $LogPrintExit2 info $0 ("Replacing DNS entry for " . $Fqdn . ", new address is " . $LeaseVal->"address" . ".") false;
+ /ip/dns/static/set name=$Fqdn address=($LeaseVal->"address") ttl=$Ttl comment=$Comment $DnsRecord;
+ }
+ } else={
+ $LogPrintExit2 info $0 ("Adding new DNS entry for " . $Fqdn . ", address is " . $LeaseVal->"address" . ".") false;
+ /ip/dns/static/add name=$Fqdn address=($LeaseVal->"address") ttl=$Ttl comment=$Comment place-before=$PlaceBefore;
+ }
+ } else={
+ $LogPrintExit2 debug $0 ("No address available... Ignoring.") false;
+ }
+}
diff --git a/doc/mod/scriptrunonce.md b/doc/mod/scriptrunonce.md
index 5472bef..20760fb 100644
--- a/doc/mod/scriptrunonce.md
+++ b/doc/mod/scriptrunonce.md
@@ -28,8 +28,8 @@ The optional configuration goes to `global-config-overlay`.
* `ScriptRunOnceUrlSuffix`: url suffix, appended to parameter
If the parameter passed to the function is not a complete URL (starting
-with protocol `ftp://`, `http://`, `https://` or `sftp://`) the values are
-prepended and appended.
+with protocol `ftp://`, `http://`, `https://` or `sftp://`) the base-url is
+prepended, and file extension `.rsc` and url-suffix are appended.
Usage and invocation
--------------------
diff --git a/firmware-upgrade-reboot b/firmware-upgrade-reboot
index cc45c38..2da00ca 100644
--- a/firmware-upgrade-reboot
+++ b/firmware-upgrade-reboot
@@ -1,42 +1,3 @@
#!rsc by RouterOS
-# RouterOS script: firmware-upgrade-reboot
-# Copyright (c) 2022-2023 Christian Hesse <mail@eworm.de>
-# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md
#
-# install firmware upgrade, and reboot
-# https://git.eworm.de/cgit/routeros-scripts/about/doc/firmware-upgrade-reboot.md
-
-:local 0 "firmware-upgrade-reboot";
-:global GlobalFunctionsReady;
-:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
-
-:global LogPrintExit2;
-:global VersionToNum;
-
-:local RouterBoard [ /system/routerboard/get ];
-:if ($RouterBoard->"current-firmware" = $RouterBoard->"upgrade-firmware") do={
- $LogPrintExit2 info $0 ("Current and upgrade firmware match with version " . \
- $RouterBoard->"current-firmware" . ".") true;
-}
-:if ([ $VersionToNum ($RouterBoard->"current-firmware") ] > [ $VersionToNum ($RouterBoard->"upgrade-firmware") ]) do={
- $LogPrintExit2 info $0 ("Different firmware version is available, but it is a downgrade. Ignoring.") true;
-}
-
-:if ([ /system/routerboard/settings/get auto-upgrade ] = false) do={
- $LogPrintExit2 info $0 ("Firmware version " . $RouterBoard->"upgrade-firmware" . \
- " is available, upgrading.") false;
- /system/routerboard/upgrade;
-}
-
-:while ([ :len [ /log/find where topics=({"system";"info";"critical"}) \
- message="Firmware upgraded successfully, please reboot for changes to take effect!" ] ] = 0) do={
- :delay 1s;
-}
-
-:local Uptime [ /system/resource/get uptime ];
-:if ($Uptime < 1m) do={
- :delay $Uptime;
-}
-
-$LogPrintExit2 info $0 ("Firmware upgrade successful, rebooting.") false;
-/system/reboot;
+# dummy for migration
diff --git a/firmware-upgrade-reboot.rsc b/firmware-upgrade-reboot.rsc
new file mode 100644
index 0000000..cc45c38
--- /dev/null
+++ b/firmware-upgrade-reboot.rsc
@@ -0,0 +1,42 @@
+#!rsc by RouterOS
+# RouterOS script: firmware-upgrade-reboot
+# Copyright (c) 2022-2023 Christian Hesse <mail@eworm.de>
+# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md
+#
+# install firmware upgrade, and reboot
+# https://git.eworm.de/cgit/routeros-scripts/about/doc/firmware-upgrade-reboot.md
+
+:local 0 "firmware-upgrade-reboot";
+:global GlobalFunctionsReady;
+:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
+
+:global LogPrintExit2;
+:global VersionToNum;
+
+:local RouterBoard [ /system/routerboard/get ];
+:if ($RouterBoard->"current-firmware" = $RouterBoard->"upgrade-firmware") do={
+ $LogPrintExit2 info $0 ("Current and upgrade firmware match with version " . \
+ $RouterBoard->"current-firmware" . ".") true;
+}
+:if ([ $VersionToNum ($RouterBoard->"current-firmware") ] > [ $VersionToNum ($RouterBoard->"upgrade-firmware") ]) do={
+ $LogPrintExit2 info $0 ("Different firmware version is available, but it is a downgrade. Ignoring.") true;
+}
+
+:if ([ /system/routerboard/settings/get auto-upgrade ] = false) do={
+ $LogPrintExit2 info $0 ("Firmware version " . $RouterBoard->"upgrade-firmware" . \
+ " is available, upgrading.") false;
+ /system/routerboard/upgrade;
+}
+
+:while ([ :len [ /log/find where topics=({"system";"info";"critical"}) \
+ message="Firmware upgraded successfully, please reboot for changes to take effect!" ] ] = 0) do={
+ :delay 1s;
+}
+
+:local Uptime [ /system/resource/get uptime ];
+:if ($Uptime < 1m) do={
+ :delay $Uptime;
+}
+
+$LogPrintExit2 info $0 ("Firmware upgrade successful, rebooting.") false;
+/system/reboot;
diff --git a/global-config b/global-config
index 770efd0..2da00ca 100644
--- a/global-config
+++ b/global-config
@@ -1,220 +1,3 @@
#!rsc by RouterOS
-# RouterOS script: global-config
-# Copyright (c) 2013-2023 Christian Hesse <mail@eworm.de>
-# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md
#
-# global configuration
-# https://git.eworm.de/cgit/routeros-scripts/about/
-
-# Set this to 'true' to disable news and change notifications.
-:global NoNewsAndChangesNotification false;
-
-# Add extra text (or emojis) in notification tags.
-:global IdentityExtra "";
-
-# This is used for DNS and backup file.
-:global Domain "example.com";
-:global HostNameInZone true;
-:global PrefixInZone true;
-:global ServerNameInZone false;
-
-# You can send e-mail notifications. Configure the system's mail settings
-# (/tool/e-mail), then install the module:
-# $ScriptInstallUpdate mod/notification-email
-# The to-address needs to be filled; cc-address can be empty, one address
-# or a comma separated list of addresses.
-:global EmailGeneralTo "";
-:global EmailGeneralCc "";
-#:global EmailGeneralTo "mail@example.com";
-#:global EmailGeneralCc "another@example.com,third@example.com";
-
-# You can send Telegram notifications. Register a bot
-# and add the token and chat ids here, then install the module:
-# $ScriptInstallUpdate mod/notification-telegram
-:global TelegramTokenId "";
-:global TelegramChatId "";
-#:global TelegramTokenId "123456:ABCDEF-GHI";
-#:global TelegramChatId "12345678";
-# Using telegram-chat you have to define trusted chat ids (not group ids!)
-# or user names. Groups allow to chat with devices simultaneously.
-#:global TelegramChatIdsTrusted {
-# "12345678";
-# "example_user";
-#};
-:global TelegramChatGroups "(all)";
-#:global TelegramChatGroups "(all|home|office)";
-# This is whether or not to send Telegram messages with fixed-width font.
-:global TelegramFixedWidthFont true;
-
-# You can send Matrix notifications. Configure these settings and
-# install the module:
-# $ScriptInstallUpdate mod/notification-matrix
-:global MatrixHomeServer "";
-:global MatrixAccessToken "";
-:global MatrixRoom "";
-#:global MatrixHomeServer "matrix.org";
-#:global MatrixAccessToken "123456ABCDEFGHI...";
-#:global MatrixRoom "!example:matrix.org";
-
-# It is possible to override e-mail, Telegram and Matrix setting for every
-# script. This is done in arrays, where 'Override' is appended to the
-# variable name, like this:
-#:global EmailGeneralToOverride {
-# "check-certificates"="override@example.com";
-# "backup-email"="backup@example.com";
-#}
-
-# Toggle this to disable symbols in notifications.
-:global NotificationsWithSymbols true;
-# Toggle this to disable color output in terminal/cli.
-:global TerminalColorOutput true;
-
-# This defines what backups to generate and what password to use.
-:global BackupSendBinary false;
-:global BackupSendExport true;
-:global BackupSendGlobalConfig true;
-:global BackupPassword "v3ry-s3cr3t";
-:global BackupRandomDelay 0;
-# These credentials are used to upload backup and config export files.
-# SFTP authentication is tricky, you may have to limit authentication
-# methods for your SSH server.
-:global BackupUploadUrl "sftp://example.com/backup/";
-:global BackupUploadUser "mikrotik";
-:global BackupUploadPass "v3ry-s3cr3t";
-
-# This defines what log messages to filter or include by topic or message
-# text. Regular expressions are supported. Do *NOT* set an empty string,
-# that will filter or include everything!
-# These are filters, so excluding messages from forwarding.
-:global LogForwardFilter "(debug|info)";
-:global LogForwardFilterMessage [];
-#:global LogForwardFilterMessage "message text";
-#:global LogForwardFilterMessage "(message text|another text|...)";
-# ... and another setting with reverse logic. This includes messages even
-# if filtered above.
-:global LogForwardInclude [];
-:global LogForwardIncludeMessage [];
-#:global LogForwardInclude "account";
-#:global LogForwardIncludeMessage "message text";
-
-# Specify an address to enable auto update to version assumed safe.
-# The configured channel (bugfix, current, release-candidate) is appended.
-:global SafeUpdateUrl "";
-#:global SafeUpdateUrl "https://example.com/ros/safe-update/";
-# Allow to install patch updates automatically.
-:global SafeUpdatePatch false;
-# Allow to install updates automatically if seen in neighbor list.
-:global SafeUpdateNeighbor false;
-# Install *ALL* updates automatically!
-# Set to all upper-case "Yes, please!" to enable.
-:global SafeUpdateAll "no";
-
-# These thresholds control when to send health notification
-# on temperature and voltage.
-:global CheckHealthTemperature {
- temperature=50;
- cpu-temperature=70;
- board-temperature1=50;
- board-temperature2=50;
-}
-# This is deviation on recovery threshold against notification flooding.
-:global CheckHealthTemperatureDeviation 3;
-:global CheckHealthVoltageLow 115;
-:global CheckHealthVoltagePercent 10;
-
-# Access-list entries matching this comment are updated
-# with daily pseudo-random PSK.
-:global DailyPskMatchComment "Daily PSK";
-:global DailyPskQrCodeUrl "https://www.eworm.de/cgi-bin/cqrlogo-wifi.cgi";
-:global DailyPskSecrets {
- { "Abusive"; "Aggressive"; "Bored"; "Chemical"; "Cold";
- "Cruel"; "Curved"; "Delightful"; "Discreet"; "Elite";
- "Evasive"; "Faded"; "Flat"; "Future"; "Grandiose";
- "Hanging"; "Humorous"; "Interesting"; "Magenta";
- "Magnificent"; "Numerous"; "Optimal"; "Pathetic";
- "Possessive"; "Remarkable"; "Rightful"; "Ruthless";
- "Stale"; "Unusual"; "Useless"; "Various" };
- { "Adhesive"; "Amusing"; "Astonishing"; "Frantic";
- "Kindhearted"; "Limping"; "Roasted"; "Robust";
- "Staking"; "Thundering"; "Ultra"; "Unreal" };
- { "Belief"; "Button"; "Curtain"; "Edge"; "Jewel";
- "String"; "Whistle" }
-}
-
-# Run different commands with multiple mode-button presses.
-:global ModeButton {
- 1="/system/script/run leds-toggle-mode;";
- 2=":global Identity; :global SendNotification; :global SymbolForNotification; \$SendNotification ([ \$SymbolForNotification \"earth\" ] . \"Hello...\") (\"Hello world, \" . \$Identity . \" calling!\");";
- 3="/system/shutdown;";
- 4="/system/reboot;";
- 5=":global BridgePortVlan; \$BridgePortVlan alt;";
-# add more here...
-};
-# This led gives visual feedback if type is 'on' or 'off'.
-:global ModeButtonLED "user-led";
-
-# Run commands on SMS action.
-:global SmsAction {
- bridge-port-vlan-alt=":global BridgePortVlan; \$BridgePortVlan alt;";
- reboot="/system/reboot;";
- shutdown="/system/shutdown;";
-# add more here...
-};
-
-# Run commands by hooking into SMS forward.
-:global SmsForwardHooks {
- { match="magic string";
- allowed-number="12345678";
- command="/system/script/run ..." };
-# add more here...
-};
-
-# This is the address used to send gps data to.
-:global GpsTrackUrl "https://example.com/index.php";
-
-# Enable this to fetch scripts from given url.
-:global ScriptUpdatesFetch true;
-:global ScriptUpdatesBaseUrl "https://git.eworm.de/cgit/routeros-scripts/plain/";
-# alternative urls - main: stable code - next: currently in development
-#:global ScriptUpdatesBaseUrl "https://raw.githubusercontent.com/eworm-de/routeros-scripts/main/";
-#:global ScriptUpdatesBaseUrl "https://raw.githubusercontent.com/eworm-de/routeros-scripts/next/";
-#:global ScriptUpdatesBaseUrl "https://gitlab.com/eworm-de/routeros-scripts/raw/main/";
-#:global ScriptUpdatesBaseUrl "https://gitlab.com/eworm-de/routeros-scripts/raw/next/";
-:global ScriptUpdatesUrlSuffix "";
-# use next branch with default url (git.eworm.de)
-#:global ScriptUpdatesUrlSuffix "\?h=next";
-
-# Use this for defaults with $ScriptRunOnce
-# Install module with:
-# $ScriptInstallUpdate mod/scriptrunonce
-:global ScriptRunOnceBaseUrl "";
-:global ScriptRunOnceUrlSuffix "";
-
-# 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 a donation:
-# https://git.eworm.de/cgit/routeros-scripts/about/#donate
-# Enable this to silence donation hint.
-:global IDonate false;
-
-# Use this for certificate auto-renew
-:global CertRenewUrl "";
-#:global CertRenewUrl "https://example.com/certificates/";
-:global CertRenewTime 3w;
-:global CertRenewPass {
- "v3ry-s3cr3t";
- "4n0th3r-s3cr3t";
-}
-:global CertWarnTime 2w;
-:global CertIssuedExportPass {
- "cert1-cn"="v3ry-s3cr3t";
- "cert2-cn"="4n0th3r-s3cr3t";
-}
-
-# load custom settings from overlay
-# Warning: Do *NOT* copy this code to overlay!
-:do {
- /system/script/run global-config-overlay;
-} on-error={
- :log error ("Loading configuration from overlay failed!");
-}
+# dummy for migration
diff --git a/global-config-overlay b/global-config-overlay.rsc
index a95f9cc..a95f9cc 100644
--- a/global-config-overlay
+++ b/global-config-overlay.rsc
diff --git a/global-config.changes b/global-config.changes
index 3e0372a..be34365 100644
--- a/global-config.changes
+++ b/global-config.changes
@@ -103,6 +103,7 @@
92="Made qr-code url configurable for 'daily-psk'.";
93="Added support to backup global-config-overlay in 'backup-email' and 'backup-upload'.";
94="Added support for host addresses in address-list for 'ipv6-update'.";
+ 95="Renamed script files in repository, running migration. No user interaction is required.";
};
# Migration steps to be applied on script updates
@@ -118,4 +119,5 @@
81=":global NtpPool; :if ([ :len [ /system/script/find where name=\"rotate-ntp\" ] ] > 0) do={ /system/script/remove [ find where name=\"rotate-ntp\" ]; /system/scheduler/remove [ find where name=\"rotate-ntp\" ]; /system/ntp/client/set servers=\$NtpPool; };";
82=":global CharacterReplace; :foreach Netwatch in=[ /tool/netwatch/find where comment~\"notify\" !disabled ] do={ /tool/netwatch/set \$Netwatch comment=[ \$CharacterReplace [ get \$Netwatch comment ] \"hostname=\" \"name=\" ]; };";
84=":global ScriptInstallUpdate; :global EmailGeneralTo; :if ([ /tool/e-mail/get address ] != \"0.0.0.0\" && [ :len \$EmailGeneralTo ] > 0) do={ \$ScriptInstallUpdate mod/notification-email; }";
+ 95=":global ScriptInstallUpdate; :global CharacterReplace; :foreach Script in=[ /system/script/find where name~\"\\\\.rsc\\\$\" source~\"^#!rsc by RouterOS\\n\" ] do={ /system/script/set \$Script name=[ \$CharacterReplace [ get \$Script name ] \".rsc\" \"\" ]; }; \$ScriptInstallUpdate;";
};
diff --git a/global-config.rsc b/global-config.rsc
new file mode 100644
index 0000000..770efd0
--- /dev/null
+++ b/global-config.rsc
@@ -0,0 +1,220 @@
+#!rsc by RouterOS
+# RouterOS script: global-config
+# Copyright (c) 2013-2023 Christian Hesse <mail@eworm.de>
+# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md
+#
+# global configuration
+# https://git.eworm.de/cgit/routeros-scripts/about/
+
+# Set this to 'true' to disable news and change notifications.
+:global NoNewsAndChangesNotification false;
+
+# Add extra text (or emojis) in notification tags.
+:global IdentityExtra "";
+
+# This is used for DNS and backup file.
+:global Domain "example.com";
+:global HostNameInZone true;
+:global PrefixInZone true;
+:global ServerNameInZone false;
+
+# You can send e-mail notifications. Configure the system's mail settings
+# (/tool/e-mail), then install the module:
+# $ScriptInstallUpdate mod/notification-email
+# The to-address needs to be filled; cc-address can be empty, one address
+# or a comma separated list of addresses.
+:global EmailGeneralTo "";
+:global EmailGeneralCc "";
+#:global EmailGeneralTo "mail@example.com";
+#:global EmailGeneralCc "another@example.com,third@example.com";
+
+# You can send Telegram notifications. Register a bot
+# and add the token and chat ids here, then install the module:
+# $ScriptInstallUpdate mod/notification-telegram
+:global TelegramTokenId "";
+:global TelegramChatId "";
+#:global TelegramTokenId "123456:ABCDEF-GHI";
+#:global TelegramChatId "12345678";
+# Using telegram-chat you have to define trusted chat ids (not group ids!)
+# or user names. Groups allow to chat with devices simultaneously.
+#:global TelegramChatIdsTrusted {
+# "12345678";
+# "example_user";
+#};
+:global TelegramChatGroups "(all)";
+#:global TelegramChatGroups "(all|home|office)";
+# This is whether or not to send Telegram messages with fixed-width font.
+:global TelegramFixedWidthFont true;
+
+# You can send Matrix notifications. Configure these settings and
+# install the module:
+# $ScriptInstallUpdate mod/notification-matrix
+:global MatrixHomeServer "";
+:global MatrixAccessToken "";
+:global MatrixRoom "";
+#:global MatrixHomeServer "matrix.org";
+#:global MatrixAccessToken "123456ABCDEFGHI...";
+#:global MatrixRoom "!example:matrix.org";
+
+# It is possible to override e-mail, Telegram and Matrix setting for every
+# script. This is done in arrays, where 'Override' is appended to the
+# variable name, like this:
+#:global EmailGeneralToOverride {
+# "check-certificates"="override@example.com";
+# "backup-email"="backup@example.com";
+#}
+
+# Toggle this to disable symbols in notifications.
+:global NotificationsWithSymbols true;
+# Toggle this to disable color output in terminal/cli.
+:global TerminalColorOutput true;
+
+# This defines what backups to generate and what password to use.
+:global BackupSendBinary false;
+:global BackupSendExport true;
+:global BackupSendGlobalConfig true;
+:global BackupPassword "v3ry-s3cr3t";
+:global BackupRandomDelay 0;
+# These credentials are used to upload backup and config export files.
+# SFTP authentication is tricky, you may have to limit authentication
+# methods for your SSH server.
+:global BackupUploadUrl "sftp://example.com/backup/";
+:global BackupUploadUser "mikrotik";
+:global BackupUploadPass "v3ry-s3cr3t";
+
+# This defines what log messages to filter or include by topic or message
+# text. Regular expressions are supported. Do *NOT* set an empty string,
+# that will filter or include everything!
+# These are filters, so excluding messages from forwarding.
+:global LogForwardFilter "(debug|info)";
+:global LogForwardFilterMessage [];
+#:global LogForwardFilterMessage "message text";
+#:global LogForwardFilterMessage "(message text|another text|...)";
+# ... and another setting with reverse logic. This includes messages even
+# if filtered above.
+:global LogForwardInclude [];
+:global LogForwardIncludeMessage [];
+#:global LogForwardInclude "account";
+#:global LogForwardIncludeMessage "message text";
+
+# Specify an address to enable auto update to version assumed safe.
+# The configured channel (bugfix, current, release-candidate) is appended.
+:global SafeUpdateUrl "";
+#:global SafeUpdateUrl "https://example.com/ros/safe-update/";
+# Allow to install patch updates automatically.
+:global SafeUpdatePatch false;
+# Allow to install updates automatically if seen in neighbor list.
+:global SafeUpdateNeighbor false;
+# Install *ALL* updates automatically!
+# Set to all upper-case "Yes, please!" to enable.
+:global SafeUpdateAll "no";
+
+# These thresholds control when to send health notification
+# on temperature and voltage.
+:global CheckHealthTemperature {
+ temperature=50;
+ cpu-temperature=70;
+ board-temperature1=50;
+ board-temperature2=50;
+}
+# This is deviation on recovery threshold against notification flooding.
+:global CheckHealthTemperatureDeviation 3;
+:global CheckHealthVoltageLow 115;
+:global CheckHealthVoltagePercent 10;
+
+# Access-list entries matching this comment are updated
+# with daily pseudo-random PSK.
+:global DailyPskMatchComment "Daily PSK";
+:global DailyPskQrCodeUrl "https://www.eworm.de/cgi-bin/cqrlogo-wifi.cgi";
+:global DailyPskSecrets {
+ { "Abusive"; "Aggressive"; "Bored"; "Chemical"; "Cold";
+ "Cruel"; "Curved"; "Delightful"; "Discreet"; "Elite";
+ "Evasive"; "Faded"; "Flat"; "Future"; "Grandiose";
+ "Hanging"; "Humorous"; "Interesting"; "Magenta";
+ "Magnificent"; "Numerous"; "Optimal"; "Pathetic";
+ "Possessive"; "Remarkable"; "Rightful"; "Ruthless";
+ "Stale"; "Unusual"; "Useless"; "Various" };
+ { "Adhesive"; "Amusing"; "Astonishing"; "Frantic";
+ "Kindhearted"; "Limping"; "Roasted"; "Robust";
+ "Staking"; "Thundering"; "Ultra"; "Unreal" };
+ { "Belief"; "Button"; "Curtain"; "Edge"; "Jewel";
+ "String"; "Whistle" }
+}
+
+# Run different commands with multiple mode-button presses.
+:global ModeButton {
+ 1="/system/script/run leds-toggle-mode;";
+ 2=":global Identity; :global SendNotification; :global SymbolForNotification; \$SendNotification ([ \$SymbolForNotification \"earth\" ] . \"Hello...\") (\"Hello world, \" . \$Identity . \" calling!\");";
+ 3="/system/shutdown;";
+ 4="/system/reboot;";
+ 5=":global BridgePortVlan; \$BridgePortVlan alt;";
+# add more here...
+};
+# This led gives visual feedback if type is 'on' or 'off'.
+:global ModeButtonLED "user-led";
+
+# Run commands on SMS action.
+:global SmsAction {
+ bridge-port-vlan-alt=":global BridgePortVlan; \$BridgePortVlan alt;";
+ reboot="/system/reboot;";
+ shutdown="/system/shutdown;";
+# add more here...
+};
+
+# Run commands by hooking into SMS forward.
+:global SmsForwardHooks {
+ { match="magic string";
+ allowed-number="12345678";
+ command="/system/script/run ..." };
+# add more here...
+};
+
+# This is the address used to send gps data to.
+:global GpsTrackUrl "https://example.com/index.php";
+
+# Enable this to fetch scripts from given url.
+:global ScriptUpdatesFetch true;
+:global ScriptUpdatesBaseUrl "https://git.eworm.de/cgit/routeros-scripts/plain/";
+# alternative urls - main: stable code - next: currently in development
+#:global ScriptUpdatesBaseUrl "https://raw.githubusercontent.com/eworm-de/routeros-scripts/main/";
+#:global ScriptUpdatesBaseUrl "https://raw.githubusercontent.com/eworm-de/routeros-scripts/next/";
+#:global ScriptUpdatesBaseUrl "https://gitlab.com/eworm-de/routeros-scripts/raw/main/";
+#:global ScriptUpdatesBaseUrl "https://gitlab.com/eworm-de/routeros-scripts/raw/next/";
+:global ScriptUpdatesUrlSuffix "";
+# use next branch with default url (git.eworm.de)
+#:global ScriptUpdatesUrlSuffix "\?h=next";
+
+# Use this for defaults with $ScriptRunOnce
+# Install module with:
+# $ScriptInstallUpdate mod/scriptrunonce
+:global ScriptRunOnceBaseUrl "";
+:global ScriptRunOnceUrlSuffix "";
+
+# 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 a donation:
+# https://git.eworm.de/cgit/routeros-scripts/about/#donate
+# Enable this to silence donation hint.
+:global IDonate false;
+
+# Use this for certificate auto-renew
+:global CertRenewUrl "";
+#:global CertRenewUrl "https://example.com/certificates/";
+:global CertRenewTime 3w;
+:global CertRenewPass {
+ "v3ry-s3cr3t";
+ "4n0th3r-s3cr3t";
+}
+:global CertWarnTime 2w;
+:global CertIssuedExportPass {
+ "cert1-cn"="v3ry-s3cr3t";
+ "cert2-cn"="4n0th3r-s3cr3t";
+}
+
+# load custom settings from overlay
+# Warning: Do *NOT* copy this code to overlay!
+:do {
+ /system/script/run global-config-overlay;
+} on-error={
+ :log error ("Loading configuration from overlay failed!");
+}
diff --git a/global-functions b/global-functions
index 996a6e1..366ef94 100644
--- a/global-functions
+++ b/global-functions
@@ -6,708 +6,27 @@
#
# requires RouterOS, version=7.7
#
-# global functions
-# https://git.eworm.de/cgit/routeros-scripts/about/
-
-:local 0 "global-functions";
+# WARNING: If you find this stripped version of global-functions
+# on your Router something went wrong and migration failed. To
+# recover run this function: $RouterOSScriptsRecover
# expected configuration version
-:global ExpectedConfigVersion 94;
-
-# global variables not to be changed by user
-:global GlobalFunctionsReady false;
-:global Identity [ /system/identity/get name ];
+:global ExpectedConfigVersion 95;
# global functions
-:global CertificateAvailable;
-:global CertificateDownload;
-:global CertificateNameByCN;
-:global CharacterReplace;
-:global CleanFilePath;
-:global DeviceInfo;
-:global Dos2Unix;
-:global DownloadPackage;
-:global EitherOr;
-:global EscapeForRegEx;
-:global GetMacVendor;
-:global GetRandom20CharAlNum;
-:global GetRandom20CharHex;
-:global GetRandomNumber;
-:global Grep;
-:global HexToNum;
-:global IfThenElse;
-:global IsDefaultRouteReachable;
-:global IsDNSResolving;
-:global IsFullyConnected;
-:global IsMacLocallyAdministered;
-:global IsTimeSync;
-:global LogPrintExit2;
-:global MkDir;
-:global NotificationFunctions;
-:global ParseKeyValueStore;
-:global PrettyPrint;
-:global RandomDelay;
-:global Read;
-:global RequiredRouterOS;
-:global ScriptFromTerminal;
+:global RouterOSScriptsRecover;
:global ScriptInstallUpdate;
-:global ScriptLock;
-:global SendNotification;
-:global SendNotification2;
-:global SymbolByUnicodeName;
-:global SymbolForNotification;
-:global Unix2Dos;
-:global UrlEncode;
-:global ValidateSyntax;
-:global VersionToNum;
-:global WaitDefaultRouteReachable;
-:global WaitDNSResolving;
-:global WaitForFile;
-:global WaitFullyConnected;
-:global WaitTimeSync;
-
-# check and download required certificate
-:set CertificateAvailable do={
- :local CommonName [ :tostr $1 ];
-
- :global CertificateDownload;
- :global LogPrintExit2;
- :global ParseKeyValueStore;
-
- :if ([ /system/resource/get free-hdd-space ] < 8388608 && \
- [ /certificate/settings/get crl-download ] = true && \
- [ /certificate/settings/get crl-store ] = "system") do={
- $LogPrintExit2 warning $0 ("This system has low free flash space but " . \
- "is configured to download certificate CRLs to system!") false;
- }
-
- :if ([ :len [ /certificate/find where common-name=$CommonName ] ] = 0) do={
- $LogPrintExit2 info $0 ("Certificate with CommonName \"" . $CommonName . "\" not available.") false;
- :if ([ $CertificateDownload $CommonName ] = false) do={
- :return false;
- }
- }
-
- :local CertVal [ /certificate/get [ find where common-name=$CommonName ] ];
- :while (($CertVal->"akid") != "" && ($CertVal->"akid") != ($CertVal->"skid")) do={
- :if ([ :len [ /certificate/find where skid=($CertVal->"akid") ] ] = 0) do={
- $LogPrintExit2 info $0 ("Certificate chain for \"" . $CommonName . \
- "\" is incomplete, missing \"" . ([ $ParseKeyValueStore ($CertVal->"issuer") ]->"CN") . "\".") false;
- :if ([ $CertificateDownload $CommonName ] = false) do={
- :return false;
- }
- }
- :set CertVal [ /certificate/get [ find where skid=($CertVal->"akid") ] ];
- }
- :return true;
-}
-
-# download and import certificate
-:set CertificateDownload do={
- :local CommonName [ :tostr $1 ];
-
- :global ScriptUpdatesBaseUrl;
- :global ScriptUpdatesUrlSuffix;
-
- :global CertificateNameByCN;
- :global LogPrintExit2;
- :global UrlEncode;
- :global WaitForFile;
-
- $LogPrintExit2 info $0 ("Downloading and importing certificate with " . \
- "CommonName \"" . $CommonName . "\".") false;
- :do {
- :local LocalFileName ($CommonName . ".pem");
- :local UrlFileName ([ $UrlEncode $CommonName ] . ".pem");
- /tool/fetch check-certificate=yes-without-crl \
- ($ScriptUpdatesBaseUrl . "certs/" . \
- $UrlFileName . $ScriptUpdatesUrlSuffix) \
- dst-path=$LocalFileName as-value;
- $WaitForFile $LocalFileName;
- /certificate/import file-name=$LocalFileName passphrase="" as-value;
- /file/remove $LocalFileName;
-
- :foreach Cert in=[ /certificate/find where name~("^" . $LocalFileName . "_[0-9]+\$") ] do={
- $CertificateNameByCN [ /certificate/get $Cert common-name ];
- }
- } on-error={
- $LogPrintExit2 warning $0 ("Failed importing certificate with " . \
- "CommonName \"" . $CommonName . "\"!") false;
- :return false;
- }
- :return true;
-}
-
-# name a certificate by its common-name
-:set CertificateNameByCN do={
- :local CommonName [ :tostr $1 ];
-
- :global CharacterReplace;
-
- :local Cert [ /certificate/find where common-name=$CommonName ];
- /certificate/set $Cert \
- name=[ $CharacterReplace [ $CharacterReplace [ $CharacterReplace $CommonName "'" "-" ] " " "-" ] "---" "-" ];
-}
-
-# character replace
-:set CharacterReplace do={
- :local String [ :tostr $1 ];
- :local ReplaceFrom [ :tostr $2 ];
- :local ReplaceWith [ :tostr $3 ];
- :local Return "";
-
- :if ($ReplaceFrom = "") do={
- :return $String;
- }
-
- :while ([ :typeof [ :find $String $ReplaceFrom ] ] != "nil") do={
- :local Pos [ :find $String $ReplaceFrom ];
- :set Return ($Return . [ :pick $String 0 $Pos ] . $ReplaceWith);
- :set String [ :pick $String ($Pos + [ :len $ReplaceFrom ]) [ :len $String ] ];
- }
-
- :return ($Return . $String);
-}
-
-# clean file path
-:set CleanFilePath do={
- :local Path [ :tostr $1 ];
-
- :global CharacterReplace;
-
- :while ($Path ~ "//") do={
- :set $Path [ $CharacterReplace $Path "//" "/" ];
- }
- :if ([ :pick $Path 0 ] = "/") do={
- :set Path [ :pick $Path 1 [ :len $Path ] ];
- }
- :if ([ :pick $Path ([ :len $Path ] - 1) ] = "/") do={
- :set Path [ :pick $Path 0 ([ :len $Path ] - 1) ];
- }
-
- :return $Path;
-}
-
-# get readable device info
-:set DeviceInfo do={
- :global ExpectedConfigVersion;
- :global Identity;
-
- :global IfThenElse;
-
- :local Resource [ /system/resource/get ];
- :local RouterBoard;
- :do {
- :set RouterBoard [[ :parse "/system/routerboard/get" ]];
- } on-error={ }
- :local License [ /system/license/get ];
- :local Update [ /system/package/update/get ];
-
- :return ( \
- "Hostname: " . $Identity . \
- "\nBoard name: " . $Resource->"board-name" . \
- "\nArchitecture: " . $Resource->"architecture-name" . \
- [ $IfThenElse ($RouterBoard->"routerboard" = true) \
- ("\nModel: " . $RouterBoard->"model" . \
- [ $IfThenElse ([ :len ($RouterBoard->"revision") ] > 0) \
- (" " . $RouterBoard->"revision") ] . \
- "\nSerial number: " . $RouterBoard->"serial-number") ] . \
- [ $IfThenElse ([ :len ($License->"level") ] > 0) \
- ("\nLicense: " . $License->"level") ] . \
- "\nRouterOS:" . \
- "\n Channel: " . $Update->"channel" . \
- "\n Installed: " . $Update->"installed-version" . \
- [ $IfThenElse ([ :typeof ($Update->"latest-version") ] != "nothing" && \
- $Update->"installed-version" != $Update->"latest-version") \
- ("\n Available: " . $Update->"latest-version") ] . \
- [ $IfThenElse ($RouterBoard->"routerboard" = true && \
- $RouterBoard->"current-firmware" != $RouterBoard->"upgrade-firmware") \
- ("\n Firmware: " . $RouterBoard->"current-firmware") ] . \
- "\nRouterOS-Scripts:" . \
- "\n Version: " . $ExpectedConfigVersion);
-}
-
-# convert line endings, DOS -> UNIX
-:set Dos2Unix do={
- :local Input [ :tostr $1 ];
-
- :global CharacterReplace;
-
- :return [ $CharacterReplace $Input ("\r\n") ("\n") ];
-}
-
-# download package from upgrade server
-:set DownloadPackage do={
- :local PkgName [ :tostr $1 ];
- :local PkgVer [ :tostr $2 ];
- :local PkgArch [ :tostr $3 ];
- :local PkgDir [ :tostr $4 ];
-
- :global CertificateAvailable;
- :global CleanFilePath;
- :global LogPrintExit2;
- :global MkDir;
- :global WaitForFile;
-
- :if ([ :len $PkgName ] = 0) do={ :return false; }
- :if ([ :len $PkgVer ] = 0) do={ :set PkgVer [ /system/package/update/get installed-version ]; }
- :if ([ :len $PkgArch ] = 0) do={ :set PkgArch [ /system/resource/get architecture-name ]; }
-
- :if ($PkgName = "system") do={ :set PkgName "routeros"; }
-
- :local PkgFile ($PkgName . "-" . $PkgVer . "-" . $PkgArch . ".npk");
- :if ($PkgArch = "x86_64") do={ :set PkgFile ($PkgName . "-" . $PkgVer . ".npk"); }
- :local PkgDest [ $CleanFilePath ($PkgDir . "/" . $PkgFile) ];
-
- :if ([ $MkDir $PkgDir ] = false) do={
- $LogPrintExit2 warning $0 ("Failed creating directory, not downloading package.") false;
- :return false;
- }
-
- :if ([ :len [ /file/find where name=$PkgDest type="package" ] ] > 0) do={
- $LogPrintExit2 info $0 ("Package file " . $PkgName . " already exists.") false;
- :return true;
- }
-
- :if ([ $CertificateAvailable "R3" ] = false) do={
- $LogPrintExit2 error $0 ("Downloading required certificate failed.") true;
- }
-
- :local Url ("https://upgrade.mikrotik.com/routeros/" . $PkgVer . "/" . $PkgFile);
- $LogPrintExit2 info $0 ("Downloading package file '" . $PkgName . "'...") false;
- $LogPrintExit2 debug $0 ("... from url: " . $Url) false;
- :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={
- $LogPrintExit2 debug $0 ("Downloading package file failed.") false;
- }
-
- /file/remove [ find where name=$PkgDest ];
- :set Retry ($Retry - 1);
- }
-
- $LogPrintExit2 warning $0 ("Downloading package file '" . $PkgName . "' failed.") false;
- :return false;
-}
-
-# return either first (if "true") or second
-:set EitherOr do={
- :global IfThenElse;
-
- :if ([ :typeof $1 ] = "num") do={
- :return [ $IfThenElse ($1 != 0) $1 $2 ];
- }
- :return [ $IfThenElse ([ :len [ :tostr $1 ] ] > 0) $1 $2 ];
-}
-
-# escape for regular expression
-:set EscapeForRegEx do={
- :local Input [ :tostr $1 ];
-
- :if ([ :len $Input ] = 0) do={
- :return "";
- }
-
- :local Return "";
- :local Chars ("^.[]\$()|*+\?{}\\");
-
- :for I from=0 to=([ :len $Input ] - 1) do={
- :local Char [ :pick $Input $I ];
- :if ([ :find $Chars $Char ]) do={
- :set Char ("\\" . $Char);
- }
- :set Return ($Return . $Char);
- }
-
- :return $Return;
-}
-
-# get MAC vendor
-:set GetMacVendor do={
- :local Mac [ :tostr $1 ];
-
- :global CertificateAvailable;
- :global IsMacLocallyAdministered;
- :global LogPrintExit2;
-
- :if ([ $IsMacLocallyAdministered $Mac ] = true) do={
- :return "locally administered";
- }
-
- :do {
- :if ([ $CertificateAvailable "R3" ] = false) do={
- $LogPrintExit2 warning $0 ("Downloading required certificate failed.") true;
- }
- :local Vendor ([ /tool/fetch check-certificate=yes-without-crl \
- ("https://api.macvendors.com/" . [ :pick $Mac 0 8 ]) output=user as-value ]->"data");
- :return $Vendor;
- } on-error={
- :do {
- /tool/fetch check-certificate=yes-without-crl ("https://api.macvendors.com/") \
- output=none as-value;
- $LogPrintExit2 debug $0 ("The mac vendor is not known in database.") false;
- } on-error={
- $LogPrintExit2 warning $0 ("Failed getting mac vendor.") false;
- }
- :return "unknown vendor";
- }
-}
-
-# generate random 20 chars alphabetical (A-Z & a-z) and numerical (0-9)
-:set GetRandom20CharAlNum do={
- :global EitherOr;
-
- :return [ :rndstr length=[ $EitherOr [ :tonum $1 ] 20 ] from="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789" ];
-}
-
-# generate random 20 chars hex (0-9 and a-f)
-:set GetRandom20CharHex do={
- :global EitherOr;
-
- :return [ :rndstr length=[ $EitherOr [ :tonum $1 ] 20 ] from="0123456789abcdef" ];
-}
-
-# generate random number
-:set GetRandomNumber do={
- :global EitherOr;
-
- :return [ :rndnum from=0 to=[ $EitherOr [ :tonum $1 ] 4294967295 ] ];
-}
-
-# return first line that matches a pattern
-:set Grep do={
- :local Input ([ :tostr $1 ] . "\n");
- :local Pattern [ :tostr $2 ];
-
- :if ([ :typeof [ :find $Input $Pattern ] ] = "nil") do={
- :return [];
- }
-
- :do {
- :local Line [ :pick $Input 0 [ :find $Input "\n" ] ];
- :if ([ :typeof [ :find $Line $Pattern ] ] = "num") do={
- :return $Line;
- }
- :set Input [ :pick $Input ([ :find $Input "\n" ] + 1) [ :len $Input ] ];
- } while=([ :len $Input ] > 0);
-
- :return [];
-}
-
-# convert from hex (string) to num
-:set HexToNum do={
- :local Input [ :tostr $1 ];
- :local Hex "0123456789abcdef0123456789ABCDEF";
- :local Multi 1;
- :local Return 0;
-
- :for I from=([ :len $Input ] - 1) to=0 do={
- :set Return ($Return + (([ :find $Hex [ :pick $Input $I ] ] % 16) * $Multi));
- :set Multi ($Multi * 16);
- }
-
- :return $Return;
-}
-
-# mimic conditional/ternary operator (condition ? consequent : alternative)
-:set IfThenElse do={
- :if ([ :tostr $1 ] = "true" || [ :tobool $1 ] = true) do={
- :return $2;
- }
- :return $3;
-}
-
-# check if default route is reachable
-:set IsDefaultRouteReachable do={
- :if ([ :len [ /ip/route/find where dst-address=0.0.0.0/0 active routing-table=main ] ] > 0) do={
- :return true;
- }
- :return false;
-}
-
-# check if DNS is resolving
-:set IsDNSResolving do={
- :global CharacterReplace;
-
- :do {
- :resolve "low-ttl.eworm.de";
- } on-error={
- :return false;
- }
- :return true;
-}
-
-# check if system is is fully connected (default route reachable, DNS resolving, time sync)
-:set IsFullyConnected do={
- :global IsDefaultRouteReachable;
- :global IsDNSResolving;
- :global IsTimeSync;
-
- :if ([ $IsDefaultRouteReachable ] = false) do={
- :return false;
- }
- :if ([ $IsDNSResolving ] = false) do={
- :return false;
- }
- :if ([ $IsTimeSync ] = false) do={
- :return false;
- }
- :return true;
-}
-
-# check if mac address is locally administered
-:set IsMacLocallyAdministered do={
- :if ([ :tonum ("0x" . [ :pick $1 0 [ :find $1 ":" ] ]) ] & 2 = 2) do={
- :return true;
- }
- :return false;
-}
-
-# check if system time is sync
-:set IsTimeSync do={
- :global IsTimeSyncCached;
-
- :global LogPrintExit2;
-
- :if ($IsTimeSyncCached = true) do={
- :return true;
- }
-
- :if ([ /system/ntp/client/get enabled ] = true) do={
- :if ([ /system/ntp/client/get status ] = "synchronized") do={
- :set IsTimeSyncCached true;
- :return true;
- }
- :return false;
- }
-
- :if ([ /system/license/get ]->"level" = "free" || \
- [ /system/resource/get ]->"board-name" = "x86") do={
- $LogPrintExit2 debug $0 ("No ntp client configured, relying on RTC for CHR free license and x86.") false;
- :return true;
- }
-
- :if ([ /ip/cloud/get update-time ] = true) do={
- :if ([ :typeof [ /ip/cloud/get public-address ] ] = "ip") do={
- :set IsTimeSyncCached true;
- :return true;
- }
- :return false;
- }
-
- $LogPrintExit2 debug $0 ("No time source configured! Returning gracefully...") false;
- :return true;
-}
-
-# log and print with same text, optionally exit
-:set LogPrintExit2 do={
- :local Severity [ :tostr $1 ];
- :local Name [ :tostr $2 ];
- :local Message [ :tostr $3 ];
- :local Exit [ :tostr $4 ];
-
- :global PrintDebug;
- :global PrintDebugOverride;
-
- :global EitherOr;
-
- :local Debug [ $EitherOr ($PrintDebugOverride->$Name) $PrintDebug ];
-
- :local PrintSeverity do={
- :global TerminalColorOutput;
-
- :if ($TerminalColorOutput != true) do={
- :return $1;
- }
-
- :local Color { debug=96; info=97; warning=93; error=91 };
- :return ("\1B[" . $Color->$1 . "m" . $1 . "\1B[0m");
- }
-
- :local Log ([ $EitherOr $Name "<unknown>" ] . ": " . $Message);
- :if ($Severity ~ ("^(debug|error|info)\$")) do={
- :if ($Severity = "debug") do={ :log debug $Log; }
- :if ($Severity = "error") do={ :log error $Log; }
- :if ($Severity = "info" ) do={ :log info $Log; }
- } else={
- :log warning $Log;
- :set Severity "warning";
- }
-
- :if ($Severity != "debug" || $Debug = true) do={
- :put ([ $PrintSeverity $Severity ] . ": " . $Message);
- }
-
- :if ($Exit = "true") do={
- :error ("Hard error to exit.");
- }
-}
-
-# create directory
-:set MkDir do={
- :local Path [ :tostr $1 ];
- :global CharacterReplace;
- :global CleanFilePath;
- :global GetRandom20CharAlNum;
- :global LogPrintExit2;
- :global RequiredRouterOS;
- :global WaitForFile;
-
- :set Path [ $CleanFilePath $Path ];
-
- :if ($Path = "") do={
- :return true;
- }
-
- :if ([ :len [ /file/find where name=$Path type="directory" ] ] = 1) do={
- :return true;
- }
-
- :local Error false;
- :local PathNext "";
- :foreach Dir in=[ :toarray [ $CharacterReplace $Path "/" "," ] ] do={
- :local Continue false;
- :set PathNext [ $CleanFilePath ($PathNext . "/" . $Dir) ];
-
- :if ([ :len [ /file/find where name=$PathNext !(name="tmpfs") type="directory" ] ] = 1) do={
- :set Continue true;
- }
-
- :if ($Continue = false && $PathNext = "tmpfs") do={
- :if ([ :len [ /disk/find where slot=tmpfs type=tmpfs ] ] = 0) do={
- $LogPrintExit2 info $0 ("Creating disk of type tmpfs.") false;
- /file/remove [ find where name="tmpfs" type="directory" ];
- :do {
- /disk/add slot=tmpfs type=tmpfs tmpfs-max-size=([ /system/resource/get total-memory ] / 3);
- $WaitForFile "tmpfs";
- } on-error={
- $LogPrintExit2 warning $0 ("Creating disk of type tmpfs failed!") false;
- :set Error true;
- }
- }
- :set Continue true;
- }
-
- :if ($Continue = false && [ :len [ /file/find where name=$PathNext ] ] = 1) do={
- $LogPrintExit2 warning $0 ("The path '" . $PathNext . "' exists, but is not a directory.") false;
- :return false;
- }
+# recover from failed migration
+:set RouterOSScriptsRecover do={
+ :global ScriptInstallUpdate;
- :if ($Continue = false) do={
- :local Name ($PathNext . "-" . [ $GetRandom20CharAlNum 6 ]);
- :do {
- /ip/smb/share/add disabled=yes directory=$PathNext name=$Name;
- $WaitForFile $PathNext;
- } on-error={
- $LogPrintExit2 warning $0 ("Making directory '" . $PathNext . "' failed!") false;
- :set Error true;
- }
- /ip/smb/share/remove [ find where name=$Name ];
- :if ($Error = true) do={
- :return false;
- }
- }
+ :foreach Script in={ "global-config"; "global-functions" } do={
+ /system/script/set name=$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/run $Script;
}
- :return true;
-}
-# prepare NotificationFunctions array
-:if ([ :typeof $NotificationFunctions ] != "array") do={
- :set NotificationFunctions ({});
-}
-
-# parse key value store
-:set ParseKeyValueStore do={
- :local Source $1;
- :if ([ :typeof $Source ] != "array") do={
- :set Source [ :tostr $1 ];
- }
- :local Result ({});
- :foreach KeyValue in=[ :toarray $Source ] do={
- :if ([ :find $KeyValue "=" ]) do={
- :set ($Result->[ :pick $KeyValue 0 [ :find $KeyValue "=" ] ]) \
- [ :pick $KeyValue ([ :find $KeyValue "=" ] + 1) [ :len $KeyValue ] ];
- } else={
- :set ($Result->$KeyValue) true;
- }
- }
- :return $Result;
-}
-
-# print lines with trailing carriage return
-:set PrettyPrint do={
- :local Input [ :tostr $1 ];
-
- :global Unix2Dos;
-
- :put [ $Unix2Dos $Input ];
-}
-
-# delay a random amount of seconds
-:set RandomDelay do={
- :global EitherOr;
- :global GetRandomNumber;
-
- :delay ([ $GetRandomNumber $1 ] . [ $EitherOr $2 "s" ]);
-}
-
-# read input from user
-:set Read do={
- :return;
-}
-
-# check for required RouterOS version
-:set RequiredRouterOS do={
- :local Caller [ :tostr $1 ];
- :local Required [ :tostr $2 ];
- :local Warn [ :tostr $3 ];
-
- :global IfThenElse;
- :global LogPrintExit2;
- :global VersionToNum;
-
- :if (!($Required ~ "^\\d+\\.\\d+((beta|rc|\\.)\\d+|)\$")) do={
- $LogPrintExit2 error $0 ("No valid RouterOS version: " . $Required) false;
- :return false;
- }
-
- :if ([ $VersionToNum $Required ] > [ $VersionToNum [ /system/package/update/get installed-version ] ]) do={
- :if ($Warn = "true") do={
- $LogPrintExit2 warning $0 ("This " . [ $IfThenElse ([ :pick $Caller 0 ] = ("\$")) "function" "script" ] . \
- " '" . $Caller . "' (at least specific functionality) requires RouterOS " . $Required . ". Please update!") false;
- }
- :return false;
- }
- :return true;
-}
-
-# check if script is run from terminal
-:set ScriptFromTerminal do={
- :local Script [ :tostr $1 ];
-
- :global LogPrintExit2;
-
- :foreach Job in=[ /system/script/job/find where script=$Script ] do={
- :set Job [ /system/script/job/get $Job ];
- :while ([ :typeof ($Job->"parent") ] = "id") do={
- :set Job [ /system/script/job/get [ find where .id=($Job->"parent") ] ];
- }
- :if (($Job->"type") = "login") do={
- $LogPrintExit2 debug $0 ("Script " . $Script . " started from terminal.") false;
- :return true;
- }
- }
- $LogPrintExit2 debug $0 ("Script " . $Script . " NOT started from terminal.") false;
-
- :return false;
+ $ScriptInstallUpdate;
}
# install new scripts, update existing scripts
@@ -779,7 +98,7 @@
:local UrlSuffix $ScriptUpdatesUrlSuffix;
:if ([ :typeof ($Comment->"base-url") ] = "str") do={ :set BaseUrl ($Comment->"base-url"); }
:if ([ :typeof ($Comment->"url-suffix") ] = "str") do={ :set UrlSuffix ($Comment->"url-suffix"); }
- :local Url ($BaseUrl . $ScriptVal->"name" . $UrlSuffix);
+ :local Url ($BaseUrl . $ScriptVal->"name" . ".rsc" . $UrlSuffix);
$LogPrintExit2 debug $0 ("Fetching script '" . $ScriptVal->"name" . "' from url: " . $Url) false;
:local Result [ /tool/fetch check-certificate=yes-without-crl $Url output=user as-value ];
@@ -863,7 +182,7 @@
:local ChangeLogCode;
:do {
- :local Url ($ScriptUpdatesBaseUrl . "global-config.changes" . $ScriptUpdatesUrlSuffix);
+ :local Url ($ScriptUpdatesBaseUrl . "news-and-changes.rsc" . $ScriptUpdatesUrlSuffix);
$LogPrintExit2 debug $0 ("Fetching news, changes and migration: " . $Url) false;
:local Result [ /tool/fetch check-certificate=yes-without-crl $Url output=user as-value ];
:if ($Result->"status" = "finished") do={
@@ -940,353 +259,3 @@
:set GlobalConfigMigration;
}
}
-
-# lock script against multiple invocation
-:set ScriptLock do={
- :local Script [ :tostr $1 ];
- :local DoReturn $2;
- :local WaitMax ([ :tonum $3 ] * 10);
-
- :global GetRandom20CharAlNum;
- :global IfThenElse;
- :global LogPrintExit2;
-
- :global ScriptLockOrder;
- :if ([ :typeof $ScriptLockOrder ] = "nothing") do={
- :set ScriptLockOrder ({});
- }
- :if ([ :typeof ($ScriptLockOrder->$Script) ] = "nothing") do={
- :set ($ScriptLockOrder->$Script) ({});
- }
-
- :local JobCount do={
- :local Script [ :tostr $1 ];
-
- :return [ :len [ /system/script/job/find where script=$Script ] ];
- }
-
- :local TicketCount do={
- :local Script [ :tostr $1 ];
-
- :global ScriptLockOrder;
-
- :local Count 0;
- :foreach Ticket in=($ScriptLockOrder->$Script) do={
- :if ([ :typeof $Ticket ] != "nothing") do={
- :set Count ($Count + 1);
- }
- }
- :return $Count;
- }
-
- :local IsFirstTicket do={
- :local Script [ :tostr $1 ];
- :local Check [ :tostr $2 ];
-
- :global ScriptLockOrder;
-
- :foreach Ticket in=($ScriptLockOrder->$Script) do={
- :if ($Ticket = $Check) do={ :return true; }
- :if ([ :typeof $Ticket ] != "nothing" && $Ticket != $Check) do={ :return false; }
- }
- :return false;
- }
-
- :local AddTicket do={
- :local Script [ :tostr $1 ];
- :local Add [ :tostr $2 ];
-
- :global ScriptLockOrder;
-
- :while (true) do={
- :local Pos [ :len ($ScriptLockOrder->$Script) ];
- :set ($ScriptLockOrder->$Script->$Pos) $Add;
- :delay 10ms;
- :if (($ScriptLockOrder->$Script->$Pos) = $Add) do={ :return true; }
- }
- }
-
- :local RemoveTicket do={
- :local Script [ :tostr $1 ];
- :local Remove [ :tostr $2 ];
-
- :global ScriptLockOrder;
-
- :foreach Id,Ticket in=($ScriptLockOrder->$Script) do={
- :while (($ScriptLockOrder->$Script->$Id) = $Remove) do={
- :set ($ScriptLockOrder->$Script->$Id);
- :delay 10ms;
- }
- }
- }
-
- :local CleanupTickets do={
- :local Script [ :tostr $1 ];
-
- :global ScriptLockOrder;
-
- :foreach Ticket in=($ScriptLockOrder->$Script) do={
- :if ([ :typeof $Ticket ] != "nothing") do={
- :return false;
- }
- }
-
- :set ($ScriptLockOrder->$Script) ({});
- }
-
- :if ([ :len [ /system/script/find where name=$Script ] ] = 0) do={
- $LogPrintExit2 error $0 ("A script named '" . $Script . "' does not exist!") true;
- }
-
- :if ([ $JobCount $Script ] = 0) do={
- $LogPrintExit2 error $0 ("No script '" . $Script . "' is running!") true;
- }
-
- :if ([ $TicketCount $Script ] >= [ $JobCount $Script ]) do={
- $LogPrintExit2 error $0 ("More tickets than running scripts '" . $Script . "', resetting!") false;
- :set ($ScriptLockOrder->$Script) ({});
- /system/script/job/remove [ find where script=$Script ];
- }
-
- :local MyTicket [ $GetRandom20CharAlNum 6 ];
- $AddTicket $Script $MyTicket;
-
- :local WaitCount 0;
- :while ($WaitMax > $WaitCount && ([ $IsFirstTicket $Script $MyTicket ] = false || [ $TicketCount $Script ] < [ $JobCount $Script ])) do={
- :set WaitCount ($WaitCount + 1);
- :delay 100ms;
- }
-
- :if ([ $IsFirstTicket $Script $MyTicket ] = true && [ $TicketCount $Script ] = [ $JobCount $Script ]) do={
- $RemoveTicket $Script $MyTicket;
- $CleanupTickets $Script;
- :return false;
- }
-
- $RemoveTicket $Script $MyTicket;
- $LogPrintExit2 info $0 ("Script '" . $Script . "' started more than once" . [ $IfThenElse ($WaitCount > 0) \
- " and timed out waiting for lock" "" ] . "... Aborting.") [ $IfThenElse ($DoReturn = true) false true ];
- :return true;
-}
-
-# send notification via NotificationFunctions - expects at least two string arguments
-:set SendNotification do={
- :global SendNotification2;
-
- $SendNotification2 ({ subject=$1; message=$2; link=$3; silent=$4 });
-}
-
-# send notification via NotificationFunctions - expects one array argument
-:set SendNotification2 do={
- :local Notification $1;
-
- :global NotificationFunctions;
-
- :foreach FunctionName,Discard in=$NotificationFunctions do={
- ($NotificationFunctions->$FunctionName) \
- ("\$NotificationFunctions->\"" . $FunctionName . "\"") \
- $Notification;
- }
-}
-
-# return UTF-8 symbol for unicode name
-:set SymbolByUnicodeName do={
- :local Symbols {
- "abacus"="\F0\9F\A7\AE";
- "alarm-clock"="\E2\8F\B0";
- "calendar"="\F0\9F\93\85";
- "card-file-box"="\F0\9F\97\83";
- "chart-decreasing"="\F0\9F\93\89";
- "chart-increasing"="\F0\9F\93\88";
- "cloud"="\E2\98\81";
- "cross-mark"="\E2\9D\8C";
- "earth"="\F0\9F\8C\8D";
- "fire"="\F0\9F\94\A5";
- "floppy-disk"="\F0\9F\92\BE";
- "high-voltage-sign"="\E2\9A\A1";
- "incoming-envelope"="\F0\9F\93\A8";
- "link"="\F0\9F\94\97";
- "lock-with-ink-pen"="\F0\9F\94\8F";
- "memo"="\F0\9F\93\9D";
- "mobile-phone"="\F0\9F\93\B1";
- "pushpin"="\F0\9F\93\8C";
- "scissors"="\E2\9C\82";
- "sparkles"="\E2\9C\A8";
- "speech-balloon"="\F0\9F\92\AC";
- "up-arrow"="\E2\AC\86";
- "warning-sign"="\E2\9A\A0";
- "white-heavy-check-mark"="\E2\9C\85"
- }
-
- :return (($Symbols->$1) . "\EF\B8\8F");
-}
-
-# return symbol for notification
-:set SymbolForNotification do={
- :global NotificationsWithSymbols;
- :global SymbolByUnicodeName;
-
- :if ($NotificationsWithSymbols != true) do={
- :return "";
- }
- :local Return "";
- :foreach Symbol in=[ :toarray $1 ] do={
- :set Return ($Return . [ $SymbolByUnicodeName $Symbol ]);
- }
- :return ($Return . " ");
-}
-
-# convert line endings, UNIX -> DOS
-:set Unix2Dos do={
- :local Input [ :tostr $1 ];
-
- :global CharacterReplace;
-
- :return [ $CharacterReplace [ $CharacterReplace $Input \
- ("\n") ("\r\n") ] ("\r\r\n") ("\r\n") ];
-}
-
-# url encoding
-:set UrlEncode do={
- :local Input [ :tostr $1 ];
-
- :if ([ :len $Input ] = 0) do={
- :return "";
- }
-
- :local Return "";
- :local Chars ("\n\r !\"#\$%&'()*+,:;<=>\?@[\\]^`{|}~");
- :local Subs { "%0A"; "%0D"; "%20"; "%21"; "%22"; "%23"; "%24"; "%25"; "%26"; "%27";
- "%28"; "%29"; "%2A"; "%2B"; "%2C"; "%3A"; "%3B"; "%3C"; "%3D"; "%3E"; "%3F";
- "%40"; "%5B"; "%5C"; "%5D"; "%5E"; "%60"; "%7B"; "%7C"; "%7D"; "%7E" };
-
- :for I from=0 to=([ :len $Input ] - 1) do={
- :local Char [ :pick $Input $I ];
- :local Replace [ :find $Chars $Char ];
-
- :if ([ :typeof $Replace ] = "num") do={
- :set Char ($Subs->$Replace);
- }
- :set Return ($Return . $Char);
- }
-
- :return $Return;
-}
-
-# basic syntax validation
-:set ValidateSyntax do={
- :local Code [ :tostr $1 ];
-
- :do {
- [ :parse (":local Validate do={\n" . $Code . "\n}") ];
- } on-error={
- :return false;
- }
- :return true;
-}
-
-# convert version string to numeric value
-:set VersionToNum do={
- :local Input [ :tostr $1 ];
- :local Multi 0x1000000;
- :local Return 0;
-
- :global CharacterReplace;
-
- :set Input [ $CharacterReplace [ $CharacterReplace [ $CharacterReplace $Input \
- "." "," ] "beta" ",beta," ] "rc" ",rc," ];
-
- :foreach Value in=([ :toarray $Input ], 0) do={
- :local Num [ :tonum $Value ];
- :if ($Multi = 0x100) do={
- :if ([ :typeof $Num ] = "num") do={
- :set Return ($Return + 0xff00);
- :set Multi ($Multi / 0x100);
- } else={
- :if ($Value = "beta") do={ :set Return ($Return + 0x3f00); }
- :if ($Value = "rc") do={ :set Return ($Return + 0x7f00); }
- }
- }
- :if ([ :typeof $Num ] = "num") do={ :set Return ($Return + ($Value * $Multi)); }
- :set Multi ($Multi / 0x100);
- }
-
- :return $Return;
-}
-
-# wait for default route to be reachable
-:set WaitDefaultRouteReachable do={
- :global IsDefaultRouteReachable;
-
- :while ([ $IsDefaultRouteReachable ] = false) do={
- :delay 1s;
- }
-}
-
-# wait for DNS to resolve
-:set WaitDNSResolving do={
- :global IsDNSResolving;
-
- :while ([ $IsDNSResolving ] = false) do={
- :delay 1s;
- }
-}
-
-# wait for file to be available
-:set WaitForFile do={
- :local FileName [ :tostr $1 ];
- :local WaitTime [ :totime $2 ];
-
- :global CleanFilePath;
- :global EitherOr;
-
- :set FileName [ $CleanFilePath $FileName ];
- :local I 1;
- :local Delay ([ :totime [ $EitherOr $WaitTime 2s ] ] / 20);
-
- :while ([ :len [ /file/find where name=$FileName ] ] = 0) do={
- :if ($I >= 20) do={
- :return false;
- }
- :delay $Delay;
- :set I ($I + 1);
- }
- :return true;
-}
-
-# wait to be fully connected (default route is reachable, time is sync, DNS resolves)
-:set WaitFullyConnected do={
- :global WaitDefaultRouteReachable;
- :global WaitDNSResolving;
- :global WaitTimeSync;
-
- $WaitDefaultRouteReachable;
- $WaitTimeSync;
- $WaitDNSResolving;
-}
-
-# wait for time to become synced
-:set WaitTimeSync do={
- :global IsTimeSync;
-
- :while ([ $IsTimeSync ] = false) do={
- :delay 1s;
- }
-}
-
-# load modules
-:foreach Script in=[ /system/script/find where name ~ "^mod/." ] do={
- :local ScriptVal [ /system/script/get $Script ];
- :if ([ $ValidateSyntax ($ScriptVal->"source") ] = true) do={
- :do {
- /system/script/run $Script;
- } on-error={
- $LogPrintExit2 error $0 ("Module '" . $ScriptVal->"name" . "' failed to run.") false;
- }
- } else={
- $LogPrintExit2 error $0 ("Module '" . $ScriptVal->"name" . "' failed syntax validation, skipping.") false;
- }
-}
-
-# signal we are ready
-:set GlobalFunctionsReady true;
diff --git a/global-functions.rsc b/global-functions.rsc
new file mode 100644
index 0000000..6f3bb86
--- /dev/null
+++ b/global-functions.rsc
@@ -0,0 +1,1292 @@
+#!rsc by RouterOS
+# RouterOS script: global-functions
+# Copyright (c) 2013-2023 Christian Hesse <mail@eworm.de>
+# Michael Gisbers <michael@gisbers.de>
+# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md
+#
+# requires RouterOS, version=7.7
+#
+# global functions
+# https://git.eworm.de/cgit/routeros-scripts/about/
+
+:local 0 "global-functions";
+
+# expected configuration version
+:global ExpectedConfigVersion 95;
+
+# global variables not to be changed by user
+:global GlobalFunctionsReady false;
+:global Identity [ /system/identity/get name ];
+
+# global functions
+:global CertificateAvailable;
+:global CertificateDownload;
+:global CertificateNameByCN;
+:global CharacterReplace;
+:global CleanFilePath;
+:global DeviceInfo;
+:global Dos2Unix;
+:global DownloadPackage;
+:global EitherOr;
+:global EscapeForRegEx;
+:global GetMacVendor;
+:global GetRandom20CharAlNum;
+:global GetRandom20CharHex;
+:global GetRandomNumber;
+:global Grep;
+:global HexToNum;
+:global IfThenElse;
+:global IsDefaultRouteReachable;
+:global IsDNSResolving;
+:global IsFullyConnected;
+:global IsMacLocallyAdministered;
+:global IsTimeSync;
+:global LogPrintExit2;
+:global MkDir;
+:global NotificationFunctions;
+:global ParseKeyValueStore;
+:global PrettyPrint;
+:global RandomDelay;
+:global Read;
+:global RequiredRouterOS;
+:global ScriptFromTerminal;
+:global ScriptInstallUpdate;
+:global ScriptLock;
+:global SendNotification;
+:global SendNotification2;
+:global SymbolByUnicodeName;
+:global SymbolForNotification;
+:global Unix2Dos;
+:global UrlEncode;
+:global ValidateSyntax;
+:global VersionToNum;
+:global WaitDefaultRouteReachable;
+:global WaitDNSResolving;
+:global WaitForFile;
+:global WaitFullyConnected;
+:global WaitTimeSync;
+
+# check and download required certificate
+:set CertificateAvailable do={
+ :local CommonName [ :tostr $1 ];
+
+ :global CertificateDownload;
+ :global LogPrintExit2;
+ :global ParseKeyValueStore;
+
+ :if ([ /system/resource/get free-hdd-space ] < 8388608 && \
+ [ /certificate/settings/get crl-download ] = true && \
+ [ /certificate/settings/get crl-store ] = "system") do={
+ $LogPrintExit2 warning $0 ("This system has low free flash space but " . \
+ "is configured to download certificate CRLs to system!") false;
+ }
+
+ :if ([ :len [ /certificate/find where common-name=$CommonName ] ] = 0) do={
+ $LogPrintExit2 info $0 ("Certificate with CommonName \"" . $CommonName . "\" not available.") false;
+ :if ([ $CertificateDownload $CommonName ] = false) do={
+ :return false;
+ }
+ }
+
+ :local CertVal [ /certificate/get [ find where common-name=$CommonName ] ];
+ :while (($CertVal->"akid") != "" && ($CertVal->"akid") != ($CertVal->"skid")) do={
+ :if ([ :len [ /certificate/find where skid=($CertVal->"akid") ] ] = 0) do={
+ $LogPrintExit2 info $0 ("Certificate chain for \"" . $CommonName . \
+ "\" is incomplete, missing \"" . ([ $ParseKeyValueStore ($CertVal->"issuer") ]->"CN") . "\".") false;
+ :if ([ $CertificateDownload $CommonName ] = false) do={
+ :return false;
+ }
+ }
+ :set CertVal [ /certificate/get [ find where skid=($CertVal->"akid") ] ];
+ }
+ :return true;
+}
+
+# download and import certificate
+:set CertificateDownload do={
+ :local CommonName [ :tostr $1 ];
+
+ :global ScriptUpdatesBaseUrl;
+ :global ScriptUpdatesUrlSuffix;
+
+ :global CertificateNameByCN;
+ :global LogPrintExit2;
+ :global UrlEncode;
+ :global WaitForFile;
+
+ $LogPrintExit2 info $0 ("Downloading and importing certificate with " . \
+ "CommonName \"" . $CommonName . "\".") false;
+ :do {
+ :local LocalFileName ($CommonName . ".pem");
+ :local UrlFileName ([ $UrlEncode $CommonName ] . ".pem");
+ /tool/fetch check-certificate=yes-without-crl \
+ ($ScriptUpdatesBaseUrl . "certs/" . \
+ $UrlFileName . $ScriptUpdatesUrlSuffix) \
+ dst-path=$LocalFileName as-value;
+ $WaitForFile $LocalFileName;
+ /certificate/import file-name=$LocalFileName passphrase="" as-value;
+ /file/remove $LocalFileName;
+
+ :foreach Cert in=[ /certificate/find where name~("^" . $LocalFileName . "_[0-9]+\$") ] do={
+ $CertificateNameByCN [ /certificate/get $Cert common-name ];
+ }
+ } on-error={
+ $LogPrintExit2 warning $0 ("Failed importing certificate with " . \
+ "CommonName \"" . $CommonName . "\"!") false;
+ :return false;
+ }
+ :return true;
+}
+
+# name a certificate by its common-name
+:set CertificateNameByCN do={
+ :local CommonName [ :tostr $1 ];
+
+ :global CharacterReplace;
+
+ :local Cert [ /certificate/find where common-name=$CommonName ];
+ /certificate/set $Cert \
+ name=[ $CharacterReplace [ $CharacterReplace [ $CharacterReplace $CommonName "'" "-" ] " " "-" ] "---" "-" ];
+}
+
+# character replace
+:set CharacterReplace do={
+ :local String [ :tostr $1 ];
+ :local ReplaceFrom [ :tostr $2 ];
+ :local ReplaceWith [ :tostr $3 ];
+ :local Return "";
+
+ :if ($ReplaceFrom = "") do={
+ :return $String;
+ }
+
+ :while ([ :typeof [ :find $String $ReplaceFrom ] ] != "nil") do={
+ :local Pos [ :find $String $ReplaceFrom ];
+ :set Return ($Return . [ :pick $String 0 $Pos ] . $ReplaceWith);
+ :set String [ :pick $String ($Pos + [ :len $ReplaceFrom ]) [ :len $String ] ];
+ }
+
+ :return ($Return . $String);
+}
+
+# clean file path
+:set CleanFilePath do={
+ :local Path [ :tostr $1 ];
+
+ :global CharacterReplace;
+
+ :while ($Path ~ "//") do={
+ :set $Path [ $CharacterReplace $Path "//" "/" ];
+ }
+ :if ([ :pick $Path 0 ] = "/") do={
+ :set Path [ :pick $Path 1 [ :len $Path ] ];
+ }
+ :if ([ :pick $Path ([ :len $Path ] - 1) ] = "/") do={
+ :set Path [ :pick $Path 0 ([ :len $Path ] - 1) ];
+ }
+
+ :return $Path;
+}
+
+# get readable device info
+:set DeviceInfo do={
+ :global ExpectedConfigVersion;
+ :global Identity;
+
+ :global IfThenElse;
+
+ :local Resource [ /system/resource/get ];
+ :local RouterBoard;
+ :do {
+ :set RouterBoard [[ :parse "/system/routerboard/get" ]];
+ } on-error={ }
+ :local License [ /system/license/get ];
+ :local Update [ /system/package/update/get ];
+
+ :return ( \
+ "Hostname: " . $Identity . \
+ "\nBoard name: " . $Resource->"board-name" . \
+ "\nArchitecture: " . $Resource->"architecture-name" . \
+ [ $IfThenElse ($RouterBoard->"routerboard" = true) \
+ ("\nModel: " . $RouterBoard->"model" . \
+ [ $IfThenElse ([ :len ($RouterBoard->"revision") ] > 0) \
+ (" " . $RouterBoard->"revision") ] . \
+ "\nSerial number: " . $RouterBoard->"serial-number") ] . \
+ [ $IfThenElse ([ :len ($License->"level") ] > 0) \
+ ("\nLicense: " . $License->"level") ] . \
+ "\nRouterOS:" . \
+ "\n Channel: " . $Update->"channel" . \
+ "\n Installed: " . $Update->"installed-version" . \
+ [ $IfThenElse ([ :typeof ($Update->"latest-version") ] != "nothing" && \
+ $Update->"installed-version" != $Update->"latest-version") \
+ ("\n Available: " . $Update->"latest-version") ] . \
+ [ $IfThenElse ($RouterBoard->"routerboard" = true && \
+ $RouterBoard->"current-firmware" != $RouterBoard->"upgrade-firmware") \
+ ("\n Firmware: " . $RouterBoard->"current-firmware") ] . \
+ "\nRouterOS-Scripts:" . \
+ "\n Version: " . $ExpectedConfigVersion);
+}
+
+# convert line endings, DOS -> UNIX
+:set Dos2Unix do={
+ :local Input [ :tostr $1 ];
+
+ :global CharacterReplace;
+
+ :return [ $CharacterReplace $Input ("\r\n") ("\n") ];
+}
+
+# download package from upgrade server
+:set DownloadPackage do={
+ :local PkgName [ :tostr $1 ];
+ :local PkgVer [ :tostr $2 ];
+ :local PkgArch [ :tostr $3 ];
+ :local PkgDir [ :tostr $4 ];
+
+ :global CertificateAvailable;
+ :global CleanFilePath;
+ :global LogPrintExit2;
+ :global MkDir;
+ :global WaitForFile;
+
+ :if ([ :len $PkgName ] = 0) do={ :return false; }
+ :if ([ :len $PkgVer ] = 0) do={ :set PkgVer [ /system/package/update/get installed-version ]; }
+ :if ([ :len $PkgArch ] = 0) do={ :set PkgArch [ /system/resource/get architecture-name ]; }
+
+ :if ($PkgName = "system") do={ :set PkgName "routeros"; }
+
+ :local PkgFile ($PkgName . "-" . $PkgVer . "-" . $PkgArch . ".npk");
+ :if ($PkgArch = "x86_64") do={ :set PkgFile ($PkgName . "-" . $PkgVer . ".npk"); }
+ :local PkgDest [ $CleanFilePath ($PkgDir . "/" . $PkgFile) ];
+
+ :if ([ $MkDir $PkgDir ] = false) do={
+ $LogPrintExit2 warning $0 ("Failed creating directory, not downloading package.") false;
+ :return false;
+ }
+
+ :if ([ :len [ /file/find where name=$PkgDest type="package" ] ] > 0) do={
+ $LogPrintExit2 info $0 ("Package file " . $PkgName . " already exists.") false;
+ :return true;
+ }
+
+ :if ([ $CertificateAvailable "R3" ] = false) do={
+ $LogPrintExit2 error $0 ("Downloading required certificate failed.") true;
+ }
+
+ :local Url ("https://upgrade.mikrotik.com/routeros/" . $PkgVer . "/" . $PkgFile);
+ $LogPrintExit2 info $0 ("Downloading package file '" . $PkgName . "'...") false;
+ $LogPrintExit2 debug $0 ("... from url: " . $Url) false;
+ :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={
+ $LogPrintExit2 debug $0 ("Downloading package file failed.") false;
+ }
+
+ /file/remove [ find where name=$PkgDest ];
+ :set Retry ($Retry - 1);
+ }
+
+ $LogPrintExit2 warning $0 ("Downloading package file '" . $PkgName . "' failed.") false;
+ :return false;
+}
+
+# return either first (if "true") or second
+:set EitherOr do={
+ :global IfThenElse;
+
+ :if ([ :typeof $1 ] = "num") do={
+ :return [ $IfThenElse ($1 != 0) $1 $2 ];
+ }
+ :return [ $IfThenElse ([ :len [ :tostr $1 ] ] > 0) $1 $2 ];
+}
+
+# escape for regular expression
+:set EscapeForRegEx do={
+ :local Input [ :tostr $1 ];
+
+ :if ([ :len $Input ] = 0) do={
+ :return "";
+ }
+
+ :local Return "";
+ :local Chars ("^.[]\$()|*+\?{}\\");
+
+ :for I from=0 to=([ :len $Input ] - 1) do={
+ :local Char [ :pick $Input $I ];
+ :if ([ :find $Chars $Char ]) do={
+ :set Char ("\\" . $Char);
+ }
+ :set Return ($Return . $Char);
+ }
+
+ :return $Return;
+}
+
+# get MAC vendor
+:set GetMacVendor do={
+ :local Mac [ :tostr $1 ];
+
+ :global CertificateAvailable;
+ :global IsMacLocallyAdministered;
+ :global LogPrintExit2;
+
+ :if ([ $IsMacLocallyAdministered $Mac ] = true) do={
+ :return "locally administered";
+ }
+
+ :do {
+ :if ([ $CertificateAvailable "R3" ] = false) do={
+ $LogPrintExit2 warning $0 ("Downloading required certificate failed.") true;
+ }
+ :local Vendor ([ /tool/fetch check-certificate=yes-without-crl \
+ ("https://api.macvendors.com/" . [ :pick $Mac 0 8 ]) output=user as-value ]->"data");
+ :return $Vendor;
+ } on-error={
+ :do {
+ /tool/fetch check-certificate=yes-without-crl ("https://api.macvendors.com/") \
+ output=none as-value;
+ $LogPrintExit2 debug $0 ("The mac vendor is not known in database.") false;
+ } on-error={
+ $LogPrintExit2 warning $0 ("Failed getting mac vendor.") false;
+ }
+ :return "unknown vendor";
+ }
+}
+
+# generate random 20 chars alphabetical (A-Z & a-z) and numerical (0-9)
+:set GetRandom20CharAlNum do={
+ :global EitherOr;
+
+ :return [ :rndstr length=[ $EitherOr [ :tonum $1 ] 20 ] from="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789" ];
+}
+
+# generate random 20 chars hex (0-9 and a-f)
+:set GetRandom20CharHex do={
+ :global EitherOr;
+
+ :return [ :rndstr length=[ $EitherOr [ :tonum $1 ] 20 ] from="0123456789abcdef" ];
+}
+
+# generate random number
+:set GetRandomNumber do={
+ :global EitherOr;
+
+ :return [ :rndnum from=0 to=[ $EitherOr [ :tonum $1 ] 4294967295 ] ];
+}
+
+# return first line that matches a pattern
+:set Grep do={
+ :local Input ([ :tostr $1 ] . "\n");
+ :local Pattern [ :tostr $2 ];
+
+ :if ([ :typeof [ :find $Input $Pattern ] ] = "nil") do={
+ :return [];
+ }
+
+ :do {
+ :local Line [ :pick $Input 0 [ :find $Input "\n" ] ];
+ :if ([ :typeof [ :find $Line $Pattern ] ] = "num") do={
+ :return $Line;
+ }
+ :set Input [ :pick $Input ([ :find $Input "\n" ] + 1) [ :len $Input ] ];
+ } while=([ :len $Input ] > 0);
+
+ :return [];
+}
+
+# convert from hex (string) to num
+:set HexToNum do={
+ :local Input [ :tostr $1 ];
+ :local Hex "0123456789abcdef0123456789ABCDEF";
+ :local Multi 1;
+ :local Return 0;
+
+ :for I from=([ :len $Input ] - 1) to=0 do={
+ :set Return ($Return + (([ :find $Hex [ :pick $Input $I ] ] % 16) * $Multi));
+ :set Multi ($Multi * 16);
+ }
+
+ :return $Return;
+}
+
+# mimic conditional/ternary operator (condition ? consequent : alternative)
+:set IfThenElse do={
+ :if ([ :tostr $1 ] = "true" || [ :tobool $1 ] = true) do={
+ :return $2;
+ }
+ :return $3;
+}
+
+# check if default route is reachable
+:set IsDefaultRouteReachable do={
+ :if ([ :len [ /ip/route/find where dst-address=0.0.0.0/0 active routing-table=main ] ] > 0) do={
+ :return true;
+ }
+ :return false;
+}
+
+# check if DNS is resolving
+:set IsDNSResolving do={
+ :global CharacterReplace;
+
+ :do {
+ :resolve "low-ttl.eworm.de";
+ } on-error={
+ :return false;
+ }
+ :return true;
+}
+
+# check if system is is fully connected (default route reachable, DNS resolving, time sync)
+:set IsFullyConnected do={
+ :global IsDefaultRouteReachable;
+ :global IsDNSResolving;
+ :global IsTimeSync;
+
+ :if ([ $IsDefaultRouteReachable ] = false) do={
+ :return false;
+ }
+ :if ([ $IsDNSResolving ] = false) do={
+ :return false;
+ }
+ :if ([ $IsTimeSync ] = false) do={
+ :return false;
+ }
+ :return true;
+}
+
+# check if mac address is locally administered
+:set IsMacLocallyAdministered do={
+ :if ([ :tonum ("0x" . [ :pick $1 0 [ :find $1 ":" ] ]) ] & 2 = 2) do={
+ :return true;
+ }
+ :return false;
+}
+
+# check if system time is sync
+:set IsTimeSync do={
+ :global IsTimeSyncCached;
+
+ :global LogPrintExit2;
+
+ :if ($IsTimeSyncCached = true) do={
+ :return true;
+ }
+
+ :if ([ /system/ntp/client/get enabled ] = true) do={
+ :if ([ /system/ntp/client/get status ] = "synchronized") do={
+ :set IsTimeSyncCached true;
+ :return true;
+ }
+ :return false;
+ }
+
+ :if ([ /system/license/get ]->"level" = "free" || \
+ [ /system/resource/get ]->"board-name" = "x86") do={
+ $LogPrintExit2 debug $0 ("No ntp client configured, relying on RTC for CHR free license and x86.") false;
+ :return true;
+ }
+
+ :if ([ /ip/cloud/get update-time ] = true) do={
+ :if ([ :typeof [ /ip/cloud/get public-address ] ] = "ip") do={
+ :set IsTimeSyncCached true;
+ :return true;
+ }
+ :return false;
+ }
+
+ $LogPrintExit2 debug $0 ("No time source configured! Returning gracefully...") false;
+ :return true;
+}
+
+# log and print with same text, optionally exit
+:set LogPrintExit2 do={
+ :local Severity [ :tostr $1 ];
+ :local Name [ :tostr $2 ];
+ :local Message [ :tostr $3 ];
+ :local Exit [ :tostr $4 ];
+
+ :global PrintDebug;
+ :global PrintDebugOverride;
+
+ :global EitherOr;
+
+ :local Debug [ $EitherOr ($PrintDebugOverride->$Name) $PrintDebug ];
+
+ :local PrintSeverity do={
+ :global TerminalColorOutput;
+
+ :if ($TerminalColorOutput != true) do={
+ :return $1;
+ }
+
+ :local Color { debug=96; info=97; warning=93; error=91 };
+ :return ("\1B[" . $Color->$1 . "m" . $1 . "\1B[0m");
+ }
+
+ :local Log ([ $EitherOr $Name "<unknown>" ] . ": " . $Message);
+ :if ($Severity ~ ("^(debug|error|info)\$")) do={
+ :if ($Severity = "debug") do={ :log debug $Log; }
+ :if ($Severity = "error") do={ :log error $Log; }
+ :if ($Severity = "info" ) do={ :log info $Log; }
+ } else={
+ :log warning $Log;
+ :set Severity "warning";
+ }
+
+ :if ($Severity != "debug" || $Debug = true) do={
+ :put ([ $PrintSeverity $Severity ] . ": " . $Message);
+ }
+
+ :if ($Exit = "true") do={
+ :error ("Hard error to exit.");
+ }
+}
+
+# create directory
+:set MkDir do={
+ :local Path [ :tostr $1 ];
+
+ :global CharacterReplace;
+ :global CleanFilePath;
+ :global GetRandom20CharAlNum;
+ :global LogPrintExit2;
+ :global RequiredRouterOS;
+ :global WaitForFile;
+
+ :set Path [ $CleanFilePath $Path ];
+
+ :if ($Path = "") do={
+ :return true;
+ }
+
+ :if ([ :len [ /file/find where name=$Path type="directory" ] ] = 1) do={
+ :return true;
+ }
+
+ :local Error false;
+ :local PathNext "";
+ :foreach Dir in=[ :toarray [ $CharacterReplace $Path "/" "," ] ] do={
+ :local Continue false;
+ :set PathNext [ $CleanFilePath ($PathNext . "/" . $Dir) ];
+
+ :if ([ :len [ /file/find where name=$PathNext !(name="tmpfs") type="directory" ] ] = 1) do={
+ :set Continue true;
+ }
+
+ :if ($Continue = false && $PathNext = "tmpfs") do={
+ :if ([ :len [ /disk/find where slot=tmpfs type=tmpfs ] ] = 0) do={
+ $LogPrintExit2 info $0 ("Creating disk of type tmpfs.") false;
+ /file/remove [ find where name="tmpfs" type="directory" ];
+ :do {
+ /disk/add slot=tmpfs type=tmpfs tmpfs-max-size=([ /system/resource/get total-memory ] / 3);
+ $WaitForFile "tmpfs";
+ } on-error={
+ $LogPrintExit2 warning $0 ("Creating disk of type tmpfs failed!") false;
+ :set Error true;
+ }
+ }
+ :set Continue true;
+ }
+
+ :if ($Continue = false && [ :len [ /file/find where name=$PathNext ] ] = 1) do={
+ $LogPrintExit2 warning $0 ("The path '" . $PathNext . "' exists, but is not a directory.") false;
+ :return false;
+ }
+
+ :if ($Continue = false) do={
+ :local Name ($PathNext . "-" . [ $GetRandom20CharAlNum 6 ]);
+ :do {
+ /ip/smb/share/add disabled=yes directory=$PathNext name=$Name;
+ $WaitForFile $PathNext;
+ } on-error={
+ $LogPrintExit2 warning $0 ("Making directory '" . $PathNext . "' failed!") false;
+ :set Error true;
+ }
+ /ip/smb/share/remove [ find where name=$Name ];
+ :if ($Error = true) do={
+ :return false;
+ }
+ }
+ }
+ :return true;
+}
+
+# prepare NotificationFunctions array
+:if ([ :typeof $NotificationFunctions ] != "array") do={
+ :set NotificationFunctions ({});
+}
+
+# parse key value store
+:set ParseKeyValueStore do={
+ :local Source $1;
+ :if ([ :typeof $Source ] != "array") do={
+ :set Source [ :tostr $1 ];
+ }
+ :local Result ({});
+ :foreach KeyValue in=[ :toarray $Source ] do={
+ :if ([ :find $KeyValue "=" ]) do={
+ :set ($Result->[ :pick $KeyValue 0 [ :find $KeyValue "=" ] ]) \
+ [ :pick $KeyValue ([ :find $KeyValue "=" ] + 1) [ :len $KeyValue ] ];
+ } else={
+ :set ($Result->$KeyValue) true;
+ }
+ }
+ :return $Result;
+}
+
+# print lines with trailing carriage return
+:set PrettyPrint do={
+ :local Input [ :tostr $1 ];
+
+ :global Unix2Dos;
+
+ :put [ $Unix2Dos $Input ];
+}
+
+# delay a random amount of seconds
+:set RandomDelay do={
+ :global EitherOr;
+ :global GetRandomNumber;
+
+ :delay ([ $GetRandomNumber $1 ] . [ $EitherOr $2 "s" ]);
+}
+
+# read input from user
+:set Read do={
+ :return;
+}
+
+# check for required RouterOS version
+:set RequiredRouterOS do={
+ :local Caller [ :tostr $1 ];
+ :local Required [ :tostr $2 ];
+ :local Warn [ :tostr $3 ];
+
+ :global IfThenElse;
+ :global LogPrintExit2;
+ :global VersionToNum;
+
+ :if (!($Required ~ "^\\d+\\.\\d+((beta|rc|\\.)\\d+|)\$")) do={
+ $LogPrintExit2 error $0 ("No valid RouterOS version: " . $Required) false;
+ :return false;
+ }
+
+ :if ([ $VersionToNum $Required ] > [ $VersionToNum [ /system/package/update/get installed-version ] ]) do={
+ :if ($Warn = "true") do={
+ $LogPrintExit2 warning $0 ("This " . [ $IfThenElse ([ :pick $Caller 0 ] = ("\$")) "function" "script" ] . \
+ " '" . $Caller . "' (at least specific functionality) requires RouterOS " . $Required . ". Please update!") false;
+ }
+ :return false;
+ }
+ :return true;
+}
+
+# check if script is run from terminal
+:set ScriptFromTerminal do={
+ :local Script [ :tostr $1 ];
+
+ :global LogPrintExit2;
+
+ :foreach Job in=[ /system/script/job/find where script=$Script ] do={
+ :set Job [ /system/script/job/get $Job ];
+ :while ([ :typeof ($Job->"parent") ] = "id") do={
+ :set Job [ /system/script/job/get [ find where .id=($Job->"parent") ] ];
+ }
+ :if (($Job->"type") = "login") do={
+ $LogPrintExit2 debug $0 ("Script " . $Script . " started from terminal.") false;
+ :return true;
+ }
+ }
+ $LogPrintExit2 debug $0 ("Script " . $Script . " NOT started from terminal.") false;
+
+ :return false;
+}
+
+# install new scripts, update existing scripts
+:set ScriptInstallUpdate do={
+ :local Scripts [ :toarray $1 ];
+ :local NewComment [ :tostr $2 ];
+
+ :global ExpectedConfigVersion;
+ :global Identity;
+ :global IDonate;
+ :global NoNewsAndChangesNotification;
+ :global NotificationsWithSymbols;
+ :global ScriptUpdatesBaseUrl;
+ :global ScriptUpdatesFetch;
+ :global ScriptUpdatesUrlSuffix;
+
+ :global CertificateAvailable;
+ :global EitherOr;
+ :global Grep;
+ :global IfThenElse;
+ :global LogPrintExit2;
+ :global ParseKeyValueStore;
+ :global RequiredRouterOS;
+ :global SendNotification2;
+ :global SymbolForNotification;
+ :global ValidateSyntax;
+
+ :if ([ $CertificateAvailable "R3" ] = false) do={
+ $LogPrintExit2 warning $0 ("Downloading certificate failed, trying without.") false;
+ }
+
+ :if ([ $CertificateAvailable "E1" ] = false) do={
+ $LogPrintExit2 warning $0 ("Downloading certificate failed, trying without.") false;
+ }
+
+ :foreach Script in=$Scripts do={
+ :if ([ :len [ /system/script/find where name=$Script ] ] = 0) do={
+ $LogPrintExit2 info $0 ("Adding new script: " . $Script) false;
+ /system/script/add name=$Script owner=$Script source="#!rsc by RouterOS\n" comment=$NewComment;
+ }
+ }
+
+ :local ExpectedConfigVersionBefore $ExpectedConfigVersion;
+ :local ReloadGlobalFunctions false;
+ :local ReloadGlobalConfig false;
+
+ :foreach Script in=[ /system/script/find where source~"^#!rsc by RouterOS\n" ] do={
+ :local ScriptVal [ /system/script/get $Script ];
+ :local ScriptFile [ /file/find where name=("script-updates/" . $ScriptVal->"name") . ".rsc" ];
+ :local SourceNew;
+ :if ([ :len $ScriptFile ] > 0) do={
+ :set SourceNew [ /file/get $ScriptFile contents ];
+ /file/remove $ScriptFile;
+ }
+
+ :foreach Scheduler in=[ /system/scheduler/find where on-event~("\\b" . $ScriptVal->"name" . "\\b") ] do={
+ :local SchedulerVal [ /system/scheduler/get $Scheduler ];
+ :if ($ScriptVal->"policy" != $SchedulerVal->"policy") do={
+ $LogPrintExit2 warning $0 ("Policies differ for script '" . $ScriptVal->"name" . \
+ "' and its scheduler '" . $SchedulerVal->"name" . "'!") false;
+ }
+ }
+
+ :if ([ :len $SourceNew ] = 0 && $ScriptUpdatesFetch = true) do={
+ :local Comment [ $ParseKeyValueStore ($ScriptVal->"comment") ];
+ :if (!($Comment->"ignore" = true)) do={
+ :do {
+ :local BaseUrl $ScriptUpdatesBaseUrl;
+ :local UrlSuffix $ScriptUpdatesUrlSuffix;
+ :if ([ :typeof ($Comment->"base-url") ] = "str") do={ :set BaseUrl ($Comment->"base-url"); }
+ :if ([ :typeof ($Comment->"url-suffix") ] = "str") do={ :set UrlSuffix ($Comment->"url-suffix"); }
+ :local Url ($BaseUrl . $ScriptVal->"name" . ".rsc" . $UrlSuffix);
+
+ $LogPrintExit2 debug $0 ("Fetching script '" . $ScriptVal->"name" . "' from url: " . $Url) false;
+ :local Result [ /tool/fetch check-certificate=yes-without-crl $Url output=user as-value ];
+ :if ($Result->"status" = "finished") do={
+ :set SourceNew ($Result->"data");
+ }
+ } on-error={
+ :if ($ScriptVal->"source" = "#!rsc by RouterOS\n") do={
+ $LogPrintExit2 warning $0 ("Failed fetching script '" . $ScriptVal->"name" . \
+ "', removing dummy. Typo on installation?") false;
+ /system/script/remove $Script;
+ } else={
+ $LogPrintExit2 warning $0 ("Failed fetching script '" . $ScriptVal->"name" . "'!") false;
+ }
+ }
+ }
+ }
+
+ :if ([ :len $SourceNew ] > 0) do={
+ :if ($SourceNew != $ScriptVal->"source") do={
+ :if ([ :pick $SourceNew 0 18 ] = "#!rsc by RouterOS\n") do={
+ :local Required ([ $ParseKeyValueStore [ $Grep $SourceNew "# requires RouterOS, " ] ]->"version");
+ :if ([ $RequiredRouterOS $0 [ $EitherOr $Required "0.0" ] false ] = true) do={
+ :if ([ $ValidateSyntax $SourceNew ] = true) do={
+ $LogPrintExit2 info $0 ("Updating script: " . $ScriptVal->"name") false;
+ /system/script/set owner=($ScriptVal->"name") source=$SourceNew $Script;
+ :if ($ScriptVal->"name" = "global-config") do={
+ :set ReloadGlobalConfig true;
+ }
+ :if ($ScriptVal->"name" = "global-functions" || $ScriptVal->"name" ~ ("^mod/.")) do={
+ :set ReloadGlobalFunctions true;
+ }
+ } else={
+ $LogPrintExit2 warning $0 ("Syntax validation for script '" . $ScriptVal->"name" . \
+ "' failed! Ignoring!") false;
+ }
+ } else={
+ $LogPrintExit2 warning $0 ("The script '" . $ScriptVal->"name" . "' requires RouterOS " . \
+ $Required . ", which is not met by your installation. Ignoring!") false;
+ }
+ } else={
+ $LogPrintExit2 warning $0 ("Looks like new script '" . $ScriptVal->"name" . \
+ "' is not valid (missing shebang). Ignoring!") false;
+ }
+ } else={
+ $LogPrintExit2 debug $0 ("Script '" . $ScriptVal->"name" . "' did not change.") false;
+ }
+ } else={
+ $LogPrintExit2 debug $0 ("No update for script '" . $ScriptVal->"name" . "'.") false;
+ }
+ }
+
+ :if ($ReloadGlobalFunctions = true) do={
+ $LogPrintExit2 info $0 ("Reloading global functions.") false;
+ :do {
+ /system/script/run global-functions;
+ } on-error={
+ $LogPrintExit2 error $0 ("Reloading global functions failed!") false;
+ }
+ }
+
+ :if ($ReloadGlobalConfig = true) do={
+ $LogPrintExit2 info $0 ("Reloading global configuration.") false;
+ :do {
+ /system/script/run global-config;
+ } on-error={
+ $LogPrintExit2 error $0 ("Reloading global configuration failed!" . \
+ " Syntax error or missing overlay\?") false;
+ }
+ }
+
+ :if ($ExpectedConfigVersionBefore > $ExpectedConfigVersion) do={
+ $LogPrintExit2 warning $0 ("The configuration version decreased from " . \
+ $ExpectedConfigVersionBefore . " to " . $ExpectedConfigVersion . \
+ ". Installed an older version?") false;
+ }
+
+ :if ($ExpectedConfigVersionBefore < $ExpectedConfigVersion) do={
+ :global GlobalConfigChanges;
+ :global GlobalConfigMigration;
+ :local ChangeLogCode;
+
+ :do {
+ :local Url ($ScriptUpdatesBaseUrl . "news-and-changes.rsc" . $ScriptUpdatesUrlSuffix);
+ $LogPrintExit2 debug $0 ("Fetching news, changes and migration: " . $Url) false;
+ :local Result [ /tool/fetch check-certificate=yes-without-crl $Url output=user as-value ];
+ :if ($Result->"status" = "finished") do={
+ :set ChangeLogCode ($Result->"data");
+ }
+ } on-error={
+ $LogPrintExit2 warning $0 ("Failed fetching news, changes and migration!") false;
+ }
+
+ :if ([ :len $ChangeLogCode ] > 0) do={
+ :if ([ $ValidateSyntax $ChangeLogCode ] = true) do={
+ :do {
+ [ :parse $ChangeLogCode ];
+ } on-error={
+ $LogPrintExit2 warning $0 ("The changelog failed to run!") false;
+ }
+ } else={
+ $LogPrintExit2 warning $0 ("The changelog failed syntax validation!") false;
+ }
+ }
+
+ :if ([ :len $GlobalConfigMigration ] > 0) do={
+ :for I from=($ExpectedConfigVersionBefore + 1) to=$ExpectedConfigVersion do={
+ :local Migration ($GlobalConfigMigration->[ :tostr $I ]);
+ :if ([ :typeof $Migration ] = "str") do={
+ :if ([ $ValidateSyntax $Migration ] = true) do={
+ $LogPrintExit2 info $0 ("Applying migration for change " . $I . ": " . $Migration) false;
+ :do {
+ [ :parse $Migration ];
+ } on-error={
+ $LogPrintExit2 warning $0 ("Migration code for change " . $I . " failed to run!") false;
+ }
+ } else={
+ $LogPrintExit2 warning $0 ("Migration code for change " . $I . " failed syntax validation!") false;
+ }
+ }
+ }
+ }
+
+ :local NotificationMessage ("The configuration version on " . $Identity . " increased " . \
+ "to " . $ExpectedConfigVersion . ", current configuration may need modification. " . \
+ "Please review and update global-config-overlay, then re-run global-config.");
+ $LogPrintExit2 info $0 ($NotificationMessage) false;
+
+ :if ([ :len $GlobalConfigChanges ] > 0) do={
+ :set NotificationMessage ($NotificationMessage . "\n\nChanges:");
+ :for I from=($ExpectedConfigVersionBefore + 1) to=$ExpectedConfigVersion do={
+ :local Change ($GlobalConfigChanges->[ :tostr $I ]);
+ :set NotificationMessage ($NotificationMessage . "\n " . \
+ [ $IfThenElse ($NotificationsWithSymbols = true) ("\E2\97\8F") "*" ] . " " . $Change);
+ $LogPrintExit2 info $0 ("Change " . $I . ": " . $Change) false;
+ }
+ } else={
+ :set NotificationMessage ($NotificationMessage . "\n\nNews and changes are not available.");
+ }
+
+ :if ($NoNewsAndChangesNotification != true) do={
+ :local Link;
+ :if ($IDonate != true) do={
+ :set NotificationMessage ($NotificationMessage . \
+ "\n\n==== donation hint ====\n" . \
+ "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 a donation.");
+ :set Link "https://git.eworm.de/cgit/routeros-scripts/about/#donate";
+ }
+
+ $SendNotification2 ({ origin=$0; \
+ subject=([ $SymbolForNotification "pushpin" ] . "News and configuration changes"); \
+ message=$NotificationMessage; link=$Link });
+ }
+
+ :set GlobalConfigChanges;
+ :set GlobalConfigMigration;
+ }
+}
+
+# lock script against multiple invocation
+:set ScriptLock do={
+ :local Script [ :tostr $1 ];
+ :local DoReturn $2;
+ :local WaitMax ([ :tonum $3 ] * 10);
+
+ :global GetRandom20CharAlNum;
+ :global IfThenElse;
+ :global LogPrintExit2;
+
+ :global ScriptLockOrder;
+ :if ([ :typeof $ScriptLockOrder ] = "nothing") do={
+ :set ScriptLockOrder ({});
+ }
+ :if ([ :typeof ($ScriptLockOrder->$Script) ] = "nothing") do={
+ :set ($ScriptLockOrder->$Script) ({});
+ }
+
+ :local JobCount do={
+ :local Script [ :tostr $1 ];
+
+ :return [ :len [ /system/script/job/find where script=$Script ] ];
+ }
+
+ :local TicketCount do={
+ :local Script [ :tostr $1 ];
+
+ :global ScriptLockOrder;
+
+ :local Count 0;
+ :foreach Ticket in=($ScriptLockOrder->$Script) do={
+ :if ([ :typeof $Ticket ] != "nothing") do={
+ :set Count ($Count + 1);
+ }
+ }
+ :return $Count;
+ }
+
+ :local IsFirstTicket do={
+ :local Script [ :tostr $1 ];
+ :local Check [ :tostr $2 ];
+
+ :global ScriptLockOrder;
+
+ :foreach Ticket in=($ScriptLockOrder->$Script) do={
+ :if ($Ticket = $Check) do={ :return true; }
+ :if ([ :typeof $Ticket ] != "nothing" && $Ticket != $Check) do={ :return false; }
+ }
+ :return false;
+ }
+
+ :local AddTicket do={
+ :local Script [ :tostr $1 ];
+ :local Add [ :tostr $2 ];
+
+ :global ScriptLockOrder;
+
+ :while (true) do={
+ :local Pos [ :len ($ScriptLockOrder->$Script) ];
+ :set ($ScriptLockOrder->$Script->$Pos) $Add;
+ :delay 10ms;
+ :if (($ScriptLockOrder->$Script->$Pos) = $Add) do={ :return true; }
+ }
+ }
+
+ :local RemoveTicket do={
+ :local Script [ :tostr $1 ];
+ :local Remove [ :tostr $2 ];
+
+ :global ScriptLockOrder;
+
+ :foreach Id,Ticket in=($ScriptLockOrder->$Script) do={
+ :while (($ScriptLockOrder->$Script->$Id) = $Remove) do={
+ :set ($ScriptLockOrder->$Script->$Id);
+ :delay 10ms;
+ }
+ }
+ }
+
+ :local CleanupTickets do={
+ :local Script [ :tostr $1 ];
+
+ :global ScriptLockOrder;
+
+ :foreach Ticket in=($ScriptLockOrder->$Script) do={
+ :if ([ :typeof $Ticket ] != "nothing") do={
+ :return false;
+ }
+ }
+
+ :set ($ScriptLockOrder->$Script) ({});
+ }
+
+ :if ([ :len [ /system/script/find where name=$Script ] ] = 0) do={
+ $LogPrintExit2 error $0 ("A script named '" . $Script . "' does not exist!") true;
+ }
+
+ :if ([ $JobCount $Script ] = 0) do={
+ $LogPrintExit2 error $0 ("No script '" . $Script . "' is running!") true;
+ }
+
+ :if ([ $TicketCount $Script ] >= [ $JobCount $Script ]) do={
+ $LogPrintExit2 error $0 ("More tickets than running scripts '" . $Script . "', resetting!") false;
+ :set ($ScriptLockOrder->$Script) ({});
+ /system/script/job/remove [ find where script=$Script ];
+ }
+
+ :local MyTicket [ $GetRandom20CharAlNum 6 ];
+ $AddTicket $Script $MyTicket;
+
+ :local WaitCount 0;
+ :while ($WaitMax > $WaitCount && ([ $IsFirstTicket $Script $MyTicket ] = false || [ $TicketCount $Script ] < [ $JobCount $Script ])) do={
+ :set WaitCount ($WaitCount + 1);
+ :delay 100ms;
+ }
+
+ :if ([ $IsFirstTicket $Script $MyTicket ] = true && [ $TicketCount $Script ] = [ $JobCount $Script ]) do={
+ $RemoveTicket $Script $MyTicket;
+ $CleanupTickets $Script;
+ :return false;
+ }
+
+ $RemoveTicket $Script $MyTicket;
+ $LogPrintExit2 info $0 ("Script '" . $Script . "' started more than once" . [ $IfThenElse ($WaitCount > 0) \
+ " and timed out waiting for lock" "" ] . "... Aborting.") [ $IfThenElse ($DoReturn = true) false true ];
+ :return true;
+}
+
+# send notification via NotificationFunctions - expects at least two string arguments
+:set SendNotification do={
+ :global SendNotification2;
+
+ $SendNotification2 ({ subject=$1; message=$2; link=$3; silent=$4 });
+}
+
+# send notification via NotificationFunctions - expects one array argument
+:set SendNotification2 do={
+ :local Notification $1;
+
+ :global NotificationFunctions;
+
+ :foreach FunctionName,Discard in=$NotificationFunctions do={
+ ($NotificationFunctions->$FunctionName) \
+ ("\$NotificationFunctions->\"" . $FunctionName . "\"") \
+ $Notification;
+ }
+}
+
+# return UTF-8 symbol for unicode name
+:set SymbolByUnicodeName do={
+ :local Symbols {
+ "abacus"="\F0\9F\A7\AE";
+ "alarm-clock"="\E2\8F\B0";
+ "calendar"="\F0\9F\93\85";
+ "card-file-box"="\F0\9F\97\83";
+ "chart-decreasing"="\F0\9F\93\89";
+ "chart-increasing"="\F0\9F\93\88";
+ "cloud"="\E2\98\81";
+ "cross-mark"="\E2\9D\8C";
+ "earth"="\F0\9F\8C\8D";
+ "fire"="\F0\9F\94\A5";
+ "floppy-disk"="\F0\9F\92\BE";
+ "high-voltage-sign"="\E2\9A\A1";
+ "incoming-envelope"="\F0\9F\93\A8";
+ "link"="\F0\9F\94\97";
+ "lock-with-ink-pen"="\F0\9F\94\8F";
+ "memo"="\F0\9F\93\9D";
+ "mobile-phone"="\F0\9F\93\B1";
+ "pushpin"="\F0\9F\93\8C";
+ "scissors"="\E2\9C\82";
+ "sparkles"="\E2\9C\A8";
+ "speech-balloon"="\F0\9F\92\AC";
+ "up-arrow"="\E2\AC\86";
+ "warning-sign"="\E2\9A\A0";
+ "white-heavy-check-mark"="\E2\9C\85"
+ }
+
+ :return (($Symbols->$1) . "\EF\B8\8F");
+}
+
+# return symbol for notification
+:set SymbolForNotification do={
+ :global NotificationsWithSymbols;
+ :global SymbolByUnicodeName;
+
+ :if ($NotificationsWithSymbols != true) do={
+ :return "";
+ }
+ :local Return "";
+ :foreach Symbol in=[ :toarray $1 ] do={
+ :set Return ($Return . [ $SymbolByUnicodeName $Symbol ]);
+ }
+ :return ($Return . " ");
+}
+
+# convert line endings, UNIX -> DOS
+:set Unix2Dos do={
+ :local Input [ :tostr $1 ];
+
+ :global CharacterReplace;
+
+ :return [ $CharacterReplace [ $CharacterReplace $Input \
+ ("\n") ("\r\n") ] ("\r\r\n") ("\r\n") ];
+}
+
+# url encoding
+:set UrlEncode do={
+ :local Input [ :tostr $1 ];
+
+ :if ([ :len $Input ] = 0) do={
+ :return "";
+ }
+
+ :local Return "";
+ :local Chars ("\n\r !\"#\$%&'()*+,:;<=>\?@[\\]^`{|}~");
+ :local Subs { "%0A"; "%0D"; "%20"; "%21"; "%22"; "%23"; "%24"; "%25"; "%26"; "%27";
+ "%28"; "%29"; "%2A"; "%2B"; "%2C"; "%3A"; "%3B"; "%3C"; "%3D"; "%3E"; "%3F";
+ "%40"; "%5B"; "%5C"; "%5D"; "%5E"; "%60"; "%7B"; "%7C"; "%7D"; "%7E" };
+
+ :for I from=0 to=([ :len $Input ] - 1) do={
+ :local Char [ :pick $Input $I ];
+ :local Replace [ :find $Chars $Char ];
+
+ :if ([ :typeof $Replace ] = "num") do={
+ :set Char ($Subs->$Replace);
+ }
+ :set Return ($Return . $Char);
+ }
+
+ :return $Return;
+}
+
+# basic syntax validation
+:set ValidateSyntax do={
+ :local Code [ :tostr $1 ];
+
+ :do {
+ [ :parse (":local Validate do={\n" . $Code . "\n}") ];
+ } on-error={
+ :return false;
+ }
+ :return true;
+}
+
+# convert version string to numeric value
+:set VersionToNum do={
+ :local Input [ :tostr $1 ];
+ :local Multi 0x1000000;
+ :local Return 0;
+
+ :global CharacterReplace;
+
+ :set Input [ $CharacterReplace [ $CharacterReplace [ $CharacterReplace $Input \
+ "." "," ] "beta" ",beta," ] "rc" ",rc," ];
+
+ :foreach Value in=([ :toarray $Input ], 0) do={
+ :local Num [ :tonum $Value ];
+ :if ($Multi = 0x100) do={
+ :if ([ :typeof $Num ] = "num") do={
+ :set Return ($Return + 0xff00);
+ :set Multi ($Multi / 0x100);
+ } else={
+ :if ($Value = "beta") do={ :set Return ($Return + 0x3f00); }
+ :if ($Value = "rc") do={ :set Return ($Return + 0x7f00); }
+ }
+ }
+ :if ([ :typeof $Num ] = "num") do={ :set Return ($Return + ($Value * $Multi)); }
+ :set Multi ($Multi / 0x100);
+ }
+
+ :return $Return;
+}
+
+# wait for default route to be reachable
+:set WaitDefaultRouteReachable do={
+ :global IsDefaultRouteReachable;
+
+ :while ([ $IsDefaultRouteReachable ] = false) do={
+ :delay 1s;
+ }
+}
+
+# wait for DNS to resolve
+:set WaitDNSResolving do={
+ :global IsDNSResolving;
+
+ :while ([ $IsDNSResolving ] = false) do={
+ :delay 1s;
+ }
+}
+
+# wait for file to be available
+:set WaitForFile do={
+ :local FileName [ :tostr $1 ];
+ :local WaitTime [ :totime $2 ];
+
+ :global CleanFilePath;
+ :global EitherOr;
+
+ :set FileName [ $CleanFilePath $FileName ];
+ :local I 1;
+ :local Delay ([ :totime [ $EitherOr $WaitTime 2s ] ] / 20);
+
+ :while ([ :len [ /file/find where name=$FileName ] ] = 0) do={
+ :if ($I >= 20) do={
+ :return false;
+ }
+ :delay $Delay;
+ :set I ($I + 1);
+ }
+ :return true;
+}
+
+# wait to be fully connected (default route is reachable, time is sync, DNS resolves)
+:set WaitFullyConnected do={
+ :global WaitDefaultRouteReachable;
+ :global WaitDNSResolving;
+ :global WaitTimeSync;
+
+ $WaitDefaultRouteReachable;
+ $WaitTimeSync;
+ $WaitDNSResolving;
+}
+
+# wait for time to become synced
+:set WaitTimeSync do={
+ :global IsTimeSync;
+
+ :while ([ $IsTimeSync ] = false) do={
+ :delay 1s;
+ }
+}
+
+# load modules
+:foreach Script in=[ /system/script/find where name ~ "^mod/." ] do={
+ :local ScriptVal [ /system/script/get $Script ];
+ :if ([ $ValidateSyntax ($ScriptVal->"source") ] = true) do={
+ :do {
+ /system/script/run $Script;
+ } on-error={
+ $LogPrintExit2 error $0 ("Module '" . $ScriptVal->"name" . "' failed to run.") false;
+ }
+ } else={
+ $LogPrintExit2 error $0 ("Module '" . $ScriptVal->"name" . "' failed syntax validation, skipping.") false;
+ }
+}
+
+# signal we are ready
+:set GlobalFunctionsReady true;
diff --git a/global-wait b/global-wait
index fe1928b..2da00ca 100644
--- a/global-wait
+++ b/global-wait
@@ -1,11 +1,3 @@
#!rsc by RouterOS
-# RouterOS script: global-wait
-# Copyright (c) 2020-2023 Christian Hesse <mail@eworm.de>
-# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md
#
-# wait for global-functions to finish
-# https://git.eworm.de/cgit/routeros-scripts/about/doc/global-wait.md
-
-:local 0 "global-wait";
-:global GlobalFunctionsReady;
-:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
+# dummy for migration
diff --git a/global-wait.rsc b/global-wait.rsc
new file mode 100644
index 0000000..fe1928b
--- /dev/null
+++ b/global-wait.rsc
@@ -0,0 +1,11 @@
+#!rsc by RouterOS
+# RouterOS script: global-wait
+# Copyright (c) 2020-2023 Christian Hesse <mail@eworm.de>
+# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md
+#
+# wait for global-functions to finish
+# https://git.eworm.de/cgit/routeros-scripts/about/doc/global-wait.md
+
+:local 0 "global-wait";
+:global GlobalFunctionsReady;
+:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
diff --git a/gps-track b/gps-track
index d0d1232..2da00ca 100644
--- a/gps-track
+++ b/gps-track
@@ -1,34 +1,3 @@
#!rsc by RouterOS
-# RouterOS script: gps-track
-# Copyright (c) 2018-2023 Christian Hesse <mail@eworm.de>
-# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md
#
-# track gps data by sending json data to http server
-# https://git.eworm.de/cgit/routeros-scripts/about/doc/gps-track.md
-
-:local 0 "gps-track";
-:global GlobalFunctionsReady;
-:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
-
-:global GpsTrackUrl;
-:global Identity;
-
-:global LogPrintExit2;
-
-:local CoordinateFormat [ /system/gps/get coordinate-format ];
-:local Gps [ /system/gps/monitor once as-value ];
-
-:if ($Gps->"valid" = true) do={
- /tool/fetch check-certificate=yes-without-crl $GpsTrackUrl output=none \
- http-method=post http-header-field="Content-Type: application/json" \
- http-data=("{" . \
- "\"lat\":\"" . ($Gps->"latitude") . "\"," . \
- "\"lon\":\"" . ($Gps->"longitude") . "\"," . \
- "\"identity\":\"" . $Identity . "\"" . \
- "}") as-value;
- $LogPrintExit2 debug $0 ("Sending GPS data in " . $CoordinateFormat . " format: " . \
- "lat: " . ($Gps->"latitude") . " " . \
- "lon: " . ($Gps->"longitude")) false;
-} else={
- $LogPrintExit2 debug $0 ("GPS data not valid.") false;
-}
+# dummy for migration
diff --git a/gps-track.rsc b/gps-track.rsc
new file mode 100644
index 0000000..d0d1232
--- /dev/null
+++ b/gps-track.rsc
@@ -0,0 +1,34 @@
+#!rsc by RouterOS
+# RouterOS script: gps-track
+# Copyright (c) 2018-2023 Christian Hesse <mail@eworm.de>
+# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md
+#
+# track gps data by sending json data to http server
+# https://git.eworm.de/cgit/routeros-scripts/about/doc/gps-track.md
+
+:local 0 "gps-track";
+:global GlobalFunctionsReady;
+:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
+
+:global GpsTrackUrl;
+:global Identity;
+
+:global LogPrintExit2;
+
+:local CoordinateFormat [ /system/gps/get coordinate-format ];
+:local Gps [ /system/gps/monitor once as-value ];
+
+:if ($Gps->"valid" = true) do={
+ /tool/fetch check-certificate=yes-without-crl $GpsTrackUrl output=none \
+ http-method=post http-header-field="Content-Type: application/json" \
+ http-data=("{" . \
+ "\"lat\":\"" . ($Gps->"latitude") . "\"," . \
+ "\"lon\":\"" . ($Gps->"longitude") . "\"," . \
+ "\"identity\":\"" . $Identity . "\"" . \
+ "}") as-value;
+ $LogPrintExit2 debug $0 ("Sending GPS data in " . $CoordinateFormat . " format: " . \
+ "lat: " . ($Gps->"latitude") . " " . \
+ "lon: " . ($Gps->"longitude")) false;
+} else={
+ $LogPrintExit2 debug $0 ("GPS data not valid.") false;
+}
diff --git a/hotspot-to-wpa b/hotspot-to-wpa
index dbce9ff..2da00ca 100644
--- a/hotspot-to-wpa
+++ b/hotspot-to-wpa
@@ -1,72 +1,3 @@
#!rsc by RouterOS
-# RouterOS script: hotspot-to-wpa
-# Copyright (c) 2019-2023 Christian Hesse <mail@eworm.de>
-# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md
#
-# add private WPA passphrase after hotspot login
-# https://git.eworm.de/cgit/routeros-scripts/about/doc/hotspot-to-wpa.md
-
-:local 0 "hotspot-to-wpa";
-:global GlobalFunctionsReady;
-:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
-
-:global EitherOr;
-:global LogPrintExit2;
-:global ParseKeyValueStore;
-
-:local MacAddress $"mac-address";
-:local UserName $username;
-:local Date [ /system/clock/get date ];
-:local UserVal [ /ip/hotspot/user/get [ find where name=$UserName ] ];
-:local UserInfo [ $ParseKeyValueStore ($UserVal->"comment") ];
-:local Hotspot [ /ip/hotspot/host/get [ find where mac-address=$MacAddress authorized ] server ];
-
-:if ([ :len [ /caps-man/access-list/find where comment="--- hotspot-to-wpa above ---" disabled ] ] = 0) do={
- /caps-man/access-list/add comment="--- hotspot-to-wpa above ---" disabled=yes;
- $LogPrintExit2 warning $0 ("Added disabled access-list entry with comment '--- hotspot-to-wpa above ---'.") false;
-}
-:local PlaceBefore ([ /caps-man/access-list/find where comment="--- hotspot-to-wpa above ---" disabled ]->0);
-
-:if ([ :len [ /caps-man/access-list/find where \
- comment=("hotspot-to-wpa template " . $Hotspot) disabled ] ] = 0) do={
- /caps-man/access-list/add comment=("hotspot-to-wpa template " . $Hotspot) disabled=yes place-before=$PlaceBefore;
- $LogPrintExit2 warning $0 ("Added template in access-list for hotspot '" . $Hotspot . "'.") false;
-}
-:local Template [ /caps-man/access-list/get ([ find where \
- comment=("hotspot-to-wpa template " . $Hotspot) disabled ]->0) ];
-
-:if ($Template->"action" = "reject") do={
- $LogPrintExit2 info $0 ("Ignoring login for hotspot '" . $Hotspot . "'.") true;
-}
-
-# allow login page to load
-:delay 1s;
-
-$LogPrintExit2 info $0 ("Adding/updating access-list entry for mac address " . $MacAddress . \
- " (user " . $UserName . ").") false;
-/caps-man/access-list/remove [ find where mac-address=$MacAddress comment~"^hotspot-to-wpa: " ];
-/caps-man/access-list/add comment=("hotspot-to-wpa: " . $UserName . ", " . $MacAddress . ", " . $Date) \
- mac-address=$MacAddress private-passphrase=($UserVal->"password") ssid-regexp="-wpa\$" place-before=$PlaceBefore;
-
-:local Entry [ /caps-man/access-list/find where mac-address=$MacAddress \
- comment=("hotspot-to-wpa: " . $UserName . ", " . $MacAddress . ", " . $Date) ];
-:local PrivatePassphrase [ $EitherOr ($UserInfo->"private-passphrase") ($Template->"private-passphrase") ];
-:if ([ :len $PrivatePassphrase ] > 0) do={
- :if ($PrivatePassphrase = "ignore") do={
- /caps-man/access-list/set $Entry !private-passphrase;
- } else={
- /caps-man/access-list/set $Entry private-passphrase=$PrivatePassphrase;
- }
-}
-:local SsidRegexp [ $EitherOr ($UserInfo->"ssid-regexp") ($Template->"ssid-regexp") ];
-:if ([ :len $SsidRegexp ] > 0) do={
- /caps-man/access-list/set $Entry ssid-regexp=$SsidRegexp;
-}
-:local VlanId [ $EitherOr ($UserInfo->"vlan-id") ($Template->"vlan-id") ];
-:if ([ :len $VlanId ] > 0) do={
- /caps-man/access-list/set $Entry vlan-id=$VlanId;
-}
-:local VlanMode [ $EitherOr ($UserInfo->"vlan-mode") ($Template->"vlan-mode") ];
-:if ([ :len $VlanMode] > 0) do={
- /caps-man/access-list/set $Entry vlan-mode=$VlanMode;
-}
+# dummy for migration
diff --git a/hotspot-to-wpa-cleanup b/hotspot-to-wpa-cleanup
index 15f63f9..2da00ca 100644
--- a/hotspot-to-wpa-cleanup
+++ b/hotspot-to-wpa-cleanup
@@ -1,51 +1,3 @@
#!rsc by RouterOS
-# RouterOS script: hotspot-to-wpa-cleanup
-# Copyright (c) 2021-2023 Christian Hesse <mail@eworm.de>
-# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md
#
-# provides: lease-script, order=80
-#
-# manage and clean up private WPA passphrase after hotspot login
-# https://git.eworm.de/cgit/routeros-scripts/about/doc/hotspot-to-wpa.md
-
-:local 0 "hotspot-to-wpa-cleanup";
-:global GlobalFunctionsReady;
-:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
-
-:global LogPrintExit2;
-:global ScriptLock;
-
-$ScriptLock $0 false 10;
-
-:foreach Client in=[ /caps-man/registration-table/find where comment~"^hotspot-to-wpa:" ] do={
- :local ClientVal [ /caps-man/registration-table/get $Client ];
- :local Lease [ /ip/dhcp-server/lease/find where server~"wpa" dynamic \
- mac-address=($ClientVal->"mac-address") ];
- :if ([ :len $Lease ] > 0) do={
- $LogPrintExit2 info $0 ("Client with mac address " . ($ClientVal->"mac-address") . \
- " connected to WPA, making lease static.") false;
- /ip/dhcp-server/lease/make-static $Lease;
- /ip/dhcp-server/lease/set comment=($ClientVal->"comment") $Lease;
- }
-}
-
-:foreach Client in=[ /caps-man/access-list/find where comment~"^hotspot-to-wpa:" and \
- !(comment~[ /system/clock/get date ]) ] do={
- :local ClientVal [ /caps-man/access-list/get $Client ];
- :if ([ :len [ /ip/dhcp-server/lease/find where server~"wpa" !dynamic \
- mac-address=($ClientVal->"mac-address") ] ] = 0) do={
- $LogPrintExit2 info $0 ("Client with mac address " . ($ClientVal->"mac-address") . \
- " did not connect to WPA, removing from access list.") false;
- /caps-man/access-list/remove $Client;
- }
-}
-
-:foreach Lease in=[ /ip/dhcp-server/lease/find where !dynamic status=waiting \
- last-seen>4w comment~"^hotspot-to-wpa:" ] do={
- :local LeaseVal [ /ip/dhcp-server/lease/get $Lease ];
- $LogPrintExit2 info $0 ("Client with mac address " . ($LeaseVal->"mac-address") . \
- " was not seen for long time, removing.") false;
- /caps-man/access-list/remove [ find where comment~"^hotspot-to-wpa:" \
- mac-address=($LeaseVal->"mac-address") ];
- /ip/dhcp-server/lease/remove $Lease;
-}
+# dummy for migration
diff --git a/hotspot-to-wpa-cleanup.rsc b/hotspot-to-wpa-cleanup.rsc
new file mode 100644
index 0000000..15f63f9
--- /dev/null
+++ b/hotspot-to-wpa-cleanup.rsc
@@ -0,0 +1,51 @@
+#!rsc by RouterOS
+# RouterOS script: hotspot-to-wpa-cleanup
+# Copyright (c) 2021-2023 Christian Hesse <mail@eworm.de>
+# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md
+#
+# provides: lease-script, order=80
+#
+# manage and clean up private WPA passphrase after hotspot login
+# https://git.eworm.de/cgit/routeros-scripts/about/doc/hotspot-to-wpa.md
+
+:local 0 "hotspot-to-wpa-cleanup";
+:global GlobalFunctionsReady;
+:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
+
+:global LogPrintExit2;
+:global ScriptLock;
+
+$ScriptLock $0 false 10;
+
+:foreach Client in=[ /caps-man/registration-table/find where comment~"^hotspot-to-wpa:" ] do={
+ :local ClientVal [ /caps-man/registration-table/get $Client ];
+ :local Lease [ /ip/dhcp-server/lease/find where server~"wpa" dynamic \
+ mac-address=($ClientVal->"mac-address") ];
+ :if ([ :len $Lease ] > 0) do={
+ $LogPrintExit2 info $0 ("Client with mac address " . ($ClientVal->"mac-address") . \
+ " connected to WPA, making lease static.") false;
+ /ip/dhcp-server/lease/make-static $Lease;
+ /ip/dhcp-server/lease/set comment=($ClientVal->"comment") $Lease;
+ }
+}
+
+:foreach Client in=[ /caps-man/access-list/find where comment~"^hotspot-to-wpa:" and \
+ !(comment~[ /system/clock/get date ]) ] do={
+ :local ClientVal [ /caps-man/access-list/get $Client ];
+ :if ([ :len [ /ip/dhcp-server/lease/find where server~"wpa" !dynamic \
+ mac-address=($ClientVal->"mac-address") ] ] = 0) do={
+ $LogPrintExit2 info $0 ("Client with mac address " . ($ClientVal->"mac-address") . \
+ " did not connect to WPA, removing from access list.") false;
+ /caps-man/access-list/remove $Client;
+ }
+}
+
+:foreach Lease in=[ /ip/dhcp-server/lease/find where !dynamic status=waiting \
+ last-seen>4w comment~"^hotspot-to-wpa:" ] do={
+ :local LeaseVal [ /ip/dhcp-server/lease/get $Lease ];
+ $LogPrintExit2 info $0 ("Client with mac address " . ($LeaseVal->"mac-address") . \
+ " was not seen for long time, removing.") false;
+ /caps-man/access-list/remove [ find where comment~"^hotspot-to-wpa:" \
+ mac-address=($LeaseVal->"mac-address") ];
+ /ip/dhcp-server/lease/remove $Lease;
+}
diff --git a/hotspot-to-wpa.rsc b/hotspot-to-wpa.rsc
new file mode 100644
index 0000000..dbce9ff
--- /dev/null
+++ b/hotspot-to-wpa.rsc
@@ -0,0 +1,72 @@
+#!rsc by RouterOS
+# RouterOS script: hotspot-to-wpa
+# Copyright (c) 2019-2023 Christian Hesse <mail@eworm.de>
+# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md
+#
+# add private WPA passphrase after hotspot login
+# https://git.eworm.de/cgit/routeros-scripts/about/doc/hotspot-to-wpa.md
+
+:local 0 "hotspot-to-wpa";
+:global GlobalFunctionsReady;
+:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
+
+:global EitherOr;
+:global LogPrintExit2;
+:global ParseKeyValueStore;
+
+:local MacAddress $"mac-address";
+:local UserName $username;
+:local Date [ /system/clock/get date ];
+:local UserVal [ /ip/hotspot/user/get [ find where name=$UserName ] ];
+:local UserInfo [ $ParseKeyValueStore ($UserVal->"comment") ];
+:local Hotspot [ /ip/hotspot/host/get [ find where mac-address=$MacAddress authorized ] server ];
+
+:if ([ :len [ /caps-man/access-list/find where comment="--- hotspot-to-wpa above ---" disabled ] ] = 0) do={
+ /caps-man/access-list/add comment="--- hotspot-to-wpa above ---" disabled=yes;
+ $LogPrintExit2 warning $0 ("Added disabled access-list entry with comment '--- hotspot-to-wpa above ---'.") false;
+}
+:local PlaceBefore ([ /caps-man/access-list/find where comment="--- hotspot-to-wpa above ---" disabled ]->0);
+
+:if ([ :len [ /caps-man/access-list/find where \
+ comment=("hotspot-to-wpa template " . $Hotspot) disabled ] ] = 0) do={
+ /caps-man/access-list/add comment=("hotspot-to-wpa template " . $Hotspot) disabled=yes place-before=$PlaceBefore;
+ $LogPrintExit2 warning $0 ("Added template in access-list for hotspot '" . $Hotspot . "'.") false;
+}
+:local Template [ /caps-man/access-list/get ([ find where \
+ comment=("hotspot-to-wpa template " . $Hotspot) disabled ]->0) ];
+
+:if ($Template->"action" = "reject") do={
+ $LogPrintExit2 info $0 ("Ignoring login for hotspot '" . $Hotspot . "'.") true;
+}
+
+# allow login page to load
+:delay 1s;
+
+$LogPrintExit2 info $0 ("Adding/updating access-list entry for mac address " . $MacAddress . \
+ " (user " . $UserName . ").") false;
+/caps-man/access-list/remove [ find where mac-address=$MacAddress comment~"^hotspot-to-wpa: " ];
+/caps-man/access-list/add comment=("hotspot-to-wpa: " . $UserName . ", " . $MacAddress . ", " . $Date) \
+ mac-address=$MacAddress private-passphrase=($UserVal->"password") ssid-regexp="-wpa\$" place-before=$PlaceBefore;
+
+:local Entry [ /caps-man/access-list/find where mac-address=$MacAddress \
+ comment=("hotspot-to-wpa: " . $UserName . ", " . $MacAddress . ", " . $Date) ];
+:local PrivatePassphrase [ $EitherOr ($UserInfo->"private-passphrase") ($Template->"private-passphrase") ];
+:if ([ :len $PrivatePassphrase ] > 0) do={
+ :if ($PrivatePassphrase = "ignore") do={
+ /caps-man/access-list/set $Entry !private-passphrase;
+ } else={
+ /caps-man/access-list/set $Entry private-passphrase=$PrivatePassphrase;
+ }
+}
+:local SsidRegexp [ $EitherOr ($UserInfo->"ssid-regexp") ($Template->"ssid-regexp") ];
+:if ([ :len $SsidRegexp ] > 0) do={
+ /caps-man/access-list/set $Entry ssid-regexp=$SsidRegexp;
+}
+:local VlanId [ $EitherOr ($UserInfo->"vlan-id") ($Template->"vlan-id") ];
+:if ([ :len $VlanId ] > 0) do={
+ /caps-man/access-list/set $Entry vlan-id=$VlanId;
+}
+:local VlanMode [ $EitherOr ($UserInfo->"vlan-mode") ($Template->"vlan-mode") ];
+:if ([ :len $VlanMode] > 0) do={
+ /caps-man/access-list/set $Entry vlan-mode=$VlanMode;
+}
diff --git a/ip-addr-bridge b/ip-addr-bridge
index 99fcba5..2da00ca 100644
--- a/ip-addr-bridge
+++ b/ip-addr-bridge
@@ -1,18 +1,3 @@
#!rsc by RouterOS
-# RouterOS script: ip-addr-bridge
-# Copyright (c) 2018-2023 Christian Hesse <mail@eworm.de>
-# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md
#
-# enable or disable ip addresses based on bridge port state
-# https://git.eworm.de/cgit/routeros-scripts/about/doc/ip-addr-bridge.md
-
-:foreach Bridge in=[ /interface/bridge/find ] do={
- :local BrName [ /interface/bridge/get $Bridge name ];
- :if ([ :len [ /interface/bridge/port/find where bridge=$BrName ] ] > 0) do={
- :if ([ :len [ /interface/bridge/port/find where bridge=$BrName and inactive=no ] ] = 0) do={
- /ip/address/disable [ find where !dynamic interface=$BrName ];
- } else={
- /ip/address/enable [ find where !dynamic interface=$BrName ];
- }
- }
-}
+# dummy for migration
diff --git a/ip-addr-bridge.rsc b/ip-addr-bridge.rsc
new file mode 100644
index 0000000..99fcba5
--- /dev/null
+++ b/ip-addr-bridge.rsc
@@ -0,0 +1,18 @@
+#!rsc by RouterOS
+# RouterOS script: ip-addr-bridge
+# Copyright (c) 2018-2023 Christian Hesse <mail@eworm.de>
+# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md
+#
+# enable or disable ip addresses based on bridge port state
+# https://git.eworm.de/cgit/routeros-scripts/about/doc/ip-addr-bridge.md
+
+:foreach Bridge in=[ /interface/bridge/find ] do={
+ :local BrName [ /interface/bridge/get $Bridge name ];
+ :if ([ :len [ /interface/bridge/port/find where bridge=$BrName ] ] > 0) do={
+ :if ([ :len [ /interface/bridge/port/find where bridge=$BrName and inactive=no ] ] = 0) do={
+ /ip/address/disable [ find where !dynamic interface=$BrName ];
+ } else={
+ /ip/address/enable [ find where !dynamic interface=$BrName ];
+ }
+ }
+}
diff --git a/ipsec-to-dns b/ipsec-to-dns
index 530c714..2da00ca 100644
--- a/ipsec-to-dns
+++ b/ipsec-to-dns
@@ -1,69 +1,3 @@
#!rsc by RouterOS
-# RouterOS script: ipsec-to-dns
-# Copyright (c) 2021-2023 Christian Hesse <mail@eworm.de>
-# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md
#
-# and add/remove/update DNS entries from IPSec mode-config
-# https://git.eworm.de/cgit/routeros-scripts/about/doc/ipsec-to-dns.md
-
-:local 0 "ipsec-to-dns";
-:global GlobalFunctionsReady;
-:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
-
-:global Domain;
-:global HostNameInZone;
-:global Identity;
-:global PrefixInZone;
-
-:global CharacterReplace;
-:global EscapeForRegEx;
-:global IfThenElse;
-:global LogPrintExit2;
-
-:local Zone \
- ([ $IfThenElse ($PrefixInZone = true) "ipsec." ] . \
- [ $IfThenElse ($HostNameInZone = true) ($Identity . ".") ] . $Domain);
-:local Ttl 5m;
-:local CommentPrefix ("managed by " . $0 . " for ");
-:local CommentString ("--- " . $0 . " above ---");
-
-:if ([ :len [ /ip/dns/static/find where comment=$CommentString name=- type=NXDOMAIN disabled ] ] = 0) do={
- /ip/dns/static/add comment=$CommentString name=- type=NXDOMAIN disabled=yes;
- $LogPrintExit2 warning $0 ("Added disabled static dns record with comment '" . $CommentString . "'.") false;
-}
-:local PlaceBefore ([ /ip/dns/static/find where comment=$CommentString name=- type=NXDOMAIN disabled ]->0);
-
-:foreach DnsRecord in=[ /ip/dns/static/find where comment ~ $CommentPrefix ] do={
- :local DnsRecordVal [ /ip/dns/static/get $DnsRecord ];
- :local PeerId [ $CharacterReplace ($DnsRecordVal->"comment") $CommentPrefix "" ];
- :if ([ :len [ /ip/ipsec/active-peers/find where id~("^(CN=)?" . [ $EscapeForRegEx $PeerId ] . "\$") \
- dynamic-address=($DnsRecordVal->"address") ] ] > 0) do={
- $LogPrintExit2 debug $0 ("Peer " . $PeerId . " (" . $DnsRecordVal->"name" . ") still exists. Not deleting DNS entry.") false;
- } else={
- :local Found false;
- $LogPrintExit2 info $0 ("Peer " . $PeerId . " (" . $DnsRecordVal->"name" . ") has gone, deleting DNS entry.") false;
- /ip/dns/static/remove $DnsRecord;
- }
-}
-
-:foreach Peer in=[ /ip/ipsec/active-peers/find where !(dynamic-address=[]) ] do={
- :local PeerVal [ /ip/ipsec/active-peers/get $Peer ];
- :local PeerId [ $CharacterReplace ($PeerVal->"id") "CN=" "" ];
- :local Comment ($CommentPrefix . $PeerId);
- :local HostName [ :pick $PeerId 0 [ :find ($PeerId . ".") "." ] ];
-
- :local Fqdn ($HostName . "." . $Zone);
- :local DnsRecord [ /ip/dns/static/find where name=$Fqdn ];
- :if ([ :len $DnsRecord ] > 0) do={
- :local DnsIp [ /ip/dns/static/get $DnsRecord address ];
- :if ($DnsIp = $PeerVal->"dynamic-address") do={
- $LogPrintExit2 debug $0 ("DNS entry for " . $Fqdn . " does not need updating.") false;
- } else={
- $LogPrintExit2 info $0 ("Replacing DNS entry for " . $Fqdn . ", new address is " . $PeerVal->"dynamic-address" . ".") false;
- /ip/dns/static/set name=$Fqdn address=($PeerVal->"dynamic-address") ttl=$Ttl comment=$Comment $DnsRecord;
- }
- } else={
- $LogPrintExit2 info $0 ("Adding new DNS entry for " . $Fqdn . ", address is " . $PeerVal->"dynamic-address" . ".") false;
- /ip/dns/static/add name=$Fqdn address=($PeerVal->"dynamic-address") ttl=$Ttl comment=$Comment place-before=$PlaceBefore;
- }
-}
+# dummy for migration
diff --git a/ipsec-to-dns.rsc b/ipsec-to-dns.rsc
new file mode 100644
index 0000000..530c714
--- /dev/null
+++ b/ipsec-to-dns.rsc
@@ -0,0 +1,69 @@
+#!rsc by RouterOS
+# RouterOS script: ipsec-to-dns
+# Copyright (c) 2021-2023 Christian Hesse <mail@eworm.de>
+# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md
+#
+# and add/remove/update DNS entries from IPSec mode-config
+# https://git.eworm.de/cgit/routeros-scripts/about/doc/ipsec-to-dns.md
+
+:local 0 "ipsec-to-dns";
+:global GlobalFunctionsReady;
+:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
+
+:global Domain;
+:global HostNameInZone;
+:global Identity;
+:global PrefixInZone;
+
+:global CharacterReplace;
+:global EscapeForRegEx;
+:global IfThenElse;
+:global LogPrintExit2;
+
+:local Zone \
+ ([ $IfThenElse ($PrefixInZone = true) "ipsec." ] . \
+ [ $IfThenElse ($HostNameInZone = true) ($Identity . ".") ] . $Domain);
+:local Ttl 5m;
+:local CommentPrefix ("managed by " . $0 . " for ");
+:local CommentString ("--- " . $0 . " above ---");
+
+:if ([ :len [ /ip/dns/static/find where comment=$CommentString name=- type=NXDOMAIN disabled ] ] = 0) do={
+ /ip/dns/static/add comment=$CommentString name=- type=NXDOMAIN disabled=yes;
+ $LogPrintExit2 warning $0 ("Added disabled static dns record with comment '" . $CommentString . "'.") false;
+}
+:local PlaceBefore ([ /ip/dns/static/find where comment=$CommentString name=- type=NXDOMAIN disabled ]->0);
+
+:foreach DnsRecord in=[ /ip/dns/static/find where comment ~ $CommentPrefix ] do={
+ :local DnsRecordVal [ /ip/dns/static/get $DnsRecord ];
+ :local PeerId [ $CharacterReplace ($DnsRecordVal->"comment") $CommentPrefix "" ];
+ :if ([ :len [ /ip/ipsec/active-peers/find where id~("^(CN=)?" . [ $EscapeForRegEx $PeerId ] . "\$") \
+ dynamic-address=($DnsRecordVal->"address") ] ] > 0) do={
+ $LogPrintExit2 debug $0 ("Peer " . $PeerId . " (" . $DnsRecordVal->"name" . ") still exists. Not deleting DNS entry.") false;
+ } else={
+ :local Found false;
+ $LogPrintExit2 info $0 ("Peer " . $PeerId . " (" . $DnsRecordVal->"name" . ") has gone, deleting DNS entry.") false;
+ /ip/dns/static/remove $DnsRecord;
+ }
+}
+
+:foreach Peer in=[ /ip/ipsec/active-peers/find where !(dynamic-address=[]) ] do={
+ :local PeerVal [ /ip/ipsec/active-peers/get $Peer ];
+ :local PeerId [ $CharacterReplace ($PeerVal->"id") "CN=" "" ];
+ :local Comment ($CommentPrefix . $PeerId);
+ :local HostName [ :pick $PeerId 0 [ :find ($PeerId . ".") "." ] ];
+
+ :local Fqdn ($HostName . "." . $Zone);
+ :local DnsRecord [ /ip/dns/static/find where name=$Fqdn ];
+ :if ([ :len $DnsRecord ] > 0) do={
+ :local DnsIp [ /ip/dns/static/get $DnsRecord address ];
+ :if ($DnsIp = $PeerVal->"dynamic-address") do={
+ $LogPrintExit2 debug $0 ("DNS entry for " . $Fqdn . " does not need updating.") false;
+ } else={
+ $LogPrintExit2 info $0 ("Replacing DNS entry for " . $Fqdn . ", new address is " . $PeerVal->"dynamic-address" . ".") false;
+ /ip/dns/static/set name=$Fqdn address=($PeerVal->"dynamic-address") ttl=$Ttl comment=$Comment $DnsRecord;
+ }
+ } else={
+ $LogPrintExit2 info $0 ("Adding new DNS entry for " . $Fqdn . ", address is " . $PeerVal->"dynamic-address" . ".") false;
+ /ip/dns/static/add name=$Fqdn address=($PeerVal->"dynamic-address") ttl=$Ttl comment=$Comment place-before=$PlaceBefore;
+ }
+}
diff --git a/ipv6-update b/ipv6-update
index 2838feb..2da00ca 100644
--- a/ipv6-update
+++ b/ipv6-update
@@ -1,76 +1,3 @@
#!rsc by RouterOS
-# RouterOS script: ipv6-update
-# Copyright (c) 2013-2023 Christian Hesse <mail@eworm.de>
-# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md
#
-# update firewall and dns settings on IPv6 prefix change
-# https://git.eworm.de/cgit/routeros-scripts/about/doc/ipv6-update.md
-
-:local 0 "ipv6-update";
-:global GlobalFunctionsReady;
-:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
-
-:local PdPrefix $"pd-prefix";
-
-:global LogPrintExit2;
-:global ParseKeyValueStore;
-
-:if ([ :typeof $PdPrefix ] = "nothing") do={
- $LogPrintExit2 error $0 ("This script is supposed to run from ipv6 dhcp-client.") true;
-}
-
-: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);
- $LogPrintExit2 warning $0 ("Added ipv6 address list entry for ipv6-pool-" . $Pool) false;
-}
-:local AddrList [ /ipv6/firewall/address-list/find where comment=("ipv6-pool-" . $Pool) ];
-:local OldPrefix [ /ipv6/firewall/address-list/get ($AddrList->0) address ];
-
-:if ($OldPrefix != $PdPrefix) do={
- $LogPrintExit2 info $0 ("Updating IPv6 address list with new IPv6 prefix " . $PdPrefix) false;
- /ipv6/firewall/address-list/set address=$PdPrefix $AddrList;
-
- # give the interfaces a moment to receive their addresses
- :delay 2s;
-
- :foreach ListEntry in=[ /ipv6/firewall/address-list/find where comment~("^ipv6-pool-" . $Pool . ",") ] do={
- :local ListEntryVal [ /ipv6/firewall/address-list/get $ListEntry ];
- :local Comment [ $ParseKeyValueStore ($ListEntryVal->"comment") ];
-
- :local Prefix [ /ipv6/address/find where from-pool=$Pool interface=($Comment->"interface") global ];
- :if ([ :len $Prefix ] = 1) do={
- :set Prefix [ /ipv6/address/get $Prefix address ];
-
- :if ([ :typeof [ :find ($ListEntryVal->"address") "/128" ] ] = "num" ) do={
- :set Prefix ([ :toip6 [ :pick $Prefix 0 [ :find $Prefix "/64" ] ] ] & ffff:ffff:ffff:ffff::);
- :local Address ($ListEntryVal->"address");
- :local Address ($Prefix | ([ :toip6 [ :pick $Address 0 [ :find $Address "/128" ] ] ] & ::ffff:ffff:ffff:ffff));
-
- $LogPrintExit2 info $0 ("Updating IPv6 address list with new IPv6 host address " . $Address . \
- " from interface " . ($Comment->"interface")) false;
- /ipv6/firewall/address-list/set address=$Address $ListEntry;
- } else={
- $LogPrintExit2 info $0 ("Updating IPv6 address list with new IPv6 prefix " . $Prefix . \
- " from interface " . ($Comment->"interface")) false;
- /ipv6/firewall/address-list/set address=$Prefix $ListEntry;
- }
- }
- }
-
- :foreach Record in=[ /ip/dns/static/find where comment~("^ipv6-pool-" . $Pool . ",") ] do={
- :local RecordVal [ /ip/dns/static/get $Record ];
- :local Comment [ $ParseKeyValueStore ($RecordVal->"comment") ];
-
- :local Prefix [ /ipv6/address/find where from-pool=$Pool interface=($Comment->"interface") global ];
- :if ([ :len $Prefix ] = 1) do={
- :set Prefix [ /ipv6/address/get $Prefix address ];
- :set Prefix ([ :toip6 [ :pick $Prefix 0 [ :find $Prefix "/64" ] ] ] & ffff:ffff:ffff:ffff::);
- :local Address ($Prefix | ([ :toip6 ($RecordVal->"address") ] & ::ffff:ffff:ffff:ffff));
-
- $LogPrintExit2 info $0 ("Updating DNS record for " . ($RecordVal->"name") . \
- ($RecordVal->"regexp") . " to " . $Address) false;
- /ip/dns/static/set address=$Address $Record;
- }
- }
-}
+# dummy for migration
diff --git a/ipv6-update.rsc b/ipv6-update.rsc
new file mode 100644
index 0000000..2838feb
--- /dev/null
+++ b/ipv6-update.rsc
@@ -0,0 +1,76 @@
+#!rsc by RouterOS
+# RouterOS script: ipv6-update
+# Copyright (c) 2013-2023 Christian Hesse <mail@eworm.de>
+# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md
+#
+# update firewall and dns settings on IPv6 prefix change
+# https://git.eworm.de/cgit/routeros-scripts/about/doc/ipv6-update.md
+
+:local 0 "ipv6-update";
+:global GlobalFunctionsReady;
+:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
+
+:local PdPrefix $"pd-prefix";
+
+:global LogPrintExit2;
+:global ParseKeyValueStore;
+
+:if ([ :typeof $PdPrefix ] = "nothing") do={
+ $LogPrintExit2 error $0 ("This script is supposed to run from ipv6 dhcp-client.") true;
+}
+
+: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);
+ $LogPrintExit2 warning $0 ("Added ipv6 address list entry for ipv6-pool-" . $Pool) false;
+}
+:local AddrList [ /ipv6/firewall/address-list/find where comment=("ipv6-pool-" . $Pool) ];
+:local OldPrefix [ /ipv6/firewall/address-list/get ($AddrList->0) address ];
+
+:if ($OldPrefix != $PdPrefix) do={
+ $LogPrintExit2 info $0 ("Updating IPv6 address list with new IPv6 prefix " . $PdPrefix) false;
+ /ipv6/firewall/address-list/set address=$PdPrefix $AddrList;
+
+ # give the interfaces a moment to receive their addresses
+ :delay 2s;
+
+ :foreach ListEntry in=[ /ipv6/firewall/address-list/find where comment~("^ipv6-pool-" . $Pool . ",") ] do={
+ :local ListEntryVal [ /ipv6/firewall/address-list/get $ListEntry ];
+ :local Comment [ $ParseKeyValueStore ($ListEntryVal->"comment") ];
+
+ :local Prefix [ /ipv6/address/find where from-pool=$Pool interface=($Comment->"interface") global ];
+ :if ([ :len $Prefix ] = 1) do={
+ :set Prefix [ /ipv6/address/get $Prefix address ];
+
+ :if ([ :typeof [ :find ($ListEntryVal->"address") "/128" ] ] = "num" ) do={
+ :set Prefix ([ :toip6 [ :pick $Prefix 0 [ :find $Prefix "/64" ] ] ] & ffff:ffff:ffff:ffff::);
+ :local Address ($ListEntryVal->"address");
+ :local Address ($Prefix | ([ :toip6 [ :pick $Address 0 [ :find $Address "/128" ] ] ] & ::ffff:ffff:ffff:ffff));
+
+ $LogPrintExit2 info $0 ("Updating IPv6 address list with new IPv6 host address " . $Address . \
+ " from interface " . ($Comment->"interface")) false;
+ /ipv6/firewall/address-list/set address=$Address $ListEntry;
+ } else={
+ $LogPrintExit2 info $0 ("Updating IPv6 address list with new IPv6 prefix " . $Prefix . \
+ " from interface " . ($Comment->"interface")) false;
+ /ipv6/firewall/address-list/set address=$Prefix $ListEntry;
+ }
+ }
+ }
+
+ :foreach Record in=[ /ip/dns/static/find where comment~("^ipv6-pool-" . $Pool . ",") ] do={
+ :local RecordVal [ /ip/dns/static/get $Record ];
+ :local Comment [ $ParseKeyValueStore ($RecordVal->"comment") ];
+
+ :local Prefix [ /ipv6/address/find where from-pool=$Pool interface=($Comment->"interface") global ];
+ :if ([ :len $Prefix ] = 1) do={
+ :set Prefix [ /ipv6/address/get $Prefix address ];
+ :set Prefix ([ :toip6 [ :pick $Prefix 0 [ :find $Prefix "/64" ] ] ] & ffff:ffff:ffff:ffff::);
+ :local Address ($Prefix | ([ :toip6 ($RecordVal->"address") ] & ::ffff:ffff:ffff:ffff));
+
+ $LogPrintExit2 info $0 ("Updating DNS record for " . ($RecordVal->"name") . \
+ ($RecordVal->"regexp") . " to " . $Address) false;
+ /ip/dns/static/set address=$Address $Record;
+ }
+ }
+}
diff --git a/lease-script b/lease-script
index 346d52b..2da00ca 100644
--- a/lease-script
+++ b/lease-script
@@ -1,51 +1,3 @@
#!rsc by RouterOS
-# RouterOS script: lease-script
-# Copyright (c) 2013-2023 Christian Hesse <mail@eworm.de>
-# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md
#
-# run scripts on DHCP lease
-# https://git.eworm.de/cgit/routeros-scripts/about/doc/lease-script.md
-
-:local 0 "lease-script";
-:global GlobalFunctionsReady;
-:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
-
-:global Grep;
-:global IfThenElse;
-:global LogPrintExit2;
-:global ParseKeyValueStore;
-:global ScriptLock;
-
-:if ([ :typeof $leaseActIP ] = "nothing" || \
- [ :typeof $leaseActMAC ] = "nothing" || \
- [ :typeof $leaseServerName ] = "nothing" || \
- [ :typeof $leaseBound ] = "nothing") do={
- $LogPrintExit2 error $0 ("This script is supposed to run from ip dhcp-server.") true;
-}
-
-$LogPrintExit2 debug $0 ("DHCP Server " . $leaseServerName . " " . [ $IfThenElse ($leaseBound = 0) \
- "de" "" ] . "assigned lease " . $leaseActIP . " to " . $leaseActMAC) false;
-
-$ScriptLock $0 false 10;
-
-:if ([ :len [ /system/script/job/find where script=$0 ] ] > 1) do={
- $LogPrintExit2 debug $0 ("More invocations are waiting, exiting early.") true;
-}
-
-:local RunOrder ({});
-
-:foreach Script in=[ /system/script/find where source~("\n# provides: lease-script, ") ] do={
- :local ScriptVal [ /system/script/get $Script ];
- :local Store [ $ParseKeyValueStore [ $Grep ($ScriptVal->"source") "# provides: lease-script, " ] ];
-
- :set ($RunOrder->($Store->"order")) ($ScriptVal->"name");
-}
-
-:foreach Order,Script in=$RunOrder do={
- :do {
- $LogPrintExit2 debug $0 ("Running script with order " . $Order . ": " . $Script) false;
- /system/script/run $Script;
- } on-error={
- $LogPrintExit2 warning $0 ("Running script '" . $Script . "' failed!") false;
- }
-}
+# dummy for migration
diff --git a/lease-script.rsc b/lease-script.rsc
new file mode 100644
index 0000000..346d52b
--- /dev/null
+++ b/lease-script.rsc
@@ -0,0 +1,51 @@
+#!rsc by RouterOS
+# RouterOS script: lease-script
+# Copyright (c) 2013-2023 Christian Hesse <mail@eworm.de>
+# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md
+#
+# run scripts on DHCP lease
+# https://git.eworm.de/cgit/routeros-scripts/about/doc/lease-script.md
+
+:local 0 "lease-script";
+:global GlobalFunctionsReady;
+:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
+
+:global Grep;
+:global IfThenElse;
+:global LogPrintExit2;
+:global ParseKeyValueStore;
+:global ScriptLock;
+
+:if ([ :typeof $leaseActIP ] = "nothing" || \
+ [ :typeof $leaseActMAC ] = "nothing" || \
+ [ :typeof $leaseServerName ] = "nothing" || \
+ [ :typeof $leaseBound ] = "nothing") do={
+ $LogPrintExit2 error $0 ("This script is supposed to run from ip dhcp-server.") true;
+}
+
+$LogPrintExit2 debug $0 ("DHCP Server " . $leaseServerName . " " . [ $IfThenElse ($leaseBound = 0) \
+ "de" "" ] . "assigned lease " . $leaseActIP . " to " . $leaseActMAC) false;
+
+$ScriptLock $0 false 10;
+
+:if ([ :len [ /system/script/job/find where script=$0 ] ] > 1) do={
+ $LogPrintExit2 debug $0 ("More invocations are waiting, exiting early.") true;
+}
+
+:local RunOrder ({});
+
+:foreach Script in=[ /system/script/find where source~("\n# provides: lease-script, ") ] do={
+ :local ScriptVal [ /system/script/get $Script ];
+ :local Store [ $ParseKeyValueStore [ $Grep ($ScriptVal->"source") "# provides: lease-script, " ] ];
+
+ :set ($RunOrder->($Store->"order")) ($ScriptVal->"name");
+}
+
+:foreach Order,Script in=$RunOrder do={
+ :do {
+ $LogPrintExit2 debug $0 ("Running script with order " . $Order . ": " . $Script) false;
+ /system/script/run $Script;
+ } on-error={
+ $LogPrintExit2 warning $0 ("Running script '" . $Script . "' failed!") false;
+ }
+}
diff --git a/leds-day-mode b/leds-day-mode
index ca2e8d8..2da00ca 100644
--- a/leds-day-mode
+++ b/leds-day-mode
@@ -1,9 +1,3 @@
#!rsc by RouterOS
-# RouterOS script: leds-day-mode
-# Copyright (c) 2013-2023 Christian Hesse <mail@eworm.de>
-# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md
#
-# enable LEDs
-# https://git.eworm.de/cgit/routeros-scripts/about/doc/leds-mode.md
-
-/system/leds/settings/set all-leds-off=never;
+# dummy for migration
diff --git a/leds-day-mode.rsc b/leds-day-mode.rsc
new file mode 100644
index 0000000..ca2e8d8
--- /dev/null
+++ b/leds-day-mode.rsc
@@ -0,0 +1,9 @@
+#!rsc by RouterOS
+# RouterOS script: leds-day-mode
+# Copyright (c) 2013-2023 Christian Hesse <mail@eworm.de>
+# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md
+#
+# enable LEDs
+# https://git.eworm.de/cgit/routeros-scripts/about/doc/leds-mode.md
+
+/system/leds/settings/set all-leds-off=never;
diff --git a/leds-night-mode b/leds-night-mode
index cdd8127..2da00ca 100644
--- a/leds-night-mode
+++ b/leds-night-mode
@@ -1,9 +1,3 @@
#!rsc by RouterOS
-# RouterOS script: leds-night-mode
-# Copyright (c) 2013-2023 Christian Hesse <mail@eworm.de>
-# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md
#
-# disable LEDs
-# https://git.eworm.de/cgit/routeros-scripts/about/doc/leds-mode.md
-
-/system/leds/settings/set all-leds-off=immediate;
+# dummy for migration
diff --git a/leds-night-mode.rsc b/leds-night-mode.rsc
new file mode 100644
index 0000000..cdd8127
--- /dev/null
+++ b/leds-night-mode.rsc
@@ -0,0 +1,9 @@
+#!rsc by RouterOS
+# RouterOS script: leds-night-mode
+# Copyright (c) 2013-2023 Christian Hesse <mail@eworm.de>
+# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md
+#
+# disable LEDs
+# https://git.eworm.de/cgit/routeros-scripts/about/doc/leds-mode.md
+
+/system/leds/settings/set all-leds-off=immediate;
diff --git a/leds-toggle-mode b/leds-toggle-mode
index da972b7..2da00ca 100644
--- a/leds-toggle-mode
+++ b/leds-toggle-mode
@@ -1,13 +1,3 @@
#!rsc by RouterOS
-# RouterOS script: leds-toggle-mode
-# Copyright (c) 2018-2023 Christian Hesse <mail@eworm.de>
-# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md
#
-# toggle LEDs mode
-# https://git.eworm.de/cgit/routeros-scripts/about/doc/leds-mode.md
-
-:if ([ /system/leds/settings/get all-leds-off ] = "never") do={
- /system/leds/settings/set all-leds-off=immediate;
-} else={
- /system/leds/settings/set all-leds-off=never;
-}
+# dummy for migration
diff --git a/leds-toggle-mode.rsc b/leds-toggle-mode.rsc
new file mode 100644
index 0000000..da972b7
--- /dev/null
+++ b/leds-toggle-mode.rsc
@@ -0,0 +1,13 @@
+#!rsc by RouterOS
+# RouterOS script: leds-toggle-mode
+# Copyright (c) 2018-2023 Christian Hesse <mail@eworm.de>
+# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md
+#
+# toggle LEDs mode
+# https://git.eworm.de/cgit/routeros-scripts/about/doc/leds-mode.md
+
+:if ([ /system/leds/settings/get all-leds-off ] = "never") do={
+ /system/leds/settings/set all-leds-off=immediate;
+} else={
+ /system/leds/settings/set all-leds-off=never;
+}
diff --git a/log-forward b/log-forward
index 96cb257..2da00ca 100644
--- a/log-forward
+++ b/log-forward
@@ -1,90 +1,3 @@
#!rsc by RouterOS
-# RouterOS script: log-forward
-# Copyright (c) 2020-2023 Christian Hesse <mail@eworm.de>
-# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md
#
-# forward log messages via notification
-# https://git.eworm.de/cgit/routeros-scripts/about/doc/log-forward.md
-
-:local 0 "log-forward";
-:global GlobalFunctionsReady;
-:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
-
-:global Identity;
-:global LogForwardFilter;
-:global LogForwardFilterMessage;
-:global LogForwardInclude;
-:global LogForwardIncludeMessage;
-:global LogForwardLast;
-:global LogForwardRateLimit;
-:global NotificationsWithSymbols;
-
-:global EitherOr;
-:global HexToNum;
-:global IfThenElse;
-:global LogForwardFilterLogForwarding;
-:global LogPrintExit2;
-:global ScriptLock;
-:global SendNotification2;
-:global SymbolForNotification;
-
-$ScriptLock $0;
-
-:if ([ :typeof $LogForwardRateLimit ] = "nothing") do={
- :set LogForwardRateLimit 0;
-}
-
-:if ($LogForwardRateLimit > 30) do={
- :set LogForwardRateLimit ($LogForwardRateLimit - 1);
- $LogPrintExit2 info $0 ("Rate limit in action, not forwarding logs, if any!") true;
-}
-
-:local Count 0;
-:local Duplicates false;
-:local Last [ $IfThenElse ([ :len $LogForwardLast ] > 0) [ $HexToNum $LogForwardLast ] -1 ];
-:local Messages "";
-:local Warning false;
-:local MessageVal;
-:local MessageDups ({});
-
-:local LogForwardFilterLogForwardingCached [ $EitherOr [ $LogForwardFilterLogForwarding ] ("\$^") ];
-:foreach Message in=[ /log/find where (!(message="") and \
- !(message~$LogForwardFilterLogForwardingCached) and \
- !(topics~$LogForwardFilter) and !(message~$LogForwardFilterMessage)) or \
- topics~$LogForwardInclude or message~$LogForwardIncludeMessage ] do={
- :set MessageVal [ /log/get $Message ];
-
- :if ($Last < [ $HexToNum ($MessageVal->".id") ]) do={
- :local DupCount ($MessageDups->($MessageVal->"message"));
- :if ($MessageVal->"topics" ~ "(emergency|alert|critical|error|warning)") do={
- :set Warning true;
- }
- :if ($DupCount < 3) do={
- :set Messages ($Messages . "\n" . [ $IfThenElse ($NotificationsWithSymbols = true) (" \E2\97\8F ") ] . \
- $MessageVal->"time" . " " . [ :tostr ($MessageVal->"topics") ] . " " . $MessageVal->"message");
- } else={
- :set Duplicates true;
- }
- :set ($MessageDups->($MessageVal->"message")) ($DupCount + 1);
- :set Count ($Count + 1);
- }
-}
-
-:if ($Count > 0) do={
- :set LogForwardRateLimit ($LogForwardRateLimit + 10);
-
- $SendNotification2 ({ origin=$0; \
- subject=([ $SymbolForNotification [ $IfThenElse ($Warning = true) "warning-sign" "memo" ] ] . \
- "Log Forwarding"); \
- message=("The log on " . $Identity . " contains " . [ $IfThenElse ($Count = 1) "this message" \
- ("these " . $Count . " messages") ] . " after " . [ /system/resource/get uptime ] . " uptime." . \
- [ $IfThenElse ($Duplicates = true) (" Multi-repeated messages have been skipped.") ] . \
- [ $IfThenElse ($LogForwardRateLimit > 30) ("\nRate limit in action, delaying forwarding.") ] . \
- "\n" . $Messages) });
-
- :set LogForwardLast ($MessageVal->".id");
-} else={
- :if ($LogForwardRateLimit > 0) do={
- :set LogForwardRateLimit ($LogForwardRateLimit - 1);
- }
-}
+# dummy for migration
diff --git a/log-forward.rsc b/log-forward.rsc
new file mode 100644
index 0000000..96cb257
--- /dev/null
+++ b/log-forward.rsc
@@ -0,0 +1,90 @@
+#!rsc by RouterOS
+# RouterOS script: log-forward
+# Copyright (c) 2020-2023 Christian Hesse <mail@eworm.de>
+# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md
+#
+# forward log messages via notification
+# https://git.eworm.de/cgit/routeros-scripts/about/doc/log-forward.md
+
+:local 0 "log-forward";
+:global GlobalFunctionsReady;
+:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
+
+:global Identity;
+:global LogForwardFilter;
+:global LogForwardFilterMessage;
+:global LogForwardInclude;
+:global LogForwardIncludeMessage;
+:global LogForwardLast;
+:global LogForwardRateLimit;
+:global NotificationsWithSymbols;
+
+:global EitherOr;
+:global HexToNum;
+:global IfThenElse;
+:global LogForwardFilterLogForwarding;
+:global LogPrintExit2;
+:global ScriptLock;
+:global SendNotification2;
+:global SymbolForNotification;
+
+$ScriptLock $0;
+
+:if ([ :typeof $LogForwardRateLimit ] = "nothing") do={
+ :set LogForwardRateLimit 0;
+}
+
+:if ($LogForwardRateLimit > 30) do={
+ :set LogForwardRateLimit ($LogForwardRateLimit - 1);
+ $LogPrintExit2 info $0 ("Rate limit in action, not forwarding logs, if any!") true;
+}
+
+:local Count 0;
+:local Duplicates false;
+:local Last [ $IfThenElse ([ :len $LogForwardLast ] > 0) [ $HexToNum $LogForwardLast ] -1 ];
+:local Messages "";
+:local Warning false;
+:local MessageVal;
+:local MessageDups ({});
+
+:local LogForwardFilterLogForwardingCached [ $EitherOr [ $LogForwardFilterLogForwarding ] ("\$^") ];
+:foreach Message in=[ /log/find where (!(message="") and \
+ !(message~$LogForwardFilterLogForwardingCached) and \
+ !(topics~$LogForwardFilter) and !(message~$LogForwardFilterMessage)) or \
+ topics~$LogForwardInclude or message~$LogForwardIncludeMessage ] do={
+ :set MessageVal [ /log/get $Message ];
+
+ :if ($Last < [ $HexToNum ($MessageVal->".id") ]) do={
+ :local DupCount ($MessageDups->($MessageVal->"message"));
+ :if ($MessageVal->"topics" ~ "(emergency|alert|critical|error|warning)") do={
+ :set Warning true;
+ }
+ :if ($DupCount < 3) do={
+ :set Messages ($Messages . "\n" . [ $IfThenElse ($NotificationsWithSymbols = true) (" \E2\97\8F ") ] . \
+ $MessageVal->"time" . " " . [ :tostr ($MessageVal->"topics") ] . " " . $MessageVal->"message");
+ } else={
+ :set Duplicates true;
+ }
+ :set ($MessageDups->($MessageVal->"message")) ($DupCount + 1);
+ :set Count ($Count + 1);
+ }
+}
+
+:if ($Count > 0) do={
+ :set LogForwardRateLimit ($LogForwardRateLimit + 10);
+
+ $SendNotification2 ({ origin=$0; \
+ subject=([ $SymbolForNotification [ $IfThenElse ($Warning = true) "warning-sign" "memo" ] ] . \
+ "Log Forwarding"); \
+ message=("The log on " . $Identity . " contains " . [ $IfThenElse ($Count = 1) "this message" \
+ ("these " . $Count . " messages") ] . " after " . [ /system/resource/get uptime ] . " uptime." . \
+ [ $IfThenElse ($Duplicates = true) (" Multi-repeated messages have been skipped.") ] . \
+ [ $IfThenElse ($LogForwardRateLimit > 30) ("\nRate limit in action, delaying forwarding.") ] . \
+ "\n" . $Messages) });
+
+ :set LogForwardLast ($MessageVal->".id");
+} else={
+ :if ($LogForwardRateLimit > 0) do={
+ :set LogForwardRateLimit ($LogForwardRateLimit - 1);
+ }
+}
diff --git a/mod/bridge-port-to b/mod/bridge-port-to
index f752d30..2da00ca 100644
--- a/mod/bridge-port-to
+++ b/mod/bridge-port-to
@@ -1,65 +1,3 @@
#!rsc by RouterOS
-# RouterOS script: mod/bridge-port-to
-# Copyright (c) 2013-2023 Christian Hesse <mail@eworm.de>
-# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md
#
-# reset bridge ports to default bridge
-# https://git.eworm.de/cgit/routeros-scripts/about/doc/mod/bridge-port-to.md
-
-:global BridgePortTo;
-
-:set BridgePortTo do={
- :local BridgePortTo [ :tostr $1 ];
-
- :global IfThenElse;
- :global LogPrintExit2;
- :global ParseKeyValueStore;
-
- :local InterfaceReEnable ({});
- :foreach BridgePort in=[ /interface/bridge/port/find where !(comment=[]) ] do={
- :local BridgePortVal [ /interface/bridge/port/get $BridgePort ];
- :foreach Config,BridgeDefault in=[ $ParseKeyValueStore ($BridgePortVal->"comment") ] do={
- :if ($Config = $BridgePortTo) do={
- :local DHCPClient [ /ip/dhcp-client/find where interface=$BridgePortVal->"interface" comment="toggle with bridge port" ];
-
- :if ($BridgeDefault = "dhcp-client") do={
- :if ([ :len $DHCPClient ] != 1) do={
- $LogPrintExit2 warning $0 ([ $IfThenElse ([ :len $DHCPClient ] = 0) "Missing" "Duplicate" ] . \
- " dhcp client configuration for interface " . $BridgePortVal->"interface" . "!") true;
- }
- :local DHCPClientDisabled [ /ip/dhcp-client/get $DHCPClient disabled ];
-
- :if ($BridgePortVal->"disabled" = false || $DHCPClientDisabled = true) do={
- $LogPrintExit2 info $0 ("Disabling bridge port for interface " . $BridgePortVal->"interface" . ", enabling dhcp client.") false;
- /interface/bridge/port/disable $BridgePort;
- :delay 200ms;
- /ip/dhcp-client/enable $DHCPClient;
- }
- } else={
- :if ($BridgePortVal->"disabled" = true || $BridgeDefault != $BridgePortVal->"bridge") do={
- $LogPrintExit2 info $0 ("Enabling bridge port for interface " . $BridgePortVal->"interface" . ", changing to " . $BridgePortTo . \
- " bridge " . $BridgeDefault . ", disabling dhcp client.") false;
- :if ([ :len $DHCPClient ] = 1) do={
- /ip/dhcp-client/disable $DHCPClient;
- :delay 200ms;
- }
- :local Disable [ /interface/ethernet/find where name=$BridgePortVal->"interface" ];
- :if ([ :len $Disable ] > 0) do={
- /interface/ethernet/disable $Disable;
- :set InterfaceReEnable ($InterfaceReEnable, $Disable);
- }
- /interface/bridge/port/set disabled=no bridge=$BridgeDefault $BridgePort;
- } else={
- $LogPrintExit2 debug $0 ("Interface " . $BridgePortVal->"interface" . " already connected to " . $BridgePortTo . \
- " bridge " . $BridgeDefault . ".") false;
- }
- }
- }
- }
- }
- :if ([ :len $InterfaceReEnable ] > 0) do={
- :delay 2s;
- $LogPrintExit2 info $0 ("Re-enabling interfaces...") false;
- /interface/ethernet/enable $InterfaceReEnable;
- }
-}
+# dummy for migration
diff --git a/mod/bridge-port-to.rsc b/mod/bridge-port-to.rsc
new file mode 100644
index 0000000..f752d30
--- /dev/null
+++ b/mod/bridge-port-to.rsc
@@ -0,0 +1,65 @@
+#!rsc by RouterOS
+# RouterOS script: mod/bridge-port-to
+# Copyright (c) 2013-2023 Christian Hesse <mail@eworm.de>
+# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md
+#
+# reset bridge ports to default bridge
+# https://git.eworm.de/cgit/routeros-scripts/about/doc/mod/bridge-port-to.md
+
+:global BridgePortTo;
+
+:set BridgePortTo do={
+ :local BridgePortTo [ :tostr $1 ];
+
+ :global IfThenElse;
+ :global LogPrintExit2;
+ :global ParseKeyValueStore;
+
+ :local InterfaceReEnable ({});
+ :foreach BridgePort in=[ /interface/bridge/port/find where !(comment=[]) ] do={
+ :local BridgePortVal [ /interface/bridge/port/get $BridgePort ];
+ :foreach Config,BridgeDefault in=[ $ParseKeyValueStore ($BridgePortVal->"comment") ] do={
+ :if ($Config = $BridgePortTo) do={
+ :local DHCPClient [ /ip/dhcp-client/find where interface=$BridgePortVal->"interface" comment="toggle with bridge port" ];
+
+ :if ($BridgeDefault = "dhcp-client") do={
+ :if ([ :len $DHCPClient ] != 1) do={
+ $LogPrintExit2 warning $0 ([ $IfThenElse ([ :len $DHCPClient ] = 0) "Missing" "Duplicate" ] . \
+ " dhcp client configuration for interface " . $BridgePortVal->"interface" . "!") true;
+ }
+ :local DHCPClientDisabled [ /ip/dhcp-client/get $DHCPClient disabled ];
+
+ :if ($BridgePortVal->"disabled" = false || $DHCPClientDisabled = true) do={
+ $LogPrintExit2 info $0 ("Disabling bridge port for interface " . $BridgePortVal->"interface" . ", enabling dhcp client.") false;
+ /interface/bridge/port/disable $BridgePort;
+ :delay 200ms;
+ /ip/dhcp-client/enable $DHCPClient;
+ }
+ } else={
+ :if ($BridgePortVal->"disabled" = true || $BridgeDefault != $BridgePortVal->"bridge") do={
+ $LogPrintExit2 info $0 ("Enabling bridge port for interface " . $BridgePortVal->"interface" . ", changing to " . $BridgePortTo . \
+ " bridge " . $BridgeDefault . ", disabling dhcp client.") false;
+ :if ([ :len $DHCPClient ] = 1) do={
+ /ip/dhcp-client/disable $DHCPClient;
+ :delay 200ms;
+ }
+ :local Disable [ /interface/ethernet/find where name=$BridgePortVal->"interface" ];
+ :if ([ :len $Disable ] > 0) do={
+ /interface/ethernet/disable $Disable;
+ :set InterfaceReEnable ($InterfaceReEnable, $Disable);
+ }
+ /interface/bridge/port/set disabled=no bridge=$BridgeDefault $BridgePort;
+ } else={
+ $LogPrintExit2 debug $0 ("Interface " . $BridgePortVal->"interface" . " already connected to " . $BridgePortTo . \
+ " bridge " . $BridgeDefault . ".") false;
+ }
+ }
+ }
+ }
+ }
+ :if ([ :len $InterfaceReEnable ] > 0) do={
+ :delay 2s;
+ $LogPrintExit2 info $0 ("Re-enabling interfaces...") false;
+ /interface/ethernet/enable $InterfaceReEnable;
+ }
+}
diff --git a/mod/bridge-port-vlan b/mod/bridge-port-vlan
index 8fb64e1..2da00ca 100644
--- a/mod/bridge-port-vlan
+++ b/mod/bridge-port-vlan
@@ -1,73 +1,3 @@
#!rsc by RouterOS
-# RouterOS script: mod/bridge-port-vlan
-# Copyright (c) 2013-2023 Christian Hesse <mail@eworm.de>
-# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md
#
-# manage VLANs on bridge ports
-# https://git.eworm.de/cgit/routeros-scripts/about/doc/mod/bridge-port-vlan.md
-
-:global BridgePortVlan;
-
-:global BridgePortVlan do={
- :local ConfigTo [ :tostr $1 ];
-
- :global IfThenElse;
- :global LogPrintExit2;
- :global ParseKeyValueStore;
-
- :local InterfaceReEnable ({});
- :foreach BridgePort in=[ /interface/bridge/port/find where !(comment=[]) ] do={
- :local BridgePortVal [ /interface/bridge/port/get $BridgePort ];
- :foreach Config,Vlan in=[ $ParseKeyValueStore ($BridgePortVal->"comment") ] do={
- :if ($Config = $ConfigTo) do={
- :local DHCPClient [ /ip/dhcp-client/find where interface=$BridgePortVal->"interface" comment="toggle with bridge port" ];
-
- :if ($Vlan = "dhcp-client") do={
- :if ([ :len $DHCPClient ] != 1) do={
- $LogPrintExit2 warning $0 ([ $IfThenElse ([ :len $DHCPClient ] = 0) "Missing" "Duplicate" ] . \
- " dhcp client configuration for interface " . $BridgePortVal->"interface" . "!") true;
- }
- :local DHCPClientDisabled [ /ip/dhcp-client/get $DHCPClient disabled ];
-
- :if ($BridgePortVal->"disabled" = false || $DHCPClientDisabled = true) do={
- $LogPrintExit2 info $0 ("Disabling bridge port for interface " . $BridgePortVal->"interface" . ", enabling dhcp client.") false;
- /interface/bridge/port/disable $BridgePort;
- :delay 200ms;
- /ip/dhcp-client/enable $DHCPClient;
- }
- } else={
- :local VlanName $Vlan;
- :if ($Vlan != [ :tostr [ :tonum $Vlan ] ]) do={
- :do {
- :set $Vlan ([ /interface/bridge/vlan/get [ find where comment=$Vlan ] vlan-ids ]->0);
- } on-error={
- $LogPrintExit2 warning $0 ("Could not find VLAN '" . $Vlan . "' for interface " . $BridgePortVal->"interface" . "!") true;
- }
- }
- :if ($BridgePortVal->"disabled" = true || $Vlan != $BridgePortVal->"pvid") do={
- $LogPrintExit2 info $0 ("Enabling bridge port for interface " . $BridgePortVal->"interface" . ", changing to " . $ConfigTo . \
- " vlan " . $Vlan . [ $IfThenElse ($Vlan != $VlanName) (" (" . $VlanName . ")") ] . ", disabling dhcp client.") false;
- :if ([ :len $DHCPClient ] = 1) do={
- /ip/dhcp-client/disable $DHCPClient;
- :delay 200ms;
- }
- :local Disable [ /interface/ethernet/find where name=$BridgePortVal->"interface" ];
- :if ([ :len $Disable ] > 0) do={
- /interface/ethernet/disable $Disable;
- :set InterfaceReEnable ($InterfaceReEnable, $Disable);
- }
- /interface/bridge/port/set disabled=no pvid=$Vlan $BridgePort;
- } else={
- $LogPrintExit2 debug $0 ("Interface " . $BridgePortVal->"interface" . " already connected to " . $ConfigTo . \
- " vlan " . $Vlan . ".") false;
- }
- }
- }
- }
- }
- :if ([ :len $InterfaceReEnable ] > 0) do={
- :delay 2s;
- $LogPrintExit2 info $0 ("Re-enabling interfaces...") false;
- /interface/ethernet/enable $InterfaceReEnable;
- }
-}
+# dummy for migration
diff --git a/mod/bridge-port-vlan.rsc b/mod/bridge-port-vlan.rsc
new file mode 100644
index 0000000..8fb64e1
--- /dev/null
+++ b/mod/bridge-port-vlan.rsc
@@ -0,0 +1,73 @@
+#!rsc by RouterOS
+# RouterOS script: mod/bridge-port-vlan
+# Copyright (c) 2013-2023 Christian Hesse <mail@eworm.de>
+# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md
+#
+# manage VLANs on bridge ports
+# https://git.eworm.de/cgit/routeros-scripts/about/doc/mod/bridge-port-vlan.md
+
+:global BridgePortVlan;
+
+:global BridgePortVlan do={
+ :local ConfigTo [ :tostr $1 ];
+
+ :global IfThenElse;
+ :global LogPrintExit2;
+ :global ParseKeyValueStore;
+
+ :local InterfaceReEnable ({});
+ :foreach BridgePort in=[ /interface/bridge/port/find where !(comment=[]) ] do={
+ :local BridgePortVal [ /interface/bridge/port/get $BridgePort ];
+ :foreach Config,Vlan in=[ $ParseKeyValueStore ($BridgePortVal->"comment") ] do={
+ :if ($Config = $ConfigTo) do={
+ :local DHCPClient [ /ip/dhcp-client/find where interface=$BridgePortVal->"interface" comment="toggle with bridge port" ];
+
+ :if ($Vlan = "dhcp-client") do={
+ :if ([ :len $DHCPClient ] != 1) do={
+ $LogPrintExit2 warning $0 ([ $IfThenElse ([ :len $DHCPClient ] = 0) "Missing" "Duplicate" ] . \
+ " dhcp client configuration for interface " . $BridgePortVal->"interface" . "!") true;
+ }
+ :local DHCPClientDisabled [ /ip/dhcp-client/get $DHCPClient disabled ];
+
+ :if ($BridgePortVal->"disabled" = false || $DHCPClientDisabled = true) do={
+ $LogPrintExit2 info $0 ("Disabling bridge port for interface " . $BridgePortVal->"interface" . ", enabling dhcp client.") false;
+ /interface/bridge/port/disable $BridgePort;
+ :delay 200ms;
+ /ip/dhcp-client/enable $DHCPClient;
+ }
+ } else={
+ :local VlanName $Vlan;
+ :if ($Vlan != [ :tostr [ :tonum $Vlan ] ]) do={
+ :do {
+ :set $Vlan ([ /interface/bridge/vlan/get [ find where comment=$Vlan ] vlan-ids ]->0);
+ } on-error={
+ $LogPrintExit2 warning $0 ("Could not find VLAN '" . $Vlan . "' for interface " . $BridgePortVal->"interface" . "!") true;
+ }
+ }
+ :if ($BridgePortVal->"disabled" = true || $Vlan != $BridgePortVal->"pvid") do={
+ $LogPrintExit2 info $0 ("Enabling bridge port for interface " . $BridgePortVal->"interface" . ", changing to " . $ConfigTo . \
+ " vlan " . $Vlan . [ $IfThenElse ($Vlan != $VlanName) (" (" . $VlanName . ")") ] . ", disabling dhcp client.") false;
+ :if ([ :len $DHCPClient ] = 1) do={
+ /ip/dhcp-client/disable $DHCPClient;
+ :delay 200ms;
+ }
+ :local Disable [ /interface/ethernet/find where name=$BridgePortVal->"interface" ];
+ :if ([ :len $Disable ] > 0) do={
+ /interface/ethernet/disable $Disable;
+ :set InterfaceReEnable ($InterfaceReEnable, $Disable);
+ }
+ /interface/bridge/port/set disabled=no pvid=$Vlan $BridgePort;
+ } else={
+ $LogPrintExit2 debug $0 ("Interface " . $BridgePortVal->"interface" . " already connected to " . $ConfigTo . \
+ " vlan " . $Vlan . ".") false;
+ }
+ }
+ }
+ }
+ }
+ :if ([ :len $InterfaceReEnable ] > 0) do={
+ :delay 2s;
+ $LogPrintExit2 info $0 ("Re-enabling interfaces...") false;
+ /interface/ethernet/enable $InterfaceReEnable;
+ }
+}
diff --git a/mod/inspectvar b/mod/inspectvar
index 8bb5c5f..2da00ca 100644
--- a/mod/inspectvar
+++ b/mod/inspectvar
@@ -1,54 +1,3 @@
#!rsc by RouterOS
-# RouterOS script: mod/inspectvar
-# Copyright (c) 2020-2023 Christian Hesse <mail@eworm.de>
-# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md
-
-:global InspectVar;
-:global InspectVarReturn;
-
-# inspect variable and print on terminal
-:set InspectVar do={
- :global InspectVarReturn;
- :global PrettyPrint;
-
- $PrettyPrint [ $InspectVarReturn $1 ];
-}
-
-# inspect variable and return formatted string
-:set InspectVarReturn do={
- :local Input $1;
- :local Level (0 + [ :tonum $2 ]);
-
- :global IfThenElse;
- :global InspectVarReturn;
-
- :local IndentReturn do={
- :local Prefix [ :tostr $1 ];
- :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);
- }
-
- :local TypeOf [ :typeof $Input ];
- :local Return [ $IndentReturn "type" $TypeOf $Level ];
-
- :if ($TypeOf = "array") do={
- :foreach Key,Value in=$Input do={
- :set $Return ($Return . "\n" . \
- [ $IndentReturn "key" $Key ($Level + 1) ] . "\n" . \
- [ $InspectVarReturn $Value ($Level + 2) ]);
- }
- } else={
- :if ($TypeOf != "nothing") do={
- :set $Return ($Return . "\n" . \
- [ $IndentReturn "value" [ $IfThenElse ([ :len $Input ] > 80) \
- ([ :pick $Input 0 77 ] . "...") $Input ] $Level ]);
- }
- }
- :return $Return;
-}
+#
+# dummy for migration
diff --git a/mod/inspectvar.rsc b/mod/inspectvar.rsc
new file mode 100644
index 0000000..8bb5c5f
--- /dev/null
+++ b/mod/inspectvar.rsc
@@ -0,0 +1,54 @@
+#!rsc by RouterOS
+# RouterOS script: mod/inspectvar
+# Copyright (c) 2020-2023 Christian Hesse <mail@eworm.de>
+# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md
+
+:global InspectVar;
+:global InspectVarReturn;
+
+# inspect variable and print on terminal
+:set InspectVar do={
+ :global InspectVarReturn;
+ :global PrettyPrint;
+
+ $PrettyPrint [ $InspectVarReturn $1 ];
+}
+
+# inspect variable and return formatted string
+:set InspectVarReturn do={
+ :local Input $1;
+ :local Level (0 + [ :tonum $2 ]);
+
+ :global IfThenElse;
+ :global InspectVarReturn;
+
+ :local IndentReturn do={
+ :local Prefix [ :tostr $1 ];
+ :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);
+ }
+
+ :local TypeOf [ :typeof $Input ];
+ :local Return [ $IndentReturn "type" $TypeOf $Level ];
+
+ :if ($TypeOf = "array") do={
+ :foreach Key,Value in=$Input do={
+ :set $Return ($Return . "\n" . \
+ [ $IndentReturn "key" $Key ($Level + 1) ] . "\n" . \
+ [ $InspectVarReturn $Value ($Level + 2) ]);
+ }
+ } else={
+ :if ($TypeOf != "nothing") do={
+ :set $Return ($Return . "\n" . \
+ [ $IndentReturn "value" [ $IfThenElse ([ :len $Input ] > 80) \
+ ([ :pick $Input 0 77 ] . "...") $Input ] $Level ]);
+ }
+ }
+ :return $Return;
+}
diff --git a/mod/ipcalc b/mod/ipcalc
index 92e246f..2da00ca 100644
--- a/mod/ipcalc
+++ b/mod/ipcalc
@@ -1,46 +1,3 @@
#!rsc by RouterOS
-# RouterOS script: mod/ipcalc
-# Copyright (c) 2020-2023 Christian Hesse <mail@eworm.de>
-# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md
-
-:global IPCalc;
-:global IPCalcReturn;
-
-# print netmask, network, min host, max host and broadcast
-:set IPCalc do={
- :local Input [ :tostr $1 ];
-
- :global IPCalcReturn;
- :global PrettyPrint;
-
- :local Values [ $IPCalcReturn $1 ];
-
- $PrettyPrint ( \
- "Address: " . $Values->"address" . "\n" . \
- "Netmask: " . $Values->"netmask" . "\n" . \
- "Network: " . $Values->"network" . "\n" . \
- "HostMin: " . $Values->"hostmin" . "\n" . \
- "HostMax: " . $Values->"hostmax" . "\n" . \
- "Broadcast: " . $Values->"broadcast");
-}
-
-# calculate and return netmask, network, min host, max host and broadcast
-:set IPCalcReturn do={
- :local Input [ :tostr $1 ];
- :local Address [ :toip [ :pick $Input 0 [ :find $Input "/" ] ] ];
- :local Bits [ :tonum [ :pick $Input ([ :find $Input "/" ] + 1) [ :len $Input ] ] ];
- :local Mask ((255.255.255.255 << (32 - $Bits)) & 255.255.255.255);
-
- :local Return {
- "address"=$Address;
- "netmask"=$Mask;
- "networkaddress"=($Address & $Mask);
- "networkbits"=$Bits;
- "network"=(($Address & $Mask) . "/" . $Bits);
- "hostmin"=(($Address & $Mask) | 0.0.0.1);
- "hostmax"=(($Address | ~$Mask) ^ 0.0.0.1);
- "broadcast"=($Address | ~$Mask);
- }
-
- :return $Return;
-}
+#
+# dummy for migration
diff --git a/mod/ipcalc.rsc b/mod/ipcalc.rsc
new file mode 100644
index 0000000..92e246f
--- /dev/null
+++ b/mod/ipcalc.rsc
@@ -0,0 +1,46 @@
+#!rsc by RouterOS
+# RouterOS script: mod/ipcalc
+# Copyright (c) 2020-2023 Christian Hesse <mail@eworm.de>
+# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md
+
+:global IPCalc;
+:global IPCalcReturn;
+
+# print netmask, network, min host, max host and broadcast
+:set IPCalc do={
+ :local Input [ :tostr $1 ];
+
+ :global IPCalcReturn;
+ :global PrettyPrint;
+
+ :local Values [ $IPCalcReturn $1 ];
+
+ $PrettyPrint ( \
+ "Address: " . $Values->"address" . "\n" . \
+ "Netmask: " . $Values->"netmask" . "\n" . \
+ "Network: " . $Values->"network" . "\n" . \
+ "HostMin: " . $Values->"hostmin" . "\n" . \
+ "HostMax: " . $Values->"hostmax" . "\n" . \
+ "Broadcast: " . $Values->"broadcast");
+}
+
+# calculate and return netmask, network, min host, max host and broadcast
+:set IPCalcReturn do={
+ :local Input [ :tostr $1 ];
+ :local Address [ :toip [ :pick $Input 0 [ :find $Input "/" ] ] ];
+ :local Bits [ :tonum [ :pick $Input ([ :find $Input "/" ] + 1) [ :len $Input ] ] ];
+ :local Mask ((255.255.255.255 << (32 - $Bits)) & 255.255.255.255);
+
+ :local Return {
+ "address"=$Address;
+ "netmask"=$Mask;
+ "networkaddress"=($Address & $Mask);
+ "networkbits"=$Bits;
+ "network"=(($Address & $Mask) . "/" . $Bits);
+ "hostmin"=(($Address & $Mask) | 0.0.0.1);
+ "hostmax"=(($Address | ~$Mask) ^ 0.0.0.1);
+ "broadcast"=($Address | ~$Mask);
+ }
+
+ :return $Return;
+}
diff --git a/mod/notification-email b/mod/notification-email
index b03e176..2da00ca 100644
--- a/mod/notification-email
+++ b/mod/notification-email
@@ -1,206 +1,3 @@
#!rsc by RouterOS
-# RouterOS script: mod/notification-email
-# Copyright (c) 2013-2023 Christian Hesse <mail@eworm.de>
-# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md
-
-:global FlushEmailQueue;
-:global LogForwardFilterLogForwarding;
-:global NotificationEMailSubject;
-:global NotificationFunctions;
-:global QuotedPrintable;
-:global SendEMail;
-:global SendEMail2;
-
-# flush e-mail queue
-:set FlushEmailQueue do={
- :global EmailQueue;
-
- :global EitherOr;
- :global IsDNSResolving;
- :global IsTimeSync;
- :global LogPrintExit2;
-
- :local AllDone true;
- :local QueueLen [ :len $EmailQueue ];
- :local Scheduler [ /system/scheduler/find where name=$0 ];
-
- :if ([ :len $Scheduler ] > 0 && [ /system/scheduler/get $Scheduler interval ] < 1m) do={
- /system/scheduler/set interval=1m comment="Doing initial checks..." $Scheduler;
- }
-
- :if ([ /tool/e-mail/get last-status ] = "in-progress") do={
- $LogPrintExit2 debug $0 ("Sending mail is currently in progress, not flushing.") false;
- :return false;
- }
-
- :if ([ $IsTimeSync ] = false) do={
- $LogPrintExit2 debug $0 ("Time is not synced, not flushing.") false;
- :return false;
- }
-
- :if ([ :typeof [ :toip [ /tool/e-mail/get address ] ] ] != "ip" && [ $IsDNSResolving ] = false) do={
- $LogPrintExit2 debug $0 ("Server address is a DNS name and resolving fails, not flushing.") false;
- :return false;
- }
-
- :if ([ :len $Scheduler ] > 0 && $QueueLen = 0) do={
- $LogPrintExit2 warning $0 ("Flushing E-Mail messages from scheduler, but queue is empty.") false;
- }
-
- /system/scheduler/set interval=([ $EitherOr $QueueLen 1 ] . "m") comment="Sending..." $Scheduler;
-
- :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={
- $LogPrintExit2 warning $0 ("File '" . $File . "' does not exist, can not attach.") false;
- }
- }
- /tool/e-mail/send 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;
- }
- }
- }
- :if ($Status = "failed") do={
- :set AllDone false;
- :set Wait false;
- }
- } while=($Wait = true);
- }
- }
-
- :if ($AllDone = true && $QueueLen = [ :len $EmailQueue ]) do={
- /system/scheduler/remove $Scheduler;
- :set EmailQueue;
- } else={
- /system/scheduler/set interval=1m comment="Waiting for retry..." $Scheduler;
- }
-}
-
-# generate filter for log-forward
-:set LogForwardFilterLogForwarding do={
- :global EscapeForRegEx;
- :global NotificationEMailSubject;
- :global SymbolForNotification;
-
- :return ("^Error sending e-mail <(" . \
- [ $EscapeForRegEx [ $NotificationEMailSubject ([ $SymbolForNotification \
- "memo" ] . "Log Forwarding") ] ] . "|" . \
- [ $EscapeForRegEx [ $NotificationEMailSubject ([ $SymbolForNotification \
- "warning-sign" ] . "Log Forwarding") ] ] . ")>:");
-}
-
-# generate the e-mail subject
-:set NotificationEMailSubject do={
- :global Identity;
- :global IdentityExtra;
-
- :global QuotedPrintable;
-
- :return [ $QuotedPrintable ("[" . $IdentityExtra . $Identity . "] " . $1) ];
-}
-
-# send notification via e-mail - expects one array argument
-:set ($NotificationFunctions->"email") do={
- :local Notification $1;
-
- :global EmailGeneralTo;
- :global EmailGeneralToOverride;
- :global EmailGeneralCc;
- :global EmailGeneralCcOverride;
- :global EmailQueue;
-
- :global EitherOr;
- :global IfThenElse;
- :global NotificationEMailSubject;
-
- :local To [ $EitherOr ($EmailGeneralToOverride->($Notification->"origin")) $EmailGeneralTo ];
- :local Cc [ $EitherOr ($EmailGeneralCcOverride->($Notification->"origin")) $EmailGeneralCc ];
-
- :local EMailSettings [ /tool/e-mail/get ];
- :if ([ :len $To ] = 0 || ($EMailSettings->"address") = "0.0.0.0" || ($EMailSettings->"from") = "<>") do={
- :return false;
- }
-
- :if ([ :typeof $EmailQueue ] = "nothing") do={
- :set EmailQueue ({});
- }
- :local Signature [ /system/note/get note ];
- :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) "" ]); \
- 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 \
- comment="Queuing new mail..." on-event=(":global FlushEmailQueue; \$FlushEmailQueue;");
- }
-}
-
-# convert string to quoted-printable
-:global QuotedPrintable do={
- :local Input [ :tostr $1 ];
-
- :if ([ :len $Input ] = 0) do={
- :return $Input;
- }
-
- :local Return "";
- :local Chars ("\80\81\82\83\84\85\86\87\88\89\8A\8B\8C\8D\8E\8F\90\91\92\93\94\95\96\97" . \
- "\98\99\9A\9B\9C\9D\9E\9F\A0\A1\A2\A3\A4\A5\A6\A7\A8\A9\AA\AB\AC\AD\AE\AF\B0\B1\B2\B3" . \
- "\B4\B5\B6\B7\B8\B9\BA\BB\BC\BD\BE\BF\C0\C1\C2\C3\C4\C5\C6\C7\C8\C9\CA\CB\CC\CD\CE\CF" . \
- "\D0\D1\D2\D3\D4\D5\D6\D7\D8\D9\DA\DB\DC\DD\DE\DF\E0\E1\E2\E3\E4\E5\E6\E7\E8\E9\EA\EB" . \
- "\EC\ED\EE\EF\F0\F1\F2\F3\F4\F5\F6\F7\F8\F9\FA\FB\FC\FD\FE\FF");
- :local Hex { "0"; "1"; "2"; "3"; "4"; "5"; "6"; "7"; "8"; "9"; "A"; "B"; "C"; "D"; "E"; "F" };
-
- :for I from=0 to=([ :len $Input ] - 1) do={
- :local Char [ :pick $Input $I ];
- :local Replace [ :find $Chars $Char ];
-
- :if ($Char = "=") do={
- :set Char "=3D";
- }
- :if ([ :typeof $Replace ] = "num") do={
- :set Char ("=" . ($Hex->($Replace / 16 + 8)) . ($Hex->($Replace % 16)));
- }
- :set Return ($Return . $Char);
- }
-
- :if ($Input = $Return) do={
- :return $Input;
- }
-
- :return ("=\?utf-8\?Q\?" . $Return . "\?=");
-}
-
-# send notification via e-mail - expects at least two string arguments
-:set SendEMail do={
- :global SendEMail2;
-
- $SendEMail2 ({ subject=$1; message=$2; link=$3 });
-}
-
-# send notification via e-mail - expects one array argument
-:set SendEMail2 do={
- :local Notification $1;
-
- :global NotificationFunctions;
-
- ($NotificationFunctions->"email") ("\$NotificationFunctions->\"email\"") $Notification;
-}
+#
+# dummy for migration
diff --git a/mod/notification-email.rsc b/mod/notification-email.rsc
new file mode 100644
index 0000000..b03e176
--- /dev/null
+++ b/mod/notification-email.rsc
@@ -0,0 +1,206 @@
+#!rsc by RouterOS
+# RouterOS script: mod/notification-email
+# Copyright (c) 2013-2023 Christian Hesse <mail@eworm.de>
+# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md
+
+:global FlushEmailQueue;
+:global LogForwardFilterLogForwarding;
+:global NotificationEMailSubject;
+:global NotificationFunctions;
+:global QuotedPrintable;
+:global SendEMail;
+:global SendEMail2;
+
+# flush e-mail queue
+:set FlushEmailQueue do={
+ :global EmailQueue;
+
+ :global EitherOr;
+ :global IsDNSResolving;
+ :global IsTimeSync;
+ :global LogPrintExit2;
+
+ :local AllDone true;
+ :local QueueLen [ :len $EmailQueue ];
+ :local Scheduler [ /system/scheduler/find where name=$0 ];
+
+ :if ([ :len $Scheduler ] > 0 && [ /system/scheduler/get $Scheduler interval ] < 1m) do={
+ /system/scheduler/set interval=1m comment="Doing initial checks..." $Scheduler;
+ }
+
+ :if ([ /tool/e-mail/get last-status ] = "in-progress") do={
+ $LogPrintExit2 debug $0 ("Sending mail is currently in progress, not flushing.") false;
+ :return false;
+ }
+
+ :if ([ $IsTimeSync ] = false) do={
+ $LogPrintExit2 debug $0 ("Time is not synced, not flushing.") false;
+ :return false;
+ }
+
+ :if ([ :typeof [ :toip [ /tool/e-mail/get address ] ] ] != "ip" && [ $IsDNSResolving ] = false) do={
+ $LogPrintExit2 debug $0 ("Server address is a DNS name and resolving fails, not flushing.") false;
+ :return false;
+ }
+
+ :if ([ :len $Scheduler ] > 0 && $QueueLen = 0) do={
+ $LogPrintExit2 warning $0 ("Flushing E-Mail messages from scheduler, but queue is empty.") false;
+ }
+
+ /system/scheduler/set interval=([ $EitherOr $QueueLen 1 ] . "m") comment="Sending..." $Scheduler;
+
+ :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={
+ $LogPrintExit2 warning $0 ("File '" . $File . "' does not exist, can not attach.") false;
+ }
+ }
+ /tool/e-mail/send 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;
+ }
+ }
+ }
+ :if ($Status = "failed") do={
+ :set AllDone false;
+ :set Wait false;
+ }
+ } while=($Wait = true);
+ }
+ }
+
+ :if ($AllDone = true && $QueueLen = [ :len $EmailQueue ]) do={
+ /system/scheduler/remove $Scheduler;
+ :set EmailQueue;
+ } else={
+ /system/scheduler/set interval=1m comment="Waiting for retry..." $Scheduler;
+ }
+}
+
+# generate filter for log-forward
+:set LogForwardFilterLogForwarding do={
+ :global EscapeForRegEx;
+ :global NotificationEMailSubject;
+ :global SymbolForNotification;
+
+ :return ("^Error sending e-mail <(" . \
+ [ $EscapeForRegEx [ $NotificationEMailSubject ([ $SymbolForNotification \
+ "memo" ] . "Log Forwarding") ] ] . "|" . \
+ [ $EscapeForRegEx [ $NotificationEMailSubject ([ $SymbolForNotification \
+ "warning-sign" ] . "Log Forwarding") ] ] . ")>:");
+}
+
+# generate the e-mail subject
+:set NotificationEMailSubject do={
+ :global Identity;
+ :global IdentityExtra;
+
+ :global QuotedPrintable;
+
+ :return [ $QuotedPrintable ("[" . $IdentityExtra . $Identity . "] " . $1) ];
+}
+
+# send notification via e-mail - expects one array argument
+:set ($NotificationFunctions->"email") do={
+ :local Notification $1;
+
+ :global EmailGeneralTo;
+ :global EmailGeneralToOverride;
+ :global EmailGeneralCc;
+ :global EmailGeneralCcOverride;
+ :global EmailQueue;
+
+ :global EitherOr;
+ :global IfThenElse;
+ :global NotificationEMailSubject;
+
+ :local To [ $EitherOr ($EmailGeneralToOverride->($Notification->"origin")) $EmailGeneralTo ];
+ :local Cc [ $EitherOr ($EmailGeneralCcOverride->($Notification->"origin")) $EmailGeneralCc ];
+
+ :local EMailSettings [ /tool/e-mail/get ];
+ :if ([ :len $To ] = 0 || ($EMailSettings->"address") = "0.0.0.0" || ($EMailSettings->"from") = "<>") do={
+ :return false;
+ }
+
+ :if ([ :typeof $EmailQueue ] = "nothing") do={
+ :set EmailQueue ({});
+ }
+ :local Signature [ /system/note/get note ];
+ :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) "" ]); \
+ 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 \
+ comment="Queuing new mail..." on-event=(":global FlushEmailQueue; \$FlushEmailQueue;");
+ }
+}
+
+# convert string to quoted-printable
+:global QuotedPrintable do={
+ :local Input [ :tostr $1 ];
+
+ :if ([ :len $Input ] = 0) do={
+ :return $Input;
+ }
+
+ :local Return "";
+ :local Chars ("\80\81\82\83\84\85\86\87\88\89\8A\8B\8C\8D\8E\8F\90\91\92\93\94\95\96\97" . \
+ "\98\99\9A\9B\9C\9D\9E\9F\A0\A1\A2\A3\A4\A5\A6\A7\A8\A9\AA\AB\AC\AD\AE\AF\B0\B1\B2\B3" . \
+ "\B4\B5\B6\B7\B8\B9\BA\BB\BC\BD\BE\BF\C0\C1\C2\C3\C4\C5\C6\C7\C8\C9\CA\CB\CC\CD\CE\CF" . \
+ "\D0\D1\D2\D3\D4\D5\D6\D7\D8\D9\DA\DB\DC\DD\DE\DF\E0\E1\E2\E3\E4\E5\E6\E7\E8\E9\EA\EB" . \
+ "\EC\ED\EE\EF\F0\F1\F2\F3\F4\F5\F6\F7\F8\F9\FA\FB\FC\FD\FE\FF");
+ :local Hex { "0"; "1"; "2"; "3"; "4"; "5"; "6"; "7"; "8"; "9"; "A"; "B"; "C"; "D"; "E"; "F" };
+
+ :for I from=0 to=([ :len $Input ] - 1) do={
+ :local Char [ :pick $Input $I ];
+ :local Replace [ :find $Chars $Char ];
+
+ :if ($Char = "=") do={
+ :set Char "=3D";
+ }
+ :if ([ :typeof $Replace ] = "num") do={
+ :set Char ("=" . ($Hex->($Replace / 16 + 8)) . ($Hex->($Replace % 16)));
+ }
+ :set Return ($Return . $Char);
+ }
+
+ :if ($Input = $Return) do={
+ :return $Input;
+ }
+
+ :return ("=\?utf-8\?Q\?" . $Return . "\?=");
+}
+
+# send notification via e-mail - expects at least two string arguments
+:set SendEMail do={
+ :global SendEMail2;
+
+ $SendEMail2 ({ subject=$1; message=$2; link=$3 });
+}
+
+# send notification via e-mail - expects one array argument
+:set SendEMail2 do={
+ :local Notification $1;
+
+ :global NotificationFunctions;
+
+ ($NotificationFunctions->"email") ("\$NotificationFunctions->\"email\"") $Notification;
+}
diff --git a/mod/notification-matrix b/mod/notification-matrix
index 6266b75..2da00ca 100644
--- a/mod/notification-matrix
+++ b/mod/notification-matrix
@@ -1,165 +1,3 @@
#!rsc by RouterOS
-# RouterOS script: mod/notification-matrix
-# Copyright (c) 2013-2023 Michael Gisbers <michael@gisbers.de>
-# Christian Hesse <mail@eworm.de>
-# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md
-
-:global FlushMatrixQueue;
-:global NotificationFunctions;
-:global SendMatrix;
-:global SendMatrix2;
-
-# flush Matrix queue
-:set FlushMatrixQueue do={
- :global MatrixQueue;
-
- :global IsFullyConnected;
- :global LogPrintExit2;
-
- :if ([ $IsFullyConnected ] = false) do={
- $LogPrintExit2 debug $0 ("System is not fully connected, not flushing.") false;
- :return false;
- }
-
- :local AllDone true;
- :local QueueLen [ :len $MatrixQueue ];
-
- :if ([ :len [ /system/scheduler/find where name=$0 ] ] > 0 && $QueueLen = 0) do={
- $LogPrintExit2 warning $0 ("Flushing Matrix messages from scheduler, but queue is empty.") false;
- }
-
- :foreach Id,Message in=$MatrixQueue do={
- :if ([ :typeof $Message ] = "array" ) do={
- :do {
- /tool/fetch check-certificate=yes-without-crl output=none http-method=post \
- ("https://" . $Message->"homeserver" . "/_matrix/client/r0/rooms/" . $Message->"room" . \
- "/send/m.room.message?access_token=" . $Message->"accesstoken") \
- http-data=("{ \"msgtype\": \"m.text\", \"body\": \"" . $Message->"plain" . "\"," . \
- "\"format\": \"org.matrix.custom.html\", \"formatted_body\": \"" . \
- $Message->"formatted" . "\" }") as-value;
- :set ($MatrixQueue->$Id);
- } on-error={
- $LogPrintExit2 debug $0 ("Sending queued Matrix message failed.") false;
- :set AllDone false;
- }
- }
- }
-
- :if ($AllDone = true && $QueueLen = [ :len $MatrixQueue ]) do={
- /system/scheduler/remove [ find where name=$0 ];
- :set MatrixQueue;
- }
-}
-
-# send notification via Matrix - expects one array argument
-:set ($NotificationFunctions->"matrix") do={
- :local Notification $1;
-
- :global Identity;
- :global IdentityExtra;
- :global MatrixAccessToken;
- :global MatrixAccessTokenOverride;
- :global MatrixHomeServer;
- :global MatrixHomeServerOverride;
- :global MatrixQueue;
- :global MatrixRoom;
- :global MatrixRoomOverride;
-
- :global EitherOr;
- :global LogPrintExit2;
- :global SymbolForNotification;
-
- :local PrepareText do={
- :local Input [ :tostr $1 ];
-
- :if ([ :len $Input ] = 0) do={
- :return "";
- }
-
- :local Return "";
- :local Chars {
- "plain"={ "\\"; "\""; "\n" };
- "format"={ "\\"; "\""; "\n"; "&"; "<"; ">" };
- }
- :local Subs {
- "plain"={ "\\\\"; "\\\""; "\\n" };
- "format"={ "\\\\"; "&quot;"; "<br/>"; "&amp;"; "&lt;"; "&gt;" };
- }
-
- :for I from=0 to=([ :len $Input ] - 1) do={
- :local Char [ :pick $Input $I ];
- :local Replace [ :find ($Chars->$2) $Char ];
-
- :if ([ :typeof $Replace ] = "num") do={
- :set Char ($Subs->$2->$Replace);
- }
- :set Return ($Return . $Char);
- }
-
- :return $Return;
- }
-
- :local AccessToken [ $EitherOr ($MatrixAccessTokenOverride->($Notification->"origin")) $MatrixAccessToken ];
- :local HomeServer [ $EitherOr ($MatrixHomeServerOverride->($Notification->"origin")) $MatrixHomeServer ];
- :local Room [ $EitherOr ($MatrixRoomOverride->($Notification->"origin")) $MatrixRoom ];
-
- :if ([ :len $AccessToken ] = 0 || [ :len $HomeServer ] = 0 || [ :len $Room ] = 0) do={
- :return false;
- }
-
- :local Plain [ $PrepareText ("## [" . $IdentityExtra . $Identity . "] " . \
- ($Notification->"subject") . "\n```\n" . ($Notification->"message") . "\n```") "plain" ];
- :local Formatted ("<h2>" . [ $PrepareText ("[" . $IdentityExtra . $Identity . "] " . \
- ($Notification->"subject")) "format" ] . "</h2>" . "<pre><code>" . \
- [ $PrepareText ($Notification->"message") "format" ] . "</code></pre>");
- :if ([ :len ($Notification->"link") ] > 0) do={
- :set Plain ($Plain . "\\n" . [ $SymbolForNotification "link" ] . \
- [ $PrepareText ("[" . $Notification->"link" . "](" . $Notification->"link" . ")") "plain" ]);
- :set Formatted ($Formatted . "<br/>" . [ $SymbolForNotification "link" ] . \
- "<a href=\\\"" . [ $PrepareText ($Notification->"link") "format" ] . "\\\">" . \
- [ $PrepareText ($Notification->"link") "format" ] . "</a>");
- }
-
- :do {
- /tool/fetch check-certificate=yes-without-crl output=none http-method=post \
- ("https://" . $HomeServer . "/_matrix/client/r0/rooms/" . $Room . \
- "/send/m.room.message?access_token=" . $AccessToken) \
- http-data=("{ \"msgtype\": \"m.text\", \"body\": \"" . $Plain . "\"," . \
- "\"format\": \"org.matrix.custom.html\", \"formatted_body\": \"" . \
- $Formatted . "\" }") as-value;
- } on-error={
- $LogPrintExit2 info $0 ("Failed sending Matrix notification! Queuing...") false;
-
- :if ([ :typeof $MatrixQueue ] = "nothing") do={
- :set MatrixQueue ({});
- }
- :local Text ([ $SymbolForNotification "alarm-clock" ] . \
- "This message was queued since " . [ /system/clock/get date ] . \
- " " . [ /system/clock/get time ] . " and may be obsolete.");
- :set Plain ($Plain . "\\n" . $Text);
- :set Formatted ($Formatted . "<br/>" . $Text);
- :set ($MatrixQueue->[ :len $MatrixQueue ]) { room=$Room; \
- accesstoken=$AccessToken; homeserver=$HomeServer; \
- plain=$Plain; formatted=$Formatted };
- :if ([ :len [ /system/scheduler/find where name="\$FlushMatrixQueue" ] ] = 0) do={
- /system/scheduler/add name="\$FlushMatrixQueue" interval=1m start-time=startup \
- on-event=(":global FlushMatrixQueue; \$FlushMatrixQueue;");
- }
- }
-}
-
-# send notification via Matrix - expects at least two string arguments
-:set SendMatrix do={
- :global SendMatrix2;
-
- $SendMatrix2 ({ subject=$1; message=$2; link=$3 });
-}
-
-# send notification via Matrix - expects one array argument
-:set SendMatrix2 do={
- :local Notification $1;
-
- :global NotificationFunctions;
-
- ($NotificationFunctions->"matrix") ("\$NotificationFunctions->\"matrix\"") $Notification;
-}
+#
+# dummy for migration
diff --git a/mod/notification-matrix.rsc b/mod/notification-matrix.rsc
new file mode 100644
index 0000000..6266b75
--- /dev/null
+++ b/mod/notification-matrix.rsc
@@ -0,0 +1,165 @@
+#!rsc by RouterOS
+# RouterOS script: mod/notification-matrix
+# Copyright (c) 2013-2023 Michael Gisbers <michael@gisbers.de>
+# Christian Hesse <mail@eworm.de>
+# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md
+
+:global FlushMatrixQueue;
+:global NotificationFunctions;
+:global SendMatrix;
+:global SendMatrix2;
+
+# flush Matrix queue
+:set FlushMatrixQueue do={
+ :global MatrixQueue;
+
+ :global IsFullyConnected;
+ :global LogPrintExit2;
+
+ :if ([ $IsFullyConnected ] = false) do={
+ $LogPrintExit2 debug $0 ("System is not fully connected, not flushing.") false;
+ :return false;
+ }
+
+ :local AllDone true;
+ :local QueueLen [ :len $MatrixQueue ];
+
+ :if ([ :len [ /system/scheduler/find where name=$0 ] ] > 0 && $QueueLen = 0) do={
+ $LogPrintExit2 warning $0 ("Flushing Matrix messages from scheduler, but queue is empty.") false;
+ }
+
+ :foreach Id,Message in=$MatrixQueue do={
+ :if ([ :typeof $Message ] = "array" ) do={
+ :do {
+ /tool/fetch check-certificate=yes-without-crl output=none http-method=post \
+ ("https://" . $Message->"homeserver" . "/_matrix/client/r0/rooms/" . $Message->"room" . \
+ "/send/m.room.message?access_token=" . $Message->"accesstoken") \
+ http-data=("{ \"msgtype\": \"m.text\", \"body\": \"" . $Message->"plain" . "\"," . \
+ "\"format\": \"org.matrix.custom.html\", \"formatted_body\": \"" . \
+ $Message->"formatted" . "\" }") as-value;
+ :set ($MatrixQueue->$Id);
+ } on-error={
+ $LogPrintExit2 debug $0 ("Sending queued Matrix message failed.") false;
+ :set AllDone false;
+ }
+ }
+ }
+
+ :if ($AllDone = true && $QueueLen = [ :len $MatrixQueue ]) do={
+ /system/scheduler/remove [ find where name=$0 ];
+ :set MatrixQueue;
+ }
+}
+
+# send notification via Matrix - expects one array argument
+:set ($NotificationFunctions->"matrix") do={
+ :local Notification $1;
+
+ :global Identity;
+ :global IdentityExtra;
+ :global MatrixAccessToken;
+ :global MatrixAccessTokenOverride;
+ :global MatrixHomeServer;
+ :global MatrixHomeServerOverride;
+ :global MatrixQueue;
+ :global MatrixRoom;
+ :global MatrixRoomOverride;
+
+ :global EitherOr;
+ :global LogPrintExit2;
+ :global SymbolForNotification;
+
+ :local PrepareText do={
+ :local Input [ :tostr $1 ];
+
+ :if ([ :len $Input ] = 0) do={
+ :return "";
+ }
+
+ :local Return "";
+ :local Chars {
+ "plain"={ "\\"; "\""; "\n" };
+ "format"={ "\\"; "\""; "\n"; "&"; "<"; ">" };
+ }
+ :local Subs {
+ "plain"={ "\\\\"; "\\\""; "\\n" };
+ "format"={ "\\\\"; "&quot;"; "<br/>"; "&amp;"; "&lt;"; "&gt;" };
+ }
+
+ :for I from=0 to=([ :len $Input ] - 1) do={
+ :local Char [ :pick $Input $I ];
+ :local Replace [ :find ($Chars->$2) $Char ];
+
+ :if ([ :typeof $Replace ] = "num") do={
+ :set Char ($Subs->$2->$Replace);
+ }
+ :set Return ($Return . $Char);
+ }
+
+ :return $Return;
+ }
+
+ :local AccessToken [ $EitherOr ($MatrixAccessTokenOverride->($Notification->"origin")) $MatrixAccessToken ];
+ :local HomeServer [ $EitherOr ($MatrixHomeServerOverride->($Notification->"origin")) $MatrixHomeServer ];
+ :local Room [ $EitherOr ($MatrixRoomOverride->($Notification->"origin")) $MatrixRoom ];
+
+ :if ([ :len $AccessToken ] = 0 || [ :len $HomeServer ] = 0 || [ :len $Room ] = 0) do={
+ :return false;
+ }
+
+ :local Plain [ $PrepareText ("## [" . $IdentityExtra . $Identity . "] " . \
+ ($Notification->"subject") . "\n```\n" . ($Notification->"message") . "\n```") "plain" ];
+ :local Formatted ("<h2>" . [ $PrepareText ("[" . $IdentityExtra . $Identity . "] " . \
+ ($Notification->"subject")) "format" ] . "</h2>" . "<pre><code>" . \
+ [ $PrepareText ($Notification->"message") "format" ] . "</code></pre>");
+ :if ([ :len ($Notification->"link") ] > 0) do={
+ :set Plain ($Plain . "\\n" . [ $SymbolForNotification "link" ] . \
+ [ $PrepareText ("[" . $Notification->"link" . "](" . $Notification->"link" . ")") "plain" ]);
+ :set Formatted ($Formatted . "<br/>" . [ $SymbolForNotification "link" ] . \
+ "<a href=\\\"" . [ $PrepareText ($Notification->"link") "format" ] . "\\\">" . \
+ [ $PrepareText ($Notification->"link") "format" ] . "</a>");
+ }
+
+ :do {
+ /tool/fetch check-certificate=yes-without-crl output=none http-method=post \
+ ("https://" . $HomeServer . "/_matrix/client/r0/rooms/" . $Room . \
+ "/send/m.room.message?access_token=" . $AccessToken) \
+ http-data=("{ \"msgtype\": \"m.text\", \"body\": \"" . $Plain . "\"," . \
+ "\"format\": \"org.matrix.custom.html\", \"formatted_body\": \"" . \
+ $Formatted . "\" }") as-value;
+ } on-error={
+ $LogPrintExit2 info $0 ("Failed sending Matrix notification! Queuing...") false;
+
+ :if ([ :typeof $MatrixQueue ] = "nothing") do={
+ :set MatrixQueue ({});
+ }
+ :local Text ([ $SymbolForNotification "alarm-clock" ] . \
+ "This message was queued since " . [ /system/clock/get date ] . \
+ " " . [ /system/clock/get time ] . " and may be obsolete.");
+ :set Plain ($Plain . "\\n" . $Text);
+ :set Formatted ($Formatted . "<br/>" . $Text);
+ :set ($MatrixQueue->[ :len $MatrixQueue ]) { room=$Room; \
+ accesstoken=$AccessToken; homeserver=$HomeServer; \
+ plain=$Plain; formatted=$Formatted };
+ :if ([ :len [ /system/scheduler/find where name="\$FlushMatrixQueue" ] ] = 0) do={
+ /system/scheduler/add name="\$FlushMatrixQueue" interval=1m start-time=startup \
+ on-event=(":global FlushMatrixQueue; \$FlushMatrixQueue;");
+ }
+ }
+}
+
+# send notification via Matrix - expects at least two string arguments
+:set SendMatrix do={
+ :global SendMatrix2;
+
+ $SendMatrix2 ({ subject=$1; message=$2; link=$3 });
+}
+
+# send notification via Matrix - expects one array argument
+:set SendMatrix2 do={
+ :local Notification $1;
+
+ :global NotificationFunctions;
+
+ ($NotificationFunctions->"matrix") ("\$NotificationFunctions->\"matrix\"") $Notification;
+}
diff --git a/mod/notification-telegram b/mod/notification-telegram
index c90e3f0..2da00ca 100644
--- a/mod/notification-telegram
+++ b/mod/notification-telegram
@@ -1,176 +1,3 @@
#!rsc by RouterOS
-# RouterOS script: mod/notification-telegram
-# Copyright (c) 2013-2023 Christian Hesse <mail@eworm.de>
-# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md
-
-:global FlushTelegramQueue;
-:global NotificationFunctions;
-:global SendTelegram;
-:global SendTelegram2;
-
-# flush telegram queue
-:set FlushTelegramQueue do={
- :global TelegramQueue;
-
- :global IsFullyConnected;
- :global LogPrintExit2;
-
- :if ([ $IsFullyConnected ] = false) do={
- $LogPrintExit2 debug $0 ("System is not fully connected, not flushing.") false;
- :return false;
- }
-
- :local AllDone true;
- :local QueueLen [ :len $TelegramQueue ];
-
- :if ([ :len [ /system/scheduler/find where name=$0 ] ] > 0 && $QueueLen = 0) do={
- $LogPrintExit2 warning $0 ("Flushing Telegram messages from scheduler, but queue is empty.") false;
- }
-
- :foreach Id,Message in=$TelegramQueue do={
- :if ([ :typeof $Message ] = "array" ) do={
- :do {
- /tool/fetch check-certificate=yes-without-crl output=none http-method=post \
- ("https://api.telegram.org/bot" . ($Message->"tokenid") . "/sendMessage") \
- http-data=("chat_id=" . ($Message->"chatid") . \
- "&disable_notification=" . ($Message->"silent") . \
- "&reply_to_message_id=" . ($Notification->"replyto") . \
- "&disable_web_page_preview=true&parse_mode=" . ($Message->"parsemode") . \
- "&text=" . ($Message->"text")) as-value;
- :set ($TelegramQueue->$Id);
- } on-error={
- $LogPrintExit2 debug $0 ("Sending queued Telegram message failed.") false;
- :set AllDone false;
- }
- }
- }
-
- :if ($AllDone = true && $QueueLen = [ :len $TelegramQueue ]) do={
- /system/scheduler/remove [ find where name=$0 ];
- :set TelegramQueue;
- }
-}
-
-# send notification via telegram - expects one array argument
-:set ($NotificationFunctions->"telegram") do={
- :local Notification $1;
-
- :global Identity;
- :global IdentityExtra;
- :global TelegramChatId;
- :global TelegramChatIdOverride;
- :global TelegramFixedWidthFont;
- :global TelegramQueue;
- :global TelegramTokenId;
- :global TelegramTokenIdOverride;
-
- :global CertificateAvailable;
- :global CharacterReplace;
- :global EitherOr;
- :global IfThenElse;
- :global LogPrintExit2;
- :global SymbolForNotification;
- :global UrlEncode;
-
- :local EscapeMD do={
- :global TelegramFixedWidthFont;
-
- :global CharacterReplace;
- :global IfThenElse;
-
- :if ($TelegramFixedWidthFont != true) do={
- :return ($1 . [ $IfThenElse ($2 = "body") ("\n") "" ]);
- }
-
- :local Return $1;
- :local Chars {
- "body"={ "\\"; "`" };
- "plain"={ "_"; "*"; "["; "]"; "("; ")"; "~"; "`"; ">";
- "#"; "+"; "-"; "="; "|"; "{"; "}"; "."; "!" };
- }
- :foreach Char in=($Chars->$2) do={
- :set Return [ $CharacterReplace $Return $Char ("\\" . $Char) ];
- }
-
- :if ($2 = "body") do={
- :return ("```\n" . $Return . "\n```");
- }
-
- :return $Return;
- }
-
- :local ChatId [ $EitherOr ($Notification->"chatid") \
- [ $EitherOr ($TelegramChatIdOverride->($Notification->"origin")) $TelegramChatId ] ];
- :local TokenId [ $EitherOr ($TelegramTokenIdOverride->($Notification->"origin")) $TelegramTokenId ];
-
- :if ([ :len $TokenId ] = 0 || [ :len $ChatId ] = 0) do={
- :return false;
- }
-
- :local Truncated false;
- :local Text ("*__" . [ $EscapeMD ("[" . $IdentityExtra . $Identity . "] " . \
- ($Notification->"subject")) "plain" ] . "__*\n\n");
- :local LenSubject [ :len $Text ];
- :local LenMessage [ :len ($Notification->"message") ];
- :local LenLink [ :len ($Notification->"link") ];
- :local LenSum ($LenSubject + $LenMessage + $LenLink);
- :if ($LenSum > 3968) do={
- :set Text ($Text . [ $EscapeMD ([ :pick ($Notification->"message") 0 (3840 - $LenSubject - $LenLink) ] . "...") "body" ]);
- :set Truncated true;
- } else={
- :set Text ($Text . [ $EscapeMD ($Notification->"message") "body" ]);
- }
- :if ($LenLink > 0) do={
- :set Text ($Text . "\n" . [ $SymbolForNotification "link" ] . [ $EscapeMD ($Notification->"link") "plain" ]);
- }
- :if ($Truncated = true) do={
- :set Text ($Text . "\n" . [ $SymbolForNotification "scissors" ] . \
- [ $EscapeMD ("The message was too long and has been truncated, cut off " . \
- (($LenSum - [ :len $Text ]) * 100 / $LenSum) . "%!") "plain" ]);
- }
- :set Text [ $UrlEncode $Text ];
- :local ParseMode [ $IfThenElse ($TelegramFixedWidthFont = true) "MarkdownV2" "" ];
-
- :do {
- :if ([ $CertificateAvailable "Go Daddy Secure Certificate Authority - G2" ] = false) do={
- $LogPrintExit2 warning $0 ("Downloading required certificate failed.") true;
- }
- /tool/fetch check-certificate=yes-without-crl output=none http-method=post \
- ("https://api.telegram.org/bot" . $TokenId . "/sendMessage") \
- http-data=("chat_id=" . $ChatId . "&disable_notification=" . ($Notification->"silent") . \
- "&reply_to_message_id=" . ($Notification->"replyto") . \
- "&disable_web_page_preview=true&parse_mode=" . $ParseMode . "&text=" . $Text) as-value;
- } on-error={
- $LogPrintExit2 info $0 ("Failed sending telegram notification! Queuing...") false;
-
- :if ([ :typeof $TelegramQueue ] = "nothing") do={
- :set TelegramQueue ({});
- }
- :set Text ($Text . [ $UrlEncode ("\n" . [ $SymbolForNotification "alarm-clock" ] . \
- [ $EscapeMD ("This message was queued since " . [ /system/clock/get date ] . \
- " " . [ /system/clock/get time ] . " and may be obsolete.") "plain" ]) ]);
- :set ($TelegramQueue->[ :len $TelegramQueue ]) { chatid=$ChatId; tokenid=$TokenId;
- parsemode=$ParseMode; text=$Text; silent=($Notification->"silent");
- replyto=($Notification->"replyto") };
- :if ([ :len [ /system/scheduler/find where name="\$FlushTelegramQueue" ] ] = 0) do={
- /system/scheduler/add name="\$FlushTelegramQueue" interval=1m start-time=startup \
- on-event=(":global FlushTelegramQueue; \$FlushTelegramQueue;");
- }
- }
-}
-
-# send notification via telegram - expects at least two string arguments
-:set SendTelegram do={
- :global SendTelegram2;
-
- $SendTelegram2 ({ subject=$1; message=$2; link=$3; silent=$4 });
-}
-
-# send notification via telegram - expects one array argument
-:set SendTelegram2 do={
- :local Notification $1;
-
- :global NotificationFunctions;
-
- ($NotificationFunctions->"telegram") ("\$NotificationFunctions->\"telegram\"") $Notification;
-}
+#
+# dummy for migration
diff --git a/mod/notification-telegram.rsc b/mod/notification-telegram.rsc
new file mode 100644
index 0000000..c90e3f0
--- /dev/null
+++ b/mod/notification-telegram.rsc
@@ -0,0 +1,176 @@
+#!rsc by RouterOS
+# RouterOS script: mod/notification-telegram
+# Copyright (c) 2013-2023 Christian Hesse <mail@eworm.de>
+# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md
+
+:global FlushTelegramQueue;
+:global NotificationFunctions;
+:global SendTelegram;
+:global SendTelegram2;
+
+# flush telegram queue
+:set FlushTelegramQueue do={
+ :global TelegramQueue;
+
+ :global IsFullyConnected;
+ :global LogPrintExit2;
+
+ :if ([ $IsFullyConnected ] = false) do={
+ $LogPrintExit2 debug $0 ("System is not fully connected, not flushing.") false;
+ :return false;
+ }
+
+ :local AllDone true;
+ :local QueueLen [ :len $TelegramQueue ];
+
+ :if ([ :len [ /system/scheduler/find where name=$0 ] ] > 0 && $QueueLen = 0) do={
+ $LogPrintExit2 warning $0 ("Flushing Telegram messages from scheduler, but queue is empty.") false;
+ }
+
+ :foreach Id,Message in=$TelegramQueue do={
+ :if ([ :typeof $Message ] = "array" ) do={
+ :do {
+ /tool/fetch check-certificate=yes-without-crl output=none http-method=post \
+ ("https://api.telegram.org/bot" . ($Message->"tokenid") . "/sendMessage") \
+ http-data=("chat_id=" . ($Message->"chatid") . \
+ "&disable_notification=" . ($Message->"silent") . \
+ "&reply_to_message_id=" . ($Notification->"replyto") . \
+ "&disable_web_page_preview=true&parse_mode=" . ($Message->"parsemode") . \
+ "&text=" . ($Message->"text")) as-value;
+ :set ($TelegramQueue->$Id);
+ } on-error={
+ $LogPrintExit2 debug $0 ("Sending queued Telegram message failed.") false;
+ :set AllDone false;
+ }
+ }
+ }
+
+ :if ($AllDone = true && $QueueLen = [ :len $TelegramQueue ]) do={
+ /system/scheduler/remove [ find where name=$0 ];
+ :set TelegramQueue;
+ }
+}
+
+# send notification via telegram - expects one array argument
+:set ($NotificationFunctions->"telegram") do={
+ :local Notification $1;
+
+ :global Identity;
+ :global IdentityExtra;
+ :global TelegramChatId;
+ :global TelegramChatIdOverride;
+ :global TelegramFixedWidthFont;
+ :global TelegramQueue;
+ :global TelegramTokenId;
+ :global TelegramTokenIdOverride;
+
+ :global CertificateAvailable;
+ :global CharacterReplace;
+ :global EitherOr;
+ :global IfThenElse;
+ :global LogPrintExit2;
+ :global SymbolForNotification;
+ :global UrlEncode;
+
+ :local EscapeMD do={
+ :global TelegramFixedWidthFont;
+
+ :global CharacterReplace;
+ :global IfThenElse;
+
+ :if ($TelegramFixedWidthFont != true) do={
+ :return ($1 . [ $IfThenElse ($2 = "body") ("\n") "" ]);
+ }
+
+ :local Return $1;
+ :local Chars {
+ "body"={ "\\"; "`" };
+ "plain"={ "_"; "*"; "["; "]"; "("; ")"; "~"; "`"; ">";
+ "#"; "+"; "-"; "="; "|"; "{"; "}"; "."; "!" };
+ }
+ :foreach Char in=($Chars->$2) do={
+ :set Return [ $CharacterReplace $Return $Char ("\\" . $Char) ];
+ }
+
+ :if ($2 = "body") do={
+ :return ("```\n" . $Return . "\n```");
+ }
+
+ :return $Return;
+ }
+
+ :local ChatId [ $EitherOr ($Notification->"chatid") \
+ [ $EitherOr ($TelegramChatIdOverride->($Notification->"origin")) $TelegramChatId ] ];
+ :local TokenId [ $EitherOr ($TelegramTokenIdOverride->($Notification->"origin")) $TelegramTokenId ];
+
+ :if ([ :len $TokenId ] = 0 || [ :len $ChatId ] = 0) do={
+ :return false;
+ }
+
+ :local Truncated false;
+ :local Text ("*__" . [ $EscapeMD ("[" . $IdentityExtra . $Identity . "] " . \
+ ($Notification->"subject")) "plain" ] . "__*\n\n");
+ :local LenSubject [ :len $Text ];
+ :local LenMessage [ :len ($Notification->"message") ];
+ :local LenLink [ :len ($Notification->"link") ];
+ :local LenSum ($LenSubject + $LenMessage + $LenLink);
+ :if ($LenSum > 3968) do={
+ :set Text ($Text . [ $EscapeMD ([ :pick ($Notification->"message") 0 (3840 - $LenSubject - $LenLink) ] . "...") "body" ]);
+ :set Truncated true;
+ } else={
+ :set Text ($Text . [ $EscapeMD ($Notification->"message") "body" ]);
+ }
+ :if ($LenLink > 0) do={
+ :set Text ($Text . "\n" . [ $SymbolForNotification "link" ] . [ $EscapeMD ($Notification->"link") "plain" ]);
+ }
+ :if ($Truncated = true) do={
+ :set Text ($Text . "\n" . [ $SymbolForNotification "scissors" ] . \
+ [ $EscapeMD ("The message was too long and has been truncated, cut off " . \
+ (($LenSum - [ :len $Text ]) * 100 / $LenSum) . "%!") "plain" ]);
+ }
+ :set Text [ $UrlEncode $Text ];
+ :local ParseMode [ $IfThenElse ($TelegramFixedWidthFont = true) "MarkdownV2" "" ];
+
+ :do {
+ :if ([ $CertificateAvailable "Go Daddy Secure Certificate Authority - G2" ] = false) do={
+ $LogPrintExit2 warning $0 ("Downloading required certificate failed.") true;
+ }
+ /tool/fetch check-certificate=yes-without-crl output=none http-method=post \
+ ("https://api.telegram.org/bot" . $TokenId . "/sendMessage") \
+ http-data=("chat_id=" . $ChatId . "&disable_notification=" . ($Notification->"silent") . \
+ "&reply_to_message_id=" . ($Notification->"replyto") . \
+ "&disable_web_page_preview=true&parse_mode=" . $ParseMode . "&text=" . $Text) as-value;
+ } on-error={
+ $LogPrintExit2 info $0 ("Failed sending telegram notification! Queuing...") false;
+
+ :if ([ :typeof $TelegramQueue ] = "nothing") do={
+ :set TelegramQueue ({});
+ }
+ :set Text ($Text . [ $UrlEncode ("\n" . [ $SymbolForNotification "alarm-clock" ] . \
+ [ $EscapeMD ("This message was queued since " . [ /system/clock/get date ] . \
+ " " . [ /system/clock/get time ] . " and may be obsolete.") "plain" ]) ]);
+ :set ($TelegramQueue->[ :len $TelegramQueue ]) { chatid=$ChatId; tokenid=$TokenId;
+ parsemode=$ParseMode; text=$Text; silent=($Notification->"silent");
+ replyto=($Notification->"replyto") };
+ :if ([ :len [ /system/scheduler/find where name="\$FlushTelegramQueue" ] ] = 0) do={
+ /system/scheduler/add name="\$FlushTelegramQueue" interval=1m start-time=startup \
+ on-event=(":global FlushTelegramQueue; \$FlushTelegramQueue;");
+ }
+ }
+}
+
+# send notification via telegram - expects at least two string arguments
+:set SendTelegram do={
+ :global SendTelegram2;
+
+ $SendTelegram2 ({ subject=$1; message=$2; link=$3; silent=$4 });
+}
+
+# send notification via telegram - expects one array argument
+:set SendTelegram2 do={
+ :local Notification $1;
+
+ :global NotificationFunctions;
+
+ ($NotificationFunctions->"telegram") ("\$NotificationFunctions->\"telegram\"") $Notification;
+}
diff --git a/mod/scriptrunonce b/mod/scriptrunonce
index 7b87b4c..2da00ca 100644
--- a/mod/scriptrunonce
+++ b/mod/scriptrunonce
@@ -1,46 +1,3 @@
#!rsc by RouterOS
-# RouterOS script: mod/scriptrunonece
-# Copyright (c) 2020-2023 Christian Hesse <mail@eworm.de>
-# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md
-
-:global ScriptRunOnce;
-
-# fetch and run script(s) once
-:set ScriptRunOnce do={
- :local Scripts [ :toarray $1 ];
-
- :global ScriptRunOnceBaseUrl;
- :global ScriptRunOnceUrlSuffix;
-
- :global LogPrintExit2;
- :global ValidateSyntax;
-
- :foreach Script in=$Scripts do={
- :if (!($Script ~ "^(ftp|https\?|sftp)://")) do={
- :if ([ :len $ScriptRunOnceBaseUrl ] = 0) do={
- $LogPrintExit2 warning $0 ("Script '" . $Script . "' is not an url and base url is not available.") true;
- }
- :set Script ($ScriptRunOnceBaseUrl . $Script . $ScriptRunOnceUrlSuffix);
- }
-
- :local Source;
- :do {
- :set Source ([ /tool/fetch check-certificate=yes-without-crl $Script output=user as-value ]->"data");
- } on-error={
- $LogPrintExit2 warning $0 ("Failed fetching script '" . $Script . "'!") false;
- }
-
- :if ([ :len $Source ] > 0) do={
- :if ([ $ValidateSyntax $Source ] = true) do={
- :do {
- $LogPrintExit2 info $0 ("Running script '" . $Script . "' now.") false;
- [ :parse $Source ];
- } on-error={
- $LogPrintExit2 warning $0 ("The script '" . $Script . "' failed to run!") false;
- }
- } else={
- $LogPrintExit2 warning $0 ("The script '" . $Script . "' failed syntax validation!") false;
- }
- }
- }
-}
+#
+# dummy for migration
diff --git a/mod/scriptrunonce.rsc b/mod/scriptrunonce.rsc
new file mode 100644
index 0000000..96c49c3
--- /dev/null
+++ b/mod/scriptrunonce.rsc
@@ -0,0 +1,46 @@
+#!rsc by RouterOS
+# RouterOS script: mod/scriptrunonece
+# Copyright (c) 2020-2023 Christian Hesse <mail@eworm.de>
+# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md
+
+:global ScriptRunOnce;
+
+# fetch and run script(s) once
+:set ScriptRunOnce do={
+ :local Scripts [ :toarray $1 ];
+
+ :global ScriptRunOnceBaseUrl;
+ :global ScriptRunOnceUrlSuffix;
+
+ :global LogPrintExit2;
+ :global ValidateSyntax;
+
+ :foreach Script in=$Scripts do={
+ :if (!($Script ~ "^(ftp|https\?|sftp)://")) do={
+ :if ([ :len $ScriptRunOnceBaseUrl ] = 0) do={
+ $LogPrintExit2 warning $0 ("Script '" . $Script . "' is not an url and base url is not available.") true;
+ }
+ :set Script ($ScriptRunOnceBaseUrl . $Script . ".rsc" . $ScriptRunOnceUrlSuffix);
+ }
+
+ :local Source;
+ :do {
+ :set Source ([ /tool/fetch check-certificate=yes-without-crl $Script output=user as-value ]->"data");
+ } on-error={
+ $LogPrintExit2 warning $0 ("Failed fetching script '" . $Script . "'!") false;
+ }
+
+ :if ([ :len $Source ] > 0) do={
+ :if ([ $ValidateSyntax $Source ] = true) do={
+ :do {
+ $LogPrintExit2 info $0 ("Running script '" . $Script . "' now.") false;
+ [ :parse $Source ];
+ } on-error={
+ $LogPrintExit2 warning $0 ("The script '" . $Script . "' failed to run!") false;
+ }
+ } else={
+ $LogPrintExit2 warning $0 ("The script '" . $Script . "' failed syntax validation!") false;
+ }
+ }
+ }
+}
diff --git a/mode-button b/mode-button
index 52ab00e..2da00ca 100644
--- a/mode-button
+++ b/mode-button
@@ -1,76 +1,3 @@
#!rsc by RouterOS
-# RouterOS script: mode-button
-# Copyright (c) 2018-2023 Christian Hesse <mail@eworm.de>
-# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md
#
-# act on multiple mode and reset button presses
-# https://git.eworm.de/cgit/routeros-scripts/about/doc/mode-button.md
-
-:local 0 "mode-button";
-:global GlobalFunctionsReady;
-:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
-
-:global ModeButton;
-
-:global LogPrintExit2;
-
-:set ($ModeButton->"count") ($ModeButton->"count" + 1);
-
-:local Scheduler [ /system/scheduler/find where name="ModeButtonScheduler" ];
-
-:if ([ :len $Scheduler ] = 0) do={
- $LogPrintExit2 info $0 ("Creating scheduler ModeButtonScheduler, counting presses...") false;
- :global ModeButtonScheduler do={
- :global ModeButton;
-
- :global LogPrintExit2;
- :global ModeButtonScheduler;
- :global ValidateSyntax;
-
- :local LEDInvert do={
- :global ModeButtonLED;
-
- :global IfThenElse;
-
- :local LED [ /system/leds/find where leds=$ModeButtonLED type~"^(on|off)\$" interface=[] ];
- :if ([ :len $LED ] = 0) do={
- :return false;
- }
- /system/leds/set type=[ $IfThenElse ([ get $LED type ] = "on") "off" "on" ] $LED;
- }
-
- :local Count ($ModeButton->"count");
- :local Code ($ModeButton->[ :tostr $Count ]);
-
- :set ($ModeButton->"count") 0;
- :set ModeButtonScheduler;
- /system/scheduler/remove ModeButtonScheduler;
-
- :if ([ :len $Code ] > 0) do={
- :if ([ $ValidateSyntax $Code ] = true) do={
- $LogPrintExit2 info $0 ("Acting on " . $Count . " mode-button presses: " . $Code) false;
-
- :for I from=1 to=$Count do={
- $LEDInvert;
- :if ([ /system/routerboard/settings/get silent-boot ] = false) do={
- :beep length=200ms;
- }
- :delay 200ms;
- $LEDInvert;
- :delay 200ms;
- }
-
- [ :parse $Code ];
- } else={
- $LogPrintExit2 warning $0 ("The code for " . $Count . " mode-button presses failed syntax validation!") false;
- }
- } else={
- $LogPrintExit2 info $0 ("No action defined for " . $Count . " mode-button presses.") false;
- }
- }
- /system/scheduler/add name="ModeButtonScheduler" \
- on-event=":global ModeButtonScheduler; \$ModeButtonScheduler;" interval=3s;
-} else={
- $LogPrintExit2 debug $0 ("Updating scheduler ModeButtonScheduler...") false;
- /system/scheduler/set $Scheduler start-time=[ /system/clock/get time ];
-}
+# dummy for migration
diff --git a/mode-button.rsc b/mode-button.rsc
new file mode 100644
index 0000000..52ab00e
--- /dev/null
+++ b/mode-button.rsc
@@ -0,0 +1,76 @@
+#!rsc by RouterOS
+# RouterOS script: mode-button
+# Copyright (c) 2018-2023 Christian Hesse <mail@eworm.de>
+# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md
+#
+# act on multiple mode and reset button presses
+# https://git.eworm.de/cgit/routeros-scripts/about/doc/mode-button.md
+
+:local 0 "mode-button";
+:global GlobalFunctionsReady;
+:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
+
+:global ModeButton;
+
+:global LogPrintExit2;
+
+:set ($ModeButton->"count") ($ModeButton->"count" + 1);
+
+:local Scheduler [ /system/scheduler/find where name="ModeButtonScheduler" ];
+
+:if ([ :len $Scheduler ] = 0) do={
+ $LogPrintExit2 info $0 ("Creating scheduler ModeButtonScheduler, counting presses...") false;
+ :global ModeButtonScheduler do={
+ :global ModeButton;
+
+ :global LogPrintExit2;
+ :global ModeButtonScheduler;
+ :global ValidateSyntax;
+
+ :local LEDInvert do={
+ :global ModeButtonLED;
+
+ :global IfThenElse;
+
+ :local LED [ /system/leds/find where leds=$ModeButtonLED type~"^(on|off)\$" interface=[] ];
+ :if ([ :len $LED ] = 0) do={
+ :return false;
+ }
+ /system/leds/set type=[ $IfThenElse ([ get $LED type ] = "on") "off" "on" ] $LED;
+ }
+
+ :local Count ($ModeButton->"count");
+ :local Code ($ModeButton->[ :tostr $Count ]);
+
+ :set ($ModeButton->"count") 0;
+ :set ModeButtonScheduler;
+ /system/scheduler/remove ModeButtonScheduler;
+
+ :if ([ :len $Code ] > 0) do={
+ :if ([ $ValidateSyntax $Code ] = true) do={
+ $LogPrintExit2 info $0 ("Acting on " . $Count . " mode-button presses: " . $Code) false;
+
+ :for I from=1 to=$Count do={
+ $LEDInvert;
+ :if ([ /system/routerboard/settings/get silent-boot ] = false) do={
+ :beep length=200ms;
+ }
+ :delay 200ms;
+ $LEDInvert;
+ :delay 200ms;
+ }
+
+ [ :parse $Code ];
+ } else={
+ $LogPrintExit2 warning $0 ("The code for " . $Count . " mode-button presses failed syntax validation!") false;
+ }
+ } else={
+ $LogPrintExit2 info $0 ("No action defined for " . $Count . " mode-button presses.") false;
+ }
+ }
+ /system/scheduler/add name="ModeButtonScheduler" \
+ on-event=":global ModeButtonScheduler; \$ModeButtonScheduler;" interval=3s;
+} else={
+ $LogPrintExit2 debug $0 ("Updating scheduler ModeButtonScheduler...") false;
+ /system/scheduler/set $Scheduler start-time=[ /system/clock/get time ];
+}
diff --git a/netwatch-dns b/netwatch-dns
index 4eb3285..2da00ca 100644
--- a/netwatch-dns
+++ b/netwatch-dns
@@ -1,94 +1,3 @@
#!rsc by RouterOS
-# RouterOS script: netwatch-dns
-# Copyright (c) 2022-2023 Christian Hesse <mail@eworm.de>
-# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md
#
-# monitor and manage dns/doh with netwatch
-# https://git.eworm.de/cgit/routeros-scripts/about/doc/netwatch-dns.md
-
-:local 0 "netwatch-dns";
-:global GlobalFunctionsReady;
-:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
-
-:global CertificateAvailable;
-:global EitherOr;
-:global LogPrintExit2;
-:global ParseKeyValueStore;
-:global ScriptLock;
-
-$ScriptLock $0;
-
-:if ([ /system/resource/get uptime ] < 5m) do={
- $LogPrintExit2 info $0 ("System just booted, giving netwatch some time to settle.") true;
-}
-
-:local DnsServers ({});
-:local DnsFallback ({});
-:local DnsCurrent [ /ip/dns/get servers ];
-
-:foreach Host in=[ /tool/netwatch/find where comment~"dns" !disabled ] do={
- :local HostVal [ /tool/netwatch/get $Host ];
- :local HostInfo [ $ParseKeyValueStore ($HostVal->"comment") ];
-
- :if ($HostVal->"status" = "up" && $HostInfo->"disabled" != true) do={
- :if ($HostInfo->"dns" = true) do={
- :set DnsServers ($DnsServers, $HostVal->"host");
- }
- :if ($HostInfo->"dns-fallback" = true) do={
- :set DnsFallback ($DnsFallback, $HostVal->"host");
- }
- }
-}
-
-:if ([ :len $DnsServers ] > 0) do={
- :if ($DnsServers != $DnsCurrent) do={
- $LogPrintExit2 info $0 ("Updating DNS servers: " . [ :tostr $DnsServers ]) false;
- /ip/dns/set servers=$DnsServers;
- /ip/dns/cache/flush;
- }
-} else={
- :if ([ :len $DnsFallback ] > 0) do={
- :if ($DnsFallback != $DnsCurrent) do={
- $LogPrintExit2 info $0 ("Updating DNS servers to fallback: " . \
- [ :tostr $DnsFallback ]) false;
- /ip/dns/set servers=$DnsFallback;
- /ip/dns/cache/flush;
- }
- }
-}
-
-:local DohServer "";
-:local DohCurrent [ /ip/dns/get use-doh-server ];
-:local DohCert "";
-
-:foreach Host in=[ /tool/netwatch/find where comment~"doh" !disabled ] do={
- :local HostVal [ /tool/netwatch/get $Host ];
- :local HostInfo [ $ParseKeyValueStore ($HostVal->"comment") ];
-
- :if ($HostVal->"status" = "up" && $HostInfo->"doh" = true && \
- $HostInfo->"disabled" != true && $DohServer = "") do={
- :set DohServer [ $EitherOr ($HostInfo->"doh-url") \
- ("https://" . $HostVal->"host" . "/dns-query") ];
- :set DohCert ($HostInfo->"doh-cert");
- }
-}
-
-:if ($DohServer != "") do={
- :if ($DohServer != $DohCurrent) do={
- $LogPrintExit2 info $0 ("Updating DoH server: " . $DohServer) false;
- :if ([ :len $DohCert ] > 0) do={
- /ip/dns/set use-doh-server="";
- :if ([ $CertificateAvailable $DohCert ] = false) do={
- $LogPrintExit2 warning $0 ("Downloading certificate failed, trying without.") false;
- }
- }
- /ip/dns/set use-doh-server=$DohServer;
- /ip/dns/cache/flush;
- }
-} else={
- :if ($DohCurrent != "") do={
- $LogPrintExit2 info $0 ("DoH server (" . $DohCurrent . ") is down, disabling.") false;
- /ip/dns/set use-doh-server="";
- /ip/dns/cache/flush;
- }
-}
+# dummy for migration
diff --git a/netwatch-dns.rsc b/netwatch-dns.rsc
new file mode 100644
index 0000000..4eb3285
--- /dev/null
+++ b/netwatch-dns.rsc
@@ -0,0 +1,94 @@
+#!rsc by RouterOS
+# RouterOS script: netwatch-dns
+# Copyright (c) 2022-2023 Christian Hesse <mail@eworm.de>
+# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md
+#
+# monitor and manage dns/doh with netwatch
+# https://git.eworm.de/cgit/routeros-scripts/about/doc/netwatch-dns.md
+
+:local 0 "netwatch-dns";
+:global GlobalFunctionsReady;
+:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
+
+:global CertificateAvailable;
+:global EitherOr;
+:global LogPrintExit2;
+:global ParseKeyValueStore;
+:global ScriptLock;
+
+$ScriptLock $0;
+
+:if ([ /system/resource/get uptime ] < 5m) do={
+ $LogPrintExit2 info $0 ("System just booted, giving netwatch some time to settle.") true;
+}
+
+:local DnsServers ({});
+:local DnsFallback ({});
+:local DnsCurrent [ /ip/dns/get servers ];
+
+:foreach Host in=[ /tool/netwatch/find where comment~"dns" !disabled ] do={
+ :local HostVal [ /tool/netwatch/get $Host ];
+ :local HostInfo [ $ParseKeyValueStore ($HostVal->"comment") ];
+
+ :if ($HostVal->"status" = "up" && $HostInfo->"disabled" != true) do={
+ :if ($HostInfo->"dns" = true) do={
+ :set DnsServers ($DnsServers, $HostVal->"host");
+ }
+ :if ($HostInfo->"dns-fallback" = true) do={
+ :set DnsFallback ($DnsFallback, $HostVal->"host");
+ }
+ }
+}
+
+:if ([ :len $DnsServers ] > 0) do={
+ :if ($DnsServers != $DnsCurrent) do={
+ $LogPrintExit2 info $0 ("Updating DNS servers: " . [ :tostr $DnsServers ]) false;
+ /ip/dns/set servers=$DnsServers;
+ /ip/dns/cache/flush;
+ }
+} else={
+ :if ([ :len $DnsFallback ] > 0) do={
+ :if ($DnsFallback != $DnsCurrent) do={
+ $LogPrintExit2 info $0 ("Updating DNS servers to fallback: " . \
+ [ :tostr $DnsFallback ]) false;
+ /ip/dns/set servers=$DnsFallback;
+ /ip/dns/cache/flush;
+ }
+ }
+}
+
+:local DohServer "";
+:local DohCurrent [ /ip/dns/get use-doh-server ];
+:local DohCert "";
+
+:foreach Host in=[ /tool/netwatch/find where comment~"doh" !disabled ] do={
+ :local HostVal [ /tool/netwatch/get $Host ];
+ :local HostInfo [ $ParseKeyValueStore ($HostVal->"comment") ];
+
+ :if ($HostVal->"status" = "up" && $HostInfo->"doh" = true && \
+ $HostInfo->"disabled" != true && $DohServer = "") do={
+ :set DohServer [ $EitherOr ($HostInfo->"doh-url") \
+ ("https://" . $HostVal->"host" . "/dns-query") ];
+ :set DohCert ($HostInfo->"doh-cert");
+ }
+}
+
+:if ($DohServer != "") do={
+ :if ($DohServer != $DohCurrent) do={
+ $LogPrintExit2 info $0 ("Updating DoH server: " . $DohServer) false;
+ :if ([ :len $DohCert ] > 0) do={
+ /ip/dns/set use-doh-server="";
+ :if ([ $CertificateAvailable $DohCert ] = false) do={
+ $LogPrintExit2 warning $0 ("Downloading certificate failed, trying without.") false;
+ }
+ }
+ /ip/dns/set use-doh-server=$DohServer;
+ /ip/dns/cache/flush;
+ }
+} else={
+ :if ($DohCurrent != "") do={
+ $LogPrintExit2 info $0 ("DoH server (" . $DohCurrent . ") is down, disabling.") false;
+ /ip/dns/set use-doh-server="";
+ /ip/dns/cache/flush;
+ }
+}
diff --git a/netwatch-notify b/netwatch-notify
index d04d23f..2da00ca 100644
--- a/netwatch-notify
+++ b/netwatch-notify
@@ -1,186 +1,3 @@
#!rsc by RouterOS
-# RouterOS script: netwatch-notify
-# Copyright (c) 2020-2023 Christian Hesse <mail@eworm.de>
-# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md
#
-# monitor netwatch and send notifications
-# https://git.eworm.de/cgit/routeros-scripts/about/doc/netwatch-notify.md
-
-:local 0 "netwatch-notify";
-:global GlobalFunctionsReady;
-:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
-
-:global NetwatchNotify;
-
-:global EitherOr;
-:global IfThenElse;
-:global IsDNSResolving;
-:global LogPrintExit2;
-:global ParseKeyValueStore;
-:global ScriptFromTerminal;
-:global ScriptLock;
-:global SendNotification2;
-:global SymbolForNotification;
-
-:local NetwatchNotifyHook do={
- :local Name [ :tostr $1 ];
- :local Type [ :tostr $2 ];
- :local State [ :tostr $3 ];
- :local Hook [ :tostr $4 ];
-
- :global LogPrintExit2;
- :global ValidateSyntax;
-
- :if ([ $ValidateSyntax $Hook ] = true) do={
- :do {
- [ :parse $Hook ];
- } on-error={
- $LogPrintExit2 warning $0 ("The " . $State . "-hook for " . $Type . " '" . $Name . \
- "' failed to run.") false;
- :return ("The hook failed to run.");
- }
- } else={
- $LogPrintExit2 warning $0 ("The " . $State . "-hook for " . $Type . " '" . $Name . \
- "' failed syntax validation.") false;
- :return ("The hook failed syntax validation.");
- }
-
- $LogPrintExit2 info $0 ("Ran hook on " . $Type . " '" . $Name . "' " . $State . ": " . \
- $Hook) false;
- :return ("Ran hook:\n" . $Hook);
-}
-
-$ScriptLock $0;
-
-:local ScriptFromTerminalCached [ $ScriptFromTerminal $0 ];
-
-:if ([ /system/resource/get uptime ] < 5m) do={
- $LogPrintExit2 info $0 ("System just booted, giving netwatch some time to settle.") true;
-}
-
-:if ([ :typeof $NetwatchNotify ] = "nothing") do={
- :set NetwatchNotify ({});
-}
-
-:foreach Host in=[ /tool/netwatch/find where comment~"notify" !disabled ] do={
- :local HostVal [ /tool/netwatch/get $Host ];
- :local Type [ $IfThenElse ($HostVal->"type" ~ "^(https?-get|tcp-conn)\$") "service" "host" ];
- :local HostInfo [ $ParseKeyValueStore ($HostVal->"comment") ];
- :local HostDetails ($HostVal->"host" . \
- [ $IfThenElse ([ :len ($HostInfo->"resolve") ] > 0) (", " . $HostInfo->"resolve") ]);
-
- :if ($HostInfo->"notify" = true && $HostInfo->"disabled" != true) do={
- :local Name [ $EitherOr ($HostInfo->"name") ($HostVal->"name") ];
-
- :local Metric { "count-down"=0; "count-up"=0; "notified"=false; "resolve-failcnt"=0 };
- :if ([ :typeof ($NetwatchNotify->$Name) ] = "array") do={
- :set $Metric ($NetwatchNotify->$Name);
- }
-
- :if ([ :typeof ($HostInfo->"resolve") ] = "str") do={
- :if ([ $IsDNSResolving ] = true) do={
- :do {
- :local Resolve [ :resolve ($HostInfo->"resolve") ];
- :if ($Resolve != $HostVal->"host") do={
- $LogPrintExit2 info $0 ("Name '" . $HostInfo->"resolve" . [ $IfThenElse \
- ($HostInfo->"resolve" != $HostInfo->"name") ("' for " . $Type . " '" . \
- $HostInfo->"name") "" ] . "' resolves to different address " . $Resolve . \
- ", updating.") false;
- /tool/netwatch/set host=$Resolve $Host;
- :set ($Metric->"resolve-failcnt") 0;
- }
- } on-error={
- :set ($Metric->"resolve-failcnt") ($Metric->"resolve-failcnt" + 1);
- :if ($Metric->"resolve-failcnt" = 3) do={
- $LogPrintExit2 warning $0 ("Resolving name '" . $HostInfo->"resolve" . [ $IfThenElse \
- ($HostInfo->"resolve" != $HostInfo->"name") ("' for " . $Type . " '" . \
- $HostInfo->"name") "" ] . "' failed.") false;
- }
- }
- }
- }
-
- :if ($HostVal->"status" = "up") do={
- :local CountDown ($Metric->"count-down");
- :if ($CountDown > 0) do={
- $LogPrintExit2 info $0 \
- ("The " . $Type . " '" . $Name . "' (" . $HostDetails . ") is up.") false;
- :set ($Metric->"count-down") 0;
- }
- :set ($Metric->"count-up") ($Metric->"count-up" + 1);
- :if ($Metric->"notified" = true) do={
- :local Message ("The " . $Type . " '" . $Name . "' (" . $HostDetails . \
- ") is up since " . $HostVal->"since" . ".\n" . \
- "It was down for " . $CountDown . " checks since " . ($Metric->"since") . ".");
- :if ([ :typeof ($HostInfo->"up-hook") ] = "str") do={
- :set Message ($Message . "\n\n" . [ $NetwatchNotifyHook $Name $Type "up" \
- ($HostInfo->"up-hook") ]);
- }
- $SendNotification2 ({ origin=$0; silent=($HostInfo->"silent"); \
- subject=([ $SymbolForNotification "white-heavy-check-mark" ] . "Netwatch Notify: " . \
- $Name . " up"); \
- message=$Message });
- }
- :set ($Metric->"notified") false;
- :set ($Metric->"parent") ($HostInfo->"parent");
- :set ($Metric->"since");
- } else={
- :set ($Metric->"count-down") ($Metric->"count-down" + 1);
- :set ($Metric->"count-up") 0;
- :set ($Metric->"parent") ($HostInfo->"parent");
- :set ($Metric->"since") ($HostVal->"since");
- :local CountDown [ $IfThenElse ([ :tonum ($HostInfo->"count") ] > 0) ($HostInfo->"count") 5 ];
- :local Parent ($HostInfo->"parent");
- :local ParentUp false;
- :while ([ :len $Parent ] > 0) do={
- :set CountDown ($CountDown + 1);
- :set Parent ($NetwatchNotify->$Parent->"parent");
- }
- :set Parent ($HostInfo->"parent");
- :local ParentNotified false;
- :while ($ParentNotified = false && [ :len $Parent ] > 0) do={
- :set ParentNotified [ $IfThenElse (($NetwatchNotify->$Parent->"notified") = true) \
- true false ];
- :set ParentUp ($NetwatchNotify->$Parent->"count-up");
- :if ($ParentNotified = false) do={
- :set Parent ($NetwatchNotify->$Parent->"parent");
- }
- }
- :if ($Metric->"notified" = false || $Metric->"count-down" % 120 = 0 || \
- $ScriptFromTerminalCached = true) do={
- $LogPrintExit2 [ $IfThenElse ($HostInfo->"no-down-notification" != true) info debug ] $0 \
- ("The " . $Type . " '" . $Name . "' (" . $HostDetails . ") is down for " . \
- $Metric->"count-down" . " checks, " . [ $IfThenElse ($ParentNotified = false) [ $IfThenElse \
- ($Metric->"notified" = true) ("already notified.") ($CountDown - $Metric->"count-down" . \
- " to go.") ] ("parent " . $Type . " " . $Parent . " is down.") ]) false;
- }
- :if ((($CountDown * 2) - ($Metric->"count-down" * 3)) / 2 = 0 && \
- [ :typeof ($HostInfo->"pre-down-hook") ] = "str") do={
- $NetwatchNotifyHook $Name $Type "pre-down" ($HostInfo->"pre-down-hook");
- }
- :if ($ParentNotified = false && $Metric->"count-down" >= $CountDown && \
- ($ParentUp = false || $ParentUp > 2) && $Metric->"notified" != true) do={
- :local Message ("The " . $Type . " '" . $Name . "' (" . $HostDetails . \
- ") is down since " . $HostVal->"since" . ".");
- :if ([ :typeof ($HostInfo->"down-hook") ] = "str") do={
- :set Message ($Message . "\n\n" . [ $NetwatchNotifyHook $Name $Type "down" \
- ($HostInfo->"down-hook") ]);
- }
- :if ($HostInfo->"no-down-notification" != true) do={
- $SendNotification2 ({ origin=$0; silent=($HostInfo->"silent"); \
- subject=([ $SymbolForNotification "cross-mark" ] . "Netwatch Notify: " . \
- $Name . " down"); \
- message=$Message });
- }
- :set ($Metric->"notified") true;
- }
- }
- :set ($NetwatchNotify->$Name) {
- "count-down"=($Metric->"count-down");
- "count-up"=($Metric->"count-up");
- "notified"=($Metric->"notified");
- "parent"=($Metric->"parent");
- "resolve-failcnt"=($Metric->"resolve-failcnt");
- "since"=($Metric->"since") };
- }
-}
+# dummy for migration
diff --git a/netwatch-notify.rsc b/netwatch-notify.rsc
new file mode 100644
index 0000000..d04d23f
--- /dev/null
+++ b/netwatch-notify.rsc
@@ -0,0 +1,186 @@
+#!rsc by RouterOS
+# RouterOS script: netwatch-notify
+# Copyright (c) 2020-2023 Christian Hesse <mail@eworm.de>
+# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md
+#
+# monitor netwatch and send notifications
+# https://git.eworm.de/cgit/routeros-scripts/about/doc/netwatch-notify.md
+
+:local 0 "netwatch-notify";
+:global GlobalFunctionsReady;
+:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
+
+:global NetwatchNotify;
+
+:global EitherOr;
+:global IfThenElse;
+:global IsDNSResolving;
+:global LogPrintExit2;
+:global ParseKeyValueStore;
+:global ScriptFromTerminal;
+:global ScriptLock;
+:global SendNotification2;
+:global SymbolForNotification;
+
+:local NetwatchNotifyHook do={
+ :local Name [ :tostr $1 ];
+ :local Type [ :tostr $2 ];
+ :local State [ :tostr $3 ];
+ :local Hook [ :tostr $4 ];
+
+ :global LogPrintExit2;
+ :global ValidateSyntax;
+
+ :if ([ $ValidateSyntax $Hook ] = true) do={
+ :do {
+ [ :parse $Hook ];
+ } on-error={
+ $LogPrintExit2 warning $0 ("The " . $State . "-hook for " . $Type . " '" . $Name . \
+ "' failed to run.") false;
+ :return ("The hook failed to run.");
+ }
+ } else={
+ $LogPrintExit2 warning $0 ("The " . $State . "-hook for " . $Type . " '" . $Name . \
+ "' failed syntax validation.") false;
+ :return ("The hook failed syntax validation.");
+ }
+
+ $LogPrintExit2 info $0 ("Ran hook on " . $Type . " '" . $Name . "' " . $State . ": " . \
+ $Hook) false;
+ :return ("Ran hook:\n" . $Hook);
+}
+
+$ScriptLock $0;
+
+:local ScriptFromTerminalCached [ $ScriptFromTerminal $0 ];
+
+:if ([ /system/resource/get uptime ] < 5m) do={
+ $LogPrintExit2 info $0 ("System just booted, giving netwatch some time to settle.") true;
+}
+
+:if ([ :typeof $NetwatchNotify ] = "nothing") do={
+ :set NetwatchNotify ({});
+}
+
+:foreach Host in=[ /tool/netwatch/find where comment~"notify" !disabled ] do={
+ :local HostVal [ /tool/netwatch/get $Host ];
+ :local Type [ $IfThenElse ($HostVal->"type" ~ "^(https?-get|tcp-conn)\$") "service" "host" ];
+ :local HostInfo [ $ParseKeyValueStore ($HostVal->"comment") ];
+ :local HostDetails ($HostVal->"host" . \
+ [ $IfThenElse ([ :len ($HostInfo->"resolve") ] > 0) (", " . $HostInfo->"resolve") ]);
+
+ :if ($HostInfo->"notify" = true && $HostInfo->"disabled" != true) do={
+ :local Name [ $EitherOr ($HostInfo->"name") ($HostVal->"name") ];
+
+ :local Metric { "count-down"=0; "count-up"=0; "notified"=false; "resolve-failcnt"=0 };
+ :if ([ :typeof ($NetwatchNotify->$Name) ] = "array") do={
+ :set $Metric ($NetwatchNotify->$Name);
+ }
+
+ :if ([ :typeof ($HostInfo->"resolve") ] = "str") do={
+ :if ([ $IsDNSResolving ] = true) do={
+ :do {
+ :local Resolve [ :resolve ($HostInfo->"resolve") ];
+ :if ($Resolve != $HostVal->"host") do={
+ $LogPrintExit2 info $0 ("Name '" . $HostInfo->"resolve" . [ $IfThenElse \
+ ($HostInfo->"resolve" != $HostInfo->"name") ("' for " . $Type . " '" . \
+ $HostInfo->"name") "" ] . "' resolves to different address " . $Resolve . \
+ ", updating.") false;
+ /tool/netwatch/set host=$Resolve $Host;
+ :set ($Metric->"resolve-failcnt") 0;
+ }
+ } on-error={
+ :set ($Metric->"resolve-failcnt") ($Metric->"resolve-failcnt" + 1);
+ :if ($Metric->"resolve-failcnt" = 3) do={
+ $LogPrintExit2 warning $0 ("Resolving name '" . $HostInfo->"resolve" . [ $IfThenElse \
+ ($HostInfo->"resolve" != $HostInfo->"name") ("' for " . $Type . " '" . \
+ $HostInfo->"name") "" ] . "' failed.") false;
+ }
+ }
+ }
+ }
+
+ :if ($HostVal->"status" = "up") do={
+ :local CountDown ($Metric->"count-down");
+ :if ($CountDown > 0) do={
+ $LogPrintExit2 info $0 \
+ ("The " . $Type . " '" . $Name . "' (" . $HostDetails . ") is up.") false;
+ :set ($Metric->"count-down") 0;
+ }
+ :set ($Metric->"count-up") ($Metric->"count-up" + 1);
+ :if ($Metric->"notified" = true) do={
+ :local Message ("The " . $Type . " '" . $Name . "' (" . $HostDetails . \
+ ") is up since " . $HostVal->"since" . ".\n" . \
+ "It was down for " . $CountDown . " checks since " . ($Metric->"since") . ".");
+ :if ([ :typeof ($HostInfo->"up-hook") ] = "str") do={
+ :set Message ($Message . "\n\n" . [ $NetwatchNotifyHook $Name $Type "up" \
+ ($HostInfo->"up-hook") ]);
+ }
+ $SendNotification2 ({ origin=$0; silent=($HostInfo->"silent"); \
+ subject=([ $SymbolForNotification "white-heavy-check-mark" ] . "Netwatch Notify: " . \
+ $Name . " up"); \
+ message=$Message });
+ }
+ :set ($Metric->"notified") false;
+ :set ($Metric->"parent") ($HostInfo->"parent");
+ :set ($Metric->"since");
+ } else={
+ :set ($Metric->"count-down") ($Metric->"count-down" + 1);
+ :set ($Metric->"count-up") 0;
+ :set ($Metric->"parent") ($HostInfo->"parent");
+ :set ($Metric->"since") ($HostVal->"since");
+ :local CountDown [ $IfThenElse ([ :tonum ($HostInfo->"count") ] > 0) ($HostInfo->"count") 5 ];
+ :local Parent ($HostInfo->"parent");
+ :local ParentUp false;
+ :while ([ :len $Parent ] > 0) do={
+ :set CountDown ($CountDown + 1);
+ :set Parent ($NetwatchNotify->$Parent->"parent");
+ }
+ :set Parent ($HostInfo->"parent");
+ :local ParentNotified false;
+ :while ($ParentNotified = false && [ :len $Parent ] > 0) do={
+ :set ParentNotified [ $IfThenElse (($NetwatchNotify->$Parent->"notified") = true) \
+ true false ];
+ :set ParentUp ($NetwatchNotify->$Parent->"count-up");
+ :if ($ParentNotified = false) do={
+ :set Parent ($NetwatchNotify->$Parent->"parent");
+ }
+ }
+ :if ($Metric->"notified" = false || $Metric->"count-down" % 120 = 0 || \
+ $ScriptFromTerminalCached = true) do={
+ $LogPrintExit2 [ $IfThenElse ($HostInfo->"no-down-notification" != true) info debug ] $0 \
+ ("The " . $Type . " '" . $Name . "' (" . $HostDetails . ") is down for " . \
+ $Metric->"count-down" . " checks, " . [ $IfThenElse ($ParentNotified = false) [ $IfThenElse \
+ ($Metric->"notified" = true) ("already notified.") ($CountDown - $Metric->"count-down" . \
+ " to go.") ] ("parent " . $Type . " " . $Parent . " is down.") ]) false;
+ }
+ :if ((($CountDown * 2) - ($Metric->"count-down" * 3)) / 2 = 0 && \
+ [ :typeof ($HostInfo->"pre-down-hook") ] = "str") do={
+ $NetwatchNotifyHook $Name $Type "pre-down" ($HostInfo->"pre-down-hook");
+ }
+ :if ($ParentNotified = false && $Metric->"count-down" >= $CountDown && \
+ ($ParentUp = false || $ParentUp > 2) && $Metric->"notified" != true) do={
+ :local Message ("The " . $Type . " '" . $Name . "' (" . $HostDetails . \
+ ") is down since " . $HostVal->"since" . ".");
+ :if ([ :typeof ($HostInfo->"down-hook") ] = "str") do={
+ :set Message ($Message . "\n\n" . [ $NetwatchNotifyHook $Name $Type "down" \
+ ($HostInfo->"down-hook") ]);
+ }
+ :if ($HostInfo->"no-down-notification" != true) do={
+ $SendNotification2 ({ origin=$0; silent=($HostInfo->"silent"); \
+ subject=([ $SymbolForNotification "cross-mark" ] . "Netwatch Notify: " . \
+ $Name . " down"); \
+ message=$Message });
+ }
+ :set ($Metric->"notified") true;
+ }
+ }
+ :set ($NetwatchNotify->$Name) {
+ "count-down"=($Metric->"count-down");
+ "count-up"=($Metric->"count-up");
+ "notified"=($Metric->"notified");
+ "parent"=($Metric->"parent");
+ "resolve-failcnt"=($Metric->"resolve-failcnt");
+ "since"=($Metric->"since") };
+ }
+}
diff --git a/news-and-changes.rsc b/news-and-changes.rsc
new file mode 100644
index 0000000..e532496
--- /dev/null
+++ b/news-and-changes.rsc
@@ -0,0 +1,16 @@
+# News, changes and migration by RouterOS Scripts
+# Copyright (c) 2019-2023 Christian Hesse <mail@eworm.de>
+# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md
+
+:global IfThenElse;
+:global RequiredRouterOS;
+
+# News, changes and migration up to change 95 are in global-config.changes!
+
+# Changes for global-config to be added to notification on script updates
+:global GlobalConfigChanges {
+};
+
+# Migration steps to be applied on script updates
+:global GlobalConfigMigration {
+};
diff --git a/ospf-to-leds b/ospf-to-leds
index 12ec820..2da00ca 100644
--- a/ospf-to-leds
+++ b/ospf-to-leds
@@ -1,35 +1,3 @@
#!rsc by RouterOS
-# RouterOS script: ospf-to-leds
-# Copyright (c) 2020-2023 Christian Hesse <mail@eworm.de>
-# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md
#
-# visualize ospf instance state via leds
-# https://git.eworm.de/cgit/routeros-scripts/about/doc/ospf-to-leds.md
-
-:local 0 "ospf-to-leds";
-:global GlobalFunctionsReady;
-:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
-
-:global LogPrintExit2;
-:global ParseKeyValueStore;
-
-:foreach Instance in=[ /routing/ospf/instance/find where comment~"^ospf-to-leds," ] do={
- :local InstanceVal [ /routing/ospf/instance/get $Instance ];
- :local LED ([ $ParseKeyValueStore ($InstanceVal->"comment") ]->"leds");
- :local LEDType [ /system/leds/get [ find where leds=$LED ] type ];
-
- :local NeighborCount 0;
- :foreach Area in=[ /routing/ospf/area/find where instance=($InstanceVal->"name") ] do={
- :local AreaName [ /routing/ospf/area/get $Area name ];
- :set NeighborCount ($NeighborCount + [ :len [ /routing/ospf/neighbor/find where area=$AreaName ] ]);
- }
-
- :if ($NeighborCount > 0 && $LEDType = "off") do={
- $LogPrintExit2 info $0 ("OSPF instance " . $InstanceVal->"name" . " has " . $NeighborCount . " neighbors, led on!") false;
- /system/leds/set type=on [ find where leds=$LED ];
- }
- :if ($NeighborCount = 0 && $LEDType = "on") do={
- $LogPrintExit2 info $0 ("OSPF instance " . $InstanceVal->"name" . " has no neighbors, led off!") false;
- /system/leds/set type=off [ find where leds=$LED ];
- }
-}
+# dummy for migration
diff --git a/ospf-to-leds.rsc b/ospf-to-leds.rsc
new file mode 100644
index 0000000..12ec820
--- /dev/null
+++ b/ospf-to-leds.rsc
@@ -0,0 +1,35 @@
+#!rsc by RouterOS
+# RouterOS script: ospf-to-leds
+# Copyright (c) 2020-2023 Christian Hesse <mail@eworm.de>
+# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md
+#
+# visualize ospf instance state via leds
+# https://git.eworm.de/cgit/routeros-scripts/about/doc/ospf-to-leds.md
+
+:local 0 "ospf-to-leds";
+:global GlobalFunctionsReady;
+:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
+
+:global LogPrintExit2;
+:global ParseKeyValueStore;
+
+:foreach Instance in=[ /routing/ospf/instance/find where comment~"^ospf-to-leds," ] do={
+ :local InstanceVal [ /routing/ospf/instance/get $Instance ];
+ :local LED ([ $ParseKeyValueStore ($InstanceVal->"comment") ]->"leds");
+ :local LEDType [ /system/leds/get [ find where leds=$LED ] type ];
+
+ :local NeighborCount 0;
+ :foreach Area in=[ /routing/ospf/area/find where instance=($InstanceVal->"name") ] do={
+ :local AreaName [ /routing/ospf/area/get $Area name ];
+ :set NeighborCount ($NeighborCount + [ :len [ /routing/ospf/neighbor/find where area=$AreaName ] ]);
+ }
+
+ :if ($NeighborCount > 0 && $LEDType = "off") do={
+ $LogPrintExit2 info $0 ("OSPF instance " . $InstanceVal->"name" . " has " . $NeighborCount . " neighbors, led on!") false;
+ /system/leds/set type=on [ find where leds=$LED ];
+ }
+ :if ($NeighborCount = 0 && $LEDType = "on") do={
+ $LogPrintExit2 info $0 ("OSPF instance " . $InstanceVal->"name" . " has no neighbors, led off!") false;
+ /system/leds/set type=off [ find where leds=$LED ];
+ }
+}
diff --git a/packages-update b/packages-update
index 5162103..2da00ca 100644
--- a/packages-update
+++ b/packages-update
@@ -1,98 +1,3 @@
#!rsc by RouterOS
-# RouterOS script: packages-update
-# Copyright (c) 2019-2023 Christian Hesse <mail@eworm.de>
-# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md
#
-# download packages and reboot for installation
-# https://git.eworm.de/cgit/routeros-scripts/about/doc/packages-update.md
-
-:local 0 "packages-update";
-:global GlobalFunctionsReady;
-:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
-
-:global DownloadPackage;
-:global LogPrintExit2;
-:global ScriptFromTerminal;
-:global ScriptLock;
-:global VersionToNum;
-
-$ScriptLock $0;
-
-:local Update [ /system/package/update/get ];
-
-:if ([ :typeof ($Update->"latest-version") ] = "nothing") do={
- $LogPrintExit2 warning $0 ("Latest version is not known.") true;
-}
-
-:if ($Update->"installed-version" = $Update->"latest-version") do={
- $LogPrintExit2 info $0 ("Version " . $Update->"latest-version" . " is already installed.") true;
-}
-
-:local NumInstalled [ $VersionToNum ($Update->"installed-version") ];
-:local NumLatest [ $VersionToNum ($Update->"latest-version") ];
-
-:local DoDowngrade false;
-:if ($NumInstalled > $NumLatest) do={
- :if ([ $ScriptFromTerminal $0 ] = true) do={
- :put "Latest version is older than installed one. Want to downgrade? [y/N]";
- :if (([ /terminal/inkey timeout=60 ] % 32) = 25) do={
- :set DoDowngrade true;
- } else={
- :put "Canceled...";
- }
- } else={
- $LogPrintExit2 warning $0 ("Not installing downgrade automatically.") true;
- }
-}
-
-:foreach Package in=[ /system/package/find where !bundle ] do={
- :local PkgName [ /system/package/get $Package name ];
- :if ([ $DownloadPackage $PkgName ($Update->"latest-version") ] = false) do={
- $LogPrintExit2 error $0 ("Download for package " . $PkgName . " failed, update aborted.") true;
- }
-}
-
-:foreach Script in=[ /system/script/find where source~"\n# provides: backup-script\n" ] do={
- :local ScriptName [ /system/script/get $Script name ];
- :do {
- $LogPrintExit2 info $0 ("Running backup script " . $ScriptName . " before update.") false;
- /system/script/run $Script;
- } on-error={
- $LogPrintExit2 warning $0 ("Running backup script " . $ScriptName . " before update failed!") false;
- :if ([ $ScriptFromTerminal $0 ] = true) do={
- :put "Do you want to continue anyway? [y/N]";
- :if (([ /terminal/inkey timeout=60 ] % 32) = 25) do={
- $LogPrintExit2 info $0 ("User requested to continue anyway.") false;
- } else={
- $LogPrintExit2 info $0 ("Canceled update...") true;
- }
- } else={
- $LogPrintExit2 info $0 ("Canceled non-interactive update.") true;
- }
- }
-}
-
-:if ($DoDowngrade = true) do={
- $LogPrintExit2 info $0 ("Rebooting for downgrade.") false;
- :delay 1s;
- /system/package/downgrade;
-}
-
-:if ([ $ScriptFromTerminal $0 ] = true) do={
- :put "Do you want to (s)chedule reboot or (r)eboot now? [s/R]";
- :if (([ /terminal/inkey timeout=60 ] % 32) = 19) do={
- :global RebootForUpdate do={
- :global RandomDelay;
- $RandomDelay 3600;
- /system/reboot;
- }
- /system/scheduler/add name="reboot-for-update" start-time=03:00:00 interval=1d \
- on-event=("/system/scheduler/remove reboot-for-update; " . \
- ":global RebootForUpdate; \$RebootForUpdate;");
- $LogPrintExit2 info $0 ("Scheduled reboot for update between 03:00 and 04:00.") true;
- }
-}
-
-$LogPrintExit2 info $0 ("Rebooting for update.") false;
-:delay 1s;
-/system/reboot;
+# dummy for migration
diff --git a/packages-update.rsc b/packages-update.rsc
new file mode 100644
index 0000000..5162103
--- /dev/null
+++ b/packages-update.rsc
@@ -0,0 +1,98 @@
+#!rsc by RouterOS
+# RouterOS script: packages-update
+# Copyright (c) 2019-2023 Christian Hesse <mail@eworm.de>
+# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md
+#
+# download packages and reboot for installation
+# https://git.eworm.de/cgit/routeros-scripts/about/doc/packages-update.md
+
+:local 0 "packages-update";
+:global GlobalFunctionsReady;
+:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
+
+:global DownloadPackage;
+:global LogPrintExit2;
+:global ScriptFromTerminal;
+:global ScriptLock;
+:global VersionToNum;
+
+$ScriptLock $0;
+
+:local Update [ /system/package/update/get ];
+
+:if ([ :typeof ($Update->"latest-version") ] = "nothing") do={
+ $LogPrintExit2 warning $0 ("Latest version is not known.") true;
+}
+
+:if ($Update->"installed-version" = $Update->"latest-version") do={
+ $LogPrintExit2 info $0 ("Version " . $Update->"latest-version" . " is already installed.") true;
+}
+
+:local NumInstalled [ $VersionToNum ($Update->"installed-version") ];
+:local NumLatest [ $VersionToNum ($Update->"latest-version") ];
+
+:local DoDowngrade false;
+:if ($NumInstalled > $NumLatest) do={
+ :if ([ $ScriptFromTerminal $0 ] = true) do={
+ :put "Latest version is older than installed one. Want to downgrade? [y/N]";
+ :if (([ /terminal/inkey timeout=60 ] % 32) = 25) do={
+ :set DoDowngrade true;
+ } else={
+ :put "Canceled...";
+ }
+ } else={
+ $LogPrintExit2 warning $0 ("Not installing downgrade automatically.") true;
+ }
+}
+
+:foreach Package in=[ /system/package/find where !bundle ] do={
+ :local PkgName [ /system/package/get $Package name ];
+ :if ([ $DownloadPackage $PkgName ($Update->"latest-version") ] = false) do={
+ $LogPrintExit2 error $0 ("Download for package " . $PkgName . " failed, update aborted.") true;
+ }
+}
+
+:foreach Script in=[ /system/script/find where source~"\n# provides: backup-script\n" ] do={
+ :local ScriptName [ /system/script/get $Script name ];
+ :do {
+ $LogPrintExit2 info $0 ("Running backup script " . $ScriptName . " before update.") false;
+ /system/script/run $Script;
+ } on-error={
+ $LogPrintExit2 warning $0 ("Running backup script " . $ScriptName . " before update failed!") false;
+ :if ([ $ScriptFromTerminal $0 ] = true) do={
+ :put "Do you want to continue anyway? [y/N]";
+ :if (([ /terminal/inkey timeout=60 ] % 32) = 25) do={
+ $LogPrintExit2 info $0 ("User requested to continue anyway.") false;
+ } else={
+ $LogPrintExit2 info $0 ("Canceled update...") true;
+ }
+ } else={
+ $LogPrintExit2 info $0 ("Canceled non-interactive update.") true;
+ }
+ }
+}
+
+:if ($DoDowngrade = true) do={
+ $LogPrintExit2 info $0 ("Rebooting for downgrade.") false;
+ :delay 1s;
+ /system/package/downgrade;
+}
+
+:if ([ $ScriptFromTerminal $0 ] = true) do={
+ :put "Do you want to (s)chedule reboot or (r)eboot now? [s/R]";
+ :if (([ /terminal/inkey timeout=60 ] % 32) = 19) do={
+ :global RebootForUpdate do={
+ :global RandomDelay;
+ $RandomDelay 3600;
+ /system/reboot;
+ }
+ /system/scheduler/add name="reboot-for-update" start-time=03:00:00 interval=1d \
+ on-event=("/system/scheduler/remove reboot-for-update; " . \
+ ":global RebootForUpdate; \$RebootForUpdate;");
+ $LogPrintExit2 info $0 ("Scheduled reboot for update between 03:00 and 04:00.") true;
+ }
+}
+
+$LogPrintExit2 info $0 ("Rebooting for update.") false;
+:delay 1s;
+/system/reboot;
diff --git a/ppp-on-up b/ppp-on-up
index ac01c97..2da00ca 100644
--- a/ppp-on-up
+++ b/ppp-on-up
@@ -1,34 +1,3 @@
#!rsc by RouterOS
-# RouterOS script: ppp-on-up
-# Copyright (c) 2013-2023 Christian Hesse <mail@eworm.de>
-# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md
#
-# run scripts on ppp up
-# https://git.eworm.de/cgit/routeros-scripts/about/doc/ppp-on-up.md
-
-:local 0 "ppp-on-up";
-:global GlobalFunctionsReady;
-:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
-
-:global LogPrintExit2;
-
-:local Interface $interface;
-
-:if ([ :typeof $Interface ] = "nothing") do={
- $LogPrintExit2 error $0 ("This script is supposed to run from ppp on-up script hook.") true;
-}
-
-:local IntName [ /interface/get $Interface name ];
-$LogPrintExit2 info $0 ("PPP interface " . $IntName . " is up.") false;
-
-/ipv6/dhcp-client/release [ find where interface=$IntName !disabled ];
-
-:foreach Script in=[ /system/script/find where source~("\n# provides: ppp-on-up\n") ] do={
- :local ScriptName [ /system/script/get $Script name ];
- :do {
- $LogPrintExit2 debug $0 ("Running script: " . $ScriptName) false;
- /system/script/run $Script;
- } on-error={
- $LogPrintExit2 warning $0 ("Running script '" . $ScriptName . "' failed!") false;
- }
-}
+# dummy for migration
diff --git a/ppp-on-up.rsc b/ppp-on-up.rsc
new file mode 100644
index 0000000..ac01c97
--- /dev/null
+++ b/ppp-on-up.rsc
@@ -0,0 +1,34 @@
+#!rsc by RouterOS
+# RouterOS script: ppp-on-up
+# Copyright (c) 2013-2023 Christian Hesse <mail@eworm.de>
+# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md
+#
+# run scripts on ppp up
+# https://git.eworm.de/cgit/routeros-scripts/about/doc/ppp-on-up.md
+
+:local 0 "ppp-on-up";
+:global GlobalFunctionsReady;
+:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
+
+:global LogPrintExit2;
+
+:local Interface $interface;
+
+:if ([ :typeof $Interface ] = "nothing") do={
+ $LogPrintExit2 error $0 ("This script is supposed to run from ppp on-up script hook.") true;
+}
+
+:local IntName [ /interface/get $Interface name ];
+$LogPrintExit2 info $0 ("PPP interface " . $IntName . " is up.") false;
+
+/ipv6/dhcp-client/release [ find where interface=$IntName !disabled ];
+
+:foreach Script in=[ /system/script/find where source~("\n# provides: ppp-on-up\n") ] do={
+ :local ScriptName [ /system/script/get $Script name ];
+ :do {
+ $LogPrintExit2 debug $0 ("Running script: " . $ScriptName) false;
+ /system/script/run $Script;
+ } on-error={
+ $LogPrintExit2 warning $0 ("Running script '" . $ScriptName . "' failed!") false;
+ }
+}
diff --git a/sms-action b/sms-action
index f5de11f..2da00ca 100644
--- a/sms-action
+++ b/sms-action
@@ -1,31 +1,3 @@
#!rsc by RouterOS
-# RouterOS script: sms-action
-# Copyright (c) 2018-2023 Christian Hesse <mail@eworm.de>
-# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md
#
-# run action on received SMS
-# https://git.eworm.de/cgit/routeros-scripts/about/doc/sms-action.md
-
-:local 0 "sms-action";
-:global GlobalFunctionsReady;
-:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
-
-:global SmsAction;
-
-:global LogPrintExit2;
-:global ValidateSyntax;
-
-:local Action $action;
-
-:if ([ :typeof $Action ] = "nothing") do={
- $LogPrintExit2 error $0 ("This script is supposed to run from SMS hook with action=...") true;
-}
-
-:local Code ($SmsAction->$Action);
-:if ([ $ValidateSyntax $Code ] = true) do={
- :log info ("Acting on SMS action '" . $Action . "': " . $Code);
- :delay 1s;
- [ :parse $Code ];
-} else={
- $LogPrintExit2 warning $0 ("The code for action '" . $Action . "' failed syntax validation!") false;
-}
+# dummy for migration
diff --git a/sms-action.rsc b/sms-action.rsc
new file mode 100644
index 0000000..f5de11f
--- /dev/null
+++ b/sms-action.rsc
@@ -0,0 +1,31 @@
+#!rsc by RouterOS
+# RouterOS script: sms-action
+# Copyright (c) 2018-2023 Christian Hesse <mail@eworm.de>
+# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md
+#
+# run action on received SMS
+# https://git.eworm.de/cgit/routeros-scripts/about/doc/sms-action.md
+
+:local 0 "sms-action";
+:global GlobalFunctionsReady;
+:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
+
+:global SmsAction;
+
+:global LogPrintExit2;
+:global ValidateSyntax;
+
+:local Action $action;
+
+:if ([ :typeof $Action ] = "nothing") do={
+ $LogPrintExit2 error $0 ("This script is supposed to run from SMS hook with action=...") true;
+}
+
+:local Code ($SmsAction->$Action);
+:if ([ $ValidateSyntax $Code ] = true) do={
+ :log info ("Acting on SMS action '" . $Action . "': " . $Code);
+ :delay 1s;
+ [ :parse $Code ];
+} else={
+ $LogPrintExit2 warning $0 ("The code for action '" . $Action . "' failed syntax validation!") false;
+}
diff --git a/sms-forward b/sms-forward
index 802da48..2da00ca 100644
--- a/sms-forward
+++ b/sms-forward
@@ -1,84 +1,3 @@
#!rsc by RouterOS
-# RouterOS script: sms-forward
-# Copyright (c) 2013-2023 Christian Hesse <mail@eworm.de>
-# Anatoly Bubenkov <bubenkoff@gmail.com>
-# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md
#
-# forward SMS to e-mail
-# https://git.eworm.de/cgit/routeros-scripts/about/doc/sms-forward.md
-
-:local 0 "sms-forward";
-:global GlobalFunctionsReady;
-:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
-
-:global Identity;
-:global SmsForwardHooks;
-
-:global IfThenElse;
-:global LogPrintExit2;
-:global ScriptLock;
-:global SendNotification2;
-:global SymbolForNotification;
-:global ValidateSyntax;
-:global WaitFullyConnected;
-
-$ScriptLock $0;
-
-:if ([ /tool/sms/get receive-enabled ] = false) do={
- $LogPrintExit2 warning $0 ("Receiving of SMS is not enabled.") true;
-}
-
-$WaitFullyConnected;
-
-:local Settings [ /tool/sms/get ];
-
-# forward SMS in a loop
-:while ([ :len [ /tool/sms/inbox/find ] ] > 0) do={
- :local Phone [ /tool/sms/inbox/get ([ find ]->0) phone ];
- :local Messages "";
- :local Delete ({});
-
- :foreach Sms in=[ /tool/sms/inbox/find where phone=$Phone ] do={
- :local SmsVal [ /tool/sms/inbox/get $Sms ];
-
- :if ($Phone = $Settings->"allowed-number" && \
- ($SmsVal->"message")~("^:cmd " . $Settings->"secret" . " script ")) do={
- $LogPrintExit2 debug $0 ("Removing SMS, which started a script.") false;
- /tool/sms/inbox/remove $Sms;
- } else={
- :set Messages ($Messages . "\n\nOn " . $SmsVal->"timestamp" . \
- " type " . $SmsVal->"type" . ":\n" . $SmsVal->"message");
- :foreach Hook in=$SmsForwardHooks do={
- :if ($Phone~($Hook->"allowed-number") && ($SmsVal->"message")~($Hook->"match")) do={
- :if ([ $ValidateSyntax ($Hook->"command") ] = true) do={
- $LogPrintExit2 info $0 ("Running hook '" . $Hook->"match" . "': " . \
- $Hook->"command") false;
- :do {
- [ :parse ($Hook->"command") ];
- :set Messages ($Messages . "\n\nRan hook '" . $Hook->"match" . "':\n" . \
- $Hook->"command");
- } on-error={
- $LogPrintExit2 warning $0 ("The code for hook '" . $Hook->"match" . \
- "' failed to run!") false;
- }
- } else={
- $LogPrintExit2 warning $0 ("The code for hook '" . $Hook->"match" . \
- "' failed syntax validation!") false;
- }
- }
- }
- :set Delete ($Delete, $Sms);
- }
- }
-
- :if ([ :len $Messages ] > 0) do={
- :local Count [ :len $Delete ];
- $SendNotification2 ({ origin=$0; \
- subject=([ $SymbolForNotification "incoming-envelope" ] . "SMS Forwarding from " . $Phone); \
- message=("Received " . [ $IfThenElse ($Count = 1) "this message" ("these " . $Count . " messages") ] . \
- " by " . $Identity . " from " . $Phone . ":" . $Messages) });
- :foreach Sms in=$Delete do={
- /tool/sms/inbox/remove $Sms;
- }
- }
-}
+# dummy for migration
diff --git a/sms-forward.rsc b/sms-forward.rsc
new file mode 100644
index 0000000..802da48
--- /dev/null
+++ b/sms-forward.rsc
@@ -0,0 +1,84 @@
+#!rsc by RouterOS
+# RouterOS script: sms-forward
+# Copyright (c) 2013-2023 Christian Hesse <mail@eworm.de>
+# Anatoly Bubenkov <bubenkoff@gmail.com>
+# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md
+#
+# forward SMS to e-mail
+# https://git.eworm.de/cgit/routeros-scripts/about/doc/sms-forward.md
+
+:local 0 "sms-forward";
+:global GlobalFunctionsReady;
+:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
+
+:global Identity;
+:global SmsForwardHooks;
+
+:global IfThenElse;
+:global LogPrintExit2;
+:global ScriptLock;
+:global SendNotification2;
+:global SymbolForNotification;
+:global ValidateSyntax;
+:global WaitFullyConnected;
+
+$ScriptLock $0;
+
+:if ([ /tool/sms/get receive-enabled ] = false) do={
+ $LogPrintExit2 warning $0 ("Receiving of SMS is not enabled.") true;
+}
+
+$WaitFullyConnected;
+
+:local Settings [ /tool/sms/get ];
+
+# forward SMS in a loop
+:while ([ :len [ /tool/sms/inbox/find ] ] > 0) do={
+ :local Phone [ /tool/sms/inbox/get ([ find ]->0) phone ];
+ :local Messages "";
+ :local Delete ({});
+
+ :foreach Sms in=[ /tool/sms/inbox/find where phone=$Phone ] do={
+ :local SmsVal [ /tool/sms/inbox/get $Sms ];
+
+ :if ($Phone = $Settings->"allowed-number" && \
+ ($SmsVal->"message")~("^:cmd " . $Settings->"secret" . " script ")) do={
+ $LogPrintExit2 debug $0 ("Removing SMS, which started a script.") false;
+ /tool/sms/inbox/remove $Sms;
+ } else={
+ :set Messages ($Messages . "\n\nOn " . $SmsVal->"timestamp" . \
+ " type " . $SmsVal->"type" . ":\n" . $SmsVal->"message");
+ :foreach Hook in=$SmsForwardHooks do={
+ :if ($Phone~($Hook->"allowed-number") && ($SmsVal->"message")~($Hook->"match")) do={
+ :if ([ $ValidateSyntax ($Hook->"command") ] = true) do={
+ $LogPrintExit2 info $0 ("Running hook '" . $Hook->"match" . "': " . \
+ $Hook->"command") false;
+ :do {
+ [ :parse ($Hook->"command") ];
+ :set Messages ($Messages . "\n\nRan hook '" . $Hook->"match" . "':\n" . \
+ $Hook->"command");
+ } on-error={
+ $LogPrintExit2 warning $0 ("The code for hook '" . $Hook->"match" . \
+ "' failed to run!") false;
+ }
+ } else={
+ $LogPrintExit2 warning $0 ("The code for hook '" . $Hook->"match" . \
+ "' failed syntax validation!") false;
+ }
+ }
+ }
+ :set Delete ($Delete, $Sms);
+ }
+ }
+
+ :if ([ :len $Messages ] > 0) do={
+ :local Count [ :len $Delete ];
+ $SendNotification2 ({ origin=$0; \
+ subject=([ $SymbolForNotification "incoming-envelope" ] . "SMS Forwarding from " . $Phone); \
+ message=("Received " . [ $IfThenElse ($Count = 1) "this message" ("these " . $Count . " messages") ] . \
+ " by " . $Identity . " from " . $Phone . ":" . $Messages) });
+ :foreach Sms in=$Delete do={
+ /tool/sms/inbox/remove $Sms;
+ }
+ }
+}
diff --git a/ssh-keys-import b/ssh-keys-import
index b40a997..2da00ca 100644
--- a/ssh-keys-import
+++ b/ssh-keys-import
@@ -1,11 +1,3 @@
#!rsc by RouterOS
-# RouterOS script: ssh-keys-import
-# Copyright (c) 2013-2023 Christian Hesse <mail@eworm.de>
-# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md
#
-# import ssh keys from file
-# https://git.eworm.de/cgit/routeros-scripts/about/doc/ssh-keys-import.md
-
-:foreach Key in=[ /file/find where type="ssh key" ] do={
- /user/ssh-key/import user=admin public-key-file=[ /file/get $Key name ];
-}
+# dummy for migration
diff --git a/ssh-keys-import.rsc b/ssh-keys-import.rsc
new file mode 100644
index 0000000..b40a997
--- /dev/null
+++ b/ssh-keys-import.rsc
@@ -0,0 +1,11 @@
+#!rsc by RouterOS
+# RouterOS script: ssh-keys-import
+# Copyright (c) 2013-2023 Christian Hesse <mail@eworm.de>
+# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md
+#
+# import ssh keys from file
+# https://git.eworm.de/cgit/routeros-scripts/about/doc/ssh-keys-import.md
+
+:foreach Key in=[ /file/find where type="ssh key" ] do={
+ /user/ssh-key/import user=admin public-key-file=[ /file/get $Key name ];
+}
diff --git a/super-mario-theme b/super-mario-theme
index 7787a12..2da00ca 100644
--- a/super-mario-theme
+++ b/super-mario-theme
@@ -1,69 +1,3 @@
#!rsc by RouterOS
-# RouterOS script: super-mario-theme
-# Copyright (c) 2013-2023 Christian Hesse <mail@eworm.de>
-# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md
#
-# play Super Mario theme
-# https://git.eworm.de/cgit/routeros-scripts/about/doc/super-mario-theme.md
-
-:local Beeps {
- { 660; 100 }; 150; { 660; 100 }; 300; { 660; 100 }; 300;
- { 510; 100 }; 100; { 660; 100 }; 300; { 770; 100 }; 550;
- { 380; 100 }; 575; { 510; 100 }; 450; { 380; 100 }; 400;
- { 320; 100 }; 500; { 440; 100 }; 300; { 480; 80 }; 330;
- { 450; 100 }; 150; { 430; 100 }; 300; { 380; 100 }; 200;
- { 660; 80 }; 200; { 760; 50 }; 150; { 860; 100 }; 300;
- { 700; 80 }; 150; { 760; 50 }; 350; { 660; 80 }; 300;
- { 520; 80 }; 150; { 580; 80 }; 150; { 480; 80 }; 500;
- { 510; 100 }; 450; { 380; 100 }; 400; { 320; 100 }; 500;
- { 440; 100 }; 300; { 480; 80 }; 330; { 450; 100 }; 150;
- { 430; 100 }; 300; { 380; 100 }; 200; { 660; 80 }; 200;
- { 760; 50 }; 150; { 860; 100 }; 300; { 700; 80 }; 150;
- { 760; 50 }; 350; { 660; 80 }; 300; { 520; 80 }; 150;
- { 580; 80 }; 150; { 480; 80 }; 500; { 500; 100 }; 300;
- { 760; 100 }; 100; { 720; 100 }; 150; { 680; 100 }; 150;
- { 620; 150 }; 300; { 650; 150 }; 300; { 380; 100 }; 150;
- { 430; 100 }; 150; { 500; 100 }; 300; { 430; 100 }; 150;
- { 500; 100 }; 100; { 570; 100 }; 220; { 500; 100 }; 300;
- { 760; 100 }; 100; { 720; 100 }; 150; { 680; 100 }; 150;
- { 620; 150 }; 300; { 650; 200 }; 300; { 1020; 80 }; 300;
- { 1020; 80 }; 150; { 1020; 80 }; 300; { 380; 100 }; 300;
- { 500; 100 }; 300; { 760; 100 }; 100; { 720; 100 }; 150;
- { 680; 100 }; 150; { 620; 150 }; 300; { 650; 150 }; 300;
- { 380; 100 }; 150; { 430; 100 }; 150; { 500; 100 }; 300;
- { 430; 100 }; 150; { 500; 100 }; 100; { 570; 100 }; 420;
- { 585; 100 }; 450; { 550; 100 }; 420; { 500; 100 }; 360;
- { 380; 100 }; 300; { 500; 100 }; 300; { 500; 100 }; 150;
- { 500; 100 }; 300; { 500; 100 }; 300; { 760; 100 }; 100;
- { 720; 100 }; 150; { 680; 100 }; 150; { 620; 150 }; 300;
- { 650; 150 }; 300; { 380; 100 }; 150; { 430; 100 }; 150;
- { 500; 100 }; 300; { 430; 100 }; 150; { 500; 100 }; 100;
- { 570; 100 }; 220; { 500; 100 }; 300; { 760; 100 }; 100;
- { 720; 100 }; 150; { 680; 100 }; 150; { 620; 150 }; 300;
- { 650; 200 }; 300; { 1020; 80 }; 300; { 1020; 80 }; 150;
- { 1020; 80 }; 300; { 380; 100 }; 300; { 500; 100 }; 300;
- { 760; 100 }; 100; { 720; 100 }; 150; { 680; 100 }; 150;
- { 620; 150 }; 300; { 650; 150 }; 300; { 380; 100 }; 150;
- { 430; 100 }; 150; { 500; 100 }; 300; { 430; 100 }; 150;
- { 500; 100 }; 100; { 570; 100 }; 420; { 585; 100 }; 450;
- { 550; 100 }; 420; { 500; 100 }; 360; { 380; 100 }; 300;
- { 500; 100 }; 300; { 500; 100 }; 150; { 500; 100 }; 300;
- { 500; 60 }; 150; { 500; 80 }; 300; { 500; 60 }; 350;
- { 500; 80 }; 150; { 580; 80 }; 350; { 660; 80 }; 150;
- { 500; 80 }; 300; { 430; 80 }; 150; { 380; 80 }; 600;
- { 500; 60 }; 150; { 500; 80 }; 300; { 500; 60 }; 350;
- { 500; 80 }; 150; { 580; 80 }; 150; { 660; 80 }; 550;
- { 870; 80 }; 325; { 760; 80 }; 600; { 500; 60 }; 150;
- { 500; 80 }; 300; { 500; 60 }; 350; { 500; 80 }; 150;
- { 580; 80 }; 350; { 660; 80 }; 150; { 500; 80 }; 300;
- { 430; 80 }; 150; { 380; 80 }; 600; { 660; 100 }; 150;
- { 660; 100 }; 300; { 660; 100 }; 300; { 510; 100 }; 100;
- { 660; 100 }; 300; { 770; 100 }; 550; { 380; 100 }; 575 };
-
-:foreach Beep in=$Beeps do={
- :if ([ :len $Beep ] = 2) do={
- :beep frequency=($Beep->0) length=(($Beep->1) . "ms");
- } else={
- :delay ($Beep . "ms");
- }
-}
+# dummy for migration
diff --git a/super-mario-theme.rsc b/super-mario-theme.rsc
new file mode 100644
index 0000000..7787a12
--- /dev/null
+++ b/super-mario-theme.rsc
@@ -0,0 +1,69 @@
+#!rsc by RouterOS
+# RouterOS script: super-mario-theme
+# Copyright (c) 2013-2023 Christian Hesse <mail@eworm.de>
+# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md
+#
+# play Super Mario theme
+# https://git.eworm.de/cgit/routeros-scripts/about/doc/super-mario-theme.md
+
+:local Beeps {
+ { 660; 100 }; 150; { 660; 100 }; 300; { 660; 100 }; 300;
+ { 510; 100 }; 100; { 660; 100 }; 300; { 770; 100 }; 550;
+ { 380; 100 }; 575; { 510; 100 }; 450; { 380; 100 }; 400;
+ { 320; 100 }; 500; { 440; 100 }; 300; { 480; 80 }; 330;
+ { 450; 100 }; 150; { 430; 100 }; 300; { 380; 100 }; 200;
+ { 660; 80 }; 200; { 760; 50 }; 150; { 860; 100 }; 300;
+ { 700; 80 }; 150; { 760; 50 }; 350; { 660; 80 }; 300;
+ { 520; 80 }; 150; { 580; 80 }; 150; { 480; 80 }; 500;
+ { 510; 100 }; 450; { 380; 100 }; 400; { 320; 100 }; 500;
+ { 440; 100 }; 300; { 480; 80 }; 330; { 450; 100 }; 150;
+ { 430; 100 }; 300; { 380; 100 }; 200; { 660; 80 }; 200;
+ { 760; 50 }; 150; { 860; 100 }; 300; { 700; 80 }; 150;
+ { 760; 50 }; 350; { 660; 80 }; 300; { 520; 80 }; 150;
+ { 580; 80 }; 150; { 480; 80 }; 500; { 500; 100 }; 300;
+ { 760; 100 }; 100; { 720; 100 }; 150; { 680; 100 }; 150;
+ { 620; 150 }; 300; { 650; 150 }; 300; { 380; 100 }; 150;
+ { 430; 100 }; 150; { 500; 100 }; 300; { 430; 100 }; 150;
+ { 500; 100 }; 100; { 570; 100 }; 220; { 500; 100 }; 300;
+ { 760; 100 }; 100; { 720; 100 }; 150; { 680; 100 }; 150;
+ { 620; 150 }; 300; { 650; 200 }; 300; { 1020; 80 }; 300;
+ { 1020; 80 }; 150; { 1020; 80 }; 300; { 380; 100 }; 300;
+ { 500; 100 }; 300; { 760; 100 }; 100; { 720; 100 }; 150;
+ { 680; 100 }; 150; { 620; 150 }; 300; { 650; 150 }; 300;
+ { 380; 100 }; 150; { 430; 100 }; 150; { 500; 100 }; 300;
+ { 430; 100 }; 150; { 500; 100 }; 100; { 570; 100 }; 420;
+ { 585; 100 }; 450; { 550; 100 }; 420; { 500; 100 }; 360;
+ { 380; 100 }; 300; { 500; 100 }; 300; { 500; 100 }; 150;
+ { 500; 100 }; 300; { 500; 100 }; 300; { 760; 100 }; 100;
+ { 720; 100 }; 150; { 680; 100 }; 150; { 620; 150 }; 300;
+ { 650; 150 }; 300; { 380; 100 }; 150; { 430; 100 }; 150;
+ { 500; 100 }; 300; { 430; 100 }; 150; { 500; 100 }; 100;
+ { 570; 100 }; 220; { 500; 100 }; 300; { 760; 100 }; 100;
+ { 720; 100 }; 150; { 680; 100 }; 150; { 620; 150 }; 300;
+ { 650; 200 }; 300; { 1020; 80 }; 300; { 1020; 80 }; 150;
+ { 1020; 80 }; 300; { 380; 100 }; 300; { 500; 100 }; 300;
+ { 760; 100 }; 100; { 720; 100 }; 150; { 680; 100 }; 150;
+ { 620; 150 }; 300; { 650; 150 }; 300; { 380; 100 }; 150;
+ { 430; 100 }; 150; { 500; 100 }; 300; { 430; 100 }; 150;
+ { 500; 100 }; 100; { 570; 100 }; 420; { 585; 100 }; 450;
+ { 550; 100 }; 420; { 500; 100 }; 360; { 380; 100 }; 300;
+ { 500; 100 }; 300; { 500; 100 }; 150; { 500; 100 }; 300;
+ { 500; 60 }; 150; { 500; 80 }; 300; { 500; 60 }; 350;
+ { 500; 80 }; 150; { 580; 80 }; 350; { 660; 80 }; 150;
+ { 500; 80 }; 300; { 430; 80 }; 150; { 380; 80 }; 600;
+ { 500; 60 }; 150; { 500; 80 }; 300; { 500; 60 }; 350;
+ { 500; 80 }; 150; { 580; 80 }; 150; { 660; 80 }; 550;
+ { 870; 80 }; 325; { 760; 80 }; 600; { 500; 60 }; 150;
+ { 500; 80 }; 300; { 500; 60 }; 350; { 500; 80 }; 150;
+ { 580; 80 }; 350; { 660; 80 }; 150; { 500; 80 }; 300;
+ { 430; 80 }; 150; { 380; 80 }; 600; { 660; 100 }; 150;
+ { 660; 100 }; 300; { 660; 100 }; 300; { 510; 100 }; 100;
+ { 660; 100 }; 300; { 770; 100 }; 550; { 380; 100 }; 575 };
+
+:foreach Beep in=$Beeps do={
+ :if ([ :len $Beep ] = 2) do={
+ :beep frequency=($Beep->0) length=(($Beep->1) . "ms");
+ } else={
+ :delay ($Beep . "ms");
+ }
+}
diff --git a/telegram-chat b/telegram-chat
index ba2a3ff..2da00ca 100644
--- a/telegram-chat
+++ b/telegram-chat
@@ -1,150 +1,3 @@
#!rsc by RouterOS
-# RouterOS script: telegram-chat
-# Copyright (c) 2023 Christian Hesse <mail@eworm.de>
-# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md
#
-# use Telegram to chat with your Router and send commands
-# https://git.eworm.de/cgit/routeros-scripts/about/doc/telegram-chat.md
-
-:local 0 "telegram-chat";
-:global GlobalFunctionsReady;
-:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
-
-:global Identity;
-:global TelegramChatActive;
-:global TelegramChatGroups;
-:global TelegramChatId;
-:global TelegramChatIdsTrusted;
-:global TelegramChatOffset;
-:global TelegramChatRunTime;
-:global TelegramTokenId;
-
-:global CertificateAvailable;
-:global EitherOr;
-:global EscapeForRegEx;
-:global GetRandom20CharAlNum;
-:global IfThenElse;
-:global LogPrintExit2;
-:global MkDir;
-:global ScriptLock;
-:global SendTelegram2;
-:global SymbolForNotification;
-:global ValidateSyntax;
-:global WaitForFile;
-:global WaitFullyConnected;
-
-$ScriptLock $0;
-
-$WaitFullyConnected;
-
-:if ([ :typeof $TelegramChatOffset ] != "array") do={
- :set TelegramChatOffset { 0; 0; 0 };
-}
-
-:if ([ $CertificateAvailable "Go Daddy Secure Certificate Authority - G2" ] = false) do={
- $LogPrintExit2 warning $0 ("Downloading required certificate failed.") true;
-}
-
-:local JsonGetKey do={
- :local Array [ :toarray $1 ];
- :local Key [ :tostr $2 ];
-
- :for I from=0 to=([ :len $Array ] - 1) do={
- :if (($Array->$I) = $Key) do={
- :if ($Array->($I + 1) = ":") do={
- :return ($Array->($I + 2));
- }
- :return [ :pick ($Array->($I + 1)) 1 [ :len ($Array->($I + 1)) ] ];
- }
- }
-
- :return false;
-}
-
-:local Data;
-:do {
- :set Data ([ /tool/fetch check-certificate=yes-without-crl output=user \
- ("https://api.telegram.org/bot" . $TelegramTokenId . "/getUpdates?offset=" . \
- $TelegramChatOffset->0 . "&allowed_updates=%5B%22message%22%5D") as-value ]->"data");
- :set Data [ :pick $Data ([ :find $Data "[" ] + 1) ([ :len $Data ] - 2) ];
-} on-error={
- $LogPrintExit2 debug $0 ("Failed getting updates from Telegram.") true;
-}
-
-:local UpdateID 0;
-:local Uptime [ /system/resource/get uptime ];
-:foreach Update in=[ :toarray $Data ] do={
- :set UpdateID [ $JsonGetKey $Update "update_id" ];
- :if (($TelegramChatOffset->0 > 0 || $Uptime > 5m) && $UpdateID >= $TelegramChatOffset->2) do={
- :local Trusted false;
- :local Message [ $JsonGetKey $Update "message" ];
- :local MessageId [ $JsonGetKey $Message "message_id" ];
- :local From [ $JsonGetKey $Message "from" ];
- :local FromID [ $JsonGetKey $From "id" ];
- :local FromUserName [ $JsonGetKey $From "username" ];
- :local ChatID [ $JsonGetKey [ $JsonGetKey $Message "chat" ] "id" ];
- :local Text [ $JsonGetKey $Message "text" ];
- :foreach IdsTrusted in=($TelegramChatId, $TelegramChatIdsTrusted) do={
- :if ($FromID = $IdsTrusted || $FromUserName = $IdsTrusted) do={
- :set Trusted true;
- }
- }
-
- :if ($Trusted = true) do={
- :if ([ :pick $Text 0 1 ] = "!") do={
- :if ($Text ~ ("^! *(" . [ $EscapeForRegEx $Identity ] . "|@" . $TelegramChatGroups . ")\$")) do={
- :set TelegramChatActive true;
- } else={
- :set TelegramChatActive false;
- }
- $LogPrintExit2 info $0 ("Now " . [ $IfThenElse $TelegramChatActive "active" "passive" ] . \
- " from update " . $UpdateID . "!") false;
- } else={
- :if ($TelegramChatActive = true && $Text != false && [ :len $Text ] > 0) do={
- :if ([ $ValidateSyntax $Text ] = true) do={
- :local State "";
- :local File ("tmpfs/telegram-chat/" . [ $GetRandom20CharAlNum 6 ]);
- $MkDir "tmpfs/telegram-chat";
- $LogPrintExit2 info $0 ("Running command from update " . $UpdateID . ": " . $Text) false;
- :exec script=(":do {\n" . $Text . "\n} on-error={ :execute script=\"/\" file=" . $File . ".failed };" . \
- ":execute script=\"/\" file=" . $File . ".done") file=$File;
- :if ([ $WaitForFile ($File . ".done.txt") [ $EitherOr $TelegramChatRunTime 20s ] ] = false) do={
- :set State "The command did not finish, still running in background.\n\n";
- }
- :if ([ :len [ /file/find where name=($File . ".failed.txt") ] ] > 0) do={
- :set State "The command failed with an error!\n\n";
- }
- :local Content [ /file/get ($File . ".txt") contents ];
- $SendTelegram2 ({ origin=$0; chatid=$ChatID; silent=false; replyto=$MessageId; \
- subject=([ $SymbolForNotification "speech-balloon" ] . "Telegram Chat"); \
- message=("Command:\n" . $Text . "\n\n" . $State . [ $IfThenElse ([ :len $Content ] > 0) \
- ("Output:\n" . $Content) [ $IfThenElse ([ /file/get ($File . ".txt") size ] > 0) \
- ("Output exceeds file read size.") ("No output.") ] ]) });
- /file/remove "tmpfs/telegram-chat";
- } else={
- $LogPrintExit2 info $0 ("The command from update " . $UpdateID . " failed syntax validation!") false;
- $SendTelegram2 ({ origin=$0; chatid=$ChatID; silent=false; replyto=$MessageId; \
- subject=([ $SymbolForNotification "speech-balloon" ] . "Telegram Chat"); \
- message=("Command:\n" . $Text . "\n\nThe command failed syntax validation!") });
- }
- }
- }
- } else={
- :local Message ("Received a message from untrusted contact " . \
- [ $IfThenElse ($FromUserName = false) "without username" ("'" . $FromUserName . "'") ] . \
- " (ID " . $FromID . ") in update " . $UpdateID . "!");
- :if ($Text ~ ("^! *" . [ $EscapeForRegEx $Identity ] . "\$")) do={
- $LogPrintExit2 warning $0 $Message false;
- $SendTelegram2 ({ origin=$0; chatid=$ChatID; silent=false; replyto=$MessageId; \
- subject=([ $SymbolForNotification "speech-balloon" ] . "Telegram Chat"); \
- message=("You are not trusted.") });
- } else={
- $LogPrintExit2 info $0 $Message false;
- }
- }
- } else={
- $LogPrintExit2 debug $0 ("Already handled update " . $UpdateID . ".") false;
- }
-}
-:set TelegramChatOffset ([ :pick $TelegramChatOffset 1 3 ], \
- [ $IfThenElse ($UpdateID >= $TelegramChatOffset->2) ($UpdateID + 1) ($TelegramChatOffset->2) ]);
+# dummy for migration
diff --git a/telegram-chat.rsc b/telegram-chat.rsc
new file mode 100644
index 0000000..ba2a3ff
--- /dev/null
+++ b/telegram-chat.rsc
@@ -0,0 +1,150 @@
+#!rsc by RouterOS
+# RouterOS script: telegram-chat
+# Copyright (c) 2023 Christian Hesse <mail@eworm.de>
+# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md
+#
+# use Telegram to chat with your Router and send commands
+# https://git.eworm.de/cgit/routeros-scripts/about/doc/telegram-chat.md
+
+:local 0 "telegram-chat";
+:global GlobalFunctionsReady;
+:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
+
+:global Identity;
+:global TelegramChatActive;
+:global TelegramChatGroups;
+:global TelegramChatId;
+:global TelegramChatIdsTrusted;
+:global TelegramChatOffset;
+:global TelegramChatRunTime;
+:global TelegramTokenId;
+
+:global CertificateAvailable;
+:global EitherOr;
+:global EscapeForRegEx;
+:global GetRandom20CharAlNum;
+:global IfThenElse;
+:global LogPrintExit2;
+:global MkDir;
+:global ScriptLock;
+:global SendTelegram2;
+:global SymbolForNotification;
+:global ValidateSyntax;
+:global WaitForFile;
+:global WaitFullyConnected;
+
+$ScriptLock $0;
+
+$WaitFullyConnected;
+
+:if ([ :typeof $TelegramChatOffset ] != "array") do={
+ :set TelegramChatOffset { 0; 0; 0 };
+}
+
+:if ([ $CertificateAvailable "Go Daddy Secure Certificate Authority - G2" ] = false) do={
+ $LogPrintExit2 warning $0 ("Downloading required certificate failed.") true;
+}
+
+:local JsonGetKey do={
+ :local Array [ :toarray $1 ];
+ :local Key [ :tostr $2 ];
+
+ :for I from=0 to=([ :len $Array ] - 1) do={
+ :if (($Array->$I) = $Key) do={
+ :if ($Array->($I + 1) = ":") do={
+ :return ($Array->($I + 2));
+ }
+ :return [ :pick ($Array->($I + 1)) 1 [ :len ($Array->($I + 1)) ] ];
+ }
+ }
+
+ :return false;
+}
+
+:local Data;
+:do {
+ :set Data ([ /tool/fetch check-certificate=yes-without-crl output=user \
+ ("https://api.telegram.org/bot" . $TelegramTokenId . "/getUpdates?offset=" . \
+ $TelegramChatOffset->0 . "&allowed_updates=%5B%22message%22%5D") as-value ]->"data");
+ :set Data [ :pick $Data ([ :find $Data "[" ] + 1) ([ :len $Data ] - 2) ];
+} on-error={
+ $LogPrintExit2 debug $0 ("Failed getting updates from Telegram.") true;
+}
+
+:local UpdateID 0;
+:local Uptime [ /system/resource/get uptime ];
+:foreach Update in=[ :toarray $Data ] do={
+ :set UpdateID [ $JsonGetKey $Update "update_id" ];
+ :if (($TelegramChatOffset->0 > 0 || $Uptime > 5m) && $UpdateID >= $TelegramChatOffset->2) do={
+ :local Trusted false;
+ :local Message [ $JsonGetKey $Update "message" ];
+ :local MessageId [ $JsonGetKey $Message "message_id" ];
+ :local From [ $JsonGetKey $Message "from" ];
+ :local FromID [ $JsonGetKey $From "id" ];
+ :local FromUserName [ $JsonGetKey $From "username" ];
+ :local ChatID [ $JsonGetKey [ $JsonGetKey $Message "chat" ] "id" ];
+ :local Text [ $JsonGetKey $Message "text" ];
+ :foreach IdsTrusted in=($TelegramChatId, $TelegramChatIdsTrusted) do={
+ :if ($FromID = $IdsTrusted || $FromUserName = $IdsTrusted) do={
+ :set Trusted true;
+ }
+ }
+
+ :if ($Trusted = true) do={
+ :if ([ :pick $Text 0 1 ] = "!") do={
+ :if ($Text ~ ("^! *(" . [ $EscapeForRegEx $Identity ] . "|@" . $TelegramChatGroups . ")\$")) do={
+ :set TelegramChatActive true;
+ } else={
+ :set TelegramChatActive false;
+ }
+ $LogPrintExit2 info $0 ("Now " . [ $IfThenElse $TelegramChatActive "active" "passive" ] . \
+ " from update " . $UpdateID . "!") false;
+ } else={
+ :if ($TelegramChatActive = true && $Text != false && [ :len $Text ] > 0) do={
+ :if ([ $ValidateSyntax $Text ] = true) do={
+ :local State "";
+ :local File ("tmpfs/telegram-chat/" . [ $GetRandom20CharAlNum 6 ]);
+ $MkDir "tmpfs/telegram-chat";
+ $LogPrintExit2 info $0 ("Running command from update " . $UpdateID . ": " . $Text) false;
+ :exec script=(":do {\n" . $Text . "\n} on-error={ :execute script=\"/\" file=" . $File . ".failed };" . \
+ ":execute script=\"/\" file=" . $File . ".done") file=$File;
+ :if ([ $WaitForFile ($File . ".done.txt") [ $EitherOr $TelegramChatRunTime 20s ] ] = false) do={
+ :set State "The command did not finish, still running in background.\n\n";
+ }
+ :if ([ :len [ /file/find where name=($File . ".failed.txt") ] ] > 0) do={
+ :set State "The command failed with an error!\n\n";
+ }
+ :local Content [ /file/get ($File . ".txt") contents ];
+ $SendTelegram2 ({ origin=$0; chatid=$ChatID; silent=false; replyto=$MessageId; \
+ subject=([ $SymbolForNotification "speech-balloon" ] . "Telegram Chat"); \
+ message=("Command:\n" . $Text . "\n\n" . $State . [ $IfThenElse ([ :len $Content ] > 0) \
+ ("Output:\n" . $Content) [ $IfThenElse ([ /file/get ($File . ".txt") size ] > 0) \
+ ("Output exceeds file read size.") ("No output.") ] ]) });
+ /file/remove "tmpfs/telegram-chat";
+ } else={
+ $LogPrintExit2 info $0 ("The command from update " . $UpdateID . " failed syntax validation!") false;
+ $SendTelegram2 ({ origin=$0; chatid=$ChatID; silent=false; replyto=$MessageId; \
+ subject=([ $SymbolForNotification "speech-balloon" ] . "Telegram Chat"); \
+ message=("Command:\n" . $Text . "\n\nThe command failed syntax validation!") });
+ }
+ }
+ }
+ } else={
+ :local Message ("Received a message from untrusted contact " . \
+ [ $IfThenElse ($FromUserName = false) "without username" ("'" . $FromUserName . "'") ] . \
+ " (ID " . $FromID . ") in update " . $UpdateID . "!");
+ :if ($Text ~ ("^! *" . [ $EscapeForRegEx $Identity ] . "\$")) do={
+ $LogPrintExit2 warning $0 $Message false;
+ $SendTelegram2 ({ origin=$0; chatid=$ChatID; silent=false; replyto=$MessageId; \
+ subject=([ $SymbolForNotification "speech-balloon" ] . "Telegram Chat"); \
+ message=("You are not trusted.") });
+ } else={
+ $LogPrintExit2 info $0 $Message false;
+ }
+ }
+ } else={
+ $LogPrintExit2 debug $0 ("Already handled update " . $UpdateID . ".") false;
+ }
+}
+:set TelegramChatOffset ([ :pick $TelegramChatOffset 1 3 ], \
+ [ $IfThenElse ($UpdateID >= $TelegramChatOffset->2) ($UpdateID + 1) ($TelegramChatOffset->2) ]);
diff --git a/unattended-lte-firmware-upgrade b/unattended-lte-firmware-upgrade
index eac65c3..2da00ca 100644
--- a/unattended-lte-firmware-upgrade
+++ b/unattended-lte-firmware-upgrade
@@ -1,45 +1,3 @@
#!rsc by RouterOS
-# RouterOS script: unattended-lte-firmware-upgrade
-# Copyright (c) 2018-2023 Christian Hesse <mail@eworm.de>
-# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md
#
-# schedule unattended lte firmware upgrade
-# https://git.eworm.de/cgit/routeros-scripts/about/doc/unattended-lte-firmware-upgrade.md
-
-:foreach Interface in=[ /interface/lte/find where running ] do={
- :local Firmware;
- :local IntName [ /interface/lte/get $Interface name ];
- :do {
- :set Firmware [ /interface/lte/firmware-upgrade $Interface once as-value ];
- } on-error={
- :log debug ("Could not get latest LTE firmware version for interface " . $IntName . ".");
- }
-
- :if ([ :typeof $Firmware ] = "array") do={
- :if (($Firmware->"installed") != ($Firmware->"latest")) do={
- :log info ("Scheduling LTE firmware upgrade for interface " . $IntName . ".");
-
- :global LTEFirmwareUpgrade do={
- :global LTEFirmwareUpgrade;
- :set LTEFirmwareUpgrade;
-
- /system/scheduler/remove ($1 . "-firmware-upgrade");
- /interface/lte/firmware-upgrade $1 upgrade=yes;
- :log info ("LTE firmware upgrade on '" . $1 . "' finished, waiting for reset.");
- :delay 240s;
- :local Firmware [ /interface/lte/firmware-upgrade $1 once as-value ];
- :if (($Firmware->"installed") != ($Firmware->"latest")) do={
- :log warning ("LTE firmware versions still differ. Resetting again...");
- /interface/lte/at-chat $1 input="AT+RESET";
- }
- }
-
- /system/scheduler/add name=($IntName . "-firmware-upgrade") start-time=startup interval=2s \
- on-event=(":global LTEFirmwareUpgrade; \$LTEFirmwareUpgrade \"" . $IntName . "\";");
- } else={
- :log info ("The LTE firmware is up to date on interface " . $IntName . ".");
- }
- } else={
- :log info ("No LTE firmware information available for interface " . $IntName . ".");
- }
-}
+# dummy for migration
diff --git a/unattended-lte-firmware-upgrade.rsc b/unattended-lte-firmware-upgrade.rsc
new file mode 100644
index 0000000..eac65c3
--- /dev/null
+++ b/unattended-lte-firmware-upgrade.rsc
@@ -0,0 +1,45 @@
+#!rsc by RouterOS
+# RouterOS script: unattended-lte-firmware-upgrade
+# Copyright (c) 2018-2023 Christian Hesse <mail@eworm.de>
+# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md
+#
+# schedule unattended lte firmware upgrade
+# https://git.eworm.de/cgit/routeros-scripts/about/doc/unattended-lte-firmware-upgrade.md
+
+:foreach Interface in=[ /interface/lte/find where running ] do={
+ :local Firmware;
+ :local IntName [ /interface/lte/get $Interface name ];
+ :do {
+ :set Firmware [ /interface/lte/firmware-upgrade $Interface once as-value ];
+ } on-error={
+ :log debug ("Could not get latest LTE firmware version for interface " . $IntName . ".");
+ }
+
+ :if ([ :typeof $Firmware ] = "array") do={
+ :if (($Firmware->"installed") != ($Firmware->"latest")) do={
+ :log info ("Scheduling LTE firmware upgrade for interface " . $IntName . ".");
+
+ :global LTEFirmwareUpgrade do={
+ :global LTEFirmwareUpgrade;
+ :set LTEFirmwareUpgrade;
+
+ /system/scheduler/remove ($1 . "-firmware-upgrade");
+ /interface/lte/firmware-upgrade $1 upgrade=yes;
+ :log info ("LTE firmware upgrade on '" . $1 . "' finished, waiting for reset.");
+ :delay 240s;
+ :local Firmware [ /interface/lte/firmware-upgrade $1 once as-value ];
+ :if (($Firmware->"installed") != ($Firmware->"latest")) do={
+ :log warning ("LTE firmware versions still differ. Resetting again...");
+ /interface/lte/at-chat $1 input="AT+RESET";
+ }
+ }
+
+ /system/scheduler/add name=($IntName . "-firmware-upgrade") start-time=startup interval=2s \
+ on-event=(":global LTEFirmwareUpgrade; \$LTEFirmwareUpgrade \"" . $IntName . "\";");
+ } else={
+ :log info ("The LTE firmware is up to date on interface " . $IntName . ".");
+ }
+ } else={
+ :log info ("No LTE firmware information available for interface " . $IntName . ".");
+ }
+}
diff --git a/update-gre-address b/update-gre-address
index 2958055..2da00ca 100644
--- a/update-gre-address
+++ b/update-gre-address
@@ -1,32 +1,3 @@
#!rsc by RouterOS
-# RouterOS script: update-gre-address
-# Copyright (c) 2013-2023 Christian Hesse <mail@eworm.de>
-# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md
#
-# update gre interface remote address with dynamic address from
-# ipsec remote peer
-# https://git.eworm.de/cgit/routeros-scripts/about/doc/update-gre-address.md
-
-:local 0 "update-gre-address";
-:global GlobalFunctionsReady;
-:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
-
-:global CharacterReplace;
-:global LogPrintExit2;
-
-/interface/gre/set remote-address=0.0.0.0 disabled=yes [ find where !running !disabled ];
-
-:foreach Peer in=[ /ip/ipsec/active-peers/find ] do={
- :local PeerVal [ /ip/ipsec/active-peers/get $Peer ];
- :local GreInt [ /interface/gre/find where comment=($PeerVal->"id") or comment=[ $CharacterReplace ($PeerVal->"id") "CN=" "" ] ];
- :if ([ :len $GreInt ] > 0) do={
- :local GreIntVal [ /interface/gre/get $GreInt ];
- :if ([ :typeof ($PeerVal->"dynamic-address") ] = "str" && \
- ($PeerVal->"dynamic-address" != $GreIntVal->"remote-address" || \
- $GreIntVal->"disabled" = true)) do={
- $LogPrintExit2 info $0 ("Updating remote address for interface " . $GreIntVal->"name" . " to " . $PeerVal->"dynamic-address") false;
- /interface/gre/set remote-address=0.0.0.0 disabled=yes [ find where remote-address=$PeerVal->"dynamic-address" name!=$GreIntVal->"name" ];
- /interface/gre/set $GreInt remote-address=($PeerVal->"dynamic-address") disabled=no;
- }
- }
-}
+# dummy for migration
diff --git a/update-gre-address.rsc b/update-gre-address.rsc
new file mode 100644
index 0000000..2958055
--- /dev/null
+++ b/update-gre-address.rsc
@@ -0,0 +1,32 @@
+#!rsc by RouterOS
+# RouterOS script: update-gre-address
+# Copyright (c) 2013-2023 Christian Hesse <mail@eworm.de>
+# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md
+#
+# update gre interface remote address with dynamic address from
+# ipsec remote peer
+# https://git.eworm.de/cgit/routeros-scripts/about/doc/update-gre-address.md
+
+:local 0 "update-gre-address";
+:global GlobalFunctionsReady;
+:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
+
+:global CharacterReplace;
+:global LogPrintExit2;
+
+/interface/gre/set remote-address=0.0.0.0 disabled=yes [ find where !running !disabled ];
+
+:foreach Peer in=[ /ip/ipsec/active-peers/find ] do={
+ :local PeerVal [ /ip/ipsec/active-peers/get $Peer ];
+ :local GreInt [ /interface/gre/find where comment=($PeerVal->"id") or comment=[ $CharacterReplace ($PeerVal->"id") "CN=" "" ] ];
+ :if ([ :len $GreInt ] > 0) do={
+ :local GreIntVal [ /interface/gre/get $GreInt ];
+ :if ([ :typeof ($PeerVal->"dynamic-address") ] = "str" && \
+ ($PeerVal->"dynamic-address" != $GreIntVal->"remote-address" || \
+ $GreIntVal->"disabled" = true)) do={
+ $LogPrintExit2 info $0 ("Updating remote address for interface " . $GreIntVal->"name" . " to " . $PeerVal->"dynamic-address") false;
+ /interface/gre/set remote-address=0.0.0.0 disabled=yes [ find where remote-address=$PeerVal->"dynamic-address" name!=$GreIntVal->"name" ];
+ /interface/gre/set $GreInt remote-address=($PeerVal->"dynamic-address") disabled=no;
+ }
+ }
+}
diff --git a/update-tunnelbroker b/update-tunnelbroker
index 84d7430..2da00ca 100644
--- a/update-tunnelbroker
+++ b/update-tunnelbroker
@@ -1,55 +1,3 @@
#!rsc by RouterOS
-# RouterOS script: update-tunnelbroker
-# Copyright (c) 2013-2023 Christian Hesse <mail@eworm.de>
-# Michael Gisbers <michael@gisbers.de>
-# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md
#
-# provides: ppp-on-up
-#
-# update local address of tunnelbroker interface
-# https://git.eworm.de/cgit/routeros-scripts/about/doc/update-tunnelbroker.md
-
-:local 0 "update-tunnelbroker";
-:global GlobalFunctionsReady;
-:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
-
-:global CertificateAvailable;
-:global LogPrintExit2;
-:global ParseKeyValueStore;
-
-:if ([ $CertificateAvailable "Starfield Secure Certificate Authority - G2" ] = false) do={
- $LogPrintExit2 error $0 ("Downloading required certificate failed.") true;
-}
-
-:foreach Interface in=[ /interface/6to4/find where comment~"^tunnelbroker" !disabled ] do={
- :local I 0;
- :local Response "";
- :local InterfaceVal [ /interface/6to4/get $Interface ];
- :local Comment [ $ParseKeyValueStore ($InterfaceVal->"comment") ];
-
- :while ($I < 3 && $Response = "") do={
- :do {
- :set Response ([ /tool/fetch check-certificate=yes-without-crl \
- ("https://ipv4.tunnelbroker.net/nic/update\?hostname=" . $Comment->"id") \
- user=($Comment->"user") password=($Comment->"pass") output=user as-value ]->"data");
- } on-error={
- :delay 10s;
- :set I ($I + 1);
- }
- }
-
- :if (!($Response~"^(good|nochg) ")) do={
- $LogPrintExit2 error $0 ("Failed sending the local address to tunnelbroker or unexpected response!") true;
- }
-
- :local PublicAddress [ :pick $Response ([ :find $Response " " ] + 1) [ :find $Response "\n" ] ];
-
- :if ($PublicAddress != $InterfaceVal->"local-address") do={
- :if ([ :len [ /ip/address find where address~("^" . $PublicAddress . "/") ] ] < 1) do={
- $LogPrintExit2 warning $0 ("The address " . $PublicAddress . " is not configured on your device. NAT by ISP?") false;
- }
-
- $LogPrintExit2 info $0 ("Local address changed, updating tunnel configuration with address: " . $PublicAddress) false;
- /interface/6to4/set $Interface local-address=$PublicAddress;
- }
-}
+# dummy for migration
diff --git a/update-tunnelbroker.rsc b/update-tunnelbroker.rsc
new file mode 100644
index 0000000..84d7430
--- /dev/null
+++ b/update-tunnelbroker.rsc
@@ -0,0 +1,55 @@
+#!rsc by RouterOS
+# RouterOS script: update-tunnelbroker
+# Copyright (c) 2013-2023 Christian Hesse <mail@eworm.de>
+# Michael Gisbers <michael@gisbers.de>
+# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md
+#
+# provides: ppp-on-up
+#
+# update local address of tunnelbroker interface
+# https://git.eworm.de/cgit/routeros-scripts/about/doc/update-tunnelbroker.md
+
+:local 0 "update-tunnelbroker";
+:global GlobalFunctionsReady;
+:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
+
+:global CertificateAvailable;
+:global LogPrintExit2;
+:global ParseKeyValueStore;
+
+:if ([ $CertificateAvailable "Starfield Secure Certificate Authority - G2" ] = false) do={
+ $LogPrintExit2 error $0 ("Downloading required certificate failed.") true;
+}
+
+:foreach Interface in=[ /interface/6to4/find where comment~"^tunnelbroker" !disabled ] do={
+ :local I 0;
+ :local Response "";
+ :local InterfaceVal [ /interface/6to4/get $Interface ];
+ :local Comment [ $ParseKeyValueStore ($InterfaceVal->"comment") ];
+
+ :while ($I < 3 && $Response = "") do={
+ :do {
+ :set Response ([ /tool/fetch check-certificate=yes-without-crl \
+ ("https://ipv4.tunnelbroker.net/nic/update\?hostname=" . $Comment->"id") \
+ user=($Comment->"user") password=($Comment->"pass") output=user as-value ]->"data");
+ } on-error={
+ :delay 10s;
+ :set I ($I + 1);
+ }
+ }
+
+ :if (!($Response~"^(good|nochg) ")) do={
+ $LogPrintExit2 error $0 ("Failed sending the local address to tunnelbroker or unexpected response!") true;
+ }
+
+ :local PublicAddress [ :pick $Response ([ :find $Response " " ] + 1) [ :find $Response "\n" ] ];
+
+ :if ($PublicAddress != $InterfaceVal->"local-address") do={
+ :if ([ :len [ /ip/address find where address~("^" . $PublicAddress . "/") ] ] < 1) do={
+ $LogPrintExit2 warning $0 ("The address " . $PublicAddress . " is not configured on your device. NAT by ISP?") false;
+ }
+
+ $LogPrintExit2 info $0 ("Local address changed, updating tunnel configuration with address: " . $PublicAddress) false;
+ /interface/6to4/set $Interface local-address=$PublicAddress;
+ }
+}