/*
**	$RCSfile: UnMount.c,v $
**	$Filename: UnMount.c $
**	$Revision: 0.1 $
**	$Date: 1995/04/29 17:33:51 $
**
**	Try to Shutdown or at least Inhibit DOS-Handlers (version 0.6)
**
**	(C) Copyright 1995-2000 by Etienne Vogt
*/

#include <exec/alerts.h>
#include <exec/memory.h>
#include <dos/dosextens.h>
#include <dos/dostags.h>
#include <workbench/startup.h>
#define __USE_SYSBASE
#include <proto/exec.h>
#include <proto/dos.h>
#include <clib/alib_protos.h>
#include <string.h>

struct ExecBase *SysBase;
struct DosLibrary *DOSBase;
static struct WBStartup *wbmsg;
static struct RDArgs *myrda;

static UBYTE version[] = "$VER: UnMount 0.6 (8.1.00)";
static UBYTE template[] = "DEVICE,INHIBIT/S,RETRY/K/N,ALL/S,FREENODE/S";

#define DBUFSIZE	16
#define DNAMSIZE	24

struct devBuffer
{	struct devBuffer *dbf_Link;
	UBYTE	dbf_Name[DBUFSIZE][DNAMSIZE];
};

#define	OPT_DEVICE	0
#define OPT_INHIBIT	1
#define	OPT_RETRY	2
#define OPT_ALL		3
#define OPT_FREENODE	4
#define OPTMAX		5

ULONG __saveds main(void);
static void cleanexit(ULONG rc);
static int unmount(STRPTR device, int inhibit, int retry, BOOL verbose);
static struct devBuffer *getdevices(int *nodes);
static void freedbufs(struct devBuffer *dbufs);
static void killdevices(struct devBuffer *dbuf, int numdevs, int inhibit, int retry, int freenode);
static void freeNode(STRPTR device);

ULONG __saveds main(void)	/* No startup code */
{
  struct Process *myproc;
  LONG opts[OPTMAX];
  ULONG rc = 0;

  SysBase = *(struct ExecBase **)4;
  DOSBase = NULL;
  wbmsg = NULL;
  myrda = NULL;

  myproc = (struct Process *)FindTask(NULL);
  if ((DOSBase = (struct DosLibrary *)OpenLibrary("dos.library",36)) == NULL)
  { Alert(AT_Recovery|AG_OpenLib|AO_DOSLib);
    return 100;
  }

  if (!(myproc->pr_CLI))		/* If started from WB, exit cleanly */
  { WaitPort(&(myproc->pr_MsgPort));
    wbmsg = (struct WBStartup *)GetMsg(&(myproc->pr_MsgPort));
    cleanexit(20);
  }
  else
  { APTR oldwinptr;
    int retrycount = 10, error;

    memset((char *)opts, 0, sizeof(opts));
    if ((myrda = ReadArgs(template, opts, NULL)) == NULL)
    { PrintFault(IoErr(),"UnMount");
      cleanexit(20);
    }

    if (opts[OPT_RETRY]) retrycount = *((LONG *)opts[OPT_RETRY]);

    oldwinptr = myproc->pr_WindowPtr;		/* Disable DOS Requesters	*/
    myproc->pr_WindowPtr = (APTR)(-1L);

    if (opts[OPT_DEVICE])
    { struct DosList *dol;
      char buffer[DNAMSIZE];
      STRPTR colon;

      dol = LockDosList(LDF_DEVICES | LDF_READ);
      strncpy(buffer, (STRPTR)opts[OPT_DEVICE], sizeof(buffer));
      if (colon = (UBYTE *)strchr(buffer,':')) *colon = (UBYTE)0;
      else buffer[DNAMSIZE-1] = (UBYTE)0;
      if (dol = FindDosEntry(dol, buffer, LDF_DEVICES))
      { UnLockDosList(LDF_DEVICES | LDF_READ);
	if (error = unmount((STRPTR)opts[OPT_DEVICE], opts[OPT_INHIBIT], retrycount, TRUE))
	{ PrintFault(error, "UnMount");
	  rc = 10;
	}
	else if (opts[OPT_FREENODE]) freeNode((STRPTR)opts[OPT_DEVICE]);
      }
      else
      { UnLockDosList(LDF_DEVICES | LDF_READ);
	PrintFault(ERROR_DEVICE_NOT_MOUNTED, "UnMount");
	rc = 20;
      }

    }
    else if (opts[OPT_ALL])
    { struct devBuffer *dbuf;
      int numdevs;

      if (dbuf = getdevices(&numdevs)) killdevices(dbuf, numdevs, opts[OPT_INHIBIT], retrycount, opts[OPT_FREENODE]);
      else
      { PrintFault(ERROR_NO_FREE_STORE, "UnMount");
	rc = 20;
      }
    }
    else
    { PrintFault(ERROR_REQUIRED_ARG_MISSING, "UnMount");
      rc = 20;
    }

    myproc->pr_WindowPtr = oldwinptr;
  }
  cleanexit(rc);
}

