webif/webif/lib/auto/scan

419 lines
9.3 KiB
Plaintext
Executable File

#!/mod/bin/jimsh
source /mod/webif/lib/setup
require system.class settings.class ts.class rsv.class browse.class \
queue.class \
lock plugin safe_delete pretty_size
source /mod/webif/lib/auto/util.jim
set ::auto::settings [settings]
set ::auto::loglevel [$::auto::settings autolog]
set ::auto::prelocked 0
set ::auto::earlyexit 0
set ::auto::force 0
while {[llength $argv]} {
switch -- [lindex $argv 0] {
-d {
incr ::auto::loglevel
set ::auto::logfd stdout
}
-f {
set ::auto::force 1
}
-prelocked {
set ::auto::prelocked 1
}
-logprefix {
set argv [lrange $argv 1 end]
if {[llength $argv]} {
set ::auto::logprefix [lindex $argv 0]
}
}
-test {
set ::auto::earlyexit 1
}
default {
# Pass to rest of script.
break
}
}
set argv [lrange $argv 1 end]
}
# Acquire lock
if {$::auto::logfd ne "unset"} {
puts $::auto::logfd "Acquiring lock..."
}
if {!$::auto::prelocked && ![acquire_lock webif_autoscan]} {
if {$::auto::loglevel > 1} {
system plog auto "Could not acquire lock."
}
puts "Could not acquire exclusive lock, terminating."
exit
}
::auto::loginit
######################################################################
# Determine if it's time to run
if {[system uptime] < 180} {
::auto::log "Aborting, system has just booted." 2
exit
}
::auto::dsc
::auto::oktorun
if {!$::auto::force} {
set autofreq [$::auto::settings autofreq]
if {$autofreq == 0} { set autofreq 10 }
set timesincelast $(([clock seconds] - [$::auto::settings autolast]) \
/ 60)
if {$timesincelast < 0} {
# time machine issue: correct it
$::auto::settings autolast 0
} elseif {$timesincelast < $autofreq} {
::auto::log "Aborting, not yet time to run." 2
::auto::log " elapsed (minutes): $timesincelast (<$autofreq)" 2
exit
}
}
if {$::auto::earlyexit} {
if {!$::auto::force} {
$::auto::settings autolast [clock seconds]
}
puts "Early exit."
exit
}
#########################################################################
# Initialisation
set scanstart [clock milliseconds]
::auto::log "Auto processing starting" 2
::auto::tmpdir "webif_auto"
if {[system pkginst undelete]} {
set ::auto::dustbin "[system dustbin]"
} else {
set ::auto::dustbin ""
}
set ::auto::root [system mediaroot]
file stat "$::auto::root/" rootstat
set ::auto::rootdev $rootstat(dev)
#########################################################################
# Utility functions
set ::auto::flagdb {}
set ::auto::activeflags {}
set ::auto::extraflags {}
proc ::auto::buildflagdb {dir {active {}}} {{seen {}} {indent 0}} {
variable flagdb
variable activeflags
variable extraflags
variable dustbin
variable rootdev
incr indent 2
set pre [string repeat " " $indent]
log "${pre}\[$dir]" 3
if {$dir eq $dustbin} {
log "${pre}Dustbin, skipping." 3
incr indent -2
return
}
if {[catch {file stat "$dir/" st} msg]} {
log "${pre}Can't stat $dir - $msg, skipping.." 2
incr indent -2
return
}
if {[specialdir $dir]} {
# Special folder
if {$st(dev) != $rootdev} {
log "${pre}Special folder on different device, skipping." 3
incr indent -2
return
}
if {[llength $active]} {
set active {}
log "${pre}Special folder, overriding recursion." 3
}
}
# Already seen
set key "$st(dev):$st(ino)"
if {$key in $seen} {
log "${pre}Already seen, skipping." 3
incr indent -2
return
}
lappend seen $key
set flags [lmap i \
[glob -nocomplain -directory $dir -tails ".auto*"] \
{ string range $i 5 end }]
foreach x $extraflags {
foreach f [lmap i \
[glob -nocomplain -directory $dir -tails ".$x"] \
{ string range $i 1 end }] {
ladd flags $f
}
}
foreach f $flags {
# If recursive flag found, add to active set.
if {[string index $f end] eq "R"} {
ladd active [string range $f 0 end-1]
}
}
# Add any active flags to the set.
foreach f $active {
ladd flags $f
}
if {[llength $flags]} {
lappend flagdb $dir $flags
log "${pre} $flags" 3
foreach f $flags { ladd activeflags $f }
}
foreach entry [readdir -nocomplain $dir] {
if {[file isdirectory "$dir/$entry"]} {
buildflagdb "$dir/$entry" $active
}
}
set active {}
incr indent -2
}
set ::auto::recalcdirs {}
proc ::auto::recalcdir {dir} {
variable recalcdirs
ladd recalcdirs $dir
}
proc ::auto::direntries {dir callback} {
foreach entry [readdir -nocomplain $dir] {
if {![string match {*.ts} $entry]} continue
if {[catch {set ts [ts fetch "$dir/$entry"]}]} continue
if {$ts == 0} continue
if {[$callback $ts] eq "STOP"} break
}
}
proc ::auto::flagscan {root flag callback {recurse 1}} {
variable flagdb
variable activeflags
if {$flag ni $activeflags} {
log "No $flag flags in filesystem, suppressing scan."
return
}
if {!$recurse} {
# Just this exact directory
if {[dict exists $flagdb $root] && $flag in $flagdb($root)} {
log "[string toupper $flag]: \[$root]" 2
$callback $root
}
return
}
set rootl [string length $root]
foreach {dir flags} $flagdb {
if {![string equal -length $rootl $dir $root]} continue
if {$flag in $flags} {
oktorun
dsc
log "[string toupper $flag]: \[$dir]" 2
if {[$callback $dir] eq "STOP"} break
}
}
}
alias ::auto::autoflagscan ::auto::flagscan
######################################################################
# Plugin registration
set ::auto::plugins {}
proc ::auto::register {plugin {priority 500} {fn ""}} {
variable plugins
if {$fn eq ""} { set fn "::${plugin}::run" }
lappend plugins [list $plugin $fn $priority]
log "Registered $plugin with priority $priority ($fn)" 2
}
proc ::auto::register_flag {plugin flag} {
variable extraflags
ladd extraflags $flag
log "Registered flag '$flag' for $plugin" 2
}
# Backwards compatibility with legacy plugins
set ::auto::legacy {}
proc register {type fn {priority 50}} {
set module [lindex [split $fn :] 2]
switch -- $type {
predecryptscan { incr priority 600 }
postdecryptscan { incr priority 500 }
prededupscan { incr priority 800 }
postdedupscan { incr priority 700 }
preshrinkscan { incr priority 400 }
postshrinkscan { incr priority 300 }
prempgscan { incr priority 300 }
postmpgscan { incr priority 200 }
premp3scan { incr priority 300 }
postmp3scan { incr priority 200 }
preexpirescan { incr priority 900 }
postexpirescan { incr priority 800 }
postdecryptsingledir {
::auto::log \
"Mapping ::${module}::rundir on to $fn" 1
alias ::${module}::rundir $fn
return
}
default {
::auto::log \
"Legacy registration ignored for $type $fn" 1
return
}
}
::auto::register $module $priority $fn
lappend ::auto::legacy $module
}
######################################################################
# Load plugins
# Bundled
eval_plugins auto 1 "" /mod/webif/lib/auto/plugin
# Third party
# temporary for legacy plugins
set settings $::auto::settings
set root $::auto::root
eval_plugins auto 1
unset settings root
######################################################################
# Set expected variables for legacy plugins
if {[llength $::auto::legacy]} {
::auto::log "Legacy plugins in use - $::auto::legacy" 1
set settings $::auto::settings
set root $::auto::root
alias log ::auto::log
alias scan_run ::auto::flagscan
alias scanup ::auto::autoflagscanup
}
######################################################################
# Run media flag database
::auto::log "Scanning media for flags..."
set st [clock milliseconds]
::auto::buildflagdb $::auto::root
::auto::log "Scan completed ([::auto::elapsed $st] seconds)"
::auto::log "Active flags: $::auto::activeflags"
if {$::auto::loglevel > 1} {
foreach {dir flags} $::auto::flagdb {
::auto::log "[format %-80s $dir] - $flags" 2
}
}
######################################################################
# Run plugins
set ::auto::orderedplugins [\
lsort -index end -decreasing -integer $::auto::plugins]
set __dummy ""
proc ::auto::runplugin {fn {_plugin ""} args} {
variable orderedplugins
variable legacy
foreach p $orderedplugins {
lassign $p plugin xfn priority
if {$_plugin ne "" && $plugin ne $_plugin} continue
if {$fn eq "run"} {
set rfn $xfn
} else {
set rfn "::${plugin}::$fn"
}
if {[exists -proc $rfn] || [exists -alias $rfn]} {
set st [clock milliseconds]
log [string repeat * 56] 2
log "*********> $rfn (Priority $priority)" 1
if {$plugin in $legacy && $fn ne "rundir"} {
set call [list $rfn __dummy]
} else {
set call [list $rfn $args]
}
set ologprefix $::auto::logprefix
set ::auto::logprefix "$plugin:$::auto::logprefix"
set ret [catch {uplevel 1 {*}$call} msg]
set ::auto::logprefix $ologprefix
if {$ret} {
log "$rfn: $msg" 0
lassign [info stacktrace] p f l
log " $f:$l @ $p" 0
}
log "<********* $rfn ([elapsed $st] seconds)" 1
}
}
}
set ::auto::passes {init run cleanup}
if {[lindex $argv 0] eq "-singledir"} {
foreach dir [lrange $argv 1 end] {
::auto::runplugin rundir "" $dir
}
} elseif {[llength $argv] > 0} {
foreach pass $::auto::passes {
foreach arg $argv { ::auto::runplugin $pass $arg }
}
} else {
foreach pass $::auto::passes {
::auto::runplugin $pass
}
$::auto::settings autolast [clock seconds]
}
if {!$::auto::prelocked} { release_lock webif_autoscan }
foreach dir $::auto::recalcdirs {
::auto::log "Running unwatched recalculation for $dir" 2
ts resetnew $dir
}
::auto::log "Auto processing completed in [::auto::elapsed $scanstart] seconds."