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

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)