home *** CD-ROM | disk | FTP | other *** search
/ Club Amiga de Montreal - CAM / CAM_CD_1.iso / files / 230.lha / SPY / Sources / Spy.Mod < prev    next >
Text File  |  1989-04-08  |  16KB  |  539 lines

  1. MODULE Spy;
  2.  
  3.         (************************************************)
  4.         (*    Spy - A Task Control Block Snooper        *)
  5.         (*                                              *)
  6.         (*  Written by Steve Faiwiszewski,   June 1988  *)
  7.         (*                                              *)
  8.         (*   Not to be used for commercial purpose      *)
  9.         (************************************************)
  10.  
  11. FROM Termination IMPORT ExitGracefully, AddTerminator;
  12. FROM Snoop      IMPORT  WINDOWLEFT, WINDOWTOP, WIDTH,
  13.                         HEIGHT, SpyWindow, Observe;
  14. FROM Nodes      IMPORT  Node, NodePtr, NTProcess;
  15. FROM Heap       IMPORT  ALLOCATE, FreeHeap;
  16. FROM TermInOut  IMPORT  WriteLn, WriteString, WriteCard,
  17.                         Write, WriteHex;
  18. FROM Strings    IMPORT  StringLength;
  19. FROM Tasks      IMPORT  Task, TaskPtr, CurrentTask,
  20.                         TaskState, FindTask, SignalSet, Wait;
  21. FROM Interrupts IMPORT  Forbid, Permit;
  22. FROM Rasters    IMPORT  Jam1, Jam2, RastPortPtr;
  23. FROM System     IMPORT  argc, argv, ExecBase;
  24. FROM ExecBase   IMPORT  ExecBasePtr;
  25. FROM Ports      IMPORT  MsgPortPtr, MessagePtr, GetMsg,
  26.                         ReplyMsg, WaitPort;
  27. FROM Text       IMPORT  Text;
  28. FROM Drawing    IMPORT  Move, Draw, SetAPen, SetBPen,
  29.                         WritePixel, RectFill, SetDrMd;
  30. FROM Intuition  IMPORT  WindowFlags, WindowFlagsSet,
  31.                         IDCMPFlagsSet, IDCMPFlags,
  32.                         GadgetActivation,
  33.                         WindowPtr, CloseWindow, RemoveGadget,
  34.                         GadgetPtr, PropInfoPtr, ModifyIDCMP,
  35.                         IntuiMessagePtr, DoubleClick;
  36. FROM SYSTEM     IMPORT  ADDRESS, ADR, WORD, LONGWORD, BYTE,
  37.                         TSIZE;
  38. FROM IntuiCommon
  39.                 IMPORT  OpenSimpleWindow;
  40. FROM Conversions
  41.                 IMPORT  ConvStringToNumber;
  42. FROM AmigaDOSProcess
  43.                 IMPORT  ProcessPtr;
  44. FROM AmigaDOSExt
  45.                 IMPORT  CommandLineInterfacePtr;
  46. FROM InputEvents
  47.                 IMPORT  IECodeLButton;
  48. FROM SimpleGadgets
  49.                 IMPORT  BeginGadgetList, EndGadgetList,
  50.                         LastGadget, AddGadgetProp,
  51.                         FreeGadgetList;
  52.  
  53. CONST
  54.     PROPLEFT = 280;
  55.     PROPTOP = 10; 
  56.     PROPWIDTH = WIDTH - PROPLEFT;
  57.     PROPHEIGHT = HEIGHT - PROPTOP - 1;
  58.     LetterHeight = 9;
  59.     LetterWidth = 8;
  60.     MaxNameLength = (PROPLEFT DIV LetterWidth) - 1;
  61.     MaxDisplayLines = PROPHEIGHT DIV LetterHeight;
  62.     
  63. TYPE
  64.     MyNodePtr = POINTER TO MyNode;
  65.  
  66.     MyNode = RECORD
  67.                 address : ADDRESS;
  68.                 next : MyNodePtr;
  69.              END;
  70.  
  71. VAR
  72.     TargetTask : TaskPtr;
  73.     ExecBaseP : ExecBasePtr;
  74.     MyGadList : GadgetPtr;
  75.     PIptr : PropInfoPtr;
  76.     Divisor : CARDINAL;
  77.     PreviousSelectedLine : CARDINAL;
  78.     PreviousSelectedItemPtr : MyNodePtr;
  79.     Blanks : ARRAY[0..MaxNameLength-1] OF CHAR;
  80.     CloseTheWindow : BOOLEAN;
  81.  
  82.  
  83. (* +++++++++++++++++++++++++++++++++++++++++++++++++++++ *)
  84. PROCEDURE CopyList(n : NodePtr; VAR tail : MyNodePtr;
  85.                    VAR count : CARDINAL): MyNodePtr;
  86. (* make a copy of the list while multitasking is FORBIDen *)
  87. VAR
  88.     tmp,
  89.     head : MyNodePtr;
  90. BEGIN
  91.     head := NIL;
  92.     tail := NIL;
  93.     WHILE (n <> NIL) AND (n^.lnSucc <> NIL) DO
  94.         INC(count);
  95.         ALLOCATE(tmp,TSIZE(MyNode));
  96.         WITH tmp^ DO
  97.             address := n;
  98.             next := head;
  99.         END;
  100.         head := tmp;
  101.         IF tail = NIL THEN tail := tmp END;
  102.         n := n^.lnSucc;
  103.     END; (* while n <> NIL *)
  104.     RETURN head
  105. END CopyList;
  106.  
  107. (* +++++++++++++++++++++++++++++++++++++++++++++++++++++ *)
  108. PROCEDURE BuildTaskList(VAR total : CARDINAL) : MyNodePtr;
  109. (* Build a list of all the tasks on the system *)
  110. VAR
  111.     tail,
  112.     tail2,
  113.     tmp,
  114.     MyTaskList : MyNodePtr;
  115. BEGIN
  116.     Forbid;
  117.     total := 0;
  118.     MyTaskList := NIL;
  119.     WITH ExecBaseP^ DO
  120. (* First get all the "ready" tasks *)
  121.         MyTaskList := CopyList(TaskReady.lhHead,tail,total);
  122. (* Now get all the "waiting" tasks *)
  123.         tmp := CopyList(TaskWait.lhHead,tail2,total);
  124.         IF MyTaskList = NIL THEN
  125.             MyTaskList := tmp
  126.         ELSE
  127.             tail^.next := tmp
  128.         END
  129.     END;
  130.     Permit;
  131.     RETURN MyTaskList;
  132. END BuildTaskList;
  133.  
  134.  
  135. (* +++++++++++++++++++++++++++++++++++++++++++++++++++++ *)
  136. PROCEDURE VerifyTaskIsReal(TargetTask : ADDRESS) : BOOLEAN;
  137.  
  138. (* Make sure that the task we'll be trying to spy on is   *)
  139. (* a real one (i.e. it's not a bogus address and the task *)
  140. (* hasn't disappeared on us.                              *)
  141.  
  142. VAR
  143.     t : MyNodePtr;
  144.     found : BOOLEAN;
  145.     total : CARDINAL;
  146. BEGIN
  147.     t := BuildTaskList(total);
  148.     found := FALSE;
  149.     WHILE (t <> NIL) AND NOT found DO
  150.         found := t^.address = TargetTask;
  151.         t := t^.next
  152.     END;
  153.     FreeHeap;
  154.     RETURN found
  155. END VerifyTaskIsReal;
  156.  
  157.  
  158. (* +++++++++++++++++++++++++++++++++++++++++++++++++++++ *)
  159. PROCEDURE Min(x,y : CARDINAL): CARDINAL;
  160. BEGIN
  161.     IF x > y THEN RETURN y ELSE RETURN x END
  162. END Min;
  163.  
  164. (* +++++++++++++++++++++++++++++++++++++++++++++++++++++ *)
  165. PROCEDURE Len(s : ADDRESS) : CARDINAL;
  166. (* Calculate the length of a string pointed to by s *)
  167. VAR cp : POINTER TO CHAR;
  168.     i  : CARDINAL;
  169. BEGIN
  170.     cp := s;
  171.     i := 0;
  172.     WHILE cp^ <> 0C DO
  173.         INC(i);
  174.         cp := ADDRESS(LONGCARD(cp) + 1D);
  175.     END;
  176.     RETURN i
  177. END Len;
  178.  
  179.  
  180. (* +++++++++++++++++++++++++++++++++++++++++++++++++++++ *)
  181. PROCEDURE PrintTaskName(RP : RastPortPtr; t : MyNodePtr;
  182.                         line, APen, BPen : CARDINAL);
  183. (* Print a task's name.  If it also happens to be a process *)
  184. (* then print the process (command) name instead.           *)
  185. VAR
  186.     tp : TaskPtr;
  187.     pp : ProcessPtr;
  188.     CliPtr : CommandLineInterfacePtr;
  189.     NameP : POINTER TO CHAR;
  190.     y,len : CARDINAL;
  191. BEGIN
  192.     tp := t^.address;
  193.     NameP := tp^.tcNode.lnName;
  194.     IF CHAR(tp^.tcNode.lnType) = CHAR(NTProcess) THEN
  195.         pp := ProcessPtr(tp);
  196.         IF pp^.prCLI <> NIL THEN
  197.             CliPtr := ADDRESS(LONGCARD(pp^.prCLI)*4D);
  198.             NameP := 
  199.                 ADDRESS(LONGCARD(CliPtr^.cliCommandName)*4D);
  200.             IF NameP^ = 0C THEN
  201.                 NameP := ADR('(No Command)')
  202.             ELSE
  203.                 NameP := ADDRESS(LONGCARD(NameP) + 1D)
  204.             END;
  205.         END
  206.     END;
  207.     y := 10 + line * LetterHeight;
  208.     SetAPen(RP^,0); SetBPen(RP^,0);
  209.     RectFill(RP^,5,y,PROPLEFT-2,y+LetterHeight);
  210.     SetAPen(RP^,APen);
  211.     SetBPen(RP^,BPen);
  212.     Move(RP^,5,y + LetterHeight - 2);
  213.     len := Len(NameP);
  214.     IF len > MaxNameLength THEN len := MaxNameLength END;
  215.     Text(RP^,NameP,len);
  216.     Text(RP^,ADR(Blanks),MaxNameLength - len);
  217. END PrintTaskName;
  218.  
  219.  
  220. (* +++++++++++++++++++++++++++++++++++++++++++++++++++++ *)
  221. PROCEDURE CleanUp;
  222. VAR i : INTEGER;
  223. BEGIN
  224.     IF CloseTheWindow AND (SpyWindow <> NIL) THEN
  225.         CloseWindow(SpyWindow^);
  226.         SpyWindow := NIL
  227.     END;
  228.     IF SpyWindow <> NIL THEN
  229.         i := RemoveGadget(SpyWindow^,MyGadList^);
  230.     END;
  231.     IF MyGadList <> NIL THEN
  232.         FreeGadgetList(MyGadList^);
  233.         MyGadList := NIL
  234.     END;
  235.     FreeHeap;
  236. END CleanUp;
  237.  
  238.  
  239. (* +++++++++++++++++++++++++++++++++++++++++++++++++++++ *)
  240. PROCEDURE CalculateTaskFromItem(item : CARDINAL;
  241.                                 TaskList : MyNodePtr): ADDRESS;
  242. (* Find out which task corresponds to position number `item' *)
  243. VAR
  244.     t : MyNodePtr;
  245.     i : CARDINAL;
  246. BEGIN
  247.     t := TaskList;
  248.     FOR i := 1 TO item-1 DO
  249.         t := t^.next
  250.     END;
  251.     RETURN t^.address
  252. END CalculateTaskFromItem;
  253.  
  254.  
  255. (* +++++++++++++++++++++++++++++++++++++++++++++++++++++ *)
  256. PROCEDURE SelectItem(item, FirstItem : CARDINAL;
  257.                      RP : RastPortPtr; TaskList : MyNodePtr);
  258. (* Highlight the name of the task the user just clicked on *)
  259. VAR
  260.     i,
  261.     line : CARDINAL;
  262.     t : MyNodePtr;
  263. BEGIN
  264.     IF PreviousSelectedItemPtr <> NIL THEN
  265.         PrintTaskName(RP,PreviousSelectedItemPtr,
  266.                          PreviousSelectedLine,1,0);
  267.     END;
  268.     line := item - FirstItem;
  269.     t := TaskList;
  270.     FOR i := 1 TO item-1 DO
  271.         t := t^.next
  272.     END;
  273.     PrintTaskName(RP,t,line,0,1);
  274.     PreviousSelectedItemPtr := t;
  275.     PreviousSelectedLine := line;
  276. END SelectItem;
  277.  
  278.  
  279. (* +++++++++++++++++++++++++++++++++++++++++++++++++++++ *)
  280. PROCEDURE CalculateFirstItem(TotalTasks : CARDINAL) : CARDINAL;
  281. (* Calculate which task is the first on the display *)
  282. VAR FirstItem : CARDINAL;
  283. BEGIN
  284.     FirstItem := PIptr^.VertPot DIV Divisor + 1;
  285.     IF FirstItem > (TotalTasks + 1 - MaxDisplayLines) THEN
  286.         FirstItem := TotalTasks + 1 - MaxDisplayLines
  287.     END;
  288.     RETURN FirstItem
  289. END CalculateFirstItem;
  290.  
  291.  
  292. (* +++++++++++++++++++++++++++++++++++++++++++++++++++++ *)
  293. PROCEDURE OpenTaskWindow(VAR Divisor : CARDINAL;
  294.                          VAR MyProp : GadgetPtr;
  295.                          VAR PIptr : PropInfoPtr): SignalSet;
  296. VAR
  297.     i : CARDINAL;
  298. BEGIN
  299.     FOR i := 0 TO MaxNameLength - 1 DO Blanks[i] := ' ' END;
  300.     BeginGadgetList;
  301.     AddGadgetProp(PROPLEFT,PROPTOP,PROPWIDTH,PROPHEIGHT,
  302.                         FALSE,TRUE,1,1,1,Divisor);
  303.     MyProp := LastGadget;
  304.    (* Add GadgImmediate so we get GadgetDown event *)
  305.     INCL(MyProp^.Activation,GadgImmediate);
  306.     PIptr := MyProp^.SpecialInfo;
  307.     MyGadList := EndGadgetList();
  308.     SpyWindow := OpenSimpleWindow(WIDTH,HEIGHT,WINDOWLEFT,
  309.                 WINDOWTOP,
  310.                 ADR('Snoop: List of Tasks'),
  311.                 WindowFlagsSet{Activate,WindowDrag,
  312.                       WindowDepth,WindowClose, NoCareRefresh},
  313.                 IDCMPFlagsSet{MouseButtons,GadgetDown,
  314.                                 GadgetUp,Closewindow},
  315.                 MyGadList,NIL);
  316.     SetAPen(SpyWindow^.RPort^,1);
  317.     SetDrMd(SpyWindow^.RPort^,Jam2);
  318.     RETURN SignalSet{CARDINAL(SpyWindow^.UserPort^.mpSigBit)};
  319. END OpenTaskWindow;
  320.  
  321.  
  322. (* +++++++++++++++++++++++++++++++++++++++++++++++++++++ *)
  323. PROCEDURE GetTaskFromUser(VAR task : ADDRESS) : BOOLEAN;
  324. (* Display the list of tasks that are currently in the  *)
  325. (* system.                                              *)
  326. (* Wait for the user to either choose one task, or to   *)
  327. (* exit.                                                *)
  328. VAR
  329.     sig,
  330.     MySig          : SignalSet;
  331.     msg            : IntuiMessagePtr;
  332.     PreviousSecs,
  333.     PreviousMicros : LONGCARD;
  334.     MyProp         : GadgetPtr;
  335.     good,
  336.     done           : BOOLEAN;
  337.     TotalTasks,
  338.     FirstItem,
  339.     PreviousItem   : CARDINAL;
  340.     TaskList       : MyNodePtr;
  341.  
  342. (* ------------------------------- *)
  343. PROCEDURE NeedUpdate() : BOOLEAN;
  344. (* Check if display needs to be refreshed *)
  345. VAR NewFirstItem : CARDINAL;
  346. BEGIN
  347.     IF TotalTasks <= MaxDisplayLines THEN RETURN FALSE END;
  348.     NewFirstItem := CalculateFirstItem(TotalTasks);
  349.     IF NewFirstItem = FirstItem THEN
  350.         RETURN FALSE
  351.     ELSE
  352.         RETURN TRUE
  353.     END
  354. END NeedUpdate;
  355.  
  356. (* ------------------------------- *)
  357. PROCEDURE DisplayIt(RP : RastPortPtr);
  358. (* Display the list of tasks *)
  359. VAR
  360.     t : MyNodePtr;
  361.     i,
  362.     LastItem : CARDINAL;
  363. BEGIN
  364.     PreviousSelectedItemPtr := NIL;
  365.     IF TotalTasks <= MaxDisplayLines THEN
  366.         FirstItem := 1
  367.     ELSE
  368.         FirstItem := CalculateFirstItem(TotalTasks)
  369.     END;
  370.     LastItem := Min(FirstItem + MaxDisplayLines - 1,
  371.                     TotalTasks);
  372.     t := TaskList;
  373.     FOR i := 1 TO FirstItem-1 DO
  374.         IF t <> NIL THEN t := t^.next END
  375.     END;
  376.     FOR i := FirstItem TO LastItem DO
  377.         IF t = NIL THEN RETURN END;
  378.         PrintTaskName(RP,t,(i - FirstItem),1,0);
  379.         t := t^.next
  380.     END;
  381. END DisplayIt;
  382.  
  383. (* ------------------------------- *)
  384. PROCEDURE CalcItem(x,y : INTEGER): CARDINAL;
  385. (* Find out which task was selected.                       *)
  386. (* Return the task's position number in the list of tasks. *)
  387. VAR item : CARDINAL;
  388. BEGIN
  389.     item := CARDINAL(y + 1 - LetterHeight) DIV LetterHeight;
  390.     IF item > (MaxDisplayLines - 1) THEN
  391.         item := MaxDisplayLines - 1
  392.     END;
  393.     IF item <= TotalTasks THEN
  394.         RETURN item
  395.     ELSE
  396.         RETURN 0
  397.     END
  398. END CalcItem;
  399.  
  400. (* ------------------------------- *)
  401. PROCEDURE ProcessIntuiMsgs(msg : IntuiMessagePtr;
  402.                            VAR done, good : BOOLEAN);
  403. VAR
  404.     item : CARDINAL;
  405.     secs,
  406.     micros : LONGCARD;
  407.     address : ADDRESS;
  408.     class  : IDCMPFlagsSet;
  409.     code : CARDINAL;
  410.     mx,my : INTEGER;
  411. BEGIN
  412.     WITH msg^ DO
  413.         class := Class;
  414.         address := IAddress;
  415.         code := Code;
  416.         mx := MouseX;
  417.         my := MouseY;
  418.         secs := Seconds;
  419.         micros := Micros;
  420.         ReplyMsg(msg)
  421.     END; (* with *)
  422.     IF Closewindow IN class THEN (* User wants out *)
  423.         done := TRUE
  424.     ELSIF GadgetDown IN class THEN
  425.         IF (address = MyProp) AND
  426.                          (TotalTasks > MaxDisplayLines) THEN
  427. (* User clicked on slider, so start listening to IntuiTicks *)
  428.             ModifyIDCMP(SpyWindow^, SpyWindow^.IDCMPFlags +
  429.                         IDCMPFlagsSet{IntuiTicks})
  430.         END
  431.     ELSIF GadgetUp IN class THEN
  432.         IF address = MyProp THEN
  433. (* User released slider, so stop listening to IntuiTicks *)
  434.             ModifyIDCMP(SpyWindow^, SpyWindow^.IDCMPFlags -
  435.                         IDCMPFlagsSet{IntuiTicks})
  436.         END
  437.     ELSIF IntuiTicks IN class THEN
  438. (* Got a clock tick, so check if we need to refresh display *)
  439.         IF NeedUpdate() THEN
  440.             DisplayIt(SpyWindow^.RPort)
  441.         END
  442.     ELSIF MouseButtons IN class THEN
  443.         IF code = IECodeLButton THEN
  444.             item := CalcItem(mx,my) + 1;
  445.             item := FirstItem + item - 1;
  446.             IF (PreviousItem = item) AND
  447.                 DoubleClick(PreviousSecs,PreviousMicros,
  448.                                         secs,micros) THEN
  449. (* User picked a task to spy on *)
  450.                 task := CalculateTaskFromItem(item,TaskList);
  451.                 done := TRUE;
  452.                 good := TRUE
  453.             ELSE
  454.         (* User is thinking about spying on a task, *)
  455.         (* so let's highlight it                    *)
  456.                 PreviousItem := item;
  457.                 PreviousSecs := secs;
  458.                 PreviousMicros := micros;
  459.                 SelectItem(item,FirstItem,SpyWindow^.RPort,
  460.                                 TaskList)
  461.             END (* if PreviousItem ... *)
  462.         END (* if code = IECodeLButton *)
  463.     END;
  464. END ProcessIntuiMsgs;
  465.  
  466. (* ------------------------------- *)
  467. BEGIN (* GetTaskFromUser *)
  468.     PreviousSelectedItemPtr := NIL;
  469.     good := FALSE;
  470.     done := FALSE;
  471.     TaskList := BuildTaskList(TotalTasks);
  472.     IF TotalTasks <= MaxDisplayLines THEN
  473.         Divisor := 0FFFFH
  474.     ELSE
  475.         Divisor := 0FFFFH DIV (1+ TotalTasks - MaxDisplayLines)
  476.     END;
  477.     MySig := OpenTaskWindow(Divisor,MyProp,PIptr);
  478.     DisplayIt(SpyWindow^.RPort);
  479.     REPEAT
  480.         sig := Wait(MySig);
  481.         msg := GetMsg(SpyWindow^.UserPort^);
  482.         WHILE (msg <> NIL) DO
  483.             ProcessIntuiMsgs(msg,done,good);
  484.             msg := GetMsg(SpyWindow^.UserPort^);
  485.         END; (* while *)
  486.     UNTIL done;
  487.     CloseTheWindow := FALSE;
  488.     CleanUp;
  489.     CloseTheWindow := TRUE;
  490.     RETURN good
  491. END GetTaskFromUser;
  492.  
  493.  
  494. (* +++++++++++++++++++++++++++++++++++++++++++++++++++++ *)
  495. PROCEDURE Main;
  496. VAR
  497.     good : BOOLEAN;
  498.     Myself : TaskPtr;
  499. BEGIN
  500.     Myself := FindTask(CurrentTask);
  501.     IF argc < 2 THEN
  502.         good := GetTaskFromUser(TargetTask);
  503.     ELSIF argc > 2 THEN
  504.         WriteString('Format: ');
  505.         WriteString(argv^[0]^);
  506.         WriteString(
  507.           ' xxxx\nwhere xxxx is the hex address of a task\n');
  508.         good := FALSE
  509.     ELSE
  510.         good := ConvStringToNumber(argv^[1]^, TargetTask,
  511.                                    FALSE, 16);
  512.         IF NOT good THEN
  513.             WriteString('Invalid data in address field!\n')
  514.         ELSIF LONGCARD(TargetTask) MOD 4D <> 0D THEN
  515.             WriteString('Invalid address!\n');
  516.             good := FALSE;
  517.         ELSIF TargetTask = Myself THEN
  518.             WriteString("Can't snoop on myself!!\n");
  519.             good := FALSE;
  520.         END;
  521.     END;
  522.     IF good THEN good := VerifyTaskIsReal(TargetTask) END;
  523.     IF good THEN
  524.         Observe(TargetTask)
  525.     END;
  526. END Main;
  527.  
  528. (* +++++++++++++++++++++++++++++++++++++++++++++++++++++ *)
  529.  
  530. BEGIN
  531.     ExecBaseP := ExecBase;
  532.     CloseTheWindow := TRUE;
  533.     SpyWindow := NIL;
  534.     MyGadList := NIL;
  535.     AddTerminator(CleanUp);
  536.     Main;
  537.     ExitGracefully(0)
  538. END Spy.
  539.