[comp.sources.amiga] v90i283: SnoopDos 1.00 - Monitors calls to AmigaDOS functions, Part02/02

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.