This is an automated email from Gerrit. "Jan Matyas <[email protected]>" just uploaded a new patch set to Gerrit, which you can find at https://review.openocd.org/c/openocd/+/9380
-- gerrit commit 0be76d9204e923392e04df32b55916fd3180d7b5 Author: Jan Matyas <[email protected]> Date: Mon Jan 5 07:20:37 2026 +0100 server: Make "exit" command behave consistently Before this fix, the "exit" command behaved differently depending on where it was called from: - "exit" in a telnet session: Session disconnected. - "exit" in a Tcl session: Empty response sent but the session remained connected. - "exit" in a script (outside of Telnet or Tcl): OpenOCD exited with code 0. Based on the help and documentation "exit" was apparently not intended for these cases. What's more, if "exit" is allowed in Tcl scripts, user may confuse it with the Tcl's native "exit" command that is not available in OpenOCD (it is shadowed with this OpenOCD's "exit" command). This fix makes the behavior of "exit" consistent: - "exit" in a telnet session: Session disconnected (no change). - "exit" in a Tcl session: Session disconnected (same as for telnet). - "exit" in a script: Notify the user that "exit" is not valid in this context. Update the documentation to make it very clear to users when to use "exit" vs. "shutdown". Change-Id: I790495330e1fa705b34097a1347fdc57aaa86de1 Signed-off-by: Jan Matyas <[email protected]> diff --git a/doc/openocd.texi b/doc/openocd.texi index ebe9567892..ea47cac20b 100644 --- a/doc/openocd.texi +++ b/doc/openocd.texi @@ -9502,7 +9502,11 @@ port is 6666. @section Server Commands @deffn {Command} {exit} -Exits the current telnet session. +Exits the current telnet or Tcl session but the OpenOCD process remains running. +This command can only be used in telnet or Tcl sessions. + +Note: To exit the whole OpenOCD process, use the +@command{shutdown} command instead. @end deffn @deffn {Command} {help} [string] diff --git a/src/server/server.c b/src/server/server.c index 81d79d41b1..a2a43a4833 100644 --- a/src/server/server.c +++ b/src/server/server.c @@ -769,6 +769,21 @@ COMMAND_HANDLER(handle_shutdown_command) return ERROR_COMMAND_CLOSE_CONNECTION; } +COMMAND_HANDLER(handle_exit_command) +{ + if (CMD_ARGC != 0) + return ERROR_COMMAND_SYNTAX_ERROR; + + if (!tcl_is_from_tcl_session(CMD_CTX) + && !telnet_is_from_telnet_session(CMD_CTX)) { + LOG_ERROR("'exit' can only be used in telnet or Tcl sessions"); + return ERROR_FAIL; + } + + /* Disconnect telnet / Tcl session */ + return ERROR_COMMAND_CLOSE_CONNECTION; +} + COMMAND_HANDLER(handle_poll_period_command) { if (CMD_ARGC == 0) @@ -805,6 +820,13 @@ static const struct command_registration server_command_handlers[] = { .usage = "", .help = "shut the server down", }, + { + .name = "exit", + .handler = handle_exit_command, + .mode = COMMAND_ANY, + .usage = "", + .help = "exit (disconnect) telnet or Tcl session", + }, { .name = "poll_period", .handler = &handle_poll_period_command, diff --git a/src/server/tcl_server.c b/src/server/tcl_server.c index 16cc55e29b..929fbee0c7 100644 --- a/src/server/tcl_server.c +++ b/src/server/tcl_server.c @@ -230,12 +230,22 @@ static int tcl_input(struct connection *connection) #undef ESTR } else { tclc->tc_line[tclc->tc_lineoffset-1] = '\0'; - command_run_line(connection->cmd_ctx, tclc->tc_line); + retval = command_run_line(connection->cmd_ctx, tclc->tc_line); + + if (retval == ERROR_COMMAND_CLOSE_CONNECTION) { + /* "shutdown" or "exit" executed. + * Send an empty response - just the response termination character. + */ + tcl_output(connection, "\x1a", 1); + return ERROR_SERVER_REMOTE_CLOSED; + } + result = Jim_GetString(Jim_GetResult(interp), &reslen); retval = tcl_output(connection, result, reslen); if (retval != ERROR_OK) return retval; - /* Always output ctrl-z as end of line to allow multiline results */ + + /* Signal the end of response by a termination character (ctrl-z) */ tcl_output(connection, "\x1a", 1); } @@ -284,6 +294,15 @@ int tcl_init(void) return add_service(&tcl_service_driver, tcl_port, CONNECTION_LIMIT_UNLIMITED, NULL); } +bool tcl_is_from_tcl_session(struct command_context *cmd_ctx) +{ + if (!cmd_ctx->output_handler_priv) + return false; + + struct connection *conn = (struct connection *)cmd_ctx->output_handler_priv; + return strcmp(conn->service->name, "tcl") == 0; +} + COMMAND_HANDLER(handle_tcl_port_command) { return CALL_COMMAND_HANDLER(server_pipe_command, &tcl_port); @@ -297,7 +316,7 @@ COMMAND_HANDLER(handle_tcl_notifications_command) if (CMD_CTX->output_handler_priv) connection = CMD_CTX->output_handler_priv; - if (connection && !strcmp(connection->service->name, "tcl")) { + if (connection && tcl_is_from_tcl_session(CMD_CTX)) { tclc = connection->priv; return CALL_COMMAND_HANDLER(handle_command_parse_bool, &tclc->tc_notify, "Target Notification output "); } else { @@ -314,7 +333,7 @@ COMMAND_HANDLER(handle_tcl_trace_command) if (CMD_CTX->output_handler_priv) connection = CMD_CTX->output_handler_priv; - if (connection && !strcmp(connection->service->name, "tcl")) { + if (connection && tcl_is_from_tcl_session(CMD_CTX)) { tclc = connection->priv; return CALL_COMMAND_HANDLER(handle_command_parse_bool, &tclc->tc_trace, "Target trace output "); } else { diff --git a/src/server/tcl_server.h b/src/server/tcl_server.h index bee562ce88..f92656f9c3 100644 --- a/src/server/tcl_server.h +++ b/src/server/tcl_server.h @@ -12,5 +12,6 @@ int tcl_init(void); int tcl_register_commands(struct command_context *cmd_ctx); void tcl_service_free(void); +bool tcl_is_from_tcl_session(struct command_context *ctx); #endif /* OPENOCD_SERVER_TCL_SERVER_H */ diff --git a/src/server/telnet_server.c b/src/server/telnet_server.c index 3634a2a592..ea7f38fb88 100644 --- a/src/server/telnet_server.c +++ b/src/server/telnet_server.c @@ -519,6 +519,7 @@ static int telnet_exec_line(struct connection *connection) t_con->prompt_visible = true; if (retval == ERROR_COMMAND_CLOSE_CONNECTION) + /* "shutdown" or "exit" executed. */ return ERROR_SERVER_REMOTE_CLOSED; /* the prompt is always placed at the line beginning */ @@ -967,14 +968,14 @@ int telnet_init(char *banner) return ERROR_OK; } -COMMAND_HANDLER(handle_telnet_port_command) +bool telnet_is_from_telnet_session(struct command_context *cmd_ctx) { - return CALL_COMMAND_HANDLER(server_pipe_command, &telnet_port); + return cmd_ctx->output_handler == telnet_output; } -COMMAND_HANDLER(handle_exit_command) +COMMAND_HANDLER(handle_telnet_port_command) { - return ERROR_COMMAND_CLOSE_CONNECTION; + return CALL_COMMAND_HANDLER(server_pipe_command, &telnet_port); } static const struct command_registration telnet_subcommand_handlers[] = { @@ -991,13 +992,6 @@ static const struct command_registration telnet_subcommand_handlers[] = { }; static const struct command_registration telnet_command_handlers[] = { - { - .name = "exit", - .handler = handle_exit_command, - .mode = COMMAND_ANY, - .usage = "", - .help = "exit telnet session", - }, { .name = "telnet", .chain = telnet_subcommand_handlers, diff --git a/src/server/telnet_server.h b/src/server/telnet_server.h index 313b529f0d..a905b4199e 100644 --- a/src/server/telnet_server.h +++ b/src/server/telnet_server.h @@ -54,5 +54,6 @@ struct telnet_service { int telnet_init(char *banner); int telnet_register_commands(struct command_context *command_context); void telnet_service_free(void); +bool telnet_is_from_telnet_session(struct command_context *cmd_ctx); #endif /* OPENOCD_SERVER_TELNET_SERVER_H */ --
