/* * This file contains routines useful to the applications developer who * must read or write BDT data. */ /* $Log: bdt.c,v $ * Revision 2.0 85/11/21 07:22:02 jqj * 4.3BSD standard release * * Revision 1.4 85/03/11 16:36:38 jqj * *** empty log message *** * * Revision 1.4 85/03/11 16:36:38 jqj * Public alpha-test version, released 11 March 1985 * * Revision 1.3 85/03/11 16:34:19 jqj * Public alpha-test version, released 11 March 1985 * * Revision 1.2 85/01/27 07:37:06 jqj * finished but undebugged version * */ #ifndef lint static char rcsid[] = "$Header: bdt.c,v 2.0 85/11/21 07:22:02 jqj Exp $"; #endif #include #include #include /* for socket.h and xn.h */ #include #include /* for scatter/gather io */ #include /* for XNS addresses and courierconnection.h */ #include #include /* for spphdr */ #include "courier.h" #include "realcourierconnection.h" #define MAKEVEC(idx, addr, len) our_iovec[idx].iov_base = (caddr_t)addr;\ our_iovec[idx].iov_len = len; int BDTwrite(f,buffer,nbytes) /* Call with CourierConnection*, not *(CourierConnection*) */ /* Semantics are much like write(), except that it returns -1 * if a BDT abort message arrives from receiver. * Returns # of bytes actually written. */ register CourierConnection *f; char *buffer; int nbytes; { register int n, w; struct iovec our_iovec[2]; MAKEVEC(0, &(f->sphdrOpts), sizeof(f->sphdrOpts)); MAKEVEC(1, buffer, SPPMAXDATA); if (f->bdtstate == wantdata) { /* stream=BDT, EOM=FALSE, Attn=FALSE */ f->sphdrOpts.sp_dt = SPPSST_BDT; f->sphdrOpts.sp_cc &= ~SP_EM; f->bdtstate = established; } if (BDTabortSeen(f)) { BDTabort(f); /* send end (abort) */ f->abortseen = FALSE; /* clear abort */ f->bdtstate = bdteomseen; return(-1); /* truncate the stream */ } /* ### if nbytes > SPPMAXDATA, do something intelligent? */ for(n = nbytes; n > SPPMAXDATA; n -= SPPMAXDATA) { w = writev(f->fd, our_iovec, 2); if(w < SPPMAXDATA) return( w + nbytes - n); our_iovec[1].iov_base += SPPMAXDATA; } our_iovec[1].iov_len = n; w = writev(f->fd, our_iovec, 2); return( w + nbytes - n); } int BDTclosewrite(f) /* call with CourierConnection*, not *(CourierConnection*) */ /* End a BDT connection. Returns 0 on success, -1 on failure. */ register CourierConnection *f; { f->bdtstate = bdteomseen; if (BDTabortSeen(f)) { BDTabort(f); f->abortseen = FALSE; return(-1); } /* stream=BDT, EOM=TRUE, Attn=FALSE */ f->sphdrOpts.sp_dt = SPPSST_BDT; f->sphdrOpts.sp_cc |= SP_EM; /* finally, send normal end in a packet of its own */ write(f->fd,(char*)&f->sphdrOpts,sizeof(struct sphdr)); return(0); } int BDTread(f, buffer, nbytes) /* Call with CourierConnection*, not *(CourierConnection*) */ /* Semantics are much like read(), except that it returns -1 on * more conditions. Returns number of characters actually read, * or 0 on end of message. */ register CourierConnection *f; char *buffer; int nbytes; { register int count; struct { struct sphdr hdr; char data[SPPMAXDATA]; } packbuf; struct iovec our_iovec[2]; switch (f->state) { case closed: case calldone: fprintf(stderr,"BDTread() called while connection state = %s\n", (f->state == closed) ? "closed" : "calldone"); exit(1); /* NOTREACHED */ case wantversion: count = recv(f->fd, (char*) &packbuf, sizeof(packbuf), MSG_PEEK) - sizeof(struct sphdr); while (count == 0 && packbuf.hdr.sp_dt == SPPSST_RPC) { read(f->fd, (char*) &packbuf, sizeof(packbuf)); count = recv(f->fd, (char*) &packbuf, sizeof(packbuf), MSG_PEEK) - sizeof(struct sphdr); } if (count == 0) /* streamtype != SPPSST_RPC, so we can't */ /* have a version number */ break; /* fall out of switch, still wantversion */ /* ### N.B. we don't handle count==2 */ else if (count != (2*sizeof(Cardinal))) /* must be a REJECT or ABORT message */ /* let someone else handle it! */ return(-1); else { /* must be a Courier version number */ /* read it and throw it away */ read(f->fd, (char*) &packbuf, sizeof(packbuf)); f->state = inprogress; /* fall into case inprogress */ } case inprogress: switch (f->bdtstate) { case wantdata: count = recv(f->fd, (char*) &packbuf, sizeof(packbuf), MSG_PEEK) - sizeof(struct sphdr); if (packbuf.hdr.sp_dt == SPPSST_RPC) return(-1); f->bdtstate = established; /* fall through to case established */ case established: break; /* fall out of inner (and outer!) switch */ case bdteomseen: return(0); } break; } MAKEVEC(0,&packbuf.hdr,sizeof(struct sphdr)); MAKEVEC(1,buffer,nbytes); count = readv(f->fd,our_iovec,2) - sizeof(struct sphdr); /* at this point, we've read a packet that isn't SPPSST_RPC */ while (TRUE) { if (packbuf.hdr.sp_dt == SPPSST_END) { (void) sppclosereply(f->fd); f->state = closed; fprintf(stderr,"SPP END received during BDT\n"); exit(1); } if (packbuf.hdr.sp_dt != SPPSST_BDT) { fprintf(stderr, "wrong stream type, %d, seen during BDT\n", packbuf.hdr.sp_dt); exit(1); /* NOTREACHED */ } if (f->abortseen || (packbuf.hdr.sp_cc & SP_OB)) { f->abortseen = TRUE; return(-1); } if (packbuf.hdr.sp_cc & SP_EM) { f->bdtstate = bdteomseen; /* next read will return 0 */ return(count); } if (count > 0) return(count); count = readv(f->fd,our_iovec,2) - sizeof(struct sphdr); } } BDTabort(f) register CourierConnection *f; { static struct handy { struct sphdr hdr; char value; } data; /* stream=BDT, EOM=FALSE, Attn=TRUE */ data.hdr.sp_dt = SPPSST_BDT; data.hdr.sp_cc = SP_EM; data.value = 1; /* BDT abort data value */ send(f->fd, &data, sizeof(data), MSG_OOB); f->bdtstate = bdteomseen; f->abortseen = TRUE; } BDTabortSeen(f) register CourierConnection *f; { struct { struct sphdr hdr; Unspecified data[MAXWORDS]; } packbuf; int fdmask; register int count; static struct timeval timeout = {0,0}; fdmask = 1<<(f->fd); /* ### code here for OOB signalling! */ while (select(f->fd+1,&fdmask,(int*)NULL,(int*)NULL,&timeout) > 0 && (count = recv(f->fd,(char*)&packbuf, sizeof(packbuf), MSG_PEEK) - sizeof(struct sphdr)) > 0) { if (packbuf.hdr.sp_dt == SPPSST_BDT && (packbuf.hdr.sp_dt & SP_OB) && count == 1) { read(f->fd, (char*)&packbuf, sizeof(packbuf)); f->abortseen = TRUE; return(TRUE); } else if (count == 0) read(f->fd, (char*)&packbuf, sizeof(packbuf)); else return(FALSE); } return(FALSE); }