1 /***************************************************************************
2 * Copyright (C) 2005 by Dominic Rath *
3 * Dominic.Rath@gmx.de *
5 * Copyright (C) 2007-2010 Øyvind Harboe *
6 * oyvind.harboe@zylin.com *
8 * Copyright (C) 2008 by Spencer Oliver *
9 * spen@spen-soft.co.uk *
11 * This program is free software; you can redistribute it and/or modify *
12 * it under the terms of the GNU General Public License as published by *
13 * the Free Software Foundation; either version 2 of the License, or *
14 * (at your option) any later version. *
16 * This program is distributed in the hope that it will be useful, *
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
19 * GNU General Public License for more details. *
21 * You should have received a copy of the GNU General Public License *
22 * along with this program. If not, see <http://www.gnu.org/licenses/>. *
23 ***************************************************************************/
29 #include "telnet_server.h"
30 #include <target/target_request.h>
31 #include <helper/configuration.h>
32 #include <helper/list.h>
34 static char *telnet_port
;
36 static char *negotiate
=
37 "\xFF\xFB\x03" /* IAC WILL Suppress Go Ahead */
38 "\xFF\xFB\x01" /* IAC WILL Echo */
39 "\xFF\xFD\x03" /* IAC DO Suppress Go Ahead */
40 "\xFF\xFE\x01"; /* IAC DON'T Echo */
42 #define CTRL(c) (c - '@')
43 #define TELNET_HISTORY ".openocd_history"
45 /* The only way we can detect that the socket is closed is the first time
46 * we write to it, we will fail. Subsequent write operations will
49 static int telnet_write(struct connection
*connection
, const void *data
,
52 struct telnet_connection
*t_con
= connection
->priv
;
54 return ERROR_SERVER_REMOTE_CLOSED
;
56 if (connection_write(connection
, data
, len
) == len
)
59 return ERROR_SERVER_REMOTE_CLOSED
;
62 /* output an audible bell */
63 static int telnet_bell(struct connection
*connection
)
65 /* ("\a" does not work, at least on windows) */
66 return telnet_write(connection
, "\x07", 1);
69 static int telnet_prompt(struct connection
*connection
)
71 struct telnet_connection
*t_con
= connection
->priv
;
73 return telnet_write(connection
, t_con
->prompt
, strlen(t_con
->prompt
));
76 static int telnet_outputline(struct connection
*connection
, const char *line
)
80 /* process lines in buffer */
82 char *line_end
= strchr(line
, '\n');
89 telnet_write(connection
, line
, len
);
91 telnet_write(connection
, "\r\n", 2);
100 static int telnet_output(struct command_context
*cmd_ctx
, const char *line
)
102 struct connection
*connection
= cmd_ctx
->output_handler_priv
;
104 return telnet_outputline(connection
, line
);
107 static void telnet_log_callback(void *priv
, const char *file
, unsigned line
,
108 const char *function
, const char *string
)
110 struct connection
*connection
= priv
;
111 struct telnet_connection
*t_con
= connection
->priv
;
115 /* If the prompt is not visible, simply output the message. */
116 if (!t_con
->prompt_visible
) {
117 telnet_outputline(connection
, string
);
121 /* Clear the command line. */
122 tmp
= strlen(t_con
->prompt
) + t_con
->line_size
;
124 for (i
= 0; i
< tmp
; i
+= 16)
125 telnet_write(connection
, "\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b",
128 for (i
= 0; i
< tmp
; i
+= 16)
129 telnet_write(connection
, " ", MIN(tmp
- i
, 16));
131 for (i
= 0; i
< tmp
; i
+= 16)
132 telnet_write(connection
, "\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b",
135 telnet_outputline(connection
, string
);
137 /* Put the command line to its previous state. */
138 telnet_prompt(connection
);
139 telnet_write(connection
, t_con
->line
, t_con
->line_size
);
141 for (i
= t_con
->line_cursor
; i
< t_con
->line_size
; i
++)
142 telnet_write(connection
, "\b", 1);
145 static void telnet_load_history(struct telnet_connection
*t_con
)
148 char buffer
[TELNET_BUFFER_SIZE
];
151 char *history
= get_home_dir(TELNET_HISTORY
);
154 LOG_INFO("unable to get user home directory, telnet history will be disabled");
158 histfp
= fopen(history
, "rb");
162 while (fgets(buffer
, sizeof(buffer
), histfp
)) {
164 char *p
= strchr(buffer
, '\n');
167 if (buffer
[0] && i
< TELNET_LINE_HISTORY_SIZE
)
168 t_con
->history
[i
++] = strdup(buffer
);
171 t_con
->next_history
= i
;
172 t_con
->next_history
%= TELNET_LINE_HISTORY_SIZE
;
173 /* try to set to last entry - 1, that way we skip over any exit/shutdown cmds */
174 t_con
->current_history
= t_con
->next_history
> 0 ? i
- 1 : 0;
181 static void telnet_save_history(struct telnet_connection
*t_con
)
187 char *history
= get_home_dir(TELNET_HISTORY
);
190 LOG_INFO("unable to get user home directory, telnet history will be disabled");
194 histfp
= fopen(history
, "wb");
198 num
= TELNET_LINE_HISTORY_SIZE
;
199 i
= t_con
->current_history
+ 1;
200 i
%= TELNET_LINE_HISTORY_SIZE
;
202 while (!t_con
->history
[i
] && num
> 0) {
204 i
%= TELNET_LINE_HISTORY_SIZE
;
209 for (; num
> 0; num
--) {
210 fprintf(histfp
, "%s\n", t_con
->history
[i
]);
212 i
%= TELNET_LINE_HISTORY_SIZE
;
221 static int telnet_new_connection(struct connection
*connection
)
223 struct telnet_connection
*telnet_connection
;
224 struct telnet_service
*telnet_service
= connection
->service
->priv
;
227 telnet_connection
= malloc(sizeof(struct telnet_connection
));
229 if (!telnet_connection
) {
230 LOG_ERROR("Failed to allocate telnet connection.");
234 connection
->priv
= telnet_connection
;
236 /* initialize telnet connection information */
237 telnet_connection
->closed
= false;
238 telnet_connection
->line_size
= 0;
239 telnet_connection
->line_cursor
= 0;
240 telnet_connection
->prompt
= strdup("> ");
241 telnet_connection
->prompt_visible
= true;
242 telnet_connection
->state
= TELNET_STATE_DATA
;
244 /* output goes through telnet connection */
245 command_set_output_handler(connection
->cmd_ctx
, telnet_output
, connection
);
247 /* negotiate telnet options */
248 telnet_write(connection
, negotiate
, strlen(negotiate
));
250 /* print connection banner */
251 if (telnet_service
->banner
) {
252 telnet_write(connection
, telnet_service
->banner
, strlen(telnet_service
->banner
));
253 telnet_write(connection
, "\r\n", 2);
256 /* the prompt is always placed at the line beginning */
257 telnet_write(connection
, "\r", 1);
258 telnet_prompt(connection
);
260 /* initialize history */
261 for (i
= 0; i
< TELNET_LINE_HISTORY_SIZE
; i
++)
262 telnet_connection
->history
[i
] = NULL
;
263 telnet_connection
->next_history
= 0;
264 telnet_connection
->current_history
= 0;
265 telnet_load_history(telnet_connection
);
267 log_add_callback(telnet_log_callback
, connection
);
272 static void telnet_clear_line(struct connection
*connection
,
273 struct telnet_connection
*t_con
)
275 /* move to end of line */
276 if (t_con
->line_cursor
< t_con
->line_size
)
277 telnet_write(connection
,
278 t_con
->line
+ t_con
->line_cursor
,
279 t_con
->line_size
- t_con
->line_cursor
);
281 /* backspace, overwrite with space, backspace */
282 while (t_con
->line_size
> 0) {
283 telnet_write(connection
, "\b \b", 3);
286 t_con
->line_cursor
= 0;
289 static void telnet_history_go(struct connection
*connection
, int idx
)
291 struct telnet_connection
*t_con
= connection
->priv
;
293 if (t_con
->history
[idx
]) {
294 telnet_clear_line(connection
, t_con
);
295 t_con
->line_size
= strlen(t_con
->history
[idx
]);
296 t_con
->line_cursor
= t_con
->line_size
;
297 memcpy(t_con
->line
, t_con
->history
[idx
], t_con
->line_size
);
298 telnet_write(connection
, t_con
->line
, t_con
->line_size
);
299 t_con
->current_history
= idx
;
301 t_con
->state
= TELNET_STATE_DATA
;
304 static void telnet_history_up(struct connection
*connection
)
306 struct telnet_connection
*t_con
= connection
->priv
;
308 size_t last_history
= (t_con
->current_history
> 0) ?
309 t_con
->current_history
- 1 :
310 TELNET_LINE_HISTORY_SIZE
-1;
311 telnet_history_go(connection
, last_history
);
314 static void telnet_history_down(struct connection
*connection
)
316 struct telnet_connection
*t_con
= connection
->priv
;
319 next_history
= (t_con
->current_history
+ 1) % TELNET_LINE_HISTORY_SIZE
;
320 telnet_history_go(connection
, next_history
);
323 static void telnet_history_add(struct connection
*connection
)
325 struct telnet_connection
*t_con
= connection
->priv
;
327 /* save only non-blank not repeating lines in the history */
328 char *prev_line
= t_con
->history
[(t_con
->current_history
> 0) ?
329 t_con
->current_history
- 1 : TELNET_LINE_HISTORY_SIZE
-1];
331 if (*t_con
->line
&& (!prev_line
|| strcmp(t_con
->line
, prev_line
))) {
332 /* if the history slot is already taken, free it */
333 free(t_con
->history
[t_con
->next_history
]);
335 /* add line to history */
336 t_con
->history
[t_con
->next_history
] = strdup(t_con
->line
);
338 /* wrap history at TELNET_LINE_HISTORY_SIZE */
339 t_con
->next_history
= (t_con
->next_history
+ 1) % TELNET_LINE_HISTORY_SIZE
;
341 /* current history line starts at the new entry */
342 t_con
->current_history
= t_con
->next_history
;
344 free(t_con
->history
[t_con
->current_history
]);
345 t_con
->history
[t_con
->current_history
] = strdup("");
349 static int telnet_history_print(struct connection
*connection
)
351 struct telnet_connection
*tc
;
353 tc
= connection
->priv
;
355 for (size_t i
= 1; i
< TELNET_LINE_HISTORY_SIZE
; i
++) {
359 * The tc->next_history line contains empty string (unless NULL), thus
362 line
= tc
->history
[(tc
->next_history
+ i
) % TELNET_LINE_HISTORY_SIZE
];
365 telnet_write(connection
, line
, strlen(line
));
366 telnet_write(connection
, "\r\n\x00", 3);
373 /* The prompt is always placed at the line beginning. */
374 telnet_write(connection
, "\r", 1);
376 return telnet_prompt(connection
);
379 static void telnet_move_cursor(struct connection
*connection
, size_t pos
)
381 struct telnet_connection
*tc
= connection
->priv
;
384 if (pos
== tc
->line_cursor
) /* nothing to do */
387 if (pos
> tc
->line_size
) /* out of bounds */
390 if (pos
< tc
->line_cursor
) {
391 tmp
= tc
->line_cursor
- pos
;
393 for (size_t i
= 0; i
< tmp
; i
+= 16)
394 telnet_write(connection
, "\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b",
397 tmp
= pos
- tc
->line_cursor
;
399 for (size_t i
= 0; i
< tmp
; i
+= 16)
400 telnet_write(connection
, tc
->line
+ tc
->line_cursor
+ i
,
404 tc
->line_cursor
= pos
;
407 /* check buffer size leaving one spare character for string null termination */
408 static inline bool telnet_can_insert(struct connection
*connection
, size_t len
)
410 struct telnet_connection
*t_con
= connection
->priv
;
412 return t_con
->line_size
+ len
< TELNET_LINE_MAX_SIZE
;
415 /* write to telnet console, and update the telnet_connection members
416 * this function is capable of inserting in the middle of a line
417 * please ensure that data does not contain special characters (\n, \r, \t, \b ...)
419 * returns false when it fails to insert the requested data
421 static bool telnet_insert(struct connection
*connection
, const void *data
, size_t len
)
423 struct telnet_connection
*t_con
= connection
->priv
;
425 if (!telnet_can_insert(connection
, len
)) {
426 telnet_bell(connection
);
430 if (t_con
->line_cursor
< t_con
->line_size
) {
431 /* we have some content after the cursor */
432 memmove(t_con
->line
+ t_con
->line_cursor
+ len
,
433 t_con
->line
+ t_con
->line_cursor
,
434 t_con
->line_size
- t_con
->line_cursor
);
437 strncpy(t_con
->line
+ t_con
->line_cursor
, data
, len
);
439 telnet_write(connection
,
440 t_con
->line
+ t_con
->line_cursor
,
441 t_con
->line_size
+ len
- t_con
->line_cursor
);
443 t_con
->line_size
+= len
;
444 t_con
->line_cursor
+= len
;
446 for (size_t i
= t_con
->line_cursor
; i
< t_con
->line_size
; i
++)
447 telnet_write(connection
, "\b", 1);
452 static void telnet_delete_character(struct connection
*connection
)
454 struct telnet_connection
*t_con
= connection
->priv
;
456 if (t_con
->line_cursor
== 0)
459 if (t_con
->line_cursor
!= t_con
->line_size
) {
461 telnet_write(connection
, "\b", 1);
462 t_con
->line_cursor
--;
464 memmove(t_con
->line
+ t_con
->line_cursor
,
465 t_con
->line
+ t_con
->line_cursor
+ 1,
469 telnet_write(connection
,
470 t_con
->line
+ t_con
->line_cursor
,
473 telnet_write(connection
, " \b", 2);
474 for (i
= t_con
->line_cursor
; i
< t_con
->line_size
; i
++)
475 telnet_write(connection
, "\b", 1);
478 t_con
->line_cursor
--;
479 /* back space: move the 'printer' head one char
480 * back, overwrite with space, move back again */
481 telnet_write(connection
, "\b \b", 3);
485 static void telnet_remove_character(struct connection
*connection
)
487 struct telnet_connection
*t_con
= connection
->priv
;
489 if (t_con
->line_cursor
< t_con
->line_size
) {
492 /* remove char from line buffer */
493 memmove(t_con
->line
+ t_con
->line_cursor
,
494 t_con
->line
+ t_con
->line_cursor
+ 1,
495 t_con
->line_size
- t_con
->line_cursor
);
497 /* print remainder of buffer */
498 telnet_write(connection
, t_con
->line
+ t_con
->line_cursor
,
499 t_con
->line_size
- t_con
->line_cursor
);
500 /* overwrite last char with whitespace */
501 telnet_write(connection
, " \b", 2);
503 /* move back to cursor position*/
504 for (i
= t_con
->line_cursor
; i
< t_con
->line_size
; i
++)
505 telnet_write(connection
, "\b", 1);
509 static int telnet_exec_line(struct connection
*connection
)
511 struct telnet_connection
*t_con
= connection
->priv
;
512 struct command_context
*command_context
= connection
->cmd_ctx
;
515 telnet_write(connection
, "\r\n\x00", 3);
517 if (strcmp(t_con
->line
, "history") == 0) {
518 retval
= telnet_history_print(connection
);
520 if (retval
!= ERROR_OK
)
526 telnet_history_add(connection
);
528 t_con
->line_size
= 0;
530 /* to suppress prompt in log callback during command execution */
531 t_con
->prompt_visible
= false;
533 if (strcmp(t_con
->line
, "shutdown") == 0)
534 telnet_save_history(t_con
);
536 retval
= command_run_line(command_context
, t_con
->line
);
538 t_con
->line_cursor
= 0;
539 t_con
->prompt_visible
= true;
541 if (retval
== ERROR_COMMAND_CLOSE_CONNECTION
)
542 return ERROR_SERVER_REMOTE_CLOSED
;
544 /* the prompt is always placed at the line beginning */
545 telnet_write(connection
, "\r", 1);
547 retval
= telnet_prompt(connection
);
548 if (retval
== ERROR_SERVER_REMOTE_CLOSED
)
549 return ERROR_SERVER_REMOTE_CLOSED
;
554 static void telnet_cut_line_to_end(struct connection
*connection
)
556 struct telnet_connection
*t_con
= connection
->priv
;
558 /* FIXME: currently this function does not save to clipboard */
560 if (t_con
->line_cursor
< t_con
->line_size
) {
561 /* overwrite with space, until end of line, move back */
562 for (size_t i
= t_con
->line_cursor
; i
< t_con
->line_size
; i
++)
563 telnet_write(connection
, " ", 1);
564 for (size_t i
= t_con
->line_cursor
; i
< t_con
->line_size
; i
++)
565 telnet_write(connection
, "\b", 1);
566 t_con
->line
[t_con
->line_cursor
] = '\0';
567 t_con
->line_size
= t_con
->line_cursor
;
571 static void telnet_interrupt(struct connection
*connection
)
573 struct telnet_connection
*t_con
= connection
->priv
;
575 /* print '^C' at line end, and display a new command prompt */
576 telnet_move_cursor(connection
, t_con
->line_size
);
577 telnet_write(connection
, "^C\n\r", 4);
578 t_con
->line_cursor
= 0;
579 t_con
->line_size
= 0;
580 telnet_prompt(connection
);
583 static void telnet_auto_complete(struct connection
*connection
)
585 struct telnet_connection
*t_con
= connection
->priv
;
586 struct command_context
*command_context
= connection
->cmd_ctx
;
595 /* user command sequence, either at line beginning
596 * or we start over after these characters ';', '[', '{' */
597 size_t seq_start
= (t_con
->line_cursor
== 0) ? 0 : (t_con
->line_cursor
- 1);
598 while (seq_start
> 0) {
599 char c
= t_con
->line
[seq_start
];
600 if (c
== ';' || c
== '[' || c
== '{') {
608 /* user command position in the line, ignore leading spaces */
609 size_t usr_cmd_pos
= seq_start
;
610 while ((usr_cmd_pos
< t_con
->line_cursor
) && isspace(t_con
->line
[usr_cmd_pos
]))
613 /* user command length */
614 size_t usr_cmd_len
= t_con
->line_cursor
- usr_cmd_pos
;
616 /* optimize multiple spaces in the user command,
617 * because info commands does not tolerate multiple spaces */
618 size_t optimized_spaces
= 0;
619 char query
[usr_cmd_len
+ 1];
620 for (size_t i
= 0; i
< usr_cmd_len
; i
++) {
621 if ((i
< usr_cmd_len
- 1) && isspace(t_con
->line
[usr_cmd_pos
+ i
])
622 && isspace(t_con
->line
[usr_cmd_pos
+ i
+ 1])) {
627 query
[i
- optimized_spaces
] = t_con
->line
[usr_cmd_pos
+ i
];
630 usr_cmd_len
-= optimized_spaces
;
631 query
[usr_cmd_len
] = '\0';
633 /* filter commands */
634 char *query_cmd
= alloc_printf("_telnet_autocomplete_helper {%s*}", query
);
637 LOG_ERROR("Out of memory");
641 int retval
= Jim_EvalSource(command_context
->interp
, __FILE__
, __LINE__
, query_cmd
);
643 if (retval
!= JIM_OK
)
646 Jim_Obj
*list
= Jim_GetResult(command_context
->interp
);
647 Jim_IncrRefCount(list
);
649 /* common prefix length of the matched commands */
650 size_t common_len
= 0;
651 char *first_match
= NULL
; /* used to compute the common prefix length */
653 int len
= Jim_ListLength(command_context
->interp
, list
);
654 for (int i
= 0; i
< len
; i
++) {
655 Jim_Obj
*elem
= Jim_ListGetIndex(command_context
->interp
, list
, i
);
656 Jim_IncrRefCount(elem
);
658 char *name
= (char *)Jim_GetString(elem
, NULL
);
660 /* validate the command */
661 bool ignore_cmd
= false;
662 Jim_Cmd
*jim_cmd
= Jim_GetCommand(command_context
->interp
, elem
, JIM_NONE
);
665 /* Why we are here? Let's ignore it! */
667 } else if (jimcmd_is_oocd_command(jim_cmd
)) {
668 struct command
*cmd
= jimcmd_privdata(jim_cmd
);
670 if (cmd
&& !cmd
->handler
&& !cmd
->jim_handler
) {
671 /* Initial part of a multi-word command. Ignore it! */
673 } else if (cmd
&& cmd
->mode
== COMMAND_CONFIG
) {
674 /* Not executable after config phase. Ignore it! */
679 /* save the command in the prediction list */
681 struct cmd_match
*match
= calloc(1, sizeof(struct cmd_match
));
683 LOG_ERROR("Out of memory");
684 Jim_DecrRefCount(command_context
->interp
, elem
);
685 break; /* break the for loop */
688 if (list_empty(&matches
)) {
689 common_len
= strlen(name
);
692 size_t new_common_len
= usr_cmd_len
; /* save some loops */
694 while (new_common_len
< common_len
&& first_match
[new_common_len
] == name
[new_common_len
])
697 common_len
= new_common_len
;
701 list_add_tail(&match
->lh
, &matches
);
704 Jim_DecrRefCount(command_context
->interp
, elem
);
706 /* end of command filtering */
708 /* proceed with auto-completion */
709 if (list_empty(&matches
))
710 telnet_bell(connection
);
711 else if (common_len
== usr_cmd_len
&& list_is_singular(&matches
) && t_con
->line_cursor
== t_con
->line_size
)
712 telnet_insert(connection
, " ", 1);
713 else if (common_len
> usr_cmd_len
) {
714 int completion_size
= common_len
- usr_cmd_len
;
715 if (telnet_insert(connection
, first_match
+ usr_cmd_len
, completion_size
)) {
716 /* in bash this extra space is only added when the cursor in at the end of line */
717 if (list_is_singular(&matches
) && t_con
->line_cursor
== t_con
->line_size
)
718 telnet_insert(connection
, " ", 1);
720 } else if (!list_is_singular(&matches
)) {
721 telnet_write(connection
, "\n\r", 2);
723 struct cmd_match
*match
;
724 list_for_each_entry(match
, &matches
, lh
) {
725 telnet_write(connection
, match
->cmd
, strlen(match
->cmd
));
726 telnet_write(connection
, "\n\r", 2);
729 telnet_prompt(connection
);
730 telnet_write(connection
, t_con
->line
, t_con
->line_size
);
732 /* restore the terminal visible cursor location */
733 for (size_t i
= t_con
->line_cursor
; i
< t_con
->line_size
; i
++)
734 telnet_write(connection
, "\b", 1);
737 /* destroy the command_list */
738 struct cmd_match
*tmp
, *match
;
739 list_for_each_entry_safe(match
, tmp
, &matches
, lh
)
742 Jim_DecrRefCount(command_context
->interp
, list
);
745 static int telnet_input(struct connection
*connection
)
748 unsigned char buffer
[TELNET_BUFFER_SIZE
];
749 unsigned char *buf_p
;
750 struct telnet_connection
*t_con
= connection
->priv
;
752 bytes_read
= connection_read(connection
, buffer
, TELNET_BUFFER_SIZE
);
755 return ERROR_SERVER_REMOTE_CLOSED
;
756 else if (bytes_read
== -1) {
757 LOG_ERROR("error during read: %s", strerror(errno
));
758 return ERROR_SERVER_REMOTE_CLOSED
;
763 switch (t_con
->state
) {
764 case TELNET_STATE_DATA
:
765 if (*buf_p
== 0xff) {
766 t_con
->state
= TELNET_STATE_IAC
;
768 if (isprint(*buf_p
)) { /* printable character */
769 telnet_insert(connection
, buf_p
, 1);
770 } else { /* non-printable */
771 if (*buf_p
== 0x1b) { /* escape */
772 t_con
->state
= TELNET_STATE_ESCAPE
;
773 t_con
->last_escape
= '\x00';
774 } else if ((*buf_p
== 0xd) || (*buf_p
== 0xa)) { /* CR/LF */
777 /* skip over combinations with CR/LF and NUL characters */
778 if ((bytes_read
> 1) && ((*(buf_p
+ 1) == 0xa) ||
779 (*(buf_p
+ 1) == 0xd))) {
783 if ((bytes_read
> 1) && (*(buf_p
+ 1) == 0)) {
787 t_con
->line
[t_con
->line_size
] = 0;
789 retval
= telnet_exec_line(connection
);
790 if (retval
!= ERROR_OK
)
792 } else if ((*buf_p
== 0x7f) || (*buf_p
== 0x8)) { /* delete character */
793 telnet_delete_character(connection
);
794 } else if (*buf_p
== 0x15) { /* clear line */
795 telnet_clear_line(connection
, t_con
);
796 } else if (*buf_p
== CTRL('B')) { /* cursor left */
797 telnet_move_cursor(connection
, t_con
->line_cursor
- 1);
798 t_con
->state
= TELNET_STATE_DATA
;
799 } else if (*buf_p
== CTRL('C')) { /* interrupt */
800 telnet_interrupt(connection
);
801 } else if (*buf_p
== CTRL('F')) { /* cursor right */
802 telnet_move_cursor(connection
, t_con
->line_cursor
+ 1);
803 t_con
->state
= TELNET_STATE_DATA
;
804 } else if (*buf_p
== CTRL('P')) { /* cursor up */
805 telnet_history_up(connection
);
806 } else if (*buf_p
== CTRL('N')) { /* cursor down */
807 telnet_history_down(connection
);
808 } else if (*buf_p
== CTRL('A')) { /* move the cursor to the beginning of the line */
809 telnet_move_cursor(connection
, 0);
810 } else if (*buf_p
== CTRL('E')) { /* move the cursor to the end of the line */
811 telnet_move_cursor(connection
, t_con
->line_size
);
812 } else if (*buf_p
== CTRL('K')) { /* kill line to end */
813 telnet_cut_line_to_end(connection
);
814 } else if (*buf_p
== '\t') {
815 telnet_auto_complete(connection
);
817 LOG_DEBUG("unhandled nonprintable: %2.2x", *buf_p
);
822 case TELNET_STATE_IAC
:
825 t_con
->state
= TELNET_STATE_DONT
;
828 t_con
->state
= TELNET_STATE_DO
;
831 t_con
->state
= TELNET_STATE_WONT
;
834 t_con
->state
= TELNET_STATE_WILL
;
838 case TELNET_STATE_SB
:
840 case TELNET_STATE_SE
:
842 case TELNET_STATE_WILL
:
843 case TELNET_STATE_WONT
:
844 case TELNET_STATE_DO
:
845 case TELNET_STATE_DONT
:
846 t_con
->state
= TELNET_STATE_DATA
;
848 case TELNET_STATE_ESCAPE
:
849 if (t_con
->last_escape
== '[') {
850 if (*buf_p
== 'D') { /* cursor left */
851 telnet_move_cursor(connection
, t_con
->line_cursor
- 1);
852 t_con
->state
= TELNET_STATE_DATA
;
853 } else if (*buf_p
== 'C') { /* cursor right */
854 telnet_move_cursor(connection
, t_con
->line_cursor
+ 1);
855 t_con
->state
= TELNET_STATE_DATA
;
856 } else if (*buf_p
== 'A') { /* cursor up */
857 telnet_history_up(connection
);
858 } else if (*buf_p
== 'B') { /* cursor down */
859 telnet_history_down(connection
);
860 } else if (*buf_p
== 'F') { /* end key */
861 telnet_move_cursor(connection
, t_con
->line_size
);
862 t_con
->state
= TELNET_STATE_DATA
;
863 } else if (*buf_p
== 'H') { /* home key */
864 telnet_move_cursor(connection
, 0);
865 t_con
->state
= TELNET_STATE_DATA
;
866 } else if (*buf_p
== '3') {
867 t_con
->last_escape
= *buf_p
;
869 t_con
->state
= TELNET_STATE_DATA
;
871 } else if (t_con
->last_escape
== '3') {
872 /* Remove character */
874 telnet_remove_character(connection
);
875 t_con
->state
= TELNET_STATE_DATA
;
877 t_con
->state
= TELNET_STATE_DATA
;
878 } else if (t_con
->last_escape
== '\x00') {
880 t_con
->last_escape
= *buf_p
;
882 t_con
->state
= TELNET_STATE_DATA
;
884 LOG_ERROR("BUG: unexpected value in t_con->last_escape");
885 t_con
->state
= TELNET_STATE_DATA
;
890 LOG_ERROR("unknown telnet state");
901 static int telnet_connection_closed(struct connection
*connection
)
903 struct telnet_connection
*t_con
= connection
->priv
;
906 log_remove_callback(telnet_log_callback
, connection
);
909 t_con
->prompt
= NULL
;
911 /* save telnet history */
912 telnet_save_history(t_con
);
914 for (i
= 0; i
< TELNET_LINE_HISTORY_SIZE
; i
++) {
915 free(t_con
->history
[i
]);
916 t_con
->history
[i
] = NULL
;
919 /* if this connection registered a debug-message receiver delete it */
920 delete_debug_msg_receiver(connection
->cmd_ctx
, NULL
);
922 free(connection
->priv
);
923 connection
->priv
= NULL
;
928 int telnet_init(char *banner
)
930 if (strcmp(telnet_port
, "disabled") == 0) {
931 LOG_INFO("telnet server disabled");
935 struct telnet_service
*telnet_service
=
936 malloc(sizeof(struct telnet_service
));
938 if (!telnet_service
) {
939 LOG_ERROR("Failed to allocate telnet service.");
943 telnet_service
->banner
= banner
;
945 int ret
= add_service("telnet", telnet_port
, CONNECTION_LIMIT_UNLIMITED
,
946 telnet_new_connection
, telnet_input
, telnet_connection_closed
,
949 if (ret
!= ERROR_OK
) {
950 free(telnet_service
);
957 /* daemon configuration command telnet_port */
958 COMMAND_HANDLER(handle_telnet_port_command
)
960 return CALL_COMMAND_HANDLER(server_pipe_command
, &telnet_port
);
963 COMMAND_HANDLER(handle_exit_command
)
965 return ERROR_COMMAND_CLOSE_CONNECTION
;
968 static const struct command_registration telnet_command_handlers
[] = {
971 .handler
= handle_exit_command
,
972 .mode
= COMMAND_EXEC
,
974 .help
= "exit telnet session",
977 .name
= "telnet_port",
978 .handler
= handle_telnet_port_command
,
979 .mode
= COMMAND_CONFIG
,
980 .help
= "Specify port on which to listen "
981 "for incoming telnet connections. "
982 "Read help on 'gdb_port'.",
983 .usage
= "[port_num]",
985 COMMAND_REGISTRATION_DONE
988 int telnet_register_commands(struct command_context
*cmd_ctx
)
990 telnet_port
= strdup("4444");
991 return register_commands(cmd_ctx
, NULL
, telnet_command_handlers
);
994 void telnet_service_free(void)
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)