summaryrefslogtreecommitdiff
path: root/src/lib/exec.go
diff options
context:
space:
mode:
Diffstat (limited to 'src/lib/exec.go')
-rw-r--r--src/lib/exec.go160
1 files changed, 160 insertions, 0 deletions
diff --git a/src/lib/exec.go b/src/lib/exec.go
new file mode 100644
index 000000000..ec48801f7
--- /dev/null
+++ b/src/lib/exec.go
@@ -0,0 +1,160 @@
+// Copyright 2009 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package exec
+
+import (
+ "os";
+ "syscall";
+)
+
+const (
+ DevNull = iota;
+ Passthru;
+ Pipe;
+ MergeWithStdout;
+)
+
+type Cmd struct {
+ Stdin *os.FD;
+ Stdout *os.FD;
+ Stderr *os.FD;
+ Pid int;
+}
+
+// Given mode (DevNull, etc), return fd for child
+// and fd to record in Cmd structure.
+func modeToFDs(mode, fd int) (*os.FD, *os.FD, *os.Error) {
+ switch mode {
+ case DevNull:
+ rw := os.O_WRONLY;
+ if fd == 0 {
+ rw = os.O_RDONLY;
+ }
+ f, err := os.Open("/dev/null", rw, 0);
+ return f, nil, err;
+ case Passthru:
+ switch fd {
+ case 0:
+ return os.Stdin, nil, nil;
+ case 1:
+ return os.Stdout, nil, nil;
+ case 2:
+ return os.Stderr, nil, nil;
+ }
+ case Pipe:
+ r, w, err := os.Pipe();
+ if err != nil {
+ return nil, nil, err;
+ }
+ if fd == 0 {
+ return r, w, nil;
+ }
+ return w, r, nil;
+ }
+ return nil, nil, os.EINVAL;
+}
+
+// Start command running with pipes possibly
+// connected to stdin, stdout, stderr.
+// TODO(rsc): Should the stdin,stdout,stderr args
+// be [3]int instead?
+func OpenCmd(argv0 string, argv, envv []string, stdin, stdout, stderr int)
+ (p *Cmd, err *os.Error)
+{
+ p = new(Cmd);
+ var fd [3]*os.FD;
+
+ if fd[0], p.Stdin, err = modeToFDs(stdin, 0); err != nil {
+ goto Error;
+ }
+ if fd[1], p.Stdout, err = modeToFDs(stdout, 1); err != nil {
+ goto Error;
+ }
+ if stderr == MergeWithStdout {
+ p.Stderr = p.Stdout;
+ } else if fd[2], p.Stderr, err = modeToFDs(stderr, 2); err != nil {
+ goto Error;
+ }
+
+ // Run command.
+ p.Pid, err = os.ForkExec(argv0, argv, envv, fd);
+ if err != nil {
+ goto Error;
+ }
+ if fd[0] != os.Stdin {
+ fd[0].Close();
+ }
+ if fd[1] != os.Stdout {
+ fd[1].Close();
+ }
+ if fd[2] != os.Stderr && fd[2] != fd[1] {
+ fd[2].Close();
+ }
+ return p, nil;
+
+Error:
+ if fd[0] != os.Stdin && fd[0] != nil {
+ fd[0].Close();
+ }
+ if fd[1] != os.Stdout && fd[1] != nil {
+ fd[1].Close();
+ }
+ if fd[2] != os.Stderr && fd[2] != nil && fd[2] != fd[1] {
+ fd[2].Close();
+ }
+ if p.Stdin != nil {
+ p.Stdin.Close();
+ }
+ if p.Stdout != nil {
+ p.Stdout.Close();
+ }
+ if p.Stderr != nil {
+ p.Stderr.Close();
+ }
+ return nil, err;
+}
+
+func (p *Cmd) Wait(options uint64) (*os.Waitmsg, *os.Error) {
+ if p.Pid < 0 {
+ return nil, os.EINVAL;
+ }
+ w, err := os.Wait(p.Pid, options);
+ if w != nil && (w.Exited() || w.Signaled()) {
+ p.Pid = -1;
+ }
+ return w, err;
+}
+
+func (p *Cmd) Close() *os.Error {
+ if p.Pid >= 0 {
+ // Loop on interrupt, but
+ // ignore other errors -- maybe
+ // caller has already waited for pid.
+ w, err := p.Wait(0);
+ for err == os.EINTR {
+ w, err = p.Wait(0);
+ }
+ }
+
+ // Close the FDs that are still open.
+ var err *os.Error;
+ if p.Stdin != nil && p.Stdin.Fd() >= 0 {
+ if err1 := p.Stdin.Close(); err1 != nil {
+ err = err1;
+ }
+ }
+ if p.Stdout != nil && p.Stdout.Fd() >= 0 {
+ if err1 := p.Stdout.Close(); err1 != nil && err != nil {
+ err = err1;
+ }
+ }
+ if p.Stderr != nil && p.Stderr != p.Stdout && p.Stderr.Fd() >= 0 {
+ if err1 := p.Stderr.Close(); err1 != nil && err != nil {
+ err = err1;
+ }
+ }
+ return err;
+}
+