1
2
3
4
5
6
7
8 package exec
9
10 import (
11 "bytes"
12 "io"
13 "os"
14 "strconv"
15 "syscall"
16 )
17
18
19
20 type Error struct {
21 Name string
22 Error os.Error
23 }
24
25 func (e *Error) String() string {
26 return "exec: " + strconv.Quote(e.Name) + ": " + e.Error.String()
27 }
28
29
30 type Cmd struct {
31
32
33
34
35 Path string
36
37
38
39
40
41 Args []string
42
43
44
45 Env []string
46
47
48
49
50 Dir string
51
52
53
54 Stdin io.Reader
55
56
57
58
59
60
61
62
63 Stdout io.Writer
64 Stderr io.Writer
65
66
67
68 SysProcAttr *syscall.SysProcAttr
69
70
71 Process *os.Process
72
73 err os.Error
74 finished bool
75 childFiles []*os.File
76 closeAfterStart []io.Closer
77 closeAfterWait []io.Closer
78 goroutine []func() os.Error
79 errch chan os.Error
80 }
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95 func Command(name string, arg ...string) *Cmd {
96 aname, err := LookPath(name)
97 if err != nil {
98 aname = name
99 }
100 return &Cmd{
101 Path: aname,
102 Args: append([]string{name}, arg...),
103 err: err,
104 }
105 }
106
107
108
109 func interfaceEqual(a, b interface{}) bool {
110 defer func() {
111 recover()
112 }()
113 return a == b
114 }
115
116 func (c *Cmd) envv() []string {
117 if c.Env != nil {
118 return c.Env
119 }
120 return os.Environ()
121 }
122
123 func (c *Cmd) argv() []string {
124 if len(c.Args) > 0 {
125 return c.Args
126 }
127 return []string{c.Path}
128 }
129
130 func (c *Cmd) stdin() (f *os.File, err os.Error) {
131 if c.Stdin == nil {
132 f, err = os.Open(os.DevNull)
133 c.closeAfterStart = append(c.closeAfterStart, f)
134 return
135 }
136
137 if f, ok := c.Stdin.(*os.File); ok {
138 return f, nil
139 }
140
141 pr, pw, err := os.Pipe()
142 if err != nil {
143 return
144 }
145
146 c.closeAfterStart = append(c.closeAfterStart, pr)
147 c.closeAfterWait = append(c.closeAfterWait, pw)
148 c.goroutine = append(c.goroutine, func() os.Error {
149 _, err := io.Copy(pw, c.Stdin)
150 if err1 := pw.Close(); err == nil {
151 err = err1
152 }
153 return err
154 })
155 return pr, nil
156 }
157
158 func (c *Cmd) stdout() (f *os.File, err os.Error) {
159 return c.writerDescriptor(c.Stdout)
160 }
161
162 func (c *Cmd) stderr() (f *os.File, err os.Error) {
163 if c.Stderr != nil && interfaceEqual(c.Stderr, c.Stdout) {
164 return c.childFiles[1], nil
165 }
166 return c.writerDescriptor(c.Stderr)
167 }
168
169 func (c *Cmd) writerDescriptor(w io.Writer) (f *os.File, err os.Error) {
170 if w == nil {
171 f, err = os.OpenFile(os.DevNull, os.O_WRONLY, 0)
172 c.closeAfterStart = append(c.closeAfterStart, f)
173 return
174 }
175
176 if f, ok := w.(*os.File); ok {
177 return f, nil
178 }
179
180 pr, pw, err := os.Pipe()
181 if err != nil {
182 return
183 }
184
185 c.closeAfterStart = append(c.closeAfterStart, pw)
186 c.closeAfterWait = append(c.closeAfterWait, pr)
187 c.goroutine = append(c.goroutine, func() os.Error {
188 _, err := io.Copy(w, pr)
189 return err
190 })
191 return pw, nil
192 }
193
194
195
196
197
198
199
200
201
202
203 func (c *Cmd) Run() os.Error {
204 if err := c.Start(); err != nil {
205 return err
206 }
207 return c.Wait()
208 }
209
210
211 func (c *Cmd) Start() os.Error {
212 if c.err != nil {
213 return c.err
214 }
215 if c.Process != nil {
216 return os.NewError("exec: already started")
217 }
218
219 type F func(*Cmd) (*os.File, os.Error)
220 for _, setupFd := range []F{(*Cmd).stdin, (*Cmd).stdout, (*Cmd).stderr} {
221 fd, err := setupFd(c)
222 if err != nil {
223 return err
224 }
225 c.childFiles = append(c.childFiles, fd)
226 }
227
228 var err os.Error
229 c.Process, err = os.StartProcess(c.Path, c.argv(), &os.ProcAttr{
230 Dir: c.Dir,
231 Files: c.childFiles,
232 Env: c.envv(),
233 Sys: c.SysProcAttr,
234 })
235 if err != nil {
236 return err
237 }
238
239 for _, fd := range c.closeAfterStart {
240 fd.Close()
241 }
242
243 c.errch = make(chan os.Error, len(c.goroutine))
244 for _, fn := range c.goroutine {
245 go func(fn func() os.Error) {
246 c.errch <- fn()
247 }(fn)
248 }
249
250 return nil
251 }
252
253
254
255
256
257
258
259
260
261
262
263 func (c *Cmd) Wait() os.Error {
264 if c.Process == nil {
265 return os.NewError("exec: not started")
266 }
267 if c.finished {
268 return os.NewError("exec: Wait was already called")
269 }
270 c.finished = true
271 msg, err := c.Process.Wait(0)
272
273 var copyError os.Error
274 for _ = range c.goroutine {
275 if err := <-c.errch; err != nil && copyError == nil {
276 copyError = err
277 }
278 }
279
280 for _, fd := range c.closeAfterWait {
281 fd.Close()
282 }
283
284 if err != nil {
285 return err
286 } else if !msg.Exited() || msg.ExitStatus() != 0 {
287 return msg
288 }
289
290 return copyError
291 }
292
293
294 func (c *Cmd) Output() ([]byte, os.Error) {
295 if c.Stdout != nil {
296 return nil, os.NewError("exec: Stdout already set")
297 }
298 var b bytes.Buffer
299 c.Stdout = &b
300 err := c.Run()
301 return b.Bytes(), err
302 }
303
304
305
306 func (c *Cmd) CombinedOutput() ([]byte, os.Error) {
307 if c.Stdout != nil {
308 return nil, os.NewError("exec: Stdout already set")
309 }
310 if c.Stderr != nil {
311 return nil, os.NewError("exec: Stderr already set")
312 }
313 var b bytes.Buffer
314 c.Stdout = &b
315 c.Stderr = &b
316 err := c.Run()
317 return b.Bytes(), err
318 }
319
320
321
322 func (c *Cmd) StdinPipe() (io.WriteCloser, os.Error) {
323 if c.Stdin != nil {
324 return nil, os.NewError("exec: Stdin already set")
325 }
326 if c.Process != nil {
327 return nil, os.NewError("exec: StdinPipe after process started")
328 }
329 pr, pw, err := os.Pipe()
330 if err != nil {
331 return nil, err
332 }
333 c.Stdin = pr
334 c.closeAfterStart = append(c.closeAfterStart, pr)
335 c.closeAfterWait = append(c.closeAfterWait, pw)
336 return pw, nil
337 }
338
339
340
341
342 func (c *Cmd) StdoutPipe() (io.ReadCloser, os.Error) {
343 if c.Stdout != nil {
344 return nil, os.NewError("exec: Stdout already set")
345 }
346 if c.Process != nil {
347 return nil, os.NewError("exec: StdoutPipe after process started")
348 }
349 pr, pw, err := os.Pipe()
350 if err != nil {
351 return nil, err
352 }
353 c.Stdout = pw
354 c.closeAfterStart = append(c.closeAfterStart, pw)
355 c.closeAfterWait = append(c.closeAfterWait, pr)
356 return pr, nil
357 }
358
359
360
361
362 func (c *Cmd) StderrPipe() (io.ReadCloser, os.Error) {
363 if c.Stderr != nil {
364 return nil, os.NewError("exec: Stderr already set")
365 }
366 if c.Process != nil {
367 return nil, os.NewError("exec: StderrPipe after process started")
368 }
369 pr, pw, err := os.Pipe()
370 if err != nil {
371 return nil, err
372 }
373 c.Stderr = pw
374 c.closeAfterStart = append(c.closeAfterStart, pw)
375 c.closeAfterWait = append(c.closeAfterWait, pr)
376 return pr, nil
377 }