- added patch to Improving progress/error output for telnet & GDB monitor
[openocd.git] / src / server / telnet_server.c
1 /***************************************************************************
2 * Copyright (C) 2005 by Dominic Rath *
3 * Dominic.Rath@gmx.de *
4 * *
5 * This program is free software; you can redistribute it and/or modify *
6 * it under the terms of the GNU General Public License as published by *
7 * the Free Software Foundation; either version 2 of the License, or *
8 * (at your option) any later version. *
9 * *
10 * This program is distributed in the hope that it will be useful, *
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
13 * GNU General Public License for more details. *
14 * *
15 * You should have received a copy of the GNU General Public License *
16 * along with this program; if not, write to the *
17 * Free Software Foundation, Inc., *
18 * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
19 ***************************************************************************/
20 #ifdef HAVE_CONFIG_H
21 #include "config.h"
22 #endif
23
24 #include "replacements.h"
25
26 #include "telnet_server.h"
27
28 #include "server.h"
29 #include "log.h"
30 #include "command.h"
31 #include "target.h"
32 #include "target_request.h"
33
34 #include <stdlib.h>
35 #include <unistd.h>
36 #include <errno.h>
37 #include <string.h>
38 #include <ctype.h>
39
40 static unsigned short telnet_port = 0;
41
42 int handle_exit_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc);
43 int handle_telnet_port_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc);
44
45 static char *negotiate =
46 "\xFF\xFB\x03" /* IAC WILL Suppress Go Ahead */
47 "\xFF\xFB\x01" /* IAC WILL Echo */
48 "\xFF\xFD\x03" /* IAC DO Suppress Go Ahead */
49 "\xFF\xFE\x01"; /* IAC DON'T Echo */
50
51 #define CTRL(c) (c - '@')
52
53 void telnet_prompt(connection_t *connection)
54 {
55 telnet_connection_t *t_con = connection->priv;
56
57 write_socket(connection->fd, t_con->prompt, strlen(t_con->prompt));
58 }
59
60 int telnet_outputline(connection_t *connection, char* line)
61 {
62 write_socket(connection->fd, line, strlen(line));
63 return write_socket(connection->fd, "\r\n\0", 3);
64 }
65
66 int telnet_output(struct command_context_s *cmd_ctx, char* line)
67 {
68 connection_t *connection = cmd_ctx->output_handler_priv;
69
70 write_socket(connection->fd, line, strlen(line));
71 write_socket(connection->fd, "\r\n\0", 3);
72
73 return ERROR_OK;
74 }
75
76 void telnet_log_callback(void *privData, const char *file, int line,
77 const char *function, const char *format, va_list args)
78 {
79 connection_t *connection=(connection_t *)privData;
80 char *t=allocPrintf(format, args);
81 if (t==NULL)
82 return;
83
84 telnet_outputline(connection, t);
85
86 free(t);
87 }
88
89 int telnet_target_callback_event_handler(struct target_s *target, enum target_event event, void *priv)
90 {
91 struct command_context_s *cmd_ctx = priv;
92 char buffer[512];
93
94 switch (event)
95 {
96 case TARGET_EVENT_HALTED:
97 command_print(cmd_ctx, "Target %i halted", get_num_by_target(target));
98 target->type->arch_state(target, buffer, 512);
99 buffer[511] = 0;
100 command_print(cmd_ctx, "%s", buffer);
101 break;
102 case TARGET_EVENT_RESUMED:
103 command_print(cmd_ctx, "Target %i resumed", get_num_by_target(target));
104 break;
105 default:
106 break;
107 }
108
109 return ERROR_OK;
110 }
111
112 int telnet_new_connection(connection_t *connection)
113 {
114 telnet_connection_t *telnet_connection = malloc(sizeof(telnet_connection_t));
115 telnet_service_t *telnet_service = connection->service->priv;
116 int i;
117
118 connection->priv = telnet_connection;
119
120 /* initialize telnet connection information */
121 telnet_connection->line_size = 0;
122 telnet_connection->line_cursor = 0;
123 telnet_connection->option_size = 0;
124 telnet_connection->prompt = strdup("> ");
125 telnet_connection->state = TELNET_STATE_DATA;
126
127 /* output goes through telnet connection */
128 command_set_output_handler(connection->cmd_ctx, telnet_output, connection);
129
130 /* negotiate telnet options */
131 write_socket(connection->fd, negotiate, strlen(negotiate));
132
133 /* print connection banner */
134 if (telnet_service->banner)
135 {
136 write_socket(connection->fd, telnet_service->banner, strlen(telnet_service->banner));
137 write_socket(connection->fd, "\r\n\0", 3);
138 }
139
140 telnet_prompt(connection);
141
142 /* initialize history */
143 for (i = 0; i < TELNET_LINE_HISTORY_SIZE; i++)
144 {
145 telnet_connection->history[i] = NULL;
146 }
147 telnet_connection->next_history = 0;
148 telnet_connection->current_history = 0;
149
150 target_register_event_callback(telnet_target_callback_event_handler, connection->cmd_ctx);
151
152 return ERROR_OK;
153 }
154
155 void telnet_clear_line(connection_t *connection, telnet_connection_t *t_con)
156 {
157 /* move to end of line */
158 if (t_con->line_cursor < t_con->line_size)
159 {
160 write_socket(connection->fd, t_con->line + t_con->line_cursor, t_con->line_size - t_con->line_cursor);
161 }
162
163 /* backspace, overwrite with space, backspace */
164 while (t_con->line_size > 0)
165 {
166 write_socket(connection->fd, "\b \b", 3);
167 t_con->line_size--;
168 }
169 t_con->line_cursor = 0;
170 }
171
172 int telnet_input(connection_t *connection)
173 {
174 int bytes_read;
175 char buffer[TELNET_BUFFER_SIZE];
176 char *buf_p;
177 telnet_connection_t *t_con = connection->priv;
178 command_context_t *command_context = connection->cmd_ctx;
179
180 bytes_read = read_socket(connection->fd, buffer, TELNET_BUFFER_SIZE);
181
182 if (bytes_read == 0)
183 return ERROR_SERVER_REMOTE_CLOSED;
184 else if (bytes_read == -1)
185 {
186 ERROR("error during read: %s", strerror(errno));
187 return ERROR_SERVER_REMOTE_CLOSED;
188 }
189
190 buf_p = buffer;
191 while (bytes_read)
192 {
193 switch (t_con->state)
194 {
195 case TELNET_STATE_DATA:
196 if (*buf_p == '\xff')
197 {
198 t_con->state = TELNET_STATE_IAC;
199 }
200 else
201 {
202 if (isprint(*buf_p)) /* printable character */
203 {
204 write_socket(connection->fd, buf_p, 1);
205 if (t_con->line_cursor == t_con->line_size)
206 {
207 t_con->line[t_con->line_size++] = *buf_p;
208 t_con->line_cursor++;
209 }
210 else
211 {
212 int i;
213 memmove(t_con->line + t_con->line_cursor + 1, t_con->line + t_con->line_cursor, t_con->line_size - t_con->line_cursor);
214 t_con->line[t_con->line_cursor++] = *buf_p;
215 t_con->line_size++;
216 write_socket(connection->fd, t_con->line + t_con->line_cursor, t_con->line_size - t_con->line_cursor);
217 for (i = t_con->line_cursor; i < t_con->line_size; i++)
218 {
219 write_socket(connection->fd, "\b", 1);
220 }
221 }
222 }
223 else /* non-printable */
224 {
225 if (*buf_p == 0x1b) /* escape */
226 {
227 t_con->state = TELNET_STATE_ESCAPE;
228 t_con->last_escape = '\x00';
229 }
230 else if ((*buf_p == 0xd) || (*buf_p == 0xa)) /* CR/LF */
231 {
232 int retval;
233
234 /* skip over combinations with CR/LF + NUL */
235 if (((*(buf_p + 1) == 0xa) || (*(buf_p + 1) == 0xd)) && (bytes_read > 1))
236 {
237 buf_p++;
238 bytes_read--;
239 }
240 if ((*(buf_p + 1) == 0) && (bytes_read > 1))
241 {
242 buf_p++;
243 bytes_read--;
244 }
245 t_con->line[t_con->line_size] = 0;
246
247 write_socket(connection->fd, "\r\n\x00", 3);
248
249 if (strcmp(t_con->line, "history") == 0)
250 {
251 int i;
252 for (i = 0; i < TELNET_LINE_HISTORY_SIZE; i++)
253 {
254 if (t_con->history[i])
255 {
256 write_socket(connection->fd, t_con->history[i], strlen(t_con->history[i]));
257 write_socket(connection->fd, "\r\n\x00", 3);
258 }
259 }
260 telnet_prompt(connection);
261 t_con->line_size = 0;
262 t_con->line_cursor = 0;
263 continue;
264 }
265
266
267
268 log_setCallback(telnet_log_callback, connection);
269
270 if ((retval = command_run_line(command_context, t_con->line)) != ERROR_OK)
271 {
272 if (retval == ERROR_COMMAND_CLOSE_CONNECTION)
273 {
274 return ERROR_SERVER_REMOTE_CLOSED;
275 }
276 }
277
278 /* Save only non-blank lines in the history */
279 if (t_con->line_size > 0)
280 {
281 /* if the history slot is already taken, free it */
282 if (t_con->history[t_con->next_history])
283 {
284 free(t_con->history[t_con->next_history]);
285 }
286
287 /* add line to history */
288 t_con->history[t_con->next_history] = strdup(t_con->line);
289
290 /* wrap history at TELNET_LINE_HISTORY_SIZE */
291 t_con->next_history = (t_con->next_history + 1) % TELNET_LINE_HISTORY_SIZE;
292
293 /* current history line starts at the new entry */
294 t_con->current_history = t_con->next_history;
295
296 if (t_con->history[t_con->current_history])
297 {
298 free(t_con->history[t_con->current_history]);
299 }
300 t_con->history[t_con->current_history] = strdup("");
301 }
302
303 /* output prompt after command */
304 telnet_prompt(connection);
305
306 t_con->line_size = 0;
307 t_con->line_cursor = 0;
308 }
309 else if ((*buf_p == 0x7f) || (*buf_p == 0x8)) /* delete character */
310 {
311 if (t_con->line_cursor > 0)
312 {
313 if (t_con->line_cursor != t_con->line_size)
314 {
315 int i;
316 write_socket(connection->fd, "\b", 1);
317 t_con->line_cursor--;
318 t_con->line_size--;
319 memmove(t_con->line + t_con->line_cursor, t_con->line + t_con->line_cursor + 1, t_con->line_size - t_con->line_cursor);
320
321 write_socket(connection->fd, t_con->line + t_con->line_cursor, t_con->line_size - t_con->line_cursor);
322 write_socket(connection->fd, " \b", 2);
323 for (i = t_con->line_cursor; i < t_con->line_size; i++)
324 {
325 write_socket(connection->fd, "\b", 1);
326 }
327 }
328 else
329 {
330 t_con->line_size--;
331 t_con->line_cursor--;
332 /* back space: move the 'printer' head one char back, overwrite with space, move back again */
333 write_socket(connection->fd, "\b \b", 3);
334 }
335 }
336 }
337 else if (*buf_p == 0x15) /* clear line */
338 {
339 telnet_clear_line(connection, t_con);
340 }
341 else if (*buf_p == CTRL('B')) /* cursor left */
342 {
343 if (t_con->line_cursor > 0)
344 {
345 write_socket(connection->fd, "\b", 1);
346 t_con->line_cursor--;
347 }
348 t_con->state = TELNET_STATE_DATA;
349 }
350 else if (*buf_p == CTRL('F')) /* cursor right */
351 {
352 if (t_con->line_cursor < t_con->line_size)
353 {
354 write_socket(connection->fd, t_con->line + t_con->line_cursor++, 1);
355 }
356 t_con->state = TELNET_STATE_DATA;
357 }
358 else
359 {
360 DEBUG("unhandled nonprintable: %2.2x", *buf_p);
361 }
362 }
363 }
364 break;
365 case TELNET_STATE_IAC:
366 switch (*buf_p)
367 {
368 case '\xfe':
369 t_con->state = TELNET_STATE_DONT;
370 break;
371 case '\xfd':
372 t_con->state = TELNET_STATE_DO;
373 break;
374 case '\xfc':
375 t_con->state = TELNET_STATE_WONT;
376 break;
377 case '\xfb':
378 t_con->state = TELNET_STATE_WILL;
379 break;
380 }
381 break;
382 case TELNET_STATE_SB:
383 break;
384 case TELNET_STATE_SE:
385 break;
386 case TELNET_STATE_WILL:
387 case TELNET_STATE_WONT:
388 case TELNET_STATE_DO:
389 case TELNET_STATE_DONT:
390 t_con->state = TELNET_STATE_DATA;
391 break;
392 case TELNET_STATE_ESCAPE:
393 if (t_con->last_escape == '[')
394 {
395 if (*buf_p == 'D') /* cursor left */
396 {
397 if (t_con->line_cursor > 0)
398 {
399 write_socket(connection->fd, "\b", 1);
400 t_con->line_cursor--;
401 }
402 t_con->state = TELNET_STATE_DATA;
403 }
404 else if (*buf_p == 'C') /* cursor right */
405 {
406 if (t_con->line_cursor < t_con->line_size)
407 {
408 write_socket(connection->fd, t_con->line + t_con->line_cursor++, 1);
409 }
410 t_con->state = TELNET_STATE_DATA;
411 }
412 else if (*buf_p == 'A') /* cursor up */
413 {
414 int last_history = (t_con->current_history > 0) ? t_con->current_history - 1 : TELNET_LINE_HISTORY_SIZE-1;
415 if (t_con->history[last_history])
416 {
417 telnet_clear_line(connection, t_con);
418 t_con->line_size = strlen(t_con->history[last_history]);
419 t_con->line_cursor = t_con->line_size;
420 memcpy(t_con->line, t_con->history[last_history], t_con->line_size + 1);
421 write_socket(connection->fd, t_con->line, t_con->line_size);
422 t_con->current_history = last_history;
423 }
424 t_con->state = TELNET_STATE_DATA;
425 }
426 else if (*buf_p == 'B') /* cursor down */
427 {
428 int next_history = (t_con->current_history + 1) % TELNET_LINE_HISTORY_SIZE;
429 if (t_con->history[next_history])
430 {
431 telnet_clear_line(connection, t_con);
432 t_con->line_size = strlen(t_con->history[next_history]);
433 t_con->line_cursor = t_con->line_size;
434 memcpy(t_con->line, t_con->history[next_history], t_con->line_size + 1);
435 write_socket(connection->fd, t_con->line, t_con->line_size);
436 t_con->current_history = next_history;
437 }
438 t_con->state = TELNET_STATE_DATA;
439 }
440 else if (*buf_p == '3')
441 {
442 t_con->last_escape = *buf_p;
443 }
444 else
445 {
446 t_con->state = TELNET_STATE_DATA;
447 }
448 }
449 else if (t_con->last_escape == '3')
450 {
451 /* Remove character */
452 if (*buf_p == '~')
453 {
454 if (t_con->line_cursor < t_con->line_size)
455 {
456 int i;
457 t_con->line_size--;
458 /* remove char from line buffer */
459 memmove(t_con->line + t_con->line_cursor, t_con->line + t_con->line_cursor + 1, t_con->line_size - t_con->line_cursor);
460
461 /* print remainder of buffer */
462 write_socket(connection->fd, t_con->line + t_con->line_cursor, t_con->line_size - t_con->line_cursor);
463 /* overwrite last char with whitespace */
464 write_socket(connection->fd, " \b", 2);
465
466 /* move back to cursor position*/
467 for (i = t_con->line_cursor; i < t_con->line_size; i++)
468 {
469 write_socket(connection->fd, "\b", 1);
470 }
471 }
472
473 t_con->state = TELNET_STATE_DATA;
474 }
475 else
476 {
477 t_con->state = TELNET_STATE_DATA;
478 }
479 }
480 else if (t_con->last_escape == '\x00')
481 {
482 if (*buf_p == '[')
483 {
484 t_con->last_escape = *buf_p;
485 }
486 else
487 {
488 t_con->state = TELNET_STATE_DATA;
489 }
490 }
491 else
492 {
493 ERROR("BUG: unexpected value in t_con->last_escape");
494 t_con->state = TELNET_STATE_DATA;
495 }
496
497 break;
498 default:
499 ERROR("unknown telnet state");
500 exit(-1);
501 }
502
503 bytes_read--;
504 buf_p++;
505 }
506
507 return ERROR_OK;
508 }
509
510 int telnet_connection_closed(connection_t *connection)
511 {
512 telnet_connection_t *t_con = connection->priv;
513 int i;
514
515 if (t_con->prompt)
516 {
517 free(t_con->prompt);
518 t_con->prompt = NULL;
519 }
520
521 for (i = 0; i < TELNET_LINE_HISTORY_SIZE; i++)
522 {
523 if (t_con->history[i])
524 {
525 free(t_con->history[i]);
526 t_con->history[i] = NULL;
527 }
528 }
529
530 /* if this connection registered a debug-message receiver delete it */
531 delete_debug_msg_receiver(connection->cmd_ctx, NULL);
532
533 if (connection->priv)
534 {
535 free(connection->priv);
536 connection->priv = NULL;
537 }
538 else
539 {
540 ERROR("BUG: connection->priv == NULL");
541 }
542
543 target_unregister_event_callback(telnet_target_callback_event_handler, connection->cmd_ctx);
544
545 return ERROR_OK;
546 }
547
548 int telnet_set_prompt(connection_t *connection, char *prompt)
549 {
550 telnet_connection_t *t_con = connection->priv;
551
552 t_con->prompt = strdup(prompt);
553
554 return ERROR_OK;
555 }
556
557 int telnet_init(char *banner)
558 {
559 telnet_service_t *telnet_service = malloc(sizeof(telnet_service_t));
560
561 if (telnet_port == 0)
562 {
563 WARNING("no telnet port specified, using default port 4444");
564 telnet_port = 4444;
565 }
566
567 telnet_service->banner = banner;
568
569 add_service("telnet", CONNECTION_TELNET, telnet_port, 1, telnet_new_connection, telnet_input, telnet_connection_closed, telnet_service);
570
571 return ERROR_OK;
572 }
573
574 int telnet_register_commands(command_context_t *command_context)
575 {
576 register_command(command_context, NULL, "exit", handle_exit_command,
577 COMMAND_EXEC, "exit telnet session");
578
579 register_command(command_context, NULL, "telnet_port", handle_telnet_port_command,
580 COMMAND_CONFIG, "");
581
582 return ERROR_OK;
583 }
584
585 /* daemon configuration command telnet_port */
586 int handle_telnet_port_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc)
587 {
588 if (argc == 0)
589 return ERROR_OK;
590
591 /* only if the port wasn't overwritten by cmdline */
592 if (telnet_port == 0)
593 telnet_port = strtoul(args[0], NULL, 0);
594
595 return ERROR_OK;
596 }
597
598 int handle_exit_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc)
599 {
600 return ERROR_COMMAND_CLOSE_CONNECTION;
601 }

