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 7package windows 8 9import ( 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. 25func 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. 86func 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. 100func 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 117func CloseOnExec(fd Handle) { 118 SetHandleInformation(Handle(fd), HANDLE_FLAG_INHERIT, 0) 119} 120 121// FullPath retrieves the full path of the specified file. 122func 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. 141func 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. 164func (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. 184func (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. 193func (al *ProcThreadAttributeListContainer) List() *ProcThreadAttributeList { 194 return al.data 195} 196