prettier async output
[openocd.git] / src / server / telnet_server.c
1 /***************************************************************************
2 * Copyright (C) 2005 by Dominic Rath *
3 * Dominic.Rath@gmx.de *
4 * *
5 * This program is free software; you can redistribute it and/or modify *
6 * it under the terms of the GNU General Public License as published by *
7 * the Free Software Foundation; either version 2 of the License, or *
8 * (at your option) any later version. *
9 * *
10 * This program is distributed in the hope that it will be useful, *
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
13 * GNU General Public License for more details. *
14 * *
15 * You should have received a copy of the GNU General Public License *
16 * along with this program; if not, write to the *
17 * Free Software Foundation, Inc., *
18 * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
19 ***************************************************************************/
20 #ifdef HAVE_CONFIG_H
21 #include "config.h"
22 #endif
23
24 #include "replacements.h"
25
26 #include "telnet_server.h"
27
28 #include "server.h"
29 #include "log.h"
30 #include "command.h"
31 #include "target.h"
32 #include "target_request.h"
33
34 #include <stdlib.h>
35 #include <unistd.h>
36 #include <errno.h>
37 #include <string.h>
38 #include <ctype.h>
39
40 static unsigned short telnet_port = 0;
41
42 int handle_exit_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc);
43 int handle_telnet_port_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc);
44
45 static char *negotiate =
46 "\xFF\xFB\x03" /* IAC WILL Suppress Go Ahead */
47 "\xFF\xFB\x01" /* IAC WILL Echo */
48 "\xFF\xFD\x03" /* IAC DO Suppress Go Ahead */
49 "\xFF\xFE\x01"; /* IAC DON'T Echo */
50
51 #define CTRL(c) (c - '@')
52
53 /* The only way we can detect that the socket is closed is the first time
54 * we write to it, we will fail. Subsequent write operations will
55 * succeed. Shudder!
56 */
57 int telnet_write(connection_t *connection, const void *data, int len)
58 {
59 telnet_connection_t *t_con = connection->priv;
60 if (t_con->closed)
61 return ERROR_SERVER_REMOTE_CLOSED;
62
63 if (write_socket(connection->fd, data, len) == len)
64 {
65 return ERROR_OK;
66 }
67 t_con->closed = 1;
68 return ERROR_SERVER_REMOTE_CLOSED;
69 }
70
71 int telnet_prompt(connection_t *connection)
72 {
73 telnet_connection_t *t_con = connection->priv;
74
75 return telnet_write(connection, t_con->prompt, strlen(t_con->prompt));
76 }
77
78 int telnet_outputline(connection_t *connection, const char *line)
79 {
80 int len;
81
82 /* process lines in buffer */
83 while (*line) {
84 char *line_end = strchr(line, '\n');
85
86 if (line_end)
87 len = line_end-line;
88 else
89 len = strlen(line);
90
91 telnet_write(connection, line, len);
92 if (line_end)
93 {
94 telnet_write(connection, "\r\n\0", 3);
95 line += len+1;
96 }
97 else
98 {
99 line += len;
100 }
101 }
102
103 return ERROR_OK;
104 }
105
106 int telnet_output(struct command_context_s *cmd_ctx, char* line)
107 {
108 connection_t *connection = cmd_ctx->output_handler_priv;
109
110 return telnet_outputline(connection, line);
111 }
112
113 void telnet_log_callback(void *priv, const char *file, int line,
114 const char *function, const char *string)
115 {
116 connection_t *connection = priv;
117
118 telnet_write(connection, "\b\b \b\b", strlen("\b\b \b\b"));
119
120 telnet_outputline(connection, string);
121
122 telnet_prompt(connection);
123 }
124
125 int telnet_target_callback_event_handler(struct target_s *target, enum target_event event, void *priv)
126 {
127 switch (event)
128 {
129 case TARGET_EVENT_HALTED:
130 target_arch_state(target);
131 break;
132 default:
133 break;
134 }
135
136 return ERROR_OK;
137 }
138
139 int telnet_new_connection(connection_t *connection)
140 {
141 telnet_connection_t *telnet_connection = malloc(sizeof(telnet_connection_t));
142 telnet_service_t *telnet_service = connection->service->priv;
143 int i;
144
145 connection->priv = telnet_connection;
146
147 /* initialize telnet connection information */
148 telnet_connection->closed = 0;
149 telnet_connection->line_size = 0;
150 telnet_connection->line_cursor = 0;
151 telnet_connection->option_size = 0;
152 telnet_connection->prompt = strdup("> ");
153 telnet_connection->state = TELNET_STATE_DATA;
154
155 /* output goes through telnet connection */
156 command_set_output_handler(connection->cmd_ctx, telnet_output, connection);
157
158 /* negotiate telnet options */
159 telnet_write(connection, negotiate, strlen(negotiate));
160
161 /* print connection banner */
162 if (telnet_service->banner)
163 {
164 telnet_write(connection, telnet_service->banner, strlen(telnet_service->banner));
165 telnet_write(connection, "\r\n\0", 3);
166 }
167
168 telnet_prompt(connection);
169
170 /* initialize history */
171 for (i = 0; i < TELNET_LINE_HISTORY_SIZE; i++)
172 {
173 telnet_connection->history[i] = NULL;
174 }
175 telnet_connection->next_history = 0;
176 telnet_connection->current_history = 0;
177
178 target_register_event_callback(telnet_target_callback_event_handler, connection->cmd_ctx);
179
180 log_add_callback(telnet_log_callback, connection);
181
182
183
184 return ERROR_OK;
185 }
186
187 void telnet_clear_line(connection_t *connection, telnet_connection_t *t_con)
188 {
189 /* move to end of line */
190 if (t_con->line_cursor < t_con->line_size)
191 {
192 telnet_write(connection, t_con->line + t_con->line_cursor, t_con->line_size - t_con->line_cursor);
193 }
194
195 /* backspace, overwrite with space, backspace */
196 while (t_con->line_size > 0)
197 {
198 telnet_write(connection, "\b \b", 3);
199 t_con->line_size--;
200 }
201 t_con->line_cursor = 0;
202 }
203
204 int telnet_input(connection_t *connection)
205 {
206 int bytes_read;
207 char buffer[TELNET_BUFFER_SIZE];
208 char *buf_p;
209 telnet_connection_t *t_con = connection->priv;
210 command_context_t *command_context = connection->cmd_ctx;
211
212 bytes_read = read_socket(connection->fd, buffer, TELNET_BUFFER_SIZE);
213
214 if (bytes_read == 0)
215 return ERROR_SERVER_REMOTE_CLOSED;
216 else if (bytes_read == -1)
217 {
218 ERROR("error during read: %s", strerror(errno));
219 return ERROR_SERVER_REMOTE_CLOSED;
220 }
221
222 buf_p = buffer;
223 while (bytes_read)
224 {
225 switch (t_con->state)
226 {
227 case TELNET_STATE_DATA:
228 if (*buf_p == '\xff')
229 {
230 t_con->state = TELNET_STATE_IAC;
231 }
232 else
233 {
234 if (isprint(*buf_p)) /* printable character */
235 {
236 telnet_write(connection, buf_p, 1);
237 if (t_con->line_cursor == t_con->line_size)
238 {
239 t_con->line[t_con->line_size++] = *buf_p;
240 t_con->line_cursor++;
241 }
242 else
243 {
244 int i;
245 memmove(t_con->line + t_con->line_cursor + 1, t_con->line + t_con->line_cursor, t_con->line_size - t_con->line_cursor);
246 t_con->line[t_con->line_cursor++] = *buf_p;
247 t_con->line_size++;
248 telnet_write(connection, t_con->line + t_con->line_cursor, t_con->line_size - t_con->line_cursor);
249 for (i = t_con->line_cursor; i < t_con->line_size; i++)
250 {
251 telnet_write(connection, "\b", 1);
252 }
253 }
254 }
255 else /* non-printable */
256 {
257 if (*buf_p == 0x1b) /* escape */
258 {
259 t_con->state = TELNET_STATE_ESCAPE;
260 t_con->last_escape = '\x00';
261 }
262 else if ((*buf_p == 0xd) || (*buf_p == 0xa)) /* CR/LF */
263 {
264 int retval;
265
266 /* skip over combinations with CR/LF + NUL */
267 if (((*(buf_p + 1) == 0xa) || (*(buf_p + 1) == 0xd)) && (bytes_read > 1))
268 {
269 buf_p++;
270 bytes_read--;
271 }
272 if ((*(buf_p + 1) == 0) && (bytes_read > 1))
273 {
274 buf_p++;
275 bytes_read--;
276 }
277 t_con->line[t_con->line_size] = 0;
278
279 telnet_write(connection, "\r\n\x00", 3);
280
281 if (strcmp(t_con->line, "history") == 0)
282 {
283 int i;
284 for (i = 0; i < TELNET_LINE_HISTORY_SIZE; i++)
285 {
286 if (t_con->history[i])
287 {
288 telnet_write(connection, t_con->history[i], strlen(t_con->history[i]));
289 telnet_write(connection, "\r\n\x00", 3);
290 }
291 }
292 telnet_prompt(connection);
293 t_con->line_size = 0;
294 t_con->line_cursor = 0;
295 continue;
296 }
297
298 retval = command_run_line(command_context, t_con->line);
299
300 if (retval == ERROR_COMMAND_CLOSE_CONNECTION)
301 {
302 return ERROR_SERVER_REMOTE_CLOSED;
303 }
304
305 /* Save only non-blank lines in the history */
306 if (t_con->line_size > 0)
307 {
308 /* if the history slot is already taken, free it */
309 if (t_con->history[t_con->next_history])
310 {
311 free(t_con->history[t_con->next_history]);
312 }
313
314 /* add line to history */
315 t_con->history[t_con->next_history] = strdup(t_con->line);
316
317 /* wrap history at TELNET_LINE_HISTORY_SIZE */
318 t_con->next_history = (t_con->next_history + 1) % TELNET_LINE_HISTORY_SIZE;
319
320 /* current history line starts at the new entry */
321 t_con->current_history = t_con->next_history;
322
323 if (t_con->history[t_con->current_history])
324 {
325 free(t_con->history[t_con->current_history]);
326 }
327 t_con->history[t_con->current_history] = strdup("");
328 }
329
330 int t = telnet_prompt(connection);
331 if (t == ERROR_SERVER_REMOTE_CLOSED)
332 return t;
333
334 t_con->line_size = 0;
335 t_con->line_cursor = 0;
336 }
337 else if ((*buf_p == 0x7f) || (*buf_p == 0x8)) /* delete character */
338 {
339 if (t_con->line_cursor > 0)
340 {
341 if (t_con->line_cursor != t_con->line_size)
342 {
343 int i;
344 telnet_write(connection, "\b", 1);
345 t_con->line_cursor--;
346 t_con->line_size--;
347 memmove(t_con->line + t_con->line_cursor, t_con->line + t_con->line_cursor + 1, t_con->line_size - t_con->line_cursor);
348
349 telnet_write(connection, t_con->line + t_con->line_cursor, t_con->line_size - t_con->line_cursor);
350 telnet_write(connection, " \b", 2);
351 for (i = t_con->line_cursor; i < t_con->line_size; i++)
352 {
353 telnet_write(connection, "\b", 1);
354 }
355 }
356 else
357 {
358 t_con->line_size--;
359 t_con->line_cursor--;
360 /* back space: move the 'printer' head one char back, overwrite with space, move back again */
361 telnet_write(connection, "\b \b", 3);
362 }
363 }
364 }
365 else if (*buf_p == 0x15) /* clear line */
366 {
367 telnet_clear_line(connection, t_con);
368 }
369 else if (*buf_p == CTRL('B')) /* cursor left */
370 {
371 if (t_con->line_cursor > 0)
372 {
373 telnet_write(connection, "\b", 1);
374 t_con->line_cursor--;
375 }
376 t_con->state = TELNET_STATE_DATA;
377 }
378 else if (*buf_p == CTRL('F')) /* cursor right */
379 {
380 if (t_con->line_cursor < t_con->line_size)
381 {
382 telnet_write(connection, t_con->line + t_con->line_cursor++, 1);
383 }
384 t_con->state = TELNET_STATE_DATA;
385 }
386 else
387 {
388 DEBUG("unhandled nonprintable: %2.2x", *buf_p);
389 }
390 }
391 }
392 break;
393 case TELNET_STATE_IAC:
394 switch (*buf_p)
395 {
396 case '\xfe':
397 t_con->state = TELNET_STATE_DONT;
398 break;
399 case '\xfd':
400 t_con->state = TELNET_STATE_DO;
401 break;
402 case '\xfc':
403 t_con->state = TELNET_STATE_WONT;
404 break;
405 case '\xfb':
406 t_con->state = TELNET_STATE_WILL;
407 break;
408 }
409 break;
410 case TELNET_STATE_SB:
411 break;
412 case TELNET_STATE_SE:
413 break;
414 case TELNET_STATE_WILL:
415 case TELNET_STATE_WONT:
416 case TELNET_STATE_DO:
417 case TELNET_STATE_DONT:
418 t_con->state = TELNET_STATE_DATA;
419 break;
420 case TELNET_STATE_ESCAPE:
421 if (t_con->last_escape == '[')
422 {
423 if (*buf_p == 'D') /* cursor left */
424 {
425 if (t_con->line_cursor > 0)
426 {
427 telnet_write(connection, "\b", 1);
428 t_con->line_cursor--;
429 }
430 t_con->state = TELNET_STATE_DATA;
431 }
432 else if (*buf_p == 'C') /* cursor right */
433 {
434 if (t_con->line_cursor < t_con->line_size)
435 {
436 telnet_write(connection, t_con->line + t_con->line_cursor++, 1);
437 }
438 t_con->state = TELNET_STATE_DATA;
439 }
440 else if (*buf_p == 'A') /* cursor up */
441 {
442 int last_history = (t_con->current_history > 0) ? t_con->current_history - 1 : TELNET_LINE_HISTORY_SIZE-1;
443 if (t_con->history[last_history])
444 {
445 telnet_clear_line(connection, t_con);
446 t_con->line_size = strlen(t_con->history[last_history]);
447 t_con->line_cursor = t_con->line_size;
448 memcpy(t_con->line, t_con->history[last_history], t_con->line_size + 1);
449 telnet_write(connection, t_con->line, t_con->line_size);
450 t_con->current_history = last_history;
451 }
452 t_con->state = TELNET_STATE_DATA;
453 }
454 else if (*buf_p == 'B') /* cursor down */
455 {
456 int next_history = (t_con->current_history + 1) % TELNET_LINE_HISTORY_SIZE;
457 if (t_con->history[next_history])
458 {
459 telnet_clear_line(connection, t_con);
460 t_con->line_size = strlen(t_con->history[next_history]);
461 t_con->line_cursor = t_con->line_size;
462 memcpy(t_con->line, t_con->history[next_history], t_con->line_size + 1);
463 telnet_write(connection, t_con->line, t_con->line_size);
464 t_con->current_history = next_history;
465 }
466 t_con->state = TELNET_STATE_DATA;
467 }
468 else if (*buf_p == '3')
469 {
470 t_con->last_escape = *buf_p;
471 }
472 else
473 {
474 t_con->state = TELNET_STATE_DATA;
475 }
476 }
477 else if (t_con->last_escape == '3')
478 {
479 /* Remove character */
480 if (*buf_p == '~')
481 {
482 if (t_con->line_cursor < t_con->line_size)
483 {
484 int i;
485 t_con->line_size--;
486 /* remove char from line buffer */
487 memmove(t_con->line + t_con->line_cursor, t_con->line + t_con->line_cursor + 1, t_con->line_size - t_con->line_cursor);
488
489 /* print remainder of buffer */
490 telnet_write(connection, t_con->line + t_con->line_cursor, t_con->line_size - t_con->line_cursor);
491 /* overwrite last char with whitespace */
492 telnet_write(connection, " \b", 2);
493
494 /* move back to cursor position*/
495 for (i = t_con->line_cursor; i < t_con->line_size; i++)
496 {
497 telnet_write(connection, "\b", 1);
498 }
499 }
500
501 t_con->state = TELNET_STATE_DATA;
502 }
503 else
504 {
505 t_con->state = TELNET_STATE_DATA;
506 }
507 }
508 else if (t_con->last_escape == '\x00')
509 {
510 if (*buf_p == '[')
511 {
512 t_con->last_escape = *buf_p;
513 }
514 else
515 {
516 t_con->state = TELNET_STATE_DATA;
517 }
518 }
519 else
520 {
521 ERROR("BUG: unexpected value in t_con->last_escape");
522 t_con->state = TELNET_STATE_DATA;
523 }
524
525 break;
526 default:
527 ERROR("unknown telnet state");
528 exit(-1);
529 }
530
531 bytes_read--;
532 buf_p++;
533 }
534
535 return ERROR_OK;
536 }
537
538 int telnet_connection_closed(connection_t *connection)
539 {
540 telnet_connection_t *t_con = connection->priv;
541 int i;
542
543 log_remove_callback(telnet_log_callback, connection);
544
545 if (t_con->prompt)
546 {
547 free(t_con->prompt);
548 t_con->prompt = NULL;
549 }
550
551 for (i = 0; i < TELNET_LINE_HISTORY_SIZE; i++)
552 {
553 if (t_con->history[i])
554 {
555 free(t_con->history[i]);
556 t_con->history[i] = NULL;
557 }
558 }
559
560 /* if this connection registered a debug-message receiver delete it */
561 delete_debug_msg_receiver(connection->cmd_ctx, NULL);
562
563 if (connection->priv)
564 {
565 free(connection->priv);
566 connection->priv = NULL;
567 }
568 else
569 {
570 ERROR("BUG: connection->priv == NULL");
571 }
572
573 target_unregister_event_callback(telnet_target_callback_event_handler, connection->cmd_ctx);
574
575 return ERROR_OK;
576 }
577
578 int telnet_set_prompt(connection_t *connection, char *prompt)
579 {
580 telnet_connection_t *t_con = connection->priv;
581
582 t_con->prompt = strdup(prompt);
583
584 return ERROR_OK;
585 }
586
587 int telnet_init(char *banner)
588 {
589 telnet_service_t *telnet_service = malloc(sizeof(telnet_service_t));
590
591 if (telnet_port == 0)
592 {
593 WARNING("no telnet port specified, using default port 4444");
594 telnet_port = 4444;
595 }
596
597 telnet_service->banner = banner;
598
599 add_service("telnet", CONNECTION_TELNET, telnet_port, 1, telnet_new_connection, telnet_input, telnet_connection_closed, telnet_service);
600
601 return ERROR_OK;
602 }
603
604 int telnet_register_commands(command_context_t *command_context)
605 {
606 register_command(command_context, NULL, "exit", handle_exit_command,
607 COMMAND_EXEC, "exit telnet session");
608
609 register_command(command_context, NULL, "telnet_port", handle_telnet_port_command,
610 COMMAND_CONFIG, "");
611
612 return ERROR_OK;
613 }
614
615 /* daemon configuration command telnet_port */
616 int handle_telnet_port_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc)
617 {
618 if (argc == 0)
619 return ERROR_OK;
620
621 telnet_port = strtoul(args[0], NULL, 0);
622
623 return ERROR_OK;
624 }
625
626 int handle_exit_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc)
627 {
628 return ERROR_COMMAND_CLOSE_CONNECTION;
629 }

