github.com/bazelbuild/bazel-watcher@v0.25.2/internal/ibazel/process_group/process_group_windows.go (about) 1 // Copyright 2018 The Bazel Authors. All rights reserved. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package process_group 16 17 import ( 18 "bytes" 19 "errors" 20 "os/exec" 21 "syscall" 22 "unsafe" 23 ) 24 25 type winProcessGroup struct { 26 root *exec.Cmd 27 job syscall.Handle 28 ioport syscall.Handle 29 } 30 31 // Command creates a new ProcessGroup with a root command specified by the 32 // arguments. 33 func Command(name string, arg ...string) ProcessGroup { 34 root := exec.Command(name, arg...) 35 root.SysProcAttr = &syscall.SysProcAttr{CreationFlags: createSuspended} 36 return &winProcessGroup{root, syscall.Handle(0), syscall.Handle(0)} 37 } 38 39 func (pg *winProcessGroup) RootProcess() *exec.Cmd { 40 return pg.root 41 } 42 43 func (pg *winProcessGroup) Start() error { 44 if pg.job != 0 { 45 return errors.New("job already started") 46 } 47 48 err := pg.root.Start() 49 if err != nil { 50 return err 51 } 52 53 pg.job, err = createJobObject() 54 if err != nil { 55 return err 56 } 57 58 pg.ioport, err = syscall.CreateIoCompletionPort(syscall.InvalidHandle, syscall.Handle(0), 0, 1) 59 if err != nil { 60 return err 61 } 62 63 port := jobObjectAssociationCompletionPort{ 64 CompletionKey: pg.job, 65 CompletionPort: pg.ioport, 66 } 67 68 err = setInformationJobObject(pg.job, jobObjectAssociateCompletionPortInformation, uintptr(unsafe.Pointer(&port)), unsafe.Sizeof(port)) 69 if err != nil { 70 return err 71 } 72 73 process, err := syscall.OpenProcess(processAllAccess, false, uint32(pg.root.Process.Pid)) 74 if err != nil { 75 return err 76 } 77 78 err = assignProcessToJobObject(pg.job, process) 79 if err != nil { 80 return err 81 } 82 83 err = ntResumeProcess(process) 84 if err != nil { 85 return err 86 } 87 88 syscall.CloseHandle(process) 89 90 return nil 91 } 92 93 func (pg *winProcessGroup) Signal(signum syscall.Signal) error { 94 // signum is ignored on Windows as there's no support for signals 95 if pg.job == 0 { 96 return errors.New("job not started") 97 } 98 99 err := terminateJobObject(pg.job, 0) 100 if err != nil { 101 return err 102 } 103 104 return nil 105 } 106 107 func (pg *winProcessGroup) Wait() error { 108 var code uint32 109 var key uint32 110 var op *syscall.Overlapped 111 112 for { 113 err := syscall.GetQueuedCompletionStatus(pg.ioport, &code, &key, &op, syscall.INFINITE) 114 if err != nil { 115 return err 116 } 117 if key == uint32(pg.job) && code == jobObjectMsgActiveProcessZero { 118 break 119 } 120 } 121 122 return nil 123 } 124 125 func (pg *winProcessGroup) Close() error { 126 err := syscall.CloseHandle(pg.job) 127 if err != nil { 128 return err 129 } 130 131 err = syscall.CloseHandle(pg.ioport) 132 if err != nil { 133 return err 134 } 135 136 return nil 137 } 138 139 func (pg *winProcessGroup) Run() error { 140 if err := pg.Start(); err != nil { 141 return err 142 } 143 return pg.Wait() 144 } 145 146 func (pg *winProcessGroup) CombinedOutput() ([]byte, error) { 147 var b bytes.Buffer 148 pg.root.Stdout = &b 149 pg.root.Stderr = &b 150 err := pg.Run() 151 return b.Bytes(), err 152 }