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