Annotation of 41BSD/cmd/berknet/netdaemon.c, revision 1.1.1.1

1.1       root        1: /*
                      2:        The daemon program that runs the network.
                      3: 
                      4: Usage:
                      5:        netdaemon -m mach [-r readfd] [-w writefd] [-d] [-h]
                      6:                [-os] [-or] [-ou num]
                      7: 
                      8: Must be started by root.
                      9: Options:
                     10:        -m mach         remote machine is mach (required)
                     11:        -d              turn debugging on
                     12:        -r num          if simulute w/pipes, read from num
                     13:        -w num          if simulate w/pipes, write on num
                     14:        -h              use high-speed link (not implemented yet)
                     15:        -os             only send
                     16:        -or             only receive
                     17:        -ou num         only send things with uid = num
                     18:        -p num          length of packet
                     19: */
                     20: 
                     21: # include "defs.h"
                     22: /* take a time, adjust to be in PST, and divide by no of secs in a day */
                     23: /* adjust by 10 mins, and day is considered to begin at 3AM */
                     24: /* 6*3600 = 21600 +17400 = 39000 */
                     25: /* number of seconds in a day, usually 86400L */
                     26: # define nsecday 86400L
                     27: /* number of days since time began */
                     28: # define numdays(S) ((S - 39000L)/nsecday)
                     29: /* set my priority to normal */
                     30: # define RENICE (nice(-40), nice(20), nice(0))
                     31: 
                     32: /* global variables */
                     33: extern char **environ;
                     34: struct dumpstruc dump;
                     35: struct bstruct btable[];
                     36: struct daemonparms netd;
                     37: struct userinfo status;
                     38: 
                     39: /* local variables */
                     40: static long length;
                     41: static FILE *dir;
                     42: /* static char sheader[] =             "ABCDE"; */
                     43: static char tempfile[]=        TEMPFILE;
                     44: static char publogfile[]=      PUBLOGFILE;
                     45: static struct stat statbuf;
                     46: static struct direct dirbuf;
                     47: int handlekill();
                     48: static char frommach;
                     49: long linechars();
                     50: 
                     51: main(argc,argv)
                     52:   char **argv; {
                     53:        register int i;
                     54:        long ltime,t;
                     55:        char buf[100];
                     56: 
                     57:        nice(-1);
                     58:        signal(SIGTERM,handlekill);
                     59:        debugflg = DBV;
                     60:        setupdaemon(argc,argv);
                     61:        /* now running alone as a daemon */
                     62:                /*
                     63:                for(i=0; i<15; i++)close(i);
                     64:                signal(SIGHUP,SIG_IGN);
                     65:                signal(SIGQUIT,SIG_IGN);
                     66:                signal(SIGINT,SIG_IGN);
                     67:                */
                     68:        senddir[strlen(senddir)-1] = remote;            /* choose dir */
                     69:        if(chdir(senddir) < 0){
                     70:                perror(senddir);
                     71:                exit(EX_OSFILE);
                     72:                }
                     73:        dir = fopen(senddir,"r");
                     74:        if(dir == NULL){
                     75:                perror(senddir);
                     76:                exit(EX_OSFILE);
                     77:                }
                     78:        mktemp(tempfile);
                     79:        tempfile[strlen(tempfile) - 7] = remote;
                     80:        ltime = gettime();
                     81:        if(ltime == 0L)
                     82:                fprintf(stderr,"The network says 'The clock is set wrong.'\n");
                     83:        sprintf(buf,"net restarted to %s %d %s",longname(remote),
                     84:                getpid(),ctime(&ltime));
                     85:        dump.longtime = ltime;
                     86:        dump.lastndays = numdays(ltime);
                     87:        addtolog(remote,buf);
                     88:        addtopublic(buf);
                     89:        fprintf(stderr,buf);
                     90:        if(!debugflg)fclose(stderr);
                     91:        sendpurge();
                     92:        mainloop();
                     93:        /* never returns */
                     94: }
                     95: /* the main loop of the daemon, alternatively rcv then send, if poss.*/
                     96: mainloop(){
                     97:        register int i;
                     98: 
                     99:        for(;;){        /* begin reading file */
                    100:                debug("daemon %c %d\n",remote,getpid());
                    101:                /* first receive */
                    102:                if(netd.dp_sndorcv >= 0){       /* if we can receive */
                    103:                        i = netrcv();
                    104:                        if(i == -1)dump.nabnormal++;
                    105:                }
                    106:                /* now look to send */
                    107:                if(netd.dp_sndorcv <= 0)        /* if we can send */
                    108:                        netsend();
                    109:                /* print out statistics if the right time */
                    110:                printstat();
                    111:                dump.nloop++;
                    112:        }
                    113: }
                    114:        /* this code is a little strange because some machines
                    115:           seem to have trouble having the date set, and time()
                    116:           returns 0 until somebody remembers to set the date */
                    117: printstat(){
                    118:        long thisndays, thistime;
                    119:        thistime = gettime();
                    120:        thisndays = numdays(thistime);
                    121:        if(dump.longtime == 0L){
                    122:                dump.longtime = thistime;
                    123:                dump.lastndays = thisndays;
                    124:                return;
                    125:                }
                    126:        if(thisndays == dump.lastndays + 1L) dumpit(thistime);
                    127:        dump.lastndays = thisndays;
                    128: }
                    129: /* look for files to send */
                    130: netsend(){
                    131:        static long lasttime = 0;
                    132:        static char nleft = 1;
                    133:        long lFileLen,diff;
                    134:        double drate;
                    135:        register int uid,uidBest;
                    136:        char *sdate,*sn,*swait;
                    137:        long ot,nt,filesize;
                    138:        register int i;
                    139:        char stemp[20];
                    140:        static char jname[FNS];
                    141: 
                    142:        debug("ck send");
                    143:        if(stat(senddir,&statbuf) < 0){
                    144:                error("%s %s",senddir,sys_errlist[errno]);
                    145:                return;
                    146:                }
                    147:        if(statbuf.st_mtime == lasttime && nleft == 0)return;   /* no need to search */
                    148:        lasttime = statbuf.st_mtime;
                    149:        fseek(dir,0L,0);
                    150:        lFileLen = 10000000L;
                    151:        nleft = 0;
                    152:        while(fread(&dirbuf,1,sizeof dirbuf,dir) == sizeof dirbuf){
                    153:                if(dirbuf.d_ino == 0
                    154:                   || dirbuf.d_name[0] != 'c'
                    155:                   || dirbuf.d_name[1] != 'f'
                    156:                   || dirbuf.d_name[2] != remote
                    157:                   || stat(dirbuf.d_name,&statbuf) < 0
                    158:                   || statbuf.st_mode == 0)
                    159:                        continue;
                    160:                dirbuf.d_name[0] = 'd';
                    161:                if(stat(dirbuf.d_name,&statbuf) < 0 || statbuf.st_mode == 0)
                    162:                        continue;
                    163:                uid = guid(statbuf.st_uid,statbuf.st_gid);
                    164:                if(netd.dp_onlyuid != 0 && uid != netd.dp_onlyuid && uid != SUPERUSER
                    165:                        && uid != NUID)continue;
                    166:                nleft++;
                    167:                filesize = getsize(&statbuf);
                    168:                if(lFileLen > filesize){
                    169:                        lFileLen = filesize;
                    170:                        for(i=0; i<DIRSIZ; i++)
                    171:                                jname[i] = dirbuf.d_name[i];
                    172:                        uidBest = uid;
                    173:                }
                    174: # ifdef MAXSENDQ
                    175:                if(nleft > MAXSENDQ)break;
                    176: # endif
                    177:        }
                    178:        if(lFileLen == 10000000L)return;
                    179:        strcpy(stemp,jname);
                    180:        stemp[0] = 'c';
                    181:        sn = SnFromUid(uidBest);
                    182:        if(sn == NULL){
                    183:                addtolog(remote,"Unknown userid %d\n",uidBest);
                    184:                        return;
                    185:        }
                    186:        addtolog(remote,"^S %s %c: %s ",sn,remote,jname+2);
                    187:        ot = gettime();
                    188:        if(send(jname) == 0)return;
                    189:        nt = gettime();
                    190:        filesize = getsize(&statbuf);
                    191:        unlink(jname);
                    192:        unlink(stemp);
                    193:        diff = nt - ot;
                    194:        if(diff < 1)diff = 1;           /* avoid dividing by zero */
                    195:        sdate = ctime(&nt)+4;
                    196:        sdate[strlen(sdate) -9] = 0;
                    197:        swait = comptime(ot - statbuf.st_mtime);
                    198:        jname[3] = jname[2];
                    199: # ifndef NOFP
                    200:        drate = (double)filesize / (double)diff;
                    201:        addtolog(remote,"^T%c(%s, %ldb, %ldsec, %4.1fb/sec, w %s)\n",
                    202:                remote,sdate,filesize, diff,drate, swait);
                    203: # else
                    204:        addtolog(remote,"^T%c(%s, %ldb, %ldsec, w %s)\n",
                    205:                remote,sdate,filesize, diff,swait);
                    206: # endif
                    207:        addtopublic("%s: sent %-8s to %s (%s, %ld b, wait %s)\n",
                    208:                sdate,sn,longname(remote),jname+3,filesize,swait);
                    209:        dump.nsend++;
                    210:        dump.bytetot += filesize;
                    211:        dump.elaptot += diff;
                    212:        }
                    213: send(jname)
                    214:        char *jname;
                    215: {      /* push those bytes */
                    216:        /* returns 0 if send fails, 1 otherwise */
                    217:        register int n;
                    218:        int i;
                    219:        long lsize;
                    220:        char mbuf[20], buf[MAXNBUF];
                    221:        register char *p;
                    222:        register FILE *jfile;
                    223: 
                    224:        debug("send %s",jname);
                    225:        if(stat(jname,&statbuf) < 0)goto sfail;
                    226:        lsize = getsize(&statbuf);
                    227:        if(lsize < MINSIZE){            /* all files are at least this long */
                    228:                unlink(jname);
                    229:                jname[0] = 'c';
                    230:                unlink(jname);
                    231:                return(1);
                    232:                }
                    233:        jfile = fopen(jname,"r");
                    234:        if(jfile == NULL)goto sfail;
                    235:        /*
                    236:        strcpy(mbuf,sheader);
                    237:        i = strlen(sheader);
                    238:        p = (char *)&lsize;
                    239:        lsize = fixuplong(lsize);
                    240:        mbuf[i] = *p++;
                    241:        mbuf[i+1] = *p++;
                    242:        mbuf[i+2] = *p++;
                    243:        mbuf[i+3] = *p++;
                    244:        i = i + 4;
                    245:        sendreset();
                    246:        */
                    247:        initseqno();
                    248:        sprintf(mbuf,"|%08ld|",lsize);
                    249:        i = 10;
                    250:        if(xwrite(mbuf,i) == WRITEFAIL)goto bwrite;
                    251:        while((n=read(fileno(jfile),buf,MAXNBUF)) > 0)
                    252:                if(xwrite(buf,n) == WRITEFAIL)goto bwrite;
                    253:        fclose(jfile);
                    254:        debug("end send");
                    255:        return(1);
                    256: bwrite:
                    257:        dump.nsendfail++;
                    258:        fclose(jfile);
                    259:        addtolog(remote,"^F%c\n",remote);
                    260:        return(0);
                    261: sfail:
                    262:        error("%s: %s",jname,sys_errlist[errno]);
                    263:        dump.nsendfail++;
                    264:        return(0);
                    265:        }
                    266: netrcv(){
                    267:        /* returns -2 in normal fail, -1 in abnormal fail, >= 0 otherwise */
                    268:        char sin;
                    269:        char mgetc(), *s;
                    270:        register int n;
                    271:        char c;
                    272:        int i, dummy, pid;
                    273:        unsigned rcode;
                    274:        long otime,olength,diff,rcvfinish,nt;
                    275:        double r;
                    276:        char hbuf[20], buf[MAXNBUF];
                    277:        register FILE *temp;
                    278:        static struct header hd;
                    279: 
                    280:        initseqno();
                    281:        /*
                    282:        n = nread(hbuf,strlen(sheader));
                    283:        if(n == BROKENREAD)return(-2);
                    284:        if(n != strlen(sheader) || strcmp(sheader,hbuf) != 0){
                    285:                error("wrong head %d %s",n,hbuf);
                    286:                return(-1);
                    287:                }
                    288:        n = nread(&length,4);
                    289:        length = fixuplong(length);
                    290:        */
                    291:        n = nread(hbuf,10);
                    292:        if(n == BROKENREAD)return(-2);
                    293:        if(n != 10){
                    294:                error("bad length nread %d",n);
                    295:                return(-1);
                    296:                }
                    297:        hbuf[10] = 0;
                    298:        if(hbuf[0] != '|' || hbuf[9] != '|'){
                    299:                error("poor format %s",hbuf);
                    300:                return(-1);
                    301:                }
                    302:        hbuf[9] = 0;
                    303:        length = atol(hbuf+1);
                    304:        if(length < 0 || length > 100000000L){
                    305:                error("bad length %ld",length);
                    306:                return(-1);
                    307:                }
                    308:        dump.braw = 4;
                    309:        olength = length;
                    310:        otime = gettime();
                    311:        debug("length = %ld\n",length);
                    312: 
                    313: /* 
                    314:        begin parsing header
                    315: 
                    316:        from local to remote (requests)
                    317:        code    net option      reason
                    318:        q                       normal request
                    319:        y       -y              simply skips login check (used by netlpr)
                    320: 
                    321:        from remote to local
                    322:        code    net option      reason
                    323:        w       -w              message to be written/mailed back
                    324:        s       -z              normal response
                    325: */
                    326: 
                    327:        i = readhd(&hd);
                    328:        if(i == -3)goto forw;                   /* being forwarded thru us */
                    329:        if(i != 0)return(i);
                    330: 
                    331:        strcpy(status.login, hd.hd_snto);
                    332:        strcpy(status.localname,hd.hd_snfrom);
                    333: 
                    334:        demask(hd.hd_spasswd);
                    335: 
                    336:        s = hd.hd_scmdvirt;
                    337:        while(*s && *s != ' ')s++;
                    338:        c = *s;
                    339:        *s = 0;
                    340:        if(strcmp(hd.hd_scmdvirt,"netlpr") == 0)dump.nnetlpr++;
                    341:        else if(strcmp(hd.hd_scmdvirt,"netmail") == 0)dump.nnetmail++;
                    342:        else if(strcmp(hd.hd_scmdvirt,"mail") == 0)dump.nsmail++;
                    343:        else if(strcmp(hd.hd_scmdvirt,"netcp") == 0)dump.nnetcp++;
                    344:        else if(strcmp(hd.hd_scmdvirt,"response") == 0)dump.nresp++;
                    345:        else dump.nnet++;
                    346:        *s = c;
                    347: 
                    348:        printhd(&hd);
                    349: 
                    350:        /* any chars left are data */
                    351: forw:
                    352:        sin = 0;
                    353:        if(length > 0){ /* make a temp input file */
                    354:                increment(tempfile);
                    355:                temp = fopen(tempfile,"w");
                    356:                if(temp == NULL){
                    357:                        error("%s %s",tempfile,sys_errlist[errno]);
                    358:                        return(-1);
                    359:                        }
                    360:                chmod(tempfile,0600);
                    361:                if(hd.hd_mchto != local){
                    362:                        fprintf(temp,"%c :%c :",hd.hd_code,hd.hd_mchto);
                    363:                        fflush(temp);
                    364:                }
                    365:                /* this is the loop to read in all the data */
                    366:                while((n = mread(buf,MAXNBUF)) > 0)
                    367:                        if(write(fileno(temp),buf,n) != n){
                    368:                                error("%s %s",tempfile,sys_errlist[errno]);
                    369:                                fclose(temp);
                    370:                                unlink(tempfile);
                    371:                                return(-1);
                    372:                                };
                    373:                fclose(temp);
                    374:                if(n == BROKENREAD || length > 0){
                    375:                        unlink(tempfile);
                    376:                        return(-2);
                    377:                        }
                    378:                sin = 1;
                    379:                if(hd.hd_mchto != local){
                    380:                        diff = gettime() - otime;
                    381:                        if(diff < 1)diff = 1;   /* avoid dividing by 0 */
                    382: # ifndef NOFP
                    383:                        r = olength;
                    384:                        r = r/diff;
                    385:                        addtolog(remote,"^P(to %c, %ldb, %ldsec, %4.1fb/sec)\n",
                    386:                                hd.hd_mchto,olength,diff,r);
                    387: # else
                    388:                        addtolog(remote,"^P(to %c, %ldb, %ldsec)\n",
                    389:                                hd.hd_mchto,olength,diff);
                    390: # endif
                    391:                        dump.npass++;
                    392:                        dump.bytetot += olength;
                    393:                        dump.elaptot += diff;
                    394:                        while((pid = fork()) == -1)sleep(2);
                    395:                        if(pid == 0){
                    396:                                RENICE;
                    397:                                execl(netcmd,"net","-x","-m",longname(hd.hd_mchto),
                    398:                                        "-s",tempfile,0);
                    399:                                error("%s: %s",netcmd,sys_errlist[errno]);
                    400:                                exit(EX_UNAVAILABLE);
                    401:                                }
                    402:                        wait(&rcode);
                    403:                        unlink(tempfile);
                    404:                        rcode >>= 8;
                    405:                        if(rcode != 0)
                    406:                                error("pass-thru rcode %d");
                    407:                        debug("passthru to %c code %c rcode %d",
                    408:                                hd.hd_mchto,hd.hd_code,rcode);
                    409:                        return(1);
                    410:                        }
                    411:                }
                    412:        if(length > 0){error("file too short"); return(-1); }
                    413:        rcvfinish = gettime();
                    414: 
                    415:        while((pid = fork()) == -1)sleep(2);
                    416:        if(pid > 0){
                    417:                wait(&dummy);
                    418:                return(1);      /* normal return */
                    419:        }
                    420:        RENICE;
                    421:        while((pid = fork()) == -1)sleep(2);
                    422:        if(pid != 0)exit(EX_OK);
                    423: 
                    424:        /* child process which forks and waits */
                    425:        mktemp(resfile);
                    426:        while((pid = fork()) == -1)sleep(2);
                    427:        if(pid == 0){
                    428:                /* child */
                    429:                strcpy(status.loginshell,Bsh);
                    430:                frommach = hd.hd_mchfrom;
                    431:                n = check(&hd,(hd.hd_code == 'q'));
                    432:                if(!n)errormsg(TRUE,&hd,NULL,
                    433:                        "Bad remote login/password '%s'",hd.hd_snto);
                    434:                temp = fopen(resfile,"w");
                    435:                if(temp == NULL)
                    436:                        errormsg(TRUE,&hd,NULL,
                    437:                        "Create file %s: %s",resfile,sys_errlist[errno]);
                    438:                fclose(temp);
                    439:                chmod(resfile,0600);
                    440:                mchown(resfile,status.muid,status.mgid);
                    441:                if(sin)
                    442:                        mchown(tempfile,status.muid,status.mgid);
                    443:                else tempfile[0] = 0;
                    444:                setgid(status.mgid);
                    445:                setuid(status.muid);
                    446:                /* after this point our gid, uid is the target user's */
                    447:                excmd(&hd,resfile,tempfile);
                    448:        }
                    449:        /* parent */
                    450:        wait(&rcode);
                    451:        rcode = (((rcode&077400) >>8) &0177);
                    452:        /*
                    453:        fclose(stdin);
                    454:        fclose(stdout);
                    455:        fclose(stderr);
                    456:        */
                    457:        if(sin)unlink(tempfile);
                    458:        /* 
                    459:           now send something back to the sender 
                    460:           unless this was a response (file or message)
                    461:        */
                    462:        if((hd.hd_code == 'q' || hd.hd_code == 'y')
                    463:        && (hd.hd_srespfile[0] || !hd.hd_fnonotify))
                    464:                sndresponse(&hd,rcode);
                    465:        unlink(resfile);
                    466:        s = ctime(&rcvfinish);
                    467:        s += 4;
                    468:        s[strlen(s) -8] = 0;
                    469:        diff = rcvfinish - otime;
                    470:        if(diff < 1)diff = 1;           /* avoid dividing by zero */
                    471:        dump.bytetot += olength;
                    472:        dump.elaptot += diff;
                    473:        sprintf(buf,"%s rcv  %c:%-8s (%s)",
                    474:                s,hd.hd_mchfrom,hd.hd_snfrom,hd.hd_snto);
                    475:        addtolog(remote,"%s C: %s\n",buf,hd.hd_scmdvirt);
                    476:        addtopublic("%s R: %d C: %s\n",buf,rcode,hd.hd_scmdvirt);
                    477:        nt = rcvfinish - hd.hd_ltimesent;
                    478:        buf[0] = 0;
                    479:        if(nt > 0L)sprintf(buf," took (%s)",comptime(nt));
                    480: # ifndef NOFP
                    481:        r = olength;
                    482:        r = r/diff;
                    483:        addtolog(remote,"\t\tR: %d%s %ldb %ldsec %4.1fb/sec\n",
                    484:                rcode,buf,olength,diff,r);
                    485:        r = dump.braw;
                    486:        r = r/diff;
                    487:        addtolog(remote,"\t\t%4.1frb/sec %4.1f%% use\n",r,(r/linechars())*100L);
                    488: # else
                    489:        addtolog(remote,"\t\tR: %d%s %ldb %ldsec\n",
                    490:                rcode,buf,olength,diff);
                    491: # endif
                    492:        exit(EX_OK);
                    493:        /*UNREACHED*/
                    494:        }
                    495: long linechars(){
                    496:        if(netd.dp_inspeed == 13)return(960L);
                    497:        else return(120L);
                    498:        }
                    499: /* 
                    500:        execute the user's command
                    501:        this procedure is executed with uid, gid of the user
                    502: */
                    503: excmd(phd,tempresfile,tempinfile)
                    504:        register struct header *phd;
                    505:        char *tempresfile, *tempinfile;
                    506: {
                    507:        FILE *fd;
                    508:        int i, uid;
                    509:        register char *s, c;
                    510: 
                    511:        uid = getuid();
                    512:        uid = uidmask(uid);
                    513:        status.muid = uidmask(status.muid);
                    514:        if(uid != status.muid)error("setuid fails");
                    515:        debug("uid: %u, gid: %u\n",uid,status.mgid);
                    516:        /* check for allowed root commands, for security reasons */
                    517:        if(uid == SUPERUSER){
                    518:                s = phd->hd_scmdact;
                    519:                while(*s && *s != ' ')s++;
                    520:                c = *s;
                    521:                *s = 0;
                    522:                /* these are the only commands root may execute */
                    523:                if(strcmp(phd->hd_scmdact,"cat")            != 0
                    524:                && strcmp(phd->hd_scmdact,"/bin/cat")       != 0
                    525:                && strcmp(phd->hd_scmdact,MWRITECMD)        != 0
                    526:                && strcmp(phd->hd_scmdact,"netrm")          != 0
                    527:                && strcmp(phd->hd_scmdact,"/usr/lib/tq")    != 0
                    528:                && strcmp(phd->hd_scmdact,"/usr/lib/rtrrm") != 0
                    529:                && strcmp(phd->hd_scmdact,"lpr")            != 0)
                    530:                        errormsg(TRUE,phd,tempresfile,
                    531:                                "Not allowed to execute '%s' as root",
                    532:                                phd->hd_scmdact);
                    533:                *s = c;
                    534:                }
                    535:        if(chdir(status.dir) < 0)
                    536:                errormsg(TRUE,phd,tempresfile,
                    537:                        "chdir %s: %s",status.dir,sys_errlist[errno]);
                    538:        setenv(status.dir);     /* set up v7 environment */
                    539:        if(tempinfile[0])mreopen(TRUE,phd,tempresfile,tempinfile,"r",stdin);
                    540:        else if(phd->hd_sinfile[0])mreopen(TRUE,phd,tempresfile,phd->hd_sinfile,"r",stdin);
                    541:        else mreopen(TRUE,phd,tempresfile,"/dev/null","r",stdin);
                    542:        if(phd->hd_code == 's' && phd->hd_soutfile[0]){
                    543:                if(stat(phd->hd_soutfile,&statbuf) < 0
                    544:                   || getsize(&statbuf) != 0)
                    545:                        errormsg(FALSE,phd,tempresfile,"Bad result file '%s'",phd->hd_soutfile);
                    546:                mreopen(TRUE,phd,tempresfile,phd->hd_soutfile,"w",stdout);
                    547:                }
                    548:        else if(phd->hd_soutfile[0]){
                    549:                fd = fopen(phd->hd_soutfile,"w");
                    550:                if(fd == NULL)
                    551:                        errormsg(TRUE,phd,tempresfile,"Open file %s: %s",
                    552:                                phd->hd_soutfile,sys_errlist[errno]);
                    553:                fclose(fd);
                    554:                mreopen(TRUE,phd,tempresfile,phd->hd_soutfile,"w",stdout);
                    555:                }
                    556:        else mreopen(TRUE,phd,tempresfile,tempresfile,"a",stdout);
                    557:        debug("exec '%s'\n",phd->hd_scmdact);
                    558:        if(debugflg == 0){
                    559:                /* cheat */
                    560:                close(2);
                    561:                dup(1);
                    562:                /*
                    563:                mreopen(TRUE,phd,tempresfile,tempresfile,"a",stderr);
                    564:                */
                    565:                }
                    566:        for(i=3;i<15;i++)close(i);
                    567:        if(strcmp(phd->hd_scmdact,"cat") == 0
                    568:        || strcmp(phd->hd_scmdact,"/bin/cat") == 0)excat();
                    569:        do {
                    570:                mexecl(status.loginshell,"sh","-c",phd->hd_scmdact,0);
                    571:                sleep(2);
                    572:                } while(errno == ETXTBSY);
                    573:        perror(status.loginshell);
                    574:        exit(EX_UNAVAILABLE);
                    575: }
                    576: /* 
                    577:        send back a response
                    578: 
                    579:        if errormsg was called the resfile should be unlinked,
                    580:        to avoid two messages being sent there
                    581: */
                    582: sndresponse(phd,rcode)
                    583: unsigned rcode;
                    584: struct header *phd;
                    585: {
                    586:        char cmdstr[BUFSIZ], buf[BUFSIZ];
                    587:        int dummy;
                    588:        long maxfile = MAXFILE;
                    589:        /* send response back if a response file
                    590:        was given or if mail/write is allowed */
                    591:        if(stat(resfile,&statbuf) < 0){
                    592:                error("%s %s",resfile,sys_errlist[errno]);
                    593:                return;
                    594:                }
                    595:        /* allow larger files between the Ingres machines */
                    596:        if(machtype[local  - 'a'] == M_INGRES
                    597:        && machtype[remote - 'a'] == M_INGRES)
                    598:                maxfile = MAXFILELARGE;
                    599:        if(getsize(&statbuf) >= maxfile){
                    600:                errormsg(TRUE,phd,"Result file too large - not sent");
                    601:                return;
                    602:                }
                    603:        if(getsize(&statbuf) == 0){
                    604:                /* response file specified, no output generated */
                    605:                if(phd->hd_srespfile[0] != 0)return;
                    606:                /* quiet option - no output and a rcode of 0 */
                    607:                if(rcode == 0 && phd->hd_fquiet)return;
                    608:        }
                    609:        /* use both old and new mwrite parm lists */
                    610: 
                    611:        if(phd->hd_srespfile[0])
                    612:                sprintf(cmdstr,"-o %s cat",phd->hd_srespfile);
                    613:        else sprintf(cmdstr,
                    614:        "%s -t %s -f %s -x %ld -c \"'%s'\" -y %s -e %ld -r %d",
                    615:        MWRITECMD, phd->hd_addrfrom, phd->hd_addrto, phd->hd_lttytime,
                    616:        phd->hd_scmdvirt, phd->hd_sttyname, phd->hd_ltimesent-TIMEBASE, rcode);
                    617: 
                    618:        sprintf(buf,"%s -m%c -z -b -l %s -s %s -c response %s",
                    619:                netcmd,phd->hd_mchfrom,phd->hd_snfrom,resfile,cmdstr);
                    620:        dummy = system(buf);            /* execute command buf */
                    621: }
                    622:        
                    623: /*
                    624: 
                    625:        excat
                    626:        does nothing more than copy standard input to standard
                    627:        output, like the cat command, but reports write errors.
                    628:        Uses getc and putc rather than fwrite and fread because
                    629:        the latter call getc and putc.
                    630: */
                    631: excat(){
                    632:        register int n;
                    633:        char buf[BUFSIZ];
                    634: 
                    635:        errno = 0;
                    636:        while((n = read(0,buf,BUFSIZ)) > 0){
                    637:                if(write(1,buf,n) != n){
                    638:                        perror("filecat: stdout");
                    639:                        exit(EX_OSFILE);
                    640:                        }
                    641:                }
                    642:        if(errno){
                    643:                perror("filecat: stdin");
                    644:                exit(EX_OSFILE);
                    645:        }
                    646:        exit(EX_OK);
                    647: }
                    648: /* returns errors for netrcv() */
                    649: static readhd(phd)
                    650: register struct header *phd;
                    651: {
                    652:        char cflag, sbuf[BUFSIZ], parmlist[PARMLIST];
                    653:        int i = 0;
                    654:        char code;
                    655:        code = mgetc();
                    656:        phd->hd_mchto = mgetc();
                    657:        if(code != 'q' && code != 'y' && code != 'w' && code != 's'){
                    658:                error("bad code");
                    659:                return(-1);
                    660:                }
                    661:        phd->hd_code = code;
                    662:        if(phd->hd_mchto < 'a' || 'z' < phd->hd_mchto){
                    663:                error("bad phd->hd_mchto");
                    664:                return(-1);
                    665:                }
                    666:        if(phd->hd_mchto != local)return(-3);   /* being forwarded through us */
                    667:        phd->hd_mchfrom = mgetc();
                    668:        phd->hd_vmajor = mgetc();
                    669:        phd->hd_vminor = mgetc();
                    670:        i += mgets(phd->hd_snto,NS);
                    671:        i += mgets(phd->hd_spasswd,20);
                    672:        i += mgets(phd->hd_sinfile,FNS);
                    673:        i += mgets(phd->hd_soutfile,FNS);
                    674:        i += mgets(phd->hd_srespfile,FNS);
                    675:        i += mgets(phd->hd_snfrom,NS);
                    676: 
                    677:        /* addrfrom is the person who sent this to us,
                    678:           addrto is the person who received the command, i.e.
                    679:           addrto is on this machine */
                    680:        if(phd->hd_snfrom[0] == 0)strcpy(phd->hd_snfrom,"root");
                    681:        sprintf(phd->hd_addrfrom,  "%s:%s",longname(phd->hd_mchfrom),phd->hd_snfrom);
                    682:        sprintf(phd->hd_addrto,    "%s:%s",longname(phd->hd_mchto),phd->hd_snto);
                    683: 
                    684:        i += mgets(phd->hd_sttyname,20);
                    685:        if(phd->hd_sttyname[0] == 0)strcpy(phd->hd_sttyname,"/dev/ttyx");
                    686:        cflag = mgetc();
                    687:        if(!phd->hd_mchfrom || !phd->hd_code || !cflag || !phd->hd_vmajor || !phd->hd_vminor){
                    688:                error("mgetc fails");
                    689:                return(-1);
                    690:                }
                    691: 
                    692:        cflag -= 'a';
                    693:        phd->hd_fnonotify = (cflag & F_NONOTIFY);
                    694:        phd->hd_fquiet = (cflag & F_QUIET);
                    695: 
                    696:        phd->hd_vmajor -= 'a';
                    697:        phd->hd_vminor -= 'a';
                    698: 
                    699:        i += mgets(sbuf,BUFSIZ);
                    700:        phd->hd_lttytime = 0;
                    701:        sscanf(sbuf,"%lo",&phd->hd_lttytime);
                    702: 
                    703:        i += mgets(parmlist,PARMLIST);
                    704:        phd->hd_ijobno = atoi(parmlist);
                    705:        /* keep variable parameter list in jobno slot */
                    706:        parseparmlist(parmlist);
                    707: 
                    708:        i += mgets(sbuf,BUFSIZ);                /* time sent */
                    709:        sscanf(sbuf,"%ld",&phd->hd_ltimesent);
                    710:        phd->hd_ltimesent += TIMEBASE;
                    711:        i += mgetcmd(phd->hd_scmdact);
                    712:        i += mgetcmd(phd->hd_scmdvirt);
                    713:        if(i != 0){error("mgets fails"); return(-1);}
                    714:        if(phd->hd_scmdvirt[0] == 0)strcpy(phd->hd_scmdvirt,phd->hd_scmdact);
                    715:        return(0);
                    716: }
                    717: /* 
                    718:    check() -- verify login name and password
                    719:    phd    = login,passwd
                    720:    fverify  = 1 if password must check
                    721:    Returns 1 if password is ok, 0 if not.
                    722: */
                    723: check(phd,fverify)     /* 1 if OK, 0 if not */
                    724: register struct header *phd;
                    725: int fverify;
                    726: {
                    727:        char *sencpasswd, *u, *nullstr = "";
                    728:        struct passwd *pwd;
                    729:        if(phd->hd_snto[0] == 0)return(!fverify);
                    730:        if(!goodacctname(phd->hd_snto))return(!fverify);
                    731:        pwd = getpwnam(phd->hd_snto);
                    732:        if(pwd == NULL)return(!fverify);
                    733: 
                    734:        if(machtype[local-'a'] == M_CC && machtype[frommach-'a'] == M_CC)
                    735:                sencpasswd = phd->hd_spasswd;
                    736:        else if(*phd->hd_spasswd)sencpasswd = crypt(phd->hd_spasswd,pwd->pw_passwd);
                    737:        else sencpasswd = nullstr;
                    738: 
                    739:        status.muid = guid(pwd->pw_uid,pwd->pw_gid);
                    740:        status.mgid = pwd->pw_gid;
                    741:        if(isdigit(pwd->pw_gecos[0]))status.jobno = atoi(pwd->pw_gecos);
                    742:        else status.jobno = 32767;
                    743:        strcpy(status.dir,pwd->pw_dir);
                    744:        strcpy(status.loginshell,pwd->pw_shell);
                    745:        u = status.loginshell;
                    746:        if(u[0] == 0 || strcmp("/bin/sbash",u) == 0)strcpy(u,Bsh);
                    747: 
                    748:        getpwdf(pwd);
                    749:        /* ignore network passwd */
                    750:        /* acct is not a pair, acct is not "network", passwd is incorrect,
                    751:        and verification is requested => passwd not ok */
                    752:        if(!facctpaircheck(phd) && strcmp(phd->hd_snto,"network") != 0
                    753:        && strcmp(pwd->pw_passwd,sencpasswd) != 0 && fverify)
                    754:                return(0);
                    755:        return(1);      /* otherwise passwd ok */
                    756:        }
                    757: mread(b,n)
                    758:   register int n; {
                    759:        if(length <= 0)return(0);
                    760:        if(length < n)n = length;
                    761:        n = nread(b,n);
                    762:        if(n != BROKENREAD)length -= n;
                    763:        return(n);
                    764:        }
                    765: char mgetc(){                  /* returns 0 if fail */
                    766:        register char c;
                    767:        register int n;
                    768:        char buf[3];
                    769:        if((n=nread(buf,3)) == BROKENREAD)return(0);
                    770:        if(n != 3){error("bad read %d",n); return(0); }
                    771:        c = buf[0];
                    772:        if(buf[1] != ' ' && buf[1] != ':'){error("Bad char %c",buf[1]); return(0); }
                    773:        length -= 3;
                    774:        if(length < 0){error("length wrong2 %ld",length); return(0); }
                    775:        return(c);
                    776:        }
                    777: /* read in string over the network wire */
                    778: /* put string in s, max length is maxlen */
                    779: mgets(s,maxlen)                        /* returns 0 if ok, 1 if not */
                    780:   int maxlen;
                    781:   register char *s; {
                    782:        register char *q;
                    783:        register int n;
                    784:        char c;
                    785:        q = s;
                    786:        for(;;) {
                    787:                if((n=nread(&c,1)) == BROKENREAD){
                    788:                        *s = 0;
                    789:                        error("mgets %s",s);
                    790:                        return(1);
                    791:                        }
                    792:                if(n == 0)break;
                    793:                if(c == '\\'){
                    794:                        if((n=nread(&c,1)) == BROKENREAD){
                    795:                                *s = 0;
                    796:                                error("mgets %s",s);
                    797:                                return(1);
                    798:                                }
                    799:                        if(n == 0)break;
                    800:                        }
                    801:                if(c == ' ')break;
                    802:                if(maxlen-- > 0) *s++ = c;
                    803:                }
                    804:        *s = 0;
                    805:        if(nread(&c,1) == BROKENREAD){
                    806:                error("mgets %s",s);
                    807:                return(1);
                    808:                }
                    809:        length -= (s - q + 2);
                    810:        if(length < 0){error("length wrong1 %ld %s",length,q); return(-1); }
                    811:        if(maxlen < 0)
                    812:                error("mgets - string too long");
                    813:        return(0);
                    814:        }
                    815: mgetcmd(s)                     /* returns 0 if succeed, 1 otherwise */
                    816:   char *s; {
                    817:        int i,n;
                    818:        char c;
                    819:        i = 0;
                    820:        for(;;){
                    821:                if((n=nread(&c,1)) == BROKENREAD){
                    822:                        s[i] = 0;
                    823:                        error("mgetcmd %s",s);
                    824:                        return(1);
                    825:                        }
                    826:                if(n <= 0 || c == '\n')break;
                    827:                if(c == '\\'){
                    828:                        if(nread(&c,1) == BROKENREAD){
                    829:                                s[i] = 0;
                    830:                                error("mgetcmd %s",s);
                    831:                                return(1);
                    832:                                }
                    833:                        length--;
                    834:                        }
                    835:                s[i++] = c;
                    836:                length--;
                    837:                }
                    838:        s[i] = 0;
                    839:        length--;
                    840:        return(0);
                    841:        }
                    842: increment(s)
                    843:  char *s; {
                    844:        int i;
                    845:        char *p;
                    846:        i = strlen(s) - 1;
                    847:        while(s[i] == '9')i--;
                    848:        if(s[i] < '0' || s[i] > '9'){
                    849:                p = s+i+1;
                    850:                while(*p)*p++ = '0';
                    851:                return;
                    852:                }
                    853:        (s[i])++;
                    854:        i++;
                    855:        while(s[i])s[i++] = '0';
                    856:        return;
                    857:        }
                    858: /* gather 24-hour stats and  mail to STATADDR */
                    859: /* should also gather stats on # error msgs */
                    860: dumpit(currt)
                    861:   long currt; {
                    862:        register struct dumpstruc *p = &dump;
                    863:        register int ntot;
                    864:        long elapt;
                    865:        double cputime,utime,stime,bs,rawbs;
                    866:        char *sstartt;
                    867:        FILE *fdm;
                    868:        char froma[30];
                    869:        struct tms tbf;
                    870: 
                    871:        /* if STATADDR is a file, the mail program this call will
                    872:           ultimately execute must be able to deal with it,
                    873:           and the remote mail program must be able to write on the
                    874:           file, i.e. mode 666 */
                    875:        sprintf(froma,"%s=>",longname(local));
                    876:        strcat(froma,longname(remote));
                    877:        fdm = mailopen(STATADDR,froma,1,0);
                    878:        if(fdm == NULL)return;
                    879: 
                    880:        /* calculate times */
                    881:        elapt = currt - dump.longtime;
                    882:        ntot = p->nnetcp + p->nnetmail + p->nsmail + p->nnetlpr
                    883:                + p->nresp + p->nnet;
                    884:        sstartt = ctime(&dump.longtime) + 4;
                    885:        sstartt[strlen(sstartt) - 9] = 0;
                    886: 
                    887:        times(&tbf);
                    888: # ifndef NOFP
                    889:        utime = tbf.tms_utime + tbf.tms_cutime;
                    890:        stime = tbf.tms_stime + tbf.tms_cstime;
                    891:        cputime = utime + stime;
                    892: 
                    893:        if(elapt > 0)cputime = (cputime/elapt) * 100.0;
                    894:        else cputime = 0.0;
                    895:        utime = utime/60.0;
                    896:        stime = stime/60.0;
                    897:        cputime = cputime/60.0;
                    898:        bs = p->bytetot;
                    899:        if(p->elaptot > 0)bs = bs /p->elaptot;
                    900:        else bs = 0.0;
                    901: # endif
                    902: 
                    903:        /* print out the statistics */
                    904:        fprintf(fdm,"Subject: %s, %s, time %s\n",
                    905:                froma,sstartt, comptime(elapt));
                    906:        fprintf(fdm,"Command summary:\n");
                    907:        fprintf(fdm,"\t# sent %d\t# pass_thru %d\t# rcv %d:\t# netcp %d\n",
                    908:                p->nsend,p->npass,ntot,p->nnetcp);
                    909:        fprintf(fdm,"\t# netlpr %d\t# netmail %d\t# sendbmail %d\t# resp %d\n",
                    910:                p->nnetlpr,p->nnetmail,p->nsmail,p->nresp);
                    911:        fprintf(fdm,"Protocol summary:\n");
                    912:        fprintf(fdm,"\t# pk_sent %d\t# pk_rcv %d\t# b_sent %ld\t# b_rcv %ld\n",
                    913:                p->npacksent,p->npackrcv,p->nbytesent, p->nbytercv);
                    914:        fprintf(fdm,
                    915:                "\t# send_fails %d\t# retrans %d\t# abn %d\t\t# cksum_errs %d\n",
                    916:                p->nsendfail,p->nretrans, p->nabnormal,p->ncksum);
                    917: # ifndef NOFP
                    918:        fprintf(fdm,"Load:\tuser %4.1f\tsys %4.1f\tpct %5.2f\trate %6.1f\n",
                    919:                utime,stime,cputime,bs);
                    920:        rawbs = p->brawtot*100L;
                    921:        rawbs = rawbs / linechars();
                    922:        fprintf(fdm,"\trawbytes %ld\tuse %4.1f\n", p->brawtot,rawbs);
                    923: # endif
                    924:        mailclose(fdm);
                    925: 
                    926:        /* reset counters */
                    927:        p->nbytesent = p->nbytercv = p->elaptot = p->bytetot = 0L;
                    928:        p->nretrans = p->nloop = p->nabnormal = p->ncksum = 0;
                    929:        p->npacksent = p->npackrcv = p->nnetcp = p->nnetmail = 0;
                    930:        p->nsmail = p->nnetlpr = p->nnet = p->npass = 0;
                    931:        p->nsend = p->nsendfail = 0;
                    932:        dump.longtime = currt;
                    933:        }
                    934: /* returns 1 if n is ok, 0 if not */
                    935: goodacctname(n)
                    936:   char *n; {
                    937:        int i;
                    938:        i = -1;
                    939:        while(btable[++i].bname)
                    940:                if(strcmp(btable[i].bname,n) == 0 &&
                    941:                        local == btable[i].bmach)return(0);
                    942:        return(1);
                    943:        }
                    944: demask(s)
                    945:   register char *s; {
                    946: /*
                    947:        static char buf[20];
                    948:        char skey[30];
                    949:        makeuukey(skey,status.login,local);
                    950:        strcpy(s,nbsdecrypt(s,skey,buf));
                    951: */
                    952:        while(*s){
                    953:                *s &= 0177;             /* strip quote bites */
                    954:                *s++ ^= 040;            /* invert upper-lower */
                    955:                }
                    956:        }
                    957: /*VARARGS0*/
                    958: mreopen(fsendtofmach,phd,sfn,a,b,c){
                    959: /* simply handles errors by giving error msg */
                    960:        if(freopen(a,b,c) == NULL)
                    961:                errormsg(fsendtofmach,phd,sfn,"%s: %s",a,sys_errlist[errno]);
                    962: }
                    963: /* 
                    964:        addtopub(string, args)
                    965: 
                    966:        add a message to the public logfile /usr/net/logfile.
                    967:        note that the file must be writeable by everyone
                    968:        if error messages from the netrcv subroutine
                    969:        such as chdir errors are to be noticed.
                    970: */
                    971: /*VARARGS0*/
                    972: addtopublic(s,a,b,c,d,e,f,g,h,i,j,k,l,m,n)
                    973: char *s;
                    974: {
                    975:        static FILE *log = NULL;
                    976:        if(log == NULL){
                    977:                if(stat(publogfile,&statbuf) < 0)return;
                    978:                log = fopen(publogfile,"a");
                    979:                if(log == NULL)return;
                    980:                }
                    981:        fseek(log,0L,2);
                    982:        fprintf(log,s,a,b,c,d,e,f,g,h,i,j,k,l,m,n);
                    983:        fflush(log);
                    984:        }
                    985: /* set up a dummy environment for v7 /bin/sh */
                    986: setenv(home)
                    987:   char *home; {
                    988:        static char *env[3],benv[2][50];
                    989:        env[0] = benv[0];
                    990:        env[1] = benv[1];
                    991:        strcpy(env[0],"PATH=:/bin:/usr/bin");
                    992:        sprintf(env[1],"HOME=%s",home);
                    993:        env[2] = 0;
                    994:        environ = env;
                    995:        }
                    996: /* 
                    997:        errormsg(fsendtofmach,phd,sfn,"string",arg(s))
                    998:        
                    999:        Sends error message to user.
                   1000:        If fsendtofmach=TRUE, send to phd->hd_mchfrom, otherwise
                   1001:        send to phd->hd_mchto.
                   1002:        Also, if error occured during return of a "response",
                   1003:        send to local machine.
                   1004: 
                   1005:        Note that errormsg can be called by the netrcv subroutine
                   1006:        after the setuid() call to the specific user, so the 
                   1007:        user must be able to get off an error msg back to him,
                   1008:        and to write in the two log files.
                   1009:        Can't use -w,-x,-y,-z for the net cmd because must be root for those.
                   1010: 
                   1011:        If sfn != NULL, then unlink sfn before exiting.
                   1012: */
                   1013: /*VARARGS0*/
                   1014: errormsg(fsendtofmach,phd,sfn,s,a,b,c,d,e,f,g,h)
                   1015: char fsendtofmach;                             
                   1016: struct header *phd;
                   1017: char *sfn,*s;
                   1018: {
                   1019:        int rcode;
                   1020:        char errstr[BUFSIZ], cmdstr[BUFSIZ], rcmd[BUFSIZ];
                   1021:        char toadd[FNS], fromadd[FNS], mchto, mchfrom;
                   1022:        char snto[FNS], snfrom[FNS];
                   1023: 
                   1024:        if(phd->hd_sttyname[0] == 0)strcpy(phd->hd_sttyname,"/dev/ttyx");
                   1025:        /* will send to toadd, from fromadd */
                   1026:        if(!fsendtofmach || strcmp(phd->hd_scmdvirt,"response") == 0){
                   1027:                /* send to tomach mach, thus send to toaddr. */
                   1028:                /* if this is an error during a response, send to local mach. */
                   1029:                strcpy(toadd,  phd->hd_addrto);
                   1030:                strcpy(fromadd,phd->hd_addrfrom);
                   1031:        }
                   1032:        else {          /* send to remote mach, thus send back to addrfrom*/
                   1033:                strcpy(toadd,  phd->hd_addrfrom);
                   1034:                strcpy(fromadd,phd->hd_addrto);
                   1035:        }
                   1036:        sprintf(errstr,"Error: ");
                   1037:        sprintf(cmdstr,s,a,b,c,d,e,f,g,h);
                   1038:        strcat(errstr,cmdstr);
                   1039:        strcat(errstr,"\n");
                   1040:        addtolog(remote,errstr);
                   1041:        addtopublic(errstr);
                   1042: 
                   1043:        mchto =   MchSFromAddr(snto,toadd); 
                   1044:        mchfrom = MchSFromAddr(snfrom,fromadd);
                   1045: 
                   1046:        sprintf(rcmd,
                   1047: "%s %s %s %lo %c %s \"'%s'\" %ld -t %s -f %s -x %ld -y %s -c \"'%s'\" -e %ld",
                   1048:        MWRITECMD, snto, phd->hd_sttyname, phd->hd_lttytime, 
                   1049:        local, snfrom,phd->hd_scmdvirt, phd->hd_ltimesent-TIMEBASE,
                   1050:        toadd, fromadd, phd->hd_lttytime, phd->hd_sttyname, phd->hd_scmdvirt,
                   1051:        phd->hd_ltimesent-TIMEBASE);
                   1052: 
                   1053:        if(mchto == local)
                   1054:                sprintf(cmdstr, "echo \"%s\" | %s", errstr,rcmd);
                   1055:        else 
                   1056:                sprintf(cmdstr,
                   1057:                "echo \"%s\" | %s -m%c -b -c errormessage -l network - %s",
                   1058:                        errstr,netcmd,mchto,rcmd);
                   1059:        rcode = system(cmdstr);
                   1060:        if(sfn != NULL)unlink(sfn);
                   1061:        exit(EX_USAGE);
                   1062:        }
                   1063: handlekill(){  /* SIGTERM signal */
                   1064:        long t;
                   1065:        /*
                   1066:        t = gettime();
                   1067:        dumpit(t);
                   1068:        */
                   1069:        exit(EX_OK);    /* kill myself */
                   1070:        }
                   1071: 
                   1072: /* check a request to see if it is an acct pair */
                   1073: /* returns 1 if it is, 0 if not */
                   1074: static facctpaircheck(phd)
                   1075: register struct header *phd;
                   1076: {
                   1077:        return(0);
                   1078: }
                   1079: 

unix.superglobalmegacorp.com

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