Back to index

lightning-sunbird  0.9+nobinonly
Defines | Functions | Variables
ipcdUnix.cpp File Reference
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "prio.h"
#include "prerror.h"
#include "prthread.h"
#include "prinrval.h"
#include "plstr.h"
#include "prprf.h"
#include "ipcConfig.h"
#include "ipcLog.h"
#include "ipcMessage.h"
#include "ipcClient.h"
#include "ipcModuleReg.h"
#include "ipcdPrivate.h"
#include "ipcd.h"

Go to the source code of this file.

Defines

#define IPC_USE_FILE_LOCK

Functions

static PRBool AcquireDaemonLock (const char *baseDir)
static PRBool InitDaemonDir (const char *socketPath)
static void ShutdownDaemonDir ()
static int AddClient (PRFileDesc *fd)
static int RemoveClient (int clientIndex)
static void PollLoop (PRFileDesc *listenFD)
PRStatus IPC_PlatformSendMsg (ipcClient *client, ipcMessage *msg)
int main (int argc, char **argv)
 The Xalan testcases app.

Variables

static int ipcLockFD = 0
ipcClientipcClients = NULL
int ipcClientCount = 0
static ipcClient ipcClientArray [IPC_MAX_CLIENTS+1]
static PRPollDesc ipcPollList [IPC_MAX_CLIENTS+1]

Define Documentation

Definition at line 86 of file ipcdUnix.cpp.


Function Documentation

static PRBool AcquireDaemonLock ( const char *  baseDir) [static]

Definition at line 93 of file ipcdUnix.cpp.

{
    const char lockName[] = "lock";

    int dirLen = strlen(baseDir);
    int len = dirLen            // baseDir
            + 1                 // "/"
            + sizeof(lockName); // "lock"

    char *lockFile = (char *) malloc(len);
    memcpy(lockFile, baseDir, dirLen);
    lockFile[dirLen] = '/';
    memcpy(lockFile + dirLen + 1, lockName, sizeof(lockName));

    // 
    // open lock file.  it remains open until we shutdown.
    //
    ipcLockFD = open(lockFile, O_WRONLY|O_CREAT, S_IWUSR|S_IRUSR);

    free(lockFile);

    if (ipcLockFD == -1)
        return PR_FALSE;

    //
    // we use fcntl for locking.  assumption: filesystem should be local.
    // this API is nice because the lock will be automatically released
    // when the process dies.  it will also be released when the file
    // descriptor is closed.
    //
    struct flock lock;
    lock.l_type = F_WRLCK;
    lock.l_start = 0;
    lock.l_len = 0;
    lock.l_whence = SEEK_SET;
    if (fcntl(ipcLockFD, F_SETLK, &lock) == -1)
        return PR_FALSE;

    //
    // truncate lock file once we have exclusive access to it.
    //
    ftruncate(ipcLockFD, 0);

    //
    // write our PID into the lock file (this just seems like a good idea...
    // no real purpose otherwise).
    //
    char buf[32];
    int nb = PR_snprintf(buf, sizeof(buf), "%u\n", (unsigned long) getpid());
    write(ipcLockFD, buf, nb);

    return PR_TRUE;
}

Here is the call graph for this function:

Here is the caller graph for this function:

static int AddClient ( PRFileDesc fd) [static]

Definition at line 218 of file ipcdUnix.cpp.

{
    if (ipcClientCount == IPC_MAX_CLIENTS) {
        LOG(("reached maximum client limit\n"));
        return -1;
    }

    int pollCount = ipcClientCount + 1;

    ipcClientArray[pollCount].Init();

    ipcPollList[pollCount].fd = fd;
    ipcPollList[pollCount].in_flags = PR_POLL_READ;
    ipcPollList[pollCount].out_flags = 0;

    ++ipcClientCount;
    return 0;
}

Here is the call graph for this function:

Here is the caller graph for this function:

static PRBool InitDaemonDir ( const char *  socketPath) [static]

Definition at line 147 of file ipcdUnix.cpp.

