1 /****************************************************************************
2 ** File: pipe-handler.c
3 ** Program: pipe-handler - an AmigaDOS handler for named pipes
5 ** Author: Ed Puckett qix@mit-oz
7 ** Copyright 1987 by EpAc Software. All Rights Reserved.
9 ** History: 05-Jan-87 Original Version (1.0)
10 ** 07-Feb-87 Added shared locks for individual pipes.
11 ** PIPEDATA structure modified to include
12 ** a FileLock structure.
13 ** 07-Feb-87 Added #if's forautomatic pipe naming "feature"
14 ** for pipes specified with empty names.
15 ** 12-Feb-87 Added ParentDir packet handling.
16 ** 12-Feb-87 Fixed bug in OpenPipe() and PipeLock():
17 ** they previously ignored the lock passed in
18 ** packet. Bug uncovered when pipes became
19 ** lockable, and thus assignable.
20 ** 27-Mar-87 Added the case for PipeDupLock(). This was
21 ** missing in the original version!
22 ** 28-Mar-87 Added code to handler() to remove ':' from
23 ** end of handler name. This caused problems
24 ** with Examine(); it expects no ending ':'.
27 #include <libraries/dos.h>
28 #include <libraries/dosextens.h>
29 #include <libraries/filehandler.h>
30 #include <exec/exec.h>
32 #include <proto/exec.h>
33 #include <proto/dos.h>
34 #include <proto/alib.h>
36 #include "pipelists.h"
39 #include "pipecreate.h"
40 #include "pipesched.h"
41 #include "pipe-handler.h"
48 # include "pipedebug.h"
53 /*---------------------------------------------------------------------------
56 ** This is the main module for the handler. Handlers are started with
57 ** register D1 containing a BPTR to a startup packet, which in turn contains
58 ** (BCPL) pointers to the name and DeviceNode. Since the entry, handler(),
59 ** expects a byte address of the startup packet, an assembly language startup
60 ** must be used to convert the BCPL pointer, and pass it on the stack.
62 ** Problems arise if a handler tries to do I/O via the DOS functions Open(),
63 ** Close(), Read() and Write(). DOS sends request packets to the handler
64 ** via its DOS port (the one whose address forms the process ID). This is
65 ** also the port used by the I/O functions. Therefore, if a request comes,
66 ** and then an Open() call is performed, DOS will send a request packet for
67 ** the open and erroneously pick up the request packet meant for the handler
68 ** as its reply. A crash ensues.
70 ** This is the reason for the I/O functions in pipedebug.c. They implement
71 ** the regular I/O calls, but use a different ReplyPort. With no debugging,
72 ** these functions are unneeded, since all of the handler's normal I/O is
73 ** performed asynchronously, using PutMsg().
75 ** An alternate solution is to patch the handler's Task field with a new port
76 ** instead of the handler's DOS port. This works, except that DOS always
77 ** sends the initial request packets to the DOS port (when the handler is
78 ** first started). This is probably because DeviceProc(), upon seeing that
79 ** the handler has not yet been loaded, returns the result from its call to
80 ** CreateProc() for the handler process. Only on subsequent calls to
81 ** DeviceProc() will the patched field be returned. The upshot of this is
82 ** that an alternate port can be used for handler requests, but there are
83 ** always an unspecified number that may come over the DOS port regardless.
84 ** Note that since not all handlers patch their Task field (because they want
85 ** to be restarted each time), DOS is doing the "right" thing, or at least
90 ** void handler (StartPkt)
91 ** PIPEDATA *FindPipe (name)
93 ** Macros (in pipe-handler.h)
94 ** --------------------------
97 ** QuickReplyPkt (pkt)
101 ** struct DosPacket *QuickGetPkt (port)
106 /*---------------------------------------------------------------------------
107 ** HandlerName : passed as a BSTR in startup packet Arg1, our device name.
108 ** Everything from the ':' and beyond is removed.
109 ** Used by PipeExamine() for the handler's "directory" name.
111 ** DevNode : passed as a BPTR in startup packet Arg3. This is a pointer
112 ** to our DeviceNode entry in the system device list (DevInfo).
114 ** PipePort : our DOS MsgPort, as well as our process ID. See above for
115 ** notes about why we can't let DOS use this.
117 ** pipelist : the list of currently existing pipes. PIPEDATA nodes are
118 ** linked into this list.
120 ** tapwaitlist : the list of requests waiting on tap opens/closes/writes.
121 ** WAITINGDATA nodes are linked into this list. See pipesched.c
124 ** TapReplyPort : this is the MsgPort to which tap I/O replys are returned.
127 ** DOSBase : Standard system library pointers. Since we don't have the
128 ** usual startup code, we must initialize these ourselves.
130 ** PipeDate : If compiled with PIPEDIR true, the handler responds to some
131 ** directory-like actions. This is the date for the entire
132 ** handler, i.e., the directory date. The flag UPDATE_PIPEDATE
133 ** controls whether this date is updated with each pipe access
134 ** (true) or not (false). See SetPipeDate() and PipeExamine().
137 char HandlerName
[30];
138 struct DeviceNode
*DevNode
= NULL
;
139 struct MsgPort
*PipePort
= NULL
;
141 PIPELISTHEADER pipelist
;
143 PIPELISTHEADER tapwaitlist
;
144 struct MsgPort
*TapReplyPort
= NULL
;
147 struct Library
*SysBase
= NULL
;
149 struct DosLibrary
*DOSBase
= NULL
;
152 struct DateStamp PipeDate
;
155 static struct DosPacket
*QuickGetPkt (register struct MsgPort
*port
);
158 /*---------------------------------------------------------------------------
159 ** Performs initialization, replies to startup packet, and dispatches
160 ** incoming request packets to the apropriate functions. The TapReplyPort is
161 ** also monitored for returning requests which were sent out by the handler.
162 ** These returned requests are routed to HandleTapReply().
163 ** Our DeviceNode Task field is patched with our process ID so that this
164 ** process is used for subsequent handler requests. The function exits only
165 ** if there is some initialization error.
168 void handler (StartPkt
)
170 struct DosPacket
*StartPkt
;
174 ULONG PipeMask
, TapReplyMask
, WakeupMask
, SigMask
;
175 struct DosPacket
*pkt
;
178 SysBase
= AbsExecBase
;
181 if ((DOSBase
= (APTR
)OpenLibrary (DOSNAME
, 0)) == NULL
)
184 BSTRtoCstr (BPTRtoCptr (StartPkt
->dp_Arg1
), HandlerName
, sizeof (HandlerName
));
185 for (cp
= HandlerName
; *cp
!= '\0'; ++cp
)
186 if (*cp
== ':') /* remainder of handler's first refernece follows */
191 Task
= FindTask (NULL
);
192 PipePort
= &((struct Process
*)Task
)->pr_MsgPort
;
193 ((struct Process
*) Task
)->pr_CurrentDir
= 0; /* initial file system root */
195 if ((TapReplyPort
= CreatePort (NULL
, PipePort
->mp_Node
.ln_Pri
)) == NULL
)
199 if (! InitDebugIO (PipePort
->mp_Node
.ln_Pri
))
204 PipeMask
= (1L << PipePort
->mp_SigBit
);
205 TapReplyMask
= (1L << TapReplyPort
->mp_SigBit
);
206 WakeupMask
= (PipeMask
| TapReplyMask
);
208 DevNode
= (struct DeviceNode
*) BPTRtoCptr (StartPkt
->dp_Arg3
);
209 DevNode
->dn_Task
= PipePort
;
211 InitList (&pipelist
);
212 InitList (&tapwaitlist
);
215 (void) DateStamp (&PipeDate
);
218 StartPkt
->dp_Res1
= DOSTRUE
;
219 QuickReplyPkt (StartPkt
);
223 SigMask
= Wait (WakeupMask
);
225 if (SigMask
& TapReplyMask
)
226 while ((pkt
= QuickGetPkt (TapReplyPort
)) != NULL
)
227 HandleTapReply (pkt
);
229 if (SigMask
& PipeMask
)
230 while ((pkt
= QuickGetPkt (PipePort
)) != NULL
)
231 switch (pkt
->dp_Type
)
232 { case MODE_READWRITE
:
234 OS ("Open READWRITE packet received\n");
239 case MODE_READONLY
: /* syn: MODE_OLDFILE, ACTION_FINDINPUT */
241 OS ("Open READONLY packet received\n");
246 case MODE_NEWFILE
: /* syn: ACTION_FINDOUTPUT */
248 OS ("Open NEWFILE packet received\n");
255 OS ("Close packet received\n");
262 OS ("<<< Read packet received\n");
264 StartPipeIO (pkt
, PIPEREAD
);
269 OS (">>> Write packet received\n");
271 StartPipeIO (pkt
, PIPEWRITE
);
275 case ACTION_LOCATE_OBJECT
:
277 OS ( "Lock packet received\n");
282 case ACTION_FH_FROM_LOCK
:
284 OS ( "FHFromLock packet received\n");
286 PipeFHFromLock (pkt
);
289 case ACTION_COPY_DIR
:
291 OS ( "DupLock packet received\n");
296 case ACTION_COPY_DIR_FH
:
298 OS ( "DupLockFH packet received\n");
303 case ACTION_FREE_LOCK
:
305 OS ( "UnLock packet received\n");
310 case ACTION_EXAMINE_OBJECT
:
312 OS ( "Examine packet received\n");
317 case ACTION_EXAMINE_NEXT
:
319 OS ( "ExNext packet received\n");
324 case ACTION_EXAMINE_FH
:
326 OS ( "ExFH packet received\n");
333 OS ( "ParentDir packet received\n");
338 case ACTION_PARENT_FH
:
340 OS ( "ParentFH packet received\n");
348 OS ("BAD packet received, type = "); OL (pkt
->dp_Type
); NL
;
351 pkt
->dp_Res2
= ERROR_ACTION_NOT_KNOWN
;
359 DevNode
->dn_Task
= NULL
; /* bad if someone in process of accessing us . . . */
361 if (TapReplyPort
!= NULL
)
362 FreeMem (TapReplyPort
, sizeof (struct MsgPort
)); /* signal bit won't matter */
369 CloseLibrary ((APTR
)DOSBase
);
374 /*---------------------------------------------------------------------------
375 ** Returns the DosPacket associated with the next message on "port", or NULL
376 ** if the port is empty. The message is removed from the port.
377 ** A related macro, QuickReplyPkt() is provided in pipe-handler.h.
379 static struct DosPacket
*QuickGetPkt (port
)
381 register struct MsgPort
*port
;
383 { register struct Message
*msg
;
385 return ((msg
= GetMsg (port
)) == NULL
)
387 : (struct DosPacket
*) msg
->mn_Node
.ln_Name
;
392 /*---------------------------------------------------------------------------
393 ** Searches "pipelist" for a pipe whose name is "name". If found, a pointer
394 ** to the pipe returns. Otherwise, NULL returns.
397 PIPEDATA
*FindPipe (name
)
405 for (p
= (PIPEDATA
*) FirstItem (&pipelist
); p
!= NULL
; p
= (PIPEDATA
*) NextItem (p
))
406 { cp
= strdiff (name
, p
->name
);
408 if ((*cp
== '\0') && (p
->name
[(const UBYTE
*)cp
- (const UBYTE
*)name
] == '\0'))
409 return p
; /* same name */
412 return NULL
; /* no match found */