train-core 2.1.7 → 2.1.13
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 +4 -4
- data/lib/train.rb +20 -20
- data/lib/train/errors.rb +1 -1
- data/lib/train/extras.rb +2 -2
- data/lib/train/extras/command_wrapper.rb +24 -24
- data/lib/train/extras/stat.rb +27 -27
- data/lib/train/file.rb +30 -30
- data/lib/train/file/local.rb +8 -8
- data/lib/train/file/local/unix.rb +5 -5
- data/lib/train/file/local/windows.rb +1 -1
- data/lib/train/file/remote.rb +8 -8
- data/lib/train/file/remote/aix.rb +1 -1
- data/lib/train/file/remote/linux.rb +2 -2
- data/lib/train/file/remote/qnx.rb +8 -8
- data/lib/train/file/remote/unix.rb +10 -14
- data/lib/train/file/remote/windows.rb +5 -5
- data/lib/train/globals.rb +1 -1
- data/lib/train/options.rb +8 -8
- data/lib/train/platforms.rb +8 -8
- data/lib/train/platforms/common.rb +1 -1
- data/lib/train/platforms/detect/helpers/os_common.rb +36 -32
- data/lib/train/platforms/detect/helpers/os_linux.rb +12 -12
- data/lib/train/platforms/detect/helpers/os_windows.rb +27 -29
- data/lib/train/platforms/detect/scanner.rb +4 -4
- data/lib/train/platforms/detect/specifications/api.rb +8 -8
- data/lib/train/platforms/detect/specifications/os.rb +252 -252
- data/lib/train/platforms/detect/uuid.rb +5 -7
- data/lib/train/platforms/platform.rb +9 -5
- data/lib/train/plugin_test_helper.rb +12 -12
- data/lib/train/plugins.rb +5 -5
- data/lib/train/plugins/base_connection.rb +13 -13
- data/lib/train/plugins/transport.rb +7 -7
- data/lib/train/transports/cisco_ios_connection.rb +20 -20
- data/lib/train/transports/local.rb +22 -22
- data/lib/train/transports/mock.rb +33 -35
- data/lib/train/transports/ssh.rb +47 -47
- data/lib/train/transports/ssh_connection.rb +28 -28
- data/lib/train/transports/winrm.rb +37 -37
- data/lib/train/transports/winrm_connection.rb +12 -12
- data/lib/train/version.rb +1 -1
- metadata +2 -2
@@ -1,8 +1,6 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
require
|
4
|
-
require 'securerandom'
|
5
|
-
require 'json'
|
1
|
+
require "digest/sha1"
|
2
|
+
require "securerandom"
|
3
|
+
require "json"
|
6
4
|
|
7
5
|
module Train::Platforms::Detect
|
8
6
|
class UUID
|
@@ -24,10 +22,10 @@ module Train::Platforms::Detect
|
|
24
22
|
else
|
25
23
|
if @platform[:uuid_command]
|
26
24
|
result = @backend.run_command(@platform[:uuid_command])
|
27
|
-
return uuid_from_string(result.stdout.chomp) if result.exit_status
|
25
|
+
return uuid_from_string(result.stdout.chomp) if result.exit_status == 0 && !result.stdout.empty?
|
28
26
|
end
|
29
27
|
|
30
|
-
raise
|
28
|
+
raise "Could not find platform uuid! Please set a uuid_command for your platform."
|
31
29
|
end
|
32
30
|
end
|
33
31
|
end
|
@@ -44,7 +44,7 @@ module Train::Platforms
|
|
44
44
|
@cleaned_name = nil if force
|
45
45
|
@cleaned_name ||= begin
|
46
46
|
name = (@platform[:name] || @name)
|
47
|
-
name.downcase!.tr!(
|
47
|
+
name.downcase!.tr!(" ", "_") if name =~ /[A-Z ]/
|
48
48
|
name
|
49
49
|
end
|
50
50
|
end
|
@@ -59,7 +59,7 @@ module Train::Platforms
|
|
59
59
|
if respond_to?(name)
|
60
60
|
send(name)
|
61
61
|
else
|
62
|
-
|
62
|
+
"unknown"
|
63
63
|
end
|
64
64
|
end
|
65
65
|
|
@@ -73,6 +73,10 @@ module Train::Platforms
|
|
73
73
|
@platform
|
74
74
|
end
|
75
75
|
|
76
|
+
def cisco_ios? # TODO: kinda a hack. needed to prevent tests from corrupting.
|
77
|
+
false
|
78
|
+
end
|
79
|
+
|
76
80
|
# Add generic family? and platform methods to an existing platform
|
77
81
|
#
|
78
82
|
# This is done later to add any custom
|
@@ -84,8 +88,8 @@ module Train::Platforms
|
|
84
88
|
# Add in family methods
|
85
89
|
family_list = Train::Platforms.families
|
86
90
|
family_list.each_value do |k|
|
87
|
-
next if respond_to?(k.name +
|
88
|
-
define_singleton_method(k.name +
|
91
|
+
next if respond_to?(k.name + "?")
|
92
|
+
define_singleton_method(k.name + "?") do
|
89
93
|
family_hierarchy.include?(k.name)
|
90
94
|
end
|
91
95
|
end
|
@@ -99,7 +103,7 @@ module Train::Platforms
|
|
99
103
|
end
|
100
104
|
|
101
105
|
# Create method for name if its not already true
|
102
|
-
m = name +
|
106
|
+
m = name + "?"
|
103
107
|
return if respond_to?(m)
|
104
108
|
define_singleton_method(m) do
|
105
109
|
true
|
@@ -3,27 +3,27 @@
|
|
3
3
|
|
4
4
|
# Load Train. We certainly need the plugin system, and also several other parts
|
5
5
|
# that are tightly coupled. Train itself is fairly light, and non-invasive.
|
6
|
-
require
|
6
|
+
require "train"
|
7
7
|
|
8
8
|
# You can select from a number of test harnesses. Since Train is closely related
|
9
9
|
# to InSpec, and InSpec uses Spec-style controls in profile code, you will
|
10
10
|
# probably want to use something like minitest/spec, which provides Spec-style
|
11
11
|
# tests.
|
12
|
-
require
|
13
|
-
require
|
12
|
+
require "minitest/spec"
|
13
|
+
require "minitest/autorun"
|
14
14
|
|
15
15
|
# Data formats commonly used in testing
|
16
|
-
require
|
17
|
-
require
|
16
|
+
require "json"
|
17
|
+
require "ostruct"
|
18
18
|
|
19
19
|
# Utilities often needed
|
20
|
-
require
|
21
|
-
require
|
22
|
-
require
|
20
|
+
require "fileutils"
|
21
|
+
require "tmpdir"
|
22
|
+
require "pathname"
|
23
23
|
|
24
24
|
# You might want to put some debugging tools here. We run tests to find bugs,
|
25
25
|
# after all.
|
26
|
-
require
|
26
|
+
require "byebug"
|
27
27
|
|
28
28
|
# Configure MiniTest to expose things like `let`
|
29
29
|
class Module
|
@@ -38,11 +38,11 @@ module TrainPluginBaseHelper
|
|
38
38
|
plugin_test_helper_path = Pathname.new(caller_locations(4, 1).first.absolute_path)
|
39
39
|
plugin_src_root = plugin_test_helper_path.parent.parent
|
40
40
|
base.let(:plugin_src_path) { plugin_src_root }
|
41
|
-
base.let(:plugin_fixtures_path) { File.join(plugin_src_root,
|
41
|
+
base.let(:plugin_fixtures_path) { File.join(plugin_src_root, "test", "fixtures") }
|
42
42
|
end
|
43
43
|
|
44
|
-
let(:train_src_path) { File.expand_path(File.join(__FILE__,
|
45
|
-
let(:train_fixtures_path) { File.join(train_src_path,
|
44
|
+
let(:train_src_path) { File.expand_path(File.join(__FILE__, "..", "..")) }
|
45
|
+
let(:train_fixtures_path) { File.join(train_src_path, "test", "fixtures") }
|
46
46
|
let(:registry) { Train::Plugins.registry }
|
47
47
|
end
|
48
48
|
|
data/lib/train/plugins.rb
CHANGED
@@ -3,11 +3,11 @@
|
|
3
3
|
# Author:: Dominik Richter (<dominik.richter@gmail.com>)
|
4
4
|
# Author:: Christoph Hartmann (<chris@lollyrock.com>)
|
5
5
|
|
6
|
-
require
|
6
|
+
require "train/errors"
|
7
7
|
|
8
8
|
module Train
|
9
9
|
class Plugins
|
10
|
-
require
|
10
|
+
require "train/plugins/transport"
|
11
11
|
|
12
12
|
class << self
|
13
13
|
# Retrieve the current plugin registry, containing all plugin names
|
@@ -30,10 +30,10 @@ module Train
|
|
30
30
|
# @return [Transport] the versioned transport base class
|
31
31
|
def self.plugin(version = 1)
|
32
32
|
if version != 1
|
33
|
-
|
34
|
-
|
33
|
+
raise ClientError,
|
34
|
+
"Only understand train plugin version 1. You are trying to "\
|
35
35
|
"initialize a train plugin #{version}, which is not supported "\
|
36
|
-
|
36
|
+
"in the current release of train."
|
37
37
|
end
|
38
38
|
::Train::Plugins::Transport
|
39
39
|
end
|
@@ -1,9 +1,9 @@
|
|
1
1
|
# encoding: utf-8
|
2
2
|
|
3
|
-
require
|
4
|
-
require
|
5
|
-
require
|
6
|
-
require
|
3
|
+
require "train/errors"
|
4
|
+
require "train/extras"
|
5
|
+
require "train/file"
|
6
|
+
require "logger"
|
7
7
|
|
8
8
|
class Train::Plugins::Transport
|
9
9
|
# A Connection instance can be generated and re-generated, given new
|
@@ -21,7 +21,7 @@ class Train::Plugins::Transport
|
|
21
21
|
# @yield [self] yields itself for block-style invocation
|
22
22
|
def initialize(options = nil)
|
23
23
|
@options = options || {}
|
24
|
-
@logger = @options.delete(:logger) || Logger.new(
|
24
|
+
@logger = @options.delete(:logger) || Logger.new($stdout, level: :fatal)
|
25
25
|
Train::Platforms::Detect::Specifications::OS.load
|
26
26
|
Train::Platforms::Detect::Specifications::Api.load
|
27
27
|
|
@@ -64,12 +64,12 @@ class Train::Plugins::Transport
|
|
64
64
|
# Enable caching types for Train. Currently we support
|
65
65
|
# :api_call, :file and :command types
|
66
66
|
def enable_cache(type)
|
67
|
-
|
67
|
+
raise Train::UnknownCacheType, "#{type} is not a valid cache type" unless @cache_enabled.keys.include?(type.to_sym)
|
68
68
|
@cache_enabled[type.to_sym] = true
|
69
69
|
end
|
70
70
|
|
71
71
|
def disable_cache(type)
|
72
|
-
|
72
|
+
raise Train::UnknownCacheType, "#{type} is not a valid cache type" unless @cache_enabled.keys.include?(type.to_sym)
|
73
73
|
@cache_enabled[type.to_sym] = false
|
74
74
|
clear_cache(type.to_sym)
|
75
75
|
end
|
@@ -81,13 +81,13 @@ class Train::Plugins::Transport
|
|
81
81
|
|
82
82
|
def to_json
|
83
83
|
{
|
84
|
-
|
84
|
+
"files" => Hash[@cache[:file].map { |x, y| [x, y.to_json] }],
|
85
85
|
}
|
86
86
|
end
|
87
87
|
|
88
88
|
def load_json(j)
|
89
|
-
require
|
90
|
-
j[
|
89
|
+
require "train/transports/mock"
|
90
|
+
j["files"].each do |path, jf|
|
91
91
|
@cache[:file][path] = Train::Transports::Mock::Connection::File.from_json(jf)
|
92
92
|
end
|
93
93
|
end
|
@@ -137,7 +137,7 @@ class Train::Plugins::Transport
|
|
137
137
|
#
|
138
138
|
# @return [LoginCommand] array of command line tokens
|
139
139
|
def login_command
|
140
|
-
|
140
|
+
raise NotImplementedError, "#{self.class} does not implement #login_command()"
|
141
141
|
end
|
142
142
|
|
143
143
|
# Block and return only when the remote host is prepared and ready to
|
@@ -161,7 +161,7 @@ class Train::Plugins::Transport
|
|
161
161
|
#
|
162
162
|
# @return [CommandResult] contains the result of running the command
|
163
163
|
def run_command_via_connection(_command, &_data_handler)
|
164
|
-
|
164
|
+
raise NotImplementedError, "#{self.class} does not implement #run_command_via_connection()"
|
165
165
|
end
|
166
166
|
|
167
167
|
# Interact with files on the target. Read, write, and get metadata
|
@@ -170,7 +170,7 @@ class Train::Plugins::Transport
|
|
170
170
|
# @param [String] path which is being inspected
|
171
171
|
# @return [FileCommon] file object that allows for interaction
|
172
172
|
def file_via_connection(_path, *_args)
|
173
|
-
|
173
|
+
raise NotImplementedError, "#{self.class} does not implement #file_via_connection(...)"
|
174
174
|
end
|
175
175
|
|
176
176
|
def clear_cache(type)
|
@@ -3,17 +3,17 @@
|
|
3
3
|
# Author:: Dominik Richter (<dominik.richter@gmail.com>)
|
4
4
|
# Author:: Christoph Hartmann (<chris@lollyrock.com>)
|
5
5
|
|
6
|
-
require
|
7
|
-
require
|
8
|
-
require
|
9
|
-
require
|
6
|
+
require "logger"
|
7
|
+
require "train/errors"
|
8
|
+
require "train/extras"
|
9
|
+
require "train/options"
|
10
10
|
|
11
11
|
class Train::Plugins
|
12
12
|
class Transport
|
13
13
|
include Train::Extras
|
14
14
|
Train::Options.attach(self)
|
15
15
|
|
16
|
-
require
|
16
|
+
require "train/plugins/base_connection"
|
17
17
|
|
18
18
|
# Initialize a new Transport object
|
19
19
|
#
|
@@ -21,7 +21,7 @@ class Train::Plugins
|
|
21
21
|
# @return [Transport] the transport object
|
22
22
|
def initialize(options = {})
|
23
23
|
@options = merge_options({}, options || {})
|
24
|
-
@logger = @options[:logger] || Logger.new(
|
24
|
+
@logger = @options[:logger] || Logger.new($stdout, level: :fatal)
|
25
25
|
end
|
26
26
|
|
27
27
|
# Create a connection to the target. Options may be provided
|
@@ -30,7 +30,7 @@ class Train::Plugins
|
|
30
30
|
# @param [Hash] _options = nil provide optional configuration params
|
31
31
|
# @return [Connection] the connection for this configuration
|
32
32
|
def connection(_options = nil)
|
33
|
-
|
33
|
+
raise Train::ClientError, "#{self.class} does not implement #connection()"
|
34
34
|
end
|
35
35
|
|
36
36
|
# Register the inheriting class with as a train plugin using the
|
@@ -24,8 +24,8 @@ class Train::Transports::SSH
|
|
24
24
|
end
|
25
25
|
|
26
26
|
def unique_identifier
|
27
|
-
result = run_command_via_connection(
|
28
|
-
result.stdout.split(
|
27
|
+
result = run_command_via_connection("show version | include Processor")
|
28
|
+
result.stdout.split(" ")[-1]
|
29
29
|
end
|
30
30
|
|
31
31
|
private
|
@@ -45,26 +45,26 @@ class Train::Transports::SSH
|
|
45
45
|
if @enable_password
|
46
46
|
# This verifies we are not in privileged exec mode before running the
|
47
47
|
# enable command. Otherwise, the password will be in history.
|
48
|
-
if run_command_via_connection(
|
48
|
+
if run_command_via_connection("show privilege").stdout.split[-1] != "15"
|
49
49
|
# Extra newlines to get back to prompt if incorrect password is used
|
50
50
|
run_command_via_connection("enable\n#{@enable_password}\n\n\n")
|
51
51
|
end
|
52
52
|
end
|
53
53
|
|
54
54
|
# Prevent `--MORE--` by removing terminal length limit
|
55
|
-
run_command_via_connection(
|
55
|
+
run_command_via_connection("terminal length 0")
|
56
56
|
|
57
57
|
@session
|
58
58
|
end
|
59
59
|
|
60
60
|
def run_command_via_connection(cmd, &_data_handler)
|
61
61
|
# Ensure buffer is empty before sending data
|
62
|
-
@buf =
|
62
|
+
@buf = ""
|
63
63
|
|
64
64
|
logger.debug("[SSH] Running `#{cmd}` on #{self}")
|
65
65
|
session.send_data(cmd + "\r\n")
|
66
66
|
|
67
|
-
logger.debug(
|
67
|
+
logger.debug("[SSH] waiting for prompt")
|
68
68
|
until @buf =~ @prompt
|
69
69
|
if @buf =~ /Bad (secrets|password)|Access denied/
|
70
70
|
raise BadEnablePassword
|
@@ -74,16 +74,16 @@ class Train::Transports::SSH
|
|
74
74
|
|
75
75
|
# Save the buffer and clear it for the next command
|
76
76
|
output = @buf.dup
|
77
|
-
@buf =
|
77
|
+
@buf = ""
|
78
78
|
|
79
79
|
format_result(format_output(output, cmd))
|
80
80
|
end
|
81
81
|
|
82
82
|
ERROR_MATCHERS = [
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
83
|
+
"Bad IP address",
|
84
|
+
"Incomplete command",
|
85
|
+
"Invalid input detected",
|
86
|
+
"Unrecognized host",
|
87
87
|
].freeze
|
88
88
|
|
89
89
|
# IOS commands do not have an exit code so we must compare the command
|
@@ -92,9 +92,9 @@ class Train::Transports::SSH
|
|
92
92
|
# result.
|
93
93
|
def format_result(result)
|
94
94
|
if ERROR_MATCHERS.none? { |e| result.include?(e) }
|
95
|
-
CommandResult.new(result,
|
95
|
+
CommandResult.new(result, "", 0)
|
96
96
|
else
|
97
|
-
CommandResult.new(
|
97
|
+
CommandResult.new("", result, 1)
|
98
98
|
end
|
99
99
|
end
|
100
100
|
|
@@ -107,10 +107,10 @@ class Train::Transports::SSH
|
|
107
107
|
trailing_line_endings = /(\r\n)+$/
|
108
108
|
|
109
109
|
output
|
110
|
-
.sub(leading_prompt,
|
111
|
-
.sub(command_string,
|
112
|
-
.gsub(trailing_prompt,
|
113
|
-
.gsub(trailing_line_endings,
|
110
|
+
.sub(leading_prompt, "")
|
111
|
+
.sub(command_string, "")
|
112
|
+
.gsub(trailing_prompt, "")
|
113
|
+
.gsub(trailing_line_endings, "")
|
114
114
|
end
|
115
115
|
|
116
116
|
# Create an SSH channel that writes to @buf when data is received
|
@@ -121,9 +121,9 @@ class Train::Transports::SSH
|
|
121
121
|
@buf += data
|
122
122
|
end
|
123
123
|
|
124
|
-
ch.send_channel_request(
|
125
|
-
raise
|
126
|
-
logger.debug(
|
124
|
+
ch.send_channel_request("shell") do |_, success|
|
125
|
+
raise "Failed to open SSH shell" unless success
|
126
|
+
logger.debug("[SSH] shell opened")
|
127
127
|
end
|
128
128
|
end
|
129
129
|
end
|
@@ -3,13 +3,13 @@
|
|
3
3
|
# author: Dominik Richter
|
4
4
|
# author: Christoph Hartmann
|
5
5
|
|
6
|
-
require
|
7
|
-
require
|
8
|
-
require
|
6
|
+
require "train/plugins"
|
7
|
+
require "train/errors"
|
8
|
+
require "mixlib/shellout"
|
9
9
|
|
10
10
|
module Train::Transports
|
11
11
|
class Local < Train.plugin(1)
|
12
|
-
name
|
12
|
+
name "local"
|
13
13
|
|
14
14
|
class PipeError < Train::TransportError; end
|
15
15
|
|
@@ -33,7 +33,7 @@ module Train::Transports
|
|
33
33
|
end
|
34
34
|
|
35
35
|
def uri
|
36
|
-
|
36
|
+
"local://"
|
37
37
|
end
|
38
38
|
|
39
39
|
private
|
@@ -41,10 +41,10 @@ module Train::Transports
|
|
41
41
|
def select_runner(options)
|
42
42
|
if os.windows?
|
43
43
|
# Force a 64 bit poweshell if needed
|
44
|
-
if RUBY_PLATFORM ==
|
44
|
+
if RUBY_PLATFORM == "i386-mingw32" && os.arch == "x86_64"
|
45
45
|
powershell_cmd = "#{ENV['SystemRoot']}\\sysnative\\WindowsPowerShell\\v1.0\\powershell.exe"
|
46
46
|
else
|
47
|
-
powershell_cmd =
|
47
|
+
powershell_cmd = "powershell"
|
48
48
|
end
|
49
49
|
|
50
50
|
# Attempt to use a named pipe but fallback to ShellOut if that fails
|
@@ -67,7 +67,7 @@ module Train::Transports
|
|
67
67
|
when :windows_shell
|
68
68
|
WindowsShellRunner.new
|
69
69
|
else
|
70
|
-
|
70
|
+
raise "Runner type `#{command_runner}` not supported"
|
71
71
|
end
|
72
72
|
end
|
73
73
|
|
@@ -82,7 +82,7 @@ module Train::Transports
|
|
82
82
|
res.run_command
|
83
83
|
Local::CommandResult.new(res.stdout, res.stderr, res.exitstatus)
|
84
84
|
rescue Errno::ENOENT => _
|
85
|
-
CommandResult.new(
|
85
|
+
CommandResult.new("", "", 1)
|
86
86
|
end
|
87
87
|
|
88
88
|
def file_via_connection(path)
|
@@ -112,10 +112,10 @@ module Train::Transports
|
|
112
112
|
end
|
113
113
|
|
114
114
|
class WindowsShellRunner
|
115
|
-
require
|
116
|
-
require
|
115
|
+
require "json"
|
116
|
+
require "base64"
|
117
117
|
|
118
|
-
def initialize(powershell_cmd =
|
118
|
+
def initialize(powershell_cmd = "powershell")
|
119
119
|
@powershell_cmd = powershell_cmd
|
120
120
|
end
|
121
121
|
|
@@ -124,7 +124,7 @@ module Train::Transports
|
|
124
124
|
script = "$ProgressPreference='SilentlyContinue';" + script
|
125
125
|
|
126
126
|
# Encode script so PowerShell can use it
|
127
|
-
script = script.encode(
|
127
|
+
script = script.encode("UTF-16LE", "UTF-8")
|
128
128
|
base64_script = Base64.strict_encode64(script)
|
129
129
|
|
130
130
|
cmd = "#{@powershell_cmd} -NoProfile -EncodedCommand #{base64_script}"
|
@@ -136,14 +136,14 @@ module Train::Transports
|
|
136
136
|
end
|
137
137
|
|
138
138
|
class WindowsPipeRunner
|
139
|
-
require
|
140
|
-
require
|
141
|
-
require
|
139
|
+
require "json"
|
140
|
+
require "base64"
|
141
|
+
require "securerandom"
|
142
142
|
|
143
|
-
def initialize(powershell_cmd =
|
143
|
+
def initialize(powershell_cmd = "powershell")
|
144
144
|
@powershell_cmd = powershell_cmd
|
145
145
|
@pipe = acquire_pipe
|
146
|
-
|
146
|
+
raise PipeError if @pipe.nil?
|
147
147
|
end
|
148
148
|
|
149
149
|
def run_command(cmd)
|
@@ -167,7 +167,7 @@ module Train::Transports
|
|
167
167
|
# PowerShell needs time to create pipe.
|
168
168
|
100.times do
|
169
169
|
begin
|
170
|
-
pipe = open("//./pipe/#{pipe_name}",
|
170
|
+
pipe = open("//./pipe/#{pipe_name}", "r+")
|
171
171
|
break
|
172
172
|
rescue
|
173
173
|
sleep 0.1
|
@@ -178,7 +178,7 @@ module Train::Transports
|
|
178
178
|
end
|
179
179
|
|
180
180
|
def start_pipe_server(pipe_name)
|
181
|
-
require
|
181
|
+
require "win32/process"
|
182
182
|
|
183
183
|
script = <<-EOF
|
184
184
|
$ErrorActionPreference = 'Stop'
|
@@ -213,14 +213,14 @@ module Train::Transports
|
|
213
213
|
}
|
214
214
|
EOF
|
215
215
|
|
216
|
-
utf8_script = script.encode(
|
216
|
+
utf8_script = script.encode("UTF-16LE", "UTF-8")
|
217
217
|
base64_script = Base64.strict_encode64(utf8_script)
|
218
218
|
cmd = "#{@powershell_cmd} -NoProfile -ExecutionPolicy bypass -NonInteractive -EncodedCommand #{base64_script}"
|
219
219
|
|
220
220
|
server_pid = Process.create(command_line: cmd).process_id
|
221
221
|
|
222
222
|
# Ensure process is killed when the Train process exits
|
223
|
-
at_exit { Process.kill(
|
223
|
+
at_exit { Process.kill("KILL", server_pid) }
|
224
224
|
end
|
225
225
|
end
|
226
226
|
end
|