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