home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
AmigActive 19
/
AACD19.BIN
/
AACD
/
System
/
SnoopDos
/
SnoopDos_Src
/
SnoopDos_Source
/
patches.c
< prev
next >
Wrap
C/C++ Source or Header
|
2001-02-04
|
213KB
|
4,936 lines
/*
* PATCHES.C vi:ts=4
*
* Copyright (c) Eddy Carroll, September 1994.
* Updated by Thomas Richter, THOR, 5.3.2000.
*
* 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);
if (AttemptSemaphoreHeavely(&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! */
if (AttemptSemaphoreHeavely(&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, "<Unknown>");
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)
*
* THOR: RPort->Layer is set to NULL by some rendering calls
* and hence the code might find a NULL here. Handle this as if
* the layer is locked.
*/
/*
*
*
Forbid();
if (MainWindow &&
(MainWindow->RPort->Layer == NULL ||
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;
/*
* THOR: Check whether the layer is available. Some Gfx rendering
* calls clear the layer temporarely.
* In that case, do not proceed since we do not really
* know who owns the layer.
*/
if (layer) {
gotlock = (li->Lock.ss_Owner == thistask) ||
(layer->Lock.ss_Owner == thistask);
} else gotlock = TRUE;
}
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) {
if (AttemptSemaphoreHeavely(&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) {
if (AttemptSemaphoreHeavely(&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);
}
if (AttemptSemaphoreHeavely(&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);
}
if (AttemptSemaphoreHeavely(&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);
}
if (AttemptSemaphoreHeavely(&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);
}
if (AttemptSemaphoreHeavely(&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);
}
if (AttemptSemaphoreHeavely(&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);
}
if (AttemptSemaphoreHeavely(&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);
}
/*
* THOR: Do not monitor input.device OpenFont().
* This is only annoying...
*/
if (SysBase->ThisTask == InputTask) {
ev = NULL;
} else {
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);
}
if (AttemptSemaphoreHeavely(&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);
}
if (AttemptSemaphoreHeavely(&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);
}
if (AttemptSemaphoreHeavely(&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);
}
if (AttemptSemaphoreHeavely(&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);
}
if (AttemptSemaphoreHeavely(&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);
if (AttemptSemaphoreHeavely(&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);
}
if (AttemptSemaphoreHeavely(&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);
}
if (AttemptSemaphoreHeavely(&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);
}
if (AttemptSemaphoreHeavely(&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);
}
if (AttemptSemaphoreHeavely(&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);
}
if (AttemptSemaphoreHeavely(&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);
}
if (AttemptSemaphoreHeavely(&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_d2 BPTR hunk, reg_d3 BPTR file,reg_a6 void *libbase)
{
MarkCallAddr;
FP_LoadSeg origfunc = (FP_LoadSeg)PatchList[GID_LOADSEG].origfunc;
int onlyfails = OnlyShowFails;
Event *ev;
ULONG result;
LONG seqnum;
/*
* THOR: Fix overlay loading here.
*/
if (name == NULL) {
return origfunc(NULL,hunk,file,libbase);
}
if (onlyfails) {
result = origfunc(name, NULL, NULL, 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, NULL, NULL, libbase);
LoadTask = 0;
#else
result = origfunc(name, NULL, NULL, libbase);
#endif
}
if (AttemptSemaphoreHeavely(&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);
}
if (AttemptSemaphoreHeavely(&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);
}
if (AttemptSemaphoreHeavely(&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);
}
if (AttemptSemaphoreHeavely(&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 --> <dest>" 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);
}
if (AttemptSemaphoreHeavely(&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);
}
if (AttemptSemaphoreHeavely(&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);
}
if (AttemptSemaphoreHeavely(&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);
}
if (AttemptSemaphoreHeavely(&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);
}
if (AttemptSemaphoreHeavely(&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) {
if (AttemptSemaphoreHeavely(&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 --> <dest>" 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 &&
(!TypeOfMem(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);
if (AttemptSemaphoreHeavely(&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 <result>", and "Okay <result1> <result2>".
*
* For events which fail, we can print
* "Fail <result2>" or "Fail <result1> <result2>"
*/
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);
}
/*
* AttemptSemaphoreHeavely
*
* Try to get a semaphore, try by waiting a certain amount of time,
* but give up if the semaphore is really not free.
*/
LONG AttemptSemaphoreHeavely(struct SignalSemaphore *ss)
{
LONG rnd;
struct MsgPort myport;
struct timerequest treq;
if (AttemptSemaphore(ss))
return TRUE;
/*
* rand is not multi-threadable....
*/
Forbid();
rnd = rand();
Permit();
/*
* Now initialize the port and the timer for waiting...
*/
myport.mp_Node.ln_Type = NT_MSGPORT;
myport.mp_Node.ln_Pri = 0;
myport.mp_Flags = PA_SIGNAL;
myport.mp_SigBit = SIGB_SINGLE;
myport.mp_SigTask = SysBase->ThisTask;
NewList(&myport.mp_MsgList);
treq = *TimerRequest;
treq.tr_node.io_Message.mn_ReplyPort = &myport;
rnd >>= 27; /* at most about two seconds... */
rnd++;
do {
treq.tr_node.io_Command = TR_ADDREQUEST;
treq.tr_time.tv_secs = 0;
treq.tr_time.tv_micro = 100000; /* one tenth of a second */
DoIO(&treq);
if (AttemptSemaphore(ss))
return TRUE;
rnd--;
} while(rnd);
return FALSE;
}