]> git.ipfire.org Git - thirdparty/binutils-gdb.git/blob - sim/common/dv-sockser.c
d5e657732dd863b73058eeaa84bb634fcf4052b6
[thirdparty/binutils-gdb.git] / sim / common / dv-sockser.c
1 /* Serial port emulation using sockets.
2 Copyright (C) 1998 Free Software Foundation, Inc.
3 Contributed by Cygnus Solutions.
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, or (at your option)
8 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 along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
18
19 /* FIXME: will obviously need to evolve.
20 - connectionless sockets might be more appropriate. */
21
22 #include "sim-main.h"
23
24 #ifdef HAVE_STRING_H
25 #include <string.h>
26 #else
27 #ifdef HAVE_STRINGS_H
28 #include <strings.h>
29 #endif
30 #endif
31 #include <signal.h>
32 #ifdef HAVE_STDLIB_H
33 #include <stdlib.h>
34 #endif
35 #ifdef HAVE_FCNTL_H
36 #include <fcntl.h>
37 #endif
38 #ifdef HAVE_UNISTD_H
39 #include <unistd.h>
40 #endif
41
42 #include <errno.h>
43 #include <sys/types.h>
44 #include <sys/time.h>
45 #include <netinet/in.h>
46 #include <arpa/inet.h>
47 #include <netdb.h>
48 #include <sys/socket.h>
49
50 #ifndef __CYGWIN32__
51 #include <netinet/tcp.h>
52 #endif
53
54 #include "sim-assert.h"
55 #include "sim-options.h"
56
57 #include "dv-sockser.h"
58 \f
59 /* Get definitions for both O_NONBLOCK and O_NDELAY. */
60
61 #ifndef O_NDELAY
62 #ifdef FNDELAY
63 #define O_NDELAY FNDELAY
64 #else /* ! defined (FNDELAY) */
65 #define O_NDELAY 0
66 #endif /* ! defined (FNDELAY) */
67 #endif /* ! defined (O_NDELAY) */
68
69 #ifndef O_NONBLOCK
70 #ifdef FNBLOCK
71 #define O_NONBLOCK FNBLOCK
72 #else /* ! defined (FNBLOCK) */
73 #define O_NONBLOCK 0
74 #endif /* ! defined (FNBLOCK) */
75 #endif /* ! defined (O_NONBLOCK) */
76 \f
77
78 /* Compromise between eating cpu and properly busy-waiting.
79 One could have an option to set this but for now that seems
80 like featuritis. */
81 #define DEFAULT_TIMEOUT 1000 /* microseconds */
82
83 /* FIXME: These should allocated at run time and kept with other simulator
84 state (duh...). Later. */
85 const char * sockser_addr = NULL;
86 /* Timeout in microseconds during status flag computation.
87 Setting this to zero achieves proper busy wait semantics but eats cpu. */
88 static unsigned int sockser_timeout = DEFAULT_TIMEOUT;
89 static int sockser_listen_fd = -1;
90 static int sockser_fd = -1;
91 \f
92 /* FIXME: use tree properties when they're ready. */
93
94 typedef enum {
95 OPTION_ADDR = OPTION_START
96 } SOCKSER_OPTIONS;
97
98 static DECLARE_OPTION_HANDLER (sockser_option_handler);
99
100 static const OPTION sockser_options[] =
101 {
102 { { "sockser-addr", required_argument, NULL, OPTION_ADDR },
103 '\0', "SOCKET ADDRESS", "Set serial emulation socket address",
104 sockser_option_handler },
105 { { NULL, no_argument, NULL, 0 }, '\0', NULL, NULL, NULL }
106 };
107
108 static SIM_RC
109 sockser_option_handler (SIM_DESC sd, sim_cpu *cpu, int opt,
110 char *arg, int is_command)
111 {
112 switch (opt)
113 {
114 case OPTION_ADDR :
115 sockser_addr = arg;
116 break;
117 }
118
119 return SIM_RC_OK;
120 }
121
122 static SIM_RC
123 dv_sockser_init (SIM_DESC sd)
124 {
125 struct hostent *hostent;
126 struct sockaddr_in sockaddr;
127 char hostname[100];
128 const char *port_str;
129 int tmp,port;
130
131 if (STATE_ENVIRONMENT (sd) != OPERATING_ENVIRONMENT
132 || sockser_addr == NULL)
133 return SIM_RC_OK;
134
135 if (*sockser_addr == '/')
136 {
137 /* support for these can come later */
138 sim_io_eprintf (sd, "sockser init: unix domain sockets not supported: `%s'\n",
139 sockser_addr);
140 return SIM_RC_FAIL;
141 }
142
143 port_str = strchr (sockser_addr, ':');
144 if (!port_str)
145 {
146 sim_io_eprintf (sd, "sockser init: missing port number: `%s'\n",
147 sockser_addr);
148 return SIM_RC_FAIL;
149 }
150 tmp = port_str - sockser_addr;
151 if (tmp >= sizeof hostname)
152 tmp = sizeof (hostname) - 1;
153 strncpy (hostname, sockser_addr, tmp);
154 hostname[tmp] = '\000';
155 port = atoi (port_str + 1);
156
157 hostent = gethostbyname (hostname);
158 if (! hostent)
159 {
160 sim_io_eprintf (sd, "sockser init: unknown host: %s\n",
161 hostname);
162 return SIM_RC_FAIL;
163 }
164
165 sockser_listen_fd = socket (PF_INET, SOCK_STREAM, 0);
166 if (sockser_listen_fd < 0)
167 {
168 sim_io_eprintf (sd, "sockser init: unable to get socket: %s\n",
169 strerror (errno));
170 return SIM_RC_FAIL;
171 }
172
173 sockaddr.sin_family = PF_INET;
174 sockaddr.sin_port = htons(port);
175 memcpy (&sockaddr.sin_addr.s_addr, hostent->h_addr,
176 sizeof (struct in_addr));
177
178 tmp = 1;
179 if (setsockopt (sockser_listen_fd, SOL_SOCKET, SO_REUSEADDR, (void*)& tmp, sizeof(tmp)) < 0)
180 {
181 sim_io_eprintf (sd, "sockser init: unable to set SO_REUSEADDR: %s\n",
182 strerror (errno));
183 }
184 if (bind (sockser_listen_fd, (struct sockaddr *) &sockaddr, sizeof (sockaddr)) < 0)
185 {
186 sim_io_eprintf (sd, "sockser init: unable to bind socket address: %s\n",
187 strerror (errno));
188 close (sockser_listen_fd);
189 sockser_listen_fd = -1;
190 return SIM_RC_FAIL;
191 }
192 if (listen (sockser_listen_fd, 1) < 0)
193 {
194 sim_io_eprintf (sd, "sockser init: unable to set up listener: %s\n",
195 strerror (errno));
196 close (sockser_listen_fd);
197 sockser_listen_fd = -1;
198 return SIM_RC_OK;
199 }
200
201 /* Handle writes to missing client -> SIGPIPE.
202 ??? Need a central signal management module. */
203 {
204 RETSIGTYPE (*orig) ();
205 orig = signal (SIGPIPE, SIG_IGN);
206 /* If a handler is already set up, don't mess with it. */
207 if (orig != SIG_DFL && orig != SIG_IGN)
208 signal (SIGPIPE, orig);
209 }
210
211 return SIM_RC_OK;
212 }
213
214 static void
215 dv_sockser_uninstall (SIM_DESC sd)
216 {
217 if (sockser_listen_fd != -1)
218 {
219 close (sockser_listen_fd);
220 sockser_listen_fd = -1;
221 }
222 if (sockser_fd != -1)
223 {
224 close (sockser_fd);
225 sockser_fd = -1;
226 }
227 }
228
229 SIM_RC
230 dv_sockser_install (SIM_DESC sd)
231 {
232 SIM_ASSERT (STATE_MAGIC (sd) == SIM_MAGIC_NUMBER);
233 if (sim_add_option_table (sd, NULL, sockser_options) != SIM_RC_OK)
234 return SIM_RC_FAIL;
235 sim_module_add_init_fn (sd, dv_sockser_init);
236 sim_module_add_uninstall_fn (sd, dv_sockser_uninstall);
237 return SIM_RC_OK;
238 }
239
240 static int
241 connected_p (SIM_DESC sd)
242 {
243 int numfds,flags;
244 struct timeval tv;
245 fd_set readfds;
246 struct sockaddr sockaddr;
247 int addrlen;
248
249 if (sockser_listen_fd == -1)
250 return 0;
251
252 if (sockser_fd >= 0)
253 {
254 /* FIXME: has client gone away? */
255 return 1;
256 }
257
258 /* Not connected. Connect with a client if there is one. */
259
260 FD_ZERO (&readfds);
261 FD_SET (sockser_listen_fd, &readfds);
262
263 /* ??? One can certainly argue this should be done differently,
264 but for now this is sufficient. */
265 tv.tv_sec = 0;
266 tv.tv_usec = sockser_timeout;
267
268 numfds = select (sockser_listen_fd + 1, &readfds, 0, 0, &tv);
269 if (numfds <= 0)
270 return 0;
271
272 addrlen = sizeof (sockaddr);
273 sockser_fd = accept (sockser_listen_fd, &sockaddr, &addrlen);
274 if (sockser_fd < 0)
275 return 0;
276
277 /* Set non-blocking i/o. */
278 flags = fcntl (sockser_fd, F_GETFL);
279 flags |= O_NONBLOCK | O_NDELAY;
280 if (fcntl (sockser_fd, F_SETFL, flags) == -1)
281 {
282 sim_io_eprintf (sd, "unable to set nonblocking i/o");
283 close (sockser_fd);
284 sockser_fd = -1;
285 return 0;
286 }
287 return 1;
288 }
289
290 int
291 dv_sockser_status (SIM_DESC sd)
292 {
293 int numrfds,numwfds,status;
294 struct timeval tv;
295 fd_set readfds,writefds;
296
297 /* status to return if the socket isn't set up, or select fails */
298 status = DV_SOCKSER_INPUT_EMPTY | DV_SOCKSER_OUTPUT_EMPTY;
299
300 if (! connected_p (sd))
301 return status;
302
303 FD_ZERO (&readfds);
304 FD_ZERO (&writefds);
305 FD_SET (sockser_fd, &readfds);
306 FD_SET (sockser_fd, &writefds);
307
308 /* ??? One can certainly argue this should be done differently,
309 but for now this is sufficient. The read is done separately
310 from the write to enforce the delay which we heuristically set to
311 once every SOCKSER_TIMEOUT_FREQ tries.
312 No, this isn't great for SMP situations, blah blah blah. */
313
314 {
315 static int n;
316 #define SOCKSER_TIMEOUT_FREQ 42
317 if (++n == SOCKSER_TIMEOUT_FREQ)
318 n = 0;
319 if (n == 0)
320 {
321 tv.tv_sec = 0;
322 tv.tv_usec = sockser_timeout;
323 numrfds = select (sockser_fd + 1, &readfds, 0, 0, &tv);
324 tv.tv_sec = 0;
325 tv.tv_usec = 0;
326 numwfds = select (sockser_fd + 1, 0, &writefds, 0, &tv);
327 }
328 else /* do both selects at once */
329 {
330 tv.tv_sec = 0;
331 tv.tv_usec = 0;
332 numrfds = numwfds = select (sockser_fd + 1, &readfds, &writefds, 0, &tv);
333 }
334 }
335
336 status = 0;
337 if (numrfds <= 0 || ! FD_ISSET (sockser_fd, &readfds))
338 status |= DV_SOCKSER_INPUT_EMPTY;
339 if (numwfds <= 0 || FD_ISSET (sockser_fd, &writefds))
340 status |= DV_SOCKSER_OUTPUT_EMPTY;
341 return status;
342 }
343
344 int
345 dv_sockser_write (SIM_DESC sd, unsigned char c)
346 {
347 int n;
348
349 if (! connected_p (sd))
350 return -1;
351 n = write (sockser_fd, &c, 1);
352 if (n == -1)
353 {
354 if (errno == EPIPE)
355 {
356 close (sockser_fd);
357 sockser_fd = -1;
358 }
359 return -1;
360 }
361 if (n != 1)
362 return -1;
363 return 1;
364 }
365
366 int
367 dv_sockser_read (SIM_DESC sd)
368 {
369 unsigned char c;
370 int n;
371
372 if (! connected_p (sd))
373 return -1;
374 n = read (sockser_fd, &c, 1);
375 /* ??? We're assuming semantics that may not be correct for all hosts.
376 In particular (from cvssrc/src/server.c), this assumes that we are using
377 BSD or POSIX nonblocking I/O. System V nonblocking I/O returns zero if
378 there is nothing to read. */
379 if (n == 0)
380 {
381 close (sockser_fd);
382 sockser_fd = -1;
383 return -1;
384 }
385 if (n != 1)
386 return -1;
387 return c;
388 }