Linking to existing account procedure

If you already have an account and want to add another login method you MUST first sign in with your existing account and then change URL to read https://review.openocd.org/login/?link to get to this page again but this time it'll work for linking. Thank you.

SSH host keys fingerprints

1024 SHA256:YKx8b7u5ZWdcbp7/4AeXNaqElP49m6QrwfXaqQGJAOk gerrit-code-review@openocd.zylin.com (DSA)
384 SHA256:jHIbSQa4REvwCFG4cq5LBlBLxmxSqelQPem/EXIrxjk gerrit-code-review@openocd.org (ECDSA)
521 SHA256:UAOPYkU9Fjtcao0Ul/Rrlnj/OsQvt+pgdYSZ4jOYdgs gerrit-code-review@openocd.org (ECDSA)
256 SHA256:A13M5QlnozFOvTllybRZH6vm7iSt0XLxbA48yfc2yfY gerrit-code-review@openocd.org (ECDSA)
256 SHA256:spYMBqEYoAOtK7yZBrcwE8ZpYt6b68Cfh9yEVetvbXg gerrit-code-review@openocd.org (ED25519)
+--[ED25519 256]--+
|=..              |
|+o..   .         |
|*.o   . .        |
|+B . . .         |
|Bo. = o S        |
|Oo.+ + =         |
|oB=.* = . o      |
| =+=.+   + E     |
|. .=o   . o      |
+----[SHA256]-----+
2048 SHA256:0Onrb7/PHjpo6iVZ7xQX2riKN83FJ3KGU0TvI0TaFG4 gerrit-code-review@openocd.zylin.com (RSA)