mirror of
				https://github.com/eworm-de/routeros-scripts.git
				synced 2024-05-11 05:55:19 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			262 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			Plaintext
		
	
	
	
	
	
			
		
		
	
	
			262 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			Plaintext
		
	
	
	
	
	
#!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
 | 
						|
#
 | 
						|
# 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 95;
 | 
						|
 | 
						|
# global functions
 | 
						|
:global RouterOSScriptsRecover;
 | 
						|
:global ScriptInstallUpdate;
 | 
						|
 | 
						|
# recover from failed migration
 | 
						|
:set RouterOSScriptsRecover do={
 | 
						|
  :global ScriptInstallUpdate;
 | 
						|
 | 
						|
  :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;
 | 
						|
  }
 | 
						|
 | 
						|
  $ScriptInstallUpdate;
 | 
						|
}
 | 
						|
 | 
						|
# 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") ];
 | 
						|
    :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;
 | 
						|
  }
 | 
						|
}
 |