static void cleanexit(ULONG rc)
{
  if (myrda) FreeArgs(myrda);
  if (DOSBase) CloseLibrary((struct Library *)DOSBase);
  if (wbmsg)
  { Forbid();
    ReplyMsg((struct Message *)wbmsg);
  }
  Exit(rc);
}

static int unmount(STRPTR device, int inhibit, int retry, BOOL verbose)
{ struct DevProc *dvp;
  int count = 0, rc = 0;

  if (!strchr(device,':')) return ERROR_INVALID_COMPONENT_NAME;
  if (dvp = GetDeviceProc(device, NULL))
  { if (DoPkt0(dvp->dvp_Port, ACTION_DIE) == DOSFALSE)
    { rc = IoErr();
      if (IsFileSystem(device) && inhibit)
      { if (verbose) Printf("UnMount : Couldn't kill device %s, Trying to inhibit...\n", device);
	do
	{ if (Inhibit(device, DOSTRUE)) break;
	  else if ((rc = IoErr()) == ERROR_ACTION_NOT_KNOWN) count = retry;
	  else Delay(TICKS_PER_SECOND);
        } while (++count < retry);
	if (count < retry) rc = 0;
      }
    }
    FreeDeviceProc(dvp);
  }
  else rc = IoErr();
  return rc;
}

static struct devBuffer *getdevices(int *nodes)
{ struct devBuffer *dbuf;

  if (dbuf = AllocVec(sizeof(struct devBuffer), MEMF_PUBLIC | MEMF_CLEAR))
  { struct DosList *dol;
    struct devBuffer *db = dbuf;
    int i = 0;

    dol = LockDosList(LDF_DEVICES | LDF_READ);
    *nodes = 0;
    while (dol = NextDosEntry(dol, LDF_DEVICES))
    { UBYTE namlen;
      UBYTE *namptr;

      if (dol->dol_Task == NULL) continue;
      namptr = BADDR(dol->dol_Name);
      namlen = (namptr[0] < DNAMSIZE - 1 ? namptr[0] : DNAMSIZE - 2);
      strncpy(db->dbf_Name[i], namptr + 1, namlen);
      db->dbf_Name[i][namlen] = ':';
      db->dbf_Name[i][namlen+1] = '\0';
      if (strcmp(db->dbf_Name[i], "RAM:") == 0) continue;
      (*nodes)++;

      if (++i == DBUFSIZE)
      { struct devBuffer *db1;

	if (db1 = AllocVec(sizeof(struct devBuffer), MEMF_PUBLIC | MEMF_CLEAR))
	{ db->dbf_Link = db1;
	  db = db1;
	  i = 0;
	}
	else
	{ UnLockDosList(LDF_DEVICES | LDF_READ);
	  freedbufs(dbuf);
	  return NULL;
	}
      }
    }
    UnLockDosList(LDF_DEVICES | LDF_READ);
  }
  return dbuf;
}

static void freedbufs(struct devBuffer *dbufs)
{ struct devBuffer *nextdb;

  do
  { nextdb = dbufs->dbf_Link;
    FreeVec(dbufs);
  } while (dbufs = nextdb);
}

static void killdevices(struct devBuffer *dbuf, int numdevs, int inhibit, int retry, int freenode)
{ struct devBuffer *db = dbuf;
  STRPTR devname;
  int j, i, error;

  for (j = 0, i = 0 ; j < numdevs ; j++)
  { devname = db->dbf_Name[i];
    if (IsFileSystem(devname))
    { Printf("Unmounting device %s\n", db->dbf_Name[i]);
      if (error = unmount(devname, inhibit, retry, FALSE)) PrintFault(error, "UnMount");
      else if (freenode) freeNode(devname);
    }

    if (++i == DBUFSIZE)
    { db = db->dbf_Link;
      i = 0;
    }
  }
  freedbufs(dbuf);
}

static void freeNode(STRPTR device)
{ struct DosList *dol;
  char buffer[DNAMSIZE];
  STRPTR colon;

  dol = LockDosList(LDF_DEVICES | LDF_WRITE);
  strncpy(buffer, device, sizeof(buffer));
  if (colon = (UBYTE *)strchr(buffer,':')) *colon = (UBYTE)0;
  else buffer[DNAMSIZE-1] = (UBYTE)0;
  if (dol = FindDosEntry(dol, buffer, LDF_DEVICES))
  { RemDosEntry(dol);
    FreeDosEntry(dol);
  }
  UnLockDosList(LDF_DEVICES | LDF_WRITE);
}
