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