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

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)