aboutsummaryrefslogtreecommitdiffstats
path: root/packages-update.rsc
blob: 97039ecbe001f9734e1bba139569c7aaacbfad8c (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
#!rsc by RouterOS
# RouterOS script: packages-update
# Copyright (c) 2019-2024 Christian Hesse <mail@eworm.de>
# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md
#
# requires RouterOS, version=7.13
#
# download packages and reboot for installation
# https://git.eworm.de/cgit/routeros-scripts/about/doc/packages-update.md

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

:do {
  :local ScriptName [ :jobname ];

  :global DownloadPackage;
  :global Grep;
  :global LogPrint;
  :global ParseKeyValueStore;
  :global ScriptFromTerminal;
  :global ScriptLock;
  :global VersionToNum;

  :global PackagesUpdateDeferReboot;
  :global PackagesUpdateBackupFailure;

  :local Schedule do={
    :local ScriptName [ :tostr $1 ];

    :global GetRandomNumber;
    :global LogPrint;

    :global RebootForUpdate do={
      /system/reboot;
    }

    :local StartTime [ :tostr [ :totime (10800 + [ $GetRandomNumber 7200 ]) ] ];
    /system/scheduler/add name="_RebootForUpdate" start-time=$StartTime interval=1d \
        on-event=("/system/scheduler/remove \"_RebootForUpdate\"; " . \
        ":global RebootForUpdate; \$RebootForUpdate;");
    $LogPrint info $ScriptName ("Scheduled reboot for update at " . $StartTime . \
        " local time (" . [ /system/clock/get time-zone-name ] . ").");
    :return true;
  }

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

  :local Update [ /system/package/update/get ];

  :if ([ :typeof ($Update->"latest-version") ] = "nothing") do={
    $LogPrint warning $ScriptName ("Latest version is not known.");
    :error false;
  }

  :if ($Update->"installed-version" = $Update->"latest-version") do={
    $LogPrint info $ScriptName ("Version " . $Update->"latest-version" . " is already installed.");
    :error true;
  }

  :local RunOrder ({});
  :foreach Script in=[ /system/script/find where source~("\n# provides: backup-script\\b") ] do={
    :local ScriptVal [ /system/script/get $Script ];
    :local Store [ $ParseKeyValueStore [ $Grep ($ScriptVal->"source") ("\23 provides: backup-script, ") ] ];

    :set ($RunOrder->($Store->"order" . "-" . $ScriptVal->"name")) ($ScriptVal->"name");
  }

  :foreach Order,Script in=$RunOrder do={
    :set PackagesUpdateBackupFailure false;
    :do {
      $LogPrint info $ScriptName ("Running backup script " . $Script . " before update.");
      /system/script/run $Script;
    } on-error={
      :set PackagesUpdateBackupFailure true;
    }

    :if ($PackagesUpdateBackupFailure = true) do={
      $LogPrint warning $ScriptName ("Running backup script " . $Script . " before update failed!");
      :if ([ $ScriptFromTerminal $ScriptName ] = true) do={
        :put "Do you want to continue anyway? [y/N]";
        :if (([ /terminal/inkey timeout=60 ] % 32) = 25) do={
          $LogPrint info $ScriptName ("User requested to continue anyway.");
        } else={
          $LogPrint info $ScriptName ("Canceled update...");
          :error false;
        }
      } else={
        $LogPrint warning $ScriptName ("Canceled non-interactive update.");
        :error false;
      }
    }
  }

  :local NumInstalled [ $VersionToNum ($Update->"installed-version") ];
  :local NumLatest [ $VersionToNum ($Update->"latest-version") ];

  :local DoDowngrade false;
  :if ($NumInstalled > $NumLatest) do={
    :if ([ $ScriptFromTerminal $ScriptName ] = true) do={
      :put "Latest version is older than installed one. Want to downgrade? [y/N]";
      :if (([ /terminal/inkey timeout=60 ] % 32) = 25) do={
        :set DoDowngrade true;
      } else={
        :put "Canceled...";
      }
    } else={
      $LogPrint warning $ScriptName ("Not installing downgrade automatically.");
      :error false;
    }
  }

  :foreach Package in=[ /system/package/find where !bundle ] do={
    :local PkgName [ /system/package/get $Package name ];
    :if ([ $DownloadPackage $PkgName ($Update->"latest-version") ] = false) do={
      $LogPrint error $ScriptName ("Download for package " . $PkgName . " failed, update aborted.");
      :error false;
    }
  }

  :if ($DoDowngrade = true) do={
    $LogPrint info $ScriptName ("Rebooting for downgrade.");
    :delay 1s;
    /system/package/downgrade;
  }

  :if ([ $ScriptFromTerminal $ScriptName ] = true) do={
    :put "Do you want to (s)chedule reboot or (r)eboot now? [s/R]";
    :if (([ /terminal/inkey timeout=60 ] % 32) = 19) do={
      $Schedule $ScriptName;
      :error true;
    }
  } else={
    :if ($PackagesUpdateDeferReboot = true) do={
      $Schedule $ScriptName;
      :error true;
    }
  }

  $LogPrint info $ScriptName ("Rebooting for update.");
  :delay 1s;
  /system/reboot;
} on-error={ }