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/+/9381

-- gerrit

commit a8c97abb0ae95200709c9012d77cb5e60bddf8bd
Author: Jan Matyas <[email protected]>
Date:   Mon Jan 5 07:27:20 2026 +0100

    server: Allow OpenOCD to exit with user-specified code
    
    Improve the "shutdown" command so that the user can optionally
    specify a concrete error code (exit code) for OpenOCD.
    
    This new argument to "shutdown" is optional, meaning this change
    is backward compatible:
    
    shutdown          ;# no change: zero exit code
    shutdown error    ;# no change: some non-zero exit code
    shutdown error 25 ;# new funciontality: use the given exit code
    
    This feature is useful when different errors can occur in a given Tcl
    script, and the parent process (that launched OpenOCD) needs to
    distinguish between these errors.
    
    Concrete use case (example): When testing OpenOCD in an automated
    way, some tests may need to be skipped because they are not applicable
    for the given situation (e.g. for the target under test). In that case,
    the Tcl script can now exit with a specific code, which the parent
    process will then interpret as a skipped test, not as a test failure.
    
    While making the change, the code related to the shutdown reason
    was cleaned up.
    
    Documentation was updated accordingly.
    
    Change-Id: I4279b80853db55b1f0c7f930883fcd7f16cae00c
    Signed-off-by: Jan Matyas <[email protected]>

diff --git a/doc/openocd.texi b/doc/openocd.texi
index ea47cac20b..33580d163a 100644
--- a/doc/openocd.texi
+++ b/doc/openocd.texi
@@ -9535,10 +9535,20 @@ Useful in connection with script files
 (@command{script} command and @command{target_name} configuration).
 @end deffn
 
-@deffn {Command} {shutdown} [@option{error}]
+@deffn {Command} {shutdown} [@option{error} [concrete_error_code]]
 Close the OpenOCD server, disconnecting all clients (GDB, telnet,
-other). If option @option{error} is used, OpenOCD will return a
-non-zero exit code to the parent process.
+other). Then the OpenOCD process quits. The exit code of OpenOCD,
+returned to the parent process, is as follows:
+
+@itemize
+@item If @command{shutdown} command is executed without arguments,
+zero exit code is used, indicating success.
+@item If @option{error} is passed as the first arugment, OpenOCD will exit
+with some non-zero code, indicating a failure.
+@item If @option{error} is passed as the first arugment and a concrete error 
code
+as a second argument, then OpenOCD will exit with this user-specified error 
code.
+The code must be in range 1-255.
+@end itemize
 
 If user types CTRL-C or kills OpenOCD, the command @command{shutdown}
 will be automatically executed to cause OpenOCD to exit.
diff --git a/src/openocd.c b/src/openocd.c
index f3e1bee48e..8029f504a2 100644
--- a/src/openocd.c
+++ b/src/openocd.c
@@ -275,7 +275,8 @@ static struct command_context 
*setup_command_handler(Jim_Interp *interp)
        return cmd_ctx;
 }
 
-/** OpenOCD runtime meat that can become single-thread in future. It parse
+/**
+ * OpenOCD runtime meat that can become single-thread in future. It parses
  * commandline, reads configuration, sets up the target and starts server loop.
  * Commandline arguments are passed into this function from openocd_main().
  */
@@ -291,6 +292,8 @@ static int openocd_thread(int argc, char *argv[], struct 
command_context *cmd_ct
 
        ret = parse_config_file(cmd_ctx);
        if (ret == ERROR_COMMAND_CLOSE_CONNECTION) {
+               /* Shutdown command encountered while processing the initial
+                * commands/scripts. */
                server_quit(); /* gdb server may be initialized by -c init */
                return ERROR_OK;
        } else if (ret != ERROR_OK) {
@@ -310,14 +313,10 @@ static int openocd_thread(int argc, char *argv[], struct 
command_context *cmd_ct
                }
        }
 
-       ret = server_loop(cmd_ctx);
+       server_loop(cmd_ctx);
 
-       int last_signal = server_quit();
-       if (last_signal != ERROR_OK)
-               return last_signal;
+       server_quit();
 
-       if (ret != ERROR_OK)
-               return ERROR_FAIL;
        return ERROR_OK;
 }
 
