1/*++
2/* NAME
3/*	timed_wait 3
4/* SUMMARY
5/*	wait operations with timeout
6/* SYNOPSIS
7/*	#include <timed_wait.h>
8/*
9/*	int	timed_waitpid(pid, statusp, options, time_limit)
10/*	pid_t	pid;
11/*	WAIT_STATUS_T *statusp;
12/*	int	options;
13/*	int	time_limit;
14/* DESCRIPTION
15/*	\fItimed_waitpid\fR() waits at most \fItime_limit\fR seconds
16/*	for process termination.
17/*
18/*	Arguments:
19/* .IP "pid, statusp, options"
20/*	The process ID, status pointer and options passed to waitpid(3).
21/* .IP time_limit
22/*	The time in seconds that timed_waitpid() will wait.
23/*	This must be a number > 0.
24/* DIAGNOSTICS
25/*	Panic: interface violation.
26/*
27/*	When the time limit is exceeded, the result is -1 and errno
28/*	is set to ETIMEDOUT. Otherwise, the result value is the result
29/*	from the underlying waitpid() routine.
30/* BUGS
31/*	If there were a \fIportable\fR way to select() on process status
32/*	information, these routines would not have to use a steenkeeng
33/*	alarm() timer and signal() handler.
34/* LICENSE
35/* .ad
36/* .fi
37/*	The Secure Mailer license must be distributed with this software.
38/* AUTHOR(S)
39/*	Wietse Venema
40/*	IBM T.J. Watson Research
41/*	P.O. Box 704
42/*	Yorktown Heights, NY 10598, USA
43/*--*/
44
45/* System library. */
46
47#include <sys_defs.h>
48#include <sys/wait.h>
49#include <unistd.h>
50#include <signal.h>
51#include <errno.h>
52
53/* Utility library. */
54
55#include <msg.h>
56#include <posix_signals.h>
57#include <timed_wait.h>
58
59/* Application-specific. */
60
61static int timed_wait_expired;
62
63/* timed_wait_alarm - timeout handler */
64
65static void timed_wait_alarm(int unused_sig)
66{
67
68    /*
69     * WARNING WARNING WARNING.
70     *
71     * This code runs at unpredictable moments, as a signal handler. This code
72     * is here only so that we can break out of waitpid(). Don't put any code
73     * here other than for setting a global flag.
74     */
75    timed_wait_expired = 1;
76}
77
78/* timed_waitpid - waitpid with time limit */
79
80int     timed_waitpid(pid_t pid, WAIT_STATUS_T *statusp, int options,
81		              int time_limit)
82{
83    const char *myname = "timed_waitpid";
84    struct sigaction action;
85    struct sigaction old_action;
86    int     time_left;
87    int     wpid;
88
89    /*
90     * Sanity checks.
91     */
92    if (time_limit <= 0)
93	msg_panic("%s: bad time limit: %d", myname, time_limit);
94
95    /*
96     * Set up a timer.
97     */
98    sigemptyset(&action.sa_mask);
99    action.sa_flags = 0;
100    action.sa_handler = timed_wait_alarm;
101    if (sigaction(SIGALRM, &action, &old_action) < 0)
102	msg_fatal("%s: sigaction(SIGALRM): %m", myname);
103    timed_wait_expired = 0;
104    time_left = alarm(time_limit);
105
106    /*
107     * Wait for only a limited amount of time.
108     */
109    if ((wpid = waitpid(pid, statusp, options)) < 0 && timed_wait_expired)
110	errno = ETIMEDOUT;
111
112    /*
113     * Cleanup.
114     */
115    alarm(0);
116    if (sigaction(SIGALRM, &old_action, (struct sigaction *) 0) < 0)
117	msg_fatal("%s: sigaction(SIGALRM): %m", myname);
118    if (time_left)
119	alarm(time_left);
120
121    return (wpid);
122}
123