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