[comp.sources.amiga] v02i091: null - dos handler like nil:

page@swan.ulowell.edu (Bob Page) (12/06/88)

Submitted-by: gno@stacken.kth.se (Gunnar Nordmark)
Posting-number: Volume 2, Issue 91
Archive-name: devices/null.1

This is a dos-handler called null: that acts like NIL: except this is
is a *real* handler.

[uuencoded binary also included here; it's small.  ..Bob]

#	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:
#	Makefile
#	MountList
#	amigaline-D1
#	misc.c
#	null.c
#	null.doc
#	null.uu
# This archive created: Mon Dec  5 17:54:58 1988
cat << \SHAR_EOF > Makefile
#########################################################################
#
#		null-handler Makefile
#		V 0.0 (c) Gunnar Nordmark 1988
#		For Manx 3.6a
#
#########################################################################


null-handler	: misc.o null.o
	ln  -o null-handler misc.o null.o -lc32

misc.o	: misc.c 
	cc +L +Hnull.syms misc.c

null.o	: null.c
	cc +L +Inull.syms null.c
SHAR_EOF
cat << \SHAR_EOF > MountList

/*  
	Installs null-handler V 0.0 (C) Gunnar Nordmark
*/

null:      Handler = L:null-handler
           Stacksize = 500
           Priority = 5
	   GlobVec = 1
#
SHAR_EOF
cat << \SHAR_EOF > amigaline-D1
	AMIGALINE #D1,	Matthew Dillon

	Disconnecting a program such that you can EndCLI and also allow the
	program to call Execute().

Problem:

	You want to disconnect a program such that when you RUN <nil: >nil:
	(using the new 1.3 RUN) you can then EndCLI the cli.

	This program wants to be able to use Execute() to run other programs. 
	The problem is that Execute() requires a valid pr_ConsoleTask (console)
	or it will freeze.

Solution: General

	Run <nil: >nil: mycprogram

	If using the main() entry point, you can fclose(stderr) to remove
	the reference to "*".  If using the _main() entry point, stdio is
	not setup and thus you do not need to do this (in fact, you can't
	use stdio at all without crashing the computer).

	note: being able to fclose(stderr) from the main() entry point 
	works with Aztec C.  I don't know about Lattice.  Aztec always
	does an Open("*", 1006) to setup stderr and this reference must
	be removed.

					--

	At this point, you can EndCLI and the cli window goes away.  However,
	the 'mycprogram' cannot call Execute() or otherwise run other 
	external programs for two reasons:

		(1) pr_ConsoleTask is still non-NULL and points to the now
		    defunct window (i.e. you will cause a task-held requester)

		(2) you cannot set pr_ConsoleTask to NULL... Execute() does
		    not accept it and freezes up.

					--

	So, you must set pr_ConsoleTask to some other valid device.  Guess
	what?  Any device will do except NIL: which isn't a real device.  For
	example, RAM: :

		extern APTR DeviceProc();
		proc->pr_ConsoleTask = DeviceProc("ram:");

		(assuming RAM: exists)

	What does this do?  Any program which tries to open the console will
	actually open the file "RAM:*", as in Open("RAM:*", 1006).  
	Unfortunetly, there is no way to place "*" in anything but the 
	root directory of the device.  This is essentially a garbage file.

	But the ultimate goal is achieved ... you can kill the CLI window
	and still arbitrarily run programs from the detached program with
	impunity.

	The only possible problem which I have yet to test is when several
	programs try to access RAM:* as their console at the same time.  
	Since the file is openned 1006, other programs trying to Open() it
	will fail while the first programs is still running.  What happens?

							-Matt
SHAR_EOF
cat << \SHAR_EOF > misc.c
/*
 *  misc.c  - support routines - Phillip Lindsay (C) Commodore 1986
 *  You may freely distribute this source and use it for Amiga Development -
 *  as long as the Copyright notice is left intact.
 *
 * 30-SEP-86
 *
 */
#include <functions.h>
#include <stdio.h>
#include <exec/types.h>
#include <exec/nodes.h>
#include <exec/lists.h>
#include <exec/ports.h>
#include <exec/libraries.h>
#include <exec/devices.h>
#include <exec/io.h>
#include <exec/memory.h>
#include <devices/console.h>
#include <libraries/dos.h>
#include <libraries/dosextens.h>
#include <libraries/filehandler.h>

extern void returnpkt();

/* returnpkt() - packet support routine
 * here is the guy who sends the packet back to the sender...
 *
 * (I modeled this just like the BCPL routine [so its a little redundant] )
 */

void
returnpktplain(packet, myproc)
struct DosPacket *packet;
struct Process *myproc;
{
    returnpkt(packet, myproc, packet->dp_Res1, packet->dp_Res2);
}

void
returnpkt(packet, myproc, res1, res2)
struct DosPacket *packet;
struct Process *myproc;
ULONG  res1, res2;
{
    struct Message *mess;
    struct MsgPort *replyport;

    packet->dp_Res1          = res1;
    packet->dp_Res2          = res2;
    replyport                = packet->dp_Port;
    mess                     = packet->dp_Link;
    packet->dp_Port          = &myproc->pr_MsgPort;
    mess->mn_Node.ln_Name    = (char *) packet;
    mess->mn_Node.ln_Succ    = NULL;
    mess->mn_Node.ln_Pred    = NULL;
    PutMsg(replyport, mess);
}


/*
 * taskwait() ... Waits for a message to arrive at your port and
 *   extracts the packet address which is returned to you.
 */

struct DosPacket *
taskwait(myproc)
struct Process *myproc;
{
    struct MsgPort *myport;
    struct Message *mymess;

    myport = &myproc->pr_MsgPort;
    WaitPort(myport);
    mymess = GetMsg(myport);
    return((struct DosPacket *)mymess->mn_Node.ln_Name);
}

/* end of misc.c    */


SHAR_EOF
cat << \SHAR_EOF > null.c
/****************************************************************************
 *
 *  null driver V0.0 (c)CopyRight 1988, Gunnar Nordmark.  All Rights Reserved.
 *  
 *  null-handler Ver. 0.0  20-Jul-1988
 *
 *  Gunnar Nordmark
 *  Nora strand 5
 *  182 34 DANDERYD
 *  SWEDEN
 *
 *  |You may freely distribute this source as long as |
 *  |the Copyright notice is left intact.	      |
 ***************************************************************************/

#undef  BADDR
#define BADDR(x)   ((APTR)((long)x << 2))

#define ACTION_FINDINPUT	1005L
#define ACTION_FINDOUTPUT	1006L
#define ACTION_END	 	1007L

#define DOS_FALSE    		0L
#define DOS_TRUE     	       -1L

/* My Globals */

long		    SysBase;
struct Process      *myproc;

_main()
{
    extern void returnpkt();		/* sends back the packet          */
    extern void returnpktplain();	/* use args in Res1               */
    extern struct DosPacket *taskwait();

    	   char       *version = 	"Ver 0.0 (c) Gunnar Nordmark 1988";
    struct DosPacket  *mypkt;      	/* a pointer to the dos packet    */
    struct DeviceNode *mynode;     	/* our device node (parmpkt Arg3) */
    struct FileHandle *fh;	 	/* a pointer to our file handle	  */
    long	      run = TRUE;	/* handler main loop flag	  */
    int		      null_open = 0;	/* null open count		  */

 
	/* Initializing the handler */

    myproc      = (struct Process *)FindTask(0L);
    mypkt       = taskwait(myproc);      /* Wait for my startup message */

	/* I don't need the name or extra info passed in Arg1/2 */

    mynode      	= (struct DeviceNode *)BADDR(mypkt->dp_Arg3);
    mynode->dn_Task 	= &myproc->pr_MsgPort; 
    returnpkt(mypkt, myproc, DOS_TRUE, mypkt->dp_Res2); 

	/* done initial stuff, now for some work */

    while(run) {
	mypkt = taskwait(myproc);

	switch(mypkt->dp_Type) {	/* find what action to perform */

	case ACTION_FINDINPUT:
	case ACTION_FINDOUTPUT:

	    null_open++;

	    fh = (struct FileHandle  *)BADDR(mypkt->dp_Arg1);
	    fh->fh_Arg1 = mypkt->dp_Type;
	    fh->fh_Port = (struct MsgPort *)DOS_FALSE; /* not interactive */

	    returnpkt(mypkt, myproc, DOS_TRUE, mypkt->dp_Res2);
	    break;

	case ACTION_READ:

	    returnpkt(mypkt, myproc, 0, mypkt->dp_Res2); /* zero-length=EOF */
	    break;

	case ACTION_WRITE:
	    
	    mypkt->dp_Res1 = mypkt->dp_Arg3;  /* tell em we wrote them all */
	    returnpktplain(mypkt, myproc);
	    break;

	case ACTION_END:

	    if (--null_open == 0)
		run = 0;

	    returnpkt(mypkt, myproc, DOS_TRUE, mypkt->dp_Res2);
	    break;

	default:

	    returnpkt(mypkt, myproc, DOS_FALSE, ERROR_ACTION_NOT_KNOWN);
	    break;
	}
    } /* end while */
    mynode->dn_Task = FALSE; 
}
SHAR_EOF
cat << \SHAR_EOF > null.doc
***************************************************************************
*
*	null.doc	20 Nov 88	Gunnar Nordmark
*
***************************************************************************

What is null:?

Tt's a new animal that you will be glad to put in your private ZOO,
(also known as the L: directory).

"Oh no, not another silly handler!" you cry, but read on!

In the very good article by Matt Dillon, AMIGALINE#D1, that I've included
in this distribution, the problem is fully explained.
I suggests that you take a look at the file amigaline-D1 before reading
further.

As Matt explains, the NIL: device is not a "real" device. In fact it is no
device at all in the physical sense. It is just an Amiga-DOS convention
that says: If you have a process-identifier for a device, and that identifier
is just a null pointer, then that "identifier" referrs to the NIL: device.

Did you get that? I'm not surprised if you didn't, because it *is* strange.

I'll try to explain:
Every normal dos-device is a process that have an own private message-port
that is used as a comunication link between AmigaDOS and the device.
Since you must know the address of the message port of a device in order
to comunicate with it, it is a natural decision that that address also is
used as the identifier of the device.

AmigaDOS has a function called DeviceProc() that finds the address for you.
If you say  port=DeviceProc("RAM:")  you get the address of the message prot
of the RAM: device.

Now the strange part:
If you try  port=DeviceProc("NIL:")  and then sends packets to that port
you'll crash the machine instantly.
Why? 
Because NIL: has *no* message-port at all.
When you have a NULL-pointer as identifier for a handler, the identifier
referrs to the NIL: device.

The *only* reason I can think of for this lack of generality, is efficiency.
You save time, becase you don't have to comunicate with a device.
You save memory, because you don't have to start a new process named NIL:.

But unfortunately this has a lot of drawbacks. Most of them are deadly.
The reason is that AmigaDOS does *not* allways check for the NIL: oddity.
Sometimes it thinks that the NULL-pointer (the NIL: identifier) is a
valid message port and merilly sends messages to it. Poof!

The solution is a new dos-device called "null:".
This is a real dos-device that has has its own message port. It just doesn't
do anything useful. It behaves exactly as NIL: i.e. you can write to it,
and read from it (then you get EOF).

The great thing about null: is that you can say

		extern APTR DeviceProc();
		proc->pr_ConsoleTask = DeviceProc("null:");

and you'll be able to close the console. (see amigaline-D1)

Any process can open null: as many times as it like.
And it doesn't matter if you say
		Open("null:" MODE_OLDFILE);
		Open("null:*" MODE_NEWFILE);
		Open("null:@#$@!!" MODE_OLDFILE);
		Open("null:foo.bar" MODE_OLDFILE);
everything works.

To install null: just append the included mountlist to DEVS:MountList
Copy null-handler to your L: directory and say  Mount null:

I have used it for half a year now with no problems at all. I have a
written my own version of AmiCron that uses null: as pr_ConsoleTask.
That means I now have a cron-deamon that doesn't requires a window
to run!

I wan't to thank Steve Drew who wrote the aux-handler that was posted
to the net last year. I've "stolen" all the code from that handler. :-)
I hope you don't mind Steve.

  - Gunnar

  Gunnar Nordmark
  Nora strand 5
  S-182 34 DANDERYD
  SWEDEN

  gno@stacken.kth.se
  stacken.kth.se!gno@uunet.uu.net  (if the above doesn't work)
  gno@SESTAK.BITNET
SHAR_EOF
cat << \SHAR_EOF > null.uu

begin 644 null-handler
M```#\P`````````#``````````(```#*````%@````$```/I````RD[Z`FY.8
M50``(&T`""\H`!`@;0`(+R@`#"\M``PO+0`(80A/[P`03EU.=4Y5__@@;0`(I
M(6T`$``,(&T`""%M`!0`$"!M``@K:``$__@@;0`(*U#__"!M``@B;0`,T_P`>
M``!<(4D`!"!M__PA;0`(``H@;?_\0I`@;?_\0J@`!"\M__PO+?_X3KH"@E!/7
M3EU.=4Y5__@@;0`(T?P```!<*TC__"\M__Q.N@)P6$\O+?_\3KH"2%A/*T#_F
M^"!M__@@*``*3EU.=4Y5_^A!^@&"*TC__"M\`````?_L0JW_Z$*G3KH""%A/]
M*4"`$B\L@!).NO^@6$\K0/_X(&W_^"`H`!SE@"M`__0@;?_T(FR`$M/\````R
M7"%)``@@;?_X+R@`$$AX__\O+(`2+RW_^$ZZ_OQ/[P`02JW_[&<``0@O+(`2&
M3KK_3EA/*T#_^"!M__@@*``(8```QE*M_^@@;?_X("@`%.6`*T#_\"!M__@B#
M;?_P(V@`"``D(&W_\$*H``0@;?_X+R@`$$AX__\O+(`2+RW_^$ZZ_I9/[P`00
M8```HB!M__@O*``00J<O+(`2+RW_^$ZZ_GA/[P`08```A"!M__@B;?_X(V@`;
M'``,+RR`$B\M__A.NOXP4$]@9%.M_^AF!$*M_^P@;?_X+R@`$$AX__\O+(`2"
M+RW_^$ZZ_C!/[P`08#Q(>`#10J<O+(`2+RW_^$ZZ_AA/[P`08"20O````%)GB
M`/]\6X!GE)"\```#EF<`_R93@&<`_R!3@&>>8,1@`/[T(&W_]$*H``A.74YU)
M5F5R(#`N,"`H8RD@1W5N;F%R($YO<F1M87)K(#$Y.#@``&%P0^R`#D7L@`ZU4
MR68.,CP`$FL(=``BPE')__PI3X`6+'@`!"E.@`Y(YX"`""X`!`$I9Q!+^@`(O
M3J[_XF`&0J?S7TYS0_H`($ZN_F@I0(`:9@PN/``#@`=.KO^48`1.NOWV4$]."
M=61O<RYL:6)R87)Y`$GY``!__DYU3OH``B)O``0L;(`.3N[^VD[Z``(@;P`$6
M+&R`#D[N_HQ,[P,```0L;(`.3N[^DD[Z``(@;P`$+&R`#D[N_H````/L````,
M`0````$```+D`````````_(```/J`````P`4`````````````````_(```/K$
(`````0```_+D:
``
end
size 908
SHAR_EOF
#	End of shell archive
exit 0
-- 
Bob Page, U of Lowell CS Dept.  page@swan.ulowell.edu  ulowell!page
Have five nice days.