root/src/dps8/uvutil.c

/* [previous][next][first][last][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. alloc_buffer
  2. accessWriteCallback
  3. accessReadCallback
  4. accessReadStart
  5. accessStartWriteActual
  6. accessStartWrite
  7. accessPutCharForce
  8. accessPutStrForce
  9. accessStartWriteStr
  10. accessLogon
  11. accessCloseCallback
  12. accessCloseConnection
  13. accessProcessInput
  14. accessTelnetReadCallback
  15. evHandler
  16. accessTelnetConnect
  17. onNewAccess
  18. uv_open_access
  19. accessPutChar
  20. accessGetChar
  21. accessPutStr

   1 /*
   2  * vim: filetype=c:tabstop=4:ai:expandtab
   3  * SPDX-License-Identifier: ICU
   4  * scspell-id: 6315eaa3-f630-11ec-96fc-80ee73e9b8e7
   5  *
   6  * ---------------------------------------------------------------------------
   7  *
   8  * Copyright (c) 2013-2019 Charles Anthony
   9  * Copyright (c) 2021 Jeffrey H. Johnson
  10  * Copyright (c) 2021-2025 The DPS8M Development Team
  11  *
  12  * This software is made available under the terms of the ICU License.
  13  * See the LICENSE.md file at the top-level directory of this distribution.
  14  *
  15  * ---------------------------------------------------------------------------
  16  */
  17 
  18 #include <uv.h>
  19 #include <ctype.h>
  20 #include <signal.h>
  21 #include "dps8.h"
  22 #include "dps8_sys.h"
  23 #include "dps8_cpu.h"
  24 #include "dps8_utils.h"
  25 #include "libtelnet.h"
  26 #include "uvutil.h"
  27 
  28 #if defined(NO_LOCALE)
  29 # define xstrerror_l strerror
  30 #endif
  31 
  32 #if defined(FREE)
  33 # undef FREE
  34 #endif /* if defined(FREE) */
  35 #define FREE(p) do  \
  36   {                 \
  37     free((p));      \
  38     (p) = NULL;     \
  39   } while(0)
  40 
  41 #define USE_REQ_DATA
  42 
  43 static void accessTelnetReadCallback (uv_tcp_t * client,
  44                             ssize_t nread,
  45                             unsigned char * buf);
  46 
  47 //
  48 // alloc_buffer: libuv callback handler to allocate buffers for incoming data.
  49 //
  50 
  51 static void alloc_buffer (UNUSED uv_handle_t * handle, size_t suggested_size,
     /* [previous][next][first][last][top][bottom][index][help] */
  52                           uv_buf_t * buf)
  53   {
  54 /* Suppress Clang Analyzer's possible memory leak warning */
  55 #if !defined(__clang_analyzer__)
  56     * buf = uv_buf_init ((char *) malloc (suggested_size),
  57                          (uint) suggested_size);
  58 #endif /* if !defined(__clang_analyzer__) */
  59   }
  60 
  61 static void accessWriteCallback (uv_write_t * req, int status)
     /* [previous][next][first][last][top][bottom][index][help] */
  62   {
  63     if (status < 0)
  64       {
  65         if (status == -ECONNRESET || status == -ECANCELED ||
  66             status == -EPIPE)
  67           {
  68             // This occurs when the other end disconnects; not an "error"
  69           }
  70         else
  71           {
  72             sim_warn ("accessWriteCallback status %d (%s)\n", -status,
  73                       xstrerror_l(-status));
  74           }
  75 
  76         // connection reset by peer
  77         accessCloseConnection (req->handle);
  78       }
  79 
  80 #if defined(USE_REQ_DATA)
  81     FREE (req->data);
  82 #else
  83     unsigned int nbufs = req->nbufs;
  84     uv_buf_t * bufs = req->bufs;
  85     for (unsigned int i = 0; i < nbufs; i ++)
  86       {
  87         if (bufs && bufs[i].base)
  88           {
  89             FREE (bufs[i].base);
  90             //bufp->base = NULL;
  91           }
  92         if (req->bufsml[i].base)
  93           {
  94             FREE (req->bufsml[i].base);
  95           }
  96       }
  97 #endif /* if defined(USE_REQ_DATA) */
  98 
  99     // the buf structure is copied; do not free.
 100     //sim_printf ("freeing req %p\n", req);
 101     FREE (req);
 102   }
 103 
 104 //
 105 // accessReadCallback: libuv read complete callback
 106 //
 107 //   Cleanup on read error.
 108 //   Forward data to appropriate handler.
 109 
 110 static void accessReadCallback (uv_stream_t* stream,
     /* [previous][next][first][last][top][bottom][index][help] */
 111                              ssize_t nread,
 112                              const uv_buf_t* buf)
 113   {
 114     uv_access * access = (uv_access *) stream->data;
 115     if (nread < 0)
 116       {
 117         //if (nread == UV_EOF)
 118           {
 119             accessCloseConnection (stream);
 120           }
 121       }
 122     else if (nread > 0)
 123       {
 124         if (access->telnetp)
 125           {
 126             telnet_recv (access->telnetp, buf->base, (size_t) nread);
 127           }
 128         else
 129           {
 130             accessTelnetReadCallback ((uv_tcp_t *) stream,
 131                                       (ssize_t) nread,
 132                                       (unsigned char *) buf->base);
 133           }
 134       }
 135     if (buf->base)
 136         free (buf->base); /* X-LINTED: FREE */
 137   }
 138 
 139 //
 140 // Enable reading on connection
 141 //
 142 
 143 static void accessReadStart (uv_tcp_t * client)
     /* [previous][next][first][last][top][bottom][index][help] */
 144   {
 145     if (! client || uv_is_closing ((uv_handle_t *) client))
 146       return;
 147     uv_read_start ((uv_stream_t *) client, alloc_buffer, accessReadCallback);
 148   }
 149 
 150 // Create and start a write request
 151 
 152 static void accessStartWriteActual (uv_tcp_t * client, char * data,
     /* [previous][next][first][last][top][bottom][index][help] */
 153                                         ssize_t datalen)
 154   {
 155     if (! client || uv_is_closing ((uv_handle_t *) client))
 156       return;
 157 /* Suppress Clang Analyzer's possible memory leak warning */
 158 #if !defined(__clang_analyzer__)
 159     uv_write_t * req = (uv_write_t *) malloc (sizeof (uv_write_t));
 160     if (!req)
 161       {
 162         (void)fprintf (stderr, "\rFATAL: Out of memory! Aborting at %s[%s:%d]\r\n",
 163                        __func__, __FILE__, __LINE__);
 164 # if defined(USE_BACKTRACE)
 165 #  if defined(SIGUSR2)
 166         (void)raise(SIGUSR2);
 167         /*NOTREACHED*/ /* unreachable */
 168 #  endif /* if defined(SIGUSR2) */
 169 # endif /* if defined(USE_BACKTRACE) */
 170         abort();
 171       }
 172     // This makes sure that bufs*.base and bufsml*.base are NULL
 173     (void)memset (req, 0, sizeof (uv_write_t));
 174     uv_buf_t buf = uv_buf_init ((char *) malloc ((unsigned long) datalen),
 175                                                  (uint) datalen);
 176 # if defined(USE_REQ_DATA)
 177     req->data = buf.base;
 178 # endif /* if defined(USE_REQ_DATA) */
 179     memcpy (buf.base, data, (unsigned long) datalen);
 180 //sim_printf ("write %u<%s>\n", datalen, data);
 181     int ret = uv_write (req, (uv_stream_t *) client, & buf, 1,
 182                         accessWriteCallback);
 183 // There seems to be a race condition when Multics signals a disconnect_line;
 184 // We close the socket, but Multics is still writing its goodbye text trailing
 185 // NULs.
 186 // If the socket has been closed, write will return BADF; just ignore it.
 187     if (ret < 0 && ret != -EBADF)
 188       sim_printf ("uv_write returns %d\n", ret);
 189 #endif /* if !defined(__clang_analyzer__) */
 190   }
 191 
 192 void accessStartWrite (uv_tcp_t * client, char * data, ssize_t datalen)
     /* [previous][next][first][last][top][bottom][index][help] */
 193   {
 194     if ((client == NULL) || uv_is_closing ((uv_handle_t *) client))
 195       return;
 196     uv_access * access = (uv_access *) client->data;
 197     if (access->telnetp)
 198       telnet_send (access->telnetp, data, (size_t) datalen);
 199     else
 200       accessStartWriteActual (client, data, (ssize_t) datalen);
 201   }
 202 
 203 static void accessPutCharForce (uv_access * access, char ch)
     /* [previous][next][first][last][top][bottom][index][help] */
 204   {
 205     //sim_putchar (ch);
 206     accessStartWrite (access->client, & ch, 1);
 207   }
 208 
 209 static void accessPutStrForce (uv_access * access, char * str)
     /* [previous][next][first][last][top][bottom][index][help] */
 210   {
 211     size_t l = strlen (str);
 212     accessStartWrite (access->client, str, (ssize_t) l);
 213   }
 214 
 215 void accessStartWriteStr (uv_tcp_t * client, char * data)
     /* [previous][next][first][last][top][bottom][index][help] */
 216   {
 217     accessStartWrite (client, data, (ssize_t) strlen (data));
 218   }
 219 
 220 static void accessLogon (uv_access * access, unsigned char * buf, ssize_t nread)
     /* [previous][next][first][last][top][bottom][index][help] */
 221   {
 222     for (ssize_t nchar = 0; nchar < nread; nchar ++)
 223       {
 224         unsigned char kar = buf[nchar];
 225         // buffer too full for anything more?
 226         if ((unsigned long) access->pwPos >= sizeof (access->pwBuffer) - 1)
 227           {
 228             // yes. Only allow \n, \r, ^H, ^R
 229             switch (kar)
 230               {
 231                 case '\b':  // backspace
 232                 case 127:   // delete
 233                   {
 234                     // remove char from line
 235                     accessPutStrForce (access, "\b \b");
 236                     access->pwBuffer[access->pwPos] = 0;     // remove char from buffer
 237                     if (access->pwPos > 0)
 238                       access->pwPos -= 1;             // back up buffer pointer
 239                   }
 240                   break;
 241 
 242                 case '\n':
 243                 case '\r':
 244                   {
 245                     access->pwBuffer[access->pwPos] = 0;
 246                     goto check;
 247                   }
 248 
 249                 case 0x12:  // ^R
 250                   {
 251                     accessPutStrForce (access, "^R\r\n"); // echo ^R
 252                     access->connectPrompt (access->client);
 253                     accessPutStrForce (access, access->pwBuffer);
 254                   }
 255                  break;
 256 
 257                 default:
 258                   break;
 259               } // switch kar
 260             continue; // process next character in buffer
 261           } // if buffer full
 262 
 263         if (isprint (kar))   // printable?
 264           {
 265             accessPutCharForce (access, '*');
 266             access->pwBuffer[access->pwPos++] = (char) kar;
 267             access->pwBuffer[access->pwPos] = 0;
 268           }
 269         else
 270           {
 271             switch (kar)
 272               {
 273                 case '\b':  // backspace
 274                 case 127:   // delete
 275                   {
 276                     // remove char from line
 277                     accessPutStrForce (access, "\b \b");
 278                    // remove char from buffer
 279                     access->pwBuffer[access->pwPos] = 0;
 280                     if (access->pwPos > 0)
 281                       access->pwPos -= 1;   // back up buffer pointer
 282                   }
 283                   break;
 284 
 285                 case '\n':
 286                 case '\r':
 287                   {
 288                     access->pwBuffer[access->pwPos] = 0;
 289                     goto check;
 290                   }
 291 
 292                 case 0x12:  // ^R
 293                   {
 294                     accessPutStrForce (access, "^R\r\n"); // echo ^R
 295                     access->connectPrompt (access->client);
 296                     accessPutStrForce (access, access->pwBuffer);
 297                   }
 298                   break;
 299 
 300                 default:
 301                   break;
 302               } // switch kar
 303           } // not printable
 304       } // for nchar
 305     return;
 306 
 307 check:;
 308     char cpy[access->pwPos + 1];
 309     memcpy (cpy, access->pwBuffer, (unsigned long) access->pwPos);
 310     cpy[access->pwPos] = 0;
 311     trim (cpy);
 312     //sim_printf ("<%s>", cpy);
 313     access->pwPos = 0;
 314     accessPutStrForce (access, "\r\n");
 315 
 316     if (strcmp (cpy, access->pw) == 0)
 317       {
 318         accessPutStrForce (access, "ok\r\n");
 319         sim_printf ("\r[OPC emulation: ACCESS GRANTED]\r\n");
 320         goto associate;
 321       }
 322     else
 323       {
 324         //accessPutStrForce ("<");
 325         //accessPutStrForce (access->pwBuffer);
 326         //accessPutStrForce (">\r\n");
 327         accessPutStrForce (access, "nope\r\n");
 328         sim_printf ("\r[OPC emulation: INVALID PASSWORD]\r\n");
 329         goto reprompt;
 330       }
 331 
 332 reprompt:;
 333     access->connectPrompt (access->client);
 334     return;
 335 
 336 associate:;
 337     access->loggedOn = true;
 338     if (access->connected)
 339       access->connected (access->client);
 340   }
 341 
 342 static void accessCloseCallback (uv_handle_t * stream)
     /* [previous][next][first][last][top][bottom][index][help] */
 343   {
 344     FREE (stream);
 345     //access->client = NULL;
 346   }
 347 
 348 void accessCloseConnection (uv_stream_t* stream)
     /* [previous][next][first][last][top][bottom][index][help] */
 349   {
 350     uv_access * access = (uv_access *) stream->data;
 351     sim_printf ("\r[OPC emulation: DISCONNECT]\r\n");
 352     // Clean up allocated data
 353     if (access->telnetp)
 354       {
 355         telnet_free (access->telnetp);
 356         access->telnetp = NULL;
 357       }
 358     if (! uv_is_closing ((uv_handle_t *) stream))
 359       uv_close ((uv_handle_t *) stream, accessCloseCallback);
 360     access->client = NULL;
 361   }
 362 
 363 static void accessProcessInput (uv_access * access, unsigned char * buf,
     /* [previous][next][first][last][top][bottom][index][help] */
 364                                  ssize_t nread)
 365   {
 366     if (access->inBuffer)
 367       {
 368         //sim_warn ("inBuffer overrun\n");
 369         unsigned char * new =
 370           realloc (access->inBuffer,
 371                    (unsigned long) (access->inSize + nread));
 372         if (! new)
 373           {
 374             (void)fprintf (stderr, "\rFATAL: Out of memory! Aborting at %s[%s:%d]\r\n",
 375                            __func__, __FILE__, __LINE__);
 376 #if defined(USE_BACKTRACE)
 377 # if defined(SIGUSR2)
 378             (void)raise(SIGUSR2);
 379             /*NOTREACHED*/ /* unreachable */
 380 # endif /* if defined(SIGUSR2) */
 381 #endif /* if defined(USE_BACKTRACE) */
 382             abort();
 383           }
 384         memcpy (new + access->inSize, buf, (unsigned long) nread);
 385         access->inSize += nread;
 386         access->inBuffer = new;
 387       }
 388     else
 389       {
 390         access->inBuffer = malloc ((unsigned long) nread);
 391         if (! access->inBuffer)
 392           {
 393             (void)fprintf (stderr, "\rFATAL: Out of memory! Aborting at %s[%s:%d]\r\n",
 394                            __func__, __FILE__, __LINE__);
 395 #if defined(USE_BACKTRACE)
 396 # if defined(SIGUSR2)
 397             (void)raise(SIGUSR2);
 398             /*NOTREACHED*/ /* unreachable */
 399 # endif /* if defined(SIGUSR2) */
 400 #endif /* if defined(USE_BACKTRACE) */
 401             abort();
 402           }
 403         memcpy (access->inBuffer, buf, (unsigned long) nread);
 404         access->inSize = (uint) nread;
 405         access->inUsed = 0;
 406       }
 407 
 408     // Prevent further reading until this buffer is consumed
 409     //fnpuv_read_stop (client);
 410     //if (! client || uv_is_closing ((uv_handle_t *) client))
 411       //return;
 412     //uv_read_stop ((uv_stream_t *) client);
 413   }
 414 
 415 static void accessTelnetReadCallback (uv_tcp_t * client,
     /* [previous][next][first][last][top][bottom][index][help] */
 416                             ssize_t nread,
 417                             unsigned char * buf)
 418   {
 419     uv_access * access = (uv_access *) client->data;
 420     if (access->loggedOn)
 421       accessProcessInput (access, buf, nread);
 422     else
 423       accessLogon (access, buf, nread);
 424   }
 425 
 426 static void evHandler (UNUSED telnet_t *telnet, telnet_event_t *event,
     /* [previous][next][first][last][top][bottom][index][help] */
 427                        void *user_data)
 428   {
 429     uv_tcp_t * client = (uv_tcp_t *) user_data;
 430 
 431     switch (event->type)
 432       {
 433         case TELNET_EV_DATA:
 434           {
 435             accessTelnetReadCallback (client, (ssize_t) event->data.size,
 436                             (unsigned char *)event->data.buffer);
 437           }
 438           break;
 439 
 440         case TELNET_EV_SEND:
 441           {
 442             accessStartWriteActual (client, (char *) event->data.buffer,
 443                                         (ssize_t) event->data.size);
 444           }
 445           break;
 446 
 447         case TELNET_EV_DO:
 448           {
 449             if (event->neg.telopt == TELNET_TELOPT_BINARY)
 450               {
 451                 // DO Binary
 452               }
 453             else if (event->neg.telopt == TELNET_TELOPT_SGA)
 454               {
 455                 // DO Suppress Go Ahead
 456               }
 457             else if (event->neg.telopt == TELNET_TELOPT_ECHO)
 458               {
 459                 // DO Suppress Echo
 460               }
 461             else
 462               {
 463                 sim_printf ("evHandler DO %d\n", event->neg.telopt);
 464               }
 465           }
 466           break;
 467 
 468         case TELNET_EV_DONT:
 469           {
 470             sim_printf ("evHandler DONT %d\n", event->neg.telopt);
 471           }
 472           break;
 473 
 474         case TELNET_EV_WILL:
 475           {
 476             if (event->neg.telopt != 3)
 477               sim_printf ("evHandler WILL %d\n", event->neg.telopt);
 478           }
 479           break;
 480 
 481         case TELNET_EV_WONT:
 482           {
 483             sim_printf ("evHandler WONT %d\n", event->neg.telopt);
 484           }
 485           break;
 486 
 487         case TELNET_EV_ERROR:
 488           {
 489             sim_warn ("libtelnet evHandler error <%s>\n", event->error.msg);
 490           }
 491           break;
 492 
 493         case TELNET_EV_IAC:
 494           {
 495             if (event->iac.cmd == 243 || // BRK
 496                 event->iac.cmd == 244)   // IP
 497               {
 498                 sim_warn ("libtelnet dropping unassociated BRK/IP\n");
 499               }
 500             else
 501               if ((!sim_quiet) || (event->iac.cmd != 241))
 502                 sim_warn ("libtelnet unhandled IAC event %d\n", event->iac.cmd);
 503           }
 504           break;
 505 
 506         default:
 507           sim_printf ("evHandler: unhandled event %d\n", event->type);
 508           break;
 509       }
 510 
 511   }
 512 
 513 static const telnet_telopt_t my_telopts[] =
 514   {
 515     { TELNET_TELOPT_SGA,       TELNET_WILL, TELNET_DO   },
 516     { TELNET_TELOPT_ECHO,      TELNET_WILL, TELNET_DONT },
 517   //{ TELNET_TELOPT_TTYPE,     TELNET_WONT, TELNET_DONT },
 518  //   { TELNET_TELOPT_BINARY,    TELNET_WILL, TELNET_DO   },
 519     { TELNET_TELOPT_BINARY,    TELNET_WONT, TELNET_DONT   },
 520   //{ TELNET_TELOPT_NAWS,      TELNET_WONT, TELNET_DONT },
 521     { -1, 0, 0 }
 522   };
 523 
 524 static void * accessTelnetConnect (uv_tcp_t * client)
     /* [previous][next][first][last][top][bottom][index][help] */
 525   {
 526     void * p = (void *) telnet_init (my_telopts, evHandler, 0, client);
 527     if (!p)
 528       {
 529         (void)fprintf(stderr, "\rtelnet_init failed at %s[%s:%d]\r\n",
 530                       __func__, __FILE__, __LINE__);
 531         return NULL;
 532       }
 533     const telnet_telopt_t * q = my_telopts;
 534     while (q->telopt != -1)
 535       {
 536         telnet_negotiate (p, q->us, (unsigned char) q->telopt);
 537         q ++;
 538       }
 539     return p;
 540   }
 541 
 542 //
 543 // Connection callback handler
 544 //
 545 
 546 static void onNewAccess (uv_stream_t * server, int status)
     /* [previous][next][first][last][top][bottom][index][help] */
 547   {
 548     if (status < 0)
 549       {
 550         (void)fprintf (stderr, "\r[OPC emulation: new connection error %s]\r\n", uv_strerror(status));
 551         // error!
 552         return;
 553       }
 554 
 555     uv_access * access = (uv_access *) server->data;
 556 
 557     uv_tcp_t * client = (uv_tcp_t *) malloc (sizeof (uv_tcp_t));
 558     if (!client)
 559       {
 560         (void)fprintf(stderr, "\rFATAL: Out of memory! Aborting at %s[%s:%d]\r\n",
 561                       __func__, __FILE__, __LINE__);
 562 #if defined(USE_BACKTRACE)
 563 # if defined(SIGUSR2)
 564         (void)raise(SIGUSR2);
 565         /*NOTREACHED*/ /* unreachable */
 566 # endif /* if defined(SIGUSR2) */
 567 #endif /* if defined(USE_BACKTRACE) */
 568         abort();
 569       }
 570     uv_tcp_init (access->loop, client);
 571     client->data = server->data;
 572     if (uv_accept (server, (uv_stream_t *) client) == 0)
 573       {
 574         // Only a single connection at a time
 575         if (access->client)
 576           {
 577             sim_printf ("\r\n[OPC emulation: BUMPED]\r\n\r\n");
 578             accessPutStrForce (access, "\r[OPC emulation: BUMPED]\r\n");
 579             access->loggedOn = false;
 580             sim_printf ("\rMultics has disconnected you\r\n");
 581             accessCloseConnection ((uv_stream_t *) access->client);
 582           }
 583         access->client = client;
 584         uv_tcp_nodelay (client, 1);
 585         struct sockaddr name;
 586         int namelen = sizeof (name);
 587         int ret = uv_tcp_getpeername (access->client, & name, & namelen);
 588         if (ret < 0)
 589           {
 590             sim_printf ("\r[OPC emulation: CONNECT; addr err %d]\r\n", ret);
 591           }
 592         else
 593           {
 594             struct sockaddr_in * p = (struct sockaddr_in *) & name;
 595             sim_printf ("\r[OPC emulation: CONNECT %s]\r\n", inet_ntoa (p -> sin_addr));
 596           }
 597 
 598         if (access->useTelnet)
 599           {
 600             access->telnetp = accessTelnetConnect (access->client);
 601             if (!access->telnetp)
 602               {
 603                  sim_warn ("ltnConnect failed\n");
 604                  return;
 605               }
 606           }
 607         else
 608           {
 609             access->telnetp = NULL;
 610           }
 611         access->loggedOn =
 612           ! strlen (access->pw);
 613         if (access->loggedOn)
 614           access->connected (access->client);
 615         else
 616           access->connectPrompt (access->client);
 617         accessReadStart (access->client);
 618       }
 619     else
 620       {
 621         uv_close ((uv_handle_t *) client, accessCloseCallback);
 622       }
 623   }
 624 
 625 void uv_open_access (uv_access * access)
     /* [previous][next][first][last][top][bottom][index][help] */
 626   {
 627     if (access->open == true)
 628       {
 629         sim_printf ("\r[OPC emulation: OPC already initialized]\r\n");
 630       }
 631 
 632     if (! access->port)
 633       {
 634         //sim_printf ("access port disabled\n");
 635         return;
 636       }
 637 
 638     if (! access->loop)
 639       access->loop = uv_default_loop ();
 640 
 641     // Do we already have a listening port (ie not first time)?
 642     if (access->open)
 643       return;
 644 
 645     uv_tcp_init (access->loop, & access->server);
 646     access->server.data = (void *) access;
 647     struct sockaddr_in addr;
 648     uv_ip4_addr (access->address, access->port, & addr);
 649     uv_tcp_bind (& access->server, (const struct sockaddr *) & addr, 0);
 650 #define DEFAULT_BACKLOG 1024
 651     int r = uv_listen ((uv_stream_t *) & access->server,
 652                        DEFAULT_BACKLOG,
 653                        onNewAccess);
 654     if (r)
 655      {
 656         (void)fprintf (stderr, "\r[OPC emulation: listen error: %s:%ld: %s]\r\n",
 657                        access->address, (long) access->port, uv_strerror(r));
 658       }
 659     access->open = true;
 660     if (access->address != NULL)
 661       sim_printf ("\r[OPC emulation: TELNET server listening on %s:%ld]\r\n",
 662                   access->address, (long) access->port);
 663     else
 664       sim_printf ("\r[OPC emulation: TELNET server listening on 0.0.0.0:%ld]\r\n",
 665                   (long) access->port);
 666   }
 667 
 668 #if !defined(QUIET_UNUSED)
 669 void accessPutChar (uv_access * access, char ch)
     /* [previous][next][first][last][top][bottom][index][help] */
 670   {
 671     //sim_putchar (ch);
 672     if (access->loggedOn)
 673       accessStartWrite (access->client, & ch, 1);
 674   }
 675 #endif /* if !defined(QUIET_UNUSED) */
 676 
 677 int accessGetChar (uv_access * access)
     /* [previous][next][first][last][top][bottom][index][help] */
 678   {
 679     // The connection could have closed when we were not looking
 680     if (! access->client)
 681       {
 682         if (access->inBuffer)
 683           FREE (access->inBuffer);
 684         access->inBuffer = NULL;
 685         access->inSize = 0;
 686         access->inUsed = 0;
 687         return SCPE_OK;
 688       }
 689 
 690     if (access->inBuffer && access->inUsed < access->inSize)
 691        {
 692          unsigned char c = access->inBuffer[access->inUsed ++];
 693          if (access->inUsed >= access->inSize)
 694            {
 695              FREE (access->inBuffer);
 696              access->inBuffer = NULL;
 697              access->inSize = 0;
 698              access->inUsed = 0;
 699              // The connection could have been closed when we weren't looking
 700              //if (access->client)
 701                //fnpuv_read_start (access->client);
 702            }
 703          return (int) c + SCPE_KFLAG;
 704        }
 705     return SCPE_OK;
 706 
 707   }
 708 
 709 #if !defined(QUIET_UNUSED)
 710 void accessPutStr (uv_access * access, char * str)
     /* [previous][next][first][last][top][bottom][index][help] */
 711   {
 712     size_t l = strlen (str);
 713     //for (size_t i = 0; i < l; i ++)
 714       //sim_putchar (str[i]);
 715     if (access->loggedOn)
 716       accessStartWrite (access->client, str, (ssize_t) l);
 717   }
 718 #endif /* if !defined(QUIET_UNUSED) */

/* [previous][next][first][last][top][bottom][index][help] */