root/src/simh/sim_tmxr.c

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

DEFINITIONS

This source file includes following definitions.
  1. tmxr_init_line
  2. tmxr_report_connection
  3. tmxr_report_disconnection
  4. loop_write_ex
  5. loop_write
  6. loop_read_ex
  7. loop_read
  8. tmxr_read
  9. tmxr_write
  10. tmxr_rmvrc
  11. tmxr_find_ldsc
  12. tmxr_get_ldsc
  13. growstring
  14. tmxr_mux_attach_string
  15. tmxr_line_attach_string
  16. hash32s
  17. tmxr_poll_conn
  18. tmxr_reset_ln_ex
  19. tmxr_close_ln
  20. tmxr_reset_ln
  21. tmxr_set_get_modem_bits
  22. tmxr_set_line_loopback
  23. tmxr_get_line_loopback
  24. tmxr_set_line_halfduplex
  25. tmxr_get_line_halfduplex
  26. tmxr_input_pending_ln
  27. tmxr_getc_ln
  28. tmxr_get_packet_ln
  29. tmxr_get_packet_ln_ex
  30. tmxr_poll_rx
  31. tmxr_rqln_bare
  32. tmxr_rqln
  33. tmxr_putc_ln
  34. tmxr_put_packet_ln
  35. tmxr_put_packet_ln_ex
  36. tmxr_poll_tx
  37. tmxr_send_buffered_data
  38. tmxr_tqln
  39. tmxr_tpqln
  40. tmxr_tpbusyln
  41. _mux_detach_line
  42. tmxr_detach_ln
  43. _tmln_speed_delta
  44. tmxr_set_line_speed
  45. tmxr_open_master
  46. tmxr_set_line_unit
  47. tmxr_set_line_output_unit
  48. tmxr_start_poll
  49. tmxr_stop_poll
  50. tmxr_add_to_open_list
  51. _tmxr_remove_from_open_list
  52. _tmxr_locate_line_send_expect
  53. tmxr_locate_line_send
  54. tmxr_locate_line_expect
  55. tmxr_change_async
  56. tmxr_attach_ex
  57. tmxr_startup
  58. tmxr_shutdown
  59. tmxr_show_open_devices
  60. tmxr_close_master
  61. tmxr_detach
  62. tmxr_activate
  63. tmxr_activate_after
  64. tmxr_attach_help
  65. tmxr_ex
  66. tmxr_dep
  67. tmxr_msg
  68. tmxr_linemsg
  69. tmxr_linemsgf
  70. tmxr_linemsgvf
  71. tmxr_fconns
  72. tmxr_fstats
  73. tmxr_dscln
  74. tmxr_set_log
  75. tmxr_set_nolog
  76. tmxr_show_log
  77. tmxr_set_lnorder
  78. tmxr_show_lnorder
  79. tmxr_show_summ
  80. tmxr_show_cstat
  81. tmxr_show_lines

   1 /*
   2  * sim_tmxr.c: Telnet terminal multiplexer library
   3  *
   4  * vim: filetype=c:tabstop=4:ai:expandtab
   5  * SPDX-License-Identifier: MIT
   6  * scspell-id: e76cc98d-f62a-11ec-967b-80ee73e9b8e7
   7  *
   8  * ---------------------------------------------------------------------------
   9  *
  10  * Copyright (c) 2001-2011 Robert M. Supnik
  11  * Copyright (c) 2021-2025 The DPS8M Development Team
  12  *
  13  * Permission is hereby granted, free of charge, to any person obtaining a
  14  * copy of this software and associated documentation files (the "Software"),
  15  * to deal in the Software without restriction, including without limitation
  16  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
  17  * and/or sell copies of the Software, and to permit persons to whom the
  18  * Software is furnished to do so, subject to the following conditions:
  19  *
  20  * The above copyright notice and this permission notice shall be included
  21  * in all copies or substantial portions of the Software.
  22  *
  23  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
  24  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  25  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
  26  * IN NO EVENT SHALL ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR
  27  * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
  28  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
  29  * OTHER DEALINGS IN THE SOFTWARE.
  30  *
  31  * Except as contained in this notice, the name of Robert M. Supnik shall
  32  * not be used in advertising or otherwise to promote the sale, use or
  33  * other dealings in this Software without prior written authorization from
  34  * Robert M. Supnik.
  35  *
  36  * ---------------------------------------------------------------------------
  37  */
  38 
  39 //-V::701
  40 
  41 /*
  42  * Based on the original DZ11 simulator by Thord Nilson, as updated by
  43  * Arthur Krewat.
  44  *
  45  * This library includes:
  46  *
  47  * tmxr_poll_conn -                     poll for connection
  48  * tmxr_reset_ln -                      reset line (drops connections)
  49  * tmxr_detach_ln -                     reset line and close per line listener and outgoing destination
  50  * tmxr_getc_ln -                       get character for line
  51  * tmxr_get_packet_ln -                 get packet from line
  52  * tmxr_get_packet_ln_ex -              get packet from line with separator byte
  53  * tmxr_poll_rx -                       poll receive
  54  * tmxr_putc_ln -                       put character for line
  55  * tmxr_put_packet_ln -                 put packet on line
  56  * tmxr_put_packet_ln_ex -              put packet on line with separator byte
  57  * tmxr_poll_tx -                       poll transmit
  58  * tmxr_send_buffered_data -            transmit buffered data
  59  * tmxr_set_get_modem_bits -            set and/or get a line modem bits
  60  * tmxr_set_line_loopback -             enable or disable loopback mode on a line
  61  * tmxr_get_line_loopback -             returns the current loopback status of a line
  62  * tmxr_set_line_halfduplex -           enable or disable halfduplex mode on a line
  63  * tmxr_get_line_halfduplex -           returns the current halfduplex status of a line
  64  * tmxr_open_master -                   open master connection
  65  * tmxr_close_master -                  close master connection
  66  * tmxr_attach  -                       attach terminal multiplexor to listening port
  67  * tmxr_detach  -                       detach terminal multiplexor to listening port
  68  * tmxr_attach_help  -                  help routine for attaching multiplexer devices
  69  * tmxr_set_line_unit -                 set the unit which polls for input for a given line
  70  * tmxr_ex      -                       (null) examine
  71  * tmxr_dep     -                       (null) deposit
  72  * tmxr_msg     -                       send message to socket
  73  * tmxr_linemsg -                       send message to line
  74  * tmxr_linemsgf -                      send formatted message to line
  75  * tmxr_fconns  -                       output connection status
  76  * tmxr_fstats  -                       output connection statistics
  77  * tmxr_set_log -                       enable logging for line
  78  * tmxr_set_nolog -                     disable logging for line
  79  * tmxr_show_log -                      show logging status for line
  80  * tmxr_dscln   -                       disconnect line (SET routine)
  81  * tmxr_rqln    -                       number of available characters for line
  82  * tmxr_tqln    -                       number of buffered characters for line
  83  * tmxr_tpqln    -                      number of buffered packet characters for line
  84  * tmxr_tpbusyln -                      transmit packet busy status for line
  85  * tmxr_set_lnorder -                   set line connection order
  86  * tmxr_show_lnorder -                  show line connection order
  87  * tmxr_show_summ -                     show connection summary
  88  * tmxr_show_cstat -                    show line connections or status
  89  * tmxr_show_lines -                    show number of lines
  90  * tmxr_show_open_devices -             show info about all open tmxr devices
  91  */
  92 
  93 #include "sim_defs.h"
  94 #include "sim_sock.h"
  95 #include "sim_timer.h"
  96 #include "sim_tmxr.h"
  97 #include "scp.h"
  98 
  99 #define DBG_CTR 0
 100 
 101 #include "../dps8/dps8.h"
 102 #include "../dps8/dps8_sir.h"
 103 
 104 #include <ctype.h>
 105 #include <time.h>
 106 #include <sys/time.h>
 107 #include <signal.h>
 108 #include <unistd.h>
 109 #include <math.h>
 110 
 111 #if defined(__MINGW64__) || defined(__MINGW32__)
 112 # include "../dps8/bsd_random.h"
 113 # define random  bsd_random
 114 # define srandom bsd_srandom
 115 #endif /* if defined(__MINGW64__) || defined(__MINGW32__) */
 116 
 117 // #if defined(TESTING)
 118 // # include "../dps8/dps8_cpu.h"
 119 // #endif /* if defined(TESTING) */
 120 
 121 #define FREE(p) do  \
 122   {                 \
 123     free((p));      \
 124     (p) = NULL;     \
 125   } while(0)
 126 
 127 /* Telnet protocol constants - negatives are for init'ing signed char data */
 128 
 129 /* Commands */
 130 #define TN_IAC          0xFFu /* -1 */                  /* protocol delim */
 131 #define TN_DONT         0xFEu /* -2 */                  /* don't */
 132 #define TN_DO           0xFDu /* -3 */                  /* do */
 133 #define TN_WONT         0xFCu /* -4 */                  /* won't */
 134 #define TN_WILL         0xFBu /* -5 */                  /* will */
 135 #define TN_SB           0xFAu /* -6 */                  /* sub-option negotiation */
 136 #define TN_GA           0xF9u /* -7 */                  /* go ahead */
 137 #define TN_EL           0xF8u /* -8 */                  /* erase line */
 138 #define TN_EC           0xF7u /* -9 */                  /* erase character */
 139 #define TN_AYT          0xF6u /* -10 */                 /* are you there */
 140 #define TN_AO           0xF5u /* -11 */                 /* abort output */
 141 #define TN_IP           0xF4u /* -12 */                 /* interrupt process */
 142 #define TN_BRK          0xF3u /* -13 */                 /* break */
 143 #define TN_DATAMK       0xF2u /* -14 */                 /* data mark */
 144 #define TN_NOP          0xF1u /* -15 */                 /* no operation */
 145 #define TN_SE           0xF0u /* -16 */                 /* end sub-option negot */
 146 
 147 /* Options */
 148 
 149 #define TN_BIN            0                             /* bin */
 150 #define TN_ECHO           1                             /* echo */
 151 #define TN_SGA            3                             /* sga */
 152 #define TN_STATUS         5                             /* option status query */
 153 #define TN_TIMING         6                             /* Timing Mark */
 154 #define TN_NAOCRD        10                             /* Output Carriage-Return Disposition */
 155 #define TN_NAOHTS        11                             /* Output Horizontal Tab Stops */
 156 #define TN_NAOHTD        12                             /* Output Horizontal Tab Stop Disposition */
 157 #define TN_NAOFFD        13                             /* Output Forfeed Disposition */
 158 #define TN_NAOVTS        14                             /* Output Vertical Tab Stop */
 159 #define TN_NAOVTD        15                             /* Output Vertical Tab Stop Disposition */
 160 #define TN_NAOLFD        16                             /* Output Linefeed Disposition */
 161 #define TN_EXTEND        17                             /* Extended Ascii */
 162 #define TN_LOGOUT        18                             /* Logout */
 163 #define TN_BM            19                             /* Byte Macro */
 164 #define TN_DET           20                             /* Data Entry Terminal */
 165 #define TN_SENDLO        23                             /* Send Location */
 166 #define TN_TERMTY        24                             /* Terminal Type */
 167 #define TN_ENDREC        25                             /* Terminal Type */
 168 #define TN_TUID          26                             /* TACACS User Identification */
 169 #define TN_OUTMRK        27                             /* Output Marking */
 170 #define TN_TTYLOC        28                             /* Terminal Location Number */
 171 #define TN_3270          29                             /* 3270 Regime */
 172 #define TN_X3PAD         30                             /* X.3 PAD */
 173 #define TN_NAWS          31                             /* Negotiate About Window Size */
 174 #define TN_TERMSP        32                             /* Terminal Speed */
 175 #define TN_TOGFLO        33                             /* Remote Flow Control */
 176 #define TN_LINE          34                             /* line mode */
 177 #define TN_XDISPL        35                             /* X Display Location */
 178 #define TN_ENVIRO        36                             /* Environment */
 179 #define TN_AUTH          37                             /* Authentication */
 180 #define TN_ENCRYP        38                             /* Data Encryption */
 181 #define TN_NEWENV        39                             /* New Environment */
 182 #define TN_TN3270        40                             /* TN3270 Enhancements */
 183 #define TN_CHARST        42                             /* CHARSET */
 184 #define TN_COMPRT        44                             /* Com Port Control */
 185 #define TN_KERMIT        47                             /* KERMIT */
 186 
 187 #define TN_CR           015                             /* carriage return */
 188 #define TN_LF           012                             /* line feed */
 189 #define TN_NUL          000                             /* null */
 190 
 191 /* Telnet line states */
 192 
 193 #define TNS_NORM        000                             /* normal */
 194 #define TNS_IAC         001                             /* IAC seen */
 195 #define TNS_WILL        002                             /* WILL seen */
 196 #define TNS_WONT        003                             /* WONT seen */
 197 #define TNS_SKIP        004                             /* skip next cmd */
 198 #define TNS_CRPAD       005                             /* CR padding */
 199 #define TNS_DO          006                             /* DO request pending rejection */
 200 
 201 /* Telnet Option Sent Flags */
 202 
 203 #define TNOS_DONT       001                             /* Don't has been sent */
 204 #define TNOS_WONT       002                             /* Won't has been sent */
 205 
 206 // static BITFIELD tmxr_modem_bits[] = {
 207 //   BIT(DTR),                                 /* Data Terminal Ready */
 208 //   BIT(RTS),                                 /* Request To Send     */
 209 //   BIT(DCD),                                 /* Data Carrier Detect */
 210 //   BIT(RNG),                                 /* Ring Indicator      */
 211 //   BIT(CTS),                                 /* Clear To Send       */
 212 //   BIT(DSR),                                 /* Data Set Ready      */
 213 //   ENDBITS
 214 // };
 215 
 216 static u_char mantra[] = {                  /* Telnet Option Negotiation Mantra */
 217     TN_IAC, TN_WILL, TN_LINE,
 218     TN_IAC, TN_WILL, TN_SGA,
 219     TN_IAC, TN_WILL, TN_ECHO,
 220     TN_IAC, TN_WILL, TN_BIN,
 221     TN_IAC, TN_DO,   TN_BIN
 222     };
 223 
 224 #define TMXR_GUARD  ((sizeof(mantra))) /* buffer guard */
 225 
 226 /* Local routines */
 227 
 228 static void tmxr_add_to_open_list (TMXR* mux);
 229 
 230 /* Initialize the line state.
 231 
 232    Reset the line state to represent an idle line.  Note that we do not clear
 233    all of the line structure members, so a connected line remains connected
 234    after this call.
 235 
 236    Because a line break is represented by a flag in the "receive break status"
 237    array, we must zero that array in order to clear any pending break
 238    indications.
 239 */
 240 
 241 static void tmxr_init_line (TMLN *lp)
     /* [previous][next][first][last][top][bottom][index][help] */
 242 {
 243 lp->tsta           = 0;                                           /* init telnet state */
 244 lp->xmte           = 1;                                           /* enable transmit */
 245 lp->dstb           = 0;                                           /* default bin mode */
 246 lp->rxbpr          = lp->rxbpi = lp->rxcnt = lp->rxpcnt = 0;      /* init receive indexes */
 247 if (!lp->txbfd || lp->notelnet)                                   /* if not buffered telnet */
 248     lp->txbpr      = lp->txbpi = lp->txcnt = lp->txpcnt = 0;      /*   init transmit indexes */
 249 lp->txdrp          = 0;
 250 tmxr_set_get_modem_bits (lp, 0, 0, NULL);
 251 if ((!lp->mp->buffered) && (!lp->txbfd)) {
 252     lp->txbfd      = 0; //-V1048
 253     lp->txbsz      = TMXR_MAXBUF;
 254     lp->txb        = (char *)realloc (lp->txb, lp->txbsz);
 255     if (!lp->txb)
 256       {
 257         fprintf (stderr, "\rFATAL: Out of memory! Aborting at %s[%s:%d]\r\n",
 258                  __func__, __FILE__, __LINE__);
 259 #if defined(USE_BACKTRACE)
 260 # if defined(SIGUSR2)
 261         (void)raise(SIGUSR2);
 262         /*NOTREACHED*/ /* unreachable */
 263 # endif /* if defined(SIGUSR2) */
 264 #endif /* if defined(USE_BACKTRACE) */
 265         abort();
 266       }
 267     lp->rxbsz      = TMXR_MAXBUF;
 268     lp->rxb        = (char *)realloc(lp->rxb, lp->rxbsz);
 269     if (!lp->rxb)
 270       {
 271         fprintf (stderr, "\rFATAL: Out of memory! Aborting at %s[%s:%d]\r\n",
 272                  __func__, __FILE__, __LINE__);
 273 #if defined(USE_BACKTRACE)
 274 # if defined(SIGUSR2)
 275         (void)raise(SIGUSR2);
 276         /*NOTREACHED*/ /* unreachable */
 277 # endif /* if defined(SIGUSR2) */
 278 #endif /* if defined(USE_BACKTRACE) */
 279         abort();
 280       }
 281     lp->rbr        = (char *)realloc(lp->rbr, lp->rxbsz);
 282     if (!lp->rbr)
 283       {
 284         fprintf (stderr, "\rFATAL: Out of memory! Aborting at %s[%s:%d]\r\n",
 285                  __func__, __FILE__, __LINE__);
 286 #if defined(USE_BACKTRACE)
 287 # if defined(SIGUSR2)
 288         (void)raise(SIGUSR2);
 289         /*NOTREACHED*/ /* unreachable */
 290 # endif /* if defined(SIGUSR2) */
 291 #endif /* if defined(USE_BACKTRACE) */
 292         abort();
 293       }
 294     }
 295 if (lp->loopback) {
 296     lp->lpbsz      = lp->rxbsz;
 297     lp->lpb        = (char *)realloc(lp->lpb, lp->lpbsz);
 298     if (!lp->lpb)
 299       {
 300         fprintf (stderr, "\rFATAL: Out of memory! Aborting at %s[%s:%d]\r\n",
 301                  __func__, __FILE__, __LINE__);
 302 #if defined(USE_BACKTRACE)
 303 # if defined(SIGUSR2)
 304         (void)raise(SIGUSR2);
 305         /*NOTREACHED*/ /* unreachable */
 306 # endif /* if defined(SIGUSR2) */
 307 #endif /* if defined(USE_BACKTRACE) */
 308         abort();
 309       }
 310     lp->lpbcnt     = lp->lpbpi = lp->lpbpr = 0;
 311     }
 312 if (lp->rxpb) {
 313     lp->rxpboffset = lp->rxpbsize = 0;
 314     FREE (lp->rxpb);
 315     lp->rxpb       = NULL;
 316     }
 317 if (lp->txpb) {
 318     lp->txpbsize   = lp->txppsize = lp->txppoffset = 0;
 319     FREE (lp->txpb);
 320     lp->txpb       = NULL;
 321     }
 322 (void)memset (lp->rbr, 0, lp->rxbsz);                         /* clear break status array */
 323 return;
 324 }
 325 
 326 /* Report a connection to a line.
 327 
 328    If the indicated line (lp) is speaking the telnet wire protocol, a
 329    notification of the form:
 330 
 331       Connected to the <sim> simulator <dev> device, line <n>
 332 
 333    is sent to the newly connected line.  If the device has only one line, the
 334    "line <n>" part is omitted.  If the device has not been defined, the "<dev>
 335    device" part is omitted.
 336 
 337 */
 338 
 339 static void tmxr_report_connection (TMXR *mp, TMLN *lp)
     /* [previous][next][first][last][top][bottom][index][help] */
 340 {
 341 int32 unwritten, psave;
 342 char  cmsg[160];
 343 char   dmsg[80]  = "";
 344 char   lmsg[80]  = "";
 345 char msgbuf[512] = "";
 346 
 347 if ((!lp->notelnet) || (sim_switches & SWMASK ('V'))) {
 348     (void)sprintf (cmsg, "\n\r\nConnected to the %s simulator ", sim_name);
 349 
 350     if (mp->dptr) {                                     /* device defined? */
 351         (void)sprintf (dmsg, "%s device",                     /* report device name */
 352                        sim_dname (mp->dptr));
 353 
 354         if (mp->lines > 1)                              /* more than one line? */
 355             (void)sprintf (lmsg, ", line %d", (int)(lp-mp->ldsc));/* report the line number */
 356         }
 357 
 358     (void)sprintf (msgbuf, "%s%s%s\r\n\n", cmsg, dmsg, lmsg);
 359     }
 360 
 361 if (!mp->buffered) {
 362     lp->txbpi  = 0;                                      /* init buf pointers */
 363     lp->txbpr  = (int32)(lp->txbsz - strlen (msgbuf));
 364     lp->rxcnt  = lp->txcnt = lp->txdrp = 0;              /* init counters */
 365     lp->rxpcnt = lp->txpcnt = 0;
 366     }
 367 else
 368     if (lp->txcnt > lp->txbsz)
 369         lp->txbpr = (lp->txbpi + 1) % lp->txbsz;
 370     else
 371         lp->txbpr = (int32)(lp->txbsz - strlen (msgbuf));
 372 
 373 psave     = lp->txbpi;                                  /* save insertion pointer */
 374 lp->txbpi = lp->txbpr;                                  /* insert connection message */
 375 tmxr_linemsg (lp, msgbuf);                              /* beginning of buffer */
 376 lp->txbpi = psave;                                      /* restore insertion pointer */
 377 
 378 unwritten = tmxr_send_buffered_data (lp);               /* send the message */
 379 
 380 if (unwritten == 0)                                     /* buffer now empty? */
 381     lp->xmte = 1;                                       /* re-enable transmission if paused */
 382 
 383 lp->txcnt -= (int32)strlen (msgbuf);                    /* adjust statistics */
 384 return;
 385 }
 386 
 387 /* Report a disconnection to a line.
 388 
 389    A notification of the form:
 390 
 391       Disconnected from the <sim> simulator
 392 
 393    is sent to the line about to be disconnected.  We do not flush the buffer
 394    here, because the disconnect routines will do that just after calling us.
 395 */
 396 
 397 static void tmxr_report_disconnection (TMLN *lp)
     /* [previous][next][first][last][top][bottom][index][help] */
 398 {
 399 if (lp->notelnet)
 400     return;
 401 tmxr_linemsgf (lp, "\r\nDisconnected from the %s simulator\r\n\n", sim_name);/* report disconnection */
 402 return;
 403 }
 404 
 405 static int32 loop_write_ex (TMLN *lp, char *buf, int32 length, t_bool prefix_datagram)
     /* [previous][next][first][last][top][bottom][index][help] */
 406 {
 407 int32 written = 0;
 408 int32 loopfree = lp->lpbsz - lp->lpbcnt;
 409 
 410 if (lp->datagram && prefix_datagram) {
 411     if ((size_t)loopfree < (size_t)(length + sizeof(length)))
 412         return written;
 413     loop_write_ex (lp, (char *)&length, sizeof(length), FALSE);
 414     }
 415 while (length) {
 416     int32 chunksize;
 417 
 418     loopfree = lp->lpbsz - lp->lpbcnt;
 419     if (loopfree == 0)
 420         break;
 421     if (loopfree < length)
 422         length = loopfree;
 423     if (lp->lpbpi >= lp->lpbpr)
 424         chunksize = lp->lpbsz - lp->lpbpi;
 425     else
 426         chunksize = lp->lpbpr - lp->lpbpi;
 427     if (chunksize > length)
 428         chunksize = length;
 429     memcpy (&lp->lpb[lp->lpbpi], buf, chunksize);
 430     buf += chunksize;
 431     length -= chunksize;
 432     written += chunksize;
 433     lp->lpbpi = (lp->lpbpi + chunksize) % lp->lpbsz;
 434     }
 435 lp->lpbcnt += written;
 436 return written;
 437 }
 438 
 439 static int32 loop_write (TMLN *lp, char *buf, int32 length)
     /* [previous][next][first][last][top][bottom][index][help] */
 440 {
 441 return loop_write_ex (lp, buf, length, TRUE);
 442 }
 443 
 444 static int32 loop_read_ex (TMLN *lp, char *buf, int32 bufsize)
     /* [previous][next][first][last][top][bottom][index][help] */
 445 {
 446 int32 bytesread = 0;
 447 
 448 while (bufsize > 0) {
 449     int32 chunksize;
 450     int32 loopused = lp->lpbcnt;
 451 
 452     if (loopused < bufsize)
 453         bufsize = loopused;
 454     if (loopused == 0)
 455         break;
 456     if (lp->lpbpi > lp->lpbpr)
 457         chunksize = lp->lpbpi - lp->lpbpr;
 458     else
 459         chunksize = lp->lpbsz - lp->lpbpr;
 460     if (chunksize > bufsize)
 461         chunksize = bufsize;
 462     memcpy (buf, &lp->lpb[lp->lpbpr], chunksize);
 463     buf += chunksize;
 464     bufsize -= chunksize;
 465     bytesread += chunksize;
 466     lp->lpbpr = (lp->lpbpr + chunksize) % lp->lpbsz;
 467     }
 468 lp->lpbcnt -= bytesread;
 469 return bytesread;
 470 }
 471 
 472 static int32 loop_read (TMLN *lp, char *buf, int32 bufsize)
     /* [previous][next][first][last][top][bottom][index][help] */
 473 {
 474 if (lp->datagram) {
 475     int32 pktsize;
 476 
 477     if (lp->lpbcnt < (int32)sizeof(pktsize))
 478         return 0;
 479     if ((sizeof(pktsize) != loop_read_ex (lp, (char *)&pktsize, sizeof(pktsize))) ||
 480         (pktsize > bufsize))
 481         return -1;
 482     bufsize = pktsize;
 483     }
 484 return loop_read_ex (lp, buf, bufsize);
 485 }
 486 
 487 /* Read from a line.
 488 
 489    Up to "length" characters are read into the character buffer associated with
 490    line "lp".  The actual number of characters read is returned.  If no
 491    characters are available, 0 is returned.  If an error occurred while reading,
 492    -1 is returned.
 493 */
 494 
 495 static int32 tmxr_read (TMLN *lp, int32 length)
     /* [previous][next][first][last][top][bottom][index][help] */
 496 {
 497 int32 i = lp->rxbpi;
 498 
 499 if (lp->loopback)
 500     return loop_read (lp, &(lp->rxb[i]), length);
 501 else                                                    /* Telnet connection */
 502     return sim_read_sock (lp->sock, &(lp->rxb[i]), length);
 503 }
 504 
 505 /* Write to a line.
 506 
 507    Up to "length" characters are written from the character buffer associated
 508    with "lp".  The actual number of characters written is returned.  If an error
 509    occurred while writing, -1 is returned.
 510 */
 511 
 512 static int32 tmxr_write (TMLN *lp, int32 length)
     /* [previous][next][first][last][top][bottom][index][help] */
 513 {
 514 int32 written;
 515 int32 i = lp->txbpr;
 516 
 517 if (lp->loopback)
 518   return loop_write (lp, &(lp->txb[i]), length);
 519 
 520 written = sim_write_sock (lp->sock, &(lp->txb[i]), length);
 521 
 522 if (written == SOCKET_ERROR)                        /* did an error occur? */
 523   if (lp->datagram)
 524     return written;                             /* ignore errors on datagram sockets */
 525   else
 526     return -1;                                  /* return error indication */
 527 else
 528   return written;
 529 }
 530 
 531 /* Remove a character from the read buffer.
 532 
 533    The character at position "p" in the read buffer associated with line "lp" is
 534    removed by moving all of the following received characters down one position.
 535    The receive break status array is adjusted accordingly.
 536 */
 537 
 538 static void tmxr_rmvrc (TMLN *lp, int32 p)
     /* [previous][next][first][last][top][bottom][index][help] */
 539 {
 540 for ( ; p < lp->rxbpi; p++) {                           /* work from "p" through end of buffer */
 541     lp->rxb[p] = lp->rxb[p + 1];                        /* slide following character down */
 542     lp->rbr[p] = lp->rbr[p + 1];                        /* adjust break status too */
 543     }
 544 
 545 lp->rbr[p] = 0;                                         /* clear potential break from vacated slot */
 546 lp->rxbpi = lp->rxbpi - 1;                              /* drop buffer insert index */
 547 return;
 548 }
 549 
 550 /* Find a line descriptor indicated by unit or number.
 551 
 552    If "uptr" is NULL, then the line descriptor is determined by the line number
 553    passed in "val".  If "uptr" is not NULL, then it must point to a unit
 554    associated with a line, and the line descriptor is determined by the unit
 555    number, which is derived by the position of the unit in the device's unit
 556    array.
 557 
 558    Note: This routine may be called with a UNIT that does not belong to the
 559    device indicated in the TMXR structure.  That is, the multiplexer lines may
 560    belong to a device other than the one attached to the socket (the HP 2100 MUX
 561    device is one example).  Therefore, we must look up the device from the unit
 562    at each call, rather than depending on the DEVICE pointer stored in the TMXR.
 563 */
 564 
 565 static TMLN *tmxr_find_ldsc (UNIT *uptr, int32 val, const TMXR *mp)
     /* [previous][next][first][last][top][bottom][index][help] */
 566 {
 567 if (mp == NULL)                                         /* invalid multiplexer descriptor? */
 568     return NULL;                                        /* programming error! */
 569 if (uptr) {                                             /* called from SET? */
 570     DEVICE *dptr = find_dev_from_unit (uptr);           /* find device */
 571     if (dptr == NULL)                                   /* what?? */
 572         return NULL;
 573     val = (int32) (uptr - dptr->units);                 /* implicit line # */
 574     }
 575 if ((val < 0) || (val >= mp->lines))                    /* invalid line? */
 576     return NULL;
 577 return mp->ldsc + val;                                  /* line descriptor */
 578 }
 579 
 580 /* Get a line descriptor indicated by a string or unit.
 581 
 582    A pointer to the line descriptor associated with multiplexer "mp" and unit
 583    "uptr" or specified by string "cptr" is returned.  If "uptr" is non-null,
 584    then the unit number within its associated device implies the line number.
 585    If "uptr" is null, then the string "cptr" is parsed for a decimal line
 586    number.  If the line number is missing, malformed, or outside of the range of
 587    line numbers associated with "mp", then NULL is returned with status set to
 588    SCPE_ARG.
 589 
 590    Implementation note:
 591 
 592     1. A return status of SCPE_IERR implies a programming error (passing an
 593        invalid pointer or an invalid unit).
 594 */
 595 
 596 static TMLN *tmxr_get_ldsc (UNIT *uptr, const char *cptr, TMXR *mp, t_stat *status)
     /* [previous][next][first][last][top][bottom][index][help] */
 597 {
 598 t_value  ln;
 599 TMLN    *lp = NULL;
 600 t_stat   code = SCPE_OK;
 601 
 602 if (mp == NULL)                                         /* missing mux descriptor? */
 603     code = SCPE_IERR;                                   /* programming error! */
 604 
 605 else if (uptr) {                                        /* implied line form? */
 606     lp = tmxr_find_ldsc (uptr, mp->lines, mp);          /* determine line from unit */
 607 
 608     if (lp == NULL)                                     /* invalid line number? */
 609         code = SCPE_IERR;                               /* programming error! */
 610     }
 611 
 612 else if (cptr == NULL)                                  /* named line form, parameter supplied? */
 613     code = SCPE_MISVAL;                                 /* no, so report missing */
 614 
 615 else {
 616     ln = get_uint (cptr, 10, mp->lines - 1, &code);     /* get line number */
 617 
 618     if (code == SCPE_OK)                                /* line number OK? */
 619         lp = mp->ldsc + (int32) ln;                     /* use as index to determine line */
 620     }
 621 
 622 if (status)                                             /* return value pointer supplied? */
 623     *status = code;                                     /* store return status value */
 624 
 625 return lp;                                              /* return pointer to line descriptor */
 626 }
 627 
 628 /* Generate the Attach string which will fully configure the multiplexer
 629 
 630    Inputs:
 631         old     =       pointer to the original configuration string which will be replaced
 632         *mp     =       pointer to multiplexer
 633 
 634    Output:
 635         a complete attach string for the current state of the multiplexer
 636 
 637 */
 638 static char *growstring(char **string, size_t growth)
     /* [previous][next][first][last][top][bottom][index][help] */
 639 {
 640 if (!*string)
 641   {
 642     fprintf(stderr, "\rFATAL: Bugcheck! Aborting at %s[%s:%d]\r\n",
 643             __func__, __FILE__, __LINE__);
 644 #if defined(USE_BACKTRACE)
 645 # if defined(SIGUSR2)
 646         (void)raise(SIGUSR2);
 647         /*NOTREACHED*/ /* unreachable */
 648 # endif /* if defined(SIGUSR2) */
 649 #endif /* if defined(USE_BACKTRACE) */
 650     abort();
 651   }
 652 *string = (char *)realloc (*string, 1 + (*string ? strlen (*string) : 0) + growth);
 653 if (!*string)
 654   {
 655     fprintf(stderr, "\rFATAL: Out of memory! Aborting at %s[%s:%d]\r\n",
 656             __func__, __FILE__, __LINE__);
 657 #if defined(USE_BACKTRACE)
 658 # if defined(SIGUSR2)
 659         (void)raise(SIGUSR2);
 660         /*NOTREACHED*/ /* unreachable */
 661 # endif /* if defined(SIGUSR2) */
 662 #endif /* if defined(USE_BACKTRACE) */
 663     abort();
 664   }
 665 return *string + strlen(*string);
 666 }
 667 
 668 static char *tmxr_mux_attach_string(char *old, TMXR *mp)
     /* [previous][next][first][last][top][bottom][index][help] */
 669 {
 670 char* tptr = NULL;
 671 int32 i;
 672 TMLN *lp;
 673 
 674 FREE (old);
 675 tptr = (char *) calloc (1, 1);
 676 if (tptr == NULL)                                       /* no more mem? */
 677     return tptr;
 678 
 679 if (mp->port)                                           /* copy port */
 680     (void)sprintf (growstring(&tptr, 33 + strlen (mp->port)), "%s%s",
 681                    mp->port, mp->notelnet ? ";notelnet" : "");
 682 if (mp->logfiletmpl[0])                                 /* logfile info */
 683     (void)sprintf (growstring(&tptr, 7 + strlen (mp->logfiletmpl)), ",Log=%s",
 684                    mp->logfiletmpl);
 685 while ((*tptr == ',') || (*tptr == ' '))
 686     memmove (tptr, tptr+1, strlen(tptr+1)+1);
 687 for (i=0; i<mp->lines; ++i) {
 688     char *lptr;
 689     lp = mp->ldsc + i;
 690 
 691     lptr = tmxr_line_attach_string(lp);
 692     if (lptr) {
 693         char *tempstr = growstring(&tptr, 10 + strlen(lptr));
 694         (void)sprintf(tempstr, "%s%s", *tptr ? "," : "", lptr);
 695         FREE (lptr);
 696         }
 697     }
 698 if (mp->lines == 1)
 699     while ((*tptr == ',') || (*tptr == ' '))
 700         memcpy (tptr, tptr+1, strlen(tptr+1)+1);
 701 if (*tptr == '\0') {
 702     FREE (tptr);
 703     tptr = NULL;
 704     }
 705 return tptr;
 706 }
 707 
 708 /* Global routines */
 709 
 710 /* Return the Line specific attach setup currently configured for a given line
 711 
 712    Inputs:
 713         *lp     =       pointer to terminal line descriptor
 714    Outputs:
 715         a string which can be used to reconfigure the line,
 716         NULL if the line isn't configured
 717 
 718    Note: The returned string is dynamically allocated memory and must be freed
 719          when it is no longer needed by calling free
 720 
 721 */
 722 
 723 char *tmxr_line_attach_string(TMLN *lp)
     /* [previous][next][first][last][top][bottom][index][help] */
 724 {
 725 char* tptr = NULL;
 726 
 727 tptr = (char *) calloc (1, 1);
 728 if (tptr == NULL)                                       /* no more mem? */
 729     return tptr;
 730 
 731 if (lp->destination || lp->port || lp->txlogname) {
 732     if ((lp->mp->lines > 1) || (lp->port))
 733         (void)sprintf (growstring(&tptr, 32), "Line=%d", (int)(lp-lp->mp->ldsc));
 734     if (lp->modem_control != lp->mp->modem_control)
 735         (void)sprintf (growstring(&tptr, 32), ",%s", lp->modem_control ? "Modem" : "NoModem");
 736     if (lp->txbfd && (lp->txbsz != lp->mp->buffered))
 737         (void)sprintf (growstring(&tptr, 32), ",Buffered=%d", lp->txbsz);
 738     if (!lp->txbfd && (lp->mp->buffered > 0))
 739         (void)sprintf (growstring(&tptr, 32), ",UnBuffered");
 740     if (lp->mp->datagram != lp->datagram)
 741         (void)sprintf (growstring(&tptr, 8), ",%s", lp->datagram ? "UDP" : "TCP");
 742     if (lp->mp->packet != lp->packet)
 743         (void)sprintf (growstring(&tptr, 8), ",Packet");
 744     if (lp->port)
 745         (void)sprintf (growstring(&tptr, 32 + strlen (lp->port)), ",%s%s",
 746                        lp->port, ((lp->mp->notelnet != lp->notelnet) && \
 747                            (!lp->datagram)) ? (lp->notelnet ? ";notelnet" : ";telnet") : "");
 748     if (lp->destination) {
 749             (void)sprintf (growstring(&tptr, 25 + strlen (lp->destination)),
 750                            ",Connect=%s%s", lp->destination,
 751                            ((lp->mp->notelnet != lp->notelnet) && \
 752                                (!lp->datagram)) ? (lp->notelnet ? ";notelnet" : ";telnet") : "");
 753         }
 754     if (lp->txlogname)
 755         (void)sprintf (growstring(&tptr, 12 + strlen (lp->txlogname)), ",Log=%s", lp->txlogname);
 756     if (lp->loopback)
 757         (void)sprintf (growstring(&tptr, 12 ), ",Loopback");
 758     }
 759 if (*tptr == '\0') {
 760     FREE (tptr);
 761     tptr = NULL;
 762     }
 763 return tptr;
 764 }
 765 
 766 static inline uint32_t
 767 hash32s(const void *buf, size_t len, uint32_t h)
     /* [previous][next][first][last][top][bottom][index][help] */
 768 {
 769   const unsigned char *p = buf;
 770 
 771   for (size_t i = 0; i < len; i++)
 772     h = h * 31 + p[i];
 773 
 774   h ^= h >> 17;
 775   h *= UINT32_C(0xed5ad4bb);
 776   h ^= h >> 11;
 777   h *= UINT32_C(0xac4c1b51);
 778   h ^= h >> 15;
 779   h *= UINT32_C(0x31848bab);
 780   h ^= h >> 14;
 781 
 782   return h;
 783 }
 784 
 785 /* Poll for new connection
 786 
 787    Called from unit service routine to test for new connection
 788 
 789    Inputs:
 790         *mp     =       pointer to terminal multiplexer descriptor
 791    Outputs:
 792         line number activated, -1 if none
 793 
 794    If a connection order is defined for the descriptor, and the first value is
 795    not -1 (indicating default order), then the order array is used to find an
 796    open line.  Otherwise, a search is made of all lines in numerical sequence.
 797 */
 798 
 799 int32 tmxr_poll_conn (TMXR *mp)
     /* [previous][next][first][last][top][bottom][index][help] */
 800 {
 801 SOCKET newsock;
 802 TMLN *lp = NULL;
 803 int32 *op;
 804 int32 i, j;
 805 int st1ret;
 806 char *address;
 807 char msg[512];
 808 uint32 poll_time = sim_os_msec ();
 809 struct timespec ts;
 810 
 811 (void)lp;
 812 (void)memset (msg, 0, sizeof (msg));
 813 if (mp->last_poll_time == 0) {                          /* first poll initializations */
 814     UNIT *uptr = mp->uptr;
 815 
 816     if (!uptr)                                          /* Attached ? */
 817         return -1;                                      /* No connections are possible! */
 818 
 819     if (mp->poll_interval == 0)                         /* Assure reasonable polling interval */
 820         mp->poll_interval = TMXR_DEFAULT_CONNECT_POLL_INTERVAL;
 821 
 822     if (!(uptr->dynflags & TMUF_NOASYNCH)) {            /* if asynch not disabled */
 823         uptr->dynflags |= UNIT_TM_POLL;                 /* tag as polling unit */
 824         sim_cancel (uptr);
 825         }
 826     for (i=0; i < mp->lines; i++) {
 827         uptr = mp->ldsc[i].uptr ? mp->ldsc[i].uptr : mp->uptr;
 828 
 829         if (!(mp->uptr->dynflags & TMUF_NOASYNCH)) {    /* if asynch not disabled */
 830             uptr->dynflags |= UNIT_TM_POLL;             /* tag as polling unit */
 831             sim_cancel (uptr);
 832             }
 833         }
 834     }
 835 
 836 if ((poll_time - mp->last_poll_time) < mp->poll_interval*1000)
 837     return -1;                                          /* too soon to try */
 838 
 839   st1ret = clock_gettime(SIR_WALLCLOCK, &ts);
 840   if (st1ret != 0)
 841     {
 842       fprintf (stderr, "\rFATAL: clock_gettime failure! Aborting at %s[%s:%d]\r\n",
 843                __func__, __FILE__, __LINE__);
 844 #if defined(USE_BACKTRACE)
 845 # if defined(SIGUSR2)
 846       (void)raise(SIGUSR2);
 847       /*NOTREACHED*/ /* unreachable */
 848 # endif /* if defined(SIGUSR2) */
 849 #endif /* if defined(USE_BACKTRACE) */
 850       abort();
 851     }
 852 
 853 uint32_t h = 0;  /* initial hash value */
 854 #if __STDC_VERSION__ < 201112L
 855 /* LINTED E_OLD_STYLE_FUNC_DECL */
 856 void *(*mallocptr)() = malloc;
 857 h = hash32s(&mallocptr, sizeof(mallocptr), h);
 858 #endif /* __STDC_VERSION__ < 201112L */
 859 void *ptr = &ptr;
 860 h = hash32s(&ptr, sizeof(ptr), h);
 861 time_t t = time(0);
 862 h = hash32s(&t, sizeof(t), h);
 863 #if !defined(_AIX)
 864 for (int i = 0; i < 1000; i++)
 865   {
 866     unsigned long counter = 0;
 867     clock_t start = clock();
 868     while (clock() == start)
 869       {
 870         counter++;
 871       }
 872     h = hash32s(&start, sizeof(start), h);
 873     h = hash32s(&counter, sizeof(counter), h);
 874   }
 875 #endif /* if !defined(_AIX) */
 876 int mypid = (int)_sir_getpid();
 877 h = hash32s(&mypid, sizeof(mypid), h);
 878 char rnd[4];
 879 FILE *f = fopen("/dev/urandom", "rb");
 880 if (f)
 881   {
 882     if (fread(rnd, sizeof(rnd), 1, f))
 883       {
 884         h = hash32s(rnd, sizeof(rnd), h);
 885       }
 886     fclose(f);
 887   }
 888 srandom(h);
 889 
 890 mp->last_poll_time = poll_time;
 891 
 892 /* Check for a pending Telnet/tcp connection */
 893 
 894 if (mp->master) {
 895     if (mp->ring_sock != INVALID_SOCKET) {  /* Use currently 'ringing' socket if one is active */
 896         newsock       = mp->ring_sock;
 897         mp->ring_sock = INVALID_SOCKET;
 898         address       = mp->ring_ipad;
 899         mp->ring_ipad = NULL;
 900         }
 901     else
 902         newsock = sim_accept_conn_ex (mp->master, &address, (mp->packet ? SIM_SOCK_OPT_NODELAY : 0));/* poll connect */
 903 
 904     if (newsock != INVALID_SOCKET) {                    /* got a live one? */
 905         snprintf (msg, sizeof (msg)-1, "tmxr_poll_conn() - Connection from %s", address);
 906         op = mp->lnorder;                               /* get line connection order list pointer */
 907         i = mp->lines;                                  /* play it safe in case lines == 0 */
 908         ++mp->sessions;                                 /* count the new session */
 909 
 910         for (j = 0; j < mp->lines; j++, i++) {          /* find next avail line */
 911             if (op && (*op >= 0) && (*op < mp->lines))  /* order list present and valid? */
 912                 i = *op++;                              /* get next line in list to try */
 913             else                                        /* no list or not used or range error */
 914                 i = j;                                  /* get next sequential line */
 915 
 916             lp = mp->ldsc + i;                          /* get pointer to line descriptor */
 917             if ((lp->conn == FALSE) &&                  /* is the line available? */
 918                 (lp->destination == NULL) &&
 919                 (lp->master == 0) &&
 920                 (lp->ser_connect_pending == FALSE) &&
 921                 (lp->modem_control ? ((lp->modembits & TMXR_MDM_DTR) != 0) : TRUE))
 922                 break;                                  /* yes, so stop search */
 923             }
 924 
 925         if (i >= mp->lines) {                           /* all busy? */
 926             int32 ringable_count = 0;
 927 
 928             for (j = 0; j < mp->lines; j++, i++) {      /* find next avail line */
 929                 lp = mp->ldsc + j;                      /* get pointer to line descriptor */
 930                 if ((lp->conn == FALSE) &&              /* is the line available? */
 931                     (lp->destination == NULL) &&
 932                     (lp->master == 0) &&
 933                     (lp->ser_connect_pending == FALSE) &&
 934                     ((lp->modembits & TMXR_MDM_DTR) == 0)) {
 935                     ++ringable_count;
 936                     tmxr_set_get_modem_bits (lp, TMXR_MDM_RNG, 0, NULL);
 937                     }
 938                 }
 939             if (ringable_count > 0) {
 940                 if (mp->ring_start_time == 0) {
 941                     mp->ring_start_time = poll_time;
 942                     mp->ring_sock = newsock;
 943                     mp->ring_ipad = address;
 944                     }
 945                 else {
 946                     if ((poll_time - mp->ring_start_time) < TMXR_MODEM_RING_TIME*1000) {
 947                         mp->ring_sock = newsock;
 948                         mp->ring_ipad = address;
 949                         }
 950                     else {                                      /* Timeout waiting for DTR */
 951                         int ln;
 952 
 953                         /* turn off pending ring signals */
 954                         for (ln = 0; ln < lp->mp->lines; ln++) {
 955                             TMLN *tlp = lp->mp->ldsc + ln;
 956                             if (((tlp->destination == NULL) && (tlp->master == 0)) &&
 957                                 (tlp->modembits & TMXR_MDM_RNG) && (tlp->conn == FALSE))
 958                                 tlp->modembits &= ~TMXR_MDM_RNG;
 959                             }
 960                         mp->ring_start_time = 0;
 961                         tmxr_msg (newsock, "No answer on any connection\r\n");
 962                         sim_close_sock (newsock);
 963                         FREE (address);
 964                         }
 965                     }
 966                 }
 967             else {
 968                 tmxr_msg (newsock, "All connections busy\r\n");
 969                 sim_close_sock (newsock);
 970                 FREE (address);
 971                 }
 972             }
 973         else {
 974             lp = mp->ldsc + i;                          /* get line desc */
 975             lp->conn = TRUE;                            /* record connection */
 976             lp->sock = newsock;                         /* save socket */
 977             lp->ipad = address;                         /* ip address */
 978             tmxr_init_line (lp);                        /* init line */
 979             lp->notelnet = mp->notelnet;                /* apply mux default telnet setting */
 980             if (!lp->notelnet) {
 981                 sim_write_sock (newsock, (char *)mantra, sizeof(mantra));
 982                 lp->telnet_sent_opts = (uint8 *)realloc (lp->telnet_sent_opts, 256);
 983                 if (!lp->telnet_sent_opts)
 984                   {
 985                     fprintf (stderr, "\rFATAL: Out of memory! Aborting at %s[%s:%d]\r\n",
 986                              __func__, __FILE__, __LINE__);
 987 #if defined(USE_BACKTRACE)
 988 # if defined(SIGUSR2)
 989                     (void)raise(SIGUSR2);
 990                     /*NOTREACHED*/ /* unreachable */
 991 # endif /* if defined(SIGUSR2 */
 992 #endif /* if defined(USE_BACKTRACE) */
 993                     abort();
 994                   }
 995                 (void)memset (lp->telnet_sent_opts, 0, 256);
 996                 }
 997             tmxr_report_connection (mp, lp);
 998             lp->cnms = sim_os_msec ();                  /* time of connection */
 999             return i;
1000             }
1001         }                                               /* end if newsock */
1002     }
1003 
1004 /* Look for per line listeners or outbound connecting sockets */
1005 for (i = 0; i < mp->lines; i++) {                       /* check each line in sequence */
1006     int j, r = (int)random();
1007     lp = mp->ldsc + i;                                  /* get pointer to line descriptor */
1008 
1009     if (lp->ser_connect_pending) {
1010         lp->ser_connect_pending = FALSE;
1011         lp->conn = TRUE;
1012         return i;
1013         }
1014 
1015     /* Don't service network connections for loopbacked lines */
1016 
1017     if (lp->loopback)
1018         continue;
1019 
1020     /* If two simulators are configured with symmetric virtual null modem
1021        cables pointing at each other, there may be a problem establishing
1022        a connection if both systems happen to be checking for the success
1023        of their connections in the exact same order.  They can each observe
1024        success in their respective outgoing connections, which haven't
1025        actually been 'accept'ed on the peer end of the connection.
1026        We address this issue by checking for the success of an outgoing
1027        connection and the arrival of an incoming one in a random order.
1028      */
1029     for (j=0; j<2; j++)
1030         switch (((unsigned)j+(unsigned)r)&1) {
1031             case 0:
1032                 if (lp->connecting) {                           /* connecting? */
1033                     char *sockname, *peername;
1034 
1035                     switch (sim_check_conn(lp->connecting, FALSE))
1036                         {
1037                         case 1:                                 /* successful connection */
1038                             lp->conn = TRUE;                    /* record connection */
1039                             lp->sock = lp->connecting;          /* it now looks normal */
1040                             lp->connecting = 0;
1041                             int lpdlen = 1;
1042                             if (lp->destination != NULL)
1043                                 lpdlen = 1+strlen (lp->destination);
1044                             lp->ipad = (char *)realloc (lp->ipad, lpdlen);
1045                             if (!lp->ipad)
1046                               {
1047                                 fprintf(stderr, "\rFATAL: Out of memory! Aborting at %s[%s:%d]\r\n",
1048                                         __func__, __FILE__, __LINE__);
1049 #if defined(USE_BACKTRACE)
1050 # if defined(SIGUSR2)
1051                                 (void)raise(SIGUSR2);
1052                                 /*NOTREACHED*/ /* unreachable */
1053 # endif /* if defined(SIGUSR2) */
1054 #endif /* if defined(USE_BACKTRACE) */
1055                                 abort();
1056                               }
1057                             if (lp->destination != NULL)
1058                                 (void)strcpy (lp->ipad, lp->destination);
1059                             lp->cnms = sim_os_msec ();
1060                             sim_getnames_sock (lp->sock, &sockname, &peername);
1061                             if (lp->destination)
1062                               snprintf (msg, sizeof(msg)-1,
1063                                         "tmxr_poll_conn() - Outgoing Line Connection to %s (%s->%s) established",
1064                                         lp->destination, sockname, peername);
1065                             FREE (sockname);
1066                             FREE (peername);
1067                             return i;
1068                         case -1:                                /* failed connection */
1069                             snprintf (msg, sizeof(msg)-1, "tmxr_poll_conn() - Outgoing Line Connection to %s failed",
1070                                       lp->destination);
1071                             tmxr_reset_ln (lp);                 /* retry */
1072                             break;
1073                         }
1074                     }
1075                 break;
1076             case 1:
1077                 if (lp->master) {                                   /* Check for a pending Telnet/tcp connection */
1078                     while (INVALID_SOCKET != (newsock = sim_accept_conn_ex (lp->master, &address,
1079                                               (lp->packet ? SIM_SOCK_OPT_NODELAY : 0)))) {/* got a live one? */
1080                         char *sockname, *peername;
1081 
1082                         sim_getnames_sock (newsock, &sockname, &peername);
1083                         snprintf (msg, sizeof(msg)-1, "tmxr_poll_conn() - Incoming Line Connection from %s (%s->%s)",
1084                                   address, peername, sockname);
1085                         FREE (sockname);
1086                         FREE (peername);
1087                         ++mp->sessions;                             /* count the new session */
1088 
1089                         if (lp->destination) {                      /* Virtual Null Modem Cable? */
1090                             char host[sizeof(msg) - 64];
1091 
1092                             if (sim_parse_addr (lp->destination, host, sizeof(host), NULL, NULL, 0, NULL, address)) {
1093                                 tmxr_msg (newsock, "Rejecting connection from unexpected source\r\n");
1094                                 snprintf (msg, sizeof(msg)-1, "tmxr_poll_conn() - Rejecting line connection from: %s, Expected: %s",
1095                                           address, host);
1096                                 sim_close_sock (newsock);
1097                                 FREE (address);
1098                                 continue;                           /* Try for another connection */
1099                                 }
1100                             if (lp->connecting) {
1101                                 snprintf (msg, sizeof(msg)-1, "tmxr_poll_conn() - aborting outgoing line connection attempt to: %s",
1102                                           lp->destination);
1103                                 sim_close_sock (lp->connecting);    /* abort our as yet unconnnected socket */
1104                                 lp->connecting = 0;
1105                                 }
1106                             }
1107                         if (lp->conn == FALSE) {                    /* is the line available? */
1108                             if ((!lp->modem_control) || (lp->modembits & TMXR_MDM_DTR)) {
1109                                 lp->conn = TRUE;                    /* record connection */
1110                                 lp->sock = newsock;                 /* save socket */
1111                                 lp->ipad = address;                 /* ip address */
1112                                 tmxr_init_line (lp);                /* init line */
1113                                 if (!lp->notelnet) {
1114                                     sim_write_sock (newsock, (char *)mantra, sizeof(mantra));
1115                                     lp->telnet_sent_opts = (uint8 *)realloc (lp->telnet_sent_opts, 256);
1116                                     if (!lp->telnet_sent_opts)
1117                                       {
1118                                         fprintf (stderr, "\rFATAL: Out of memory! Aborting at %s[%s:%d]\r\n",
1119                                                  __func__, __FILE__, __LINE__);
1120 #if defined(USE_BACKTRACE)
1121 # if defined(SIGUSR2)
1122                                         (void)raise(SIGUSR2);
1123                                         /*NOTREACHED*/ /* unreachable */
1124 # endif /* if defined(SIGUSR2) */
1125 #endif /* if defined(USE_BACKTRACE) */
1126                                         abort();
1127                                       }
1128                                     (void)memset (lp->telnet_sent_opts, 0, 256);
1129                                     }
1130                                 tmxr_report_connection (mp, lp);
1131                                 lp->cnms = sim_os_msec ();          /* time of connection */
1132                                 return i;
1133                                 }
1134                             else {
1135                                 tmxr_msg (newsock, "Line connection not available\r\n");
1136                                 sim_close_sock (newsock);
1137                                 FREE (address);
1138                                 }
1139                             }
1140                         else {
1141                             tmxr_msg (newsock, "Line connection busy\r\n");
1142                             sim_close_sock (newsock);
1143                             FREE (address);
1144                             }
1145                         }
1146                     }
1147                 break;
1148             }
1149 
1150     /* Check for needed outgoing connection initiation */
1151 
1152     if (lp->destination && (!lp->sock) && (!lp->connecting) &&
1153         (!lp->modem_control || (lp->modembits & TMXR_MDM_DTR))) {
1154         snprintf (msg, sizeof(msg)-1, "tmxr_poll_conn() - establishing outgoing connection to: %s", lp->destination);
1155                   lp->connecting = sim_connect_sock_ex (lp->datagram ? lp->port : NULL,
1156                                                         lp->destination, "localhost", NULL,
1157                                                         (lp->datagram   ? SIM_SOCK_OPT_DATAGRAM : 0) |
1158                                                         (lp->mp->packet ? SIM_SOCK_OPT_NODELAY  : 0) |
1159                                                         SIM_SOCK_OPT_BLOCKING);
1160         }
1161 
1162     }
1163 
1164 return -1;                                              /* no new connections made */
1165 }
1166 
1167 /* Reset a line. */
1168 
1169 static t_stat tmxr_reset_ln_ex (TMLN *lp, t_bool closeserial)
     /* [previous][next][first][last][top][bottom][index][help] */
