[comp.sys.amiga] Dangling Locks

cmcmanis%pepper@Sun.COM (Chuck McManis) (03/15/88)

Greetings, generally when you pop out a disk the icon on the workbench
disappears. Sometimes it doesn't and that is because some program is
still holding a lock on the disk or some file on the disk. I wrote a
program to show me the list of locks on any volume in the system. I 
started with Lockmon and noticed it wasn't to useful when it came time
to 'pop out the disk'. Mainly because I was trying to use it on my 
hard disk! The other problem is that I cannot find out where it is 
documented that the handler keeps it's list of locks for a given volume.
Fortunately, a consistency in the DOS helps here. The program enclosed
is called 'ShowLocks' and does exactly that, it shows you all of the
locks that are being held either on the volume you specify or all volumes
if you type 'all'. It finds the lock list by creating a lock of it's 
own, which gets placed on the head of the list. Then it reads the locks
by following down the linked list of BPTRs. It differs from Lockmon in 
that it only prints out locks for mounted volumes (they have to be mounted
so that it can get the path information). Since it creates a lock to
get a list of locks Lock #0 always exists (since the program created it).
Only run this program on a quiet system, since the lock list can be 
disturbed by other processes and that can confuse the program. It's main
use is to find out if your program leaves around dangling locks on files
or directories.  Some notes about the various DOS routines that use Locks.
	a) L1 = Lock(string,mode); /* You must Unlock L1 		*/
 	b) L1 = ParentDir(L2);	   /* You must Unlock *both* L1, and L2 */
	c) L1 = CurrentDir(L2);	   /* You *only* unlock L1 		*/
	d) L1 = DupLock(L2);	   /* You must UnLock *both* L1, and L2 */
        e) One way to get the current directory without changing it...
	   TmpLock = Lock("RAM:",ACCESS_READ);
	   MyDir = CurrentDir(TmpLock);
	   OriginalDir = DupLock(MyDir);
	   TmpLock = CurrentDir(MyDir); /* Replace it */
	   UnLock(TmpLock); /* Unlock OriginalDir later */
	   The other way to get the current directory without changing it ...
	   MyProc = FindTask(0L);
	   OriginalDir = DupLock(MyProc->pr_CurrentDir);
	f) When started from the workbench, always save the original directory
	   lock and replace it before exiting.
	g) Leaving a Lock keeps the icon around, UnLocking something twice
	   causes an 0000000003. GURU. 
	h) Exiting back to workbench after changing the CurrentDir will 
	   cause workbench to lock up. 

So without further ado, the tool you have been hearing about for two pages
now, 'showlocks.c' (also cut the signature from the end ...)

--Chuck McManis

---------------------------------cut here----------------------------
/*
 *	showlocks.c
 *
 * Written 13-Mar-88 by Chuck McManis
 * Copyright 1988 Charles McManis, All rights reserved. 
 * This file may be freely redistributed and used as long as this notice remains
 * with it.
 *
 * This program will list out all of the locks that are being held on the 
 * specified volume. You can enter either a volume name or disk device.
 *
 * The output of this program is :
 *	Lock #0 : 'Path'
 *	Lock #1 : 'Path'
 *	...
 */

#include <exec/types.h>
#include <exec/memory.h>
#include <libraries/dos.h>
#include <libraries/dosextens.h>

extern struct DosLibrary *DOSBase;
char	*GetPath();

void
main(argc,argv)

int	argc;
char	*argv[];

{
  int			j,NumVols;
  struct DeviceList	*dl,*vols[20];
  struct RootNode	*rn;
  struct DosInfo	*di;
  char			*t,tmpname[40];


  if (argc != 2) {
    printf("Usage is : ShowLocks [all | Volumename: ]\n");
    exit(0);
  }

  NumVols=0;
  if (strcmp(argv[1],"all") == 0) {
    /* Find the device list */
    rn = (struct RootNode *)DOSBase->dl_Root;
    di = (struct DosInfo  *)BADDR(rn->rn_Info);
    /* While searching the list we lock out Multitasking */
    Forbid();
    for (dl = (struct DeviceList *)BADDR(di->di_DevInfo); dl;
	 dl = (struct DeviceList *)BADDR(dl->dl_Next)) 
      if (dl->dl_Type == DLT_VOLUME) vols[NumVols++] = dl;
    Permit(); /* Back to multitasking mode */
  }

  printf("Lock Monitor, prints out outstanding locks on a given volume.\n");
  if (NumVols) {
    for (j=0; j<NumVols; j++) {
      t = (char *)BADDR(vols[j]->dl_Name);
      t++; /* point past the length */
      strcpy(tmpname,t);   /* Put the name in here... */
      strcat(tmpname,":"); /* and append a colon ... */
      if (vols[j]->dl_Task) {
        printf("Locks held on Volume %s\n",tmpname);
	PrintLocks(tmpname);
      } else printf("Volume %s is not mounted.\n",tmpname);
    } 
  } else {
    printf("Locks held on Volume %s\n",argv[1]);
    PrintLocks(argv[1]);
  }
}

