mirror of https://github.com/lianthony/NT4.0
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
3808 lines
137 KiB
3808 lines
137 KiB
/* pap.c, /appletalk/source, Garth Conboy, 07/14/89 */
|
|
/* Copyright (c) 1989 by Pacer Software Inc., La Jolla, CA */
|
|
|
|
/* GC - Initial coding.
|
|
GC - (12/11/89): AppleTalk phase II comes to town.
|
|
GC - (08/18/90): New error logging mechanism.
|
|
GC - (01/20/92): Workaround for bug in the new Apple LaserWriter IIg
|
|
(it sends truncated open request denied replys, it
|
|
doesn't bother to add the one byte for a zero-sized
|
|
status string).
|
|
GC - (03/24/92): Got "static"s right in NbpLookupComplete().
|
|
GC - (03/24/92): Added PapCancelGetNextJob().
|
|
GC - (03/24/92): Changed calls to AtpEnqueueRequestHandler() to operate
|
|
in the new mode that allows Atp to simply pass up pointers
|
|
into incoming Ddp buffers rather than copying the data
|
|
into supplied buffers.
|
|
GC - (06/27/92): All buffers coming from user space are now "opaque," they
|
|
may or may not be "char *." We now use the new routines
|
|
MoveFromOpaque() and MoveToOpaque() to access these
|
|
"buffer"s rather than MemMove().
|
|
GC - (07/24/92): Undid a little of the above... NbpAction() and NbpRemove()
|
|
now take real "char *"s rather than opaque guys.
|
|
GC - (09/02/92): PapCreateServiceListenerOnNode now can optionally take
|
|
and existing Atp socket on which to create the listener,
|
|
and may optionally not do server name registration.
|
|
GC - (09/02/92): PapRead now takes a buffer size and returns an error if
|
|
it is smaller than the receive quantum size.
|
|
GC - (10/13/92): OpenJobOnNode can now take a target address as well as
|
|
an Nbp name.
|
|
GC - (11/14/92): Added PapSetCookieForJob() and PapGetCookieForJob().
|
|
GC - (11/14/92): Added "existingAtpSocket" argument to PapOpenJobOnNode().
|
|
GC - (12/10/92): Integrated Microsoft (Nikki) indication changes.
|
|
GC - (12/13/92): Locks and reference counts come to town.
|
|
|
|
*** Make the PVCS source control system happy:
|
|
$Header$
|
|
$Log$
|
|
***
|
|
|
|
Yep, this module contains all of the code for managing the AppleTalk
|
|
PAP protocol. This is it, the last major module of Pacer's portable
|
|
AppleTalk stack!
|
|
|
|
*/
|
|
|
|
#define IncludePapErrors 1
|
|
|
|
#include "atalk.h"
|
|
|
|
#define VerboseMessages 0
|
|
#define DebugCode 0
|
|
|
|
ExternForVisibleFunction ServiceListenerInfo
|
|
FindServiceListenerInfoFor(long serviceListenerRefNum);
|
|
|
|
ExternForVisibleFunction NbpCompletionHandler NbpRegisterComplete;
|
|
|
|
ExternForVisibleFunction ActiveJobInfo FindActiveJobInfoFor(long jobRefNum);
|
|
|
|
ExternForVisibleFunction AtpIncomingRequestHandler IncomingServiceListenerPacket;
|
|
|
|
ExternForVisibleFunction long GetNextJobRefNum(void);
|
|
|
|
ExternForVisibleFunction TimerHandler ConnectionTimerExpired;
|
|
|
|
ExternForVisibleFunction AtpIncomingRequestHandler IncomingRequestPacket;
|
|
|
|
ExternForVisibleFunction AtpIncomingResponseHandler IncomingStatus;
|
|
|
|
ExternForVisibleFunction AtpIncomingResponseHandler IncomingOpenReply;
|
|
|
|
ExternForVisibleFunction NbpCompletionHandler NbpLookupComplete;
|
|
|
|
ExternForVisibleFunction AtpIncomingResponseHandler IncomingReadComplete;
|
|
|
|
ExternForVisibleFunction AtpIncomingReleaseHandler PapIncomingRelease,
|
|
SlsIncomingRelease;
|
|
|
|
ExternForVisibleFunction void PostSendDataResponse(ActiveJobInfo activeJobInfo);
|
|
|
|
ExternForVisibleFunction void far
|
|
UnlinkServiceListenerInfo(ServiceListenerInfo serviceListenerInfo);
|
|
|
|
ExternForVisibleFunction void far
|
|
UnlinkActiveJobInfo(ActiveJobInfo activeJobInfo);
|
|
|
|
ExternForVisibleFunction Boolean far
|
|
UnlinkOpenJobInfo(OpenJobInfo openJobInfo);
|
|
|
|
ExternForVisibleFunction CloseCompletionRoutine PapSlCloseComplete;
|
|
|
|
ExternForVisibleFunction CloseCompletionRoutine PapAjCloseComplete;
|
|
|
|
/* The service listener needs to send out various internally created Atp
|
|
responses (open replies, status replies, etc.). The data packet that
|
|
is sent must be dynamically allocated, and freed when the Release comes
|
|
in; to make matters worse, Atp wants an "opaque" buffer rather than a
|
|
"char *." So, the following node holds the required fields, it is passed
|
|
"as userData" to these AtpPostResponses and, at the release, we free it
|
|
and the "opaque descriptor," if needed. */
|
|
|
|
typedef struct { char papData[PapMaximumDataPacketSize];
|
|
void far *opaquePapData; /* Opaque descriptor for above. */
|
|
Boolean freeOpaquePapData; /* Do we need to free descriptor? */
|
|
} *SlsResponseCompletionCookie;
|
|
|
|
ExternForVisibleFunction SlsResponseCompletionCookie
|
|
NewSlsResponseCompletionCookie(void);
|
|
|
|
ExternForVisibleFunction void
|
|
FreeSlsResponseCompletionCookie(SlsResponseCompletionCookie cookie);
|
|
|
|
AppleTalkErrorCode far PapCreateServiceListenerOnNode(
|
|
int port, /* Port on which the socket should live. */
|
|
long existingAtpSocket, /* "-1" if we should open our own Atp socket
|
|
for the listener; if ">= 0" this is
|
|
an already open Atp socket on which to
|
|
create the listener. */
|
|
int desiredSocket, /* Desired static socket or zero for
|
|
dynamic; ignored if above isnt -1. */
|
|
char far *object, /* NBP object of the server; if Empty, don't
|
|
bother doing a name registration; in this
|
|
case this routine completes syncrhonously
|
|
(the completionRoutine will NOT be
|
|
called). */
|
|
char far *type, /* NBP type of the server. */
|
|
char far *zone, /* NBP zone of the server, beter be Empty
|
|
or "*". */
|
|
short serverQuantum, /* How many full PAP packets (512 bytes)
|
|
can the server handle. */
|
|
long far *returnSocket, /* Full AppleTalkAddress of the ATP socket
|
|
we'll open (the socket that the service
|
|
listener will be open on). */
|
|
long far *serviceListenerRefNum,
|
|
/* The service listener ref num of the
|
|
created service listener. */
|
|
PapNbpRegisterComplete *completionRoutine,
|
|
/* "Who ya gonna call?", "PapBusters!";
|
|
not called if "object" is Empty. */
|
|
long unsigned userData, /* User data to be passed on to the
|
|
completion routine. */
|
|
StartJobHandler *startJobRoutine,
|
|
/* Incoming connection event handler
|
|
(may be empty). */
|
|
long unsigned startJobUserData) /* User data for above. */
|
|
{
|
|
StaticForSmallStack Boolean wrapped, okay;
|
|
StaticForSmallStack ServiceListenerInfo serviceListenerInfo;
|
|
StaticForSmallStack AppleTalkErrorCode errorCode;
|
|
StaticForSmallStack PapCompletionInfo completionInfo;
|
|
long socket;
|
|
|
|
/* Create a new service listener. Register the specified NBP name on the
|
|
new socket. When the NBP register completes call the specified completion
|
|
routine with the following arguments:
|
|
|
|
errorCode - AppleTalkErrorCode; from NBP.
|
|
userData - long unsigned; as passed to this routine.
|
|
serviceListenerRefNum - long; the service listener who's register
|
|
is now complete.
|
|
|
|
This routine will NOT be called if the "object" argument is Empty,
|
|
indicating that we shouldn't do a name registation.
|
|
*/
|
|
|
|
/* Argument error checking. */
|
|
|
|
if (serverQuantum < 1 or serverQuantum > PapMaximumFlowQuantum)
|
|
return(ATpapBadQuantum);
|
|
|
|
wrapped = False;
|
|
okay = False;
|
|
|
|
DeferTimerChecking();
|
|
DeferAtpPackets();
|
|
|
|
/* Try to open the ATP socket for the service listener. */
|
|
|
|
if (existingAtpSocket >= 0)
|
|
socket = existingAtpSocket;
|
|
else
|
|
{
|
|
if ((errorCode = AtpOpenSocketOnNode(&socket, port, empty, desiredSocket,
|
|
empty, 0)) isnt ATnoError)
|
|
{
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
return((AppleTalkErrorCode)socket);
|
|
}
|
|
}
|
|
if (returnSocket isnt empty)
|
|
*returnSocket = socket;
|
|
|
|
/* Well, so far, so good, come up with a service listener ref num. */
|
|
|
|
TakeLock(PapLock);
|
|
while(not okay)
|
|
{
|
|
okay = True;
|
|
if ((lastServiceListenerRefNum += 1) < 0)
|
|
{
|
|
lastServiceListenerRefNum = 0;
|
|
if (wrapped)
|
|
{
|
|
ReleaseLock(PapLock);
|
|
ErrorLog("PapCreateServiceListenerOnNode", ISevError, __LINE__,
|
|
port, IErrPapNoMoreSLRefNums, IMsgPapNoMoreSLRefNums,
|
|
Insert0());
|
|
if (existingAtpSocket < 0)
|
|
AtpCloseSocketOnNode(socket, Empty, (long unsigned)0);
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
return(ATinternalError);
|
|
}
|
|
wrapped = True;
|
|
}
|
|
for (serviceListenerInfo = serviceListenerInfoHead;
|
|
okay and serviceListenerInfo isnt empty;
|
|
serviceListenerInfo = serviceListenerInfo->next)
|
|
if (serviceListenerInfo->serviceListenerRefNum is
|
|
lastServiceListenerRefNum)
|
|
okay = False;
|
|
}
|
|
*serviceListenerRefNum = lastServiceListenerRefNum;
|
|
ReleaseLock(PapLock);
|
|
|
|
/* Okay, things are really looking good now, build up a new service listener
|
|
structure. */
|
|
|
|
serviceListenerInfo =
|
|
(ServiceListenerInfo)Calloc(sizeof(*serviceListenerInfo), 1);
|
|
if (object is Empty)
|
|
{
|
|
/* We don't need to do the register, so pick some dummy non-Empty value
|
|
so that the following test won't trigger. */
|
|
|
|
completionInfo = (PapCompletionInfo)&completionInfo;
|
|
}
|
|
else
|
|
{
|
|
/* Else, get a completionInfo structure. */
|
|
|
|
completionInfo = (PapCompletionInfo)Calloc(sizeof(*completionInfo), 1);
|
|
}
|
|
|
|
if (serviceListenerInfo is empty or completionInfo is empty)
|
|
{
|
|
ErrorLog("PapCreateServiceListenerOnNode", ISevError, __LINE__,
|
|
port, IErrPapOutOfMemory, IMsgPapOutOfMemory,
|
|
Insert0());
|
|
if (serviceListenerInfo isnt empty)
|
|
Free(serviceListenerInfo);
|
|
if (existingAtpSocket < 0)
|
|
AtpCloseSocketOnNode(socket, Empty, (long unsigned)0);
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
return(AToutOfMemory);
|
|
}
|
|
serviceListenerInfo->serviceListenerRefNum = lastServiceListenerRefNum;
|
|
serviceListenerInfo->port = port;
|
|
serviceListenerInfo->socket = socket;
|
|
serviceListenerInfo->closeSocket = (existingAtpSocket < 0);
|
|
serviceListenerInfo->serverState = PapBlockedState;
|
|
serviceListenerInfo->serverFlowQuantum = serverQuantum;
|
|
|
|
/* Link 'em up! */
|
|
|
|
TakeLock(PapLock);
|
|
serviceListenerInfo->next = serviceListenerInfoHead;
|
|
serviceListenerInfoHead = Link(serviceListenerInfo);
|
|
|
|
/* Set indication handler info - it could be Empty. */
|
|
|
|
serviceListenerInfo->startJobRoutine = startJobRoutine;
|
|
serviceListenerInfo->startJobUserData = startJobUserData;
|
|
|
|
/* If the startJob indication handler is non-Empty, unblock. */
|
|
|
|
if (startJobRoutine isnt empty)
|
|
serviceListenerInfo->serverState = PapUnblockedState;
|
|
ReleaseLock(PapLock);
|
|
|
|
/* If we're not doing a register, we need to post an outstanding ATP
|
|
GetRequest in order to handle incoming OpenConn's and SendStatus's. */
|
|
|
|
if (object is Empty)
|
|
{
|
|
errorCode = AtpEnqueueRequestHandler(empty, serviceListenerInfo->socket,
|
|
empty, 0, empty,
|
|
IncomingServiceListenerPacket,
|
|
(long unsigned)
|
|
lastServiceListenerRefNum);
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
if (errorCode isnt ATnoError)
|
|
PapDeleteServiceListener(*serviceListenerRefNum, Empty,
|
|
(long unsigned)0);
|
|
else
|
|
errorCode = ATnoError; /* We don't care about the request
|
|
handler ID. */
|
|
return(errorCode);
|
|
}
|
|
|
|
/* Otherwise, start the NBP register of the server's name. */
|
|
|
|
completionInfo->serviceListenerRefNum = lastServiceListenerRefNum;
|
|
completionInfo->initialRegister = True;
|
|
completionInfo->completionRoutine = (void *)completionRoutine;
|
|
completionInfo->userData = userData;
|
|
errorCode = NbpAction(ForRegister, socket, object, type,
|
|
zone, GetNextNbpIdForNode(socket), 0, 0,
|
|
NbpRegisterComplete,
|
|
(long unsigned)completionInfo);
|
|
if (errorCode isnt ATnoError)
|
|
{
|
|
if (completionInfo->freeOpaqueResponse)
|
|
FreeOpaqueDataDescriptor(completionInfo->opaqueResponse);
|
|
PapDeleteServiceListener(*serviceListenerRefNum, Empty,
|
|
(long unsigned)0);
|
|
Free(completionInfo);
|
|
}
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
return(errorCode);
|
|
|
|
} /* PapCreateServiceListener */
|
|
|
|
AppleTalkErrorCode far PapDeleteServiceListener(
|
|
long serviceListenerRefNum, /* The service listener to delete. */
|
|
CloseCompletionRoutine far *closeCompletionRoutine,
|
|
/* Routine to call when the close completes. */
|
|
long unsigned closeUserData) /* User data for the above. */
|
|
{
|
|
/* This routine can indirectly recurse through "AtpCloseSocketOnNode". */
|
|
|
|
StaticForSmallStack ActiveJobInfo activeJobInfo, nextActiveJobInfo;
|
|
ServiceListenerInfo serviceListenerInfo;
|
|
|
|
DeferTimerChecking();
|
|
DeferAtpPackets();
|
|
|
|
/* Find our service listener. */
|
|
|
|
if ((serviceListenerInfo =
|
|
FindServiceListenerInfoFor(serviceListenerRefNum)) is Empty or
|
|
serviceListenerInfo->closing)
|
|
{
|
|
UnlinkServiceListenerInfo(serviceListenerInfo);
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
return(ATpapNoSuchServiceListener);
|
|
}
|
|
|
|
/* Set about closing. */
|
|
|
|
TakeLock(PapLock);
|
|
serviceListenerInfo->closing = True;
|
|
serviceListenerInfo->closeContext.closeCompletionRoutine =
|
|
closeCompletionRoutine;
|
|
serviceListenerInfo->closeContext.closeUserData = closeUserData;
|
|
ReleaseLock(PapLock);
|
|
|
|
/* Get rid of all pending GetNextJobs. */
|
|
|
|
while (serviceListenerInfo->getNextJobList isnt Empty)
|
|
PapCancelGetNextJob(serviceListenerRefNum,
|
|
serviceListenerInfo->getNextJobList->jobRefNum,
|
|
True);
|
|
|
|
/* Close all active jobs to this server. */
|
|
|
|
TakeLock(PapLock);
|
|
activeJobInfo = Link(serviceListenerInfo->activeJobList);
|
|
ReleaseLock(PapLock);
|
|
while (activeJobInfo isnt empty)
|
|
{
|
|
PapCloseJob(activeJobInfo->jobRefNum, Empty, (long unsigned)0,
|
|
False, False);
|
|
TakeLock(PapLock);
|
|
nextActiveJobInfo = Link(activeJobInfo->nextForMyServiceListener);
|
|
ReleaseLock(PapLock);
|
|
UnlinkActiveJobInfo(activeJobInfo);
|
|
activeJobInfo = nextActiveJobInfo;
|
|
}
|
|
|
|
/* Okay, unlink the serive listener (twice) [once for the above Link and
|
|
one for the "create"]. */
|
|
|
|
UnlinkServiceListenerInfo(serviceListenerInfo);
|
|
UnlinkServiceListenerInfo(serviceListenerInfo);
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
|
|
return(ATnoError);
|
|
|
|
} /* PapDeleteServiceListener */
|
|
|
|
AppleTalkErrorCode far PapSetConnectionEventHandler(
|
|
long serviceListenerRefNum, /* Listener on which to set the handler. */
|
|
StartJobHandler *startJobRoutine,
|
|
/* Incoming connection event handler (may
|
|
be Empty). */
|
|
long unsigned startJobUserData) /* User data for above. */
|
|
{
|
|
AppleTalkErrorCode errorCode = ATnoError;
|
|
ServiceListenerInfo serviceListenerInfo;
|
|
|
|
/* Find our service listener. */
|
|
|
|
DeferTimerChecking();
|
|
DeferAtpPackets();
|
|
|
|
serviceListenerInfo = FindServiceListenerInfoFor(serviceListenerRefNum);
|
|
if (serviceListenerInfo isnt Empty and not serviceListenerInfo->closing)
|
|
{
|
|
TakeLock(PapLock);
|
|
serviceListenerInfo->startJobRoutine = startJobRoutine;
|
|
serviceListenerInfo->startJobUserData = startJobUserData;
|
|
|
|
/* If the startJob indication handler is non-Empty, unblock. */
|
|
|
|
if (startJobRoutine isnt empty)
|
|
serviceListenerInfo->serverState = PapUnblockedState;
|
|
ReleaseLock(PapLock);
|
|
|
|
}
|
|
else
|
|
errorCode = ATpapNoSuchServiceListener;
|
|
|
|
UnlinkServiceListenerInfo(serviceListenerInfo);
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
return(errorCode);
|
|
|
|
} /* PapSetConnectionEventHandler */
|
|
|
|
AppleTalkErrorCode far PapOpenJobOnNode(
|
|
int port, /* Port on which the ATP socket should
|
|
live. */
|
|
long existingAtpSocket, /* "-1" if we should open our own Atp socket
|
|
for the new job; if ">= 0" this is
|
|
an already open Atp socket on which to
|
|
open job. */
|
|
int desiredSocket, /* Desired static socket or zero for
|
|
dynamic; ignored if above isnt "-1". */
|
|
long far *jobRefNum, /* The jobRefNum that will be used when this
|
|
job is really started. */
|
|
AppleTalkAddress *serviceListenerAddress,
|
|
/* Address of remote service listener, if
|
|
Empty look it up using the below Nbp
|
|
information. */
|
|
char far *object, /* The NBP object of the service listener. */
|
|
char far *type, /* The NBP type of the service listener. */
|
|
char far *zone, /* The NBP zone of the service listener. */
|
|
short workstationQuantum, /* The number of full PAP buffers (512 bytes
|
|
each) that we can handle coming in our
|
|
direction; 8 is the "right" value. */
|
|
void far *opaqueStatusBuffer, /* "Buffer" to be filled in with the server's
|
|
status (at least 255 bytes). */
|
|
SendPossibleHandler *sendPossibleRoutine,
|
|
/* Send okay routine (may be Empty) */
|
|
long unsigned sendPossibleUserData,
|
|
/* User data for above. */
|
|
CloseJobHandler *closeJobRoutine,
|
|
/* If the open goes okay, when do we call
|
|
when the connection is eventually
|
|
closed (may be Empty). */
|
|
long unsigned closeJobUserData, /* User data for close routine. */
|
|
PapOpenComplete *completionRoutine,
|
|
/* Who to call on completeion. */
|
|
long unsigned userData) /* Passed on to the above. */
|
|
{
|
|
PapCompletionInfo completionInfo;
|
|
OpenJobInfo openJobInfo;
|
|
AppleTalkAddress address;
|
|
AppleTalkErrorCode errorCode;
|
|
long socket;
|
|
StaticForSmallStack char userBytes[AtpUserBytesSize];
|
|
long unsigned now;
|
|
|
|
/* A little argument verification. */
|
|
|
|
if (workstationQuantum < 1 or workstationQuantum > PapMaximumFlowQuantum)
|
|
return(ATpapBadQuantum);
|
|
if (port is DefaultPort)
|
|
if ((port = FindDefaultPort()) < 0)
|
|
return(ATappleTalkShutDown);
|
|
|
|
/* We will need a socket to do an NBP lookup from; use the NIS on one of
|
|
our current port's node to perform the lookup. There is a vauge chance
|
|
that our port doesn't have any nodes yet... */
|
|
|
|
if (PortDescriptor(port)->activeNodes is empty)
|
|
if ((errorCode = GetNodeOnPort(port, True, True, False, empty))
|
|
isnt ATnoError)
|
|
return(errorCode);
|
|
|
|
/* Pick (and assign) the next job ref num. */
|
|
|
|
DeferTimerChecking();
|
|
DeferAtpPackets();
|
|
if ((*jobRefNum = GetNextJobRefNum()) < 0)
|
|
{
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
return(ATinternalError);
|
|
}
|
|
|
|
/* Well, it looks as we can get started; get the needed memory. We'll
|
|
need to house an NbpLookupReply in our completion info... Nbp and Atp
|
|
want to fill an "opaque" buffer, so create one of these. */
|
|
|
|
completionInfo =
|
|
(PapCompletionInfo)Calloc(sizeof(*completionInfo) +
|
|
PapMaximumDataPacketSize, 1);
|
|
if (completionInfo isnt Empty)
|
|
completionInfo->opaqueResponse =
|
|
MakeOpaqueDataDescriptor(completionInfo->responseBuffer,
|
|
PapMaximumDataPacketSize,
|
|
&completionInfo->freeOpaqueResponse);
|
|
openJobInfo = (OpenJobInfo)Calloc(sizeof(*openJobInfo), 1);
|
|
if (completionInfo is Empty or
|
|
completionInfo->opaqueResponse is Empty or
|
|
openJobInfo is Empty)
|
|
{
|
|
if (completionInfo isnt Empty)
|
|
{
|
|
if (completionInfo->freeOpaqueResponse)
|
|
FreeOpaqueDataDescriptor(completionInfo->opaqueResponse);
|
|
Free(completionInfo);
|
|
}
|
|
if (openJobInfo isnt Empty)
|
|
Free(openJobInfo);
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
return(AToutOfMemory);
|
|
}
|
|
|
|
/* Build (and link) the OpenJob node. */
|
|
|
|
openJobInfo->jobRefNum = *jobRefNum;
|
|
openJobInfo->port = port;
|
|
openJobInfo->existingAtpSocket = existingAtpSocket;
|
|
openJobInfo->desiredSocket = desiredSocket;
|
|
openJobInfo->workstationQuantum = workstationQuantum;
|
|
openJobInfo->closeJobRoutine = closeJobRoutine;
|
|
openJobInfo->closeJobUserData = closeJobUserData;
|
|
openJobInfo->startTime = CurrentRelativeTime();
|
|
TakeLock(PapLock);
|
|
openJobInfo->next = openJobInfoHead;
|
|
openJobInfoHead = Link(openJobInfo);
|
|
ReleaseLock(PapLock);
|
|
|
|
/* Build the completion into structure. */
|
|
|
|
completionInfo->completionRoutine = (void *)completionRoutine;
|
|
completionInfo->userData = userData;
|
|
completionInfo->jobRefNum = *jobRefNum;
|
|
completionInfo->usersOpaqueStatusBuffer = opaqueStatusBuffer;
|
|
|
|
/* If we know the SLS address, just proceed to doing a Pap open. */
|
|
|
|
if (serviceListenerAddress isnt Empty)
|
|
{
|
|
/* We need to open our ATP responding socket. */
|
|
|
|
if (existingAtpSocket < 0)
|
|
errorCode = AtpOpenSocketOnNode(&socket, openJobInfo->port, empty,
|
|
openJobInfo->desiredSocket, empty, 0);
|
|
else
|
|
{
|
|
errorCode = ATnoError;
|
|
socket = existingAtpSocket;
|
|
}
|
|
|
|
if (errorCode isnt ATnoError or MapSocketToAddress(socket, &address))
|
|
{
|
|
/* Couldn't open our ATP socket. Sigh. */
|
|
|
|
if (openJobInfo->freeOpaqueDataDescriptor)
|
|
FreeOpaqueDataDescriptor(openJobInfo->opaqueDataDescriptor);
|
|
UnlinkOpenJobInfo(openJobInfo);
|
|
if (completionInfo->freeOpaqueResponse)
|
|
FreeOpaqueDataDescriptor(completionInfo->opaqueResponse);
|
|
Free(completionInfo);
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
return(errorCode);
|
|
}
|
|
|
|
/* Save this beast. */
|
|
|
|
completionInfo->socket = socket;
|
|
completionInfo->mustCloseAtpSocket = (existingAtpSocket < 0);
|
|
|
|
/* Set the ATP data packet size correctly for PAP. */
|
|
|
|
if (AtpMaximumSinglePacketDataSize(completionInfo->socket,
|
|
PapMaximumDataPacketSize) isnt
|
|
ATnoError)
|
|
ErrorLog("PapOpenJobOnNode", ISevError, __LINE__, UnknownPort,
|
|
IErrPapBadSetSize, IMsgPapBadSetSize,
|
|
Insert0());
|
|
|
|
/* Assign a new connection ID. */
|
|
|
|
TakeLock(PapLock);
|
|
openJobInfo->connectionId = (unsigned char)(lastConnectionId += 1);
|
|
ReleaseLock(PapLock);
|
|
|
|
/* Save away the address of our target service listener. */
|
|
|
|
openJobInfo->serviceListenerAddress = *serviceListenerAddress;
|
|
|
|
/* Okay, prepare to post the OpenConn request; build up both userBytes and
|
|
data buffer! */
|
|
|
|
userBytes[PapConnectionIdOffset] = openJobInfo->connectionId;
|
|
userBytes[PapCommandTypeOffset] = PapOpenConnectionCommand;
|
|
userBytes[PapSequenceNumberOffset] = 0;
|
|
userBytes[PapSequenceNumberOffset + 1] = 0;
|
|
|
|
/* The following is somewhat of a hack... we really want to build our own
|
|
data buffer: a "char *," not whatever our surrounding environment thinks
|
|
is an "opaque" buffer. So, build our array, and then make a system
|
|
dependent "opaque data descriptor" for it, noting whether we need to
|
|
free this when we free the openJobInfo. */
|
|
|
|
now = CurrentRelativeTime();
|
|
openJobInfo->buffer[PapRespondingSocketOffset] = address.socketNumber;
|
|
openJobInfo->buffer[PapFlowQuantumOffset] =
|
|
(char)openJobInfo->workstationQuantum;
|
|
openJobInfo->buffer[PapWaitTimeOffset] =
|
|
(char)((now - openJobInfo->startTime) >> 8);
|
|
openJobInfo->buffer[PapWaitTimeOffset + 1] =
|
|
(char)((now - openJobInfo->startTime) & 0xFF);
|
|
errorCode = ATnoError;
|
|
if ((openJobInfo->opaqueDataDescriptor =
|
|
MakeOpaqueDataDescriptor(openJobInfo->buffer, PapStatusOffset,
|
|
&openJobInfo->freeOpaqueDataDescriptor))
|
|
is Empty)
|
|
errorCode = AToutOfMemory;
|
|
|
|
/* PostIt! (a trademark of 3M) */
|
|
|
|
if (errorCode is ATnoError)
|
|
errorCode = AtpPostRequest(completionInfo->socket,
|
|
*serviceListenerAddress,
|
|
Empty,
|
|
openJobInfo->opaqueDataDescriptor,
|
|
PapStatusOffset, userBytes,
|
|
True, completionInfo->opaqueResponse,
|
|
PapMaximumDataPacketSize,
|
|
completionInfo->responseUserBytes,
|
|
PapOpenConnRequestRetryCount,
|
|
PapOpenConnAtpRetrySeconds,
|
|
ThirtySecondsTRelTimer,
|
|
IncomingOpenReply,
|
|
(long unsigned)completionInfo);
|
|
|
|
if (errorCode < 0) /* Couldn't send request. */
|
|
{
|
|
if (completionInfo->mustCloseAtpSocket)
|
|
AtpCloseSocketOnNode(completionInfo->socket, Empty,
|
|
(long unsigned)0);
|
|
if (openJobInfo->freeOpaqueDataDescriptor)
|
|
FreeOpaqueDataDescriptor(openJobInfo->opaqueDataDescriptor);
|
|
UnlinkOpenJobInfo(openJobInfo);
|
|
if (completionInfo->freeOpaqueResponse)
|
|
FreeOpaqueDataDescriptor(completionInfo->opaqueResponse);
|
|
Free(completionInfo);
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
return(errorCode);
|
|
}
|
|
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
return(ATnoError);
|
|
}
|
|
|
|
/* Okay, we need to lookup the guy up first, use the NIS socket, and
|
|
post the lookup. */
|
|
|
|
errorCode = ATnoError;
|
|
if ((socket = MapNisOnPortToSocket(port)) < 0 or
|
|
(errorCode = NbpAction(ForLookup, socket, object, type,
|
|
zone, GetNextNbpIdForNode(socket), 0, 0,
|
|
NbpLookupComplete, (long unsigned)completionInfo,
|
|
completionInfo->opaqueResponse,
|
|
MaximumNbpTupleLength, 1)) isnt ATnoError)
|
|
{
|
|
if (errorCode = ATnoError)
|
|
errorCode = ATinternalError;
|
|
if (completionInfo->freeOpaqueResponse)
|
|
FreeOpaqueDataDescriptor(completionInfo->opaqueResponse);
|
|
Free(completionInfo);
|
|
if (openJobInfo->freeOpaqueDataDescriptor)
|
|
FreeOpaqueDataDescriptor(openJobInfo->opaqueDataDescriptor);
|
|
UnlinkOpenJobInfo(openJobInfo);
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
return(errorCode);
|
|
}
|
|
else
|
|
{
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
return(ATnoError);
|
|
}
|
|
|
|
} /* PapOpenJob */
|
|
|
|
AppleTalkErrorCode far PapCloseJob(
|
|
long jobRefNum, /* The job to close. */
|
|
CloseCompletionRoutine far *closeCompletionRoutine,
|
|
/* Close completion routine. */
|
|
long unsigned closeUserData, /* User data for above. */
|
|
Boolean remoteClose, /* All external callers should pass "False";
|
|
inverts server/workstation context of the
|
|
code passed to the close completeion
|
|
routine. */
|
|
Boolean closedByConnectionTimer)
|
|
/* All external callers should pass "False";
|
|
causes close code to be due to the
|
|
connection timer expiring. */
|
|
{
|
|
ActiveJobInfo activeJobInfo;
|
|
AppleTalkErrorCode closeCode;
|
|
char userBytes[AtpUserBytesSize];
|
|
|
|
/* Find the activeJob node. */
|
|
|
|
DeferTimerChecking();
|
|
DeferAtpPackets();
|
|
|
|
if ((activeJobInfo = FindActiveJobInfoFor(jobRefNum)) is Empty or
|
|
activeJobInfo->closing)
|
|
{
|
|
UnlinkActiveJobInfo(activeJobInfo);
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
return(ATpapNoSuchJob);
|
|
}
|
|
|
|
/* Set about closing. */
|
|
|
|
TakeLock(PapLock);
|
|
activeJobInfo->closing = True;
|
|
activeJobInfo->closeContext.closeCompletionRoutine = closeCompletionRoutine;
|
|
activeJobInfo->closeContext.closeUserData = closeUserData;
|
|
ReleaseLock(PapLock);
|
|
|
|
/* Cancel the connection maintenance timer. */
|
|
|
|
if (not closedByConnectionTimer)
|
|
CancelTimer(activeJobInfo->connectionTimerId);
|
|
|
|
/* Send a close-connection to the other side. Don't send out a Close packet
|
|
if this close is taking place on behalf of an incoming close from the
|
|
other side! */
|
|
|
|
if (not remoteClose)
|
|
{
|
|
userBytes[PapConnectionIdOffset] = (char)activeJobInfo->connectionId;
|
|
userBytes[PapCommandTypeOffset] = PapCloseConnectionCommand;
|
|
userBytes[PapSequenceNumberOffset] = 0;
|
|
userBytes[PapSequenceNumberOffset + 1] = 0;
|
|
if (AtpPostRequest(activeJobInfo->ourSocket,
|
|
activeJobInfo->theirAddress, empty,
|
|
empty, 0, userBytes, False,
|
|
empty, 0, empty,
|
|
0, 0, ThirtySecondsTRelTimer,
|
|
empty, (long unsigned)0) isnt ATnoError)
|
|
ErrorLog("PapCloseJob", ISevError, __LINE__, UnknownPort,
|
|
IErrPapBadCloseConnSend, IMsgPapBadCloseConnSend,
|
|
Insert0());
|
|
}
|
|
|
|
/* Compute close type... */
|
|
|
|
if (closedByConnectionTimer)
|
|
closeCode = ATpapClosedByConnectionTimer;
|
|
else if ((not remoteClose and activeJobInfo->serverJob) or
|
|
(remoteClose and not activeJobInfo->serverJob))
|
|
closeCode = ATpapClosedByServer;
|
|
else
|
|
closeCode = ATpapClosedByWorkstation;
|
|
activeJobInfo->closeCode = closeCode;
|
|
|
|
/* Unlink (twice) to set the close into process. */
|
|
|
|
UnlinkActiveJobInfo(activeJobInfo);
|
|
UnlinkActiveJobInfo(activeJobInfo);
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
return(ATnoError);
|
|
|
|
} /* PapCloseJob */
|
|
|
|
AppleTalkErrorCode far PapGetRemoteAddressForJob(
|
|
long jobRefNum,
|
|
AppleTalkAddress *remoteAddress)
|
|
{
|
|
ActiveJobInfo activeJobInfo;
|
|
AppleTalkErrorCode errorCode = ATnoError;
|
|
|
|
/* Find the activeJob node. */
|
|
|
|
DeferTimerChecking();
|
|
DeferAtpPackets();
|
|
|
|
if ((activeJobInfo = FindActiveJobInfoFor(jobRefNum)) isnt Empty)
|
|
{
|
|
*remoteAddress = activeJobInfo->theirAddress;
|
|
UnlinkActiveJobInfo(activeJobInfo);
|
|
}
|
|
else
|
|
errorCode = ATpapNoSuchJob;
|
|
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
return(errorCode);
|
|
}
|
|
|
|
AppleTalkErrorCode far PapSetCookieForJob(
|
|
long jobRefNum, /* The job on which to set the cookie. */
|
|
long unsigned cookie) /* The cookie to set. */
|
|
{
|
|
ActiveJobInfo activeJobInfo;
|
|
|
|
/* Find the activeJob node. */
|
|
|
|
DeferTimerChecking();
|
|
DeferAtpPackets();
|
|
if ((activeJobInfo = FindActiveJobInfoFor(jobRefNum)) is Empty)
|
|
{
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
return(ATpapNoSuchJob);
|
|
}
|
|
|
|
/* Do the deed. */
|
|
|
|
activeJobInfo->usersCookie = cookie;
|
|
UnlinkActiveJobInfo(activeJobInfo);
|
|
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
return(ATnoError);
|
|
|
|
} /* PapSetCookieForJob */
|
|
|
|
AppleTalkErrorCode far PapGetCookieForJob(
|
|
long jobRefNum, /* The job from which to get the cookie. */
|
|
long unsigned far *cookie) /* The place to stick the gotten cookie. */
|
|
{
|
|
ActiveJobInfo activeJobInfo;
|
|
|
|
/* Find the activeJob node. */
|
|
|
|
DeferTimerChecking();
|
|
DeferAtpPackets();
|
|
if ((activeJobInfo = FindActiveJobInfoFor(jobRefNum)) is Empty)
|
|
{
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
return(ATpapNoSuchJob);
|
|
}
|
|
|
|
/* Do the deed. */
|
|
|
|
*cookie = activeJobInfo->usersCookie;
|
|
UnlinkActiveJobInfo(activeJobInfo);
|
|
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
return(ATnoError);
|
|
|
|
} /* PapGetCookieForJob */
|
|
|
|
AppleTalkErrorCode far PapGetNextJob(
|
|
long serviceListenerRefNum, /* The service listener ref num of the
|
|
service listener we're looking for
|
|
work on. */
|
|
long far *jobRefNum, /* The job ref num that will be assigned to
|
|
this job when it gets started. */
|
|
StartJobHandler *startJobRoutine,
|
|
/* Routine to call when we get going. */
|
|
long unsigned startJobUserData, /* User data for above. */
|
|
CloseJobHandler *closeJobRoutine,
|
|
/* Routine to call when the job completes
|
|
(may be Empty). */
|
|
long unsigned closeJobUserData) /* User data for above. */
|
|
{
|
|
ServiceListenerInfo serviceListenerInfo;
|
|
GetNextJobInfo getNextJobInfo;
|
|
|
|
/* Find our service listener. */
|
|
|
|
DeferTimerChecking();
|
|
DeferAtpPackets();
|
|
serviceListenerInfo = FindServiceListenerInfoFor(serviceListenerRefNum);
|
|
if (serviceListenerInfo is empty or serviceListenerInfo->closing)
|
|
{
|
|
UnlinkServiceListenerInfo(serviceListenerInfo);
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
return(ATpapNoSuchServiceListener);
|
|
}
|
|
|
|
/* Build up a getNextJobInfo. */
|
|
|
|
getNextJobInfo = (GetNextJobInfo)Calloc(sizeof(*getNextJobInfo), 1);
|
|
if (getNextJobInfo is empty)
|
|
{
|
|
ErrorLog("PapGetNextJob", ISevError, __LINE__, UnknownPort,
|
|
IErrPapOutOfMemory, IMsgPapOutOfMemory,
|
|
Insert0());
|
|
UnlinkServiceListenerInfo(serviceListenerInfo);
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
return(AToutOfMemory);
|
|
}
|
|
if ((getNextJobInfo->jobRefNum = GetNextJobRefNum()) < 0)
|
|
{
|
|
UnlinkServiceListenerInfo(serviceListenerInfo);
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
return(ATinternalError);
|
|
}
|
|
getNextJobInfo->startJobRoutine = startJobRoutine;
|
|
getNextJobInfo->startJobUserData = startJobUserData;
|
|
getNextJobInfo->closeJobRoutine = closeJobRoutine;
|
|
getNextJobInfo->closeJobUserData = closeJobUserData;
|
|
|
|
/* Link it into the service listener's list. */
|
|
|
|
TakeLock(PapLock);
|
|
if (serviceListenerInfo->closing)
|
|
{
|
|
ReleaseLock(PapLock);
|
|
Free(getNextJobInfo);
|
|
UnlinkServiceListenerInfo(serviceListenerInfo);
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
return(ATpapNoSuchServiceListener);
|
|
}
|
|
getNextJobInfo->next = serviceListenerInfo->getNextJobList;
|
|
serviceListenerInfo->getNextJobList = getNextJobInfo;
|
|
serviceListenerInfo->serverState = PapUnblockedState;
|
|
ReleaseLock(PapLock);
|
|
|
|
/* All set. */
|
|
|
|
*jobRefNum = getNextJobInfo->jobRefNum;
|
|
UnlinkServiceListenerInfo(serviceListenerInfo);
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
return(ATnoError);
|
|
|
|
} /* PapGetNextJob */
|
|
|
|
AppleTalkErrorCode far PapCancelGetNextJob(
|
|
long serviceListenerRefNum, /* The service listener ref num of the
|
|
service listener we're looking to
|
|
cancel a GetNextJob on. */
|
|
long jobRefNum, /* The job ref num of the GetNextJob to
|
|
cancel. */
|
|
Boolean sessionListenerClosing) /* All external callers should pass
|
|
"False." */
|
|
{
|
|
ServiceListenerInfo serviceListenerInfo;
|
|
GetNextJobInfo getNextJobInfo, previousGetNextJobInfo = Empty;
|
|
|
|
/* Find our service listener. */
|
|
|
|
DeferTimerChecking();
|
|
DeferAtpPackets();
|
|
|
|
serviceListenerInfo = FindServiceListenerInfoFor(serviceListenerRefNum);
|
|
if (serviceListenerInfo is empty)
|
|
{
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
return(ATpapNoSuchServiceListener);
|
|
}
|
|
|
|
/* Okay, walk the list of GetNextJob handlers and find the one we're
|
|
looking to cancel. */
|
|
|
|
TakeLock(PapLock);
|
|
for (getNextJobInfo = serviceListenerInfo->getNextJobList;
|
|
getNextJobInfo isnt Empty;
|
|
previousGetNextJobInfo = getNextJobInfo,
|
|
getNextJobInfo = getNextJobInfo->next)
|
|
if (getNextJobInfo->jobRefNum is jobRefNum)
|
|
break;
|
|
if (getNextJobInfo is Empty)
|
|
{
|
|
ReleaseLock(PapLock);
|
|
UnlinkServiceListenerInfo(serviceListenerInfo);
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
return(ATpapNoSuchGetNextJob);
|
|
}
|
|
|
|
/* Remove this guy from the list. */
|
|
|
|
if (previousGetNextJobInfo is Empty)
|
|
serviceListenerInfo->getNextJobList = getNextJobInfo->next;
|
|
else
|
|
previousGetNextJobInfo->next = getNextJobInfo->next;
|
|
if (serviceListenerInfo->getNextJobList is Empty)
|
|
serviceListenerInfo->serverState = PapBlockedState;
|
|
ReleaseLock(PapLock);
|
|
|
|
/* If we're deleting the service listener, notify the start job routine. */
|
|
|
|
if (sessionListenerClosing)
|
|
(*getNextJobInfo->startJobRoutine)(ATpapServiceListenerDeleted,
|
|
getNextJobInfo->startJobUserData,
|
|
getNextJobInfo->jobRefNum,
|
|
0, 0);
|
|
|
|
Free(getNextJobInfo);
|
|
|
|
/* All set. */
|
|
|
|
UnlinkServiceListenerInfo(serviceListenerInfo);
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
return(ATnoError);
|
|
|
|
} /* PapCancelGetNextJob */
|
|
|
|
AppleTalkErrorCode far PapAcceptJob(
|
|
long jobRefNum, /* The job to accept. */
|
|
SendPossibleHandler *sendPossibleRoutine,
|
|
/* Send okay routine. */
|
|
long unsigned sendPossibleUserData,
|
|
/* User data for above. */
|
|
CloseJobHandler *closeJobRoutine,
|
|
/* Routine to call when the job completes. */
|
|
long unsigned closeJobUserData) /* User data for above. */
|
|
{
|
|
ActiveJobInfo activeJobInfo;
|
|
AppleTalkErrorCode errorCode = ATnoError;
|
|
|
|
/* This routine need only be used if a "start job indication" is used to
|
|
get the next job (non-empty startJobRoutine passed in when the service
|
|
listener was created) rather than the normal PapGetNext mechanism. */
|
|
|
|
/* Find the activeJob node. */
|
|
|
|
DeferTimerChecking();
|
|
DeferAtpPackets();
|
|
|
|
if ((activeJobInfo = FindActiveJobInfoFor(jobRefNum)) isnt Empty and
|
|
not activeJobInfo->closing)
|
|
{
|
|
/* Set the handlers for the job. */
|
|
|
|
TakeLock(PapLock);
|
|
activeJobInfo->closeJobRoutine = closeJobRoutine;
|
|
activeJobInfo->closeJobUserData = closeJobUserData;
|
|
activeJobInfo->sendPossibleRoutine = sendPossibleRoutine;
|
|
activeJobInfo->sendPossibleUserData = sendPossibleUserData;
|
|
ReleaseLock(PapLock);
|
|
}
|
|
else
|
|
errorCode = ATpapNoSuchJob;
|
|
|
|
UnlinkActiveJobInfo(activeJobInfo);
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
|
|
return(errorCode);
|
|
|
|
} /* PapAcceptJob */
|
|
|
|
AppleTalkErrorCode far PapRegisterName(
|
|
long serviceListenerRefNum, /* The service listener ref num of the
|
|
service listener that the new name should
|
|
be registered on. */
|
|
char far *object, /* NBP object of the server. */
|
|
char far *type, /* NBP type of the server. */
|
|
char far *zone, /* NBP zone of the server, beter be empty
|
|
or "*". */
|
|
PapNbpRegisterComplete *completionRoutine,
|
|
/* Who to call on NBP completion. */
|
|
long unsigned userData) /* Passed on to the completion routine. */
|
|
{
|
|
ServiceListenerInfo serviceListenerInfo;
|
|
PapCompletionInfo completionInfo;
|
|
AppleTalkErrorCode errorCode;
|
|
|
|
/* Find our service listener. */
|
|
|
|
DeferTimerChecking();
|
|
DeferAtpPackets();
|
|
serviceListenerInfo = FindServiceListenerInfoFor(serviceListenerRefNum);
|
|
if (serviceListenerInfo is empty or serviceListenerInfo->closing)
|
|
{
|
|
UnlinkServiceListenerInfo(serviceListenerInfo);
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
return(ATpapNoSuchServiceListener);
|
|
}
|
|
|
|
/* Build the completion info that we'll need for NBP. */
|
|
|
|
completionInfo = (PapCompletionInfo)Calloc(sizeof(*completionInfo), 1);
|
|
if (completionInfo is empty)
|
|
{
|
|
ErrorLog("PapRegisterName", ISevError, __LINE__, UnknownPort,
|
|
IErrPapOutOfMemory, IMsgPapOutOfMemory,
|
|
Insert0());
|
|
UnlinkServiceListenerInfo(serviceListenerInfo);
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
return(AToutOfMemory);
|
|
}
|
|
completionInfo->serviceListenerRefNum = serviceListenerRefNum;
|
|
completionInfo->initialRegister = False;
|
|
completionInfo->completionRoutine = (void *)completionRoutine;
|
|
completionInfo->userData = userData;
|
|
|
|
/* Start the NBP action and return. */
|
|
|
|
errorCode = NbpAction(ForRegister, serviceListenerInfo->socket,
|
|
object, type, zone,
|
|
GetNextNbpIdForNode(serviceListenerInfo->socket), 0, 0,
|
|
NbpRegisterComplete,
|
|
(long unsigned)completionInfo);
|
|
if (errorCode isnt ATnoError)
|
|
{
|
|
if (completionInfo->freeOpaqueResponse)
|
|
FreeOpaqueDataDescriptor(completionInfo->opaqueResponse);
|
|
Free(completionInfo);
|
|
}
|
|
UnlinkServiceListenerInfo(serviceListenerInfo);
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
return(errorCode);
|
|
|
|
} /* PapRegisterName */
|
|
|
|
AppleTalkErrorCode far PapRemoveName(
|
|
long serviceListenerRefNum, /* The service listener ref num of the
|
|
service listener that the name should
|
|
be removed from. */
|
|
char far *object, /* NBP object of the server. */
|
|
char far *type, /* NBP type of the server. */
|
|
char far *zone) /* NBP zone of the server, beter be empty
|
|
or "*". */
|
|
{
|
|
ServiceListenerInfo serviceListenerInfo;
|
|
AppleTalkErrorCode errorCode;
|
|
|
|
/* Find our service listener. */
|
|
|
|
DeferTimerChecking();
|
|
DeferAtpPackets();
|
|
serviceListenerInfo = FindServiceListenerInfoFor(serviceListenerRefNum);
|
|
if (serviceListenerInfo is empty or serviceListenerInfo->closing)
|
|
{
|
|
UnlinkServiceListenerInfo(serviceListenerInfo);
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
return(ATpapNoSuchServiceListener);
|
|
}
|
|
|
|
/* Do the NBP remove. */
|
|
|
|
errorCode = NbpRemove(serviceListenerInfo->socket, object,
|
|
type, zone);
|
|
UnlinkServiceListenerInfo(serviceListenerInfo);
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
return(errorCode);
|
|
|
|
} /* PapRemoveName */
|
|
|
|
AppleTalkErrorCode far PapHereIsStatus(
|
|
long serviceListenerRefNum, /* The server to set statrus for. */
|
|
void far *opaqueStatusBuffer, /* Actual status "buffer." */
|
|
int statusSize) /* Size of status string. */
|
|
{
|
|
ServiceListenerInfo serviceListenerInfo;
|
|
char far *statusBuffer = Empty, far *previousStatusBuffer = Empty;
|
|
|
|
if (statusSize < 0 or statusSize > PapMaximumStatusSize)
|
|
return(ATpapBadStatus);
|
|
DeferTimerChecking();
|
|
DeferAtpPackets();
|
|
serviceListenerInfo = FindServiceListenerInfoFor(serviceListenerRefNum);
|
|
if (serviceListenerInfo is empty or serviceListenerInfo->closing)
|
|
{
|
|
UnlinkServiceListenerInfo(serviceListenerInfo);
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
return(ATpapNoSuchServiceListener);
|
|
}
|
|
|
|
if (statusSize isnt 0)
|
|
if ((statusBuffer = (char *)Malloc(statusSize)) is empty)
|
|
{
|
|
UnlinkServiceListenerInfo(serviceListenerInfo);
|
|
ErrorLog("PapHereIsStatus", ISevError, __LINE__, UnknownPort,
|
|
IErrPapOutOfMemory, IMsgPapOutOfMemory,
|
|
Insert0());
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
return(AToutOfMemory);
|
|
}
|
|
|
|
/* Replace the previous status buffer. */
|
|
|
|
TakeLock(PapLock);
|
|
if (serviceListenerInfo->statusBuffer isnt empty)
|
|
previousStatusBuffer = serviceListenerInfo->statusBuffer;
|
|
|
|
/* Copy in the new status. */
|
|
|
|
if (statusSize isnt 0)
|
|
MoveFromOpaque(statusBuffer, opaqueStatusBuffer, 0, statusSize);
|
|
serviceListenerInfo->statusBuffer = statusBuffer;
|
|
serviceListenerInfo->statusSize = (short)statusSize;
|
|
ReleaseLock(PapLock);
|
|
|
|
if (previousStatusBuffer isnt Empty)
|
|
Free(previousStatusBuffer);
|
|
|
|
UnlinkServiceListenerInfo(serviceListenerInfo);
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
return(ATnoError);
|
|
|
|
} /* PapHereIsStatus */
|
|
|
|
AppleTalkErrorCode far PapGetStatus(
|
|
long jobRefNum, /* Ref num of a workstation job so we can
|
|
query its server side; if <0 not used. */
|
|
AppleTalkAddress far *serverAddress,
|
|
/* if jobRefNum <0, maybe this is the address
|
|
of the server. */
|
|
char far *object, /* if jobRefNum <0 and serverAddress is Empty;
|
|
NBP object of server. */
|
|
char far *type, /* if jobRefNum <0 and serverAddress is Empty;
|
|
NBP type of server. */
|
|
char far *zone, /* if jobRefNum <0 and serverAddress is Empty;
|
|
NBP zone of server. */
|
|
void far *opaqueStatusBuffer, /* Buffer to fill with status (at least 255
|
|
bytes). */
|
|
PapGetStatusComplete *completionRoutine,
|
|
/* Who to call when the call completes. */
|
|
long unsigned userData) /* User data to pass on to the completion
|
|
routine. */
|
|
{
|
|
StaticForSmallStack PapCompletionInfo completionInfo;
|
|
StaticForSmallStack char userBytes[AtpUserBytesSize];
|
|
StaticForSmallStack AppleTalkAddress destinationAddress;
|
|
ActiveJobInfo activeJobInfo = Empty;
|
|
AppleTalkErrorCode errorCode = ATnoError;
|
|
int port;
|
|
long socket = -1;
|
|
|
|
/* Get a server's status. Call a completion routine with the following
|
|
arguments when status arrives:
|
|
|
|
errorCode - AppleTalkErrorCode; completion code.
|
|
userData - long unsigned; as passed to us.
|
|
opaqueStatusBuffer
|
|
- void *; as passed to us.
|
|
statusSize - int; actual length of status buffer.
|
|
*/
|
|
|
|
if ((port = FindDefaultPort()) < 0)
|
|
return(ATappleTalkShutDown);
|
|
|
|
/* We may need to post an NBP lookup for the SL... make sure we have a
|
|
node. */
|
|
|
|
if (PortDescriptor(port)->activeNodes is empty)
|
|
if ((errorCode = GetNodeOnPort(port, True, True, False, empty))
|
|
isnt ATnoError)
|
|
return(errorCode);
|
|
|
|
DeferTimerChecking();
|
|
DeferAtpPackets();
|
|
if (jobRefNum >= 0)
|
|
{
|
|
if ((activeJobInfo = FindActiveJobInfoFor(jobRefNum)) is empty or
|
|
activeJobInfo->closing)
|
|
errorCode = ATpapNoSuchJob;
|
|
else if (activeJobInfo->serverJob)
|
|
errorCode = ATpapNotWorkstationJob;
|
|
if (errorCode isnt ATnoError)
|
|
{
|
|
UnlinkActiveJobInfo(activeJobInfo);
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
return(errorCode);
|
|
}
|
|
}
|
|
|
|
/* Well, it looks like we'll need to make either an NBP or ATP call, so
|
|
build up the data block that we'll need for completion handling.
|
|
The max size we can get back is a staus buffer, allocate that too,
|
|
and build and "opaque" descriptor for it, so we can pass it down
|
|
to Atp. */
|
|
|
|
completionInfo = (PapCompletionInfo)Calloc(sizeof(*completionInfo) +
|
|
PapMaximumDataPacketSize, 1);
|
|
if (completionInfo is empty or
|
|
(completionInfo->opaqueResponse =
|
|
MakeOpaqueDataDescriptor(completionInfo->responseBuffer,
|
|
PapMaximumDataPacketSize,
|
|
&completionInfo->freeOpaqueResponse)) is Empty)
|
|
{
|
|
UnlinkActiveJobInfo(activeJobInfo);
|
|
if (completionInfo isnt Empty)
|
|
Free(completionInfo);
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
return(AToutOfMemory);
|
|
}
|
|
completionInfo->completionRoutine = (void *)completionRoutine;
|
|
completionInfo->userData = userData;
|
|
completionInfo->lookupForStatus = True;
|
|
completionInfo->usersOpaqueStatusBuffer = opaqueStatusBuffer;
|
|
|
|
/* If we know the address of the SL, just send the ATP request. If we
|
|
have a jobRefNum, this is easy, we have an Atp socket to post the
|
|
request from. If we just have the server's address, it is a little
|
|
harder, we don't have any particularly good socket (let alone an ATP
|
|
socket) to post the GetRequest for status on. For lack of any better
|
|
idea, open an Atp socket on the default port. */
|
|
|
|
if (jobRefNum >= 0)
|
|
{
|
|
socket = activeJobInfo->ourSocket;
|
|
destinationAddress = activeJobInfo->serviceListenerAddress;
|
|
UnlinkActiveJobInfo(activeJobInfo);
|
|
}
|
|
else if (serverAddress isnt Empty)
|
|
{
|
|
destinationAddress = *serverAddress;
|
|
if ((errorCode = AtpOpenSocketOnNode(&socket, -1, empty, 0, empty, 0))
|
|
isnt ATnoError)
|
|
{
|
|
if (completionInfo->freeOpaqueResponse)
|
|
FreeOpaqueDataDescriptor(completionInfo->opaqueResponse);
|
|
Free(completionInfo);
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
return(errorCode);
|
|
}
|
|
completionInfo->socket = socket;
|
|
completionInfo->mustCloseAtpSocket = True; /* !!! */
|
|
}
|
|
|
|
if (socket >= 0)
|
|
{
|
|
userBytes[PapConnectionIdOffset] = 0;
|
|
userBytes[PapCommandTypeOffset] = PapSendStatusCommand;
|
|
userBytes[PapSequenceNumberOffset] = 0;
|
|
userBytes[PapSequenceNumberOffset + 1] = 0;
|
|
errorCode = AtpPostRequest(socket,
|
|
destinationAddress,
|
|
Empty,
|
|
empty, 0, userBytes,
|
|
True, completionInfo->opaqueResponse,
|
|
PapMaximumDataPacketSize,
|
|
completionInfo->responseUserBytes,
|
|
PapGetStatusRequestRetryCount,
|
|
PapGetStatusAtpRetrySeconds,
|
|
ThirtySecondsTRelTimer,
|
|
IncomingStatus,
|
|
(long unsigned)completionInfo);
|
|
if (errorCode isnt ATnoError)
|
|
{
|
|
if (completionInfo->freeOpaqueResponse)
|
|
FreeOpaqueDataDescriptor(completionInfo->opaqueResponse);
|
|
Free(completionInfo);
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
return(errorCode);
|
|
}
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
return(ATnoError);
|
|
}
|
|
|
|
/* We don't know the SL's address, so post an NBP lookup to find it!
|
|
We don't really have a particularly good address to post the lookup
|
|
from, so use the NIS of a node on our default port. */
|
|
|
|
errorCode = ATinternalError;
|
|
if ((socket = MapNisOnPortToSocket(port)) < 0 or
|
|
(errorCode = NbpAction(ForLookup, socket, object, type,
|
|
zone, GetNextNbpIdForNode(socket), 0, 0,
|
|
NbpLookupComplete, (long unsigned)completionInfo,
|
|
completionInfo->opaqueResponse,
|
|
MaximumNbpTupleLength, 1)) isnt ATnoError)
|
|
{
|
|
if (socket < 0)
|
|
ErrorLog("PapGetStatus", ISevError, __LINE__, UnknownPort,
|
|
IErrPapBadSocketMake, IMsgPapBadSocketMake,
|
|
Insert0());
|
|
if (completionInfo->freeOpaqueResponse)
|
|
FreeOpaqueDataDescriptor(completionInfo->opaqueResponse);
|
|
Free(completionInfo);
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
return(errorCode);
|
|
}
|
|
|
|
/* All set. */
|
|
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
return(ATnoError);
|
|
|
|
} /* PapGetStatus */
|
|
|
|
AppleTalkErrorCode far PapRead(
|
|
long jobRefNum, /* The job we're reading from. */
|
|
void far *opaqueBuffer, /* The "buffer" to fill. */
|
|
long bufferSize, /* How big a buffer... better be able to
|
|
handle a full flow quantum. */
|
|
PapReadComplete *completionRoutine,
|
|
/* Routine to call when the read
|
|
completes. */
|
|
long unsigned userData) /* Data passed to the above. */
|
|
{
|
|
StaticForSmallStack ActiveJobInfo activeJobInfo;
|
|
StaticForSmallStack char userBytes[AtpUserBytesSize];
|
|
StaticForSmallStack PapCompletionInfo completionInfo;
|
|
AppleTalkErrorCode errorCode;
|
|
|
|
/* Post a PAP read (send data). Call a supplied completion routine when
|
|
the read completes:
|
|
|
|
errorCode - AppleTalkErrorCode; completion code.
|
|
userData - long unsigned; as passed to this routine.
|
|
jobRefNum - long; as passed to this routine.
|
|
opaqueBuffer - void *; buffer full of data; as passed to this routine.
|
|
bufferLength - int; actual number of recieved bytes.
|
|
eofFlag - Boolean; did the other side signal EOF?
|
|
*/
|
|
|
|
/* Do some error checking. */
|
|
|
|
DeferTimerChecking();
|
|
DeferAtpPackets();
|
|
activeJobInfo = FindActiveJobInfoFor(jobRefNum);
|
|
TakeLock(PapLock);
|
|
if (activeJobInfo is empty or
|
|
activeJobInfo->closing or
|
|
activeJobInfo->outgoingSendDataPending)
|
|
{
|
|
ReleaseLock(PapLock);
|
|
if (activeJobInfo is empty or activeJobInfo->closing)
|
|
errorCode = ATpapNoSuchJob;
|
|
else
|
|
errorCode = ATpapReadAlreadyPending;
|
|
UnlinkActiveJobInfo(activeJobInfo);
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
return(errorCode);
|
|
}
|
|
activeJobInfo->outgoingSendDataPending = True;
|
|
ReleaseLock(PapLock);
|
|
|
|
#if IamNot a WindowsNT
|
|
if (bufferSize < activeJobInfo->receiveFlowQuantum *
|
|
PapMaximumDataPacketSize)
|
|
#else
|
|
if (bufferSize < 0)
|
|
#endif
|
|
{
|
|
activeJobInfo->outgoingSendDataPending = False;
|
|
UnlinkActiveJobInfo(activeJobInfo);
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
return(ATpapReadBufferTooSmall);
|
|
}
|
|
|
|
/* Okay, build the completion info that we'll need to handle the completion
|
|
of this transaction. */
|
|
|
|
completionInfo = (PapCompletionInfo)Calloc(sizeof(*completionInfo), 1);
|
|
if (completionInfo is empty)
|
|
{
|
|
activeJobInfo->outgoingSendDataPending = False;
|
|
UnlinkActiveJobInfo(activeJobInfo);
|
|
ErrorLog("PapRead", ISevError, __LINE__, UnknownPort,
|
|
IErrPapOutOfMemory, IMsgPapOutOfMemory,
|
|
Insert0());
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
return(AToutOfMemory);
|
|
}
|
|
completionInfo->completionRoutine = (void *)completionRoutine;
|
|
completionInfo->userData = userData;
|
|
completionInfo->jobRefNum = jobRefNum;
|
|
|
|
/* Build up userBytes to post the "sendData" to the other side. */
|
|
|
|
userBytes[PapConnectionIdOffset] = activeJobInfo->connectionId;
|
|
userBytes[PapCommandTypeOffset] = PapSendDataCommand;
|
|
if (activeJobInfo->lastOutgoingSequenceNumber is (short unsigned)0xFFFF)
|
|
activeJobInfo->lastOutgoingSequenceNumber = 0;
|
|
activeJobInfo->lastOutgoingSequenceNumber += 1;
|
|
userBytes[PapSequenceNumberOffset] =
|
|
(char)(activeJobInfo->lastOutgoingSequenceNumber >> 8);
|
|
userBytes[PapSequenceNumberOffset + 1] =
|
|
(char)(activeJobInfo->lastOutgoingSequenceNumber & 0xFF);
|
|
|
|
/* Post the SendData request. */
|
|
|
|
errorCode = AtpPostRequest(activeJobInfo->ourSocket,
|
|
activeJobInfo->theirAddress,
|
|
Empty,
|
|
empty, 0, userBytes, True, opaqueBuffer,
|
|
#if IamNot a WindowsNT
|
|
activeJobInfo->receiveFlowQuantum *
|
|
PapMaximumDataPacketSize,
|
|
#else
|
|
bufferSize,
|
|
#endif
|
|
activeJobInfo->outgoingSendDataUserBytes,
|
|
AtpInfiniteRetries, PapSendDataRequestRetrySeconds,
|
|
ThirtySecondsTRelTimer,
|
|
IncomingReadComplete,
|
|
(long unsigned)completionInfo);
|
|
if (errorCode isnt ATnoError)
|
|
{
|
|
activeJobInfo->outgoingSendDataPending = False;
|
|
UnlinkActiveJobInfo(activeJobInfo);
|
|
ErrorLog("PapRead", ISevError, __LINE__, UnknownPort,
|
|
IErrPapBadSendDataSend, IMsgPapBadSendDataSend,
|
|
Insert0());
|
|
if (completionInfo->freeOpaqueResponse)
|
|
FreeOpaqueDataDescriptor(completionInfo->opaqueResponse);
|
|
Free(completionInfo);
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
return(errorCode);
|
|
}
|
|
|
|
/* We've got one going now! */
|
|
|
|
UnlinkActiveJobInfo(activeJobInfo);
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
return(ATnoError);
|
|
|
|
} /* PapRead */
|
|
|
|
AppleTalkErrorCode far PapWrite(
|
|
long jobRefNum, /* The job we're writting from. */
|
|
void far *opaqueBuffer, /* The "buffer" to write from. */
|
|
long bufferSize, /* Number of bytes to write. */
|
|
Boolean eofFlag, /* End of xfer flag. */
|
|
PapWriteComplete *completionRoutine,
|
|
/* Routine to call when the write
|
|
completes. */
|
|
long unsigned userData) /* Data passed to the above. */
|
|
{
|
|
ActiveJobInfo activeJobInfo;
|
|
AppleTalkErrorCode errorCode;
|
|
|
|
/* Post a PAP write (handle a send data). Call a supplied completion
|
|
routine when the read completes:
|
|
|
|
errorCode - AppleTalkErrorCode; completion code.
|
|
userData - long unsigned; as passed to this routine.
|
|
jobRefNum - long; as passed to this routine.
|
|
*/
|
|
|
|
/* Do some error checking. */
|
|
|
|
DeferTimerChecking();
|
|
DeferAtpPackets();
|
|
activeJobInfo = FindActiveJobInfoFor(jobRefNum);
|
|
TakeLock(PapLock);
|
|
if (activeJobInfo is empty or
|
|
activeJobInfo->closing or
|
|
activeJobInfo->writeDataWaiting or
|
|
bufferSize > activeJobInfo->sendFlowQuantum * PapMaximumDataPacketSize)
|
|
{
|
|
ReleaseLock(PapLock);
|
|
if (activeJobInfo is empty or activeJobInfo->closing)
|
|
errorCode = ATpapNoSuchJob;
|
|
else if (activeJobInfo->writeDataWaiting)
|
|
errorCode = ATpapWriteAlreadyPending;
|
|
else
|
|
errorCode = ATpapWriteTooBig;
|
|
UnlinkActiveJobInfo(activeJobInfo);
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
return(errorCode);
|
|
}
|
|
activeJobInfo->writeDataWaiting = True;
|
|
ReleaseLock(PapLock);
|
|
|
|
/* Place our write request onto the active job structure. */
|
|
|
|
activeJobInfo->writeOpaqueBuffer = opaqueBuffer;
|
|
activeJobInfo->writeBufferSize = bufferSize;
|
|
activeJobInfo->writeEofFlag = eofFlag;
|
|
activeJobInfo->writeCompletionRoutine = completionRoutine;
|
|
activeJobInfo->writeUserData = userData;
|
|
|
|
/* We're all set if we don't have a pending "sendData" from the other
|
|
side. */
|
|
|
|
if (not activeJobInfo->incomingSendDataPending)
|
|
{
|
|
UnlinkActiveJobInfo(activeJobInfo);
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
return(ATnoError);
|
|
}
|
|
|
|
/* Otherwise, the other side is already waiting for a response, so send it
|
|
off... */
|
|
|
|
PostSendDataResponse(activeJobInfo);
|
|
UnlinkActiveJobInfo(activeJobInfo);
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
return(ATnoError);
|
|
|
|
} /* PapWrite */
|
|
|
|
Boolean far PapSendCreditAvailable(
|
|
long jobRefNum) /* The job we're checking. */
|
|
{
|
|
ActiveJobInfo activeJobInfo;
|
|
Boolean sendCreditAvailable;
|
|
|
|
/* Do some error checking. */
|
|
|
|
DeferTimerChecking();
|
|
DeferAtpPackets();
|
|
|
|
activeJobInfo = FindActiveJobInfoFor(jobRefNum);
|
|
sendCreditAvailable = ((activeJobInfo isnt Empty) and
|
|
not activeJobInfo->writeDataWaiting and
|
|
activeJobInfo->incomingSendDataPending);
|
|
|
|
UnlinkActiveJobInfo(activeJobInfo);
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
|
|
return(sendCreditAvailable);
|
|
|
|
} /* PapSendCreditAvailable */
|
|
|
|
void far ShutdownPap(void)
|
|
{
|
|
OpenJobInfo openJobInfo, nextOpenJobInfo;
|
|
|
|
/* Most of Pap is shutdown bottom-up (closing sockets, canceling timers)
|
|
but we may have a something left on the OpenJob list, free that here. */
|
|
|
|
TakeLock(PapLock);
|
|
openJobInfo = Link(openJobInfoHead);
|
|
openJobInfoHead = Empty;
|
|
ReleaseLock(PapLock);
|
|
while (openJobInfo isnt Empty)
|
|
{
|
|
TakeLock(PapLock);
|
|
nextOpenJobInfo = Link(openJobInfo->next);
|
|
ReleaseLock(PapLock);
|
|
if (not UnlinkOpenJobInfo(openJobInfo))
|
|
UnlinkOpenJobInfo(openJobInfo);
|
|
openJobInfo = nextOpenJobInfo;
|
|
}
|
|
return;
|
|
|
|
} /* ShutdownPap */
|
|
|
|
ExternForVisibleFunction void far
|
|
IncomingServiceListenerPacket(AppleTalkErrorCode errorCode,
|
|
long unsigned userData,
|
|
AppleTalkAddress source,
|
|
void far *buffer,
|
|
/* really a "char *" */
|
|
int bufferSize,
|
|
char far *incomingUserBytes,
|
|
Boolean exactlyOnce,
|
|
TRelTimerValue trelTimerValue,
|
|
short unsigned transactionId,
|
|
short unsigned bitmap)
|
|
{
|
|
StaticForSmallStack ServiceListenerInfo serviceListenerInfo;
|
|
StaticForSmallStack short commandType, connectionId;
|
|
StaticForSmallStack char userBytes[AtpUserBytesSize];
|
|
StaticForSmallStack Boolean error;
|
|
StaticForSmallStack long serverSocket, workstationSocket;
|
|
StaticForSmallStack ActiveJobInfo activeJobInfo;
|
|
StaticForSmallStack GetNextJobInfo getNextJobInfo;
|
|
StaticForSmallStack SlsResponseCompletionCookie cookie;
|
|
long index;
|
|
long serviceListenerRefNum = (long)userData;
|
|
StartJobHandler *startJobRoutine;
|
|
short waitTime, workstationQuantum;
|
|
long jobRefNum;
|
|
long unsigned startJobUserData;
|
|
Boolean needToUndefer = True;
|
|
AppleTalkAddress serverAddress;
|
|
Boolean indication = False;
|
|
|
|
/* Touch unused formal... */
|
|
|
|
bitmap, trelTimerValue;
|
|
|
|
error = False;
|
|
|
|
if (errorCode is ATatpTransactionAborted or errorCode is ATsocketClosed)
|
|
return; /* Service listener closed... */
|
|
|
|
/* Is service listener still with us? */
|
|
|
|
DeferTimerChecking();
|
|
DeferAtpPackets();
|
|
serviceListenerInfo = FindServiceListenerInfoFor(serviceListenerRefNum);
|
|
if (serviceListenerInfo is empty or serviceListenerInfo->closing)
|
|
{
|
|
UnlinkServiceListenerInfo(serviceListenerInfo);
|
|
ErrorLog("IncomingServiceListenerPacket", ISevWarning, __LINE__,
|
|
UnknownPort, IErrPapSLGonePoof, IMsgPapSLGonePoof,
|
|
Insert0());
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
return;
|
|
}
|
|
|
|
/* For other AppleTalk errors we could try to re-enqueue the request handler,
|
|
but more than likely the error would happen again (and quickly) thus
|
|
hanging the driver... */
|
|
|
|
if (errorCode isnt ATnoError and errorCode isnt ATatpResponseBufferTooSmall)
|
|
{
|
|
UnlinkServiceListenerInfo(serviceListenerInfo);
|
|
ErrorLog("IncomingServiceListenerPacket", ISevError, __LINE__,
|
|
UnknownPort, IErrPapLostSLPacket, IMsgPapLostSLPacket,
|
|
Insert0());
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
return;
|
|
}
|
|
|
|
/* Pull the needed fields out of the UserBytes. */
|
|
|
|
connectionId = (unsigned char)incomingUserBytes[PapConnectionIdOffset];
|
|
commandType = (unsigned char)incomingUserBytes[PapCommandTypeOffset];
|
|
switch(commandType)
|
|
{
|
|
case PapOpenConnectionCommand:
|
|
|
|
/* First get the buffer to send the response with, if we can't
|
|
get that, run away! */
|
|
|
|
if ((cookie = NewSlsResponseCompletionCookie()) is Empty)
|
|
break;
|
|
|
|
/* Start trying to set up a new connection. */
|
|
|
|
TakeLock(PapLock);
|
|
if (bufferSize < PapStatusOffset or
|
|
serviceListenerInfo->serverState isnt PapUnblockedState)
|
|
error = True;
|
|
|
|
/* If PapUnblockedState - either there is a GetNextJob posted, or
|
|
an incoming event handler is set on the listener */
|
|
|
|
if ((getNextJobInfo = serviceListenerInfo->getNextJobList) is Empty)
|
|
{
|
|
/* Grab our indication info. */
|
|
|
|
startJobRoutine = serviceListenerInfo->startJobRoutine;
|
|
startJobUserData = serviceListenerInfo->startJobUserData;
|
|
indication = True;
|
|
}
|
|
else
|
|
{
|
|
/* Grab the GetNextJob info, and unthread him. */
|
|
|
|
startJobRoutine = getNextJobInfo->startJobRoutine;
|
|
startJobUserData = getNextJobInfo->startJobUserData;
|
|
serviceListenerInfo->getNextJobList = getNextJobInfo->next;
|
|
if (serviceListenerInfo->getNextJobList is empty)
|
|
serviceListenerInfo->serverState = PapBlockedState;
|
|
}
|
|
ReleaseLock(PapLock);
|
|
|
|
if (not error)
|
|
{
|
|
/* Try to open our responding socket. */
|
|
|
|
if (AtpOpenSocketOnNode(&serverSocket, serviceListenerInfo->port,
|
|
empty, 0, empty, 0) isnt ATnoError)
|
|
error = True;
|
|
else if (MapSocketToAddress(serverSocket, &serverAddress)
|
|
isnt ATnoError)
|
|
{
|
|
ErrorLog("IncomingServiceListenerPacket", ISevError, __LINE__,
|
|
UnknownPort, IErrPapBadSrvrAddrMap, IMsgPapBadSrvrAddrMap,
|
|
Insert0());
|
|
AtpCloseSocketOnNode(serverSocket, Empty, (long unsigned)0);
|
|
error = True;
|
|
}
|
|
|
|
}
|
|
|
|
if (not error)
|
|
{
|
|
/* Allocate a new active job node. */
|
|
|
|
activeJobInfo = (ActiveJobInfo)Calloc(sizeof(*activeJobInfo), 1);
|
|
if (activeJobInfo is empty)
|
|
{
|
|
ErrorLog("IncomingServiceListenerPacket", ISevError, __LINE__,
|
|
UnknownPort, IErrPapOutOfMemory, IMsgPapOutOfMemory,
|
|
Insert0());
|
|
AtpCloseSocketOnNode(serverSocket, Empty, (long unsigned)0);
|
|
error = True;
|
|
}
|
|
}
|
|
|
|
/* Okay, we should have all of our resources now... return an error
|
|
if we couldn't get any of them. */
|
|
|
|
if (error)
|
|
{
|
|
userBytes[PapConnectionIdOffset] = (char)connectionId;
|
|
userBytes[PapCommandTypeOffset] = PapOpenConnectionReplyCommand;
|
|
userBytes[PapSequenceNumberOffset] = 0;
|
|
userBytes[PapSequenceNumberOffset + 1] = 0;
|
|
cookie->papData[PapRespondingSocketOffset] = 0;
|
|
cookie->papData[PapFlowQuantumOffset] =
|
|
(char)serviceListenerInfo->serverFlowQuantum;
|
|
cookie->papData[PapResultOffset] = (char)(PapPrinterBusy >> 8);
|
|
cookie->papData[PapResultOffset + 1] = (char)(PapPrinterBusy & 0xFF);
|
|
TakeLock(PapLock);
|
|
cookie->papData[PapStatusOffset] =
|
|
(char)serviceListenerInfo->statusSize;
|
|
if (serviceListenerInfo->statusSize isnt 0)
|
|
MoveMem(cookie->papData + PapStatusOffset + 1,
|
|
serviceListenerInfo->statusBuffer,
|
|
serviceListenerInfo->statusSize);
|
|
|
|
/* Put an unused GetNextJob back... */
|
|
|
|
if (getNextJobInfo isnt Empty)
|
|
{
|
|
if (serviceListenerInfo->closing)
|
|
{
|
|
ReleaseLock(PapLock);
|
|
Free(getNextJobInfo);
|
|
}
|
|
else
|
|
{
|
|
getNextJobInfo->next = serviceListenerInfo->getNextJobList;
|
|
serviceListenerInfo->getNextJobList = getNextJobInfo;
|
|
serviceListenerInfo->serverState = PapUnblockedState;
|
|
ReleaseLock(PapLock);
|
|
}
|
|
}
|
|
else
|
|
ReleaseLock(PapLock);
|
|
errorCode = AtpPostResponse(serviceListenerInfo->socket,
|
|
source, transactionId,
|
|
cookie->opaquePapData,
|
|
PapStatusOffset + 1 +
|
|
serviceListenerInfo->statusSize,
|
|
userBytes, exactlyOnce,
|
|
SlsIncomingRelease,
|
|
(long unsigned)cookie);
|
|
if (errorCode isnt ATnoError)
|
|
{
|
|
ErrorLog("IncomingServiceListenerPacket", ISevError, __LINE__,
|
|
UnknownPort, IErrPapBadPrinterBusySend, IMsgPapBadPrinterBusySend,
|
|
Insert0());
|
|
FreeSlsResponseCompletionCookie(cookie);
|
|
}
|
|
break;
|
|
}
|
|
|
|
|
|
/* Okay, start filling in our new active job structure. */
|
|
|
|
workstationSocket =
|
|
(unsigned char)((char *)buffer)[PapRespondingSocketOffset];
|
|
|
|
/* If we are indicating this, the jobRefNum needs to be generated
|
|
now. */
|
|
|
|
if (indication)
|
|
{
|
|
activeJobInfo->jobRefNum = jobRefNum = GetNextJobRefNum();
|
|
if (jobRefNum < 0)
|
|
{
|
|
/* Free the activeJobInfo structure; release socket and flee */
|
|
AtpCloseSocketOnNode(serverSocket, Empty, (long unsigned)0);
|
|
Free(activeJobInfo);
|
|
break;
|
|
}
|
|
|
|
}
|
|
else
|
|
{
|
|
/* Otherwise, we're completing a GetNextJob... */
|
|
|
|
activeJobInfo->jobRefNum = jobRefNum = getNextJobInfo->jobRefNum;
|
|
activeJobInfo->closeJobRoutine = getNextJobInfo->closeJobRoutine;
|
|
activeJobInfo->closeJobUserData = getNextJobInfo->closeJobUserData;
|
|
|
|
/* Now we can free the GetNextJob node. */
|
|
|
|
Free(getNextJobInfo);
|
|
}
|
|
|
|
activeJobInfo->serverJob = True;
|
|
activeJobInfo->ourPort = serviceListenerInfo->port;
|
|
activeJobInfo->ourSocket = serverSocket;
|
|
activeJobInfo->closeOurSocket = True;
|
|
activeJobInfo->theirAddress = source;
|
|
activeJobInfo->theirAddress.socketNumber = (char)workstationSocket;
|
|
if (MapSocketToAddress(serviceListenerInfo->socket,
|
|
&activeJobInfo->serviceListenerAddress)
|
|
isnt ATnoError)
|
|
ErrorLog("IncomingServiceListenerPacket", ISevError, __LINE__,
|
|
UnknownPort, IErrPapBadSLSAddress, IMsgPapBadSLSAddress,
|
|
Insert0());
|
|
activeJobInfo->connectionId = (unsigned char)connectionId;
|
|
activeJobInfo->receiveFlowQuantum =
|
|
serviceListenerInfo->serverFlowQuantum;
|
|
workstationQuantum =
|
|
(unsigned char)((char *)buffer)[PapFlowQuantumOffset];
|
|
if (workstationQuantum > PapMaximumFlowQuantum)
|
|
workstationQuantum = PapMaximumFlowQuantum;
|
|
activeJobInfo->sendFlowQuantum = workstationQuantum;
|
|
|
|
/* Thead the new active job into the two lookup structures. */
|
|
|
|
CheckMod(index, jobRefNum, NumberOfActiveJobHashBuckets,
|
|
"IncomingServiceListenerPacket");
|
|
TakeLock(PapLock);
|
|
if (serviceListenerInfo->closing)
|
|
{
|
|
ReleaseLock(PapLock);
|
|
AtpCloseSocketOnNode(serverSocket, Empty, (long unsigned)0);
|
|
Free(activeJobInfo);
|
|
break;
|
|
}
|
|
activeJobInfo->next = activeJobHashBuckets[index];
|
|
activeJobHashBuckets[index] = Link(activeJobInfo);
|
|
|
|
activeJobInfo->nextForMyServiceListener =
|
|
serviceListenerInfo->activeJobList;
|
|
serviceListenerInfo->activeJobList = activeJobInfo;
|
|
activeJobInfo->ourServiceListener = Link(serviceListenerInfo);
|
|
ReleaseLock(PapLock);
|
|
|
|
/* Set the ATP data packet size correctly for PAP. */
|
|
|
|
if (AtpMaximumSinglePacketDataSize(activeJobInfo->ourSocket,
|
|
PapMaximumDataPacketSize)
|
|
isnt ATnoError)
|
|
ErrorLog("IncomingServiceListenerPacket", ISevError, __LINE__,
|
|
UnknownPort, IErrPapBadSetSize, IMsgPapBadSetSize,
|
|
Insert0());
|
|
|
|
/* Get the last of the usefull information out of the OpenConn
|
|
buffer; the wait time. */
|
|
|
|
waitTime = (short)(((char *)buffer)[PapWaitTimeOffset] << 8);
|
|
waitTime += (unsigned char)((char *)buffer)[PapWaitTimeOffset + 1];
|
|
|
|
/* Build up and send the open reply. */
|
|
|
|
userBytes[PapConnectionIdOffset] = (char)connectionId;
|
|
userBytes[PapCommandTypeOffset] = PapOpenConnectionReplyCommand;
|
|
userBytes[PapSequenceNumberOffset] = 0;
|
|
userBytes[PapSequenceNumberOffset + 1] = 0;
|
|
cookie->papData[PapRespondingSocketOffset] = serverAddress.socketNumber;
|
|
cookie->papData[PapFlowQuantumOffset] =
|
|
(char)activeJobInfo->receiveFlowQuantum;
|
|
cookie->papData[PapResultOffset] = (char)(PapNoError >> 8);
|
|
cookie->papData[PapResultOffset + 1] = (char)(PapNoError & 0xFF);
|
|
TakeLock(PapLock);
|
|
cookie->papData[PapStatusOffset] =
|
|
(char)serviceListenerInfo->statusSize;
|
|
if (serviceListenerInfo->statusSize isnt 0)
|
|
MoveMem(cookie->papData + PapStatusOffset + 1,
|
|
serviceListenerInfo->statusBuffer,
|
|
serviceListenerInfo->statusSize);
|
|
ReleaseLock(PapLock);
|
|
errorCode = AtpPostResponse(serviceListenerInfo->socket,
|
|
source, transactionId,
|
|
cookie->opaquePapData,
|
|
PapStatusOffset + 1 +
|
|
serviceListenerInfo->statusSize,
|
|
userBytes, exactlyOnce,
|
|
SlsIncomingRelease,
|
|
(long unsigned)cookie);
|
|
if (errorCode isnt ATnoError)
|
|
{
|
|
ErrorLog("IncomingServiceListenerPacket", ISevError, __LINE__,
|
|
UnknownPort, IErrPapBadOkaySend, IMsgPapBadOkaySend,
|
|
Insert0());
|
|
FreeSlsResponseCompletionCookie(cookie);
|
|
}
|
|
|
|
/* Build up userBytes to start tickling the other end. */
|
|
|
|
userBytes[PapConnectionIdOffset] = (char)connectionId;
|
|
userBytes[PapCommandTypeOffset] = PapTickleCommand;
|
|
userBytes[PapSequenceNumberOffset] = 0;
|
|
userBytes[PapSequenceNumberOffset + 1] = 0;
|
|
if (AtpPostRequest(activeJobInfo->ourSocket,
|
|
activeJobInfo->theirAddress,
|
|
Empty,
|
|
empty, 0, userBytes, False, empty, 0, empty,
|
|
AtpInfiniteRetries, PapTickleSeconds,
|
|
ThirtySecondsTRelTimer, empty,
|
|
(long unsigned)0) isnt ATnoError)
|
|
ErrorLog("IncomingServiceListenerPacket", ISevError, __LINE__,
|
|
UnknownPort, IErrPapBadTickleStart, IMsgPapBadTickleStart,
|
|
Insert0());
|
|
|
|
/* Post a few get request handlers on this connection (for incoming
|
|
tickle's, close's, or sendData's). */
|
|
|
|
for (index = 0; index < PapPendingReadsPerJob; index += 1)
|
|
{
|
|
if (AtpEnqueueRequestHandler(empty, activeJobInfo->ourSocket,
|
|
empty, 0, empty,
|
|
IncomingRequestPacket,
|
|
(long unsigned)activeJobInfo->jobRefNum)
|
|
isnt ATnoError)
|
|
ErrorLog("IncomingServiceListenerPacket", ISevError, __LINE__,
|
|
UnknownPort, IErrPapBadRequestHandler, IMsgPapBadRequestHandler,
|
|
Insert0());
|
|
}
|
|
|
|
/* Start the connection timer. */
|
|
|
|
activeJobInfo->lastContactTime = CurrentRelativeTime();
|
|
activeJobInfo->connectionTimerId =
|
|
StartTimer(ConnectionTimerExpired, PapConnectionSeconds,
|
|
sizeof(long), (char *)&activeJobInfo->jobRefNum);
|
|
|
|
/* Call our completion routine. */
|
|
|
|
needToUndefer = False;
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
if (startJobRoutine isnt empty)
|
|
(*startJobRoutine)(errorCode, startJobUserData, jobRefNum,
|
|
workstationQuantum, waitTime);
|
|
|
|
break;
|
|
|
|
case PapSendStatusCommand:
|
|
|
|
/* Get the buffering for a Atp response... run away if we fail. */
|
|
|
|
if ((cookie = NewSlsResponseCompletionCookie()) is Empty)
|
|
break;
|
|
|
|
/* Reply with our current status. */
|
|
|
|
userBytes[PapConnectionIdOffset] = 0;
|
|
userBytes[PapCommandTypeOffset] = PapStatusReplyCommand;
|
|
userBytes[PapSequenceNumberOffset] = 0;
|
|
userBytes[PapSequenceNumberOffset + 1] = 0;
|
|
TakeLock(PapLock);
|
|
cookie->papData[PapStatusOffset] =
|
|
(char)serviceListenerInfo->statusSize;
|
|
if (serviceListenerInfo->statusSize isnt 0)
|
|
MoveMem(cookie->papData + PapStatusOffset + 1,
|
|
serviceListenerInfo->statusBuffer,
|
|
serviceListenerInfo->statusSize);
|
|
ReleaseLock(PapLock);
|
|
errorCode = AtpPostResponse(serviceListenerInfo->socket,
|
|
source, transactionId,
|
|
cookie->opaquePapData,
|
|
PapStatusOffset + 1 +
|
|
serviceListenerInfo->statusSize,
|
|
userBytes, exactlyOnce,
|
|
SlsIncomingRelease,
|
|
(long unsigned)cookie);
|
|
if (errorCode isnt ATnoError)
|
|
{
|
|
ErrorLog("IncomingServiceListenerPacket", ISevError, __LINE__,
|
|
UnknownPort, IErrPapBadSendStatSend, IMsgPapBadSendStatSend,
|
|
Insert0());
|
|
FreeSlsResponseCompletionCookie(cookie);
|
|
}
|
|
break;
|
|
|
|
default:
|
|
ErrorLog("IncomingServiceListenerPacket", ISevWarning, __LINE__,
|
|
UnknownPort, IErrPapBadCommand, IMsgPapBadCommand,
|
|
Insert0());
|
|
}
|
|
|
|
/* Re-enqueue the GetRequest handler. */
|
|
|
|
if (AtpEnqueueRequestHandler(empty, serviceListenerInfo->socket,
|
|
empty, 0, empty,
|
|
IncomingServiceListenerPacket,
|
|
(long unsigned)serviceListenerRefNum)
|
|
isnt ATnoError)
|
|
ErrorLog("IncomingServiceListenerPacket", ISevError, __LINE__, UnknownPort,
|
|
IErrPapBadReEnqueue, IMsgPapBadReEnqueue,
|
|
Insert0());
|
|
if (needToUndefer)
|
|
{
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
}
|
|
UnlinkServiceListenerInfo(serviceListenerInfo);
|
|
return;
|
|
|
|
} /* IncomingServiceListenerPacket */
|
|
|
|
ExternForVisibleFunction void far
|
|
NbpRegisterComplete(AppleTalkErrorCode errorCode,
|
|
long unsigned incomingUserData,
|
|
int reason,
|
|
long onWhosBehalf,
|
|
int nbpId,
|
|
...)
|
|
{
|
|
PapCompletionInfo completionInfo = (PapCompletionInfo)incomingUserData;
|
|
ServiceListenerInfo serviceListenerInfo;
|
|
Boolean initialRegister;
|
|
PapNbpRegisterComplete *completionRoutine;
|
|
long serviceListenerRefNum;
|
|
long unsigned userData;
|
|
|
|
/* Touch unused formals... */
|
|
|
|
reason, onWhosBehalf, nbpId;
|
|
|
|
/* Pull the information out of the completion info block then free it. */
|
|
|
|
DeferTimerChecking();
|
|
DeferAtpPackets();
|
|
serviceListenerRefNum = completionInfo->serviceListenerRefNum;
|
|
initialRegister = completionInfo->initialRegister;
|
|
completionRoutine =
|
|
(PapNbpRegisterComplete *)completionInfo->completionRoutine;
|
|
userData = completionInfo->userData;
|
|
if (completionInfo->freeOpaqueResponse)
|
|
FreeOpaqueDataDescriptor(completionInfo->opaqueResponse);
|
|
Free(completionInfo);
|
|
|
|
/* If this was the initial register (due to a call to
|
|
PapCreateServiceListener) and the register failed, we should remove the
|
|
service listener. */
|
|
|
|
if (initialRegister and errorCode isnt ATnoError)
|
|
PapDeleteServiceListener(serviceListenerRefNum, Empty,
|
|
(long unsigned)0);
|
|
|
|
/* If this is the first register (i.e. the service listener is just being
|
|
created) we need to post an outstanding ATP GetRequest in order to handle
|
|
incoming OpenConn's and SendStatus's. */
|
|
|
|
if (initialRegister and errorCode is ATnoError)
|
|
{
|
|
serviceListenerInfo = FindServiceListenerInfoFor(serviceListenerRefNum);
|
|
if (serviceListenerInfo is empty or serviceListenerInfo->closing)
|
|
{
|
|
UnlinkServiceListenerInfo(serviceListenerInfo);
|
|
ErrorLog("NbpRegisterComplete", ISevError, __LINE__, UnknownPort,
|
|
IErrPapSLGonePoof, IMsgPapSLGonePoof,
|
|
Insert0());
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
(*completionRoutine)(ATinternalError, userData, serviceListenerRefNum);
|
|
return;
|
|
}
|
|
errorCode = AtpEnqueueRequestHandler(empty, serviceListenerInfo->socket,
|
|
empty, 0, empty,
|
|
IncomingServiceListenerPacket,
|
|
(long unsigned)serviceListenerRefNum);
|
|
if (errorCode isnt ATnoError)
|
|
PapDeleteServiceListener(serviceListenerRefNum, Empty,
|
|
(long unsigned)0);
|
|
UnlinkServiceListenerInfo(serviceListenerInfo);
|
|
}
|
|
|
|
/* Okay, call the user's completion routine. */
|
|
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
if (completionRoutine isnt empty)
|
|
(*completionRoutine)(errorCode, userData, serviceListenerRefNum);
|
|
return;
|
|
|
|
} /* NbpRegisterComplete */
|
|
|
|
ExternForVisibleFunction ActiveJobInfo FindActiveJobInfoFor(long jobRefNum)
|
|
{
|
|
long index;
|
|
ActiveJobInfo activeJobInfo;
|
|
|
|
CheckMod(index, jobRefNum, NumberOfActiveJobHashBuckets,
|
|
"FindActiveJobInfoFor");
|
|
|
|
TakeLock(PapLock);
|
|
for (activeJobInfo = activeJobHashBuckets[index];
|
|
activeJobInfo isnt empty;
|
|
activeJobInfo = activeJobInfo->next)
|
|
if (activeJobInfo->jobRefNum is jobRefNum)
|
|
{
|
|
Link(activeJobInfo);
|
|
ReleaseLock(PapLock);
|
|
return(activeJobInfo);
|
|
}
|
|
ReleaseLock(PapLock);
|
|
|
|
return(Empty);
|
|
|
|
} /* FindActiveJobInfoFor */
|
|
|
|
ExternForVisibleFunction long GetNextJobRefNum(void)
|
|
{
|
|
StaticForSmallStack ActiveJobInfo activeJobInfo;
|
|
StaticForSmallStack GetNextJobInfo getNextJobInfo;
|
|
StaticForSmallStack OpenJobInfo openJobInfo;
|
|
StaticForSmallStack ServiceListenerInfo serviceListenerInfo;
|
|
StaticForSmallStack int index;
|
|
Boolean wrapped = False;
|
|
Boolean okay = False;
|
|
|
|
/* This routine is only a bitch because we solve the problem with such
|
|
tremendous overkill. Active job ref nums are kept on one of three lists:
|
|
the get next job list, and the active job list, and the pending open job
|
|
list. We walk all of these to see if our cantidate is truely available.
|
|
The likelyhood of us ever wrapping in 32 bits is so small that this is
|
|
a thorough waste of time! */
|
|
|
|
TakeLock(PapLock);
|
|
while(not okay)
|
|
{
|
|
okay = True;
|
|
if ((lastJobRefNum += 1) < 0)
|
|
{
|
|
lastJobRefNum = 0;
|
|
if (wrapped)
|
|
{
|
|
ReleaseLock(PapLock);
|
|
ErrorLog("GetNextJobRefNum", ISevError, __LINE__, UnknownPort,
|
|
IErrPapNoMoreJobRefNums, IMsgPapNoMoreJobRefNums,
|
|
Insert0());
|
|
return(-1);
|
|
}
|
|
wrapped = True;
|
|
}
|
|
|
|
/* First, walk the active job hash table... */
|
|
|
|
for (index = 0;
|
|
okay and index < NumberOfActiveJobHashBuckets;
|
|
index += 1)
|
|
for (activeJobInfo = activeJobHashBuckets[index];
|
|
okay and activeJobInfo isnt empty;
|
|
activeJobInfo = activeJobInfo->next)
|
|
if (activeJobInfo->jobRefNum is lastJobRefNum)
|
|
okay = False;
|
|
|
|
/* Now walk all of the GetNextJobLists. */
|
|
|
|
for (serviceListenerInfo = serviceListenerInfoHead;
|
|
okay and serviceListenerInfo isnt empty;
|
|
serviceListenerInfo = serviceListenerInfo->next)
|
|
for (getNextJobInfo = serviceListenerInfo->getNextJobList;
|
|
okay and getNextJobInfo isnt empty;
|
|
getNextJobInfo = getNextJobInfo->next)
|
|
if (getNextJobInfo->jobRefNum is lastJobRefNum)
|
|
okay = False;
|
|
|
|
/* Lastly, the pending open job list. */
|
|
|
|
for (openJobInfo = openJobInfoHead;
|
|
okay and openJobInfo isnt empty;
|
|
openJobInfo = openJobInfo->next)
|
|
if (openJobInfo->jobRefNum is lastJobRefNum)
|
|
okay = False;
|
|
}
|
|
ReleaseLock(PapLock);
|
|
|
|
/* We found one. What do you want to bet that it was the first one we
|
|
tried? */
|
|
|
|
return(lastJobRefNum);
|
|
|
|
} /* GetNextJobRefNum */
|
|
|
|
ExternForVisibleFunction void far
|
|
ConnectionTimerExpired(long unsigned timerId,
|
|
int additionalDataSize,
|
|
char far *additionalData)
|
|
{
|
|
long jobRefNum;
|
|
ActiveJobInfo activeJobInfo;
|
|
|
|
/* Touch unused formal... */
|
|
|
|
timerId;
|
|
|
|
/* Find our active job structure. */
|
|
|
|
if (additionalDataSize isnt sizeof(long))
|
|
{
|
|
ErrorLog("ConnectionTimerExpired", ISevError, __LINE__, UnknownPort,
|
|
IErrPapBadData, IMsgPapBadData,
|
|
Insert0());
|
|
return;
|
|
}
|
|
DeferAtpPackets();
|
|
jobRefNum = *(long *)additionalData;
|
|
if ((activeJobInfo = FindActiveJobInfoFor(jobRefNum)) is empty or
|
|
activeJobInfo->closing)
|
|
{
|
|
if (activeJobInfo is Empty)
|
|
ErrorLog("ConnectionTimerExpired", ISevError, __LINE__, UnknownPort,
|
|
IErrPapJobNotActive, IMsgPapJobNotActive,
|
|
Insert0());
|
|
else
|
|
UnlinkActiveJobInfo(activeJobInfo);
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
return;
|
|
}
|
|
|
|
/* If we've heard from this fellow recently, just restart the timer and
|
|
return. */
|
|
|
|
if ((CurrentRelativeTime() - activeJobInfo->lastContactTime) <=
|
|
PapConnectionSeconds)
|
|
{
|
|
activeJobInfo->connectionTimerId =
|
|
StartTimer(ConnectionTimerExpired, PapConnectionSeconds,
|
|
sizeof(long), (char *)&jobRefNum);
|
|
UnlinkActiveJobInfo(activeJobInfo);
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
return;
|
|
}
|
|
|
|
/* All is not well... put this guy out of his misery! */
|
|
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
PapCloseJob(jobRefNum, Empty, (long unsigned)0, False, True);
|
|
UnlinkActiveJobInfo(activeJobInfo);
|
|
return;
|
|
|
|
} /* ConnectionTimerExpired */
|
|
|
|
ExternForVisibleFunction void far
|
|
IncomingRequestPacket(AppleTalkErrorCode errorCode,
|
|
long unsigned userData,
|
|
AppleTalkAddress source,
|
|
void far *buffer, /* Really "char *" */
|
|
int bufferSize,
|
|
char far *userBytes,
|
|
Boolean exactlyOnce,
|
|
TRelTimerValue trelTimerValue,
|
|
short unsigned transactionId,
|
|
short unsigned bitmap)
|
|
{
|
|
ActiveJobInfo activeJobInfo;
|
|
unsigned char commandType;
|
|
unsigned char connectionId;
|
|
short unsigned sequenceNumber, expectedSequenceNumber;
|
|
long jobRefNum = (long)userData;
|
|
Boolean enqueueNewRequestHandler = True;
|
|
Boolean mustUndefer = True;
|
|
|
|
/* Touch unused formals... */
|
|
|
|
source, buffer, bufferSize, bitmap, trelTimerValue;
|
|
|
|
/* Check for errors. */
|
|
|
|
if (errorCode is ATatpTransactionAborted or errorCode is ATsocketClosed)
|
|
return; /* Job closed... */
|
|
|
|
DeferTimerChecking();
|
|
DeferAtpPackets();
|
|
if ((activeJobInfo = FindActiveJobInfoFor(jobRefNum)) is empty or
|
|
activeJobInfo->closing)
|
|
{
|
|
if (activeJobInfo is Empty)
|
|
ErrorLog("IncomingRequestPacket", ISevWarning, __LINE__, UnknownPort,
|
|
IErrPapJobGonePoof, IMsgPapJobGonePoof,
|
|
Insert0());
|
|
else
|
|
UnlinkActiveJobInfo(activeJobInfo);
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
return;
|
|
}
|
|
|
|
/* Make sure connection ID's match, if not, just re-enqueue the request
|
|
handler and we're done. */
|
|
|
|
commandType = (unsigned char)userBytes[PapCommandTypeOffset];
|
|
connectionId = (unsigned char)userBytes[PapConnectionIdOffset];
|
|
if (connectionId isnt activeJobInfo->connectionId)
|
|
{
|
|
ErrorLog("IncomingRequestPacket", ISevVerbose, __LINE__, UnknownPort,
|
|
IErrPapIdMismatch, IMsgPapIdMismatch,
|
|
Insert0());
|
|
AtpCancelResponse(activeJobInfo->ourSocket,
|
|
activeJobInfo->theirAddress,
|
|
transactionId, False);
|
|
if (AtpEnqueueRequestHandler(empty, activeJobInfo->ourSocket,
|
|
empty, 0, empty,
|
|
IncomingRequestPacket,
|
|
(long unsigned)jobRefNum) isnt ATnoError)
|
|
ErrorLog("IncomingRequestPacket", ISevError, __LINE__, UnknownPort,
|
|
IErrPapBadRequestHandler, IMsgPapBadRequestHandler,
|
|
Insert0());
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
UnlinkActiveJobInfo(activeJobInfo);
|
|
return;
|
|
}
|
|
|
|
/* Well, regardless of what we've got, at least it's contact. */
|
|
|
|
activeJobInfo->lastContactTime = CurrentRelativeTime();
|
|
|
|
/* Do the right thing with the contained command. */
|
|
|
|
switch(commandType)
|
|
{
|
|
case PapTickleCommand:
|
|
|
|
/* Not much to do here, we've already noted the contact. */
|
|
|
|
break;
|
|
|
|
case PapSendDataCommand:
|
|
|
|
/* Check Sequence number, note that zero means "unsequenced" and we
|
|
should accept the beast. */
|
|
|
|
sequenceNumber =
|
|
(short unsigned)(userBytes[PapSequenceNumberOffset] << 8);
|
|
sequenceNumber +=
|
|
(unsigned char)(userBytes[PapSequenceNumberOffset + 1] & 0xFF);
|
|
|
|
TakeLock(PapLock);
|
|
if (sequenceNumber is 0 and
|
|
activeJobInfo->incomingSendDataPending)
|
|
{
|
|
/* What we have here is failure to communicate... no, no, just
|
|
kidding. What we really have here is an incoming unsequenced
|
|
sendData request before we've finished with the previous one
|
|
(gotten a release for it). We don't clear the
|
|
"incomingSendDataPending" flag until a release comes in (either
|
|
real or time-out). Also, we can't play Steve Wolfe's "implied
|
|
release" game that is legal with sequenced requests. So,
|
|
we don't let the other side get a jump on us, just cancel the
|
|
response, so we'll get another delivery of the sendData later
|
|
(maybe we'll be ready then). */
|
|
|
|
ReleaseLock(PapLock);
|
|
AtpCancelResponse(activeJobInfo->ourSocket,
|
|
source, transactionId, False);
|
|
break;
|
|
}
|
|
else if (sequenceNumber is 0)
|
|
{
|
|
/* Okay, an incoming unsequenced sendData that we're really ready
|
|
to handle... accept him. */
|
|
}
|
|
else
|
|
{
|
|
/* Verify sequence number. */
|
|
|
|
expectedSequenceNumber = activeJobInfo->lastIncomingSequenceNumber;
|
|
if (expectedSequenceNumber is (short unsigned)0xFFFF)
|
|
expectedSequenceNumber = 0;
|
|
expectedSequenceNumber += 1;
|
|
if (sequenceNumber isnt expectedSequenceNumber)
|
|
{
|
|
ReleaseLock(PapLock);
|
|
ErrorLog("IncomingRequestPacket", ISevVerbose, __LINE__,
|
|
UnknownPort, IErrPapSendDataSeqError, IMsgPapSendDataSeqError,
|
|
Insert0());
|
|
AtpCancelResponse(activeJobInfo->ourSocket,
|
|
source, transactionId, False);
|
|
break;
|
|
}
|
|
|
|
if (activeJobInfo->incomingSendDataPending)
|
|
{
|
|
/* Steve Wolfe [Apple] taught me a neat trick here. Whats up
|
|
is we've gotten a new sendData before we think we're finished
|
|
with the previous (i.e. have gotten its release or time out).
|
|
However, this new guy has arrived AND it has the next
|
|
expected sequence number which means, given PAPs ono-at-a-
|
|
time nature, that the previous transaction's release was
|
|
dropped on the floor (due, most likey, to a network error).
|
|
So, we can assume this and "finish" the previous transaction
|
|
and then move ahead with this one. This gets rid of the
|
|
30 second pauses when a release is dropped! */
|
|
|
|
ReleaseLock(PapLock);
|
|
AtpCancelResponse(activeJobInfo->ourSocket,
|
|
activeJobInfo->incomingSendDataSource,
|
|
activeJobInfo->incomingSendDataTransactionId,
|
|
False);
|
|
TakeLock(PapLock);
|
|
activeJobInfo->incomingSendDataPending = False;
|
|
activeJobInfo->writeDataWaiting = False;
|
|
}
|
|
|
|
activeJobInfo->lastIncomingSequenceNumber = expectedSequenceNumber;
|
|
}
|
|
|
|
/* Copy the needed information into the active job node. */
|
|
|
|
activeJobInfo->incomingSendDataPending = True;
|
|
activeJobInfo->incomingSendDataTransactionId = transactionId;
|
|
activeJobInfo->incomingSendDataExactlyOnce = exactlyOnce;
|
|
|
|
/* The Macintosh may not send the "SendData" from its "responding
|
|
socket" (the one we're tickling and we've noted as "theirAddress"),
|
|
we need to address the response to the socket that the request
|
|
originated on, so, save it away. */
|
|
|
|
activeJobInfo->incomingSendDataSource = source;
|
|
ReleaseLock(PapLock);
|
|
|
|
/* If we're waiting to write data, do it! */
|
|
|
|
if (activeJobInfo->writeDataWaiting)
|
|
PostSendDataResponse(activeJobInfo);
|
|
else
|
|
{
|
|
/* If we have a sendPossible handler, invoke it. */
|
|
|
|
if (activeJobInfo->sendPossibleRoutine isnt empty)
|
|
{
|
|
(*activeJobInfo->sendPossibleRoutine)(
|
|
activeJobInfo->jobRefNum,
|
|
activeJobInfo->sendPossibleUserData,
|
|
activeJobInfo->sendFlowQuantum);
|
|
}
|
|
}
|
|
|
|
break;
|
|
|
|
case PapCloseConnectionCommand:
|
|
|
|
/* Post the close connection reply. Who really cares whether the
|
|
other side really gets it??? PapCloseJob will call the
|
|
closeJobRoutine (indication) if set. */
|
|
|
|
userBytes[PapCommandTypeOffset] = PapCloseConnectionReplyCommand;
|
|
AtpPostResponse(activeJobInfo->ourSocket,
|
|
source,
|
|
transactionId, empty, 0,
|
|
userBytes, exactlyOnce, empty,
|
|
(long unsigned)0);
|
|
|
|
/* Close the specified connection (job). */
|
|
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
PapCloseJob(jobRefNum, Empty, (long unsigned)0, True, False);
|
|
mustUndefer = False;
|
|
enqueueNewRequestHandler = False;
|
|
break;
|
|
|
|
default:
|
|
ErrorLog("IncomingRequestPacket", ISevWarning, __LINE__, UnknownPort,
|
|
IErrPapFunnyCommand, IMsgPapFunnyCommand,
|
|
Insert0());
|
|
break;
|
|
}
|
|
|
|
/* Repost the request handler. */
|
|
|
|
if (enqueueNewRequestHandler)
|
|
if (AtpEnqueueRequestHandler(empty, activeJobInfo->ourSocket,
|
|
empty, 0, empty,
|
|
IncomingRequestPacket,
|
|
(long unsigned)jobRefNum) isnt ATnoError)
|
|
ErrorLog("IncomingRequestPacket", ISevError, __LINE__, UnknownPort,
|
|
IErrPapBadRequestHandler, IMsgPapBadRequestHandler,
|
|
Insert0());
|
|
if (mustUndefer)
|
|
{
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
}
|
|
UnlinkActiveJobInfo(activeJobInfo);
|
|
return;
|
|
|
|
} /* IncomingRequestPacket */
|
|
|
|
ExternForVisibleFunction void far
|
|
NbpLookupComplete(AppleTalkErrorCode errorCode,
|
|
long unsigned incomingUserData,
|
|
int reason,
|
|
long onWhosBehalf,
|
|
int nbpId,
|
|
...)
|
|
{
|
|
StaticForSmallStack AppleTalkAddress serviceListenerAddress;
|
|
StaticForSmallStack OpenJobInfo openJobInfo;
|
|
StaticForSmallStack char userBytes[AtpUserBytesSize];
|
|
StaticForSmallStack char far *tupleOpaqueBuffer;
|
|
StaticForSmallStack int tupleCount;
|
|
StaticForSmallStack va_list ap;
|
|
StaticForSmallStack long unsigned now;
|
|
PapCompletionInfo completionInfo = (PapCompletionInfo)incomingUserData;
|
|
Boolean getStatus = completionInfo->lookupForStatus;
|
|
long unsigned userData = completionInfo->userData;
|
|
long jobRefNum = completionInfo->jobRefNum;
|
|
PapGetStatusComplete *getStatusCompletionRoutine =
|
|
(PapGetStatusComplete *)completionInfo->completionRoutine;
|
|
PapOpenComplete *openCompletionRoutine =
|
|
(PapOpenComplete *)completionInfo->completionRoutine;
|
|
long socket;
|
|
AppleTalkAddress socketAddress;
|
|
|
|
/* Touch unused formal... */
|
|
|
|
reason, onWhosBehalf;
|
|
|
|
now = CurrentRelativeTime();
|
|
|
|
DeferTimerChecking();
|
|
DeferAtpPackets();
|
|
|
|
/* Okay, in the event of an NBP lookup completed on behalf of an OpenJob.
|
|
Find the OpenJob. Just for fun, and in-case we encouter and error
|
|
try to find the openJobInfo... */
|
|
|
|
if (not getStatus)
|
|
{
|
|
TakeLock(PapLock);
|
|
for (openJobInfo = openJobInfoHead;
|
|
openJobInfo isnt empty;
|
|
openJobInfo = openJobInfo->next)
|
|
if (openJobInfo->jobRefNum is jobRefNum)
|
|
break;
|
|
if (openJobInfo is empty)
|
|
{
|
|
ReleaseLock(PapLock);
|
|
if (completionInfo->freeOpaqueResponse)
|
|
FreeOpaqueDataDescriptor(completionInfo->opaqueResponse);
|
|
Free(completionInfo);
|
|
ErrorLog("NbpLookupComplete", ISevError, __LINE__, UnknownPort,
|
|
IErrPapLostNode, IMsgPapLostNode,
|
|
Insert0());
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
(*openCompletionRoutine)(ATinternalError, userData, jobRefNum, 0,
|
|
empty, 0);
|
|
return;
|
|
}
|
|
Link(openJobInfo);
|
|
ReleaseLock(PapLock);
|
|
}
|
|
|
|
/* If we got an error... give up. */
|
|
|
|
if (errorCode isnt ATnoError)
|
|
{
|
|
if (not getStatus)
|
|
{
|
|
if (openJobInfo->freeOpaqueDataDescriptor)
|
|
FreeOpaqueDataDescriptor(openJobInfo->opaqueDataDescriptor);
|
|
if (not UnlinkOpenJobInfo(openJobInfo))
|
|
UnlinkOpenJobInfo(openJobInfo);
|
|
}
|
|
if (completionInfo->freeOpaqueResponse)
|
|
FreeOpaqueDataDescriptor(completionInfo->opaqueResponse);
|
|
Free(completionInfo);
|
|
if (getStatus)
|
|
(*getStatusCompletionRoutine)(errorCode, userData, empty, 0);
|
|
else
|
|
(*openCompletionRoutine)(errorCode, userData, jobRefNum, 0, empty, 0);
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
return;
|
|
}
|
|
|
|
/* Okay, extract the additional arguments... */
|
|
|
|
va_start(ap, nbpId);
|
|
tupleOpaqueBuffer = va_arg(ap, void far *);
|
|
tupleCount = va_arg(ap, int);
|
|
va_end(ap);
|
|
if (tupleCount isnt 1)
|
|
{
|
|
if (tupleCount is 0)
|
|
errorCode = ATpapServiceListenerNotFound;
|
|
else
|
|
errorCode = ATpapNonUniqueLookup;
|
|
if (not getStatus)
|
|
{
|
|
if (openJobInfo->freeOpaqueDataDescriptor)
|
|
FreeOpaqueDataDescriptor(openJobInfo->opaqueDataDescriptor);
|
|
if (not UnlinkOpenJobInfo(openJobInfo))
|
|
UnlinkOpenJobInfo(openJobInfo);
|
|
}
|
|
if (completionInfo->freeOpaqueResponse)
|
|
FreeOpaqueDataDescriptor(completionInfo->opaqueResponse);
|
|
Free(completionInfo);
|
|
if (getStatus)
|
|
(*getStatusCompletionRoutine)(errorCode, userData, empty, 0);
|
|
else
|
|
(*openCompletionRoutine)(errorCode, userData, jobRefNum, 0,
|
|
empty, 0);
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
return;
|
|
}
|
|
|
|
/* Decode the NBP tuple... that will be the address of the service
|
|
listener (server). The tupleBuffer is really the response buffer in
|
|
our completionInfo -- use this rather than the "opaque" guy that we
|
|
just got as extra arguments... might as well take the direct aproach. */
|
|
|
|
serviceListenerAddress.networkNumber =
|
|
(unsigned short)(completionInfo->responseBuffer[0] << 8);
|
|
serviceListenerAddress.networkNumber +=
|
|
(unsigned char)(completionInfo->responseBuffer[1]);
|
|
serviceListenerAddress.nodeNumber =
|
|
(unsigned char)(completionInfo->responseBuffer[2]);
|
|
serviceListenerAddress.socketNumber =
|
|
(unsigned char)(completionInfo->responseBuffer[3]);
|
|
|
|
/* Okay, do the right thing for our two cases... GetStatus or
|
|
OpenConnection. */
|
|
|
|
if (getStatus)
|
|
{
|
|
/* This is a little hard, we don't have any particularly good socket
|
|
(let alone an ATP socket) to post the GetRequest for status on. For
|
|
lack of any better idea, open one on the default port. */
|
|
|
|
if ((errorCode = AtpOpenSocketOnNode(&socket, -1, empty, 0, empty, 0))
|
|
isnt ATnoError)
|
|
{
|
|
if (completionInfo->freeOpaqueResponse)
|
|
FreeOpaqueDataDescriptor(completionInfo->opaqueResponse);
|
|
Free(completionInfo);
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
(*getStatusCompletionRoutine)(errorCode, userData, empty, 0);
|
|
return;
|
|
}
|
|
completionInfo->socket = socket;
|
|
completionInfo->mustCloseAtpSocket = True; /* !!! */
|
|
|
|
/* Okay, finally post the damn request! */
|
|
|
|
userBytes[PapConnectionIdOffset] = 0;
|
|
userBytes[PapCommandTypeOffset] = PapSendStatusCommand;
|
|
userBytes[PapSequenceNumberOffset] = 0;
|
|
userBytes[PapSequenceNumberOffset + 1] = 0;
|
|
errorCode = AtpPostRequest(completionInfo->socket,
|
|
serviceListenerAddress, Empty,
|
|
empty, 0, userBytes,
|
|
True, completionInfo->opaqueResponse,
|
|
PapMaximumDataPacketSize,
|
|
completionInfo->responseUserBytes,
|
|
PapGetStatusRequestRetryCount,
|
|
PapGetStatusAtpRetrySeconds,
|
|
ThirtySecondsTRelTimer,
|
|
IncomingStatus,
|
|
(long unsigned)completionInfo);
|
|
if (errorCode isnt ATnoError)
|
|
{
|
|
if (AtpCloseSocketOnNode(socket, Empty, (long unsigned)0) isnt
|
|
ATnoError)
|
|
ErrorLog("NbpLookupComplete", ISevError, __LINE__, UnknownPort,
|
|
IErrPapBadTempSocketClose, IMsgPapBadTempSocketClose,
|
|
Insert0());
|
|
if (completionInfo->freeOpaqueResponse)
|
|
FreeOpaqueDataDescriptor(completionInfo->opaqueResponse);
|
|
Free(completionInfo);
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
(*getStatusCompletionRoutine)(errorCode, userData, empty, 0);
|
|
}
|
|
else
|
|
{
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
return;
|
|
}
|
|
}
|
|
|
|
/* We need to open our ATP responding socket. */
|
|
|
|
if (openJobInfo->existingAtpSocket < 0)
|
|
errorCode = AtpOpenSocketOnNode(&socket, openJobInfo->port, empty,
|
|
openJobInfo->desiredSocket, empty, 0);
|
|
else
|
|
{
|
|
errorCode = ATnoError;
|
|
socket = openJobInfo->existingAtpSocket;
|
|
}
|
|
|
|
if (errorCode isnt ATnoError or
|
|
MapSocketToAddress(socket, &socketAddress) isnt ATnoError)
|
|
{
|
|
/* Couldn't open our ATP socket. Sigh. */
|
|
|
|
if (openJobInfo->freeOpaqueDataDescriptor)
|
|
FreeOpaqueDataDescriptor(openJobInfo->opaqueDataDescriptor);
|
|
if (not UnlinkOpenJobInfo(openJobInfo))
|
|
UnlinkOpenJobInfo(openJobInfo);
|
|
if (completionInfo->freeOpaqueResponse)
|
|
FreeOpaqueDataDescriptor(completionInfo->opaqueResponse);
|
|
Free(completionInfo);
|
|
ErrorLog("NbpLookupComplete", ISevVerbose, __LINE__, UnknownPort,
|
|
IErrPapCouldNotOpen, IMsgPapCouldNotOpen,
|
|
Insert0());
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
(*openCompletionRoutine)(errorCode, userData, jobRefNum, 0, empty, 0);
|
|
return;
|
|
}
|
|
|
|
/* Save this beast. */
|
|
|
|
completionInfo->socket = socket;
|
|
completionInfo->mustCloseAtpSocket = (openJobInfo->existingAtpSocket < 0);
|
|
|
|
/* Set the ATP data packet size correctly for PAP. */
|
|
|
|
if (AtpMaximumSinglePacketDataSize(completionInfo->socket,
|
|
PapMaximumDataPacketSize) isnt ATnoError)
|
|
ErrorLog("NbpLookupComplete", ISevError, __LINE__, UnknownPort,
|
|
IErrPapBadSetSize, IMsgPapBadSetSize,
|
|
Insert0());
|
|
|
|
/* Assign a new connection ID. */
|
|
|
|
openJobInfo->connectionId = (unsigned char)(lastConnectionId += 1);
|
|
|
|
/* Save away the address of our target service listener. */
|
|
|
|
openJobInfo->serviceListenerAddress = serviceListenerAddress;
|
|
|
|
/* Okay, prepare to post the OpenConn request; build up both userBytes and
|
|
data buffer! */
|
|
|
|
userBytes[PapConnectionIdOffset] = openJobInfo->connectionId;
|
|
userBytes[PapCommandTypeOffset] = PapOpenConnectionCommand;
|
|
userBytes[PapSequenceNumberOffset] = 0;
|
|
userBytes[PapSequenceNumberOffset + 1] = 0;
|
|
|
|
/* The following is somewhat of a hack... we really want to build our own
|
|
data buffer: a "char *," not whatever our surrounding environment thinks
|
|
is an "opaque" buffer. So, build our array, and then make a system
|
|
dependent "opaque data descriptor" for it, noting whether we need to free
|
|
this when we free the openJobInfo. */
|
|
|
|
openJobInfo->buffer[PapRespondingSocketOffset] = socketAddress.socketNumber;
|
|
openJobInfo->buffer[PapFlowQuantumOffset] =
|
|
(char)openJobInfo->workstationQuantum;
|
|
openJobInfo->buffer[PapWaitTimeOffset] =
|
|
(char)((now - openJobInfo->startTime) >> 8);
|
|
openJobInfo->buffer[PapWaitTimeOffset + 1] =
|
|
(char)((now - openJobInfo->startTime) & 0xFF);
|
|
errorCode = ATnoError;
|
|
if ((openJobInfo->opaqueDataDescriptor =
|
|
MakeOpaqueDataDescriptor(openJobInfo->buffer, PapStatusOffset,
|
|
&openJobInfo->freeOpaqueDataDescriptor))
|
|
is Empty)
|
|
errorCode = AToutOfMemory;
|
|
|
|
/* PostIt! (a trademark of 3M) */
|
|
|
|
if (errorCode is ATnoError)
|
|
errorCode = AtpPostRequest(completionInfo->socket,
|
|
serviceListenerAddress,
|
|
Empty,
|
|
openJobInfo->opaqueDataDescriptor,
|
|
PapStatusOffset, userBytes,
|
|
True, completionInfo->opaqueResponse,
|
|
PapMaximumDataPacketSize,
|
|
completionInfo->responseUserBytes,
|
|
PapOpenConnRequestRetryCount,
|
|
PapOpenConnAtpRetrySeconds,
|
|
ThirtySecondsTRelTimer,
|
|
IncomingOpenReply,
|
|
(long unsigned)completionInfo);
|
|
|
|
if (errorCode < 0) /* Couldn't send request. */
|
|
{
|
|
if (completionInfo->mustCloseAtpSocket)
|
|
AtpCloseSocketOnNode(completionInfo->socket, Empty, (long unsigned)0);
|
|
if (openJobInfo->freeOpaqueDataDescriptor)
|
|
FreeOpaqueDataDescriptor(openJobInfo->opaqueDataDescriptor);
|
|
if (not UnlinkOpenJobInfo(openJobInfo))
|
|
UnlinkOpenJobInfo(openJobInfo);
|
|
if (completionInfo->freeOpaqueResponse)
|
|
FreeOpaqueDataDescriptor(completionInfo->opaqueResponse);
|
|
Free(completionInfo);
|
|
ErrorLog("NbpLookupComplete", ISevVerbose, __LINE__, UnknownPort,
|
|
IErrPapBadOpenConnReqSend, IMsgPapBadOpenConnReqSend,
|
|
Insert0());
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
(*openCompletionRoutine)(errorCode, userData, jobRefNum, 0,
|
|
empty, 0);
|
|
return;
|
|
}
|
|
|
|
Unlink(openJobInfo, PapLock);
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
return;
|
|
|
|
} /* NbpLookupComplete */
|
|
|
|
ExternForVisibleFunction void far
|
|
IncomingStatus(AppleTalkErrorCode errorCode,
|
|
long unsigned incomingUserData,
|
|
AppleTalkAddress source,
|
|
void far *opaqueResponseBuffer,
|
|
int responseBufferSize,
|
|
char far *responseUserBytes,
|
|
short unsigned transactionId)
|
|
{
|
|
PapCompletionInfo completionInfo = (PapCompletionInfo)incomingUserData;
|
|
PapGetStatusComplete *completionRoutine =
|
|
(PapGetStatusComplete *)completionInfo->completionRoutine;
|
|
long unsigned userData = completionInfo->userData;
|
|
Boolean mustCloseAtpSocket = completionInfo->mustCloseAtpSocket;
|
|
long socket = completionInfo->socket ;
|
|
void far *usersOpaqueStatusBuffer = completionInfo->usersOpaqueStatusBuffer;
|
|
int statusSize;
|
|
|
|
/* Touch unused formals... */
|
|
|
|
source, transactionId;
|
|
|
|
/* We only use our stack... no need to defer anything... */
|
|
|
|
/* If we used a temporary ATP socket, close it now. */
|
|
|
|
if (mustCloseAtpSocket)
|
|
if (AtpCloseSocketOnNode(socket, Empty, (long unsigned)0) isnt ATnoError)
|
|
ErrorLog("IncomingStatus", ISevError, __LINE__, UnknownPort,
|
|
IErrPapBadTempSocketClose, IMsgPapBadTempSocketClose,
|
|
Insert0());
|
|
|
|
/* Check for errors. */
|
|
|
|
if (errorCode isnt ATnoError)
|
|
{
|
|
if (completionInfo->freeOpaqueResponse)
|
|
FreeOpaqueDataDescriptor(completionInfo->opaqueResponse);
|
|
Free(completionInfo);
|
|
(*completionRoutine)(errorCode, userData, empty, 0);
|
|
return;
|
|
}
|
|
if (responseUserBytes[PapCommandTypeOffset] isnt PapStatusReplyCommand or
|
|
responseBufferSize < PapStatusOffset + 1)
|
|
{
|
|
ErrorLog("IncomingStatus", ISevWarning, __LINE__, UnknownPort,
|
|
IErrPapBadStatusResp, IMsgPapBadStatusResp,
|
|
Insert0());
|
|
if (completionInfo->freeOpaqueResponse)
|
|
FreeOpaqueDataDescriptor(completionInfo->opaqueResponse);
|
|
Free(completionInfo);
|
|
(*completionRoutine)(ATinternalError, userData, empty, 0);
|
|
return;
|
|
}
|
|
|
|
/* Okay, copy the status buffer into the user's buffer. We know for a
|
|
fact that our incoming opaqueResponseBuffer really describes the
|
|
responseBuffer in our completionInfo, so use the easy route! */
|
|
|
|
statusSize = (unsigned char)completionInfo->responseBuffer[PapStatusOffset];
|
|
MoveToOpaque(usersOpaqueStatusBuffer, 0, completionInfo->responseBuffer +
|
|
PapStatusOffset + 1, statusSize);
|
|
|
|
/* All looks good, call the completion routine. */
|
|
|
|
if (completionInfo->freeOpaqueResponse)
|
|
FreeOpaqueDataDescriptor(completionInfo->opaqueResponse);
|
|
Free(completionInfo);
|
|
(*completionRoutine)(ATnoError, userData, usersOpaqueStatusBuffer,
|
|
statusSize);
|
|
return;
|
|
|
|
} /* IncomingStatus */
|
|
|
|
ExternForVisibleFunction void far
|
|
IncomingOpenReply(AppleTalkErrorCode errorCode,
|
|
long unsigned incomingUserData,
|
|
AppleTalkAddress source,
|
|
void far *opaqueResponseBuffer,
|
|
int responseBufferSize,
|
|
char far *responseUserBytes,
|
|
short unsigned transactionId)
|
|
{
|
|
StaticForSmallStack unsigned char respondingSocket;
|
|
StaticForSmallStack Boolean error;
|
|
StaticForSmallStack char userBytes[AtpUserBytesSize];
|
|
StaticForSmallStack OpenJobInfo openJobInfo;
|
|
StaticForSmallStack ActiveJobInfo activeJobInfo;
|
|
StaticForSmallStack unsigned char connectionId;
|
|
StaticForSmallStack short unsigned result;
|
|
long index;
|
|
PapCompletionInfo completionInfo = (PapCompletionInfo)incomingUserData;
|
|
PapOpenComplete *completionRoutine =
|
|
(PapOpenComplete *)completionInfo->completionRoutine;
|
|
long unsigned userData = completionInfo->userData;
|
|
long jobRefNum = completionInfo->jobRefNum;
|
|
char *usersOpaqueStatusBuffer = completionInfo->usersOpaqueStatusBuffer;
|
|
int statusBufferLength;
|
|
short serverFlowQuantum;
|
|
|
|
/* Touch unused formals... */
|
|
|
|
source, transactionId;
|
|
|
|
error = False;
|
|
|
|
/* Okay, find our OpenJobInfo... */
|
|
|
|
DeferTimerChecking();
|
|
DeferAtpPackets();
|
|
TakeLock(PapLock);
|
|
for (openJobInfo = openJobInfoHead;
|
|
openJobInfo isnt empty;
|
|
openJobInfo = openJobInfo->next)
|
|
if (openJobInfo->jobRefNum is jobRefNum)
|
|
break;
|
|
if (openJobInfo is empty)
|
|
{
|
|
ReleaseLock(PapLock);
|
|
if (completionInfo->mustCloseAtpSocket)
|
|
AtpCloseSocketOnNode(completionInfo->socket, Empty, (long unsigned)0);
|
|
if (completionInfo->freeOpaqueResponse)
|
|
FreeOpaqueDataDescriptor(completionInfo->opaqueResponse);
|
|
Free(completionInfo);
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
(*completionRoutine)(ATinternalError, userData, jobRefNum, 0,
|
|
empty, 0);
|
|
ErrorLog("IncomingOpenReply", ISevError, __LINE__, UnknownPort,
|
|
IErrPapOpenJobMissing, IMsgPapOpenJobMissing,
|
|
Insert0());
|
|
return;
|
|
}
|
|
Link(openJobInfo);
|
|
ReleaseLock(PapLock);
|
|
|
|
/* Check the incoming ATP error code. Pap-ize the error code and call the
|
|
user's completion routine. */
|
|
|
|
if (errorCode isnt ATnoError)
|
|
{
|
|
if (errorCode is ATatpTransactionAborted)
|
|
errorCode = ATpapOpenAborted; /* Won't really happen. */
|
|
else if (errorCode is ATatpRequestTimedOut)
|
|
errorCode = ATpapOpenTimedOut;
|
|
if (errorCode isnt ATsocketClosed)
|
|
if (completionInfo->mustCloseAtpSocket)
|
|
AtpCloseSocketOnNode(completionInfo->socket, Empty,
|
|
(long unsigned)0);
|
|
if (openJobInfo->freeOpaqueDataDescriptor)
|
|
FreeOpaqueDataDescriptor(openJobInfo->opaqueDataDescriptor);
|
|
if (not UnlinkOpenJobInfo(openJobInfo))
|
|
UnlinkOpenJobInfo(openJobInfo);
|
|
if (completionInfo->freeOpaqueResponse)
|
|
FreeOpaqueDataDescriptor(completionInfo->opaqueResponse);
|
|
Free(completionInfo);
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
(*completionRoutine)(errorCode, userData, jobRefNum, 0,
|
|
empty, 0);
|
|
return;
|
|
}
|
|
|
|
/* NOTE BENE: In the following code you will note that we refernce
|
|
"completionInfo->responseBuffer" when one would think we
|
|
shoud be referencing "opaqueResponse." Not to worry!
|
|
We know for a fact that the latter describes the former,
|
|
so we take the easy way out! */
|
|
|
|
/* Well, lets see what kind of response we got; take a look at both the
|
|
response user-bytes and the response buffer. Note that we now allow
|
|
the silly LaserWriter IIg to leave the status string off altogether,
|
|
rather than the [correct] zero-sized string. */
|
|
|
|
errorCode = ATinternalError; /* Default reason for giving up. */
|
|
if (responseBufferSize < PapStatusOffset)
|
|
{
|
|
ErrorLog("IncomingOpenReply", ISevWarning, __LINE__, UnknownPort,
|
|
IErrPapTooShort, IMsgPapTooShort,
|
|
Insert0());
|
|
error = True;
|
|
}
|
|
if (not error)
|
|
{
|
|
if (responseBufferSize is PapStatusOffset)
|
|
statusBufferLength = 0; /* Missing, from LaserWriter IIg. */
|
|
else
|
|
statusBufferLength =
|
|
(unsigned char)completionInfo->responseBuffer[PapStatusOffset];
|
|
if (statusBufferLength isnt 0 and
|
|
statusBufferLength + 1 + PapStatusOffset > responseBufferSize)
|
|
{
|
|
ErrorLog("IncomingOpenReply", ISevWarning, __LINE__, UnknownPort,
|
|
IErrPapTooLong, IMsgPapTooLong,
|
|
Insert0());
|
|
error = True;
|
|
}
|
|
else
|
|
{
|
|
connectionId = (unsigned char)responseUserBytes[PapConnectionIdOffset];
|
|
if (connectionId isnt openJobInfo->connectionId)
|
|
{
|
|
ErrorLog("IncomingOpenReply", ISevWarning, __LINE__, UnknownPort,
|
|
IErrPapIdMismatch, IMsgPapIdMismatch,
|
|
Insert0());
|
|
error = True;
|
|
}
|
|
if (responseUserBytes[PapCommandTypeOffset] isnt
|
|
PapOpenConnectionReplyCommand)
|
|
{
|
|
ErrorLog("IncomingOpenReply", ISevWarning, __LINE__, UnknownPort,
|
|
IErrPapNotOpenReply, IMsgPapNotOpenReply,
|
|
Insert0());
|
|
error = True;
|
|
}
|
|
respondingSocket = (unsigned char)completionInfo->
|
|
responseBuffer[PapRespondingSocketOffset];
|
|
serverFlowQuantum = (unsigned char)completionInfo->
|
|
responseBuffer[PapFlowQuantumOffset];
|
|
if (serverFlowQuantum > PapMaximumFlowQuantum)
|
|
serverFlowQuantum = PapMaximumFlowQuantum;
|
|
|
|
result = (short unsigned)((unsigned char)completionInfo->
|
|
responseBuffer[PapResultOffset] << 8);
|
|
result += (unsigned char)completionInfo->
|
|
responseBuffer[PapResultOffset + 1];
|
|
if (result isnt PapNoError)
|
|
{
|
|
errorCode = ATpapServerBusy;
|
|
error = True;
|
|
}
|
|
|
|
/* Move the returned status information to the user's buffer. */
|
|
|
|
if (usersOpaqueStatusBuffer isnt empty)
|
|
MoveToOpaque(usersOpaqueStatusBuffer, 0,
|
|
completionInfo->responseBuffer + PapStatusOffset + 1,
|
|
statusBufferLength);
|
|
}
|
|
}
|
|
|
|
/* If we're okay so far, try to allocate the active-job node. */
|
|
|
|
if (not error)
|
|
{
|
|
activeJobInfo = (ActiveJobInfo)Calloc(sizeof(*activeJobInfo), 1);
|
|
if (activeJobInfo is empty)
|
|
{
|
|
ErrorLog("IncomingOpenReply", ISevError, __LINE__, UnknownPort,
|
|
IErrPapOutOfMemory, IMsgPapOutOfMemory,
|
|
Insert0());
|
|
errorCode = AToutOfMemory;
|
|
error = True;
|
|
}
|
|
}
|
|
|
|
/* Well, if things aren't going to fly, lets be real men and run away... */
|
|
|
|
if (error)
|
|
{
|
|
if (completionInfo->mustCloseAtpSocket)
|
|
AtpCloseSocketOnNode(completionInfo->socket, Empty, (long unsigned)0);
|
|
if (openJobInfo->freeOpaqueDataDescriptor)
|
|
FreeOpaqueDataDescriptor(openJobInfo->opaqueDataDescriptor);
|
|
if (not UnlinkOpenJobInfo(openJobInfo))
|
|
UnlinkOpenJobInfo(openJobInfo);
|
|
if (completionInfo->freeOpaqueResponse)
|
|
FreeOpaqueDataDescriptor(completionInfo->opaqueResponse);
|
|
Free(completionInfo);
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
(*completionRoutine)(errorCode, userData, jobRefNum, 0,
|
|
empty, 0);
|
|
return;
|
|
}
|
|
|
|
/* Looks pretty good so far, start filling in our new ActiveJob structure. */
|
|
|
|
activeJobInfo->jobRefNum = jobRefNum;
|
|
activeJobInfo->ourSocket = completionInfo->socket;
|
|
activeJobInfo->closeOurSocket = completionInfo->mustCloseAtpSocket;
|
|
activeJobInfo->ourPort = openJobInfo->port;
|
|
activeJobInfo->theirAddress = openJobInfo->serviceListenerAddress;
|
|
activeJobInfo->theirAddress.socketNumber = respondingSocket;
|
|
activeJobInfo->serviceListenerAddress = openJobInfo->serviceListenerAddress;
|
|
activeJobInfo->connectionId = connectionId;
|
|
activeJobInfo->receiveFlowQuantum = openJobInfo->workstationQuantum;
|
|
activeJobInfo->sendFlowQuantum = serverFlowQuantum;
|
|
activeJobInfo->closeJobRoutine = openJobInfo->closeJobRoutine;
|
|
activeJobInfo->closeJobUserData = openJobInfo->closeJobUserData;
|
|
|
|
/* Thread this new beast into the lookup structure. */
|
|
|
|
CheckMod(index, jobRefNum, NumberOfActiveJobHashBuckets, "IncomingOpenReply");
|
|
TakeLock(PapLock);
|
|
activeJobInfo->next = activeJobHashBuckets[index];
|
|
activeJobHashBuckets[index] = Link(activeJobInfo);
|
|
ReleaseLock(PapLock);
|
|
|
|
/* Start tickling (coochi coo) the other side. */
|
|
|
|
userBytes[PapConnectionIdOffset] = (char)connectionId;
|
|
userBytes[PapCommandTypeOffset] = PapTickleCommand;
|
|
userBytes[PapSequenceNumberOffset] = 0;
|
|
userBytes[PapSequenceNumberOffset + 1] = 0;
|
|
if (AtpPostRequest(activeJobInfo->ourSocket,
|
|
activeJobInfo->theirAddress,
|
|
Empty,
|
|
empty, 0, userBytes, False, empty, 0, empty,
|
|
AtpInfiniteRetries, PapTickleSeconds,
|
|
ThirtySecondsTRelTimer, empty,
|
|
(long unsigned)0) isnt ATnoError)
|
|
ErrorLog("IncomingOpenReply", ISevError, __LINE__, UnknownPort,
|
|
IErrPapBadTickleStart, IMsgPapBadTickleStart,
|
|
Insert0());
|
|
|
|
/* Post a few get request handlers on this connection (for incoming
|
|
tickle's, close's, or sendData's). */
|
|
|
|
for (index = 0; index < PapPendingReadsPerJob; index += 1)
|
|
{
|
|
if (AtpEnqueueRequestHandler(empty, activeJobInfo->ourSocket,
|
|
empty, 0, empty,
|
|
IncomingRequestPacket,
|
|
(long unsigned)jobRefNum) isnt ATnoError)
|
|
ErrorLog("IncomingOpenReply", ISevError, __LINE__, UnknownPort,
|
|
IErrPapBadRequestHandler, IMsgPapBadRequestHandler,
|
|
Insert0());
|
|
}
|
|
|
|
/* Start the connection timer... */
|
|
|
|
activeJobInfo->lastContactTime = CurrentRelativeTime();
|
|
activeJobInfo->connectionTimerId =
|
|
StartTimer(ConnectionTimerExpired, PapConnectionSeconds,
|
|
sizeof(long), (char *)&jobRefNum);
|
|
|
|
/* We're radio-active now! */
|
|
|
|
if (openJobInfo->freeOpaqueDataDescriptor)
|
|
FreeOpaqueDataDescriptor(openJobInfo->opaqueDataDescriptor);
|
|
if (not UnlinkOpenJobInfo(openJobInfo))
|
|
UnlinkOpenJobInfo(openJobInfo);
|
|
if (completionInfo->freeOpaqueResponse)
|
|
FreeOpaqueDataDescriptor(completionInfo->opaqueResponse);
|
|
Free(completionInfo);
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
(*completionRoutine)(ATnoError, userData, jobRefNum, serverFlowQuantum,
|
|
usersOpaqueStatusBuffer, statusBufferLength);
|
|
return;
|
|
|
|
} /* IncomingOpenReply */
|
|
|
|
ExternForVisibleFunction void far
|
|
IncomingReadComplete(AppleTalkErrorCode errorCode,
|
|
long unsigned incomingUserData,
|
|
AppleTalkAddress source,
|
|
void far *opaqueResponseBuffer,
|
|
int responseBufferSize,
|
|
char far *responseUserBytes,
|
|
short unsigned transactionId)
|
|
{
|
|
PapCompletionInfo completionInfo = (PapCompletionInfo)incomingUserData;
|
|
long jobRefNum = completionInfo->jobRefNum;
|
|
PapReadComplete *completionRoutine =
|
|
(PapReadComplete *)completionInfo->completionRoutine;
|
|
long unsigned userData = completionInfo->userData;
|
|
ActiveJobInfo activeJobInfo;
|
|
Boolean eofFlag;
|
|
|
|
/* Touch unused formals... */
|
|
|
|
source, transactionId;
|
|
|
|
/* We've already pulled all of the good stuff out of the completion node,
|
|
so lets free it now. */
|
|
|
|
if (completionInfo->freeOpaqueResponse)
|
|
FreeOpaqueDataDescriptor(completionInfo->opaqueResponse);
|
|
Free(completionInfo);
|
|
if (errorCode is ATsocketClosed)
|
|
{
|
|
(*completionRoutine)(errorCode, userData, jobRefNum, empty, 0, False);
|
|
return;
|
|
}
|
|
|
|
DeferTimerChecking();
|
|
DeferAtpPackets();
|
|
|
|
/* Before we look at the incoming error code, see if we can mark in the active
|
|
job structure that the other sides sendData is complete. */
|
|
|
|
activeJobInfo = FindActiveJobInfoFor(jobRefNum);
|
|
if (activeJobInfo is empty or activeJobInfo->closing)
|
|
{
|
|
if (activeJobInfo is Empty and errorCode isnt ATatpTransactionAborted)
|
|
ErrorLog("IncomingReadComplete", ISevError, __LINE__, UnknownPort,
|
|
IErrPapActiveJobMissing, IMsgPapActiveJobMissing,
|
|
Insert0());
|
|
else
|
|
UnlinkActiveJobInfo(activeJobInfo);
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
(*completionRoutine)(errorCode, userData, jobRefNum, empty, 0, False);
|
|
return;
|
|
}
|
|
activeJobInfo->outgoingSendDataPending = False;
|
|
if (errorCode isnt ATnoError)
|
|
{
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
(*completionRoutine)(errorCode, userData, jobRefNum, empty, 0, False);
|
|
return;
|
|
}
|
|
|
|
/* Sometimes the Mac forgets to Tickle when its just responding to SendData's
|
|
from a PAP server. So, when a read completes, tag that we have heard
|
|
something from the other side... */
|
|
|
|
activeJobInfo->lastContactTime = CurrentRelativeTime();
|
|
|
|
/* A little error checking, just for fun. */
|
|
|
|
if ((unsigned char)responseUserBytes[PapConnectionIdOffset] isnt
|
|
activeJobInfo->connectionId or
|
|
responseUserBytes[PapCommandTypeOffset] isnt PapDataCommand)
|
|
{
|
|
ErrorLog("IncomingReadComplete", ISevWarning, __LINE__, UnknownPort,
|
|
IErrPapBadUserBytes, IMsgPapBadUserBytes,
|
|
Insert0());
|
|
UnlinkActiveJobInfo(activeJobInfo);
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
(*completionRoutine)(ATinternalError, userData, jobRefNum, empty,
|
|
0, False);
|
|
return;
|
|
}
|
|
|
|
/* Looks fine to us... */
|
|
|
|
eofFlag = (responseUserBytes[PapEofFlagOffset] isnt 0);
|
|
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
(*completionRoutine)(ATnoError, userData, jobRefNum, opaqueResponseBuffer,
|
|
responseBufferSize, eofFlag);
|
|
UnlinkActiveJobInfo(activeJobInfo);
|
|
return;
|
|
|
|
} /* IncomingReadComplete */
|
|
|
|
ExternForVisibleFunction void PostSendDataResponse(ActiveJobInfo activeJobInfo)
|
|
{
|
|
AppleTalkErrorCode errorCode;
|
|
char userBytes[AtpUserBytesSize];
|
|
PapCompletionInfo completionInfo;
|
|
|
|
/* We should only be called from cluey sources... */
|
|
|
|
if (not activeJobInfo->incomingSendDataPending or
|
|
not activeJobInfo->writeDataWaiting)
|
|
{
|
|
ErrorLog("PostSendDataResponse", ISevError, __LINE__, UnknownPort,
|
|
IErrPapWhyAreWeHere, IMsgPapWhyAreWeHere,
|
|
Insert0());
|
|
return;
|
|
}
|
|
|
|
/* Our function in life is to post the actual ATP response and set up
|
|
a handler for the release. Build up completion info and user bytes. */
|
|
|
|
completionInfo = (PapCompletionInfo)Calloc(sizeof(*completionInfo), 1);
|
|
if (completionInfo is empty)
|
|
{
|
|
ErrorLog("PostSendDataResponse", ISevError, __LINE__, UnknownPort,
|
|
IErrPapOutOfMemory, IMsgPapOutOfMemory,
|
|
Insert0());
|
|
AtpCancelResponse(activeJobInfo->ourSocket,
|
|
activeJobInfo->theirAddress,
|
|
activeJobInfo->incomingSendDataTransactionId, False);
|
|
TakeLock(PapLock);
|
|
activeJobInfo->incomingSendDataPending = False;
|
|
activeJobInfo->writeDataWaiting = False;
|
|
ReleaseLock(PapLock);
|
|
(*activeJobInfo->writeCompletionRoutine)(AToutOfMemory,
|
|
activeJobInfo->writeUserData,
|
|
activeJobInfo->jobRefNum);
|
|
return;
|
|
}
|
|
completionInfo->completionRoutine =
|
|
(void *)activeJobInfo->writeCompletionRoutine;
|
|
completionInfo->userData = activeJobInfo->writeUserData;
|
|
completionInfo->jobRefNum = activeJobInfo->jobRefNum;
|
|
userBytes[PapConnectionIdOffset] = (char)activeJobInfo->connectionId;
|
|
userBytes[PapCommandTypeOffset] = PapDataCommand;
|
|
userBytes[PapEofFlagOffset] = (char)(activeJobInfo->writeEofFlag);
|
|
userBytes[PapEofFlagOffset + 1] = 0;
|
|
|
|
/* Post the response. */
|
|
|
|
errorCode = AtpPostResponse(activeJobInfo->ourSocket,
|
|
activeJobInfo->incomingSendDataSource,
|
|
activeJobInfo->incomingSendDataTransactionId,
|
|
activeJobInfo->writeOpaqueBuffer,
|
|
activeJobInfo->writeBufferSize, userBytes,
|
|
activeJobInfo->incomingSendDataExactlyOnce,
|
|
PapIncomingRelease,
|
|
(long unsigned)completionInfo);
|
|
if (errorCode isnt ATnoError)
|
|
{
|
|
if (completionInfo->freeOpaqueResponse)
|
|
FreeOpaqueDataDescriptor(completionInfo->opaqueResponse);
|
|
Free(completionInfo);
|
|
ErrorLog("PostSendDataResponse", ISevError, __LINE__, UnknownPort,
|
|
IErrPapBadResponseSend, IMsgPapBadResponseSend,
|
|
Insert0());
|
|
TakeLock(PapLock);
|
|
activeJobInfo->incomingSendDataPending = False;
|
|
activeJobInfo->writeDataWaiting = False;
|
|
ReleaseLock(PapLock);
|
|
(*activeJobInfo->writeCompletionRoutine)(errorCode,
|
|
activeJobInfo->writeUserData,
|
|
activeJobInfo->jobRefNum);
|
|
return;
|
|
}
|
|
|
|
/* All set. */
|
|
|
|
return;
|
|
|
|
} /* PostSendDataResponse */
|
|
|
|
ExternForVisibleFunction void far
|
|
PapIncomingRelease(AppleTalkErrorCode errorCode,
|
|
long unsigned incomingUserData,
|
|
AppleTalkAddress source,
|
|
short unsigned transactionId)
|
|
{
|
|
PapCompletionInfo completionInfo = (PapCompletionInfo)incomingUserData;
|
|
PapWriteComplete *completionRoutine =
|
|
(PapWriteComplete *)completionInfo->completionRoutine;
|
|
long unsigned userData = completionInfo->userData;
|
|
long jobRefNum = completionInfo->jobRefNum;
|
|
ActiveJobInfo activeJobInfo;
|
|
|
|
/* Touch unused formals... */
|
|
|
|
source, transactionId;
|
|
|
|
/* Okay, call the completion... we don't really care about the reason.
|
|
Mark the write as complete first. */
|
|
|
|
if (completionInfo->freeOpaqueResponse)
|
|
FreeOpaqueDataDescriptor(completionInfo->opaqueResponse);
|
|
Free(completionInfo);
|
|
if (errorCode is ATsocketClosed)
|
|
{
|
|
(*completionRoutine)(errorCode, userData, jobRefNum);
|
|
return;
|
|
}
|
|
|
|
DeferTimerChecking();
|
|
DeferAtpPackets();
|
|
|
|
activeJobInfo = FindActiveJobInfoFor(jobRefNum);
|
|
if (activeJobInfo is empty)
|
|
{
|
|
if (activeJobInfo is Empty and errorCode isnt ATatpTransactionAborted)
|
|
ErrorLog("PapIncomingRelease", ISevError, __LINE__, UnknownPort,
|
|
IErrPapLostActiveNode, IMsgPapLostActiveNode,
|
|
Insert0());
|
|
}
|
|
else
|
|
{
|
|
TakeLock(PapLock);
|
|
activeJobInfo->incomingSendDataPending = False;
|
|
activeJobInfo->writeDataWaiting = False;
|
|
ReleaseLock(PapLock);
|
|
}
|
|
|
|
UnlinkActiveJobInfo(activeJobInfo);
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
(*completionRoutine)(errorCode, userData, jobRefNum);
|
|
return;
|
|
|
|
} /* PapIncomingRelease */
|
|
|
|
ExternForVisibleFunction SlsResponseCompletionCookie
|
|
NewSlsResponseCompletionCookie(void)
|
|
{
|
|
SlsResponseCompletionCookie cookie;
|
|
|
|
/* Allocate buffering and make "opaque descriptor" for posting an Sls
|
|
Atp reponse. */
|
|
|
|
if ((cookie = Malloc(sizeof(*cookie))) is Empty or
|
|
(cookie->opaquePapData =
|
|
MakeOpaqueDataDescriptor(cookie->papData,
|
|
PapMaximumDataPacketSize,
|
|
&cookie->freeOpaquePapData)) is Empty)
|
|
{
|
|
if (cookie isnt Empty)
|
|
Free(cookie);
|
|
ErrorLog("NewSlsResponseCompletionCookie", ISevError, __LINE__,
|
|
UnknownPort, IErrPapOutOfMemory, IMsgPapOutOfMemory,
|
|
Insert0());
|
|
return(Empty);
|
|
}
|
|
|
|
return(cookie);
|
|
|
|
} /* NewSlsResponseCompletionCookie */
|
|
|
|
ExternForVisibleFunction void
|
|
FreeSlsResponseCompletionCookie(SlsResponseCompletionCookie cookie)
|
|
{
|
|
/* Free above buffering. */
|
|
|
|
if (cookie->freeOpaquePapData)
|
|
FreeOpaqueDataDescriptor(cookie->opaquePapData);
|
|
Free(cookie);
|
|
return;
|
|
|
|
} /* FreeSlsResponseCompletionCookie */
|
|
|
|
ExternForVisibleFunction void far
|
|
SlsIncomingRelease(AppleTalkErrorCode errorCode,
|
|
long unsigned incomingUserData,
|
|
AppleTalkAddress source,
|
|
short unsigned transactionId)
|
|
{
|
|
SlsResponseCompletionCookie cookie =
|
|
(SlsResponseCompletionCookie)incomingUserData;
|
|
|
|
/* An Sls AtpPostResponse completed... free the buffering. */
|
|
|
|
FreeSlsResponseCompletionCookie(cookie);
|
|
return;
|
|
|
|
} /* SlsIncomingRelease */
|
|
|
|
ExternForVisibleFunction ServiceListenerInfo far FindServiceListenerInfoFor(
|
|
long serviceListenerRefNum)
|
|
{
|
|
ServiceListenerInfo serviceListenerInfo;
|
|
|
|
TakeLock(PapLock);
|
|
for (serviceListenerInfo = serviceListenerInfoHead;
|
|
serviceListenerInfo isnt empty;
|
|
serviceListenerInfo = serviceListenerInfo->next)
|
|
if (serviceListenerInfo->serviceListenerRefNum is serviceListenerRefNum)
|
|
{
|
|
Link(serviceListenerInfo);
|
|
ReleaseLock(PapLock);
|
|
return(serviceListenerInfo);
|
|
}
|
|
ReleaseLock(PapLock);
|
|
return(Empty);
|
|
|
|
} /* FindServiceListenerInfoFor */
|
|
|
|
ExternForVisibleFunction void far
|
|
UnlinkServiceListenerInfo(ServiceListenerInfo serviceListenerInfo)
|
|
{
|
|
AppleTalkErrorCode errorCode;
|
|
|
|
if (serviceListenerInfo is Empty)
|
|
return;
|
|
|
|
/* Are we the last referant? */
|
|
|
|
TakeLock(PapLock);
|
|
if (not UnlinkNoFree(serviceListenerInfo))
|
|
{
|
|
ReleaseLock(PapLock);
|
|
return;
|
|
}
|
|
|
|
/* Yes, remove the service listener from the master linked list. */
|
|
|
|
if (RemoveFromListNoUnlink(serviceListenerInfoHead, serviceListenerInfo,
|
|
next) is Empty)
|
|
{
|
|
ReleaseLock(PapLock);
|
|
ErrorLog("UnlinkServiceListenerInfo", ISevError, __LINE__, UnknownPort,
|
|
IErrPapSLGonePoof, IMsgPapSLGonePoof,
|
|
Insert0());
|
|
}
|
|
else
|
|
ReleaseLock(PapLock);
|
|
|
|
/* Close the ATP socket that the service listener is open on. */
|
|
|
|
if (serviceListenerInfo->closeSocket)
|
|
{
|
|
if ((errorCode = AtpCloseSocketOnNode(serviceListenerInfo->socket,
|
|
PapSlCloseComplete,
|
|
(long unsigned)serviceListenerInfo))
|
|
isnt ATnoError)
|
|
PapSlCloseComplete(errorCode, (long unsigned)serviceListenerInfo, 0);
|
|
}
|
|
else
|
|
PapSlCloseComplete(ATnoError, (long unsigned)serviceListenerInfo, 0);
|
|
|
|
/* All set. */
|
|
|
|
return;
|
|
|
|
} /* UnlinkServiceListenerInfo */
|
|
|
|
ExternForVisibleFunction void far
|
|
PapSlCloseComplete(AppleTalkErrorCode errorCode,
|
|
long unsigned userData,
|
|
long cookie)
|
|
{
|
|
ServiceListenerInfo serviceListenerInfo = (ServiceListenerInfo)userData;
|
|
|
|
/* If there is a completion routine, call it. */
|
|
|
|
if (serviceListenerInfo->closeContext.closeCompletionRoutine isnt Empty)
|
|
(*(serviceListenerInfo->closeContext.closeCompletionRoutine))(
|
|
errorCode, serviceListenerInfo->closeContext.closeUserData,
|
|
serviceListenerInfo->serviceListenerRefNum);
|
|
|
|
/* Finally free the ServiceListenerInfo and flee. */
|
|
|
|
Free(serviceListenerInfo);
|
|
return;
|
|
|
|
} /* PapSlCloseComplete */
|
|
|
|
ExternForVisibleFunction void far
|
|
UnlinkActiveJobInfo(ActiveJobInfo activeJobInfo)
|
|
{
|
|
long index;
|
|
AppleTalkErrorCode errorCode;
|
|
|
|
if (activeJobInfo is Empty)
|
|
return;
|
|
|
|
/* Are we the last referant? */
|
|
|
|
TakeLock(PapLock);
|
|
if (not UnlinkNoFree(activeJobInfo))
|
|
{
|
|
ReleaseLock(PapLock);
|
|
return;
|
|
}
|
|
|
|
/* Yes, remove from activeJob list. */
|
|
|
|
CheckMod(index, activeJobInfo->jobRefNum, NumberOfActiveJobHashBuckets,
|
|
"UnlinkActiveJobInfo");
|
|
if (RemoveFromListNoUnlink(activeJobHashBuckets[index], activeJobInfo,
|
|
next) is Empty)
|
|
ErrorLog("UnlinkServiceListenerInfo", ISevError, __LINE__, UnknownPort,
|
|
IErrPapJobGonePoof, IMsgPapJobGonePoof,
|
|
Insert0());
|
|
|
|
/* If we're a server job, we also need to remove this guy from the active
|
|
job list hanging off the service listener. */
|
|
|
|
if (activeJobInfo->serverJob)
|
|
if (RemoveFromListNoUnlink(activeJobInfo->ourServiceListener->activeJobList,
|
|
activeJobInfo, nextForMyServiceListener) is
|
|
Empty)
|
|
ErrorLog("UnlinkServiceListenerInfo", ISevError, __LINE__, UnknownPort,
|
|
IErrPapJobGonePoof, IMsgPapJobGonePoof,
|
|
Insert0());
|
|
ReleaseLock(PapLock);
|
|
|
|
/* If a PapWrite has been posted, but no "sendData" has been recieved yet,
|
|
we should simulate that completion... */
|
|
|
|
if (activeJobInfo->writeDataWaiting and
|
|
not activeJobInfo->incomingSendDataPending)
|
|
(*activeJobInfo->writeCompletionRoutine)(ATatpTransactionAborted,
|
|
activeJobInfo->writeUserData,
|
|
activeJobInfo->jobRefNum);
|
|
|
|
/* Close the ATP socket. */
|
|
|
|
if (activeJobInfo->closeOurSocket)
|
|
{
|
|
if ((errorCode = AtpCloseSocketOnNode(activeJobInfo->ourSocket,
|
|
PapAjCloseComplete,
|
|
(long unsigned)activeJobInfo)) isnt
|
|
ATnoError)
|
|
{
|
|
ErrorLog("PapCloseJob", ISevError, __LINE__, UnknownPort,
|
|
IErrPapBadSocketClose, IMsgPapBadSocketClose,
|
|
Insert0());
|
|
PapAjCloseComplete(errorCode, (long unsigned)activeJobInfo, 0);
|
|
}
|
|
}
|
|
else
|
|
PapAjCloseComplete(ATnoError, (long unsigned)activeJobInfo, 0);
|
|
|
|
/* All set. */
|
|
|
|
return;
|
|
|
|
} /* UnlinkActiveJobInfo */
|
|
|
|
ExternForVisibleFunction void far
|
|
PapAjCloseComplete(AppleTalkErrorCode errorCode,
|
|
long unsigned userData,
|
|
long cookie)
|
|
{
|
|
ActiveJobInfo activeJobInfo = (ActiveJobInfo)userData;
|
|
|
|
/* Call the active job, close complete routine, if any. */
|
|
|
|
if (activeJobInfo->closeJobRoutine isnt empty)
|
|
(*activeJobInfo->closeJobRoutine)(activeJobInfo->closeCode,
|
|
activeJobInfo->closeJobUserData,
|
|
activeJobInfo->jobRefNum);
|
|
|
|
/* If there is a completion routine, call it. */
|
|
|
|
if (activeJobInfo->closeContext.closeCompletionRoutine isnt Empty)
|
|
(*(activeJobInfo->closeContext.closeCompletionRoutine))(
|
|
errorCode, activeJobInfo->closeContext.closeUserData,
|
|
activeJobInfo->jobRefNum);
|
|
|
|
/* If we were a server job, we had a link to our service listener. */
|
|
|
|
if (activeJobInfo->serverJob)
|
|
UnlinkServiceListenerInfo(activeJobInfo->ourServiceListener);
|
|
|
|
/* Finally free the ActiveJob and flee. */
|
|
|
|
Free(activeJobInfo);
|
|
return;
|
|
|
|
} /* PapAjCloseComplete */
|
|
|
|
ExternForVisibleFunction Boolean far
|
|
UnlinkOpenJobInfo(OpenJobInfo openJobInfo)
|
|
{
|
|
/* Are we the last referant? */
|
|
|
|
TakeLock(PapLock);
|
|
if (not UnlinkNoFree(openJobInfo))
|
|
{
|
|
ReleaseLock(PapLock);
|
|
return(False);
|
|
}
|
|
|
|
/* Yes, remove from the list. */
|
|
|
|
if (RemoveFromListNoUnlink(openJobInfoHead, openJobInfo, next) is Empty)
|
|
ErrorLog("UnlinkOpenJobInfo", ISevError, __LINE__, UnknownPort,
|
|
IErrPapLostNode, IMsgPapLostNode,
|
|
Insert0());
|
|
ReleaseLock(PapLock);
|
|
|
|
/* All set. */
|
|
|
|
Free(openJobInfo);
|
|
return(True);
|
|
|
|
} /* UnlinkOpenJobInfo */
|