/*
* Copyright (c) 2016 Linux Test Project
*
* Licensed under the GNU GPLv2 or later.
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
* the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program.
*/
#ifndef WAITPID_COMMON_H__
#define WAITPID_COMMON_H__
#include <sys/types.h>
#include <errno.h>
#include <sys/wait.h>
#include <stdlib.h>
#include "tst_test.h"
#define MAXKIDS 8
static pid_t *fork_kid_pid;
static pid_t child_1_pid;
static void do_child_1(void);
static void waitpid_setup(void)
{
fork_kid_pid = SAFE_MMAP(NULL, sizeof(*fork_kid_pid) * MAXKIDS,
PROT_READ | PROT_WRITE,
MAP_SHARED | MAP_ANONYMOUS, -1, 0);
}
static void waitpid_cleanup(void)
{
int i;
for (i = 0; i < MAXKIDS; i++) {
if (fork_kid_pid[i] > 0)
kill(fork_kid_pid[i], SIGKILL);
}
if (child_1_pid > 0)
kill(child_1_pid, SIGKILL);
munmap(fork_kid_pid, sizeof(*fork_kid_pid) * MAXKIDS);
}
static void waitpid_test(void)
{
child_1_pid = SAFE_FORK();
if (child_1_pid == 0) {
do_child_1();
} else {
tst_reap_children();
child_1_pid = 0;
}
}
static void do_exit(int stop)
{
TST_CHECKPOINT_WAIT(0);
if (stop)
kill(getpid(), SIGSTOP);
exit(3);
}
static int waitpid_errno_check(int err, int exp_err)
{
if (err != exp_err) {
tst_res(TFAIL, "waitpid() set errno to %s, expected %s",
tst_strerrno(err), tst_strerrno(exp_err));
return -1;
}
return 0;
}
int waitpid_ret_test(pid_t wp_pid, int *wp_status, int wp_opts,
pid_t wp_ret, int wp_errno)
{
pid_t ret;
ret = waitpid(wp_pid, wp_status, wp_opts);
if (ret != wp_ret) {
tst_res(TFAIL, "waitpid() returned %d, expected %d",
ret, wp_ret);
return -1;
}
if ((ret == -1) && waitpid_errno_check(errno, wp_errno))
return -1;
return 0;
}
static int reap_children(pid_t wp_pid, int wp_opts, pid_t *children, int len)
{
pid_t pid;
int i;
int status;
for (;;) {
pid = waitpid(wp_pid, &status, wp_opts);
if (pid == -1) {
if (errno == EINTR)
continue;
if (waitpid_errno_check(errno, ECHILD))
return -1;
break;
}
if (pid == 0) {
if (wp_opts & WNOHANG)
continue;
tst_res(TFAIL, "waitpid() returned 0 unexpectedly");
return -1;
}
if (WIFSTOPPED(status)) {
if (WSTOPSIG(status) != SIGSTOP) {
tst_res(TFAIL,
"Pid %d: expected SIGSTOP, got %d",
pid, WSTOPSIG(status));
return -1;
}
tst_res(TINFO, "Sending SIGCONT to %d", pid);
if (kill(pid, SIGCONT) < 0) {
tst_res(TFAIL | TERRNO,
"kill(%d, SIGCONT) failed", pid);
return -1;
}
continue;
}
for (i = 0; i < len; i++) {
if (pid == children[i]) {
children[i] = 0;
break;
}
}
if (i == len) {
tst_res(TFAIL, "Pid %d not found", pid);
return -1;
}
if (!WIFEXITED(status)) {
tst_res(TFAIL, "Pid %d exited abnormally", pid);
return -1;
}
if (WEXITSTATUS(status) != 3) {
tst_res(TFAIL, "Pid %d exited with %d, expected 3",
pid, WEXITSTATUS(status));
return -1;
}
}
for (i = 0; i < len; i++) {
if (children[i]) {
tst_res(TFAIL, "Pid %d not reaped", children[i]);
return -1;
}
}
return 0;
}
#endif /* WAITPID_COMMON_H__ */