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 **********************************************************************/
41 #error This implementation is Windows specific.
45 * wait_for_process_timed waits for a process to terminate or until a
46 * specified timeout occurs.
48 * @param[in] pid Process id for the monitored process
50 * @param[in] func Timeout callback function. When the wait times out,
51 * the callback function is called. The possible return values
52 * from the callback function are:
54 * - ((time_t) -2) Exit loop without killing child and return SE_E_EXECTIMEOUT.
55 * - ((time_t) -1) Kill child with SIGTERM and wait for child to exit.
56 * - 0 Don't timeout again
57 * - n Seconds to next timeout
59 * @param[in] ptr Optional parameter for func()
61 * @param[in] timeout Seconds to first timeout.
63 * @retval SE_E_UNSPECIFIED Unspecified system error
64 * @retval SE_E_FORKFAILED Fork failure (not applicable for _WIN32 targets)
65 * @retval SE_E_WAITPIDFAILED waitpid errors
66 * @retval SE_E_EXECTIMEOUT exec timeout
67 * @retval 0 <= Return value from subprocess
68 * @retval SE_E_NOTFOUND The program coudln't be found
69 * @retval 128- The signal that killed the subprocess +128.
71 ROKEN_LIB_FUNCTION
int ROKEN_LIB_CALL
72 wait_for_process_timed(pid_t pid
, time_t (*func
)(void *),
73 void *ptr
, time_t timeout
)
80 hProcess
= OpenProcess(SYNCHRONIZE
, FALSE
, pid
);
82 if (hProcess
== NULL
) {
83 DWORD dw
= GetLastError();
84 return SE_E_WAITPIDFAILED
;
87 dtimeout
= (DWORD
) ((timeout
== 0)? INFINITE
: timeout
* 1000);
90 wrv
= WaitForSingleObject(hProcess
, dtimeout
);
92 if (wrv
== WAIT_OBJECT_0
) {
96 GetExitCodeProcess(hProcess
, &prv
);
100 } else if (wrv
== WAIT_TIMEOUT
) {
105 timeout
= (*func
)(ptr
);
107 if (timeout
== (time_t)-1) {
109 if (TerminateProcess(hProcess
, 128 + 9)) {
113 rv
= SE_E_UNSPECIFIED
;
116 } else if (timeout
== (time_t) -2) {
118 rv
= SE_E_EXECTIMEOUT
;
123 dtimeout
= (DWORD
) ((timeout
== 0)? INFINITE
: timeout
* 1000);
130 rv
= SE_E_UNSPECIFIED
;
137 CloseHandle(hProcess
);
142 ROKEN_LIB_FUNCTION
int ROKEN_LIB_CALL
143 wait_for_process(pid_t pid
)
145 return wait_for_process_timed(pid
, NULL
, NULL
, 0);
149 collect_commandline(const char * fn
, va_list * ap
)
152 size_t alloc_len
= 0;
156 for (s
= fn
; s
; s
= (char *) va_arg(*ap
, char *)) {
158 int need_quote
= FALSE
;
160 if (FAILED(StringCchLength(s
, MAX_PATH
, &cmp_len
))) {
169 if (strchr(s
, ' ') && /* need to quote any component that
170 has embedded spaces, but not if
171 they are already quoted. */
173 s
[cmp_len
- 1] != '"') {
175 cmp_len
+= 2 * sizeof(char);
179 cmp_len
+= 1 * sizeof(char);
181 if (alloc_len
< len
+ cmp_len
+ 1) {
184 alloc_len
+= ((len
+ cmp_len
- alloc_len
) / MAX_PATH
+ 1) * MAX_PATH
;
185 nc
= (char *) realloc(cmd
, alloc_len
* sizeof(char));
200 StringCchPrintf(cmd
+ len
, alloc_len
- len
, "\"%s\"", s
);
202 StringCchCopy(cmd
+ len
, alloc_len
- len
, s
);
211 ROKEN_LIB_FUNCTION pid_t ROKEN_LIB_CALL
212 pipe_execv(FILE **stdin_fd
, FILE **stdout_fd
, FILE **stderr_fd
,
213 const char *file
, ...)
215 HANDLE hOut_r
= NULL
;
216 HANDLE hOut_w
= NULL
;
219 HANDLE hErr_r
= NULL
;
220 HANDLE hErr_w
= NULL
;
222 SECURITY_ATTRIBUTES sa
;
224 PROCESS_INFORMATION pi
;
226 char * commandline
= NULL
;
228 pid_t rv
= (pid_t
) -1;
234 commandline
= collect_commandline(file
, &ap
);
236 if (commandline
== NULL
)
240 ZeroMemory(&si
, sizeof(si
));
241 ZeroMemory(&pi
, sizeof(pi
));
242 ZeroMemory(&sa
, sizeof(sa
));
247 sa
.nLength
= sizeof(sa
);
248 sa
.bInheritHandle
= TRUE
;
249 sa
.lpSecurityDescriptor
= NULL
;
251 if ((stdout_fd
&& !CreatePipe(&hOut_r
, &hOut_w
, &sa
, 0 /* Use default */)) ||
253 (stdin_fd
&& !CreatePipe(&hIn_r
, &hIn_w
, &sa
, 0)) ||
255 (stderr_fd
&& !CreatePipe(&hErr_r
, &hErr_w
, &sa
, 0)) ||
257 (!stdout_fd
&& (hOut_w
= CreateFile("CON", GENERIC_WRITE
, FILE_SHARE_READ
|FILE_SHARE_WRITE
,
258 &sa
, OPEN_EXISTING
, 0, NULL
)) == INVALID_HANDLE_VALUE
) ||
260 (!stdin_fd
&& (hIn_r
= CreateFile("CON",GENERIC_READ
, FILE_SHARE_READ
|FILE_SHARE_WRITE
,
261 &sa
, OPEN_EXISTING
, 0, NULL
)) == INVALID_HANDLE_VALUE
) ||
263 (!stderr_fd
&& (hErr_w
= CreateFile("CON", GENERIC_WRITE
, FILE_SHARE_READ
|FILE_SHARE_WRITE
,
264 &sa
, OPEN_EXISTING
, 0, NULL
)) == INVALID_HANDLE_VALUE
))
268 /* We don't want the child processes inheriting these */
270 SetHandleInformation(hOut_r
, HANDLE_FLAG_INHERIT
, FALSE
);
273 SetHandleInformation(hIn_w
, HANDLE_FLAG_INHERIT
, FALSE
);
276 SetHandleInformation(hErr_r
, HANDLE_FLAG_INHERIT
, FALSE
);
279 si
.lpReserved
= NULL
;
282 si
.dwFlags
= STARTF_USESTDHANDLES
;
283 si
.hStdInput
= hIn_r
;
284 si
.hStdOutput
= hOut_w
;
285 si
.hStdError
= hErr_w
;
287 if (!CreateProcess(file
, commandline
, NULL
, NULL
,
288 TRUE
, /* bInheritHandles */
289 CREATE_NO_WINDOW
, /* dwCreationFlags */
290 NULL
, /* lpEnvironment */
291 NULL
, /* lpCurrentDirectory */
295 rv
= (pid_t
) (GetLastError() == ERROR_FILE_NOT_FOUND
)? 127 : -1;
300 *stdin_fd
= _fdopen(_open_osfhandle((intptr_t) hIn_w
, 0), "wb");
306 *stdout_fd
= _fdopen(_open_osfhandle((intptr_t) hOut_r
, _O_RDONLY
), "rb");
312 *stderr_fd
= _fdopen(_open_osfhandle((intptr_t) hErr_r
, _O_RDONLY
), "rb");
317 rv
= (pid_t
) pi
.dwProcessId
;
321 if (pi
.hProcess
) CloseHandle(pi
.hProcess
);
323 if (pi
.hThread
) CloseHandle(pi
.hThread
);
325 if (hIn_r
) CloseHandle(hIn_r
);
327 if (hIn_w
) CloseHandle(hIn_w
);
329 if (hOut_r
) CloseHandle(hOut_r
);
331 if (hOut_w
) CloseHandle(hOut_w
);
333 if (hErr_r
) CloseHandle(hErr_r
);
335 if (hErr_w
) CloseHandle(hErr_w
);
341 ROKEN_LIB_FUNCTION
int ROKEN_LIB_CALL
342 simple_execvp_timed(const char *file
, char *const args
[],
343 time_t (*func
)(void *), void *ptr
, time_t timeout
)
348 hp
= spawnvp(_P_NOWAIT
, file
, args
);
351 return (errno
== ENOENT
)? 127: 126;
355 rv
= wait_for_process_timed(GetProcessId((HANDLE
) hp
), func
, ptr
, timeout
);
357 CloseHandle((HANDLE
) hp
);
363 ROKEN_LIB_FUNCTION
int ROKEN_LIB_CALL
364 simple_execvp(const char *file
, char *const args
[])
366 return simple_execvp_timed(file
, args
, NULL
, NULL
, 0);
370 ROKEN_LIB_FUNCTION
int ROKEN_LIB_CALL
371 simple_execve_timed(const char *file
, char *const args
[], char *const envp
[],
372 time_t (*func
)(void *), void *ptr
, time_t timeout
)
377 hp
= spawnve(_P_NOWAIT
, file
, args
, envp
);
380 return (errno
== ENOENT
)? 127: 126;
384 rv
= wait_for_process_timed(GetProcessId((HANDLE
) hp
), func
, ptr
, timeout
);
386 CloseHandle((HANDLE
) hp
);
392 ROKEN_LIB_FUNCTION
int ROKEN_LIB_CALL
393 simple_execve(const char *file
, char *const args
[], char *const envp
[])
395 return simple_execve_timed(file
, args
, envp
, NULL
, NULL
, 0);
399 ROKEN_LIB_FUNCTION
int ROKEN_LIB_CALL
400 simple_execlp(const char *file
, ...)
407 argv
= vstrcollect(&ap
);
410 return SE_E_UNSPECIFIED
;
411 ret
= simple_execvp(file
, argv
);
417 ROKEN_LIB_FUNCTION
int ROKEN_LIB_CALL
418 simple_execle(const char *file
, ... /* ,char *const envp[] */)
426 argv
= vstrcollect(&ap
);
427 envp
= va_arg(ap
, char **);
430 return SE_E_UNSPECIFIED
;
431 ret
= simple_execve(file
, argv
, envp
);