Annotation of 43BSDTahoe/etc/timed/timed.c, revision 1.1.1.1

1.1       root        1: /*
                      2:  * Copyright (c) 1985 Regents of the University of California.
                      3:  * All rights reserved.
                      4:  *
                      5:  * Redistribution and use in source and binary forms are permitted
                      6:  * provided that the above copyright notice and this paragraph are
                      7:  * duplicated in all such forms and that any documentation,
                      8:  * advertising materials, and other materials related to such
                      9:  * distribution and use acknowledge that the software was developed
                     10:  * by the University of California, Berkeley.  The name of the
                     11:  * University may not be used to endorse or promote products derived
                     12:  * from this software without specific prior written permission.
                     13:  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
                     14:  * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
                     15:  * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
                     16:  */
                     17: 
                     18: #ifndef lint
                     19: char copyright[] =
                     20: "@(#) Copyright (c) 1985 Regents of the University of California.\n\
                     21:  All rights reserved.\n";
                     22: #endif /* not lint */
                     23: 
                     24: #ifndef lint
                     25: static char sccsid[] = "@(#)timed.c    2.14 (Berkeley) 6/18/88";
                     26: #endif /* not lint */
                     27: 
                     28: #include "globals.h"
                     29: #define TSPTYPES
                     30: #include <protocols/timed.h>
                     31: #include <net/if.h>
                     32: #include <sys/file.h>
                     33: #include <sys/ioctl.h>
                     34: #include <setjmp.h>
                     35: 
                     36: int id;
                     37: int trace;
                     38: int sock, sock_raw = -1;
                     39: int status = 0;
                     40: int backoff;
                     41: int slvcount;                          /* no. of slaves controlled by master */
                     42: int machup;
                     43: u_short sequence;                      /* sequence number */
                     44: long delay1;
                     45: long delay2;
                     46: long random();
                     47: char hostname[MAXHOSTNAMELEN];
                     48: struct host hp[NHOSTS];
                     49: char tracefile[] = "/usr/adm/timed.log";
                     50: FILE *fd;
                     51: jmp_buf jmpenv;
                     52: struct netinfo *nettab = NULL;
                     53: int nslavenets;                /* Number of networks were I could be a slave */
                     54: int nmasternets;       /* Number of networks were I could be a master */
                     55: int nignorednets;      /* Number of ignored networks */
                     56: int nnets;             /* Number of networks I am connected to */
                     57: struct netinfo *slavenet;
                     58: struct netinfo *firstslavenet();
                     59: int Mflag;
                     60: int justquit = 0;
                     61: 
                     62: struct nets {
                     63:        char *name;
                     64:        long net;
                     65:        struct nets *next;
                     66: } *nets = (struct nets *)0;
                     67: 
                     68: /*
                     69:  * The timedaemons synchronize the clocks of hosts in a local area network.
                     70:  * One daemon runs as master, all the others as slaves. The master
                     71:  * performs the task of computing clock differences and sends correction
                     72:  * values to the slaves. 
                     73:  * Slaves start an election to choose a new master when the latter disappears 
                     74:  * because of a machine crash, network partition, or when killed.
                     75:  * A resolution protocol is used to kill all but one of the masters
                     76:  * that happen to exist in segments of a partitioned network when the 
                     77:  * network partition is fixed.
                     78:  *
                     79:  * Authors: Riccardo Gusella & Stefano Zatti
                     80:  */
                     81: 
                     82: main(argc, argv)
                     83: int argc;
                     84: char **argv;
                     85: {
                     86:        int on;
                     87:        int ret;
                     88:        long seed;
                     89:        int nflag, iflag;
                     90:        struct timeval time;
                     91:        struct servent *srvp;
                     92:        long casual();
                     93:        char *date();
                     94:        int n;
                     95:        int flag;
                     96:        char buf[BUFSIZ];
                     97:        struct ifconf ifc;
                     98:        struct ifreq ifreq, *ifr;
                     99:        register struct netinfo *ntp;
                    100:        struct netinfo *ntip;
                    101:        struct netinfo *savefromnet;
                    102:        struct sockaddr_in server;
                    103:        u_short port;
                    104:        uid_t getuid();
                    105: 
                    106: #ifdef lint
                    107:        ntip = NULL;
                    108: #endif
                    109: 
                    110:        Mflag = 0;
                    111:        on = 1;
                    112:        backoff = 1;
                    113:        trace = OFF;
                    114:        nflag = OFF;
                    115:        iflag = OFF;
                    116:        openlog("timed", LOG_CONS|LOG_PID, LOG_DAEMON);
                    117: 
                    118:        if (getuid() != 0) {
                    119:                fprintf(stderr, "Timed: not superuser\n");
                    120:                exit(1);
                    121:        }
                    122: 
                    123:        while (--argc > 0 && **++argv == '-') {
                    124:                (*argv)++;
                    125:                do {
                    126:                        switch (**argv) {
                    127: 
                    128:                        case 'M':
                    129:                                Mflag = 1; 
                    130:                                break;
                    131:                        case 't':
                    132:                                trace = ON; 
                    133:                                break;
                    134:                        case 'n':
                    135:                                argc--, argv++;
                    136:                                if (iflag) {
                    137:                                        fprintf(stderr,
                    138:                                    "timed: -i and -n make no sense together\n");
                    139:                                } else {
                    140:                                        nflag = ON;
                    141:                                        addnetname(*argv);
                    142:                                }
                    143:                                while (*(++(*argv)+1)) ;
                    144:                                break;
                    145:                        case 'i':
                    146:                                argc--, argv++;
                    147:                                if (nflag) {
                    148:                                        fprintf(stderr,
                    149:                                    "timed: -i and -n make no sense together\n");
                    150:                                } else {
                    151:                                        iflag = ON;
                    152:                                        addnetname(*argv);
                    153:                                }
                    154:                                while (*(++(*argv)+1)) ;
                    155:                                break;
                    156:                        default:
                    157:                                fprintf(stderr, "timed: -%c: unknown option\n", 
                    158:                                                        **argv);
                    159:                                break;
                    160:                        }
                    161:                } while (*++(*argv));
                    162:        }
                    163: 
                    164: #ifndef DEBUG
                    165:        if (fork())
                    166:                exit(0);
                    167:        { int s;
                    168:          for (s = getdtablesize(); s >= 0; --s)
                    169:                (void) close(s);
                    170:          (void) open("/dev/null", 0);
                    171:          (void) dup2(0, 1);
                    172:          (void) dup2(0, 2);
                    173:          s = open("/dev/tty", 2);
                    174:          if (s >= 0) {
                    175:                (void) ioctl(s, TIOCNOTTY, (char *)0);
                    176:                (void) close(s);
                    177:          }
                    178:        }
                    179: #endif
                    180: 
                    181:        if (trace == ON) {
                    182:                fd = fopen(tracefile, "w");
                    183:                setlinebuf(fd);
                    184:                fprintf(fd, "Tracing started on: %s\n\n", 
                    185:                                        date());
                    186:        }
                    187: 
                    188:        srvp = getservbyname("timed", "udp");
                    189:        if (srvp == 0) {
                    190:                syslog(LOG_CRIT, "unknown service 'timed/udp'");
                    191:                exit(1);
                    192:        }
                    193:        port = srvp->s_port;
                    194:        server.sin_port = srvp->s_port;
                    195:        server.sin_family = AF_INET;
                    196:        sock = socket(AF_INET, SOCK_DGRAM, 0);
                    197:        if (sock < 0) {
                    198:                syslog(LOG_ERR, "socket: %m");
                    199:                exit(1);
                    200:        }
                    201:        if (setsockopt(sock, SOL_SOCKET, SO_BROADCAST, (char *)&on, 
                    202:                                                        sizeof(on)) < 0) {
                    203:                syslog(LOG_ERR, "setsockopt: %m");
                    204:                exit(1);
                    205:        }
                    206:        if (bind(sock, &server, sizeof(server))) {
                    207:                if (errno == EADDRINUSE)
                    208:                        syslog(LOG_ERR, "server already running");
                    209:                else
                    210:                        syslog(LOG_ERR, "bind: %m");
                    211:                exit(1);
                    212:        }
                    213: 
                    214:        /* choose a unique seed for random number generation */
                    215:        (void)gettimeofday(&time, (struct timezone *)0);
                    216:        seed = time.tv_sec + time.tv_usec;
                    217:        srandom(seed);
                    218: 
                    219:        sequence = random();     /* initial seq number */
                    220: 
                    221:        /* rounds kernel variable time to multiple of 5 ms. */
                    222:        time.tv_sec = 0;
                    223:        time.tv_usec = -((time.tv_usec/1000) % 5) * 1000;
                    224:        (void)adjtime(&time, (struct timeval *)0);
                    225: 
                    226:        id = getpid();
                    227: 
                    228:        if (gethostname(hostname, sizeof(hostname) - 1) < 0) {
                    229:                syslog(LOG_ERR, "gethostname: %m");
                    230:                exit(1);
                    231:        }
                    232:        hp[0].name = hostname;
                    233: 
                    234:        if (nflag || iflag) {
                    235:                struct netent *getnetent();
                    236:                struct netent *n;
                    237:                struct nets *np;
                    238:                for ( np = nets ; np ; np = np->next) {
                    239:                        n = getnetbyname(np->name);
                    240:                        if (n == NULL) {
                    241:                                syslog(LOG_ERR, "getnetbyname: unknown net %s",
                    242:                                        np->name);
                    243:                                exit(1);
                    244:                        }
                    245:                        np->net = n->n_net;
                    246:                }
                    247:        }
                    248:        ifc.ifc_len = sizeof(buf);
                    249:        ifc.ifc_buf = buf;
                    250:        if (ioctl(sock, SIOCGIFCONF, (char *)&ifc) < 0) {
                    251:                syslog(LOG_ERR, "get interface configuration: %m");
                    252:                exit(1);
                    253:        }
                    254:        n = ifc.ifc_len/sizeof(struct ifreq);
                    255:        ntp = NULL;
                    256:        for (ifr = ifc.ifc_req; n > 0; n--, ifr++) {
                    257:                if (ifr->ifr_addr.sa_family != AF_INET)
                    258:                        continue;
                    259:                ifreq = *ifr;
                    260:                if (ntp == NULL)
                    261:                        ntp = (struct netinfo *)malloc(sizeof(struct netinfo));
                    262:                ntp->my_addr = 
                    263:                        ((struct sockaddr_in *)&ifreq.ifr_addr)->sin_addr;
                    264:                if (ioctl(sock, SIOCGIFFLAGS, 
                    265:                                        (char *)&ifreq) < 0) {
                    266:                        syslog(LOG_ERR, "get interface flags: %m");
                    267:                        continue;
                    268:                }
                    269:                if ((ifreq.ifr_flags & IFF_UP) == 0 ||
                    270:                        ((ifreq.ifr_flags & IFF_BROADCAST) == 0 &&
                    271:                        (ifreq.ifr_flags & IFF_POINTOPOINT) == 0)) {
                    272:                        continue;
                    273:                }
                    274:                if (ifreq.ifr_flags & IFF_BROADCAST)
                    275:                        flag = 1;
                    276:                else
                    277:                        flag = 0;
                    278:                if (ioctl(sock, SIOCGIFNETMASK, 
                    279:                                        (char *)&ifreq) < 0) {
                    280:                        syslog(LOG_ERR, "get netmask: %m");
                    281:                        continue;
                    282:                }
                    283:                ntp->mask = ((struct sockaddr_in *)
                    284:                        &ifreq.ifr_addr)->sin_addr.s_addr;
                    285:                if (flag) {
                    286:                        if (ioctl(sock, SIOCGIFBRDADDR, 
                    287:                                                (char *)&ifreq) < 0) {
                    288:                                syslog(LOG_ERR, "get broadaddr: %m");
                    289:                                continue;
                    290:                        }
                    291:                        ntp->dest_addr = *(struct sockaddr_in *)&ifreq.ifr_broadaddr;
                    292:                } else {
                    293:                        if (ioctl(sock, SIOCGIFDSTADDR, 
                    294:                                                (char *)&ifreq) < 0) {
                    295:                                syslog(LOG_ERR, "get destaddr: %m");
                    296:                                continue;
                    297:                        }
                    298:                        ntp->dest_addr = *(struct sockaddr_in *)&ifreq.ifr_dstaddr;
                    299:                }
                    300:                ntp->dest_addr.sin_port = port;
                    301:                if (nflag || iflag) {
                    302:                        u_long addr, mask;
                    303:                        struct nets *n;
                    304: 
                    305:                        addr = ntohl(ntp->dest_addr.sin_addr.s_addr);
                    306:                        mask = ntohl(ntp->mask);
                    307:                        while ((mask & 1) == 0) {
                    308:                                addr >>= 1;
                    309:                                mask >>= 1;
                    310:                        }
                    311:                        for (n = nets ; n ; n = n->next)
                    312:                                if (addr == n->net)
                    313:                                        break;
                    314:                        if (nflag && !n || iflag && n)
                    315:                                continue;
                    316:                }
                    317:                ntp->net = ntp->mask & ntp->dest_addr.sin_addr.s_addr;
                    318:                ntp->next = NULL;
                    319:                if (nettab == NULL) {
                    320:                        nettab = ntp;
                    321:                } else {
                    322:                        ntip->next = ntp;
                    323:                }
                    324:                ntip = ntp;
                    325:                ntp = NULL;
                    326:        }
                    327:        if (ntp)
                    328:                (void) free((char *)ntp);
                    329:        if (nettab == NULL) {
                    330:                syslog(LOG_ERR, "No network usable");
                    331:                exit(1);
                    332:        }
                    333: 
                    334:        for (ntp = nettab; ntp != NULL; ntp = ntp->next)
                    335:                lookformaster(ntp);
                    336:        setstatus();
                    337:        /*
                    338:         * Take care of some basic initialization.
                    339:         */
                    340:        /* us. delay to be used in response to broadcast */
                    341:        delay1 = casual((long)10000, 200000);   
                    342: 
                    343:        /* election timer delay in secs. */
                    344:        delay2 = casual((long)MINTOUT, (long)MAXTOUT);
                    345: 
                    346:        if (Mflag) {
                    347:                /*
                    348:                 * number (increased by 1) of slaves controlled by master: 
                    349:                 * used in master.c, candidate.c, networkdelta.c, and 
                    350:                 * correct.c 
                    351:                 */
                    352:                slvcount = 1;
                    353:                ret = setjmp(jmpenv);
                    354: 
                    355:                switch (ret) {
                    356: 
                    357:                case 0: 
                    358:                        makeslave(firstslavenet());
                    359:                        setstatus();
                    360:                        break;
                    361:                case 1: 
                    362:                        /* Just lost our master */
                    363:                        setstatus();
                    364:                        slavenet->status = election(slavenet);
                    365:                        checkignorednets();
                    366:                        setstatus();
                    367:                        if (slavenet->status == MASTER)
                    368:                                makeslave(firstslavenet());
                    369:                        else
                    370:                                makeslave(slavenet);
                    371:                        setstatus();
                    372:                        break;
                    373:                case 2:
                    374:                        /* Just been told to quit */
                    375:                        fromnet->status = SLAVE;
                    376:                        setstatus();
                    377:                        savefromnet = fromnet;
                    378:                        rmnetmachs(fromnet);
                    379:                        checkignorednets();
                    380:                        if (slavenet)
                    381:                                makeslave(slavenet);
                    382:                        else
                    383:                                makeslave(savefromnet);
                    384:                        setstatus();
                    385:                        justquit = 1;
                    386:                        break;
                    387:                        
                    388:                default:
                    389:                        /* this should not happen */
                    390:                        syslog(LOG_ERR, "Attempt to enter invalid state");
                    391:                        break;
                    392:                }
                    393:                        
                    394:                if (status & MASTER) {
                    395:                        /* open raw socket used to measure time differences */
                    396:                        if (sock_raw == -1) {
                    397:                            sock_raw = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP); 
                    398:                            if (sock_raw < 0)  {
                    399:                                    syslog(LOG_ERR, "opening raw socket: %m");
                    400:                                    exit (1);
                    401:                            }
                    402:                        }
                    403:                } else {
                    404:                        /* sock_raw is not being used now */
                    405:                        if (sock_raw != -1) {
                    406:                            (void)close(sock_raw);
                    407:                            sock_raw = -1;
                    408:                        }
                    409:                }
                    410: 
                    411:                if (status == MASTER) 
                    412:                        master();
                    413:                else 
                    414:                        slave();
                    415:        } else {
                    416:                /* if Mflag is not set timedaemon is forced to act as a slave */
                    417:                status = SLAVE;
                    418:                if (setjmp(jmpenv)) {
                    419:                        setstatus();
                    420:                        checkignorednets();
                    421:                }
                    422:                makeslave(firstslavenet());
                    423:                for (ntp = nettab; ntp != NULL; ntp = ntp->next)
                    424:                        if (ntp->status == MASTER)
                    425:                                ntp->status = IGNORE;
                    426:                setstatus();
                    427:                slave();
                    428:        }
                    429: }
                    430: 
                    431: /*
                    432:  * Try to become master over ignored nets..
                    433:  */
                    434: checkignorednets()
                    435: {
                    436:        register struct netinfo *ntp;
                    437:        for (ntp = nettab; ntp != NULL; ntp = ntp->next)
                    438:                if (ntp->status == IGNORE)
                    439:                        lookformaster(ntp);
                    440: }
                    441: 
                    442: lookformaster(ntp)
                    443:        register struct netinfo *ntp;
                    444: {
                    445:        struct tsp resp, conflict, *answer, *readmsg(), *acksend();
                    446:        struct timeval time;
                    447:        char mastername[MAXHOSTNAMELEN];
                    448:        struct sockaddr_in masteraddr;
                    449: 
                    450:        ntp->status = SLAVE;
                    451:        /* look for master */
                    452:        resp.tsp_type = TSP_MASTERREQ;
                    453:        (void)strcpy(resp.tsp_name, hostname);
                    454:        answer = acksend(&resp, &ntp->dest_addr, (char *)ANYADDR, 
                    455:            TSP_MASTERACK, ntp);
                    456:        if (answer == NULL) {
                    457:                /*
                    458:                 * Various conditions can cause conflict: race between
                    459:                 * two just started timedaemons when no master is
                    460:                 * present, or timedaemon started during an election.
                    461:                 * Conservative approach is taken: give up and became a
                    462:                 * slave postponing election of a master until first
                    463:                 * timer expires.
                    464:                 */
                    465:                time.tv_sec = time.tv_usec = 0;
                    466:                answer = readmsg(TSP_MASTERREQ, (char *)ANYADDR,
                    467:                    &time, ntp);
                    468:                if (answer != NULL) {
                    469:                        ntp->status = SLAVE;
                    470:                        return;
                    471:                }
                    472: 
                    473:                time.tv_sec = time.tv_usec = 0;
                    474:                answer = readmsg(TSP_MASTERUP, (char *)ANYADDR,
                    475:                    &time, ntp);
                    476:                if (answer != NULL) {
                    477:                        ntp->status = SLAVE;
                    478:                        return;
                    479:                }
                    480: 
                    481:                time.tv_sec = time.tv_usec = 0;
                    482:                answer = readmsg(TSP_ELECTION, (char *)ANYADDR,
                    483:                    &time, ntp);
                    484:                if (answer != NULL) {
                    485:                        ntp->status = SLAVE;
                    486:                        return;
                    487:                }
                    488:                ntp->status = MASTER;
                    489:        } else {
                    490:                (void)strcpy(mastername, answer->tsp_name);
                    491:                masteraddr = from;
                    492: 
                    493:                /*
                    494:                 * If network has been partitioned, there might be other
                    495:                 * masters; tell the one we have just acknowledged that 
                    496:                 * it has to gain control over the others. 
                    497:                 */
                    498:                time.tv_sec = 0;
                    499:                time.tv_usec = 300000;
                    500:                answer = readmsg(TSP_MASTERACK, (char *)ANYADDR, &time,
                    501:                    ntp);
                    502:                /*
                    503:                 * checking also not to send CONFLICT to ack'ed master
                    504:                 * due to duplicated MASTERACKs
                    505:                 */
                    506:                if (answer != NULL && 
                    507:                    strcmp(answer->tsp_name, mastername) != 0) {
                    508:                        conflict.tsp_type = TSP_CONFLICT;
                    509:                        (void)strcpy(conflict.tsp_name, hostname);
                    510:                        if (acksend(&conflict, &masteraddr, mastername,
                    511:                            TSP_ACK, (struct netinfo *)NULL) == NULL) {
                    512:                                syslog(LOG_ERR, 
                    513:                                    "error on sending TSP_CONFLICT");
                    514:                                exit(1);
                    515:                        }
                    516:                }
                    517:        }
                    518: }
                    519: /*
                    520:  * based on the current network configuration, set the status, and count
                    521:  * networks;
                    522:  */
                    523: setstatus()
                    524: {
                    525:        register struct netinfo *ntp;
                    526: 
                    527:        status = 0;
                    528:        nmasternets = nslavenets = nnets = nignorednets = 0;
                    529:        if (trace)
                    530:                fprintf(fd, "Net status:\n");
                    531:        for (ntp = nettab; ntp != NULL; ntp = ntp->next) {
                    532:                switch ((int)ntp->status) {
                    533:                  case MASTER:
                    534:                        nmasternets++;
                    535:                        break;
                    536:                  case SLAVE:
                    537:                        nslavenets++;
                    538:                        break;
                    539:                  case IGNORE:
                    540:                        nignorednets++;
                    541:                        break;
                    542:                }
                    543:                if (trace) {
                    544:                        fprintf(fd, "\t%-16s", inet_ntoa(ntp->net));
                    545:                        switch ((int)ntp->status) {
                    546:                          case MASTER:
                    547:                                fprintf(fd, "MASTER\n");
                    548:                                break;
                    549:                          case SLAVE:
                    550:                                fprintf(fd, "SLAVE\n");
                    551:                                break;
                    552:                          case IGNORE:
                    553:                                fprintf(fd, "IGNORE\n");
                    554:                                break;
                    555:                          default:
                    556:                                fprintf(fd, "invalid state %d\n",(int)ntp->status);
                    557:                                break;
                    558:                        }
                    559:                }
                    560:                nnets++;
                    561:                status |= ntp->status;
                    562:        }
                    563:        status &= ~IGNORE;
                    564:        if (trace)
                    565:                fprintf(fd,
                    566:                      "\tnets = %d, masters = %d, slaves = %d, ignored = %d\n",
                    567:                      nnets, nmasternets, nslavenets, nignorednets);
                    568: }
                    569: 
                    570: makeslave(net)
                    571:        struct netinfo *net;
                    572: {
                    573:        register struct netinfo *ntp;
                    574: 
                    575:        for (ntp = nettab; ntp != NULL; ntp = ntp->next)
                    576:                if (ntp->status == SLAVE && ntp != net)
                    577:                        ntp->status = IGNORE;
                    578:        slavenet = net;
                    579: }
                    580:        
                    581: struct netinfo *
                    582: firstslavenet()
                    583: {
                    584:        register struct netinfo *ntp;
                    585: 
                    586:        for (ntp = nettab; ntp != NULL; ntp = ntp->next)
                    587:                if (ntp->status == SLAVE)
                    588:                        return (ntp);
                    589:        return ((struct netinfo *)0);
                    590: }
                    591: 
                    592: /*
                    593:  * `casual' returns a random number in the range [inf, sup]
                    594:  */
                    595: 
                    596: long
                    597: casual(inf, sup)
                    598: long inf;
                    599: long sup;
                    600: {
                    601:        float value;
                    602: 
                    603:        value = (float)(random() & 0x7fffffff) / 0x7fffffff;
                    604:        return(inf + (sup - inf) * value);
                    605: }
                    606: 
                    607: char *
                    608: date()
                    609: {
                    610:        char    *ctime();
                    611:        struct  timeval tv;
                    612: 
                    613:        (void)gettimeofday(&tv, (struct timezone *)0);
                    614:        return (ctime(&tv.tv_sec));
                    615: }
                    616: 
                    617: addnetname(name)
                    618:        char *name;
                    619: {
                    620:        register struct nets **netlist = &nets;
                    621: 
                    622:        while (*netlist)
                    623:                netlist = &((*netlist)->next);
                    624:        *netlist = (struct nets *)malloc(sizeof **netlist);
                    625:        if (*netlist == (struct nets *)0) {
                    626:                syslog(LOG_ERR, "malloc failed");
                    627:                exit(1);
                    628:        }
                    629:        bzero((char *)*netlist, sizeof(**netlist));
                    630:        (*netlist)->name = name;
                    631: }

unix.superglobalmegacorp.com

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