Skip to content
This repository
Browse code

Add a PID handler.

Prior to this the PID of the running process was
invisible to the caller of popen3.  An optional
handler notifies the caller as soon as the PID
is known.

Add pid_handler to example.
  • Loading branch information...
commit 34a1f7b8ae9a5281835feaa7bdd84311bc627afc 1 parent 425dac8
authored August 26, 2010
7  README.rdoc
Source Rendered
@@ -26,6 +26,11 @@ to report issues.
26 26
   @stdout_text = ""
27 27
   @stderr_text = ""
28 28
   @exit_status = nil
  29
+  @pid = nil
  30
+
  31
+  def on_pid(pid)
  32
+    @pid = pid
  33
+  end
29 34
 
30 35
   def on_read_stdout(data)
31 36
     @stdout_text << data
@@ -45,6 +50,7 @@ to report issues.
45 50
       RightScale.popen3(:command        => command,
46 51
                         :target         => self,
47 52
                         :environment    => nil,
  53
+                        :pid_handler    => :on_pid,
48 54
                         :stdout_handler => :on_read_stdout,
49 55
                         :stderr_handler => :on_read_stderr,
50 56
                         :exit_handler   => :on_exit)
@@ -60,6 +66,7 @@ to report issues.
60 66
   puts "@stdout_text = #{@stdout_text}"
61 67
   puts "@stderr_text = #{@stderr_text}"
62 68
   puts "@exit_status.exitstatus = #{@exit_status.exitstatus}"
  69
+  puts "@pid = #{@pid}"
63 70
 
64 71
 
65 72
 == INSTALLATION
8  lib/linux/right_popen.rb
@@ -110,6 +110,7 @@ def force_detach
110 110
   # standard streams of the child process.
111 111
   #
112 112
   # === Parameters
  113
+  # options[:pid_handler](Symbol):: Token for pid handler method name.
113 114
   # options[:temp_dir]:: Path to temporary directory where executable files are
114 115
   #                      created, default to /tmp if not specified
115 116
   #
@@ -140,7 +141,10 @@ def self.popen3_imp(options)
140 141
       end
141 142
 
142 143
       # Launch child process
143  
-      EM.popen(exec_file, StdOutHandler, options, c, r, w)
  144
+      connection = EM.popen(exec_file, StdOutHandler, options, c, r, w)
  145
+      if options[:pid_handler]
  146
+        options[:target].method(options[:pid_handler]).call(EM.get_subprocess_pid(connection.signature))
  147
+      end
144 148
 
145 149
       # Restore environment variables
146 150
       unless envs.empty?
@@ -149,7 +153,7 @@ def self.popen3_imp(options)
149 153
       end
150 154
 
151 155
       # Do not close 'w', strange things happen otherwise
152  
-      # (command protocol socket gets closed during decommission) 
  156
+      # (command protocol socket gets closed during decommission)
153 157
       $stderr.reopen saved_stderr
154 158
     end
155 159
     true
3  lib/right_popen.rb
@@ -49,6 +49,7 @@ module RightScale
49 49
   # options[:environment](Hash):: Hash of environment variables values keyed by name
50 50
   # options[:input](String):: Input string that will get streamed into child's process stdin
51 51
   # options[:target](Object):: object defining handler methods to be called, optional (no handlers can be defined if not specified)
  52
+  # options[:pid_handler](String):: PID notification handler method name, optional
52 53
   # options[:stdout_handler](String):: Stdout handler method name, optional
53 54
   # options[:stderr_handler](String):: Stderr handler method name, optional
54 55
   # options[:exit_handler](String):: Exit handler method name, optional
@@ -58,7 +59,7 @@ module RightScale
58 59
   def self.popen3(options)
59 60
     raise "EventMachine reactor must be started" unless EM.reactor_running?
60 61
     raise "Missing command" unless options[:command]
61  
-    raise "Missing target" unless options[:target] || !options[:stdout_handler] && !options[:stderr_handler] && !options[:exit_handler]
  62
+    raise "Missing target" unless options[:target] || !options[:stdout_handler] && !options[:stderr_handler] && !options[:exit_handler] && !options[:pid_handler]
62 63
     RightScale.popen3_imp(options)
63 64
     true
64 65
   end
5  lib/win32/right_popen.rb
@@ -217,7 +217,10 @@ def self.popen3_imp(options)
217 217
     # streams aren't used directly by the connectors except that they are closed
218 218
     # on unbind.
219 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 }
  220
+    EM.watch(stream_out, StdOutHandler, options, stderr_eventable, stream_out, pid) do |c|
  221
+      c.notify_readable = true
  222
+      options[:target].method(options[:pid_handler]).call(pid) if options[:pid_handler]
  223
+    end
221 224
     EM.attach(stream_in, StdInHandler, options, stream_in) if options[:input]
222 225
 
223 226
     # note that control returns to the caller, but the launched cmd continues
30  spec/right_popen_spec.rb
@@ -31,18 +31,21 @@ def initialize
31 31
       end
32 32
 
33 33
       attr_reader :output_text, :error_text, :status
  34
+      attr_accessor :pid
34 35
 
35 36
       def do_right_popen(command, env=nil, input=nil)
36 37
         @timeout = EM::Timer.new(2) { puts "\n** Failed to run #{command.inspect}: Timeout"; EM.stop }
37 38
         @output_text = ''
38 39
         @error_text  = ''
39 40
         @status      = nil
