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