1/* $NetBSD: simple_exec_w32.c,v 1.2 2017/01/28 21:31:50 christos 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 */ 70ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL 71wait_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 140ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL 141wait_for_process(pid_t pid) 142{ 143 return wait_for_process_timed(pid, NULL, NULL, 0); 144} 145 146static char * 147collect_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 cmd = nc; 190 } 191 192 if (cmd == NULL) 193 return NULL; 194 195 if (s != fn) 196 cmd[len++] = ' '; 197 198 if (need_quote) { 199 StringCchPrintf(cmd + len, alloc_len - len, "\"%s\"", s); 200 } else { 201 StringCchCopy(cmd + len, alloc_len - len, s); 202 } 203 204 len += cmp_len; 205 } 206 207 return cmd; 208} 209 210ROKEN_LIB_FUNCTION pid_t ROKEN_LIB_CALL 211pipe_execv(FILE **stdin_fd, FILE **stdout_fd, FILE **stderr_fd, 212 const char *file, ...) 213{ 214 HANDLE hOut_r = NULL; 215 HANDLE hOut_w = NULL; 216 HANDLE hIn_r = NULL; 217 HANDLE hIn_w = NULL; 218 HANDLE hErr_r = NULL; 219 HANDLE hErr_w = NULL; 220 221 SECURITY_ATTRIBUTES sa; 222 STARTUPINFO si; 223 PROCESS_INFORMATION pi; 224 225 char * commandline = NULL; 226 227 pid_t rv = (pid_t) -1; 228 229 { 230 va_list ap; 231 232 va_start(ap, file); 233 commandline = collect_commandline(file, &ap); 234 235 if (commandline == NULL) 236 return rv; 237 } 238 239 ZeroMemory(&si, sizeof(si)); 240 ZeroMemory(&pi, sizeof(pi)); 241 ZeroMemory(&sa, sizeof(sa)); 242 243 pi.hProcess = NULL; 244 pi.hThread = NULL; 245 246 sa.nLength = sizeof(sa); 247 sa.bInheritHandle = TRUE; 248 sa.lpSecurityDescriptor = NULL; 249 250 if ((stdout_fd && !CreatePipe(&hOut_r, &hOut_w, &sa, 0 /* Use default */)) || 251 252 (stdin_fd && !CreatePipe(&hIn_r, &hIn_w, &sa, 0)) || 253 254 (stderr_fd && !CreatePipe(&hErr_r, &hErr_w, &sa, 0)) || 255 256 (!stdout_fd && (hOut_w = CreateFile("CON", GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE, 257 &sa, OPEN_EXISTING, 0, NULL)) == INVALID_HANDLE_VALUE) || 258 259 (!stdin_fd && (hIn_r = CreateFile("CON",GENERIC_READ, FILE_SHARE_READ|FILE_SHARE_WRITE, 260 &sa, OPEN_EXISTING, 0, NULL)) == INVALID_HANDLE_VALUE) || 261 262 (!stderr_fd && (hErr_w = CreateFile("CON", GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE, 263 &sa, OPEN_EXISTING, 0, NULL)) == INVALID_HANDLE_VALUE)) 264 265 goto _exit; 266 267 /* We don't want the child processes inheriting these */ 268 if (hOut_r) 269 SetHandleInformation(hOut_r, HANDLE_FLAG_INHERIT, FALSE); 270 271 if (hIn_w) 272 SetHandleInformation(hIn_w, HANDLE_FLAG_INHERIT, FALSE); 273 274 if (hErr_r) 275 SetHandleInformation(hErr_r, HANDLE_FLAG_INHERIT, FALSE); 276 277 si.cb = sizeof(si); 278 si.lpReserved = NULL; 279 si.lpDesktop = NULL; 280 si.lpTitle = NULL; 281 si.dwFlags = STARTF_USESTDHANDLES; 282 si.hStdInput = hIn_r; 283 si.hStdOutput = hOut_w; 284 si.hStdError = hErr_w; 285 286 if (!CreateProcess(file, commandline, NULL, NULL, 287 TRUE, /* bInheritHandles */ 288 CREATE_NO_WINDOW, /* dwCreationFlags */ 289 NULL, /* lpEnvironment */ 290 NULL, /* lpCurrentDirectory */ 291 &si, 292 &pi)) { 293 294 rv = (pid_t) (GetLastError() == ERROR_FILE_NOT_FOUND)? 127 : -1; 295 goto _exit; 296 } 297 298 if (stdin_fd) { 299 *stdin_fd = _fdopen(_open_osfhandle((intptr_t) hIn_w, 0), "wb"); 300 if (*stdin_fd) 301 hIn_w = NULL; 302 } 303 304 if (stdout_fd) { 305 *stdout_fd = _fdopen(_open_osfhandle((intptr_t) hOut_r, _O_RDONLY), "rb"); 306 if (*stdout_fd) 307 hOut_r = NULL; 308 } 309 310 if (stderr_fd) { 311 *stderr_fd = _fdopen(_open_osfhandle((intptr_t) hErr_r, _O_RDONLY), "rb"); 312 if (*stderr_fd) 313 hErr_r = NULL; 314 } 315 316 rv = (pid_t) pi.dwProcessId; 317 318 _exit: 319 320 if (pi.hProcess) CloseHandle(pi.hProcess); 321 322 if (pi.hThread) CloseHandle(pi.hThread); 323 324 if (hIn_r) CloseHandle(hIn_r); 325 326 if (hIn_w) CloseHandle(hIn_w); 327 328 if (hOut_r) CloseHandle(hOut_r); 329 330 if (hOut_w) CloseHandle(hOut_w); 331 332 if (hErr_r) CloseHandle(hErr_r); 333 334 if (hErr_w) CloseHandle(hErr_w); 335 336 return rv; 337} 338 339 340ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL 341simple_execvp_timed(const char *file, char *const args[], 342 time_t (*func)(void *), void *ptr, time_t timeout) 343{ 344 intptr_t hp; 345 int rv; 346 347 hp = spawnvp(_P_NOWAIT, file, args); 348 349 if (hp == -1) 350 return (errno == ENOENT)? 127: 126; 351 else if (hp == 0) 352 return 0; 353 354 rv = wait_for_process_timed(GetProcessId((HANDLE) hp), func, ptr, timeout); 355 356 CloseHandle((HANDLE) hp); 357 358 return rv; 359} 360 361 362ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL 363simple_execvp(const char *file, char *const args[]) 364{ 365 return simple_execvp_timed(file, args, NULL, NULL, 0); 366} 367 368 369ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL 370simple_execve_timed(const char *file, char *const args[], char *const envp[], 371 time_t (*func)(void *), void *ptr, time_t timeout) 372{ 373 intptr_t hp; 374 int rv; 375 376 hp = spawnve(_P_NOWAIT, file, args, envp); 377 378 if (hp == -1) 379 return (errno == ENOENT)? 127: 126; 380 else if (hp == 0) 381 return 0; 382 383 rv = wait_for_process_timed(GetProcessId((HANDLE) hp), func, ptr, timeout); 384 385 CloseHandle((HANDLE) hp); 386 387 return rv; 388} 389 390 391ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL 392simple_execve(const char *file, char *const args[], char *const envp[]) 393{ 394 return simple_execve_timed(file, args, envp, NULL, NULL, 0); 395} 396 397 398ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL 399simple_execlp(const char *file, ...) 400{ 401 va_list ap; 402 char **argv; 403 int ret; 404 405 va_start(ap, file); 406 argv = vstrcollect(&ap); 407 va_end(ap); 408 if(argv == NULL) 409 return SE_E_UNSPECIFIED; 410 ret = simple_execvp(file, argv); 411 free(argv); 412 return ret; 413} 414 415 416ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL 417simple_execle(const char *file, ... /* ,char *const envp[] */) 418{ 419 va_list ap; 420 char **argv; 421 char *const* envp; 422 int ret; 423 424 va_start(ap, file); 425 argv = vstrcollect(&ap); 426 envp = va_arg(ap, char **); 427 va_end(ap); 428 if(argv == NULL) 429 return SE_E_UNSPECIFIED; 430 ret = simple_execve(file, argv, envp); 431 free(argv); 432 return ret; 433} 434