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