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