blob: 67d163abb7790672d5dba5721162f00177c16130 [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"
Alexander Hansen1e04f442024-06-12 16:35:58 +020032#include "config.h"
Jeremy Kerr2bd05182016-03-10 16:59:43 +080033
William A. Kennington III8a154352019-07-24 23:01:47 -070034#define EXIT_ESCAPE 2
35
Jeremy Kerr2bd05182016-03-10 16:59:43 +080036enum process_rc {
37 PROCESS_OK = 0,
38 PROCESS_ERR,
39 PROCESS_EXIT,
William A. Kennington III8a154352019-07-24 23:01:47 -070040 PROCESS_ESC,
Jeremy Kerr2bd05182016-03-10 16:59:43 +080041};
42
William A. Kennington IIIff569832019-07-24 23:07:15 -070043enum esc_type {
44 ESC_TYPE_SSH,
William A. Kennington III15691c82019-07-24 23:11:18 -070045 ESC_TYPE_STR,
William A. Kennington IIIff569832019-07-24 23:07:15 -070046};
47
48struct ssh_esc_state {
49 uint8_t state;
50};
51
William A. Kennington III15691c82019-07-24 23:11:18 -070052struct str_esc_state {
53 const uint8_t *str;
54 size_t pos;
55};
56
Jeremy Kerr2bd05182016-03-10 16:59:43 +080057struct console_client {
Andrew Jefferya72711a2023-04-18 18:19:41 +093058 int console_sd;
59 int fd_in;
60 int fd_out;
61 bool is_tty;
62 struct termios orig_termios;
63 enum esc_type esc_type;
William A. Kennington IIIff569832019-07-24 23:07:15 -070064 union {
65 struct ssh_esc_state ssh;
William A. Kennington III15691c82019-07-24 23:11:18 -070066 struct str_esc_state str;
William A. Kennington IIIff569832019-07-24 23:07:15 -070067 } esc_state;
Jeremy Kerr2bd05182016-03-10 16:59:43 +080068};
69
Andrew Jefferya72711a2023-04-18 18:19:41 +093070static enum process_rc process_ssh_tty(struct console_client *client,
71 const uint8_t *buf, size_t len)
William A. Kennington IIIff569832019-07-24 23:07:15 -070072{
73 struct ssh_esc_state *esc_state = &client->esc_state.ssh;
74 const uint8_t *out_buf = buf;
75 int rc;
76
77 for (size_t i = 0; i < len; ++i) {
Andrew Jefferya72711a2023-04-18 18:19:41 +093078 switch (buf[i]) {
William A. Kennington IIIff569832019-07-24 23:07:15 -070079 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 */
Andrew Jefferya72711a2023-04-18 18:19:41 +093092 rc = write_buf_to_fd(client->console_sd, out_buf,
93 i - (out_buf - buf));
Andrew Jeffery2834c5b2023-04-19 12:47:09 +093094 if (rc < 0) {
William A. Kennington IIIff569832019-07-24 23:07:15 -070095 return PROCESS_ERR;
Andrew Jeffery2834c5b2023-04-19 12:47:09 +093096 }
Andrew Jefferya72711a2023-04-18 18:19:41 +093097 out_buf = &buf[i + 1];
William A. Kennington IIIff569832019-07-24 23:07:15 -070098 break;
99 case '\r':
100 esc_state->state = '\r';
101 break;
102 default:
103 esc_state->state = '\0';
104 }
105 }
106
Andrew Jefferya72711a2023-04-18 18:19:41 +0930107 rc = write_buf_to_fd(client->console_sd, out_buf,
108 len - (out_buf - buf));
William A. Kennington IIIff569832019-07-24 23:07:15 -0700109 return rc < 0 ? PROCESS_ERR : PROCESS_OK;
110}
Jeremy Kerr2bd05182016-03-10 16:59:43 +0800111
Andrew Jefferya72711a2023-04-18 18:19:41 +0930112static enum process_rc process_str_tty(struct console_client *client,
113 const uint8_t *buf, size_t len)
William A. Kennington III15691c82019-07-24 23:11:18 -0700114{
115 struct str_esc_state *esc_state = &client->esc_state.str;
116 enum process_rc prc = PROCESS_OK;
117 size_t i;
118
119 for (i = 0; i < len; ++i) {
Andrew Jeffery2834c5b2023-04-19 12:47:09 +0930120 if (buf[i] == esc_state->str[esc_state->pos]) {
William A. Kennington III15691c82019-07-24 23:11:18 -0700121 esc_state->pos++;
Andrew Jeffery2834c5b2023-04-19 12:47:09 +0930122 } else {
William A. Kennington III15691c82019-07-24 23:11:18 -0700123 esc_state->pos = 0;
Andrew Jeffery2834c5b2023-04-19 12:47:09 +0930124 }
William A. Kennington III15691c82019-07-24 23:11:18 -0700125
126 if (esc_state->str[esc_state->pos] == '\0') {
William A. Kennington III8a154352019-07-24 23:01:47 -0700127 prc = PROCESS_ESC;
William A. Kennington III5b16dc82019-07-26 01:18:05 -0700128 ++i;
William A. Kennington III15691c82019-07-24 23:11:18 -0700129 break;
130 }
131 }
132
Andrew Jeffery2834c5b2023-04-19 12:47:09 +0930133 if (write_buf_to_fd(client->console_sd, buf, i) < 0) {
William A. Kennington III15691c82019-07-24 23:11:18 -0700134 return PROCESS_ERR;
Andrew Jeffery2834c5b2023-04-19 12:47:09 +0930135 }
William A. Kennington III15691c82019-07-24 23:11:18 -0700136 return prc;
137}
138
Jeremy Kerr2bd05182016-03-10 16:59:43 +0800139static enum process_rc process_tty(struct console_client *client)
140{
William A. Kennington IIIff569832019-07-24 23:07:15 -0700141 uint8_t buf[4096];
Jeremy Kerr2bd05182016-03-10 16:59:43 +0800142 ssize_t len;
Jeremy Kerr2bd05182016-03-10 16:59:43 +0800143
144 len = read(client->fd_in, buf, sizeof(buf));
Andrew Jeffery2834c5b2023-04-19 12:47:09 +0930145 if (len < 0) {
Jeremy Kerr2bd05182016-03-10 16:59:43 +0800146 return PROCESS_ERR;
Andrew Jeffery2834c5b2023-04-19 12:47:09 +0930147 }
148 if (len == 0) {
Jeremy Kerr2bd05182016-03-10 16:59:43 +0800149 return PROCESS_EXIT;
Andrew Jeffery2834c5b2023-04-19 12:47:09 +0930150 }
Jeremy Kerr2bd05182016-03-10 16:59:43 +0800151
Andrew Jefferya72711a2023-04-18 18:19:41 +0930152 switch (client->esc_type) {
William A. Kennington IIIff569832019-07-24 23:07:15 -0700153 case ESC_TYPE_SSH:
154 return process_ssh_tty(client, buf, len);
William A. Kennington III15691c82019-07-24 23:11:18 -0700155 case ESC_TYPE_STR:
156 return process_str_tty(client, buf, len);
William A. Kennington IIIff569832019-07-24 23:07:15 -0700157 default:
Jeremy Kerr2bd05182016-03-10 16:59:43 +0800158 return PROCESS_ERR;
William A. Kennington IIIff569832019-07-24 23:07:15 -0700159 }
Jeremy Kerr2bd05182016-03-10 16:59:43 +0800160}
161
Jeremy Kerr2bd05182016-03-10 16:59:43 +0800162static int process_console(struct console_client *client)
163{
164 uint8_t buf[4096];
Andrew Jeffery5c359cc2023-04-18 22:50:07 +0930165 ssize_t len;
166 int rc;
Jeremy Kerr2bd05182016-03-10 16:59:43 +0800167
168 len = read(client->console_sd, buf, sizeof(buf));
169 if (len < 0) {
170 warn("Can't read from server");
171 return PROCESS_ERR;
172 }
173 if (len == 0) {
174 fprintf(stderr, "Connection closed\n");
175 return PROCESS_EXIT;
176 }
177
178 rc = write_buf_to_fd(client->fd_out, buf, len);
179 return rc ? PROCESS_ERR : PROCESS_OK;
180}
181
182/*
183 * Setup our local file descriptors for IO: use stdin/stdout, and if we're on a
184 * TTY, put it in canonical mode
185 */
186static int client_tty_init(struct console_client *client)
187{
188 struct termios termios;
189 int rc;
190
191 client->fd_in = STDIN_FILENO;
192 client->fd_out = STDOUT_FILENO;
193 client->is_tty = isatty(client->fd_in);
194
Andrew Jeffery2834c5b2023-04-19 12:47:09 +0930195 if (!client->is_tty) {
Jeremy Kerr2bd05182016-03-10 16:59:43 +0800196 return 0;
Andrew Jeffery2834c5b2023-04-19 12:47:09 +0930197 }
Jeremy Kerr2bd05182016-03-10 16:59:43 +0800198
199 rc = tcgetattr(client->fd_in, &termios);
200 if (rc) {
201 warn("Can't get terminal attributes for console");
202 return -1;
203 }
204 memcpy(&client->orig_termios, &termios, sizeof(client->orig_termios));
205 cfmakeraw(&termios);
206
207 rc = tcsetattr(client->fd_in, TCSANOW, &termios);
208 if (rc) {
209 warn("Can't set terminal attributes for console");
210 return -1;
211 }
212
213 return 0;
214}
215
Ninad Palsule5ba20b52023-05-12 14:03:15 -0500216static int client_init(struct console_client *client, struct config *config,
217 const char *console_id)
Jeremy Kerr2bd05182016-03-10 16:59:43 +0800218{
Ninad Palsule5ba20b52023-05-12 14:03:15 -0500219 const char *resolved_id = NULL;
Jeremy Kerr2bd05182016-03-10 16:59:43 +0800220 struct sockaddr_un addr;
Andrew Jeffery6ed0e4e2020-02-21 14:35:39 +1030221 socket_path_t path;
Andrew Jeffery5e7c0782020-02-10 12:12:36 +1030222 ssize_t len;
Jeremy Kerr2bd05182016-03-10 16:59:43 +0800223 int rc;
224
225 client->console_sd = socket(AF_UNIX, SOCK_STREAM, 0);
226 if (!client->console_sd) {
227 warn("Can't open socket");
228 return -1;
229 }
230
Ninad Palsule5ba20b52023-05-12 14:03:15 -0500231 /* Get the console id */
232 resolved_id = config_resolve_console_id(config, console_id);
233
Jeremy Kerr2bd05182016-03-10 16:59:43 +0800234 memset(&addr, 0, sizeof(addr));
235 addr.sun_family = AF_UNIX;
Ninad Palsule5ba20b52023-05-12 14:03:15 -0500236 len = console_socket_path(addr.sun_path, resolved_id);
Andrew Jeffery5e7c0782020-02-10 12:12:36 +1030237 if (len < 0) {
Andrew Jeffery2834c5b2023-04-19 12:47:09 +0930238 if (errno) {
Andrew Jeffery5e7c0782020-02-10 12:12:36 +1030239 warn("Failed to configure socket: %s", strerror(errno));
Andrew Jeffery2834c5b2023-04-19 12:47:09 +0930240 } else {
Andrew Jeffery5e7c0782020-02-10 12:12:36 +1030241 warn("Socket name length exceeds buffer limits");
Andrew Jeffery2834c5b2023-04-19 12:47:09 +0930242 }
Andrew Jeffery5e7c0782020-02-10 12:12:36 +1030243 goto cleanup;
Jeremy Kerr2bd05182016-03-10 16:59:43 +0800244 }
245
Andrew Jeffery5e7c0782020-02-10 12:12:36 +1030246 rc = connect(client->console_sd, (struct sockaddr *)&addr,
Andrew Jefferya72711a2023-04-18 18:19:41 +0930247 sizeof(addr) - sizeof(addr.sun_path) + len);
Andrew Jeffery2834c5b2023-04-19 12:47:09 +0930248 if (!rc) {
Andrew Jeffery5e7c0782020-02-10 12:12:36 +1030249 return 0;
Andrew Jeffery2834c5b2023-04-19 12:47:09 +0930250 }
Andrew Jeffery5e7c0782020-02-10 12:12:36 +1030251
Andrew Jeffery6ed0e4e2020-02-21 14:35:39 +1030252 console_socket_path_readable(&addr, len, path);
253 warn("Can't connect to console server '@%s'", path);
Andrew Jeffery5e7c0782020-02-10 12:12:36 +1030254cleanup:
255 close(client->console_sd);
256 return -1;
Jeremy Kerr2bd05182016-03-10 16:59:43 +0800257}
258
259static void client_fini(struct console_client *client)
260{
Andrew Jeffery2834c5b2023-04-19 12:47:09 +0930261 if (client->is_tty) {
Jeremy Kerr2bd05182016-03-10 16:59:43 +0800262 tcsetattr(client->fd_in, TCSANOW, &client->orig_termios);
Andrew Jeffery2834c5b2023-04-19 12:47:09 +0930263 }
Jeremy Kerr2bd05182016-03-10 16:59:43 +0800264 close(client->console_sd);
265}
266
William A. Kennington III15691c82019-07-24 23:11:18 -0700267int main(int argc, char *argv[])
Jeremy Kerr2bd05182016-03-10 16:59:43 +0800268{
Andrew Jefferyb70f8712023-04-19 12:53:34 +0930269 struct console_client _client;
270 struct console_client *client;
Jeremy Kerr2bd05182016-03-10 16:59:43 +0800271 struct pollfd pollfds[2];
William A. Kennington III8a154352019-07-24 23:01:47 -0700272 enum process_rc prc = PROCESS_OK;
Andrew Jeffery71e7a242020-02-12 22:58:16 +1030273 const char *config_path = NULL;
274 struct config *config = NULL;
Andrew Jeffery9a8f30e2023-05-02 13:56:02 +0930275 const char *console_id = NULL;
Andrew Jeffery71e7a242020-02-12 22:58:16 +1030276 const uint8_t *esc = NULL;
Jeremy Kerr2bd05182016-03-10 16:59:43 +0800277 int rc;
278
279 client = &_client;
280 memset(client, 0, sizeof(*client));
William A. Kennington IIIff569832019-07-24 23:07:15 -0700281 client->esc_type = ESC_TYPE_SSH;
Jeremy Kerr2bd05182016-03-10 16:59:43 +0800282
William A. Kennington III15691c82019-07-24 23:11:18 -0700283 for (;;) {
Andrew Jeffery71e7a242020-02-12 22:58:16 +1030284 rc = getopt(argc, argv, "c:e:i:");
Andrew Jeffery2834c5b2023-04-19 12:47:09 +0930285 if (rc == -1) {
William A. Kennington III15691c82019-07-24 23:11:18 -0700286 break;
Andrew Jeffery2834c5b2023-04-19 12:47:09 +0930287 }
William A. Kennington III15691c82019-07-24 23:11:18 -0700288
289 switch (rc) {
Andrew Jeffery71e7a242020-02-12 22:58:16 +1030290 case 'c':
291 if (optarg[0] == '\0') {
292 fprintf(stderr, "Config str cannot be empty\n");
293 return EXIT_FAILURE;
294 }
295 config_path = optarg;
296 break;
William A. Kennington III15691c82019-07-24 23:11:18 -0700297 case 'e':
298 if (optarg[0] == '\0') {
299 fprintf(stderr, "Escape str cannot be empty\n");
300 return EXIT_FAILURE;
301 }
Andrew Jefferya72711a2023-04-18 18:19:41 +0930302 esc = (const uint8_t *)optarg;
William A. Kennington III15691c82019-07-24 23:11:18 -0700303 break;
Andrew Jefferyddf2ab72020-02-10 12:36:09 +1030304 case 'i':
305 if (optarg[0] == '\0') {
Andrew Jefferya72711a2023-04-18 18:19:41 +0930306 fprintf(stderr,
307 "Socket ID str cannot be empty\n");
Andrew Jefferyddf2ab72020-02-10 12:36:09 +1030308 return EXIT_FAILURE;
309 }
Andrew Jeffery9a8f30e2023-05-02 13:56:02 +0930310 console_id = optarg;
Andrew Jefferyddf2ab72020-02-10 12:36:09 +1030311 break;
William A. Kennington III15691c82019-07-24 23:11:18 -0700312 default:
313 fprintf(stderr,
314 "Usage: %s "
Andrew Jefferyddf2ab72020-02-10 12:36:09 +1030315 "[-e <escape sequence>]"
Andrew Jeffery9a8f30e2023-05-02 13:56:02 +0930316 "[-i <console ID>]"
Andrew Jeffery71e7a242020-02-12 22:58:16 +1030317 "[-c <config>]\n",
William A. Kennington III15691c82019-07-24 23:11:18 -0700318 argv[0]);
319 return EXIT_FAILURE;
320 }
321 }
322
Andrew Jeffery71e7a242020-02-12 22:58:16 +1030323 if (config_path) {
324 config = config_init(config_path);
325 if (!config) {
326 warnx("Can't read configuration, exiting.");
327 return EXIT_FAILURE;
328 }
329
Andrew Jeffery2834c5b2023-04-19 12:47:09 +0930330 if (!esc) {
Andrew Jefferya72711a2023-04-18 18:19:41 +0930331 esc = (const uint8_t *)config_get_value(
332 config, "escape-sequence");
Andrew Jeffery2834c5b2023-04-19 12:47:09 +0930333 }
Andrew Jeffery71e7a242020-02-12 22:58:16 +1030334 }
335
336 if (esc) {
337 client->esc_type = ESC_TYPE_STR;
338 client->esc_state.str.str = esc;
339 }
340
Ninad Palsule5ba20b52023-05-12 14:03:15 -0500341 rc = client_init(client, config, console_id);
Andrew Jeffery2834c5b2023-04-19 12:47:09 +0930342 if (rc) {
Andrew Jeffery71e7a242020-02-12 22:58:16 +1030343 goto out_config_fini;
Andrew Jeffery2834c5b2023-04-19 12:47:09 +0930344 }
Jeremy Kerr2bd05182016-03-10 16:59:43 +0800345
346 rc = client_tty_init(client);
Andrew Jeffery2834c5b2023-04-19 12:47:09 +0930347 if (rc) {
Andrew Jeffery71e7a242020-02-12 22:58:16 +1030348 goto out_client_fini;
Andrew Jeffery2834c5b2023-04-19 12:47:09 +0930349 }
Jeremy Kerr2bd05182016-03-10 16:59:43 +0800350
Jeremy Kerr2bd05182016-03-10 16:59:43 +0800351 for (;;) {
352 pollfds[0].fd = client->fd_in;
353 pollfds[0].events = POLLIN;
354 pollfds[1].fd = client->console_sd;
355 pollfds[1].events = POLLIN;
356
357 rc = poll(pollfds, 2, -1);
358 if (rc < 0) {
359 warn("Poll failure");
360 break;
361 }
362
Andrew Jeffery2834c5b2023-04-19 12:47:09 +0930363 if (pollfds[0].revents) {
Jeremy Kerr2bd05182016-03-10 16:59:43 +0800364 prc = process_tty(client);
Andrew Jeffery2834c5b2023-04-19 12:47:09 +0930365 }
Jeremy Kerr2bd05182016-03-10 16:59:43 +0800366
Andrew Jeffery2834c5b2023-04-19 12:47:09 +0930367 if (prc == PROCESS_OK && pollfds[1].revents) {
Jeremy Kerr2bd05182016-03-10 16:59:43 +0800368 prc = process_console(client);
Andrew Jeffery2834c5b2023-04-19 12:47:09 +0930369 }
Jeremy Kerr2bd05182016-03-10 16:59:43 +0800370
371 rc = (prc == PROCESS_ERR) ? -1 : 0;
Andrew Jeffery2834c5b2023-04-19 12:47:09 +0930372 if (prc != PROCESS_OK) {
Jeremy Kerr2bd05182016-03-10 16:59:43 +0800373 break;
Andrew Jeffery2834c5b2023-04-19 12:47:09 +0930374 }
Jeremy Kerr2bd05182016-03-10 16:59:43 +0800375 }
376
Andrew Jeffery71e7a242020-02-12 22:58:16 +1030377out_client_fini:
Jeremy Kerr2bd05182016-03-10 16:59:43 +0800378 client_fini(client);
Andrew Jeffery71e7a242020-02-12 22:58:16 +1030379
380out_config_fini:
Andrew Jeffery2834c5b2023-04-19 12:47:09 +0930381 if (config_path) {
Andrew Jeffery71e7a242020-02-12 22:58:16 +1030382 config_fini(config);
Andrew Jeffery2834c5b2023-04-19 12:47:09 +0930383 }
Andrew Jeffery71e7a242020-02-12 22:58:16 +1030384
Andrew Jeffery2834c5b2023-04-19 12:47:09 +0930385 if (prc == PROCESS_ESC) {
William A. Kennington III8a154352019-07-24 23:01:47 -0700386 return EXIT_ESCAPE;
Andrew Jeffery2834c5b2023-04-19 12:47:09 +0930387 }
Jeremy Kerr2bd05182016-03-10 16:59:43 +0800388 return rc ? EXIT_FAILURE : EXIT_SUCCESS;
389}