blob: 3dc31908b15b37b002249dc70ebdc6f473b82819 [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 {
Andrew Jefferya72711a2023-04-18 18:19:41 +093057 int console_sd;
58 int fd_in;
59 int fd_out;
60 bool is_tty;
61 struct termios orig_termios;
62 enum esc_type esc_type;
William A. Kennington IIIff569832019-07-24 23:07:15 -070063 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
Andrew Jefferya72711a2023-04-18 18:19:41 +093069static enum process_rc process_ssh_tty(struct console_client *client,
70 const uint8_t *buf, size_t len)
William A. Kennington IIIff569832019-07-24 23:07:15 -070071{
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) {
Andrew Jefferya72711a2023-04-18 18:19:41 +093077 switch (buf[i]) {
William A. Kennington IIIff569832019-07-24 23:07:15 -070078 case '.':
79 if (esc_state->state != '~') {
80 esc_state->state = '\0';
81 break;
82 }
William A. Kennington III8a154352019-07-24 23:01:47 -070083 return PROCESS_ESC;
William A. Kennington IIIff569832019-07-24 23:07:15 -070084 case '~':
85 if (esc_state->state != '\r') {
86 esc_state->state = '\0';
87 break;
88 }
89 esc_state->state = '~';
90 /* We need to print everything to skip the tilde */
Andrew Jefferya72711a2023-04-18 18:19:41 +093091 rc = write_buf_to_fd(client->console_sd, out_buf,
92 i - (out_buf - buf));
Andrew Jeffery2834c5b2023-04-19 12:47:09 +093093 if (rc < 0) {
William A. Kennington IIIff569832019-07-24 23:07:15 -070094 return PROCESS_ERR;
Andrew Jeffery2834c5b2023-04-19 12:47:09 +093095 }
Andrew Jefferya72711a2023-04-18 18:19:41 +093096 out_buf = &buf[i + 1];
William A. Kennington IIIff569832019-07-24 23:07:15 -070097 break;
98 case '\r':
99 esc_state->state = '\r';
100 break;
101 default:
102 esc_state->state = '\0';
103 }
104 }
105
Andrew Jefferya72711a2023-04-18 18:19:41 +0930106 rc = write_buf_to_fd(client->console_sd, out_buf,
107 len - (out_buf - buf));
William A. Kennington IIIff569832019-07-24 23:07:15 -0700108 return rc < 0 ? PROCESS_ERR : PROCESS_OK;
109}
Jeremy Kerr2bd05182016-03-10 16:59:43 +0800110
Andrew Jefferya72711a2023-04-18 18:19:41 +0930111static enum process_rc process_str_tty(struct console_client *client,
112 const uint8_t *buf, size_t len)
William A. Kennington III15691c82019-07-24 23:11:18 -0700113{
114 struct str_esc_state *esc_state = &client->esc_state.str;
115 enum process_rc prc = PROCESS_OK;
116 size_t i;
117
118 for (i = 0; i < len; ++i) {
Andrew Jeffery2834c5b2023-04-19 12:47:09 +0930119 if (buf[i] == esc_state->str[esc_state->pos]) {
William A. Kennington III15691c82019-07-24 23:11:18 -0700120 esc_state->pos++;
Andrew Jeffery2834c5b2023-04-19 12:47:09 +0930121 } else {
William A. Kennington III15691c82019-07-24 23:11:18 -0700122 esc_state->pos = 0;
Andrew Jeffery2834c5b2023-04-19 12:47:09 +0930123 }
William A. Kennington III15691c82019-07-24 23:11:18 -0700124
125 if (esc_state->str[esc_state->pos] == '\0') {
William A. Kennington III8a154352019-07-24 23:01:47 -0700126 prc = PROCESS_ESC;
William A. Kennington III5b16dc82019-07-26 01:18:05 -0700127 ++i;
William A. Kennington III15691c82019-07-24 23:11:18 -0700128 break;
129 }
130 }
131
Andrew Jeffery2834c5b2023-04-19 12:47:09 +0930132 if (write_buf_to_fd(client->console_sd, buf, i) < 0) {
William A. Kennington III15691c82019-07-24 23:11:18 -0700133 return PROCESS_ERR;
Andrew Jeffery2834c5b2023-04-19 12:47:09 +0930134 }
William A. Kennington III15691c82019-07-24 23:11:18 -0700135 return prc;
136}
137
Jeremy Kerr2bd05182016-03-10 16:59:43 +0800138static enum process_rc process_tty(struct console_client *client)
139{
William A. Kennington IIIff569832019-07-24 23:07:15 -0700140 uint8_t buf[4096];
Jeremy Kerr2bd05182016-03-10 16:59:43 +0800141 ssize_t len;
Jeremy Kerr2bd05182016-03-10 16:59:43 +0800142
143 len = read(client->fd_in, buf, sizeof(buf));
Andrew Jeffery2834c5b2023-04-19 12:47:09 +0930144 if (len < 0) {
Jeremy Kerr2bd05182016-03-10 16:59:43 +0800145 return PROCESS_ERR;
Andrew Jeffery2834c5b2023-04-19 12:47:09 +0930146 }
147 if (len == 0) {
Jeremy Kerr2bd05182016-03-10 16:59:43 +0800148 return PROCESS_EXIT;
Andrew Jeffery2834c5b2023-04-19 12:47:09 +0930149 }
Jeremy Kerr2bd05182016-03-10 16:59:43 +0800150
Andrew Jefferya72711a2023-04-18 18:19:41 +0930151 switch (client->esc_type) {
William A. Kennington IIIff569832019-07-24 23:07:15 -0700152 case ESC_TYPE_SSH:
153 return process_ssh_tty(client, buf, len);
William A. Kennington III15691c82019-07-24 23:11:18 -0700154 case ESC_TYPE_STR:
155 return process_str_tty(client, buf, len);
William A. Kennington IIIff569832019-07-24 23:07:15 -0700156 default:
Jeremy Kerr2bd05182016-03-10 16:59:43 +0800157 return PROCESS_ERR;
William A. Kennington IIIff569832019-07-24 23:07:15 -0700158 }
Jeremy Kerr2bd05182016-03-10 16:59:43 +0800159}
160
Jeremy Kerr2bd05182016-03-10 16:59:43 +0800161static int process_console(struct console_client *client)
162{
163 uint8_t buf[4096];
Andrew Jeffery5c359cc2023-04-18 22:50:07 +0930164 ssize_t len;
165 int rc;
Jeremy Kerr2bd05182016-03-10 16:59:43 +0800166
167 len = read(client->console_sd, buf, sizeof(buf));
168 if (len < 0) {
169 warn("Can't read from server");
170 return PROCESS_ERR;
171 }
172 if (len == 0) {
173 fprintf(stderr, "Connection closed\n");
174 return PROCESS_EXIT;
175 }
176
177 rc = write_buf_to_fd(client->fd_out, buf, len);
178 return rc ? PROCESS_ERR : PROCESS_OK;
179}
180
181/*
182 * Setup our local file descriptors for IO: use stdin/stdout, and if we're on a
183 * TTY, put it in canonical mode
184 */
185static int client_tty_init(struct console_client *client)
186{
187 struct termios termios;
188 int rc;
189
190 client->fd_in = STDIN_FILENO;
191 client->fd_out = STDOUT_FILENO;
192 client->is_tty = isatty(client->fd_in);
193
Andrew Jeffery2834c5b2023-04-19 12:47:09 +0930194 if (!client->is_tty) {
Jeremy Kerr2bd05182016-03-10 16:59:43 +0800195 return 0;
Andrew Jeffery2834c5b2023-04-19 12:47:09 +0930196 }
Jeremy Kerr2bd05182016-03-10 16:59:43 +0800197
198 rc = tcgetattr(client->fd_in, &termios);
199 if (rc) {
200 warn("Can't get terminal attributes for console");
201 return -1;
202 }
203 memcpy(&client->orig_termios, &termios, sizeof(client->orig_termios));
204 cfmakeraw(&termios);
205
206 rc = tcsetattr(client->fd_in, TCSANOW, &termios);
207 if (rc) {
208 warn("Can't set terminal attributes for console");
209 return -1;
210 }
211
212 return 0;
213}
214
Ninad Palsule5ba20b52023-05-12 14:03:15 -0500215static int client_init(struct console_client *client, struct config *config,
216 const char *console_id)
Jeremy Kerr2bd05182016-03-10 16:59:43 +0800217{
Ninad Palsule5ba20b52023-05-12 14:03:15 -0500218 const char *resolved_id = NULL;
Jeremy Kerr2bd05182016-03-10 16:59:43 +0800219 struct sockaddr_un addr;
Andrew Jeffery6ed0e4e2020-02-21 14:35:39 +1030220 socket_path_t path;
Andrew Jeffery5e7c0782020-02-10 12:12:36 +1030221 ssize_t len;
Jeremy Kerr2bd05182016-03-10 16:59:43 +0800222 int rc;
223
224 client->console_sd = socket(AF_UNIX, SOCK_STREAM, 0);
225 if (!client->console_sd) {
226 warn("Can't open socket");
227 return -1;
228 }
229
Ninad Palsule5ba20b52023-05-12 14:03:15 -0500230 /* Get the console id */
231 resolved_id = config_resolve_console_id(config, console_id);
232
Jeremy Kerr2bd05182016-03-10 16:59:43 +0800233 memset(&addr, 0, sizeof(addr));
234 addr.sun_family = AF_UNIX;
Ninad Palsule5ba20b52023-05-12 14:03:15 -0500235 len = console_socket_path(addr.sun_path, resolved_id);
Andrew Jeffery5e7c0782020-02-10 12:12:36 +1030236 if (len < 0) {
Andrew Jeffery2834c5b2023-04-19 12:47:09 +0930237 if (errno) {
Andrew Jeffery5e7c0782020-02-10 12:12:36 +1030238 warn("Failed to configure socket: %s", strerror(errno));
Andrew Jeffery2834c5b2023-04-19 12:47:09 +0930239 } else {
Andrew Jeffery5e7c0782020-02-10 12:12:36 +1030240 warn("Socket name length exceeds buffer limits");
Andrew Jeffery2834c5b2023-04-19 12:47:09 +0930241 }
Andrew Jeffery5e7c0782020-02-10 12:12:36 +1030242 goto cleanup;
Jeremy Kerr2bd05182016-03-10 16:59:43 +0800243 }
244
Andrew Jeffery5e7c0782020-02-10 12:12:36 +1030245 rc = connect(client->console_sd, (struct sockaddr *)&addr,
Andrew Jefferya72711a2023-04-18 18:19:41 +0930246 sizeof(addr) - sizeof(addr.sun_path) + len);
Andrew Jeffery2834c5b2023-04-19 12:47:09 +0930247 if (!rc) {
Andrew Jeffery5e7c0782020-02-10 12:12:36 +1030248 return 0;
Andrew Jeffery2834c5b2023-04-19 12:47:09 +0930249 }
Andrew Jeffery5e7c0782020-02-10 12:12:36 +1030250
Andrew Jeffery6ed0e4e2020-02-21 14:35:39 +1030251 console_socket_path_readable(&addr, len, path);
252 warn("Can't connect to console server '@%s'", path);
Andrew Jeffery5e7c0782020-02-10 12:12:36 +1030253cleanup:
254 close(client->console_sd);
255 return -1;
Jeremy Kerr2bd05182016-03-10 16:59:43 +0800256}
257
258static void client_fini(struct console_client *client)
259{
Andrew Jeffery2834c5b2023-04-19 12:47:09 +0930260 if (client->is_tty) {
Jeremy Kerr2bd05182016-03-10 16:59:43 +0800261 tcsetattr(client->fd_in, TCSANOW, &client->orig_termios);
Andrew Jeffery2834c5b2023-04-19 12:47:09 +0930262 }
Jeremy Kerr2bd05182016-03-10 16:59:43 +0800263 close(client->console_sd);
264}
265
William A. Kennington III15691c82019-07-24 23:11:18 -0700266int main(int argc, char *argv[])
Jeremy Kerr2bd05182016-03-10 16:59:43 +0800267{
Andrew Jefferyb70f8712023-04-19 12:53:34 +0930268 struct console_client _client;
269 struct console_client *client;
Jeremy Kerr2bd05182016-03-10 16:59:43 +0800270 struct pollfd pollfds[2];
William A. Kennington III8a154352019-07-24 23:01:47 -0700271 enum process_rc prc = PROCESS_OK;
Andrew Jeffery71e7a242020-02-12 22:58:16 +1030272 const char *config_path = NULL;
273 struct config *config = NULL;
Andrew Jeffery9a8f30e2023-05-02 13:56:02 +0930274 const char *console_id = NULL;
Andrew Jeffery71e7a242020-02-12 22:58:16 +1030275 const uint8_t *esc = NULL;
Jeremy Kerr2bd05182016-03-10 16:59:43 +0800276 int rc;
277
278 client = &_client;
279 memset(client, 0, sizeof(*client));
William A. Kennington IIIff569832019-07-24 23:07:15 -0700280 client->esc_type = ESC_TYPE_SSH;
Jeremy Kerr2bd05182016-03-10 16:59:43 +0800281
William A. Kennington III15691c82019-07-24 23:11:18 -0700282 for (;;) {
Andrew Jeffery71e7a242020-02-12 22:58:16 +1030283 rc = getopt(argc, argv, "c:e:i:");
Andrew Jeffery2834c5b2023-04-19 12:47:09 +0930284 if (rc == -1) {
William A. Kennington III15691c82019-07-24 23:11:18 -0700285 break;
Andrew Jeffery2834c5b2023-04-19 12:47:09 +0930286 }
William A. Kennington III15691c82019-07-24 23:11:18 -0700287
288 switch (rc) {
Andrew Jeffery71e7a242020-02-12 22:58:16 +1030289 case 'c':
290 if (optarg[0] == '\0') {
291 fprintf(stderr, "Config str cannot be empty\n");
292 return EXIT_FAILURE;
293 }
294 config_path = optarg;
295 break;
William A. Kennington III15691c82019-07-24 23:11:18 -0700296 case 'e':
297 if (optarg[0] == '\0') {
298 fprintf(stderr, "Escape str cannot be empty\n");
299 return EXIT_FAILURE;
300 }
Andrew Jefferya72711a2023-04-18 18:19:41 +0930301 esc = (const uint8_t *)optarg;
William A. Kennington III15691c82019-07-24 23:11:18 -0700302 break;
Andrew Jefferyddf2ab72020-02-10 12:36:09 +1030303 case 'i':
304 if (optarg[0] == '\0') {
Andrew Jefferya72711a2023-04-18 18:19:41 +0930305 fprintf(stderr,
306 "Socket ID str cannot be empty\n");
Andrew Jefferyddf2ab72020-02-10 12:36:09 +1030307 return EXIT_FAILURE;
308 }
Andrew Jeffery9a8f30e2023-05-02 13:56:02 +0930309 console_id = optarg;
Andrew Jefferyddf2ab72020-02-10 12:36:09 +1030310 break;
William A. Kennington III15691c82019-07-24 23:11:18 -0700311 default:
312 fprintf(stderr,
313 "Usage: %s "
Andrew Jefferyddf2ab72020-02-10 12:36:09 +1030314 "[-e <escape sequence>]"
Andrew Jeffery9a8f30e2023-05-02 13:56:02 +0930315 "[-i <console ID>]"
Andrew Jeffery71e7a242020-02-12 22:58:16 +1030316 "[-c <config>]\n",
William A. Kennington III15691c82019-07-24 23:11:18 -0700317 argv[0]);
318 return EXIT_FAILURE;
319 }
320 }
321
Andrew Jeffery71e7a242020-02-12 22:58:16 +1030322 if (config_path) {
323 config = config_init(config_path);
324 if (!config) {
325 warnx("Can't read configuration, exiting.");
326 return EXIT_FAILURE;
327 }
328
Andrew Jeffery2834c5b2023-04-19 12:47:09 +0930329 if (!esc) {
Andrew Jefferya72711a2023-04-18 18:19:41 +0930330 esc = (const uint8_t *)config_get_value(
331 config, "escape-sequence");
Andrew Jeffery2834c5b2023-04-19 12:47:09 +0930332 }
Andrew Jeffery71e7a242020-02-12 22:58:16 +1030333 }
334
335 if (esc) {
336 client->esc_type = ESC_TYPE_STR;
337 client->esc_state.str.str = esc;
338 }
339
Ninad Palsule5ba20b52023-05-12 14:03:15 -0500340 rc = client_init(client, config, console_id);
Andrew Jeffery2834c5b2023-04-19 12:47:09 +0930341 if (rc) {
Andrew Jeffery71e7a242020-02-12 22:58:16 +1030342 goto out_config_fini;
Andrew Jeffery2834c5b2023-04-19 12:47:09 +0930343 }
Jeremy Kerr2bd05182016-03-10 16:59:43 +0800344
345 rc = client_tty_init(client);
Andrew Jeffery2834c5b2023-04-19 12:47:09 +0930346 if (rc) {
Andrew Jeffery71e7a242020-02-12 22:58:16 +1030347 goto out_client_fini;
Andrew Jeffery2834c5b2023-04-19 12:47:09 +0930348 }
Jeremy Kerr2bd05182016-03-10 16:59:43 +0800349
Jeremy Kerr2bd05182016-03-10 16:59:43 +0800350 for (;;) {
351 pollfds[0].fd = client->fd_in;
352 pollfds[0].events = POLLIN;
353 pollfds[1].fd = client->console_sd;
354 pollfds[1].events = POLLIN;
355
356 rc = poll(pollfds, 2, -1);
357 if (rc < 0) {
358 warn("Poll failure");
359 break;
360 }
361
Andrew Jeffery2834c5b2023-04-19 12:47:09 +0930362 if (pollfds[0].revents) {
Jeremy Kerr2bd05182016-03-10 16:59:43 +0800363 prc = process_tty(client);
Andrew Jeffery2834c5b2023-04-19 12:47:09 +0930364 }
Jeremy Kerr2bd05182016-03-10 16:59:43 +0800365
Andrew Jeffery2834c5b2023-04-19 12:47:09 +0930366 if (prc == PROCESS_OK && pollfds[1].revents) {
Jeremy Kerr2bd05182016-03-10 16:59:43 +0800367 prc = process_console(client);
Andrew Jeffery2834c5b2023-04-19 12:47:09 +0930368 }
Jeremy Kerr2bd05182016-03-10 16:59:43 +0800369
370 rc = (prc == PROCESS_ERR) ? -1 : 0;
Andrew Jeffery2834c5b2023-04-19 12:47:09 +0930371 if (prc != PROCESS_OK) {
Jeremy Kerr2bd05182016-03-10 16:59:43 +0800372 break;
Andrew Jeffery2834c5b2023-04-19 12:47:09 +0930373 }
Jeremy Kerr2bd05182016-03-10 16:59:43 +0800374 }
375
Andrew Jeffery71e7a242020-02-12 22:58:16 +1030376out_client_fini:
Jeremy Kerr2bd05182016-03-10 16:59:43 +0800377 client_fini(client);
Andrew Jeffery71e7a242020-02-12 22:58:16 +1030378
379out_config_fini:
Andrew Jeffery2834c5b2023-04-19 12:47:09 +0930380 if (config_path) {
Andrew Jeffery71e7a242020-02-12 22:58:16 +1030381 config_fini(config);
Andrew Jeffery2834c5b2023-04-19 12:47:09 +0930382 }
Andrew Jeffery71e7a242020-02-12 22:58:16 +1030383
Andrew Jeffery2834c5b2023-04-19 12:47:09 +0930384 if (prc == PROCESS_ESC) {
William A. Kennington III8a154352019-07-24 23:01:47 -0700385 return EXIT_ESCAPE;
Andrew Jeffery2834c5b2023-04-19 12:47:09 +0930386 }
Jeremy Kerr2bd05182016-03-10 16:59:43 +0800387 return rc ? EXIT_FAILURE : EXIT_SUCCESS;
388}