1170 {
1171 char msg[512];
1172 
1173 if (lp->txlog)
1174     (void)fflush (lp->txlog);                           /* flush log */
1175 
1176 tmxr_send_buffered_data (lp);                           /* send any buffered data */
1177 
1178 snprintf (msg, sizeof(msg)-1, "tmxr_reset_ln_ex(%s)", closeserial ? "TRUE" : "FALSE");
1179 
1180     if (lp->sock) {
1181         sim_close_sock (lp->sock);                      /* close socket */
1182         FREE (lp->telnet_sent_opts);
1183         lp->telnet_sent_opts = NULL;
1184         lp->sock = 0;
1185         lp->conn = FALSE;
1186         lp->cnms = 0;
1187         lp->xmte = 1;
1188         }
1189 FREE(lp->ipad);
1190 lp->ipad = NULL;
1191 if ((lp->destination)
1192 ) {
1193     if (lp->connecting) {
1194         sim_close_sock (lp->connecting);
1195         lp->connecting = 0;
1196         }
1197     if ((!lp->modem_control) || (lp->modembits & TMXR_MDM_DTR)) {
1198         snprintf (msg, sizeof(msg)-1, "tmxr_reset_ln_ex() - connecting to %s", lp->destination);
1199         lp->connecting = sim_connect_sock_ex (lp->datagram ? lp->port : NULL,
1200                                               lp->destination, "localhost", NULL,
1201                                               (lp->datagram ? SIM_SOCK_OPT_DATAGRAM : 0) |
1202                                               (lp->packet   ? SIM_SOCK_OPT_NODELAY  : 0) |
1203                                               SIM_SOCK_OPT_BLOCKING);
1204         }
1205     }
1206 tmxr_init_line (lp);                                /* initialize line state */
1207 return SCPE_OK;
1208 }
1209 
1210 t_stat tmxr_close_ln (TMLN *lp)
     /* [previous][next][first][last][top][bottom][index][help] */
