1 /** 2 * Copyright © 2019 IBM Corporation 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 #include <assert.h> 18 #include <stdint.h> 19 #include <stdio.h> 20 21 #define read __read 22 #include "config.c" 23 #include "console-socket.c" 24 #define main __main 25 #include "console-client.c" 26 #undef read 27 #undef main 28 29 struct test { 30 enum esc_type esc_type; 31 union { 32 struct ssh_esc_state ssh; 33 struct str_esc_state str; 34 } esc_state; 35 const char *in[4]; 36 int n_in; 37 const char *exp_out; 38 int exp_rc; 39 }; 40 41 struct test_ctx { 42 struct console_client client; 43 struct test *test; 44 uint8_t out[4096]; 45 int cur_in; 46 int cur_out; 47 }; 48 49 50 struct test tests[] = { 51 { 52 /* no escape code */ 53 .esc_type = ESC_TYPE_SSH, 54 .in = {"a"}, 55 .n_in = 1, 56 .exp_out = "a", 57 .exp_rc = PROCESS_EXIT, 58 }, 59 { 60 /* no escape code, multiple reads */ 61 .esc_type = ESC_TYPE_SSH, 62 .in = {"a", "b"}, 63 .n_in = 2, 64 .exp_out = "ab", 65 .exp_rc = PROCESS_EXIT, 66 }, 67 { 68 /* ssh escape in one read */ 69 .esc_type = ESC_TYPE_SSH, 70 .in = {"a\r~."}, 71 .n_in = 1, 72 .exp_out = "a\r", 73 .exp_rc = PROCESS_ESC, 74 }, 75 { 76 /* ssh escape, partial ~ is not output. */ 77 .esc_type = ESC_TYPE_SSH, 78 .in = {"a\r~"}, 79 .n_in = 1, 80 .exp_out = "a\r", 81 .exp_rc = PROCESS_EXIT, 82 }, 83 { 84 /* ssh escape split into individual reads */ 85 .esc_type = ESC_TYPE_SSH, 86 .in = {"a", "\r", "~", "."}, 87 .n_in = 4, 88 .exp_out = "a\r", 89 .exp_rc = PROCESS_ESC, 90 }, 91 { 92 /* ssh escape, escaped. */ 93 .esc_type = ESC_TYPE_SSH, 94 .in = {"a\r~~."}, 95 .n_in = 1, 96 .exp_out = "a\r~.", 97 .exp_rc = PROCESS_EXIT, 98 }, 99 { 100 /* ssh escape, escaped ~, and not completed. */ 101 .esc_type = ESC_TYPE_SSH, 102 .in = {"a\r~~"}, 103 .n_in = 1, 104 .exp_out = "a\r~", 105 .exp_rc = PROCESS_EXIT, 106 }, 107 { 108 /* str escape, no match */ 109 .esc_type = ESC_TYPE_STR, 110 .esc_state = { .str = { .str = (const uint8_t *)"c" } }, 111 .in = {"ab"}, 112 .n_in = 1, 113 .exp_out = "ab", 114 .exp_rc = PROCESS_EXIT, 115 }, 116 { 117 /* str escape, one byte, as one read */ 118 .esc_type = ESC_TYPE_STR, 119 .esc_state = { .str = { .str = (const uint8_t *)"b" } }, 120 .in = {"abc"}, 121 .n_in = 1, 122 .exp_out = "ab", 123 .exp_rc = PROCESS_ESC, 124 }, 125 { 126 /* str escape, multiple bytes, as one read */ 127 .esc_type = ESC_TYPE_STR, 128 .esc_state = { .str = { .str = (const uint8_t *)"bc" } }, 129 .in = {"abcd"}, 130 .n_in = 1, 131 .exp_out = "abc", 132 .exp_rc = PROCESS_ESC, 133 }, 134 { 135 /* str escape, multiple bytes, split over reads */ 136 .esc_type = ESC_TYPE_STR, 137 .esc_state = { .str = { .str = (const uint8_t *)"bc" } }, 138 .in = {"ab", "cd"}, 139 .n_in = 2, 140 .exp_out = "abc", 141 .exp_rc = PROCESS_ESC, 142 }, 143 { 144 /* str escape, not matched due to intermediate data */ 145 .esc_type = ESC_TYPE_STR, 146 .esc_state = { .str = { .str = (const uint8_t *)"ab" } }, 147 .in = {"acb"}, 148 .n_in = 1, 149 .exp_out = "acb", 150 .exp_rc = PROCESS_EXIT, 151 }, 152 }; 153 154 #define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0])) 155 156 struct test_ctx ctxs[ARRAY_SIZE(tests)]; 157 158 int write_buf_to_fd(int fd, const uint8_t *buf, size_t len) 159 { 160 struct test_ctx *ctx = &ctxs[fd]; 161 162 assert(ctx->cur_out + len <= sizeof(ctx->out)); 163 memcpy(ctx->out + ctx->cur_out, buf, len); 164 ctx->cur_out += len; 165 166 return 0; 167 } 168 169 ssize_t __read(int fd, void *buf, size_t len) 170 { 171 struct test_ctx *ctx = &ctxs[fd]; 172 const char *inbuf; 173 size_t inlen; 174 175 if (ctx->cur_in >= ctx->test->n_in) 176 return 0; 177 178 inbuf = ctx->test->in[ctx->cur_in]; 179 inlen = strlen(inbuf); 180 assert(inlen <= len); 181 memcpy(buf, inbuf, inlen); 182 ctx->cur_in++; 183 return inlen; 184 } 185 186 void run_one_test(int idx, struct test *test, struct test_ctx *ctx) 187 { 188 size_t exp_out_len; 189 int rc; 190 191 /* we store the index into the context array as a FD, so we 192 * can refer to it through the read & write callbacks. 193 */ 194 ctx->client.console_sd = idx; 195 ctx->client.fd_in = idx; 196 ctx->client.esc_type = test->esc_type; 197 memcpy(&ctx->client.esc_state, &test->esc_state, 198 sizeof(test->esc_state)); 199 ctx->test = test; 200 201 for (;;) { 202 rc = process_tty(&ctx->client); 203 if (rc != PROCESS_OK) 204 break; 205 } 206 207 exp_out_len = strlen(test->exp_out); 208 209 #ifdef DEBUG 210 printf("got: rc %d %s(%d), exp: rc %d %s(%ld)\n", 211 rc, ctx->out, ctx->cur_out, 212 test->exp_rc, test->exp_out, exp_out_len); 213 fflush(stdout); 214 #endif 215 assert(rc == test->exp_rc); 216 assert(exp_out_len == ctx->cur_out); 217 assert(!memcmp(ctx->out, test->exp_out, exp_out_len)); 218 } 219 220 int main(void) 221 { 222 int i; 223 224 for (i = 0; i < ARRAY_SIZE(tests); i++) 225 run_one_test(i, &tests[i], &ctxs[i]); 226 227 return EXIT_SUCCESS; 228 } 229