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

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)