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