]> git.sur5r.net Git - openocd/blobdiff - src/server/server.c
server: explicitly call "shutdown" when catch CTRL-C or a signal
[openocd] / src / server / server.c
index 5e1ae36b1db0d84e2178b392f5b2b4d31f61309e..bc5856cccbc88c03ce91ee0c18d052559fdd6aae 100644 (file)
@@ -19,9 +19,7 @@
  *   GNU General Public License for more details.                          *
  *                                                                         *
  *   You should have received a copy of the GNU General Public License     *
- *   along with this program; if not, write to the                         *
- *   Free Software Foundation, Inc.,                                       *
- *   51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.           *
+ *   along with this program.  If not, see <http://www.gnu.org/licenses/>. *
  ***************************************************************************/
 
 #ifdef HAVE_CONFIG_H
 #include "server.h"
 #include <target/target.h>
 #include <target/target_request.h>
+#include <target/openrisc/jsp_server.h>
 #include "openocd.h"
 #include "tcl_server.h"
 #include "telnet_server.h"
 
 #include <signal.h>
 
+#ifdef HAVE_NETDB_H
+#include <netdb.h>
+#endif
+
 #ifndef _WIN32
 #include <netinet/tcp.h>
 #endif
 
 static struct service *services;
 
-/* shutdown_openocd == 1: exit the main event loop, and quit the debugger */
-static int shutdown_openocd;
+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 enum shutdown_reason shutdown_openocd = CONTINUE_MAIN_LOOP;
+
+/* store received signal to exit application by killing ourselves */
+static int last_signal;
+
+/* set the polling period to 100ms */
+static int polling_period = 100;
+
+/* address by name on which to listen for incoming TCP/IP connections */
+static char *bindto_name;
 
 static int add_connection(struct service *service, struct command_context *cmd_ctx)
 {
@@ -80,7 +97,7 @@ static int add_connection(struct service *service, struct command_context *cmd_c
                        (char *)&flag,                  /* the cast is historical cruft */
                        sizeof(int));                   /* length of option value */
 
-               LOG_INFO("accepting '%s' connection from %s", service->name, service->port);
+               LOG_INFO("accepting '%s' connection on tcp/%s", service->name, service->port);
                retval = service->new_connection(c);
                if (retval != ERROR_OK) {
                        close_socket(c->fd);
@@ -119,7 +136,9 @@ static int add_connection(struct service *service, struct command_context *cmd_c
                free(out_file);
                if (c->fd_out == -1) {
                        LOG_ERROR("could not open %s", service->port);
-                       exit(1);
+                       command_done(c->cmd_ctx);
+                       free(c);
+                       return ERROR_FAIL;
                }
 
                LOG_INFO("accepting '%s' connection from pipe %s", service->name, service->port);
@@ -137,7 +156,8 @@ static int add_connection(struct service *service, struct command_context *cmd_c
                ;
        *p = c;
 
-       service->max_connections--;
+       if (service->max_connections != CONNECTION_LIMIT_UNLIMITED)
+               service->max_connections--;
 
        return ERROR_OK;
 }
@@ -164,7 +184,9 @@ static int remove_connection(struct service *service, struct connection *connect
                        *p = c->next;
                        free(c);
 
-                       service->max_connections++;
+                       if (service->max_connections != CONNECTION_LIMIT_UNLIMITED)
+                               service->max_connections++;
+
                        break;
                }
 
@@ -175,7 +197,13 @@ static int remove_connection(struct service *service, struct connection *connect
        return ERROR_OK;
 }
 
-/* FIX! make service return error instead of invoking exit() */
+static void free_service(struct service *c)
+{
+       free(c->name);
+       free(c->port);
+       free(c);
+}
+
 int add_service(char *name,
        const char *port,
        int max_connections,
@@ -185,6 +213,7 @@ int add_service(char *name,
        void *priv)
 {
        struct service *c, **p;
+       struct hostent *hp;
        int so_reuseaddr_option = 1;
 
        c = malloc(sizeof(struct service));
@@ -218,7 +247,8 @@ int add_service(char *name,
                c->fd = socket(AF_INET, SOCK_STREAM, 0);
                if (c->fd == -1) {
                        LOG_ERROR("error creating socket: %s", strerror(errno));
-                       exit(-1);
+                       free_service(c);
+                       return ERROR_FAIL;
                }
 
                setsockopt(c->fd,
@@ -231,12 +261,26 @@ int add_service(char *name,
 
                memset(&c->sin, 0, sizeof(c->sin));
                c->sin.sin_family = AF_INET;
-               c->sin.sin_addr.s_addr = INADDR_ANY;
+
+               if (bindto_name == NULL)
+                       c->sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+               else {
+                       hp = gethostbyname(bindto_name);
+                       if (hp == NULL) {
+                               LOG_ERROR("couldn't resolve bindto address: %s", bindto_name);
+                               close_socket(c->fd);
+                               free_service(c);
+                               return ERROR_FAIL;
+                       }
+                       memcpy(&c->sin.sin_addr, hp->h_addr_list[0], hp->h_length);
+               }
                c->sin.sin_port = htons(c->portnumber);
 
                if (bind(c->fd, (struct sockaddr *)&c->sin, sizeof(c->sin)) == -1) {
-                       LOG_ERROR("couldn't bind to socket: %s", strerror(errno));
-                       exit(-1);
+                       LOG_ERROR("couldn't bind %s to socket on port %d: %s", name, c->portnumber, strerror(errno));
+                       close_socket(c->fd);
+                       free_service(c);
+                       return ERROR_FAIL;
                }
 
 #ifndef _WIN32
@@ -254,8 +298,17 @@ int add_service(char *name,
 
                if (listen(c->fd, 1) == -1) {
                        LOG_ERROR("couldn't listen on socket: %s", strerror(errno));
-                       exit(-1);
+                       close_socket(c->fd);
+                       free_service(c);
+                       return ERROR_FAIL;
                }
+
+               struct sockaddr_in addr_in;
+               addr_in.sin_port = 0;
+               socklen_t addr_in_size = sizeof(addr_in);
+               if (getsockname(c->fd, (struct sockaddr *)&addr_in, &addr_in_size) == 0)
+                       LOG_INFO("Listening on port %hu for %s connections",
+                                ntohs(addr_in.sin_port), name);
        } else if (c->type == CONNECTION_STDINOUT) {
                c->fd = fileno(stdin);
 
@@ -275,13 +328,15 @@ int add_service(char *name,
                /* we currenty do not support named pipes under win32
                 * so exit openocd for now */
                LOG_ERROR("Named pipes currently not supported under this os");
-               exit(1);
+               free_service(c);
+               return ERROR_FAIL;
 #else
                /* Pipe we're reading from */
                c->fd = open(c->port, O_RDONLY | O_NONBLOCK);
                if (c->fd == -1) {
                        LOG_ERROR("could not open %s", c->port);
-                       exit(1);
+                       free_service(c);
+                       return ERROR_FAIL;
                }
 #endif
        }
@@ -294,6 +349,21 @@ int add_service(char *name,
        return ERROR_OK;
 }
 
+static void remove_connections(struct service *service)
+{
+       struct connection *connection;
+
+       connection = service->connections;
+
+       while (connection) {
+               struct connection *tmp;
+
+               tmp = connection->next;
+               remove_connection(service, connection);
+               connection = tmp;
+       }
+}
+
 static int remove_services(void)
 {
        struct service *c = services;
@@ -302,6 +372,8 @@ static int remove_services(void)
        while (c) {
                struct service *next = c->next;
 
+               remove_connections(c);
+
                if (c->name)
                        free(c->name);
 
@@ -345,7 +417,7 @@ int server_loop(struct command_context *command_context)
                LOG_ERROR("couldn't set SIGPIPE to SIG_IGN");
 #endif
 
-       while (!shutdown_openocd) {
+       while (shutdown_openocd == CONTINUE_MAIN_LOOP) {
                /* monitor sockets for activity */
                fd_max = 0;
                FD_ZERO(&read_fds);
@@ -380,8 +452,8 @@ int server_loop(struct command_context *command_context)
                        tv.tv_usec = 0;
                        retval = socket_select(fd_max + 1, &read_fds, NULL, NULL, &tv);
                } else {
-                       /* Every 100ms */
-                       tv.tv_usec = 100000;
+                       /* Every 100ms, can be changed with "poll_period" command */
+                       tv.tv_usec = polling_period * 1000;
                        /* Only while we're sleeping we'll let others run */
                        openocd_sleep_prelude();
                        kept_alive();
@@ -398,7 +470,7 @@ int server_loop(struct command_context *command_context)
                                FD_ZERO(&read_fds);
                        else {
                                LOG_ERROR("error during select: %s", strerror(errno));
-                               exit(-1);
+                               return ERROR_FAIL;
                        }
 #else
 
@@ -406,7 +478,7 @@ int server_loop(struct command_context *command_context)
                                FD_ZERO(&read_fds);
                        else {
                                LOG_ERROR("error during select: %s", strerror(errno));
-                               exit(-1);
+                               return ERROR_FAIL;
                        }
 #endif
                }
@@ -437,8 +509,8 @@ int server_loop(struct command_context *command_context)
                for (service = services; service; service = service->next) {
                        /* handle new connections on listeners */
                        if ((service->fd != -1)
-                           && (FD_ISSET(service->fd, &read_fds))) {
-                               if (service->max_connections > 0)
+                               && (FD_ISSET(service->fd, &read_fds))) {
+                               if (service->max_connections != 0)
                                        add_connection(service, command_context);
                                else {
                                        if (service->type == CONNECTION_TCP) {
@@ -469,7 +541,7 @@ int server_loop(struct command_context *command_context)
                                                                        service->type == CONNECTION_STDINOUT) {
                                                                /* if connection uses a pipe then
                                                                 * shutdown openocd on error */
-                                                               shutdown_openocd = 1;
+                                                               shutdown_openocd = SHUTDOWN_REQUESTED;
                                                        }
                                                        remove_connection(service, c);
                                                        LOG_INFO("dropped '%s' connection",
@@ -487,27 +559,49 @@ int server_loop(struct command_context *command_context)
                MSG msg;
                while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
                        if (msg.message == WM_QUIT)
-                               shutdown_openocd = 1;
+                               shutdown_openocd = SHUTDOWN_WITH_SIGNAL_CODE;
                }
 #endif
        }
 
-       return ERROR_OK;
+       /* when quit for signal or CTRL-C, run (eventually user implemented) "shutdown" */
+       if (shutdown_openocd == SHUTDOWN_WITH_SIGNAL_CODE)
+               command_run_line(command_context, "shutdown");
+
+       return shutdown_openocd == SHUTDOWN_WITH_ERROR_CODE ? ERROR_FAIL : ERROR_OK;
+}
+
+void sig_handler(int sig)
+{
+       /* store only first signal that hits us */
+       if (shutdown_openocd == CONTINUE_MAIN_LOOP) {
+               shutdown_openocd = SHUTDOWN_WITH_SIGNAL_CODE;
+               last_signal = sig;
+               LOG_DEBUG("Terminating on Signal %d", sig);
+       } else
+               LOG_DEBUG("Ignored extra Signal %d", sig);
 }
 
+
 #ifdef _WIN32
 BOOL WINAPI ControlHandler(DWORD dwCtrlType)
 {
-       shutdown_openocd = 1;
+       shutdown_openocd = SHUTDOWN_WITH_SIGNAL_CODE;
        return TRUE;
 }
-
-void sig_handler(int sig)
+#else
+static void sigkey_handler(int sig)
 {
-       shutdown_openocd = 1;
+       /* ignore keystroke generated signals if not in foreground process group */
+
+       if (tcgetpgrp(STDIN_FILENO) > 0)
+               sig_handler(sig);
+       else
+               LOG_DEBUG("Ignored Signal %d", sig);
 }
 #endif
 
+
 int server_preinit(void)
 {
        /* this currently only calls WSAStartup on native win32 systems
@@ -522,17 +616,22 @@ int server_preinit(void)
 
        if (WSAStartup(wVersionRequested, &wsaData) != 0) {
                LOG_ERROR("Failed to Open Winsock");
-               exit(-1);
+               return ERROR_FAIL;
        }
 
        /* register ctrl-c handler */
        SetConsoleCtrlHandler(ControlHandler, TRUE);
 
+       signal(SIGBREAK, sig_handler);
        signal(SIGINT, sig_handler);
+#else
+       signal(SIGHUP, sig_handler);
+       signal(SIGPIPE, sig_handler);
+       signal(SIGQUIT, sigkey_handler);
+       signal(SIGINT, sigkey_handler);
+#endif
        signal(SIGTERM, sig_handler);
-       signal(SIGBREAK, sig_handler);
        signal(SIGABRT, sig_handler);
-#endif
 
        return ERROR_OK;
 }
@@ -540,22 +639,50 @@ int server_preinit(void)
 int server_init(struct command_context *cmd_ctx)
 {
        int ret = tcl_init();
-       if (ERROR_OK != ret)
+
+       if (ret != ERROR_OK)
                return ret;
 
-       return telnet_init("Open On-Chip Debugger");
+       ret = telnet_init("Open On-Chip Debugger");
+
+       if (ret != ERROR_OK) {
+               remove_services();
+               return ret;
+       }
+
+       return ERROR_OK;
 }
 
 int server_quit(void)
 {
        remove_services();
+       target_quit();
 
 #ifdef _WIN32
        WSACleanup();
        SetConsoleCtrlHandler(ControlHandler, FALSE);
-#endif
 
        return ERROR_OK;
+#endif
+
+       /* return signal number so we can kill ourselves */
+       return last_signal;
+}
+
+void server_free(void)
+{
+       tcl_service_free();
+       telnet_service_free();
+       jsp_service_free();
+}
+
+void exit_on_signal(int sig)
+{
+#ifndef _WIN32
+       /* bring back default system handler and kill yourself */
+       signal(sig, SIG_DFL);
+       kill(getpid(), sig);
+#endif
 }
 
 int connection_write(struct connection *connection, const void *data, int len)
@@ -583,11 +710,46 @@ COMMAND_HANDLER(handle_shutdown_command)
 {
        LOG_USER("shutdown command invoked");
 
-       shutdown_openocd = 1;
+       shutdown_openocd = SHUTDOWN_REQUESTED;
+
+       if (CMD_ARGC == 1) {
+               if (!strcmp(CMD_ARGV[0], "error")) {
+                       shutdown_openocd = SHUTDOWN_WITH_ERROR_CODE;
+                       return ERROR_FAIL;
+               }
+       }
+
+       return ERROR_COMMAND_CLOSE_CONNECTION;
+}
+
+COMMAND_HANDLER(handle_poll_period_command)
+{
+       if (CMD_ARGC == 0)
+               LOG_WARNING("You need to set a period value");
+       else
+               COMMAND_PARSE_NUMBER(int, CMD_ARGV[0], polling_period);
+
+       LOG_INFO("set servers polling period to %ums", polling_period);
 
        return ERROR_OK;
 }
 
+COMMAND_HANDLER(handle_bindto_command)
+{
+       switch (CMD_ARGC) {
+               case 0:
+                       command_print(CMD_CTX, "bindto name: %s", bindto_name);
+                       break;
+               case 1:
+                       free(bindto_name);
+                       bindto_name = strdup(CMD_ARGV[0]);
+                       break;
+               default:
+                       return ERROR_COMMAND_SYNTAX_ERROR;
+       }
+       return ERROR_OK;
+}
+
 static const struct command_registration server_command_handlers[] = {
        {
                .name = "shutdown",
@@ -596,6 +758,21 @@ static const struct command_registration server_command_handlers[] = {
                .usage = "",
                .help = "shut the server down",
        },
+       {
+               .name = "poll_period",
+               .handler = &handle_poll_period_command,
+               .mode = COMMAND_ANY,
+               .usage = "",
+               .help = "set the servers polling period",
+       },
+       {
+               .name = "bindto",
+               .handler = &handle_bindto_command,
+               .mode = COMMAND_ANY,
+               .usage = "[name]",
+               .help = "Specify address by name on which to listen for "
+                       "incoming TCP/IP connections",
+       },
        COMMAND_REGISTRATION_DONE
 };
 
@@ -609,6 +786,10 @@ int server_register_commands(struct command_context *cmd_ctx)
        if (ERROR_OK != retval)
                return retval;
 
+       retval = jsp_register_commands(cmd_ctx);
+       if (ERROR_OK != retval)
+               return retval;
+
        return register_commands(cmd_ctx, NULL, server_command_handlers);
 }