#!/mod/bin/jimsh

package require cgi
source /mod/webif/lib/setup
require system.class settings.class

jqplugin enadis
header

set device [system disk]
set settings [settings]

set smart Unknown
set line ""
# smartctl uses non-zero exit status to indicate health hence catch.
catch { set line [exec /bin/smartctl -H $device | sed -n 5p] } msg
if {$msg ne "" && $line eq ""} { set line $msg }
if {$line ne ""} {
	set smart [string range [lindex [split $line :] 1] 1 end]
	$settings _tval_setting "SMART_status" $smart
}

puts "
<h3 class=va>
	<img class=va width=100 src=/img/disc.png>
	SMART data read from device $device
</h3>
<fieldset class=cleft>
<legend>Disk Information</legend>
<table>
<tr><th class=key>SMART Status</th><td>$smart<td></tr>
"
if {[catch {set msg [exec /bin/smartctl -i $device]} xmsg]} {
	set msg $xmsg
}
foreach line [split $msg "\n"] {
	if {[string match "*Not in smartctl database*" $line]} continue
	if {[string match "*: *" $line]} {
		regsub -all -- {[[:space:]]+} $line " " line
		set fields [split $line ":"]
		puts "<tr><th class=key>[lindex $fields 0]</th>"
		puts "<td>[join [lrange $fields 1 end] :]</td></tr>"
	}
}
puts {
</table>
</fieldset>

<fieldset class=cleft>
<legend>Attributes</legend>
<table class=borders cellpadding=3>
<tr>
	<th class=odd>ID</th>
	<th class=odd>Name</th>
	<th class=odd>Flags</th>
	<th class=odd>Raw Value</th>
	<th class=odd>Value</th>
	<th class=odd>Worst</th>
	<th class=odd>Threshold</th>
	<th class=odd>Life Left</th>
	<th class=odd>Notes</th>
</tr>
}

#set flag_autokeep 	0x20
#set flag_count		0x10
#set flag_rate		0x08
#set flag_speed		0x04
#set flag_online	0x02
#set flag_prefailure	0x01

set flaglist [split "POSRCK" ""]
set flagdescr [list \
	"P prefailure warning" \
	"O updated online" \
	"S speed/performance" \
	"R error rate" \
	"C event count" \
	"K auto-keep" \
]

proc flags {val} {
	global flaglist flagdescr
	set f ""
	set fx ""
	loop i 0 [llength $flaglist] {
		if {[expr {$val & (1 << $i)}]} {
			append f [lindex $flaglist $i]
			append fx "[lindex $flagdescr $i]\n"
		} else {
			append f "-"
		}
	}
	return "<span title=\"$fx\">$f</span>"
}

set nolife {184 187 188 189 191 197 198}

set derive {4 "" 5 sectors 9 hours 12 cycles 192 "" 193 "cycles"}

if {[catch {set msg [exec /bin/smartctl -A $device]}]} {
	set msg ""
}
set i 0
foreach line [split $msg "\n"] {
	regsub -all -- {[[:space:]]+} $line " " line
	regsub -all -- {^[[:space:]]+} $line "" line
	if {[incr i] < 8} continue
	if {$line eq ""} continue
	lassign [split $line] \
	    id name flags val worst thresh type updated when rval
	if {$id eq ""} continue
	set class normal
	switch $id {
		5 {
			$settings _nval_setting "SMART_realloc" $rval
			if {$rval > 0} { set class orangeshade }
		}
		10 {
			$settings _nval_setting "SMART_spinretry" $rval
			#if {$rval > 0} { set class orangeshade }
		}
		197 {
			$settings _nval_setting "SMART_pending" $rval
			if {$rval > 0} { set class redshade }
		}
		198 {
			$settings _nval_setting "SMART_offline" $rval
			if {$rval > 0} { set class redshade }
		}
	}
	if {$val <= $thresh} {
		set class redshade
	}
	set left ""
	if {$flags eq ""} { set flags 0 }
	if {$id ni $nolife &&
	    [expr {$flags & 0x18}] == 0x10 && $val == $worst && $val <= 100 &&
	    $thresh < 100} {
		# Event counter which is not a rate and has not regressed.
		set notes ""
		set left [expr {100 - 100 * (100 - $val) / (100 - $thresh)}]
		if {$left < 10} { set class orangeshade }
		# rval can be non-numeric (e.g. 6078h+41m+22.260s)
		if {[regexp {^\d+$} $rval] && $rval > 0 && [\
		    dict exists $derive $id]} {
			if {$left < 100} {
				set total [ \
				    expr {round(($rval / (100 - $left) * 100) \
					/ 10000) * 10000} \
				]
				set remain $($total - $rval)
				set notes "$remain of approximately"
				append notes " $total $derive($id)"
			}
		}
		set left "<span title=\"$notes\">$left%</span>"
	}
	if {$id == 190} {
		append val " ([expr {100 - $val}]&deg;C)"
		append worst " ([expr {100 - $worst}]&deg;C)"
		append thresh " ([expr {100 - $thresh}]&deg;C)"
	}
	puts "<tr class=$class>
		<td>$id</td>
		<td>$name</td>
		<td>[flags $flags]</td>
		<td>$rval</td>
		<td>$val</td>
		<td>$worst</td>
		<td>$thresh</td>
		<td>$left</td>
		<td>$when</td>
	</tr>"
}

puts {
</table>
</fieldset>

<fieldset class=cleft>
<legend>Self-test logs</legend>
<table class=borders cellpadding=3>
<tr>
	<th class=odd>No.</th>
	<th class=odd>Description</th>
	<th class=odd>Status</th>
	<th class=odd>Remaining</th>
	<th class=odd>When</th>
	<th class=odd>First Error LBA</th>
</tr>
}

if {[catch {exec /bin/smartctl -l selftest $device} msg]} {
        set rc [lindex $::errorCode 2]
        set rc $($rc eq "" ? 1 : $rc & 7)
        if {$rc} { set msg "" }
}

set i 0
foreach line [split $msg "\n"] {
	if {[incr i] < 7} continue
	if {[regexp {\s*#\s*(\d+)\s+((?:[\s-]?\w[:;.,]?)+)\s+((?:[\s-]?\w[:;.,]?)+?)\s+(\d\d?%)\s+(\d+)\s+([\d-]\d*)} \
		$line x id name status remaining when lba] == 0} continue
	puts "<tr>
		<td>$id</td>
		<td>$name</td>
		<td>$status</td>
		<td>$remaining</td>
		<td>$when</td>
		<td>$lba</td>
	</tr>"
}

puts {
</table>
</fieldset>

<div class=cleft style="padding-top: 2em">
<button id=diskack>Acknowledge any current disk faults.</button>
</div>

<script type=text/javascript>
$(function() {

$('#diskack')
	.button()
	.on('click', function(e) {
		e.preventDefault();
		$('button').disable();
		$.get('diskack.jim', function() {
			window.location.reload(true);
		});
	});
});

</script>

}

footer

