2 return document
.getElementById(s
.substring(1));
6 $(s
).style
.display
= 'block';
10 $(s
).style
.display
= 'none';
13 function set_server() {
15 data
.url
= $("#server").value
;
16 ubus_call("uci", "set", { "config": "attendedsysupgrade", "section": "server", values
: { "url": data
.url
} })
17 ubus_call("uci", "commit", { "config": "attendedsysupgrade" })
18 var server_button
= $("#server")
19 server_button
.type
= 'button';
20 server_button
.className
= 'cbi-button cbi-button-edit';
21 server_button
.parentElement
.removeChild($("#button_set"));
22 server_button
.onclick
= edit_server
;
25 function edit_server() {
26 $("#server").type
= 'text';
27 $("#server").onkeydown = function(event
) {
28 if(event
.key
=== 'Enter') {
33 $("#server").className
= '';
34 $("#server").onclick
= null;
36 var button_set
= document
.createElement("input");
37 button_set
.type
= "button";
38 button_set
.value
= "Save";
39 button_set
.name
= "button_set";
40 button_set
.id
= "button_set";
41 button_set
.className
= 'cbi-button cbi-button-edit';
42 button_set
.style
= 'background-image: url("/luci-static/resources/cbi/save.gif");'
43 button_set
.onclick
= set_server
44 $("#server").parentElement
.appendChild(button_set
);
47 function edit_packages() {
48 data
.edit_packages
= true
50 $("#edit_packages").value
= data
.packages
.join("\n");
51 show("#edit_packages");
54 // requests to the upgrade server
55 function server_request(request_dict
, path
, callback
) {
56 request_dict
.distro
= data
.release
.distribution
;
57 request_dict
.target
= data
.release
.target
.split("\/")[0];
58 request_dict
.subtarget
= data
.release
.target
.split("\/")[1];
59 var request
= new XMLHttpRequest();
60 request
.open("POST", data
.url
+ "/" + path
, true);
61 request
.setRequestHeader("Content-type", "application/json");
62 request
.send(JSON
.stringify(request_dict
));
63 request
.onerror = function(e
) {
64 error_box("upgrade server down")
67 request
.addEventListener('load', function(event
) {
72 // initial setup, get system information
74 ubus_call("rpc-sys", "packagelist", {}, "packages");
75 ubus_call("system", "board", {}, "release");
76 ubus_call("system", "board", {}, "board_name");
77 ubus_call("system", "board", {}, "model");
78 ubus_call("system", "info", {}, "memory");
79 uci_get({ "config": "attendedsysupgrade", "section": "server", "option": "url" })
80 uci_get({ "config": "attendedsysupgrade", "section": "client", "option": "upgrade_packages" })
81 uci_get({ "config": "attendedsysupgrade", "section": "client", "option": "advanced_mode" })
82 uci_get({ "config": "attendedsysupgrade", "section": "client", "option": "auto_search" })
86 function setup_ready() {
87 // checks if a async ubus calls have finished
88 if(ubus_counter
!= ubus_closed
) {
89 setTimeout(setup_ready
, 300)
91 if(data
.auto_search
== 1) {
94 show("#upgrade_button");
96 $("#server").value
= data
.url
;
101 function uci_get(option
) {
102 // simple wrapper to get a uci value store in data.<option>
103 ubus_call("uci", "get", option
, option
["option"])
108 function ubus_call(command
, argument
, params
, variable
) {
109 var request_data
= {};
110 request_data
.jsonrpc
= "2.0";
111 request_data
.id
= ubus_counter
;
112 request_data
.method
= "call";
113 request_data
.params
= [ data
.ubus_rpc_session
, command
, argument
, params
]
114 var request_json
= JSON
.stringify(request_data
)
116 var request
= new XMLHttpRequest();
117 request
.open("POST", ubus_url
, true);
118 request
.setRequestHeader("Content-type", "application/json");
119 request
.onload = function(event
) {
120 if(request
.status
=== 200) {
121 var response
= JSON
.parse(request
.responseText
)
122 if(!("error" in response
) && "result" in response
) {
123 if(response
.result
.length
=== 2) {
124 if(command
=== "uci") {
125 data
[variable
] = response
.result
[1].value
127 data
[variable
] = response
.result
[1][variable
]
131 error_box("<b>Ubus call failed:</b><br />Request: " + request_json
+ "<br />Response: " + JSON
.stringify(response
))
136 request
.send(request_json
);
139 function info_box(info_output
, loading
) {
140 // Shows notification if upgrade is available
141 // If loading is true then an "processing" animation is added
143 var loading_image
= '';
145 loading_image
= '<img src="/luci-static/resources/icons/loading.gif" alt="Loading" style="vertical-align:middle">';
147 $("#info_box").innerHTML
= loading_image
+ info_output
;
150 function error_box(error_output
) {
151 // Shows erros in red box
153 $("#error_box").innerHTML
= error_output
;
157 function upgrade_check() {
158 // Asks server for new firmware
159 // If data.upgrade_packages is set to true search for new package versions as well
162 info_box("Searching for upgrades", true);
163 var request_dict
= {}
164 request_dict
.version
= data
.release
.version
;
165 request_dict
.packages
= data
.packages
;
166 request_dict
.upgrade_packages
= data
.upgrade_packages
167 server_request(request_dict
, "api/upgrade-check", upgrade_check_callback
)
170 function upgrade_check_callback(request_text
) {
171 var request_json
= JSON
.parse(request_text
)
173 // create simple output to tell user what's going to be upgrade (release/packages)
175 if(request_json
.version
!= undefined) {
176 info_output
+= "<h3>New firmware release available</h3>"
177 info_output
+= data
.release
.version
+ " to " + request_json
.version
178 data
.latest_version
= request_json
.version
;
180 if(request_json
.upgrades
!= undefined) {
181 info_output
+= "<h3>Package upgrades available</h3>"
182 for (var upgrade
in request_json
.upgrades
) {
183 info_output
+= "<b>" + upgrade
+ "</b>: " + request_json
.upgrades
[upgrade
][1] + " to " + request_json
.upgrades
[upgrade
][0] + "<br />"
186 data
.packages
= request_json
.packages
187 info_box(info_output
)
189 if(data
.advanced_mode
== 1) {
190 show("#edit_button");
192 var upgrade_button
= $("#upgrade_button")
193 upgrade_button
.value
= "Request firmware";
194 upgrade_button
.style
.display
= "block";
195 upgrade_button
.disabled
= false;
196 upgrade_button
.onclick
= upgrade_request
;
200 function upgrade_request() {
204 // board_name or model (server tries to find the correct profile)
206 // The rest is added by server_request()
207 $("#upgrade_button").disabled
= true;
208 hide("#edit_packages");
209 hide("#edit_button");
210 hide("#keep_container");
212 var request_dict
= {}
213 request_dict
.version
= data
.latest_version
;
214 request_dict
.board
= data
.board_name
215 request_dict
.model
= data
.model
217 if(data
.edit_packages
== true) {
218 request_dict
.packages
= $("#edit_packages").value
.split("\n")
220 request_dict
.packages
= data
.packages
;
223 server_request(request_dict
, "api/upgrade-request", upgrade_request_callback
)
226 function upgrade_request_callback(request
) {
228 var request_json
= JSON
.parse(request
);
229 data
.sysupgrade_url
= request_json
.sysupgrade
;
231 var filename_split
= data
.sysupgrade_url
.split("/")
232 data
.filename
= filename_split
[filename_split
.length
- 1]
234 var info_output
= 'Firmware created: <a href="' + data
.sysupgrade_url
+ '"><b>' + data
.filename
+ '</b></a>'
235 if(data
.advanced_mode
== 1) {
236 info_output
+= '<br /><a target="_blank" href="' + data
.sysupgrade_url
+ '.log">Build log</a>'
238 info_box(info_output
);
240 show("#keep_container");
241 var upgrade_button
= $("#upgrade_button")
242 upgrade_button
.disabled
= false;
243 upgrade_button
.style
.display
= "block";
244 upgrade_button
.value
= "Flash firmware";
245 upgrade_button
.onclick
= download_image
;
248 function flash_image() {
249 // Flash image via rpc-sys upgrade_start
250 info_box("Flashing firmware. Don't unpower device", true)
251 ubus_call("rpc-sys", "upgrade_start", { "keep": $("#keep").checked
}, 'message');
252 ping_max
= 3600; // in seconds
253 setTimeout(ping_ubus
, 10000)
256 function ping_ubus() {
257 // Tries to connect to ubus. If the connection fails the device is likely still rebooting.
258 // If more time than ping_max passes update may failed
261 var request
= new XMLHttpRequest();
262 request
.open("GET", ubus_url
, true);
263 request
.addEventListener('error', function(event
) {
264 info_box("Rebooting device", true);
265 setTimeout(ping_ubus
, 1000)
267 request
.addEventListener('load', function(event
) {
268 info_box("Success! Please reload web interface");
269 $("#upgrade_button").value
= "Reload page";
270 show("#upgrade_button");
271 $("#upgrade_button").disabled
= false;
272 $("#upgrade_button").onclick = function() { location
.reload(); }
276 error_box("Web interface could not reconnect to your device. Please reload web interface or check device manually")
280 function upload_image(blob
) {
281 // Uploads received blob data to the server using cgi-io
282 var request
= new XMLHttpRequest();
283 var form_data
= new FormData();
285 form_data
.append("sessionid", data
.ubus_rpc_session
)
286 form_data
.append("filename", "/tmp/firmware.bin")
287 form_data
.append("filemode", 755) // insecure?
288 form_data
.append("filedata", blob
)
290 request
.addEventListener('load', function(event
) {
291 request_json
= JSON
.parse(request
.responseText
)
295 request
.addEventListener('error', function(event
) {
296 info_box("Upload of firmware failed, please retry by reloading web interface")
299 request
.open('POST', origin
+ '/cgi-bin/cgi-upload');
300 request
.send(form_data
);
304 function download_image() {
305 // Download image from server once the url was received by upgrade_request
306 hide("#keep_container");
307 hide("#upgrade_button");
308 var download_request
= new XMLHttpRequest();
309 download_request
.open("GET", data
.sysupgrade_url
);
310 download_request
.responseType
= "arraybuffer";
312 download_request
.onload = function () {
313 if (this.status
=== 200) {
314 var blob
= new Blob([download_request
.response
], {type
: "application/octet-stream"});
318 info_box("Downloading firmware", true);
319 download_request
.send();
322 function server_request(request_dict
, path
, callback
) {
324 request_dict
.distro
= data
.release
.distribution
;
325 request_dict
.target
= data
.release
.target
.split("\/")[0];
326 request_dict
.subtarget
= data
.release
.target
.split("\/")[1];
327 var request
= new XMLHttpRequest();
328 request
.open("POST", data
.url
+ "/" + path
, true);
329 request
.setRequestHeader("Content-type", "application/json");
330 request
.send(JSON
.stringify(request_dict
));
331 request
.onerror = function(e
) {
332 error_box("Upgrade server down or could not connect")
335 request
.addEventListener('load', function(event
) {
336 var request_text
= request
.responseText
;
337 if (request
.status
=== 200) {
338 callback(request_text
)
340 } else if (request
.status
=== 202) {
341 var imagebuilder
= request
.getResponseHeader("X-Imagebuilder-Status");
342 if(imagebuilder
=== "queue") {
344 var queue
= request
.getResponseHeader("X-Build-Queue-Position");
345 info_box("In build queue position " + queue
, true)
346 console
.log("queued");
347 } else if(imagebuilder
=== "initialize") {
348 info_box("Setting up ImageBuilder", true)
349 console
.log("Setting up imagebuilder");
350 } else if(imagebuilder
=== "building") {
351 info_box("Building image", true);
352 console
.log("building");
354 // fallback if for some reasons the headers are missing e.g. browser blocks access
355 info_box("Processing request", true);
356 console
.log(imagebuilder
)
358 setTimeout(function() { server_request(request_dict
, path
, callback
) }, 5000)
360 } else if (request
.status
=== 204) {
361 // no upgrades available
362 info_box("No upgrades available")
364 } else if (request
.status
=== 400) {
366 request_json
= JSON
.parse(request_text
)
367 error_box(request_json
.error
)
369 } else if (request
.status
=== 412) {
370 // this is a bit generic
371 error_box("Unsupported device, release, target, subtraget or board")
373 } else if (request
.status
=== 413) {
374 error_box("No firmware created due to image size. Try again with less packages selected.")
376 } else if (request
.status
=== 422) {
377 var package_missing
= request
.getResponseHeader("X-Unknown-Package");
378 error_box("Unknown package in request: <b>" + package_missing
+ "</b>")
379 } else if (request
.status
=== 500) {
380 request_json
= JSON
.parse(request_text
)
382 var error_box_content
= "<b>Internal server error</b><br />"
383 error_box_content
+= request_json
.error
384 if(request_json
.log
!= undefined) {
385 data
.log_url
= request_json
.log
387 error_box(error_box_content
)
389 } else if (request
.status
=== 501) {
390 error_box("No sysupgrade file produced, may not supported by model.")
392 } else if (request
.status
=== 502) {
393 // python part offline
394 error_box("Server down for maintenance")
395 setTimeout(function() { server_request(request_dict
, path
, callback
) }, 30000)
396 } else if (request
.status
=== 503) {
397 error_box("Server overloaded")
398 setTimeout(function() { server_request(request_dict
, path
, callback
) }, 30000)
402 document
.onload
= setup()