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

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)