diff --git a/doc/openocd.texi b/doc/openocd.texi index 579f39844..a21a3e4bd 100644 --- a/doc/openocd.texi +++ b/doc/openocd.texi @@ -9529,7 +9529,12 @@ 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 should only be used in telnet or Tcl sessions (and not directly +in Tcl scripts). + +Note: To terminate 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 0b957836d..62c2ce821 100644 --- a/src/server/server.c +++ b/src/server/server.c @@ -783,6 +783,23 @@ COMMAND_HANDLER(handle_shutdown_command) return ERROR_COMMAND_CLOSE_CONNECTION; } +COMMAND_HANDLER(handle_exit_command) +{ + if (!tcl_is_from_tcl_session(CMD_CTX) + && !telnet_is_from_telnet_session(CMD_CTX)) { + LOG_WARNING("DEPRECATED: 'exit' should only be used in telnet or Tcl " + "sessions to close the session"); + LOG_WARNING("Did you mean 'shutdown'?"); + return command_run_line(CMD_CTX, "shutdown"); + } + + if (CMD_ARGC != 0) + return ERROR_COMMAND_SYNTAX_ERROR; + + /* Disconnect telnet / Tcl session */ + return ERROR_COMMAND_CLOSE_CONNECTION; +} + COMMAND_HANDLER(handle_poll_period_command) { if (CMD_ARGC == 0) @@ -819,6 +836,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 16cc55e29..929fbee0c 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 bee562ce8..f92656f9c 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 3634a2a59..ea7f38fb8 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,16 +968,16 @@ int telnet_init(char *banner) return ERROR_OK; } +bool telnet_is_from_telnet_session(struct command_context *cmd_ctx) +{ + return cmd_ctx->output_handler == telnet_output; +} + COMMAND_HANDLER(handle_telnet_port_command) { return CALL_COMMAND_HANDLER(server_pipe_command, &telnet_port); } -COMMAND_HANDLER(handle_exit_command) -{ - return ERROR_COMMAND_CLOSE_CONNECTION; -} - static const struct command_registration telnet_subcommand_handlers[] = { { .name = "port", @@ -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 313b529f0..a905b4199 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 */