diff options
Diffstat (limited to 'src/pkg/os')
-rw-r--r-- | src/pkg/os/Makefile | 4 | ||||
-rw-r--r-- | src/pkg/os/env_windows.go | 4 | ||||
-rw-r--r-- | src/pkg/os/exec.go | 61 | ||||
-rw-r--r-- | src/pkg/os/exec_unix.go | 63 | ||||
-rw-r--r-- | src/pkg/os/exec_windows.go | 52 | ||||
-rw-r--r-- | src/pkg/os/file_windows.go | 9 | ||||
-rw-r--r-- | src/pkg/os/inotify/inotify_linux.go | 4 | ||||
-rw-r--r-- | src/pkg/os/os_test.go | 12 |
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] |