childprocess 0.8.0 → 2.0.0

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.
Files changed (51) hide show
  1. checksums.yaml +5 -5
  2. data/.document +6 -6
  3. data/.gitignore +28 -28
  4. data/.rspec +1 -1
  5. data/.travis.yml +42 -36
  6. data/CHANGELOG.md +67 -44
  7. data/Gemfile +18 -15
  8. data/LICENSE +20 -20
  9. data/README.md +216 -192
  10. data/Rakefile +61 -61
  11. data/appveyor.yml +42 -43
  12. data/childprocess.gemspec +32 -30
  13. data/ext/mkrf_conf.rb +24 -0
  14. data/lib/childprocess/abstract_io.rb +36 -36
  15. data/lib/childprocess/abstract_process.rb +192 -192
  16. data/lib/childprocess/errors.rb +37 -26
  17. data/lib/childprocess/jruby/io.rb +16 -16
  18. data/lib/childprocess/jruby/process.rb +184 -159
  19. data/lib/childprocess/jruby/pump.rb +53 -53
  20. data/lib/childprocess/jruby.rb +56 -56
  21. data/lib/childprocess/tools/generator.rb +145 -145
  22. data/lib/childprocess/unix/fork_exec_process.rb +78 -70
  23. data/lib/childprocess/unix/io.rb +21 -21
  24. data/lib/childprocess/unix/lib.rb +186 -186
  25. data/lib/childprocess/unix/platform/i386-linux.rb +12 -12
  26. data/lib/childprocess/unix/platform/i386-solaris.rb +11 -11
  27. data/lib/childprocess/unix/platform/x86_64-linux.rb +12 -12
  28. data/lib/childprocess/unix/platform/x86_64-macosx.rb +11 -11
  29. data/lib/childprocess/unix/posix_spawn_process.rb +134 -134
  30. data/lib/childprocess/unix/process.rb +90 -89
  31. data/lib/childprocess/unix.rb +9 -9
  32. data/lib/childprocess/version.rb +3 -3
  33. data/lib/childprocess/windows/handle.rb +91 -91
  34. data/lib/childprocess/windows/io.rb +25 -25
  35. data/lib/childprocess/windows/lib.rb +416 -416
  36. data/lib/childprocess/windows/process.rb +130 -130
  37. data/lib/childprocess/windows/process_builder.rb +178 -175
  38. data/lib/childprocess/windows/structs.rb +148 -148
  39. data/lib/childprocess/windows.rb +33 -33
  40. data/lib/childprocess.rb +210 -205
  41. data/spec/abstract_io_spec.rb +12 -12
  42. data/spec/childprocess_spec.rb +447 -391
  43. data/spec/get_env.ps1 +13 -0
  44. data/spec/io_spec.rb +228 -228
  45. data/spec/jruby_spec.rb +24 -24
  46. data/spec/pid_behavior.rb +12 -12
  47. data/spec/platform_detection_spec.rb +86 -86
  48. data/spec/spec_helper.rb +270 -261
  49. data/spec/unix_spec.rb +57 -57
  50. data/spec/windows_spec.rb +23 -23
  51. metadata +18 -33
