This version handles out-of-band data, abort output, and
[unix-history] / usr / src / libexec / telnetd / telnetd.c
CommitLineData
8c5eec2f
DF
1/*
2 * Copyright (c) 1983 Regents of the University of California.
3 * All rights reserved. The Berkeley software License Agreement
4 * specifies the terms and conditions for redistribution.
5 */
6
7#ifndef lint
8char copyright[] =
9"@(#) Copyright (c) 1983 Regents of the University of California.\n\
10 All rights reserved.\n";
11#endif not lint
12
ac6e6727 13#ifndef lint
5d78ef73 14static char sccsid[] = "@(#)telnetd.c 5.9 (Berkeley) %G%";
8c5eec2f 15#endif not lint
ac6e6727 16
66b878f6
BJ
17/*
18 * Stripped-down telnet server.
19 */
de3b21e8
SL
20#include <sys/types.h>
21#include <sys/socket.h>
ce4fd43b 22#include <sys/wait.h>
1a33b848 23#include <sys/file.h>
c29f876c 24#include <sys/stat.h>
5d78ef73 25#include <sys/time.h>
de3b21e8
SL
26
27#include <netinet/in.h>
28
2b597a6b
SL
29#include <arpa/telnet.h>
30
66b878f6
BJ
31#include <stdio.h>
32#include <signal.h>
33#include <errno.h>
34#include <sgtty.h>
9f005877 35#include <netdb.h>
3f99c0f7 36#include <syslog.h>
de3b21e8 37
4898c5bf 38#define BELL '\07'
122f5efc 39#define BANNER "\r\n\r\n4.3 BSD UNIX (%s)\r\n\r\r\n\r%s"
66b878f6
BJ
40
41char hisopts[256];
42char myopts[256];
43
44char doopt[] = { IAC, DO, '%', 'c', 0 };
45char dont[] = { IAC, DONT, '%', 'c', 0 };
46char will[] = { IAC, WILL, '%', 'c', 0 };
47char wont[] = { IAC, WONT, '%', 'c', 0 };
48
49/*
50 * I/O data buffers, pointers, and counters.
51 */
52char ptyibuf[BUFSIZ], *ptyip = ptyibuf;
53char ptyobuf[BUFSIZ], *pfrontp = ptyobuf, *pbackp = ptyobuf;
54char netibuf[BUFSIZ], *netip = netibuf;
9ef3087d 55char netobuf[BUFSIZ], *nfrontp = netobuf, *nbackp = netobuf;
5d78ef73 56char *neturg = 0; /* one past last bye of urgent data */
66b878f6
BJ
57int pcc, ncc;
58
59int pty, net;
60int inter;
fe5c5547 61extern char **environ;
66b878f6 62extern int errno;
c29f876c 63char *line;
5d78ef73
GM
64int SYNCHing = 0; /* we are in TELNET SYNCH mode */
65/*
66 * The following are some clocks used to decide how to interpret
67 * the relationship between various variables.
68 */
66b878f6 69
5d78ef73
GM
70struct {
71 int
72 system, /* what the current time is */
73 echotoggle, /* last time user entered echo character */
74 modenegotiated, /* last time operating mode negotiated */
75 didnetreceive, /* last time we read data from network */
76 gotDM; /* when did we last see a data mark */
77} clocks;
78
79#define settimer(x) clocks.x = clocks.system++
80\f
66b878f6
BJ
81main(argc, argv)
82 char *argv[];
83{
bb933cc2 84 struct sockaddr_in from;
bcb894cb 85 int on = 1, fromlen;
bb933cc2 86
5d78ef73
GM
87#if defined(DEBUG)
88 {
89 int s, ns, foo;
90 struct servent *sp;
91 static struct sockaddr_in sin = { AF_INET };
92
93 sp = getservbyname("telnet", "tcp");
94 if (sp == 0) {
95 fprintf(stderr, "telnetd: tcp/telnet: unknown service\n");
96 exit(1);
97 }
98 sin.sin_port = sp->s_port;
99 argc--, argv++;
100 if (argc > 0) {
101 sin.sin_port = atoi(*argv);
102 sin.sin_port = htons((u_short)sin.sin_port);
103 }
104
105 s = socket(AF_INET, SOCK_STREAM, 0);
106 if (s < 0) {
107 perror("telnetd: socket");;
108 exit(1);
109 }
110 if (bind(s, &sin, sizeof sin) < 0) {
111 perror("bind");
112 exit(1);
113 }
114 if (listen(s, 1) < 0) {
115 perror("listen");
116 exit(1);
117 }
118 foo = sizeof sin;
119 ns = accept(s, &sin, &foo);
120 if (ns < 0) {
121 perror("accept");
122 exit(1);
123 }
124 dup2(ns, 0);
125 close(s);
126 }
127#endif /* defined(DEBUG) */
076ae92c 128 openlog("telnetd", LOG_PID | LOG_ODELAY, LOG_DAEMON);
bb933cc2
MK
129 fromlen = sizeof (from);
130 if (getpeername(0, &from, &fromlen) < 0) {
131 fprintf(stderr, "%s: ", argv[0]);
132 perror("getpeername");
133 _exit(1);
de3b21e8 134 }
bcb894cb 135 if (setsockopt(0, SOL_SOCKET, SO_KEEPALIVE, &on, sizeof (on)) < 0) {
3f99c0f7 136 syslog(LOG_WARNING, "setsockopt (SO_KEEPALIVE): %m");
de3b21e8 137 }
bb933cc2 138 doit(0, &from);
f553aca8
SL
139}
140
fe5c5547 141char *envinit[] = { "TERM=network", 0 };
66b878f6
BJ
142int cleanup();
143
144/*
145 * Get a pty, scan input lines.
146 */
37c640e2
SL
147doit(f, who)
148 int f;
149 struct sockaddr_in *who;
66b878f6 150{
c29f876c 151 char *host, *inet_ntoa();
1a33b848 152 int i, p, t;
66b878f6 153 struct sgttyb b;
37c640e2 154 struct hostent *hp;
c29f876c 155 char c;
1a33b848 156
c29f876c
MK
157 for (c = 'p'; c <= 's'; c++) {
158 struct stat stb;
159
160 line = "/dev/ptyXX";
161 line[strlen("/dev/pty")] = c;
162 line[strlen("/dev/ptyp")] = '0';
163 if (stat(line, &stb) < 0)
164 break;
1a33b848 165 for (i = 0; i < 16; i++) {
c29f876c
MK
166 line[strlen("/dev/ptyp")] = "0123456789abcdef"[i];
167 p = open(line, 2);
1a33b848
SL
168 if (p > 0)
169 goto gotpty;
170 }
66b878f6 171 }
8f2758db
SL
172 fatal(f, "All network ports in use");
173 /*NOTREACHED*/
66b878f6
BJ
174gotpty:
175 dup2(f, 0);
c29f876c 176 line[strlen("/dev/")] = 't';
1a33b848 177 t = open("/dev/tty", O_RDWR);
66b878f6
BJ
178 if (t >= 0) {
179 ioctl(t, TIOCNOTTY, 0);
180 close(t);
181 }
c29f876c 182 t = open(line, O_RDWR);
8f2758db 183 if (t < 0)
c29f876c 184 fatalperror(f, line, errno);
66b878f6 185 ioctl(t, TIOCGETP, &b);
9ef3087d 186 b.sg_flags = CRMOD|XTABS|ANYP;
66b878f6 187 ioctl(t, TIOCSETP, &b);
9ef3087d 188 ioctl(p, TIOCGETP, &b);
da96b661 189 b.sg_flags &= ~ECHO;
9ef3087d 190 ioctl(p, TIOCSETP, &b);
37c640e2
SL
191 hp = gethostbyaddr(&who->sin_addr, sizeof (struct in_addr),
192 who->sin_family);
193 if (hp)
194 host = hp->h_name;
195 else
05fa5465 196 host = inet_ntoa(who->sin_addr);
8f2758db
SL
197 if ((i = fork()) < 0)
198 fatalperror(f, "fork", errno);
66b878f6
BJ
199 if (i)
200 telnet(f, p);
201 close(f);
202 close(p);
203 dup2(t, 0);
204 dup2(t, 1);
205 dup2(t, 2);
206 close(t);
fe5c5547 207 environ = envinit;
0c285f22 208 execl("/bin/login", "login", "-h", host, 0);
8f2758db
SL
209 fatalperror(f, "/bin/login", errno);
210 /*NOTREACHED*/
211}
212
213fatal(f, msg)
214 int f;
215 char *msg;
216{
217 char buf[BUFSIZ];
218
1a33b848 219 (void) sprintf(buf, "telnetd: %s.\r\n", msg);
8f2758db 220 (void) write(f, buf, strlen(buf));
66b878f6
BJ
221 exit(1);
222}
223
8f2758db
SL
224fatalperror(f, msg, errno)
225 int f;
226 char *msg;
227 int errno;
228{
229 char buf[BUFSIZ];
230 extern char *sys_errlist[];
231
1a33b848 232 (void) sprintf(buf, "%s: %s\r\n", msg, sys_errlist[errno]);
8f2758db
SL
233 fatal(f, buf);
234}
235
5d78ef73
GM
236
237/*
238 * Check a descriptor to see if out of band data exists on it.
239 */
240
241
242stilloob(s)
243int s; /* socket number */
244{
245 static struct timeval timeout = { 0 };
246 fd_set excepts;
247 int value;
248
249 do {
250 FD_ZERO(&excepts);
251 FD_SET(s, &excepts);
252 value = select(s+1, (fd_set *)0, (fd_set *)0, &excepts, &timeout);
253 } while ((value == -1) && (errno = EINTR));
254
255 if (value < 0) {
256 fatalperror(pty, "select", errno);
257 }
258 if (FD_ISSET(s, &excepts)) {
259 return 1;
260 } else {
261 return 0;
262 }
263}
264\f
66b878f6
BJ
265/*
266 * Main loop. Select from pty and network, and
267 * hand data to telnet receiver finite state machine.
268 */
269telnet(f, p)
270{
271 int on = 1;
0c285f22 272 char hostname[32];
66b878f6
BJ
273
274 net = f, pty = p;
275 ioctl(f, FIONBIO, &on);
276 ioctl(p, FIONBIO, &on);
277 signal(SIGTSTP, SIG_IGN);
8a53982e 278 signal(SIGCHLD, cleanup);
f4c5d9f9 279 setpgrp(0, 0);
66b878f6 280
da96b661 281 /*
5d78ef73 282 * Request to do remote echo and to suppress go ahead.
da96b661
SL
283 */
284 dooption(TELOPT_ECHO);
5d78ef73 285 dooption(TELOPT_SGA);
0c285f22
SL
286 /*
287 * Show banner that getty never gave.
288 */
289 gethostname(hostname, sizeof (hostname));
290 sprintf(nfrontp, BANNER, hostname, "");
291 nfrontp += strlen(nfrontp);
66b878f6 292 for (;;) {
5d78ef73 293 fd_set ibits, obits, xbits;
66b878f6
BJ
294 register int c;
295
5d78ef73
GM
296 if (ncc < 0 && pcc < 0)
297 break;
298
299 FD_ZERO(&ibits);
300 FD_ZERO(&obits);
301 FD_ZERO(&xbits);
66b878f6
BJ
302 /*
303 * Never look for input if there's still
304 * stuff in the corresponding output buffer
305 */
5d78ef73
GM
306 if (nfrontp - nbackp || pcc > 0) {
307 FD_SET(f, &obits);
308 } else {
309 FD_SET(p, &ibits);
310 }
311 if (pfrontp - pbackp || ncc > 0) {
312 FD_SET(p, &obits);
313 } else {
314 FD_SET(f, &ibits);
315 }
316 if (!SYNCHing) {
317 FD_SET(f, &xbits);
318 }
319 if ((c = select(16, &ibits, &obits, &xbits,
320 (struct timeval *)0)) < 1) {
321 if (c == -1) {
322 if (errno == EINTR) {
323 continue;
324 }
325 }
66b878f6
BJ
326 sleep(5);
327 continue;
328 }
329
5d78ef73
GM
330 /*
331 * Any urgent data?
332 */
333 if (FD_ISSET(net, &xbits)) {
334 SYNCHing = 1;
335 }
336
66b878f6
BJ
337 /*
338 * Something to read from the network...
339 */
5d78ef73
GM
340 if (FD_ISSET(net, &ibits)) {
341#if !defined(IOCTL_TO_DO_UNIX_OOB_IN_TCP_WAY)
342 /*
343 * In 4.2 (and some early 4.3) systems, the
344 * OOB indication and data handling in the kernel
345 * is such that if two separate TCP Urgent requests
346 * come in, one byte of TCP data will be overlaid.
347 * This is fatal for Telnet, but we try to live
348 * with it.
349 *
350 * In addition, in 4.2 (and...), a special protocol
351 * is needed to pick up the TCP Urgent data in
352 * the correct sequence.
353 *
354 * What we do is: if we think we are in urgent
355 * mode, we look to see if we are "at the mark".
356 * If we are, we do an OOB receive. If we run
357 * this twice, we will do the OOB receive twice,
358 * but the second will fail, since the second
359 * time we were "at the mark", but there wasn't
360 * any data there (the kernel doesn't reset
361 * "at the mark" until we do a normal read).
362 * Once we've read the OOB data, we go ahead
363 * and do normal reads.
364 *
365 * There is also another problem, which is that
366 * since the OOB byte we read doesn't put us
367 * out of OOB state, and since that byte is most
368 * likely the TELNET DM (data mark), we would
369 * stay in the TELNET SYNCH (SYNCHing) state.
370 * So, clocks to the rescue. If we've "just"
371 * received a DM, then we test for the
372 * presence of OOB data when the receive OOB
373 * fails (and AFTER we did the normal mode read
374 * to clear "at the mark").
375 */
376 if (SYNCHing) {
377 int atmark;
378
379 ioctl(net, SIOCATMARK, (char *)&atmark);
380 if (atmark) {
381 ncc = recv(net, netibuf, sizeof (netibuf), MSG_OOB);
382 if ((ncc == -1) && (errno == EINVAL)) {
383 ncc = read(net, netibuf, sizeof (netibuf));
384 if (clocks.didnetreceive < clocks.gotDM) {
385 SYNCHing = stilloob(net);
386 }
387 }
388 } else {
389 ncc = read(net, netibuf, sizeof (netibuf));
66b878f6 390 }
5d78ef73
GM
391 } else {
392 ncc = read(net, netibuf, sizeof (netibuf));
393 }
394 settimer(didnetreceive);
395#else /* !defined(IOCTL_TO_DO_UNIX_OOB_IN_TCP_WAY) */
396 ncc = read(net, netibuf, sizeof (netibuf));
397#endif /* !defined(IOCTL_TO_DO_UNIX_OOB_IN_TCP_WAY) */
398 if (ncc < 0 && errno == EWOULDBLOCK)
399 ncc = 0;
400 else {
401 if (ncc <= 0) {
402 break;
403 }
404 netip = netibuf;
405 }
66b878f6
BJ
406 }
407
408 /*
409 * Something to read from the pty...
410 */
5d78ef73 411 if (FD_ISSET(p, &ibits)) {
66b878f6
BJ
412 pcc = read(p, ptyibuf, BUFSIZ);
413 if (pcc < 0 && errno == EWOULDBLOCK)
414 pcc = 0;
415 else {
416 if (pcc <= 0)
417 break;
418 ptyip = ptyibuf;
419 }
420 }
421
422 while (pcc > 0) {
423 if ((&netobuf[BUFSIZ] - nfrontp) < 2)
424 break;
425 c = *ptyip++ & 0377, pcc--;
426 if (c == IAC)
427 *nfrontp++ = c;
428 *nfrontp++ = c;
9f515693
GM
429 if (c == '\r') {
430 if (pcc > 0 && ((*ptyip & 0377) == '\n')) {
431 *nfrontp++ = *ptyip++ & 0377;
432 pcc--;
433 } else
434 *nfrontp++ = '\0';
435 }
66b878f6 436 }
5d78ef73 437 if (FD_ISSET(f, &obits) && (nfrontp - nbackp) > 0)
66b878f6
BJ
438 netflush();
439 if (ncc > 0)
440 telrcv();
5d78ef73 441 if (FD_ISSET(p, &obits) && (pfrontp - pbackp) > 0)
66b878f6
BJ
442 ptyflush();
443 }
444 cleanup();
445}
446
447/*
448 * State for recv fsm
449 */
450#define TS_DATA 0 /* base state */
451#define TS_IAC 1 /* look for double IAC's */
452#define TS_CR 2 /* CR-LF ->'s CR */
453#define TS_BEGINNEG 3 /* throw away begin's... */
454#define TS_ENDNEG 4 /* ...end's (suboption negotiation) */
455#define TS_WILL 5 /* will option negotiation */
456#define TS_WONT 6 /* wont " */
457#define TS_DO 7 /* do " */
458#define TS_DONT 8 /* dont " */
459
460telrcv()
461{
462 register int c;
463 static int state = TS_DATA;
66b878f6
BJ
464
465 while (ncc > 0) {
466 if ((&ptyobuf[BUFSIZ] - pfrontp) < 2)
467 return;
468 c = *netip++ & 0377, ncc--;
469 switch (state) {
470
8356bfad
GM
471 case TS_CR:
472 state = TS_DATA;
a6d8450f 473 if ((c == 0) || (c == '\n')) {
8356bfad 474 break;
a6d8450f 475 }
8356bfad
GM
476 /* FALL THROUGH */
477
66b878f6
BJ
478 case TS_DATA:
479 if (c == IAC) {
480 state = TS_IAC;
481 break;
482 }
483 if (inter > 0)
484 break;
9f515693
GM
485 /*
486 * We map \r\n ==> \n, since \r\n says
487 * that we want to be in column 1 of the next
488 * printable line, and \n is the standard
489 * unix way of saying that (\r is only good
490 * if CRMOD is set, which it normally is).
491 */
a6d8450f 492 if (!myopts[TELOPT_BINARY] && c == '\r') {
9f515693
GM
493 if ((ncc > 0) && ('\n' == *netip)) {
494 netip++; ncc--;
495 c = '\n';
496 } else {
497 state = TS_CR;
498 }
a6d8450f
GM
499 }
500 *pfrontp++ = c;
66b878f6
BJ
501 break;
502
503 case TS_IAC:
504 switch (c) {
505
506 /*
507 * Send the process on the pty side an
508 * interrupt. Do this with a NULL or
509 * interrupt char; depending on the tty mode.
510 */
511 case BREAK:
512 case IP:
513 interrupt();
514 break;
515
516 /*
517 * Are You There?
518 */
519 case AYT:
1a33b848
SL
520 strcpy(nfrontp, "\r\n[Yes]\r\n");
521 nfrontp += 9;
66b878f6
BJ
522 break;
523
5d78ef73
GM
524 /*
525 * Abort Output
526 */
527 case AO: {
528 struct ltchars tmpltc;
529
530 ptyflush(); /* half-hearted */
531 ioctl(pty, TIOCGLTC, &tmpltc);
532 if (tmpltc.t_flushc != '\377') {
533 *pfrontp++ = tmpltc.t_flushc;
534 }
535 *nfrontp++ = IAC;
536 *nfrontp++ = DM;
537 neturg = nfrontp;
538 break;
539 }
540
66b878f6
BJ
541 /*
542 * Erase Character and
543 * Erase Line
544 */
545 case EC:
5d78ef73
GM
546 case EL: {
547 struct sgttyb b;
548 char ch;
549
550 ptyflush(); /* half-hearted */
551 ioctl(pty, TIOCGETP, &b);
552 ch = (c == EC) ?
553 b.sg_erase : b.sg_kill;
554 if (ch != '\377') {
555 *pfrontp++ = ch;
556 }
557 break;
558 }
66b878f6
BJ
559
560 /*
561 * Check for urgent data...
562 */
563 case DM:
5d78ef73
GM
564 SYNCHing = stilloob(net);
565 settimer(gotDM);
66b878f6
BJ
566 break;
567
5d78ef73 568
66b878f6
BJ
569 /*
570 * Begin option subnegotiation...
571 */
572 case SB:
573 state = TS_BEGINNEG;
574 continue;
575
576 case WILL:
577 case WONT:
578 case DO:
579 case DONT:
580 state = TS_WILL + (c - WILL);
581 continue;
582
583 case IAC:
584 *pfrontp++ = c;
585 break;
586 }
587 state = TS_DATA;
588 break;
589
590 case TS_BEGINNEG:
591 if (c == IAC)
592 state = TS_ENDNEG;
593 break;
594
595 case TS_ENDNEG:
596 state = c == SE ? TS_DATA : TS_BEGINNEG;
597 break;
598
599 case TS_WILL:
600 if (!hisopts[c])
601 willoption(c);
602 state = TS_DATA;
603 continue;
604
605 case TS_WONT:
606 if (hisopts[c])
607 wontoption(c);
608 state = TS_DATA;
609 continue;
610
611 case TS_DO:
612 if (!myopts[c])
613 dooption(c);
614 state = TS_DATA;
615 continue;
616
617 case TS_DONT:
618 if (myopts[c]) {
619 myopts[c] = 0;
620 sprintf(nfrontp, wont, c);
da96b661 621 nfrontp += sizeof (wont) - 2;
66b878f6
BJ
622 }
623 state = TS_DATA;
624 continue;
625
626 default:
de3b21e8 627 printf("telnetd: panic state=%d\n", state);
66b878f6
BJ
628 exit(1);
629 }
630 }
631}
632
633willoption(option)
634 int option;
635{
636 char *fmt;
637
638 switch (option) {
639
640 case TELOPT_BINARY:
641 mode(RAW, 0);
642 goto common;
643
644 case TELOPT_ECHO:
645 mode(0, ECHO|CRMOD);
646 /*FALL THRU*/
647
648 case TELOPT_SGA:
649 common:
650 hisopts[option] = 1;
651 fmt = doopt;
652 break;
653
654 case TELOPT_TM:
655 fmt = dont;
656 break;
657
658 default:
659 fmt = dont;
660 break;
661 }
13646f15 662 sprintf(nfrontp, fmt, option);
da96b661 663 nfrontp += sizeof (dont) - 2;
66b878f6
BJ
664}
665
666wontoption(option)
667 int option;
668{
669 char *fmt;
670
671 switch (option) {
672
673 case TELOPT_ECHO:
674 mode(ECHO|CRMOD, 0);
675 goto common;
676
677 case TELOPT_BINARY:
678 mode(0, RAW);
679 /*FALL THRU*/
680
681 case TELOPT_SGA:
682 common:
683 hisopts[option] = 0;
684 fmt = dont;
685 break;
686
687 default:
688 fmt = dont;
689 }
690 sprintf(nfrontp, fmt, option);
da96b661 691 nfrontp += sizeof (doopt) - 2;
66b878f6
BJ
692}
693
694dooption(option)
695 int option;
696{
697 char *fmt;
698
699 switch (option) {
700
701 case TELOPT_TM:
702 fmt = wont;
703 break;
704
705 case TELOPT_ECHO:
706 mode(ECHO|CRMOD, 0);
707 goto common;
708
709 case TELOPT_BINARY:
710 mode(RAW, 0);
711 /*FALL THRU*/
712
713 case TELOPT_SGA:
714 common:
715 fmt = will;
716 break;
717
718 default:
719 fmt = wont;
720 break;
721 }
722 sprintf(nfrontp, fmt, option);
da96b661 723 nfrontp += sizeof (doopt) - 2;
66b878f6
BJ
724}
725
726mode(on, off)
727 int on, off;
728{
729 struct sgttyb b;
730
731 ptyflush();
732 ioctl(pty, TIOCGETP, &b);
733 b.sg_flags |= on;
734 b.sg_flags &= ~off;
735 ioctl(pty, TIOCSETP, &b);
736}
737
738/*
739 * Send interrupt to process on other side of pty.
740 * If it is in raw mode, just write NULL;
741 * otherwise, write intr char.
742 */
743interrupt()
744{
745 struct sgttyb b;
746 struct tchars tchars;
747
748 ptyflush(); /* half-hearted */
749 ioctl(pty, TIOCGETP, &b);
750 if (b.sg_flags & RAW) {
751 *pfrontp++ = '\0';
752 return;
753 }
754 *pfrontp++ = ioctl(pty, TIOCGETC, &tchars) < 0 ?
755 '\177' : tchars.t_intrc;
756}
757
758ptyflush()
759{
760 int n;
761
762 if ((n = pfrontp - pbackp) > 0)
763 n = write(pty, pbackp, n);
9f005877
SL
764 if (n < 0)
765 return;
66b878f6
BJ
766 pbackp += n;
767 if (pbackp == pfrontp)
768 pbackp = pfrontp = ptyobuf;
769}
770
5d78ef73 771#if 0
66b878f6
BJ
772netflush()
773{
774 int n;
775
776 if ((n = nfrontp - nbackp) > 0)
777 n = write(net, nbackp, n);
9f005877
SL
778 if (n < 0) {
779 if (errno == EWOULDBLOCK)
780 return;
781 /* should blow this guy away... */
782 return;
783 }
66b878f6
BJ
784 nbackp += n;
785 if (nbackp == nfrontp)
786 nbackp = nfrontp = netobuf;
787}
5d78ef73
GM
788#else /* 0 */
789
790
791/*
792 * netflush
793 * Send as much data as possible to the network,
794 * handling requests for urgent data.
795 */
796
797
798netflush()
799{
800 int n;
801
802 if ((n = nfrontp - nbackp) > 0) {
803 if (!neturg) {
804 n = write(net, nbackp, n); /* normal write */
805 } else {
806 n = neturg - nbackp;
807 /*
808 * In 4.2 (and 4.3) systems, there is some question about
809 * what byte in a sendOOB operation is the "OOB" data.
810 * To make ourselves compatible, we only send ONE byte
811 * out of band, the one WE THINK should be OOB (though
812 * we really have more the TCP philosophy of urgent data
813 * rather than the Unix philosophy of OOB data).
814 */
815 if (n > 1) {
816 n = send(net, nbackp, n-1, 0); /* send URGENT all by itself */
817 } else {
818 n = send(net, nbackp, n, MSG_OOB); /* URGENT data */
819 }
820 }
821 }
822 if (n < 0) {
823 if (errno == EWOULDBLOCK)
824 return;
825 /* should blow this guy away... */
826 return;
827 }
828 nbackp += n;
829 if (nbackp >= neturg) {
830 neturg = 0;
831 }
832 if (nbackp == nfrontp) {
833 nbackp = nfrontp = netobuf;
834 }
835}
836#endif /* 0 */
66b878f6
BJ
837
838cleanup()
839{
66b878f6
BJ
840
841 rmut();
a7f6263e 842 vhangup(); /* XXX */
ff24c640 843 shutdown(net, 2);
66b878f6
BJ
844 exit(1);
845}
846
847#include <utmp.h>
848
849struct utmp wtmp;
850char wtmpf[] = "/usr/adm/wtmp";
122f5efc
JB
851char utmpf[] = "/etc/utmp";
852#define SCPYN(a, b) strncpy(a, b, sizeof(a))
853#define SCMPN(a, b) strncmp(a, b, sizeof(a))
66b878f6
BJ
854
855rmut()
856{
857 register f;
858 int found = 0;
122f5efc
JB
859 struct utmp *u, *utmp;
860 int nutmp;
861 struct stat statbf;
66b878f6 862
122f5efc 863 f = open(utmpf, O_RDWR);
66b878f6 864 if (f >= 0) {
122f5efc
JB
865 fstat(f, &statbf);
866 utmp = (struct utmp *)malloc(statbf.st_size);
867 if (!utmp)
868 syslog(LOG_ERR, "utmp malloc failed");
869 if (statbf.st_size && utmp) {
870 nutmp = read(f, utmp, statbf.st_size);
871 nutmp /= sizeof(struct utmp);
872
873 for (u = utmp ; u < &utmp[nutmp] ; u++) {
874 if (SCMPN(u->ut_line, line+5) ||
875 u->ut_name[0]==0)
876 continue;
877 lseek(f, ((long)u)-((long)utmp), L_SET);
878 SCPYN(u->ut_name, "");
879 SCPYN(u->ut_host, "");
880 time(&u->ut_time);
881 write(f, (char *)u, sizeof(wtmp));
882 found++;
883 }
66b878f6
BJ
884 }
885 close(f);
886 }
887 if (found) {
1a33b848 888 f = open(wtmpf, O_WRONLY|O_APPEND);
66b878f6
BJ
889 if (f >= 0) {
890 SCPYN(wtmp.ut_line, line+5);
891 SCPYN(wtmp.ut_name, "");
37c640e2 892 SCPYN(wtmp.ut_host, "");
66b878f6 893 time(&wtmp.ut_time);
122f5efc 894 write(f, (char *)&wtmp, sizeof(wtmp));
66b878f6
BJ
895 close(f);
896 }
897 }
898 chmod(line, 0666);
899 chown(line, 0, 0);
900 line[strlen("/dev/")] = 'p';
901 chmod(line, 0666);
902 chown(line, 0, 0);
903}