36b017c58ebb2916d81ad42b7ca0e5b6c5eca4e4
[openocd.git] / 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 #include <helper/list.h>
33
34 static char *telnet_port;
35
36 static char *negotiate =
37 "\xFF\xFB\x03" /* IAC WILL Suppress Go Ahead */
38 "\xFF\xFB\x01" /* IAC WILL Echo */
39 "\xFF\xFD\x03" /* IAC DO Suppress Go Ahead */
40 "\xFF\xFE\x01"; /* IAC DON'T Echo */
41
42 #define CTRL(c) (c - '@')
43 #define TELNET_HISTORY ".openocd_history"
44
45 /* The only way we can detect that the socket is closed is the first time
46 * we write to it, we will fail. Subsequent write operations will
47 * succeed. Shudder!
48 */
49 static int telnet_write(struct connection *connection, const void *data,
50 int len)
51 {
52 struct telnet_connection *t_con = connection->priv;
53 if (t_con->closed)
54 return ERROR_SERVER_REMOTE_CLOSED;
55
56 if (connection_write(connection, data, len) == len)
57 return ERROR_OK;
58 t_con->closed = true;
59 return ERROR_SERVER_REMOTE_CLOSED;
60 }
61
62 /* output an audible bell */
63 static int telnet_bell(struct connection *connection)
64 {
65 /* ("\a" does not work, at least on windows) */
66 return telnet_write(connection, "\x07", 1);
67 }
68
69 static int telnet_prompt(struct connection *connection)
70 {
71 struct telnet_connection *t_con = connection->priv;
72
73 return telnet_write(connection, t_con->prompt, strlen(t_con->prompt));
74 }
75
76 static int telnet_outputline(struct connection *connection, const char *line)
77 {
78 int len;
79
80 /* process lines in buffer */
81 while (*line) {
82 char *line_end = strchr(line, '\n');
83
84 if (line_end)
85 len = line_end-line;
86 else
87 len = strlen(line);
88
89 telnet_write(connection, line, len);
90 if (line_end) {
91 telnet_write(connection, "\r\n", 2);
92 line += len + 1;
93 } else
94 line += len;
95 }
96
97 return ERROR_OK;
98 }
99
100 static int telnet_output(struct command_context *cmd_ctx, const char *line)
101 {
102 struct connection *connection = cmd_ctx->output_handler_priv;
103
104 return telnet_outputline(connection, line);
105 }
106
107 static void telnet_log_callback(void *priv, const char *file, unsigned line,
108 const char *function, const char *string)
109 {
110 struct connection *connection = priv;
111 struct telnet_connection *t_con = connection->priv;
112 size_t i;
113 size_t tmp;
114
115 /* If the prompt is not visible, simply output the message. */
116 if (!t_con->prompt_visible) {
117 telnet_outputline(connection, string);
118 return;
119 }
120
121 /* Clear the command line. */
122 tmp = strlen(t_con->prompt) + t_con->line_size;
123
124 for (i = 0; i < tmp; i += 16)
125 telnet_write(connection, "\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b",
126 MIN(tmp - i, 16));
127
128 for (i = 0; i < tmp; i += 16)
129 telnet_write(connection, " ", MIN(tmp - i, 16));
130
131 for (i = 0; i < tmp; i += 16)
132 telnet_write(connection, "\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b",
133 MIN(tmp - i, 16));
134
135 telnet_outputline(connection, string);
136
137 /* Put the command line to its previous state. */
138 telnet_prompt(connection);
139 telnet_write(connection, t_con->line, t_con->line_size);
140
141 for (i = t_con->line_cursor; i < t_con->line_size; i++)
142 telnet_write(connection, "\b", 1);
143 }
144
145 static void telnet_load_history(struct telnet_connection *t_con)
146 {
147 FILE *histfp;
148 char buffer[TELNET_BUFFER_SIZE];
149 int i = 0;
150
151 char *history = get_home_dir(TELNET_HISTORY);
152
153 if (!history) {
154 LOG_INFO("unable to get user home directory, telnet history will be disabled");
155 return;
156 }
157
158 histfp = fopen(history, "rb");
159
160 if (histfp) {
161
162 while (fgets(buffer, sizeof(buffer), histfp)) {
163
164 char *p = strchr(buffer, '\n');
165 if (p)
166 *p = '\0';
167 if (buffer[0] && i < TELNET_LINE_HISTORY_SIZE)
168 t_con->history[i++] = strdup(buffer);
169 }
170
171 t_con->next_history = i;
172 t_con->next_history %= TELNET_LINE_HISTORY_SIZE;
173 /* try to set to last entry - 1, that way we skip over any exit/shutdown cmds */
174 t_con->current_history = t_con->next_history > 0 ? i - 1 : 0;
175 fclose(histfp);
176 }
177
178 free(history);
179 }
180
181 static void telnet_save_history(struct telnet_connection *t_con)
182 {
183 FILE *histfp;
184 int i;
185 int num;
186
187 char *history = get_home_dir(TELNET_HISTORY);
188
189 if (!history) {
190 LOG_INFO("unable to get user home directory, telnet history will be disabled");
191 return;
192 }
193
194 histfp = fopen(history, "wb");
195
196 if (histfp) {
197
198 num = TELNET_LINE_HISTORY_SIZE;
199 i = t_con->current_history + 1;
200 i %= TELNET_LINE_HISTORY_SIZE;
201
202 while (!t_con->history[i] && num > 0) {
203 i++;
204 i %= TELNET_LINE_HISTORY_SIZE;
205 num--;
206 }
207
208 if (num > 0) {
209 for (; num > 0; num--) {
210 fprintf(histfp, "%s\n", t_con->history[i]);
211 i++;
212 i %= TELNET_LINE_HISTORY_SIZE;
213 }
214 }
215 fclose(histfp);
216 }
217
218 free(history);
219 }
220
221 static int telnet_new_connection(struct connection *connection)
222 {
223 struct telnet_connection *telnet_connection;
224 struct telnet_service *telnet_service = connection->service->priv;
225 int i;
226
227 telnet_connection = malloc(sizeof(struct telnet_connection));
228
229 if (!telnet_connection) {
230 LOG_ERROR("Failed to allocate telnet connection.");
231 return ERROR_FAIL;
232 }
233
234 connection->priv = telnet_connection;
235
236 /* initialize telnet connection information */
237 telnet_connection->closed = false;
238 telnet_connection->line_size = 0;
239 telnet_connection->line_cursor = 0;
240 telnet_connection->prompt = strdup("> ");
241 telnet_connection->prompt_visible = true;
242 telnet_connection->state = TELNET_STATE_DATA;
243
244 /* output goes through telnet connection */
245 command_set_output_handler(connection->cmd_ctx, telnet_output, connection);
246
247 /* negotiate telnet options */
248 telnet_write(connection, negotiate, strlen(negotiate));
249
250 /* print connection banner */
251 if (telnet_service->banner) {
252 telnet_write(connection, telnet_service->banner, strlen(telnet_service->banner));
253 telnet_write(connection, "\r\n", 2);
254 }
255
256 /* the prompt is always placed at the line beginning */
257 telnet_write(connection, "\r", 1);
258 telnet_prompt(connection);
259
260 /* initialize history */
261 for (i = 0; i < TELNET_LINE_HISTORY_SIZE; i++)
262 telnet_connection->history[i] = NULL;
263 telnet_connection->next_history = 0;
264 telnet_connection->current_history = 0;
265 telnet_load_history(telnet_connection);
266
267 log_add_callback(telnet_log_callback, connection);
268
269 return ERROR_OK;
270 }
271
272 static void telnet_clear_line(struct connection *connection,
273 struct telnet_connection *t_con)
274 {
275 /* move to end of line */
276 if (t_con->line_cursor < t_con->line_size)
277 telnet_write(connection,
278 t_con->line + t_con->line_cursor,
279 t_con->line_size - t_con->line_cursor);
280
281 /* backspace, overwrite with space, backspace */
282 while (t_con->line_size > 0) {
283 telnet_write(connection, "\b \b", 3);
284 t_con->line_size--;
285 }
286 t_con->line_cursor = 0;
287 }
288
289 static void telnet_history_go(struct connection *connection, int idx)
290 {
291 struct telnet_connection *t_con = connection->priv;
292
293 if (t_con->history[idx]) {
294 telnet_clear_line(connection, t_con);
295 t_con->line_size = strlen(t_con->history[idx]);
296 t_con->line_cursor = t_con->line_size;
297 memcpy(t_con->line, t_con->history[idx], t_con->line_size);
298 telnet_write(connection, t_con->line, t_con->line_size);
299 t_con->current_history = idx;
300 }
301 t_con->state = TELNET_STATE_DATA;
302 }
303
304 static void telnet_history_up(struct connection *connection)
305 {
306 struct telnet_connection *t_con = connection->priv;
307
308 size_t last_history = (t_con->current_history > 0) ?
309 t_con->current_history - 1 :
310 TELNET_LINE_HISTORY_SIZE-1;
311 telnet_history_go(connection, last_history);
312 }
313
314 static void telnet_history_down(struct connection *connection)
315 {
316 struct telnet_connection *t_con = connection->priv;
317 size_t next_history;
318
319 next_history = (t_con->current_history + 1) % TELNET_LINE_HISTORY_SIZE;
320 telnet_history_go(connection, next_history);
321 }
322
323 static int telnet_history_print(struct connection *connection)
324 {
325 struct telnet_connection *tc;
326
327 tc = connection->priv;
328
329 for (size_t i = 1; i < TELNET_LINE_HISTORY_SIZE; i++) {
330 char *line;
331
332 /*
333 * The tc->next_history line contains empty string (unless NULL), thus
334 * it is not printed.
335 */
336 line = tc->history[(tc->next_history + i) % TELNET_LINE_HISTORY_SIZE];
337
338 if (line) {
339 telnet_write(connection, line, strlen(line));
340 telnet_write(connection, "\r\n\x00", 3);
341 }
342 }
343
344 tc->line_size = 0;
345 tc->line_cursor = 0;
346
347 /* The prompt is always placed at the line beginning. */
348 telnet_write(connection, "\r", 1);
349
350 return telnet_prompt(connection);
351 }
352
353 static void telnet_move_cursor(struct connection *connection, size_t pos)
354 {
355 struct telnet_connection *tc = connection->priv;
356 size_t tmp;
357
358 if (pos == tc->line_cursor) /* nothing to do */
359 return;
360
361 if (pos > tc->line_size) /* out of bounds */
362 return;
363
364 if (pos < tc->line_cursor) {
365 tmp = tc->line_cursor - pos;
366
367 for (size_t i = 0; i < tmp; i += 16)
368 telnet_write(connection, "\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b",
369 MIN(tmp - i, 16));
370 } else {
371 tmp = pos - tc->line_cursor;
372
373 for (size_t i = 0; i < tmp; i += 16)
374 telnet_write(connection, tc->line + tc->line_cursor + i,
375 MIN(tmp - i, 16));
376 }
377
378 tc->line_cursor = pos;
379 }
380
381 /* check buffer size leaving one spare character for string null termination */
382 static inline bool telnet_can_insert(struct connection *connection, size_t len)
383 {
384 struct telnet_connection *t_con = connection->priv;
385
386 return t_con->line_size + len < TELNET_LINE_MAX_SIZE;
387 }
388
389 /* write to telnet console, and update the telnet_connection members
390 * this function is capable of inserting in the middle of a line
391 * please ensure that data does not contain special characters (\n, \r, \t, \b ...)
392 *
393 * returns false when it fails to insert the requested data
394 */
395 static bool telnet_insert(struct connection *connection, const void *data, size_t len)
396 {
397 struct telnet_connection *t_con = connection->priv;
398
399 if (!telnet_can_insert(connection, len)) {
400 telnet_bell(connection);
401 return false;
402 }
403
404 if (t_con->line_cursor < t_con->line_size) {
405 /* we have some content after the cursor */
406 memmove(t_con->line + t_con->line_cursor + len,
407 t_con->line + t_con->line_cursor,
408 t_con->line_size - t_con->line_cursor);
409 }
410
411 strncpy(t_con->line + t_con->line_cursor, data, len);
412
413 telnet_write(connection,
414 t_con->line + t_con->line_cursor,
415 t_con->line_size + len - t_con->line_cursor);
416
417 t_con->line_size += len;
418 t_con->line_cursor += len;
419
420 for (size_t i = t_con->line_cursor; i < t_con->line_size; i++)
421 telnet_write(connection, "\b", 1);
422
423 return true;
424 }
425
426 static void telnet_auto_complete(struct connection *connection)
427 {
428 struct telnet_connection *t_con = connection->priv;
429 struct command_context *command_context = connection->cmd_ctx;
430
431 struct cmd_match {
432 char *cmd;
433 struct list_head lh;
434 };
435
436 LIST_HEAD(matches);
437
438 /* user command sequence, either at line beginning
439 * or we start over after these characters ';', '[', '{' */
440 size_t seq_start = (t_con->line_cursor == 0) ? 0 : (t_con->line_cursor - 1);
441 while (seq_start > 0) {
442 char c = t_con->line[seq_start];
443 if (c == ';' || c == '[' || c == '{') {
444 seq_start++;
445 break;
446 }
447
448 seq_start--;
449 }
450
451 /* user command position in the line, ignore leading spaces */
452 size_t usr_cmd_pos = seq_start;
453 while ((usr_cmd_pos < t_con->line_cursor) && isspace(t_con->line[usr_cmd_pos]))
454 usr_cmd_pos++;
455
456 /* user command length */
457 size_t usr_cmd_len = t_con->line_cursor - usr_cmd_pos;
458
459 /* optimize multiple spaces in the user command,
460 * because info commands does not tolerate multiple spaces */
461 size_t optimized_spaces = 0;
462 char query[usr_cmd_len + 1];
463 for (size_t i = 0; i < usr_cmd_len; i++) {
464 if ((i < usr_cmd_len - 1) && isspace(t_con->line[usr_cmd_pos + i])
465 && isspace(t_con->line[usr_cmd_pos + i + 1])) {
466 optimized_spaces++;
467 continue;
468 }
469
470 query[i - optimized_spaces] = t_con->line[usr_cmd_pos + i];
471 }
472
473 usr_cmd_len -= optimized_spaces;
474 query[usr_cmd_len] = '\0';
475
476 /* filter commands */
477 char *query_cmd = alloc_printf("_telnet_autocomplete_helper {%s*}", query);
478
479 if (!query_cmd) {
480 LOG_ERROR("Out of memory");
481 return;
482 }
483
484 int retval = Jim_EvalSource(command_context->interp, __FILE__, __LINE__, query_cmd);
485 free(query_cmd);
486 if (retval != JIM_OK)
487 return;
488
489 Jim_Obj *list = Jim_GetResult(command_context->interp);
490 Jim_IncrRefCount(list);
491
492 /* common prefix length of the matched commands */
493 size_t common_len = 0;
494 char *first_match = NULL; /* used to compute the common prefix length */
495
496 int len = Jim_ListLength(command_context->interp, list);
497 for (int i = 0; i < len; i++) {
498 Jim_Obj *elem = Jim_ListGetIndex(command_context->interp, list, i);
499 Jim_IncrRefCount(elem);
500
501 char *name = (char *)Jim_GetString(elem, NULL);
502
503 /* validate the command */
504 bool ignore_cmd = false;
505 Jim_Cmd *jim_cmd = Jim_GetCommand(command_context->interp, elem, JIM_NONE);
506
507 if (!jim_cmd) {
508 /* Why we are here? Let's ignore it! */
509 ignore_cmd = true;
510 } else if (jimcmd_is_oocd_command(jim_cmd)) {
511 struct command *cmd = jimcmd_privdata(jim_cmd);
512
513 if (cmd && !cmd->handler && !cmd->jim_handler) {
514 /* Initial part of a multi-word command. Ignore it! */
515 ignore_cmd = true;
516 } else if (cmd && cmd->mode == COMMAND_CONFIG) {
517 /* Not executable after config phase. Ignore it! */
518 ignore_cmd = true;
519 }
520 }
521
522 /* save the command in the prediction list */
523 if (!ignore_cmd) {
524 struct cmd_match *match = calloc(1, sizeof(struct cmd_match));
525 if (!match) {
526 LOG_ERROR("Out of memory");
527 Jim_DecrRefCount(command_context->interp, elem);
528 break; /* break the for loop */
529 }
530
531 if (list_empty(&matches)) {
532 common_len = strlen(name);
533 first_match = name;
534 } else {
535 size_t new_common_len = usr_cmd_len; /* save some loops */
536
537 while (new_common_len < common_len && first_match[new_common_len] == name[new_common_len])
538 new_common_len++;
539
540 common_len = new_common_len;
541 }
542
543 match->cmd = name;
544 list_add_tail(&match->lh, &matches);
545 }
546
547 Jim_DecrRefCount(command_context->interp, elem);
548 }
549 /* end of command filtering */
550
551 /* proceed with auto-completion */
552 if (list_empty(&matches))
553 telnet_bell(connection);
554 else if (common_len == usr_cmd_len && list_is_singular(&matches) && t_con->line_cursor == t_con->line_size)
555 telnet_insert(connection, " ", 1);
556 else if (common_len > usr_cmd_len) {
557 int completion_size = common_len - usr_cmd_len;
558 if (telnet_insert(connection, first_match + usr_cmd_len, completion_size)) {
559 /* in bash this extra space is only added when the cursor in at the end of line */
560 if (list_is_singular(&matches) && t_con->line_cursor == t_con->line_size)
561 telnet_insert(connection, " ", 1);
562 }
563 } else if (!list_is_singular(&matches)) {
564 telnet_write(connection, "\n\r", 2);
565
566 struct cmd_match *match;
567 list_for_each_entry(match, &matches, lh) {
568 telnet_write(connection, match->cmd, strlen(match->cmd));
569 telnet_write(connection, "\n\r", 2);
570 }
571
572 telnet_prompt(connection);
573 telnet_write(connection, t_con->line, t_con->line_size);
574
575 /* restore the terminal visible cursor location */
576 for (size_t i = t_con->line_cursor; i < t_con->line_size; i++)
577 telnet_write(connection, "\b", 1);
578 }
579
580 /* destroy the command_list */
581 struct cmd_match *tmp, *match;
582 list_for_each_entry_safe(match, tmp, &matches, lh)
583 free(match);
584
585 Jim_DecrRefCount(command_context->interp, list);
586 }
587
588 static int telnet_input(struct connection *connection)
589 {
590 int bytes_read;
591 unsigned char buffer[TELNET_BUFFER_SIZE];
592 unsigned char *buf_p;
593 struct telnet_connection *t_con = connection->priv;
594 struct command_context *command_context = connection->cmd_ctx;
595
596 bytes_read = connection_read(connection, buffer, TELNET_BUFFER_SIZE);
597
598 if (bytes_read == 0)
599 return ERROR_SERVER_REMOTE_CLOSED;
600 else if (bytes_read == -1) {
601 LOG_ERROR("error during read: %s", strerror(errno));
602 return ERROR_SERVER_REMOTE_CLOSED;
603 }
604
605 buf_p = buffer;
606 while (bytes_read) {
607 switch (t_con->state) {
608 case TELNET_STATE_DATA:
609 if (*buf_p == 0xff) {
610 t_con->state = TELNET_STATE_IAC;
611 } else {
612 if (isprint(*buf_p)) { /* printable character */
613 telnet_insert(connection, buf_p, 1);
614 } else { /* non-printable */
615 if (*buf_p == 0x1b) { /* escape */
616 t_con->state = TELNET_STATE_ESCAPE;
617 t_con->last_escape = '\x00';
618 } else if ((*buf_p == 0xd) || (*buf_p == 0xa)) { /* CR/LF */
619 int retval;
620
621 /* skip over combinations with CR/LF and NUL characters */
622 if ((bytes_read > 1) && ((*(buf_p + 1) == 0xa) ||
623 (*(buf_p + 1) == 0xd))) {
624 buf_p++;
625 bytes_read--;
626 }
627 if ((bytes_read > 1) && (*(buf_p + 1) == 0)) {
628 buf_p++;
629 bytes_read--;
630 }
631 t_con->line[t_con->line_size] = 0;
632
633 telnet_write(connection, "\r\n\x00", 3);
634
635 if (strcmp(t_con->line, "history") == 0) {
636 retval = telnet_history_print(connection);
637
638 if (retval != ERROR_OK)
639 return retval;
640
641 continue;
642 }
643
644 /* save only non-blank not repeating lines in the history */
645 char *prev_line = t_con->history[(t_con->current_history > 0) ?
646 t_con->current_history - 1 : TELNET_LINE_HISTORY_SIZE-1];
647 if (*t_con->line && (!prev_line ||
648 strcmp(t_con->line, prev_line))) {
649 /* if the history slot is already taken, free it */
650 free(t_con->history[t_con->next_history]);
651
652 /* add line to history */
653 t_con->history[t_con->next_history] = strdup(t_con->line);
654
655 /* wrap history at TELNET_LINE_HISTORY_SIZE */
656 t_con->next_history = (t_con->next_history + 1) %
657 TELNET_LINE_HISTORY_SIZE;
658
659 /* current history line starts at the new entry */
660 t_con->current_history =
661 t_con->next_history;
662
663 free(t_con->history[t_con->current_history]);
664 t_con->history[t_con->current_history] = strdup("");
665 }
666
667 t_con->line_size = 0;
668
669 /* to suppress prompt in log callback during command execution */
670 t_con->prompt_visible = false;
671
672 if (strcmp(t_con->line, "shutdown") == 0)
673 telnet_save_history(t_con);
674
675 retval = command_run_line(command_context, t_con->line);
676
677 t_con->line_cursor = 0;
678 t_con->prompt_visible = true;
679
680 if (retval == ERROR_COMMAND_CLOSE_CONNECTION)
681 return ERROR_SERVER_REMOTE_CLOSED;
682
683 /* the prompt is always * placed at the line beginning */
684 telnet_write(connection, "\r", 1);
685
686 retval = telnet_prompt(connection);
687 if (retval == ERROR_SERVER_REMOTE_CLOSED)
688 return ERROR_SERVER_REMOTE_CLOSED;
689
690 } else if ((*buf_p == 0x7f) || (*buf_p == 0x8)) { /* delete character */
691 if (t_con->line_cursor > 0) {
692 if (t_con->line_cursor != t_con->line_size) {
693 size_t i;
694 telnet_write(connection, "\b", 1);
695 t_con->line_cursor--;
696 t_con->line_size--;
697 memmove(t_con->line + t_con->line_cursor,
698 t_con->line + t_con->line_cursor + 1,
699 t_con->line_size -
700 t_con->line_cursor);
701
702 telnet_write(connection,
703 t_con->line + t_con->line_cursor,
704 t_con->line_size -
705 t_con->line_cursor);
706 telnet_write(connection, " \b", 2);
707 for (i = t_con->line_cursor; i < t_con->line_size; i++)
708 telnet_write(connection, "\b", 1);
709 } else {
710 t_con->line_size--;
711 t_con->line_cursor--;
712 /* back space: move the 'printer' head one char
713 * back, overwrite with space, move back again */
714 telnet_write(connection, "\b \b", 3);
715 }
716 }
717 } else if (*buf_p == 0x15) { /* clear line */
718 telnet_clear_line(connection, t_con);
719 } else if (*buf_p == CTRL('B')) { /* cursor left */
720 if (t_con->line_cursor > 0) {
721 telnet_write(connection, "\b", 1);
722 t_con->line_cursor--;
723 }
724 t_con->state = TELNET_STATE_DATA;
725 } else if (*buf_p == CTRL('C')) { /* interrupt */
726 /* print '^C' at line end, and display a new command prompt */
727 telnet_move_cursor(connection, t_con->line_size);
728 telnet_write(connection, "^C\n\r", 4);
729 t_con->line_cursor = 0;
730 t_con->line_size = 0;
731 telnet_prompt(connection);
732 } else if (*buf_p == CTRL('F')) { /* cursor right */
733 if (t_con->line_cursor < t_con->line_size)
734 telnet_write(connection, t_con->line + t_con->line_cursor++, 1);
735 t_con->state = TELNET_STATE_DATA;
736 } else if (*buf_p == CTRL('P')) { /* cursor up */
737 telnet_history_up(connection);
738 } else if (*buf_p == CTRL('N')) { /* cursor down */
739 telnet_history_down(connection);
740 } else if (*buf_p == CTRL('A')) { /* move the cursor to the beginning of the line */
741 telnet_move_cursor(connection, 0);
742 } else if (*buf_p == CTRL('E')) { /* move the cursor to the end of the line */
743 telnet_move_cursor(connection, t_con->line_size);
744 } else if (*buf_p == CTRL('K')) { /* kill line to end */
745 if (t_con->line_cursor < t_con->line_size) {
746 /* overwrite with space, until end of line, move back */
747 for (size_t i = t_con->line_cursor; i < t_con->line_size; i++)
748 telnet_write(connection, " ", 1);
749 for (size_t i = t_con->line_cursor; i < t_con->line_size; i++)
750 telnet_write(connection, "\b", 1);
751 t_con->line[t_con->line_cursor] = '\0';
752 t_con->line_size = t_con->line_cursor;
753 }
754 } else if (*buf_p == '\t') {
755 telnet_auto_complete(connection);
756 } else {
757 LOG_DEBUG("unhandled nonprintable: %2.2x", *buf_p);
758 }
759 }
760 }
761 break;
762 case TELNET_STATE_IAC:
763 switch (*buf_p) {
764 case 0xfe:
765 t_con->state = TELNET_STATE_DONT;
766 break;
767 case 0xfd:
768 t_con->state = TELNET_STATE_DO;
769 break;
770 case 0xfc:
771 t_con->state = TELNET_STATE_WONT;
772 break;
773 case 0xfb:
774 t_con->state = TELNET_STATE_WILL;
775 break;
776 }
777 break;
778 case TELNET_STATE_SB:
779 break;
780 case TELNET_STATE_SE:
781 break;
782 case TELNET_STATE_WILL:
783 case TELNET_STATE_WONT:
784 case TELNET_STATE_DO:
785 case TELNET_STATE_DONT:
786 t_con->state = TELNET_STATE_DATA;
787 break;
788 case TELNET_STATE_ESCAPE:
789 if (t_con->last_escape == '[') {
790 if (*buf_p == 'D') { /* cursor left */
791 if (t_con->line_cursor > 0) {
792 telnet_write(connection, "\b", 1);
793 t_con->line_cursor--;
794 }
795 t_con->state = TELNET_STATE_DATA;
796 } else if (*buf_p == 'C') { /* cursor right */
797 if (t_con->line_cursor < t_con->line_size)
798 telnet_write(connection,
799 t_con->line + t_con->line_cursor++, 1);
800 t_con->state = TELNET_STATE_DATA;
801 } else if (*buf_p == 'A') { /* cursor up */
802 telnet_history_up(connection);
803 } else if (*buf_p == 'B') { /* cursor down */
804 telnet_history_down(connection);
805 } else if (*buf_p == 'F') { /* end key */
806 telnet_move_cursor(connection, t_con->line_size);
807 t_con->state = TELNET_STATE_DATA;
808 } else if (*buf_p == 'H') { /* home key */
809 telnet_move_cursor(connection, 0);
810 t_con->state = TELNET_STATE_DATA;
811 } else if (*buf_p == '3') {
812 t_con->last_escape = *buf_p;
813 } else {
814 t_con->state = TELNET_STATE_DATA;
815 }
816 } else if (t_con->last_escape == '3') {
817 /* Remove character */
818 if (*buf_p == '~') {
819 if (t_con->line_cursor < t_con->line_size) {
820 size_t i;
821 t_con->line_size--;
822 /* remove char from line buffer */
823 memmove(t_con->line + t_con->line_cursor,
824 t_con->line + t_con->line_cursor + 1,
825 t_con->line_size - t_con->line_cursor);
826
827 /* print remainder of buffer */
828 telnet_write(connection, t_con->line + t_con->line_cursor,
829 t_con->line_size - t_con->line_cursor);
830 /* overwrite last char with whitespace */
831 telnet_write(connection, " \b", 2);
832
833 /* move back to cursor position*/
834 for (i = t_con->line_cursor; i < t_con->line_size; i++)
835 telnet_write(connection, "\b", 1);
836 }
837
838 t_con->state = TELNET_STATE_DATA;
839 } else
840 t_con->state = TELNET_STATE_DATA;
841 } else if (t_con->last_escape == '\x00') {
842 if (*buf_p == '[')
843 t_con->last_escape = *buf_p;
844 else
845 t_con->state = TELNET_STATE_DATA;
846 } else {
847 LOG_ERROR("BUG: unexpected value in t_con->last_escape");
848 t_con->state = TELNET_STATE_DATA;
849 }
850
851 break;
852 default:
853 LOG_ERROR("unknown telnet state");
854 return ERROR_FAIL;
855 }
856
857 bytes_read--;
858 buf_p++;
859 }
860
861 return ERROR_OK;
862 }
863
864 static int telnet_connection_closed(struct connection *connection)
865 {
866 struct telnet_connection *t_con = connection->priv;
867 int i;
868
869 log_remove_callback(telnet_log_callback, connection);
870
871 free(t_con->prompt);
872 t_con->prompt = NULL;
873
874 /* save telnet history */
875 telnet_save_history(t_con);
876
877 for (i = 0; i < TELNET_LINE_HISTORY_SIZE; i++) {
878 free(t_con->history[i]);
879 t_con->history[i] = NULL;
880 }
881
882 /* if this connection registered a debug-message receiver delete it */
883 delete_debug_msg_receiver(connection->cmd_ctx, NULL);
884
885 free(connection->priv);
886 connection->priv = NULL;
887
888 return ERROR_OK;
889 }
890
891 int telnet_init(char *banner)
892 {
893 if (strcmp(telnet_port, "disabled") == 0) {
894 LOG_INFO("telnet server disabled");
895 return ERROR_OK;
896 }
897
898 struct telnet_service *telnet_service =
899 malloc(sizeof(struct telnet_service));
900
901 if (!telnet_service) {
902 LOG_ERROR("Failed to allocate telnet service.");
903 return ERROR_FAIL;
904 }
905
906 telnet_service->banner = banner;
907
908 int ret = add_service("telnet", telnet_port, CONNECTION_LIMIT_UNLIMITED,
909 telnet_new_connection, telnet_input, telnet_connection_closed,
910 telnet_service);
911
912 if (ret != ERROR_OK) {
913 free(telnet_service);
914 return ret;
915 }
916
917 return ERROR_OK;
918 }
919
920 /* daemon configuration command telnet_port */
921 COMMAND_HANDLER(handle_telnet_port_command)
922 {
923 return CALL_COMMAND_HANDLER(server_pipe_command, &telnet_port);
924 }
925
926 COMMAND_HANDLER(handle_exit_command)
927 {
928 return ERROR_COMMAND_CLOSE_CONNECTION;
929 }
930
931 static const struct command_registration telnet_command_handlers[] = {
932 {
933 .name = "exit",
934 .handler = handle_exit_command,
935 .mode = COMMAND_EXEC,
936 .usage = "",
937 .help = "exit telnet session",
938 },
939 {
940 .name = "telnet_port",
941 .handler = handle_telnet_port_command,
942 .mode = COMMAND_CONFIG,
943 .help = "Specify port on which to listen "
944 "for incoming telnet connections. "
945 "Read help on 'gdb_port'.",
946 .usage = "[port_num]",
947 },
948 COMMAND_REGISTRATION_DONE
949 };
950
951 int telnet_register_commands(struct command_context *cmd_ctx)
952 {
953 telnet_port = strdup("4444");
954 return register_commands(cmd_ctx, NULL, telnet_command_handlers);
955 }
956
957 void telnet_service_free(void)
958 {
959 free(telnet_port);
960 }

