/*++ 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 #include #include #include #include #include #include #include #include #include #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; }