Linking to existing account procedure

If you already have an account and want to add another login method you MUST first sign in with your existing account and then change URL to read https://review.openocd.org/login/?link to get to this page again but this time it'll work for linking. Thank you.

SSH host keys fingerprints

1024 SHA256:YKx8b7u5ZWdcbp7/4AeXNaqElP49m6QrwfXaqQGJAOk gerrit-code-review@openocd.zylin.com (DSA)
384 SHA256:jHIbSQa4REvwCFG4cq5LBlBLxmxSqelQPem/EXIrxjk gerrit-code-review@openocd.org (ECDSA)
521 SHA256:UAOPYkU9Fjtcao0Ul/Rrlnj/OsQvt+pgdYSZ4jOYdgs gerrit-code-review@openocd.org (ECDSA)
256 SHA256:A13M5QlnozFOvTllybRZH6vm7iSt0XLxbA48yfc2yfY gerrit-code-review@openocd.org (ECDSA)
256 SHA256:spYMBqEYoAOtK7yZBrcwE8ZpYt6b68Cfh9yEVetvbXg gerrit-code-review@openocd.org (ED25519)
+--[ED25519 256]--+
|=..              |
|+o..   .         |
|*.o   . .        |
|+B . . .         |
|Bo. = o S        |
|Oo.+ + =         |
|oB=.* = . o      |
| =+=.+   + E     |
|. .=o   . o      |
+----[SHA256]-----+
2048 SHA256:0Onrb7/PHjpo6iVZ7xQX2riKN83FJ3KGU0TvI0TaFG4 gerrit-code-review@openocd.zylin.com (RSA)