dpvc@ur-tut.UUCP (05/11/87)
[I'm not afraid of no line eaters!] I was so intrigued by Phillip Lindsay's MONPROC program, I got out my AmigaDOS Technical Reference Manual and looked up the DOS packet section. I found that there was a lot of interesting information in the packets besides the packet type, so I modified MONPROC to print these fields as well. For instance, it prints the data that was read or writen during ACTION_READ and ACTION_WRITE packets, and prints the name of the file associated with the Lock field for ACTION_PARENT and ACTION_LOCATE_OBJECT. The following is the source to this modified version. While I was working on it, I restructured the program a bit (but the algorithms are the same). It is changed to such a degree, however, that I can't just post the differences, so I'm posting the whole source. I have retained Phillip's copywrite notice, as he requested. See the documentation for usage information, especially the cautions about monitoring file system processes (remember to use the NOLOCK option when you do), and for compiling and linking instructions. I found this a very useful and fun tool, and hope others do as well. Davide P. Cervone University of Rochester Computing Center DPVC@UORDBV.BITNET Taylor Hall dpvc@tut.cc.rochester.EDU Rochester, New York 14627 dpvc@ut-tut.UUCP # This is a shell archive. # Remove everything above and including the cut line. # Then run the rest of the file through sh. #----cut here-----cut here-----cut here-----cut here----# #!/bin/sh # shar: Shell Archiver # Run the following text with /bin/sh to create: # monproc.doc # monproc.c # getprocs.c # getdir.c # printpkt.c # This archive created: Fri May 08 21:29:59 1987 # By: () sed 's/^X//' << \SHAR_EOF > monproc.doc XOVERVIEW: X XMONPROC is a utility that monitors the AmigaDOS message activity for any Xprocess. This is useful when debugging device handlers or other such Xprocesses, or just for seeing how AmigaDOS works. X XMONPROC was originally written by Phillip Lindsay of Commodore-Amiga, Inc. Xusing the Manx Aztec C compiler. I have re-organized the routines, although Xthe algorithms are basically the same, and have used the Lattice C compiler Xversion 3.10. I hope that it still works with Manx, but I do not own that Xcompiler, so I can not test it. X XI have retained Phillips original copywrite notice, and have added my own when Xappropriate. I hope that this satisfies Phillip's request to use the code but Xkeep the copywrite notice intact. I have included his original mail message Xat the end of this document. X X XHOW TO USE MONPROC: X XTo start MONPROC, type X X 1> MONPROC X XMONPROC will show you a list of the processes currently running, and request Xthat you choose one from the list by typing its number. Once you have Xselected a process to monitor, MONPROC inserts its own PacketWait() routine in Xthe pr_PktWait field of the monitored process' Process structure. Every time Xthe monitored process calls the AmigaDOS taskwait() routine to wait for a Xmessage to be issued or returned, MONPROC prints the contents of the message, Xand then returns it to the monitored process for its own use. (See the Xcomments within the code for details of how this is done). X XUsually, you get lots of packets when anything interesting happens. For Xinstance, if you monitor a CLI process, you'll see the command-line prompts, Xthen the command that was typed, then file lookups as the CLI searches the Xcurrent PATH for the command, then file opens and file reads as the scatter Xloader brings the command into memory, then a close file, and then file Xlook ups as the re-direction files are openned, and finally the output Xfrom the command. X XIn order to slow down the printed packets so that you can read them, just Xclick in the CLI window where the monitor is running (and the messages are Xprinting) and press the space-bar. This will halt the output until you Xpress the back-space key. Note that the monitored process will block until Xyou press the back-space because it is waiting for the monitor process to Xreturn its AmigaDOS message to it. X XWhen you are done monitoring the process, click in the window where the Xpackets are being displayed and press CTRL-C. You should receive a message Xstating that the monitor is waiting for one more packet or for a CTRL-E. XTry to force some DOS activity to occur in the monitored process (i.e., if the Xmonitored process is a CLI, give it a command; if it's a disk handler, read Xsomething from the disk, etc.). MONPROC should end when the next packet Xarrives. X XIf, for some reason, you can not force an AmigaDOS message to the monitored Xprocess, you can type CTRL-E to stop MONPROC. The next message that arrives Xfor the monitored processd is likely to cause it to crash, however. X XThe reason for this is simple, when you press CTRL-C, MONPROC removes its XPacketWait() routine from the pr_PktWait field of the monitored process, but Xthe monitored process probably IS STILL USING THE PacketWait() ROUTINE, so we Xcan't let the monitor die just yet. (If we did, it's code would be removed Xfrom memory, and the monitored process would crash when it received the next Xmessage and tried to run the rest of the PacketWait() routine). To solve this, Xwe simply wait for one more message to be signalled, and then die. Since Xwe've already removed our code from pr_PktWiat, we can be assured that after Xthe monitored process finishes using our PacketWait() routine, it will go Xback to using the normal one, so we can delete our code from memory without Xcausing a crash. X X XCOMMAND OPTIONS: X XMONPROC takes two optional arguments. The first controls the way large data Xbuffers are displayed, and the second controls how FileLocks are displayed. X XNormally, MONPROC tries to display data buffers as character strings within Xquotes. When the buffer is very long, however (e.g., when a disk handler is Xreturning a request for 1728 characters from a file), MONPROC will print Xthe data buffer as a HEX dump (HEX codes followed by ASCII representations of Xthese codes). Since these take up a lot of space, you might not want to see Xthem. To suppress HEX dumps, use the NODUMP option when calling MONPROC: X X 1> MONPROC NODUMP X XAny data buffer that is over 50 characters will not be printed when you use Xthe NODUMP option. X XThe second command line option is NOLOCK. Normally when an AmigaDOS packet Xincludes a FileLock structure, MONPROC tries to look up the name of the file Xassociated with the lock and prints that rather than the lock itself. XUsually, this gives the most information about the lock. In order to look up Xthe name, however, MONPROC calls AmigaDOS routines like DupLock(), XParentDir(), and Examine(). If you are monitoring a file system handler, it Xwill not be able to respond to these calls, because it is waiting for the Xmonitor process to return the packet containing the FileLock in question. XIf MONPROC waits for the handler to finish with ParentDir() or Examine() Xbefore returing the packet, we have a deadlock situation, and both processes Xfreeze. X XTo avoid this problem, use the NOLOCK option whenever you monitor file system Xprocesses (like those associated with DF0:, DF1:, RAM:, and VD0:): X X 1> MONPROC NOLOCK X XWhen NOLOCK is used, MONPROC will not try to look up the names of the locks, Xbut will print just the volume name for the given lock. In addition, it will Xgive the access type (shared or exclusive) for the lock. This information Xnormally is not shown, because MONPROC can not look up the name associated Xwith an exclusive lock. X XThe NODUMP and NOLOCK options can be used together, and can be supplied in any Xorder. The defaults are to show Locks and perform HEX dumps when needed. XThese defaults can be changed in the file PrintPkt.c X X XCOMPILING AND LINKING MONPROC: X XFor Lattice C, use the following commands to compile and link the files: X X 1> LC -v -L MONPROC GETPROCS GETDIR PRINTPKT X XThe -v option is required; MONPROC will not work without it. I do not have Xthe Manx copmiler, so I can't tell you how to compile MONPROC with it. I Xsuspect that if any changes are required to make it run with Manx, they are Xprobably minor. I have retained all of Phillip Lindsay's #ifdef MANX code, so Xthe transition should be pretty easy. X X XSOME INTERESTING USES FOR THIS PROGRAM: X XI saw MONPROC basically as an interesting tool for learning about how AmigaDOS Xworks. For instance, you can use it to see how the scatter-loader actually Xloads your program. I was shocked to see how many parts my programs Xcontained. You can see what effect the SMALLDATA and SMALLCODE options have Xon your executables. X XA more interesting use is to see how some of the CLI commands work. You can Xfollow the ParentDir calls for the CD command, and can see how the CLI looks Xdown the PATH directories for your commands. X XYou can use MONPROC to help you debug device handlers (for instance, I used it Xto help me understand Matt Dillon's PIPE device). X XYou can use it to help debug file actions from within your own programs (see Xwhen your program opens files, and whether they are for reading or writing; Xsee the data as it is sent to the files; see where you end up after a Seek(), Xsee when it closes the files; etc.). X XFinally, you can use MONPROC to produce a hardcopy listing of the I/O activity Xto particular devices. For instance, I used MONPROC as the basis for my own Xprogram called HARDCOPY, which produces a hardcopy transcript of any CLI Xsession (i.e., all the text that appears in the CLI window is sent to a file Xas well as to the CLI window). X XDavide P. Cervone XUniversity of Rochester Computing Center DPVC@UORDBV.BITNET XTaylor Hall dpvc@tut.cc.rochester.EDU XRochester, New York 14627 dpvc@ur-tut.UUCP X(716) 275-2811 X X XPHILLIP LINDSAY'S ORIGINAL COMMENTS: X X In the process structure you will find a field labeled "pr_PktWait." This Xis a pointer for a alternate wait routine for AmigaDOS message handling. XEverytime the AmigaDOS taskwait() routine is called a check is made to see Xif the process PktWait field is non-zero...if non-zero a subroutine call is Xmade to that address. The requirement of the subroutine is to wait for a Xmessage to arrive at the process port (pr_MsgPort) and to return the address Xof the message (NOT PACKET!) in D0. XAnyway, what follows is a simple program that will allow you to monitor any Xprocess for packet activity. A binary is available for the asking. (uuencoded) X BTW, When you call a AmigaDOS routine [like Read()] that talks to a XAmigaDOS handler you can monitor a process receiving packets. X-phil [ I promise this time no two signatures ] X============================================================================== X Phillip Lindsay - Commodore Business Machines - Amiga Technical Support X UUCP: {ihnp4|seismo|caip}!cbmvax!phillip - Phone: (215) 431-9180 X No warranty is implied or otherwise given in the form of suggestion or X example. Any opinions found here are of my making. X SHAR_EOF sed 's/^X//' << \SHAR_EOF > monproc.c X/* X * MONPROC.C - Monitor AmigaDOS process packet activity. X * X * Phillip Lindsay (c) 1987 Commodore-Amiga, Inc. X * You may use this source as long as this copywrite notice is left intact. X * X * re-organized and slightly re-worked by Davide P. Cervone, 4/25/87. X */ X X#include <exec/types.h> X#include <exec/ports.h> X#include <exec/semaphores.h> X#include <libraries/dos.h> X#include <libraries/dosextens.h> X#include <stdio.h> X X#ifdef MANX X#include <functions.h> X#endif X X#define ONE 1L X X/* X * AmigaDOS uses task signal bit 8 for message signaling X */ X X#define DOS_SIGNAL 8 X#define DOS_MASK (ONE<<DOS_SIGNAL) X X#define PTR(x,p) ((struct x *)(p)) X Xextern void GetProcList(),FreeProcList(),PrintPkt(); Xextern LONG AllocSignal(), Wait(); Xextern struct Message *PacketWait(), *GetMsg(); Xextern struct Process *FindTask(); Xextern struct DosLibrary *DOSBase; Xextern int Dump_OK, Lock_OK; X X Xstruct MsgPort *thePort; /* the port we will be monitoring */ Xstruct Process *myProcess; /* pointer to our own process */ XULONG WaitMask; /* the task signal mask */ Xstruct SignalSemaphore CanReturn = {0}; /* coordinates PacketWait */ Xstruct Message *theMessage; /* the message we received */ XAPTR OldPktWait; /* the old pr_PktWait routine */ X X X#ifdef MANX X LONG PWait(); /* this is the ASM stub that calls PacketWait() */ X#else X #define PWait PacketWait X#endif X X#ifndef MANX X Ctrl_C() /* Control-C Trap routine for Lattice */ X { X return(0); X } X#endif X X X/* X * PacketWait() X * X * This is the routine placed in the pr_PktWait field of the monitored X * precess. It is run asynchronously by the monitored process, and is X * called whenever AmigaDOS does a taskwait(). PacketWait() waits for X * a message to come in and then signals the monitoring task that one has X * arrived. It then attempts to obtain the semaphore, which will not be X * released by the monitoring process until it is finished printing the X * contents of the packet. X */ X Xstruct Message *PacketWait() X{ X#ifdef MANX X /* X * if MANX, make sure we can see our data X */ X geta4(); X#endif X X SetSignal(FALSE,DOS_MASK); X X while(!(theMessage = GetMsg(thePort))) Wait(DOS_MASK); X X Signal(myProcess,WaitMask); X ObtainSemaphore(&CanReturn); X ReleaseSemaphore(&CanReturn); X X return(theMessage); X} X X X/* X * DoArguments() X * X * Parses the command line to make sure it contains only valid arguments, X * and sets the flag variables to their proper values (they are initialized X * in printpkt.c). The legal options are: X * X * [NO]DUMP To control hex dumps of long buffers X * [NO]LOCK To control the display of LOCK parameters X * (you should use NOLOCK if you are monitoring a X * file system process). X */ X Xvoid DoArguments(argc,argv) Xint argc; Xchar **argv; X{ X while (--argc) X { X argv++; X if (stricmp(*argv,"NODUMP") == 0) Dump_OK = FALSE; X else if (stricmp(*argv,"DUMP") == 0) Dump_OK = TRUE; X else if (stricmp(*argv,"NOLOCK") == 0) Lock_OK = FALSE; X else if (stricmp(*argv,"LOCK") == 0) Lock_OK = TRUE; X else X { X printf("Bad Argument: '%s'\n",*argv); X printf("Usage: MONPROC [NO]DUMP [NO]LOCK\n"); X exit(5); X } X } X} X X X/* X * GetProcToMonitor() X * X * Lists the processes currently running and lets the user choose one. X * Returns a pointer the the chosen process. X */ X Xstruct Process *GetProcToMonitor() X{ X struct List ProcList; X struct Node *theProc; X struct Process *ChosenProcess = NULL; X ULONG count,choice; X char s[80]; X X/* X * Set up ProcList and a list header and read the process list into it X * (we assume the process list won't change). X */ X NewList(&ProcList); X GetProcList(&ProcList); X X/* X * If there are any processes, list them and let the user chose one by X * number. If he choses a legal number, find the process in the list X * and return a pointer to the process structure for that process. X * Finally, free the process list. X */ X if (ProcList.lh_TailPred == PTR(Node,&ProcList)) X { X printf("No Processes.\n"); X } else { X do X { X printf("\nEnter NUMBER for process to monitor:\n"); X printf("Pick# Process MsgPort Name\n"); X for (count=1,theProc=ProcList.lh_Head; /* start at the list head */ X theProc->ln_Succ; /* while there are more */ X theProc=theProc->ln_Succ,count++) /* go on to the next */ X { X printf("%4ld. %08lX %08lX %s\n",count, X theProc->ln_Name, X &(PTR(Process,theProc->ln_Name)->pr_MsgPort), X PTR(Process,theProc->ln_Name)->pr_Task.tc_Node.ln_Name); X } X printf("\nNUMBER: "); X gets(s); X } while (sscanf(s,"%ld",&choice) != 1); X X if (choice < 1 || choice >= count) X { X printf("Operation Aborted.\n"); X } else { X for (count=1,theProc=ProcList.lh_Head; X count < choice; X theProc=theProc->ln_Succ,count++); X ChosenProcess = PTR(Process,theProc->ln_Name); X } X FreeProcList(&ProcList); X } X return(ChosenProcess); X} X X X/* X * SetupSignal() X * X * Allocate a signal to use for our inter-task communication, and X * set up the mask for using it. X */ X Xvoid SetupSignal(theSignal) XLONG *theSignal; X{ X *theSignal = AllocSignal(-ONE); X if (*theSignal == -ONE) X { X printf("Can't Allocate a Task Signal."); X exit(10); X } X WaitMask = (ONE << (*theSignal)); X} X X X/* X * SetupProcess() X * X * Copy the process name, and gets it Message port. Set our priority X * higher than the monitored process so we will be able to react to its X * signals. Then set the pr_PktWait field so that we begin monitoring X * the other task. Save the old one so we can put it back. X */ X Xvoid SetupProcess(theProcess,name) Xstruct Process *theProcess; Xchar *name; X{ X strcpy(name,theProcess->pr_Task.tc_Node.ln_Name); X thePort = &theProcess->pr_MsgPort; X X Forbid(); X SetTaskPri(myProcess,(ULONG)(theProcess->pr_Task.tc_Node.ln_Pri + 1)); X OldPktWait = theProcess->pr_PktWait; X theProcess->pr_PktWait = (APTR) PWait; X Permit(); X} X X X/* X * MonitorProcess() X * X * Wait for the monitored process to receive a message (our PacketWait() X * function signals us via theSignal when it has received a message), then X * print out the contents of the message. A semaphore is used to coordinate X * this routine with the PacketWait() routine (which is run asynchonously X * by the monitored process). Phillip Lindsay says "there are probably a X * hundred better was of doing this. I just went with the first one [that] X * came to mind." I couldn't think of a better one, so I still use it. X * Since our process is running at a higher priority than the monitored one, X * we should obtain the semaphore first. The other process wil block until X * we release it (when we are done printing the contents). X */ X Xvoid MonitorProcess(name,theSignal) Xchar *name; XULONG theSignal; X{ X ULONG signals; X struct DosPacket *thePacket; X X do X { X signals = Wait(SIGBREAKF_CTRL_C | WaitMask); X ObtainSemaphore(&CanReturn); X if (signals & WaitMask) X { X /* X * PacketWait() signalled us so print the message it put in X * theMessage. X */ X thePacket = PTR(DosPacket,theMessage->mn_Node.ln_Name); X printf("\n%s: ",name); X PrintPkt(thePacket); X } X ReleaseSemaphore(&CanReturn); X } while(!(signals & SIGBREAKF_CTRL_C)); X} X X X/* X * ClenUpProcess() X * X * Put everything back the way we found it, except that the monitored process X * is still running our code... X */ X Xvoid CleanUpProcess(theProcess) Xstruct Process *theProcess; X{ X Forbid(); X theProcess->pr_PktWait = OldPktWait; X Permit(); X SetTaskPri(myProcess,0L); X} X X X/* X * WaitForLastPacket() X * X * Since the monitored process is still running our PacketWait() code, we X * can't quit yet. Since we already put back the old pr_PktWait pointer, X * our code will not be called after the next packet is received, so we have X * to wait for one more packet before we can quit. Note that PacketWait() X * will still signal us, so we know when it will be safe to remove the code. X * Just in case we goof, let CTRL-E cancel, too. X */ X Xvoid WaitForLastPacket(theSignal) XULONG theSignal; X{ X ULONG signals; X X printf("\nThe process must receive another message before we can safely\n"); X printf("remove the packet wait code. We will wait for one more packet.\n"); X printf("Press CTRL-E if you want to quit early, but that will likely\n"); X printf("crash the monitored process!\n"); X X signals = Wait(SIGBREAKF_CTRL_E | WaitMask); X if (signals & WaitMask) X { X ObtainSemaphore(&CanReturn); X ReleaseSemaphore(&CanReturn); X } X} X Xvoid main(argc,argv) Xint argc; Xchar **argv; X{ X struct Process *ChosenProcess; X LONG TaskSignal; X UBYTE ProcessName[81]; X X if (argc > 1) DoArguments(argc,argv); X X printf("\n%s - monitor AmigaDOS process packet activity.\n",*argv); X X myProcess = FindTask(NULL); X ChosenProcess = GetProcToMonitor(); X if (ChosenProcess != NULL) X { X #ifndef MANX X onbreak(&Ctrl_C); /* Turn off CTRL-C for Lattice: we do our own */ X #endif X X SetupSignal(&TaskSignal); X InitSemaphore(&CanReturn); X SetupProcess(ChosenProcess,ProcessName); X printf("Monitor Installed, press CTRL-C to quit monitoring\n"); X X MonitorProcess(ProcessName,TaskSignal); X X CleanUpProcess(ChosenProcess); X WaitForLastPacket(TaskSignal); X FreeSignal(TaskSignal); X X printf("\nAll done.\n"); X } X} X X X/* X * This code stub has been known to save lives... X */ X X#if MANX X#asm X XREF _PacketWait X X XDEF _PWait X_PWait: X movem.l a2/a3/a4,-(sp) X jsr _PacketWait X movem.l (sp)+,a2/a3/a4 X rts X#endasm X#endif SHAR_EOF sed 's/^X//' << \SHAR_EOF > getprocs.c X/* X * GETPROCS.C - Grab all available processes and return them to you in a X * simple exec list. The list is made up of nodes--the "name" X * fields pointing to the process structure. X * X * Phillip Lindsay (c) 1987 Commodore-Amiga Inc. X * You may use this source as long as the copyright notice is left intact. X * X * Re-organized and re-worked by Davide P. Cervone, 4/25/87 X */ X X#include <exec/types.h> X#include <exec/memory.h> X#include <exec/tasks.h> X#include <exec/execbase.h> X#include <libraries/dos.h> X#include <libraries/dosextens.h> X#include <stdio.h> X X#ifdef MANX X #include <functions.h> X#endif X X#define NODESIZE ((ULONG)sizeof(struct Node)) X#define MEMFLAGS (MEMF_PUBLIC | MEMF_CLEAR) X Xextern struct Node *AllocMem(), *RemTail(); X X/* X * GetNode() X * X * Allocates a node structure for you and initializes the node X * with the given values. X */ X Xstruct Node *GetNode(name,type,pri) Xchar *name; XUBYTE type,pri; X{ X register struct Node *theNode; X X theNode = AllocMem(NODESIZE,MEMFLAGS); X if (theNode != NULL) X { X theNode->ln_Name = name; X theNode->ln_Type = type; X theNode->ln_Pri = pri; X } X return(theNode); X} X X X/* X * FreeNode() X * X * Frees a given Exec node. You must remove it from any list before X * calling FreeNode() X */ X Xvoid FreeNode(theNode) Xstruct Node *theNode; X{ X FreeMem(theNode,NODESIZE); X} X X X/* X * AddProcs() X * X * Adds the processes from the ProcList onto the end of the list 'plist'. X */ X Xvoid AddProcs(plist,ProcList) Xstruct List *plist; Xstruct List *ProcList; X{ X register struct Node *aProc,*bProc; X X if (ProcList->lh_TailPred != (struct Node *)ProcList) X { X for (aProc=ProcList->lh_Head; aProc->ln_Succ; aProc=aProc->ln_Succ) X { X if (aProc->ln_Type == NT_PROCESS) X { X if (bProc = GetNode(aProc,0,0)) AddTail(plist,bProc); X } X } X } X} X X X/* X * GetProcList() X * X * For each process in the system's task list, GetProcList appends an exec X * node to the given list and fills in the node name field with a pointer X * to the process strcuture. X * X * plist is an initialized Exec List. X * X * WARNING: This isn't a casual subroutine. The returned list is valid as X * long as a process is not ended or added after this subroutine is X * called. To be real safe you should probably check to see if the X * process is still around before trying to look at the data structure. X */ X Xvoid GetProcList(plist) Xstruct List *plist; X{ X extern struct ExecBase *SysBase; X register struct ExecBase *execbase=SysBase; X register struct Node *aProc; X X/* X * I haven't clocked this code, but as a rule, you shouldn't stay disabled for X * more than 250ms. I have no doubt I'm illegal, but you can't be too safe X * when dealing with something that changes with a blink of an interrupt. X */ X X Disable(); X X /* X * Add our own process to the list X */ X if (execbase->ThisTask->tc_Node.ln_Type == NT_PROCESS) X { X if (aProc = GetNode(execbase->ThisTask,0,0)) AddTail(plist,aProc); X } X X /* X * Add the process from the ExecBase queues X */ X AddProcs(plist,&execbase->TaskReady); X AddProcs(plist,&execbase->TaskWait); X X Enable(); X} X X X/* X * FreeProcList() X * X * Frees all nodes in the given list. FreeProcList() assumes the nodes X * where initialized with GetNode(). X */ X Xvoid FreeProcList(plist) Xstruct List *plist; X{ X register struct Node *theProc; X X while (theProc = RemTail(plist)) FreeNode(theProc); X} SHAR_EOF sed 's/^X//' << \SHAR_EOF > getdir.c X/* GETDIR.C - Does some simple lock and device-list manipulation X * X * Copywrite (c) 1987 by Davide P. Cervone X * This code may be used so long as this copywrite notice is left in tact. X */ X X#include "exec/types.h" X#include "exec/memory.h" X#include "libraries/dos.h" X#include "libraries/dosextens.h" X X#define BCPL(x,y) ((struct x *)(BADDR(y))) X#define BCPL_TO_CHAR(y) ((char *)(BADDR(y))) X#define FILELOCK(x) BCPL(FileLock,x) X#define DEVLIST(x) BCPL(DeviceList,x) X X#define FIBSIZE ((ULONG)sizeof(struct FileInfoBlock)) X#define RETURN(dir) return((*dir == '\0')? NULL: dir) X X X/* X * BSTRcopy() X * X * Copies a BCPL string into a C string and returns the length of the X * string. X */ X XBSTRcpy(to,BCPLfrom) Xchar *to, *BCPLfrom; X{ X char *from = BCPL_TO_CHAR(BCPLfrom); X X strncpy(to,from+1,(int)(*from)); X *(to+(*from)) = '\0'; X return((int) *from); X} X X X/* X * GetPathFromLock() X * X * GetPathFromLock looks up the path name to the file or directory specified X * by the lock. The volume name is included in the returned string. X * If an error occurs, the function returns NULL, and the path name is set X * to an empty string (a null byte). The lock remains intact (it is not X * UnLocked). X * X * dir a pointer to a string area where the path name X * will be stored. No check is made to be sure that X * the path name fits. X * lock a lock on the file or directory whose path is to X * be found. X * X * Note: there seems to be a problem with the RAM: device (AmigaDOS v1.1). X * ParentDir() always returns NULL for a lock on the RAM-disk; Therefore, X * GetPathFromLock() always returns "RAM:" for any lock on the RAM: device. X * X * Note: this function finds the complete path name to the locked object, X * including the volume name. ASSIGNed names will be translated to their X * physical names. For instance, if you CD to the C: directory, and X * C: is ASSIGNed as SYS:C, and SYS: is ASSIGNed as your boot disk, and X * your boot disk is named "WorkBench", then if you call GetPathFromLock() X * with a lock in the C: directory, you will get back the string X * "WorkBench:c", not "C:". X */ X Xchar *GetPathFromLock(dir,lock) Xchar *dir; Xstruct FileLock *lock; X{ X struct FileLock *CurDir, *OldDir, *DupLock(), *ParentDir(); X struct FileInfoBlock *fib, *AllocMem(); X char *subdir = dir, *s, *s1, c; X int len; X X/* X * Clear the path name, and duplicate the lock (so we can UnLock our X * duplicate without destroying the user's lock). If DupLock returned X * NULL, then there was an error. (An exclusive write lock, perhaps?) X */ X X *dir = '\0'; X CurDir = DupLock(lock); X if (CurDir == NULL) return(NULL); X X/* X * the FileInfoBlock must be longword alligned, so use AllocMem X */ X fib = AllocMem(FIBSIZE,MEMF_CLEAR | MEMF_PUBLIC); X if (fib != NULL) X { X/* X * Get the device name from the volume entry in the device list X * that is pointed to by the file lock. Since the device list is X * a shared list, use Forbid() and Permit() so it doesn't change while X * we are looking at it. Add a colon, and set the subdir pointer to X * the character following the volume name. Subdirectory names will be X * added starting there. X */ X Forbid(); X subdir += BSTRcpy(dir,DEVLIST(FILELOCK(CurDir)->fl_Volume)->dl_Name); X Permit(); X *subdir++ = ':'; X *subdir = '\0'; X/* X * While we have a valid lock (ParentDir() will return a NULL pointer X * when we reach the root of the disk), examine the directory (or file) X * that we have locked. If we can't examine it, give an error, otherwise X * move up to the parent dir and unlock the old directory lock. If we haven't X * moved up past the root, then we want to add the directory name (found X * in the FileInfoBlock structure returned by Examine()) to the path name. X * To do so, we shift any existing sub-directory names to the right leaving X * enough room for the new directory name (plus a slash, if needed). X * X * Note: the highest directory on a disk (the root), seems to have the same X * name as the disk itself (look at the ASSIGN list, for instance), so X * ParentDir() does not return NULL until we move past the root, that's why X * the ParentDir() call comes before we add the directory name into the X * string. X * X * For instance, if the current directory is "disk:a/b/c/d", and X * the CurDir lock is on "d", then the calls to ParentDir() will X * produce locks on "c", "b", "a", "disk", and then NULL. X */ X while (CurDir != NULL) X { X if (!Examine(CurDir,fib)) X { X *dir = '\0'; X UnLock(CurDir); X CurDir = NULL; X } else { X OldDir = CurDir; X CurDir = ParentDir(OldDir); X UnLock(OldDir); X X if (CurDir != NULL) X { X len = strlen(fib->fib_FileName); X for (s=subdir+strlen(subdir), s1=s+len+1; X s >= subdir; *s1-- = *s--); X c = *subdir; X strcpy(subdir,fib->fib_FileName); X *s1 = (c != '\0')? '/': c; X } X } X } X FreeMem(fib,FIBSIZE); X } X RETURN(dir); X} X X X/* X * GetInfoVolume() X * X * GetInfoVolume looks up the volume name from an InfoData structure and X * returns a C string containing the name. X * X * name a pointer to a string area where the volume name X * will be stored. No check is made to be sure that X * the name fits. X * info an InfoData structure that has been filed in by a call X * to Info(). X * X */ X Xvoid GetInfoVolume(name,info) Xchar *name; Xstruct InfoData *info; X{ X/* X * Get the device name from the volume entry in the device list X * that is pointed to by the InfoData. Since the device list is X * a shared list, use Forbid() and Permit() so it doesn't change while X * we are looking at it. Add a colon for looks. X */ X Forbid(); X name += BSTRcpy(name,DEVLIST(info->id_VolumeNode)->dl_Name); X Permit(); X *name++ = ':'; X *name = '\0'; X} SHAR_EOF sed 's/^X//' << \SHAR_EOF > printpkt.c X/* X * PrintPkt.C - This doesn't do much, it takes a packet type (number) and X * gives you something more readable on stdout. X * X * Phillip Lindsay (c) 1987 Commodore-Amiga Inc. X * You may use this source as long as the copyright notice is left intact. X * X * Modified to print details of packet contents X * by Davide P. Cervone, 4/25/87 X */ X X#include <exec/types.h> X#include <libraries/dosextens.h> X X/* X * packet types X */ X X#define _ACTION_NIL 0L X#define _ACTION_GET_BLOCK 2L X#define _ACTION_SET_MAP 4L X#define _ACTION_DIE 5L X#define _ACTION_EVENT 6L X#define _ACTION_CURRENT_VOLUME 7L X#define _ACTION_LOCATE_OBJECT 8L X#define _ACTION_RENAME_DISK 9L X#define _ACTION_FREE_LOCK 15L X#define _ACTION_DELETE_OBJECT 16L X#define _ACTION_RENAME_OBJECT 17L X#define _ACTION_MORE_CACHE 18L X#define _ACTION_COPY_DIR 19L X#define _ACTION_WAIT_CHAR 20L X#define _ACTION_SET_PROTECT 21L X#define _ACTION_CREATE_DIR 22L X#define _ACTION_EXAMINE_OBJECT 23L X#define _ACTION_EXAMINE_NEXT 24L X#define _ACTION_DISK_INFO 25L X#define _ACTION_INFO 26L X#define _ACTION_FLUSH 27L X#define _ACTION_SET_COMMENT 28L X#define _ACTION_PARENT 29L X X/* X * This is normally a returning timer device request. (internal) X */ X#define _ACTION_TIMER 30L X X#define _ACTION_INHIBIT 31L X#define _ACTION_DISK_TYPE 32L X#define _ACTION_DISK_CHANGE 33L X#define _ACTION_SET_FILE_DATE 34L X#define _ACTION_READ 82L X#define _ACTION_WRITE 87L X#define _ACTION_SET_SCREEN_MODE 994L X X/* X * When a handler internally sends a device i/o request they are sent using X * their process port and in the form of a "packet" the packet types below: X * (ACTION_TIMER above also falls into this catagory) X */ X#define _ACTION_READ_INTERNAL 1001L X#define _ACTION_WRITE_INTERNAL 1002L X X#define _ACTION_FIND_INPUT 1005L X#define _ACTION_FIND_OUTPUT 1006L X#define _ACTION_CLOSE 1007L X#define _ACTION_SEEK 1008L X X X/* X * Short-hand for the parts of the DosPacket X */ X#define ARG1 pkt->dp_Arg1 X#define ARG2 pkt->dp_Arg2 X#define ARG3 pkt->dp_Arg3 X#define ARG4 pkt->dp_Arg4 X#define RES1 pkt->dp_Res1 X X#define BCPL(s,p) ((struct s *)(((ULONG)p)<<2)) X X X Xint Dump_OK = TRUE; /* it's OK to perform HEX dumps for long data buffers */ Xint Lock_OK = TRUE; /* it's OK to print LOCKs */ X X/* X * printall() X * X * Prints all the important parts of a DosPacket when we don't know X * what they are. X */ X Xvoid printall(s,pkt) Xchar *s; Xstruct DosPacket *pkt; X{ X printf("%s (%X,%X,%X) Returning: %X\n",s,ARG1,ARG2,ARG3,RES1); X} X X X/* X * printINT() X * X * Prints a label and an integer in decimal notation. X */ X Xvoid printINT(s,i) Xchar *s; Xint i; X{ X printf(" %s: %d",s,i); X} X X X/* X * printBOOL() X * X * Prints return values as OK or FALSE X */ X Xvoid printBOOL(b) Xint b; X{ X printf(" Return: %s\n",(b)? "OK": "FALSE"); X} X X X/* X * printX() X * X * Prints a label and HEX value X */ X Xvoid printX(s,x) Xchar *s; Xunsigned int x; X{ X printf(" %s: %X",s,x); X} X X X/* X * printQuoted() X * X * Prints a data buffer as a quoted string. Special characters are shown X * as '\n','\r','\t', '\001', etc. X */ X Xvoid printQuoted(buf,length) Xunsigned char *buf; Xint length; X{ X short i; X unsigned char c, c7; X char line[200]; X char *s = &line[0]; X X for (i=0; i<length; i++) X { X c = *(buf++); c7 = c & 0x7F; X if (c7 < ' ' || c7 > 0x7E) X { X switch(c) X { X case '\b': X *s++ = '\\'; X *s++ = 'b'; X break; X X case '\t': X *s++ = '\\'; X *s++ = 't'; X break; X X case '\r': X *s++ = '\\'; X *s++ = 'r'; X break; X X case '\n': X *s++ = '\\'; X *s++ = 'n'; X break; X X default: X sprintf(s,"\\%03o",c); X s += 4; X break; X } X } else { X if (c == '"' || c == '\\') *s++ = '\\'; X *s++ = c; X } X } X *s = '\0'; X printf("\"%s\"",line); X} X X X/* X * printDump() X * X * Prints a data buffer in HEX dump format with ASCII equivalents. X * Non-printing characters are shown as '.' Only the first 80 characters X * are displayed. X */ X Xvoid printDump(buf,length) Xunsigned char *buf; Xunsigned int length; X{ X short i,j,count; X int l = (length > 80)? 80: length; X unsigned char c, c7; X char line[40]; X X line[39] = '\0'; X count = 0; X printf("\n"); X for (i=l/8; i>=0; i--) X { X for (j=0; j<39; j++) line[j] = ' '; X for (j=0; j<8 & count<l; j++,count++) X { X c = *(buf++); c7 = c & 0x7F; X sprintf(&line[j*3],"%02X ",c); X sprintf(&line[j+26],"%c ",(c7 < ' ' || c7 > 0x7E)? '.': c); X } X line[24] = ' '; X printf("\n %s",line); X } X if (l < length) printf("\n [more]\n"); else printf("\n"); X} X X X/* X * printBUF() X * X * Prints a data buffer as a quoted string (if it is small enough) X * or as a HEX dump (if it is big). X */ X Xvoid printBUF(s,buf,length) Xchar *s; Xunsigned char *buf; Xunsigned int length; X{ X printf(" %s: ",s); X if (length <= 50) X printQuoted(buf,length); X else X if (Dump_OK) printDump(buf,length); X} X X X/* X * printBSTR() X * X * Prints the contents of a BCPL string buffer X */ X Xvoid printBSTR(s1,s2) Xchar *s1,*s2; X{ X printBUF(s1,BADDR(s2)+1,(long)(*((char *)BADDR(s2)))); X} X X/* X * printBPTR() X * X * Prints the contents of a buffer pointed to by a BPTR X */ X X#define printBPTR(s,b,l) printBUF(s,BADDR(b),l) X X X/* X * printLOCK() X * X * Prints the name of the file locked by a FileLock. DON'T ATTEMPT TO X * MONITOR LOCKS FROIM FILE HANDLERS! It will put you into a deadlock X * situation: the handler is waiting for the returned packet from X * PacketWait, and we are waiting for the handler to process our DupLock X * in GetPathFromLock. Use MONPROC NOLOCK when you want to monitor file X * handlers. X * Notice that we expect a BPTR to a Lock. X */ X Xvoid printLOCK(s,FL) Xchar *s; XBPTR FL; X{ X struct FileLock *fl = BCPL(FileLock,FL); X char name[60]; X X if (Lock_OK) X { X name[0] = '\0'; X if (FL) GetPathFromLock(name,FL); X printBUF(s,name,strlen(name)); X } else { X if (fl) X { X printBSTR("Lock Volume",BCPL(DeviceList,fl->fl_Volume)->dl_Name); X printINT("Lock Access",fl->fl_Access); X } else { X printf(" Volume: \"\""); X } X } X X/* X printX(s,fl); X if (fl) X { X printINT("Key",fl->fl_Key); X printINT("Access",fl->fl_Access); X printX("Task",fl->fl_Task); X printf("\n"); X printBSTR("Volume",BCPL(DeviceList,fl->fl_Volume)->dl_Name); X } X*/ X} X X X/* X * printFH() X * X * Should print the contents of a FileHandle. Does anyone have a good idea X * about what's useful in a Filehandle? X * Note that we expect a BPTR to a FileHandle. X */ X Xvoid printFH(s,FH) Xchar *s; XBPTR FH; X{ X/* X struct FileHandle *fh = BCPL(FileHandle,FH); X X if (fh) X { X printX("FH Port",fh->fh_Port); X printX("FH Type",fh->fh_Type); X } X*/ X} X X X/* X * printFIB() X * X * Prints the fib_FileName field of a FileInfoBlock. X * Note that we expect a BPTR to a FileInfoBlock. X */ X Xvoid printFIB(FIB) XBPTR FIB; X{ X struct FileInfoBlock *fib = BCPL(FileInfoBlock,FIB); X X if (fib) X printBUF("File Name",&(fib->fib_FileName[1]),fib->fib_FileName[0]); X else X printf(" File Name: \"\""); X} X X X/* X * printfINFO() X * X * Prints the volume name from an InfoData structure. X * Note that we expect a BPTR to InfoData. X */ X Xvoid printINFO(INFO) XBPTR INFO; X{ X struct InfoData *info = BCPL(InfoData,INFO); X char name[60]; X X name[0] = '\0'; X if (info) GetInfoVolume(name,info); X printBUF("Volume",name,strlen(name)); X} X X X/* X * PrintPkt() X * X * Prints the type of packet and the information appropriate to X * that packet type (if known). Packet types and contents are described X * in the AmigaDOS Technical Reference Manual, secion 3.8.1. X */ X Xvoid PrintPkt(pkt) Xstruct DosPacket *pkt; X{ X switch(pkt->dp_Type) X { X case _ACTION_NIL: X printall("ACTION_NIL",pkt); X break; X X case _ACTION_GET_BLOCK: X printall("ACTION_GET_BLOCK",pkt); X break; X X case _ACTION_SET_MAP: X printall("ACTION_SET_MAP",pkt); X break; X X case _ACTION_DIE: X printall("ACTION_DIE",pkt); X break; X X case _ACTION_EVENT: X printall("ACTION_EVENT",pkt); X break; X X case _ACTION_CURRENT_VOLUME: X printall("ACTION_CURRENT_VOLUME",pkt); X break; X X case _ACTION_LOCATE_OBJECT: X printf("ACTION_LOCATE_OBJECT (Lock)\n"); X printLOCK("Lock",ARG1); X printBSTR("Name",ARG2); X printINT("Mode",ARG3); X printf("\n"); X printLOCK("NewLock",RES1); X printf("\n"); X break; X X case _ACTION_RENAME_DISK: X printf("ACTION_RENAME_DISK\n"); X printBSTR("New Name",ARG1); X printBOOL(RES1); X break; X X case _ACTION_FREE_LOCK: X printf("ACTION_FREE_LOCK\n"); X printLOCK("Lock",ARG1); X printBOOL(RES1); X break; X X case _ACTION_DELETE_OBJECT: X printf("ACTION_DELETE_OBJECT\n"); X printLOCK("Dir Lock",ARG1); X printBSTR("Name",ARG2); X printf("\n"); X printBOOL(RES1); X break; X X case _ACTION_RENAME_OBJECT: X printf("ACTION_RENAME_OBJECT\n"); X printLOCK("From Lock",ARG1); X printBSTR("From Name",ARG2); X printf("\n"); X printLOCK("To Lock",ARG3); X printBSTR("To Name",ARG4); X printf("\n"); X printBOOL(RES1); X break; X X case _ACTION_MORE_CACHE: X printall("ACTION_MORE_CACHE",pkt); X break; X X case _ACTION_COPY_DIR: X printf("ACTION_COPY_DIR (DupLock)\n"); X printLOCK("Lock",ARG1); X printLOCK("NewLock",RES1); X printf("\n"); X break; X X case _ACTION_WAIT_CHAR: X printf("ACTION_WAIT_CHAR\n"); X printINT("Timeout",ARG1); X printBOOL(RES1); X break; X X case _ACTION_SET_PROTECT: X printf("ACTION_SET_PROTECT\n"); X printLOCK("Dir Lock",ARG2); X printBSTR("Name",ARG3); X printf("\n"); X printX("Mask",ARG4); X printBOOL(RES1); X break; X X case _ACTION_CREATE_DIR: X printf("ACTION_CREATE_DIR\n"); X printLOCK("Dir Lock",ARG1); X printBSTR("Name",ARG2); X printf("\n"); X printLOCK("New Lock",RES1); X printf("\n"); X break; X X case _ACTION_EXAMINE_OBJECT: X printf("ACTION_EXAMINE_OBJECT\n"); X printLOCK("Lock",ARG1); X printFIB(ARG2); X printBOOL(RES1); X break; X X case _ACTION_EXAMINE_NEXT: X printf("ACTION_EXAMINE_NEXT\n"); X printLOCK("Dir Lock",ARG1); X printFIB(ARG2); X printBOOL(RES1); X break; X X case _ACTION_DISK_INFO: X printf("ACTION_DISK_INFO\n"); X printINFO(ARG1); X printBOOL(RES1); X break; X X case _ACTION_INFO: X printall("ACTION_INFO",pkt); X break; X X case _ACTION_FLUSH: X printall("ACTION_FLUSH",pkt); X break; X X case _ACTION_SET_COMMENT: X printf("ACTION_SET_COMMENT\n"); X printLOCK("Lock",ARG2); X printBSTR("Name",ARG3); X printf("\n"); X printBSTR("Comment",ARG4); X printBOOL(RES1); X break; X X case _ACTION_PARENT: X printf("ACTION_PARENT\n"); X printLOCK("Dir Lock",ARG1); X printLOCK("Parent",RES1); X printf("\n"); X break; X X case _ACTION_TIMER: X printall("ACTION_TIMER",pkt); X break; X X case _ACTION_INHIBIT: X printf("ACTION_INHIBIT\n"); X printINT("On/Off",ARG1); X printBOOL(RES1); X break; X X case _ACTION_DISK_TYPE: X printall("ACTION_DISK_TYPE",pkt); X break; X X case _ACTION_DISK_CHANGE: X printall("ACTION_DISK_CHANGE",pkt); X break; X X case _ACTION_SET_FILE_DATE: X printall("ACTION_SET_FILE_DATE",pkt); X break; X X case _ACTION_READ: X printf("ACTION_READ\n"); X printX("Arg1",ARG1); X printX("Buffer",ARG2); X printINT("Length",ARG3); X printINT("Actual Length",RES1); X printf("\n"); X printBUF("Data",ARG2,RES1); X printf("\n"); X break; X X case _ACTION_WRITE: X printf("ACTION_WRITE\n"); X printX("Arg1",ARG1); X printX("Buffer",ARG2); X printINT("Length",ARG3); X printINT("Actual Length",RES1); X printf("\n"); X printBUF("Data",ARG2,RES1); X printf("\n"); X break; X X case _ACTION_SET_SCREEN_MODE: X printall("ACTION_SET_SCREEN_MODE",pkt); X break; X X case _ACTION_READ_INTERNAL: X printall("ACTION_READ_INTERNAL",pkt); X break; X X case _ACTION_WRITE_INTERNAL: X printall("ACTION_WRITE_INTERNAL",pkt); X break; X X case _ACTION_FIND_INPUT: X printf("ACTION_FIND_INPUT\n"); X printFH("File Handle",ARG1); X printLOCK("Dir Lock",ARG2); X printBSTR("Name",ARG3); X printf("\n"); X printBOOL(RES1); X break; X X case _ACTION_FIND_OUTPUT: X printf("ACTION_FIND_OUTPUT\n"); X printFH("File Handle",ARG1); X printLOCK("Lock",ARG2); X printBSTR("Name",ARG3); X printf("\n"); X printBOOL(RES1); X break; X X case _ACTION_CLOSE: X printf("ACTION_CLOSE\n"); X printX("Arg1",ARG1); X printBOOL(RES1); X break; X X case _ACTION_SEEK: X printf("ACTION_SEEK\n"); X printFH("File Handle",ARG1); X printINT("Position",ARG2); X printINT("Mode",ARG3); X printINT("Old Pos",RES1); X printf("\n"); X break; X X default: X printf("Unknown packet: %ld",pkt->dp_Type); X printall("",pkt); X break; X } X} SHAR_EOF # End of shell archive exit 0