Add an option to tell the server that the GS is going to sleep after each command sent.

This tells the server to keep the session alive (typically for at least several hours) without sending or expecting tickles. This is useful in cases where our run queue procedure is not run regularly or at all, e.g. in text-mode shells.

The implementation here is geared toward compatibility with Netatalk and differs from Apple's published standards in a couple ways. It may or may not work with other servers.
This commit is contained in:
Stephen Heumann 2017-04-21 21:09:57 -05:00
parent 2cb7cc7572
commit 78a300f799
10 changed files with 115 additions and 2 deletions

View File

@ -50,6 +50,7 @@
#define afpOverTCPOptionsItem 301
#define useLargeReadsItem 302
#define forceAFP22Item 303
#define fakeSleepItem 304
#define fstMissingError 3000
#define noEasyMountError 3001
@ -492,6 +493,18 @@ void DoHit(long ctlID, CtlRecHndl ctlHandle)
} else if (menuItem == forceAFP22Item) {
flags ^= fForceAFP22;
CheckMItem((flags & fForceAFP22) ? TRUE : FALSE, forceAFP22Item);
/* Fake sleep is allowed only with AFP 2.2. */
if (flags & fForceAFP22) {
EnableMItem(fakeSleepItem);
} else {
DisableMItem(fakeSleepItem);
CheckMItem(FALSE, fakeSleepItem);
flags &= ~fFakeSleep;
}
} else if (menuItem == fakeSleepItem) {
flags ^= fFakeSleep;
CheckMItem((flags & fFakeSleep) ? TRUE : FALSE, fakeSleepItem);
}
SetCtlValue(afpOverTCPOptionsItem, ctlHandle);

View File

@ -97,6 +97,7 @@ resource rIcon (1) {
#define afpOverTCPOptionsItem 301
#define useLargeReadsItem 302
#define forceAFP22Item 303
#define fakeSleepItem 304
/*
* Controls in the control panel window (for 640 mode or 320 mode)
@ -244,7 +245,7 @@ resource rMenu (optionsMenu) {
optionsMenu, /* menu ID */
refIsResource*menuTitleRefShift + refIsResource*itemRefShift,
optionsMenu, /* menu title ref (not drawn) */
{afpOverTCPOptionsItem, useLargeReadsItem, forceAFP22Item};
{afpOverTCPOptionsItem, useLargeReadsItem, forceAFP22Item, fakeSleepItem};
};
resource rPString(optionsMenu,noCrossBank) { "" };
@ -275,6 +276,15 @@ resource rMenuItem (forceAFP22Item) {
};
resource rPString(forceAFP22Item,noCrossBank) { "Force AFP Version 2.2" };
resource rMenuItem (fakeSleepItem) {
fakeSleepItem, /* menu item ID */
"","",
0,
fDisabled+refIsResource*itemTitleRefShift,
fakeSleepItem /* menu item title ref */
};
resource rPString(fakeSleepItem,noCrossBank) { "Fake Sleep to Keep Alive" };
/*
* Controls in the help window
*/

View File

@ -9,5 +9,7 @@ AFPOptions afpOptions[] =
{"AFP over TCP (LargeReads)", fLargeReads},
{"AFP over TCP (AFP2.2)", fForceAFP22},
{"AFP over TCP (LargeReads,AFP2.2)", fLargeReads | fForceAFP22},
{"AFP over TCP (AFP2.2,FakeSleep)", fForceAFP22 | fFakeSleep},
{"AFP over TCP (LR,AFP2.2,FS)", fLargeReads | fForceAFP22 | fFakeSleep},
{0, 0}
};

View File