Linking to existing account procedure

If you already have an account and want to add another login method you MUST first sign in with your existing account and then change URL to read https://review.openocd.org/login/?link to get to this page again but this time it'll work for linking. Thank you.

SSH host keys fingerprints

1024 SHA256:YKx8b7u5ZWdcbp7/4AeXNaqElP49m6QrwfXaqQGJAOk gerrit-code-review@openocd.zylin.com (DSA)
384 SHA256:jHIbSQa4REvwCFG4cq5LBlBLxmxSqelQPem/EXIrxjk gerrit-code-review@openocd.org (ECDSA)
521 SHA256:UAOPYkU9Fjtcao0Ul/Rrlnj/OsQvt+pgdYSZ4jOYdgs gerrit-code-review@openocd.org (ECDSA)
256 SHA256:A13M5QlnozFOvTllybRZH6vm7iSt0XLxbA48yfc2yfY gerrit-code-review@openocd.org (ECDSA)
256 SHA256:spYMBqEYoAOtK7yZBrcwE8ZpYt6b68Cfh9yEVetvbXg gerrit-code-review@openocd.org (ED25519)
+--[ED25519 256]--+
|=..              |
|+o..   .         |
|*.o   . .        |
|+B . . .         |
|Bo. = o S        |
|Oo.+ + =         |
|oB=.* = . o      |
| =+=.+   + E     |
|. .=o   . o      |
+----[SHA256]-----+
2048 SHA256:0Onrb7/PHjpo6iVZ7xQX2riKN83FJ3KGU0TvI0TaFG4 gerrit-code-review@openocd.zylin.com (RSA)