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 *rpfd, int *wpfd) 108 { 109 STARTUPINFO si; 110 SECURITY_ATTRIBUTES sec; 111 HANDLE rh, wh, srh, swh; 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 if(!CreatePipe(&rh, &swh, &sec, 0)) { 130 print("can't create pipe\n"); 131 free(cmd); 132 free(wcmd); 133 free(wdir); 134 return nil; 135 } 136 if(!CreatePipe(&srh, &wh, &sec, 0)) { 137 print("can't create pipe\n"); 138 CloseHandle(rh); 139 CloseHandle(swh); 140 free(cmd); 141 free(wcmd); 142 free(wdir); 143 return nil; 144 } 145 rh = exporthandle(rh, 1); 146 wh = exporthandle(wh, 1); 147 if (rh == nil || wh == nil) { 148 print("can't dup pipes\n"); 149 CloseHandle(rh); 150 CloseHandle(swh); 151 CloseHandle(wh); 152 CloseHandle(srh); 153 free(cmd); 154 free(wcmd); 155 free(wdir); 156 return nil; 157 } 158 159 memset(&si, 0, sizeof(si)); 160 si.cb = sizeof(si); 161 si.dwFlags = STARTF_USESHOWWINDOW|STARTF_USESTDHANDLES; 162 si.wShowWindow = SW_SHOW; 163 si.hStdInput = rh; 164 si.hStdOutput = wh; 165 si.hStdError = exporthandle(wh, 0); 166 167 prio = 0; 168 if(nice){ 169 prio = IDLE_PRIORITY_CLASS; 170 if(nice > 1) 171 prio |= CREATE_SUSPENDED; 172 } 173 174 /* default of nil for wpath seems to be what we want; nil for env exports our current one */ 175 if(!CreateProcess(nil/*wpath*/, wcmd, 0, 0, 1, 176 CREATE_NEW_PROCESS_GROUP|CREATE_DEFAULT_ERROR_MODE|prio, 177 0 /*env*/, wdir, &si, &pinfo)){ 178 print("can't create process '%Q' %d\n", wcmd, GetLastError()); 179 CloseHandle(si.hStdInput); 180 CloseHandle(swh); 181 CloseHandle(si.hStdOutput); 182 CloseHandle(si.hStdError); 183 CloseHandle(srh); 184 free(cmd); 185 free(wcmd); 186 free(wdir); 187 return nil; 188 } 189 190 *rpfd = nth2fd(srh); 191 *wpfd = nth2fd(swh); 192 if(*wpfd == 1 || *wpfd == 2) 193 panic("invalid mapping of handle to fd"); 194 CloseHandle(si.hStdInput); 195 CloseHandle(si.hStdOutput); 196 CloseHandle(si.hStdError); 197 198 if(prio & CREATE_SUSPENDED){ 199 if(nice > 1) 200 SetThreadPriority(pinfo.hThread, 201 nice>3? THREAD_PRIORITY_IDLE: 202 nice>2? THREAD_PRIORITY_LOWEST: 203 THREAD_PRIORITY_BELOW_NORMAL); 204 ResumeThread(pinfo.hThread); 205 } 206 CloseHandle(pinfo.hThread); 207 /* don't close process handle */ 208 free(cmd); 209 free(wcmd); 210 free(wdir); 211 return pinfo.hProcess; 212 } 213 214 int 215 oscmdwait(void *v, char *buf, int n) 216 { 217 int status; 218 HANDLE proc = (HANDLE)v; 219 220 /* need not worry about being interrupted */ 221 if(WaitForSingleObject(proc, INFINITE) == WAIT_FAILED) 222 return -1; 223 if(!GetExitCodeProcess(proc, &status)) 224 status = 1; 225 if(status) 226 n = snprint(buf, n, "0 0 0 0 'status %d'", status); 227 else 228 n = snprint(buf, n, "0 0 0 0 ''"); 229 return n; 230 231 } 232 233 int 234 oscmdkill(void *v) 235 { 236 if(TerminateProcess((HANDLE)v, 666) == FALSE) 237 return -1; 238 return 0; 239 } 240 241 void 242 oscmdfree(void *v) 243 { 244 CloseHandle((HANDLE)v); 245 } 246