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

Linking to existing account procedure

If you already have an account and want to add another login method you MUST first sign in with your existing account and then change URL to read https://review.openocd.org/login/?link to get to this page again but this time it'll work for linking. Thank you.

SSH host keys fingerprints

1024 SHA256:YKx8b7u5ZWdcbp7/4AeXNaqElP49m6QrwfXaqQGJAOk gerrit-code-review@openocd.zylin.com (DSA)
384 SHA256:jHIbSQa4REvwCFG4cq5LBlBLxmxSqelQPem/EXIrxjk gerrit-code-review@openocd.org (ECDSA)
521 SHA256:UAOPYkU9Fjtcao0Ul/Rrlnj/OsQvt+pgdYSZ4jOYdgs gerrit-code-review@openocd.org (ECDSA)
256 SHA256:A13M5QlnozFOvTllybRZH6vm7iSt0XLxbA48yfc2yfY gerrit-code-review@openocd.org (ECDSA)
256 SHA256:spYMBqEYoAOtK7yZBrcwE8ZpYt6b68Cfh9yEVetvbXg gerrit-code-review@openocd.org (ED25519)
+--[ED25519 256]--+
|=..              |
|+o..   .         |
|*.o   . .        |
|+B . . .         |
|Bo. = o S        |
|Oo.+ + =         |
|oB=.* = . o      |
| =+=.+   + E     |
|. .=o   . o      |
+----[SHA256]-----+
2048 SHA256:0Onrb7/PHjpo6iVZ7xQX2riKN83FJ3KGU0TvI0TaFG4 gerrit-code-review@openocd.zylin.com (RSA)