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