zerg_support 0.0.1 → 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
data/CHANGELOG CHANGED
@@ -1 +1,3 @@
1
+ v0.0.2. Process management.
2
+
1
3
  v0.0.1. Initial release. Support code for zerg* gem installation.
data/Manifest CHANGED
@@ -1,8 +1,10 @@
1
+ CHANGELOG
1
2
  lib/zerg_support/gems.rb
3
+ lib/zerg_support/process.rb
2
4
  lib/zerg_support.rb
3
- test/test_gems.rb
4
- CHANGELOG
5
5
  LICENSE
6
- README
7
- Rakefile
8
6
  Manifest
7
+ Rakefile
8
+ README
9
+ test/test_gems.rb
10
+ test/test_process.rb
data/lib/zerg_support.rb CHANGED
@@ -7,3 +7,4 @@ module Zerg::Support
7
7
  end
8
8
 
9
9
  require 'zerg_support/gems.rb'
10
+ require 'zerg_support/process.rb'
@@ -0,0 +1,173 @@
1
+ # process management
2
+ # extends Base with process management features
3
+
4
+ require 'time'
5
+
6
+ module Zerg::Support::Process
7
+ @@no_multiple_pids = false
8
+
9
+ # Translates process info given by Sys::ProcTable into our own nicer format.
10
+ def self.xlate_process_info(low_info)
11
+ {
12
+ :pid => low_info.pid,
13
+ :parent_pid => low_info.ppid,
14
+ :real_uid => low_info.ruid || -1,
15
+ :real_gid => low_info.rgid || -1,
16
+ :start_time => low_info.start,
17
+ :nice => low_info.nice || 0,
18
+ :priority => low_info.priority || 0,
19
+ :syscall_priority => low_info.user_priority || 0,
20
+ :resident_size => low_info.id_rss || 0,
21
+ :code_size => low_info.ix_rss || 0,
22
+ :virtual_size => low_info.is_rss || 0,
23
+ :percent_cpu => low_info.pctcpu,
24
+ :percent_ram => low_info.pctmem,
25
+ :state => low_info.state,
26
+ :command_line => low_info.cmdline
27
+ }
28
+ end
29
+
30
+ # Collects information about a single process. Returns nil
31
+ def self.process_info(pid)
32
+ pinfo = Sys::ProcTable.ps pid
33
+ pinfo ? xlate_process_info(pinfo) : nil
34
+ end
35
+
36
+ # Collects information about processes with the given pids.
37
+ # Returns all processes if no list of pids is given.
38
+ def self.processes(pids = nil)
39
+ if @@no_multiple_pids and !pids.empty
40
+ pids.map { |pid| process_info pid }
41
+ else
42
+ begin
43
+ if pids
44
+ ps_result = Sys::ProcTable.ps(pids)
45
+ else
46
+ ps_result = Sys::ProcTable.ps
47
+ end
48
+ ps_result.map { |pinfo| xlate_process_info pinfo }
49
+ rescue TypeError
50
+ # we're using the real sys-proctable, and its ps doesn't like multiple
51
+ # arguments
52
+ @@no_multiple_pids = true
53
+ processes pids
54
+ end
55
+ end
56
+ end
57
+
58
+ # Collects information about processes with the given pids.
59
+ # The information is returned indexed by the processes' pids.
60
+ def self.processes_by_id(pids = nil)
61
+ retval = {}
62
+ self.processes(pids).each { |pinfo| retval[pinfo[:pid]] = pinfo }
63
+ return retval
64
+ end
65
+
66
+ # Returns information about the descendants of the process with the given pid.
67
+ def self.process_tree(*root_pids)
68
+ procs_by_ppid = {}
69
+ proc_list = self.processes_by_id
70
+ proc_list.each do |pid, pinfo|
71
+ procs_by_ppid[pinfo[:parent_pid]] ||= []
72
+ procs_by_ppid[pinfo[:parent_pid]] << pinfo
73
+ end
74
+
75
+ proc_queue = root_pids.map { |pid| proc_list[pid] }.select { |pinfo| pinfo }
76
+
77
+ index = 0
78
+ while index < proc_queue.length
79
+ pid, index = proc_queue[index][:pid], index + 1
80
+ next unless procs_by_ppid.has_key? pid
81
+ proc_queue += procs_by_ppid[pid]
82
+ end
83
+ return proc_queue
84
+ end
85
+
86
+ # Kills the process with the given pid.
87
+ def self.kill(pid)
88
+ begin
89
+ Process.kill "TERM", pid
90
+ Thread.new(pid) do |victim_pid|
91
+ Kernel.sleep 0.2
92
+ Process.kill "KILL", victim_pid
93
+ end
94
+ rescue
95
+ # we probably don't have the right to kill the process
96
+ print "#{$!.class.name}: #{$!}\n"
97
+ end
98
+ end
99
+
100
+ # Kills all the processes descending from the process with the given pid.
101
+ def self.kill_tree(root_pid)
102
+ self.process_tree(root_pid).each do |pinfo|
103
+ self.kill pinfo[:pid]
104
+ end
105
+ end
106
+ end
107
+
108
+
109
+ ## Backend for process information listing
110
+
111
+ begin
112
+ raise 'Use ps' unless RUBY_PLAYFORM =~ /win/ and RUBY_PLATFORM !~ /darwin/
113
+ require 'sys/proctable'
114
+ rescue Exception
115
+ # Emulate the sys-proctable gem using the ps command.
116
+ # This will be slower than having native syscalls, but at least it doesn't
117
+ # crash ruby. (yes, sys-proctable 0.7.6, I mean you!)
118
+
119
+ #:nodoc: all
120
+ module Sys; end
121
+
122
+ #:nodoc: all
123
+ module Sys::ProcTable
124
+ class ProcInfo
125
+ def initialize(pid, ppid, ruid, rgid, uid, gid, start, nice, rss, rssize,
126
+ text_size, vsz, user_time, total_time, pctcpu, pctmem,
127
+ priority, user_priority, state, cmdline)
128
+ @pid, @ppid, @ruid, @rgid, @uid, @gid, @nice, @priority,
129
+ @user_priority = *([pid, ppid, ruid, rgid, uid, gid, nice,
130
+ priority, user_priority].map { |s| s.to_i })
131
+ @start = Time.parse start
132
+ @ix_rss, @id_rss, @is_rss = text_size.to_f, rssize.to_f, vsz.to_f
133
+ @pctcpu, @pctmem = *([pctcpu, pctmem].map { |s| s.to_f })
134
+ @cmdline = cmdline
135
+ end
136
+ attr_reader :pid, :ppid, :ruid, :rgid, :uid, :gid, :nice, :priority,
137
+ :user_priority, :start, :ix_rss, :id_rss, :is_rss, :pctcpu,
138
+ :pctmem, :state, :cmdline
139
+ end
140
+
141
+ @@ps_root_cmdline = 'ps -o pid,ppid,ruid,rgid,uid,gid' +
142
+ ',lstart=STARTED+++++++++++++++++++++++++++++++++' +
143
+ ',nice,rss,rssize,tsiz,vsz,utime,cputime,pcpu=%CPU+,' +
144
+ ',pmem=%MEM+,pri,usrpri,state,command'
145
+
146
+ @@ps_all_cmdline = @@ps_root_cmdline + ' -A'
147
+ @@ps_some_cmdline = @@ps_root_cmdline + ' -p '
148
+
149
+ def self.ps(pid = nil)
150
+ pids = pid ? (pid.kind_of?(Enumerable) ? pid : [pid]) : []
151
+ retval = []
152
+ ps_cmdline = pids.empty? ? @@ps_all_cmdline :
153
+ @@ps_some_cmdline + pids.join(',')
154
+ ps_output = Kernel.` ps_cmdline
155
+ header_splits = nil
156
+ ps_output.each_line do |pline|
157
+ if header_splits
158
+ # result line, break it up
159
+ retval << ProcInfo.new(*(header_splits.map { |hs| pline[hs].strip }))
160
+ else
161
+ # first line, compute headers
162
+ lengths = pline.split(/\S\s/).map { |h| h.length + 2 }
163
+ header_splits, sum = [], 0
164
+ lengths.each_index do |i|
165
+ header_splits[i] = Range.new sum, sum += lengths[i], true
166
+ end
167
+ header_splits[-1] = Range.new header_splits[-1].begin, -1, false
168
+ end
169
+ end
170
+ return pids.length == 1 ? retval.first : retval
171
+ end
172
+ end
173
+ end
data/test/test_gems.rb CHANGED
@@ -11,7 +11,7 @@ class TestGems < Test::Unit::TestCase
11
11
  end
12
12
 
13
13
  def test_source_is_manually_tested
14
- golden_hash = 'f02b6c7852af883d60d4ec0c658928579e61f270'
14
+ golden_hash = '7fca02be12fb30415a492544d91f977e8de0e440'
15
15
  source_hash = hash_gems_file
16
16
 
17
17
  assert_equal golden_hash, source_hash, <<END_MESSAGE
@@ -0,0 +1,68 @@
1
+ require 'English'
2
+ require 'test/unit'
3
+
4
+ require 'zerg_support'
5
+
6
+ class TestProcess < Test::Unit::TestCase
7
+ def test_process_info
8
+ info = Zerg::Support::Process.process_info($PID)
9
+ assert info, 'Failed to find current process'
10
+
11
+ $ARGV.each do |arg|
12
+ assert info[:command_line].index(arg),
13
+ "Command line for current process does not contain argument #{arg}"
14
+ end
15
+ end
16
+
17
+ def test_process_list_contains_current_process
18
+ list = Zerg::Support::Process.processes_by_id
19
+
20
+ assert list[$PID], 'Process list does not contain current process'
21
+ end
22
+
23
+ # spawn 2^exp children, and their command lines will contain "cookie"
24
+ def spawn_sleepers(exp, cookie)
25
+ unless child_pid = fork
26
+ exp.times { fork }
27
+ exec 'ruby', '-e', 'sleep 100', '--', cookie
28
+ end
29
+ sleep 0.5
30
+ return child_pid
31
+ end
32
+
33
+ def now_cookie
34
+ "zerg_test_#{Time.now.to_f}"
35
+ end
36
+
37
+ def test_process_tree
38
+ cookie = now_cookie
39
+ root_pid = spawn_sleepers 3, cookie
40
+
41
+ tree_processes = Zerg::Support::Process.process_tree root_pid
42
+ assert_equal 8, tree_processes.length, 'Missed some processes'
43
+ assert tree_processes.all? { |info| info[:command_line].index cookie },
44
+ 'Got the wrong processes'
45
+ end
46
+
47
+ def test_kill
48
+ cookie = now_cookie
49
+ victim_pid = spawn_sleepers 0, cookie
50
+
51
+ Zerg::Support::Process.kill victim_pid
52
+ sleep 0.5
53
+ processes = Zerg::Support::Process.processes
54
+ assert_equal nil, processes[victim_pid], 'Victim was not killed'
55
+ end
56
+
57
+ def test_kill_tree
58
+ cookie = now_cookie
59
+ root_pid = spawn_sleepers 3, cookie
60
+
61
+ Zerg::Support::Process.kill_tree root_pid
62
+ sleep 0.5
63
+ processes = Zerg::Support::Process.processes
64
+ escaped = processes.select { |info| info[:command_line].index cookie }
65
+
66
+ assert_equal [], escaped, 'Some process(es) escaped'
67
+ end
68
+ end
data/zerg_support.gemspec CHANGED
@@ -1,30 +1,28 @@
1
- # -*- encoding: utf-8 -*-
2
-
3
1
  Gem::Specification.new do |s|
4
2
  s.name = %q{zerg_support}
5
- s.version = "0.0.1"
3
+ s.version = "0.0.2"
6
4
 
7
5
  s.required_rubygems_version = Gem::Requirement.new(">= 1.2") if s.respond_to? :required_rubygems_version=
8
6
  s.authors = ["Victor Costan"]
9
- s.date = %q{2008-11-12}
7
+ s.date = %q{2008-11-13}
10
8
  s.description = %q{Support libraries used by the Zerg system.}
11
9
  s.email = %q{victor@zergling.net}
12
- s.extra_rdoc_files = ["lib/zerg_support/gems.rb", "lib/zerg_support.rb", "CHANGELOG", "LICENSE", "README"]
13
- s.files = ["lib/zerg_support/gems.rb", "lib/zerg_support.rb", "test/test_gems.rb", "CHANGELOG", "LICENSE", "README", "Rakefile", "Manifest", "zerg_support.gemspec"]
10
+ s.extra_rdoc_files = ["CHANGELOG", "lib/zerg_support/gems.rb", "lib/zerg_support/process.rb", "lib/zerg_support.rb", "LICENSE", "README"]
11
+ s.files = ["CHANGELOG", "lib/zerg_support/gems.rb", "lib/zerg_support/process.rb", "lib/zerg_support.rb", "LICENSE", "Manifest", "Rakefile", "README", "test/test_gems.rb", "test/test_process.rb", "zerg_support.gemspec"]
14
12
  s.has_rdoc = true
15
13
  s.homepage = %q{http://www.zergling.net}
16
14
  s.rdoc_options = ["--line-numbers", "--inline-source", "--title", "Zerg_support", "--main", "README"]
17
15
  s.require_paths = ["lib"]
18
16
  s.rubyforge_project = %q{rails-pwnage}
19
- s.rubygems_version = %q{1.3.1}
17
+ s.rubygems_version = %q{1.2.0}
20
18
  s.summary = %q{Support libraries used by the Zerg system.}
21
- s.test_files = ["test/test_gems.rb"]
19
+ s.test_files = ["test/test_gems.rb", "test/test_process.rb"]
22
20
 
23
21
  if s.respond_to? :specification_version then
24
22
  current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
25
23
  s.specification_version = 2
26
24
 
27
- if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
25
+ if current_version >= 3 then
28
26
  s.add_development_dependency(%q<echoe>, [">= 0"])
29
27
  else
30
28
  s.add_dependency(%q<echoe>, [">= 0"])
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: zerg_support
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 0.0.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Victor Costan
@@ -9,7 +9,7 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2008-11-12 00:00:00 -05:00
12
+ date: 2008-11-13 00:00:00 -05:00
13
13
  default_executable:
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
@@ -29,20 +29,23 @@ executables: []
29
29
  extensions: []
30
30
 
31
31
  extra_rdoc_files:
32
+ - CHANGELOG
32
33
  - lib/zerg_support/gems.rb
34
+ - lib/zerg_support/process.rb
33
35
  - lib/zerg_support.rb
34
- - CHANGELOG
35
36
  - LICENSE
36
37
  - README
37
38
  files:
39
+ - CHANGELOG
38
40
  - lib/zerg_support/gems.rb
41
+ - lib/zerg_support/process.rb
39
42
  - lib/zerg_support.rb
40
- - test/test_gems.rb
41
- - CHANGELOG
42
43
  - LICENSE
43
- - README
44
- - Rakefile
45
44
  - Manifest
45
+ - Rakefile
46
+ - README
47
+ - test/test_gems.rb
48
+ - test/test_process.rb
46
49
  - zerg_support.gemspec
47
50
  has_rdoc: true
48
51
  homepage: http://www.zergling.net
@@ -71,9 +74,10 @@ required_rubygems_version: !ruby/object:Gem::Requirement
71
74
  requirements: []
72
75
 
73
76
  rubyforge_project: rails-pwnage
74
- rubygems_version: 1.3.1
77
+ rubygems_version: 1.2.0
75
78
  signing_key:
76
79
  specification_version: 2
77
80
  summary: Support libraries used by the Zerg system.
78
81
  test_files:
79
82
  - test/test_gems.rb
83
+ - test/test_process.rb