File: processman.tcl

package info (click to toggle)
tcllib 1.20%2Bdfsg-1
  • links: PTS
  • area: main
  • in suites: bullseye
  • size: 68,064 kB
  • sloc: tcl: 216,842; ansic: 14,250; sh: 2,846; xml: 1,766; yacc: 1,145; pascal: 881; makefile: 107; perl: 84; f90: 84; python: 33; ruby: 13; php: 11
file content (344 lines) | stat: -rw-r--r-- 7,332 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
###
# IRM External Process Manager
###

package require cron 2.0

::namespace eval ::processman {}

###
# Attempt to locate some C - API helpers
###
set ::processman::api tcl
foreach {command package api} {
  {::twapi::process_exists} twapi    twapi
  umask                     tclx     tclx
  subprocess_exists         tclextra tclextra
  {}                        odielibc tclextra
} {
  if {[info commands $command] ne {}} {
    set ::processman::api $api
    break
  }
  if {![catch {package require $package}]} {
    set ::processman::api $api
    break
  }
}

switch $api {
tclx {
proc ::processman::kill_subprocess pid {
  catch {::kill $pid}
}
}
tclextra {
proc ::processman::kill_subprocess pid {
  catch {::kill_subprocess $pid}
}

}
twapi {
  
proc ::processman::priority {id level} {
  foreach pid [PIDLIST $id] {
    switch $level {
      background {
        if  {[catch {twapi::set_priority_class $pid 0x00104000} err]} {
          puts "BG Mode failed - $err"
          twapi::set_priority_class $pid 0x00004000
        }
      }
      low {
        twapi::set_priority_class $pid 0x00004000
      }
      high {
        twapi::set_priority_class $pid 0x00000020
      }
      default {
        twapi::set_priority_class $pid 0x00008000
      }
    }
  }
}
proc ::processman::killexe name {
  set pids [twapi::get_process_ids -name $name.exe]
  foreach pid $pids {
    # Catch the error in case process does not exist any more
    if {[catch {twapi::end_process $pid} err]} {
      puts $err
    }
  }
  #catch {exec taskkill /F /IM $name.exe} err
  #puts $err
}
proc ::processman::kill_subprocess pid {
  if {[catch {::twapi::end_process $pid} err]} {
    puts $err
  }
}
proc ::processman::subprocess_exists pid {
  return [::twapi::process_exists $pid]
}
proc ::processman::keep_machine_awake {truefalse} {
  if {[string is true -strict $truefalse]} {
    twapi::SetThreadExecutionState 0x80000040
  } else {
    twapi::SetThreadExecutionState 0x00000000
  }
}
}
default {}
}

###
# Create fallback implementations for functions we don't have a
# C API call for
###

proc ::processman::fallback {name arglist body} {
  if {[info commands ::${name}] eq {} && [info commands ::processman::${name}] eq {} } {
    ::proc ::processman::${name} $arglist $body
  }

}

# title: Keep the machine from going to sleep
::processman::fallback keep_machine_awake {truefalse} {
}

::processman::fallback killexe name {
  if {[catch {exec killall -9 $name} err]} {
    puts $err
  }
  harvest_zombies
}

###
# title: Detect a running process
# usage: subprocess_exists PID
# description:
#  Returns true if PID is running. If PID is an integer
#  it is interpreted as Process Id from the operating system.
#  Otherwise it is assumed to be a handle previously registered
#  with the processman package
###
::processman::fallback 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
}

# title: Changes priority of task
::processman::fallback priority {id level} {
  if {$::tcl_platform(platform) eq "windows"} {
    return
  }
  foreach pid [PIDLIST $id] {
    switch $level {
      background {
        exec renice -n 20 -p $pid
      }
      low {
        exec renice -n 10 -p $pid
      }
      high {
        exec renice -n -5 -p $pid
      }
      default {
        exec renice -n 0 -p $pid
      }
    }
  }
}

::processman::fallback kill_subprocess pid {
  catch {exec kill $pid}
}

::processman::fallback 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
}

proc ::processman::PIDLIST id {
  variable process_list
  if {[string is integer -strict $id]} {
    return $id
  }
  if {[dict exists $process_list $id]} {
    return [dict get $process_list $id]
  }
  return {}
}

###
# topic: ac021b1116f0c1d5e3319d9f333f0c89
# title: Kill a process
###
proc ::processman::kill id {
  variable process_list
  variable process_binding
  global tcl_platform
  foreach pid [PIDLIST $id] {
    kill_subprocess $pid
  }
  if {![string is integer $id]} {
    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: 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: 8bccf62b4fa11949dba4c85e05d116e9
# title: Return a list of processes and their current state
###
proc ::processman::process_list {} {
  variable process_list
  set result {}
  dict set result self [pid]
  if {![info exists process_list]} {
    return $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
  set pidlist {}
  if {![string is integer -strict $id]} {
    if {$id eq "self"} {
      return [pid]
    }
    if {![dict exists $process_list $id]} {
      return 0
    }
    set pidlist [dict get $process_list $id]
  } else {
    set pidlist $id
  }
  foreach pid $pidlist {
    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

package provide odie::processman 0.5
package provide processman 0.5