telnet: support end and home keys
[fw/openocd] / src / server / telnet_server.c
1 /***************************************************************************
2  *   Copyright (C) 2005 by Dominic Rath                                    *
3  *   Dominic.Rath@gmx.de                                                   *
4  *                                                                         *
5  *   Copyright (C) 2007-2010 Ã˜yvind Harboe                                 *
6  *   oyvind.harboe@zylin.com                                               *
7  *                                                                         *
8  *   Copyright (C) 2008 by Spencer Oliver                                  *
9  *   spen@spen-soft.co.uk                                                  *
10  *                                                                         *
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.                                   *
15  *                                                                         *
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.                          *
20  *                                                                         *
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  ***************************************************************************/
24
25 #ifdef HAVE_CONFIG_H
26 #include "config.h"
27 #endif
28
29 #include "telnet_server.h"
30 #include <target/target_request.h>
31 #include <helper/configuration.h>
32
33 static char *telnet_port;
34
35 static char *negotiate =
36         "\xFF\xFB\x03"                  /* IAC WILL Suppress Go Ahead */
37         "\xFF\xFB\x01"                  /* IAC WILL Echo */
38         "\xFF\xFD\x03"                  /* IAC DO Suppress Go Ahead */
39         "\xFF\xFE\x01";                 /* IAC DON'T Echo */
40
41 #define CTRL(c) (c - '@')
42 #define TELNET_HISTORY  ".openocd_history"
43
44 /* The only way we can detect that the socket is closed is the first time
45  * we write to it, we will fail. Subsequent write operations will
46  * succeed. Shudder!
47  */
48 static int telnet_write(struct connection *connection, const void *data,
49         int len)
50 {
51         struct telnet_connection *t_con = connection->priv;
52         if (t_con->closed)
53                 return ERROR_SERVER_REMOTE_CLOSED;
54
55         if (connection_write(connection, data, len) == len)
56                 return ERROR_OK;
57         t_con->closed = true;
58         return ERROR_SERVER_REMOTE_CLOSED;
59 }
60
61 static int telnet_prompt(struct connection *connection)
62 {
63         struct telnet_connection *t_con = connection->priv;
64
65         return telnet_write(connection, t_con->prompt, strlen(t_con->prompt));
66 }
67
68 static int telnet_outputline(struct connection *connection, const char *line)
69 {
70         int len;
71
72         /* process lines in buffer */
73         while (*line) {
74                 char *line_end = strchr(line, '\n');
75
76                 if (line_end)
77                         len = line_end-line;
78                 else
79                         len = strlen(line);
80
81                 telnet_write(connection, line, len);
82                 if (line_end) {
83                         telnet_write(connection, "\r\n", 2);
84                         line += len + 1;
85                 } else
86                         line += len;
87         }
88
89         return ERROR_OK;
90 }
91
92 static int telnet_output(struct command_context *cmd_ctx, const char *line)
93 {
94         struct connection *connection = cmd_ctx->output_handler_priv;
95
96         return telnet_outputline(connection, line);
97 }
98
99 static void telnet_log_callback(void *priv, const char *file, unsigned line,
100         const char *function, const char *string)
101 {
102         struct connection *connection = priv;
103         struct telnet_connection *t_con = connection->priv;
104         size_t i;
105         size_t tmp;
106
107         /* If the prompt is not visible, simply output the message. */
108         if (!t_con->prompt_visible) {
109                 telnet_outputline(connection, string);
110                 return;
111         }
112
113         /* Clear the command line. */
114         tmp = strlen(t_con->prompt) + t_con->line_size;
115
116         for (i = 0; i < tmp; i += 16)
117                 telnet_write(connection, "\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b",
118                         MIN(tmp - i, 16));
119
120         for (i = 0; i < tmp; i += 16)
121                 telnet_write(connection, "                ", MIN(tmp - i, 16));
122
123         for (i = 0; i < tmp; i += 16)
124                 telnet_write(connection, "\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b",
125                         MIN(tmp - i, 16));
126
127         telnet_outputline(connection, string);
128
129         /* Put the command line to its previous state. */
130         telnet_prompt(connection);
131         telnet_write(connection, t_con->line, t_con->line_size);
132
133         for (i = t_con->line_cursor; i < t_con->line_size; i++)
134                 telnet_write(connection, "\b", 1);
135 }
136
137 static void telnet_load_history(struct telnet_connection *t_con)
138 {
139         FILE *histfp;
140         char buffer[TELNET_BUFFER_SIZE];
141         int i = 0;
142
143         char *history = get_home_dir(TELNET_HISTORY);
144
145         if (history == NULL) {
146                 LOG_INFO("unable to get user home directory, telnet history will be disabled");
147                 return;
148         }
149
150         histfp = fopen(history, "rb");
151
152         if (histfp) {
153
154                 while (fgets(buffer, sizeof(buffer), histfp) != NULL) {
155
156                         char *p = strchr(buffer, '\n');
157                         if (p)
158                                 *p = '\0';
159                         if (buffer[0] && i < TELNET_LINE_HISTORY_SIZE)
160                                 t_con->history[i++] = strdup(buffer);
161                 }
162
163                 t_con->next_history = i;
164                 t_con->next_history %= TELNET_LINE_HISTORY_SIZE;
165                 /* try to set to last entry - 1, that way we skip over any exit/shutdown cmds */
166                 t_con->current_history = t_con->next_history > 0 ? i - 1 : 0;
167                 fclose(histfp);
168         }
169
170         free(history);
171 }
172
173 static void telnet_save_history(struct telnet_connection *t_con)
174 {
175         FILE *histfp;
176         int i;
177         int num;
178
179         char *history = get_home_dir(TELNET_HISTORY);
180
181         if (history == NULL) {
182                 LOG_INFO("unable to get user home directory, telnet history will be disabled");
183                 return;
184         }
185
186         histfp = fopen(history, "wb");
187
188         if (histfp) {
189
190                 num = TELNET_LINE_HISTORY_SIZE;
191                 i = t_con->current_history + 1;
192                 i %= TELNET_LINE_HISTORY_SIZE;
193
194                 while (t_con->history[i] == NULL && num > 0) {
195                         i++;
196                         i %= TELNET_LINE_HISTORY_SIZE;
197                         num--;
198                 }
199
200                 if (num > 0) {
201                         for (; num > 0; num--) {
202                                 fprintf(histfp, "%s\n", t_con->history[i]);
203                                 i++;
204                                 i %= TELNET_LINE_HISTORY_SIZE;
205                         }
206                 }
207                 fclose(histfp);
208         }
209
210         free(history);
211 }
212
213 static int telnet_new_connection(struct connection *connection)
214 {
215         struct telnet_connection *telnet_connection;
216         struct telnet_service *telnet_service = connection->service->priv;
217         int i;
218
219         telnet_connection = malloc(sizeof(struct telnet_connection));
220
221         if (!telnet_connection) {
222                 LOG_ERROR("Failed to allocate telnet connection.");
223                 return ERROR_FAIL;
224         }
225
226         connection->priv = telnet_connection;
227
228         /* initialize telnet connection information */
229         telnet_connection->closed = false;
230         telnet_connection->line_size = 0;
231         telnet_connection->line_cursor = 0;
232         telnet_connection->prompt = strdup("> ");
233         telnet_connection->prompt_visible = true;
234         telnet_connection->state = TELNET_STATE_DATA;
235
236         /* output goes through telnet connection */
237         command_set_output_handler(connection->cmd_ctx, telnet_output, connection);
238
239         /* negotiate telnet options */
240         telnet_write(connection, negotiate, strlen(negotiate));
241
242         /* print connection banner */
243         if (telnet_service->banner) {
244                 telnet_write(connection, telnet_service->banner, strlen(telnet_service->banner));
245                 telnet_write(connection, "\r\n", 2);
246         }
247
248         /* the prompt is always placed at the line beginning */
249         telnet_write(connection, "\r", 1);
250         telnet_prompt(connection);
251
252         /* initialize history */
253         for (i = 0; i < TELNET_LINE_HISTORY_SIZE; i++)
254                 telnet_connection->history[i] = NULL;
255         telnet_connection->next_history = 0;
256         telnet_connection->current_history = 0;
257         telnet_load_history(telnet_connection);
258
259         log_add_callback(telnet_log_callback, connection);
260
261         return ERROR_OK;
262 }
263
264 static void telnet_clear_line(struct connection *connection,
265         struct telnet_connection *t_con)
266 {
267         /* move to end of line */
268         if (t_con->line_cursor < t_con->line_size)
269                 telnet_write(connection,
270                         t_con->line + t_con->line_cursor,
271                         t_con->line_size - t_con->line_cursor);
272
273         /* backspace, overwrite with space, backspace */
274         while (t_con->line_size > 0) {
275                 telnet_write(connection, "\b \b", 3);
276                 t_con->line_size--;
277         }
278         t_con->line_cursor = 0;
279 }
280
281 static void telnet_history_go(struct connection *connection, int idx)
282 {
283         struct telnet_connection *t_con = connection->priv;
284
285         if (t_con->history[idx]) {
286                 telnet_clear_line(connection, t_con);
287                 t_con->line_size = strlen(t_con->history[idx]);
288                 t_con->line_cursor = t_con->line_size;
289                 memcpy(t_con->line, t_con->history[idx], t_con->line_size);
290                 telnet_write(connection, t_con->line, t_con->line_size);
291                 t_con->current_history = idx;
292         }
293         t_con->state = TELNET_STATE_DATA;
294 }
295
296 static void telnet_history_up(struct connection *connection)
297 {
298         struct telnet_connection *t_con = connection->priv;
299
300         size_t last_history = (t_con->current_history > 0) ?
301                                 t_con->current_history - 1 :
302                                 TELNET_LINE_HISTORY_SIZE-1;
303         telnet_history_go(connection, last_history);
304 }
305
306 static void telnet_history_down(struct connection *connection)
307 {
308         struct telnet_connection *t_con = connection->priv;
309         size_t next_history;
310
311         next_history = (t_con->current_history + 1) % TELNET_LINE_HISTORY_SIZE;
312         telnet_history_go(connection, next_history);
313 }
314
315 static int telnet_history_print(struct connection *connection)
316 {
317         struct telnet_connection *tc;
318
319         tc = connection->priv;
320
321         for (size_t i = 1; i < TELNET_LINE_HISTORY_SIZE; i++) {
322                 char *line;
323
324                 /*
325                  * The tc->next_history line contains empty string (unless NULL), thus
326                  * it is not printed.
327                  */
328                 line = tc->history[(tc->next_history + i) % TELNET_LINE_HISTORY_SIZE];
329
330                 if (line) {
331                         telnet_write(connection, line, strlen(line));
332                         telnet_write(connection, "\r\n\x00", 3);
333                 }
334         }
335
336         tc->line_size = 0;
337         tc->line_cursor = 0;
338
339         /* The prompt is always placed at the line beginning. */
340         telnet_write(connection, "\r", 1);
341
342         return telnet_prompt(connection);
343 }
344
345 static void telnet_move_cursor(struct connection *connection, size_t pos)
346 {
347         struct telnet_connection *tc;
348         size_t tmp;
349
350         tc = connection->priv;
351
352         if (pos < tc->line_cursor) {
353                 tmp = tc->line_cursor - pos;
354
355                 for (size_t i = 0; i < tmp; i += 16)
356                         telnet_write(connection, "\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b",
357                                 MIN(tmp - i, 16));
358         } else {
359                 tmp = pos - tc->line_cursor;
360
361                 for (size_t i = 0; i < tmp; i += 16)
362                         telnet_write(connection, tc->line + tc->line_cursor + i,
363                                 MIN(tmp - i, 16));
364         }
365
366         tc->line_cursor = pos;
367 }
368
369 static int telnet_input(struct connection *connection)
370 {
371         int bytes_read;
372         unsigned char buffer[TELNET_BUFFER_SIZE];
373         unsigned char *buf_p;
374         struct telnet_connection *t_con = connection->priv;
375         struct command_context *command_context = connection->cmd_ctx;
376
377         bytes_read = connection_read(connection, buffer, TELNET_BUFFER_SIZE);
378
379         if (bytes_read == 0)
380                 return ERROR_SERVER_REMOTE_CLOSED;
381         else if (bytes_read == -1) {
382                 LOG_ERROR("error during read: %s", strerror(errno));
383                 return ERROR_SERVER_REMOTE_CLOSED;
384         }
385
386         buf_p = buffer;
387         while (bytes_read) {
388                 switch (t_con->state) {
389                         case TELNET_STATE_DATA:
390                                 if (*buf_p == 0xff)
391                                         t_con->state = TELNET_STATE_IAC;
392                                 else {
393                                         if (isprint(*buf_p)) {  /* printable character */
394                                                 /* watch buffer size leaving one spare character for
395                                                  * string null termination */
396                                                 if (t_con->line_size == TELNET_LINE_MAX_SIZE-1) {
397                                                         /* output audible bell if buffer is full
398                                                          * "\a" does not work, at least on windows */
399                                                         telnet_write(connection, "\x07", 1);
400                                                 } else if (t_con->line_cursor == t_con->line_size) {
401                                                         telnet_write(connection, buf_p, 1);
402                                                         t_con->line[t_con->line_size++] = *buf_p;
403                                                         t_con->line_cursor++;
404                                                 } else {
405                                                         size_t i;
406                                                         memmove(t_con->line + t_con->line_cursor + 1,
407                                                                         t_con->line + t_con->line_cursor,
408                                                                         t_con->line_size - t_con->line_cursor);
409                                                         t_con->line[t_con->line_cursor] = *buf_p;
410                                                         t_con->line_size++;
411                                                         telnet_write(connection,
412                                                                         t_con->line + t_con->line_cursor,
413                                                                         t_con->line_size - t_con->line_cursor);
414                                                         t_con->line_cursor++;
415                                                         for (i = t_con->line_cursor; i < t_con->line_size; i++)
416                                                                 telnet_write(connection, "\b", 1);
417                                                 }
418                                         } else {        /* non-printable */
419                                                 if (*buf_p == 0x1b) {   /* escape */
420                                                         t_con->state = TELNET_STATE_ESCAPE;
421                                                         t_con->last_escape = '\x00';
422                                                 } else if ((*buf_p == 0xd) || (*buf_p == 0xa)) {        /* CR/LF */
423                                                         int retval;
424
425                                                         /* skip over combinations with CR/LF and NUL characters */
426                                                         if ((bytes_read > 1) && ((*(buf_p + 1) == 0xa) ||
427                                                                         (*(buf_p + 1) == 0xd))) {
428                                                                 buf_p++;
429                                                                 bytes_read--;
430                                                         }
431                                                         if ((bytes_read > 1) && (*(buf_p + 1) == 0)) {
432                                                                 buf_p++;
433                                                                 bytes_read--;
434                                                         }
435                                                         t_con->line[t_con->line_size] = 0;
436
437                                                         telnet_write(connection, "\r\n\x00", 3);
438
439                                                         if (strcmp(t_con->line, "history") == 0) {
440                                                                 retval = telnet_history_print(connection);
441
442                                                                 if (retval != ERROR_OK)
443                                                                         return retval;
444
445                                                                 continue;
446                                                         }
447
448                                                         /* save only non-blank not repeating lines in the history */
449                                                         char *prev_line = t_con->history[(t_con->current_history > 0) ?
450                                                                         t_con->current_history - 1 : TELNET_LINE_HISTORY_SIZE-1];
451                                                         if (*t_con->line && (prev_line == NULL ||
452                                                                         strcmp(t_con->line, prev_line))) {
453                                                                 /* if the history slot is already taken, free it */
454                                                                 free(t_con->history[t_con->next_history]);
455
456                                                                 /* add line to history */
457                                                                 t_con->history[t_con->next_history] = strdup(t_con->line);
458
459                                                                 /* wrap history at TELNET_LINE_HISTORY_SIZE */
460                                                                 t_con->next_history = (t_con->next_history + 1) %
461                                                                                 TELNET_LINE_HISTORY_SIZE;
462
463                                                                 /* current history line starts at the new entry */
464                                                                 t_con->current_history =
465                                                                                 t_con->next_history;
466
467                                                                 free(t_con->history[t_con->current_history]);
468                                                                 t_con->history[t_con->current_history] = strdup("");
469                                                         }
470
471                                                         t_con->line_size = 0;
472
473                                                         /* to suppress prompt in log callback during command execution */
474                                                         t_con->prompt_visible = false;
475
476                                                         if (strcmp(t_con->line, "shutdown") == 0)
477                                                                 telnet_save_history(t_con);
478
479                                                         retval = command_run_line(command_context, t_con->line);
480
481                                                         t_con->line_cursor = 0;
482                                                         t_con->prompt_visible = true;
483
484                                                         if (retval == ERROR_COMMAND_CLOSE_CONNECTION)
485                                                                 return ERROR_SERVER_REMOTE_CLOSED;
486
487                                                         /* the prompt is always * placed at the line beginning */
488                                                         telnet_write(connection, "\r", 1);
489
490                                                         retval = telnet_prompt(connection);
491                                                         if (retval == ERROR_SERVER_REMOTE_CLOSED)
492                                                                 return ERROR_SERVER_REMOTE_CLOSED;
493
494                                                 } else if ((*buf_p == 0x7f) || (*buf_p == 0x8)) {       /* delete character */
495                                                         if (t_con->line_cursor > 0) {
496                                                                 if (t_con->line_cursor != t_con->line_size) {
497                                                                         size_t i;
498                                                                         telnet_write(connection, "\b", 1);
499                                                                         t_con->line_cursor--;
500                                                                         t_con->line_size--;
501                                                                         memmove(t_con->line + t_con->line_cursor,
502                                                                                         t_con->line + t_con->line_cursor + 1,
503                                                                                         t_con->line_size -
504                                                                                         t_con->line_cursor);
505
506                                                                         telnet_write(connection,
507                                                                                         t_con->line + t_con->line_cursor,
508                                                                                         t_con->line_size -
509                                                                                         t_con->line_cursor);
510                                                                         telnet_write(connection, " \b", 2);
511                                                                         for (i = t_con->line_cursor; i < t_con->line_size; i++)
512                                                                                 telnet_write(connection, "\b", 1);
513                                                                 } else {
514                                                                         t_con->line_size--;
515                                                                         t_con->line_cursor--;
516                                                                         /* back space: move the 'printer' head one char
517                                                                          * back, overwrite with space, move back again */
518                                                                         telnet_write(connection, "\b \b", 3);
519                                                                 }
520                                                         }
521                                                 } else if (*buf_p == 0x15) /* clear line */
522                                                         telnet_clear_line(connection, t_con);
523                                                 else if (*buf_p == CTRL('B')) { /* cursor left */
524                                                         if (t_con->line_cursor > 0) {
525                                                                 telnet_write(connection, "\b", 1);
526                                                                 t_con->line_cursor--;
527                                                         }
528                                                         t_con->state = TELNET_STATE_DATA;
529                                                 } else if (*buf_p == CTRL('F')) {       /* cursor right */
530                                                         if (t_con->line_cursor < t_con->line_size)
531                                                                 telnet_write(connection, t_con->line + t_con->line_cursor++, 1);
532                                                         t_con->state = TELNET_STATE_DATA;
533                                                 } else if (*buf_p == CTRL('P'))         /* cursor up */
534                                                         telnet_history_up(connection);
535                                                 else if (*buf_p == CTRL('N'))           /* cursor down */
536                                                         telnet_history_down(connection);
537                                                 else if (*buf_p == CTRL('A'))
538                                                         telnet_move_cursor(connection, 0);
539                                                 else if (*buf_p == CTRL('E'))
540                                                         telnet_move_cursor(connection, t_con->line_size);
541                                                 else if (*buf_p == CTRL('K')) {         /* kill line to end */
542                                                         if (t_con->line_cursor < t_con->line_size) {
543                                                                 /* overwrite with space, until end of line, move back */
544                                                                 for (size_t i = t_con->line_cursor; i < t_con->line_size; i++)
545                                                                         telnet_write(connection, " ", 1);
546                                                                 for (size_t i = t_con->line_cursor; i < t_con->line_size; i++)
547                                                                         telnet_write(connection, "\b", 1);
548                                                                 t_con->line[t_con->line_cursor] = '\0';
549                                                                 t_con->line_size = t_con->line_cursor;
550                                                         }
551                                                 } else
552                                                         LOG_DEBUG("unhandled nonprintable: %2.2x", *buf_p);
553                                         }
554                                 }
555                                 break;
556                         case TELNET_STATE_IAC:
557                                 switch (*buf_p) {
558                                 case 0xfe:
559                                         t_con->state = TELNET_STATE_DONT;
560                                         break;
561                                 case 0xfd:
562                                         t_con->state = TELNET_STATE_DO;
563                                         break;
564                                 case 0xfc:
565                                         t_con->state = TELNET_STATE_WONT;
566                                         break;
567                                 case 0xfb:
568                                         t_con->state = TELNET_STATE_WILL;
569                                         break;
570                                 }
571                                 break;
572                         case TELNET_STATE_SB:
573                                 break;
574                         case TELNET_STATE_SE:
575                                 break;
576                         case TELNET_STATE_WILL:
577                         case TELNET_STATE_WONT:
578                         case TELNET_STATE_DO:
579                         case TELNET_STATE_DONT:
580                                 t_con->state = TELNET_STATE_DATA;
581                                 break;
582                         case TELNET_STATE_ESCAPE:
583                                 if (t_con->last_escape == '[') {
584                                         if (*buf_p == 'D') {    /* cursor left */
585                                                 if (t_con->line_cursor > 0) {
586                                                         telnet_write(connection, "\b", 1);
587                                                         t_con->line_cursor--;
588                                                 }
589                                                 t_con->state = TELNET_STATE_DATA;
590                                         } else if (*buf_p == 'C') {     /* cursor right */
591                                                 if (t_con->line_cursor < t_con->line_size)
592                                                         telnet_write(connection,
593                                                                         t_con->line + t_con->line_cursor++, 1);
594                                                 t_con->state = TELNET_STATE_DATA;
595                                         } else if (*buf_p == 'A') {     /* cursor up */
596                                                 telnet_history_up(connection);
597                                         } else if (*buf_p == 'B') {     /* cursor down */
598                                                 telnet_history_down(connection);
599                                         } else if (*buf_p == 'F') { /* end key */
600                                                 telnet_move_cursor(connection, t_con->line_size);
601                                                 t_con->state = TELNET_STATE_DATA;
602                                         } else if (*buf_p == 'H') { /* home key */
603                                                 telnet_move_cursor(connection, 0);
604                                                 t_con->state = TELNET_STATE_DATA;
605                                         } else if (*buf_p == '3')
606                                                 t_con->last_escape = *buf_p;
607                                         else
608                                                 t_con->state = TELNET_STATE_DATA;
609                                 } else if (t_con->last_escape == '3') {
610                                         /* Remove character */
611                                         if (*buf_p == '~') {
612                                                 if (t_con->line_cursor < t_con->line_size) {
613                                                         size_t i;
614                                                         t_con->line_size--;
615                                                         /* remove char from line buffer */
616                                                         memmove(t_con->line + t_con->line_cursor,
617                                                                         t_con->line + t_con->line_cursor + 1,
618                                                                         t_con->line_size - t_con->line_cursor);
619
620                                                         /* print remainder of buffer */
621                                                         telnet_write(connection, t_con->line + t_con->line_cursor,
622                                                                         t_con->line_size - t_con->line_cursor);
623                                                         /* overwrite last char with whitespace */
624                                                         telnet_write(connection, " \b", 2);
625
626                                                         /* move back to cursor position*/
627                                                         for (i = t_con->line_cursor; i < t_con->line_size; i++)
628                                                                 telnet_write(connection, "\b", 1);
629                                                 }
630
631                                                 t_con->state = TELNET_STATE_DATA;
632                                         } else
633                                                 t_con->state = TELNET_STATE_DATA;
634                                 } else if (t_con->last_escape == '\x00') {
635                                         if (*buf_p == '[')
636                                                 t_con->last_escape = *buf_p;
637                                         else
638                                                 t_con->state = TELNET_STATE_DATA;
639                                 } else {
640                                         LOG_ERROR("BUG: unexpected value in t_con->last_escape");
641                                         t_con->state = TELNET_STATE_DATA;
642                                 }
643
644                                 break;
645                         default:
646                                 LOG_ERROR("unknown telnet state");
647                                 return ERROR_FAIL;
648                 }
649
650                 bytes_read--;
651                 buf_p++;
652         }
653
654         return ERROR_OK;
655 }
656
657 static int telnet_connection_closed(struct connection *connection)
658 {
659         struct telnet_connection *t_con = connection->priv;
660         int i;
661
662         log_remove_callback(telnet_log_callback, connection);
663
664         free(t_con->prompt);
665         t_con->prompt = NULL;
666
667         /* save telnet history */
668         telnet_save_history(t_con);
669
670         for (i = 0; i < TELNET_LINE_HISTORY_SIZE; i++) {
671                 free(t_con->history[i]);
672                 t_con->history[i] = NULL;
673         }
674
675         /* if this connection registered a debug-message receiver delete it */
676         delete_debug_msg_receiver(connection->cmd_ctx, NULL);
677
678         free(connection->priv);
679         connection->priv = NULL;
680
681         return ERROR_OK;
682 }
683
684 int telnet_init(char *banner)
685 {
686         if (strcmp(telnet_port, "disabled") == 0) {
687                 LOG_INFO("telnet server disabled");
688                 return ERROR_OK;
689         }
690
691         struct telnet_service *telnet_service =
692                 malloc(sizeof(struct telnet_service));
693
694         if (!telnet_service) {
695                 LOG_ERROR("Failed to allocate telnet service.");
696                 return ERROR_FAIL;
697         }
698
699         telnet_service->banner = banner;
700
701         int ret = add_service("telnet", telnet_port, CONNECTION_LIMIT_UNLIMITED,
702                 telnet_new_connection, telnet_input, telnet_connection_closed,
703                 telnet_service);
704
705         if (ret != ERROR_OK) {
706                 free(telnet_service);
707                 return ret;
708         }
709
710         return ERROR_OK;
711 }
712
713 /* daemon configuration command telnet_port */
714 COMMAND_HANDLER(handle_telnet_port_command)
715 {
716         return CALL_COMMAND_HANDLER(server_pipe_command, &telnet_port);
717 }
718
719 COMMAND_HANDLER(handle_exit_command)
720 {
721         return ERROR_COMMAND_CLOSE_CONNECTION;
722 }
723
724 static const struct command_registration telnet_command_handlers[] = {
725         {
726                 .name = "exit",
727                 .handler = handle_exit_command,
728                 .mode = COMMAND_EXEC,
729                 .usage = "",
730                 .help = "exit telnet session",
731         },
732         {
733                 .name = "telnet_port",
734                 .handler = handle_telnet_port_command,
735                 .mode = COMMAND_CONFIG,
736                 .help = "Specify port on which to listen "
737                         "for incoming telnet connections.  "
738                         "Read help on 'gdb_port'.",
739                 .usage = "[port_num]",
740         },
741         COMMAND_REGISTRATION_DONE
742 };
743
744 int telnet_register_commands(struct command_context *cmd_ctx)
745 {
746         telnet_port = strdup("4444");
747         return register_commands(cmd_ctx, NULL, telnet_command_handlers);
748 }
749
750 void telnet_service_free(void)
751 {
752         free(telnet_port);
753 }