Retired Document
Important: This sample code may not represent best practices for current development. The project may use deprecated symbols and illustrate technologies and techniques that are no longer recommended.
PAPServerSample.c
/* |
File: PAPServerSample.c |
Contains: This sample demonstrates the implementation of a PAP server using Open Transport. |
Issues: |
1. PostScript Query responses are default only |
To respond to the LaserWriter client, the PostScript query handler code is |
designed to return the default responses. A real PAP spooler would have to |
respond more appropriately for the printer that it is supporting. This could mean |
delaying the response back to the client until the spooler can actually |
pass the query to the printer and get a real response. The code for detecting |
a PostScript query is not very robust. The purpose of this sample was not to |
demonstrate PostScript support, but to packet flow via OT. |
2. If the received data is not part of a PostScript query, the spooler sample |
assumes that all of the incoming data is part of the same job. There is no check |
for the PostScript EOF indicator to distinguish different spooler jobs. All of the |
incoming data is saved to a temporary disk file at the root of the boot hard drive. |
The code does not try to preflight the amount of storage present, nor protect |
some minimum hard drive storage in case the hard drive is close to full. |
3. This sample requires OT 1.1.2 or greater. |
4. This sample support multiple handoff endpoints. The constant kMaxHandoffEPs |
defines the maximum number of handoff endpoints that this sample supports. I have |
tested that the server supports 2 endpoint connections at the same time, but have not |
gone into testing more simultaneous connections. |
Written by: Rich Kubota |
Copyright: Copyright © 1999 by Apple Computer, Inc., All Rights Reserved. |
You may incorporate this Apple sample source code into your program(s) without |
restriction. This Apple sample source code has been provided "AS IS" and the |
responsibility for its operation is yours. You are not permitted to redistribute |
this Apple sample source code as "Apple sample source code" after having made |
changes. If you're going to re-distribute the source, we require that you make |
it clear in the source that the code was descended from Apple sample source |
code, but that you've made changes. |
Change History (most recent first): |
7/22/1999 Karl Groethe Updated for Metrowerks Codewarror Pro 2.1 |
*/ |
#define HANDOFF_EP 1 // set to to to indicate use of handoff endpoint |
#include <GestaltEqu.h> |
#include <TextUtils.h> |
#include <DiskInit.h> |
#include <Types.h> |
#include <Errors.h> |
#include <String.h> |
#include <ToolUtils.h> |
#include "StringUtils.h" |
#include <AppleTalk.h> |
#include "OpenTransport.h" |
#include "OpenTptAppleTalk.h" |
#include "ATalkSampleUtils.h" |
#include "PAPServerUtilities.h" |
#include "PAPServerSample.h" |
#include "PAPPostScriptStuff.h" |
extern OSStatus OTSetMemoryLimits(size_t growSize, size_t maxSize); |
/********************/ |
/* globals */ |
/********************/ |
DDPAddress gAddr; |
MyEndpointRef gEp; |
#if HANDOFF_EP |
MyEndpointRef gHandoffEp[kMaxHandoffEPs]; |
#endif |
OTLIFO* gFreeQ; // note that an OTLIFO structure must be on a 4-byte boundary |
// instead of trying to force it to be on a 4-byte boundary in |
// global memory, we use a pointer, then allocate memory |
// which will force the global to be on a 4-byte boundary. |
UInt32 gFlags = 0; |
UInt32 gBufsAvail; |
PacketPtr gTempPackPtr; |
short gFRefNumToClose = 0; |
Boolean gDone = false; |
Str255 gIdleStr; |
Str255 gBusyStr; |
Str255 gServerNBPNameStr; |
UInt32 gOptionCompleted; // used to flag completion of Option Management calls |
extern char gEOFStr[8]; |
extern char gBeginPSStr[8]; |
extern char qBeginQueryStr[8]; |
extern char gEndStr[8]; |
extern char gQueryStr[8]; |
OSType gOTVersionSelector = 'otvr'; |
UInt32 gOTVersion; |
/******************************************************************************* |
** Prototypes |
********************************************************************************/ |
pascal void EventHandler(void*, OTEventCode, OTResult, void*); |
static void EnterListenAccept(MyEndpointRef *myServerEp); |
void DoListenAccept(MyEndpointRef *myServerEp); |
Boolean ReceiveOnePacket(MyEndpointRef *myEp, PacketPtr packetPtr); |
void DoReceiveData(MyEndpointRef *myEp); |
OSStatus DoDisconnect(MyEndpointRef *myEp); |
OSStatus DoOrderlyDisconnect(MyEndpointRef *myEp); |
OSStatus PollQueueList(void); |
OSErr ProcessIncomingDataFile(MyEndpointRef *theEp, PacketPtr packetPtr); |
Boolean TestDataIsPSQuery(PacketPtr packetPtr); |
OSStatus ProcessPSQuery(PacketPtr packetPtr); |
Boolean DoProcessPSQuery(PacketPtr packetPtr); |
OSErr CheckFileToClose(void); |
void DoServer(void); |
void DoEvent(EventRecord *event); |
OSStatus DoBind(EndpointRef ep, UInt8 socket, UInt8 type, OTQLen qlen, |
char *nbpName, UInt32 nbpNameLen); |
OSStatus ActivatePAPEndpoint(MyEndpointRef *myEp); |
OSStatus InitPAPServerStuff(void); |
void ClosePAPServerStuff(void); |
OSStatus InitMyEndpointRef(MyEndpointRef *myEp); |
OSStatus InitPAPBuffers(void); |
void ReleasePAPMemory(void); |
extern OSStatus DoNegotiateEOMOption(EndpointRef ep, Boolean enableEOM); |
extern OSStatus DoSetServerStatusOption(EndpointRef ep, char *statusStr); |
extern OSStatus DoNegotiateSelfSendOption(EndpointRef ep, long enableSelfSend); |
void DoValueBreak(long value, const char* message); |
OSStatus BeginSetServerStatusOption(EndpointRef ep, UInt16 whichStr); |
UInt32 GetYesNoOption(void); |
Boolean EndpointsAllBusy(void); |
#if HANDOFF_EP |
MyEndpointRef* FindFreeHandoffEp(void); |
#endif |
/******************************************************************************* |
** EventHandler |
** The event handler can be called at times when it is not safe to do console I/O, |
** so this routine takes the event and jams it on a list to be handled when |
** we know it's safe to print informative messages |
********************************************************************************/ |
pascal void EventHandler(void* contextPtr, OTEventCode code, |
OTResult result, void* cookie) |
{ |
#pragma unused(result,cookie) |
switch (code) |
{ |
case T_LISTEN: /* An connection request is available */ |
SetListenPendFlag(((MyEndpointRef*)contextPtr)->flags); |
DoListenAccept(contextPtr); |
break; |
// T_DATA: |
// |
// The main rule for processing T_DATA's is to remember that once you have |
// a T_DATA, you won't get another one until you have read to a kOTNoDataErr. |
// The advanced rule is to remember that you could get another T_DATA |
// during an OTRcv() which will eventually return kOTNoDataErr, presenting |
// the application with a synchronization issue to be most careful about. |
// |
// In this application, since an OTRcv() calls are made from inside the notifier, |
// this particular synchronization issue doesn't become a problem. |
// |
case T_DATA: /* Standard data is available */ |
// check that we are in the DATAXFER state |
SetHasDataFlag(((MyEndpointRef*)contextPtr)->flags); |
if (TstPassconFlag(((MyEndpointRef*)contextPtr)->flags)) |
DoReceiveData(contextPtr); |
break; |
case T_DISCONNECT: /* A disconnect is available */ |
// check whether there is a file open on this endpoint |
DoDisconnect(contextPtr); |
break; |
case T_ERROR: /* obsolete/unused in library */ |
case T_UDERR: /* A Unit Data Error has occurred */ |
#if SHOW_DEBUG_FLOW |
DebugStr((const unsigned char *)"\p T_ERROR called;g"); |
#endif |
break; |
case T_ORDREL: /* An orderly release is available */ |
DoOrderlyDisconnect(contextPtr); |
break; |
case T_GODATA: /* Flow control lifted on standard data */ |
// for this sample, I do not expect this event to occur. |
#if SHOW_DEBUG_FLOW |
DebugStr((const unsigned char *)"\p T_GODATA called;g"); |
#endif |
break; |
case T_GOEXDATA: /* Flow control lifted on expedited data*/ |
// for this sample, I do not expect this event to occur. |
#if SHOW_DEBUG_FLOW |
DebugStr((const unsigned char *)"\p T_GOEXDATA called;g"); |
#endif |
break; |
case T_PASSCON: /* State is now case T_DATAXFER */ |
// set the passcon flag |
// set flag to indicate that the endpoint is busy |
SetEPBusyFlag(((MyEndpointRef*)contextPtr)->flags); |
// set flag to teel system to set server status flag appropriately |
// for multiple handoff endpoint servers, this would need to be |
// handled differently. |
SetCheckOptFlag(gFlags); |
SetPassconFlag(((MyEndpointRef*)contextPtr)->flags); |
// Set the timer for this connection |
OTGetTimeStamp(&((MyEndpointRef*)contextPtr)->timerDog); |
// check to see if there is any data to process |
DoReceiveData(contextPtr); |
break; |
case T_RESET: /* Protocol has been reset */ |
#if SHOW_DEBUG_FLOW |
DebugStr("\p T_RESET called;g"); |
#endif |
break; |
case T_ACCEPTCOMPLETE: /* Accept call is complete */ |
#if SHOW_DEBUG_FLOW |
DebugStr("\p T_ACCEPTCOMPLETE called;g"); |
#endif |
ClrAcceptPendFlag(((MyEndpointRef*)contextPtr)->flags); |
break; |
case T_DISCONNECTCOMPLETE: /* Disconnect call is complete */ |
break; |
case T_UNBINDCOMPLETE: |
#if SHOW_DEBUG_FLOW |
DebugStr("\p T_UNBINDCOMPLETE called;g"); |
#endif |
SetCheckOptFlag(gFlags); |
ClrEPBusyFlag(((MyEndpointRef*)contextPtr)->flags); |
SetStatusIdleFlag(gFlags); |
ClrEPBoundFlag(((MyEndpointRef*)contextPtr)->flags); // is the server endpoint bound |
break; |
// |
// kStreamIoctlEvent: |
// |
// This event is returned when an I_FLUSH ioctl has completed. |
// The flush was done in an attempt to get back all T_MEMORYRELEASED events |
// for outstanding OTSnd() calls with Ack Sends. Errors are ignored at this point since it is |
// possible that the connection will already be gone, etc. |
// |
case kStreamIoctlEvent: |
return; |
break; |
case T_OPTMGMTCOMPLETE: |
gOptionCompleted = 1; |
break; |
default: |
break; |
} |
} |
// |
// EnterListenAccept |
// |
// This is a front end to DoListenAccept() which is used whenever |
// it is not being called from inside the listener endpoint's notifier. |
// We do this for syncrhonization. If we were processing an OTListen() |
// or an OTAccept() and we were interrupted at the listener endpoint's |
// notifier with a T_LISTEN, etc, it would be inconvenient and would require |
// some more sophisticated synchronization code to deal with the problem. |
// The easy way to avoid this is to do an OTEnterNotifier() on the listener's |
// endpoint. |
// |
// Important note - doing OTEnterNotifier on one endpoint only prevents that |
// endpoint's notifier for interrupting us. Since the same notifier code |
// is used for lots of endpoints here, remember that the one endpoint's |
// notifier can interrupt another. Doing an OTEnterNotifier() on the |
// listener endpoint prevents the listener from interrupting us, but it |
// does not prevent the Notifier() routine from interrupting us via |
// another endpoint which also uses the same routine. |
// |
// Important note #2 - Don't ever do an OTEnterNotifier on an acceptor endpoint |
// before doing the OTAccept(). This confuses OT and creates problems. |
// |
static void EnterListenAccept(MyEndpointRef *myServerEp) |
{ |
Boolean doLeave; |
doLeave = OTEnterNotifier(myServerEp->ep); |
DoListenAccept(myServerEp); |
if (doLeave) |
OTLeaveNotifier(myServerEp->ep); |
} |
/* |
DoListenAccept is designed to handle an incoming connect request. This module demonstrates a |
very complex task that all X/OPEN style acceptors must implement. The problem stems from |
fact that an endpoint may be hit by a number of simultaneous connect requests. In addition |
if there is a connection already established and the endpoint handles multiple |
handoff endpoints, then there could also be an incoming disconnect request to process before |
one can issue either an Accept or Disconnect response, all pending Connect requests must |
be consumed from the streamhead first. This places a burden on the accept routine to have |
a place to temporarily stash all of the addresses to which discon messages will have to be |
sent. |
Refer to Tech Note 1059 for the 8 steps to handling an incoming connection request. |
Note that once all of the connect request have been consumed and we are in the process of |
sending discon responses, it could be that there will again be an kOTLookErr, which |
indicates that another incoming connect request heeds to be consumed before |
resuming with the discon responses. |
In this sample we consume the first listen request. We then check whether we can accept |
the incoming request, If so, then call OTAccept. If OTAccept fails with a lookErr, then |
there is another connect request to be consumed. |
processed. |
*/ |
void DoListenAccept(MyEndpointRef *myServerEp) |
{ |
MyEndpointRef *myAcceptEp; |
OSStatus err; |
TDiscon discon; |
OTResult result; |
TCall call; |
Boolean done = false; |
// check we have already entered DoListenAccept from the main event loop and are |
// trying to do so again from the secondary interrupt |
if (OTAtomicSetBit(&myServerEp->semaphore, kInListenLoop)) |
return; // if the bit was previous set, then we already have entered, but not exitted |
// this proc from the main event loop |
if (TstAcceptPendFlag(myServerEp->flags)) |
{ |
// we're waiting for an Accept call to complete, so don't accept a new |
// connection request as we will get an out of state error |
// set the ListenPendFlag so we will check the server endpoint later |
SetListenPendFlag(myServerEp->flags); |
done = true; |
} |
else |
ClrListenPendFlag(myServerEp->flags); |
if (done == false) |
{ |
// clear out the TCall structure |
memset(&call, 0, sizeof(TCall)); |
// set up the TCall structure |
call.addr.maxlen = sizeof(struct DDPAddress); |
call.addr.len = sizeof(struct DDPAddress); |
call.addr.buf = (unsigned char *) &gAddr; |
if (OTIsSynchronous(myServerEp->ep) == true) |
OTSetAsynchronous(myServerEp->ep); |
// step 1 for handling an incoming connection request |
err = OTListen(myServerEp->ep, &call); |
if (err == kOTNoDataErr) |
{ |
#if SHOW_DEBUG_FLOW |
DebugStr("\p kOTNoDataErr returned by OTListen"); |
#endif |
// don't need to do anything |
} |
else if (err == kOTLookErr) |
{ |
// step 2 for handling incoming connection requests |
// handle disconnect indications for a pending connection request |
// the only look indication allowed on an OTListen call is |
// T_DISCONNECT |
result = OTLook(myServerEp->ep); |
if (result == T_DISCONNECT) |
{ |
// this is what we were expecting |
// the disconnect is for an outstanding listen request |
err = OTRcvDisconnect(myServerEp->ep, &discon); |
} |
else |
{ |
#if SHOW_DEBUG_FLOW |
DebugStr((const unsigned char *)"\p unknown event returned by OTLook"); |
#endif |
} |
done = true; |
} |
else if (err != kOTNoError) // there was an unknown error in doing the listen |
{ |
DoValueBreak((long)err, "error occured on OTListen #"); |
done = true; |
} |
} |
if (done == false) |
{ |
#if HANDOFF_EP |
myAcceptEp = FindFreeHandoffEp(); |
#else |
if (TstEPBusyFlag(myServerEp.flags) == false) |
myAcceptEp = myServerEp; |
else |
myAcceptEp = nil; |
#endif |
} |
if (myAcceptEp == nil) |
{ |
// if there are no available handoff endpoints, then send the disconnect |
OTSndDisconnect(myServerEp->ep, &call); |
done = true; |
} |
if (done == false) |
{ |
// set a flag to indicate that an accept call is pending |
// if we try to accept another connection before this |
// accept call is completed, an out of state error results |
// need to do this before we make the OTAccept call |
SetAcceptPendFlag(myServerEp->flags); |
// make the accept call and handoff the connection |
err = OTAccept(myServerEp->ep, myAcceptEp->ep, &call); |
#if SHOW_DEBUG_FLOW |
DoValueBreak((long)err, "OTAccept returned #"); |
#endif |
if (err == kOTNoError) |
{ |
SetEPBusyFlag(myAcceptEp->flags); |
// set flag to have server check the serverstatus |
TstCheckOptFlag(gFlags); |
} |
else if (err == kOTLookErr) |
{ |
// an OTLookErr for an OTAccept call means that there is |
// either an T_LISTEN or T_DISCONNECT event |
// check what event is on the pipe |
result = OTLook(myServerEp->ep); |
if (result == T_DISCONNECT) |
{ |
err = OTRcvDisconnect(myServerEp->ep, &discon); |
} |
} |
else |
{ |
#if HANDOFF_EP |
DoValueBreak((long)err, "error occured on OTAccept for handoff #"); |
#else |
DoValueBreak((long)err, "error occured on OTAccept #"); |
#endif |
} |
} |
OTAtomicClearBit(&myServerEp->semaphore, kInListenLoop); // clear the bit that indicates we're in this loop |
} |
/******************************************************************************* |
** ReceiveOnePacket - makes the call to receive just one packet |
** If there is data, then the data is placed into the packetPtr |
** and the incoming byte count is incremented |
** if the kOTNoDataErr occurs, then the HasDataFlag is cleared. |
** |
** |
********************************************************************************/ |
Boolean ReceiveOnePacket(MyEndpointRef *myEp, PacketPtr packetPtr) |
{ |
UInt32 localFlag = 0; |
OTResult result, res; |
Boolean done = false; |
result = OTRcv(myEp->ep, &packetPtr->data, kPAPDataSize, &packetPtr->flags); |
if (result < 0) |
{ |
// requeue the buffer to the freeq |
OTLIFOEnqueue(gFreeQ, &(packetPtr->fLink)); |
done = true; |
if (result == kOTLookErr) |
{ |
res = OTLook(myEp->ep); |
// for an OTRcv call, there are only 2 OTLook responses |
if (res == T_DISCONNECT) |
{ |
res = DoDisconnect(myEp->ep); |
// if we get a disconnect event, then there will be no more |
// data in the pipe. |
#if SHOW_DEBUG_FLOW |
DebugStr("\p processing disconnect in OTRcv;g"); |
#endif |
ClrOrdDisconFlag(myEp->flags); |
ClrInPSQueryFlag(myEp->flags); |
} |
else if (res == T_ORDREL) |
{ |
#if SHOW_DEBUG_FLOW |
DebugStr("\p processing orderly disconnect in OTRcv;g"); |
#endif |
res = DoOrderlyDisconnect(myEp->ep); |
localFlag = 1; |
} |
} |
else if (result == kOTOutStateErr) |
{ |
// client could have issued a disconnect call while this call was about to |
// to be processed |
ClrHasDataFlag(myEp->flags); |
} |
else if (result == kOTNoDataErr) |
{ |
// no more data so clr the flag |
ClrHasDataFlag(myEp->flags); |
} |
else |
{ |
#if SHOW_DEBUG_FLOW |
DebugStr("\pUnknown error occurred in OTRcv"); |
#endif |
} |
} |
else if (result == 0) |
{ |
#if SHOW_DEBUG_FLOW |
DebugStr("\p OTRcv got nothing ;g"); |
#endif |
if (TstInPSQueryFlag(myEp->flags)) // check if the endpoint is already processing a postscript |
{ |
#if SHOW_DEBUG_FLOW |
DebugStr("\p 0 byte packet while processing ps query ;g"); |
#endif |
#if SHOW_DEBUG_FLOW |
if (packetPtr->flags & T_MORE == 0) |
DebugStr("\p T_MORE was not set;g"); |
else |
DebugStr("\p T_MORE was set;g"); |
#endif |
// send an empty response with EOF flasg set since it appears that this is what the Laserwriter |
// client wants to see. |
packetPtr->theEp = myEp; |
SendEmptyPacket(packetPtr); |
// clear bit that indicates we are processing a postscript query |
ClrInPSQueryFlag(myEp->flags); |
#if SHOW_DEBUG_FLOW |
DebugStr("\p Have finished ps query;g"); |
#endif |
} |
// requeue the buffer to the freeq since no data was returned |
OTLIFOEnqueue(gFreeQ, &(packetPtr->fLink)); |
} |
else |
{ |
if (TstdumpPktsFlag(gFlags)) |
{ |
OTLIFOEnqueue(gFreeQ, &(packetPtr->fLink)); |
} |
else |
{ |
// save the endpoint ref associated with this data |
packetPtr->theEp = myEp; |
// save the number of bytes read |
packetPtr->numBytes = result; |
// intialize the lastPos field |
packetPtr->lastPos = 0; |
// get the timestamp for this packet |
OTGetTimeStamp(&packetPtr->timeStamp); |
// Set the timer for this connection |
OTGetTimeStamp(&myEp->timerDog); |
if (!DoProcessPSQuery(packetPtr)) |
{ |
// packet is not a postscript query so queue to the endpoint usedQ |
// queue the buffer to the usedQ associated with the endpoint |
OTLIFOEnqueue(myEp->usedQ, &(packetPtr->fLink)); |
} |
} |
} |
return done; |
} |
/******************************************************************************* |
** DoReceiveData |
** |
********************************************************************************/ |
void DoReceiveData(MyEndpointRef *myEp) |
{ |
PacketPtr packetPtr; |
OTFlags flags = 0; |
UInt32 localFlag = 0; |
Boolean done; |
// check we have already entered DoReceiveData from the main event loop |
// or from the event handler and are |
// trying to do so again a second time |
if (OTAtomicSetBit(&myEp->semaphore, kInRcvDataFlag)) |
return; // if the bit was previous set, then we already have entered, but not exitted |
// this proc from the main event loop |
done = gDone; |
while (done == false) |
{ |
if (gFreeQ->fHead != nil) |
{ |
packetPtr = (PacketPtr)OTLIFODequeue(gFreeQ); |
done = ReceiveOnePacket(myEp, packetPtr); |
} |
else |
{ |
done = true; |
} |
} |
OTAtomicClearBit(&myEp->semaphore, kInRcvDataFlag); |
// clear the bit that indicates we're in this loop |
} |
OSStatus DoDisconnect(MyEndpointRef *myEp) |
{ |
OSStatus err; |
err = OTRcvDisconnect(myEp->ep, NULL); |
if (err != kOTNoError) |
{ |
#if SHOW_DEBUG_FLOW |
DoValueBreak((long)err, "error occured on OTRcvDisconnect #"); |
#endif |
} |
else |
{ |
if (TstAcceptPendFlag(myEp->flags)) |
ClrAcceptPendFlag(myEp->flags); |
} |
return err; |
} |
OSStatus DoOrderlyDisconnect(MyEndpointRef *myEp) |
{ |
OSStatus err; |
err = OTRcvOrderlyDisconnect(myEp->ep); |
if (err < kOTNoError) |
{ |
#if SHOW_DEBUG_FLOW |
DoValueBreak((long)err, "error occured on OTRcvOrderlyDisconnect #"); |
#endif |
} |
else if (err > 0) |
{ |
#if SHOW_DEBUG_FLOW |
DebugStr("\p OTRcvOrderlyDisconnect returned positive result;g"); |
#endif |
SetOrdDisconFlag(myEp->flags); |
} |
else |
{ |
SetOrdDisconFlag(myEp->flags); |
} |
return err; |
} |
/******************************************************************************* |
** PollEventList |
** This routine checks the used queue to see whether the handler has processed |
incoming PAP data from the client. |
** If the used queue is not empty, there are two different options to deal with. |
First we check whether we are in the middle of processing a postscript data |
file. If so, we continue to read the data into a file which we have created. |
If not currently processing postscript data, we then check to see if the incoming |
packet is part of a postscript query in progress or is the beginning of a |
postscript query. If so, we continue to read the query and respond as appropriate. |
If not a query, then this must be the first packet of postscript data being sent. |
********************************************************************************/ |
OSStatus PollQueueList(void) |
{ |
PacketPtr packetPtr, nextPacketPtr; |
MyEndpointRef *theEp; |
OSStatus err = kOTNoError; |
short i = 0; |
Boolean done = false; |
#if SHOW_DEBUG_FLOW |
short num = 0; |
Boolean inLoop = false; |
#endif |
#if HANDOFF_EP |
for (i = 0; (i < kMaxHandoffEPs) && (err == kOTNoError); i++) |
{ |
theEp = &gHandoffEp[i]; |
#else |
theEp = &gEp; |
#endif |
packetPtr = (PacketPtr)OTLIFOStealList(theEp->usedQ); |
while (packetPtr != nil) |
{ |
#if SHOW_DEBUG_FLOW |
inLoop = true; |
#endif |
packetPtr = (PacketPtr)OTReverseList((OTLink*)packetPtr); |
do |
{ |
#if SHOW_DEBUG_FLOW |
num++; |
#endif |
nextPacketPtr = (PacketPtr)packetPtr->fLink.fNext; |
err = ProcessIncomingDataFile(theEp, packetPtr); |
// queue the packetPtr back to the freeQ |
OTLIFOEnqueue(gFreeQ, &(packetPtr->fLink)); |
packetPtr = nextPacketPtr; |
} while((packetPtr != nil) && (err == kOTNoError)); |
packetPtr = (PacketPtr)OTLIFOStealList(theEp->usedQ); |
} |
#if HANDOFF_EP |
} |
#endif |
#if SHOW_DEBUG_FLOW |
if (inLoop) |
{ |
// fprintf(stdout, "numbers packets processed by PollQueueList %d\n", num); |
} |
#endif |
return err; |
} |
OSErr ProcessIncomingDataFile(MyEndpointRef *theEp, PacketPtr packetPtr) |
{ |
OSStatus err = noErr; |
short fRefNum; |
// For each incoming packet, we need to check whether it |
// contains postscript query requests, or if we have not yet |
// received the EOF |
if (!TstdumpPktsFlag(gFlags)) // check if we are dumping the packets |
{ |
if (!TstTempFileFlag(theEp->flags)) // is there a temp file opened for this endpoint |
{ |
// no, so create one |
err = OpenTempFile(&fRefNum); |
if (err == noErr) |
{ |
fprintf(stdout, "processing incoming file\n"); |
theEp->fRefNum = fRefNum; // save the refnum of the temp file; |
SetTempFileFlag(theEp->flags); // indicate that there is a temp file associated |
// with this endpoint. |
// set the time Data In timestamp field |
BlockMove((Ptr)&packetPtr->timeStamp, (Ptr)&theEp->timeDataIn, sizeof(OTTimeStamp)); |
// reset the numBytesIn field to zero |
theEp->numBytesIn = 0; |
} |
} |
} |
if (err == noErr) |
{ |
if (!TstdumpPktsFlag(gFlags)) // check if we are dumping the packets |
{ |
// the temp file has been created or already exists, so read the data and write it |
// to the temp file |
err = WriteDataToTempFile(theEp->fRefNum, packetPtr->data, packetPtr->numBytes); |
} |
if (err != noErr) |
{ |
#if SHOW_DEBUG_FLOW |
DoValueBreak((long)err, "error occured writing data to tempfile #"); |
#endif |
} |
else |
{ |
// set the time data end timestamp field |
BlockMove((Ptr)&packetPtr->timeStamp, (Ptr)&theEp->timeDataEnd, sizeof(OTTimeStamp)); |
// add the number of bytes in the packet to the numBytesIn field |
theEp->numBytesIn += packetPtr->numBytes; |
// check if we've read the last packet for the file |
if ((packetPtr->flags & T_MORE) == 0) |
{ |
if (!TstdumpPktsFlag(gFlags)) |
{ |
fprintf(stdout, "T_MORE flag not set \n"); |
// the proper way to detect the end of a file is to look for |
// the EOF flag at the end of the file. |
// It also appears that looking for the T_MORE flag to be clear |
// is not a reliable way to detect the end of a file. |
// When a status request is sent by the client that is sending |
// data, the T_MORE flag appears to be cleared for the incoming |
// packet. |
} |
} |
} |
} |
return err; |
} |
OSErr CheckFileToClose(void) |
{ |
OSErr err = noErr; |
if (gFRefNumToClose != 0) |
{ |
fprintf(stdout, "\n\nAm about to close file with fRefNum %ld.\n", gFRefNumToClose); |
err = CloseTempFile(gFRefNumToClose); |
if (err != noErr) |
fprintf(stdout, "\n\nError occurred closing file - error %ld.\n", err); |
// reset the tempfile to close so that we don't close this file again |
gFRefNumToClose = 0; |
} |
return err; |
} |
/******************************************************************************* |
** DoServer |
********************************************************************************/ |
void DoServer(void) |
{ |
EventRecord theEvent; |
OTTimeStamp eTime; |
OTResult state; |
UInt32 eTimeMSec, sec, msec, rate; |
MyEndpointRef *theEp; |
OSStatus err1, err = kOTNoError; |
#if HANDOFF_EP |
short i = 0; |
#endif |
while (gDone == false) |
{ |
if (!WaitNextEvent(everyEvent, &theEvent, 15, nil)) |
theEvent.what = nullEvent; |
DoEvent(&theEvent); |
if (TstListenPendFlag(gEp.flags)) // are there incoming connect requests to process |
{ |
fprintf(stdout, "\n processing EnterListenAccept from even tloop"); |
EnterListenAccept(&gEp); |
} |
err = PollQueueList(); |
if (err != kOTNoError) // we're outta here if an error occurred. |
gDone = true; |
#if HANDOFF_EP |
theEp = &gHandoffEp[i]; |
i++; |
if (i >= kMaxHandoffEPs) |
i = 0; |
#else |
theEp = &gEp; |
#endif |
// get the current endpoint state |
state = OTGetEndpointState(theEp->ep); |
switch (state) |
{ |
case T_DATAXFER: |
// check if we haven't completed reading data from OT. |
DoReceiveData(theEp); |
break; |
case T_INREL: // we've processed an incoming Orderly Release request |
// but have not issued our own orderly disconnect |
// note that since we have already processed an incoming orderly disconnect |
// we are in the INREL state. After making the OTSndOrderlyDisconnect |
// call, there will not be a T_DISCONNECTCOMPLETE call, since we complete |
// the orderly release with the above call. We go ahead and reset our flags |
// and issue the OTUnbind call if we are using a handoff endpoint. |
// if we did get the orderly release indication, then make sure we have |
// drained the receive queue - PollQueueList does this |
do |
{ |
fprintf(stdout, "processing some last data\n"); |
PollQueueList(); |
// check if we haven't completed reading data from OT. |
DoReceiveData(theEp); |
}while( (TstHasDataFlag(theEp->flags)) || (theEp->usedQ->fHead != nil)); |
// clear the flag and send a corresponding disconnect |
err = OTSndOrderlyDisconnect(theEp->ep); |
if (err) |
{ |
// the only error that makes sense here is the kOTLookErr |
// which the only event is the T_DISCONNECT event. This should not happen, |
// however, if there are multiple handoffs, this may be an issue. Given the way |
// incoming requests are handled, I don't expect that there would be an |
// unresponded listen request here that would warrant a possible T_DISCONNECT |
// however some implementation will need to consider this possibility. |
#if SHOW_DEBUG_FLOW |
DoValueBreak((long)err, "error occured on OTSndOrderlyDisconnect #"); |
DebugStr("\p quitting program"); |
#endif |
gDone = true; |
} |
ClrOrdDisconFlag(theEp->flags); |
if (TstTempFileFlag(theEp->flags)) |
{ |
// is there an open spool file associated with this endpoint |
gFRefNumToClose = theEp->fRefNum; // set the global to have this file closed |
// note that this method of signalling for a file to |
// get closed is a quick and dirty method and assumes th |
// that only one file at a time is being processed here. |
CheckFileToClose(); |
} |
ClrTempFileFlag(theEp->flags); |
ClrPassconFlag(theEp->flags); |
#if HANDOFF_EP |
// in order to re-use a handoff endpoint, it must be unbound. Unbinding the |
// endpoint clears the connection information that allows to to know which |
// endpoint, incoming PAP data is routed to. |
if (theEp->ep != gEp.ep) |
{ |
OTUnbind(theEp->ep); |
} |
#else |
// indicate that we are no longer busy |
ClrEPBusyFlag(theEp->flags); |
// set flag so that we will check the status |
SetCheckOptFlag(gFlags); |
SetStatusIdleFlag(gFlags); |
#endif |
// calculate the elapsed time |
OTSubtractTimeStamps(&eTime, &theEp->timeDataIn, &theEp->timeDataEnd); |
// convert to millisecs |
eTimeMSec = OTTimeStampInMilliseconds(&eTime); |
sec = eTimeMSec / (UInt32)1000; |
msec = eTimeMSec %(UInt32)1000; |
fprintf(stdout, "Time to transfer file was %ld.%ld seconds.\n", sec, msec); |
fprintf(stdout, "Bytes transferred was - %ld.\n", theEp->numBytesIn); |
if (sec != 0) |
{ |
rate = (theEp->numBytesIn / sec) / 1024; |
fprintf(stdout, "Transfer rate - %ld KBytes/Sec. \n", rate); |
} |
else if (msec != 0) |
{ |
rate = (theEp->numBytesIn *1000 / msec) / 1024; |
fprintf(stdout, "Transfer rate - %ld KBytes/Sec. \n", rate); |
} |
break; |
case T_IDLE: // we're either really idle, or the we've processed a disconnect event |
// if we processed a disconnect event then the busy flag for the ep would still be set |
if (TstEPBusyFlag(theEp->flags)) // we're we previously connected |
{ |
ClrPassconFlag(theEp->flags); // clear the flag so that we can accept a new connection |
ClrOrdDisconFlag(theEp->flags); |
ClrTempFileFlag(theEp->flags); |
ClrPassconFlag(theEp->flags); |
if (TstTempFileFlag(theEp->flags)) |
// is there an open spool file associated with this endpoint |
gFRefNumToClose = theEp->fRefNum; // set the global to have this file closed |
// note that this method of signalling for a file to |
// get closed is a quick and dirty method and assumes th |
// that only one file at a time is being processed here. |
#if HANDOFF_EP |
// in order to re-use a handoff endpoint, it must be unbound. Unbinding the |
// endpoint clears the connection information that allows to to know which |
// endpoint, incoming PAP data is routed to. |
if (theEp->ep != gEp.ep) |
{ |
OTUnbind(theEp->ep); |
} |
#else |
// set flag so that we will check the status |
SetCheckOptFlag(gFlags); |
// indicate that we are no longer busy |
ClrEPBusyFlag(theEp->flags); |
SetStatusIdleFlag(gFlags); |
#endif |
} |
break; |
} |
err1 = CheckFileToClose(); |
if (err1 != noErr) |
fprintf(stdout, "\n\nCheckFileToClose error - %ld.\n", err1); |
if (TstCheckOptFlag(gFlags)) |
{ |
if (EndpointsAllBusy()) |
{ |
if (TstStatusBusyFlag(gFlags) == false) |
{ |
BeginSetServerStatusOption(gEp.ep, kSetBusyStr); |
SetStatusBusyFlag(gFlags); |
} |
} |
else |
{ |
if (TstStatusBusyFlag(gFlags) == true) |
{ |
BeginSetServerStatusOption(gEp.ep, kSetIdleStr); |
ClrStatusBusyFlag(gFlags); |
} |
} |
ClrCheckOptFlag(gFlags); |
} |
} |
} |
/* Do the right thing for an event. Determine what kind of event it is, and |
** call the appropriate routines. */ |
void DoEvent(EventRecord *event) |
{ |
Point pt; |
char key; |
switch(event->what) |
{ |
case nullEvent: |
case mouseDown: |
case activateEvt: |
case updateEvt: |
case kHighLevelEvent: |
break; |
case autoKey: |
case keyDown: |
key = event->message & charCodeMask; |
switch (key) |
{ |
case 'q': |
case 'Q': |
if (event->modifiers & cmdKey) |
gDone = true; |
break; |
} |
break; |
case osEvt: |
switch ((event->message >> 24) & 0xFF) |
{ |
/* Must logical and with 0xFF to get only low byte. */ |
/* High byte of message. */ |
case mouseMovedMessage: |
break; |
case suspendResumeMessage: |
/* Suspend/resume is also an activate/deactivate. */ |
if ((event->message) & resumeFlag) |
ClrInBackGndFlag(gFlags); // process has come to the foreground |
else |
SetInBackGndFlag(gFlags); // process has gone into background |
break; |
} |
break; |
case diskEvt: |
if (HiWord(event->message) != noErr) |
{ |
SetPt(&pt, kDILeft, kDITop); |
DIBadMount(pt, event->message); |
} /* It is not a bad idea to at least call DIBadMount in response to */ |
break; /* a diskEvt, so that the user can format a floppy. */ |
} |
} |
/******************************************************************************* |
** DoBind |
** Implements the option to set the qlen field and to bing with an nbpName |
** by setting the nbpName field with a pointer to an entity name. |
** If nbpName is not nil, you must also set the nbpNameLen to the size of the |
** nbpName field. Note, nbpName is not a pascal nor a C string. |
********************************************************************************/ |
OSStatus DoBind(EndpointRef ep, UInt8 socket, UInt8 type, OTQLen qlen, |
char *nbpName, UInt32 nbpNameLen) |
{ |
TBind req, ret; |
OSStatus err = kOTNoError; |
DDPNBPAddress addr; |
// init the DDPAddress variable |
if (nbpName == nil) // are we registering with no nbp name |
{ |
addr.fAddressType = AF_ATALK_DDP; // set the address type |
req.addr.len = kDDPAddressLength; |
} |
else |
{ |
addr.fAddressType = AF_ATALK_DDPNBP; // registering with an nbp name |
// copy the name to the name buffer |
BlockMove(nbpName, &addr.fNBPNameBuffer, nbpNameLen); |
// set the request length to include the name len |
req.addr.len = kDDPAddressLength + nbpNameLen; |
} |
addr.fNetwork = 0; |
addr.fNodeID = 0; |
addr.fSocket = socket; |
addr.fDDPType = type; |
// set up the request |
req.addr.buf = (UInt8*)&addr; |
req.qlen = qlen; |
// set up the response |
ret.addr.buf = (UInt8*)&addr; |
ret.addr.maxlen = sizeof(addr); |
fprintf(stderr, "Doing Bind\n"); |
// |
// Try to bind |
// |
err = OTBind(ep, &req, &ret); |
if ( err != kOTNoError ) |
{ |
fprintf(stderr, "PAPSample: DoBind returns error %ld\n", err); |
return err; |
} |
fprintf(stderr, "Bound address = "); |
ShowDDPAddress((DDPAddress*)&addr); |
fprintf(stderr, "\n"); |
fprintf(stderr, "Bound queue len is %ld\n", ret.qlen); |
fprintf(stderr, "After Bind, "); |
ShowEndpointState(ep, ""); |
return err; |
} |
/******************************************************************************* |
** ActivatePAPEndpoint |
** |
********************************************************************************/ |
OSStatus ActivatePAPEndpoint(MyEndpointRef *myEp) |
{ |
OSStatus err = kOTNoError; |
// |
// Create a PAP endpoint |
// Open listener, using the tilisten module to make |
// listen/accept/disconnect processing much simpler. |
// |
myEp->ep = OTOpenEndpoint(OTCreateConfiguration("tilisten, pap"), 0, NULL, &err); |
if ( myEp->ep == NULL || err != kOTNoError ) |
{ |
myEp->ep = NULL; |
if (err == kOTNoError) |
err = memFullErr; |
fprintf(stderr,"ERROR: OpenEndpoint(\"pap\") failed with %ld\n", err); |
} |
else |
{ |
SetEPActiveFlag(myEp->flags); |
} |
/*------------------------------------------------------------------------- |
Enable self send |
have to do this before we set the endpoint into async mode. |
------------------------------------------------------------------------- */ |
if (err == kOTNoError) |
{ |
err = DoNegotiateSelfSendOption(myEp->ep, 1); |
if (err <= kOTNoError) |
{ |
fprintf(stderr, "error negotiating selfsend on main endpoint\n"); |
err = kOTNoError; // don't let this stop the app. |
} |
else |
{ |
fprintf(stderr, "selfsend enabled\n"); |
if (err == 0) |
fprintf(stderr, "selfsend was previously off\n"); |
else |
fprintf(stderr, "selfsend was previously on\n"); |
} |
} |
#if HANDOFF_EP |
#else |
// don't need EOM option |
if (err == kOTNoError) |
{ |
// turn on the EOM option |
err = DoNegotiateEOMOption(myEp->ep, true); |
if (err != kOTNoError) |
{ |
ClrEOMOnFlag(myEp->flags); |
fprintf(stderr, "\n\nError setting EOM option!\n"); |
} |
else |
{ |
SetEOMOnFlag(myEp->flags); |
} |
} |
#endif |
if (err == kOTNoError) |
{ |
// |
// Install notifier we're going to use for testing - we do this before |
// binding - at this point, we're still synchronous |
// |
err = OTInstallNotifier(myEp->ep, EventHandler, (void*)myEp); |
if ( err != kOTNoError ) |
{ |
fprintf(stderr, "ERROR: InstallNotifier() failed with %ld\n for server endpoint", err); |
} |
} |
if (err == kOTNoError) |
{ |
/*------------------------------------------------------------------------- |
Prepare to Bind the endpoint by getting the NBP Name |
------------------------------------------------------------------------- */ |
/*------------------------------------------------------------------------- |
Bind the endpoint endpoint. |
------------------------------------------------------------------------- */ |
err = DoBind(myEp->ep, kDynamicSocket, kATPType, 2, (char*)gServerNBPNameStr, clen((char*)gServerNBPNameStr)); |
if ( err != kOTNoError ) |
{ |
fprintf(stderr, "PAPSample: bind of server endpoint failed.\n"); |
} |
else |
{ |
SetEPBoundFlag(myEp->flags); |
OTSetAsynchronous(myEp->ep); |
} |
} |
/*------------------------------------------------------------------------- |
Set the server status string |
------------------------------------------------------------------------- */ |
if (err == kOTNoError) |
BeginSetServerStatusOption(myEp->ep, kSetIdleStr); |
return err; |
} |
/******************************************************************************* |
** InitMyEndpointRef function - zero out the MyEndpointRef structure |
** Course we could use the memset function here. |
********************************************************************************/ |
OSStatus InitMyEndpointRef(MyEndpointRef *myEp) |
{ |
myEp->ep = nil; |
myEp->flags = 0; |
myEp->usedQ = OTAllocMem(sizeof(OTLIFO)); |
if (myEp->usedQ == nil) |
return memFullErr; |
myEp->usedQ->fHead = nil; // no packets with this endpoint. |
return kOTNoError; |
} |
OSStatus InitPAPBuffers(void) |
{ |
PacketPtr packetPtr; |
OSStatus err = kOTNoError; |
short i; |
// allocate memory for the freeQ |
gFreeQ = OTAllocMem(sizeof(OTLIFO)); |
if (gFreeQ == nil) |
return memFullErr; |
/* set up the free queue */ |
gFreeQ->fHead = nil; |
// allocate a temp buffer to be used when packet data overlaps. |
gTempPackPtr = OTAllocMem(sizeof(PacketBuffer) + kPAPDataSize); |
if (gTempPackPtr == nil) |
err = memFullErr; |
/* enqueue the packet buffer records to the free queue */ |
for (i=0; (i<kNumBuffers) && (err == kOTNoError); i++) |
{ |
// before calling OTAllocMem, we must have already have called |
// InitOpenTransport |
packetPtr = OTAllocMem(sizeof(PacketBuffer)); |
if (packetPtr != nil) |
{ |
packetPtr->flags = 0; |
packetPtr->fLink.fNext = nil; |
OTLIFOEnqueue(gFreeQ, &(packetPtr->fLink)); // first field is fLink field |
} |
else |
err = memFullErr; |
} |
if (err == kOTNoError) |
{ |
/* show that the buffers have been init'd */ |
SetBufsInitdFlag(gFlags); |
/* note that we don't need to worry about holding memory in the |
presence of VM. The notifier routine will be called at |
deferred task time when it is safe to page memory |
*/ |
} |
else |
{ |
fprintf(stderr, "\n\nError allocating receive buffers!\n"); |
fprintf(stderr, "\nBye Bye.\n"); |
} |
return(err); |
} |
/******************************************************************************* |
** Initialize OpenTransport and call DoTest function |
********************************************************************************/ |
OSStatus InitPAPServerStuff(void) |
{ |
OSStatus err = kOTNoError; |
UInt32 growAmt, selection; |
UInt32 i; |
char mystr[255]; |
// register ourselves with OT |
OTRegisterAsClient((OTClientName) "PAPServerSample", nil); |
// ask whether to alter current options |
fprintf(stdout, "\nDo you want to change the current options?"); |
fprintf(stdout, "\n Options to change 1. SetMemoryLimits"); |
fprintf(stdout, "\n 2. Dump packets"); |
selection = GetYesNoOption(); |
if (selection == kQuitTest) |
err = -1; |
else if (selection == kAcceptOption) |
{ |
// ask whether to use the set memory limits option or not |
fprintf(stdout, "\nDo you want to set memory limits?"); |
selection = GetYesNoOption(); |
if (selection == kQuitTest) |
err = -1; |
else if (selection == kAcceptOption) |
{ |
fprintf(stderr, "How much memory to grow heap ?\n"); |
if (gets(mystr) != 0) |
{ |
StringToNum(c2pstr(mystr), (long*)&growAmt); |
OTSetMemoryLimits(growAmt, 0); |
} |
} |
if (err == kOTNoError) |
{ |
// ask whether to use the set memory limits option or not |
fprintf(stdout, "\nDo you want to just dump packets?"); |
selection = GetYesNoOption(); |
if (selection == kQuitTest) |
err = -1; |
else if (selection == kAcceptOption) |
{ |
SetdumpPktsFlag(gFlags); |
} |
} |
} |
if (err == kOTNoError) |
{ |
// load in the various strings |
GetIndString((unsigned char*)&gIdleStr, kServerStrID, kIdleStrID); |
GetIndString((unsigned char*)&gBusyStr, kServerStrID, kBusyStrID); |
GetIndString((unsigned char*)&gServerNBPNameStr, kServerStrID, kServerNBPStrID); |
// covert each string to c |
p2c(gIdleStr); |
p2c(gBusyStr); |
p2c(gServerNBPNameStr); |
// initialize the endpoint ref |
err = InitMyEndpointRef(&gEp); |
} |
if (err == kOTNoError) |
{ |
#if HANDOFF_EP |
for (i = 0; (i < kMaxHandoffEPs) && (err == kOTNoError) ; i++) |
err = InitMyEndpointRef(&gHandoffEp[i]); |
#endif |
} |
if (err == kOTNoError) |
{ |
// |
// Initialize the buffers |
// |
err = InitPAPBuffers(); |
} |
if (err == kOTNoError) |
{ |
// |
// Init the server endpoint |
// |
err = ActivatePAPEndpoint(&gEp); |
} |
#if HANDOFF_EP |
if (err == kOTNoError) |
{ |
// |
// Init the handoff endpoint |
// |
for (i = 0; (i < kMaxHandoffEPs) && (err == kOTNoError); i++) |
gHandoffEp[i].ep = OTOpenEndpoint(OTCreateConfiguration(kPAPName), (OTOpenFlags)NULL, NULL, &err); |
if (err != kOTNoError) |
{ |
fprintf(stderr, "\n\nError opening the handoff endpoint!\n"); |
fprintf(stderr, "\nBye Bye.\n"); |
} |
else |
{ |
for (i = 0; (i < kMaxHandoffEPs) && (err == kOTNoError); i++) |
{ |
SetEPActiveFlag(gHandoffEp[i].flags); |
// turn on the EOM option |
err = DoNegotiateEOMOption(gHandoffEp[i].ep, true); |
if (err != kOTNoError) |
{ |
ClrEOMOnFlag(gHandoffEp[i].flags); |
fprintf(stderr, "\n\nError setting EOM option!\n"); |
} |
else |
{ |
SetEOMOnFlag(gHandoffEp[i].flags); |
} |
} |
} |
} |
if (err == kOTNoError) |
{ |
for (i = 0; (i < kMaxHandoffEPs) && (err == kOTNoError); i++) |
{ |
err = OTInstallNotifier(gHandoffEp[i].ep, EventHandler, (void*)&gHandoffEp[i]); |
if ( err != kOTNoError ) |
{ |
fprintf(stderr, "ERROR: InstallNotifier() failed with %ld\n for handoff endpoint", err); |
} |
else |
OTSetAsynchronous(gHandoffEp[i].ep); |
} |
} |
#endif // HANDOFF_EP |
return err; |
} |
/******************************************************************************* |
** ReleasePAPMemory |
********************************************************************************/ |
void ReleasePAPMemory(void) |
{ |
PacketPtr packetPtr; |
OTLIFO *otLIFOPtr; |
short i; |
if (gTempPackPtr) |
OTFreeMem(gTempPackPtr); |
// first release the freeQ memory |
otLIFOPtr = gFreeQ; |
packetPtr = (PacketPtr)OTLIFODequeue(otLIFOPtr); |
while (packetPtr != nil) // while the queue is not empty |
{ |
// free the packetbuffer |
OTFreeMem(packetPtr); |
packetPtr = (PacketPtr)OTLIFODequeue(otLIFOPtr); |
} |
#if HANDOFF_EP |
for (i = 0; i < kMaxHandoffEPs; i++) // |
{ |
otLIFOPtr = gHandoffEp[i].usedQ; |
#else |
otLIFOPtr = gEp.usedQ; |
#endif |
if (otLIFOPtr->fHead) |
fprintf(stderr, "Am releasing buffer from the used queue.\n\n"); |
packetPtr = (PacketPtr)OTLIFODequeue(otLIFOPtr); |
while (packetPtr != nil) // while the queue is not empty |
{ |
// free the packetbuffer |
OTFreeMem(packetPtr); |
packetPtr = (PacketPtr)OTLIFODequeue(otLIFOPtr); |
} |
#if HANDOFF_EP |
} |
#endif |
OTFreeMem(gFreeQ); |
} |
/******************************************************************************* |
** Close endpoints, and OpenTransport |
********************************************************************************/ |
void ClosePAPServerStuff(void) |
{ |
short i; |
if (gEp.ep) |
OTSetSynchronous(gEp.ep); |
// note that we don't check whether the endpoint is still connected. Since |
// we are leving anyway, let the UnBind call clear things up. If we did do a |
// disconnect here, we would meed to also implement a timer mechanism. so that |
// we didn't get hung awaiting a DisconnectComplete event that might not come. |
#if HANDOFF_EP |
for (i = 0; i < kMaxHandoffEPs; i++) |
{ |
if (gHandoffEp[i].ep) |
OTSetSynchronous(gHandoffEp[i].ep); |
if (TstEPBoundFlag(gHandoffEp[i].flags)) // is the handoff endpoint bound |
OTUnbind(gHandoffEp[i].ep); // unbind the endpoint |
if (gHandoffEp[i].usedQ != nil) // free allocated memory with usedQ. |
OTFreeMem(gHandoffEp[i].usedQ); |
if (TstEPActiveFlag(gHandoffEp[i].flags)) // is the handoff endpoint open |
OTCloseProvider(gHandoffEp[i].ep); // close the endpoint |
} |
#endif |
if (TstEPBoundFlag(gEp.flags)) // is the server endpoint bound |
OTUnbind(gEp.ep); // unbind the endpoint |
if (gEp.usedQ != nil) // free allocated memory with usedQ. |
OTFreeMem(gEp.usedQ); |
if (TstEPActiveFlag(gEp.flags)) // is the server endpoint open |
OTCloseProvider(gEp.ep); // close the endpoint |
// ReleasePAPMemory(); // release memory before OT goes away |
// not really necessary since we are just going to |
// exit after this. |
if (gFreeQ != nil) |
OTFreeMem(gFreeQ); |
if (TstOTActiveFlag(gFlags)) // is OpenTransport active |
CloseOpenTransport(); // shutdown OT services |
} |
// |
// NetInit: |
// |
// This routine does various networking related startup tasks: |
// |
// (1) it does InitOpenTransport |
// (2) it records the OT version for us. |
// |
// result - true - OpenTransport init'd and the correct version is present |
// false - problem opening OT or OT version not correct |
// |
static Boolean NetInit() |
{ |
OSStatus err; |
err = Gestalt(gOTVersionSelector, (long*) &gOTVersion); |
if (err || (gOTVersion < kOTVersion111)) |
{ |
fprintf(stderr, "Please install Open Transport 1.1.1 or later"); |
return false; |
} |
err = InitOpenTransport(); |
if (err) |
{ |
fprintf(stderr, "NetInit: InitOpenTransport error %d", err); |
return false; |
} |
return true; |
} |
/******************************************************************************* |
** main function |
********************************************************************************/ |
main(void) |
{ |
OSStatus osstatus; |
InitGraf(&qd.thePort); // initialize quickdraw so we can use regions |
fprintf(stderr, "PAPServerSample showing implementation of PAP server.\n\n"); |
// init the flags variable |
gFlags = 0; |
#if DO_DEBUG_LOG |
DebugStr("\p doing log;dx;log papserver;g"); |
#endif |
if (NetInit() == false) |
return 0; |
// |
// Initialize the OT and the global Endpoints |
// |
osstatus = InitPAPServerStuff(); |
if (osstatus == kOTNoError) |
{ |
// |
// Run the PAPServer code |
// |
DoServer(); |
// |
} |
// |
// Close things down |
// |
ClosePAPServerStuff(); |
CheckFileToClose(); |
fprintf(stderr, "\n\nDone\n"); |
return 0; |
} |
void DoValueBreak(long value, const char* message) |
{ |
static short sDoErrorBreak = 0; |
{ |
Str255 s, |
n = "\p"; |
s[0] = strlen(message); |
BlockMoveData(message,&s[1],s[0]); |
if (value < 0) |
{ |
s[0] += 1; |
s[s[0]] = '-'; |
value = -value; |
} |
while (value) |
{ |
if (n[0]) |
BlockMoveData(&n[1],&n[2],n[0]); |
n[0]++; |
n[1] = 48 + (value % 10); |
value /= 10; |
} |
BlockMoveData(&n[1],&s[s[0]+1],n[0]); |
s[0] += n[0]; |
sDoErrorBreak++; |
{ |
short cnt = sDoErrorBreak; |
s[0]++; |
s[s[0]] = ','; |
s[0]++; |
s[s[0]] = ' '; |
n[0] = 0; |
while (cnt) |
{ |
if (n[0]) |
BlockMoveData(&n[1],&n[2],n[0]); |
n[0]++; |
n[1] = 48 + (cnt % 10); |
cnt /= 10; |
} |
BlockMoveData(&n[1],&s[s[0]+1],n[0]); |
s[0] += n[0]; |
} |
DebugStr(s); |
} |
} |
OSStatus BeginSetServerStatusOption(EndpointRef ep, UInt16 whichStr) |
{ |
OSStatus err; |
unsigned char *in = "\p._in"; |
if (whichStr == kSetIdleStr) |
{ |
err = DoSetServerStatusOption(ep, (char*)&gIdleStr); |
} |
else |
{ |
err = DoSetServerStatusOption(ep, (char*)&gBusyStr); |
} |
if (err != kOTNoError) |
{ |
fprintf(stderr, "ServerStatus option call failed\n"); |
fprintf(stderr, "however, this will not keep the server from running\n"); |
err = kOTNoError; |
} |
return err; |
} |
UInt32 GetYesNoOption(void) |
{ |
UInt32 result; |
char selection[32]; |
Boolean done; |
fprintf(stdout, "\n Enter Y - To accept option"); |
fprintf(stdout, "\n Enter N - To decline option"); |
fprintf(stdout, "\n Enter Q - To quit"); |
fprintf(stdout, "\nYour selection -> "); |
fflush(stdout); |
done = false; |
do |
{ |
if (gets(selection) != 0) |
{ |
switch (selection[0]) |
{ |
case 'y': |
case 'Y': |
result = kAcceptOption; |
done = true; |
break; |
case 'n': |
case 'N': |
result = kDeclineOption; |
done = true; |
break; |
case 'q': |
case 'Q': |
result = kQuitTest; |
done = true; |
break; |
default: |
fprintf(stdout, "\nInvalid entry - %c, try again -> ", selection); |
fflush(stdout); |
break; |
} |
} |
} while (!done); |
fflush (stdout); |
return result; |
} |
#if HANDOFF_EP |
/* |
Iterate through the handoff endpoints and find one that doesn't have the busy flag |
set - return the pointer else return null |
*/ |
MyEndpointRef* FindFreeHandoffEp(void) |
{ |
short i; |
for (i = 0; i < kMaxHandoffEPs; i++) |
{ |
if (TstEPBusyFlag(gHandoffEp[i].flags) == false) |
return &gHandoffEp[i]; |
} |
return nil; |
} |
#endif |
Boolean EndpointsAllBusy(void) |
{ |
Boolean result = true; |
#if HANDOFF_EP |
short i; |
for (i = 0; (i < kMaxHandoffEPs) && (result == true); i++) |
{ |
if (TstEPBusyFlag(gHandoffEp[i].flags) == false) |
result = false; // any endpoint not busy means that not all eps are busy |
} |
#else |
result = TstEPBusyFlag(gEp.flags); |
#endif |
return result; |
} |
Copyright © 2003 Apple Computer, Inc. All Rights Reserved. Terms of Use | Privacy Policy | Updated: 2003-07-22