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)
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)
530 
531  return ERROR_OK;
532 }
533 
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 
552 {
553  struct telnet_connection *t_con = connection->priv;
554 
555  /* print '^C' at line end, and display a new command prompt */
557  telnet_write(connection, "^C\n\r", 4);
558  t_con->line_cursor = 0;
559  t_con->line_size = 0;
561 }
562 
564 {
565  struct telnet_connection *t_con = connection->priv;
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) {
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))
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 
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 
758 
759  if (bytes_read == 0)
761  else if (bytes_read == -1) {
762  LOG_ERROR("error during read: %s", strerror(errno));
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 */
799  } else if (*buf_p == 0x15) { /* clear line */
801  } else if (*buf_p == CTRL('B')) { /* cursor left */
803  t_con->state = TELNET_STATE_DATA;
804  } else if (*buf_p == CTRL('C')) { /* interrupt */
806  } else if (*buf_p == CTRL('F')) { /* cursor right */
808  t_con->state = TELNET_STATE_DATA;
809  } else if (*buf_p == CTRL('P')) { /* cursor up */
811  } else if (*buf_p == CTRL('N')) { /* cursor down */
813  } else if (*buf_p == CTRL('A')) { /* move the cursor to the beginning of the line */
815  } else if (*buf_p == CTRL('E')) { /* move the cursor to the end of the line */
817  } else if (*buf_p == CTRL('K')) { /* kill line to end */
819  } else if (*buf_p == '\t') {
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 */
857  t_con->state = TELNET_STATE_DATA;
858  } else if (*buf_p == 'C') { /* cursor right */
860  t_con->state = TELNET_STATE_DATA;
861  } else if (*buf_p == 'A') { /* cursor up */
863  } else if (*buf_p == 'B') { /* cursor down */
865  } else if (*buf_p == 'F') { /* end key */
867  t_con->state = TELNET_STATE_DATA;
868  } else if (*buf_p == 'H') { /* home key */
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 == '~') {
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 
907 {
908  struct telnet_connection *t_con = connection->priv;
909  int i;
910 
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 */
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 
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 
958 
961 
962  if (ret != ERROR_OK) {
963  free(telnet_service);
964  return ret;
965  }
966 
967  return ERROR_OK;
968 }
969 
970 COMMAND_HANDLER(handle_telnet_port_command)
971 {
972  return CALL_COMMAND_HANDLER(server_pipe_command, &telnet_port);
973 }
974 
975 COMMAND_HANDLER(handle_exit_command)
976 {
978 }
979 
980 static const struct command_registration telnet_subcommand_handlers[] = {
981  {
982  .name = "port",
983  .handler = handle_telnet_port_command,
984  .mode = COMMAND_CONFIG,
985  .help = "Specify port on which to listen "
986  "for incoming telnet connections. "
987  "Read help on 'gdb port'.",
988  .usage = "[port_num]",
989  },
991 };
992 
993 static const struct command_registration telnet_command_handlers[] = {
994  {
995  .name = "exit",
996  .handler = handle_exit_command,
997  .mode = COMMAND_ANY,
998  .usage = "",
999  .help = "exit telnet session",
1000  },
1001  {
1002  .name = "telnet",
1003  .chain = telnet_subcommand_handlers,
1004  .mode = COMMAND_CONFIG,
1005  .help = "telnet commands",
1006  .usage = "",
1007  },
1009 };
1010 
1012 {
1013  telnet_port = strdup("4444");
1015 }
1016 
1018 {
1019  free(telnet_port);
1020 }
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:51
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:56
void command_set_output_handler(struct command_context *context, command_output_handler_t output_handler, void *priv)
Definition: command.c:628
int command_run_line(struct command_context *context, char *line)
Definition: command.c:547
#define CALL_COMMAND_HANDLER(name, extra ...)
Use this to macro to call a command helper (or a nested handler).
Definition: command.h:118
#define ERROR_COMMAND_CLOSE_CONNECTION
Definition: command.h:401
#define COMMAND_REGISTRATION_DONE
Use this as the last entry in an array of command_registration records.
Definition: command.h:253
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:274
@ COMMAND_CONFIG
Definition: command.h:41
@ COMMAND_ANY
Definition: command.h:42
char * get_home_dir(const char *append_path)
static struct esp_usb_jtag * priv
Definition: esp_usb_jtag.c:219
#define LIST_HEAD(name)
Definition: list.h:49
static int list_is_singular(const struct list_head *head)
Definition: list.h:284
static void list_add_tail(struct list_head *new, struct list_head *head)
Definition: list.h:199
static int list_empty(const struct list_head *head)
Definition: list.h:60
#define list_for_each_entry_safe(p, n, h, field)
Definition: list.h:155
#define list_for_each_entry(p, h, field)
Definition: list.h:151
int log_remove_callback(log_callback_fn fn, void *priv)
Definition: log.c:322
int log_add_callback(log_callback_fn fn, void *priv)
Definition: log.c:297
char * alloc_printf(const char *format,...)
Definition: log.c:364
#define ERROR_FAIL
Definition: log.h:170
#define LOG_ERROR(expr ...)
Definition: log.h:132
#define LOG_INFO(expr ...)
Definition: log.h:126
#define LOG_DEBUG(expr ...)
Definition: log.h:109
#define ERROR_OK
Definition: log.h:164
static uint32_t lh(unsigned int rd, unsigned int base, uint16_t offset) __attribute__((unused))
Definition: opcodes.h:117
#define MIN(a, b)
Definition: replacements.h:22
int connection_write(struct connection *connection, const void *data, int len)
Definition: server.c:732
int connection_read(struct connection *connection, void *data, int len)
Definition: server.c:744
int add_service(const struct service_driver *driver, const char *port, int max_connections, void *priv)
Definition: server.c:198
#define CONNECTION_LIMIT_UNLIMITED
Definition: server.h:34
#define ERROR_SERVER_REMOTE_CLOSED
Definition: server.h:119
Jim_Interp * interp
Definition: command.h:53
void * output_handler_priv
Definition: command.h:65
const char * name
Definition: command.h:235
enum command_mode mode
Definition: command.h:238
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:40
const char * name
the name of the server
Definition: server.h:49
void * priv
Definition: server.h:81
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)
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