40  
-        RightScale.popen3(:command        => command, 
  41
+        @pid         = nil
  42
+        RightScale.popen3(:command        => command,
41 43
                           :input          => input,
42  
-                          :target         => self, 
  44
+                          :target         => self,
43 45
                           :environment    => env,
44  
-                          :stdout_handler => :on_read_stdout, 
45  
-                          :stderr_handler => :on_read_stderr, 
  46
+                          :stdout_handler => :on_read_stdout,
  47
+                          :stderr_handler => :on_read_stderr,
  48
+                          :pid_handler    => :on_pid,
46 49
                           :exit_handler   => :on_exit)
47 50
       end
48 51
 
@@ -68,6 +71,11 @@ def on_read_stderr(data)
68 71
         @error_text << data
69 72
       end
70 73
 
  74
+      def on_pid(pid)
  75
+        raise "PID already set!" unless @pid.nil?
  76
+        @pid = pid
  77
+      end
  78
+
71 79
       def on_exit(status)
72 80
         @last_iteration += 1
73 81
         @timeout.cancel if @timeout
@@ -94,6 +102,7 @@ def on_exit(status)
94 102
     runner.status.exitstatus.should == 0
95 103
     runner.output_text.should == STANDARD_MESSAGE + "\n"
96 104
     runner.error_text.should == ERROR_MESSAGE + "\n"
  105
+    runner.pid.should > 0
97 106
   end
98 107
 
99 108
   it 'should return the right status' do
@@ -103,6 +112,7 @@ def on_exit(status)
103 112
     runner.status.exitstatus.should == EXIT_STATUS
104 113
     runner.output_text.should == ''
105 114
     runner.error_text.should == ''
  115
+    runner.pid.should > 0
106 116
   end
107 117
 
108 118
   it 'should preserve the integrity of stdout when stderr is unavailable' do
@@ -118,6 +128,7 @@ def on_exit(status)
118 128
     end
119 129
     runner.output_text.should == results
120 130
     runner.error_text.should == ''
  131
+    runner.pid.should > 0
121 132
   end
122 133
 
123 134
   it 'should preserve the integrity of stderr when stdout is unavailable' do
@@ -133,6 +144,7 @@ def on_exit(status)
133 144
     end
134 145
     runner.error_text.should == results
135 146
     runner.output_text.should == ''
  147
+    runner.pid.should > 0
136 148
   end
137 149
 
138 150
   it 'should preserve the integrity of stdout and stderr despite interleaving' do
@@ -153,17 +165,20 @@ def on_exit(status)
153 165
       (results << "stderr #{i}\n") if 0 == i % 10
154 166
     end
155 167
     runner.error_text.should == results
  168
+    runner.pid.should > 0
156 169
   end
157  
-  
  170
+
158 171
   it 'should setup environment variables' do
159 172
     command = "\"#{RUBY_CMD}\" \"#{File.expand_path(File.join(File.dirname(__FILE__), 'print_env.rb'))}\""
160 173
     runner = RightPopenSpec::Runner.new
161 174
     runner.run_right_popen(command)
162 175
     runner.status.exitstatus.should == 0
163 176
     runner.output_text.should_not include('_test_')
  177
+    runner.pid = nil
164 178
     runner.run_right_popen(command, :__test__ => '42')
165 179
     runner.status.exitstatus.should == 0
166 180
     runner.output_text.should match(/^__test__=42$/)
  181
+    runner.pid.should > 0
167 182
   end
168 183
 
169 184
   it 'should restore environment variables' do
@@ -177,6 +192,7 @@ def on_exit(status)
177 192
     runner.output_text.should match(/^__test__=42$/)
178 193
     ENV.each { |k, v| old_envs[k].should == v }
179 194
     old_envs.each { |k, v| ENV[k].should == v }
  195
+    runner.pid.should > 0
180 196
   end
181 197
 
182 198
   if is_windows?
@@ -188,6 +204,7 @@ def on_exit(status)
188 204
       runner.run_right_popen(command, 'PATH' => "c:/bogus\\bin")
189 205
       runner.status.exitstatus.should == 0
190 206
       runner.output_text.should include('PATH=c:\\bogus\\bin;')
  207
+      runner.pid.should > 0
191 208
     end
192 209
   else
193 210
     it 'should allow running bash command lines starting with a built-in command' do
@@ -196,6 +213,7 @@ def on_exit(status)
196 213
       runner.run_right_popen(command)
197 214
       runner.status.exitstatus.should == 0
198 215
       runner.output_text.should == "1\n2\n3\n4\n5\n"
  216
+      runner.pid.should > 0
199 217
     end
200 218
   end
201 219
 
@@ -207,6 +225,7 @@ def on_exit(status)
207 225
     runner.status.exitstatus.should == 0
208 226
     runner.output_text.should == STANDARD_MESSAGE + "\n"
209 227
     runner.error_text.should == ERROR_MESSAGE + "\n"
  228
+    runner.pid.should > 0
210 229
   end
211 230
 
212 231
   it 'should pass input to child process' do
@@ -216,6 +235,7 @@ def on_exit(status)
216 235
     runner.status.exitstatus.should == 0
217 236
     runner.output_text.should == "43\n"
218 237
     runner.error_text.should be_empty
  238
+    runner.pid.should > 0
219 239
   end
220 240
 
221 241
 end

0 notes on commit 34a1f7b

Please sign in to comment.
Something went wrong with that request. Please try again.