Pavel Chromy: hopefully perfection for async output and prompt/partially typed comman...
[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 /* The only way we can detect that the socket is closed is the first time
54 * we write to it, we will fail. Subsequent write operations will
55 * succeed. Shudder!
56 */
57 int telnet_write(connection_t *connection, const void *data, int len)
58 {
59 telnet_connection_t *t_con = connection->priv;
60 if (t_con->closed)
61 return ERROR_SERVER_REMOTE_CLOSED;
62
63 if (write_socket(connection->fd, data, len) == len)
64 {
65 return ERROR_OK;
66 }
67 t_con->closed = 1;
68 return ERROR_SERVER_REMOTE_CLOSED;
69 }
70
71 int telnet_prompt(connection_t *connection)
72 {
73 telnet_connection_t *t_con = connection->priv;
74
75 telnet_write(connection, "\r", 1); /* the prompt is always placed at the line beginning */
76 return telnet_write(connection, t_con->prompt, strlen(t_con->prompt));
77 }
78
79 int telnet_outputline(connection_t *connection, const char *line)
80 {
81 int len;
82
83 /* process lines in buffer */
84 while (*line) {
85 char *line_end = strchr(line, '\n');
86
87 if (line_end)
88 len = line_end-line;
89 else
90 len = strlen(line);
91
92 telnet_write(connection, line, len);
93 if (line_end)
94 {
95 telnet_write(connection, "\r\n\0", 3);
96 line += len+1;
97 }
98 else
99 {
100 line += len;
101 }
102 }
103
104 return ERROR_OK;
105 }
106
107 int telnet_output(struct command_context_s *cmd_ctx, char* line)
108 {
109 connection_t *connection = cmd_ctx->output_handler_priv;
110
111 return telnet_outputline(connection, line);
112 }
113
114 void telnet_log_callback(void *priv, const char *file, int line,
115 const char *function, const char *string)
116 {
117 connection_t *connection = priv;
118 telnet_connection_t *t_con = connection->priv;
119 int i;
120
121 /* clear the command line */
122 telnet_write(connection, "\r", 1);
123 for (i = strlen(t_con->prompt) + t_con->line_size; i>0; i-=16)
124 telnet_write(connection, " ", i>16 ? 16 : i);
125
126 telnet_write(connection, "\r", 1);
127 telnet_outputline(connection, string);
128
129 /* put the command line to its previous state */
130 telnet_prompt(connection);
131 telnet_write(connection, t_con->line, t_con->line_size);
132 for (i=t_con->line_size; i>t_con->line_cursor; i--)
133 telnet_write(connection, "\b", 1);
134 }
135
136 int telnet_target_callback_event_handler(struct target_s *target, enum target_event event, void *priv)
137 {
138 switch (event)
139 {
140 case TARGET_EVENT_HALTED:
141 target_arch_state(target);
142 break;
143 default:
144 break;
145 }
146
147 return ERROR_OK;
148 }
149
150 int telnet_new_connection(connection_t *connection)
151 {
152 telnet_connection_t *telnet_connection = malloc(sizeof(telnet_connection_t));
153 telnet_service_t *telnet_service = connection->service->priv;
154 int i;
155
156 connection->priv = telnet_connection;
157
158 /* initialize telnet connection information */
159 telnet_connection->closed = 0;
160 telnet_connection->line_size = 0;
161 telnet_connection->line_cursor = 0;
162 telnet_connection->option_size = 0;
163 telnet_connection->prompt = strdup("> ");
164 telnet_connection->state = TELNET_STATE_DATA;
165
166 /* output goes through telnet connection */
167 command_set_output_handler(connection->cmd_ctx, telnet_output, connection);
168
169 /* negotiate telnet options */
170 telnet_write(connection, negotiate, strlen(negotiate));
171
172 /* print connection banner */
173 if (telnet_service->banner)
174 {
175 telnet_write(connection, telnet_service->banner, strlen(telnet_service->banner));
176 telnet_write(connection, "\r\n\0", 3);
177 }
178
179 telnet_prompt(connection);
180
181 /* initialize history */
182 for (i = 0; i < TELNET_LINE_HISTORY_SIZE; i++)
183 {
184 telnet_connection->history[i] = NULL;
185 }
186 telnet_connection->next_history = 0;
187 telnet_connection->current_history = 0;
188
189 target_register_event_callback(telnet_target_callback_event_handler, connection->cmd_ctx);
190
191 log_add_callback(telnet_log_callback, connection);
192
193
194
195 return ERROR_OK;
196 }
197
198 void telnet_clear_line(connection_t *connection, telnet_connection_t *t_con)
199 {
200 /* move to end of line */
201 if (t_con->line_cursor < t_con->line_size)
202 {
203 telnet_write(connection, t_con->line + t_con->line_cursor, t_con->line_size - t_con->line_cursor);
204 }
205
206 /* backspace, overwrite with space, backspace */
207 while (t_con->line_size > 0)
208 {
209 telnet_write(connection, "\b \b", 3);
210 t_con->line_size--;
211 }
212 t_con->line_cursor = 0;
213 }
214
215 int telnet_input(connection_t *connection)
216 {
217 int bytes_read;
218 char buffer[TELNET_BUFFER_SIZE];
219 char *buf_p;
220 telnet_connection_t *t_con = connection->priv;
221 command_context_t *command_context = connection->cmd_ctx;
222
223 bytes_read = read_socket(connection->fd, buffer, TELNET_BUFFER_SIZE);
224
225 if (bytes_read == 0)
226 return ERROR_SERVER_REMOTE_CLOSED;
227 else if (bytes_read == -1)
228 {
229 ERROR("error during read: %s", strerror(errno));
230 return ERROR_SERVER_REMOTE_CLOSED;
231 }
232
233 buf_p = buffer;
234 while (bytes_read)
235 {
236 switch (t_con->state)
237 {
238 case TELNET_STATE_DATA:
239 if (*buf_p == '\xff')
240 {
241 t_con->state = TELNET_STATE_IAC;
242 }
243 else
244 {
245 if (isprint(*buf_p)) /* printable character */
246 {
247 telnet_write(connection, buf_p, 1);
248 if (t_con->line_cursor == t_con->line_size)
249 {
250 t_con->line[t_con->line_size++] = *buf_p;
251 t_con->line_cursor++;
252 }
253 else
254 {
255 int i;
256 memmove(t_con->line + t_con->line_cursor + 1, t_con->line + t_con->line_cursor, t_con->line_size - t_con->line_cursor);
257 t_con->line[t_con->line_cursor++] = *buf_p;
258 t_con->line_size++;
259 telnet_write(connection, t_con->line + t_con->line_cursor, t_con->line_size - t_con->line_cursor);
260 for (i = t_con->line_cursor; i < t_con->line_size; i++)
261 {
262 telnet_write(connection, "\b", 1);
263 }
264 }
265 }
266 else /* non-printable */
267 {
268 if (*buf_p == 0x1b) /* escape */
269 {
270 t_con->state = TELNET_STATE_ESCAPE;
271 t_con->last_escape = '\x00';
272 }
273 else if ((*buf_p == 0xd) || (*buf_p == 0xa)) /* CR/LF */
274 {
275 int retval;
276
277 /* skip over combinations with CR/LF + NUL */
278 if (((*(buf_p + 1) == 0xa) || (*(buf_p + 1) == 0xd)) && (bytes_read > 1))
279 {
280 buf_p++;
281 bytes_read--;
282 }
283 if ((*(buf_p + 1) == 0) && (bytes_read > 1))
284 {
285 buf_p++;
286 bytes_read--;
287 }
288 t_con->line[t_con->line_size] = 0;
289
290 telnet_write(connection, "\r\n\x00", 3);
291
292 if (strcmp(t_con->line, "history") == 0)
293 {
294 int i;
295 for (i = 0; i < TELNET_LINE_HISTORY_SIZE; i++)
296 {
297 if (t_con->history[i])
298 {
299 telnet_write(connection, t_con->history[i], strlen(t_con->history[i]));
300 telnet_write(connection, "\r\n\x00", 3);
301 }
302 }
303 t_con->line_size = 0;
304 t_con->line_cursor = 0;
305 continue;
306 }
307
308 /* Save only non-blank lines in the history */
309 if (t_con->line_size > 0)
310 {
311 /* if the history slot is already taken, free it */
312 if (t_con->history[t_con->next_history])
313 {
314 free(t_con->history[t_con->next_history]);
315 }
316
317 /* add line to history */
318 t_con->history[t_con->next_history] = strdup(t_con->line);
319
320 /* wrap history at TELNET_LINE_HISTORY_SIZE */
321 t_con->next_history = (t_con->next_history + 1) % TELNET_LINE_HISTORY_SIZE;
322
323 /* current history line starts at the new entry */
324 t_con->current_history = t_con->next_history;
325
326 if (t_con->history[t_con->current_history])
327 {
328 free(t_con->history[t_con->current_history]);
329 }
330 t_con->history[t_con->current_history] = strdup("");
331 }
332
333 t_con->line_size = 0;
334 t_con->line_cursor = 0;
335
336 retval = command_run_line(command_context, t_con->line);
337 if (retval == ERROR_COMMAND_CLOSE_CONNECTION)
338 return ERROR_SERVER_REMOTE_CLOSED;
339
340 retval = telnet_prompt(connection);
341 if (retval == ERROR_SERVER_REMOTE_CLOSED)
342 return ERROR_SERVER_REMOTE_CLOSED;
343
344 }
345 else if ((*buf_p == 0x7f) || (*buf_p == 0x8)) /* delete character */
346 {
347 if (t_con->line_cursor > 0)
348 {
349 if (t_con->line_cursor != t_con->line_size)
350 {
351 int i;
352 telnet_write(connection, "\b", 1);
353 t_con->line_cursor--;
354 t_con->line_size--;
355 memmove(t_con->line + t_con->line_cursor, t_con->line + t_con->line_cursor + 1, t_con->line_size - t_con->line_cursor);
356
357 telnet_write(connection, t_con->line + t_con->line_cursor, t_con->line_size - t_con->line_cursor);
358 telnet_write(connection, " \b", 2);
359 for (i = t_con->line_cursor; i < t_con->line_size; i++)
360 {
361 telnet_write(connection, "\b", 1);
362 }
363 }
364 else
365 {
366 t_con->line_size--;
367 t_con->line_cursor--;
368 /* back space: move the 'printer' head one char back, overwrite with space, move back again */
369 telnet_write(connection, "\b \b", 3);
370 }
371 }
372 }
373 else if (*buf_p == 0x15) /* clear line */
374 {
375 telnet_clear_line(connection, t_con);
376 }
377 else if (*buf_p == CTRL('B')) /* cursor left */
378 {
379 if (t_con->line_cursor > 0)
380 {
381 telnet_write(connection, "\b", 1);
382 t_con->line_cursor--;
383 }
384 t_con->state = TELNET_STATE_DATA;
385 }
386 else if (*buf_p == CTRL('F')) /* cursor right */
387 {
388 if (t_con->line_cursor < t_con->line_size)
389 {
390 telnet_write(connection, t_con->line + t_con->line_cursor++, 1);
391 }
392 t_con->state = TELNET_STATE_DATA;
393 }
394 else
395 {
396 DEBUG("unhandled nonprintable: %2.2x", *buf_p);
397 }
398 }
399 }
400 break;
401 case TELNET_STATE_IAC:
402 switch (*buf_p)
403 {
404 case '\xfe':
405 t_con->state = TELNET_STATE_DONT;
406 break;
407 case '\xfd':
408 t_con->state = TELNET_STATE_DO;
409 break;
410 case '\xfc':
411 t_con->state = TELNET_STATE_WONT;
412 break;
413 case '\xfb':
414 t_con->state = TELNET_STATE_WILL;
415 break;
416 }
417 break;
418 case TELNET_STATE_SB:
419 break;
420 case TELNET_STATE_SE:
421 break;
422 case TELNET_STATE_WILL:
423 case TELNET_STATE_WONT:
424 case TELNET_STATE_DO:
425 case TELNET_STATE_DONT:
426 t_con->state = TELNET_STATE_DATA;
427 break;
428 case TELNET_STATE_ESCAPE:
429 if (t_con->last_escape == '[')
430 {
431 if (*buf_p == 'D') /* cursor left */
432 {
433 if (t_con->line_cursor > 0)
434 {
435 telnet_write(connection, "\b", 1);
436 t_con->line_cursor--;
437 }
438 t_con->state = TELNET_STATE_DATA;
439 }
440 else if (*buf_p == 'C') /* cursor right */
441 {
442 if (t_con->line_cursor < t_con->line_size)
443 {
444 telnet_write(connection, t_con->line + t_con->line_cursor++, 1);
445 }
446 t_con->state = TELNET_STATE_DATA;
447 }
448 else if (*buf_p == 'A') /* cursor up */
449 {
450 int last_history = (t_con->current_history > 0) ? t_con->current_history - 1 : TELNET_LINE_HISTORY_SIZE-1;
451 if (t_con->history[last_history])
452 {
453 telnet_clear_line(connection, t_con);
454 t_con->line_size = strlen(t_con->history[last_history]);
455 t_con->line_cursor = t_con->line_size;
456 memcpy(t_con->line, t_con->history[last_history], t_con->line_size + 1);
457 telnet_write(connection, t_con->line, t_con->line_size);
458 t_con->current_history = last_history;
459 }
460 t_con->state = TELNET_STATE_DATA;
461 }
462 else if (*buf_p == 'B') /* cursor down */
463 {
464 int next_history = (t_con->current_history + 1) % TELNET_LINE_HISTORY_SIZE;
465 if (t_con->history[next_history])
466 {
467 telnet_clear_line(connection, t_con);
468 t_con->line_size = strlen(t_con->history[next_history]);
469 t_con->line_cursor = t_con->line_size;
470 memcpy(t_con->line, t_con->history[next_history], t_con->line_size + 1);
471 telnet_write(connection, t_con->line, t_con->line_size);
472 t_con->current_history = next_history;
473 }
474 t_con->state = TELNET_STATE_DATA;
475 }
476 else if (*buf_p == '3')
477 {
478 t_con->last_escape = *buf_p;
479 }
480 else
481 {
482 t_con->state = TELNET_STATE_DATA;
483 }
484 }
485 else if (t_con->last_escape == '3')
486 {
487 /* Remove character */
488 if (*buf_p == '~')
489 {
490 if (t_con->line_cursor < t_con->line_size)
491 {
492 int i;
493 t_con->line_size--;
494 /* remove char from line buffer */
495 memmove(t_con->line + t_con->line_cursor, t_con->line + t_con->line_cursor + 1, t_con->line_size - t_con->line_cursor);
496
497 /* print remainder of buffer */
498 telnet_write(connection, t_con->line + t_con->line_cursor, t_con->line_size - t_con->line_cursor);
499 /* overwrite last char with whitespace */
500 telnet_write(connection, " \b", 2);
501
502 /* move back to cursor position*/
503 for (i = t_con->line_cursor; i < t_con->line_size; i++)
504 {
505 telnet_write(connection, "\b", 1);
506 }
507 }
508
509 t_con->state = TELNET_STATE_DATA;
510 }
511 else
512 {
513 t_con->state = TELNET_STATE_DATA;
514 }
515 }
516 else if (t_con->last_escape == '\x00')
517 {
518 if (*buf_p == '[')
519 {
520 t_con->last_escape = *buf_p;
521 }
522 else
523 {
524 t_con->state = TELNET_STATE_DATA;
525 }
526 }
527 else
528 {
529 ERROR("BUG: unexpected value in t_con->last_escape");
530 t_con->state = TELNET_STATE_DATA;
531 }
532
533 break;
534 default:
535 ERROR("unknown telnet state");
536 exit(-1);
537 }
538
539 bytes_read--;
540 buf_p++;
541 }
542
543 return ERROR_OK;
544 }
545
546 int telnet_connection_closed(connection_t *connection)
547 {
548 telnet_connection_t *t_con = connection->priv;
549 int i;
550
551 log_remove_callback(telnet_log_callback, connection);
552
553 if (t_con->prompt)
554 {
555 free(t_con->prompt);
556 t_con->prompt = NULL;
557 }
558
559 for (i = 0; i < TELNET_LINE_HISTORY_SIZE; i++)
560 {
561 if (t_con->history[i])
562 {
563 free(t_con->history[i]);
564 t_con->history[i] = NULL;
565 }
566 }
567
568 /* if this connection registered a debug-message receiver delete it */
569 delete_debug_msg_receiver(connection->cmd_ctx, NULL);
570
571 if (connection->priv)
572 {
573 free(connection->priv);
574 connection->priv = NULL;
575 }
576 else
577 {
578 ERROR("BUG: connection->priv == NULL");
579 }
580
581 target_unregister_event_callback(telnet_target_callback_event_handler, connection->cmd_ctx);
582
583 return ERROR_OK;
584 }
585
586 int telnet_set_prompt(connection_t *connection, char *prompt)
587 {
588 telnet_connection_t *t_con = connection->priv;
589
590 if (t_con->prompt != NULL)
591 free(t_con->prompt);
592
593 t_con->prompt = strdup(prompt);
594
595 return ERROR_OK;
596 }
597
598 int telnet_init(char *banner)
599 {
600 telnet_service_t *telnet_service = malloc(sizeof(telnet_service_t));
601
602 if (telnet_port == 0)
603 {
604 WARNING("no telnet port specified, using default port 4444");
605 telnet_port = 4444;
606 }
607
608 telnet_service->banner = banner;
609
610 add_service("telnet", CONNECTION_TELNET, telnet_port, 1, telnet_new_connection, telnet_input, telnet_connection_closed, telnet_service);
611
612 return ERROR_OK;
613 }
614
615 int telnet_register_commands(command_context_t *command_context)
616 {
617 register_command(command_context, NULL, "exit", handle_exit_command,
618 COMMAND_EXEC, "exit telnet session");
619
620 register_command(command_context, NULL, "telnet_port", handle_telnet_port_command,
621 COMMAND_CONFIG, "");
622
623 return ERROR_OK;
624 }
625
626 /* daemon configuration command telnet_port */
627 int handle_telnet_port_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc)
628 {
629 if (argc == 0)
630 return ERROR_OK;
631
632 telnet_port = strtoul(args[0], NULL, 0);
633
634 return ERROR_OK;
635 }
636
637 int handle_exit_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc)
638 {
639 return ERROR_COMMAND_CLOSE_CONNECTION;
640 }