nexpose_pxgrid 0.1.2-java
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +12 -0
- data/.travis.yml +4 -0
- data/Gemfile +4 -0
- data/LICENCE.APACHE +144 -0
- data/LICENSE.MIT +21 -0
- data/README.md +49 -0
- data/RadiusSimulator.jar +0 -0
- data/Rakefile +31 -0
- data/bin/logback.xml +17 -0
- data/bin/pxnx.rb +34 -0
- data/bin/pxnx_daemon.rb +105 -0
- data/keystore.jks +0 -0
- data/lib/java_libs/FastInfoset-1.2.12.jar +0 -0
- data/lib/java_libs/commons-cli-20040117.000000.jar +0 -0
- data/lib/java_libs/commons-codec-1.7.jar +0 -0
- data/lib/java_libs/commons-io-2.4.jar +0 -0
- data/lib/java_libs/commons-lang-2.6.jar +0 -0
- data/lib/java_libs/cxf-api-2.7.3.jar +0 -0
- data/lib/java_libs/cxf-rt-bindings-xml-2.7.3.jar +0 -0
- data/lib/java_libs/cxf-rt-core-2.7.3.jar +0 -0
- data/lib/java_libs/cxf-rt-frontend-jaxrs-2.7.3.jar +0 -0
- data/lib/java_libs/cxf-rt-transports-http-2.7.3.jar +0 -0
- data/lib/java_libs/geronimo-javamail_1.4_spec-1.7.1.jar +0 -0
- data/lib/java_libs/istack-commons-runtime-2.14.jar +0 -0
- data/lib/java_libs/javax.ws.rs-api-2.0-m10.jar +0 -0
- data/lib/java_libs/jaxb-api-2.2.7.jar +0 -0
- data/lib/java_libs/jaxb-core-2.2.7-b57.jar +0 -0
- data/lib/java_libs/jaxb-impl-2.2.7-b57.jar +0 -0
- data/lib/java_libs/log4j-1.2.17.jar +0 -0
- data/lib/java_libs/log4j-rolling-appender-20131024-2017.jar +0 -0
- data/lib/java_libs/logback-classic-1.1.2.jar +0 -0
- data/lib/java_libs/logback-core-1.1.2.jar +0 -0
- data/lib/java_libs/pxgrid-core-model-1.0.0.10.jar +0 -0
- data/lib/java_libs/pxgrid-eps-client-stub-1.0.0.10.jar +0 -0
- data/lib/java_libs/pxgrid-eps-model-1.0.0.10.jar +0 -0
- data/lib/java_libs/pxgrid-gc-admin-model-1.0.0.10.jar +0 -0
- data/lib/java_libs/pxgrid-gcl-1.0.0.10.jar +0 -0
- data/lib/java_libs/pxgrid-identity-client-stub-1.0.0.10.jar +0 -0
- data/lib/java_libs/pxgrid-identity-model-1.0.0.10.jar +0 -0
- data/lib/java_libs/pxgrid-ise-model-1.0.0.10.jar +0 -0
- data/lib/java_libs/pxgrid-isemetadata-client-stub-1.0.0.10.jar +0 -0
- data/lib/java_libs/pxgrid-isemetadata-model-1.0.0.10.jar +0 -0
- data/lib/java_libs/pxgrid-net-model-1.0.0.10.jar +0 -0
- data/lib/java_libs/pxgrid-netcapture-model-1.0.0.10.jar +0 -0
- data/lib/java_libs/pxgrid-tdanalysis-model-1.0.0.10.jar +0 -0
- data/lib/java_libs/slf4j-api-1.7.2.jar +0 -0
- data/lib/java_libs/smack-3.2.1.jar +0 -0
- data/lib/java_libs/smackx-3.2.1.jar +0 -0
- data/lib/java_libs/stax-api-1.0-2.jar +0 -0
- data/lib/java_libs/stax2-api-3.1.1.jar +0 -0
- data/lib/java_libs/woodstox-core-asl-4.1.4.jar +0 -0
- data/lib/java_libs/wsdl4j-1.6.2.jar +0 -0
- data/lib/java_libs/xmlschema-core-2.0.3.jar +0 -0
- data/lib/pxnx_jruby.rb +70 -0
- data/lib/pxnx_jruby/config/pxnx.config +40 -0
- data/lib/pxnx_jruby/connection.rb +84 -0
- data/lib/pxnx_jruby/connection_manager.rb +44 -0
- data/lib/pxnx_jruby/eps_broker.rb +49 -0
- data/lib/pxnx_jruby/nexpose_connection.rb +62 -0
- data/lib/pxnx_jruby/nx_logger.rb +166 -0
- data/lib/pxnx_jruby/session_directory_notification.rb +26 -0
- data/lib/pxnx_jruby/spoon/README +7 -0
- data/lib/pxnx_jruby/spoon/pxnx_daemon_unix.rb +181 -0
- data/lib/pxnx_jruby/spoon/pxnx_daemon_windows.rb +50 -0
- data/lib/pxnx_jruby/spoon/pxnx_runner.rb +7 -0
- data/lib/pxnx_jruby/version.rb +5 -0
- data/pxnx.gemspec +28 -0
- data/releases/pxnx-0.1.0-java.gem +0 -0
- data/truststore.jks +0 -0
- metadata +220 -0
@@ -0,0 +1,166 @@
|
|
1
|
+
require 'fileutils'
|
2
|
+
require 'json'
|
3
|
+
require 'net/http'
|
4
|
+
require 'singleton'
|
5
|
+
|
6
|
+
module PxnxJruby
|
7
|
+
class NxLogger
|
8
|
+
include Singleton
|
9
|
+
LOG_PATH = "../logs/rapid7_%s.log"
|
10
|
+
KEY_FORMAT = "external.integration.%s"
|
11
|
+
PRODUCT_FORMAT = "%s_%s"
|
12
|
+
|
13
|
+
DEFAULT_LOG = 'integration'
|
14
|
+
PRODUCT_RANGE = 4..30
|
15
|
+
KEY_RANGE = 3..15
|
16
|
+
|
17
|
+
ENDPOINT = '/data/external/statistic/'
|
18
|
+
|
19
|
+
def initialize()
|
20
|
+
create_calls
|
21
|
+
@logger_file = get_log_path @product
|
22
|
+
setup_logging(true, 'info')
|
23
|
+
end
|
24
|
+
|
25
|
+
def setup_statistics_collection(vendor, product_name, gem_version)
|
26
|
+
begin
|
27
|
+
@statistic_key = get_statistic_key vendor
|
28
|
+
@product = get_product product_name, gem_version
|
29
|
+
rescue => e
|
30
|
+
#Continue
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def setup_logging(enabled, log_level = 'info', stdout=false)
|
35
|
+
@stdout = stdout
|
36
|
+
|
37
|
+
log_message('Logging disabled.') unless enabled || @log.nil?
|
38
|
+
@enabled = enabled
|
39
|
+
return unless @enabled
|
40
|
+
|
41
|
+
@logger_file = get_log_path @product
|
42
|
+
|
43
|
+
require 'logger'
|
44
|
+
directory = File.dirname(@logger_file)
|
45
|
+
FileUtils.mkdir_p(directory) unless File.directory?(directory)
|
46
|
+
io = IO.for_fd(IO.sysopen(@logger_file, 'a'), 'a')
|
47
|
+
io.autoclose = false
|
48
|
+
io.sync = true
|
49
|
+
@log = Logger.new(io, 'weekly')
|
50
|
+
@log.level = if log_level.to_s.casecmp('info') == 0
|
51
|
+
Logger::INFO
|
52
|
+
else
|
53
|
+
Logger::DEBUG
|
54
|
+
end
|
55
|
+
log_message("Logging enabled at level <#{log_level}>")
|
56
|
+
end
|
57
|
+
|
58
|
+
def create_calls
|
59
|
+
levels = [:info, :debug, :error, :warn]
|
60
|
+
levels.each do |level|
|
61
|
+
method_name =
|
62
|
+
define_singleton_method("log_#{level.to_s}_message") do |message|
|
63
|
+
puts message if @stdout
|
64
|
+
@log.send(level, message) unless !@enabled || @log.nil?
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
def log_message(message)
|
70
|
+
log_info_message message
|
71
|
+
end
|
72
|
+
|
73
|
+
def log_stat_message(message)
|
74
|
+
end
|
75
|
+
|
76
|
+
def get_log_path(product)
|
77
|
+
product.downcase! unless product.nil?
|
78
|
+
File.join(File.dirname(__FILE__), LOG_PATH % (product || DEFAULT_LOG))
|
79
|
+
end
|
80
|
+
|
81
|
+
def get_statistic_key(vendor)
|
82
|
+
if vendor.nil? || vendor.length < KEY_RANGE.min
|
83
|
+
log_stat_message("Vendor length is below minimum of <#{KEY_RANGE}>")
|
84
|
+
return nil
|
85
|
+
end
|
86
|
+
|
87
|
+
vendor.gsub!('-', '_')
|
88
|
+
vendor.slice! vendor.rindex('_') until vendor.count('_') <= 1
|
89
|
+
|
90
|
+
vendor.delete! "^A-Za-z0-9\_"
|
91
|
+
|
92
|
+
KEY_FORMAT % vendor[0...KEY_RANGE.max].downcase
|
93
|
+
end
|
94
|
+
|
95
|
+
def get_product(product, version)
|
96
|
+
return nil if ((product.nil? || product.empty?) ||
|
97
|
+
(version.nil? || version.empty?))
|
98
|
+
|
99
|
+
product.gsub!('-', '_')
|
100
|
+
product.slice! product.rindex('_') until product.count('_') <= 1
|
101
|
+
|
102
|
+
product.delete! "^A-Za-z0-9\_"
|
103
|
+
version.delete! "^A-Za-z0-9\.\-"
|
104
|
+
|
105
|
+
product = (PRODUCT_FORMAT % [product, version])[0...PRODUCT_RANGE.max]
|
106
|
+
|
107
|
+
product.slice! product.rindex(/[A-Z0-9]/i)+1..-1
|
108
|
+
|
109
|
+
if product.length < PRODUCT_RANGE.min
|
110
|
+
log_stat_message("Product length below minimum <#{PRODUCT_RANGE.min}>.")
|
111
|
+
return nil
|
112
|
+
end
|
113
|
+
product.downcase
|
114
|
+
end
|
115
|
+
|
116
|
+
def generate_payload(statistic_value='')
|
117
|
+
product_name, separator, version = @product.to_s.rpartition('_')
|
118
|
+
payload_value = {'version' => version}.to_json
|
119
|
+
|
120
|
+
payload = {'statistic-key' => @statistic_key.to_s,
|
121
|
+
'statistic-value' => payload_value,
|
122
|
+
'product' => product_name}
|
123
|
+
JSON.generate(payload)
|
124
|
+
end
|
125
|
+
|
126
|
+
def send(nexpose_address, nexpose_port, session_id, payload)
|
127
|
+
header = {'Content-Type' => 'application/json',
|
128
|
+
'nexposeCCSessionID' => session_id,
|
129
|
+
'Cookie' => "nexposeCCSessionID=#{session_id}"}
|
130
|
+
req = Net::HTTP::Put.new(ENDPOINT, header)
|
131
|
+
req.body = payload
|
132
|
+
http_instance = Net::HTTP.new(nexpose_address, nexpose_port)
|
133
|
+
http_instance.use_ssl = true
|
134
|
+
http_instance.verify_mode = OpenSSL::SSL::VERIFY_NONE
|
135
|
+
response = http_instance.start { |http| http.request(req) }
|
136
|
+
log_stat_message "Received code #{response.code} from Nexpose console."
|
137
|
+
log_stat_message "Received message #{response.msg} from Nexpose console."
|
138
|
+
log_stat_message 'Finished sending statistics data to Nexpose.'
|
139
|
+
|
140
|
+
response.code
|
141
|
+
end
|
142
|
+
|
143
|
+
def on_connect(nexpose_address, nexpose_port, session_id, value)
|
144
|
+
log_stat_message 'Sending statistics data to Nexpose'
|
145
|
+
|
146
|
+
if @product.nil? || @statistic_key.nil?
|
147
|
+
log_stat_message('Invalid product name and/or statistics key.')
|
148
|
+
log_stat_message('Statistics collection not enabled.')
|
149
|
+
return
|
150
|
+
end
|
151
|
+
|
152
|
+
begin
|
153
|
+
payload = generate_payload value
|
154
|
+
send(nexpose_address, nexpose_port, session_id, payload)
|
155
|
+
rescue => e
|
156
|
+
#Let the program continue
|
157
|
+
end
|
158
|
+
end
|
159
|
+
|
160
|
+
#Used by net library for debugging
|
161
|
+
def <<(value)
|
162
|
+
log_debug_message(value)
|
163
|
+
end
|
164
|
+
|
165
|
+
end
|
166
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module PxnxJruby
|
2
|
+
class SessionDirectoryNotification
|
3
|
+
require 'java'
|
4
|
+
require 'jruby/core_ext'
|
5
|
+
java_import org.slf4j.Logger
|
6
|
+
java_import org.slf4j.LoggerFactory
|
7
|
+
include Java::com.cisco.pxgrid.stub.identity.SessionDirectoryNotification
|
8
|
+
java_signature 'void onChange(com.cisco.pxgrid.model.net.Session)'
|
9
|
+
attr_accessor :session, :interface, :device_ip_address
|
10
|
+
|
11
|
+
# TODO: Test this method.
|
12
|
+
# TODO: Properly create calls to start integration with the IP received.
|
13
|
+
def onChange(session)
|
14
|
+
# Log this session id.
|
15
|
+
@session = session
|
16
|
+
@interface = session.getInterface
|
17
|
+
device_interfaces = @interface.getIpIntfIDs
|
18
|
+
device = device_interfaces[0]
|
19
|
+
@device_ip_address = device.get_ip_address
|
20
|
+
connect_manager = PxnxJruby::ConnectionManager.instance
|
21
|
+
log = LoggerFactory.getLogger(SessionDirectoryNotification.become_java!)
|
22
|
+
log.info("Found new IP #{@device_ip_address} connecting to the network, assessing.")
|
23
|
+
connect_manager.new_connection(@device_ip_address)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,7 @@
|
|
1
|
+
The Apache licence applies to all files within this folder ('spoon').
|
2
|
+
|
3
|
+
The original project was authored by 'headius' (https://github.com/headius) and is located at: https://github.com/headius/spoon.
|
4
|
+
|
5
|
+
Modifications have been made to the pxnx_daemon_windows.rb file to fix issue #17 (https://github.com/headius/spoon/issues/17):
|
6
|
+
* Now extends the FFI Lib
|
7
|
+
* Imports MSVCRT DLL where some of the used functions are defined.
|
@@ -0,0 +1,181 @@
|
|
1
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
2
|
+
# you may not use this file except in compliance with the License.
|
3
|
+
# You may obtain a copy of the License at
|
4
|
+
#
|
5
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
6
|
+
#
|
7
|
+
# Unless required by applicable law or agreed to in writing, software
|
8
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
9
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
10
|
+
# See the License for the specific language governing permissions and
|
11
|
+
# limitations under the License.
|
12
|
+
|
13
|
+
# UNIX posix_spawn
|
14
|
+
|
15
|
+
require 'ffi'
|
16
|
+
|
17
|
+
module Spoon
|
18
|
+
class FileActions
|
19
|
+
attr_reader :pointer
|
20
|
+
SIZE = FFI::Platform.mac? ? FFI.type_size(:pointer) : 128
|
21
|
+
|
22
|
+
def initialize
|
23
|
+
@pointer = FFI::AutoPointer.new(LibC.malloc(SIZE), Releaser)
|
24
|
+
error = LibC.posix_spawn_file_actions_init(@pointer)
|
25
|
+
raise SystemCallError.new("posix_file_actions_init", error) unless error == 0
|
26
|
+
end
|
27
|
+
|
28
|
+
class Releaser
|
29
|
+
def self.call(ptr)
|
30
|
+
LibC.posix_spawn_file_actions_destroy(ptr)
|
31
|
+
LibC.free(ptr)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def open(fd, path, oflag, mode)
|
36
|
+
error = LibC.posix_spawn_file_actions_addopen(@pointer, fd, path, oflag, mode)
|
37
|
+
raise SystemCallError.new("posix_file_actions_addopen", error) unless error == 0
|
38
|
+
self
|
39
|
+
end
|
40
|
+
|
41
|
+
def close(fd)
|
42
|
+
error = LibC.posix_spawn_file_actions_addclose(@pointer, fd)
|
43
|
+
raise SystemCallError.new("posix_file_actions_addclose", error) unless error == 0
|
44
|
+
self
|
45
|
+
end
|
46
|
+
|
47
|
+
def dup2(fd, newfd)
|
48
|
+
error = LibC.posix_spawn_file_actions_adddup2(@pointer, fd, newfd)
|
49
|
+
raise SystemCallError.new("posix_file_actions_adddup2", error) unless error == 0
|
50
|
+
self
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
class SpawnAttributes
|
55
|
+
attr_reader :pointer
|
56
|
+
SIZE = FFI::Platform.mac? ? FFI.type_size(:pointer) : 512
|
57
|
+
|
58
|
+
def initialize
|
59
|
+
@pointer = FFI::AutoPointer.new(LibC.malloc(SIZE), Releaser)
|
60
|
+
error = LibC.posix_spawnattr_init(@pointer)
|
61
|
+
raise SystemCallError.new("posix_spawnattr_init", error) unless error == 0
|
62
|
+
end
|
63
|
+
|
64
|
+
class Releaser
|
65
|
+
def self.call(ptr)
|
66
|
+
LibC.posix_spawnattr_destroy(ptr)
|
67
|
+
LibC.free(ptr)
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
def pgroup=(group)
|
72
|
+
error = LibC.posix_spawnattr_setpgroup(pointer, group)
|
73
|
+
raise SystemCallError.new("posix_spawnattr_setpgroup", error) unless error == 0
|
74
|
+
group
|
75
|
+
end
|
76
|
+
|
77
|
+
def pgroup
|
78
|
+
group = FFI::MemoryPointer.new :pid_t
|
79
|
+
error = LibC.posix_spawnattr_getpgroup(pointer, group)
|
80
|
+
raise SystemCallError.new("posix_spawnattr_getpgroup", error) unless error == 0
|
81
|
+
get_pid(group)
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
def self.posix_spawn(path, file_actions, spawn_attr, argv, env = ENV)
|
86
|
+
pid_ptr, argv_ptr, env_ptr = _prepare_spawn_args(argv, env)
|
87
|
+
error = LibC.posix_spawnp(pid_ptr, path, file_actions, spawn_attr, argv_ptr, env_ptr)
|
88
|
+
raise SystemCallError.new(path, error) unless error == 0
|
89
|
+
get_pid(pid_ptr)
|
90
|
+
end
|
91
|
+
|
92
|
+
def self.posix_spawnp(file, file_actions, spawn_attr, argv, env = ENV)
|
93
|
+
pid_ptr, argv_ptr, env_ptr = _prepare_spawn_args(argv, env)
|
94
|
+
error = LibC.posix_spawnp(pid_ptr, file, file_actions, spawn_attr, argv_ptr, env_ptr)
|
95
|
+
raise SystemCallError.new(file, error) unless error == 0
|
96
|
+
get_pid(pid_ptr)
|
97
|
+
end
|
98
|
+
|
99
|
+
def self.spawn(*args)
|
100
|
+
posix_spawn(args[0], nil, nil, args, ENV)
|
101
|
+
end
|
102
|
+
|
103
|
+
def self.spawnp(*args)
|
104
|
+
posix_spawnp(args[0], nil, nil, args, ENV)
|
105
|
+
end
|
106
|
+
|
107
|
+
private
|
108
|
+
|
109
|
+
class PointerArray
|
110
|
+
def initialize
|
111
|
+
@ary = []
|
112
|
+
end
|
113
|
+
|
114
|
+
def <<(ptr)
|
115
|
+
@ary << ptr
|
116
|
+
self
|
117
|
+
end
|
118
|
+
|
119
|
+
def pointer
|
120
|
+
if @pointer.nil? || (@pointer.size / @pointer.type_size) <= @ary.length
|
121
|
+
ptr = FFI::MemoryPointer.new(:pointer, @ary.length + 1)
|
122
|
+
ptr.put_array_of_pointer(0, @ary)
|
123
|
+
@pointer = ptr
|
124
|
+
end
|
125
|
+
@pointer
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
if FFI.type_size(:pid_t) == 4
|
130
|
+
def self.get_pid(ptr)
|
131
|
+
ptr.get_int32(0)
|
132
|
+
end
|
133
|
+
else
|
134
|
+
def self.get_pid(ptr)
|
135
|
+
ptr.get_int64(0)
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
module LibC
|
140
|
+
extend FFI::Library
|
141
|
+
ffi_lib FFI::Library::LIBC
|
142
|
+
|
143
|
+
class PointerConverter
|
144
|
+
extend FFI::DataConverter
|
145
|
+
native_type FFI::Type::POINTER
|
146
|
+
|
147
|
+
def self.to_native(value, ctx)
|
148
|
+
value ? value.pointer : nil
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
typedef PointerConverter, :file_actions
|
153
|
+
typedef PointerConverter, :spawn_attr
|
154
|
+
typedef PointerConverter, :ptr_array
|
155
|
+
|
156
|
+
attach_function :posix_spawn, [:pointer, :string, :file_actions, :spawn_attr, :ptr_array, :ptr_array ], :int
|
157
|
+
attach_function :posix_spawnp, [:pointer, :string, :file_actions, :spawn_attr, :ptr_array, :ptr_array ], :int
|
158
|
+
attach_function :posix_spawn_file_actions_init, [ :pointer ], :int
|
159
|
+
attach_function :posix_spawn_file_actions_destroy, [ :pointer ], :int
|
160
|
+
attach_function :posix_spawn_file_actions_adddup2, [ :pointer, :int, :int ], :int
|
161
|
+
attach_function :posix_spawn_file_actions_addclose, [ :pointer, :int ], :int
|
162
|
+
attach_function :posix_spawn_file_actions_addopen, [ :pointer, :int, :string, :int, :mode_t ], :int
|
163
|
+
attach_function :posix_spawnattr_init, [ :pointer ], :int
|
164
|
+
attach_function :posix_spawnattr_destroy, [ :pointer ], :int
|
165
|
+
attach_function :posix_spawnattr_setpgroup, [ :pointer, :pid_t ], :int
|
166
|
+
attach_function :posix_spawnattr_getpgroup, [ :pointer, :pointer ], :int
|
167
|
+
attach_function :malloc, [ :size_t ], :pointer
|
168
|
+
attach_function :free, [ :pointer ], :void
|
169
|
+
attach_function :strerror, [ :int ], :string
|
170
|
+
end
|
171
|
+
|
172
|
+
def self._prepare_spawn_args(argv, env)
|
173
|
+
pid_ptr = FFI::MemoryPointer.new(:pid_t, 1)
|
174
|
+
|
175
|
+
args_ary = argv.inject(PointerArray.new) { |ary, str| ary << FFI::MemoryPointer.from_string(str) }
|
176
|
+
env_ary = PointerArray.new
|
177
|
+
env.each_pair { |key, value| env_ary << FFI::MemoryPointer.from_string("#{key}=#{value}") }
|
178
|
+
|
179
|
+
[pid_ptr, args_ary, env_ary]
|
180
|
+
end
|
181
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
2
|
+
# you may not use this file except in compliance with the License.
|
3
|
+
# You may obtain a copy of the License at
|
4
|
+
#
|
5
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
6
|
+
#
|
7
|
+
# Unless required by applicable law or agreed to in writing, software
|
8
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
9
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
10
|
+
# See the License for the specific language governing permissions and
|
11
|
+
# limitations under the License.
|
12
|
+
|
13
|
+
require 'ffi'
|
14
|
+
|
15
|
+
module Spoon
|
16
|
+
extend FFI::Library
|
17
|
+
P_NOWAIT = 1
|
18
|
+
|
19
|
+
ffi_lib 'MSVCRT'
|
20
|
+
attach_function :_spawnve, [:int, :string, :pointer, :pointer], :int
|
21
|
+
attach_function :_spawnvpe, [:int, :string, :pointer, :pointer], :int
|
22
|
+
|
23
|
+
ffi_lib 'kernel32'
|
24
|
+
ffi_convention :stdcall
|
25
|
+
attach_function :_get_process_id, :GetProcessId, [:int], :ulong
|
26
|
+
|
27
|
+
def self.spawn(*args)
|
28
|
+
spawn_args = _prepare_spawn_args(args)
|
29
|
+
_get_process_id(_spawnve(*spawn_args))
|
30
|
+
end
|
31
|
+
|
32
|
+
def self.spawnp(*args)
|
33
|
+
spawn_args = _prepare_spawn_args(args)
|
34
|
+
_get_process_id(_spawnvpe(*spawn_args))
|
35
|
+
end
|
36
|
+
|
37
|
+
private
|
38
|
+
|
39
|
+
def self._prepare_spawn_args(args)
|
40
|
+
args_ary = FFI::MemoryPointer.new(:pointer, args.length + 1)
|
41
|
+
str_ptrs = args.map {|str| FFI::MemoryPointer.from_string(str)}
|
42
|
+
args_ary.put_array_of_pointer(0, str_ptrs)
|
43
|
+
|
44
|
+
env_ary = FFI::MemoryPointer.new(:pointer, ENV.length + 1)
|
45
|
+
env_ptrs = ENV.map {|key,value| FFI::MemoryPointer.from_string("#{key}=#{value}")}
|
46
|
+
env_ary.put_array_of_pointer(0, env_ptrs)
|
47
|
+
|
48
|
+
[P_NOWAIT, args[0], args_ary, env_ary]
|
49
|
+
end
|
50
|
+
end
|