mirror of
https://github.com/signalwire/freeswitch.git
synced 2025-07-14 19:16:59 +00:00
git-svn-id: http://svn.freeswitch.org/svn/freeswitch/trunk@3693 d0543943-73ff-0310-b7d9-9358b9ac24b2
769 lines
20 KiB
C
769 lines
20 KiB
C
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
|
/* ***** BEGIN LICENSE BLOCK *****
|
|
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
|
*
|
|
* The contents of this file are subject to the Mozilla Public License Version
|
|
* 1.1 (the "License"); you may not use this file except in compliance with
|
|
* the License. You may obtain a copy of the License at
|
|
* http://www.mozilla.org/MPL/
|
|
*
|
|
* Software distributed under the License is distributed on an "AS IS" basis,
|
|
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
|
* for the specific language governing rights and limitations under the
|
|
* License.
|
|
*
|
|
* The Original Code is the Netscape Portable Runtime (NSPR).
|
|
*
|
|
* The Initial Developer of the Original Code is
|
|
* Netscape Communications Corporation.
|
|
* Portions created by the Initial Developer are Copyright (C) 1998-2000
|
|
* the Initial Developer. All Rights Reserved.
|
|
*
|
|
* Contributor(s):
|
|
*
|
|
* Alternatively, the contents of this file may be used under the terms of
|
|
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
|
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
|
* in which case the provisions of the GPL or the LGPL are applicable instead
|
|
* of those above. If you wish to allow use of your version of this file only
|
|
* under the terms of either the GPL or the LGPL, and not to allow others to
|
|
* use your version of this file under the terms of the MPL, indicate your
|
|
* decision by deleting the provisions above and replace them with the notice
|
|
* and other provisions required by the GPL or the LGPL. If you do not delete
|
|
* the provisions above, a recipient may use your version of this file under
|
|
* the terms of any one of the MPL, the GPL or the LGPL.
|
|
*
|
|
* ***** END LICENSE BLOCK ***** */
|
|
|
|
/*
|
|
** File: prlayer.c
|
|
** Description: Routines for handling pushable protocol modules on sockets.
|
|
*/
|
|
|
|
#include "primpl.h"
|
|
#include "prerror.h"
|
|
#include "prmem.h"
|
|
#include "prlock.h"
|
|
#include "prlog.h"
|
|
#include "prio.h"
|
|
|
|
#include <string.h> /* for memset() */
|
|
static PRStatus _PR_DestroyIOLayer(PRFileDesc *stack);
|
|
|
|
void PR_CALLBACK pl_FDDestructor(PRFileDesc *fd)
|
|
{
|
|
PR_ASSERT(fd != NULL);
|
|
if (NULL != fd->lower) fd->lower->higher = fd->higher;
|
|
if (NULL != fd->higher) fd->higher->lower = fd->lower;
|
|
PR_DELETE(fd);
|
|
}
|
|
|
|
/*
|
|
** Default methods that just call down to the next fd.
|
|
*/
|
|
static PRStatus PR_CALLBACK pl_TopClose (PRFileDesc *fd)
|
|
{
|
|
PRFileDesc *top, *lower;
|
|
PRStatus rv;
|
|
|
|
PR_ASSERT(fd != NULL);
|
|
PR_ASSERT(fd->lower != NULL);
|
|
PR_ASSERT(fd->secret == NULL);
|
|
PR_ASSERT(fd->methods->file_type == PR_DESC_LAYERED);
|
|
|
|
if (PR_IO_LAYER_HEAD == fd->identity) {
|
|
/*
|
|
* new style stack; close all the layers, before deleting the
|
|
* stack head
|
|
*/
|
|
rv = fd->lower->methods->close(fd->lower);
|
|
_PR_DestroyIOLayer(fd);
|
|
return rv;
|
|
} else if ((fd->higher) && (PR_IO_LAYER_HEAD == fd->higher->identity)) {
|
|
/*
|
|
* lower layers of new style stack
|
|
*/
|
|
lower = fd->lower;
|
|
/*
|
|
* pop and cleanup current layer
|
|
*/
|
|
top = PR_PopIOLayer(fd->higher, PR_TOP_IO_LAYER);
|
|
top->dtor(top);
|
|
/*
|
|
* then call lower layer
|
|
*/
|
|
return (lower->methods->close(lower));
|
|
} else {
|
|
/* old style stack */
|
|
top = PR_PopIOLayer(fd, PR_TOP_IO_LAYER);
|
|
top->dtor(top);
|
|
return (fd->methods->close)(fd);
|
|
}
|
|
}
|
|
|
|
static PRInt32 PR_CALLBACK pl_DefRead (PRFileDesc *fd, void *buf, PRInt32 amount)
|
|
{
|
|
PR_ASSERT(fd != NULL);
|
|
PR_ASSERT(fd->lower != NULL);
|
|
|
|
return (fd->lower->methods->read)(fd->lower, buf, amount);
|
|
}
|
|
|
|
static PRInt32 PR_CALLBACK pl_DefWrite (
|
|
PRFileDesc *fd, const void *buf, PRInt32 amount)
|
|
{
|
|
PR_ASSERT(fd != NULL);
|
|
PR_ASSERT(fd->lower != NULL);
|
|
|
|
return (fd->lower->methods->write)(fd->lower, buf, amount);
|
|
}
|
|
|
|
static PRInt32 PR_CALLBACK pl_DefAvailable (PRFileDesc *fd)
|
|
{
|
|
PR_ASSERT(fd != NULL);
|
|
PR_ASSERT(fd->lower != NULL);
|
|
|
|
return (fd->lower->methods->available)(fd->lower);
|
|
}
|
|
|
|
static PRInt64 PR_CALLBACK pl_DefAvailable64 (PRFileDesc *fd)
|
|
{
|
|
PR_ASSERT(fd != NULL);
|
|
PR_ASSERT(fd->lower != NULL);
|
|
|
|
return (fd->lower->methods->available64)(fd->lower);
|
|
}
|
|
|
|
static PRStatus PR_CALLBACK pl_DefFsync (PRFileDesc *fd)
|
|
{
|
|
PR_ASSERT(fd != NULL);
|
|
PR_ASSERT(fd->lower != NULL);
|
|
|
|
return (fd->lower->methods->fsync)(fd->lower);
|
|
}
|
|
|
|
static PRInt32 PR_CALLBACK pl_DefSeek (
|
|
PRFileDesc *fd, PRInt32 offset, PRSeekWhence how)
|
|
{
|
|
PR_ASSERT(fd != NULL);
|
|
PR_ASSERT(fd->lower != NULL);
|
|
|
|
return (fd->lower->methods->seek)(fd->lower, offset, how);
|
|
}
|
|
|
|
static PRInt64 PR_CALLBACK pl_DefSeek64 (
|
|
PRFileDesc *fd, PRInt64 offset, PRSeekWhence how)
|
|
{
|
|
PR_ASSERT(fd != NULL);
|
|
PR_ASSERT(fd->lower != NULL);
|
|
|
|
return (fd->lower->methods->seek64)(fd->lower, offset, how);
|
|
}
|
|
|
|
static PRStatus PR_CALLBACK pl_DefFileInfo (PRFileDesc *fd, PRFileInfo *info)
|
|
{
|
|
PR_ASSERT(fd != NULL);
|
|
PR_ASSERT(fd->lower != NULL);
|
|
|
|
return (fd->lower->methods->fileInfo)(fd->lower, info);
|
|
}
|
|
|
|
static PRStatus PR_CALLBACK pl_DefFileInfo64 (PRFileDesc *fd, PRFileInfo64 *info)
|
|
{
|
|
PR_ASSERT(fd != NULL);
|
|
PR_ASSERT(fd->lower != NULL);
|
|
|
|
return (fd->lower->methods->fileInfo64)(fd->lower, info);
|
|
}
|
|
|
|
static PRInt32 PR_CALLBACK pl_DefWritev (PRFileDesc *fd, const PRIOVec *iov,
|
|
PRInt32 size, PRIntervalTime timeout)
|
|
{
|
|
PR_ASSERT(fd != NULL);
|
|
PR_ASSERT(fd->lower != NULL);
|
|
|
|
return (fd->lower->methods->writev)(fd->lower, iov, size, timeout);
|
|
}
|
|
|
|
static PRStatus PR_CALLBACK pl_DefConnect (
|
|
PRFileDesc *fd, const PRNetAddr *addr, PRIntervalTime timeout)
|
|
{
|
|
PR_ASSERT(fd != NULL);
|
|
PR_ASSERT(fd->lower != NULL);
|
|
|
|
return (fd->lower->methods->connect)(fd->lower, addr, timeout);
|
|
}
|
|
|
|
static PRStatus PR_CALLBACK pl_DefConnectcontinue (
|
|
PRFileDesc *fd, PRInt16 out_flags)
|
|
{
|
|
PR_ASSERT(fd != NULL);
|
|
PR_ASSERT(fd->lower != NULL);
|
|
|
|
return (fd->lower->methods->connectcontinue)(fd->lower, out_flags);
|
|
}
|
|
|
|
static PRFileDesc* PR_CALLBACK pl_TopAccept (
|
|
PRFileDesc *fd, PRNetAddr *addr, PRIntervalTime timeout)
|
|
{
|
|
PRStatus rv;
|
|
PRFileDesc *newfd, *layer = fd;
|
|
PRFileDesc *newstack;
|
|
PRBool newstyle_stack = PR_FALSE;
|
|
|
|
PR_ASSERT(fd != NULL);
|
|
PR_ASSERT(fd->lower != NULL);
|
|
|
|
/* test for new style stack */
|
|
while (NULL != layer->higher)
|
|
layer = layer->higher;
|
|
newstyle_stack = (PR_IO_LAYER_HEAD == layer->identity) ? PR_TRUE : PR_FALSE;
|
|
newstack = PR_NEW(PRFileDesc);
|
|
if (NULL == newstack)
|
|
{
|
|
PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0);
|
|
return NULL;
|
|
}
|
|
*newstack = *fd; /* make a copy of the accepting layer */
|
|
|
|
newfd = (fd->lower->methods->accept)(fd->lower, addr, timeout);
|
|
if (NULL == newfd)
|
|
{
|
|
PR_DELETE(newstack);
|
|
return NULL;
|
|
}
|
|
|
|
if (newstyle_stack) {
|
|
newstack->lower = newfd;
|
|
newfd->higher = newstack;
|
|
return newstack;
|
|
} else {
|
|
/* this PR_PushIOLayer call cannot fail */
|
|
rv = PR_PushIOLayer(newfd, PR_TOP_IO_LAYER, newstack);
|
|
PR_ASSERT(PR_SUCCESS == rv);
|
|
return newfd; /* that's it */
|
|
}
|
|
}
|
|
|
|
static PRStatus PR_CALLBACK pl_DefBind (PRFileDesc *fd, const PRNetAddr *addr)
|
|
{
|
|
PR_ASSERT(fd != NULL);
|
|
PR_ASSERT(fd->lower != NULL);
|
|
|
|
return (fd->lower->methods->bind)(fd->lower, addr);
|
|
}
|
|
|
|
static PRStatus PR_CALLBACK pl_DefListen (PRFileDesc *fd, PRIntn backlog)
|
|
{
|
|
PR_ASSERT(fd != NULL);
|
|
PR_ASSERT(fd->lower != NULL);
|
|
|
|
return (fd->lower->methods->listen)(fd->lower, backlog);
|
|
}
|
|
|
|
static PRStatus PR_CALLBACK pl_DefShutdown (PRFileDesc *fd, PRIntn how)
|
|
{
|
|
PR_ASSERT(fd != NULL);
|
|
PR_ASSERT(fd->lower != NULL);
|
|
|
|
return (fd->lower->methods->shutdown)(fd->lower, how);
|
|
}
|
|
|
|
static PRInt32 PR_CALLBACK pl_DefRecv (
|
|
PRFileDesc *fd, void *buf, PRInt32 amount,
|
|
PRIntn flags, PRIntervalTime timeout)
|
|
{
|
|
PR_ASSERT(fd != NULL);
|
|
PR_ASSERT(fd->lower != NULL);
|
|
|
|
return (fd->lower->methods->recv)(
|
|
fd->lower, buf, amount, flags, timeout);
|
|
}
|
|
|
|
static PRInt32 PR_CALLBACK pl_DefSend (
|
|
PRFileDesc *fd, const void *buf,
|
|
PRInt32 amount, PRIntn flags, PRIntervalTime timeout)
|
|
{
|
|
PR_ASSERT(fd != NULL);
|
|
PR_ASSERT(fd->lower != NULL);
|
|
|
|
return (fd->lower->methods->send)(fd->lower, buf, amount, flags, timeout);
|
|
}
|
|
|
|
static PRInt32 PR_CALLBACK pl_DefRecvfrom (
|
|
PRFileDesc *fd, void *buf, PRInt32 amount,
|
|
PRIntn flags, PRNetAddr *addr, PRIntervalTime timeout)
|
|
{
|
|
PR_ASSERT(fd != NULL);
|
|
PR_ASSERT(fd->lower != NULL);
|
|
|
|
return (fd->lower->methods->recvfrom)(
|
|
fd->lower, buf, amount, flags, addr, timeout);
|
|
}
|
|
|
|
static PRInt32 PR_CALLBACK pl_DefSendto (
|
|
PRFileDesc *fd, const void *buf, PRInt32 amount, PRIntn flags,
|
|
const PRNetAddr *addr, PRIntervalTime timeout)
|
|
{
|
|
PR_ASSERT(fd != NULL);
|
|
PR_ASSERT(fd->lower != NULL);
|
|
|
|
return (fd->lower->methods->sendto)(
|
|
fd->lower, buf, amount, flags, addr, timeout);
|
|
}
|
|
|
|
static PRInt16 PR_CALLBACK pl_DefPoll (
|
|
PRFileDesc *fd, PRInt16 in_flags, PRInt16 *out_flags)
|
|
{
|
|
PR_ASSERT(fd != NULL);
|
|
PR_ASSERT(fd->lower != NULL);
|
|
|
|
return (fd->lower->methods->poll)(fd->lower, in_flags, out_flags);
|
|
}
|
|
|
|
static PRInt32 PR_CALLBACK pl_DefAcceptread (
|
|
PRFileDesc *sd, PRFileDesc **nd, PRNetAddr **raddr, void *buf,
|
|
PRInt32 amount, PRIntervalTime t)
|
|
{
|
|
PRInt32 nbytes;
|
|
PRStatus rv;
|
|
PRFileDesc *newstack;
|
|
PRFileDesc *layer = sd;
|
|
PRBool newstyle_stack = PR_FALSE;
|
|
|
|
PR_ASSERT(sd != NULL);
|
|
PR_ASSERT(sd->lower != NULL);
|
|
|
|
/* test for new style stack */
|
|
while (NULL != layer->higher)
|
|
layer = layer->higher;
|
|
newstyle_stack = (PR_IO_LAYER_HEAD == layer->identity) ? PR_TRUE : PR_FALSE;
|
|
newstack = PR_NEW(PRFileDesc);
|
|
if (NULL == newstack)
|
|
{
|
|
PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0);
|
|
return -1;
|
|
}
|
|
*newstack = *sd; /* make a copy of the accepting layer */
|
|
|
|
nbytes = sd->lower->methods->acceptread(
|
|
sd->lower, nd, raddr, buf, amount, t);
|
|
if (-1 == nbytes)
|
|
{
|
|
PR_DELETE(newstack);
|
|
return nbytes;
|
|
}
|
|
if (newstyle_stack) {
|
|
newstack->lower = *nd;
|
|
(*nd)->higher = newstack;
|
|
*nd = newstack;
|
|
return nbytes;
|
|
} else {
|
|
/* this PR_PushIOLayer call cannot fail */
|
|
rv = PR_PushIOLayer(*nd, PR_TOP_IO_LAYER, newstack);
|
|
PR_ASSERT(PR_SUCCESS == rv);
|
|
return nbytes;
|
|
}
|
|
}
|
|
|
|
static PRInt32 PR_CALLBACK pl_DefTransmitfile (
|
|
PRFileDesc *sd, PRFileDesc *fd, const void *headers, PRInt32 hlen,
|
|
PRTransmitFileFlags flags, PRIntervalTime t)
|
|
{
|
|
PR_ASSERT(sd != NULL);
|
|
PR_ASSERT(sd->lower != NULL);
|
|
|
|
return sd->lower->methods->transmitfile(
|
|
sd->lower, fd, headers, hlen, flags, t);
|
|
}
|
|
|
|
static PRStatus PR_CALLBACK pl_DefGetsockname (PRFileDesc *fd, PRNetAddr *addr)
|
|
{
|
|
PR_ASSERT(fd != NULL);
|
|
PR_ASSERT(fd->lower != NULL);
|
|
|
|
return (fd->lower->methods->getsockname)(fd->lower, addr);
|
|
}
|
|
|
|
static PRStatus PR_CALLBACK pl_DefGetpeername (PRFileDesc *fd, PRNetAddr *addr)
|
|
{
|
|
PR_ASSERT(fd != NULL);
|
|
PR_ASSERT(fd->lower != NULL);
|
|
|
|
return (fd->lower->methods->getpeername)(fd->lower, addr);
|
|
}
|
|
|
|
static PRStatus PR_CALLBACK pl_DefGetsocketoption (
|
|
PRFileDesc *fd, PRSocketOptionData *data)
|
|
{
|
|
PR_ASSERT(fd != NULL);
|
|
PR_ASSERT(fd->lower != NULL);
|
|
|
|
return (fd->lower->methods->getsocketoption)(fd->lower, data);
|
|
}
|
|
|
|
static PRStatus PR_CALLBACK pl_DefSetsocketoption (
|
|
PRFileDesc *fd, const PRSocketOptionData *data)
|
|
{
|
|
PR_ASSERT(fd != NULL);
|
|
PR_ASSERT(fd->lower != NULL);
|
|
|
|
return (fd->lower->methods->setsocketoption)(fd->lower, data);
|
|
}
|
|
|
|
static PRInt32 PR_CALLBACK pl_DefSendfile (
|
|
PRFileDesc *sd, PRSendFileData *sfd,
|
|
PRTransmitFileFlags flags, PRIntervalTime timeout)
|
|
{
|
|
PR_ASSERT(sd != NULL);
|
|
PR_ASSERT(sd->lower != NULL);
|
|
|
|
return sd->lower->methods->sendfile(
|
|
sd->lower, sfd, flags, timeout);
|
|
}
|
|
|
|
/* Methods for the top of the stack. Just call down to the next fd. */
|
|
static PRIOMethods pl_methods = {
|
|
PR_DESC_LAYERED,
|
|
pl_TopClose,
|
|
pl_DefRead,
|
|
pl_DefWrite,
|
|
pl_DefAvailable,
|
|
pl_DefAvailable64,
|
|
pl_DefFsync,
|
|
pl_DefSeek,
|
|
pl_DefSeek64,
|
|
pl_DefFileInfo,
|
|
pl_DefFileInfo64,
|
|
pl_DefWritev,
|
|
pl_DefConnect,
|
|
pl_TopAccept,
|
|
pl_DefBind,
|
|
pl_DefListen,
|
|
pl_DefShutdown,
|
|
pl_DefRecv,
|
|
pl_DefSend,
|
|
pl_DefRecvfrom,
|
|
pl_DefSendto,
|
|
pl_DefPoll,
|
|
pl_DefAcceptread,
|
|
pl_DefTransmitfile,
|
|
pl_DefGetsockname,
|
|
pl_DefGetpeername,
|
|
(PRReservedFN)_PR_InvalidInt,
|
|
(PRReservedFN)_PR_InvalidInt,
|
|
pl_DefGetsocketoption,
|
|
pl_DefSetsocketoption,
|
|
pl_DefSendfile,
|
|
pl_DefConnectcontinue,
|
|
(PRReservedFN)_PR_InvalidInt,
|
|
(PRReservedFN)_PR_InvalidInt,
|
|
(PRReservedFN)_PR_InvalidInt,
|
|
(PRReservedFN)_PR_InvalidInt
|
|
};
|
|
|
|
PR_IMPLEMENT(const PRIOMethods*) PR_GetDefaultIOMethods(void)
|
|
{
|
|
return &pl_methods;
|
|
} /* PR_GetDefaultIOMethods */
|
|
|
|
PR_IMPLEMENT(PRFileDesc*) PR_CreateIOLayerStub(
|
|
PRDescIdentity ident, const PRIOMethods *methods)
|
|
{
|
|
PRFileDesc *fd = NULL;
|
|
PR_ASSERT((PR_NSPR_IO_LAYER != ident) && (PR_TOP_IO_LAYER != ident));
|
|
if ((PR_NSPR_IO_LAYER == ident) || (PR_TOP_IO_LAYER == ident))
|
|
PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
|
|
else
|
|
{
|
|
fd = PR_NEWZAP(PRFileDesc);
|
|
if (NULL == fd)
|
|
PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0);
|
|
else
|
|
{
|
|
fd->methods = methods;
|
|
fd->dtor = pl_FDDestructor;
|
|
fd->identity = ident;
|
|
}
|
|
}
|
|
return fd;
|
|
} /* PR_CreateIOLayerStub */
|
|
|
|
/*
|
|
* PR_CreateIOLayer
|
|
* Create a new style stack, where the stack top is a dummy header.
|
|
* Unlike the old style stacks, the contents of the stack head
|
|
* are not modified when a layer is pushed onto or popped from a new
|
|
* style stack.
|
|
*/
|
|
|
|
PR_IMPLEMENT(PRFileDesc*) PR_CreateIOLayer(PRFileDesc *top)
|
|
{
|
|
PRFileDesc *fd = NULL;
|
|
|
|
fd = PR_NEWZAP(PRFileDesc);
|
|
if (NULL == fd)
|
|
PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0);
|
|
else
|
|
{
|
|
fd->methods = &pl_methods;
|
|
fd->dtor = pl_FDDestructor;
|
|
fd->identity = PR_IO_LAYER_HEAD;
|
|
fd->higher = NULL;
|
|
fd->lower = top;
|
|
top->higher = fd;
|
|
top->lower = NULL;
|
|
}
|
|
return fd;
|
|
} /* PR_CreateIOLayer */
|
|
|
|
/*
|
|
* _PR_DestroyIOLayer
|
|
* Delete the stack head of a new style stack.
|
|
*/
|
|
|
|
static PRStatus _PR_DestroyIOLayer(PRFileDesc *stack)
|
|
{
|
|
if (NULL == stack)
|
|
return PR_FAILURE;
|
|
else {
|
|
PR_DELETE(stack);
|
|
return PR_SUCCESS;
|
|
}
|
|
} /* _PR_DestroyIOLayer */
|
|
|
|
PR_IMPLEMENT(PRStatus) PR_PushIOLayer(
|
|
PRFileDesc *stack, PRDescIdentity id, PRFileDesc *fd)
|
|
{
|
|
PRFileDesc *insert = PR_GetIdentitiesLayer(stack, id);
|
|
|
|
PR_ASSERT(fd != NULL);
|
|
PR_ASSERT(stack != NULL);
|
|
PR_ASSERT(insert != NULL);
|
|
PR_ASSERT(PR_IO_LAYER_HEAD != id);
|
|
if ((NULL == stack) || (NULL == fd) || (NULL == insert))
|
|
{
|
|
PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
|
|
return PR_FAILURE;
|
|
}
|
|
|
|
if (stack == insert)
|
|
{
|
|
/* going on top of the stack */
|
|
/* old-style stack */
|
|
PRFileDesc copy = *stack;
|
|
*stack = *fd;
|
|
*fd = copy;
|
|
fd->higher = stack;
|
|
stack->lower = fd;
|
|
stack->higher = NULL;
|
|
} else {
|
|
/*
|
|
* going somewhere in the middle of the stack for both old and new
|
|
* style stacks, or going on top of stack for new style stack
|
|
*/
|
|
fd->lower = insert;
|
|
fd->higher = insert->higher;
|
|
|
|
insert->higher->lower = fd;
|
|
insert->higher = fd;
|
|
}
|
|
|
|
return PR_SUCCESS;
|
|
}
|
|
|
|
PR_IMPLEMENT(PRFileDesc*) PR_PopIOLayer(PRFileDesc *stack, PRDescIdentity id)
|
|
{
|
|
PRFileDesc *extract = PR_GetIdentitiesLayer(stack, id);
|
|
|
|
PR_ASSERT(0 != id);
|
|
PR_ASSERT(NULL != stack);
|
|
PR_ASSERT(NULL != extract);
|
|
if ((NULL == stack) || (0 == id) || (NULL == extract))
|
|
{
|
|
PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
|
|
return NULL;
|
|
}
|
|
|
|
if (extract == stack) {
|
|
/* popping top layer of the stack */
|
|
/* old style stack */
|
|
PRFileDesc copy = *stack;
|
|
extract = stack->lower;
|
|
*stack = *extract;
|
|
*extract = copy;
|
|
stack->higher = NULL;
|
|
} else if ((PR_IO_LAYER_HEAD == stack->identity) &&
|
|
(extract == stack->lower) && (extract->lower == NULL)) {
|
|
/*
|
|
* new style stack
|
|
* popping the only layer in the stack; delete the stack too
|
|
*/
|
|
stack->lower = NULL;
|
|
_PR_DestroyIOLayer(stack);
|
|
} else {
|
|
/* for both kinds of stacks */
|
|
extract->lower->higher = extract->higher;
|
|
extract->higher->lower = extract->lower;
|
|
}
|
|
extract->higher = extract->lower = NULL;
|
|
return extract;
|
|
} /* PR_PopIOLayer */
|
|
|
|
#define ID_CACHE_INCREMENT 16
|
|
typedef struct _PRIdentity_cache
|
|
{
|
|
PRLock *ml;
|
|
char **name;
|
|
PRIntn length;
|
|
PRDescIdentity ident;
|
|
} _PRIdentity_cache;
|
|
|
|
static _PRIdentity_cache identity_cache;
|
|
|
|
PR_IMPLEMENT(PRDescIdentity) PR_GetUniqueIdentity(const char *layer_name)
|
|
{
|
|
PRDescIdentity identity, length;
|
|
char **names = NULL, *name = NULL, **old = NULL;
|
|
|
|
if (!_pr_initialized) _PR_ImplicitInitialization();
|
|
|
|
PR_ASSERT((PRDescIdentity)0x7fff > identity_cache.ident);
|
|
|
|
if (NULL != layer_name)
|
|
{
|
|
name = (char*)PR_Malloc(strlen(layer_name) + 1);
|
|
if (NULL == name)
|
|
{
|
|
PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0);
|
|
return PR_INVALID_IO_LAYER;
|
|
}
|
|
strcpy(name, layer_name);
|
|
}
|
|
|
|
/* this initial code runs unsafe */
|
|
retry:
|
|
PR_ASSERT(NULL == names);
|
|
length = identity_cache.length;
|
|
if (length < (identity_cache.ident + 1))
|
|
{
|
|
length += ID_CACHE_INCREMENT;
|
|
names = (char**)PR_CALLOC(length * sizeof(char*));
|
|
if (NULL == names)
|
|
{
|
|
if (NULL != name) PR_DELETE(name);
|
|
PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0);
|
|
return PR_INVALID_IO_LAYER;
|
|
}
|
|
}
|
|
|
|
/* now we get serious about thread safety */
|
|
PR_Lock(identity_cache.ml);
|
|
PR_ASSERT(identity_cache.ident <= identity_cache.length);
|
|
identity = identity_cache.ident + 1;
|
|
if (identity > identity_cache.length) /* there's no room */
|
|
{
|
|
/* we have to do something - hopefully it's already done */
|
|
if ((NULL != names) && (length >= identity))
|
|
{
|
|
/* what we did is still okay */
|
|
memcpy(
|
|
names, identity_cache.name,
|
|
identity_cache.length * sizeof(char*));
|
|
old = identity_cache.name;
|
|
identity_cache.name = names;
|
|
identity_cache.length = length;
|
|
names = NULL;
|
|
}
|
|
else
|
|
{
|
|
PR_ASSERT(identity_cache.ident <= identity_cache.length);
|
|
PR_Unlock(identity_cache.ml);
|
|
if (NULL != names) PR_DELETE(names);
|
|
goto retry;
|
|
}
|
|
}
|
|
if (NULL != name) /* there's a name to be stored */
|
|
{
|
|
identity_cache.name[identity] = name;
|
|
}
|
|
identity_cache.ident = identity;
|
|
PR_ASSERT(identity_cache.ident <= identity_cache.length);
|
|
PR_Unlock(identity_cache.ml);
|
|
|
|
if (NULL != old) PR_DELETE(old);
|
|
if (NULL != names) PR_DELETE(names);
|
|
|
|
return identity;
|
|
} /* PR_GetUniqueIdentity */
|
|
|
|
PR_IMPLEMENT(const char*) PR_GetNameForIdentity(PRDescIdentity ident)
|
|
{
|
|
if (!_pr_initialized) _PR_ImplicitInitialization();
|
|
|
|
if (PR_TOP_IO_LAYER == ident) return NULL;
|
|
|
|
PR_ASSERT(ident <= identity_cache.ident);
|
|
return (ident > identity_cache.ident) ? NULL : identity_cache.name[ident];
|
|
} /* PR_GetNameForIdentity */
|
|
|
|
PR_IMPLEMENT(PRDescIdentity) PR_GetLayersIdentity(PRFileDesc* fd)
|
|
{
|
|
PR_ASSERT(NULL != fd);
|
|
if (PR_IO_LAYER_HEAD == fd->identity) {
|
|
PR_ASSERT(NULL != fd->lower);
|
|
return fd->lower->identity;
|
|
} else
|
|
return fd->identity;
|
|
} /* PR_GetLayersIdentity */
|
|
|
|
PR_IMPLEMENT(PRFileDesc*) PR_GetIdentitiesLayer(PRFileDesc* fd, PRDescIdentity id)
|
|
{
|
|
PRFileDesc *layer = fd;
|
|
|
|
if (PR_TOP_IO_LAYER == id) {
|
|
if (PR_IO_LAYER_HEAD == fd->identity)
|
|
return fd->lower;
|
|
else
|
|
return fd;
|
|
}
|
|
|
|
for (layer = fd; layer != NULL; layer = layer->lower)
|
|
{
|
|
if (id == layer->identity) return layer;
|
|
}
|
|
for (layer = fd; layer != NULL; layer = layer->higher)
|
|
{
|
|
if (id == layer->identity) return layer;
|
|
}
|
|
return NULL;
|
|
} /* PR_GetIdentitiesLayer */
|
|
|
|
void _PR_InitLayerCache(void)
|
|
{
|
|
memset(&identity_cache, 0, sizeof(identity_cache));
|
|
identity_cache.ml = PR_NewLock();
|
|
PR_ASSERT(NULL != identity_cache.ml);
|
|
} /* _PR_InitLayerCache */
|
|
|
|
void _PR_CleanupLayerCache(void)
|
|
{
|
|
if (identity_cache.ml)
|
|
{
|
|
PR_DestroyLock(identity_cache.ml);
|
|
identity_cache.ml = NULL;
|
|
}
|
|
|
|
if (identity_cache.name)
|
|
{
|
|
PRDescIdentity ident;
|
|
|
|
for (ident = 0; ident <= identity_cache.ident; ident++)
|
|
PR_DELETE(identity_cache.name[ident]);
|
|
|
|
PR_DELETE(identity_cache.name);
|
|
}
|
|
} /* _PR_CleanupLayerCache */
|
|
|
|
/* prlayer.c */
|