Annotation of 43BSD/etc/telnetd.c, revision 1.1

1.1     ! root        1: /*
        !             2:  * Copyright (c) 1983,1986 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
        !             8: char copyright[] =
        !             9: "@(#) Copyright (c) 1983 Regents of the University of California.\n\
        !            10:  All rights reserved.\n";
        !            11: #endif not lint
        !            12: 
        !            13: #ifndef lint
        !            14: static char sccsid[] = "@(#)telnetd.c  5.18 (Berkeley) 5/12/86";
        !            15: #endif not lint
        !            16: 
        !            17: /*
        !            18:  * Telnet server.
        !            19:  */
        !            20: #include <sys/param.h>
        !            21: #include <sys/socket.h>
        !            22: #include <sys/wait.h>
        !            23: #include <sys/file.h>
        !            24: #include <sys/stat.h>
        !            25: #include <sys/time.h>
        !            26: 
        !            27: #include <netinet/in.h>
        !            28: 
        !            29: #include <arpa/telnet.h>
        !            30: 
        !            31: #include <stdio.h>
        !            32: #include <signal.h>
        !            33: #include <errno.h>
        !            34: #include <sgtty.h>
        !            35: #include <netdb.h>
        !            36: #include <syslog.h>
        !            37: #include <ctype.h>
        !            38: 
        !            39: #define        OPT_NO                  0               /* won't do this option */
        !            40: #define        OPT_YES                 1               /* will do this option */
        !            41: #define        OPT_YES_BUT_ALWAYS_LOOK 2
        !            42: #define        OPT_NO_BUT_ALWAYS_LOOK  3
        !            43: char   hisopts[256];
        !            44: char   myopts[256];
        !            45: 
        !            46: char   doopt[] = { IAC, DO, '%', 'c', 0 };
        !            47: char   dont[] = { IAC, DONT, '%', 'c', 0 };
        !            48: char   will[] = { IAC, WILL, '%', 'c', 0 };
        !            49: char   wont[] = { IAC, WONT, '%', 'c', 0 };
        !            50: 
        !            51: /*
        !            52:  * I/O data buffers, pointers, and counters.
        !            53:  */
        !            54: char   ptyibuf[BUFSIZ], *ptyip = ptyibuf;
        !            55: 
        !            56: char   ptyobuf[BUFSIZ], *pfrontp = ptyobuf, *pbackp = ptyobuf;
        !            57: 
        !            58: char   netibuf[BUFSIZ], *netip = netibuf;
        !            59: #define        NIACCUM(c)      {   *netip++ = c; \
        !            60:                            ncc++; \
        !            61:                        }
        !            62: 
        !            63: char   netobuf[BUFSIZ], *nfrontp = netobuf, *nbackp = netobuf;
        !            64: char   *neturg = 0;            /* one past last bye of urgent data */
        !            65:        /* the remote system seems to NOT be an old 4.2 */
        !            66: int    not42 = 1;
        !            67: 
        !            68: 
        !            69: char BANNER1[] = "\r\n\r\n4.3 BSD UNIX (",
        !            70:     BANNER2[] = ")\r\n\r\0\r\n\r\0";
        !            71: 
        !            72:                /* buffer for sub-options */
        !            73: char   subbuffer[100], *subpointer= subbuffer, *subend= subbuffer;
        !            74: #define        SB_CLEAR()      subpointer = subbuffer;
        !            75: #define        SB_TERM()       { subend = subpointer; SB_CLEAR(); }
        !            76: #define        SB_ACCUM(c)     if (subpointer < (subbuffer+sizeof subbuffer)) { \
        !            77:                                *subpointer++ = (c); \
        !            78:                        }
        !            79: #define        SB_GET()        ((*subpointer++)&0xff)
        !            80: #define        SB_EOF()        (subpointer >= subend)
        !            81: 
        !            82: int    pcc, ncc;
        !            83: 
        !            84: int    pty, net;
        !            85: int    inter;
        !            86: extern char **environ;
        !            87: extern int errno;
        !            88: char   *line;
        !            89: int    SYNCHing = 0;           /* we are in TELNET SYNCH mode */
        !            90: /*
        !            91:  * The following are some clocks used to decide how to interpret
        !            92:  * the relationship between various variables.
        !            93:  */
        !            94: 
        !            95: struct {
        !            96:     int
        !            97:        system,                 /* what the current time is */
        !            98:        echotoggle,             /* last time user entered echo character */
        !            99:        modenegotiated,         /* last time operating mode negotiated */
        !           100:        didnetreceive,          /* last time we read data from network */
        !           101:        ttypeopt,               /* ttype will/won't received */
        !           102:        ttypesubopt,            /* ttype subopt is received */
        !           103:        getterminal,            /* time started to get terminal information */
        !           104:        gotDM;                  /* when did we last see a data mark */
        !           105: } clocks;
        !           106: 
        !           107: #define        settimer(x)     (clocks.x = ++clocks.system)
        !           108: #define        sequenceIs(x,y) (clocks.x < clocks.y)
        !           109: 
        !           110: main(argc, argv)
        !           111:        char *argv[];
        !           112: {
        !           113:        struct sockaddr_in from;
        !           114:        int on = 1, fromlen;
        !           115: 
        !           116: #if    defined(DEBUG)
        !           117:        {
        !           118:            int s, ns, foo;
        !           119:            struct servent *sp;
        !           120:            static struct sockaddr_in sin = { AF_INET };
        !           121: 
        !           122:            sp = getservbyname("telnet", "tcp");
        !           123:            if (sp == 0) {
        !           124:                    fprintf(stderr, "telnetd: tcp/telnet: unknown service\n");
        !           125:                    exit(1);
        !           126:            }
        !           127:            sin.sin_port = sp->s_port;
        !           128:            argc--, argv++;
        !           129:            if (argc > 0) {
        !           130:                    sin.sin_port = atoi(*argv);
        !           131:                    sin.sin_port = htons((u_short)sin.sin_port);
        !           132:            }
        !           133: 
        !           134:            s = socket(AF_INET, SOCK_STREAM, 0);
        !           135:            if (s < 0) {
        !           136:                    perror("telnetd: socket");;
        !           137:                    exit(1);
        !           138:            }
        !           139:            if (bind(s, &sin, sizeof sin) < 0) {
        !           140:                perror("bind");
        !           141:                exit(1);
        !           142:            }
        !           143:            if (listen(s, 1) < 0) {
        !           144:                perror("listen");
        !           145:                exit(1);
        !           146:            }
        !           147:            foo = sizeof sin;
        !           148:            ns = accept(s, &sin, &foo);
        !           149:            if (ns < 0) {
        !           150:                perror("accept");
        !           151:                exit(1);
        !           152:            }
        !           153:            dup2(ns, 0);
        !           154:            close(s);
        !           155:        }
        !           156: #endif /* defined(DEBUG) */
        !           157:        openlog("telnetd", LOG_PID | LOG_ODELAY, LOG_DAEMON);
        !           158:        fromlen = sizeof (from);
        !           159:        if (getpeername(0, &from, &fromlen) < 0) {
        !           160:                fprintf(stderr, "%s: ", argv[0]);
        !           161:                perror("getpeername");
        !           162:                _exit(1);
        !           163:        }
        !           164:        if (setsockopt(0, SOL_SOCKET, SO_KEEPALIVE, &on, sizeof (on)) < 0) {
        !           165:                syslog(LOG_WARNING, "setsockopt (SO_KEEPALIVE): %m");
        !           166:        }
        !           167:        doit(0, &from);
        !           168: }
        !           169: 
        !           170: char   *terminaltype = 0;
        !           171: char   *envinit[2];
        !           172: int    cleanup();
        !           173: 
        !           174: /*
        !           175:  * ttloop
        !           176:  *
        !           177:  *     A small subroutine to flush the network output buffer, get some data
        !           178:  * from the network, and pass it through the telnet state machine.  We
        !           179:  * also flush the pty input buffer (by dropping its data) if it becomes
        !           180:  * too full.
        !           181:  */
        !           182: 
        !           183: void
        !           184: ttloop()
        !           185: {
        !           186:     if (nfrontp-nbackp) {
        !           187:        netflush();
        !           188:     }
        !           189:     ncc = read(net, netibuf, sizeof netibuf);
        !           190:     if (ncc < 0) {
        !           191:        syslog(LOG_INFO, "ttloop:  read: %m\n");
        !           192:        exit(1);
        !           193:     } else if (ncc == 0) {
        !           194:        syslog(LOG_INFO, "ttloop:  peer died: %m\n");
        !           195:        exit(1);
        !           196:     }
        !           197:     netip = netibuf;
        !           198:     telrcv();                  /* state machine */
        !           199:     if (ncc > 0) {
        !           200:        pfrontp = pbackp = ptyobuf;
        !           201:        telrcv();
        !           202:     }
        !           203: }
        !           204: 
        !           205: /*
        !           206:  * getterminaltype
        !           207:  *
        !           208:  *     Ask the other end to send along its terminal type.
        !           209:  * Output is the variable terminaltype filled in.
        !           210:  */
        !           211: 
        !           212: void
        !           213: getterminaltype()
        !           214: {
        !           215:     static char sbuf[] = { IAC, DO, TELOPT_TTYPE };
        !           216: 
        !           217:     settimer(getterminal);
        !           218:     bcopy(sbuf, nfrontp, sizeof sbuf);
        !           219:     nfrontp += sizeof sbuf;
        !           220:     hisopts[TELOPT_TTYPE] = OPT_YES_BUT_ALWAYS_LOOK;
        !           221:     while (sequenceIs(ttypeopt, getterminal)) {
        !           222:        ttloop();
        !           223:     }
        !           224:     if (hisopts[TELOPT_TTYPE] == OPT_YES) {
        !           225:        static char sbbuf[] = { IAC, SB, TELOPT_TTYPE, TELQUAL_SEND, IAC, SE };
        !           226: 
        !           227:        bcopy(sbbuf, nfrontp, sizeof sbbuf);
        !           228:        nfrontp += sizeof sbbuf;
        !           229:        while (sequenceIs(ttypesubopt, getterminal)) {
        !           230:            ttloop();
        !           231:        }
        !           232:     }
        !           233: }
        !           234: 
        !           235: /*
        !           236:  * Get a pty, scan input lines.
        !           237:  */
        !           238: doit(f, who)
        !           239:        int f;
        !           240:        struct sockaddr_in *who;
        !           241: {
        !           242:        char *host, *inet_ntoa();
        !           243:        int i, p, t;
        !           244:        struct sgttyb b;
        !           245:        struct hostent *hp;
        !           246:        int c;
        !           247: 
        !           248:        for (c = 'p'; c <= 's'; c++) {
        !           249:                struct stat stb;
        !           250: 
        !           251:                line = "/dev/ptyXX";
        !           252:                line[strlen("/dev/pty")] = c;
        !           253:                line[strlen("/dev/ptyp")] = '0';
        !           254:                if (stat(line, &stb) < 0)
        !           255:                        break;
        !           256:                for (i = 0; i < 16; i++) {
        !           257:                        line[strlen("/dev/ptyp")] = "0123456789abcdef"[i];
        !           258:                        p = open(line, 2);
        !           259:                        if (p > 0)
        !           260:                                goto gotpty;
        !           261:                }
        !           262:        }
        !           263:        fatal(f, "All network ports in use");
        !           264:        /*NOTREACHED*/
        !           265: gotpty:
        !           266:        dup2(f, 0);
        !           267:        line[strlen("/dev/")] = 't';
        !           268:        t = open("/dev/tty", O_RDWR);
        !           269:        if (t >= 0) {
        !           270:                ioctl(t, TIOCNOTTY, 0);
        !           271:                close(t);
        !           272:        }
        !           273:        t = open(line, O_RDWR);
        !           274:        if (t < 0)
        !           275:                fatalperror(f, line, errno);
        !           276:        ioctl(t, TIOCGETP, &b);
        !           277:        b.sg_flags = CRMOD|XTABS|ANYP;
        !           278:        ioctl(t, TIOCSETP, &b);
        !           279:        ioctl(p, TIOCGETP, &b);
        !           280:        b.sg_flags &= ~ECHO;
        !           281:        ioctl(p, TIOCSETP, &b);
        !           282:        hp = gethostbyaddr(&who->sin_addr, sizeof (struct in_addr),
        !           283:                who->sin_family);
        !           284:        if (hp)
        !           285:                host = hp->h_name;
        !           286:        else
        !           287:                host = inet_ntoa(who->sin_addr);
        !           288: 
        !           289:        net = f;
        !           290:        pty = p;
        !           291: 
        !           292:        /*
        !           293:         * get terminal type.
        !           294:         */
        !           295:        getterminaltype();
        !           296: 
        !           297:        if ((i = fork()) < 0)
        !           298:                fatalperror(f, "fork", errno);
        !           299:        if (i)
        !           300:                telnet(f, p);
        !           301:        close(f);
        !           302:        close(p);
        !           303:        dup2(t, 0);
        !           304:        dup2(t, 1);
        !           305:        dup2(t, 2);
        !           306:        close(t);
        !           307:        envinit[0] = terminaltype;
        !           308:        envinit[1] = 0;
        !           309:        environ = envinit;
        !           310:        /*
        !           311:         * -h : pass on name of host.
        !           312:         *              WARNING:  -h is accepted by login if and only if
        !           313:         *                      getuid() == 0.
        !           314:         * -p : don't clobber the environment (so terminal type stays set).
        !           315:         */
        !           316:        execl("/bin/login", "login", "-h", host,
        !           317:                                        terminaltype ? "-p" : 0, 0);
        !           318:        fatalperror(f, "/bin/login", errno);
        !           319:        /*NOTREACHED*/
        !           320: }
        !           321: 
        !           322: fatal(f, msg)
        !           323:        int f;
        !           324:        char *msg;
        !           325: {
        !           326:        char buf[BUFSIZ];
        !           327: 
        !           328:        (void) sprintf(buf, "telnetd: %s.\r\n", msg);
        !           329:        (void) write(f, buf, strlen(buf));
        !           330:        exit(1);
        !           331: }
        !           332: 
        !           333: fatalperror(f, msg, errno)
        !           334:        int f;
        !           335:        char *msg;
        !           336:        int errno;
        !           337: {
        !           338:        char buf[BUFSIZ];
        !           339:        extern char *sys_errlist[];
        !           340: 
        !           341:        (void) sprintf(buf, "%s: %s\r\n", msg, sys_errlist[errno]);
        !           342:        fatal(f, buf);
        !           343: }
        !           344: 
        !           345: 
        !           346: /*
        !           347:  * Check a descriptor to see if out of band data exists on it.
        !           348:  */
        !           349: 
        !           350: 
        !           351: stilloob(s)
        !           352: int    s;              /* socket number */
        !           353: {
        !           354:     static struct timeval timeout = { 0 };
        !           355:     fd_set     excepts;
        !           356:     int value;
        !           357: 
        !           358:     do {
        !           359:        FD_ZERO(&excepts);
        !           360:        FD_SET(s, &excepts);
        !           361:        value = select(s+1, (fd_set *)0, (fd_set *)0, &excepts, &timeout);
        !           362:     } while ((value == -1) && (errno == EINTR));
        !           363: 
        !           364:     if (value < 0) {
        !           365:        fatalperror(pty, "select", errno);
        !           366:     }
        !           367:     if (FD_ISSET(s, &excepts)) {
        !           368:        return 1;
        !           369:     } else {
        !           370:        return 0;
        !           371:     }
        !           372: }
        !           373: 
        !           374: /*
        !           375:  * Main loop.  Select from pty and network, and
        !           376:  * hand data to telnet receiver finite state machine.
        !           377:  */
        !           378: telnet(f, p)
        !           379: {
        !           380:        int on = 1;
        !           381:        char hostname[MAXHOSTNAMELEN];
        !           382: 
        !           383:        ioctl(f, FIONBIO, &on);
        !           384:        ioctl(p, FIONBIO, &on);
        !           385: #if    defined(SO_OOBINLINE)
        !           386:        setsockopt(net, SOL_SOCKET, SO_OOBINLINE, &on, sizeof on);
        !           387: #endif /* defined(SO_OOBINLINE) */
        !           388:        signal(SIGTSTP, SIG_IGN);
        !           389:        signal(SIGCHLD, cleanup);
        !           390:        setpgrp(0, 0);
        !           391: 
        !           392:        /*
        !           393:         * Request to do remote echo and to suppress go ahead.
        !           394:         */
        !           395:        if (!myopts[TELOPT_ECHO]) {
        !           396:            dooption(TELOPT_ECHO);
        !           397:        }
        !           398:        if (!myopts[TELOPT_SGA]) {
        !           399:            dooption(TELOPT_SGA);
        !           400:        }
        !           401:        /*
        !           402:         * Is the client side a 4.2 (NOT 4.3) system?  We need to know this
        !           403:         * because 4.2 clients are unable to deal with TCP urgent data.
        !           404:         *
        !           405:         * To find out, we send out a "DO ECHO".  If the remote system
        !           406:         * answers "WILL ECHO" it is probably a 4.2 client, and we note
        !           407:         * that fact ("WILL ECHO" ==> that the client will echo what
        !           408:         * WE, the server, sends it; it does NOT mean that the client will
        !           409:         * echo the terminal input).
        !           410:         */
        !           411:        sprintf(nfrontp, doopt, TELOPT_ECHO);
        !           412:        nfrontp += sizeof doopt-2;
        !           413:        hisopts[TELOPT_ECHO] = OPT_YES_BUT_ALWAYS_LOOK;
        !           414: 
        !           415:        /*
        !           416:         * Show banner that getty never gave.
        !           417:         *
        !           418:         * The banner includes some null's (for TELNET CR disambiguation),
        !           419:         * so we have to be somewhat complicated.
        !           420:         */
        !           421: 
        !           422:        gethostname(hostname, sizeof (hostname));
        !           423: 
        !           424:        bcopy(BANNER1, nfrontp, sizeof BANNER1 -1);
        !           425:        nfrontp += sizeof BANNER1 - 1;
        !           426:        bcopy(hostname, nfrontp, strlen(hostname));
        !           427:        nfrontp += strlen(hostname);
        !           428:        bcopy(BANNER2, nfrontp, sizeof BANNER2 -1);
        !           429:        nfrontp += sizeof BANNER2 - 1;
        !           430: 
        !           431:        /*
        !           432:         * Call telrcv() once to pick up anything received during
        !           433:         * terminal type negotiation.
        !           434:         */
        !           435:        telrcv();
        !           436: 
        !           437:        for (;;) {
        !           438:                fd_set ibits, obits, xbits;
        !           439:                register int c;
        !           440: 
        !           441:                if (ncc < 0 && pcc < 0)
        !           442:                        break;
        !           443: 
        !           444:                FD_ZERO(&ibits);
        !           445:                FD_ZERO(&obits);
        !           446:                FD_ZERO(&xbits);
        !           447:                /*
        !           448:                 * Never look for input if there's still
        !           449:                 * stuff in the corresponding output buffer
        !           450:                 */
        !           451:                if (nfrontp - nbackp || pcc > 0) {
        !           452:                        FD_SET(f, &obits);
        !           453:                } else {
        !           454:                        FD_SET(p, &ibits);
        !           455:                }
        !           456:                if (pfrontp - pbackp || ncc > 0) {
        !           457:                        FD_SET(p, &obits);
        !           458:                } else {
        !           459:                        FD_SET(f, &ibits);
        !           460:                }
        !           461:                if (!SYNCHing) {
        !           462:                        FD_SET(f, &xbits);
        !           463:                }
        !           464:                if ((c = select(16, &ibits, &obits, &xbits,
        !           465:                                                (struct timeval *)0)) < 1) {
        !           466:                        if (c == -1) {
        !           467:                                if (errno == EINTR) {
        !           468:                                        continue;
        !           469:                                }
        !           470:                        }
        !           471:                        sleep(5);
        !           472:                        continue;
        !           473:                }
        !           474: 
        !           475:                /*
        !           476:                 * Any urgent data?
        !           477:                 */
        !           478:                if (FD_ISSET(net, &xbits)) {
        !           479:                    SYNCHing = 1;
        !           480:                }
        !           481: 
        !           482:                /*
        !           483:                 * Something to read from the network...
        !           484:                 */
        !           485:                if (FD_ISSET(net, &ibits)) {
        !           486: #if    !defined(SO_OOBINLINE)
        !           487:                        /*
        !           488:                         * In 4.2 (and 4.3 beta) systems, the
        !           489:                         * OOB indication and data handling in the kernel
        !           490:                         * is such that if two separate TCP Urgent requests
        !           491:                         * come in, one byte of TCP data will be overlaid.
        !           492:                         * This is fatal for Telnet, but we try to live
        !           493:                         * with it.
        !           494:                         *
        !           495:                         * In addition, in 4.2 (and...), a special protocol
        !           496:                         * is needed to pick up the TCP Urgent data in
        !           497:                         * the correct sequence.
        !           498:                         *
        !           499:                         * What we do is:  if we think we are in urgent
        !           500:                         * mode, we look to see if we are "at the mark".
        !           501:                         * If we are, we do an OOB receive.  If we run
        !           502:                         * this twice, we will do the OOB receive twice,
        !           503:                         * but the second will fail, since the second
        !           504:                         * time we were "at the mark", but there wasn't
        !           505:                         * any data there (the kernel doesn't reset
        !           506:                         * "at the mark" until we do a normal read).
        !           507:                         * Once we've read the OOB data, we go ahead
        !           508:                         * and do normal reads.
        !           509:                         *
        !           510:                         * There is also another problem, which is that
        !           511:                         * since the OOB byte we read doesn't put us
        !           512:                         * out of OOB state, and since that byte is most
        !           513:                         * likely the TELNET DM (data mark), we would
        !           514:                         * stay in the TELNET SYNCH (SYNCHing) state.
        !           515:                         * So, clocks to the rescue.  If we've "just"
        !           516:                         * received a DM, then we test for the
        !           517:                         * presence of OOB data when the receive OOB
        !           518:                         * fails (and AFTER we did the normal mode read
        !           519:                         * to clear "at the mark").
        !           520:                         */
        !           521:                    if (SYNCHing) {
        !           522:                        int atmark;
        !           523: 
        !           524:                        ioctl(net, SIOCATMARK, (char *)&atmark);
        !           525:                        if (atmark) {
        !           526:                            ncc = recv(net, netibuf, sizeof (netibuf), MSG_OOB);
        !           527:                            if ((ncc == -1) && (errno == EINVAL)) {
        !           528:                                ncc = read(net, netibuf, sizeof (netibuf));
        !           529:                                if (sequenceIs(didnetreceive, gotDM)) {
        !           530:                                    SYNCHing = stilloob(net);
        !           531:                                }
        !           532:                            }
        !           533:                        } else {
        !           534:                            ncc = read(net, netibuf, sizeof (netibuf));
        !           535:                        }
        !           536:                    } else {
        !           537:                        ncc = read(net, netibuf, sizeof (netibuf));
        !           538:                    }
        !           539:                    settimer(didnetreceive);
        !           540: #else  /* !defined(SO_OOBINLINE)) */
        !           541:                    ncc = read(net, netibuf, sizeof (netibuf));
        !           542: #endif /* !defined(SO_OOBINLINE)) */
        !           543:                    if (ncc < 0 && errno == EWOULDBLOCK)
        !           544:                        ncc = 0;
        !           545:                    else {
        !           546:                        if (ncc <= 0) {
        !           547:                            break;
        !           548:                        }
        !           549:                        netip = netibuf;
        !           550:                    }
        !           551:                }
        !           552: 
        !           553:                /*
        !           554:                 * Something to read from the pty...
        !           555:                 */
        !           556:                if (FD_ISSET(p, &ibits)) {
        !           557:                        pcc = read(p, ptyibuf, BUFSIZ);
        !           558:                        if (pcc < 0 && errno == EWOULDBLOCK)
        !           559:                                pcc = 0;
        !           560:                        else {
        !           561:                                if (pcc <= 0)
        !           562:                                        break;
        !           563:                                ptyip = ptyibuf;
        !           564:                        }
        !           565:                }
        !           566: 
        !           567:                while (pcc > 0) {
        !           568:                        if ((&netobuf[BUFSIZ] - nfrontp) < 2)
        !           569:                                break;
        !           570:                        c = *ptyip++ & 0377, pcc--;
        !           571:                        if (c == IAC)
        !           572:                                *nfrontp++ = c;
        !           573:                        *nfrontp++ = c;
        !           574:                        if (c == '\r') {
        !           575:                                if (pcc > 0 && ((*ptyip & 0377) == '\n')) {
        !           576:                                        *nfrontp++ = *ptyip++ & 0377;
        !           577:                                        pcc--;
        !           578:                                } else
        !           579:                                        *nfrontp++ = '\0';
        !           580:                        }
        !           581:                }
        !           582:                if (FD_ISSET(f, &obits) && (nfrontp - nbackp) > 0)
        !           583:                        netflush();
        !           584:                if (ncc > 0)
        !           585:                        telrcv();
        !           586:                if (FD_ISSET(p, &obits) && (pfrontp - pbackp) > 0)
        !           587:                        ptyflush();
        !           588:        }
        !           589:        cleanup();
        !           590: }
        !           591:        
        !           592: /*
        !           593:  * State for recv fsm
        !           594:  */
        !           595: #define        TS_DATA         0       /* base state */
        !           596: #define        TS_IAC          1       /* look for double IAC's */
        !           597: #define        TS_CR           2       /* CR-LF ->'s CR */
        !           598: #define        TS_SB           3       /* throw away begin's... */
        !           599: #define        TS_SE           4       /* ...end's (suboption negotiation) */
        !           600: #define        TS_WILL         5       /* will option negotiation */
        !           601: #define        TS_WONT         6       /* wont " */
        !           602: #define        TS_DO           7       /* do " */
        !           603: #define        TS_DONT         8       /* dont " */
        !           604: 
        !           605: telrcv()
        !           606: {
        !           607:        register int c;
        !           608:        static int state = TS_DATA;
        !           609: 
        !           610:        while (ncc > 0) {
        !           611:                if ((&ptyobuf[BUFSIZ] - pfrontp) < 2)
        !           612:                        return;
        !           613:                c = *netip++ & 0377, ncc--;
        !           614:                switch (state) {
        !           615: 
        !           616:                case TS_CR:
        !           617:                        state = TS_DATA;
        !           618:                        if ((c == 0) || (c == '\n')) {
        !           619:                                break;
        !           620:                        }
        !           621:                        /* FALL THROUGH */
        !           622: 
        !           623:                case TS_DATA:
        !           624:                        if (c == IAC) {
        !           625:                                state = TS_IAC;
        !           626:                                break;
        !           627:                        }
        !           628:                        if (inter > 0)
        !           629:                                break;
        !           630:                        /*
        !           631:                         * We map \r\n ==> \n, since \r\n says
        !           632:                         * that we want to be in column 1 of the next
        !           633:                         * printable line, and \n is the standard
        !           634:                         * unix way of saying that (\r is only good
        !           635:                         * if CRMOD is set, which it normally is).
        !           636:                         */
        !           637:                        if ((myopts[TELOPT_BINARY] == OPT_NO) && c == '\r') {
        !           638:                                if ((ncc > 0) && ('\n' == *netip)) {
        !           639:                                        netip++; ncc--;
        !           640:                                        c = '\n';
        !           641:                                } else {
        !           642:                                        state = TS_CR;
        !           643:                                }
        !           644:                        }
        !           645:                        *pfrontp++ = c;
        !           646:                        break;
        !           647: 
        !           648:                case TS_IAC:
        !           649:                        switch (c) {
        !           650: 
        !           651:                        /*
        !           652:                         * Send the process on the pty side an
        !           653:                         * interrupt.  Do this with a NULL or
        !           654:                         * interrupt char; depending on the tty mode.
        !           655:                         */
        !           656:                        case IP:
        !           657:                                interrupt();
        !           658:                                break;
        !           659: 
        !           660:                        case BREAK:
        !           661:                                sendbrk();
        !           662:                                break;
        !           663: 
        !           664:                        /*
        !           665:                         * Are You There?
        !           666:                         */
        !           667:                        case AYT:
        !           668:                                strcpy(nfrontp, "\r\n[Yes]\r\n");
        !           669:                                nfrontp += 9;
        !           670:                                break;
        !           671: 
        !           672:                        /*
        !           673:                         * Abort Output
        !           674:                         */
        !           675:                        case AO: {
        !           676:                                        struct ltchars tmpltc;
        !           677: 
        !           678:                                        ptyflush();     /* half-hearted */
        !           679:                                        ioctl(pty, TIOCGLTC, &tmpltc);
        !           680:                                        if (tmpltc.t_flushc != '\377') {
        !           681:                                                *pfrontp++ = tmpltc.t_flushc;
        !           682:                                        }
        !           683:                                        netclear();     /* clear buffer back */
        !           684:                                        *nfrontp++ = IAC;
        !           685:                                        *nfrontp++ = DM;
        !           686:                                        neturg = nfrontp-1; /* off by one XXX */
        !           687:                                        break;
        !           688:                                }
        !           689: 
        !           690:                        /*
        !           691:                         * Erase Character and
        !           692:                         * Erase Line
        !           693:                         */
        !           694:                        case EC:
        !           695:                        case EL: {
        !           696:                                        struct sgttyb b;
        !           697:                                        char ch;
        !           698: 
        !           699:                                        ptyflush();     /* half-hearted */
        !           700:                                        ioctl(pty, TIOCGETP, &b);
        !           701:                                        ch = (c == EC) ?
        !           702:                                                b.sg_erase : b.sg_kill;
        !           703:                                        if (ch != '\377') {
        !           704:                                                *pfrontp++ = ch;
        !           705:                                        }
        !           706:                                        break;
        !           707:                                }
        !           708: 
        !           709:                        /*
        !           710:                         * Check for urgent data...
        !           711:                         */
        !           712:                        case DM:
        !           713:                                SYNCHing = stilloob(net);
        !           714:                                settimer(gotDM);
        !           715:                                break;
        !           716: 
        !           717: 
        !           718:                        /*
        !           719:                         * Begin option subnegotiation...
        !           720:                         */
        !           721:                        case SB:
        !           722:                                state = TS_SB;
        !           723:                                continue;
        !           724: 
        !           725:                        case WILL:
        !           726:                                state = TS_WILL;
        !           727:                                continue;
        !           728: 
        !           729:                        case WONT:
        !           730:                                state = TS_WONT;
        !           731:                                continue;
        !           732: 
        !           733:                        case DO:
        !           734:                                state = TS_DO;
        !           735:                                continue;
        !           736: 
        !           737:                        case DONT:
        !           738:                                state = TS_DONT;
        !           739:                                continue;
        !           740: 
        !           741:                        case IAC:
        !           742:                                *pfrontp++ = c;
        !           743:                                break;
        !           744:                        }
        !           745:                        state = TS_DATA;
        !           746:                        break;
        !           747: 
        !           748:                case TS_SB:
        !           749:                        if (c == IAC) {
        !           750:                                state = TS_SE;
        !           751:                        } else {
        !           752:                                SB_ACCUM(c);
        !           753:                        }
        !           754:                        break;
        !           755: 
        !           756:                case TS_SE:
        !           757:                        if (c != SE) {
        !           758:                                if (c != IAC) {
        !           759:                                        SB_ACCUM(IAC);
        !           760:                                }
        !           761:                                SB_ACCUM(c);
        !           762:                                state = TS_SB;
        !           763:                        } else {
        !           764:                                SB_TERM();
        !           765:                                suboption();    /* handle sub-option */
        !           766:                                state = TS_DATA;
        !           767:                        }
        !           768:                        break;
        !           769: 
        !           770:                case TS_WILL:
        !           771:                        if (hisopts[c] != OPT_YES)
        !           772:                                willoption(c);
        !           773:                        state = TS_DATA;
        !           774:                        continue;
        !           775: 
        !           776:                case TS_WONT:
        !           777:                        if (hisopts[c] != OPT_NO)
        !           778:                                wontoption(c);
        !           779:                        state = TS_DATA;
        !           780:                        continue;
        !           781: 
        !           782:                case TS_DO:
        !           783:                        if (myopts[c] != OPT_YES)
        !           784:                                dooption(c);
        !           785:                        state = TS_DATA;
        !           786:                        continue;
        !           787: 
        !           788:                case TS_DONT:
        !           789:                        if (myopts[c] != OPT_NO) {
        !           790:                                dontoption(c);
        !           791:                        }
        !           792:                        state = TS_DATA;
        !           793:                        continue;
        !           794: 
        !           795:                default:
        !           796:                        syslog(LOG_ERR, "telnetd: panic state=%d\n", state);
        !           797:                        printf("telnetd: panic state=%d\n", state);
        !           798:                        exit(1);
        !           799:                }
        !           800:        }
        !           801: }
        !           802: 
        !           803: willoption(option)
        !           804:        int option;
        !           805: {
        !           806:        char *fmt;
        !           807: 
        !           808:        switch (option) {
        !           809: 
        !           810:        case TELOPT_BINARY:
        !           811:                mode(RAW, 0);
        !           812:                fmt = doopt;
        !           813:                break;
        !           814: 
        !           815:        case TELOPT_ECHO:
        !           816:                not42 = 0;              /* looks like a 4.2 system */
        !           817:                /*
        !           818:                 * Now, in a 4.2 system, to break them out of ECHOing
        !           819:                 * (to the terminal) mode, we need to send a "WILL ECHO".
        !           820:                 * Kludge upon kludge!
        !           821:                 */
        !           822:                if (myopts[TELOPT_ECHO] == OPT_YES) {
        !           823:                    dooption(TELOPT_ECHO);
        !           824:                }
        !           825:                fmt = dont;
        !           826:                break;
        !           827: 
        !           828:        case TELOPT_TTYPE:
        !           829:                settimer(ttypeopt);
        !           830:                if (hisopts[TELOPT_TTYPE] == OPT_YES_BUT_ALWAYS_LOOK) {
        !           831:                    hisopts[TELOPT_TTYPE] = OPT_YES;
        !           832:                    return;
        !           833:                }
        !           834:                fmt = doopt;
        !           835:                break;
        !           836: 
        !           837:        case TELOPT_SGA:
        !           838:                fmt = doopt;
        !           839:                break;
        !           840: 
        !           841:        case TELOPT_TM:
        !           842:                fmt = dont;
        !           843:                break;
        !           844: 
        !           845:        default:
        !           846:                fmt = dont;
        !           847:                break;
        !           848:        }
        !           849:        if (fmt == doopt) {
        !           850:                hisopts[option] = OPT_YES;
        !           851:        } else {
        !           852:                hisopts[option] = OPT_NO;
        !           853:        }
        !           854:        sprintf(nfrontp, fmt, option);
        !           855:        nfrontp += sizeof (dont) - 2;
        !           856: }
        !           857: 
        !           858: wontoption(option)
        !           859:        int option;
        !           860: {
        !           861:        char *fmt;
        !           862: 
        !           863:        switch (option) {
        !           864:        case TELOPT_ECHO:
        !           865:                not42 = 1;              /* doesn't seem to be a 4.2 system */
        !           866:                break;
        !           867: 
        !           868:        case TELOPT_BINARY:
        !           869:                mode(0, RAW);
        !           870:                break;
        !           871: 
        !           872:        case TELOPT_TTYPE:
        !           873:            settimer(ttypeopt);
        !           874:            break;
        !           875:        }
        !           876: 
        !           877:        fmt = dont;
        !           878:        hisopts[option] = OPT_NO;
        !           879:        sprintf(nfrontp, fmt, option);
        !           880:        nfrontp += sizeof (doopt) - 2;
        !           881: }
        !           882: 
        !           883: dooption(option)
        !           884:        int option;
        !           885: {
        !           886:        char *fmt;
        !           887: 
        !           888:        switch (option) {
        !           889: 
        !           890:        case TELOPT_TM:
        !           891:                fmt = wont;
        !           892:                break;
        !           893: 
        !           894:        case TELOPT_ECHO:
        !           895:                mode(ECHO|CRMOD, 0);
        !           896:                fmt = will;
        !           897:                break;
        !           898: 
        !           899:        case TELOPT_BINARY:
        !           900:                mode(RAW, 0);
        !           901:                fmt = will;
        !           902:                break;
        !           903: 
        !           904:        case TELOPT_SGA:
        !           905:                fmt = will;
        !           906:                break;
        !           907: 
        !           908:        default:
        !           909:                fmt = wont;
        !           910:                break;
        !           911:        }
        !           912:        if (fmt == will) {
        !           913:            myopts[option] = OPT_YES;
        !           914:        } else {
        !           915:            myopts[option] = OPT_NO;
        !           916:        }
        !           917:        sprintf(nfrontp, fmt, option);
        !           918:        nfrontp += sizeof (doopt) - 2;
        !           919: }
        !           920: 
        !           921: 
        !           922: dontoption(option)
        !           923: int option;
        !           924: {
        !           925:     char *fmt;
        !           926: 
        !           927:     switch (option) {
        !           928:     case TELOPT_ECHO:          /* we should stop echoing */
        !           929:        mode(0, ECHO|CRMOD);
        !           930:        fmt = wont;
        !           931:        break;
        !           932: 
        !           933:     default:
        !           934:        fmt = wont;
        !           935:        break;
        !           936:     }
        !           937: 
        !           938:     if (fmt = wont) {
        !           939:        myopts[option] = OPT_NO;
        !           940:     } else {
        !           941:        myopts[option] = OPT_YES;
        !           942:     }
        !           943:     sprintf(nfrontp, fmt, option);
        !           944:     nfrontp += sizeof (wont) - 2;
        !           945: }
        !           946: 
        !           947: /*
        !           948:  * suboption()
        !           949:  *
        !           950:  *     Look at the sub-option buffer, and try to be helpful to the other
        !           951:  * side.
        !           952:  *
        !           953:  *     Currently we recognize:
        !           954:  *
        !           955:  *     Terminal type is
        !           956:  */
        !           957: 
        !           958: suboption()
        !           959: {
        !           960:     switch (SB_GET()) {
        !           961:     case TELOPT_TTYPE: {               /* Yaaaay! */
        !           962:        static char terminalname[5+41] = "TERM=";
        !           963: 
        !           964:        settimer(ttypesubopt);
        !           965: 
        !           966:        if (SB_GET() != TELQUAL_IS) {
        !           967:            return;             /* ??? XXX but, this is the most robust */
        !           968:        }
        !           969: 
        !           970:        terminaltype = terminalname+strlen(terminalname);
        !           971: 
        !           972:        while ((terminaltype < (terminalname + sizeof terminalname-1)) &&
        !           973:                                                                    !SB_EOF()) {
        !           974:            register int c;
        !           975: 
        !           976:            c = SB_GET();
        !           977:            if (isupper(c)) {
        !           978:                c = tolower(c);
        !           979:            }
        !           980:            *terminaltype++ = c;    /* accumulate name */
        !           981:        }
        !           982:        *terminaltype = 0;
        !           983:        terminaltype = terminalname;
        !           984:        break;
        !           985:     }
        !           986: 
        !           987:     default:
        !           988:        ;
        !           989:     }
        !           990: }
        !           991: 
        !           992: mode(on, off)
        !           993:        int on, off;
        !           994: {
        !           995:        struct sgttyb b;
        !           996: 
        !           997:        ptyflush();
        !           998:        ioctl(pty, TIOCGETP, &b);
        !           999:        b.sg_flags |= on;
        !          1000:        b.sg_flags &= ~off;
        !          1001:        ioctl(pty, TIOCSETP, &b);
        !          1002: }
        !          1003: 
        !          1004: /*
        !          1005:  * Send interrupt to process on other side of pty.
        !          1006:  * If it is in raw mode, just write NULL;
        !          1007:  * otherwise, write intr char.
        !          1008:  */
        !          1009: interrupt()
        !          1010: {
        !          1011:        struct sgttyb b;
        !          1012:        struct tchars tchars;
        !          1013: 
        !          1014:        ptyflush();     /* half-hearted */
        !          1015:        ioctl(pty, TIOCGETP, &b);
        !          1016:        if (b.sg_flags & RAW) {
        !          1017:                *pfrontp++ = '\0';
        !          1018:                return;
        !          1019:        }
        !          1020:        *pfrontp++ = ioctl(pty, TIOCGETC, &tchars) < 0 ?
        !          1021:                '\177' : tchars.t_intrc;
        !          1022: }
        !          1023: 
        !          1024: /*
        !          1025:  * Send quit to process on other side of pty.
        !          1026:  * If it is in raw mode, just write NULL;
        !          1027:  * otherwise, write quit char.
        !          1028:  */
        !          1029: sendbrk()
        !          1030: {
        !          1031:        struct sgttyb b;
        !          1032:        struct tchars tchars;
        !          1033: 
        !          1034:        ptyflush();     /* half-hearted */
        !          1035:        ioctl(pty, TIOCGETP, &b);
        !          1036:        if (b.sg_flags & RAW) {
        !          1037:                *pfrontp++ = '\0';
        !          1038:                return;
        !          1039:        }
        !          1040:        *pfrontp++ = ioctl(pty, TIOCGETC, &tchars) < 0 ?
        !          1041:                '\034' : tchars.t_quitc;
        !          1042: }
        !          1043: 
        !          1044: ptyflush()
        !          1045: {
        !          1046:        int n;
        !          1047: 
        !          1048:        if ((n = pfrontp - pbackp) > 0)
        !          1049:                n = write(pty, pbackp, n);
        !          1050:        if (n < 0)
        !          1051:                return;
        !          1052:        pbackp += n;
        !          1053:        if (pbackp == pfrontp)
        !          1054:                pbackp = pfrontp = ptyobuf;
        !          1055: }
        !          1056: 
        !          1057: /*
        !          1058:  * nextitem()
        !          1059:  *
        !          1060:  *     Return the address of the next "item" in the TELNET data
        !          1061:  * stream.  This will be the address of the next character if
        !          1062:  * the current address is a user data character, or it will
        !          1063:  * be the address of the character following the TELNET command
        !          1064:  * if the current address is a TELNET IAC ("I Am a Command")
        !          1065:  * character.
        !          1066:  */
        !          1067: 
        !          1068: char *
        !          1069: nextitem(current)
        !          1070: char   *current;
        !          1071: {
        !          1072:     if ((*current&0xff) != IAC) {
        !          1073:        return current+1;
        !          1074:     }
        !          1075:     switch (*(current+1)&0xff) {
        !          1076:     case DO:
        !          1077:     case DONT:
        !          1078:     case WILL:
        !          1079:     case WONT:
        !          1080:        return current+3;
        !          1081:     case SB:           /* loop forever looking for the SE */
        !          1082:        {
        !          1083:            register char *look = current+2;
        !          1084: 
        !          1085:            for (;;) {
        !          1086:                if ((*look++&0xff) == IAC) {
        !          1087:                    if ((*look++&0xff) == SE) {
        !          1088:                        return look;
        !          1089:                    }
        !          1090:                }
        !          1091:            }
        !          1092:        }
        !          1093:     default:
        !          1094:        return current+2;
        !          1095:     }
        !          1096: }
        !          1097: 
        !          1098: 
        !          1099: /*
        !          1100:  * netclear()
        !          1101:  *
        !          1102:  *     We are about to do a TELNET SYNCH operation.  Clear
        !          1103:  * the path to the network.
        !          1104:  *
        !          1105:  *     Things are a bit tricky since we may have sent the first
        !          1106:  * byte or so of a previous TELNET command into the network.
        !          1107:  * So, we have to scan the network buffer from the beginning
        !          1108:  * until we are up to where we want to be.
        !          1109:  *
        !          1110:  *     A side effect of what we do, just to keep things
        !          1111:  * simple, is to clear the urgent data pointer.  The principal
        !          1112:  * caller should be setting the urgent data pointer AFTER calling
        !          1113:  * us in any case.
        !          1114:  */
        !          1115: 
        !          1116: netclear()
        !          1117: {
        !          1118:     register char *thisitem, *next;
        !          1119:     char *good;
        !          1120: #define        wewant(p)       ((nfrontp > p) && ((*p&0xff) == IAC) && \
        !          1121:                                ((*(p+1)&0xff) != EC) && ((*(p+1)&0xff) != EL))
        !          1122: 
        !          1123:     thisitem = netobuf;
        !          1124: 
        !          1125:     while ((next = nextitem(thisitem)) <= nbackp) {
        !          1126:        thisitem = next;
        !          1127:     }
        !          1128: 
        !          1129:     /* Now, thisitem is first before/at boundary. */
        !          1130: 
        !          1131:     good = netobuf;    /* where the good bytes go */
        !          1132: 
        !          1133:     while (nfrontp > thisitem) {
        !          1134:        if (wewant(thisitem)) {
        !          1135:            int length;
        !          1136: 
        !          1137:            next = thisitem;
        !          1138:            do {
        !          1139:                next = nextitem(next);
        !          1140:            } while (wewant(next) && (nfrontp > next));
        !          1141:            length = next-thisitem;
        !          1142:            bcopy(thisitem, good, length);
        !          1143:            good += length;
        !          1144:            thisitem = next;
        !          1145:        } else {
        !          1146:            thisitem = nextitem(thisitem);
        !          1147:        }
        !          1148:     }
        !          1149: 
        !          1150:     nbackp = netobuf;
        !          1151:     nfrontp = good;            /* next byte to be sent */
        !          1152:     neturg = 0;
        !          1153: }
        !          1154: 
        !          1155: /*
        !          1156:  *  netflush
        !          1157:  *             Send as much data as possible to the network,
        !          1158:  *     handling requests for urgent data.
        !          1159:  */
        !          1160: 
        !          1161: 
        !          1162: netflush()
        !          1163: {
        !          1164:     int n;
        !          1165: 
        !          1166:     if ((n = nfrontp - nbackp) > 0) {
        !          1167:        /*
        !          1168:         * if no urgent data, or if the other side appears to be an
        !          1169:         * old 4.2 client (and thus unable to survive TCP urgent data),
        !          1170:         * write the entire buffer in non-OOB mode.
        !          1171:         */
        !          1172:        if ((neturg == 0) || (not42 == 0)) {
        !          1173:            n = write(net, nbackp, n);  /* normal write */
        !          1174:        } else {
        !          1175:            n = neturg - nbackp;
        !          1176:            /*
        !          1177:             * In 4.2 (and 4.3) systems, there is some question about
        !          1178:             * what byte in a sendOOB operation is the "OOB" data.
        !          1179:             * To make ourselves compatible, we only send ONE byte
        !          1180:             * out of band, the one WE THINK should be OOB (though
        !          1181:             * we really have more the TCP philosophy of urgent data
        !          1182:             * rather than the Unix philosophy of OOB data).
        !          1183:             */
        !          1184:            if (n > 1) {
        !          1185:                n = send(net, nbackp, n-1, 0);  /* send URGENT all by itself */
        !          1186:            } else {
        !          1187:                n = send(net, nbackp, n, MSG_OOB);      /* URGENT data */
        !          1188:            }
        !          1189:        }
        !          1190:     }
        !          1191:     if (n < 0) {
        !          1192:        if (errno == EWOULDBLOCK)
        !          1193:            return;
        !          1194:        /* should blow this guy away... */
        !          1195:        return;
        !          1196:     }
        !          1197:     nbackp += n;
        !          1198:     if (nbackp >= neturg) {
        !          1199:        neturg = 0;
        !          1200:     }
        !          1201:     if (nbackp == nfrontp) {
        !          1202:        nbackp = nfrontp = netobuf;
        !          1203:     }
        !          1204: }
        !          1205: 
        !          1206: cleanup()
        !          1207: {
        !          1208: 
        !          1209:        rmut();
        !          1210:        vhangup();      /* XXX */
        !          1211:        shutdown(net, 2);
        !          1212:        exit(1);
        !          1213: }
        !          1214: 
        !          1215: #include <utmp.h>
        !          1216: 
        !          1217: struct utmp wtmp;
        !          1218: char   wtmpf[] = "/usr/adm/wtmp";
        !          1219: char   utmpf[] = "/etc/utmp";
        !          1220: #define SCPYN(a, b)    strncpy(a, b, sizeof(a))
        !          1221: #define SCMPN(a, b)    strncmp(a, b, sizeof(a))
        !          1222: 
        !          1223: rmut()
        !          1224: {
        !          1225:        register f;
        !          1226:        int found = 0;
        !          1227:        struct utmp *u, *utmp;
        !          1228:        int nutmp;
        !          1229:        struct stat statbf;
        !          1230: 
        !          1231:        f = open(utmpf, O_RDWR);
        !          1232:        if (f >= 0) {
        !          1233:                fstat(f, &statbf);
        !          1234:                utmp = (struct utmp *)malloc(statbf.st_size);
        !          1235:                if (!utmp)
        !          1236:                        syslog(LOG_ERR, "utmp malloc failed");
        !          1237:                if (statbf.st_size && utmp) {
        !          1238:                        nutmp = read(f, utmp, statbf.st_size);
        !          1239:                        nutmp /= sizeof(struct utmp);
        !          1240:                
        !          1241:                        for (u = utmp ; u < &utmp[nutmp] ; u++) {
        !          1242:                                if (SCMPN(u->ut_line, line+5) ||
        !          1243:                                    u->ut_name[0]==0)
        !          1244:                                        continue;
        !          1245:                                lseek(f, ((long)u)-((long)utmp), L_SET);
        !          1246:                                SCPYN(u->ut_name, "");
        !          1247:                                SCPYN(u->ut_host, "");
        !          1248:                                time(&u->ut_time);
        !          1249:                                write(f, (char *)u, sizeof(wtmp));
        !          1250:                                found++;
        !          1251:                        }
        !          1252:                }
        !          1253:                close(f);
        !          1254:        }
        !          1255:        if (found) {
        !          1256:                f = open(wtmpf, O_WRONLY|O_APPEND);
        !          1257:                if (f >= 0) {
        !          1258:                        SCPYN(wtmp.ut_line, line+5);
        !          1259:                        SCPYN(wtmp.ut_name, "");
        !          1260:                        SCPYN(wtmp.ut_host, "");
        !          1261:                        time(&wtmp.ut_time);
        !          1262:                        write(f, (char *)&wtmp, sizeof(wtmp));
        !          1263:                        close(f);
        !          1264:                }
        !          1265:        }
        !          1266:        chmod(line, 0666);
        !          1267:        chown(line, 0, 0);
        !          1268:        line[strlen("/dev/")] = 'p';
        !          1269:        chmod(line, 0666);
        !          1270:        chown(line, 0, 0);
        !          1271: }

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.