4fc71f384e26a693e9bc0dc5d538811da732065d
[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
226 telnet_connection = calloc(1, sizeof(struct telnet_connection));
227
228 if (!telnet_connection) {
229 LOG_ERROR("Failed to allocate telnet connection.");
230 return ERROR_FAIL;
231 }
232
233 connection->priv = telnet_connection;
234
235 /* initialize telnet connection information */
236 telnet_connection->prompt = strdup("> ");
237 telnet_connection->prompt_visible = true;
238 telnet_connection->state = TELNET_STATE_DATA;
239
240 /* output goes through telnet connection */
241 command_set_output_handler(connection->cmd_ctx, telnet_output, connection);
242
243 /* negotiate telnet options */
244 telnet_write(connection, negotiate, strlen(negotiate));
245
246 /* print connection banner */
247 if (telnet_service->banner) {
248 telnet_write(connection, telnet_service->banner, strlen(telnet_service->banner));
249 telnet_write(connection, "\r\n", 2);
250 }
251
252 /* the prompt is always placed at the line beginning */
253 telnet_write(connection, "\r", 1);
254 telnet_prompt(connection);
255
256 telnet_load_history(telnet_connection);
257
258 log_add_callback(telnet_log_callback, connection);
259
260 return ERROR_OK;
261 }
262
263 static void telnet_clear_line(struct connection *connection,
264 struct telnet_connection *t_con)
265 {
266 /* move to end of line */
267 if (t_con->line_cursor < t_con->line_size)
268 telnet_write(connection,
269 t_con->line + t_con->line_cursor,
270 t_con->line_size - t_con->line_cursor);
271
272 /* backspace, overwrite with space, backspace */
273 while (t_con->line_size > 0) {
274 telnet_write(connection, "\b \b", 3);
275 t_con->line_size--;
276 }
277 t_con->line_cursor = 0;
278 }
279
280 static void telnet_history_go(struct connection *connection, int idx)
281 {
282 struct telnet_connection *t_con = connection->priv;
283
284 if (t_con->history[idx]) {
285 telnet_clear_line(connection, t_con);
286 t_con->line_size = strlen(t_con->history[idx]);
287 t_con->line_cursor = t_con->line_size;
288 memcpy(t_con->line, t_con->history[idx], t_con->line_size);
289 telnet_write(connection, t_con->line, t_con->line_size);
290 t_con->current_history = idx;
291 }
292 t_con->state = TELNET_STATE_DATA;
293 }
294
295 static void telnet_history_up(struct connection *connection)
296 {
297 struct telnet_connection *t_con = connection->priv;
298
299 size_t last_history = (t_con->current_history > 0) ?
300 t_con->current_history - 1 :
301 TELNET_LINE_HISTORY_SIZE-1;
302 telnet_history_go(connection, last_history);
303 }
304
305 static void telnet_history_down(struct connection *connection)
306 {
307 struct telnet_connection *t_con = connection->priv;
308 size_t next_history;
309
310 next_history = (t_con->current_history + 1) % TELNET_LINE_HISTORY_SIZE;
311 telnet_history_go(connection, next_history);
312 }
313
314 static void telnet_history_add(struct connection *connection)
315 {
316 struct telnet_connection *t_con = connection->priv;
317
318 /* save only non-blank not repeating lines in the history */
319 char *prev_line = t_con->history[(t_con->current_history > 0) ?
320 t_con->current_history - 1 : TELNET_LINE_HISTORY_SIZE-1];
321
322 if (*t_con->line && (!prev_line || strcmp(t_con->line, prev_line))) {
323 /* if the history slot is already taken, free it */
324 free(t_con->history[t_con->next_history]);
325
326 /* add line to history */
327 t_con->history[t_con->next_history] = strdup(t_con->line);
328
329 /* wrap history at TELNET_LINE_HISTORY_SIZE */
330 t_con->next_history = (t_con->next_history + 1) % TELNET_LINE_HISTORY_SIZE;
331
332 /* current history line starts at the new entry */
333 t_con->current_history = t_con->next_history;
334
335 free(t_con->history[t_con->current_history]);
336 t_con->history[t_con->current_history] = strdup("");
337 }
338 }
339
340 static int telnet_history_print(struct connection *connection)
341 {
342 struct telnet_connection *tc;
343
344 tc = connection->priv;
345
346 for (size_t i = 1; i < TELNET_LINE_HISTORY_SIZE; i++) {
347 char *line;
348
349 /*
350 * The tc->next_history line contains empty string (unless NULL), thus
351 * it is not printed.
352 */
353 line = tc->history[(tc->next_history + i) % TELNET_LINE_HISTORY_SIZE];
354
355 if (line) {
356 telnet_write(connection, line, strlen(line));
357 telnet_write(connection, "\r\n\x00", 3);
358 }
359 }
360
361 tc->line_size = 0;
362 tc->line_cursor = 0;
363
364 /* The prompt is always placed at the line beginning. */
365 telnet_write(connection, "\r", 1);
366
367 return telnet_prompt(connection);
368 }
369
370 static void telnet_move_cursor(struct connection *connection, size_t pos)
371 {
372 struct telnet_connection *tc = connection->priv;
373 size_t tmp;
374
375 if (pos == tc->line_cursor) /* nothing to do */
376 return;
377
378 if (pos > tc->line_size) /* out of bounds */
379 return;
380
381 if (pos < tc->line_cursor) {
382 tmp = tc->line_cursor - pos;
383
384 for (size_t i = 0; i < tmp; i += 16)
385 telnet_write(connection, "\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b",
386 MIN(tmp - i, 16));
387 } else {
388 tmp = pos - tc->line_cursor;
389
390 for (size_t i = 0; i < tmp; i += 16)
391 telnet_write(connection, tc->line + tc->line_cursor + i,
392 MIN(tmp - i, 16));
393 }
394
395 tc->line_cursor = pos;
396 }
397
398 /* check buffer size leaving one spare character for string null termination */
399 static inline bool telnet_can_insert(struct connection *connection, size_t len)
400 {
401 struct telnet_connection *t_con = connection->priv;
402
403 return t_con->line_size + len < TELNET_LINE_MAX_SIZE;
404 }
405
406 /* write to telnet console, and update the telnet_connection members
407 * this function is capable of inserting in the middle of a line
408 * please ensure that data does not contain special characters (\n, \r, \t, \b ...)
409 *
410 * returns false when it fails to insert the requested data
411 */
412 static bool telnet_insert(struct connection *connection, const void *data, size_t len)
413 {
414 struct telnet_connection *t_con = connection->priv;
415
416 if (!telnet_can_insert(connection, len)) {
417 telnet_bell(connection);
418 return false;
419 }
420
421 if (t_con->line_cursor < t_con->line_size) {
422 /* we have some content after the cursor */
423 memmove(t_con->line + t_con->line_cursor + len,
424 t_con->line + t_con->line_cursor,
425 t_con->line_size - t_con->line_cursor);
426 }
427
428 strncpy(t_con->line + t_con->line_cursor, data, len);
429
430 telnet_write(connection,
431 t_con->line + t_con->line_cursor,
432 t_con->line_size + len - t_con->line_cursor);
433
434 t_con->line_size += len;
435 t_con->line_cursor += len;
436
437 for (size_t i = t_con->line_cursor; i < t_con->line_size; i++)
438 telnet_write(connection, "\b", 1);
439
440 return true;
441 }
442
443 static void telnet_delete_character(struct connection *connection)
444 {
445 struct telnet_connection *t_con = connection->priv;
446
447 if (t_con->line_cursor == 0)
448 return;
449
450 if (t_con->line_cursor != t_con->line_size) {
451 size_t i;
452 telnet_write(connection, "\b", 1);
453 t_con->line_cursor--;
454 t_con->line_size--;
455 memmove(t_con->line + t_con->line_cursor,
456 t_con->line + t_con->line_cursor + 1,
457 t_con->line_size -
458 t_con->line_cursor);
459
460 telnet_write(connection,
461 t_con->line + t_con->line_cursor,
462 t_con->line_size -
463 t_con->line_cursor);
464 telnet_write(connection, " \b", 2);
465 for (i = t_con->line_cursor; i < t_con->line_size; i++)
466 telnet_write(connection, "\b", 1);
467 } else {
468 t_con->line_size--;
469 t_con->line_cursor--;
470 /* back space: move the 'printer' head one char
471 * back, overwrite with space, move back again */
472 telnet_write(connection, "\b \b", 3);
473 }
474 }
475
476 static void telnet_remove_character(struct connection *connection)
477 {
478 struct telnet_connection *t_con = connection->priv;
479
480 if (t_con->line_cursor < t_con->line_size) {
481 size_t i;
482 t_con->line_size--;
483 /* remove char from line buffer */
484 memmove(t_con->line + t_con->line_cursor,
485 t_con->line + t_con->line_cursor + 1,
486 t_con->line_size - t_con->line_cursor);
487
488 /* print remainder of buffer */
489 telnet_write(connection, t_con->line + t_con->line_cursor,
490 t_con->line_size - t_con->line_cursor);
491 /* overwrite last char with whitespace */
492 telnet_write(connection, " \b", 2);
493
494 /* move back to cursor position*/
495 for (i = t_con->line_cursor; i < t_con->line_size; i++)
496 telnet_write(connection, "\b", 1);
497 }
498 }
499
500 static int telnet_exec_line(struct connection *connection)
501 {
502 struct telnet_connection *t_con = connection->priv;
503 struct command_context *command_context = connection->cmd_ctx;
504 int retval;
505
506 telnet_write(connection, "\r\n\x00", 3);
507
508 if (strcmp(t_con->line, "history") == 0) {
509 retval = telnet_history_print(connection);
510
511 if (retval != ERROR_OK)
512 return retval;
513
514 return ERROR_OK;
515 }
516
517 telnet_history_add(connection);
518
519 t_con->line_size = 0;
520
521 /* to suppress prompt in log callback during command execution */
522 t_con->prompt_visible = false;
523
524 if (strcmp(t_con->line, "shutdown") == 0)
525 telnet_save_history(t_con);
526
527 retval = command_run_line(command_context, t_con->line);
528
529 t_con->line_cursor = 0;
530 t_con->prompt_visible = true;
531
532 if (retval == ERROR_COMMAND_CLOSE_CONNECTION)
533 return ERROR_SERVER_REMOTE_CLOSED;
534
535 /* the prompt is always placed at the line beginning */
536 telnet_write(connection, "\r", 1);
537
538 retval = telnet_prompt(connection);
539 if (retval == ERROR_SERVER_REMOTE_CLOSED)
540 return ERROR_SERVER_REMOTE_CLOSED;
541
542 return ERROR_OK;
543 }
544
545 static void telnet_cut_line_to_end(struct connection *connection)
546 {
547 struct telnet_connection *t_con = connection->priv;
548
549 /* FIXME: currently this function does not save to clipboard */
550
551 if (t_con->line_cursor < t_con->line_size) {
552 /* overwrite with space, until end of line, move back */
553 for (size_t i = t_con->line_cursor; i < t_con->line_size; i++)
554 telnet_write(connection, " ", 1);
555 for (size_t i = t_con->line_cursor; i < t_con->line_size; i++)
556 telnet_write(connection, "\b", 1);
557 t_con->line[t_con->line_cursor] = '\0';
558 t_con->line_size = t_con->line_cursor;
559 }
560 }
561
562 static void telnet_interrupt(struct connection *connection)
563 {
564 struct telnet_connection *t_con = connection->priv;
565
566 /* print '^C' at line end, and display a new command prompt */
567 telnet_move_cursor(connection, t_con->line_size);
568 telnet_write(connection, "^C\n\r", 4);
569 t_con->line_cursor = 0;
570 t_con->line_size = 0;
571 telnet_prompt(connection);
572 }
573
574 static void telnet_auto_complete(struct connection *connection)
575 {
576 struct telnet_connection *t_con = connection->priv;
577 struct command_context *command_context = connection->cmd_ctx;
578
579 struct cmd_match {
580 char *cmd;
581 struct list_head lh;
582 };
583
584 LIST_HEAD(matches);
585
586 /* - user command sequence, either at line beginning
587 * or we start over after these characters ';', '[', '{'
588 * - user variable sequence, start after the character '$'
589 * and do not contain white spaces */
590 bool is_variable_auto_completion = false;
591 bool have_spaces = false;
592 size_t seq_start = (t_con->line_cursor == 0) ? 0 : (t_con->line_cursor - 1);
593 while (1) {
594 char c = t_con->line[seq_start];
595
596 if (c == ';' || c == '[' || c == '{') {
597 seq_start++;
598 break;
599 } else if (c == ' ') {
600 have_spaces = true;
601 } else if (c == '$' && !have_spaces) {
602 is_variable_auto_completion = true;
603 seq_start++;
604 break;
605 }
606
607 if (seq_start == 0)
608 break;
609
610 seq_start--;
611 }
612
613 /* user command position in the line, ignore leading spaces */
614 size_t usr_cmd_pos = seq_start;
615 while ((usr_cmd_pos < t_con->line_cursor) && isspace(t_con->line[usr_cmd_pos]))
616 usr_cmd_pos++;
617
618 /* user command length */
619 size_t usr_cmd_len = t_con->line_cursor - usr_cmd_pos;
620
621 /* optimize multiple spaces in the user command,
622 * because info commands does not tolerate multiple spaces */
623 size_t optimized_spaces = 0;
624 char query[usr_cmd_len + 1];
625 for (size_t i = 0; i < usr_cmd_len; i++) {
626 if ((i < usr_cmd_len - 1) && isspace(t_con->line[usr_cmd_pos + i])
627 && isspace(t_con->line[usr_cmd_pos + i + 1])) {
628 optimized_spaces++;
629 continue;
630 }
631
632 query[i - optimized_spaces] = t_con->line[usr_cmd_pos + i];
633 }
634
635 usr_cmd_len -= optimized_spaces;
636 query[usr_cmd_len] = '\0';
637
638 /* filter commands */
639 char *query_cmd;
640
641 if (is_variable_auto_completion)
642 query_cmd = alloc_printf("lsort [info vars {%s*}]", query);
643 else
644 query_cmd = alloc_printf("_telnet_autocomplete_helper {%s*}", query);
645
646 if (!query_cmd) {
647 LOG_ERROR("Out of memory");
648 return;
649 }
650
651 int retval = Jim_EvalSource(command_context->interp, __FILE__, __LINE__, query_cmd);
652 free(query_cmd);
653 if (retval != JIM_OK)
654 return;
655
656 Jim_Obj *list = Jim_GetResult(command_context->interp);
657 Jim_IncrRefCount(list);
658
659 /* common prefix length of the matched commands */
660 size_t common_len = 0;
661 char *first_match = NULL; /* used to compute the common prefix length */
662
663 int len = Jim_ListLength(command_context->interp, list);
664 for (int i = 0; i < len; i++) {
665 Jim_Obj *elem = Jim_ListGetIndex(command_context->interp, list, i);
666 Jim_IncrRefCount(elem);
667
668 char *name = (char *)Jim_GetString(elem, NULL);
669
670 /* validate the command */
671 bool ignore_cmd = false;
672 if (!is_variable_auto_completion) {
673 Jim_Cmd *jim_cmd = Jim_GetCommand(command_context->interp, elem, JIM_NONE);
674
675 if (!jim_cmd) {
676 /* Why we are here? Let's ignore it! */
677 ignore_cmd = true;
678 } else if (jimcmd_is_oocd_command(jim_cmd)) {
679 struct command *cmd = jimcmd_privdata(jim_cmd);
680
681 if (cmd && !cmd->handler && !cmd->jim_handler) {
682 /* Initial part of a multi-word command. Ignore it! */
683 ignore_cmd = true;
684 } else if (cmd && cmd->mode == COMMAND_CONFIG) {
685 /* Not executable after config phase. Ignore it! */
686 ignore_cmd = true;
687 }
688 }
689 }
690
691 /* save the command in the prediction list */
692 if (!ignore_cmd) {
693 struct cmd_match *match = calloc(1, sizeof(struct cmd_match));
694 if (!match) {
695 LOG_ERROR("Out of memory");
696 Jim_DecrRefCount(command_context->interp, elem);
697 break; /* break the for loop */
698 }
699
700 if (list_empty(&matches)) {
701 common_len = strlen(name);
702 first_match = name;
703 } else {
704 size_t new_common_len = usr_cmd_len; /* save some loops */
705
706 while (new_common_len < common_len && first_match[new_common_len] == name[new_common_len])
707 new_common_len++;
708
709 common_len = new_common_len;
710 }
711
712 match->cmd = name;
713 list_add_tail(&match->lh, &matches);
714 }
715
716 Jim_DecrRefCount(command_context->interp, elem);
717 }
718 /* end of command filtering */
719
720 /* proceed with auto-completion */
721 if (list_empty(&matches))
722 telnet_bell(connection);
723 else if (common_len == usr_cmd_len && list_is_singular(&matches) && t_con->line_cursor == t_con->line_size)
724 telnet_insert(connection, " ", 1);
725 else if (common_len > usr_cmd_len) {
726 int completion_size = common_len - usr_cmd_len;
727 if (telnet_insert(connection, first_match + usr_cmd_len, completion_size)) {
728 /* in bash this extra space is only added when the cursor in at the end of line */
729 if (list_is_singular(&matches) && t_con->line_cursor == t_con->line_size)
730 telnet_insert(connection, " ", 1);
731 }
732 } else if (!list_is_singular(&matches)) {
733 telnet_write(connection, "\n\r", 2);
734
735 struct cmd_match *match;
736 list_for_each_entry(match, &matches, lh) {
737 telnet_write(connection, match->cmd, strlen(match->cmd));
738 telnet_write(connection, "\n\r", 2);
739 }
740
741 telnet_prompt(connection);
742 telnet_write(connection, t_con->line, t_con->line_size);
743
744 /* restore the terminal visible cursor location */
745 for (size_t i = t_con->line_cursor; i < t_con->line_size; i++)
746 telnet_write(connection, "\b", 1);
747 }
748
749 /* destroy the command_list */
750 struct cmd_match *tmp, *match;
751 list_for_each_entry_safe(match, tmp, &matches, lh)
752 free(match);
753
754 Jim_DecrRefCount(command_context->interp, list);
755 }
756
757 static int telnet_input(struct connection *connection)
758 {
759 int bytes_read;
760 unsigned char buffer[TELNET_BUFFER_SIZE];
761 unsigned char *buf_p;
762 struct telnet_connection *t_con = connection->priv;
763
764 bytes_read = connection_read(connection, buffer, TELNET_BUFFER_SIZE);
765
766 if (bytes_read == 0)
767 return ERROR_SERVER_REMOTE_CLOSED;
768 else if (bytes_read == -1) {
769 LOG_ERROR("error during read: %s", strerror(errno));
770 return ERROR_SERVER_REMOTE_CLOSED;
771 }
772
773 buf_p = buffer;
774 while (bytes_read) {
775 switch (t_con->state) {
776 case TELNET_STATE_DATA:
777 if (*buf_p == 0xff) {
778 t_con->state = TELNET_STATE_IAC;
779 } else {
780 if (isprint(*buf_p)) { /* printable character */
781 telnet_insert(connection, buf_p, 1);
782 } else { /* non-printable */
783 if (*buf_p == 0x1b) { /* escape */
784 t_con->state = TELNET_STATE_ESCAPE;
785 t_con->last_escape = '\x00';
786 } else if ((*buf_p == 0xd) || (*buf_p == 0xa)) { /* CR/LF */
787 int retval;
788
789 /* skip over combinations with CR/LF and NUL characters */
790 if ((bytes_read > 1) && ((*(buf_p + 1) == 0xa) ||
791 (*(buf_p + 1) == 0xd))) {
792 buf_p++;
793 bytes_read--;
794 }
795 if ((bytes_read > 1) && (*(buf_p + 1) == 0)) {
796 buf_p++;
797 bytes_read--;
798 }
799 t_con->line[t_con->line_size] = 0;
800
801 retval = telnet_exec_line(connection);
802 if (retval != ERROR_OK)
803 return retval;
804 } else if ((*buf_p == 0x7f) || (*buf_p == 0x8)) { /* delete character */
805 telnet_delete_character(connection);
806 } else if (*buf_p == 0x15) { /* clear line */
807 telnet_clear_line(connection, t_con);
808 } else if (*buf_p == CTRL('B')) { /* cursor left */
809 telnet_move_cursor(connection, t_con->line_cursor - 1);
810 t_con->state = TELNET_STATE_DATA;
811 } else if (*buf_p == CTRL('C')) { /* interrupt */
812 telnet_interrupt(connection);
813 } else if (*buf_p == CTRL('F')) { /* cursor right */
814 telnet_move_cursor(connection, t_con->line_cursor + 1);
815 t_con->state = TELNET_STATE_DATA;
816 } else if (*buf_p == CTRL('P')) { /* cursor up */
817 telnet_history_up(connection);
818 } else if (*buf_p == CTRL('N')) { /* cursor down */
819 telnet_history_down(connection);
820 } else if (*buf_p == CTRL('A')) { /* move the cursor to the beginning of the line */
821 telnet_move_cursor(connection, 0);
822 } else if (*buf_p == CTRL('E')) { /* move the cursor to the end of the line */
823 telnet_move_cursor(connection, t_con->line_size);
824 } else if (*buf_p == CTRL('K')) { /* kill line to end */
825 telnet_cut_line_to_end(connection);
826 } else if (*buf_p == '\t') {
827 telnet_auto_complete(connection);
828 } else {
829 LOG_DEBUG("unhandled nonprintable: %2.2x", *buf_p);
830 }
831 }
832 }
833 break;
834 case TELNET_STATE_IAC:
835 switch (*buf_p) {
836 case 0xfe:
837 t_con->state = TELNET_STATE_DONT;
838 break;
839 case 0xfd:
840 t_con->state = TELNET_STATE_DO;
841 break;
842 case 0xfc:
843 t_con->state = TELNET_STATE_WONT;
844 break;
845 case 0xfb:
846 t_con->state = TELNET_STATE_WILL;
847 break;
848 }
849 break;
850 case TELNET_STATE_SB:
851 break;
852 case TELNET_STATE_SE:
853 break;
854 case TELNET_STATE_WILL:
855 case TELNET_STATE_WONT:
856 case TELNET_STATE_DO:
857 case TELNET_STATE_DONT:
858 t_con->state = TELNET_STATE_DATA;
859 break;
860 case TELNET_STATE_ESCAPE:
861 if (t_con->last_escape == '[') {
862 if (*buf_p == 'D') { /* cursor left */
863 telnet_move_cursor(connection, t_con->line_cursor - 1);
864 t_con->state = TELNET_STATE_DATA;
865 } else if (*buf_p == 'C') { /* cursor right */
866 telnet_move_cursor(connection, t_con->line_cursor + 1);
867 t_con->state = TELNET_STATE_DATA;
868 } else if (*buf_p == 'A') { /* cursor up */
869 telnet_history_up(connection);
870 } else if (*buf_p == 'B') { /* cursor down */
871 telnet_history_down(connection);
872 } else if (*buf_p == 'F') { /* end key */
873 telnet_move_cursor(connection, t_con->line_size);
874 t_con->state = TELNET_STATE_DATA;
875 } else if (*buf_p == 'H') { /* home key */
876 telnet_move_cursor(connection, 0);
877 t_con->state = TELNET_STATE_DATA;
878 } else if (*buf_p == '3') {
879 t_con->last_escape = *buf_p;
880 } else {
881 t_con->state = TELNET_STATE_DATA;
882 }
883 } else if (t_con->last_escape == '3') {
884 /* Remove character */
885 if (*buf_p == '~') {
886 telnet_remove_character(connection);
887 t_con->state = TELNET_STATE_DATA;
888 } else
889 t_con->state = TELNET_STATE_DATA;
890 } else if (t_con->last_escape == '\x00') {
891 if (*buf_p == '[')
892 t_con->last_escape = *buf_p;
893 else
894 t_con->state = TELNET_STATE_DATA;
895 } else {
896 LOG_ERROR("BUG: unexpected value in t_con->last_escape");
897 t_con->state = TELNET_STATE_DATA;
898 }
899
900 break;
901 default:
902 LOG_ERROR("unknown telnet state");
903 return ERROR_FAIL;
904 }
905
906 bytes_read--;
907 buf_p++;
908 }
909
910 return ERROR_OK;
911 }
912
913 static int telnet_connection_closed(struct connection *connection)
914 {
915 struct telnet_connection *t_con = connection->priv;
916 int i;
917
918 log_remove_callback(telnet_log_callback, connection);
919
920 free(t_con->prompt);
921 t_con->prompt = NULL;
922
923 /* save telnet history */
924 telnet_save_history(t_con);
925
926 for (i = 0; i < TELNET_LINE_HISTORY_SIZE; i++) {
927 free(t_con->history[i]);
928 t_con->history[i] = NULL;
929 }
930
931 /* if this connection registered a debug-message receiver delete it */
932 delete_debug_msg_receiver(connection->cmd_ctx, NULL);
933
934 free(connection->priv);
935 connection->priv = NULL;
936
937 return ERROR_OK;
938 }
939
940 static const struct service_driver telnet_service_driver = {
941 .name = "telnet",
942 .new_connection_during_keep_alive_handler = NULL,
943 .new_connection_handler = telnet_new_connection,
944 .input_handler = telnet_input,
945 .connection_closed_handler = telnet_connection_closed,
946 .keep_client_alive_handler = NULL,
947 };
948
949 int telnet_init(char *banner)
950 {
951 if (strcmp(telnet_port, "disabled") == 0) {
952 LOG_INFO("telnet server disabled");
953 return ERROR_OK;
954 }
955
956 struct telnet_service *telnet_service =
957 malloc(sizeof(struct telnet_service));
958
959 if (!telnet_service) {
960 LOG_ERROR("Failed to allocate telnet service.");
961 return ERROR_FAIL;
962 }
963
964 telnet_service->banner = banner;
965
966 int ret = add_service(&telnet_service_driver, telnet_port, CONNECTION_LIMIT_UNLIMITED,
967 telnet_service);
968
969 if (ret != ERROR_OK) {
970 free(telnet_service);
971 return ret;
972 }
973
974 return ERROR_OK;
975 }
976
977 /* daemon configuration command telnet_port */
978 COMMAND_HANDLER(handle_telnet_port_command)
979 {
980 return CALL_COMMAND_HANDLER(server_pipe_command, &telnet_port);
981 }
982
983 COMMAND_HANDLER(handle_exit_command)
984 {
985 return ERROR_COMMAND_CLOSE_CONNECTION;
986 }
987
988 static const struct command_registration telnet_command_handlers[] = {
989 {
990 .name = "exit",
991 .handler = handle_exit_command,
992 .mode = COMMAND_EXEC,
993 .usage = "",
994 .help = "exit telnet session",
995 },
996 {
997 .name = "telnet_port",
998 .handler = handle_telnet_port_command,
999 .mode = COMMAND_CONFIG,
1000 .help = "Specify port on which to listen "
1001 "for incoming telnet connections. "
1002 "Read help on 'gdb_port'.",
1003 .usage = "[port_num]",
1004 },
1005 COMMAND_REGISTRATION_DONE
1006 };
1007
1008 int telnet_register_commands(struct command_context *cmd_ctx)
1009 {
1010 telnet_port = strdup("4444");
1011 return register_commands(cmd_ctx, NULL, telnet_command_handlers);
1012 }
1013
1014 void telnet_service_free(void)
1015 {
1016 free(telnet_port);
1017 }

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)