{
    LOG(("InitDaemonDir [sock=%s]\n", socketPath));

    char *baseDir = PL_strdup(socketPath);

    //
    // make sure IPC directory exists (XXX this should be recursive)
    //
    char *p = strrchr(baseDir, '/');
    if (p)
        p[0] = '\0';
    mkdir(baseDir, 0700);

    //
    // if we can't acquire the daemon lock, then another daemon
    // must be active, so bail.
    //
    PRBool haveLock = AcquireDaemonLock(baseDir);

    PL_strfree(baseDir);

    if (haveLock) {
        // delete an existing socket to prevent bind from failing.
        unlink(socketPath);
    }
    return haveLock;
}

Here is the call graph for this function:

Here is the caller graph for this function:

PRStatus IPC_PlatformSendMsg ( ipcClient client,
ipcMessage msg 
)

Definition at line 364 of file ipcdUnix.cpp.

{
    LOG(("IPC_PlatformSendMsg\n"));

    //
    // must copy message onto send queue.
    //
    client->EnqueueOutboundMsg(msg);

    //
    // since our Process method may have already been called, we must ensure
    // that the PR_POLL_WRITE flag is set.
    //
    int clientIndex = client - ipcClientArray;
    ipcPollList[clientIndex].in_flags |= PR_POLL_WRITE;

    return PR_SUCCESS;
}
int main ( int  argc,
char **  argv 
)

The Xalan testcases app.

Definition at line 385 of file ipcdUnix.cpp.

{
    PRFileDesc *listenFD = NULL;
    PRNetAddr addr;

    //
    // ignore SIGINT so <ctrl-c> from terminal only kills the client
    // which spawned this daemon.
    //
    signal(SIGINT, SIG_IGN);
    // XXX block others?  check cartman

    // ensure strict file permissions
    umask(0077);

    IPC_InitLog("###");

    LOG(("daemon started...\n"));

    //XXX uncomment these lines to test slow starting daemon
    //IPC_Sleep(2);

    // set socket address
    addr.local.family = PR_AF_LOCAL;
    if (argc < 2)
        IPC_GetDefaultSocketPath(addr.local.path, sizeof(addr.local.path));
    else
        PL_strncpyz(addr.local.path, argv[1], sizeof(addr.local.path));

#ifdef IPC_USE_FILE_LOCK
    if (!InitDaemonDir(addr.local.path)) {
        LOG(("InitDaemonDir failed\n"));
        goto end;
    }
#endif

    listenFD = PR_OpenTCPSocket(PR_AF_LOCAL);
    if (!listenFD) {
        LOG(("PR_OpenTCPSocket failed [%d]\n", PR_GetError()));
        goto end;
    }

    if (PR_Bind(listenFD, &addr) != PR_SUCCESS) {
        LOG(("PR_Bind failed [%d]\n", PR_GetError()));
        goto end;
    }

    IPC_InitModuleReg(argv[0]);

    if (PR_Listen(listenFD, 5) != PR_SUCCESS) {
        LOG(("PR_Listen failed [%d]\n", PR_GetError()));
        goto end;
    }

    IPC_NotifyParent();

    PollLoop(listenFD);

end:
    IPC_ShutdownModuleReg();

    IPC_NotifyParent();

    //IPC_Sleep(5);

#ifdef IPC_USE_FILE_LOCK
    // it is critical that we release the lock before closing the socket,
    // otherwise, a client might launch another daemon that would be unable
    // to acquire the lock and would then leave the client without a daemon.
 
    ShutdownDaemonDir();
#endif

    if (listenFD) {
        LOG(("closing socket\n"));
        PR_Close(listenFD);
    }
    return 0;
}

Here is the call graph for this function:

static void PollLoop ( PRFileDesc listenFD) [static]

Definition at line 268 of file ipcdUnix.cpp.