@ -3,6 +3,7 @@
#define fLargeReads 0x0001
#define fForceAFP22 0x0002
#define fFakeSleep 0x0004
typedef struct AFPOptions {
char *zoneString;

View File

@ -28,6 +28,7 @@ typedef struct FPReadRec {
#define kFPRead 27
#define kFPLogin 18
#define kFPZzzzz 122
/* For forced AFP 2.2 login */
static Byte loginBuf[100];
@ -41,6 +42,7 @@ static void DoSPCommand(Session *sess, ASPCommandRec *commandRec);
static void DoSPWrite(Session *sess, ASPWriteRec *commandRec);
static void CompleteASPCommand(SPCommandRec *commandRec, Word result);
static void InitSleepRec(Session *sess, Byte sessRefNum);
Session sessionTbl[MAX_SESSIONS];
@ -54,6 +56,7 @@ LongWord DispatchASPCommand(SPCommandRec *commandRec) {
stateReg = ForceRomIn();
top:
if (commandRec->command == aspGetStatusCommand
|| commandRec->command==aspOpenSessionCommand)
{
@ -84,6 +87,7 @@ LongWord DispatchASPCommand(SPCommandRec *commandRec) {
sess = &sessionTbl[i];
sess->spCommandRec = commandRec;
sess->commandPending = TRUE;
InitSleepRec(sess, i + SESSION_NUM_START);
if ((i = StartTCPConnection(sess)) != 0) {
FlagFatalError(sess, i);
@ -186,7 +190,30 @@ LongWord DispatchASPCommand(SPCommandRec *commandRec) {
}
}
ret:
ret:
/*
* If requested by user, we tell the server we are "going to sleep"
* after every command we send. This avoids having the server
* disconnect us because we can't send tickles for a while.
*
* This implementation is designed to work with Netatalk:
* -Apple's docs say FPZzzzz was introduced with AFP 2.3, but Netatalk
* supports it with AFP 2.2 and doesn't support AFP 2.3 at all.
* -Apple's docs also say there is no reply to FPZzzzz, but Netatalk
* send a reply with four bytes of data.
* Netatalk includes comments indicating Apple's implementation in OS X
* may really work more like Netatalk, but I haven't checked this.
*/
if ((sess->atipMapping.flags & fFakeSleep)
&& sess->loggedIn && commandRec->result == 0
&& (commandRec->command == aspCommandCommand
|| commandRec->command == aspWriteCommand)
&& commandRec != (SPCommandRec*)&sess->sleepCommandRec)
{
commandRec = (SPCommandRec*)&sess->sleepCommandRec;
goto top;
}
RestoreStateReg(stateReg);
return 0;
@ -454,6 +481,19 @@ void CallAttentionRoutine(Session *sess, Byte attenType, Word atten) {
CallCompletionRoutine((void *)(sess->attention + 1));
}
static void InitSleepRec(Session *sess, Byte sessRefNum) {
sess->sleepCommandRec.async = 0;
sess->sleepCommandRec.command = aspCommandCommand;
sess->sleepCommandRec.completionPtr = 0;
sess->sleepCommandRec.refNum = sessRefNum;
sess->sleepCommandRec.cmdBlkLength = sizeof(sess->fpZzzzzRec);
sess->sleepCommandRec.cmdBlkAddr = (LongWord)&sess->fpZzzzzRec;
sess->sleepCommandRec.replyBufferLen = sizeof(sess->fpZzzzzResponseRec);
sess->sleepCommandRec.replyBufferAddr = (LongWord)&sess->fpZzzzzResponseRec;
sess->fpZzzzzRec.CommandCode = kFPZzzzz;
sess->fpZzzzzRec.Flag = 0;
}
void PollAllSessions(void) {
unsigned int i;

View File

@ -88,6 +88,19 @@ jslOldPFIListSessions2 entry
rtl
end
pfiLoginContCmdProc start
lda cmdRecPtr+2
pha
lda cmdRecPtr
pha
jslOldPFILoginCont entry
jsl jslOldPFILoginCont ; to be changed
rep #$30 ; ensure full native mode
jsl PostLoginCont
rtl
end
CallCompletionRoutine start
phb
jsl ForceLCBank2 ;use LC bank 2

View File

@ -7,6 +7,8 @@ void pfiLoginCmdProc(void);
extern LongWord jslOldPFILogin;
void pfiLogin2CmdProc(void);
extern LongWord jslOldPFILogin2;
void pfiLoginContCmdProc(void);
extern LongWord jslOldPFILoginCont;
void pfiListSessions2CmdProc(void);
extern LongWord jslOldPFIListSessions2;
void CallCompletionRoutine(void *);

View File

@ -21,6 +21,7 @@ NewCmd newCmds[] = {
{nbpLookupNameCommand, nbpCmdProc, NULL},
{pfiLoginCommand, pfiLoginCmdProc, &jslOldPFILogin},
{pfiLogin2Command, pfiLogin2CmdProc, &jslOldPFILogin2},
{pfiLoginContCommand, pfiLoginContCmdProc, &jslOldPFILoginCont},
{pfiListSessions2Command, pfiListSessions2CmdProc, &jslOldPFIListSessions2},
{0, 0, 0}
};

View File

@ -87,6 +87,10 @@ void SaveNames(PFILogin2Rec *commandRec) {
goto ret;
}
if (commandRec->result == 0 && commandRec->sessRefID >= SESSION_NUM_START) {
sessionTbl[commandRec->sessRefID - SESSION_NUM_START].loggedIn = TRUE;
}
if (commandRec->sessRefID <= MAX_ASP_SESSION_NUM) {
nameRec = &aspSessionNames[commandRec->sessRefID];
aspActiveFlags[commandRec->sessRefID] = 1;
@ -154,3 +158,16 @@ ret:
}
#pragma databank 0
#pragma databank 1
void PostLoginCont(PFILoginContRec *commandRec) {
Word stateReg;
stateReg = ForceRomIn();
if (commandRec->result == 0 && commandRec->sessRefID >= SESSION_NUM_START) {
sessionTbl[commandRec->sessRefID - SESSION_NUM_START].loggedIn = TRUE;
}
RestoreStateReg(stateReg);
}
#pragma databank 0

View File

@ -2,6 +2,7 @@
#define SESSION_H
#include <types.h>
#include <appletalk.h>
#include "dsiproto.h"
#include "atipmapping.h"
@ -23,6 +24,11 @@ typedef enum DSISessionStatus {
needsReset /* set after control-reset in P8 mode */
} DSISessionStatus;
typedef struct FPZzzzzRec {
Word CommandCode; /* includes pad byte */
LongWord Flag;
} FPZzzzzRec;
typedef struct Session {
/* Marinetti TCP connection status */
Word ipid;
@ -62,6 +68,14 @@ typedef struct Session {
/* Attention routine header (followed by the routine) */
ASPAttentionHeaderRec *attention;
/* Is this session successfully logged in? */
Boolean loggedIn;
/* Records for sending (fake) sleep messages to server */
ASPCommandRec sleepCommandRec;
FPZzzzzRec fpZzzzzRec;
Byte fpZzzzzResponseRec[4];
} Session;
#endif