/* * PATCHES.C vi:ts=4 * * Copyright (c) Eddy Carroll, September 1994. * * Controls the patching of dos.library and other functions in a * reliable manner. */ #define DEBUG_PATTERN 0 #define MONITOR_SEMAPHORE 0 #define STACK_CHECK 0 /* Not currently used */ #pragma libcall SysBase RawPutChar 204 001 #include "system.h" #include "snoopdos.h" #include "patches.h" /* * These four pointers are imported from PATCHCODE.S, which is * also where the real code size is determined. */ #define CODESIZE 88 /* Must equal PatchCode_End - PatchCode_Start */ #define STACKADJUST 14 /* Must equal pc_JumpOrigFunc - pc_NormalReturn */ #define PatchCode_Size (PatchCode_End - PatchCode_Start) #define PatchCode_StackAdjust (PatchCode_JumpOrigFunc - PatchCode_NormalReturn) extern char far PatchCode_Start[]; extern char far PatchCode_NormalReturn[]; extern char far PatchCode_JumpOrigFunc[]; extern char far PatchCode_End[]; #define ASM __asm __saveds #if MONITOR_SEMAPHORE Task *LoadTask; /* Indicates we're inside LoadSeg() */ #endif /* * Now some packet IDs not defined by Commodore in dos/dosextens.h */ #define ACTION_DOUBLE 2000 /* Conman: create pipe filehandle */ #define ACTION_FORCE 2001 /* Conman: Force input into handler */ #define ACTION_DROP 2004 /* Conman: Discard all queued input */ #define ACTION_GET_DISK_FSSM 4201 /* Get disk startup message */ #define ACTION_FREE_DISK_FSSM 4202 /* Free disk startup message */ /* * Next, our three message types for the background process. These * correspond to pattern and pathname expansion messages. */ #define QUIT_MSG 0 #define PATTERN_MSG 1 #define PATHEXPAND_MSG 2 /* * Some miscellaneous strings */ char LinkPointerString[] = " --> "; /* * I needed a quick way to hook into ReleaseSemaphore() to try and * figure out why ramlib would sometimes crash on calling it. What * better way to do this than by re-using one of our existing * patched functions (one which takes a parameter in A0). OpenDevice * fits the bill nicely (we just ignore the additional parameters). */ #if MONITOR_SEMAPHORE #undef LVO_OpenDevice #define LVO_OpenDevice -570 // ReleaseSemaphore() LVO vector #endif /* * This structure is used to communicate between the PutMsg() patch * and the background SnoopDos process when doing path expansion * for ACTION_MAKE_LINK or ShowFullPaths (when we're monitoring packets, * we can't guarantee that the calling processes' message port is free). */ typedef struct NameFromLockMsg { struct Message msg; /* Standard exec message */ int type; /* Type of this message */ BPTR lock; /* Lock to calculate path relative to */ char *filename; /* Filename relative to lock */ char *buf; /* Buffer to store result in */ int maxlen; /* Maximum length of buffer */ char *newbuf; /* On return, points to path in buffer */ int sigmask; /* Signal number to signal when done */ Task *task; /* Task to signal when done */ } NameFromLockMsg; /* * This structure is used to communicate between the patches and * the background SnoopDos process when doing pattern matching. */ typedef struct PatternMsg { struct Message msg; /* Standard exec message */ int type; /* Type of this message */ char *name; /* Name of task to check */ int match; /* Result of pattern match (1 == okay) */ int sigmask; /* Signal number to signal when done */ Task *task; /* Task to signal when done */ } PatternMsg; /* * This structure is used to cache the results of pattern comparisons * for various tasks. */ typedef struct PatternCache { struct MinNode node; /* Used to link items together */ Task *task; /* ID of task stored in this entry */ char name[PC_NAMELEN];/* Name of task stored in this entry */ int match; /* True=monitor this task, false=don't */ } PatternCache; PatternCache PCacheEntries[NUM_PCACHE_ENTRIES]; /* Entries on list */ MsgPort BackgroundPort; /* Where to send pattern match requests to */ List PatternList; /* List of cached patterns */ Semaphore PatternCacheSem;/* Sem to control access to pattern cache */ Semaphore PatternBufSem; /* Sem to control access to pattern buffer */ Semaphore TaskCacheSem; /* Sem used to arbitrate cache access */ Semaphore DosDeviceSem; /* Sem used to control access to dev list */ Process *BackgroundProc;/* Process used to do pattern matching */ ULONG PatternEnabled; /* If true, pattern matching is enabled */ ULONG PatternWildcard; /* If true, pattern contains a wildcard */ Task *SnoopTask; /* Pointer to our own task */ Task *RamLibTask; /* Pointer to RamLib process */ Task *RealRamLibTask; /* Pointer to Ramlib process (always!) */ Task *InputTask; /* Pointer to input.device's task */ ULONG NewEventSig = -1;/* Signal bit used when new event ready */ ULONG ScanDosListSig = -1;/* Signal bit used to say rescan list */ ULONG TaskCacheIndex = 1; /* Index into task cache array */ #define PAT_MAX_LEN (MAX_STR_LEN + 50) #define PAT_BUF_LEN (PAT_MAX_LEN*2 + 3) char PatternString[PAT_MAX_LEN]; /* Holds source pattern */ char PatternBuf[PAT_BUF_LEN]; /* Holds parsed pattern */ Task *CachedTask[NUM_CACHED_TASKS]; /* Holds cached tasks */ Task *DeviceTaskList[MAX_DOS_DEVICES]; /* Holds device IDs */ /* * This structure records outstanding packets (for direct packet i/o * that bypasses AmigaDOS) which we are watching out for a reply to. * We keep all the additional info because it's likely that any given * task will re-use a packet structure when sending packets to different * DOS processes, and we want to be able to distinguish these cases. */ typedef struct WaitPacket { struct MinNode node; /* Used to link items together */ struct DosPacket *dp; /* Packet that was dispatched */ struct Task *sendtask; /* Task that sent it */ struct MsgPort *destport; /* Port it was sent to */ LONG eventnum; /* Associated event number */ Event *event; /* Associated event pointer */ ULONG arg1; /* ARG1 of the packet */ ULONG arg2; /* ARG2 of the packet */ ULONG arg3; /* ARG3 of the packet */ char *resmsg; /* Message to use for result */ UWORD flags; /* Flags associated with result */ } WaitPacket; Semaphore PacketSem; /* Arbitrates list access */ struct List PacketWaitList; /* List of waiting packets */ struct List PacketFreeList; /* List of free nodes */ WaitPacket PacketEntries[NUM_PACKET_ENTRIES]; /* The nodes on the list */ /* * Prototypes for all our replacement patched functions */ void BackgroundProcCode(void); char *MyNameFromLock(BPTR lock, char *filename, char *buf, int maxlen); typedef unsigned long (*FuncPtr)(); #define FPROTO(name) ULONG ASM New_##name(){return (0);} #define DPROTO(name) ULONG ASM New_##name(); DPROTO(AddDosEntry) DPROTO(CurrentDir) DPROTO(DeleteFile) DPROTO(Execute) DPROTO(GetVar) DPROTO(FindVar) DPROTO(LoadSeg) DPROTO(NewLoadSeg) DPROTO(Lock) DPROTO(CreateDir) DPROTO(MakeLink) DPROTO(Open) DPROTO(Rename) DPROTO(RunCommand) DPROTO(SetVar) DPROTO(DeleteVar) DPROTO(SystemTagList) DPROTO(FindPort) DPROTO(FindResident) DPROTO(FindSemaphore) DPROTO(FindTask) DPROTO(OpenDevice) DPROTO(OpenLibrary) DPROTO(OpenResource) DPROTO(PutMsg) DPROTO(OpenFont) DPROTO(LockPubScreen) DPROTO(FindToolType) DPROTO(MatchToolValue) /* * These two defines control whether or not a patch is enabled. It's * vital that PATCH_ENABLED be -1 rather than simply any non-zero * value -- see the comments in patchcode.s for more details. */ #define PATCH_ENABLED ((ULONG)(-1)) #define PATCH_DISABLED 0 /* * MarkCallAddr * CallAddr * * These two macros allow us to determine what address we were called * from in a patch function. MarkCallAddr should be the first * declaration at the start of the patch function, and then CallAddr * will correspond to the caller's address throughout that function. * callmarker[0] is the place marker itself, callmarker[1] is the * immediate return address, and callmarker[3] takes into the * account the assembly language patch stub as well. */ #define MarkCallAddr ULONG volatile callmarker[1] #define CallAddr (callmarker[6]) /* * JumpOrigFunc(retval) * * This macro lets us return and call the original function from the * resident patch code rather than from our C code. The passed * parameter can be 0 in most cases, but if the function we're using * this in had a formal parameter passed in reg_d0, then that * formal parameter must be used as the return value. * * We implement this by patching the stack directly to adjust the * return address so that we arrive back at a second bit of code * instead of the first bit. */ #define JumpOrigFunc(retval) \ { callmarker[1] += STACKADJUST; \ return (ULONG)(retval); } /* * The patch structure itself. Don't change the order of the first * four fields without updating the code in PATCHCODE.S as well! * * Note that we use a library number rather than a pointer to an * actual library, for convenience. */ typedef struct Patch { UWORD code[CODESIZE/2]; /* Assembly code to handle patch */ FuncPtr origfunc; /* Original function pointer */ FuncPtr newfunc; /* Replacement function pointer */ ULONG enabled; /* If -1, patch currently active */ void *sysbase; /* Points to ExecBase */ ULONG stackneeded; /* #bytes free stack required */ UWORD usecount; /* No. of tasks currently in code */ UWORD library; /* Library to patch */ WORD offset; /* Offset into library */ WORD pad; /* Keep structure longword-aligned */ } Patch; #define PATCH_DEF(lib,func) { lib, LVO_##func, (FuncPtr)New_##func } #define PATCH_END { 0, 0, 0 } /* * This structure is used to initialise the main Patch array in memory. * * IMPORTANT -- these patches MUST be defined in exactly the same * order as the first entries in the GID_* defines in snoopdos.h * since those same GID_* values are used to toggle the patches * on and off. */ struct PatchInitTable { UWORD library; /* Library number to patch */ WORD offset; /* Offset in that library */ FuncPtr newfunc; /* Replacement function to call */ } PatchInit[] = { PATCH_END, PATCH_DEF( EXEC_LIB, FindPort ), /* GID_FINDPORT */ PATCH_DEF( EXEC_LIB, FindResident ), /* GID_FINDRESIDENT */ PATCH_DEF( EXEC_LIB, FindSemaphore ), /* GID_FINDSEMAPHORE */ PATCH_DEF( EXEC_LIB, FindTask ), /* GID_FINDTASK */ PATCH_DEF( INTUITION_LIB, LockPubScreen ), /* GID_LOCKSCREEN */ PATCH_DEF( EXEC_LIB, OpenDevice ), /* GID_OPENDEVICE */ PATCH_DEF( GRAPHICS_LIB, OpenFont ), /* GID_OPENFONT */ PATCH_DEF( EXEC_LIB, OpenLibrary ), /* GID_OPENLIBRARY */ PATCH_DEF( EXEC_LIB, OpenResource ), /* GID_OPENRESOURCE */ PATCH_DEF( ICON_LIB, FindToolType ), /* GID_READTOOLTYPES */ PATCH_DEF( EXEC_LIB, PutMsg ), /* GID_SENDREXX */ PATCH_DEF( DOS_LIB, CurrentDir ), /* GID_CHANGEDIR */ PATCH_DEF( DOS_LIB, DeleteFile ), /* GID_DELETE */ PATCH_DEF( DOS_LIB, Execute ), /* GID_EXECUTE */ PATCH_DEF( DOS_LIB, GetVar ), /* GID_GETVAR */ PATCH_DEF( DOS_LIB, LoadSeg ), /* GID_LOADSEG */ PATCH_DEF( DOS_LIB, Lock ), /* GID_LOCKFILE */ PATCH_DEF( DOS_LIB, CreateDir ), /* GID_MAKEDIR */ PATCH_DEF( DOS_LIB, MakeLink ), /* GID_MAKELINK */ PATCH_DEF( DOS_LIB, Open ), /* GID_OPENFILE */ PATCH_DEF( DOS_LIB, Rename ), /* GID_RENAME */ PATCH_DEF( DOS_LIB, RunCommand ), /* GID_RUNCOMMAND */ PATCH_DEF( DOS_LIB, SetVar ), /* GID_SETVAR */ PATCH_DEF( DOS_LIB, SystemTagList ), /* GID_SYSTEM */ /* * Now the paired functions that track their partner's state */ PATCH_DEF( ICON_LIB, MatchToolValue ), /* GID_READTOOLTYPES2 */ PATCH_DEF( DOS_LIB, NewLoadSeg ), /* GID_LOADSEG2 */ PATCH_DEF( DOS_LIB, FindVar ), /* GID_GETVAR2 */ PATCH_DEF( DOS_LIB, DeleteVar ), /* GID_SETVAR2 */ PATCH_DEF( DOS_LIB, AddDosEntry ), /* GID_ADDDOSENTRY */ PATCH_END }; #define PatchInitCount (sizeof(PatchInit) / sizeof(PatchInit[0])) #define PatchInitSize (PatchInitCount * sizeof(Patch)) /* * This anchor structure is used to let us locate the patches if we * quit SnoopDos and then rerun it -- the patches themselves always * stay in memory once loaded. */ typedef struct { Semaphore sem; /* Semaphore to arbitrate access to patches */ char name[30]; /* Somewhere permanent to store sem name */ Patch patchdata[1]; /* This is an open-ended array */ } PatchAnchorData; PatchAnchorData *PatchAnchor; /* Points to the current patch anchor */ /* * If SAS/C allowed us to examine the value of Enum types from * within the preprocessor, the following check would actually work */ #if SASC_ALLOWS_ENUM_CHECKS #if (sizeof(PatchInit)/sizeof(PatchInit[0])) < (GID_NUMPATCHES+1) #error "PatchInit[] table has too few entries for patches" #else #if (sizeof(PatchInit)/sizeof(PatchInit[0])) > (GID_NUMPATCHES+1) #error "PatchInit[] table has too many entries for patches" #endif #endif #endif void *LibList[NUM_LIBS]; Patch *PatchList; /* * Now we have a big table of DOS packets that we recognise while * monitoring. For each packet, we record the internal AmigaDOS ID * we use to recognise it, the message ID of its name, the number * of dp_Args to display, and the number of results to display. */ /* The following constants define how many parameters are returned * by the handler when it receives this packet. PK_0 means there is * no return value, PK_1 means 1 return value, PK_2 means there are * two return values (with the second being the error code if the * first one indicates failure) and PK_2OK means there are two actual * returned values, both of which are legitimate. */ #define PK_0 0 /* No return value from this packet */ #define PK_1 1 /* One return value from this packet */ #define PK_2 2 /* Two return values from this packet */ #define PK_2OK 3 /* Two return values even when okay */ #define PK_MASK 3 /* Used to extract PK_CODE */ /* * How to detect when an event failed. Selecting none of these means * that the packet can never fail. */ #define PKF_BOOL 0x08 /* True if zero result == fail */ #define PKF_NEG 0x10 /* True if -1 result == fail */ /* * We want to completely ignore some packets, namely all those sent * by the handler to lower level devices like timer.device or scsi.device * for its own needs. We use PK_IGNORE to identify all such packets * so we can discard them. * * PK_COMMON packets are those which are monitored when the * "Monitor Packets" option is enabled. These are connected * with the associated DOS functions Open(), Lock() etc. and * are output as such by SnoopDos, rather than as raw packets. * * PK_RAW is used for our sentinel packet which indicates that * even the packet type should be output since it's not recognised. */ #define PK_COMMON 0x20 /* Common packet (Open, Lock, etc.) */ #define PK_RAW 0x40 /* Completely raw packet */ #define PK_IGNORE 0x80 /* Ignore this packet completely */ /* * Most packets return a value in dp_Res1 and if that value is * zero or -1, then the error code is in dp_Res2. We define * two constants to handle these common case. */ #define PK_BOOLEAN (PKF_BOOL | PK_2) #define PK_NEGATIVE (PKF_NEG | PK_2) /* * Warning: this table MUST be sorted by packet ID in ascending order, * since a binary search is used to locate packet types */ #define LAST_PACK_MSG 0 struct PacketRef { UWORD packetid; /* The ID we watch out for */ UWORD msgid; /* The name of the action to print */ UBYTE numparams; /* Number of parameters to display */ UBYTE flags; /* How to interpret the result */ } PacketTable[] = { ACTION_STARTUP, MSG_ACT_STARTUP, 1, PK_1, ACTION_GET_BLOCK, MSG_ACT_GET_BLOCK, 1, PK_IGNORE, ACTION_SET_MAP, MSG_ACT_SET_MAP, 1, PK_IGNORE, ACTION_DIE, MSG_ACT_DIE, 0, PK_BOOLEAN, ACTION_EVENT, MSG_ACT_EVENT, 1, PK_IGNORE, ACTION_CURRENT_VOLUME, MSG_ACT_CURRENT_VOLUME, 1, PK_2OK, ACTION_LOCATE_OBJECT, MSG_ACT_LOCATE_OBJECT, 3, PK_BOOLEAN | PK_COMMON, ACTION_RENAME_DISK, MSG_ACT_RENAME_DISK, 1, PK_BOOLEAN, ACTION_FREE_LOCK, MSG_ACT_FREE_LOCK, 1, PK_BOOLEAN, ACTION_DELETE_OBJECT, MSG_ACT_DELETE_OBJECT, 2, PK_BOOLEAN | PK_COMMON, ACTION_RENAME_OBJECT, MSG_ACT_RENAME_OBJECT, 4, PK_BOOLEAN | PK_COMMON, ACTION_MORE_CACHE, MSG_ACT_MORE_CACHE, 1, PKF_BOOL | PK_2OK, ACTION_COPY_DIR, MSG_ACT_COPY_DIR, 1, PK_BOOLEAN, ACTION_WAIT_CHAR, MSG_ACT_WAIT_CHAR, 1, PKF_BOOL | PK_2OK, ACTION_SET_PROTECT, MSG_ACT_SET_PROTECT, 4, PK_BOOLEAN, ACTION_CREATE_DIR, MSG_ACT_CREATE_DIR, 2, PK_BOOLEAN | PK_COMMON, ACTION_EXAMINE_OBJECT, MSG_ACT_EXAMINE_OBJECT, 2, PK_BOOLEAN, ACTION_EXAMINE_NEXT, MSG_ACT_EXAMINE_NEXT, 2, PK_BOOLEAN, ACTION_DISK_INFO, MSG_ACT_DISK_INFO, 1, PK_BOOLEAN, ACTION_INFO, MSG_ACT_INFO, 2, PK_BOOLEAN, ACTION_FLUSH, MSG_ACT_FLUSH, 0, PK_BOOLEAN, ACTION_SET_COMMENT, MSG_ACT_SET_COMMENT, 4, PK_BOOLEAN, ACTION_PARENT, MSG_ACT_PARENT, 1, PK_BOOLEAN, ACTION_TIMER, MSG_ACT_TIMER, 1, PK_IGNORE, ACTION_INHIBIT, MSG_ACT_INHIBIT, 1, PK_BOOLEAN, ACTION_DISK_TYPE, MSG_ACT_DISK_TYPE, 1, PK_IGNORE, ACTION_DISK_CHANGE, MSG_ACT_DISK_CHANGE, 1, PK_1, ACTION_SET_DATE, MSG_ACT_SET_DATE, 4, PK_BOOLEAN, ACTION_SAME_LOCK, MSG_ACT_SAME_LOCK, 2, PK_BOOLEAN, ACTION_READ, MSG_ACT_READ, 3, PK_NEGATIVE, ACTION_WRITE, MSG_ACT_WRITE, 3, PK_NEGATIVE, ACTION_SCREEN_MODE, MSG_ACT_SCREEN_MODE, 1, PK_BOOLEAN, ACTION_CHANGE_SIGNAL, MSG_ACT_CHANGE_SIGNAL, 3, PKF_BOOL | PK_2OK, ACTION_READ_RETURN, MSG_ACT_READ_RETURN, 1, PK_IGNORE, ACTION_WRITE_RETURN, MSG_ACT_WRITE_RETURN, 1, PK_IGNORE, ACTION_FINDUPDATE, MSG_ACT_FINDUPDATE, 3, PK_BOOLEAN | PK_COMMON, ACTION_FINDINPUT, MSG_ACT_FINDINPUT, 3, PK_BOOLEAN | PK_COMMON, ACTION_FINDOUTPUT, MSG_ACT_FINDOUTPUT, 3, PK_BOOLEAN | PK_COMMON, ACTION_END, MSG_ACT_END, 1, PK_BOOLEAN, ACTION_SEEK, MSG_ACT_SEEK, 3, PK_NEGATIVE, ACTION_FORMAT, MSG_ACT_FORMAT, 2, PK_BOOLEAN, ACTION_MAKE_LINK, MSG_ACT_MAKE_LINK, 4, PK_BOOLEAN | PK_COMMON, ACTION_SET_FILE_SIZE, MSG_ACT_SET_FILE_SIZE, 3, PK_NEGATIVE, ACTION_WRITE_PROTECT, MSG_ACT_WRITE_PROTECT, 2, PK_BOOLEAN, ACTION_READ_LINK, MSG_ACT_READ_LINK, 4, PK_BOOLEAN, ACTION_FH_FROM_LOCK, MSG_ACT_FH_FROM_LOCK, 2, PK_BOOLEAN, ACTION_IS_FILESYSTEM, MSG_ACT_IS_FILESYSTEM, 0, PK_BOOLEAN, ACTION_CHANGE_MODE, MSG_ACT_CHANGE_MODE, 3, PK_BOOLEAN, ACTION_COPY_DIR_FH, MSG_ACT_COPY_DIR_FH, 1, PK_BOOLEAN, ACTION_PARENT_FH, MSG_ACT_PARENT_FH, 1, PK_BOOLEAN, ACTION_EXAMINE_ALL, MSG_ACT_EXAMINE_ALL, 5, PK_BOOLEAN, ACTION_EXAMINE_FH, MSG_ACT_EXAMINE_FH, 2, PK_BOOLEAN, ACTION_EXAMINE_ALL_END, MSG_ACT_EXAMINE_ALL_END, 5, PK_BOOLEAN, ACTION_SET_OWNER, MSG_ACT_SET_OWNER, 4, PK_BOOLEAN, ACTION_DOUBLE, MSG_ACT_DOUBLE, 3, PK_BOOLEAN, ACTION_FORCE, MSG_ACT_FORCE, 3, PK_BOOLEAN, ACTION_STACK, MSG_ACT_STACK, 3, PK_BOOLEAN, ACTION_QUEUE, MSG_ACT_QUEUE, 3, PK_BOOLEAN, ACTION_DROP, MSG_ACT_DROP, 1, PK_BOOLEAN, ACTION_LOCK_RECORD, MSG_ACT_LOCK_RECORD, 5, PK_BOOLEAN, ACTION_FREE_RECORD, MSG_ACT_FREE_RECORD, 3, PK_BOOLEAN, ACTION_ADD_NOTIFY, MSG_ACT_ADD_NOTIFY, 1, PK_BOOLEAN, ACTION_REMOVE_NOTIFY, MSG_ACT_REMOVE_NOTIFY, 1, PK_BOOLEAN, ACTION_SERIALIZE_DISK, MSG_ACT_SERIALIZE_DISK, 0, PK_BOOLEAN, ACTION_GET_DISK_FSSM, MSG_ACT_GET_DISK_FSSM, 0, PK_BOOLEAN, ACTION_FREE_DISK_FSSM, MSG_ACT_FREE_DISK_FSSM, 0, PK_BOOLEAN, /* * Our final table entry is used as a sentinel -- we key off the * final packet message as an indication that we're finished * searching the table. */ 0, LAST_PACK_MSG, 4, PK_2OK | PK_RAW }; #define NUM_PACKETS (sizeof(PacketTable) / sizeof(PacketTable[0])) /***************************************************************************** * * Start of functions! * *****************************************************************************/ /* * InitPatches() * * Sets up the library pointers and makes sure all the patch * code vectors are initialised properly. * * Also allocates memory for the patches (if necessary) * * Returns TRUE for success, FALSE for failure. */ int InitPatches(void) { struct PatchInitTable *pi; Patch *p; int i; /* * Before doing anything else, do a quick check to ensure that * the patch code in PATCHCODE.S agrees with the equivalent patch * structure defined in this file. */ if (CODESIZE != PatchCode_Size || STACKADJUST != PatchCode_StackAdjust) { Printf("In patches.c, adjust CODESIZE by %ld and STACKADJUST by %ld\n", PatchCode_Size-CODESIZE, PatchCode_StackAdjust-STACKADJUST); return (FALSE); } LibList[DOS_LIB] = DOSBase; LibList[EXEC_LIB] = SysBase; LibList[INTUITION_LIB] = IntuitionBase; LibList[GRAPHICS_LIB] = GfxBase; LibList[ICON_LIB] = IconBase; /* * First, let's calculate our ROM start and end address. * We do this by getting the address pointed to by the * Open LVO vector in exec.library and rounding it down * to the nearest 512K */ RomStart = (*((ULONG *)(((char *)SysBase) + LIB_OPEN + 2))) & 0xFFF80000; RomEnd = RomStart + 0x7FFFF; /* * Now see if the patches were already installed by an earlier * version of SnoopDos. */ Forbid(); PatchAnchor = (PatchAnchorData *)FindSemaphore(PATCHES_NAME); if (!PatchAnchor) { /* * Patches weren't found, so allocate a new set and initialise * it with our patch code. */ PatchAnchor = AllocMem(sizeof(PatchAnchorData) + PatchInitSize, MEMF_ANY | MEMF_CLEAR); if (!PatchAnchor) { Permit(); return (FALSE); } strcpy(PatchAnchor->name, PATCHES_NAME); PatchAnchor->sem.ss_Link.ln_Name = PatchAnchor->name; PatchAnchor->sem.ss_Link.ln_Pri = 0; AddSemaphore(&PatchAnchor->sem); } /* * Now PatchAnchor points to our patch list in memory -- copy over * our patch code and initialised data accordingly. */ PatchList = PatchAnchor->patchdata; for (pi = PatchInit+1, p = PatchList+1; pi->offset; p++, pi++) { memcpy(p->code, PatchCode_Start, CODESIZE); p->library = pi->library; p->offset = pi->offset; p->newfunc = pi->newfunc; p->stackneeded = CurSettings.StackLimit; p->sysbase = SysBase; } CacheClearU(); /* Invalidate cache for code we just built */ Permit(); /* * Now initialise other variables associated with the patch code */ SnoopTask = SysBase->ThisTask; InputTask = FindTask("input.device"); InitRamLibPatch(); InitSemaphore(&PatternBufSem); InitSemaphore(&PatternCacheSem); InitSemaphore(&TaskCacheSem); InitSemaphore(&DosDeviceSem); InitSemaphore(&PauseSem); InitSemaphore(&PacketSem); UpdateDeviceList(SCANDEV_IMMEDIATE); NewList(&PatternList); for (i = 0; i < NUM_PCACHE_ENTRIES; i++) AddHead(&PatternList, (Node *)&PCacheEntries[i]); NewList(&PacketWaitList); NewList(&PacketFreeList); for (i = 0; i < NUM_PACKET_ENTRIES; i++) AddHead(&PacketFreeList, (Node *)&PacketEntries[i]); NewEventSig = AllocSignal(-1); if (NewEventSig == -1) return (FALSE); NewEventMask = 1 << NewEventSig; /* * Signal bit used to tell main task to rescan device list because * a device was added (or removed?) */ ScanDosListSig = AllocSignal(-1); if (ScanDosListSig == -1) return (FALSE); ScanDosListMask = 1 << ScanDosListSig; /* * Now create a background process to handle process matching. * We run this at a high priority to ensure quick response. * * We first of all initialise our pattern port, and set it * to NOT signal our task when a message arrives. This ensures * that if, for some reason, our new process doesn't run * until after someone tries to send it a message, nothing * bad will happen (e.g. signalling a task at 0x0000) * * The background process itself will replace this with * PA_SIGNAL to make things work normally again. */ NewList(&BackgroundPort.mp_MsgList); BackgroundPort.mp_Flags = PA_IGNORE; BackgroundProc = CreateNewProcTags(NP_Entry, BackgroundProcCode, NP_Name, BACKGROUND_NAME, NP_Priority, 5, TAG_DONE); return (BackgroundProc != NULL); } /* * UpdatePatches() * * Scans the list of patches, updating our behaviour as follows. * This means: * * - Any patch that's currently active and which has been flagged * as inactive should be disabled if possible (i.e. origfunc * set to NULL). * * - Any patch that's currently inactive and which has been flagged * as active should be enabled if possible (i.e. origfunc set * to point to the original function). * * If we are currently Disabled, then all patches are made inactive. * */ void UpdatePatches(void) { Patch *p; if (!PatchList) return; if (MonPackets || ShowAllPackets) UpdateDeviceList(SCANDEV_IMMEDIATE); /* Keep device list up to date */ Forbid(); /* Don't even think about letting anyone else run! */ /* * Update status of pair functions to reflect the master * function's status */ PatchList[GID_READTOOLTYPES2].enabled = PatchList[GID_READTOOLTYPES].enabled; PatchList[GID_LOADSEG2].enabled = PatchList[GID_LOADSEG].enabled; PatchList[GID_GETVAR2].enabled = PatchList[GID_GETVAR].enabled; PatchList[GID_SETVAR2].enabled = PatchList[GID_SETVAR].enabled; for (p = PatchList+1; p->offset; p++) { void *lib = LibList[p->library]; int func_enabled = (Disabled ? PATCH_DISABLED : p->enabled); p->stackneeded = CurSettings.StackLimit; /* Update this */ if (!lib) /* Skip over any libraries we couldn't open */ continue; if (p->origfunc == NULL && func_enabled != PATCH_DISABLED) { /* * Okay, we want to enable this function. */ p->origfunc = (FuncPtr)SetFunction(lib, p->offset, (FuncPtr)p); } else if (p->origfunc && func_enabled == PATCH_DISABLED) { /* * Okay, we want to disable this function. However, we * may not be able to do this cleanly, if it's been * since patched by someone else. Thus, if our attempt * to unpatch it doesn't return a pointer to our replacement * code, we have to leave the patch installed. However, it * will be re-tried automatically the next time someone * calls this function. */ FuncPtr curfunc = (FuncPtr)SetFunction(lib, p->offset, p->origfunc); if (curfunc != (FuncPtr)p) { /* * Uhoh -- someone else patched us! */ SetFunction(lib, p->offset, (FuncPtr)curfunc); } else { /* * We unpatched successfully, so zero pointer */ p->origfunc = NULL; } } } /* * We disable stack monitoring for the PutMsg() patch because * we need to catch the return packet from small-stack processes * like RAM: etc. Instead, that patch does its own stack checking. */ PatchList[GID_SENDREXX].stackneeded = 0; CacheClearU(); /* Just to be safe */ Permit(); /* * Finally, check if we've disabled packet monitoring and * there are some outstanding packets that have not yet had * return values assigned. Free them all as appropriate */ if (!MonPackets && !ShowAllPackets) { for (;;) { WaitPacket *wp; Event *ev; LONG seqnum; ObtainSemaphore(&PacketSem); if (IsListEmpty(&PacketWaitList)) { ReleaseSemaphore(&PacketSem); break; } wp = (WaitPacket *)RemHead(&PacketWaitList); AddHead(&PacketFreeList, (Node *)wp); ev = wp->event; seqnum = wp->eventnum; ReleaseSemaphore(&PacketSem); ObtainSemaphore(&BufSem); if (seqnum >= RealFirstSeq) { ev->status = ES_READY; Signal(SnoopTask, NewEventMask); } ReleaseSemaphore(&BufSem); } } } /* * CleanupPatches() * * Frees any resources allocated when initialising this module. Also * disable any patches that are still active. */ void CleanupPatches(void) { static int unpatchtries = 0; if (PatchList) { struct PatchInitTable *pi; Patch *p; for (p = PatchList+1; p->offset; p++) p->enabled = PATCH_DISABLED; UpdatePatches(); /* * Now our patches have been disabled, and it's time to * ensure nobody got left inside our code. We do this by * checking that all the usecounts are set to zero, and if * they're not, giving the user the option of continuing * to try, or exiting. * * Before we do this, however, we record all the functions * which still have a usecount > 0 -- this way, if someone * else runs SnoopDos while we are checking and installs * a new set of patches in the patchtable, and enables * some additional functions, we don't have to check that * ALL usage counts are zero, only those which were non-zero * at the start of the loop. * * (That is, we ignore any usage counts which are results of * functions being re-enabled by the new SnoopDos -- typically, * there will only be one or two functions at most to be * checked.) * * For convenience, we re-use the PatchInit list to hold the * exit "usage" states for each function -- we won't be needing * it any more. */ for (p = (PatchList+1), pi = (PatchInit+1); p->offset; p++, pi++) pi->offset = p->usecount; for (;;) { for (p = (PatchList+1), pi = (PatchInit+1); p->offset; p++, pi++) { if (pi->offset > 0) { if (p->usecount > 0) break; pi->offset = 0; /* No longer active, so mark as done */ } } if (p->offset) { /* * Couldn't unpatch, so go around the loop and try * again. After about 8-10 seconds, we warn the user * that we're still trying. */ unpatchtries++; if (unpatchtries == 5) { char errorbuf[400]; mysprintf(errorbuf, MSG(MSG_ERROR_UNPATCH), GetFuncName(p - PatchList)); ShowError(errorbuf); } Delay(2*50); /* Wait 2 seconds before next try */ } else break; } } if (BackgroundProc) { /* * Send a terminate message to the pattern process and wait * for it to respond */ PatternMsg pmsg; pmsg.type = QUIT_MSG; pmsg.sigmask = SIGF_SINGLE; pmsg.task = SysBase->ThisTask; pmsg.msg.mn_Node.ln_Name = NULL; SetSignal(0, SIGF_SINGLE); /* Ensure signal is clear first */ PutMsg(&BackgroundPort, &pmsg); /* Send quit message to process */ Wait(SIGF_SINGLE); /* Wait for acknowledgement */ } if (NewEventSig != -1) FreeSignal(NewEventSig), NewEventSig = -1; if (ScanDosListSig != -1) FreeSignal(ScanDosListSig), ScanDosListSig = -1; } /* * InitRamLibPatch() * * Attempts to patch the ramlib process so that it no longer uses * a message port with the signal bit set to SIGB_SINGLE (which * is asking for trouble if you call any functions that use * semaphores, since a SIGB_SINGLE can be generated by a message * arriving at the message port while waiting for the semaphore, * causing all hell to break loose -- the semaphore appears to be * obtained while in fact it's still in use by someone else). */ void InitRamLibPatch(void) { unsigned char *ramdata; int i; RealRamLibTask = FindTask("ramlib"); if (NoPatchRamLib || !RealRamLibTask) { /* * Not patching ramlib, so instead, install our bypass patch * that stops us from monitoring any ramlib tasks so that * we won't get crashed. */ RamLibTask = RealRamLibTask; } else { /* * Patch ramlib itself so that it doesn't use SIGF_SINGLE * any more. */ ramdata = (void *)SysBase->ex_RamLibPrivate; if (!ramdata) { /* * You never know ... this field may no longer exist. If * it doesn't, then assume the bug no longer exists either * and exit. */ return; } for (i = 0x20; i < 0x50; i += 2) { struct MsgPort *ramport = (struct MsgPort *)(ramdata + i); /* * Depending on the Kickstart version (V37 through V40) * the ramlib message port lies somewhere in the range * 0x30 to 0x50 of the ramlib's private area. We do * some integrity checks on the port to make sure it's * not just a lucky hit. If some future SetPatch fixes * this, then we will never find a match, which suits * us fine. */ if (ramport->mp_Flags == 0 && ramport->mp_SigBit == SIGB_SINGLE && ramport->mp_SigTask == RealRamLibTask) { /* * Now patch ramlib's message port so it won't use * SIGB_SINGLE any more. This is a little tricky, since * ramlib process will be in the middle of WaitPort() * and WaitPort() won't return until a message arrives at * the port. Essentially, we can only change the port's * message bit while we are sure that the ramlib process * is NOT in a WaitPort(). We do this by bumping our * process priority up to 127 temporarily, making the * call, and then restoring it. * * Since we're at a higher priority than ramlib, then * as soon as ramlib responds to the message, it will * be switched out (in the Ready state) before it has a * chance to call WaitPort() and go back to sleep again. */ ULONG oldpri; Forbid(); oldpri = SetTaskPri(SysBase->ThisTask, 127); OpenLibrary("ramlib-patch.library", 0); /* Wake-up ramlib */ ramport->mp_SigBit = SIGBREAKB_CTRL_E; SetTaskPri(SysBase->ThisTask, oldpri); Permit(); break; } } } } /* * UpdateDeviceList(method) * * Updates the DOS device list. This is used to keep an internal list * of all the task IDs of DOS handlers so we can quickly determine * if a given ID belongs to a handler or not. * * This should be called at least once, and ideally every now and * again, so that new devices added to the DOS list can be detected * and old ones ignored. * * The method can be SCANDEV_IMMEDIATE, to immediately scan the device * list, or SCANDEV_DELAY to wait for a second before updating. The * delay is because the patch that signals us to rescan (AddDosEntry) * happens before the device is actually fully initialised, so we need * to give it a little extra time to get going. * * It's not critical if we don't give it enough time, it just means * that the new device won't appear in the list after all -- a bit * annoying for people using the packet debugger to help debug a * device they keep mounting interactively. */ void UpdateDeviceList(int method) { struct DosList *dlist; int i = 0; if (method == SCANDEV_DELAY) Delay(50); ObtainSemaphore(&DosDeviceSem); dlist = LockDosList(LDF_DEVICES | LDF_READ); while ((dlist = NextDosEntry(dlist, LDF_DEVICES)) && i < (MAX_DOS_DEVICES-1)) { if (dlist->dol_Task) DeviceTaskList[i++] = dlist->dol_Task->mp_SigTask; } UnLockDosList(LDF_DEVICES | LDF_READ); DeviceTaskList[i] = 0; #if 0 Printf("New device list:\n"); for (i = 0; DeviceTaskList[i]; i++) Printf(" %2ld = %s\n", i, DeviceTaskList[i]->tc_Node.ln_Name); #endif ReleaseSemaphore(&DosDeviceSem); } /* * SetPattern(string, ignorewb) * * Sets the current pattern to the given string (possibly containing * wildcards). If ignorewb is true, then the pattern is modified to * also exclude any processes with the names Workbench, Shell Process * or Background CLI. */ void SetPattern(char *pattern, int ignorewb) { PatternCache *pc; char *p; if (*pattern == '\0' && !ignorewb) { PatternEnabled = 0; return; } ObtainSemaphore(&PatternCacheSem); ObtainSemaphore(&PatternBufSem); PatternEnabled = 0; /* * Now, update our pattern string. If the pattern string is empty, * we disable patterns. If the pattern string contains no wildcards, * we enable patterns but disable wildcard patching (this is much * faster, since it can be done in the patch's task, rather than * having to call back to the main routine). * * We allocate both pattern semaphores: the Cache semaphore to * ensure that no pattern code can execute when PatternEnabled * is zero, and the buffer semaphore to ensure that the pattern * process doesn't try to do pattern matching while we are * building the new pattern string. * * We need to be careful to get the cache semaphore BEFORE we get * the pattern buffer semaphore, otherwise we could deadlock if * a patch decided to check a pattern at just the wrong time. */ if (ignorewb) { if (*pattern == '\0') strcpy(PatternString, PAT_EASY_EXCLUDE); else mysprintf(PatternString, PAT_COMPLEX_EXCLUDE, pattern); } else strcpy(PatternString, pattern); /* * Now, due to a bug in AmigaDOS's ParsePatternNoCase, we * have to convert the string to upper case first. (The bug * is that character classes like [a-z] will never match anything, * only [A-Z] will work. Since it's case insensitive for * everything else, we simply convert everything to upper case.) */ for (p = PatternString; *p; p++) { if (*p >= 'a' && *p <= 'z') *p &= 0x5F; } PatternWildcard = ParsePatternNoCase(PatternString, PatternBuf, PAT_BUF_LEN); if (PatternWildcard != -1) /* Only enable patterns if successful */ PatternEnabled = 1; /* * Regardless of what happened, invalidate the pattern cache * to force a new match for each task name. */ FORLIST(&PatternList, pc) pc->task = NULL; ReleaseSemaphore(&PatternBufSem); ReleaseSemaphore(&PatternCacheSem); } /* * FlushWaitingPackets(void) * * Flushes any packets currently in the waiting packets queue * (marking them as missed). Usually called after packet monitoring * has been disabled, in case there were any half-completed packets * in the queue. */ void FlushWaitingPackets(void) { WaitPacket *wp; int flushed = 0; ObtainSemaphore(&PacketSem); wp = HeadNode(&PacketWaitList); while (NextNode(wp)) { ULONG seqnum = wp->eventnum; Event *ev = wp->event; Remove((Node *)wp); AddHead(&PacketFreeList, (Node *)wp); ReleaseSemaphore(&PacketSem); /* Never lock two sem's at once! */ ObtainSemaphore(&BufSem); if (seqnum >= RealFirstSeq) { ev->result = MSG(MSG_RES_MISSED); ev->status = ES_READY; flushed = 1; } ReleaseSemaphore(&BufSem); ObtainSemaphore(&PacketSem); wp = HeadNode(&PacketWaitList); } ReleaseSemaphore(&PacketSem); if (flushed) Signal(SnoopTask, NewEventMask); } /* * LoadFuncSettings(FuncSets) * * Downloads the values in the passed-in FuncSettings array to the * various local structures that require them. */ void LoadFuncSettings(FuncSettings *func) { Patch *p; int i; if (!PatchList) return; Forbid(); for (p = PatchList+1, i = 1; p->offset; p++, i++) p->enabled = ((func->Opts[i] && !Disabled) ? PATCH_ENABLED : PATCH_DISABLED); /* * A bit of a hack -- the PutMsg() function is needed to monitor * three individual patches: SendRexx, Monitor Packets, and * Packet Debugger. We do the check for the last two here. */ if ((func->Opts[GID_MONPACKETS] || func->Opts[GID_MONALLPACKETS]) && !Disabled) { PatchList[GID_SENDREXX].enabled = PATCH_ENABLED; PatchList[GID_ADDDOSENTRY].enabled = PATCH_ENABLED; } else { PatchList[GID_ADDDOSENTRY].enabled = PATCH_DISABLED; FlushWaitingPackets(); } UpdatePatches(); Permit(); SetPattern(func->Pattern, func->Opts[GID_IGNOREWB]); } /* * BackgroundProcCode() * * This function is called as a separate process. It is used * to service requests for pattern matching by patched code. * We do this in a separate process to ensure we have enough * stack to perform the pattern matching (pattern matching can * be very stack-intensive.) * * This process is also used to do pathname expansion when we * are monitoring packets -- this is needed for both the * ShowAllPaths option and monitoring of the ACTION_MAKE_LINK packet. * * We keep going until we get a QUIT_MSG from the main task, * at which point we exit. */ void __saveds BackgroundProcCode(void) { #if DEBUG_PATTERN BPTR dbfile = Open("CON:100/100/400/100/Pattern/WAIT/AUTO/CLOSE", MODE_OLDFILE); #define DBP(s) FPrintf s #else #define DBP(s) #endif Task *quittask; ULONG quitsig; int done = 0; /* * Our first task is to re-route the message port allocated * on our behalf by the main process so that it points to us. * We also re-enable signals on incoming messages to ensure * we are correctly interrupted. */ BackgroundPort.mp_SigTask = SysBase->ThisTask; BackgroundPort.mp_SigBit = AllocSignal(-1); /* Better not fail! */ BackgroundPort.mp_Flags = PA_SIGNAL; /* Allow signalling now */ DBP((dbfile, "Started pattern debug code\n")); while (!done) { /* Now handle all pattern messages queue on the pattern port. * Unlike normal messages, we don't simply reply to these * with ReplyMsg(); instead, we signal the indicated task * using the signal bit specified in the message. * * This is because the sender may not have an available * signal bit to use to receive the reply so it uses a * system bit which is guaranteed to be unused. * * Note that we grab the pattern semapore while we process * the messages; this stops the mainline code from updating * the middle of the buffer while we are doing a comparison. */ PatternMsg *pmsg; NameFromLockMsg *lmsg; WaitPort(&BackgroundPort); ObtainSemaphore(&PatternBufSem); while ((pmsg = (void *)GetMsg(&BackgroundPort)) != NULL) { DBP((dbfile, "Got a message, type = %ld!\n", pmsg->type)); switch (pmsg->type) { case QUIT_MSG: /* * We've been told to quit by the main task, so * set some variables appropriately. We flush * the remaining messages in the queue first * just to be safe (we wouldn't want to leave * anyone hanging, now, would we?) */ PatternEnabled = 0; /* Safety first */ done = 1; quittask = pmsg->task; quitsig = pmsg->sigmask; break; case PATTERN_MSG: DBP((dbfile, "Got a pmatch from %s\n", pmsg->name)); if (PatternEnabled) pmsg->match = MatchPatternNoCase(PatternBuf, pmsg->name); else pmsg->match = 1; /* If disabled, everything matches */ Signal(pmsg->task, pmsg->sigmask); break; case PATHEXPAND_MSG: lmsg = (NameFromLockMsg *)pmsg; lmsg->newbuf = MyNameFromLock(lmsg->lock, lmsg->filename, lmsg->buf, lmsg->maxlen); Signal(lmsg->task, lmsg->sigmask); break; } } ReleaseSemaphore(&PatternBufSem); } #if DEBUG_PATTERN DBP((dbfile, "Quitting...\n")); Close(dbfile); #endif /* * Now cleanup and exit from this task. We need to Forbid() * before we signal the main task that we're ready to quit, * since otherwise the main task might go ahead and unload * us before we have a chance to execute the last few lines * in the function (e.g. if the main task is running at a * higher priority than we are). */ Forbid(); FreeSignal(BackgroundPort.mp_SigBit); Signal(quittask, quitsig); } /* * CheckPattern(name, namelen) * * Checks if our task's name has been masked out by the * user, using our current AmigaDOS pattern string. * * We can't call the DOS pattern match function directly, due to * stack limitations (the caller may not have a very big stack). * So, we maintain a cache of recently used tasks -- the first * task on the list is always the most recently monitored task. * * If a task doesn't appear on the list, or if it appears on the * list but its name has changed, then we send a message to the * main SnoopDos task asking it to check the pattern. We also * add the result of this check to our cache for future calls. * * Sending a message to another task from within a patch is tricky; * more specifically, receiving the acknowledgement back from the * main task is tricky. This is because we don't know what signal * bits the caller might be using, and so we can't just grab one. * We could use AllocSignal(), but then what if the caller has no * free signal bits? * * Our solution (not necessarily the best) is to use the reserved * exec signal bit SIGB_SINGLE. This is used for semaphore operations, * among others, and is only ever used while waiting for a single * event to happen. Thus, when our code is reached, we know this * bit is not in use and thus we can use it ourselves. * * Returns true if no pattern is set, or if the pattern matches * the current task name. Returns false otherwise. * */ int CheckPattern(char *taskname, int namelen) { PatternCache *pc; Task *thistask = SysBase->ThisTask; PatternMsg pmsg; if (!PatternEnabled) return (TRUE); if (!PatternWildcard) return (stricmp(taskname, PatternString) == 0); namelen = MIN(namelen, PC_NAMELEN-1); /* * Got a pattern, so search our cache list */ ObtainSemaphore(&PatternCacheSem); FORLIST(&PatternList, pc) if (pc->task == thistask) break; /* * Okay, now pc is either the matching task, or else a pointer * to the end of our list. */ if (NextNode(pc) && pc->task == thistask) { /* * We broke out of the loop since we matched the task. * Now check if the name matches too. */ if (strncmp(pc->name, taskname, namelen) == 0) { /* * We're the same. Move this node to the start of * the list, and return the appropriate state. */ Remove((Node *)pc); AddHead(&PatternList, (Node *)pc); ReleaseSemaphore(&PatternCacheSem); return (pc->match); } /* * The name is different but the Task ID is the same -- * most likely, someone ran a new CLI command. Thus, use * this cache entry for our new task name and fall through. */ } else { /* * We didn't match a task so grab one from the end of the * list and use that instead. */ pc = TailNode(&PatternList); pc->task = thistask; } /* * Move the node to the start of the list to stop it being flushed * any time soon, and update the name to match our own name. */ Remove((Node *)pc); AddHead(&PatternList, (Node *)pc); strncpy(pc->name, taskname, namelen); pc->name[namelen] = 0; ReleaseSemaphore(&PatternCacheSem); /* * Now send a message to the main task asking it to perform a name * match for us. We need to be careful to make sure our pmsg won't * be confused with a DOS packet by our PutMsg() patch -- we do * this by NULLing out the name field. */ pmsg.type = PATTERN_MSG; pmsg.name = taskname; pmsg.sigmask = SIGF_SINGLE; pmsg.task = thistask; pmsg.msg.mn_Node.ln_Name = NULL; SetSignal(0, SIGF_SINGLE); /* Ensure signal is clear first */ PutMsg(&BackgroundPort, &pmsg); /* Send request to background task */ Wait(SIGF_SINGLE); /* Wait for acknowledgement */ pc->match = pmsg.match; /* And save result in cache */ return (pmsg.match); } /* * GetVolName(lock, buf, maxlen) * * Copies the volume name associated with lock into the buffer, * with terminating ':'. If lock is NULL, the volume address is * taken directly from volume. * * If UseDevNames is true, the device list is searched looking * for the device node associated with the volume node (i.e. two * nodes sharing the same task address). * * WARNING: This function must not be called from within a DOS * device handler due to potential deadlock errors! * */ void GetVolName(BPTR lock, char *buf, int maxlen) { struct DeviceList *vol; struct DosList *dl; int gotdev = 0; if (lock == NULL) { NameFromLock(lock, buf, maxlen); return; } vol = BTOC(((struct FileLock *)BTOC(lock))->fl_Volume); if (UseDevNames == 0 || vol->dl_Task == NULL) { /* * Use volume name, especially if the volume isn't currently * mounted! */ UBYTE *volname = BTOC(vol->dl_Name); int len = MIN(maxlen-2, *volname); memcpy(buf, volname+1, len); buf[len++] = ':'; buf[len] = '\0'; return; } /* * The user wants the device name. The only way to obtain this * is to search the device list looking for the device node with * the same task address as this volume. */ dl = LockDosList(LDF_DEVICES | LDF_READ); while (dl = NextDosEntry(dl, LDF_DEVICES)) { if (dl->dol_Task == vol->dl_Task) { /* * Found our task, so now copy device name */ UBYTE *devname = BTOC(dl->dol_Name); int len = MIN(maxlen-2, *devname); memcpy(buf, devname+1, len); buf[len++] = ':'; buf[len] = '\0'; gotdev = 1; break; } } UnLockDosList(LDF_DEVICES | LDF_READ); if (!gotdev) strcpy(buf, "???:"); } /* * MyNameFromLock(lock, filename, buf, maxlen) * * This is a custom version of the DOS function NameFromLock() * which expands a disk lock into a full path. * * Our version adds the following features. The device name will be * given as either the physical device (like DH0:) if UseDevNames * is true, or the volume name (like System3.0:) if UseDevNames is * false. * * If filename is non-NULL, then it will be appended to the lock * path. If filename contains path info, then this will be taken * into account when generating the lock, so that an absolute path * in filename will not have any effect, and a relative filename * (like //directory) will cause the original lock to be ParentDir()'d * twice before being resolved. * * This function can't fail. In the event of an error (string too * long, or something like that), the buffer will be filled accordingly. * It is assumed that the buffer will always be big enough to hold short * error messages or a volume name. * * Returns a pointer to the path (which will not necessarily be at * the start of the buffer, but is guaranteed null-terminated.) * Note that it's even possible that the pointer returned will be * to the original filename if no path expansion was required. * * New: We now preserve the IoErr() that was present on entry, since * it may have been set by the calling function if OnlyShowFails is * true. Otherwise, IoErr() will be screwed up by the operations we * do here (e.g. SAS/C deleting a non-existent file when OnlyShowFails * is true). * * WARNING: This function must not be called from within a DOS * device handler due to potential deadlock errors! */ char *MyNameFromLock(BPTR lock, char *filename, char *buf, int maxlen) { Process *myproc = (Process *)SysBase->ThisTask; int pos = maxlen - 1; D_S(fib, struct FileInfoBlock); LONG savedioerr = IoErr(); BPTR curlock; BPTR newlock; void *savewinptr; char *p; int len; int skipfirstslash = 0; /* If true, skip first slash when building name */ int err = 0; /* * Check for special magic filehandle */ if (filename && *filename) { if (strcmp(filename, "*") == 0) return (filename); /* * First determine if we have any work to do. */ if (*filename == ':') { /* * Got a reference relative to the root directory. Simply * grab the volume (or device) name from the lock and go * with that. */ int len; GetVolName(lock, buf, maxlen); len = strlen(buf); strncat(buf+len, filename+1, maxlen-len); buf[maxlen-1] = '\0'; SetIoErr(savedioerr); return (buf); } for (p = filename; *p; p++) { if (*p == ':') /* If absolute path name, leave it alone */ return (filename); } } else { /* * Filename is null, so indicate we want to skip the first * slash when building the directory path */ skipfirstslash = 1; } savewinptr = myproc->pr_WindowPtr; myproc->pr_WindowPtr = (APTR)-1; /* Disable error requesters */ newlock = DupLock(lock); if (lock && !newlock) { GetVolName(lock, buf, 20); if (filename) { strcat(buf, ".../"); strcat(buf, filename); } myproc->pr_WindowPtr = savewinptr; /* Re-enable error requesters */ SetIoErr(savedioerr); return (buf); } buf[pos] = '\0'; curlock = newlock; if (filename) { while (newlock && *filename == '/') { /* * Handle leading /'s by moving back a directory level * but nothing else */ newlock = ParentDir(curlock); if (newlock) { UnLock(curlock); curlock = newlock; filename++; } } len = strlen(filename); if (len > (pos-2)) { memcpy(buf+2, filename+len-pos, pos-2); buf[0] = buf[1] = '.'; pos = 0; UnLock(curlock); } else { pos -= len; memcpy(buf+pos, filename, len); } } /* * At this point, we have buf containing the filename (minus any * leading /'s), starting at the index given by pos. If filename * was NULL or empty, then pos indexes to a \0 terminator. * * Next, we want to pre-pend directory names to the front of * the filename (assuming there _is_ a filename) until we get * to the device root. */ newlock = curlock; while (newlock) { if (!Examine(curlock, fib)) { err++; break; } len = strlen(fib->fib_FileName); if (len > (pos-3)) { /* * Not enough room: prefix dots at start to indicate * an overrun. We use pos-3 since we need one char * for a possible slash and two more to accomodate a * leading ".." */ memcpy(buf+2, fib->fib_FileName+len-pos+3, pos-2); buf[0] = buf[1] = '.'; buf[pos-1] = '/'; pos = 0; break; } newlock = ParentDir(curlock); if (newlock) { UnLock(curlock); curlock = newlock; pos -= len + 1; memcpy(buf + pos, fib->fib_FileName, len); if (skipfirstslash) { skipfirstslash = 0; buf[pos+len] = '\0'; } else buf[pos+len] = '/'; } } /* * Now we've built the path components; add the volume node * to the beginning if possible. */ if (err) { /* * If an error occurred, the volume is probably not mounted, * so we include a ".../" component in the path to show * we couldn't get all the info */ pos -= 4; memcpy(buf + pos, ".../", 4); } if (pos > 0) { char volname[20]; int len; char *p; GetVolName(curlock, volname, 20); len = strlen(volname); if (len > pos) { p = volname + len - pos; len = pos; } else { p = volname; } pos -= len; memcpy(buf + pos, p, len); } if (curlock) UnLock(curlock); myproc->pr_WindowPtr = savewinptr; /* Re-enable error requesters */ SetIoErr(savedioerr); return (buf+pos); } /* * AsyncNameFromLock(volname, lock, filename, dest, maxlen) * * This is identical to the MyNameFromLock() function except that * it gets the background SnoopDos process to do the path expansion * instead of doinng it from within the caller's process. This is * necessary if we need to expand paths and we are not sure if the * caller's process mesage port is free. * * Volname is the volume on which the lock resides. If the lock itself * is NULL, then the root of the volume will be assumed and this * string will be copied into the buffer as the volume name. * * WARNING: This function must not be called from within a DOS * device handler due to potential deadlock errors! */ char *AsyncNameFromLock(char *volname, BPTR lock, char *filename, char *buf, int maxlen) { NameFromLockMsg lmsg; if (lock == NULL) { /* * Do a quick scan on the filename itself to see * if it contains a colon prefixed by some text; * if it does, then we already have a full path. * If it's just a colon, prefix it with the * volume name. If it has no colon, prefix it * with the volume name and a colon. */ char *p; strcpy(buf, volname); if (filename) { if (*filename == ':') { strcat(buf, filename); } else { for (p = filename; *p && *p != ':'; p++) ; if (*p) strcpy(buf, filename); else mysprintf(buf, "%s:%s", volname, filename); } } return (buf); } lmsg.type = PATHEXPAND_MSG; lmsg.lock = lock; lmsg.filename = filename; lmsg.buf = buf; lmsg.maxlen = maxlen; lmsg.sigmask = SIGF_SINGLE; lmsg.task = SysBase->ThisTask; lmsg.msg.mn_Node.ln_Name = NULL; SetSignal(0, SIGF_SINGLE); /* Ensure signal is clear first */ PutMsg(&BackgroundPort, &lmsg); /* Send request to background task */ Wait(SIGF_SINGLE); /* Wait for acknowledgement */ return (lmsg.newbuf); } #if STACK_CHECK /* * GetFreeStack() * * Returns the amount of free space available on the calling task's * stack (thanks to Ralph Babel for this). */ ULONG GetFreeStack(void) { Process *pr = (Process *)SysBase->ThisTask; char *upper; char *lower; ULONG total; ULONG avail; if (pr->pr_Task.tc_Node.ln_Type == NT_PROCESS && pr->pr_CLI != NULL) { upper = (char *)pr->pr_ReturnAddr + sizeof(ULONG); total = *(ULONG *)pr->pr_ReturnAddr; lower = upper - total; } else { upper = (char *)pr->pr_Task.tc_SPUpper; lower = (char *)pr->pr_Task.tc_SPLower; total = upper - lower; } avail = (char *)getreg(REG_A7) - lower; return (avail); } #endif /* * CreateEvent(calladdr, seqnum, action, filename, * options, expandname [, lock] ) * * Allocates a new event for the current task and initialises * almost all the fields in it accordingly. * * seqnum is initialised to the sequence number of the new event. This * is used to allow later detection of events which have scrolled off * the top of the buffer. * * filename and options are text strings, and actionid is a message ID. * The storage for these will be allocated in the event buffer. * * If expandname is EXPAND_NAME, then 'filename' is assumed to represent * a real disk filename, and it will be expanded to a full path if that * option is set. If expandname is NO_EXPAND, then the name is passed * unchanged. * * If expandname is neither EXPAND_NAME or NO_EXPAND, then it is * interpreted as a volume name, and one additional parameter * following it will be expected -- this additional parameter will * be a lock on that volume. The filename will then be expanded * to a full pathname on that volume. If the lock is null, then it * is assumed to represent the root of the volume. The filename * expansion is done by the background process, making it safe to * call from within the PutMsg() patch which monitors raw packets. * * If you pass in a NULL value for any of the three pointers, it will * be made to point to a blank string. * * If for some reason the event could not be created, returns NULL, * else returns a pointer to the initialised event. * * Possible reasons for failure include: * * - Calling task is main SnoopDos task (!) * - Calling task is not included in the match pattern * - Calling task was called by a ROM function * - Disk error occurred expanding filename * - Out of memory allocating new event */ struct Event *CreateEvent(ULONG calladdr, LONG *seqnum, ULONG actionid, char *filename, char *options, int expandname, ...) { Task *thistask = SysBase->ThisTask; struct CommandLineInterface *cli; Event *ev; char *procname; char *strptr; char *action; char namebuf[MAX_SHORT_LEN+1]; char pathbuf[MAX_STR_LEN+1]; int plen = 0; int flen = 0; int olen = 0; int alen = 0; int slen = 0; int clinum; /* * We ignore all calls from the main SnoopDOS process and * background process, and also any nested calls made while * we have the semaphore locked (which might otherwise lead * to nasty trashing of the list!) * * Also, any calls from input.device are ignored since these * typically call us with layers locked, and the slightest * delay could be fatal. */ if (thistask == SnoopTask || thistask == (Task *)BackgroundProc || thistask == BufSem.ss_Owner || thistask == RamLibTask || thistask == InputTask) return (NULL); /* * We also ignore any calls made from ROM addresses (if this * option is enabled), since this cuts down the amount of * superfluos output considerably. * * We specifically DO allow calls from within ramlib, since * it's very easy to disable ramlib directly if need be (by * putting ~ramlib in the match name) and ramlib does * many useful operations that are convenient to monitor. */ if (thistask != RealRamLibTask && !MonROMCalls && calladdr >= RomStart && calladdr <= RomEnd) return (NULL); /* * Work out the correct name for our task. This will either be * the task name, the process name, or the current CLI command * (if the process is a CLI, _and_ is currently running a command.) * * In addition, for CLIs, we set clinum to the CLI number. For * tasks and non-CLI processes, clinum becomes zero. */ procname = thistask->tc_Node.ln_Name; plen = strlen(procname); cli = BTOC(((Process *)thistask)->pr_CLI); clinum = 0; if (thistask->tc_Node.ln_Type == NT_PROCESS && cli) { /* * If we have a CLI command, we have to convert the name * from a BSTR into a C string */ clinum = ((Process *)thistask)->pr_TaskNum; if (cli->cli_Module) { UBYTE *cliname = BTOC(cli->cli_CommandName); plen = *cliname; if (plen) { if (plen > MAX_SHORT_LEN) plen = MAX_SHORT_LEN; memcpy(namebuf, cliname+1, plen); namebuf[plen] = 0; } else { strcpy(namebuf, ""); plen = strlen(namebuf); } procname = namebuf; } } #if 0 if (stricmp(procname, "ramlib") == 0) { KPrintF("Ramlib: Stacksize is %ld\n", GetFreeStack()); } #endif if (!CheckPattern(procname, plen)) /* Perform pattern check */ return (FALSE); /* * Now deal with our three strings */ if (ShowPaths && filename != NULL) { if (expandname == EXPAND_NAME) { /* * Expand filename to full path */ filename = MyNameFromLock(((Process *)thistask)->pr_CurrentDir, filename, pathbuf, MAX_STR_LEN); } else if (expandname != NO_EXPAND) { /* * Expand filename to full path using background process * and the lock specified in expandname. */ BPTR lock = *(ULONG *)(&expandname+1); filename = AsyncNameFromLock((char *)expandname, lock, filename, pathbuf, MAX_STR_LEN); } } plen++; if (ShowCLINum && clinum) plen += 8; /* Leave room for CLI display */ action = MSG(actionid); alen = strlen(action) + 1; if (filename) flen = strlen(filename) + 1; if (options) olen = strlen(options) + 1; if (SegTrackerActive) slen = MAX_SEGTRACKER_LEN; /* * If the calling task has locked the layers of the SnoopDos * screen, then we ignore all calls by that task. This prevents * us deadlocking when we wait for the semaphore (while meanwhile, * the SnoopDos mainline code could own the semaphore, and be * waiting for the layers to be unlocked by our caller so it can * render some output). * * We Forbid() to make sure the window doesn't get closed from * under our feet while we're checking it. We have to try and * allocate the event while still Forbid'd since otherwise, there * is a small window where the main window could open, lock the * layers, and lock the semaphore, in between our check. It's * unlikely, but not _that_ unlikely (especially since the main * window always does an immediate redraw when it opens) */ Forbid(); if (MainWindow && (MainWindow->RPort->Layer->Lock.ss_Owner == thistask || MainWindow->WScreen->LayerInfo.Lock.ss_Owner == thistask)) { Permit(); return (NULL); } ev = GetNewEvent(plen + alen + flen + olen + slen); Permit(); if (!ev) return (NULL); ev->action = ev->filename = ev->options = ev->result = ""; strptr = (char *)(ev+1); if (ShowCLINum && clinum) mysprintf(strptr, "[%ld] %s", clinum, procname); else strcpy(strptr, procname); ev->procname = strptr; strptr += plen; // KPrintF("New event: %ld, %s, %s\n", ev->seqnum, procname, action); /* DB */ if (alen) { strcpy(strptr, action); ev->action = strptr; strptr += alen; } if (flen) { strcpy(strptr, filename); ev->filename = strptr; strptr += flen; } if (olen) { strcpy(strptr, options); ev->options = strptr; strptr += olen; } if (slen) { ev->segname = strptr; strptr += slen; } else { ev->segname = NULL; /* Say no segtracker storage allocated */ } // DateStamp(&ev->datestamp); ev->calladdr = calladdr; ev->processid = (ULONG)thistask; // ev->processid = GetFreeStack(); if (seqnum) *seqnum = ev->seqnum; return (ev); } /* * CheckTaskRecursion() * * Checks if the current task is already listed as being on the * recursion list. If so, returns FALSE, indicating that the * caller should immediately pass control back to the original * function and not try and do anything else. * * Otherwise, records the task's address in a recursion list * and returns a handle. This handle should be passed back to * EndTaskRecursion() when the function is complete, which will * cause it to be removed from the list. * * OpenLibrary() needs to surround its call to CreateEvent() with * CheckTaskRecursion()/EndTaskRecursion. Also, GetMsg()/PutMsg() need * this to check if any of the DOS packets it monitors have been * generated by AmigaDOS functions we monitor. */ ULONG CheckTaskRecursion(void) { Task *taskid = SysBase->ThisTask; ULONG taskhandle; int i; for (i = 1; i < NUM_CACHED_TASKS; i++) if (CachedTask[i] == taskid) return (0); ObtainSemaphore(&TaskCacheSem); taskhandle = TaskCacheIndex++; if (TaskCacheIndex >= NUM_CACHED_TASKS) TaskCacheIndex = 1; CachedTask[taskhandle] = taskid; ReleaseSemaphore(&TaskCacheSem); return (taskhandle); } /* * EndTaskRecursion(taskhandle) * * Called to remove a task from the recursion list. * See CheckTaskRecursion() above. */ void EndTaskRecursion(ULONG taskhandle) { CachedTask[taskhandle] = 0; } #if 0 /* * TestPatch() * * Patches a single function to try it out */ void TestPatch(void) { extern List EventList; /* List of events */ static EventFormat evformat[50]; static char outstr[500]; char *formatstr = "%d %t %c %n %s %h %a %40f %o %r"; int formatlen; OnlyShowFails = 0; SegTrackerActive = 1; formatlen = ParseFormatString(formatstr, evformat, 50); if (!InitPatches()) { printf("Patch initialisation failed!\n"); return; } PatchList[GID_OPENFILE].enabled = 1; UpdatePatches(); printf("Patch now enabled!\n\n"); FormatEvent(evformat, NULL, outstr, 0, formatlen-1); printf("%s\n", outstr); printf("%s\n", UnderlineTitles(evformat, outstr, '-')); for (;;) { ULONG mask; mask = Wait(SIGBREAKF_CTRL_C | NewEventMask); if (mask & SIGBREAKF_CTRL_C) break; if (mask & NewEventMask) { Event *ev; SetSignal(0, NewEventMask); /* Clear signal for next time */ FORLIST(&EventList, ev) { int stat = ev->status; if (stat == ES_UPDATING || stat == ES_READY) { FormatEvent(evformat, ev, outstr, 0, formatlen-1); printf("%s\n", outstr); if (stat == ES_READY) ev->status = ES_ACCEPTED; } } } } PatchList[GID_OPENFILE].enabled = 0; UpdatePatches(); printf("Patch disabled\n"); CleanupPatches(); } #endif /* * HandlePaused(event, seqnum) * * Should be called when pausing is enabled -- it sets the * event Result code to "...." to indicate the event is about * to be executed, then waits for the pausing to be turned off. * If you pass in NULL as the event code, then this isn't done. * * seqnum is the actual event number remembered by the caller, * not (necessarily) the event number stored in the event. This * is so that the code can check if the event has rolled off the * top of the buffer or not before modifying it. * * There are some circumstances where it is dangerous for * us to pause because we are being called by a system task * that is needed by the main SnoopDos program. * * We work around this as follows. Firstly, if the caller happens * to own the semaphore associated with the layer info for the * window or screen containing the SnoopDos window, then we * don't try and pause it since otherwise we might deadlock * (the user wouldn't be able to unpause SnoopDos because that * requires clicking on a button.) * * Secondly, whenever SnoopDos itself does something that might * stop it processing the main messages (e.g. show a requester, * open a file, check for ASL, etc.) then pausing is temporarily * turned off since we have no way of knowing whether a third * party patch might be spinning off a background task to do it * and the background task might try and wait on this. Bummer, eh? */ void HandlePaused(Event *ev, LONG seqnum) { if (Paused) { /* * Need to forbid when checking layers since otherwise the * main window might close while we're reading its pointers. */ int gotlock = 0; char *oldresmsg; Forbid(); if (MainWindow) { struct Layer *layer; struct Layer_Info *li; Task *thistask = SysBase->ThisTask; layer = MainWindow->RPort->Layer; li = &MainWindow->WScreen->LayerInfo; gotlock = (li->Lock.ss_Owner == thistask) || (layer->Lock.ss_Owner == thistask); } Permit(); if (gotlock) return; /* * Okay, safe to proceed with pausing. We simply set the * result message to indicate we're paused, signal * the main task, wait for the pausing to finish, restore * the result message, and return. */ if (ev) { ObtainSemaphore(&BufSem); if (seqnum >= RealFirstSeq) { oldresmsg = ev->result; ev->result = MSG(MSG_RES_PAUSE); } ReleaseSemaphore(&BufSem); } Signal(SnoopTask, NewEventMask); /* Make sure main task updates */ ObtainSemaphore(&PauseSem); ReleaseSemaphore(&PauseSem); if (ev) { ObtainSemaphore(&BufSem); if (seqnum >= RealFirstSeq) ev->result = oldresmsg; ReleaseSemaphore(&BufSem); } } } #define CheckForPause(ev,seqnum) if (Paused) { HandlePaused(ev, seqnum); } /***************************************************************************** * * Now here are all the patches for the 25 or so functions that we * patch into. Most of the patches follow a similar template, and * share similar lower-level functions, but are different enough to * prevent us from using the same patch for several functions. * * We always signal both before and after a particular call, so that * in the event of a call causing a crash, it will still show up in * the SnoopDos window. * * The OnlyShowFails flag is used to say whether we want to monitor * all calls to a function, or only those which return a failure code. * If the latter, we need to call the function first before deciding * whether or not to create an event to record it -- in this case, the * result code remains cached for the remainder of the function. The * rest of the time, we create the event and signal the main task, * then call the function to get the result, and finally update the * event with the result and signal the main task again. * * Each patch that calls a function and then updates the buffer on its * return must ensure that the buffer entry is has a pointer to is still * valid before it updates it. It does this by remembering the sequence * number of the buffer entry, and then checking that the sequence * number of the first buffer entry is still <= that (i.e. the entry * is still somewhere in the buffer). This is vital to ensure that * innocent buffer memory isn't trashed by old events. * *****************************************************************************/ /* * New_FindPort(name) * * Searches the system port list looking for a port name */ ULONG ASM New_FindPort(reg_a1 char *name, reg_a6 void *libbase) { MarkCallAddr; FP_FindPort origfunc = (FP_FindPort)PatchList[GID_FINDPORT].origfunc; int onlyfails = OnlyShowFails; ULONG result; Event *ev; LONG seqnum; if (onlyfails) { result = origfunc(name, libbase); if (result) return (result); } ev = CreateEvent(CallAddr, &seqnum, MSG_ACT_FINDPORT, name, NULL, NO_EXPAND); if (!ev) { if (onlyfails) return (result); JumpOrigFunc(0); } /* * Allocated event safely, now tell SnoopDos task to wake up */ if (onlyfails) { CheckForPause(NULL, 0); } else { ev->status = ES_UPDATING; CheckForPause(ev, seqnum); Signal(SnoopTask, NewEventMask); result = origfunc(name, libbase); } ObtainSemaphore(&BufSem); if (seqnum >= RealFirstSeq) { if (result) ev->result = MSG(MSG_RES_OKAY); else ev->result = MSG(MSG_RES_FAIL); ev->status = ES_READY; Signal(SnoopTask, NewEventMask); } ReleaseSemaphore(&BufSem); return (result); } /* * New_FindResident(name) * * Searches the system resident list looking for a resident module */ ULONG ASM New_FindResident(reg_a1 char *name, reg_a6 void *libbase) { MarkCallAddr; FP_FindResident origfunc = (FP_FindResident) PatchList[GID_FINDRESIDENT].origfunc; int onlyfails = OnlyShowFails; ULONG result; Event *ev; LONG seqnum; if (SysBase->ThisTask == RealRamLibTask) JumpOrigFunc(0); if (onlyfails) { result = origfunc(name, libbase); if (result) return (result); } ev = CreateEvent(CallAddr, &seqnum, MSG_ACT_FINDRESIDENT, name, NULL, NO_EXPAND); if (!ev) { if (onlyfails) return (result); JumpOrigFunc(0); } /* * Allocated event safely, now tell SnoopDos task to wake up */ if (onlyfails) { CheckForPause(NULL, 0); } else { ev->status = ES_UPDATING; CheckForPause(ev, seqnum); Signal(SnoopTask, NewEventMask); result = origfunc(name, libbase); } ObtainSemaphore(&BufSem); if (seqnum >= RealFirstSeq) { if (result) ev->result = MSG(MSG_RES_OKAY); else ev->result = MSG(MSG_RES_FAIL); ev->status = ES_READY; Signal(SnoopTask, NewEventMask); } ReleaseSemaphore(&BufSem); return (result); } /* * New_FindSemaphore(name) * * Searches the system semaphore list looking for the named semaphore */ ULONG ASM New_FindSemaphore(reg_a1 char *name, reg_a6 void *libbase) { MarkCallAddr; FP_FindSemaphore origfunc = (FP_FindSemaphore) PatchList[GID_FINDSEMAPHORE].origfunc; int onlyfails = OnlyShowFails; ULONG result; Event *ev; LONG seqnum; if (onlyfails) { result = origfunc(name, libbase); if (result) return (result); } ev = CreateEvent(CallAddr, &seqnum, MSG_ACT_FINDSEM, name, NULL, NO_EXPAND); if (!ev) { if (onlyfails) return (result); JumpOrigFunc(0); } /* * Allocated event safely, now tell SnoopDos task to wake up */ if (onlyfails) { CheckForPause(NULL, 0); } else { ev->status = ES_UPDATING; CheckForPause(ev, seqnum); Signal(SnoopTask, NewEventMask); result = origfunc(name, libbase); } ObtainSemaphore(&BufSem); if (seqnum >= RealFirstSeq) { if (result) ev->result = MSG(MSG_RES_OKAY); else ev->result = MSG(MSG_RES_FAIL); ev->status = ES_READY; Signal(SnoopTask, NewEventMask); } ReleaseSemaphore(&BufSem); return (result); } /* * New_FindTask(name) * * Searches the system task list looking for the named task. * We ignore calls to FindTask(0) since this is just * looking for a pointer to the current task. */ ULONG ASM New_FindTask(reg_a1 char *name, reg_a6 void *libbase) { MarkCallAddr; FP_FindTask origfunc = (FP_FindTask)PatchList[GID_FINDTASK].origfunc; int onlyfails = OnlyShowFails; ULONG result; Event *ev; LONG seqnum; if (name == NULL) return (origfunc(name, libbase)); if (onlyfails) { result = origfunc(name, libbase); if (result) return (result); } ev = CreateEvent(CallAddr, &seqnum, MSG_ACT_FINDTASK, name, NULL, NO_EXPAND); if (!ev) { if (onlyfails) return (result); JumpOrigFunc(0); } /* * Allocated event safely, now tell SnoopDos task to wake up */ if (onlyfails) { CheckForPause(NULL, 0); } else { ev->status = ES_UPDATING; CheckForPause(ev, seqnum); Signal(SnoopTask, NewEventMask); result = origfunc(name, libbase); } ObtainSemaphore(&BufSem); if (seqnum >= RealFirstSeq) { if (result) ev->result = MSG(MSG_RES_OKAY); else ev->result = MSG(MSG_RES_FAIL); ev->status = ES_READY; Signal(SnoopTask, NewEventMask); } ReleaseSemaphore(&BufSem); return (result); } /* * New_LockPubScreen(name) * * Obtains a lock on the named public screen. If the name is NULL, * then the caller is looking for a Lock() on the default screen, * and we ignore it (otherwise we'd get loads of output). */ ULONG ASM New_LockPubScreen(reg_a0 char *name, reg_a6 void *libbase) { MarkCallAddr; FP_LockPubScreen origfunc = (FP_LockPubScreen) PatchList[GID_LOCKSCREEN].origfunc; int onlyfails = OnlyShowFails; ULONG result; Event *ev; LONG seqnum; if (!name) JumpOrigFunc(0); if (onlyfails) { result = origfunc(name, libbase); if (result) return (result); } ev = CreateEvent(CallAddr, &seqnum, MSG_ACT_LOCKSCREEN, name, NULL, NO_EXPAND); if (!ev) { if (onlyfails) return (result); JumpOrigFunc(0); } /* * Allocated event safely, now tell SnoopDos task to wake up */ if (onlyfails) { CheckForPause(NULL, 0); } else { ev->status = ES_UPDATING; Signal(SnoopTask, NewEventMask); CheckForPause(ev, seqnum); result = origfunc(name, libbase); } ObtainSemaphore(&BufSem); if (seqnum >= RealFirstSeq) { if (result) ev->result = MSG(MSG_RES_OKAY); else ev->result = MSG(MSG_RES_FAIL); ev->status = ES_READY; Signal(SnoopTask, NewEventMask); } ReleaseSemaphore(&BufSem); return (result); } /* * New_OpenDevice(name, unit, ioreq, flags) * * Opens a new device. We need to be careful with the error return * for this function, since success is indicated by a return of 0! * * N.b. Kickstart 3.1 has a space-saving hack which allows a NULL * device name to be interpreted as a magic cookie indicating a * particular ROM devices. We ignore such calls (especially from CON, * which does it a lot!) but flag other occurrances of NULL device * names as an error. */ ULONG ASM New_OpenDevice(reg_a0 char *name, reg_d0 long unit, reg_a1 struct IORequest *ioreq, reg_d1 long flags, reg_a6 void *libbase) { MarkCallAddr; FP_OpenDevice origfunc = (FP_OpenDevice)PatchList[GID_OPENDEVICE].origfunc; int onlyfails = OnlyShowFails; ULONG result; Event *ev; char unitstr[20]; char *devname = name; LONG seqnum; /* * Release Semaphore replacement code for debugging patch */ #if MONITOR_SEMAPHORE Semaphore *sem = (Semaphore *)name; if (LoadTask == SysBase->ThisTask) KPrintF("ReleaseSemaphore in LoadSeg: retaddr = %08lx\n", CallAddr); return origfunc(name, unit, ioreq, flags, libbase); #endif if (onlyfails) { result = origfunc(name, unit, ioreq, flags, libbase); if (!result) return (result); } /* * Now a workaround for a bug in CON that makes it call * OpenDevice("timer.device" | NULL) very frequently. * (The NULL is a space-saving shorthand which the 3.1 Kickstart * interprets as a magic cookie for "timer.device" to save some * ROM space). */ if ((CallAddr >= RomStart && CallAddr <= RomEnd) || strcmp(SysBase->ThisTask->tc_Node.ln_Name, "CON") == 0) { if (onlyfails) return (result); JumpOrigFunc(unit); } if (!devname) devname = MSG(MSG_NULLSTR); mysprintf(unitstr, MSG(MSG_OPT_DEVUNIT), unit); ev = CreateEvent(CallAddr, &seqnum, MSG_ACT_OPENDEV, devname, unitstr, NO_EXPAND); if (!ev) { if (onlyfails) return (result); JumpOrigFunc(unit); } /* * Allocated event safely, now tell SnoopDos task to wake up */ if (onlyfails) { CheckForPause(NULL, 0); } else { ev->status = ES_UPDATING; CheckForPause(ev, seqnum); Signal(SnoopTask, NewEventMask); result = origfunc(name, unit, ioreq, flags, libbase); } ObtainSemaphore(&BufSem); if (seqnum >= RealFirstSeq) { if (!result) ev->result = MSG(MSG_RES_OKAY); else ev->result = MSG(MSG_RES_FAIL); ev->status = ES_READY; Signal(SnoopTask, NewEventMask); } ReleaseSemaphore(&BufSem); return (result); } /* * New_OpenFont(textattr) * * Opens a new font that's currently in memory. OpenDiskFont will * call this function first before checking the disk, so patching * this function catches all accesses. */ ULONG ASM New_OpenFont(reg_a0 struct TextAttr *textattr, reg_a6 void *libbase) { MarkCallAddr; FP_OpenFont origfunc = (FP_OpenFont)PatchList[GID_OPENFONT].origfunc; int onlyfails = OnlyShowFails; ULONG result; Event *ev; char *name; char sizestr[20]; LONG seqnum; if (onlyfails) { result = origfunc(textattr, libbase); if (result) return (result); } if (textattr) { mysprintf(sizestr, MSG(MSG_OPT_FONTSIZE), textattr->ta_YSize); name = textattr->ta_Name; } else { *sizestr = '\0'; name = MSG(MSG_NULLSTR); } ev = CreateEvent(CallAddr, &seqnum, MSG_ACT_OPENFONT, name, sizestr, NO_EXPAND); if (!ev) { if (onlyfails) return (result); JumpOrigFunc(0); } /* * Allocated event safely, now tell SnoopDos task to wake up */ if (onlyfails) { CheckForPause(NULL, 0); } else { ev->status = ES_UPDATING; CheckForPause(ev, seqnum); Signal(SnoopTask, NewEventMask); result = origfunc(textattr, libbase); } ObtainSemaphore(&BufSem); if (seqnum >= RealFirstSeq) { if (result) ev->result = MSG(MSG_RES_OKAY); else ev->result = MSG(MSG_RES_FAIL); ev->status = ES_READY; Signal(SnoopTask, NewEventMask); } ReleaseSemaphore(&BufSem); return (result); } /* * New_OpenLibrary(name, version) * * Opens the named library with the specified version number. * * We have a nasty potential for deadlock here -- CreateEvent() * which we call, in turns calls DateStamp(), and DateStamp() * itself calls OpenLibrary(....). So, we need to protect * ourselves against a recursive call. * * We do this by ignoring any call to open dos.library made * by a call originating in ROM. */ ULONG ASM New_OpenLibrary(reg_a1 char *name, reg_d0 long version, reg_a6 void *libbase) { MarkCallAddr; FP_OpenLibrary origfunc = (FP_OpenLibrary) PatchList[GID_OPENLIBRARY].origfunc; int onlyfails = OnlyShowFails; ULONG result; Event *ev; char verstr[20]; ULONG taskhandle; LONG seqnum; /* * Check for re-entrant calling (usually caused by the * DateStamp call in the GetNewEvent() code). * * In theory, we should only need to check if the semaphore * is owned by the current task. In practise, we need * the additional checks to be really sure (otherwise * we seem to get unexplained crashes very frequently -- * very mysterious and worrying). * * We also ignore calls to open dos.library -- this is partly * because it gets called by DateStamp() inside dos.library * itself (so things that call DateStamp once a second, like * Enforcer(), will generate loads of calls to it) and partly * to avoid problems with our own CreateEvent() code calling it. */ if (BufSem.ss_Owner == SysBase->ThisTask || strcmp(name, "dos.library") == 0) { JumpOrigFunc(version); } if (onlyfails) { result = origfunc(name, version, libbase); if (result) return (result); } taskhandle = CheckTaskRecursion(); if (!taskhandle) { if (!onlyfails) result = origfunc(name, version, libbase); return (result); } mysprintf(verstr, MSG(MSG_OPT_LIBVER), version); ev = CreateEvent(CallAddr, &seqnum, MSG_ACT_OPENLIB, name, verstr, NO_EXPAND); EndTaskRecursion(taskhandle); if (!ev) { if (onlyfails) return (result); JumpOrigFunc(version); } /* * Allocated event safely, now tell SnoopDos task to wake up */ if (onlyfails) { CheckForPause(NULL, 0); } else { ev->status = ES_UPDATING; CheckForPause(ev, seqnum); Signal(SnoopTask, NewEventMask); result = origfunc(name, version, libbase); } ObtainSemaphore(&BufSem); if (seqnum >= RealFirstSeq) { if (result) ev->result = MSG(MSG_RES_OKAY); else ev->result = MSG(MSG_RES_FAIL); ev->status = ES_READY; Signal(SnoopTask, NewEventMask); } ReleaseSemaphore(&BufSem); return (result); } /* * New_OpenResource(name) * * Opens the named resource */ ULONG ASM New_OpenResource(reg_a1 char *name, reg_a6 void *libbase) { MarkCallAddr; FP_OpenResource origfunc = (FP_OpenResource) PatchList[GID_OPENRESOURCE].origfunc; int onlyfails = OnlyShowFails; ULONG result; Event *ev; LONG seqnum; if (onlyfails) { result = origfunc(name, libbase); if (result) return (result); } ev = CreateEvent(CallAddr, &seqnum, MSG_ACT_OPENRES, name, NULL, NO_EXPAND); if (!ev) { if (onlyfails) return (result); JumpOrigFunc(0); } /* * Allocated event safely, now tell SnoopDos task to wake up */ if (onlyfails) { CheckForPause(NULL, 0); } else { ev->status = ES_UPDATING; CheckForPause(ev, seqnum); Signal(SnoopTask, NewEventMask); result = origfunc(name, libbase); } ObtainSemaphore(&BufSem); if (seqnum >= RealFirstSeq) { if (result) ev->result = MSG(MSG_RES_OKAY); else ev->result = MSG(MSG_RES_FAIL); ev->status = ES_READY; Signal(SnoopTask, NewEventMask); } ReleaseSemaphore(&BufSem); return (result); } /* * New_FindToolType(array, tooltype) * * Searches the tooltype array for a particular tooltype */ ULONG ASM New_FindToolType(reg_a0 char **array, reg_a1 char *tooltype, reg_a6 void *libbase) { MarkCallAddr; FP_FindToolType origfunc = (FP_FindToolType) PatchList[GID_READTOOLTYPES].origfunc; int onlyfails = OnlyShowFails; ULONG result; Event *ev; char *toolname = tooltype; LONG seqnum; if (onlyfails) { result = origfunc(array, tooltype, libbase); if (result) return (result); } if (toolname == NULL) toolname = MSG(MSG_NULLSTR); ev = CreateEvent(CallAddr, &seqnum, MSG_ACT_FINDTOOL, toolname, NULL, NO_EXPAND); if (!ev) { if (onlyfails) return (result); JumpOrigFunc(0); } /* * Allocated event safely, now tell SnoopDos task to wake up */ if (onlyfails) { CheckForPause(NULL, 0); } else { ev->status = ES_UPDATING; CheckForPause(ev, seqnum); Signal(SnoopTask, NewEventMask); result = origfunc(array, tooltype, libbase); } ObtainSemaphore(&BufSem); if (seqnum >= RealFirstSeq) { if (result) ev->result = MSG(MSG_RES_OKAY); else ev->result = MSG(MSG_RES_FAIL); ev->status = ES_READY; Signal(SnoopTask, NewEventMask); } ReleaseSemaphore(&BufSem); return (result); } /* * New_MatchToolValue(tooltype, value) * * Checks if a specified tooltype contains a given value */ ULONG ASM New_MatchToolValue(reg_a0 char *tooltype, reg_a1 char *value, reg_a6 void *libbase) { MarkCallAddr; FP_MatchToolValue origfunc = (FP_MatchToolValue) PatchList[GID_READTOOLTYPES2].origfunc; int onlyfails = OnlyShowFails; ULONG result; Event *ev; char *toolname = tooltype; LONG seqnum; if (onlyfails) { result = origfunc(tooltype, value, libbase); if (result) return (result); } if (toolname == NULL) toolname = MSG(MSG_NULLSTR); ev = CreateEvent(CallAddr, &seqnum, MSG_ACT_MATCHTOOL, toolname, value, NO_EXPAND); if (!ev) { if (onlyfails) return (result); JumpOrigFunc(0); } /* * Allocated event safely, now tell SnoopDos task to wake up */ if (onlyfails) { CheckForPause(NULL, 0); } else { ev->status = ES_UPDATING; CheckForPause(ev, seqnum); Signal(SnoopTask, NewEventMask); result = origfunc(tooltype, value, libbase); } ObtainSemaphore(&BufSem); if (seqnum >= RealFirstSeq) { if (result) ev->result = MSG(MSG_RES_OKAY); else ev->result = MSG(MSG_RES_FAIL); ev->status = ES_READY; Signal(SnoopTask, NewEventMask); } ReleaseSemaphore(&BufSem); return (result); } /* * New_CurrentDir() * * Changes current directory to somewhere else */ ULONG ASM New_CurrentDir(reg_d1 BPTR lock, reg_a6 void *libbase) { MarkCallAddr; FP_CurrentDir origfunc = (FP_CurrentDir)PatchList[GID_CHANGEDIR].origfunc; int onlyfails = OnlyShowFails; Event *ev; char lockbuf[MAX_LOCK_LEN+1]; char *lockpath; LONG seqnum; if (onlyfails) return origfunc(lock, libbase); /* CurrentDir never fails */ lockpath = MyNameFromLock(lock, NULL, lockbuf, MAX_LOCK_LEN); ev = CreateEvent(CallAddr, &seqnum, MSG_ACT_CHANGEDIR, lockpath, NULL, NO_EXPAND); if (!ev) JumpOrigFunc(0); /* * Allocated event safely, now tell SnoopDos task to wake up */ ev->status = ES_UPDATING; CheckForPause(ev, seqnum); ObtainSemaphore(&BufSem); if (seqnum >= RealFirstSeq) { ev->status = ES_READY; Signal(SnoopTask, NewEventMask); } ReleaseSemaphore(&BufSem); return origfunc(lock, libbase); } /* * New_DeleteFile() * * Deletes a file from disk */ ULONG ASM New_DeleteFile(reg_d1 char *name, reg_a6 void *libbase) { MarkCallAddr; FP_DeleteFile origfunc = (FP_DeleteFile)PatchList[GID_DELETE].origfunc; int onlyfails = OnlyShowFails; Event *ev; ULONG result; LONG seqnum; if (onlyfails) { result = origfunc(name, libbase); if (result) return (result); } ev = CreateEvent(CallAddr, &seqnum, MSG_ACT_DELETE, name, NULL, EXPAND_NAME); if (!ev) { if (onlyfails) return (result); JumpOrigFunc(0); } /* * Allocated event safely, now tell SnoopDos task to wake up */ if (onlyfails) { CheckForPause(NULL, 0); } else { ev->status = ES_UPDATING; CheckForPause(ev, seqnum); Signal(SnoopTask, NewEventMask); result = origfunc(name, libbase); } ObtainSemaphore(&BufSem); if (seqnum >= RealFirstSeq) { if (result) ev->result = MSG(MSG_RES_OKAY); else ev->result = MSG(MSG_RES_FAIL); ev->status = ES_READY; Signal(SnoopTask, NewEventMask); } ReleaseSemaphore(&BufSem); return (result); } /* * New_Execute() * * Executes a command from disk. Now superceded by System and RunCommand */ ULONG ASM New_Execute(reg_d1 char *cmdline, reg_d2 BPTR fin, reg_d3 BPTR fout, reg_a6 void *libbase) { MarkCallAddr; FP_Execute origfunc = (FP_Execute)PatchList[GID_EXECUTE].origfunc; int onlyfails = OnlyShowFails; Event *ev; ULONG result; char *optstr; LONG seqnum; /* * For functions that may potentially take a long time to complete, * we check if they're being called from ROM straight away, so * that we can exit and not hang around waiting for them if they * are, even in the OnlyShowFails case. */ if (!MonROMCalls && CallAddr >= RomStart && CallAddr <= RomEnd) JumpOrigFunc(0); if (onlyfails) { result = origfunc(cmdline, fin, fout, libbase); if (result) return (result); } if (fin == NULL) optstr = MSG(MSG_OPT_EXECSINGLE); else optstr = MSG(MSG_OPT_EXECBATCH); ev = CreateEvent(CallAddr, &seqnum, MSG_ACT_EXECUTE, cmdline, optstr, NO_EXPAND); if (!ev) { if (onlyfails) return (result); JumpOrigFunc(0); } /* * Allocated event safely, now tell SnoopDos task to wake up */ if (onlyfails) { CheckForPause(NULL, 0); } else { ev->status = ES_UPDATING; CheckForPause(ev, seqnum); Signal(SnoopTask, NewEventMask); result = origfunc(cmdline, fin, fout, libbase); } ObtainSemaphore(&BufSem); if (seqnum >= RealFirstSeq) { if (result) ev->result = MSG(MSG_RES_OKAY); else ev->result = MSG(MSG_RES_FAIL); ev->status = ES_READY; Signal(SnoopTask, NewEventMask); } ReleaseSemaphore(&BufSem); return (result); } /* * New_GetVar() * * Inquires about the value of an environment variable */ ULONG ASM New_GetVar(reg_d1 char *name, reg_d2 char *buffer, reg_d3 size, reg_d4 flags, reg_a6 void *libbase) { MarkCallAddr; FP_GetVar origfunc = (FP_GetVar)PatchList[GID_GETVAR].origfunc; int onlyfails = OnlyShowFails; Event *ev; ULONG result; char optstr[20]; ULONG optid; LONG seqnum; if (onlyfails) { result = origfunc(name, buffer, size, flags, libbase); if (result != -1) return (result); } /* * Now determine our flags. We recognise global, local * or either types of variable. Binary variables are * flagged with a trailing asterisk. */ if (flags & GVF_GLOBAL_ONLY) optid = MSG_OPT_GLOBAL; else if ((flags & 7) == LV_ALIAS) optid = MSG_OPT_ALIAS; else if (flags & GVF_LOCAL_ONLY) optid = MSG_OPT_LOCAL; else optid = MSG_OPT_ANY; strcpy(optstr, MSG(optid)); if (flags & GVF_BINARY_VAR) strcat(optstr, "*"); ev = CreateEvent(CallAddr, &seqnum, MSG_ACT_GETVAR, name, optstr, NO_EXPAND); if (!ev) { if (onlyfails) return (result); JumpOrigFunc(0); } /* * Allocated event safely, now tell SnoopDos task to wake up */ if (onlyfails) { CheckForPause(NULL, 0); } else { ev->status = ES_UPDATING; CheckForPause(ev, seqnum); Signal(SnoopTask, NewEventMask); result = origfunc(name, buffer, size, flags, libbase); } ObtainSemaphore(&BufSem); if (seqnum >= RealFirstSeq) { if (result != -1) ev->result = MSG(MSG_RES_OKAY); else ev->result = MSG(MSG_RES_FAIL); ev->status = ES_READY; Signal(SnoopTask, NewEventMask); } ReleaseSemaphore(&BufSem); return (result); } /* * New_FindVar() * * Inquires about the value of a local environment variable */ ULONG ASM New_FindVar(reg_d1 char *name, reg_d2 ULONG type, reg_a6 void *libbase) { MarkCallAddr; FP_FindVar origfunc = (FP_FindVar)PatchList[GID_GETVAR2].origfunc; int onlyfails = OnlyShowFails; Event *ev; ULONG result; ULONG optid; LONG seqnum; if (onlyfails) { result = origfunc(name, type, libbase); if (result) return (result); } if ((type & 7) == LV_VAR) optid = MSG_OPT_LOCAL; else if ((type & 7) == LV_ALIAS) optid = MSG_OPT_ALIAS; else optid = MSG_OPT_UNKNOWN; ev = CreateEvent(CallAddr, &seqnum, MSG_ACT_FINDVAR, name, MSG(optid), NO_EXPAND); if (!ev) { if (onlyfails) return (result); JumpOrigFunc(0); } /* * Allocated event safely, now tell SnoopDos task to wake up */ if (onlyfails) { CheckForPause(NULL, 0); } else { ev->status = ES_UPDATING; Signal(SnoopTask, NewEventMask); CheckForPause(ev, seqnum); result = origfunc(name, type, libbase); } ObtainSemaphore(&BufSem); if (seqnum >= RealFirstSeq) { if (result) ev->result = MSG(MSG_RES_OKAY); else ev->result = MSG(MSG_RES_FAIL); ev->status = ES_READY; Signal(SnoopTask, NewEventMask); } ReleaseSemaphore(&BufSem); return (result); } /* * New_SetVar() * * Sets a (possibly new) variable to a particular value */ ULONG ASM New_SetVar(reg_d1 char *name, reg_d2 char *buffer, reg_d3 size, reg_d4 flags, reg_a6 void *libbase) { MarkCallAddr; FP_SetVar origfunc = (FP_SetVar)PatchList[GID_SETVAR].origfunc; int onlyfails = OnlyShowFails; Event *ev; ULONG result; char varstr[MAX_STR_LEN+1]; int vlen; ULONG optid; LONG seqnum; if (onlyfails) { result = origfunc(name, buffer, size, flags, libbase); if (result) return (result); } /* * Now determine our flags. We recognise global, local * or alias variables. */ if (flags & GVF_GLOBAL_ONLY) optid = MSG_OPT_GLOBAL; else if ((flags & 7) == LV_VAR) optid = MSG_OPT_LOCAL; else if ((flags & 7) == LV_ALIAS) optid = MSG_OPT_ALIAS; else optid = MSG_OPT_UNKNOWN; /* * Now create a string that looks like "Variable=Value" * * We go to some pains to ensure we don't overwrite our * string length */ vlen = strlen(name); if (vlen > (MAX_STR_LEN-1)) { strncpy(varstr, name, MAX_STR_LEN); varstr[MAX_STR_LEN] = 0; } else { strcpy(varstr, name); strcat(varstr, "="); vlen = 98 - vlen; if (size != -1) vlen = MIN(vlen, size); strncat(varstr, buffer, vlen); varstr[MAX_STR_LEN] = 0; } ev = CreateEvent(CallAddr, &seqnum, MSG_ACT_SETVAR, varstr, MSG(optid), NO_EXPAND); if (!ev) { if (onlyfails) return (result); JumpOrigFunc(0); } /* * Allocated event safely, now tell SnoopDos task to wake up */ if (onlyfails) { CheckForPause(NULL, 0); } else { ev->status = ES_UPDATING; CheckForPause(ev, seqnum); Signal(SnoopTask, NewEventMask); result = origfunc(name, buffer, size, flags, libbase); } ObtainSemaphore(&BufSem); if (seqnum >= RealFirstSeq) { if (result) ev->result = MSG(MSG_RES_OKAY); else ev->result = MSG(MSG_RES_FAIL); ev->status = ES_READY; Signal(SnoopTask, NewEventMask); } ReleaseSemaphore(&BufSem); return (result); } /* * New_DeleteVar() * * Deletes an environment variable from the environment */ ULONG ASM New_DeleteVar(reg_d1 char *name, reg_d2 ULONG flags, reg_a6 void *libbase) { MarkCallAddr; FP_DeleteVar origfunc = (FP_DeleteVar)PatchList[GID_SETVAR2].origfunc; int onlyfails = OnlyShowFails; Event *ev; ULONG result; ULONG optid; LONG seqnum; if (onlyfails) { result = origfunc(name, flags, libbase); if (result) return (result); } /* * Now determine our flags. We recognise global, local * or alias variables. */ if (flags & GVF_GLOBAL_ONLY) optid = MSG_OPT_GLOBAL; else if ((flags & 7) == LV_VAR) optid = MSG_OPT_LOCAL; else if ((flags & 7) == LV_ALIAS) optid = MSG_OPT_ALIAS; else optid = MSG_OPT_UNKNOWN; ev = CreateEvent(CallAddr, &seqnum, MSG_ACT_KILLVAR, name, MSG(optid), NO_EXPAND); if (!ev) { if (onlyfails) return (result); JumpOrigFunc(0); } /* * Allocated event safely, now tell SnoopDos task to wake up */ if (onlyfails) { CheckForPause(NULL, 0); } else { ev->status = ES_UPDATING; CheckForPause(ev, seqnum); Signal(SnoopTask, NewEventMask); result = origfunc(name, flags, libbase); } ObtainSemaphore(&BufSem); if (seqnum >= RealFirstSeq) { if (result) ev->result = MSG(MSG_RES_OKAY); else ev->result = MSG(MSG_RES_FAIL); ev->status = ES_READY; Signal(SnoopTask, NewEventMask); } ReleaseSemaphore(&BufSem); return (result); } /* * New_LoadSeg() * * Tries to load in a module from disk */ ULONG ASM New_LoadSeg(reg_d1 char *name, reg_a6 void *libbase) { MarkCallAddr; FP_LoadSeg origfunc = (FP_LoadSeg)PatchList[GID_LOADSEG].origfunc; int onlyfails = OnlyShowFails; Event *ev; ULONG result; LONG seqnum; if (onlyfails) { result = origfunc(name, libbase); if (result) return (result); } ev = CreateEvent(CallAddr, &seqnum, MSG_ACT_LOADSEG, name, NULL, EXPAND_NAME); if (!ev) { if (onlyfails) return (result); JumpOrigFunc(0); } /* * Allocated event safely, now tell SnoopDos task to wake up */ if (onlyfails) { CheckForPause(NULL, 0); } else { ev->status = ES_UPDATING; CheckForPause(ev, seqnum); #if MONITOR_SEMAPHORE LoadTask = SysBase->ThisTask; result = origfunc(name, libbase); LoadTask = 0; #else result = origfunc(name, libbase); #endif } ObtainSemaphore(&BufSem); if (seqnum >= RealFirstSeq) { if (result) ev->result = MSG(MSG_RES_OKAY); else ev->result = MSG(MSG_RES_FAIL); ev->status = ES_READY; Signal(SnoopTask, NewEventMask); } ReleaseSemaphore(&BufSem); return (result); } /* * New_NewLoadSeg() * * Tries to load in a module from disk */ ULONG ASM New_NewLoadSeg(reg_d1 char *name, reg_d2 TAGPTR tags, reg_a6 void *libbase) { MarkCallAddr; FP_NewLoadSeg origfunc = (FP_NewLoadSeg)PatchList[GID_LOADSEG2].origfunc; int onlyfails = OnlyShowFails; Event *ev; ULONG result; LONG seqnum; if (onlyfails) { result = origfunc(name, tags, libbase); if (result) return (result); } ev = CreateEvent(CallAddr, &seqnum, MSG_ACT_NEWLOADSEG, name, NULL, EXPAND_NAME); if (!ev) { if (onlyfails) return (result); JumpOrigFunc(0); } /* * Allocated event safely, now tell SnoopDos task to wake up */ if (onlyfails) { CheckForPause(NULL, 0); } else { ev->status = ES_UPDATING; CheckForPause(ev, seqnum); Signal(SnoopTask, NewEventMask); result = origfunc(name, tags, libbase); } ObtainSemaphore(&BufSem); if (seqnum >= RealFirstSeq) { if (result) ev->result = MSG(MSG_RES_OKAY); else ev->result = MSG(MSG_RES_FAIL); ev->status = ES_READY; Signal(SnoopTask, NewEventMask); } ReleaseSemaphore(&BufSem); return (result); } /* * New_NewLoadSeg() * * Tries to load in a module from disk */ ULONG ASM New_Lock(reg_d1 char *name, reg_d2 LONG mode, reg_a6 void *libbase) { MarkCallAddr; FP_Lock origfunc = (FP_Lock)PatchList[GID_LOCKFILE].origfunc; int onlyfails = OnlyShowFails; Event *ev; ULONG result; LONG optid; char *curname; LONG seqnum; if (onlyfails) { result = origfunc(name, mode, libbase); if (result) return (result); } if (mode == ACCESS_READ) optid = MSG_OPT_READ; else if (mode == ACCESS_WRITE) optid = MSG_OPT_WRITE; else optid = MSG_OPT_READBAD; curname = name; if (!ShowPaths && *curname == '\0') curname = "\"\""; ev = CreateEvent(CallAddr, &seqnum, MSG_ACT_LOCK, curname, MSG(optid), EXPAND_NAME); if (!ev) { if (onlyfails) return (result); JumpOrigFunc(0); } /* * Allocated event safely, now tell SnoopDos task to wake up */ if (onlyfails) { CheckForPause(NULL, 0); } else { ev->status = ES_UPDATING; CheckForPause(ev, seqnum); Signal(SnoopTask, NewEventMask); result = origfunc(name, mode, libbase); } ObtainSemaphore(&BufSem); if (seqnum >= RealFirstSeq) { if (result) ev->result = MSG(MSG_RES_OKAY); else ev->result = MSG(MSG_RES_FAIL); ev->status = ES_READY; Signal(SnoopTask, NewEventMask); } ReleaseSemaphore(&BufSem); return (result); } /* * New_CreateDir() * * Creates a new directory on disk */ ULONG ASM New_CreateDir(reg_d1 char *name, reg_a6 void *libbase) { MarkCallAddr; FP_CreateDir origfunc = (FP_CreateDir)PatchList[GID_MAKEDIR].origfunc; int onlyfails = OnlyShowFails; Event *ev; ULONG result; LONG seqnum; if (onlyfails) { result = origfunc(name, libbase); if (result) return (result); } ev = CreateEvent(CallAddr, &seqnum, MSG_ACT_MAKEDIR, name, NULL, EXPAND_NAME); if (!ev) { if (onlyfails) return (result); JumpOrigFunc(0); } /* * Allocated event safely, now tell SnoopDos task to wake up */ if (onlyfails) { CheckForPause(NULL, 0); } else { ev->status = ES_UPDATING; CheckForPause(ev, seqnum); Signal(SnoopTask, NewEventMask); result = origfunc(name, libbase); } ObtainSemaphore(&BufSem); if (seqnum >= RealFirstSeq) { if (result) ev->result = MSG(MSG_RES_OKAY); else ev->result = MSG(MSG_RES_FAIL); ev->status = ES_READY; Signal(SnoopTask, NewEventMask); } ReleaseSemaphore(&BufSem); return (result); } /* * New_MakeLink() * * Creates a new hard or soft link */ ULONG ASM New_MakeLink(reg_d1 char *name, reg_d2 LONG dest, reg_d3 LONG soft, reg_a6 void *libbase) { MarkCallAddr; struct Process *myproc = (struct Process *)SysBase->ThisTask; FP_MakeLink origfunc = (FP_MakeLink)PatchList[GID_MAKELINK].origfunc; int onlyfails = OnlyShowFails; Event *ev; ULONG result; LONG optid; char namestr[MAX_STR_LEN+1]; LONG seqnum; int len; if (onlyfails) { result = origfunc(name, dest, soft, libbase); if (result) return (result); } if (soft) optid = MSG_OPT_SOFTLINK; else optid = MSG_OPT_HARDLINK; /* * Now build a string that looks like "name --> " to display * as our link. For soft links, we just concatenate the two * strings. For hard links, we have to generate a filename * from the lock also. */ len = strlen(name); if (len >= MAX_STR_LEN) { strncpy(namestr, name, MAX_STR_LEN); namestr[MAX_STR_LEN] = 0; } else { if (ShowPaths) { strcpy(namestr, MyNameFromLock(myproc->pr_CurrentDir, name, namestr, MAX_STR_LEN-2)); len = strlen(namestr); } else strcpy(namestr, name); strcat(namestr, LinkPointerString); /* Usually ' --> ' */ if (soft) { strncat(namestr, (char *)dest, MAX_STR_LEN - len - 1); namestr[MAX_STR_LEN] = 0; } else { strcat(namestr, MyNameFromLock(dest, NULL, namestr+len+1, MAX_STR_LEN-len-1)); } } ev = CreateEvent(CallAddr, &seqnum, MSG_ACT_MAKELINK, namestr, MSG(optid), NO_EXPAND); if (!ev) { if (onlyfails) return (result); JumpOrigFunc(0); } /* * Allocated event safely, now tell SnoopDos task to wake up */ if (onlyfails) { CheckForPause(NULL, 0); } else { ev->status = ES_UPDATING; CheckForPause(ev, seqnum); Signal(SnoopTask, NewEventMask); result = origfunc(name, dest, soft, libbase); } ObtainSemaphore(&BufSem); if (seqnum >= RealFirstSeq) { if (result) ev->result = MSG(MSG_RES_OKAY); else ev->result = MSG(MSG_RES_FAIL); ev->status = ES_READY; Signal(SnoopTask, NewEventMask); } ReleaseSemaphore(&BufSem); return (result); } /* * New_Open() * * Opens a new file on disk */ ULONG ASM New_Open(reg_d1 char *name, reg_d2 LONG accessMode, reg_a6 void *libbase) { MarkCallAddr; FP_Open origfunc = (FP_Open)PatchList[GID_OPENFILE].origfunc; int onlyfails = OnlyShowFails; Event *ev; ULONG result; char *optstr; LONG seqnum; if (accessMode == MODE_OLDFILE) optstr = MSG(MSG_OPT_READ); else if (accessMode == MODE_NEWFILE) optstr = MSG(MSG_OPT_WRITE); else if (accessMode == MODE_READWRITE) optstr = MSG(MSG_OPT_MODIFY); else optstr = MSG(MSG_OPT_UNKNOWN); if (onlyfails) { result = origfunc(name, accessMode, libbase); if (result) return (result); } ev = CreateEvent(CallAddr, &seqnum, MSG_ACT_OPEN, name, optstr, EXPAND_NAME); if (!ev) { if (onlyfails) return (result); JumpOrigFunc(0); } /* * Allocated event safely, now tell SnoopDos task to wake up */ if (onlyfails) { CheckForPause(NULL, 0); } else { ev->status = ES_UPDATING; CheckForPause(ev, seqnum); Signal(SnoopTask, NewEventMask); result = origfunc(name, accessMode, libbase); } ObtainSemaphore(&BufSem); if (seqnum >= RealFirstSeq) { if (result) ev->result = MSG(MSG_RES_OKAY); else ev->result = MSG(MSG_RES_FAIL); ev->status = ES_READY; Signal(SnoopTask, NewEventMask); } ReleaseSemaphore(&BufSem); return (result); } /* * New_Rename() * * Renames a file on disk. This is a little tricky, since we have * to generate two events: one containing the source name and * one containing the destination name. */ ULONG ASM New_Rename(reg_d1 char *oldname, reg_d2 char *newname, reg_a6 void *libbase) { MarkCallAddr; FP_Rename origfunc = (FP_Rename)PatchList[GID_RENAME].origfunc; int onlyfails = OnlyShowFails; Event *ev; ULONG result; LONG seqnum; if (onlyfails) { result = origfunc(oldname, newname, libbase); if (result) return (result); } ev = CreateEvent(CallAddr, &seqnum, MSG_ACT_RENAME, oldname, NULL, EXPAND_NAME); if (!ev) { if (onlyfails) return (result); JumpOrigFunc(0); } ev->status = ES_READY; ev = CreateEvent(CallAddr, &seqnum, MSG_ACT_RENAME2, newname, NULL, EXPAND_NAME); if (!ev) { if (onlyfails) return (result); JumpOrigFunc(0); } /* * Allocated event safely, now tell SnoopDos task to wake up */ if (onlyfails) { CheckForPause(NULL, 0); } else { ev->status = ES_UPDATING; CheckForPause(ev, seqnum); Signal(SnoopTask, NewEventMask); result = origfunc(oldname, newname, libbase); } ObtainSemaphore(&BufSem); if (seqnum >= RealFirstSeq) { if (result) ev->result = MSG(MSG_RES_OKAY); else ev->result = MSG(MSG_RES_FAIL); ev->status = ES_READY; Signal(SnoopTask, NewEventMask); } ReleaseSemaphore(&BufSem); return (result); } /* * New_RunCommand() * * Runs a command from disk. Like Execute() only with more control. */ ULONG ASM New_RunCommand(reg_d1 BPTR seglist, reg_d2 ULONG stack, reg_d3 char *cmdline, reg_d4 cmdlen, reg_a6 void *libbase) { MarkCallAddr; FP_RunCommand origfunc = (FP_RunCommand)PatchList[GID_RUNCOMMAND].origfunc; int onlyfails = OnlyShowFails; Event *ev; ULONG result; char stacksize[40]; /* Reserve additional space for return code! */ LONG seqnum; int stklen; char *p; /* * For functions that may potentially take a long time to complete, * we check if they're being called from ROM straight away, so * that we can exit and not hang around waiting for them if they * are, even in the OnlyShowFails case. */ if (!MonROMCalls && CallAddr >= RomStart && CallAddr <= RomEnd) JumpOrigFunc(0); if (onlyfails) { result = origfunc(seglist, stack, cmdline, cmdlen, libbase); if (result != -1) return (result); } /* * This is kind of nasty but it works. We need somewhere to store * the return code string, but when we call CreateEvent(), we * don't know what it will be yet. So, we allocate additional * space at the end of our stacksize to hold this info, and * overwrite it later on. */ mysprintf(stacksize, "%ld", stack); stklen = strlen(stacksize); strcat(stacksize, "................"); /* Storage for return code */ /* * Next, a quick hack to get rid of that nasty CR at the end * of the string (purely for visual purposes -- we put it back * afterwards, of course!) */ for (p = cmdline; *p; p++) { if (*p == '\n') { *p = ' '; break; } } ev = CreateEvent(CallAddr, &seqnum, MSG_ACT_RUNCOMMAND, cmdline, stacksize, NO_EXPAND); if (*p) *p = '\n'; if (!ev) { if (onlyfails) return (result); JumpOrigFunc(0); } ev->options[stklen] = 0; /* * Allocated event safely, now tell SnoopDos task to wake up */ if (onlyfails) { CheckForPause(NULL, 0); } else { ev->result = "----"; ev->status = ES_UPDATING; CheckForPause(ev, seqnum); Signal(SnoopTask, NewEventMask); result = origfunc(seglist, stack, cmdline, cmdlen, libbase); } ObtainSemaphore(&BufSem); if (seqnum >= RealFirstSeq) { if (result != -1) { ev->result = ev->options + stklen + 1; mysprintf(ev->result, "%ld", result); } else { ev->result = MSG(MSG_RES_FAIL); } ev->status = ES_READY; Signal(SnoopTask, NewEventMask); } ReleaseSemaphore(&BufSem); return (result); } /* * New_SystemTagList() * * Executes a command line. Like Execute() only more powerful. */ ULONG ASM New_SystemTagList(reg_d1 char *cmdline, reg_d2 TAGPTR tags, reg_a6 void *libbase) { MarkCallAddr; FP_SystemTagList origfunc = (FP_SystemTagList) PatchList[GID_SYSTEM].origfunc; int onlyfails = OnlyShowFails; Event *ev; ULONG result; char optstr[20]; /* Reserve additional space for return code! */ LONG seqnum; /* * For functions that may potentially take a long time to complete, * we check if they're being called from ROM straight away, so * that we can exit and not hang around waiting for them if they * are, even in the OnlyShowFails case. */ if (!MonROMCalls && CallAddr >= RomStart && CallAddr <= RomEnd) JumpOrigFunc(0); if (onlyfails) { result = origfunc(cmdline, tags, libbase); if (result != -1) return (result); } /* * This is kind of nasty but it works. We need somewhere to store * the return code string, but when we call CreateEvent(), we * don't know what it will be yet. So, we allocate space for our * option string instead and then replace it with our result * string later on. */ strcpy(optstr, "............"); /* Storage for result string */ ev = CreateEvent(CallAddr, &seqnum, MSG_ACT_SYSTEM, cmdline, optstr, NO_EXPAND); if (!ev) { if (onlyfails) return (result); JumpOrigFunc(0); } *ev->options = '\0'; /* * Allocated event safely, now tell SnoopDos task to wake up */ if (onlyfails) { CheckForPause(NULL, 0); } else { ev->result = "----"; ev->status = ES_UPDATING; CheckForPause(ev, seqnum); Signal(SnoopTask, NewEventMask); result = origfunc(cmdline, tags, libbase); } ObtainSemaphore(&BufSem); if (seqnum >= RealFirstSeq) { if (result != -1) { ev->result = ev->options; ev->options = ""; mysprintf(ev->result, "%ld", result); } else { ev->result = MSG(MSG_RES_FAIL); } ev->status = ES_READY; Signal(SnoopTask, NewEventMask); } ReleaseSemaphore(&BufSem); return (result); } /***************************************************************************** * * Now the Mother of all patches -- the PutMsg() packet monitor/Rexx patch * * (There are quite a few sub-functions associated with this.) * *****************************************************************************/ /* * New_AddDosEntry(dlist) * * This is a very simple patch which is only enabled when we are * monitoring DOS packets. It simply signals the main task to update * the device list whenever a new device is added to the DOS device * list. */ ULONG ASM New_AddDosEntry(reg_d1 struct DosList *dlist, reg_a6 void *libbase) { FP_AddDosEntry origfunc = (FP_AddDosEntry) PatchList[GID_ADDDOSENTRY].origfunc; ULONG result; /* * Note that we better remember to actually add the new device * before telling SnoopDos to update its list, otherwise it * might miss it if it's too quick (e.g. priority > 0). * * New: Even though we DO add it before signalling SnoopDos, the * device itself won't be fully initialised for a little bit longer. * Therefore, the main SnoopDos code */ result = origfunc(dlist, libbase); if (dlist && dlist->dol_Type == DLT_DEVICE) Signal(SnoopTask, ScanDosListMask); return (result); } /* * AddWaitingPacket(ev, seqnum, failmsg, dp, port, pt) * * Adds a new entry to the list of waiting packets containing enough * information to allow us to identify the reply to the packet when * it is processed later on (in the main PutMsg() patch). * * If there isn't a spare waiting packet, we will steal one and mark * the event it represented as "Miss" to indicate the return code * was missed. */ void AddWaitingPacket(Event *ev, LONG seqnum, char *failmsg, struct DosPacket *dp, struct MsgPort *port, struct PacketRef *pt) { WaitPacket *wp; Event *oldev; ULONG oldseq; /* * Add details about this packet to our list of waiting * packets, so that when we spot the packet being returned * by PutMsg() later on, we can fill in the result code. * * Note that we must not obtain both the buffer and packet * semaphores simultaneously, or a deadlock could occur. */ ObtainSemaphore(&PacketSem); if (IsListEmpty(&PacketFreeList)) { /* * Removing an existing node which is already in use. We need * to signal our main task to tell it not to continue waiting * for this event to complete. We have to wait until we unlock * the packetsem so we just note the details for now. */ wp = (WaitPacket *)RemTail(&PacketWaitList); oldseq = wp->eventnum; oldev = wp->event; // if (!wp) // KPrintF("Warning!! No packet available on WaitList!\n"); } else { wp = (WaitPacket *)RemHead(&PacketFreeList); oldseq = 0; } wp->dp = dp; wp->sendtask = SysBase->ThisTask; wp->destport = port; wp->event = ev; wp->eventnum = seqnum; wp->arg1 = dp->dp_Arg1; wp->arg2 = dp->dp_Arg2; wp->arg3 = dp->dp_Arg3; wp->resmsg = failmsg; wp->flags = pt->flags; AddHead(&PacketWaitList, (Node *)wp); ReleaseSemaphore(&PacketSem); /* * Finally, check if we had to flush out an old packet from * our list of waiting packets when we allocated the new * packet. If so, mark the packet as missed (if it hasn't * already scrolled off the top of the buffer) */ if (oldseq) { ObtainSemaphore(&BufSem); if (oldseq >= RealFirstSeq) { oldev->result = MSG(MSG_RES_MISSED); oldev->status = ES_READY; Signal(SnoopTask, NewEventMask); } ReleaseSemaphore(&BufSem); } } /* * HandleSimplePacket(calladdr, packet, port, packetref) * * Handles the given packet by creating an event that matches the * packet type (if we support it) and creating a new entry on the * waiting packet list so we can match it up later. * * This function handles only simple packets (i.e. those with the * PK_COMMON flag set in the packet table). If the corresponding * AmigaDOS option (Open, Lock, MakeDir etc.) is not currently * enabled, then no entry is made for this packet. */ void HandleSimplePacket(ULONG calladdr, struct DosPacket *dp, struct MsgPort *port, struct PacketRef *pt) { /* * Now create our new simple event to represent the packet. * The contents of this will vary depending on the packet * itself, but in general, is an exact match with the format * of the AmigaDOS function associated with the packet. * * (If that function is not being monitored, then we do nothing.) */ Event *ev; LONG seqnum; ULONG gadid; ULONG actid = 0; char *volname = ((Task *)(port->mp_SigTask))->tc_Node.ln_Name; BPTR lock = NULL; BSTR name; char *optstr; char namebuf[MAX_STR_LEN+1]; char optbuf[20]; namebuf[0] = '\0'; /* * First, determine the format to use, based on the packet type */ switch (dp->dp_Type) { case ACTION_FINDINPUT: gadid = GID_OPENFILE; actid = MSG_ACT_POPEN; lock = dp->dp_Arg2; name = dp->dp_Arg3; optstr = MSG(MSG_OPT_READ); break; case ACTION_FINDOUTPUT: gadid = GID_OPENFILE; actid = MSG_ACT_POPEN; lock = dp->dp_Arg2; name = dp->dp_Arg3; optstr = MSG(MSG_OPT_WRITE); break; case ACTION_FINDUPDATE: gadid = GID_OPENFILE; actid = MSG_ACT_POPEN; lock = dp->dp_Arg2; name = dp->dp_Arg3; optstr = MSG(MSG_OPT_MODIFY); break; case ACTION_DELETE_OBJECT: gadid = GID_DELETE; actid = MSG_ACT_PDELETE; lock = dp->dp_Arg1; name = dp->dp_Arg2; optstr = NULL; break; case ACTION_CREATE_DIR: gadid = GID_MAKEDIR; actid = MSG_ACT_PMAKEDIR; lock = dp->dp_Arg1; name = dp->dp_Arg2; optstr = NULL; break; case ACTION_LOCATE_OBJECT: gadid = GID_LOCKFILE; actid = MSG_ACT_PLOCK; lock = dp->dp_Arg1; name = dp->dp_Arg2; switch (dp->dp_Arg3) { case ACCESS_READ: optstr = MSG(MSG_OPT_READ); break; case ACCESS_WRITE: optstr = MSG(MSG_OPT_WRITE); break; default: optstr = MSG(MSG_OPT_READBAD); break; } break; case ACTION_MAKE_LINK: { int len; char *cname; gadid = GID_MAKELINK; actid = MSG_ACT_PMAKELINK; lock = dp->dp_Arg1; cname = BTOC(dp->dp_Arg2); /* * Now build a string that looks like "name --> " to * display as our link. For soft links, we just concatenate * the two strings. For hard links, we have to generate a * filename from the lock also. */ len = *cname; if (len >= MAX_STR_LEN) { strncpy(namebuf, cname+1, MAX_STR_LEN); namebuf[MAX_STR_LEN] = 0; } else { if (ShowPaths) { char *tempbuf = namebuf + MAX_STR_LEN-9 - len; memcpy(tempbuf, cname+1, len); tempbuf[len] = '\0'; strcpy(namebuf, AsyncNameFromLock(volname, lock, tempbuf, namebuf, MAX_STR_LEN-8)); len = strlen(namebuf); } else { memcpy(namebuf, cname+1, len); namebuf[len] = '\0'; } strcat(namebuf, LinkPointerString); /* Usually ' --> ' */ len += strlen(LinkPointerString); if (dp->dp_Arg4 == LINK_HARD) { /* * Hard link: interpret dp->dp_Arg3 as the lock * being linked to */ strcat(namebuf, AsyncNameFromLock(volname, dp->dp_Arg3, NULL, namebuf+len+1, MAX_STR_LEN-len-1)); optstr = MSG(MSG_OPT_HARDLINK); } else { /* * Soft link: interpret dp->dp_Arg3 as the name of * the path to the link */ strncat(namebuf, (char *)dp->dp_Arg3, MAX_STR_LEN-len-1); namebuf[MAX_STR_LEN] = 0; optstr = MSG(MSG_OPT_SOFTLINK); } } volname = (char *)NO_EXPAND; /* Don't try and expand filename */ name = NULL; break; } case ACTION_RENAME_OBJECT: lock = dp->dp_Arg1; name = dp->dp_Arg2; optstr = NULL; /* * This is a bit cheeky. The Rename action comes in * two parts, the first line with the original name and * the second part with the new name. We create the * first part here, and then fall through to let the * rest of the code handle the second part. */ if (CurSettings.Func.Opts[GID_RENAME]) { char *cname = BTOC(name); lock = dp->dp_Arg3; name = dp->dp_Arg4; if (*cname) { memcpy(namebuf, cname+1, *cname); namebuf[*cname] = '\0'; } else { /* * If we have an empty string, we replace it with "" * since we may be renaming the current directory (?) */ strcpy(namebuf, "\"\""); } ev = CreateEvent(calladdr, &seqnum, MSG_ACT_PRENAME, namebuf, NULL, (int)volname, lock); if (!ev) return; ev->status = ES_READY; gadid = GID_RENAME; actid = MSG_ACT_PRENAME2; } break; } /* * Now, if we recognised the packet type, create a new event * with the info stored accordingly. We ignore any packets * for which monitoring is disabled. */ if (actid && CurSettings.Func.Opts[gadid]) { /* * We reserve 8 bytes at the start of the options string to * hold the result message (Ok or Fail) that is filled in * by the PutMsg() code when the packet is returned. */ #define RES_LEN 8 strcpy(optbuf, "........"); if (optstr) strcat(optbuf, optstr); if (name) { char *cname = BTOC(name); namebuf[0] = '\0'; if (*cname) { memcpy(namebuf, cname+1, *cname); namebuf[*cname] = '\0'; } else { /* * If we have an empty string, we replace it with "" * since it will typically be a Lock(""). */ strcpy(namebuf, "\"\""); } } ev = CreateEvent(calladdr, &seqnum, actid, namebuf, optbuf, (int)volname, lock); if (!ev) return; ev->result = ev->options; ev->options += RES_LEN; *ev->result = '\0'; ev->status = ES_UPDATING; /* * We add the event to the waiting list BEFORE checking for Pause * so that if while paused, the user decides to disable packet * monitoring, the packet will be in the wait queue already and * thus will be safely flushed; otherwise, it would hang around * for ever (not doing any damage but wasting resources). */ AddWaitingPacket(ev, seqnum, "%s", dp, port, pt); CheckForPause(ev, seqnum); Signal(SnoopTask, NewEventMask); } #undef RES_LEN } /* * HandleRawPacket(calladdr, packet, port, packetref) * * Handles the given packet by creating an event that matches the * packet type (if we support it) and creating a new entry on the * waiting packet list so we can match it up later. * * This function handles all packets and produces a raw hex dump * (with the name of the packet) rather than the more english-like * "Open", "Lock", etc. */ void HandleRawPacket(ULONG calladdr, struct DosPacket *dp, struct MsgPort *port, struct PacketRef *pt) { /* * Building this event is a little tricky, since the result * string will not be a static string but will contain the * actual return value. Thus, we allocate enough room in the * filename portion to hold the result string as well, and * adjust our variables accordingly after the event has * been created. * * The same is true for raw packet types (i.e. those that we * don't recognise at all). */ #define RAW_LEN 20 /* Length of possible raw packet type */ #define RES_LEN 26 /* Length of longest result code */ #define ARG_LEN 50 /* Length of longest argument list */ static char *fmt[] = { "(None)", "%8lx", "%8lx, %8lx", "%8lx, %8lx, %8lx", "%8lx, %8lx, %8lx, %8lx", "%8lx, %8lx, %8lx, %8lx, %8lx" }; static char *failmsg[] = { "%s", "%-4s %8lx", "%-4s %8lx", "%-4s %8lx, %8lx" }; char args[RAW_LEN+RES_LEN+ARG_LEN]; char *tname = args; Event *ev; LONG seqnum; if (pt->flags & PK_RAW) tname += RAW_LEN; tname += RES_LEN; memset(args, ' ', tname - args); mysprintf(tname, fmt[pt->numparams], dp->dp_Arg1, dp->dp_Arg2, dp->dp_Arg3, dp->dp_Arg4, dp->dp_Arg5); ev = CreateEvent(calladdr, &seqnum, pt->msgid, args, ((Task *)(port->mp_SigTask))->tc_Node.ln_Name, NO_EXPAND); if (!ev) return; if (pt->flags & PK_RAW) { /* * Now fix up action name and target name */ ev->action = ev->filename; ev->filename += RAW_LEN; mysprintf(ev->action, MSG(MSG_ACT_RAWPACKET), dp->dp_Type); } ev->result = ev->filename; /* Point to storage for later result */ ev->filename += RES_LEN; *ev->result = 0; /* Make result field empty initially */ ev->status = ES_UPDATING; /* * We add the event to the waiting list BEFORE checking for Pause * so that if while paused, the user decides to disable packet * monitoring, the packet will be in the wait queue already and * thus will be safely flushed; otherwise, it would hang around * for ever (not doing any damage but wasting resources). */ AddWaitingPacket(ev, seqnum, failmsg[pt->flags & PK_MASK], dp, port, pt); CheckForPause(ev, seqnum); Signal(SnoopTask, NewEventMask); } /* * New_PutMsg() * * This function is used frequently by the system to send a message * from one task to another. It is also used by applications wishing * to send ARexx messages, which is what we want to monitor. * * The problem is: how do we distinguish system messages (of which * there are lots!) from ARexx messages (which are relatively * infrequent). * * It's almost impossible to get a perfect solution to this, but we * can get fairly close by checking the following: * * - The destination port must have a name * - The host name associated with the supposed Rexx message must * point to valid memory * - The action type for the Rexx message is RXCOMM * - The Rexx system library must be up and running * - The library base must be word aligned (else we fault on a 68000) * - The Close() vector in the Rexx private system library must * match the Close() vector in the Rexx library base that we * opened ourselves (the library bases themselves can be different). * * If all these hold true, then we can assume it is a valid ARexx * message and display it accordingly. * * For DOS packets, things are a little simpler since DOS packets contain * cross-links that can be easily checked for validity. To keep things * moving at a reasonable speed, we only consider StandardPackets, where * the DosPacket structure immediately follows the exec message structure. * This is not universally true, but it covers all the cases we want to * monitor. * * We need to intercept DOS packets twice -- once on the initial PutMsg * to the target device, and once when the target device PutMsg's the * reply. Thus, we maintain a list of outstanding packets so we can * detect which is which. * * To identify DOS packets, we use the following criterion: * * - Task is a process * - Message node's name points to valid longword-aligned memory * - DOS packet (from name ptr) points back to message header * - Either current task is on the list of devices we're monitoring * or the destination message port's owner is on the list of * devices we're monitoring. * * If any of these conditions fail, we ignore the message. * */ ULONG ASM New_PutMsg(reg_a0 struct MsgPort *port, reg_a1 struct Message *msg, reg_a6 void *libbase) { MarkCallAddr; FP_PutMsg origfunc = (FP_PutMsg)PatchList[GID_SENDREXX].origfunc; Task *thistask = SysBase->ThisTask; /* * Save a little stack space and time by making these non-local */ #define rmsg ((struct RexxMsg *)msg) #define rexxclosevect (ULONG *)(((ULONG)RexxSysBase) - 10) #define thisclosevect (ULONG *)(((ULONG)rmsg->rm_LibBase) - 10) struct DosPacket *dp; Task *desttask; Event *ev; LONG seqnum; ULONG taskhandle; /* * Before doing anything else, check if we're being called from * within an interrupt. If so, get the heck out of here since * trying to obtain a semaphore from within an interrupt is * definitely a bad idea! * * To quickly check if we're within an interrupt, we simply see * if our stack pointer is within the range of the supervisor stack. * If it is, then we're probably in an interrupt, or at least in * Supervisor mode, and either way we don't want to continue. * * We also ignore ramlib, for the usual reasons, and our own * background task (since it is used to expand locks from within * of this patch and an infinite loop would be easy!) */ if (thistask == RamLibTask || thistask == (Task *)BackgroundProc || (getreg(REG_A7) >= (ULONG)SysBase->SysStkLower && getreg(REG_A7) <= (ULONG)SysBase->SysStkUpper)) { JumpOrigFunc(0); } /* * See if we have a valid ARexx message. See the function * header for an explanation of all the checks */ if (CurSettings.Func.Opts[GID_SENDREXX] && port->mp_Node.ln_Name && *port->mp_Node.ln_Name && rmsg->rm_CommAddr && TypeOfMem(rmsg->rm_CommAddr) && (rmsg->rm_Action & RXCODEMASK) == RXCOMM && RexxSysBase && TypeOfMem(thisclosevect) && ((ULONG)thisclosevect & 1) == 0 && *thisclosevect == *rexxclosevect) { /* * Okay, we know we've got a valid Rexx message so now enter * it into the event buffer. First, however, check that we * are within our stack bounds (since the normal check done * in the assembly patch code is bypassed for this function * to accomodate RAM returning DOS packets). */ if (getreg(REG_A7) >= (ULONG)thistask->tc_SPLower && getreg(REG_A7) <= (ULONG)thistask->tc_SPUpper && getreg(REG_A7) <= ((ULONG)thistask->tc_SPLower+CurSettings.StackLimit)) { JumpOrigFunc(0); } ev = CreateEvent(CallAddr, &seqnum, MSG_ACT_SENDREXX, rmsg->rm_Args[0], port->mp_Node.ln_Name, NO_EXPAND); if (!ev) return origfunc(port, msg, libbase); ev->status = ES_UPDATING; CheckForPause(ev, seqnum); ObtainSemaphore(&BufSem); if (seqnum >= RealFirstSeq) { ev->status = ES_READY; Signal(SnoopTask, NewEventMask); } ReleaseSemaphore(&BufSem); return origfunc(port, msg, libbase); } /* * Not an ARexx message, now check if it's a DOS packet. * First though, we must check to see if we're already in * the middle of a SnoopDos patch -- if so, we could end up * recursing forever unless we don't try and monitor this one. * * See above for the criteria we use to spot packet messages. */ taskhandle = CheckTaskRecursion(); if (!taskhandle) return origfunc(port, msg, libbase); dp = (struct DosPacket *)(msg->mn_Node.ln_Name); desttask = port->mp_SigTask; if ((ShowAllPackets | MonPackets) && thistask->tc_Node.ln_Type == NT_PROCESS && ((ULONG)dp & 1) == 0 && TypeOfMem(dp) && dp->dp_Link == msg && ((ULONG)(dp->dp_Port) & 3) == 0 && TypeOfMem(dp->dp_Port)) { /* * Looks like we're going to monitor the packet. * * First, check if the packet is already on our list of * outstanding packets. If so, we can handle it immediately. */ struct PacketRef *pt; WaitPacket *wp; Task **ptask; char *p = NULL; /* * Now some slightly tricky stuff. We can have up to TWO * entries on the waiting packet list which match the current * packet (one for the Packet Debugger and one for the * Monitor Packets option) and we need to acknowledge them * both. However, we also need to ensure we never try and * lock both the PacketSem and BufferSem at the same time * (since otherwise we could deadlock). * * This effectively means we have to restart our list scan * after each packet has been acknowledged, until we have * no more left -- a small speed hit, but actually not too * bad since most times, we will be the only packet on the * wait list anyway. (We use 'p' as a flag to indicate * whether or not we found a packet, so we know to return * at the end of it all.) */ ObtainSemaphore(&PacketSem); wp = HeadNode(&PacketWaitList); while (NextNode(wp)) { if (wp->dp == dp && wp->sendtask != thistask && wp->destport != port && wp->arg1 == dp->dp_Arg1 && wp->arg2 == dp->dp_Arg2 && wp->arg3 == dp->dp_Arg3) { /* * Found the packet on our list, so go ahead and fill in * the result code for the associated event (assuming it * hasn't scrolled off the buffer, of course!) */ Event *ev = wp->event; LONG seqnum = wp->eventnum; char *fmsg = wp->resmsg; char flags = wp->flags; Remove((Node *)wp); AddHead(&PacketFreeList, (Node *)wp); ReleaseSemaphore(&PacketSem); ObtainSemaphore(&BufSem); if (seqnum >= RealFirstSeq) { /* * Okay, now fill in the result string. The space for * this was allocated when the event was defined. * * For events that succeed, we can print "Okay", * "Okay ", and "Okay ". * * For events which fail, we can print * "Fail " or "Fail " */ UWORD fail; ULONG param; char *q, *r; #define evresmsg MSG(fail ? MSG_RES_FAIL : MSG_RES_OKAY) fail = ((flags & PKF_BOOL) && dp->dp_Res1 == 0) || ((flags & PKF_NEG) && dp->dp_Res1 == (ULONG)-1); #if NOT_WORRIED_ABOUT_STACK_SIZE /* * Unfortunately, we can't use this nice simple * code because calling mysprintf() uses too much * stack space if we're being called from certain * handlers with minimal stack space (e.g. RAM). * Instead, we must do the same effect ourselves. * This code is left here so you can see what the * original intention was. */ if ((flags & PK_MASK) == PK_2OK) { mysprintf(ev->result, fmsg, evresmsg, dp->dp_Res1, dp->dp_Res2); } else { mysprintf(ev->result, fmsg, evresmsg, (fail ? dp->dp_Res2 : dp->dp_Res1)); } p = (char *)-1; /* Non-zero means we did something */ #else /* Extremely worried about stack size */ /* * Here we roll our own sprintf() (inline!) to keep * stack usage to an absolute minimum. It's a rather * small subset and somewhat hacked, you'll notice. * * We make P non-null to indicate we did some work */ p = fmsg; q = ev->result; /* * First, skip over string formatter at start which * always holds either 'Ok' or 'Fail' */ strcpy(q, evresmsg); q += strlen(q); while (q < (ev->result + 4)) *q++ = ' '; while (*p && *p != 's') p++; if (*p) p++; /* * Now parse remainder of string. The first parameter * encountered gets replaced with either dp_Res1 or * dp_Res2, depending on circumstances. The second * parameter always gets replaced with dp_Res2. */ if ((flags & PK_MASK) == PK_2OK || !fail) param = dp->dp_Res1; else param = dp->dp_Res2; while (*p) { if (*p != '%') { *q++ = *p++; continue; } while (*p && *p != 'x' && *p != 'X') p++; p++; if (param == 0) { strcpy(q, " 0"); } else if (param == (ULONG)-1) { strcpy(q, " -1"); } else { /* * Expand param to 8 hex digits, right-aligned */ r = q + 7; while (param > 0) { *r-- = "0123456789ABCDEF"[param & 15]; param >>= 4; } while (r >= q) *r-- = ' '; } q += 8; param = dp->dp_Res2; /* Second parm always dp_Res2 */ } *q = '\0'; #endif /* * Unlike most times, we don't signal SnoopDos that * an event has been updated (yet) since there may * be another event to update too, and it's best * to do a single signal at the end (fewer context * switches are required.) So, we just mark the * event as ready. */ ev->status = ES_READY; } ReleaseSemaphore(&BufSem); ObtainSemaphore(&PacketSem); /* * Now restart the search from the beginning of the * list (since wp is no longer valid) */ wp = HeadNode(&PacketWaitList); } else { /* * Just advance to next element on list */ wp = NextNode(wp); } } ReleaseSemaphore(&PacketSem); if (p) { /* * Found a matching packet, so wake up the main task * and finish up. (We only signal the main snoop task * now, so that both events get printed simultaneously, * rather than one after another, which would be a little * messy). This also avoids us getting pre-empted inside * our loop above if SnoopDos is running at a higher priority * than this task, which is convenient (though not essential). */ Signal(SnoopTask, NewEventMask); goto done_putmsg; } /* * If we didn't find a match on our list of waiting packets, * then check to see if we're ignoring ROM calls. If so, * and if we were called from ROM, then immediately abort * (this would be done later anyway, but by checking now, * we save quite a bit of time.) * * (We can't do this checking any earlier, or we'd miss * returned packets from ROM filesystems like RAM: and FFS). * * We also exit if we're under the stack limit. (We can't * check this earlier because otherwise we'd miss the * return packet from RAM which only has a tiny stack.) */ if (((!MonROMCalls && CallAddr >= RomStart && CallAddr <= RomEnd)) || (getreg(REG_A7) >= (ULONG)thistask->tc_SPLower && getreg(REG_A7) <= (ULONG)thistask->tc_SPUpper && getreg(REG_A7) <= ((ULONG)thistask->tc_SPLower+CurSettings.StackLimit))) { goto done_putmsg; } /* * Okay, didn't match the packet with a previous outgoing one. * Now search the device list to see if we can match either * the owner of the port (a new outgoing packet) or the task ID * of the sender (a returning packet). * * In the case of a returning packet, we just ignore it since * we missed it on the way out. */ for (ptask = DeviceTaskList; *ptask && *ptask != desttask; ptask++) { if (*ptask == thistask) goto done_putmsg; /* Sender was a DOS device so ignore */ } if (!*ptask) { /* * Couldn't find dest task on device list so it must * be a message to somewhere else -- hence, ignore it. */ goto done_putmsg; } /* * Okay, got ourselves a bona fida packet. Next, look it up * in our packet table to decide what to do with it. * Unrecognised packets come out as LAST_PACK_MSG which is * specifically initialised to handle them properly. */ for (pt = &PacketTable[0]; pt->msgid != LAST_PACK_MSG; pt++) if (pt->packetid == dp->dp_Type) break; if (pt->flags & PK_IGNORE) /* Certain packet types are ignored */ goto done_putmsg; /* * Okay, we finally (finally!) get to create a new entry * in our event buffer. Actually, we get to create two * entries, since if we have both the Packet Debugger * AND the Monitor Packets option turned on, we want to * see the output from both of them (even if they're both * the same event!). * * Why? Well, if you're debugging a device driver and you * have Open packets etc. coming back and forth, then * as well as the raw packet data, it's also kind of nice * to see actual filenames etc. in english instead of hex. */ if (MonPackets && (pt->flags & PK_COMMON)) HandleSimplePacket(CallAddr, dp, port, pt); if (ShowAllPackets) HandleRawPacket(CallAddr, dp, port, pt); } done_putmsg: EndTaskRecursion(taskhandle); return origfunc(port, msg, libbase); }