/**************************************************************************** ** File: pipe-handler.c ** Program: pipe-handler - an AmigaDOS handler for named pipes ** Version: 1.2 ** Author: Ed Puckett qix@mit-oz ** ** Copyright 1987 by EpAc Software. All Rights Reserved. ** ** History: 05-Jan-87 Original Version (1.0) ** 07-Feb-87 Added shared locks for individual pipes. ** PIPEDATA structure modified to include ** a FileLock structure. ** 07-Feb-87 Added #if's forautomatic pipe naming "feature" ** for pipes specified with empty names. ** 12-Feb-87 Added ParentDir packet handling. ** 12-Feb-87 Fixed bug in OpenPipe() and PipeLock(): ** they previously ignored the lock passed in ** packet. Bug uncovered when pipes became ** lockable, and thus assignable. ** 27-Mar-87 Added the case for PipeDupLock(). This was ** missing in the original version! ** 28-Mar-87 Added code to handler() to remove ':' from ** end of handler name. This caused problems ** with Examine(); it expects no ending ':'. */ #include #include #include #include #include "pipelists.h" #include "pipename.h" #include "pipebuf.h" #include "pipecreate.h" #include "pipesched.h" #include "pipe-handler.h" #if PIPEDIR # include "pipedir.h" #endif PIPEDIR #ifdef DEBUG # include "pipedebug.h" #endif DEBUG /*--------------------------------------------------------------------------- ** pipe-handler.c ** -------------- ** This is the main module for the handler. Handlers are started with ** register D1 containing a BPTR to a startup packet, which in turn contains ** (BCPL) pointers to the name and DeviceNode. Since the entry, handler(), ** expects a byte address of the startup packet, an assembly language startup ** must be used to convert the BCPL pointer, and pass it on the stack. ** ** Problems arise if a handler tries to do I/O via the DOS functions Open(), ** Close(), Read() and Write(). DOS sends request packets to the handler ** via its DOS port (the one whose address forms the process ID). This is ** also the port used by the I/O functions. Therefore, if a request comes, ** and then an Open() call is performed, DOS will send a request packet for ** the open and erroneously pick up the request packet meant for the handler ** as its reply. A crash ensues. ** ** This is the reason for the I/O functions in pipedebug.c. They implement ** the regualar I/O calls, but use a different ReplyPort. With no debugging, ** these functions are unneeded, since all of the handler's normal I/O is ** performed asynchronously, using PutMsg(). ** ** An alternate solution is to patch the handler's Task field with a new port ** instead of the handler's DOS port. This works, except that DOS always ** sends the initial request packets to the DOS port (when the handler is ** first started). This is probably because DeviceProc(), upon seeing that ** the handler has not yet been loaded, returns the result from its call to ** CreateProc() for the handler process. Only on subsequent calls to ** DeviceProc() will the patched field be returned. The upshot of this is ** that an alternate port can be used for handler requests, but there are ** always an unspecified number that may come over the DOS port regardless. ** Note that since not all handlers patch their Task field (because they want ** to be restarted each time), DOS is doing the "right" thing, or at least ** the best it can. ** ** Visible Functions ** ----------------- ** void handler (StartPkt) ** PIPEDATA *FindPipe (name) ** ** Macros (in pipe-handler.h) ** -------------------------- ** BPTRtoCptr (Bp) ** CptrtoBPTR (Cp) ** ReplyPkt (pkt) ** ** Local Functions ** --------------- ** struct DosPacket *GetPkt (port) */ /*--------------------------------------------------------------------------- ** HandlerName : passed as a BSTR in startup packet Arg1, our device name. ** Everything from the ':' and beyond is removed. ** Used by PipeExamine() for the handler's "directory" name. ** ** DevNode : passed as a BPTR in startup packet Arg3. This is a pointer ** to our DeviceNode entry in the system device list (DevInfo). ** ** Pipeort : our DOS MsgPort, as well as our process ID. See above for ** notes about why we can't let DOS use this. ** ** pipelist : the list of currently existing pipes. PIPEDATA nodes are ** linked into this list. ** ** tapwaitlist : the list of requests waiting on tap opens/closes/writes. ** WAITINGDATA nodes are linked into this list. See pipesched.c ** and pipecreate.c. ** ** TapReplyPort : this is the MsgPort to which tap I/O replys are returned. ** ** SysBase, ** DOSBase : Standard system library pointers. Since we don't have the ** usual startup code, we must initialize these ourselves. ** ** PipeDate : If compiled with PIPEDIR true, the handler responds to some ** directory-like actions. This is the date for the entire ** handler, i.e., the directory date. The flag UPDATE_PIPEDATE ** controls whether this date is updated with each pipe access ** (true) or not (false). See SetPipeDate() and PipeExamine(). */ char HandlerName[30]; struct DeviceNode *DevNode = NULL; struct MsgPort *PipePort = NULL; PIPELISTHEADER pipelist; PIPELISTHEADER tapwaitlist; struct MsgPort *TapReplyPort = NULL; struct Library *SysBase = NULL; struct Library *DOSBase = NULL; #if PIPEDIR struct DateStamp PipeDate; #endif PIPEDIR /*--------------------------------------------------------------------------- ** Performs initialization, replies to startup packet, and dispatches ** incoming request packets to the apropriate functions. The TapReplyPort is ** also monitored for returning requests which were sent out by the handler. ** These returned requests are routed to HandleTapReply(). ** Our DeviceNode Task field is patched with our process ID so that this ** process is used for subsequent handler requests. The function exits only ** if there is some initialization error. */ void handler (StartPkt) struct DosPacket *StartPkt; { char *cp; struct Task *Task; ULONG PipeMask, TapReplyMask, WakeupMask, SigMask; struct DosPacket *pkt, *GetPkt(); void OpenPipe(), ClosePipe(); SysBase= AbsExecBase; if ((DOSBase= OpenLibrary (DOSNAME, 0)) == NULL) goto QUIT; BSTRtoCstr (BPTRtoCptr (StartPkt->dp_Arg1), HandlerName, sizeof (HandlerName)); for (cp= HandlerName; *cp != '\0'; ++cp) if (*cp == ':') /* remainder of handler's first refernece follows */ { *cp= '\0'; break; } Task= FindTask (0); PipePort= (struct MsgPort *) ((ULONG) Task + sizeof (struct Task)); ((struct Process *) Task)->pr_CurrentDir= 0; /* initial file system root */ if ((TapReplyPort= CreatePort (NULL, PipePort->mp_Node.ln_Pri)) == NULL) goto QUIT; #ifdef DEBUG if (! InitDebugIO (PipePort->mp_Node.ln_Pri)) goto QUIT; #endif DEBUG PipeMask= (1L << PipePort->mp_SigBit); TapReplyMask= (1L << TapReplyPort->mp_SigBit); WakeupMask= (PipeMask | TapReplyMask); DevNode= (struct DeviceNode *) BPTRtoCptr (StartPkt->dp_Arg3); DevNode->dn_Task= PipePort; InitList (&pipelist); InitList (&tapwaitlist); #if PIPEDIR (void) DateStamp (&PipeDate); #endif PIPEDIR ReplyPkt (StartPkt); LOOP: SigMask= Wait (WakeupMask); if (SigMask & TapReplyMask) while ((pkt= GetPkt (TapReplyPort)) != NULL) HandleTapReply (pkt); if (SigMask & PipeMask) while ((pkt= GetPkt (PipePort)) != NULL) switch (pkt->dp_Type) { case MODE_READWRITE: #ifdef DEBUG OS ("Open READWRITE packet received\n"); #endif DEBUG OpenPipe (pkt, 0); break; case MODE_READONLY: /* syn: MODE_OLDFILE, ACTION_FINDINPUT */ #ifdef DEBUG OS ("Open READONLY packet received\n"); #endif DEBUG OpenPipe (pkt, 0); break; case MODE_NEWFILE: /* syn: ACTION_FINDOUTPUT */ #ifdef DEBUG OS ("Open NEWFILE packet received\n"); #endif DEBUG OpenPipe (pkt, 0); break; case ACTION_END: #ifdef DEBUG OS ("Close packet received\n"); #endif DEBUG ClosePipe (pkt); break; case ACTION_READ: #ifdef DEBUG OS ("<<< Read packet received\n"); #endif DEBUG StartPipeIO (pkt, PIPEREAD); break; case ACTION_WRITE: #ifdef DEBUG OS (">>> Write packet received\n"); #endif DEBUG StartPipeIO (pkt, PIPEWRITE); break; #if PIPEDIR case ACTION_LOCATE_OBJECT: # ifdef DEBUG OS ( "Lock packet received\n"); # endif DEBUG PipeLock (pkt); break; case ACTION_COPY_DIR: # ifdef DEBUG OS ( "DupLock packet received\n"); # endif DEBUG PipeDupLock (pkt); break; case ACTION_FREE_LOCK: # ifdef DEBUG OS ( "UnLock packet received\n"); # endif DEBUG PipeUnLock (pkt); break; case ACTION_EXAMINE_OBJECT: # ifdef DEBUG OS ( "Examine packet received\n"); # endif DEBUG PipeExamine (pkt); break; case ACTION_EXAMINE_NEXT: # ifdef DEBUG OS ( "ExNext packet received\n"); # endif DEBUG PipeExNext (pkt); break; case ACTION_PARENT: # ifdef DEBUG OS ( "ParentDir packet received\n"); # endif DEBUG PipeParentDir (pkt); break; #endif PIPEDIR default: #ifdef DEBUG OS ("BAD packet received, type = "); OL (pkt->dp_Type); NL; #endif DEBUG pkt->dp_Res1= 0; pkt->dp_Res2= ERROR_ACTION_NOT_KNOWN; ReplyPkt (pkt); } goto LOOP; QUIT: DevNode->dn_Task= NULL; /* bad if someone in process of accessing us . . . */ if (TapReplyPort != NULL) FreeMem (TapReplyPort, sizeof (struct MsgPort)); /* signal bit won't matter */ #ifdef DEBUG CleanupDebugIO (); #endif DEBUG if (DOSBase != NULL) CloseLibrary (DOSBase); } /*--------------------------------------------------------------------------- ** Returns the DosPacket associated with the next message on "port", or NULL ** if the port is empty. The message is removed from the port. ** A related macro, ReplyPkt() is provided in pipe-handler.h. */ static struct DosPacket *GetPkt (port) register struct MsgPort *port; { register struct Message *msg; return ((msg= GetMsg (port)) == NULL) ? NULL : (struct DosPacket *) msg->mn_Node.ln_Name; } /*--------------------------------------------------------------------------- ** Searches "pipelist" for a pipe whose name is "name". If found, a pointer ** to the pipe returns. Otherwise, NULL returns. */ PIPEDATA *FindPipe (name) char *name; { PIPEDATA *p; char *cp, *strdiff(); for (p= (PIPEDATA *) FirstItem (&pipelist); p != NULL; p= (PIPEDATA *) NextItem (p)) { cp= strdiff (name, p->name); if ((*cp == '\0') && (p->name[(LONG) cp - (LONG) name] == '\0')) return p; /* same name */ } return NULL; /* no match found */ }