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