Back to index

lightning-sunbird  0.9+nobinonly
Classes | Defines | Typedefs | Functions | Variables
ipcConnectionUnix.cpp File Reference
#include "private/pprio.h"
#include "prerror.h"
#include "prthread.h"
#include "prlock.h"
#include "prlog.h"
#include "prio.h"
#include "ipcConnection.h"
#include "ipcMessageQ.h"
#include "ipcConfig.h"
#include "ipcLog.h"

Go to the source code of this file.

Classes

struct  ipcCallback
struct  ipcConnectionState

Defines

#define IPC_SKIP_SECURITY_CHECKS
#define SOCK   0
#define POLL   1

Typedefs

typedef ipcList< ipcCallbackipcCallbackQ

Functions

static PRStatus DoSecurityCheck (PRFileDesc *fd, const char *path)
static void ConnDestroy (ipcConnectionState *s)
static ipcConnectionStateConnCreate (PRFileDesc *fd)
static nsresult ConnRead (ipcConnectionState *s)
static nsresult ConnWrite (ipcConnectionState *s)
 ConnThread (void *arg)
nsresult TryConnect (PRFileDesc **result)
nsresult IPC_Connect (const char *daemonPath)
 IPC_Connect.
nsresult IPC_Disconnect ()
 IPC_Disconnect.
nsresult IPC_SendMsg (ipcMessage *msg)
 IPC_SendMsg.
nsresult IPC_DoCallback (ipcCallbackFunc func, void *arg)
 IPC_DoCallback.

Variables

static ipcConnectionStategConnState = NULL
static PRThreadgConnThread = NULL

Class Documentation

struct ipcConnectionState

Definition at line 128 of file ipcConnectionUnix.cpp.

Collaboration diagram for ipcConnectionState:
Class Members
ipcCallbackQ callback_queue
PRPollDesc fds
ipcMessage * in_msg
PRLock * lock
PRUint32 send_offset
ipcMessageQ send_queue
PRBool shutdown

Define Documentation

Definition at line 62 of file ipcConnectionUnix.cpp.

#define POLL   1

Definition at line 140 of file ipcConnectionUnix.cpp.

#define SOCK   0

Definition at line 139 of file ipcConnectionUnix.cpp.


Typedef Documentation

Definition at line 124 of file ipcConnectionUnix.cpp.


Function Documentation

static ipcConnectionState* ConnCreate ( PRFileDesc fd) [static]

Definition at line 162 of file ipcConnectionUnix.cpp.

{
  ipcConnectionState *s = new ipcConnectionState;
  if (!s)
    return NULL;

  s->lock = PR_NewLock();
  s->fds[SOCK].fd = NULL;
  s->fds[POLL].fd = PR_NewPollableEvent();
  s->send_offset = 0;
  s->in_msg = NULL;
  s->shutdown = PR_FALSE;

  if (!s->lock || !s->fds[1].fd)
  {
    ConnDestroy(s);
    return NULL;
  }

  // store this only if we are going to succeed.
  s->fds[SOCK].fd = fd;

  return s;
}

Here is the call graph for this function:

Here is the caller graph for this function:

static void ConnDestroy ( ipcConnectionState s) [static]

Definition at line 143 of file ipcConnectionUnix.cpp.

{
  if (s->lock)
    PR_DestroyLock(s->lock);  

  if (s->fds[SOCK].fd)
    PR_Close(s->fds[SOCK].fd);

  if (s->fds[POLL].fd)
    PR_DestroyPollableEvent(s->fds[POLL].fd);

  if (s->in_msg)
    delete s->in_msg;

  s->send_queue.DeleteAll();
  delete s;
}

Here is the call graph for this function:

Here is the caller graph for this function:

static nsresult ConnRead ( ipcConnectionState s) [static]

Definition at line 188 of file ipcConnectionUnix.cpp.

