#!/usr/bin/tclsh set ::debug 0 set ::lockfile /var/lock/owfs/oweventd set ::tryrestart 3 set ::waitresetexiterror 60 proc logger {n msg {priority err}} { syslog -facility daemon -ident "oweventd\($n\)" $priority $msg # if {$::debug} {puts stderr "$msg"} } proc MakeLock {} { global errorCode set tmplock "$::lockfile.[pid]" if {[catch {open $tmplock w} lck]} { logger 0 "Cannot create tmp lock file $tmplock. $lck" "err" return 0 } puts $lck [pid] close $lck while {1} { if {[catch {link $tmplock $::lockfile} msg] == 0} { file delete -force $tmplock return 1 } else { if {[lindex $errorCode 1] != "EEXIST"} { logger 0 "Cannot create lock file $::lockfile. $msg" "err" file delete -force $tmplock return 0; } else { if {[catch {open $::lockfile r} lck]} { logger 0 "Cannot read existing lock file $::lockfile. $lck" "err" file delete -force $tmplock return 0; } set pid [read -nonewline $lck] close $lck if {[catch {kill 0 $pid} msg]} { if {[lindex $errorCode 1] == "ESRCH"} { logger 0 "Warning: delete unused lock file." "err" file delete -force $::lockfile continue } } } } } file delete -force $tmplock logger 0 "Already running." "err" return 0 } proc ReadConf {n} { logger $n "Load [lindex $::FileConf $n]" info if {[catch {open [lindex $::FileConf $n]} cfg]} { logger 0 $cfg err return 0 } catch {unset ::Config} set ::Config(mtime) [file mtime [lindex $::FileConf $n]] set ::Config(owfs) "" set ::Config(ow) {} set ::Config(file) {} set ::Config(check,time) 0 set ::Config(check,empty) "" set nstr 0 while {![eof $cfg]} { incr nstr set str [string trim [gets $cfg]] if {[regexp {(^\#.*|^$)} $str]} {continue} if {[regexp -nocase {^owfs\s+(\S.*)$} $str foo v]} { set ::Config(owfs) $v } elseif {[regexp -nocase {^init\s+(\S+)\s+(\S.*)$} $str foo f c]} { set ::Config(init,$f) $c } elseif {[regexp -nocase {^event\s+ow\s+(\S+)\s+(\S+)\s+(\S.*)$} $str foo f v c]} { lappend ::Config(ow) [list $f $v $c] } elseif {[regexp -nocase {^event\s+file\s+(\S+)\s+(\S+)\s+(\S.*)$} $str foo f v c]} { lappend ::Config(file) [list $f $v $c] } elseif {[regexp -nocase {^check\s+time\s+(\S.*)$} $str foo v]} { set ::Config(check,time) $v } elseif {[regexp -nocase {^check\s+empty\s+(\S.*)$} $str foo c]} { set ::Config(check,empty) $c } else { logger 0 "Bad parameter in [lindex $::FileConf $n]:$nstr. Ignored." warning } } close $cfg return 1 } proc Exec {command} { signal ignore SIGCHLD if {[catch fork pid]} { logger "Fork ERROR ($command): $pid" err return 1 } if {$pid == 0} { set foo [split $command] set name [lindex $foo 0] if {$::debug} {logger "Exec $command" info} if {[catch {execl -argv0 $name /bin/sh [list -c $command]} msg]} { logger "Exec ERROR ($command): $msg" err exit 1 } exit 0 } else { return 0 } } proc owget {name} { if {[catch {open $::Config(owfs)/$name} f]} { logger owget "ERROR: $f" return "" } set res [read -nonewline $f] close $f return $res } proc owput {name {val ""}} { if {[catch {open $::Config(owfs)/$name WRONLY} f]} { logger owput "ERROR: $f" return 0 } puts $f $val close $f return 1 } proc Loop {n} { set lastcheck 0 while {1} { if {[file exists [lindex $::FileConf $n]]} { if {$::Config(mtime) < [file mtime [lindex $::FileConf $n]]} { if {[ReadConf $n] == 0} {return 1} if {($::Config(owfs) == "") || (![file isdirectory $::Config(owfs)])} { if {[llength $::Config(ow)]} { logger $n "ERROR: owfs directory not found - required for \"event ow\"." err return 1 } else { logger $n "WARNING: owfs directory not found." warning } set checkow 0 } else { set checkow 1 if {$::debug} {logger $n "checkow" info} } } } if {$checkow} { if {[file exists $::Config(owfs)/uncached]} {set ::Config(uncached) uncached/} {set ::Config(uncached) ""} set tcheck [expr $lastcheck + $::Config(check,time)] if {(($::Config(check,time)==0) && ($lastcheck==0)) || ($tcheck < [clock seconds])} { set lastcheck [clock seconds] if {$::debug} {logger $n "check" info} if {[catch {glob -tails -directory $::Config(owfs) {[0-9A-F][0-9A-F].[0-9A-F]*[0-9A-F]}} devs]} { logger $n "WARNING: $devs" warning catch {unset olddevs} if {[info exists ::Config(check,empty)]} { if {$::debug} {logger $n "check empty" info} Exec $::Config(check,empty) # if {[catch {system $::Config(check,empty)} res]} { # logger $n "Cannot execute \"$::Config(check,empty)\": $res" # return 1 # } } set nodevices 1 } else { foreach d $devs {set newdevs($d) 0} foreach d [array names olddevs] { if {![info exists newdevs($d)]} { logger $n "$d is lost." warning unset olddevs($d) } } foreach d [array names newdevs] { if {![info exists olddevs($d)]} { logger $n "$d is found." warning if {[info exists ::Config(init,$d)]} { set cmdinit $::Config(init,$d) append cmdinit " " $d logger $n "$d init" warning if {[Exec $cmdinit] == 0} {set olddevs($d) $newdevs($d)} # if {[catch {system $cmdinit} res]} { # logger $n "Cannot execute \"$cmdinit\": $res" # } else { # set olddevs($d) $newdevs($d) # } } else { set olddevs($d) $newdevs($d) } } } unset newdevs set nodevices 0 } } if {$nodevices == 0} { if {[catch {glob -tails -directory $::Config(owfs)/alarm {[0-9A-F][0-9A-F].[0-9A-F]*[0-9A-F]}} devs]} { # logger $n "WARNING: $devs" warning } else { foreach dev $devs { if {$::debug} {logger $n "alarm/$dev" info} foreach e $::Config(ow) { if {$::debug} {logger $n "string first \"$dev\" \"[lindex $e 0]\" = [string first $dev [lindex $e 0]]" info} if {[string first $dev [lindex $e 0]] != -1} { if {$::debug} {logger $n "alarm/$dev found" info} if {[regexp {^[*?]$} [lindex $e 1]]} { Exec [lindex $e 2] } else { if {[string match -nocase "*[lindex $e 1]*" [owget [lindex $e 0]]]} { Exec [lindex $e 2] } } } } } } } } foreach e $::Config(file) { if {[file exists [lindex $e 0]]} { if {[regexp {^[*?]$} [lindex $e 1]]} { Exec [lindex $e 2] } else { if {[catch {open [lindex $e 0]} f]} { logger $n "ERROR: $f" continue } set val [read $f] close $f if {[string match -nocase "*[lindex $e 1]*" $val]} { Exec [lindex $e 2] } } } } if {([info exists ::Config(wait)]) && ($::Config(wait) > 0)} { sleep $::Config(wait) } else { sleep 1 } } return 0 } proc Restart {n} { if {[catch fork pid]} { logger $n "Fork ERROR - $pid" "err" return } if {$pid == 0} { set ::Config(mtime) 0 exit [Loop $n] } else { set ::ProcList($pid) $n set ProcPid($n) $pid } } proc ResetExitError {n} { if {[info exists ::ProcExit($n)]} { logger $n "Reset exit error for $n" info set ::ProcExit($n) 0 } } # Main if {$argc == 0} { puts stderr "Usage: $argv0 config \[config\] ..." exit 1 } package require Syslog package require Tclx set ::FileConf [linsert $argv 0 ""] if {[catch fork pid]} { logger "Fork ERROR: $pid" exit 1 } elseif {$pid == 0} { if {[MakeLock] == 0} {exit 1} for {set n 1} {$n < [llength $::FileConf]} {incr n} { set ProcPid($n) 0 set ::ProcExit($n) 0 Restart $n } while {1} { if {[catch {wait -pgroup} stat]} {continue} set pid [lindex $stat 0] if {$pid <= 0} {continue} if {![info exists ::ProcList($pid)]} {continue} set st [lindex $stat 2] set n $::ProcList($pid) unset ::ProcList($pid) if {[info exists ProcPid($n)]} {unset ProcPid($n)} if {$st != 0} { # logger $n "Exit failed: $st" "err" incr ::ProcExit($n) after [expr $::waitresetexiterror * 1000] "ResetExitError $n" if {$tryrestart > 0} { if {$::ProcExit($n) > $::tryrestart} { logger $n "Too many fails - $st" "err" set ::ProcExit($n) 0 continue } } } else { set ::ProcExit($n) 0 } logger $n "Exit failed: $st" "err" Restart $n } } else { exit 0 }