All pastes #2001202 Raw Edit

Zygo - duty-idle

public text v1 · immutable
#2001202 ·published 2010-11-24 18:48 UTC
rendered paste body
#!/usr/bin/tclsh

# Copyright 2010 Zygo Blaxell <duty-idle@mailtoo.hungrycats.org>
# Released under the GNU GPL.

# Usage:
# duty-idle <interval-in-ms> <process-name-list>

# 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 <process-name-list>".
# When no input is detected for <interval-in-ms> milliseconds, we execute
# "killall -CONT <process-name-list>".
# 
# 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