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