Duane Ellis: fix warnings
[fw/openocd] / src / server / server.c
1 /***************************************************************************
2  *   Copyright (C) 2005 by Dominic Rath                                    *
3  *   Dominic.Rath@gmx.de                                                   *
4  *                                                                         *
5  *   Copyright (C) 2007,2008 Ã˜yvind Harboe                                      *
6  *   oyvind.harboe@zylin.com                                               *
7  *                                                                         *
8  *   This program is free software; you can redistribute it and/or modify  *
9  *   it under the terms of the GNU General Public License as published by  *
10  *   the Free Software Foundation; either version 2 of the License, or     *
11  *   (at your option) any later version.                                   *
12  *                                                                         *
13  *   This program is distributed in the hope that it will be useful,       *
14  *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
15  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
16  *   GNU General Public License for more details.                          *
17  *                                                                         *
18  *   You should have received a copy of the GNU General Public License     *
19  *   along with this program; if not, write to the                         *
20  *   Free Software Foundation, Inc.,                                       *
21  *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
22  ***************************************************************************/
23 #ifdef HAVE_CONFIG_H
24 #include "config.h"
25 #endif
26
27 #include "replacements.h"
28
29 #include "server.h"
30
31 #include "log.h"
32 #include "telnet_server.h"
33 #include "target.h"
34
35 #include <command.h>
36 #include <string.h>
37 #include <stdlib.h>
38 #include <errno.h>
39 #include <unistd.h>
40 #include <sys/types.h>
41 #include <fcntl.h>
42 #include <signal.h>
43 #ifndef _WIN32
44 #include <netinet/tcp.h>
45 #endif
46
47 service_t *services = NULL;
48
49 /* shutdown_openocd == 1: exit the main event loop, and quit the debugger */
50 static int shutdown_openocd = 0;
51 int handle_shutdown_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc);
52
53 int add_connection(service_t *service, command_context_t *cmd_ctx)
54 {
55         unsigned int address_size;
56         connection_t *c, **p;
57         int retval;
58         int flag=1;
59         
60         c = malloc(sizeof(connection_t));
61         c->fd = -1;
62         memset(&c->sin, 0, sizeof(c->sin));
63         c->cmd_ctx = copy_command_context(cmd_ctx);
64         c->service = service;
65         c->input_pending = 0;
66         c->priv = NULL;
67         c->next = NULL;
68
69         address_size = sizeof(c->sin);
70         
71         c->fd = accept(service->fd, (struct sockaddr *)&service->sin, &address_size);
72         
73         /* This increases performance dramatically for e.g. GDB load which
74          * does not have a sliding window protocol. */
75         retval=setsockopt(c->fd,        /* socket affected */
76                         IPPROTO_TCP,            /* set option at TCP level */
77                         TCP_NODELAY,            /* name of option */
78                         (char *)&flag,          /* the cast is historical cruft */
79                         sizeof(int));           /* length of option value */
80                 
81         LOG_INFO("accepting '%s' connection from %i", service->name, c->sin.sin_port);
82         if ((retval = service->new_connection(c)) == ERROR_OK)
83         {
84         }
85         else
86         {
87                 close_socket(c->fd);
88                 LOG_ERROR("attempted '%s' connection rejected", service->name);
89                 free(c);
90                 return retval;
91         }
92         
93         /* add to the end of linked list */
94         for (p = &service->connections; *p; p = &(*p)->next);
95         *p = c;
96         
97         service->max_connections--;
98         
99         return ERROR_OK;
100 }
101
102 int remove_connection(service_t *service, connection_t *connection)
103 {
104         connection_t **p = &service->connections;
105         connection_t *c;
106         
107         /* find connection */
108         while((c = *p))
109         {               
110                 if (c->fd == connection->fd)
111                 {       
112                         service->connection_closed(c);
113                         close_socket(c->fd);
114                         command_done(c->cmd_ctx);
115                         
116                         /* delete connection */
117                         *p = c->next;
118                         free(c);
119                         
120                         service->max_connections++;
121                         break;
122                 }
123                 
124                 /* redirect p to next list pointer */
125                 p = &(*p)->next;                
126         }
127         
128         return ERROR_OK;
129 }
130
131 int add_service(char *name, enum connection_type type, unsigned short port, int max_connections, new_connection_handler_t new_connection_handler, input_handler_t input_handler, connection_closed_handler_t connection_closed_handler, void *priv)
132 {
133         service_t *c, **p;
134         int so_reuseaddr_option = 1;
135         
136         c = malloc(sizeof(service_t));
137         
138         c->name = strdup(name);
139         c->type = type;
140         c->port = port;
141         c->max_connections = max_connections;
142         c->fd = -1;
143         c->connections = NULL;
144         c->new_connection = new_connection_handler;
145         c->input = input_handler;
146         c->connection_closed = connection_closed_handler;
147         c->priv = priv;
148         c->next = NULL;
149         
150         if ((c->fd = socket(AF_INET, SOCK_STREAM, 0)) == -1)
151         {
152                 LOG_ERROR("error creating socket: %s", strerror(errno));
153                 exit(-1);
154         }
155         
156         setsockopt(c->fd, SOL_SOCKET, SO_REUSEADDR, (void*)&so_reuseaddr_option, sizeof(int));
157         
158         socket_nonblock(c->fd);
159         
160         memset(&c->sin, 0, sizeof(c->sin));
161         c->sin.sin_family = AF_INET;
162         c->sin.sin_addr.s_addr = INADDR_ANY;
163         c->sin.sin_port = htons(port);
164         
165         if (bind(c->fd, (struct sockaddr *)&c->sin, sizeof(c->sin)) == -1)
166         {
167                 LOG_ERROR("couldn't bind to socket: %s", strerror(errno));
168                 exit(-1);
169         }
170         
171 #ifndef _WIN32
172         int segsize=65536;
173         setsockopt(c->fd, IPPROTO_TCP, TCP_MAXSEG,  &segsize, sizeof(int));
174 #endif
175         int window_size = 128 * 1024;   
176
177         /* These setsockopt()s must happen before the listen() */
178         
179         setsockopt(c->fd, SOL_SOCKET, SO_SNDBUF,
180                 (char *)&window_size, sizeof(window_size));
181         setsockopt(c->fd, SOL_SOCKET, SO_RCVBUF,
182                 (char *)&window_size, sizeof(window_size));
183         
184         if (listen(c->fd, 1) == -1)
185         {
186                 LOG_ERROR("couldn't listen on socket: %s", strerror(errno));
187                 exit(-1);
188         }
189         
190         /* add to the end of linked list */
191         for (p = &services; *p; p = &(*p)->next);
192         *p = c;
193         
194         return ERROR_OK;
195 }
196
197 int remove_service(unsigned short port)
198 {
199         service_t **p = &services;
200         service_t *c;
201         
202         /* find service */
203         while((c = *p))
204         {               
205                 if (c->port == port)
206                 {       
207                         if (c->name)
208                                 free(c->name);
209                         
210                         if (c->priv)
211                                 free(c->priv);
212                         
213                         /* delete service */
214                         *p = c->next;
215                         free(c);
216                 }
217
218                 /* redirect p to next list pointer */
219                 p = &(*p)->next;
220         }
221         
222         return ERROR_OK;
223 }
224
225 int remove_services(void)
226 {
227         service_t *c = services;
228
229         /* loop service */
230         while(c)
231         {
232                 service_t *next = c->next;
233
234                 if (c->name)
235                         free(c->name);
236
237                 if (c->priv)
238                         free(c->priv);
239
240                 /* delete service */
241                 free(c);
242
243                 /* remember the last service for unlinking */
244                 c = next;
245         }
246
247         services = NULL;
248         
249         return ERROR_OK;
250 }
251
252 extern void openocd_sleep_prelude(void);
253 extern void openocd_sleep_postlude(void);
254
255 int server_loop(command_context_t *command_context)
256 {
257         service_t *service;
258
259         /* used in select() */
260         fd_set read_fds;
261         struct timeval tv;
262         int fd_max;
263         
264         /* used in accept() */
265         int retval;
266         
267 #ifndef _WIN32
268         if (signal(SIGPIPE, SIG_IGN) == SIG_ERR)
269                 LOG_ERROR("couldn't set SIGPIPE to SIG_IGN");
270 #endif
271
272         /* do regular tasks after at most 10ms */
273         tv.tv_sec = 0;
274         tv.tv_usec = 10000;
275         
276         while(!shutdown_openocd)
277         {
278                 /* monitor sockets for acitvity */
279                 fd_max = 0;
280                 FD_ZERO(&read_fds);
281
282                 /* add service and connection fds to read_fds */
283                 for (service = services; service; service = service->next)
284                 {
285                         if (service->fd != -1)
286                         {
287                                 /* listen for new connections */
288                                 FD_SET(service->fd, &read_fds);
289
290                                 if (service->fd > fd_max)
291                                         fd_max = service->fd;
292                         }
293                         
294                         if (service->connections)
295                         {
296                                 connection_t *c;
297                                 
298                                 for (c = service->connections; c; c = c->next)
299                                 {
300                                         /* check for activity on the connection */
301                                         FD_SET(c->fd, &read_fds);
302                                         if (c->fd > fd_max)
303                                                 fd_max = c->fd;
304                                 }
305                         }
306                 }
307                 
308 #ifndef _WIN32
309 #ifndef BUILD_ECOSBOARD
310                 /* add STDIN to read_fds */
311                 FD_SET(fileno(stdin), &read_fds);
312 #endif
313 #endif
314
315                 openocd_sleep_prelude();
316                 kept_alive();
317                 // Only while we're sleeping we'll let others run
318                 retval = select(fd_max + 1, &read_fds, NULL, NULL, &tv);
319                 openocd_sleep_postlude();
320
321                 if (retval == -1)
322                 {
323 #ifdef _WIN32
324
325                         errno = WSAGetLastError();
326
327                         if (errno == WSAEINTR)
328                                 FD_ZERO(&read_fds);
329                         else
330                         {
331                                 LOG_ERROR("error during select: %s", strerror(errno));
332                                 exit(-1);
333                         }
334 #else
335
336                         if (errno == EINTR)
337                         {
338                                 FD_ZERO(&read_fds);
339                         }
340                         else
341                         {
342                                 LOG_ERROR("error during select: %s", strerror(errno));
343                                 exit(-1);
344                         }
345 #endif
346                 }
347                 
348                 target_call_timer_callbacks();
349
350                 if (retval == 0)
351                 {
352                         /* do regular tasks after at most 100ms */
353                         tv.tv_sec = 0;
354                         tv.tv_usec = 10000;
355                         FD_ZERO(&read_fds); /* eCos leaves read_fds unchanged in this case!  */
356                 }
357                 
358                 for (service = services; service; service = service->next)
359                 {
360                         /* handle new connections on listeners */
361                         if ((service->fd != -1) 
362                                 && (FD_ISSET(service->fd, &read_fds))) 
363                         {
364                                 if (service->max_connections > 0)
365                                 {
366                                         add_connection(service, command_context);
367                                 }
368                                 else
369                                 {
370                                         struct sockaddr_in sin;
371                                         unsigned int address_size = sizeof(sin);
372                                         int tmp_fd;
373                                         tmp_fd = accept(service->fd, (struct sockaddr *)&service->sin, &address_size);
374                                         close_socket(tmp_fd);
375                                         LOG_INFO("rejected '%s' connection, no more connections allowed", service->name);
376                                 }
377                         }
378                         
379                         /* handle activity on connections */
380                         if (service->connections)
381                         {
382                                 connection_t *c;
383                                 
384                                 for (c = service->connections; c;)
385                                 {
386                                         if ((FD_ISSET(c->fd, &read_fds)) || c->input_pending)
387                                         {
388                                                 if (service->input(c) != ERROR_OK)
389                                                 {
390                                                         connection_t *next = c->next;
391                                                         remove_connection(service, c);
392                                                         LOG_INFO("dropped '%s' connection", service->name);
393                                                         c = next;
394                                                         continue;
395                                                 }
396                                         }
397                                         c = c->next;
398                                 }
399                         }
400                 }
401                 
402 #ifndef _WIN32
403 #ifndef BUILD_ECOSBOARD
404                 if (FD_ISSET(fileno(stdin), &read_fds))
405                 {
406                         if (getc(stdin) == 'x')
407                         {
408                                 shutdown_openocd = 1;
409                         }
410                 }
411 #endif
412 #else
413                 MSG msg;
414                 while (PeekMessage(&msg,NULL,0,0,PM_REMOVE))
415                 {
416                         if (msg.message == WM_QUIT)
417                                 shutdown_openocd = 1;
418                 }
419 #endif
420         }
421         
422         return ERROR_OK;
423 }
424
425 #ifdef _WIN32
426 BOOL WINAPI ControlHandler(DWORD dwCtrlType)
427 {
428     shutdown_openocd = 1;
429     return TRUE;
430 }
431
432 void sig_handler(int sig) {
433     shutdown_openocd = 1;
434 }
435 #endif
436
437 int server_init(void)
438 {
439 #ifdef _WIN32
440         WORD wVersionRequested;
441         WSADATA wsaData;
442
443         wVersionRequested = MAKEWORD( 2, 2 );
444
445         if (WSAStartup(wVersionRequested, &wsaData) != 0)
446         {
447                 LOG_ERROR("Failed to Open Winsock");
448                 exit(-1);
449         }
450
451         SetConsoleCtrlHandler( ControlHandler, TRUE );
452
453         signal(SIGINT, sig_handler);
454         signal(SIGTERM, sig_handler);
455         signal(SIGBREAK, sig_handler);
456         signal(SIGABRT, sig_handler);
457 #endif
458
459         
460         return ERROR_OK;
461 }
462
463 int server_quit(void)
464 {
465         remove_services();
466
467 #ifdef _WIN32
468         WSACleanup();
469         SetConsoleCtrlHandler( ControlHandler, FALSE );
470 #endif
471
472         return ERROR_OK;
473 }
474
475 int server_register_commands(command_context_t *context)
476 {
477         register_command(context, NULL, "shutdown", handle_shutdown_command,
478                                          COMMAND_ANY, "shut the server down");
479         
480         return ERROR_OK;
481 }
482
483 /* tell the server we want to shut down */
484 int handle_shutdown_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc)
485 {
486         shutdown_openocd = 1;
487
488         return ERROR_COMMAND_CLOSE_CONNECTION;
489 }
490
491