working version

This commit is contained in:
Jan Schneider 2017-03-26 00:26:39 +01:00
parent 63580ae33f
commit 0f3cc9b506
4 changed files with 162 additions and 115 deletions

View File

@ -24,6 +24,9 @@ namespace eval rmupdate {
variable mnt_new "/usr/local/addons/rmupdate/var/mnt_new"
variable sys_dev "/dev/mmcblk0"
variable loop_dev "/dev/loop7"
variable install_log "/usr/local/addons/rmupdate/var/install.log"
variable install_lock "/usr/local/addons/rmupdate/var/install.lock"
variable log_file ""
}
proc ::rmupdate::compare_versions {a b} {
@ -31,10 +34,21 @@ proc ::rmupdate::compare_versions {a b} {
}
proc ::rmupdate::write_log {str} {
variable log_file
puts stderr $str
#set fd [open "/tmp/rmupdate.log" "a"]
#puts $fd $str
#close $fd
if {$log_file != ""} {
set fd [open $log_file "a"]
puts $fd $str
close $fd
}
}
proc ::rmupdate::read_install_log {} {
variable install_log
set fp [open $install_log r]
set data [read $fp]
close $fp
return $data
}
proc ::rmupdate::version {} {
@ -46,7 +60,7 @@ proc ::rmupdate::version {} {
}
proc ::rmupdate::get_partion_start_and_size {device partition} {
set data [exec parted $device unit B print]
set data [exec /usr/sbin/parted $device unit B print]
foreach d [split $data "\n"] {
regexp {^\s*(\d)\s+(\d+)B\s+(\d+)B\s+(\d+)B.*} $d match num start end size
if { [info exists num] && $num == $partition } {
@ -62,13 +76,14 @@ proc ::rmupdate::mount_image_partition {image partition mountpoint} {
write_log "Mounting parition ${partition} of image ${image}."
set p [get_partion_start_and_size $sys_dev $partition]
set p [get_partion_start_and_size $image $partition]
#write_log "Partiton start=[lindex $p 0], size=[lindex $p 1]."
file mkdir $mountpoint
catch {exec umount "${mountpoint}"}
catch {exec losetup -d $loop_dev}
exec losetup -o [lindex $p 0] $loop_dev "${image}"
exec mount $loop_dev -o ro "${mountpoint}"
catch {exec /bin/umount "${mountpoint}"}
catch {exec /sbin/losetup -d $loop_dev}
exec /sbin/losetup -o [lindex $p 0] $loop_dev "${image}"
exec /bin/mount $loop_dev -o ro "${mountpoint}"
}
proc ::rmupdate::mount_system_partition {partition_or_filesystem mountpoint} {
@ -83,13 +98,13 @@ proc ::rmupdate::mount_system_partition {partition_or_filesystem mountpoint} {
write_log "Remounting filesystem ${partition_or_filesystem} (rw)."
file mkdir $mountpoint
catch {exec umount "${mountpoint}"}
exec mount -o bind $partition_or_filesystem "${mountpoint}"
exec mount -o remount,rw "${mountpoint}"
catch {exec /bin/umount "${mountpoint}"}
exec /bin/mount -o bind $partition_or_filesystem "${mountpoint}"
exec /bin/mount -o remount,rw "${mountpoint}"
}
proc ::rmupdate::umount {device_or_mountpoint} {
exec umount "${device_or_mountpoint}"
exec /bin/umount "${device_or_mountpoint}"
}
proc ::rmupdate::get_filesystem_size_and_usage {device_or_mountpoint} {
@ -128,11 +143,11 @@ proc ::rmupdate::check_sizes {image} {
umount $mnt_new
umount $mnt_cur
# Minimum free space 100 MB
if { [expr {$new_used+100*1024*1024}] >= $cur_size } {
if { [expr {$new_used*1.5}] > $cur_size && [expr {$new_used+50*1024*1024}] >= $cur_size } {
error "Current filesystem of partition $partition (${cur_size} bytes) not big enough (new usage: ${new_used} bytes)."
}
}
write_log "Sizes of filesystems checked successfully."
}
proc ::rmupdate::update_filesystems {image} {
@ -150,8 +165,9 @@ proc ::rmupdate::update_filesystems {image} {
mount_image_partition $image $partition $mnt_new
mount_system_partition $partition $mnt_cur
write_log "Rsyncing filesystem."
write_log "Rsyncing filesystem of partition ${partition}."
set data [exec rsync --progress --archive --delete "${mnt_new}/" "${mnt_cur}"]
write_log "Rsync finished."
umount $mnt_new
umount $mnt_cur
@ -204,6 +220,7 @@ proc ::rmupdate::get_latest_firmware_version {} {
proc ::rmupdate::download_firmware {version} {
variable img_dir
variable log_file
set image_file "${img_dir}/RaspberryMatic-${version}.img"
set download_url ""
foreach e [get_available_firmware_downloads] {
@ -220,7 +237,13 @@ proc ::rmupdate::download_firmware {version} {
regexp {/([^/]+)$} $download_url match archive_file
set archive_file "${img_dir}/${archive_file}"
file mkdir $img_dir
exec wget "${download_url}" --no-check-certificate -q --output-document=$archive_file
if {$log_file != ""} {
exec wget "${download_url}" --show-progress --progress=dot:giga --no-check-certificate --quiet --output-document=$archive_file 2>>${log_file}
write_log ""
} else {
exec wget "${download_url}" --no-check-certificate --quiet --output-document=$archive_file
}
write_log "Download completed."
write_log "Extracting firmware ${archive_file}."
set data [exec unzip -ql "${archive_file}"]
@ -295,18 +318,57 @@ proc ::rmupdate::get_firmware_info {} {
return $json
}
proc ::rmupdate::install_firmware_version {version} {
set firmware_image ""
#foreach e [get_available_firmware_images] {
# set v [get_version_from_filename $e]
# if {$v == $version} {
# set firmware_image $e
# break
# }
#}
if {$firmware_image == ""} {
download_firmware $version
proc ::rmupdate::install_process_running {} {
variable install_lock
if {! [file exists $install_lock]} {
return 0
}
set fp [open $install_lock "r"]
set lpid [string trim [read $fp]]
close $fp
if {[file exists "/proc/${lpid}"]} {
return 1
}
file delete $install_lock
return 0
}
proc ::rmupdate::install_firmware_version {version} {
if {[rmupdate::install_process_running]} {
error "Another install process is running."
}
variable install_lock
variable log_file
variable install_log
set fd [open $install_lock "w"]
puts $fd [pid]
close $fd
file delete $install_log
set log_file $install_log
set firmware_image ""
foreach e [get_available_firmware_images] {
set v [get_version_from_filename $e]
if {$v == $version} {
set firmware_image $e
break
}
}
if {$firmware_image == ""} {
set firmware_image [download_firmware $version]
}
check_sizes $firmware_image
update_filesystems $firmware_image
file delete $install_lock
}
#puts [rmupdate::get_latest_firmware_version]

View File

@ -39,11 +39,11 @@ proc main {} {
if {$cmd == "show_current"} {
puts [rmupdate::get_current_firmware_version]
} elsif {$cmd == "show_latest"} {
} elseif {$cmd == "show_latest"} {
puts [rmupdate::get_latest_firmware_version]
} elsif {$cmd == "install_latest"} {
} elseif {$cmd == "install_latest"} {
rmupdate::install_firmware_version [rmupdate::get_latest_firmware_version]
} elsif {$cmd == "install"} {
} elseif {$cmd == "install"} {
if {$argc < 2} {
usage
exit 1

View File

@ -31,13 +31,14 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
<title>RaspberryMatic Update Addon</title>
<script>
var message_timer_id = null;
var install_running = false;
function display_message(type, html, millis) {
clear_message();
$('#message').html(html);
$('#message').attr('class', 'ui ' + type + ' message visible');
$('#message-edit-bridge').html(html);
$('#message-edit-bridge').attr('class', 'ui ' + type + ' message visible');
$('#message-log').html(html);
$('#message-log').attr('class', 'ui ' + type + ' message visible');
message_timer_id = setTimeout(clear_message, millis);
}
@ -48,25 +49,27 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
message_timer_id = null;
$('#message').html('');
$('#message').attr('class', 'ui message hidden');
$('#message-edit-bridge').html('');
$('#message-edit-bridge').attr('class', 'ui message hidden');
$('#message-log').html('');
$('#message-log').attr('class', 'ui message hidden');
}
function default_error_callback(xhr, ajaxOptions, thrownError) {
console.error(xhr);
err = thrownError;
try {
obj = JSON.parse(xhr.responseText);
if (obj.error != null) {
err = obj.error;
}
}
catch(e) {
}
display_message('error', 'An error occurred: ' + err, 6000000);
}
function rest(method, path, data, success_callback, error_callback) {
if (!error_callback) {
error_callback = function(xhr, ajaxOptions, thrownError) {
console.error(xhr);
err = thrownError;
try {
obj = JSON.parse(xhr.responseText);
if (obj.error != null) {
err = obj.error;
}
}
catch(e) {
}
display_message('error', 'An error occurred: ' + err, 60000);
}
error_callback = default_error_callback
}
$.ajax({
url: "rest.cgi?" + path,
@ -78,49 +81,35 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
});
}
function install_firmware(version) {
$('#modal-log').modal('show');
//$('#log-content').append('Some text<br/>');
//$('#modal-log').modal('refresh');
//$('#modal-log').modal('hide');
var xhr = new XMLHttpRequest();
xhr.open('GET', 'rest.cgi?/install_firmware', true);
xhr.send(null);
var timer;
timer = window.setInterval(function() {
if (xhr.readyState == XMLHttpRequest.DONE) {
window.clearTimeout(timer);
//$('body').append('done <br />');
function update_install_log() {
rest("GET", "/read_install_log", null, function(data) {
$('#log-content').html(data.replace(/\n/g, '<br />'));
$('#modal-log').modal('refresh');
$('#log-content').scrollTop($('#log-content').prop("scrollHeight"));
if (install_running) {
setTimeout(update_install_log, 1000);
}
//$('body').append('state: ' + xhr.readyState + '<br />');
console.log(xhr.responseText);
$('#log-content').append(xhr.responseText + '<br/>');
//$('body').append('data: ' + xhr.responseText + '<br />');
}, 1000);
/*
var last_response_len = false;
$.ajax('rest.cgi?/install_firmware', {
xhrFields: {
onprogress: function(e) {
var this_response, response = e.currentTarget.response;
if(last_response_len === false) {
this_response = response;
last_response_len = response.length;
}
else {
this_response = response.substring(last_response_len);
last_response_len = response.length;
}
console.log(this_response);
}
}
}).done(function(data) {
console.log('Complete response = ' + data);
}).fail(function(data) {
console.log('Error: ', data);
});
console.log('Request Sent');
*/
}
function install_firmware(version) {
display_message('info', 'Installing firmware ' + version + '.', 6000000);
install_running = true;
clear_message();
$('#log-content').html('');
$('#modal-log').modal('show');
rest("POST", "/start_install_firmware", version,
function(data) {
install_running = false;
display_message('success', 'Firmware ' + version + ' successfully installed.', 6000000);
},
function(xhr, ajaxOptions, thrownError) {
install_running = false;
//$('#modal-log').modal('hide');
default_error_callback(xhr, ajaxOptions, thrownError);
}
);
setTimeout(update_install_log, 1000);
}
$(document).ready(function() {
@ -174,13 +163,16 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
</table>
</div>
<div id="modal-log" class="ui modal">
<div style="height:80%" id="modal-log" class="ui modal">
<i class="close icon"></i>
<div class="header">
Progress
</div>
<div id="log-content" class="content">
Line 1
<div class="content">
<div id="message-log" class="ui message hidden">
</div>
<pre style="max-height:80%; overflow-x:hidden; overflow-y:auto; white-space: pre-wrap;" id="log-content" class="content">
</pre>
</div>
</div>
</body>

View File

@ -47,43 +47,36 @@ proc process {} {
return "\"[rmupdate::version]\""
} elseif {[lindex $path 1] == "get_firmware_info"} {
return [rmupdate::get_firmware_info]
} elseif {[lindex $path 1] == "install_firmware"} {
fconfigure stdout -buffering none
#puts "Content-Type: application/octet-stream"
puts "Content-Type: text/html; charset=utf-8"
puts "Status: 200 OK";
puts ""
flush stdout
after 1000
puts "Line 1\n"
flush stdout
after 1000
puts "Line 2\n"
flush stdout
after 1000
puts "Line 3\n"
flush stdout
after 1000
puts "Line 4\n"
flush stdout
return ""
} elseif {[lindex $path 1] == "start_install_firmware"} {
regexp {^([\d\.]+)$} $data match version
if { [info exists version] && $version != "" } {
rmupdate::install_firmware_version $version
return "\"done\""
} else {
error "Invalid version: ${data}"
}
} elseif {[lindex $path 1] == "read_install_log"} {
variable content_type "text/html"
return [rmupdate::read_install_log]
}
}
error "invalid request" "Not found" 404
}
variable content_type "application/json"
if [catch {process} result] {
set status 500
if { [info exists $errorCode] } {
set status $errorCode
}
puts "Content-Type: application/json"
puts "Content-Type: ${content_type}"
puts "Status: $status";
puts ""
set result [json_string $result]
puts -nonewline "\{\"error\":\"${result}\"\}"
} elseif {$result != ""} {
puts "Content-Type: application/json"
} else {
puts "Content-Type: ${content_type}"
puts "Status: 200 OK";
puts ""
puts -nonewline $result