mirror of
https://github.com/microsoft/WSL.git
synced 2025-07-03 15:23:22 +00:00
407 lines
9.1 KiB
C
407 lines
9.1 KiB
C
![]() |
/*++
|
||
|
|
||
|
Copyright (c) Microsoft. All rights reserved.
|
||
|
|
||
|
Module Name:
|
||
|
|
||
|
interop.c
|
||
|
|
||
|
Abstract:
|
||
|
|
||
|
This file is the interop test for WSL2.
|
||
|
|
||
|
--*/
|
||
|
|
||
|
#include "lxtcommon.h"
|
||
|
#include "unittests.h"
|
||
|
#include <unistd.h>
|
||
|
#include <sys/types.h>
|
||
|
#include <sys/wait.h>
|
||
|
#include <pty.h>
|
||
|
#include <poll.h>
|
||
|
#include <errno.h>
|
||
|
#include <fcntl.h>
|
||
|
#include <stdio.h>
|
||
|
#include <stdlib.h>
|
||
|
|
||
|
#define LXT_NAME "interop"
|
||
|
|
||
|
#define BUF_SIZE 1024
|
||
|
#define LARGE_MESSAGE_SIZE (4096)
|
||
|
#define PIPE_READ_END 0
|
||
|
#define PIPE_WRITE_END 1
|
||
|
#define CMD_NT_BINARY "/mnt/c/Windows/System32/cmd.exe"
|
||
|
#define CMD_SYMLINK "/tmp/cmd"
|
||
|
#define HELLO_WORLD_STR "hello world"
|
||
|
|
||
|
int BasicBasicTests(PLXT_ARGS Args);
|
||
|
|
||
|
int InteropWithPtyTest(PLXT_ARGS Args);
|
||
|
|
||
|
int InteropWithPipesTest(PLXT_ARGS Args);
|
||
|
|
||
|
//
|
||
|
// Global constants
|
||
|
//
|
||
|
|
||
|
static const LXT_VARIATION g_LxtVariations[] = {
|
||
|
{"Interop Test with pipes", InteropWithPipesTest},
|
||
|
/* {"Interop Test with pty", InteropWithPtyTest}, */
|
||
|
{"Basic interop tests", BasicBasicTests}};
|
||
|
|
||
|
int InteropTestEntry(int Argc, char* Argv[])
|
||
|
|
||
|
/*++
|
||
|
--*/
|
||
|
|
||
|
{
|
||
|
|
||
|
LXT_ARGS Args;
|
||
|
int Result;
|
||
|
|
||
|
LxtCheckResult(LxtInitialize(Argc, Argv, &Args, LXT_NAME));
|
||
|
LxtCheckResult(LxtRunVariations(&Args, g_LxtVariations, LXT_COUNT_OF(g_LxtVariations)));
|
||
|
|
||
|
ErrorExit:
|
||
|
LxtUninitialize();
|
||
|
return !LXT_SUCCESS(Result);
|
||
|
}
|
||
|
|
||
|
int readStringFrom(int fd, char* buffer, int buf_size)
|
||
|
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
|
||
|
Reads characters from given file descriptor until either we reach EOF
|
||
|
or reach the buffer limit.
|
||
|
This function adds the '\0' character at the end of the string.
|
||
|
|
||
|
Arguments:
|
||
|
|
||
|
fd : the file descriptor from which data should be read.
|
||
|
buffer: the buffer into which data should be read.
|
||
|
buf_size: Size of the 'buffer'. This function will read at max
|
||
|
(buf_size - 1) bytes from fd. Last byte is reserved to write '\0'
|
||
|
character.
|
||
|
|
||
|
Return Value:
|
||
|
|
||
|
Returns number of bytes read.
|
||
|
|
||
|
--*/
|
||
|
|
||
|
{
|
||
|
if (fd < 0 || !buffer || (buf_size <= 0))
|
||
|
{
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
char* readptr = buffer;
|
||
|
int remaining = buf_size - 1;
|
||
|
int rc = 0;
|
||
|
while (remaining > 0 && (rc = read(fd, readptr, remaining)) > 0)
|
||
|
{
|
||
|
readptr += rc;
|
||
|
remaining -= rc;
|
||
|
}
|
||
|
*readptr = 0;
|
||
|
// exclude '\0' from length
|
||
|
return (buf_size - remaining - 1);
|
||
|
}
|
||
|
|
||
|
int InteropWithPtyTest(PLXT_ARGS Args)
|
||
|
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
|
||
|
Test for verifying that interop works as expected when the windows executable is started
|
||
|
inside a pseudoterminal.
|
||
|
|
||
|
Arguments:
|
||
|
|
||
|
Args - standard arguments provided to every test.
|
||
|
|
||
|
Return Value:
|
||
|
|
||
|
Returns 0 on success, -1 on failure.
|
||
|
|
||
|
--*/
|
||
|
|
||
|
{
|
||
|
int Result;
|
||
|
int status;
|
||
|
int childpid;
|
||
|
int master_fd = -1;
|
||
|
char buf[BUF_SIZE];
|
||
|
|
||
|
LxtCheckErrno((childpid = forkpty(&master_fd, NULL, NULL, NULL)));
|
||
|
if (childpid == 0)
|
||
|
{
|
||
|
// child
|
||
|
LxtCheckErrno(execl("/mnt/c/Windows/System32/cmd.exe", "cmd.exe", "/c", "echo", HELLO_WORLD_STR, (char*)NULL));
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// parent
|
||
|
|
||
|
readStringFrom(master_fd, buf, sizeof(buf));
|
||
|
|
||
|
// output generated by echo command should match exactly with the
|
||
|
// expected string (along with the CRLF and quotation marks)
|
||
|
LxtCheckStringEqual(
|
||
|
buf,
|
||
|
"\"" HELLO_WORLD_STR
|
||
|
"\""
|
||
|
"\r\n");
|
||
|
|
||
|
// make sure child exits normally
|
||
|
LxtCheckErrno(waitpid(childpid, &status, 0));
|
||
|
LxtCheckResult(WIFEXITED(status));
|
||
|
Result = LXT_RESULT_SUCCESS;
|
||
|
}
|
||
|
|
||
|
ErrorExit:
|
||
|
if (master_fd != -1)
|
||
|
{
|
||
|
LxtClose(master_fd);
|
||
|
}
|
||
|
if (childpid == 0)
|
||
|
{
|
||
|
_exit(Result);
|
||
|
}
|
||
|
return Result;
|
||
|
}
|
||
|
|
||
|
int InteropWithPipesTest(PLXT_ARGS Args)
|
||
|
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
|
||
|
Test for verifying that interop works as expected when stdout of the windows executable is redirected.
|
||
|
|
||
|
Arguments:
|
||
|
|
||
|
Args - standard arguments provided to every test.
|
||
|
|
||
|
Return Value:
|
||
|
|
||
|
Returns 0 on success, -1 on failure.
|
||
|
|
||
|
--*/
|
||
|
|
||
|
{
|
||
|
|
||
|
int Result;
|
||
|
int status;
|
||
|
int childpid;
|
||
|
int child_out_pipe[2];
|
||
|
char buf[BUF_SIZE];
|
||
|
|
||
|
// Pipe to which child's stdout is redirected
|
||
|
LxtCheckErrno(pipe(child_out_pipe));
|
||
|
|
||
|
LxtCheckErrno(childpid = fork());
|
||
|
if (childpid == 0)
|
||
|
{
|
||
|
// child
|
||
|
LxtCheckErrno(dup2(child_out_pipe[PIPE_WRITE_END], STDOUT_FILENO));
|
||
|
LxtClose(child_out_pipe[PIPE_READ_END]);
|
||
|
child_out_pipe[PIPE_READ_END] = -1;
|
||
|
LxtClose(child_out_pipe[PIPE_WRITE_END]);
|
||
|
child_out_pipe[PIPE_WRITE_END] = -1;
|
||
|
// execute a windows executable
|
||
|
LxtCheckErrno(execl("/mnt/c/Windows/System32/cmd.exe", "cmd.exe", "/c", "echo", HELLO_WORLD_STR, (char*)NULL));
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// parent
|
||
|
|
||
|
LxtClose(child_out_pipe[PIPE_WRITE_END]);
|
||
|
child_out_pipe[PIPE_WRITE_END] = -1;
|
||
|
|
||
|
readStringFrom(child_out_pipe[PIPE_READ_END], buf, sizeof(buf));
|
||
|
|
||
|
// output generated by echo command should match exactly with the
|
||
|
// expected string (along with the CRLF and quotation marks)
|
||
|
LxtCheckStringEqual(
|
||
|
buf,
|
||
|
"\"" HELLO_WORLD_STR
|
||
|
"\""
|
||
|
"\r\n");
|
||
|
|
||
|
// make sure child exits normally
|
||
|
LxtCheckErrno(waitpid(childpid, &status, 0));
|
||
|
LxtCheckResult(WIFEXITED(status));
|
||
|
Result = LXT_RESULT_SUCCESS;
|
||
|
}
|
||
|
|
||
|
ErrorExit:
|
||
|
if (child_out_pipe[PIPE_READ_END] != -1)
|
||
|
{
|
||
|
LxtClose(child_out_pipe[PIPE_READ_END]);
|
||
|
}
|
||
|
if (child_out_pipe[PIPE_WRITE_END] != -1)
|
||
|
{
|
||
|
LxtClose(child_out_pipe[PIPE_WRITE_END]);
|
||
|
}
|
||
|
if (childpid == 0)
|
||
|
{
|
||
|
_exit(Result);
|
||
|
}
|
||
|
return Result;
|
||
|
}
|
||
|
|
||
|
int BasicBasicTests(PLXT_ARGS Args)
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
|
||
|
WSL interop tests from version 1 ported for WSL2.
|
||
|
These tests call some standard windows commands/executables in different ways and make sure
|
||
|
that interop is working as expected.
|
||
|
|
||
|
Arguments:
|
||
|
|
||
|
Args - standard arguments provided to every test.
|
||
|
|
||
|
Return Value:
|
||
|
|
||
|
Returns 0 on success, -1 on failure.
|
||
|
|
||
|
--*/
|
||
|
|
||
|
{
|
||
|
int ChildPid;
|
||
|
char* ExecArgs[5];
|
||
|
int ExitStatus;
|
||
|
char* LargeArgument;
|
||
|
int Result = LXT_RESULT_FAILURE;
|
||
|
|
||
|
memset(ExecArgs, 0, sizeof(ExecArgs));
|
||
|
LargeArgument = NULL;
|
||
|
|
||
|
//
|
||
|
// Try to launch an invalid NT binary.
|
||
|
//
|
||
|
|
||
|
LxtCheckResult(ChildPid = fork());
|
||
|
if (ChildPid == 0)
|
||
|
{
|
||
|
ExecArgs[0] = "/init";
|
||
|
ExecArgs[1] = "invalid_binary_name";
|
||
|
LxtCheckErrnoFailure(LxtExecve(ExecArgs[0], ExecArgs, NULL), ENOENT);
|
||
|
//
|
||
|
// The parent waits for the child to exit successfully.
|
||
|
//
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
wait(&ExitStatus);
|
||
|
LxtCheckTrue(WIFEXITED(ExitStatus));
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Launch the cmd.exe NT binary.
|
||
|
//
|
||
|
|
||
|
LxtCheckResult(ChildPid = fork());
|
||
|
if (ChildPid == 0)
|
||
|
{
|
||
|
ExecArgs[0] = CMD_NT_BINARY;
|
||
|
ExecArgs[1] = "/c";
|
||
|
ExecArgs[2] = "exit 0";
|
||
|
LxtCheckErrno(LxtExecve(ExecArgs[0], ExecArgs, NULL));
|
||
|
|
||
|
//
|
||
|
// The parent waits for the child to exit successfully.
|
||
|
//
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
LxtCheckResult(LxtWaitPidPoll(ChildPid, 0));
|
||
|
}
|
||
|
|
||
|
LxtCheckResult(ChildPid = fork());
|
||
|
if (ChildPid == 0)
|
||
|
{
|
||
|
ExecArgs[0] = CMD_NT_BINARY;
|
||
|
ExecArgs[1] = "/c";
|
||
|
ExecArgs[2] = "exit 1";
|
||
|
LxtCheckErrno(LxtExecve(ExecArgs[0], ExecArgs, NULL));
|
||
|
|
||
|
//
|
||
|
// The parent waits for the child to exit successfully.
|
||
|
//
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
LxtCheckResult(LxtWaitPidPoll(ChildPid, 1 << 8));
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Launch cmd.exe with a very long command line.
|
||
|
//
|
||
|
|
||
|
LargeArgument = LxtAlloc(LARGE_MESSAGE_SIZE);
|
||
|
if (LargeArgument == NULL)
|
||
|
{
|
||
|
goto ErrorExit;
|
||
|
}
|
||
|
|
||
|
memset(LargeArgument, 'a', LARGE_MESSAGE_SIZE);
|
||
|
LargeArgument[LARGE_MESSAGE_SIZE - 1] = '\0';
|
||
|
LxtCheckResult(ChildPid = fork());
|
||
|
if (ChildPid == 0)
|
||
|
{
|
||
|
ExecArgs[0] = CMD_NT_BINARY;
|
||
|
ExecArgs[1] = "/c";
|
||
|
ExecArgs[2] = "echo";
|
||
|
ExecArgs[3] = LargeArgument;
|
||
|
LxtCheckErrno(LxtExecve(ExecArgs[0], ExecArgs, NULL));
|
||
|
|
||
|
//
|
||
|
// The parent waits for the child to exit successfully.
|
||
|
//
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
LxtCheckResult(LxtWaitPidPoll(ChildPid, 0));
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Launch cmd.exe through a symlink.
|
||
|
//
|
||
|
|
||
|
LxtCheckErrno(symlink(CMD_NT_BINARY, CMD_SYMLINK));
|
||
|
LxtCheckResult(ChildPid = fork());
|
||
|
if (ChildPid == 0)
|
||
|
{
|
||
|
ExecArgs[0] = CMD_SYMLINK;
|
||
|
ExecArgs[1] = "/c";
|
||
|
ExecArgs[2] = "exit 0";
|
||
|
LxtCheckErrno(LxtExecve(ExecArgs[0], ExecArgs, NULL));
|
||
|
|
||
|
//
|
||
|
// The parent waits for the child to exit successfully.
|
||
|
//
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
LxtCheckResult(LxtWaitPidPoll(ChildPid, 0));
|
||
|
}
|
||
|
|
||
|
ErrorExit:
|
||
|
if (ChildPid > 0)
|
||
|
{
|
||
|
unlink(CMD_SYMLINK);
|
||
|
}
|
||
|
if (ChildPid == 0)
|
||
|
{
|
||
|
_exit(Result);
|
||
|
}
|
||
|
return Result;
|
||
|
}
|