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