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