#include <limits.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <unistd.h>
#include <errno.h>
#include <pthread.h>
#include <sys/types.h>
#include <sys/wait.h>
typedef struct {
pid_t pid; pid_t return_val; pthread_cond_t cond; pthread_mutex_t *mutex; } proc_info_t;
void *wait_for_process(void *arg) {
proc_info_t *proc_info = (proc_info_t *)arg;
siginfo_t info;
if (waitid(P_PID, proc_info->pid, &info, WNOWAIT | WEXITED) == -1) {
proc_info->return_val = -1;
} else {
proc_info->return_val = 0;
}
(void)pthread_mutex_lock(proc_info->mutex);
(void)pthread_cond_signal(&proc_info->cond);
(void)pthread_mutex_unlock(proc_info->mutex);
return NULL;
}
time_t get_max_time_t() {
return _Generic((time_t)0, signed char
: SCHAR_MAX, unsigned char
: UCHAR_MAX, signed short
: SHRT_MAX, unsigned short
: USHRT_MAX, signed int
: INT_MAX, unsigned int
: UINT_MAX, signed long
: LONG_MAX, unsigned long
: ULONG_MAX, signed long long
: LLONG_MAX, unsigned long long
: ULLONG_MAX);
}
int wait_timeout_untraced_internal_4(proc_info_t *proc_info,
uint32_t timeout_ms) {
struct timespec ts = {0};
if (clock_gettime(CLOCK_MONOTONIC, &ts) != 0) {
return -1;
}
time_t seconds = (time_t)(timeout_ms / 1000);
long nano_seconds = ts.tv_nsec + ((long)(timeout_ms % 1000) * (long)1000000);
seconds = seconds + (nano_seconds / 1000000000);
nano_seconds = nano_seconds % 1000000000;
if (get_max_time_t() - seconds < ts.tv_sec) {
return -1;
}
ts.tv_sec += seconds;
ts.tv_nsec = nano_seconds;
int ret = pthread_cond_timedwait(&proc_info->cond, proc_info->mutex, &ts);
return ret;
}
pid_t wait_timeout_untraced_internal_3(proc_info_t *proc_info,
uint32_t timeout_ms) {
pthread_t thread;
if (pthread_create(&thread, NULL, wait_for_process, proc_info) != 0) {
return -1;
}
(void)pthread_mutex_lock(proc_info->mutex);
int ret = wait_timeout_untraced_internal_4(proc_info, timeout_ms);
(void)pthread_mutex_unlock(proc_info->mutex);
if (ret == ETIMEDOUT) {
(void)pthread_cancel(thread);
}
(void)pthread_join(thread, NULL);
if (ret == ETIMEDOUT) {
errno = ETIMEDOUT;
return -1;
}
return proc_info->return_val;
}
pid_t wait_timeout_untraced_internal_2(pid_t pid, uint32_t timeout_ms,
pthread_mutex_t *mutex,
pthread_condattr_t *attr) {
proc_info_t proc_info;
proc_info.pid = pid;
proc_info.return_val = -1;
proc_info.mutex = mutex;
if (pthread_condattr_setclock(attr, CLOCK_MONOTONIC) != 0) {
return -1;
}
if (pthread_cond_init(&proc_info.cond, attr) != 0) {
return -1;
}
pid_t result = wait_timeout_untraced_internal_3(&proc_info, timeout_ms);
(void)pthread_cond_destroy(&proc_info.cond);
return result;
}
pid_t wait_timeout_untraced_internal_1(pid_t pid, uint32_t timeout_ms,
pthread_mutex_t *mutex) {
pthread_condattr_t attr;
if (pthread_condattr_init(&attr) != 0) {
return -1;
}
pid_t result =
wait_timeout_untraced_internal_2(pid, timeout_ms, mutex, &attr);
(void)pthread_condattr_destroy(&attr);
return result;
}
pid_t wait_timeout_untraced(pid_t pid, uint32_t timeout_ms) {
pthread_mutex_t mutex;
if (pthread_mutex_init(&mutex, NULL) != 0) {
return -1;
}
pid_t result = wait_timeout_untraced_internal_1(pid, timeout_ms, &mutex);
(void)pthread_mutex_destroy(&mutex);
return result;
}