1 /* $NetBSD: simple_exec_w32.c,v 1.1.1.2 2014/04/24 12:45:52 pettai Exp $ */ 2 3 /*********************************************************************** 4 * Copyright (c) 2009, Secure Endpoints Inc. 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 11 * - Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 14 * - Redistributions in binary form must reproduce the above copyright 15 * notice, this list of conditions and the following disclaimer in 16 * the documentation and/or other materials provided with the 17 * distribution. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 20 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 21 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 22 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 23 * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 24 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 25 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 26 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 28 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 29 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 30 * OF THE POSSIBILITY OF SUCH DAMAGE. 31 * 32 **********************************************************************/ 33 34 #include <config.h> 35 36 #include <krb5/roken.h> 37 #include <strsafe.h> 38 39 #ifndef _WIN32 40 #error This implementation is Windows specific. 41 #endif 42 43 /** 44 * wait_for_process_timed waits for a process to terminate or until a 45 * specified timeout occurs. 46 * 47 * @param[in] pid Process id for the monitored process 48 49 * @param[in] func Timeout callback function. When the wait times out, 50 * the callback function is called. The possible return values 51 * from the callback function are: 52 * 53 * - ((time_t) -2) Exit loop without killing child and return SE_E_EXECTIMEOUT. 54 * - ((time_t) -1) Kill child with SIGTERM and wait for child to exit. 55 * - 0 Don't timeout again 56 * - n Seconds to next timeout 57 * 58 * @param[in] ptr Optional parameter for func() 59 * 60 * @param[in] timeout Seconds to first timeout. 61 * 62 * @retval SE_E_UNSPECIFIED Unspecified system error 63 * @retval SE_E_FORKFAILED Fork failure (not applicable for _WIN32 targets) 64 * @retval SE_E_WAITPIDFAILED waitpid errors 65 * @retval SE_E_EXECTIMEOUT exec timeout 66 * @retval 0 <= Return value from subprocess 67 * @retval SE_E_NOTFOUND The program coudln't be found 68 * @retval 128- The signal that killed the subprocess +128. 69 */ 70 ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL 71 wait_for_process_timed(pid_t pid, time_t (*func)(void *), 72 void *ptr, time_t timeout) 73 { 74 HANDLE hProcess; 75 DWORD wrv = 0; 76 DWORD dtimeout; 77 int rv = 0; 78 79 hProcess = OpenProcess(SYNCHRONIZE, FALSE, pid); 80 81 if (hProcess == NULL) { 82 return SE_E_WAITPIDFAILED; 83 } 84 85 dtimeout = (DWORD) ((timeout == 0)? INFINITE: timeout * 1000); 86 87 do { 88 wrv = WaitForSingleObject(hProcess, dtimeout); 89 90 if (wrv == WAIT_OBJECT_0) { 91 92 DWORD prv = 0; 93 94 GetExitCodeProcess(hProcess, &prv); 95 rv = (int) prv; 96 break; 97 98 } else if (wrv == WAIT_TIMEOUT) { 99 100 if (func == NULL) 101 continue; 102 103 timeout = (*func)(ptr); 104 105 if (timeout == (time_t)-1) { 106 107 if (TerminateProcess(hProcess, 128 + 9)) { 108 dtimeout = INFINITE; 109 continue; 110 } 111 rv = SE_E_UNSPECIFIED; 112 break; 113 114 } else if (timeout == (time_t) -2) { 115 116 rv = SE_E_EXECTIMEOUT; 117 break; 118 119 } else { 120 121 dtimeout = (DWORD) ((timeout == 0)? INFINITE: timeout * 1000); 122 continue; 123 124 } 125 126 } else { 127 128 rv = SE_E_UNSPECIFIED; 129 break; 130 131 } 132 133 } while(TRUE); 134 135 CloseHandle(hProcess); 136 137 return rv; 138 } 139 140 ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL 141 wait_for_process(pid_t pid) 142 { 143 return wait_for_process_timed(pid, NULL, NULL, 0); 144 } 145 146 static char * 147 collect_commandline(const char * fn, va_list * ap) 148 { 149 size_t len = 0; 150 size_t alloc_len = 0; 151 const char * s; 152 char * cmd = NULL; 153 154 for (s = fn; s; s = (char *) va_arg(*ap, char *)) { 155 size_t cmp_len; 156 int need_quote = FALSE; 157 158 if (FAILED(StringCchLength(s, MAX_PATH, &cmp_len))) { 159 if (cmd) 160 free(cmd); 161 return NULL; 162 } 163 164 if (cmp_len == 0) 165 continue; 166 167 if (strchr(s, ' ') && /* need to quote any component that 168 has embedded spaces, but not if 169 they are already quoted. */ 170 s[0] != '"' && 171 s[cmp_len - 1] != '"') { 172 need_quote = TRUE; 173 cmp_len += 2 * sizeof(char); 174 } 175 176 if (s != fn) 177 cmp_len += 1 * sizeof(char); 178 179 if (alloc_len < len + cmp_len + 1) { 180 char * nc; 181 182 alloc_len += ((len + cmp_len - alloc_len) / MAX_PATH + 1) * MAX_PATH; 183 nc = (char *) realloc(cmd, alloc_len * sizeof(char)); 184 if (nc == NULL) { 185 if (cmd) 186 free(cmd); 187 return NULL; 188 } 189 } 190 191 if (cmd == NULL) 192 return NULL; 193 194 if (s != fn) 195 cmd[len++] = ' '; 196 197 if (need_quote) { 198 StringCchPrintf(cmd + len, alloc_len - len, "\"%s\"", s); 199 } else { 200 StringCchCopy(cmd + len, alloc_len - len, s); 201 } 202 203 len += cmp_len; 204 } 205 206 return cmd; 207 } 208 209 ROKEN_LIB_FUNCTION pid_t ROKEN_LIB_CALL 210 pipe_execv(FILE **stdin_fd, FILE **stdout_fd, FILE **stderr_fd, 211 const char *file, ...) 212 { 213 HANDLE hOut_r = NULL; 214 HANDLE hOut_w = NULL; 215 HANDLE hIn_r = NULL; 216 HANDLE hIn_w = NULL; 217 HANDLE hErr_r = NULL; 218 HANDLE hErr_w = NULL; 219 220 SECURITY_ATTRIBUTES sa; 221 STARTUPINFO si; 222 PROCESS_INFORMATION pi; 223 224 char * commandline = NULL; 225 226 pid_t rv = (pid_t) -1; 227 228 { 229 va_list ap; 230 231 va_start(ap, file); 232 commandline = collect_commandline(file, &ap); 233 234 if (commandline == NULL) 235 return rv; 236 } 237 238 ZeroMemory(&si, sizeof(si)); 239 ZeroMemory(&pi, sizeof(pi)); 240 ZeroMemory(&sa, sizeof(sa)); 241 242 pi.hProcess = NULL; 243 pi.hThread = NULL; 244 245 sa.nLength = sizeof(sa); 246 sa.bInheritHandle = TRUE; 247 sa.lpSecurityDescriptor = NULL; 248 249 if ((stdout_fd && !CreatePipe(&hOut_r, &hOut_w, &sa, 0 /* Use default */)) || 250 251 (stdin_fd && !CreatePipe(&hIn_r, &hIn_w, &sa, 0)) || 252 253 (stderr_fd && !CreatePipe(&hErr_r, &hErr_w, &sa, 0)) || 254 255 (!stdout_fd && (hOut_w = CreateFile("CON", GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE, 256 &sa, OPEN_EXISTING, 0, NULL)) == INVALID_HANDLE_VALUE) || 257 258 (!stdin_fd && (hIn_r = CreateFile("CON",GENERIC_READ, FILE_SHARE_READ|FILE_SHARE_WRITE, 259 &sa, OPEN_EXISTING, 0, NULL)) == INVALID_HANDLE_VALUE) || 260 261 (!stderr_fd && (hErr_w = CreateFile("CON", GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE, 262 &sa, OPEN_EXISTING, 0, NULL)) == INVALID_HANDLE_VALUE)) 263 264 goto _exit; 265 266 /* We don't want the child processes inheriting these */ 267 if (hOut_r) 268 SetHandleInformation(hOut_r, HANDLE_FLAG_INHERIT, FALSE); 269 270 if (hIn_w) 271 SetHandleInformation(hIn_w, HANDLE_FLAG_INHERIT, FALSE); 272 273 if (hErr_r) 274 SetHandleInformation(hErr_r, HANDLE_FLAG_INHERIT, FALSE); 275 276 si.cb = sizeof(si); 277 si.lpReserved = NULL; 278 si.lpDesktop = NULL; 279 si.lpTitle = NULL; 280 si.dwFlags = STARTF_USESTDHANDLES; 281 si.hStdInput = hIn_r; 282 si.hStdOutput = hOut_w; 283 si.hStdError = hErr_w; 284 285 if (!CreateProcess(file, commandline, NULL, NULL, 286 TRUE, /* bInheritHandles */ 287 CREATE_NO_WINDOW, /* dwCreationFlags */ 288 NULL, /* lpEnvironment */ 289 NULL, /* lpCurrentDirectory */ 290 &si, 291 &pi)) { 292 293 rv = (pid_t) (GetLastError() == ERROR_FILE_NOT_FOUND)? 127 : -1; 294 goto _exit; 295 } 296 297 if (stdin_fd) { 298 *stdin_fd = _fdopen(_open_osfhandle((intptr_t) hIn_w, 0), "wb"); 299 if (*stdin_fd) 300 hIn_w = NULL; 301 } 302 303 if (stdout_fd) { 304 *stdout_fd = _fdopen(_open_osfhandle((intptr_t) hOut_r, _O_RDONLY), "rb"); 305 if (*stdout_fd) 306 hOut_r = NULL; 307 } 308 309 if (stderr_fd) { 310 *stderr_fd = _fdopen(_open_osfhandle((intptr_t) hErr_r, _O_RDONLY), "rb"); 311 if (*stderr_fd) 312 hErr_r = NULL; 313 } 314 315 rv = (pid_t) pi.dwProcessId; 316 317 _exit: 318 319 if (pi.hProcess) CloseHandle(pi.hProcess); 320 321 if (pi.hThread) CloseHandle(pi.hThread); 322 323 if (hIn_r) CloseHandle(hIn_r); 324 325 if (hIn_w) CloseHandle(hIn_w); 326 327 if (hOut_r) CloseHandle(hOut_r); 328 329 if (hOut_w) CloseHandle(hOut_w); 330 331 if (hErr_r) CloseHandle(hErr_r); 332 333 if (hErr_w) CloseHandle(hErr_w); 334 335 return rv; 336 } 337 338 339 ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL 340 simple_execvp_timed(const char *file, char *const args[], 341 time_t (*func)(void *), void *ptr, time_t timeout) 342 { 343 intptr_t hp; 344 int rv; 345 346 hp = spawnvp(_P_NOWAIT, file, args); 347 348 if (hp == -1) 349 return (errno == ENOENT)? 127: 126; 350 else if (hp == 0) 351 return 0; 352 353 rv = wait_for_process_timed(GetProcessId((HANDLE) hp), func, ptr, timeout); 354 355 CloseHandle((HANDLE) hp); 356 357 return rv; 358 } 359 360 361 ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL 362 simple_execvp(const char *file, char *const args[]) 363 { 364 return simple_execvp_timed(file, args, NULL, NULL, 0); 365 } 366 367 368 ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL 369 simple_execve_timed(const char *file, char *const args[], char *const envp[], 370 time_t (*func)(void *), void *ptr, time_t timeout) 371 { 372 intptr_t hp; 373 int rv; 374 375 hp = spawnve(_P_NOWAIT, file, args, envp); 376 377 if (hp == -1) 378 return (errno == ENOENT)? 127: 126; 379 else if (hp == 0) 380 return 0; 381 382 rv = wait_for_process_timed(GetProcessId((HANDLE) hp), func, ptr, timeout); 383 384 CloseHandle((HANDLE) hp); 385 386 return rv; 387 } 388 389 390 ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL 391 simple_execve(const char *file, char *const args[], char *const envp[]) 392 { 393 return simple_execve_timed(file, args, envp, NULL, NULL, 0); 394 } 395 396 397 ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL 398 simple_execlp(const char *file, ...) 399 { 400 va_list ap; 401 char **argv; 402 int ret; 403 404 va_start(ap, file); 405 argv = vstrcollect(&ap); 406 va_end(ap); 407 if(argv == NULL) 408 return SE_E_UNSPECIFIED; 409 ret = simple_execvp(file, argv); 410 free(argv); 411 return ret; 412 } 413 414 415 ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL 416 simple_execle(const char *file, ... /* ,char *const envp[] */) 417 { 418 va_list ap; 419 char **argv; 420 char *const* envp; 421 int ret; 422 423 va_start(ap, file); 424 argv = vstrcollect(&ap); 425 envp = va_arg(ap, char **); 426 va_end(ap); 427 if(argv == NULL) 428 return SE_E_UNSPECIFIED; 429 ret = simple_execve(file, argv, envp); 430 free(argv); 431 return ret; 432 } 433