right_agent 2.0.7-x86-mingw32
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/LICENSE +20 -0
- data/README.rdoc +82 -0
- data/Rakefile +113 -0
- data/lib/right_agent.rb +59 -0
- data/lib/right_agent/actor.rb +182 -0
- data/lib/right_agent/actor_registry.rb +76 -0
- data/lib/right_agent/actors/agent_manager.rb +232 -0
- data/lib/right_agent/agent.rb +1149 -0
- data/lib/right_agent/agent_config.rb +480 -0
- data/lib/right_agent/agent_identity.rb +210 -0
- data/lib/right_agent/agent_tag_manager.rb +237 -0
- data/lib/right_agent/audit_formatter.rb +107 -0
- data/lib/right_agent/clients.rb +31 -0
- data/lib/right_agent/clients/api_client.rb +383 -0
- data/lib/right_agent/clients/auth_client.rb +247 -0
- data/lib/right_agent/clients/balanced_http_client.rb +369 -0
- data/lib/right_agent/clients/base_retry_client.rb +495 -0
- data/lib/right_agent/clients/right_http_client.rb +279 -0
- data/lib/right_agent/clients/router_client.rb +493 -0
- data/lib/right_agent/command.rb +30 -0
- data/lib/right_agent/command/agent_manager_commands.rb +150 -0
- data/lib/right_agent/command/command_client.rb +136 -0
- data/lib/right_agent/command/command_constants.rb +33 -0
- data/lib/right_agent/command/command_io.rb +126 -0
- data/lib/right_agent/command/command_parser.rb +87 -0
- data/lib/right_agent/command/command_runner.rb +118 -0
- data/lib/right_agent/command/command_serializer.rb +63 -0
- data/lib/right_agent/connectivity_checker.rb +179 -0
- data/lib/right_agent/console.rb +65 -0
- data/lib/right_agent/core_payload_types.rb +44 -0
- data/lib/right_agent/core_payload_types/cookbook.rb +61 -0
- data/lib/right_agent/core_payload_types/cookbook_position.rb +46 -0
- data/lib/right_agent/core_payload_types/cookbook_repository.rb +116 -0
- data/lib/right_agent/core_payload_types/cookbook_sequence.rb +70 -0
- data/lib/right_agent/core_payload_types/dev_repositories.rb +100 -0
- data/lib/right_agent/core_payload_types/dev_repository.rb +76 -0
- data/lib/right_agent/core_payload_types/event_categories.rb +38 -0
- data/lib/right_agent/core_payload_types/executable_bundle.rb +130 -0
- data/lib/right_agent/core_payload_types/login_policy.rb +72 -0
- data/lib/right_agent/core_payload_types/login_user.rb +79 -0
- data/lib/right_agent/core_payload_types/planned_volume.rb +94 -0
- data/lib/right_agent/core_payload_types/recipe_instantiation.rb +73 -0
- data/lib/right_agent/core_payload_types/repositories_bundle.rb +50 -0
- data/lib/right_agent/core_payload_types/right_script_attachment.rb +95 -0
- data/lib/right_agent/core_payload_types/right_script_instantiation.rb +94 -0
- data/lib/right_agent/core_payload_types/runlist_policy.rb +44 -0
- data/lib/right_agent/core_payload_types/secure_document.rb +66 -0
- data/lib/right_agent/core_payload_types/secure_document_location.rb +63 -0
- data/lib/right_agent/core_payload_types/software_repository_instantiation.rb +61 -0
- data/lib/right_agent/daemonize.rb +35 -0
- data/lib/right_agent/dispatched_cache.rb +109 -0
- data/lib/right_agent/dispatcher.rb +272 -0
- data/lib/right_agent/enrollment_result.rb +221 -0
- data/lib/right_agent/exceptions.rb +87 -0
- data/lib/right_agent/history.rb +145 -0
- data/lib/right_agent/log.rb +460 -0
- data/lib/right_agent/minimal.rb +46 -0
- data/lib/right_agent/monkey_patches.rb +30 -0
- data/lib/right_agent/monkey_patches/ruby_patch.rb +55 -0
- data/lib/right_agent/monkey_patches/ruby_patch/array_patch.rb +29 -0
- data/lib/right_agent/monkey_patches/ruby_patch/darwin_patch.rb +24 -0
- data/lib/right_agent/monkey_patches/ruby_patch/linux_patch.rb +24 -0
- data/lib/right_agent/monkey_patches/ruby_patch/linux_patch/file_patch.rb +30 -0
- data/lib/right_agent/monkey_patches/ruby_patch/object_patch.rb +49 -0
- data/lib/right_agent/monkey_patches/ruby_patch/windows_patch.rb +32 -0
- data/lib/right_agent/monkey_patches/ruby_patch/windows_patch/file_patch.rb +60 -0
- data/lib/right_agent/monkey_patches/ruby_patch/windows_patch/process_patch.rb +63 -0
- data/lib/right_agent/monkey_patches/ruby_patch/windows_patch/stdio_patch.rb +27 -0
- data/lib/right_agent/monkey_patches/ruby_patch/windows_patch/time_patch.rb +55 -0
- data/lib/right_agent/monkey_patches/ruby_patch/windows_patch/win32ole_patch.rb +34 -0
- data/lib/right_agent/multiplexer.rb +102 -0
- data/lib/right_agent/offline_handler.rb +270 -0
- data/lib/right_agent/operation_result.rb +300 -0
- data/lib/right_agent/packets.rb +673 -0
- data/lib/right_agent/payload_formatter.rb +104 -0
- data/lib/right_agent/pending_requests.rb +128 -0
- data/lib/right_agent/pid_file.rb +159 -0
- data/lib/right_agent/platform.rb +770 -0
- data/lib/right_agent/platform/unix/darwin/platform.rb +102 -0
- data/lib/right_agent/platform/unix/linux/platform.rb +305 -0
- data/lib/right_agent/platform/unix/platform.rb +226 -0
- data/lib/right_agent/platform/windows/mingw/platform.rb +447 -0
- data/lib/right_agent/platform/windows/mswin/platform.rb +236 -0
- data/lib/right_agent/platform/windows/platform.rb +1808 -0
- data/lib/right_agent/protocol_version_mixin.rb +69 -0
- data/lib/right_agent/retryable_request.rb +195 -0
- data/lib/right_agent/scripts/agent_controller.rb +543 -0
- data/lib/right_agent/scripts/agent_deployer.rb +400 -0
- data/lib/right_agent/scripts/common_parser.rb +160 -0
- data/lib/right_agent/scripts/log_level_manager.rb +192 -0
- data/lib/right_agent/scripts/stats_manager.rb +268 -0
- data/lib/right_agent/scripts/usage.rb +58 -0
- data/lib/right_agent/secure_identity.rb +92 -0
- data/lib/right_agent/security.rb +32 -0
- data/lib/right_agent/security/cached_certificate_store_proxy.rb +77 -0
- data/lib/right_agent/security/certificate.rb +102 -0
- data/lib/right_agent/security/certificate_cache.rb +89 -0
- data/lib/right_agent/security/distinguished_name.rb +56 -0
- data/lib/right_agent/security/encrypted_document.rb +83 -0
- data/lib/right_agent/security/rsa_key_pair.rb +76 -0
- data/lib/right_agent/security/signature.rb +86 -0
- data/lib/right_agent/security/static_certificate_store.rb +85 -0
- data/lib/right_agent/sender.rb +792 -0
- data/lib/right_agent/serialize.rb +29 -0
- data/lib/right_agent/serialize/message_pack.rb +107 -0
- data/lib/right_agent/serialize/secure_serializer.rb +151 -0
- data/lib/right_agent/serialize/secure_serializer_initializer.rb +47 -0
- data/lib/right_agent/serialize/serializable.rb +151 -0
- data/lib/right_agent/serialize/serializer.rb +159 -0
- data/lib/right_agent/subprocess.rb +38 -0
- data/lib/right_agent/tracer.rb +124 -0
- data/right_agent.gemspec +101 -0
- data/spec/actor_registry_spec.rb +80 -0
- data/spec/actor_spec.rb +162 -0
- data/spec/agent_config_spec.rb +235 -0
- data/spec/agent_identity_spec.rb +78 -0
- data/spec/agent_spec.rb +734 -0
- data/spec/agent_tag_manager_spec.rb +319 -0
- data/spec/clients/api_client_spec.rb +423 -0
- data/spec/clients/auth_client_spec.rb +272 -0
- data/spec/clients/balanced_http_client_spec.rb +576 -0
- data/spec/clients/base_retry_client_spec.rb +635 -0
- data/spec/clients/router_client_spec.rb +594 -0
- data/spec/clients/spec_helper.rb +111 -0
- data/spec/command/agent_manager_commands_spec.rb +51 -0
- data/spec/command/command_io_spec.rb +93 -0
- data/spec/command/command_parser_spec.rb +79 -0
- data/spec/command/command_runner_spec.rb +107 -0
- data/spec/command/command_serializer_spec.rb +51 -0
- data/spec/connectivity_checker_spec.rb +83 -0
- data/spec/core_payload_types/dev_repositories_spec.rb +64 -0
- data/spec/core_payload_types/dev_repository_spec.rb +33 -0
- data/spec/core_payload_types/executable_bundle_spec.rb +67 -0
- data/spec/core_payload_types/login_user_spec.rb +102 -0
- data/spec/core_payload_types/recipe_instantiation_spec.rb +81 -0
- data/spec/core_payload_types/right_script_attachment_spec.rb +65 -0
- data/spec/core_payload_types/right_script_instantiation_spec.rb +79 -0
- data/spec/core_payload_types/spec_helper.rb +23 -0
- data/spec/dispatched_cache_spec.rb +136 -0
- data/spec/dispatcher_spec.rb +324 -0
- data/spec/enrollment_result_spec.rb +53 -0
- data/spec/history_spec.rb +246 -0
- data/spec/log_spec.rb +192 -0
- data/spec/monkey_patches/eventmachine_spec.rb +62 -0
- data/spec/multiplexer_spec.rb +48 -0
- data/spec/offline_handler_spec.rb +340 -0
- data/spec/operation_result_spec.rb +208 -0
- data/spec/packets_spec.rb +461 -0
- data/spec/pending_requests_spec.rb +136 -0
- data/spec/platform/spec_helper.rb +216 -0
- data/spec/platform/unix/darwin/platform_spec.rb +181 -0
- data/spec/platform/unix/linux/platform_spec.rb +540 -0
- data/spec/platform/unix/spec_helper.rb +149 -0
- data/spec/platform/windows/mingw/platform_spec.rb +222 -0
- data/spec/platform/windows/mswin/platform_spec.rb +259 -0
- data/spec/platform/windows/spec_helper.rb +720 -0
- data/spec/retryable_request_spec.rb +306 -0
- data/spec/secure_identity_spec.rb +50 -0
- data/spec/security/cached_certificate_store_proxy_spec.rb +62 -0
- data/spec/security/certificate_cache_spec.rb +71 -0
- data/spec/security/certificate_spec.rb +49 -0
- data/spec/security/distinguished_name_spec.rb +46 -0
- data/spec/security/encrypted_document_spec.rb +55 -0
- data/spec/security/rsa_key_pair_spec.rb +55 -0
- data/spec/security/signature_spec.rb +66 -0
- data/spec/security/static_certificate_store_spec.rb +58 -0
- data/spec/sender_spec.rb +1045 -0
- data/spec/serialize/message_pack_spec.rb +131 -0
- data/spec/serialize/secure_serializer_spec.rb +132 -0
- data/spec/serialize/serializable_spec.rb +90 -0
- data/spec/serialize/serializer_spec.rb +197 -0
- data/spec/spec.opts +2 -0
- data/spec/spec.win32.opts +1 -0
- data/spec/spec_helper.rb +130 -0
- data/spec/tracer_spec.rb +114 -0
- metadata +447 -0
@@ -0,0 +1,447 @@
|
|
1
|
+
#
|
2
|
+
# Copyright (c) 2013 RightScale Inc
|
3
|
+
#
|
4
|
+
# Permission is hereby granted, free of charge, to any person obtaining
|
5
|
+
# a copy of this software and associated documentation files (the
|
6
|
+
# "Software"), to deal in the Software without restriction, including
|
7
|
+
# without limitation the rights to use, copy, modify, merge, publish,
|
8
|
+
# distribute, sublicense, and/or sell copies of the Software, and to
|
9
|
+
# permit persons to whom the Software is furnished to do so, subject to
|
10
|
+
# the following conditions:
|
11
|
+
#
|
12
|
+
# The above copyright notice and this permission notice shall be
|
13
|
+
# included in all copies or substantial portions of the Software.
|
14
|
+
#
|
15
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
16
|
+
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
17
|
+
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
18
|
+
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
19
|
+
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
20
|
+
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
21
|
+
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
22
|
+
|
23
|
+
require ::File.expand_path('../../../../platform', __FILE__)
|
24
|
+
|
25
|
+
# ignore unless Windows as a concession to spec testing from non-Windows.
|
26
|
+
# any windows-only gems must be fully mocked under test.
|
27
|
+
if RUBY_PLATFORM =~ /mingw/
|
28
|
+
# Foreign Function Interface used for all API calls in mingw
|
29
|
+
require 'ffi'
|
30
|
+
end
|
31
|
+
|
32
|
+
module RightScale
|
33
|
+
|
34
|
+
# Windows specific implementation
|
35
|
+
class Platform
|
36
|
+
|
37
|
+
# helpers
|
38
|
+
class PlatformHelperBase
|
39
|
+
# _W (i.e. 'wide') Windows APIs all use little-endian unicode.
|
40
|
+
#
|
41
|
+
# _A doesn't correspond to any particular single/multi-byte encoding (even
|
42
|
+
# though n00bs get confused and think in means ASCII). _A actually means
|
43
|
+
# 'use the current codepage' for which you would need to make another API
|
44
|
+
# call to discover what the current thread thinks is the current codepage.
|
45
|
+
WIDE = ::Encoding::UTF_16LE
|
46
|
+
|
47
|
+
# We favor the _W APIs to ensure proper marshalling of Unicode characters
|
48
|
+
# to/from the ruby interpreter's default codepage. This method facilitates
|
49
|
+
# API calls that fill a Unicode buffer and need to marshal the buffered
|
50
|
+
# data to a multi-byte (default encoding) buffer.
|
51
|
+
#
|
52
|
+
# @param [String] buffer to receive marshalled data from Unicode API or
|
53
|
+
# nil to query required buffer.
|
54
|
+
# @param [Hash] copy_options for copy_to_string_buffer
|
55
|
+
#
|
56
|
+
# @yield [buffer] yields the buffer at current size for API call
|
57
|
+
# @yieldparam [String] buffer to use for API call (get size from buffer)
|
58
|
+
# or nil to query required buffer size.
|
59
|
+
#
|
60
|
+
# @return [String] buffered data length or zero for failure
|
61
|
+
def with_unicode_buffer(buffer, copy_options = {})
|
62
|
+
# note that _W methods always expect UTF-16 LE (Little Endian) due to
|
63
|
+
# the Intel chipset representing word values as LE. Windows runs on
|
64
|
+
# other chipsets but LE is always used by API calls.
|
65
|
+
if buffer && buffer.encoding != WIDE
|
66
|
+
unicode_buffer = 0.chr.encode(WIDE) * buffer.size
|
67
|
+
unicode_length_or_buffer_count = yield(unicode_buffer)
|
68
|
+
|
69
|
+
if unicode_length_or_buffer_count > 0 &&
|
70
|
+
unicode_length_or_buffer_count < unicode_buffer.size
|
71
|
+
|
72
|
+
# reencode to non-Unicode buffer, including trailing NUL character.
|
73
|
+
#
|
74
|
+
# note that reencoding may exceed given (Unicode) buffer size
|
75
|
+
# because of two-byte chars expanded from single unicode chars.
|
76
|
+
# in this case the required multi-byte buffer size will be returned
|
77
|
+
# and then promoted to a 'doubled' Unicode buffer size on next call.
|
78
|
+
result = copy_to_string_buffer(
|
79
|
+
unicode_buffer[0, unicode_length_or_buffer_count + 1],
|
80
|
+
buffer,
|
81
|
+
copy_options)
|
82
|
+
else
|
83
|
+
result = unicode_length_or_buffer_count
|
84
|
+
end
|
85
|
+
else
|
86
|
+
# buffer is already UTF-16LE or else nil
|
87
|
+
result = yield(buffer)
|
88
|
+
end
|
89
|
+
result
|
90
|
+
end
|
91
|
+
|
92
|
+
# Performs a bytewise copy to given target buffer with respect for the
|
93
|
+
# encoding of the source and target buffers. Assumes a NUL terminator
|
94
|
+
# appears in the string to be copied per normal Windows API behavor.
|
95
|
+
#
|
96
|
+
# @param [String] source_buffer encoded source
|
97
|
+
# @param [String] target_buffer encoded target
|
98
|
+
# @param [Hash] options for copy
|
99
|
+
# @option options [TrueClass|FalseClass] :truncate is true to allow
|
100
|
+
# trunction of string to fit buffer, false to return required buffer
|
101
|
+
# size if copy would exceed buffer
|
102
|
+
#
|
103
|
+
# @return [Integer] length of encoded target or else buffer length needed for full copy
|
104
|
+
def copy_to_string_buffer(source_buffer, target_buffer, options = {})
|
105
|
+
options = { :truncate => false }.merge(options)
|
106
|
+
|
107
|
+
# note that a buffer of NULs will default to ASCII encoding in
|
108
|
+
# ruby 1.9 so change to use default encoding automagically. this makes
|
109
|
+
# it easier to support the default codepage in 1.9 but be oblivious to
|
110
|
+
# it in 1.8, which does not support these encoding methods. if the
|
111
|
+
# caller wants any other codepage (in 1.9) then it should be specified
|
112
|
+
# on the target.
|
113
|
+
if target_buffer.encoding == Encoding::ASCII
|
114
|
+
# we can force encoding only if the default encoding is ASCII
|
115
|
+
# compatible. we are going to clobber the bytes so the old bytes are
|
116
|
+
# irrelevant unless the bytesize of the buffer would differ.
|
117
|
+
if ::Encoding.default_external.ascii_compatible?
|
118
|
+
# note that force_encoding modifies self even though it's not a
|
119
|
+
# banged! method.
|
120
|
+
target_buffer.force_encoding(::Encoding.default_external)
|
121
|
+
else
|
122
|
+
target_buffer.encode!(::Encoding.default_external)
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
# reencode the source buffer, changing any characters that cannot be
|
127
|
+
# encoded to the default replacement character, which is usually '?'
|
128
|
+
target_encoding = target_buffer.encoding
|
129
|
+
reencoded_source_buffer = source_buffer.encode(
|
130
|
+
target_encoding, :invalid => :replace, :undef => :replace)
|
131
|
+
|
132
|
+
# determine if sufficient buffer exists.
|
133
|
+
result = nil
|
134
|
+
nul_char = 0.chr.encode(target_encoding)
|
135
|
+
reencoded_source_length = reencoded_source_buffer.index(nul_char) ||
|
136
|
+
reencoded_source_buffer.length
|
137
|
+
if reencoded_source_length <= target_buffer.length || options[:truncate]
|
138
|
+
# bytewise replacement (to reuse caller's buffer instead of
|
139
|
+
# reallocating the string's buffer).
|
140
|
+
copy_length = [reencoded_source_length, target_buffer.length - 1].min
|
141
|
+
copy_byte_count = nul_char.bytesize * copy_length
|
142
|
+
reencoded_source_buffer.bytes.each_with_index do |b, b_index|
|
143
|
+
break if b_index >= copy_byte_count
|
144
|
+
target_buffer.setbyte(b_index, b)
|
145
|
+
end
|
146
|
+
target_buffer[copy_length] = nul_char
|
147
|
+
result = copy_length
|
148
|
+
else
|
149
|
+
result = reencoded_source_length
|
150
|
+
end
|
151
|
+
result
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
155
|
+
class Filesystem
|
156
|
+
|
157
|
+
# FFI APIs
|
158
|
+
class API
|
159
|
+
extend FFI::Library
|
160
|
+
|
161
|
+
typedef :ulong, :dword
|
162
|
+
|
163
|
+
ffi_lib :kernel32
|
164
|
+
|
165
|
+
# the FFI wiki documents that it is important to specify the stdcall
|
166
|
+
# calling convention properly, but public gems like win32-dir don't
|
167
|
+
# bother; who do you believe?
|
168
|
+
#
|
169
|
+
# @see https://github.com/ffi/ffi/wiki/Windows-Examples
|
170
|
+
#
|
171
|
+
# looking at the FFI assembly code, it may workaround a gem using the
|
172
|
+
# wrong calling convention by always restoring the stack pointer after
|
173
|
+
# the call, but that's no excuse to call things improperly.
|
174
|
+
ffi_convention :stdcall
|
175
|
+
|
176
|
+
# yes, we could import the _A APIs but what codepage would they use?
|
177
|
+
# it's better not to think about it and do the extra marshalling for _W,
|
178
|
+
# which is always Encoding::UTF_16LE. the reality of WinNT+ is that it
|
179
|
+
# implements all APIs as _W and does extra marshalling with a lookup to
|
180
|
+
# the thread's current codepage on every call to an _A API.
|
181
|
+
attach_function :GetShortPathNameW, [:buffer_in, :buffer_out, :dword], :dword
|
182
|
+
attach_function :GetTempPathW, [:dword, :buffer_out], :dword
|
183
|
+
|
184
|
+
begin
|
185
|
+
attach_function :CreateSymbolicLinkW, [:buffer_in, :buffer_in, :dword], :bool
|
186
|
+
rescue FFI::NotFoundError
|
187
|
+
# we don't really support 2003 server any longer but ignore this.
|
188
|
+
end
|
189
|
+
end
|
190
|
+
|
191
|
+
# Overrides base Filesystem#CreateSymbolicLink
|
192
|
+
def CreateSymbolicLink(symlink_file_path, target_file_path, flags)
|
193
|
+
must_be_string(symlink_file_path)
|
194
|
+
must_be_string(target_file_path)
|
195
|
+
must_be_dword(flags)
|
196
|
+
if defined?(API.CreateSymbolicLinkW)
|
197
|
+
API.CreateSymbolicLinkW(
|
198
|
+
symlink_file_path.encode(WIDE),
|
199
|
+
target_file_path.encode(WIDE),
|
200
|
+
flags)
|
201
|
+
else
|
202
|
+
raise ::RightScale::Exceptions::PlatformError,
|
203
|
+
'Cannot create symlinks on this platform'
|
204
|
+
end
|
205
|
+
end
|
206
|
+
|
207
|
+
# Overrides base Filesystem#GetShortPathName
|
208
|
+
def GetShortPathName(long_path, short_path_buffer, short_path_buffer_length)
|
209
|
+
must_be_string(long_path)
|
210
|
+
must_be_char_buffer_or_nil(short_path_buffer, short_path_buffer_length)
|
211
|
+
with_unicode_buffer(short_path_buffer) do |unicode_buffer|
|
212
|
+
API.GetShortPathNameW(
|
213
|
+
long_path.encode(WIDE),
|
214
|
+
unicode_buffer,
|
215
|
+
unicode_buffer ? unicode_buffer.size : 0)
|
216
|
+
end
|
217
|
+
end
|
218
|
+
|
219
|
+
# Overrides base Filesystem#GetTempDir
|
220
|
+
def GetTempPath(buffer_length, buffer)
|
221
|
+
must_be_char_buffer_or_nil(buffer, buffer_length)
|
222
|
+
with_unicode_buffer(buffer) do |unicode_buffer|
|
223
|
+
API.GetTempPathW(
|
224
|
+
unicode_buffer ? unicode_buffer.size : 0,
|
225
|
+
unicode_buffer)
|
226
|
+
end
|
227
|
+
end
|
228
|
+
end # Filesystem
|
229
|
+
|
230
|
+
class Controller
|
231
|
+
|
232
|
+
# FFI APIs
|
233
|
+
class API
|
234
|
+
extend FFI::Library
|
235
|
+
|
236
|
+
typedef :ulong, :dword
|
237
|
+
|
238
|
+
ffi_lib :advapi32
|
239
|
+
ffi_convention :stdcall
|
240
|
+
|
241
|
+
attach_function :InitiateSystemShutdownW, [:buffer_in, :buffer_in, :dword, :bool, :bool], :bool
|
242
|
+
end
|
243
|
+
|
244
|
+
# Overrides base Controller#InitiateSystemShutdown
|
245
|
+
def InitiateSystemShutdown(machine_name, message, timeout, force_apps_closed, reboot_after_shutdown)
|
246
|
+
must_be_string_or_nil(machine_name)
|
247
|
+
must_be_string_or_nil(message)
|
248
|
+
must_be_dword(timeout)
|
249
|
+
must_be_bool(force_apps_closed)
|
250
|
+
must_be_bool(reboot_after_shutdown)
|
251
|
+
API.InitiateSystemShutdownW(
|
252
|
+
machine_name ? machine_name.encode(WIDE) : nil,
|
253
|
+
message ? message.encode(WIDE) : nil,
|
254
|
+
timeout,
|
255
|
+
force_apps_closed,
|
256
|
+
reboot_after_shutdown)
|
257
|
+
end
|
258
|
+
end # Controller
|
259
|
+
|
260
|
+
class Process
|
261
|
+
|
262
|
+
# FFI APIs
|
263
|
+
class API
|
264
|
+
extend FFI::Library
|
265
|
+
|
266
|
+
typedef :uintptr_t, :handle
|
267
|
+
typedef :ulong, :dword
|
268
|
+
|
269
|
+
ffi_lib :kernel32
|
270
|
+
ffi_convention :stdcall
|
271
|
+
|
272
|
+
attach_function :GetCurrentProcess, [], :handle
|
273
|
+
|
274
|
+
ffi_lib :psapi
|
275
|
+
ffi_convention :stdcall
|
276
|
+
|
277
|
+
attach_function :GetProcessMemoryInfo, [:handle, :buffer_out, :dword], :bool
|
278
|
+
|
279
|
+
ffi_lib :advapi32
|
280
|
+
ffi_convention :stdcall
|
281
|
+
|
282
|
+
attach_function :OpenProcessToken, [:handle, :dword, :buffer_out], :bool
|
283
|
+
end
|
284
|
+
|
285
|
+
# Overrides base Process#GetCurrentProcess
|
286
|
+
def GetCurrentProcess
|
287
|
+
API.GetCurrentProcess
|
288
|
+
end
|
289
|
+
|
290
|
+
# Overrides base Process#GetProcessMemoryInfo
|
291
|
+
def GetProcessMemoryInfo(process_handle, process_memory_info_buffer, process_memory_info_buffer_size)
|
292
|
+
must_be_dword(process_handle)
|
293
|
+
must_be_size_prefixed_buffer(process_memory_info_buffer, process_memory_info_buffer_size)
|
294
|
+
API.GetProcessMemoryInfo(
|
295
|
+
process_handle,
|
296
|
+
process_memory_info_buffer,
|
297
|
+
process_memory_info_buffer_size)
|
298
|
+
end
|
299
|
+
|
300
|
+
# Overrides base Process#OpenProcessToken
|
301
|
+
def OpenProcessToken(process_handle, desired_access, token_handle)
|
302
|
+
must_be_dword(process_handle)
|
303
|
+
must_be_dword(desired_access)
|
304
|
+
must_be_byte_buffer(token_handle, SIZEOF_DWORD)
|
305
|
+
API.OpenProcessToken(process_handle, desired_access, token_handle)
|
306
|
+
end
|
307
|
+
end # Process
|
308
|
+
|
309
|
+
class WindowsCommon
|
310
|
+
|
311
|
+
# FFI APIs
|
312
|
+
class API
|
313
|
+
extend FFI::Library
|
314
|
+
|
315
|
+
typedef :uintptr_t, :handle
|
316
|
+
typedef :ulong, :dword
|
317
|
+
|
318
|
+
ffi_lib :kernel32
|
319
|
+
ffi_convention :stdcall
|
320
|
+
|
321
|
+
attach_function :CloseHandle, [:handle], :bool
|
322
|
+
attach_function :FormatMessageW, [:dword, :buffer_in, :dword, :dword, :buffer_out, :dword, :buffer_in], :dword
|
323
|
+
end
|
324
|
+
|
325
|
+
# Overrides base WindowsCommon#CloseHandle
|
326
|
+
def CloseHandle(handle)
|
327
|
+
must_be_dword(handle)
|
328
|
+
API.CloseHandle(handle)
|
329
|
+
end
|
330
|
+
|
331
|
+
# Overrides base WindowsCommon#FormatMessage
|
332
|
+
def FormatMessage(flags, source, message_id, language_id, buffer, buffer_size, arguments)
|
333
|
+
unless source.nil? && arguments.nil?
|
334
|
+
raise ::NotImplementedError,
|
335
|
+
'Not supporting FormatMessage source or arguments'
|
336
|
+
end
|
337
|
+
must_be_dword(message_id)
|
338
|
+
must_be_dword(language_id)
|
339
|
+
must_be_char_buffer(buffer, buffer_size)
|
340
|
+
with_unicode_buffer(buffer, :truncate => true) do |unicode_buffer|
|
341
|
+
API.FormatMessageW(
|
342
|
+
flags, source, message_id, language_id,
|
343
|
+
unicode_buffer, unicode_buffer.size, arguments)
|
344
|
+
end
|
345
|
+
end
|
346
|
+
|
347
|
+
# Overrides base WindowsCommon#GetLastError
|
348
|
+
def GetLastError
|
349
|
+
# note that you cannot call GetLastError API via FFI without changing
|
350
|
+
# the last error code; Heisenberg Uncertainty API. the documented
|
351
|
+
# workaround is to call the following method.
|
352
|
+
::FFI::errno
|
353
|
+
end
|
354
|
+
end # WindowsCommon
|
355
|
+
|
356
|
+
class WindowsSecurity
|
357
|
+
|
358
|
+
# FFI APIs
|
359
|
+
class API
|
360
|
+
extend FFI::Library
|
361
|
+
|
362
|
+
typedef :uintptr_t, :handle
|
363
|
+
typedef :ulong, :dword
|
364
|
+
|
365
|
+
ffi_lib :advapi32
|
366
|
+
ffi_convention :stdcall
|
367
|
+
|
368
|
+
attach_function :LookupPrivilegeValueW, [:buffer_in, :buffer_in, :pointer], :bool
|
369
|
+
attach_function :AdjustTokenPrivileges, [:handle, :bool, :buffer_in, :dword, :buffer_out, :buffer_out], :bool
|
370
|
+
end
|
371
|
+
|
372
|
+
# Overrides base WindowsSecurity#LookupPrivilegeValue
|
373
|
+
def LookupPrivilegeValue(system_name, name, luid)
|
374
|
+
must_be_string_or_nil(system_name)
|
375
|
+
must_be_string(name)
|
376
|
+
must_be_byte_buffer(luid, SIZEOF_QWORD)
|
377
|
+
API.LookupPrivilegeValueW(
|
378
|
+
system_name ? system_name.encode(WIDE) : nil,
|
379
|
+
name.encode(WIDE),
|
380
|
+
luid)
|
381
|
+
end
|
382
|
+
|
383
|
+
# Overrides base WindowsSecurity#AdjustTokenPrivileges
|
384
|
+
def AdjustTokenPrivileges(token_handle, disable_all_privileges, new_state, buffer_length, previous_state, return_length)
|
385
|
+
must_be_dword(token_handle)
|
386
|
+
must_be_bool(disable_all_privileges)
|
387
|
+
must_be_buffer_or_nil(new_state)
|
388
|
+
must_be_byte_buffer_or_nil(previous_state, buffer_length)
|
389
|
+
must_be_byte_buffer_or_nil(return_length, return_length ? SIZEOF_DWORD : 0)
|
390
|
+
API.AdjustTokenPrivileges(
|
391
|
+
token_handle, disable_all_privileges, new_state,
|
392
|
+
buffer_length, previous_state, return_length)
|
393
|
+
end
|
394
|
+
end # WindowsSecurity
|
395
|
+
|
396
|
+
class WindowsSystemInformation
|
397
|
+
|
398
|
+
# FFI APIs
|
399
|
+
class API
|
400
|
+
extend FFI::Library
|
401
|
+
|
402
|
+
typedef :ulong, :dword
|
403
|
+
|
404
|
+
ffi_lib :kernel32
|
405
|
+
ffi_convention :stdcall
|
406
|
+
|
407
|
+
attach_function :GetVersionExW, [:buffer_out], :bool
|
408
|
+
end
|
409
|
+
|
410
|
+
# Overrides base WindowsSystemInformation#GetVersionEx
|
411
|
+
def GetVersionEx(version_info_buffer)
|
412
|
+
must_be_size_prefixed_buffer(version_info_buffer)
|
413
|
+
API.GetVersionExW(version_info_buffer)
|
414
|
+
end
|
415
|
+
|
416
|
+
# Overrides base WindowsSystemInformation#create_os_version_info
|
417
|
+
def create_os_version_info
|
418
|
+
[
|
419
|
+
276, # 0 - size of OSVERSIONINFO (IN)
|
420
|
+
0, # 4 - major version (OUT)
|
421
|
+
0, # 8 - minor version (OUT)
|
422
|
+
0, # 12 - build (OUT)
|
423
|
+
0, # 16 - platform (OUT)
|
424
|
+
0.chr * 128 * 2 # 20 - additional info (OUT)
|
425
|
+
].pack('LLLLLa256')
|
426
|
+
end
|
427
|
+
|
428
|
+
# Overrides base WindowsSystemInformation#unpack_os_version_info
|
429
|
+
def unpack_os_version_info(buffer)
|
430
|
+
result = buffer.unpack('LLLLL')
|
431
|
+
additional_info = buffer[20, 256].force_encoding(WIDE)
|
432
|
+
additional_info_length = additional_info.index(0.chr.encode(additional_info.encoding)) || additional_info.length
|
433
|
+
result << additional_info[0, additional_info_length].encode(::Encoding.default_external)
|
434
|
+
result
|
435
|
+
end
|
436
|
+
end # WindowsSystemInformation
|
437
|
+
|
438
|
+
private
|
439
|
+
|
440
|
+
# Overrides base Platform#initialize_species
|
441
|
+
def initialize_species
|
442
|
+
true # do nothing
|
443
|
+
end
|
444
|
+
|
445
|
+
end # Platform
|
446
|
+
|
447
|
+
end # RightScale
|
@@ -0,0 +1,236 @@
|
|
1
|
+
#
|
2
|
+
# Copyright (c) 2009-2013 RightScale Inc
|
3
|
+
#
|
4
|
+
# Permission is hereby granted, free of charge, to any person obtaining
|
5
|
+
# a copy of this software and associated documentation files (the
|
6
|
+
# "Software"), to deal in the Software without restriction, including
|
7
|
+
# without limitation the rights to use, copy, modify, merge, publish,
|
8
|
+
# distribute, sublicense, and/or sell copies of the Software, and to
|
9
|
+
# permit persons to whom the Software is furnished to do so, subject to
|
10
|
+
# the following conditions:
|
11
|
+
#
|
12
|
+
# The above copyright notice and this permission notice shall be
|
13
|
+
# included in all copies or substantial portions of the Software.
|
14
|
+
#
|
15
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
16
|
+
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
17
|
+
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
18
|
+
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
19
|
+
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
20
|
+
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
21
|
+
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
22
|
+
|
23
|
+
require ::File.expand_path('../../../../platform', __FILE__)
|
24
|
+
|
25
|
+
# ignore unless Windows as a concession to spec testing from non-Windows.
|
26
|
+
# any windows-only gems must be fully mocked under test.
|
27
|
+
if RUBY_PLATFORM =~ /mswin/
|
28
|
+
# legacy API gem for ruby 1.8, superceded by FFI in ruby 1.9
|
29
|
+
require 'win32/api'
|
30
|
+
end
|
31
|
+
|
32
|
+
module RightScale
|
33
|
+
|
34
|
+
# Windows specific implementation
|
35
|
+
class Platform
|
36
|
+
|
37
|
+
# helpers
|
38
|
+
class PlatformHelperBase
|
39
|
+
API_NULL = 0
|
40
|
+
API_FALSE = 0
|
41
|
+
API_TRUE = 1
|
42
|
+
|
43
|
+
private
|
44
|
+
|
45
|
+
# it is incorrect in the general case to say result == API_TRUE
|
46
|
+
# Quote from every API doc I've ever read:
|
47
|
+
# "If the function succeeds, the return value is nonzero"
|
48
|
+
def api_succeeded(result)
|
49
|
+
result != API_FALSE
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
class Filesystem
|
54
|
+
|
55
|
+
# Overrides base Filesystem#CreateSymbolicLink
|
56
|
+
def CreateSymbolicLink(symlink_file_path, target_file_path, flags)
|
57
|
+
must_be_string(symlink_file_path)
|
58
|
+
must_be_string(target_file_path)
|
59
|
+
must_be_dword(flags)
|
60
|
+
@create_symbolic_link ||= ::Win32::API.new('CreateSymbolicLink', 'SSL', 'B', 'kernel32')
|
61
|
+
api_succeeded(
|
62
|
+
@create_symbolic_link.call(
|
63
|
+
symlink_file_path, target_file_path, flags))
|
64
|
+
rescue ::Win32::API::LoadLibraryError
|
65
|
+
raise ::RightScale::Exceptions::PlatformError,
|
66
|
+
'Cannot create symlinks on this platform'
|
67
|
+
end
|
68
|
+
|
69
|
+
# Overrides base Filesystem#GetShortPathName
|
70
|
+
def GetShortPathName(long_path, short_path_buffer, short_path_buffer_length)
|
71
|
+
must_be_string(long_path)
|
72
|
+
must_be_char_buffer(short_path_buffer, short_path_buffer_length)
|
73
|
+
@get_short_path_name ||= ::Win32::API.new('GetShortPathName', 'SPL', 'L', 'kernel32')
|
74
|
+
@get_short_path_name.call(
|
75
|
+
long_path, short_path_buffer, short_path_buffer_length)
|
76
|
+
end
|
77
|
+
|
78
|
+
# Overrides base Filesystem#GetTempPath
|
79
|
+
def GetTempPath(buffer_length, buffer)
|
80
|
+
must_be_char_buffer_or_nil(buffer, buffer_length)
|
81
|
+
@get_temp_dir_api ||= ::Win32::API.new('GetTempPath', 'LP', 'L', 'kernel32')
|
82
|
+
@get_temp_dir_api.call(buffer_length, buffer ? buffer : API_NULL)
|
83
|
+
end
|
84
|
+
end # Filesystem
|
85
|
+
|
86
|
+
class Controller
|
87
|
+
|
88
|
+
# Overrides base Controller#InitiateSystemShutdown
|
89
|
+
def InitiateSystemShutdown(machine_name, message, timeout, force_apps_closed, reboot_after_shutdown)
|
90
|
+
must_be_string_or_nil(machine_name)
|
91
|
+
must_be_string_or_nil(message)
|
92
|
+
must_be_dword(timeout)
|
93
|
+
must_be_bool(force_apps_closed)
|
94
|
+
must_be_bool(reboot_after_shutdown)
|
95
|
+
@initiate_system_shutdown ||= ::Win32::API.new('InitiateSystemShutdown', 'PPLLL', 'B', 'advapi32')
|
96
|
+
api_succeeded(
|
97
|
+
@initiate_system_shutdown.call(
|
98
|
+
machine_name ? machine_name : API_NULL,
|
99
|
+
message ? message : API_NULL,
|
100
|
+
timeout,
|
101
|
+
force_apps_closed ? API_TRUE : API_FALSE,
|
102
|
+
reboot_after_shutdown ? API_TRUE : API_FALSE))
|
103
|
+
end
|
104
|
+
end # Controller
|
105
|
+
|
106
|
+
class Process
|
107
|
+
|
108
|
+
# Overrides base Process#GetCurrentProcess
|
109
|
+
def GetCurrentProcess
|
110
|
+
@get_current_process ||= ::Win32::API.new('GetCurrentProcess', 'V', 'L', 'kernel32')
|
111
|
+
@get_current_process.call
|
112
|
+
end
|
113
|
+
|
114
|
+
# Overrides base Process#GetProcessMemoryInfo
|
115
|
+
def GetProcessMemoryInfo(process_handle, process_memory_info_buffer, process_memory_info_buffer_size)
|
116
|
+
must_be_dword(process_handle)
|
117
|
+
must_be_size_prefixed_buffer(process_memory_info_buffer, process_memory_info_buffer_size)
|
118
|
+
@get_process_memory_info ||= ::Win32::API.new('GetProcessMemoryInfo', 'LPL', 'B', 'psapi')
|
119
|
+
api_succeeded(
|
120
|
+
@get_process_memory_info.call(
|
121
|
+
process_handle,
|
122
|
+
process_memory_info_buffer,
|
123
|
+
process_memory_info_buffer_size))
|
124
|
+
end
|
125
|
+
|
126
|
+
# Overrides base Process#OpenProcessToken
|
127
|
+
def OpenProcessToken(process_handle, desired_access, token_handle)
|
128
|
+
must_be_dword(process_handle)
|
129
|
+
must_be_dword(desired_access)
|
130
|
+
must_be_byte_buffer(token_handle, SIZEOF_DWORD)
|
131
|
+
@open_process_token ||= ::Win32::API.new('OpenProcessToken', 'LLP', 'B', 'advapi32')
|
132
|
+
api_succeeded(
|
133
|
+
@open_process_token.call(
|
134
|
+
process_handle, desired_access, token_handle))
|
135
|
+
end
|
136
|
+
end # Process
|
137
|
+
|
138
|
+
class WindowsCommon
|
139
|
+
|
140
|
+
# Overrides base WindowsCommon#CloseHandle
|
141
|
+
def CloseHandle(handle)
|
142
|
+
must_be_dword(handle)
|
143
|
+
@close_handle ||= ::Win32::API.new('CloseHandle', 'L', 'B', 'kernel32')
|
144
|
+
api_succeeded(@close_handle.call(handle))
|
145
|
+
end
|
146
|
+
|
147
|
+
# Overrides base WindowsCommon#FormatMessage
|
148
|
+
def FormatMessage(flags, source, message_id, language_id, buffer, buffer_size, arguments)
|
149
|
+
if source || arguments
|
150
|
+
raise ::NotImplementedError, 'Not supporting FormatMessage source or arguments'
|
151
|
+
end
|
152
|
+
must_be_dword(message_id)
|
153
|
+
must_be_dword(language_id)
|
154
|
+
must_be_char_buffer(buffer, buffer_size)
|
155
|
+
@format_message ||= ::Win32::API.new('FormatMessage', 'LLLLPLP', 'L', 'kernel32')
|
156
|
+
@format_message.call(flags, API_NULL, message_id, language_id, buffer, buffer_size, API_NULL)
|
157
|
+
end
|
158
|
+
|
159
|
+
# Overrides base WindowsCommon#GetLastError
|
160
|
+
def GetLastError
|
161
|
+
@get_last_error ||= ::Win32::API.new('GetLastError', 'V', 'L', 'kernel32')
|
162
|
+
@get_last_error.call
|
163
|
+
end
|
164
|
+
end # WindowsCommon
|
165
|
+
|
166
|
+
class WindowsSecurity
|
167
|
+
|
168
|
+
# Overrides base WindowsSecurity#LookupPrivilegeValue
|
169
|
+
def LookupPrivilegeValue(system_name, name, luid)
|
170
|
+
must_be_string_or_nil(system_name)
|
171
|
+
must_be_string(name)
|
172
|
+
must_be_byte_buffer(luid, SIZEOF_QWORD)
|
173
|
+
@lookup_privilege_value ||= ::Win32::API.new('LookupPrivilegeValue', 'PPP', 'B', 'advapi32')
|
174
|
+
api_succeeded(
|
175
|
+
@lookup_privilege_value.call(
|
176
|
+
system_name ? system_name : API_NULL,
|
177
|
+
name,
|
178
|
+
luid))
|
179
|
+
end
|
180
|
+
|
181
|
+
# Overrides base WindowsSecurity#AdjustTokenPrivileges
|
182
|
+
def AdjustTokenPrivileges(token_handle, disable_all_privileges, new_state, buffer_length, previous_state, return_length)
|
183
|
+
must_be_dword(token_handle)
|
184
|
+
must_be_bool(disable_all_privileges)
|
185
|
+
must_be_buffer_or_nil(new_state)
|
186
|
+
must_be_byte_buffer_or_nil(previous_state, buffer_length)
|
187
|
+
must_be_byte_buffer_or_nil(return_length, return_length ? SIZEOF_DWORD : 0)
|
188
|
+
@adjust_token_privileges ||= ::Win32::API.new('AdjustTokenPrivileges', 'LLPLPP', 'B', 'advapi32')
|
189
|
+
api_succeeded(
|
190
|
+
@adjust_token_privileges.call(
|
191
|
+
token_handle,
|
192
|
+
disable_all_privileges ? API_TRUE : API_FALSE,
|
193
|
+
new_state ? new_state : API_NULL,
|
194
|
+
buffer_length,
|
195
|
+
previous_state ? previous_state : API_NULL,
|
196
|
+
return_length ? return_length : API_NULL))
|
197
|
+
end
|
198
|
+
end # WindowsSecurity
|
199
|
+
|
200
|
+
class WindowsSystemInformation
|
201
|
+
|
202
|
+
# Overrides base WindowsSystemInformation#GetVersionEx
|
203
|
+
def GetVersionEx(version_info_buffer)
|
204
|
+
must_be_size_prefixed_buffer(version_info_buffer)
|
205
|
+
@get_version_ex ||= ::Win32::API.new('GetVersionEx', 'P', 'L', 'kernel32')
|
206
|
+
api_succeeded(@get_version_ex.call(version_info_buffer))
|
207
|
+
end
|
208
|
+
|
209
|
+
# Overrides base WindowsSystemInformation#create_os_version_info
|
210
|
+
def create_os_version_info
|
211
|
+
[
|
212
|
+
148, # 0 - size of OSVERSIONINFO (IN)
|
213
|
+
0, # 4 - major version (OUT)
|
214
|
+
0, # 8 - minor version (OUT)
|
215
|
+
0, # 12 - build (OUT)
|
216
|
+
0, # 16 - platform (OUT)
|
217
|
+
0.chr * 128 # 20 - additional info (OUT)
|
218
|
+
].pack('LLLLLa128')
|
219
|
+
end
|
220
|
+
|
221
|
+
# Overrides base WindowsSystemInformation#unpack_os_version_info
|
222
|
+
def unpack_os_version_info(buffer)
|
223
|
+
buffer.unpack('LLLLLZ128') # 'Z' means ASCIIZ string
|
224
|
+
end
|
225
|
+
end # WindowsSystemInformation
|
226
|
+
|
227
|
+
private
|
228
|
+
|
229
|
+
# Overrides base Platform#initialize_species
|
230
|
+
def initialize_species
|
231
|
+
true # do nothing
|
232
|
+
end
|
233
|
+
|
234
|
+
end # Platform
|
235
|
+
|
236
|
+
end # RightScale
|