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

1938 lines
64 KiB
C
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
Hacks to match MacOS (most recent first):
<Sys7.1> 8/3/92 Conditionalized a check for a nil CODE 0 handle (a PowerPC-only case)
under "PsychicTV".
9/2/94 SuperMario ROM source dump (header preserved below)
*/
/*
File: Processes.c
Contains: Process Mgr creation/deletion routines.
Written by: Phil Goldman and Erich Ringewald
Copyright: © 1986-1993 by Apple Computer, Inc., all rights reserved.
Change History (most recent first):
<SM55> 7/21/93 joe Back out <SM54>.
<55> 12/4/92 DRF Only support stub 'cfri' resource consisting of a single 32-bit
zero. This special case indicates launch a PowerPC main code
fragment living in the datafork. In the future, we will support
more complicated 'cfri' structures indicated by a non-zero value
in the first 32-bit word.
<54> 11/25/92 DRF Roll-in changes for PsychicTV. CreateProcess() now calls CFM to
create a native runtime context for every process. Native
applications are also detected via the presence of a 'cfri' (0)
resource, and launched with a mini-A5 world. In ExitToShell(),
any connections to system fragments (DLLs), or native resources
are released. All changes are conditionalized under "PsychicTV".
<53> 10/28/92 DTY Use new Get/Set macros to access ExpandMem.
<52> 8/26/92 DTY Roll in latest changes for A/UX.
<51> 5/29/92 DCL Included Script.h. iuSystemScript moved there for the New Inside
Mac.
<50> 3/23/92 JSM OSEvents.h is obsolete, use Events.h.
<49> 2/18/92 DTY #1021226: Switch emMessageManagerGlobals again.
<48> 1/14/92 YK Take out <46> since we found a better way. But <47> is still
there.
<47> 1/11/92 YK Make tsmLayerOwner nil if the dying process is it. (for TSM)
<46> 1/10/92 YK Allocate enough stack space to the Service Window Manager app
(for TSM) even though it is the background only app. (because it
owns windows.)
<45> 11/22/91 DTY Set the allocateGMBlkInSysHeap semaphore around the
PostHighLevelEvent in SendDeathNotice to force the event to be
allocated in the system heap instead of the Process Manager
heap. This is a fix from 7-Up.
<44> 10/23/91 DTY Use new _GetOverrideMap call instead of calling
GetTopSystemOverrideMap.
<43> 10/20/91 DTY New resource chains should start at the topmost System Override
map, not SysMapHndl. Call GetTopSystemOverrideMap to get this
map.
<42> 10/9/91 YK Added code to dispose float layer in c_exittoshell.
<41> 10/4/91 JSM Change PsychoticFarmerOrLater to TheFuture.
<40> 9/22/91 DTY Change PsychoticFarmerAndLater to PsychoticFarmerOrLater.
<39> 9/20/91 DFH Conditionalize initialization of emMessageManagerGlobals based
on PsychoticFarmerAndLater.
<38> 9/16/91 DFH Changed CreateProcess to initialize new process' SR from the SR
we found at startup, rather than a hard-coded 0x2000. Needed for
NuKernel.
<37> 7/26/91 stb replace HOpenResFileTrap with HOpenResFile. No glue is used
when SystemSevenOrLater.
<36> 6/4/91 DFH Zero emMessageManagerGlobals in InitLowMemory.
<35> 5/23/91 dba do a style of coercion that MPW 3.2 C likes
<34> 4/9/91 DFH VL, WS#BB-KA-034 : ExitToShell now checks QDExist before calling
InitCursor. Caused system crash when running application that
quits before InitGraf (such as Giffer). application on a MacIIci
or MacIIfx.
<33> 4/2/91 DFH (with John Iarocci) Added version check for MicroSoft Word
workaround. NOTE: This only affects the code when it is built
with the HAS_AUX_PROCESSMGR condition defined.
<32> 4/2/91 DFH VL,#86163: Fixed CreateProcess to not be vulnerable to
purgeability of the DA Handler CODE 0 resource. If purged,
LaunchDeskAccessory gets appMemFullErr, since we get garbage
when trying to fetch a5 world info from the resource.
<31> 3/27/91 DFH JWM,#84862: Fixed CreateProcess to look at result of
HOpenResFile, rather than at RESERR, to deceide whether the call
succeeded. RESERR can be set even if file got opened (e.g. when
there is not enough room for the preload resources).
<30> 3/25/91 DFH JSM,#DFH-910325a: Fix CreateProcess to deal with the fact that
fabricated Apple Event is in an unlocked handle.
<29> 3/21/91 DFH tes,#84989: Fixed pointer math in FMKill change #28.
<28> 3/20/91 DFH gbm,#84989: Added MakeSynStrikesPurgeable.
<27> 3/1/91 DFH KST,WS#DFH-910301a: Fixed GetFinderInfoForFile to extract script
code correctly. Was setting the high byte to 0xFF.
<26> 2/26/91 DFH JWM,WS#DFH-910226c: Fixed NewProcess to make sure pBabyProc is
set before jumping to ErrorRecovery after failed
NewProcessEntryClear.
<25> 2/21/91 DFH dba,#82504, #82681, #83168, #83182, #83207: HiNewHandle rolled
into ProcessMgrHiNewHandle. CreateProcess now calls the latter
instead of the former.
<24> 2/18/91 DFH BAC,#83089, #82873:Fix CreateProcess so that DA Handler's CODE 0
can remain unlocked, but is not susceptible to movement.
Currently, HOpenResFile is called after we deref the handle for
later use.
<23> 2/1/91 DFH BAC,WS#DFH-910131c:aeApplicationDied keyword parameter with the
OSErr is now the 'errn', rather than 'errv'.
<22> 1/31/91 DFH NGK,WS#DFH-910131b:Changed ExitToShell to adjust the application
menu for the incoming front process. Copes with applications
that call DrawMenuBar before we fix up the menu at the end of
the foreground switch.
<21> 1/28/91 DFH JSM,#81425: Removed inhibit_DAs mechanism.
<20> 1/21/91 DFH (KSM) Process Menu is now called Application Menu.
<19> 1/14/91 DFH (dba) Make system mode per process.
<18> 1/14/91 DFH (JDR) Conditionalize out AUX support.
<17> 1/10/91 DFH (JDR) Add hack to prevent HyperCard 1.2.5 patch to SysBeep.
<16> 1/8/91 DFH (rdd) Got rid of pErrantProcess since it is no longer needed.
<15> 12/18/90 DFH Removed fgAppsUnexecuted mechanism, since front switch queue
works better.
<14> 12/17/90 DFH Change NewProcess to always initialize the SCRAPHANDLE to an
zero length handle, and let CS_INIT give the process the
correct, up-to-date scrap.
<13> 12/15/90 DFH OrphanScrapIntoPartition no longer has a parameter.
<12> 12/14/90 DFH (SMC) Remove PMgrExit workaround in ExitToShell. Was stuffing
HEAPEND around CleanupApplication, since PMgrExit used it to
figure out which palettes to dispose. Now, PMgrExit uses
ApplZone->bkLim like it should.
<11> 12/10/90 DFH Adding support for custom icons (i.e. the ones the user can
specify).
<10> 12/5/90 DFH Integrated AUX support.
<9> 11/27/90 DFH Removed unneeded parameter from cpu_resched.
<8> 11/26/90 DFH Change trespass() to dbmsg().
<7> 11/21/90 DFH Changed InitHeapZone to use ROUTINE_ADDR of MyGrowZone so that
jump table is bypassed.
<6> 11/9/90 DFH Added CancelSleep() to c_exittoshell in case ExitToShell was
called when process was sleeping, e.g. by dsForcedQuit handler
or MacsBug. Otherwise, sleep queue is hosed.
<5> 11/6/90 DFH Renamed emAppleEventsGlobal to emAppleEvents.
<4> 11/1/90 DFH Put workaround in ExitToShell for PMgrExit using HEAPEND for
limit check. Remove when PMgrExit fixed. Unconditionalized
edition mgr lomem switch.
<2> 10/30/90 csd Fixed some identifiers to match the newest interfaces.
<0> 9/2/86 PYG New Today.
*/
#pragma segment kernel_segment
#include <types.h>
#include <memory.h>
#include <files.h>
#include <quickdraw.h>
#include <windows.h>
#include <menus.h>
#include <events.h>
#include <resources.h>
#include <osutils.h>
#include <script.h>
#include <Notification.h>
#include <retrace.h>
#include <segload.h>
#include <desk.h>
#include <dialogs.h>
#include <errors.h>
#include <shutdown.h>
#include <EPPC.h>
#include <MFPrivate.h>
#include <Icons.h>
#include <AppleEvents.h>
#include <AppleEventsInternal.h>
#include <ExpandMemPriv.h>
#include <ResourceMgrPriv.h>
#include "Glue.h"
#include "Lomem.h"
#include "Data.h"
#include "SysMisc.h"
#include "Patches.h"
#include "HList.h"
#include "Aux.h"
#include "Puppet.h"
#include "Zone.h"
#include "OSDispatchPrivate.h"
#include "AppleEventExtensions.h"
#if PsychicTV
#include "MixedMode.h" /* <54> Some new interfaces from NativeProj */
#include "CodeFragments.h"
#include "CodeFragmentsTrap.h"
#endif
/* Some function prototypes that should be in (yet another) header file */
void GetProcessIconCache(PEntryPtr);
void AdjustApplicationMenu(PEntryPtr pTitleProc);
void DeleteAppFromApplicationMenu(PEntryPtr);
void AppleMenuCleanup(PEntryPtr);
Handle GetMapHdlFromRefNum(short, Handle);
OSErr GetDrvrResourceAndInfo(StringPtr, Handle *, short *, Str255 *);
OSErr CheckUnitTableEntry(short);
OSErr MakeHidingString(PEntryPtr, StringPtr);
void InitPuppets(PEntryProc);
void SetAppParameters(PCB **, unsigned long, InternalLaunchPBPtr, Boolean);
void kill_state(PEntryPtr);
void PuppetKill(void), QDKill(void), VNKill(void);
void save_lomem(PEntryPtr);
/* Function prototypes internal to this file */
void InitLowMemory(FSSpecPtr);
Ptr GetCeilingPtr(PEntryPtr pPEntry);
OSErr NewProcessEntryClear(PEntryPtr *);
void DisposeProcessEntry(PEntryPtr);
OSErr GetProcessMemory(PEntryPtr, Ptr);
void DisposeProcessMemory(PEntryPtr);
OSErr CreateProcess(InternalLaunchPBPtr, PEntryPtr, PEntryPtr, u_long *);
OSErr InitHeapZone(PEntryPtr);
void GetFinderInfoForFile(FSSpec *, PEntryPtr);
void HandleShellDeath(PEntryPtr, OSErr, Boolean);
void MenuKill(void), FMKill(Boolean), NMKill(void);
#if PsychicTV
/* <54> Function Prototypes for CodeFragmentMgr/MixedMode Specific Stuff */
Boolean FindFragmentEntryPoint(FSSpec *theAppFSSpec,
FragmentLocatorPtr mainFragment,
OSType *architecture);
OSErr SetupPsychicTVProcess(PEntryPtr pNewProc,
Boolean launchingFragment,
FragmentLocatorPtr mainFragment,
OSType archictecture,
RoutineDescriptor *nativeEntryPoint);
void TearDownPsychicTVProcess(PEntryPtr theProc);
#endif
/*---------------------------------------------------------------------------------*
Process Creation Routines
*---------------------------------------------------------------------------------*/
/* Process heap and stack control */
#define TOPROOM (0x0400) /* APPLLIMIT during launch */
#define APPZONESIZE (0x1800) /* minimum app heap size at launch */
#define DAEMON_APPZONESIZE (0x1000) /* minimum BG only app heap size at launch */
#define DAEMON_STACKSIZE (0x800) /* max stack size for BG only app */
/* NewProcessEntryClear. Make a new process desriptor. Zeroes out the entire entry, then
* allocates a ProcessID and ProcessSerialNumber.
*/
OSErr
NewProcessEntryClear(PEntryPtr *ppProc)
{
register ProcessID pid;
PEntryPtr retProc, *hProc;
/* safety first! */
*ppProc = nil;
/* Allocate the PEntry high and lock it down */
if ((hProc = ProcessMgrHiNewHandle(sizeof(PEntry), nil)) == nil)
return(memFullErr);
HLock(hProc);
/* Clear PEntry, then fill in preliminary information */
retProc = (char *) StripAddress(*hProc);
MemClear(retProc, sizeof(PEntry));
retProc->p_HandleForThisPEntry = hProc;
/* Allocate a ProcessID (handles wrap-around gracefully). */
pid = ProcessIDSource;
do
{
if ((--pid) <= 0)
pid = MAX_PROCESSID;
}
while (PEntryFromPID(pid) != nil);
retProc->p_mypid = ProcessIDSource = pid;
retProc->p_serialNumber.lowLongOfPSN = ProcessNumberSource++;
retProc->p_serialNumber.highLongOfPSN = 0;
#ifdef DEBUG
retProc->p_tattoo = BRAND;
#endif DEBUG
/* Set output param and return value */
*ppProc = retProc;
return(noErr);
}
/* DisposeProcessEntry. Opposite of NewProcessEntryClear. */
void
DisposeProcessEntry(PEntryPtr pDeadProc)
{
#ifdef DEBUG
pDeadProc->p_tattoo = 0;
#endif DEBUG
DisposHandle(pDeadProc->p_HandleForThisPEntry);
}
/* Flag to disable app gz proc of launcher while launchees lomem is current */
Boolean inLauncheesWorld = false;
/* NewProcess. Create the new process with a world size of reqSize bytes, and a
* stack size of ssize bytes.
*/
OSErr
NewProcess(InternalLaunchPBPtr pParams)
{
register PEntryPtr pBabyProc, pLauncherProc;
FSSpecPtr pFileSpec;
PEntryPtr pProcOnStack;
PCB *pPCB, **hOldPCB;
Handle rsrcHdl;
short err;
u_long init_appzonesize, currenta5;
AppParametersPtr pAEParm;
Ptr msgAddr;
#ifdef HAS_AUX_PROCESSMGR
/* Sanity check */
if ((pParams->ilTaskMode & COFF_BINARY) != 0)
assert(AUXIsPresent);
#endif HAS_AUX_PROCESSMGR
/* Don't call gz proc of launcher (pCurrentProcess) */
inLauncheesWorld = true;
pLauncherProc = pCurrentProcess;
/* allocate a process list entry */
err = NewProcessEntryClear(&pProcOnStack);
if ((pBabyProc = pProcOnStack) == nil)
goto ErrorRecovery;
/* Determine process dimensions */
pBabyProc->p_size = pParams->ilPartitionSize;
LONG_ALIGN(pBabyProc->p_size);
if ((pParams->ilTaskMode & modeOnlyBackground) != 0)
{
init_appzonesize = DAEMON_APPZONESIZE;
pBabyProc->p_ssize = DAEMON_STACKSIZE;
}
else
{
init_appzonesize = APPZONESIZE;
pBabyProc->p_ssize = pParams->ilStackSize;
}
pBabyProc->p_taskmode = pParams->ilTaskMode;
pBabyProc->p_haseditcmdkeys = (-1); /* Have not yet gone through CS_INIT */
pBabyProc->p_wakeuptime = TICKS; /* Reset the wakeup placeholder */
#ifdef HAS_AUX_PROCESSMGR
/* Initialize osInfo to prevent random switches for real Macintosh applications */
if (AUXIsPresent)
pBabyProc->aux_realpid = AUX_ProcessMgrTask();
#endif HAS_AUX_PROCESSMGR
/* Set signature, type */
pFileSpec = pParams->ilAppSpecPtr;
if ((pBabyProc->p_taskmode & modeDeskAccessory) == 0)
GetFinderInfoForFile(pFileSpec, pBabyProc);
/* Save the launcher's state so we can restore after generating launchee's state */
save_state(pLauncherProc);
/* Go get Excel's version number to determine whether we have to kludge
* the partition allocation.
* NOTE: Sets THEZONE = SYSZONE so map goes into impartial heap.
* NOTE: Assumes that the PEntry was zeroed, since we haven't set the p_signature
* (or p_type) if we're launching a DA.
* NOTE: Assumes that the resource chain changes resulting from HOpenResFile and
* CloseResFile will not be reflected in the caller's state, since our save_state
* (which just happened) and restore_state will take care of the relevant info.
*/
if (pBabyProc->p_signature == 'XCEL' || pBabyProc->p_signature == 'nX^n')
{
THEZONE = SYSZONE;
SetResLoad(false);
if (HOpenResFile(pFileSpec->vRefNum, pFileSpec->parID, &pFileSpec->name, fsRdPerm) != (-1))
{
SetResLoad(true);
if ((rsrcHdl = Get1Resource('VERS', 0)) != nil)
pBabyProc->p_version = **((unsigned long **) rsrcHdl);
CloseResFile(CurResFile());
}
SetResLoad(true);
/* At this point THEZONE == SYSZONE */
}
/* Big enough zone requested? */
if (pParams->ilPartitionSize < (init_appzonesize + pBabyProc->p_ssize))
{
err = appMemFullErr;
goto ErrorRecovery;
}
/* try allocating the physical memory partition for this process
* NOTE: Side effect is to set THEZONE to ProcessMgrZone.
*/
if ((err = GetProcessMemory(pBabyProc, GetCeilingPtr(pBabyProc)) ) != noErr)
goto ErrorRecovery;
/* At this point THEZONE == ProcessMgrZone */
/* initialize the portions of low memory which need it.
* NOTE: CURAPNAME for a DA will be set later when we know for sure what the
* the driver name is.
*/
InitLowMemory(pFileSpec);
/* create the application heap zone low in the partition (sets THEZONE) */
if ((err = InitHeapZone(pBabyProc)) != noErr)
goto ErrorRecovery;
/* At this point THEZONE == pBabyProc->p_zone */
/* Set the top of the resource chain back down to the system file. This causes
* subsequent resource file opens to create a new chain.
*/
TOPMAPHANDLE = GetOverrideMap(SYSMAPHANDLE);
/* At this point, an ExitToShell should (safely) cleanup launchee, not launcher. So,
* set pCurrentProcess to new PEntry. This means that any tracking will be against
* launchee (such as the FCB for CURAPREFNUM).
*/
pCurrentProcess = pBabyProc;
InitAllPacks();
/* Initialize auxiliary information in the PCB */
pPCB = *pBabyProc->p_pcb;
pPCB->p_vrefnum = 0;
pPCB->launchDate = TICKS;
InitApplPatchTable(&pPCB->applPatchTable, &pPCB->applPatchCount);
if ((err = MEMERROR) != noErr)
goto ErrorRecovery;
/* load resources, set up A5 world and stack, and fill in the PEntry some more. */
if ((err = CreateProcess(pParams, pBabyProc, pLauncherProc, &currenta5)) != noErr)
goto ErrorRecovery;
/* Create custom string for hide item of Application Menu */
if ((err = MakeHidingString(pBabyProc, CURAPNAME)) != noErr)
goto ErrorRecovery;
/********************************************************************************
* NOTE: There is no error handling from here on out.
********************************************************************************/
/* Get the application icon */
GetProcessIconCache(pBabyProc);
/* Set up initial scrap info. The correct scrap will get moved in later during
* CS_INIT in Coercion_State_Engine when the process is brought to the front.
* NOTE: This HandToHand's the current SCRAPHANDLE from the launcher to the
* launchee because there any app combos that depend on this direct inheritance
* as a covert means of communication. However, they don't need it after the
* launchee's first event call (since the scrap can change there anyway). Strictly
* a compatibility thing.
*/
if ((pBabyProc->p_taskmode & modeDeskAccessory) == 0)
{
if (SCRAPHANDLE != nil)
(void) HandToHand(&SCRAPHANDLE);
}
else
{
SCRAPINFO = 0;
SCRAPSTATE = 1;
SCRAPHANDLE = NewHandleSys(0);
}
/* Get version longword and release it. This non-standard version resource is
* a special deal for MicroSoft applications so they can call TWGetProcInfo.
*/
if ((rsrcHdl = (long **) GetResource('VERS', 0)) != nil)
{
pBabyProc->p_version = **((long **) rsrcHdl);
ReleaseResource(rsrcHdl);
}
else
pBabyProc->p_version = 0L;
/* Check whether this application is HyperCard 1.2.5. If so, we need to prevent
* it from patching SysBeep, since its patch breaks horribly under System 7.0 and
* later. Remove this when HyperCard 1.2.5 is no longer supported.
*/
if ((pBabyProc->p_signature == 'WILD') &&
((rsrcHdl = (long **) Get1Resource('vers', 1)) != nil))
{
if (**((short **) rsrcHdl) <= 0x125)
pBabyProc->p_preventFirstSysBeepPatch = true;
ReleaseResource(rsrcHdl);
}
/* Set up puppet string handling */
InitPuppets(pBabyProc);
/* Get Event PPC resource and create message queues */
CreateMsgQ(pBabyProc);
/* Handle any necessary APPPARMHANDLE/ilAppParameters conversion. */
SetAppParameters(pBabyProc->p_pcb, currenta5, pParams, pBabyProc->eppcBlk.portID != 0);
#ifdef HAS_AUX_PROCESSMGR
/* Fix bug in Microsoft Word. If A/UX is running they don't try to grow
* the zone and then don't believe they have any memory available. Historical
* braindamage that's really because they don't use the MaxApplZone, but
* instead manually scribble a 24-bit style heap block at the end to artificially
* grow the zone. That doesn't work on A/UX.
*/
if (AUXIsPresent && pBabyProc->p_signature == 'MSWD' && pBabyProc->p_version <= '0400')
MaxApplZone();
#endif HAS_AUX_PROCESSMGR
/* fork caller's low memory into createe's PCB. */
save_lomem(pBabyProc);
/* Copy the following info into PCB directly, rather than through low memory, so
* that VBL tasks of the calling process will not ever access the wrong values.
* Stklowpt is set with knowledge that return address INITRET is already on the stack.
* NOTE: The PMSPHOOK lomem is NIL'd by the call to save_state(pLauncherProc), to
* make sure the hook is not used while lomem is in transition. For the launchee to
* correctly inherit the launcher's value, we must copy the saved value from the
* launcher's PCB. This is normally fine. It gets tricky when the caller has no
* PCB (!), such as pNullProcess launching Finder. The solution is to leave PMSPHOOK
* alone in this case. Since there is no PCB, the context save didn't have to alter
* the lomem. Therefore, the save_lomem(pBabyProc) just above copied the correct value.
*/
pPCB = *pBabyProc->p_pcb;
pPCB->currenta5 = currenta5;
pPCB->stklowpt = CURSTACKBASE - sizeof(Ptr);
pPCB->fsqueuehook = (void (**)())initFSQueueHook;
if ((hOldPCB = pLauncherProc->p_pcb) != nil)
{
assert(pLauncherProc != pNullProcess);
pPCB->pmsphook = (*hOldPCB)->pmsphook;
}
/* Restore caller's state */
pCurrentProcess = nil;
(void)restore_state(pLauncherProc, true);
pCurrentProcess = pLauncherProc;
inLauncheesWorld = false;
/* Send any necessary AppleEvents. Free the message if SetAppParameters made it. */
pAEParm = pParams->ilAppParameters;
if (pParams->ilAppParamHdl != nil)
{
HLock(pParams->ilAppParamHdl);
pAEParm = *pParams->ilAppParamHdl;
}
if (pAEParm != nil)
{
msgAddr = (pAEParm->messageLength == 0) ? nil : ((Ptr) pAEParm + sizeof(AppParameters));
(void) BeginSystemMode();
(void) PostHighLevelEvent(&(pAEParm->theMsgEvent), (u_long) &pBabyProc->p_serialNumber, pAEParm->eventRefCon, msgAddr, pAEParm->messageLength, receiverIDisPSN);
(void) EndSystemMode();
}
if (pParams->ilAppParamHdl != nil)
DisposHandle(pParams->ilAppParamHdl);
/* Return output parameters to caller */
pParams->ilResultProc = pBabyProc;
return(noErr);
/* Common code to do the cleanup after a fatal error occurred (err==reason) */
ErrorRecovery:
if (pBabyProc != nil)
{
pCurrentProcess = nil;
DisposeProcessMemory(pBabyProc);
(void)restore_state(pLauncherProc, true);
pCurrentProcess = pLauncherProc;
DisposeProcessEntry(pBabyProc);
}
inLauncheesWorld = false;
pParams->ilResultProc = nil;
return(err);
}
/* userret. The address of this routine is placed on the process' stack as the return
* address of the main routine, so it is entered when a process exits that routine.
*/
void
userret(void)
{
ExitToShell();
}
/* CreateProcess. Given a name and a PEntryPtr describing a valid address space, make
* the given process ready to run.
* NOTE: Assumes that PEntry and PCB were zeroed when created, so fields that are
* supposed to be init'd to zero need no attention.
*/
OSErr
CreateProcess(InternalLaunchPBPtr pParams, PEntryPtr pNewProc, PEntryPtr pLauncherProc, u_long *pCurrenta5)
{
OSErr err;
u_long currenta5;
Ptr curStackEnd, saveApplLimit;
u_size minappheapsize;
struct code0 *pCode0;
FSSpecPtr pFileSpec;
Handle drvrHdl;
short resourceID;
short daFileRefNum;
#ifdef HAS_AUX_PROCESSMGR
static struct code0 coffSegInfo =
{
32, /* above a5 */
sizeof(qd), /* below a5 */
0, /* jtSize */
32 /* jtOffset */
};
#endif HAS_AUX_PROCESSMGR
#if PsychicTV
static struct code0 fakeCode0 = /* <54> This should be combined with A/UX stuff, */
{ /* but the #ifdefs dont seem to be working out… */
32, /* above a5 */
sizeof(qd), /* below a5 */
0, /* jtSize */
32 /* jtOffset */
};
Boolean launchingFragment = false;
FragmentLocator mainFragLocation;
OSType architecture;
RoutineDescriptor nativeEntryPoint;
#endif
if ((pNewProc->p_taskmode & modeDeskAccessory) != 0)
{
/* Get the DA resource file.
* NOTE: We turn off ResLoad because we want the DA Handler to load the
* resource. We set THEZONE to SYSZONE so that pre-load resources will
* get empty handles in the SYSZONE.
*/
SetResLoad(false);
THEZONE = SYSZONE;
pFileSpec = (pParams->ilDAInfoPtr)->daFileSpec;
(*pNewProc->p_pcb)->p_fileRefNum = daFileRefNum = HOpenResFile(pFileSpec->vRefNum,
pFileSpec->parID, &pFileSpec->name, fsCurPerm);
/* Fix up CURAPNAME, since we know it now. Make sure table entry is OK to use. */
err = RESERR;
if (daFileRefNum != (-1))
if ((err = GetDrvrResourceAndInfo((pParams->ilDAInfoPtr)->daDrvrName, &drvrHdl, &resourceID, CURAPNAME)) == noErr)
err = CheckUnitTableEntry(resourceID);
THEZONE = APPLZONE;
SetResLoad(true);
UseResFile(0);
/* Try for DAHandler's segment loader information resource */
if (err == noErr)
{
CURAPREFNUM = SYSMAP;
if ((SAVESEGHANDLE = GetResource(SYS_SEGMENT_TYPE, DAH_SEGMENT_ZERO)) == nil)
if ((err = RESERR) == noErr)
err = resNotFound;
}
/* Handle error opening the file, getting the driver, or checking the DCE. We
* can close the file only if it was opened above the system file.
*/
if (err != noErr)
{
if ( (daFileRefNum != (-1)) && (GetMapHdlFromRefNum(daFileRefNum, SYSMAPHANDLE) == nil) )
CloseResFile(daFileRefNum);
return(err);
}
/* Get address of segment loader info, since we got it. */
pCode0 = *SAVESEGHANDLE;
/* Set signature, type, and name script code */
GetFinderInfoForFile(pFileSpec, pNewProc);
}
else
{
/* Open the app file (read-only if the app is being multilaunched) */
pFileSpec = pParams->ilAppSpecPtr;
CURAPREFNUM = HOpenResFile(pFileSpec->vRefNum, pFileSpec->parID, &pFileSpec->name,
((pNewProc->p_taskmode & modeMultiLaunch) != 0) ? fsRdPerm : fsCurPerm);
if (((*pNewProc->p_pcb)->p_fileRefNum = CURAPREFNUM) == (-1))
return(RESERR);
#ifdef HAS_AUX_PROCESSMGR
/* Try to load the initial code segment. Not available or needed for coff binaries. */
if (AUXIsPresent && (pNewProc->p_taskmode & COFF_BINARY) != 0)
{
pCode0 = &coffSegInfo;
SAVESEGHANDLE = nil;
}
else
#endif HAS_AUX_PROCESSMGR
#if PsychicTV
/* <54> Find out if there is a main code fragment to launch */
if ((launchingFragment = FindFragmentEntryPoint(pFileSpec,&mainFragLocation,&architecture)))
{
pCode0 = &fakeCode0;
SAVESEGHANDLE = nil;
}
else
#endif
{
if ((SAVESEGHANDLE = GetResource('CODE', 0)) == nil)
{
if ((err = RESERR) == noErr)
err = resNotFound;
CloseResFile(CURAPREFNUM);
return(err);
}
pCode0 = *SAVESEGHANDLE;
}
}
/* really enough room for min heap, a5 world, and stack, at least? */
minappheapsize = ((pNewProc->p_taskmode & modeOnlyBackground) != 0) ? DAEMON_APPZONESIZE : APPZONESIZE;
if (pNewProc->p_size < (minappheapsize + pCode0->abovea5 + pCode0->belowa5 + pNewProc->p_ssize) )
{
if (CURAPREFNUM != SYSMAP)
CloseResFile(CURAPREFNUM);
if ((pNewProc->p_taskmode & modeDeskAccessory) != 0)
CloseResFile(daFileRefNum);
return(appMemFullErr);
}
#if PsychicTV
if ((err = SetupPsychicTVProcess(pNewProc,launchingFragment,&mainFragLocation,architecture,&nativeEntryPoint)) != noErr)
{
DebugStr("\pSetup Failed");
CloseResFile(CURAPREFNUM);
return(err);
}
#endif
/* Setup a5, curjtoffset, stackbase, and lowpt based on CODE 0 info. */
currenta5 = (unsigned long)pNewProc->p_zone + pNewProc->p_size - pCode0->abovea5;
CURJTOFFSET = (unsigned short)pCode0->jtoffset;
CURSTACKBASE = curStackEnd = (Ptr) ((unsigned long)currenta5 - pCode0->belowa5);
/* Push return address onto his stack a la JSR, so that an RTS from his main
* routine dumps him into our handler. Then, push the real first routine, so
* that InitializeProcess() returns to it.
*/
*--(Ptr *)curStackEnd = INITRET;
#if PsychicTV
/* <54> If were launching a fragment, push its main entry point instead of jumptable entry */
if (launchingFragment)
*--(Ptr *)curStackEnd = nativeEntryPoint; // a pointer to a private_RD that points to the native entry point
else
#endif
*--(Ptr *)curStackEnd = (Ptr)(currenta5 + pCode0->jtoffset + 2); // first jump table entry
/* copy jump table from resource to its home in the app's a5 world */
BlockMove(pCode0->jt, (Ptr)currenta5 + pCode0->jtoffset, pCode0->jtsize);
/* Now that things are set up, we can release segment 0 */
#if PsychicTV // <Sys7.1>
if (SAVESEGHANDLE != nil)
#endif // <Sys7.1>
ReleaseResource(SAVESEGHANDLE);
/* Try to set APPLLIMIT to ssize below current stack. We get an error if this
* would be below the current HEAPEND. This can happen if the application heap
* had to be expanded after we initialized it. Essentially, it means that the
* specified partition was not big enough. We could give the guy a substandard
* stack size (i.e. a higher APPLLIMIT), but prefer not to.
* NOTE: SetApplLimit has a bug wherein APPLLIMIT is set even if an error is
* returned, causing the heap globals to be bad. We therefore try to restore the
* value if the set failed. That should always work. Even if it doesn't we can
* probably still back out OK, since all we'll do in the heap is dispose blocks.
*/
saveApplLimit = APPLLIMIT;
SetApplLimit((Ptr) ((u_long) curStackEnd - pNewProc->p_ssize));
if (MEMERROR != noErr)
{
SetApplLimit(saveApplLimit);
if (CURAPREFNUM != SYSMAP)
CloseResFile(CURAPREFNUM);
return(appMemFullErr);
}
/* Set up registers for first context switch in */
{
curStackEnd -= sizeof(SaveRegs); /* To leave room for regs on the stack */
((SaveRegs *)curStackEnd)->d2 = 0; /* Because MS Works assumes this to be so */
((SaveRegs *)curStackEnd)->a6 = (unsigned long) SCRATCH20;
/* NOTE:
* This is always supposed to be a safe value.
* MacWrite has the following code at init:
* link a6,#$fff0 ; make room for buffer
* move.l (a6),a0 ; should be move.l a6,a0
* ...
* _SysEnvirons ; a0 supposed to point to buffer
*/
((SaveRegs *)curStackEnd)->a5 = currenta5;
((SaveRegs *)curStackEnd)->sr = initialProcessSR;
((SaveRegs *)curStackEnd)->pc = InitializeProcess;
pNewProc->p_sp = (unsigned long)curStackEnd;
}
pNewProc->p_dadProcess = pLauncherProc;
if ((pNewProc->p_taskmode & modeCanBackground) != 0)
{
if ((pNewProc->p_taskmode & (modeNeedSuspendResume | modeDoesActivateOnFGSwitch))
== (modeNeedSuspendResume | modeDoesActivateOnFGSwitch))
pNewProc->p_isfirstbackevt = true;
if (nbacks++ == 0)
pLastBGProcess = pNewProc;
}
/* Now that the entire thing is set up, claim the PEntry */
pNewProc->p_state = PRREADY;
pNewProc->p_NextProcess = pProcessList;
pProcessList = pNewProc;
DSERRCODE = noErr;
/* Success! Set the output parameter and leave. */
*pCurrenta5 = currenta5;
return(noErr);
}
/* InitHeapZone. This routine will initialize the heap zone for the given process.
* NOTE: A side effect is to set THEZONE == pProc->p_zone.
*/
OSErr
InitHeapZone(PEntryPtr pProc)
{
/* turn his allocated world into a zone. */
InitZone(ROUTINE_ADDR(MyGrowZone), 64, (Ptr)pProc->p_zone + (((pProc->p_taskmode & modeOnlyBackground) != 0) ? DAEMON_APPZONESIZE : APPZONESIZE), pProc->p_zone);
if (MEMERROR != noErr)
return(MEMERROR);
/* Mark heap delimiters in lomem, set applzone there. Our new zone will be
* the `application zone,' so we can perform memory manager calls which
* manipulate it.
*/
HIHEAPMARK = HEAPEND = (APPLZONE = THEZONE)->bkLim;
/* Heap end is now the nominal APPZONESIZE (approx 6K, 4k for daemons).
* Set a reasonable appl limit (Top of world less 'TOPROOM'). This
* gives us a provisional heap until we get the CODE 0 resource loaded
* to determine where the real APPLLIMIT should go.
* NOTE: Launch will fail if our allocations in the app heap cause the
* HEAPEND to move higher than the real APPLLIMIT we set later.
*/
SetApplLimit((Ptr)pProc->p_zone + pProc->p_size - TOPROOM);
if (MEMERROR != noErr)
return (MEMERROR);
/* Set application's memtop as if partition were located low in memory
* NOTE: Since Process Mgr makes SYSZONE->bklim a *variable*, the exact
* value we give MEMTOP is somewhat irrelevant.
*/
MEMTOP = SYSZONE->bkLim + pProc->p_size;
return(noErr);
}
/* InitLowMemory. This routine initializes the low memory for a fledgling process for
* those portions that cannot be inherited from one's parent.
*/
void
InitLowMemory(FSSpecPtr pAppFileSpec)
{
register long zero = 0, minusOne = (-1);
/* turn off stack sniffing whilst hacking on heaps... */
STKLOWPT = (char *)zero;
SEVTENB = minusOne;
DSDRAWPROC = (void (**)())minusOne;
EJECTNOTIFY = (void (**)())zero;
IAZNOTIFY = (void (**)())zero;
/* Set current layer using inherited window mgr and qd state */
SetCurLayer(MFLayer);
WWEXIST = minusOne;
QDEXIST = minusOne;
/* Set CURAPNAME, but make sure to limit the size (MFS names can be bigger) */
if (pAppFileSpec != nil)
{
BlockMove(&pAppFileSpec->name, CURAPNAME, sizeof(Str31));
if (Length(CURAPNAME) > sizeof(Str31) - 1)
Length(CURAPNAME) = sizeof(Str31) - 1;
}
SetExpandMemAppleEvents(zero);
SetExpandMemScriptAppGlobals((Ptr) zero);
SetExpandMemEditionMgrPerApp((Ptr) zero);
SetExpandMemMessageManagerGlobals(0, zero);
DESKHOOK = (void (**)())zero;
CLOSEORNHOOK = (void (**)())zero;
DRAGHOOK = (void (**)())zero;
RESUMEPROC = (void (**)())zero;
TASKLOCK = zero;
FSCALEDISABLE = zero;
RESERRPROC = (void (**)())zero;
MENULIST = (Handle)zero;
LASTSPEXTRA = minusOne;
WIDTHTABHANDLE = (Handle)zero;
WIDTHLISTHAND = (Handle)zero;
if (Colorized)
{
FMEXIST = minusOne;
SYNLISTHANDLE = (Handle)zero;
MENUCINFO = (Handle)minusOne;
LAYERPALETTE = (Handle)zero;
}
MACPGM = zero; /* Don't confuse the runtime systems */
SWITCHDATA = (Ptr)zero; /* Convince every one that switcher isn't running */
SWITCHEDBITS = (char)minusOne;
}
#define ONE_K (1024)
#define ONE_MEG_PTR ((Ptr)0x100000)
#define MIN_EXCEL_K_ABOVE_1MEG (100) /* In K units */
#define MAX_EXCEL_K_ABOVE_1MEG (525) /* In K units */
#define MIN_EXCEL_K_PARTITION_SIZE (224) /* In K units */
#define MAX_EXCEL_K_PARTITION_SIZE (1385) /* In K units */
#define MAX_REASONABLE_SYS_HEAP_TOP ((Ptr)(167*ONE_K)) /* In bytes */
/* GetCeilingPtr. Return the address that bounds the process from above. This is
* necessary because certain applications make assumptions about loading in the 1st
* meg. In particular, make sure that WriteNow 1.00 never reaches higher than
* 1M and calculate the ceiling for Excel 1.00-1.999... based on the empirical given
* below (linear interpolation based on partition size).
* NOTE: We don't care if p_size > MAX_EXCEL_K_PARTITION_SIZE: it won't be gotten and
* GetProcessMemory will return memFragErr.
* NOTE: We should actually check for the case in which the user has set the Excel
* size so large that the multiplication overflows. However, as long as the constants
* stay hardwired this could only happen if the size is set to 2**31/(525-127) > 10M.
* It's not worth the extra code to handle this. If the constants get bigger, just
* change the units to make the product smaller. The goal here is to waste as little
* code as possible on this.
*/
Ptr
GetCeilingPtr(PEntryPtr pPEntry)
{
long cbDiffActualFromMin, cbAboveAsk;
Ptr minCeilingPtr, ceilingPtr;
ceilingPtr = nil;
if (pPEntry->p_signature == 'nX^n' && pPEntry->p_version <= '0100')
ceilingPtr = ONE_MEG_PTR;
else if (pPEntry->p_signature == 'XCEL' && pPEntry->p_version < '0200')
{
/* Find difference between requested and minimum partitions (might be negative). */
cbDiffActualFromMin = pPEntry->p_size - (MIN_EXCEL_K_PARTITION_SIZE * ONE_K);
/* Adjust ceiling pointer. Use default if requested size was too small. */
ceilingPtr = ONE_MEG_PTR;
if (cbDiffActualFromMin > 0)
{
cbAboveAsk = (cbDiffActualFromMin
* (MAX_EXCEL_K_ABOVE_1MEG - MIN_EXCEL_K_ABOVE_1MEG)
/ (MAX_EXCEL_K_PARTITION_SIZE - MIN_EXCEL_K_PARTITION_SIZE))
+ (MIN_EXCEL_K_ABOVE_1MEG * ONE_K);
ceilingPtr += cbAboveAsk;
}
/* Now bound ceiling from the bottom */
minCeilingPtr = MAX_REASONABLE_SYS_HEAP_TOP + (Ptr) pPEntry->p_size;
if (ceilingPtr < minCeilingPtr)
ceilingPtr = minCeilingPtr;
}
return ceilingPtr;
}
/* GetProcessMemory. Get all of the physical memory required to run this process.
* NOTE: Has unique ability to force application to be loaded completely
* below a given ceiling. The hokey method is to allocate a specially-marked
* block at the ceiling point, then mangle the ProcessMgrHiNewHandle to not look higher
* than said block. This is to kow tow to Microsoft Excel 1.5's and WriteNow 1.0's
* crippled designs.
* NOTE: Assumes a5 == PROCESSMGRGLOBALS
* NOTE: Side effect is to set THEZONE to ProcessMgrZone.
*/
static Ptr myRoverPtr = nil;
OSErr
GetProcessMemory(PEntryPtr pProc, Ptr ceilingPtr)
{
Ptr lockPtr;
OSErr retVal;
PCB *pPCB;
lockPtr = nil;
retVal = memFullErr;
THEZONE = ProcessMgrZone;
assert(APPLZONE != THEZONE || STKLOWPT == nil);
/* If a ceiling-application in then load on top of it */
if (myRoverPtr != nil)
THEZONE->allocPtr = myRoverPtr;
/* Punt if the ceiling is below the Process Mgr zone, because we can't
* allocate a "lock block" there. On the other hand, a ceiling above
* the Process Mgr zone is taken care of naturally (because we never go
* higher anyway).
*/
if (ceilingPtr != nil)
{
retVal = memFragErr;
if ( (ceilingPtr >= &THEZONE->heapData) && (ceilingPtr < THEZONE->bkLim) )
lockPtr = GetLockPtr(ceilingPtr);
if (lockPtr == nil)
goto ReturnRetVal;
}
/* Allocate the application partition. The error code for failure should
* be memFragErr if ceilingPtr != nil, memFullErr if ceilingPtr == nil.
*/
if ( (pProc->p_zoneh = ProcessMgrHiNewHandle(pProc->p_size, lockPtr)) == nil)
goto ReturnRetVal;
/* Switch back to plain error once ceiling-application partition has been made */
retVal = memFullErr;
/* lock the physical zone, and strip its tags. */
HLock(pProc->p_zoneh);
pProc->p_zone = (char *)(StripAddress(*pProc->p_zoneh));
/* Set rover for next guy */
if (lockPtr != nil)
{
THEZONE->allocPtr = myRoverPtr = (Ptr)pProc->p_zone + pProc->p_size;
}
/* Get the block in which to record lomem context */
if ( (pProc->p_lmemtool = NewHandle(lmemToolDataSize)) == nil)
goto ReturnRetVal;
/* NOTE: PCB must be initialized to 0's, because much of the data
* needs to be initialized to zero, so this is a fast, convenient way.
*/
if ( (pProc->p_pcb = (PCB **) NewHandleClear(sizeof(PCB))) == nil)
goto ReturnRetVal;
/* Well. Now we have a process control block, and can continue to build
* the rest of the dynamic data structures for this process in the Process Mgr
* heap. When all are allocated, we make sure all really exist and have one
* last chance to back out.
*/
if ( ((*pProc->p_pcb)->vblvars = NewHandle(0)) == nil)
goto ReturnRetVal;
if ( ((*pProc->p_pcb)->dces = NewHandle(0)) == nil)
goto ReturnRetVal;
if ( ((*pProc->p_pcb)->tempMemHListHdl = CreateHList(0)) == nil)
goto ReturnRetVal;
/* Set up the PCB. */
pPCB = *pProc->p_pcb;
pPCB->sysevtmask = everyEvent - keyUpMask;
pPCB->enablePrTypeChanges = PRTYPE_CHANGESTATUS_ENABLED;
retVal = noErr;
ReturnRetVal:
if (lockPtr != nil)
DisposPtr(lockPtr);
THEZONE->allocPtr = nil;
return(retVal);
}
/* GetFinderInfoForFile. Handy routine to set a bunch of app file info we need. */
void
GetFinderInfoForFile(FSSpec *pFileSpec, PEntryPtr pProc)
{
register ScriptCode scriptCode;
CInfoPBRec ioPB;
pProc->p_nameScript = iuSystemScript;
ioPB.hFileInfo.ioNamePtr = &pFileSpec->name;
ioPB.hFileInfo.ioVRefNum = pFileSpec->vRefNum;
ioPB.hFileInfo.ioDirID = pFileSpec->parID;
ioPB.hFileInfo.ioFDirIndex = 0;
ioPB.hFileInfo.ioCompletion = nil;
if (PBGetCatInfo(&ioPB.hFileInfo, SyncHFS) == noErr)
{
pProc->p_type = ioPB.hFileInfo.ioFlFndrInfo.fdType;
pProc->p_signature = ioPB.hFileInfo.ioFlFndrInfo.fdCreator;
scriptCode = (ScriptCode) ((FXInfo *) &ioPB.hFileInfo.ioFlXFndrInfo)->fdScript;
if ((scriptCode & (1 << kScriptValidBit)) != 0)
pProc->p_nameScript = (scriptCode & xfiScriptCodeMask);
if ((((FInfo *) &ioPB.hFileInfo.ioFlFndrInfo)->fdFlags & FILE_FLAG_HAS_CUSTOM_ICON) != 0)
pProc->iconResourceID = kCustomIconID;
}
}
/*---------------------------------------------------------------------------------*
Process Liquidation Routines
*---------------------------------------------------------------------------------*/
/* HandleShellDeath. Determine the significance of the current death. Relaunch
* Finder if we Deep Shat from the it or else tried to _ExitToShell from the last
* layer.
* NOTE: What we really want to do is switch into pNullProcess and have it just
* constantly spawn new Finders. The problem is, we can't really switch to it because
* right now pNullProcess has no context other than registers (and not even these
* if we don't change switch_task()).
* NOTE: Assumes a5 == PROCESSMGRGLOBALS
*/
void
HandleShellDeath(PEntryPtr pDeadProc, OSErr status, Boolean isLastLayer)
{
register PEntryPtr pProc;
Boolean relaunch;
FSSpec finderSpec;
LaunchResults finderLaunchResults;
/* Set likely result */
relaunch = false;
/* Weed out case of crash during LaunchFacelessTasks() */
if (pShellProcess == ((Ptr) -1))
return;
/* At the very least, relaunch Finder if it crashed */
if (pDeadProc == pShellProcess)
{
pShellProcess = nil;
if ((ShellWasFinderType) && (status != noErr))
relaunch = true;
}
/* Handle death of last app on the system. If process we launched from FINDERNAME
* is Finder or Finder-replacement, relaunch it. Otherwise, call for a restart as
* soon as the corpse is buried.
* NOTE: The SysError handler needs the fonts set up still.
*/
if (pProcessList == nil)
{
if (ShellWasFinderType)
relaunch = true;
else
ShutDwnUserChoice();
}
/* Handle last faceful app. If process we launched from FINDERNAME is Finder or a
* Finder-replacement, relaunch it. Otherwise, kill all the remaining apps (i.e.
* daemons). When the last of them dies, we'll be called again and say to shutdown.
*/
else if (isLastLayer)
{
if (ShellWasFinderType)
relaunch = true;
else
{
pProc = pProcessList;
while (pProc != nil)
{
if (pProc != pDeadProc)
KillProcess(&pProc->p_serialNumber);
pProc = pProc->p_NextProcess;
}
}
}
/* Well, do we take the initiative about Finder? */
if (relaunch)
{
if ((finderLaunchResults.LaunchError = FSMakeFSSpec(BOOTVOL, 0, FINDERNAME, &finderSpec)) == noErr)
StandardLaunch(&finderSpec, launchUseMinimum, 0, nil, &finderLaunchResults, nil);
if (finderLaunchResults.LaunchError != noErr)
SysError(dsFinderErr);
/* Wait to do this 'til we're sure. */
pShellProcess = finderLaunchResults.pNewProcess;
pShellProcess->p_dadProcess = nil;
ShellWasFinderType = IS_FINDER(pShellProcess);
(void) SetFrontProcess(&pShellProcess->p_serialNumber);
}
}
/* DisposeProcessMemory. Dispose of the physical memory occupied by the specified process.
* Doesn't assume structures exist, because this may be called as error recovery
* from launch.
* NOTE: Does not explicitly dispose things that are known to reside in the application
* heap, since these are implicitly freed by freeing or resizing the app partition.
*/
void
DisposeProcessMemory(PEntryPtr pProc)
{
PCB *pPCB, **hPCB;
/* Must invalidate rover if it was set for this app's partition */
if (myRoverPtr == ((Ptr)pProc->p_zone + pProc->p_size))
myRoverPtr = nil;
/* Free the partition */
if (pProc->p_zoneh != nil)
{
DisposHandle(pProc->p_zoneh);
pProc->p_zoneh = nil;
}
/* free structures hanging off of PEntry proper */
if (pProc->p_lmemtool != nil)
{
DisposHandle(pProc->p_lmemtool);
pProc->p_lmemtool = nil;
}
/* now free structures hanging off PCB, then PCB itself */
if ((hPCB = pProc->p_pcb) == nil)
return;
pProc->p_pcb = nil;
HLock(hPCB);
pPCB = *hPCB;
if (pPCB->vblvars != nil)
DisposHandle(pPCB->vblvars);
if (pPCB->dces != nil)
DisposHandle(pPCB->dces);
if (HListExists(pPCB->p_PseudoEvtQHList))
DestroyHList(pPCB->p_PseudoEvtQHList);
if (HListExists(pPCB->tempMemHListHdl))
DestroyHList(pPCB->tempMemHListHdl);
/* puppetstringhdl is either a) in app heap (dispose is not needed), or b) a shared
* resource (dispose would be bad for others). Don't dispose it.
*/
if (pPCB->puppetstringhdl != nil)
{
/* DisposHandle(pPCB->puppetstringhdl); */
}
DisposHandle(hPCB);
}
/* MenuKill. Delete Apple menu items defined by this app, then delete application
* name itself. */
void
MenuKill(void)
{
/* Nuke MENULIST because app resource files will be closed, which releases all the
* menu resources, leaving disposed handles in the menulist.
*/
MENULIST = nil;
/* Remove our record of this app having an Apple menu */
AppleMenuCleanup(pCurrentProcess);
/* Update the Application Menu. Must reset the enable state of the hiding items if
* this process is dying in the background.
*/
DeleteAppFromApplicationMenu(pCurrentProcess);
if (pCurrentProcess != LayerOwner(GetFrontAppLayer()))
AdjustApplicationMenu(nil);
}
/* NMKill. Remove all notification manager requests for the current process.
* Criteria is that request memory lies within the application's partition.
* NOTE: This should be a trap call to NM mgr.
*/
void
NMKill(void)
{
register NMRecPtr curNMRec;
NMRecPtr oldNMRec;
Ptr base, bound;
unsigned short ps;
/* Bail out if NM doesn't exist or there are no outstanding requests */
if (NMQHDRPTR == nil || NMQHDRPTR == ((Ptr) -1))
return;
/* Cache the application boundaries */
base = pCurrentProcess->p_zone;
bound = base + pCurrentProcess->p_size;
/* Scan the list. Disables interrupts to avoid collisions. */
curNMRec = NMQHDRPTR->qHead;
ps = disable();
while (curNMRec != nil)
{
oldNMRec = curNMRec; curNMRec = curNMRec->qLink;
if ((oldNMRec >= base) && (oldNMRec <= bound))
(void) NMRemove(oldNMRec);
}
spl(ps);
}
/* FMKill. Remove all font handles for the current process. If the location of these
* handles ever changes to another zone, so should this.
* NOTE: WIDTHTABHANDLE, WIDTHLISTHAND, and SYNLISTHANDLE are nil'd out in case the
* lomem may be used again (esp. for sys startup caller).
* NOTE: Assumes THEZONE != ProcessMgrZone, in that it restores THEZONE unconditionally,
* instead of using SafeSetZone and RestoreZone.
*/
void
FMKill(Boolean disposeSynStrikes)
{
short iTable;
Ptr sysZoneEnd, tableEnd;
Handle widthTabHdl, *widthTabHdlPtr;
SynEntryPtr synEntryPtr;
THz saveZone;
/* All the action here occurs in the system heap */
saveZone = THEZONE;
THEZONE = SYSZONE;
sysZoneEnd = SYSZONE->bkLim;
/* Deallocate the current WIDTHTABHANDLE */
if (WIDTHTABHANDLE != nil)
{
DisposHandle(WIDTHTABHANDLE);
WIDTHTABHANDLE = nil;
}
/* Get rid of any system handle in WIDTHLISTHAND, then dispose the list itself */
if (WIDTHLISTHAND != nil)
{
assert(*WIDTHLISTHAND != nil);
HLock(WIDTHLISTHAND);
widthTabHdlPtr = *WIDTHLISTHAND;
iTable = NUMITABLES - 1;
do
{
/* Table may prematurely end with a 0 */
if ((widthTabHdl = (Handle) StripAddress(*widthTabHdlPtr++)) == nil)
break;
/* Get rid of sys heap handles */
if (widthTabHdl < sysZoneEnd)
DisposHandle(widthTabHdl);
}
while (--iTable >= 0);
/* Dispose of the list itself */
DisposHandle(WIDTHLISTHAND);
WIDTHLISTHAND = nil;
}
/* Get rid of any system handle in SYNLISTHANDLE, then dispose the list itself.
* NOTE: We are guaranteed that when we are called by InitializeEnvironment(), that
* only the first entry could be in the sys heap. Worse yet, a following entry
* might appear to be in the sys heap because it was from a MP block at the
* bottom of the app heap, and a subsequent _SetAppBase has raised the top of
* the sys heap over it. We therefore check that entry is below sysZoneEnd.
*/
if (Colorized && (SYNLISTHANDLE != nil) && ((synEntryPtr = StripAddress(*SYNLISTHANDLE)) != nil))
{
if (disposeSynStrikes)
{
HLock(SYNLISTHANDLE);
tableEnd = (Ptr) synEntryPtr + GetHandleSize(SYNLISTHANDLE);
/* Get rid of sys heap handles */
while ((Ptr) synEntryPtr < tableEnd)
{
if ( (synEntryPtr->synNFNT != (-1)) &&
(StripAddress(synEntryPtr->synStrikeHdl) < sysZoneEnd) )
DisposHandle(synEntryPtr->synStrikeHdl);
synEntryPtr += 1;
}
}
/* Dispose of the list itself */
DisposHandle(SYNLISTHANDLE);
SYNLISTHANDLE = nil;
}
THEZONE = saveZone;
}
/* MakeSynStrikesPurgeable. The Font Manager (spec. _FMSwapFont) always leaves one of
* the synthetic strikes non-purgeable. It stays that way indefinitely, even though the
* Font Manager can easily cope with a purged strike. Since the memory for the strike
* (10's of Kb) comes from the system heap, we would like to see the strike purgeable.
* There is no great time in QuickDraw or the Font Manager we can do this, since the
* caller of _FMSwapFont is a public call (app may assume strike is non-purgeable in
* places the system doesn't). We figure, tho, that by the time the app is making a
* ToolBox event call it can cope with a purgeable strike.
*/
void
MakeSynStrikesPurgeable(void)
{
SynEntryPtr synEntryPtr;
Ptr tableEnd;
if (Colorized && (SYNLISTHANDLE != nil) && ((synEntryPtr = StripAddress(*SYNLISTHANDLE)) != nil))
{
tableEnd = (Ptr) synEntryPtr + GetHandleSize(SYNLISTHANDLE);
while ((Ptr) synEntryPtr < tableEnd)
{
if (synEntryPtr->synNFNT != (-1))
HPurge(synEntryPtr->synStrikeHdl);
synEntryPtr += 1;
}
}
}
/* Message format for aeApplicationDied we send */
typedef struct ApplicationDiedMsg {
AETFHeader messageHeader;
KeyWord metaDataMark;
AETFParameter appDiedErrorHdr;
long appDiedErrorNumber;
AETFParameter appDiedProcessHdr;
ProcessSerialNumber appDiedProcessNumber;
} ApplicationDiedMsg;
/* SendDeathNotice. Notify the specified process that its pal is dying. */
void
SendDeathNotice(PEntryPtr pTargetProc, PEntryPtr pProc, OSErr status)
{
ApplicationDiedMsg msgStorage;
HighLevelEventRecord theEvent;
/* Do nothing if not there, or doesn't want the notification */
if ((pTargetProc == nil) || ((pTargetProc->p_taskmode & modeGetAppDiedMsg) == 0))
return;
/* Set up the high-level event record */
SetAppleEvent(aeApplicationDied, &theEvent);
/* Set up the message buffer */
GenericAEMessage(&msgStorage.messageHeader);
msgStorage.metaDataMark = aeEndOfMetaDataKeyword;
SetAEParmHeader(&msgStorage.appDiedErrorHdr, keyErrorNumber, aeLongintParamType, sizeof(long));
msgStorage.appDiedErrorNumber = (long) status;
SetAEParmHeader(&msgStorage.appDiedProcessHdr, aeProcessKeyword, aeProcessParamType, sizeof(ProcessSerialNumber));
msgStorage.appDiedProcessNumber = pProc->p_serialNumber;
/* Post the event. Do it in system mode since the dying process is incapable of EPPC. */
(void) BeginSystemMode();
allocateGMBlkInSysHeap = true; // <45> Force event to be created in the System heap
(void) PostHighLevelEvent((EventRecord *) &theEvent, (u_long) &pTargetProc->p_serialNumber, 0, &msgStorage, sizeof(ApplicationDiedMsg), receiverIDisPSN);
allocateGMBlkInSysHeap = false; // <45> Go back to trying to allocate from the PM heap first
(void) EndSystemMode();
}
/* c_exittoshell. Here is our patch for ExitToShell(). We don't want this to run as a
* kernel routine!!! Either the application called this in the normal course of events,
* or else some type of exception was fielded by the debugger and the user did an
* exit (e.g. ES in MacsBug) from there, thus setting the DSErrCode. This status is
* passed on to the bereaved parent.
* This is the last subroutine a process ever calls.
*/
pascal void
c_exittoshell(void)
{
PEntryPtr pDeadProc, pOtherProc;
LayerPtr pLayer;
Boolean frontmost, isLastLayer, deathByQuitAll;
PCB **pcbHdl;
OSErr exitStatus;
unsigned long oldA5;
/* Use our A5, but swipe the graphics globals from CURRENTA5 as long as InitGraf has
* been called (i.e. QDEXIST == 0).
*/
if (QDEXIST == (char) 0)
{
A5SimpleRestore(CURRENTA5);
oldA5 = ProcessMgrA5Setup();
}
else
oldA5 = ProcessMgrA5SimpleSetup();
/* Disk switches don't count as sys errors */
if ((exitStatus = (OSErr) DSERRCODE) == dsReinsert)
exitStatus = noErr;
/* Switch to a neutral zone, in case we call something that allocates memory */
THEZONE = SYSZONE;
#if PsychicTV
/* <54> Tear down any CFM-loaded system DLLs or fat resources */
TearDownPsychicTVProcess(pCurrentProcess);
#endif
/* Collect some useful information for later */
pDeadProc = pCurrentProcess;
pcbHdl = pDeadProc->p_pcb;
/* Reset this so that cleanup won't wipe out files/data acquired in system mode */
pDeadProc->p_systemModeLevel = 0;
/* Clean up executable stuff so it doesn't interfere with our cleanup (for example,
* the dead app might have patches to traps we call).
*/
kill_state(pDeadProc);
/* Dump any pending events if app crashed.
* NOTE: Do this only for the foreground process?
*/
if (exitStatus != noErr)
FlushEvents(everyEvent, 0);
/* See if process died in its sleep. Something other than the app code may be
* calling _ExitToShell (like MacsBug or the dsForcedQuit handler).
*/
CancelSleep(pDeadProc);
/* Reset other process' entries */
pOtherProc = pProcessList;
while (pOtherProc != nil)
{
if (pOtherProc->p_dadProcess == pDeadProc)
pOtherProc->p_dadProcess = nil;
pOtherProc = pOtherProc->p_NextProcess;
}
/* Remove from St. Peter's waiting list */
if (pDeadProc->p_condemned)
{
RemoveFromStateList(pDeadProc, &pDyingStateList);
pDeadProc->p_condemned = wFalse;
}
/* Make sure the registered debugger checks out (and takes his debuggees with him) */
if (pDeadProc == pDebugProcess)
{
ProcessSerialNumber noProcess;
SetPSN(kNoProcess, &noProcess);
RegisterDebugger(&noProcess, nil, nil, nil, 0);
}
/* Cancel system quit if the dead app is the one that started the whole thing. */
frontmost = (pDeadProc == pFrontProcess);
deathByQuitAll = false;
if (pSystemQuitAllMsg != nil)
{
if (EqualPSN(&(pSystemQuitAllMsg->sendermfid.localPSN), &pDeadProc->p_serialNumber))
CancelQuitAll(nil);
else
deathByQuitAll = (EqualPSN(&(pSystemQuitAllMsg->targetmfid.localPSN), &pDeadProc->p_serialNumber));
}
/* Cancel puppet stringing. */
PuppetKill();
/* Nuke EPPC and PPC stuff. */
DestroyMsgQ(&pDeadProc->eppcBlk);
/* Cleanup volume notification */
VNKill();
/* Make sure cursor is visible */
if ((frontmost) && (QDEXIST == (char) 0))
InitCursor();
/* Menu manager cleanup */
MenuKill();
/* Re-enable CloseWD for home working directory (p_vrefnum) */
(*pcbHdl)->p_vrefnum = 0;
/* Get system and toolbox to cleanup. Set the zone and A5 since some need them
* to know which structures to wipe.
*/
THEZONE = APPLZONE;
A5SimpleRestore(oldA5);
CleanupApplication();
(void) ProcessMgrA5SimpleSetup();
THEZONE = SYSZONE;
/* Close down the toolbox for the dead process (needs our help) */
if ((pLayer = pDeadProc->p_layer) != nil)
LayerClose(pLayer);
if (pDeadProc->p_floatLayer != nil)
DisposeWindow(pDeadProc->p_floatLayer);
if (pDeadProc == tsmLayerOwner)
tsmLayerOwner = nil;
QDKill();
NMKill();
/* Remember whether this is the last visible layer going away */
isLastLayer = (GetFrontAppLayer() == nil);
/* Reallow switching in case he died in the middle. */
while (dont_switch > 0)
EnableSwitching();
/* Remove from count of background apps, if he is one */
if ((pDeadProc->p_taskmode & modeCanBackground) != 0)
nbacks--;
{
/* unlink pDeadProc from list of active entries */
PEntryPtr pPrev, pNext;
pPrev = pNullProcess;
while ((pNext = pPrev->p_NextProcess) != pDeadProc)
{
assert(pNext != nil);
pPrev = pNext;
}
pPrev->p_NextProcess = pDeadProc->p_NextProcess;
}
/* Adjust BG round-robin placeholder */
if (pDeadProc == pLastBGProcess)
{
if ((pLastBGProcess = pDeadProc->p_NextProcess) == nil)
pLastBGProcess = pProcessList;
}
/* Take out of queue if dead guy was waiting to come to the front */
RemoveFromFrontProcessQueue(pDeadProc);
/* Reset global so we don't end up switching to same guy! */
if (pDeadProc == pNewFrontProcess)
pNewFrontProcess = nil;
/* Let the world know of the tragedy that has beset us */
SendDeathNotice(pDeadProc->p_dadProcess, pDeadProc, exitStatus);
if (pDebugProcess != pDeadProc->p_dadProcess)
SendDeathNotice(pDebugProcess, pDeadProc, exitStatus);
/* Handle shell quit or crash, or death of the last layer.
* NOTE: We wait to FMKill until after HandleShellDeath, because fonts will be
* needed if a SysError is generated (like dsFinderErr, or inside ShutDwnUserChoice).
*/
HandleShellDeath(pDeadProc, exitStatus, isLastLayer);
FMKill(true);
/* Remove the process' memory. Be very careful for awhile, since some of the basic
* structures are about to become invalid!
* NOTE: OrphanScrapIntoPartition commandeers the partition to hold the scrap until
* it coercion is satisfied. This means we'd better be done with everything we need
* in the partition!
*/
OrphanScrapIntoPartition();
pCurrentProcess = nil;
DisposeProcessMemory(pDeadProc);
DisposeProcessEntry(pDeadProc);
/* Set pFrontProcess to new front process now that current guy is dead (in the
* event we don't call cpu_resched() below).
* Candidates:
* • current frontmost if that isn't the dead app (pFrontProcess)
* • the process from SetFrontProcess() or canceled switch (pNewFrontProcess)
* • process waiting to come to the front (PopFrontProcessQueue())
* • process that initiated a system quit (from pSystemQuitAllMsg)
* • process controlling the top layer
*/
/* See if we need to select new foreground app */
if (frontmost)
{
if ( ((pOtherProc = pNewFrontProcess) == nil) &&
((pOtherProc = PopFrontProcessQueue()) == nil) )
{
if (pSystemQuitAllMsg != nil)
pOtherProc = PEntryFromPSN(&(pSystemQuitAllMsg->sendermfid.localPSN));
else
{
if ((pLayer = LGetNextLayer(nil, true)) == nil)
pLayer = LGetNextLayer(nil, false);
assert(pLayer != nil);
pOtherProc = LayerOwner(pLayer);
}
}
assert(IsProcessEntry(pOtherProc));
pFrontProcess = pOtherProc;
}
/* User deaths require full scrap-in switches. Background deaths cause control to
* go the frontmost, unless generalized rescheduling was requested. Otherwise, it
* was a simple murder of a non-frontmost process.
*/
/* Check for death of foreground process with other facefuls available */
if (frontmost && (isLastLayer == false) && (pFrontProcess != nil))
{
/* Jump into end of coercion (scrap info set by OrphanScrapIntoPartition) */
coercionState = CS_ENTERING;
/* So that new front process sees valid menu bar */
AdjustApplicationMenu(pFrontProcess);
/* Switch to front process we located earlier */
pNewFrontProcess = nil;
CancelSleep(pFrontProcess);
pFrontProcess->p_state = PRMOVING;
/* Reset OSDispatch depth indicator. This is positioned such that we have
* already made all the OSDispatch calls we need before the switch.
*/
kernelbusy = 0;
switch_task(pFrontProcess);
}
/* Death of BG process or last foreground process */
else
{
/* Continue system quit, if it's happening */
if (deathByQuitAll)
ContinueQuitAll();
/* Reset OSDispatch depth indicator. This is positioned such that we have
* already made all the OSDispatch calls we need before the switch.
*/
kernelbusy = 0;
cpu_resched();
}
dbmsg("ExitToShell failed to schedule a different application.");
}
/*----------------------------------------------------------------------------------*
* Process Access Routines *
*----------------------------------------------------------------------------------*/
/* PEntryFromPID. Locate existing process having given ProcessID. */
PEntryPtr
PEntryFromPID(ProcessID pid)
{
register PEntryPtr pProc;
unsigned long oldA5;
/* Loop until ProcessID matches or end of list */
oldA5 = ProcessMgrA5SimpleSetup();
pProc = pProcessList;
while (pProc != nil)
{
if ((pProc->p_mypid) == pid)
break;
pProc = pProc->p_NextProcess;
}
A5SimpleRestore(oldA5);
return(pProc);
}
/* EqualPSN. Return whether the two PSNs are identical. */
Boolean
EqualPSN(ProcessSerialNumberPtr pFirstPSN, ProcessSerialNumberPtr pSecondPSN)
{
return( (pFirstPSN->lowLongOfPSN == pSecondPSN->lowLongOfPSN) &&
(pFirstPSN->highLongOfPSN == pSecondPSN->highLongOfPSN) );
}
/* PEntryFromPSN. Locate existing process having given ProcessSerialNumber.
* NOTE: Caller may be passing in PSN address from heap block, so we should
* be careful to not move memory in this call.
*/
PEntryPtr
PEntryFromPSN(ProcessSerialNumberPtr pPSN)
{
register PEntryPtr pProc;
unsigned long oldA5;
/* Weed out the junkies */
if (pPSN == nil)
return(nil);
/* Look first for special value */
oldA5 = ProcessMgrA5SimpleSetup();
if ((pPSN->lowLongOfPSN == kCurrentProcess) && (pPSN->highLongOfPSN == 0))
pProc = pCurrentProcess;
else
{
/* Loop until ProcessSerialNumber matches or end of list */
pProc = pProcessList;
while (pProc != nil)
{
if (EqualPSN(&pProc->p_serialNumber, pPSN))
break;
pProc = pProc->p_NextProcess;
}
}
A5SimpleRestore(oldA5);
return(pProc);
}
/* PEntryFromFileSpec. See if we can find a process that is running off the
* the specified file. Include the pResName if you're looking for a DA.
* NOTE: Does PBGetFCBInfo return appropriate ioFCBParID on MFS volume?
*/
PEntryPtr
PEntryFromFileSpec(FSSpecPtr pSpec, StringPtr pResName)
{
PEntryPtr pProc;
FCBPBRec fcbInfoRec;
Str255 rsrcFileName;
/* Iterate through the process list */
pProc = pProcessList;
fcbInfoRec.ioCompletion = nil;
while (pProc != nil)
{
/* Get resource file spec for this process */
fcbInfoRec.ioFCBIndx = 0;
fcbInfoRec.ioRefNum = (*pProc->p_pcb)->p_fileRefNum;
fcbInfoRec.ioNamePtr = &rsrcFileName;
if (PBGetFCBInfo(&fcbInfoRec, SyncHFS) == noErr)
{
/* See if spec matches */
if ((pSpec->vRefNum == fcbInfoRec.ioFCBVRefNum) && (pSpec->parID == fcbInfoRec.ioFCBParID))
{
if ( (MyRelString(&pSpec->name, &rsrcFileName) == sortsEqual) &&
((pResName == nil) || (MyRelString(pResName, &((*pProc->p_pcb)->curapname)) == sortsEqual)) )
break;
}
}
pProc = pProc->p_NextProcess;
}
return (pProc);
}
#if PsychicTV
/*----------------------------------------------------------------------------------*
* <54> PsychicTV Support Routines *
*----------------------------------------------------------------------------------*/
/* Routines added to support launching of Native applications under RISC System Software */
/* These routines can also be generalized to support Code Fragment Manager on 68K */
Boolean
FindFragmentEntryPoint(FSSpec *theAppFSSpec,FragmentLocatorPtr mainFragment,OSType *architecture)
{
// <55> were looking for a longword version for now, and we only support version 0.
// (PowerPC code in the datafork).
long **codeFragIndex;
if ((codeFragIndex = (long **) Get1Resource('cfri',0)) && (**codeFragIndex == 0))
{
mainFragment->where = kOnDiskFlat; // In the data fork
mainFragment->u.onDisk.fileSpec = theAppFSSpec;
mainFragment->u.onDisk.offset = 0; // the whole data fork is the container
mainFragment->u.onDisk.length = 0; // another for the whole data fork
*architecture = kPowerPC; // this is all we have now…
ReleaseResource(codeFragIndex);
return(true);
}
else
return(false);
}
OSErr
SetupPsychicTVProcess(PEntryPtr pNewProc,Boolean launchingFragment,FragmentLocatorPtr mainFragment,
OSType architecture,RoutineDescriptor *nativeEntryPoint)
{
OSErr err;
FragContextID fragContext;
FragConnectionID fragConnection;
LogicalAddress entryPoint;
Str255 badLibName;
err = FragCreateContext(&fragContext,
pNewProc->p_zone,
kDefaultAddrSpaceID,
kDefaultTeamID,
kDefaultTaskID,
0);
if (err != noErr)
return(err);
(*pNewProc->p_pcb)->fragContext = fragContext; // Save contextID for later use
if (launchingFragment)
{
if (architecture != kPowerPC)
return(-1);
err = FragPrepare( fragContext,
mainFragment,
kNoLibName,
kAttachStatic,
architecture, // usually PowerPC
&fragConnection,
&entryPoint,
badLibName); // Is the errorString useful to "Joe User"?
if (err == noErr)
*nativeEntryPoint = NewRoutineDescriptor((ProcPtr) entryPoint,kCStackBased+kNoReturnValue,kCodeTypePower);
else
{
// We couldnt load a fragment, so destroy the context, and return the
// error code. We could pass the badLibName up a level, but I'm not sure
// this is useful to the average user. Maybe a debugging version could…
DebugStr("\pPrepare Failed!");
(void) FragRelease(fragContext,kAllConnections,kDefaultRelease,kNormalTerm);
(*pNewProc->p_pcb)->fragContext = 0; // so we dont double-release
return (err);
}
}
return(noErr);
}
void
TearDownPsychicTVProcess(PEntryPtr pProc)
{
FragErr err;
FragContextID fragContext;
fragContext = (*pProc->p_pcb)->fragContext;
if (fragContext)
{
err = FragRelease(fragContext,kAllConnections,kDefaultRelease,kNormalTerm);
if (err != noErr)
FragRelease(fragContext,kAllConnections,kDefaultRelease,kAbnormalTerm);
}
}
#endif PsychicTV