@@ -326,8 +325,6 @@ static int openocd_thread(int argc, char *argv[], struct 
command_context *cmd_ct
  * application will have it's own implementation of main(). */
 int openocd_main(int argc, char *argv[])
 {
-       int ret;
-
        /* initialize commandline interface */
        struct command_context *cmd_ctx;
 
@@ -349,7 +346,7 @@ int openocd_main(int argc, char *argv[])
        server_host_os_entry();
 
        /* Start the executable meat that can evolve into thread in future. */
-       ret = openocd_thread(argc, argv, cmd_ctx);
+       int ret = openocd_thread(argc, argv, cmd_ctx);
 
        flash_free_all_banks();
        gdb_service_free();
@@ -382,10 +379,24 @@ int openocd_main(int argc, char *argv[])
        __gcov_dump();
 #endif
 
-       if (ret == ERROR_FAIL)
+       if (ret != ERROR_OK) {
+               /* An error occurred before the server could be fully started.
+                * For example during the processing of initial 
commands/scripts. */
                return EXIT_FAILURE;
-       else if (ret != ERROR_OK)
-               exit_on_signal(ret);
+       }
+       else {
+               /* Otherwise, look at the server shutdown reason */
+               switch (server_get_shutdown_reason()) {
+               case SHUTDOWN_REQUESTED:
+                       return EXIT_SUCCESS;
+               case SHUTDOWN_WITH_SIGNAL_CODE:
+                       return exit_on_signal(server_get_last_signal());
+               case SHUTDOWN_WITH_ERROR_CODE:
+                       return server_get_error_code();
+               default:
+                       assert(false);
+                       return EXIT_FAILURE;
+               }
+       }
 
-       return ret;
 }
diff --git a/src/server/server.c b/src/server/server.c
index a2a43a4833..aedb9e6d34 100644
--- a/src/server/server.c
+++ b/src/server/server.c
@@ -37,16 +37,12 @@
 
 static struct service *services;
 
-enum shutdown_reason {
-       CONTINUE_MAIN_LOOP,                     /* stay in main event loop */
-       SHUTDOWN_REQUESTED,                     /* set by shutdown command; 
exit the event loop and quit the debugger */
-       SHUTDOWN_WITH_ERROR_CODE,       /* set by shutdown command; quit with 
non-zero return code */
-       SHUTDOWN_WITH_SIGNAL_CODE       /* set by sig_handler; exec shutdown 
then exit with signal as return code */
-};
-
 static volatile sig_atomic_t shutdown_openocd = CONTINUE_MAIN_LOOP;
-/* store received signal to exit application by killing ourselves */
+/* Received signal number, later used to kill ourselves.
+ * Only relevant for SHUTDOWN_WITH_SIGNAL_CODE. */
 static volatile sig_atomic_t last_signal;
+/* Exit code to use. Only relevant for SHUTDOWN_WITH_ERROR_CODE. */
+static uint8_t openocd_error_code;
 
 /* set the polling period to 100ms */
 static int polling_period = 100;
@@ -414,7 +410,7 @@ void server_keep_clients_alive(void)
                                s->keep_client_alive(c);
 }
 
-int server_loop(struct command_context *command_context)
+void server_loop(struct command_context *command_context)
 {
        struct service *service;
 
@@ -489,7 +485,9 @@ int server_loop(struct command_context *command_context)
                                FD_ZERO(&read_fds);
                        else {
                                LOG_ERROR("error during select: %s", 
strerror(errno));
-                               return ERROR_FAIL;
+                               shutdown_openocd = SHUTDOWN_WITH_ERROR_CODE;
+                               openocd_error_code = EXIT_FAILURE;
+                               return;
                        }
 #else
 
@@ -497,7 +495,9 @@ int server_loop(struct command_context *command_context)
                                FD_ZERO(&read_fds);
                        else {
                                LOG_ERROR("error during select: %s", 
strerror(errno));
-                               return ERROR_FAIL;
+                               shutdown_openocd = SHUTDOWN_WITH_ERROR_CODE;
+                               openocd_error_code = EXIT_FAILURE;
+                               return;
                        }
 #endif
                }
@@ -591,7 +591,22 @@ int server_loop(struct command_context *command_context)
        if (shutdown_openocd == SHUTDOWN_WITH_SIGNAL_CODE)
                command_run_line(command_context, "shutdown");
 
-       return shutdown_openocd == SHUTDOWN_WITH_ERROR_CODE ? ERROR_FAIL : 
ERROR_OK;
+       return;
+}
+
+enum shutdown_reason server_get_shutdown_reason(void)
+{
+       return shutdown_openocd;
+}
+
+int server_get_last_signal(void)
+{
+       return last_signal;
+}
+
+uint8_t server_get_error_code(void)
+{
+       return openocd_error_code;
 }
 
 static void sig_handler(int sig)
@@ -691,19 +706,14 @@ int server_init(struct command_context *cmd_ctx)
        return ERROR_OK;
 }
 
-int server_quit(void)
+void server_quit(void)
 {
        remove_services();
        target_quit();
 
 #ifdef _WIN32
        SetConsoleCtrlHandler(control_handler, FALSE);
-
-       return ERROR_OK;
 #endif
-
-       /* return signal number so we can kill ourselves */
-       return last_signal;
 }
 
 void server_free(void)
@@ -716,12 +726,16 @@ void server_free(void)
        free(bindto_name);
 }
 
