2 -- all lua code provided by https://github.com/jow-/
3 -- thank you very much!
5 function apply_acls(filename, session)
6 local json = require
"luci.jsonc"
7 local util = require
"luci.util"
8 local fs = require
"nixio.fs"
12 local acl = json.parse(fs.readfile(filename))
13 if type(acl) ~=
"table" then
18 for group, perms in pairs(acl) do
20 for perm, scopes in pairs(perms) do
21 if type(scopes) ==
"table" then
23 for scope, objects in pairs(scopes) do
24 if type(objects) ==
"table" then
25 if not grants[scope] then
29 if next(objects) ==
1 then
31 for _, object in ipairs(objects) do
32 if not grants[scope][object] then
33 grants[scope][object] = { }
35 table.insert(grants[scope][object], perm)
39 for object, funcs in pairs(objects) do
40 if type(funcs) ==
"table" then
42 for _, func in ipairs(funcs) do
43 if not grants[scope][object] then
44 grants[scope][object] = { }
46 table.insert(grants[scope][object], func)
57 local _, scope, object, func
58 for scope, _ in pairs(grants) do
60 for object, _ in pairs(_) do
61 for _, func in ipairs(_) do
62 table.insert(objects, { object, func })
66 util.ubus(
"session",
"grant", {
67 ubus_rpc_session = session,
68 scope = scope, objects = objects
73 apply_acls(
"/usr/share/rpcd/acl.d/attendedsysupgrade.json", luci.dispatcher.context.authsession)
74 apply_acls(
"/usr/share/rpcd/acl.d/packagelist.json", luci.dispatcher.context.authsession)
77 <h2 name=
"content"><%:Attended Sysupgrade%
></h2>
78 <div class=
"container">
79 <div style=
"display: none" id=
"update_info" class=
"alert-message info"></div>
80 <div style=
"display: none" id=
"update_error" class=
"alert-message danger"></div>
82 <input class=
"cbi-button" value=
"search for updates" onclick=
"update_request()" type=
"button" id=
"update_button">
83 <div style=
"display: none" id=
"packages" class=
"alert-message success"></div>
84 <div class=
"cbi-value" id=
"update_packages_container" style=
"display: block">
85 <label class=
"cbi-value-title" for=
"keep">search for package updates:
</label>
86 <div class=
"cbi-value-field">
87 <input type=
"checkbox" name=
"update_packages" id=
"update_packages" />
90 <div class=
"cbi-value" id=
"keep_container" style=
"display: none">
91 <label class=
"cbi-value-title" for=
"keep">keep settings:
</label>
92 <div class=
"cbi-value-field">
93 <input type=
"checkbox" name=
"keep" id=
"keep" checked=
"checked" />
97 <script type=
"text/javascript">
102 origin = document.location.href.replace(location.pathname,
"")
103 ubus_url = origin +
"/ubus/"
105 // requests to the update server
106 function server_request(request_dict, path, callback) {
107 url = data.update_server +
"/" + path
108 request_dict.distro = data.release.distribution;
109 request_dict.target = data.release.target.split(
"\/")[
0];
110 request_dict.subtarget = data.release.target.split(
"\/")[
1];
111 var xmlhttp = new XMLHttpRequest();
112 xmlhttp.open(
"POST", url, true);
113 xmlhttp.setRequestHeader(
"Content-type",
"application/json");
114 xmlhttp.send(JSON.stringify(request_dict));
115 xmlhttp.onerror = function(e) {
116 update_error(
"update server down")
118 xmlhttp.addEventListener('load', function(event) {
123 // requests ubus via rpcd
124 function ubus_request(command, argument, params, callback) {
126 request_data.jsonrpc =
"2.0";
127 request_data.id = ubus_counter;
128 request_data.method =
"call";
129 request_data.params = [ data.ubus_rpc_session, command, argument, params ]
131 var xmlhttp = new XMLHttpRequest();
132 xmlhttp.open(
"POST", ubus_url, true);
133 xmlhttp.setRequestHeader(
"Content-type",
"application/json");
134 xmlhttp.onerror = function(e) {
135 setTimeout(back_online,
5000)
137 xmlhttp.addEventListener('load', function(event) {
138 if(command ===
"uci") {
139 ubus_request_callback_uci(xmlhttp, callback)
141 ubus_request_callback(xmlhttp, callback)
144 xmlhttp.send(JSON.stringify(request_data));
147 // handle ubus_requests, set variables or perform functions
148 function ubus_request_callback(response_object, callback) {
149 if(response_object.status ===
200) {
150 console.log(callback)
151 if(typeof callback ===
"string") {
152 response_json = JSON.parse(response_object.responseText).result[
1]
153 if (callback ==
"release") {
154 latest_version = response_json.release.version
156 data[callback] = response_json[callback]
158 callback(response_object)
161 console.log(respons_object.responseText)
165 function ubus_request_callback_uci(response_object, callback) {
166 if(response_object.status ===
200) {
167 console.log(callback)
168 response_json = JSON.parse(response_object.responseText).result[
1].value
169 data[callback] = response_json
171 document.getElementById(
"update_packages").checked = data.update_packages;
173 console.log(respons_object.responseText)
177 // initial setup, get system information
179 data[
"ubus_rpc_session"] =
"<%=luci.dispatcher.context.authsession%>"
180 ubus_request(
"packagelist",
"list", {},
"packagelist");
181 ubus_request(
"system",
"board", {},
"release");
182 ubus_request(
"system",
"board", {},
"board_name");
183 ubus_request(
"system",
"board", {},
"model");
184 ubus_request(
"uci",
"get", {
"config":
"attendedsysupgrade",
"section":
"updateserver",
"option":
"url" },
"update_server")
185 ubus_request(
"uci",
"get", {
"config":
"attendedsysupgrade",
"section":
"updateclient",
"option":
"update_packages" },
"update_packages")
188 // shows notification if update is available
189 function update_info(info_output) {
190 document.getElementById(
"update_info").style.display =
"block";
191 document.getElementById(
"update_info").innerHTML = info_output;
194 function update_error(error_output) {
195 document.getElementById(
"update_error").style.display =
"block";
196 document.getElementById(
"update_error").innerHTML = error_output;
197 document.getElementById(
"update_info").style.display =
"none";
200 // asks server for news updates, actually only based on relesae not packages
201 function update_request() {
202 console.log(
"update_request")
204 request_dict.version = data.release.version;
205 request_dict.packages = data.packagelist;
206 if (document.getElementById(
"update_packages").checked ==
1) {
207 request_dict.update_packages =
1
209 server_request(request_dict,
"update-request", update_request_callback)
212 function update_request_callback(response_object) {
213 if (response_object.status ===
500) {
215 update_error(
"internal server error, please try again later")
216 console.log(
"update server issue")
217 } else if (response_object.status ===
502) {
218 // python part offline
219 update_error(
"internal server error, please try again later")
220 console.log(
"update server issue")
221 } else if (response_object.status ===
503) {
223 update_error(
"server overloaded, retry in 5 minutes")
224 console.log(
"server overloaded")
225 setTimeout(update_request,
300000)
226 } else if (response_object.status ===
201) {
227 update_info(
"imagebuilder not ready, please wait")
228 console.log(
"setting up imagebuilder")
229 setTimeout(update_request,
5000)
230 } else if (response_object.status ===
204) {
232 update_info(
"no updates available")
233 } else if (response_object.status ===
400) {
235 console.log(response_object.responseText)
236 response_object_content = JSON.parse(response_object.responseText)
237 update_error(response_object_content.error)
238 } else if (response_object.status ===
200) {
239 // new release/updates
240 response_object_content = JSON.parse(response_object.responseText)
241 update_request_200(response_object_content)
245 function back_online() {
246 ubus_request(
"session",
"login", {}, back_online_callback)
249 function back_online_callback(response_object) {
250 if (response_object.status !=
200) {
251 setTimeout(back_online,
5000)
253 update_info(
"upgrade successfull!")
254 document.getElementById(
"update_button").value =
"reload page";
255 document.getElementById(
"update_button").onclick = function() { location.reload(); }
260 function update_request_200(response_content) {
262 if(response_content.version != undefined) {
263 info_output +=
"<h3>new update available</h3>"
264 info_output += data.release.version +
" to " + response_content.version
265 latest_version = response_content.version;
267 if(response_content.updates != undefined) {
268 info_output +=
"<h3>package updates available</h3>"
269 for (update in response_content.updates) {
270 info_output +=
"<b>" + update +
"</b>: " + response_content.updates[update][
1] +
" to " + response_content.updates[update][
0] +
"</br>"
273 data.packages = response_content.packages
274 update_info(info_output)
275 document.getElementById(
"update_button").value =
"request image";
276 document.getElementById(
"update_packages_container").style.display =
"none";
277 document.getElementById(
"update_button").onclick = image_request;
280 // request the image, need merge with update_request
281 function image_request() {
282 console.log(
"image_request")
283 document.getElementById(
"update_packages_container").style.display =
"none";
285 request_dict.version = latest_version;
286 request_dict.board = data.board_name
287 request_dict.packages = data.packages;
288 request_dict.model = data.model
289 server_request(request_dict,
"image-request", image_request_handler)
292 function image_request_handler(response) {
293 if (response.status ===
400) {
294 response_content = JSON.parse(response.responseText)
295 update_error(response_content.error)
296 } else if (response.status ===
500) {
298 } else if (response.status ===
503) {
299 update_error(
"please wait. server overloaded")
301 setTimeout(image_request,
30000)
302 } else if (response.status ===
201) {
303 response_content = JSON.parse(response.responseText)
304 if(response_content.queue != undefined) {
306 update_info(
"please wait. you are in queue position " + response_content.queue)
307 console.log(
"queued")
309 update_info(
"imagebuilder not ready, please wait")
310 console.log(
"setting up imagebuilder")
312 setTimeout(image_request,
5000)
313 } else if (response.status ===
206) {
315 console.log(
"building")
316 update_info(
"building image")
317 setTimeout(image_request,
5000)
318 } else if (response.status ===
200) {
320 response_content = JSON.parse(response.responseText)
321 update_info(
"image created")
322 document.getElementById(
"update_button").value =
"sysupgrade"
323 document.getElementById(
"update_button").onclick = function() {download_image(response_content.url); }
324 document.getElementById(
"keep_container").style.display =
"block";
329 // uploads received blob data to the server using cgi-io
330 function upload_image(blob) {
331 var upload_request = new XMLHttpRequest();
332 var form_data = new FormData();
334 form_data.append(
"sessionid", data.ubus_rpc_session)
335 form_data.append(
"filename",
"/tmp/sysupgrade.bin")
336 form_data.append(
"filemode",
755) // insecure?
337 form_data.append(
"filedata", blob)
339 upload_request.addEventListener('load', function(event) {
340 // this checksum should be parsed
341 document.getElementById(
"update_info").innerHTML =
"flashing... please wait" // show fancy indicator http://www.ajaxload.info/
343 ubus_request(
"attendedsysupgrade",
"sysupgrade", {
"keep_settings": document.getElementById(
"keep").checked }, 'done');
346 upload_request.addEventListener('error', function(event) {
347 document.getElementById(
"update_info").innerHTML =
"uploading failed, please retry"
350 upload_request.open('POST', origin + '/cgi-bin/cgi-upload');
351 upload_request.send(form_data);
354 // download image from server once the url was received by image_request
355 function download_image(url) {
356 console.log(
"download_image")
357 document.getElementById(
"update_button").value =
"flashing..."
358 document.getElementById(
"update_button").disabled = true;
359 var download_request = new XMLHttpRequest();
360 download_request.open(
"GET", url);
361 download_request.responseType =
"arraybuffer";
363 download_request.onload = function () {
364 if (this.status ===
200) {
365 var blob = new Blob([download_request.response], {type:
"application/octet-stream"});
369 document.getElementById(
"update_info").innerHTML =
"downloading image"
370 download_request.send();
373 document.onload = setup()