aboutsummaryrefslogtreecommitdiffstats
path: root/check-routeros-update.rsc
blob: fde7cb51ce1fa1dd338a0780521c0636cc61947e (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
#!rsc by RouterOS
# RouterOS script: check-routeros-update
# Copyright (c) 2013-2024 Christian Hesse <mail@eworm.de>
# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md
#
# requires RouterOS, version=7.12
#
# check for RouterOS update, send notification and/or install
# https://git.eworm.de/cgit/routeros-scripts/about/doc/check-routeros-update.md

:global GlobalFunctionsReady;
:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }

:do {
  :local ScriptName [ :jobname ];

  :global Identity;
  :global SafeUpdateAll;
  :global SafeUpdateNeighbor;
  :global SafeUpdateNeighborIdentity;
  :global SafeUpdatePatch;
  :global SafeUpdateUrl;
  :global SentRouterosUpdateNotification;

  :global DeviceInfo;
  :global EscapeForRegEx;
  :global FetchUserAgent;
  :global LogPrint;
  :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.";
  }

  :if ([ $ScriptLock $ScriptName ] = false) do={
    :error false;
  }
  $WaitFullyConnected;

  :if ([ :len [ /system/scheduler/find where name="_RebootForUpdate" ] ] > 0) do={
    :error "A reboot for update is already scheduled.";
  }

  $LogPrint debug $ScriptName ("Checking for updates...");
  /system/package/update/check-for-updates without-paging as-value;
  :local Update [ /system/package/update/get ];

  :if ([ $ScriptFromTerminal $ScriptName ] = true && ($Update->"installed-version") = ($Update->"latest-version")) do={
    $LogPrint info $ScriptName ("System is already up to date.");
    :error 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={
    $LogPrint info $ScriptName ("The version '" . ($Update->"latest-version") . "' is not a valid version.");
    :error false;
  }

  :if ($NumInstalled < $NumLatest) do={
    :if ($SafeUpdateAll ~ "^YES,? ?PLEASE!?\$") do={
      $LogPrint info $ScriptName ("Installing ALL versions automatically, including " . \
        $Update->"latest-version" . "...");
      $SendNotification2 ({ origin=$ScriptName; \
        subject=([ $SymbolForNotification "sparkles" ] . "RouterOS update: " . $Update->"latest-version"); \
        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={
      $LogPrint info $ScriptName ("Version " . $Update->"latest-version" . " is a patch release, updating...");
      $SendNotification2 ({ origin=$ScriptName; \
        subject=([ $SymbolForNotification "sparkles" ] . "RouterOS update: " . $Update->"latest-version"); \
        message=("Version " . $Update->"latest-version" . " is a patch update for " . $Update->"channel" . \
          ", updating on " . $Identity . "..."); link=$Link; silent=true });
      $DoUpdate;
    }

    :if ($SafeUpdateNeighbor = true) do={
      :local Neighbors [ /ip/neighbor/find where platform="MikroTik" identity~$SafeUpdateNeighborIdentity \
         version~("^" . [ $EscapeForRegEx ($Update->"latest-version") ] . "\\b") ];
      :if ([ :len $Neighbors ] > 0) do={
        :local Neighbor [ /ip/neighbor/get ($Neighbors->0) identity ];
        $LogPrint info $ScriptName ("Seen a neighbor (" . $Neighbor . ") running version " . \
          $Update->"latest-version" . " from " . $Update->"channel" . ", updating...");
        $SendNotification2 ({ origin=$ScriptName; \
          subject=([ $SymbolForNotification "sparkles" ] . "RouterOS update: " . $Update->"latest-version"); \
          message=("Seen a neighbor (" . $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") http-header-field=({ [ $FetchUserAgent $ScriptName ] }) \
            output=user as-value ];
      } on-error={
        $LogPrint warning $ScriptName ("Failed receiving safe version for " . $Update->"channel" . ".");
      }
      :if ($Result->"status" = "finished" && $Result->"data" = $Update->"latest-version") do={
        $LogPrint info $ScriptName ("Version " . $Update->"latest-version" . " is considered safe, updating...");
        $SendNotification2 ({ origin=$ScriptName; \
          subject=([ $SymbolForNotification "sparkles" ] . "RouterOS update: " . $Update->"latest-version"); \
          message=("Version " . $Update->"latest-version" . " is considered safe for " . $Update->"channel" . \
            ", updating on " . $Identity . "..."); link=$Link; silent=true });
        $DoUpdate;
      }
    }

    :if ([ $ScriptFromTerminal $ScriptName ] = 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={
      $LogPrint info $ScriptName ("Already sent the RouterOS update notification for version " . \
          $Update->"latest-version" . ".");
      :error true;
    }

    $SendNotification2 ({ origin=$ScriptName; \
      subject=([ $SymbolForNotification "sparkles" ] . "RouterOS update: " . $Update->"latest-version"); \
      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={
      $LogPrint info $ScriptName ("Already sent the RouterOS downgrade notification for version " . \
          $Update->"latest-version" . ".");
      :error true;
    }

    $SendNotification2 ({ origin=$ScriptName; \
      subject=([ $SymbolForNotification "warning-sign" ] . "RouterOS version: " . $Update->"latest-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 });
    $LogPrint info $ScriptName ("A different RouterOS version " . ($Update->"latest-version") . \
      " is available for downgrade.");
    :set SentRouterosUpdateNotification ($Update->"latest-version");
  }
} on-error={ }