1 #define Unknown win_Unknown 2 #define UNICODE 3 #include <windows.h> 4 #include <winbase.h> 5 #include <winsock.h> 6 #undef Unknown 7 #include "dat.h" 8 #include "fns.h" 9 #include "error.h" 10 11 extern int nth2fd(HANDLE); 12 extern wchar_t *widen(char*); 13 14 /* 15 * thanks to rcsh for these. 16 * 17 * windows quoting rules - I think 18 * Words are separated by space or tab 19 * Words containing a space or tab can be quoted using " 20 * 2N backslashes + " ==> N backslashes and end quote 21 * 2N+1 backslashes + " ==> N backslashes + literal " 22 * N backslashes not followed by " ==> N backslashes 23 */ 24 static char * 25 dblquote(char *cmd, char *s) 26 { 27 int nb; 28 char *p; 29 30 for(p=s; *p; p++) 31 if(*p == ' ' || *p == '\t' || *p == '\n' || *p == '\r' || *p == '"') 32 break; 33 34 if(*p == 0){ /* easy case */ 35 strcpy(cmd, s); 36 return cmd+(p-s); 37 } 38 39 *cmd++ = '"'; 40 for(;;) { 41 for(nb=0; *s=='\\'; nb++) 42 *cmd++ = *s++; 43 44 if(*s == 0) { /* trailing backslashes -> 2N */ 45 while(nb-- > 0) 46 *cmd++ = '\\'; 47 break; 48 } 49 50 if(*s == '"') { /* literal quote -> 2N+1 backslashes */ 51 while(nb-- > 0) 52 *cmd++ = '\\'; 53 *cmd++ = '\\'; /* escape the quote */ 54 } 55 *cmd++ = *s++; 56 } 57 58 *cmd++ = '"'; 59 *cmd = 0; 60 61 return cmd; 62 } 63 64 static char * 65 ntquotedcmd(char **argv) 66 { 67 int i, n; 68 char *cmd, *p; 69 70 /* conservatively calculate length of command; 71 * backslash expansion can cause growth in dblquote(). 72 */ 73 for(i=0,n=0; argv[i]; i++) 74 n += 2*strlen(argv[i]); 75 n++; 76 77 cmd = malloc(n); 78 if(cmd == nil) 79 return nil; 80 for(i=0,p=cmd; argv[i]; i++) { 81 p = dblquote(p, argv[i]); 82 *p++ = ' '; 83 } 84 if(p != cmd) 85 p--; 86 *p = 0; 87 88 return cmd; 89 } 90 91 static HANDLE 92 exporthandle(HANDLE h, int close) 93 { 94 HANDLE cp, dh; 95 DWORD flags = DUPLICATE_SAME_ACCESS; 96 if (close) 97 flags |= DUPLICATE_CLOSE_SOURCE; 98 cp = GetCurrentProcess(); 99 if (!DuplicateHandle(cp, h, cp, &dh, DUPLICATE_SAME_ACCESS, 1, flags)) 100 return nil; 101 return dh; 102 } 103 104 /* TO DO: check that oserrstr will have the right text on error */ 105 106 void* 107 oscmd(char **args, int nice, char *dir, int *fd) 108 { 109 STARTUPINFO si; 110 SECURITY_ATTRIBUTES sec; 111 HANDLE rh, wh, eh, srh, swh, seh; 112 PROCESS_INFORMATION pinfo; 113 char *cmd; 114 wchar_t *wcmd, *wdir; 115 int prio; 116 117 wdir = nil; 118 if(dir != nil) 119 wdir = widen(dir); 120 121 cmd = ntquotedcmd(args); 122 if(cmd == nil) 123 error(Enomem); 124 125 wcmd = widen(cmd); 126 sec.nLength = sizeof(sec); 127 sec.lpSecurityDescriptor = 0; 128 sec.bInheritHandle = 0; 129 rh = wh = eh = srh = swh = seh = nil; 130 if(!CreatePipe(&rh, &swh, &sec, 0)) 131 goto Error; 132 if(!CreatePipe(&srh, &wh, &sec, 0)) 133 goto Error; 134 if(!CreatePipe(&seh, &eh, &sec, 0)) 135 goto Error; 136 rh = exporthandle(rh, 1); 137 if(rh == nil) 138 goto Error; 139 wh = exporthandle(wh, 1); 140 if(wh == nil) 141 goto Error; 142 eh = exporthandle(eh, 1); 143 if(eh == nil) 144 goto Error; 145 146 memset(&si, 0, sizeof(si)); 147 si.cb = sizeof(si); 148 si.dwFlags = STARTF_USESHOWWINDOW|STARTF_USESTDHANDLES; 149 si.wShowWindow = SW_SHOW; 150 si.hStdInput = rh; 151 si.hStdOutput = wh; 152 si.hStdError = eh; 153 154 prio = 0; 155 if(nice){ 156 prio = IDLE_PRIORITY_CLASS; 157 if(nice > 1) 158 prio |= CREATE_SUSPENDED; 159 } 160 161 /* default of nil for wpath seems to be what we want; nil for env exports our current one */ 162 if(!CreateProcess(nil/*wpath*/, wcmd, 0, 0, 1, 163 CREATE_NEW_PROCESS_GROUP|CREATE_DEFAULT_ERROR_MODE|prio, 164 0 /*env*/, wdir, &si, &pinfo)){ 165 //print("can't create process '%Q' %d\n", wcmd, GetLastError()); 166 goto Error; 167 } 168 169 fd[0] = nth2fd(swh); 170 fd[1] = nth2fd(srh); 171 fd[2] = nth2fd(seh); 172 if(fd[1] == 1 || fd[2] == 2) 173 panic("invalid mapping of handle to fd"); 174 CloseHandle(si.hStdInput); 175 CloseHandle(si.hStdOutput); 176 CloseHandle(si.hStdError); 177 178 if(prio & CREATE_SUSPENDED){ 179 if(nice > 1) 180 SetThreadPriority(pinfo.hThread, 181 nice>3? THREAD_PRIORITY_IDLE: 182 nice>2? THREAD_PRIORITY_LOWEST: 183 THREAD_PRIORITY_BELOW_NORMAL); 184 ResumeThread(pinfo.hThread); 185 } 186 CloseHandle(pinfo.hThread); 187 /* don't close process handle */ 188 free(cmd); 189 free(wcmd); 190 free(wdir); 191 return pinfo.hProcess; 192 193 Error: 194 if(rh) 195 CloseHandle(rh); 196 if(wh) 197 CloseHandle(wh); 198 if(eh) 199 CloseHandle(eh); 200 if(srh) 201 CloseHandle(srh); 202 if(swh) 203 CloseHandle(swh); 204 if(seh) 205 CloseHandle(seh); 206 free(cmd); 207 free(wcmd); 208 free(wdir); 209 return nil; 210 } 211 212 int 213 oscmdwait(void *v, char *buf, int n) 214 { 215 int status; 216 HANDLE proc = (HANDLE)v; 217 218 /* need not worry about being interrupted */ 219 if(WaitForSingleObject(proc, INFINITE) == WAIT_FAILED) 220 return -1; 221 if(!GetExitCodeProcess(proc, &status)) 222 status = 1; 223 if(status) 224 n = snprint(buf, n, "0 0 0 0 'status %d'", status); 225 else 226 n = snprint(buf, n, "0 0 0 0 ''"); 227 return n; 228 229 } 230 231 int 232 oscmdkill(void *v) 233 { 234 if(TerminateProcess((HANDLE)v, 666) == FALSE) 235 return -1; 236 return 0; 237 } 238 239 void 240 oscmdfree(void *v) 241 { 242 CloseHandle((HANDLE)v); 243 } 244