]> git.sur5r.net Git - openocd/blob - src/server/tcl_server.c
tcl_server: Support line buffers up to 4M (v3)
[openocd] / src / server / tcl_server.c
1 /***************************************************************************
2  *   Copyright (C) 2010 Ã˜yvind Harboe                                      *
3  *   oyvind.harboe@zylin.com                                               *
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  *   51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.           *
19  ***************************************************************************/
20
21 #ifdef HAVE_CONFIG_H
22 #include "config.h"
23 #endif
24
25 #include "tcl_server.h"
26 #include <target/target.h>
27 #include <helper/binarybuffer.h>
28
29 #define TCL_SERVER_VERSION              "TCL Server 0.1"
30 #define TCL_LINE_INITIAL                (4*1024)
31 #define TCL_LINE_MAX                    (4*1024*1024)
32
33 struct tcl_connection {
34         int tc_linedrop;
35         int tc_lineoffset;
36         int tc_line_size;
37         char *tc_line;
38         int tc_outerror;/* flag an output error */
39         enum target_state tc_laststate;
40         bool tc_notify;
41         bool tc_trace;
42 };
43
44 static char *tcl_port;
45
46 /* handlers */
47 static int tcl_new_connection(struct connection *connection);
48 static int tcl_input(struct connection *connection);
49 static int tcl_output(struct connection *connection, const void *buf, ssize_t len);
50 static int tcl_closed(struct connection *connection);
51
52 static int tcl_target_callback_event_handler(struct target *target,
53                 enum target_event event, void *priv)
54 {
55         struct connection *connection = priv;
56         struct tcl_connection *tclc;
57         char buf[256];
58
59         tclc = connection->priv;
60
61         if (tclc->tc_notify) {
62                 snprintf(buf, sizeof(buf), "type target_event event %s\r\n\x1a", target_event_name(event));
63                 tcl_output(connection, buf, strlen(buf));
64         }
65
66         if (tclc->tc_laststate != target->state) {
67                 tclc->tc_laststate = target->state;
68                 if (tclc->tc_notify) {
69                         snprintf(buf, sizeof(buf), "type target_state state %s\r\n\x1a", target_state_name(target));
70                         tcl_output(connection, buf, strlen(buf));
71                 }
72         }
73
74         return ERROR_OK;
75 }
76
77 static int tcl_target_callback_reset_handler(struct target *target,
78                 enum target_reset_mode reset_mode, void *priv)
79 {
80         struct connection *connection = priv;
81         struct tcl_connection *tclc;
82         char buf[256];
83
84         tclc = connection->priv;
85
86         if (tclc->tc_notify) {
87                 snprintf(buf, sizeof(buf), "type target_reset mode %s\r\n\x1a", target_reset_mode_name(reset_mode));
88                 tcl_output(connection, buf, strlen(buf));
89         }
90
91         return ERROR_OK;
92 }
93
94 static int tcl_target_callback_trace_handler(struct target *target,
95                 size_t len, uint8_t *data, void *priv)
96 {
97         struct connection *connection = priv;
98         struct tcl_connection *tclc;
99         char *header = "type target_trace data ";
100         char *trailer = "\r\n\x1a";
101         size_t hex_len = len * 2 + 1;
102         size_t max_len = hex_len + strlen(header) + strlen(trailer);
103         char *buf, *hex;
104
105         tclc = connection->priv;
106
107         if (tclc->tc_trace) {
108                 hex = malloc(hex_len);
109                 buf = malloc(max_len);
110                 hexify(hex, (const char *)data, len, hex_len);
111                 snprintf(buf, max_len, "%s%s%s", header, hex, trailer);
112                 tcl_output(connection, buf, strlen(buf));
113                 free(hex);
114                 free(buf);
115         }
116
117         return ERROR_OK;
118 }
119
120 /* write data out to a socket.
121  *
122  * this is a blocking write, so the return value must equal the length, if
123  * that is not the case then flag the connection with an output error.
124  */
125 int tcl_output(struct connection *connection, const void *data, ssize_t len)
126 {
127         ssize_t wlen;
128         struct tcl_connection *tclc;
129
130         tclc = connection->priv;
131         if (tclc->tc_outerror)
132                 return ERROR_SERVER_REMOTE_CLOSED;
133
134         wlen = connection_write(connection, data, len);
135
136         if (wlen == len)
137                 return ERROR_OK;
138
139         LOG_ERROR("error during write: %d != %d", (int)wlen, (int)len);
140         tclc->tc_outerror = 1;
141         return ERROR_SERVER_REMOTE_CLOSED;
142 }
143
144 /* connections */
145 static int tcl_new_connection(struct connection *connection)
146 {
147         struct tcl_connection *tclc;
148
149         tclc = calloc(1, sizeof(struct tcl_connection));
150         if (tclc == NULL)
151                 return ERROR_CONNECTION_REJECTED;
152
153         tclc->tc_line_size = TCL_LINE_INITIAL;
154         tclc->tc_line = malloc(tclc->tc_line_size);
155         if (tclc->tc_line == NULL) {
156                 free(tclc);
157                 return ERROR_CONNECTION_REJECTED;
158         }
159
160         connection->priv = tclc;
161
162         struct target *target = get_target_by_num(connection->cmd_ctx->current_target);
163         if (target != NULL)
164                 tclc->tc_laststate = target->state;
165
166         /* store the connection object on cmd_ctx so we can access it from command handlers */
167         connection->cmd_ctx->output_handler_priv = connection;
168
169         target_register_event_callback(tcl_target_callback_event_handler, connection);
170         target_register_reset_callback(tcl_target_callback_reset_handler, connection);
171         target_register_trace_callback(tcl_target_callback_trace_handler, connection);
172
173         return ERROR_OK;
174 }
175
176 static int tcl_input(struct connection *connection)
177 {
178         Jim_Interp *interp = (Jim_Interp *)connection->cmd_ctx->interp;
179         int retval;
180         int i;
181         ssize_t rlen;
182         const char *result;
183         int reslen;
184         struct tcl_connection *tclc;
185         unsigned char in[256];
186         char *tc_line_new;
187         int tc_line_size_new;
188
189         rlen = connection_read(connection, &in, sizeof(in));
190         if (rlen <= 0) {
191                 if (rlen < 0)
192                         LOG_ERROR("error during read: %s", strerror(errno));
193                 return ERROR_SERVER_REMOTE_CLOSED;
194         }
195
196         tclc = connection->priv;
197         if (tclc == NULL)
198                 return ERROR_CONNECTION_REJECTED;
199
200         /* push as much data into the line as possible */
201         for (i = 0; i < rlen; i++) {
202                 /* buffer the data */
203                 tclc->tc_line[tclc->tc_lineoffset] = in[i];
204                 if (tclc->tc_lineoffset < tclc->tc_line_size) {
205                         tclc->tc_lineoffset++;
206                 } else if (tclc->tc_line_size >= TCL_LINE_MAX) {
207                         /* maximum line size reached, drop line */
208                         tclc->tc_linedrop = 1;
209                 } else {
210                         /* grow line buffer: exponential below 1 MB, linear above */
211                         if (tclc->tc_line_size <= 1*1024*1024)
212                                 tc_line_size_new = tclc->tc_line_size * 2;
213                         else
214                                 tc_line_size_new = tclc->tc_line_size + 1*1024*1024;
215
216                         if (tc_line_size_new > TCL_LINE_MAX)
217                                 tc_line_size_new = TCL_LINE_MAX;
218
219                         tc_line_new = realloc(tclc->tc_line, tc_line_size_new);
220                         if (tc_line_new == NULL) {
221                                 tclc->tc_linedrop = 1;
222                         } else {
223                                 tclc->tc_line = tc_line_new;
224                                 tclc->tc_line_size = tc_line_size_new;
225                                 tclc->tc_lineoffset++;
226                         }
227
228                 }
229
230                 /* ctrl-z is end of command. When testing from telnet, just
231                  * press ctrl-z a couple of times first to put telnet into the
232                  * mode where it will send 0x1a in response to pressing ctrl-z
233                  */
234                 if (in[i] != '\x1a')
235                         continue;
236
237                 /* process the line */
238                 if (tclc->tc_linedrop) {
239 #define ESTR "line too long\n"
240                         retval = tcl_output(connection, ESTR, sizeof(ESTR));
241                         if (retval != ERROR_OK)
242                                 return retval;
243 #undef ESTR
244                 } else {
245                         tclc->tc_line[tclc->tc_lineoffset-1] = '\0';
246                         command_run_line(connection->cmd_ctx, tclc->tc_line);
247                         result = Jim_GetString(Jim_GetResult(interp), &reslen);
248                         retval = tcl_output(connection, result, reslen);
249                         if (retval != ERROR_OK)
250                                 return retval;
251                         /* Always output ctrl-d as end of line to allow multiline results */
252                         tcl_output(connection, "\x1a", 1);
253                 }
254
255                 tclc->tc_lineoffset = 0;
256                 tclc->tc_linedrop = 0;
257         }
258
259         return ERROR_OK;
260 }
261
262 static int tcl_closed(struct connection *connection)
263 {
264         struct tcl_connection *tclc;
265         tclc = connection->priv;
266
267         /* cleanup connection context */
268         if (tclc) {
269                 free(tclc->tc_line);
270                 free(tclc);
271                 connection->priv = NULL;
272         }
273
274         target_unregister_event_callback(tcl_target_callback_event_handler, connection);
275         target_unregister_reset_callback(tcl_target_callback_reset_handler, connection);
276         target_unregister_trace_callback(tcl_target_callback_trace_handler, connection);
277
278         return ERROR_OK;
279 }
280
281 int tcl_init(void)
282 {
283         if (strcmp(tcl_port, "disabled") == 0) {
284                 LOG_INFO("tcl server disabled");
285                 return ERROR_OK;
286         }
287
288         return add_service("tcl", tcl_port, CONNECTION_LIMIT_UNLIMITED,
289                 &tcl_new_connection, &tcl_input,
290                 &tcl_closed, NULL);
291 }
292
293 COMMAND_HANDLER(handle_tcl_port_command)
294 {
295         return CALL_COMMAND_HANDLER(server_pipe_command, &tcl_port);
296 }
297
298 COMMAND_HANDLER(handle_tcl_notifications_command)
299 {
300         struct connection *connection = NULL;
301         struct tcl_connection *tclc = NULL;
302
303         if (CMD_CTX->output_handler_priv != NULL)
304                 connection = CMD_CTX->output_handler_priv;
305
306         if (connection != NULL && !strcmp(connection->service->name, "tcl")) {
307                 tclc = connection->priv;
308                 return CALL_COMMAND_HANDLER(handle_command_parse_bool, &tclc->tc_notify, "Target Notification output ");
309         } else {
310                 LOG_ERROR("%s: can only be called from the tcl server", CMD_NAME);
311                 return ERROR_COMMAND_SYNTAX_ERROR;
312         }
313 }
314
315 COMMAND_HANDLER(handle_tcl_trace_command)
316 {
317         struct connection *connection = NULL;
318         struct tcl_connection *tclc = NULL;
319
320         if (CMD_CTX->output_handler_priv != NULL)
321                 connection = CMD_CTX->output_handler_priv;
322
323         if (connection != NULL && !strcmp(connection->service->name, "tcl")) {
324                 tclc = connection->priv;
325                 return CALL_COMMAND_HANDLER(handle_command_parse_bool, &tclc->tc_trace, "Target trace output ");
326         } else {
327                 LOG_ERROR("%s: can only be called from the tcl server", CMD_NAME);
328                 return ERROR_COMMAND_SYNTAX_ERROR;
329         }
330 }
331
332 static const struct command_registration tcl_command_handlers[] = {
333         {
334                 .name = "tcl_port",
335                 .handler = handle_tcl_port_command,
336                 .mode = COMMAND_ANY,
337                 .help = "Specify port on which to listen "
338                         "for incoming Tcl syntax.  "
339                         "Read help on 'gdb_port'.",
340                 .usage = "[port_num]",
341         },
342         {
343                 .name = "tcl_notifications",
344                 .handler = handle_tcl_notifications_command,
345                 .mode = COMMAND_EXEC,
346                 .help = "Target Notification output",
347                 .usage = "[on|off]",
348         },
349         {
350                 .name = "tcl_trace",
351                 .handler = handle_tcl_trace_command,
352                 .mode = COMMAND_EXEC,
353                 .help = "Target trace output",
354                 .usage = "[on|off]",
355         },
356         COMMAND_REGISTRATION_DONE
357 };
358
359 int tcl_register_commands(struct command_context *cmd_ctx)
360 {
361         tcl_port = strdup("6666");
362         return register_commands(cmd_ctx, NULL, tcl_command_handlers);
363 }