# ACTIVESTATE TEAPOT-PKG BEGIN TM -*- tcl -*- # -- Tcl Module # @@ Meta Begin # Package odie::processman 0.3 # Meta as::build::date 2015-05-25 # Meta as::origin http://sourceforge.net/projects/tcllib # Meta license BSD # Meta platform tcl # Meta recommend Tclx # Meta recommend odielib # Meta require {cron 1.1} # Meta require twapi # @@ Meta End # ACTIVESTATE TEAPOT-PKG BEGIN REQUIREMENTS package require cron 1.1 package require twapi # ACTIVESTATE TEAPOT-PKG END REQUIREMENTS # ACTIVESTATE TEAPOT-PKG BEGIN DECLARE package provide odie::processman 0.3 # ACTIVESTATE TEAPOT-PKG END DECLARE # ACTIVESTATE TEAPOT-PKG END TM ### # IRM External Process Manager ### package provide odie::processman 0.3 package require cron 1.1 ::namespace eval ::processman {} if { $::tcl_platform(platform) eq "windows" } { package require twapi } else { ### # Try to utilize C level utilities that are bundled # with either TclX or Odielib ### if [catch {package require odielib}] { catch {package require Tclx} } if {[info command subprocess_exists] eq {}} { proc ::processman::subprocess_exists pid { set dat [exec ps] foreach line [split $dat \n] { if {![scan $line "%d %s" thispid rest]} continue if { $thispid eq $pid} { return $thispid } } return 0 } } if {[info command kill_subprocess] eq {}} { proc ::processman::kill_subprocess pid { catch {exec kill $pid} } } } if {[info command harvest_zombies] eq {}} { proc ::processman::harvest_zombies args { } } ### # topic: a0cdb7503872cd302756c732956cd5c3 # title: Periodic scan of the state of processes ### proc ::processman::events {} { variable process_binding foreach {id bind} $process_binding { if {![running $id]} { kill $id catch {eval $bind} } } } ### # topic: 95edbb845e0a8802b1cc3119516a6502 # title: Locate and executable of name ### proc ::processman::find_exe name { global tcl_platform if {$tcl_platform(platform)=="windows"} {set suffix .exe} {set suffix {}} foreach f [list $name ~/irm/bin/$name ./$name/$name ./$name ../$name/$name ../../$name/$name] { if {[file executable $f]} break append f $suffix if {[file executable $f]} break } if {![file executable $f]} { error "Cannot find the $name executable" return {} } return $f } ### # topic: ac021b1116f0c1d5e3319d9f333f0c89 # title: Kill a process ### proc ::processman::kill id { variable process_list variable process_binding global tcl_platform if {![dict exists $process_list $id]} { return } foreach pid [dict get $process_list $id] { if { $tcl_platform(platform) eq "unix" } { catch {kill_subprocess $pid} } elseif { $tcl_platform(platform) eq "windows" } { catch {::twapi::end_process $pid} } } dict set process_list $id {} dict unset process_binding $id harvest_zombies } ### # topic: 8987329d60cd1adc766e09a0227f87b6 # title: Kill all processes spawned by this program ### proc ::processman::kill_all {} { variable process_list if {![info exists process_list]} { return {} } foreach {name pidlist} $process_list { kill $name } harvest_zombies } ### # topic: 17a9014236425560140ba62bbb2745a8 # title: Kill a process ### proc ::processman::killexe name { if {$::tcl_platform(platform) eq "windows" } { set pids [twapi::get_process_ids -name $name.exe] foreach pid $pids { # Catch the error in case process does not exist any more catch {twapi::end_process $pid} } #catch {exec taskkill /F /IM $name.exe} err puts $err } else { catch {exec killall -9 $name} err ##puts $err } harvest_zombies } ### # topic: 02406b2a7edd05c887554384ad2db41f # title: Issue a command when process {$id} exits ### proc ::processman::onexit {id cmd} { variable process_binding if {![running $id]} { catch {eval $cmd} return } dict set process_binding $id $cmd } ### # topic: ee784ae29e5b66867a30428b3095dc65 # title: Changes priority of task ### proc ::processman::priority {id level} { variable process_list set pid [running $id] if {![string is integer $pid]} { set pid 0 } if {!$pid} { return } if { $::tcl_platform(platform) eq "windows" } { package require twapi switch $level { low { twapi::set_priority_class $pid 0x4000 } high { twapi::set_priority_class $pid 0x20 } default { twapi::set_priority_class $pid 0x8000 } } } else { switch $level { low { exec renice -n 10 -p $pid } high { exec renice -n -5 -p $pid } default { exec renice -n 0 -p $pid } } } } ### # topic: 8bccf62b4fa11949dba4c85e05d116e9 # title: Return a list of processes and their current state ### proc ::processman::process_list {} { variable process_list if {![info exists process_list]} { return {} } set result {} foreach {name pidlist} $process_list { foreach pid $pidlist { lappend result $name $pid [subprocess_exists $pid] } } return $result } ### # topic: 96b4b2c53ea1554006417e507197488c # title: Test if a process is running ### proc ::processman::running id { variable process_list if {![dict exists $process_list $id]} { return 0 } foreach pid [dict get $process_list $id] { if { $::tcl_platform(platform) eq "windows" } { if {[::twapi::process_exists $pid]} { return $pid } } else { if {[subprocess_exists $pid]} { return $pid } } } return 0 } ### # topic: 61694ad97dbac52351431ad0d8c448e3 # title: Launch a task in the background ### proc ::processman::spawn {id command args} { variable process_list if {[llength $command] == 1} { set command [lindex $command 0] } if {$::tcl_platform(platform) eq "windows"} { set pid [exec "$command" {*}$args &] } else { set pid [exec $command {*}$args &] } dict lappend process_list $id $pid return $pid } ### # topic: 56fbf345652c5ca18543a67a6bc95787 # title: Process Management Tools ### namespace eval ::processman { ### # initialize tables ### variable process_list variable process_binding if { ![info exists process_list]} { set process_list {} } if {![info exists process_binding]} { set process_binding {} } } ::cron::every processman 60 ::processman::events