blob: 1f8517611019a63f41e8c205697d65b6e5997f9d [file] [log] [blame]
Jeremy Kerr9326d772016-03-17 17:15:02 +08001/**
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 */
Jeremy Kerr2bd05182016-03-10 16:59:43 +080016
17#include <err.h>
Andrew Jeffery5e7c0782020-02-10 12:12:36 +103018#include <errno.h>
William A. Kennington III15691c82019-07-24 23:11:18 -070019#include <getopt.h>
Jeremy Kerr2bd05182016-03-10 16:59:43 +080020#include <stdbool.h>
21#include <stdint.h>
22#include <stdio.h>
23#include <stdlib.h>
24#include <string.h>
25#include <termios.h>
26#include <unistd.h>
27
28#include <sys/socket.h>
29#include <sys/un.h>
30
31#include "console-server.h"
32
William A. Kennington III8a154352019-07-24 23:01:47 -070033#define EXIT_ESCAPE 2
34
Jeremy Kerr2bd05182016-03-10 16:59:43 +080035enum process_rc {
36 PROCESS_OK = 0,
37 PROCESS_ERR,
38 PROCESS_EXIT,
William A. Kennington III8a154352019-07-24 23:01:47 -070039 PROCESS_ESC,
Jeremy Kerr2bd05182016-03-10 16:59:43 +080040};
41
William A. Kennington IIIff569832019-07-24 23:07:15 -070042enum esc_type {
43 ESC_TYPE_SSH,
William A. Kennington III15691c82019-07-24 23:11:18 -070044 ESC_TYPE_STR,
William A. Kennington IIIff569832019-07-24 23:07:15 -070045};
46
47struct ssh_esc_state {
48 uint8_t state;
49};
50
William A. Kennington III15691c82019-07-24 23:11:18 -070051struct str_esc_state {
52 const uint8_t *str;
53 size_t pos;
54};
55
Jeremy Kerr2bd05182016-03-10 16:59:43 +080056struct console_client {
57 int console_sd;
58 int fd_in;
59 int fd_out;
60 bool is_tty;
61 struct termios orig_termios;
William A. Kennington IIIff569832019-07-24 23:07:15 -070062 enum esc_type esc_type;
63 union {
64 struct ssh_esc_state ssh;
William A. Kennington III15691c82019-07-24 23:11:18 -070065 struct str_esc_state str;
William A. Kennington IIIff569832019-07-24 23:07:15 -070066 } esc_state;
Jeremy Kerr2bd05182016-03-10 16:59:43 +080067};
68
William A. Kennington IIIff569832019-07-24 23:07:15 -070069static enum process_rc process_ssh_tty(
70 struct console_client *client, const uint8_t *buf, size_t len)
71{
72 struct ssh_esc_state *esc_state = &client->esc_state.ssh;
73 const uint8_t *out_buf = buf;
74 int rc;
75
76 for (size_t i = 0; i < len; ++i) {
77 switch (buf[i])
78 {
79 case '.':
80 if (esc_state->state != '~') {
81 esc_state->state = '\0';
82 break;
83 }
William A. Kennington III8a154352019-07-24 23:01:47 -070084 return PROCESS_ESC;
William A. Kennington IIIff569832019-07-24 23:07:15 -070085 case '~':
86 if (esc_state->state != '\r') {
87 esc_state->state = '\0';
88 break;
89 }
90 esc_state->state = '~';
91 /* We need to print everything to skip the tilde */
92 rc = write_buf_to_fd(
93 client->console_sd, out_buf, i-(out_buf-buf));
94 if (rc < 0)
95 return PROCESS_ERR;
96 out_buf = &buf[i+1];
97 break;
98 case '\r':
99 esc_state->state = '\r';
100 break;
101 default:
102 esc_state->state = '\0';
103 }
104 }
105
106 rc = write_buf_to_fd(client->console_sd, out_buf, len-(out_buf-buf));
107 return rc < 0 ? PROCESS_ERR : PROCESS_OK;
108}
Jeremy Kerr2bd05182016-03-10 16:59:43 +0800109
William A. Kennington III15691c82019-07-24 23:11:18 -0700110static enum process_rc process_str_tty(
111 struct console_client *client, const uint8_t *buf, size_t len)
112{
113 struct str_esc_state *esc_state = &client->esc_state.str;
114 enum process_rc prc = PROCESS_OK;
115 size_t i;
116
117 for (i = 0; i < len; ++i) {
118 if (buf[i] == esc_state->str[esc_state->pos])
119 esc_state->pos++;
120 else
121 esc_state->pos = 0;
122
123 if (esc_state->str[esc_state->pos] == '\0') {
William A. Kennington III8a154352019-07-24 23:01:47 -0700124 prc = PROCESS_ESC;
William A. Kennington III5b16dc82019-07-26 01:18:05 -0700125 ++i;
William A. Kennington III15691c82019-07-24 23:11:18 -0700126 break;
127 }
128 }
129
130 if (write_buf_to_fd(client->console_sd, buf, i) < 0)
131 return PROCESS_ERR;
132 return prc;
133}
134
Jeremy Kerr2bd05182016-03-10 16:59:43 +0800135static enum process_rc process_tty(struct console_client *client)
136{
William A. Kennington IIIff569832019-07-24 23:07:15 -0700137 uint8_t buf[4096];
Jeremy Kerr2bd05182016-03-10 16:59:43 +0800138 ssize_t len;
Jeremy Kerr2bd05182016-03-10 16:59:43 +0800139
140 len = read(client->fd_in, buf, sizeof(buf));
141 if (len < 0)
142 return PROCESS_ERR;
143 if (len == 0)
144 return PROCESS_EXIT;
145
William A. Kennington IIIff569832019-07-24 23:07:15 -0700146 switch (client->esc_type)
147 {
148 case ESC_TYPE_SSH:
149 return process_ssh_tty(client, buf, len);
William A. Kennington III15691c82019-07-24 23:11:18 -0700150 case ESC_TYPE_STR:
151 return process_str_tty(client, buf, len);
William A. Kennington IIIff569832019-07-24 23:07:15 -0700152 default:
Jeremy Kerr2bd05182016-03-10 16:59:43 +0800153 return PROCESS_ERR;
William A. Kennington IIIff569832019-07-24 23:07:15 -0700154 }
Jeremy Kerr2bd05182016-03-10 16:59:43 +0800155}
156
157
158static int process_console(struct console_client *client)
159{
160 uint8_t buf[4096];
161 int len, rc;
162
163 len = read(client->console_sd, buf, sizeof(buf));
164 if (len < 0) {
165 warn("Can't read from server");
166 return PROCESS_ERR;
167 }
168 if (len == 0) {
169 fprintf(stderr, "Connection closed\n");
170 return PROCESS_EXIT;
171 }
172
173 rc = write_buf_to_fd(client->fd_out, buf, len);
174 return rc ? PROCESS_ERR : PROCESS_OK;
175}
176
177/*
178 * Setup our local file descriptors for IO: use stdin/stdout, and if we're on a
179 * TTY, put it in canonical mode
180 */
181static int client_tty_init(struct console_client *client)
182{
183 struct termios termios;
184 int rc;
185
186 client->fd_in = STDIN_FILENO;
187 client->fd_out = STDOUT_FILENO;
188 client->is_tty = isatty(client->fd_in);
189
190 if (!client->is_tty)
191 return 0;
192
193 rc = tcgetattr(client->fd_in, &termios);
194 if (rc) {
195 warn("Can't get terminal attributes for console");
196 return -1;
197 }
198 memcpy(&client->orig_termios, &termios, sizeof(client->orig_termios));
199 cfmakeraw(&termios);
200
201 rc = tcsetattr(client->fd_in, TCSANOW, &termios);
202 if (rc) {
203 warn("Can't set terminal attributes for console");
204 return -1;
205 }
206
207 return 0;
208}
209
Andrew Jefferyddf2ab72020-02-10 12:36:09 +1030210static int client_init(struct console_client *client, const char *socket_id)
Jeremy Kerr2bd05182016-03-10 16:59:43 +0800211{
212 struct sockaddr_un addr;
Andrew Jeffery6ed0e4e2020-02-21 14:35:39 +1030213 socket_path_t path;
Andrew Jeffery5e7c0782020-02-10 12:12:36 +1030214 ssize_t len;
Jeremy Kerr2bd05182016-03-10 16:59:43 +0800215 int rc;
216
217 client->console_sd = socket(AF_UNIX, SOCK_STREAM, 0);
218 if (!client->console_sd) {
219 warn("Can't open socket");
220 return -1;
221 }
222
223 memset(&addr, 0, sizeof(addr));
224 addr.sun_family = AF_UNIX;
Andrew Jefferyddf2ab72020-02-10 12:36:09 +1030225 len = console_socket_path(&addr, socket_id);
Andrew Jeffery5e7c0782020-02-10 12:12:36 +1030226 if (len < 0) {
227 if (errno)
228 warn("Failed to configure socket: %s", strerror(errno));
229 else
230 warn("Socket name length exceeds buffer limits");
231 goto cleanup;
Jeremy Kerr2bd05182016-03-10 16:59:43 +0800232 }
233
Andrew Jeffery5e7c0782020-02-10 12:12:36 +1030234 rc = connect(client->console_sd, (struct sockaddr *)&addr,
235 sizeof(addr) - sizeof(addr.sun_path) + len);
236 if (!rc)
237 return 0;
238
Andrew Jeffery6ed0e4e2020-02-21 14:35:39 +1030239 console_socket_path_readable(&addr, len, path);
240 warn("Can't connect to console server '@%s'", path);
Andrew Jeffery5e7c0782020-02-10 12:12:36 +1030241cleanup:
242 close(client->console_sd);
243 return -1;
Jeremy Kerr2bd05182016-03-10 16:59:43 +0800244}
245
246static void client_fini(struct console_client *client)
247{
248 if (client->is_tty)
249 tcsetattr(client->fd_in, TCSANOW, &client->orig_termios);
250 close(client->console_sd);
251}
252
William A. Kennington III15691c82019-07-24 23:11:18 -0700253int main(int argc, char *argv[])
Jeremy Kerr2bd05182016-03-10 16:59:43 +0800254{
255 struct console_client _client, *client;
256 struct pollfd pollfds[2];
William A. Kennington III8a154352019-07-24 23:01:47 -0700257 enum process_rc prc = PROCESS_OK;
Andrew Jeffery71e7a242020-02-12 22:58:16 +1030258 const char *config_path = NULL;
259 struct config *config = NULL;
Andrew Jefferyddf2ab72020-02-10 12:36:09 +1030260 const char *socket_id = NULL;
Andrew Jeffery71e7a242020-02-12 22:58:16 +1030261 const uint8_t *esc = NULL;
Jeremy Kerr2bd05182016-03-10 16:59:43 +0800262 int rc;
263
264 client = &_client;
265 memset(client, 0, sizeof(*client));
William A. Kennington IIIff569832019-07-24 23:07:15 -0700266 client->esc_type = ESC_TYPE_SSH;
Jeremy Kerr2bd05182016-03-10 16:59:43 +0800267
William A. Kennington III15691c82019-07-24 23:11:18 -0700268 for (;;) {
Andrew Jeffery71e7a242020-02-12 22:58:16 +1030269 rc = getopt(argc, argv, "c:e:i:");
William A. Kennington III15691c82019-07-24 23:11:18 -0700270 if (rc == -1)
271 break;
272
273 switch (rc) {
Andrew Jeffery71e7a242020-02-12 22:58:16 +1030274 case 'c':
275 if (optarg[0] == '\0') {
276 fprintf(stderr, "Config str cannot be empty\n");
277 return EXIT_FAILURE;
278 }
279 config_path = optarg;
280 break;
William A. Kennington III15691c82019-07-24 23:11:18 -0700281 case 'e':
282 if (optarg[0] == '\0') {
283 fprintf(stderr, "Escape str cannot be empty\n");
284 return EXIT_FAILURE;
285 }
Andrew Jeffery71e7a242020-02-12 22:58:16 +1030286 esc = (const uint8_t*)optarg;
William A. Kennington III15691c82019-07-24 23:11:18 -0700287 break;
Andrew Jefferyddf2ab72020-02-10 12:36:09 +1030288 case 'i':
289 if (optarg[0] == '\0') {
290 fprintf(stderr, "Socket ID str cannot be empty\n");
291 return EXIT_FAILURE;
292 }
293 socket_id = optarg;
294 break;
William A. Kennington III15691c82019-07-24 23:11:18 -0700295 default:
296 fprintf(stderr,
297 "Usage: %s "
Andrew Jefferyddf2ab72020-02-10 12:36:09 +1030298 "[-e <escape sequence>]"
Andrew Jeffery71e7a242020-02-12 22:58:16 +1030299 "[-i <socket ID>]"
300 "[-c <config>]\n",
William A. Kennington III15691c82019-07-24 23:11:18 -0700301 argv[0]);
302 return EXIT_FAILURE;
303 }
304 }
305
Andrew Jeffery71e7a242020-02-12 22:58:16 +1030306 if (config_path) {
307 config = config_init(config_path);
308 if (!config) {
309 warnx("Can't read configuration, exiting.");
310 return EXIT_FAILURE;
311 }
312
313 if (!esc)
314 esc = (const uint8_t *)config_get_value(config, "escape-sequence");
315
316 if (!socket_id)
317 socket_id = config_get_value(config, "socket-id");
318 }
319
320 if (esc) {
321 client->esc_type = ESC_TYPE_STR;
322 client->esc_state.str.str = esc;
323 }
324
Andrew Jefferyddf2ab72020-02-10 12:36:09 +1030325 rc = client_init(client, socket_id);
Jeremy Kerr2bd05182016-03-10 16:59:43 +0800326 if (rc)
Andrew Jeffery71e7a242020-02-12 22:58:16 +1030327 goto out_config_fini;
Jeremy Kerr2bd05182016-03-10 16:59:43 +0800328
329 rc = client_tty_init(client);
330 if (rc)
Andrew Jeffery71e7a242020-02-12 22:58:16 +1030331 goto out_client_fini;
Jeremy Kerr2bd05182016-03-10 16:59:43 +0800332
Jeremy Kerr2bd05182016-03-10 16:59:43 +0800333 for (;;) {
334 pollfds[0].fd = client->fd_in;
335 pollfds[0].events = POLLIN;
336 pollfds[1].fd = client->console_sd;
337 pollfds[1].events = POLLIN;
338
339 rc = poll(pollfds, 2, -1);
340 if (rc < 0) {
341 warn("Poll failure");
342 break;
343 }
344
345 if (pollfds[0].revents)
346 prc = process_tty(client);
347
348 if (prc == PROCESS_OK && pollfds[1].revents)
349 prc = process_console(client);
350
351 rc = (prc == PROCESS_ERR) ? -1 : 0;
352 if (prc != PROCESS_OK)
353 break;
354 }
355
Andrew Jeffery71e7a242020-02-12 22:58:16 +1030356out_client_fini:
Jeremy Kerr2bd05182016-03-10 16:59:43 +0800357 client_fini(client);
Andrew Jeffery71e7a242020-02-12 22:58:16 +1030358
359out_config_fini:
360 if (config_path)
361 config_fini(config);
362
William A. Kennington III8a154352019-07-24 23:01:47 -0700363 if (prc == PROCESS_ESC)
364 return EXIT_ESCAPE;
Jeremy Kerr2bd05182016-03-10 16:59:43 +0800365 return rc ? EXIT_FAILURE : EXIT_SUCCESS;
366}
367