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 }