Home | History | Annotate | Download | only in os
      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 package os
      6 
      7 import (
      8 	"errors"
      9 	"runtime"
     10 	"sync/atomic"
     11 	"syscall"
     12 	"time"
     13 	"unsafe"
     14 )
     15 
     16 func (p *Process) wait() (ps *ProcessState, err error) {
     17 	handle := atomic.LoadUintptr(&p.handle)
     18 	s, e := syscall.WaitForSingleObject(syscall.Handle(handle), syscall.INFINITE)
     19 	switch s {
     20 	case syscall.WAIT_OBJECT_0:
     21 		break
     22 	case syscall.WAIT_FAILED:
     23 		return nil, NewSyscallError("WaitForSingleObject", e)
     24 	default:
     25 		return nil, errors.New("os: unexpected result from WaitForSingleObject")
     26 	}
     27 	var ec uint32
     28 	e = syscall.GetExitCodeProcess(syscall.Handle(handle), &ec)
     29 	if e != nil {
     30 		return nil, NewSyscallError("GetExitCodeProcess", e)
     31 	}
     32 	var u syscall.Rusage
     33 	e = syscall.GetProcessTimes(syscall.Handle(handle), &u.CreationTime, &u.ExitTime, &u.KernelTime, &u.UserTime)
     34 	if e != nil {
     35 		return nil, NewSyscallError("GetProcessTimes", e)
     36 	}
     37 	p.setDone()
     38 	// NOTE(brainman): It seems that sometimes process is not dead
     39 	// when WaitForSingleObject returns. But we do not know any
     40 	// other way to wait for it. Sleeping for a while seems to do
     41 	// the trick sometimes. So we will sleep and smell the roses.
     42 	defer time.Sleep(5 * time.Millisecond)
     43 	defer p.Release()
     44 	return &ProcessState{p.Pid, syscall.WaitStatus{ExitCode: ec}, &u}, nil
     45 }
     46 
     47 func terminateProcess(pid, exitcode int) error {
     48 	h, e := syscall.OpenProcess(syscall.PROCESS_TERMINATE, false, uint32(pid))
     49 	if e != nil {
     50 		return NewSyscallError("OpenProcess", e)
     51 	}
     52 	defer syscall.CloseHandle(h)
     53 	e = syscall.TerminateProcess(h, uint32(exitcode))
     54 	return NewSyscallError("TerminateProcess", e)
     55 }
     56 
     57 func (p *Process) signal(sig Signal) error {
     58 	handle := atomic.LoadUintptr(&p.handle)
     59 	if handle == uintptr(syscall.InvalidHandle) {
     60 		return syscall.EINVAL
     61 	}
     62 	if p.done() {
     63 		return errors.New("os: process already finished")
     64 	}
     65 	if sig == Kill {
     66 		return terminateProcess(p.Pid, 1)
     67 	}
     68 	// TODO(rsc): Handle Interrupt too?
     69 	return syscall.Errno(syscall.EWINDOWS)
     70 }
     71 
     72 func (p *Process) release() error {
     73 	handle := atomic.LoadUintptr(&p.handle)
     74 	if handle == uintptr(syscall.InvalidHandle) {
     75 		return syscall.EINVAL
     76 	}
     77 	e := syscall.CloseHandle(syscall.Handle(handle))
     78 	if e != nil {
     79 		return NewSyscallError("CloseHandle", e)
     80 	}
     81 	atomic.StoreUintptr(&p.handle, uintptr(syscall.InvalidHandle))
     82 	// no need for a finalizer anymore
     83 	runtime.SetFinalizer(p, nil)
     84 	return nil
     85 }
     86 
     87 func findProcess(pid int) (p *Process, err error) {
     88 	const da = syscall.STANDARD_RIGHTS_READ |
     89 		syscall.PROCESS_QUERY_INFORMATION | syscall.SYNCHRONIZE
     90 	h, e := syscall.OpenProcess(da, false, uint32(pid))
     91 	if e != nil {
     92 		return nil, NewSyscallError("OpenProcess", e)
     93 	}
     94 	return newProcess(pid, uintptr(h)), nil
     95 }
     96 
     97 func init() {
     98 	var argc int32
     99 	cmd := syscall.GetCommandLine()
    100 	argv, e := syscall.CommandLineToArgv(cmd, &argc)
    101 	if e != nil {
    102 		return
    103 	}
    104 	defer syscall.LocalFree(syscall.Handle(uintptr(unsafe.Pointer(argv))))
    105 	Args = make([]string, argc)
    106 	for i, v := range (*argv)[:argc] {
    107 		Args[i] = string(syscall.UTF16ToString((*v)[:]))
    108 	}
    109 }
    110 
    111 func ftToDuration(ft *syscall.Filetime) time.Duration {
    112 	n := int64(ft.HighDateTime)<<32 + int64(ft.LowDateTime) // in 100-nanosecond intervals
    113 	return time.Duration(n*100) * time.Nanosecond
    114 }
    115 
    116 func (p *ProcessState) userTime() time.Duration {
    117 	return ftToDuration(&p.rusage.UserTime)
    118 }
    119 
    120 func (p *ProcessState) systemTime() time.Duration {
    121 	return ftToDuration(&p.rusage.KernelTime)
    122 }
    123