xref: /openbmc/obmc-console/console-server.c (revision d831f960d32325bf4fa22ca6f73626ff0566636b)
1*d831f960SJeremy Kerr /**
2*d831f960SJeremy Kerr  * Console server process for OpenBMC
3*d831f960SJeremy Kerr  *
4*d831f960SJeremy Kerr  * Copyright © 2016 IBM Corporation <jk@ozlabs.org>
5*d831f960SJeremy Kerr  */
6*d831f960SJeremy Kerr 
7*d831f960SJeremy Kerr #include <stdint.h>
8*d831f960SJeremy Kerr #include <stdbool.h>
9*d831f960SJeremy Kerr #include <stdlib.h>
10*d831f960SJeremy Kerr #include <stdio.h>
11*d831f960SJeremy Kerr #include <fcntl.h>
12*d831f960SJeremy Kerr #include <unistd.h>
13*d831f960SJeremy Kerr #include <err.h>
14*d831f960SJeremy Kerr #include <termios.h>
15*d831f960SJeremy Kerr #include <string.h>
16*d831f960SJeremy Kerr #include <getopt.h>
17*d831f960SJeremy Kerr 
18*d831f960SJeremy Kerr #include <sys/types.h>
19*d831f960SJeremy Kerr #include <sys/poll.h>
20*d831f960SJeremy Kerr 
21*d831f960SJeremy Kerr #define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0]))
22*d831f960SJeremy Kerr 
23*d831f960SJeremy Kerr static const char esc_str[] = { '\r', '~', '.' };
24*d831f960SJeremy Kerr 
25*d831f960SJeremy Kerr struct console_ctx {
26*d831f960SJeremy Kerr 	const char	*tty_dev;
27*d831f960SJeremy Kerr 	int		tty_fd;
28*d831f960SJeremy Kerr 	int		console_fd_in;
29*d831f960SJeremy Kerr 	int		console_fd_out;
30*d831f960SJeremy Kerr 	bool		console_is_tty;
31*d831f960SJeremy Kerr 	struct termios	orig_termios;
32*d831f960SJeremy Kerr 	int		esc_str_pos;
33*d831f960SJeremy Kerr };
34*d831f960SJeremy Kerr 
35*d831f960SJeremy Kerr static void usage(const char *progname)
36*d831f960SJeremy Kerr {
37*d831f960SJeremy Kerr 	fprintf(stderr,
38*d831f960SJeremy Kerr "usage: %s [options]\n"
39*d831f960SJeremy Kerr "\n"
40*d831f960SJeremy Kerr "Options:\n"
41*d831f960SJeremy Kerr "  --device <TTY>  Use serial device TTY\n"
42*d831f960SJeremy Kerr "",
43*d831f960SJeremy Kerr 		progname);
44*d831f960SJeremy Kerr }
45*d831f960SJeremy Kerr 
46*d831f960SJeremy Kerr /**
47*d831f960SJeremy Kerr  * Open and initialise the serial device
48*d831f960SJeremy Kerr  */
49*d831f960SJeremy Kerr static int tty_init_io(struct console_ctx *ctx)
50*d831f960SJeremy Kerr {
51*d831f960SJeremy Kerr 	ctx->tty_fd = open(ctx->tty_dev, O_RDWR);
52*d831f960SJeremy Kerr 	if (ctx->tty_fd <= 0) {
53*d831f960SJeremy Kerr 		warn("Can't open tty %s", ctx->tty_dev);
54*d831f960SJeremy Kerr 		return -1;
55*d831f960SJeremy Kerr 	}
56*d831f960SJeremy Kerr 
57*d831f960SJeremy Kerr 	/* Disable character delay. We may want to later enable this when
58*d831f960SJeremy Kerr 	 * we detect larger amounts of data
59*d831f960SJeremy Kerr 	 */
60*d831f960SJeremy Kerr 	fcntl(ctx->tty_fd, F_SETFL, FNDELAY);
61*d831f960SJeremy Kerr 
62*d831f960SJeremy Kerr 	return 0;
63*d831f960SJeremy Kerr }
64*d831f960SJeremy Kerr 
65*d831f960SJeremy Kerr /*
66*d831f960SJeremy Kerr  * Setup our console channel for IO: use stdin/stdout, and if we're on a TTY,
67*d831f960SJeremy Kerr  * put it in canonical mode
68*d831f960SJeremy Kerr  */
69*d831f960SJeremy Kerr static int console_init_io(struct console_ctx *ctx)
70*d831f960SJeremy Kerr {
71*d831f960SJeremy Kerr 	struct termios termios;
72*d831f960SJeremy Kerr 	int rc;
73*d831f960SJeremy Kerr 
74*d831f960SJeremy Kerr 	ctx->console_fd_in = STDIN_FILENO;
75*d831f960SJeremy Kerr 	ctx->console_fd_out = STDOUT_FILENO;
76*d831f960SJeremy Kerr 	ctx->console_is_tty = isatty(ctx->console_fd_in);
77*d831f960SJeremy Kerr 
78*d831f960SJeremy Kerr 	if (!ctx->console_is_tty)
79*d831f960SJeremy Kerr 		return 0;
80*d831f960SJeremy Kerr 
81*d831f960SJeremy Kerr 	rc = tcgetattr(ctx->console_fd_in, &termios);
82*d831f960SJeremy Kerr 	if (rc) {
83*d831f960SJeremy Kerr 		warn("Can't get terminal attributes for console");
84*d831f960SJeremy Kerr 		return -1;
85*d831f960SJeremy Kerr 	}
86*d831f960SJeremy Kerr 	memcpy(&ctx->orig_termios, &termios, sizeof(ctx->orig_termios));
87*d831f960SJeremy Kerr 	cfmakeraw(&termios);
88*d831f960SJeremy Kerr 
89*d831f960SJeremy Kerr 	rc = tcsetattr(ctx->console_fd_in, TCSANOW, &termios);
90*d831f960SJeremy Kerr 	if (rc) {
91*d831f960SJeremy Kerr 		warn("Can't set terminal attributes for console");
92*d831f960SJeremy Kerr 		return -1;
93*d831f960SJeremy Kerr 	}
94*d831f960SJeremy Kerr 
95*d831f960SJeremy Kerr 	return 0;
96*d831f960SJeremy Kerr }
97*d831f960SJeremy Kerr 
98*d831f960SJeremy Kerr static int console_process_input(struct console_ctx *ctx,
99*d831f960SJeremy Kerr 		uint8_t *buf, size_t len)
100*d831f960SJeremy Kerr {
101*d831f960SJeremy Kerr 	unsigned long i;
102*d831f960SJeremy Kerr 	uint8_t e;
103*d831f960SJeremy Kerr 
104*d831f960SJeremy Kerr 	e = esc_str[ctx->esc_str_pos];
105*d831f960SJeremy Kerr 
106*d831f960SJeremy Kerr 	for (i = 0; i < len; i++) {
107*d831f960SJeremy Kerr 		if (buf[i] == e) {
108*d831f960SJeremy Kerr 			ctx->esc_str_pos++;
109*d831f960SJeremy Kerr 			if (ctx->esc_str_pos == ARRAY_SIZE(esc_str))
110*d831f960SJeremy Kerr 				return 1;
111*d831f960SJeremy Kerr 			e = esc_str[ctx->esc_str_pos];
112*d831f960SJeremy Kerr 		} else {
113*d831f960SJeremy Kerr 
114*d831f960SJeremy Kerr 			ctx->esc_str_pos = 0;
115*d831f960SJeremy Kerr 		}
116*d831f960SJeremy Kerr 	}
117*d831f960SJeremy Kerr 	return 0;
118*d831f960SJeremy Kerr }
119*d831f960SJeremy Kerr 
120*d831f960SJeremy Kerr static void console_restore_termios(struct console_ctx *ctx)
121*d831f960SJeremy Kerr {
122*d831f960SJeremy Kerr 	if (ctx->console_is_tty)
123*d831f960SJeremy Kerr 		tcsetattr(ctx->console_fd_in, TCSANOW, &ctx->orig_termios);
124*d831f960SJeremy Kerr }
125*d831f960SJeremy Kerr 
126*d831f960SJeremy Kerr static int write_buf_to_fd(int fd, uint8_t *buf, size_t len)
127*d831f960SJeremy Kerr {
128*d831f960SJeremy Kerr 	size_t pos;
129*d831f960SJeremy Kerr 	ssize_t rc;
130*d831f960SJeremy Kerr 
131*d831f960SJeremy Kerr 	for (pos = 0; pos < len; pos += rc) {
132*d831f960SJeremy Kerr 		rc = write(fd, buf + pos, len - pos);
133*d831f960SJeremy Kerr 		if (rc <= 0) {
134*d831f960SJeremy Kerr 			warn("Write error");
135*d831f960SJeremy Kerr 			return -1;
136*d831f960SJeremy Kerr 		}
137*d831f960SJeremy Kerr 	}
138*d831f960SJeremy Kerr 
139*d831f960SJeremy Kerr 	return 0;
140*d831f960SJeremy Kerr }
141*d831f960SJeremy Kerr 
142*d831f960SJeremy Kerr int run_console(struct console_ctx *ctx)
143*d831f960SJeremy Kerr {
144*d831f960SJeremy Kerr 	struct pollfd pollfds[2];
145*d831f960SJeremy Kerr 	int rc, len;
146*d831f960SJeremy Kerr 
147*d831f960SJeremy Kerr 	pollfds[0].fd = ctx->tty_fd;
148*d831f960SJeremy Kerr 	pollfds[0].events = POLLIN;
149*d831f960SJeremy Kerr 	pollfds[1].fd = ctx->console_fd_in;
150*d831f960SJeremy Kerr 	pollfds[1].events = POLLIN;
151*d831f960SJeremy Kerr 
152*d831f960SJeremy Kerr 	for (;;) {
153*d831f960SJeremy Kerr 		uint8_t buf[4096];
154*d831f960SJeremy Kerr 
155*d831f960SJeremy Kerr 		rc = poll(pollfds, 2, -1);
156*d831f960SJeremy Kerr 		if (rc < 0) {
157*d831f960SJeremy Kerr 			warn("poll error");
158*d831f960SJeremy Kerr 			return -1;
159*d831f960SJeremy Kerr 		}
160*d831f960SJeremy Kerr 
161*d831f960SJeremy Kerr 		if (pollfds[0].revents) {
162*d831f960SJeremy Kerr 			rc = read(ctx->tty_fd, buf, sizeof(buf));
163*d831f960SJeremy Kerr 			if (rc <= 0) {
164*d831f960SJeremy Kerr 				warn("Error reading from tty device");
165*d831f960SJeremy Kerr 				return -1;
166*d831f960SJeremy Kerr 			}
167*d831f960SJeremy Kerr 			rc = write_buf_to_fd(ctx->console_fd_out, buf, rc);
168*d831f960SJeremy Kerr 			if (rc < 0)
169*d831f960SJeremy Kerr 				return -1;
170*d831f960SJeremy Kerr 		}
171*d831f960SJeremy Kerr 		if (pollfds[1].revents) {
172*d831f960SJeremy Kerr 			rc = read(ctx->console_fd_in, buf, sizeof(buf));
173*d831f960SJeremy Kerr 			if (rc == 0)
174*d831f960SJeremy Kerr 				return 0;
175*d831f960SJeremy Kerr 
176*d831f960SJeremy Kerr 			if (rc <= 0) {
177*d831f960SJeremy Kerr 				warn("Error reading from console");
178*d831f960SJeremy Kerr 				return -1;
179*d831f960SJeremy Kerr 			}
180*d831f960SJeremy Kerr 			len = rc;
181*d831f960SJeremy Kerr 			rc = console_process_input(ctx, buf, len);
182*d831f960SJeremy Kerr 			if (rc) {
183*d831f960SJeremy Kerr 				rc = 0;
184*d831f960SJeremy Kerr 				return 0;
185*d831f960SJeremy Kerr 			}
186*d831f960SJeremy Kerr 			rc = write_buf_to_fd(ctx->tty_fd, buf, len);
187*d831f960SJeremy Kerr 			if (rc < 0)
188*d831f960SJeremy Kerr 				return -1;
189*d831f960SJeremy Kerr 		}
190*d831f960SJeremy Kerr 	}
191*d831f960SJeremy Kerr }
192*d831f960SJeremy Kerr 
193*d831f960SJeremy Kerr static const struct option options[] = {
194*d831f960SJeremy Kerr 	{ "device",	required_argument,	0, 'd'},
195*d831f960SJeremy Kerr 	{ },
196*d831f960SJeremy Kerr };
197*d831f960SJeremy Kerr 
198*d831f960SJeremy Kerr int main(int argc, char **argv)
199*d831f960SJeremy Kerr {
200*d831f960SJeremy Kerr 	struct console_ctx *ctx;
201*d831f960SJeremy Kerr 	int rc;
202*d831f960SJeremy Kerr 
203*d831f960SJeremy Kerr 	ctx = malloc(sizeof(struct console_ctx));
204*d831f960SJeremy Kerr 	memset(ctx, 0, sizeof(*ctx));
205*d831f960SJeremy Kerr 
206*d831f960SJeremy Kerr 	for (;;) {
207*d831f960SJeremy Kerr 		int c, idx;
208*d831f960SJeremy Kerr 
209*d831f960SJeremy Kerr 		c = getopt_long(argc, argv, "d", options, &idx);
210*d831f960SJeremy Kerr 		if (c == -1)
211*d831f960SJeremy Kerr 			break;
212*d831f960SJeremy Kerr 
213*d831f960SJeremy Kerr 		switch (c) {
214*d831f960SJeremy Kerr 		case 'd':
215*d831f960SJeremy Kerr 			ctx->tty_dev = optarg;
216*d831f960SJeremy Kerr 			break;
217*d831f960SJeremy Kerr 
218*d831f960SJeremy Kerr 		case 'h':
219*d831f960SJeremy Kerr 		case '?':
220*d831f960SJeremy Kerr 			usage(argv[0]);
221*d831f960SJeremy Kerr 			break;
222*d831f960SJeremy Kerr 		}
223*d831f960SJeremy Kerr 	}
224*d831f960SJeremy Kerr 
225*d831f960SJeremy Kerr 	if (!ctx->tty_dev) {
226*d831f960SJeremy Kerr 		fprintf(stderr,
227*d831f960SJeremy Kerr 			"Error: No TTY device specified (use --device)\n");
228*d831f960SJeremy Kerr 		return EXIT_FAILURE;
229*d831f960SJeremy Kerr 	}
230*d831f960SJeremy Kerr 
231*d831f960SJeremy Kerr 	rc = tty_init_io(ctx);
232*d831f960SJeremy Kerr 	if (rc)
233*d831f960SJeremy Kerr 		return EXIT_FAILURE;
234*d831f960SJeremy Kerr 
235*d831f960SJeremy Kerr 	rc = console_init_io(ctx);
236*d831f960SJeremy Kerr 	if (rc)
237*d831f960SJeremy Kerr 		return EXIT_FAILURE;
238*d831f960SJeremy Kerr 
239*d831f960SJeremy Kerr 	rc = run_console(ctx);
240*d831f960SJeremy Kerr 
241*d831f960SJeremy Kerr 	console_restore_termios(ctx);
242*d831f960SJeremy Kerr 
243*d831f960SJeremy Kerr 	free(ctx);
244*d831f960SJeremy Kerr 
245*d831f960SJeremy Kerr 	return rc == 0 ? EXIT_SUCCESS : EXIT_FAILURE;
246*d831f960SJeremy Kerr }
247