{
    // the first element of ipcClientArray is unused.
    memset(ipcClientArray, 0, sizeof(ipcClientArray));
    ipcClients = ipcClientArray + 1;
    ipcClientCount = 0;

    ipcPollList[0].fd = listenFD;
    ipcPollList[0].in_flags = PR_POLL_EXCEPT | PR_POLL_READ;
    
    while (1) {
        PRInt32 rv;
        PRIntn i;

        int pollCount = ipcClientCount + 1;

        ipcPollList[0].out_flags = 0;

        //
        // poll
        //
        // timeout after 5 minutes.  if no connections after timeout, then
        // exit.  this timeout ensures that we don't stay resident when no
        // clients are interested in connecting after spawning the daemon.
        //
        // XXX add #define for timeout value
        //
        LOG(("calling PR_Poll [pollCount=%d]\n", pollCount));
        rv = PR_Poll(ipcPollList, pollCount, PR_SecondsToInterval(60 * 5));
        if (rv == -1) {
            LOG(("PR_Poll failed [%d]\n", PR_GetError()));
            return;
        }

        if (rv > 0) {
            //
            // process clients that are ready
            //
            for (i = 1; i < pollCount; ++i) {
                if (ipcPollList[i].out_flags != 0) {
                    ipcPollList[i].in_flags =
                        ipcClientArray[i].Process(ipcPollList[i].fd,
                                                  ipcPollList[i].out_flags);
                    ipcPollList[i].out_flags = 0;
                }
            }

            //
            // cleanup any dead clients (indicated by a zero in_flags)
            //
            for (i = pollCount - 1; i >= 1; --i) {
                if (ipcPollList[i].in_flags == 0)
                    RemoveClient(i);
            }

            //
            // check for new connection
            //
            if (ipcPollList[0].out_flags & PR_POLL_READ) {
                LOG(("got new connection\n"));

                PRNetAddr clientAddr;
                memset(&clientAddr, 0, sizeof(clientAddr));
                PRFileDesc *clientFD;

                clientFD = PR_Accept(listenFD, &clientAddr, PR_INTERVAL_NO_WAIT);
                if (clientFD == NULL) {
                    // ignore this error... perhaps the client disconnected.
                    LOG(("PR_Accept failed [%d]\n", PR_GetError()));
                }
                else {
                    // make socket non-blocking
                    PRSocketOptionData opt;
                    opt.option = PR_SockOpt_Nonblocking;
                    opt.value.non_blocking = PR_TRUE;
                    PR_SetSocketOption(clientFD, &opt);

                    if (AddClient(clientFD) != 0)
                        PR_Close(clientFD);
                }
            }
        }

        //
        // shutdown if no clients
        //
        if (ipcClientCount == 0) {
            LOG(("shutting down\n"));
            break;
        }
    }
}

Here is the call graph for this function:

Here is the caller graph for this function:

static int RemoveClient ( int  clientIndex) [static]

Definition at line 237 of file ipcdUnix.cpp.

{
    PRPollDesc *pd = &ipcPollList[clientIndex];

    PR_Close(pd->fd);

    ipcClientArray[clientIndex].Finalize();

    //
    // keep the clients and poll_fds contiguous; move the last one into
    // the spot held by the one that is going away.
    //
    int toIndex = clientIndex;
    int fromIndex = ipcClientCount;
    if (fromIndex != toIndex) {
        memcpy(&ipcClientArray[toIndex], &ipcClientArray[fromIndex], sizeof(ipcClient));
        memcpy(&ipcPollList[toIndex], &ipcPollList[fromIndex], sizeof(PRPollDesc));
    }

    //
    // zero out the old entries.
    //
    memset(&ipcClientArray[fromIndex], 0, sizeof(ipcClient));
    memset(&ipcPollList[fromIndex], 0, sizeof(PRPollDesc));

    --ipcClientCount;
    return 0;
}

Here is the call graph for this function:

Here is the caller graph for this function:

static void ShutdownDaemonDir ( ) [static]

Definition at line 176 of file ipcdUnix.cpp.

{
    LOG(("ShutdownDaemonDir\n"));

    // deleting directory and files underneath it allows another process
    // to think it has exclusive access.  better to just leave the hidden
    // directory in /tmp and let the OS clean it up via the usual tmpdir
    // cleanup cron job.

    // this removes the advisory lock, allowing other processes to acquire it.
    if (ipcLockFD) {
        close(ipcLockFD);
        ipcLockFD = 0;
    }
}

Here is the caller graph for this function:


Variable Documentation

Definition at line 209 of file ipcdUnix.cpp.

Definition at line 202 of file ipcdUnix.cpp.

Definition at line 201 of file ipcdUnix.cpp.

int ipcLockFD = 0 [static]

Definition at line 91 of file ipcdUnix.cpp.

Definition at line 214 of file ipcdUnix.cpp.