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

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