PrintLocks(str)

char *str;
{
  ULONG			thislock;
  struct FileLock 	*fl;
  int			i;

  thislock = Lock(str,ACCESS_READ);
  i = 0;
  for (fl = (struct FileLock *)(BADDR(thislock)); fl;
       fl = (struct FileLock *)(BADDR(fl->fl_Link))) 
      printf("    Lock #%d : '%s'\n",i++,GetPath((ULONG)(fl) >> 2));
  if (thislock) UnLock(thislock);
  return(0);
}

/*
 * Function GetPath()
 *
 * This function will return a pointer to a string with the path of
 * the passed lock.
 */
char *
GetPath(Lck)

ULONG	Lck;

{

  static char	LockPath[256];
  UWORD		FDATA[sizeof(struct FileInfoBlock)/2+1];
  ULONG		CurLock,NewLock;
  char		*s;
  struct FileInfoBlock	*fi;


  /* Initialize LockPath to the NULL string */
  LockPath[0]  = '\0';
  if ((Lck == 0) || (Lck == ~0L))
    return(LockPath); /* If Lock is on root (0) return */

  /* Initialize fi so that it is on a long word boundary */
  if ((long)(FDATA) & 2) fi = (struct FileInfoBlock *)(&FDATA[1]);
  else fi = (struct FileInfoBlock *)FDATA;

  CurLock = DupLock(Lck); /* Make a copy of the Lock passed */
  while (CurLock != NULL) {
    Examine(CurLock,fi);
    if ((CurLock != 0) && (fi->fib_DiskKey != 0)) {
      /* strins just prepends a string rather than append it ... */
      if (strlen(LockPath)) strins(LockPath,"/");
      strins(LockPath,fi->fib_FileName);
    }
    NewLock = ParentDir(CurLock);
    UnLock(CurLock);
    CurLock = NewLock;
  }
  /* Fix up the volume name to include a colon */
  for (s = LockPath; *s; s++) if (*s == '/') {*s = ':'; break;}
  if (!(*s)) strcat(LockPath,":"); 
  return(LockPath);
}

---------------------cut here too---------------------------------

--Chuck McManis
uucp: {anywhere}!sun!cmcmanis   BIX: cmcmanis  ARPAnet: cmcmanis@sun.com
These opinions are my own and no one elses, but you knew that didn't you.

sjl@myrias.UUCP (Stuart Lomas) (03/18/88)

In message <45448@sun.uucp> cmcmanis%pepper@Sun.COM (Chuck McManis) writes:

>       e) One way to get the current directory without changing it...
>	   TmpLock = Lock("RAM:",ACCESS_READ);
>	   MyDir = CurrentDir(TmpLock);
>	   OriginalDir = DupLock(MyDir);
>	   TmpLock = CurrentDir(MyDir); /* Replace it */
>	   UnLock(TmpLock); /* Unlock OriginalDir later */
>	   The other way to get the current directory without changing it ...
>	   MyProc = FindTask(0L);
>	   OriginalDir = DupLock(MyProc->pr_CurrentDir);

Both of these methods seem rather more complicated than:

	currentDir := Lock("",AccessRead);

(I write in Modula-2, but I believe this will work the same in C)

Someone asked for an example of where the Amiga documentation is unclear. This
is an example - it is not at all clear from the documentation that passing
Lock the null string as a file name will get you the current directory.

Stuart Lomas
Myrias Research Corporation
Edmonton, Alberta, Canada
{ihnp4,ubc-vision,rutgers}!alberta!myrias!sjl

Naturally, if anything above constitutes an opinion, I claim it.

dillon@CORY.BERKELEY.EDU (Matt Dillon) (03/18/88)

:Both of these methods seem rather more complicated than:
:
:	currentDir := Lock("",AccessRead);
:
:(I write in Modula-2, but I believe this will work the same in C)

	Yes, that works fine.  An interesting variation which is also
valid (which the workbench uses) is to open files by (A) lock'ing them,
(B) setting the current directory to the FILELOCK, and (C) openning "":
Open("", 1005).  Cute, eh?

					-Matt