First Commit of my working state
[simh.git] / sim_tmxr.c
CommitLineData
196ba1fc
PH
1/* sim_tmxr.c: Telnet terminal multiplexor library\r
2\r
3 Copyright (c) 2001-2007, Robert M Supnik\r
4\r
5 Permission is hereby granted, free of charge, to any person obtaining a\r
6 copy of this software and associated documentation files (the "Software"),\r
7 to deal in the Software without restriction, including without limitation\r
8 the rights to use, copy, modify, merge, publish, distribute, sublicense,\r
9 and/or sell copies of the Software, and to permit persons to whom the\r
10 Software is furnished to do so, subject to the following conditions:\r
11\r
12 The above copyright notice and this permission notice shall be included in\r
13 all copies or substantial portions of the Software.\r
14\r
15 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\r
16 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\r
17 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL\r
18 ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER\r
19 IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\r
20 CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\r
21\r
22 Except as contained in this notice, the name of Robert M Supnik shall not be\r
23 used in advertising or otherwise to promote the sale, use or other dealings\r
24 in this Software without prior written authorization from Robert M Supnik.\r
25\r
26 Based on the original DZ11 simulator by Thord Nilson, as updated by\r
27 Arthur Krewat.\r
28\r
29 11-Apr-07 JDB Worked around Telnet negotiation problem with QCTerm\r
30 16-Aug-05 RMS Fixed C++ declaration and cast problems\r
31 29-Jun-05 RMS Extended tmxr_dscln to support unit array devices\r
32 Fixed bug in SET LOG/NOLOG\r
33 04-Jan-04 RMS Changed TMXR ldsc to be pointer to linedesc array\r
34 Added tmxr_linemsg, circular output pointers, logging\r
35 (from Mark Pizzolato)\r
36 29-Dec-03 RMS Added output stall support\r
37 01-Nov-03 RMS Cleaned up attach routine\r
38 09-Mar-03 RMS Fixed bug in SHOW CONN\r
39 22-Dec-02 RMS Fixed bugs in IAC+IAC receive and transmit sequences\r
40 Added support for received break (all from by Mark Pizzolato)\r
41 Fixed bug in attach\r
42 31-Oct-02 RMS Fixed bug in 8b (binary) support\r
43 22-Aug-02 RMS Added tmxr_open_master, tmxr_close_master\r
44 30-Dec-01 RMS Added tmxr_fstats, tmxr_dscln, renamed tmxr_fstatus\r
45 03-Dec-01 RMS Changed tmxr_fconns for extended SET/SHOW\r
46 20-Oct-01 RMS Fixed bugs in read logic (found by Thord Nilson).\r
47 Added tmxr_rqln, tmxr_tqln\r
48\r
49 This library includes:\r
50\r
51 tmxr_poll_conn - poll for connection\r
52 tmxr_reset_ln - reset line\r
53 tmxr_getc_ln - get character for line\r
54 tmxr_poll_rx - poll receive\r
55 tmxr_putc_ln - put character for line\r
56 tmxr_poll_tx - poll transmit\r
57 tmxr_open_master - open master connection\r
58 tmxr_close_master - close master connection\r
59 tmxr_attach - attach terminal multiplexor\r
60 tmxr_detach - detach terminal multiplexor\r
61 tmxr_ex - (null) examine\r
62 tmxr_dep - (null) deposit\r
63 tmxr_msg - send message to socket\r
64 tmxr_linemsg - send message to line\r
65 tmxr_fconns - output connection status\r
66 tmxr_fstats - output connection statistics\r
67 tmxr_dscln - disconnect line (SET routine)\r
68 tmxr_rqln - number of available characters for line\r
69 tmxr_tqln - number of buffered characters for line\r
70\r
71 All routines are OS-independent.\r
72*/\r
73\r
74#include "sim_defs.h"\r
75#include "sim_sock.h"\r
76#include "sim_tmxr.h"\r
77#include <ctype.h>\r
78\r
79/* Telnet protocol constants - negatives are for init'ing signed char data */\r
80\r
81#define TN_IAC -1 /* protocol delim */\r
82#define TN_DONT -2 /* dont */\r
83#define TN_DO -3 /* do */\r
84#define TN_WONT -4 /* wont */\r
85#define TN_WILL -5 /* will */\r
86#define TN_BRK -13 /* break */\r
87#define TN_BIN 0 /* bin */\r
88#define TN_ECHO 1 /* echo */\r
89#define TN_SGA 3 /* sga */\r
90#define TN_LINE 34 /* line mode */\r
91#define TN_CR 015 /* carriage return */\r
92#define TN_LF 012 /* line feed */\r
93#define TN_NUL 000 /* null */\r
94\r
95/* Telnet line states */\r
96\r
97#define TNS_NORM 000 /* normal */\r
98#define TNS_IAC 001 /* IAC seen */\r
99#define TNS_WILL 002 /* WILL seen */\r
100#define TNS_WONT 003 /* WONT seen */\r
101#define TNS_SKIP 004 /* skip next cmd */\r
102#define TNS_CRPAD 005 /* CR padding */\r
103\r
104void tmxr_rmvrc (TMLN *lp, int32 p);\r
105int32 tmxr_send_buffered_data (TMLN *lp);\r
106TMLN *tmxr_find_ldsc (UNIT *uptr, int32 val, TMXR *mp);\r
107\r
108extern int32 sim_switches;\r
109extern char sim_name[];\r
110extern FILE *sim_log;\r
111extern uint32 sim_os_msec (void);\r
112\r
113/* Poll for new connection\r
114\r
115 Called from unit service routine to test for new connection\r
116\r
117 Inputs:\r
118 *mp = pointer to terminal multiplexor descriptor\r
119 Outputs:\r
120 line number activated, -1 if none\r
121*/\r
122\r
123int32 tmxr_poll_conn (TMXR *mp)\r
124{\r
125SOCKET newsock;\r
126TMLN *lp;\r
127int32 i;\r
128uint32 ipaddr;\r
129static char mantra[] = {\r
130 TN_IAC, TN_WILL, TN_LINE,\r
131 TN_IAC, TN_WILL, TN_SGA,\r
132 TN_IAC, TN_WILL, TN_ECHO,\r
133 TN_IAC, TN_WILL, TN_BIN,\r
134 TN_IAC, TN_DO, TN_BIN\r
135 };\r
136\r
137newsock = sim_accept_conn (mp->master, &ipaddr); /* poll connect */\r
138if (newsock != INVALID_SOCKET) { /* got a live one? */\r
139 for (i = 0; i < mp->lines; i++) { /* find avail line */\r
140 lp = mp->ldsc + i; /* ptr to ln desc */\r
141 if (lp->conn == 0) break; /* available? */\r
142 }\r
143 if (i >= mp->lines) { /* all busy? */\r
144 tmxr_msg (newsock, "All connections busy\r\n");\r
145 sim_close_sock (newsock, 0);\r
146 }\r
147 else {\r
148 lp = mp->ldsc + i; /* get line desc */\r
149 lp->conn = newsock; /* record connection */\r
150 lp->ipad = ipaddr; /* ip address */\r
151 lp->cnms = sim_os_msec (); /* time of conn */\r
152 lp->rxbpr = lp->rxbpi = 0; /* init buf pointers */\r
153 lp->txbpr = lp->txbpi = 0;\r
154 lp->rxcnt = lp->txcnt = 0; /* init counters */\r
155 lp->tsta = 0; /* init telnet state */\r
156 lp->xmte = 1; /* enable transmit */\r
157 lp->dstb = 0; /* default bin mode */\r
158 sim_write_sock (newsock, mantra, 15);\r
159 tmxr_linemsg (lp, "\n\r\nConnected to the ");\r
160 tmxr_linemsg (lp, sim_name);\r
161 tmxr_linemsg (lp, " simulator\r\n\n");\r
162 tmxr_poll_tx (mp); /* flush output */\r
163 return i;\r
164 }\r
165 } /* end if newsock */\r
166return -1;\r
167}\r
168\r
169/* Reset line */\r
170\r
171void tmxr_reset_ln (TMLN *lp)\r
172{\r
173if (lp->txlog) fflush (lp->txlog); /* dump log */\r
174tmxr_send_buffered_data (lp); /* send buffered data */\r
175sim_close_sock (lp->conn, 0); /* reset conn */\r
176lp->conn = lp->tsta = 0; /* reset state */\r
177lp->rxbpr = lp->rxbpi = 0;\r
178lp->txbpr = lp->txbpi = 0;\r
179lp->xmte = 1;\r
180lp->dstb = 0;\r
181return;\r
182}\r
183\r
184/* Get character from specific line\r
185\r
186 Inputs:\r
187 *lp = pointer to terminal line descriptor\r
188 Output:\r
189 valid + char, 0 if line\r
190*/\r
191\r
192int32 tmxr_getc_ln (TMLN *lp)\r
193{\r
194int32 j, val = 0;\r
195uint32 tmp;\r
196\r
197if (lp->conn && lp->rcve) { /* conn & enb? */\r
198 j = lp->rxbpi - lp->rxbpr; /* # input chrs */\r
199 if (j) { /* any? */\r
200 tmp = lp->rxb[lp->rxbpr]; /* get char */\r
201 val = TMXR_VALID | (tmp & 0377); /* valid + chr */\r
202 if (lp->rbr[lp->rxbpr]) val = val | SCPE_BREAK; /* break? */\r
203 lp->rxbpr = lp->rxbpr + 1; /* adv pointer */\r
204 }\r
205 } /* end if conn */\r
206if (lp->rxbpi == lp->rxbpr) /* empty? zero ptrs */\r
207 lp->rxbpi = lp->rxbpr = 0;\r
208return val;\r
209}\r
210\r
211/* Poll for input\r
212\r
213 Inputs:\r
214 *mp = pointer to terminal multiplexor descriptor\r
215 Outputs: none\r
216*/\r
217\r
218void tmxr_poll_rx (TMXR *mp)\r
219{\r
220int32 i, nbytes, j;\r
221TMLN *lp;\r
222\r
223for (i = 0; i < mp->lines; i++) { /* loop thru lines */\r
224 lp = mp->ldsc + i; /* get line desc */\r
225 if (!lp->conn || !lp->rcve) continue; /* skip if !conn */\r
226\r
227 nbytes = 0;\r
228 if (lp->rxbpi == 0) /* need input? */\r
229 nbytes = sim_read_sock (lp->conn, /* yes, read */\r
230 &(lp->rxb[lp->rxbpi]), /* leave spc for */\r
231 TMXR_MAXBUF - TMXR_GUARD); /* Telnet cruft */\r
232 else if (lp->tsta) /* in Telnet seq? */\r
233 nbytes = sim_read_sock (lp->conn, /* yes, read to end */\r
234 &(lp->rxb[lp->rxbpi]),\r
235 TMXR_MAXBUF - lp->rxbpi);\r
236 if (nbytes < 0) tmxr_reset_ln (lp); /* closed? reset ln */\r
237 else if (nbytes > 0) { /* if data rcvd */\r
238 j = lp->rxbpi; /* start of data */\r
239 memset (&lp->rbr[j], 0, nbytes); /* clear status */\r
240 lp->rxbpi = lp->rxbpi + nbytes; /* adv pointers */\r
241 lp->rxcnt = lp->rxcnt + nbytes;\r
242\r
243/* Examine new data, remove TELNET cruft before making input available */\r
244\r
245 for (; j < lp->rxbpi; ) { /* loop thru char */\r
246 signed char tmp = lp->rxb[j]; /* get char */\r
247 switch (lp->tsta) { /* case tlnt state */\r
248\r
249 case TNS_NORM: /* normal */\r
250 if (tmp == TN_IAC) { /* IAC? */\r
251 lp->tsta = TNS_IAC; /* change state */\r
252 tmxr_rmvrc (lp, j); /* remove char */\r
253 break;\r
254 }\r
255 if ((tmp == TN_CR) && lp->dstb) /* CR, no bin */\r
256 lp->tsta = TNS_CRPAD; /* skip pad char */\r
257 j = j + 1; /* advance j */\r
258 break;\r
259\r
260 case TNS_IAC: /* IAC prev */\r
261 if ((tmp == TN_IAC) & !lp->dstb) { /* IAC + IAC, bin? */\r
262 lp->tsta = TNS_NORM; /* treat as normal */\r
263 j = j + 1; /* advance j */\r
264 break; /* keep IAC */\r
265 }\r
266 if (tmp == TN_BRK) { /* IAC + BRK? */\r
267 lp->tsta = TNS_NORM; /* treat as normal */\r
268 lp->rxb[j] = 0; /* char is null */\r
269 lp->rbr[j] = 1; /* flag break */\r
270 j = j + 1; /* advance j */\r
271 break;\r
272 }\r
273 if (tmp == TN_WILL) /* IAC + WILL? */\r
274 lp->tsta = TNS_WILL;\r
275 else if (tmp == TN_WONT) /* IAC + WONT? */\r
276 lp->tsta = TNS_WONT;\r
277 else lp->tsta = TNS_SKIP; /* IAC + other */\r
278 tmxr_rmvrc (lp, j); /* remove char */\r
279 break;\r
280\r
281 case TNS_WILL: case TNS_WONT: /* IAC+WILL/WONT prev */\r
282 if (tmp == TN_BIN) { /* BIN? */\r
283 if (lp->tsta == TNS_WILL) lp->dstb = 0;\r
284 else lp->dstb = 1;\r
285 }\r
286\r
287 /* Negotiation with the HP terminal emulator "QCTerm" is not working.\r
288 QCTerm says "WONT BIN" but sends bare CRs. RFC 854 says:\r
289\r
290 Note that "CR LF" or "CR NUL" is required in both directions\r
291 (in the default ASCII mode), to preserve the symmetry of the\r
292 NVT model. ...The protocol requires that a NUL be inserted\r
293 following a CR not followed by a LF in the data stream.\r
294\r
295 Until full negotiation is implemented, we work around the problem\r
296 by checking the character following the CR in non-BIN mode and\r
297 strip it only if it is LF or NUL. This should not affect\r
298 conforming clients.\r
299 */\r
300 \r
301 case TNS_CRPAD: /* only LF or NUL should follow CR */\r
302 lp->tsta = TNS_NORM; /* next normal */\r
303 if ((tmp == TN_LF) || /* CR + LF ? */\r
304 (tmp == TN_NUL)) /* CR + NUL? */\r
305 tmxr_rmvrc (lp, j); /* remove it */\r
306 break;\r
307\r
308 case TNS_SKIP: default: /* skip char */\r
309 lp->tsta = TNS_NORM; /* next normal */\r
310 tmxr_rmvrc (lp, j); /* remove char */\r
311 break;\r
312 } /* end case state */\r
313 } /* end for char */\r
314 } /* end else nbytes */\r
315 } /* end for lines */\r
316for (i = 0; i < mp->lines; i++) { /* loop thru lines */\r
317 lp = mp->ldsc + i; /* get line desc */\r
318 if (lp->rxbpi == lp->rxbpr) /* if buf empty, */\r
319 lp->rxbpi = lp->rxbpr = 0; /* reset pointers */\r
320 } /* end for */\r
321return;\r
322}\r
323\r
324/* Return count of available characters for line */\r
325\r
326int32 tmxr_rqln (TMLN *lp)\r
327{\r
328return (lp->rxbpi - lp->rxbpr);\r
329}\r
330\r
331/* Remove character p (and matching status) from line l input buffer */\r
332\r
333void tmxr_rmvrc (TMLN *lp, int32 p)\r
334{\r
335for ( ; p < lp->rxbpi; p++) {\r
336 lp->rxb[p] = lp->rxb[p + 1];\r
337 lp->rbr[p] = lp->rbr[p + 1];\r
338 }\r
339lp->rxbpi = lp->rxbpi - 1;\r
340return;\r
341}\r
342\r
343/* Store character in line buffer\r
344\r
345 Inputs:\r
346 *lp = pointer to line descriptor\r
347 chr = characters\r
348 Outputs:\r
349 status = ok, connection lost, or stall\r
350*/\r
351\r
352t_stat tmxr_putc_ln (TMLN *lp, int32 chr)\r
353{\r
354if (lp->txlog) fputc (chr, lp->txlog); /* log if available */\r
355if (lp->conn == 0) return SCPE_LOST; /* no conn? lost */\r
356if (tmxr_tqln (lp) < (TMXR_MAXBUF - 1)) { /* room for char (+ IAC)? */\r
357 lp->txb[lp->txbpi] = (char) chr; /* buffer char */\r
358 lp->txbpi = lp->txbpi + 1; /* adv pointer */\r
359 if (lp->txbpi >= TMXR_MAXBUF) lp->txbpi = 0; /* wrap? */\r
360 if ((char) chr == TN_IAC) { /* IAC? */\r
361 lp->txb[lp->txbpi] = (char) chr; /* IAC + IAC */\r
362 lp->txbpi = lp->txbpi + 1; /* adv pointer */ \r
363 if (lp->txbpi >= TMXR_MAXBUF) lp->txbpi = 0; /* wrap? */\r
364 }\r
365 if (tmxr_tqln (lp) > (TMXR_MAXBUF - TMXR_GUARD)) /* near full? */\r
366 lp->xmte = 0; /* disable line */\r
367 return SCPE_OK; /* char sent */\r
368 }\r
369lp->xmte = 0; /* no room, dsbl line */\r
370return SCPE_STALL; /* char not sent */\r
371}\r
372\r
373/* Poll for output\r
374\r
375 Inputs:\r
376 *mp = pointer to terminal multiplexor descriptor\r
377 Outputs:\r
378 none\r
379*/\r
380\r
381void tmxr_poll_tx (TMXR *mp)\r
382{\r
383int32 i, nbytes;\r
384TMLN *lp;\r
385\r
386for (i = 0; i < mp->lines; i++) { /* loop thru lines */\r
387 lp = mp->ldsc + i; /* get line desc */\r
388 if (lp->conn == 0) continue; /* skip if !conn */\r
389 nbytes = tmxr_send_buffered_data (lp); /* buffered bytes */\r
390 if (nbytes == 0) lp->xmte = 1; /* buf empty? enab line */\r
391 } /* end for */\r
392return;\r
393}\r
394\r
395/* Send buffered data across network\r
396\r
397 Inputs:\r
398 *lp = pointer to line descriptor\r
399 Outputs:\r
400 returns number of bytes still buffered\r
401*/\r
402\r
403int32 tmxr_send_buffered_data (TMLN *lp)\r
404{\r
405int32 nbytes, sbytes;\r
406\r
407nbytes = tmxr_tqln(lp); /* avail bytes */\r
408if (nbytes) { /* >0? write */\r
409 if (lp->txbpr < lp->txbpi) /* no wrap? */\r
410 sbytes = sim_write_sock (lp->conn, /* write all data */\r
411 &(lp->txb[lp->txbpr]), nbytes);\r
412 else sbytes = sim_write_sock (lp->conn, /* write to end buf */\r
413 &(lp->txb[lp->txbpr]), TMXR_MAXBUF - lp->txbpr);\r
414 if (sbytes != SOCKET_ERROR) { /* ok? */\r
415 lp->txbpr = (lp->txbpr + sbytes); /* update remove ptr */\r
416 if (lp->txbpr >= TMXR_MAXBUF) lp->txbpr = 0; /* wrap? */\r
417 lp->txcnt = lp->txcnt + sbytes; /* update counts */\r
418 nbytes = nbytes - sbytes;\r
419 }\r
420 if (nbytes && (lp->txbpr == 0)) { /* more data and wrap? */\r
421 sbytes = sim_write_sock (lp->conn, lp->txb, nbytes);\r
422 if (sbytes != SOCKET_ERROR) { /* ok */\r
423 lp->txbpr = (lp->txbpr + sbytes); /* update remove ptr */\r
424 if (lp->txbpr >= TMXR_MAXBUF) lp->txbpr = 0;/* wrap? */\r
425 lp->txcnt = lp->txcnt + sbytes; /* update counts */\r
426 nbytes = nbytes - sbytes;\r
427 }\r
428 }\r
429 } /* end if nbytes */\r
430return nbytes;\r
431}\r
432\r
433/* Return count of buffered characters for line */\r
434\r
435int32 tmxr_tqln (TMLN *lp)\r
436{\r
437return (lp->txbpi - lp->txbpr + ((lp->txbpi < lp->txbpr)? TMXR_MAXBUF: 0));\r
438}\r
439\r
440/* Open master socket */\r
441\r
442t_stat tmxr_open_master (TMXR *mp, char *cptr)\r
443{\r
444int32 i, port;\r
445SOCKET sock;\r
446TMLN *lp;\r
447t_stat r;\r
448\r
449port = (int32) get_uint (cptr, 10, 65535, &r); /* get port */\r
450if ((r != SCPE_OK) || (port == 0)) return SCPE_ARG;\r
451sock = sim_master_sock (port); /* make master socket */\r
452if (sock == INVALID_SOCKET) return SCPE_OPENERR; /* open error */\r
453printf ("Listening on port %d (socket %d)\n", port, sock);\r
454if (sim_log) fprintf (sim_log,\r
455 "Listening on port %d (socket %d)\n", port, sock);\r
456mp->port = port; /* save port */\r
457mp->master = sock; /* save master socket */\r
458for (i = 0; i < mp->lines; i++) { /* initialize lines */\r
459 lp = mp->ldsc + i;\r
460 lp->conn = lp->tsta = 0;\r
461 lp->rxbpi = lp->rxbpr = 0;\r
462 lp->txbpi = lp->txbpr = 0;\r
463 lp->rxcnt = lp->txcnt = 0;\r
464 lp->xmte = 1;\r
465 lp->dstb = 0;\r
466 }\r
467return SCPE_OK;\r
468}\r
469\r
470/* Attach unit to master socket */\r
471\r
472t_stat tmxr_attach (TMXR *mp, UNIT *uptr, char *cptr)\r
473{\r
474char* tptr;\r
475t_stat r;\r
476\r
477tptr = (char *) malloc (strlen (cptr) + 1); /* get string buf */\r
478if (tptr == NULL) return SCPE_MEM; /* no more mem? */\r
479r = tmxr_open_master (mp, cptr); /* open master socket */\r
480if (r != SCPE_OK) { /* error? */\r
481 free (tptr); /* release buf */\r
482 return SCPE_OPENERR;\r
483 }\r
484strcpy (tptr, cptr); /* copy port */\r
485uptr->filename = tptr; /* save */\r
486uptr->flags = uptr->flags | UNIT_ATT; /* no more errors */\r
487return SCPE_OK;\r
488}\r
489\r
490/* Close master socket */\r
491\r
492t_stat tmxr_close_master (TMXR *mp)\r
493{\r
494int32 i;\r
495TMLN *lp;\r
496\r
497for (i = 0; i < mp->lines; i++) { /* loop thru conn */\r
498 lp = mp->ldsc + i;\r
499 if (lp->conn) {\r
500 tmxr_linemsg (lp, "\r\nDisconnected from the ");\r
501 tmxr_linemsg (lp, sim_name);\r
502 tmxr_linemsg (lp, " simulator\r\n\n");\r
503 tmxr_reset_ln (lp);\r
504 } /* end if conn */\r
505 } /* end for */\r
506sim_close_sock (mp->master, 1); /* close master socket */\r
507mp->master = 0;\r
508return SCPE_OK;\r
509}\r
510\r
511/* Detach unit from master socket */\r
512\r
513t_stat tmxr_detach (TMXR *mp, UNIT *uptr)\r
514{\r
515if (!(uptr->flags & UNIT_ATT)) return SCPE_OK; /* attached? */\r
516tmxr_close_master (mp); /* close master socket */\r
517free (uptr->filename); /* free port string */\r
518uptr->filename = NULL;\r
519uptr->flags = uptr->flags & ~UNIT_ATT; /* not attached */\r
520return SCPE_OK;\r
521}\r
522\r
523/* Stub examine and deposit */\r
524\r
525t_stat tmxr_ex (t_value *vptr, t_addr addr, UNIT *uptr, int32 sw)\r
526{\r
527return SCPE_NOFNC;\r
528}\r
529\r
530t_stat tmxr_dep (t_value val, t_addr addr, UNIT *uptr, int32 sw)\r
531{\r
532return SCPE_NOFNC;\r
533}\r
534\r
535/* Output message to socket or line descriptor */\r
536\r
537void tmxr_msg (SOCKET sock, char *msg)\r
538{\r
539if (sock) sim_write_sock (sock, msg, strlen (msg));\r
540return;\r
541}\r
542\r
543void tmxr_linemsg (TMLN *lp, char *msg)\r
544{\r
545int32 len;\r
546\r
547for (len = strlen (msg); len > 0; --len)\r
548 tmxr_putc_ln (lp, *msg++);\r
549return;\r
550}\r
551\r
552/* Print connections - used only in named SHOW command */\r
553\r
554void tmxr_fconns (FILE *st, TMLN *lp, int32 ln)\r
555{\r
556if (ln >= 0) fprintf (st, "line %d: ", ln);\r
557if (lp->conn) {\r
558 int32 o1, o2, o3, o4, hr, mn, sc;\r
559 uint32 ctime;\r
560\r
561 o1 = (lp->ipad >> 24) & 0xFF;\r
562 o2 = (lp->ipad >> 16) & 0xFF;\r
563 o3 = (lp->ipad >> 8) & 0xFF;\r
564 o4 = (lp->ipad) & 0xFF;\r
565 ctime = (sim_os_msec () - lp->cnms) / 1000;\r
566 hr = ctime / 3600;\r
567 mn = (ctime / 60) % 60;\r
568 sc = ctime % 60;\r
569 fprintf (st, "IP address %d.%d.%d.%d", o1, o2, o3, o4);\r
570 if (ctime) fprintf (st, ", connected %02d:%02d:%02d\n", hr, mn, sc);\r
571 }\r
572else fprintf (st, "line disconnected\n");\r
573if (lp->txlog) fprintf (st, "Logging to %s\n", lp->txlogname);\r
574return;\r
575}\r
576\r
577/* Print statistics - used only in named SHOW command */\r
578\r
579void tmxr_fstats (FILE *st, TMLN *lp, int32 ln)\r
580{\r
581static const char *enab = "on";\r
582static const char *dsab = "off";\r
583\r
584if (ln >= 0) fprintf (st, "line %d: ", ln);\r
585if (lp->conn) {\r
586 fprintf (st, "input (%s) queued/total = %d/%d, ",\r
587 (lp->rcve? enab: dsab),\r
588 lp->rxbpi - lp->rxbpr, lp->rxcnt);\r
589 fprintf (st, "output (%s) queued/total = %d/%d\n",\r
590 (lp->xmte? enab: dsab),\r
591 lp->txbpi - lp->txbpr, lp->txcnt);\r
592 }\r
593else fprintf (st, "line disconnected\n");\r
594return;\r
595}\r
596\r
597/* Disconnect line */\r
598\r
599t_stat tmxr_dscln (UNIT *uptr, int32 val, char *cptr, void *desc)\r
600{\r
601TMXR *mp = (TMXR *) desc;\r
602TMLN *lp;\r
603int32 ln;\r
604t_stat r;\r
605\r
606if (mp == NULL) return SCPE_IERR;\r
607if (val) { /* = n form */\r
608 if (cptr == NULL) return SCPE_ARG;\r
609 ln = (int32) get_uint (cptr, 10, mp->lines - 1, &r);\r
610 if (r != SCPE_OK) return SCPE_ARG;\r
611 lp = mp->ldsc + ln;\r
612 }\r
613else {\r
614 lp = tmxr_find_ldsc (uptr, 0, mp);\r
615 if (lp == NULL) return SCPE_IERR;\r
616 }\r
617if (lp->conn) {\r
618 tmxr_linemsg (lp, "\r\nOperator disconnected line\r\n\n");\r
619 tmxr_reset_ln (lp);\r
620 }\r
621return SCPE_OK;\r
622}\r
623\r
624/* Enable logging for line */\r
625\r
626t_stat tmxr_set_log (UNIT *uptr, int32 val, char *cptr, void *desc)\r
627{\r
628TMXR *mp = (TMXR *) desc;\r
629TMLN *lp;\r
630\r
631if (cptr == NULL) return SCPE_2FARG; /* no file name? */\r
632lp = tmxr_find_ldsc (uptr, val, mp); /* find line desc */\r
633if (lp == NULL) return SCPE_IERR;\r
634if (lp->txlog) tmxr_set_nolog (NULL, val, NULL, desc); /* close existing log */\r
635lp->txlogname = (char *) calloc (CBUFSIZE, sizeof (char)); /* alloc namebuf */\r
636if (lp->txlogname == NULL) return SCPE_MEM; /* can't? */\r
637strncpy (lp->txlogname, cptr, CBUFSIZE); /* save file name */\r
638lp->txlog = fopen (cptr, "ab"); /* open log */\r
639if (lp->txlog == NULL) { /* error? */\r
640 free (lp->txlogname); /* free buffer */\r
641 return SCPE_OPENERR;\r
642 }\r
643return SCPE_OK;\r
644}\r
645\r
646/* Disable logging for line */\r
647\r
648t_stat tmxr_set_nolog (UNIT *uptr, int32 val, char *cptr, void *desc)\r
649{\r
650TMXR *mp = (TMXR *) desc;\r
651TMLN *lp;\r
652\r
653if (cptr) return SCPE_2MARG; /* no arguments */\r
654lp = tmxr_find_ldsc (uptr, val, mp); /* find line desc */\r
655if (lp == NULL) return SCPE_IERR;\r
656if (lp->txlog) { /* logging? */\r
657 fclose (lp->txlog); /* close log */\r
658 free (lp->txlogname); /* free namebuf */\r
659 lp->txlog = NULL;\r
660 lp->txlogname = NULL;\r
661 }\r
662return SCPE_OK;\r
663}\r
664\r
665/* Show logging status for line */\r
666\r
667t_stat tmxr_show_log (FILE *st, UNIT *uptr, int32 val, void *desc)\r
668{\r
669TMXR *mp = (TMXR *) desc;\r
670TMLN *lp;\r
671\r
672lp = tmxr_find_ldsc (uptr, val, mp); /* find line desc */\r
673if (lp == NULL) return SCPE_IERR;\r
674if (lp->txlog) fprintf (st, "logging to %s", lp->txlogname);\r
675else fprintf (st, "no logging");\r
676return SCPE_OK;\r
677}\r
678\r
679/* Find line descriptor */\r
680\r
681TMLN *tmxr_find_ldsc (UNIT *uptr, int32 val, TMXR *mp)\r
682{\r
683if (uptr) { /* called from SET? */\r
684 DEVICE *dptr = find_dev_from_unit (uptr); /* find device */\r
685 if (dptr == NULL) return NULL; /* what?? */\r
686 val = (int32) (uptr - dptr->units); /* implicit line # */\r
687 }\r
688if ((val < 0) || (val >= mp->lines)) return NULL; /* invalid line? */\r
689return mp->ldsc + val; /* line descriptor */\r
690}\r