687 lines
17 KiB
C
687 lines
17 KiB
C
/*
|
||
* generic/dpPackOff.c --
|
||
*
|
||
* This file contains the implementation of the packoff channel. This type of
|
||
* filter identifies packets generated by the packon plugin filter and separates
|
||
* them from the input stream, returning them separately. Since this operation
|
||
* makes sense only when reading data, this channel is not writable.
|
||
* These are channels that are created by evaluating "dp_connect dpPackOff".
|
||
*
|
||
*/
|
||
|
||
|
||
#include <string.h>
|
||
#include <errno.h>
|
||
#include <tcl.h>
|
||
#include <generic/dpInt.h>
|
||
|
||
|
||
/*
|
||
* Prototypes for functions referenced only in this file.
|
||
*/
|
||
|
||
static int ClosePOChannel _ANSI_ARGS_((ClientData instanceData,
|
||
Tcl_Interp *interp));
|
||
|
||
static int InputPOChannel _ANSI_ARGS_((ClientData instanceData,
|
||
char *buf, int bufsize,
|
||
int *errorCodePtr));
|
||
|
||
static int OutputPOChannel _ANSI_ARGS_((ClientData instanceData,
|
||
char *buf, int toWrite,
|
||
int *errorCodePtr));
|
||
|
||
static int SOPPOChannel _ANSI_ARGS_((ClientData instanceData,
|
||
Tcl_Interp *interp,
|
||
char *optionName,
|
||
char *optionValue));
|
||
|
||
#ifndef _TCL76
|
||
static int GOPPOChannel _ANSI_ARGS_((ClientData instanceData,
|
||
Tcl_Interp *interp,
|
||
char *optionName,
|
||
Tcl_DString *dsPtr));
|
||
#else
|
||
static int GOPPOChannel _ANSI_ARGS_((ClientData instanceData,
|
||
char *optionName,
|
||
Tcl_DString *dsPtr));
|
||
#endif
|
||
|
||
#ifndef _TCL76
|
||
static int GFPPOChannel _ANSI_ARGS_((ClientData instanceData,
|
||
int direction,
|
||
FileHandle *handlePtr));
|
||
#else
|
||
static Tcl_File GFPPOChannel _ANSI_ARGS_((ClientData instanceData,
|
||
int direction));
|
||
#endif
|
||
|
||
static int CRPPOChannel _ANSI_ARGS_((ClientData instanceData,
|
||
int mask));
|
||
|
||
static void WCPPOChannel _ANSI_ARGS_((ClientData instanceData,
|
||
int mask));
|
||
|
||
|
||
|
||
/* memmove does not seem to be available on all systems */
|
||
static void mymove _ANSI_ARGS_((char *to, char *from, int number));
|
||
|
||
/*
|
||
* This structure stores the names of the functions that tcl calls when certain
|
||
* actions have to be performed on a packoff channel. To understand this entry,
|
||
* please refer to the documentation of the Tcl_CreateChannel and its associated
|
||
* functions in the tcl 7.6 documentation.
|
||
*
|
||
* A packoff channel will always be non-blocking, and non-writable.
|
||
* Seek on a packoff channel is not allowed.
|
||
*/
|
||
|
||
|
||
Tcl_ChannelType poChannelType = {
|
||
"packoff",
|
||
NULL, /* blockModeProc */
|
||
ClosePOChannel, /* closeProc */
|
||
InputPOChannel, /* inputProc */
|
||
OutputPOChannel, /* outputProc */
|
||
NULL, /* seekProc */
|
||
SOPPOChannel, /* setOptionProc */
|
||
GOPPOChannel, /* getOptionProc */
|
||
WCPPOChannel, /* watchChannelProc */
|
||
#ifdef _TCL76
|
||
CRPPOChannel, /* channelReadyProc */
|
||
#endif
|
||
GFPPOChannel /* getFileProc */
|
||
};
|
||
|
||
|
||
/*
|
||
* Structure that stores the data needed to manage a packoff filter.
|
||
*/
|
||
|
||
typedef struct {
|
||
/* Pointer to the subordinated channel. */
|
||
Tcl_Channel channelPtr;
|
||
|
||
/* If peek = 0 consume input, otherwise not. */
|
||
int peek;
|
||
|
||
/* The variables below are used in managing the input. Please refer to the
|
||
* input function to understand their meaning.
|
||
*/
|
||
|
||
char *buffer;
|
||
int bufLength;
|
||
int used;
|
||
int dataLength;
|
||
int precRead;
|
||
int ignoreNextRead;
|
||
|
||
} PackOffInfo;
|
||
|
||
|
||
/* Arbitrary value, it should not be less then the tcl's buffer size. */
|
||
|
||
#define BUFFER_CHUNK 8 * 1024
|
||
|
||
|
||
/*
|
||
*-----------------------------------------------------------------------------
|
||
*
|
||
* DpCreatePOChannel --
|
||
*
|
||
* Creates a packoff filter channel.
|
||
*
|
||
* Results:
|
||
*
|
||
* Returns a channel data structure. If an error happens, NULL
|
||
* is returned.
|
||
*
|
||
* Side effects:
|
||
*
|
||
* Alocates memory for the instance data that is associated
|
||
* with the channel.
|
||
*
|
||
* ----------------------------------------------------------------------------
|
||
*/
|
||
|
||
Tcl_Channel
|
||
DpCreatePOChannel (interp, argc, argv)
|
||
Tcl_Interp *interp; /* (in) Pointer to tcl interpreter. */
|
||
int argc; /* (in) Number of arguments. */
|
||
char **argv; /* (in) Argument strings. */
|
||
{
|
||
static int openedChannels = 0;
|
||
int i;
|
||
PackOffInfo *instanceData;
|
||
Tcl_Channel newChannel;
|
||
char chanName [20];
|
||
|
||
|
||
instanceData = (PackOffInfo *)ckalloc(sizeof(PackOffInfo));
|
||
|
||
if(instanceData == NULL) {
|
||
Tcl_AppendResult(interp, "unable to allocate memory for packoff ",
|
||
"filter channel", NULL);
|
||
return NULL;
|
||
}
|
||
|
||
instanceData->channelPtr = NULL;
|
||
|
||
for (i = 0; i < argc; i += 2) {
|
||
int v = i+1;
|
||
size_t len = strlen(argv[i]);
|
||
|
||
if (strncmp(argv[i], "-channel", len)==0) {
|
||
if (v == argc) {
|
||
goto error2;
|
||
}
|
||
|
||
instanceData->channelPtr = Tcl_GetChannel(interp, argv[v], NULL);
|
||
|
||
if(instanceData->channelPtr == NULL) {
|
||
goto error1;
|
||
}
|
||
} else {
|
||
Tcl_AppendResult(interp, "unknown option \"",
|
||
argv[i], "\", must be -channel", NULL);
|
||
goto error1;
|
||
}
|
||
}
|
||
|
||
if(instanceData->channelPtr == NULL) {
|
||
Tcl_AppendResult(interp, "-channel must be defined for a packoff ",
|
||
"channel", NULL);
|
||
goto error1;
|
||
}
|
||
|
||
/* No peek by default. */
|
||
instanceData->peek = 0;
|
||
|
||
/* Variables related to buffer management (see input function). */
|
||
|
||
instanceData->buffer = (char *)ckalloc(BUFFER_CHUNK);
|
||
|
||
if(instanceData->buffer == NULL) {
|
||
Tcl_AppendResult(interp, "unable to allocate memory for packoff ",
|
||
"filter channel buffer", NULL);
|
||
goto error1;
|
||
}
|
||
|
||
instanceData->used = 0;
|
||
instanceData->bufLength = BUFFER_CHUNK;
|
||
instanceData->dataLength = 0;
|
||
instanceData->ignoreNextRead = 0;
|
||
|
||
/* Packoff filters are only readable. */
|
||
sprintf(chanName, "pofilter%d", openedChannels++);
|
||
newChannel = Tcl_CreateChannel(&poChannelType, chanName,
|
||
(ClientData)instanceData, TCL_READABLE);
|
||
|
||
if(newChannel == NULL) {
|
||
Tcl_AppendResult(interp, "tcl unable to create packoff channel", NULL);
|
||
goto error1;
|
||
}
|
||
Tcl_RegisterChannel(interp, newChannel);
|
||
|
||
return newChannel;
|
||
|
||
|
||
error2:
|
||
Tcl_AppendResult(interp, "option value missing for -channel", NULL);
|
||
/* continues with error1 */
|
||
|
||
error1:
|
||
ckfree((char *)instanceData);
|
||
return NULL;
|
||
}
|
||
|
||
|
||
|
||
/*
|
||
*-----------------------------------------------------------------------------
|
||
*
|
||
* ClosePOChannel --
|
||
*
|
||
* Closes the given packoff filter channel.
|
||
*
|
||
* Results:
|
||
*
|
||
* If everything goes well, returns 0. If any error happens,
|
||
* it returns a POSIX error code.
|
||
*
|
||
* Side effects:
|
||
*
|
||
* It frees the instance data associated with the channel.
|
||
*
|
||
* ----------------------------------------------------------------------------
|
||
*/
|
||
|
||
static int
|
||
ClosePOChannel (instanceData, interp)
|
||
ClientData instanceData; /* (in) Pointer to PackOffInfo struct. */
|
||
Tcl_Interp *interp; /* Pointer to the tcl interpreter. */
|
||
{
|
||
PackOffInfo *pd = (PackOffInfo *) instanceData;
|
||
ckfree((char *)pd->buffer);
|
||
ckfree((char *)instanceData);
|
||
return 0;
|
||
}
|
||
|
||
|
||
|
||
/*
|
||
*-----------------------------------------------------------------------------
|
||
*
|
||
* InputPOChannel --
|
||
*
|
||
* Reads in a stream of data that was generated using the packon plugin
|
||
* filter (or a similar algorithm), and separates the packets, returning them
|
||
* separately to the tcl level.
|
||
*
|
||
* Results:
|
||
*
|
||
* Number of bytes read if no error happened, -1 otherwise.
|
||
*
|
||
* Side effects:
|
||
*
|
||
* 1. Calls the read procedure of the subordinated channel.
|
||
* 2. Stores a POSIX code at errorBuffer if an error occurs.
|
||
*
|
||
* ----------------------------------------------------------------------------
|
||
*/
|
||
|
||
static int
|
||
InputPOChannel (instanceData, buf, bufsize, errorCodePtr)
|
||
ClientData instanceData; /* (in) Pointer to PackOffInfo struct. */
|
||
char *buf; /* (in/out) Buffer to fill. */
|
||
int bufsize; /* (in) Size of buffer. */
|
||
int *errorCodePtr; /* (out) POSIX error code (if any). */
|
||
{
|
||
|
||
PackOffInfo *pD = (PackOffInfo *)instanceData;
|
||
|
||
char temp [7], inBufX [BUFFER_CHUNK];
|
||
int messLength, inLength;
|
||
|
||
char *inBuf = inBufX;
|
||
|
||
inLength = Tcl_Read(pD->channelPtr, inBuf, BUFFER_CHUNK);
|
||
|
||
if (inLength == -1) {
|
||
*errorCodePtr = Tcl_GetErrno();
|
||
return -1;
|
||
}
|
||
|
||
/* Do we have at least a message header in the buffer? */
|
||
|
||
if (pD->dataLength - pD->used < 6) {
|
||
|
||
/* No. Can we make up a header using the input? */
|
||
|
||
int available = pD->dataLength - pD->used;
|
||
|
||
if (inLength >= 6 - available) {
|
||
|
||
/* Yes. Transfer rest of header and adjust local structures. */
|
||
|
||
mymove(pD->buffer, pD->buffer + pD->used, 6 - available);
|
||
mymove(pD->buffer + available, inBuf, 6 - available);
|
||
|
||
pD->dataLength = 6;
|
||
pD->used = 0;
|
||
|
||
inBuf += 6 - available;
|
||
inLength -= 6 - available;
|
||
|
||
} else {
|
||
|
||
/* No. Transfer input and return. */
|
||
|
||
mymove(pD->buffer, pD->buffer + pD->used, inLength);
|
||
pD->dataLength += inLength;
|
||
pD->used = 0;
|
||
|
||
*errorCodePtr = EAGAIN;
|
||
return 0;
|
||
}
|
||
|
||
}
|
||
|
||
/* See how many bytes we need for the next message. */
|
||
|
||
memcpy(temp, pD->buffer + pD->used, 6);
|
||
temp[6] = '\0';
|
||
|
||
if((messLength = atoi(temp)) <= 0) {
|
||
return -1;
|
||
}
|
||
|
||
/* Do not allow packet sizes that are bigger than what the size of buf is.
|
||
* This is because we want a packet to be returned as a unit.
|
||
*/
|
||
|
||
if(messLength > bufsize) {
|
||
*errorCodePtr = EINVAL;
|
||
return -1;
|
||
}
|
||
|
||
/* Do we have enough data in the buffer? */
|
||
|
||
if(pD->dataLength - pD->used >= 6 + messLength) {
|
||
|
||
/* Yes. Output data and adjust local structures. */
|
||
|
||
memcpy(buf, pD->buffer + pD->used + 6, messLength);
|
||
pD->used += 6 + messLength;
|
||
pD->ignoreNextRead = 1;
|
||
|
||
} else if(pD->dataLength - pD->used + inLength >= 6 + messLength) {
|
||
|
||
/* Do we have enough data in buffer and inBuf together? */
|
||
|
||
int useful = pD->dataLength - pD->used - 6;
|
||
|
||
memcpy(buf, pD->buffer + pD->used + 6, useful);
|
||
memcpy(buf + useful, inBuf, messLength - useful);
|
||
|
||
pD->used = pD->dataLength = 0;
|
||
|
||
inBuf += messLength - useful;
|
||
inLength -= messLength - useful;
|
||
|
||
pD->ignoreNextRead = 1;
|
||
|
||
} else {
|
||
|
||
messLength = 0;
|
||
}
|
||
|
||
/* Is there enough space to hold the data? */
|
||
|
||
if(pD->bufLength - (pD->dataLength - pD->used) >= inLength) {
|
||
|
||
/* If yes, just put everything in place. */
|
||
|
||
if(pD->dataLength - pD->used > 0) {
|
||
mymove(pD->buffer, pD->buffer + pD->used, pD->dataLength - pD->used);
|
||
}
|
||
|
||
mymove(pD->buffer + pD->dataLength - pD->used, inBuf, inLength);
|
||
|
||
pD->dataLength = pD->dataLength - pD->used + inLength;
|
||
pD->used = 0;
|
||
|
||
} else {
|
||
|
||
/* If no, create a bigger buffer. */
|
||
|
||
int neededSpace = inLength - (pD->bufLength - (pD->dataLength - pD->used));
|
||
|
||
char *temp = ckalloc(pD->bufLength + neededSpace + BUFFER_CHUNK);
|
||
|
||
if(temp == NULL) {
|
||
return ENOMEM;
|
||
}
|
||
|
||
memcpy(temp, pD->buffer + pD->used, pD->dataLength - pD->used);
|
||
memcpy(temp + pD->dataLength - pD->used, inBuf, inLength);
|
||
|
||
pD->dataLength = pD->dataLength - pD->used + inLength;
|
||
pD->bufLength = pD->bufLength + neededSpace + BUFFER_CHUNK;
|
||
pD->used = 0;
|
||
|
||
ckfree(pD->buffer);
|
||
pD->buffer = temp;
|
||
|
||
}
|
||
|
||
return messLength;
|
||
|
||
}
|
||
|
||
|
||
|
||
/*
|
||
*-----------------------------------------------------------------------------
|
||
*
|
||
* OutputPOChannel --
|
||
*
|
||
* A packoff channel is not writable.
|
||
*
|
||
* Results:
|
||
*
|
||
* Error code EINVAL.
|
||
*
|
||
* Side effects:
|
||
*
|
||
* None.
|
||
*
|
||
* ----------------------------------------------------------------------------
|
||
*/
|
||
/* ARGSUSED */
|
||
static int
|
||
OutputPOChannel (instanceData, buf, toWrite, errorCodePtr)
|
||
ClientData instanceData; /* channel to send the message to */
|
||
char *buf; /* output buffer */
|
||
int toWrite; /* number of characters to write */
|
||
int *errorCodePtr; /* place to store the POSIX error code */
|
||
{
|
||
return -1;
|
||
}
|
||
|
||
|
||
|
||
/*
|
||
*-----------------------------------------------------------------------------
|
||
*
|
||
* GFPPOChannel --
|
||
*
|
||
* "Get file" function for packoff channels. Since there are no files
|
||
* associated with filters, it always returns NULL.
|
||
*
|
||
* Results:
|
||
*
|
||
* TCL_OK
|
||
*
|
||
* Side effects:
|
||
*
|
||
* None.
|
||
*
|
||
* ----------------------------------------------------------------------------
|
||
*/
|
||
/* ARGSUSED */
|
||
#ifndef _TCL76
|
||
static int
|
||
GFPPOChannel (instanceData, direction, handlePtr)
|
||
ClientData instanceData;
|
||
int direction;
|
||
FileHandle *handlePtr;
|
||
{
|
||
*handlePtr = NULL;
|
||
return TCL_OK;
|
||
}
|
||
#else
|
||
static Tcl_File
|
||
GFPPOChannel (instanceData, direction)
|
||
ClientData instanceData;
|
||
int direction;
|
||
{
|
||
return NULL;
|
||
}
|
||
#endif
|
||
|
||
|
||
|
||
/*
|
||
*-----------------------------------------------------------------------------
|
||
*
|
||
* SOPPOChannel --
|
||
*
|
||
* There is no non-standard option allowed for packoff filters.
|
||
*
|
||
* Results:
|
||
*
|
||
* Tcl error code.
|
||
*
|
||
* Side effects:
|
||
*
|
||
* None.
|
||
*
|
||
* ----------------------------------------------------------------------------
|
||
*/
|
||
/* ARGSUSED */
|
||
static int
|
||
SOPPOChannel (instanceData, interp, optionName, optionValue)
|
||
ClientData instanceData; /* (in) Pointer to PackOffInfo struct. */
|
||
Tcl_Interp *interp; /* (in) Pointer to tcl interpreter. */
|
||
char *optionName;
|
||
char *optionValue;
|
||
{
|
||
Tcl_AppendResult (interp, "illegal option \"", optionName, "\" -- ",
|
||
"must be a standard fconfigure option", NULL);
|
||
return TCL_ERROR;
|
||
|
||
}
|
||
|
||
|
||
|
||
/*
|
||
*-----------------------------------------------------------------------------
|
||
*
|
||
* GOPPOChannel --
|
||
*
|
||
* There are no non-standard options for a packoff channel.
|
||
*
|
||
* Results:
|
||
*
|
||
* Standard Tcl result.
|
||
*
|
||
* Side effects:
|
||
*
|
||
* None.
|
||
*
|
||
* ----------------------------------------------------------------------------
|
||
*/
|
||
|
||
static int
|
||
GOPPOChannel (instanceData,
|
||
#ifdef _TCL80
|
||
interp,
|
||
#endif
|
||
optionName, dsPtr)
|
||
ClientData instanceData;
|
||
#ifdef _TCL80
|
||
Tcl_Interp *interp;
|
||
#endif
|
||
char *optionName;
|
||
Tcl_DString *dsPtr; /* (out) String to store the result in. */
|
||
{
|
||
if (optionName != NULL) {
|
||
Tcl_SetErrno(EINVAL);
|
||
return TCL_ERROR;
|
||
}
|
||
|
||
return TCL_OK;
|
||
}
|
||
|
||
|
||
|
||
/*
|
||
*-----------------------------------------------------------------------------
|
||
*
|
||
* WCPPOChannel --
|
||
*
|
||
* This is the "watch channel" procedure for packoff filters. It is
|
||
* assumed that no events are generated internally in the filter channel,
|
||
* so the procedure only calls the corresponding procedure of the
|
||
* subordinated channel.
|
||
*
|
||
* Results:
|
||
*
|
||
* None.
|
||
*
|
||
* Side effects:
|
||
*
|
||
* Calls the "watch channel" procedure of the subordinated channel.
|
||
*
|
||
* ----------------------------------------------------------------------------
|
||
*/
|
||
|
||
static void
|
||
WCPPOChannel (instanceData, mask)
|
||
ClientData instanceData; /* (in) Pointer to PlugFInfo struct. */
|
||
int mask; /* (in) ORed combination of TCL_READABLE,
|
||
* TCL_WRITABLE and TCL_EXCEPTION. It designates
|
||
* the event categories that have to be watched.
|
||
*/
|
||
{
|
||
Tcl_Channel channelPtr = ((PackOffInfo *)instanceData)->channelPtr;
|
||
|
||
#ifdef _TCL76
|
||
(Tcl_GetChannelType(channelPtr)->watchChannelProc)
|
||
(Tcl_GetChannelInstanceData(channelPtr), mask);
|
||
#endif
|
||
|
||
return;
|
||
}
|
||
|
||
|
||
|
||
/*
|
||
*-----------------------------------------------------------------------------
|
||
*
|
||
* CRPPOChannel --
|
||
*
|
||
* This is the "channel ready" procedure for packoff filters. It is
|
||
* assumed that no events are generated internally in the filter channel,
|
||
* so the procedure only calls the corresponding procedure of the
|
||
* subordinated channel.
|
||
*
|
||
* Results:
|
||
*
|
||
* The value returned by the "channel ready" procedure of the subordinated
|
||
* channel.
|
||
*
|
||
* Side effects:
|
||
*
|
||
* Calls the "channel ready" procedure of the subordinated channel.
|
||
*
|
||
* ----------------------------------------------------------------------------
|
||
*/
|
||
|
||
static int
|
||
CRPPOChannel (instanceData, mask)
|
||
ClientData instanceData; /* (in) Pointer to PackOffInfo struct. */
|
||
int mask; /* (in) ORed combination of TCL_READABLE,
|
||
* TCL_WRITABLE and TCL_EXCEPTION. It designates
|
||
* the event categories whose occurence has to
|
||
* be signalled.
|
||
*/
|
||
{
|
||
Tcl_Channel channelPtr = ((PackOffInfo *)instanceData)->channelPtr;
|
||
|
||
#ifdef _TCL76
|
||
return (Tcl_GetChannelType(channelPtr)->channelReadyProc)
|
||
(Tcl_GetChannelInstanceData(channelPtr), mask);
|
||
#else
|
||
return TCL_OK;
|
||
#endif
|
||
}
|
||
|
||
|
||
/* memmove does not seem to be available on all systems */
|
||
static void
|
||
mymove (to, from, number)
|
||
char *to;
|
||
char *from;
|
||
int number;
|
||
{
|
||
|
||
for(/* empty */; number--; *to++ = *from++);
|
||
|
||
}
|
||
|
||
|
||
|