nexpose_pxgrid 0.1.2-java
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.
- 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
|