1211 {
1212 return tmxr_reset_ln_ex (lp, TRUE);
1213 }
1214 
1215 t_stat tmxr_reset_ln (TMLN *lp)
     /* [previous][next][first][last][top][bottom][index][help] */
1216 {
1217 return tmxr_reset_ln_ex (lp, FALSE);
1218 }
1219 
1220 /* Manipulate the modem control bits of a specific line
1221 
1222    Inputs:
1223         *lp     =       pointer to terminal line descriptor
1224         bits_to_set     TMXR_MDM_DTR and/or TMXR_MDM_RTS as desired
1225         bits_to_clear   TMXR_MDM_DTR and/or TMXR_MDM_RTS as desired
1226 
1227    Output:
1228         incoming_bits   if non NULL, returns the current stat of DCD,
1229                         RNG, CTS and DSR along with the current state
1230                         of DTR and RTS
1231 */
1232 t_stat tmxr_set_get_modem_bits (TMLN *lp, int32 bits_to_set, int32 bits_to_clear, int32 *incoming_bits)
     /* [previous][next][first][last][top][bottom][index][help] */
1233 {
1234 int32 before_modem_bits, incoming_state = 0;
1235 DEVICE *dptr;
1236 
1237 if ((bits_to_set & ~(TMXR_MDM_OUTGOING)) ||         /* Assure only settable bits */
1238     (bits_to_clear & ~(TMXR_MDM_OUTGOING)) ||
1239     (bits_to_set & bits_to_clear))                  /* and can't set and clear the same bits */
1240     return SCPE_ARG;
1241 before_modem_bits = lp->modembits;
1242 lp->modembits |= bits_to_set;
1243 lp->modembits &= ~(bits_to_clear | TMXR_MDM_INCOMING);
1244 if ((lp->sock) || (lp->loopback)) {
1245     if (lp->modembits & TMXR_MDM_DTR) {
1246         incoming_state = TMXR_MDM_DSR;
1247         if (lp->modembits & TMXR_MDM_RTS)
1248             incoming_state |= TMXR_MDM_CTS;
1249         if (lp->halfduplex) {
1250             if (incoming_state & TMXR_MDM_CTS)
1251                 incoming_state |= TMXR_MDM_DCD;
1252             }
1253         else
1254             incoming_state |= TMXR_MDM_DCD;
1255         }
1256     //else
1257         //XXX: Coverity warns as unreachable / this code NOT in use by DPS8M
1258         //incoming_state = TMXR_MDM_DCD | TMXR_MDM_DSR | ((lp->modembits & TMXR_MDM_DTR) ? 0 : TMXR_MDM_RNG);
1259     }
1260 else {
1261     if (((before_modem_bits & TMXR_MDM_DTR) == 0) &&    /* Upward transition of DTR? */
1262         ((lp->modembits & TMXR_MDM_DTR) != 0)     &&
1263         (lp->conn == FALSE)                       &&    /* Not connected */
1264         (lp->modembits & TMXR_MDM_RNG)) {               /* and Ring Signal Present */
1265         if ((lp->destination == NULL) &&
1266             (lp->master == 0) &&
1267             (lp->mp && (lp->mp->ring_sock))) {
1268             int ln;
1269 
1270             lp->conn = TRUE;                            /* record connection */
1271             lp->sock = lp->mp->ring_sock;               /* save socket */
1272             lp->mp->ring_sock = INVALID_SOCKET;
1273             lp->ipad = lp->mp->ring_ipad;               /* ip address */
1274             lp->mp->ring_ipad = NULL;
1275             lp->mp->ring_start_time = 0;
1276             tmxr_init_line (lp);                        /* init line */
1277             lp->notelnet = lp->mp->notelnet;            /* apply mux default telnet setting */
1278             if (!lp->notelnet) {
1279                 sim_write_sock (lp->sock, (char *)mantra, sizeof(mantra));
1280                 lp->telnet_sent_opts = (uint8 *)realloc (lp->telnet_sent_opts, 256);
1281                 if (!lp->telnet_sent_opts)
1282                   {
1283                     fprintf (stderr, "\rFATAL: Out of memory! Aborting at %s[%s:%d]\r\n",
1284                              __func__, __FILE__, __LINE__);
1285 #if defined(USE_BACKTRACE)
1286 # if defined(SIGUSR2)
1287                     (void)raise(SIGUSR2);
1288                     /*NOTREACHED*/ /* unreachable */
1289 # endif /* if defined(SIGUSR2) */
1290 #endif /* if defined(USE_BACKTRACE) */
1291                     abort();
1292                   }
1293                 (void)memset (lp->telnet_sent_opts, 0, 256);
1294                 }
1295             tmxr_report_connection (lp->mp, lp);
1296             lp->cnms = sim_os_msec ();                  /* time of connection */
1297             lp->modembits &= ~TMXR_MDM_RNG;             /* turn off ring on this line*/
1298             /* turn off other pending ring signals */
1299             for (ln = 0; ln < lp->mp->lines; ln++) {
1300                 TMLN *tlp = lp->mp->ldsc + ln;
1301                 if (((tlp->destination == NULL) && (tlp->master == 0)) &&
1302                     (tlp->modembits & TMXR_MDM_RNG) && (tlp->conn == FALSE))
1303                     tlp->modembits &= ~TMXR_MDM_RNG;
1304                 }
1305             }
1306         }
1307     if ((lp->master) || (lp->mp && lp->mp->master) ||
1308         (lp->port && lp->destination))
1309         incoming_state = TMXR_MDM_DSR;
1310     else
1311         incoming_state = 0;
1312     }
1313 lp->modembits |= incoming_state;
1314 dptr = (lp->dptr ? lp->dptr : (lp->mp ? lp->mp->dptr : NULL));
1315 (void)dptr;
1316 // if ((lp->modembits != before_modem_bits) && (sim_deb && lp->mp && dptr)) {
1317    // sim_debug_bits (TMXR_DBG_MDM, dptr, tmxr_modem_bits, before_modem_bits, lp->modembits, FALSE);
1318    // sim_debug (TMXR_DBG_MDM, dptr, " - Line %d - %p\n", (int)(lp-lp->mp->ldsc), lp->txb);
1319     // }
1320 if (incoming_bits)
1321     *incoming_bits = lp->modembits;
1322 if (lp->mp && lp->modem_control) {                  /* This API ONLY works on modem_control enabled multiplexer lines */
1323     if (bits_to_set | bits_to_clear) {              /* Anything to do? */
1324         if (lp->loopback) {
1325             if ((lp->modembits ^ before_modem_bits) & TMXR_MDM_DTR) { /* DTR changed? */
1326                 lp->ser_connect_pending = (lp->modembits & TMXR_MDM_DTR);
1327                 lp->conn = !(lp->modembits & TMXR_MDM_DTR);
1328                 }
1329             return SCPE_OK;
1330             }
1331         if ((lp->sock) || (lp->connecting)) {
1332             if ((before_modem_bits & bits_to_clear & TMXR_MDM_DTR) != 0) { /* drop DTR? */
1333                 if (lp->sock)
1334                     tmxr_report_disconnection (lp);     /* report closure */
1335                 tmxr_reset_ln (lp);
1336                 }
1337             }
1338         else {
1339             if ((lp->destination) &&                    /* Virtual Null Modem Cable */
1340                 (bits_to_set & ~before_modem_bits &     /* and DTR being Raised */
1341                  TMXR_MDM_DTR)) {
1342                 char msg[512];
1343 
1344                 snprintf (msg, sizeof(msg)-1, "tmxr_set_get_modem_bits() - establishing outgoing connection to: %s",
1345                           lp->destination);
1346                 lp->connecting = sim_connect_sock_ex (lp->datagram ? lp->port : NULL, lp->destination, "localhost", NULL,
1347                                                       (lp->datagram ? SIM_SOCK_OPT_DATAGRAM : 0) |
1348                                                       (lp->packet   ? SIM_SOCK_OPT_NODELAY  : 0) |
1349                                                       SIM_SOCK_OPT_BLOCKING);
1350                 }
1351             }
1352         }
1353     return SCPE_OK;
1354     }
1355 if ((lp->sock) || (lp->connecting)) {
1356     if ((before_modem_bits & bits_to_clear & TMXR_MDM_DTR) != 0) { /* drop DTR? */
1357         if (lp->sock)
1358             tmxr_report_disconnection (lp);     /* report closure */
1359         tmxr_reset_ln (lp);
1360         }
1361     }
1362 return SCPE_INCOMP;
1363 }
1364 
1365 /* Enable or Disable loopback mode on a line
1366 
1367    Inputs:
1368         lp -                the line to change
1369         enable_loopback -   enable or disable flag
1370 
1371    Output:
1372         none
1373 */
1374 t_stat tmxr_set_line_loopback (TMLN *lp, t_bool enable_loopback)
     /* [previous][next][first][last][top][bottom][index][help] */
1375 {
1376 if (lp->loopback == (enable_loopback != FALSE))
1377     return SCPE_OK;                 /* Nothing to do */
1378 lp->loopback = (enable_loopback != FALSE);
1379 if (lp->loopback) {
1380     lp->lpbsz = lp->rxbsz;
1381     lp->lpb = (char *)realloc(lp->lpb, lp->lpbsz);
1382     if (!lp->lpb)
1383       {
1384         fprintf (stderr, "\rFATAL: Out of memory! Aborting at %s[%s:%d]\r\n",
1385                  __func__, __FILE__, __LINE__);
1386 #if defined(USE_BACKTRACE)
1387 # if defined(SIGUSR2)
1388         (void)raise(SIGUSR2);
1389         /*NOTREACHED*/ /* unreachable */
1390 # endif /* if defined(SIGUSR2) */
1391 #endif /* if defined(USE_BACKTRACE) */
1392         abort();
1393       }
1394     lp->lpbcnt = lp->lpbpi = lp->lpbpr = 0;
1395     if (!lp->conn)
1396         lp->ser_connect_pending = TRUE;
1397     }
1398 else {
1399     FREE (lp->lpb);
1400     lp->lpb = NULL;
1401     lp->lpbsz = 0;
1402     }
1403 return SCPE_OK;
1404 }
1405 
1406 t_bool tmxr_get_line_loopback (TMLN *lp)
     /* [previous][next][first][last][top][bottom][index][help] */
1407 {
1408 return (lp->loopback != FALSE);
1409 }
1410 
1411 /* Enable or Disable halfduplex mode on a line
1412 
1413    Inputs:
1414         lp -                the line to change
1415         enable_halfduplex - enable or disable flag
1416 
1417    Output:
1418         none
1419 
1420    When a network connected line is in halfduplex mode, DCD modem signal
1421    track with CTS.  When not in halfduplex mode the DCD modem signal for
1422    network connected lines tracks with DSR.
1423 
1424 */
1425 t_stat tmxr_set_line_halfduplex (TMLN *lp, t_bool enable_halfduplex)
     /* [previous][next][first][last][top][bottom][index][help] */
1426 {
1427 if (lp->halfduplex == (enable_halfduplex != FALSE))
1428     return SCPE_OK;                 /* Nothing to do */
1429 lp->halfduplex = (enable_halfduplex != FALSE);
1430 return SCPE_OK;
1431 }
1432 
1433 t_bool tmxr_get_line_halfduplex (TMLN *lp)
     /* [previous][next][first][last][top][bottom][index][help] */
1434 {
1435 return (lp->halfduplex != FALSE);
1436 }
1437 
1438 /* Get character from specific line
1439 
1440    Inputs:
1441         *lp     =       pointer to terminal line descriptor
1442    Output:
1443         valid + char, 0 if line
1444 
1445    Implementation note:
1446 
1447     1. If a line break was detected coincident with the current character, the
1448        receive break status associated with the character is cleared, and
1449        SCPE_BREAK is ORed into the return value.
1450 */
1451 
1452 int32 tmxr_input_pending_ln (TMLN *lp)
     /* [previous][next][first][last][top][bottom][index][help] */
1453 {
1454 return (lp->rxbpi - lp->rxbpr);
1455 }
1456 
1457 int32 tmxr_getc_ln (TMLN *lp)
     /* [previous][next][first][last][top][bottom][index][help] */
1458 {
1459 int32 j;
1460 t_stat val = 0;
1461 uint32 tmp;
1462 
1463 if ((lp->conn && lp->rcve) &&                           /* conn & enb & */
1464     ((!lp->rxbps) ||                                    /* (!rate limited || enough time passed)? */
1465      (sim_gtime () >= lp->rxnexttime))) {
1466     if (!sim_send_poll_data (&lp->send, &val)) {        /* injected input characters available? */
1467         j = lp->rxbpi - lp->rxbpr;                      /* # input chrs */
1468         if (j) {                                        /* any? */
1469             tmp = lp->rxb[lp->rxbpr];                   /* get char */
1470             val = TMXR_VALID | (tmp & 0377);            /* valid + chr */
1471             if (lp->rbr[lp->rxbpr]) {                   /* break? */
1472                 lp->rbr[lp->rxbpr] = 0;                 /* clear status */
1473                 val = val | SCPE_BREAK;                 /* indicate to caller */
1474                 }
1475             lp->rxbpr = lp->rxbpr + 1;                  /* adv pointer */
1476             }
1477         }
1478     }                                                   /* end if conn */
1479 if (lp->rxbpi == lp->rxbpr)                             /* empty? zero ptrs */
1480     lp->rxbpi = lp->rxbpr = 0;
1481 if (lp->rxbps) {
1482     if (val)
1483         lp->rxnexttime = floor (sim_gtime () + ((lp->rxdelta * sim_timer_inst_per_sec ())/lp->rxbpsfactor));
1484     }
1485 return val;
1486 }
1487 
1488 /* Get packet from specific line
1489 
1490    Inputs:
1491         *lp     =       pointer to terminal line descriptor
1492         **pbuf  =       pointer to pointer of packet contents
1493         *psize  =       pointer to packet size
1494         frame_byte -    byte which separates packets in the tcp stream
1495                         (0 means no separation character)
1496 
1497    Output:
1498         SCPE_LOST       link state lost
1499         SCPE_OK         Packet returned OR no packet available
1500 
1501    Implementation notes:
1502 
1503     1. If a packet is not yet available, then the pbuf address returned is
1504        NULL, but success (SCPE_OK) is returned
1505 */
1506 
1507 t_stat tmxr_get_packet_ln (TMLN *lp, const uint8 **pbuf, size_t *psize)
     /* [previous][next][first][last][top][bottom][index][help] */
