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 return SE_E_WAITPIDFAILED
;
86 dtimeout
= (DWORD
) ((timeout
== 0)? INFINITE
: timeout
* 1000);
89 wrv
= WaitForSingleObject(hProcess
, dtimeout
);
91 if (wrv
== WAIT_OBJECT_0
) {
95 GetExitCodeProcess(hProcess
, &prv
);
99 } else if (wrv
== WAIT_TIMEOUT
) {
104 timeout
= (*func
)(ptr
);
106 if (timeout
== (time_t)-1) {
108 if (TerminateProcess(hProcess
, 128 + 9)) {
112 rv
= SE_E_UNSPECIFIED
;
115 } else if (timeout
== (time_t) -2) {
117 rv
= SE_E_EXECTIMEOUT
;
122 dtimeout
= (DWORD
) ((timeout
== 0)? INFINITE
: timeout
* 1000);
129 rv
= SE_E_UNSPECIFIED
;
136 CloseHandle(hProcess
);
141 ROKEN_LIB_FUNCTION
int ROKEN_LIB_CALL
142 wait_for_process(pid_t pid
)
144 return wait_for_process_timed(pid
, NULL
, NULL
, 0);
148 collect_commandline(const char * fn
, va_list * ap
)
151 size_t alloc_len
= 0;
155 for (s
= fn
; s
; s
= (char *) va_arg(*ap
, char *)) {
157 int need_quote
= FALSE
;
159 if (FAILED(StringCchLength(s
, MAX_PATH
, &cmp_len
))) {
168 if (strchr(s
, ' ') && /* need to quote any component that
169 has embedded spaces, but not if
170 they are already quoted. */
172 s
[cmp_len
- 1] != '"') {
174 cmp_len
+= 2 * sizeof(char);
178 cmp_len
+= 1 * sizeof(char);
180 if (alloc_len
< len
+ cmp_len
+ 1) {
183 alloc_len
+= ((len
+ cmp_len
- alloc_len
) / MAX_PATH
+ 1) * MAX_PATH
;
184 nc
= (char *) realloc(cmd
, alloc_len
* sizeof(char));
199 StringCchPrintf(cmd
+ len
, alloc_len
- len
, "\"%s\"", s
);
201 StringCchCopy(cmd
+ len
, alloc_len
- len
, s
);
210 ROKEN_LIB_FUNCTION pid_t ROKEN_LIB_CALL
211 pipe_execv(FILE **stdin_fd
, FILE **stdout_fd
, FILE **stderr_fd
,
212 const char *file
, ...)
214 HANDLE hOut_r
= NULL
;
215 HANDLE hOut_w
= NULL
;
218 HANDLE hErr_r
= NULL
;
219 HANDLE hErr_w
= NULL
;
221 SECURITY_ATTRIBUTES sa
;
223 PROCESS_INFORMATION pi
;
225 char * commandline
= NULL
;
227 pid_t rv
= (pid_t
) -1;
233 commandline
= collect_commandline(file
, &ap
);
235 if (commandline
== NULL
)
239 ZeroMemory(&si
, sizeof(si
));
240 ZeroMemory(&pi
, sizeof(pi
));
241 ZeroMemory(&sa
, sizeof(sa
));
246 sa
.nLength
= sizeof(sa
);
247 sa
.bInheritHandle
= TRUE
;
248 sa
.lpSecurityDescriptor
= NULL
;
250 if ((stdout_fd
&& !CreatePipe(&hOut_r
, &hOut_w
, &sa
, 0 /* Use default */)) ||
252 (stdin_fd
&& !CreatePipe(&hIn_r
, &hIn_w
, &sa
, 0)) ||
254 (stderr_fd
&& !CreatePipe(&hErr_r
, &hErr_w
, &sa
, 0)) ||
256 (!stdout_fd
&& (hOut_w
= CreateFile("CON", GENERIC_WRITE
, FILE_SHARE_READ
|FILE_SHARE_WRITE
,
257 &sa
, OPEN_EXISTING
, 0, NULL
)) == INVALID_HANDLE_VALUE
) ||
259 (!stdin_fd
&& (hIn_r
= CreateFile("CON",GENERIC_READ
, FILE_SHARE_READ
|FILE_SHARE_WRITE
,
260 &sa
, OPEN_EXISTING
, 0, NULL
)) == INVALID_HANDLE_VALUE
) ||
262 (!stderr_fd
&& (hErr_w
= CreateFile("CON", GENERIC_WRITE
, FILE_SHARE_READ
|FILE_SHARE_WRITE
,
263 &sa
, OPEN_EXISTING
, 0, NULL
)) == INVALID_HANDLE_VALUE
))
267 /* We don't want the child processes inheriting these */
269 SetHandleInformation(hOut_r
, HANDLE_FLAG_INHERIT
, FALSE
);
272 SetHandleInformation(hIn_w
, HANDLE_FLAG_INHERIT
, FALSE
);
275 SetHandleInformation(hErr_r
, HANDLE_FLAG_INHERIT
, FALSE
);
278 si
.lpReserved
= NULL
;
281 si
.dwFlags
= STARTF_USESTDHANDLES
;
282 si
.hStdInput
= hIn_r
;
283 si
.hStdOutput
= hOut_w
;
284 si
.hStdError
= hErr_w
;
286 if (!CreateProcess(file
, commandline
, NULL
, NULL
,
287 TRUE
, /* bInheritHandles */
288 CREATE_NO_WINDOW
, /* dwCreationFlags */
289 NULL
, /* lpEnvironment */
290 NULL
, /* lpCurrentDirectory */
294 rv
= (pid_t
) (GetLastError() == ERROR_FILE_NOT_FOUND
)? 127 : -1;
299 *stdin_fd
= _fdopen(_open_osfhandle((intptr_t) hIn_w
, 0), "wb");
305 *stdout_fd
= _fdopen(_open_osfhandle((intptr_t) hOut_r
, _O_RDONLY
), "rb");
311 *stderr_fd
= _fdopen(_open_osfhandle((intptr_t) hErr_r
, _O_RDONLY
), "rb");
316 rv
= (pid_t
) pi
.dwProcessId
;
320 if (pi
.hProcess
) CloseHandle(pi
.hProcess
);
322 if (pi
.hThread
) CloseHandle(pi
.hThread
);
324 if (hIn_r
) CloseHandle(hIn_r
);
326 if (hIn_w
) CloseHandle(hIn_w
);
328 if (hOut_r
) CloseHandle(hOut_r
);
330 if (hOut_w
) CloseHandle(hOut_w
);
332 if (hErr_r
) CloseHandle(hErr_r
);
334 if (hErr_w
) CloseHandle(hErr_w
);
340 ROKEN_LIB_FUNCTION
int ROKEN_LIB_CALL
341 simple_execvp_timed(const char *file
, char *const args
[],
342 time_t (*func
)(void *), void *ptr
, time_t timeout
)
347 hp
= spawnvp(_P_NOWAIT
, file
, args
);
350 return (errno
== ENOENT
)? 127: 126;
354 rv
= wait_for_process_timed(GetProcessId((HANDLE
) hp
), func
, ptr
, timeout
);
356 CloseHandle((HANDLE
) hp
);
362 ROKEN_LIB_FUNCTION
int ROKEN_LIB_CALL
363 simple_execvp(const char *file
, char *const args
[])
365 return simple_execvp_timed(file
, args
, NULL
, NULL
, 0);
369 ROKEN_LIB_FUNCTION
int ROKEN_LIB_CALL
370 simple_execve_timed(const char *file
, char *const args
[], char *const envp
[],
371 time_t (*func
)(void *), void *ptr
, time_t timeout
)
376 hp
= spawnve(_P_NOWAIT
, file
, args
, envp
);
379 return (errno
== ENOENT
)? 127: 126;
383 rv
= wait_for_process_timed(GetProcessId((HANDLE
) hp
), func
, ptr
, timeout
);
385 CloseHandle((HANDLE
) hp
);
391 ROKEN_LIB_FUNCTION
int ROKEN_LIB_CALL
392 simple_execve(const char *file
, char *const args
[], char *const envp
[])
394 return simple_execve_timed(file
, args
, envp
, NULL
, NULL
, 0);
398 ROKEN_LIB_FUNCTION
int ROKEN_LIB_CALL
399 simple_execlp(const char *file
, ...)
406 argv
= vstrcollect(&ap
);
409 return SE_E_UNSPECIFIED
;
410 ret
= simple_execvp(file
, argv
);
416 ROKEN_LIB_FUNCTION
int ROKEN_LIB_CALL
417 simple_execle(const char *file
, ... /* ,char *const envp[] */)
425 argv
= vstrcollect(&ap
);
426 envp
= va_arg(ap
, char **);
429 return SE_E_UNSPECIFIED
;
430 ret
= simple_execve(file
, argv
, envp
);