mirror of https://github.com/microsoft/WSL
395 lines
9.8 KiB
C
395 lines
9.8 KiB
C
/*++
|
|
|
|
Copyright (c) Microsoft. All rights reserved.
|
|
|
|
Module Name:
|
|
|
|
tty.c
|
|
|
|
Abstract:
|
|
|
|
This file contains unit tests for the /dev/tty0
|
|
|
|
--*/
|
|
|
|
#include "lxtcommon.h"
|
|
#include "unittests.h"
|
|
#include <unistd.h>
|
|
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
#include <errno.h>
|
|
#include <fcntl.h>
|
|
#include <poll.h>
|
|
#include <linux/kd.h>
|
|
#include <linux/vt.h>
|
|
#include <termios.h>
|
|
#include <sys/ioctl.h>
|
|
|
|
#define LXT_NAME "tty"
|
|
|
|
#define LXT_MANUAL_OUTPUT "ttyOutput.txt"
|
|
|
|
#define LXT_NON_DEFAULT_MODE (S_IFCHR | 0111)
|
|
|
|
#define LXT_NON_DEFAULT_ID 255
|
|
|
|
LXT_VARIATION_HANDLER TestDevTty0Ioctl;
|
|
|
|
LXT_VARIATION_HANDLER TestDevTtyIoctl;
|
|
|
|
LXT_VARIATION_HANDLER TestDevTtyStat;
|
|
|
|
LXT_VARIATION_HANDLER TestDevTtyOpen;
|
|
|
|
LXT_VARIATION_HANDLER TestDevTtySecurity;
|
|
|
|
//
|
|
// TODO: Enable TestDevTty0Ioctl
|
|
//
|
|
|
|
static const LXT_VARIATION g_LxtVariations[] = {
|
|
// {"Test the implementation of the ioctl(/dev/tty0)", TestDevTty0Ioctl},
|
|
{"tty stat", TestDevTtyStat},
|
|
{"tty open", TestDevTtyOpen},
|
|
{"tty security", TestDevTtySecurity},
|
|
{"tty ioctl", TestDevTtyIoctl}};
|
|
|
|
int TtyTestEntry(int argc, char* argv[])
|
|
{
|
|
LXT_ARGS Args;
|
|
int Result;
|
|
|
|
//
|
|
// Started in a unit test mode.
|
|
//
|
|
|
|
LxtCheckResult(LxtInitialize(argc, argv, &Args, LXT_NAME));
|
|
LxtCheckResult(LxtRunVariations(&Args, g_LxtVariations, LXT_COUNT_OF(g_LxtVariations)));
|
|
|
|
ErrorExit:
|
|
LxtUninitialize();
|
|
return !LXT_SUCCESS(Result);
|
|
}
|
|
|
|
int TestDevTty0Ioctl(PLXT_ARGS Args)
|
|
|
|
{
|
|
|
|
int fd;
|
|
int err;
|
|
char path[] = "/dev/tty0";
|
|
struct vt_stat vt_stat;
|
|
int vt_index;
|
|
struct termios termios;
|
|
int i;
|
|
|
|
err = -1;
|
|
fd = -1;
|
|
|
|
//
|
|
// Open the target.
|
|
//
|
|
|
|
fd = open(path, O_RDWR);
|
|
if (-1 == fd)
|
|
{
|
|
err = errno;
|
|
LxtLogError("open('%s') failed, %d", path, err);
|
|
goto exit;
|
|
}
|
|
|
|
//
|
|
// Test the ioctl(KDSETMODE)
|
|
//
|
|
|
|
err = ioctl(fd, KDSETMODE, KD_TEXT);
|
|
if (-1 == err)
|
|
{
|
|
err = errno;
|
|
LxtLogError("ioctl('%s', KDSETMODE, KD_TEXT) failed, %d", path, err);
|
|
goto exit;
|
|
}
|
|
|
|
LxtLogInfo("ioctl('%s', KDSETMODE) -> %d ", path, err);
|
|
|
|
//
|
|
// Test the ioctl(VT_GETSTATE)
|
|
//
|
|
|
|
err = ioctl(fd, VT_GETSTATE, &vt_stat);
|
|
if (-1 == err)
|
|
{
|
|
err = errno;
|
|
LxtLogError("ioctl('%s', VT_GETSTATE) failed, %d", path, err);
|
|
goto exit;
|
|
}
|
|
|
|
LxtLogInfo("ioctl('%s', VT_GETSTATE) -> %d ", path, err);
|
|
LxtLogInfo(" vt_stat.v_active = %d", vt_stat.v_active);
|
|
LxtLogInfo(" vt_stat.v_signal = %d", vt_stat.v_signal);
|
|
LxtLogInfo(" vt_stat.v_state = %d", vt_stat.v_state);
|
|
|
|
//
|
|
// Test the activation of the VT#7
|
|
//
|
|
|
|
vt_index = 7;
|
|
|
|
err = ioctl(fd, VT_ACTIVATE, vt_index);
|
|
if (-1 == err)
|
|
{
|
|
err = errno;
|
|
LxtLogError("ioctl('%s', VT_ACTIVATE, %d) failed, %d", path, vt_index, err);
|
|
goto exit;
|
|
}
|
|
|
|
LxtLogInfo("ioctl('%s', VT_ACTIVATE, %d) -> %d ", path, vt_index, err);
|
|
|
|
err = ioctl(fd, VT_WAITACTIVE, vt_index);
|
|
if (-1 == err)
|
|
{
|
|
err = errno;
|
|
LxtLogError("ioctl('%s', VT_WAITACTIVE, %d) failed, %d", path, vt_index, err);
|
|
goto exit;
|
|
}
|
|
|
|
LxtLogInfo("ioctl('%s', VT_WAITACTIVE, %d) -> %d ", path, vt_index, err);
|
|
|
|
//
|
|
// Get/set port settings.
|
|
//
|
|
|
|
#if !defined(__amd64__)
|
|
|
|
err = ioctl(fd, TCGETS, &termios);
|
|
if (-1 == err)
|
|
{
|
|
err = errno;
|
|
LxtLogError("ioctl('%s', TCGETS) failed, %d", path, err);
|
|
goto exit;
|
|
}
|
|
|
|
LxtLogInfo("ioctl('%s', TCGETS) -> %d ", path, err);
|
|
LxtLogInfo(" termios.c_iflag = %d", termios.c_iflag);
|
|
LxtLogInfo(" termios.c_oflag = %d", termios.c_oflag);
|
|
LxtLogInfo(" termios.c_cflag = %d", termios.c_cflag);
|
|
LxtLogInfo(" termios.c_lflag = %d", termios.c_lflag);
|
|
LxtLogInfo(" termios.c_line = %d", termios.c_line);
|
|
for (i = 0; i < NCCS; i++)
|
|
{
|
|
LxtLogInfo(" termios.c_cc[%d] = %d", i, termios.c_cc[i]);
|
|
}
|
|
|
|
#endif
|
|
|
|
//
|
|
// Test the ioctl(VT_GETSTATE) after all preparation completed.
|
|
//
|
|
|
|
err = ioctl(fd, VT_GETSTATE, &vt_stat);
|
|
if (-1 == err)
|
|
{
|
|
err = errno;
|
|
LxtLogError("ioctl('%s', VT_GETSTATE) failed, %d", path, err);
|
|
goto exit;
|
|
}
|
|
|
|
LxtLogInfo("ioctl('%s', VT_GETSTATE) -> %d ", path, err);
|
|
LxtLogInfo(" vt_stat.v_active = %d", vt_stat.v_active);
|
|
LxtLogInfo(" vt_stat.v_signal = %d", vt_stat.v_signal);
|
|
LxtLogInfo(" vt_stat.v_state = %d", vt_stat.v_state);
|
|
|
|
//
|
|
// Done.
|
|
// Close the test device handle.
|
|
//
|
|
|
|
close(fd);
|
|
fd = -1;
|
|
|
|
err = 0;
|
|
|
|
exit:
|
|
if (-1 != fd)
|
|
{
|
|
close(fd);
|
|
}
|
|
|
|
return err;
|
|
}
|
|
|
|
int TestDevTtyIoctl(PLXT_ARGS Args)
|
|
|
|
{
|
|
int Mode = 0;
|
|
int Result;
|
|
|
|
LxtCheckErrno(ioctl(0, KDGKBTYPE, &Mode));
|
|
LxtCheckEqual(Mode, KB_101, "%d");
|
|
LxtCheckErrno(ioctl(0, KDGKBMODE, &Mode));
|
|
LxtCheckEqual(Mode, K_UNICODE, "%d");
|
|
LxtCheckErrno(ioctl(0, KDSKBMODE, Mode));
|
|
|
|
Result = LXT_RESULT_SUCCESS;
|
|
|
|
ErrorExit:
|
|
return Result;
|
|
}
|
|
|
|
int TestDevTtyStat(PLXT_ARGS Args)
|
|
|
|
{
|
|
|
|
int Index;
|
|
int Result;
|
|
struct stat StatFd;
|
|
struct stat StatFile;
|
|
char TtyName[32];
|
|
|
|
//
|
|
// stat the file through the fd and tty name result.
|
|
//
|
|
|
|
for (Index = 0; Index < 3; ++Index)
|
|
{
|
|
LxtCheckErrno(ttyname_r(Index, TtyName, sizeof(TtyName)));
|
|
LxtLogInfo("Name %d: %s", Index, TtyName);
|
|
LxtCheckErrno(fstat(Index, &StatFd));
|
|
LxtCheckErrno(stat(TtyName, &StatFile));
|
|
LxtCheckEqual(StatFd.st_dev, StatFile.st_dev, "%d");
|
|
LxtCheckNotEqual(StatFd.st_dev, 0, "%d");
|
|
LxtCheckEqual(StatFd.st_ino, StatFile.st_ino, "%d");
|
|
LxtCheckNotEqual(StatFd.st_ino, 0, "%d");
|
|
LxtCheckEqual(StatFd.st_mode, StatFile.st_mode, "%d");
|
|
LxtCheckNotEqual(StatFd.st_mode, 0, "%d");
|
|
LxtCheckEqual(StatFd.st_nlink, StatFile.st_nlink, "%d");
|
|
LxtCheckEqual(StatFd.st_nlink, 1, "%d");
|
|
LxtCheckEqual(StatFd.st_uid, StatFile.st_uid, "%d");
|
|
LxtCheckEqual(StatFd.st_uid, 0, "%d");
|
|
LxtCheckEqual(StatFd.st_gid, StatFile.st_gid, "%d");
|
|
LxtCheckEqual(StatFd.st_gid, 5, "%d");
|
|
LxtCheckEqual(StatFd.st_rdev, StatFile.st_rdev, "%d");
|
|
LxtCheckNotEqual(StatFd.st_rdev, 0, "%d");
|
|
LxtCheckEqual(StatFd.st_size, StatFile.st_size, "%d");
|
|
LxtCheckEqual(StatFd.st_size, 0, "%d");
|
|
LxtCheckEqual(StatFd.st_blocks, StatFile.st_blocks, "%d");
|
|
LxtCheckEqual(StatFd.st_blocks, 0, "%d");
|
|
}
|
|
|
|
Result = 0;
|
|
|
|
ErrorExit:
|
|
return Result;
|
|
}
|
|
|
|
int TestDevTtyOpen(PLXT_ARGS Args)
|
|
|
|
{
|
|
|
|
ssize_t BytesRead;
|
|
int Index;
|
|
char LinkName[32];
|
|
char LinkTarget[32];
|
|
int Result;
|
|
struct stat StatFd;
|
|
struct stat StatFile;
|
|
int TtyFd;
|
|
char TtyName[32];
|
|
char TtyNameFd[32];
|
|
|
|
TtyFd = -1;
|
|
|
|
//
|
|
// Check that the current tty can be opened by name and is linked
|
|
// appropriately in procfs.
|
|
//
|
|
|
|
for (Index = 0; Index < 3; ++Index)
|
|
{
|
|
LxtCheckErrno(ttyname_r(Index, TtyName, sizeof(TtyName)));
|
|
LxtCheckErrno(TtyFd = open(TtyName, O_RDWR));
|
|
LxtCheckErrno(ttyname_r(TtyFd, TtyNameFd, sizeof(TtyNameFd)));
|
|
LxtCheckStringEqual(TtyName, TtyNameFd);
|
|
|
|
snprintf(LinkName, sizeof(LinkName), "/proc/self/fd/%d", Index);
|
|
LxtCheckErrno(BytesRead = readlink(LinkName, LinkTarget, sizeof(LinkTarget) - 1));
|
|
LinkTarget[BytesRead] = 0;
|
|
LxtCheckStringEqual(TtyName, LinkTarget);
|
|
|
|
snprintf(LinkName, sizeof(LinkName), "/proc/self/fd/%d", TtyFd);
|
|
LxtCheckErrno(BytesRead = readlink(LinkName, LinkTarget, sizeof(LinkTarget) - 1));
|
|
LinkTarget[BytesRead] = 0;
|
|
LxtCheckStringEqual(TtyName, LinkTarget);
|
|
|
|
LxtCheckErrno(fstat(TtyFd, &StatFd));
|
|
LxtCheckErrno(fstat(Index, &StatFile));
|
|
LxtCheckEqual(StatFd.st_ino, StatFile.st_ino, "%d");
|
|
LxtCheckEqual(StatFd.st_rdev, StatFile.st_rdev, "%d");
|
|
LxtClose(TtyFd);
|
|
TtyFd = -1;
|
|
}
|
|
|
|
//
|
|
// Check that /dev/tty0 fails to open, this behavior differs from native
|
|
// Linux.
|
|
//
|
|
|
|
LxtCheckErrnoFailure(TtyFd = open("/dev/tty0", O_RDWR), EIO);
|
|
Result = 0;
|
|
|
|
ErrorExit:
|
|
if (TtyFd != -1)
|
|
{
|
|
LxtClose(TtyFd);
|
|
}
|
|
|
|
return Result;
|
|
}
|
|
|
|
int TestDevTtySecurity(PLXT_ARGS Args)
|
|
|
|
{
|
|
|
|
bool ResetSecurity;
|
|
int Result;
|
|
struct stat Stat;
|
|
struct stat StatOriginal;
|
|
char TtyName[32];
|
|
|
|
ResetSecurity = false;
|
|
LxtCheckErrno(fstat(0, &StatOriginal));
|
|
ResetSecurity = true;
|
|
LxtCheckErrno(ttyname_r(0, TtyName, sizeof(TtyName)));
|
|
|
|
//
|
|
// Check that the uid, gid, and mode can be changed on the name or fd and
|
|
// are reflected into the stat on the fd and name
|
|
//
|
|
|
|
LxtCheckErrno(chmod(TtyName, LXT_NON_DEFAULT_MODE));
|
|
LxtCheckErrno(chown(TtyName, LXT_NON_DEFAULT_ID, LXT_NON_DEFAULT_ID));
|
|
LxtCheckErrno(fstat(0, &Stat));
|
|
LxtCheckEqual(Stat.st_mode, LXT_NON_DEFAULT_MODE, "%d");
|
|
LxtCheckEqual(Stat.st_uid, LXT_NON_DEFAULT_ID, "%d");
|
|
LxtCheckEqual(Stat.st_gid, LXT_NON_DEFAULT_ID, "%d");
|
|
|
|
LxtCheckErrno(fchmod(0, StatOriginal.st_mode));
|
|
LxtCheckErrno(fchown(0, StatOriginal.st_uid, StatOriginal.st_gid));
|
|
ResetSecurity = false;
|
|
LxtCheckErrno(stat(TtyName, &Stat));
|
|
LxtCheckEqual(Stat.st_mode, StatOriginal.st_mode, "%d");
|
|
LxtCheckEqual(Stat.st_uid, StatOriginal.st_uid, "%d");
|
|
LxtCheckEqual(Stat.st_gid, StatOriginal.st_gid, "%d");
|
|
|
|
Result = 0;
|
|
|
|
ErrorExit:
|
|
if (ResetSecurity != false)
|
|
{
|
|
fchmod(0, StatOriginal.st_mode);
|
|
fchown(0, StatOriginal.st_uid, StatOriginal.st_gid);
|
|
}
|
|
|
|
return Result;
|
|
}
|