revisions following review w/ dougt

This commit is contained in:
darin%netscape.com 2002-11-21 00:13:21 +00:00
Родитель 049a2f65b5
Коммит 8ee92ff6ca
12 изменённых файлов: 189 добавлений и 764 удалений

Просмотреть файл

@ -1,81 +0,0 @@
# ***** 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 Mozilla IPC.
#
# The Initial Developer of the Original Code is
# Netscape Communications Corporation.
# Portions created by the Initial Developer are Copyright (C) 2002
# the Initial Developer. All Rights Reserved.
#
# Contributor(s):
# Darin Fisher <darin@netscape.com>
#
# 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 *****
DEPTH = ../../..
topsrcdir = @top_srcdir@
srcdir = @srcdir@
VPATH = @srcdir@
include $(DEPTH)/config/autoconf.mk
MODULE = ipc
REQUIRES = \
xpcom \
$(NULL)
CPPSRCS = \
ipcd.cpp \
ipcClient.cpp \
ipcModuleReg.cpp \
ipcCommandModule.cpp
ifeq ($(MOZ_WIDGET_TOOLKIT),windows)
CPPSRCS += ipcdWin.cpp
else
CPPSRCS += ipcdUnix.cpp
endif
PROGRAM = mozipcd$(BIN_SUFFIX)
EXPORTS = \
ipcModule.h \
ipcModuleUtil.h \
$(NULL)
LOCAL_INCLUDES = \
-I$(srcdir)/../common \
$(NULL)
include $(topsrcdir)/config/config.mk
LIBS = \
$(EXTRA_DSO_LIBS) \
$(NSPR_LIBS) \
$(DIST)/lib/$(LIB_PREFIX)ipccom_s.$(LIB_SUFFIX) \
$(NULL)
include $(topsrcdir)/config/rules.mk

Просмотреть файл

