home *** CD-ROM | disk | FTP | other *** search
/ Club Amiga de Montreal - CAM / CAM_CD_1.iso / files / 594b.lha / SnoopDos_v1.4 / src / snoopdos.c < prev    next >
C/C++ Source or Header  |  1991-10-21  |  33KB  |  1,174 lines

  1. /*
  2.  *        SNOOPDOS.C
  3.  *
  4.  *        (C) Copyright Eddy Carroll, May 1990. Freely distributable.
  5.  *
  6.  *        Snoopdos patches into dos.library and outputs a message to a
  7.  *        debugging window or file whenever a process calls certain DOS
  8.  *        functions.
  9.  *
  10.  *        Type snoopdos -h for a list of available options. See the
  11.  *        documentation for further details.
  12.  *
  13.  *        Compiles under Lattice C V5.10. I use flags: -cusq -j88i -ms -v
  14.  *        Note: Source assumes tabs set every 4 columns.
  15.  *
  16.  *        Revisions
  17.  *        ---------
  18.  *        30 Oct 90:  Fixed potential deadlock problem in termination code
  19.  *                    Improved handling of NULL lock (i.e. boot device)
  20.  *
  21.  *        27 Jan 91:    Really fixed deadlock problem in termination code!
  22.  *
  23.  *       8 Feb 91:  Fixed small bug in tiny.a. Saved 2K by replacing
  24.  *                  sprintf in lc.lib with a version that uses RawDoFmt.
  25.  *
  26.  *        21 Oct 91:    Fixed silly bug with -z option whereby if a space was
  27.  *                    left between the z and the filename, SnoopDos would
  28.  *                    crash the system. I had mistyped extfilename as extfile.
  29.  *                    Silly silly silly!
  30.  */
  31.  
  32. #ifndef LATTICE_50
  33. #include "system.h"
  34. #endif
  35.  
  36. #define tolower(ch)    (((ch) >= 'A' && (ch) <= 'Z') ? (ch) + 0x20 : (ch))
  37.  
  38. /*
  39.  *        Assorted strings
  40.  */
  41.  
  42. #define NAME    "SnoopDos V1.4"
  43.  
  44. #define TITLE \
  45. NAME ".  Copyright \251 Eddy Carroll, Oct 1991. Freely distributable."
  46.  
  47. char *HEADER =
  48. "Process name          Func  Filename                                Mode Res."
  49. "\r\n"
  50. "------------          ----  --------                                ---- ----"
  51. "\r\n";
  52.  
  53. #define PORTNAME    NAME
  54.  
  55. #define DEFWINDOW    "CON:0/0/640/120/"
  56.  
  57. /*
  58.  *        The following message array contains both colour and non-colour
  59.  *        versions of the various short message strings displayed.
  60.  */
  61. char *msgs[][2] = {
  62. /* Monochrome           Colour   */
  63. /* ----------           ------   */
  64.     "OLD ",                "OLD ",
  65.     "NEW ",                "\033[33mNEW\033[0m ",
  66.     "R/W ",                "\033[32mR/W\033[0m ",
  67.     "??? ",                "??? ",
  68.     "SHAR",                "SHAR",
  69.     "EXCL",                "\033[33mEXCL\033[0m",
  70.     "????",                "????",
  71.     "Okay\r\n",            "Okay\r\n",
  72.     "Fail\r\n",            "\033[33mFail\033[0m\r\n",
  73.     ">",                "\033[33m>\033[0m",
  74.     "> (Done)",            "> (Done)",
  75.     "Warning: Missed",    "\033[33mWarning:\033[0m Missed",
  76.     ">>>>\r\n",            ">>>>\r\n",
  77.     "Open",                "Open",
  78.     "Lock",                "\033[33mLock\033[0m",
  79.     "Load",                "\033[32mLoad\033[0m",
  80.     "Exec",                "\033[32mExec\033[0m",
  81.     "CD  ",                "CD  ",
  82.     "Del ",                "\033[33mDel\033[0m "
  83. };
  84.  
  85. #define TXT_OLD        msgs[ 0][colour]
  86. #define TXT_NEW        msgs[ 1][colour]
  87. #define TXT_R_W        msgs[ 2][colour]
  88. #define TXT_QM3        msgs[ 3][colour]
  89. #define TXT_SHAR    msgs[ 4][colour]
  90. #define TXT_EXCL    msgs[ 5][colour]
  91. #define TXT_QM4        msgs[ 6][colour]
  92. #define TXT_OKAY    msgs[ 7][colour]
  93. #define TXT_FAIL    msgs[ 8][colour]
  94. #define TXT_POINT    msgs[ 9][colour]
  95. #define TXT_DONE    msgs[10][colour]
  96. #define TXT_WARN    msgs[11][colour]
  97. #define TXT_NEST    msgs[12][colour]
  98. #define TXT_OPEN    msgs[13][colour]
  99. #define TXT_LOCK    msgs[14][colour]
  100. #define TXT_LOAD    msgs[15][colour]
  101. #define TXT_EXEC    msgs[16][colour]
  102. #define TXT_CURDIR    msgs[17][colour]
  103. #define TXT_DELETE    msgs[18][colour]
  104.  
  105. #define POINT(x)    ((x) ? TXT_POINT : " ")
  106.  
  107. /*
  108.  *        Now some standard system-type macros
  109.  */
  110. #define reg_d0    register __d0
  111. #define reg_d1    register __d1
  112. #define reg_d2    register __d2
  113. #define reg_d3    register __d3
  114. #define reg_a0    register __a0
  115.  
  116. #define BTOC(x)    (void *)(((ULONG)x) << 2)
  117.  
  118. #define D_S(name, type) char c_##name[sizeof(type)+3];\
  119.                         type *name = (type *)((long)(c_##name+3) & ~3)
  120.  
  121. extern __asm BPTR CallOpen(reg_d1 UBYTE *, reg_d2 int);
  122. extern __asm BPTR CallLock(reg_d1 UBYTE *, reg_d2 int);
  123. extern __asm BPTR CallLoadSeg(reg_d1 UBYTE *);
  124. extern __asm LONG CallExecute(reg_d1 UBYTE *, reg_d2 BPTR, reg_d3 BPTR);
  125. extern __asm BPTR CallCurrentDir(reg_d1 BPTR);
  126. extern __asm LONG CallDeleteFile(reg_d1 UBYTE *);
  127.  
  128. /*
  129.  *        Structure used to pass messages back and fro
  130.  */
  131. typedef struct {
  132.     struct Message msg;            /* Standard message header    */
  133.     struct Process *process;    /* Sending process id        */
  134.     int msgtype;                /* Message type, see below    */
  135.     int  data1;                    /* Data field 1                */ 
  136.     void *data2;                /* Data field 2                */
  137. } MYMSG;
  138.  
  139. /*
  140.  *        Now the various settings that can be set to affect the monitoring
  141.  */
  142. typedef struct {
  143.     int set_doopen;                /* If true, monitor Open()            */
  144.     int    set_dolock;                /* If true, monitor Lock()            */
  145.     int set_doloadseg;            /* If true, monitor LoadSeg()        */
  146.     int set_doexecute;            /* If true, monitor Execute()        */
  147.     int set_docurdir;            /* If true, monitor CurrentDir()    */
  148.     int set_dodelete;            /* If true, monitor DeleteFile()    */
  149.     int    set_showfullpath;        /* If true, display full paths        */
  150.     int set_sleepwait;            /* If true, sleep if necessary        */
  151.     int set_snoopactive;        /* If true, monitoring is active    */
  152.     int set_colour;                /* If true, use ANSI colour codes    */
  153. } SETTINGS;
  154.  
  155. /*
  156.  *        Default settings
  157.  */
  158. SETTINGS settings = { 1, 0, 1, 1, 1, 1, 0, 0, 1, 1 };
  159.  
  160. /*
  161.  *        These defines allow the various settings to be accessed as
  162.  *        normal variables rather than structure members; this is purely
  163.  *        for convenience.
  164.  */
  165. #define doopen            settings.set_doopen
  166. #define dolock            settings.set_dolock
  167. #define doloadseg        settings.set_doloadseg
  168. #define doexecute        settings.set_doexecute
  169. #define docurdir        settings.set_docurdir
  170. #define dodelete        settings.set_dodelete
  171. #define showfullpath    settings.set_showfullpath
  172. #define sleepwait        settings.set_sleepwait
  173. #define snoopactive        settings.set_snoopactive
  174. #define colour            settings.set_colour
  175.  
  176. /*
  177.  *        Now the various message types that can be sent
  178.  */
  179. typedef enum {
  180.     MSG_QUIT,            /* Quit                     */
  181.     MSG_GETOPTIONS,        /* Read options                */
  182.     MSG_SETOPTIONS,        /* Update options            */
  183.     MSG_OPEN,            /* Open file                */
  184.     MSG_OPEN_DONE,        /* Open file completed        */
  185.     MSG_LOCK,            /* Lock file                */
  186.     MSG_LOCK_DONE,        /* Lock file completed        */
  187.     MSG_LOADSEG,        /* LoadSeg file                */
  188.     MSG_LOADSEG_DONE,    /* LoadSeg file completed    */
  189.     MSG_EXECUTE,        /* Execute command            */
  190.     MSG_CURDIR,            /* CurrentDir                */
  191.     MSG_DELETE,            /* DeleteFile                */
  192.     MSG_DELETE_DONE,    /* DeleteFile completed        */
  193. } MSGTYPES;
  194.  
  195. struct SignalSemaphore sem[1];
  196. struct MsgPort   *myport;        /* Pointer to background SnoopDos msg port    */
  197. struct MsgPort   *inport;        /* Pointer to our own message port            */
  198. struct Task      *snooptask;    /* Pointer to our own task                    */
  199. BPTR debugwin;                    /* Output file or window                    */
  200. BPTR stderr;                    /* Standard CLI console                        */
  201. int  extfile;                    /* True if output directed to external file    */
  202. char extfilename[100];            /* Name of window/file to open if specified    */
  203. char outbuf[800];                /* Output buffer used by myprintf() etc.    */
  204.  
  205. int missed_open_sig;            /* Signal to indicate Open() missed            */
  206. int missed_lock_sig;            /* Signal to indicate Lock() missed            */
  207. int missed_loadseg_sig;            /* Signal to indicate LoadSeg() missed        */
  208. int missed_execute_sig;            /* Signal to indicate Execute() missed        */
  209. int missed_curdir_sig;            /* Signal to indicate CurrentDir() missed    */
  210. int missed_delete_sig;            /* Signal to indicate DeleteFile() missed    */
  211. int portsig;                    /* Signal used by our public port            */
  212.  
  213. /**************************** Start of Functions ****************************/
  214.  
  215. /*
  216.  *        cleanup()
  217.  *        ---------
  218.  *        Cleans up all active resources and exits. If err is -1, then
  219.  *        returns instead of exiting.
  220.  */
  221. void cleanup(int err)
  222. {
  223.     static int called = 0;
  224.  
  225.     if (called++)        /* Make sure not called twice by accident */
  226.         return;
  227.  
  228.     if (inport)
  229.         DeletePort(inport);
  230.  
  231.     if (debugwin)
  232.         Close(debugwin);
  233.  
  234.     if (stderr)
  235.         Close(stderr);
  236.  
  237.     if (err != -1)
  238.         exit(err);
  239. }
  240.  
  241. /*
  242.  *        myprintf(), myfprintf()
  243.  *        -----------------------
  244.  *        Two low cost alternatives to printf that go directly to AmigaDOS
  245.  *        Note we deliberately use the pre-ANSI declaration, to avoid Lattice
  246.  *        kicking up a fuss about the wrong number of parameters.
  247.  */
  248. void myprintf(format, p1, p2, p3, p4, p5, p6)
  249. UBYTE *format;
  250. ULONG p1, p2, p3, p4, p5, p6;
  251. {
  252.     sprintf(outbuf, format, p1, p2, p3, p4, p5, p6);
  253.     Write(Output(), outbuf, strlen(outbuf));
  254. }
  255.  
  256. void myfprintf(file, format, p1, p2, p3, p4, p5, p6)
  257. BPTR file;
  258. char *format;
  259. ULONG p1, p2, p3, p4, p5, p6;
  260. {
  261.     sprintf(outbuf, format, p1, p2, p3, p4, p5, p6);
  262.     Write(file, outbuf, strlen(outbuf));
  263. }
  264.  
  265. /*
  266.  *        sendmsg()
  267.  *        ---------
  268.  *        Sends a message to myport, and waits for a reply to arrive.
  269.  *        A message type and some message data are all that need be provided
  270.  *        by the caller; the callee will provide the rest. Doesn't return
  271.  *        until a reply has been received.
  272.  */
  273. void sendmsg(int type, int data1, void *data2)
  274. {
  275.     MYMSG msg;
  276.     struct Process *me = (struct Process *)FindTask(0);
  277.     struct MsgPort *replyport = &me->pr_MsgPort;
  278.  
  279.     msg.msg.mn_Node.ln_Type = NT_MESSAGE;
  280.     msg.msg.mn_Length       = sizeof(MYMSG);
  281.     msg.msg.mn_ReplyPort    = replyport;
  282.     msg.process                = me;
  283.     msg.msgtype             = type;
  284.     msg.data1               = data1;
  285.     msg.data2               = data2;
  286.  
  287.     PutMsg(myport, &msg);
  288.     WaitPort(replyport);
  289.     GetMsg(replyport);
  290. }
  291.  
  292.  
  293. /*
  294.  *        getlockpath()
  295.  *        -------------
  296.  *        Returns a pointer to a string containing the full path for the
  297.  *        specified lock. You must copy the string before calling this
  298.  *        routine again.
  299.  */
  300. char *getlockpath(BPTR lock)
  301. {
  302.     struct Process *p = (struct Process *)FindTask(0L);
  303.     APTR oldwin = p->pr_WindowPtr;
  304.     static char path[300];
  305.     int pos = 299;
  306.     BPTR mylock;
  307.     char *name;
  308.     D_S(fib, struct FileInfoBlock);
  309.     int err = 0;
  310.  
  311.     p->pr_WindowPtr = (APTR)-1;    /* Disable error requesters */
  312.     mylock = DupLock(lock);
  313.  
  314.     path[pos] = '\0';
  315.  
  316.     do {
  317.         int len;
  318.         BPTR newlock;
  319.  
  320.         if (!Examine(mylock, fib)) {
  321.             UnLock(mylock);
  322.             err++;
  323.             break;
  324.         }
  325.         name = fib->fib_FileName;
  326.         if (*name == '\0')
  327.             name = "RAM";        /* Workaround for old RAM: disk bug */
  328.         len = strlen(name);
  329.         pos = pos - len - 1;
  330.         newlock = ParentDir(mylock);
  331.         UnLock(mylock);
  332.         mylock = newlock;
  333.         strncpy(path + pos, name, len);
  334.         if (mylock)
  335.             path[pos + len] = '/';
  336.         else
  337.             path[pos + len] = ':';
  338.     } while (mylock);
  339.     p->pr_WindowPtr = oldwin;    /* Enable error requesters again */
  340.  
  341.     if (err) {
  342.         /*
  343.          *        Volume not present so have to be happy with just
  344.          *        returning the volume node instead.
  345.          */
  346.         struct FileLock *fl;
  347.         struct DeviceList *dl;
  348.         UBYTE *name;
  349.  
  350.         if (lock) {
  351.             fl = BTOC(lock);
  352.             dl = BTOC(fl->fl_Volume);
  353.             name = BTOC(dl->dl_Name);
  354.  
  355.             strncpy(path, name + 1, *name);
  356.             path[*name] = '\0';
  357.         } else {
  358.             strcpy(path, "<Boot device>");
  359.         }
  360.         strcat(path, ":.../");
  361.         return (path);
  362.     } else
  363.         return (path + pos);
  364. }
  365.  
  366. /*
  367.  *        makepath()
  368.  *        ----------
  369.  *        Builds a full path string given a process ptr and a filename.
  370.  *        If the filename includes relative references (// or :)
  371.  *        these are handled also. The point of this is to resolve relative
  372.  *        directory references.
  373.  *
  374.  *        Returns TRUE if a new string was generated, FALSE if the existing
  375.  *        string didn't depend on the current directory (this doesn't affect
  376.  *        the output stored in buf though).
  377.  */
  378. int makepath(char *buf, BPTR curdir, char *filename)
  379. {
  380.     char *origfilename = filename;
  381.     int pos;
  382.     int doneroot = 0;
  383.  
  384.     /*
  385.      *        Special check for the 'current process console' file '*'
  386.      */
  387.     if (strcmp(filename, "*") == 0) {
  388.         strcpy(buf, filename);
  389.         return (FALSE);
  390.     }
  391.  
  392.     strcpy(buf, getlockpath(curdir));
  393.     pos = strlen(buf);
  394.  
  395.     for (;;) {
  396.         if (!doneroot && *filename == ':') {
  397.             /*
  398.              *        Path is relative to root
  399.              */
  400.             doneroot = 1;
  401.             for (pos = 0; buf[pos] && buf[pos] != ':'; pos++)
  402.                 ;
  403.             if (buf[pos] == ':')
  404.                 pos++;
  405.         } else if (*filename == '/') {
  406.             /*
  407.              *        Path is relative to parent directory; if none, then
  408.              *        remains the same.
  409.              */
  410.             int newpos = pos - 2;
  411.             while (newpos >= 0 && buf[newpos] != '/' && buf[newpos] != ':')
  412.                 newpos--;
  413.             if (newpos >= 0)
  414.                 pos = newpos + 1;
  415.         } else {
  416.             /*
  417.              *        No more special characters; just append what's left of
  418.              *        the filename.
  419.              */
  420.             if (!doneroot) {
  421.                 /*
  422.                  *        If the filename wasn't relative to the root
  423.                  *        directory, then make sure there are no device/volume
  424.                  *        references contained within it. We copy the original
  425.                  *        filename rather than the currently modified version
  426.                  *        since a volume name of /A: is legal and the / would
  427.                  *        be stripped off the modified name.
  428.                  */
  429.                 char *p;
  430.                 for (p = filename; *p; p++) {
  431.                     if (*p == ':') {
  432.                         strcpy(buf, origfilename);
  433.                         return (0);
  434.                     }
  435.                 }
  436.             }
  437.             strcpy(buf + pos, filename);
  438.             return (1);
  439.         }
  440.         filename++;
  441.     }
  442. }
  443.  
  444. /*
  445.  *        mainloop()
  446.  *        ----------
  447.  *        This is the main event loop for SnoopDOS, where everything happens.
  448.  *        Control is passed here when SnoopDOS first starts up. When this
  449.  *        function returns, it terminates the background process.
  450.  */
  451. void mainloop(void)
  452. {
  453.     static char fullname[300];
  454.     int done   = 0;                    /* True if finished processing            */
  455.     int col0   = 1;                    /* True if cursor in column 0            */
  456.     int nested = 0;                    /* True if nested DOS calls                */
  457.  
  458. #define MISSED_NONE        0            /* Haven't missed anything    */
  459. #define MISSED_OPEN        (1 << 0)    /* Missed Open() call        */
  460. #define MISSED_LOCK        (1 << 1)    /* Missed Lock() call        */
  461. #define MISSED_LOADSEG    (1 << 2)    /* Missed LoadSeg() call    */
  462. #define MISSED_EXECUTE    (1 << 3)    /* Missed Execute() call    */
  463. #define MISSED_CURDIR    (1 << 4)    /* Missed CurrentDir() call    */
  464. #define MISSED_DELETE    (1 << 5)    /* Missed DeleteFile() call    */
  465.  
  466.     int missed = MISSED_NONE;        /* Was a DOS function missed? See above    */
  467.  
  468.     inport = CreatePort(PORTNAME, 0);
  469.     if (!inport) {
  470.         myfprintf(stderr, "SnoopDos: Can't allocate message port.\n");
  471.         cleanup(-1);
  472.         return;
  473.     }
  474.  
  475.     myport  = inport;
  476.     portsig = 1 << myport->mp_SigBit;
  477.  
  478.     /*
  479.      *        Allocate signals
  480.      */
  481.     missed_open_sig       = 1 << AllocSignal(-1);
  482.     missed_lock_sig       = 1 << AllocSignal(-1);
  483.     missed_loadseg_sig = 1 << AllocSignal(-1);
  484.     missed_execute_sig = 1 << AllocSignal(-1);
  485.     missed_curdir_sig  = 1 << AllocSignal(-1);
  486.     missed_delete_sig  = 1 << AllocSignal(-1);
  487.  
  488.     if (    missed_open_sig    == -1  || missed_lock_sig    == -1  ||
  489.             missed_loadseg_sig == -1  || missed_execute_sig == -1  ||
  490.             missed_curdir_sig  == -1  || missed_delete_sig  == -1) {
  491.         myfprintf(stderr, "SnoopDos: Can't allocate enough signals.\n");
  492.         cleanup(-1);
  493.         return;
  494.     }
  495.  
  496.     if (extfile) {
  497.         debugwin = Open(extfilename, MODE_NEWFILE);
  498.         if (!debugwin) {
  499.             myfprintf(stderr, "SnoopDos: Can't open file %s for output.\n",
  500.                                 extfilename);
  501.             cleanup(-1);
  502.             return;
  503.         }
  504.         myfprintf(debugwin, "\r\n" TITLE "\r\n\r\n");
  505.     } else {
  506.         debugwin = Open(DEFWINDOW TITLE, MODE_NEWFILE);
  507.         if (!debugwin) {
  508.             myfprintf(stderr, "SnoopDos: Can't open display window.\n");
  509.             cleanup(-1);
  510.             return;
  511.         }
  512.     }
  513.     Close(stderr); stderr = NULL;
  514.  
  515.     if (!extfile) {
  516.         myfprintf(debugwin, "\n"
  517.     "Type CTRL-E/CTRL-D to enable/disable snooping. Type CTRL-C to exit.\n\n");
  518.     };
  519.     myfprintf(debugwin, HEADER);
  520.  
  521.     InitSemaphore(sem);
  522.     snooptask = FindTask(0L);
  523.     installdospatch();
  524.  
  525.     /*
  526.      *        Now just sit processing messages
  527.      */
  528.     while (!done) {
  529.         int mask;
  530.  
  531. #define SIGMASK    (portsig | missed_open_sig | missed_lock_sig | \
  532.                  missed_loadseg_sig | missed_execute_sig |     \
  533.                  missed_curdir_sig | missed_delete_sig |       \
  534.                  SIGBREAKF_CTRL_C | SIGBREAKF_CTRL_D | SIGBREAKF_CTRL_E)
  535.  
  536.         mask = Wait(SIGMASK);
  537.  
  538.         if (mask & SIGBREAKF_CTRL_C)
  539.             done = 1;
  540.  
  541.         if (mask & SIGBREAKF_CTRL_D) {
  542.             if (snoopactive) {
  543.                 myfprintf(debugwin,
  544. "\nSnooping now disabled; type CTRL-E to enable it again.\r\n\r\n");
  545.                 snoopactive = 0;
  546.             }
  547.         }
  548.  
  549.         if (mask & SIGBREAKF_CTRL_E) {
  550.             if (!snoopactive) {
  551.                 myfprintf(debugwin,
  552. "Snooping now enabled; type CTRL-D to disable it again.\r\n\r\n%s", HEADER);
  553.                 snoopactive = 1;
  554.             }
  555.         }
  556.  
  557.         if (mask & missed_open_sig)
  558.             missed |= MISSED_OPEN;
  559.  
  560.         if (mask & missed_lock_sig)
  561.             missed |= MISSED_LOCK;
  562.  
  563.         if (mask & missed_loadseg_sig)
  564.             missed |= MISSED_LOADSEG;
  565.  
  566.         if (mask & missed_execute_sig)
  567.             missed |= MISSED_EXECUTE;
  568.  
  569.         if (mask & missed_curdir_sig)
  570.             missed |= MISSED_CURDIR;
  571.  
  572.         if (mask & missed_delete_sig)
  573.             missed |= MISSED_DELETE;
  574.  
  575.         if (mask & portsig) {
  576.             MYMSG *msg;
  577.  
  578.             /*
  579.              *        Note in the following, there is some slightly tricky
  580.              *        stuff to handle the case where a DOS function calls
  581.              *        another DOS function internally. Under 1.3, this doesn't
  582.              *        happen, but it may under WB 2.0 and in addition, Jim
  583.              *        Goodnow's handy Rez utility patches LoadSeg() so that
  584.              *        it calls Open(). `nested' is true when a nested call
  585.              *        like this happens, and in this case, process names are
  586.              *        printed out prefixed by `> ' to indicate the nesting.
  587.              *
  588.              *        The nesting is detected because an incoming Open/Lock/
  589.              *        LoadSeg message will arrive when the cursor is positioned
  590.              *        to print a Fail/Okay result from a previous call (i.e.
  591.              *        col0 is false; the cursor is not in column 0). When a
  592.              *        result arrives in and the cursor IS in column 0, then
  593.              *        this must be the result from an earlier call so the
  594.              *        nesting is ended. Note that the semaphores used ensures
  595.              *        that this situation can only ever occur as a result of
  596.              *        nesting; other tasks will always wait until they are
  597.              *        properly in sync before sending messages to the main
  598.              *        Snoopdos process.
  599.              *
  600.              *        The more alert among you are probably saying something
  601.              *        like "Ah, but what if SnoopDos is configured to display
  602.              *        all DOS calls, even if it means forcing some tasks to
  603.              *        sleep until SnoopDos can process them? Then, when a
  604.              *        task calls LoadSeg() (say) and LoadSeg() calls Open(),
  605.              *        Open() will sleep forever waiting for the semaphore
  606.              *        Obtain()ed by LoadSeg() to become free again." Well,
  607.              *        in fact, it won't, since exec's ObtainSemaphore() call
  608.              *        will suceed even when the semaphore is in use, IF the
  609.              *        caller is the task that owns the semaphore -- and in such
  610.              *        a case, the caller will be the same task. (It took me a
  611.              *        while to figure out exactly what things were working --
  612.              *        it looked like I should get a serious lockup in certain
  613.              *        circumstances).
  614.              *        
  615.              */
  616.             while ((msg = (MYMSG *)GetMsg(myport)) != NULL) {
  617.                 /*
  618.                  *        Get the name of the process/command for
  619.                  *        printing out if necessary.
  620.                  */
  621.                 static char namebuf[256];
  622.                 UBYTE *procname = msg->process->pr_Task.tc_Node.ln_Name;
  623.                 struct CommandLineInterface *cli = BTOC(msg->process->pr_CLI);
  624.                 BPTR curdir = msg->process->pr_CurrentDir;
  625.                 UBYTE *s;
  626.                 UBYTE *filename;
  627.                 int newname;    /* If true, a new name was generated */
  628.  
  629.                 if (!col0 && (msg->msgtype == MSG_OPEN        ||
  630.                               msg->msgtype == MSG_LOCK        ||
  631.                               msg->msgtype == MSG_LOADSEG    ||
  632.                               msg->msgtype == MSG_CURDIR    ||
  633.                               msg->msgtype == MSG_DELETE)) {
  634.                     nested = 1;
  635.                     myfprintf(debugwin, TXT_NEST);
  636.                 }
  637.                 if (cli && cli->cli_Module) {
  638.                     UBYTE *cliname = BTOC(cli->cli_CommandName);
  639.                     if (*cliname > 0) {
  640.                         /* Only use CLI name if it's not null */
  641.                         strncpy(namebuf+2, cliname + 1, *cliname);
  642.                         namebuf[*cliname+2] = '\0';
  643.                         procname = namebuf + 2;
  644.                         if (nested) {
  645.                             /*
  646.                              *        If we're not at column 0, then we're
  647.                              *        calling this DOS function from within
  648.                              *        another DOS function so indent display
  649.                              */
  650.                             procname = namebuf;
  651.                             procname[0] = '>';
  652.                             procname[1] = ' ';
  653.                         }
  654.                     }
  655.                 } else {
  656.                     if (nested) {
  657.                         sprintf(namebuf, "> %s", procname);
  658.                         procname = namebuf;
  659.                     }
  660.                 }
  661.  
  662.                 /*
  663.                  *        Now handle the message
  664.                  */
  665.                 filename = msg->data2;    /* Standard file name            */
  666.                 newname  = 0;            /* No problems expanding it        */
  667.  
  668.                 switch (msg->msgtype) {
  669.  
  670.                     case MSG_QUIT:
  671.                         done = 1;
  672.                         break;
  673.  
  674.                     case MSG_GETOPTIONS:
  675.                         memcpy(msg->data2, &settings, sizeof(SETTINGS));
  676.                         break;
  677.  
  678.                     case MSG_SETOPTIONS:
  679.                         memcpy(&settings, msg->data2, sizeof(SETTINGS));
  680.                         break;
  681.  
  682.                     case MSG_OPEN:
  683.                         if (msg->data1 == MODE_OLDFILE)
  684.                             s = TXT_OLD;
  685.                         else if (msg->data1 == MODE_NEWFILE)
  686.                             s = TXT_NEW;
  687.                         else if (msg->data1 == MODE_READWRITE)
  688.                             s = TXT_R_W;
  689.                         else
  690.                             s = TXT_QM3;
  691.  
  692.                         if (showfullpath) {
  693.                             newname = makepath(fullname, curdir, msg->data2);
  694.                             filename = fullname;
  695.                         }
  696.                         myfprintf(debugwin, "%-21s %s %s%-39s %s ", procname,
  697.                                     TXT_OPEN, POINT(newname), filename, s);
  698.                         col0 = 0;
  699.                         break;  
  700.  
  701.                     case MSG_LOCK:
  702.                         if (msg->data1 == ACCESS_READ)
  703.                             s = TXT_SHAR;
  704.                         else if (msg->data1 == ACCESS_WRITE)
  705.                             s = TXT_EXCL;
  706.                         else
  707.                             s = TXT_QM4;
  708.  
  709.                         if (showfullpath) {
  710.                             newname = makepath(fullname, curdir, msg->data2);
  711.                             filename = fullname;
  712.                         }
  713.                         myfprintf(debugwin, "%-21s %s %s%-39s %s ", procname,
  714.                                     TXT_LOCK, POINT(newname), filename, s);
  715.                         col0 = 0;
  716.                         break;
  717.  
  718.                     case MSG_LOADSEG:
  719.                         if (showfullpath) {
  720.                             newname = makepath(fullname, curdir, msg->data2);
  721.                             filename = fullname;
  722.                         }
  723.                         myfprintf(debugwin, "%-21s %s %s%-44s ", procname,
  724.                                     TXT_LOAD, POINT(newname), filename);
  725.                         col0 = 0;
  726.                         break;
  727.  
  728.                     case MSG_EXECUTE:
  729.                         myfprintf(debugwin, "%-21s %s  %s\r\n", procname,
  730.                                                     TXT_EXEC, filename);
  731.                         col0 = 1;
  732.                         break;
  733.  
  734.                     case MSG_CURDIR:
  735.                         {
  736.                             char *dirname = getlockpath(msg->data1);
  737.                             int i = strlen(dirname);
  738.  
  739.                             if (dirname[i-1] == '/')
  740.                                 dirname[i-1] = '\0';
  741.                             myfprintf(debugwin, "%-21s %s  %s\r\n", procname,
  742.                                                     TXT_CURDIR, dirname);
  743.                             col0 = 1;
  744.                         }
  745.                         break;
  746.  
  747.                     case MSG_DELETE:
  748.                         if (showfullpath) {
  749.                             newname = makepath(fullname, curdir, msg->data2);
  750.                             filename = fullname;
  751.                         }
  752.                         myfprintf(debugwin, "%-21s %s %s%-44s ", procname,
  753.                                     TXT_DELETE, POINT(newname), filename);
  754.                         col0 = 0;
  755.                         break;
  756.  
  757.                     case MSG_LOCK_DONE:
  758.                     case MSG_OPEN_DONE:
  759.                     case MSG_LOADSEG_DONE:
  760.                     case MSG_DELETE_DONE:
  761.                         if (col0 && nested) {
  762.                             myfprintf(debugwin, "%-73s", TXT_DONE);
  763.                             nested = 0;
  764.                         }
  765.                         if (msg->data1)
  766.                             myfprintf(debugwin, TXT_OKAY);
  767.                         else
  768.                             myfprintf(debugwin, TXT_FAIL);
  769.                         col0 = 1;
  770.                         break;
  771.                 }
  772.                 ReplyMsg(msg);
  773.             }
  774.         }
  775.  
  776.         /*
  777.          *        Finally, check if we missed a DOS function. If we did,
  778.          *        AND we are in column 0, print out an appropriate message.
  779.          *        Otherwise, wait until next time. This stops the display
  780.          *        getting messed up when waiting to print a 'Fail' or 'Okay'.
  781.          */
  782.         if (missed) {
  783.             if (col0) {
  784.                 if (missed & MISSED_OPEN)
  785.                     myfprintf(debugwin, "%s an Open()\r\n", TXT_WARN);
  786.                 if (missed & MISSED_LOCK)
  787.                     myfprintf(debugwin, "%s a Lock()\r\n", TXT_WARN);
  788.                 if (missed & MISSED_LOADSEG)
  789.                     myfprintf(debugwin, "%s a LoadSeg()\r\n", TXT_WARN);
  790.                 if (missed & MISSED_EXECUTE)
  791.                     myfprintf(debugwin, "%s an Execute()\r\n", TXT_WARN);
  792.                 if (missed & MISSED_CURDIR)
  793.                     myfprintf(debugwin, "%s a CurrentDir()\r\n", TXT_WARN);
  794.                 if (missed & MISSED_DELETE)
  795.                     myfprintf(debugwin, "%s a DeleteFile()\r\n", TXT_WARN);
  796.                 missed = MISSED_NONE;
  797.             }
  798.         }
  799.     }
  800.  
  801.     /*
  802.      *        Remove the port from the public ports list; this stops any
  803.      *        SnoopDos processes started up elsewhere from trying to
  804.      *        communicate with us (which may happen if this process can't
  805.      *        exit immediately).
  806.      */
  807.     RemPort(myport);
  808.     myport->mp_Node.ln_Name = NULL;
  809.     snoopactive = 0;    /* Make sure our patch stops sending msgs to us! */
  810.  
  811.     /*
  812.      *        Now try and obtain our semaphore. Until we can achieve it,
  813.      *        we may still be receiving messages from other tasks telling
  814.      *        us about DOS operations, so, we reply to these as they arrive
  815.      *        (else deadlock occurs).
  816.      */
  817.     while (!AttemptSemaphore(sem)) {
  818.         MYMSG *msg = (MYMSG *)GetMsg(myport);
  819.  
  820.         if (msg)
  821.             ReplyMsg(msg);
  822.         Delay(5);                /* Wait for 0.2 seconds */
  823.     }
  824.  
  825.     /*
  826.      *        Now remove the dospatch, and cleanup
  827.      */
  828.     if (!uninstalldospatch()) {
  829.         /*
  830.          *        Someone else has patched DOSbase so print a message for
  831.          *        the user and keep trying every 5 seconds until we succeed.
  832.          */
  833.         int count = 0;
  834.  
  835.         myfprintf(debugwin,
  836. "\r\n"
  837. "Someone else has patched dos.library so I'll have to wait until they quit."
  838. "\r\n");
  839.         if (!extfile) {
  840.             myfprintf(debugwin, 
  841.   "This window will close shortly though to stop it from annoying you.\r\n");
  842.         }
  843.  
  844.         do {
  845.             Delay(50);                    /* Wait for one second */
  846.             if (debugwin) {
  847.                 count++;
  848.                 if (count > 10) {
  849.                     Close(debugwin);
  850.                     debugwin = NULL;
  851.                 }
  852.             }
  853.         } while (!uninstalldospatch());
  854.     }
  855.  
  856.     if (debugwin)
  857.         myfprintf(debugwin, "\r\nSnoopDos terminated.\r\n");
  858.  
  859.     /*
  860.      *        We do a final wait for 0.2 seconds, just in case their are any
  861.      *        other tasks still running in our SetFunction'd code.
  862.      *        (This isn't 100% bulletproof, but it's fairly safe).
  863.      */
  864.     Delay(10);
  865.     cleanup(-1);
  866. }
  867.  
  868. /*
  869.  *        main()
  870.  *        ------
  871.  *        Mainline. Parses the command line and, if necessary, kicks off the
  872.  *        background task to do the real work.
  873.  */
  874. void main(int argc, char **argv)
  875. {
  876.     int error  = 0;            /* True if error detected in cmd line    */
  877.     int quit   = 0;            /* True if we want to quit                */
  878.     int colsel = 0;            /* True if colour option was specified    */
  879.  
  880.     /*
  881.      *        See if we are already active elsewhere; if yes, then read in
  882.      *        the default settings used by that process.
  883.      */
  884.  
  885.     myport = FindPort(PORTNAME);
  886.     if (myport)
  887.         sendmsg(MSG_GETOPTIONS, 0, &settings);
  888.  
  889.     while (argc > 1 && argv[1][0] == '-') {
  890.         int val;
  891.  
  892.         /* Make -X == -x0 and -x == -x1 */
  893.         if (argv[1][1] >= 'a' && argv[1][2] != '0')
  894.             val = 1;
  895.         else
  896.             val = 0;
  897.  
  898.         switch (tolower(argv[1][1])) {
  899.  
  900.             case 'a': colour         = val;
  901.                       colsel        = 1;   break;    /* Display ANSI colour    */
  902.             case 'c': docurdir        = val; break;    /* Monitor CurrentDir() */
  903.             case 'd': dodelete        = val; break;    /* Monitor DeleteFile()    */
  904.             case 'f': showfullpath    = val; break;    /* Show full filepath    */
  905.             case 'g': doloadseg        = val; break;    /* Monitor LoadSeg()    */
  906.             case 'h': error            = 1;   break;    /* Just print help msg    */
  907.             case 'l': dolock        = val; break;    /* Monitor Lock()        */
  908.             case 'm': snoopactive    = val; break;    /* Actively monitoring    */
  909.             case 'o': doopen        = val; break;    /* Monitor Open()        */
  910.             case 'q': quit            = 1;   break;    /* Ask SnoopDos to quit */
  911.             case 'r': error            = 2;   break;    /* Report settings        */
  912.             case 'w': sleepwait        = val; break;    /* Wait when necessary    */
  913.             case 'x': doexecute        = val; break;    /* Monitor Execute()    */
  914.             case 'z':
  915.                     if (argv[1][2]) {
  916.                         strcpy(extfilename, &argv[1][2]);
  917.                         extfile = 1;
  918.                     } else {
  919.                         argv++;
  920.                         argc--;
  921.                         if (argc > 1) {
  922.                             strcpy(extfilename, argv[1]);
  923.                             extfile = 1;
  924.                         }
  925.                     }
  926.                     if (!extfile) {
  927.                         myprintf(
  928.         "-z must be followed by a filename. Type SnoopDos -h for help.\n");
  929.                         exit(5);
  930.                     }
  931.                     /*
  932.                      *        If outputting to a file, make colour off
  933.                      *        by default, rather than on.
  934.                      */
  935.                     if (colsel == 0)
  936.                         colour = 0;
  937.                     break;
  938.  
  939.             default:
  940.                 myprintf("Unrecognised option %s. "
  941.                          "Type Snoopdos -h for help.\n", argv[1]);
  942.                 exit(5);
  943.         }
  944.         argv++; argc--;
  945.     }
  946.  
  947.     if (argc > 1)
  948.         error = 1;
  949.  
  950.     if (error) {
  951.         if (error == 1) {
  952.             myprintf(TITLE "\n\n"
  953. "SnoopDos monitors calls made to various AmigaDOS functions by all processes\n"
  954. "on the system. The following options are available. Use -x1 or just -x to\n"
  955. "enable an option, -x0 or -X to disable that option. Current settings in (-)."
  956. "\n\n");
  957.         } else
  958.             myprintf("Current SnoopDos settings:\n\n");
  959.  
  960. #define O(x,y) myprintf(x, y ? "(on)" : "(off)")
  961. O("    -a  Use ANSI colour sequences                     %s\n", colour);
  962. O("    -c  Monitor CurrentDir() calls                    %s\n", docurdir);
  963. O("    -d  Monitor DeleteFile() calls                    %s\n", dodelete);
  964. O("    -f  Display full filename paths                   %s\n", showfullpath);
  965. O("    -g  Monitor LoadSeg() calls                       %s\n", doloadseg);
  966. O("    -l  Monitor Lock() calls                          %s\n", dolock);
  967. O("    -m  Globally enable/disable monitoring            %s\n", snoopactive);
  968. O("    -o  Monitor Open() calls                          %s\n", doopen);
  969. O("    -w  Display all activity, sleeping if necessary   %s\n", sleepwait);
  970. O("    -x  Monitor Execute() calls                       %s\n", doexecute);
  971.  
  972.         if (error == 1) {
  973.             myprintf("\n"
  974. "    -h  Print out this help message\n"
  975. "    -q  Tell SnoopDos to quit\n"
  976. "    -r  Report current SnoopDos settings\n" 
  977. "    -z$ Output to file $ (e.g. -zCON:0/0/640/100/SnoopDos or -zSER:)\n"
  978. "\n");
  979.         }
  980.         cleanup(5);
  981.     }
  982.  
  983.     /*
  984.      *        First see are we already installed. If so, send a copy of the
  985.      *        updated settings to the background process, or send a QUIT
  986.      *        message if the user wanted to quit.
  987.      */
  988.     if (myport) {
  989.         if (quit)
  990.             sendmsg(MSG_QUIT, 0, 0);
  991.         else
  992.             sendmsg(MSG_SETOPTIONS, 0, &settings);
  993.         cleanup(0);
  994.     }
  995.  
  996.     /*
  997.      *        If user wanted to quit and we weren't already running, just
  998.      *        quit without doing anything.
  999.      */
  1000.     if (quit) {
  1001.         myprintf("There is no background SnoopDos process to tell to quit!\n");
  1002.         cleanup(0);
  1003.     }
  1004.  
  1005.     /*
  1006.      *        Not installed, so install ourselves. First of all though, we
  1007.      *        kick ourselves into the background and return control to the
  1008.      *        calling CLI. We need to do this before we start creating ports
  1009.      *        and opening files, to prevent Exec and AmigaDOS getting
  1010.      *        confused about which task we are.
  1011.      *
  1012.      *        Also open stderr channel for the new task to output fatal
  1013.      *        error messages to; the new task takes responsibility for closing
  1014.      *        this channel.
  1015.      */
  1016.     stderr = Open("*", MODE_NEWFILE);
  1017.     if (!res("SnoopDos", 5, mainloop, 4000)) {
  1018.         myprintf("Couldn't spawn background SnoopDos task.\n");
  1019.         cleanup(10);
  1020.     }
  1021. }
  1022.  
  1023. /*
  1024.  *        NewOpen()
  1025.  *        ---------
  1026.  *        This is the new Open() code. It checks to see if the calling task
  1027.  *        is actually the SnoopDos task; if it is, then it essentially does
  1028.  *        nothing (this stops any possible deadlock occurring). Otherwise,
  1029.  *        it sends a message to the task with the pertinent info.
  1030.  *
  1031.  *        If sleeping is disabled then if the semaphore isn't immediately
  1032.  *        available, a signal is sent to the Snoopdos task telling it that
  1033.  *        it's missed trapping a function.
  1034.  */
  1035. __asm BPTR NewOpen(reg_d1 char *filename, reg_d2 int mode)
  1036. {
  1037.     BPTR filehandle;
  1038.     geta4();
  1039.     if (snoopactive && doopen && FindTask(0) != snooptask) {
  1040.         if (sleepwait)
  1041.             ObtainSemaphore(sem);
  1042.         else if (!AttemptSemaphore(sem)) {
  1043.             Signal(snooptask, missed_open_sig);
  1044.             return (CallOpen(filename, mode));
  1045.         }
  1046.         sendmsg(MSG_OPEN, mode, filename);
  1047.         filehandle = CallOpen(filename, mode);
  1048.         sendmsg(MSG_OPEN_DONE, filehandle, 0);
  1049.         ReleaseSemaphore(sem);
  1050.         return (filehandle);
  1051.     } else {
  1052.         return (CallOpen(filename, mode));
  1053.     }
  1054. }
  1055. /*
  1056.  *        NewLock()
  1057.  *        ---------
  1058.  *        Replacement for Lock(), all the comments for NewOpen() apply
  1059.  *        equally here.
  1060.  */
  1061. __asm BPTR NewLock(reg_d1 char *filename, reg_d2 int mode)
  1062. {
  1063.     BPTR lock;
  1064.     geta4();
  1065.     if (snoopactive && dolock && FindTask(0) != snooptask) {
  1066.         if (sleepwait)
  1067.             ObtainSemaphore(sem);
  1068.         else if (!AttemptSemaphore(sem)) {
  1069.             Signal(snooptask, missed_lock_sig);
  1070.             return (CallLock(filename, mode));
  1071.         }
  1072.         sendmsg(MSG_LOCK, mode, filename);
  1073.         lock = CallLock(filename, mode);
  1074.         sendmsg(MSG_LOCK_DONE, lock, 0);
  1075.         ReleaseSemaphore(sem);
  1076.         return (lock);
  1077.     } else {
  1078.         return (CallLock(filename, mode));
  1079.     }
  1080. }
  1081.  
  1082. /*
  1083.  *        NewLoadSeg()
  1084.  *        ------------
  1085.  *        Replacement for Lock(), all the comments for NewOpen() apply
  1086.  *        equally here.
  1087.  */
  1088. __asm BPTR NewLoadSeg(reg_d1 char *filename)
  1089. {
  1090.     BPTR seglist;
  1091.     geta4();
  1092.     if (snoopactive && doloadseg && FindTask(0) != snooptask) {
  1093.         if (sleepwait)
  1094.             ObtainSemaphore(sem);
  1095.         else if (!AttemptSemaphore(sem)) {
  1096.             Signal(snooptask, missed_loadseg_sig);
  1097.             return (CallLoadSeg(filename));
  1098.         }
  1099.         sendmsg(MSG_LOADSEG, 0, filename);
  1100.         seglist = CallLoadSeg(filename);
  1101.         sendmsg(MSG_LOADSEG_DONE, seglist, 0);
  1102.         ReleaseSemaphore(sem);
  1103.         return (seglist);
  1104.     } else {
  1105.         return (CallLoadSeg(filename));
  1106.     }
  1107. }
  1108.  
  1109. /*
  1110.  *        NewExecute()
  1111.  *        ------------
  1112.  *        Replacement for Execute()
  1113.  */
  1114. __asm LONG NewExecute(reg_d1 char *command, reg_d2 BPTR in, reg_d3 BPTR out)
  1115. {
  1116.     geta4();
  1117.     if (snoopactive && doexecute && FindTask(0) != snooptask) {
  1118.         if (sleepwait)
  1119.             ObtainSemaphore(sem);
  1120.         else if (!AttemptSemaphore(sem)) {
  1121.             Signal(snooptask, missed_execute_sig);
  1122.             return(CallExecute(command, in, out));
  1123.         }
  1124.         sendmsg(MSG_EXECUTE, 0, command);
  1125.         ReleaseSemaphore(sem);
  1126.     }
  1127.     return(CallExecute(command, in, out));
  1128. }
  1129.  
  1130. /*
  1131.  *        NewCurrentDir()
  1132.  *        ---------------
  1133.  *        Replacement for CurrentDir()
  1134.  */
  1135. __asm BPTR NewCurrentDir(reg_d1 BPTR newlock)
  1136. {
  1137.     geta4();
  1138.     if (snoopactive && docurdir && FindTask(0) != snooptask) {
  1139.         if (sleepwait)
  1140.             ObtainSemaphore(sem);
  1141.         else if (!AttemptSemaphore(sem)) {
  1142.             Signal(snooptask, missed_curdir_sig);
  1143.             return(CallCurrentDir(newlock));
  1144.         }
  1145.         sendmsg(MSG_CURDIR, newlock, 0);
  1146.         ReleaseSemaphore(sem);
  1147.     }
  1148.     return(CallCurrentDir(newlock));
  1149. }
  1150.  
  1151. /*
  1152.  *        NewDeleteFile()
  1153.  *        ---------------
  1154.  *        Replacement for DeleteFile()
  1155.  */
  1156. __asm LONG NewDeleteFile(reg_d1 char *filename)
  1157. {
  1158.     LONG success;
  1159.     geta4();
  1160.     if (snoopactive && dodelete && FindTask(0) != snooptask) {
  1161.         if (sleepwait)
  1162.             ObtainSemaphore(sem);
  1163.         else if (!AttemptSemaphore(sem)) {
  1164.             Signal(snooptask, missed_delete_sig);
  1165.             return(CallDeleteFile(filename));
  1166.         }
  1167.         sendmsg(MSG_DELETE, 0, filename);
  1168.         success = CallDeleteFile(filename);
  1169.         sendmsg(MSG_DELETE_DONE, success, 0);
  1170.         ReleaseSemaphore(sem);
  1171.     } else
  1172.         return(CallDeleteFile(filename));
  1173. }
  1174.