{
  char buf[1024];
  nsresult rv = NS_OK;
  PRInt32 n;

  do
  {
    n = PR_Read(s->fds[SOCK].fd, buf, sizeof(buf));
    if (n < 0)
    {
      PRErrorCode err = PR_GetError();
      if (err == PR_WOULD_BLOCK_ERROR)
      {
        // socket is empty... we need to go back to polling.
        break;
      }
      LOG(("PR_Read returned failure [err=%d]\n", err));
      rv = NS_ERROR_UNEXPECTED;
    }
    else if (n == 0)
    {
      LOG(("PR_Read returned EOF\n"));
      rv = NS_ERROR_UNEXPECTED;
    }
    else
    {
      const char *pdata = buf;
      while (n)
      {
        PRUint32 bytesRead;
        PRBool complete;

        if (!s->in_msg)
        {
          s->in_msg = new ipcMessage;
          if (!s->in_msg)
          {
            rv = NS_ERROR_OUT_OF_MEMORY;
            break;
          }
        }

        if (s->in_msg->ReadFrom(pdata, n, &bytesRead, &complete) != PR_SUCCESS)
        {
          LOG(("error reading IPC message\n"));
          rv = NS_ERROR_UNEXPECTED;
          break;
        }

        PR_ASSERT(PRUint32(n) >= bytesRead);
        n -= bytesRead;
        pdata += bytesRead;

        if (complete)
        {
          // protect against weird re-entrancy cases...
          ipcMessage *m = s->in_msg;
          s->in_msg = NULL;

          IPC_OnMessageAvailable(m);
        }
      }
    }
  }
  while (NS_SUCCEEDED(rv));

  return rv;
}

Here is the call graph for this function:

Here is the caller graph for this function:

ConnThread ( void arg)

Definition at line 304 of file ipcConnectionUnix.cpp.

{
  PRInt32 num;
  nsresult rv = NS_OK;

  ipcConnectionState *s = (ipcConnectionState *) arg;

  // we monitor two file descriptors in this thread.  the first (at index 0) is
  // the socket connection with the IPC daemon.  the second (at index 1) is the
  // pollable event we monitor in order to know when to send messages to the
  // IPC daemon.

  s->fds[SOCK].in_flags = PR_POLL_READ;
  s->fds[POLL].in_flags = PR_POLL_READ;

  while (NS_SUCCEEDED(rv))
  {
    s->fds[SOCK].out_flags = 0;
    s->fds[POLL].out_flags = 0;

    //
    // poll on the IPC socket and NSPR pollable event
    //
    num = PR_Poll(s->fds, 2, PR_INTERVAL_NO_TIMEOUT);
    if (num > 0)
    {
      ipcCallbackQ cbs_to_run;

      // check if something has been added to the send queue.  if so, then
      // acknowledge pollable event (wait should not block), and configure
      // poll flags to find out when we can write.

      if (s->fds[POLL].out_flags & PR_POLL_READ)
      {
        PR_WaitForPollableEvent(s->fds[POLL].fd);
        PR_Lock(s->lock);

        if (!s->send_queue.IsEmpty())
          s->fds[SOCK].in_flags |= PR_POLL_WRITE;

        if (!s->callback_queue.IsEmpty())
          s->callback_queue.MoveTo(cbs_to_run);

        PR_Unlock(s->lock);
      }

      // check if we can read...
      if (s->fds[SOCK].out_flags & PR_POLL_READ)
        rv = ConnRead(s);

      // check if we can write...
      if (s->fds[SOCK].out_flags & PR_POLL_WRITE)
        rv = ConnWrite(s);

      // check if we have callbacks to run
      while (!cbs_to_run.IsEmpty())
      {
        ipcCallback *cb = cbs_to_run.First();
        (cb->func)(cb->arg);
        cbs_to_run.DeleteFirst();
      }

      // check if we should exit this thread.  delay processing a shutdown
      // request until after all queued up messages have been sent and until
      // after all queued up callbacks have been run.
      PR_Lock(s->lock);
      if (s->shutdown && s->send_queue.IsEmpty() && s->callback_queue.IsEmpty())
        rv = NS_ERROR_ABORT;
      PR_Unlock(s->lock);
    }
    else
    {
      LOG(("PR_Poll returned an error\n"));
      rv = NS_ERROR_UNEXPECTED;
    }
  }

  // notify termination of the IPC connection
  if (rv == NS_ERROR_ABORT)
    rv = NS_OK;
  IPC_OnConnectionEnd(rv);

  LOG(("IPC thread exiting\n"));
}

