Asynchronous output information from e.g. a halt is now displayed again.
[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 telnet_outputline(connection, string);
118 }
119
120 int telnet_target_callback_event_handler(struct target_s *target, enum target_event event, void *priv)
121 {
122 switch (event)
123 {
124 case TARGET_EVENT_HALTED:
125 target_arch_state(target);
126 break;
127 default:
128 break;
129 }
130
131 return ERROR_OK;
132 }
133
134 int telnet_new_connection(connection_t *connection)
135 {
136 telnet_connection_t *telnet_connection = malloc(sizeof(telnet_connection_t));
137 telnet_service_t *telnet_service = connection->service->priv;
138 int i;
139
140 connection->priv = telnet_connection;
141
142 /* initialize telnet connection information */
143 telnet_connection->closed = 0;
144 telnet_connection->line_size = 0;
145 telnet_connection->line_cursor = 0;
146 telnet_connection->option_size = 0;
147 telnet_connection->prompt = strdup("> ");
148 telnet_connection->state = TELNET_STATE_DATA;
149
150 /* output goes through telnet connection */
151 command_set_output_handler(connection->cmd_ctx, telnet_output, connection);
152
153 /* negotiate telnet options */
154 telnet_write(connection, negotiate, strlen(negotiate));
155
156 /* print connection banner */
157 if (telnet_service->banner)
158 {
159 telnet_write(connection, telnet_service->banner, strlen(telnet_service->banner));
160 telnet_write(connection, "\r\n\0", 3);
161 }
162
163 telnet_prompt(connection);
164
165 /* initialize history */
166 for (i = 0; i < TELNET_LINE_HISTORY_SIZE; i++)
167 {
168 telnet_connection->history[i] = NULL;
169 }
170 telnet_connection->next_history = 0;
171 telnet_connection->current_history = 0;
172
173 target_register_event_callback(telnet_target_callback_event_handler, connection->cmd_ctx);
174
175 log_add_callback(telnet_log_callback, connection);
176
177
178
179 return ERROR_OK;
180 }
181
182 void telnet_clear_line(connection_t *connection, telnet_connection_t *t_con)
183 {
184 /* move to end of line */
185 if (t_con->line_cursor < t_con->line_size)
186 {
187 telnet_write(connection, t_con->line + t_con->line_cursor, t_con->line_size - t_con->line_cursor);
188 }
189
190 /* backspace, overwrite with space, backspace */
191 while (t_con->line_size > 0)
192 {
193 telnet_write(connection, "\b \b", 3);
194 t_con->line_size--;
195 }
196 t_con->line_cursor = 0;
197 }
198
199 int telnet_input(connection_t *connection)
200 {
201 int bytes_read;
202 char buffer[TELNET_BUFFER_SIZE];
203 char *buf_p;
204 telnet_connection_t *t_con = connection->priv;
205 command_context_t *command_context = connection->cmd_ctx;
206
207 bytes_read = read_socket(connection->fd, buffer, TELNET_BUFFER_SIZE);
208
209 if (bytes_read == 0)
210 return ERROR_SERVER_REMOTE_CLOSED;
211 else if (bytes_read == -1)
212 {
213 ERROR("error during read: %s", strerror(errno));
214 return ERROR_SERVER_REMOTE_CLOSED;
215 }
216
217 buf_p = buffer;
218 while (bytes_read)
219 {
220 switch (t_con->state)
221 {
222 case TELNET_STATE_DATA:
223 if (*buf_p == '\xff')
224 {
225 t_con->state = TELNET_STATE_IAC;
226 }
227 else
228 {
229 if (isprint(*buf_p)) /* printable character */
230 {
231 telnet_write(connection, buf_p, 1);
232 if (t_con->line_cursor == t_con->line_size)
233 {
234 t_con->line[t_con->line_size++] = *buf_p;
235 t_con->line_cursor++;
236 }
237 else
238 {
239 int i;
240 memmove(t_con->line + t_con->line_cursor + 1, t_con->line + t_con->line_cursor, t_con->line_size - t_con->line_cursor);
241 t_con->line[t_con->line_cursor++] = *buf_p;
242 t_con->line_size++;
243 telnet_write(connection, t_con->line + t_con->line_cursor, t_con->line_size - t_con->line_cursor);
244 for (i = t_con->line_cursor; i < t_con->line_size; i++)
245 {
246 telnet_write(connection, "\b", 1);
247 }
248 }
249 }
250 else /* non-printable */
251 {
252 if (*buf_p == 0x1b) /* escape */
253 {
254 t_con->state = TELNET_STATE_ESCAPE;
255 t_con->last_escape = '\x00';
256 }
257 else if ((*buf_p == 0xd) || (*buf_p == 0xa)) /* CR/LF */
258 {
259 int retval;
260
261 /* skip over combinations with CR/LF + NUL */
262 if (((*(buf_p + 1) == 0xa) || (*(buf_p + 1) == 0xd)) && (bytes_read > 1))
263 {
264 buf_p++;
265 bytes_read--;
266 }
267 if ((*(buf_p + 1) == 0) && (bytes_read > 1))
268 {
269 buf_p++;
270 bytes_read--;
271 }
272 t_con->line[t_con->line_size] = 0;
273
274 telnet_write(connection, "\r\n\x00", 3);
275
276 if (strcmp(t_con->line, "history") == 0)
277 {
278 int i;
279 for (i = 0; i < TELNET_LINE_HISTORY_SIZE; i++)
280 {
281 if (t_con->history[i])
282 {
283 telnet_write(connection, t_con->history[i], strlen(t_con->history[i]));
284 telnet_write(connection, "\r\n\x00", 3);
285 }
286 }
287 telnet_prompt(connection);
288 t_con->line_size = 0;
289 t_con->line_cursor = 0;
290 continue;
291 }
292
293 retval = command_run_line(command_context, t_con->line);
294
295 if (retval == ERROR_COMMAND_CLOSE_CONNECTION)
296 {
297 return ERROR_SERVER_REMOTE_CLOSED;
298 }
299
300 /* Save only non-blank lines in the history */
301 if (t_con->line_size > 0)
302 {
303 /* if the history slot is already taken, free it */
304 if (t_con->history[t_con->next_history])
305 {
306 free(t_con->history[t_con->next_history]);
307 }
308
309 /* add line to history */
310 t_con->history[t_con->next_history] = strdup(t_con->line);
311
312 /* wrap history at TELNET_LINE_HISTORY_SIZE */
313 t_con->next_history = (t_con->next_history + 1) % TELNET_LINE_HISTORY_SIZE;
314
315 /* current history line starts at the new entry */
316 t_con->current_history = t_con->next_history;
317
318 if (t_con->history[t_con->current_history])
319 {
320 free(t_con->history[t_con->current_history]);
321 }
322 t_con->history[t_con->current_history] = strdup("");
323 }
324
325 int t = telnet_prompt(connection);
326 if (t == ERROR_SERVER_REMOTE_CLOSED)
327 return t;
328
329 t_con->line_size = 0;
330 t_con->line_cursor = 0;
331 }
332 else if ((*buf_p == 0x7f) || (*buf_p == 0x8)) /* delete character */
333 {
334 if (t_con->line_cursor > 0)
335 {
336 if (t_con->line_cursor != t_con->line_size)
337 {
338 int i;
339 telnet_write(connection, "\b", 1);
340 t_con->line_cursor--;
341 t_con->line_size--;
342 memmove(t_con->line + t_con->line_cursor, t_con->line + t_con->line_cursor + 1, t_con->line_size - t_con->line_cursor);
343
344 telnet_write(connection, t_con->line + t_con->line_cursor, t_con->line_size - t_con->line_cursor);
345 telnet_write(connection, " \b", 2);
346 for (i = t_con->line_cursor; i < t_con->line_size; i++)
347 {
348 telnet_write(connection, "\b", 1);
349 }
350 }
351 else
352 {
353 t_con->line_size--;
354 t_con->line_cursor--;
355 /* back space: move the 'printer' head one char back, overwrite with space, move back again */
356 telnet_write(connection, "\b \b", 3);
357 }
358 }
359 }
360 else if (*buf_p == 0x15) /* clear line */
361 {
362 telnet_clear_line(connection, t_con);
363 }
364 else if (*buf_p == CTRL('B')) /* cursor left */
365 {
366 if (t_con->line_cursor > 0)
367 {
368 telnet_write(connection, "\b", 1);
369 t_con->line_cursor--;
370 }
371 t_con->state = TELNET_STATE_DATA;
372 }
373 else if (*buf_p == CTRL('F')) /* cursor right */
374 {
375 if (t_con->line_cursor < t_con->line_size)
376 {
377 telnet_write(connection, t_con->line + t_con->line_cursor++, 1);
378 }
379 t_con->state = TELNET_STATE_DATA;
380 }
381 else
382 {
383 DEBUG("unhandled nonprintable: %2.2x", *buf_p);
384 }
385 }
386 }
387 break;
388 case TELNET_STATE_IAC:
389 switch (*buf_p)
390 {
391 case '\xfe':
392 t_con->state = TELNET_STATE_DONT;
393 break;
394 case '\xfd':
395 t_con->state = TELNET_STATE_DO;
396 break;
397 case '\xfc':
398 t_con->state = TELNET_STATE_WONT;
399 break;
400 case '\xfb':
401 t_con->state = TELNET_STATE_WILL;
402 break;
403 }
404 break;
405 case TELNET_STATE_SB:
406 break;
407 case TELNET_STATE_SE:
408 break;
409 case TELNET_STATE_WILL:
410 case TELNET_STATE_WONT:
411 case TELNET_STATE_DO:
412 case TELNET_STATE_DONT:
413 t_con->state = TELNET_STATE_DATA;
414 break;
415 case TELNET_STATE_ESCAPE:
416 if (t_con->last_escape == '[')
417 {
418 if (*buf_p == 'D') /* cursor left */
419 {
420 if (t_con->line_cursor > 0)
421 {
422 telnet_write(connection, "\b", 1);
423 t_con->line_cursor--;
424 }
425 t_con->state = TELNET_STATE_DATA;
426 }
427 else if (*buf_p == 'C') /* cursor right */
428 {
429 if (t_con->line_cursor < t_con->line_size)
430 {
431 telnet_write(connection, t_con->line + t_con->line_cursor++, 1);
432 }
433 t_con->state = TELNET_STATE_DATA;
434 }
435 else if (*buf_p == 'A') /* cursor up */
436 {
437 int last_history = (t_con->current_history > 0) ? t_con->current_history - 1 : TELNET_LINE_HISTORY_SIZE-1;
438 if (t_con->history[last_history])
439 {
440 telnet_clear_line(connection, t_con);
441 t_con->line_size = strlen(t_con->history[last_history]);
442 t_con->line_cursor = t_con->line_size;
443 memcpy(t_con->line, t_con->history[last_history], t_con->line_size + 1);
444 telnet_write(connection, t_con->line, t_con->line_size);
445 t_con->current_history = last_history;
446 }
447 t_con->state = TELNET_STATE_DATA;
448 }
449 else if (*buf_p == 'B') /* cursor down */
450 {
451 int next_history = (t_con->current_history + 1) % TELNET_LINE_HISTORY_SIZE;
452 if (t_con->history[next_history])
453 {
454 telnet_clear_line(connection, t_con);
455 t_con->line_size = strlen(t_con->history[next_history]);
456 t_con->line_cursor = t_con->line_size;
457 memcpy(t_con->line, t_con->history[next_history], t_con->line_size + 1);
458 telnet_write(connection, t_con->line, t_con->line_size);
459 t_con->current_history = next_history;
460 }
461 t_con->state = TELNET_STATE_DATA;
462 }
463 else if (*buf_p == '3')
464 {
465 t_con->last_escape = *buf_p;
466 }
467 else
468 {
469 t_con->state = TELNET_STATE_DATA;
470 }
471 }
472 else if (t_con->last_escape == '3')
473 {
474 /* Remove character */
475 if (*buf_p == '~')
476 {
477 if (t_con->line_cursor < t_con->line_size)
478 {
479 int i;
480 t_con->line_size--;
481 /* remove char from line buffer */
482 memmove(t_con->line + t_con->line_cursor, t_con->line + t_con->line_cursor + 1, t_con->line_size - t_con->line_cursor);
483
484 /* print remainder of buffer */
485 telnet_write(connection, t_con->line + t_con->line_cursor, t_con->line_size - t_con->line_cursor);
486 /* overwrite last char with whitespace */
487 telnet_write(connection, " \b", 2);
488
489 /* move back to cursor position*/
490 for (i = t_con->line_cursor; i < t_con->line_size; i++)
491 {
492 telnet_write(connection, "\b", 1);
493 }
494 }
495
496 t_con->state = TELNET_STATE_DATA;
497 }
498 else
499 {
500 t_con->state = TELNET_STATE_DATA;
501 }
502 }
503 else if (t_con->last_escape == '\x00')
504 {
505 if (*buf_p == '[')
506 {
507 t_con->last_escape = *buf_p;
508 }
509 else
510 {
511 t_con->state = TELNET_STATE_DATA;
512 }
513 }
514 else
515 {
516 ERROR("BUG: unexpected value in t_con->last_escape");
517 t_con->state = TELNET_STATE_DATA;
518 }
519
520 break;
521 default:
522 ERROR("unknown telnet state");
523 exit(-1);
524 }
525
526 bytes_read--;
527 buf_p++;
528 }
529
530 return ERROR_OK;
531 }
532
533 int telnet_connection_closed(connection_t *connection)
534 {
535 telnet_connection_t *t_con = connection->priv;
536 int i;
537
538 log_remove_callback(telnet_log_callback, connection);
539
540 if (t_con->prompt)
541 {
542 free(t_con->prompt);
543 t_con->prompt = NULL;
544 }
545
546 for (i = 0; i < TELNET_LINE_HISTORY_SIZE; i++)
547 {
548 if (t_con->history[i])
549 {
550 free(t_con->history[i]);
551 t_con->history[i] = NULL;
552 }
553 }
554
555 /* if this connection registered a debug-message receiver delete it */
556 delete_debug_msg_receiver(connection->cmd_ctx, NULL);
557
558 if (connection->priv)
559 {
560 free(connection->priv);
561 connection->priv = NULL;
562 }
563 else
564 {
565 ERROR("BUG: connection->priv == NULL");
566 }
567
568 target_unregister_event_callback(telnet_target_callback_event_handler, connection->cmd_ctx);
569
570 return ERROR_OK;
571 }
572
573 int telnet_set_prompt(connection_t *connection, char *prompt)
574 {
575 telnet_connection_t *t_con = connection->priv;
576
577 t_con->prompt = strdup(prompt);
578
579 return ERROR_OK;
580 }
581
582 int telnet_init(char *banner)
583 {
584 telnet_service_t *telnet_service = malloc(sizeof(telnet_service_t));
585
586 if (telnet_port == 0)
587 {
588 WARNING("no telnet port specified, using default port 4444");
589 telnet_port = 4444;
590 }
591
592 telnet_service->banner = banner;
593
594 add_service("telnet", CONNECTION_TELNET, telnet_port, 1, telnet_new_connection, telnet_input, telnet_connection_closed, telnet_service);
595
596 return ERROR_OK;
597 }
598
599 int telnet_register_commands(command_context_t *command_context)
600 {
601 register_command(command_context, NULL, "exit", handle_exit_command,
602 COMMAND_EXEC, "exit telnet session");
603
604 register_command(command_context, NULL, "telnet_port", handle_telnet_port_command,
605 COMMAND_CONFIG, "");
606
607 return ERROR_OK;
608 }
609
610 /* daemon configuration command telnet_port */
611 int handle_telnet_port_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc)
612 {
613 if (argc == 0)
614 return ERROR_OK;
615
616 telnet_port = strtoul(args[0], NULL, 0);
617
618 return ERROR_OK;
619 }
620
621 int handle_exit_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc)
622 {
623 return ERROR_COMMAND_CLOSE_CONNECTION;
624 }

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)