d41cfb53166e61fbb33c587c4c3ef9d24e939855
[fw/openocd] / src / server / telnet_server.c
1 /* SPDX-License-Identifier: GPL-2.0-or-later */
2
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  ***************************************************************************/
13
14 #ifdef HAVE_CONFIG_H
15 #include "config.h"
16 #endif
17
18 #include "telnet_server.h"
19 #include <target/target_request.h>
20 #include <helper/configuration.h>
21 #include <helper/list.h>
22
23 static char *telnet_port;
24
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 */
30
31 #define CTRL(c) (c - '@')
32 #define TELNET_HISTORY  ".openocd_history"
33
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!
37  */
38 static int telnet_write(struct connection *connection, const void *data,
39         int len)
40 {
41         struct telnet_connection *t_con = connection->priv;
42         if (t_con->closed)
43                 return ERROR_SERVER_REMOTE_CLOSED;
44
45         if (connection_write(connection, data, len) == len)
46                 return ERROR_OK;
47         t_con->closed = true;
48         return ERROR_SERVER_REMOTE_CLOSED;
49 }
50
51 /* output an audible bell */
52 static int telnet_bell(struct connection *connection)
53 {
54         /* ("\a" does not work, at least on windows) */
55         return telnet_write(connection, "\x07", 1);
56 }
57
58 static int telnet_prompt(struct connection *connection)
59 {
60         struct telnet_connection *t_con = connection->priv;
61
62         return telnet_write(connection, t_con->prompt, strlen(t_con->prompt));
63 }
64
65 static int telnet_outputline(struct connection *connection, const char *line)
66 {
67         int len;
68
69         /* process lines in buffer */
70         while (*line) {
71                 char *line_end = strchr(line, '\n');
72
73                 if (line_end)
74                         len = line_end-line;
75                 else
76                         len = strlen(line);
77
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;
84         }
85
86         return ERROR_OK;
87 }
88
89 static int telnet_output(struct command_context *cmd_ctx, const char *line)
90 {
91         struct connection *connection = cmd_ctx->output_handler_priv;
92
93         return telnet_outputline(connection, line);
94 }
95
96 static void telnet_log_callback(void *priv, const char *file, unsigned line,
97         const char *function, const char *string)
98 {
99         struct connection *connection = priv;
100         struct telnet_connection *t_con = connection->priv;
101         size_t i;
102         size_t tmp;
103
104         /* If the prompt is not visible, simply output the message. */
105         if (!t_con->prompt_visible) {
106                 telnet_outputline(connection, string);
107                 return;
108         }
109
110         /* Clear the command line. */
111         tmp = strlen(t_con->prompt) + t_con->line_size;
112
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));
116
117         for (i = 0; i < tmp; i += 16)
118                 telnet_write(connection, "                ", MIN(tmp - i, 16));
119
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));
123
124         telnet_outputline(connection, string);
125
126         /* Put the command line to its previous state. */
127         telnet_prompt(connection);
128         telnet_write(connection, t_con->line, t_con->line_size);
129
130         for (i = t_con->line_cursor; i < t_con->line_size; i++)
131                 telnet_write(connection, "\b", 1);
132 }
133
134 static void telnet_load_history(struct telnet_connection *t_con)
135 {
136         FILE *histfp;
137         char buffer[TELNET_BUFFER_SIZE];
138         int i = 0;
139
140         char *history = get_home_dir(TELNET_HISTORY);
141
142         if (!history) {
143                 LOG_INFO("unable to get user home directory, telnet history will be disabled");
144                 return;
145         }
146
147         histfp = fopen(history, "rb");
148
149         if (histfp) {
150
151                 while (fgets(buffer, sizeof(buffer), histfp)) {
152
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);
158                 }
159
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);
165         }
166
167         free(history);
168 }
169
170 static void telnet_save_history(struct telnet_connection *t_con)
171 {
172         FILE *histfp;
173         int i;
174         int num;
175
176         char *history = get_home_dir(TELNET_HISTORY);
177
178         if (!history) {
179                 LOG_INFO("unable to get user home directory, telnet history will be disabled");
180                 return;
181         }
182
183         histfp = fopen(history, "wb");
184
185         if (histfp) {
186
187                 num = TELNET_LINE_HISTORY_SIZE;
188                 i = t_con->current_history + 1;
189                 i %= TELNET_LINE_HISTORY_SIZE;
190
191                 while (!t_con->history[i] && num > 0) {
192                         i++;
193                         i %= TELNET_LINE_HISTORY_SIZE;
194                         num--;
195                 }
196
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;
202                         }
203                 }
204                 fclose(histfp);
205         }
206
207         free(history);
208 }
209
210 static int telnet_new_connection(struct connection *connection)
211 {
212         struct telnet_connection *telnet_connection;
213         struct telnet_service *telnet_service = connection->service->priv;
214
215         telnet_connection = calloc(1, sizeof(struct telnet_connection));
216
217         if (!telnet_connection) {
218                 LOG_ERROR("Failed to allocate telnet connection.");
219                 return ERROR_FAIL;
220         }
221
222         connection->priv = telnet_connection;
223
224         /* initialize telnet connection information */
225         telnet_connection->prompt = strdup("> ");
226         telnet_connection->prompt_visible = true;
227         telnet_connection->state = TELNET_STATE_DATA;
228
229         /* output goes through telnet connection */
230         command_set_output_handler(connection->cmd_ctx, telnet_output, connection);
231
232         /* negotiate telnet options */
233         telnet_write(connection, negotiate, strlen(negotiate));
234
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);
239         }
240
241         /* the prompt is always placed at the line beginning */
242         telnet_write(connection, "\r", 1);
243         telnet_prompt(connection);
244
245         telnet_load_history(telnet_connection);
246
247         log_add_callback(telnet_log_callback, connection);
248
249         return ERROR_OK;
250 }
251
252 static void telnet_clear_line(struct connection *connection,
253         struct telnet_connection *t_con)
254 {
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);
260
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--;
265         }
266         t_con->line_cursor = 0;
267 }
268
269 static void telnet_history_go(struct connection *connection, int idx)
270 {
271         struct telnet_connection *t_con = connection->priv;
272
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;
280         }
281         t_con->state = TELNET_STATE_DATA;
282 }
283
284 static void telnet_history_up(struct connection *connection)
285 {
286         struct telnet_connection *t_con = connection->priv;
287
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);
292 }
293
294 static void telnet_history_down(struct connection *connection)
295 {
296         struct telnet_connection *t_con = connection->priv;
297         size_t next_history;
298
299         next_history = (t_con->current_history + 1) % TELNET_LINE_HISTORY_SIZE;
300         telnet_history_go(connection, next_history);
301 }
302
303 static void telnet_history_add(struct connection *connection)
304 {
305         struct telnet_connection *t_con = connection->priv;
306
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];
310
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]);
314
315                 /* add line to history */
316                 t_con->history[t_con->next_history] = strdup(t_con->line);
317
318                 /* wrap history at TELNET_LINE_HISTORY_SIZE */
319                 t_con->next_history = (t_con->next_history + 1) % TELNET_LINE_HISTORY_SIZE;
320
321                 /* current history line starts at the new entry */
322                 t_con->current_history = t_con->next_history;
323
324                 free(t_con->history[t_con->current_history]);
325                 t_con->history[t_con->current_history] = strdup("");
326         }
327 }
328
329 static int telnet_history_print(struct connection *connection)
330 {
331         struct telnet_connection *tc;
332
333         tc = connection->priv;
334
335         for (size_t i = 1; i < TELNET_LINE_HISTORY_SIZE; i++) {
336                 char *line;
337
338                 /*
339                  * The tc->next_history line contains empty string (unless NULL), thus
340                  * it is not printed.
341                  */
342                 line = tc->history[(tc->next_history + i) % TELNET_LINE_HISTORY_SIZE];
343
344                 if (line) {
345                         telnet_write(connection, line, strlen(line));
346                         telnet_write(connection, "\r\n\x00", 3);
347                 }
348         }
349
350         tc->line_size = 0;
351         tc->line_cursor = 0;
352
353         /* The prompt is always placed at the line beginning. */
354         telnet_write(connection, "\r", 1);
355
356         return telnet_prompt(connection);
357 }
358
359 static void telnet_move_cursor(struct connection *connection, size_t pos)
360 {
361         struct telnet_connection *tc = connection->priv;
362         size_t tmp;
363
364         if (pos == tc->line_cursor) /* nothing to do */
365                 return;
366
367         if (pos > tc->line_size) /* out of bounds */
368                 return;
369
370         if (pos < tc->line_cursor) {
371                 tmp = tc->line_cursor - pos;
372
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;
378
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));
382         }
383
384         tc->line_cursor = pos;
385 }
386
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)
389 {
390         struct telnet_connection *t_con = connection->priv;
391
392         return t_con->line_size + len < TELNET_LINE_MAX_SIZE;
393 }
394
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 ...)
398  *
399  * returns false when it fails to insert the requested data
400  */
401 static bool telnet_insert(struct connection *connection, const void *data, size_t len)
402 {
403         struct telnet_connection *t_con = connection->priv;
404
405         if (!telnet_can_insert(connection, len)) {
406                 telnet_bell(connection);
407                 return false;
408         }
409
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);
415         }
416
417         strncpy(t_con->line + t_con->line_cursor, data, len);
418
419         telnet_write(connection,
420                         t_con->line + t_con->line_cursor,
421                         t_con->line_size + len - t_con->line_cursor);
422
423         t_con->line_size += len;
424         t_con->line_cursor += len;
425
426         for (size_t i = t_con->line_cursor; i < t_con->line_size; i++)
427                 telnet_write(connection, "\b", 1);
428
429         return true;
430 }
431
432 static void telnet_delete_character(struct connection *connection)
433 {
434         struct telnet_connection *t_con = connection->priv;
435
436         if (t_con->line_cursor == 0)
437                 return;
438
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);
448
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);
462         }
463 }
464
465 static void telnet_remove_character(struct connection *connection)
466 {
467         struct telnet_connection *t_con = connection->priv;
468
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);
476
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);
482
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);
486         }
487 }
488
489 static int telnet_exec_line(struct connection *connection)
490 {
491         struct telnet_connection *t_con = connection->priv;
492         struct command_context *command_context = connection->cmd_ctx;
493         int retval;
494
495         telnet_write(connection, "\r\n\x00", 3);
496
497         if (strcmp(t_con->line, "history") == 0) {
498                 retval = telnet_history_print(connection);
499
500                 if (retval != ERROR_OK)
501                         return retval;
502
503                 return ERROR_OK;
504         }
505
506         telnet_history_add(connection);
507
508         t_con->line_size = 0;
509
510         /* to suppress prompt in log callback during command execution */
511         t_con->prompt_visible = false;
512
513         if (strcmp(t_con->line, "shutdown") == 0)
514                 telnet_save_history(t_con);
515
516         retval = command_run_line(command_context, t_con->line);
517
518         t_con->line_cursor = 0;
519         t_con->prompt_visible = true;
520
521         if (retval == ERROR_COMMAND_CLOSE_CONNECTION)
522                 return ERROR_SERVER_REMOTE_CLOSED;
523
524         /* the prompt is always placed at the line beginning */
525         telnet_write(connection, "\r", 1);
526
527         retval = telnet_prompt(connection);
528         if (retval == ERROR_SERVER_REMOTE_CLOSED)
529                 return ERROR_SERVER_REMOTE_CLOSED;
530
531         return ERROR_OK;
532 }
533
534 static void telnet_cut_line_to_end(struct connection *connection)
535 {
536         struct telnet_connection *t_con = connection->priv;
537
538         /* FIXME: currently this function does not save to clipboard */
539
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;
548         }
549 }
550
551 static void telnet_interrupt(struct connection *connection)
552 {
553         struct telnet_connection *t_con = connection->priv;
554
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);
561 }
562
563 static void telnet_auto_complete(struct connection *connection)
564 {
565         struct telnet_connection *t_con = connection->priv;
566         struct command_context *command_context = connection->cmd_ctx;
567
568         struct cmd_match {
569                 char *cmd;
570                 struct list_head lh;
571         };
572
573         LIST_HEAD(matches);
574
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];
584
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;
594                 }
595
596                 if (seq_start == 0)
597                         break;
598
599                 seq_start--;
600         }
601
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++;
606
607         /* check user command length */
608         if (t_con->line_cursor < usr_cmd_pos) {
609                 telnet_bell(connection);
610                 return;
611         }
612         size_t usr_cmd_len = t_con->line_cursor - usr_cmd_pos;
613
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;
623                 }
624
625                 query[i - optimized_spaces] = t_con->line[usr_cmd_pos + i];
626         }
627
628         usr_cmd_len -= optimized_spaces;
629         query[usr_cmd_len] = '\0';
630
631         /* filter commands */
632         char *query_cmd;
633
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);
638
639         if (!query_cmd) {
640                 LOG_ERROR("Out of memory");
641                 return;
642         }
643
644         int retval = Jim_EvalSource(command_context->interp, __FILE__, __LINE__, query_cmd);
645         free(query_cmd);
646         if (retval != JIM_OK)
647                 return;
648
649         Jim_Obj *list = Jim_GetResult(command_context->interp);
650         Jim_IncrRefCount(list);
651
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 */
655
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);
660
661                 char *name = (char *)Jim_GetString(elem, NULL);
662
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);
667
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);
673
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;
680                                 }
681                         }
682                 }
683
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 */
691                         }
692
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 */
698
699                                 while (new_common_len < common_len && first_match[new_common_len] == name[new_common_len])
700                                         new_common_len++;
701
702                                 common_len = new_common_len;
703                         }
704
705                         match->cmd = name;
706                         list_add_tail(&match->lh, &matches);
707                 }
708
709                 Jim_DecrRefCount(command_context->interp, elem);
710         }
711         /* end of command filtering */
712
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);
724                 }
725         } else if (!list_is_singular(&matches)) {
726                 telnet_write(connection, "\n\r", 2);
727
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);
732                 }
733
734                 telnet_prompt(connection);
735                 telnet_write(connection, t_con->line, t_con->line_size);
736
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);
740         }
741
742         /* destroy the command_list */
743         struct cmd_match *tmp, *match;
744         list_for_each_entry_safe(match, tmp, &matches, lh)
745                 free(match);
746
747         Jim_DecrRefCount(command_context->interp, list);
748 }
749
750 static int telnet_input(struct connection *connection)
751 {
752         int bytes_read;
753         unsigned char buffer[TELNET_BUFFER_SIZE];
754         unsigned char *buf_p;
755         struct telnet_connection *t_con = connection->priv;
756
757         bytes_read = connection_read(connection, buffer, TELNET_BUFFER_SIZE);
758
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;
764         }
765
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;
781
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--;
787                                                         }
788                                                         if ((bytes_read > 1) && (*(buf_p + 1) == 0)) {
789                                                                 buf_p++;
790                                                                 bytes_read--;
791                                                         }
792                                                         t_con->line[t_con->line_size] = 0;
793
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);
823                                                 }
824                                         }
825                                 }
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;
841                                 }
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;
875                                         }
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;
891                                 }
892
893                                 break;
894                         default:
895                                 LOG_ERROR("unknown telnet state");
896                                 return ERROR_FAIL;
897                 }
898
899                 bytes_read--;
900                 buf_p++;
901         }
902
903         return ERROR_OK;
904 }
905
906 static int telnet_connection_closed(struct connection *connection)
907 {
908         struct telnet_connection *t_con = connection->priv;
909         int i;
910
911         log_remove_callback(telnet_log_callback, connection);
912
913         free(t_con->prompt);
914         t_con->prompt = NULL;
915
916         /* save telnet history */
917         telnet_save_history(t_con);
918
919         for (i = 0; i < TELNET_LINE_HISTORY_SIZE; i++) {
920                 free(t_con->history[i]);
921                 t_con->history[i] = NULL;
922         }
923
924         /* if this connection registered a debug-message receiver delete it */
925         delete_debug_msg_receiver(connection->cmd_ctx, NULL);
926
927         free(connection->priv);
928         connection->priv = NULL;
929
930         return ERROR_OK;
931 }
932
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,
940 };
941
942 int telnet_init(char *banner)
943 {
944         if (strcmp(telnet_port, "disabled") == 0) {
945                 LOG_INFO("telnet server disabled");
946                 return ERROR_OK;
947         }
948
949         struct telnet_service *telnet_service =
950                 malloc(sizeof(struct telnet_service));
951
952         if (!telnet_service) {
953                 LOG_ERROR("Failed to allocate telnet service.");
954                 return ERROR_FAIL;
955         }
956
957         telnet_service->banner = banner;
958
959         int ret = add_service(&telnet_service_driver, telnet_port, CONNECTION_LIMIT_UNLIMITED,
960                 telnet_service);
961
962         if (ret != ERROR_OK) {
963                 free(telnet_service);
964                 return ret;
965         }
966
967         return ERROR_OK;
968 }
969
970 /* daemon configuration command telnet_port */
971 COMMAND_HANDLER(handle_telnet_port_command)
972 {
973         return CALL_COMMAND_HANDLER(server_pipe_command, &telnet_port);
974 }
975
976 COMMAND_HANDLER(handle_exit_command)
977 {
978         return ERROR_COMMAND_CLOSE_CONNECTION;
979 }
980
981 static const struct command_registration telnet_command_handlers[] = {
982         {
983                 .name = "exit",
984                 .handler = handle_exit_command,
985                 .mode = COMMAND_EXEC,
986                 .usage = "",
987                 .help = "exit telnet session",
988         },
989         {
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]",
997         },
998         COMMAND_REGISTRATION_DONE
999 };
1000
1001 int telnet_register_commands(struct command_context *cmd_ctx)
1002 {
1003         telnet_port = strdup("4444");
1004         return register_commands(cmd_ctx, NULL, telnet_command_handlers);
1005 }
1006
1007 void telnet_service_free(void)
1008 {
1009         free(telnet_port);
1010 }