WSL/test/linux/unit_tests/waitpid.c
Josh Soref 6d18bf5074
Spelling (#13289)
* link: report them to the Microsoft Security Response Center (MSRC)

Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com>

* link: Microsoft Security Response Center

Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com>

* spelling: , or

Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com>

* spelling: adapters

Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com>

* spelling: at

Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com>

* spelling: attaching them to the bug, please send them to wsl-gh-logs@microsoft.com with the github issue number in the subject, and include a link to your github issue comment in the message body.

Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com>

* spelling: disk

Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com>

* spelling: effect

Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com>

* spelling: for the file

Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com>

* spelling: for the mount to be available

Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com>

* spelling: grandchild

Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com>

* spelling: last pid

Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com>

* spelling: nonexistent

Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com>

* spelling: on which the...

Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com>

* spelling: path

Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com>

* spelling: pseudo

Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com>

* spelling: script

Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com>

* spelling: service, the

Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com>

* spelling: session

Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com>

* spelling: strip

Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com>

* spelling: that a

Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com>

* spelling: that the

Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com>

* spelling: the first one we find in the test

Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com>

* spelling: to which

Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com>

* spelling: waitpid

Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com>

* spelling: whether

Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com>

* spelling: while the relay was being set up,

Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com>

* spelling: with an

Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com>

* spelling: without specifying

Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com>

---------

Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com>
Co-authored-by: Ben Hillis <benhillis@gmail.com>
2025-07-29 11:24:39 -07:00

1701 lines
44 KiB
C

/*++
Copyright (c) Microsoft. All rights reserved.
Module Name:
WaitPid.c
Abstract:
This file is a WaitPid test.
--*/
#include "lxtcommon.h"
#include "unittests.h"
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/wait.h>
#include <sys/resource.h>
#include <sys/syscall.h>
#include <sys/prctl.h>
#include <stdio.h>
#include <stdlib.h>
#include "lxtutil.h"
#define LXT_NAME "WaitPid"
#define WAITPID_DEFAULT_WAIT_TIMEOUT_US 100000
#define WAITPID_DEFAULT_WAIT_COUNT 20
#define WAITPID_THREADGROUP_LEADER_UID 1044
#define WAITPID_PTHREAD_UID 1055
bool g_VmMode = false;
int GetPPidPoll(pid_t ExpectedPid);
LXT_VARIATION_HANDLER WaitPidVariationExitStatusBlock;
int WaitPidVariationExitStatusHelper(PLXT_ARGS Args, int Blocking);
LXT_VARIATION_HANDLER WaitPidVariationExitStatusPoll;
LXT_VARIATION_HANDLER WaitPidVariationInitPid;
LXT_VARIATION_HANDLER WaitPidVariationParentChild;
LXT_VARIATION_HANDLER WaitPidVariationProcessGroup;
LXT_VARIATION_HANDLER WaitPidVariationInvalidParameter;
LXT_VARIATION_HANDLER WaitPidVariationWaitId;
LXT_VARIATION_HANDLER WaitPidVariationCloneParent;
LXT_VARIATION_HANDLER WaitPidVariationZombie;
LXT_VARIATION_HANDLER WaitPidVariationZombieStress;
//
// Global constants
//
// FIXME: Enable parent\child test when clone gs issue is resolved.
//
static const LXT_VARIATION g_LxtVariations[] = {
{"WaitPidVariation - Exit status poll", WaitPidVariationExitStatusPoll},
{"WaitPidVariation - Exit status block", WaitPidVariationExitStatusBlock},
{"WaitPidVariation - Init pid", WaitPidVariationInitPid},
{"WaitPidVariation - Process groups", WaitPidVariationProcessGroup},
{"WaitPidVariation - Invalid parameter", WaitPidVariationInvalidParameter},
{"WaitPidVariation - waitid", WaitPidVariationWaitId},
{"WaitPidVariation - CLONE_PARENT", WaitPidVariationCloneParent},
{"WaitPidVariation - zombie support", WaitPidVariationZombie},
{"WaitPidVariation - zombie stress", WaitPidVariationZombieStress}};
int WaitPidTestEntry(int Argc, char* Argv[])
/*++
--*/
{
LXT_ARGS Args;
int Result;
if (LxtWslVersion() == 2)
{
g_VmMode = true;
}
LxtCheckResult(LxtInitialize(Argc, Argv, &Args, LXT_NAME));
LXT_SYNCHRONIZATION_POINT_INIT();
LxtCheckResult(LxtRunVariations(&Args, g_LxtVariations, LXT_COUNT_OF(g_LxtVariations)));
ErrorExit:
LXT_SYNCHRONIZATION_POINT_DESTROY();
LxtUninitialize();
return !LXT_SUCCESS(Result);
}
int GetPPidPoll(pid_t ExpectedPPid)
/*++
--*/
{
pid_t CurrentPPid;
int Result;
int WaitCount;
//
// Wait for the current ppid to reach the expected ppid.
//
for (WaitCount = 0; WaitCount < WAITPID_DEFAULT_WAIT_COUNT; ++WaitCount)
{
CurrentPPid = getppid();
if (CurrentPPid == ExpectedPPid)
{
break;
}
usleep(WAITPID_DEFAULT_WAIT_TIMEOUT_US);
}
if (WaitCount == WAITPID_DEFAULT_WAIT_COUNT)
{
Result = LXT_RESULT_FAILURE;
LxtLogError("Unexpected pid, %d != %d", CurrentPPid, ExpectedPPid);
goto ErrorExit;
}
Result = LXT_RESULT_SUCCESS;
ErrorExit:
return Result;
}
int LxtWaitPidHelper(pid_t ChildPid, int ExpectedWaitStatus, int Blocking)
/*++
--*/
{
int SecondWaitPidStatus;
int Result;
int WaitCount;
int WaitPidResult;
int WaitPidStatus;
if (Blocking == 0)
{
LxtCheckResult(LxtWaitPidPoll(ChildPid, ExpectedWaitStatus));
}
else
{
LxtCheckErrno((WaitPidResult = waitpid(ChildPid, &WaitPidStatus, 0)));
if ((WaitPidStatus & 0xFFFF0000) != 0)
{
Result = LXT_RESULT_FAILURE;
LxtLogError("Unexpected high short status: %d - %d", WaitPidStatus, ExpectedWaitStatus);
goto ErrorExit;
}
if (WaitPidStatus != ExpectedWaitStatus)
{
Result = LXT_RESULT_FAILURE;
LxtLogError("Unexpected status: %d != %d", WaitPidStatus, ExpectedWaitStatus);
goto ErrorExit;
}
if (WIFEXITED(WaitPidStatus) != 0)
{
LxtCheckErrnoFailure(waitpid(ChildPid, &SecondWaitPidStatus, WNOHANG), ECHILD);
}
}
ErrorExit:
return Result;
}
int WaitPidVariationInitPid(PLXT_ARGS Args)
/*++
--*/
{
pid_t ChildPid;
int ExitCodeIndex;
unsigned char ExitCode;
int ExpectedPid;
int ExpectedWaitStatus;
pid_t ParentParentPid;
LXT_PIPE Pipe = {-1, -1};
int Result;
pid_t WaitPidResult;
int WaitPidStatus;
int WaitPipe;
//
// Determine who the subreaper of the test is. On WSL 1 it will be init, on
// WSL 2 it will be the relay process (parent of this process).
//
ExpectedPid = LXT_INIT_PID;
if (g_VmMode != false)
{
ExpectedPid = getppid();
}
LxtCheckResult(LxtCreatePipe(&Pipe));
//
// Check when a parent dies, the child has a parent pid of the subreaper.
//
ExitCode = 0;
LxtCheckErrno(ChildPid = fork());
if (ChildPid == 0)
{
LxtCheckErrno(ChildPid = fork());
if (ChildPid == 0)
{
LxtCheckResult(GetPPidPoll(ExpectedPid));
LxtCheckErrno(write(Pipe.Write, &WaitPipe, sizeof(WaitPipe)));
}
_exit(ExitCode);
}
else
{
LxtCheckResult(LxtWaitPidPoll(-1, ExitCode));
LxtCheckErrno(read(Pipe.Read, &WaitPipe, sizeof(WaitPipe)));
}
//
// The init process should never be a child.
//
LxtCheckErrnoFailure(waitpid(LXT_INIT_PID, &WaitPidStatus, WNOHANG), ECHILD);
ErrorExit:
LxtClosePipe(&Pipe);
return Result;
}
int WaitPidVariationExitStatusBlock(PLXT_ARGS Args)
/*++
--*/
{
return WaitPidVariationExitStatusHelper(Args, 1);
}
int WaitPidVariationExitStatusHelper(PLXT_ARGS Args, int Blocking)
/*++
--*/
{
int ExitCodeIndex;
unsigned char ExitCodes[] = {0, 1, 128, 255};
pid_t ChildPid[LXT_COUNT_OF(ExitCodes)];
int ExpectedWaitStatus;
pid_t ParentParentPid;
pid_t ParentPid;
int Result;
//
// Check that the correct _exit status is returned to a parent process and
// that it can only be checked once serially.
//
ParentParentPid = getppid();
ParentPid = getpid();
for (ExitCodeIndex = 0; ExitCodeIndex < LXT_COUNT_OF(ExitCodes); ++ExitCodeIndex)
{
LxtCheckErrno(ChildPid[ExitCodeIndex] = fork());
if (ChildPid[ExitCodeIndex] == 0)
{
ChildPid[ExitCodeIndex] = getpid();
if (getppid() != ParentPid)
{
Result = LXT_RESULT_FAILURE;
LxtLogError("Unexpected parent pid in child - %d != %d", ChildPid[ExitCodeIndex], ParentPid);
goto ErrorExit;
}
_exit(ExitCodes[ExitCodeIndex]);
}
else
{
ExpectedWaitStatus = ExitCodes[ExitCodeIndex] << 8;
LxtCheckResult(LxtWaitPidHelper(ChildPid[ExitCodeIndex], ExpectedWaitStatus, Blocking));
}
}
//
// Recheck the results after launching the children in parallel.
//
LxtLogInfo("Running forks in parallel...");
for (ExitCodeIndex = 0; ExitCodeIndex < LXT_COUNT_OF(ExitCodes); ++ExitCodeIndex)
{
LxtCheckErrno(ChildPid[ExitCodeIndex] = fork());
if (ChildPid[ExitCodeIndex] == 0)
{
ChildPid[ExitCodeIndex] = getpid();
if (getppid() != ParentPid)
{
Result = LXT_RESULT_FAILURE;
LxtLogError("Unexpected parent pid in child - %d != %d", ChildPid[ExitCodeIndex], ParentPid);
goto ErrorExit;
}
_exit(ExitCodes[ExitCodeIndex]);
}
}
while (ExitCodeIndex--)
{
ExpectedWaitStatus = ExitCodes[ExitCodeIndex] << 8;
LxtCheckResult(LxtWaitPidHelper(ChildPid[ExitCodeIndex], ExpectedWaitStatus, Blocking));
}
ErrorExit:
return Result;
}
int WaitPidVariationExitStatusPoll(PLXT_ARGS Args)
/*++
--*/
{
return WaitPidVariationExitStatusHelper(Args, 0);
}
typedef struct _WAIT_PID_PARENT_DATA
{
int Generation;
pid_t ParentPid;
pid_t ParentTid;
LXT_PIPE Pipes[4];
LXT_CLONE_ARGS CloneArgs[2];
pid_t ForkPid;
} WAIT_PID_PARENT_DATA, *PWAIT_PID_PARENT_DATA;
void WaitPidPrintData(PWAIT_PID_PARENT_DATA CurrentData, char* Message)
{
int Index;
pid_t WaitResult;
int WaitStatus;
//
// TODO: Enable _CLONE
//
LxtLogInfo("***** %s", Message);
WaitStatus = -1;
for (Index = 0; Index < LXT_COUNT_OF(CurrentData->CloneArgs); ++Index)
{
LxtLogInfo("Before waitpid", Message);
WaitResult = waitpid(CurrentData->CloneArgs[Index].CloneId, &WaitStatus, WNOHANG);
LxtLogInfo("%s - Clone %d WNOHANG - %d, %d", Message, CurrentData->CloneArgs[Index].CloneId, WaitResult, WaitStatus);
// WaitResult = waitpid(CurrentData->CloneArgs[Index].CloneId, &WaitStatus, WNOHANG | __WCLONE);
// LxtLogInfo("%s - Clone %d WNOHANG | __WCLONE - %d, %d", Message, CurrentData->CloneArgs[Index].CloneId, WaitResult, WaitStatus);
}
WaitResult = waitpid(CurrentData->ForkPid, &WaitStatus, WNOHANG);
LxtLogInfo("%s - Fork %d WNOHANG - %d, %d", Message, CurrentData->ForkPid, WaitResult, WaitStatus);
// WaitResult = waitpid(CurrentData->ForkPid, &WaitStatus, WNOHANG | __WCLONE);
// LxtLogInfo("%s - Fork %d WNOHANG | __WCLONE - %d, %d", Message, CurrentData->ForkPid, WaitResult, WaitStatus);
LxtLogInfo("***** %s", Message);
return;
}
int WaitPidVariationParentChildClone(void* Parameter)
/*++
--*/
{
int CloneIndex;
PWAIT_PID_PARENT_DATA CurrentData;
pid_t CurrentPid;
pid_t CurrentTid;
int Index;
int ParentIndex;
int Result;
int WaitPipe;
CurrentData = Parameter;
CurrentTid = gettid();
for (Index = 0; Index < LXT_COUNT_OF(CurrentData->CloneArgs); ++Index)
{
if (CurrentData->CloneArgs[Index].CloneId == CurrentTid)
{
CloneIndex = Index;
break;
}
}
if (Index == LXT_COUNT_OF(CurrentData->CloneArgs))
{
Result = LXT_RESULT_FAILURE;
LxtLogError("Unable to find clone tid %d", CurrentTid);
goto ErrorExit;
}
LxtCheckErrno(read(CurrentData->Pipes[CloneIndex].Read, &WaitPipe, sizeof(WaitPipe)));
CurrentPid = getpid();
if (CurrentPid != CurrentData->ParentPid)
{
Result = LXT_RESULT_FAILURE;
LxtLogError("Unexpected pid for clone %d: %d != %d", CloneIndex, CurrentPid, CurrentData->ParentPid);
goto ErrorExit;
}
WaitPidPrintData(CurrentData, "Clone");
ParentIndex = LXT_COUNT_OF(CurrentData->CloneArgs) + 1;
LxtCheckErrno(write(CurrentData->Pipes[ParentIndex].Write, &WaitPipe, sizeof(WaitPipe)));
LxtCheckErrno(read(CurrentData->Pipes[CloneIndex].Read, &WaitPipe, sizeof(WaitPipe)));
ErrorExit:
LxtLogInfo("Clone %d exit", CloneIndex);
return Result;
}
int WaitPidVariationParentChildFork(PWAIT_PID_PARENT_DATA ParentData)
/*++
--*/
{
WAIT_PID_PARENT_DATA CurrentData;
int ForkIndex;
pid_t ParentPid;
int ParentIndex;
int Result;
int WaitPipe;
ForkIndex = LXT_COUNT_OF(ParentData->CloneArgs);
LxtCheckErrno(read(ParentData->Pipes[ForkIndex].Read, &WaitPipe, sizeof(WaitPipe)));
CurrentData.ParentPid = getpid();
CurrentData.ParentTid = gettid();
if (CurrentData.ParentPid != CurrentData.ParentTid)
{
Result = LXT_RESULT_FAILURE;
LxtLogError("Current fork thread is not thread group leader %d != %d", CurrentData.ParentPid, CurrentData.ParentTid);
goto ErrorExit;
}
ParentPid = getppid();
if (ParentPid != ParentData->ParentPid)
{
Result = LXT_RESULT_FAILURE;
LxtLogError("Unexpected ppid %d != %d", ParentPid, ParentData->ParentPid);
goto ErrorExit;
}
if (CurrentData.ParentPid == ParentData->ParentPid)
{
Result = LXT_RESULT_FAILURE;
LxtLogError("Unexpected pid %d == %d", CurrentData.ParentPid, ParentData->ParentPid);
goto ErrorExit;
}
ParentData->ForkPid = getpid();
WaitPidPrintData(ParentData, "Fork");
ParentIndex = LXT_COUNT_OF(ParentData->CloneArgs) + 1;
LxtCheckErrno(write(ParentData->Pipes[ParentIndex].Write, &WaitPipe, sizeof(WaitPipe)));
LxtCheckErrno(read(ParentData->Pipes[ForkIndex].Read, &WaitPipe, sizeof(WaitPipe)));
ErrorExit:
_exit(Result);
}
int WaitPidVariationCreateParentData(PWAIT_PID_PARENT_DATA CurrentData)
/*++
--*/
{
int Index;
int ParentIndex;
int Result;
int WaitPipe;
pid_t WaitResult;
int WaitStatus;
CurrentData->Generation = CurrentData->Generation + 1;
CurrentData->ParentPid = getpid();
CurrentData->ParentTid = gettid();
if (CurrentData->ParentPid != CurrentData->ParentTid)
{
Result = LXT_RESULT_FAILURE;
LxtLogError("Current thread is not thread group leader %d != %d", CurrentData->ParentPid, CurrentData->ParentTid);
goto ErrorExit;
}
for (Index = 0; Index < LXT_COUNT_OF(CurrentData->Pipes); ++Index)
{
LxtCheckResult(LxtCreatePipe(&CurrentData->Pipes[Index]));
}
//
// TODO: Enable _CLONE
//
for (Index = 0; Index < LXT_COUNT_OF(CurrentData->CloneArgs); ++Index)
{
LxtCheckResult(LxtClone(WaitPidVariationParentChildClone, CurrentData, LXT_CLONE_FLAGS_DEFAULT, &CurrentData->CloneArgs[Index]));
}
LxtCheckErrno(CurrentData->ForkPid = fork());
if (CurrentData->ForkPid == 0)
{
WaitPidVariationParentChildFork(CurrentData);
}
WaitPidPrintData(CurrentData, "Parent");
ParentIndex = LXT_COUNT_OF(CurrentData->CloneArgs) + 1;
for (Index = 0; Index < LXT_COUNT_OF(CurrentData->Pipes) - 1; ++Index)
{
LxtCheckErrno(write(CurrentData->Pipes[Index].Write, &WaitPipe, sizeof(WaitPipe)));
LxtCheckErrno(read(CurrentData->Pipes[ParentIndex].Read, &WaitPipe, sizeof(WaitPipe)));
}
//
// Release the waiters
//
for (Index = 0; Index < LXT_COUNT_OF(CurrentData->Pipes) - 1; ++Index)
{
LxtCheckErrno(write(CurrentData->Pipes[Index].Write, &WaitPipe, sizeof(WaitPipe)));
}
ErrorExit:
for (Index = 0; Index < LXT_COUNT_OF(CurrentData->Pipes); ++Index)
{
LxtClosePipe(&CurrentData->Pipes[Index]);
}
return Result;
}
int WaitPidVariationParentChild(PLXT_ARGS Args)
/*++
--*/
{
WAIT_PID_PARENT_DATA CurrentData;
int Result;
memset(&CurrentData, 0, sizeof(CurrentData));
LxtCheckResult(WaitPidVariationCreateParentData(&CurrentData));
ErrorExit:
return Result;
}
int WaitPidVariationProcessGroup(PLXT_ARGS Args)
/*++
Description:
This routine tests waiting on all children in the same process group.
Arguments:
Args - Supplies the command line arguments.
Return Value:
Returns 0 on success, -1 on failure.
--*/
{
const int OtherGroupChildCount = 2;
pid_t OtherGroupChild[OtherGroupChildCount];
int Result;
pid_t SameGroupChild;
int Status;
int WaitResult;
LxtCheckResult(LxtSignalBlock(SIGUSR1));
for (int Child = 0; Child < OtherGroupChildCount; Child++)
{
LxtCheckErrno(OtherGroupChild[Child] = fork());
if (OtherGroupChild[Child] == 0)
{
//
// Change process group, then signal the parent.
//
LxtCheckErrnoZeroSuccess(setpgid(0, 0));
LxtCheckErrnoZeroSuccess(kill(getppid(), SIGUSR1));
_exit(0);
}
//
// Wait to make sure the child changed its process group.
//
LxtCheckResult(LxtSignalWaitBlocked(SIGUSR1, OtherGroupChild[Child], 2));
}
LxtCheckErrno(SameGroupChild = fork());
if (SameGroupChild == 0)
{
_exit(0);
}
//
// Wait for the same process group, which should return the status of the
// second child.
//
LxtCheckResult(WaitResult = LxtWaitPidPoll(0, 0));
LxtCheckEqual(WaitResult, SameGroupChild, "%d");
//
// Wait again, which should fail because there are no more children in this
// process group.
//
LxtCheckErrnoFailure(waitpid(0, &Status, WNOHANG), ECHILD);
//
// Wait on the specific process group of one of the remaining children.
//
LxtCheckResult(WaitResult = LxtWaitPidPoll(-OtherGroupChild[0], 0));
LxtCheckEqual(WaitResult, OtherGroupChild[0], "%d");
//
// Wait on all children, which should return the child in the other process
// group.
//
LxtCheckResult(WaitResult = LxtWaitPidPoll(-1, 0));
LxtCheckEqual(WaitResult, OtherGroupChild[1], "%d");
ErrorExit:
return Result;
}
int WaitPidVariationInvalidParameter(PLXT_ARGS Args)
/*++
Description:
This routine tests invalid parameter handling for the waitpid system call.
Arguments:
Args - Supplies the command line arguments.
Return Value:
Returns 0 on success, -1 on failure.
--*/
{
int Result;
int Status;
LxtCheckErrnoFailure(waitpid(0, &Status, WEXITED), EINVAL);
LxtCheckErrnoFailure(waitpid(0, &Status, WNOWAIT), EINVAL);
LxtCheckErrnoFailure(waitpid(0, NULL, WNOHANG), ECHILD);
LxtCheckErrnoFailure(waitpid(0, (void*)-1, WNOHANG), ECHILD);
ErrorExit:
return Result;
}
void* WaitPidVariationWaitIdThread(void* Parameter)
/*++
Description:
This routine is the thread handler for the waitid test.
Arguments:
Parameter - Supplies the thread parameter.
Return Value:
Returns the thread id on success, -1 on failure.
--*/
{
int Result;
LxtLogInfo("WaitPid child tid %d", gettid());
LxtCheckErrno(LxtSetUid(WAITPID_PTHREAD_UID));
//
// Enter a very long sleep, this will be interrupted when the threadgroup
// leader dies.
//
sleep(-1);
Result = 0;
ErrorExit:
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wint-to-pointer-cast"
return (void*)Result;
#pragma GCC diagnostic pop
}
int WaitPidVariationWaitId(PLXT_ARGS Args)
/*++
Description:
This routine tests the waitid system call.
Arguments:
Args - Supplies the command line arguments.
Return Value:
Returns 0 on success, -1 on failure.
--*/
{
int ChildPid;
int ExpectedStatus;
int Result;
siginfo_t SigInfo;
pthread_t Thread;
struct rusage Usage;
ExpectedStatus = 44;
LxtCheckErrno(ChildPid = fork());
if (ChildPid == 0)
{
//
// Create a child thread, set the uid of the threadgroup leader,
// and exit.
//
LxtCheckResultError(pthread_create(&Thread, NULL, WaitPidVariationWaitIdThread, NULL));
LxtLogInfo("Waitid parent tid %d", gettid());
//
// Briefly sleep to allow the pthread to run.
//
sleep(1);
LxtCheckErrno(LxtSetUid(WAITPID_THREADGROUP_LEADER_UID));
_exit(ExpectedStatus);
}
LxtCheckErrnoZeroSuccess(Result = LxtWaitId(P_ALL, 0, &SigInfo, WEXITED, &Usage));
LxtCheckEqual(Result, 0, "%d");
LxtCheckEqual(SigInfo.si_code, CLD_EXITED, "%d");
LxtCheckEqual(SigInfo.si_status, ExpectedStatus, "%d");
LxtCheckEqual(SigInfo.si_pid, ChildPid, "%d");
LxtCheckEqual(SigInfo.si_uid, WAITPID_THREADGROUP_LEADER_UID, "%d");
//
// Wait for a specific child.
//
LxtCheckErrno(ChildPid = fork());
if (ChildPid == 0)
{
_exit(ExpectedStatus);
}
LxtCheckErrnoZeroSuccess(Result = LxtWaitId(P_PID, ChildPid, &SigInfo, WEXITED, &Usage));
LxtCheckEqual(Result, 0, "%d");
LxtCheckEqual(SigInfo.si_code, CLD_EXITED, "%d");
LxtCheckEqual(SigInfo.si_status, ExpectedStatus, "%d");
LxtCheckEqual(SigInfo.si_pid, ChildPid, "%d");
//
// Wait with WNOHANG specified.
//
// N.B. Parent must sleep to allow the child to exit before waiting.
//
LxtCheckErrno(ChildPid = fork());
if (ChildPid == 0)
{
_exit(ExpectedStatus);
}
sleep(1);
LxtCheckErrnoZeroSuccess(Result = LxtWaitId(P_PID, ChildPid, &SigInfo, (WEXITED | WNOHANG), &Usage));
LxtCheckEqual(Result, 0, "%d");
LxtCheckEqual(SigInfo.si_code, CLD_EXITED, "%d");
LxtCheckEqual(SigInfo.si_status, ExpectedStatus, "%d");
LxtCheckEqual(SigInfo.si_pid, ChildPid, "%d");
//
// Wait with a null siginfo structure.
//
// N.B. The man page states that the pid of the child should be returned but
// this is not the case. The wait should still be consumed so the
// second wait should fail with ECHILD.
//
LxtCheckErrno(ChildPid = fork());
if (ChildPid == 0)
{
_exit(0);
}
LxtCheckErrnoZeroSuccess(Result = LxtWaitId(P_ALL, 0, NULL, WEXITED, NULL));
LxtCheckErrnoFailure(LxtWaitId(P_ALL, 0, NULL, WEXITED, NULL), ECHILD);
//
// Wait with the WNOWAIT option supplied which means the wait is not
// consumed. Wait again to consume the wait and verify it was not consumed
// by the first call.
//
LxtCheckErrno(ChildPid = fork());
if (ChildPid == 0)
{
_exit(ExpectedStatus);
}
LxtCheckErrnoZeroSuccess(Result = LxtWaitId(P_PID, ChildPid, &SigInfo, (WEXITED | WNOWAIT), &Usage));
LxtCheckEqual(Result, 0, "%d");
LxtCheckEqual(SigInfo.si_code, CLD_EXITED, "%d");
LxtCheckEqual(SigInfo.si_status, ExpectedStatus, "%d");
LxtCheckEqual(SigInfo.si_pid, ChildPid, "%d");
LxtCheckErrnoZeroSuccess(Result = LxtWaitId(P_PID, ChildPid, &SigInfo, WEXITED, &Usage));
LxtCheckEqual(Result, 0, "%d");
LxtCheckEqual(SigInfo.si_code, CLD_EXITED, "%d");
LxtCheckEqual(SigInfo.si_status, ExpectedStatus, "%d");
LxtCheckEqual(SigInfo.si_pid, ChildPid, "%d");
//
// Call getrusage with the supported values.
//
LxtCheckErrno(getrusage(RUSAGE_SELF, &Usage));
LxtCheckErrno(getrusage(RUSAGE_THREAD, &Usage));
LxtCheckErrno(getrusage(RUSAGE_CHILDREN, &Usage));
//
// Invalid parameter variations.
//
LxtCheckErrno(ChildPid = fork());
if (ChildPid == 0)
{
_exit(0);
}
LxtCheckErrnoFailure(LxtWaitId(P_ALL, 0, NULL, WNOHANG, NULL), EINVAL);
LxtCheckErrnoFailure(LxtWaitId(-1, 0, NULL, WEXITED, NULL), EINVAL);
LxtCheckErrnoFailure(LxtWaitId(P_PGID + 5, 0, NULL, WEXITED, NULL), EINVAL);
LxtCheckErrnoFailure(LxtWaitId(P_ALL, 0, NULL, 0x10, NULL), EINVAL);
//
// N.B. Providing an invalid pointer to a siginfo structure returns efault
// but consumes the wait.
//
LxtCheckErrnoFailure(LxtWaitId(P_ALL, 0, (void*)-1, WEXITED, NULL), EFAULT);
LxtCheckErrnoFailure(LxtWaitId(P_ALL, 0, NULL, WEXITED, NULL), ECHILD);
LxtCheckErrno(ChildPid = fork());
if (ChildPid == 0)
{
_exit(0);
}
//
// N.B. Providing an invalid pointer to a rusage structure returns efault
// but consumes the wait.
//
LxtCheckErrnoFailure(LxtWaitId(P_ALL, 0, NULL, WEXITED, (void*)-1), EFAULT);
LxtCheckErrnoFailure(LxtWaitId(P_ALL, 0, NULL, WEXITED, NULL), ECHILD);
ErrorExit:
if (ChildPid == 0)
{
_exit(Result);
}
return Result;
}
int WaitPidVariationCloneParentChild(void)
/*++
Description:
This routine is the child process for WaitPidVariationCloneParent.
Arguments:
None..
Return Value:
Returns 0 on success, -1 on failure.
--*/
{
pid_t ChildPid;
pid_t GrandChildParent;
pid_t ChildParent;
int Result;
int WaitPidStatus;
Result = 0;
//
// Create a child process with the CLONE_PARENT flag.
//
// The new process should not be reported as a child.
//
ChildParent = getppid();
LxtLogInfo("ChildParent %d", ChildParent);
LxtCheckResult(ChildPid = LxtCloneSyscall(CLONE_PARENT | SIGCHLD, NULL, NULL, NULL, NULL));
if (ChildPid == 0)
{
GrandChildParent = getppid();
LxtCheckEqual(ChildParent, GrandChildParent, "%d");
LxtLogInfo("Grand child %d exiting", LxtGetTid());
}
else
{
LxtCheckErrnoFailure(waitpid(ChildPid, &WaitPidStatus, 0), ECHILD);
LxtLogInfo("Child %d exiting", LxtGetTid());
}
ErrorExit:
_exit(Result);
}
int WaitPidVariationCloneParent(PLXT_ARGS Args)
/*++
Description:
This routine tests invalid parameter handling for the waitpid system call.
Arguments:
Args - Supplies the command line arguments.
Return Value:
Returns 0 on success, -1 on failure.
--*/
{
pid_t ChildPid;
pid_t GrandChildPid;
int Result;
int Status;
ChildPid = -1;
//
// Create a child process, that in turn creates a grandchild process
// with CLONE_PARENT.
//
LxtCheckErrno(ChildPid = fork());
if (ChildPid == 0)
{
WaitPidVariationCloneParentChild();
}
//
// Wait for the child and the grandchild.
//
LxtCheckResult(LxtWaitPidPoll(ChildPid, 0));
LxtCheckResult(GrandChildPid = LxtWaitPidPoll(0, 0));
LxtLogInfo("Waited on grandchild %d", GrandChildPid);
//
// Check that the same scenario works when parent dies before
// CLONE_PARENT.
//
LxtCheckErrno(ChildPid = fork());
if (ChildPid == 0)
{
LxtCheckErrno(ChildPid = fork());
if (ChildPid == 0)
{
sleep(1);
WaitPidVariationCloneParentChild();
}
_exit(0);
}
//
// Wait for the child and give the grandchild time to finish.
//
LxtCheckResult(LxtWaitPidPoll(ChildPid, 0));
LxtLogInfo("Waited on child %d", ChildPid);
sleep(2);
ErrorExit:
if (ChildPid == 0)
{
_exit(Result);
}
return Result;
}
typedef struct _WAITPID_THREAD_PARAMETERS
{
pid_t ChildPid;
pid_t GrandChildPid1;
pid_t GrandChildPid2;
LXT_SYNCHRONIZATION_POINT_DECLARE_FOR(ChildPid);
LXT_SYNCHRONIZATION_POINT_DECLARE_FOR(GrandChildPid2);
LXT_PIPE Pipe;
int ChildLevel;
int Variation;
} WAITPID_THREAD_PARAMETERS, *PWAITPID_THREAD_PARAMETERS;
int ThreadZombieThread(void* Context)
/*++
Description:
This routine is the thread proc used by WaitPidVariationThreadZombie.
Arguments:
Context - Supplies the thread context.
Return Value:
Returns 0 on success, -1 on failure.
--*/
{
int ChildLevel;
pid_t ChildPid;
int Flags;
pid_t GrandChildPid1;
pid_t GrandChildPid2;
LXT_CLONE_ARGS CloneArgs;
WAITPID_THREAD_PARAMETERS LocalParameter;
PWAITPID_THREAD_PARAMETERS Parameter;
int Result;
int Status;
LXT_SYNCHRONIZATION_POINT_DECLARE_FOR(ChildPid);
LXT_SYNCHRONIZATION_POINT_DECLARE_FOR(GrandChildPid2);
memcpy(&LocalParameter, Context, sizeof(LocalParameter));
ChildPid = 0;
ChildLevel = LocalParameter.ChildLevel;
if (ChildLevel != 0)
{
free(Context);
}
LxtSyncChildPidParent = LocalParameter.LxtSyncChildPidParent;
LxtSyncChildPidChild = LocalParameter.LxtSyncChildPidChild;
GrandChildPid1 = -1;
GrandChildPid2 = -1;
LxtSyncGrandChildPid2Parent = LocalParameter.LxtSyncGrandChildPid2Parent;
LxtSyncGrandChildPid2Child = LocalParameter.LxtSyncGrandChildPid2Child;
Parameter = NULL;
LxtSignalInitializeThread();
if (ChildLevel == 0)
{
LxtLogInfo("Child %d starting, variation = %d...", getpid(), LocalParameter.Variation);
LxtCheckResult(LxtSignalInitialize());
LxtCheckResult(LxtSignalSetupHandler(SIGCHLD, SA_SIGINFO));
LxtCheckErrno(setsid());
switch (LocalParameter.Variation)
{
case 0:
Flags = SIGCHLD;
break;
case 1:
Flags = CLONE_FS | CLONE_FILES | SIGCHLD;
break;
case 2:
Flags = CLONE_FS | CLONE_FILES;
break;
case 3:
Flags = CLONE_THREAD | CLONE_VM | CLONE_SIGHAND | CLONE_FS | CLONE_FILES;
break;
};
Parameter = malloc(sizeof(*Parameter));
if (Parameter != NULL)
{
memcpy(Parameter, &LocalParameter, sizeof(*Parameter));
}
LxtCheckNotEqual(Parameter, NULL, "%p");
Parameter->ChildLevel = 1;
//
// The clone stack is leaked but the current process will exit shortly.
//
LxtCheckErrno(LxtClone(ThreadZombieThread, Parameter, Flags, &CloneArgs));
Parameter = NULL;
GrandChildPid1 = CloneArgs.CloneId;
LocalParameter.GrandChildPid1 = GrandChildPid1;
Parameter = malloc(sizeof(*Parameter));
if (Parameter != NULL)
{
memcpy(Parameter, &LocalParameter, sizeof(*Parameter));
}
LxtCheckNotEqual(Parameter, NULL, "%p");
Parameter->ChildLevel = 2;
//
// The clone stack is leaked but the current process will exit shortly.
//
LxtCheckErrno(LxtClone(ThreadZombieThread, Parameter, Flags, &CloneArgs));
Parameter = NULL;
LocalParameter.GrandChildPid2 = CloneArgs.CloneId;
LxtSignalWait();
if (LocalParameter.Variation < 2)
{
LxtCheckResult(LxtSignalCheckSigChldReceived(CLD_EXITED, GrandChildPid1, getuid(), 0));
}
else if (LocalParameter.Variation != 3)
{
LxtCheckResult(LxtSignalCheckNoSignal());
}
LXT_SYNCHRONIZATION_POINT_CHILD();
LxtCheckErrno(write(LocalParameter.Pipe.Write, &LocalParameter, sizeof(LocalParameter)));
LXT_SYNCHRONIZATION_POINT_CHILD();
//
// Exiting with one zombie child, and one child waiting for an exit
// signal.
//
LxtLogInfo("Exiting pid = %d", getpid());
Result = 0;
goto ErrorExit;
}
else if (ChildLevel == 1)
{
//
// First child created via this thread, grandchild of the original.
//
GrandChildPid1 = 0;
LxtLogInfo("Grandchild %d starting...", getpid());
LxtLogInfo("Exiting pid = %d", getpid());
Result = 0;
goto ErrorExit;
}
else
{
//
// Second child created via this thread, second grandchild of the
// original.
//
GrandChildPid2 = 0;
LxtLogInfo("Grandchild %d starting...", getpid());
LxtCheckResult(LxtSignalInitialize());
LxtCheckResult(LxtSignalSetupHandler(SIGHUP, SA_SIGINFO));
LXT_SYNCHRONIZATION_POINT_CHILD_FOR(GrandChildPid2);
LxtCheckResult(LxtSignalCheckNoSignal());
LxtLogInfo("Exiting pid = %d", getpid());
Result = 0;
goto ErrorExit;
}
ErrorExit:
if (Result != 0)
{
//
// Intentionally orphaning the thread unless there was an error.
//
LXT_SYNCHRONIZATION_POINT_END_FOR(GrandChildPid2, FALSE);
}
if (ChildLevel == 0)
{
if (LocalParameter.Variation != 3)
{
LXT_SYNCHRONIZATION_POINT_END();
}
else
{
LXT_SYNCHRONIZATION_POINT_PTHREAD_END_THREAD();
}
}
if (Parameter != NULL)
{
free(Parameter);
}
syscall(SYS_exit, Result);
}
int WaitPidVariationZombie(PLXT_ARGS Args)
/*++
Description:
This routine tests zombie handling with waitpid system call.
Arguments:
Args - Supplies the command line arguments.
Return Value:
Returns 0 on success, -1 on failure.
--*/
{
char Buffer[50];
LXT_CLONE_ARGS ChildClone;
int ChildDir;
pid_t ChildPid;
FILE* ChildStatusFile;
pthread_t ChildThread;
int GrandChildDir;
pid_t GrandChildPid1;
pid_t GrandChildPid2;
FILE* GrandChildStatusFile;
int Index;
bool IsFork;
char Path[32];
LXT_PIPE Pipe = {-1, -1};
void* PointerResult;
int Result;
struct stat StatBuffer;
int Status;
char StatusDescription[25];
char* StatusFileEntry;
size_t StatusFileEntryLength;
char StatusToken;
WAITPID_THREAD_PARAMETERS ThreadParam;
LXT_SYNCHRONIZATION_POINT_DECLARE_FOR(GrandChildPid2);
ChildDir = -1;
ChildPid = -1;
ChildStatusFile = NULL;
ChildThread = 0;
GrandChildDir = -1;
GrandChildPid1 = -1;
GrandChildPid2 = -1;
GrandChildStatusFile = NULL;
IsFork = true;
StatusFileEntry = NULL;
LXT_SYNCHRONIZATION_POINT_INIT_FOR(GrandChildPid2);
//
// TODO: test needs to be debugged for WSL2.
//
if (g_VmMode != false)
{
Result = 0;
goto ErrorExit;
}
LxtCheckResult(LxtSignalInitialize());
LxtCheckResult(LxtSignalSetupHandler(SIGCHLD, SA_SIGINFO));
LxtCheckResult(LxtSignalIgnore(SIGPIPE));
memset(&ThreadParam, 0, sizeof(ThreadParam));
ThreadParam.LxtSyncChildPidParent = LxtSyncChildPidParent;
ThreadParam.LxtSyncChildPidChild = LxtSyncChildPidChild;
ThreadParam.LxtSyncGrandChildPid2Parent = LxtSyncGrandChildPid2Parent;
ThreadParam.LxtSyncGrandChildPid2Child = LxtSyncGrandChildPid2Child;
for (Index = 0; Index < 4; Index += 1)
{
ChildPid = -1;
GrandChildPid1 = -1;
GrandChildPid2 = -1;
LXT_SYNCHRONIZATION_POINT_START();
LXT_SYNCHRONIZATION_POINT_START_FOR(GrandChildPid2);
ThreadParam.ChildPid = -1;
ThreadParam.GrandChildPid2 = -1;
ThreadParam.GrandChildPid2 = -1;
LxtCheckResult(LxtCreatePipe(&Pipe));
memcpy(&ThreadParam.Pipe, &Pipe, sizeof(Pipe));
ThreadParam.Variation = Index;
LxtCheckErrno(ChildPid = fork());
if (ChildPid == 0)
{
return ThreadZombieThread(&ThreadParam);
}
ThreadParam.ChildPid = ChildPid;
LXT_SYNCHRONIZATION_POINT();
LxtCheckErrno(read(Pipe.Read, &ThreadParam, sizeof(ThreadParam)));
GrandChildPid1 = ThreadParam.GrandChildPid1;
GrandChildPid2 = ThreadParam.GrandChildPid2;
//
// Check basic info of child after one of its children has entered
// zombie state.
//
LxtCheckErrno(sprintf(Path, "/proc/%d", ChildPid));
LxtCheckErrno(ChildDir = open(Path, O_RDONLY));
LxtCheckErrno(fstat(ChildDir, &StatBuffer));
LxtCheckErrno(sprintf(Path, "/proc/%d/status", ChildPid));
LxtCheckNullErrno(ChildStatusFile = fopen(Path, "r"));
while ((Result = getline(&StatusFileEntry, &StatusFileEntryLength, ChildStatusFile)) >= 0)
{
if (strncmp(StatusFileEntry, "State:\t", 7) == 0)
{
break;
}
free(StatusFileEntry);
StatusFileEntry = NULL;
}
LxtCheckErrno(Result);
Result = sscanf(StatusFileEntry, "State: %c (%s)", &StatusToken, StatusDescription);
LxtCheckEqual(Result, 2, "%d");
if (StatusToken == 'R')
{
LxtCheckStringEqual(StatusDescription, "running)");
}
else if (StatusToken == 'S')
{
LxtCheckStringEqual(StatusDescription, "sleeping)");
}
else
{
LxtLogError("Unexpected status: %c (%s)", StatusToken, StatusDescription);
Result = -1;
goto ErrorExit;
}
LxtCheckErrno(getpgid(ChildPid));
LxtCheckErrno(kill(ChildPid, SIGWINCH));
LxtCheckErrno(sprintf(Path, "/proc/%d/ns/mnt", ChildPid));
LxtCheckErrno(readlink(Path, Buffer, sizeof(Buffer)));
LxtCheckErrno(sprintf(Path, "/proc/%d/fd/0", ChildPid));
LxtCheckErrno(stat(Path, &StatBuffer));
//
// Check basic info of first zombie.
//
LxtCheckErrno(sprintf(Path, "/proc/%d", GrandChildPid1));
if (Index != 3)
{
LxtCheckErrno(getpgid(GrandChildPid1));
LxtCheckErrno(kill(GrandChildPid1, SIGWINCH));
LxtCheckErrno(GrandChildDir = open(Path, O_RDONLY));
LxtCheckErrno(fstat(GrandChildDir, &StatBuffer));
LxtCheckErrno(sprintf(Path, "/proc/%d/fd/0", GrandChildPid1));
//
// TODO_LX: Zombied procfs "fd" entry should be accessible but
// empty.
//
// LxtCheckErrnoFailure(stat(Path, &StatBuffer), ENOENT);
//
LxtCheckErrno(sprintf(Path, "/proc/%d/status", GrandChildPid1));
LxtCheckNullErrno(GrandChildStatusFile = fopen(Path, "r"));
free(StatusFileEntry);
StatusFileEntry = NULL;
while ((Result = getline(&StatusFileEntry, &StatusFileEntryLength, GrandChildStatusFile)) >= 0)
{
if (strncmp(StatusFileEntry, "State:\t", 7) == 0)
{
break;
}
free(StatusFileEntry);
StatusFileEntry = NULL;
}
LxtCheckErrno(Result);
Result = sscanf(StatusFileEntry, "State: %c (%s)", &StatusToken, StatusDescription);
LxtCheckEqual(Result, 2, "%d");
LxtCheckEqual(StatusToken, 'Z', "%c");
LxtCheckStringEqual(StatusDescription, "zombie)");
LxtCheckErrno(sprintf(Path, "/proc/%d/fd/0", GrandChildPid1));
//
// TODO_LX: Zombied procfs "fd" entry should be accessible but
// empty.
//
// LxtCheckErrnoFailure(open(Path, O_RDONLY), ENOENT);
//
}
else
{
LxtCheckErrnoFailure(GrandChildDir = open(Path, O_RDONLY), ENOENT);
}
//
// Close the read end of the pipe.
//
close(Pipe.Read);
Pipe.Read = -1;
LxtCheckErrno(write(Pipe.Write, &Result, sizeof(Result)));
//
// Allow the child to exit.
//
LXT_SYNCHRONIZATION_POINT();
LxtSignalWait();
if (Index != 3)
{
LxtCheckResult(LxtSignalCheckSigChldReceived(CLD_EXITED, ChildPid, getuid(), 0));
LxtSignalResetReceived();
}
else
{
//
// The threadgroup leader should have exited but there is still a
// thread running so the signal will not yet be sent.
//
LxtCheckResult(LxtSignalCheckNoSignal());
}
//
// Check basic info of running grandchild after child exit.
//
if (Index != 3)
{
//
// TODO_LX: These calls fail due to lack of proper thread support.
//
LxtCheckErrno(getpgid(GrandChildPid2));
LxtCheckErrno(kill(GrandChildPid2, SIGWINCH));
}
LxtCheckErrno(sprintf(Path, "/proc/%d/ns/mnt", GrandChildPid2));
LxtCheckErrno(readlink(Path, Buffer, sizeof(Buffer)));
LxtCheckErrno(sprintf(Path, "/proc/%d/fd/0", GrandChildPid2));
LxtCheckErrno(stat(Path, &StatBuffer));
//
// Signal last grandchild to exit.
//
LXT_SYNCHRONIZATION_POINT_PARENT_FOR(GrandChildPid2);
LxtSignalWait();
if (Index == 3)
{
LxtCheckResult(LxtSignalCheckSigChldReceived(CLD_EXITED, ChildPid, getuid(), 0));
LxtSignalResetReceived();
}
//
// Check the child information after it is a zombie.
//
LxtCheckErrno(fstat(ChildDir, &StatBuffer));
rewind(ChildStatusFile);
free(StatusFileEntry);
StatusFileEntry = NULL;
while ((Result = getline(&StatusFileEntry, &StatusFileEntryLength, ChildStatusFile)) >= 0)
{
if (strncmp(StatusFileEntry, "State:\t", 7) == 0)
{
break;
}
free(StatusFileEntry);
StatusFileEntry = NULL;
}
LxtCheckErrno(Result);
Result = sscanf(StatusFileEntry, "State: %c (%s)", &StatusToken, StatusDescription);
LxtCheckEqual(Result, 2, "%d");
LxtCheckEqual(StatusToken, 'Z', "%c");
LxtCheckStringEqual(StatusDescription, "zombie)");
LxtCheckErrno(getpgid(ChildPid));
LxtCheckErrno(kill(ChildPid, SIGWINCH));
LxtCheckErrno(sprintf(Path, "/proc/%d/ns/mnt", ChildPid));
LxtCheckErrnoFailure(readlink(Path, Buffer, sizeof(Buffer)), ENOENT);
sleep(2);
LxtCheckErrnoFailure(write(Pipe.Write, &Result, sizeof(Result)), EPIPE);
LXT_SYNCHRONIZATION_POINT_END();
ChildPid = -1;
LxtCheckErrnoFailure(waitpid(-1, &Status, WNOHANG), ECHILD);
LxtCheckEqual(Result, 0, "%d");
LxtClosePipe(&Pipe);
}
ErrorExit:
free(StatusFileEntry);
LXT_SYNCHRONIZATION_POINT_END();
if (GrandChildPid2 > 0)
{
LXT_SYNCHRONIZATION_POINT_PTHREAD_END_THREAD_FOR(GrandChildPid2);
}
LXT_SYNCHRONIZATION_POINT_DESTROY_FOR(GrandChildPid2);
if (GrandChildStatusFile != NULL)
{
fclose(GrandChildStatusFile);
}
if (GrandChildDir >= 0)
{
close(GrandChildDir);
}
if (ChildStatusFile != NULL)
{
fclose(ChildStatusFile);
}
if (ChildDir >= 0)
{
close(ChildDir);
}
//
// Intentionally leaking pipes from children / grandchildren to test
// file descriptor cleanup on exit in the fork case.
//
LxtClosePipe(&Pipe);
LxtSignalDefault(SIGPIPE);
LxtSignalDefault(SIGCHLD);
return Result;
}
int WaitPidVariationZombieStress(PLXT_ARGS Args)
/*++
Description:
This routine stress tests zombie handling.
Arguments:
Args - Supplies the command line arguments.
Return Value:
Returns 0 on success, -1 on failure.
--*/
{
pid_t ChildPid;
pid_t GrandChildPid;
pid_t GreatGrandChildPid;
int Iterations;
int Result;
int Status;
GrandChildPid = -1;
GreatGrandChildPid = -1;
for (Iterations = 0; Iterations < 100; Iterations += 1)
{
ChildPid = -1;
LxtCheckErrno(ChildPid = fork());
if (ChildPid == 0)
{
LxtCheckErrno(setsid());
LxtCheckErrno(prctl(PR_SET_CHILD_SUBREAPER, 1));
for (Iterations = 0; Iterations < 3; Iterations += 1)
{
LxtCheckErrno(GrandChildPid = fork());
if (GrandChildPid == 0)
{
ChildPid = -1;
LxtCheckErrno(GreatGrandChildPid = fork());
if (GreatGrandChildPid == 0)
{
usleep(random() % 100);
goto ErrorExit;
}
usleep(random() % 100);
(void)waitpid(GreatGrandChildPid, &Status, WNOHANG);
goto ErrorExit;
}
(void)waitpid(GrandChildPid, &Status, WNOHANG);
}
usleep(random() % 100);
(void)waitpid(GrandChildPid, &Status, WNOHANG);
goto ErrorExit;
}
LXT_SYNCHRONIZATION_POINT_END();
ChildPid = -1;
}
ErrorExit:
if ((GreatGrandChildPid == 0) || (GrandChildPid == 0))
{
_exit(Result);
}
LXT_SYNCHRONIZATION_POINT_END();
return Result;
}