...

Source file src/os/exec_windows.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	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.
    42		// See https://golang.org/issue/25965 for details.
    43		defer time.Sleep(5 * time.Millisecond)
    44		defer p.Release()
    45		return &ProcessState{p.Pid, syscall.WaitStatus{ExitCode: ec}, &u}, nil
    46	}
    47	
    48	func terminateProcess(pid, exitcode int) error {
    49		h, e := syscall.OpenProcess(syscall.PROCESS_TERMINATE, false, uint32(pid))
    50		if e != nil {
    51			return NewSyscallError("OpenProcess", e)
    52		}
    53		defer syscall.CloseHandle(h)
    54		e = syscall.TerminateProcess(h, uint32(exitcode))
    55		return NewSyscallError("TerminateProcess", e)
    56	}
    57	
    58	func (p *Process) signal(sig Signal) error {
    59		handle := atomic.LoadUintptr(&p.handle)
    60		if handle == uintptr(syscall.InvalidHandle) {
    61			return syscall.EINVAL
    62		}
    63		if p.done() {
    64			return errors.New("os: process already finished")
    65		}
    66		if sig == Kill {
    67			err := terminateProcess(p.Pid, 1)
    68			runtime.KeepAlive(p)
    69			return err
    70		}
    71		// TODO(rsc): Handle Interrupt too?
    72		return syscall.Errno(syscall.EWINDOWS)
    73	}
    74	
    75	func (p *Process) release() error {
    76		handle := atomic.LoadUintptr(&p.handle)
    77		if handle == uintptr(syscall.InvalidHandle) {
    78			return syscall.EINVAL
    79		}
    80		e := syscall.CloseHandle(syscall.Handle(handle))
    81		if e != nil {
    82			return NewSyscallError("CloseHandle", e)
    83		}
    84		atomic.StoreUintptr(&p.handle, uintptr(syscall.InvalidHandle))
    85		// no need for a finalizer anymore
    86		runtime.SetFinalizer(p, nil)
    87		return nil
    88	}
    89	
    90	func findProcess(pid int) (p *Process, err error) {
    91		const da = syscall.STANDARD_RIGHTS_READ |
    92			syscall.PROCESS_QUERY_INFORMATION | syscall.SYNCHRONIZE
    93		h, e := syscall.OpenProcess(da, false, uint32(pid))
    94		if e != nil {
    95			return nil, NewSyscallError("OpenProcess", e)
    96		}
    97		return newProcess(pid, uintptr(h)), nil
    98	}
    99	
   100	func init() {
   101		p := syscall.GetCommandLine()
   102		cmd := syscall.UTF16ToString((*[0xffff]uint16)(unsafe.Pointer(p))[:])
   103		if len(cmd) == 0 {
   104			arg0, _ := Executable()
   105			Args = []string{arg0}
   106		} else {
   107			Args = commandLineToArgv(cmd)
   108		}
   109	}
   110	
   111	// appendBSBytes appends n '\\' bytes to b and returns the resulting slice.
   112	func appendBSBytes(b []byte, n int) []byte {
   113		for ; n > 0; n-- {
   114			b = append(b, '\\')
   115		}
   116		return b
   117	}
   118	
   119	// readNextArg splits command line string cmd into next
   120	// argument and command line remainder.
   121	func readNextArg(cmd string) (arg []byte, rest string) {
   122		var b []byte
   123		var inquote bool
   124		var nslash int
   125		for ; len(cmd) > 0; cmd = cmd[1:] {
   126			c := cmd[0]
   127			switch c {
   128			case ' ', '\t':
   129				if !inquote {
   130					return appendBSBytes(b, nslash), cmd[1:]
   131				}
   132			case '"':
   133				b = appendBSBytes(b, nslash/2)
   134				if nslash%2 == 0 {
   135					// use "Prior to 2008" rule from
   136					// http://daviddeley.com/autohotkey/parameters/parameters.htm
   137					// section 5.2 to deal with double double quotes
   138					if inquote && len(cmd) > 1 && cmd[1] == '"' {
   139						b = append(b, c)
   140						cmd = cmd[1:]
   141					}
   142					inquote = !inquote
   143				} else {
   144					b = append(b, c)
   145				}
   146				nslash = 0
   147				continue
   148			case '\\':
   149				nslash++
   150				continue
   151			}
   152			b = appendBSBytes(b, nslash)
   153			nslash = 0
   154			b = append(b, c)
   155		}
   156		return appendBSBytes(b, nslash), ""
   157	}
   158	
   159	// commandLineToArgv splits a command line into individual argument
   160	// strings, following the Windows conventions documented
   161	// at http://daviddeley.com/autohotkey/parameters/parameters.htm#WINARGV
   162	func commandLineToArgv(cmd string) []string {
   163		var args []string
   164		for len(cmd) > 0 {
   165			if cmd[0] == ' ' || cmd[0] == '\t' {
   166				cmd = cmd[1:]
   167				continue
   168			}
   169			var arg []byte
   170			arg, cmd = readNextArg(cmd)
   171			args = append(args, string(arg))
   172		}
   173		return args
   174	}
   175	
   176	func ftToDuration(ft *syscall.Filetime) time.Duration {
   177		n := int64(ft.HighDateTime)<<32 + int64(ft.LowDateTime) // in 100-nanosecond intervals
   178		return time.Duration(n*100) * time.Nanosecond
   179	}
   180	
   181	func (p *ProcessState) userTime() time.Duration {
   182		return ftToDuration(&p.rusage.UserTime)
   183	}
   184	
   185	func (p *ProcessState) systemTime() time.Duration {
   186		return ftToDuration(&p.rusage.KernelTime)
   187	}
   188	

View as plain text