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