|
| 1 | +#include <emscripten.h> |
| 2 | +#include <errno.h> |
| 3 | +#include <fcntl.h> |
| 4 | +#include <stdio.h> |
| 5 | +#include <string.h> |
| 6 | +#include <sys/ioctl.h> |
| 7 | +#include <termios.h> |
| 8 | +#include <unistd.h> |
| 9 | + |
| 10 | +EM_JS_DEPS(main, "$TTY"); |
| 11 | + |
| 12 | +// clang-format off |
| 13 | +EM_JS(void, init, (void), { |
| 14 | + var major = 100; |
| 15 | + var tty_ops = { |
| 16 | + get_char: function (tty) { |
| 17 | + if (tty.input.length > 0) { |
| 18 | + return tty.input.shift(); |
| 19 | + } |
| 20 | + return undefined; |
| 21 | + }, |
| 22 | + put_char: function (tty, val) { |
| 23 | + if (val !== 0 && val !== 10) { |
| 24 | + tty.output.push(val); |
| 25 | + } |
| 26 | + }, |
| 27 | + fsync: function (tty) { |
| 28 | + console.log('fsync called'); |
| 29 | + tty.output = []; |
| 30 | + }, |
| 31 | + ioctl_tcgets: function (tty) { |
| 32 | + return { |
| 33 | + c_iflag: 0, |
| 34 | + c_oflag: 0, |
| 35 | + c_cflag: 0, |
| 36 | + c_lflag: 0, |
| 37 | + c_cc: new Array(32).fill(0), |
| 38 | + }; |
| 39 | + }, |
| 40 | + ioctl_tcsets: function (tty, optional_actions, data) { |
| 41 | + return 0; |
| 42 | + }, |
| 43 | + ioctl_tiocgwinsz: function (tty) { |
| 44 | + return [25, 80]; |
| 45 | + }, |
| 46 | + }; |
| 47 | + var device = FS.makedev(major, 0); |
| 48 | + TTY.register(device, tty_ops); |
| 49 | + FS.mkdev('/custom_tty', device); |
| 50 | + // Populate the TTY input buffer with test data "ABCD" |
| 51 | + TTY.ttys[device].input = [65, 66, 67, 68]; |
| 52 | + |
| 53 | + // TTY without get_char - should cause ENXIO on read |
| 54 | + var tty_no_getchar = {put_char: function (tty, val) {}}; |
| 55 | + var device_no_getchar = FS.makedev(major + 1, 0); |
| 56 | + TTY.register(device_no_getchar, tty_no_getchar); |
| 57 | + FS.mkdev('/tty_no_getchar', device_no_getchar); |
| 58 | + |
| 59 | + // TTY without put_char - should cause ENXIO on write |
| 60 | + var tty_no_putchar = { |
| 61 | + get_char: function (tty) { |
| 62 | + return 0; |
| 63 | + }, |
| 64 | + }; |
| 65 | + var device_no_putchar = FS.makedev(major + 2, 0); |
| 66 | + TTY.register(device_no_putchar, tty_no_putchar); |
| 67 | + FS.mkdev('/tty_no_putchar', device_no_putchar); |
| 68 | + |
| 69 | + // TTY with throwing get_char - should cause EIO on read |
| 70 | + var tty_throw_getchar = { |
| 71 | + get_char: function (tty) { |
| 72 | + throw new Error('get_char error'); |
| 73 | + }, |
| 74 | + put_char: function (tty, val) {}, |
| 75 | + }; |
| 76 | + var device_throw_getchar = FS.makedev(major + 3, 0); |
| 77 | + TTY.register(device_throw_getchar, tty_throw_getchar); |
| 78 | + FS.mkdev('/tty_throw_getchar', device_throw_getchar); |
| 79 | + |
| 80 | + // TTY with throwing put_char - should cause EIO on write |
| 81 | + var tty_throw_putchar = { |
| 82 | + get_char: function (tty) { |
| 83 | + return 0; |
| 84 | + }, |
| 85 | + put_char: function (tty, val) { |
| 86 | + throw new Error('put_char error'); |
| 87 | + }, |
| 88 | + }; |
| 89 | + var device_throw_putchar = FS.makedev(major + 4, 0); |
| 90 | + TTY.register(device_throw_putchar, tty_throw_putchar); |
| 91 | + FS.mkdev('/tty_throw_putchar', device_throw_putchar); |
| 92 | + |
| 93 | + // TTY with empty input (returns undefined immediately) - should cause EAGAIN on |
| 94 | + // read |
| 95 | + var tty_empty = { |
| 96 | + get_char: function (tty) { |
| 97 | + return undefined; |
| 98 | + }, |
| 99 | + put_char: function (tty, val) {}, |
| 100 | + }; |
| 101 | + var device_empty = FS.makedev(major + 5, 0); |
| 102 | + TTY.register(device_empty, tty_empty); |
| 103 | + FS.mkdev('/tty_empty', device_empty); |
| 104 | +}); |
| 105 | +// clang-format on |
| 106 | + |
| 107 | +int main() { |
| 108 | + init(); |
| 109 | + char readBuffer[256] = {0}; |
| 110 | + char writeBuffer[] = "Test"; |
| 111 | + struct winsize ws; |
| 112 | + struct termios term; |
| 113 | + |
| 114 | + printf("\nTest 1: open custom TTY device and check isatty\n"); |
| 115 | + int fd = open("/custom_tty", O_RDWR); |
| 116 | + printf("open custom_tty: %d\n", fd >= 0 ? 1 : 0); |
| 117 | + printf("isatty: %d\n", isatty(fd)); |
| 118 | + printf("errno after open: %s\n", strerror(errno)); |
| 119 | + errno = 0; |
| 120 | + |
| 121 | + printf("\nTest 2: read from TTY with data\n"); |
| 122 | + ssize_t bytesRead = read(fd, readBuffer, sizeof(readBuffer)); |
| 123 | + printf("read bytes: %zd\n", bytesRead); |
| 124 | + printf("read data: %s\n", bytesRead > 0 ? readBuffer : "(none)"); |
| 125 | + printf("errno after read: %s\n", strerror(errno)); |
| 126 | + errno = 0; |
| 127 | + |
| 128 | + printf("\nTest 3: write to TTY\n"); |
| 129 | + ssize_t bytesWritten = write(fd, writeBuffer, strlen(writeBuffer)); |
| 130 | + printf("write bytes: %zd\n", bytesWritten); |
| 131 | + printf("errno after write: %s\n", strerror(errno)); |
| 132 | + errno = 0; |
| 133 | + |
| 134 | + printf("\nTest 4: ioctl TIOCGWINSZ\n"); |
| 135 | + int result = ioctl(fd, TIOCGWINSZ, &ws); |
| 136 | + printf("ioctl TIOCGWINSZ: %d\n", result); |
| 137 | + printf("ws_row: %d ws_col: %d\n", ws.ws_row, ws.ws_col); |
| 138 | + printf("errno after ioctl: %s\n", strerror(errno)); |
| 139 | + errno = 0; |
| 140 | + |
| 141 | + printf("\nTest 5: ioctl TCGETS\n"); |
| 142 | + result = ioctl(fd, TCGETS, &term); |
| 143 | + printf("ioctl TCGETS: %d\n", result); |
| 144 | + printf("errno after TCGETS: %s\n", strerror(errno)); |
| 145 | + errno = 0; |
| 146 | + |
| 147 | + printf("\nTest 6: ioctl TCSETS\n"); |
| 148 | + result = ioctl(fd, TCSETS, &term); |
| 149 | + printf("ioctl TCSETS: %d\n", result); |
| 150 | + printf("errno after TCSETS: %s\n", strerror(errno)); |
| 151 | + errno = 0; |
| 152 | + |
| 153 | + printf("\nTest 7: fsync\n"); |
| 154 | + result = fsync(fd); |
| 155 | + printf("fsync: %d\n", result); |
| 156 | + printf("errno after fsync: %s\n", strerror(errno)); |
| 157 | + errno = 0; |
| 158 | + |
| 159 | + close(fd); |
| 160 | + |
| 161 | + printf("\nTest 8: no put_char\n"); |
| 162 | + fd = open("/tty_no_putchar", O_WRONLY); |
| 163 | + printf("open: %d\n", fd >= 0 ? 1 : 0); |
| 164 | + bytesWritten = write(fd, writeBuffer, strlen(writeBuffer)); |
| 165 | + printf("write: %zd\n", bytesWritten); |
| 166 | + printf("errno: %s\n", strerror(errno)); |
| 167 | + errno = 0; |
| 168 | + close(fd); |
| 169 | + |
| 170 | + printf("\nTest 9: no get_char\n"); |
| 171 | + fd = open("/tty_no_getchar", O_RDONLY); |
| 172 | + printf("open: %d\n", fd >= 0 ? 1 : 0); |
| 173 | + bytesRead = read(fd, readBuffer, sizeof(readBuffer)); |
| 174 | + printf("read: %zd\n", bytesRead); |
| 175 | + printf("errno: %s\n", strerror(errno)); |
| 176 | + errno = 0; |
| 177 | + close(fd); |
| 178 | + |
| 179 | + printf("\nTest 10: put_char throws\n"); |
| 180 | + fd = open("/tty_throw_putchar", O_WRONLY); |
| 181 | + printf("open: %d\n", fd >= 0 ? 1 : 0); |
| 182 | + bytesWritten = write(fd, writeBuffer, strlen(writeBuffer)); |
| 183 | + printf("write: %zd\n", bytesWritten); |
| 184 | + printf("errno: %s\n", strerror(errno)); |
| 185 | + errno = 0; |
| 186 | + close(fd); |
| 187 | + |
| 188 | + printf("\nTest 11: get_char throws\n"); |
| 189 | + fd = open("/tty_throw_getchar", O_RDONLY); |
| 190 | + printf("open: %d\n", fd >= 0 ? 1 : 0); |
| 191 | + bytesRead = read(fd, readBuffer, sizeof(readBuffer)); |
| 192 | + printf("read: %zd\n", bytesRead); |
| 193 | + printf("errno: %s\n", strerror(errno)); |
| 194 | + errno = 0; |
| 195 | + close(fd); |
| 196 | + |
| 197 | + printf("\nTest 12: get_char returns undefined\n"); |
| 198 | + fd = open("/tty_empty", O_RDONLY); |
| 199 | + printf("open: %d\n", fd >= 0 ? 1 : 0); |
| 200 | + bytesRead = read(fd, readBuffer, sizeof(readBuffer)); |
| 201 | + printf("read: %zd\n", bytesRead); |
| 202 | + printf("errno: %s\n", strerror(errno)); |
| 203 | + errno = 0; |
| 204 | + close(fd); |
| 205 | + |
| 206 | + printf("\ndone\n"); |
| 207 | + return 0; |
| 208 | +} |
0 commit comments