sys7.1-doc-wip/ProcessMgr/Aux.c

573 lines
14 KiB
C

/*
File: Aux.c
Contains: Process Mgr support for A/UX.
NOTE: None of the code in this file is referenced unless the build
defines the HAS_AUX_PROCESSMGR condition.
Written by: David Harrison
Copyright: © 1980-1992 by Apple Computer, Inc., all rights reserved.
Change History (most recent first):
<5> 8/26/92 DTY Roll in latest changes for A/UX.
<4> 4/1/91 DFH (with John Iarocci) Bring this file up to date.
<2> 12/5/90 DFH Fixed warning in AUX_Block re: unused param.
<0> 12/4/90 DFH New Today.
*/
#include <Files.h>
#include <QuickDraw.h>
#include <Resources.h>
#include <Retrace.h>
#include <Errors.h>
#include <MFPrivate.h>
#include "Data.h"
#include "OSDispatchPrivate.h"
#include "Glue.h"
#include "SysMisc.h"
#include "Aux.h"
#include "Patches.h"
#include "Lomem.h"
/* Variables local to this module */
static short auxMaxSelector = -1;
static long ProcessMgrTaskID = -1;
static Getnextevent_ptr realGNE;
static Eventavail_ptr realEA;
static AUXDispatch_ptr realAD;
/* Some function prototypes that should be in (yet another) header file */
OSErr NewProcess(InternalLaunchPBPtr pParams);
short GetNextPseudoEvent(short, EventRecord *, Boolean);
void OpenAppWDs(PEntryPtr);
/* Handy macro to determine whether given event is best handled by AUX. */
#define IsAUXEvent(pEvent) ((pEvent->what >= FIRST_AUX_EVENT) && (pEvent->what <= LAST_AUX_EVENT))
#pragma segment AUX_SEGMENT
/* AUX_Setup. Set things up as necessary for A/UX. */
void
AUX_Setup(void)
{
/* Determine level of functionality. This check can probably go away once
* we settle on a common version of A/UX
*/
auxMaxSelector = AUX_Highest();
/* Set extra patches so we can fool ourselves */
realGNE = (Getnextevent_ptr) NGetTrapAddress(0xa970, ToolTrap);
NSetTrapAddress((long) AUX_GetNextEvent, 0xa970, ToolTrap);
realEA = (Eventavail_ptr) NGetTrapAddress(0xa971, ToolTrap);
NSetTrapAddress((long) AUX_EventAvail, 0xa971, ToolTrap);
realAD = (AUXDispatch_ptr) NGetTrapAddress(0xabf9, ToolTrap);
NSetTrapAddress((long) AUX_Dispatch_Patch, 0xabf9, ToolTrap);
}
/* AUX_EventAvail. Similar to our replacement for getnextevent, we also replace
* EventAvail for usage by Process Mgr only.
*/
pascal short
AUX_EventAvail(short evtMask, EventRecord *theEvent)
{
Boolean oldMask;
EventRecord anEvent;
oldMask = AUX_SetAUXMask(true);
for (;;)
{
(void) (*realEA)(evtMask, theEvent);
if (IsAUXEvent(theEvent) == false)
break;
/* if an A/UX event is available, remove it from the queue and deal with it */
AUX_GetAnyEventGlue(0, &anEvent, 0, 0, true);
(void) AUX_HandleEvent(&anEvent);
};
(void) AUX_SetAUXMask(oldMask);
return theEvent->what == nullEvent ? wFalse : wTrue;
}
/* AUX_Dispatch_Patch. Patch AUX_Dispatch so we can look for a AUX_BEGINAPPLICATION
* selector and execute within the ProcessManagers context
*/
pascal long
AUX_Dispatch_Patch(short selector, char *p)
{
unsigned long olda5;
long retval;
olda5 = ProcessMgrA5SimpleSetup();
if (selector != AUX_BEGINAPPLICATION)
retval = (*realAD)(selector, p);
else
/* Fix up working directories for home directory and files in APPPARMHANDLE */
OpenAppWDs(pCurrentProcess);
A5SimpleRestore(olda5);
return(retval);
}
/* AUX_Block. */
void
AUX_Block(PEntryPtr)
{
}
/* AUX_CoffLaunch. If a coff binary toolbox program is executed, it will first issue a
* UI_ATTACH ioctl, which will send us an attachEvt. Upon seeing the attach event, we
* use AUX_MakeLayer to create a minimal process which can then be switched to using the
* normal context switching code. The coff binary will then call (via OSDispatch) this
* routine to really fill the process out. All this is necessary because the coff
* program must execute in a separate A/UX process and it's accesses to the shared data
* areas must be synchronized.
*/
pascal short
AUX_CoffLaunch(u_long reqSize, u_long taskmode, StringPtr procName, short vrefnum, u_long ssize)
{
#pragma unused (reqSize, taskmode, procName, vrefnum, ssize)
return pCurrentProcess->p_mypid;
}
/* AUX_FindProc. Find the process associated with taskId. */
PEntryPtr
AUX_FindProc(taskId)
{
PEntryPtr pProc;
pProc = pProcessList;
while (pProc != nil)
{
if (pProc->aux_realpid == taskId)
break;
pProc = pProc->p_NextProcess;
}
return pProc;
}
/* AUX_ForkExec. Fork and exec a new coff binary. */
short
AUX_ForkExecGlue(short vrefnum, char *name)
{
ForkExecRec params;
assert(AUX_FORKEXEC <= auxMaxSelector);
params.vrefnum = vrefnum;
params.name = name;
if (AUX_ForkExec(&params) != 0)
return permErr;
else
return params.pid;
}
/* AUX_GetAnyEventGlue. Handy routine for calling AUX_GetAnyEvent, since latter is
* rather awkward.
*/
short
AUX_GetAnyEventGlue(short evtMask, EventRecord *theEvent, long timeout, RgnHandle bounds, Boolean pullit)
{
GetAnyEventRec params;
assert(AUX_GETANYEVENT <= auxMaxSelector);
params.mask = evtMask;
params.timeout = timeout;
params.mouseBounds = bounds;
params.pullIt = pullit;
(void) AUX_GetAnyEvent(&params);
*theEvent = params.event;
return theEvent->what != nullEvent ? -1 : 0;
}
/* AUX_GetMinTimeout. Compute the minimum timeout necessary, we use this so we can
* sleep in the kernel instead of spinning in user mode.
*/
long
AUX_GetMinTimeout(void)
{
extern Boolean napOver;
if ((pFrontProcess->p_waitnexteventlevel > 0) /* GetNextEvent never blocks */
&& (coercionState == CS_DONE) /* if coercing, don't want physical events */
&& (napOver == 0) /* somebody needs attention */
&& (pSleepingStateList != nil)) /* somebody is sleeping */
return (pSleepingStateList->p_wakeuptime - TICKS);
else
return 0;
}
/* AUX_GetMouseBounds. Compute the rectangle for mouse moved events. Once again, this
* is so we can sleep in the kernel instead of user mode spinning.
*/
RgnHandle
AUX_GetMouseBounds(void)
{
extern RgnHandle mousebox;
return mousebox;
}
/* AUX_GetNextEvent. Our replacement for the real GetNextEvent. This isn't the one
* seen by the user program, but is the one we will use to actually get events out of
* the queue. Ours is different because we can get some funky events back from the
* kernel and mere mortals shouldn't ever get them.
*
* NOTE: ProcessManager overpatches this trap, but calls us in event_common when
* the process state is PRRUN.
*/
pascal short
AUX_GetNextEvent(short evtMask, register EventRecord *theEvent)
{
Boolean oldMask;
short realRetVal;
oldMask = AUX_SetAUXMask(true);
while ((realRetVal = (*realGNE)(evtMask, theEvent)) && AUX_HandleEvent(theEvent))
{
/* Just loop. All the work is done in the loop control. */
}
(void) AUX_SetAUXMask(oldMask);
return realRetVal;
}
/* AUX_GetTaskGlue. Handy utility to return the A/UX style task identifier of this
* process so we can do a real context switch to it later.
*/
long
AUX_GetTaskGlue(void)
{
long taskId;
assert(AUX_GETTASK <= auxMaxSelector);
AUX_GetTask(&taskId);
return taskId;
}
/* AUX_HandleEvent. Handle an A/UX specific event. */
short
AUX_HandleEvent(EventRecord *theEvent)
{
short retVal;
retVal = wTrue;
if (IsAUXEvent(theEvent) == false)
return wFalse;
switch(theEvent->what)
{
/* somebody's UI_SETSELECT broke, make 'em runnable. */
case auxSelectEvent:
AUX_MakeRunnable(theEvent->message);
break;
/* a new layer was asynchronously created, fit it in to the scheduling */
case auxAttachEvent:
AUX_MakeLayer(theEvent->message, theEvent->modifiers, theEvent->when);
break;
/* a layer went away by mistake (kill(9...) for instance, clean it up */
case auxExitEvent:
AUX_KillLayer(theEvent->message);
break;
}
theEvent->what = nullEvent;
return retVal;
}
#define LAUNCH_DELAY (60*60) /* one minute */
/* AUX_IdleProc. */
void
AUX_IdleProc(void)
{
register PEntryPtr myProc;
unsigned long olda5;
EventRecord anEvent;
olda5 = ProcessMgrA5Setup();
myProc = pCurrentProcess;
for (;;)
{
assert(myProc->aux_waitproc);
(void) AUX_GetAnyEventGlue(0, &anEvent, LAUNCH_DELAY, 0, true);
/* Handle the attach. aux_restore_ctx should get us out of here, never to return. */
if ((anEvent.what == auxAttachEvent) && (anEvent.message == myProc->aux_waitproc))
{
myProc->aux_realpid = myProc->aux_waitproc;
myProc->aux_waitproc = 0;
AUX_SwitchGlue(myProc->aux_realpid);
aux_restore_ctx(pCurrentProcess->p_sp);
dbmsg("How the ≈ did I get here?");
}
/* AUX event was not the usual attach, or an attach came from an incorrect
* AUX process. Feebly try to handle the event. Then die.
*/
(void) AUX_HandleEvent(&anEvent);
AUX_Kill(myProc->aux_waitproc);
ExitToShell();
}
}
/* AUX_KernelEvents. */
short
AUX_KernelEvents(EventRecord *theEvent)
{
short foundSome;
foundSome = 0;
while (AUX_GetAnyEventGlue(0, theEvent, 0, 0, 1) && IsAUXEvent(theEvent) != 0)
{
(void) AUX_HandleEvent(theEvent);
foundSome++;
}
return foundSome;
}
/* AUX_KillLayer. Get rid of an abnormally terminated process */
void
AUX_KillLayer(long taskId)
{
register PEntryPtr pProc;
if ((pProc = AUX_FindProc(taskId)) != nil)
(void) KillProcess(&pProc->p_serialNumber);
}
/* AUX_Launch. */
OSErr
AUX_Launch(InternalLaunchPBPtr pParams)
{
short realPid;
PEntryPtr pProc;
OSErr err;
WDPBRec wd;
/* setup a WDPBRec to pass to AUX_ForkExec through the AUXDispatch */
wd.ioCompletion = nil;
wd.ioNamePtr = nil;
wd.ioWDProcID = 0;
wd.ioVRefNum = pParams->ilAppSpecPtr->vRefNum;
wd.ioWDDirID = pParams->ilAppSpecPtr->parID;
PBOpenWD(&wd, true);
realPid = AUX_ForkExecGlue(wd.ioVRefNum, pParams->ilAppSpecPtr->name);
/* Close the WD, if we opened it */
if (wd.ioWDCreated != 0)
PBCloseWD(&wd, SyncHFS);
/* Negative result is an OSErr. Result should never be 0. Positive result is AUX pid. */
if (realPid < 0)
return((OSErr) realPid);
pParams->ilTaskMode |= COFF_BINARY;
if ((err = NewProcess(pParams)) != noErr)
{
dbmsg("AUX_Launch: NewProcess failed");
AUX_Kill(realPid);
return(err);
}
pProc = pParams->ilResultProc;
pProc->aux_waitproc = realPid;
pProc->aux_realpid = AUX_ProcessMgrTask();
return(noErr);
}
/* AUX_MakeLayer. Create an empty process. After the A/UX process switch it will be
* filled out by whomever did the UI_ATTACH.
*/
void
AUX_MakeLayer(long taskId, unsigned short flags, long size)
{
register PEntryPtr pProc;
InternalLaunchPB launchPB;
CoffName coffname;
coffname.taskid = taskId;
if ((pProc = AUX_Unblock(taskId)) != nil)
return;
if (AUX_COFF_FSSpec(&coffname) != noErr)
return;
launchPB.ilAppSpecPtr = &coffname.fsspec;
launchPB.ilTaskMode = COFF_BINARY | ASYNC_LAUNCH | (unsigned long) flags;
launchPB.ilDAInfoPtr = nil;
launchPB.ilPartitionSize = size + DEFLTSTACK - MACPLUS_DEFLTSTACK;
launchPB.ilStackSize = DEFLTSTACK;
launchPB.ilAppParameters = nil;
launchPB.ilAppParamHdl = nil;
if (NewProcess(&launchPB) != noErr)
{
dbmsg("AUX_MakeLayer: NewProcess failed");
AUX_Kill(taskId);
return;
}
pProc = launchPB.ilResultProc;
pProc->aux_realpid = taskId;
pProc->aux_waitproc = 0;
(void) WakeUpProcess(&pFrontProcess->p_serialNumber);
}
/* AUX_MakeRunnable. Make an application contained in a different task runnable. taskId
* refers to the kernel event layer identifier we got back from AUX_GetTask and stowed
* away in auxInfo.
*/
void
AUX_MakeRunnable(long taskId)
{
PEntryPtr pProc;
if ((pProc = AUX_FindProc(taskId)) != nil)
(void) WakeUpProcess(&pProc->p_serialNumber);
}
/* AUX_ProcessMgrTask. Return the task descriptor for Macintosh Applications */
long
AUX_ProcessMgrTask(void)
{
if (ProcessMgrTaskID < 0)
AUX_GetTask(&ProcessMgrTaskID);
return ProcessMgrTaskID;
}
/* AUX_SwitchGlue. Force an A/UX kernel context switch. */
void
AUX_SwitchGlue(long taskId)
{
long rval;
EventRecord anEvent;
assert(AUX_SWITCH <= auxMaxSelector);
/* If the Process Mgr process this task is associated with is gone, kill this task
* after the switch, if this is the original Process Mgr process we have to
* leave it laying around anyway.
* NOTE: Inverting taskID tells AUX_Switch to kill the originating process after
* enabling the new one.
*/
if (((rval = AUX_GetTaskGlue()) != AUX_ProcessMgrTask()) && AUX_FindProc(rval) == 0)
taskId = (-(taskId));
for (;;)
{
/* Try switch. On error we try to destroy the layer (AUX_KillLayer won't
* return). If it does, we aren't in a valid process, so we pick a new one.
*/
if (AUX_Switch(taskId) != 0)
{
AUX_KillLayer(taskId);
cpu_resched();
}
/* Normally, when we get back from the AUX_Switch, we will have already been
* made the current process. If the task id of this a/ux process is not the
* same as where we think we ought to be, we got woken up because the current
* process died (most likely, perhaps theres something else, so we're pretending
* to be paranoid) so we reap and handle any a/ux events. If it's true that
* the current process really did die, we will eventually receive an exitEvent,
* which will result in the associated Process Mgr process being killed and the
* processor reschedule, in which case we won't ever return to this routine.
*/
if (pCurrentProcess->aux_realpid == AUX_GetTaskGlue())
break;
/* AUX_HandleEvent won't return if the process we're switching to has exited,
* thus giving us an exitEvent
*/
do
{
(void) AUX_GetAnyEventGlue(0, &anEvent, 0l, 0l, true);
} while (AUX_HandleEvent(&anEvent));
dbmsg("AUX_SwitchGlue: probably shouldn't ever get here");
/* having handled whatever broke us out of our interminable sleep, try again */
}
}
/* AUX_Unblock. Find the nacent Process Mgr process associated with the given taskID,
* and declare it alive and well.
*/
PEntryPtr
AUX_Unblock(long taskId)
{
PEntryPtr pProc;
pProc = pProcessList;
while (pProc != nil)
{
if (pProc->aux_waitproc == taskId)
{
pProc->aux_waitproc = 0;
pProc->aux_realpid = taskId;
break;
}
pProc = pProc->p_NextProcess;
}
return pProc;
}
/* AUX_WaitForEvent. */
short
AUX_WaitForEvent(short mask, EventRecord *theEvent)
{
if (pCurrentProcess)
if (GetNextPseudoEvent(mask, theEvent, false))
return wTrue;
(void) AUX_GetAnyEventGlue(mask, theEvent, AUX_GetMinTimeout(), AUX_GetMouseBounds(), false);
return theEvent->what == nullEvent ? wFalse : wTrue;
}
/* AUX_WaitNextEvent. */
short
AUX_WaitNextEvent(short mask, EventRecord *theEvent, long timeout, RgnHandle mbox)
{
short rval;
AUX_SetTimeOut(timeout);
AUX_SetBounds(mbox);
rval = GetNextEvent(mask, theEvent);
AUX_SetTimeOut(0);
AUX_SetBounds(nil);
return rval;
}