1508 {
1509 return tmxr_get_packet_ln_ex (lp, pbuf, psize, 0);
1510 }
1511 
1512 t_stat tmxr_get_packet_ln_ex (TMLN *lp, const uint8 **pbuf, size_t *psize, uint8 frame_byte)
     /* [previous][next][first][last][top][bottom][index][help] */
1513 {
1514 int32 c;
1515 size_t pktsize;
1516 size_t fc_size = (frame_byte ? 1 : 0);
1517 
1518 while (TMXR_VALID & (c = tmxr_getc_ln (lp))) {
1519     if (lp->rxpboffset + 3 > lp->rxpbsize) {
1520         lp->rxpbsize += 512;
1521         lp->rxpb = (uint8 *)realloc (lp->rxpb, lp->rxpbsize);
1522         if (!lp->rxpb)
1523           {
1524             fprintf (stderr, "\rFATAL: Out of memory! Aborting at %s[%s:%d]\r\n",
1525                      __func__, __FILE__, __LINE__);
1526 #if defined(USE_BACKTRACE)
1527 # if defined(SIGUSR2)
1528             (void)raise(SIGUSR2);
1529             /*NOTREACHED*/ /* unreachable */
1530 # endif /* if defined(SIGUSR2) */
1531 #endif /* if defined(USE_BACKTRACE) */
1532             abort();
1533           }
1534         }
1535     if ((lp->rxpboffset == 0) && (fc_size) && (c != frame_byte)) {
1536         continue;
1537         }
1538     if ((lp->datagram) && (lp->rxpboffset == fc_size)) {
1539         /* Datagram packet length is provided as a part of the natural datagram
1540            delivery, for TCP lines, we read the packet length from the data stream.
1541            So, here we stuff packet size into head of packet buffer so it looks like
1542            it was delivered by TCP and the below return logic doesn't have to worry */
1543         lp->rxpb[lp->rxpboffset++] = (uint8)(((1 + lp->rxbpi - lp->rxbpr) >> 8) & 0xFF);
1544         lp->rxpb[lp->rxpboffset++] = (uint8)((1 + lp->rxbpi - lp->rxbpr) & 0xFF);
1545         }
1546     lp->rxpb[lp->rxpboffset++] = c & 0xFF;
1547     if (lp->rxpboffset >= (2 + fc_size)) {
1548         pktsize = (lp->rxpb[0+fc_size] << 8) | lp->rxpb[1+fc_size];
1549         if (pktsize == (lp->rxpboffset - 2)) {
1550             ++lp->rxpcnt;
1551             *pbuf = &lp->rxpb[2+fc_size];
1552             *psize = pktsize;
1553             lp->rxpboffset = 0;
1554             return SCPE_OK;
1555             }
1556         }
1557     }
1558 *pbuf = NULL;
1559 *psize = 0;
1560 if (lp->conn)
1561     return SCPE_OK;
1562 return SCPE_LOST;
1563 }
1564 
1565 /* Poll for input
1566 
1567    Inputs:
1568         *mp     =       pointer to terminal multiplexer descriptor
1569    Outputs:     none
1570 */
1571 
1572 void tmxr_poll_rx (TMXR *mp)
     /* [previous][next][first][last][top][bottom][index][help] */
1573 {
1574 int32 i, nbytes, j;
1575 TMLN *lp;
1576 
1577 for (i = 0; i < mp->lines; i++) {                       /* loop thru lines */
1578     lp = mp->ldsc + i;                                  /* get line desc */
1579     if (!(lp->sock
1580         || lp->loopback) ||
1581         !(lp->rcve))                                    /* skip if not connected */
1582         continue;
1583 
1584     nbytes = 0;
1585     if (lp->rxbpi == 0)                                 /* need input? */
1586         nbytes = tmxr_read (lp,                         /* yes, read */
1587             lp->rxbsz - TMXR_GUARD);                    /* leave spc for Telnet cruft */
1588     else if (lp->tsta)                                  /* in Telnet seq? */
1589         nbytes = tmxr_read (lp,                         /* yes, read to end */
1590             lp->rxbsz - lp->rxbpi);
1591 
1592     if (nbytes < 0) {                                   /* line error? */
1593         if (!lp->datagram) {                            /* ignore errors reading UDP sockets */
1594             if (!lp->txbfd || lp->notelnet)
1595                 lp->txbpi = lp->txbpr = 0;              /* Drop the data we already know we can't send */
1596             tmxr_close_ln (lp);                         /* disconnect line */
1597             }
1598         }
1599 
1600     else if (nbytes > 0) {                              /* if data rcvd */
1601 
1602         j = lp->rxbpi;                                  /* start of data */
1603         lp->rxbpi = lp->rxbpi + nbytes;                 /* adv pointers */
1604         lp->rxcnt = lp->rxcnt + nbytes;
1605 
1606 /* Examine new data, remove TELNET cruft before making input available */
1607 
1608         if (!lp->notelnet) {                            /* Are we looking for telnet interpretation? */
1609             for (; j < lp->rxbpi; ) {                   /* loop thru char */
1610                 u_char tmp = (u_char)lp->rxb[j];        /* get char */
1611                 switch (lp->tsta) {                     /* case tlnt state */
1612 
1613                 case TNS_NORM:                          /* normal */
1614                     if (tmp == TN_IAC) {                /* IAC? */
1615                         lp->tsta = TNS_IAC;             /* change state */
1616                         tmxr_rmvrc (lp, j);             /* remove char */
1617                         break;
1618                         }
1619                     if ((tmp == TN_CR) && lp->dstb)     /* CR, no bin */
1620                         lp->tsta = TNS_CRPAD;           /* skip pad char */
1621                     j = j + 1;                          /* advance j */
1622                     break;
1623 
1624                 case TNS_IAC:                           /* IAC prev */
1625                     if (tmp == TN_IAC) {                /* IAC + IAC */
1626                         lp->tsta = TNS_NORM;            /* treat as normal */
1627                         j = j + 1;                      /* advance j */
1628                         break;                          /* keep IAC */
1629                         }
1630                     if (tmp == TN_BRK) {                /* IAC + BRK? */
1631                         lp->tsta = TNS_NORM;            /* treat as normal */
1632                         lp->rxb[j] = 0;                 /* char is null */
1633                         lp->rbr[j] = 1;                 /* flag break */
1634                         j = j + 1;                      /* advance j */
1635                         break;
1636                         }
1637                     switch (tmp) {
1638                     case TN_WILL:                       /* IAC + WILL? */
1639                         lp->tsta = TNS_WILL;
1640                         break;
1641                     case TN_WONT:                       /* IAC + WONT? */
1642                         lp->tsta = TNS_WONT;
1643                         break;
1644                     case TN_DO:                         /* IAC + DO? */
1645                         lp->tsta = TNS_DO;
1646                         break;
1647                     case TN_DONT:                       /* IAC + DONT? */
1648                         lp->tsta = TNS_SKIP;            /* IAC + other */
1649                         break;
1650                     case TN_GA: case TN_EL:             /* IAC + other 2 byte types */
1651                     case TN_EC: case TN_AYT:
1652                     case TN_AO: case TN_IP:
1653                     case TN_NOP:
1654                         lp->tsta = TNS_NORM; //-V1037   /* ignore */
1655                         break;
1656                     case TN_SB:                         /* IAC + SB sub-opt negotiation */
1657                     case TN_DATAMK:                     /* IAC + data mark */
1658                     case TN_SE:                         /* IAC + SE sub-opt end */
1659                         lp->tsta = TNS_NORM; //-V1037   /* ignore */
1660                         break;
1661                         }
1662                     tmxr_rmvrc (lp, j);                 /* remove char */
1663                     break;
1664 
1665                 case TNS_WILL:                          /* IAC+WILL prev */
1666                     if ((tmp == TN_STATUS) ||
1667                         (tmp == TN_TIMING) ||
1668                         (tmp == TN_NAOCRD) ||
1669                         (tmp == TN_NAOHTS) ||
1670                         (tmp == TN_NAOHTD) ||
1671                         (tmp == TN_NAOFFD) ||
1672                         (tmp == TN_NAOVTS) ||
1673                         (tmp == TN_NAOVTD) ||
1674                         (tmp == TN_NAOLFD) ||
1675                         (tmp == TN_EXTEND) ||
1676                         (tmp == TN_LOGOUT) ||
1677                         (tmp == TN_BM)     ||
1678                         (tmp == TN_DET)    ||
1679                         (tmp == TN_SENDLO) ||
1680                         (tmp == TN_TERMTY) ||
1681                         (tmp == TN_ENDREC) ||
1682                         (tmp == TN_TUID)   ||
1683                         (tmp == TN_OUTMRK) ||
1684                         (tmp == TN_TTYLOC) ||
1685                         (tmp == TN_3270)   ||
1686                         (tmp == TN_X3PAD)  ||
1687                         (tmp == TN_NAWS)   ||
1688                         (tmp == TN_TERMSP) ||
1689                         (tmp == TN_TOGFLO) ||
1690                         (tmp == TN_XDISPL) ||
1691                         (tmp == TN_ENVIRO) ||
1692                         (tmp == TN_AUTH)   ||
1693                         (tmp == TN_ENCRYP) ||
1694                         (tmp == TN_NEWENV) ||
1695                         (tmp == TN_TN3270) ||
1696                         (tmp == TN_CHARST) ||
1697                         (tmp == TN_COMPRT) ||
1698                         (tmp == TN_KERMIT)) {
1699                         /* Reject (DONT) these 'uninteresting' options only one time to avoid loops */
1700                         if (0 == (lp->telnet_sent_opts[tmp] & TNOS_DONT)) {
1701                             lp->notelnet = TRUE;                /* Temporarily disable so */
1702                             tmxr_putc_ln (lp, TN_IAC);          /* IAC gets injected bare */
1703                             lp->notelnet = FALSE;
1704                             tmxr_putc_ln (lp, TN_DONT);
1705                             tmxr_putc_ln (lp, tmp);
1706                             lp->telnet_sent_opts[tmp] |= TNOS_DONT;/* Record DONT sent */
1707                             }
1708                         }
1709                 /*FALLTHRU*/ /* fall through */ /* fallthrough */
1710                 case TNS_WONT:           /* IAC+WILL/WONT prev */
1711                     if (tmp == TN_BIN) {                /* BIN? */
1712                         if (lp->tsta == TNS_WILL) {
1713                             lp->dstb = 0;
1714                             }
1715                         else {
1716                             lp->dstb = 1;
1717                             }
1718                         }
1719                     tmxr_rmvrc (lp, j);                 /* remove it */
1720                     lp->tsta = TNS_NORM;                /* next normal */
1721                     break;
1722 
1723                 /* Negotiation with the HP terminal emulator "QCTerm" is not working.
1724                    QCTerm says "WONT BIN" but sends bare CRs.  RFC 854 says:
1725 
1726                      Note that "CR LF" or "CR NUL" is required in both directions
1727                      (in the default ASCII mode), to preserve the symmetry of the
1728                      NVT model.  ...The protocol requires that a NUL be inserted
1729                      following a CR not followed by a LF in the data stream.
1730 
1731                    Until full negotiation is implemented, we work around the problem
1732                    by checking the character following the CR in non-BIN mode and
1733                    strip it only if it is LF or NUL.  This should not affect
1734                    conforming clients.
1735                 */
1736 
1737                 case TNS_CRPAD:                         /* only LF or NUL should follow CR */
1738                     lp->tsta = TNS_NORM;                /* next normal */
1739                     if ((tmp == TN_LF) ||               /* CR + LF ? */
1740                         (tmp == TN_NUL))                /* CR + NUL? */
1741                         tmxr_rmvrc (lp, j);             /* remove it */
1742                     break;
1743 
1744                 case TNS_DO:                            /* pending DO request */
1745                     if ((tmp == TN_STATUS) ||
1746                         (tmp == TN_TIMING) ||
1747                         (tmp == TN_NAOCRD) ||
1748                         (tmp == TN_NAOHTS) ||
1749                         (tmp == TN_NAOHTD) ||
1750                         (tmp == TN_NAOFFD) ||
1751                         (tmp == TN_NAOVTS) ||
1752                         (tmp == TN_NAOVTD) ||
1753                         (tmp == TN_NAOLFD) ||
1754                         (tmp == TN_EXTEND) ||
1755                         (tmp == TN_LOGOUT) ||
1756                         (tmp == TN_BM)     ||
1757                         (tmp == TN_DET)    ||
1758                         (tmp == TN_SENDLO) ||
1759                         (tmp == TN_TERMTY) ||
1760                         (tmp == TN_ENDREC) ||
1761                         (tmp == TN_TUID)   ||
1762                         (tmp == TN_OUTMRK) ||
1763                         (tmp == TN_TTYLOC) ||
1764                         (tmp == TN_3270)   ||
1765                         (tmp == TN_X3PAD)  ||
1766                         (tmp == TN_NAWS)   ||
1767                         (tmp == TN_TERMSP) ||
1768                         (tmp == TN_TOGFLO) ||
1769                         (tmp == TN_XDISPL) ||
1770                         (tmp == TN_ENVIRO) ||
1771                         (tmp == TN_AUTH)   ||
1772                         (tmp == TN_ENCRYP) ||
1773                         (tmp == TN_NEWENV) ||
1774                         (tmp == TN_TN3270) ||
1775                         (tmp == TN_CHARST) ||
1776                         (tmp == TN_COMPRT) ||
1777                         (tmp == TN_KERMIT)) {
1778                         /* Reject (WONT) these 'uninteresting' options only one time to avoid loops */
1779                         if (0 == (lp->telnet_sent_opts[tmp] & TNOS_WONT)) {
1780                             lp->notelnet = TRUE;                /* Temporarily disable so */
1781                             tmxr_putc_ln (lp, TN_IAC);          /* IAC gets injected bare */
1782                             lp->notelnet = FALSE;
1783                             tmxr_putc_ln (lp, TN_WONT);
1784                             tmxr_putc_ln (lp, tmp);
1785                             lp->telnet_sent_opts[tmp] |= TNOS_WONT;/* Record WONT sent */
1786                             }
1787                         }
1788                 /*FALLTHRU*/ /* fall through */ /* fallthrough */
1789                 case TNS_SKIP: default:                 /* skip char */
1790                     tmxr_rmvrc (lp, j);                 /* remove char */
1791                     lp->tsta = TNS_NORM;                /* next normal */
1792                     break;
1793                     }                                   /* end case state */
1794                 }                                       /* end for char */
1795             }
1796         }                                               /* end else nbytes */
1797     }                                                   /* end for lines */
1798 for (i = 0; i < mp->lines; i++) {                       /* loop thru lines */
1799     lp = mp->ldsc + i;                                  /* get line desc */
1800     if (lp->rxbpi == lp->rxbpr)                         /* if buf empty, */
1801         lp->rxbpi = lp->rxbpr = 0;                      /* reset pointers */
1802     }                                                   /* end for */
1803 return;
1804 }
1805 
1806 /* Return count of available characters for line */
1807 
1808 int32 tmxr_rqln_bare (const TMLN *lp, t_bool speed)
     /* [previous][next][first][last][top][bottom][index][help] */
1809 {
1810 if ((speed) && (lp->rxbps)) {                   /* consider speed and rate limiting? */
1811     if (sim_gtime () < lp->rxnexttime)          /* too soon? */
1812         return 0;
1813     else
1814         return (lp->rxbpi - lp->rxbpr + ((lp->rxbpi < lp->rxbpr)? lp->rxbsz : 0)) ? 1 : 0;
1815     }
1816 return (lp->rxbpi - lp->rxbpr + ((lp->rxbpi < lp->rxbpr)? lp->rxbsz: 0));
1817 }
1818 
1819 int32 tmxr_rqln (const TMLN *lp)
     /* [previous][next][first][last][top][bottom][index][help] */
1820 {
1821 return tmxr_rqln_bare (lp, TRUE);
1822 }
1823 
1824 /* Store character in line buffer
1825 
1826    Inputs:
1827         *lp     =       pointer to line descriptor
1828         chr     =       character
1829    Outputs:
1830         status  =       ok, connection lost, or stall
1831 
1832    Implementation note:
1833 
1834     1. If the line is not connected, SCPE_LOST is returned.
1835 */
1836 
1837 t_stat tmxr_putc_ln (TMLN *lp, int32 chr)
     /* [previous][next][first][last][top][bottom][index][help] */
1838 {
1839 if ((lp->conn == FALSE) &&                              /* no conn & not buffered telnet? */
1840     (!lp->txbfd || lp->notelnet)) {
1841     ++lp->txdrp;                                        /* lost */
1842     return SCPE_LOST;
1843     }
1844 
1845 #define TXBUF_AVAIL(lp) (lp->txbsz - tmxr_tqln (lp))
1846 
1847 #define TXBUF_CHAR(lp, c) {                               \
1848     lp->txb[lp->txbpi++] = (char)(c);                     \
1849     lp->txbpi %= lp->txbsz;                               \
1850     if (lp->txbpi == lp->txbpr)                           \
1851         lp->txbpr = (1+lp->txbpr)%lp->txbsz, ++lp->txdrp; \
1852     }
1853 
1854 if ((lp->txbfd && !lp->notelnet) || (TXBUF_AVAIL(lp) > 1)) {/* room for char (+ IAC)? */
1855     if ((TN_IAC == (u_char) chr) && (!lp->notelnet))    /* char == IAC in telnet session? */
1856         TXBUF_CHAR (lp, TN_IAC);                        /* stuff extra IAC char */
1857     TXBUF_CHAR (lp, chr);                               /* buffer char & adv pointer */
1858     if ((!lp->txbfd) && ((unsigned long int)TXBUF_AVAIL (lp) <= TMXR_GUARD)) /* near full? */
1859         lp->xmte = 0;                                   /* disable line */
1860     if (lp->txlog)                                      /* log if available */
1861         fputc (chr, lp->txlog);
1862     sim_exp_check (&lp->expect, chr);                   /* process expect rules as needed */
1863     return SCPE_OK;                                     /* char sent */
1864     }
1865 ++lp->txdrp; lp->xmte = 0;                              /* no room, dsbl line */
1866 return SCPE_STALL;                                      /* char not sent */
1867 }
1868 
1869 /* Store packet in line buffer
1870 
1871    Inputs:
1872         *lp     =       pointer to line descriptor
1873         *buf    =       pointer to packet data
1874         size    =       size of packet
1875         frame_char =    inter-packet framing character (0 means no frame character)
1876 
1877    Outputs:
1878         status  =       ok, connection lost, or stall
1879 
1880    Implementation notes:
1881 
1882     1. If the line is not connected, SCPE_LOST is returned.
1883     2. If prior packet transmission still in progress, SCPE_STALL is
1884        returned and no packet data is stored.  The caller must retry later.
1885 */
1886 t_stat tmxr_put_packet_ln (TMLN *lp, const uint8 *buf, size_t size)
     /* [previous][next][first][last][top][bottom][index][help] */
1887 {
1888 return tmxr_put_packet_ln_ex (lp, buf, size, 0);
1889 }
1890 
1891 t_stat tmxr_put_packet_ln_ex (TMLN *lp, const uint8 *buf, size_t size, uint8 frame_byte)
     /* [previous][next][first][last][top][bottom][index][help] */
1892 {
1893 t_stat r;
1894 size_t fc_size = (frame_byte ? 1 : 0);
1895 size_t pktlen_size = (lp->datagram ? 0 : 2);
1896 
1897 if ((!lp->conn) && (!lp->loopback))
1898     return SCPE_LOST;
1899 if (lp->txppoffset < lp->txppsize) {
1900     return SCPE_STALL;
1901     }
1902 if (lp->txpbsize < size + pktlen_size + fc_size) {
1903     lp->txpbsize = size + pktlen_size + fc_size;
1904     lp->txpb = (uint8 *)realloc (lp->txpb, lp->txpbsize);
1905     if (!lp->txpb)
1906       {
1907         fprintf (stderr, "\rFATAL: Out of memory! Aborting at %s[%s:%d]\r\n",
1908                  __func__, __FILE__, __LINE__);
1909 #if defined(USE_BACKTRACE)
1910 # if defined(SIGUSR2)
1911         (void)raise(SIGUSR2);
1912         /*NOTREACHED*/ /* unreachable */
1913 # endif /* if defined(SIGUSR2) */
1914 #endif /* if defined(USE_BACKTRACE) */
1915         abort();
1916       }
1917     }
1918 lp->txpb[0] = frame_byte;
1919 if (!lp->datagram) {
1920     lp->txpb[0+fc_size] = (size >> 8) & 0xFF;
1921     lp->txpb[1+fc_size] = size & 0xFF;
1922     }
1923 memcpy (lp->txpb + pktlen_size + fc_size, buf, size);
1924 lp->txppsize = size + pktlen_size + fc_size;
1925 lp->txppoffset = 0;
1926 ++lp->txpcnt;
1927 while ((lp->txppoffset < lp->txppsize) &&
1928        (SCPE_OK == (r = tmxr_putc_ln (lp, lp->txpb[lp->txppoffset]))))
1929    ++lp->txppoffset;
1930 (void)r;
1931 tmxr_send_buffered_data (lp);
1932 return (lp->conn || lp->loopback) ? SCPE_OK : SCPE_LOST;
1933 }
1934 
1935 /* Poll for output
1936 
1937    Inputs:
1938         *mp     =       pointer to terminal multiplexer descriptor
1939    Outputs:
1940         none
1941 */
1942 
1943 void tmxr_poll_tx (TMXR *mp)
     /* [previous][next][first][last][top][bottom][index][help] */
1944 {
1945 int32 i, nbytes;
1946 TMLN *lp;
1947 
1948 for (i = 0; i < mp->lines; i++) {                       /* loop thru lines */
1949     lp = mp->ldsc + i;                                  /* get line desc */
1950     if (!lp->conn)                                      /* skip if !conn */
1951         continue;
1952     nbytes = tmxr_send_buffered_data (lp);              /* buffered bytes */
1953     if (nbytes == 0) {                                  /* buf empty? enab line */
1954         lp->xmte = 1;                                   /* enable line transmit */
1955         }
1956     }                                                   /* end for */
1957 return;
1958 }
1959 
1960 /* Send buffered data across network
1961 
1962    Inputs:
1963         *lp     =       pointer to line descriptor
1964    Outputs:
1965         returns number of bytes still buffered
1966 */
1967 
1968 int32 tmxr_send_buffered_data (TMLN *lp)
     /* [previous][next][first][last][top][bottom][index][help] */
