1 /***********************************************************************
2 * Copyright (c) 2009, Secure Endpoints Inc.
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
9 * - Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
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
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.
30 **********************************************************************/
38 #error This implementation is Windows specific.
42 * wait_for_process_timed waits for a process to terminate or until a
43 * specified timeout occurs.
45 * @param[in] pid Process id for the monitored process
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:
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
56 * @param[in] ptr Optional parameter for func()
58 * @param[in] timeout Seconds to first timeout.
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.
68 ROKEN_LIB_FUNCTION
int ROKEN_LIB_CALL
69 wait_for_process_timed(pid_t pid
, time_t (*func
)(void *),
70 void *ptr
, time_t timeout
)
77 hProcess
= OpenProcess(SYNCHRONIZE
, FALSE
, pid
);
79 if (hProcess
== NULL
) {
80 return SE_E_WAITPIDFAILED
;
83 dtimeout
= (DWORD
) ((timeout
== 0)? INFINITE
: timeout
* 1000);
86 wrv
= WaitForSingleObject(hProcess
, dtimeout
);
88 if (wrv
== WAIT_OBJECT_0
) {
92 GetExitCodeProcess(hProcess
, &prv
);
96 } else if (wrv
== WAIT_TIMEOUT
) {
101 timeout
= (*func
)(ptr
);
103 if (timeout
== (time_t)-1) {
105 if (TerminateProcess(hProcess
, 128 + 9)) {
109 rv
= SE_E_UNSPECIFIED
;
112 } else if (timeout
== (time_t) -2) {
114 rv
= SE_E_EXECTIMEOUT
;
119 dtimeout
= (DWORD
) ((timeout
== 0)? INFINITE
: timeout
* 1000);
126 rv
= SE_E_UNSPECIFIED
;
133 CloseHandle(hProcess
);
138 ROKEN_LIB_FUNCTION
int ROKEN_LIB_CALL
139 wait_for_process(pid_t pid
)
141 return wait_for_process_timed(pid
, NULL
, NULL
, 0);
145 collect_commandline(const char * fn
, va_list * ap
)
148 size_t alloc_len
= 0;
152 for (s
= fn
; s
; s
= (char *) va_arg(*ap
, char *)) {
154 int need_quote
= FALSE
;
156 if (FAILED(StringCchLength(s
, MAX_PATH
, &cmp_len
))) {
165 if (strchr(s
, ' ') && /* need to quote any component that
166 has embedded spaces, but not if
167 they are already quoted. */
169 s
[cmp_len
- 1] != '"') {
171 cmp_len
+= 2 * sizeof(char);
175 cmp_len
+= 1 * sizeof(char);
177 if (alloc_len
< len
+ cmp_len
+ 1) {
180 alloc_len
+= ((len
+ cmp_len
- alloc_len
) / MAX_PATH
+ 1) * MAX_PATH
;
181 nc
= (char *) realloc(cmd
, alloc_len
* sizeof(char));
196 StringCchPrintf(cmd
+ len
, alloc_len
- len
, "\"%s\"", s
);
198 StringCchCopy(cmd
+ len
, alloc_len
- len
, s
);
207 ROKEN_LIB_FUNCTION pid_t ROKEN_LIB_CALL
208 pipe_execv(FILE **stdin_fd
, FILE **stdout_fd
, FILE **stderr_fd
,
209 const char *file
, ...)
211 HANDLE hOut_r
= NULL
;
212 HANDLE hOut_w
= NULL
;
215 HANDLE hErr_r
= NULL
;
216 HANDLE hErr_w
= NULL
;
218 SECURITY_ATTRIBUTES sa
;
220 PROCESS_INFORMATION pi
;
222 char * commandline
= NULL
;
224 pid_t rv
= (pid_t
) -1;
230 commandline
= collect_commandline(file
, &ap
);
232 if (commandline
== NULL
)
236 ZeroMemory(&si
, sizeof(si
));
237 ZeroMemory(&pi
, sizeof(pi
));
238 ZeroMemory(&sa
, sizeof(sa
));
243 sa
.nLength
= sizeof(sa
);
244 sa
.bInheritHandle
= TRUE
;
245 sa
.lpSecurityDescriptor
= NULL
;
247 if ((stdout_fd
&& !CreatePipe(&hOut_r
, &hOut_w
, &sa
, 0 /* Use default */)) ||
249 (stdin_fd
&& !CreatePipe(&hIn_r
, &hIn_w
, &sa
, 0)) ||
251 (stderr_fd
&& !CreatePipe(&hErr_r
, &hErr_w
, &sa
, 0)) ||
253 (!stdout_fd
&& (hOut_w
= CreateFile("CON", GENERIC_WRITE
, FILE_SHARE_READ
|FILE_SHARE_WRITE
,
254 &sa
, OPEN_EXISTING
, 0, NULL
)) == INVALID_HANDLE_VALUE
) ||
256 (!stdin_fd
&& (hIn_r
= CreateFile("CON",GENERIC_READ
, FILE_SHARE_READ
|FILE_SHARE_WRITE
,
257 &sa
, OPEN_EXISTING
, 0, NULL
)) == INVALID_HANDLE_VALUE
) ||
259 (!stderr_fd
&& (hErr_w
= CreateFile("CON", GENERIC_WRITE
, FILE_SHARE_READ
|FILE_SHARE_WRITE
,
260 &sa
, OPEN_EXISTING
, 0, NULL
)) == INVALID_HANDLE_VALUE
))
264 /* We don't want the child processes inheriting these */
266 SetHandleInformation(hOut_r
, HANDLE_FLAG_INHERIT
, FALSE
);
269 SetHandleInformation(hIn_w
, HANDLE_FLAG_INHERIT
, FALSE
);
272 SetHandleInformation(hErr_r
, HANDLE_FLAG_INHERIT
, FALSE
);
275 si
.lpReserved
= NULL
;
278 si
.dwFlags
= STARTF_USESTDHANDLES
;
279 si
.hStdInput
= hIn_r
;
280 si
.hStdOutput
= hOut_w
;
281 si
.hStdError
= hErr_w
;
283 if (!CreateProcess(file
, commandline
, NULL
, NULL
,
284 TRUE
, /* bInheritHandles */
285 CREATE_NO_WINDOW
, /* dwCreationFlags */
286 NULL
, /* lpEnvironment */
287 NULL
, /* lpCurrentDirectory */
291 rv
= (pid_t
) (GetLastError() == ERROR_FILE_NOT_FOUND
)? 127 : -1;
296 *stdin_fd
= _fdopen(_open_osfhandle((intptr_t) hIn_w
, 0), "wb");
302 *stdout_fd
= _fdopen(_open_osfhandle((intptr_t) hOut_r
, _O_RDONLY
), "rb");
308 *stderr_fd
= _fdopen(_open_osfhandle((intptr_t) hErr_r
, _O_RDONLY
), "rb");
313 rv
= (pid_t
) pi
.dwProcessId
;
317 if (pi
.hProcess
) CloseHandle(pi
.hProcess
);
319 if (pi
.hThread
) CloseHandle(pi
.hThread
);
321 if (hIn_r
) CloseHandle(hIn_r
);
323 if (hIn_w
) CloseHandle(hIn_w
);
325 if (hOut_r
) CloseHandle(hOut_r
);
327 if (hOut_w
) CloseHandle(hOut_w
);
329 if (hErr_r
) CloseHandle(hErr_r
);
331 if (hErr_w
) CloseHandle(hErr_w
);
337 ROKEN_LIB_FUNCTION
int ROKEN_LIB_CALL
338 simple_execvp_timed(const char *file
, char *const args
[],
339 time_t (*func
)(void *), void *ptr
, time_t timeout
)
344 hp
= spawnvp(_P_NOWAIT
, file
, args
);
347 return (errno
== ENOENT
)? 127: 126;
351 rv
= wait_for_process_timed(GetProcessId((HANDLE
) hp
), func
, ptr
, timeout
);
353 CloseHandle((HANDLE
) hp
);
359 ROKEN_LIB_FUNCTION
int ROKEN_LIB_CALL
360 simple_execvp(const char *file
, char *const args
[])
362 return simple_execvp_timed(file
, args
, NULL
, NULL
, 0);
366 ROKEN_LIB_FUNCTION
int ROKEN_LIB_CALL
367 simple_execve_timed(const char *file
, char *const args
[], char *const envp
[],
368 time_t (*func
)(void *), void *ptr
, time_t timeout
)
373 hp
= spawnve(_P_NOWAIT
, file
, args
, envp
);
376 return (errno
== ENOENT
)? 127: 126;
380 rv
= wait_for_process_timed(GetProcessId((HANDLE
) hp
), func
, ptr
, timeout
);
382 CloseHandle((HANDLE
) hp
);
388 ROKEN_LIB_FUNCTION
int ROKEN_LIB_CALL
389 simple_execve(const char *file
, char *const args
[], char *const envp
[])
391 return simple_execve_timed(file
, args
, envp
, NULL
, NULL
, 0);
395 ROKEN_LIB_FUNCTION
int ROKEN_LIB_CALL
396 simple_execlp(const char *file
, ...)
403 argv
= vstrcollect(&ap
);
406 return SE_E_UNSPECIFIED
;
407 ret
= simple_execvp(file
, argv
);
413 ROKEN_LIB_FUNCTION
int ROKEN_LIB_CALL
414 simple_execle(const char *file
, ... /* ,char *const envp[] */)
422 argv
= vstrcollect(&ap
);
423 envp
= va_arg(ap
, char **);
426 return SE_E_UNSPECIFIED
;
427 ret
= simple_execve(file
, argv
, envp
);