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