target: Do not use LOG_USER() for error messages
[openocd.git] / src / server / telnet_server.c
blob938bc5b0624972e52cf207326fbac5d6a82a85ae
1 // SPDX-License-Identifier: GPL-2.0-or-later
3 /***************************************************************************
4 * Copyright (C) 2005 by Dominic Rath *
5 * Dominic.Rath@gmx.de *
6 * *
7 * Copyright (C) 2007-2010 Øyvind Harboe *
8 * oyvind.harboe@zylin.com *
9 * *
10 * Copyright (C) 2008 by Spencer Oliver *
11 * spen@spen-soft.co.uk *
12 ***************************************************************************/
14 #ifdef HAVE_CONFIG_H
15 #include "config.h"
16 #endif
18 #include "telnet_server.h"
19 #include <target/target_request.h>
20 #include <helper/configuration.h>
21 #include <helper/list.h>
23 static char *telnet_port;
25 static char *negotiate =
26 "\xFF\xFB\x03" /* IAC WILL Suppress Go Ahead */
27 "\xFF\xFB\x01" /* IAC WILL Echo */
28 "\xFF\xFD\x03" /* IAC DO Suppress Go Ahead */
29 "\xFF\xFE\x01"; /* IAC DON'T Echo */
31 #define CTRL(c) (c - '@')
32 #define TELNET_HISTORY ".openocd_history"
34 /* The only way we can detect that the socket is closed is the first time
35 * we write to it, we will fail. Subsequent write operations will
36 * succeed. Shudder!
38 static int telnet_write(struct connection *connection, const void *data,
39 int len)
41 struct telnet_connection *t_con = connection->priv;
42 if (t_con->closed)
43 return ERROR_SERVER_REMOTE_CLOSED;
45 if (connection_write(connection, data, len) == len)
46 return ERROR_OK;
47 t_con->closed = true;
48 return ERROR_SERVER_REMOTE_CLOSED;
51 /* output an audible bell */
52 static int telnet_bell(struct connection *connection)
54 /* ("\a" does not work, at least on windows) */
55 return telnet_write(connection, "\x07", 1);
58 static int telnet_prompt(struct connection *connection)
60 struct telnet_connection *t_con = connection->priv;
62 return telnet_write(connection, t_con->prompt, strlen(t_con->prompt));
65 static int telnet_outputline(struct connection *connection, const char *line)
67 int len;
69 /* process lines in buffer */
70 while (*line) {
71 char *line_end = strchr(line, '\n');
73 if (line_end)
74 len = line_end-line;
75 else
76 len = strlen(line);
78 telnet_write(connection, line, len);
79 if (line_end) {
80 telnet_write(connection, "\r\n", 2);
81 line += len + 1;
82 } else
83 line += len;
86 return ERROR_OK;
89 static int telnet_output(struct command_context *cmd_ctx, const char *line)
91 struct connection *connection = cmd_ctx->output_handler_priv;
93 return telnet_outputline(connection, line);
96 static void telnet_log_callback(void *priv, const char *file, unsigned line,
97 const char *function, const char *string)
99 struct connection *connection = priv;
100 struct telnet_connection *t_con = connection->priv;
101 size_t i;
102 size_t tmp;
104 /* If the prompt is not visible, simply output the message. */
105 if (!t_con->prompt_visible) {
106 telnet_outputline(connection, string);
107 return;
110 /* Clear the command line. */
111 tmp = strlen(t_con->prompt) + t_con->line_size;
113 for (i = 0; i < tmp; i += 16)
114 telnet_write(connection, "\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b",
115 MIN(tmp - i, 16));
117 for (i = 0; i < tmp; i += 16)
118 telnet_write(connection, " ", MIN(tmp - i, 16));
120 for (i = 0; i < tmp; i += 16)
121 telnet_write(connection, "\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b",
122 MIN(tmp - i, 16));
124 telnet_outputline(connection, string);
126 /* Put the command line to its previous state. */
127 telnet_prompt(connection);
128 telnet_write(connection, t_con->line, t_con->line_size);
130 for (i = t_con->line_cursor; i < t_con->line_size; i++)
131 telnet_write(connection, "\b", 1);
134 static void telnet_load_history(struct telnet_connection *t_con)
136 FILE *histfp;
137 char buffer[TELNET_BUFFER_SIZE];
138 int i = 0;
140 char *history = get_home_dir(TELNET_HISTORY);
142 if (!history) {
143 LOG_INFO("unable to get user home directory, telnet history will be disabled");
144 return;
147 histfp = fopen(history, "rb");
149 if (histfp) {
151 while (fgets(buffer, sizeof(buffer), histfp)) {
153 char *p = strchr(buffer, '\n');
154 if (p)
155 *p = '\0';
156 if (buffer[0] && i < TELNET_LINE_HISTORY_SIZE)
157 t_con->history[i++] = strdup(buffer);
160 t_con->next_history = i;
161 t_con->next_history %= TELNET_LINE_HISTORY_SIZE;
162 /* try to set to last entry - 1, that way we skip over any exit/shutdown cmds */
163 t_con->current_history = t_con->next_history > 0 ? i - 1 : 0;
164 fclose(histfp);
167 free(history);
170 static void telnet_save_history(struct telnet_connection *t_con)
172 FILE *histfp;
173 int i;
174 int num;
176 char *history = get_home_dir(TELNET_HISTORY);
178 if (!history) {
179 LOG_INFO("unable to get user home directory, telnet history will be disabled");
180 return;
183 histfp = fopen(history, "wb");
185 if (histfp) {
187 num = TELNET_LINE_HISTORY_SIZE;
188 i = t_con->current_history + 1;
189 i %= TELNET_LINE_HISTORY_SIZE;
191 while (!t_con->history[i] && num > 0) {
192 i++;
193 i %= TELNET_LINE_HISTORY_SIZE;
194 num--;
197 if (num > 0) {
198 for (; num > 0; num--) {
199 fprintf(histfp, "%s\n", t_con->history[i]);
200 i++;
201 i %= TELNET_LINE_HISTORY_SIZE;
204 fclose(histfp);
207 free(history);
210 static int telnet_new_connection(struct connection *connection)
212 struct telnet_connection *telnet_connection;
213 struct telnet_service *telnet_service = connection->service->priv;
215 telnet_connection = calloc(1, sizeof(struct telnet_connection));
217 if (!telnet_connection) {
218 LOG_ERROR("Failed to allocate telnet connection.");
219 return ERROR_FAIL;
222 connection->priv = telnet_connection;
224 /* initialize telnet connection information */
225 telnet_connection->prompt = strdup("> ");
226 telnet_connection->prompt_visible = true;
227 telnet_connection->state = TELNET_STATE_DATA;
229 /* output goes through telnet connection */
230 command_set_output_handler(connection->cmd_ctx, telnet_output, connection);
232 /* negotiate telnet options */
233 telnet_write(connection, negotiate, strlen(negotiate));
235 /* print connection banner */
236 if (telnet_service->banner) {
237 telnet_write(connection, telnet_service->banner, strlen(telnet_service->banner));
238 telnet_write(connection, "\r\n", 2);
241 /* the prompt is always placed at the line beginning */
242 telnet_write(connection, "\r", 1);
243 telnet_prompt(connection);
245 telnet_load_history(telnet_connection);
247 log_add_callback(telnet_log_callback, connection);
249 return ERROR_OK;
252 static void telnet_clear_line(struct connection *connection,
253 struct telnet_connection *t_con)
255 /* move to end of line */
256 if (t_con->line_cursor < t_con->line_size)
257 telnet_write(connection,
258 t_con->line + t_con->line_cursor,
259 t_con->line_size - t_con->line_cursor);
261 /* backspace, overwrite with space, backspace */
262 while (t_con->line_size > 0) {
263 telnet_write(connection, "\b \b", 3);
264 t_con->line_size--;
266 t_con->line_cursor = 0;
269 static void telnet_history_go(struct connection *connection, int idx)
271 struct telnet_connection *t_con = connection->priv;
273 if (t_con->history[idx]) {
274 telnet_clear_line(connection, t_con);
275 t_con->line_size = strlen(t_con->history[idx]);
276 t_con->line_cursor = t_con->line_size;
277 memcpy(t_con->line, t_con->history[idx], t_con->line_size);
278 telnet_write(connection, t_con->line, t_con->line_size);
279 t_con->current_history = idx;
281 t_con->state = TELNET_STATE_DATA;
284 static void telnet_history_up(struct connection *connection)
286 struct telnet_connection *t_con = connection->priv;
288 size_t last_history = (t_con->current_history > 0) ?
289 t_con->current_history - 1 :
290 TELNET_LINE_HISTORY_SIZE-1;
291 telnet_history_go(connection, last_history);
294 static void telnet_history_down(struct connection *connection)
296 struct telnet_connection *t_con = connection->priv;
297 size_t next_history;
299 next_history = (t_con->current_history + 1) % TELNET_LINE_HISTORY_SIZE;
300 telnet_history_go(connection, next_history);
303 static void telnet_history_add(struct connection *connection)
305 struct telnet_connection *t_con = connection->priv;
307 /* save only non-blank not repeating lines in the history */
308 char *prev_line = t_con->history[(t_con->current_history > 0) ?
309 t_con->current_history - 1 : TELNET_LINE_HISTORY_SIZE-1];
311 if (*t_con->line && (!prev_line || strcmp(t_con->line, prev_line))) {
312 /* if the history slot is already taken, free it */
313 free(t_con->history[t_con->next_history]);
315 /* add line to history */
316 t_con->history[t_con->next_history] = strdup(t_con->line);
318 /* wrap history at TELNET_LINE_HISTORY_SIZE */
319 t_con->next_history = (t_con->next_history + 1) % TELNET_LINE_HISTORY_SIZE;
321 /* current history line starts at the new entry */
322 t_con->current_history = t_con->next_history;
324 free(t_con->history[t_con->current_history]);
325 t_con->history[t_con->current_history] = strdup("");
329 static int telnet_history_print(struct connection *connection)
331 struct telnet_connection *tc;
333 tc = connection->priv;
335 for (size_t i = 1; i < TELNET_LINE_HISTORY_SIZE; i++) {
336 char *line;
339 * The tc->next_history line contains empty string (unless NULL), thus
340 * it is not printed.
342 line = tc->history[(tc->next_history + i) % TELNET_LINE_HISTORY_SIZE];
344 if (line) {
345 telnet_write(connection, line, strlen(line));
346 telnet_write(connection, "\r\n\x00", 3);
350 tc->line_size = 0;
351 tc->line_cursor = 0;
353 /* The prompt is always placed at the line beginning. */
354 telnet_write(connection, "\r", 1);
356 return telnet_prompt(connection);
359 static void telnet_move_cursor(struct connection *connection, size_t pos)
361 struct telnet_connection *tc = connection->priv;
362 size_t tmp;
364 if (pos == tc->line_cursor) /* nothing to do */
365 return;
367 if (pos > tc->line_size) /* out of bounds */
368 return;
370 if (pos < tc->line_cursor) {
371 tmp = tc->line_cursor - pos;
373 for (size_t i = 0; i < tmp; i += 16)
374 telnet_write(connection, "\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b",
375 MIN(tmp - i, 16));
376 } else {
377 tmp = pos - tc->line_cursor;
379 for (size_t i = 0; i < tmp; i += 16)
380 telnet_write(connection, tc->line + tc->line_cursor + i,
381 MIN(tmp - i, 16));
384 tc->line_cursor = pos;
387 /* check buffer size leaving one spare character for string null termination */
388 static inline bool telnet_can_insert(struct connection *connection, size_t len)
390 struct telnet_connection *t_con = connection->priv;
392 return t_con->line_size + len < TELNET_LINE_MAX_SIZE;
395 /* write to telnet console, and update the telnet_connection members
396 * this function is capable of inserting in the middle of a line
397 * please ensure that data does not contain special characters (\n, \r, \t, \b ...)
399 * returns false when it fails to insert the requested data
401 static bool telnet_insert(struct connection *connection, const void *data, size_t len)
403 struct telnet_connection *t_con = connection->priv;
405 if (!telnet_can_insert(connection, len)) {
406 telnet_bell(connection);
407 return false;
410 if (t_con->line_cursor < t_con->line_size) {
411 /* we have some content after the cursor */
412 memmove(t_con->line + t_con->line_cursor + len,
413 t_con->line + t_con->line_cursor,
414 t_con->line_size - t_con->line_cursor);
417 strncpy(t_con->line + t_con->line_cursor, data, len);
419 telnet_write(connection,
420 t_con->line + t_con->line_cursor,
421 t_con->line_size + len - t_con->line_cursor);
423 t_con->line_size += len;
424 t_con->line_cursor += len;
426 for (size_t i = t_con->line_cursor; i < t_con->line_size; i++)
427 telnet_write(connection, "\b", 1);
429 return true;
432 static void telnet_delete_character(struct connection *connection)
434 struct telnet_connection *t_con = connection->priv;
436 if (t_con->line_cursor == 0)
437 return;
439 if (t_con->line_cursor != t_con->line_size) {
440 size_t i;
441 telnet_write(connection, "\b", 1);
442 t_con->line_cursor--;
443 t_con->line_size--;
444 memmove(t_con->line + t_con->line_cursor,
445 t_con->line + t_con->line_cursor + 1,
446 t_con->line_size -
447 t_con->line_cursor);
449 telnet_write(connection,
450 t_con->line + t_con->line_cursor,
451 t_con->line_size -
452 t_con->line_cursor);
453 telnet_write(connection, " \b", 2);
454 for (i = t_con->line_cursor; i < t_con->line_size; i++)
455 telnet_write(connection, "\b", 1);
456 } else {
457 t_con->line_size--;
458 t_con->line_cursor--;
459 /* back space: move the 'printer' head one char
460 * back, overwrite with space, move back again */
461 telnet_write(connection, "\b \b", 3);
465 static void telnet_remove_character(struct connection *connection)
467 struct telnet_connection *t_con = connection->priv;
469 if (t_con->line_cursor < t_con->line_size) {
470 size_t i;
471 t_con->line_size--;
472 /* remove char from line buffer */
473 memmove(t_con->line + t_con->line_cursor,
474 t_con->line + t_con->line_cursor + 1,
475 t_con->line_size - t_con->line_cursor);
477 /* print remainder of buffer */
478 telnet_write(connection, t_con->line + t_con->line_cursor,
479 t_con->line_size - t_con->line_cursor);
480 /* overwrite last char with whitespace */
481 telnet_write(connection, " \b", 2);
483 /* move back to cursor position*/
484 for (i = t_con->line_cursor; i < t_con->line_size; i++)
485 telnet_write(connection, "\b", 1);
489 static int telnet_exec_line(struct connection *connection)
491 struct telnet_connection *t_con = connection->priv;
492 struct command_context *command_context = connection->cmd_ctx;
493 int retval;
495 telnet_write(connection, "\r\n\x00", 3);
497 if (strcmp(t_con->line, "history") == 0) {
498 retval = telnet_history_print(connection);
500 if (retval != ERROR_OK)
501 return retval;
503 return ERROR_OK;
506 telnet_history_add(connection);
508 t_con->line_size = 0;
510 /* to suppress prompt in log callback during command execution */
511 t_con->prompt_visible = false;
513 if (strcmp(t_con->line, "shutdown") == 0)
514 telnet_save_history(t_con);
516 retval = command_run_line(command_context, t_con->line);
518 t_con->line_cursor = 0;
519 t_con->prompt_visible = true;
521 if (retval == ERROR_COMMAND_CLOSE_CONNECTION)
522 return ERROR_SERVER_REMOTE_CLOSED;
524 /* the prompt is always placed at the line beginning */
525 telnet_write(connection, "\r", 1);
527 retval = telnet_prompt(connection);
528 if (retval == ERROR_SERVER_REMOTE_CLOSED)
529 return ERROR_SERVER_REMOTE_CLOSED;
531 return ERROR_OK;
534 static void telnet_cut_line_to_end(struct connection *connection)
536 struct telnet_connection *t_con = connection->priv;
538 /* FIXME: currently this function does not save to clipboard */
540 if (t_con->line_cursor < t_con->line_size) {
541 /* overwrite with space, until end of line, move back */
542 for (size_t i = t_con->line_cursor; i < t_con->line_size; i++)
543 telnet_write(connection, " ", 1);
544 for (size_t i = t_con->line_cursor; i < t_con->line_size; i++)
545 telnet_write(connection, "\b", 1);
546 t_con->line[t_con->line_cursor] = '\0';
547 t_con->line_size = t_con->line_cursor;
551 static void telnet_interrupt(struct connection *connection)
553 struct telnet_connection *t_con = connection->priv;
555 /* print '^C' at line end, and display a new command prompt */
556 telnet_move_cursor(connection, t_con->line_size);
557 telnet_write(connection, "^C\n\r", 4);
558 t_con->line_cursor = 0;
559 t_con->line_size = 0;
560 telnet_prompt(connection);
563 static void telnet_auto_complete(struct connection *connection)
565 struct telnet_connection *t_con = connection->priv;
566 struct command_context *command_context = connection->cmd_ctx;
568 struct cmd_match {
569 char *cmd;
570 struct list_head lh;
573 LIST_HEAD(matches);
575 /* - user command sequence, either at line beginning
576 * or we start over after these characters ';', '[', '{'
577 * - user variable sequence, start after the character '$'
578 * and do not contain white spaces */
579 bool is_variable_auto_completion = false;
580 bool have_spaces = false;
581 size_t seq_start = (t_con->line_cursor == 0) ? 0 : (t_con->line_cursor - 1);
582 while (1) {
583 char c = t_con->line[seq_start];
585 if (c == ';' || c == '[' || c == '{') {
586 seq_start++;
587 break;
588 } else if (c == ' ') {
589 have_spaces = true;
590 } else if (c == '$' && !have_spaces) {
591 is_variable_auto_completion = true;
592 seq_start++;
593 break;
596 if (seq_start == 0)
597 break;
599 seq_start--;
602 /* user command position in the line, ignore leading spaces */
603 size_t usr_cmd_pos = seq_start;
604 while ((usr_cmd_pos < t_con->line_cursor) && isspace(t_con->line[usr_cmd_pos]))
605 usr_cmd_pos++;
607 /* check user command length */
608 if (t_con->line_cursor < usr_cmd_pos) {
609 telnet_bell(connection);
610 return;
612 size_t usr_cmd_len = t_con->line_cursor - usr_cmd_pos;
614 /* optimize multiple spaces in the user command,
615 * because info commands does not tolerate multiple spaces */
616 size_t optimized_spaces = 0;
617 char query[usr_cmd_len + 1];
618 for (size_t i = 0; i < usr_cmd_len; i++) {
619 if ((i < usr_cmd_len - 1) && isspace(t_con->line[usr_cmd_pos + i])
620 && isspace(t_con->line[usr_cmd_pos + i + 1])) {
621 optimized_spaces++;
622 continue;
625 query[i - optimized_spaces] = t_con->line[usr_cmd_pos + i];
628 usr_cmd_len -= optimized_spaces;
629 query[usr_cmd_len] = '\0';
631 /* filter commands */
632 char *query_cmd;
634 if (is_variable_auto_completion)
635 query_cmd = alloc_printf("lsort [info vars {%s*}]", query);
636 else
637 query_cmd = alloc_printf("_telnet_autocomplete_helper {%s*}", query);
639 if (!query_cmd) {
640 LOG_ERROR("Out of memory");
641 return;
644 int retval = Jim_EvalSource(command_context->interp, __FILE__, __LINE__, query_cmd);
645 free(query_cmd);
646 if (retval != JIM_OK)
647 return;
649 Jim_Obj *list = Jim_GetResult(command_context->interp);
650 Jim_IncrRefCount(list);
652 /* common prefix length of the matched commands */
653 size_t common_len = 0;
654 char *first_match = NULL; /* used to compute the common prefix length */
656 int len = Jim_ListLength(command_context->interp, list);
657 for (int i = 0; i < len; i++) {
658 Jim_Obj *elem = Jim_ListGetIndex(command_context->interp, list, i);
659 Jim_IncrRefCount(elem);
661 char *name = (char *)Jim_GetString(elem, NULL);
663 /* validate the command */
664 bool ignore_cmd = false;
665 if (!is_variable_auto_completion) {
666 Jim_Cmd *jim_cmd = Jim_GetCommand(command_context->interp, elem, JIM_NONE);
668 if (!jim_cmd) {
669 /* Why we are here? Let's ignore it! */
670 ignore_cmd = true;
671 } else if (jimcmd_is_oocd_command(jim_cmd)) {
672 struct command *cmd = jimcmd_privdata(jim_cmd);
674 if (cmd && !cmd->handler && !cmd->jim_handler) {
675 /* Initial part of a multi-word command. Ignore it! */
676 ignore_cmd = true;
677 } else if (cmd && cmd->mode == COMMAND_CONFIG) {
678 /* Not executable after config phase. Ignore it! */
679 ignore_cmd = true;
684 /* save the command in the prediction list */
685 if (!ignore_cmd) {
686 struct cmd_match *match = calloc(1, sizeof(struct cmd_match));
687 if (!match) {
688 LOG_ERROR("Out of memory");
689 Jim_DecrRefCount(command_context->interp, elem);
690 break; /* break the for loop */
693 if (list_empty(&matches)) {
694 common_len = strlen(name);
695 first_match = name;
696 } else {
697 size_t new_common_len = usr_cmd_len; /* save some loops */
699 while (new_common_len < common_len && first_match[new_common_len] == name[new_common_len])
700 new_common_len++;
702 common_len = new_common_len;
705 match->cmd = name;
706 list_add_tail(&match->lh, &matches);
709 Jim_DecrRefCount(command_context->interp, elem);
711 /* end of command filtering */
713 /* proceed with auto-completion */
714 if (list_empty(&matches))
715 telnet_bell(connection);
716 else if (common_len == usr_cmd_len && list_is_singular(&matches) && t_con->line_cursor == t_con->line_size)
717 telnet_insert(connection, " ", 1);
718 else if (common_len > usr_cmd_len) {
719 int completion_size = common_len - usr_cmd_len;
720 if (telnet_insert(connection, first_match + usr_cmd_len, completion_size)) {
721 /* in bash this extra space is only added when the cursor in at the end of line */
722 if (list_is_singular(&matches) && t_con->line_cursor == t_con->line_size)
723 telnet_insert(connection, " ", 1);
725 } else if (!list_is_singular(&matches)) {
726 telnet_write(connection, "\n\r", 2);
728 struct cmd_match *match;
729 list_for_each_entry(match, &matches, lh) {
730 telnet_write(connection, match->cmd, strlen(match->cmd));
731 telnet_write(connection, "\n\r", 2);
734 telnet_prompt(connection);
735 telnet_write(connection, t_con->line, t_con->line_size);
737 /* restore the terminal visible cursor location */
738 for (size_t i = t_con->line_cursor; i < t_con->line_size; i++)
739 telnet_write(connection, "\b", 1);
742 /* destroy the command_list */
743 struct cmd_match *tmp, *match;
744 list_for_each_entry_safe(match, tmp, &matches, lh)
745 free(match);
747 Jim_DecrRefCount(command_context->interp, list);
750 static int telnet_input(struct connection *connection)
752 int bytes_read;
753 unsigned char buffer[TELNET_BUFFER_SIZE];
754 unsigned char *buf_p;
755 struct telnet_connection *t_con = connection->priv;
757 bytes_read = connection_read(connection, buffer, TELNET_BUFFER_SIZE);
759 if (bytes_read == 0)
760 return ERROR_SERVER_REMOTE_CLOSED;
761 else if (bytes_read == -1) {
762 LOG_ERROR("error during read: %s", strerror(errno));
763 return ERROR_SERVER_REMOTE_CLOSED;
766 buf_p = buffer;
767 while (bytes_read) {
768 switch (t_con->state) {
769 case TELNET_STATE_DATA:
770 if (*buf_p == 0xff) {
771 t_con->state = TELNET_STATE_IAC;
772 } else {
773 if (isprint(*buf_p)) { /* printable character */
774 telnet_insert(connection, buf_p, 1);
775 } else { /* non-printable */
776 if (*buf_p == 0x1b) { /* escape */
777 t_con->state = TELNET_STATE_ESCAPE;
778 t_con->last_escape = '\x00';
779 } else if ((*buf_p == 0xd) || (*buf_p == 0xa)) { /* CR/LF */
780 int retval;
782 /* skip over combinations with CR/LF and NUL characters */
783 if ((bytes_read > 1) && ((*(buf_p + 1) == 0xa) ||
784 (*(buf_p + 1) == 0xd))) {
785 buf_p++;
786 bytes_read--;
788 if ((bytes_read > 1) && (*(buf_p + 1) == 0)) {
789 buf_p++;
790 bytes_read--;
792 t_con->line[t_con->line_size] = 0;
794 retval = telnet_exec_line(connection);
795 if (retval != ERROR_OK)
796 return retval;
797 } else if ((*buf_p == 0x7f) || (*buf_p == 0x8)) { /* delete character */
798 telnet_delete_character(connection);
799 } else if (*buf_p == 0x15) { /* clear line */
800 telnet_clear_line(connection, t_con);
801 } else if (*buf_p == CTRL('B')) { /* cursor left */
802 telnet_move_cursor(connection, t_con->line_cursor - 1);
803 t_con->state = TELNET_STATE_DATA;
804 } else if (*buf_p == CTRL('C')) { /* interrupt */
805 telnet_interrupt(connection);
806 } else if (*buf_p == CTRL('F')) { /* cursor right */
807 telnet_move_cursor(connection, t_con->line_cursor + 1);
808 t_con->state = TELNET_STATE_DATA;
809 } else if (*buf_p == CTRL('P')) { /* cursor up */
810 telnet_history_up(connection);
811 } else if (*buf_p == CTRL('N')) { /* cursor down */
812 telnet_history_down(connection);
813 } else if (*buf_p == CTRL('A')) { /* move the cursor to the beginning of the line */
814 telnet_move_cursor(connection, 0);
815 } else if (*buf_p == CTRL('E')) { /* move the cursor to the end of the line */
816 telnet_move_cursor(connection, t_con->line_size);
817 } else if (*buf_p == CTRL('K')) { /* kill line to end */
818 telnet_cut_line_to_end(connection);
819 } else if (*buf_p == '\t') {
820 telnet_auto_complete(connection);
821 } else {
822 LOG_DEBUG("unhandled nonprintable: %2.2x", *buf_p);
826 break;
827 case TELNET_STATE_IAC:
828 switch (*buf_p) {
829 case 0xfe:
830 t_con->state = TELNET_STATE_DONT;
831 break;
832 case 0xfd:
833 t_con->state = TELNET_STATE_DO;
834 break;
835 case 0xfc:
836 t_con->state = TELNET_STATE_WONT;
837 break;
838 case 0xfb:
839 t_con->state = TELNET_STATE_WILL;
840 break;
842 break;
843 case TELNET_STATE_SB:
844 break;
845 case TELNET_STATE_SE:
846 break;
847 case TELNET_STATE_WILL:
848 case TELNET_STATE_WONT:
849 case TELNET_STATE_DO:
850 case TELNET_STATE_DONT:
851 t_con->state = TELNET_STATE_DATA;
852 break;
853 case TELNET_STATE_ESCAPE:
854 if (t_con->last_escape == '[') {
855 if (*buf_p == 'D') { /* cursor left */
856 telnet_move_cursor(connection, t_con->line_cursor - 1);
857 t_con->state = TELNET_STATE_DATA;
858 } else if (*buf_p == 'C') { /* cursor right */
859 telnet_move_cursor(connection, t_con->line_cursor + 1);
860 t_con->state = TELNET_STATE_DATA;
861 } else if (*buf_p == 'A') { /* cursor up */
862 telnet_history_up(connection);
863 } else if (*buf_p == 'B') { /* cursor down */
864 telnet_history_down(connection);
865 } else if (*buf_p == 'F') { /* end key */
866 telnet_move_cursor(connection, t_con->line_size);
867 t_con->state = TELNET_STATE_DATA;
868 } else if (*buf_p == 'H') { /* home key */
869 telnet_move_cursor(connection, 0);
870 t_con->state = TELNET_STATE_DATA;
871 } else if (*buf_p == '3') {
872 t_con->last_escape = *buf_p;
873 } else {
874 t_con->state = TELNET_STATE_DATA;
876 } else if (t_con->last_escape == '3') {
877 /* Remove character */
878 if (*buf_p == '~') {
879 telnet_remove_character(connection);
880 t_con->state = TELNET_STATE_DATA;
881 } else
882 t_con->state = TELNET_STATE_DATA;
883 } else if (t_con->last_escape == '\x00') {
884 if (*buf_p == '[')
885 t_con->last_escape = *buf_p;
886 else
887 t_con->state = TELNET_STATE_DATA;
888 } else {
889 LOG_ERROR("BUG: unexpected value in t_con->last_escape");
890 t_con->state = TELNET_STATE_DATA;
893 break;
894 default:
895 LOG_ERROR("unknown telnet state");
896 return ERROR_FAIL;
899 bytes_read--;
900 buf_p++;
903 return ERROR_OK;
906 static int telnet_connection_closed(struct connection *connection)
908 struct telnet_connection *t_con = connection->priv;
909 int i;
911 log_remove_callback(telnet_log_callback, connection);
913 free(t_con->prompt);
914 t_con->prompt = NULL;
916 /* save telnet history */
917 telnet_save_history(t_con);
919 for (i = 0; i < TELNET_LINE_HISTORY_SIZE; i++) {
920 free(t_con->history[i]);
921 t_con->history[i] = NULL;
924 /* if this connection registered a debug-message receiver delete it */
925 delete_debug_msg_receiver(connection->cmd_ctx, NULL);
927 free(connection->priv);
928 connection->priv = NULL;
930 return ERROR_OK;
933 static const struct service_driver telnet_service_driver = {
934 .name = "telnet",
935 .new_connection_during_keep_alive_handler = NULL,
936 .new_connection_handler = telnet_new_connection,
937 .input_handler = telnet_input,
938 .connection_closed_handler = telnet_connection_closed,
939 .keep_client_alive_handler = NULL,
942 int telnet_init(char *banner)
944 if (strcmp(telnet_port, "disabled") == 0) {
945 LOG_INFO("telnet server disabled");
946 return ERROR_OK;
949 struct telnet_service *telnet_service =
950 malloc(sizeof(struct telnet_service));
952 if (!telnet_service) {
953 LOG_ERROR("Failed to allocate telnet service.");
954 return ERROR_FAIL;
957 telnet_service->banner = banner;
959 int ret = add_service(&telnet_service_driver, telnet_port, CONNECTION_LIMIT_UNLIMITED,
960 telnet_service);
962 if (ret != ERROR_OK) {
963 free(telnet_service);
964 return ret;
967 return ERROR_OK;
970 /* daemon configuration command telnet_port */
971 COMMAND_HANDLER(handle_telnet_port_command)
973 return CALL_COMMAND_HANDLER(server_pipe_command, &telnet_port);
976 COMMAND_HANDLER(handle_exit_command)
978 return ERROR_COMMAND_CLOSE_CONNECTION;
981 static const struct command_registration telnet_command_handlers[] = {
983 .name = "exit",
984 .handler = handle_exit_command,
985 .mode = COMMAND_EXEC,
986 .usage = "",
987 .help = "exit telnet session",
990 .name = "telnet_port",
991 .handler = handle_telnet_port_command,
992 .mode = COMMAND_CONFIG,
993 .help = "Specify port on which to listen "
994 "for incoming telnet connections. "
995 "Read help on 'gdb_port'.",
996 .usage = "[port_num]",
998 COMMAND_REGISTRATION_DONE
1001 int telnet_register_commands(struct command_context *cmd_ctx)
1003 telnet_port = strdup("4444");
1004 return register_commands(cmd_ctx, NULL, telnet_command_handlers);
1007 void telnet_service_free(void)
1009 free(telnet_port);