xref: /openbmc/obmc-console/console-client.c (revision ff569830)
1 /**
2  * Copyright © 2016 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 <err.h>
18 #include <stdbool.h>
19 #include <stdint.h>
20 #include <stdio.h>
21 #include <stdlib.h>
22 #include <string.h>
23 #include <termios.h>
24 #include <unistd.h>
25 
26 #include <sys/socket.h>
27 #include <sys/un.h>
28 
29 #include "console-server.h"
30 
31 enum process_rc {
32 	PROCESS_OK = 0,
33 	PROCESS_ERR,
34 	PROCESS_EXIT,
35 };
36 
37 enum esc_type {
38 	ESC_TYPE_SSH,
39 };
40 
41 struct ssh_esc_state {
42 	uint8_t state;
43 };
44 
45 struct console_client {
46 	int		console_sd;
47 	int		fd_in;
48 	int		fd_out;
49 	bool		is_tty;
50 	struct termios	orig_termios;
51 	enum esc_type	esc_type;
52 	union {
53 		struct ssh_esc_state ssh;
54 	} esc_state;
55 };
56 
57 static enum process_rc process_ssh_tty(
58 		struct console_client *client, const uint8_t *buf, size_t len)
59 {
60 	struct ssh_esc_state *esc_state = &client->esc_state.ssh;
61 	const uint8_t *out_buf = buf;
62 	int rc;
63 
64 	for (size_t i = 0; i < len; ++i) {
65 		switch (buf[i])
66 		{
67 		case '.':
68 			if (esc_state->state != '~') {
69 				esc_state->state = '\0';
70 				break;
71 			}
72 			return PROCESS_EXIT;
73 		case '~':
74 			if (esc_state->state != '\r') {
75 				esc_state->state = '\0';
76 				break;
77 			}
78 			esc_state->state = '~';
79 			/* We need to print everything to skip the tilde */
80 			rc = write_buf_to_fd(
81 				client->console_sd, out_buf, i-(out_buf-buf));
82 			if (rc < 0)
83 				return PROCESS_ERR;
84 			out_buf = &buf[i+1];
85 			break;
86 		case '\r':
87 			esc_state->state = '\r';
88 			break;
89 		default:
90 			esc_state->state = '\0';
91 		}
92 	}
93 
94 	rc = write_buf_to_fd(client->console_sd, out_buf, len-(out_buf-buf));
95 	return rc < 0 ? PROCESS_ERR : PROCESS_OK;
96 }
97 
98 static enum process_rc process_tty(struct console_client *client)
99 {
100 	uint8_t buf[4096];
101 	ssize_t len;
102 
103 	len = read(client->fd_in, buf, sizeof(buf));
104 	if (len < 0)
105 		return PROCESS_ERR;
106 	if (len == 0)
107 		return PROCESS_EXIT;
108 
109 	switch (client->esc_type)
110 	{
111 	case ESC_TYPE_SSH:
112 		return process_ssh_tty(client, buf, len);
113 	default:
114 		return PROCESS_ERR;
115 	}
116 }
117 
118 
119 static int process_console(struct console_client *client)
120 {
121 	uint8_t buf[4096];
122 	int len, rc;
123 
124 	len = read(client->console_sd, buf, sizeof(buf));
125 	if (len < 0) {
126 		warn("Can't read from server");
127 		return PROCESS_ERR;
128 	}
129 	if (len == 0) {
130 		fprintf(stderr, "Connection closed\n");
131 		return PROCESS_EXIT;
132 	}
133 
134 	rc = write_buf_to_fd(client->fd_out, buf, len);
135 	return rc ? PROCESS_ERR : PROCESS_OK;
136 }
137 
138 /*
139  * Setup our local file descriptors for IO: use stdin/stdout, and if we're on a
140  * TTY, put it in canonical mode
141  */
142 static int client_tty_init(struct console_client *client)
143 {
144 	struct termios termios;
145 	int rc;
146 
147 	client->fd_in = STDIN_FILENO;
148 	client->fd_out = STDOUT_FILENO;
149 	client->is_tty = isatty(client->fd_in);
150 
151 	if (!client->is_tty)
152 		return 0;
153 
154 	rc = tcgetattr(client->fd_in, &termios);
155 	if (rc) {
156 		warn("Can't get terminal attributes for console");
157 		return -1;
158 	}
159 	memcpy(&client->orig_termios, &termios, sizeof(client->orig_termios));
160 	cfmakeraw(&termios);
161 
162 	rc = tcsetattr(client->fd_in, TCSANOW, &termios);
163 	if (rc) {
164 		warn("Can't set terminal attributes for console");
165 		return -1;
166 	}
167 
168 	return 0;
169 }
170 
171 static int client_init(struct console_client *client)
172 {
173 	struct sockaddr_un addr;
174 	int rc;
175 
176 	client->console_sd = socket(AF_UNIX, SOCK_STREAM, 0);
177 	if (!client->console_sd) {
178 		warn("Can't open socket");
179 		return -1;
180 	}
181 
182 	memset(&addr, 0, sizeof(addr));
183 	addr.sun_family = AF_UNIX;
184 	memcpy(&addr.sun_path, &console_socket_path, console_socket_path_len);
185 
186 	rc = connect(client->console_sd, (struct sockaddr *)&addr,
187 			sizeof(addr) - sizeof(addr.sun_path) + console_socket_path_len);
188 	if (rc) {
189 		warn("Can't connect to console server");
190 		close(client->console_sd);
191 		return -1;
192 	}
193 
194 	return 0;
195 }
196 
197 static void client_fini(struct console_client *client)
198 {
199 	if (client->is_tty)
200 		tcsetattr(client->fd_in, TCSANOW, &client->orig_termios);
201 	close(client->console_sd);
202 }
203 
204 int main(void)
205 {
206 	struct console_client _client, *client;
207 	struct pollfd pollfds[2];
208 	enum process_rc prc;
209 	int rc;
210 
211 	client = &_client;
212 	memset(client, 0, sizeof(*client));
213 	client->esc_type = ESC_TYPE_SSH;
214 
215 	rc = client_init(client);
216 	if (rc)
217 		return EXIT_FAILURE;
218 
219 	rc = client_tty_init(client);
220 	if (rc)
221 		goto out_fini;
222 
223 	prc = PROCESS_OK;
224 	for (;;) {
225 		pollfds[0].fd = client->fd_in;
226 		pollfds[0].events = POLLIN;
227 		pollfds[1].fd = client->console_sd;
228 		pollfds[1].events = POLLIN;
229 
230 		rc = poll(pollfds, 2, -1);
231 		if (rc < 0) {
232 			warn("Poll failure");
233 			break;
234 		}
235 
236 		if (pollfds[0].revents)
237 			prc = process_tty(client);
238 
239 		if (prc == PROCESS_OK && pollfds[1].revents)
240 			prc = process_console(client);
241 
242 		rc = (prc == PROCESS_ERR) ? -1 : 0;
243 		if (prc != PROCESS_OK)
244 			break;
245 	}
246 
247 out_fini:
248 	client_fini(client);
249 	return rc ? EXIT_FAILURE : EXIT_SUCCESS;
250 }
251 
252