aboutsummaryrefslogtreecommitdiffstats
path: root/global-functions.rsc
diff options
context:
space:
mode:
Diffstat (limited to 'global-functions.rsc')
-rw-r--r--global-functions.rsc303
1 files changed, 185 insertions, 118 deletions
diff --git a/global-functions.rsc b/global-functions.rsc
index 8ae7bb8..829cbf2 100644
--- a/global-functions.rsc
+++ b/global-functions.rsc
@@ -15,7 +15,7 @@
# Git commit id & info, expected configuration version
:global CommitId "unknown";
:global CommitInfo "unknown";
-:global ExpectedConfigVersion 135;
+:global ExpectedConfigVersion 138;
# global variables not to be changed by user
:global GlobalFunctionsReady false;
@@ -38,6 +38,8 @@
:global ExitError;
:global FetchHuge;
:global FetchUserAgentStr;
+:global FileExists;
+:global FileGet;
:global FormatLine;
:global FormatMultiLines;
:global GetMacVendor;
@@ -119,6 +121,11 @@
:return false;
}
+ :if (([ /certificate/settings/get ]->"builtin-trust-anchors") = "trusted" && \
+ [[ :parse (":return [ :len [ /certificate/builtin/find where common-name=\"" . $CommonName . "\" ] ]") ]] > 0) do={
+ :return true;
+ }
+
:if ([ :len [ /certificate/find where common-name=$CommonName ] ] = 0) do={
$LogPrint info $0 ("Certificate with CommonName '" . $CommonName . "' not available.");
:if ([ $CertificateDownload $CommonName ] = false) do={
@@ -167,8 +174,8 @@
$LogPrint warning $0 ("Failed downloading certificate with CommonName '" . $CommonName . \
"' from repository! Trying fallback to mkcert.org...");
:do {
- :if ([ $CertificateAvailable "ISRG Root X1" ] = false) do={
- $LogPrint error $0 ("Downloading required certificate failed.");
+ :if ([ :len [ /certificate/find where common-name="ISRG Root X1" ] ] = 0) do={
+ $LogPrint error $0 ("Required certificate is not available.");
:return false;
}
/tool/fetch check-certificate=yes-without-crl http-header-field=({ [ $FetchUserAgentStr $0 ] }) \
@@ -203,12 +210,19 @@
# name a certificate by its common-name
:set CertificateNameByCN do={
- :local CommonName [ :tostr $1 ];
+ :local Match [ :tostr $1 ];
:global CleanName;
+ :global LogPrint;
- :local Cert [ /certificate/find where common-name=$CommonName ];
+ :local Cert ([ /certificate/find where (common-name=$Match or fingerprint=$Match or name=$Match) ]->0);
+ :if ([ :len $Cert ] = 0) do={
+ $LogPrint warning $0 ("No matching certificate found.");
+ :return false;
+ }
+ :local CommonName [ /certificate/get $Cert common-name ];
/certificate/set $Cert name=[ $CleanName $CommonName ];
+ :return true;
}
# multiply given character(s)
@@ -351,6 +365,7 @@
:global CertificateAvailable;
:global CleanFilePath;
+ :global FileExists;
:global LogPrint;
:global MkDir;
:global RmFile;
@@ -371,7 +386,7 @@
:return false;
}
- :if ([ :len [ /file/find where name=$PkgDest type="package" ] ] > 0) do={
+ :if ([ $FileExists $PkgDest "package" ] = true) do={
$LogPrint info $0 ("Package file " . $PkgName . " already exists.");
:return true;
}
@@ -384,25 +399,22 @@
:local Url ("https://upgrade.mikrotik.com/routeros/" . $PkgVer . "/" . $PkgFile);
$LogPrint info $0 ("Downloading package file '" . $PkgName . "'...");
$LogPrint debug $0 ("... from url: " . $Url);
- :local Retry 3;
- :while ($Retry > 0) do={
- :do {
- /tool/fetch check-certificate=yes-without-crl $Url dst-path=$PkgDest;
- $WaitForFile $PkgDest;
- :if ([ /file/get [ find where name=$PkgDest ] type ] = "package") do={
- :return true;
- }
- } on-error={
- $LogPrint debug $0 ("Downloading package file failed.");
- }
+ :onerror Err {
+ /tool/fetch check-certificate=yes-without-crl $Url dst-path=$PkgDest;
+ $WaitForFile $PkgDest;
+ } do={
+ $LogPrint warning $0 ("Downloading package file '" . $PkgName . "' failed: " . $Err);
+ :return false;
+ }
+ :if ([ $FileExists $PkgDest "package" ] = false) do={
+ $LogPrint warning $0 ("Downloaded file is not a package, removing.");
$RmFile $PkgDest;
- :set Retry ($Retry - 1);
+ :return false;
}
- $LogPrint warning $0 ("Downloading package file '" . $PkgName . "' failed.");
- :return false;
+ :return true;
}
# return either first (if "true") or second
@@ -445,13 +457,15 @@
:set ExitError do={
:local ExitOK [ :tostr $1 ];
:local Name [ :tostr $2 ];
+ :local Error [ :tostr $3 ];
:global IfThenElse;
:global LogPrint;
:if ($ExitOK = "false") do={
$LogPrint error $Name ([ $IfThenElse ([ :pick $Name 0 1 ] = "\$") \
- "Function" "Script" ] . " '" . $Name . "' exited with error.");
+ "Function" "Script" ] . " '" . $Name . "' exited with error" . \
+ [ $IfThenElse (!($Error ~ "^(|true|false)\$")) (": " . $Error) "." ]);
}
}
@@ -480,14 +494,14 @@
}
:local FileName ($DirName . "/" . [ $CleanName $0 ] . "-" . [ $GetRandom20CharAlNum ]);
- :do {
+ :onerror Err {
/tool/fetch check-certificate=$CheckCert $Url dst-path=$FileName \
http-header-field=({ [ $FetchUserAgentStr $ScriptName ] }) as-value;
- } on-error={
+ } do={
:if ([ $WaitForFile $FileName 500ms ] = true) do={
$RmFile $FileName;
}
- $LogPrint debug $0 ("Failed downloading from: " . $Url);
+ $LogPrint debug $0 ("Failed downloading from " . $Url . " - " . $Err);
$RmDir $DirName;
:return false;
}
@@ -518,6 +532,47 @@
$Resource->"architecture-name" . " " . $Caller . "/Fetch (https://rsc.eworm.de/)");
}
+# check for existence of file, optionally with type
+:set FileExists do={
+ :local FileName [ :tostr $1 ];
+ :local Type [ :tostr $2 ];
+
+ :global FileGet;
+
+ :local FileVal [ $FileGet $FileName ];
+ :if ($FileVal = false) do={
+ :return false;
+ }
+
+ :if ([ :len ($FileVal->"size") ] = 0) do={
+ :return false;
+ }
+
+ :if ([ :len $Type ] = 0 || $FileVal->"type" = $Type) do={
+ :return true;
+ }
+
+ :return false;
+}
+
+# get file properties in array, or false on error
+:set FileGet do={
+ :local FileName [ :tostr $1 ];
+
+ :global WaitForFile;
+
+ :if ([ $WaitForFile $FileName 0s ] = false) do={
+ :return false;
+ }
+
+ :local FileVal false;
+ :do {
+ :set FileVal [ /file/get $FileName ];
+ } on-error={ }
+
+ :return $FileVal;
+}
+
# format a line for output
:set FormatLine do={
:local Key [ :tostr $1 ];
@@ -580,12 +635,12 @@
("https://api.macvendors.com/" . [ :pick $Mac 0 8 ]) output=user as-value ]->"data");
:return $Vendor;
} on-error={
- :do {
+ :onerror Err {
/tool/fetch check-certificate=yes-without-crl ("https://api.macvendors.com/") \
output=none as-value;
$LogPrint debug $0 ("The mac vendor is not known in database.");
- } on-error={
- $LogPrint warning $0 ("Failed getting mac vendor.");
+ } do={
+ $LogPrint warning $0 ("Failed getting mac vendor: " . $Err);
}
:return "unknown vendor";
}
@@ -869,6 +924,7 @@
:local Path [ :tostr $1 ];
:global CleanFilePath;
+ :global FileGet;
:global LogPrint;
:global RmDir;
:global WaitForFile;
@@ -888,11 +944,11 @@
$LogPrint info $0 ("Creating disk of type tmpfs.");
$RmDir "tmpfs";
- :do {
+ :onerror Err {
/disk/add slot=tmpfs type=tmpfs tmpfs-max-size=([ /system/resource/get total-memory ] / 3);
$WaitForFile "tmpfs";
- } on-error={
- $LogPrint warning $0 ("Creating disk of type tmpfs failed!");
+ } do={
+ $LogPrint warning $0 ("Creating disk of type tmpfs failed: " . $Err);
:return false;
}
:return true;
@@ -906,7 +962,8 @@
$LogPrint debug $0 ("Making directory: " . $Path);
- :if ([ :len [ /file/find where name=$Path type="directory" ] ] = 1) do={
+ :local PathVal [ $FileGet $Path ];
+ :if ($PathVal->"type" = "directory") do={
$LogPrint debug $0 ("... which already exists.");
:return true;
}
@@ -917,11 +974,11 @@
}
}
- :do {
+ :onerror Err {
/file/add type="directory" name=$Path;
$WaitForFile $Path;
- } on-error={
- $LogPrint warning $0 ("Making directory '" . $Path . "' failed!");
+ } do={
+ $LogPrint warning $0 ("Making directory '" . $Path . "' failed: " . $Err);
:return false;
}
@@ -1031,25 +1088,26 @@
:set RmDir do={
:local DirName [ :tostr $1 ];
+ :global FileGet;
:global LogPrint;
$LogPrint debug $0 ("Removing directory: ". $DirName);
- :if ([ :len [ /file/find where name=$DirName type!=directory ] ] > 0) do={
- $LogPrint error $0 ("Directory '" . $DirName . "' is not a directory.");
- :return false;
- }
-
- :local Dir [ /file/find where name=$DirName type=directory ];
- :if ([ :len $Dir ] = 0) do={
+ :local DirVal [ $FileGet $DirName ];
+ :if ($DirVal = false) do={
$LogPrint debug $0 ("... which does not exist.");
:return true;
}
- :do {
- /file/remove $Dir;
- } on-error={
- $LogPrint error $0 ("Removing directory '" . $DirName . "' (" . $Dir . ") failed.");
+ :if ($DirVal->"type" != "directory") do={
+ $LogPrint error $0 ("Directory '" . $DirName . "' is not a directory.");
+ :return false;
+ }
+
+ :onerror Err {
+ /file/remove $DirName;
+ } do={
+ $LogPrint error $0 ("Removing directory '" . $DirName . "' failed: " . $Err);
:return false;
}
:return true;
@@ -1059,25 +1117,26 @@
:set RmFile do={
:local FileName [ :tostr $1 ];
+ :global FileGet;
:global LogPrint;
$LogPrint debug $0 ("Removing file: ". $FileName);
- :if ([ :len [ /file/find where name=$FileName (type=directory or type=disk) ] ] > 0) do={
- $LogPrint error $0 ("File '" . $FileName . "' is not a file.");
- :return false;
- }
-
- :local File [ /file/find where name=$FileName !(type=directory or type=disk) ];
- :if ([ :len $File ] = 0) do={
+ :local FileVal [ $FileGet $FileName ];
+ :if ($FileVal = false) do={
$LogPrint debug $0 ("... which does not exist.");
:return true;
}
- :do {
- /file/remove $File;
- } on-error={
- $LogPrint error $0 ("Removing file '" . $FileName . "' (" . $File . ") failed.");
+ :if ($FileVal->"type" = "directory" || $FileVal->"type" = "disk") do={
+ $LogPrint error $0 ("File '" . $FileName . "' is not a file.");
+ :return false;
+ }
+
+ :onerror Err {
+ /file/remove $FileName;
+ } do={
+ $LogPrint error $0 ("Removing file '" . $FileName . "' failed: " . $Err);
:return false;
}
:return true;
@@ -1110,13 +1169,15 @@
}
# install new scripts, update existing scripts
-:set ScriptInstallUpdate do={ :do {
+:set ScriptInstallUpdate do={ :onerror Err {
:local Scripts [ :toarray $1 ];
:local NewComment [ :tostr $2 ];
:global CommitId;
:global CommitInfo;
:global ExpectedConfigVersion;
+ :global GlobalConfigReady;
+ :global GlobalFunctionsReady;
:global Identity;
:global IDonate;
:global NoNewsAndChangesNotification;
@@ -1150,8 +1211,7 @@
:local CommitIdBefore $CommitId;
:local ExpectedConfigVersionBefore $ExpectedConfigVersion;
- :local ReloadGlobalFunctions false;
- :local ReloadGlobalConfig false;
+ :local ReloadGlobal false;
:local DeviceMode [ /system/device-mode/get ];
:local CheckSums ({});
@@ -1188,7 +1248,13 @@
:error true;
}
- :do {
+ :if ([ :len ($ScriptInfo->"certificate") ] > 0) do={
+ :if ([ $CertificateAvailable ($ScriptInfo->"certificate") ] = false) do={
+ $LogPrint warning $0 ("Downloading certificate failed, trying without.");
+ }
+ }
+
+ :onerror Err {
:local BaseUrl [ $EitherOr ($ScriptInfo->"base-url") $ScriptUpdatesBaseUrl ];
:local UrlSuffix [ $EitherOr ($ScriptInfo->"url-suffix") $ScriptUpdatesUrlSuffix ];
:local Url ($BaseUrl . $ScriptVal->"name" . ".rsc" . $UrlSuffix);
@@ -1198,13 +1264,11 @@
:if ($Result->"status" = "finished") do={
:set SourceNew [ :tolf ($Result->"data") ];
}
- } on-error={
+ } do={
+ $LogPrint warning $0 ("Failed fetching script '" . $ScriptVal->"name" . "': " . $Err);
:if ($ScriptVal->"source" = "#!rsc by RouterOS\n") do={
- $LogPrint warning $0 ("Failed fetching script '" . $ScriptVal->"name" . \
- "', removing dummy. Typo on installation?");
+ $LogPrint warning $0 ("Removing dummy. Typo on installation?");
/system/script/remove $Script;
- } else={
- $LogPrint warning $0 ("Failed fetching script '" . $ScriptVal->"name" . "'!");
}
:error false;
}
@@ -1254,31 +1318,25 @@
$LogPrint info $0 ("Updating script: " . $ScriptVal->"name");
/system/script/set owner=($ScriptVal->"name") \
source=[ $IfThenElse ($ScriptUpdatesCRLF = true) $SourceCRLF $SourceNew ] $Script;
- :if ($ScriptVal->"name" = "global-config") do={
- :set ReloadGlobalConfig true;
- }
- :if ($ScriptVal->"name" = "global-functions" || $ScriptVal->"name" ~ ("^mod/.")) do={
- :set ReloadGlobalFunctions true;
+ :if ($ScriptVal->"name" = "global-config" || \
+ $ScriptVal->"name" = "global-functions" || \
+ $ScriptVal->"name" ~ ("^mod/.")) do={
+ :set ReloadGlobal true;
}
} on-error={ }
}
- :if ($ReloadGlobalFunctions = true) do={
- $LogPrint info $0 ("Reloading global functions.");
- :do {
- /system/script/run global-functions;
- } on-error={
- $LogPrint error $0 ("Reloading global functions failed!");
- }
- }
+ :if ($ReloadGlobal = true) do={
+ $LogPrint info $0 ("Reloading global configuration and functions.");
+ :set GlobalConfigReady false;
+ :set GlobalFunctionsReady false;
+ :delay 1s;
- :if ($ReloadGlobalConfig = true) do={
- $LogPrint info $0 ("Reloading global configuration.");
- :do {
+ :onerror Err {
/system/script/run global-config;
- } on-error={
- $LogPrint error $0 ("Reloading global configuration failed!" . \
- " Syntax error or missing overlay?");
+ /system/script/run global-functions;
+ } do={
+ $LogPrint error $0 ("Reloading global configuration and functions failed! " . $Err);
}
}
@@ -1297,7 +1355,7 @@
:global GlobalConfigMigration;
:local ChangeLogCode;
- :do {
+ :onerror Err {
:local Url ($ScriptUpdatesBaseUrl . "news-and-changes.rsc" . $ScriptUpdatesUrlSuffix);
$LogPrint debug $0 ("Fetching news, changes and migration: " . $Url);
:local Result [ /tool/fetch check-certificate=yes-without-crl \
@@ -1305,16 +1363,16 @@
:if ($Result->"status" = "finished") do={
:set ChangeLogCode ($Result->"data");
}
- } on-error={
- $LogPrint warning $0 ("Failed fetching news, changes and migration!");
+ } do={
+ $LogPrint warning $0 ("Failed fetching news, changes and migration: " . $Err);
}
:if ([ :len $ChangeLogCode ] > 0) do={
:if ([ $ValidateSyntax $ChangeLogCode ] = true) do={
- :do {
+ :onerror Err {
[ :parse $ChangeLogCode ];
- } on-error={
- $LogPrint warning $0 ("The changelog failed to run!");
+ } do={
+ $LogPrint warning $0 ("The changelog failed to run: " . $Err);
}
} else={
$LogPrint warning $0 ("The changelog failed syntax validation!");
@@ -1336,10 +1394,10 @@
}
$LogPrint info $0 ("Applying migration for change " . $I . ": " . $Migration);
- :do {
+ :onerror Err {
[ :parse $Migration ];
- } on-error={
- $LogPrint warning $0 ("Migration code for change " . $I . " failed to run!");
+ } do={
+ $LogPrint warning $0 ("Migration code for change " . $I . " failed to run: " . $Err);
}
} on-error={ }
}
@@ -1381,14 +1439,14 @@
:set GlobalConfigChanges;
:set GlobalConfigMigration;
}
-} on-error={
- :global ExitError; $ExitError false $0;
+} do={
+ :global ExitError; $ExitError false $0 $Err;
} }
# lock script against multiple invocation
:set ScriptLock do={
- :local Script [ :tostr $1 ];
- :local WaitMax ([ :tonum $3 ] * 10);
+ :local Script [ :tostr $1 ];
+ :local WaitMax [ :totime $2 ];
:global GetRandom20CharAlNum;
:global IfThenElse;
@@ -1477,6 +1535,10 @@
:set ($ScriptLockOrder->$Script) ({});
}
+ :if ([ :typeof $WaitMax ] = "nil" ) do={
+ :set WaitMax 0s;
+ }
+
:if ([ :len [ /system/script/find where name=$Script ] ] = 0) do={
$LogPrint error $0 ("A script named '" . $Script . "' does not exist!");
:error false;
@@ -1496,12 +1558,13 @@
:local MyTicket [ $GetRandom20CharAlNum 6 ];
$AddTicket $Script $MyTicket;
- :local WaitCount 0;
- :while ($WaitMax > $WaitCount && \
+ :local WaitInterval ($WaitMax / 20);
+ :local WaitTime $WaitMax;
+ :while ($WaitTime > 0 && \
([ $IsFirstTicket $Script $MyTicket ] = false || \
[ $TicketCount $Script ] < [ $JobCount $Script ])) do={
- :set WaitCount ($WaitCount + 1);
- :delay 100ms;
+ :set WaitTime ($WaitTime - $WaitInterval);
+ :delay $WaitInterval;
}
:if ([ $IsFirstTicket $Script $MyTicket ] = true && \
@@ -1513,17 +1576,17 @@
$RemoveTicket $Script $MyTicket;
$LogPrint debug $0 ("Script '" . $Script . "' started more than once" . \
- [ $IfThenElse ($WaitCount > 0) " and timed out waiting for lock" "" ] . "...");
+ [ $IfThenElse ($WaitTime < $WaitMax) " and timed out waiting for lock" "" ] . "...");
:return false;
}
# send notification via NotificationFunctions - expects at least two string arguments
-:set SendNotification do={ :do {
+:set SendNotification do={ :onerror Err {
:global SendNotification2;
$SendNotification2 ({ origin=$0; subject=$1; message=$2; link=$3; silent=$4 });
-} on-error={
- :global ExitError; $ExitError false $0;
+} do={
+ :global ExitError; $ExitError false $0 $Err;
} }
# send notification via NotificationFunctions - expects one array argument
@@ -1642,9 +1705,12 @@
:set ValidateSyntax do={
:local Code [ :tostr $1 ];
- :do {
+ :global LogPrint;
+
+ :onerror Err {
[ :parse (":local Validate do={\n" . $Code . "\n}") ];
- } on-error={
+ } do={
+ $LogPrint debug $0 ("Valdation failed: " . $Err);
:return false;
}
:return true;
@@ -1711,15 +1777,16 @@
:global MAX;
:set FileName [ $CleanFilePath $FileName ];
- :local I 1;
- :local Delay ([ $MAX [ $EitherOr $WaitTime 2s ] 100ms ] / 10);
+ :local Delay ([ $MAX [ $EitherOr $WaitTime 2s ] 100ms ] / 9);
- :while ([ :len [ /file/find where name=$FileName ] ] = 0) do={
- :if ($I >= 10) do={
- :return false;
- }
- :delay $Delay;
- :set I ($I + 1);
+ :do {
+ :retry {
+ :if ([ :len [ /file/find where name=$FileName ] ] = 0) do={
+ :error false;
+ }
+ } delay=$Delay max=10;
+ } on-error={
+ :return false;
}
:while ([ :len [ /file/find where name=$FileName ] ] > 0) do={
@@ -1758,10 +1825,10 @@
:foreach Script in=[ /system/script/find where name ~ "^mod/." ] do={
:local ScriptVal [ /system/script/get $Script ];
:if ([ $ValidateSyntax ($ScriptVal->"source") ] = true) do={
- :do {
+ :onerror Err {
/system/script/run $Script;
- } on-error={
- $LogPrint error $0 ("Module '" . $ScriptVal->"name" . "' failed to run.");
+ } do={
+ $LogPrint error $0 ("Module '" . $ScriptVal->"name" . "' failed to run: " . $Err);
}
} else={
$LogPrint error $0 ("Module '" . $ScriptVal->"name" . "' failed syntax validation, skipping.");