summaryrefslogtreecommitdiff
path: root/src/pkg/os
diff options
context:
space:
mode:
Diffstat (limited to 'src/pkg/os')
-rw-r--r--src/pkg/os/Makefile4
-rw-r--r--src/pkg/os/env_windows.go4
-rw-r--r--src/pkg/os/exec.go61
-rw-r--r--src/pkg/os/exec_unix.go63
-rw-r--r--src/pkg/os/exec_windows.go52
-rw-r--r--src/pkg/os/file_windows.go9
-rw-r--r--src/pkg/os/inotify/inotify_linux.go4
-rw-r--r--src/pkg/os/os_test.go12
8 files changed, 162 insertions, 47 deletions
diff --git a/src/pkg/os/Makefile b/src/pkg/os/Makefile
index f6caf084c..3a81afe39 100644
--- a/src/pkg/os/Makefile
+++ b/src/pkg/os/Makefile
@@ -22,21 +22,25 @@ GOFILES_freebsd=\
env_unix.go\
file_unix.go\
sys_bsd.go\
+ exec_unix.go\
GOFILES_darwin=\
env_unix.go\
file_unix.go\
sys_bsd.go\
+ exec_unix.go\
GOFILES_linux=\
env_unix.go\
file_unix.go\
sys_linux.go\
+ exec_unix.go\
GOFILES_windows=\
env_windows.go\
file_windows.go\
sys_windows.go\
+ exec_windows.go\
GOFILES+=$(GOFILES_$(GOOS))
diff --git a/src/pkg/os/env_windows.go b/src/pkg/os/env_windows.go
index d2b159dfb..a45d79be3 100644
--- a/src/pkg/os/env_windows.go
+++ b/src/pkg/os/env_windows.go
@@ -50,8 +50,8 @@ func Setenv(key, value string) Error {
if len(value) > 0 {
v = syscall.StringToUTF16Ptr(value)
}
- ok, e := syscall.SetEnvironmentVariable(syscall.StringToUTF16Ptr(key), v)
- if !ok {
+ e := syscall.SetEnvironmentVariable(syscall.StringToUTF16Ptr(key), v)
+ if e != 0 {
return NewSyscallError("SetEnvironmentVariable", e)
}
return nil
diff --git a/src/pkg/os/exec.go b/src/pkg/os/exec.go
index 100d984d1..dbdfacc58 100644
--- a/src/pkg/os/exec.go
+++ b/src/pkg/os/exec.go
@@ -5,17 +5,29 @@
package os
import (
+ "runtime"
"syscall"
)
-// ForkExec forks the current process and invokes Exec with the program, arguments,
-// and environment specified by name, argv, and envv. It returns the process
-// id of the forked process and an Error, if any. The fd array specifies the
+// Process stores the information about a process created by StartProcess.
+type Process struct {
+ Pid int
+ handle int
+}
+
+func newProcess(pid, handle int) *Process {
+ p := &Process{pid, handle}
+ runtime.SetFinalizer(p, (*Process).Release)
+ return p
+}
+
+// StartProcess starts a new process with the program, arguments,
+// and environment specified by name, argv, and envv. The fd array specifies the
// file descriptors to be set up in the new process: fd[0] will be Unix file
// descriptor 0 (standard input), fd[1] descriptor 1, and so on. A nil entry
// will cause the child to have no open file descriptor with that index.
// If dir is not empty, the child chdirs into the directory before execing the program.
-func ForkExec(name string, argv []string, envv []string, dir string, fd []*File) (pid int, err Error) {
+func StartProcess(name string, argv []string, envv []string, dir string, fd []*File) (p *Process, err Error) {
if envv == nil {
envv = Environ()
}
@@ -29,17 +41,17 @@ func ForkExec(name string, argv []string, envv []string, dir string, fd []*File)
}
}
- p, e := syscall.ForkExec(name, argv, envv, dir, intfd)
+ pid, h, e := syscall.StartProcess(name, argv, envv, dir, intfd)
if e != 0 {
- return 0, &PathError{"fork/exec", name, Errno(e)}
+ return nil, &PathError{"fork/exec", name, Errno(e)}
}
- return p, nil
+ return newProcess(pid, h), nil
}
// Exec replaces the current process with an execution of the
// named binary, with arguments argv and environment envv.
// If successful, Exec never returns. If it fails, it returns an Error.
-// ForkExec is almost always a better way to execute a program.
+// StartProcess is almost always a better way to execute a program.
func Exec(name string, argv []string, envv []string) Error {
if envv == nil {
envv = Environ()
@@ -65,37 +77,18 @@ type Waitmsg struct {
Rusage *syscall.Rusage // System-dependent resource usage info.
}
-// Options for Wait.
-const (
- WNOHANG = syscall.WNOHANG // Don't wait if no process has exited.
- WSTOPPED = syscall.WSTOPPED // If set, status of stopped subprocesses is also reported.
- WUNTRACED = syscall.WUNTRACED // Usually an alias for WSTOPPED.
- WRUSAGE = 1 << 20 // Record resource usage.
-)
-
-// WRUSAGE must not be too high a bit, to avoid clashing with Linux's
-// WCLONE, WALL, and WNOTHREAD flags, which sit in the top few bits of
-// the options
-
// Wait waits for process pid to exit or stop, and then returns a
// Waitmsg describing its status and an Error, if any. The options
// (WNOHANG etc.) affect the behavior of the Wait call.
+// Wait is equivalent to calling FindProcess and then Wait
+// and Release on the result.
func Wait(pid int, options int) (w *Waitmsg, err Error) {
- var status syscall.WaitStatus
- var rusage *syscall.Rusage
- if options&WRUSAGE != 0 {
- rusage = new(syscall.Rusage)
- options ^= WRUSAGE
- }
- pid1, e := syscall.Wait4(pid, &status, options, rusage)
- if e != 0 {
- return nil, NewSyscallError("wait", e)
+ p, e := FindProcess(pid)
+ if e != nil {
+ return nil, e
}
- w = new(Waitmsg)
- w.Pid = pid1
- w.WaitStatus = status
- w.Rusage = rusage
- return w, nil
+ defer p.Release()
+ return p.Wait(options)
}
// Convert i to decimal string.
diff --git a/src/pkg/os/exec_unix.go b/src/pkg/os/exec_unix.go
new file mode 100644
index 000000000..8990d6a97
--- /dev/null
+++ b/src/pkg/os/exec_unix.go
@@ -0,0 +1,63 @@
+// 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 os
+
+import (
+ "runtime"
+ "syscall"
+)
+
+// Options for Wait.
+const (
+ WNOHANG = syscall.WNOHANG // Don't wait if no process has exited.
+ WSTOPPED = syscall.WSTOPPED // If set, status of stopped subprocesses is also reported.
+ WUNTRACED = syscall.WUNTRACED // Usually an alias for WSTOPPED.
+ WRUSAGE = 1 << 20 // Record resource usage.
+)
+
+// WRUSAGE must not be too high a bit, to avoid clashing with Linux's
+// WCLONE, WALL, and WNOTHREAD flags, which sit in the top few bits of
+// the options
+
+// Wait waits for the Process to exit or stop, and then returns a
+// Waitmsg describing its status and an Error, if any. The options
+// (WNOHANG etc.) affect the behavior of the Wait call.
+func (p *Process) Wait(options int) (w *Waitmsg, err Error) {
+ if p.Pid == -1 {
+ return nil, EINVAL
+ }
+ var status syscall.WaitStatus
+ var rusage *syscall.Rusage
+ if options&WRUSAGE != 0 {
+ rusage = new(syscall.Rusage)
+ options ^= WRUSAGE
+ }
+ pid1, e := syscall.Wait4(p.Pid, &status, options, rusage)
+ if e != 0 {
+ return nil, NewSyscallError("wait", e)
+ }
+ w = new(Waitmsg)
+ w.Pid = pid1
+ w.WaitStatus = status
+ w.Rusage = rusage
+ return w, nil
+}
+
+// Release releases any resources associated with the Process.
+func (p *Process) Release() Error {
+ // NOOP for unix.
+ p.Pid = -1
+ // no need for a finalizer anymore
+ runtime.SetFinalizer(p, nil)
+ return nil
+}
+
+// FindProcess looks for a running process by its pid.
+// The Process it returns can be used to obtain information
+// about the underlying operating system process.
+func FindProcess(pid int) (p *Process, err Error) {
+ // NOOP for unix.
+ return newProcess(pid, 0), nil
+}
diff --git a/src/pkg/os/exec_windows.go b/src/pkg/os/exec_windows.go
new file mode 100644
index 000000000..ae8ffeab2
--- /dev/null
+++ b/src/pkg/os/exec_windows.go
@@ -0,0 +1,52 @@
+// 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 os
+
+import (
+ "runtime"
+ "syscall"
+)
+
+func (p *Process) Wait(options int) (w *Waitmsg, err Error) {
+ s, e := syscall.WaitForSingleObject(int32(p.handle), syscall.INFINITE)
+ switch s {
+ case syscall.WAIT_OBJECT_0:
+ break
+ case syscall.WAIT_FAILED:
+ return nil, NewSyscallError("WaitForSingleObject", e)
+ default:
+ return nil, ErrorString("os: unexpected result from WaitForSingleObject")
+ }
+ var ec uint32
+ e = syscall.GetExitCodeProcess(uint32(p.handle), &ec)
+ if e != 0 {
+ return nil, NewSyscallError("GetExitCodeProcess", e)
+ }
+ return &Waitmsg{p.Pid, syscall.WaitStatus{s, ec}, new(syscall.Rusage)}, nil
+}
+
+func (p *Process) Release() Error {
+ if p.handle == -1 {
+ return EINVAL
+ }
+ e := syscall.CloseHandle(int32(p.handle))
+ if e != 0 {
+ return NewSyscallError("CloseHandle", e)
+ }
+ p.handle = -1
+ // no need for a finalizer anymore
+ runtime.SetFinalizer(p, nil)
+ return nil
+}
+
+func FindProcess(pid int) (p *Process, err Error) {
+ const da = syscall.STANDARD_RIGHTS_READ |
+ syscall.PROCESS_QUERY_INFORMATION | syscall.SYNCHRONIZE
+ h, e := syscall.OpenProcess(da, false, uint32(pid))
+ if e != 0 {
+ return nil, NewSyscallError("OpenProcess", e)
+ }
+ return newProcess(pid, int(h)), nil
+}
diff --git a/src/pkg/os/file_windows.go b/src/pkg/os/file_windows.go
index bf710bb67..d14c38e17 100644
--- a/src/pkg/os/file_windows.go
+++ b/src/pkg/os/file_windows.go
@@ -83,9 +83,9 @@ func (file *File) Close() Error {
}
var e int
if file.isdir() {
- _, e = syscall.FindClose(int32(file.fd))
+ e = syscall.FindClose(int32(file.fd))
} else {
- _, e = syscall.CloseHandle(int32(file.fd))
+ e = syscall.CloseHandle(int32(file.fd))
}
var err Error
if e != 0 {
@@ -100,7 +100,8 @@ func (file *File) Close() Error {
func (file *File) statFile(name string) (fi *FileInfo, err Error) {
var stat syscall.ByHandleFileInformation
- if ok, e := syscall.GetFileInformationByHandle(int32(file.fd), &stat); !ok {
+ e := syscall.GetFileInformationByHandle(int32(file.fd), &stat)
+ if e != 0 {
return nil, &PathError{"stat", file.name, Errno(e)}
}
return fileInfoFromByHandleInfo(new(FileInfo), file.name, &stat), nil
@@ -142,7 +143,7 @@ func (file *File) Readdir(count int) (fi []FileInfo, err Error) {
if di.usefirststat {
di.usefirststat = false
} else {
- _, e := syscall.FindNextFile(int32(file.fd), &di.stat.Windata)
+ e := syscall.FindNextFile(int32(file.fd), &di.stat.Windata)
if e != 0 {
if e == syscall.ERROR_NO_MORE_FILES {
break
diff --git a/src/pkg/os/inotify/inotify_linux.go b/src/pkg/os/inotify/inotify_linux.go
index 9d7a07442..96c229e7b 100644
--- a/src/pkg/os/inotify/inotify_linux.go
+++ b/src/pkg/os/inotify/inotify_linux.go
@@ -8,11 +8,11 @@ This package implements a wrapper for the Linux inotify system.
Example:
watcher, err := inotify.NewWatcher()
if err != nil {
- log.Exit(err)
+ log.Fatal(err)
}
err = watcher.Watch("/tmp")
if err != nil {
- log.Exit(err)
+ log.Fatal(err)
}
for {
select {
diff --git a/src/pkg/os/os_test.go b/src/pkg/os/os_test.go
index 49b58c83c..2ea8acdc4 100644
--- a/src/pkg/os/os_test.go
+++ b/src/pkg/os/os_test.go
@@ -427,10 +427,11 @@ func TestForkExec(t *testing.T) {
adir = "/"
expect = "/\n"
}
- pid, err := ForkExec(cmd, args, nil, adir, []*File{nil, w, Stderr})
+ p, err := StartProcess(cmd, args, nil, adir, []*File{nil, w, Stderr})
if err != nil {
- t.Fatalf("ForkExec: %v", err)
+ t.Fatalf("StartProcess: %v", err)
}
+ defer p.Release()
w.Close()
var b bytes.Buffer
@@ -440,7 +441,7 @@ func TestForkExec(t *testing.T) {
args[0] = cmd
t.Errorf("exec %q returned %q wanted %q", strings.Join(args, " "), output, expect)
}
- Wait(pid, 0)
+ p.Wait(0)
}
func checkMode(t *testing.T, path string, mode uint32) {
@@ -750,15 +751,16 @@ func run(t *testing.T, cmd []string) string {
if err != nil {
t.Fatal(err)
}
- pid, err := ForkExec("/bin/hostname", []string{"hostname"}, nil, "/", []*File{nil, w, Stderr})
+ p, err := StartProcess("/bin/hostname", []string{"hostname"}, nil, "/", []*File{nil, w, Stderr})
if err != nil {
t.Fatal(err)
}
+ defer p.Release()
w.Close()
var b bytes.Buffer
io.Copy(&b, r)
- Wait(pid, 0)
+ p.Wait(0)
output := b.String()
if n := len(output); n > 0 && output[n-1] == '\n' {
output = output[0 : n-1]