telnet_server: fix scan-build warning
[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 /* check user command length */
619 if (t_con->line_cursor < usr_cmd_pos) {
620 telnet_bell(connection);
621 return;
622 }
623 size_t usr_cmd_len = t_con->line_cursor - usr_cmd_pos;
624
625 /* optimize multiple spaces in the user command,
626 * because info commands does not tolerate multiple spaces */
627 size_t optimized_spaces = 0;
628 char query[usr_cmd_len + 1];
629 for (size_t i = 0; i < usr_cmd_len; i++) {
630 if ((i < usr_cmd_len - 1) && isspace(t_con->line[usr_cmd_pos + i])
631 && isspace(t_con->line[usr_cmd_pos + i + 1])) {
632 optimized_spaces++;
633 continue;
634 }
635
636 query[i - optimized_spaces] = t_con->line[usr_cmd_pos + i];
637 }
638
639 usr_cmd_len -= optimized_spaces;
640 query[usr_cmd_len] = '\0';
641
642 /* filter commands */
643 char *query_cmd;
644
645 if (is_variable_auto_completion)
646 query_cmd = alloc_printf("lsort [info vars {%s*}]", query);
647 else
648 query_cmd = alloc_printf("_telnet_autocomplete_helper {%s*}", query);
649
650 if (!query_cmd) {
651 LOG_ERROR("Out of memory");
652 return;
653 }
654
655 int retval = Jim_EvalSource(command_context->interp, __FILE__, __LINE__, query_cmd);
656 free(query_cmd);
657 if (retval != JIM_OK)
658 return;
659
660 Jim_Obj *list = Jim_GetResult(command_context->interp);
661 Jim_IncrRefCount(list);
662
663 /* common prefix length of the matched commands */
664 size_t common_len = 0;
665 char *first_match = NULL; /* used to compute the common prefix length */
666
667 int len = Jim_ListLength(command_context->interp, list);
668 for (int i = 0; i < len; i++) {
669 Jim_Obj *elem = Jim_ListGetIndex(command_context->interp, list, i);
670 Jim_IncrRefCount(elem);
671
672 char *name = (char *)Jim_GetString(elem, NULL);
673
674 /* validate the command */
675 bool ignore_cmd = false;
676 if (!is_variable_auto_completion) {
677 Jim_Cmd *jim_cmd = Jim_GetCommand(command_context->interp, elem, JIM_NONE);
678
679 if (!jim_cmd) {
680 /* Why we are here? Let's ignore it! */
681 ignore_cmd = true;
682 } else if (jimcmd_is_oocd_command(jim_cmd)) {
683 struct command *cmd = jimcmd_privdata(jim_cmd);
684
685 if (cmd && !cmd->handler && !cmd->jim_handler) {
686 /* Initial part of a multi-word command. Ignore it! */
687 ignore_cmd = true;
688 } else if (cmd && cmd->mode == COMMAND_CONFIG) {
689 /* Not executable after config phase. Ignore it! */
690 ignore_cmd = true;
691 }
692 }
693 }
694
695 /* save the command in the prediction list */
696 if (!ignore_cmd) {
697 struct cmd_match *match = calloc(1, sizeof(struct cmd_match));
698 if (!match) {
699 LOG_ERROR("Out of memory");
700 Jim_DecrRefCount(command_context->interp, elem);
701 break; /* break the for loop */
702 }
703
704 if (list_empty(&matches)) {
705 common_len = strlen(name);
706 first_match = name;
707 } else {
708 size_t new_common_len = usr_cmd_len; /* save some loops */
709
710 while (new_common_len < common_len && first_match[new_common_len] == name[new_common_len])
711 new_common_len++;
712
713 common_len = new_common_len;
714 }
715
716 match->cmd = name;
717 list_add_tail(&match->lh, &matches);
718 }
719
720 Jim_DecrRefCount(command_context->interp, elem);
721 }
722 /* end of command filtering */
723
724 /* proceed with auto-completion */
725 if (list_empty(&matches))
726 telnet_bell(connection);
727 else if (common_len == usr_cmd_len && list_is_singular(&matches) && t_con->line_cursor == t_con->line_size)
728 telnet_insert(connection, " ", 1);
729 else if (common_len > usr_cmd_len) {
730 int completion_size = common_len - usr_cmd_len;
731 if (telnet_insert(connection, first_match + usr_cmd_len, completion_size)) {
732 /* in bash this extra space is only added when the cursor in at the end of line */
733 if (list_is_singular(&matches) && t_con->line_cursor == t_con->line_size)
734 telnet_insert(connection, " ", 1);
735 }
736 } else if (!list_is_singular(&matches)) {
737 telnet_write(connection, "\n\r", 2);
738
739 struct cmd_match *match;
740 list_for_each_entry(match, &matches, lh) {
741 telnet_write(connection, match->cmd, strlen(match->cmd));
742 telnet_write(connection, "\n\r", 2);
743 }
744
745 telnet_prompt(connection);
746 telnet_write(connection, t_con->line, t_con->line_size);
747
748 /* restore the terminal visible cursor location */
749 for (size_t i = t_con->line_cursor; i < t_con->line_size; i++)
750 telnet_write(connection, "\b", 1);
751 }
752
753 /* destroy the command_list */
754 struct cmd_match *tmp, *match;
755 list_for_each_entry_safe(match, tmp, &matches, lh)
756 free(match);
757
758 Jim_DecrRefCount(command_context->interp, list);
759 }
760
761 static int telnet_input(struct connection *connection)
762 {
763 int bytes_read;
764 unsigned char buffer[TELNET_BUFFER_SIZE];
765 unsigned char *buf_p;
766 struct telnet_connection *t_con = connection->priv;
767
768 bytes_read = connection_read(connection, buffer, TELNET_BUFFER_SIZE);
769
770 if (bytes_read == 0)
771 return ERROR_SERVER_REMOTE_CLOSED;
772 else if (bytes_read == -1) {
773 LOG_ERROR("error during read: %s", strerror(errno));
774 return ERROR_SERVER_REMOTE_CLOSED;
775 }
776
777 buf_p = buffer;
778 while (bytes_read) {
779 switch (t_con->state) {
780 case TELNET_STATE_DATA:
781 if (*buf_p == 0xff) {
782 t_con->state = TELNET_STATE_IAC;
783 } else {
784 if (isprint(*buf_p)) { /* printable character */
785 telnet_insert(connection, buf_p, 1);
786 } else { /* non-printable */
787 if (*buf_p == 0x1b) { /* escape */
788 t_con->state = TELNET_STATE_ESCAPE;
789 t_con->last_escape = '\x00';
790 } else if ((*buf_p == 0xd) || (*buf_p == 0xa)) { /* CR/LF */
791 int retval;
792
793 /* skip over combinations with CR/LF and NUL characters */
794 if ((bytes_read > 1) && ((*(buf_p + 1) == 0xa) ||
795 (*(buf_p + 1) == 0xd))) {
796 buf_p++;
797 bytes_read--;
798 }
799 if ((bytes_read > 1) && (*(buf_p + 1) == 0)) {
800 buf_p++;
801 bytes_read--;
802 }
803 t_con->line[t_con->line_size] = 0;
804
805 retval = telnet_exec_line(connection);
806 if (retval != ERROR_OK)
807 return retval;
808 } else if ((*buf_p == 0x7f) || (*buf_p == 0x8)) { /* delete character */
809 telnet_delete_character(connection);
810 } else if (*buf_p == 0x15) { /* clear line */
811 telnet_clear_line(connection, t_con);
812 } else if (*buf_p == CTRL('B')) { /* cursor left */
813 telnet_move_cursor(connection, t_con->line_cursor - 1);
814 t_con->state = TELNET_STATE_DATA;
815 } else if (*buf_p == CTRL('C')) { /* interrupt */
816 telnet_interrupt(connection);
817 } else if (*buf_p == CTRL('F')) { /* cursor right */
818 telnet_move_cursor(connection, t_con->line_cursor + 1);
819 t_con->state = TELNET_STATE_DATA;
820 } else if (*buf_p == CTRL('P')) { /* cursor up */
821 telnet_history_up(connection);
822 } else if (*buf_p == CTRL('N')) { /* cursor down */
823 telnet_history_down(connection);
824 } else if (*buf_p == CTRL('A')) { /* move the cursor to the beginning of the line */
825 telnet_move_cursor(connection, 0);
826 } else if (*buf_p == CTRL('E')) { /* move the cursor to the end of the line */
827 telnet_move_cursor(connection, t_con->line_size);
828 } else if (*buf_p == CTRL('K')) { /* kill line to end */
829 telnet_cut_line_to_end(connection);
830 } else if (*buf_p == '\t') {
831 telnet_auto_complete(connection);
832 } else {
833 LOG_DEBUG("unhandled nonprintable: %2.2x", *buf_p);
834 }
835 }
836 }
837 break;
838 case TELNET_STATE_IAC:
839 switch (*buf_p) {
840 case 0xfe:
841 t_con->state = TELNET_STATE_DONT;
842 break;
843 case 0xfd:
844 t_con->state = TELNET_STATE_DO;
845 break;
846 case 0xfc:
847 t_con->state = TELNET_STATE_WONT;
848 break;
849 case 0xfb:
850 t_con->state = TELNET_STATE_WILL;
851 break;
852 }
853 break;
854 case TELNET_STATE_SB:
855 break;
856 case TELNET_STATE_SE:
857 break;
858 case TELNET_STATE_WILL:
859 case TELNET_STATE_WONT:
860 case TELNET_STATE_DO:
861 case TELNET_STATE_DONT:
862 t_con->state = TELNET_STATE_DATA;
863 break;
864 case TELNET_STATE_ESCAPE:
865 if (t_con->last_escape == '[') {
866 if (*buf_p == 'D') { /* cursor left */
867 telnet_move_cursor(connection, t_con->line_cursor - 1);
868 t_con->state = TELNET_STATE_DATA;
869 } else if (*buf_p == 'C') { /* cursor right */
870 telnet_move_cursor(connection, t_con->line_cursor + 1);
871 t_con->state = TELNET_STATE_DATA;
872 } else if (*buf_p == 'A') { /* cursor up */
873 telnet_history_up(connection);
874 } else if (*buf_p == 'B') { /* cursor down */
875 telnet_history_down(connection);
876 } else if (*buf_p == 'F') { /* end key */
877 telnet_move_cursor(connection, t_con->line_size);
878 t_con->state = TELNET_STATE_DATA;
879 } else if (*buf_p == 'H') { /* home key */
880 telnet_move_cursor(connection, 0);
881 t_con->state = TELNET_STATE_DATA;
882 } else if (*buf_p == '3') {
883 t_con->last_escape = *buf_p;
884 } else {
885 t_con->state = TELNET_STATE_DATA;
886 }
887 } else if (t_con->last_escape == '3') {
888 /* Remove character */
889 if (*buf_p == '~') {
890 telnet_remove_character(connection);
891 t_con->state = TELNET_STATE_DATA;
892 } else
893 t_con->state = TELNET_STATE_DATA;
894 } else if (t_con->last_escape == '\x00') {
895 if (*buf_p == '[')
896 t_con->last_escape = *buf_p;
897 else
898 t_con->state = TELNET_STATE_DATA;
899 } else {
900 LOG_ERROR("BUG: unexpected value in t_con->last_escape");
901 t_con->state = TELNET_STATE_DATA;
902 }
903
904 break;
905 default:
906 LOG_ERROR("unknown telnet state");
907 return ERROR_FAIL;
908 }
909
910 bytes_read--;
911 buf_p++;
912 }
913
914 return ERROR_OK;
915 }
916
917 static int telnet_connection_closed(struct connection *connection)
918 {
919 struct telnet_connection *t_con = connection->priv;
920 int i;
921
922 log_remove_callback(telnet_log_callback, connection);
923
924 free(t_con->prompt);
925 t_con->prompt = NULL;
926
927 /* save telnet history */
928 telnet_save_history(t_con);
929
930 for (i = 0; i < TELNET_LINE_HISTORY_SIZE; i++) {
931 free(t_con->history[i]);
932 t_con->history[i] = NULL;
933 }
934
935 /* if this connection registered a debug-message receiver delete it */
936 delete_debug_msg_receiver(connection->cmd_ctx, NULL);
937
938 free(connection->priv);
939 connection->priv = NULL;
940
941 return ERROR_OK;
942 }
943
944 static const struct service_driver telnet_service_driver = {
945 .name = "telnet",
946 .new_connection_during_keep_alive_handler = NULL,
947 .new_connection_handler = telnet_new_connection,
948 .input_handler = telnet_input,
949 .connection_closed_handler = telnet_connection_closed,
950 .keep_client_alive_handler = NULL,
951 };
952
953 int telnet_init(char *banner)
954 {
955 if (strcmp(telnet_port, "disabled") == 0) {
956 LOG_INFO("telnet server disabled");
957 return ERROR_OK;
958 }
959
960 struct telnet_service *telnet_service =
961 malloc(sizeof(struct telnet_service));
962
963 if (!telnet_service) {
964 LOG_ERROR("Failed to allocate telnet service.");
965 return ERROR_FAIL;
966 }
967
968 telnet_service->banner = banner;
969
970 int ret = add_service(&telnet_service_driver, telnet_port, CONNECTION_LIMIT_UNLIMITED,
971 telnet_service);
972
973 if (ret != ERROR_OK) {
974 free(telnet_service);
975 return ret;
976 }
977
978 return ERROR_OK;
979 }
980
981 /* daemon configuration command telnet_port */
982 COMMAND_HANDLER(handle_telnet_port_command)
983 {
984 return CALL_COMMAND_HANDLER(server_pipe_command, &telnet_port);
985 }
986
987 COMMAND_HANDLER(handle_exit_command)
988 {
989 return ERROR_COMMAND_CLOSE_CONNECTION;
990 }
991
992 static const struct command_registration telnet_command_handlers[] = {
993 {
994 .name = "exit",
995 .handler = handle_exit_command,
996 .mode = COMMAND_EXEC,
997 .usage = "",
998 .help = "exit telnet session",
999 },
1000 {
1001 .name = "telnet_port",
1002 .handler = handle_telnet_port_command,
1003 .mode = COMMAND_CONFIG,
1004 .help = "Specify port on which to listen "
1005 "for incoming telnet connections. "
1006 "Read help on 'gdb_port'.",
1007 .usage = "[port_num]",
1008 },
1009 COMMAND_REGISTRATION_DONE
1010 };
1011
1012 int telnet_register_commands(struct command_context *cmd_ctx)
1013 {
1014 telnet_port = strdup("4444");
1015 return register_commands(cmd_ctx, NULL, telnet_command_handlers);
1016 }
1017
1018 void telnet_service_free(void)
1019 {
1020 free(telnet_port);
1021 }

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)