1969 {
1970 int32 nbytes, sbytes;
1971 t_stat r;
1972 
1973 nbytes = tmxr_tqln(lp);                                 /* avail bytes */
1974 if (nbytes) {                                           /* >0? write */
1975     if (lp->txbpr < lp->txbpi)                          /* no wrap? */
1976         sbytes = tmxr_write (lp, nbytes);               /* write all data */
1977     else
1978         sbytes = tmxr_write (lp, lp->txbsz - lp->txbpr);/* write to end buf */
1979     if (sbytes >= 0) {                                  /* ok? */
1980         lp->txbpr = (lp->txbpr + sbytes);               /* update remove ptr */
1981         if (lp->txbpr >= lp->txbsz)                     /* wrap? */
1982             lp->txbpr = 0;
1983         lp->txcnt = lp->txcnt + sbytes;                 /* update counts */
1984         nbytes = nbytes - sbytes;
1985         if ((nbytes == 0) && (lp->datagram))            /* if Empty buffer on datagram line */
1986             lp->txbpi = lp->txbpr = 0;                  /* Start next packet at beginning of buffer */
1987         }
1988     if (sbytes < 0) {                                   /* I/O Error? */
1989         lp->txbpi = lp->txbpr = 0;                      /* Drop the data we already know we can't send */
1990         lp->rxpboffset = lp->txppoffset = lp->txppsize = 0;/* Drop the data we already know we can't send */
1991         tmxr_close_ln (lp);                             /*  close line/port on error */
1992         return nbytes;                                  /*  done now. */
1993         }
1994     if (nbytes && (lp->txbpr == 0))     {               /* more data and wrap? */
1995         sbytes = tmxr_write (lp, nbytes);
1996         if (sbytes > 0) {                               /* ok */
1997             lp->txbpr = (lp->txbpr + sbytes);           /* update remove ptr */
1998             if (lp->txbpr >= lp->txbsz)                 /* wrap? */
1999                 lp->txbpr = 0;
2000             lp->txcnt = lp->txcnt + sbytes;             /* update counts */
2001             nbytes = nbytes - sbytes;
2002             }
2003         }
2004     }                                                   /* end if nbytes */
2005 while ((lp->txppoffset < lp->txppsize) &&               /* buffered packet data? */
2006        (lp->txbsz > nbytes) &&                          /* and room in xmt buffer */
2007        (SCPE_OK == (r = tmxr_putc_ln (lp, lp->txpb[lp->txppoffset]))))
2008    ++lp->txppoffset;
2009 (void)r;
2010 if ((nbytes == 0) && (tmxr_tqln(lp) > 0))
2011     return tmxr_send_buffered_data (lp);
2012 return tmxr_tqln(lp) + tmxr_tpqln(lp);
2013 }
2014 
2015 /* Return count of buffered characters for line */
2016 
2017 int32 tmxr_tqln (const TMLN *lp)
     /* [previous][next][first][last][top][bottom][index][help] */
2018 {
2019 return (lp->txbpi - lp->txbpr + ((lp->txbpi < lp->txbpr)? lp->txbsz: 0));
2020 }
2021 
2022 /* Return count of buffered packet characters for line */
2023 
2024 int32 tmxr_tpqln (const TMLN *lp)
     /* [previous][next][first][last][top][bottom][index][help] */
2025 {
2026 return (lp->txppsize - lp->txppoffset);
2027 }
2028 
2029 /* Return transmit packet busy status for line */
2030 
2031 t_bool tmxr_tpbusyln (const TMLN *lp)
     /* [previous][next][first][last][top][bottom][index][help] */
2032 {
2033 return (0 != (lp->txppsize - lp->txppoffset));
2034 }
2035 
2036 static void _mux_detach_line (TMLN *lp, t_bool close_listener, t_bool close_connecting)
     /* [previous][next][first][last][top][bottom][index][help] */
2037 {
2038 if (close_listener && lp->master) {
2039     sim_close_sock (lp->master);
2040     lp->master = 0;
2041     FREE (lp->port);
2042     lp->port = NULL;
2043     }
2044 if (lp->sock) {                             /* if existing tcp, drop it */
2045     tmxr_report_disconnection (lp);         /* report disconnection */
2046     tmxr_reset_ln (lp);
2047     }
2048 if (close_connecting) {
2049     FREE (lp->destination);
2050     lp->destination = NULL;
2051     if (lp->connecting) {                   /* if existing outgoing tcp, drop it */
2052         lp->sock = lp->connecting;
2053         lp->connecting = 0;
2054         tmxr_reset_ln (lp);
2055         }
2056     }
2057 tmxr_set_line_loopback (lp, FALSE);
2058 }
2059 
2060 t_stat tmxr_detach_ln (TMLN *lp)
     /* [previous][next][first][last][top][bottom][index][help] */
