BSD 4_3_Net_2 development
[unix-history] / usr / src / contrib / usr.x25 / nimd / nim.c
CommitLineData
d6fe8fca
C
1/*
2 * Network Interface Machine Server
3 *
4 * Frank Pronk
5 * Copyright (c) 1984
6 */
7
8#include <sys/types.h>
9#include <sys/socket.h>
10#include <sys/time.h>
11
12#include <signal.h>
13#include <errno.h>
14#include <setjmp.h>
15#include <sgtty.h>
16#include <pwd.h>
17
18#include "../h/x29.h"
19
20#include "buf.h"
21#include "nim.h"
22
23extern char chartab[128];
24
25jmp_buf JmpBuf; /* non-local goto buffer for interval timer */
26short PtyFd = -1, NetFd = -1;
27short LogFd = -1;
28char *LogDev;
29short TimerOn; /* interval timer armed */
30char Banner[] = "NIM daemon\r";
31extern int errno;
32char *TraceFile; /* trace file name */
33char *PtyName;
34struct bufhd ptyoqueue, netoqueue;
35struct buf *packet;
36char user_name[50];
37
38struct netinfo NetInfo = { CCITT1980, NX29_1980_PARMS, 128 };
39
40struct PtyPacket {
41 char p_type;
42 char p_data[1]; /* usually more than one byte */
43};
44
45char *x25err();
46
47main(argc, argv)
48register char **argv;
49{
50 register int s;
51 register char *p;
52 int on = 1, uid;
53 struct passwd *pw;
54
55#ifdef waterloo
56 /*
57 * If this host doesn't support X.25, give up.
58 */
59 s = socket(AF_CCITT, SOCK_STREAM, 0);
60 if (s < 0 && errno == EPROTONOSUPPORT) {
61 fprint(2, "nimd: X.25 is not supported on this machine\n");
62 exit(1);
63 }
64 close(s);
65#endif
66
67 for (argv++; argc > 1; argc--, argv++)
68 if (**argv == '-')
69 for (p = *argv+1; *p; p++)
70 switch (*p) {
71 case 'c':
72 if (argc > 1) {
73 argc--; argv++;
74 if (strcmp (*argv, "1978") == 0) {
75 NetInfo.n_nparms = NX29_1978_PARMS;
76 break;
77 } else if (strcmp (*argv, "1980") == 0) {
78 NetInfo.n_nparms = NX29_1980_PARMS;
79 break;
80 }
81 }
82 fatal ("1978 or 1980 expected after -c");
83 break;
84
85 case 't':
86 if (argc > 1) {
87 argc--; argv++;
88 TraceFile = *argv;
89 } else
90 fatal ("trace file name expected");
91 break;
92
93 default:
94 fatal("usage: nimd [-c ccitt-date] [-h size] [-n size] [-t trace_file] pty_name");
95 }
96 else
97 PtyName = *argv;
98
99 if (PtyName == 0)
100 fatal ("No pseudo-tty specified");
101
102 if (fork())
103 exit(0);
104 for (s = 0; s < 10; s++)
105 (void) close(s);
106 (void) open("/", 0);
107 (void) dup2(0, 1);
108 (void) dup2(0, 2);
109 { int tt = open("/dev/tty", 2);
110 if (tt > 0) {
111 ioctl(tt, TIOCNOTTY, (char *)0);
112 close(tt);
113 }
114 }
115
116 OpenLog ();
117 packet = getbuf (MAXPSIZ);
118 ResetBufs ();
119 signal (SIGPIPE, SIG_IGN);
120
121 for (;;) {
122 int bits;
123
124 if ((PtyFd = open (PtyName, 2)) < 0)
125 fatal (x25err(PtyName));
126 ioctl (PtyFd, FIONBIO, (char *)&on);
127 ioctl (PtyFd, TIOCPKT, (char *)&on);
128 ioctl (PtyFd, TIOCREMOTE, (char *)&on);
129 bits = (1 << PtyFd);
130 select (16, (int *)0, &bits, (int *)0, (struct timeval *)0);
131 sleep (1); /* wait for pty to settle down */
132#ifdef TIOCGSUID
133 /*
134 * Get the slave's uid.
135 */
136 uid = -1;
137 if (ioctl(PtyFd, TIOCGSUID, &uid) < 0) {
138 perror("nimd: ioctl TIOCGSUID");
139 close(PtyFd);
140 continue;
141 }
142 if (uid < 0 || (pw = getpwuid(uid)) == 0) {
143 message("nimd: uid %d: Who are you?\n", uid);
144 close(PtyFd);
145 continue;
146 }
147 strcpy(user_name, pw->pw_name);
148 /*
149 * Set effective uid to the slave
150 * so he/she will be charged for X.25 usage.
151 */
152 setreuid(0, pw->pw_uid);
153#endif
154 nim ();
155 sleep (1); /* wait for slave to disconnect */
156 setreuid(0, 0);
157 CloseLog(); OpenLog(); /* allow log rollover */
158 }
159 /*NOTREACHED*/
160}
161
162fatal (msg)
163char *msg;
164{
165 OpenLog ();
166 log ("%s", msg);
167 print ("nimd: %s\n", msg);
168 exit (1);
169}
170
171/*VARARGS*/
172message(fmt, a1, a2, a3, a4, a5)
173char *fmt;
174{
175 char buf[128];
176
177 sprint (buf, fmt, a1, a2, a3, a4, a5);
178 ToPty (buf, strlen (buf), FROMNIM);
179}
180
181error()
182{
183 register char *errp;
184
185 errp = x25err ((char *)0);
186 log (errp);
187 message ("nimd: %s\r", errp);
188}
189
190
191/*
192 * Main loop. Select from pty and network, and
193 * hand data to nim receiver.
194 */
195
196nim()
197{
198 register int len;
199 char buf[MAXPSIZ];
200 int on = 1, ibits, obits, first = 1;
201
202 InitProfile (DEFAULT_PROFILE);
203 message (Banner);
204 State = ST_COMMAND;
205 OpenLog ();
206 if (user_name[0])
207 log("slave connect: %s", user_name);
208 else
209 log ("slave connect");
210
211 for (;;) {
212 (void) setjmp (JmpBuf);
213 if (first) {
214 obits = (1 << PtyFd);
215 ibits = 0;
216 first = 0;
217 goto do_io;
218 }
219 ibits = obits = 0;
220
221 /*
222 * Never look for input if there's still
223 * stuff in the corresponding output buffer
224 */
225
226 if (PtyFd >= 0)
227 if (!QEMPTY(&ptyoqueue))
228 obits |= (1 << PtyFd);
229 else
230 if (NetFd >= 0 && State == ST_DATA)
231 ibits |= (1 << NetFd);
232
233 if (!QEMPTY(&netoqueue) && NetFd >= 0) {
234 if (!OutputBlocked)
235 obits |= (1 << NetFd);
236 } else
237 if (PtyFd >= 0)
238 ibits |= (1 << PtyFd);
239
240 if (ibits == 0 && obits == 0) /* nothing to do; go home */
241 break;
242
243 if (State & ST_COMMAND) {
244 struct timeval TimeOut;
245
246 TimeOut.tv_sec = 60;
247 TimeOut.tv_usec = 0;
248 if (select (16, &ibits, &obits, (int *)0, &TimeOut) <= 0) {
249 log ("slave inactivity timeout");
250 break;
251 }
252 } else {
253 if (TimerOn)
254 sigsetmask (0);
255 (void) select (16, &ibits, &obits, (int *)0, (struct timeval *)0);
256 if (TimerOn)
257 sigsetmask (1 << (SIGALRM -1 ));
258 }
259
260 do_io:
261 /*
262 * Something to read from the network...
263 */
264 if (ibits & (1 << NetFd))
265 if ((len = ReadAndTrace(NetFd, buf, MAXPSIZ, "net rx")) == 0) {
266 if (errno != EWOULDBLOCK)
267 NetworkError ();
268 } else
269 FromNet (buf, len);
270
271 /*
272 * Something to read from the pty...
273 */
274 if (ibits & (1 << PtyFd))
275 if ((len = ReadAndTrace (PtyFd, buf, NetInfo.n_psize+1, "pty rx")) == 0) {
276 if (errno != EWOULDBLOCK) {
277 close (PtyFd);
278 PtyFd = -1;
279 }
280 } else
281 FromPty ((struct PtyPacket *)buf, len);
282
283 if (obits & (1<<NetFd)) {
284 if (FlushQueue (NetFd, &netoqueue, "net tx") < 0 &&
285 errno != EWOULDBLOCK)
286 NetworkError ();
287 }
288
289 if (obits & (1 << PtyFd))
290 if (FlushQueue (PtyFd, &ptyoqueue, "pty tx") < 0 &&
291 errno != EWOULDBLOCK) {
292 close (PtyFd);
293 PtyFd = -1;
294 }
295 }
296 cleanup ();
297}
298
299ReadAndTrace (fd, buf, len, who)
300char *buf, *who;
301{
302 register int bytes;
303
304 bytes = read (fd, buf, len);
305 if (bytes <= 0)
306 return (0);
307 NimTrace (who, buf, bytes);
308 return (bytes);
309}
310
311NetworkError ()
312{
313
314 error ();
315 State = ST_COMMAND;
316 close (NetFd);
317 NetFd = -1;
318 message (Banner);
319}
320
321cleanup ()
322{
323 log ("slave disconnect");
324 close (NetFd);
325 close (PtyFd);
326 NetFd = PtyFd = -1;
327 if (TimerOn)
328 ClearTimer ();
329 ResetBufs ();
330}
331
332ResetBufs ()
333{
334 InitQueue (&ptyoqueue);
335 InitQueue (&netoqueue);
336 RESET (packet);
337}
338
339ToPty (str, len, from)
340register char *str;
341{
342 register char *end = str + len, c, c1;
343 register struct buf *bp = 0;
344 register int lfcode;
345
346 while (str < end) {
347 c = *str++;
348 c1 = c & 0177;
349 if (CurrentX29Parms[X29_AUX_DEV_CONTROL_CODE]) {
350 if (c1 == 023) {
351 OutputBlocked++;
352 continue;
353 }
354 if (c1 == 021) {
355 OutputBlocked = 0;
356 continue;
357 }
358 }
359 if (ptyoqueue.b_count > 256)
360 continue;
361 if (bp == 0)
362 bp = getbuf (MAXPSIZ);
363 PUTCHAR (c, bp);
364 if (SIZE (bp) >= MAXPSIZ-1) {
365 enqueue (bp, &ptyoqueue);
366 bp = 0;
367 }
368 if (c1 != '\r' || (lfcode = CurrentX29Parms[X29_LF_AFTER_CR]) <= 0)
369 continue;
370 if ((lfcode & 01) && from == FROMNET ||
371 (lfcode & 04) && from == FROMPTY ||
372 from == FROMNIM)
373 PUTCHAR ('\n', bp);
374 }
375 if (bp)
376 enqueue (bp, &ptyoqueue);
377}
378
379FromPty(pp, len)
380register struct PtyPacket *pp;
381{
382 register int echo;
383 register struct buf *tp = packet;
384 register char *cp, c;
385 char c1;
386
387 if (pp->p_type != TIOCPKT_DATA) { /* fetch control byte */
388 PtyControl (pp);
389 return;
390 }
391 if (State & ST_UGLY_50_BAUD_BREAK_IN_PROGRESS)
392 return;
393 cp = pp->p_data;
394 echo = CurrentX29Parms[X29_ECHO_CODE] > 0 && ptyoqueue.b_count < 256;
395 while (cp < ((char *)pp)+len) {
396 c = (c1 = *cp++) & 0177;
397 if (State & ST_ESCSEEN && C_TYPE(c) != C_ESCAPE)
398 EnterCommandState ();
399 switch (C_TYPE(c)) {
400 case C_ERASE:
401 if (!ISEMPTY(tp)) {
402 tp->b_top--;
403 if (echo)
404 if (c == '\b')
405 ToPty ("\b \b", 3, FROMPTY);
406 else
407 ToPty (&c1, 1, FROMPTY);
408 }
409 continue;
410
411 case C_KILL:
412 RESET (tp);
413 if (echo)
414 ToPty ("*poof*\r", 7, FROMPTY);
415 continue;
416
417 case C_DISPLAY:
418 ToPty (tp->b_bot, SIZE (tp), FROMPTY);
419 continue;
420
421 case C_ESCAPE:
422 if ((State & (ST_COMMAND|ST_ESCSEEN)) == 0) {
423 State |= ST_ESCSEEN;
424 continue;
425 }
426 State &= ~ST_ESCSEEN;
427 /* fall through */
428
429 default:
430 if (State & ST_COMMAND) {
431 if (c == '\r') {
432 if (echo)
433 ToPty (&c1, 1, FROMPTY);
434 PUTCHAR ('\0', tp);
435 FlushQueue (PtyFd, &ptyoqueue, "pty tx");
436 NimCommand (tp->b_bot);
437 RESET(tp);
438 continue;
439 }
440 if (SIZE (tp) < MAXPSIZ-1) {
441 PUTCHAR (c, tp);
442 if (echo)
443 ToPty (&c1, 1, FROMPTY);
444 } else
445/* ToPty ("\007", 1, FROMPTY)*/;
446 } else {
447 PUTCHAR (c1, tp);
448 if (echo)
449 ToPty (&c1, 1, FROMPTY);
450 if (ISFORWARD(c) || SIZE (tp) >= NetInfo.n_psize) {
451 ForwardPacket ();
452 continue;
453 }
454 }
455 }
456 }
457 if (!ISEMPTY (tp) && (State & ST_COMMAND) == 0)
458 SetTimer ();
459}
460
461PtyControl(pp)
462register struct PtyPacket *pp;
463{
464#ifdef notdef
465 if ((pp->p_type & (TIOCPKT_FLUSHWRITE|TIOCPKT_FLUSHREAD)) ==
466 (TIOCPKT_FLUSHWRITE|TIOCPKT_FLUSHREAD)) { /* break indication from pty */
467 if (State & ST_COMMAND)
468 RESET (packet);
469 else
470 Break (CurrentX29Parms[X29_BREAK_PROCEDURE_CODE]);
471 return;
472 }
473#endif
474#ifdef TIOCPKT_IOCTL
475 if (pp->p_type & TIOCPKT_IOCTL) { /* some kind of set tty done by slave */
476 static short UnixToX29Speed[] = {
477 0, 10, 5, 0, 1, 6, 8, 2, 4, 3, /* B0 thru B1200 */
478 12, 13, 14, 15, 16 /* B2400 thru EXTB */
479 };
480 struct sgttyb sg;
481
482 ioctl(PtyFd, TIOCGETP, (char *)&sg);
483 if (sg.sg_ospeed == B50) {
484 State |= ST_UGLY_50_BAUD_BREAK_IN_PROGRESS;
485 return;
486 }
487 CurrentX29Parms[X29_TRANSMISSION_SPEED_CODE]
488 = UnixToX29Speed[sg.sg_ospeed];
489 if (State & ST_UGLY_50_BAUD_BREAK_IN_PROGRESS && sg.sg_ospeed != B50) {
490 State &= ~ST_UGLY_50_BAUD_BREAK_IN_PROGRESS;
491 if (State & ST_COMMAND)
492 RESET (packet);
493 else
494 Break (CurrentX29Parms[X29_BREAK_PROCEDURE_CODE]);
495 }
496 }
497#endif
498}
499
500EnterCommandState ()
501{
502 State &= ~ST_ESCSEEN;
503 State |= ST_COMMAND | ST_ESCCOMM;
504 ForwardPacket ();
505}
506
507ExitDataState (cause)
508char *cause;
509{
510 ResetBufs ();
511 close (NetFd);
512 NetFd = -1;
513 State = ST_COMMAND;
514 OutputBlocked = 0;
515 CurrentX29Parms[X29_DISCARD_OUTPUT_CODE] = 0;
516 message ("nimd: Call cleared - %s\r", cause);
517 log ("Call cleared - %s", cause);
518}
519
520ForwardPacket ()
521{
522 register struct buf *bp, *tp = packet;
523
524 if (!ISEMPTY(tp) && (State & ST_COMMAND) == 0) {
525 AddParity (tp->b_bot, SIZE (tp));
526 bp = getbuf (SIZE (tp) + 1);
527 PUTCHAR(0, bp);
528 BufCopy(tp, bp);
529 enqueue (bp, &netoqueue);
530 }
531 RESET (tp);
532 if (TimerOn)
533 ClearTimer ();
534}
535
536ToNet (pp, len)
537struct packet *pp;
538{
539 register struct buf *bp;
540
541 /*
542 * round buffer size up to a multiple of 64 bytes
543 * to reduce accumulation of small and usually
544 * useless buffers in the free list. This speeds
545 * up malloc().
546 */
547 bp = getbuf ((len + 63) & ~63);
548 bcopy ((char *)pp, bp->b_bot, len);
549 bp->b_top = bp->b_bot + len;
550 enqueue (bp, &netoqueue);
551}
552
553timeout()
554{
555 TimerOn = 0;
556 ForwardPacket ();
557 longjmp (JmpBuf, 0);
558}
559
560SetTimer ()
561{
562 register int t;
563 struct itimerval itv;
564
565 if (TimerOn || (t = CurrentX29Parms[X29_IDLE_TIMER_CODE]) <= 0)
566 return;
567 itv.it_interval.tv_sec = 0;
568 itv.it_interval.tv_usec = 0;
569 itv.it_value.tv_sec = t / 20;
570 itv.it_value.tv_usec = t % 20 * 1000000 / 20;
571 signal (SIGALRM, SIG_IGN); /* cancel possible pending signal */
572 signal (SIGALRM, timeout);
573 sigsetmask (1 << (SIGALRM - 1));
574 TimerOn++;
575 setitimer (ITIMER_REAL, &itv, (struct itimerval *)0);
576}
577
578ClearTimer ()
579{
580 struct itimerval itv;
581
582 signal (SIGALRM, SIG_IGN);
583 bzero ((char *)&itv, sizeof (itv));
584 setitimer (ITIMER_REAL, &itv, (struct itimerval *)0);
585 TimerOn = 0;
586}
587
588FromNet (bp, len)
589char *bp;
590{
591 if ((*bp & Q_BIT) == 0) {
592 register struct x25packet *xp = (struct x25packet *)bp;
593
594 if (CurrentX29Parms[X29_DISCARD_OUTPUT_CODE] == 0) {
595 AddParity (xp->p_x25data, len - 1);
596 ToPty (xp->p_x25data, len - 1, FROMNET);
597 }
598 return;
599 }
600 X29ControlMessage ((struct x29packet *)bp, len);
601}
602
603SendX25Interrupt()
604{
605 char c = 0x77;
606
607 send (NetFd, &c, 1, MSG_OOB);
608}
609
610#ifdef fastidious /* we need stdio */
611/*
612 * Sorry about this...
613 * Defining this dummy procedure prevents the stdio package
614 * (about 17K bytes worth) from being loaded. This program
615 * does not require any support from the 4.2bsd stdio library.
616 */
617
618#ifdef vax
619_cleanup()
620{
621}
622#endif
623#endif
624
625NimTrace(who, bp, n)
626char *who, *bp;
627{
628 static int fd;
629
630 if (TraceFile == 0)
631 return;
632 if(fd <= 0 && (fd = creat(TraceFile, 0640)) < 0)
633 return;
634 DisplayPacketContents (fd, who, bp, n);
635}
636
637OpenLog ()
638{
639
640 if (LogFd >= 0)
641 return;
642 if (LogDev = rindex (PtyName, '/'))
643 LogDev++;
644 else
645 LogDev = PtyName;
646 if ((LogFd = open (LOGFILE, 1)) >= 0)
647 return;
648 LogFd = creat (LOGFILE, 0640);
649}
650
651CloseLog()
652{
653 if (LogFd >= 0) {
654 close(LogFd);
655 LogFd = -1;
656 }
657}
658
659/*VARARGS*/
660log(fmt, a1, a2, a3, a4, a5)
661char *fmt;
662{
663 register char *DateTime;
664 char format[128], *ctime ();
665 time_t t;
666
667 if (LogFd < 0)
668 return;
669 (void) time (&t);
670 DateTime = ctime (&t);
671 DateTime[19] = '\0';
672 sprint (format, "%s %s %s\n", DateTime+4, LogDev, fmt);
673 lseek (LogFd, (long)0, 2);
674 fprint (LogFd, format, a1, a2, a3, a4, a5);
675}
676
677LogPacket (bp, len)
678char *bp;
679{
680 if (LogFd < 0)
681 return;
682 DisplayPacketContents (LogFd, "net rx", bp, len);
683}
684
685DisplayPacketContents (fd, from, pp, len)
686char *from;
687register char *pp;
688register int len;
689{
690 register int first = 1;
691 char buf[128];
692
693 lseek (fd, (long)0, 2);
694 do {
695 ConvertToOctal (pp, len, buf);
696 if (first) {
697 fprint (fd, "%s[%d]\t%s\n", from, len, buf);
698 first = 0;
699 } else
700 fprint (fd, "\t\t%s\n", buf);
701 ConvertToAscii (pp, len, buf);
702 fprint (fd, "\t\t%s\n", buf);
703 pp += 16;
704 len -= 16;
705 } while (len > 0);
706}
707
708ConvertToOctal (start, len, bp)
709register char *start, *bp;
710{
711 register char *cp;
712
713 if (len > 16)
714 len = 16;
715 for (cp = start; cp - start < len; cp++) {
716 *bp++ = ((*cp & 0300) >> 6) + '0';
717 *bp++ = ((*cp & 070) >> 3) + '0';
718 *bp++ = (*cp & 07) + '0';
719 *bp++ = ' ';
720 }
721 bp[-1] = '\0';
722}
723
724ConvertToAscii (start, len, bp)
725register char *start, *bp;
726{
727 register char *cp;
728
729 if (len > 16)
730 len = 16;
731 for (cp = start; cp - start < len; cp++) {
732 *bp++ = ' ';
733 switch (*cp) {
734 case '\b':
735 *bp++ = '\\';
736 *bp++ = 'b';
737 break;
738
739 case '\t':
740 *bp++ = '\\';
741 *bp++ = 't';
742 break;
743
744 case '\n':
745 *bp++ = '\\';
746 *bp++ = 'n';
747 break;
748
749 case '\f':
750 *bp++ = '\\';
751 *bp++ = 'f';
752 break;
753
754 case '\r':
755 *bp++ = '\\';
756 *bp++ = 'r';
757 break;
758
759 default:
760 *bp++ = ' ';
761 if ((*cp&0177) > ' ' && (*cp&0177) < 0177)
762 *bp++ = *cp;
763 else
764 *bp++ = ' ';
765 }
766 *bp++ = ' ';
767 }
768 bp[-1] = '\0';
769}