| 1 | /*++ |
| 2 | |
| 3 | Copyright (c) Microsoft. All rights reserved. |
| 4 | |
| 5 | Module Name: |
| 6 | |
| 7 | tty.c |
| 8 | |
| 9 | Abstract: |
| 10 | |
| 11 | This file contains unit tests for the /dev/tty0 |
| 12 | |
| 13 | --*/ |
| 14 | |
| 15 | #include "lxtcommon.h" |
| 16 | #include "unittests.h" |
| 17 | #include <unistd.h> |
| 18 | #include <stdlib.h> |
| 19 | #include <stdio.h> |
| 20 | #include <errno.h> |
| 21 | #include <fcntl.h> |
| 22 | #include <poll.h> |
| 23 | #include <linux/kd.h> |
| 24 | #include <linux/vt.h> |
| 25 | #include <termios.h> |
| 26 | #include <sys/ioctl.h> |
| 27 | |
| 28 | #define LXT_NAME "tty" |
| 29 | |
| 30 | #define LXT_MANUAL_OUTPUT "ttyOutput.txt" |
| 31 | |
| 32 | #define LXT_NON_DEFAULT_MODE (S_IFCHR | 0111) |
| 33 | |
| 34 | #define LXT_NON_DEFAULT_ID 255 |
| 35 | |
| 36 | LXT_VARIATION_HANDLER TestDevTty0Ioctl; |
| 37 | |
| 38 | LXT_VARIATION_HANDLER TestDevTtyIoctl; |
| 39 | |
| 40 | LXT_VARIATION_HANDLER TestDevTtyStat; |
| 41 | |
| 42 | LXT_VARIATION_HANDLER TestDevTtyOpen; |
| 43 | |
| 44 | LXT_VARIATION_HANDLER TestDevTtySecurity; |
| 45 | |
| 46 | // |
| 47 | // TODO: Enable TestDevTty0Ioctl |
| 48 | // |
| 49 | |
| 50 | static const LXT_VARIATION g_LxtVariations[] = { |
| 51 | // {"Test the implementation of the ioctl(/dev/tty0)", TestDevTty0Ioctl}, |
| 52 | {"tty stat", TestDevTtyStat}, |
| 53 | {"tty open", TestDevTtyOpen}, |
| 54 | {"tty security", TestDevTtySecurity}, |
| 55 | {"tty ioctl", TestDevTtyIoctl}}; |
| 56 | |
| 57 | int TtyTestEntry(int argc, char* argv[]) |
| 58 | { |
| 59 | LXT_ARGS Args; |
| 60 | int Result; |
| 61 | |
| 62 | // |
| 63 | // Started in a unit test mode. |
| 64 | // |
| 65 | |
| 66 | LxtCheckResult(LxtInitialize(argc, argv, &Args, LXT_NAME)); |
| 67 | LxtCheckResult(LxtRunVariations(&Args, g_LxtVariations, LXT_COUNT_OF(g_LxtVariations))); |
| 68 | |
| 69 | ErrorExit: |
| 70 | LxtUninitialize(); |
| 71 | return !LXT_SUCCESS(Result); |
| 72 | } |
| 73 | |
| 74 | int TestDevTty0Ioctl(PLXT_ARGS Args) |
| 75 | |
| 76 | { |
| 77 | |
| 78 | int fd; |
| 79 | int err; |
| 80 | char path[] = "/dev/tty0"; |
| 81 | struct vt_stat vt_stat; |
| 82 | int vt_index; |
| 83 | struct termios termios; |
| 84 | int i; |
| 85 | |
| 86 | err = -1; |
| 87 | fd = -1; |
| 88 | |
| 89 | // |
| 90 | // Open the target. |
| 91 | // |
| 92 | |
| 93 | fd = open(path, O_RDWR); |
| 94 | if (-1 == fd) |
| 95 | { |
| 96 | err = errno; |
| 97 | LxtLogError("open('%s') failed, %d", path, err); |
| 98 | goto exit; |
| 99 | } |
| 100 | |
| 101 | // |
| 102 | // Test the ioctl(KDSETMODE) |
| 103 | // |
| 104 | |
| 105 | err = ioctl(fd, KDSETMODE, KD_TEXT); |
| 106 | if (-1 == err) |
| 107 | { |
| 108 | err = errno; |
| 109 | LxtLogError("ioctl('%s', KDSETMODE, KD_TEXT) failed, %d", path, err); |
| 110 | goto exit; |
| 111 | } |
| 112 | |
| 113 | LxtLogInfo("ioctl('%s', KDSETMODE) -> %d ", path, err); |
| 114 | |
| 115 | // |
| 116 | // Test the ioctl(VT_GETSTATE) |
| 117 | // |
| 118 | |
| 119 | err = ioctl(fd, VT_GETSTATE, &vt_stat); |
| 120 | if (-1 == err) |
| 121 | { |
| 122 | err = errno; |
| 123 | LxtLogError("ioctl('%s', VT_GETSTATE) failed, %d", path, err); |
| 124 | goto exit; |
| 125 | } |
| 126 | |
| 127 | LxtLogInfo("ioctl('%s', VT_GETSTATE) -> %d ", path, err); |
| 128 | LxtLogInfo(" vt_stat.v_active = %d", vt_stat.v_active); |
| 129 | LxtLogInfo(" vt_stat.v_signal = %d", vt_stat.v_signal); |
| 130 | LxtLogInfo(" vt_stat.v_state = %d", vt_stat.v_state); |
| 131 | |
| 132 | // |
| 133 | // Test the activation of the VT#7 |
| 134 | // |
| 135 | |
| 136 | vt_index = 7; |
| 137 | |
| 138 | err = ioctl(fd, VT_ACTIVATE, vt_index); |
| 139 | if (-1 == err) |
| 140 | { |
| 141 | err = errno; |
| 142 | LxtLogError("ioctl('%s', VT_ACTIVATE, %d) failed, %d", path, vt_index, err); |
| 143 | goto exit; |
| 144 | } |
| 145 | |
| 146 | LxtLogInfo("ioctl('%s', VT_ACTIVATE, %d) -> %d ", path, vt_index, err); |
| 147 | |
| 148 | err = ioctl(fd, VT_WAITACTIVE, vt_index); |
| 149 | if (-1 == err) |
| 150 | { |
| 151 | err = errno; |
| 152 | LxtLogError("ioctl('%s', VT_WAITACTIVE, %d) failed, %d", path, vt_index, err); |
| 153 | goto exit; |
| 154 | } |
| 155 | |
| 156 | LxtLogInfo("ioctl('%s', VT_WAITACTIVE, %d) -> %d ", path, vt_index, err); |
| 157 | |
| 158 | // |
| 159 | // Get/set port settings. |
| 160 | // |
| 161 | |
| 162 | #if !defined(__amd64__) |
| 163 | |
| 164 | err = ioctl(fd, TCGETS, &termios); |
| 165 | if (-1 == err) |
| 166 | { |
| 167 | err = errno; |
| 168 | LxtLogError("ioctl('%s', TCGETS) failed, %d", path, err); |
| 169 | goto exit; |
| 170 | } |
| 171 | |
| 172 | LxtLogInfo("ioctl('%s', TCGETS) -> %d ", path, err); |
| 173 | LxtLogInfo(" termios.c_iflag = %d", termios.c_iflag); |
| 174 | LxtLogInfo(" termios.c_oflag = %d", termios.c_oflag); |
| 175 | LxtLogInfo(" termios.c_cflag = %d", termios.c_cflag); |
| 176 | LxtLogInfo(" termios.c_lflag = %d", termios.c_lflag); |
| 177 | LxtLogInfo(" termios.c_line = %d", termios.c_line); |
| 178 | for (i = 0; i < NCCS; i++) |
| 179 | { |
| 180 | LxtLogInfo(" termios.c_cc[%d] = %d", i, termios.c_cc[i]); |
| 181 | } |
| 182 | |
| 183 | #endif |
| 184 | |
| 185 | // |
| 186 | // Test the ioctl(VT_GETSTATE) after all preparation completed. |
| 187 | // |
| 188 | |
| 189 | err = ioctl(fd, VT_GETSTATE, &vt_stat); |
| 190 | if (-1 == err) |
| 191 | { |
| 192 | err = errno; |
| 193 | LxtLogError("ioctl('%s', VT_GETSTATE) failed, %d", path, err); |
| 194 | goto exit; |
| 195 | } |
| 196 | |
| 197 | LxtLogInfo("ioctl('%s', VT_GETSTATE) -> %d ", path, err); |
| 198 | LxtLogInfo(" vt_stat.v_active = %d", vt_stat.v_active); |
| 199 | LxtLogInfo(" vt_stat.v_signal = %d", vt_stat.v_signal); |
| 200 | LxtLogInfo(" vt_stat.v_state = %d", vt_stat.v_state); |
| 201 | |
| 202 | // |
| 203 | // Done. |
| 204 | // Close the test device handle. |
| 205 | // |
| 206 | |
| 207 | close(fd); |
| 208 | fd = -1; |
| 209 | |
| 210 | err = 0; |
| 211 | |
| 212 | exit: |
| 213 | if (-1 != fd) |
| 214 | { |
| 215 | close(fd); |
| 216 | } |
| 217 | |
| 218 | return err; |
| 219 | } |
| 220 | |
| 221 | int TestDevTtyIoctl(PLXT_ARGS Args) |
| 222 | |
| 223 | { |
| 224 | int Mode = 0; |
| 225 | int Result; |
| 226 | |
| 227 | LxtCheckErrno(ioctl(0, KDGKBTYPE, &Mode)); |
| 228 | LxtCheckEqual(Mode, KB_101, "%d"); |
| 229 | LxtCheckErrno(ioctl(0, KDGKBMODE, &Mode)); |
| 230 | LxtCheckEqual(Mode, K_UNICODE, "%d"); |
| 231 | LxtCheckErrno(ioctl(0, KDSKBMODE, Mode)); |
| 232 | |
| 233 | Result = LXT_RESULT_SUCCESS; |
| 234 | |
| 235 | ErrorExit: |
| 236 | return Result; |
| 237 | } |
| 238 | |
| 239 | int TestDevTtyStat(PLXT_ARGS Args) |
| 240 | |
| 241 | { |
| 242 | |
| 243 | int Index; |
| 244 | int Result; |
| 245 | struct stat StatFd; |
| 246 | struct stat StatFile; |
| 247 | char TtyName[32]; |
| 248 | |
| 249 | // |
| 250 | // stat the file through the fd and tty name result. |
| 251 | // |
| 252 | |
| 253 | for (Index = 0; Index < 3; ++Index) |
| 254 | { |
| 255 | LxtCheckErrno(ttyname_r(Index, TtyName, sizeof(TtyName))); |
| 256 | LxtLogInfo("Name %d: %s", Index, TtyName); |
| 257 | LxtCheckErrno(fstat(Index, &StatFd)); |
| 258 | LxtCheckErrno(stat(TtyName, &StatFile)); |
| 259 | LxtCheckEqual(StatFd.st_dev, StatFile.st_dev, "%d"); |
| 260 | LxtCheckNotEqual(StatFd.st_dev, 0, "%d"); |
| 261 | LxtCheckEqual(StatFd.st_ino, StatFile.st_ino, "%d"); |
| 262 | LxtCheckNotEqual(StatFd.st_ino, 0, "%d"); |
| 263 | LxtCheckEqual(StatFd.st_mode, StatFile.st_mode, "%d"); |
| 264 | LxtCheckNotEqual(StatFd.st_mode, 0, "%d"); |
| 265 | LxtCheckEqual(StatFd.st_nlink, StatFile.st_nlink, "%d"); |
| 266 | LxtCheckEqual(StatFd.st_nlink, 1, "%d"); |
| 267 | LxtCheckEqual(StatFd.st_uid, StatFile.st_uid, "%d"); |
| 268 | LxtCheckEqual(StatFd.st_uid, 0, "%d"); |
| 269 | LxtCheckEqual(StatFd.st_gid, StatFile.st_gid, "%d"); |
| 270 | LxtCheckEqual(StatFd.st_gid, 5, "%d"); |
| 271 | LxtCheckEqual(StatFd.st_rdev, StatFile.st_rdev, "%d"); |
| 272 | LxtCheckNotEqual(StatFd.st_rdev, 0, "%d"); |
| 273 | LxtCheckEqual(StatFd.st_size, StatFile.st_size, "%d"); |
| 274 | LxtCheckEqual(StatFd.st_size, 0, "%d"); |
| 275 | LxtCheckEqual(StatFd.st_blocks, StatFile.st_blocks, "%d"); |
| 276 | LxtCheckEqual(StatFd.st_blocks, 0, "%d"); |
| 277 | } |
| 278 | |
| 279 | Result = 0; |
| 280 | |
| 281 | ErrorExit: |
| 282 | return Result; |
| 283 | } |
| 284 | |
| 285 | int TestDevTtyOpen(PLXT_ARGS Args) |
| 286 | |
| 287 | { |
| 288 | |
| 289 | ssize_t BytesRead; |
| 290 | int Index; |
| 291 | char LinkName[32]; |
| 292 | char LinkTarget[32]; |
| 293 | int Result; |
| 294 | struct stat StatFd; |
| 295 | struct stat StatFile; |
| 296 | int TtyFd; |
| 297 | char TtyName[32]; |
| 298 | char TtyNameFd[32]; |
| 299 | |
| 300 | TtyFd = -1; |
| 301 | |
| 302 | // |
| 303 | // Check that the current tty can be opened by name and is linked |
| 304 | // appropriately in procfs. |
| 305 | // |
| 306 | |
| 307 | for (Index = 0; Index < 3; ++Index) |
| 308 | { |
| 309 | LxtCheckErrno(ttyname_r(Index, TtyName, sizeof(TtyName))); |
| 310 | LxtCheckErrno(TtyFd = open(TtyName, O_RDWR)); |
| 311 | LxtCheckErrno(ttyname_r(TtyFd, TtyNameFd, sizeof(TtyNameFd))); |
| 312 | LxtCheckStringEqual(TtyName, TtyNameFd); |
| 313 | |
| 314 | snprintf(LinkName, sizeof(LinkName), "/proc/self/fd/%d", Index); |
| 315 | LxtCheckErrno(BytesRead = readlink(LinkName, LinkTarget, sizeof(LinkTarget) - 1)); |
| 316 | LinkTarget[BytesRead] = 0; |
| 317 | LxtCheckStringEqual(TtyName, LinkTarget); |
| 318 | |
| 319 | snprintf(LinkName, sizeof(LinkName), "/proc/self/fd/%d", TtyFd); |
| 320 | LxtCheckErrno(BytesRead = readlink(LinkName, LinkTarget, sizeof(LinkTarget) - 1)); |
| 321 | LinkTarget[BytesRead] = 0; |
| 322 | LxtCheckStringEqual(TtyName, LinkTarget); |
| 323 | |
| 324 | LxtCheckErrno(fstat(TtyFd, &StatFd)); |
| 325 | LxtCheckErrno(fstat(Index, &StatFile)); |
| 326 | LxtCheckEqual(StatFd.st_ino, StatFile.st_ino, "%d"); |
| 327 | LxtCheckEqual(StatFd.st_rdev, StatFile.st_rdev, "%d"); |
| 328 | LxtClose(TtyFd); |
| 329 | TtyFd = -1; |
| 330 | } |
| 331 | |
| 332 | // |
| 333 | // Check that /dev/tty0 fails to open, this behavior differs from native |
| 334 | // Linux. |
| 335 | // |
| 336 | |
| 337 | LxtCheckErrnoFailure(TtyFd = open("/dev/tty0", O_RDWR), EIO); |
| 338 | Result = 0; |
| 339 | |
| 340 | ErrorExit: |
| 341 | if (TtyFd != -1) |
| 342 | { |
| 343 | LxtClose(TtyFd); |
| 344 | } |
| 345 | |
| 346 | return Result; |
| 347 | } |
| 348 | |
| 349 | int TestDevTtySecurity(PLXT_ARGS Args) |
| 350 | |
| 351 | { |
| 352 | |
| 353 | bool ResetSecurity; |
| 354 | int Result; |
| 355 | struct stat Stat; |
| 356 | struct stat StatOriginal; |
| 357 | char TtyName[32]; |
| 358 | |
| 359 | ResetSecurity = false; |
| 360 | LxtCheckErrno(fstat(0, &StatOriginal)); |
| 361 | ResetSecurity = true; |
| 362 | LxtCheckErrno(ttyname_r(0, TtyName, sizeof(TtyName))); |
| 363 | |
| 364 | // |
| 365 | // Check that the uid, gid, and mode can be changed on the name or fd and |
| 366 | // are reflected into the stat on the fd and name |
| 367 | // |
| 368 | |
| 369 | LxtCheckErrno(chmod(TtyName, LXT_NON_DEFAULT_MODE)); |
| 370 | LxtCheckErrno(chown(TtyName, LXT_NON_DEFAULT_ID, LXT_NON_DEFAULT_ID)); |
| 371 | LxtCheckErrno(fstat(0, &Stat)); |
| 372 | LxtCheckEqual(Stat.st_mode, LXT_NON_DEFAULT_MODE, "%d"); |
| 373 | LxtCheckEqual(Stat.st_uid, LXT_NON_DEFAULT_ID, "%d"); |
| 374 | LxtCheckEqual(Stat.st_gid, LXT_NON_DEFAULT_ID, "%d"); |
| 375 | |
| 376 | LxtCheckErrno(fchmod(0, StatOriginal.st_mode)); |
| 377 | LxtCheckErrno(fchown(0, StatOriginal.st_uid, StatOriginal.st_gid)); |
| 378 | ResetSecurity = false; |
| 379 | LxtCheckErrno(stat(TtyName, &Stat)); |
| 380 | LxtCheckEqual(Stat.st_mode, StatOriginal.st_mode, "%d"); |
| 381 | LxtCheckEqual(Stat.st_uid, StatOriginal.st_uid, "%d"); |
| 382 | LxtCheckEqual(Stat.st_gid, StatOriginal.st_gid, "%d"); |
| 383 | |
| 384 | Result = 0; |
| 385 | |
| 386 | ErrorExit: |
| 387 | if (ResetSecurity != false) |
| 388 | { |
| 389 | fchmod(0, StatOriginal.st_mode); |
| 390 | fchown(0, StatOriginal.st_uid, StatOriginal.st_gid); |
| 391 | } |
| 392 | |
| 393 | return Result; |
| 394 | } |