#!/usr/bin/tclsh # Copyright 2010 Zygo Blaxell # Released under the GNU GPL. # Usage: # duty-idle # Dependencies: # The notion of "idle" comes from the 'evtest' package and Linux event # subsystem. Search for 'evtest' in the program text. The script # iterates over /dev/input/event* and sets up an 'evtest' process # to monitor each one. # # You'll also need tclsh, if that wasn't obvious from the first line. # Theory of operation: # # When input is detected (and at startup), we execute # "killall -STOP ". # When no input is detected for milliseconds, we execute # "killall -CONT ". # # This can be used to implement a human-detecting QoS scheme which stops # and starts some processes based on whether there's a human in front of # the console. # Failure modes: # # 'killall' is racy, especially if the process name includes things like 'make' or 'cc1'. # If a process fork()s and exec()s, its name might change between the 'killall -STOP' # and 'killall -CONT', which will prevent the process from ever receiving a SIGCONT. # If someone wants to fix that (e.g. by using 'pidof' to record a PID list in stop, # and signal just those PIDs in go), patches are welcome. # # 'killall' also can't find some processes, especially scripts where # argv() gets swapped out. set ::state 1 set ::goWhen [lindex $argv 0] set ::argv [lrange $argv 1 end] set ::goAfter {} set ::fds 0 proc stop {} { if {$::state} { puts stderr "\n[clock format [clock seconds]] STOP\n" set code [catch {eval exec killall -STOP $::argv} rv] if {$code} { puts stderr "killall: $::argv: $rv" } set ::state 0 } else { puts -nonewline stderr - } after cancel $::goAfter set ::goAfter [after $::goWhen go] } proc go {} { if {!$::state} { puts stderr "\n[clock format [clock seconds]] GO\n" set code [catch {eval exec killall -CONT $::argv} rv] if {$code} { puts stderr "killall: $::argv: $rv" } set ::state 1 } else { puts -nonewline stderr + } } proc inputHandler {fd} { if {[eof $fd]} { close $fd if {![incr ::fds -1]} { set ::exit 0 } } set rv [gets $fd line] if {$rv >= 0} { stop } } foreach dev [glob /dev/input/event*] { set fd [open "|evtest $dev" r] fconfigure $fd -translation binary -buffering line -blocking 0 fileevent $fd readable [list inputHandler $fd] incr ::fds } vwait ::exit