/* Copyright (C) 1991, Marcus J. Ranum. All rights reserved. VMS port by Andrew Molitor (with many thanks!) */ #ifndef lint static char RCSid[] = "$Header: /usr/users/mjr/hacks/umud/RCS/net_vms.c,v 1.1 91/06/17 14:37:36 mjr Exp $"; #endif /* configure all options BEFORE including system stuff. */ #include "config.h" #include "multinet_root:[multinet.include.sys]types.h" #include "multinet_root:[multinet.include]netdb.h" #include "multinet_root:[multinet.include.sys]socket.h" #include "multinet_root:[multinet.include.netinet]in.h" #include "multinet_root:[multinet.include.vms]inetiodef.h" #include <ctype.h> #include <varargs.h> #include <ssdef.h> #include <libdtdef.h> #include "mud.h" #include "vars.h" /* For the VMS version, we use event flags to synchronize most everything. Writes we don't care about. All reads set flag 32 on completion, server fd connects set flag 33, player connects set flag 34. We use 35 for the timer. */ /* #define VMSNET_DEBUG */ /* as always the network code comprises the lioness' share of the MUD. this module supports connection maintenance and buffering for berkeley-style tcp/ip sockets. each connection has a pair of buffers allocated, and once it has been authenticated as a given player's connection, an entry into a player-name hash table is made for faster access. the manner in which buffers are shutdown is bizarre, but it is all handled in io_sync() to prevent writes into buffers that have been freed. this way buffers *WILL* hang around 'till the end of the play/run. */ /* you can change PHSIZ, but don't mess with PHMAG */ #define PHSIZ 31 /* width of internal name hash table */ /* When we queue up a write, we stuff this in a queue, and wait for it to complete. */ typedef struct obuff { struct obuff *bck,*fwd; short int iosb[4]; char buffer[MUDBUF]; } obuff; /* This is the queue of buffers $qio'd for output */ static obuff *outQ = (obuff *)0; /* Iob flags */ #define IOBOK 001 /* OK/logged in player connection */ #define IOBKILL 002 /* kill this IOB at sync time - it's dead */ #define IOBERR 004 /* ignore this IOB - it is f***ed up. */ typedef struct { int flg; /* flags */ time_t ltim; /* last input time */ time_t ctim; /* connect time */ char who[MAXOID]; /* player object ID */ int fd; /* file desc */ char *obuf; /* malloced output buffer */ char *op; /* output buf ptr */ char *ibuf; /* malloced input buffer */ char *ip; /* input buf ptr */ int ic; /* input byte cnt */ obuff *outputbuff; /* The structure. */ short int iosb[4]; /* For read $qios */ int opcnt; /* Number of oputstanding ops */ } Iob; /* player-name to Iob resolution map an entry is made in this when the player is authenticated by the login(), and is subsequently used to quickly map names to Iobs. the mapping is destroyed when the iob is dropped by iobdrop(). don't mess with this code - it's icky. */ typedef struct pmap { Iob *iob; struct pmap *n; } Pmap; static Pmap *pmaptab[PHSIZ]; static Iob **iobtab; /* active Iob table */ static Iob *lastiob = (Iob *)0; static int onewrt = 0; /* optimization */ static int iobtabsiz; /* top bound of Iob table */ static int curriob; /* highest Iob in use */ static short int seriosb[4]; static short int plyiosb[4]; static Iob *orphans = (Iob *)0; static long timo[2]; static int serfd; /* inter server service port */ static int plyfd; /* main player service port */ struct sockaddr_in addr; /* address of newly connected */ static int sport = NET_SRVPRT; /* service port # */ static int pport = NET_PLYPRT; /* play port # */ static int net_initted = 0; static int timosec; /* Seconds per tick */ static int nxtwhoiobptr; /* used for programmed Iob traverse */ static void dec_opcnt(); /* set up the iob tables, iob maps, sockets, the whole bit */ io_init() { int x; int op; addr.sin_family = AF_INET; addr.sin_addr.s_addr = INADDR_ANY; timosec = cron_quantum; op = LIB$K_DELTA_SECONDS; lib$cvt_to_internal_time(&op,&timosec,timo); /* if you ain't got dtablesize(), fudge this with whatever your systems max file descriptor value is. erring on the high side will waste a little memory is all. */ iobtabsiz = 128; sys$clref(32L); sys$clref(33L); sys$clref(34L); sys$setef(35L); /* SET this to get the timer going. */ /* initialize the Iob hash table */ iobtab = (Iob **)malloc((unsigned)(sizeof(Iob *) * iobtabsiz)); if(iobtab == (Iob **)0) return(1); /* zero iob table */ for(x = 0; x < iobtabsiz; x++) iobtab[x] = (Iob *)0; /* zero player to Iob hash table */ for(x = 0; x < PHSIZ; x++) pmaptab[x] = (Pmap *)0; /* OPEN INTER SERVER FILE DESCRIPTOR AND BIND IT */ if((serfd = socket(AF_INET,SOCK_STREAM,0)) < 0) return(1); addr.sin_port = htons(sport); if(bind(serfd,(struct sockaddr *)&addr,sizeof(addr))) { logf("cannot bind socket: ",(char *)-1,"\n",0); return(1); } if(listen(serfd,5) == -1) { logf("cannot listen at socket: ",(char *)-1,"\n",0); return(1); } /* Queue up a wait for connects type thing. */ sys$qio(33L,serfd,IO$_ACCEPT_WAIT,seriosb,0,0,0,0,0,0,0,0); /* OPEN PLAYER ACCESS FILE DESCRIPTOR AND BIND IT */ if((plyfd = socket(AF_INET,SOCK_STREAM,0)) < 0) return(1); addr.sin_port = htons(pport); if(bind(plyfd,(struct sockaddr *)&addr,sizeof(addr))) { logf("cannot bind socket: ",(char *)-1,"\n",0); return(1); } if(listen(plyfd,5) == -1) { logf("cannot listen at socket: ",(char *)-1,"\n",0); return(1); } /* Queue up a wait for connects type thing. */ sys$qio(34L,plyfd,IO$_ACCEPT_WAIT,plyiosb,0,0,0,0,0,0,0,0); /* In VMS, the iotab is ONLY for existing connects. curriob is the high-water mark, this indicates an empty table. */ curriob = -1; net_initted++; return(0); } /* disconnect an Iob (low level) */ static void iobdrop(ip) Iob *ip; { int x,y; Pmap *pp; Pmap *pr; if(ip == (Iob *)0) return; #ifdef VMSNET_DEBUG printf("Dropping Iob on fd %d.\n",ip->fd); #endif if (ip->who[0]) { plogf("DISCONNECT %s on %d\n", ip->who, ip->fd); #ifdef USE_RWHO rwhocli_userlogout(ip->who); #endif } else plogf("DISCONNECT %d\n", ip->fd); for(y = 0; y < iobtabsiz; y++) { if(iobtab[y] == ip) { iobtab[y] = (Iob *)0; break; } } /* unlink player map hash. this is somewhat convoluted. */ for(x = 0; x < PHSIZ; x++) { pp = pmaptab[x]; while(pp != (Pmap *)0) { if(pp->iob == ip && pp == pmaptab[x]) { pr = pp; pmaptab[x] = pp->n; free((mall_t)pr); break; } else { if(pp->n != (Pmap *)0 && pp->n->iob == ip) { pr = pp->n; pp->n = pp->n->n; free((mall_t)pr); break; } } pp = pp->n; } } /* adjust top iob in use count */ /* y had BETTER not have been messed with */ if(y == curriob) curriob--; /* final cleanup */ (void)shutdown(ip->fd,2); (void)socket_close(ip->fd); if(ip->opcnt > 0){ /* Stuff it on the orphaned Iob list for io_sync() to handle */ ip->ip = (char *) orphans; orphans = ip; return; } (void)free((mall_t)ip->ibuf); (void)free((mall_t)ip->obuf); /* this is done to cause a coredump in case someone's code is so stupid as to write into an Iob that has been closed down */ ip->ibuf = ip->obuf = (char *)0; (void)free((mall_t)ip); } /* tokenize out a single line of text (destructive) this function handles a buffer of player input, returning a sequence of lines - it may move data around in the buffer, and may alter the content of the buffer as it does so. this handles cases where a line may be broken into two packets by the network - it re-assembles them efficiently. */ static char * iobgl(i) Iob *i; { char *op; int ic; while(i->ic > 0 && (*i->ip == '\n' || *i->ip == '\r')) { *i->ip++ = '\0'; i->ic--; } if((ic = i->ic) <= 0) { i->ip = i->ibuf; i->ic = 0; return((char *)0); } op = i->ip; while(1) { if(*i->ip == '\r' && *(i->ip + 1) == '\n' && ic > 1) { *i->ip++ = '\0'; *i->ip++ = '\0'; if((i->ic = ic - 2) < 1) i->ip = i->ibuf; return(op); } if((*i->ip == '\n' || *i-> ip == '\r') && ic > 0) { *i->ip++ = '\0'; if((i->ic = --ic) < 1) i->ip = i->ibuf; return(op); } /* nothing left in buffer, but no newline */ if(--ic <= 0) { if(i->ic >= MUDBUF - 1) return((char *)0); /* shift down */ bcopy(op,i->ibuf,i->ic); /* resync */ i->ip = i->ibuf; i->ic = 0; return((char *)0); } i->ip++; } } /* flush a single Iob */ static int iobflush(ip) Iob *ip; { int xx = ip->op - ip->obuf; int wr; if(ip->flg & IOBERR) return(1); /* Too much pending. Flush it. */ if(ip->opcnt > 16){ sys$qio(0,ip->fd,IO$_WRITEVBLK,0,0,0,"<Output Flushed>",16, 0,0,0,0); ip->op = ip->obuf; return(0); } /* Queue this up. */ #ifdef VMSNET_DEBUG printf("Queueing %d bytes on fd %d.\n",xx,ip->fd); #endif sys$qio(0,ip->fd,IO$_WRITEVBLK,(ip->outputbuff)->iosb,dec_opcnt,ip, ip->obuf,xx,0,0,0,0); ip->opcnt++; if(outQ == (obuff *)0){ /* Empty queue */ (ip->outputbuff)->fwd = (ip->outputbuff)->bck = (obuff *)0; outQ = ip->outputbuff; } else { (ip->outputbuff)->fwd = outQ; (ip->outputbuff)->bck = outQ->bck; (outQ->bck)->fwd = ip->outputbuff; outQ->bck = ip->outputbuff; } /* Get a new obuffer. */ if(getnewobuff(ip) == -1){ ip->flg |= IOBERR; return(1); } return(0); } /* VARARGS1 */ void say(who,va_alist) char *who; va_dcl { Pmap *p; char *s; va_list ap; if(who == (char *)0 || who[0] == '\0' || !net_initted) return; for(p = pmaptab[objid_hash(who,PHSIZ)];p != (Pmap *)0; p = p->n) { /* wrong guy */ if((p->iob->flg & IOBERR) || strcmp(p->iob->who,who)) continue; if(lastiob != p->iob) { lastiob = p->iob; onewrt++; } va_start(ap); while((s = va_arg(ap,char *)) != (char *)0) { while(*s) { if((p->iob->op - p->iob->obuf) > MUDBUF - 1) { if(iobflush(p->iob)) goto dropthru; } else { *p->iob->op++ = *s++; } } } dropthru: va_end(ap); } } #ifdef CONNONLY int playerconn(who) char *who; { Pmap *p; if(who == (char *)0 || who[0] == '\0' || !net_initted) return(0); for(p = pmaptab[objid_hash(who,PHSIZ)];p != (Pmap *)0; p = p->n) { if((p->iob->flg & IOBERR) || strcmp(p->iob->who,who)) /* wrong guy */ continue; else return(1); } return(0); } #endif void io_logoff(who) char *who; { Pmap *p; if(who == (char *)0 || who[0] == '\0' || !net_initted) return; p = pmaptab[objid_hash(who,PHSIZ)]; while(p != (Pmap *)0) { if(strcmp(p->iob->who,who)) { p = p->n; continue; } p->iob->flg |= IOBKILL; onewrt = 2; p = p->n; } } /* VARARGS1 */ void iobsay(ip,va_alist) Iob *ip; va_dcl { char *s; va_list ap; if(ip->flg & IOBERR) return; if(lastiob != ip) { lastiob = ip; onewrt++; } va_start(ap); while((s = va_arg(ap,char *)) != (char *)0) { while(*s) { if((ip->op - ip->obuf) > MUDBUF - 1) { if(iobflush(ip)) return; } else { *ip->op++ = *s++; } } } va_end(ap); } /* This trots down the list of orphaned Iob's, checking to see if any have all outstanbding ops on them completed, and freeing those. */ static void purge_orphans() { Iob *bp,*tmp; for(bp = orphans; bp != (Iob *) 0; bp = (Iob *) bp->ip) { if(bp->opcnt <= 0 && bp == orphans){ orphans = (char *) bp->ip; free((mall_t) bp->ibuf); free((mall_t) bp->obuf); bp->ibuf = bp->obuf = (char *)0; free((mall_t)bp); bp = orphans; } else { #ifdef VMSNET_DEBUG printf("Orphaned Iob on fd %d has pending ops.\n",bp->fd); #endif if(bp->ip != (char *)0 && ((Iob *)bp->ip)->opcnt <= 0){ tmp = (Iob *) bp->ip; bp->ip = ((Iob *)bp->ip)->ip; free((mall_t) tmp->ibuf); free((mall_t) tmp->obuf); tmp->ibuf = tmp->obuf = (char *)0; free((mall_t) tmp); } } } } /* flush the connected (valid) Iobs */ void io_sync() { int n; Iob *bp; #ifdef VMSNET_DEBUG printf("Syncing..\n"); #endif if(!onewrt || !net_initted) return; /* WARNING!!! the calls to goodbye() actually may make more Iob writes as a result of a player hangup! Do NOT do much in goodbye!() or madness may result. */ if(onewrt == 1 && lastiob != (Iob *)0) { #ifdef VMSNET_DEBUG printf("Only one Iob to flush.\n"); #endif if(iobflush(lastiob)) { if(lastiob->flg & IOBOK) goodbye(lastiob->who); iobdrop(lastiob); return; } if(lastiob->flg & IOBERR || lastiob->flg & IOBKILL) { if(lastiob->flg & IOBOK) goodbye(lastiob->who); iobdrop(lastiob); } purge_orphans(); return; } for(n = 0; n < iobtabsiz; n++) { if(iobtab[n] != (Iob *)0) { if(iobtab[n]->op > iobtab[n]->obuf) { if(iobflush(iobtab[n])) { if(iobtab[n]->flg & IOBOK) goodbye(iobtab[n]->who); iobdrop(iobtab[n]); } continue; } if(iobtab[n]->flg & IOBKILL || iobtab[n]->flg & IOBERR) { if(iobtab[n]->flg & IOBOK) goodbye(iobtab[n]->who); iobdrop(iobtab[n]); } } } onewrt = 1; lastiob = (Iob *)0; purge_orphans(); } /* wrapper around the player authentication routine. said routine will return a 1 if the login is valid, a 0 if it is not. if the login is OK, we then fiddle some Iob values and make a hash-table map entry. if login() returns -1, then we are to close the connection. */ static void io_dologin(bp,line) Iob *bp; char *line; { Pmap *pm; int hv; hv = login(line,bp->who); if(hv == 0) { iobsay(bp,"Either that object does not exist, or the password was incorrect, butthead.\n",(char *)0); logf("badlogin: ",line,"\n",(char *)0); return; } if(hv == -1) { bp->flg |= IOBKILL; onewrt = 2; return; } bp->flg |= IOBOK; /* now add a pointer to the character's name in the Iob hash */ pm = (Pmap *)malloc(sizeof(Pmap)); if(pm == (Pmap *)0) fatal("out of memory building new connection\n",(char *)0); pm->iob = bp; pm->n = pmaptab[(hv = objid_hash(bp->who,PHSIZ))]; pmaptab[hv] = pm; plogf("CONNECT %s on %d\n", bp->who, bp->fd); welcome(bp->who); #ifdef USE_RWHO rwhocli_userlogin(bp->who,ut_name(bp->who),bp->ctim); #endif } /* main I/O loop - listens for new connections, accepts them, validates them, reads input, and dispatches it. */ io_loop() { Iob *bp; int n; int seld; int rd; char *lp; time_t now; int x; int eflags; #ifdef VMSNET_DEBUG printf("Waiting..."); #endif if(!net_initted) return(1); /* Set up a timer. */ sys$readef(35L,&eflags); /* If the last timer has expired, or tick interval changed */ if(eflags & 8 || cron_quantum != timosec){ /* Has our tick interval changed? */ if(cron_quantum != timosec){ int op = LIB$K_DELTA_SECONDS; timosec = cron_quantum; lib$cvt_to_internal_time(&op,&timosec,timo); } #ifdef VMSNET_DEBUG printf("Setting timer for %d seconds.\n",timosec); #endif sys$cantim(1); sys$setimr(35L,timo,0,1,0); } sys$wflor(32L,15L); /* Wait on event flags 32, through 35. */ sys$readef(32L,&eflags); /* Read this event flag group. */ #ifdef VMSNET_DEBUG printf("Event! flags are: %x\n",eflags); #endif if(eflags & 7 == 0) /* None of the good flags are set. yawn. */ return(0); /* start the clock */ (void)time(&now); /* new SERVER TO SERVER connection */ if(eflags & 2){ /* 33 is masked 0x0002 in this group. */ n = accept(serfd,(struct sockaddr *)0,(int *)0); #ifdef VMSNET_DEBUG printf("New server connect on fd %d.\n",n); #endif if(n == -1) { logf("aiee! connect on server port dropped\n",0); socket_close(n); } else { /* deal with it */ xact_in(n); /* This even closes the channel */ } /* Queue another one */ sys$qio(33L,serfd,IO$_ACCEPT_WAIT,seriosb,0,0,0,0,0,0,0,0); } /* new PLAYER connection */ if(eflags & 4){ /* 34 is masked 0x0004 in this group */ n = accept(plyfd,(struct sockaddr *)0,(int *)0); #ifdef VMSNET_DEBUG printf("New player connect on fd %d.\n",n); #endif if(n != -1) { /* Queue another one */ sys$qio(34L,plyfd,IO$_ACCEPT_WAIT,plyiosb, 0,0,0,0,0,0,0,0); bp = (Iob *)malloc(sizeof(Iob)); if(bp == (Iob *)0) { logf("cannot alloc Iob: ",(char *)-1,"\n",0); return(-1); } bp->flg = 0; bp->who[0] = '\0'; bp->fd = n; bp->ctim = now; if((bp->ibuf = (char *)malloc(MUDBUF)) == (char *)0) return(-1); bp->ip = bp->ibuf; bp->ic = 0; if(getnewobuff(bp) == -1) return(-1); bp->op = bp->obuf; bp->opcnt = 0; /* queue up a read on the new socket */ #ifdef VMSNET_DEBUG printf("Queueing initial read.\n"); #endif sys$qio(32L,bp->fd,IO$_READVBLK,bp->iosb, dec_opcnt,bp, bp->ibuf + bp->ic,MUDBUF - bp->ic - 1, 0,0,0,0); /* Put it in the iobtable */ for(x=0; x < iobtabsiz; x++){ if(iobtab[x] == (Iob *)0) break; } if(x == iobtabsiz || iobtab[x] != (Iob *)0){ iobsay(bp,"No space!\n",(char *)0); iobdrop(bp); } else { iobtab[x] = bp; if(x > curriob){ curriob = x; } } iobsay(bp,"c[onnect] objectid password\n",(char *)0); } } mainloop: /* check input on existing fds. */ /* Clear the event flag NOW. It's safe to do it here. */ sys$clref(32L); for(n = 0; n <= curriob; n++) { if((bp = iobtab[n]) != (Iob *)0 && bp->iosb[0] == SS$_NORMAL) { rd = bp->iosb[1]; #ifdef VMSNET_DEBUG printf("Read %d bytes on fd %d\n",rd,bp->fd); #endif if(rd <= 0) { bp->flg |= IOBERR | IOBKILL; onewrt = 2; continue; } /* increment count of bytes in buffer */ bp->ic += rd; /* adjust last active time */ bp->ltim = now; /* process input based on state of connection */ while((lp = iobgl(bp)) != (char *)0) { if(bp->flg & IOBOK) run(bp->who,bp->who,lp,0,(char **)0,1); else io_dologin(bp,lp); } bp->opcnt++; /* queue up another read */ sys$qio(32L,bp->fd,IO$_READVBLK,bp->iosb, dec_opcnt,bp, bp->ibuf + bp->ic,MUDBUF - bp->ic - 1, 0,0,0,0); continue; } /* lastly, check exceptions in case of no input */ if((bp = iobtab[n]) != (Iob *)0 && bp->iosb[0] != 0 && bp->iosb[0] != SS$_NORMAL) { #ifdef VMSNET_DEBUG printf("Error %x on fd %d.\n",bp->iosb[0],bp->fd); #endif /* default case */ if(bp != (Iob *)0) { bp->flg |= IOBERR | IOBKILL; onewrt = 2; } continue; } } /* e! */ return(0); } /* ARGSUSED */ cmd__netconfig(argc,argv,who) int argc; char *argv[]; char *who; { static char *nactm = "network layer is already active.\n"; static char *badp = "invalid port number.\n"; /* configure service port */ if(!strcmp(argv[1],"playport")) { int tmpx; if(net_initted) { logf(nactm,(char *)0); return(UERR_FATAL); } if(argc < 3 || (tmpx = atoi(argv[2])) <= 0) { logf(badp,(char *)0); return(UERR_FATAL); } pport = tmpx; logf("player port is #",argv[2],"\n",(char *)0); return(UERR_NONE); } /* configure service port */ if(!strcmp(argv[1],"servport")) { int tmpx; if(net_initted) { logf(nactm,(char *)0); return(UERR_FATAL); } if(argc < 3 || (tmpx = atoi(argv[2])) <= 0) { logf(badp,(char *)0); return(UERR_BADPARAM); } sport = tmpx; logf("server port is #",argv[2],"\n",(char *)0); return(UERR_NONE); } logf("_netconfig: I don't understand ",argv[1],"\n",(char *)0); return(UERR_BADPARAM); } /* ARGSUSED */ cmd_WHO(argc,argv,who,aswho) int argc; char *argv[]; char *who; char *aswho; { int x; int jj; time_t now; char xuf[180]; extern char *sprintf(); if(!net_initted) return(UERR_FATAL); (void)time(&now); say(who,"Player Name On For Idle\n",(char *)0); if(argc < 2) { for(x = 0; x < iobtabsiz; x++) { if(iobtab[x] != (Iob *)0 && (iobtab[x]->flg & IOBOK) && iobtab[x]->who[0] != '\0') { sprintf(xuf,"%-21.21s %3d:%02d.%02d %3d:%02d.%02d (%s)\n", ut_name(iobtab[x]->who), (int)((now - iobtab[x]->ctim) / 3600), (int)((now - iobtab[x]->ctim) / 60), (int)((now - iobtab[x]->ctim) % 60), (int)((now - iobtab[x]->ltim) / 3600), (int)((now - iobtab[x]->ltim) / 60), (int)((now - iobtab[x]->ltim) % 60), iobtab[x]->who); say(who,xuf,(char *)0); } } return(UERR_NONE); } for(jj = 1; jj < argc; jj++ ) { char *na; for(x = 0; x < iobtabsiz; x++) { if(iobtab[x] != (Iob *)0 && (iobtab[x]->flg & IOBOK) && iobtab[x]->who[0] != '\0') { na = ut_name(iobtab[x]->who); if(na != (char *)0 && matchstr(na,argv[jj],0) != 0) { sprintf(xuf,"%-21.21s %3d:%02d.%02d %3d:%02d.%02d (%s)\n", na, (int)((now - iobtab[x]->ctim) / 3600), (int)((now - iobtab[x]->ctim) / 60), (int)((now - iobtab[x]->ctim) % 60), (int)((now - iobtab[x]->ltim) / 3600), (int)((now - iobtab[x]->ltim) / 60), (int)((now - iobtab[x]->ltim) % 60), iobtab[x]->who); say(who,xuf,(char *)0); } } } } return(UERR_NONE); } /* This is called when any queued operation on a socket completes. It merely decrements the count of outstanding operations on the socket, so we can tell when the socket is recycleable. */ void dec_opcnt(bp) Iob *bp; { bp->opcnt--; } /* Get a new obuff for an Iob, and hook it up. */ int getnewobuff(ip) Iob *ip; { obuff *skip; int found; /* Scan down the list of queued obuffs for one that's done. */ skip = outQ; found = 0; if(skip != (obuff *)0){ do { if(skip->iosb[0] != 0){ /* Hah! It's *done* */ found = 1; break; } skip = skip->fwd; } while(skip != outQ); } if(!found){ /* Nothing free, malloc one */ #ifdef VMSNET_DEBUG printf("No free obuff's, mallocing one\n"); #endif if( (ip->outputbuff = (obuff *) malloc(sizeof(obuff))) == (obuff *)0){ return(-1); } } else { /* skip points at something we can rip out of the queue */ if(skip->fwd == skip->bck){ outQ = (obuff *)0; } else { (skip->bck)->fwd = skip->fwd; (skip->fwd)->bck = skip->bck; if(outQ == skip) outQ = skip->fwd; } ip->outputbuff = skip; } ip->op = ip->obuf = (ip->outputbuff)->buffer; #ifdef VMSNET_DEBUG printf("Got new obuff for Iob on fd %d.\n",ip->fd); #endif return(0); } /* reset pointer to live Iobs for programmed traverse */ void io_rstnxtwho() { nxtwhoiobptr = 0; } /* programmed traverse of object-IDs of logged-in players */ char *io_nxtwho(timp) time_t *timp; { if(nxtwhoiobptr > curriob || !net_initted) return((char *)0); while(nxtwhoiobptr <= curriob) { if(iobtab[nxtwhoiobptr] != (Iob *)0 && (iobtab[nxtwhoiobptr]->flg & IOBOK) && (iobtab[nxtwhoiobptr]->flg & (IOBKILL|IOBERR)) == 0 && iobtab[nxtwhoiobptr]->who[0] != '\0') { if(timp != (time_t *)0) *timp = iobtab[nxtwhoiobptr]->ltim; return(iobtab[nxtwhoiobptr++]->who); } nxtwhoiobptr++; } return((char *)0); }