webif/webif/lib/system.class

848 lines
18 KiB
Tcl

source /mod/webif/lib/setup
package require xconv
if {![exists -proc class]} { package require oo }
if {![exists -proc sqlite3.open]} { package require sqlite3 }
if {![exists -proc binary]} { package require binary }
class system {}
proc {system model} {} {{model ""}} {
if {$model ne ""} { return $model }
if {[catch {set fp [open /etc/model r]}]} {
set model {HD[R]}
} else {
set model [string trim [read $fp]]
close $fp
}
return $model
}
proc {system hostname} {} {
if {[catch {set hostname [string trim [exec hostname]]}]} {
set hostname "humax"
}
return $hostname
}
proc {system ip} {} {
if {[catch {set fp [open /etc/hosts r]}]} {
set ip "127.0.0.1"
} else {
set ipl [lindex [split [$fp read] "\n"] 1]
regsub -- {[[:space:]].*} $ipl "" ip
$fp close
}
return $ip
}
proc {system modversion} {{short 0}} {{modver ""}} {
if {$modver eq ""} {
if {[catch {set fp [open /etc/modversion r]}]} {
set modver "102"
} else {
set modver [string trim [read $fp]]
close $fp
}
}
if {$short} { return $modver }
lassign [split $modver ""] a b c
return [format "%d.%d%d" $a $b $c]
}
proc {system modbuild} {} {{modbuild ""}} {
if {$modbuild ne ""} { return $modbuild }
if {[catch {set modbuild [string trim [file read /etc/modbuild]]}]} {
set modbuild 0
}
return $modbuild
}
proc {system lastbootreason} {{descr 1}} {{lbr -1}} {
if {$lbr == -1} {
set lbr 0
if {[file readable /tmp/.lbr]} {
set lbr [file read /tmp/.lbr]
}
}
if {!$descr} { return $lbr }
switch $lbr {
1 { return "Front panel button" }
2 { return "Remote control handset" }
3 { return "Scheduled event" }
}
return "Unknown ($lbr)"
}
proc {system idletime} {} {
if {[file exists "/tmp/.lastir"]} {
return $([clock seconds] - [file mtime "/tmp/.lastir"])
}
return 0
}
proc {system fhtcpversion} {} {{ver ""}} {
if {$ver ne ""} { return $ver }
set file "/etc/fhtcpversion"
if {![file exists $file]} { set file "/root/fhtcpversion" }
if {[catch {set fp [open $file r]}]} {
set ver "?.??.??"
} else {
set ver [string trim [read $fp]]
close $fp
}
return $ver
}
# :c0400 System ID
# 4:c8400 read 1128 bytes
# 3:c8c00 MAC address
# 2:c9000
# 1:cb400 DTCP key material
# 0:cb800 Serial Number
# 5:cbc00
proc {system systemid} {} {{id ""}} {
if {$id ne ""} { return $id }
set fd [open /dev/mtd3 r]
$fd seek 0xc0400
set bytes [$fd read 4]
$fd close
binary scan $bytes H* hex
set id [format "%s.%s" [string range $hex 0 3] [string range $hex 4 end]]
return $id
}
proc {system macaddr} {} {{mac ""}} {
if {$mac ne ""} { return $mac }
set fd [open /dev/mtd3 r]
$fd seek 0xc8c00
set bytes [$fd read 6]
$fd close
set tmac ""
foreach b [split $bytes ""] {
binary scan $b H* hex
if {$tmac ne ""} { append tmac : }
append tmac $hex
}
return [set mac $tmac]
}
proc {system serialno} {} {{serial ""}} {
if {$serial ne ""} { return $serial }
set fd [open /dev/mtd3 r]
$fd seek 0xcb800
set bytes [$fd read 14]
$fd close
set serial "[string range $bytes 0 1] [string range $bytes 2 8] [
string range $bytes 9 end]"
return $serial
}
proc {system encryptionkey} {} {{key ""}} {
if {$key ne ""} { return $key }
set fd [open /dev/mtd3 r]
$fd seek 0xc8c00
set bytes [$fd read 6]
$fd seek 0xcb800
append bytes [$fd read 10]
$fd close
binary scan $bytes H* key
return $key
}
proc {system loaderver} {} {{ver ""}} {
if {$ver ne ""} { return $ver }
set fd [open /dev/mtd3 r]
$fd seek 0x20006
set bytes [$fd read 2]
$fd close
set ver [switch $bytes {
"\x04\x5f" { format "a7.30" }
"\xec\xe5" { format "a7.31" }
"\x59\x9d" { format "a7.33" }
"\x72\x5c" { format "a7.34" }
"\xe5\xe3" { format "L7.26" }
"\x8f\x7c" { format "L7.27" }
default {
binary scan $bytes H* hex
format "Unknown - $hex"
}
}]
return $ver
}
proc {system kernelver} {} {{ver ""}} {
if {$ver ne ""} { return $ver }
#1 SMP Sun Mar 25 18:30:38 KST 2012
set rver [string range [exec uname -v] 11 end]
set ver [switch $rver {
# HDR
"Sep 16 20:17:56 KST 2010" { format "HDR_1.01.05" }
"Oct 11 21:11:05 KST 2010" { format "HDR_1.01.09" }
"Jan 21 15:44:39 KST 2011" { format "HDR_1.02.07" }
"Jun 11 00:54:19 KST 2011" { format "HDR_1.02.20" }
"Mar 25 18:30:38 KST 2012" { format "HDR_1.02.27" }
"May 27 00:19:34 KST 2012" { format "HDR_1.02.28" }
"Jul 5 11:11:28 KST 2012" { format "HDR_1.02.29" }
"Jan 12 16:49:05 KST 2013" { format "HDR_1.02.32" }
"Mar 6 07:27:02 KST 2013" { format "HDR_1.03.06(a)" }
"May 8 14:32:30 KST 2013" { format "HDR_1.03.06(b)" }
"Dec 10 14:36:54 KST 2013" { format "HDR_1.03.11" }
"Feb 7 14:15:02 KST 2014" { format "HDR_1.03.12" }
"May 19 22:39:27 BST 2014" { format "HDR_CFW_3.00" }
"Feb 19 20:58:57 GMT 2015" { format "HDR_CFW_3.03" }
"Mar 11 23:34:27 GMT 2016" { format "HDR_CFW_3.10" }
"Mar 11 23:37:22 GMT 2016" { format "HDR_CFW_3.10d" }
"Jan 9 04:46:49 GMT 2017" { format "HDR_CFW_3.11" }
"Jan 9 04:50:13 GMT 2017" { format "HDR_CFW_3.11d" }
"Mar 23 19:36:14 GMT 2017" { format "HDR_CFW_3.12" }
"Mar 23 19:39:44 GMT 2017" { format "HDR_CFW_3.12d" }
"Apr 18 22:42:30 BST 2017" { format "HDR_CFW_3.13" }
"Apr 18 22:45:34 BST 2017" { format "HDR_CFW_3.13d" }
# HD
"Oct 11 21:14:31 KST 2010" { format "HD_1.01.12" }
"May 17 14:16:20 KST 2011" { format "HD_1.02.18" }
"Jun 11 00:54:19 KST 2011" { format "HD_1.02.20" }
"Mar 25 07:09:19 KST 2012" { format "HD_1.02.27" }
"May 27 00:19:40 KST 2012" { format "HD_1.02.28" }
"Jul 5 19:41:17 KST 2012" { format "HD_1.02.29" }
"Oct 13 12:48:09 KST 2012" { format "HD_1.02.31" }
"Dec 9 14:15:07 KST 2014" { format "HD_1.03.02" }
default { format "Unknown - $rver" }
}]
return $ver
}
# Newer version below is over 100 times faster
#proc {system pkgver} {{pkg webif}} {
# return [lrange [split [exec opkg list-installed $pkg] " "] 2 end]
#}
proc {system pkgver} {{pkg webif}} {
if {[catch {set fp [open /mod/var/opkg/info/$pkg.control r]}]} {
return 0
}
set v 0
foreach line [split [$fp read] "\n"] {
if {[string equal -length 9 $line "Version: "]} {
lassign $line x v
break
}
}
$fp close
return $v
}
proc {system pkginst} {pkg} {
return [file exists "/mod/var/opkg/info/$pkg.control"]
}
proc {system mediaroot} {} {
switch [system model] {
HDR { return "/media/My Video" }
HD { return "/media/drive1/Video" }
}
return ""
}
proc {system dlnastatus} {} {
return [system is_listening 9000]
}
proc {system dlnadb} {} {
switch [system model] {
HDR { return "/mnt/hd2/dms_cds.db" }
HD { return "/media/drive1/dms_cds.db" }
}
return ""
}
proc {system _dlnaurl} {file urlbase} {
set mime "video/ts"
if {[catch {set db [sqlite3.open [system dlnadb]]}]} {
return {}
}
set muri [$db query {
select tblresource.mimetype, contenturi
from tblresource join tblmedia using (mediaid)
where localurl = '%s'} $file]
if {[llength $muri]} {
lassign [lindex $muri 0] x mime x xuri
} else {
# Try for partially linked entry
set muri [$db query {
select mediaid from tblmedia
where localurl = '%s'
} $file]
if {![llength $muri]} { return {} }
lassign [lindex $muri 0] x mediaid
set xuri "media/$mediaid.TS"
}
$db close
set url "http://${urlbase}:9000/web/$xuri"
return [list $url $mime]
}
proc {system dlnaurl} {file {urlbase ""}} {
if {$urlbase eq ""} { set urlbase [system ip] }
set retries 5
set ret {}
while {$retries > 0 && [\
catch {set ret [system _dlnaurl $file $urlbase]}]} {
incr retries -1
sleep 1
}
return $ret
}
proc {system dlnahelper} {file {urlbase ""}} {
set dir /mnt/hd2/mod/.dlnahelper
require lock
if {$file eq "-release"} {
release_lock dlnahelper
return {}
}
if {![acquire_lock dlnahelper 2]} { return {} }
if {![file isdirectory $dir]} { file mkdir $dir }
set base [file rootname $file]
foreach ext {hmt ts} {
file delete "$dir/helper.$ext"
file link -symbolic "$dir/helper.$ext" "$base.$ext"
}
set ret [system dlnaurl "$dir/helper.ts" $urlbase]
if {![llength $ret]} {
# Manually import the helper file.
if {![catch {set db [sqlite3.open [system dlnadb]]}]} {
$db query {
insert into tblmedia
(localurl,folder,mediatype,refcount)
values('%s','%s',0,1)
} "$dir/helper.ts" $dir
$db close
set ret [system dlnaurl "$dir/helper.ts" $urlbase]
}
}
if {![llength $ret]} {
release_lock dlnahelper
}
return $ret
}
proc {system dustbin} {{short 0}} {
set dustbin "\[Deleted Items\]"
if {![catch {set fd [open "/mod/boot/dustbin.name" r]}]} {
set dustbin [lindex [split [read $fd] "\n"] 0]
$fd close
}
if {$short} { return $dustbin }
return "[system mediaroot]/$dustbin"
}
proc {system dustbinsize} {} {
set bin [system dustbin]
set ret 0
if {[file isdirectory $bin]} {
if {[catch {
lassign [exec /mod/bin/busybox/du -s $bin] ret
}]} {
set ret 0
}
}
return $($ret * 1024)
}
proc {system diskpart} {} {
switch [system model] {
HDR { return "/mnt/hd2" }
HD { return "/media/drive1" }
}
return ""
}
proc {system diskdev} {} {
return [lindex [lsearch -regexp -inline \
[split [file read /proc/mounts] "\n\r"] " [system diskpart] "] 0]
}
proc {system disk} {} {
return [string range [system diskdev] 0 end-1]
}
proc {system disktemp} {} {
if {[catch {
set smart [exec /bin/smartctl -A [system disk] | grep ^194]
regsub -all -- {[[:space:]]+} $smart " " smart
set temp [lindex [split $smart] 9]
}]} {
set temp 0
}
return $($temp + 0)
}
require pretty_size
proc {system diskspace} {{raw 0}} {
set part [system diskpart]
lassign [exec /mod/bin/busybox/stat -f -c {%S %b %f} $part] \
bsize blocks fblocks
set size $($bsize * $blocks)
set free $($bsize * $fblocks)
set used $($size - $free)
set perc $($used * 100 / $size)
set fperc $(100 - $perc)
switch [system model] {
HDR {
set tsrdir "/mnt/hd2/Tsr"
set tsrok [file isdirectory $tsrdir]
}
HD {
set tsrdir "/media/drive1/.tsr"
set tsrok [file exists "$tsrdir/0.ts"]
}
}
if {$tsrok} {
set tsrbuf 21474836480
if {[catch {
lassign [exec du -ks $tsrdir] tsrused
}]} {
set tsrused 0
set tsrbuf 0
} else {
set tsrused $($tsrused * 1024)
}
} else {
set tsrbuf 0
set tsrused 0
}
if {!$raw} {
set size [pretty_size $size]
set free [pretty_size $free]
set used [pretty_size $used]
set tsrbuf [pretty_size $tsrbuf]
set tsrused [pretty_size $tsrused]
}
return [list $size $used $perc $free $fperc $tsrbuf $tsrused]
}
proc {system diskfree} {} {
lassign [exec /mod/bin/busybox/stat -f -c {%S %f} [system diskpart]] \
bsize fblocks
return $($bsize * $fblocks)
}
proc {system busy} {} {
# Is humaxtv doing anything important?
if {[catch {set pid [exec /mod/bin/busybox/pgrep -n humaxtv]}]} {
return 0
}
set c 0
foreach line [split [\
exec /mod/webif/lib/bin/lsof -X -Fn -p $pid] "\n"] {
if {[string match {*Video*.ts} $line]} { incr c }
}
if {$c > 0} { return 1 }
return 0
}
proc {system inuse} {file} {
if {![file exists $file]} { return 0 }
set op [exec /mod/webif/lib/bin/lsof -X -Fn $file]
switch -glob -- $op {
"" -
"*status error*" { return 0 }
}
return 1
}
proc {system dirinuse} {dir} {
set files {}
foreach line [split [\
exec /mod/webif/lib/bin/lsof -X -Fn +d $dir] "\n"] {
if {[string index $line 0] ne "n"} continue
lappend files [file tail $line]
}
return $files
}
proc {system reboot} {{fast 0}} {
if {$fast} {
exec /mod/webif/lib/bin/reboot -f
} else {
exec /mod/webif/lib/bin/reboot
}
}
proc {system restartpending} {{mode 1}} {
if {$mode} {
close [open /tmp/.restartpending w]
} else {
file delete /tmp/.restartpending
}
}
proc {system param} {param {type Value} {tbl MENUCONFIG}} {
if {[catch {set db [sqlite3.open /var/lib/humaxtv/setup.db]} msg]} {
return 0
}
set val 0
set ret [$db query "
select item$type
from TBL_$tbl
where itemName = '$param'
"]
if {[llength $ret] == 1} {
lassign [lindex $ret 0] x val
}
$db close
return $val
}
proc {system padding} {} {
return [list \
[system param START_PADDING_TIME] \
[system param STOP_PADDING_TIME] \
]
}
proc {system instandby} {} {
return [system param LAST_STANDBY Value USERCONFIG]
}
proc {system mkdir_p} {dir} {
exec /mod/bin/busybox/mkdir -p $dir
}
proc {system dirblockers} {dir {permitted {".*"}}} {
set fl {}
foreach e [readdir -nocomplain $dir] {
if {$e in {. ..}} continue
if {[file isdirectory "$dir/$e"]} {
lappend fl $e
continue
}
set flag 1
foreach p $permitted {
if {[string match -nocase $p $e]} {
set flag 0
break
}
}
if {$flag} {
lappend fl $e
}
}
return $fl
}
proc {system rmdir_if_empty} {dir {permitted {".*"}}} {
set fl [system dirblockers $dir $permitted]
if {![llength $fl]} {
file delete -force $dir
return 1
}
return 0
}
proc {system listening} {{mport 0}} {
set ret {}
foreach line [split [exec /mod/bin/busybox/netstat -lntpw] "\n"] {
# Remove repeated spaces
regsub -all -- {[[:space:]]+} $line " " line
# tcp 0 0 0.0.0.0:9955 0.0.0.0:* LISTEN 169/humaxtv
lassign [split $line " "] x x x port x type app
if {$type ne "LISTEN"} { continue }
lassign [split $port ":"] x port
if {$mport && $mport != $port} { continue }
lappend ret [list $port [split $app "/"]]
}
return $ret
}
proc {system is_listening} {mport} {
return [llength [system listening $mport]]
}
proc {system logtimestamp} {} {
return [clock format [clock seconds] -format "%d/%m/%Y %H:%M:%S"]
}
proc {system _log} {log msg} {
set logfd [open $log "a+"]
puts $logfd "[system logtimestamp] - $msg"
$logfd close
}
proc {system log} {log msg} {
system _log "/var/log/$log.log" $msg
}
proc {system plog} {log msg} {
system _log "/mod/tmp/$log.log" $msg
}
proc {system notify} {msg} {
system plog notify $msg
}
proc {system display} {hdr hd} {
if {[system model] eq "HDR"} {
exec /sbin/display $hdr
} else {
exec /sbin/display "\$$hd"
}
}
proc {system tuners} {} {
if {[system model] eq "HDR"} {
return 2
} else {
return 1
}
}
proc {system uptime} {} {
set fd [open /proc/uptime r]
set uptime [lindex [split [read $fd]] 0]
$fd close
return $uptime
}
proc {system filename} {str {extra ""}} {
# Humax TV replaces these characters.
# "%*./:<>?\|
set chars "\"%*\./:<>?\\\\|$extra"
return [regsub -all -- "\[$chars]" $str "_"]
}
proc {system connectivity} {{site "hpkg.tv"} {port 80} {ret "0"}} {
set extra [lassign [exec /mod/bin/tcpping $site $port] result]
if {$ret ne "0"} { upvar $ret err }
switch $result {
OK { return 1 }
default {
set err [list $result $extra]
return 0
}
}
}
proc {system nuggeted} {} {
if {![system pkginst nugget]} { return 0 }
set ret 0
catch {
if {[exec /mod/bin/nugget ping] eq "PONG"} {
set ret 1
}
}
return $ret
}
proc {system strip} {str} {
if {[string range $str 1 2] eq "i7"} {
# ISO6937, convert to UTF-8
return [xconv [string range $str 3 end]]
}
if {[string first "\025" $str] == 0} {
# UTF-8
return [string range $str 1 end]
}
return [xconv $str]
}
proc {system usbdisks} {{full 0}} {
set disks [lmap i [glob -nocomplain \
-directory /sys/bus/usb/drivers/usb-storage *:*] {
file tail $i
}]
if {!$full} {
return [llength $disks]
}
if {![llength $disks]} { return {} }
# Build list of device to device ID
set map {}
foreach block [glob -nocomplain -directory /sys/block sd?] {
set link [file readlink $block/device]
foreach disk $disks {
if {[string first "/$disk/" $link] >= 0} {
set map([file tail $block]) $disk
}
}
}
return $map
}
proc {system usbmounts} {{full 0}} {
if {!$full} {
set num 0
catch {
set num [
exec grep -cE {/media/([a-z-]+-)?drive} \
/proc/mounts]
}
return $num
}
set ret {}
if {[catch {
set lines [exec grep -E {media/([a-z-]+-)?drive} \
/proc/mounts]
}]} { return $ret }
foreach line [split $lines "\n"] {
lassign $line dev mp
set rec "DEV $dev MP $mp LABEL {NO NAME} TYPE Unknown"
set rec(VENDOR) Unknown
set rec(MODEL) Unknown
if {[regexp {/dev/(sd[a-z])} $dev x rdev]} {
set rec(RDEV) $rdev
if {[file readable [
set f /sys/block/$rdev/device/vendor]]} {
set rec(VENDOR) [string trim [file read $f]]
}
if {[file readable [
set f /sys/block/$rdev/device/model]]} {
set rec(MODEL) [string trim [file read $f]]
}
}
lassign [exec stat -f -c {%S %b %f} $mp] blocks blockc blockf
set rec(SIZE) $($blockc * $blocks)
set rec(USED) $(100 - $blockf * 100 / $blockc)
# /dev/sda1: LABEL="SAN" UUID="DBC1-1CF8" TYPE="vfat"
catch {
foreach field [
split [exec /mod/sbin/blkid -c /dev/null $dev] "\n"] {
while {[regexp { *([A-Z]+)="?([^"]+)"?(.*)} \
$field x k v field]} {
set rec($k) $v
}
}
}
# Test for EXFAT
if {$rec(TYPE) eq "Unknown" && [
file exists /mod/sbin/exfatlabel]} {
catch {
set label [exec /mod/sbin/exfatlabel $dev 2>/dev/null]
if {$label ne ""} {
set rec(TYPE) "EXFAT"
set rec(LABEL) $label
}
}
}
lappend ret $rec
}
return $ret
}
proc {system has} {comp} {
switch $comp {
wifi_dongle {
if {[catch {exec /mod/bin/iwgetid}]} { return 0 }
return 1
}
tvdb {
require settings.class
return [[settings] _nval_setting tvdb]
}
}
return 0
}
# Note that {system checkop} can be called from processes other than that
# which started the operation. Hence no visilibty into the ::system::ops
# dictionary.
proc {system checkop} {op} {
if {![regexp -nocase -- {^[.a-z0-9]+$} $op]} { return 0 }
if {[catch {set fp [open "/tmp/.bgop.$op" "a"]}]} {
return 0
}
set ret 1
if {[$fp lock]} {
$fp unlock
set ret 0
}
$fp close
return $ret
}
set ::system::ops {}
proc {system startop} {args op file} {
if {![regexp -nocase -- {^[a-z0-9]+$} $op]} { return 0 }
if {"-multiple" in $args} {
set i 0
append op "[pid]."
while {[dict exists $::system::ops "$op$i"]} { incr i }
append op $i
} elseif {[dict exists $::system::ops $op]} {
system endop $op
}
if {[catch {
set fp [open "/tmp/.bgop.$op" "a"]
if {[$fp lock]} {
$fp puts "$file\n[pid]"
$fp flush
set ::system::ops($op) $fp
} else {
$fp close
error "Could not lock op file."
}
}]} { return 0 }
return $op
}
proc {system endop} {op} {
if {![regexp -nocase -- {^[.a-z0-9]+$} $op]} { return 0 }
if {[dict exists $::system::ops $op]} {
catch {
set fp $::system::ops($op)
$fp unlock
$fp close
}
dict unset ::system::ops $op
}
if {[file exists "/tmp/.bgop.$op"]} {
file delete "/tmp/.bgop.$op"
return 1
}
return 0
}
proc {system specialdir} {dir} {
if {[string match {\[*} [string trimleft [file tail $dir]]]} {
return 1
}
return 0
}