blob: b70fc95437a100ff85c680e179a49726a066b588 [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>
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
31enum process_rc {
32 PROCESS_OK = 0,
33 PROCESS_ERR,
34 PROCESS_EXIT,
35};
36
37struct console_client {
38 int console_sd;
39 int fd_in;
40 int fd_out;
41 bool is_tty;
42 struct termios orig_termios;
43 int esc_str_pos;
44 bool newline;
45};
46
47static const uint8_t esc_str[] = { '~', '.' };
48
49static enum process_rc process_tty(struct console_client *client)
50{
51 uint8_t e, buf[4096];
52 long i;
53 ssize_t len;
54 int rc;
55
56 len = read(client->fd_in, buf, sizeof(buf));
57 if (len < 0)
58 return PROCESS_ERR;
59 if (len == 0)
60 return PROCESS_EXIT;
61
62 /* check escape sequence status */
63 for (i = 0; i < len; i++) {
64 /* the escape string is only valid after a newline */
65 if (buf[i] == '\r') {
66 client->newline = true;
67 continue;
68 }
69
70 if (!client->newline)
71 continue;
72
73 e = esc_str[client->esc_str_pos];
74 if (buf[i] == e) {
75 client->esc_str_pos++;
76
77 /* have we hit the end of the escape string? */
78 if (client->esc_str_pos == ARRAY_SIZE(esc_str)) {
79
80 /* flush out any data before the escape */
81 if (i > client->esc_str_pos)
82 write_buf_to_fd(client->console_sd,
83 buf,
84 i - client->esc_str_pos);
85
86 return PROCESS_EXIT;
87 }
88 } else {
89 /* if we're partially the way through the escape
90 * string, flush out the bytes we'd skipped */
91 if (client->esc_str_pos)
92 write_buf_to_fd(client->console_sd,
93 esc_str, client->esc_str_pos);
94 client->esc_str_pos = 0;
95 client->newline = false;
96 }
97 }
98
99 rc = write_buf_to_fd(client->console_sd, buf,
100 len - client->esc_str_pos);
101 if (rc < 0)
102 return PROCESS_ERR;
103
104 return PROCESS_OK;
105}
106
107
108static int process_console(struct console_client *client)
109{
110 uint8_t buf[4096];
111 int len, rc;
112
113 len = read(client->console_sd, buf, sizeof(buf));
114 if (len < 0) {
115 warn("Can't read from server");
116 return PROCESS_ERR;
117 }
118 if (len == 0) {
119 fprintf(stderr, "Connection closed\n");
120 return PROCESS_EXIT;
121 }
122
123 rc = write_buf_to_fd(client->fd_out, buf, len);
124 return rc ? PROCESS_ERR : PROCESS_OK;
125}
126
127/*
128 * Setup our local file descriptors for IO: use stdin/stdout, and if we're on a
129 * TTY, put it in canonical mode
130 */
131static int client_tty_init(struct console_client *client)
132{
133 struct termios termios;
134 int rc;
135
136 client->fd_in = STDIN_FILENO;
137 client->fd_out = STDOUT_FILENO;
138 client->is_tty = isatty(client->fd_in);
139
140 if (!client->is_tty)
141 return 0;
142
143 rc = tcgetattr(client->fd_in, &termios);
144 if (rc) {
145 warn("Can't get terminal attributes for console");
146 return -1;
147 }
148 memcpy(&client->orig_termios, &termios, sizeof(client->orig_termios));
149 cfmakeraw(&termios);
150
151 rc = tcsetattr(client->fd_in, TCSANOW, &termios);
152 if (rc) {
153 warn("Can't set terminal attributes for console");
154 return -1;
155 }
156
157 return 0;
158}
159
160static int client_init(struct console_client *client)
161{
162 struct sockaddr_un addr;
163 int rc;
164
165 client->console_sd = socket(AF_UNIX, SOCK_STREAM, 0);
166 if (!client->console_sd) {
167 warn("Can't open socket");
168 return -1;
169 }
170
171 memset(&addr, 0, sizeof(addr));
172 addr.sun_family = AF_UNIX;
173 memcpy(addr.sun_path, console_socket_path, console_socket_path_len);
174
175 rc = connect(client->console_sd, (struct sockaddr *)&addr,
176 sizeof(addr));
177 if (rc) {
178 warn("Can't connect to console server");
179 close(client->console_sd);
180 return -1;
181 }
182
183 return 0;
184}
185
186static void client_fini(struct console_client *client)
187{
188 if (client->is_tty)
189 tcsetattr(client->fd_in, TCSANOW, &client->orig_termios);
190 close(client->console_sd);
191}
192
193int main(void)
194{
195 struct console_client _client, *client;
196 struct pollfd pollfds[2];
197 enum process_rc prc;
198 int rc;
199
200 client = &_client;
201 memset(client, 0, sizeof(*client));
202
203 rc = client_init(client);
204 if (rc)
205 return EXIT_FAILURE;
206
207 rc = client_tty_init(client);
208 if (rc)
209 goto out_fini;
210
Joel Stanley85965812016-03-17 20:38:12 +1030211 prc = PROCESS_OK;
Jeremy Kerr2bd05182016-03-10 16:59:43 +0800212 for (;;) {
213 pollfds[0].fd = client->fd_in;
214 pollfds[0].events = POLLIN;
215 pollfds[1].fd = client->console_sd;
216 pollfds[1].events = POLLIN;
217
218 rc = poll(pollfds, 2, -1);
219 if (rc < 0) {
220 warn("Poll failure");
221 break;
222 }
223
224 if (pollfds[0].revents)
225 prc = process_tty(client);
226
227 if (prc == PROCESS_OK && pollfds[1].revents)
228 prc = process_console(client);
229
230 rc = (prc == PROCESS_ERR) ? -1 : 0;
231 if (prc != PROCESS_OK)
232 break;
233 }
234
235out_fini:
236 client_fini(client);
237 return rc ? EXIT_FAILURE : EXIT_SUCCESS;
238}
239