zerg_support 0.0.5 → 0.0.8
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG +6 -0
- data/Manifest +10 -0
- data/README +0 -61
- data/RUBYFORGE +40 -0
- data/Rakefile +8 -5
- data/lib/zerg_support/event_machine/connection_mocks.rb +46 -0
- data/lib/zerg_support/event_machine/frame_protocol.rb +68 -0
- data/lib/zerg_support/event_machine/object_protocol.rb +24 -0
- data/lib/zerg_support/gems.rb +33 -9
- data/lib/zerg_support/open_ssh.rb +104 -0
- data/lib/zerg_support/process.rb +65 -23
- data/lib/zerg_support/spawn.rb +65 -6
- data/lib/zerg_support.rb +10 -0
- data/test/fork_tree.rb +34 -0
- data/test/test_connection_mocks.rb +57 -0
- data/test/test_frame_protocol.rb +119 -0
- data/test/test_gems.rb +1 -1
- data/test/test_object_protocol.rb +55 -0
- data/test/test_open_ssh.rb +125 -0
- data/test/test_process.rb +19 -9
- data/test/test_spawn.rb +44 -18
- data/zerg_support.gemspec +18 -10
- metadata +44 -6
data/CHANGELOG
CHANGED
@@ -1,3 +1,9 @@
|
|
1
|
+
v0.0.8. Extension emulation works in Windows without a build environment.
|
2
|
+
|
3
|
+
v0.0.7. Windows support.
|
4
|
+
|
5
|
+
v0.0.6. Event Machine protocols for talking to zergling daemons.
|
6
|
+
|
1
7
|
v0.0.5. Added forgotten spawn.rb to Manifest.
|
2
8
|
|
3
9
|
v0.0.4. Polished implementation and tests for process spawning.
|
data/Manifest
CHANGED
@@ -1,5 +1,9 @@
|
|
1
1
|
CHANGELOG
|
2
|
+
lib/zerg_support/event_machine/connection_mocks.rb
|
3
|
+
lib/zerg_support/event_machine/frame_protocol.rb
|
4
|
+
lib/zerg_support/event_machine/object_protocol.rb
|
2
5
|
lib/zerg_support/gems.rb
|
6
|
+
lib/zerg_support/open_ssh.rb
|
3
7
|
lib/zerg_support/process.rb
|
4
8
|
lib/zerg_support/spawn.rb
|
5
9
|
lib/zerg_support.rb
|
@@ -7,6 +11,12 @@ LICENSE
|
|
7
11
|
Manifest
|
8
12
|
Rakefile
|
9
13
|
README
|
14
|
+
RUBYFORGE
|
15
|
+
test/fork_tree.rb
|
16
|
+
test/test_connection_mocks.rb
|
17
|
+
test/test_frame_protocol.rb
|
10
18
|
test/test_gems.rb
|
19
|
+
test/test_object_protocol.rb
|
20
|
+
test/test_open_ssh.rb
|
11
21
|
test/test_process.rb
|
12
22
|
test/test_spawn.rb
|
data/README
CHANGED
@@ -1,61 +0,0 @@
|
|
1
|
-
INSTALL
|
2
|
-
-
|
3
|
-
|
4
|
-
on server and local machine:
|
5
|
-
|
6
|
-
`gem sources -a http://gems.github.com`
|
7
|
-
|
8
|
-
`sudo gem install coderrr-rtunnel`
|
9
|
-
|
10
|
-
If you don't have root access on server, you can use either the rtunnel_server_linux binary (only works with linux), or extract the .tar.gz and use `rtunnel_server.rb` (all function the same)
|
11
|
-
|
12
|
-
USAGE
|
13
|
-
-
|
14
|
-
|
15
|
-
on server (myserver.com):
|
16
|
-
|
17
|
-
`rtunnel_server`
|
18
|
-
|
19
|
-
on your local machine:
|
20
|
-
|
21
|
-
`rtunnel_client -c myserver.com -f 4000 -t 3000`
|
22
|
-
|
23
|
-
This would reverse tunnel myserver.com:4000 to localhost:3000 so that if you had a web server running at port 3000 on your local machine, anyone on the internet could access it by going to http://myserver.com:4000
|
24
|
-
|
25
|
-
**News**
|
26
|
-
|
27
|
-
* 0.3.6 released, new protocol
|
28
|
-
* created gem for easier installation
|
29
|
-
* 0.2.1 released, minor bugfix, cmdline options change
|
30
|
-
* 0.2.0 released, much simpler
|
31
|
-
* 0.1.2 released
|
32
|
-
* Created rtunnel_server binary for linux so you don't need Ruby installed on the host you want to reverse tunnel from
|
33
|
-
* 0.1.1 released
|
34
|
-
* Added default control port of 19050, no longer have to specify this on client or server unless you care to change it
|
35
|
-
|
36
|
-
RTunnel?
|
37
|
-
-
|
38
|
-
|
39
|
-
This client/server allow you to reverse tunnel traffic. Reverse tunneling is useful if you want to run a server behind a NAT and you do not have the ability use port forwarding. The specific reason I created this program was to reduce the pain of Facebook App development on a crappy internet connection that drops often. ssh -R was not cutting it.
|
40
|
-
|
41
|
-
**How does reverse tunneling work?**
|
42
|
-
|
43
|
-
* tunnel\_client makes connection to tunnel\_server (through NAT)
|
44
|
-
* tunnel_server listens on port X
|
45
|
-
* internet_user connects to port X on tunnel server
|
46
|
-
* tunnel\_server uses existing connection to tunnel internet user's request back to tunnel\_client
|
47
|
-
* tunnel_client connects to local server on port Y
|
48
|
-
* tunnel_client tunnels internet users connection through to local server
|
49
|
-
|
50
|
-
or:
|
51
|
-
|
52
|
-
* establish connection: tunnel\_client --NAT--> tunnel\_server
|
53
|
-
* reverse tunnel: internet\_user -> tunnel_server --(NAT)--> tunnel\_client -> server\_running\_behind\_nat
|
54
|
-
|
55
|
-
**How is this different than normal tunneling?**
|
56
|
-
|
57
|
-
With tunneling, usually your connections are made in the same direction you create the tunnel connection. With reverse tunneling, you tunnel your connections the opposite direction of which you made the tunnel connection. So you initiate the tunnel with A -> B, but connections are tunneled from B -> A.
|
58
|
-
|
59
|
-
**Why not just use ssh -R?**
|
60
|
-
|
61
|
-
The same thing can be achieved with ssh -R, why not just use it? A lot of ssh servers don't have the GatewayPorts sshd option set up to allow you to reverse tunnel. If you are not in control of the server and it is not setup correctly then you are SOL. RTunnel does not require you are in control of the server. ssh -R has other annoyances. When your connection drops and you try to re-initiate the reverse tunnel sometimes you get an address already in use error because the old tunnel process is still laying around. This requires you to kill the existing sshd process. RTunnel does not have this problem.
|
data/RUBYFORGE
ADDED
@@ -0,0 +1,40 @@
|
|
1
|
+
Quickstart for Rubyforge:
|
2
|
+
|
3
|
+
1) Get the code
|
4
|
+
git clone git@github.com:costan/zerg_support.git
|
5
|
+
|
6
|
+
2) Install the rubyforge gem
|
7
|
+
gem install rubyforge
|
8
|
+
|
9
|
+
3) Save your rubyforge.org login information
|
10
|
+
rubyforge setup
|
11
|
+
|
12
|
+
4) Get a login cookie
|
13
|
+
rubyforge login
|
14
|
+
|
15
|
+
5) Get project configuration from rubyforge
|
16
|
+
rubyforge config zerglings
|
17
|
+
|
18
|
+
6) Create a package to release under
|
19
|
+
rubyforge create_package zerglings zerg_support
|
20
|
+
|
21
|
+
7) Install the echoe gem (required for building this gem)
|
22
|
+
gem install echoe
|
23
|
+
|
24
|
+
8) Release the gem (finally!)
|
25
|
+
rake release
|
26
|
+
|
27
|
+
Releasing a new gemspec to Github
|
28
|
+
|
29
|
+
1) Build the gem
|
30
|
+
rake package
|
31
|
+
|
32
|
+
2) Copy the spec
|
33
|
+
cp pkg/zerg_support-*/zerg_support.gemspec .
|
34
|
+
|
35
|
+
3) Commit the spec
|
36
|
+
git add zerg_support.gemspec
|
37
|
+
git commit -m "New gemspec, for Github distribution."
|
38
|
+
|
39
|
+
4) Push to Github
|
40
|
+
git push
|
data/Rakefile
CHANGED
@@ -3,18 +3,21 @@ gem 'echoe'
|
|
3
3
|
require 'echoe'
|
4
4
|
|
5
5
|
Echoe.new('zerg_support') do |p|
|
6
|
-
p.project = '
|
6
|
+
p.project = 'zerglings' # rubyforge project
|
7
7
|
|
8
8
|
p.author = 'Victor Costan'
|
9
9
|
p.email = 'victor@zergling.net'
|
10
|
-
p.summary = 'Support libraries used by
|
10
|
+
p.summary = 'Support libraries used by Zergling.Net deployment code.'
|
11
11
|
p.url = 'http://www.zergling.net'
|
12
12
|
|
13
|
-
p.need_tar_gz =
|
14
|
-
p.need_zip =
|
13
|
+
p.need_tar_gz = !Platform.windows?
|
14
|
+
p.need_zip = !Platform.windows?
|
15
15
|
p.rdoc_pattern = /^(lib|bin|tasks|ext)|^BUILD|^README|^CHANGELOG|^TODO|^LICENSE|^COPYING$/
|
16
16
|
|
17
|
-
p.development_dependencies = ["
|
17
|
+
p.development_dependencies = ["echoe >=3.0.2",
|
18
|
+
"event_machine >=0.12.2",
|
19
|
+
"flexmock >=0.8.3",
|
20
|
+
]
|
18
21
|
end
|
19
22
|
|
20
23
|
if $0 == __FILE__
|
@@ -0,0 +1,46 @@
|
|
1
|
+
# Mocks the sending end of an EventMachine connection.
|
2
|
+
# The sent data is concatenated in a string available by calling #string.
|
3
|
+
class Zerg::Support::EventMachine::SendMock
|
4
|
+
attr_reader :string
|
5
|
+
|
6
|
+
def initialize
|
7
|
+
@string = ''
|
8
|
+
end
|
9
|
+
|
10
|
+
def send_data(data)
|
11
|
+
@string << data
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
# Mocks the receiving end of an EventMachine connection.
|
16
|
+
# The data to be received is passed as an array of strings to the constructor.
|
17
|
+
# Calling #replay mocks receiving the data.
|
18
|
+
class Zerg::Support::EventMachine::ReceiveMock
|
19
|
+
attr_accessor :strings
|
20
|
+
attr_accessor :objects
|
21
|
+
|
22
|
+
def initialize(strings = [''])
|
23
|
+
@strings = strings
|
24
|
+
@objects = []
|
25
|
+
end
|
26
|
+
|
27
|
+
# Simulates receiving all the given strings as data from Event Machine.
|
28
|
+
def replay
|
29
|
+
@strings.each { |str| receive_data str }
|
30
|
+
self
|
31
|
+
end
|
32
|
+
|
33
|
+
#:nodoc:
|
34
|
+
def receive__object(object)
|
35
|
+
@objects << object
|
36
|
+
end
|
37
|
+
|
38
|
+
# Declares the name of the object to be received. For instance, a frame
|
39
|
+
# protocol would use :frame for name. This generates a receive_frame method,
|
40
|
+
# and a frames accessor.
|
41
|
+
def self.object_name(name)
|
42
|
+
alias_method "receive_#{name}".to_sym, :receive__object
|
43
|
+
return if name == :object
|
44
|
+
alias_method "#{name}s".to_sym, :objects
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
#:nodoc: namespace
|
2
|
+
module Zerg::Support::EventMachine
|
3
|
+
|
4
|
+
# Event Machine protocol for sending and receiving discrete-sized frames
|
5
|
+
module FrameProtocol
|
6
|
+
#:nodoc: This is called by Event Machine when TCP stream data is available.
|
7
|
+
def receive_data(data)
|
8
|
+
@frame_protocol_varsize ||= ''
|
9
|
+
|
10
|
+
i = 0
|
11
|
+
loop do
|
12
|
+
while @frame_protocol_buffer.nil? and i < data.size
|
13
|
+
@frame_protocol_varsize << data[i]
|
14
|
+
if (data[i] & 0x80) == 0
|
15
|
+
@frame_protocol_bytes_left =
|
16
|
+
FrameProtocol.decode_natural @frame_protocol_varsize
|
17
|
+
@frame_protocol_buffer = ''
|
18
|
+
end
|
19
|
+
i += 1
|
20
|
+
end
|
21
|
+
|
22
|
+
return if @frame_protocol_buffer.nil?
|
23
|
+
break if @frame_protocol_bytes_left > data.size - i
|
24
|
+
|
25
|
+
receive_frame @frame_protocol_buffer + data[i, @frame_protocol_bytes_left]
|
26
|
+
@frame_protocol_varsize, @frame_protocol_buffer = '', nil
|
27
|
+
i += @frame_protocol_bytes_left
|
28
|
+
end
|
29
|
+
|
30
|
+
@frame_protocol_buffer << data[i..-1]
|
31
|
+
@frame_protocol_bytes_left -= data.size-i
|
32
|
+
end
|
33
|
+
|
34
|
+
# Override to process incoming frames.
|
35
|
+
def receive_frame(frame_data); end
|
36
|
+
|
37
|
+
# Sends a frame via the underlying Event Machine TCP stream.
|
38
|
+
def send_frame(frame_data)
|
39
|
+
encoded_length = FrameProtocol.encode_natural(frame_data.length)
|
40
|
+
send_data encoded_length + frame_data
|
41
|
+
end
|
42
|
+
|
43
|
+
#:nodoc: Encodes a natural (non-negative) integer into a string.
|
44
|
+
def self.encode_natural(number)
|
45
|
+
string = ''
|
46
|
+
loop do
|
47
|
+
number, byte = number.divmod(0x80)
|
48
|
+
string << (byte | ((number > 0) ? 0x80 : 0x00))
|
49
|
+
break if number == 0
|
50
|
+
end
|
51
|
+
string
|
52
|
+
end
|
53
|
+
|
54
|
+
#:nodoc: Decodes a natural (non-negative) integer from a string.
|
55
|
+
def self.decode_natural(string)
|
56
|
+
number = 0
|
57
|
+
multiplier = 1
|
58
|
+
string.each_byte do |byte|
|
59
|
+
more, number_bits = byte.divmod 0x80
|
60
|
+
number += number_bits * multiplier
|
61
|
+
break if more == 0
|
62
|
+
multiplier *= 0x80
|
63
|
+
end
|
64
|
+
return number
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
end # namespace Zerg::Support::EventMachine
|
@@ -0,0 +1,24 @@
|
|
1
|
+
require 'yaml'
|
2
|
+
|
3
|
+
#:nodoc: namespace
|
4
|
+
module Zerg::Support::EventMachine
|
5
|
+
|
6
|
+
# Event Machine protocol for sending serializable objects.
|
7
|
+
module ObjectProtocol
|
8
|
+
include FrameProtocol
|
9
|
+
|
10
|
+
# Send a serialized object.
|
11
|
+
def send_object(object)
|
12
|
+
send_frame YAML.dump(object)
|
13
|
+
end
|
14
|
+
|
15
|
+
#:nodoc: Processes an incoming frame and de-serializes the object in it.
|
16
|
+
def receive_frame(frame_data)
|
17
|
+
receive_object YAML.load(frame_data)
|
18
|
+
end
|
19
|
+
|
20
|
+
# Override to process incoming objects.
|
21
|
+
def receive_object(object); end
|
22
|
+
end
|
23
|
+
|
24
|
+
end # namespace Zerg::Support::EventMachine
|
data/lib/zerg_support/gems.rb
CHANGED
@@ -2,23 +2,42 @@
|
|
2
2
|
module Zerg::Support::Gems
|
3
3
|
# called by ensure_on_path for Windows systems
|
4
4
|
def self.ensure_on_windows_path(bin_file)
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
5
|
+
bat_file = File.expand_path(File.join(ENV["WINDIR"],
|
6
|
+
File.basename(bin_file) + ".bat"))
|
7
|
+
begin
|
8
|
+
File.open(bat_file, 'w') do |f|
|
9
|
+
f.write <<END_BATCH
|
10
|
+
@ECHO OFF
|
11
|
+
IF NOT "%~f0" == "~f0" GOTO :WinNT
|
12
|
+
@"ruby.exe" "#{File.expand_path(bin_file)}" %1 %2 %3 %4 %5 %6 %7 %8 %9
|
13
|
+
GOTO :EOF
|
14
|
+
:WinNT
|
15
|
+
@"ruby.exe" "#{File.expand_path(bin_file)}" %*
|
16
|
+
END_BATCH
|
17
|
+
end
|
18
|
+
#rescue
|
19
|
+
# if anything goes wrong we probably don't have permissions (hi Vista?)
|
20
|
+
end
|
9
21
|
end
|
10
22
|
|
11
23
|
# called by ensure_on_path for UNIX systems
|
12
24
|
def self.ensure_on_unix_path(bin_file)
|
13
25
|
path = "/usr/bin/#{File.basename bin_file}"
|
14
|
-
|
15
|
-
|
16
|
-
|
26
|
+
begin
|
27
|
+
# using a link so the gem can be updated and the link still works
|
28
|
+
FileUtils.ln_s(bin_file, path, :force)
|
29
|
+
rescue
|
30
|
+
# if anything goes wrong we probably don't have permissions
|
31
|
+
# oh well at least we tried
|
32
|
+
end
|
17
33
|
end
|
18
34
|
|
19
35
|
# ensures that bin_file can be invoked from a shell
|
20
36
|
def self.ensure_on_path(bin_script)
|
21
|
-
|
37
|
+
caller_trace = Kernel.caller.first
|
38
|
+
caller_match = /^(.*)\:\d+\:in /.match(caller_trace) ||
|
39
|
+
/^(.*)\:\d+$/.match(caller_trace)
|
40
|
+
bin_file = File.expand_path caller_match[1] + '/../../../bin/' + bin_script
|
22
41
|
# this is a cheat to get the binary in the right place on stubborn Debians
|
23
42
|
if RUBY_PLATFORM =~ /win/ and RUBY_PLATFORM !~ /darwin/
|
24
43
|
ensure_on_windows_path bin_file
|
@@ -30,7 +49,12 @@ module Zerg::Support::Gems
|
|
30
49
|
# tricks rubygems into believeing that the extension compiled and worked out
|
31
50
|
def self.emulate_extension_install(extension_name)
|
32
51
|
File.open('Makefile', 'w') { |f| f.write "all:\n\ninstall:\n\n" }
|
52
|
+
File.open('make', 'w') do |f|
|
53
|
+
f.write '#!/bin/sh'
|
54
|
+
f.chmod f.stat.mode | 0111
|
55
|
+
end
|
33
56
|
File.open(extension_name + '.so', 'w') {}
|
34
57
|
File.open(extension_name + '.dll', 'w') {}
|
58
|
+
File.open('nmake.bat', 'w') { |f| }
|
35
59
|
end
|
36
|
-
end
|
60
|
+
end
|
@@ -0,0 +1,104 @@
|
|
1
|
+
require 'base64'
|
2
|
+
require 'openssl'
|
3
|
+
|
4
|
+
# Tools for managing openssh cryptographic material
|
5
|
+
module Zerg::Support::OpenSSH
|
6
|
+
# Extracts the keys from a file of known_hosts format.
|
7
|
+
def self.known_hosts_keys(io)
|
8
|
+
io.each_line do |line|
|
9
|
+
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
# The components in a openssh .pub / known_host RSA public key.
|
14
|
+
RSA_COMPONENTS = ['ssh-rsa', :e, :n]
|
15
|
+
# The components in a openssh .pub / known_host DSA public key.
|
16
|
+
DSA_COMPONENTS = ['ssh-dss', :p, :q, :g, :pub_key]
|
17
|
+
|
18
|
+
# Encodes a key's public part in the format found in .pub & known_hosts files.
|
19
|
+
def self.encode_pubkey(key)
|
20
|
+
case key
|
21
|
+
when OpenSSL::PKey::RSA
|
22
|
+
components = RSA_COMPONENTS
|
23
|
+
when OpenSSL::PKey::DSA
|
24
|
+
components = DSA_COMPONENTS
|
25
|
+
else
|
26
|
+
raise "Unsupported key type #{key.class.name}"
|
27
|
+
end
|
28
|
+
components.map! { |c| c.kind_of?(Symbol) ? encode_mpi(key.send(c)) : c }
|
29
|
+
# ruby tries to be helpful and adds new lines every 60 bytes :(
|
30
|
+
[pack_pubkey_components(components)].pack('m').gsub("\n", '')
|
31
|
+
end
|
32
|
+
|
33
|
+
# Decodes an openssh public key from the format of .pub & known_hosts files.
|
34
|
+
def self.decode_pubkey(string)
|
35
|
+
components = unpack_pubkey_components Base64.decode64(string)
|
36
|
+
case components.first
|
37
|
+
when RSA_COMPONENTS.first
|
38
|
+
ops = RSA_COMPONENTS.zip components
|
39
|
+
key = OpenSSL::PKey::RSA.new
|
40
|
+
when DSA_COMPONENTS.first
|
41
|
+
ops = DSA_COMPONENTS.zip components
|
42
|
+
key = OpenSSL::PKey::DSA.new
|
43
|
+
else
|
44
|
+
raise "Unsupported key type #{components.first}"
|
45
|
+
end
|
46
|
+
ops.each do |o|
|
47
|
+
next unless o.first.kind_of? Symbol
|
48
|
+
key.send "#{o.first}=", decode_mpi(o.last)
|
49
|
+
end
|
50
|
+
return key
|
51
|
+
end
|
52
|
+
|
53
|
+
# Loads a serialized key from an IO instance (File, StringIO).
|
54
|
+
def self.load_key(io)
|
55
|
+
key_from_string io.read
|
56
|
+
end
|
57
|
+
|
58
|
+
# Reads a serialized key from a string.
|
59
|
+
def self.key_from_string(serialized_key)
|
60
|
+
header = first_line serialized_key
|
61
|
+
if header.index 'RSA'
|
62
|
+
OpenSSL::PKey::RSA.new serialized_key
|
63
|
+
elsif header.index 'DSA'
|
64
|
+
OpenSSL::PKey::DSA.new serialized_key
|
65
|
+
else
|
66
|
+
raise 'Unknown key type'
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
# Extracts the first line of a string.
|
71
|
+
def self.first_line(string)
|
72
|
+
string[0, string.index(/\r|\n/) || string.len]
|
73
|
+
end
|
74
|
+
|
75
|
+
# Unpacks the string components in an openssh-encoded pubkey.
|
76
|
+
def self.unpack_pubkey_components(str)
|
77
|
+
cs = []
|
78
|
+
i = 0
|
79
|
+
while i < str.length
|
80
|
+
len = str[i, 4].unpack('N').first
|
81
|
+
cs << str[i + 4, len]
|
82
|
+
i += 4 + len
|
83
|
+
end
|
84
|
+
return cs
|
85
|
+
end
|
86
|
+
|
87
|
+
# Packs string components into an openssh-encoded pubkey.
|
88
|
+
def self.pack_pubkey_components(strings)
|
89
|
+
(strings.map { |s| [s.length].pack('N') }).zip(strings).flatten.join
|
90
|
+
end
|
91
|
+
|
92
|
+
# Decodes an openssh-mpi-encoded integer.
|
93
|
+
def self.decode_mpi(mpi_str)
|
94
|
+
mpi_str.unpack('C*').inject(0) { |acc, c| (acc << 8) | c }
|
95
|
+
end
|
96
|
+
|
97
|
+
# Encodes an openssh-mpi-encoded integer.
|
98
|
+
def self.encode_mpi(n)
|
99
|
+
chars, n = [], n.to_i
|
100
|
+
chars << (n & 0xff) and n >>= 8 while n != 0
|
101
|
+
chars << 0 if chars.empty? or chars.last >= 0x80
|
102
|
+
chars.reverse.pack('C*')
|
103
|
+
end
|
104
|
+
end
|
data/lib/zerg_support/process.rb
CHANGED
@@ -4,25 +4,52 @@ require 'time'
|
|
4
4
|
module Zerg::Support::Process
|
5
5
|
@@no_multiple_pids = false
|
6
6
|
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
7
|
+
unless RUBY_PLATFORM =~ /win/ and RUBY_PLATFORM !~ /darwin/
|
8
|
+
# Translates process info given by Sys::ProcTable into our own nicer format.
|
9
|
+
def self.xlate_process_info(low_info)
|
10
|
+
{
|
11
|
+
:pid => low_info.pid,
|
12
|
+
:parent_pid => low_info.ppid,
|
13
|
+
:real_uid => low_info.ruid || -1,
|
14
|
+
:real_gid => low_info.rgid || -1,
|
15
|
+
:start_time => low_info.start,
|
16
|
+
:nice => low_info.nice || 0,
|
17
|
+
:priority => low_info.priority || 0,
|
18
|
+
:syscall_priority => low_info.user_priority || 0,
|
19
|
+
:resident_size => (low_info.id_rss || 0) * 1024,
|
20
|
+
:code_size => (low_info.ix_rss || 0) * 1024,
|
21
|
+
:virtual_size => (low_info.is_rss || 0) * 1024,
|
22
|
+
:percent_cpu => low_info.pctcpu,
|
23
|
+
:percent_ram => low_info.pctmem,
|
24
|
+
:state => low_info.state,
|
25
|
+
:command_line => low_info.cmdline
|
26
|
+
}
|
27
|
+
end
|
28
|
+
|
29
|
+
else
|
30
|
+
#:nodoc:
|
31
|
+
def self.xlate_process_info(low_info)
|
32
|
+
state = low_info.execution_state.to_s
|
33
|
+
state << 's' if low_info.session_id == 0
|
34
|
+
|
35
|
+
{
|
36
|
+
:pid => low_info.pid,
|
37
|
+
:parent_pid => low_info.ppid,
|
38
|
+
:real_uid => -1,
|
39
|
+
:real_gid => -1,
|
40
|
+
:start_time => low_info.creation_date,
|
41
|
+
:nice => 0,
|
42
|
+
:priority => low_info.priority || 0,
|
43
|
+
:syscall_priority => 0,
|
44
|
+
:resident_size => low_info.working_set_size || 0,
|
45
|
+
:code_size => 0,
|
46
|
+
:virtual_size => low_info.virtual_size || 0,
|
47
|
+
:percent_cpu => 0,
|
48
|
+
:percent_ram => 0,
|
49
|
+
:state => state,
|
50
|
+
:command_line => low_info.cmdline || low_info.comm || ''
|
51
|
+
}
|
52
|
+
end
|
26
53
|
end
|
27
54
|
|
28
55
|
# Collects information about a single process. Returns nil
|
@@ -81,13 +108,26 @@ module Zerg::Support::Process
|
|
81
108
|
return proc_queue
|
82
109
|
end
|
83
110
|
|
111
|
+
|
112
|
+
if RUBY_PLATFORM =~ /win/ and RUBY_PLATFORM !~ /darwin/
|
113
|
+
#:nodoc: Wrapper around Process.kill that works on all platforms.
|
114
|
+
def self.kill_primitive(pid, force = false)
|
115
|
+
Process.kill(force ? 9 : 4, pid)
|
116
|
+
end
|
117
|
+
else
|
118
|
+
#:nodoc: Wrapper around Process.kill that works on all platforms.
|
119
|
+
def self.kill_primitive(pid, force = false)
|
120
|
+
Process.kill(force ? 'KILL' : 'TERM', pid)
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
84
124
|
# Kills the process with the given pid.
|
85
125
|
def self.kill(pid)
|
86
126
|
begin
|
87
|
-
|
127
|
+
self.kill_primitive pid, false
|
88
128
|
Thread.new(pid) do |victim_pid|
|
89
129
|
Kernel.sleep 0.2
|
90
|
-
|
130
|
+
self.kill_primitive pid, true
|
91
131
|
end
|
92
132
|
rescue
|
93
133
|
# we probably don't have the right to kill the process
|
@@ -107,7 +147,9 @@ end
|
|
107
147
|
## Backend for process information listing
|
108
148
|
|
109
149
|
begin
|
110
|
-
raise 'Use ps' unless
|
150
|
+
raise 'Use ps' unless RUBY_PLATFORM =~ /win/ and RUBY_PLATFORM !~ /darwin/
|
151
|
+
|
152
|
+
require 'time'
|
111
153
|
require 'sys/proctable'
|
112
154
|
rescue Exception
|
113
155
|
# Emulate the sys-proctable gem using the ps command.
|
@@ -171,4 +213,4 @@ rescue Exception
|
|
171
213
|
return pids.length == 1 ? retval.first : retval
|
172
214
|
end
|
173
215
|
end
|
174
|
-
end
|
216
|
+
end
|