right_popen 1.0.7-x86-mswin32-60 → 1.0.9-x86-mswin32-60
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 -20
- data/README.rdoc +124 -124
- data/lib/right_popen.rb +66 -65
- data/lib/win32/right_popen.rb +461 -442
- data/lib/win32/right_popen.so +0 -0
- data/right_popen.gemspec +61 -64
- data/spec/increment.rb +2 -0
- data/spec/produce_status.rb +1 -1
- data/spec/right_popen_spec.rb +221 -210
- data/spec/spec_helper.rb +4 -4
- metadata +4 -3
data/LICENSE
CHANGED
@@ -1,20 +1,20 @@
|
|
1
|
-
Copyright (c) 2010 RightScale, Inc.
|
2
|
-
|
3
|
-
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
-
a copy of this software and associated documentation files (the
|
5
|
-
'Software'), to deal in the Software without restriction, including
|
6
|
-
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
-
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
-
permit persons to whom the Software is furnished to do so, subject to
|
9
|
-
the following conditions:
|
10
|
-
|
11
|
-
The above copyright notice and this permission notice shall be
|
12
|
-
included in all copies or substantial portions of the Software.
|
13
|
-
|
14
|
-
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
|
15
|
-
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
-
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
17
|
-
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
18
|
-
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
19
|
-
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
20
|
-
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
1
|
+
Copyright (c) 2010 RightScale, Inc.
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
'Software'), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
17
|
+
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
18
|
+
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
19
|
+
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
20
|
+
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.rdoc
CHANGED
@@ -1,124 +1,124 @@
|
|
1
|
-
= RightPopen
|
2
|
-
|
3
|
-
== DESCRIPTION
|
4
|
-
|
5
|
-
=== Synopsis
|
6
|
-
|
7
|
-
RightPopen allows running external processes aynchronously while still
|
8
|
-
capturing their standard and error outputs. It relies on EventMachine for most
|
9
|
-
of its internal mechanisms. The Linux implementation is valid for any Linux
|
10
|
-
platform but there is also a native implementation for Windows platforms.
|
11
|
-
|
12
|
-
Refer to the wiki (https://github.com/rightscale/right_popen/wikis) for up-to-date
|
13
|
-
documentation.
|
14
|
-
|
15
|
-
Also use the built-in issues tracker (https://github.com/rightscale/right_popen/issues)
|
16
|
-
to report issues.
|
17
|
-
|
18
|
-
|
19
|
-
== USAGE
|
20
|
-
|
21
|
-
=== Simple Example
|
22
|
-
|
23
|
-
require 'rubygems'
|
24
|
-
require 'right_popen'
|
25
|
-
|
26
|
-
@stdout_text = ""
|
27
|
-
@stderr_text = ""
|
28
|
-
@exit_status = nil
|
29
|
-
|
30
|
-
def on_read_stdout(data)
|
31
|
-
@stdout_text << data
|
32
|
-
end
|
33
|
-
|
34
|
-
def on_read_stderr(data)
|
35
|
-
@stderr_text << data
|
36
|
-
end
|
37
|
-
|
38
|
-
def on_exit(status)
|
39
|
-
@exit_status = status
|
40
|
-
end
|
41
|
-
|
42
|
-
EM.run do
|
43
|
-
EM.next_tick do
|
44
|
-
command = "ruby -e \"puts 'some stdout text'; $stderr.puts 'some stderr text'\; exit 99\""
|
45
|
-
RightScale.popen3(:command => command,
|
46
|
-
:target => self,
|
47
|
-
:environment => nil,
|
48
|
-
:stdout_handler => :on_read_stdout,
|
49
|
-
:stderr_handler => :on_read_stderr,
|
50
|
-
:exit_handler => :on_exit)
|
51
|
-
end
|
52
|
-
timer = EM::PeriodicTimer.new(0.1) do
|
53
|
-
if @exit_status
|
54
|
-
timer.cancel
|
55
|
-
EM.stop
|
56
|
-
end
|
57
|
-
end
|
58
|
-
end
|
59
|
-
|
60
|
-
puts "@stdout_text = #{@stdout_text}"
|
61
|
-
puts "@stderr_text = #{@stderr_text}"
|
62
|
-
puts "@exit_status.exitstatus = #{@exit_status.exitstatus}"
|
63
|
-
|
64
|
-
|
65
|
-
== INSTALLATION
|
66
|
-
|
67
|
-
RightPopen can be installed by entering the following at the command prompt:
|
68
|
-
|
69
|
-
gem install right_popen
|
70
|
-
|
71
|
-
|
72
|
-
== BUILDING
|
73
|
-
|
74
|
-
Install the following RubyGems required for building:
|
75
|
-
* rake
|
76
|
-
|
77
|
-
The Windows implementation relies on a native C module which must currently be
|
78
|
-
built using the MSVC 6.0 compiler due to a dependency on the standard libraries
|
79
|
-
FILE struct provided by the "msvcrt.dll".
|
80
|
-
|
81
|
-
The gem can be built on Linux or Windows platforms and will produce separate gem
|
82
|
-
files depending on current platform. Run the following command from the
|
83
|
-
directory containing the "Rakefile":
|
84
|
-
|
85
|
-
rake build_binary_gem
|
86
|
-
|
87
|
-
|
88
|
-
== TESTING
|
89
|
-
|
90
|
-
Install the following RubyGems required for testing:
|
91
|
-
* rspec
|
92
|
-
|
93
|
-
The build can be tested using the RSpec gem. Create a link to the installed
|
94
|
-
"spec" in your Ruby/bin directory (or ensure the bin directory is on the PATH
|
95
|
-
under Windows) and run the following command from the gem directory to execute
|
96
|
-
the RightPopen tests:
|
97
|
-
|
98
|
-
rake spec
|
99
|
-
|
100
|
-
|
101
|
-
== LICENSE
|
102
|
-
|
103
|
-
<b>RightPopen</b>
|
104
|
-
|
105
|
-
Copyright:: Copyright (c) 2010 RightScale, Inc.
|
106
|
-
|
107
|
-
Permission is hereby granted, free of charge, to any person obtaining
|
108
|
-
a copy of this software and associated documentation files (the
|
109
|
-
'Software'), to deal in the Software without restriction, including
|
110
|
-
without limitation the rights to use, copy, modify, merge, publish,
|
111
|
-
distribute, sublicense, and/or sell copies of the Software, and to
|
112
|
-
permit persons to whom the Software is furnished to do so, subject to
|
113
|
-
the following conditions:
|
114
|
-
|
115
|
-
The above copyright notice and this permission notice shall be
|
116
|
-
included in all copies or substantial portions of the Software.
|
117
|
-
|
118
|
-
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
|
119
|
-
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
120
|
-
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
121
|
-
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
122
|
-
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
123
|
-
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
124
|
-
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
1
|
+
= RightPopen
|
2
|
+
|
3
|
+
== DESCRIPTION
|
4
|
+
|
5
|
+
=== Synopsis
|
6
|
+
|
7
|
+
RightPopen allows running external processes aynchronously while still
|
8
|
+
capturing their standard and error outputs. It relies on EventMachine for most
|
9
|
+
of its internal mechanisms. The Linux implementation is valid for any Linux
|
10
|
+
platform but there is also a native implementation for Windows platforms.
|
11
|
+
|
12
|
+
Refer to the wiki (https://github.com/rightscale/right_popen/wikis) for up-to-date
|
13
|
+
documentation.
|
14
|
+
|
15
|
+
Also use the built-in issues tracker (https://github.com/rightscale/right_popen/issues)
|
16
|
+
to report issues.
|
17
|
+
|
18
|
+
|
19
|
+
== USAGE
|
20
|
+
|
21
|
+
=== Simple Example
|
22
|
+
|
23
|
+
require 'rubygems'
|
24
|
+
require 'right_popen'
|
25
|
+
|
26
|
+
@stdout_text = ""
|
27
|
+
@stderr_text = ""
|
28
|
+
@exit_status = nil
|
29
|
+
|
30
|
+
def on_read_stdout(data)
|
31
|
+
@stdout_text << data
|
32
|
+
end
|
33
|
+
|
34
|
+
def on_read_stderr(data)
|
35
|
+
@stderr_text << data
|
36
|
+
end
|
37
|
+
|
38
|
+
def on_exit(status)
|
39
|
+
@exit_status = status
|
40
|
+
end
|
41
|
+
|
42
|
+
EM.run do
|
43
|
+
EM.next_tick do
|
44
|
+
command = "ruby -e \"puts 'some stdout text'; $stderr.puts 'some stderr text'\; exit 99\""
|
45
|
+
RightScale.popen3(:command => command,
|
46
|
+
:target => self,
|
47
|
+
:environment => nil,
|
48
|
+
:stdout_handler => :on_read_stdout,
|
49
|
+
:stderr_handler => :on_read_stderr,
|
50
|
+
:exit_handler => :on_exit)
|
51
|
+
end
|
52
|
+
timer = EM::PeriodicTimer.new(0.1) do
|
53
|
+
if @exit_status
|
54
|
+
timer.cancel
|
55
|
+
EM.stop
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
puts "@stdout_text = #{@stdout_text}"
|
61
|
+
puts "@stderr_text = #{@stderr_text}"
|
62
|
+
puts "@exit_status.exitstatus = #{@exit_status.exitstatus}"
|
63
|
+
|
64
|
+
|
65
|
+
== INSTALLATION
|
66
|
+
|
67
|
+
RightPopen can be installed by entering the following at the command prompt:
|
68
|
+
|
69
|
+
gem install right_popen
|
70
|
+
|
71
|
+
|
72
|
+
== BUILDING
|
73
|
+
|
74
|
+
Install the following RubyGems required for building:
|
75
|
+
* rake
|
76
|
+
|
77
|
+
The Windows implementation relies on a native C module which must currently be
|
78
|
+
built using the MSVC 6.0 compiler due to a dependency on the standard libraries
|
79
|
+
FILE struct provided by the "msvcrt.dll".
|
80
|
+
|
81
|
+
The gem can be built on Linux or Windows platforms and will produce separate gem
|
82
|
+
files depending on current platform. Run the following command from the
|
83
|
+
directory containing the "Rakefile":
|
84
|
+
|
85
|
+
rake build_binary_gem
|
86
|
+
|
87
|
+
|
88
|
+
== TESTING
|
89
|
+
|
90
|
+
Install the following RubyGems required for testing:
|
91
|
+
* rspec
|
92
|
+
|
93
|
+
The build can be tested using the RSpec gem. Create a link to the installed
|
94
|
+
"spec" in your Ruby/bin directory (or ensure the bin directory is on the PATH
|
95
|
+
under Windows) and run the following command from the gem directory to execute
|
96
|
+
the RightPopen tests:
|
97
|
+
|
98
|
+
rake spec
|
99
|
+
|
100
|
+
|
101
|
+
== LICENSE
|
102
|
+
|
103
|
+
<b>RightPopen</b>
|
104
|
+
|
105
|
+
Copyright:: Copyright (c) 2010 RightScale, Inc.
|
106
|
+
|
107
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
108
|
+
a copy of this software and associated documentation files (the
|
109
|
+
'Software'), to deal in the Software without restriction, including
|
110
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
111
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
112
|
+
permit persons to whom the Software is furnished to do so, subject to
|
113
|
+
the following conditions:
|
114
|
+
|
115
|
+
The above copyright notice and this permission notice shall be
|
116
|
+
included in all copies or substantial portions of the Software.
|
117
|
+
|
118
|
+
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
|
119
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
120
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
121
|
+
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
122
|
+
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
123
|
+
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
124
|
+
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/lib/right_popen.rb
CHANGED
@@ -1,65 +1,66 @@
|
|
1
|
-
#--
|
2
|
-
# Copyright (c) 2009 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
|
-
|
24
|
-
# RightScale.popen3 allows running external processes aynchronously
|
25
|
-
# while still capturing their standard and error outputs.
|
26
|
-
# It relies on EventMachine for most of its internal mechanisms.
|
27
|
-
|
28
|
-
if RUBY_PLATFORM =~ /mswin/
|
29
|
-
require File.expand_path(File.join(File.dirname(__FILE__), 'win32', 'right_popen'))
|
30
|
-
else
|
31
|
-
require File.expand_path(File.join(File.dirname(__FILE__), 'linux', 'right_popen'))
|
32
|
-
end
|
33
|
-
|
34
|
-
module RightScale
|
35
|
-
|
36
|
-
# Spawn process to run given command asynchronously, hooking all three
|
37
|
-
# standard streams of the child process.
|
38
|
-
#
|
39
|
-
# Streams the command's stdout and stderr to the given handlers. Time-
|
40
|
-
# ordering of bytes sent to stdout and stderr is not preserved.
|
41
|
-
#
|
42
|
-
# Calls given exit handler upon command process termination, passing in the
|
43
|
-
# resulting Process::Status.
|
44
|
-
#
|
45
|
-
# All handlers must be methods exposed by the given target.
|
46
|
-
#
|
47
|
-
# === Parameters
|
48
|
-
# options[:command](String):: Command to execute, including any arguments
|
49
|
-
# options[:environment](Hash):: Hash of environment variables values keyed by name
|
50
|
-
# options[:
|
51
|
-
# options[:
|
52
|
-
# options[:
|
53
|
-
# options[:
|
54
|
-
#
|
55
|
-
#
|
56
|
-
#
|
57
|
-
|
58
|
-
|
59
|
-
raise "
|
60
|
-
raise "Missing
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
1
|
+
#--
|
2
|
+
# Copyright (c) 2009 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
|
+
|
24
|
+
# RightScale.popen3 allows running external processes aynchronously
|
25
|
+
# while still capturing their standard and error outputs.
|
26
|
+
# It relies on EventMachine for most of its internal mechanisms.
|
27
|
+
|
28
|
+
if RUBY_PLATFORM =~ /mswin/
|
29
|
+
require File.expand_path(File.join(File.dirname(__FILE__), 'win32', 'right_popen'))
|
30
|
+
else
|
31
|
+
require File.expand_path(File.join(File.dirname(__FILE__), 'linux', 'right_popen'))
|
32
|
+
end
|
33
|
+
|
34
|
+
module RightScale
|
35
|
+
|
36
|
+
# Spawn process to run given command asynchronously, hooking all three
|
37
|
+
# standard streams of the child process.
|
38
|
+
#
|
39
|
+
# Streams the command's stdout and stderr to the given handlers. Time-
|
40
|
+
# ordering of bytes sent to stdout and stderr is not preserved.
|
41
|
+
#
|
42
|
+
# Calls given exit handler upon command process termination, passing in the
|
43
|
+
# resulting Process::Status.
|
44
|
+
#
|
45
|
+
# All handlers must be methods exposed by the given target.
|
46
|
+
#
|
47
|
+
# === Parameters
|
48
|
+
# options[:command](String):: Command to execute, including any arguments
|
49
|
+
# options[:environment](Hash):: Hash of environment variables values keyed by name
|
50
|
+
# options[:input](String):: Input string that will get streamed into child's process stdin
|
51
|
+
# options[:target](Object):: object defining handler methods to be called, optional (no handlers can be defined if not specified)
|
52
|
+
# options[:stdout_handler](String):: Stdout handler method name, optional
|
53
|
+
# options[:stderr_handler](String):: Stderr handler method name, optional
|
54
|
+
# options[:exit_handler](String):: Exit handler method name, optional
|
55
|
+
#
|
56
|
+
# === Returns
|
57
|
+
# true:: Always returns true
|
58
|
+
def self.popen3(options)
|
59
|
+
raise "EventMachine reactor must be started" unless EM.reactor_running?
|
60
|
+
raise "Missing command" unless options[:command]
|
61
|
+
raise "Missing target" unless options[:target] || !options[:stdout_handler] && !options[:stderr_handler] && !options[:exit_handler]
|
62
|
+
RightScale.popen3_imp(options)
|
63
|
+
true
|
64
|
+
end
|
65
|
+
|
66
|
+
end
|
data/lib/win32/right_popen.rb
CHANGED
@@ -1,442 +1,461 @@
|
|
1
|
-
#--
|
2
|
-
# Copyright (c) 2009 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
|
-
|
24
|
-
require 'rubygems'
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
#
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
# ===
|
72
|
-
#
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
#
|
104
|
-
#
|
105
|
-
def
|
106
|
-
@target
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
end
|
122
|
-
|
123
|
-
# Callback from EM to
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
#
|
158
|
-
#
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
#
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
|
267
|
-
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
-
|
273
|
-
|
274
|
-
|
275
|
-
|
276
|
-
|
277
|
-
|
278
|
-
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
|
283
|
-
#
|
284
|
-
#
|
285
|
-
|
286
|
-
|
287
|
-
|
288
|
-
|
289
|
-
|
290
|
-
|
291
|
-
|
292
|
-
|
293
|
-
|
294
|
-
|
295
|
-
|
296
|
-
|
297
|
-
|
298
|
-
|
299
|
-
|
300
|
-
|
301
|
-
|
302
|
-
|
303
|
-
|
304
|
-
|
305
|
-
|
306
|
-
|
307
|
-
|
308
|
-
|
309
|
-
|
310
|
-
|
311
|
-
|
312
|
-
|
313
|
-
|
314
|
-
|
315
|
-
|
316
|
-
|
317
|
-
|
318
|
-
|
319
|
-
|
320
|
-
|
321
|
-
|
322
|
-
|
323
|
-
|
324
|
-
|
325
|
-
|
326
|
-
|
327
|
-
|
328
|
-
|
329
|
-
|
330
|
-
|
331
|
-
|
332
|
-
|
333
|
-
|
334
|
-
|
335
|
-
|
336
|
-
|
337
|
-
|
338
|
-
|
339
|
-
|
340
|
-
|
341
|
-
|
342
|
-
|
343
|
-
|
344
|
-
|
345
|
-
|
346
|
-
|
347
|
-
|
348
|
-
|
349
|
-
|
350
|
-
|
351
|
-
|
352
|
-
|
353
|
-
|
354
|
-
|
355
|
-
|
356
|
-
|
357
|
-
|
358
|
-
|
359
|
-
|
360
|
-
|
361
|
-
|
362
|
-
|
363
|
-
|
364
|
-
|
365
|
-
|
366
|
-
|
367
|
-
|
368
|
-
|
369
|
-
|
370
|
-
|
371
|
-
|
372
|
-
|
373
|
-
|
374
|
-
|
375
|
-
|
376
|
-
#
|
377
|
-
#
|
378
|
-
#
|
379
|
-
|
380
|
-
|
381
|
-
|
382
|
-
|
383
|
-
|
384
|
-
|
385
|
-
|
386
|
-
|
387
|
-
|
388
|
-
|
389
|
-
|
390
|
-
|
391
|
-
|
392
|
-
|
393
|
-
|
394
|
-
|
395
|
-
#
|
396
|
-
#
|
397
|
-
#
|
398
|
-
#
|
399
|
-
#
|
400
|
-
|
401
|
-
|
402
|
-
|
403
|
-
|
404
|
-
|
405
|
-
|
406
|
-
|
407
|
-
|
408
|
-
|
409
|
-
|
410
|
-
|
411
|
-
|
412
|
-
|
413
|
-
|
414
|
-
|
415
|
-
|
416
|
-
|
417
|
-
|
418
|
-
|
419
|
-
|
420
|
-
|
421
|
-
|
422
|
-
|
423
|
-
|
424
|
-
|
425
|
-
|
426
|
-
|
427
|
-
|
428
|
-
|
429
|
-
|
430
|
-
|
431
|
-
|
432
|
-
|
433
|
-
|
434
|
-
|
435
|
-
|
436
|
-
|
437
|
-
|
438
|
-
|
439
|
-
|
440
|
-
|
441
|
-
|
442
|
-
|
1
|
+
#--
|
2
|
+
# Copyright (c) 2009 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
|
+
|
24
|
+
require 'rubygems'
|
25
|
+
require 'eventmachine'
|
26
|
+
require 'win32/process'
|
27
|
+
|
28
|
+
require File.join(File.dirname(__FILE__), 'right_popen.so') # win32 native code
|
29
|
+
|
30
|
+
module RightScale
|
31
|
+
|
32
|
+
# Eventmachine callback handler for stdin stream
|
33
|
+
module StdInHandler
|
34
|
+
|
35
|
+
# === Parameters
|
36
|
+
# options[:input](String):: Input to be streamed into child process stdin
|
37
|
+
# stream_in(IO):: Standard input stream.
|
38
|
+
def initialize(options, stream_in)
|
39
|
+
@stream_in = stream_in
|
40
|
+
@input = options[:input]
|
41
|
+
end
|
42
|
+
|
43
|
+
# Eventmachine callback asking for more to write
|
44
|
+
# Send input and close stream in
|
45
|
+
def post_init
|
46
|
+
if @input
|
47
|
+
send_data(@input)
|
48
|
+
close_connection_after_writing
|
49
|
+
@input = nil
|
50
|
+
else
|
51
|
+
close_connection
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
end
|
56
|
+
|
57
|
+
# Provides an eventmachine callback handler for the stdout stream.
|
58
|
+
module StdOutHandler
|
59
|
+
|
60
|
+
# Quacks like Process::Status, which we cannot instantiate ourselves because
|
61
|
+
# has no public new method. RightScale::popen3 needs this because the
|
62
|
+
# 'win32/process' gem currently won't return Process::Status objects but
|
63
|
+
# only returns a [pid, exitstatus] value.
|
64
|
+
class Status
|
65
|
+
# Process ID
|
66
|
+
attr_reader :pid
|
67
|
+
|
68
|
+
# Process exit code
|
69
|
+
attr_reader :exitstatus
|
70
|
+
|
71
|
+
# === Parameters
|
72
|
+
# pid(Integer):: Process ID.
|
73
|
+
#
|
74
|
+
# exitstatus(Integer):: Process exit code
|
75
|
+
def initialize(pid, exitstatus)
|
76
|
+
@pid = pid
|
77
|
+
@exitstatus = exitstatus
|
78
|
+
end
|
79
|
+
|
80
|
+
# Simulates Process::Status.exited?
|
81
|
+
#
|
82
|
+
# === Returns
|
83
|
+
# true in all cases because this object cannot be returned until the
|
84
|
+
# process exits
|
85
|
+
def exited?
|
86
|
+
return true
|
87
|
+
end
|
88
|
+
|
89
|
+
# Simulates Process::Status.success?
|
90
|
+
#
|
91
|
+
# === Returns
|
92
|
+
# true if the process returned zero as its exit code
|
93
|
+
def success?
|
94
|
+
return @exitstatus ? (0 == @exitstatus) : true;
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
# === Parameters
|
99
|
+
# options[:target](Object):: Object defining handler methods to be called.
|
100
|
+
# options[:stdout_handler](String):: Token for stdout handler method name.
|
101
|
+
# options[:exit_handler](String):: Token for exit handler method name.
|
102
|
+
# stderr_eventable(Connector):: EM object representing stderr handler.
|
103
|
+
# stream_out(IO):: Standard output stream.
|
104
|
+
# pid(Integer):: Child process ID.
|
105
|
+
def initialize(options, stderr_eventable, stream_out, pid)
|
106
|
+
@target = options[:target]
|
107
|
+
@stdout_handler = options[:stdout_handler]
|
108
|
+
@exit_handler = options[:exit_handler]
|
109
|
+
@stderr_eventable = stderr_eventable
|
110
|
+
@stream_out = stream_out
|
111
|
+
@pid = pid
|
112
|
+
@status = nil
|
113
|
+
end
|
114
|
+
|
115
|
+
# Callback from EM to asynchronously read the stdout stream. Note that this
|
116
|
+
# callback mechanism is deprecated after EM v0.12.8
|
117
|
+
def notify_readable
|
118
|
+
data = RightPopen.async_read(@stream_out)
|
119
|
+
receive_data(data) if (data && data.length > 0)
|
120
|
+
detach unless data
|
121
|
+
end
|
122
|
+
|
123
|
+
# Callback from EM to receive data, which we also use to handle the
|
124
|
+
# asynchronous data we read ourselves.
|
125
|
+
def receive_data(data)
|
126
|
+
@target.method(@stdout_handler).call(data) if @stdout_handler
|
127
|
+
end
|
128
|
+
|
129
|
+
# Override of Connection.get_status() for Windows implementation.
|
130
|
+
def get_status
|
131
|
+
unless @status
|
132
|
+
begin
|
133
|
+
@status = Status.new(@pid, Process.waitpid2(@pid)[1])
|
134
|
+
rescue Process::Error
|
135
|
+
# process is gone, which means we have no recourse to retrieve the
|
136
|
+
# actual exit code; let's be optimistic.
|
137
|
+
@status = Status.new(@pid, 0)
|
138
|
+
end
|
139
|
+
end
|
140
|
+
return @status
|
141
|
+
end
|
142
|
+
|
143
|
+
# Callback from EM to unbind.
|
144
|
+
def unbind
|
145
|
+
# We force the stderr watched handler to go away so that
|
146
|
+
# we don't end up with a broken pipe
|
147
|
+
@stderr_eventable.force_detach if @stderr_eventable
|
148
|
+
@target.method(@exit_handler).call(get_status) if @exit_handler
|
149
|
+
@stream_out.close
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
# Provides an eventmachine callback handler for the stderr stream.
|
154
|
+
module StdErrHandler
|
155
|
+
|
156
|
+
# === Parameters
|
157
|
+
# options[:target](Object):: Object defining handler methods to be called.
|
158
|
+
# options[:stderr_handler](Symbol):: Token for stderr handler method name.
|
159
|
+
# stream_err(IO):: Standard error stream.
|
160
|
+
def initialize(options, stream_err)
|
161
|
+
@target = options[:target]
|
162
|
+
@stderr_handler = options[:stderr_handler]
|
163
|
+
@stream_err = stream_err
|
164
|
+
@unbound = false
|
165
|
+
end
|
166
|
+
|
167
|
+
# Callback from EM to asynchronously read the stderr stream. Note that this
|
168
|
+
# callback mechanism is deprecated after EM v0.12.8
|
169
|
+
def notify_readable
|
170
|
+
data = RightPopen.async_read(@stream_err)
|
171
|
+
receive_data(data) if (data && data.length > 0)
|
172
|
+
detach unless data
|
173
|
+
end
|
174
|
+
|
175
|
+
# Callback from EM to receive data, which we also use to handle the
|
176
|
+
# asynchronous data we read ourselves.
|
177
|
+
def receive_data(data)
|
178
|
+
@target.method(@stderr_handler).call(data)
|
179
|
+
end
|
180
|
+
|
181
|
+
# Callback from EM to unbind.
|
182
|
+
def unbind
|
183
|
+
@unbound = true
|
184
|
+
@stream_err.close
|
185
|
+
end
|
186
|
+
|
187
|
+
# Forces detachment of the stderr handler on EM's next tick.
|
188
|
+
def force_detach
|
189
|
+
# Use next tick to prevent issue in EM where descriptors list
|
190
|
+
# gets out-of-sync when calling detach in an unbind callback
|
191
|
+
EM.next_tick { detach unless @unbound }
|
192
|
+
end
|
193
|
+
end
|
194
|
+
|
195
|
+
# Creates a child process and connects event handlers to the standard output
|
196
|
+
# and error streams used by the created process. Connectors use named pipes
|
197
|
+
# and asynchronous I/O in the native Windows implementation.
|
198
|
+
#
|
199
|
+
# See RightScale.popen3
|
200
|
+
def self.popen3_imp(options)
|
201
|
+
raise "EventMachine reactor must be started" unless EM.reactor_running?
|
202
|
+
|
203
|
+
# merge and format environment strings, if necessary.
|
204
|
+
environment_hash = options[:environment] || {}
|
205
|
+
environment_strings = RightPopenEx.merge_environment(environment_hash)
|
206
|
+
|
207
|
+
# launch cmd and request asynchronous output.
|
208
|
+
mode = "t"
|
209
|
+
show_window = false
|
210
|
+
asynchronous_output = true
|
211
|
+
stream_in, stream_out, stream_err, pid = RightPopen.popen4(options[:command], mode, show_window, asynchronous_output, environment_strings)
|
212
|
+
|
213
|
+
# close input immediately.
|
214
|
+
stream_in.close if options[:input].nil?
|
215
|
+
|
216
|
+
# attach handlers to event machine and let it monitor incoming data. the
|
217
|
+
# streams aren't used directly by the connectors except that they are closed
|
218
|
+
# on unbind.
|
219
|
+
stderr_eventable = EM.watch(stream_err, StdErrHandler, options, stream_err) { |c| c.notify_readable = true } if options[:stderr_handler]
|
220
|
+
EM.watch(stream_out, StdOutHandler, options, stderr_eventable, stream_out, pid) { |c| c.notify_readable = true }
|
221
|
+
EM.attach(stream_in, StdInHandler, options, stream_in) if options[:input]
|
222
|
+
|
223
|
+
# note that control returns to the caller, but the launched cmd continues
|
224
|
+
# running and sends output to the handlers. the caller is not responsible
|
225
|
+
# for waiting for the process to terminate or closing streams as the
|
226
|
+
# watched eventables will handle this automagically. notification will be
|
227
|
+
# sent to the exit_handler on process termination.
|
228
|
+
end
|
229
|
+
|
230
|
+
protected
|
231
|
+
|
232
|
+
module RightPopenEx
|
233
|
+
# Key class for case-insensitive hash insertion/lookup.
|
234
|
+
class NoCaseKey
|
235
|
+
# Internal key
|
236
|
+
attr_reader :key
|
237
|
+
|
238
|
+
# Stringizes object to be used as key
|
239
|
+
def initialize key
|
240
|
+
@key = key.to_s
|
241
|
+
end
|
242
|
+
|
243
|
+
# Hash code
|
244
|
+
def hash
|
245
|
+
@key.downcase.hash
|
246
|
+
end
|
247
|
+
|
248
|
+
# Equality for hash
|
249
|
+
def eql? other
|
250
|
+
@key.downcase.hash == other.key.downcase.hash
|
251
|
+
end
|
252
|
+
|
253
|
+
# Sort operator
|
254
|
+
def <=> other
|
255
|
+
@key.downcase <=> other.key.downcase
|
256
|
+
end
|
257
|
+
|
258
|
+
# Stringizer
|
259
|
+
def to_s
|
260
|
+
@key
|
261
|
+
end
|
262
|
+
|
263
|
+
# Inspector
|
264
|
+
def inspect
|
265
|
+
"\"#{@key}\""
|
266
|
+
end
|
267
|
+
end
|
268
|
+
|
269
|
+
# Hash of known environment variable keys to special merge method proc.
|
270
|
+
SPECIAL_MERGE_ENV_KEY_HASH = {
|
271
|
+
NoCaseKey.new('PATH') => lambda { |from_value, to_value| merge_environment_path_value(from_value, to_value) }
|
272
|
+
}
|
273
|
+
|
274
|
+
# Merges the given environment hash with the current environment for this
|
275
|
+
# process and the current environment for the current thread user from the
|
276
|
+
# registry. The result is a nul-terminated block of nul-terminated strings
|
277
|
+
# suitable for use in creating the child process.
|
278
|
+
#
|
279
|
+
# === Parameters
|
280
|
+
# environment_hash(Hash):: Hash of environment key/value pairs or empty to
|
281
|
+
# only merge the current process and currend thread user environment.
|
282
|
+
#
|
283
|
+
# === Returns
|
284
|
+
# merged string block
|
285
|
+
def self.merge_environment(environment_hash)
|
286
|
+
current_user_environment_hash = get_current_user_environment
|
287
|
+
machine_environment_hash = get_machine_environment
|
288
|
+
result_environment_hash = get_process_environment
|
289
|
+
|
290
|
+
# machine from registry supercedes process.
|
291
|
+
merge_environment2(machine_environment_hash, result_environment_hash)
|
292
|
+
|
293
|
+
# user environment from registry supercedes machine and process.
|
294
|
+
merge_environment2(current_user_environment_hash, result_environment_hash)
|
295
|
+
|
296
|
+
# caller's environment supercedes all.
|
297
|
+
merge_environment2(environment_hash, result_environment_hash)
|
298
|
+
|
299
|
+
return environment_hash_to_string_block(result_environment_hash)
|
300
|
+
end
|
301
|
+
|
302
|
+
# Merges from hash to another with special handling for known env vars.
|
303
|
+
#
|
304
|
+
# === Parameters
|
305
|
+
# from_hash(Hash):: hash of string or environment keys to environment values
|
306
|
+
# to_hash(Hash):: resulting hash or environment keys to environment values
|
307
|
+
#
|
308
|
+
# === Returns
|
309
|
+
# to_hash(Hash):: merged 'to' hash
|
310
|
+
def self.merge_environment2(from_hash, to_hash)
|
311
|
+
from_hash.each do |from_key, from_value|
|
312
|
+
to_key = from_key.kind_of?(NoCaseKey) ?
|
313
|
+
from_key :
|
314
|
+
NoCaseKey.new(from_key)
|
315
|
+
to_value = to_hash[to_key]
|
316
|
+
if to_value
|
317
|
+
special_merge_proc = SPECIAL_MERGE_ENV_KEY_HASH[to_key]
|
318
|
+
if special_merge_proc
|
319
|
+
# special merge
|
320
|
+
to_hash[to_key] = special_merge_proc.call(from_value, to_value)
|
321
|
+
else
|
322
|
+
# 'from' value supercedes existing 'to' value
|
323
|
+
to_hash[to_key] = from_value
|
324
|
+
end
|
325
|
+
else
|
326
|
+
# 'from' value replaces missing 'to' value
|
327
|
+
to_hash[to_key] = from_value
|
328
|
+
end
|
329
|
+
end
|
330
|
+
end
|
331
|
+
|
332
|
+
# Merges a PATH-style variable by appending any missing subpaths on the
|
333
|
+
# 'to' side to the value on the 'from' side in order of appearance. note
|
334
|
+
# that the ordering of paths on the 'to' side is not preserved when some of
|
335
|
+
# the paths also appear on the 'from' side. This is because paths on the
|
336
|
+
# 'from' side always take precedence. This is an issue if two paths
|
337
|
+
# reference similarly named executables and swapping the order of paths
|
338
|
+
# would cause the wrong executable to be invoked. To resolve this, the
|
339
|
+
# higher precedence path can be changed to ensure that the conflicting paths
|
340
|
+
# are both specified in the proper order. There is no trivial algorithm
|
341
|
+
# which can predict the proper ordering of such paths.
|
342
|
+
#
|
343
|
+
# === Parameters
|
344
|
+
# from_value(String):: value to merge from
|
345
|
+
# to_value(String):: value to merge to
|
346
|
+
#
|
347
|
+
# === Returns
|
348
|
+
# merged_value(String):: merged value
|
349
|
+
def self.merge_environment_path_value(from_value, to_value)
|
350
|
+
# normalize to backslashes for Windows-style PATH variable.
|
351
|
+
from_value = from_value.gsub(File::SEPARATOR, File::ALT_SEPARATOR)
|
352
|
+
to_value = to_value.gsub(File::SEPARATOR, File::ALT_SEPARATOR)
|
353
|
+
|
354
|
+
# quick outs.
|
355
|
+
return from_value if to_value.empty?
|
356
|
+
return to_value if from_value.empty?
|
357
|
+
|
358
|
+
# Windows paths are case-insensitive, so we want to match paths efficiently
|
359
|
+
# while being case-insensitive. we will make use of NoCaseKey again.
|
360
|
+
from_value_hash = {}
|
361
|
+
from_value.split(File::PATH_SEPARATOR).each { |path| from_value_hash[NoCaseKey.new(path)] = true }
|
362
|
+
appender = ""
|
363
|
+
to_value.split(File::PATH_SEPARATOR).each do |path|
|
364
|
+
if not from_value_hash[NoCaseKey.new(path)]
|
365
|
+
appender += File::PATH_SEPARATOR + path
|
366
|
+
end
|
367
|
+
end
|
368
|
+
|
369
|
+
return from_value + appender
|
370
|
+
end
|
371
|
+
|
372
|
+
# Queries the environment strings from the current thread/process user's
|
373
|
+
# environment. The resulting hash represents any variables set for the
|
374
|
+
# persisted user context but any set dynamically in the current process
|
375
|
+
# context.
|
376
|
+
#
|
377
|
+
# === Returns
|
378
|
+
# environment_hash(Hash):: hash of environment key (String) to value (String).
|
379
|
+
def self.get_current_user_environment
|
380
|
+
environment_strings = RightPopen.get_current_user_environment
|
381
|
+
|
382
|
+
return string_block_to_environment_hash(environment_strings)
|
383
|
+
end
|
384
|
+
|
385
|
+
# Queries the environment strings from the machine's environment.
|
386
|
+
#
|
387
|
+
# === Returns
|
388
|
+
# environment_hash(Hash):: hash of environment key (String) to value (String).
|
389
|
+
def self.get_machine_environment
|
390
|
+
environment_strings = RightPopen.get_machine_environment
|
391
|
+
|
392
|
+
return string_block_to_environment_hash(environment_strings)
|
393
|
+
end
|
394
|
+
|
395
|
+
# Queries the environment strings from the process environment (which is kept
|
396
|
+
# in memory for each process and generally begins life as a copy of the
|
397
|
+
# process user's environment context plus any changes made by ancestral
|
398
|
+
# processes).
|
399
|
+
#
|
400
|
+
# === Returns
|
401
|
+
# environment_hash(Hash):: hash of environment key (String) to value (String).
|
402
|
+
def self.get_process_environment
|
403
|
+
environment_strings = RightPopen.get_process_environment
|
404
|
+
|
405
|
+
return string_block_to_environment_hash(environment_strings)
|
406
|
+
end
|
407
|
+
|
408
|
+
# Converts a nul-terminated block of nul-terminated strings to a hash by
|
409
|
+
# splitting the block on nul characters until the empty string is found.
|
410
|
+
# splits substrings on the '=' character which is used to delimit key from
|
411
|
+
# value in Windows environment blocks.
|
412
|
+
#
|
413
|
+
# === Paramters
|
414
|
+
# string_block(String):: string containing nul-terminated substrings followed
|
415
|
+
# by a nul-terminator.
|
416
|
+
#
|
417
|
+
# === Returns
|
418
|
+
# string_hash(Hash):: hash of string to string
|
419
|
+
def self.string_block_to_environment_hash(string_block)
|
420
|
+
result_hash = {}
|
421
|
+
last_offset = 0
|
422
|
+
string_block_length = string_block.length
|
423
|
+
while last_offset < string_block_length
|
424
|
+
offset = string_block.index(0.chr, last_offset)
|
425
|
+
if offset.nil?
|
426
|
+
offset = string_block.length
|
427
|
+
end
|
428
|
+
env_string = string_block[last_offset, offset - last_offset]
|
429
|
+
break if env_string.empty?
|
430
|
+
last_offset = offset + 1
|
431
|
+
|
432
|
+
# note that Windows uses "=C:=C:\" notation for working directory info, so
|
433
|
+
# ignore equals if it is the first character.
|
434
|
+
equals_offset = env_string.index('=', 1)
|
435
|
+
if equals_offset
|
436
|
+
env_key = env_string[0, equals_offset]
|
437
|
+
env_value = env_string[equals_offset + 1..-1]
|
438
|
+
result_hash[NoCaseKey.new(env_key)] = env_value
|
439
|
+
end
|
440
|
+
end
|
441
|
+
|
442
|
+
return result_hash
|
443
|
+
end
|
444
|
+
|
445
|
+
# Converts a hash of string to string to a string block by combining pairs
|
446
|
+
# into a single string delimited by the '=' character and then placing nul-
|
447
|
+
# terminators after each pair, followed by a final nul-terminator.
|
448
|
+
#
|
449
|
+
# === Parameters
|
450
|
+
# environment_hash(Hash):: hash of
|
451
|
+
def self.environment_hash_to_string_block(environment_hash)
|
452
|
+
result_block = ""
|
453
|
+
environment_hash.keys.sort.each do |key|
|
454
|
+
result_block += "#{key}=#{environment_hash[key]}\0"
|
455
|
+
end
|
456
|
+
|
457
|
+
return result_block + "\0"
|
458
|
+
end
|
459
|
+
|
460
|
+
end
|
461
|
+
end
|