mirror of
https://github.com/microsoft/WSL.git
synced 2025-12-10 00:44:55 -06:00
* link: Collect WSL logs (recommended method) Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * link: Advanced Authoring Tests in C++ Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * link: CMake Documentation and Community Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * link: Collect WSL logs for networking issues Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * link: Collect WSL logs (recommended method) Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: ; otherwise, Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: a Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: access Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: accessible Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: across Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: actively 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: address Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: addresses Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: and Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: appropriate Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: argument Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: associated Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: attach Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: available Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: beginning Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: between Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: binaries Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: bound Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: buffer Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: buffers Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: cannot Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: canonical Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: capabilities Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: case-insensitive Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: case-sensitive Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: certified Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: command Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: committer Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: communication Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: complains Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: configuration Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: consumed Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: continue Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: converted Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: currently Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: customers Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: daemon Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: deferred Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: definitions Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: delimiter Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: delivered Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: dellink Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: derived Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: descriptor Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: destined Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: destruct Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: destructible Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: destructor Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: detach Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: differentiate Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: directories Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: disassociate Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: disposition Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: distribution Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: distro Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: duping Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: emitted Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: empty Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: environment Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: every time Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: exclusive Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: expected Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: expire Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: explicitly Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: fall back Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: false Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: fastfail Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: filesystem Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: first Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: followed Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: for Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: functionality Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: functionally Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: github Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: greater Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: guarantee Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: guaranteed Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: handles Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: hangup Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: hierarchy Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: hogwarts Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: hydrated Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: icrnl Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: implementation Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: implementing Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: initialize Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: instance Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: instantiate Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: instantiations Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: intentionally Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: interpret Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: interpreter Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: irreversibly Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: iteration Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: iterator Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: its Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: kernel Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: kmsg Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: knowledge Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: maximum Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: mirrored Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: msftconnecttest Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: multi Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: multiple Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: mutable Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: namespace 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: notifications Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: occurred Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: occurring Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: otherwise, Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: outstanding Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: overridden Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: partition Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: pass through Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: passthrough Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: performs Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: periodically Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: positional Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: precedence Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: preexisting Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: preferring Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: prepopulate Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: previous Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: privileges Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: process Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: processes Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: programmatically Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: protection Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: provided Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: reasonable Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: receive Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: received Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: red hat Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: reentrant Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: registered Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: regularly Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: relay Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: release Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: representing Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: requests Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: response Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: resurrect Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: retention Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: returned Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: security Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: semaphore Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: separate Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: separator Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: service Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: set up Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: setup Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: severely Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: should Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: signal Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: similarly Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: simple Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: simplified Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: single Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: specified Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: splitting Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: standard Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: stress Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: succeed Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: success Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: successfully Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: supplementary Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: synced Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: system Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: take Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: than Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: that opening Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: the Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: threadpool Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: to Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: true Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: truncate Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: tunneling Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: unexpected Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: uninitialize Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: unique Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: unprivileged Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: unregistered Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: untrusted Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: upgrade Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: utility Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: validating Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: variant Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: variation Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: variations Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: verify Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: visible 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: winget Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: worker Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: written Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: wslservice Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * format source --------- Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> Co-authored-by: Ben Hillis <benhillis@gmail.com> Co-authored-by: Ben Hillis <benhill@ntdev.microsoft.com>
2709 lines
54 KiB
C
2709 lines
54 KiB
C
/*++
|
|
|
|
Copyright (c) Microsoft. All rights reserved.
|
|
|
|
Module Name:
|
|
|
|
lxtlog.c
|
|
|
|
Abstract:
|
|
|
|
This file contains lx test logging routines.
|
|
|
|
--*/
|
|
|
|
#include "lxtutil.h"
|
|
#include "lxtlog.h"
|
|
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
#include <unistd.h>
|
|
#include <errno.h>
|
|
#include <sched.h>
|
|
#include <linux/futex.h>
|
|
#include <sys/types.h>
|
|
#include <sys/wait.h>
|
|
#include <sys/socket.h>
|
|
#include <sys/syscall.h>
|
|
#include <sys/stat.h>
|
|
#include <sys/mman.h>
|
|
#include <sys/utsname.h>
|
|
#include <fcntl.h>
|
|
#include <dirent.h>
|
|
#include <signal.h>
|
|
|
|
#define SIGNAL_WAIT_COUNT (20)
|
|
#define SIGNAL_WAIT_TIMEOUT_US (100000)
|
|
#define SIGNAL_MAX_SIGNALS (10)
|
|
#define SIGNAL_MAX_THREADS (5)
|
|
|
|
typedef struct _LXT_SIGNAL_INFO
|
|
{
|
|
pid_t ThreadId;
|
|
int ReceivedSignal[SIGNAL_MAX_SIGNALS];
|
|
siginfo_t SignalInfo[SIGNAL_MAX_SIGNALS];
|
|
BOOLEAN AllowMultipleSignals;
|
|
int SignalCount;
|
|
} LXT_SIGNAL_INFO, *PLXT_SIGNAL_INFO;
|
|
|
|
typedef struct _LXT_TYPE_MAPPING
|
|
{
|
|
char Type;
|
|
mode_t Mode;
|
|
} LXT_TYPE_MAPPING, *PLXT_TYPE_MAPPING;
|
|
|
|
void LxtPrintPartialMemory(const unsigned char* Buffer, size_t Size, size_t BufferIndex, const char* Prefix);
|
|
|
|
void LxtShowUsage(PCLXT_VARIATION Variations, unsigned int VariationCount);
|
|
|
|
PLXT_SIGNAL_INFO
|
|
LxtSignalFindThreadInfo(void);
|
|
|
|
void LxtSignalHandler(int Signal);
|
|
|
|
void LxtSignalHandlerSigAction(int Signal, siginfo_t* SigInfo, void* UContext);
|
|
|
|
//
|
|
// The multi-threaded signal tests require that information about the last
|
|
// signal received is stored per-thread, however using thread local storage
|
|
// is not safe in a signal handler (TLS support may take locks, if a signal
|
|
// arrives while the lock is held and the signal handler then tries to take
|
|
// the same lock, it leads to deadlock). Instead, an array is used that
|
|
// stores information for each thread.
|
|
//
|
|
|
|
static LXT_SIGNAL_INFO g_ThreadSignalInfo[SIGNAL_MAX_THREADS];
|
|
static int g_NextSignalThread = 0;
|
|
static LXT_TYPE_MAPPING g_TypeMapping[] = {
|
|
{DT_REG, S_IFREG}, {DT_DIR, S_IFDIR}, {DT_LNK, S_IFLNK}, {DT_FIFO, S_IFIFO}, {DT_SOCK, S_IFSOCK}, {DT_CHR, S_IFCHR}, {DT_BLK, S_IFBLK}};
|
|
|
|
static int g_WslVersion = 0;
|
|
|
|
//
|
|
// Test framework code
|
|
//
|
|
|
|
int LxtCheckDirectoryContents(const char* Path, const LXT_CHILD_INFO* Children, size_t Count)
|
|
|
|
/*++
|
|
|
|
Description:
|
|
|
|
This routine tests if the specified children are present in the directory.
|
|
|
|
Arguments:
|
|
|
|
Path - Supplies the path of the directory.
|
|
|
|
Children - Supplies the list of expected children.
|
|
|
|
Count - Supplies the number of children.
|
|
|
|
Return Value:
|
|
|
|
Returns 0 on success, -1 on failure.
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
return LxtCheckDirectoryContentsEx(Path, Children, Count, LXT_CHECK_DIRECTORY_CONTENTS_READ_FILES);
|
|
}
|
|
|
|
int LxtCheckDirectoryContentsEx(const char* Path, const LXT_CHILD_INFO* Children, size_t Count, int Flags)
|
|
|
|
/*++
|
|
|
|
Description:
|
|
|
|
This routine tests if the specified children are present in the directory.
|
|
|
|
Arguments:
|
|
|
|
Path - Supplies the path of the directory.
|
|
|
|
Children - Supplies the list of expected children.
|
|
|
|
Count - Supplies the number of children.
|
|
|
|
Flags - Supplies the flags.
|
|
|
|
Return Value:
|
|
|
|
Returns 0 on success, -1 on failure.
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
DIR* Directory;
|
|
struct dirent* Entry;
|
|
BOOLEAN* FoundEntries;
|
|
char FullPath[1024];
|
|
size_t Index;
|
|
int Result;
|
|
struct stat Stat;
|
|
|
|
FoundEntries = malloc(Count * sizeof(BOOLEAN));
|
|
memset(FoundEntries, 0, Count * sizeof(BOOLEAN));
|
|
Directory = opendir(Path);
|
|
if (Directory == NULL)
|
|
{
|
|
LxtLogError("opendir failed, errno: %d (%s)", errno, strerror(errno));
|
|
Result = LXT_RESULT_FAILURE;
|
|
goto ErrorExit;
|
|
}
|
|
|
|
errno = 0;
|
|
while ((Entry = readdir(Directory)) != NULL)
|
|
{
|
|
LxtLogInfo(
|
|
"Entry %p - d_name: %s d_ino: %llu d_type: %d d_off: %d d_reclen: %d",
|
|
Entry,
|
|
Entry->d_name,
|
|
Entry->d_ino,
|
|
Entry->d_type,
|
|
Entry->d_off,
|
|
Entry->d_reclen);
|
|
|
|
for (Index = 0; Index < Count; Index += 1)
|
|
{
|
|
if (strcmp(Children[Index].Name, Entry->d_name) == 0)
|
|
{
|
|
if (FoundEntries[Index] != FALSE)
|
|
{
|
|
LxtLogError("Duplicate entry '%s'", Entry->d_name);
|
|
Result = LXT_RESULT_FAILURE;
|
|
goto ErrorExit;
|
|
}
|
|
|
|
LxtCheckEqual(FoundEntries[Index], FALSE, "%d");
|
|
LxtCheckGreater(Entry->d_ino, 0, "%llu");
|
|
LxtCheckEqual(Entry->d_type, Children[Index].FileType, "%d");
|
|
FoundEntries[Index] = TRUE;
|
|
strcpy(FullPath, Path);
|
|
strcat(FullPath, "/");
|
|
strcat(FullPath, Entry->d_name);
|
|
LxtCheckResult(LxtCheckStat(FullPath, Entry->d_ino, Children[Index].FileType));
|
|
|
|
if ((Flags & LXT_CHECK_DIRECTORY_CONTENTS_READ_FILES) != 0)
|
|
{
|
|
LxtCheckResult(LxtCheckRead(FullPath, Children[Index].FileType));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (errno != 0)
|
|
{
|
|
LxtLogError("readdir failed; errno: %d (%s)", errno, strerror(errno));
|
|
Result = LXT_RESULT_FAILURE;
|
|
goto ErrorExit;
|
|
}
|
|
|
|
//
|
|
// Check if all the required entries have been found.
|
|
//
|
|
|
|
for (Index = 0; Index < Count; Index += 1)
|
|
{
|
|
if (FoundEntries[Index] == FALSE)
|
|
{
|
|
LxtLogError("Entry '%s' is missing", Children[Index].Name);
|
|
Result = LXT_RESULT_FAILURE;
|
|
goto ErrorExit;
|
|
}
|
|
}
|
|
|
|
Result = LXT_RESULT_SUCCESS;
|
|
|
|
ErrorExit:
|
|
if (Directory != NULL)
|
|
{
|
|
closedir(Directory);
|
|
}
|
|
|
|
if (FoundEntries != NULL)
|
|
{
|
|
free(FoundEntries);
|
|
}
|
|
|
|
return Result;
|
|
}
|
|
|
|
int LxtCheckFdPath(int Fd, char* ExpectedPath)
|
|
|
|
/*++
|
|
|
|
Description:
|
|
|
|
This routine checks if the file descriptor has the specified path.
|
|
|
|
Arguments:
|
|
|
|
Fd - Supplies the file descriptor.
|
|
|
|
ExpectedPath - Supplies the expected path.
|
|
|
|
Return Value:
|
|
|
|
Returns 0 on success, -1 on failure.
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
char ProcFsPath[PATH_MAX];
|
|
char Path[PATH_MAX];
|
|
int Result;
|
|
|
|
sprintf(ProcFsPath, "/proc/self/fd/%d", Fd);
|
|
LxtCheckResult(LxtCheckLinkTarget(ProcFsPath, ExpectedPath));
|
|
|
|
ErrorExit:
|
|
return Result;
|
|
}
|
|
|
|
int LxtCheckLinkTarget(const char* Path, const char* ExpectedTarget)
|
|
|
|
/*++
|
|
|
|
Description:
|
|
|
|
This routine tests the target of the specified link.
|
|
|
|
Arguments:
|
|
|
|
Path - Supplies the path of the link.
|
|
|
|
ExpectedTarget - Supplies the expected target of the link.
|
|
|
|
Return Value:
|
|
|
|
Returns 0 on success, -1 on failure.
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
char Buffer[256] = {0};
|
|
int Result;
|
|
ssize_t Size;
|
|
|
|
LxtCheckErrno(Size = readlink(Path, Buffer, sizeof(Buffer)));
|
|
LxtCheckEqual((size_t)Size, strlen(Buffer), "%d");
|
|
LxtCheckStringEqual(ExpectedTarget, Buffer);
|
|
|
|
ErrorExit:
|
|
return Result;
|
|
}
|
|
|
|
int LxtCheckRead(const char* FullPath, unsigned char FileType)
|
|
|
|
/*++
|
|
|
|
Description:
|
|
|
|
This routine checks that the specified file can be read.
|
|
|
|
N.B. This only checks that the file can be opened and read, it doesn't
|
|
check if the contents match what's expected. Write additional tests
|
|
for a specific file if necessary.
|
|
|
|
Arguments:
|
|
|
|
FullPath - Supplies the full path of the file or directory.
|
|
|
|
FileType - Supplies the file type.
|
|
|
|
Return Value:
|
|
|
|
Returns 0 on success, -1 on failure.
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
char Buffer[1024];
|
|
int Fd;
|
|
int Result;
|
|
ssize_t Size;
|
|
struct stat Stat;
|
|
|
|
Fd = 0;
|
|
switch (FileType)
|
|
{
|
|
case DT_REG:
|
|
|
|
//
|
|
// Skip files that aren't readable.
|
|
//
|
|
|
|
LxtCheckErrnoZeroSuccess(lstat(FullPath, &Stat));
|
|
if ((Stat.st_mode & S_IRUSR) == 0)
|
|
{
|
|
Result = LXT_RESULT_SUCCESS;
|
|
goto ErrorExit;
|
|
}
|
|
|
|
LxtCheckErrno(Fd = open(FullPath, O_RDONLY));
|
|
LxtCheckErrno(Size = read(Fd, Buffer, sizeof(Buffer)));
|
|
LxtCheckGreater(Size, 0, "%d");
|
|
break;
|
|
|
|
case DT_LNK:
|
|
LxtCheckErrno(Size = readlink(FullPath, Buffer, sizeof(Buffer)));
|
|
LxtCheckGreater(Size, 0, "%d");
|
|
break;
|
|
|
|
case DT_DIR:
|
|
|
|
//
|
|
// Nothing to check.
|
|
//
|
|
|
|
Result = LXT_RESULT_SUCCESS;
|
|
break;
|
|
|
|
default:
|
|
LxtLogError("Unexpected file type %d", FileType);
|
|
Result = LXT_RESULT_FAILURE;
|
|
break;
|
|
}
|
|
|
|
ErrorExit:
|
|
if (Result < 0)
|
|
{
|
|
LxtLogError("Error reading %s", FullPath);
|
|
}
|
|
|
|
if (Fd > 0)
|
|
{
|
|
close(Fd);
|
|
}
|
|
|
|
return Result;
|
|
}
|
|
|
|
int LxtCheckStat(const char* FullPath, unsigned long long ExpectedInode, unsigned char FileType)
|
|
|
|
/*++
|
|
|
|
Description:
|
|
|
|
This routine checks the stat information for a file or directory.
|
|
|
|
Arguments:
|
|
|
|
FullPath - Supplies the full path of the file or directory.
|
|
|
|
ExpectedInode - Supplies the expected inode number.
|
|
|
|
FileType - Supplies the file type.
|
|
|
|
Return Value:
|
|
|
|
Returns 0 on success, -1 on failure.
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
int Index;
|
|
int Result;
|
|
struct stat Stat;
|
|
|
|
LxtCheckErrnoZeroSuccess(lstat(FullPath, &Stat));
|
|
LxtCheckEqual(Stat.st_ino, ExpectedInode, "%llu");
|
|
LxtCheckGreater(Stat.st_nlink, 0, "%ud");
|
|
for (Index = 0; Index < LXT_COUNT_OF(g_TypeMapping); Index += 1)
|
|
{
|
|
if (g_TypeMapping[Index].Type == FileType)
|
|
{
|
|
LxtCheckEqual((Stat.st_mode & S_IFMT), g_TypeMapping[Index].Mode, "0%o");
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (Index == LXT_COUNT_OF(g_TypeMapping))
|
|
{
|
|
LxtLogError("Unexpected file type %d", FileType);
|
|
Result = LXT_RESULT_FAILURE;
|
|
goto ErrorExit;
|
|
}
|
|
|
|
ErrorExit:
|
|
return Result;
|
|
}
|
|
|
|
int LxtCheckWrite(const char* FullPath, const char* Value)
|
|
|
|
/*++
|
|
|
|
Description:
|
|
|
|
This routine checks that the specified file can be written to.
|
|
|
|
N.B. This function is meant for writable files in /proc and /sys. It's
|
|
primarily used for files that currently don't have a real write
|
|
implementation (which allow but silently ignore the write) since the
|
|
effects of the write are not checked.
|
|
|
|
Arguments:
|
|
|
|
FullPath - Supplies the full path of the file or directory.
|
|
|
|
FileType - Supplies the file type.
|
|
|
|
Return Value:
|
|
|
|
Returns 0 on success, -1 on failure.
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
ssize_t BytesWritten;
|
|
int Fd;
|
|
int Result;
|
|
|
|
LxtCheckErrno(Fd = open(FullPath, O_WRONLY));
|
|
LxtCheckErrno(BytesWritten = write(Fd, Value, strlen(Value)));
|
|
LxtCheckEqual((size_t)BytesWritten, strlen(Value), "%d");
|
|
|
|
ErrorExit:
|
|
if (Result < 0)
|
|
{
|
|
LxtLogError("Error writing %s", FullPath);
|
|
}
|
|
|
|
if (Fd > 0)
|
|
{
|
|
close(Fd);
|
|
}
|
|
|
|
return Result;
|
|
}
|
|
|
|
int LxtCheckWslPathTranslation(char* Path, const char* ExpectedPath, bool WinPath)
|
|
|
|
/*++
|
|
|
|
Description:
|
|
|
|
This routine checks whether translating a path with wslpath matches the
|
|
specified result.
|
|
|
|
Arguments:
|
|
|
|
Path - Supplies the path to translate.
|
|
|
|
ExpectedPath - Supplies the expected translated path.
|
|
|
|
WinPath - Supplies a value that indicates whether the specified path is a
|
|
Windows path. When true, the expected path must be a Linux path and
|
|
vice versa.
|
|
|
|
Return Value:
|
|
|
|
Returns 0 on success, -1 on failure.
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
int Result;
|
|
char TranslatedPath[4096];
|
|
|
|
LxtCheckResult(LxtExecuteWslPath(Path, WinPath, TranslatedPath, sizeof(TranslatedPath)));
|
|
LxtCheckStringEqual(ExpectedPath, TranslatedPath);
|
|
LxtLogInfo("%s => %s", Path, TranslatedPath);
|
|
|
|
ErrorExit:
|
|
return Result;
|
|
}
|
|
|
|
int LxtExecuteAndReadOutput(char** Argv, char* OutputBuffer, size_t OutputBufferSize)
|
|
|
|
/*++
|
|
|
|
Description:
|
|
|
|
This routine runs an executable, and reads stdout into the specified buffer.
|
|
|
|
N.B. If the process produces more output than fits in the buffer, this
|
|
function will fail.
|
|
|
|
Arguments:
|
|
|
|
Argv - Supplies the arguments to pass to the executable. The first element
|
|
is the executable to run.
|
|
|
|
OutputBuffer - Supplies the buffer to hold the process's stdout.
|
|
|
|
OutputBufferSize - Supplies the size of the output buffer.
|
|
|
|
Return Value:
|
|
|
|
Returns 0 on success, -1 on failure.
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
ssize_t BytesRead;
|
|
pid_t ChildPid;
|
|
int Result;
|
|
LXT_PIPE Pipe = {-1, -1};
|
|
|
|
LxtCheckResult(LxtCreatePipe(&Pipe));
|
|
LxtCheckErrno(ChildPid = fork());
|
|
if (ChildPid == 0)
|
|
{
|
|
LxtCheckClose(Pipe.Read);
|
|
LxtCheckErrno(dup2(Pipe.Write, STDOUT_FILENO));
|
|
LxtCheckClose(Pipe.Write);
|
|
LxtCheckErrno(execve(Argv[0], Argv, environ));
|
|
_exit(LXT_RESULT_FAILURE);
|
|
}
|
|
|
|
LxtCheckClose(Pipe.Write);
|
|
while ((BytesRead = read(Pipe.Read, OutputBuffer, OutputBufferSize)) > 0)
|
|
{
|
|
OutputBuffer += BytesRead;
|
|
OutputBufferSize -= BytesRead;
|
|
LxtCheckGreater(OutputBufferSize, 0, "%lu");
|
|
}
|
|
|
|
//
|
|
// Make sure the result did not exceed the buffer size and NULL-terminate
|
|
// it.
|
|
//
|
|
|
|
LxtCheckErrnoZeroSuccess(BytesRead);
|
|
LxtCheckGreater(OutputBufferSize, BytesRead, "%lu");
|
|
OutputBuffer[BytesRead] = '\0';
|
|
|
|
//
|
|
// Make sure the executable exited successfully.
|
|
//
|
|
|
|
LxtCheckResult(LxtWaitPidPoll(ChildPid, 0));
|
|
|
|
ErrorExit:
|
|
LxtClosePipe(&Pipe);
|
|
|
|
return Result;
|
|
}
|
|
|
|
int LxtExecuteWslPath(char* Path, bool WinPath, char* OutputBuffer, size_t OutputBufferSize)
|
|
|
|
/*++
|
|
|
|
Description:
|
|
|
|
This routine runs wslpath, and reads stdout into the specified buffer.
|
|
|
|
N.B. If the process produces more output than fits in the buffer, this
|
|
function will fail.
|
|
|
|
Arguments:
|
|
|
|
Path - Supplies the path to translate.
|
|
|
|
WinPath - Supplies a value that indicates whether the specified path is a
|
|
Windows path. When true, the output will be a Linux path and vice versa.
|
|
|
|
OutputBuffer - Supplies the buffer to hold the process's stdout.
|
|
|
|
OutputBufferSize - Supplies the size of the output buffer.
|
|
|
|
Return Value:
|
|
|
|
Returns 0 on success, -1 on failure.
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
char* Argv[4];
|
|
int Index = 0;
|
|
int Result;
|
|
size_t OutputLength;
|
|
|
|
//
|
|
// Construct the arguments to invoke wslpath.
|
|
//
|
|
|
|
Argv[Index++] = "/bin/wslpath";
|
|
if (WinPath == false)
|
|
{
|
|
Argv[Index++] = "-w";
|
|
}
|
|
|
|
Argv[Index++] = Path;
|
|
Argv[Index] = NULL;
|
|
|
|
//
|
|
// Execute wslpath.
|
|
//
|
|
|
|
LxtCheckResult(LxtExecuteAndReadOutput(Argv, OutputBuffer, OutputBufferSize));
|
|
|
|
//
|
|
// Wslpath outputs a new line at the end. Strip it to make things easier on
|
|
// the caller.
|
|
//
|
|
|
|
OutputLength = strlen(OutputBuffer);
|
|
if ((OutputLength > 0) && (OutputBuffer[OutputLength - 1] == '\n'))
|
|
{
|
|
OutputBuffer[OutputLength - 1] = '\0';
|
|
}
|
|
|
|
ErrorExit:
|
|
return Result;
|
|
}
|
|
|
|
int LxtInitialize(int Argc, char* Argv[], PLXT_ARGS Args, const char* TestName)
|
|
|
|
/*++
|
|
--*/
|
|
|
|
{
|
|
|
|
int Opt;
|
|
int OriginalOptErr;
|
|
int Result;
|
|
|
|
//
|
|
// Set umask to 0 so files created by tests have the expected permissions.
|
|
//
|
|
|
|
Result = umask(0);
|
|
if (Result < 0)
|
|
{
|
|
LxtLogError("umask failed %d", errno);
|
|
goto ErrorExit;
|
|
}
|
|
|
|
//
|
|
// Parse the command line, ignore unrecognized options since variations can
|
|
// specify their own options, and initialize logging.
|
|
//
|
|
|
|
Args->LogType = LXT_LOG_TYPE_DEFAULT_MASK;
|
|
Args->LogAppend = false;
|
|
Args->HelpRequested = false;
|
|
Args->VariationMask = -1;
|
|
Args->Argc = Argc;
|
|
Args->Argv = Argv;
|
|
OriginalOptErr = opterr;
|
|
opterr = 0;
|
|
while ((Opt = getopt(Argc, Argv, "l:v:a:h")) != LXT_RESULT_FAILURE)
|
|
{
|
|
switch (Opt)
|
|
{
|
|
case 'a':
|
|
Args->LogAppend = true;
|
|
break;
|
|
|
|
case 'l':
|
|
Args->LogType = atoi(optarg);
|
|
if (Args->LogType >= LxtLogTypeMax)
|
|
{
|
|
Result = LXT_RESULT_FAILURE;
|
|
LxtLogError("Invalid LxtLogType %d", Args->LogType);
|
|
goto ErrorExit;
|
|
}
|
|
|
|
break;
|
|
|
|
case 'v':
|
|
Args->VariationMask = atoll(optarg);
|
|
break;
|
|
|
|
case 'h':
|
|
Args->HelpRequested = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
opterr = OriginalOptErr;
|
|
Result = LxtLogInitialize(TestName, Args->LogType, Args->LogAppend);
|
|
|
|
ErrorExit:
|
|
return Result;
|
|
}
|
|
|
|
int LxtRunVariations(PLXT_ARGS Args, PCLXT_VARIATION Variations, unsigned int VariationCount)
|
|
|
|
/*++
|
|
--*/
|
|
|
|
{
|
|
|
|
unsigned int Itr;
|
|
unsigned long long ThisVariation;
|
|
int Result;
|
|
|
|
Result = LXT_RESULT_FAILURE;
|
|
if (Args->HelpRequested != false)
|
|
{
|
|
LxtShowUsage(Variations, VariationCount);
|
|
LxtLogError("No tests executed.");
|
|
goto ErrorExit;
|
|
}
|
|
|
|
for (Itr = 0; Itr < VariationCount; Itr++)
|
|
{
|
|
ThisVariation = (1ull << Itr);
|
|
|
|
//
|
|
// TODO: Currently, variation mask is only supported for the first 64
|
|
// variations.
|
|
//
|
|
|
|
if ((Args->VariationMask != 0) && ((ThisVariation & Args->VariationMask) == 0))
|
|
{
|
|
|
|
continue;
|
|
}
|
|
|
|
LxtLogStart("%s", Variations[Itr].Name);
|
|
Result = Variations[Itr].Variation(Args);
|
|
if (LXT_SUCCESS(Result) == 0)
|
|
{
|
|
LxtLogError("%s", Variations[Itr].Name);
|
|
goto ErrorExit;
|
|
}
|
|
|
|
LxtLogPassed("%s", Variations[Itr].Name);
|
|
}
|
|
|
|
ErrorExit:
|
|
return Result;
|
|
}
|
|
|
|
int LxtRunVariationsForked(PLXT_ARGS Args, PCLXT_VARIATION Variations, unsigned int VariationCount)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine runs test variations, with each variation executing in its
|
|
own child process. Use this function if a test may change process state
|
|
that interferes with other tests.
|
|
|
|
Arguments:
|
|
|
|
Args - Supplies the command line arguments.
|
|
|
|
Variations - Supplies a pointer to an array of variations.
|
|
|
|
VariationCount - Supplies the number items in the variations array.
|
|
|
|
Return Value:
|
|
|
|
Returns 0 on success, -1 on failure.
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
int ChildPid;
|
|
unsigned int Itr;
|
|
unsigned long long ThisVariation;
|
|
int Result;
|
|
|
|
ChildPid = -1;
|
|
Result = LXT_RESULT_FAILURE;
|
|
if (Args->HelpRequested != false)
|
|
{
|
|
LxtShowUsage(Variations, VariationCount);
|
|
LxtLogError("No tests executed.");
|
|
goto ErrorExit;
|
|
}
|
|
|
|
for (Itr = 0; Itr < VariationCount; Itr++)
|
|
{
|
|
ThisVariation = (1ull << Itr);
|
|
|
|
//
|
|
// TODO: Currently, variation mask is only supported for the first 64
|
|
// variations.
|
|
//
|
|
|
|
if ((Args->VariationMask != 0) && ((ThisVariation & Args->VariationMask) == 0))
|
|
{
|
|
|
|
continue;
|
|
}
|
|
|
|
LxtCheckResult(ChildPid = fork());
|
|
if (ChildPid == 0)
|
|
{
|
|
LxtLogStart("%s", Variations[Itr].Name);
|
|
Result = Variations[Itr].Variation(Args);
|
|
if (LXT_SUCCESS(Result) == 0)
|
|
{
|
|
LxtLogError("%s", Variations[Itr].Name);
|
|
goto ErrorExit;
|
|
}
|
|
|
|
LxtLogPassed("%s", Variations[Itr].Name);
|
|
_exit(0);
|
|
}
|
|
|
|
Result = LxtWaitPidPollOptions(ChildPid, 0, 0, 120);
|
|
if (Result < 0)
|
|
{
|
|
LxtLogError("Test execution timed out.");
|
|
kill(ChildPid, SIGKILL);
|
|
Result = LXT_RESULT_FAILURE;
|
|
goto ErrorExit;
|
|
}
|
|
}
|
|
|
|
ErrorExit:
|
|
if (ChildPid == 0)
|
|
{
|
|
_exit(Result);
|
|
}
|
|
|
|
return Result;
|
|
}
|
|
|
|
void LxtUninitialize(void)
|
|
|
|
/*++
|
|
--*/
|
|
|
|
{
|
|
|
|
LxtLogUninitialize();
|
|
return;
|
|
}
|
|
|
|
//
|
|
// stdlib wrappers
|
|
//
|
|
|
|
void* LxtAlloc(size_t Size)
|
|
|
|
/*++
|
|
--*/
|
|
|
|
{
|
|
|
|
void* Allocation;
|
|
|
|
Allocation = malloc(Size);
|
|
if (Allocation == NULL)
|
|
{
|
|
LxtLogResourceError("malloc failed for size %d", Size);
|
|
}
|
|
|
|
return Allocation;
|
|
}
|
|
|
|
void LxtFree(void* Allocation)
|
|
|
|
/*++
|
|
--*/
|
|
|
|
{
|
|
|
|
free(Allocation);
|
|
return;
|
|
}
|
|
|
|
//
|
|
// syscall wrappers
|
|
//
|
|
|
|
#define LXT_WAITPID_WAIT_TIMEOUT_US 100000
|
|
#define LXT_MESSAGE_WAIT_TIMEOUT_US 100000
|
|
#define LXT_MESSAGE_WAIT_COUNT 20
|
|
|
|
int LxtClone(int (*Entry)(void* Parameter), void* Parameter, int Flags, PLXT_CLONE_ARGS Args)
|
|
|
|
/*++
|
|
--*/
|
|
|
|
{
|
|
|
|
char* ChildStack;
|
|
int Result;
|
|
|
|
Args->Stack = LxtAlloc(LXT_CLONE_STACK_SIZE);
|
|
if (Args->Stack == NULL)
|
|
{
|
|
Result = LXT_RESULT_FAILURE;
|
|
goto ErrorExit;
|
|
}
|
|
|
|
memset(Args->Stack, 0, LXT_CLONE_STACK_SIZE);
|
|
ChildStack = Args->Stack + LXT_CLONE_STACK_SIZE;
|
|
LxtCheckErrno(Args->CloneId = clone(Entry, ChildStack, Flags, Parameter, 0, 0, 0));
|
|
|
|
ErrorExit:
|
|
if (LXT_SUCCESS(Result) == 0)
|
|
{
|
|
LxtFree(Args->Stack);
|
|
Args->Stack = NULL;
|
|
}
|
|
|
|
return Result;
|
|
}
|
|
|
|
int LxtClosePipe(PLXT_PIPE Pipe)
|
|
|
|
/*++
|
|
--*/
|
|
|
|
{
|
|
|
|
int Result;
|
|
|
|
if (Pipe->Read != -1)
|
|
{
|
|
LxtCheckErrno(close(Pipe->Read));
|
|
Pipe->Read = -1;
|
|
}
|
|
|
|
if (Pipe->Write != -1)
|
|
{
|
|
LxtCheckErrno(close(Pipe->Write));
|
|
Pipe->Write = -1;
|
|
}
|
|
|
|
Result = 0;
|
|
|
|
ErrorExit:
|
|
return Result;
|
|
}
|
|
|
|
int LxtCompareMemory(const void* First, const void* Second, size_t Size, const char* FirstDescription, const char* SecondDescription)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine compares two memory locations, and if they are different logs
|
|
information about where they are different.
|
|
|
|
Arguments:
|
|
|
|
First - Supplies the address of the first memory location.
|
|
|
|
Second - Supplies the address of the second memory location.
|
|
|
|
Size - Supplies the size of the memory locations.
|
|
|
|
FirstDescription - Supplies the description of the first memory location.
|
|
|
|
SecondDescription - Supplies the description of the second memory location.
|
|
|
|
Return Value:
|
|
|
|
Returns 0 on success, -1 on failure.
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
size_t End;
|
|
const unsigned char* FirstBytes;
|
|
size_t DifferentIndex;
|
|
size_t Index;
|
|
int Result;
|
|
const unsigned char* SecondBytes;
|
|
|
|
Result = LXT_RESULT_SUCCESS;
|
|
FirstBytes = First;
|
|
SecondBytes = Second;
|
|
for (Index = 0; Index < Size; Index += 1)
|
|
{
|
|
if (FirstBytes[Index] != SecondBytes[Index])
|
|
{
|
|
Result = LXT_RESULT_FAILURE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (Result != LXT_RESULT_SUCCESS)
|
|
{
|
|
LxtLogError(
|
|
"Memory contents of '%s' [1] differ from '%s' [2] at "
|
|
"offset %ld",
|
|
FirstDescription,
|
|
SecondDescription,
|
|
Index);
|
|
|
|
LxtPrintPartialMemory(FirstBytes, Size, Index, "[1]:");
|
|
LxtPrintPartialMemory(SecondBytes, Size, Index, "[2]:");
|
|
}
|
|
|
|
return Result;
|
|
}
|
|
|
|
int LxtCopyFile(const char* Source, const char* Destination)
|
|
|
|
/*++
|
|
|
|
Description:
|
|
|
|
This routine copies a file.
|
|
|
|
Arguments:
|
|
|
|
Source - Supplies the source.
|
|
|
|
Destination - Supplies the destination.
|
|
|
|
Return Value:
|
|
|
|
Returns 0 on success, -1 on failure.
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
char Buffer[4096];
|
|
ssize_t BytesRead;
|
|
int FdDest;
|
|
int FdSource;
|
|
int Result;
|
|
struct stat Stat;
|
|
|
|
FdDest = -1;
|
|
FdSource = -1;
|
|
LxtCheckErrno(FdSource = open(Source, O_RDONLY));
|
|
LxtCheckErrnoZeroSuccess(fstat(FdSource, &Stat));
|
|
LxtCheckErrno(FdDest = creat(Destination, Stat.st_mode & ~S_IFMT));
|
|
do
|
|
{
|
|
LxtCheckErrno(BytesRead = read(FdSource, Buffer, sizeof(Buffer)));
|
|
if (BytesRead > 0)
|
|
{
|
|
LxtCheckErrno(write(FdDest, Buffer, BytesRead));
|
|
}
|
|
} while (BytesRead > 0);
|
|
|
|
ErrorExit:
|
|
if (FdDest >= 0)
|
|
{
|
|
close(FdDest);
|
|
}
|
|
|
|
if (FdSource >= 0)
|
|
{
|
|
close(FdSource);
|
|
}
|
|
|
|
return Result;
|
|
}
|
|
|
|
int LxtCreatePipe(PLXT_PIPE Pipe)
|
|
|
|
/*++
|
|
--*/
|
|
|
|
{
|
|
|
|
int Result;
|
|
|
|
memset(Pipe, -1, sizeof(*Pipe));
|
|
LxtCheckErrno(pipe((int*)Pipe));
|
|
|
|
ErrorExit:
|
|
return Result;
|
|
}
|
|
|
|
int LxtJoinThread(pid_t* Tid)
|
|
|
|
{
|
|
|
|
pid_t CurrentTid;
|
|
|
|
while ((CurrentTid = *(volatile pid_t*)Tid) != 0)
|
|
{
|
|
if (syscall(SYS_futex, Tid, FUTEX_WAIT, CurrentTid, NULL, NULL, 0) < 0 && errno != EAGAIN)
|
|
{
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int LxtReceiveMessage(int Socket, const char* ExpectedMessage)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine receives a message from a socket and checks if it was the
|
|
expected message
|
|
|
|
Arguments:
|
|
|
|
Socket - Supplies a file descriptor for the socket to send on.
|
|
|
|
ExpectedMessage - Supplies a pointer to a zero-terminated string containing
|
|
the expected message.
|
|
|
|
Return Value:
|
|
|
|
Returns 0 on success, -1 on failure.
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
int ExpectedMessageSize;
|
|
char Message[100];
|
|
int MessageSize;
|
|
int Result;
|
|
int WaitCount;
|
|
|
|
ExpectedMessageSize = strlen(ExpectedMessage);
|
|
memset(Message, 0, sizeof(Message));
|
|
for (WaitCount = 0; WaitCount < LXT_MESSAGE_WAIT_COUNT; WaitCount += 1)
|
|
{
|
|
MessageSize = recv(Socket, Message, sizeof(Message), MSG_DONTWAIT);
|
|
if (MessageSize >= 0 || (errno != EAGAIN && errno != EWOULDBLOCK))
|
|
{
|
|
break;
|
|
}
|
|
|
|
usleep(LXT_MESSAGE_WAIT_TIMEOUT_US);
|
|
}
|
|
|
|
if (WaitCount == LXT_MESSAGE_WAIT_COUNT)
|
|
{
|
|
LxtLogError("Receiving the message timed out.");
|
|
Result = LXT_RESULT_FAILURE;
|
|
goto ErrorExit;
|
|
}
|
|
|
|
LxtCheckErrno(MessageSize);
|
|
if (MessageSize != ExpectedMessageSize)
|
|
{
|
|
LxtLogError("Received %i bytes, expected %i", MessageSize, ExpectedMessageSize);
|
|
|
|
Result = LXT_RESULT_FAILURE;
|
|
goto ErrorExit;
|
|
}
|
|
|
|
if (strncmp(Message, ExpectedMessage, ExpectedMessageSize) != 0)
|
|
{
|
|
LxtLogError("Received '%s', expected '%s'", Message, ExpectedMessage);
|
|
Result = LXT_RESULT_FAILURE;
|
|
goto ErrorExit;
|
|
}
|
|
|
|
Result = LXT_RESULT_SUCCESS;
|
|
|
|
ErrorExit:
|
|
return Result;
|
|
}
|
|
|
|
void LxtPrintPartialMemory(const unsigned char* Buffer, size_t Size, size_t BufferIndex, const char* Prefix)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine prints the contents of a memory buffer at the specified index
|
|
with some context.
|
|
|
|
Arguments:
|
|
|
|
Buffer - Supplies the memory buffer to print.
|
|
|
|
Size - Supplies the size of the buffer.
|
|
|
|
BufferIndex - Supplies the index at which to print.
|
|
|
|
Prefix - Supplies the prefix for the message.
|
|
|
|
Return Value:
|
|
|
|
Returns 0 on success, -1 on failure.
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
size_t End;
|
|
size_t Index;
|
|
char Message[256];
|
|
char Temp[10];
|
|
|
|
memset(Message, 0, sizeof(Message));
|
|
Index = BufferIndex - 5;
|
|
if (Index > BufferIndex)
|
|
{
|
|
Index = 0;
|
|
}
|
|
|
|
End = Index + 11;
|
|
if (End > Size)
|
|
{
|
|
End = Size;
|
|
}
|
|
|
|
if (Prefix != NULL)
|
|
{
|
|
strcat(Message, Prefix);
|
|
strcat(Message, " ");
|
|
}
|
|
|
|
if (Index > 0)
|
|
{
|
|
strcat(Message, "...");
|
|
}
|
|
|
|
for (; Index < End; Index += 1)
|
|
{
|
|
strcat(Message, " ");
|
|
if (Index == BufferIndex)
|
|
{
|
|
strcat(Message, "(");
|
|
}
|
|
|
|
sprintf(Temp, "%02x", Buffer[Index]);
|
|
strcat(Message, Temp);
|
|
if (Index == BufferIndex)
|
|
{
|
|
strcat(Message, ")");
|
|
}
|
|
}
|
|
|
|
if (End < Size)
|
|
{
|
|
strcat(Message, " ...");
|
|
}
|
|
|
|
LxtLogInfo("%s", Message);
|
|
return;
|
|
}
|
|
|
|
int LxtSendMessage(int Socket, const char* Message)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine sends a message to a socket and checks if it was successfully
|
|
sent.
|
|
|
|
Arguments:
|
|
|
|
Socket - Supplies a file descriptor for the socket to send on.
|
|
|
|
Message - Supplies a pointer to a zero-terminated buffer containing the
|
|
message.
|
|
|
|
Return Value:
|
|
|
|
Returns 0 on success, -1 on failure.
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
int MessageSize;
|
|
int Result;
|
|
int SentSize;
|
|
|
|
MessageSize = strlen(Message);
|
|
LxtCheckErrno(SentSize = send(Socket, Message, MessageSize, 0));
|
|
if (SentSize != MessageSize)
|
|
{
|
|
LxtLogError("Sent %i bytes, expected %i", SentSize, MessageSize);
|
|
Result = LXT_RESULT_FAILURE;
|
|
goto ErrorExit;
|
|
}
|
|
|
|
Result = LXT_RESULT_SUCCESS;
|
|
|
|
ErrorExit:
|
|
return Result;
|
|
}
|
|
|
|
void LxtShowUsage(PCLXT_VARIATION Variations, unsigned int VariationCount)
|
|
|
|
/*++
|
|
|
|
Description:
|
|
|
|
This routine shows usage for the variations.
|
|
|
|
Arguments:
|
|
|
|
Variations - Supplies the variations.
|
|
|
|
VariationCount - Supplies the number of variations.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
size_t Index;
|
|
|
|
LxtLogInfo("Usage: ./test_name [-v <variation_mask>] [-l <log_type>] [-a] [-?]");
|
|
LxtLogInfo("Variations:");
|
|
for (Index = 0; Index < VariationCount; Index += 1)
|
|
{
|
|
LxtLogInfo("%s: %llu", Variations[Index].Name, 1ull << Index);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
int LxtSignalBlock(int Signal)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine blocks the specified signal.
|
|
|
|
Arguments:
|
|
|
|
Signal - Supplies the signal number.
|
|
|
|
Return Value:
|
|
|
|
Returns 0 on success, -1 on failure.
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
int Result;
|
|
sigset_t Signals;
|
|
|
|
sigemptyset(&Signals);
|
|
sigaddset(&Signals, Signal);
|
|
LxtCheckErrnoZeroSuccess(sigprocmask(SIG_BLOCK, &Signals, NULL));
|
|
|
|
ErrorExit:
|
|
return Result;
|
|
}
|
|
|
|
int LxtSignalDefault(int Signal)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine reverts to the default action for the specified signal.
|
|
|
|
Arguments:
|
|
|
|
Signal - Supplies the signal number.
|
|
|
|
Return Value:
|
|
|
|
Returns 0 on success, -1 on failure.
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
struct sigaction Action;
|
|
int Result;
|
|
|
|
memset(&Action, 0, sizeof(Action));
|
|
Action.sa_handler = SIG_DFL;
|
|
LxtCheckErrnoZeroSuccess(sigaction(Signal, &Action, NULL));
|
|
|
|
ErrorExit:
|
|
return Result;
|
|
}
|
|
|
|
int LxtSignalIgnore(int Signal)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine ignores the specified signal.
|
|
|
|
Arguments:
|
|
|
|
Signal - Supplies the signal number.
|
|
|
|
Return Value:
|
|
|
|
Returns 0 on success, -1 on failure.
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
struct sigaction Action;
|
|
int Result;
|
|
|
|
memset(&Action, 0, sizeof(Action));
|
|
Action.sa_handler = SIG_IGN;
|
|
LxtCheckErrnoZeroSuccess(sigaction(Signal, &Action, NULL));
|
|
|
|
ErrorExit:
|
|
return Result;
|
|
}
|
|
|
|
int LxtSignalCheckInfoReceived(int Signal, int Code, pid_t Pid, uid_t Uid)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine checks if the specified signal was received by the signal
|
|
handlers, with the specified info values.
|
|
|
|
N.B. The signal handler must have been established with SA_SIGINFO for this
|
|
to work.
|
|
|
|
Arguments:
|
|
|
|
Signal - Supplies the expected signal number.
|
|
|
|
Code - Supplies the expected signal code.
|
|
|
|
Pid - Supplies the expected process ID.
|
|
|
|
Uid - Supplies the expected user ID.
|
|
|
|
Return Value:
|
|
|
|
Returns the index in the received signals array on success, -1 on failure.
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
int Index;
|
|
PLXT_SIGNAL_INFO Info;
|
|
int Result;
|
|
|
|
Info = LxtSignalFindThreadInfo();
|
|
if (Info == NULL)
|
|
{
|
|
Result = LXT_RESULT_FAILURE;
|
|
goto ErrorExit;
|
|
}
|
|
|
|
LxtCheckResult(Index = LxtSignalCheckReceived(Signal));
|
|
LxtCheckEqual(Signal, Info->SignalInfo[Index].si_signo, "%d");
|
|
LxtCheckEqual(Code, Info->SignalInfo[Index].si_code, "%d");
|
|
LxtCheckEqual(Pid, Info->SignalInfo[Index].si_pid, "%d");
|
|
LxtCheckEqual(Uid, Info->SignalInfo[Index].si_uid, "%d");
|
|
Result = Index;
|
|
|
|
ErrorExit:
|
|
return Result;
|
|
}
|
|
|
|
int LxtSignalCheckNoSignal(void)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine checks if no signal was received.
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
Return Value:
|
|
|
|
Returns 0 on success, -1 on failure.
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
PLXT_SIGNAL_INFO Info;
|
|
int Result;
|
|
|
|
Info = LxtSignalFindThreadInfo();
|
|
if (Info == NULL)
|
|
{
|
|
Result = LXT_RESULT_FAILURE;
|
|
goto ErrorExit;
|
|
}
|
|
|
|
if (Info->SignalCount == 0)
|
|
{
|
|
Result = LXT_RESULT_SUCCESS;
|
|
}
|
|
else
|
|
{
|
|
Result = LXT_RESULT_FAILURE;
|
|
LxtLogError("Unexpected signal.");
|
|
}
|
|
|
|
ErrorExit:
|
|
return Result;
|
|
}
|
|
|
|
int LxtSignalCheckReceived(int Signal)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine checks if the specified signal was received by the signal
|
|
handler.
|
|
|
|
Arguments:
|
|
|
|
Signal - Supplies the expected signal number.
|
|
|
|
Return Value:
|
|
|
|
Returns the index in the received signals array on success, -1 on failure.
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
int Index;
|
|
PLXT_SIGNAL_INFO Info;
|
|
int Result;
|
|
|
|
Info = LxtSignalFindThreadInfo();
|
|
if (Info == NULL)
|
|
{
|
|
Result = LXT_RESULT_FAILURE;
|
|
goto SignalCheckReceivedEnd;
|
|
}
|
|
|
|
if (Info->SignalCount == 0)
|
|
{
|
|
Result = LXT_RESULT_FAILURE;
|
|
LxtLogError("Signal %d was not received.", Signal);
|
|
goto SignalCheckReceivedEnd;
|
|
}
|
|
|
|
for (Index = 0; Index < Info->SignalCount; Index += 1)
|
|
{
|
|
if (Info->ReceivedSignal[Index] == -1)
|
|
{
|
|
Result = LXT_RESULT_FAILURE;
|
|
LxtLogError("An error occurred in the signal handler");
|
|
goto SignalCheckReceivedEnd;
|
|
}
|
|
|
|
if (Info->ReceivedSignal[Index] == Signal)
|
|
{
|
|
Result = Index;
|
|
goto SignalCheckReceivedEnd;
|
|
}
|
|
}
|
|
|
|
Result = LXT_RESULT_FAILURE;
|
|
LxtLogError("Signal %d was not received!", Signal);
|
|
|
|
SignalCheckReceivedEnd:
|
|
return Result;
|
|
}
|
|
|
|
int LxtSignalCheckSigChldReceived(int Code, pid_t Pid, uid_t Uid, int Status)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine checks if the SIGCHLD signal was received by the signal
|
|
handlers, with the specified info values.
|
|
|
|
N.B. The signal handler must have been established with SA_SIGINFO for this
|
|
to work.
|
|
|
|
Arguments:
|
|
|
|
Code - Supplies the expected signal code.
|
|
|
|
Pid - Supplies the expected process ID.
|
|
|
|
Uid - Supplies the expected user ID.
|
|
|
|
Status - Supplies the expected process status.
|
|
|
|
Return Value:
|
|
|
|
Returns the index in the received signals array on success, -1 on failure.
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
int Index;
|
|
PLXT_SIGNAL_INFO Info;
|
|
int Result;
|
|
|
|
Info = LxtSignalFindThreadInfo();
|
|
if (Info == NULL)
|
|
{
|
|
Result = LXT_RESULT_FAILURE;
|
|
goto ErrorExit;
|
|
}
|
|
|
|
LxtCheckResult(Index = LxtSignalCheckInfoReceived(SIGCHLD, Code, Pid, Uid));
|
|
LxtCheckEqual(Status, Info->SignalInfo[Index].si_status, "%d");
|
|
Result = Index;
|
|
|
|
ErrorExit:
|
|
return Result;
|
|
}
|
|
|
|
PLXT_SIGNAL_INFO
|
|
LxtSignalFindThreadInfo(void)
|
|
|
|
/*++
|
|
|
|
Description:
|
|
|
|
This routine finds the signal test info for the current thread.
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
Return Value:
|
|
|
|
A pointer to the signal info, or NULL if the signal info was not
|
|
initialized.
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
size_t Index;
|
|
PLXT_SIGNAL_INFO Result;
|
|
pid_t ThreadId;
|
|
|
|
Result = NULL;
|
|
ThreadId = gettid();
|
|
for (Index = 0; Index < SIGNAL_MAX_THREADS; Index += 1)
|
|
{
|
|
if (g_ThreadSignalInfo[Index].ThreadId == ThreadId)
|
|
{
|
|
Result = &g_ThreadSignalInfo[Index];
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (Result == NULL)
|
|
{
|
|
LxtLogError("LxtSignalInitializeThread not called for this thread.");
|
|
goto ErrorExit;
|
|
}
|
|
|
|
ErrorExit:
|
|
return Result;
|
|
}
|
|
|
|
int LxtSignalGetCount(void)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine returns the number of received signals.
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
Return Value:
|
|
|
|
The number of received signals, or -1 on failure.
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
PLXT_SIGNAL_INFO Info;
|
|
int Result;
|
|
|
|
Info = LxtSignalFindThreadInfo();
|
|
if (Info == NULL)
|
|
{
|
|
Result = LXT_RESULT_FAILURE;
|
|
goto ErrorExit;
|
|
}
|
|
|
|
Result = Info->SignalCount;
|
|
|
|
ErrorExit:
|
|
return Result;
|
|
}
|
|
|
|
int LxtSignalGetInfo(siginfo_t* SignalInfo)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine gets a copy of the last received signal info.
|
|
|
|
Arguments:
|
|
|
|
SignalInfo - Supplies a pointer which receives the signal info.
|
|
|
|
Return Value:
|
|
|
|
Returns 0 on success, -1 on failure.
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
PLXT_SIGNAL_INFO Info;
|
|
int Result;
|
|
|
|
Info = LxtSignalFindThreadInfo();
|
|
if (Info == NULL)
|
|
{
|
|
Result = LXT_RESULT_FAILURE;
|
|
goto ErrorExit;
|
|
}
|
|
|
|
*SignalInfo = Info->SignalInfo[0];
|
|
Result = LXT_RESULT_SUCCESS;
|
|
|
|
ErrorExit:
|
|
return Result;
|
|
}
|
|
|
|
void LxtSignalHandler(int Signal)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine handles signals for the process.
|
|
|
|
Arguments:
|
|
|
|
Signal - Supplies the signal that was received
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
int AllowedSignals;
|
|
PLXT_SIGNAL_INFO Info;
|
|
int Result;
|
|
|
|
Info = NULL;
|
|
|
|
#if defined(__i386__)
|
|
|
|
register int Eax asm("eax");
|
|
register void* Ecx asm("ecx");
|
|
register void* Edx asm("edx");
|
|
|
|
//
|
|
// Verify register contents.
|
|
//
|
|
|
|
LxtCheckEqual(Eax, Signal, "%d");
|
|
LxtCheckEqual(Edx, NULL, "%p");
|
|
LxtCheckEqual(Ecx, NULL, "%p");
|
|
|
|
//
|
|
// Verify stack alignment.
|
|
//
|
|
|
|
LxtCheckEqual((uintptr_t)&Signal & 0xf, 0, "%p");
|
|
|
|
#endif
|
|
|
|
Info = LxtSignalFindThreadInfo();
|
|
if (Info == NULL)
|
|
{
|
|
Result = LXT_RESULT_FAILURE;
|
|
goto ErrorExit;
|
|
}
|
|
|
|
if (Info->AllowMultipleSignals != FALSE)
|
|
{
|
|
AllowedSignals = SIGNAL_MAX_SIGNALS;
|
|
}
|
|
else
|
|
{
|
|
AllowedSignals = 1;
|
|
}
|
|
|
|
if (Info->SignalCount < AllowedSignals)
|
|
{
|
|
LxtLogInfo("Process %d got signal %d (%s)", getpid(), Signal, strsignal(Signal));
|
|
|
|
Result = Signal;
|
|
}
|
|
else
|
|
{
|
|
LxtLogError("Unexpected signal %d (%s)", Signal, strsignal(Signal));
|
|
Result = LXT_RESULT_FAILURE;
|
|
}
|
|
|
|
ErrorExit:
|
|
if (Info != NULL)
|
|
{
|
|
if (Result < 0)
|
|
{
|
|
Info->ReceivedSignal[0] = LXT_RESULT_FAILURE;
|
|
Info->SignalCount = 1;
|
|
}
|
|
else if (Info->SignalCount < AllowedSignals)
|
|
{
|
|
Info->ReceivedSignal[Info->SignalCount] = Result;
|
|
Info->SignalCount += 1;
|
|
}
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
void LxtSignalHandlerSigAction(int Signal, siginfo_t* SigInfo, void* UContext)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine handles signals for the process using the SA_SIGINFO flag.
|
|
|
|
Arguments:
|
|
|
|
Signal - Supplies the signal that was received.
|
|
|
|
SigInfo - Supplies additional information about the signal.
|
|
|
|
UContext - Supplies the scheduling context from the process before the
|
|
signal handler was invoked.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
int AllowedSignals;
|
|
PLXT_SIGNAL_INFO Info;
|
|
int Result;
|
|
|
|
Info = NULL;
|
|
|
|
#if defined(__i386__)
|
|
|
|
register int Eax asm("eax");
|
|
register void* Ecx asm("ecx");
|
|
register void* Edx asm("edx");
|
|
|
|
//
|
|
// Verify register contents.
|
|
//
|
|
|
|
LxtCheckEqual(Eax, Signal, "%d");
|
|
LxtCheckEqual(Edx, SigInfo, "%p");
|
|
LxtCheckEqual(Ecx, UContext, "%p");
|
|
|
|
//
|
|
// Verify stack alignment.
|
|
//
|
|
|
|
LxtCheckEqual((uintptr_t)&Signal & 0xf, 0, "%p");
|
|
|
|
#endif
|
|
|
|
LxtCheckEqual(Signal, SigInfo->si_signo, "%d");
|
|
Info = LxtSignalFindThreadInfo();
|
|
if (Info == NULL)
|
|
{
|
|
Result = LXT_RESULT_FAILURE;
|
|
goto ErrorExit;
|
|
}
|
|
|
|
if (Info->AllowMultipleSignals != FALSE)
|
|
{
|
|
AllowedSignals = SIGNAL_MAX_SIGNALS;
|
|
}
|
|
else
|
|
{
|
|
AllowedSignals = 1;
|
|
}
|
|
|
|
if (Info->SignalCount < AllowedSignals)
|
|
{
|
|
if (Signal == SIGCHLD)
|
|
{
|
|
LxtLogInfo(
|
|
"Process %d(%d) got signal %d (%s), code %d, pid %d, "
|
|
"uid %d, status %d",
|
|
getpid(),
|
|
gettid(),
|
|
SigInfo->si_signo,
|
|
strsignal(SigInfo->si_signo),
|
|
SigInfo->si_code,
|
|
SigInfo->si_pid,
|
|
SigInfo->si_uid,
|
|
SigInfo->si_status);
|
|
}
|
|
else
|
|
{
|
|
LxtLogInfo(
|
|
"Process %d(%d) got signal %d (%s), code %d, pid %d, uid %d",
|
|
getpid(),
|
|
gettid(),
|
|
SigInfo->si_signo,
|
|
strsignal(SigInfo->si_signo),
|
|
SigInfo->si_code,
|
|
SigInfo->si_pid,
|
|
SigInfo->si_uid);
|
|
}
|
|
|
|
Result = Signal;
|
|
}
|
|
else
|
|
{
|
|
LxtLogError(
|
|
"Process %d got unexpected signal %d (%s), code %d, pid %d, uid %d",
|
|
getpid(),
|
|
SigInfo->si_signo,
|
|
strsignal(SigInfo->si_signo),
|
|
SigInfo->si_code,
|
|
SigInfo->si_pid,
|
|
SigInfo->si_uid);
|
|
|
|
Result = LXT_RESULT_FAILURE;
|
|
}
|
|
|
|
ErrorExit:
|
|
if (Info != NULL)
|
|
{
|
|
if (Result < 0)
|
|
{
|
|
Info->ReceivedSignal[0] = LXT_RESULT_FAILURE;
|
|
Info->SignalCount = 1;
|
|
}
|
|
else if (Info->SignalCount < AllowedSignals)
|
|
{
|
|
Info->ReceivedSignal[Info->SignalCount] = Result;
|
|
Info->SignalInfo[Info->SignalCount] = *SigInfo;
|
|
Info->SignalCount += 1;
|
|
}
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
int LxtSignalInitialize(void)
|
|
|
|
/*++
|
|
|
|
Description:
|
|
|
|
This routine initializes the signal test infrastructure for the current
|
|
process.
|
|
|
|
N.B. Run this function for any process that uses the signal test
|
|
infrastructure. If a test uses fork(), you must run this function
|
|
again in the child process.
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
Return Value:
|
|
|
|
Returns 0 on success, -1 on failure.
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
int Result;
|
|
|
|
g_NextSignalThread = 0;
|
|
memset(g_ThreadSignalInfo, 0, sizeof(g_ThreadSignalInfo));
|
|
return LxtSignalInitializeThread();
|
|
}
|
|
|
|
int LxtSignalInitializeThread(void)
|
|
|
|
/*++
|
|
|
|
Description:
|
|
|
|
This routine initializes the signal test infrastructure for the current
|
|
thread.
|
|
|
|
N.B. Run this function for any thread that uses the signal test
|
|
infrastructure, except the main thread of the process; for the main
|
|
thread, run LxtSignalInitialize instead.
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
Return Value:
|
|
|
|
Returns 0 on success, -1 on failure.
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
int Index;
|
|
int Result;
|
|
|
|
Index = __sync_fetch_and_add(&g_NextSignalThread, 1);
|
|
if (Index >= SIGNAL_MAX_THREADS)
|
|
{
|
|
LxtLogError("Too many threads in signal test.");
|
|
Result = LXT_RESULT_FAILURE;
|
|
goto ErrorExit;
|
|
}
|
|
|
|
if (g_ThreadSignalInfo[Index].ThreadId != 0)
|
|
{
|
|
LxtLogError("Invalid signal test state.");
|
|
Result = LXT_RESULT_FAILURE;
|
|
goto ErrorExit;
|
|
}
|
|
|
|
g_ThreadSignalInfo[Index].ThreadId = gettid();
|
|
Result = LXT_RESULT_SUCCESS;
|
|
|
|
ErrorExit:
|
|
return Result;
|
|
}
|
|
|
|
void LxtSignalResetReceived(void)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine resets the global variables used by the signal handlers.
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
PLXT_SIGNAL_INFO Info;
|
|
|
|
Info = LxtSignalFindThreadInfo();
|
|
if (Info == NULL)
|
|
{
|
|
goto ErrorExit;
|
|
}
|
|
|
|
Info->SignalCount = 0;
|
|
|
|
ErrorExit:
|
|
return;
|
|
}
|
|
|
|
int LxtSignalSetupHandler(int Signal, int Flags)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine sets up a signal handler.
|
|
|
|
Arguments:
|
|
|
|
Signal - Supplies the signal.
|
|
|
|
Flags - Supplies the flags.
|
|
|
|
Return Value:
|
|
|
|
0 on success, -1 on failure.
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
struct sigaction Action;
|
|
int Result;
|
|
|
|
//
|
|
// Check that the signal infrastructure was initialized properly.
|
|
//
|
|
|
|
if (LxtSignalFindThreadInfo() == NULL)
|
|
{
|
|
Result = LXT_RESULT_FAILURE;
|
|
goto ErrorExit;
|
|
}
|
|
|
|
memset(&Action, 0, sizeof(Action));
|
|
if ((Flags & SA_SIGINFO) != 0)
|
|
{
|
|
Action.sa_sigaction = LxtSignalHandlerSigAction;
|
|
}
|
|
else
|
|
{
|
|
Action.sa_handler = LxtSignalHandler;
|
|
}
|
|
|
|
Action.sa_flags = Flags;
|
|
LxtCheckErrnoZeroSuccess(sigaction(Signal, &Action, NULL));
|
|
|
|
ErrorExit:
|
|
return Result;
|
|
}
|
|
|
|
void LxtSignalSetAllowMultiple(BOOLEAN AllowMultiple)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine sets whether or not receiving another signal when one was
|
|
already received should be not considered an error.
|
|
|
|
Arguments:
|
|
|
|
AllowMultiple - Supplies a value that indicates whether multiple signals
|
|
are allowed.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
PLXT_SIGNAL_INFO Info;
|
|
|
|
Info = LxtSignalFindThreadInfo();
|
|
if (Info == NULL)
|
|
{
|
|
goto ErrorExit;
|
|
}
|
|
|
|
Info->AllowMultipleSignals = AllowMultiple;
|
|
|
|
ErrorExit:
|
|
return;
|
|
}
|
|
|
|
int LxtSignalTimedWait(sigset_t* Set, siginfo_t* SignalInfo, struct timespec* Timeout)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine calls the rt_sigtimedwait system call.
|
|
|
|
N.B. In glibc, the sigtimedwait function is available as a wrapper for
|
|
this system call, but in bionic only sigwait is available which
|
|
prevents access to some of the parameters of rt_sigtimedwait.
|
|
Even in glibc the sigtimedwait wrapper should not be used for testing
|
|
since it silently converts SI_TKILL to SI_USER.
|
|
|
|
Arguments:
|
|
|
|
Set - Supplies a pointer to the set of signals to wait for.
|
|
|
|
SignalInfo - Supplies a pointer that receives information about the signal.
|
|
|
|
Timeout - Supplies a pointer to a timeout value.
|
|
|
|
Return Value:
|
|
|
|
The signal number on success, -1 on failure with errno set appropriately.
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
#if defined(__GLIBC__)
|
|
sigset_t* SignalSetPointer;
|
|
|
|
SignalSetPointer = Set;
|
|
#else
|
|
kernel_sigset_t SignalSet;
|
|
kernel_sigset_t* SignalSetPointer;
|
|
|
|
//
|
|
// Convert to the 64-bit signal set size that the kernel expects.
|
|
//
|
|
|
|
SignalSetPointer = NULL;
|
|
if (Set != NULL)
|
|
{
|
|
SignalSet = *Set;
|
|
SignalSetPointer = &SignalSet;
|
|
}
|
|
|
|
#endif
|
|
|
|
return syscall(SYS_rt_sigtimedwait, SignalSetPointer, SignalInfo, Timeout, _NSIG / 8);
|
|
}
|
|
|
|
int LxtSignalUnblock(int Signal)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine unblocks the specified signal.
|
|
|
|
Arguments:
|
|
|
|
Signal - Supplies the signal number.
|
|
|
|
Return Value:
|
|
|
|
Returns 0 on success, -1 on failure.
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
int Result;
|
|
sigset_t Signals;
|
|
|
|
sigemptyset(&Signals);
|
|
sigaddset(&Signals, Signal);
|
|
LxtCheckErrnoZeroSuccess(sigprocmask(SIG_UNBLOCK, &Signals, NULL));
|
|
|
|
ErrorExit:
|
|
return Result;
|
|
}
|
|
|
|
void LxtSignalWait(void)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine waits until a signal has been received, or a timeout expires.
|
|
|
|
N.B. This function does not return status to indicate whether a signal was
|
|
received or not. Use the signal check functions after this
|
|
function returns.
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
PLXT_SIGNAL_INFO Info;
|
|
int WaitCount;
|
|
|
|
Info = LxtSignalFindThreadInfo();
|
|
if (Info == NULL)
|
|
{
|
|
goto ErrorExit;
|
|
}
|
|
|
|
//
|
|
// N.B. It would be possible to implement this function using sigsuspend
|
|
// but only after signal blocking is implemented. In order to avoid
|
|
// a race where sigsuspend might hang if the signal arrives before
|
|
// the call, the relevant signal should be blocked before doing the
|
|
// operation that generates the signal, then call sigsuspend with a
|
|
// mask that unblocks the signal.
|
|
//
|
|
|
|
for (WaitCount = 0; (WaitCount < SIGNAL_WAIT_COUNT) && (Info->SignalCount == 0); WaitCount += 1)
|
|
{
|
|
|
|
usleep(SIGNAL_WAIT_TIMEOUT_US);
|
|
}
|
|
|
|
ErrorExit:
|
|
return;
|
|
}
|
|
|
|
int LxtSignalWaitBlocked(int Signal, pid_t FromPid, int TimeoutSeconds)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine waits for a specific blocked signal.
|
|
|
|
Arguments:
|
|
|
|
Signal - Supplies the signal number.
|
|
|
|
FromPid - Supplies the expected origin of the signal.
|
|
|
|
TimeoutSeconds - Supplies the timeout, in seconds.
|
|
|
|
Return Value:
|
|
|
|
Returns 0 on success, -1 on failure.
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
int ReceivedSignal;
|
|
int Result;
|
|
siginfo_t SignalInfo;
|
|
sigset_t Signals;
|
|
struct timespec Timeout;
|
|
|
|
sigemptyset(&Signals);
|
|
sigaddset(&Signals, Signal);
|
|
Timeout.tv_sec = TimeoutSeconds;
|
|
Timeout.tv_nsec = 0;
|
|
LxtCheckErrno(ReceivedSignal = LxtSignalTimedWait(&Signals, &SignalInfo, &Timeout));
|
|
|
|
LxtCheckEqual(Signal, ReceivedSignal, "%d");
|
|
LxtCheckEqual(SignalInfo.si_pid, FromPid, "%d");
|
|
|
|
ErrorExit:
|
|
return Result;
|
|
}
|
|
|
|
int LxtSocketPairClose(PLXT_SOCKET_PAIR SocketPair)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine closes a socket pair.
|
|
|
|
Arguments:
|
|
|
|
SocketPair - Supplies a pointer to the socket pair.
|
|
|
|
Return Value:
|
|
|
|
0 on success, -1 on failure.
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
int Result;
|
|
|
|
LxtCheckResult(LxtSocketPairCloseChild(SocketPair));
|
|
LxtCheckResult(LxtSocketPairCloseParent(SocketPair));
|
|
|
|
ErrorExit:
|
|
return Result;
|
|
}
|
|
|
|
int LxtSocketPairCloseChild(PLXT_SOCKET_PAIR SocketPair)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine closes the child socket of a socket pair.
|
|
|
|
Arguments:
|
|
|
|
SocketPair - Supplies a pointer to the socket pair.
|
|
|
|
Return Value:
|
|
|
|
0 on success, -1 on failure.
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
int Result;
|
|
|
|
if (SocketPair->Child != 0)
|
|
{
|
|
LxtCheckErrnoZeroSuccess(close(SocketPair->Child));
|
|
SocketPair->Child = 0;
|
|
}
|
|
|
|
Result = LXT_RESULT_SUCCESS;
|
|
|
|
ErrorExit:
|
|
return Result;
|
|
}
|
|
|
|
int LxtSocketPairCloseParent(PLXT_SOCKET_PAIR SocketPair)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine closes the parent socket of a socket pair.
|
|
|
|
Arguments:
|
|
|
|
SocketPair - Supplies a pointer to the socket pair.
|
|
|
|
Return Value:
|
|
|
|
0 on success, -1 on failure.
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
int Result;
|
|
|
|
if (SocketPair->Parent != 0)
|
|
{
|
|
LxtCheckErrnoZeroSuccess(close(SocketPair->Parent));
|
|
SocketPair->Parent = 0;
|
|
}
|
|
|
|
Result = LXT_RESULT_SUCCESS;
|
|
|
|
ErrorExit:
|
|
return Result;
|
|
}
|
|
|
|
int LxtSocketPairCreate(PLXT_SOCKET_PAIR SocketPair)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine creates a socket pair.
|
|
|
|
Arguments:
|
|
|
|
SocketPair - Supplies a pointer to the socket pair.
|
|
|
|
Return Value:
|
|
|
|
0 on success, -1 on failure.
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
int Result;
|
|
|
|
memset(SocketPair, 0, sizeof(*SocketPair));
|
|
LxtCheckErrnoZeroSuccess(socketpair(AF_UNIX, SOCK_SEQPACKET, 0, (int*)SocketPair));
|
|
|
|
ErrorExit:
|
|
return Result;
|
|
}
|
|
|
|
int LxtWaitPidPoll(pid_t ChildPid, int ExpectedWaitStatus)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine waits until the specified child exits by polling its wait
|
|
status repeatedly.
|
|
|
|
Arguments:
|
|
|
|
ChildPid - Supplies the thread group ID of the child to wait on.
|
|
|
|
ExpectedWaitStatus - Supplies the expected value of the child's status.
|
|
|
|
Return Value:
|
|
|
|
Returns 0 on success, -1 on failure.
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
return LxtWaitPidPollOptions(ChildPid, ExpectedWaitStatus, 0, LXT_WAITPID_DEFAULT_TIMEOUT);
|
|
}
|
|
|
|
int LxtWaitPidPollOptions(pid_t ChildPid, int ExpectedWaitStatus, int Options, int TimeoutSeconds)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine waits until the specified child exits by polling its wait
|
|
status repeatedly.
|
|
|
|
Arguments:
|
|
|
|
ChildPid - Supplies the thread group ID of the child to wait on.
|
|
|
|
ExpectedWaitStatus - Supplies the expected value of the child's status.
|
|
|
|
Options - Supplies wait options to pass to waitpid.
|
|
|
|
TimeoutSeconds - Supplies the number of seconds to wait for the child.
|
|
|
|
Return Value:
|
|
|
|
Returns 0 on success, -1 on failure.
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
int SecondWaitPidStatus;
|
|
int Result;
|
|
int WaitCount;
|
|
int WaitCountTotal;
|
|
int WaitPidResult;
|
|
int WaitPidStatus;
|
|
|
|
//
|
|
// Only WNOHANG is supported right now, so poll for the result and check the
|
|
// status.
|
|
//
|
|
|
|
Options |= WNOHANG;
|
|
WaitCountTotal = (TimeoutSeconds * 1000000) / LXT_WAITPID_WAIT_TIMEOUT_US;
|
|
for (WaitCount = 0; WaitCount < WaitCountTotal; ++WaitCount)
|
|
{
|
|
LxtCheckErrno((WaitPidResult = waitpid(ChildPid, &WaitPidStatus, Options)));
|
|
|
|
if (WaitPidResult != 0)
|
|
{
|
|
if ((WaitPidStatus & 0x80000000) != 0)
|
|
{
|
|
Result = LXT_RESULT_FAILURE;
|
|
LxtLogError("Unexpected high bit: %x - %x", WaitPidStatus, ExpectedWaitStatus);
|
|
|
|
goto ErrorExit;
|
|
}
|
|
|
|
if (WaitPidStatus != ExpectedWaitStatus)
|
|
{
|
|
Result = LXT_RESULT_FAILURE;
|
|
LxtLogError("Unexpected status: %x != %x", WaitPidStatus, ExpectedWaitStatus);
|
|
|
|
goto ErrorExit;
|
|
}
|
|
|
|
if (WIFEXITED(WaitPidStatus) != 0)
|
|
{
|
|
LxtCheckErrnoFailure(waitpid(ChildPid, &SecondWaitPidStatus, WNOHANG), ECHILD);
|
|
}
|
|
|
|
Result = WaitPidResult;
|
|
break;
|
|
}
|
|
|
|
usleep(LXT_WAITPID_WAIT_TIMEOUT_US);
|
|
}
|
|
|
|
if (WaitCount == WaitCountTotal)
|
|
{
|
|
Result = LXT_RESULT_FAILURE;
|
|
LxtLogError("Failed to receive status %d from child {:%d:}", ExpectedWaitStatus, ChildPid);
|
|
|
|
goto ErrorExit;
|
|
}
|
|
|
|
ErrorExit:
|
|
return Result;
|
|
}
|
|
|
|
int LxtClose(int FileDescriptor)
|
|
|
|
/*++
|
|
--*/
|
|
|
|
{
|
|
int Result;
|
|
|
|
LxtCheckErrnoZeroSuccess(close(FileDescriptor));
|
|
|
|
ErrorExit:
|
|
return Result;
|
|
}
|
|
|
|
int LxtMunmap(void* Address, size_t Length)
|
|
|
|
/*++
|
|
--*/
|
|
|
|
{
|
|
int Result;
|
|
|
|
LxtCheckErrno(munmap(Address, Length));
|
|
|
|
ErrorExit:
|
|
return Result;
|
|
}
|
|
|
|
int LxtWslVersion(void)
|
|
|
|
/*++
|
|
|
|
Description:
|
|
|
|
This routine determines whether the tests are running in WSL1 or 2.
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
Return Value:
|
|
|
|
The WSL version number, 1 or 2, or 0 if an error occurred.
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
int Result;
|
|
struct utsname UnameBuffer;
|
|
|
|
if (g_WslVersion == 0)
|
|
{
|
|
memset(&UnameBuffer, 0, sizeof(UnameBuffer));
|
|
LxtCheckErrno(uname(&UnameBuffer));
|
|
if (strstr(UnameBuffer.release, "Microsoft") == NULL)
|
|
{
|
|
g_WslVersion = 2;
|
|
}
|
|
else
|
|
{
|
|
g_WslVersion = 1;
|
|
}
|
|
}
|
|
|
|
ErrorExit:
|
|
return g_WslVersion;
|
|
} |