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