aboutsummaryrefslogtreecommitdiffstats
path: root/global-functions
diff options
context:
space:
mode:
Diffstat (limited to 'global-functions')
-rw-r--r--global-functions905
1 files changed, 0 insertions, 905 deletions
diff --git a/global-functions b/global-functions
deleted file mode 100644
index 3895550..0000000
--- a/global-functions
+++ /dev/null
@@ -1,905 +0,0 @@
-#!rsc
-# RouterOS script: global-functions
-# Copyright (c) 2013-2020 Christian Hesse <mail@eworm.de>
-# Michael Gisbers <michael@gisbers.de>
-# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md
-#
-# global functions
-# https://git.eworm.de/cgit/routeros-scripts/about/
-
-# expected configuration version
-:global ExpectedConfigVersion 24;
-
-# 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 DefaultRouteIsReachable;
-:global DeviceInfo;
-:global DNSIsResolving;
-:global DownloadPackage;
-:global GetMacVendor;
-:global GetRandom20CharHex;
-:global GetRandomNumber;
-:global IfThenElse;
-:global IPCalc;
-:global LogPrintExit;
-:global MailServerIsUp;
-:global MkDir;
-:global ParseKeyValueStore;
-:global RandomDelay;
-:global ScriptFromTerminal;
-:global ScriptInstallUpdate;
-:global ScriptLock;
-:global SendEMail;
-:global SendNotification;
-:global SendTelegram;
-:global SymbolByUnicodeName;
-:global SymbolForNotification;
-:global TimeIsSync;
-:global UrlEncode;
-: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 LogPrintExit;
- :global ParseKeyValueStore;
-
- :if ([ / system resource get free-hdd-space ] < 8388608 && \
- [ / certificate settings get crl-download ] = true && \
- [ / certificate settings get crl-store ] = "system") do={
- $LogPrintExit warning ("This system has low free flash space but " . \
- "is configured to download certificate CRLs to system!") false;
- }
-
- :if ([ / certificate print count-only where common-name=$CommonName ] = 0) do={
- $LogPrintExit info ("Certificate with CommonName \"" . $CommonName . "\" not available.") false;
- :if ([ $CertificateDownload $CommonName ] = false) do={
- :return false;
- }
- }
-
- :local CertVal;
- :local Issuer $CommonName;
- :do {
- :if ([ / certificate print count-only where common-name=$Issuer ] = 0) do={
- $LogPrintExit info ("Certificate chain for \"" . $CommonName . \
- "\" is incomplete, missing \"" . $Issuer . "\".") false;
- :if ([ $CertificateDownload $CommonName ] = false) do={
- :return false;
- }
- }
- :set CertVal [ / certificate get [ find where common-name=$Issuer ] ];
- :set Issuer ([ $ParseKeyValueStore ($CertVal->"issuer") ]->"CN");
- } while=($Issuer != $CertVal->"common-name");
- :return true;
-}
-
-# download and import certificate
-:set CertificateDownload do={
- :local CommonName [ :tostr $1 ];
-
- :global ScriptUpdatesBaseUrl;
- :global ScriptUpdatesUrlSuffix;
-
- :global CertificateNameByCN;
- :global LogPrintExit;
- :global UrlEncode;
- :global WaitForFile;
-
- $LogPrintExit info ("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;
- $WaitForFile $LocalFileName;
- / certificate import file-name=$LocalFileName passphrase="";
- / file remove $LocalFileName;
-
- :foreach Cert in=[ / certificate find where name~("^" . $LocalFileName . "_[0-9]+\$") ] do={
- $CertificateNameByCN [ / certificate get $Cert common-name ];
- }
- } on-error={
- $LogPrintExit warning ("Failed imprting certificate!") 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;
-}
-
-# default route is reachable
-:set DefaultRouteIsReachable do={
- :if ([ / ip route print count-only where dst-address=0.0.0.0/0 !unreachable active !routing-mark ] > 0) do={
- :return true;
- }
- :return false;
-}
-
-# get readable device info
-:set DeviceInfo do={
- :global ExpectedConfigVersion;
- :global GlobalConfigVersion;
- :global Identity;
-
- :global IfThenElse;
-
- :local Resource [ / system resource get ];
- :local RouterBoard [ / system routerboard 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") ] . \
- "\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") ] . \
- "\nRouterOS-Scripts Configuration Version:" . \
- "\n Current: " . $GlobalConfigVersion . \
- [ $IfThenElse ($GlobalConfigVersion != $ExpectedConfigVersion) \
- ("\n Expected: " . $ExpectedConfigVersion) ]);
-}
-
-# check if DNS is resolving
-:set DNSIsResolving do={
- :global CharacterReplace;
-
- :do {
- :resolve ([ $CharacterReplace [ / system clock get time ] ":" "" ] . ".mikrotik.com");
- :return true;
- } on-error={
- :return false;
- }
-}
-
-# 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 LogPrintExit;
- :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 ]; }
-
- :local PkgFile ($PkgName . "-" . $PkgVer . "-" . $PkgArch . ".npk");
- :if ($PkgArch = "x86_64" || $PkgName ~ "^routeros-") do={
- :set PkgFile ($PkgName . "-" . $PkgVer . ".npk");
- }
- :local PkgDest [ $CleanFilePath ($PkgDir . "/" . $PkgFile) ];
-
- :if ([ / file print count-only where name=$PkgDest type="package" ] > 0) do={
- $LogPrintExit info ("Package file alreasy exists.") false;
- :return true;
- }
-
- :if ([ $CertificateAvailable "Let's Encrypt Authority X3" ] = false) do={
- $LogPrintExit error ("Downloading required certificate failed.") true;
- }
-
- :local Retry 3;
- :while ($Retry > 0) do={
- :do {
- / tool fetch check-certificate=yes-without-crl \
- ("https://upgrade.mikrotik.com/routeros/" . $PkgVer . "/" . $PkgFile) \
- dst-path=$PkgDest;
- $WaitForFile $PkgDest;
-
- :if ([ / file get [ find where name=$PkgDest ] type ] = "package") do={
- :return true;
- }
- } on-error={
- $LogPrintExit debug ("Downloading package failed.") false;
- }
-
- / file remove [ find where name=$PkgDest ];
- :set Retry ($Retry - 1);
- }
-
- :return false;
-}
-
-# get MAC vendor
-:set GetMacVendor do={
- :local Mac [ :tostr $1 ];
-
- :global CertificateAvailable;
- :global LogPrintExit;
-
- :do {
- :if ([ $CertificateAvailable "Let's Encrypt Authority X3" ] = false) do={
- $LogPrintExit warning ("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={
- :return "unknown vendor";
- }
-}
-
-# generate random 20 chars hex (0-9 and a-f)
-:set GetRandom20CharHex do={
- :local Random ([ / certificate scep-server otp generate minutes-valid=0 as-value ]->"password");
- / certificate scep-server otp remove [ find where password=$Random ];
- :return $Random;
-}
-
-# generate random number
-:set GetRandomNumber do={
- :local Max 4294967295;
- :if ([ :typeof $1 ] != "nothing" ) do={
- :set Max ([ :tonum $1 ] + 1);
- }
-
- :global GetRandom20CharHex;
-
- :local Num;
- :local 40CharHex ([ $GetRandom20CharHex ] . [ $GetRandom20CharHex ]);
-
- :for I from=0 to=39 do={
- :local Char [ :pick $40CharHex $I ];
- :if ($Char~"[0-9]") do={
- :set Num ($Num . $Char);
- }
- }
-
- :return ([ :tonum [ :pick $Num 0 18 ] ] % $Max);
-}
-
-# mimic conditional/ternary operator (condition ? consequent : alternative)
-:set IfThenElse do={
- :if ([ :tostr $1 ] = "true" || [ :tobool $1 ] = true) do={
- :return $2;
- }
- :return $3;
-}
-
-# calculate and print netmask, network, min host, max host and broadcast
-:set IPCalc 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);
-
- :put ( \
- "Address: " . $Address . "\n\r" . \
- "Netmask: " . $Mask . "\n\r" . \
- "Network: " . ($Address & $Mask) . "/" . $Bits . "\n\r" . \
- "HostMin: " . (($Address & $Mask) | 0.0.0.1) . "\n\r" . \
- "HostMax: " . (($Address | ~$Mask) ^ 0.0.0.1) . "\n\r" . \
- "Broadcast: " . ($Address | ~$Mask));
-}
-
-# log and print with same text, optionally exit
-:set LogPrintExit do={
- :local Severity [ :tostr $1 ];
- :local Message [ :tostr $2 ];
- :local Exit [ :tostr $3 ];
-
- :global PrintDebug;
-
- :if ($Severity ~ "^(debug|error|info)\$") do={
- :if ($Severity = "debug") do={ :log debug $Message; }
- :if ($Severity = "error") do={ :log error $Message; }
- :if ($Severity = "info" ) do={ :log info $Message; }
- } else={
- :log warning $Message;
- }
-
- :if ($Severity != "debug" || $PrintDebug = true) do={
- :if ($Exit = "true") do={
- :error ($Severity . ": " . $Message);
- } else={
- :put ($Severity . ": " . $Message);
- }
- }
-}
-
-# check if mail server is up
-:set MailServerIsUp do={
- :local MailServer [ / tool e-mail get address ];
-
- :global LogPrintExit;
-
- :if ([ / tool netwatch print count-only where comment=$MailServer ] = 0) do={
- $LogPrintExit warning ("Adding netwatch entry for mail server.") false;
- :local MailHost $MailServer;
- :if ([ :typeof [ :toip $MailHost ] ] != "ip" ) do={
- :do {
- :set MailHost [ :resolve $MailServer ];
- } on-error={
- $LogPrintExit warning ("Resolving mail server failed.") false;
- :return false;
- }
- }
- / tool netwatch add comment=$MailServer host=$MailHost;
- }
-
- :local NetWatch [ / tool netwatch find where comment=$MailServer ];
- :local NetWatchVal [ / tool netwatch get $NetWatch ];
- :if ($NetWatchVal->"status" = "up") do={
- :return true;
- }
-
- / tool netwatch set interval=($NetWatchVal->"interval") $NetWatch;
- :delay ($NetWatchVal->"timeout");
- :if ([ / tool netwatch get $NetWatch status ] = "up") do={
- :return true;
- }
-
- :return false;
-}
-
-# create directory
-:set MkDir do={
- :local Dir [ :tostr $1 ];
-
- :global WaitForFile;
-
- :if ([ / file print count-only where name=$Dir type="directory" ] = 0) do={
- :local WwwVal [ / ip service get www ];
- / ip service set www address=127.0.0.1/32 disabled=no port=80;
- / tool fetch http://127.0.0.1/ dst-path=($Dir . "/tmp");
- $WaitForFile ($Dir . "/tmp");
- / file remove ($Dir . "/tmp");
- / ip service set www address=($WwwVal->"address") \
- disabled=($WwwVal->"disabled") port=($WwwVal->"port");
- }
-}
-
-# parse key value store
-:set ParseKeyValueStore do={
- :local Source $1;
- :if ([ :typeof $Source ] != "array") do={
- :set Source [ :tostr $1 ];
- }
- :local Result [ :toarray "" ];
- :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;
-}
-
-# delay a random amount of seconds
-:set RandomDelay do={
- :global GetRandomNumber;
-
- :delay ([ $GetRandomNumber $1 ] . "s");
-}
-
-# check if script is run from terminal
-:set ScriptFromTerminal do={
- :local Script [ :tostr $1 ];
-
- :global LogPrintExit;
-
- :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={
- $LogPrintExit debug ("Script " . $Script . " started from terminal.") false;
- :return true;
- }
- }
- $LogPrintExit debug ("Script " . $Script . " NOT started from terminal.") false;
-
- :return false;
-}
-
-# install new scripts, update existing scripts
-:set ScriptInstallUpdate do={
- :local Scripts [ :toarray $1 ];
-
- :global ExpectedConfigVersion;
- :global GlobalConfigVersion;
- :global Identity;
- :global IDonate;
- :global ScriptUpdatesBaseUrl;
- :global ScriptUpdatesFetch;
- :global ScriptUpdatesIgnore;
- :global ScriptUpdatesUrlSuffix;
- :global SentConfigChangesNotification;
-
- :global LogPrintExit;
- :global ParseKeyValueStore;
- :global SendNotification;
- :global SymbolForNotification;
-
- :foreach Script in=$Scripts do={
- :if ([ / system script print count-only where name=$Script ] = 0) do={
- $LogPrintExit info ("Adding new script: " . $Script) false;
- / system script add name=$Script source="#!rsc";
- }
- }
-
- :foreach Script in=[ / system script find where source~"^#!rsc" ] do={
- :local Ignore 0;
- :local ScriptVal [ / system script get $Script ];
- :local ScriptFile [ / file find where name=("script-updates/" . $ScriptVal->"name") ];
- :local SourceNew;
- :if ([ :len $ScriptFile ] > 0) do={
- :set SourceNew [ / file get $ScriptFile content ];
- / 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={
- $LogPrintExit warning ("Policies differ for script " . $ScriptVal->"name" . \
- " and its scheduler " . $SchedulerVal->"name" . "!") false;
- }
- :if ($SchedulerVal->"name" != "global-scripts" && \
- $SchedulerVal->"start-time" = "startup" && \
- $SchedulerVal->"interval" = 0s && \
- !(($SchedulerVal->"on-event") ~ "\\brun global-wait\\b")) do={
- $LogPrintExit warning ("Scheduler " . $SchedulerVal->"name" . " starts on startup, " . \
- "without waiting for global-functions. Run 'global-wait' to avoid race conditions!") false;
- }
- }
-
- :if ([ :len $SourceNew ] = 0 && $ScriptUpdatesFetch = true) do={
- :local Comment [ $ParseKeyValueStore ($ScriptVal->"comment") ];
- :if ($Comment->"ignore" = true) do={
- :set Ignore 1;
- } else={
- # TODO: remove at later time
- :foreach IgnoreLoop in=$ScriptUpdatesIgnore do={
- :if ($IgnoreLoop = $ScriptVal->"name") do={
- :set Ignore 1;
- $LogPrintExit info ("Migrating script " . $ScriptVal->"name" . " to ignore flag in comment.") false;
- / system script set comment="ignore" ($ScriptVal->"name");
- }
- }
- }
-
- :if ($Ignore = 0) do={
- $LogPrintExit debug ("Fetching script from url: " . $ScriptVal->"name") false;
- :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 Result [ / tool fetch check-certificate=yes-without-crl \
- ($BaseUrl . $ScriptVal->"name" . $UrlSuffix) output=user as-value ];
- :if ($Result->"status" = "finished") do={
- :set SourceNew ($Result->"data");
- }
- } on-error={
- $LogPrintExit warning ("Failed fetching " . $ScriptVal->"name") false;
- }
- }
- }
-
- :if ([ :len $SourceNew ] > 0) do={
- :if ([ :pick $SourceNew 0 5 ] = "#!rsc") do={
- :if ($SourceNew != $ScriptVal->"source") do={
- :local DontRequirePermissions \
- ($SourceNew~"\n# requires: dont-require-permissions=yes\n");
- $LogPrintExit info ("Updating script: " . $ScriptVal->"name") false;
- / system script set owner=($ScriptVal->"name") source=$SourceNew \
- dont-require-permissions=$DontRequirePermissions $Script;
- :if ($ScriptVal->"name" = "global-config" && \
- [ / system script print count-only where name="global-config-overlay" ] > 0) do={
- $LogPrintExit info ("Reloading global configuration and overlay.") false;
- / system script { run global-config; run global-config-overlay; }
- }
- :if ($ScriptVal->"name" = "global-functions") do={
- $LogPrintExit info ("Reloading global functions.") false;
- / system script run global-functions;
- }
- } else={
- $LogPrintExit debug ("Script " . $ScriptVal->"name" . " did not change.") false;
- }
- } else={
- $LogPrintExit warning ("Looks like new script " . $ScriptVal->"name" . " is not valid. Ignoring!") false;
- }
- } else={
- $LogPrintExit debug ("No update for script " . $ScriptVal->"name" . ".") false;
- }
- }
-
- :if ($SentConfigChangesNotification!=$ExpectedConfigVersion && \
- $GlobalConfigVersion < $ExpectedConfigVersion) do={
- :global GlobalConfigChanges;
- :local ChangeLogCode;
- :local ConfigScript "global-config";
- :if ([ /system script print count-only where name="global-config-overlay" ] > 0) do={
- :set ConfigScript "global-config-overlay";
- }
- :local NotificationMessage ("Current configuration on " . $Identity . \
- " is out of date. Please update " . $ConfigScript . ", then increase " . \
- "\$GlobalConfigVersion (currently " . $GlobalConfigVersion . \
- ") to " . $ExpectedConfigVersion . " and re-run " . $ConfigScript . ".");
- $LogPrintExit info ($NotificationMessage) false;
-
- $LogPrintExit debug ("Fetching changelog.") false;
- :do {
- :local Result [ / tool fetch check-certificate=yes-without-crl \
- ($ScriptUpdatesBaseUrl . "global-config.changes" . $ScriptUpdatesUrlSuffix) \
- output=user as-value ];
- :if ($Result->"status" = "finished") do={
- :set ChangeLogCode ($Result->"data");
- }
- :set NotificationMessage ($NotificationMessage . "\n\nChanges:");
- [ :parse $ChangeLogCode ];
- :for I from=($GlobalConfigVersion + 1) to=$ExpectedConfigVersion do={
- :set NotificationMessage ($NotificationMessage . \
- "\n * " . $GlobalConfigChanges->[ :tostr $I ]);
- $LogPrintExit info ("Change: " . $GlobalConfigChanges->[ :tostr $I ]) false;
- }
- :set GlobalConfigChanges;
- } on-error={
- $LogPrintExit warning ("Failed fetching changes!") false;
- :set NotificationMessage ($NotificationMessage . \
- "\n\nChanges are not available.");
- }
-
- :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:\n" . \
- "https://git.eworm.de/cgit/routeros-scripts/about/#donate");
- }
-
- $SendNotification ([ $SymbolForNotification "pushpin" ] . "News and configuration changes") \
- $NotificationMessage;
- :set SentConfigChangesNotification $ExpectedConfigVersion;
- }
-}
-
-# lock script against multiple invocation
-:set ScriptLock do={
- :global LogPrintExit;
-
- :local Script [ :tostr $1 ];
-
- :if ([ / system script job print count-only where script=$Script ] > 1) do={
- $LogPrintExit info ("Script " . $Script . " started more than once... Aborting.") true;
- }
-}
-
-# send notification via e-mail
-:set SendEMail do={
- :local Subject [ :tostr $1 ];
- :local Message [ :tostr $2 ];
- :local Attach [ :tostr $3 ];
-
- :global Identity;
- :global EmailGeneralTo;
- :global EmailGeneralCc;
-
- :global LogPrintExit;
-
- :if ([ :len $EmailGeneralTo ] = 0) do={
- :return false;
- }
-
- :do {
- :local Signature [ / system note get note ];
- :if ([ :len $Signature ] > 0) do={
- :set Signature ("\n-- \n" . $Signature);
- }
- / tool e-mail send to=$EmailGeneralTo cc=$EmailGeneralCc \
- subject=("[" . $Identity . "] " . $Subject) \
- body=($Message . $Signature) file=$Attach;
- } on-error={
- $LogPrintExit warning ("Failed sending notification mail!") false;
- }
-}
-
-# send notification via e-mail and telegram
-# Note that attachment is ignored for telegram, silent is ignored for e-mail!
-:set SendNotification do={
- :local Subject [ :tostr $1 ];
- :local Message [ :tostr $2 ];
- :local Attach [ :tostr $3 ];
- :local Silent [ :tostr $4 ];
-
- :global SendEMail;
- :global SendTelegram;
-
- $SendEMail $Subject $Message $Attach;
- $SendTelegram $Subject $Message $Silent;
-}
-
-# send notification via telegram
-:set SendTelegram do={
- :local Subject [ :tostr $1 ];
- :local Message [ :tostr $2 ];
- :local Silent [ :tostr $3 ];
-
- :global Identity;
- :global TelegramTokenId;
- :global TelegramChatId;
- :global TelegramChatIdOverride;
-
- :global CertificateAvailable;
- :global LogPrintExit;
- :global UrlEncode;
-
- :local ChatId $TelegramChatId;
- :if ([ :len $TelegramChatIdOverride ] > 0) do={
- :set ChatId $TelegramChatIdOverride;
- }
-
- :if ([ :len $TelegramTokenId ] = 0 || [ :len $ChatId ] = 0) do={
- :return false;
- }
-
- :do {
- :if ([ $CertificateAvailable "Go Daddy Secure Certificate Authority - G2" ] = false) do={
- $LogPrintExit warning ("Downloading required certificate failed.") true;
- }
- / tool fetch check-certificate=yes-without-crl output=none http-method=post \
- ("https://api.telegram.org/bot" . $TelegramTokenId . "/sendMessage") \
- http-data=("chat_id=" . $ChatId . "&disable_notification=" . $Silent . \
- "&text=" . [ $UrlEncode ("[" . $Identity . "] " . $Subject . "\n\n" . $Message) ]);
- } on-error={
- $LogPrintExit warning ("Failed sending telegram notification!") false;
- }
-}
-
-# return UTF-8 symbol for unicode name
-:set SymbolByUnicodeName do={
- :local Symbols {
- "cross-mark"="\E2\9D\8C";
- "fire"="\F0\9F\94\A5";
- "floppy-disk"="\F0\9F\92\BE";
- "high-voltage-sign"="\E2\9A\A1";
- "incoming-envelope"="\F0\9F\93\A8";
- "lock-with-ink-pen"="\F0\9F\94\8F";
- "pushpin"="\F0\9F\93\8C";
- "sparkles"="\E2\9C\A8";
- "warning-sign"="\E2\9A\A0";
- "white-heavy-check-mark"="\E2\9C\85"
- }
-
- :return ($Symbols->$1);
-}
-
-# return symbol for notification
-:set SymbolForNotification do={
- :global NotificationsWithSymbols;
-
- :global SymbolByUnicodeName;
-
- :if ($NotificationsWithSymbols != true) do={
- :return "";
- }
- :return ([ $SymbolByUnicodeName $1 ] . " ");
-}
-
-# check if system time is sync
-:set TimeIsSync do={
- :if ([ / system ntp client get enabled ] = true) do={
- :do {
- :if ([ / system ntp client get status ] = "synchronized") do={
- :return true;
- }
- } on-error={
- :if ([ :typeof [ / system ntp client get last-adjustment ] ] = "time") do={
- :return true;
- }
- }
- }
-
- :if ([ / ip cloud get update-time ] = true && \
- [ :typeof [ / ip cloud get public-address ] ] = "ip") do={
- :return true;
- }
-
- :return false;
-}
-
-# url encoding
-:set UrlEncode do={
- :local Input [ :tostr $1 ];
- :local Return "";
-
- :if ([ :len $Input ] > 0) do={
- :local Chars " !\"#\$%&'()*+,:;<=>\?@[\\]^`{|}~";
- :local Subs { "%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 ([ :len $Replace ] > 0) do={
- :set Char ($Subs->$Replace);
- }
- :set Return ($Return . $Char);
- }
- }
-
- :return $Return;
-}
-
-# 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 DefaultRouteIsReachable;
-
- :while ([ $DefaultRouteIsReachable ] = false) do={
- :delay 1s;
- }
-}
-
-# wait for DNS to resolve
-:set WaitDNSResolving do={
- :global DNSIsResolving;
-
- :while ([ $DNSIsResolving ] = false) do={
- :delay 1s;
- }
-}
-
-# wait for file to be available
-:set WaitForFile do={
- :global CleanFilePath;
-
- :local FileName [ $CleanFilePath [ :tostr $1 ] ];
- :local I 0;
-
- :while ([ file print count-only where name=$FileName ] = 0) do={
- :if ($I > 20) do={
- :return false;
- }
- :delay 100ms;
- :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 LogPrintExit;
- :global TimeIsSync;
-
- :while ([ $TimeIsSync ] = false) do={
- :if ([ / system script print count-only where name="rotate-ntp" ] > 0 && \
- [ :tostr [ / system resource get uptime ] ] ~ "[369]:00\$") do={
- :do {
- / system script run rotate-ntp;
- } on-error={
- $LogPrintExit debug ("Running rotate-ntp failed.") false;
- }
- }
- :delay 1s;
- }
-}
-
-# signal we are ready
-:set GlobalFunctionsReady true;