home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Fish 'n' More 2
/
fishmore-publicdomainlibraryvol.ii1991xetec.iso
/
disks
/
disk388.lzh
/
SnoopDos
/
snoopdos.c
< prev
next >
Wrap
C/C++ Source or Header
|
1990-10-23
|
32KB
|
1,147 lines
/*
* SNOOPDOS.C
*
* (C) Copyright Eddy Carroll, May 1990. Freely distributable.
*
* Snoopdos patches into dos.library and outputs a message to a
* debugging window or file whenever a process calls certain DOS
* functions.
*
* Type snoopdos -h for a list of available options. See the
* documentation for further details.
*
* Compiles under Lattice C V5.04. I use flags: -cusq -j88i -ms -v
*/
#ifndef LATTICE_50
#include "system.h"
#endif
/*
* Assorted strings
*/
#define TITLE \
"SnoopDos V1.0 (C) Copyright Eddy Carroll, Sept 1990. Freely distributable."
char *HEADER =
"Process name Func Filename Mode Res."
"\r\n"
"------------ ---- -------- ---- ----"
"\r\n";
#define PORTNAME "SnoopDos Port"
#define DEFWINDOW "CON:0/0/640/120/"
/*
* The following message array contains both colour and non-colour
* versions of the various short message strings displayed.
*/
char *msgs[][2] = {
/* Monochrome Colour */
/* ---------- ------ */
"OLD ", "OLD ",
"NEW ", "\033[33mNEW\033[0m ",
"R/W ", "\033[32mR/W\033[0m ",
"??? ", "??? ",
"SHAR", "SHAR",
"EXCL", "\033[33mEXCL\033[0m",
"????", "????",
"Okay\r\n", "Okay\r\n",
"Fail\r\n", "\033[33mFail\033[0m\r\n",
">", "\033[33m>\033[0m",
"> (Done)", "> (Done)",
"Warning: Missed", "\033[33mWarning:\033[0m Missed",
">>>>\r\n", ">>>>\r\n",
"Open", "Open",
"Lock", "\033[33mLock\033[0m",
"Load", "\033[32mLoad\033[0m",
"Exec", "\033[32mExec\033[0m",
"CD ", "CD ",
"Del ", "\033[33mDel\033[0m "
};
#define TXT_OLD msgs[ 0][colour]
#define TXT_NEW msgs[ 1][colour]
#define TXT_R_W msgs[ 2][colour]
#define TXT_QM3 msgs[ 3][colour]
#define TXT_SHAR msgs[ 4][colour]
#define TXT_EXCL msgs[ 5][colour]
#define TXT_QM4 msgs[ 6][colour]
#define TXT_OKAY msgs[ 7][colour]
#define TXT_FAIL msgs[ 8][colour]
#define TXT_POINT msgs[ 9][colour]
#define TXT_DONE msgs[10][colour]
#define TXT_WARN msgs[11][colour]
#define TXT_NEST msgs[12][colour]
#define TXT_OPEN msgs[13][colour]
#define TXT_LOCK msgs[14][colour]
#define TXT_LOAD msgs[15][colour]
#define TXT_EXEC msgs[16][colour]
#define TXT_CURDIR msgs[17][colour]
#define TXT_DELETE msgs[18][colour]
#define POINT(x) ((x) ? TXT_POINT : " ")
/*
* Now some standard system-type macros
*/
#define reg_d0 register __d0
#define reg_d1 register __d1
#define reg_d2 register __d2
#define reg_d3 register __d3
#define reg_a0 register __a0
#define BTOC(x) (void *)(((ULONG)x) << 2)
#define D_S(name, type) char c_##name[sizeof(type)+3];\
type *name = (type *)((long)(c_##name+3) & ~3)
extern __asm BPTR CallOpen(reg_d1 UBYTE *, reg_d2 int);
extern __asm BPTR CallLock(reg_d1 UBYTE *, reg_d2 int);
extern __asm BPTR CallLoadSeg(reg_d1 UBYTE *);
extern __asm LONG CallExecute(reg_d1 UBYTE *, reg_d2 BPTR, reg_d3 BPTR);
extern __asm BPTR CallCurrentDir(reg_d1 BPTR);
extern __asm LONG CallDeleteFile(reg_d1 UBYTE *);
/*
* Structure used to pass messages back and fro
*/
typedef struct {
struct Message msg; /* Standard message header */
struct Process *process; /* Sending process id */
int msgtype; /* Message type, see below */
int data1; /* Data field 1 */
void *data2; /* Data field 2 */
} MYMSG;
/*
* Now the various settings that can be set to affect the monitoring
*/
typedef struct {
int set_doopen; /* If true, monitor Open() */
int set_dolock; /* If true, monitor Lock() */
int set_doloadseg; /* If true, monitor LoadSeg() */
int set_doexecute; /* If true, monitor Execute() */
int set_docurdir; /* If true, monitor CurrentDir() */
int set_dodelete; /* If true, monitor DeleteFile() */
int set_showfullpath; /* If true, display full paths */
int set_sleepwait; /* If true, sleep if necessary */
int set_snoopactive; /* If true, monitoring is active */
int set_colour; /* If true, use ANSI colour codes */
} SETTINGS;
/*
* Default settings
*/
SETTINGS settings = { 1, 0, 1, 1, 1, 1, 0, 0, 1, 1 };
/*
* These defines allow the various settings to be accessed as
* normal variables rather than structure members; this is purely
* for convenience.
*/
#define doopen settings.set_doopen
#define dolock settings.set_dolock
#define doloadseg settings.set_doloadseg
#define doexecute settings.set_doexecute
#define docurdir settings.set_docurdir
#define dodelete settings.set_dodelete
#define showfullpath settings.set_showfullpath
#define sleepwait settings.set_sleepwait
#define snoopactive settings.set_snoopactive
#define colour settings.set_colour
/*
* Now the various message types that can be sent
*/
typedef enum {
MSG_QUIT, /* Quit */
MSG_GETOPTIONS, /* Read options */
MSG_SETOPTIONS, /* Update options */
MSG_OPEN, /* Open file */
MSG_OPEN_DONE, /* Open file completed */
MSG_LOCK, /* Lock file */
MSG_LOCK_DONE, /* Lock file completed */
MSG_LOADSEG, /* LoadSeg file */
MSG_LOADSEG_DONE, /* LoadSeg file completed */
MSG_EXECUTE, /* Execute command */
MSG_CURDIR, /* CurrentDir */
MSG_DELETE, /* DeleteFile */
MSG_DELETE_DONE, /* DeleteFile completed */
} MSGTYPES;
struct SignalSemaphore sem[1];
struct MsgPort *myport; /* Pointer to background SnoopDos msg port */
struct MsgPort *inport; /* Pointer to our own message port */
struct Task *snooptask; /* Pointer to our own task */
BPTR debugwin; /* Output file or window */
BPTR stderr; /* Standard CLI console */
int extfile; /* True if output directed to external file */
char extfilename[100]; /* Name of window/file to open if specified */
char outbuf[800]; /* Output buffer used by myprintf() etc. */
int missed_open_sig; /* Signal to indicate Open() missed */
int missed_lock_sig; /* Signal to indicate Lock() missed */
int missed_loadseg_sig; /* Signal to indicate LoadSeg() missed */
int missed_execute_sig; /* Signal to indicate Execute() missed */
int missed_curdir_sig; /* Signal to indicate CurrentDir() missed */
int missed_delete_sig; /* Signal to indicate DeleteFile() missed */
int portsig; /* Signal used by our public port */
/**************************** Start of Functions ****************************/
/*
* cleanup()
* ---------
* Cleans up all active resources and exits. If err is -1, then
* returns instead of exiting.
*/
void cleanup(int err)
{
static int called = 0;
if (called++) /* Make sure not called twice by accident */
return;
if (inport)
DeletePort(inport);
if (debugwin)
Close(debugwin);
if (stderr)
Close(stderr);
if (err != -1)
exit(err);
}
/*
* myprintf(), myfprintf()
* -----------------------
* Two low cost alternatives to printf that go directly to AmigaDOS
* Note we deliberately use the pre-ANSI declaration, to avoid Lattice
* kicking up a fuss about the wrong number of parameters.
*/
void myprintf(format, p1, p2, p3, p4, p5, p6)
UBYTE *format;
ULONG p1, p2, p3, p4, p5, p6;
{
sprintf(outbuf, format, p1, p2, p3, p4, p5, p6);
Write(Output(), outbuf, strlen(outbuf));
}
void myfprintf(file, format, p1, p2, p3, p4, p5, p6)
BPTR file;
char *format;
ULONG p1, p2, p3, p4, p5, p6;
{
sprintf(outbuf, format, p1, p2, p3, p4, p5, p6);
Write(file, outbuf, strlen(outbuf));
}
#define printf myprintf
#define fprintf myfprintf
/*
* sendmsg()
* ---------
* Sends a message to myport, and waits for a reply to arrive.
* A message type and some message data are all that need be provided
* by the caller; the callee will provide the rest. Doesn't return
* until a reply has been received.
*/
void sendmsg(int type, int data1, void *data2)
{
MYMSG msg;
struct Process *me = (struct Process *)FindTask(0);
struct MsgPort *replyport = &me->pr_MsgPort;
msg.msg.mn_Node.ln_Type = NT_MESSAGE;
msg.msg.mn_Length = sizeof(MYMSG);
msg.msg.mn_ReplyPort = replyport;
msg.process = me;
msg.msgtype = type;
msg.data1 = data1;
msg.data2 = data2;
PutMsg(myport, &msg);
WaitPort(replyport);
GetMsg(replyport);
}
/*
* getlockpath()
* -------------
* Returns a pointer to a string containing the full path for the
* specified lock. You must copy the string before calling this
* routine again.
*/
char *getlockpath(BPTR lock)
{
struct Process *p = (struct Process *)FindTask(0L);
APTR oldwin = p->pr_WindowPtr;
static char path[300];
int pos = 299;
BPTR mylock;
char *name;
D_S(fib, struct FileInfoBlock);
int err = 0;
p->pr_WindowPtr = (APTR)-1; /* Disable error requesters */
mylock = DupLock(lock);
path[pos] = '\0';
do {
int len;
BPTR newlock;
if (!Examine(mylock, fib))
err++;
name = fib->fib_FileName;
if (*name == '\0')
name = "RAM"; /* Workaround for old RAM: disk bug */
len = strlen(name);
pos = pos - len - 1;
newlock = ParentDir(mylock);
UnLock(mylock);
mylock = newlock;
strncpy(path + pos, name, len);
if (mylock)
path[pos + len] = '/';
else
path[pos + len] = ':';
} while (mylock);
p->pr_WindowPtr = oldwin; /* Enable error requesters again */
if (err) {
/*
* Volume not present so have to be happy with just
* returning the volume node instead.
*/
struct FileLock *fl = BTOC(lock);
struct DeviceList *dl = BTOC(fl->fl_Volume);
UBYTE *name = BTOC(dl->dl_Name);
strncpy(path, name + 1, *name);
path[*name] = '\0';
strcat(path, ":.../");
return (path);
} else
return (path + pos);
}
/*
* makepath()
* ----------
* Builds a full path string given a process ptr and a filename.
* If the filename includes relative references (// or :)
* these are handled also. The point of this is to resolve relative
* directory references.
*
* Returns TRUE if a new string was generated, FALSE if the existing
* string didn't depend on the current directory (this doesn't affect
* the output stored in buf though).
*/
int makepath(char *buf, BPTR curdir, char *filename)
{
char *origfilename = filename;
int pos;
int doneroot = 0;
/*
* Special check for the 'current process console' file '*'
*/
if (strcmp(filename, "*") == 0) {
strcpy(buf, filename);
return (FALSE);
}
strcpy(buf, getlockpath(curdir));
pos = strlen(buf);
for (;;) {
if (!doneroot && *filename == ':') {
/*
* Path is relative to root
*/
doneroot = 1;
for (pos = 0; buf[pos] && buf[pos] != ':'; pos++)
;
if (buf[pos] == ':')
pos++;
} else if (*filename == '/') {
/*
* Path is relative to parent directory; if none, then
* remains the same.
*/
int newpos = pos - 2;
while (newpos >= 0 && buf[newpos] != '/' && buf[newpos] != ':')
newpos--;
if (newpos >= 0)
pos = newpos + 1;
} else {
/*
* No more special characters; just append what's left of
* the filename.
*/
if (!doneroot) {
/*
* If the filename wasn't relative to the root
* directory, then make sure there are no device/volume
* references contained within it. We copy the original
* filename rather than the currently modified version
* since a volume name of /A: is legal and the / would
* be stripped off the modified name.
*/
char *p;
for (p = filename; *p; p++) {
if (*p == ':') {
strcpy(buf, origfilename);
return (0);
}
}
}
strcpy(buf + pos, filename);
return (1);
}
filename++;
}
}
/*
* OutputLine()
* ------------
* Outputs a line of text in the main window, according to the
* truncation mechanism used.
*/
/*
* mainloop()
* ----------
* This is the main event loop for SnoopDOS, where everything happens.
* Control is passed here when SnoopDOS first starts up. When this
* function returns, it terminates the background process.
*/
void mainloop(void)
{
static char fullname[300];
int done = 0; /* True if finished processing */
int col0 = 1; /* True if cursor in column 0 */
int nested = 0; /* True if nested DOS calls */
#define MISSED_NONE 0 /* Haven't missed anything */
#define MISSED_OPEN (1 << 0) /* Missed Open() call */
#define MISSED_LOCK (1 << 1) /* Missed Lock() call */
#define MISSED_LOADSEG (1 << 2) /* Missed LoadSeg() call */
#define MISSED_EXECUTE (1 << 3) /* Missed Execute() call */
#define MISSED_CURDIR (1 << 4) /* Missed CurrentDir() call */
#define MISSED_DELETE (1 << 5) /* Missed DeleteFile() call */
int missed = MISSED_NONE; /* Was a DOS function missed? See above */
inport = CreatePort(PORTNAME, 0);
if (!inport) {
fprintf(stderr, "SnoopDos: Can't allocate message port.\n");
cleanup(-1);
return;
}
myport = inport;
portsig = 1 << myport->mp_SigBit;
/*
* Allocate signals
*/
missed_open_sig = 1 << AllocSignal(-1);
missed_lock_sig = 1 << AllocSignal(-1);
missed_loadseg_sig = 1 << AllocSignal(-1);
missed_execute_sig = 1 << AllocSignal(-1);
missed_curdir_sig = 1 << AllocSignal(-1);
missed_delete_sig = 1 << AllocSignal(-1);
if ( missed_open_sig == -1 || missed_lock_sig == -1 ||
missed_loadseg_sig == -1 || missed_execute_sig == -1 ||
missed_curdir_sig == -1 || missed_delete_sig == -1) {
fprintf(stderr, "SnoopDos: Can't allocate enough signals.\n");
cleanup(-1);
return;
}
if (extfile) {
debugwin = Open(extfilename, MODE_NEWFILE);
if (!debugwin) {
fprintf(stderr, "SnoopDos: Can't open file %s for output.\n",
extfilename);
cleanup(-1);
return;
}
fprintf(debugwin, "\r\n" TITLE "\r\n\r\n");
} else {
debugwin = Open(DEFWINDOW TITLE, MODE_NEWFILE);
if (!debugwin) {
fprintf(stderr, "SnoopDos: Can't open display window.\n");
cleanup(-1);
return;
}
}
Close(stderr); stderr = NULL;
if (!extfile) {
fprintf(debugwin, "\n"
"Type CTRL-E/CTRL-D to enable/disable snooping. Type CTRL-C to exit.\n\n");
};
fprintf(debugwin, HEADER);
InitSemaphore(sem);
snooptask = FindTask(0L);
installdospatch();
/*
* Now just sit processing messages
*/
while (!done) {
int mask;
#define SIGMASK (portsig | missed_open_sig | missed_lock_sig | \
missed_loadseg_sig | missed_execute_sig | \
missed_curdir_sig | missed_delete_sig | \
SIGBREAKF_CTRL_C | SIGBREAKF_CTRL_D | SIGBREAKF_CTRL_E)
mask = Wait(SIGMASK);
if (mask & SIGBREAKF_CTRL_C)
done = 1;
if (mask & SIGBREAKF_CTRL_D) {
if (snoopactive) {
fprintf(debugwin,
"\nSnooping now disabled; type CTRL-E to enable it again.\r\n\r\n");
snoopactive = 0;
}
}
if (mask & SIGBREAKF_CTRL_E) {
if (!snoopactive) {
fprintf(debugwin,
"Snooping now enabled; type CTRL-D to disable it again.\r\n\r\n%s", HEADER);
snoopactive = 1;
}
}
if (mask & missed_open_sig)
missed |= MISSED_OPEN;
if (mask & missed_lock_sig)
missed |= MISSED_LOCK;
if (mask & missed_loadseg_sig)
missed |= MISSED_LOADSEG;
if (mask & missed_execute_sig)
missed |= MISSED_EXECUTE;
if (mask & missed_curdir_sig)
missed |= MISSED_CURDIR;
if (mask & missed_delete_sig)
missed |= MISSED_DELETE;
if (mask & portsig) {
MYMSG *msg;
/*
* Note in the following, there is some slightly tricky
* stuff to handle the case where a DOS function calls
* another DOS function internally. Under 1.3, this doesn't
* happen, but it may under WB 2.0 and in addition, Jim
* Goodnow's handy Rez utility patches LoadSeg() so that
* it calls Open(). `nested' is true when a nested call
* like this happens, and in this case, process names are
* printed out prefixed by `> ' to indicate the nesting.
*
* The nesting is detected because an incoming Open/Lock/
* LoadSeg message will arrive when the cursor is positioned
* to print a Fail/Okay result from a previous call (i.e.
* col0 is false; the cursor is not in column 0). When a
* result arrives in and the cursor IS in column 0, then
* this must be the result from an earlier call so the
* nesting is ended. Note that the semaphores used ensures
* that this situation can only ever occur as a result of
* nesting; other tasks will always wait until they are
* properly in sync before sending messages to the main
* Snoopdos process.
*
* The more alert among you are probably saying something
* like "Ah, but what if SnoopDos is configured to display
* all DOS calls, even if it means forcing some tasks to
* sleep until SnoopDos can process them? Then, when a
* task calls LoadSeg() (say) and LoadSeg() calls Open(),
* Open() will sleep forever waiting for the semaphore
* Obtain()ed by LoadSeg() to become free again." Well,
* in fact, it won't, since exec's ObtainSemaphore() call
* will suceed even when the semaphore is in use, IF the
* caller is the task that owns the semaphore -- and in such
* a case, the caller will be the same task. (It took me a
* while to figure out exactly what things were working --
* it looked like I should get a serious lockup in certain
* circumstances).
*
*/
while ((msg = (MYMSG *)GetMsg(myport)) != NULL) {
/*
* Get the name of the process/command for
* printing out if necessary.
*/
static char namebuf[256];
UBYTE *procname = msg->process->pr_Task.tc_Node.ln_Name;
struct CommandLineInterface *cli = BTOC(msg->process->pr_CLI);
BPTR curdir = msg->process->pr_CurrentDir;
UBYTE *s;
UBYTE *filename;
int newname; /* If true, a new name was generated */
if (!col0 && (msg->msgtype == MSG_OPEN ||
msg->msgtype == MSG_LOCK ||
msg->msgtype == MSG_LOADSEG ||
msg->msgtype == MSG_CURDIR ||
msg->msgtype == MSG_DELETE)) {
nested = 1;
fprintf(debugwin, TXT_NEST);
}
if (cli && cli->cli_Module) {
UBYTE *cliname = BTOC(cli->cli_CommandName);
if (*cliname > 0) {
/* Only use CLI name if it's not null */
strncpy(namebuf+2, cliname + 1, *cliname);
namebuf[*cliname+2] = '\0';
procname = namebuf + 2;
if (nested) {
/*
* If we're not at column 0, then we're
* calling this DOS function from within
* another DOS function so indent display
*/
procname = namebuf;
procname[0] = '>';
procname[1] = ' ';
}
}
} else {
if (nested) {
sprintf(namebuf, "> %s", procname);
procname = namebuf;
}
}
/*
* Now handle the message
*/
filename = msg->data2; /* Standard file name */
newname = 0; /* No problems expanding it */
switch (msg->msgtype) {
case MSG_QUIT:
done = 1;
break;
case MSG_GETOPTIONS:
memcpy(msg->data2, &settings, sizeof(SETTINGS));
break;
case MSG_SETOPTIONS:
memcpy(&settings, msg->data2, sizeof(SETTINGS));
break;
case MSG_OPEN:
if (msg->data1 == MODE_OLDFILE)
s = TXT_OLD;
else if (msg->data1 == MODE_NEWFILE)
s = TXT_NEW;
else if (msg->data1 == MODE_READWRITE)
s = TXT_R_W;
else
s = TXT_QM3;
if (showfullpath) {
newname = makepath(fullname, curdir, msg->data2);
filename = fullname;
}
fprintf(debugwin, "%-21s %s %s%-39s %s ", procname,
TXT_OPEN, POINT(newname), filename, s);
col0 = 0;
break;
case MSG_LOCK:
if (msg->data1 == ACCESS_READ)
s = TXT_SHAR;
else if (msg->data1 == ACCESS_WRITE)
s = TXT_EXCL;
else
s = TXT_QM4;
if (showfullpath) {
newname = makepath(fullname, curdir, msg->data2);
filename = fullname;
}
fprintf(debugwin, "%-21s %s %s%-39s %s ", procname,
TXT_LOCK, POINT(newname), filename, s);
col0 = 0;
break;
case MSG_LOADSEG:
if (showfullpath) {
newname = makepath(fullname, curdir, msg->data2);
filename = fullname;
}
fprintf(debugwin, "%-21s %s %s%-44s ", procname,
TXT_LOAD, POINT(newname), filename);
col0 = 0;
break;
case MSG_EXECUTE:
fprintf(debugwin, "%-21s %s %s\r\n", procname,
TXT_EXEC, filename);
col0 = 1;
break;
case MSG_CURDIR:
{
char *dirname = getlockpath(msg->data1);
int i = strlen(dirname);
if (dirname[i-1] == '/')
dirname[i-1] = '\0';
fprintf(debugwin, "%-21s %s %s\r\n", procname,
TXT_CURDIR, dirname);
col0 = 1;
}
break;
case MSG_DELETE:
if (showfullpath) {
newname = makepath(fullname, curdir, msg->data2);
filename = fullname;
}
fprintf(debugwin, "%-21s %s %s%-44s ", procname,
TXT_DELETE, POINT(newname), filename);
col0 = 0;
break;
case MSG_LOCK_DONE:
case MSG_OPEN_DONE:
case MSG_LOADSEG_DONE:
case MSG_DELETE_DONE:
if (col0 && nested) {
fprintf(debugwin, "%-73s", TXT_DONE);
nested = 0;
}
if (msg->data1)
fprintf(debugwin, TXT_OKAY);
else
fprintf(debugwin, TXT_FAIL);
col0 = 1;
break;
}
ReplyMsg(msg);
}
}
/*
* Finally, check if we missed a DOS function. If we did,
* AND we are in column 0, print out an appropriate message.
* Otherwise, wait until next time. This stops the display
* getting messed up when waiting to print a 'Fail' or 'Okay'.
*/
if (missed) {
if (col0) {
if (missed & MISSED_OPEN)
fprintf(debugwin, "%s an Open()\r\n", TXT_WARN);
if (missed & MISSED_LOCK)
fprintf(debugwin, "%s a Lock()\r\n", TXT_WARN);
if (missed & MISSED_LOADSEG)
fprintf(debugwin, "%s a LoadSeg()\r\n", TXT_WARN);
if (missed & MISSED_EXECUTE)
fprintf(debugwin, "%s an Execute()\r\n", TXT_WARN);
if (missed & MISSED_CURDIR)
fprintf(debugwin, "%s a CurrentDir()\r\n", TXT_WARN);
if (missed & MISSED_DELETE)
fprintf(debugwin, "%s a DeleteFile()\r\n", TXT_WARN);
missed = MISSED_NONE;
}
}
}
/*
* Remove the port from the public ports list; this stops any
* SnoopDos processes started up elsewhere from trying to
* communicate with us (which may happen if this process can't
* exit immediately).
*/
RemPort(myport);
myport->mp_Node.ln_Name = NULL;
/*
* Now remove the dospatch, reply to all messages currently
* pending and cleanup.
*/
if (!uninstalldospatch()) {
/*
* Someone else has patched DOSbase so print a message for
* the user and keep trying every 5 seconds until we succeed.
*/
int count = 0;
fprintf(debugwin,
"\r\n"
"Someone else has patched dos.library so I'll have to wait until they quit."
"\r\n");
if (!extfile) {
fprintf(debugwin,
"This window will close shortly though to stop it from annoying you.\r\n");
}
do {
Delay(250); /* Wait 5 seconds */
if (debugwin) {
count++;
if (count > 2) {
Close(debugwin);
debugwin = NULL;
}
}
} while (!uninstalldospatch());
}
if (debugwin)
fprintf(debugwin, "\r\nSnoopDos terminated.\r\n");
/*
* To make sure there is no code still executing in our task
* before we exit, we allocate the semaphore used to ensure
* exclusive access to the code, then wait a small time to allow
* the other task a chance to execute the final instruction or two
* left in our code after it releases the semaphore. This is not
* foolproof :-( but it's better than nothing.
*/
{
MYMSG *msg;
while (msg = (MYMSG *)GetMsg(myport))
ReplyMsg(msg);
}
ObtainSemaphore(sem);
Delay(10); /* Wait 0.2 seconds */
cleanup(-1);
}
/*
* main()
* ------
* Mainline. Parses the command line and, if necessary, kicks off the
* background task to do the real work.
*/
void main(int argc, char **argv)
{
int error = 0; /* True if error detected in cmd line */
int quit = 0; /* True if we want to quit */
int colsel = 0; /* True if colour option was specified */
/*
* See if we are already active elsewhere; if yes, then read in
* the default settings used by that process.
*/
myport = FindPort(PORTNAME);
if (myport)
sendmsg(MSG_GETOPTIONS, 0, &settings);
while (argc > 1 && argv[1][0] == '-') {
int val;
/* Make -X == -x0 and -x == -x1 */
if (argv[1][1] >= 'a' && argv[1][2] != '0')
val = 1;
else
val = 0;
switch (tolower(argv[1][1])) {
case 'a': colour = val;
colsel = 1; break; /* Display ANSI colour */
case 'c': docurdir = val; break; /* Monitor CurrentDir() */
case 'd': dodelete = val; break; /* Monitor DeleteFile() */
case 'f': showfullpath = val; break; /* Show full filepath */
case 'g': doloadseg = val; break; /* Monitor LoadSeg() */
case 'h': error = 1; break; /* Just print help msg */
case 'l': dolock = val; break; /* Monitor Lock() */
case 'm': snoopactive = val; break; /* Actively monitoring */
case 'o': doopen = val; break; /* Monitor Open() */
case 'q': quit = 1; break; /* Ask SnoopDos to quit */
case 'r': error = 2; break; /* Report settings */
case 'w': sleepwait = val; break; /* Wait when necessary */
case 'x': doexecute = val; break; /* Monitor Execute() */
case 'z':
if (argv[1][2]) {
strcpy(extfilename, &argv[1][2]);
extfile = 1;
} else {
argv++;
argc--;
if (argc > 1) {
strcpy(extfile, argv[1]);
extfile = 1;
}
}
if (!extfile) {
printf(
"-z must be followed by a filename. Type SnoopDos -h for help.\n");
exit(5);
}
/*
* If outputting to a file, make colour off
* by default, rather than on.
*/
if (colsel == 0)
colour = 0;
break;
default:
printf("Unrecognised option %s. Type Snoopdos -h for help.\n",
argv[1]);
exit(5);
}
argv++; argc--;
}
if (argc > 1)
error = 1;
if (error) {
if (error == 1) {
printf(TITLE "\n\n"
"SnoopDos monitors calls made to various AmigaDOS functions by all processes\n"
"on the system. The following options are available. Use -x1 or just -x to\n"
"enable an option, -X or -x0 to disable that option. Current settings in (-)."
"\n\n");
} else
printf("Current SnoopDos settings:\n\n");
#define O(x,y) printf(x, y ? "(on)" : "(off)")
O(" -a Use ANSI colour sequences %s\n", colour);
O(" -c Monitor CurrentDir() calls %s\n", docurdir);
O(" -d Monitor DeleteFile() calls %s\n", dodelete);
O(" -f Display full filename paths %s\n", showfullpath);
O(" -g Monitor LoadSeg() calls %s\n", doloadseg);
O(" -l Monitor Lock() calls %s\n", dolock);
O(" -m Globally enable/disable monitoring %s\n", snoopactive);
O(" -o Monitor Open() calls %s\n", doopen);
O(" -w Display all activity, sleeping if necessary %s\n", sleepwait);
O(" -x Monitor Execute() calls %s\n", doexecute);
if (error == 1) {
printf("\n"
" -h Print out this help message\n"
" -q Tell SnoopDos to quit\n"
" -r Report current SnoopDos settings\n"
" -z% Output to file % (e.g. -zCON:0/0/640/100/SnoopDos or -zSER:)\n"
"\n");
}
cleanup(5);
}
/*
* First see are we already installed. If so, send a copy of the
* updated settings to the background process, or send a QUIT
* message if the user wanted to quit.
*/
if (myport) {
if (quit)
sendmsg(MSG_QUIT, 0, 0);
else
sendmsg(MSG_SETOPTIONS, 0, &settings);
cleanup(0);
}
/*
* If user wanted to quit and we weren't already running, just
* quit without doing anything.
*/
if (quit) {
printf("There is no background SnoopDos process to tell to quit!\n");
cleanup(0);
}
/*
* Not installed, so install ourselves. First of all though, we
* kick ourselves into the background and return control to the
* calling CLI. We need to do this before we start creating ports
* and opening files, to prevent Exec and AmigaDOS getting
* confused about which task we are.
*
* Also open stderr channel for the new task to output fatal
* error messages to; the new task takes responsibility for closing
* this channel.
*/
stderr = Open("*", MODE_NEWFILE);
if (!res("SnoopDos", 5, mainloop, 4000)) {
printf("Couldn't spawn background SnoopDos task.\n");
cleanup(10);
}
}
/*
* NewOpen()
* ---------
* This is the new Open() code. It checks to see if the calling task
* is actually the SnoopDos task; if it is, then it essentially does
* nothing (this stops any possible deadlock occurring). Otherwise,
* it sends a message to the task with the pertinent info.
*
* If sleeping is disabled then if the semaphore isn't immediately
* available, a signal is sent to the Snoopdos task telling it that
* it's missed trapping a function.
*/
__asm BPTR NewOpen(reg_d1 char *filename, reg_d2 int mode)
{
BPTR filehandle;
geta4();
if (snoopactive && doopen && FindTask(0) != snooptask) {
if (sleepwait)
ObtainSemaphore(sem);
else if (!AttemptSemaphore(sem)) {
Signal(snooptask, missed_open_sig);
return (CallOpen(filename, mode));
}
sendmsg(MSG_OPEN, mode, filename);
filehandle = CallOpen(filename, mode);
sendmsg(MSG_OPEN_DONE, filehandle, 0);
ReleaseSemaphore(sem);
return (filehandle);
} else {
return (CallOpen(filename, mode));
}
}
/*
* NewLock()
* ---------
* Replacement for Lock(), all the comments for NewOpen() apply
* equally here.
*/
__asm BPTR NewLock(reg_d1 char *filename, reg_d2 int mode)
{
BPTR lock;
geta4();
if (snoopactive && dolock && FindTask(0) != snooptask) {
if (sleepwait)
ObtainSemaphore(sem);
else if (!AttemptSemaphore(sem)) {
Signal(snooptask, missed_lock_sig);
return (CallLock(filename, mode));
}
sendmsg(MSG_LOCK, mode, filename);
lock = CallLock(filename, mode);
sendmsg(MSG_LOCK_DONE, lock, 0);
ReleaseSemaphore(sem);
return (lock);
} else {
return (CallLock(filename, mode));
}
}
/*
* NewLoadSeg()
* ------------
* Replacement for Lock(), all the comments for NewOpen() apply
* equally here.
*/
__asm BPTR NewLoadSeg(reg_d1 char *filename)
{
BPTR seglist;
geta4();
if (snoopactive && doloadseg && FindTask(0) != snooptask) {
if (sleepwait)
ObtainSemaphore(sem);
else if (!AttemptSemaphore(sem)) {
Signal(snooptask, missed_loadseg_sig);
return (CallLoadSeg(filename));
}
sendmsg(MSG_LOADSEG, 0, filename);
seglist = CallLoadSeg(filename);
sendmsg(MSG_LOADSEG_DONE, seglist, 0);
ReleaseSemaphore(sem);
return (seglist);
} else {
return (CallLoadSeg(filename));
}
}
/*
* NewExecute()
* ------------
* Replacement for Execute()
*/
__asm LONG NewExecute(reg_d1 char *command, reg_d2 BPTR in, reg_d3 BPTR out)
{
geta4();
if (snoopactive && doexecute && FindTask(0) != snooptask) {
if (sleepwait)
ObtainSemaphore(sem);
else if (!AttemptSemaphore(sem)) {
Signal(snooptask, missed_execute_sig);
return(CallExecute(command, in, out));
}
sendmsg(MSG_EXECUTE, 0, command);
ReleaseSemaphore(sem);
}
return(CallExecute(command, in, out));
}
/*
* NewCurrentDir()
* ---------------
* Replacement for CurrentDir()
*/
__asm BPTR NewCurrentDir(reg_d1 BPTR newlock)
{
geta4();
if (snoopactive && docurdir && FindTask(0) != snooptask) {
if (sleepwait)
ObtainSemaphore(sem);
else if (!AttemptSemaphore(sem)) {
Signal(snooptask, missed_curdir_sig);
return(CallCurrentDir(newlock));
}
sendmsg(MSG_CURDIR, newlock, 0);
ReleaseSemaphore(sem);
}
return(CallCurrentDir(newlock));
}
/*
* NewDeleteFile()
* ---------------
* Replacement for DeleteFile()
*/
__asm LONG NewDeleteFile(reg_d1 char *filename)
{
LONG success;
geta4();
if (snoopactive && dodelete && FindTask(0) != snooptask) {
if (sleepwait)
ObtainSemaphore(sem);
else if (!AttemptSemaphore(sem)) {
Signal(snooptask, missed_delete_sig);
return(CallDeleteFile(filename));
}
sendmsg(MSG_DELETE, 0, filename);
success = CallDeleteFile(filename);
sendmsg(MSG_DELETE_DONE, success, 0);
ReleaseSemaphore(sem);
} else
return(CallDeleteFile(filename));
}