Here is the call graph for this function:

Here is the caller graph for this function:

static nsresult ConnWrite ( ipcConnectionState s) [static]

Definition at line 259 of file ipcConnectionUnix.cpp.

{
  nsresult rv = NS_OK;

  PR_Lock(s->lock);

  // write one message and then return.
  if (s->send_queue.First())
  {
    PRInt32 n = PR_Write(s->fds[SOCK].fd,
                         s->send_queue.First()->MsgBuf() + s->send_offset,
                         s->send_queue.First()->MsgLen() - s->send_offset);
    if (n <= 0)
    {
      PRErrorCode err = PR_GetError();
      if (err == PR_WOULD_BLOCK_ERROR)
      {
        // socket is full... we need to go back to polling.
      }
      else
      {
        LOG(("error writing to socket [err=%d]\n", err));
        rv = NS_ERROR_UNEXPECTED;
      }
    }
    else
    {
      s->send_offset += n;
      if (s->send_offset == s->send_queue.First()->MsgLen())
      {
        s->send_queue.DeleteFirst();
        s->send_offset = 0;

        // if the send queue is empty, then we need to stop trying to write.
        if (s->send_queue.IsEmpty())
          s->fds[SOCK].in_flags &= ~PR_POLL_WRITE;
      }
    }
  }

  PR_Unlock(s->lock);
  return rv;
}

Here is the call graph for this function:

Here is the caller graph for this function:

static PRStatus DoSecurityCheck ( PRFileDesc fd,
const char *  path 
) [static]

Definition at line 71 of file ipcConnectionUnix.cpp.

{
#ifndef IPC_SKIP_SECURITY_CHECKS
    //
    // now that we have a connected socket; do some security checks on the
    // file descriptor.
    //
    //   (1) make sure owner matches
    //   (2) make sure permissions match expected permissions
    //
    // if these conditions aren't met then bail.
    //
    int unix_fd = PR_FileDesc2NativeHandle(fd);  

    struct stat st;
    if (fstat(unix_fd, &st) == -1) {
        LOG(("stat failed"));
        return PR_FAILURE;
    }

    if (st.st_uid != getuid() && st.st_uid != geteuid()) {
        //
        // on OSX 10.1.5, |fstat| has a bug when passed a file descriptor to
        // a socket.  it incorrectly returns a UID of 0.  however, |stat|
        // succeeds, but using |stat| introduces a race condition.
        //
        // XXX come up with a better security check.
        //
        if (st.st_uid != 0) {
            LOG(("userid check failed"));
            return PR_FAILURE;
        }
        if (stat(path, &st) == -1) {
            LOG(("stat failed"));
            return PR_FAILURE;
        }
        if (st.st_uid != getuid() && st.st_uid != geteuid()) {
            LOG(("userid check failed"));
            return PR_FAILURE;
        }
    }
#endif
    return PR_SUCCESS;
}

Here is the call graph for this function:

Here is the caller graph for this function:

nsresult IPC_Connect ( const char *  daemonPath)

IPC_Connect.

This function causes a connection to the IPC daemon to be established. If a connection already exists, then this function will be ignored.

Parameters:
daemonPathSpecifies the path to the IPC daemon executable.

NOTE: This function must be called on the main thread.

Definition at line 439 of file ipcConnectionUnix.cpp.

