right_popen 1.0.7-x86-mswin32-60 → 1.0.9-x86-mswin32-60
Sign up to get free protection for your applications and to get access to all the features.
- 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
|