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