1/* Serial port emulation using sockets.
2   Copyright (C) 1998-2023 Free Software Foundation, Inc.
3   Contributed by Cygnus Solutions.
4
5This program is free software; you can redistribute it and/or modify
6it under the terms of the GNU General Public License as published by
7the Free Software Foundation; either version 3 of the License, or
8(at your option) any later version.
9
10This program is distributed in the hope that it will be useful,
11but WITHOUT ANY WARRANTY; without even the implied warranty of
12MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13GNU General Public License for more details.
14
15You should have received a copy of the GNU General Public License
16along 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
49typedef 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.  */
60const 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.  */
63static unsigned int sockser_timeout = DEFAULT_TIMEOUT;
64static int sockser_listen_fd = -1;
65static int sockser_fd = -1;
66
67/* FIXME: use tree properties when they're ready.  */
68
69typedef enum {
70  OPTION_ADDR = OPTION_START
71} SOCKSER_OPTIONS;
72
73static DECLARE_OPTION_HANDLER (sockser_option_handler);
74
75static 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
83static SIM_RC
84sockser_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
97static SIM_RC
98dv_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
191static void
192dv_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.  */
207extern MODULE_INIT_FN sim_install_dv_sockser;
208
209SIM_RC
210sim_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
220static int
221connected_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
272int
273dv_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
327int
328dv_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
350int
351dv_sockser_write (SIM_DESC sd, unsigned char c)
352{
353  return dv_sockser_write_buffer (sd, &c, 1);
354}
355
356int
357dv_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