home *** CD-ROM | disk | FTP | other *** search
- /*
- * SNOOPDOS.C vi:ts=4
- *
- * (C) Copyright Eddy Carroll, December 1992. 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 SAS/C V6.1. Tabs are set every 4 columns.
- *
- * Revisions
- * ---------
- * 30 Oct 90: Fixed potential deadlock problem in termination code
- * Improved handling of NULL lock (i.e. boot device)
- *
- * 27 Jan 91: Really fixed deadlock problem in termination code!
- *
- * 8 Feb 91: Fixed small bug in tiny.a. Saved 2K by replacing
- * sprintf in lc.lib with a version that uses RawDoFmt.
- *
- * 21 Oct 91: Fixed silly bug with -z option whereby if a space was
- * left between the z and the filename, SnoopDos would
- * crash the system. I had mistyped extfilename as extfile.
- * Silly silly silly!
- *
- * 7 Jul 92: Fixed bug reported by Matthias Scheler, whereby
- * DeleteFile() sometimes trashed the return value.
- * This was a simple goof-up on my part (I forgot to
- * return the result from the function!) Also made exit
- * handling more robust to avoid lockups on exit, and
- * added -i option to ignore Shell Lock() commands and
- * Shell & Workbench CurrentDir() commands.
- *
- * 19 Aug 92: Fixed bug introduced in V1.5 with -i whereby many more
- * programs than just the CLI/Shell would have their Lock()
- * and CurrentDir() calls ignored if -i was enabled.
- *
- * 17 Dec 92: Some new features. -zd for serial port output, -z+
- * option for appending output to an existing log file.
- * File output now buffered; -n disabled this. Fixed bug
- * where current directory wasn't inherited by background
- * process. Fixed version string. CON: window now autosizes
- * depending on the user's default system font. Added -s
- * option to include CR's at the end of every output line.
- */
-
- #include "system.h"
-
- #define tolower(ch) (((ch) >= 'A' && (ch) <= 'Z') ? (ch) + 0x20 : (ch))
-
- /*
- * Assorted strings
- */
-
- #define NAME "SnoopDos 1.7"
-
- char *Version = "$VER: " NAME " (17.12.92)";
-
- #define TITLE \
- NAME ". Copyright \251 Eddy Carroll, December 1992. Freely distributable."
-
- char *HEADER =
- "Process name Func Filename Mode Res."
- "\r\n"
- "------------ ---- -------- ---- ----"
- "\r\n";
-
- #define PORTNAME NAME
-
- #define FILEBUFSIZE 8192 /* Size of output file buffer */
-
- /*
- * These defines are for use with the -i1 option; when enabled,
- * Lock() calls by the tasks called IGNORE_CLI and IGNORE_BG will
- * be ignored and CurrentDir() calls by all three tasks will also
- * be ignored. Basically, this avoids the frenzy of activity whenever
- * the Shell searches for an executable or Workbench does its once a
- * second scan of the mounted volumes.
- */
- #define IGNORE_CLI "Shell Process"
- #define IGNORE_BG "Background CLI"
- #define IGNORE_WB "Workbench"
-
- /*
- * 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 */
- int set_ignoretasks; /* If true, ignore workbench CD */
- int set_stripcr; /* If true, strip CR's from output */
- } SETTINGS;
-
- /*
- * Default settings
- */
- SETTINGS settings = { 1, 0, 1, 1, 1, 1, 0, 0, 1, 1, 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
- #define ignoretasks settings.set_ignoretasks
- #define stripcr settings.set_stripcr
-
- /*
- * 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 GfxBase *GfxBase; /* Pointer to GfxBase */
-
- 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 */
- BPTR curdir; /* Lock on SnoopDos's current directory */
- int serialio; /* True if output directed to serial port */
- int appendmode; /* True if opening file for append mode */
- int nobuffer; /* True if we are not to buffer file output */
- int buffering; /* True if currently buffering file output */
- int batchmode; /* True if printing to non-interactive file */
- int colsel; /* True if colour option was specified */
- char logfilename[150]; /* Name of window/file to open if specified */
- char outbuf[800]; /* Output buffer used by myprintf() etc. */
- char *filebuffer; /* Pointer to possible output file buffer */
- int filebufpos; /* Current offset into file buffer */
-
- 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 (filebuffer) FreeMem(filebuffer, FILEBUFSIZE);
- 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);
- if (stripcr) {
- char *p = outbuf, *q = outbuf;
-
- while (*p) {
- if (*p != '\r')
- *q++ = *p;
- p++;
- }
- *q = '\0';
- }
- Write(Output(), outbuf, strlen(outbuf));
- }
-
- /*
- * Output a formatted string to a disk file. If buffering is
- * true, then the output is buffered internally and only flushed
- * when full. Making the format string NULL causes the current
- * buffer to be flushed.
- */
- void myfprintf(file, format, p1, p2, p3, p4, p5, p6)
- BPTR file;
- char *format;
- ULONG p1, p2, p3, p4, p5, p6;
- {
- if (format == NULL) {
- if (buffering && filebufpos) {
- Write(file, filebuffer, filebufpos);
- filebufpos = 0;
- }
- return;
- }
- sprintf(outbuf, format, p1, p2, p3, p4, p5, p6);
- if (stripcr) {
- char *p = outbuf, *q = outbuf;
-
- while (*p) {
- if (*p != '\r')
- *q++ = *p;
- p++;
- }
- *q = '\0';
- }
- if (file) {
- int len = strlen(outbuf);
-
- if (buffering) {
- if ((filebufpos + len) > FILEBUFSIZE) {
- Write(file, filebuffer, filebufpos);
- filebufpos = 0;
- }
- memcpy(filebuffer + filebufpos, outbuf, len);
- filebufpos += len;
- } else {
- Write(file, outbuf, strlen(outbuf));
- }
- } else { /* Serial debugging output */
- KPutStr(outbuf);
- }
- }
-
- /*
- * 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)) {
- UnLock(mylock);
- err++;
- break;
- }
- 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;
- struct DeviceList *dl;
- UBYTE *name;
-
- if (lock) {
- fl = BTOC(lock);
- dl = BTOC(fl->fl_Volume);
- name = BTOC(dl->dl_Name);
-
- strncpy(path, name + 1, *name);
- path[*name] = '\0';
- } else {
- strcpy(path, "<Boot device>");
- }
- 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++;
- }
- }
-
- /*
- * 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];
- BPTR olddir; /* Saved current directory */
- 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) {
- myfprintf(stderr, "SnoopDos: Can't allocate message port.\n");
- cleanup(-1);
- return;
- }
-
- myport = inport;
- portsig = 1 << myport->mp_SigBit;
-
- /*
- * Setup a default window string if none has been specified
- */
- if (!*logfilename) {
- int width = 8, height = 8; /* Default character size is 8 * 8 */
- GfxBase = (struct GfxBase *)OpenLibrary("graphics.library", 0);
- if (GfxBase) {
- width = GfxBase->DefaultFont->tf_XSize;
- height = GfxBase->DefaultFont->tf_YSize;
- CloseLibrary(GfxBase);
- }
- sprintf(logfilename, "CON:0/0/%ld/%ld/%s",
- width * 77 + 24, height * 14 + 15, TITLE);
- }
-
- /*
- * 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) {
- myfprintf(stderr, "SnoopDos: Can't allocate enough signals.\n");
- cleanup(-1);
- return;
- }
-
- /*
- * Setup output file, either serial port debugging, a standard
- * CON: or AUX: file, or a non-interactive logfile.
- */
- olddir = CurrentDir(curdir);/* Switch to directory we were run from */
- if (serialio) {
- debugwin = NULL; /* Allows KPutStr() to be used instead */
- batchmode = 1; /* Enable batch mode messages */
- } else {
- if (!appendmode)
- debugwin = Open(logfilename, MODE_NEWFILE);
- else {
- debugwin = Open(logfilename, MODE_READWRITE);
- if (debugwin)
- Seek(debugwin, 0, OFFSET_END);
- }
- if (!debugwin) {
- myfprintf(stderr, "SnoopDos: Can't open log file %s.\n",
- logfilename);
- cleanup(-1);
- return;
- }
- if (!IsInteractive(debugwin)) {
- batchmode = 1;
- if (!nobuffer) {
- filebuffer = AllocMem(FILEBUFSIZE, 0);
- if (filebuffer)
- buffering = 1;
- }
- }
- }
- CurrentDir(olddir);
- UnLock(curdir); curdir = NULL; /* Free up directory lock */
- Close(stderr); stderr = NULL;
-
- if (batchmode) {
- myfprintf(debugwin, "\r\n" TITLE "\r\n\r\n");
- if (colsel == 0) /* Disable ANSI colour unless specifically enabled */
- colour = 0;
- }
- if (!batchmode) {
- myfprintf(debugwin, "\r\n"
- "Type CTRL-E/CTRL-D to enable/disable snooping. Type CTRL-C to exit."
- "\r\n\r\n");
- }
- myfprintf(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) {
- myfprintf(debugwin,
- "\nSnooping now disabled; type CTRL-E to enable it again.\r\n\r\n");
- snoopactive = 0;
- }
- }
-
- if (mask & SIGBREAKF_CTRL_E) {
- if (!snoopactive) {
- myfprintf(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 how 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;
- myfprintf(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;
- }
- myfprintf(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;
- }
- myfprintf(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;
- }
- myfprintf(debugwin, "%-21s %s %s%-44s ", procname,
- TXT_LOAD, POINT(newname), filename);
- col0 = 0;
- break;
-
- case MSG_EXECUTE:
- myfprintf(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';
- myfprintf(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;
- }
- myfprintf(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) {
- myfprintf(debugwin, "%-73s", TXT_DONE);
- nested = 0;
- }
- if (msg->data1)
- myfprintf(debugwin, TXT_OKAY);
- else
- myfprintf(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)
- myfprintf(debugwin, "%s an Open()\r\n", TXT_WARN);
- if (missed & MISSED_LOCK)
- myfprintf(debugwin, "%s a Lock()\r\n", TXT_WARN);
- if (missed & MISSED_LOADSEG)
- myfprintf(debugwin, "%s a LoadSeg()\r\n", TXT_WARN);
- if (missed & MISSED_EXECUTE)
- myfprintf(debugwin, "%s an Execute()\r\n", TXT_WARN);
- if (missed & MISSED_CURDIR)
- myfprintf(debugwin, "%s a CurrentDir()\r\n", TXT_WARN);
- if (missed & MISSED_DELETE)
- myfprintf(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;
- snoopactive = 0; /* Make sure our patch stops sending msgs to us! */
-
- /*
- * Now try and obtain our semaphore. Until we can achieve it,
- * we may still be receiving messages from other tasks telling
- * us about DOS operations, so, we reply to these as they arrive
- * (else deadlock occurs).
- */
- while (!AttemptSemaphore(sem)) {
- MYMSG *msg = (MYMSG *)GetMsg(myport);
-
- if (msg)
- ReplyMsg(msg);
- Delay(5); /* Wait for 0.1 seconds */
- }
- myfprintf(debugwin, "\r\nSnoopDos terminated.\r\n");
- myfprintf(debugwin, NULL); /* Flush possible output */
-
- /*
- * Now remove the dospatch, 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;
-
- myfprintf(debugwin,
- "\r\n"
- "Someone else has patched dos.library so I'll have to wait until they quit."
- "\r\n");
- if (!batchmode) {
- myfprintf(debugwin,
- "This window will close shortly though to stop it from annoying you.\r\n");
- }
- do {
- Delay(50); /* Wait for one second */
- if (debugwin) {
- count++;
- if (count > 10) {
- Close(debugwin);
- debugwin = NULL;
- }
- }
- } while (!uninstalldospatch());
- }
-
- /*
- * We do a final wait for 2 seconds, just in case there are any
- * other tasks still running in our SetFunction'd code. We close
- * the window/file first, to avoid annoying the user.
- * (This isn't 100% bulletproof, but it's fairly safe).
- */
- if (debugwin) {
- Close(debugwin);
- debugwin = NULL; /* Make sure cleanup code doesn't close it too */
- }
- Delay(50);
- /*
- * Now make one final check to ensure there are no outstanding
- * messages waiting to be replied too. Note that all this delaying
- * etc. isn't really a problem since we are now "invisible" to
- * the rest of the system, having deleted our AmigaDOS patch and
- * public port earlier on. It's just a bit of extra bullet
- * proofing to reduce the risk of race conditions during exit.
- */
- if (myport) {
- MYMSG *msg;
- while ((msg = (MYMSG *)GetMsg(myport)) != NULL)
- ReplyMsg(msg);
- }
- Delay(50*2); /* And give such code 2 seconds to complete */
- cleanup(-1);
- }
-
- /*
- * main()
- * ------
- * Mainline. Parses the command line and, if necessary, kicks off the
- * background task to do the real work.
- */
- void __stdargs main(int argc, char **argv)
- {
- int error = 0; /* True if error detected in cmd line */
- int quit = 0; /* True if we want to quit */
-
- /*
- * 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 'i': ignoretasks = val; break; /* Ignore WBench/Shell */
- case 'l': dolock = val; break; /* Monitor Lock() */
- case 'm': snoopactive = val; break; /* Actively monitoring */
- case 'n': nobuffer = 1; break; /* Disable output buf */
- case 'o': doopen = val; break; /* Monitor Open() */
- case 'q': quit = 1; break; /* Ask SnoopDos to quit */
- case 'r': error = 2; break; /* Report settings */
- case 's': stripcr = 0; break; /* Disable CR stripping */
- case 'w': sleepwait = val; break; /* Wait when necessary */
- case 'x': doexecute = val; break; /* Monitor Execute() */
- case 'z':
- if (strcmp(argv[1], "-zd") == 0) {
- serialio = 1;
- stripcr = 0;
- } else if (argv[1][2] == '+' && argv[1][3]) {
- strcpy(logfilename, &argv[1][3]);
- appendmode = 1;
- } else if (argv[1][2]) {
- strcpy(logfilename, &argv[1][2]);
- } else {
- argv++;
- argc--;
- if (argc > 1)
- strcpy(logfilename, argv[1]);
- }
- if (!*logfilename && !serialio) {
- myprintf(
- "-z must be followed by a filename. Type SnoopDos -h for help.\n");
- exit(5);
- }
- break;
-
- default:
- myprintf("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) {
- myprintf(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, -x0 or -X to disable that option. Current settings in (-)."
- "\n\n");
- } else
- myprintf("Current " NAME " settings:\n\n");
-
- #define O(x,y) myprintf(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(" -i Ignore some Shell & Workbench calls %s\n", ignoretasks);
- 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(" -s Add CR's to end of each output line %s\n", !stripcr);
- O(" -w Display all activity, sleeping if necessary %s\n", sleepwait);
- O(" -x Monitor Execute() calls %s\n", doexecute);
-
- if (error == 1) {
- myprintf("\n"
- " -h Print out this help message\n"
- " -n Don't buffer log file output\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 -zlogfile)\n"
- " -z+$ Append output to file $\n"
- " -zd Output to debugging terminal on serial port\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 (*logfilename) {
- myprintf("You can't use -z when SnoopDos is already running.\n");
- cleanup(0);
- }
- 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) {
- myprintf("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);
- curdir = Lock("", ACCESS_READ); /* Let sub-process access cur-dir */
- if (!res("SnoopDos", 5, mainloop, 4000)) {
- myprintf("Couldn't spawn background SnoopDos task.\n");
- UnLock(curdir);
- curdir = NULL;
- cleanup(10);
- }
- }
-
- /*
- * New_Open()
- * ----------
- * 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 New_Open(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));
- }
- }
- /*
- * New_Lock()
- * ---------
- * Replacement for Lock(), all the comments for NewOpen() apply
- * equally here.
- */
- __asm BPTR New_Lock(reg_d1 char *filename, reg_d2 int mode)
- {
- BPTR lock;
- struct Task *task;
- struct CommandLineInterface *cli;
- UBYTE *procname;
-
- geta4();
- task = FindTask(0L);
- procname = task->tc_Node.ln_Name;
- cli = BTOC(((struct Process *)task)->pr_CLI);
-
- if (cli && cli->cli_Module) {
- UBYTE *cliname = BTOC(cli->cli_CommandName);
- if (*cliname > 0) {
- /* Only use CLI name if it's not null */
- procname = cliname + 1;
- }
- }
-
- if (ignoretasks && (strncmp(procname,IGNORE_CLI,strlen(IGNORE_CLI)) == 0 ||
- strncmp(procname,IGNORE_BG, strlen(IGNORE_BG)) == 0))
- return (CallLock(filename, mode));
-
- 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));
- }
- }
-
- /*
- * New_LoadSeg()
- * -------------
- * Replacement for LoadSeg(), all the comments for New_Open() apply
- * equally here.
- */
- __asm BPTR New_LoadSeg(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));
- }
- }
-
- /*
- * New_Execute()
- * ------------
- * Replacement for Execute()
- */
- __asm LONG New_Execute(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));
- }
-
- /*
- * New_CurrentDir()
- * ---------------
- * Replacement for CurrentDir()
- */
- __asm BPTR New_CurrentDir(reg_d1 BPTR newlock)
- {
- struct Task *task;
- struct CommandLineInterface *cli;
- UBYTE *procname;
-
- geta4();
- task = FindTask(0L);
- procname = task->tc_Node.ln_Name;
- cli = BTOC(((struct Process *)task)->pr_CLI);
-
- if (cli && cli->cli_Module) {
- UBYTE *cliname = BTOC(cli->cli_CommandName);
- if (*cliname > 0) {
- /* Only use CLI name if it's not null */
- procname = cliname + 1;
- }
- }
-
- /*
- * If we are ignoring CD's from the Workbench and Shell tasks
- * (to save lots of useless output when running under
- * Kickstart 2.0x), then check to see if the task trying to
- * do a CD is Workbench, or the shell itself. If it is,
- * don't output anything. Note that we distinguish between
- * commands running from within the shell, and the shell itself,
- * by looking to see if a module has been loaded into the Shell.
- */
- if (ignoretasks && (strncmp(procname,IGNORE_WB, strlen(IGNORE_WB)) == 0 ||
- strncmp(procname,IGNORE_CLI,strlen(IGNORE_CLI)) == 0 ||
- strncmp(procname,IGNORE_BG, strlen(IGNORE_BG)) == 0))
- return (CallCurrentDir(newlock));
-
- if (snoopactive && docurdir && task != 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));
- }
-
- /*
- * New_DeleteFile()
- * ---------------
- * Replacement for DeleteFile()
- */
- __asm LONG New_DeleteFile(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);
- return (success);
- } else
- return (CallDeleteFile(filename));
- }
-