{
  // synchronous connect, spawn daemon if necessary.

  PRFileDesc *fd;
  nsresult rv = NS_ERROR_FAILURE;

  if (gConnState)
    return NS_ERROR_ALREADY_INITIALIZED;

  //
  // here's the connection algorithm:  try to connect to an existing daemon.
  // if the connection fails, then spawn the daemon (wait for it to be ready),
  // and then retry the connection.  it is critical that the socket used to
  // connect to the daemon not be inherited (this causes problems on RH9 at
  // least).
  //

  rv = TryConnect(&fd);
  if (NS_FAILED(rv))
  {
    rv = IPC_SpawnDaemon(daemonPath);
    if (NS_SUCCEEDED(rv))
      rv = TryConnect(&fd);
  }

  if (NS_FAILED(rv))
    goto end;

  //
  // ok, we have a connection to the daemon!
  //

  // build connection state object
  gConnState = ConnCreate(fd);
  if (!gConnState)
  {
    rv = NS_ERROR_OUT_OF_MEMORY;
    goto end;
  }
  fd = NULL; // connection state now owns the socket

  gConnThread = PR_CreateThread(PR_USER_THREAD,
                                ConnThread,
                                gConnState,
                                PR_PRIORITY_NORMAL,
                                PR_GLOBAL_THREAD,
                                PR_JOINABLE_THREAD,
                                0);
  if (!gConnThread)
  {
    rv = NS_ERROR_OUT_OF_MEMORY;
    goto end;
  }

#ifdef DEBUG
  gMainThread = PR_GetCurrentThread();
#endif
  return NS_OK;

end:
  if (gConnState)
  {
    ConnDestroy(gConnState);
    gConnState = NULL;
  }
  if (fd)
    PR_Close(fd);
  return rv;
}

Here is the call graph for this function:

IPC_Disconnect.

This function causes a connection to the IPC daemon to be closed. Any unsent messages (IPC_SendMsg puts messages on a queue) will be sent to the IPC daemon before the connection is closed.

NOTE: This function must be called on the main thread.

Definition at line 511 of file ipcConnectionUnix.cpp.

Here is the call graph for this function:

IPC_DoCallback.

This function executes a callback function on the same background thread that calls IPC_OnConnectionEnd and IPC_OnMessageAvailable.

If this function succeeds, then the caller is guaranteed that |func| will be called. This guarantee is important because it allows the caller to free any memory associated with |arg| once |func| has been called.

NOTE: This function may be called on any thread.

Definition at line 548 of file ipcConnectionUnix.cpp.

{
  if (!gConnState || !gConnThread)
    return NS_ERROR_NOT_INITIALIZED;
  
  ipcCallback *callback = new ipcCallback;
  if (!callback)
    return NS_ERROR_OUT_OF_MEMORY;
  callback->func = func;
  callback->arg = arg;

  PR_Lock(gConnState->lock);
  gConnState->callback_queue.Append(callback);
  PR_SetPollableEvent(gConnState->fds[POLL].fd);
  PR_Unlock(gConnState->lock);
  return NS_OK;
}

Here is the call graph for this function:

IPC_SendMsg.

This function sends a message to the IPC daemon. Typically, the message is put on a queue, to be delivered asynchronously to the IPC daemon. The ipcMessage object will be deleted when IPC_SendMsg is done with it. The caller must not touch |msg| after passing it to IPC_SendMsg.

IPC_SendMsg cannot be called before IPC_Connect or after IPC_Disconnect.

NOTE: This function may be called on any thread.

Definition at line 534 of file ipcConnectionUnix.cpp.

Here is the call graph for this function:

nsresult TryConnect ( PRFileDesc **  result)

Definition at line 401 of file ipcConnectionUnix.cpp.

{
  PRFileDesc *fd;
  PRNetAddr addr;
  PRSocketOptionData opt;
  nsresult rv = NS_ERROR_FAILURE;

  fd = PR_OpenTCPSocket(PR_AF_LOCAL);
  if (!fd)
    goto end;

  addr.local.family = PR_AF_LOCAL;
  IPC_GetDefaultSocketPath(addr.local.path, sizeof(addr.local.path));

  // blocking connect... will fail if no one is listening.
  if (PR_Connect(fd, &addr, PR_INTERVAL_NO_TIMEOUT) == PR_FAILURE)
    goto end;

  // make socket non-blocking
  opt.option = PR_SockOpt_Nonblocking;
  opt.value.non_blocking = PR_TRUE;
  PR_SetSocketOption(fd, &opt);

  // do some security checks on connection socket...
  if (DoSecurityCheck(fd, addr.local.path) != PR_SUCCESS)
    goto end;
  
  *result = fd;
  return NS_OK;

end:
  if (fd)
    PR_Close(fd);

  return rv;
}

Here is the call graph for this function:

Here is the caller graph for this function:


Variable Documentation

Definition at line 393 of file ipcConnectionUnix.cpp.

PRThread* gConnThread = NULL [static]

Definition at line 394 of file ipcConnectionUnix.cpp.