OSDN Git Service

80f6f3c7dd489d28535e026501c2b4852c26ff35
[pf3gnuchains/gcc-fork.git] / libgo / go / exec / exec.go
1 // Copyright 2009 The Go Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style
3 // license that can be found in the LICENSE file.
4
5 // The exec package runs external commands.
6 package exec
7
8 import (
9         "os"
10         "strconv"
11 )
12
13 // Arguments to Run.
14 const (
15         DevNull = iota
16         PassThrough
17         Pipe
18         MergeWithStdout
19 )
20
21 // A Cmd represents a running command.
22 // Stdin, Stdout, and Stderr are Files representing pipes
23 // connected to the running command's standard input, output, and error,
24 // or else nil, depending on the arguments to Run.
25 // Process represents the underlying operating system process.
26 type Cmd struct {
27         Stdin   *os.File
28         Stdout  *os.File
29         Stderr  *os.File
30         Process *os.Process
31 }
32
33 // PathError records the name of a binary that was not
34 // found on the current $PATH.
35 type PathError struct {
36         Name string
37 }
38
39 func (e *PathError) String() string {
40         return "command " + strconv.Quote(e.Name) + " not found in $PATH"
41 }
42
43 // Given mode (DevNull, etc), return file for child
44 // and file to record in Cmd structure.
45 func modeToFiles(mode, fd int) (*os.File, *os.File, os.Error) {
46         switch mode {
47         case DevNull:
48                 rw := os.O_WRONLY
49                 if fd == 0 {
50                         rw = os.O_RDONLY
51                 }
52                 f, err := os.Open(os.DevNull, rw, 0)
53                 return f, nil, err
54         case PassThrough:
55                 switch fd {
56                 case 0:
57                         return os.Stdin, nil, nil
58                 case 1:
59                         return os.Stdout, nil, nil
60                 case 2:
61                         return os.Stderr, nil, nil
62                 }
63         case Pipe:
64                 r, w, err := os.Pipe()
65                 if err != nil {
66                         return nil, nil, err
67                 }
68                 if fd == 0 {
69                         return r, w, nil
70                 }
71                 return w, r, nil
72         }
73         return nil, nil, os.EINVAL
74 }
75
76 // Run starts the named binary running with
77 // arguments argv and environment envv.
78 // It returns a pointer to a new Cmd representing
79 // the command or an error.
80 //
81 // The parameters stdin, stdout, and stderr
82 // specify how to handle standard input, output, and error.
83 // The choices are DevNull (connect to /dev/null),
84 // PassThrough (connect to the current process's standard stream),
85 // Pipe (connect to an operating system pipe), and
86 // MergeWithStdout (only for standard error; use the same
87 // file descriptor as was used for standard output).
88 // If a parameter is Pipe, then the corresponding field (Stdin, Stdout, Stderr)
89 // of the returned Cmd is the other end of the pipe.
90 // Otherwise the field in Cmd is nil.
91 func Run(name string, argv, envv []string, dir string, stdin, stdout, stderr int) (c *Cmd, err os.Error) {
92         c = new(Cmd)
93         var fd [3]*os.File
94
95         if fd[0], c.Stdin, err = modeToFiles(stdin, 0); err != nil {
96                 goto Error
97         }
98         if fd[1], c.Stdout, err = modeToFiles(stdout, 1); err != nil {
99                 goto Error
100         }
101         if stderr == MergeWithStdout {
102                 fd[2] = fd[1]
103         } else if fd[2], c.Stderr, err = modeToFiles(stderr, 2); err != nil {
104                 goto Error
105         }
106
107         // Run command.
108         c.Process, err = os.StartProcess(name, argv, envv, dir, fd[0:])
109         if err != nil {
110                 goto Error
111         }
112         if fd[0] != os.Stdin {
113                 fd[0].Close()
114         }
115         if fd[1] != os.Stdout {
116                 fd[1].Close()
117         }
118         if fd[2] != os.Stderr && fd[2] != fd[1] {
119                 fd[2].Close()
120         }
121         return c, nil
122
123 Error:
124         if fd[0] != os.Stdin && fd[0] != nil {
125                 fd[0].Close()
126         }
127         if fd[1] != os.Stdout && fd[1] != nil {
128                 fd[1].Close()
129         }
130         if fd[2] != os.Stderr && fd[2] != nil && fd[2] != fd[1] {
131                 fd[2].Close()
132         }
133         if c.Stdin != nil {
134                 c.Stdin.Close()
135         }
136         if c.Stdout != nil {
137                 c.Stdout.Close()
138         }
139         if c.Stderr != nil {
140                 c.Stderr.Close()
141         }
142         if c.Process != nil {
143                 c.Process.Release()
144         }
145         return nil, err
146 }
147
148 // Wait waits for the running command c,
149 // returning the Waitmsg returned when the process exits.
150 // The options are passed to the process's Wait method.
151 // Setting options to 0 waits for c to exit;
152 // other options cause Wait to return for other
153 // process events; see package os for details.
154 func (c *Cmd) Wait(options int) (*os.Waitmsg, os.Error) {
155         if c.Process == nil {
156                 return nil, os.ErrorString("exec: invalid use of Cmd.Wait")
157         }
158         w, err := c.Process.Wait(options)
159         if w != nil && (w.Exited() || w.Signaled()) {
160                 c.Process.Release()
161                 c.Process = nil
162         }
163         return w, err
164 }
165
166 // Close waits for the running command c to exit,
167 // if it hasn't already, and then closes the non-nil file descriptors
168 // c.Stdin, c.Stdout, and c.Stderr.
169 func (c *Cmd) Close() os.Error {
170         if c.Process != nil {
171                 // Loop on interrupt, but
172                 // ignore other errors -- maybe
173                 // caller has already waited for pid.
174                 _, err := c.Wait(0)
175                 for err == os.EINTR {
176                         _, err = c.Wait(0)
177                 }
178         }
179
180         // Close the FDs that are still open.
181         var err os.Error
182         if c.Stdin != nil && c.Stdin.Fd() >= 0 {
183                 if err1 := c.Stdin.Close(); err1 != nil {
184                         err = err1
185                 }
186         }
187         if c.Stdout != nil && c.Stdout.Fd() >= 0 {
188                 if err1 := c.Stdout.Close(); err1 != nil && err != nil {
189                         err = err1
190                 }
191         }
192         if c.Stderr != nil && c.Stderr != c.Stdout && c.Stderr.Fd() >= 0 {
193                 if err1 := c.Stderr.Close(); err1 != nil && err != nil {
194                         err = err1
195                 }
196         }
197         return err
198 }