OpenOCD
telnet_server.c
Go to the documentation of this file.
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)
44 
45  if (connection_write(connection, data, len) == len)
46  return ERROR_OK;
47  t_con->closed = true;
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 
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 {
92 
93  return telnet_outputline(connection, line);
94 }
95 
96 static void telnet_log_callback(void *priv, const char *file, unsigned int 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. */
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;
138  int i = 0;
139 
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;
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 
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 
188  i = t_con->current_history + 1;
190 
191  while (!t_con->history[i] && num > 0) {
192  i++;
194  num--;
195  }
196 
197  if (num > 0) {
198  for (; num > 0; num--) {
199  fprintf(histfp, "%s\n", t_con->history[i]);
200  i++;
202  }
203  }
204  fclose(histfp);
205  }
206 
207  free(history);
208 }
209 
211 {
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 
223 
224  /* initialize telnet connection information */
225  telnet_connection->prompt = strdup("> ");
228 
229  /* output goes through telnet connection */
231 
232  /* negotiate telnet options */
234 
235  /* print connection banner */
236  if (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);
244 
246 
248 
249  return ERROR_OK;
250 }
251 
253  struct telnet_connection *t_con)
254 {
255  /* move to end of line */
256  if (t_con->line_cursor < t_con->line_size)
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]) {
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 
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 :
291  telnet_history_go(connection, last_history);
292 }
293 
295 {
296  struct telnet_connection *t_con = connection->priv;
297  size_t next_history;
298 
301 }
302 
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) ?
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 
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  */
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)) {
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 
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 
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 
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 
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 
490 {
491  struct telnet_connection *t_con = connection->priv;
493  int retval;
494 
495  telnet_write(connection, "\r\n\x00", 3);
496 
497  if (strcmp(t_con->line, "history") == 0) {
499 
500  if (retval != ERROR_OK)
501  return retval;
502 
503  return ERROR_OK;
504  }
505 
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  /* "shutdown" or "exit" executed. */
524 
525  /* the prompt is always placed at the line beginning */
526  telnet_write(connection, "\r", 1);
527 
528  retval = telnet_prompt(connection);
529  if (retval == ERROR_SERVER_REMOTE_CLOSED)
531 
532  return ERROR_OK;
533 }
534 
536 {
537  struct telnet_connection *t_con = connection->priv;
538 
539  /* FIXME: currently this function does not save to clipboard */
540 
541  if (t_con->line_cursor < t_con->line_size) {
542  /* overwrite with space, until end of line, move back */
543  for (size_t i = t_con->line_cursor; i < t_con->line_size; i++)
544  telnet_write(connection, " ", 1);
545  for (size_t i = t_con->line_cursor; i < t_con->line_size; i++)
546  telnet_write(connection, "\b", 1);
547  t_con->line[t_con->line_cursor] = '\0';
548  t_con->line_size = t_con->line_cursor;
549  }
550 }
551 
553 {
554  struct telnet_connection *t_con = connection->priv;
555 
556  /* print '^C' at line end, and display a new command prompt */
558  telnet_write(connection, "^C\n\r", 4);
559  t_con->line_cursor = 0;
560  t_con->line_size = 0;
562 }
563 
565 {
566  struct telnet_connection *t_con = connection->priv;
568 
569  struct cmd_match {
570  char *cmd;
571  struct list_head lh;
572  };
573 
574  OOCD_LIST_HEAD(matches);
575 
576  /* - user command sequence, either at line beginning
577  * or we start over after these characters ';', '[', '{'
578  * - user variable sequence, start after the character '$'
579  * and do not contain white spaces */
580  bool is_variable_auto_completion = false;
581  bool have_spaces = false;
582  size_t seq_start = (t_con->line_cursor == 0) ? 0 : (t_con->line_cursor - 1);
583  while (1) {
584  char c = t_con->line[seq_start];
585 
586  if (c == ';' || c == '[' || c == '{') {
587  seq_start++;
588  break;
589  } else if (c == ' ') {
590  have_spaces = true;
591  } else if (c == '$' && !have_spaces) {
592  is_variable_auto_completion = true;
593  seq_start++;
594  break;
595  }
596 
597  if (seq_start == 0)
598  break;
599 
600  seq_start--;
601  }
602 
603  /* user command position in the line, ignore leading spaces */
604  size_t usr_cmd_pos = seq_start;
605  while ((usr_cmd_pos < t_con->line_cursor) &&
606  isspace((unsigned char)t_con->line[usr_cmd_pos]))
607  usr_cmd_pos++;
608 
609  /* check user command length */
610  if (t_con->line_cursor < usr_cmd_pos) {
612  return;
613  }
614  size_t usr_cmd_len = t_con->line_cursor - usr_cmd_pos;
615 
616  /* optimize multiple spaces in the user command,
617  * because info commands does not tolerate multiple spaces */
618  size_t optimized_spaces = 0;
619  char query[usr_cmd_len + 1];
620 
621  for (size_t i = 0; i < usr_cmd_len; i++) {
622  if ((i < usr_cmd_len - 1) && isspace((unsigned char)t_con->line[usr_cmd_pos + i])
623  && isspace((unsigned char)t_con->line[usr_cmd_pos + i + 1])) {
624  optimized_spaces++;
625  continue;
626  }
627 
628  query[i - optimized_spaces] = t_con->line[usr_cmd_pos + i];
629  }
630 
631  usr_cmd_len -= optimized_spaces;
632  query[usr_cmd_len] = '\0';
633 
634  /* filter commands */
635  char *query_cmd;
636 
637  if (is_variable_auto_completion)
638  query_cmd = alloc_printf("lsort [info vars {%s*}]", query);
639  else
640  query_cmd = alloc_printf("_telnet_autocomplete_helper {%s*}", query);
641 
642  if (!query_cmd) {
643  LOG_ERROR("Out of memory");
644  return;
645  }
646 
647  int retval = Jim_EvalSource(command_context->interp, __FILE__, __LINE__, query_cmd);
648  free(query_cmd);
649  if (retval != JIM_OK)
650  return;
651 
652  Jim_Obj *list = Jim_GetResult(command_context->interp);
653  Jim_IncrRefCount(list);
654 
655  /* common prefix length of the matched commands */
656  size_t common_len = 0;
657  char *first_match = NULL; /* used to compute the common prefix length */
658 
659  int len = Jim_ListLength(command_context->interp, list);
660  for (int i = 0; i < len; i++) {
661  Jim_Obj *elem = Jim_ListGetIndex(command_context->interp, list, i);
662  Jim_IncrRefCount(elem);
663 
664  char *name = (char *)Jim_GetString(elem, NULL);
665 
666  /* validate the command */
667  bool ignore_cmd = false;
668  if (!is_variable_auto_completion) {
669  Jim_Cmd *jim_cmd = Jim_GetCommand(command_context->interp, elem, JIM_NONE);
670 
671  if (!jim_cmd) {
672  /* Why we are here? Let's ignore it! */
673  ignore_cmd = true;
674  } else if (jimcmd_is_oocd_command(jim_cmd)) {
675  struct command *cmd = jimcmd_privdata(jim_cmd);
676 
677  if (cmd && !cmd->handler) {
678  /* Initial part of a multi-word command. Ignore it! */
679  ignore_cmd = true;
680  } else if (cmd && cmd->mode == COMMAND_CONFIG) {
681  /* Not executable after config phase. Ignore it! */
682  ignore_cmd = true;
683  }
684  }
685  }
686 
687  /* save the command in the prediction list */
688  if (!ignore_cmd) {
689  struct cmd_match *match = calloc(1, sizeof(struct cmd_match));
690  if (!match) {
691  LOG_ERROR("Out of memory");
692  Jim_DecrRefCount(command_context->interp, elem);
693  break; /* break the for loop */
694  }
695 
696  if (list_empty(&matches)) {
697  common_len = strlen(name);
698  first_match = name;
699  } else {
700  size_t new_common_len = usr_cmd_len; /* save some loops */
701 
702  while (new_common_len < common_len && first_match[new_common_len] == name[new_common_len])
703  new_common_len++;
704 
705  common_len = new_common_len;
706  }
707 
708  match->cmd = name;
709  list_add_tail(&match->lh, &matches);
710  }
711 
712  Jim_DecrRefCount(command_context->interp, elem);
713  }
714  /* end of command filtering */
715 
716  /* proceed with auto-completion */
717  if (list_empty(&matches))
719  else if (common_len == usr_cmd_len && list_is_singular(&matches) && t_con->line_cursor == t_con->line_size)
720  telnet_insert(connection, " ", 1);
721  else if (common_len > usr_cmd_len) {
722  int completion_size = common_len - usr_cmd_len;
723  if (telnet_insert(connection, first_match + usr_cmd_len, completion_size)) {
724  /* in bash this extra space is only added when the cursor in at the end of line */
725  if (list_is_singular(&matches) && t_con->line_cursor == t_con->line_size)
726  telnet_insert(connection, " ", 1);
727  }
728  } else if (!list_is_singular(&matches)) {
729  telnet_write(connection, "\n\r", 2);
730 
731  struct cmd_match *match;
732  list_for_each_entry(match, &matches, lh) {
733  telnet_write(connection, match->cmd, strlen(match->cmd));
734  telnet_write(connection, "\n\r", 2);
735  }
736 
738  telnet_write(connection, t_con->line, t_con->line_size);
739 
740  /* restore the terminal visible cursor location */
741  for (size_t i = t_con->line_cursor; i < t_con->line_size; i++)
742  telnet_write(connection, "\b", 1);
743  }
744 
745  /* destroy the command_list */
746  struct cmd_match *tmp, *match;
747  list_for_each_entry_safe(match, tmp, &matches, lh)
748  free(match);
749 
750  Jim_DecrRefCount(command_context->interp, list);
751 }
752 
753 static int telnet_input(struct connection *connection)
754 {
755  int bytes_read;
756  unsigned char buffer[TELNET_BUFFER_SIZE];
757  unsigned char *buf_p;
758  struct telnet_connection *t_con = connection->priv;
759 
761 
762  if (bytes_read == 0)
764  else if (bytes_read == -1) {
765  LOG_ERROR("error during read: %s", strerror(errno));
767  }
768 
769  buf_p = buffer;
770  while (bytes_read) {
771  switch (t_con->state) {
772  case TELNET_STATE_DATA:
773  if (*buf_p == 0xff) {
774  t_con->state = TELNET_STATE_IAC;
775  } else {
776  if (isprint(*buf_p)) { /* printable character */
777  telnet_insert(connection, buf_p, 1);
778  } else { /* non-printable */
779  if (*buf_p == 0x1b) { /* escape */
780  t_con->state = TELNET_STATE_ESCAPE;
781  t_con->last_escape = '\x00';
782  } else if ((*buf_p == 0xd) || (*buf_p == 0xa)) { /* CR/LF */
783  int retval;
784 
785  /* skip over combinations with CR/LF and NUL characters */
786  if ((bytes_read > 1) && ((*(buf_p + 1) == 0xa) ||
787  (*(buf_p + 1) == 0xd))) {
788  buf_p++;
789  bytes_read--;
790  }
791  if ((bytes_read > 1) && (*(buf_p + 1) == 0)) {
792  buf_p++;
793  bytes_read--;
794  }
795  t_con->line[t_con->line_size] = 0;
796 
797  retval = telnet_exec_line(connection);
798  if (retval != ERROR_OK)
799  return retval;
800  } else if ((*buf_p == 0x7f) || (*buf_p == 0x8)) { /* delete character */
802  } else if (*buf_p == 0x15) { /* clear line */
804  } else if (*buf_p == CTRL('B')) { /* cursor left */
806  t_con->state = TELNET_STATE_DATA;
807  } else if (*buf_p == CTRL('C')) { /* interrupt */
809  } else if (*buf_p == CTRL('F')) { /* cursor right */
811  t_con->state = TELNET_STATE_DATA;
812  } else if (*buf_p == CTRL('P')) { /* cursor up */
814  } else if (*buf_p == CTRL('N')) { /* cursor down */
816  } else if (*buf_p == CTRL('A')) { /* move the cursor to the beginning of the line */
818  } else if (*buf_p == CTRL('E')) { /* move the cursor to the end of the line */
820  } else if (*buf_p == CTRL('K')) { /* kill line to end */
822  } else if (*buf_p == '\t') {
824  } else {
825  LOG_DEBUG("unhandled nonprintable: %2.2x", *buf_p);
826  }
827  }
828  }
829  break;
830  case TELNET_STATE_IAC:
831  switch (*buf_p) {
832  case 0xfe:
833  t_con->state = TELNET_STATE_DONT;
834  break;
835  case 0xfd:
836  t_con->state = TELNET_STATE_DO;
837  break;
838  case 0xfc:
839  t_con->state = TELNET_STATE_WONT;
840  break;
841  case 0xfb:
842  t_con->state = TELNET_STATE_WILL;
843  break;
844  }
845  break;
846  case TELNET_STATE_SB:
847  break;
848  case TELNET_STATE_SE:
849  break;
850  case TELNET_STATE_WILL:
851  case TELNET_STATE_WONT:
852  case TELNET_STATE_DO:
853  case TELNET_STATE_DONT:
854  t_con->state = TELNET_STATE_DATA;
855  break;
856  case TELNET_STATE_ESCAPE:
857  if (t_con->last_escape == '[') {
858  if (*buf_p == 'D') { /* cursor left */
860  t_con->state = TELNET_STATE_DATA;
861  } else if (*buf_p == 'C') { /* cursor right */
863  t_con->state = TELNET_STATE_DATA;
864  } else if (*buf_p == 'A') { /* cursor up */
866  } else if (*buf_p == 'B') { /* cursor down */
868  } else if (*buf_p == 'F') { /* end key */
870  t_con->state = TELNET_STATE_DATA;
871  } else if (*buf_p == 'H') { /* home key */
873  t_con->state = TELNET_STATE_DATA;
874  } else if (*buf_p == '3') {
875  t_con->last_escape = *buf_p;
876  } else {
877  t_con->state = TELNET_STATE_DATA;
878  }
879  } else if (t_con->last_escape == '3') {
880  /* Remove character */
881  if (*buf_p == '~') {
883  t_con->state = TELNET_STATE_DATA;
884  } else
885  t_con->state = TELNET_STATE_DATA;
886  } else if (t_con->last_escape == '\x00') {
887  if (*buf_p == '[')
888  t_con->last_escape = *buf_p;
889  else
890  t_con->state = TELNET_STATE_DATA;
891  } else {
892  LOG_ERROR("BUG: unexpected value in t_con->last_escape");
893  t_con->state = TELNET_STATE_DATA;
894  }
895 
896  break;
897  default:
898  LOG_ERROR("unknown telnet state");
899  return ERROR_FAIL;
900  }
901 
902  bytes_read--;
903  buf_p++;
904  }
905 
906  return ERROR_OK;
907 }
908 
910 {
911  struct telnet_connection *t_con = connection->priv;
912  int i;
913 
915 
916  free(t_con->prompt);
917  t_con->prompt = NULL;
918 
919  /* save telnet history */
920  telnet_save_history(t_con);
921 
922  for (i = 0; i < TELNET_LINE_HISTORY_SIZE; i++) {
923  free(t_con->history[i]);
924  t_con->history[i] = NULL;
925  }
926 
927  /* if this connection registered a debug-message receiver delete it */
929 
930  free(connection->priv);
931  connection->priv = NULL;
932 
933  return ERROR_OK;
934 }
935 
936 static const struct service_driver telnet_service_driver = {
937  .name = "telnet",
938  .new_connection_during_keep_alive_handler = NULL,
939  .new_connection_handler = telnet_new_connection,
940  .input_handler = telnet_input,
941  .connection_closed_handler = telnet_connection_closed,
942  .keep_client_alive_handler = NULL,
943 };
944 
945 int telnet_init(char *banner)
946 {
947  if (strcmp(telnet_port, "disabled") == 0) {
948  LOG_INFO("telnet server disabled");
949  return ERROR_OK;
950  }
951 
953  malloc(sizeof(struct telnet_service));
954 
955  if (!telnet_service) {
956  LOG_ERROR("Failed to allocate telnet service.");
957  return ERROR_FAIL;
958  }
959 
961 
964 
965  if (ret != ERROR_OK) {
966  free(telnet_service);
967  return ret;
968  }
969 
970  return ERROR_OK;
971 }
972 
974 {
975  return cmd_ctx->output_handler == telnet_output;
976 }
977 
978 COMMAND_HANDLER(handle_telnet_port_command)
979 {
980  return CALL_COMMAND_HANDLER(server_pipe_command, &telnet_port);
981 }
982 
983 static const struct command_registration telnet_subcommand_handlers[] = {
984  {
985  .name = "port",
986  .handler = handle_telnet_port_command,
987  .mode = COMMAND_CONFIG,
988  .help = "Specify port on which to listen "
989  "for incoming telnet connections. "
990  "Read help on 'gdb port'.",
991  .usage = "[port_num]",
992  },
994 };
995 
996 static const struct command_registration telnet_command_handlers[] = {
997  {
998  .name = "telnet",
1000  .mode = COMMAND_CONFIG,
1001  .help = "telnet commands",
1002  .usage = "",
1003  },
1005 };
1006 
1008 {
1009  telnet_port = strdup("4444");
1011 }
1012 
1014 {
1015  free(telnet_port);
1016 }
const char * name
Definition: armv4_5.c:76
bool jimcmd_is_oocd_command(Jim_Cmd *cmd)
Return true if the command cmd is registered by OpenOCD.
Definition: command.c:56
void * jimcmd_privdata(Jim_Cmd *cmd)
Return the pointer to the command's private data specified during the registration of command cmd .
Definition: command.c:61
void command_set_output_handler(struct command_context *context, command_output_handler_t output_handler, void *priv)
Definition: command.c:568
int command_run_line(struct command_context *context, char *line)
Definition: command.c:497
#define CALL_COMMAND_HANDLER(name, extra ...)
Use this to macro to call a command helper (or a nested handler).
Definition: command.h:123
#define ERROR_COMMAND_CLOSE_CONNECTION
Definition: command.h:404
#define COMMAND_REGISTRATION_DONE
Use this as the last entry in an array of command_registration records.
Definition: command.h:256
static int register_commands(struct command_context *cmd_ctx, const char *cmd_prefix, const struct command_registration *cmds)
Register one or more commands in the specified context, as children of parent (or top-level commends,...
Definition: command.h:277
@ COMMAND_CONFIG
Definition: command.h:41
char * get_home_dir(const char *append_path)
uint64_t buffer
Pointer to data buffer to send over SPI.
Definition: dw-spi-helper.h:0
static struct esp_usb_jtag * priv
Definition: esp_usb_jtag.c:219
#define OOCD_LIST_HEAD(name)
Definition: list.h:50
static int list_is_singular(const struct list_head *head)
Definition: list.h:288
static void list_add_tail(struct list_head *new, struct list_head *head)
Definition: list.h:203
static int list_empty(const struct list_head *head)
Definition: list.h:61
#define list_for_each_entry_safe(p, n, h, field)
Definition: list.h:159
#define list_for_each_entry(p, h, field)
Definition: list.h:155
int log_remove_callback(log_callback_fn fn, void *priv)
Definition: log.c:341
int log_add_callback(log_callback_fn fn, void *priv)
Definition: log.c:316
char * alloc_printf(const char *format,...)
Definition: log.c:386
#define ERROR_FAIL
Definition: log.h:188
#define LOG_ERROR(expr ...)
Definition: log.h:147
#define LOG_INFO(expr ...)
Definition: log.h:141
#define LOG_DEBUG(expr ...)
Definition: log.h:124
#define ERROR_OK
Definition: log.h:182
static uint32_t lh(unsigned int rd, unsigned int base, int16_t offset) __attribute__((unused))
Definition: opcodes.h:172
#define MIN(a, b)
Definition: replacements.h:22
int connection_write(struct connection *connection, const void *data, int len)
Definition: server.c:741
int connection_read(struct connection *connection, void *data, int len)
Definition: server.c:753
int add_service(const struct service_driver *driver, const char *port, int max_connections, void *priv)
Definition: server.c:206
#define CONNECTION_LIMIT_UNLIMITED
Definition: server.h:34
#define ERROR_SERVER_REMOTE_CLOSED
Definition: server.h:121
Jim_Interp * interp
Definition: command.h:53
void * output_handler_priv
Definition: command.h:65
command_output_handler_t output_handler
Definition: command.h:64
const char * name
Definition: command.h:239
enum command_mode mode
Definition: command.h:241
struct command_context * cmd_ctx
Definition: server.h:40
void * priv
Definition: server.h:43
struct service * service
Definition: server.h:41
Definition: list.h:41
const char * name
the name of the server
Definition: server.h:49
void * priv
Definition: server.h:83
char * history[TELNET_LINE_HISTORY_SIZE]
Definition: telnet_server.h:44
char line[TELNET_LINE_MAX_SIZE]
Definition: telnet_server.h:40
enum telnet_states state
Definition: telnet_server.h:39
int delete_debug_msg_receiver(struct command_context *cmd_ctx, struct target *target)
void telnet_service_free(void)
#define TELNET_HISTORY
Definition: telnet_server.c:32
static int telnet_prompt(struct connection *connection)
Definition: telnet_server.c:58
static void telnet_cut_line_to_end(struct connection *connection)
static const struct service_driver telnet_service_driver
static void telnet_auto_complete(struct connection *connection)
static int telnet_bell(struct connection *connection)
Definition: telnet_server.c:52
int telnet_register_commands(struct command_context *cmd_ctx)
static void telnet_load_history(struct telnet_connection *t_con)
int telnet_init(char *banner)
static int telnet_output(struct command_context *cmd_ctx, const char *line)
Definition: telnet_server.c:89
static void telnet_clear_line(struct connection *connection, struct telnet_connection *t_con)
static char * telnet_port
Definition: telnet_server.c:23
#define CTRL(c)
Definition: telnet_server.c:31
static const struct command_registration telnet_subcommand_handlers[]
static int telnet_history_print(struct connection *connection)
COMMAND_HANDLER(handle_telnet_port_command)
static bool telnet_insert(struct connection *connection, const void *data, size_t len)
static void telnet_log_callback(void *priv, const char *file, unsigned int line, const char *function, const char *string)
Definition: telnet_server.c:96
static void telnet_history_up(struct connection *connection)
static int telnet_write(struct connection *connection, const void *data, int len)
Definition: telnet_server.c:38
static bool telnet_can_insert(struct connection *connection, size_t len)
static void telnet_history_add(struct connection *connection)
static void telnet_interrupt(struct connection *connection)
static int telnet_input(struct connection *connection)
static int telnet_exec_line(struct connection *connection)
static void telnet_move_cursor(struct connection *connection, size_t pos)
static void telnet_save_history(struct telnet_connection *t_con)
static void telnet_history_down(struct connection *connection)
bool telnet_is_from_telnet_session(struct command_context *cmd_ctx)
static void telnet_history_go(struct connection *connection, int idx)
static const struct command_registration telnet_command_handlers[]
static void telnet_remove_character(struct connection *connection)
static int telnet_new_connection(struct connection *connection)
static char * negotiate
Definition: telnet_server.c:25
static int telnet_connection_closed(struct connection *connection)
static int telnet_outputline(struct connection *connection, const char *line)
Definition: telnet_server.c:65
static void telnet_delete_character(struct connection *connection)
#define TELNET_LINE_HISTORY_SIZE
Definition: telnet_server.h:21
#define TELNET_BUFFER_SIZE
Definition: telnet_server.h:19
@ TELNET_STATE_DO
Definition: telnet_server.h:31
@ TELNET_STATE_SB
Definition: telnet_server.h:27
@ TELNET_STATE_ESCAPE
Definition: telnet_server.h:33
@ TELNET_STATE_DONT
Definition: telnet_server.h:32
@ TELNET_STATE_WILL
Definition: telnet_server.h:29
@ TELNET_STATE_IAC
Definition: telnet_server.h:26
@ TELNET_STATE_WONT
Definition: telnet_server.h:30
@ TELNET_STATE_DATA
Definition: telnet_server.h:25
@ TELNET_STATE_SE
Definition: telnet_server.h:28
#define TELNET_LINE_MAX_SIZE
Definition: telnet_server.h:22
#define NULL
Definition: usb.h:16
uint8_t cmd
Definition: vdebug.c:1