2061 {
2062 UNIT *uptr = NULL;
2063 
2064 _mux_detach_line (lp, TRUE, TRUE);
2065 if (lp->mp) {
2066     if (lp->uptr)
2067         uptr = lp->uptr;
2068     else
2069         uptr = lp->mp->uptr;
2070     }
2071 if (uptr && uptr->filename) {
2072     /* Revise the unit's connect string to reflect the current attachments */
2073     uptr->filename = tmxr_mux_attach_string (uptr->filename, lp->mp);
2074     /* No connections or listeners exist, then we're equivalent to being fully detached.  We should reflect that */
2075     if (uptr->filename == NULL)
2076         tmxr_detach (lp->mp, uptr);
2077     }
2078 return SCPE_OK;
2079 }
2080 
2081 static int32 _tmln_speed_delta (CONST char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
2082 {
2083 struct {
2084     const char *bps;
2085     int32 delta;
2086     } *spd,     speeds[] = {
2087     { "50",     TMLN_SPD_50_BPS     },
2088     { "75",     TMLN_SPD_75_BPS     },
2089     { "110",    TMLN_SPD_110_BPS    },
2090     { "134",    TMLN_SPD_134_BPS    },
2091     { "150",    TMLN_SPD_150_BPS    },
2092     { "300",    TMLN_SPD_300_BPS    },
2093     { "600",    TMLN_SPD_600_BPS    },
2094     { "1200",   TMLN_SPD_1200_BPS   },
2095     { "1800",   TMLN_SPD_1800_BPS   },
2096     { "2000",   TMLN_SPD_2000_BPS   },
2097     { "2400",   TMLN_SPD_2400_BPS   },
2098     { "3600",   TMLN_SPD_3600_BPS   },
2099     { "4800",   TMLN_SPD_4800_BPS   },
2100     { "7200",   TMLN_SPD_7200_BPS   },
2101     { "9600",   TMLN_SPD_9600_BPS   },
2102     { "19200",  TMLN_SPD_19200_BPS  },
2103     { "38400",  TMLN_SPD_38400_BPS  },
2104     { "57600",  TMLN_SPD_57600_BPS  },
2105     { "76800",  TMLN_SPD_76800_BPS  },
2106     { "115200", TMLN_SPD_115200_BPS },
2107     { "0",      0 } };                /* End of List, last valid value */
2108 int nspeed;
2109 char speed[24];
2110 
2111 nspeed = (uint32)strtotv (cptr, &cptr, 10);
2112 if ((*cptr != '\0') && (*cptr != '-') && (*cptr != '*'))
2113     return -1;
2114 (void)sprintf (speed, "%d", nspeed);
2115 
2116 spd = speeds;
2117 while (1) {
2118     if (0 == strcmp(spd->bps, speed))
2119         return spd->delta;
2120     if (spd->delta == 0)
2121         break;
2122     ++spd;
2123     }
2124 return -1;
2125 }
2126 
2127 t_stat tmxr_set_line_speed (TMLN *lp, CONST char *speed)
     /* [previous][next][first][last][top][bottom][index][help] */
2128 {
2129 UNIT *uptr;
2130 CONST char *cptr;
2131 t_stat r;
2132 
2133 if (!speed || !*speed)
2134     return SCPE_2FARG;
2135 if (_tmln_speed_delta (speed) < 0)
2136     return SCPE_ARG;
2137 if (lp == NULL)
2138     return SCPE_ARG;
2139 lp->rxbps = (uint32)strtotv (speed, &cptr, 10);
2140 if (*cptr == '*') {
2141     uint32 rxbpsfactor = (uint32) get_uint (cptr+1, 10, 32, &r);
2142     if (r != SCPE_OK)
2143         return r;
2144     lp->rxbpsfactor = TMXR_RX_BPS_UNIT_SCALE * rxbpsfactor;
2145     }
2146 lp->rxdelta = _tmln_speed_delta (speed);
2147 lp->rxnexttime = 0.0;
2148 uptr = lp->uptr;
2149 if ((!uptr) && (lp->mp))
2150     uptr = lp->mp->uptr;
2151 if (uptr)
2152     uptr->wait = lp->rxdelta;
2153 if (lp->rxbpsfactor == 0.0)
2154     lp->rxbpsfactor = TMXR_RX_BPS_UNIT_SCALE;
2155 lp->txbps = lp->rxbps;
2156 lp->txdelta = lp->rxdelta;
2157 lp->txnexttime = lp->rxnexttime;
2158 return SCPE_OK;
2159 }
2160 
2161 /* Open a master listening socket (and all of the other variances of connections).
2162 
2163    A listening socket for the port number described by "cptr" is opened for the
2164    multiplexer associated with descriptor "mp".  If the open is successful, all
2165    lines not currently otherwise connected.
2166 */
2167 
2168 t_stat tmxr_open_master (TMXR *mp, CONST char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
2169 {
2170 int32 i, line, nextline = -1;
2171 char tbuf[CBUFSIZE], listen[CBUFSIZE], destination[CBUFSIZE],
2172      logfiletmpl[CBUFSIZE], buffered[CBUFSIZE], hostport[CBUFSIZE*2],
2173      port[CBUFSIZE], option[CBUFSIZE], speed[CBUFSIZE];
2174 SOCKET sock;
2175 CONST char *tptr = cptr;
2176 t_bool nolog, notelnet, listennotelnet, modem_control, loopback, datagram, packet;
2177 TMLN *lp = NULL;
2178 t_stat r = SCPE_OK;
2179 
2180 if (*tptr == '\0')
2181     return SCPE_ARG;
2182 for (i = 0; i < mp->lines; i++) {               /* initialize lines */
2183     lp = mp->ldsc + i;
2184     lp->mp = mp;                                /* set the back pointer */
2185     lp->modem_control = mp->modem_control;
2186     if (lp->rxbpsfactor == 0.0)
2187         lp->rxbpsfactor = TMXR_RX_BPS_UNIT_SCALE;
2188     }
2189 mp->ring_sock = INVALID_SOCKET;
2190 FREE (mp->ring_ipad);
2191 mp->ring_ipad = NULL;
2192 mp->ring_start_time = 0;
2193 while (*tptr) {
2194     line = nextline;
2195     (void)memset(logfiletmpl, '\0', sizeof(logfiletmpl));
2196     (void)memset(listen,      '\0', sizeof(listen));
2197     (void)memset(destination, '\0', sizeof(destination));
2198     (void)memset(buffered,    '\0', sizeof(buffered));
2199     (void)memset(port,        '\0', sizeof(port));
2200     (void)memset(option,      '\0', sizeof(option));
2201     (void)memset(speed,       '\0', sizeof(speed));
2202     nolog = notelnet = listennotelnet = loopback = FALSE;
2203     datagram = mp->datagram;
2204     packet = mp->packet;
2205     if (mp->buffered)
2206         (void)sprintf(buffered, "%d", mp->buffered);
2207     if (line != -1)
2208         notelnet = listennotelnet = mp->notelnet;
2209     modem_control = mp->modem_control;
2210     while (*tptr) {
2211         tptr = get_glyph_nc (tptr, tbuf, ',');
2212         if (!tbuf[0])
2213             break;
2214         cptr = tbuf;
2215         if (!isdigit((unsigned char)*cptr)) {
2216             char gbuf[CBUFSIZE];
2217             CONST char *init_cptr = cptr;
2218 
2219             cptr = get_glyph (cptr, gbuf, '=');
2220             if (0 == MATCH_CMD (gbuf, "LINE")) {
2221                 if ((NULL == cptr) || ('\0' == *cptr))
2222                     return sim_messagef (SCPE_2FARG, "Missing Line Specifier\n");
2223                 nextline = (int32) get_uint (cptr, 10, mp->lines-1, &r);
2224                 if (r)
2225                     return sim_messagef (SCPE_ARG, "Invalid Line Specifier: %s\n", cptr);
2226                 break;
2227                 }
2228             if (0 == MATCH_CMD (gbuf, "LOG")) {
2229                 if ((NULL == cptr) || ('\0' == *cptr))
2230                     return sim_messagef (SCPE_2FARG, "Missing Log Specifier\n");
2231                 strncpy(logfiletmpl, cptr, sizeof(logfiletmpl)-1);
2232                 continue;
2233                 }
2234              if (0 == MATCH_CMD (gbuf, "LOOPBACK")) {
2235                 if ((NULL != cptr) && ('\0' != *cptr))
2236                     return sim_messagef (SCPE_2MARG, "Unexpected Loopback Specifier: %s\n", cptr);
2237                 loopback = TRUE;
2238                 continue;
2239                 }
2240            if ((0 == MATCH_CMD (gbuf, "NOBUFFERED")) || (0 == MATCH_CMD (gbuf, "UNBUFFERED"))) { //-V600
2241                 if ((NULL != cptr) && ('\0' != *cptr))
2242                     return sim_messagef (SCPE_2MARG, "Unexpected Unbuffered Specifier: %s\n", cptr);
2243                 buffered[0] = '\0';
2244                 continue;
2245                 }
2246             if (0 == MATCH_CMD (gbuf, "BUFFERED")) {
2247                 if ((NULL == cptr) || ('\0' == *cptr))
2248                     (void)strcpy (buffered, "32768");
2249                 else {
2250                     i = (int32) get_uint (cptr, 10, 1024*1024, &r);
2251                     if (r || (i == 0))
2252                         return sim_messagef (SCPE_ARG, "Invalid Buffered Specifier: %s\n", cptr);
2253                     (void)sprintf(buffered, "%d", i);
2254                     }
2255                 continue;
2256                 }
2257             if (0 == MATCH_CMD (gbuf, "NOLOG")) {
2258                 if ((NULL != cptr) && ('\0' != *cptr))
2259                     return sim_messagef (SCPE_2MARG, "Unexpected NoLog Specifier: %s\n", cptr);
2260                 nolog = TRUE;
2261                 continue;
2262                 }
2263             if (0 == MATCH_CMD (gbuf, "NOMODEM")) {
2264                 if ((NULL != cptr) && ('\0' != *cptr))
2265                     return sim_messagef (SCPE_2MARG, "Unexpected NoModem Specifier: %s\n", cptr);
2266                 modem_control = FALSE;
2267                 continue;
2268                 }
2269             if (0 == MATCH_CMD (gbuf, "MODEM")) {
2270                 if ((NULL != cptr) && ('\0' != *cptr))
2271                     return sim_messagef (SCPE_2MARG, "Unexpected Modem Specifier: %s\n", cptr);
2272                 modem_control = TRUE;
2273                 continue;
2274                 }
2275             if ((0 == MATCH_CMD (gbuf, "DATAGRAM")) || (0 == MATCH_CMD (gbuf, "UDP"))) { //-V600
2276                 if ((NULL != cptr) && ('\0' != *cptr))
2277                     return sim_messagef (SCPE_2MARG, "Unexpected Datagram Specifier: %s\n", cptr);
2278                 notelnet = datagram = TRUE;
2279                 continue;
2280                 }
2281             if (0 == MATCH_CMD (gbuf, "PACKET")) {
2282                 if ((NULL != cptr) && ('\0' != *cptr))
2283                     return sim_messagef (SCPE_2MARG, "Unexpected Packet Specifier: %s\n", cptr);
2284                 packet = TRUE;
2285                 continue;
2286                 }
2287             if ((0 == MATCH_CMD (gbuf, "STREAM")) || (0 == MATCH_CMD (gbuf, "TCP"))) { //-V600
2288                 if ((NULL != cptr) && ('\0' != *cptr))
2289                     return sim_messagef (SCPE_2MARG, "Unexpected Stream Specifier: %s\n", cptr);
2290                 datagram = FALSE;
2291                 continue;
2292                 }
2293             if (0 == MATCH_CMD (gbuf, "CONNECT")) {
2294                 if ((NULL == cptr) || ('\0' == *cptr))
2295                     return sim_messagef (SCPE_2FARG, "Missing Connect Specifier\n");
2296                 (void)strcpy (destination, cptr);
2297                 continue;
2298                 }
2299             if (0 == MATCH_CMD (gbuf, "SPEED")) {
2300                 if ((NULL == cptr) || ('\0' == *cptr) ||
2301                     (_tmln_speed_delta (cptr) < 0))
2302                     return sim_messagef (SCPE_ARG, "Invalid Speed Specifier: %s\n", (cptr ? cptr : ""));
2303                 (void)strcpy (speed, cptr);
2304                 continue;
2305                 }
2306             cptr = get_glyph (gbuf, port, ';');
2307             if (sim_parse_addr (port, NULL, 0, NULL, NULL, 0, NULL, NULL))
2308                 return sim_messagef (SCPE_ARG, "Invalid Port Specifier: %s\n", port);
2309             if (cptr) {
2310                 char *tptr = gbuf + (cptr - gbuf);
2311                 (void)get_glyph (cptr, tptr, 0);             /* upcase this string */
2312                 if (0 == MATCH_CMD (cptr, "NOTELNET"))
2313                     listennotelnet = TRUE;
2314                 else
2315                     if (0 == MATCH_CMD (cptr, "TELNET"))
2316                         listennotelnet = FALSE;
2317                     else
2318                         return sim_messagef (SCPE_ARG, "Invalid Specifier: %s\n", tptr);
2319                 }
2320             cptr = init_cptr;
2321             }
2322         cptr = get_glyph_nc (cptr, port, ';');
2323         sock = sim_master_sock (port, &r);                      /* make master socket to validate port */
2324         if (r)
2325             return sim_messagef (SCPE_ARG, "Invalid Port Specifier: %s\n", port);
2326         if (sock == INVALID_SOCKET)                             /* open error */
2327             return sim_messagef (SCPE_OPENERR, "Can't open network port: %s\n", port);
2328         sim_close_sock (sock);
2329         sim_os_ms_sleep (2);                                    /* let the close finish (required on some platforms) */
2330         (void)strcpy (listen, port);
2331         cptr = get_glyph (cptr, option, ';');
2332         (void)cptr;
2333         if (option[0]) {
2334             if (0 == MATCH_CMD (option, "NOTELNET"))
2335                 listennotelnet = TRUE;
2336             else
2337                 if (0 == MATCH_CMD (option, "TELNET"))
2338                     listennotelnet = FALSE;
2339             else {
2340                 if (*tptr)
2341                     return sim_messagef (SCPE_ARG, "Invalid Specifier: %s\n", tptr);
2342                 }
2343             }
2344         }
2345     if (destination[0]) {
2346         /* Validate destination */
2347             char *eptr;
2348 
2349             (void)memset (hostport, '\0', sizeof(hostport));
2350             strncpy (hostport, destination, sizeof(hostport));
2351             if ((eptr = strchr (hostport, ';')))
2352                 *(eptr++) = '\0';
2353             if (eptr) {
2354                 (void)get_glyph (eptr, eptr, 0);     /* upcase this string */
2355                 if (0 == MATCH_CMD (eptr, "NOTELNET"))
2356                     notelnet = TRUE;
2357                 else
2358                     if (0 == MATCH_CMD (eptr, "TELNET"))
2359                         if (datagram)
2360                             return sim_messagef (SCPE_ARG, "Telnet invalid on Datagram socket\n");
2361                         else
2362                             notelnet = FALSE;
2363                     else
2364                         return sim_messagef (SCPE_ARG, "Unexpected specifier: %s\n", eptr);
2365                 }
2366             sock = sim_connect_sock_ex (NULL, hostport, "localhost", NULL, (datagram ? SIM_SOCK_OPT_DATAGRAM : 0) |
2367                                                                            (packet ? SIM_SOCK_OPT_NODELAY : 0)    |
2368                                                                            SIM_SOCK_OPT_BLOCKING);
2369             if (sock != INVALID_SOCKET)
2370                 sim_close_sock (sock);
2371             else
2372                 return sim_messagef (SCPE_ARG, "Invalid destination: %s\n", hostport);
2373         }
2374     if (line == -1) {
2375         if (modem_control != mp->modem_control)
2376             return SCPE_ARG;
2377         if (logfiletmpl[0]) {
2378 #if defined(__GNUC__)
2379 # if !defined(__clang_version__)
2380 #  if !defined(__INTEL_COMPILER)
2381 #   if __GNUC__ > 7
2382 #    pragma GCC diagnostic push
2383 #    pragma GCC diagnostic ignored "-Wstringop-truncation"
2384 #   endif /* if __GNUC__ > 7 */
2385 #  endif /* if !defined(__INTEL_COMPILER) */
2386 # endif /* if !defined(__clang_version__) */
2387 #endif /* if defined(__GNUC__) */
2388             strncpy(mp->logfiletmpl, logfiletmpl, sizeof(mp->logfiletmpl)-1);
2389 #if defined(__GNUC__)
2390 # if !defined(__clang_version__)
2391 #  if !defined(__INTEL_COMPILER)
2392 #   if __GNUC__ > 7
2393 #    pragma GCC diagnostic pop
2394 #   endif /* if __GNUC__ > 7 */
2395 #  endif /* if !defined(__INTEL_COMPILER) */
2396 # endif /* if !defined(__clang_version__) */
2397 #endif /* if defined(__GNUC__) */
2398             for (i = 0; i < mp->lines; i++) {
2399                 lp = mp->ldsc + i;
2400                 sim_close_logfile (&lp->txlogref);
2401                 lp->txlog = NULL;
2402                 lp->txlogname = (char *)realloc(lp->txlogname, CBUFSIZE);
2403                 if (!lp->txlogname)
2404                   {
2405                     fprintf (stderr, "\rFATAL: Out of memory! Aborting at %s[%s:%d]\r\n",
2406                              __func__, __FILE__, __LINE__);
2407 #if defined(USE_BACKTRACE)
2408 # if defined(SIGUSR2)
2409                     (void)raise(SIGUSR2);
2410                     /*NOTREACHED*/ /* unreachable */
2411 # endif /* if defined(SIGUSR2) */
2412 #endif /* if defined(USE_BACKTRACE) */
2413                     abort();
2414                   }
2415                 if (mp->lines > 1)
2416                     (void)sprintf(lp->txlogname, "%s_%d", mp->logfiletmpl, i);
2417                 else
2418                     (void)strcpy (lp->txlogname, mp->logfiletmpl);
2419                 r = sim_open_logfile (lp->txlogname, TRUE, &lp->txlog, &lp->txlogref);
2420                 if (r == SCPE_OK)
2421                     setvbuf (lp->txlog, NULL, _IOFBF, 65536);
2422                 else {
2423                     FREE (lp->txlogname);
2424                     lp->txlogname = NULL;
2425                     break;
2426                     }
2427                 }
2428             }
2429         mp->buffered = atoi(buffered);
2430         for (i = 0; i < mp->lines; i++) { /* initialize line buffers */
2431             lp = mp->ldsc + i;
2432             if (mp->buffered) {
2433                 lp->txbsz = mp->buffered;
2434                 lp->txbfd = 1;
2435                 lp->rxbsz = mp->buffered;
2436                 }
2437             else {
2438                 lp->txbsz = TMXR_MAXBUF;
2439                 lp->txbfd = 0;
2440                 lp->rxbsz = TMXR_MAXBUF;
2441                 }
2442             lp->txbpi = lp->txbpr = 0;
2443             lp->txb = (char *)realloc(lp->txb, lp->txbsz);
2444             if (!lp->txb)
2445               {
2446                 fprintf (stderr, "\rFATAL: Out of memory! Aborting at %s[%s:%d]\r\n",
2447                          __func__, __FILE__, __LINE__);
2448 #if defined(USE_BACKTRACE)
2449 # if defined(SIGUSR2)
2450                 (void)raise(SIGUSR2);
2451                 /*NOTREACHED*/ /* unreachable */
2452 # endif /* if defined(SIGUSR2) */
2453 #endif /* if defined(USE_BACKTRACE) */
2454                 abort();
2455               }
2456             lp->rxb = (char *)realloc(lp->rxb, lp->rxbsz);
2457             if (!lp->rxb)
2458               {
2459                 fprintf (stderr, "\rFATAL: Out of memory! Aborting at %s[%s:%d]\r\n",
2460                          __func__, __FILE__, __LINE__);
2461 #if defined(USE_BACKTRACE)
2462 # if defined(SIGUSR2)
2463                 (void)raise(SIGUSR2);
2464                 /*NOTREACHED*/ /* unreachable */
2465 # endif /* if defined(SIGUSR2) */
2466 #endif /* if defined(USE_BACKTRACE) */
2467                 abort();
2468               }
2469             lp->rbr = (char *)realloc(lp->rbr, lp->rxbsz);
2470             if (!lp->rbr)
2471               {
2472                 fprintf (stderr, "\rFATAL: Out of memory! Aborting at %s[%s:%d]\r\n",
2473                          __func__, __FILE__, __LINE__);
2474 #if defined(USE_BACKTRACE)
2475 # if defined(SIGUSR2)
2476                 (void)raise(SIGUSR2);
2477                 /*NOTREACHED*/ /* unreachable */
2478 # endif /* if defined(SIGUSR2) */
2479 #endif /* if defined(USE_BACKTRACE) */
2480                 abort();
2481               }
2482             }
2483         if (nolog) {
2484             mp->logfiletmpl[0] = '\0';
2485             for (i = 0; i < mp->lines; i++) { /* close line logs */
2486                 lp = mp->ldsc + i;
2487                 FREE(lp->txlogname);
2488                 lp->txlogname = NULL;
2489                 if (lp->txlog) {
2490                     sim_close_logfile (&lp->txlogref);
2491                     lp->txlog = NULL;
2492                     }
2493                 }
2494             }
2495         if ((listen[0]) && (!datagram)) {
2496             sock = sim_master_sock (listen, &r);            /* make master socket */
2497             if (r)
2498                 return sim_messagef (SCPE_ARG, "Invalid network listen port: %s\n", listen);
2499             if (sock == INVALID_SOCKET)                     /* open error */
2500                 return sim_messagef (SCPE_OPENERR, "Can't open network socket for listen port: %s\n", listen);
2501             if (mp->port) {                                 /* close prior listener */
2502                 sim_close_sock (mp->master);
2503                 mp->master = 0;
2504                 FREE (mp->port);
2505                 mp->port = NULL;
2506                 }
2507             sim_printf ("Listening on port %s\n", listen);
2508             mp->port = (char *)realloc (mp->port, 1 + strlen (listen));
2509             if (!mp->port)
2510               {
2511                 fprintf (stderr, "\rFATAL: Out of memory! Aborting at %s[%s:%d]\r\n",
2512                          __func__, __FILE__, __LINE__);
2513 #if defined(USE_BACKTRACE)
2514 # if defined(SIGUSR2)
2515                 (void)raise(SIGUSR2);
2516                 /*NOTREACHED*/ /* unreachable */
2517 # endif /* if defined(SIGUSR2) */
2518 #endif /* if defined(USE_BACKTRACE) */
2519                 abort();
2520               }
2521             (void)strcpy (mp->port, listen);                /* save port */
2522             mp->master = sock;                              /* save master socket */
2523             mp->ring_sock = INVALID_SOCKET;
2524             if (mp->ring_ipad) FREE (mp->ring_ipad);
2525             mp->ring_ipad = NULL;
2526             mp->ring_start_time = 0;
2527             mp->notelnet = listennotelnet;                  /* save desired telnet behavior flag */
2528             for (i = 0; i < mp->lines; i++) {               /* initialize lines */
2529                 lp = mp->ldsc + i;
2530                 lp->mp = mp;                                /* set the back pointer */
2531                 lp->packet = mp->packet;
2532                     if (speed[0])
2533                         tmxr_set_line_speed (lp, speed);
2534                 tmxr_init_line (lp);                        /* initialize line state */
2535                 lp->sock = 0;                               /* clear the socket */
2536                 }
2537             }
2538         if (loopback) {
2539             if (mp->lines > 1)
2540                 return sim_messagef (SCPE_ARG, "Ambiguous Loopback specification\n");
2541             sim_printf ("Operating in loopback mode\n");
2542             for (i = 0; i < mp->lines; i++) { //-V1008
2543                 lp = mp->ldsc + i;
2544                 tmxr_set_line_loopback (lp, loopback);
2545                 if (speed[0])
2546                     tmxr_set_line_speed (lp, speed);
2547                 }
2548             }
2549         if (destination[0]) {
2550             if (mp->lines > 1)
2551                 return sim_messagef (SCPE_ARG, "Ambiguous Destination specification\n");
2552             lp = &mp->ldsc[0];
2553                 lp->datagram = datagram;
2554                 if (datagram) {
2555                     if (listen[0]) {
2556                         lp->port = (char *)realloc (lp->port, 1 + strlen (listen));
2557                         if (!lp->port)
2558                           {
2559                             fprintf(stderr, "\rFATAL: Out of memory! Aborting at %s[%s:%d]\r\n",
2560                                     __func__, __FILE__, __LINE__);
2561 #if defined(USE_BACKTRACE)
2562 # if defined(SIGUSR2)
2563                             (void)raise(SIGUSR2);
2564                             /*NOTREACHED*/ /* unreachable */
2565 # endif /* if defined(SIGUSR2) */
2566 #endif /* if defined(USE_BACKTRACE) */
2567                             abort();
2568                           }
2569                         (void)strcpy (lp->port, listen);           /* save port */
2570                         }
2571                     else
2572                         return sim_messagef (SCPE_ARG, "Missing listen port for Datagram socket\n");
2573                     }
2574                 lp->packet = packet;
2575                 sock = sim_connect_sock_ex (datagram ? listen : NULL, hostport, "localhost", NULL,
2576                                             (datagram ? SIM_SOCK_OPT_DATAGRAM : 0) |
2577                                             (packet ? SIM_SOCK_OPT_NODELAY : 0)    |
2578                                             SIM_SOCK_OPT_BLOCKING);
2579                 if (sock != INVALID_SOCKET) {
2580                     _mux_detach_line (lp, FALSE, TRUE);
2581                     lp->destination = (char *)malloc(1+strlen(hostport));
2582                     if (!lp->destination)
2583                       {
2584                         fprintf(stderr, "\rFATAL: Out of memory! Aborting at %s[%s:%d]\r\n",
2585                                 __func__, __FILE__, __LINE__);
2586 #if defined(USE_BACKTRACE)
2587 # if defined(SIGUSR2)
2588                         (void)raise(SIGUSR2);
2589                         /*NOTREACHED*/ /* unreachable */
2590 # endif /* if defined(SIGUSR2) */
2591 #endif /* if defined(USE_BACKTRACE) */
2592                         abort();
2593                       }
2594                     (void)strcpy (lp->destination, hostport);
2595                     lp->mp = mp;
2596                     if (!lp->modem_control || (lp->modembits & TMXR_MDM_DTR)) {
2597                         lp->connecting = sock;
2598                         lp->ipad = (char *)malloc (1 + strlen (lp->destination));
2599                         if (!lp->ipad)
2600                           {
2601                             fprintf(stderr, "\rFATAL: Out of memory! Aborting at %s[%s:%d]\r\n",
2602                                     __func__, __FILE__, __LINE__);
2603 #if defined(USE_BACKTRACE)
2604 # if defined(SIGUSR2)
2605                             (void)raise(SIGUSR2);
2606                             /*NOTREACHED*/ /* unreachable */
2607 # endif /* if defined(SIGUSR2) */
2608 #endif /* if defined(USE_BACKTRACE) */
2609                             abort();
2610                           }
2611                         (void)strcpy (lp->ipad, lp->destination);
2612                         }
2613                     else
2614                         sim_close_sock (sock);
2615                     lp->notelnet = notelnet;
2616                     tmxr_init_line (lp);                    /* init the line state */
2617                     if (speed[0] && (!datagram))
2618                         tmxr_set_line_speed (lp, speed);
2619                     return SCPE_OK;
2620                     }
2621                 else
2622                     return sim_messagef (SCPE_ARG, "Can't open %s socket on %s%s%s\n",
2623                                          datagram ? "Datagram" : "Stream", datagram ? listen : "",
2624                                          datagram ? "<->" : "", hostport);
2625                 }
2626         }
2627     else {                                                  /* line specific attach */
2628         lp = &mp->ldsc[line];
2629         lp->mp = mp;
2630         if (logfiletmpl[0]) {
2631             sim_close_logfile (&lp->txlogref);
2632             lp->txlog = NULL;
2633             lp->txlogname = (char *)realloc (lp->txlogname, 1 + strlen (logfiletmpl));
2634             if (!lp->txlogname)
2635               {
2636                 fprintf (stderr, "\rFATAL: Out of memory! Aborting at %s[%s:%d]\r\n",
2637                          __func__, __FILE__, __LINE__);
2638 #if defined(USE_BACKTRACE)
2639 # if defined(SIGUSR2)
2640                 (void)raise(SIGUSR2);
2641                 /*NOTREACHED*/ /* unreachable */
2642 # endif /* if defined(SIGUSR2) */
2643 #endif /* if defined(USE_BACKTRACE) */
2644                 abort();
2645               }
2646             (void)strcpy (lp->txlogname, logfiletmpl);
2647             r = sim_open_logfile (lp->txlogname, TRUE, &lp->txlog, &lp->txlogref);
2648             if (r == SCPE_OK)
2649                 setvbuf(lp->txlog, NULL, _IOFBF, 65536);
2650             else {
2651                 FREE (lp->txlogname);
2652                 lp->txlogname = NULL;
2653                 return sim_messagef (r, "Can't open log file: %s\n", logfiletmpl);
2654                 }
2655             }
2656         if (buffered[0] == '\0') {
2657             lp->rxbsz = lp->txbsz = TMXR_MAXBUF;
2658             lp->txbfd = 0;
2659             }
2660         else {
2661             lp->rxbsz = lp->txbsz = atoi(buffered);
2662             lp->txbfd = 1;
2663             }
2664         lp->txbpi = lp->txbpr = 0;
2665         lp->txb = (char *)realloc (lp->txb, lp->txbsz);
2666         if (!lp->txb)
2667           {
2668             fprintf (stderr, "\rFATAL: Out of memory! Aborting at %s[%s:%d]\r\n",
2669                      __func__, __FILE__, __LINE__);
2670 #if defined(USE_BACKTRACE)
2671 # if defined(SIGUSR2)
2672             (void)raise(SIGUSR2);
2673             /*NOTREACHED*/ /* unreachable */
2674 # endif /* if defined(SIGUSR2) */
2675 #endif /* if defined(USE_BACKTRACE) */
2676             abort();
2677           }
2678         lp->rxb = (char *)realloc (lp->rxb, lp->rxbsz);
2679         if (!lp->rxb)
2680           {
2681             fprintf (stderr, "\rFATAL: Out of memory! Aborting at %s[%s:%d]\r\n",
2682                      __func__, __FILE__, __LINE__);
2683 #if defined(USE_BACKTRACE)
2684 # if defined(SIGUSR2)
2685             (void)raise(SIGUSR2);
2686             /*NOTREACHED*/ /* unreachable */
2687 # endif /* if defined(SIGUSR2) */
2688 #endif /* if defined(USE_BACKTRACE) */
2689             abort();
2690           }
2691         lp->rbr = (char *)realloc (lp->rbr, lp->rxbsz);
2692         if (!lp->rbr)
2693           {
2694             fprintf (stderr, "\rFATAL: Out of memory! Aborting at %s[%s:%d]\r\n",
2695                      __func__, __FILE__, __LINE__);
2696 #if defined(USE_BACKTRACE)
2697 # if defined(SIGUSR2)
2698             (void)raise(SIGUSR2);
2699             /*NOTREACHED*/ /* unreachable */
2700 # endif /* if defined(SIGUSR2) */
2701 #endif /* if defined(USE_BACKTRACE) */
2702             abort();
2703           }
2704         lp->packet = packet;
2705         if (nolog) {
2706             FREE(lp->txlogname);
2707             lp->txlogname = NULL;
2708             if (lp->txlog) {
2709                 sim_close_logfile (&lp->txlogref);
2710                 lp->txlog = NULL;
2711                 }
2712             }
2713         if ((listen[0]) && (!datagram)) {
2714             if ((mp->lines == 1) && (mp->master))
2715                 return sim_messagef (SCPE_ARG, "Single Line MUX can have either line specific OR MUS listener but NOT both\n");
2716             sock = sim_master_sock (listen, &r);            /* make master socket */
2717             if (r)
2718                 return sim_messagef (SCPE_ARG, "Invalid Listen Specification: %s\n", listen);
2719             if (sock == INVALID_SOCKET)                     /* open error */
2720                 return sim_messagef (SCPE_OPENERR, "Can't listen on port: %s\n", listen);
2721             _mux_detach_line (lp, TRUE, FALSE);
2722             sim_printf ("Line %d Listening on port %s\n", line, listen);
2723             lp->port = (char *)realloc (lp->port, 1 + strlen (listen));
2724             if (!lp->port)
2725               {
2726                 fprintf (stderr, "\rFATAL: Out of memory! Aborting at %s[%s:%d]\r\n",
2727                          __func__, __FILE__, __LINE__);
2728 #if defined(USE_BACKTRACE)
2729 # if defined(SIGUSR2)
2730                 (void)raise(SIGUSR2);
2731                 /*NOTREACHED*/ /* unreachable */
2732 # endif /* if defined(SIGUSR2) */
2733 #endif /* if defined(USE_BACKTRACE) */
2734                 abort();
2735               }
2736             (void)strcpy (lp->port, listen);                /* save port */
2737             lp->master = sock;                              /* save master socket */
2738             if (listennotelnet != mp->notelnet)
2739                 lp->notelnet = listennotelnet;
2740             else
2741                 lp->notelnet = mp->notelnet;
2742             }
2743         if (destination[0]) {
2744                 lp->datagram = datagram;
2745                 if (datagram) {
2746                     if (listen[0]) {
2747                         lp->port = (char *)realloc (lp->port, 1 + strlen (listen));
2748                         if (!lp->port)
2749                           {
2750                             fprintf (stderr, "\rFATAL: Out of memory! Aborting at %s[%s:%d]\r\n",
2751                                      __func__, __FILE__, __LINE__);
2752 #if defined(USE_BACKTRACE)
2753 # if defined(SIGUSR2)
2754                             (void)raise(SIGUSR2);
2755                             /*NOTREACHED*/ /* unreachable */
2756 # endif /* if defined(SIGUSR2) */
2757 #endif /* if defined(USE_BACKTRACE) */
2758                             abort();
2759                           }
2760                         (void)strcpy (lp->port, listen);          /* save port */
2761                         }
2762                     else
2763                         return sim_messagef (SCPE_ARG, "Missing listen port for Datagram socket\n");
2764                     }
2765                 sock = sim_connect_sock_ex (datagram ? listen : NULL, hostport, "localhost", NULL,
2766                                             (datagram ? SIM_SOCK_OPT_DATAGRAM : 0) | (packet ? SIM_SOCK_OPT_NODELAY : 0));
2767                 if (sock != INVALID_SOCKET) {
2768                     _mux_detach_line (lp, FALSE, TRUE);
2769                     lp->destination = (char *)malloc(1+strlen(hostport));
2770                     if (!lp->destination)
2771                       {
2772                         fprintf (stderr, "\rFATAL: Out of memory! Aborting at %s[%s:%d]\r\n",
2773                                  __func__, __FILE__, __LINE__);
2774 #if defined(USE_BACKTRACE)
2775 # if defined(SIGUSR2)
2776                         (void)raise(SIGUSR2);
2777                         /*NOTREACHED*/ /* unreachable */
2778 # endif /* if defined(SIGUSR2) */
2779 #endif /* if defined(USE_BACKTRACE) */
2780                         abort();
2781                       }
2782                     (void)strcpy (lp->destination, hostport);
2783                     if (!lp->modem_control || (lp->modembits & TMXR_MDM_DTR)) {
2784                         lp->connecting = sock;
2785                         lp->ipad = (char *)malloc (1 + strlen (lp->destination));
2786                         if (!lp->ipad)
2787                           {
2788                             fprintf (stderr, "\rFATAL: Out of memory! Aborting at %s[%s:%d]\r\n",
2789                                      __func__, __FILE__, __LINE__);
2790 #if defined(USE_BACKTRACE)
2791 # if defined(SIGUSR2)
2792                             (void)raise(SIGUSR2);
2793                             /*NOTREACHED*/ /* unreachable */
2794 # endif /* if defined(SIGUSR2) */
2795 #endif /* if defined(USE_BACKTRACE) */
2796                             abort();
2797                           }
2798                         (void)strcpy (lp->ipad, lp->destination);
2799                         }
2800                     else
2801                         sim_close_sock (sock);
2802                     lp->notelnet = notelnet;
2803                     tmxr_init_line (lp);                    /* init the line state */
2804                     }
2805                 else
2806                     return sim_messagef (SCPE_ARG, "Can't open %s socket on %s%s%s\n",
2807                                          datagram ? "Datagram" : "Stream", datagram ? listen : "",
2808                                          datagram ? "<->" : "", hostport);
2809                 }
2810             }
2811         if (loopback) {
2812             if (lp != NULL) {
2813               tmxr_set_line_loopback (lp, loopback);
2814               sim_printf ("Line %d operating in loopback mode\n", line);
2815               }
2816             }
2817         if (lp != NULL) lp->modem_control = modem_control;
2818         if (speed[0] && (!datagram)
2819         )
2820             tmxr_set_line_speed (lp, speed);
2821         r = SCPE_OK;
2822         }
2823 if (r == SCPE_OK)
2824     tmxr_add_to_open_list (mp);
2825 return r;
2826 }
2827 
2828 /* Declare which unit polls for input
2829 
2830    Inputs:
2831         *mp     =       the mux
2832         line    =       the line number
2833         *uptr_poll =    the unit which polls
2834 
2835    Outputs:
2836         none
2837 
2838    Implementation note:
2839 
2840         Only devices which poll on a unit different from the unit provided
2841         at MUX attach time need call this function.  Calling this API is
2842         necessary for asynchronous multiplexer support and unnecessary
2843         otherwise.
2844 
2845 */
2846 
2847 t_stat tmxr_set_line_unit (TMXR *mp, int line, UNIT *uptr_poll)
     /* [previous][next][first][last][top][bottom][index][help] */
2848 {
2849 if ((line < 0) || (line >= mp->lines))
2850     return SCPE_ARG;
2851 mp->ldsc[line].uptr = uptr_poll;
2852 return SCPE_OK;
2853 }
2854 
2855 /* Declare which unit polls for output
2856 
2857    Inputs:
2858         *mp     =       the mux
2859         line    =       the line number
2860         *uptr_poll =    the unit which polls for output
2861 
2862    Outputs:
2863         none
2864 
2865    Implementation note:
2866 
2867         Only devices which poll on a unit different from the unit provided
2868         at MUX attach time need call this function ABD different from the
2869         unit which polls for input.  Calling this API is necessary for
2870         asynchronous multiplexer support and unnecessary otherwise.
2871 
2872 */
2873 
2874 t_stat tmxr_set_line_output_unit (TMXR *mp, int line, UNIT *uptr_poll)
     /* [previous][next][first][last][top][bottom][index][help] */
2875 {
2876 if ((line < 0) || (line >= mp->lines))
2877     return SCPE_ARG;
2878 mp->ldsc[line].o_uptr = uptr_poll;
2879 return SCPE_OK;
2880 }
2881 
2882 static TMXR **tmxr_open_devices = NULL;
2883 static int tmxr_open_device_count = 0;
2884 
2885 t_stat tmxr_start_poll (void)
     /* [previous][next][first][last][top][bottom][index][help] */
2886 {
2887 return SCPE_OK;
2888 }
2889 
2890 t_stat tmxr_stop_poll (void)
     /* [previous][next][first][last][top][bottom][index][help] */
2891 {
2892 return SCPE_OK;
2893 }
2894 
2895 static void tmxr_add_to_open_list (TMXR* mux)
     /* [previous][next][first][last][top][bottom][index][help] */
2896 {
2897 int i;
2898 t_bool found = FALSE;
2899 
2900 for (i=0; i<tmxr_open_device_count; ++i)
2901     if (tmxr_open_devices[i] == mux) {
2902         found = TRUE;
2903         break;
2904         }
2905 if (!found) {
2906     tmxr_open_devices = (TMXR **)realloc(tmxr_open_devices, (tmxr_open_device_count+1)*sizeof(*tmxr_open_devices));
2907     if (!tmxr_open_devices)
2908       {
2909         fprintf (stderr, "\rFATAL: Out of memory! Aborting at %s[%s:%d]\r\n",
2910                  __func__, __FILE__, __LINE__);
2911 #if defined(USE_BACKTRACE)
2912 # if defined(SIGUSR2)
2913         (void)raise(SIGUSR2);
2914         /*NOTREACHED*/ /* unreachable */
2915 # endif /* if defined(SIGUSR2) */
2916 #endif /* if defined(USE_BACKTRACE) */
2917         abort();
2918       }
2919     tmxr_open_devices[tmxr_open_device_count++] = mux;
2920     for (i=0; i<mux->lines; i++)
2921         if (0 == mux->ldsc[i].send.delay)
2922             mux->ldsc[i].send.delay = SEND_DEFAULT_DELAY;
2923     }
2924 }
2925 
2926 static void _tmxr_remove_from_open_list (const TMXR* mux)
     /* [previous][next][first][last][top][bottom][index][help] */
2927 {
2928 int i, j;
2929 
2930 for (i=0; i<tmxr_open_device_count; ++i)
2931     if (tmxr_open_devices[i] == mux) {
2932         for (j=i+1; j<tmxr_open_device_count; ++j)
2933             tmxr_open_devices[j-1] = tmxr_open_devices[j];
2934         --tmxr_open_device_count;
2935         break;
2936         }
2937 }
2938 
2939 static t_stat _tmxr_locate_line_send_expect (const char *cptr, SEND **snd, EXPECT **exp)
     /* [previous][next][first][last][top][bottom][index][help] */
2940 {
2941 char gbuf[CBUFSIZE];
2942 DEVICE *dptr;
2943 int i;
2944 t_stat r;
2945 
2946 if (snd)
2947     *snd = NULL;
2948 if (exp)
2949     *exp = NULL;
2950 cptr = get_glyph(cptr, gbuf, ':');
2951 dptr = find_dev (gbuf);                 /* device match? */
2952 if (!dptr)
2953     return SCPE_ARG;
2954 
2955 for (i=0; i<tmxr_open_device_count; ++i)
2956     if (tmxr_open_devices[i]->dptr == dptr) {
2957         int line = (int)get_uint (cptr, 10, tmxr_open_devices[i]->lines, &r);
2958         if (r != SCPE_OK)
2959             return r;
2960         if (snd)
2961             *snd = &tmxr_open_devices[i]->ldsc[line].send;
2962         if (exp)
2963             *exp = &tmxr_open_devices[i]->ldsc[line].expect;
2964         return SCPE_OK;
2965         }
2966 return SCPE_ARG;
2967 }
2968 
2969 t_stat tmxr_locate_line_send (const char *cptr, SEND **snd)
     /* [previous][next][first][last][top][bottom][index][help] */
2970 {
2971 return _tmxr_locate_line_send_expect (cptr, snd, NULL);
2972 }
2973 
2974 t_stat tmxr_locate_line_expect (const char *cptr, EXPECT **exp)
     /* [previous][next][first][last][top][bottom][index][help] */
2975 {
2976 return _tmxr_locate_line_send_expect (cptr, NULL, exp);
2977 }
2978 
2979 t_stat tmxr_change_async (void)
     /* [previous][next][first][last][top][bottom][index][help] */
2980 {
2981 return SCPE_OK;
2982 }
2983 
2984 /* Attach unit to master socket */
2985 
2986 t_stat tmxr_attach_ex (TMXR *mp, UNIT *uptr, CONST char *cptr, t_bool async)
     /* [previous][next][first][last][top][bottom][index][help] */
2987 {
2988 t_stat r;
2989 int32 i;
2990 
2991 r = tmxr_open_master (mp, cptr);                        /* open master socket */
2992 if (r != SCPE_OK)                                       /* error? */
2993     return r;
2994 mp->uptr = uptr;                                        /* save unit for polling */
2995 uptr->filename = tmxr_mux_attach_string (uptr->filename, mp);/* save */
2996 uptr->flags = uptr->flags | UNIT_ATT;                   /* no more errors */
2997 uptr->tmxr = (void *)mp;
2998 if ((mp->lines > 1) ||
2999     ((mp->master == 0) &&
3000      (mp->ldsc[0].connecting == 0)
3001 ))
3002     uptr->dynflags = uptr->dynflags | UNIT_ATTMULT;     /* allow multiple attach commands */
3003 
3004 uptr->dynflags |= TMUF_NOASYNCH;                        /* tag as no asynch */
3005 
3006 if (mp->dptr == NULL)                                   /* has device been set? */
3007     mp->dptr = find_dev_from_unit (uptr);               /* no, so set device now */
3008 
3009 if (mp->dptr) {
3010     for (i=0; i<mp->lines; i++) {
3011         mp->ldsc[i].expect.dptr = mp->dptr;
3012         mp->ldsc[i].expect.dbit = TMXR_DBG_EXP;
3013         mp->ldsc[i].send.dptr   = mp->dptr;
3014         mp->ldsc[i].send.dbit   = TMXR_DBG_SEND;
3015         }
3016     }
3017 tmxr_add_to_open_list (mp);
3018 return SCPE_OK;
3019 }
3020 
3021 t_stat tmxr_startup (void)
     /* [previous][next][first][last][top][bottom][index][help] */
3022 {
3023 return SCPE_OK;
3024 }
3025 
3026 t_stat tmxr_shutdown (void)
     /* [previous][next][first][last][top][bottom][index][help] */
3027 {
3028 if (tmxr_open_device_count)
3029     return SCPE_IERR;
3030 return SCPE_OK;
3031 }
3032 
3033 t_stat tmxr_show_open_devices (FILE* st, DEVICE *dptr, UNIT* uptr, int32 val, CONST char* desc)
     /* [previous][next][first][last][top][bottom][index][help] */
3034 {
3035 int i, j;
3036 
3037 if (0 == tmxr_open_device_count)
3038     fprintf(st, "No Attached Multiplexer Devices\n");
3039 else {
3040     for (i=0; i<tmxr_open_device_count; ++i) {
3041         TMXR *mp = tmxr_open_devices[i];
3042         TMLN *lp;
3043         char *attach;
3044 
3045         fprintf(st, "Multiplexer device: %s", (mp->dptr ? sim_dname (mp->dptr) : ""));
3046         if (mp->lines > 1) {
3047             fprintf(st, ", ");
3048             tmxr_show_lines(st, NULL, 0, mp);
3049             }
3050         if (mp->packet)
3051             fprintf(st, ", Packet");
3052         if (mp->datagram)
3053             fprintf(st, ", UDP");
3054         if (mp->notelnet)
3055             fprintf(st, ", Telnet=disabled");
3056         if (mp->modem_control)
3057             fprintf(st, ", ModemControl=enabled");
3058         if (mp->buffered)
3059             fprintf(st, ", Buffered=%d", mp->buffered);
3060         attach = tmxr_mux_attach_string (NULL, mp);
3061         if (attach)
3062             fprintf(st, ",\n    attached to %s, ", attach);
3063         FREE (attach);
3064         tmxr_show_summ(st, NULL, 0, mp);
3065         fprintf(st, ", sessions=%d", mp->sessions);
3066         if (mp->lines == 1) {
3067             if (mp->ldsc->rxbps) {
3068                 fprintf(st, ", Speed=%lu", (unsigned long)mp->ldsc->rxbps);
3069                 if (mp->ldsc->rxbpsfactor != TMXR_RX_BPS_UNIT_SCALE)
3070                     fprintf(st, "*%.0f", mp->ldsc->rxbpsfactor/TMXR_RX_BPS_UNIT_SCALE);
3071                 fprintf(st, " bps");
3072                 }
3073             }
3074         fprintf(st, "\n");
3075         if (mp->ring_start_time) {
3076             fprintf (st, "    incoming Connection from: %s ringing for %lu milliseconds\n",
3077                      mp->ring_ipad, (unsigned long)sim_os_msec () - (unsigned long)mp->ring_start_time);
3078             }
3079         for (j = 0; j < mp->lines; j++) {
3080             lp = mp->ldsc + j;
3081             if (mp->lines > 1) {
3082                 if (lp->dptr && (mp->dptr != lp->dptr))
3083                     fprintf (st, "Device: %s ", sim_dname(lp->dptr));
3084                 fprintf (st, "Line: %d", j);
3085                 if (mp->notelnet != lp->notelnet)
3086                     fprintf (st, " - %stelnet", lp->notelnet ? "no" : "");
3087                 if (lp->uptr && (lp->uptr != lp->mp->uptr))
3088                     fprintf (st, " - Unit: %s", sim_uname (lp->uptr));
3089                 if (mp->modem_control != lp->modem_control)
3090                     fprintf(st, ", ModemControl=%s", lp->modem_control ? "enabled" : "disabled");
3091                 if (lp->loopback)
3092                     fprintf(st, ", Loopback");
3093                 if (lp->rxbps) {
3094                     fprintf(st, ", Speed=%lu", (unsigned long)lp->rxbps);
3095                     if (lp->rxbpsfactor != TMXR_RX_BPS_UNIT_SCALE)
3096                         fprintf(st, "*%.0f", lp->rxbpsfactor/TMXR_RX_BPS_UNIT_SCALE);
3097                     fprintf(st, " bps");
3098                     }
3099                 fprintf (st, "\n");
3100                 }
3101             if ((!lp->sock) && (!lp->connecting)
3102             && (!lp->master)) {
3103                 if (lp->modem_control)
3104                     tmxr_fconns (st, lp, -1);
3105                 continue;
3106                 }
3107             tmxr_fconns (st, lp, -1);
3108             tmxr_fstats (st, lp, -1);
3109             }
3110         }
3111     }
3112 return SCPE_OK;
3113 }
3114 
3115 /* Close a master listening socket.
3116 
3117    The listening socket associated with multiplexer descriptor "mp" is closed
3118    and deallocated.  In addition, all current Telnet sessions are disconnected.
3119    Serial and outgoing sessions are also disconnected.
3120 */
3121 
3122 t_stat tmxr_close_master (TMXR *mp)
     /* [previous][next][first][last][top][bottom][index][help] */
3123 {
3124 int32 i;
3125 TMLN *lp;
3126 
3127 for (i = 0; i < mp->lines; i++) {  /* loop thru conn */
3128     lp = mp->ldsc + i;
3129 
3130     if (!lp->destination && lp->sock) {                 /* not serial and is connected? */
3131         tmxr_report_disconnection (lp);                 /* report disconnection */
3132         tmxr_reset_ln (lp);                             /* disconnect line */
3133         }
3134     else {
3135         if (lp->sock) {
3136             tmxr_report_disconnection (lp);             /* report disconnection */
3137             tmxr_reset_ln (lp);
3138             }
3139         FREE (lp->destination);
3140         lp->destination = NULL;
3141         if (lp->connecting) {
3142             lp->sock = lp->connecting;
3143             lp->connecting = 0;
3144             tmxr_reset_ln (lp);
3145             }
3146         lp->conn = FALSE;
3147         }
3148     if (lp->master) {
3149         sim_close_sock (lp->master);                    /* close master socket */
3150         lp->master = 0;
3151         FREE (lp->port);
3152         lp->port = NULL;
3153         }
3154     lp->txbfd = 0;
3155     FREE (lp->txb);
3156     lp->txb = NULL;
3157     FREE (lp->rxb);
3158     lp->rxb = NULL;
3159     FREE (lp->rbr);
3160     lp->rbr = NULL;
3161     lp->modembits = 0;
3162     }
3163 
3164 if (mp->master)
3165     sim_close_sock (mp->master);                        /* close master socket */
3166 mp->master = 0;
3167 FREE (mp->port);
3168 mp->port = NULL;
3169 if (mp->ring_sock != INVALID_SOCKET) {
3170     sim_close_sock (mp->ring_sock);
3171     mp->ring_sock = INVALID_SOCKET;
3172     FREE (mp->ring_ipad);
3173     mp->ring_ipad = NULL;
3174     mp->ring_start_time = 0;
3175     }
3176 _tmxr_remove_from_open_list (mp);
3177 return SCPE_OK;
3178 }
3179 
3180 /* Detach unit from master socket and close all active network connections.
3181    Note that we return SCPE_OK, regardless of whether a listening socket was
3182    attached.
3183 */
3184 
3185 t_stat tmxr_detach (TMXR *mp, UNIT *uptr)
     /* [previous][next][first][last][top][bottom][index][help] */
3186 {
3187 int32 i;
3188 
3189 if (!(uptr->flags & UNIT_ATT))                          /* attached? */
3190     return SCPE_OK;
3191 tmxr_close_master (mp);                                 /* close master socket */
3192 FREE (uptr->filename);                                  /* free setup string */
3193 uptr->filename = NULL;
3194 uptr->tmxr = NULL;
3195 mp->last_poll_time = 0;
3196 for (i=0; i < mp->lines; i++) {
3197     UNIT *uptr = mp->ldsc[i].uptr ? mp->ldsc[i].uptr : mp->uptr;
3198     UNIT *o_uptr = mp->ldsc[i].o_uptr ? mp->ldsc[i].o_uptr : mp->uptr;
3199 
3200     uptr->dynflags &= ~UNIT_TM_POLL;                    /* no polling */
3201     o_uptr->dynflags &= ~UNIT_TM_POLL;                  /* no polling */
3202     }
3203 uptr->flags &= ~(UNIT_ATT);                             /* not attached */
3204 uptr->dynflags &= ~(UNIT_TM_POLL|TMUF_NOASYNCH);        /* no polling, not asynch disabled  */
3205 return SCPE_OK;
3206 }
3207 
3208 t_stat tmxr_activate (UNIT *uptr, int32 interval)
     /* [previous][next][first][last][top][bottom][index][help] */
3209 {
3210 if (uptr->dynflags & UNIT_TMR_UNIT)
3211     return sim_timer_activate (uptr, interval);
3212 return _sim_activate (uptr, interval);
3213 }
3214 
3215 t_stat tmxr_activate_after (UNIT *uptr, uint32 usecs_walltime)
     /* [previous][next][first][last][top][bottom][index][help] */
3216 {
3217 return _sim_activate_after (uptr, usecs_walltime);
3218 }
3219 
3220 /* Generic Multiplexer attach help */
3221 
3222 t_stat tmxr_attach_help(FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
3223 {
3224 TMXR *mux = (TMXR *)dptr->help_ctx;
3225 t_bool single_line = FALSE;               /* default to Multi-Line help */
3226 
3227 if (mux)
3228    single_line = (mux->lines == 1);
3229 
3230 if (!flag)
3231     fprintf (st, "%s Multiplexer Attach Help\n\n", dptr->name);
3232 if (single_line) {          /* Single Line Multiplexer */
3233     fprintf (st, "The %s multiplexer may be connected to terminal emulators supporting the\n", dptr->name);
3234     fprintf (st, "Telnet protocol via sockets.\n\n");
3235     if (mux->modem_control) {
3236         fprintf (st, "The %s device is a full modem control device and therefore is capable of\n", dptr->name);
3237         fprintf (st, "passing port configuration information and modem signals.\n");
3238         }
3239     fprintf (st, "A Telnet listening port can be configured with:\n\n");
3240     fprintf (st, "   sim> ATTACH %s {interface:}port\n\n", dptr->name);
3241     fprintf (st, "Line buffering can be enabled for the %s device with:\n\n", dptr->name);
3242     fprintf (st, "   sim> ATTACH %s Buffer{=bufsize}\n\n", dptr->name);
3243     fprintf (st, "Line buffering can be disabled for the %s device with:\n\n", dptr->name);
3244     fprintf (st, "   sim> ATTACH %s NoBuffer\n\n", dptr->name);
3245     fprintf (st, "The default buffer size is 32k bytes, the max buffer size is 1024k bytes\n\n");
3246     fprintf (st, "The outbound traffic the %s device can be logged to a file with:\n", dptr->name);
3247     fprintf (st, "   sim> ATTACH %s Log=LogFileName\n\n", dptr->name);
3248     fprintf (st, "File logging can be disabled for the %s device with:\n\n", dptr->name);
3249     fprintf (st, "   sim> ATTACH %s NoLog\n\n", dptr->name);
3250     fprintf (st, "The %s device may be connected to a serial port on the host system.\n", dptr->name);
3251     }
3252 else {
3253     fprintf (st, "%s multiplexer lines may be connected to terminal emulators supporting the\n", dptr->name);
3254     fprintf (st, "Telnet protocol via sockets, or to hardware terminals via host serial\n");
3255     fprintf (st, "ports.  Concurrent Telnet and serial connections may be mixed on a given\n");
3256     fprintf (st, "multiplexer.\n\n");
3257     if (mux && mux->modem_control) {
3258         fprintf (st, "The %s device is a full modem control device and therefore is capable of\n", dptr->name);
3259         fprintf (st, "passing port configuration information and modem signals on all lines.\n");
3260         }
3261     fprintf (st, "Modem Control signalling behaviors can be enabled/disabled on a specific\n");
3262     fprintf (st, "multiplexer line with:\n\n");
3263     fprintf (st, "   sim> ATTACH %s Line=n,Modem\n", dptr->name);
3264     fprintf (st, "   sim> ATTACH %s Line=n,NoModem\n\n", dptr->name);
3265     fprintf (st, "A Telnet listening port can be configured with:\n\n");
3266     fprintf (st, "   sim> ATTACH %s {interface:}port\n\n", dptr->name);
3267     if (mux)
3268         fprintf (st, "Line buffering for all %d lines on the %s device can be configured with:\n\n", mux->lines, dptr->name);
3269     else
3270         fprintf (st, "Line buffering for all lines on the %s device can be configured with:\n\n", dptr->name);
3271     fprintf (st, "   sim> ATTACH %s Buffer{=bufsize}\n\n", dptr->name);
3272     if (mux)
3273         fprintf (st, "Line buffering for all %d lines on the %s device can be disabled with:\n\n", mux->lines, dptr->name);
3274     else
3275         fprintf (st, "Line buffering for all lines on the %s device can be disabled with:\n\n", dptr->name);
3276     fprintf (st, "   sim> ATTACH %s NoBuffer\n\n", dptr->name);
3277     fprintf (st, "The default buffer size is 32k bytes, the max buffer size is 1024k bytes\n\n");
3278     fprintf (st, "The outbound traffic for the lines of the %s device can be logged to files\n", dptr->name);
3279     fprintf (st, "with:\n\n");
3280     fprintf (st, "   sim> ATTACH %s Log=LogFileName\n\n", dptr->name);
3281     fprintf (st, "The log file name for each line uses the above LogFileName as a template\n");
3282     fprintf (st, "for the actual file name which will be LogFileName_n where n is the line\n");
3283     fprintf (st, "number.\n\n");
3284     fprintf (st, "Multiplexer lines may be connected to serial ports on the host system.\n");
3285     }
3286 fprintf (st, "Serial ports may be specified as an operating system specific device names\n");
3287 fprintf (st, "or using simh generic serial names.  simh generic names are of the form\n");
3288 fprintf (st, "serN, where N is from 0 thru one less than the maximum number of serial\n");
3289 fprintf (st, "ports on the local system.  The mapping of simh generic port names to OS \n");
3290 fprintf (st, "specific names can be displayed using the following command:\n\n");
3291 fprintf (st, "   sim> SHOW SERIAL\n");
3292 fprintf (st, "   Serial devices:\n");
3293 fprintf (st, "    ser0   COM1 (\\Device\\Serial0)\n");
3294 fprintf (st, "    ser1   COM3 (Winachcf0)\n\n");
3295 if (single_line) {          /* Single Line Multiplexer */
3296     fprintf (st, "   sim> ATTACH %s Connect=ser0\n\n", dptr->name);
3297     fprintf (st, "or equivalently:\n\n");
3298     fprintf (st, "   sim> ATTACH %s Connect=COM1\n\n", dptr->name);
3299     }
3300 else {
3301     fprintf (st, "   sim> ATTACH %s Line=n,Connect=ser0\n\n", dptr->name);
3302     fprintf (st, "or equivalently:\n\n");
3303     fprintf (st, "   sim> ATTACH %s Line=n,Connect=COM1\n\n", dptr->name);
3304     if (mux)
3305         fprintf (st, "Valid line numbers are from 0 thru %d\n\n", mux->lines-1);
3306     }
3307 if (single_line) {          /* Single Line Multiplexer */
3308     fprintf (st, "The input data rate for the %s device can be controlled by\n", dptr->name);
3309     fprintf (st, "specifying SPEED=nnn{*fac} on the ATTACH command.\n");
3310     }
3311 else {
3312     fprintf (st, "The input data rate for all lines or a particular line of a the %s\n", dptr->name);
3313     fprintf (st, "device can be controlled by specifying SPEED=nnn{*fac} on the ATTACH command.\n");
3314     }
3315 fprintf (st, "SPEED values can be any one of:\n\n");
3316 fprintf (st, "    0 50 75 110 134 150 300 600 1200 1800 2000 2400\n");
3317 fprintf (st, "    3600 4800 7200 9600 19200 38400 57600 76800 115200\n\n");
3318 fprintf (st, "A SPEED value of 0 causes input data to be delivered to the simulated\n");
3319 fprintf (st, "port as fast as it arrives.\n\n");
3320 fprintf (st, "If a simulated multiplexor devices can programmatically set a serial\n");
3321 fprintf (st, "port line speed, the programmatically specified speed will take precedence\n");
3322 fprintf (st, "over any input speed specified on an attach command.\n");
3323 fprintf (st, "Some simulated systems run very much faster than the original system\n");
3324 fprintf (st, "which is being simulated.  To accommodate this, the speed specified may\n");
3325 fprintf (st, "include a factor which will increase the input data delivery rate by\n");
3326 fprintf (st, "the specified factor.  A factor is specified with a speed value of the\n");
3327 fprintf (st, "form \"speed*factor\".  Factor values can range from 1 thru 32.\n");
3328 fprintf (st, "Example:\n\n");
3329 fprintf (st, "   sim> ATTACH %s 1234,SPEED=2400\n", dptr->name);
3330 fprintf (st, "   sim> ATTACH %s 1234,SPEED=9600*8\n", dptr->name);
3331 if (!single_line)
3332     fprintf (st, "   sim> ATTACH %s Line=2,SPEED=2400\n", dptr->name);
3333 fprintf (st, "\n");
3334 fprintf (st, "The SPEED parameter only influences the rate at which data is delivered\n");
3335 fprintf (st, "into the simulated multiplexor port.  Output data rates are unaffected\n");
3336 fprintf (st, "If an attach command specifies a speed multiply factor, that value will\n");
3337 fprintf (st, "persist independent of any programmatic action by the simulated system to\n");
3338 fprintf (st, "change the port speed.\n\n");
3339 fprintf (st, "An optional serial port configuration string may be present after the port\n");
3340 fprintf (st, "name.  If present, it must be separated from the port name with a semicolon\n");
3341 fprintf (st, "and has this form:\n\n");
3342 fprintf (st, "   <rate>-<charsize><parity><stopbits>\n\n");
3343 fprintf (st, "where:\n");
3344 fprintf (st, "   rate     = communication rate in bits per second\n");
3345 fprintf (st, "   charsize = character size in bits (5-8, including optional parity)\n");
3346 fprintf (st, "   parity   = parity designator (N/E/O/M/S for no/even/odd/mark/space parity)\n");
3347 fprintf (st, "   stopbits = number of stop bits (1, 1.5, or 2)\n\n");
3348 fprintf (st, "As an example:\n\n");
3349 fprintf (st, "   9600-8n1\n\n");
3350 fprintf (st, "The supported rates, sizes, and parity options are host-specific.  If\n");
3351 fprintf (st, "a configuration string is not supplied, then the default of 9600-8N1\n");
3352 fprintf (st, "is used.\n");
3353 fprintf (st, "Note: The serial port configuration option is only available on multiplexer\n");
3354 fprintf (st, "      lines which are not operating with full modem control behaviors enabled.\n");
3355 fprintf (st, "      Lines with full modem control behaviors enabled have all of their\n");
3356 fprintf (st, "      configuration managed by the Operating System running within the\n");
3357 fprintf (st, "      simulator.\n\n");
3358 fprintf (st, "An attachment to a serial port with the '-V' switch will cause a\n");
3359 fprintf (st, "connection message to be output to the connected serial port.\n");
3360 fprintf (st, "This will help to confirm the correct port has been connected and\n");
3361 fprintf (st, "that the port settings are reasonable for the connected device.\n");
3362 fprintf (st, "This would be done as:\n\n");
3363 if (single_line)            /* Single Line Multiplexer */
3364     fprintf (st, "   sim> ATTACH -V %s Connect=SerN\n", dptr->name);
3365 else {
3366     fprintf (st, "   sim> ATTACH -V %s Line=n,Connect=SerN\n\n", dptr->name);
3367     fprintf (st, "Line specific tcp listening ports are supported.  These are configured\n");
3368     fprintf (st, "using commands of the form:\n\n");
3369     fprintf (st, "   sim> ATTACH %s Line=n,{interface:}port{;notelnet}\n\n", dptr->name);
3370     }
3371 fprintf (st, "Direct computer to computer connections (Virtual Null Modem cables) may\n");
3372 fprintf (st, "be established using the telnet protocol or via raw tcp sockets.\n\n");
3373 fprintf (st, "   sim> ATTACH %s Line=n,Connect=host:port{;notelnet}\n\n", dptr->name);
3374 fprintf (st, "Computer to computer virtual connections can be one way (as illustrated\n");
3375 fprintf (st, "above) or symmetric.  A symmetric connection is configured by combining\n");
3376 if (single_line) {          /* Single Line Multiplexer */
3377     fprintf (st, "a one way connection with a tcp listening port on the same line:\n\n");
3378     fprintf (st, "   sim> ATTACH %s listenport,Connect=host:port\n\n", dptr->name);
3379     }
3380 else {
3381     fprintf (st, "a one way connection with a tcp listening port on the same line:\n\n");
3382     fprintf (st, "   sim> ATTACH %s Line=n,listenport,Connect=host:port\n\n", dptr->name);
3383     }
3384 fprintf (st, "When symmetric virtual connections are configured, incoming connections\n");
3385 fprintf (st, "on the specified listening port are checked to assure that they actually\n");
3386 fprintf (st, "come from the specified connection destination host system.\n\n");
3387 if (single_line) {          /* Single Line Multiplexer */
3388     fprintf (st, "The %s device can be attached in LOOPBACK mode:\n\n", dptr->name);
3389     fprintf (st, "   sim> ATTACH %s Loopback\n\n", dptr->name);
3390     }
3391 else {
3392     fprintf (st, "A line on the %s device can be attached in LOOPBACK mode:\n\n", dptr->name);
3393     fprintf (st, "   sim> ATTACH %s Line=n,Loopback\n\n", dptr->name);
3394     }
3395 fprintf (st, "When operating in LOOPBACK mode, all outgoing data arrives as input and\n");
3396 fprintf (st, "outgoing modem signals (if enabled) (DTR and RTS) are reflected in the\n");
3397 fprintf (st, "incoming modem signals (DTR->(DCD and DSR), RTS->CTS)\n\n");
3398 if (single_line)            /* Single Line Multiplexer */
3399     fprintf (st, "The connection configured for the %s device is unconfigured by:\n\n", dptr->name);
3400 else
3401     fprintf (st, "All connections configured for the %s device are unconfigured by:\n\n", dptr->name);
3402 fprintf (st, "   sim> DETACH %s\n\n", dptr->name);
3403 if (dptr->modifiers) {
3404     MTAB *mptr;
3405 
3406     for (mptr = dptr->modifiers; mptr->mask != 0; mptr++)
3407         if (mptr->valid == &tmxr_dscln) {
3408             fprintf (st, "A specific line on the %s device can be disconnected with:\n\n", dptr->name);
3409             fprintf (st, "   sim> SET %s %s=n\n\n", dptr->name, mptr->mstring);
3410             fprintf (st, "This will cause a telnet connection to be closed, but a serial port will\n");
3411             fprintf (st, "normally have DTR dropped for 500ms and raised again (thus hanging up a\n");
3412             fprintf (st, "modem on that serial port).\n\n");
3413             fprintf (st, "A line which is connected to a serial port can be manually closed by\n");
3414             fprintf (st, "adding the -C switch to a %s command.\n\n", mptr->mstring);
3415             fprintf (st, "   sim> SET -C %s %s=n\n\n", dptr->name, mptr->mstring);
3416             }
3417     }
3418 return SCPE_OK;
3419 }
3420 
3421 /* Stub examine and deposit */
3422 
3423 t_stat tmxr_ex (t_value *vptr, t_addr addr, UNIT *uptr, int32 sw)
     /* [previous][next][first][last][top][bottom][index][help] */
3424 {
3425 return SCPE_NOFNC;
3426 }
3427 
3428 t_stat tmxr_dep (t_value val, t_addr addr, UNIT *uptr, int32 sw)
     /* [previous][next][first][last][top][bottom][index][help] */
3429 {
3430 return SCPE_NOFNC;
3431 }
3432 
3433 /* Write a message directly to a socket */
3434 
3435 void tmxr_msg (SOCKET sock, const char *msg)
     /* [previous][next][first][last][top][bottom][index][help] */
3436 {
3437 if ((sock) && (sock != INVALID_SOCKET))
3438     sim_write_sock (sock, msg, (int32)strlen (msg));
3439 return;
3440 }
3441 
3442 /* Write a message to a line */
3443 
3444 void tmxr_linemsg (TMLN *lp, const char *msg)
     /* [previous][next][first][last][top][bottom][index][help] */
3445 {
3446 while (*msg) {
3447     while (SCPE_STALL == tmxr_putc_ln (lp, (int32)(*msg)))
3448         if (lp->txbsz == tmxr_send_buffered_data (lp))
3449             sim_os_ms_sleep (10);
3450     ++msg;
3451     }
3452 return;
3453 }
3454 
3455 /* Write a formatted message to a line */
3456 
3457 void tmxr_linemsgf (TMLN *lp, const char *fmt, ...)
     /* [previous][next][first][last][top][bottom][index][help] */
3458 {
3459 va_list arglist;
3460 
3461 va_start (arglist, fmt);
3462 tmxr_linemsgvf (lp, fmt, arglist);
3463 va_end (arglist);
3464 }
3465 
3466 void tmxr_linemsgvf (TMLN *lp, const char *fmt, va_list arglist)
     /* [previous][next][first][last][top][bottom][index][help] */
3467 {
3468 char stackbuf[STACKBUFSIZE];
3469 int32 bufsize = sizeof(stackbuf);
3470 char *buf = stackbuf;
3471 int32 i, len;
3472 
3473 buf[bufsize-1] = '\0';
3474 while (1) {                                         /* format passed string, args */
3475     len = vsnprintf (buf, bufsize-1, fmt, arglist);
3476 
3477 /* If the formatted result didn't fit into the buffer, then grow the buffer and try again */
3478 
3479     if ((len < 0) || (len >= bufsize-1)) {
3480         if (buf != stackbuf)
3481             FREE (buf);
3482         if (bufsize >= (INT_MAX / 2))
3483             return;                          /* too big */
3484         bufsize = bufsize * 2;
3485         if (bufsize < len + 2)
3486             bufsize = len + 2;
3487         buf = (char *) malloc (bufsize);
3488         if (!buf)                            /* out of memory */
3489           {
3490             fprintf(stderr, "\rFATAL: Out of memory! Aborting at %s[%s:%d]\r\n",
3491                     __func__, __FILE__, __LINE__);
3492 #if defined(USE_BACKTRACE)
3493 # if defined(SIGUSR2)
3494             (void)raise(SIGUSR2);
3495             /*NOTREACHED*/ /* unreachable */
3496 # endif /* if defined(SIGUSR2) */
3497 #endif /* if defined(USE_BACKTRACE) */
3498             abort();
3499           }
3500         buf[bufsize-1] = '\0';
3501         continue;
3502         }
3503     break;
3504     }
3505 
3506 /* Output the formatted data expanding newlines where they exist */
3507 
3508 for (i = 0; i < len; ++i) {
3509     if (('\n' == buf[i]) && ((i == 0) || ('\r' != buf[i-1]))) {
3510         while (SCPE_STALL == tmxr_putc_ln (lp, '\r'))
3511             if (lp->txbsz == tmxr_send_buffered_data (lp))
3512                 sim_os_ms_sleep (10);
3513         }
3514     while (SCPE_STALL == tmxr_putc_ln (lp, buf[i]))
3515         if (lp->txbsz == tmxr_send_buffered_data (lp))
3516             sim_os_ms_sleep (10);
3517     }
3518 if (buf != stackbuf)
3519     FREE (buf);
3520 return;
3521 }
3522 
3523 /* Print connections - used only in named SHOW command */
3524 
3525 void tmxr_fconns (FILE *st, const TMLN *lp, int32 ln)
     /* [previous][next][first][last][top][bottom][index][help] */
3526 {
3527 int32 hr, mn, sc;
3528 uint32 tctime;
3529 
3530 if (ln >= 0)
3531     fprintf (st, "line %d: ", ln);
3532 
3533 if ((lp->sock) || (lp->connecting)) {                   /* tcp connection? */
3534     if (lp->destination)                                /* remote connection? */
3535         if (lp->datagram)
3536             fprintf (st, "Datagram Connection from %s to remote port %s\n", lp->port, lp->destination);/* print port name */
3537         else
3538             fprintf (st, "Connection to remote port %s\n", lp->destination);/* print port name */
3539     else                                                /* incoming connection */
3540         fprintf (st, "Connection from IP address %s\n", lp->ipad);
3541     }
3542 else
3543     if (lp->destination)                                /* remote connection? */
3544         fprintf (st, "Connecting to remote port %s\n", lp->destination);/* print port name */
3545 if (lp->sock) {
3546     char *sockname, *peername;
3547 
3548     sim_getnames_sock (lp->sock, &sockname, &peername);
3549     fprintf (st, "Connection %s->%s\n", sockname, peername);
3550     FREE (sockname);
3551     FREE (peername);
3552     }
3553 
3554 if ((lp->port) && (!lp->datagram))
3555     fprintf (st, "Listening on port %s\n", lp->port);   /* print port name */
3556 if (lp->cnms) {
3557     tctime = (sim_os_msec () - lp->cnms) / 1000;
3558     hr = tctime / 3600;
3559     mn = (tctime / 60) % 60;
3560     sc = tctime % 60;
3561     if (tctime)
3562         fprintf (st, " %s %02d:%02d:%02d\n", lp->connecting ? "Connecting for" : "Connected", hr, mn, sc);
3563     }
3564 else
3565     fprintf (st, " Line disconnected\n");
3566 
3567 if (lp->modem_control) {
3568     fprintf (st, " Modem Bits: %s%s%s%s%s%s\n", (lp->modembits & TMXR_MDM_DTR) ? "DTR " : "",
3569                                                 (lp->modembits & TMXR_MDM_RTS) ? "RTS " : "",
3570                                                 (lp->modembits & TMXR_MDM_DCD) ? "DCD " : "",
3571                                                 (lp->modembits & TMXR_MDM_RNG) ? "RNG " : "",
3572                                                 (lp->modembits & TMXR_MDM_CTS) ? "CTS " : "",
3573                                                 (lp->modembits & TMXR_MDM_DSR) ? "DSR " : "");
3574     }
3575 
3576 if (
3577   (lp->sock) && (!lp->datagram))
3578     fprintf (st, " %s\n", (lp->notelnet) ? "Telnet disabled (RAW data)" : "Telnet protocol");
3579 if (lp->send.buffer)
3580     sim_show_send_input (st, &lp->send);
3581 if (lp->expect.buf)
3582     sim_exp_showall (st, &lp->expect);
3583 if (lp->txlog)
3584     fprintf (st, " Logging to %s\n", lp->txlogname);
3585 return;
3586 }
3587 
3588 /* Print statistics - used only in named SHOW command */
3589 
3590 void tmxr_fstats (FILE *st, const TMLN *lp, int32 ln)
     /* [previous][next][first][last][top][bottom][index][help] */
3591 {
3592 static const char *enab = "on";
3593 static const char *dsab = "off";
3594 
3595 if (ln >= 0)
3596     fprintf (st, "Line %d:", ln);
3597 if ((!lp->sock) && (!lp->connecting)
3598    )
3599     fprintf (st, " not connected\n");
3600 else {
3601     if (ln >= 0)
3602         fprintf (st, "\n");
3603     fprintf (st, "  input (%s)", (lp->rcve? enab: dsab));
3604     if (lp->rxcnt)
3605         fprintf (st, " queued/total = %d/%d", tmxr_rqln (lp), lp->rxcnt);
3606     if (lp->rxpcnt)
3607         fprintf (st, " packets = %d", lp->rxpcnt);
3608     fprintf (st, "\n  output (%s)", (lp->xmte? enab: dsab));
3609     if (lp->txcnt || lp->txbpi)
3610         fprintf (st, " queued/total = %d/%d", tmxr_tqln (lp), lp->txcnt);
3611     if (lp->txpcnt || tmxr_tpqln (lp))
3612         fprintf (st, " packet data queued/packets sent = %d/%d",
3613             tmxr_tpqln (lp), lp->txpcnt);
3614     fprintf (st, "\n");
3615     }
3616 if (lp->txbfd)
3617     fprintf (st, "  output buffer size = %d\n", lp->txbsz);
3618 if (lp->txcnt || lp->txbpi)
3619     fprintf (st, "  bytes in buffer = %d\n",
3620                ((lp->txcnt > 0) && (lp->txcnt > lp->txbsz)) ? lp->txbsz : lp->txbpi);
3621 if (lp->txdrp)
3622     fprintf (st, "  dropped = %d\n", lp->txdrp);
3623 return;
3624 }
3625 
3626 /* Disconnect a line.
3627 
3628    Disconnect a line of the multiplexer associated with descriptor "desc" from a
3629    tcp session or a serial port.  Two calling sequences are supported:
3630 
3631     1. If "val" is zero, then "uptr" is implicitly associated with the line
3632        number corresponding to the position of the unit in the zero-based array
3633        of units belonging to the associated device, and "cptr" is ignored.  For
3634        example, if "uptr" points to unit 3 in a given device, then line 3 will
3635        be disconnected.
3636 
3637     2. If "val" is non-zero, then "cptr" points to a string that is parsed for
3638        an explicit line number, and "uptr" is ignored.  For example, if "cptr"
3639        points to the string "3", then line 3 will be disconnected.
3640 
3641    If the line was connected to a tcp session, the socket associated with the
3642    line will be closed.  If the line was connected to a serial port, the port
3643    will NOT be closed, but DTR will be dropped.  After a 500ms delay DTR will
3644    be raised again.  If the sim_switches -C flag is set, then a serial port
3645    connection will be closed.
3646 
3647    Implementation notes:
3648 
3649     1. This function is usually called as an MTAB processing routine.
3650 */
3651 
3652 t_stat tmxr_dscln (UNIT *uptr, int32 val, CONST char *cptr, void *desc)
     /* [previous][next][first][last][top][bottom][index][help] */
3653 {
3654 TMXR *mp = (TMXR *) desc;
3655 TMLN *lp;
3656 t_stat status;
3657 
3658 if (val)                                                        /* explicit line? */
3659     uptr = NULL;                                                /* indicate to get routine */
3660 
3661 lp = tmxr_get_ldsc (uptr, cptr, mp, &status);                   /* get referenced line */
3662 
3663 if (lp == NULL)                                                 /* bad line number? */
3664     return status;                                              /* report it */
3665 
3666 if ((lp->sock)
3667    ) {                                                          /* connection active? */
3668     if (!lp->notelnet)
3669         tmxr_linemsg (lp, "\r\nOperator disconnected line\r\n\n");/* report closure */
3670     tmxr_reset_ln_ex (lp, (sim_switches & SWMASK ('C')));       /* drop the line */
3671     }
3672 
3673 return SCPE_OK;
3674 }
3675 
3676 /* Enable logging for line */
3677 
3678 t_stat tmxr_set_log (UNIT *uptr, int32 val, CONST char *cptr, void *desc)
     /* [previous][next][first][last][top][bottom][index][help] */
3679 {
3680 TMXR *mp = (TMXR *) desc;
3681 TMLN *lp;
3682 t_stat r;
3683 
3684 if (cptr == NULL)                                       /* no file name? */
3685     return SCPE_2FARG;
3686 lp = tmxr_find_ldsc (uptr, val, mp);                    /* find line desc */
3687 if (lp == NULL)
3688     return SCPE_IERR;
3689 if (lp->txlog)                                          /* close existing log */
3690     tmxr_set_nolog (NULL, val, NULL, desc);
3691 lp->txlogname = (char *) calloc (CBUFSIZE, sizeof (char)); /* alloc namebuf */
3692 if (lp->txlogname == NULL)                              /* can't? */
3693     return SCPE_MEM;
3694 strncpy (lp->txlogname, cptr, CBUFSIZE-1);              /* save file name */
3695 r = sim_open_logfile (cptr, TRUE, &lp->txlog, &lp->txlogref); /* open log */
3696 if (r != SCPE_OK) {                                     /* error? */
3697     FREE (lp->txlogname);                               /* free buffer */
3698     return r;
3699     }
3700 if (lp->txlog == NULL) {                                /* error? */
3701     FREE (lp->txlogname);                               /* free buffer */
3702     return SCPE_OPENERR;
3703     }
3704 if (mp->uptr)                                           /* attached?, then update attach string */
3705     lp->mp->uptr->filename = tmxr_mux_attach_string (lp->mp->uptr->filename, lp->mp);
3706 return SCPE_OK;
3707 }
3708 
3709 /* Disable logging for line */
3710 
3711 t_stat tmxr_set_nolog (UNIT *uptr, int32 val, CONST char *cptr, void *desc)
     /* [previous][next][first][last][top][bottom][index][help] */
3712 {
3713 TMXR *mp = (TMXR *) desc;
3714 TMLN *lp;
3715 
3716 if (cptr)                                               /* no arguments */
3717     return SCPE_2MARG;
3718 lp = tmxr_find_ldsc (uptr, val, mp);                    /* find line desc */
3719 if (lp == NULL)
3720     return SCPE_IERR;
3721 if (lp->txlog) {                                        /* logging? */
3722     sim_close_logfile (&lp->txlogref);                  /* close log */
3723     FREE (lp->txlogname);                               /* free namebuf */
3724     lp->txlog = NULL;
3725     lp->txlogname = NULL;
3726     }
3727 if (mp->uptr)
3728     lp->mp->uptr->filename = tmxr_mux_attach_string (lp->mp->uptr->filename, lp->mp);
3729 return SCPE_OK;
3730 }
3731 
3732 /* Show logging status for line */
3733 
3734 t_stat tmxr_show_log (FILE *st, UNIT *uptr, int32 val, CONST void *desc)
     /* [previous][next][first][last][top][bottom][index][help] */
3735 {
3736 const TMXR *mp = (const TMXR *) desc;
3737 TMLN *lp;
3738 
3739 lp = tmxr_find_ldsc (uptr, val, mp);                    /* find line desc */
3740 if (lp == NULL)
3741     return SCPE_IERR;
3742 if (lp->txlog)
3743     fprintf (st, "logging to %s", lp->txlogname);
3744 else fprintf (st, "no logging");
3745 return SCPE_OK;
3746 }
3747 
3748 /* Set the line connection order.
3749 
3750    Example command for eight-line multiplexer:
3751 
3752       SET <dev> LINEORDER=1;5;2-4;7
3753 
3754    Resulting connection order: 1,5,2,3,4,7,0,6.
3755 
3756    Parameters:
3757     - uptr = (not used)
3758     - val  = (not used)
3759     - cptr = pointer to first character of range specification
3760     - desc = pointer to multiplexer's TMXR structure
3761 
3762    On entry, cptr points to the value portion of the command string, which may
3763    be either a semicolon-separated list of line ranges or the keyword ALL.
3764 
3765    If a line connection order array is not defined in the multiplexer
3766    descriptor, the command is rejected.  If the specified range encompasses all
3767    of the lines, the first value of the connection order array is set to -1 to
3768    indicate sequential connection order.  Otherwise, the line values in the
3769    array are set to the order specified by the command string.  All values are
3770    populated, first with those explicitly specified in the command string, and
3771    then in ascending sequence with those not specified.
3772 
3773    If an error occurs, the original line order is not disturbed.
3774 */
3775 
3776 t_stat tmxr_set_lnorder (UNIT *uptr, int32 val, CONST char *carg, void *desc)
     /* [previous][next][first][last][top][bottom][index][help] */
3777 {
3778 TMXR *mp = (TMXR *) desc;
3779 char *tbuf;
3780 char *tptr;
3781 CONST char *cptr;
3782 t_addr low, high, max = (t_addr) mp->lines - 1;
3783 int32 *list;
3784 t_bool *set;
3785 uint32 line, idx = 0;
3786 t_stat result = SCPE_OK;
3787 
3788 if (mp->lnorder == NULL)                                /* line connection order undefined? */
3789     return SCPE_NXPAR;                                  /* "Non-existent parameter" error */
3790 
3791 else if ((carg == NULL) || (*carg == '\0'))             /* line range not supplied? */
3792     return SCPE_MISVAL;                                 /* "Missing value" error */
3793 
3794 list = (int32 *) calloc (mp->lines, sizeof (int32));    /* allocate new line order array */
3795 if (list == NULL)                                       /* allocation failed? */
3796     return SCPE_MEM;                                    /* report it */
3797 
3798 set = (t_bool *) calloc (mp->lines, sizeof (t_bool));   /* allocate line set tracking array */
3799 if (set == NULL) {                                      /* allocation failed? */
3800     FREE (list);                                        /* free successful list allocation */
3801     return SCPE_MEM;                                    /* report it */
3802     }
3803 
3804 tbuf = (char *) calloc (strlen(carg)+2, sizeof(*carg));
3805 if (tbuf == NULL) {
3806   FREE(set);
3807   FREE(list);
3808   return SCPE_MEM;
3809 }
3810 (void)strcpy (tbuf, carg);
3811 tptr = tbuf + strlen (tbuf);                            /* append a semicolon */
3812 *tptr++ = ';';                                          /*   to the command string */
3813 *tptr = '\0';                                           /*   to make parsing easier for get_range */
3814 cptr = tbuf;
3815 
3816 while (*cptr) {                                         /* parse command string */
3817     cptr = get_range (NULL, cptr, &low, &high, 10, max, ';');/* get a line range */
3818 
3819     if (cptr == NULL) {                                 /* parsing error? */
3820         result = SCPE_ARG;                              /* "Invalid argument" error */
3821         break;
3822         }
3823 
3824     else if ((low > max) || (high > max)) {             /* line out of range? */
3825         result = SCPE_SUB;                              /* "Subscript out of range" error */
3826         break;
3827         }
3828 
3829     else if ((low == 0) && (high == max)) {             /* entire line range specified? */
3830         list [0] = -1;                                  /* set sequential order flag */
3831         idx = (uint32) max + 1;                         /* indicate no fill-in needed */
3832         break;
3833         }
3834 
3835     else
3836         for (line = (uint32) low; line <= (uint32) high; line++) /* see if previously specified */
3837             if (set [line] == FALSE) {                  /* not already specified? */
3838                 set [line] = TRUE;                      /* now it is */
3839                 list [idx] = line;                      /* add line to connection order */
3840                 idx = idx + 1;                          /* bump "specified" count */
3841                 }
3842     }
3843 
3844 if (result == SCPE_OK) {                                /* assignment successful? */
3845     if (idx <= max)                                     /* any lines not specified? */
3846         for (line = 0; line <= max; line++)             /* fill them in sequentially */
3847             if (set [line] == FALSE) {                  /* specified? */
3848                 list [idx] = line;                      /* no, so add it */
3849                 idx = idx + 1;
3850                 }
3851 
3852     memcpy (mp->lnorder, list, mp->lines * sizeof (int32)); /* copy working array to connection array */
3853     }
3854 
3855 FREE (list);                                            /* free list allocation */
3856 FREE (set);                                             /* free set allocation */
3857 FREE (tbuf);                                            /* free arg copy with ; */
3858 
3859 return result;
3860 }
3861 
3862 /* Show line connection order.
3863 
3864    Parameters:
3865     - st   = stream on which output is to be written
3866     - uptr = (not used)
3867     - val  = (not used)
3868     - desc = pointer to multiplexer's TMXR structure
3869 
3870    If a connection order array is not defined in the multiplexer descriptor, the
3871    command is rejected.  If the first value of the connection order array is set
3872    to -1, then the connection order is sequential.  Otherwise, the line values
3873    in the array are printed as a semicolon-separated list.  Ranges are printed
3874    where possible to shorten the output.
3875 */
3876 
3877 t_stat tmxr_show_lnorder (FILE *st, UNIT *uptr, int32 val, CONST void *desc)
     /* [previous][next][first][last][top][bottom][index][help] */
3878 {
3879 int32 i, j, low, last;
3880 const TMXR *mp = (const TMXR *) desc;
3881 int32 *iptr = mp->lnorder;
3882 t_bool first = TRUE;
3883 
3884 if (iptr == NULL)                                       /* connection order undefined? */
3885     return SCPE_NXPAR;                                  /* "Non-existent parameter" error */
3886 
3887 if (*iptr < 0)                                          /* sequential order indicated? */
3888     fprintf (st, "Order=0-%d\n", mp->lines - 1);        /* print full line range */
3889 
3890 else {
3891     low = last = *iptr++;                               /* set first line value */
3892 
3893     for (j = 1; j <= mp->lines; j++) {                  /* print remaining lines in order list */
3894         if (j < mp->lines)                              /* more lines to process? */
3895             i = *iptr++;                                /* get next line in list */
3896         else                                            /* final iteration */
3897             i = -1;                                     /* get "tie-off" value */
3898 
3899         if (i != last + 1) {                            /* end of a range? */
3900             if (first) {                                /* first line to print? */
3901                 fputs ("Order=", st);                   /* print header */
3902                 first = FALSE;
3903                 }
3904 
3905             else                                        /* not first line printed */
3906                 fputc (';', st);                        /* print separator */
3907 
3908             if (low == last)                            /* range null? */
3909                 fprintf (st, "%d", last);               /* print single line value */
3910 
3911             else                                        /* range established */
3912                 fprintf (st, "%d-%d", low, last);       /* print start and end line */
3913 
3914             low = i;                                    /* start new range */
3915             }
3916 
3917         last = i;                                       /* note value for range check */
3918         }
3919     }
3920 
3921 if (first == FALSE)                                     /* sanity check for lines == 0 */
3922     fputc ('\n', st);
3923 
3924 return SCPE_OK;
3925 }
3926 
3927 /* Show summary processor */
3928 
3929 t_stat tmxr_show_summ (FILE *st, UNIT *uptr, int32 val, CONST void *desc)
     /* [previous][next][first][last][top][bottom][index][help] */
3930 {
3931 const TMXR *mp = (const TMXR *) desc;
3932 int32 i, t;
3933 
3934 if (mp == NULL)
3935     return SCPE_IERR;
3936 for (i = t = 0; i < mp->lines; i++)
3937     if ((mp->ldsc[i].sock != 0)
3938     )
3939         t = t + 1;
3940 if (mp->lines > 1)
3941     fprintf (st, "%d current connection%s", t, (t != 1) ? "s" : "");
3942 else
3943     fprintf (st, "%s", (t == 1) ? "connected" : "disconnected");
3944 return SCPE_OK;
3945 }
3946 
3947 /* Show conn/stat processor */
3948 
3949 t_stat tmxr_show_cstat (FILE *st, UNIT *uptr, int32 val, CONST void *desc)
     /* [previous][next][first][last][top][bottom][index][help] */
3950 {
3951 const TMXR *mp = (const TMXR *) desc;
3952 int32 i, any;
3953 
3954 if (mp == NULL)
3955     return SCPE_IERR;
3956 for (i = any = 0; i < mp->lines; i++) {
3957     if ((mp->ldsc[i].sock != 0)
3958       || mp->ldsc[i].modem_control) {
3959         if ((mp->ldsc[i].sock != 0)
3960       )
3961             any++;
3962         if (val)
3963             tmxr_fconns (st, &mp->ldsc[i], i);
3964         else
3965             if ((mp->ldsc[i].sock != 0)
3966           )
3967                 tmxr_fstats (st, &mp->ldsc[i], i);
3968         }
3969     }
3970 if (any == 0)
3971     fprintf (st, (mp->lines == 1? "disconnected\n": "all disconnected\n"));
3972 return SCPE_OK;
3973 }
3974 
3975 /* Show number of lines */
3976 
3977 t_stat tmxr_show_lines (FILE *st, UNIT *uptr, int32 val, CONST void *desc)
     /* [previous][next][first][last][top][bottom][index][help] */
3978 {
3979 const TMXR *mp = (const TMXR *) desc;
3980 
3981 if (mp == NULL)
3982     return SCPE_IERR;
3983 fprintf (st, "lines=%d", mp->lines);
3984 return SCPE_OK;
3985 }

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