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 |
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. |
Change history |
RRK 5/28/99 modified code to look for eof and respond to client with an empty packet with |
the eof bit set. |
RRK 3/9/99 add new code to protect against multiple unbind calls on an endpoint |
* Renamed DoDisconnect to DoRcvDisconnect and implemented |
* Implemented DoSndDisconnect |
* Check for errors in hanlder |
* implemented OTLookHandler function |
* implemented the ActivateHandoffEndpoint function where the handoff endpoint is initialized, and bound |
* fixed bug to OTRcvDisconnect where we were passing unit'd &discon parameters. |
* set flags before making OT calls instead of after the call success to fix timing issues. |
by: Rich Kubota |
Developer Technical Support |
*/ |
#define qDebug 1 |
#define HANDOFF_EP 1 // set to to to indicate use of handoff endpoint |
#include <Types.h> |
#include <Errors.h> |
#include <String.h> |
#include <ToolUtils.h> |
#include "StringUtils.h" |
#include <AppleTalk.h> |
#include <DiskInit.h> |
#include <TextUtils.h> |
#include <Gestalt.h> |
#include <Files.h> |
#include <NumberFormatting.h> |
#include "OpenTransport.h" |
#include "OpenTptAppleTalk.h" |
#include "ATalkSampleUtils.h" |
#include "PAPServerUtilities.h" |
#include "PAPServerSample.h" |
#include "PAPPostScriptStuff.h" |
#include "OTDebug.h" |
#include "StrOpts.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*); |
OSStatus HandleOTLookErr(MyEndpointRef *myEp); |
static void EnterListenAccept(MyEndpointRef *myServerEp); |
void DoListenAccept(MyEndpointRef *myServerEp); |
Boolean ReceiveOnePacket(MyEndpointRef *myEp, PacketPtr packetPtr); |
void DoReceiveData(MyEndpointRef *myEp); |
OSStatus DoSndDisconnect(MyEndpointRef *myEp); |
OSStatus DoDisconnect(MyEndpointRef *myEp); |
OSStatus DoRcvDisconnect(MyEndpointRef *myEp); |
OSStatus DoRcvOrderlyDisconnect(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 DoOTUnbind(MyEndpointRef *theEp); |
OSStatus DoBind(EndpointRef ep, UInt8 socket, UInt8 type, OTQLen qlen, |
char *nbpName, UInt32 nbpNameLen); |
OSStatus StartAbortiveDisconnect(MyEndpointRef *myEp); |
OSStatus ActivatePAPEndpoint(MyEndpointRef *myEp); |
OSStatus ActivateHandoffEndpoint(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 |
static pascal void OTDebugStr(const char *msg) |
{ |
UInt8 pMsg[256]; |
pMsg[0] = OTStrLength(msg); |
OTMemcpy(&pMsg[1], msg, pMsg[0]); |
DebugStr(pMsg); |
} |
/******************************************************************************* |
** 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 (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 |
DoRcvDisconnect(contextPtr); |
break; |
case T_ERROR: /* obsolete/unused in library */ |
case T_UDERR: /* A Unit Data Error has occurred */ |
OTDebugStr((const char *)"T_ERROR called;g"); |
break; |
case T_ORDREL: /* An orderly release is available */ |
DoRcvOrderlyDisconnect(contextPtr); |
break; |
case T_GODATA: /* Flow control lifted on standard data */ |
// for this sample, I do not expect this event to occur. |
OTDebugStr((const char *)"T_GODATA called;g"); |
break; |
case T_GOEXDATA: /* Flow control lifted on expedited data*/ |
// for this sample, I do not expect this event to occur. |
OTDebugStr((const char *)"T_GOEXDATA called;g"); |
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 indicate that the endpoint is bound |
// RRK addition 3/10/99 |
SetEPBoundFlag(((MyEndpointRef*)contextPtr)->flags); |
// set flag to tell system to set server status flag appropriately |
// for multiple handoff endpoint servers, this would need to be |
// handled differently. |
SetCheckOptFlag(gFlags); |
SetFirstPktFlag(((MyEndpointRef*)contextPtr)->flags); |
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 */ |
OTDebugStr((const char *)"T_RESET called;g"); |
break; |
case T_ACCEPTCOMPLETE: /* Accept call is complete */ |
OTDebugStr((const char *)"T_ACCEPTCOMPLETE called;g"); |
ClrAcceptPendFlag(((MyEndpointRef*)contextPtr)->flags); |
SetFirstPktFlag(((MyEndpointRef*)contextPtr)->flags); |
// RRK 3/9/99 |
// check for an error result |
if (result != kOTNoError) |
{ |
OTDebugStr("Error occured with T_ACCEPTCOMPLETE event"); |
if (result == kOTLookErr ) |
{ |
HandleOTLookErr(contextPtr); |
} |
} |
break; |
case T_DISCONNECTCOMPLETE: /* Disconnect call is complete */ |
OTDebugStr("T_DISCONNECTCOMPLETE event occurred;g"); |
ClrDisconPendFlag(((MyEndpointRef*)contextPtr)->flags); // is the server endpoint bound |
ClrEPBusyFlag(((MyEndpointRef*)contextPtr)->flags); |
ClrAcceptPendFlag(((MyEndpointRef*)contextPtr)->flags); |
ClrListenPendFlag(((MyEndpointRef*)contextPtr)->flags); |
break; |
case T_UNBINDCOMPLETE: // RRK 3/9/99 add code to protect against multiple unbind calls |
// OTDebugStr((const char *)"T_UNBINDCOMPLETE called;g"); |
DoValueBreak((long)contextPtr, "T_UNBINDCOMPLETE called #"); |
SetCheckOptFlag(gFlags); // set flag to force the server to determine |
// whether to change the server message |
ClrEPBusyFlag(((MyEndpointRef*)contextPtr)->flags); // no longer waiting for T_UNBINDCOMPLETE message |
ClrUnbindPendFlag(((MyEndpointRef*)contextPtr)->flags); |
ClrEPBoundFlag(((MyEndpointRef*)contextPtr)->flags); // is the server endpoint bound |
SetStatusIdleFlag(gFlags); |
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: // the flush is complete |
DoSndDisconnect (contextPtr); /* safe to disconnect */ |
break; |
case T_OPTMGMTCOMPLETE: |
if (result != kOTNoError) |
{ |
OTDebugStr("Error occured with T_OPTMGMTCOMPLETE event"); |
} |
gOptionCompleted = 1; |
break; |
default: |
break; |
} |
} |
/*********************************************************** |
RRK 3/9/99 addition |
HandleOTLookErr implemented to handle OTLookErr in a |
central place - only call this function in response |
to a OTLookErr for a function call |
************************************************************/ |
OSStatus HandleOTLookErr(MyEndpointRef *myEp) |
{ |
OSStatus err = kOTNoError; |
OTResult result; |
result = OTLook(myEp->ep); |
switch (result) |
{ |
case T_DISCONNECT: |
err = DoRcvDisconnect(myEp); |
if (err == kOTNoError) |
{ |
ClrInPSQueryFlag(myEp->flags); |
} |
else |
{ |
DoValueBreak(err, "Error calling DoRcvDisconnect from HandleOTLookErr #"); |
SetForceCloseFlag(myEp->flags); |
} |
break; |
case T_ORDREL: |
err = DoRcvOrderlyDisconnect(myEp); |
// if successful, then we enter T_INREL state |
if (err != kOTNoError) |
{ |
DoValueBreak(err, "Error calling DoRcvOrderlyDisconnect from HandleOTLookErr #"); |
SetForceCloseFlag(myEp->flags); |
} |
break; |
default: |
// The use of this function in this instance is to handle |
// T_DISCONNECT events that can happen. This call will |
// need to be customized for other use, like handling |
// T_DATA events which is not implemented in this sample. |
// Pass a debugStr call to show what event was not handled here |
DoValueBreak((long)result, "HandleOTLookErr passed unprocessed event - #"); |
break; |
} |
return err; |
} |
// |
// 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; |
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) |
{ |
OTDebugStr((const char*)"kOTNoDataErr returned by OTListen"); |
// 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 |
HandleOTLookErr(myServerEp); |
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) |
{ |
OTDebugStr((const char *)"rejecting T_LISTEN - no free handoff endpoints"); |
// 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 (err == kOTNoError) |
{ |
SetEPBusyFlag(myAcceptEp->flags); |
// set flag to have server check the serverstatus |
TstCheckOptFlag(gFlags); |
} |
else if (err == kOTLookErr) |
{ |
HandleOTLookErr(myServerEp); |
} |
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) |
{ |
OTResult result; |
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) |
{ |
HandleOTLookErr(myEp->ep); |
} |
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 |
{ |
DoValueBreak(result, "Unknown error occurred in OTRcv #"); |
} |
} |
else if (result == 0) |
{ |
OTDebugStr((const char *)"OTRcv got nothing ;g"); |
if (TstInPSQueryFlag(myEp->flags)) // check if the endpoint is already processing a postscript |
{ |
OTDebugStr((const char *)"0 byte packet while processing ps query ;g"); |
if (packetPtr->flags & T_MORE == 0) |
OTDebugStr((const char *)"T_MORE was not set;g"); |
else |
OTDebugStr((const char *)"T_MORE was set;g"); |
// send an empty response with EOF flags set since it appears that this is what the Laserwriter |
// client wants to see. At this point, we have already processed any preceding |
// ps queries. It is important to not respond with an empty packet and EOF bit set if |
// there is still a ps query response outstanding. |
packetPtr->theEp = myEp; |
SendEmptyPacket(packetPtr); |
// clear bit that indicates we are processing a postscript query |
ClrInPSQueryFlag(myEp->flags); |
OTDebugStr((const char *)"Have finished ps query;g"); |
// queue the buffer to the freeQ associated with the endpoint |
OTLIFOEnqueue(gFreeQ, &(packetPtr->fLink)); |
} |
else |
{ |
// pass this packet on to the event loop so that we can send a null response |
// save the endpoint ref associated with this data. |
// While we could send the empty packet here, this example waits until the |
// data is processed in the main event loop to send the null packet. |
packetPtr->theEp = myEp; |
// save the amount of data 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); |
// queue the buffer to the usedQ associated with the endpoint |
OTLIFOEnqueue(myEp->usedQ, &(packetPtr->fLink)); |
} |
} |
else |
{ |
// check if this is the first packet for this endpoint and set the time in timestamp |
if (TstFirstPktFlag(myEp->flags)) |
{ |
ClrFirstPktFlag(myEp->flags); |
// set the time Data In timestamp field |
OTGetTimeStamp(&myEp->timeDataIn); |
// zero out the numByteIn field |
myEp->numBytesIn = 0; |
} |
// save the number of bytes read |
myEp->numBytesIn += result; |
// set the time data end timestamp field |
OTGetTimeStamp(&myEp->timeDataEnd); |
if (TstdumpAllPktsFlag(gFlags)) |
{ |
OTLIFOEnqueue(gFreeQ, &(packetPtr->fLink)); |
} |
else |
{ |
// save the endpoint ref associated with this data |
packetPtr->theEp = myEp; |
// save the amount of data 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; |
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 DoSndDisconnect(MyEndpointRef *myEp) |
{ |
OSStatus err; |
err = OTSndDisconnect(myEp->ep, NULL); |
if (err != kOTNoError) |
{ |
DoValueBreak((long)err, "error occured on OTSndDisconnect #"); |
} |
else |
{ |
ClrAcceptPendFlag(myEp->flags); |
} |
return err; |
} |
OSStatus DoRcvDisconnect(MyEndpointRef *myEp) |
{ |
OSStatus err = kOTNoError; |
if (TstDisconPendFlag(myEp->flags)) |
{ |
// set discon flag before making the call and clear it if the call |
// fails so that we don't have a race condition with the handler routine |
SetDisconPendFlag(myEp->flags); |
err = OTRcvDisconnect(myEp->ep, NULL); |
if (err != kOTNoError) |
{ |
DoValueBreak((long)err, "error occured on OTRcvDisconnect #"); |
ClrDisconPendFlag(myEp->flags); |
} |
} |
return err; |
} |
OSStatus DoRcvOrderlyDisconnect(MyEndpointRef *myEp) |
{ |
OSStatus err; |
err = OTRcvOrderlyDisconnect(myEp->ep); |
if (err < kOTNoError) |
{ |
DoValueBreak((long)err, "error occured on OTRcvOrderlyDisconnect #"); |
} |
else if (err > 0) |
{ |
OTDebugStr((const char *)"OTRcvOrderlyDisconnect returned positive result;g"); |
} |
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 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) |
{ |
packetPtr = (PacketPtr)OTReverseList((OTLink*)packetPtr); |
do |
{ |
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 |
return err; |
} |
OSErr ProcessIncomingDataFile(MyEndpointRef *theEp, PacketPtr packetPtr) |
{ |
OSStatus err = noErr; |
short fRefNum; |
Boolean eofFlag = false; |
// For each incoming packet, we need to check whether the EOF |
// bit was set by looking at whether the T_MORE flag was clear |
if ((packetPtr->flags & T_MORE) == 0) |
{ |
fprintf(stdout, "\nT_MORE flag not set \n"); |
if (TstEOMOnFlag(theEp->flags)) |
{ |
// send empty packet response to the sender to acknowledge that we |
// have received the last packet |
// this is |
fprintf(stdout, "Sending empty response packet \n"); |
SendEmptyPacket(packetPtr); |
eofFlag = true; |
} |
else |
{ |
fprintf(stdout, "EOM option not enabled - cannot tell if EOF message sent \n"); |
} |
} |
if (!TstdumpAllPktsFlag(gFlags) && !TstDumpPktsFlag(theEp->flags)) // 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, "\nprocessing 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. |
} |
else |
{ |
fprintf(stdout, "Error occurred opening a temp file %d\n", err); |
fprintf(stdout, "The packets will be dumped%d\n", err); |
} |
} |
if (err == noErr) |
{ |
// 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) |
{ |
fprintf(stdout, "Error occurred writing to temp file %d\n", err); |
fprintf(stdout, "All remaining packets will be dumped for this endpoint\n"); |
SetDumpPktsFlag(theEp->flags); |
// set err to noErr otherwise the error would force the close of the |
// program |
err = noErr; |
} |
// check if the eof bit was set, and close the file if so |
if (eofFlag == true) |
{ |
err = CloseTempFile(theEp->fRefNum); |
if (err != noErr) |
fprintf(stdout, "\n\nError occurred closing file - error %ld.\n", err); |
else |
ClrTempFileFlag(theEp->flags); |
} |
} |
} |
return err; |
} |
OSErr CheckFileToClose(void) |
{ |
OSErr err = noErr; |
if (gFRefNumToClose != 0) |
{ |
fprintf(stdout, "Am 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 |
if (TstForceCloseFlag(theEp->flags)) |
{ |
state = kForceClose; |
} |
else |
{ |
// 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 |
// A PAPServer does not normally issue an OrderlyDisconnect request. Typically, |
// the client issues the orderly release, at which point the server must accept the |
// T_ORDREL event. At this point the server is |
do |
{ |
fprintf(stdout, "processing some last data\n"); |
PollQueueList(); |
}while( (TstHasDataFlag(theEp->flags)) || (theEp->usedQ->fHead != nil)); |
fprintf(stdout, "Calling OTSndOrderlyDisconnect\n"); |
// 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. |
DoValueBreak((long)err, "error occured on OTSndOrderlyDisconnect #"); |
gDone = true; |
} |
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) |
{ |
if (TstEPBoundFlag(theEp->flags)) |
{ |
OTDebugStr("Entered DoOTUnbind for T_INREL"); |
DoOTUnbind(theEp); // RRK 3/9/99 change to protect against multiple unbinds |
} |
} |
#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 |
{ |
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 |
fprintf(stdout, "i"); |
fflush(stdout); |
if (TstEPBusyFlag(theEp->flags)) // we're we previously connected |
{ |
ClrPassconFlag(theEp->flags); // clear the flag so that we can accept a new connection |
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) |
{ |
DoOTUnbind(theEp->ep); // RRK 3/9/99 change to protect against multiple unbinds |
} |
#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; |
case kForceClose: // our endpoint is really hosed so close it an reopen it |
ClrForceCloseFlag(theEp->flags); |
OTCloseProvider(theEp->ep); |
#if HANDOFF_EP |
err = ActivateHandoffEndpoint(theEp); |
#else |
err = ActivatePAPEndpoint(theEp); |
#endif |
if (err != kOTNoError) |
{ |
fprintf(stdout, "\n Error activating endpoint - %ld.\n", err); |
fprintf(stdout, "\n Quitting.\n"); |
gDone = true; |
} |
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. */ |
} |
} |
/******************************************************************************* |
DoOTUnbind |
Call to make the OTUnbind request and check for an error result. |
Once this call is made, we don't want to protect against the OTUnbind |
from being called a second time. If the OTUnbind call is made a second time |
before the T_UNBINDCOMPLETE event has occurred, then the call results in an |
StateChangeErr |
// RRK 3/9/99 add new code to protect against multiple unbind calls |
********************************************************************************/ |
OSStatus DoOTUnbind(MyEndpointRef *theEp) |
{ |
OSStatus err = kOTNoError; |
Boolean isSync; |
// OTDebugStr("Entered DoOTUnbind"); |
if (TstEPBoundFlag(theEp->flags) && !TstUnbindPendFlag(theEp->flags)) |
{ |
// check whether the ep is asynch and set a flag to indicate we are waiting for the |
// unbind to complete. We have to set the flag before we issue the call as |
// the T_UNBINDCOMPLETE call could occur before we could set the flag is done |
// after the OTUnbind call |
isSync = OTIsSynchronous(theEp->ep); |
if (isSync == false) |
{ |
SetUnbindPendFlag(theEp->flags); |
} |
OTDebugStr("About to call OTUnbind"); |
err = OTUnbind(theEp->ep); |
if (err != kOTNoError) |
{ |
DoValueBreak(err, "Error calling OTUnbind #"); |
SetForceCloseFlag(theEp->flags); |
// on err clear the UnbindPendFlag |
ClrUnbindPendFlag(theEp->flags); |
} |
else |
{ |
if (isSync == true) |
{ |
ClrEPBusyFlag(theEp->flags); |
ClrEPBoundFlag(theEp->flags); |
} |
} |
} |
return err; |
} |
/******************************************************************************* |
** 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; |
} |
/******************************************************************************* |
** StartAbortiveDisconnect RRK 3/9/99 addition |
** starts an abortive disconnect of the endpoint by first forcing a flush of the |
cache, which results in a kStreamIoctlEvent handler event |
********************************************************************************/ |
OSStatus StartAbortiveDisconnect(MyEndpointRef *myEp) |
{ |
OSStatus err = kOTNoError; |
err = OTIoctl(myEp->ep, I_FLUSH, (void*) FLUSHRW); |
if (err) |
DoOTUnbind(myEp->ep); |
return err; |
} |
/******************************************************************************* |
** ActivatePAPEndpoint |
** |
********************************************************************************/ |
OSStatus ActivatePAPEndpoint(MyEndpointRef *myEp) |
{ |
OSStatus err = kOTNoError; |
OTResult result; |
// |
// 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); |
} |
#if HANDOFF_EP |
#else |
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) |
{ |
/*------------------------------------------------------------------------- |
Bind the endpoint endpoint using NBP |
------------------------------------------------------------------------- */ |
#if HANDOFF_EP |
err = DoBind(myEp->ep, kDynamicSocket, kATPType, kMaxHandoffEPs, (char*)gServerNBPNameStr, clen((char*)gServerNBPNameStr)); |
#else |
err = DoBind(myEp->ep, kDynamicSocket, kATPType, 1, (char*)gServerNBPNameStr, clen((char*)gServerNBPNameStr)); |
#endif |
if ( err != kOTNoError ) |
{ |
fprintf(stderr, "PAPSample: bind of server endpoint failed.\n"); |
} |
else |
{ |
SetEPBoundFlag(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 if (err > kOTNoError) |
{ |
fprintf(stderr, "selfsend previously enable\n"); |
err = kOTNoError; // don't let this stop the app. |
} |
else |
{ |
fprintf(stderr, "selfsend enabled\n"); |
} |
} |
// lastly, set endpoint into async modez |
if (err == kOTNoError) |
OTSetAsynchronous(myEp->ep); |
/*------------------------------------------------------------------------- |
Set the server status string |
------------------------------------------------------------------------- */ |
if (err == kOTNoError) |
BeginSetServerStatusOption(myEp->ep, kSetIdleStr); |
return err; |
} |
#if HANDOFF_EP |
/******************************************************************************* |
** ActivateHandoffEndpoint function - Open prepare a handoff endpoint for the |
PAP server |
********************************************************************************/ |
OSStatus ActivateHandoffEndpoint(MyEndpointRef *myEp) |
{ |
OSStatus err; |
myEp->ep = OTOpenEndpoint(OTCreateConfiguration(kPAPName), (OTOpenFlags)NULL, NULL, &err); |
if (err == kOTNoError) |
{ |
SetEPActiveFlag(myEp->flags); |
// turn on the EOM option |
err = DoNegotiateEOMOption(myEp->ep, true); |
if (err != kOTNoError) |
{ |
ClrEOMOnFlag(myEp->flags); |
fprintf(stderr, "\nError setting EOM option %d\n", err); |
} |
else |
{ |
SetEOMOnFlag(myEp->flags); |
} |
} |
if (err == kOTNoError) |
{ |
err = OTInstallNotifier(myEp->ep, EventHandler, (void*)myEp); |
if ( err != kOTNoError ) |
{ |
fprintf(stderr, "ERROR: InstallNotifier() failed with %ld\n for handoff endpoint &d\n", err); |
} |
else |
OTSetAsynchronous(myEp->ep); |
} |
return err; |
} |
#endif |
/******************************************************************************* |
** 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 which can be changed:"); |
fprintf(stdout, "\n 1. SetMemoryLimits"); |
fprintf(stdout, "\n 2. Dump packets"); |
fprintf(stdout, "\n Enter - n to use default settings"); |
fprintf(stdout, "\n Enter - y to alter above settings"); |
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"); |
fprintf(stdout, "\n Enter - n to use default setting"); |
fprintf(stdout, "\n Enter - y to set heap growth"); |
if (gets(mystr) != 0) |
{ |
stringtonum(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?"); |
fprintf(stdout, "\n Enter - n to save packets to default file"); |
fprintf(stdout, "\n Enter - y to dump packets"); |
selection = GetYesNoOption(); |
if (selection == kQuitTest) |
err = -1; |
else if (selection == kAcceptOption) |
{ |
SetdumpAllPktsFlag(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++) |
err = ActivateHandoffEndpoint(&gHandoffEp[i]); // RRK 3/10/99 call ActivateHandoffEndpoint |
if (err != kOTNoError) |
{ |
fprintf(stderr, "\n\nError opening the handoff endpoint!\n"); |
fprintf(stderr, "\nBye Bye.\n"); |
} |
} |
#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 |
DoOTUnbind(gHandoffEp[i].ep); // unbind the endpoint |
// RRK 3/9/99 change to protect against multiple unbinds |
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 |
DoOTUnbind(gEp.ep); // unbind the endpoint |
// RRK 3/9/99 change to protect against multiple unbinds |
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 (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]; |
} |
OTDebugStr((const char *)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 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++) |
{ // RRK 3/9/99 change to protect against the use of a handoff endpoint that is |
// not active, will be forced to close, or is busy |
// don't allow use of a handoff endpoint if the endpoint is |
// busy, or if it will be closed, or if it not active |
if ((TstEPBusyFlag(gHandoffEp[i].flags) == false) && |
(TstEPActiveFlag(gHandoffEp[i].flags) == true) && |
(TstOpenPendFlag(gHandoffEp[i].flags) == false) && |
(TstForceCloseFlag(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