@ -62,6 +62,7 @@ ipcClient::Init()
// every client must be able to handle IPCM messages.
mTargets.Append(IPCM_TARGET);
// XXX cleanup
// see ipcCommandModule for this:
//IPC_NotifyClientUp(this);
}
@ -142,6 +143,7 @@ ipcClient::DelTarget(const nsID &target)
int
ipcClient::Process(PRFileDesc *fd, int poll_flags)
{
// XXX check if not read OR write
if ((poll_flags & PR_POLL_ERR) ||
(poll_flags & PR_POLL_HUP) ||
(poll_flags & PR_POLL_EXCEPT) ||
@ -159,7 +161,7 @@ ipcClient::Process(PRFileDesc *fd, int poll_flags)
if (poll_flags & PR_POLL_READ) {
LOG(("client socket is now readable\n"));
char buf[1024];
char buf[1024]; // XXX 4k?
PRInt32 n;
// find out how much data is available for reading...
@ -169,11 +171,12 @@ ipcClient::Process(PRFileDesc *fd, int poll_flags)
if (n <= 0)
return 0; // cancel connection
char *ptr = buf;
const char *ptr = buf;
while (n) {
PRUint32 nread;
PRBool complete;
// XXX check return value
mInMsg.ReadFrom(ptr, PRUint32(n), &nread, &complete);
if (complete) {
@ -222,6 +225,7 @@ ipcClient::WriteMsgs(PRFileDesc *fd)
if (nw == bufLen)
mOutMsgQ.DeleteFirst();
// XXX mSendOffset = 0;
else
mSendOffset += nw;
}

Просмотреть файл

@ -40,6 +40,7 @@
#include "nsID.h"
// XXX cruft
#define IPC_EXPORT extern "C" NS_EXPORT
class ipcMessage;
@ -229,7 +230,7 @@ struct ipcModuleEntry
};
//
// IPC_EXPORT int IPC_GetModules(ipcDaemonMethods *, ipcModuleEntry **);
// IPC_EXPORT int IPC_GetModules(const ipcDaemonMethods *, const ipcModuleEntry **);
//
// params:
// methods - the daemon's methods
@ -238,6 +239,6 @@ struct ipcModuleEntry
// returns:
// length of the |entries| array.
//
typedef int (* ipcGetModulesFunc) (ipcDaemonMethods *methods, ipcModuleEntry **entries);
typedef int (* ipcGetModulesFunc) (const ipcDaemonMethods *methods, const ipcModuleEntry **entries);
#endif // !ipcModule_h__

Просмотреть файл

@ -40,6 +40,7 @@
#include "prlink.h"
#include "prio.h"
#include "prlog.h"
#include "plstr.h"
#include "ipcConfig.h"
@ -49,6 +50,8 @@
#include "ipcCommandModule.h"
#include "ipcd.h"
//-----------------------------------------------------------------------------
struct ipcModuleRegEntry
{
nsID target;
@ -59,19 +62,32 @@ struct ipcModuleRegEntry
#define IPC_MAX_MODULE_COUNT 64
static ipcModuleRegEntry ipcModules[IPC_MAX_MODULE_COUNT];
static int ipcModuleCount;
static int ipcModuleCount = 0;
//-----------------------------------------------------------------------------
static PRStatus
AddModule(const nsID &target, ipcModuleMethods *methods, PRLibrary *lib)
AddModule(const nsID &target, ipcModuleMethods *methods, const char *libPath)
{
if (ipcModuleCount == IPC_MAX_MODULE_COUNT) {
LOG(("too many modules!\n"));
return PR_FAILURE;
}
if (!methods) {
PR_NOT_REACHED("null module methods");
return PR_FAILURE;
}
//
// each ipcModuleRegEntry holds a reference to a PRLibrary, and on
// shutdown, each PRLibrary reference will be released. this ensures
// that the library will not be unloaded until all of the modules in
// that library are shutdown.
//
ipcModules[ipcModuleCount].target = target;
ipcModules[ipcModuleCount].methods = methods;
ipcModules[ipcModuleCount].lib = lib;
ipcModules[ipcModuleCount].lib = PR_LoadLibrary(libPath);
++ipcModuleCount;
return PR_SUCCESS;
@ -82,7 +98,7 @@ InitModuleFromLib(const char *modulesDir, const char *fileName)
{
LOG(("InitModuleFromLib [%s]\n", fileName));
static ipcDaemonMethods gDaemonMethods =
static const ipcDaemonMethods gDaemonMethods =
{
IPC_DAEMON_METHODS_VERSION,
IPC_DispatchMsg,
@ -115,12 +131,13 @@ InitModuleFromLib(const char *modulesDir, const char *fileName)
LOG((" func=%p\n", (void*) func));
if (func) {
ipcModuleEntry *entries = NULL;
const ipcModuleEntry *entries = NULL;
int count = func(&gDaemonMethods, &entries);
for (int i=0; i<count; ++i) {
AddModule(entries[i].target, entries[i].methods, PR_LoadLibrary(buf));
if (entries[i].methods->init)
entries[i].methods->init();
if (AddModule(entries[i].target, entries[i].methods, buf)) {
if (entries[i].methods->init)
entries[i].methods->init();
}
}
}
PR_UnloadLibrary(lib);
@ -156,7 +173,7 @@ IPC_InitModuleReg(const char *exePath)
//
// register plug-in modules
//
static const char relModDir[] = "ipc/modules";
static const char relModDir[] = "ipc/modules"; // XXX fix slashes
char *p = PL_strrchr(exePath, IPC_PATH_SEP_CHAR);
if (p == NULL) {
@ -190,6 +207,8 @@ IPC_InitModuleReg(const char *exePath)
}
PR_CloseDir(dir);
}
free(modulesDir);
}
void
@ -198,15 +217,13 @@ IPC_ShutdownModuleReg()
//
// shutdown modules in reverse order
//
for (int i = ipcModuleCount - 1; i >= 0; --i) {
ipcModuleRegEntry &entry = ipcModules[i];
while (ipcModuleCount) {
ipcModuleRegEntry &entry = ipcModules[--ipcModuleCount];
if (entry.methods->shutdown)
entry.methods->shutdown();
if (entry.lib)
PR_UnloadLibrary(entry.lib);
}
// memset(ipcModules, 0, sizeof(ipcModules));
ipcModuleCount = 0;
}
void

Просмотреть файл

@ -41,7 +41,8 @@
#include "ipcModule.h"
//
// called to init the module registry.
// called to init the module registry. this may only be called once at
// startup or once after calling IPC_ShutdownModuleReg.
//
// params:
// exePath - path to the daemon executable. modules are loaded from a
@ -50,7 +51,8 @@
void IPC_InitModuleReg(const char *exePath);
//
// called to shutdown the module registry.
// called to shutdown the module registry. this may be called more than
// once and need not follow a call to IPC_InitModuleReg.
//
void IPC_ShutdownModuleReg();
@ -59,6 +61,8 @@ void IPC_ShutdownModuleReg();
//
ipcModuleMethods *IPC_GetModuleByTarget(const nsID &target);
// XXX "handle msg for target" instead
//
// notifies all modules of client connect/disconnect
//

Просмотреть файл

@ -41,10 +41,12 @@
#include "prlog.h"
#include "ipcModule.h"
extern ipcDaemonMethods *gIPCDaemonMethods;
extern const ipcDaemonMethods *gIPCDaemonMethods;
//-----------------------------------------------------------------------------
// inline wrapper functions
//
// XXX only usable inside module.. blah
//-----------------------------------------------------------------------------
inline PRStatus
@ -141,13 +143,14 @@ IPC_SendMsg(PRUint32 clientID, const nsID &target, const void *data, PRUint32 da
// module factory macros
//-----------------------------------------------------------------------------
#define IPC_IMPL_GETMODULES(_modName, _modEntries) \
ipcDaemonMethods *gIPCDaemonMethods; \
IPC_EXPORT int \
IPC_GetModules(ipcDaemonMethods *dmeths, ipcModuleEntry **ents) { \
gIPCDaemonMethods = dmeths; \
*ents = _modEntries; \
return sizeof(_modEntries) / sizeof(ipcModuleEntry); \
#define IPC_IMPL_GETMODULES(_modName, _modEntries) \
const ipcDaemonMethods *gIPCDaemonMethods; \
IPC_EXPORT int \
IPC_GetModules(const ipcDaemonMethods *dmeths, const ipcModuleEntry **ents) { \
/* XXX do version checking */ \
gIPCDaemonMethods = dmeths; \
*ents = _modEntries; \
return sizeof(_modEntries) / sizeof(ipcModuleEntry); \
}
#endif // !ipcModuleUtil_h__

Просмотреть файл

@ -48,6 +48,8 @@
PRStatus
IPC_DispatchMsg(ipcClient *client, const ipcMessage *msg)
{
// XXX assert args valid
//
if (msg->Target().Equals(IPCM_TARGET)) {
IPCM_HandleMsg(client, msg);
return PR_SUCCESS;
@ -84,8 +86,10 @@ PRStatus
IPC_DispatchMsg(ipcClient *client, const nsID &target, const void *data, PRUint32 dataLen)
{
// lookup handler for this message's topic and forward message to it.
// XXX methods should be |const|
ipcModuleMethods *methods = IPC_GetModuleByTarget(target);
if (methods) {
// XXX make sure handleMsg not null
methods->handleMsg(client, target, data, dataLen);
return PR_SUCCESS;
}
@ -124,6 +128,8 @@ IPC_GetClientByName(const char *name)
void
IPC_EnumClients(ipcClientEnumFunc func, void *closure)
{
// XXX null check
// XXX check return val for STOP
for (int i = 0; i < ipcClientCount; ++i)
func(closure, &ipcClients[i], ipcClients[i].ID());
}
@ -131,24 +137,29 @@ IPC_EnumClients(ipcClientEnumFunc func, void *closure)
PRUint32
IPC_GetClientID(ipcClient *client)
{
// XXX null check
return client->ID();
}
const char *
IPC_GetPrimaryClientName(ipcClient *client)
{
// XXX null check
// XXX eliminate
return client->PrimaryName();
}
PRBool
IPC_ClientHasName(ipcClient *client, const char *name)
{
// XXX null check
return client->HasName(name);
}
PRBool
IPC_ClientHasTarget(ipcClient *client, const nsID &target)
{
// XXX null check
return client->HasTarget(target);
}
@ -174,6 +185,7 @@ IPC_EnumClientTargets(ipcClient *client, ipcClientTargetEnumFunc func, void *clo
}
}
// XXX remove
ipcClient *
IPC_GetClients(PRUint32 *count)
{

Просмотреть файл

@ -63,26 +63,29 @@
// ipc directory and locking...
//-----------------------------------------------------------------------------
static char *ipcDir;
static char *ipcSockFile;
static char *ipcLockFile;
static int ipcLockFD;
static int ipcLockFD = 0;
static PRBool AcquireDaemonLock()
static PRBool AcquireDaemonLock(const char *baseDir)
{
const char lockName[] = "lock";
int dirLen = strlen(ipcDir);
int len = dirLen // ipcDir
int dirLen = strlen(baseDir);
int len = dirLen // baseDir
+ 1 // "/"
+ sizeof(lockName); // "lock"
ipcLockFile = (char *) malloc(len);
memcpy(ipcLockFile, ipcDir, dirLen);
ipcLockFile[dirLen] = '/';
memcpy(ipcLockFile + dirLen + 1, lockName, sizeof(lockName));
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);
ipcLockFD = open(ipcLockFile, O_WRONLY|O_CREAT|O_TRUNC, S_IWUSR|S_IRUSR);
if (ipcLockFD == -1)
return PR_FALSE;
@ -100,11 +103,16 @@ static PRBool AcquireDaemonLock()
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[256];
char buf[32];
int nb = PR_snprintf(buf, sizeof(buf), "%u\n", (unsigned long) getpid());
write(ipcLockFD, buf, nb);
@ -115,63 +123,45 @@ static PRBool InitDaemonDir(const char *socketPath)
{
LOG(("InitDaemonDir [sock=%s]\n", socketPath));
ipcSockFile = PL_strdup(socketPath);
ipcDir = PL_strdup(socketPath);
char *baseDir = PL_strdup(socketPath);
//
// make sure IPC directory exists (XXX this should be recursive)
//
char *p = strrchr(ipcDir, '/');
char *p = strrchr(baseDir, '/');
if (p)
p[0] = '\0';
mkdir(ipcDir, 0700);
mkdir(baseDir, 0700);
//
// if we can't acquire the daemon lock, then another daemon
// must be active, so bail.
//
if (!AcquireDaemonLock())
return PR_FALSE;
PRBool haveLock = AcquireDaemonLock(baseDir);
//
// delete an existing socket to prevent bind from failing.
//
unlink(socketPath);
PL_strfree(baseDir);
return PR_TRUE;
if (haveLock) {
// delete an existing socket to prevent bind from failing.
unlink(socketPath);
}
return haveLock;
}
static void ShutdownDaemonDir()
{
LOG(("ShutdownDaemonDir [sock=%s]\n", ipcSockFile));
LOG(("ShutdownDaemonDir\n"));
//
// unlink files from directory before giving up lock; otherwise, we'd be
// unable to delete it w/o introducing a race condition.
//
unlink(ipcLockFile);
unlink(ipcSockFile);
// 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.
//
// should be able to remove the ipc directory now.
//
rmdir(ipcDir);
//
// this removes the advisory lock, allowing other processes to acquire it.
//
close(ipcLockFD);
//
// free allocated memory
//
PL_strfree(ipcDir);
PL_strfree(ipcSockFile);
free(ipcLockFile);
ipcDir = NULL;
ipcSockFile = NULL;
ipcLockFile = NULL;
if (ipcLockFD) {
close(ipcLockFD);
ipcLockFD = 0;
}
}
//-----------------------------------------------------------------------------
@ -186,7 +176,7 @@ int ipcClientCount;
//
// the first element of this array is always zero; this is done so that the
// n'th element of ipcClientArray corresponds to the n'th element of
// k'th element of ipcClientArray corresponds to the k'th element of
// ipcPollList.
//
static ipcClient ipcClientArray[IPC_MAX_CLIENTS + 1];
@ -250,6 +240,7 @@ static int RemoveClient(int clientIndex)
static void PollLoop(PRFileDesc *listenFD)
{
// the first element of ipcClientArray is unused.
ipcClients = ipcClientArray + 1;
ipcClientCount = 0;
@ -312,18 +303,19 @@ static void PollLoop(PRFileDesc *listenFD)
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()));
return;
}
else {
// make socket non-blocking
PRSocketOptionData opt;
opt.option = PR_SockOpt_Nonblocking;
opt.value.non_blocking = PR_TRUE;
PR_SetSocketOption(clientFD, &opt);
// 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);
if (AddClient(clientFD) != 0)
PR_Close(clientFD);
}
}
}
@ -363,7 +355,7 @@ IPC_PlatformSendMsg(ipcClient *client, ipcMessage *msg)
int main(int argc, char **argv)
{
PRFileDesc *listenFD;
PRFileDesc *listenFD = NULL;
PRNetAddr addr;
//
@ -371,6 +363,7 @@ int main(int argc, char **argv)
// which spawned this daemon.
//
signal(SIGINT, SIG_IGN);
// XXX block others? check cartman
// ensure strict file permissions
umask(0077);
@ -383,12 +376,6 @@ int main(int argc, char **argv)
//LOG(("sleeping for 2 seconds...\n"));
//PR_Sleep(PR_SecondsToInterval(2));
listenFD = PR_OpenTCPSocket(PR_AF_LOCAL);
if (!listenFD) {
LOG(("PR_OpenUDPSocket failed [%d]\n", PR_GetError()));
return -1;
}
const char *socket_path;
if (argc < 2)
socket_path = IPC_DEFAULT_SOCKET_PATH;
@ -396,8 +383,14 @@ int main(int argc, char **argv)
socket_path = argv[1];
if (!InitDaemonDir(socket_path)) {
PR_Close(listenFD);
return 0;
LOG(("InitDaemonDir failed\n"));
goto end;
}
listenFD = PR_OpenTCPSocket(PR_AF_LOCAL);
if (!listenFD) {
LOG(("PR_OpenUDPSocket failed [%d]\n", PR_GetError()));
goto end;
}
addr.local.family = PR_AF_LOCAL;
@ -405,23 +398,30 @@ int main(int argc, char **argv)
if (PR_Bind(listenFD, &addr) != PR_SUCCESS) {
LOG(("PR_Bind failed [%d]\n", PR_GetError()));
return -1;
goto end;
}
IPC_InitModuleReg(argv[0]);
if (PR_Listen(listenFD, 5) != PR_SUCCESS) {
LOG(("PR_Listen failed [%d]\n", PR_GetError()));
return -1;
goto end;
}
PollLoop(listenFD);
end:
IPC_ShutdownModuleReg();
// 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();
LOG(("closing socket\n"));
PR_Close(listenFD);
if (listenFD) {
LOG(("closing socket\n"));
PR_Close(listenFD);
}
return 0;
}

Просмотреть файл

@ -71,7 +71,7 @@ RemoveClient(ipcClient *client)
int clientIndex = client - ipcClientArray;
client->Finalize();
client->Finalize(); // XXX pick another name..
//
// move last ipcClient object down into the spot occupied by this client.
@ -113,6 +113,8 @@ PurgeStaleClients()
IPC_GetClientWindowName(client->PID(), wName);
// XXX dougt has ideas about how to make this better
HWND hwnd = FindWindow(IPC_CLIENT_WINDOW_CLASS, wName);
if (!hwnd) {
LOG((" client window not found; removing client!\n"));
@ -140,7 +142,7 @@ AddClient(HWND hwnd, PRUint32 pid)
ipcClient *client = &ipcClientArray[ipcClientCount];
client->Init();
client->SetHwnd(hwnd);
client->SetPID(pid);
client->SetPID(pid); // XXX one funhction instead of 3
++ipcClientCount;
LOG((" num clients = %u\n", ipcClientCount));
@ -185,6 +187,7 @@ ProcessMsg(HWND hwnd, PRUint32 pid, const ipcMessage *msg)
else
client = AddClient(hwnd, pid);
// XXX add logging
if (client == NULL)
return;
@ -227,6 +230,7 @@ WindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
ipcMessage msg;
PRUint32 bytesRead;
PRBool complete;
// XXX avoid extra malloc
PRStatus rv = msg.ReadFrom((const char *) cd->lpData, cd->cbData,
&bytesRead, &complete);
if (rv == PR_SUCCESS && complete) {
@ -241,6 +245,7 @@ WindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
return TRUE;
}
// XXX don't check wParam
if (uMsg == WM_TIMER && wParam == IPC_PURGE_TIMER_ID) {
PurgeStaleClients();
return 0;
@ -263,7 +268,7 @@ WindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
}
if (uMsg == IPC_WM_SHUTDOWN) {
DestroyWindow(hWnd);
DestroyWindow(hWnd); // XXX possibly move out... think about shutdown sync
ipcHwnd = NULL;
PostQuitMessage(0);
return 0;
@ -288,12 +293,10 @@ AcquireLock()
return PR_FALSE;
}
if (ipcSyncEvent) {
// check to see if event already existed prior to this call.
if (GetLastError() == ERROR_ALREADY_EXISTS) {
LOG((" lock already set; exiting...\n"));
return PR_FALSE;
}
// check to see if event already existed prior to this call.
if (GetLastError() == ERROR_ALREADY_EXISTS) {
LOG((" lock already set; exiting...\n"));
return PR_FALSE;
}
LOG((" acquired lock\n"));
@ -323,6 +326,7 @@ main(int argc, char **argv)
if (!AcquireLock())
return 0;
// XXX add comments
ipcClients = ipcClientArray;
ipcClientCount = 0;

Просмотреть файл

@ -1,253 +0,0 @@
/* ***** 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 Mozilla IPC.
*
* The Initial Developer of the Original Code is
* Netscape Communications Corporation.
* Portions created by the Initial Developer are Copyright (C) 2002
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Darin Fisher <darin@netscape.com>
*
* 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 ***** */
#include "nsISupports.idl"
interface ipcIMessageObserver;
interface ipcIClientObserver;
interface ipcIClientQueryHandler;
/**
* ipcIService
*
* the IPC service provides the means to communicate with an external IPC
* daemon and/or other mozilla-based applications on the same physical system.
* the IPC daemon hosts modules (some builtin and others dynamically loaded)
* with which applications may interact.
*
* at application startup, the IPC service will attempt to establish a
* connection with the IPC daemon. the IPC daemon will be automatically
* started if necessary. when a connection has been established, the IPC
* service will enumerate the "ipc-startup-category" and broadcast an
* "ipc-startup" notification using the observer service.
*
* when the connection to the IPC daemon is closed, an "ipc-shutdown"
* notification will be broadcast.
*
* each client has a name. the client name need not be unique across all
* clients, but it is usually good if it is. the IPC service does not require
* unique names. instead, the IPC daemon assigns each client a unique ID that
* is good for the current "session." clients can query other clients by name
* or by ID. the IPC service supports forwarding messages from one client to
* another via the IPC daemon.
*
* for performance reasons, this system should not be used to transfer large
* amounts of data. instead, applications may choose to utilize shared memory,
* and rely on the IPC service for synchronization and small message transfer
* only.
*/
[scriptable, uuid(53d3e3a7-528f-4b09-9eab-9416272568c0)]
interface ipcIService : nsISupports
{
/**
* returns the "client ID" assigned to this process by the IPC daemon.
*
* @throws NS_ERROR_NOT_AVAILABLE if no connection to the IPC daemon.
*/
readonly attribute unsigned long clientID;
/**
* this process can appear under several client names. use the following
* methods to add or remove names for this process.
*
* for example, the mozilla browser might have the primary name "mozilla",
* but it could also register itself under the names "browser", "mail",
* "news", "addrbook", etc. other IPC clients can then query the IPC
* daemon for the client named "mail" in order to talk with a mail program.
*
* An IPC client name resembles a XPCOM contract ID.
*/
void addClientName(in ACString aName);
void removeClientName(in ACString aName);
/**
* query info about a particular client given its client name. the
* observer's onClientInfo method is called with the result of the lookup,
* or if there is no client matching the given name, the observer's
* onClientDown method will be called instead.
*
* @param aName
* the name of the client being queried.
* @param aHandler
* the handler to be notified asynchronously of result.
*
* @return integer value identifying this query.
*/
unsigned long queryClientByName(in ACString aName,
in ipcIClientQueryHandler aHandler);
/**
* query info about a particular client given its client ID. the observer's
* onClientInfo method is called with the result of the lookup, or if there
* is no client matching the given name, the observer's onClientDown method
* will be called instead.
*
* @param aClientID
* the ID of the client being queried.
* @param aHandler
* the handler to be notified asynchronously of result.
*
* @return integer value identifying this query.
*/
unsigned long queryClientByID(in unsigned long aClientID,
in ipcIClientQueryHandler aHandler);
/**
* called to cancel a pending query.
*
* @param aQueryID
* the return value from one of the "query" methods.
*/
void cancelQuery(in unsigned long aQueryID);
/**
* set client observer. observer's onClientUp method is called whenever
* a new client comes online, and the observer's onClientDown method is
* called whenever a client goes offline.
*
* @param aObserver
* the client observer.
*/
void setClientObserver(in ipcIClientObserver aObserver);
/**
* set a message observer for a particular message target.
*
* @param aTarget
* the message target being observed. any existing observer will
* be replaced.
* @param aObserver
* the message observer to receive incoming messages for the
* specified target. pass null to remove the existing observer.
*/
void setMessageObserver(in nsIDRef aTarget, in ipcIMessageObserver aObserver);
/**
* send message asynchronously to a client or a module in the IPC daemon.
* there is no guarantee that the message will be delivered.
*
* @param aClientID
* the client ID of the foreign application that should receive this
* message. pass 0 to send a message to a module in the IPC daemon.
* @param aTarget
* the target of the message. if aClientID is 0, then this is the
* ID of the daemon module that should receive this message.
* @param aData
* the message data.
* @param aDataLen
* the message length.
*/
void sendMessage(in unsigned long aClientID,
in nsIDRef aTarget,
[array, const, size_is(aDataLen)]
in octet aData,
in unsigned long aDataLen);
};
/**
* ipcIMessageObserver
*/
[scriptable, uuid(e40a4a3c-2dc1-470e-ab7f-5675fe1f1384)]
interface ipcIMessageObserver : nsISupports
{
/**
* @param aTarget
* the target of the message, corresponding to the target this
* observer was registered under. this parameter is passed to allow
* an observer instance to receive messages for more than one target.
* @param aData
* the data of the message.
* @param aDataLen
* the data length of the message.
*/
void onMessageAvailable(in nsIDRef aTarget,
[array, const, size_is(aDataLen)]
in octet aData,
in unsigned long aDataLen);
};
/**
* ipcIClientObserver
*/
[scriptable, uuid(42283079-030c-4b13-b069-a08b7ad5eab2)]
interface ipcIClientObserver : nsISupports
{
const unsigned long CLIENT_UP = 1;
const unsigned long CLIENT_DOWN = 2;
void onClientStatus(in unsigned long aClientID,
in unsigned long aClientStatus);
};
/**
* ipcIClientQueryHandler
*
* the methods on this interface are called when the result of a client query
* becomes available.
*/
[scriptable, uuid(6fefea5c-f747-4bb0-972f-2a7b363a01db)]
interface ipcIClientQueryHandler : nsISupports
{
/**
* called on successful completion of a client query.
*
* @param aQueryID
* the return value from one of the "query" methods.
* @param aClientID
*
* ...
*/
void onQueryComplete(in unsigned long aQueryID,
in unsigned long aClientID,
[array, size_is(aNameCount)]
in string aClientNames,
in unsigned long aNameCount,
[array, const, size_is(aTargetCount)]
in nsIDPtr aClientTargets,
in unsigned long aTargetCount);
void onQueryFailed(in unsigned long aQueryID,
in nsresult aReason);
};
%{C++
#define IPC_SERVICE_STARTUP_CATEGORY "ipc-startup-category"
#define IPC_SERVICE_STARTUP_TOPIC "ipc-startup"
#define IPC_SERVICE_SHUTDOWN_TOPIC "ipc-shutdown"
#define IPC_SERVICE_PREF_PRIMARY_CLIENT_NAME "ipc.primary-client-name"
%}

Просмотреть файл

@ -43,6 +43,7 @@
#include "nsIObserverService.h"
#include "nsICategoryManager.h"
#include "nsCategoryManagerUtils.h"
#include "netCore.h"
#include "ipcConfig.h"
#include "ipcLog.h"
@ -79,12 +80,10 @@ public:
static PRUint32 gLastQueryID;
void SetClientID(PRUint32 cID) { mClientID = cID; }
void OnQueryComplete(const ipcmMessageClientInfo *msg);
void OnQueryFailed(nsresult reason);
void OnQueryComplete(nsresult status, const ipcmMessageClientInfo *msg);
PRUint32 QueryID() { return mQueryID; }
PRBool IsCanceled() { return mHandler == nsnull; }
PRBool IsCanceled() { return mHandler == NULL; }
ipcClientQuery *mNext;
private:
@ -96,44 +95,46 @@ private:
PRUint32 ipcClientQuery::gLastQueryID = 0;
void
ipcClientQuery::OnQueryComplete(const ipcmMessageClientInfo *msg)
ipcClientQuery::OnQueryComplete(nsresult status, const ipcmMessageClientInfo *msg)
{
NS_ASSERTION(mHandler, "no handler");
PRUint32 nameCount = msg->NameCount();
PRUint32 targetCount = msg->TargetCount();
PRUint32 i;
PRUint32 nameCount = 0;
PRUint32 targetCount = 0;
const char **names = NULL;
const nsID **targets = NULL;
const char **names = (const char **) malloc(nameCount * sizeof(char *));
const char *lastName = NULL;
for (i = 0; i < nameCount; ++i) {
lastName = msg->NextName(lastName);
names[i] = lastName;
}
if (NS_SUCCEEDED(status)) {
nameCount = msg->NameCount();
targetCount = msg->TargetCount();
PRUint32 i;
const nsID **targets = (const nsID **) malloc(targetCount * sizeof(nsID *));
const nsID *lastTarget = NULL;
for (i = 0; i < targetCount; ++i) {
lastTarget = msg->NextTarget(lastTarget);
targets[i] = lastTarget;
names = (const char **) malloc(nameCount * sizeof(char *));
const char *lastName = NULL;
for (i = 0; i < nameCount; ++i) {
lastName = msg->NextName(lastName);
names[i] = lastName;
}
targets = (const nsID **) malloc(targetCount * sizeof(nsID *));
const nsID *lastTarget = NULL;
for (i = 0; i < targetCount; ++i) {
lastTarget = msg->NextTarget(lastTarget);
targets[i] = lastTarget;
}
}
mHandler->OnQueryComplete(mQueryID,
status,
mClientID,
names, nameCount,
targets, targetCount);
mHandler = NULL;
free(names);
free(targets);
}
void
ipcClientQuery::OnQueryFailed(nsresult status)
{
NS_ASSERTION(mHandler, "no handler");
mHandler->OnQueryFailed(mQueryID, status);
mHandler = nsnull;
if (names)
free(names);
if (targets)
free(targets);
}
//-----------------------------------------------------------------------------
@ -213,7 +214,7 @@ ipcService::OnIPCMClientInfo(const ipcmMessageClientInfo *msg)
}
if (!query->IsCanceled())
query->OnQueryComplete(msg);
query->OnQueryComplete(NS_OK, msg);
mQueryQ.DeleteFirst();
}
@ -230,7 +231,7 @@ ipcService::OnIPCMError(const ipcmMessageError *msg)
}
if (!query->IsCanceled())
query->OnQueryFailed(NS_ERROR_FAILURE);
query->OnQueryComplete(NS_ERROR_FAILURE, NULL);
mQueryQ.DeleteFirst();
}
@ -332,7 +333,7 @@ ipcService::CancelQuery(PRUint32 queryID)
ipcClientQuery *query = mQueryQ.First();
while (query) {
if (query->QueryID() == queryID) {
query->OnQueryFailed(NS_ERROR_ABORT);
query->OnQueryComplete(NS_BINDING_ABORTED, NULL);
break;
}
query = query->mNext;
@ -432,7 +433,7 @@ ipcService::OnConnectionLost()
//
while (mQueryQ.First()) {
ipcClientQuery *query = mQueryQ.First();
query->OnQueryFailed(NS_ERROR_ABORT);
query->OnQueryComplete(NS_BINDING_ABORTED, NULL);
mQueryQ.DeleteFirst();
}

Просмотреть файл

@ -1,287 +0,0 @@
/* ***** 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 Mozilla IPC.
*
* The Initial Developer of the Original Code is
* Netscape Communications Corporation.
* Portions created by the Initial Developer are Copyright (C) 2002
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Darin Fisher <darin@netscape.com>
*
* 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 ***** */
#include "ipcIService.h"
#include "nsIEventQueueService.h"
#include "nsIServiceManager.h"
#include "nsIComponentRegistrar.h"
#include "nsString.h"
#include "prmem.h"
static const nsID kIPCMTargetID =
{ /* 753ca8ff-c8c2-4601-b115-8c2944da1150 */
0x753ca8ff,
0xc8c2,
0x4601,
{0xb1, 0x15, 0x8c, 0x29, 0x44, 0xda, 0x11, 0x50}
};
static const nsID kTestTargetID =
{ /* e628fc6e-a6a7-48c7-adba-f241d1128fb8 */
0xe628fc6e,
0xa6a7,
0x48c7,
{0xad, 0xba, 0xf2, 0x41, 0xd1, 0x12, 0x8f, 0xb8}
};
#define RETURN_IF_FAILED(rv, step) \
PR_BEGIN_MACRO \
if (NS_FAILED(rv)) { \
printf("*** %s failed: rv=%x\n", step, rv); \
return rv;\
} \
PR_END_MACRO
static NS_DEFINE_CID(kEventQueueServiceCID, NS_EVENTQUEUESERVICE_CID);
static nsIEventQueue* gEventQ = nsnull;
static PRBool gKeepRunning = PR_TRUE;
//static PRInt32 gMsgCount = 0;
static ipcIService *gIpcServ = nsnull;
static void
SendMsg(ipcIService *ipc, PRUint32 cID, const nsID &target, const char *data, PRUint32 dataLen)
{
printf("*** sending message: [to-client=%u dataLen=%u]\n", cID, dataLen);
ipc->SendMessage(cID, target, (const PRUint8 *) data, dataLen);
// gMsgCount++;
}
//-----------------------------------------------------------------------------
class myIpcMessageObserver : public ipcIMessageObserver
{
public:
NS_DECL_ISUPPORTS
NS_DECL_IPCIMESSAGEOBSERVER
myIpcMessageObserver() { NS_INIT_ISUPPORTS(); }
};
NS_IMPL_ISUPPORTS1(myIpcMessageObserver, ipcIMessageObserver)
NS_IMETHODIMP
myIpcMessageObserver::OnMessageAvailable(const nsID &target, const PRUint8 *data, PRUint32 dataLen)
{
printf("*** got message: [%s]\n", (const char *) data);
// if (--gMsgCount == 0)
// gKeepRunning = PR_FALSE;
return NS_OK;
}
//-----------------------------------------------------------------------------
class myIpcClientQueryHandler : public ipcIClientQueryHandler
{
public:
NS_DECL_ISUPPORTS
NS_DECL_IPCICLIENTQUERYHANDLER
myIpcClientQueryHandler() { NS_INIT_ISUPPORTS(); }
};
NS_IMPL_ISUPPORTS1(myIpcClientQueryHandler, ipcIClientQueryHandler)
NS_IMETHODIMP
myIpcClientQueryHandler::OnQueryFailed(PRUint32 aQueryID, nsresult aReason)
{
printf("*** query failed [queryID=%u reason=0x%08x]\n", aQueryID, aReason);
return NS_OK;
}
NS_IMETHODIMP
myIpcClientQueryHandler::OnQueryComplete(PRUint32 aQueryID,
PRUint32 aClientID,
const char **aNames,
PRUint32 aNameCount,
const nsID **aTargets,
PRUint32 aTargetCount)
{
printf("*** query complete [queryID=%u clientID=%u]\n", aQueryID, aClientID);
PRUint32 i;
printf("*** names:\n");
for (i = 0; i < aNameCount; ++i)
printf("*** %d={%s}\n", i, aNames[i]);
printf("*** targets:\n");
for (i = 0; i < aTargetCount; ++i) {
const char *trailer;
if (aTargets[i]->Equals(kTestTargetID))
trailer = " (TEST_TARGET_ID)";
else if (aTargets[i]->Equals(kIPCMTargetID))
trailer = " (IPCM_TARGET_ID)";
else
trailer = " (unknown)";
char *str = aTargets[i]->ToString();
printf("*** %d=%s%s\n", i, str, trailer);
PR_Free(str);
}
if (aClientID != 0) {
const char hello[] = "hello friend!";
SendMsg(gIpcServ, aClientID, kTestTargetID, hello, sizeof(hello));
}
return NS_OK;
}
//-----------------------------------------------------------------------------
int main(int argc, char **argv)
{
nsresult rv;
{
nsCOMPtr<nsIServiceManager> servMan;
NS_InitXPCOM2(getter_AddRefs(servMan), nsnull, nsnull);
nsCOMPtr<nsIComponentRegistrar> registrar = do_QueryInterface(servMan);
NS_ASSERTION(registrar, "Null nsIComponentRegistrar");
if (registrar)
registrar->AutoRegister(nsnull);
// Create the Event Queue for this thread...
nsCOMPtr<nsIEventQueueService> eqs =
do_GetService(kEventQueueServiceCID, &rv);
RETURN_IF_FAILED(rv, "do_GetService(EventQueueService)");
rv = eqs->CreateMonitoredThreadEventQueue();
RETURN_IF_FAILED(rv, "CreateMonitoredThreadEventQueue");
rv = eqs->GetThreadEventQueue(NS_CURRENT_THREAD, &gEventQ);
RETURN_IF_FAILED(rv, "GetThreadEventQueue");
nsCOMPtr<ipcIService> ipcServ(do_GetService("@mozilla.org/ipc/service;1", &rv));
RETURN_IF_FAILED(rv, "do_GetService(ipcServ)");
NS_ADDREF(gIpcServ = ipcServ);
if (argc > 1) {
printf("*** using client name [%s]\n", argv[1]);
gIpcServ->AddClientName(nsDependentCString(argv[1]));
}
ipcServ->SetMessageObserver(kTestTargetID, new myIpcMessageObserver());
const char *data =
"01 this is a really long message.\n"
"02 this is a really long message.\n"
"03 this is a really long message.\n"
"04 this is a really long message.\n"
"05 this is a really long message.\n"
"06 this is a really long message.\n"
"07 this is a really long message.\n"
"08 this is a really long message.\n"
"09 this is a really long message.\n"
"10 this is a really long message.\n"
"11 this is a really long message.\n"
"12 this is a really long message.\n"
"13 this is a really long message.\n"
"14 this is a really long message.\n"
"15 this is a really long message.\n"
"16 this is a really long message.\n"
"17 this is a really long message.\n"
"18 this is a really long message.\n"
"19 this is a really long message.\n"
"20 this is a really long message.\n"
"21 this is a really long message.\n"
"22 this is a really long message.\n"
"23 this is a really long message.\n"
"24 this is a really long message.\n"
"25 this is a really long message.\n"
"26 this is a really long message.\n"
"27 this is a really long message.\n"
"28 this is a really long message.\n"
"29 this is a really long message.\n"
"30 this is a really long message.\n"
"31 this is a really long message.\n"
"32 this is a really long message.\n"
"33 this is a really long message.\n"
"34 this is a really long message.\n"
"35 this is a really long message.\n"
"36 this is a really long message.\n"
"37 this is a really long message.\n"
"38 this is a really long message.\n"
"39 this is a really long message.\n"
"40 this is a really long message.\n"
"41 this is a really long message.\n"
"42 this is a really long message.\n"
"43 this is a really long message.\n"
"44 this is a really long message.\n"
"45 this is a really long message.\n"
"46 this is a really long message.\n"
"47 this is a really long message.\n"
"48 this is a really long message.\n"
"49 this is a really long message.\n"
"50 this is a really long message.\n"
"51 this is a really long message.\n"
"52 this is a really long message.\n"
"53 this is a really long message.\n"
"54 this is a really long message.\n"
"55 this is a really long message.\n"
"56 this is a really long message.\n"
"57 this is a really long message.\n"
"58 this is a really long message.\n"
"59 this is a really long message.\n"
"60 this is a really long message.\n";
SendMsg(ipcServ, 0, kTestTargetID, data, strlen(data)+1);
PRUint32 queryID;
nsCOMPtr<ipcIClientQueryHandler> handler(new myIpcClientQueryHandler());
ipcServ->QueryClientByName(NS_LITERAL_CSTRING("foopy"), handler, &queryID);
while (gKeepRunning)
gEventQ->ProcessPendingEvents();
NS_RELEASE(gIpcServ);
printf("*** processing remaining events\n");
// process any remaining events
PLEvent *ev;
while (NS_SUCCEEDED(gEventQ->GetEvent(&ev)) && ev)
gEventQ->HandleEvent(ev);
printf("*** done\n");
} // this scopes the nsCOMPtrs
// no nsCOMPtrs are allowed to be alive when you call NS_ShutdownXPCOM
rv = NS_ShutdownXPCOM(nsnull);
NS_ASSERTION(NS_SUCCEEDED(rv), "NS_ShutdownXPCOM failed");
return 0;
}