2009-07-16 17:39:16 +00:00
|
|
|
/*
|
|
|
|
* Check: a unit test framework for C
|
|
|
|
* Copyright (C) 2001, 2002 Arien Malec
|
|
|
|
*
|
|
|
|
* This library is free software; you can redistribute it and/or
|
|
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
|
|
* License as published by the Free Software Foundation; either
|
|
|
|
* version 2.1 of the License, or (at your option) any later version.
|
|
|
|
*
|
|
|
|
* This library 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
|
|
|
|
* Lesser General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
|
|
* License along with this library; if not, write to the
|
2012-11-03 20:44:48 +00:00
|
|
|
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
|
|
|
|
* Boston, MA 02110-1301, USA.
|
2009-07-16 17:39:16 +00:00
|
|
|
*/
|
|
|
|
|
check: Apply GStreamer-specific patches
Reintroduced patches:
* Make sure that fail_if(1) actually fails
from commit 9f99d056a263e71a5e6181224829def906cf0226
New patches due to updated libcheck (based on 0.9.14):
* Checks in m4/check-checks.m4 to cater for new dependencies
* Conditional compile-time compat POSIX fallbacks for libcheck
* Avoid relative paths for libcheck header files
* Make timer_create() usage depend on posix timers, not librt
* Rely on default AX_PTHREAD behavior to allow HAVE_PTHREAD to be used
when checking for types and functions (like clock_gettime())
* Avoid double declaration of clock_gettime() when availabe outside of
librt by making compat clock_gettime() declaration conditional
* check 0.9.9 renamed _fail_unless() and 0.9.12 later renamed it again
to _ck_assert_failed(), so ASSERT_{CRITICAL,WARNING}() now calls this
function
* Remove libcheck fallback infrastructure for malloc(), realloc(),
gettimeofday() and snprintf() since either they appear to be
available or they introduce even more dependencies.
The result is an embedded check in gstreamer that has been tested by
running check tests in core, -base, -good, -bad, -ugly and rtsp-server
on Linux, OSX and Windows.
Fixes https://bugzilla.gnome.org/show_bug.cgi?id=727826
2014-11-15 12:26:47 +00:00
|
|
|
#include "libcompat.h"
|
2009-07-16 17:39:16 +00:00
|
|
|
|
|
|
|
#include <sys/types.h>
|
2014-11-15 11:53:32 +00:00
|
|
|
#include <time.h>
|
2009-07-16 17:39:16 +00:00
|
|
|
#include <stdlib.h>
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <stdarg.h>
|
|
|
|
#include <signal.h>
|
2014-11-15 11:53:32 +00:00
|
|
|
#include <setjmp.h>
|
2009-07-16 17:39:16 +00:00
|
|
|
|
2014-12-14 11:54:32 +00:00
|
|
|
#include "internal-check.h"
|
2009-07-16 17:39:16 +00:00
|
|
|
#include "check_error.h"
|
|
|
|
#include "check_list.h"
|
|
|
|
#include "check_impl.h"
|
|
|
|
#include "check_msg.h"
|
|
|
|
#include "check_log.h"
|
|
|
|
|
2009-10-12 11:49:35 +00:00
|
|
|
enum rinfo
|
|
|
|
{
|
2009-07-16 17:39:16 +00:00
|
|
|
CK_R_SIG,
|
|
|
|
CK_R_PASS,
|
|
|
|
CK_R_EXIT,
|
|
|
|
CK_R_FAIL_TEST,
|
|
|
|
CK_R_FAIL_FIXTURE
|
|
|
|
};
|
|
|
|
|
2009-10-12 11:49:35 +00:00
|
|
|
enum tf_type
|
|
|
|
{
|
2009-07-16 17:39:16 +00:00
|
|
|
CK_FORK_TEST,
|
|
|
|
CK_NOFORK_TEST,
|
|
|
|
CK_NOFORK_FIXTURE
|
|
|
|
};
|
|
|
|
|
2014-11-15 11:53:32 +00:00
|
|
|
|
2009-12-17 20:09:48 +00:00
|
|
|
/* all functions are defined in the same order they are declared.
|
|
|
|
functions that depend on forking are gathered all together.
|
|
|
|
non-static functions are at the end of the file. */
|
2009-10-12 11:49:35 +00:00
|
|
|
static void srunner_run_init (SRunner * sr, enum print_output print_mode);
|
|
|
|
static void srunner_run_end (SRunner * sr, enum print_output print_mode);
|
2014-11-15 11:53:32 +00:00
|
|
|
static void srunner_iterate_suites (SRunner * sr,
|
|
|
|
const char *sname, const char *tcname, enum print_output print_mode);
|
2009-12-17 20:09:48 +00:00
|
|
|
static void srunner_iterate_tcase_tfuns (SRunner * sr, TCase * tc);
|
|
|
|
static void srunner_add_failure (SRunner * sr, TestResult * tf);
|
2014-11-15 11:53:32 +00:00
|
|
|
static TestResult *srunner_run_setup (List * func_list,
|
|
|
|
enum fork_status fork_usage, const char *test_name, const char *setup_name);
|
2009-10-12 11:49:35 +00:00
|
|
|
static int srunner_run_unchecked_setup (SRunner * sr, TCase * tc);
|
|
|
|
static TestResult *tcase_run_checked_setup (SRunner * sr, TCase * tc);
|
2014-11-15 11:53:32 +00:00
|
|
|
static void srunner_run_teardown (List * fixture_list,
|
|
|
|
enum fork_status fork_usage);
|
|
|
|
static void srunner_run_unchecked_teardown (SRunner * sr, TCase * tc);
|
2009-10-12 11:49:35 +00:00
|
|
|
static void tcase_run_checked_teardown (TCase * tc);
|
2009-12-17 20:09:48 +00:00
|
|
|
static void srunner_run_tcase (SRunner * sr, TCase * tc);
|
2009-10-12 11:49:35 +00:00
|
|
|
static TestResult *tcase_run_tfun_nofork (SRunner * sr, TCase * tc, TF * tf,
|
|
|
|
int i);
|
2009-07-16 17:39:16 +00:00
|
|
|
static TestResult *receive_result_info_nofork (const char *tcname,
|
2014-11-15 11:53:32 +00:00
|
|
|
const char *tname, int iter, int duration);
|
2009-10-12 11:49:35 +00:00
|
|
|
static void set_nofork_info (TestResult * tr);
|
2009-12-17 20:09:48 +00:00
|
|
|
static char *pass_msg (void);
|
|
|
|
|
2014-11-15 11:53:32 +00:00
|
|
|
#if defined(HAVE_FORK) && HAVE_FORK==1
|
2009-12-17 20:09:48 +00:00
|
|
|
static TestResult *tcase_run_tfun_fork (SRunner * sr, TCase * tc, TF * tf,
|
|
|
|
int i);
|
|
|
|
static TestResult *receive_result_info_fork (const char *tcname,
|
2014-11-15 11:53:32 +00:00
|
|
|
const char *tname, int iter,
|
|
|
|
int status, int expected_signal, signed char allowed_exit_value);
|
2009-12-17 20:09:48 +00:00
|
|
|
static void set_fork_info (TestResult * tr, int status, int expected_signal,
|
2014-11-15 11:53:32 +00:00
|
|
|
signed char allowed_exit_value);
|
2009-07-16 17:39:16 +00:00
|
|
|
static char *signal_msg (int sig);
|
|
|
|
static char *signal_error_msg (int signal_received, int signal_expected);
|
|
|
|
static char *exit_msg (int exitstatus);
|
|
|
|
static int waserror (int status, int expected_signal);
|
|
|
|
|
|
|
|
static int alarm_received;
|
|
|
|
static pid_t group_pid;
|
|
|
|
|
2015-03-21 14:19:43 +00:00
|
|
|
#if defined(HAVE_SIGACTION) && defined(HAVE_FORK)
|
|
|
|
static struct sigaction old_action[3];
|
|
|
|
static struct sigaction new_action[3];
|
|
|
|
#endif /* HAVE_SIGACTION && HAVE_FORK */
|
|
|
|
|
2009-12-17 20:09:48 +00:00
|
|
|
static void CK_ATTRIBUTE_UNUSED
|
2009-10-12 11:49:35 +00:00
|
|
|
sig_handler (int sig_nr)
|
2009-07-16 17:39:16 +00:00
|
|
|
{
|
|
|
|
switch (sig_nr) {
|
2009-10-12 11:49:35 +00:00
|
|
|
case SIGALRM:
|
|
|
|
alarm_received = 1;
|
|
|
|
killpg (group_pid, SIGKILL);
|
|
|
|
break;
|
2015-03-21 14:19:43 +00:00
|
|
|
case SIGTERM:
|
|
|
|
case SIGINT:{
|
|
|
|
pid_t own_group_pid;
|
|
|
|
int idx;
|
|
|
|
int child_sig;
|
|
|
|
|
|
|
|
if (sig_nr == SIGINT) {
|
|
|
|
idx = 1;
|
|
|
|
child_sig = SIGKILL;
|
2015-03-21 18:37:30 +00:00
|
|
|
} else { /* if (sig_nr == SIGTERM) */
|
|
|
|
|
2015-03-21 14:19:43 +00:00
|
|
|
idx = 2;
|
|
|
|
child_sig = SIGTERM;
|
|
|
|
}
|
|
|
|
|
|
|
|
killpg (group_pid, child_sig);
|
|
|
|
|
|
|
|
/* Restore old signal handler... */
|
|
|
|
sigaction (sig_nr, &old_action[idx], NULL);
|
|
|
|
|
|
|
|
/* ... and call it. POSIX says that calling killpg(0)
|
|
|
|
* does not necessarily mean to call it on the callers
|
|
|
|
* group pid! */
|
|
|
|
own_group_pid = getpgrp ();
|
|
|
|
killpg (own_group_pid, sig_nr);
|
|
|
|
break;
|
|
|
|
}
|
2009-10-12 11:49:35 +00:00
|
|
|
default:
|
|
|
|
eprintf ("Unhandled signal: %d", __FILE__, __LINE__, sig_nr);
|
|
|
|
break;
|
2009-07-16 17:39:16 +00:00
|
|
|
}
|
|
|
|
}
|
2014-11-15 11:53:32 +00:00
|
|
|
#endif /* HAVE_FORK */
|
2009-12-17 20:09:48 +00:00
|
|
|
|
|
|
|
#define MSG_LEN 100
|
2009-07-16 17:39:16 +00:00
|
|
|
|
2009-10-12 11:49:35 +00:00
|
|
|
static void
|
|
|
|
srunner_run_init (SRunner * sr, enum print_output print_mode)
|
2009-07-16 17:39:16 +00:00
|
|
|
{
|
2009-10-12 11:49:35 +00:00
|
|
|
set_fork_status (srunner_fork_status (sr));
|
|
|
|
setup_messaging ();
|
2009-07-16 17:39:16 +00:00
|
|
|
srunner_init_logging (sr, print_mode);
|
|
|
|
log_srunner_start (sr);
|
|
|
|
}
|
|
|
|
|
2009-10-12 11:49:35 +00:00
|
|
|
static void
|
2009-12-17 20:09:48 +00:00
|
|
|
srunner_run_end (SRunner * sr, enum print_output CK_ATTRIBUTE_UNUSED print_mode)
|
2009-07-16 17:39:16 +00:00
|
|
|
{
|
|
|
|
log_srunner_end (sr);
|
|
|
|
srunner_end_logging (sr);
|
2009-10-12 11:49:35 +00:00
|
|
|
teardown_messaging ();
|
|
|
|
set_fork_status (CK_FORK);
|
2009-07-16 17:39:16 +00:00
|
|
|
}
|
|
|
|
|
2009-10-12 11:49:35 +00:00
|
|
|
static void
|
2009-12-17 20:09:48 +00:00
|
|
|
srunner_iterate_suites (SRunner * sr,
|
2014-11-15 11:53:32 +00:00
|
|
|
const char *sname, const char *tcname,
|
2009-12-17 20:09:48 +00:00
|
|
|
enum print_output CK_ATTRIBUTE_UNUSED print_mode)
|
2009-07-16 17:39:16 +00:00
|
|
|
{
|
|
|
|
List *slst;
|
|
|
|
List *tcl;
|
|
|
|
TCase *tc;
|
|
|
|
|
|
|
|
slst = sr->slst;
|
2009-10-12 11:49:35 +00:00
|
|
|
|
2014-11-15 11:53:32 +00:00
|
|
|
for (check_list_front (slst); !check_list_at_end (slst);
|
|
|
|
check_list_advance (slst)) {
|
|
|
|
Suite *s = (Suite *) check_list_val (slst);
|
|
|
|
|
|
|
|
if (((sname != NULL) && (strcmp (sname, s->name) != 0))
|
|
|
|
|| ((tcname != NULL) && (!suite_tcase (s, tcname))))
|
|
|
|
continue;
|
2009-10-12 11:49:35 +00:00
|
|
|
|
2009-07-16 17:39:16 +00:00
|
|
|
log_suite_start (sr, s);
|
|
|
|
|
|
|
|
tcl = s->tclst;
|
2009-10-12 11:49:35 +00:00
|
|
|
|
2014-11-15 11:53:32 +00:00
|
|
|
for (check_list_front (tcl); !check_list_at_end (tcl);
|
|
|
|
check_list_advance (tcl)) {
|
|
|
|
tc = (TCase *) check_list_val (tcl);
|
|
|
|
|
|
|
|
if ((tcname != NULL) && (strcmp (tcname, tc->name) != 0)) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2009-07-16 17:39:16 +00:00
|
|
|
srunner_run_tcase (sr, tc);
|
|
|
|
}
|
2009-10-12 11:49:35 +00:00
|
|
|
|
2009-07-16 17:39:16 +00:00
|
|
|
log_suite_end (sr, s);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-10-12 11:49:35 +00:00
|
|
|
static void
|
|
|
|
srunner_iterate_tcase_tfuns (SRunner * sr, TCase * tc)
|
2009-07-16 17:39:16 +00:00
|
|
|
{
|
|
|
|
List *tfl;
|
|
|
|
TF *tfun;
|
|
|
|
TestResult *tr = NULL;
|
|
|
|
|
|
|
|
tfl = tc->tflst;
|
2009-10-12 11:49:35 +00:00
|
|
|
|
2014-11-15 11:53:32 +00:00
|
|
|
for (check_list_front (tfl); !check_list_at_end (tfl);
|
|
|
|
check_list_advance (tfl)) {
|
2009-07-16 17:39:16 +00:00
|
|
|
int i;
|
2014-11-15 11:53:32 +00:00
|
|
|
|
|
|
|
tfun = (TF *) check_list_val (tfl);
|
2009-07-16 17:39:16 +00:00
|
|
|
|
2009-10-12 11:49:35 +00:00
|
|
|
for (i = tfun->loop_start; i < tfun->loop_end; i++) {
|
2009-12-17 20:09:48 +00:00
|
|
|
log_test_start (sr, tc, tfun);
|
2009-10-12 11:49:35 +00:00
|
|
|
switch (srunner_fork_status (sr)) {
|
|
|
|
case CK_FORK:
|
2014-11-15 11:53:32 +00:00
|
|
|
#if defined(HAVE_FORK) && HAVE_FORK==1
|
2009-10-12 11:49:35 +00:00
|
|
|
tr = tcase_run_tfun_fork (sr, tc, tfun, i);
|
2014-11-15 11:53:32 +00:00
|
|
|
#else /* HAVE_FORK */
|
2009-12-17 20:09:48 +00:00
|
|
|
eprintf ("This version does not support fork", __FILE__, __LINE__);
|
2014-11-15 11:53:32 +00:00
|
|
|
#endif /* HAVE_FORK */
|
2009-10-12 11:49:35 +00:00
|
|
|
break;
|
|
|
|
case CK_NOFORK:
|
|
|
|
tr = tcase_run_tfun_nofork (sr, tc, tfun, i);
|
|
|
|
break;
|
2014-11-15 11:53:32 +00:00
|
|
|
case CK_FORK_GETENV:
|
2009-10-12 11:49:35 +00:00
|
|
|
default:
|
|
|
|
eprintf ("Bad fork status in SRunner", __FILE__, __LINE__);
|
2009-07-16 17:39:16 +00:00
|
|
|
}
|
2014-11-15 11:53:32 +00:00
|
|
|
|
|
|
|
if (NULL != tr) {
|
|
|
|
srunner_add_failure (sr, tr);
|
|
|
|
log_test_end (sr, tr);
|
|
|
|
}
|
2009-07-16 17:39:16 +00:00
|
|
|
}
|
|
|
|
}
|
2009-10-12 11:49:35 +00:00
|
|
|
}
|
2009-07-16 17:39:16 +00:00
|
|
|
|
2009-12-17 20:09:48 +00:00
|
|
|
static void
|
|
|
|
srunner_add_failure (SRunner * sr, TestResult * tr)
|
|
|
|
{
|
2014-11-15 11:53:32 +00:00
|
|
|
check_list_add_end (sr->resultlst, tr);
|
2009-12-17 20:09:48 +00:00
|
|
|
sr->stats->n_checked++; /* count checks during setup, test, and teardown */
|
|
|
|
if (tr->rtype == CK_FAILURE)
|
|
|
|
sr->stats->n_failed++;
|
|
|
|
else if (tr->rtype == CK_ERROR)
|
|
|
|
sr->stats->n_errors++;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2009-10-12 11:49:35 +00:00
|
|
|
static TestResult *
|
2014-11-15 11:53:32 +00:00
|
|
|
srunner_run_setup (List * fixture_list, enum fork_status fork_usage,
|
|
|
|
const char *test_name, const char *setup_name)
|
2009-07-16 17:39:16 +00:00
|
|
|
{
|
|
|
|
TestResult *tr = NULL;
|
2014-11-15 11:53:32 +00:00
|
|
|
Fixture *setup_fixture;
|
2009-10-12 11:49:35 +00:00
|
|
|
|
2014-11-15 11:53:32 +00:00
|
|
|
if (fork_usage == CK_FORK) {
|
2009-10-12 11:49:35 +00:00
|
|
|
send_ctx_info (CK_CTX_SETUP);
|
2009-07-16 17:39:16 +00:00
|
|
|
}
|
2009-10-12 11:49:35 +00:00
|
|
|
|
2014-11-15 11:53:32 +00:00
|
|
|
for (check_list_front (fixture_list); !check_list_at_end (fixture_list);
|
|
|
|
check_list_advance (fixture_list)) {
|
|
|
|
setup_fixture = (Fixture *) check_list_val (fixture_list);
|
|
|
|
|
|
|
|
if (fork_usage == CK_NOFORK) {
|
2009-10-12 11:49:35 +00:00
|
|
|
send_ctx_info (CK_CTX_SETUP);
|
2009-07-16 17:39:16 +00:00
|
|
|
|
2014-11-15 11:53:32 +00:00
|
|
|
if (0 == setjmp (error_jmp_buffer)) {
|
|
|
|
setup_fixture->fun ();
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Stop the setup and return the failure in nofork mode. */
|
|
|
|
tr = receive_result_info_nofork (test_name, setup_name, 0, -1);
|
2009-07-16 17:39:16 +00:00
|
|
|
if (tr->rtype != CK_PASS) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2009-10-12 11:49:35 +00:00
|
|
|
free (tr->file);
|
|
|
|
free (tr->msg);
|
|
|
|
free (tr);
|
2009-07-16 17:39:16 +00:00
|
|
|
tr = NULL;
|
2014-11-15 11:53:32 +00:00
|
|
|
} else {
|
|
|
|
setup_fixture->fun ();
|
2009-07-16 17:39:16 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return tr;
|
|
|
|
}
|
|
|
|
|
2014-11-15 11:53:32 +00:00
|
|
|
static int
|
|
|
|
srunner_run_unchecked_setup (SRunner * sr, TCase * tc)
|
|
|
|
{
|
|
|
|
TestResult *tr = NULL;
|
|
|
|
int rval = 1;
|
|
|
|
|
|
|
|
set_fork_status (CK_NOFORK);
|
|
|
|
tr = srunner_run_setup (tc->unch_sflst, CK_NOFORK, tc->name,
|
|
|
|
"unchecked_setup");
|
|
|
|
set_fork_status (srunner_fork_status (sr));
|
|
|
|
|
|
|
|
if (tr != NULL && tr->rtype != CK_PASS) {
|
|
|
|
srunner_add_failure (sr, tr);
|
|
|
|
rval = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
return rval;
|
|
|
|
}
|
|
|
|
|
|
|
|
static TestResult *
|
|
|
|
tcase_run_checked_setup (SRunner * sr, TCase * tc)
|
|
|
|
{
|
|
|
|
TestResult *tr = srunner_run_setup (tc->ch_sflst, srunner_fork_status (sr),
|
|
|
|
tc->name, "checked_setup");
|
|
|
|
|
|
|
|
return tr;
|
|
|
|
}
|
|
|
|
|
2009-10-12 11:49:35 +00:00
|
|
|
static void
|
2014-11-15 11:53:32 +00:00
|
|
|
srunner_run_teardown (List * fixture_list, enum fork_status fork_usage)
|
2009-07-16 17:39:16 +00:00
|
|
|
{
|
2014-11-15 11:53:32 +00:00
|
|
|
Fixture *fixture;
|
2009-07-16 17:39:16 +00:00
|
|
|
|
2014-11-15 11:53:32 +00:00
|
|
|
for (check_list_front (fixture_list); !check_list_at_end (fixture_list);
|
|
|
|
check_list_advance (fixture_list)) {
|
|
|
|
fixture = (Fixture *) check_list_val (fixture_list);
|
2009-12-17 20:09:48 +00:00
|
|
|
send_ctx_info (CK_CTX_TEARDOWN);
|
2014-11-15 11:53:32 +00:00
|
|
|
|
|
|
|
if (fork_usage == CK_NOFORK) {
|
|
|
|
if (0 == setjmp (error_jmp_buffer)) {
|
|
|
|
fixture->fun ();
|
|
|
|
} else {
|
|
|
|
/* Abort the remaining teardowns */
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
fixture->fun ();
|
|
|
|
}
|
2009-07-16 17:39:16 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-10-12 11:49:35 +00:00
|
|
|
static void
|
2014-11-15 11:53:32 +00:00
|
|
|
srunner_run_unchecked_teardown (SRunner * sr, TCase * tc)
|
2009-07-16 17:39:16 +00:00
|
|
|
{
|
2014-11-15 11:53:32 +00:00
|
|
|
srunner_run_teardown (tc->unch_tflst, srunner_fork_status (sr));
|
2009-12-17 20:09:48 +00:00
|
|
|
}
|
2009-10-12 11:49:35 +00:00
|
|
|
|
2009-12-17 20:09:48 +00:00
|
|
|
static void
|
|
|
|
tcase_run_checked_teardown (TCase * tc)
|
|
|
|
{
|
2014-11-15 11:53:32 +00:00
|
|
|
srunner_run_teardown (tc->ch_tflst, CK_NOFORK);
|
2009-07-16 17:39:16 +00:00
|
|
|
}
|
|
|
|
|
2009-10-12 11:49:35 +00:00
|
|
|
static void
|
|
|
|
srunner_run_tcase (SRunner * sr, TCase * tc)
|
2009-07-16 17:39:16 +00:00
|
|
|
{
|
2009-10-12 11:49:35 +00:00
|
|
|
if (srunner_run_unchecked_setup (sr, tc)) {
|
|
|
|
srunner_iterate_tcase_tfuns (sr, tc);
|
2014-11-15 11:53:32 +00:00
|
|
|
srunner_run_unchecked_teardown (sr, tc);
|
2009-07-16 17:39:16 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-10-12 11:49:35 +00:00
|
|
|
static TestResult *
|
2009-12-17 20:09:48 +00:00
|
|
|
tcase_run_tfun_nofork (SRunner * sr, TCase * tc, TF * tfun, int i)
|
2009-07-16 17:39:16 +00:00
|
|
|
{
|
|
|
|
TestResult *tr;
|
2014-11-15 11:53:32 +00:00
|
|
|
struct timespec ts_start = { 0, 0 }, ts_end = {
|
|
|
|
0, 0};
|
2009-07-16 17:39:16 +00:00
|
|
|
|
2009-12-17 20:09:48 +00:00
|
|
|
tr = tcase_run_checked_setup (sr, tc);
|
|
|
|
if (tr == NULL) {
|
2014-11-15 11:53:32 +00:00
|
|
|
clock_gettime (check_get_clockid (), &ts_start);
|
|
|
|
if (0 == setjmp (error_jmp_buffer)) {
|
|
|
|
tfun->fn (i);
|
|
|
|
}
|
|
|
|
clock_gettime (check_get_clockid (), &ts_end);
|
2009-12-17 20:09:48 +00:00
|
|
|
tcase_run_checked_teardown (tc);
|
2014-11-15 11:53:32 +00:00
|
|
|
return receive_result_info_nofork (tc->name, tfun->name, i,
|
|
|
|
DIFF_IN_USEC (ts_start, ts_end));
|
2009-12-17 20:09:48 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return tr;
|
|
|
|
}
|
|
|
|
|
|
|
|
static TestResult *
|
2014-11-15 11:53:32 +00:00
|
|
|
receive_result_info_nofork (const char *tcname,
|
|
|
|
const char *tname, int iter, int duration)
|
2009-12-17 20:09:48 +00:00
|
|
|
{
|
|
|
|
TestResult *tr;
|
|
|
|
|
|
|
|
tr = receive_test_result (0);
|
2014-11-15 11:53:32 +00:00
|
|
|
if (tr == NULL) {
|
2009-10-12 11:49:35 +00:00
|
|
|
eprintf ("Failed to receive test result", __FILE__, __LINE__);
|
2014-11-15 11:53:32 +00:00
|
|
|
} else {
|
|
|
|
tr->tcname = tcname;
|
|
|
|
tr->tname = tname;
|
|
|
|
tr->iter = iter;
|
|
|
|
tr->duration = duration;
|
|
|
|
set_nofork_info (tr);
|
|
|
|
}
|
2009-07-16 17:39:16 +00:00
|
|
|
|
|
|
|
return tr;
|
|
|
|
}
|
|
|
|
|
2009-12-17 20:09:48 +00:00
|
|
|
static void
|
|
|
|
set_nofork_info (TestResult * tr)
|
|
|
|
{
|
|
|
|
if (tr->msg == NULL) {
|
|
|
|
tr->rtype = CK_PASS;
|
|
|
|
tr->msg = pass_msg ();
|
|
|
|
} else {
|
|
|
|
tr->rtype = CK_FAILURE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static char *
|
|
|
|
pass_msg (void)
|
|
|
|
{
|
2014-11-15 11:53:32 +00:00
|
|
|
return strdup ("Passed");
|
2009-12-17 20:09:48 +00:00
|
|
|
}
|
|
|
|
|
2014-11-15 11:53:32 +00:00
|
|
|
#if defined(HAVE_FORK) && HAVE_FORK==1
|
2009-10-12 11:49:35 +00:00
|
|
|
static TestResult *
|
2009-12-17 20:09:48 +00:00
|
|
|
tcase_run_tfun_fork (SRunner * sr, TCase * tc, TF * tfun, int i)
|
|
|
|
{
|
|
|
|
pid_t pid_w;
|
|
|
|
pid_t pid;
|
|
|
|
int status = 0;
|
2014-11-15 11:53:32 +00:00
|
|
|
struct timespec ts_start = { 0, 0 }, ts_end = {
|
|
|
|
0, 0};
|
|
|
|
|
|
|
|
timer_t timerid;
|
|
|
|
struct itimerspec timer_spec;
|
|
|
|
TestResult *tr;
|
|
|
|
|
2009-12-17 20:09:48 +00:00
|
|
|
|
|
|
|
pid = fork ();
|
|
|
|
if (pid == -1)
|
|
|
|
eprintf ("Error in call to fork:", __FILE__, __LINE__ - 2);
|
|
|
|
if (pid == 0) {
|
|
|
|
setpgid (0, 0);
|
|
|
|
group_pid = getpgrp ();
|
2014-11-15 11:53:32 +00:00
|
|
|
tr = tcase_run_checked_setup (sr, tc);
|
|
|
|
free (tr);
|
|
|
|
clock_gettime (check_get_clockid (), &ts_start);
|
2009-12-17 20:09:48 +00:00
|
|
|
tfun->fn (i);
|
2014-11-15 11:53:32 +00:00
|
|
|
clock_gettime (check_get_clockid (), &ts_end);
|
2009-12-17 20:09:48 +00:00
|
|
|
tcase_run_checked_teardown (tc);
|
2014-11-15 11:53:32 +00:00
|
|
|
send_duration_info (DIFF_IN_USEC (ts_start, ts_end));
|
2009-12-17 20:09:48 +00:00
|
|
|
exit (EXIT_SUCCESS);
|
|
|
|
} else {
|
|
|
|
group_pid = pid;
|
|
|
|
}
|
|
|
|
|
|
|
|
alarm_received = 0;
|
2014-11-15 11:53:32 +00:00
|
|
|
|
|
|
|
if (timer_create (check_get_clockid (),
|
|
|
|
NULL /* fire SIGALRM if timer expires */ ,
|
|
|
|
&timerid) == 0) {
|
|
|
|
/* Set the timer to fire once */
|
|
|
|
timer_spec.it_value = tc->timeout;
|
|
|
|
timer_spec.it_interval.tv_sec = 0;
|
|
|
|
timer_spec.it_interval.tv_nsec = 0;
|
|
|
|
if (timer_settime (timerid, 0, &timer_spec, NULL) == 0) {
|
|
|
|
do {
|
|
|
|
pid_w = waitpid (pid, &status, 0);
|
|
|
|
}
|
|
|
|
while (pid_w == -1);
|
|
|
|
} else {
|
|
|
|
eprintf ("Error in call to timer_settime:", __FILE__, __LINE__);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* If the timer has not fired, disable it */
|
|
|
|
timer_delete (timerid);
|
|
|
|
} else {
|
|
|
|
eprintf ("Error in call to timer_create:", __FILE__, __LINE__);
|
|
|
|
}
|
2009-12-17 20:09:48 +00:00
|
|
|
|
|
|
|
killpg (pid, SIGKILL); /* Kill remaining processes. */
|
|
|
|
|
|
|
|
return receive_result_info_fork (tc->name, tfun->name, i, status,
|
|
|
|
tfun->signal, tfun->allowed_exit_value);
|
|
|
|
}
|
|
|
|
|
|
|
|
static TestResult *
|
|
|
|
receive_result_info_fork (const char *tcname,
|
|
|
|
const char *tname,
|
2014-11-15 11:53:32 +00:00
|
|
|
int iter, int status, int expected_signal, signed char allowed_exit_value)
|
2009-07-16 17:39:16 +00:00
|
|
|
{
|
|
|
|
TestResult *tr;
|
|
|
|
|
2009-12-17 20:09:48 +00:00
|
|
|
tr = receive_test_result (waserror (status, expected_signal));
|
2014-11-15 11:53:32 +00:00
|
|
|
if (tr == NULL) {
|
2009-10-12 11:49:35 +00:00
|
|
|
eprintf ("Failed to receive test result", __FILE__, __LINE__);
|
2014-11-15 11:53:32 +00:00
|
|
|
} else {
|
|
|
|
tr->tcname = tcname;
|
|
|
|
tr->tname = tname;
|
|
|
|
tr->iter = iter;
|
|
|
|
set_fork_info (tr, status, expected_signal, allowed_exit_value);
|
|
|
|
}
|
2009-07-16 17:39:16 +00:00
|
|
|
|
|
|
|
return tr;
|
|
|
|
}
|
|
|
|
|
2009-10-12 11:49:35 +00:00
|
|
|
static void
|
2009-12-17 20:09:48 +00:00
|
|
|
set_fork_info (TestResult * tr, int status, int signal_expected,
|
2014-11-15 11:53:32 +00:00
|
|
|
signed char allowed_exit_value)
|
2009-07-16 17:39:16 +00:00
|
|
|
{
|
2009-10-12 11:49:35 +00:00
|
|
|
int was_sig = WIFSIGNALED (status);
|
|
|
|
int was_exit = WIFEXITED (status);
|
2014-11-15 11:53:32 +00:00
|
|
|
signed char exit_status = WEXITSTATUS (status);
|
2009-10-12 11:49:35 +00:00
|
|
|
int signal_received = WTERMSIG (status);
|
2009-07-16 17:39:16 +00:00
|
|
|
|
|
|
|
if (was_sig) {
|
|
|
|
if (signal_expected == signal_received) {
|
|
|
|
if (alarm_received) {
|
|
|
|
/* Got alarm instead of signal */
|
|
|
|
tr->rtype = CK_ERROR;
|
2014-11-15 11:53:32 +00:00
|
|
|
if (tr->msg != NULL) {
|
|
|
|
free (tr->msg);
|
|
|
|
}
|
2009-10-12 11:49:35 +00:00
|
|
|
tr->msg = signal_error_msg (signal_received, signal_expected);
|
2009-07-16 17:39:16 +00:00
|
|
|
} else {
|
|
|
|
tr->rtype = CK_PASS;
|
2014-11-15 11:53:32 +00:00
|
|
|
if (tr->msg != NULL) {
|
|
|
|
free (tr->msg);
|
|
|
|
}
|
2009-10-12 11:49:35 +00:00
|
|
|
tr->msg = pass_msg ();
|
2009-07-16 17:39:16 +00:00
|
|
|
}
|
|
|
|
} else if (signal_expected != 0) {
|
|
|
|
/* signal received, but not the expected one */
|
|
|
|
tr->rtype = CK_ERROR;
|
2014-11-15 11:53:32 +00:00
|
|
|
if (tr->msg != NULL) {
|
|
|
|
free (tr->msg);
|
|
|
|
}
|
2009-10-12 11:49:35 +00:00
|
|
|
tr->msg = signal_error_msg (signal_received, signal_expected);
|
2009-07-16 17:39:16 +00:00
|
|
|
} else {
|
|
|
|
/* signal received and none expected */
|
|
|
|
tr->rtype = CK_ERROR;
|
2014-11-15 11:53:32 +00:00
|
|
|
if (tr->msg != NULL) {
|
|
|
|
free (tr->msg);
|
|
|
|
}
|
2009-10-12 11:49:35 +00:00
|
|
|
tr->msg = signal_msg (signal_received);
|
2009-07-16 17:39:16 +00:00
|
|
|
}
|
|
|
|
} else if (signal_expected == 0) {
|
2009-12-17 20:09:48 +00:00
|
|
|
if (was_exit && exit_status == allowed_exit_value) {
|
2009-07-16 17:39:16 +00:00
|
|
|
tr->rtype = CK_PASS;
|
2014-11-15 11:53:32 +00:00
|
|
|
if (tr->msg != NULL) {
|
|
|
|
free (tr->msg);
|
|
|
|
}
|
2009-10-12 11:49:35 +00:00
|
|
|
tr->msg = pass_msg ();
|
2009-12-17 20:09:48 +00:00
|
|
|
} else if (was_exit && exit_status != allowed_exit_value) {
|
2009-10-12 11:49:35 +00:00
|
|
|
if (tr->msg == NULL) { /* early exit */
|
2009-07-16 17:39:16 +00:00
|
|
|
tr->rtype = CK_ERROR;
|
2009-10-12 11:49:35 +00:00
|
|
|
tr->msg = exit_msg (exit_status);
|
2009-07-16 17:39:16 +00:00
|
|
|
} else {
|
|
|
|
tr->rtype = CK_FAILURE;
|
|
|
|
}
|
|
|
|
}
|
2009-10-12 11:49:35 +00:00
|
|
|
} else { /* a signal was expected and none raised */
|
2009-07-16 17:39:16 +00:00
|
|
|
if (was_exit) {
|
2014-11-15 11:53:32 +00:00
|
|
|
if (tr->msg != NULL) {
|
|
|
|
free (tr->msg);
|
|
|
|
}
|
2009-10-12 11:49:35 +00:00
|
|
|
tr->msg = exit_msg (exit_status);
|
2015-03-10 16:42:44 +00:00
|
|
|
tr->rtype = CK_FAILURE; /* normal exit status */
|
2009-07-16 17:39:16 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-12-17 20:09:48 +00:00
|
|
|
static char *
|
|
|
|
signal_msg (int signal)
|
2009-07-16 17:39:16 +00:00
|
|
|
{
|
2014-11-15 11:53:32 +00:00
|
|
|
char *msg = (char *) emalloc (MSG_LEN); /* free'd by caller */
|
|
|
|
|
2009-12-17 20:09:48 +00:00
|
|
|
if (alarm_received) {
|
|
|
|
snprintf (msg, MSG_LEN, "Test timeout expired");
|
2009-07-16 17:39:16 +00:00
|
|
|
} else {
|
2009-12-17 20:09:48 +00:00
|
|
|
snprintf (msg, MSG_LEN, "Received signal %d (%s)",
|
|
|
|
signal, strsignal (signal));
|
2009-07-16 17:39:16 +00:00
|
|
|
}
|
2009-12-17 20:09:48 +00:00
|
|
|
return msg;
|
2009-07-16 17:39:16 +00:00
|
|
|
}
|
|
|
|
|
2009-10-12 11:49:35 +00:00
|
|
|
static char *
|
|
|
|
signal_error_msg (int signal_received, int signal_expected)
|
2009-07-16 17:39:16 +00:00
|
|
|
{
|
|
|
|
char *sig_r_str;
|
|
|
|
char *sig_e_str;
|
2014-11-15 11:53:32 +00:00
|
|
|
char *msg = (char *) emalloc (MSG_LEN); /* free'd by caller */
|
|
|
|
|
2009-10-12 11:49:35 +00:00
|
|
|
sig_r_str = strdup (strsignal (signal_received));
|
|
|
|
sig_e_str = strdup (strsignal (signal_expected));
|
2009-07-16 17:39:16 +00:00
|
|
|
if (alarm_received) {
|
2014-11-15 11:53:32 +00:00
|
|
|
snprintf (msg, MSG_LEN,
|
|
|
|
"Test timeout expired, expected signal %d (%s)",
|
2009-10-12 11:49:35 +00:00
|
|
|
signal_expected, sig_e_str);
|
2009-07-16 17:39:16 +00:00
|
|
|
} else {
|
|
|
|
snprintf (msg, MSG_LEN, "Received signal %d (%s), expected %d (%s)",
|
2009-10-12 11:49:35 +00:00
|
|
|
signal_received, sig_r_str, signal_expected, sig_e_str);
|
2009-07-16 17:39:16 +00:00
|
|
|
}
|
2009-10-12 11:49:35 +00:00
|
|
|
free (sig_r_str);
|
|
|
|
free (sig_e_str);
|
2009-07-16 17:39:16 +00:00
|
|
|
return msg;
|
|
|
|
}
|
|
|
|
|
2009-10-12 11:49:35 +00:00
|
|
|
static char *
|
|
|
|
exit_msg (int exitval)
|
2009-07-16 17:39:16 +00:00
|
|
|
{
|
2014-11-15 11:53:32 +00:00
|
|
|
char *msg = (char *) emalloc (MSG_LEN); /* free'd by caller */
|
|
|
|
|
2009-10-12 11:49:35 +00:00
|
|
|
snprintf (msg, MSG_LEN, "Early exit with return value %d", exitval);
|
2009-07-16 17:39:16 +00:00
|
|
|
return msg;
|
|
|
|
}
|
|
|
|
|
2009-12-17 20:09:48 +00:00
|
|
|
static int
|
|
|
|
waserror (int status, int signal_expected)
|
2009-07-16 17:39:16 +00:00
|
|
|
{
|
2009-12-17 20:09:48 +00:00
|
|
|
int was_sig = WIFSIGNALED (status);
|
|
|
|
int was_exit = WIFEXITED (status);
|
|
|
|
int exit_status = WEXITSTATUS (status);
|
|
|
|
int signal_received = WTERMSIG (status);
|
|
|
|
|
|
|
|
return ((was_sig && (signal_received != signal_expected)) ||
|
|
|
|
(was_exit && exit_status != 0));
|
2009-07-16 17:39:16 +00:00
|
|
|
}
|
2014-11-15 11:53:32 +00:00
|
|
|
#endif /* HAVE_FORK */
|
2009-07-16 17:39:16 +00:00
|
|
|
|
2009-10-12 11:49:35 +00:00
|
|
|
enum fork_status
|
|
|
|
srunner_fork_status (SRunner * sr)
|
2009-07-16 17:39:16 +00:00
|
|
|
{
|
|
|
|
if (sr->fstat == CK_FORK_GETENV) {
|
|
|
|
char *env = getenv ("CK_FORK");
|
2014-11-15 11:53:32 +00:00
|
|
|
|
2009-07-16 17:39:16 +00:00
|
|
|
if (env == NULL)
|
2014-11-15 11:53:32 +00:00
|
|
|
#if defined(HAVE_FORK) && HAVE_FORK==1
|
2009-07-16 17:39:16 +00:00
|
|
|
return CK_FORK;
|
2014-11-15 11:53:32 +00:00
|
|
|
#else
|
|
|
|
return CK_NOFORK;
|
|
|
|
#endif
|
2009-10-12 11:49:35 +00:00
|
|
|
if (strcmp (env, "no") == 0)
|
2009-07-16 17:39:16 +00:00
|
|
|
return CK_NOFORK;
|
2009-12-17 20:09:48 +00:00
|
|
|
else {
|
2014-11-15 11:53:32 +00:00
|
|
|
#if defined(HAVE_FORK) && HAVE_FORK==1
|
2009-07-16 17:39:16 +00:00
|
|
|
return CK_FORK;
|
2014-11-15 11:53:32 +00:00
|
|
|
#else /* HAVE_FORK */
|
2009-12-17 20:09:48 +00:00
|
|
|
eprintf ("This version does not support fork", __FILE__, __LINE__);
|
|
|
|
return CK_NOFORK;
|
2014-11-15 11:53:32 +00:00
|
|
|
#endif /* HAVE_FORK */
|
2009-12-17 20:09:48 +00:00
|
|
|
}
|
2009-07-16 17:39:16 +00:00
|
|
|
} else
|
|
|
|
return sr->fstat;
|
|
|
|
}
|
|
|
|
|
2009-10-12 11:49:35 +00:00
|
|
|
void
|
|
|
|
srunner_set_fork_status (SRunner * sr, enum fork_status fstat)
|
2009-07-16 17:39:16 +00:00
|
|
|
{
|
2014-11-15 11:53:32 +00:00
|
|
|
#if !defined(HAVE_FORK) || HAVE_FORK==0
|
|
|
|
/* If fork() is unavailable, do not allow a fork mode to be set */
|
|
|
|
if (fstat != CK_NOFORK) {
|
|
|
|
eprintf ("This version does not support fork", __FILE__, __LINE__);
|
|
|
|
}
|
|
|
|
#endif /* ! HAVE_FORK */
|
2009-07-16 17:39:16 +00:00
|
|
|
sr->fstat = fstat;
|
|
|
|
}
|
|
|
|
|
2009-12-17 20:09:48 +00:00
|
|
|
void
|
|
|
|
srunner_run_all (SRunner * sr, enum print_output print_mode)
|
|
|
|
{
|
2014-11-15 11:53:32 +00:00
|
|
|
srunner_run (sr, NULL, /* All test suites. */
|
|
|
|
NULL, /* All test cases. */
|
|
|
|
print_mode);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
srunner_run (SRunner * sr, const char *sname, const char *tcname,
|
|
|
|
enum print_output print_mode)
|
|
|
|
{
|
2015-03-21 14:19:43 +00:00
|
|
|
/* Get the selected test suite and test case from the
|
2014-11-15 11:53:32 +00:00
|
|
|
environment. */
|
|
|
|
if (!tcname)
|
|
|
|
tcname = getenv ("CK_RUN_CASE");
|
|
|
|
if (!sname)
|
|
|
|
sname = getenv ("CK_RUN_SUITE");
|
2009-12-17 20:09:48 +00:00
|
|
|
|
|
|
|
if (sr == NULL)
|
|
|
|
return;
|
|
|
|
if (print_mode >= CK_LAST) {
|
|
|
|
eprintf ("Bad print_mode argument to srunner_run_all: %d",
|
|
|
|
__FILE__, __LINE__, print_mode);
|
|
|
|
}
|
2014-11-15 11:53:32 +00:00
|
|
|
#if defined(HAVE_SIGACTION) && defined(HAVE_FORK)
|
2009-12-17 20:09:48 +00:00
|
|
|
memset (&new_action, 0, sizeof new_action);
|
2015-03-21 14:19:43 +00:00
|
|
|
new_action[0].sa_handler = sig_handler;
|
|
|
|
sigaction (SIGALRM, &new_action[0], &old_action[0]);
|
|
|
|
new_action[1].sa_handler = sig_handler;
|
|
|
|
sigaction (SIGINT, &new_action[1], &old_action[1]);
|
|
|
|
new_action[2].sa_handler = sig_handler;
|
|
|
|
sigaction (SIGTERM, &new_action[2], &old_action[2]);
|
2014-11-15 11:53:32 +00:00
|
|
|
#endif /* HAVE_SIGACTION && HAVE_FORK */
|
2009-12-17 20:09:48 +00:00
|
|
|
srunner_run_init (sr, print_mode);
|
2014-11-15 11:53:32 +00:00
|
|
|
srunner_iterate_suites (sr, sname, tcname, print_mode);
|
2009-12-17 20:09:48 +00:00
|
|
|
srunner_run_end (sr, print_mode);
|
2014-11-15 11:53:32 +00:00
|
|
|
#if defined(HAVE_SIGACTION) && defined(HAVE_FORK)
|
2015-03-21 14:19:43 +00:00
|
|
|
sigaction (SIGALRM, &old_action[0], NULL);
|
|
|
|
sigaction (SIGINT, &old_action[1], NULL);
|
|
|
|
sigaction (SIGTERM, &old_action[2], NULL);
|
2014-11-15 11:53:32 +00:00
|
|
|
#endif /* HAVE_SIGACTION && HAVE_FORK */
|
2009-12-17 20:09:48 +00:00
|
|
|
}
|
|
|
|
|
2009-10-12 11:49:35 +00:00
|
|
|
pid_t
|
|
|
|
check_fork (void)
|
2009-07-16 17:39:16 +00:00
|
|
|
{
|
2014-11-15 11:53:32 +00:00
|
|
|
#if defined(HAVE_FORK) && HAVE_FORK==1
|
2009-10-12 11:49:35 +00:00
|
|
|
pid_t pid = fork ();
|
2014-11-15 11:53:32 +00:00
|
|
|
|
2009-07-16 17:39:16 +00:00
|
|
|
/* Set the process to a process group to be able to kill it easily. */
|
2014-11-15 11:53:32 +00:00
|
|
|
if (pid >= 0) {
|
|
|
|
setpgid (pid, group_pid);
|
|
|
|
}
|
2009-07-16 17:39:16 +00:00
|
|
|
return pid;
|
2014-11-15 11:53:32 +00:00
|
|
|
#else /* HAVE_FORK */
|
2009-12-17 20:09:48 +00:00
|
|
|
eprintf ("This version does not support fork", __FILE__, __LINE__);
|
|
|
|
return 0;
|
2014-11-15 11:53:32 +00:00
|
|
|
#endif /* HAVE_FORK */
|
2009-07-16 17:39:16 +00:00
|
|
|
}
|
|
|
|
|
2009-10-12 11:49:35 +00:00
|
|
|
void
|
2009-12-17 20:09:48 +00:00
|
|
|
check_waitpid_and_exit (pid_t pid CK_ATTRIBUTE_UNUSED)
|
2009-07-16 17:39:16 +00:00
|
|
|
{
|
2014-11-15 11:53:32 +00:00
|
|
|
#if defined(HAVE_FORK) && HAVE_FORK==1
|
2009-07-16 17:39:16 +00:00
|
|
|
pid_t pid_w;
|
|
|
|
int status;
|
|
|
|
|
|
|
|
if (pid > 0) {
|
|
|
|
do {
|
2009-10-12 11:49:35 +00:00
|
|
|
pid_w = waitpid (pid, &status, 0);
|
2014-11-15 11:53:32 +00:00
|
|
|
}
|
|
|
|
while (pid_w == -1);
|
2009-12-17 20:09:48 +00:00
|
|
|
if (waserror (status, 0)) {
|
2009-10-12 11:49:35 +00:00
|
|
|
exit (EXIT_FAILURE);
|
2009-12-17 20:09:48 +00:00
|
|
|
}
|
2009-07-16 17:39:16 +00:00
|
|
|
}
|
2009-10-12 11:49:35 +00:00
|
|
|
exit (EXIT_SUCCESS);
|
2014-11-15 11:53:32 +00:00
|
|
|
#else /* HAVE_FORK */
|
2009-12-17 20:09:48 +00:00
|
|
|
eprintf ("This version does not support fork", __FILE__, __LINE__);
|
2014-11-15 11:53:32 +00:00
|
|
|
#endif /* HAVE_FORK */
|
2009-07-16 17:39:16 +00:00
|
|
|
}
|