-void exit_on_signal(int sig)
+int exit_on_signal(int sig)
 {
 #ifndef _WIN32
-       /* bring back default system handler and kill yourself */
+       /* *nix: Bring back the default system handler and kill self */
        signal(sig, SIG_DFL);
-       kill(getpid(), sig);
+       kill(getpid(), sig); /* does not return */
+       __builtin_unreachable();
+#else
+       /* On Windows, simply use the signal number as the exit code */
+       return sig;
 #endif
 }
 
@@ -753,19 +767,60 @@ bool openocd_is_shutdown_pending(void)
 /* tell the server we want to shut down */
 COMMAND_HANDLER(handle_shutdown_command)
 {
+       if (CMD_ARGC > 2)
+               return ERROR_COMMAND_SYNTAX_ERROR;
+
        LOG_USER("shutdown command invoked");
 
-       shutdown_openocd = SHUTDOWN_REQUESTED;
+       if (CMD_ARGC >= 1) {
+               if (strcmp(CMD_ARGV[0], "error") != 0)
+                       return ERROR_COMMAND_SYNTAX_ERROR;
 
-       command_run_line(CMD_CTX, "_run_pre_shutdown_commands");
+               if (CMD_ARGC == 2) {
+                       uint8_t error_code;
+                       COMMAND_PARSE_NUMBER(u8, CMD_ARGV[1], error_code);
+                       if (error_code == 0) {
+                               LOG_ERROR("Error code used in shutdown command 
must be non-zero");
+                               return ERROR_COMMAND_ARGUMENT_INVALID;
+                       }
 
-       if (CMD_ARGC == 1) {
-               if (!strcmp(CMD_ARGV[0], "error")) {
+                       LOG_DEBUG("Will quit with user-provided error code %" 
PRIu8, error_code);
                        shutdown_openocd = SHUTDOWN_WITH_ERROR_CODE;
-                       return ERROR_FAIL;
+                       openocd_error_code = error_code;
+               }
+               else {
+                       LOG_DEBUG("Will quit with a non-zero error code");
+                       shutdown_openocd = SHUTDOWN_WITH_ERROR_CODE;
+                       openocd_error_code = EXIT_FAILURE;
+               }
+       } else {
+               if (shutdown_openocd == SHUTDOWN_WITH_SIGNAL_CODE) {
+                       /* When "shutdown" is auto-executed as a result of a 
signal,
+                        * keep the original shutdown reason unchanged. */
+               } else {
+                       LOG_DEBUG("Will quit with success code (zero)");
+                       shutdown_openocd = SHUTDOWN_REQUESTED;
+                       openocd_error_code = 0;
                }
        }
 
+       command_run_line(CMD_CTX, "_run_pre_shutdown_commands");
+
+       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;
 }
 
@@ -817,8 +872,15 @@ static const struct command_registration 
server_command_handlers[] = {
                .name = "shutdown",
                .handler = &handle_shutdown_command,
                .mode = COMMAND_ANY,
+               .usage = "['error' [concrete_error_code]]",
+               .help = "shut down OpenOCD process, optionally specify an error 
exit code",
+       },
+       {
+               .name = "exit",
+               .handler = handle_exit_command,
+               .mode = COMMAND_ANY,
                .usage = "",
-               .help = "shut the server down",
+               .help = "exit (disconnect) telnet or Tcl session",
        },
        {
                .name = "exit",
diff --git a/src/server/server.h b/src/server/server.h
index 393dba7691..a2096965e3 100644
--- a/src/server/server.h
+++ b/src/server/server.h
@@ -33,6 +33,13 @@ enum connection_type {
 
 #define CONNECTION_LIMIT_UNLIMITED             (-1)
 
+enum shutdown_reason {
+       CONTINUE_MAIN_LOOP,                     /* stay in main event loop */
+       SHUTDOWN_REQUESTED,                     /* set by shutdown command; 
exit the event loop and quit the debugger */
+       SHUTDOWN_WITH_ERROR_CODE,       /* set by shutdown command; quit with 
non-zero return code */
+       SHUTDOWN_WITH_SIGNAL_CODE       /* set by sig_handler; exec shutdown 
then exit with signal as return code */
+};
+
 struct connection {
        int fd;
        int fd_out;     /* When using pipes we're writing to a different fd */
@@ -93,13 +100,16 @@ int server_host_os_close(void);
 
 int server_preinit(void);
 int server_init(struct command_context *cmd_ctx);
-int server_quit(void);
+void server_quit(void);
 void server_free(void);
-void exit_on_signal(int sig);
+int exit_on_signal(int sig);
 
 void server_keep_clients_alive(void);
 
-int server_loop(struct command_context *command_context);
+void server_loop(struct command_context *command_context);
+enum shutdown_reason server_get_shutdown_reason(void);
+int server_get_last_signal(void);
+uint8_t server_get_error_code(void);
 
 int server_register_commands(struct command_context *context);
 

-- 

Reply via email to