/* monproc.c	- monitor amigados process packet activity.
 *
 * Phillip Lindsay (c) 1987 Commodore-Amiga, Inc.
 *
 */

#include <exec/types.h>
#include <exec/nodes.h>
#include <exec/lists.h>
#include <exec/memory.h>
#include <exec/ports.h>
#include <exec/tasks.h>
#include <exec/semaphores.h>
#include <libraries/dos.h>
#include <libraries/dosextens.h>
#include <libraries/filehandler.h>

#ifdef MANX
#include <functions.h>
#else
#define index stpchr	/* can you say "ANSI STANDARD"...I knew you could... */
#endif
#include <stdio.h>

/* constants */

#define ONE			1L

/* AmigaDOS uses task signal bit 8 for message signaling */
#define DOS_SIGNAL		8
#define DOS_MASK		( ONE << DOS_SIGNAL )

/* referenced globals */
extern void		 getprocs(),freeprocs(),givepkt();
void			 memfill();
extern struct DosLibrary *DOSBase;
struct Message		 *packetwait();
LONG			 pw();	/* this the asm stub that calls packetwait() */

/* global data structures */
struct MsgPort         *deviceid;
struct Process         *memyselfNi;
ULONG		       mymask;
struct SignalSemaphore canreturn;
struct Message         *mymess;


main()
{
 struct   List			procs;
 struct   Node			*proc;
 register struct Process	*devproc;
 register struct DosPacket	*pkt;
 BYTE				mypri;	
 register ULONG			waitmask,signals,oldpktwait;
 LONG				mysignal;
 ULONG				count,choice;
 UBYTE				processname[81];

 puts("MONPROC - monitor AmigaDOS process packet activity.\n");

 memyselfNi = (struct Process *) FindTask(NULL);
 NewList(&procs); 	/* initialize list header */
 getprocs(&procs);	/* fill list with "active" devices */

 if(procs.lh_TailPred == &procs) /* list empty (should never happen) */
  {
   puts("No processes.");
   exit(0);
  }

/*
 * I am assuming that process list won't change. This is for hackers only.
 */
 do
  {
   puts("Enter NUMBER for process to monitor:"); 
   puts("Pick# Process  MsgPort  Name");    
   for(count = 1,proc = procs.lh_Head;proc->ln_Succ;proc=proc->ln_Succ,count++)
     printf("%4ld. %08lx %08lx %s\n",count,
         ((struct Process *)proc->ln_Name),
         &(((struct Process *)proc->ln_Name)->pr_MsgPort),
         ((struct Process *)proc->ln_Name)->pr_Task.tc_Node.ln_Name);
   printf("\nNUMBER: ");
   fflush(stdout);  
  } while( scanf("%ld",&choice) == EOF ) ;


 if(!choice || choice >= count)
  {
   puts("Operation aborted.");
   freeprocs(&procs);
   exit(0);
  }

/* what did he/she pick? */
  for(count = 1,proc = procs.lh_Head;proc->ln_Succ;proc=proc->ln_Succ,count++)
   if(choice == count)
    {
     devproc = (struct Process *) proc->ln_Name;
     break;
    }

/* we don't need our process list anymore */
 freeprocs(&procs);
 
/* make a copy of the process name */
 strcpy(processname,devproc->pr_Task.tc_Node.ln_Name);

/* get process msgport of process we will be monitoring */
 deviceid = &devproc->pr_MsgPort;

/* grab a signal for task intercommunication */
 mysignal = (LONG) AllocSignal(-1L);
 if(mysignal == -1)
  {
   puts("Can't allocate a task signal.");
   exit(0);
  }

 mymask = ONE << mysignal;

 waitmask = SIGBREAKF_CTRL_C | mymask;

/* initialize our semaphore */
 memfill(&canreturn,(ULONG)sizeof(canreturn),0L);
 InitSemaphore(&canreturn);

 Forbid();
 mypri = devproc->pr_Task.tc_Node.ln_Pri;
 Permit();
 
/* make sure our priority is greater than the device task  */
 SetTaskPri(memyselfNi, (ULONG)(mypri+1) ); 
 
 puts("Waiting for messages...[CTRL_C to quit monitoring...]"); 

 /* install our packet wait function */
 Forbid();
 oldpktwait          = (ULONG) devproc->pr_PktWait;
 devproc->pr_PktWait = (APTR)  pw;
 Permit();

/*
 * I am using semaphores to control the monitoring of the handler's packets.
 * There is probably a hundred better ways of doing this. I just went with
 * what first came to mind. Since I am a higher priority than the handler...
 * Once I am awake I will obtain the semaphore before the lower priority
 * task has a chance...I will give it back to him once I am done with the
 * packet data structure. 
 */
do 
  {

   signals = (ULONG) Wait(waitmask); 
   ObtainSemaphore(&canreturn);	

   if(signals & mymask)             /* did the packetwait code signal us? */
    {                               /* yes, let's boogie...             */
     pkt = (struct DosPacket *) mymess->mn_Node.ln_Name; /* get packet address */
     printf("%s: ",processname);     
     givepkt(pkt);
    } 

   ReleaseSemaphore(&canreturn);

  } while(!(signals & SIGBREAKF_CTRL_C)); 

 /* remove our packet wait function and install previous value */
 Forbid();
 devproc->pr_PktWait = (APTR) oldpktwait;
 Permit();
 SetTaskPri(memyselfNi,0L);
 puts("\nThere is no safe way to leave since the process is waiting in");
 puts("our installed code. We have de-installed ourselves, but you should");
 puts("be certain that the process received a packet AFTER we have been");
 puts("de-installed. Press CTRL-E if you are certain this has happened.");
 Wait(SIGBREAKF_CTRL_E);
 FreeSignal(mysignal);
 puts("All done.");

} /* end of main() */


/**
 ** local subroutines 
 **/

/*
 * packetwait() ... Waits for a message to arrive at your port and 
 *   returns it to you.
 */
struct Message *packetwait()
{
#ifdef MANX	/* if MANX make sure we can see our data */
 geta4();
#endif
 /* wait for packet */
 SetSignal(FALSE,DOS_MASK);	/* clear sigbit 8 */
 while( !(mymess = (struct Message *) GetMsg(deviceid)) )  Wait(DOS_MASK);
 Signal(memyselfNi,mymask); /* Wake up monitoring task he will grab the data */
 ObtainSemaphore(&canreturn); /* we get this guy when we CAN return */	
 ReleaseSemaphore(&canreturn);

 return(mymess); 
} 

/*
 * memfill() - fill memory with supplied byte value for "size" bytes
 */
void memfill(source,size,value)
UBYTE *source;
ULONG size;
UBYTE value;	
{
 register UBYTE *msource=source,
		mvalue=value;
 register ULONG msize=size;

 if(msize)
  {
   do
    {
     --msize;
     msource[msize] = mvalue;
    } while(msize);

  }
}

/*
 * This code stub has been known to save lives... 
 */

#if MANX 	
#asm
	XREF _packetwait		

 	XDEF _pw
_pw:
	movem.l a2/a3/a4,-(sp)
	jsr _packetwait
	movem.l (sp)+,a2/a3/a4
	rts
#endasm
#endif

/* end of procdev.c */
