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