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.