1# 2# shell/system-command.rb - 3# $Release Version: 0.7 $ 4# $Revision: 33638 $ 5# by Keiju ISHITSUKA(keiju@ruby-lang.org) 6# 7# -- 8# 9# 10# 11 12require "shell/filter" 13 14class Shell 15 class SystemCommand < Filter 16 def initialize(sh, command, *opts) 17 if t = opts.find{|opt| !opt.kind_of?(String) && opt.class} 18 Shell.Fail Error::TypeError, t.class, "String" 19 end 20 super(sh) 21 @command = command 22 @opts = opts 23 24 @input_queue = Queue.new 25 @pid = nil 26 27 sh.process_controller.add_schedule(self) 28 end 29 30 attr_reader :command 31 alias name command 32 33 def wait? 34 @shell.process_controller.waiting_job?(self) 35 end 36 37 def active? 38 @shell.process_controller.active_job?(self) 39 end 40 41 def input=(inp) 42 super 43 if active? 44 start_export 45 end 46 end 47 48 def start 49 notify([@command, *@opts].join(" ")) 50 51 @pid, @pipe_in, @pipe_out = @shell.process_controller.sfork(self) { 52 Dir.chdir @shell.pwd 53 $0 = @command 54 exec(@command, *@opts) 55 } 56 if @input 57 start_export 58 end 59 start_import 60 end 61 62 def flush 63 @pipe_out.flush if @pipe_out and !@pipe_out.closed? 64 end 65 66 def terminate 67 begin 68 @pipe_in.close 69 rescue IOError 70 end 71 begin 72 @pipe_out.close 73 rescue IOError 74 end 75 end 76 77 def kill(sig) 78 if @pid 79 Process.kill(sig, @pid) 80 end 81 end 82 83 def start_import 84 notify "Job(%id) start imp-pipe.", @shell.debug? 85 rs = @shell.record_separator unless rs 86 _eop = true 87 Thread.start { 88 begin 89 while l = @pipe_in.gets 90 @input_queue.push l 91 end 92 _eop = false 93 rescue Errno::EPIPE 94 _eop = false 95 ensure 96 if !ProcessController::USING_AT_EXIT_WHEN_PROCESS_EXIT and _eop 97 notify("warn: Process finishing...", 98 "wait for Job[%id] to finish pipe importing.", 99 "You can use Shell#transact or Shell#check_point for more safe execution.") 100 redo 101 end 102 notify "job(%id}) close imp-pipe.", @shell.debug? 103 @input_queue.push :EOF 104 @pipe_in.close 105 end 106 } 107 end 108 109 def start_export 110 notify "job(%id) start exp-pipe.", @shell.debug? 111 _eop = true 112 Thread.start{ 113 begin 114 @input.each do |l| 115 ProcessController::block_output_synchronize do 116 @pipe_out.print l 117 end 118 end 119 _eop = false 120 rescue Errno::EPIPE, Errno::EIO 121 _eop = false 122 ensure 123 if !ProcessController::USING_AT_EXIT_WHEN_PROCESS_EXIT and _eop 124 notify("shell: warn: Process finishing...", 125 "wait for Job(%id) to finish pipe exporting.", 126 "You can use Shell#transact or Shell#check_point for more safe execution.") 127 redo 128 end 129 notify "job(%id) close exp-pipe.", @shell.debug? 130 @pipe_out.close 131 end 132 } 133 end 134 135 alias super_each each 136 def each(rs = nil) 137 while (l = @input_queue.pop) != :EOF 138 yield l 139 end 140 end 141 142 # ex) 143 # if you wish to output: 144 # "shell: job(#{@command}:#{@pid}) close pipe-out." 145 # then 146 # mes: "job(%id) close pipe-out." 147 # yorn: Boolean(@shell.debug? or @shell.verbose?) 148 def notify(*opts) 149 @shell.notify(*opts) do |mes| 150 yield mes if iterator? 151 152 mes.gsub!("%id", "#{@command}:##{@pid}") 153 mes.gsub!("%name", "#{@command}") 154 mes.gsub!("%pid", "#{@pid}") 155 mes 156 end 157 end 158 end 159end 160