aboutsummaryrefslogtreecommitdiffstats
path: root/check-routeros-update.rsc
blob: 06248088e2b60438fbfc0abe9e3f674fdce9c68b (about) (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
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
#!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.14
#
# 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 FetchUserAgentStr;
  :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 (($Update->"installed-version") = ($Update->"latest-version")) do={
    :if ([ $ScriptFromTerminal $ScriptName ] = true) do={
      $LogPrint info $ScriptName ("System is already up to date.");
    }
    :error true;
  }

  :if ([ :len ($Update->"latest-version") ] = 0) do={
    $LogPrint info $ScriptName ("Received an empty version string from server.");
    :error false;
  }

  :local NumInstalled [ $VersionToNum ($Update->"installed-version") ];
  :local NumLatest [ $VersionToNum ($Update->"latest-version") ];
  :local BitMask [ $VersionToNum "255.255zero0" ];
  :local NumInstalledFeature ($NumInstalled & $BitMask);
  :local NumLatestFeature ($NumLatest & $BitMask);
  :local Link ("https://mikrotik.com/download/changelogs/" . $Update->"channel" . "-release-tree");

  :if ($NumLatest < [ $VersionToNum "7.0" ]) do={
    $LogPrint warning $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 && $NumInstalledFeature = $NumLatestFeature) 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=({ [ $FetchUserAgentStr $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={
      :if (($Update->"channel") = "testing" && $NumInstalledFeature < $NumLatestFeature) do={
        :put ("This is a feature update in testing channel. Switch to channel 'stable'? [y/N]");
        :if (([ /terminal/inkey timeout=60 ] % 32) = 25) do={
          /system/package/update/set channel=stable;
          $LogPrint info $ScriptName ("Switched to channel 'stable', please re-run!");
          :error true;
        }
      }

      :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={ }