/**************************************************************************** ** File: pipecreate.c ** Program: pipe-handler - an AmigaDOS handler for named pipes ** Version: 1.1 ** 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 lock initialization to OpenPipe() ** for locks on individual pipes. ** 12-Feb-87 Fixed bug in OpenPipe(): previously ignored ** lock passed in packet. Bug uncovered when ** pipes became lockable, and thus assignable. ** 26-Mar-87 Fixed bug in ClosePipe(): not closing r/w ** mode properly (extraneous else). */ #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 /*--------------------------------------------------------------------------- ** pipecreate.c ** ------------ ** This module handles opens and closes for pipes. ** ** Visible Functions ** ----------------- ** void OpenPipe (pkt, tapfh) ** void ClosePipe (pkt) ** void DiscardPipe (pipe) ** ** Macros (in pipecreate.h) ** ------------------------ ** - none - ** ** Local Functions ** --------------- ** int TapFormsLoop (tapfh, pipe) ** void OpenTap (pkt, tapname) ** void CloseTap (tapfh) */ /*--------------------------------------------------------------------------- ** OpenPipe() handles open requests. The DosPacket from the client and the ** filehandle of the tap are sent. If tapfh is 0, but the name sent ** indicates a tap is desired (see ParsePipeName() in pipename.c), then ** an OpenTap() request is initiated and OpenPipe() is immediately exited. ** Later, when the request returns (to HandleTapReply()), OpenPipe() is ** called again with the same client packet and the newly returned tapfh. ** If tapfh is nonzero, or if it is zero but no tap is desired, then ** the an attempt to open the pipe is made. If a existent pipe with a tap is ** to be opened and a new tapfh is given, the old tap is closed. ** If the name's syntax is incorrect, then the request is returned ** unsuccessful. Otherwise, if the pipe named by the request does not ** already exist, a new pipe is created (if there is enough memory). ** If it does exist, but it is already open for the mode requested, an error ** is returned (a maximum of one reader and one writer is allowed). ** A successful open returns the client's filehandle with its Arg1 field ** pointing to a PIPEKEY, which in turn identifies the pipe and open mode. ** Unless an OpenTap() is required, the packet is returned to the cleint ** by this function. If an OpenTap() is required, it will be returned by the ** the later call to this function when the tap open request is returned. ** Note: the code which checks if the lock sent in the packet relies on ** the fact that the pipe-handler does not allow subdirectories. If a lock ** on a pipe is passed in, then that pipe is opened. Otherwise, the name is ** parsed without reference to the lock. */ void OpenPipe (pkt, tapfh) struct DosPacket *pkt; BPTR tapfh; { void OpenTap(), CloseTap(); LONG openmode; struct FileHandle *handle; struct FileLock *lock; char *pipename = NULL, *tapname = NULL; ULONG pipesize; PIPEKEY *pipekey = NULL; PIPEDATA *pipe; int TapFormsLoop(); pkt->dp_Res1= 0; /* error, for now */ if (! ParsePipeName (BPTRtoCptr (pkt->dp_Arg3), &pipename, &pipesize, &tapname)) { pkt->dp_Res2= ERROR_INVALID_COMPONENT_NAME; goto OPENREPLY; } if ( (tapfh == 0) && (tapname != NULL) && (tapname[0] != '\0') ) { OpenTap (pkt, tapname); /* start tap open request */ return; /* HandleTapReply() re-calls when request returns */ } openmode= pkt->dp_Type; lock= (struct FileLock *) BPTRtoCptr (pkt->dp_Arg2); pipe= NULL; if ( (lock == NULL) || ((pipe= (PIPEDATA *) lock->fl_Key) == NULL) ) { if (pipename[0] == '\0') #if AUTONAME pipename= get_autoname ((openmode == MODE_NEWFILE) || (openmode == MODE_READWRITE)); #else !AUTONAME { pkt->dp_Res2= ERROR_INVALID_COMPONENT_NAME; goto OPENREPLY; } #endif AUTONAME pipe= FindPipe (pipename); } else /* packet's lock was on the pipe */ { if (pipename[0] != '\0') { pkt->dp_Res2= ERROR_INVALID_COMPONENT_NAME; goto OPENREPLY; } pipename= pipe->name; } handle= (struct FileHandle *) BPTRtoCptr (pkt->dp_Arg1); if ((pipekey= (PIPEKEY *) AllocMem (sizeof (PIPEKEY), ALLOCMEM_FLAGS)) == NULL) { pkt->dp_Res2= ERROR_NO_FREE_STORE; goto OPENREPLY; } if (pipe == NULL) /* then PIPE NOT FOUND */ { if (openmode == MODE_READONLY) { pkt->dp_Res2= ERROR_OBJECT_NOT_FOUND; goto OPENREPLY; } pkt->dp_Res2= ERROR_NO_FREE_STORE; /* in case of AllocMem error */ if ((pipe= (PIPEDATA *) AllocMem (sizeof (PIPEDATA), ALLOCMEM_FLAGS)) == NULL) goto OPENMEMERR1; if ((pipe->buf= AllocPipebuf (pipesize)) == NULL) goto OPENMEMERR2; if ((pipe->lock= (struct FileLock *) AllocMem (sizeof (struct FileLock), ALLOCMEM_FLAGS)) == NULL) { FreePipebuf (pipe->buf); OPENMEMERR2: FreeMem (pipe, sizeof (PIPEDATA)); OPENMEMERR1: goto OPENREPLY; } l_strcpy (pipe->name, pipename); pipekey->pipe= pipe; pipekey->openmode= openmode; if (openmode == MODE_READONLY) { pipekey->iotype= PIPEREAD; pipe->flags |= OPEN_FOR_READ; } else if (openmode == MODE_NEWFILE) { pipekey->iotype= PIPEWRITE; pipe->flags= OPEN_FOR_WRITE; } else /* MODE_READWRITE */ { pipekey->iotype= PIPERW; pipe->flags= (OPEN_FOR_READ | OPEN_FOR_WRITE); } InitList (&pipe->readerlist); InitList (&pipe->writerlist); pipe->tapfh= tapfh; #if PIPEDIR pipe->lockct= 0; InitLock (pipe->lock, pipe); #endif PIPEDIR InsertTail (&pipelist, pipe); /* at tail for directory's sake */ #ifdef DEBUG OS ("*** created pipe '"); OS (pipe->name); OS ("' [buflen "); OL (pipe->buf->len); OS ("]\n"); #endif DEBUG } else /* PIPE WAS FOUND */ { if (TapFormsLoop (tapfh, pipe)) { pkt->dp_Res2= ERROR_INVALID_COMPONENT_NAME; goto OPENREPLY; } pipekey->pipe= pipe; pipekey->openmode= openmode; pkt->dp_Res2= ERROR_OBJECT_IN_USE; /* in case of openmode error */ if (openmode == MODE_READONLY) { if (pipe->flags & OPEN_FOR_READ) goto OPENREPLY; pipekey->iotype= PIPEREAD; pipe->flags |= OPEN_FOR_READ; } else if (openmode == MODE_NEWFILE) { if (pipe->flags & OPEN_FOR_WRITE) goto OPENREPLY; pipekey->iotype= PIPEWRITE; pipe->flags |= OPEN_FOR_WRITE; } else /* MODE_READWRITE */ { if (pipe->flags & (OPEN_FOR_READ | OPEN_FOR_WRITE)) goto OPENREPLY; pipekey->iotype= PIPERW; pipe->flags= (OPEN_FOR_READ | OPEN_FOR_WRITE); } if (tapfh != 0) { if (pipe->tapfh != 0) CloseTap (pipe->tapfh); /* close old tap first */ pipe->tapfh= tapfh; } } handle->fh_Arg1= (LONG) pipekey; /* for identification on Read, Write, Close */ pkt->dp_Res1= 1; pkt->dp_Res2= 0; /* for successful open */ OPENREPLY: if (pkt->dp_Res1 == 0) /* then there was an error */ { if (pipekey != NULL) FreeMem (pipekey, sizeof (PIPEKEY)); if (tapfh != 0) CloseTap (tapfh); } #if PIPEDIR else SetPipeDate (pipe); #endif PIPEDIR ReplyPkt (pkt); } /*--------------------------------------------------------------------------- ** This routine checks for "the old loop in the pipe trick" (86). If the ** handler has a loop through its tap, the handler will endlessly pass ** packets to itself. This would be disastrous if the handler is running at ** high priority. */ static int TapFormsLoop (tapfh, pipe) BPTR tapfh; PIPEDATA *pipe; { struct FileHandle *handle; PIPEKEY *tapkey; PIPEDATA *tappipe; int numchecks; /* protection */ for (numchecks= 0; ((tapfh != 0) && (numchecks < 10000)); ++numchecks) { handle= (struct FileHandle *) BPTRtoCptr (tapfh); if (handle->fh_Type == PipePort) /* then the tap is a pipe, too */ { if ( ((tapkey= (PIPEKEY *) handle->fh_Arg1) == NULL) || ((tappipe= tapkey->pipe) == NULL) ) return FALSE; if (tappipe == pipe) return TRUE; tapfh= tappipe->tapfh; } else return FALSE; } return FALSE; } /*--------------------------------------------------------------------------- ** The previous open performed on a pipe is terminated. The PIPEKEY ** allocated for the client when the pipe was opened is freed. Then, ** CheckWaiting() is called -- it will discard the pipe if it becomes empty ** and is not opened for read or write. */ void ClosePipe (pkt) struct DosPacket *pkt; { PIPEKEY *pipekey; PIPEDATA *pipe; void DeletePipe(); pipekey= (PIPEKEY *) pkt->dp_Arg1; pipe= pipekey->pipe; if ((pipekey->iotype == PIPEREAD) || (pipekey->iotype == PIPERW)) pipe->flags &= ~OPEN_FOR_READ; if ((pipekey->iotype == PIPEWRITE) || (pipekey->iotype == PIPERW)) pipe->flags &= ~OPEN_FOR_WRITE; FreeMem (pipekey, sizeof (PIPEKEY)); CheckWaiting (pipe); /* will discard if empty */ pkt->dp_Res1= 1; pkt->dp_Res2= 0; ReplyPkt (pkt); } /*--------------------------------------------------------------------------- ** Remove a pipe from the pipe list and release its memory. The pipe is ** assumed empty and having no clients. */ void DiscardPipe (pipe) PIPEDATA *pipe; { #ifdef DEBUG OS ("*** discarding pipe '"); OS (pipe->name); OS ("'\n"); #endif DEBUG Delete (&pipelist, pipe); FreePipebuf (pipe->buf); FreeMem (pipe->lock, sizeof (struct FileLock)); if (pipe->tapfh != 0) CloseTap (pipe->tapfh); FreeMem (pipe, sizeof (PIPEDATA)); } /*--------------------------------------------------------------------------- ** An open request for a tap is performed. A WAITINGDATA structure is ** allocated to hold the client packet until later. HandleTapReply() will ** deal with the reply and, if successful, re-call OpenPipe(). */ static void OpenTap (pkt, tapname) struct DosPacket *pkt; char *tapname; { char *Bname; struct FileHandle *handle; WAITINGDATA *wd; struct DosPacket *tappkt; struct MsgPort *Handler; struct FileLock *Lock; void StartTapIO(); if ( (tapname == NULL) || ((Bname= (char *) AllocMem (OPENTAP_STRSIZE, ALLOCMEM_FLAGS)) == NULL) ) goto OPENTAPERR; if ((handle= (struct FileHandle *) AllocMem (sizeof (struct FileHandle), (ALLOCMEM_FLAGS | MEMF_CLEAR))) == NULL) goto OPENTAPERR1; if ((wd= (WAITINGDATA *) AllocMem (sizeof (WAITINGDATA), ALLOCMEM_FLAGS)) == NULL) goto OPENTAPERR2; if ((tappkt= AllocPacket (TapReplyPort)) == NULL) goto OPENTAPERR3; if ((Handler= DeviceProc (tapname)) == NULL) { FreePacket (tappkt); OPENTAPERR3: FreeMem (wd, sizeof (WAITINGDATA)); OPENTAPERR2: FreeMem (handle, sizeof (struct FileHandle)); OPENTAPERR1: FreeMem (Bname, OPENTAP_STRSIZE); OPENTAPERR: pkt->dp_Res1= 0; pkt->dp_Res2= ERROR_INVALID_COMPONENT_NAME; ReplyPkt (pkt); return; } Lock= (struct FileLock *) IoErr (); CstrtoBSTR (tapname, Bname, OPENTAP_STRSIZE); handle->fh_Pos= -1; handle->fh_End= -1; handle->fh_Type= Handler; /* initialize file handle */ wd->pkt= tappkt; wd->pktinfo.tapwait.clientpkt= pkt; wd->pktinfo.tapwait.handle= handle; /* for HandleTapReply() */ StartTapIO ( tappkt, MODE_NEWFILE, CptrtoBPTR (handle), CptrtoBPTR (Lock), CptrtoBPTR (Bname), Handler ); InsertHead (&tapwaitlist, wd); } /*--------------------------------------------------------------------------- ** A close request for a tap filehandle is initiated. When HandleTapReply() ** gets the reply, it merely discards it. */ static void CloseTap (tapfh) BPTR tapfh; { struct FileHandle *taphandle; struct DosPacket *tappkt; WAITINGDATA *wd; void StartTapIO(); taphandle= (struct FileHandle *) BPTRtoCptr (tapfh); if ((tappkt= AllocPacket (TapReplyPort)) == NULL) goto CLOSETAPERR; if ((wd= (WAITINGDATA *) AllocMem (sizeof (WAITINGDATA), ALLOCMEM_FLAGS)) == NULL) { FreePacket (tappkt); CLOSETAPERR: FreeMem (taphandle, sizeof (struct FileHandle)); #ifdef DEBUG OS ("!!! ERROR - CloseTap() failed\n"); #endif DEBUG return; } wd->pkt= tappkt; /* don't need ...tapwait.clientpkt */ wd->pktinfo.tapwait.handle= taphandle; /* for HandleTapReply() */ StartTapIO ( tappkt, ACTION_END, taphandle->fh_Arg1, 0, 0, taphandle->fh_Type ); InsertHead (&tapwaitlist, wd); }