exec_windows.go (5346B)
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 // Fork, exec, wait, etc. 6 7 package windows 8 9 import ( 10 errorspkg "errors" 11 "unsafe" 12 13 "golang.org/x/sys/internal/unsafeheader" 14 ) 15 16 // EscapeArg rewrites command line argument s as prescribed 17 // in http://msdn.microsoft.com/en-us/library/ms880421. 18 // This function returns "" (2 double quotes) if s is empty. 19 // Alternatively, these transformations are done: 20 // - every back slash (\) is doubled, but only if immediately 21 // followed by double quote ("); 22 // - every double quote (") is escaped by back slash (\); 23 // - finally, s is wrapped with double quotes (arg -> "arg"), 24 // but only if there is space or tab inside s. 25 func EscapeArg(s string) string { 26 if len(s) == 0 { 27 return "\"\"" 28 } 29 n := len(s) 30 hasSpace := false 31 for i := 0; i < len(s); i++ { 32 switch s[i] { 33 case '"', '\\': 34 n++ 35 case ' ', '\t': 36 hasSpace = true 37 } 38 } 39 if hasSpace { 40 n += 2 41 } 42 if n == len(s) { 43 return s 44 } 45 46 qs := make([]byte, n) 47 j := 0 48 if hasSpace { 49 qs[j] = '"' 50 j++ 51 } 52 slashes := 0 53 for i := 0; i < len(s); i++ { 54 switch s[i] { 55 default: 56 slashes = 0 57 qs[j] = s[i] 58 case '\\': 59 slashes++ 60 qs[j] = s[i] 61 case '"': 62 for ; slashes > 0; slashes-- { 63 qs[j] = '\\' 64 j++ 65 } 66 qs[j] = '\\' 67 j++ 68 qs[j] = s[i] 69 } 70 j++ 71 } 72 if hasSpace { 73 for ; slashes > 0; slashes-- { 74 qs[j] = '\\' 75 j++ 76 } 77 qs[j] = '"' 78 j++ 79 } 80 return string(qs[:j]) 81 } 82 83 // ComposeCommandLine escapes and joins the given arguments suitable for use as a Windows command line, 84 // in CreateProcess's CommandLine argument, CreateService/ChangeServiceConfig's BinaryPathName argument, 85 // or any program that uses CommandLineToArgv. 86 func ComposeCommandLine(args []string) string { 87 var commandLine string 88 for i := range args { 89 if i > 0 { 90 commandLine += " " 91 } 92 commandLine += EscapeArg(args[i]) 93 } 94 return commandLine 95 } 96 97 // DecomposeCommandLine breaks apart its argument command line into unescaped parts using CommandLineToArgv, 98 // as gathered from GetCommandLine, QUERY_SERVICE_CONFIG's BinaryPathName argument, or elsewhere that 99 // command lines are passed around. 100 func DecomposeCommandLine(commandLine string) ([]string, error) { 101 if len(commandLine) == 0 { 102 return []string{}, nil 103 } 104 var argc int32 105 argv, err := CommandLineToArgv(StringToUTF16Ptr(commandLine), &argc) 106 if err != nil { 107 return nil, err 108 } 109 defer LocalFree(Handle(unsafe.Pointer(argv))) 110 var args []string 111 for _, v := range (*argv)[:argc] { 112 args = append(args, UTF16ToString((*v)[:])) 113 } 114 return args, nil 115 } 116 117 func CloseOnExec(fd Handle) { 118 SetHandleInformation(Handle(fd), HANDLE_FLAG_INHERIT, 0) 119 } 120 121 // FullPath retrieves the full path of the specified file. 122 func FullPath(name string) (path string, err error) { 123 p, err := UTF16PtrFromString(name) 124 if err != nil { 125 return "", err 126 } 127 n := uint32(100) 128 for { 129 buf := make([]uint16, n) 130 n, err = GetFullPathName(p, uint32(len(buf)), &buf[0], nil) 131 if err != nil { 132 return "", err 133 } 134 if n <= uint32(len(buf)) { 135 return UTF16ToString(buf[:n]), nil 136 } 137 } 138 } 139 140 // NewProcThreadAttributeList allocates a new ProcThreadAttributeListContainer, with the requested maximum number of attributes. 141 func NewProcThreadAttributeList(maxAttrCount uint32) (*ProcThreadAttributeListContainer, error) { 142 var size uintptr 143 err := initializeProcThreadAttributeList(nil, maxAttrCount, 0, &size) 144 if err != ERROR_INSUFFICIENT_BUFFER { 145 if err == nil { 146 return nil, errorspkg.New("unable to query buffer size from InitializeProcThreadAttributeList") 147 } 148 return nil, err 149 } 150 // size is guaranteed to be ≥1 by InitializeProcThreadAttributeList. 151 al := &ProcThreadAttributeListContainer{data: (*ProcThreadAttributeList)(unsafe.Pointer(&make([]byte, size)[0]))} 152 err = initializeProcThreadAttributeList(al.data, maxAttrCount, 0, &size) 153 if err != nil { 154 return nil, err 155 } 156 return al, err 157 } 158 159 // Update modifies the ProcThreadAttributeList using UpdateProcThreadAttribute. 160 // Note that the value passed to this function will be copied into memory 161 // allocated by LocalAlloc, the contents of which should not contain any 162 // Go-managed pointers, even if the passed value itself is a Go-managed 163 // pointer. 164 func (al *ProcThreadAttributeListContainer) Update(attribute uintptr, value unsafe.Pointer, size uintptr) error { 165 alloc, err := LocalAlloc(LMEM_FIXED, uint32(size)) 166 if err != nil { 167 return err 168 } 169 var src, dst []byte 170 hdr := (*unsafeheader.Slice)(unsafe.Pointer(&src)) 171 hdr.Data = value 172 hdr.Cap = int(size) 173 hdr.Len = int(size) 174 hdr = (*unsafeheader.Slice)(unsafe.Pointer(&dst)) 175 hdr.Data = unsafe.Pointer(alloc) 176 hdr.Cap = int(size) 177 hdr.Len = int(size) 178 copy(dst, src) 179 al.heapAllocations = append(al.heapAllocations, alloc) 180 return updateProcThreadAttribute(al.data, 0, attribute, unsafe.Pointer(alloc), size, nil, nil) 181 } 182 183 // Delete frees ProcThreadAttributeList's resources. 184 func (al *ProcThreadAttributeListContainer) Delete() { 185 deleteProcThreadAttributeList(al.data) 186 for i := range al.heapAllocations { 187 LocalFree(Handle(al.heapAllocations[i])) 188 } 189 al.heapAllocations = nil 190 } 191 192 // List returns the actual ProcThreadAttributeList to be passed to StartupInfoEx. 193 func (al *ProcThreadAttributeListContainer) List() *ProcThreadAttributeList { 194 return al.data 195 }