# File lib/puppet/util.rb, line 247
247:   def execute(command, arguments = {:failonfail => true, :combine => true})
248:     if command.is_a?(Array)
249:       command = command.flatten.collect { |i| i.to_s }
250:       str = command.join(" ")
251:     else
252:       # We require an array here so we know where we're incorrectly
253:       # using a string instead of an array.  Once everything is
254:       # switched to an array, we might relax this requirement.
255:       raise ArgumentError, "Must pass an array to execute()"
256:     end
257: 
258:     if respond_to? :debug
259:       debug "Executing '#{str}'"
260:     else
261:       Puppet.debug "Executing '#{str}'"
262:     end
263: 
264:     arguments[:uid] = Puppet::Util::SUIDManager.convert_xid(:uid, arguments[:uid]) if arguments[:uid]
265:     arguments[:gid] = Puppet::Util::SUIDManager.convert_xid(:gid, arguments[:gid]) if arguments[:gid]
266: 
267:     @@os ||= Facter.value(:operatingsystem)
268:     output = nil
269:     child_pid, child_status = nil
270:     # There are problems with read blocking with badly behaved children
271:     # read.partialread doesn't seem to capture either stdout or stderr
272:     # We hack around this using a temporary file
273: 
274:     # The idea here is to avoid IO#read whenever possible.
275:     output_file="/dev/null"
276:     error_file="/dev/null"
277:     if ! arguments[:squelch]
278:       require "tempfile"
279:       output_file = Tempfile.new("puppet")
280:       error_file=output_file if arguments[:combine]
281:     end
282: 
283:     if Puppet.features.posix?
284:       oldverb = $VERBOSE
285:       $VERBOSE = nil
286:       child_pid = Kernel.fork
287:       $VERBOSE = oldverb
288:       if child_pid
289:         # Parent process executes this
290:         child_status = (Process.waitpid2(child_pid)[1]).to_i >> 8
291:       else
292:         # Child process executes this
293:         Process.setsid
294:         begin
295:           if arguments[:stdinfile]
296:             $stdin.reopen(arguments[:stdinfile])
297:           else
298:             $stdin.reopen("/dev/null")
299:           end
300:           $stdout.reopen(output_file)
301:           $stderr.reopen(error_file)
302: 
303:           3.upto(256){|fd| IO::new(fd).close rescue nil}
304:           if arguments[:gid]
305:             Process.egid = arguments[:gid]
306:             Process.gid = arguments[:gid] unless @@os == "Darwin"
307:           end
308:           if arguments[:uid]
309:             Process.euid = arguments[:uid]
310:             Process.uid = arguments[:uid] unless @@os == "Darwin"
311:           end
312:           ENV['LANG'] = ENV['LC_ALL'] = ENV['LC_MESSAGES'] = ENV['LANGUAGE'] = 'C'
313:           if command.is_a?(Array)
314:             Kernel.exec(*command)
315:           else
316:             Kernel.exec(command)
317:           end
318:         rescue => detail
319:           puts detail.to_s
320:           exit!(1)
321:         end
322:       end
323:     elsif Puppet.features.microsoft_windows?
324:       command = command.collect {|part| '"' + part.gsub(/"/, '\\"') + '"'}.join(" ") if command.is_a?(Array)
325:       Puppet.debug "Creating process '#{command}'"
326:       processinfo = Process.create( :command_line => command )
327:       child_status = (Process.waitpid2(child_pid)[1]).to_i >> 8
328:     end
329: 
330:     # read output in if required
331:     if ! arguments[:squelch]
332: 
333:       # Make sure the file's actually there.  This is
334:       # basically a race condition, and is probably a horrible
335:       # way to handle it, but, well, oh well.
336:       unless FileTest.exists?(output_file.path)
337:         Puppet.warning "sleeping"
338:         sleep 0.5
339:         unless FileTest.exists?(output_file.path)
340:           Puppet.warning "sleeping 2"
341:           sleep 1
342:           unless FileTest.exists?(output_file.path)
343:             Puppet.warning "Could not get output"
344:             output = ""
345:           end
346:         end
347:       end
348:       unless output
349:         # We have to explicitly open here, so that it reopens
350:         # after the child writes.
351:         output = output_file.open.read
352: 
353:         # The 'true' causes the file to get unlinked right away.
354:         output_file.close(true)
355:       end
356:     end
357: 
358:     if arguments[:failonfail]
359:       unless child_status == 0
360:         raise ExecutionFailure, "Execution of '#{str}' returned #{child_status}: #{output}"
361:       end
362:     end
363: 
364:     output
365:   end