@@ -1,416 +1,416 @@
1
- module ChildProcess
2
- module Windows
3
- FORMAT_MESSAGE_FROM_SYSTEM = 0x00001000
4
- FORMAT_MESSAGE_ARGUMENT_ARRAY = 0x00002000
5
-
6
- PROCESS_ALL_ACCESS = 0x1F0FFF
7
- PROCESS_QUERY_INFORMATION = 0x0400
8
- PROCESS_VM_READ = 0x0010
9
- PROCESS_STILL_ACTIVE = 259
10
-
11
- INFINITE = 0xFFFFFFFF
12
-
13
- WIN_SIGINT = 2
14
- WIN_SIGBREAK = 3
15
- WIN_SIGKILL = 9
16
-
17
- CTRL_C_EVENT = 0
18
- CTRL_BREAK_EVENT = 1
19
-
20
- CREATE_BREAKAWAY_FROM_JOB = 0x01000000
21
- DETACHED_PROCESS = 0x00000008
22
-
23
- STARTF_USESTDHANDLES = 0x00000100
24
- INVALID_HANDLE_VALUE = -1
25
- HANDLE_FLAG_INHERIT = 0x00000001
26
-
27
- DUPLICATE_SAME_ACCESS = 0x00000002
28
- CREATE_UNICODE_ENVIRONMENT = 0x00000400
29
-
30
- JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE = 0x2000
31
- JOB_OBJECT_LIMIT_BREAKAWAY_OK = 0x00000800
32
- JOB_OBJECT_EXTENDED_LIMIT_INFORMATION = 9
33
- JOB_OBJECT_BASIC_LIMIT_INFORMATION = 2
34
-
35
- module Lib
36
- enum :wait_status, [
37
- :wait_object_0, 0,
38
- :wait_timeout, 0x102,
39
- :wait_abandoned, 0x80,
40
- :wait_failed, 0xFFFFFFFF
41
- ]
42
-
43
- #
44
- # BOOL WINAPI CreateProcess(
45
- # __in_opt LPCTSTR lpApplicationName,
46
- # __inout_opt LPTSTR lpCommandLine,
47
- # __in_opt LPSECURITY_ATTRIBUTES lpProcessAttributes,
48
- # __in_opt LPSECURITY_ATTRIBUTES lpThreadAttributes,
49
- # __in BOOL bInheritHandles,
50
- # __in DWORD dwCreationFlags,
51
- # __in_opt LPVOID lpEnvironment,
52
- # __in_opt LPCTSTR lpCurrentDirectory,
53
- # __in LPSTARTUPINFO lpStartupInfo,
54
- # __out LPPROCESS_INFORMATION lpProcessInformation
55
- # );
56
- #
57
-
58
- attach_function :create_process, :CreateProcessA, [
59
- :pointer,
60
- :pointer,
61
- :pointer,
62
- :pointer,
63
- :bool,
64
- :ulong,
65
- :pointer,
66
- :pointer,
67
- :pointer,
68
- :pointer], :bool
69
-
70
- #
71
- # DWORD WINAPI FormatMessage(
72
- # __in DWORD dwFlags,
73
- # __in_opt LPCVOID lpSource,
74
- # __in DWORD dwMessageId,
75
- # __in DWORD dwLanguageId,
76
- # __out LPTSTR lpBuffer,
77
- # __in DWORD nSize,
78
- # __in_opt va_list *Arguments
79
- # );
80
- #
81
-
82
- attach_function :format_message, :FormatMessageA, [
83
- :ulong,
84
- :pointer,
85
- :ulong,
86
- :ulong,
87
- :pointer,
88
- :ulong,
89
- :pointer], :ulong
90
-
91
-
92
- attach_function :close_handle, :CloseHandle, [:pointer], :bool
93
-
94
- #
95
- # HANDLE WINAPI OpenProcess(
96
- # __in DWORD dwDesiredAccess,
97
- # __in BOOL bInheritHandle,
98
- # __in DWORD dwProcessId
99
- # );
100
- #
101
-
102
- attach_function :open_process, :OpenProcess, [:ulong, :bool, :ulong], :pointer
103
-
104
- #
105
- # HANDLE WINAPI CreateJobObject(
106
- # _In_opt_ LPSECURITY_ATTRIBUTES lpJobAttributes,
107
- # _In_opt_ LPCTSTR lpName
108
- # );
109
- #
110
-
111
- attach_function :create_job_object, :CreateJobObjectA, [:pointer, :pointer], :pointer
112
-
113
- #
114
- # BOOL WINAPI AssignProcessToJobObject(
115
- # _In_ HANDLE hJob,
116
- # _In_ HANDLE hProcess
117
- # );
118
-
119
- attach_function :assign_process_to_job_object, :AssignProcessToJobObject, [:pointer, :pointer], :bool
120
-
121
- #
122
- # BOOL WINAPI SetInformationJobObject(
123
- # _In_ HANDLE hJob,
124
- # _In_ JOBOBJECTINFOCLASS JobObjectInfoClass,
125
- # _In_ LPVOID lpJobObjectInfo,
126
- # _In_ DWORD cbJobObjectInfoLength
127
- # );
128
- #
129
-
130
- attach_function :set_information_job_object, :SetInformationJobObject, [:pointer, :int, :pointer, :ulong], :bool
131
-
132
- #
133
- #
134
- # DWORD WINAPI WaitForSingleObject(
135
- # __in HANDLE hHandle,
136
- # __in DWORD dwMilliseconds
137
- # );
138
- #
139
-
140
- attach_function :wait_for_single_object, :WaitForSingleObject, [:pointer, :ulong], :wait_status, :blocking => true
141
-
142
- #
143
- # BOOL WINAPI GetExitCodeProcess(
144
- # __in HANDLE hProcess,
145
- # __out LPDWORD lpExitCode
146
- # );
147
- #
148
-
149
- attach_function :get_exit_code, :GetExitCodeProcess, [:pointer, :pointer], :bool
150
-
151
- #
152
- # BOOL WINAPI GenerateConsoleCtrlEvent(
153
- # __in DWORD dwCtrlEvent,
154
- # __in DWORD dwProcessGroupId
155
- # );
156
- #
157
-
158
- attach_function :generate_console_ctrl_event, :GenerateConsoleCtrlEvent, [:ulong, :ulong], :bool
159
-
160
- #
161
- # BOOL WINAPI TerminateProcess(
162
- # __in HANDLE hProcess,
163
- # __in UINT uExitCode
164
- # );
165
- #
166
-
167
- attach_function :terminate_process, :TerminateProcess, [:pointer, :uint], :bool
168
-
169
- #
170
- # intptr_t _get_osfhandle(
171
- # int fd
172
- # );
173
- #
174
-
175
- attach_function :get_osfhandle, :_get_osfhandle, [:int], :intptr_t
176
-
177
- #
178
- # int _open_osfhandle (
179
- # intptr_t osfhandle,
180
- # int flags
181
- # );
182
- #
183
-
184
- attach_function :open_osfhandle, :_open_osfhandle, [:pointer, :int], :int
185
-
186
- # BOOL WINAPI SetHandleInformation(
187
- # __in HANDLE hObject,
188
- # __in DWORD dwMask,
189
- # __in DWORD dwFlags
190
- # );
191
-
192
- attach_function :set_handle_information, :SetHandleInformation, [:pointer, :ulong, :ulong], :bool
193
-
194
- # BOOL WINAPI GetHandleInformation(
195
- # __in HANDLE hObject,
196
- # __out LPDWORD lpdwFlags
197
- # );
198
-
199
- attach_function :get_handle_information, :GetHandleInformation, [:pointer, :pointer], :bool
200
-
201
- # BOOL WINAPI CreatePipe(
202
- # __out PHANDLE hReadPipe,
203
- # __out PHANDLE hWritePipe,
204
- # __in_opt LPSECURITY_ATTRIBUTES lpPipeAttributes,
205
- # __in DWORD nSize
206
- # );
207
-
208
- attach_function :create_pipe, :CreatePipe, [:pointer, :pointer, :pointer, :ulong], :bool
209
-
210
- #
211
- # HANDLE WINAPI GetCurrentProcess(void);
212
- #
213
-
214
- attach_function :current_process, :GetCurrentProcess, [], :pointer
215
-
216
- #
217
- # BOOL WINAPI DuplicateHandle(
218
- # __in HANDLE hSourceProcessHandle,
219
- # __in HANDLE hSourceHandle,
220
- # __in HANDLE hTargetProcessHandle,
221
- # __out LPHANDLE lpTargetHandle,
222
- # __in DWORD dwDesiredAccess,
223
- # __in BOOL bInheritHandle,
224
- # __in DWORD dwOptions
225
- # );
226
- #
227
-
228
- attach_function :_duplicate_handle, :DuplicateHandle, [
229
- :pointer,
230
- :pointer,
231
- :pointer,
232
- :pointer,
233
- :ulong,
234
- :bool,
235
- :ulong
236
- ], :bool
237
-
238
- class << self
239
- def kill(signal, *pids)
240
- case signal
241
- when 'SIGINT', 'INT', :SIGINT, :INT
242
- signal = WIN_SIGINT
243
- when 'SIGBRK', 'BRK', :SIGBREAK, :BRK
244
- signal = WIN_SIGBREAK
245
- when 'SIGKILL', 'KILL', :SIGKILL, :KILL
246
- signal = WIN_SIGKILL
247
- when 0..9
248
- # Do nothing
249
- else
250
- raise Error, "invalid signal #{signal.inspect}"
251
- end
252
-
253
- pids.map { |pid| pid if Lib.send_signal(signal, pid) }.compact
254
- end
255
-
256
- def waitpid(pid, flags = 0)
257
- wait_for_pid(pid, no_hang?(flags))
258
- end
259
-
260
- def waitpid2(pid, flags = 0)
261
- code = wait_for_pid(pid, no_hang?(flags))
262
-
263
- [pid, code] if code
264
- end
265
-
266
- def dont_inherit(file)
267
- unless file.respond_to?(:fileno)
268
- raise ArgumentError, "expected #{file.inspect} to respond to :fileno"
269
- end
270
-
271
- set_handle_inheritance(handle_for(file.fileno), false)
272
- end
273
-
274
- def last_error_message
275
- errnum = FFI.errno
276
-
277
- buf = FFI::MemoryPointer.new :char, 512
278
-
279
- size = format_message(
280
- FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ARGUMENT_ARRAY,
281
- nil, errnum, 0, buf, buf.size, nil
282
- )
283
-
284
- str = buf.read_string(size).strip
285
- if errnum == 0
286
- "Unknown error (Windows says #{str.inspect}, but it did not.)"
287
- else
288
- "#{str} (#{errnum})"
289
- end
290
- end
291
-
292
- def each_child_of(pid, &blk)
293
- raise NotImplementedError
294
-
295
- # http://stackoverflow.com/questions/1173342/terminate-a-process-tree-c-for-windows?rq=1
296
-
297
- # for each process entry
298
- # if pe.th32ParentProcessID == pid
299
- # Handle.open(pe.pe.th32ProcessId, &blk)
300
- # end
301
- #
302
- end
303
-
304
- def handle_for(fd_or_io)
305
- if fd_or_io.kind_of?(IO) || fd_or_io.respond_to?(:fileno)
306
- if ChildProcess.jruby?
307
- handle = ChildProcess::JRuby.windows_handle_for(fd_or_io)
308
- else
309
- handle = get_osfhandle(fd_or_io.fileno)
310
- end
311
- elsif fd_or_io.kind_of?(Integer)
312
- handle = get_osfhandle(fd_or_io)
313
- elsif fd_or_io.respond_to?(:to_io)
314
- io = fd_or_io.to_io
315
-
316
- unless io.kind_of?(IO)
317
- raise TypeError, "expected #to_io to return an instance of IO"
318
- end
319
-
320
- handle = get_osfhandle(io.fileno)
321
- else
322
- raise TypeError, "invalid type: #{fd_or_io.inspect}"
323
- end
324
-
325
- if handle == INVALID_HANDLE_VALUE
326
- raise Error, last_error_message
327
- end
328
-
329
- FFI::Pointer.new handle
330
- end
331
-
332
- def io_for(handle, flags = File::RDONLY)
333
- fd = open_osfhandle(handle, flags)
334
- if fd == -1
335
- raise Error, last_error_message
336
- end
337
-
338
- FFI::IO.for_fd fd, flags
339
- end
340
-
341
- def duplicate_handle(handle)
342
- dup = FFI::MemoryPointer.new(:pointer)
343
- proc = current_process
344
-
345
- ok = Lib._duplicate_handle(
346
- proc,
347
- handle,
348
- proc,
349
- dup,
350
- 0,
351
- false,
352
- DUPLICATE_SAME_ACCESS
353
- )
354
-
355
- check_error ok
356
-
357
- dup.read_pointer
358
- ensure
359
- close_handle proc
360
- end
361
-
362
- def set_handle_inheritance(handle, bool)
363
- status = set_handle_information(
364
- handle,
365
- HANDLE_FLAG_INHERIT,
366
- bool ? HANDLE_FLAG_INHERIT : 0
367
- )
368
-
369
- check_error status
370
- end
371
-
372
- def get_handle_inheritance(handle)
373
- flags = FFI::MemoryPointer.new(:uint)
374
-
375
- status = get_handle_information(
376
- handle,
377
- flags
378
- )
379
-
380
- check_error status
381
-
382
- flags.read_uint
383
- end
384
-
385
- def check_error(bool)
386
- bool or raise Error, last_error_message
387
- end
388
-
389
- def alive?(pid)
390
- handle = Lib.open_process(PROCESS_ALL_ACCESS, false, pid)
391
- if handle.null?
392
- false
393
- else
394
- ptr = FFI::MemoryPointer.new :ulong
395
- Lib.check_error Lib.get_exit_code(handle, ptr)
396
- ptr.read_ulong == PROCESS_STILL_ACTIVE
397
- end
398
- end
399
-
400
- def no_hang?(flags)
401
- (flags & Process::WNOHANG) == Process::WNOHANG
402
- end
403
-
404
- def wait_for_pid(pid, no_hang)
405
- code = Handle.open(pid) { |handle|
406
- handle.wait unless no_hang
407
- handle.exit_code
408
- }
409
-
410
- code if code != PROCESS_STILL_ACTIVE
411
- end
412
- end
413
-
414
- end # Lib
415
- end # Windows
416
- end # ChildProcess
1
+ module ChildProcess
2
+ module Windows
3
+ FORMAT_MESSAGE_FROM_SYSTEM = 0x00001000
4
+ FORMAT_MESSAGE_ARGUMENT_ARRAY = 0x00002000
5
+
6
+ PROCESS_ALL_ACCESS = 0x1F0FFF
7
+ PROCESS_QUERY_INFORMATION = 0x0400
8
+ PROCESS_VM_READ = 0x0010
9
+ PROCESS_STILL_ACTIVE = 259
10
+
11
+ INFINITE = 0xFFFFFFFF
12
+
13
+ WIN_SIGINT = 2
14
+ WIN_SIGBREAK = 3
15
+ WIN_SIGKILL = 9
16
+
17
+ CTRL_C_EVENT = 0
18
+ CTRL_BREAK_EVENT = 1
19
+
20
+ CREATE_BREAKAWAY_FROM_JOB = 0x01000000
21
+ DETACHED_PROCESS = 0x00000008
22
+
23
+ STARTF_USESTDHANDLES = 0x00000100
24
+ INVALID_HANDLE_VALUE = -1
25
+ HANDLE_FLAG_INHERIT = 0x00000001
26
+
27
+ DUPLICATE_SAME_ACCESS = 0x00000002
28
+ CREATE_UNICODE_ENVIRONMENT = 0x00000400
29
+
30
+ JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE = 0x2000
31
+ JOB_OBJECT_LIMIT_BREAKAWAY_OK = 0x00000800
32
+ JOB_OBJECT_EXTENDED_LIMIT_INFORMATION = 9
33
+ JOB_OBJECT_BASIC_LIMIT_INFORMATION = 2
34
+
35
+ module Lib
36
+ enum :wait_status, [
37
+ :wait_object_0, 0,
38
+ :wait_timeout, 0x102,
39
+ :wait_abandoned, 0x80,
40
+ :wait_failed, 0xFFFFFFFF
41
+ ]
42
+
43
+ #
44
+ # BOOL WINAPI CreateProcess(
45
+ # __in_opt LPCTSTR lpApplicationName,
46
+ # __inout_opt LPTSTR lpCommandLine,
47
+ # __in_opt LPSECURITY_ATTRIBUTES lpProcessAttributes,
48
+ # __in_opt LPSECURITY_ATTRIBUTES lpThreadAttributes,
49
+ # __in BOOL bInheritHandles,
50
+ # __in DWORD dwCreationFlags,
51
+ # __in_opt LPVOID lpEnvironment,
52
+ # __in_opt LPCTSTR lpCurrentDirectory,
53
+ # __in LPSTARTUPINFO lpStartupInfo,
54
+ # __out LPPROCESS_INFORMATION lpProcessInformation
55
+ # );
56
+ #
57
+
58
+ attach_function :create_process, :CreateProcessW, [
59
+ :pointer,
60
+ :buffer_inout,
61
+ :pointer,
62
+ :pointer,
63
+ :bool,
64
+ :ulong,
65
+ :pointer,
66
+ :pointer,
67
+ :pointer,
68
+ :pointer], :bool
69
+
70
+ #
71
+ # DWORD WINAPI FormatMessage(
72
+ # __in DWORD dwFlags,
73
+ # __in_opt LPCVOID lpSource,
74
+ # __in DWORD dwMessageId,
75
+ # __in DWORD dwLanguageId,
76
+ # __out LPTSTR lpBuffer,
77
+ # __in DWORD nSize,
78
+ # __in_opt va_list *Arguments
79
+ # );
80
+ #
81
+
82
+ attach_function :format_message, :FormatMessageA, [
83
+ :ulong,
84
+ :pointer,
85
+ :ulong,
86
+ :ulong,
87
+ :pointer,
88
+ :ulong,
89
+ :pointer], :ulong
90
+
91
+
92
+ attach_function :close_handle, :CloseHandle, [:pointer], :bool
93
+
94
+ #
95
+ # HANDLE WINAPI OpenProcess(
96
+ # __in DWORD dwDesiredAccess,
97
+ # __in BOOL bInheritHandle,
98
+ # __in DWORD dwProcessId
99
+ # );
100
+ #
101
+
102
+ attach_function :open_process, :OpenProcess, [:ulong, :bool, :ulong], :pointer
103
+
104
+ #
105
+ # HANDLE WINAPI CreateJobObject(
106
+ # _In_opt_ LPSECURITY_ATTRIBUTES lpJobAttributes,
107
+ # _In_opt_ LPCTSTR lpName
108
+ # );
109
+ #
110
+
111
+ attach_function :create_job_object, :CreateJobObjectA, [:pointer, :pointer], :pointer
112
+
113
+ #
114
+ # BOOL WINAPI AssignProcessToJobObject(
115
+ # _In_ HANDLE hJob,
116
+ # _In_ HANDLE hProcess
117
+ # );
118
+
119
+ attach_function :assign_process_to_job_object, :AssignProcessToJobObject, [:pointer, :pointer], :bool
120
+
121
+ #
122
+ # BOOL WINAPI SetInformationJobObject(
123
+ # _In_ HANDLE hJob,
124
+ # _In_ JOBOBJECTINFOCLASS JobObjectInfoClass,
125
+ # _In_ LPVOID lpJobObjectInfo,
126
+ # _In_ DWORD cbJobObjectInfoLength
127
+ # );
128
+ #
129
+
130
+ attach_function :set_information_job_object, :SetInformationJobObject, [:pointer, :int, :pointer, :ulong], :bool
131
+
132
+ #
133
+ #
134
+ # DWORD WINAPI WaitForSingleObject(
135
+ # __in HANDLE hHandle,
136
+ # __in DWORD dwMilliseconds
137
+ # );
138
+ #
139
+
140
+ attach_function :wait_for_single_object, :WaitForSingleObject, [:pointer, :ulong], :wait_status, :blocking => true
141
+
142
+ #
143
+ # BOOL WINAPI GetExitCodeProcess(
144
+ # __in HANDLE hProcess,
145
+ # __out LPDWORD lpExitCode
146
+ # );
147
+ #
148
+
149
+ attach_function :get_exit_code, :GetExitCodeProcess, [:pointer, :pointer], :bool
150
+
151
+ #
152
+ # BOOL WINAPI GenerateConsoleCtrlEvent(
153
+ # __in DWORD dwCtrlEvent,
154
+ # __in DWORD dwProcessGroupId
155
+ # );
156
+ #
157
+
158
+ attach_function :generate_console_ctrl_event, :GenerateConsoleCtrlEvent, [:ulong, :ulong], :bool
159
+
160
+ #
161
+ # BOOL WINAPI TerminateProcess(
162
+ # __in HANDLE hProcess,
163
+ # __in UINT uExitCode
164
+ # );
165
+ #
166
+
167
+ attach_function :terminate_process, :TerminateProcess, [:pointer, :uint], :bool
168
+
169
+ #
170
+ # intptr_t _get_osfhandle(
171
+ # int fd
172
+ # );
173
+ #
174
+
175
+ attach_function :get_osfhandle, :_get_osfhandle, [:int], :intptr_t
176
+
177
+ #
178
+ # int _open_osfhandle (
179
+ # intptr_t osfhandle,
180
+ # int flags
181
+ # );
182
+ #
183
+
184
+ attach_function :open_osfhandle, :_open_osfhandle, [:pointer, :int], :int
185
+
186
+ # BOOL WINAPI SetHandleInformation(
187
+ # __in HANDLE hObject,
188
+ # __in DWORD dwMask,
189
+ # __in DWORD dwFlags
190
+ # );
191
+
192
+ attach_function :set_handle_information, :SetHandleInformation, [:pointer, :ulong, :ulong], :bool
193
+
194
+ # BOOL WINAPI GetHandleInformation(
195
+ # __in HANDLE hObject,
196
+ # __out LPDWORD lpdwFlags
197
+ # );
198
+
199
+ attach_function :get_handle_information, :GetHandleInformation, [:pointer, :pointer], :bool
200
+
201
+ # BOOL WINAPI CreatePipe(
202
+ # __out PHANDLE hReadPipe,
203
+ # __out PHANDLE hWritePipe,
204
+ # __in_opt LPSECURITY_ATTRIBUTES lpPipeAttributes,
205
+ # __in DWORD nSize
206
+ # );
207
+
208
+ attach_function :create_pipe, :CreatePipe, [:pointer, :pointer, :pointer, :ulong], :bool
209
+
210
+ #
211
+ # HANDLE WINAPI GetCurrentProcess(void);
212
+ #
213
+
214
+ attach_function :current_process, :GetCurrentProcess, [], :pointer
215
+
216
+ #
217
+ # BOOL WINAPI DuplicateHandle(
218
+ # __in HANDLE hSourceProcessHandle,
219
+ # __in HANDLE hSourceHandle,
220
+ # __in HANDLE hTargetProcessHandle,
221
+ # __out LPHANDLE lpTargetHandle,
222
+ # __in DWORD dwDesiredAccess,
223
+ # __in BOOL bInheritHandle,
224
+ # __in DWORD dwOptions
225
+ # );
226
+ #
227
+
228
+ attach_function :_duplicate_handle, :DuplicateHandle, [
229
+ :pointer,
230
+ :pointer,
231
+ :pointer,
232
+ :pointer,
233
+ :ulong,
234
+ :bool,
235
+ :ulong
236
+ ], :bool
237
+
238
+ class << self
239
+ def kill(signal, *pids)
240
+ case signal
241
+ when 'SIGINT', 'INT', :SIGINT, :INT
242
+ signal = WIN_SIGINT
243
+ when 'SIGBRK', 'BRK', :SIGBREAK, :BRK
244
+ signal = WIN_SIGBREAK
245
+ when 'SIGKILL', 'KILL', :SIGKILL, :KILL
246
+ signal = WIN_SIGKILL
247
+ when 0..9
248
+ # Do nothing
249
+ else
250
+ raise Error, "invalid signal #{signal.inspect}"
251
+ end
252
+
253
+ pids.map { |pid| pid if Lib.send_signal(signal, pid) }.compact
254
+ end
255
+
256
+ def waitpid(pid, flags = 0)
257
+ wait_for_pid(pid, no_hang?(flags))
258
+ end
259
+
260
+ def waitpid2(pid, flags = 0)
261
+ code = wait_for_pid(pid, no_hang?(flags))
262
+
263
+ [pid, code] if code
264
+ end
265
+
266
+ def dont_inherit(file)
267
+ unless file.respond_to?(:fileno)
268
+ raise ArgumentError, "expected #{file.inspect} to respond to :fileno"
269
+ end
270
+
271
+ set_handle_inheritance(handle_for(file.fileno), false)
272
+ end
273
+
274
+ def last_error_message
275
+ errnum = FFI.errno
276
+
277
+ buf = FFI::MemoryPointer.new :char, 512
278
+
279
+ size = format_message(
280
+ FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ARGUMENT_ARRAY,
281
+ nil, errnum, 0, buf, buf.size, nil
282
+ )
283
+
284
+ str = buf.read_string(size).strip
285
+ if errnum == 0
286
+ "Unknown error (Windows says #{str.inspect}, but it did not.)"
287
+ else
288
+ "#{str} (#{errnum})"
289
+ end
290
+ end
291
+
292
+ def each_child_of(pid, &blk)
293
+ raise NotImplementedError
294
+
295
+ # http://stackoverflow.com/questions/1173342/terminate-a-process-tree-c-for-windows?rq=1
296
+
297
+ # for each process entry
298
+ # if pe.th32ParentProcessID == pid
299
+ # Handle.open(pe.pe.th32ProcessId, &blk)
300
+ # end
301
+ #
302
+ end
303
+
304
+ def handle_for(fd_or_io)
305
+ if fd_or_io.kind_of?(IO) || fd_or_io.respond_to?(:fileno)
306
+ if ChildProcess.jruby?
307
+ handle = ChildProcess::JRuby.windows_handle_for(fd_or_io)
308
+ else
309
+ handle = get_osfhandle(fd_or_io.fileno)
310
+ end
311
+ elsif fd_or_io.kind_of?(Integer)
312
+ handle = get_osfhandle(fd_or_io)
313
+ elsif fd_or_io.respond_to?(:to_io)
314
+ io = fd_or_io.to_io
315
+
316
+ unless io.kind_of?(IO)
317
+ raise TypeError, "expected #to_io to return an instance of IO"
318
+ end
319
+
320
+ handle = get_osfhandle(io.fileno)
321
+ else
322
+ raise TypeError, "invalid type: #{fd_or_io.inspect}"
323
+ end
324
+
325
+ if handle == INVALID_HANDLE_VALUE
326
+ raise Error, last_error_message
327
+ end
328
+
329
+ FFI::Pointer.new handle
330
+ end
331
+
332
+ def io_for(handle, flags = File::RDONLY)
333
+ fd = open_osfhandle(handle, flags)
334
+ if fd == -1
335
+ raise Error, last_error_message
336
+ end
337
+
338
+ FFI::IO.for_fd fd, flags
339
+ end
340
+
341
+ def duplicate_handle(handle)
342
+ dup = FFI::MemoryPointer.new(:pointer)
343
+ proc = current_process
344
+
345
+ ok = Lib._duplicate_handle(
346
+ proc,
347
+ handle,
348
+ proc,
349
+ dup,
350
+ 0,
351
+ false,
352
+ DUPLICATE_SAME_ACCESS
353
+ )
354
+
355
+ check_error ok
356
+
357
+ dup.read_pointer
358
+ ensure
359
+ close_handle proc
360
+ end
361
+
362
+ def set_handle_inheritance(handle, bool)
363
+ status = set_handle_information(
364
+ handle,
365
+ HANDLE_FLAG_INHERIT,
366
+ bool ? HANDLE_FLAG_INHERIT : 0
367
+ )
368
+
369
+ check_error status
370
+ end
371
+
372
+ def get_handle_inheritance(handle)
373
+ flags = FFI::MemoryPointer.new(:uint)
374
+
375
+ status = get_handle_information(
376
+ handle,
377
+ flags
378
+ )
379
+
380
+ check_error status
381
+
382
+ flags.read_uint
383
+ end
384
+
385
+ def check_error(bool)
386
+ bool or raise Error, last_error_message
387
+ end
388
+
389
+ def alive?(pid)
390
+ handle = Lib.open_process(PROCESS_ALL_ACCESS, false, pid)
391
+ if handle.null?
392
+ false
393
+ else
394
+ ptr = FFI::MemoryPointer.new :ulong
395
+ Lib.check_error Lib.get_exit_code(handle, ptr)
396
+ ptr.read_ulong == PROCESS_STILL_ACTIVE
397
+ end
398
+ end
399
+
400
+ def no_hang?(flags)
401
+ (flags & Process::WNOHANG) == Process::WNOHANG
402
+ end
403
+
404
+ def wait_for_pid(pid, no_hang)
405
+ code = Handle.open(pid) { |handle|
406
+ handle.wait unless no_hang
407
+ handle.exit_code
408
+ }
409
+
410
+ code if code != PROCESS_STILL_ACTIVE
411
+ end
412
+ end
413
+
414
+ end # Lib
415
+ end # Windows
416
+ end # ChildProcess