zerg_support 0.0.1 → 0.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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