diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 16:49:04 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 16:49:04 +0000 |
commit | 16f504a9dca3fe3b70568f67b7d41241ae485288 (patch) | |
tree | c60f36ada0496ba928b7161059ba5ab1ab224f9d /src/libs/xpcom18a4/ipc/ipcd | |
parent | Initial commit. (diff) | |
download | virtualbox-upstream.tar.xz virtualbox-upstream.zip |
Adding upstream version 7.0.6-dfsg.upstream/7.0.6-dfsgupstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/libs/xpcom18a4/ipc/ipcd')
140 files changed, 21568 insertions, 0 deletions
diff --git a/src/libs/xpcom18a4/ipc/ipcd/.cvsignore b/src/libs/xpcom18a4/ipc/ipcd/.cvsignore new file mode 100644 index 00000000..f3c7a7c5 --- /dev/null +++ b/src/libs/xpcom18a4/ipc/ipcd/.cvsignore @@ -0,0 +1 @@ +Makefile diff --git a/src/libs/xpcom18a4/ipc/ipcd/Makefile.in b/src/libs/xpcom18a4/ipc/ipcd/Makefile.in new file mode 100644 index 00000000..d5f24007 --- /dev/null +++ b/src/libs/xpcom18a4/ipc/ipcd/Makefile.in @@ -0,0 +1,64 @@ +# vim: noexpandtab ts=4 sw=4 +# ***** 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 = ipcd + +PACKAGE_FILE = ipc.pkg + +DIRS = \ + util \ + shared/src \ + daemon/public \ + client/public \ + extensions \ + client/src \ + daemon/src \ + $(NULL) + +ifdef ENABLE_TESTS +DIRS += test +endif + +include $(topsrcdir)/config/rules.mk diff --git a/src/libs/xpcom18a4/ipc/ipcd/Makefile.kup b/src/libs/xpcom18a4/ipc/ipcd/Makefile.kup new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/src/libs/xpcom18a4/ipc/ipcd/Makefile.kup diff --git a/src/libs/xpcom18a4/ipc/ipcd/client/Makefile.kup b/src/libs/xpcom18a4/ipc/ipcd/client/Makefile.kup new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/src/libs/xpcom18a4/ipc/ipcd/client/Makefile.kup diff --git a/src/libs/xpcom18a4/ipc/ipcd/client/public/.cvsignore b/src/libs/xpcom18a4/ipc/ipcd/client/public/.cvsignore new file mode 100644 index 00000000..f3c7a7c5 --- /dev/null +++ b/src/libs/xpcom18a4/ipc/ipcd/client/public/.cvsignore @@ -0,0 +1 @@ +Makefile diff --git a/src/libs/xpcom18a4/ipc/ipcd/client/public/Makefile.in b/src/libs/xpcom18a4/ipc/ipcd/client/public/Makefile.in new file mode 100644 index 00000000..896e68b8 --- /dev/null +++ b/src/libs/xpcom18a4/ipc/ipcd/client/public/Makefile.in @@ -0,0 +1,59 @@ +# vim: noexpandtab ts=4 sw=4 +# ***** 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 = ipcd + +EXPORTS = \ + ipcdclient.h \ + ipcCID.h \ + $(NULL) + +XPIDLSRCS = \ + ipcIService.idl \ + ipcIMessageObserver.idl \ + ipcIClientObserver.idl \ + $(NULL) + +include $(topsrcdir)/config/rules.mk diff --git a/src/libs/xpcom18a4/ipc/ipcd/client/public/ipcCID.h b/src/libs/xpcom18a4/ipc/ipcd/client/public/ipcCID.h new file mode 100644 index 00000000..4863ff0d --- /dev/null +++ b/src/libs/xpcom18a4/ipc/ipcd/client/public/ipcCID.h @@ -0,0 +1,53 @@ +/* ***** 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 ***** */ + +#ifndef ipcCID_h__ +#define ipcCID_h__ + +#define IPC_SERVICE_CLASSNAME \ + "ipcService" +#define IPC_SERVICE_CONTRACTID \ + "@mozilla.org/ipc/service;1" +#define IPC_SERVICE_CID \ +{ /* 9f12676a-5168-4a08-beb8-edf8a593a1ca */ \ + 0x9f12676a, \ + 0x5168, \ + 0x4a08, \ + {0xbe, 0xb8, 0xed, 0xf8, 0xa5, 0x93, 0xa1, 0xca} \ +} + +#endif // !ipcCID_h__ diff --git a/src/libs/xpcom18a4/ipc/ipcd/client/public/ipcIClientObserver.idl b/src/libs/xpcom18a4/ipc/ipcd/client/public/ipcIClientObserver.idl new file mode 100644 index 00000000..c72d0909 --- /dev/null +++ b/src/libs/xpcom18a4/ipc/ipcd/client/public/ipcIClientObserver.idl @@ -0,0 +1,51 @@ +/* ***** 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" + +/** + * ipcIClientObserver + */ +[scriptable, uuid(42283079-030c-4b13-b069-a08b7ad5eab2)] +interface ipcIClientObserver : nsISupports +{ + const unsigned long CLIENT_UP = 1; + const unsigned long CLIENT_DOWN = 2; + + void onClientStateChange(in unsigned long aClientID, + in unsigned long aClientState); +}; diff --git a/src/libs/xpcom18a4/ipc/ipcd/client/public/ipcIMessageObserver.idl b/src/libs/xpcom18a4/ipc/ipcd/client/public/ipcIMessageObserver.idl new file mode 100644 index 00000000..db24b1f2 --- /dev/null +++ b/src/libs/xpcom18a4/ipc/ipcd/client/public/ipcIMessageObserver.idl @@ -0,0 +1,64 @@ +/* ***** 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" + +/** + * ipcIMessageObserver + */ +[scriptable, uuid(e40a4a3c-2dc1-470e-ab7f-5675fe1f1384)] +interface ipcIMessageObserver : nsISupports +{ + /** + * @param aSenderID + * the client id of the sender of this message. if sent by the + * daemon (or a deamon module), then this will have a value of 0. + * @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 unsigned long aSenderID, + in nsIDRef aTarget, + [array, const, size_is(aDataLen)] + in octet aData, + in unsigned long aDataLen); +}; diff --git a/src/libs/xpcom18a4/ipc/ipcd/client/public/ipcIService.idl b/src/libs/xpcom18a4/ipc/ipcd/client/public/ipcIService.idl new file mode 100644 index 00000000..671b557d --- /dev/null +++ b/src/libs/xpcom18a4/ipc/ipcd/client/public/ipcIService.idl @@ -0,0 +1,228 @@ +/* ***** 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; + +/** + * 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 +{ + /************************************************************************** + * properties of this process + */ + + /** + * 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 ID; + + /** + * 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. + * + * XXX An IPC client name resembles a XPCOM contract ID. + */ + void addName(in string aName); + void removeName(in string aName); + + /** + * add a new observer of client status change notifications. + */ + void addClientObserver(in ipcIClientObserver aObserver); + + /** + * remove an observer of client status change notifications. + */ + void removeClientObserver(in ipcIClientObserver aObserver); + + /************************************************************************** + * client query methods + */ + + /** + * resolve the given client name to the id of a connected client. this + * involves a round trip to the daemon, and as a result the calling thread + * may block on this function call while waiting for the daemon to respond. + */ + unsigned long resolveClientName(in string aName); + + + /** + * tests whether a particular client is connected to the IPC daemon. + */ + boolean clientExists(in unsigned long aClientID); + + + // XXX need other functions to enumerate clients, clients implementing targets, etc. + // enumerator getClients(); + // enumerator getClientsSupportingTarget(in nsIDRef aTarget); + // enumerator getClientNames(in unsigned long aClientID); + // enumerator getClientTargets(in unsigned long aClientID); + + + /************************************************************************** + * message methods + */ + + /** + * 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. + * @param aOnCurrentThread + * if true, then the message observer will be called on the same + * thread that calls defineTarget. otherwise, aObserver will be + * called on a background thread. + */ + void defineTarget(in nsIDRef aTarget, + in ipcIMessageObserver aObserver, + in boolean aOnCurrentThread); + + /** + * 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 aReceiverID, + in nsIDRef aTarget, + [array, const, size_is(aDataLen)] + in octet aData, + in unsigned long aDataLen); + + /** + * block the calling thread until a matching message is received. + * + * @param aSenderID + * pass 0 to wait for a message from the daemon. pass PR_UINT32_MAX + * to wait for a message from any source. otherwise, pass a client + * id to wait for a message from that particular client. + * @param aTarget + * wait for a message to be delivered to this target. + * @param aObserver + * this observer's OnMessageAvailable method is called when a + * matching message is available. pass null to use the default + * observer associated with aTarget. + * @param aTimeout + * indicates maximum length of time in milliseconds that this + * function may block the calling thread. + * + * @throws IPC_ERROR_WOULD_BLOCK if the timeout expires. + * + * the observer's OnMessageAvailable method may throw IPC_WAIT_NEXT_MESSAGE + * to indicate that it does not wish to handle the message that it was + * given, and that it will wait to be called with the next message. this + * enables the observer to keep messages in the queue that do not match the + * desired message. messages that remain in the queue will be dispatched + * asynchronously to the default message handler after waitMessage finishes. + * + * NOTE: this function may hang the calling thread until a matching message + * is received, so use it with caution. + */ + void waitMessage(in unsigned long aSenderID, + in nsIDRef aTarget, + in ipcIMessageObserver aObserver, + in unsigned long aTimeout); + + /** + * Call this method to disable the default message observer for a target. + */ + void disableMessageObserver(in nsIDRef aTarget); + + /** + * Call this method to re-enable the default message observer for a target. + */ + void enableMessageObserver(in nsIDRef aTarget); +}; + +%{C++ +// category and observer event defines (XXX not yet implemented) +#define IPC_SERVICE_STARTUP_CATEGORY "ipc-startup-category" +#define IPC_SERVICE_STARTUP_TOPIC "ipc-startup" +#define IPC_SERVICE_SHUTDOWN_TOPIC "ipc-shutdown" +%} diff --git a/src/libs/xpcom18a4/ipc/ipcd/client/public/ipcdclient.h b/src/libs/xpcom18a4/ipc/ipcd/client/public/ipcdclient.h new file mode 100644 index 00000000..5cbed879 --- /dev/null +++ b/src/libs/xpcom18a4/ipc/ipcd/client/public/ipcdclient.h @@ -0,0 +1,326 @@ +/* ***** 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 IBM Corporation. + * Portions created by the Initial Developer are Copyright (C) 2004 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Darin Fisher <darin@meer.net> + * + * 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 ***** */ + +#ifndef ipcdclient_h__ +#define ipcdclient_h__ + +/***************************************************************************** + * This file provides a client-side API to the IPC daemon. + * + * This API can be used to communicate with other clients of the IPC daemon + * as well as modules running inside the IPC daemon. + * + * This API is meant to be used only on the application's main thread. It is + * assumed that callbacks can be dispatched via the main thread's event queue. + */ + +#include "nscore.h" +#include "nsID.h" +#include "nsError.h" +#include "ipcIMessageObserver.h" +#include "ipcIClientObserver.h" + +/* This API is only provided for the extensions compiled into the IPCDC + * library, hence this API is hidden in the final DSO. */ +#define IPC_METHOD NS_HIDDEN_(nsresult) + +/* This value can be used to represent the client id of any client connected + * to the IPC daemon. */ +#define IPC_SENDER_ANY PR_UINT32_MAX + +/* This error code can only be returned by OnMessageAvailable, when called by + * IPC_WaitMessage. See IPC_WaitMessage for a description of how this error + * code may be used. */ +#define IPC_WAIT_NEXT_MESSAGE \ + NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_GENERAL, 10) + +#ifdef VBOX +/* This error code can only be returned by the selector functions called by + * WaitTarget. The message should be dropped without processing. */ +#define IPC_DISCARD_MESSAGE \ + NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_GENERAL, 12) +#endif /* VBOX */ + +/* This error code is returned by IPC_WaitMessage under certain conditions. */ +#define IPC_ERROR_WOULD_BLOCK NS_BASE_STREAM_WOULD_BLOCK + +/***************************************************************************** + * Initialization and Shutdown + */ + +// XXX limit these to the main thread, and call them from our module's ctor/dtor? + +/** + * Connects this process to the IPC daemon and initializes it for use as a + * client of the IPC daemon. This function must be called once before any + * other methods defined in this file can be used. + * + * @returns NS_ERROR_ALREADY_INITIALIZED if IPC_Shutdown was not called since + * the last time IPC_Init was called. + */ +IPC_METHOD IPC_Init(); + +/** + * Disconnects this process from the IPC daemon. After this function is + * called, no other methods in this file except for IPC_Init may be called. + * + * This function calls all client observers (registered with + * IPC_AddClientObserver) before it actually disconnects from the IPC daemon, + * with aClientID parameter of the OnClientStateChange callback specially + * set to IPC_SENDER_ANY and aClientState set to CLIENT_DOWN. This gives + * the interested party an opportunity to perform a proper shutdown procedure. + * All IPC methods are still available inside OnClientStateChange call except + * the IPC_WaitMessage function, that will immediately fail with an error. + * OnClientStateChange is called on the same thread where this function is + * called. + * + * @returns NS_ERROR_NOT_INITIALIZED if IPC_Init has not been called or if + * IPC_Init did not return a success code. + */ +IPC_METHOD IPC_Shutdown(); + + +/***************************************************************************** + * The core messaging API + */ + +/** + * Call this method to define a message target. A message target is defined + * by a UUID and a message observer. This observer is notified asynchronously + * whenever a message is sent to this target in this process. + * + * This function has three main effects: + * o If the message target is already defined, then this function simply + * resets its message observer. + * o If the message target is not already defined, then the message target + * is defined and the IPC daemon is notified of the existance of this + * message target. + * o If null is passed for the message observer, then the message target is + * removed, and the daemon is notified of the removal of this message target. + * + * If aOnCurrentThread is true, then notifications to the observer will occur + * on the current thread. This means that there must be a nsIEventTarget + * associated with the calling thread. If aOnCurrentThread is false, then + * notifications to the observer will occur on a background thread. In which + * case, the observer must be threadsafe. + */ +IPC_METHOD IPC_DefineTarget( + const nsID &aTarget, + ipcIMessageObserver *aObserver, + PRBool aOnCurrentThread = PR_TRUE +); + +/** + * Call this method to temporarily disable the message observer configured + * for a message target. + */ +IPC_METHOD IPC_DisableMessageObserver( + const nsID &aTarget +); + +/** + * Call this method to re-enable the message observer configured for a + * message target. + */ +IPC_METHOD IPC_EnableMessageObserver( + const nsID &aTarget +); + +/** + * This function sends a message to the IPC daemon asynchronously. If + * aReceiverID is non-zero, then the message is forwarded to the client + * corresponding to that identifier. + * + * If there is no client corresponding to aRecieverID, then the IPC daemon will + * simply drop the message. + */ +IPC_METHOD IPC_SendMessage( + PRUint32 aReceiverID, + const nsID &aTarget, + const PRUint8 *aData, + PRUint32 aDataLen +); + +/** + * This function blocks the calling thread until a message for the given target + * is received (optionally from the specified client). + * + * The aSenderID parameter is interpreted as follows: + * o If aSenderID is 0, then this function waits for a message to be sent by + * the IPC daemon. + * o If aSenderID is IPC_SENDER_ANY, then this function waits for a message + * to be sent from any source. + * o Otherwise, this function waits for a message to be sent by the client + * with ID given by aSenderID. If aSenderID does not identify a valid + * client, or if the client dies while waiting, then this function will + * return an error. + * + * The aObserver parameter is interpreted as follows: + * o If aObserver is null, then the default message observer for the target + * is invoked when the next message is received. + * o Otherwise, aObserver will be inovked when the next message is received. + * + * The aConsumer parameter is interpreted as follows: + * o If aObserver is null or aConsumer is null, this parameter is ignored. + * o Otherwise, aConsumer will be invoked right after (and only if) aObserver + * has accepted some message. It will receive exactly the same message + * that was accepted by aObserver. + * + * The aTimeout parameter is interpreted as follows: + * o If aTimeout is PR_INTERVAL_NO_TIMEOUT, then this function will block + * until a matching message is received. + * o If aTimeout is PR_INTERVAL_NO_WAIT, then this function will only inspect + * the current queue of messages. If no matching message is found, then + * IPC_ERROR_WOULD_BLOCK is returned. + * o Otherwise, aTimeout specifies the maximum amount of time to wait for a + * matching message to be received. If no matching message is found after + * the timeout expires, then IPC_ERROR_WOULD_BLOCK is returned. + * + * If aObserver's OnMessageAvailable function returns IPC_WAIT_NEXT_MESSAGE, + * then the function will continue blocking until the next matching message + * is received. Bypassed messages will be dispatched to the default message + * observer when the event queue, associated with the thread that called + * IPC_DefineTarget, is processed. + * + * There is a special case if aSenderID is IPC_SENDER_ANY and one of IPC clients + * dies while this function waits for a message. In this case, aObserver's + * OnMessageAvailable function is called with a special set of arguments: + * dead client id, empty target id and null message (both data and data length + * are zeroes). If it returns IPC_WAIT_NEXT_MESSAGE, IPC_WaitMessage continues + * blocking as usual, otherwise an error is immediately returned to the caller. + * + * aObserver is not expected to use any IPC system functons from within its + * OnMessageAvailable implementation. This is because the IPC system is not + * re-entrant during invocation of OnMessageAvailable function, and making other + * IPC calls can lead to unexpected results (like message losses or traps). On + * the contrary, aConsumer's OnMessageAvailable function is allowed to use the + * IPC system without any limitations. + * + * This function runs the risk of hanging the calling thread indefinitely if + * no matching message is ever received. + */ +IPC_METHOD IPC_WaitMessage( + PRUint32 aSenderID, + const nsID &aTarget, + ipcIMessageObserver *aObserver = nsnull, + ipcIMessageObserver *aConsumer = nsnull, + PRIntervalTime aTimeout = PR_INTERVAL_NO_TIMEOUT +); + +/*****************************************************************************/ + +/** + * Returns the "ClientID" of the current process. + */ +IPC_METHOD IPC_GetID( + PRUint32 *aClientID +); + +/** + * Adds a new name for the current process. The IPC daemon is notified of this + * change, which allows other processes to discover this process by the given + * name. + */ +IPC_METHOD IPC_AddName( + const char *aName +); + +/** + * Removes a name associated with the current process. + */ +IPC_METHOD IPC_RemoveName( + const char *aName +); + +/** + * Adds client observer. Will be called on the main thread. + */ +IPC_METHOD IPC_AddClientObserver( + ipcIClientObserver *aObserver +); + +/** + * Removes client observer. + */ +IPC_METHOD IPC_RemoveClientObserver( + ipcIClientObserver *aObserver +); + +/** + * Resolves the given client name to a client ID of a process connected to + * the IPC daemon. + */ +IPC_METHOD IPC_ResolveClientName( + const char *aName, + PRUint32 *aClientID +); + +/** + * Tests whether the client is connected to the IPC daemon. + */ +IPC_METHOD IPC_ClientExists( + PRUint32 aClientID, + PRBool *aResult +); + +/*****************************************************************************/ + +/** + * This class can be used to temporarily disable the default message observer + * defined for a particular message target. + */ +class ipcDisableMessageObserverForScope +{ +public: + ipcDisableMessageObserverForScope(const nsID &aTarget) + : mTarget(aTarget) + { + IPC_DisableMessageObserver(mTarget); + } + + ~ipcDisableMessageObserverForScope() + { + IPC_EnableMessageObserver(mTarget); + } + +private: + const nsID &mTarget; +}; + +#define IPC_DISABLE_MESSAGE_OBSERVER_FOR_SCOPE(_t) \ + ipcDisableMessageObserverForScope ipc_dmo_for_scope##_t(_t) + +#endif /* ipcdclient_h__ */ diff --git a/src/libs/xpcom18a4/ipc/ipcd/client/src/.cvsignore b/src/libs/xpcom18a4/ipc/ipcd/client/src/.cvsignore new file mode 100644 index 00000000..f3c7a7c5 --- /dev/null +++ b/src/libs/xpcom18a4/ipc/ipcd/client/src/.cvsignore @@ -0,0 +1 @@ +Makefile diff --git a/src/libs/xpcom18a4/ipc/ipcd/client/src/Makefile.in b/src/libs/xpcom18a4/ipc/ipcd/client/src/Makefile.in new file mode 100644 index 00000000..b2854e35 --- /dev/null +++ b/src/libs/xpcom18a4/ipc/ipcd/client/src/Makefile.in @@ -0,0 +1,101 @@ +# vim: noexpandtab ts=4 sw=4 +# ***** 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 = ipcd +LIBRARY_NAME = ipcdc +EXPORT_LIBRARY = 1 +IS_COMPONENT = 1 +MODULE_NAME = ipcdclient + +REQUIRES = \ + xpcom \ + string \ + $(NULL) + +CPPSRCS = \ + ipcdclient.cpp \ + ipcService.cpp \ + ipcModuleFactory.cpp \ + $(NULL) + +ifeq ($(OS_ARCH),WINNT) +CPPSRCS += ipcConnectionWin.cpp +else +ifeq ($(OS_ARCH),BeOS) +CPPSRCS += ipcConnectionStub.cpp +else +CPPSRCS += ipcConnectionUnix.cpp +endif +endif + +LOCAL_INCLUDES = \ + -I$(srcdir)/../../shared/src \ + -I$(srcdir)/../../extensions/lock/src \ + -I$(srcdir)/../../extensions/transmngr/src \ + -I$(srcdir)/../../extensions/transmngr/common \ + $(NULL) + +SHARED_LIBRARY_LIBS = \ + $(DIST)/lib/$(LIB_PREFIX)ipcdshared_s.$(LIB_SUFFIX) \ + $(DIST)/lib/$(LIB_PREFIX)ipcdlock_s.$(LIB_SUFFIX) \ + $(DIST)/lib/$(LIB_PREFIX)transmgr_s.$(LIB_SUFFIX) \ + $(DIST)/lib/$(LIB_PREFIX)transmgrcom_s.$(LIB_SUFFIX) \ + $(NULL) + +ifneq ($(BUILD_DCONNECT),) +DEFINES += -DBUILD_DCONNECT +LOCAL_INCLUDES += -I$(srcdir)/../../extensions/dconnect/src +SHARED_LIBRARY_LIBS += $(DIST)/lib/$(LIB_PREFIX)ipcddconnect_s.$(LIB_SUFFIX) \ + $(DIST)/lib/$(LIB_PREFIX)ipcdutil_s.$(LIB_SUFFIX) \ + $(NULL) +endif + +EXTRA_DSO_LDOPTS = \ + $(LIBS_DIR) \ + $(EXTRA_DSO_LIBS) \ + $(MOZ_COMPONENT_LIBS) \ + $(NULL) + +include $(topsrcdir)/config/rules.mk diff --git a/src/libs/xpcom18a4/ipc/ipcd/client/src/Makefile.kup b/src/libs/xpcom18a4/ipc/ipcd/client/src/Makefile.kup new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/src/libs/xpcom18a4/ipc/ipcd/client/src/Makefile.kup diff --git a/src/libs/xpcom18a4/ipc/ipcd/client/src/ipcConnection.h b/src/libs/xpcom18a4/ipc/ipcd/client/src/ipcConnection.h new file mode 100644 index 00000000..8773daa5 --- /dev/null +++ b/src/libs/xpcom18a4/ipc/ipcd/client/src/ipcConnection.h @@ -0,0 +1,147 @@ +/* vim:set ts=2 sw=2 et cindent: */ +/* ***** 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 IBM Corporation. + * Portions created by IBM Corporation are Copyright (C) 2003 + * IBM Corporation. All Rights Reserved. + * + * Contributor(s): + * Darin Fisher <darin@meer.net> + * + * 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 ***** */ + +#ifndef ipcConnection_h__ +#define ipcConnection_h__ + +#include "nscore.h" + +class ipcMessage; + +#define IPC_METHOD_PRIVATE_(type) NS_HIDDEN_(type) +#define IPC_METHOD_PRIVATE IPC_METHOD_PRIVATE_(nsresult) + +/* ------------------------------------------------------------------------- */ +/* Platform specific IPC connection API. + */ + +typedef void (* ipcCallbackFunc)(void *); + +/** + * 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. + * + * @param daemonPath + * Specifies the path to the IPC daemon executable. + * + * NOTE: This function must be called on the main thread. + */ +IPC_METHOD_PRIVATE IPC_Connect(const char *daemonPath); + +/** + * 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. + */ +IPC_METHOD_PRIVATE IPC_Disconnect(); + +/** + * 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. + */ +IPC_METHOD_PRIVATE IPC_SendMsg(ipcMessage *msg); + +/** + * 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. + */ +IPC_METHOD_PRIVATE IPC_DoCallback(ipcCallbackFunc func, void *arg); + +/* ------------------------------------------------------------------------- */ +/* Cross-platform IPC connection methods. + */ + +/** + * IPC_SpawnDaemon + * + * This function launches the IPC daemon process. It is called by the platform + * specific IPC_Connect implementation. It should not return until the daemon + * process is ready to receive a client connection or an error occurs. + * + * @param daemonPath + * Specifies the path to the IPC daemon executable. + */ +IPC_METHOD_PRIVATE IPC_SpawnDaemon(const char *daemonPath); + +/* ------------------------------------------------------------------------- */ +/* IPC connection callbacks (not implemented by the connection code). + * + * NOTE: These functions execute on a background thread!! + */ + +/** + * IPC_OnConnectionEnd + * + * This function is called whenever the connection to the IPC daemon has been + * terminated. If terminated due to an abnormal error, then the error will be + * described by the |error| parameter. If |error| is NS_OK, then it means the + * connection was closed in response to a call to IPC_Disconnect. + */ +IPC_METHOD_PRIVATE_(void) IPC_OnConnectionEnd(nsresult error); + +/** + * IPC_OnMessageAvailable + * + * This function is called whenever an incoming message is read from the IPC + * daemon. The ipcMessage object, |msg|, must be deleted by the implementation + * of IPC_OnMessageAvailable when the object is no longer needed. + */ +IPC_METHOD_PRIVATE_(void) IPC_OnMessageAvailable(ipcMessage *msg); + +#endif // ipcConnection_h__ diff --git a/src/libs/xpcom18a4/ipc/ipcd/client/src/ipcConnectionStub.cpp b/src/libs/xpcom18a4/ipc/ipcd/client/src/ipcConnectionStub.cpp new file mode 100644 index 00000000..8b985785 --- /dev/null +++ b/src/libs/xpcom18a4/ipc/ipcd/client/src/ipcConnectionStub.cpp @@ -0,0 +1,70 @@ +/* vim:set ts=2 sw=2 et cindent: */ +/* ***** 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 IBM Corporation. + * Portions created by IBM Corporation are Copyright (C) 2003 + * IBM Corporation. All Rights Reserved. + * + * Contributor(s): + * Darin Fisher <darin@meer.net> + * + * 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 "ipcConnection.h" +#include "nsError.h" + +//----------------------------------------------------------------------------- +// use this file as a template to add client-side IPC connectivity. +// +// NOTE: if your platform supports local domain TCP sockets, then you should +// be able to make use of ipcConnectionUnix.cpp. +//----------------------------------------------------------------------------- + +nsresult +IPC_Connect(const char *daemonPath) +{ + return NS_ERROR_NOT_IMPLEMENTED; +} + +nsresult +IPC_Disconnect() +{ + return NS_ERROR_NOT_IMPLEMENTED; +} + +nsresult +IPC_SendMsg(ipcMessage *msg) +{ + return NS_ERROR_NOT_IMPLEMENTED; +} + +nsresult +IPC_DoCallback(ipcCallbackFunc func, void *arg) +{ + return NS_ERROR_NOT_IMPLEMENTED; +} diff --git a/src/libs/xpcom18a4/ipc/ipcd/client/src/ipcConnectionUnix.cpp b/src/libs/xpcom18a4/ipc/ipcd/client/src/ipcConnectionUnix.cpp new file mode 100644 index 00000000..5cd7a0c8 --- /dev/null +++ b/src/libs/xpcom18a4/ipc/ipcd/client/src/ipcConnectionUnix.cpp @@ -0,0 +1,615 @@ +/* vim:set ts=2 sw=2 et cindent: */ +/* ***** 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 IBM Corporation. + * Portions created by IBM Corporation are Copyright (C) 2003 + * IBM Corporation. All Rights Reserved. + * + * Contributor(s): + * Darin Fisher <darin@meer.net> + * + * 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 "private/pprio.h" +#include "prerror.h" +#include "prthread.h" +#include "prlock.h" +#include "prlog.h" // for PR_ASSERT (we don't actually use NSPR logging) +#include "prio.h" + +#include "ipcConnection.h" +#include "ipcMessageQ.h" +#include "ipcConfig.h" +#include "ipcLog.h" + +#ifdef VBOX +# include "prenv.h" +# include <stdio.h> +# include <VBox/log.h> +#endif + + +//----------------------------------------------------------------------------- +// NOTE: this code does not need to link with anything but NSPR. that is by +// design, so it can be easily reused in other projects that want to +// talk with mozilla's IPC daemon, but don't want to depend on xpcom. +// we depend at most on some xpcom header files, but no xpcom runtime +// symbols are used. +//----------------------------------------------------------------------------- + + +// single user systems, like OS/2, don't need these security checks. +#ifndef XP_OS2 +#define IPC_SKIP_SECURITY_CHECKS +#endif + +#ifndef IPC_SKIP_SECURITY_CHECKS +#include <unistd.h> +#include <sys/stat.h> +#endif + +static PRStatus +DoSecurityCheck(PRFileDesc *fd, const char *path) +{ +#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; +} + +//----------------------------------------------------------------------------- + +struct ipcCallback : public ipcListNode<ipcCallback> +{ + ipcCallbackFunc func; + void *arg; +}; + +typedef ipcList<ipcCallback> ipcCallbackQ; + +//----------------------------------------------------------------------------- + +struct ipcConnectionState +{ + PRLock *lock; + PRPollDesc fds[2]; + ipcCallbackQ callback_queue; + ipcMessageQ send_queue; + PRUint32 send_offset; // amount of send_queue.First() already written. + ipcMessage *in_msg; + PRBool shutdown; +}; + +#define SOCK 0 +#define POLL 1 + +static void +ConnDestroy(ipcConnectionState *s) +{ + 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; +} + +static ipcConnectionState * +ConnCreate(PRFileDesc *fd) +{ + 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; + } + + // disable inheritance of the IPC socket by children started + // using non-NSPR process API + PRStatus status = PR_SetFDInheritable(fd, PR_FALSE); + if (status != PR_SUCCESS) + { + LOG(("coudn't make IPC socket non-inheritable [err=%d]\n", PR_GetError())); + return NULL; + } + + // store this only if we are going to succeed. + s->fds[SOCK].fd = fd; + + return s; +} + +static nsresult +ConnRead(ipcConnectionState *s) +{ + 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; +} + +static nsresult +ConnWrite(ipcConnectionState *s) +{ + 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; +} + +PR_STATIC_CALLBACK(void) +ConnThread(void *arg) +{ + 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 error %d (%s), os error %d\n", PR_GetError(), + PR_ErrorToName(PR_GetError()), PR_GetOSError())); + 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")); +} + +//----------------------------------------------------------------------------- +// IPC connection API +//----------------------------------------------------------------------------- + +static ipcConnectionState *gConnState = NULL; +static PRThread *gConnThread = NULL; + +#ifdef DEBUG +static PRThread *gMainThread = NULL; +#endif + +nsresult +TryConnect(PRFileDesc **result) +{ + PRFileDesc *fd; + PRNetAddr addr; + PRSocketOptionData opt; + // don't use NS_ERROR_FAILURE as we want to detect these kind of errors + // in the frontend + nsresult rv = NS_ERROR_SOCKET_FAIL; + + 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; + +#ifdef VBOX + if (PR_GetEnv("TESTBOX_UUID")) + fprintf(stderr, "IPC socket path: %s\n", addr.local.path); + LogRel(("IPC socket path: %s\n", addr.local.path)); +#endif + + // 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; +} + +nsresult +IPC_Connect(const char *daemonPath) +{ + // synchronous connect, spawn daemon if necessary. + + PRFileDesc *fd = NULL; + 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)) + { + nsresult rv1 = IPC_SpawnDaemon(daemonPath); + if (NS_SUCCEEDED(rv1) || rv != NS_ERROR_SOCKET_FAIL) + rv = rv1; + 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; +} + +nsresult +IPC_Disconnect() +{ + // Must disconnect on same thread used to connect! + PR_ASSERT(gMainThread == PR_GetCurrentThread()); + + if (!gConnState || !gConnThread) + return NS_ERROR_NOT_INITIALIZED; + + PR_Lock(gConnState->lock); + gConnState->shutdown = PR_TRUE; + PR_SetPollableEvent(gConnState->fds[POLL].fd); + PR_Unlock(gConnState->lock); + + PR_JoinThread(gConnThread); + + ConnDestroy(gConnState); + + gConnState = NULL; + gConnThread = NULL; + return NS_OK; +} + +nsresult +IPC_SendMsg(ipcMessage *msg) +{ + if (!gConnState || !gConnThread) + return NS_ERROR_NOT_INITIALIZED; + + PR_Lock(gConnState->lock); + gConnState->send_queue.Append(msg); + PR_SetPollableEvent(gConnState->fds[POLL].fd); + PR_Unlock(gConnState->lock); + + return NS_OK; +} + +nsresult +IPC_DoCallback(ipcCallbackFunc func, void *arg) +{ + 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; +} + +//----------------------------------------------------------------------------- + +#ifdef TEST_STANDALONE + +void IPC_OnConnectionFault(nsresult rv) +{ + LOG(("IPC_OnConnectionFault [rv=%x]\n", rv)); +} + +void IPC_OnMessageAvailable(ipcMessage *msg) +{ + LOG(("IPC_OnMessageAvailable\n")); + delete msg; +} + +int main() +{ + IPC_InitLog(">>>"); + IPC_Connect("/builds/moz-trunk/seamonkey-debug-build/dist/bin/mozilla-ipcd"); + IPC_Disconnect(); + return 0; +} + +#endif diff --git a/src/libs/xpcom18a4/ipc/ipcd/client/src/ipcConnectionWin.cpp b/src/libs/xpcom18a4/ipc/ipcd/client/src/ipcConnectionWin.cpp new file mode 100644 index 00000000..9a99c195 --- /dev/null +++ b/src/libs/xpcom18a4/ipc/ipcd/client/src/ipcConnectionWin.cpp @@ -0,0 +1,332 @@ +/* ***** 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@meer.net> + * + * 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 <windows.h> + +#include "prprf.h" +#include "prmon.h" +#include "prthread.h" +#include "plevent.h" + +#include "nsIServiceManager.h" +#include "nsIEventQueue.h" +#include "nsIEventQueueService.h" +#include "nsAutoLock.h" + +#include "ipcConfig.h" +#include "ipcLog.h" +#include "ipcConnection.h" +#include "ipcm.h" + + +//----------------------------------------------------------------------------- +// NOTE: this code does not need to link with anything but NSPR. that is by +// design, so it can be easily reused in other projects that want to +// talk with mozilla's IPC daemon, but don't want to depend on xpcom. +// we depend at most on some xpcom header files, but no xpcom runtime +// symbols are used. +//----------------------------------------------------------------------------- + + +//----------------------------------------------------------------------------- +// windows message thread +//----------------------------------------------------------------------------- + +#define IPC_WM_SENDMSG (WM_USER + 0x1) +#define IPC_WM_CALLBACK (WM_USER + 0x2) +#define IPC_WM_SHUTDOWN (WM_USER + 0x3) + +static nsresult ipcThreadStatus = NS_OK; +static PRThread *ipcThread = NULL; +static PRMonitor *ipcMonitor = NULL; +static HWND ipcDaemonHwnd = NULL; +static HWND ipcLocalHwnd = NULL; +static PRBool ipcShutdown = PR_FALSE; // not accessed on message thread!! + +//----------------------------------------------------------------------------- +// window proc +//----------------------------------------------------------------------------- + +static LRESULT CALLBACK +ipcThreadWindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + LOG(("got message [msg=%x wparam=%x lparam=%x]\n", uMsg, wParam, lParam)); + + if (uMsg == WM_COPYDATA) { + COPYDATASTRUCT *cd = (COPYDATASTRUCT *) lParam; + if (cd && cd->lpData) { + ipcMessage *msg = new ipcMessage(); + PRUint32 bytesRead; + PRBool complete; + PRStatus rv = msg->ReadFrom((const char *) cd->lpData, cd->cbData, + &bytesRead, &complete); + if (rv == PR_SUCCESS && complete) + IPC_OnMessageAvailable(msg); // takes ownership of msg + else { + LOG((" unable to deliver message [complete=%u]\n", complete)); + delete msg; + } + } + return TRUE; + } + + if (uMsg == IPC_WM_SENDMSG) { + ipcMessage *msg = (ipcMessage *) lParam; + if (msg) { + LOG((" sending message...\n")); + COPYDATASTRUCT cd; + cd.dwData = GetCurrentProcessId(); + cd.cbData = (DWORD) msg->MsgLen(); + cd.lpData = (PVOID) msg->MsgBuf(); + SendMessageA(ipcDaemonHwnd, WM_COPYDATA, (WPARAM) hWnd, (LPARAM) &cd); + LOG((" done.\n")); + delete msg; + } + return 0; + } + + if (uMsg == IPC_WM_CALLBACK) { + ipcCallbackFunc func = (ipcCallbackFunc) wParam; + void *arg = (void *) lParam; + (func)(arg); + return 0; + } + + if (uMsg == IPC_WM_SHUTDOWN) { + IPC_OnConnectionEnd(NS_OK); + PostQuitMessage(0); + return 0; + } + + return DefWindowProc(hWnd, uMsg, wParam, lParam); +} + +//----------------------------------------------------------------------------- +// ipc thread functions +//----------------------------------------------------------------------------- + +static void +ipcThreadFunc(void *arg) +{ + LOG(("entering message thread\n")); + + DWORD pid = GetCurrentProcessId(); + + WNDCLASS wc; + memset(&wc, 0, sizeof(wc)); + wc.lpfnWndProc = ipcThreadWindowProc; + wc.lpszClassName = IPC_CLIENT_WINDOW_CLASS; + RegisterClass(&wc); + + char wName[sizeof(IPC_CLIENT_WINDOW_NAME_PREFIX) + 20]; + PR_snprintf(wName, sizeof(wName), "%s%u", IPC_CLIENT_WINDOW_NAME_PREFIX, pid); + + ipcLocalHwnd = CreateWindow(IPC_CLIENT_WINDOW_CLASS, wName, + 0, 0, 0, 10, 10, NULL, NULL, NULL, NULL); + + { + nsAutoMonitor mon(ipcMonitor); + if (!ipcLocalHwnd) + ipcThreadStatus = NS_ERROR_FAILURE; + mon.Notify(); + } + + if (ipcLocalHwnd) { + MSG msg; + while (GetMessage(&msg, ipcLocalHwnd, 0, 0)) + DispatchMessage(&msg); + + ipcShutdown = PR_TRUE; // assuming atomic memory write + + DestroyWindow(ipcLocalHwnd); + ipcLocalHwnd = NULL; + } + + LOG(("exiting message thread\n")); + return; +} + +static PRStatus +ipcThreadInit() +{ + if (ipcThread) + return PR_FAILURE; + + ipcShutdown = PR_FALSE; + + ipcMonitor = PR_NewMonitor(); + if (!ipcMonitor) + return PR_FAILURE; + + // spawn message thread + ipcThread = PR_CreateThread(PR_USER_THREAD, ipcThreadFunc, NULL, + PR_PRIORITY_NORMAL, PR_GLOBAL_THREAD, + PR_JOINABLE_THREAD, 0); + if (!ipcThread) { + NS_WARNING("thread creation failed"); + PR_DestroyMonitor(ipcMonitor); + ipcMonitor = NULL; + return PR_FAILURE; + } + + // wait for hidden window to be created + { + nsAutoMonitor mon(ipcMonitor); + while (!ipcLocalHwnd && NS_SUCCEEDED(ipcThreadStatus)) + mon.Wait(); + } + + if (NS_FAILED(ipcThreadStatus)) { + NS_WARNING("message thread failed"); + return PR_FAILURE; + } + + return PR_SUCCESS; +} + +static PRStatus +ipcThreadShutdown() +{ + if (PR_AtomicSet(&ipcShutdown, PR_TRUE) == PR_FALSE) { + LOG(("posting IPC_WM_SHUTDOWN message\n")); + PostMessage(ipcLocalHwnd, IPC_WM_SHUTDOWN, 0, 0); + } + + LOG(("joining w/ message thread...\n")); + PR_JoinThread(ipcThread); + ipcThread = NULL; + + // + // ok, now the message thread is dead + // + + PR_DestroyMonitor(ipcMonitor); + ipcMonitor = NULL; + + return PR_SUCCESS; +} + +//----------------------------------------------------------------------------- +// windows specific IPC connection impl +//----------------------------------------------------------------------------- + +nsresult +IPC_Disconnect() +{ + LOG(("IPC_Disconnect\n")); + + //XXX mHaveConnection = PR_FALSE; + + if (!ipcDaemonHwnd) + return NS_ERROR_NOT_INITIALIZED; + + if (ipcThread) + ipcThreadShutdown(); + + // clear our reference to the daemon's HWND. + ipcDaemonHwnd = NULL; + return NS_OK; +} + +nsresult +IPC_Connect(const char *daemonPath) +{ + LOG(("IPC_Connect\n")); + + NS_ENSURE_TRUE(ipcDaemonHwnd == NULL, NS_ERROR_ALREADY_INITIALIZED); + nsresult rv; + + ipcDaemonHwnd = FindWindow(IPC_WINDOW_CLASS, IPC_WINDOW_NAME); + if (!ipcDaemonHwnd) { + LOG((" daemon does not appear to be running\n")); + // + // daemon does not exist; spawn daemon... + // + rv = IPC_SpawnDaemon(daemonPath); + if (NS_FAILED(rv)) + return rv; + + ipcDaemonHwnd = FindWindow(IPC_WINDOW_CLASS, IPC_WINDOW_NAME); + if (!ipcDaemonHwnd) + return NS_ERROR_FAILURE; + } + + // + // delay creation of the message thread until we know the daemon exists. + // + if (!ipcThread && ipcThreadInit() != PR_SUCCESS) { + ipcDaemonHwnd = NULL; + return NS_ERROR_FAILURE; + } + + return NS_OK; +} + +nsresult +IPC_SendMsg(ipcMessage *msg) +{ + LOG(("IPC_SendMsg\n")); + + if (ipcShutdown) { + LOG(("unable to send message b/c message thread is shutdown\n")); + goto loser; + } + if (!PostMessage(ipcLocalHwnd, IPC_WM_SENDMSG, 0, (LPARAM) msg)) { + LOG((" PostMessage failed w/ error = %u\n", GetLastError())); + goto loser; + } + return NS_OK; +loser: + delete msg; + return NS_ERROR_FAILURE; +} + +nsresult +IPC_DoCallback(ipcCallbackFunc func, void *arg) +{ + LOG(("IPC_DoCallback\n")); + + if (ipcShutdown) { + LOG(("unable to send message b/c message thread is shutdown\n")); + return NS_ERROR_FAILURE; + } + if (!PostMessage(ipcLocalHwnd, IPC_WM_CALLBACK, (WPARAM) func, (LPARAM) arg)) { + LOG((" PostMessage failed w/ error = %u\n", GetLastError())); + return NS_ERROR_FAILURE; + } + return NS_OK; +} diff --git a/src/libs/xpcom18a4/ipc/ipcd/client/src/ipcModuleFactory.cpp b/src/libs/xpcom18a4/ipc/ipcd/client/src/ipcModuleFactory.cpp new file mode 100644 index 00000000..7dcd6eab --- /dev/null +++ b/src/libs/xpcom18a4/ipc/ipcd/client/src/ipcModuleFactory.cpp @@ -0,0 +1,196 @@ +/* ***** 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 "nsIServiceManager.h" +#include "nsIGenericFactory.h" +#include "nsICategoryManager.h" +#include "ipcdclient.h" +#include "ipcService.h" +#include "ipcConfig.h" +#include "ipcCID.h" + +//----------------------------------------------------------------------------- +// Define the contructor function for the objects +// +// NOTE: This creates an instance of objects by using the default constructor +//----------------------------------------------------------------------------- +NS_GENERIC_FACTORY_CONSTRUCTOR(ipcService) + +// enable this code to make the IPC service auto-start. +#if 0 +NS_METHOD +ipcServiceRegisterProc(nsIComponentManager *aCompMgr, + nsIFile *aPath, + const char *registryLocation, + const char *componentType, + const nsModuleComponentInfo *info) +{ + // + // add ipcService to the XPCOM startup category + // + nsCOMPtr<nsICategoryManager> catman(do_GetService(NS_CATEGORYMANAGER_CONTRACTID)); + if (catman) { + nsXPIDLCString prevEntry; + catman->AddCategoryEntry(NS_XPCOM_STARTUP_OBSERVER_ID, "ipcService", + IPC_SERVICE_CONTRACTID, PR_TRUE, PR_TRUE, + getter_Copies(prevEntry)); + } + return NS_OK; +} + +NS_METHOD +ipcServiceUnregisterProc(nsIComponentManager *aCompMgr, + nsIFile *aPath, + const char *registryLocation, + const nsModuleComponentInfo *info) +{ + nsCOMPtr<nsICategoryManager> catman(do_GetService(NS_CATEGORYMANAGER_CONTRACTID)); + if (catman) + catman->DeleteCategoryEntry(NS_XPCOM_STARTUP_OBSERVER_ID, + IPC_SERVICE_CONTRACTID, PR_TRUE); + return NS_OK; +} +#endif + +//----------------------------------------------------------------------------- +// extensions + +#include "ipcLockService.h" +#include "ipcLockCID.h" +NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(ipcLockService, Init) + +#include "tmTransactionService.h" +NS_GENERIC_FACTORY_CONSTRUCTOR(tmTransactionService) + +#ifdef BUILD_DCONNECT + +#include "ipcDConnectService.h" +NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(ipcDConnectService, Init) + +// enable this code to make the IPC DCONNECT service auto-start. +NS_METHOD +ipcDConnectServiceRegisterProc(nsIComponentManager *aCompMgr, + nsIFile *aPath, + const char *registryLocation, + const char *componentType, + const nsModuleComponentInfo *info) +{ + // + // add ipcService to the XPCOM startup category + // + nsCOMPtr<nsICategoryManager> catman(do_GetService(NS_CATEGORYMANAGER_CONTRACTID)); + if (catman) { + nsXPIDLCString prevEntry; + catman->AddCategoryEntry(NS_XPCOM_STARTUP_OBSERVER_ID, "ipcDConnectService", + IPC_DCONNECTSERVICE_CONTRACTID, PR_TRUE, PR_TRUE, + getter_Copies(prevEntry)); + } + return NS_OK; +} + +NS_METHOD +ipcDConnectServiceUnregisterProc(nsIComponentManager *aCompMgr, + nsIFile *aPath, + const char *registryLocation, + const nsModuleComponentInfo *info) +{ + nsCOMPtr<nsICategoryManager> catman(do_GetService(NS_CATEGORYMANAGER_CONTRACTID)); + if (catman) + catman->DeleteCategoryEntry(NS_XPCOM_STARTUP_OBSERVER_ID, + IPC_DCONNECTSERVICE_CONTRACTID, PR_TRUE); + return NS_OK; +} + +#endif // BUILD_DCONNECT + +//----------------------------------------------------------------------------- +// Define a table of CIDs implemented by this module along with other +// information like the function to create an instance, contractid, and +// class name. +//----------------------------------------------------------------------------- +static const nsModuleComponentInfo components[] = { + { IPC_SERVICE_CLASSNAME, + IPC_SERVICE_CID, + IPC_SERVICE_CONTRACTID, + ipcServiceConstructor }, + /* + ipcServiceRegisterProc, + ipcServiceUnregisterProc }, + */ + // + // extensions go here: + // + { IPC_LOCKSERVICE_CLASSNAME, + IPC_LOCKSERVICE_CID, + IPC_LOCKSERVICE_CONTRACTID, + ipcLockServiceConstructor }, + { IPC_TRANSACTIONSERVICE_CLASSNAME, + IPC_TRANSACTIONSERVICE_CID, + IPC_TRANSACTIONSERVICE_CONTRACTID, + tmTransactionServiceConstructor }, + +#ifdef BUILD_DCONNECT + { IPC_DCONNECTSERVICE_CLASSNAME, + IPC_DCONNECTSERVICE_CID, + IPC_DCONNECTSERVICE_CONTRACTID, + ipcDConnectServiceConstructor, + ipcDConnectServiceRegisterProc, + ipcDConnectServiceUnregisterProc }, +#endif +}; + +//----------------------------------------------------------------------------- + +PR_STATIC_CALLBACK(nsresult) +ipcdclient_init(nsIModule *module) +{ + return IPC_Init(); +} + +PR_STATIC_CALLBACK(void) +ipcdclient_shutdown(nsIModule *module) +{ + IPC_Shutdown(); +} + +//----------------------------------------------------------------------------- +// Implement the NSGetModule() exported function for your module +// and the entire implementation of the module object. +//----------------------------------------------------------------------------- +NS_IMPL_NSGETMODULE_WITH_CTOR_DTOR(ipcdclient, components, + ipcdclient_init, + ipcdclient_shutdown) diff --git a/src/libs/xpcom18a4/ipc/ipcd/client/src/ipcService.cpp b/src/libs/xpcom18a4/ipc/ipcd/client/src/ipcService.cpp new file mode 100644 index 00000000..6c2a1326 --- /dev/null +++ b/src/libs/xpcom18a4/ipc/ipcd/client/src/ipcService.cpp @@ -0,0 +1,120 @@ +/* vim:set ts=4 sw=4 et cindent: */ +/* ***** 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 IBM Corporation. + * Portions created by the Initial Developer are Copyright (C) 2004 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Darin Fisher <darin@meer.net> + * + * 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 "ipcService.h" + +// The ipcService implementation is nothing more than a thin XPCOM wrapper +// around the ipcdclient.h API. + +NS_IMPL_THREADSAFE_ISUPPORTS1(ipcService, ipcIService) + +NS_IMETHODIMP +ipcService::GetID(PRUint32 *aID) +{ + return IPC_GetID(aID); +} + +NS_IMETHODIMP +ipcService::AddName(const char *aName) +{ + return IPC_AddName(aName); +} + +NS_IMETHODIMP +ipcService::RemoveName(const char *aName) +{ + return IPC_RemoveName(aName); +} + +NS_IMETHODIMP +ipcService::AddClientObserver(ipcIClientObserver *aObserver) +{ + return IPC_AddClientObserver(aObserver); +} + +NS_IMETHODIMP +ipcService::RemoveClientObserver(ipcIClientObserver *aObserver) +{ + return IPC_RemoveClientObserver(aObserver); +} + +NS_IMETHODIMP +ipcService::ResolveClientName(const char *aName, PRUint32 *aID) +{ + return IPC_ResolveClientName(aName, aID); +} + +NS_IMETHODIMP +ipcService::ClientExists(PRUint32 aClientID, PRBool *aResult) +{ + return IPC_ClientExists(aClientID, aResult); +} + +NS_IMETHODIMP +ipcService::DefineTarget(const nsID &aTarget, ipcIMessageObserver *aObserver, + PRBool aOnCurrentThread) +{ + return IPC_DefineTarget(aTarget, aObserver, aOnCurrentThread); +} + +NS_IMETHODIMP +ipcService::SendMessage(PRUint32 aReceiverID, const nsID &aTarget, + const PRUint8 *aData, PRUint32 aDataLen) +{ + return IPC_SendMessage(aReceiverID, aTarget, aData, aDataLen); +} + +NS_IMETHODIMP +ipcService::WaitMessage(PRUint32 aSenderID, const nsID &aTarget, + ipcIMessageObserver *aObserver, + PRUint32 aTimeout) +{ + return IPC_WaitMessage(aSenderID, aTarget, aObserver, nsnull, + PR_MillisecondsToInterval(aTimeout)); +} + +NS_IMETHODIMP +ipcService::DisableMessageObserver(const nsID &aTarget) +{ + return IPC_DisableMessageObserver(aTarget); +} + +NS_IMETHODIMP +ipcService::EnableMessageObserver(const nsID &aTarget) +{ + return IPC_EnableMessageObserver(aTarget); +} diff --git a/src/libs/xpcom18a4/ipc/ipcd/client/src/ipcService.h b/src/libs/xpcom18a4/ipc/ipcd/client/src/ipcService.h new file mode 100644 index 00000000..80f953cf --- /dev/null +++ b/src/libs/xpcom18a4/ipc/ipcd/client/src/ipcService.h @@ -0,0 +1,51 @@ +/* ***** 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 ***** */ + +#ifndef ipcService_h__ +#define ipcService_h__ + +#include "ipcIService.h" +#include "ipcdclient.h" + +class ipcService : public ipcIService +{ +public: + NS_DECL_ISUPPORTS + NS_DECL_IPCISERVICE +}; + +#endif // !defined( ipcService_h__ ) diff --git a/src/libs/xpcom18a4/ipc/ipcd/client/src/ipcdclient.cpp b/src/libs/xpcom18a4/ipc/ipcd/client/src/ipcdclient.cpp new file mode 100644 index 00000000..ce0f86c8 --- /dev/null +++ b/src/libs/xpcom18a4/ipc/ipcd/client/src/ipcdclient.cpp @@ -0,0 +1,1505 @@ +/* vim:set ts=2 sw=2 et cindent: */ +/* ***** 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 IBM Corporation. + * Portions created by the Initial Developer are Copyright (C) 2004 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Darin Fisher <darin@meer.net> + * + * 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 "ipcdclient.h" +#include "ipcConnection.h" +#include "ipcConfig.h" +#include "ipcMessageQ.h" +#include "ipcMessageUtils.h" +#include "ipcLog.h" +#include "ipcm.h" + +#include "nsIFile.h" +#include "nsEventQueueUtils.h" +#include "nsDirectoryServiceUtils.h" +#include "nsDirectoryServiceDefs.h" +#include "nsCOMPtr.h" +#include "nsHashKeys.h" +#include "nsRefPtrHashtable.h" +#include "nsAutoLock.h" +#include "nsProxyRelease.h" +#include "nsCOMArray.h" + +#include "prio.h" +#include "prproces.h" +#include "pratom.h" + +#ifdef VBOX +# include <iprt/critsect.h> +# define VBOX_WITH_IPCCLIENT_RW_CS +#endif + +/* ------------------------------------------------------------------------- */ + +#define IPC_REQUEST_TIMEOUT PR_SecondsToInterval(30) + +/* ------------------------------------------------------------------------- */ + +class ipcTargetData +{ +public: + static NS_HIDDEN_(ipcTargetData*) Create(); + + // threadsafe addref/release + NS_HIDDEN_(nsrefcnt) AddRef() { return PR_AtomicIncrement(&refcnt); } + NS_HIDDEN_(nsrefcnt) Release() { PRInt32 r = PR_AtomicDecrement(&refcnt); if (r == 0) delete this; return r; } + + NS_HIDDEN_(void) SetObserver(ipcIMessageObserver *aObserver, PRBool aOnCurrentThread); + + // protects access to the members of this class + PRMonitor *monitor; + + // this may be null + nsCOMPtr<ipcIMessageObserver> observer; + + // the message observer is called via this event queue + nsCOMPtr<nsIEventQueue> eventQ; + + // incoming messages are added to this list + ipcMessageQ pendingQ; + + // non-zero if the observer has been disabled (this means that new messages + // should not be dispatched to the observer until the observer is re-enabled + // via IPC_EnableMessageObserver). + PRInt32 observerDisabled; + +private: + + ipcTargetData() + : monitor(nsAutoMonitor::NewMonitor("ipcTargetData")) + , observerDisabled(0) + , refcnt(0) + {} + + ~ipcTargetData() + { + if (monitor) + nsAutoMonitor::DestroyMonitor(monitor); + } + + PRInt32 refcnt; +}; + +ipcTargetData * +ipcTargetData::Create() +{ + ipcTargetData *td = new ipcTargetData; + if (!td) + return NULL; + + if (!td->monitor) + { + delete td; + return NULL; + } + return td; +} + +void +ipcTargetData::SetObserver(ipcIMessageObserver *aObserver, PRBool aOnCurrentThread) +{ + observer = aObserver; + + if (aOnCurrentThread) + NS_GetCurrentEventQ(getter_AddRefs(eventQ)); + else + eventQ = nsnull; +} + +/* ------------------------------------------------------------------------- */ + +typedef nsRefPtrHashtable<nsIDHashKey, ipcTargetData> ipcTargetMap; + +class ipcClientState +{ +public: + static NS_HIDDEN_(ipcClientState *) Create(); + + ~ipcClientState() + { +#ifndef VBOX_WITH_IPCCLIENT_RW_CS + if (monitor) + nsAutoMonitor::DestroyMonitor(monitor); +#else + RTCritSectRwDelete(&critSect); +#endif + } + +#ifndef VBOX_WITH_IPCCLIENT_RW_CS + // + // the monitor protects the targetMap and the connected and shutdown flags. + // + // NOTE: we use a PRMonitor for this instead of a PRLock because we need + // the lock to be re-entrant. since we don't ever need to wait on + // this monitor, it might be worth it to implement a re-entrant + // wrapper for PRLock. + // + PRMonitor *monitor; +#else /* VBOX_WITH_IPCCLIENT_RW_CS */ + RTCRITSECTRW critSect; +#endif /* VBOX_WITH_IPCCLIENT_RW_CS */ + ipcTargetMap targetMap; + PRBool connected; + PRBool shutdown; + + // our process's client id + PRUint32 selfID; + + nsCOMArray<ipcIClientObserver> clientObservers; + +private: + + ipcClientState() +#ifndef VBOX_WITH_IPCCLIENT_RW_CS + : monitor(nsAutoMonitor::NewMonitor("ipcClientState")) + , connected(PR_FALSE) +#else + : connected(PR_FALSE) +#endif + , shutdown(PR_FALSE) + , selfID(0) + { +#ifdef VBOX_WITH_IPCCLIENT_RW_CS + /* Not employing the lock validator here to keep performance up in debug builds. */ + RTCritSectRwInitEx(&critSect, RTCRITSECT_FLAGS_NO_LOCK_VAL, NIL_RTLOCKVALCLASS, RTLOCKVAL_SUB_CLASS_NONE, NULL); +#endif + } +}; + +ipcClientState * +ipcClientState::Create() +{ + ipcClientState *cs = new ipcClientState; + if (!cs) + return NULL; + +#ifndef VBOX_WITH_IPCCLIENT_RW_CS + if (!cs->monitor || !cs->targetMap.Init()) +#else + if (!RTCritSectRwIsInitialized(&cs->critSect) || !cs->targetMap.Init()) +#endif + { + delete cs; + return NULL; + } + + return cs; +} + +/* ------------------------------------------------------------------------- */ + +static ipcClientState *gClientState; + +static PRBool +GetTarget(const nsID &aTarget, ipcTargetData **td) +{ +#ifndef VBOX_WITH_IPCCLIENT_RW_CS + nsAutoMonitor mon(gClientState->monitor); + return gClientState->targetMap.Get(nsIDHashKey(&aTarget).GetKey(), td); +#else + RTCritSectRwEnterShared(&gClientState->critSect); + PRBool fRc = gClientState->targetMap.Get(nsIDHashKey(&aTarget).GetKey(), td); + RTCritSectRwLeaveShared(&gClientState->critSect); + return fRc; +#endif +} + +static PRBool +PutTarget(const nsID &aTarget, ipcTargetData *td) +{ +#ifndef VBOX_WITH_IPCCLIENT_RW_CS + nsAutoMonitor mon(gClientState->monitor); + return gClientState->targetMap.Put(nsIDHashKey(&aTarget).GetKey(), td); +#else + RTCritSectRwEnterExcl(&gClientState->critSect); + PRBool fRc = gClientState->targetMap.Put(nsIDHashKey(&aTarget).GetKey(), td); + RTCritSectRwLeaveExcl(&gClientState->critSect); + return fRc; +#endif +} + +static void +DelTarget(const nsID &aTarget) +{ +#ifndef VBOX_WITH_IPCCLIENT_RW_CS + nsAutoMonitor mon(gClientState->monitor); + gClientState->targetMap.Remove(nsIDHashKey(&aTarget).GetKey()); +#else + RTCritSectRwEnterExcl(&gClientState->critSect); + gClientState->targetMap.Remove(nsIDHashKey(&aTarget).GetKey()); + RTCritSectRwLeaveExcl(&gClientState->critSect); +#endif +} + +/* ------------------------------------------------------------------------- */ + +static nsresult +GetDaemonPath(nsCString &dpath) +{ + nsCOMPtr<nsIFile> file; + + nsresult rv = NS_GetSpecialDirectory(NS_XPCOM_CURRENT_PROCESS_DIR, + getter_AddRefs(file)); + if (NS_SUCCEEDED(rv)) + { + rv = file->AppendNative(NS_LITERAL_CSTRING(IPC_DAEMON_APP_NAME)); + if (NS_SUCCEEDED(rv)) + rv = file->GetNativePath(dpath); + } + + return rv; +} + +/* ------------------------------------------------------------------------- */ + +static void +ProcessPendingQ(const nsID &aTarget) +{ + ipcMessageQ tempQ; + + nsRefPtr<ipcTargetData> td; + if (GetTarget(aTarget, getter_AddRefs(td))) + { + nsAutoMonitor mon(td->monitor); + + // if the observer for this target has been temporarily disabled, then + // we must not processing any pending messages at this time. + + if (!td->observerDisabled) + td->pendingQ.MoveTo(tempQ); + } + + // process pending queue outside monitor + while (!tempQ.IsEmpty()) + { + ipcMessage *msg = tempQ.First(); + + // it is possible that messages for other targets are in the queue + // (currently, this can be only a IPCM_MSG_PSH_CLIENT_STATE message + // initially addressed to IPCM_TARGET, see IPC_OnMessageAvailable()) + // --ignore them. + if (td->observer && msg->Target().Equals(aTarget)) + td->observer->OnMessageAvailable(msg->mMetaData, + msg->Target(), + (const PRUint8 *) msg->Data(), + msg->DataLen()); + else + { + // the IPCM target does not have an observer, and therefore any IPCM + // messages that make it here will simply be dropped. + NS_ASSERTION(aTarget.Equals(IPCM_TARGET) || msg->Target().Equals(IPCM_TARGET), + "unexpected target"); + LOG(("dropping IPCM message: type=%x\n", IPCM_GetType(msg))); + } + tempQ.DeleteFirst(); + } +} + +/* ------------------------------------------------------------------------- */ + +// WaitTarget enables support for multiple threads blocking on the same +// message target. the selector is called while inside the target's monitor. + +typedef nsresult (* ipcMessageSelector)( + void *arg, + ipcTargetData *td, + const ipcMessage *msg +); + +// selects any +static nsresult +DefaultSelector(void *arg, ipcTargetData *td, const ipcMessage *msg) +{ + return NS_OK; +} + +static nsresult +WaitTarget(const nsID &aTarget, + PRIntervalTime aTimeout, + ipcMessage **aMsg, + ipcMessageSelector aSelector = nsnull, + void *aArg = nsnull) +{ + *aMsg = nsnull; + + if (!aSelector) + aSelector = DefaultSelector; + + nsRefPtr<ipcTargetData> td; + if (!GetTarget(aTarget, getter_AddRefs(td))) + return NS_ERROR_INVALID_ARG; // bad aTarget + + PRBool isIPCMTarget = aTarget.Equals(IPCM_TARGET); + + PRIntervalTime timeStart = PR_IntervalNow(); + PRIntervalTime timeEnd; + if (aTimeout == PR_INTERVAL_NO_TIMEOUT) + timeEnd = aTimeout; + else if (aTimeout == PR_INTERVAL_NO_WAIT) + timeEnd = timeStart; + else + { + timeEnd = timeStart + aTimeout; + + // if overflowed, then set to max value + if (timeEnd < timeStart) + timeEnd = PR_INTERVAL_NO_TIMEOUT; + } + + ipcMessage *lastChecked = nsnull, *beforeLastChecked = nsnull; + nsresult rv = NS_ERROR_ABORT; + + nsAutoMonitor mon(td->monitor); + + // only the ICPM target is allowed to wait for a message after shutdown + // (but before disconnection). this gives client observers called from + // IPC_Shutdown a chance to use IPC_SendMessage to send necessary + // "last minute" messages to other clients. + + while (gClientState->connected && (!gClientState->shutdown || isIPCMTarget)) + { + NS_ASSERTION(!lastChecked, "oops"); + + // + // NOTE: + // + // we must start at the top of the pending queue, possibly revisiting + // messages that our selector has already rejected. this is necessary + // because the queue may have been modified while we were waiting on + // the monitor. the impact of this on performance remains to be seen. + // + // one cheap solution is to keep a counter that is incremented each + // time a message is removed from the pending queue. that way we can + // avoid revisiting all messages sometimes. + // + + lastChecked = td->pendingQ.First(); + beforeLastChecked = nsnull; + + // loop over pending queue until we find a message that our selector likes. + while (lastChecked) + { + // + // it is possible that this call to WaitTarget() has been initiated by + // some other selector, that might be currently processing the same + // message (since the message remains in the queue until the selector + // returns TRUE). here we prevent this situation by using a special flag + // to guarantee that every message is processed only once. + // + + if (!lastChecked->TestFlag(IPC_MSG_FLAG_IN_PROCESS)) + { + lastChecked->SetFlag(IPC_MSG_FLAG_IN_PROCESS); + nsresult acceptedRV = (aSelector)(aArg, td, lastChecked); + lastChecked->ClearFlag(IPC_MSG_FLAG_IN_PROCESS); + + if (acceptedRV != IPC_WAIT_NEXT_MESSAGE) + { + if (acceptedRV == NS_OK) + { + // remove from pending queue + if (beforeLastChecked) + td->pendingQ.RemoveAfter(beforeLastChecked); + else + td->pendingQ.RemoveFirst(); + + lastChecked->mNext = nsnull; + *aMsg = lastChecked; + break; + } + else /* acceptedRV == IPC_DISCARD_MESSAGE */ + { + ipcMessage *nextToCheck = lastChecked->mNext; + + // discard from pending queue + if (beforeLastChecked) + td->pendingQ.DeleteAfter(beforeLastChecked); + else + td->pendingQ.DeleteFirst(); + + lastChecked = nextToCheck; + + continue; + } + } + } + + beforeLastChecked = lastChecked; + lastChecked = lastChecked->mNext; + } + + if (*aMsg) + { + rv = NS_OK; + break; + } +#ifdef VBOX + else + { + /* Special client liveness check if there is no message to process. + * This is necessary as there might be several threads waiting for + * a message from a single client, and only one gets the DOWN msg. */ + nsresult aliveRV = (aSelector)(aArg, td, NULL); + if (aliveRV != IPC_WAIT_NEXT_MESSAGE) + { + *aMsg = NULL; + break; + } + } +#endif /* VBOX */ + + PRIntervalTime t = PR_IntervalNow(); + if (t > timeEnd) // check if timeout has expired + { + rv = IPC_ERROR_WOULD_BLOCK; + break; + } + mon.Wait(timeEnd - t); + + LOG(("woke up from sleep [pendingQempty=%d connected=%d shutdown=%d isIPCMTarget=%d]\n", + td->pendingQ.IsEmpty(), gClientState->connected, + gClientState->shutdown, isIPCMTarget)); + } + + return rv; +} + +/* ------------------------------------------------------------------------- */ + +static void +PostEvent(nsIEventTarget *eventTarget, PLEvent *ev) +{ + if (!ev) + return; + + nsresult rv = eventTarget->PostEvent(ev); + if (NS_FAILED(rv)) + { + NS_WARNING("PostEvent failed"); + PL_DestroyEvent(ev); + } +} + +static void +PostEventToMainThread(PLEvent *ev) +{ + nsCOMPtr<nsIEventQueue> eventQ; + NS_GetMainEventQ(getter_AddRefs(eventQ)); + if (!eventQ) + { + NS_WARNING("unable to get reference to main event queue"); + PL_DestroyEvent(ev); + return; + } + PostEvent(eventQ, ev); +} + +/* ------------------------------------------------------------------------- */ + +class ipcEvent_ClientState : public PLEvent +{ +public: + ipcEvent_ClientState(PRUint32 aClientID, PRUint32 aClientState) + : mClientID(aClientID) + , mClientState(aClientState) + { + PL_InitEvent(this, nsnull, HandleEvent, DestroyEvent); + } + + PR_STATIC_CALLBACK(void *) HandleEvent(PLEvent *ev) + { + // maybe we've been shutdown! + if (!gClientState) + return nsnull; + + ipcEvent_ClientState *self = (ipcEvent_ClientState *) ev; + + for (PRInt32 i=0; i<gClientState->clientObservers.Count(); ++i) + gClientState->clientObservers[i]->OnClientStateChange(self->mClientID, + self->mClientState); + return nsnull; + } + + PR_STATIC_CALLBACK(void) DestroyEvent(PLEvent *ev) + { + delete (ipcEvent_ClientState *) ev; + } + +private: + PRUint32 mClientID; + PRUint32 mClientState; +}; + +/* ------------------------------------------------------------------------- */ + +class ipcEvent_ProcessPendingQ : public PLEvent +{ +public: + ipcEvent_ProcessPendingQ(const nsID &aTarget) + : mTarget(aTarget) + { + PL_InitEvent(this, nsnull, HandleEvent, DestroyEvent); + } + + PR_STATIC_CALLBACK(void *) HandleEvent(PLEvent *ev) + { + ProcessPendingQ(((ipcEvent_ProcessPendingQ *) ev)->mTarget); + return nsnull; + } + + PR_STATIC_CALLBACK(void) DestroyEvent(PLEvent *ev) + { + delete (ipcEvent_ProcessPendingQ *) ev; + } + +private: + const nsID mTarget; +}; + +static void +CallProcessPendingQ(const nsID &target, ipcTargetData *td) +{ + // we assume that we are inside td's monitor + + PLEvent *ev = new ipcEvent_ProcessPendingQ(target); + if (!ev) + return; + + nsresult rv; + + if (td->eventQ) + rv = td->eventQ->PostEvent(ev); + else + rv = IPC_DoCallback((ipcCallbackFunc) PL_HandleEvent, ev); + + if (NS_FAILED(rv)) + PL_DestroyEvent(ev); +} + +/* ------------------------------------------------------------------------- */ + +static void +DisableMessageObserver(const nsID &aTarget) +{ + nsRefPtr<ipcTargetData> td; + if (GetTarget(aTarget, getter_AddRefs(td))) + { + nsAutoMonitor mon(td->monitor); + ++td->observerDisabled; + } +} + +static void +EnableMessageObserver(const nsID &aTarget) +{ + nsRefPtr<ipcTargetData> td; + if (GetTarget(aTarget, getter_AddRefs(td))) + { + nsAutoMonitor mon(td->monitor); + if (td->observerDisabled > 0 && --td->observerDisabled == 0) + if (!td->pendingQ.IsEmpty()) + CallProcessPendingQ(aTarget, td); + } +} + +/* ------------------------------------------------------------------------- */ + +// converts IPCM_ERROR_* status codes to NS_ERROR_* status codes +static nsresult nsresult_from_ipcm_result(PRInt32 status) +{ + nsresult rv = NS_ERROR_FAILURE; + + switch (status) + { + case IPCM_ERROR_GENERIC: rv = NS_ERROR_FAILURE; break; + case IPCM_ERROR_INVALID_ARG: rv = NS_ERROR_INVALID_ARG; break; + case IPCM_ERROR_NO_CLIENT: rv = NS_ERROR_CALL_FAILED; break; + // TODO: select better mapping for the below codes + case IPCM_ERROR_NO_SUCH_DATA: + case IPCM_ERROR_ALREADY_EXISTS: rv = NS_ERROR_FAILURE; break; + default: NS_ASSERTION(PR_FALSE, "No conversion"); + } + + return rv; +} + +/* ------------------------------------------------------------------------- */ + +// selects the next IPCM message with matching request index +static nsresult +WaitIPCMResponseSelector(void *arg, ipcTargetData *td, const ipcMessage *msg) +{ +#ifdef VBOX + if (!msg) + return IPC_WAIT_NEXT_MESSAGE; +#endif /* VBOX */ + PRUint32 requestIndex = *(PRUint32 *) arg; + return IPCM_GetRequestIndex(msg) == requestIndex ? NS_OK : IPC_WAIT_NEXT_MESSAGE; +} + +// wait for an IPCM response message. if responseMsg is null, then it is +// assumed that the caller does not care to get a reference to the +// response itself. if the response is an IPCM_MSG_ACK_RESULT, then the +// status code is mapped to a nsresult and returned by this function. +static nsresult +WaitIPCMResponse(PRUint32 requestIndex, ipcMessage **responseMsg = nsnull) +{ + ipcMessage *msg; + + nsresult rv = WaitTarget(IPCM_TARGET, IPC_REQUEST_TIMEOUT, &msg, + WaitIPCMResponseSelector, &requestIndex); + if (NS_FAILED(rv)) + return rv; + + if (IPCM_GetType(msg) == IPCM_MSG_ACK_RESULT) + { + ipcMessageCast<ipcmMessageResult> result(msg); + if (result->Status() < 0) + rv = nsresult_from_ipcm_result(result->Status()); + else + rv = NS_OK; + } + + if (responseMsg) + *responseMsg = msg; + else + delete msg; + + return rv; +} + +// make an IPCM request and wait for a response. +static nsresult +MakeIPCMRequest(ipcMessage *msg, ipcMessage **responseMsg = nsnull) +{ + if (!msg) + return NS_ERROR_OUT_OF_MEMORY; + + PRUint32 requestIndex = IPCM_GetRequestIndex(msg); + + // suppress 'ProcessPendingQ' for IPCM messages until we receive the + // response to this IPCM request. if we did not do this then there + // would be a race condition leading to the possible removal of our + // response from the pendingQ between sending the request and waiting + // for the response. + DisableMessageObserver(IPCM_TARGET); + + nsresult rv = IPC_SendMsg(msg); + if (NS_SUCCEEDED(rv)) + rv = WaitIPCMResponse(requestIndex, responseMsg); + + EnableMessageObserver(IPCM_TARGET); + return rv; +} + +/* ------------------------------------------------------------------------- */ + +static void +RemoveTarget(const nsID &aTarget, PRBool aNotifyDaemon) +{ + DelTarget(aTarget); + + if (aNotifyDaemon) + { + nsresult rv = MakeIPCMRequest(new ipcmMessageClientDelTarget(aTarget)); + if (NS_FAILED(rv)) + LOG(("failed to delete target: rv=%x\n", rv)); + } +} + +static nsresult +DefineTarget(const nsID &aTarget, + ipcIMessageObserver *aObserver, + PRBool aOnCurrentThread, + PRBool aNotifyDaemon, + ipcTargetData **aResult) +{ + nsresult rv; + + nsRefPtr<ipcTargetData> td( ipcTargetData::Create() ); + if (!td) + return NS_ERROR_OUT_OF_MEMORY; + td->SetObserver(aObserver, aOnCurrentThread); + + if (!PutTarget(aTarget, td)) + return NS_ERROR_OUT_OF_MEMORY; + + if (aNotifyDaemon) + { + rv = MakeIPCMRequest(new ipcmMessageClientAddTarget(aTarget)); + if (NS_FAILED(rv)) + { + LOG(("failed to add target: rv=%x\n", rv)); + RemoveTarget(aTarget, PR_FALSE); + return rv; + } + } + + if (aResult) + NS_ADDREF(*aResult = td); + return NS_OK; +} + +/* ------------------------------------------------------------------------- */ + +static nsresult +TryConnect() +{ + nsCAutoString dpath; + nsresult rv = GetDaemonPath(dpath); + if (NS_FAILED(rv)) + return rv; + + rv = IPC_Connect(dpath.get()); + if (NS_FAILED(rv)) + return rv; + + gClientState->connected = PR_TRUE; + + rv = DefineTarget(IPCM_TARGET, nsnull, PR_FALSE, PR_FALSE, nsnull); + if (NS_FAILED(rv)) + return rv; + + ipcMessage *msg = NULL; + + // send CLIENT_HELLO and wait for CLIENT_ID response... + rv = MakeIPCMRequest(new ipcmMessageClientHello(), &msg); + if (NS_FAILED(rv)) + { +#ifdef VBOX /* MakeIPCMRequest may return a failure (e.g. NS_ERROR_CALL_FAILED) and a response msg. */ + if (msg) + delete msg; +#endif + return rv; + } + + if (IPCM_GetType(msg) == IPCM_MSG_ACK_CLIENT_ID) + gClientState->selfID = ipcMessageCast<ipcmMessageClientID>(msg)->ClientID(); + else + { + LOG(("unexpected response from CLIENT_HELLO message: type=%x!\n", + IPCM_GetType(msg))); + rv = NS_ERROR_UNEXPECTED; + } + + delete msg; + return rv; +} + +nsresult +IPC_Init() +{ + NS_ENSURE_TRUE(!gClientState, NS_ERROR_ALREADY_INITIALIZED); + + IPC_InitLog(">>>"); + + gClientState = ipcClientState::Create(); + if (!gClientState) + return NS_ERROR_OUT_OF_MEMORY; + + nsresult rv = TryConnect(); + if (NS_FAILED(rv)) + IPC_Shutdown(); + + return rv; +} + +PR_STATIC_CALLBACK(PLDHashOperator) +EnumerateTargetMapAndNotify(const nsID &aKey, + ipcTargetData *aData, + void *aClosure); + +nsresult +IPC_Shutdown() +{ + NS_ENSURE_TRUE(gClientState, NS_ERROR_NOT_INITIALIZED); + + LOG(("IPC_Shutdown: connected=%d\n",gClientState->connected)); + + if (gClientState->connected) + { + { + // first, set the shutdown flag and unblock any calls to WaitTarget. + // all targets but IPCM will not be able to use WaitTarget any more. + +#ifndef VBOX_WITH_IPCCLIENT_RW_CS + nsAutoMonitor mon(gClientState->monitor); +#else + RTCritSectRwEnterExcl(&gClientState->critSect); +#endif + + gClientState->shutdown = PR_TRUE; + gClientState->targetMap.EnumerateRead(EnumerateTargetMapAndNotify, nsnull); + +#ifdef VBOX_WITH_IPCCLIENT_RW_CS + RTCritSectRwLeaveExcl(&gClientState->critSect); +#endif + } + + // inform all client observers that we're being shutdown to let interested + // parties gracefully uninitialize themselves. the IPCM target is still + // fully operational at this point, so they can use IPC_SendMessage + // (this is essential for the DConnect extension, for example, to do the + // proper uninitialization). + + ipcEvent_ClientState *ev = new ipcEvent_ClientState(IPC_SENDER_ANY, + IPCM_CLIENT_STATE_DOWN); + ipcEvent_ClientState::HandleEvent (ev); + ipcEvent_ClientState::DestroyEvent (ev); + + IPC_Disconnect(); + } + + // + // make gClientState nsnull before deletion to cause all public IPC_* + // calls (possibly made during ipcClientState destruction) to return + // NS_ERROR_NOT_INITIALIZED. + // + // NOTE: isn't just checking for gClientState->connected in every appropriate + // IPC_* method a better solution? + // + ipcClientState *aClientState = gClientState; + gClientState = nsnull; + delete aClientState; + + return NS_OK; +} + +/* ------------------------------------------------------------------------- */ + +nsresult +IPC_DefineTarget(const nsID &aTarget, + ipcIMessageObserver *aObserver, + PRBool aOnCurrentThread) +{ + NS_ENSURE_TRUE(gClientState, NS_ERROR_NOT_INITIALIZED); + + // do not permit the re-definition of the IPCM protocol's target. + if (aTarget.Equals(IPCM_TARGET)) + return NS_ERROR_INVALID_ARG; + + nsresult rv; + + nsRefPtr<ipcTargetData> td; + if (GetTarget(aTarget, getter_AddRefs(td))) + { + // clear out observer before removing target since we want to ensure that + // the observer is released on the main thread. + { + nsAutoMonitor mon(td->monitor); + td->SetObserver(aObserver, aOnCurrentThread); + } + + // remove target outside of td's monitor to avoid holding the monitor + // while entering the client state's monitor. + if (!aObserver) + RemoveTarget(aTarget, PR_TRUE); + + rv = NS_OK; + } + else + { + if (aObserver) + rv = DefineTarget(aTarget, aObserver, aOnCurrentThread, PR_TRUE, nsnull); + else + rv = NS_ERROR_INVALID_ARG; // unknown target + } + + return rv; +} + +nsresult +IPC_DisableMessageObserver(const nsID &aTarget) +{ + NS_ENSURE_TRUE(gClientState, NS_ERROR_NOT_INITIALIZED); + + // do not permit modifications to the IPCM protocol's target. + if (aTarget.Equals(IPCM_TARGET)) + return NS_ERROR_INVALID_ARG; + + DisableMessageObserver(aTarget); + return NS_OK; +} + +nsresult +IPC_EnableMessageObserver(const nsID &aTarget) +{ + NS_ENSURE_TRUE(gClientState, NS_ERROR_NOT_INITIALIZED); + + // do not permit modifications to the IPCM protocol's target. + if (aTarget.Equals(IPCM_TARGET)) + return NS_ERROR_INVALID_ARG; + + EnableMessageObserver(aTarget); + return NS_OK; +} + +nsresult +IPC_SendMessage(PRUint32 aReceiverID, + const nsID &aTarget, + const PRUint8 *aData, + PRUint32 aDataLen) +{ + NS_ENSURE_TRUE(gClientState, NS_ERROR_NOT_INITIALIZED); + + // do not permit sending IPCM messages + if (aTarget.Equals(IPCM_TARGET)) + return NS_ERROR_INVALID_ARG; + + nsresult rv; + if (aReceiverID == 0) + { + ipcMessage *msg = new ipcMessage(aTarget, (const char *) aData, aDataLen); + if (!msg) + return NS_ERROR_OUT_OF_MEMORY; + + rv = IPC_SendMsg(msg); + } + else + rv = MakeIPCMRequest(new ipcmMessageForward(IPCM_MSG_REQ_FORWARD, + aReceiverID, + aTarget, + (const char *) aData, + aDataLen)); + + return rv; +} + +struct WaitMessageSelectorData +{ + PRUint32 senderID; + ipcIMessageObserver *observer; + PRBool senderDead; +}; + +static nsresult WaitMessageSelector(void *arg, ipcTargetData *td, const ipcMessage *msg) +{ + WaitMessageSelectorData *data = (WaitMessageSelectorData *) arg; +#ifdef VBOX + if (!msg) + { + /* Special NULL message which asks to check whether the client is + * still alive. Called when there is nothing suitable in the queue. */ + ipcIMessageObserver *obs = data->observer; + if (!obs) + obs = td->observer; + NS_ASSERTION(obs, "must at least have a default observer"); + + nsresult rv = obs->OnMessageAvailable(IPC_SENDER_ANY, nsID(), 0, 0); + if (rv != IPC_WAIT_NEXT_MESSAGE) + data->senderDead = PR_TRUE; + + return rv; + } +#endif /* VBOX */ + + // process the specially forwarded client state message to see if the + // sender we're waiting a message from has died. + + if (msg->Target().Equals(IPCM_TARGET)) + { + switch (IPCM_GetType(msg)) + { + case IPCM_MSG_PSH_CLIENT_STATE: + { + ipcMessageCast<ipcmMessageClientState> status(msg); + if ((data->senderID == IPC_SENDER_ANY || + status->ClientID() == data->senderID) && + status->ClientState() == IPCM_CLIENT_STATE_DOWN) + { + LOG(("sender (%d) we're waiting a message from (%d) has died\n", + status->ClientID(), data->senderID)); + + if (data->senderID != IPC_SENDER_ANY) + { + // we're waiting on a particular client, so IPC_WaitMessage must + // definitely fail with the NS_ERROR_xxx result. + + data->senderDead = PR_TRUE; + return IPC_DISCARD_MESSAGE; // consume the message + } + else + { + // otherwise inform the observer about the client death using a special + // null message with an empty target id, and fail IPC_WaitMessage call + // with NS_ERROR_xxx only if the observer accepts this message. + + ipcIMessageObserver *obs = data->observer; + if (!obs) + obs = td->observer; + NS_ASSERTION(obs, "must at least have a default observer"); + + nsresult rv = obs->OnMessageAvailable(status->ClientID(), nsID(), 0, 0); + if (rv != IPC_WAIT_NEXT_MESSAGE) + data->senderDead = PR_TRUE; + + return IPC_DISCARD_MESSAGE; // consume the message + } + } +#ifdef VBOX + else if ((data->senderID == IPC_SENDER_ANY || + status->ClientID() == data->senderID) && + status->ClientState() == IPCM_CLIENT_STATE_UP) + { + LOG(("sender (%d) we're waiting a message from (%d) has come up\n", + status->ClientID(), data->senderID)); + if (data->senderID == IPC_SENDER_ANY) + { + // inform the observer about the client appearance using a special + // null message with an empty target id, but a length of 1. + + ipcIMessageObserver *obs = data->observer; + if (!obs) + obs = td->observer; + NS_ASSERTION(obs, "must at least have a default observer"); + + nsresult rv = obs->OnMessageAvailable(status->ClientID(), nsID(), 0, 1); + /* VBoxSVC/VBoxXPCOMIPCD auto-start can cause that a client up + * message arrives while we're already waiting for a response + * from this client. Don't declare the connection as dead in + * this case. A client ID wraparound can't falsely trigger + * this, since the waiting thread would have hit the liveness + * check in the mean time. We MUST consume the message, otherwise + * IPCM messages pile up as long as there is a pending call, which + * can lead to severe processing overhead. */ + return IPC_DISCARD_MESSAGE; // consume the message + } + } +#endif /* VBOX */ + break; + } + default: + NS_NOTREACHED("unexpected message"); + } + return IPC_WAIT_NEXT_MESSAGE; // continue iterating + } + + nsresult rv = IPC_WAIT_NEXT_MESSAGE; + + if (data->senderID == IPC_SENDER_ANY || + msg->mMetaData == data->senderID) + { + ipcIMessageObserver *obs = data->observer; + if (!obs) + obs = td->observer; + NS_ASSERTION(obs, "must at least have a default observer"); + + rv = obs->OnMessageAvailable(msg->mMetaData, + msg->Target(), + (const PRUint8 *) msg->Data(), + msg->DataLen()); + } + + // stop iterating if we got a match that the observer accepted. + return rv != IPC_WAIT_NEXT_MESSAGE ? NS_OK : IPC_WAIT_NEXT_MESSAGE; +} + +nsresult +IPC_WaitMessage(PRUint32 aSenderID, + const nsID &aTarget, + ipcIMessageObserver *aObserver, + ipcIMessageObserver *aConsumer, + PRIntervalTime aTimeout) +{ + NS_ENSURE_TRUE(gClientState, NS_ERROR_NOT_INITIALIZED); + + // do not permit waiting for IPCM messages + if (aTarget.Equals(IPCM_TARGET)) + return NS_ERROR_INVALID_ARG; + + // use aObserver as the message selector + WaitMessageSelectorData data = { aSenderID, aObserver, PR_FALSE }; + + ipcMessage *msg; + nsresult rv = WaitTarget(aTarget, aTimeout, &msg, WaitMessageSelector, &data); + if (NS_FAILED(rv)) + return rv; + + // if the selector has accepted some message, then we pass it to aConsumer + // for safe processing. The IPC susbsystem is quite stable here (i.e. we're + // not inside any of the monitors, and the message has been already removed + // from the pending queue). + if (aObserver && aConsumer) + { + aConsumer->OnMessageAvailable(msg->mMetaData, + msg->Target(), + (const PRUint8 *) msg->Data(), + msg->DataLen()); + } + + delete msg; + + // if the requested sender has died while waiting, return an error + if (data.senderDead) + return NS_ERROR_ABORT; // XXX better error code? + + return NS_OK; +} + +/* ------------------------------------------------------------------------- */ + +nsresult +IPC_GetID(PRUint32 *aClientID) +{ + NS_ENSURE_TRUE(gClientState, NS_ERROR_NOT_INITIALIZED); + + *aClientID = gClientState->selfID; + return NS_OK; +} + +nsresult +IPC_AddName(const char *aName) +{ + NS_ENSURE_TRUE(gClientState, NS_ERROR_NOT_INITIALIZED); + + return MakeIPCMRequest(new ipcmMessageClientAddName(aName)); +} + +nsresult +IPC_RemoveName(const char *aName) +{ + NS_ENSURE_TRUE(gClientState, NS_ERROR_NOT_INITIALIZED); + + return MakeIPCMRequest(new ipcmMessageClientDelName(aName)); +} + +/* ------------------------------------------------------------------------- */ + +nsresult +IPC_AddClientObserver(ipcIClientObserver *aObserver) +{ + NS_ENSURE_TRUE(gClientState, NS_ERROR_NOT_INITIALIZED); + + return gClientState->clientObservers.AppendObject(aObserver) + ? NS_OK : NS_ERROR_OUT_OF_MEMORY; +} + +nsresult +IPC_RemoveClientObserver(ipcIClientObserver *aObserver) +{ + NS_ENSURE_TRUE(gClientState, NS_ERROR_NOT_INITIALIZED); + + for (PRInt32 i = 0; i < gClientState->clientObservers.Count(); ++i) + { + if (gClientState->clientObservers[i] == aObserver) + gClientState->clientObservers.RemoveObjectAt(i); + } + + return NS_OK; +} + +/* ------------------------------------------------------------------------- */ + +// this function could be called on any thread +nsresult +IPC_ResolveClientName(const char *aName, PRUint32 *aClientID) +{ + NS_ENSURE_TRUE(gClientState, NS_ERROR_NOT_INITIALIZED); + + ipcMessage *msg = NULL; + + nsresult rv = MakeIPCMRequest(new ipcmMessageQueryClientByName(aName), &msg); + if (NS_FAILED(rv)) + { +#ifdef VBOX /* MakeIPCMRequest may return a failure (e.g. NS_ERROR_CALL_FAILED) and a response msg. */ + if (msg) + delete msg; +#endif + return rv; + } + + if (IPCM_GetType(msg) == IPCM_MSG_ACK_CLIENT_ID) + *aClientID = ipcMessageCast<ipcmMessageClientID>(msg)->ClientID(); + else + { + LOG(("unexpected IPCM response: type=%x\n", IPCM_GetType(msg))); + rv = NS_ERROR_UNEXPECTED; + } + + delete msg; + return rv; +} + +/* ------------------------------------------------------------------------- */ + +nsresult +IPC_ClientExists(PRUint32 aClientID, PRBool *aResult) +{ + // this is a bit of a hack. we forward a PING to the specified client. + // the assumption is that the forwarding will only succeed if the client + // exists, so we wait for the RESULT message corresponding to the FORWARD + // request. if that gives a successful status, then we know that the + // client exists. + + ipcmMessagePing ping; + + return MakeIPCMRequest(new ipcmMessageForward(IPCM_MSG_REQ_FORWARD, + aClientID, + IPCM_TARGET, + ping.Data(), + ping.DataLen())); +} + +/* ------------------------------------------------------------------------- */ + +nsresult +IPC_SpawnDaemon(const char *path) +{ + PRFileDesc *readable = nsnull, *writable = nsnull; + PRProcessAttr *attr = nsnull; + nsresult rv = NS_ERROR_FAILURE; + PRFileDesc *devNull; + char *const argv[] = { (char *const) path, nsnull }; + char c; + + // setup an anonymous pipe that we can use to determine when the daemon + // process has started up. the daemon will write a char to the pipe, and + // when we read it, we'll know to proceed with trying to connect to the + // daemon. + + if (PR_CreatePipe(&readable, &writable) != PR_SUCCESS) + goto end; + PR_SetFDInheritable(writable, PR_TRUE); + + attr = PR_NewProcessAttr(); + if (!attr) + goto end; + + if (PR_ProcessAttrSetInheritableFD(attr, writable, IPC_STARTUP_PIPE_NAME) != PR_SUCCESS) + goto end; + + devNull = PR_Open("/dev/null", PR_RDWR, 0); + if (!devNull) + goto end; + + PR_ProcessAttrSetStdioRedirect(attr, PR_StandardInput, devNull); + PR_ProcessAttrSetStdioRedirect(attr, PR_StandardOutput, devNull); + PR_ProcessAttrSetStdioRedirect(attr, PR_StandardError, devNull); + + if (PR_CreateProcessDetached(path, argv, nsnull, attr) != PR_SUCCESS) + goto end; + + // Close /dev/null + PR_Close(devNull); + // close the child end of the pipe in order to get notification on unexpected + // child termination instead of being infinitely blocked in PR_Read(). + PR_Close(writable); + writable = nsnull; + + if ((PR_Read(readable, &c, 1) != 1) || (c != IPC_STARTUP_PIPE_MAGIC)) + goto end; + + rv = NS_OK; +end: + if (readable) + PR_Close(readable); + if (writable) + PR_Close(writable); + if (attr) + PR_DestroyProcessAttr(attr); + return rv; +} + +/* ------------------------------------------------------------------------- */ + +PR_STATIC_CALLBACK(PLDHashOperator) +EnumerateTargetMapAndNotify(const nsID &aKey, + ipcTargetData *aData, + void *aClosure) +{ + nsAutoMonitor mon(aData->monitor); + + // wake up anyone waiting on this target. + mon.NotifyAll(); + + return PL_DHASH_NEXT; +} + +// called on a background thread +void +IPC_OnConnectionEnd(nsresult error) +{ + // now, go through the target map, and tickle each monitor. that should + // unblock any calls to WaitTarget. + +#ifndef VBOX_WITH_IPCCLIENT_RW_CS + nsAutoMonitor mon(gClientState->monitor); +#else + RTCritSectRwEnterExcl(&gClientState->critSect); +#endif + + gClientState->connected = PR_FALSE; + gClientState->targetMap.EnumerateRead(EnumerateTargetMapAndNotify, nsnull); + +#ifdef VBOX_WITH_IPCCLIENT_RW_CS + RTCritSectRwLeaveExcl(&gClientState->critSect); +#endif +} + +/* ------------------------------------------------------------------------- */ + +static void +PlaceOnPendingQ(const nsID &target, ipcTargetData *td, ipcMessage *msg) +{ + nsAutoMonitor mon(td->monitor); + + // we only want to dispatch a 'ProcessPendingQ' event if we have not + // already done so. + PRBool dispatchEvent = td->pendingQ.IsEmpty(); + + // put this message on our pending queue + td->pendingQ.Append(msg); + +#ifdef IPC_LOGGING + if (IPC_LOG_ENABLED()) + { + char *targetStr = target.ToString(); + LOG(("placed message on pending queue for target %s and notifying all...\n", targetStr)); + nsMemory::Free(targetStr); + } +#endif + + // wake up anyone waiting on this queue + mon.NotifyAll(); + + // proxy call to target's message procedure + if (dispatchEvent) + CallProcessPendingQ(target, td); +} + +PR_STATIC_CALLBACK(PLDHashOperator) +EnumerateTargetMapAndPlaceMsg(const nsID &aKey, + ipcTargetData *aData, + void *userArg) +{ + if (!aKey.Equals(IPCM_TARGET)) + { + // place a message clone to a target's event queue + ipcMessage *msg = (ipcMessage *) userArg; + PlaceOnPendingQ(aKey, aData, msg->Clone()); + } + + return PL_DHASH_NEXT; +} + +/* ------------------------------------------------------------------------- */ + +#ifdef IPC_LOGGING +#include "prprf.h" +#include <ctype.h> +#endif + +// called on a background thread +void +IPC_OnMessageAvailable(ipcMessage *msg) +{ +#ifdef IPC_LOGGING + if (IPC_LOG_ENABLED()) + { + char *targetStr = msg->Target().ToString(); + LOG(("got message for target: %s\n", targetStr)); + nsMemory::Free(targetStr); + +// IPC_LogBinary((const PRUint8 *) msg->Data(), msg->DataLen()); + } +#endif + + if (msg->Target().Equals(IPCM_TARGET)) + { + switch (IPCM_GetType(msg)) + { + // if this is a forwarded message, then post the inner message instead. + case IPCM_MSG_PSH_FORWARD: + { + ipcMessageCast<ipcmMessageForward> fwd(msg); + ipcMessage *innerMsg = new ipcMessage(fwd->InnerTarget(), + fwd->InnerData(), + fwd->InnerDataLen()); + // store the sender's client id in the meta-data field of the message. + innerMsg->mMetaData = fwd->ClientID(); + + delete msg; + + // recurse so we can handle forwarded IPCM messages + IPC_OnMessageAvailable(innerMsg); + return; + } + case IPCM_MSG_PSH_CLIENT_STATE: + { + ipcMessageCast<ipcmMessageClientState> status(msg); + PostEventToMainThread(new ipcEvent_ClientState(status->ClientID(), + status->ClientState())); + + // go through the target map, and place this message to every target's + // pending event queue. that unblocks all WaitTarget calls (on all + // targets) giving them an opportuninty to finish wait cycle because of + // the peer client death, when appropriate. +#ifndef VBOX_WITH_IPCCLIENT_RW_CS + nsAutoMonitor mon(gClientState->monitor); +#else + RTCritSectRwEnterShared(&gClientState->critSect); +#endif + + gClientState->targetMap.EnumerateRead(EnumerateTargetMapAndPlaceMsg, msg); + +#ifdef VBOX_WITH_IPCCLIENT_RW_CS + RTCritSectRwLeaveShared(&gClientState->critSect); +#endif + delete msg; + + return; + } + } + } + + nsRefPtr<ipcTargetData> td; + if (GetTarget(msg->Target(), getter_AddRefs(td))) + { + // make copy of target since |msg| may end up pointing to free'd memory + // once we notify the monitor inside PlaceOnPendingQ(). + const nsID target = msg->Target(); + + PlaceOnPendingQ(target, td, msg); + } + else + { + NS_WARNING("message target is undefined"); +#ifdef VBOX + delete msg; +#endif + } +} diff --git a/src/libs/xpcom18a4/ipc/ipcd/daemon/public/.cvsignore b/src/libs/xpcom18a4/ipc/ipcd/daemon/public/.cvsignore new file mode 100644 index 00000000..f3c7a7c5 --- /dev/null +++ b/src/libs/xpcom18a4/ipc/ipcd/daemon/public/.cvsignore @@ -0,0 +1 @@ +Makefile diff --git a/src/libs/xpcom18a4/ipc/ipcd/daemon/public/Makefile.in b/src/libs/xpcom18a4/ipc/ipcd/daemon/public/Makefile.in new file mode 100644 index 00000000..ad1a66b7 --- /dev/null +++ b/src/libs/xpcom18a4/ipc/ipcd/daemon/public/Makefile.in @@ -0,0 +1,52 @@ +# vim: noexpandtab ts=4 sw=4 +# ***** 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 = ipcd +EXPORTS = \ + ipcModule.h \ + ipcModuleUtil.h \ + $(NULL) + +include $(topsrcdir)/config/rules.mk diff --git a/src/libs/xpcom18a4/ipc/ipcd/daemon/public/ipcModule.h b/src/libs/xpcom18a4/ipc/ipcd/daemon/public/ipcModule.h new file mode 100644 index 00000000..2f299f70 --- /dev/null +++ b/src/libs/xpcom18a4/ipc/ipcd/daemon/public/ipcModule.h @@ -0,0 +1,240 @@ +/* ***** 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 ***** */ + +#ifndef ipcModule_h__ +#define ipcModule_h__ + +#include "nsID.h" + +// +// a client handle is used to efficiently reference a client instance object +// used by the daemon to represent a connection with a particular client app. +// +// modules should treat it as an opaque type. +// +typedef class ipcClient *ipcClientHandle; + +//----------------------------------------------------------------------------- +// interface implemented by the module: +//----------------------------------------------------------------------------- + +// +// the version of ipcModuleMethods data structure. +// +#define IPC_MODULE_METHODS_VERSION (1<<16) // 1.0 + +// +// each module defines the following structure: +// +struct ipcModuleMethods +{ + // + // this field holds the version of the data structure, which is always the + // value of IPC_MODULE_METHODS_VERSION against which the module was built. + // + PRUint32 version; + + // + // called after this module is registered. + // + void (* init) (void); + + // + // called when this module will no longer be accessed. + // + void (* shutdown) (void); + + // + // called when a new message arrives for this module. + // + // params: + // client - an opaque "handle" to an object representing the client that + // sent the message. modules should not store the value of this + // beyond the duration fo this function call. (e.g., the handle + // may be invalid after this function call returns.) modules + // wishing to hold onto a reference to a "client" should store + // the client's ID (see IPC_GetClientID). + // target - message target + // data - message data + // dataLen - message data length + // + void (* handleMsg) (ipcClientHandle client, + const nsID &target, + const void *data, + PRUint32 dataLen); + + // + // called when a new client connects to the IPC daemon. + // + void (* clientUp) (ipcClientHandle client); + + // + // called when a client disconnects from the IPC daemon. + // + void (* clientDown) (ipcClientHandle client); +}; + +//----------------------------------------------------------------------------- +// interface implemented by the daemon: +//----------------------------------------------------------------------------- + +// +// the version of ipcDaemonMethods data structure. +// +#define IPC_DAEMON_METHODS_VERSION (1<<16) // 1.0 + +// +// enumeration functions may return FALSE to stop enumeration. +// +typedef PRBool (* ipcClientEnumFunc) (void *closure, ipcClientHandle client, PRUint32 clientID); +typedef PRBool (* ipcClientNameEnumFunc) (void *closure, ipcClientHandle client, const char *name); +typedef PRBool (* ipcClientTargetEnumFunc) (void *closure, ipcClientHandle client, const nsID &target); + +// +// the daemon provides the following structure: +// +struct ipcDaemonMethods +{ + PRUint32 version; + + // + // called to send a message to another module. + // + // params: + // client - identifies the client from which this message originated. + // target - message target + // data - message data + // dataLen - message data length + // + // returns: + // PR_SUCCESS if message was dispatched. + // PR_FAILURE if message could not be dispatched (possibly because + // no module is registered for the given message target). + // + PRStatus (* dispatchMsg) (ipcClientHandle client, + const nsID &target, + const void *data, + PRUint32 dataLen); + + // + // called to send a message to a particular client or to broadcast a + // message to all clients. + // + // params: + // client - if null, then broadcast message to all clients. otherwise, + // send message to the client specified. + // target - message target + // data - message data + // dataLen - message data length + // + // returns: + // PR_SUCCESS if message was sent (or queued up to be sent later). + // PR_FAILURE if message could not be sent (possibly because the client + // does not have a registered observer for the msg's target). + // + PRStatus (* sendMsg) (ipcClientHandle client, + const nsID &target, + const void *data, + PRUint32 dataLen); + + // + // called to lookup a client handle given its client ID. each client has + // a unique ID. + // + ipcClientHandle (* getClientByID) (PRUint32 clientID); + + // + // called to lookup a client by name or alias. names are not necessary + // unique to individual clients. this function returns the client first + // registered under the given name. + // + ipcClientHandle (* getClientByName) (const char *name); + + // + // called to enumerate all clients. + // + void (* enumClients) (ipcClientEnumFunc func, void *closure); + + // + // returns the client ID of the specified client. + // + PRUint32 (* getClientID) (ipcClientHandle client); + + // + // functions for inspecting the names and targets defined for a particular + // client instance. + // + PRBool (* clientHasName) (ipcClientHandle client, const char *name); + PRBool (* clientHasTarget) (ipcClientHandle client, const nsID &target); + void (* enumClientNames) (ipcClientHandle client, ipcClientNameEnumFunc func, void *closure); + void (* enumClientTargets) (ipcClientHandle client, ipcClientTargetEnumFunc func, void *closure); +}; + +//----------------------------------------------------------------------------- +// interface exported by a DSO implementing one or more modules: +//----------------------------------------------------------------------------- + +struct ipcModuleEntry +{ + // + // identifies the message target of this module. + // + nsID target; + + // + // module methods + // + ipcModuleMethods *methods; +}; + +//----------------------------------------------------------------------------- + +#define IPC_EXPORT extern "C" NS_EXPORT + +// +// IPC_EXPORT int IPC_GetModules(const ipcDaemonMethods *, const ipcModuleEntry **); +// +// params: +// methods - the daemon's methods +// entries - the module entries defined by the DSO +// +// returns: +// length of the |entries| array. +// +typedef int (* ipcGetModulesFunc) (const ipcDaemonMethods *methods, const ipcModuleEntry **entries); + +#endif // !ipcModule_h__ diff --git a/src/libs/xpcom18a4/ipc/ipcd/daemon/public/ipcModuleUtil.h b/src/libs/xpcom18a4/ipc/ipcd/daemon/public/ipcModuleUtil.h new file mode 100644 index 00000000..0d1703a8 --- /dev/null +++ b/src/libs/xpcom18a4/ipc/ipcd/daemon/public/ipcModuleUtil.h @@ -0,0 +1,151 @@ +/* ***** 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 ***** */ + +#ifndef ipcModuleUtil_h__ +#define ipcModuleUtil_h__ + +#include "prlog.h" +#include "ipcModule.h" + +extern const ipcDaemonMethods *gIPCDaemonMethods; + +//----------------------------------------------------------------------------- +// inline wrapper functions +// +// these functions may only be called by a module that uses the +// IPC_IMPL_GETMODULES macro. +//----------------------------------------------------------------------------- + +inline PRStatus +IPC_DispatchMsg(ipcClientHandle client, const nsID &target, const void *data, PRUint32 dataLen) +{ + PR_ASSERT(gIPCDaemonMethods); + return gIPCDaemonMethods->dispatchMsg(client, target, data, dataLen); +} + +inline PRStatus +IPC_SendMsg(ipcClientHandle client, const nsID &target, const void *data, PRUint32 dataLen) +{ + PR_ASSERT(gIPCDaemonMethods); + return gIPCDaemonMethods->sendMsg(client, target, data, dataLen); +} + +inline ipcClientHandle +IPC_GetClientByID(PRUint32 id) +{ + PR_ASSERT(gIPCDaemonMethods); + return gIPCDaemonMethods->getClientByID(id); +} + +inline ipcClientHandle +IPC_GetClientByName(const char *name) +{ + PR_ASSERT(gIPCDaemonMethods); + return gIPCDaemonMethods->getClientByName(name); +} + +inline void +IPC_EnumClients(ipcClientEnumFunc func, void *closure) +{ + PR_ASSERT(gIPCDaemonMethods); + gIPCDaemonMethods->enumClients(func, closure); +} + +inline PRUint32 +IPC_GetClientID(ipcClientHandle client) +{ + PR_ASSERT(gIPCDaemonMethods); + return gIPCDaemonMethods->getClientID(client); +} + +inline PRBool +IPC_ClientHasName(ipcClientHandle client, const char *name) +{ + PR_ASSERT(gIPCDaemonMethods); + return gIPCDaemonMethods->clientHasName(client, name); +} + +inline PRBool +IPC_ClientHasTarget(ipcClientHandle client, const nsID &target) +{ + PR_ASSERT(gIPCDaemonMethods); + return gIPCDaemonMethods->clientHasTarget(client, target); +} + +inline void +IPC_EnumClientNames(ipcClientHandle client, ipcClientNameEnumFunc func, void *closure) +{ + PR_ASSERT(gIPCDaemonMethods); + gIPCDaemonMethods->enumClientNames(client, func, closure); +} + +inline void +IPC_EnumClientTargets(ipcClientHandle client, ipcClientTargetEnumFunc func, void *closure) +{ + PR_ASSERT(gIPCDaemonMethods); + gIPCDaemonMethods->enumClientTargets(client, func, closure); +} + +//----------------------------------------------------------------------------- +// inline composite functions +//----------------------------------------------------------------------------- + +inline PRStatus +IPC_SendMsg(PRUint32 clientID, const nsID &target, const void *data, PRUint32 dataLen) +{ + ipcClient *client = IPC_GetClientByID(clientID); + if (!client) + return PR_FAILURE; + return IPC_SendMsg(client, target, data, dataLen); +} + +//----------------------------------------------------------------------------- +// module factory macros +//----------------------------------------------------------------------------- + +#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__ diff --git a/src/libs/xpcom18a4/ipc/ipcd/daemon/src/.cvsignore b/src/libs/xpcom18a4/ipc/ipcd/daemon/src/.cvsignore new file mode 100644 index 00000000..8d974b7c --- /dev/null +++ b/src/libs/xpcom18a4/ipc/ipcd/daemon/src/.cvsignore @@ -0,0 +1,2 @@ +Makefile +mozilla-ipcd diff --git a/src/libs/xpcom18a4/ipc/ipcd/daemon/src/Makefile.in b/src/libs/xpcom18a4/ipc/ipcd/daemon/src/Makefile.in new file mode 100644 index 00000000..e9ef6c38 --- /dev/null +++ b/src/libs/xpcom18a4/ipc/ipcd/daemon/src/Makefile.in @@ -0,0 +1,88 @@ +# vim: noexpandtab ts=4 sw=4 +# ***** 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 = ipcd + +# required for #include "nsID.h" +REQUIRES = \ + xpcom \ + $(NULL) + +CPPSRCS = \ + ipcd.cpp \ + ipcClient.cpp \ + ipcModuleReg.cpp \ + ipcCommandModule.cpp + +ifeq ($(OS_ARCH),WINNT) +CPPSRCS += ipcdWin.cpp +else +ifeq ($(OS_ARCH),BeOS) +CPPSRCS += ipcdStub.cpp +else +CPPSRCS += ipcdUnix.cpp +endif +endif + +PROGRAM = mozilla-ipcd$(BIN_SUFFIX) + +LOCAL_INCLUDES = \ + -I$(srcdir)/../../shared/src \ + $(NULL) + +include $(topsrcdir)/config/config.mk + +LIBS = \ + $(DIST)/lib/$(LIB_PREFIX)ipcdshared_s.$(LIB_SUFFIX) \ + $(EXTRA_DSO_LIBS) \ + $(NSPR_LIBS) \ + $(NULL) + +include $(topsrcdir)/config/rules.mk + +# For fruncate +ifeq ($(OS_ARCH),Linux) +DEFINES += -D_BSD_SOURCE +endif diff --git a/src/libs/xpcom18a4/ipc/ipcd/daemon/src/ipcClient.cpp b/src/libs/xpcom18a4/ipc/ipcd/daemon/src/ipcClient.cpp new file mode 100644 index 00000000..4f68d175 --- /dev/null +++ b/src/libs/xpcom18a4/ipc/ipcd/daemon/src/ipcClient.cpp @@ -0,0 +1,235 @@ +/* ***** 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 "ipcLog.h" +#include "ipcClient.h" +#include "ipcMessage.h" +#include "ipcModuleReg.h" +#include "ipcd.h" +#include "ipcm.h" + +#if defined(XP_UNIX) || defined(XP_OS2) +#include "prio.h" +#endif + +PRUint32 ipcClient::gLastID = 0; + +// +// called to initialize this client context +// +// assumptions: +// - object's memory has already been zero'd out. +// +void +ipcClient::Init() +{ + mID = ++gLastID; + + // every client must be able to handle IPCM messages. + mTargets.Append(IPCM_TARGET); + + // although it is tempting to fire off the NotifyClientUp event at this + // time, we must wait until the client sends us a CLIENT_HELLO event. + // see ipcCommandModule::OnClientHello. +} + +// +// called when this client context is going away +// +void +ipcClient::Finalize() +{ + IPC_NotifyClientDown(this); + + mNames.DeleteAll(); + mTargets.DeleteAll(); + +#if defined(XP_UNIX) || defined(XP_OS2) + mInMsg.Reset(); + mOutMsgQ.DeleteAll(); +#endif +} + +void +ipcClient::AddName(const char *name) +{ + LOG(("adding client name: %s\n", name)); + + if (HasName(name)) + return; + + mNames.Append(name); +} + +PRBool +ipcClient::DelName(const char *name) +{ + LOG(("deleting client name: %s\n", name)); + + return mNames.FindAndDelete(name); +} + +void +ipcClient::AddTarget(const nsID &target) +{ + LOG(("adding client target\n")); + + if (HasTarget(target)) + return; + + mTargets.Append(target); +} + +PRBool +ipcClient::DelTarget(const nsID &target) +{ + LOG(("deleting client target\n")); + + // + // cannot remove the IPCM target + // + if (!target.Equals(IPCM_TARGET)) + return mTargets.FindAndDelete(target); + + return PR_FALSE; +} + +#if defined(XP_UNIX) || defined(XP_OS2) + +// +// called to process a client socket +// +// params: +// fd - the client socket +// poll_flags - the state of the client socket +// +// return: +// 0 - to end session with this client +// PR_POLL_READ - to wait for the client socket to become readable +// PR_POLL_WRITE - to wait for the client socket to become writable +// +int +ipcClient::Process(PRFileDesc *fd, int inFlags) +{ + if (inFlags & (PR_POLL_ERR | PR_POLL_HUP | + PR_POLL_EXCEPT | PR_POLL_NVAL)) { + LOG(("client socket appears to have closed\n")); + return 0; + } + + // expect to wait for more data + int outFlags = PR_POLL_READ; + + if (inFlags & PR_POLL_READ) { + LOG(("client socket is now readable\n")); + + char buf[1024]; // XXX make this larger? + PRInt32 n; + + // find out how much data is available for reading... + // n = PR_Available(fd); + + n = PR_Read(fd, buf, sizeof(buf)); + if (n <= 0) + return 0; // cancel connection + + const char *ptr = buf; + while (n) { + PRUint32 nread; + PRBool complete; + + if (mInMsg.ReadFrom(ptr, PRUint32(n), &nread, &complete) == PR_FAILURE) { + LOG(("message appears to be malformed; dropping client connection\n")); + return 0; + } + + if (complete) { + IPC_DispatchMsg(this, &mInMsg); + mInMsg.Reset(); + } + + n -= nread; + ptr += nread; + } + } + + if (inFlags & PR_POLL_WRITE) { + LOG(("client socket is now writable\n")); + + if (mOutMsgQ.First()) + WriteMsgs(fd); + } + + if (mOutMsgQ.First()) + outFlags |= PR_POLL_WRITE; + + return outFlags; +} + +// +// called to write out any messages from the outgoing queue. +// +int +ipcClient::WriteMsgs(PRFileDesc *fd) +{ + while (mOutMsgQ.First()) { + const char *buf = (const char *) mOutMsgQ.First()->MsgBuf(); + PRInt32 bufLen = (PRInt32) mOutMsgQ.First()->MsgLen(); + + if (mSendOffset) { + buf += mSendOffset; + bufLen -= mSendOffset; + } + + PRInt32 nw = PR_Write(fd, buf, bufLen); + if (nw <= 0) + break; + + LOG(("wrote %d bytes\n", nw)); + + if (nw == bufLen) { + mOutMsgQ.DeleteFirst(); + mSendOffset = 0; + } + else + mSendOffset += nw; + } + + return 0; +} + +#endif diff --git a/src/libs/xpcom18a4/ipc/ipcd/daemon/src/ipcClient.h b/src/libs/xpcom18a4/ipc/ipcd/daemon/src/ipcClient.h new file mode 100644 index 00000000..12e479cf --- /dev/null +++ b/src/libs/xpcom18a4/ipc/ipcd/daemon/src/ipcClient.h @@ -0,0 +1,144 @@ +/* ***** 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 ***** */ + +#ifndef ipcClientUnix_h__ +#define ipcClientUnix_h__ + +#include "prio.h" +#include "ipcMessageQ.h" +#include "ipcStringList.h" +#include "ipcIDList.h" + +#ifdef XP_WIN +#include <windows.h> +#endif + +//----------------------------------------------------------------------------- +// ipcClient +// +// NOTE: this class is an implementation detail of the IPC daemon. IPC daemon +// modules (other than the built-in IPCM module) must not access methods on +// this class directly. use the API provided via ipcd.h instead. +//----------------------------------------------------------------------------- + +class ipcClient +{ +public: + void Init(); + void Finalize(); + + PRUint32 ID() const { return mID; } + + void AddName(const char *name); + PRBool DelName(const char *name); + PRBool HasName(const char *name) const { return mNames.Find(name) != NULL; } + + void AddTarget(const nsID &target); + PRBool DelTarget(const nsID &target); + PRBool HasTarget(const nsID &target) const { return mTargets.Find(target) != NULL; } + + // list iterators + const ipcStringNode *Names() const { return mNames.First(); } + const ipcIDNode *Targets() const { return mTargets.First(); } + + // returns primary client name (the one specified in the "client hello" message) + const char *PrimaryName() const { return mNames.First() ? mNames.First()->Value() : NULL; } + + void SetExpectsSyncReply(PRBool val) { mExpectsSyncReply = val; } + PRBool GetExpectsSyncReply() const { return mExpectsSyncReply; } + +#ifdef XP_WIN + PRUint32 PID() const { return mPID; } + void SetPID(PRUint32 pid) { mPID = pid; } + + HWND Hwnd() const { return mHwnd; } + void SetHwnd(HWND hwnd) { mHwnd = hwnd; } +#endif + +#if defined(XP_UNIX) || defined(XP_OS2) + // + // called to process a client file descriptor. the value of pollFlags + // indicates the state of the socket. + // + // returns: + // 0 - to cancel client connection + // PR_POLL_READ - to poll for a readable socket + // PR_POLL_WRITE - to poll for a writable socket + // (both flags) - to poll for either a readable or writable socket + // + // the socket is non-blocking. + // + int Process(PRFileDesc *sockFD, int pollFlags); + + // + // on success or failure, this function takes ownership of |msg| and will + // delete it when appropriate. + // + void EnqueueOutboundMsg(ipcMessage *msg) { mOutMsgQ.Append(msg); } +#endif + +private: + static PRUint32 gLastID; + + PRUint32 mID; + ipcStringList mNames; + ipcIDList mTargets; + PRBool mExpectsSyncReply; + +#ifdef XP_WIN + // on windows, we store the PID of the client process to help us determine + // the client from which a message originated. each message has the PID + // encoded in it. + PRUint32 mPID; + + // the hwnd of the client's message window. + HWND mHwnd; +#endif + +#if defined(XP_UNIX) || defined(XP_OS2) + ipcMessage mInMsg; // buffer for incoming message + ipcMessageQ mOutMsgQ; // outgoing message queue + + // keep track of the amount of the first message sent + PRUint32 mSendOffset; + + // utility function for writing out messages. + int WriteMsgs(PRFileDesc *fd); +#endif +}; + +#endif // !ipcClientUnix_h__ diff --git a/src/libs/xpcom18a4/ipc/ipcd/daemon/src/ipcCommandModule.cpp b/src/libs/xpcom18a4/ipc/ipcd/daemon/src/ipcCommandModule.cpp new file mode 100644 index 00000000..43ac547c --- /dev/null +++ b/src/libs/xpcom18a4/ipc/ipcd/daemon/src/ipcCommandModule.cpp @@ -0,0 +1,316 @@ +/* ***** 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 <stdlib.h> +#include <string.h> +#include "ipcLog.h" +#include "ipcCommandModule.h" +#include "ipcModule.h" +#include "ipcClient.h" +#include "ipcMessage.h" +#include "ipcMessageUtils.h" +#include "ipcModuleReg.h" +#include "ipcd.h" +#include "ipcm.h" + +struct ipcCommandModule +{ + typedef void (* MsgHandler)(ipcClient *, const ipcMessage *); + + // + // helpers + // + + static char ** + BuildStringArray(const ipcStringNode *nodes) + { + size_t count = 0; + + const ipcStringNode *node; + + for (node = nodes; node; node = node->mNext) + count++; + + char **strs = (char **) malloc((count + 1) * sizeof(char *)); + if (!strs) + return NULL; + + count = 0; + for (node = nodes; node; node = node->mNext, ++count) + strs[count] = (char *) node->Value(); + strs[count] = 0; + + return strs; + } + + static nsID ** + BuildIDArray(const ipcIDNode *nodes) + { + size_t count = 0; + + const ipcIDNode *node; + + for (node = nodes; node; node = node->mNext) + count++; + + nsID **ids = (nsID **) calloc(count + 1, sizeof(nsID *)); + if (!ids) + return NULL; + + count = 0; + for (node = nodes; node; node = node->mNext, ++count) + ids[count] = (nsID *) &node->Value(); + + return ids; + } + + // + // message handlers + // + + static void + OnPing(ipcClient *client, const ipcMessage *rawMsg) + { + LOG(("got PING\n")); + + IPC_SendMsg(client, new ipcmMessageResult(IPCM_GetRequestIndex(rawMsg), IPCM_OK)); + } + + static void + OnClientHello(ipcClient *client, const ipcMessage *rawMsg) + { + LOG(("got CLIENT_HELLO\n")); + + IPC_SendMsg(client, new ipcmMessageClientID(IPCM_GetRequestIndex(rawMsg), client->ID())); + + // + // NOTE: it would almost make sense for this notification to live + // in the transport layer code. however, clients expect to receive + // a CLIENT_ID as the first message following a CLIENT_HELLO, so we + // must not allow modules to see a client until after we have sent + // the CLIENT_ID message. + // + IPC_NotifyClientUp(client); + } + + static void + OnClientAddName(ipcClient *client, const ipcMessage *rawMsg) + { + LOG(("got CLIENT_ADD_NAME\n")); + + PRInt32 status = IPCM_OK; + PRUint32 requestIndex = IPCM_GetRequestIndex(rawMsg); + + ipcMessageCast<ipcmMessageClientAddName> msg(rawMsg); + const char *name = msg->Name(); + if (name) { + ipcClient *result = IPC_GetClientByName(msg->Name()); + if (result) { + LOG((" client with such name already exists (ID = %d)\n", result->ID())); + status = IPCM_ERROR_ALREADY_EXISTS; + } + else + client->AddName(name); + } + else + status = IPCM_ERROR_INVALID_ARG; + + IPC_SendMsg(client, new ipcmMessageResult(requestIndex, status)); + } + + static void + OnClientDelName(ipcClient *client, const ipcMessage *rawMsg) + { + LOG(("got CLIENT_DEL_NAME\n")); + + PRInt32 status = IPCM_OK; + PRUint32 requestIndex = IPCM_GetRequestIndex(rawMsg); + + ipcMessageCast<ipcmMessageClientDelName> msg(rawMsg); + const char *name = msg->Name(); + if (name) { + if (!client->DelName(name)) { + LOG((" client doesn't have name '%s'\n", name)); + status = IPCM_ERROR_NO_SUCH_DATA; + } + } + else + status = IPCM_ERROR_INVALID_ARG; + + IPC_SendMsg(client, new ipcmMessageResult(requestIndex, status)); + } + + static void + OnClientAddTarget(ipcClient *client, const ipcMessage *rawMsg) + { + LOG(("got CLIENT_ADD_TARGET\n")); + + PRInt32 status = IPCM_OK; + PRUint32 requestIndex = IPCM_GetRequestIndex(rawMsg); + + ipcMessageCast<ipcmMessageClientAddTarget> msg(rawMsg); + if (client->HasTarget(msg->Target())) { + LOG((" target already defined for client\n")); + status = IPCM_ERROR_ALREADY_EXISTS; + } + else + client->AddTarget(msg->Target()); + + IPC_SendMsg(client, new ipcmMessageResult(requestIndex, status)); + } + + static void + OnClientDelTarget(ipcClient *client, const ipcMessage *rawMsg) + { + LOG(("got CLIENT_DEL_TARGET\n")); + + PRInt32 status = IPCM_OK; + PRUint32 requestIndex = IPCM_GetRequestIndex(rawMsg); + + ipcMessageCast<ipcmMessageClientDelTarget> msg(rawMsg); + if (!client->DelTarget(msg->Target())) { + LOG((" client doesn't have the given target\n")); + status = IPCM_ERROR_NO_SUCH_DATA; + } + + IPC_SendMsg(client, new ipcmMessageResult(requestIndex, status)); + } + + static void + OnQueryClientByName(ipcClient *client, const ipcMessage *rawMsg) + { + LOG(("got QUERY_CLIENT_BY_NAME\n")); + + PRUint32 requestIndex = IPCM_GetRequestIndex(rawMsg); + + ipcMessageCast<ipcmMessageQueryClientByName> msg(rawMsg); + + ipcClient *result = IPC_GetClientByName(msg->Name()); + if (result) { + LOG((" client exists w/ ID = %u\n", result->ID())); + IPC_SendMsg(client, new ipcmMessageClientID(requestIndex, result->ID())); + } + else { + LOG((" client does not exist\n")); + IPC_SendMsg(client, new ipcmMessageResult(requestIndex, IPCM_ERROR_NO_CLIENT)); + } + } + +#if 0 + static void + OnQueryClientInfo(ipcClient *client, const ipcMessage *rawMsg) + { + LOG(("got QUERY_CLIENT_INFO\n")); + + ipcMessageCast<ipcmMessageQueryClientInfo> msg(rawMsg); + ipcClient *result = IPC_GetClientByID(msg->ClientID()); + if (result) { + char **names = BuildStringArray(result->Names()); + nsID **targets = BuildIDArray(result->Targets()); + + IPC_SendMsg(client, new ipcmMessageClientInfo(result->ID(), + msg->RequestIndex(), + (const char **) names, + (const nsID **) targets)); + + free(names); + free(targets); + } + else { + LOG((" client does not exist\n")); + IPC_SendMsg(client, new ipcmMessageError(IPCM_ERROR_NO_CLIENT, msg->RequestIndex())); + } + } +#endif + + static void + OnForward(ipcClient *client, const ipcMessage *rawMsg) + { + LOG(("got FORWARD\n")); + + ipcMessageCast<ipcmMessageForward> msg(rawMsg); + + ipcClient *dest = IPC_GetClientByID(msg->ClientID()); + if (!dest) { + LOG((" destination client not found!\n")); + IPC_SendMsg(client, new ipcmMessageResult(IPCM_GetRequestIndex(rawMsg), IPCM_ERROR_NO_CLIENT)); + return; + } + // inform client that its message will be forwarded + IPC_SendMsg(client, new ipcmMessageResult(IPCM_GetRequestIndex(rawMsg), IPCM_OK)); + + ipcMessage *newMsg = new ipcmMessageForward(IPCM_MSG_PSH_FORWARD, + client->ID(), + msg->InnerTarget(), + msg->InnerData(), + msg->InnerDataLen()); + IPC_SendMsg(dest, newMsg); + } +}; + +void +IPCM_HandleMsg(ipcClient *client, const ipcMessage *rawMsg) +{ + static ipcCommandModule::MsgHandler handlers[] = + { + ipcCommandModule::OnPing, + ipcCommandModule::OnForward, + ipcCommandModule::OnClientHello, + ipcCommandModule::OnClientAddName, + ipcCommandModule::OnClientDelName, + ipcCommandModule::OnClientAddTarget, + ipcCommandModule::OnClientDelTarget, + ipcCommandModule::OnQueryClientByName + }; + + int type = IPCM_GetType(rawMsg); + LOG(("IPCM_HandleMsg [type=%x]\n", type)); + + if (!(type & IPCM_MSG_CLASS_REQ)) { + LOG(("not a request -- ignoring message\n")); + return; + } + + type &= ~IPCM_MSG_CLASS_REQ; + type--; + if (type < 0 || type >= (int) (sizeof(handlers)/sizeof(handlers[0]))) { + LOG(("unknown request -- ignoring message\n")); + return; + } + + (handlers[type])(client, rawMsg); +} diff --git a/src/libs/xpcom18a4/ipc/ipcd/daemon/src/ipcCommandModule.h b/src/libs/xpcom18a4/ipc/ipcd/daemon/src/ipcCommandModule.h new file mode 100644 index 00000000..ea146cdc --- /dev/null +++ b/src/libs/xpcom18a4/ipc/ipcd/daemon/src/ipcCommandModule.h @@ -0,0 +1,48 @@ +/* ***** 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 ***** */ + +#ifndef ipcCommandModule_h__ +#define ipcCommandModule_h__ + +#include "ipcm.h" // for IPCM_TARGET + +class ipcClient; +class ipcMessage; + +void IPCM_HandleMsg(ipcClient *, const ipcMessage *); + +#endif // !ipcCommandModule_h__ diff --git a/src/libs/xpcom18a4/ipc/ipcd/daemon/src/ipcModuleReg.cpp b/src/libs/xpcom18a4/ipc/ipcd/daemon/src/ipcModuleReg.cpp new file mode 100644 index 00000000..09e60ab9 --- /dev/null +++ b/src/libs/xpcom18a4/ipc/ipcd/daemon/src/ipcModuleReg.cpp @@ -0,0 +1,245 @@ +/* ***** 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 <string.h> +#include <stdlib.h> + +#include "prlink.h" +#include "prio.h" +#include "prlog.h" +#include "plstr.h" + +#include "ipcConfig.h" +#include "ipcLog.h" +#include "ipcModuleReg.h" +#include "ipcModule.h" +#include "ipcCommandModule.h" +#include "ipcd.h" + +//----------------------------------------------------------------------------- + +struct ipcModuleRegEntry +{ + nsID target; + ipcModuleMethods *methods; + PRLibrary *lib; +}; + +#define IPC_MAX_MODULE_COUNT 64 + +static ipcModuleRegEntry ipcModules[IPC_MAX_MODULE_COUNT]; +static int ipcModuleCount = 0; + +//----------------------------------------------------------------------------- + +static PRStatus +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 = PR_LoadLibrary(libPath); + + ++ipcModuleCount; + return PR_SUCCESS; +} + +static void +InitModuleFromLib(const char *modulesDir, const char *fileName) +{ + LOG(("InitModuleFromLib [%s]\n", fileName)); + + static const ipcDaemonMethods gDaemonMethods = + { + IPC_DAEMON_METHODS_VERSION, + IPC_DispatchMsg, + IPC_SendMsg, + IPC_GetClientByID, + IPC_GetClientByName, + IPC_EnumClients, + IPC_GetClientID, + IPC_ClientHasName, + IPC_ClientHasTarget, + IPC_EnumClientNames, + IPC_EnumClientTargets + }; + + int dLen = strlen(modulesDir); + int fLen = strlen(fileName); + + char *buf = (char *) malloc(dLen + 1 + fLen + 1); + memcpy(buf, modulesDir, dLen); + buf[dLen] = IPC_PATH_SEP_CHAR; + memcpy(buf + dLen + 1, fileName, fLen); + buf[dLen + 1 + fLen] = '\0'; + + PRLibrary *lib = PR_LoadLibrary(buf); + if (lib) { + ipcGetModulesFunc func = + (ipcGetModulesFunc) PR_FindFunctionSymbol(lib, "IPC_GetModules"); + + LOG((" func=%p\n", (void*) func)); + + if (func) { + const ipcModuleEntry *entries = NULL; + int count = func(&gDaemonMethods, &entries); + for (int i=0; i<count; ++i) { + if (AddModule(entries[i].target, entries[i].methods, buf) == PR_SUCCESS) { + if (entries[i].methods->init) + entries[i].methods->init(); + } + } + } + PR_UnloadLibrary(lib); + } + + free(buf); +} + +//----------------------------------------------------------------------------- +// ipcModuleReg API +//----------------------------------------------------------------------------- + +void +IPC_InitModuleReg(const char *exePath) +{ + if (!(exePath && *exePath)) + return; + + // + // register plug-in modules + // + char *p = PL_strrchr(exePath, IPC_PATH_SEP_CHAR); + if (p == NULL) { + LOG(("unexpected exe path\n")); + return; + } + + int baseLen = p - exePath; + int finalLen = baseLen + 1 + sizeof(IPC_MODULES_DIR); + + // build full path to ipc modules + char *modulesDir = (char*) malloc(finalLen); + memcpy(modulesDir, exePath, baseLen); + modulesDir[baseLen] = IPC_PATH_SEP_CHAR; + memcpy(modulesDir + baseLen + 1, IPC_MODULES_DIR, sizeof(IPC_MODULES_DIR)); + + LOG(("loading libraries in %s\n", modulesDir)); + // + // scan directory for IPC modules + // + PRDir *dir = PR_OpenDir(modulesDir); + if (dir) { + PRDirEntry *ent; + while ((ent = PR_ReadDir(dir, PR_SKIP_BOTH)) != NULL) { + // + // locate extension, and check if dynamic library + // + + const char *p = strrchr(ent->name, '.'); + if (p && PL_strcasecmp(p, MOZ_DLL_SUFFIX) == 0) + InitModuleFromLib(modulesDir, ent->name); + } + PR_CloseDir(dir); + } + + free(modulesDir); +} + +void +IPC_ShutdownModuleReg() +{ + // + // shutdown modules in reverse order + // + while (ipcModuleCount) { + ipcModuleRegEntry &entry = ipcModules[--ipcModuleCount]; + if (entry.methods->shutdown) + entry.methods->shutdown(); + if (entry.lib) + PR_UnloadLibrary(entry.lib); + } +} + +void +IPC_NotifyModulesClientUp(ipcClient *client) +{ + for (int i = 0; i < ipcModuleCount; ++i) { + ipcModuleRegEntry &entry = ipcModules[i]; + if (entry.methods->clientUp) + entry.methods->clientUp(client); + } +} + +void +IPC_NotifyModulesClientDown(ipcClient *client) +{ + for (int i = 0; i < ipcModuleCount; ++i) { + ipcModuleRegEntry &entry = ipcModules[i]; + if (entry.methods->clientDown) + entry.methods->clientDown(client); + } +} + +PRStatus +IPC_DispatchMsg(ipcClient *client, const nsID &target, const void *data, PRUint32 dataLen) +{ + // dispatch message to every module registered under the given target. + for (int i=0; i<ipcModuleCount; ++i) { + ipcModuleRegEntry &entry = ipcModules[i]; + if (entry.target.Equals(target)) { + if (entry.methods->handleMsg) + entry.methods->handleMsg(client, target, data, dataLen); + } + } + return PR_SUCCESS; +} diff --git a/src/libs/xpcom18a4/ipc/ipcd/daemon/src/ipcModuleReg.h b/src/libs/xpcom18a4/ipc/ipcd/daemon/src/ipcModuleReg.h new file mode 100644 index 00000000..d3fecd6c --- /dev/null +++ b/src/libs/xpcom18a4/ipc/ipcd/daemon/src/ipcModuleReg.h @@ -0,0 +1,70 @@ +/* ***** 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 ***** */ + +#ifndef ipcModuleReg_h__ +#define ipcModuleReg_h__ + +#include "ipcModule.h" + +// +// 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 +// directory relative to the daemon executable. +// +void IPC_InitModuleReg(const char *exePath); + +// +// 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(); + +// +// returns the ipcModuleMethods for the given target. +// +ipcModuleMethods *IPC_GetModuleByTarget(const nsID &target); + +// +// notifies all modules of client connect/disconnect +// +void IPC_NotifyModulesClientUp(ipcClient *); +void IPC_NotifyModulesClientDown(ipcClient *); + +#endif // !ipcModuleReg_h__ diff --git a/src/libs/xpcom18a4/ipc/ipcd/daemon/src/ipcd.cpp b/src/libs/xpcom18a4/ipc/ipcd/daemon/src/ipcd.cpp new file mode 100644 index 00000000..60aca34a --- /dev/null +++ b/src/libs/xpcom18a4/ipc/ipcd/daemon/src/ipcd.cpp @@ -0,0 +1,235 @@ +/* ***** 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 "prlog.h" +#include "prio.h" + +#include "ipcConfig.h" +#include "ipcLog.h" +#include "ipcMessage.h" +#include "ipcClient.h" +#include "ipcModuleReg.h" +#include "ipcModule.h" +#include "ipcCommandModule.h" +#include "ipcdPrivate.h" +#include "ipcd.h" + +//----------------------------------------------------------------------------- + +void +IPC_NotifyParent() +{ + PRFileDesc *fd = PR_GetInheritedFD(IPC_STARTUP_PIPE_NAME); + if (fd) { + char c = IPC_STARTUP_PIPE_MAGIC; + PR_Write(fd, &c, 1); + PR_Close(fd); + } +} + +//----------------------------------------------------------------------------- + +PRStatus +IPC_DispatchMsg(ipcClient *client, const ipcMessage *msg) +{ + PR_ASSERT(client); + PR_ASSERT(msg); + + // remember if client is expecting SYNC_REPLY. we'll add that flag to the + // next message sent to the client. + if (msg->TestFlag(IPC_MSG_FLAG_SYNC_QUERY)) { + PR_ASSERT(client->GetExpectsSyncReply() == PR_FALSE); + // XXX shouldn't we remember the TargetID as well, and only set the + // SYNC_REPLY flag on the next message sent to the same TargetID? + client->SetExpectsSyncReply(PR_TRUE); + } + + if (msg->Target().Equals(IPCM_TARGET)) { + IPCM_HandleMsg(client, msg); + return PR_SUCCESS; + } + + return IPC_DispatchMsg(client, msg->Target(), msg->Data(), msg->DataLen()); +} + +PRStatus +IPC_SendMsg(ipcClient *client, ipcMessage *msg) +{ + PR_ASSERT(msg); + + if (client == NULL) { + // + // broadcast + // + for (int i=0; i<ipcClientCount; ++i) + IPC_SendMsg(&ipcClients[i], msg->Clone()); + delete msg; + return PR_SUCCESS; + } + + // add SYNC_REPLY flag to message if client is expecting... + if (client->GetExpectsSyncReply()) { + msg->SetFlag(IPC_MSG_FLAG_SYNC_REPLY); + client->SetExpectsSyncReply(PR_FALSE); + } + + if (client->HasTarget(msg->Target())) + return IPC_PlatformSendMsg(client, msg); + + LOG((" no registered message handler\n")); + return PR_FAILURE; +} + +void +IPC_NotifyClientUp(ipcClient *client) +{ + LOG(("IPC_NotifyClientUp: clientID=%d\n", client->ID())); + + // notify modules before other clients + IPC_NotifyModulesClientUp(client); + + for (int i=0; i<ipcClientCount; ++i) { + if (&ipcClients[i] != client) + IPC_SendMsg(&ipcClients[i], + new ipcmMessageClientState(client->ID(), IPCM_CLIENT_STATE_UP)); + } +} + +void +IPC_NotifyClientDown(ipcClient *client) +{ + LOG(("IPC_NotifyClientDown: clientID=%d\n", client->ID())); + + // notify modules before other clients + IPC_NotifyModulesClientDown(client); + + for (int i=0; i<ipcClientCount; ++i) { + if (&ipcClients[i] != client) + IPC_SendMsg(&ipcClients[i], + new ipcmMessageClientState(client->ID(), IPCM_CLIENT_STATE_DOWN)); + } +} + +//----------------------------------------------------------------------------- +// IPC daemon methods +//----------------------------------------------------------------------------- + +PRStatus +IPC_SendMsg(ipcClient *client, const nsID &target, const void *data, PRUint32 dataLen) +{ + return IPC_SendMsg(client, new ipcMessage(target, (const char *) data, dataLen)); +} + +ipcClient * +IPC_GetClientByID(PRUint32 clientID) +{ + // linear search OK since number of clients should be small + for (int i = 0; i < ipcClientCount; ++i) { + if (ipcClients[i].ID() == clientID) + return &ipcClients[i]; + } + return NULL; +} + +ipcClient * +IPC_GetClientByName(const char *name) +{ + // linear search OK since number of clients should be small + for (int i = 0; i < ipcClientCount; ++i) { + if (ipcClients[i].HasName(name)) + return &ipcClients[i]; + } + return NULL; +} + +void +IPC_EnumClients(ipcClientEnumFunc func, void *closure) +{ + PR_ASSERT(func); + for (int i = 0; i < ipcClientCount; ++i) { + if (func(closure, &ipcClients[i], ipcClients[i].ID()) == PR_FALSE) + break; + } +} + +PRUint32 +IPC_GetClientID(ipcClient *client) +{ + PR_ASSERT(client); + return client->ID(); +} + +PRBool +IPC_ClientHasName(ipcClient *client, const char *name) +{ + PR_ASSERT(client); + PR_ASSERT(name); + return client->HasName(name); +} + +PRBool +IPC_ClientHasTarget(ipcClient *client, const nsID &target) +{ + PR_ASSERT(client); + return client->HasTarget(target); +} + +void +IPC_EnumClientNames(ipcClient *client, ipcClientNameEnumFunc func, void *closure) +{ + PR_ASSERT(client); + PR_ASSERT(func); + const ipcStringNode *node = client->Names(); + while (node) { + if (func(closure, client, node->Value()) == PR_FALSE) + break; + node = node->mNext; + } +} + +void +IPC_EnumClientTargets(ipcClient *client, ipcClientTargetEnumFunc func, void *closure) +{ + PR_ASSERT(client); + PR_ASSERT(func); + const ipcIDNode *node = client->Targets(); + while (node) { + if (func(closure, client, node->Value()) == PR_FALSE) + break; + node = node->mNext; + } +} diff --git a/src/libs/xpcom18a4/ipc/ipcd/daemon/src/ipcd.h b/src/libs/xpcom18a4/ipc/ipcd/daemon/src/ipcd.h new file mode 100644 index 00000000..5a0f2c2a --- /dev/null +++ b/src/libs/xpcom18a4/ipc/ipcd/daemon/src/ipcd.h @@ -0,0 +1,82 @@ +/* ***** 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 ***** */ + +#ifndef IPCD_H__ +#define IPCD_H__ + +#include "ipcModule.h" +#include "ipcMessage.h" + +//----------------------------------------------------------------------------- +// IPC daemon methods (see struct ipcDaemonMethods) +// +// these functions may only be called directly from within the daemon. plug-in +// modules must access these through the ipcDaemonMethods structure. +//----------------------------------------------------------------------------- + +PRStatus IPC_DispatchMsg (ipcClientHandle client, const nsID &target, const void *data, PRUint32 dataLen); +PRStatus IPC_SendMsg (ipcClientHandle client, const nsID &target, const void *data, PRUint32 dataLen); +ipcClientHandle IPC_GetClientByID (PRUint32 id); +ipcClientHandle IPC_GetClientByName (const char *name); +void IPC_EnumClients (ipcClientEnumFunc func, void *closure); +PRUint32 IPC_GetClientID (ipcClientHandle client); +PRBool IPC_ClientHasName (ipcClientHandle client, const char *name); +PRBool IPC_ClientHasTarget (ipcClientHandle client, const nsID &target); +void IPC_EnumClientNames (ipcClientHandle client, ipcClientNameEnumFunc func, void *closure); +void IPC_EnumClientTargets (ipcClientHandle client, ipcClientTargetEnumFunc func, void *closure); + +//----------------------------------------------------------------------------- +// other internal IPCD methods +//----------------------------------------------------------------------------- + +// +// dispatch message +// +PRStatus IPC_DispatchMsg(ipcClientHandle client, const ipcMessage *msg); + +// +// send message, takes ownership of |msg|. +// +PRStatus IPC_SendMsg(ipcClientHandle client, ipcMessage *msg); + +// +// dispatch notifications about client connects and disconnects +// +void IPC_NotifyClientUp(ipcClientHandle client); +void IPC_NotifyClientDown(ipcClientHandle client); + +#endif // !IPCD_H__ diff --git a/src/libs/xpcom18a4/ipc/ipcd/daemon/src/ipcdPrivate.h b/src/libs/xpcom18a4/ipc/ipcd/daemon/src/ipcdPrivate.h new file mode 100644 index 00000000..023b3a03 --- /dev/null +++ b/src/libs/xpcom18a4/ipc/ipcd/daemon/src/ipcdPrivate.h @@ -0,0 +1,65 @@ +/* ***** 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 ***** */ + +#ifndef ipcdPrivate_h__ +#define ipcdPrivate_h__ + +class ipcClient; + +// +// upper limit on the number of active connections +// XXX may want to make this more dynamic +// +#define IPC_MAX_CLIENTS 10000 + +// +// array of connected clients +// +extern ipcClient *ipcClients; +extern int ipcClientCount; + +// +// platform specific send message function, takes ownership of |msg|. +// +PRStatus IPC_PlatformSendMsg(ipcClient *client, ipcMessage *msg); + +// +// notify parent that it can connect to the daemon. +// +void IPC_NotifyParent(); + +#endif // !ipcdPrivate_h__ diff --git a/src/libs/xpcom18a4/ipc/ipcd/daemon/src/ipcdStub.cpp b/src/libs/xpcom18a4/ipc/ipcd/daemon/src/ipcdStub.cpp new file mode 100644 index 00000000..10618110 --- /dev/null +++ b/src/libs/xpcom18a4/ipc/ipcd/daemon/src/ipcdStub.cpp @@ -0,0 +1,77 @@ +/* ***** 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 IBM Corporation. + * Portions created by IBM Corporation are Copyright (C) 2003 + * IBM Corporation. All Rights Reserved. + * + * Contributor(s): + * Darin Fisher <darin@meer.net> + * + * 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 "ipcd.h" +#include "ipcdPrivate.h" +#include "ipcLog.h" + +#include "prerror.h" + +//----------------------------------------------------------------------------- +// use this file as a template to add server-side IPC connectivity. +// +// NOTE: if your platform supports local domain TCP sockets, then you should +// be able to make use of ipcConnectionUnix.cpp. +//----------------------------------------------------------------------------- + +// these variables are declared in ipcdPrivate.h and must be initialized by +// when the daemon starts up. +ipcClient *ipcClients = NULL; +int ipcClientCount = 0; + +PRStatus +IPC_PlatformSendMsg(ipcClient *client, ipcMessage *msg) +{ + const char notimplemented[] = "IPC_PlatformSendMsg not implemented"; + PR_SetErrorText(sizeof(notimplemented), notimplemented); + return PR_FAILURE; +} + +int main(int argc, char **argv) +{ + IPC_InitLog("###"); + + LOG(("daemon started...\n")); + + /* + IPC_InitModuleReg(argv[0]); + IPC_ShutdownModuleReg(); + */ + + // let the parent process know that we are up-and-running + IPC_NotifyParent(); + return 0; +} diff --git a/src/libs/xpcom18a4/ipc/ipcd/daemon/src/ipcdUnix.cpp b/src/libs/xpcom18a4/ipc/ipcd/daemon/src/ipcdUnix.cpp new file mode 100644 index 00000000..026e9e52 --- /dev/null +++ b/src/libs/xpcom18a4/ipc/ipcd/daemon/src/ipcdUnix.cpp @@ -0,0 +1,600 @@ +/* ***** 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 <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> + +#if defined(VBOX) && !defined(XP_OS2) +# include <sys/resource.h> +# include <errno.h> +#endif + +#ifdef VBOX +# include <iprt/initterm.h> +#endif + +#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" + +#if 0 +static void +IPC_Sleep(int seconds) +{ + while (seconds > 0) { + LOG(("\rsleeping for %d seconds...", seconds)); + PR_Sleep(PR_SecondsToInterval(1)); + --seconds; + } + LOG(("\ndone sleeping\n")); +} +#endif + +//----------------------------------------------------------------------------- +// ipc directory and locking... +//----------------------------------------------------------------------------- + +// +// advisory file locking is used to ensure that only one IPC daemon is active +// and bound to the local domain socket at a time. +// +// XXX this code does not work on OS/2. +// +#if !defined(XP_OS2) +#define IPC_USE_FILE_LOCK +#endif + +#ifdef IPC_USE_FILE_LOCK + +enum Status +{ + EOk = 0, + ELockFileOpen = -1, + ELockFileLock = -2, + ELockFileOwner = -3, +}; + +static int ipcLockFD = 0; + +static Status AcquireDaemonLock(const char *baseDir) +{ + 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)); + +#ifdef VBOX + // + // Security checks for the directory + // + struct stat st; + if (stat(baseDir, &st) == -1) + { + printf("Cannot stat '%s'.\n", baseDir); + return ELockFileOwner; + } + + if (st.st_uid != getuid() && st.st_uid != geteuid()) + { + printf("Wrong owner (%d) of '%s'", st.st_uid, baseDir); + if ( !stat("/tmp", &st) + && (st.st_mode & 07777) != 01777) + printf(" -- check /tmp permissions (%o should be 1777)\n", + st.st_mode & 07777); + printf(".\n"); + return ELockFileOwner; + } + + if (st.st_mode != (S_IRUSR | S_IWUSR | S_IXUSR | S_IFDIR)) + { + printf("Wrong mode (%o) of '%s'", st.st_mode, baseDir); + if ( !stat("/tmp", &st) + && (st.st_mode & 07777) != 01777) + printf(" -- check /tmp permissions (%o should be 1777)\n", + st.st_mode & 07777); + printf(".\n"); + return ELockFileOwner; + } +#endif + + // + // open lock file. it remains open until we shutdown. + // + ipcLockFD = open(lockFile, O_WRONLY|O_CREAT, S_IWUSR|S_IRUSR); + +#ifndef VBOX + free(lockFile); +#endif + + if (ipcLockFD == -1) + return ELockFileOpen; + +#ifdef VBOX + // + // Security checks for the lock file + // + if (fstat(ipcLockFD, &st) == -1) + { + printf("Cannot stat '%s'.\n", lockFile); + free(lockFile); + return ELockFileOwner; + } + + if (st.st_uid != getuid() && st.st_uid != geteuid()) + { + printf("Wrong owner (%d) of '%s'.\n", st.st_uid, lockFile); + free(lockFile); + return ELockFileOwner; + } + + if (st.st_mode != (S_IRUSR | S_IWUSR | S_IFREG)) + { + printf("Wrong mode (%o) of '%s'.\n", st.st_mode, lockFile); + free(lockFile); + return ELockFileOwner; + } + + free(lockFile); +#endif + + // + // 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 ELockFileLock; + + // + // 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 EOk; +} + +static Status InitDaemonDir(const char *socketPath) +{ + 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. + // + Status status = AcquireDaemonLock(baseDir); + + PL_strfree(baseDir); + + if (status == EOk) { + // delete an existing socket to prevent bind from failing. + unlink(socketPath); + } + return status; +} + +static void ShutdownDaemonDir() +{ + 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; + } +} + +#endif // IPC_USE_FILE_LOCK + +//----------------------------------------------------------------------------- +// poll list +//----------------------------------------------------------------------------- + +// +// declared in ipcdPrivate.h +// +ipcClient *ipcClients = NULL; +int ipcClientCount = 0; + +// +// the first element of this array is always zero; this is done so that the +// k'th element of ipcClientArray corresponds to the k'th element of +// ipcPollList. +// +static ipcClient ipcClientArray[IPC_MAX_CLIENTS + 1]; + +// +// element 0 contains the "server socket" +// +static PRPollDesc ipcPollList[IPC_MAX_CLIENTS + 1]; + +//----------------------------------------------------------------------------- + +static int AddClient(PRFileDesc *fd) +{ + 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; +} + +static int RemoveClient(int clientIndex) +{ + 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; +} + +//----------------------------------------------------------------------------- + +static void PollLoop(PRFileDesc *listenFD) +{ + // 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; + + // @todo : We need to handle errors from accept() especially something like + // EMFILE, which happens when we run out of file descriptors. + // and puts XPCOMIPCD in a poll/accept endless loop! + 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; + } + } +} + +//----------------------------------------------------------------------------- + +PRStatus +IPC_PlatformSendMsg(ipcClient *client, ipcMessage *msg) +{ + 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) +{ + PRFileDesc *listenFD = NULL; + PRNetAddr addr; + +#ifdef VBOX + /* Set up the runtime without loading the support driver. */ + RTR3InitExe(argc, &argv, 0); +#endif + + // + // 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 + Status status = InitDaemonDir(addr.local.path); + if (status != EOk) { + if (status == ELockFileLock) { + LOG(("Another daemon is already running, exiting.\n")); + // send a signal to the blocked parent to indicate success + IPC_NotifyParent(); + return 0; + } + else { + LOG(("InitDaemonDir failed (status=%d)\n", status)); + // don't notify the parent to cause it to fail in PR_Read() after + // we terminate +#ifdef VBOX + if (status != ELockFileOwner) + printf("Cannot create a lock file for '%s'.\n" + "Check permissions.\n", addr.local.path); +#endif + return 0; + } + } +#endif + + listenFD = PR_OpenTCPSocket(PR_AF_LOCAL); + if (!listenFD) { + LOG(("PR_OpenTCPSocket failed [%d]\n", PR_GetError())); + } + else if (PR_Bind(listenFD, &addr) != PR_SUCCESS) { + LOG(("PR_Bind failed [%d]\n", PR_GetError())); + } + else { + IPC_InitModuleReg(argv[0]); + +#ifdef VBOX + // Use large backlog, as otherwise local sockets can reject connection + // attempts. Usually harmless, but causes an unnecessary start attempt + // of IPCD (which will terminate straight away), and the next attempt + // usually succeeds. But better avoid unnecessary activities. + if (PR_Listen(listenFD, 128) != PR_SUCCESS) { +#else /* !VBOX */ + if (PR_Listen(listenFD, 5) != PR_SUCCESS) { +#endif /* !VBOX */ + LOG(("PR_Listen failed [%d]\n", PR_GetError())); + } + else { +#ifndef VBOX + // redirect all standard file descriptors to /dev/null for + // proper daemonizing + PR_Close(PR_STDIN); + PR_Open("/dev/null", O_RDONLY, 0); + PR_Close(PR_STDOUT); + PR_Open("/dev/null", O_WRONLY, 0); + PR_Close(PR_STDERR); + PR_Open("/dev/null", O_WRONLY, 0); +#endif + + IPC_NotifyParent(); + +#if defined(VBOX) && !defined(XP_OS2) + // Increase the file table size to 10240 or as high as possible. + struct rlimit lim; + if (getrlimit(RLIMIT_NOFILE, &lim) == 0) + { + if ( lim.rlim_cur < 10240 + && lim.rlim_cur < lim.rlim_max) + { + lim.rlim_cur = lim.rlim_max <= 10240 ? lim.rlim_max : 10240; + if (setrlimit(RLIMIT_NOFILE, &lim) == -1) + printf("WARNING: failed to increase file descriptor limit. (%d)\n", errno); + } + } + else + printf("WARNING: failed to obtain per-process file-descriptor limit (%d).\n", errno); +#endif + + PollLoop(listenFD); + } + + IPC_ShutdownModuleReg(); + } + + //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; +} diff --git a/src/libs/xpcom18a4/ipc/ipcd/daemon/src/ipcdWin.cpp b/src/libs/xpcom18a4/ipc/ipcd/daemon/src/ipcdWin.cpp new file mode 100644 index 00000000..f89fba06 --- /dev/null +++ b/src/libs/xpcom18a4/ipc/ipcd/daemon/src/ipcdWin.cpp @@ -0,0 +1,408 @@ +/* ***** 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 <windows.h> + +#include "prthread.h" + +#include "ipcConfig.h" +#include "ipcLog.h" +#include "ipcMessage.h" +#include "ipcClient.h" +#include "ipcModuleReg.h" +#include "ipcdPrivate.h" +#include "ipcd.h" +#include "ipcm.h" + +// +// declared in ipcdPrivate.h +// +ipcClient *ipcClients = NULL; +int ipcClientCount = 0; + +static ipcClient ipcClientArray[IPC_MAX_CLIENTS]; + +static HWND ipcHwnd = NULL; +static PRBool ipcShutdown = PR_FALSE; + +#define IPC_PURGE_TIMER_ID 1 +#define IPC_WM_SENDMSG (WM_USER + 1) +#define IPC_WM_SHUTDOWN (WM_USER + 2) + +//----------------------------------------------------------------------------- +// client array manipulation +//----------------------------------------------------------------------------- + +static void +RemoveClient(ipcClient *client) +{ + LOG(("RemoveClient\n")); + + int clientIndex = client - ipcClientArray; + + client->Finalize(); + + // + // move last ipcClient object down into the spot occupied by this client. + // + int fromIndex = ipcClientCount - 1; + int toIndex = clientIndex; + if (toIndex != fromIndex) + memcpy(&ipcClientArray[toIndex], &ipcClientArray[fromIndex], sizeof(ipcClient)); + + memset(&ipcClientArray[fromIndex], 0, sizeof(ipcClient)); + + --ipcClientCount; + LOG((" num clients = %u\n", ipcClientCount)); + + if (ipcClientCount == 0) { + LOG((" shutting down...\n")); + KillTimer(ipcHwnd, IPC_PURGE_TIMER_ID); + PostQuitMessage(0); + ipcShutdown = PR_TRUE; + } +} + +static void +PurgeStaleClients() +{ + if (ipcClientCount == 0) + return; + + LOG(("PurgeStaleClients [num-clients=%u]\n", ipcClientCount)); + // + // walk the list of supposedly active clients, and verify the existance of + // their respective message windows. + // + char wName[IPC_CLIENT_WINDOW_NAME_MAXLEN]; + for (int i=ipcClientCount-1; i>=0; --i) { + ipcClient *client = &ipcClientArray[i]; + + LOG((" checking client at index %u [client-id=%u pid=%u]\n", + i, client->ID(), client->PID())); + + 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")); + RemoveClient(client); + } + } +} + +static ipcClient * +AddClient(HWND hwnd, PRUint32 pid) +{ + LOG(("AddClient\n")); + + // + // before adding a new client, verify that all existing clients are + // still up and running. remove any stale clients. + // + PurgeStaleClients(); + + if (ipcClientCount == IPC_MAX_CLIENTS) { + LOG((" reached maximum client count!\n")); + return NULL; + } + + ipcClient *client = &ipcClientArray[ipcClientCount]; + client->Init(); + client->SetHwnd(hwnd); + client->SetPID(pid); // XXX one function instead of 3 + + ++ipcClientCount; + LOG((" num clients = %u\n", ipcClientCount)); + + if (ipcClientCount == 1) + SetTimer(ipcHwnd, IPC_PURGE_TIMER_ID, 1000, NULL); + + return client; +} + +static ipcClient * +GetClientByPID(PRUint32 pid) +{ + for (int i=0; i<ipcClientCount; ++i) { + if (ipcClientArray[i].PID() == pid) + return &ipcClientArray[i]; + } + return NULL; +} + +//----------------------------------------------------------------------------- +// message processing +//----------------------------------------------------------------------------- + +static void +ProcessMsg(HWND hwnd, PRUint32 pid, const ipcMessage *msg) +{ + LOG(("ProcessMsg [pid=%u len=%u]\n", pid, msg->MsgLen())); + + ipcClient *client = GetClientByPID(pid); + + if (client) { + // + // if this is an IPCM "client hello" message, then reset the client + // instance object. + // + if (msg->Target().Equals(IPCM_TARGET) && + IPCM_GetType(msg) == IPCM_MSG_REQ_CLIENT_HELLO) { + RemoveClient(client); + client = NULL; + } + } + + if (client == NULL) { + client = AddClient(hwnd, pid); + if (!client) + return; + } + + IPC_DispatchMsg(client, msg); +} + +//----------------------------------------------------------------------------- + +PRStatus +IPC_PlatformSendMsg(ipcClient *client, ipcMessage *msg) +{ + LOG(("IPC_PlatformSendMsg [clientID=%u clientPID=%u]\n", + client->ID(), client->PID())); + + // use PostMessage to make this asynchronous; otherwise we might get + // some wierd SendMessage recursion between processes. + + WPARAM wParam = (WPARAM) client->Hwnd(); + LPARAM lParam = (LPARAM) msg; + if (!PostMessage(ipcHwnd, IPC_WM_SENDMSG, wParam, lParam)) { + LOG(("PostMessage failed\n")); + delete msg; + return PR_FAILURE; + } + return PR_SUCCESS; +} + +//----------------------------------------------------------------------------- +// windows message loop +//----------------------------------------------------------------------------- + +static LRESULT CALLBACK +WindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + LOG(("got message [msg=%x wparam=%x lparam=%x]\n", uMsg, wParam, lParam)); + + if (uMsg == WM_COPYDATA) { + if (ipcShutdown) { + LOG(("ignoring message b/c daemon is shutting down\n")); + return TRUE; + } + COPYDATASTRUCT *cd = (COPYDATASTRUCT *) lParam; + if (cd && cd->lpData) { + 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) { + // + // grab client PID and hwnd. + // + ProcessMsg((HWND) wParam, (PRUint32) cd->dwData, &msg); + } + else + LOG(("ignoring malformed message\n")); + } + return TRUE; + } + + if (uMsg == IPC_WM_SENDMSG) { + HWND hWndDest = (HWND) wParam; + ipcMessage *msg = (ipcMessage *) lParam; + + COPYDATASTRUCT cd; + cd.dwData = GetCurrentProcessId(); + cd.cbData = (DWORD) msg->MsgLen(); + cd.lpData = (PVOID) msg->MsgBuf(); + + LOG(("calling SendMessage...\n")); + SendMessage(hWndDest, WM_COPYDATA, (WPARAM) hWnd, (LPARAM) &cd); + LOG((" done.\n")); + + delete msg; + return 0; + } + + if (uMsg == WM_TIMER) { + PurgeStaleClients(); + return 0; + } + +#if 0 + if (uMsg == IPC_WM_SHUTDOWN) { + // + // since this message is handled asynchronously, it is possible + // that other clients may have come online since this was issued. + // in which case, we need to ignore this message. + // + if (ipcClientCount == 0) { + ipcShutdown = PR_TRUE; + PostQuitMessage(0); + } + return 0; + } +#endif + + return DefWindowProc(hWnd, uMsg, wParam, lParam); +} + +//----------------------------------------------------------------------------- +// daemon startup synchronization +//----------------------------------------------------------------------------- + +static HANDLE ipcSyncEvent; + +static PRBool +AcquireLock() +{ + ipcSyncEvent = CreateEvent(NULL, FALSE, FALSE, + IPC_SYNC_EVENT_NAME); + if (!ipcSyncEvent) { + LOG(("CreateEvent failed [%u]\n", GetLastError())); + 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")); + return PR_TRUE; +} + +static void +ReleaseLock() +{ + if (ipcSyncEvent) { + LOG(("releasing lock...\n")); + CloseHandle(ipcSyncEvent); + ipcSyncEvent = NULL; + } +} + +//----------------------------------------------------------------------------- +// main +//----------------------------------------------------------------------------- + +#ifdef DEBUG +int +main() +#else +int WINAPI WinMain(HINSTANCE, HINSTANCE, LPSTR, int) +#endif +{ + IPC_InitLog("###"); + + LOG(("daemon started...\n")); + + if (!AcquireLock()) { + // unblock the parent; it should be able to find the IPC window of the + // other daemon process. + IPC_NotifyParent(); + return 0; + } + + // initialize global data + memset(ipcClientArray, 0, sizeof(ipcClientArray)); + ipcClients = ipcClientArray; + ipcClientCount = 0; + + // create message window up front... + WNDCLASS wc; + memset(&wc, 0, sizeof(wc)); + wc.lpfnWndProc = WindowProc; + wc.lpszClassName = IPC_WINDOW_CLASS; + + RegisterClass(&wc); + + ipcHwnd = CreateWindow(IPC_WINDOW_CLASS, IPC_WINDOW_NAME, + 0, 0, 0, 10, 10, NULL, NULL, NULL, NULL); + + // unblock the parent process; it should now look for the IPC window. + IPC_NotifyParent(); + + if (!ipcHwnd) + return -1; + + // load modules relative to the location of the executable... + { + char path[MAX_PATH]; + GetModuleFileName(NULL, path, sizeof(path)); + IPC_InitModuleReg(path); + } + + LOG(("entering message loop...\n")); + MSG msg; + while (GetMessage(&msg, ipcHwnd, 0, 0)) + DispatchMessage(&msg); + + // unload modules + IPC_ShutdownModuleReg(); + + // + // we release the daemon lock before destroying the window because the + // absence of our window is what will cause clients to try to spawn the + // daemon. + // + ReleaseLock(); + + //LOG(("sleeping 5 seconds...\n")); + //PR_Sleep(PR_SecondsToInterval(5)); + + LOG(("destroying message window...\n")); + DestroyWindow(ipcHwnd); + ipcHwnd = NULL; + + LOG(("exiting\n")); + return 0; +} diff --git a/src/libs/xpcom18a4/ipc/ipcd/extensions/.cvsignore b/src/libs/xpcom18a4/ipc/ipcd/extensions/.cvsignore new file mode 100644 index 00000000..f3c7a7c5 --- /dev/null +++ b/src/libs/xpcom18a4/ipc/ipcd/extensions/.cvsignore @@ -0,0 +1 @@ +Makefile diff --git a/src/libs/xpcom18a4/ipc/ipcd/extensions/Makefile.in b/src/libs/xpcom18a4/ipc/ipcd/extensions/Makefile.in new file mode 100644 index 00000000..e258946b --- /dev/null +++ b/src/libs/xpcom18a4/ipc/ipcd/extensions/Makefile.in @@ -0,0 +1,49 @@ +# vim: noexpandtab ts=4 sw=4 +# ***** 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 = ipcd +DIRS = lock transmngr dconnect + +include $(topsrcdir)/config/rules.mk diff --git a/src/libs/xpcom18a4/ipc/ipcd/extensions/Makefile.kup b/src/libs/xpcom18a4/ipc/ipcd/extensions/Makefile.kup new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/src/libs/xpcom18a4/ipc/ipcd/extensions/Makefile.kup diff --git a/src/libs/xpcom18a4/ipc/ipcd/extensions/dconnect/Makefile.in b/src/libs/xpcom18a4/ipc/ipcd/extensions/dconnect/Makefile.in new file mode 100644 index 00000000..e6a89153 --- /dev/null +++ b/src/libs/xpcom18a4/ipc/ipcd/extensions/dconnect/Makefile.in @@ -0,0 +1,52 @@ +# vim: noexpandtab ts=4 sw=4 +# ***** 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 + +DIRS = public src + +ifdef ENABLE_TESTS +DIRS += test +endif + +include $(topsrcdir)/config/rules.mk diff --git a/src/libs/xpcom18a4/ipc/ipcd/extensions/dconnect/Makefile.kup b/src/libs/xpcom18a4/ipc/ipcd/extensions/dconnect/Makefile.kup new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/src/libs/xpcom18a4/ipc/ipcd/extensions/dconnect/Makefile.kup diff --git a/src/libs/xpcom18a4/ipc/ipcd/extensions/dconnect/public/Makefile.in b/src/libs/xpcom18a4/ipc/ipcd/extensions/dconnect/public/Makefile.in new file mode 100644 index 00000000..b2b3dbaa --- /dev/null +++ b/src/libs/xpcom18a4/ipc/ipcd/extensions/dconnect/public/Makefile.in @@ -0,0 +1,53 @@ +# vim: noexpandtab ts=4 sw=4 +# ***** 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 = ipcd +XPIDL_MODULE= ipcd_dconnect + +XPIDLSRCS = \ + ipcIDConnectService.idl \ + $(NULL) + +include $(topsrcdir)/config/rules.mk diff --git a/src/libs/xpcom18a4/ipc/ipcd/extensions/dconnect/public/ipcIDConnectService.idl b/src/libs/xpcom18a4/ipc/ipcd/extensions/dconnect/public/ipcIDConnectService.idl new file mode 100644 index 00000000..6ca16a2d --- /dev/null +++ b/src/libs/xpcom18a4/ipc/ipcd/extensions/dconnect/public/ipcIDConnectService.idl @@ -0,0 +1,79 @@ +/* vim:set ts=2 sw=2 et cindent: */ +/* ***** 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 IBM Corporation. + * Portions created by the Initial Developer are Copyright (C) 2004 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Darin Fisher <darin@meer.net> + * + * 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" + +/** + * This service provides methods similar to nsIComponentManager and + * nsIServiceManager. A ClientID parameter specifies the remote process + * in which the object should live. + * + * ipcIService can be used to determine the ClientID of a remote process. + * + * It is assumed that both processes have access to the same typelibs. + */ +[scriptable, uuid(fe07ed16-2710-4a1e-a4e2-81302b62bf0e)] +interface ipcIDConnectService : nsISupports +{ + void createInstance( + in unsigned long aClientID, + in nsCIDRef aClass, + in nsIIDRef aIID, + [iid_is(aIID),retval] out nsQIResult result + ); + + void createInstanceByContractID( + in unsigned long aClientID, + in string aContractID, + in nsIIDRef aIID, + [iid_is(aIID),retval] out nsQIResult result + ); + + void getService( + in unsigned long aClientID, + in nsCIDRef aClass, + in nsIIDRef aIID, + [iid_is(aIID),retval] out nsQIResult result + ); + + void getServiceByContractID( + in unsigned long aClientID, + in string aContractID, + in nsIIDRef aIID, + [iid_is(aIID),retval] out nsQIResult result + ); +}; diff --git a/src/libs/xpcom18a4/ipc/ipcd/extensions/dconnect/src/Makefile.in b/src/libs/xpcom18a4/ipc/ipcd/extensions/dconnect/src/Makefile.in new file mode 100644 index 00000000..a9efaadf --- /dev/null +++ b/src/libs/xpcom18a4/ipc/ipcd/extensions/dconnect/src/Makefile.in @@ -0,0 +1,66 @@ +# vim: noexpandtab ts=4 sw=4 +# ***** 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 = ipcd +LIBRARY_NAME = ipcddconnect_s +FORCE_STATIC_LIB = 1 +MODULE_NAME = ipcd + +FORCE_USE_PIC = 1 + +REQUIRES = \ + xpcom \ + string \ + $(NULL) + +CPPSRCS = \ + ipcDConnectService.cpp \ + $(NULL) + +LOCAL_INCLUDES = \ + -I$(srcdir)/../../../shared/src \ + $(NULL) + +include $(topsrcdir)/config/rules.mk diff --git a/src/libs/xpcom18a4/ipc/ipcd/extensions/dconnect/src/Makefile.kup b/src/libs/xpcom18a4/ipc/ipcd/extensions/dconnect/src/Makefile.kup new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/src/libs/xpcom18a4/ipc/ipcd/extensions/dconnect/src/Makefile.kup diff --git a/src/libs/xpcom18a4/ipc/ipcd/extensions/dconnect/src/ipcDConnectService.cpp b/src/libs/xpcom18a4/ipc/ipcd/extensions/dconnect/src/ipcDConnectService.cpp new file mode 100644 index 00000000..32565df2 --- /dev/null +++ b/src/libs/xpcom18a4/ipc/ipcd/extensions/dconnect/src/ipcDConnectService.cpp @@ -0,0 +1,4210 @@ +/* vim:set ts=2 sw=2 et cindent: */ +/* ***** 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 IBM Corporation. + * Portions created by the Initial Developer are Copyright (C) 2004 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Darin Fisher <darin@meer.net> + * Dmitry A. Kuminov <dmik@innotek.de> + * + * 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 "ipcDConnectService.h" +#include "ipcMessageWriter.h" +#include "ipcMessageReader.h" +#include "ipcLog.h" + +#include "nsIServiceManagerUtils.h" +#include "nsIInterfaceInfo.h" +#include "nsIInterfaceInfoManager.h" +#include "nsIExceptionService.h" +#include "nsString.h" +#include "nsVoidArray.h" +#include "nsCRT.h" +#include "nsDeque.h" +#include "xptcall.h" + +#ifdef VBOX +# include <map> +# include <list> +# include <iprt/err.h> +# include <iprt/req.h> +# include <iprt/mem.h> +# include <iprt/time.h> +# include <iprt/thread.h> +#endif /* VBOX */ + +#if defined(DCONNECT_MULTITHREADED) + +#if !defined(DCONNECT_WITH_IPRT_REQ_POOL) +#include "nsIThread.h" +#include "nsIRunnable.h" +#endif + +#if defined(DEBUG) && !defined(DCONNECT_STATS) +#define DCONNECT_STATS +#endif + +#if defined(DCONNECT_STATS) +#include <stdio.h> +#endif + +#endif + +// XXX TODO: +// 1. add thread affinity field to SETUP messages + +//----------------------------------------------------------------------------- + +#define DCONNECT_IPC_TARGETID \ +{ /* 43ca47ef-ebc8-47a2-9679-a4703218089f */ \ + 0x43ca47ef, \ + 0xebc8, \ + 0x47a2, \ + {0x96, 0x79, 0xa4, 0x70, 0x32, 0x18, 0x08, 0x9f} \ +} +static const nsID kDConnectTargetID = DCONNECT_IPC_TARGETID; + +//----------------------------------------------------------------------------- + +#define DCON_WAIT_TIMEOUT PR_INTERVAL_NO_TIMEOUT + +//----------------------------------------------------------------------------- + +// +// +--------------------------------------+ +// | major opcode : 1 byte | +// +--------------------------------------+ +// | minor opcode : 1 byte | +// +--------------------------------------+ +// | flags : 2 bytes | +// +--------------------------------------+ +// . . +// . variable payload . +// . . +// +--------------------------------------+ +// + +// dconnect major opcodes +#define DCON_OP_SETUP 1 +#define DCON_OP_RELEASE 2 +#define DCON_OP_INVOKE 3 + +#define DCON_OP_SETUP_REPLY 4 +#define DCON_OP_INVOKE_REPLY 5 + +// dconnect minor opcodes for DCON_OP_SETUP +#define DCON_OP_SETUP_NEW_INST_CLASSID 1 +#define DCON_OP_SETUP_NEW_INST_CONTRACTID 2 +#define DCON_OP_SETUP_GET_SERV_CLASSID 3 +#define DCON_OP_SETUP_GET_SERV_CONTRACTID 4 +#define DCON_OP_SETUP_QUERY_INTERFACE 5 + +// dconnect minor opcodes for RELEASE +// dconnect minor opcodes for INVOKE + +// DCON_OP_SETUP_REPLY and DCON_OP_INVOKE_REPLY flags +#define DCON_OP_FLAGS_REPLY_EXCEPTION 0x0001 + +// Within this time all the worker threads must be terminated. +#define VBOX_XPCOM_SHUTDOWN_TIMEOUT_MS (5000) + +#pragma pack(1) + +struct DConnectOp +{ + PRUint8 opcode_major; + PRUint8 opcode_minor; + PRUint16 flags; + PRUint32 request_index; // initialized with NewRequestIndex +}; + +// SETUP structs + +struct DConnectSetup : DConnectOp +{ + nsID iid; +}; + +struct DConnectSetupClassID : DConnectSetup +{ + nsID classid; +}; + +struct DConnectSetupContractID : DConnectSetup +{ + char contractid[1]; // variable length +}; + +struct DConnectSetupQueryInterface : DConnectSetup +{ + DConAddr instance; +}; + +// SETUP_REPLY struct + +struct DConnectSetupReply : DConnectOp +{ + DConAddr instance; + nsresult status; + // optionally followed by a specially serialized nsIException instance (see + // ipcDConnectService::SerializeException) if DCON_OP_FLAGS_REPLY_EXCEPTION is + // present in flags +}; + +// RELEASE struct + +struct DConnectRelease : DConnectOp +{ + DConAddr instance; +}; + +// INVOKE struct + +struct DConnectInvoke : DConnectOp +{ + DConAddr instance; + PRUint16 method_index; + // followed by an array of in-param blobs +}; + +// INVOKE_REPLY struct + +struct DConnectInvokeReply : DConnectOp +{ + nsresult result; + // followed by an array of out-param blobs if NS_SUCCEEDED(result), and + // optionally by a specially serialized nsIException instance (see + // ipcDConnectService::SerializeException) if DCON_OP_FLAGS_REPLY_EXCEPTION is + // present in flags +}; + +#pragma pack() + +//----------------------------------------------------------------------------- + +struct DConAddrPlusPtr +{ + DConAddr addr; + void *p; +}; + +//----------------------------------------------------------------------------- + +ipcDConnectService *ipcDConnectService::mInstance = nsnull; + +//----------------------------------------------------------------------------- + +static nsresult +SetupPeerInstance(PRUint32 aPeerID, DConnectSetup *aMsg, PRUint32 aMsgLen, + void **aInstancePtr); + +//----------------------------------------------------------------------------- + +// A wrapper class holding an instance to an in-process XPCOM object. + +class DConnectInstance +{ +public: + DConnectInstance(PRUint32 peer, nsIInterfaceInfo *iinfo, nsISupports *instance) + : mPeer(peer) + , mIInfo(iinfo) + , mInstance(instance) + {} + + nsISupports *RealInstance() { return mInstance; } + nsIInterfaceInfo *InterfaceInfo() { return mIInfo; } + PRUint32 Peer() { return mPeer; } + + DConnectInstanceKey::Key GetKey() { + const nsID *iid; + mIInfo->GetIIDShared(&iid); + return DConnectInstanceKey::Key(mPeer, mInstance, iid); + } + + NS_IMETHODIMP_(nsrefcnt) AddRef(void) + { + NS_PRECONDITION(PRInt32(mRefCnt) >= 0, "illegal refcnt"); + nsrefcnt count; + count = PR_AtomicIncrement((PRInt32*)&mRefCnt); + return count; + } + + NS_IMETHODIMP_(nsrefcnt) Release(void) + { + nsrefcnt count; + NS_PRECONDITION(0 != mRefCnt, "dup release"); + count = PR_AtomicDecrement((PRInt32 *)&mRefCnt); + if (0 == count) { + NS_PRECONDITION(PRInt32(mRefCntIPC) == 0, "non-zero IPC refcnt"); + mRefCnt = 1; /* stabilize */ + delete this; + return 0; + } + return count; + } + + // this gets called after calling AddRef() on an instance passed to the + // client over IPC in order to have a count of IPC client-related references + // separately from the overall reference count + NS_IMETHODIMP_(nsrefcnt) AddRefIPC(void) + { + NS_PRECONDITION(PRInt32(mRefCntIPC) >= 0, "illegal refcnt"); + nsrefcnt count = PR_AtomicIncrement((PRInt32*)&mRefCntIPC); + return count; + } + + // this gets called before calling Release() when DCON_OP_RELEASE is + // received from the IPC client and in other cases to balance AddRefIPC() + NS_IMETHODIMP_(nsrefcnt) ReleaseIPC(PRBool locked = PR_FALSE) + { + NS_PRECONDITION(0 != mRefCntIPC, "dup release"); + nsrefcnt count = PR_AtomicDecrement((PRInt32 *)&mRefCntIPC); + if (0 == count) { + // If the last IPC reference is released, remove this instance from the map. + // ipcDConnectService is guaranteed to still exist here + // (DConnectInstance lifetime is bound to ipcDConnectService) + nsRefPtr <ipcDConnectService> dConnect (ipcDConnectService::GetInstance()); + if (dConnect) + dConnect->DeleteInstance(this, locked); + else + NS_NOTREACHED("ipcDConnectService has gone before DConnectInstance"); + } + return count; + } + +private: + nsAutoRefCnt mRefCnt; + nsAutoRefCnt mRefCntIPC; + PRUint32 mPeer; // peer process "owning" this instance + nsCOMPtr<nsIInterfaceInfo> mIInfo; + nsCOMPtr<nsISupports> mInstance; +}; + +void +ipcDConnectService::ReleaseWrappers(nsVoidArray &wrappers, PRUint32 peer) +{ + nsAutoLock lock (mLock); + + for (PRInt32 i=0; i<wrappers.Count(); ++i) + { + DConnectInstance *wrapper = (DConnectInstance *)wrappers[i]; + if (mInstanceSet.Contains(wrapper) && wrapper->Peer() == peer) + { + wrapper->ReleaseIPC(PR_TRUE /* locked */); + wrapper->Release(); + } + } +} + +//----------------------------------------------------------------------------- + +static nsresult +SerializeParam(ipcMessageWriter &writer, const nsXPTType &t, const nsXPTCMiniVariant &v) +{ + switch (t.TagPart()) + { + case nsXPTType::T_I8: + case nsXPTType::T_U8: + writer.PutInt8(v.val.u8); + break; + + case nsXPTType::T_I16: + case nsXPTType::T_U16: + writer.PutInt16(v.val.u16); + break; + + case nsXPTType::T_I32: + case nsXPTType::T_U32: + writer.PutInt32(v.val.u32); + break; + + case nsXPTType::T_I64: + case nsXPTType::T_U64: + writer.PutBytes(&v.val.u64, sizeof(PRUint64)); + break; + + case nsXPTType::T_FLOAT: + writer.PutBytes(&v.val.f, sizeof(float)); + break; + + case nsXPTType::T_DOUBLE: + writer.PutBytes(&v.val.d, sizeof(double)); + break; + + case nsXPTType::T_BOOL: + writer.PutBytes(&v.val.b, sizeof(PRBool)); + break; + + case nsXPTType::T_CHAR: + writer.PutBytes(&v.val.c, sizeof(char)); + break; + + case nsXPTType::T_WCHAR: + writer.PutBytes(&v.val.wc, sizeof(PRUnichar)); + break; + + case nsXPTType::T_IID: + { + AssertReturn(v.val.p, NS_ERROR_INVALID_POINTER); + writer.PutBytes(v.val.p, sizeof(nsID)); + } + break; + + case nsXPTType::T_CHAR_STR: + { + if (v.val.p) + { + int len = strlen((const char *) v.val.p); + writer.PutInt32(len); + writer.PutBytes(v.val.p, len); + } + else + { + // put -1 to indicate null string + writer.PutInt32((PRUint32) -1); + } + } + break; + + case nsXPTType::T_WCHAR_STR: + { + if (v.val.p) + { + int len = 2 * nsCRT::strlen((const PRUnichar *) v.val.p); + writer.PutInt32(len); + writer.PutBytes(v.val.p, len); + } + else + { + // put -1 to indicate null string + writer.PutInt32((PRUint32) -1); + } + } + break; + + case nsXPTType::T_INTERFACE: + case nsXPTType::T_INTERFACE_IS: + NS_NOTREACHED("this should be handled elsewhere"); + return NS_ERROR_UNEXPECTED; + + case nsXPTType::T_ASTRING: + case nsXPTType::T_DOMSTRING: + { + const nsAString *str = (const nsAString *) v.val.p; + + PRUint32 len = 2 * str->Length(); + nsAString::const_iterator begin; + const PRUnichar *data = str->BeginReading(begin).get(); + + writer.PutInt32(len); + writer.PutBytes(data, len); + } + break; + + case nsXPTType::T_UTF8STRING: + case nsXPTType::T_CSTRING: + { + const nsACString *str = (const nsACString *) v.val.p; + + PRUint32 len = str->Length(); + nsACString::const_iterator begin; + const char *data = str->BeginReading(begin).get(); + + writer.PutInt32(len); + writer.PutBytes(data, len); + } + break; + + case nsXPTType::T_ARRAY: + // arrays are serialized after all other params outside this routine + break; + + case nsXPTType::T_VOID: + case nsXPTType::T_PSTRING_SIZE_IS: + case nsXPTType::T_PWSTRING_SIZE_IS: + default: + LOG(("unexpected parameter type: %d\n", t.TagPart())); + return NS_ERROR_UNEXPECTED; + } + return NS_OK; +} + +static nsresult +DeserializeParam(ipcMessageReader &reader, const nsXPTType &t, nsXPTCVariant &v) +{ + // defaults + v.ptr = nsnull; + v.type = t; + v.flags = 0; + + switch (t.TagPart()) + { + case nsXPTType::T_I8: + case nsXPTType::T_U8: + v.val.u8 = reader.GetInt8(); + break; + + case nsXPTType::T_I16: + case nsXPTType::T_U16: + v.val.u16 = reader.GetInt16(); + break; + + case nsXPTType::T_I32: + case nsXPTType::T_U32: + v.val.u32 = reader.GetInt32(); + break; + + case nsXPTType::T_I64: + case nsXPTType::T_U64: + reader.GetBytes(&v.val.u64, sizeof(v.val.u64)); + break; + + case nsXPTType::T_FLOAT: + reader.GetBytes(&v.val.f, sizeof(v.val.f)); + break; + + case nsXPTType::T_DOUBLE: + reader.GetBytes(&v.val.d, sizeof(v.val.d)); + break; + + case nsXPTType::T_BOOL: + reader.GetBytes(&v.val.b, sizeof(v.val.b)); + break; + + case nsXPTType::T_CHAR: + reader.GetBytes(&v.val.c, sizeof(v.val.c)); + break; + + case nsXPTType::T_WCHAR: + reader.GetBytes(&v.val.wc, sizeof(v.val.wc)); + break; + + case nsXPTType::T_IID: + { + nsID *buf = (nsID *) nsMemory::Alloc(sizeof(nsID)); + reader.GetBytes(buf, sizeof(nsID)); + v.val.p = buf; + v.SetValIsAllocated(); + } + break; + + case nsXPTType::T_CHAR_STR: + { + PRUint32 len = reader.GetInt32(); + if (len == (PRUint32) -1) + { + // it's a null string + v.val.p = nsnull; + } + else + { + char *buf = (char *) nsMemory::Alloc(len + 1); + reader.GetBytes(buf, len); + buf[len] = char(0); + + v.val.p = buf; + v.SetValIsAllocated(); + } + } + break; + + case nsXPTType::T_WCHAR_STR: + { + PRUint32 len = reader.GetInt32(); + if (len == (PRUint32) -1) + { + // it's a null string + v.val.p = nsnull; + } + else + { + PRUnichar *buf = (PRUnichar *) nsMemory::Alloc(len + 2); + reader.GetBytes(buf, len); + buf[len / 2] = PRUnichar(0); + + v.val.p = buf; + v.SetValIsAllocated(); + } + } + break; + + case nsXPTType::T_INTERFACE: + case nsXPTType::T_INTERFACE_IS: + { + reader.GetBytes(&v.val.u64, sizeof(DConAddr)); + // stub creation will be handled outside this routine. we only + // deserialize the DConAddr into v.val.u64 temporarily. + } + break; + + case nsXPTType::T_ASTRING: + case nsXPTType::T_DOMSTRING: + { + PRUint32 len = reader.GetInt32(); + + nsString *str = new nsString(); + str->SetLength(len / 2); + PRUnichar *buf = str->BeginWriting(); + reader.GetBytes(buf, len); + + v.val.p = str; + v.SetValIsDOMString(); + } + break; + + case nsXPTType::T_UTF8STRING: + case nsXPTType::T_CSTRING: + { + PRUint32 len = reader.GetInt32(); + + nsCString *str = new nsCString(); + str->SetLength(len); + char *buf = str->BeginWriting(); + reader.GetBytes(buf, len); + + v.val.p = str; + + // this distinction here is pretty pointless + if (t.TagPart() == nsXPTType::T_CSTRING) + v.SetValIsCString(); + else + v.SetValIsUTF8String(); + } + break; + + case nsXPTType::T_ARRAY: + // arrays are deserialized after all other params outside this routine + break; + + case nsXPTType::T_VOID: + case nsXPTType::T_PSTRING_SIZE_IS: + case nsXPTType::T_PWSTRING_SIZE_IS: + default: + LOG(("unexpected parameter type\n")); + return NS_ERROR_UNEXPECTED; + } + return NS_OK; +} + +static nsresult +SetupParam(const nsXPTParamInfo &p, nsXPTCVariant &v) +{ + const nsXPTType &t = p.GetType(); + + if (p.IsIn() && p.IsDipper()) + { + v.ptr = nsnull; + v.flags = 0; + + switch (t.TagPart()) + { + case nsXPTType::T_ASTRING: + case nsXPTType::T_DOMSTRING: + v.val.p = new nsString(); + if (!v.val.p) + return NS_ERROR_OUT_OF_MEMORY; + v.type = t; + v.SetValIsDOMString(); + break; + + case nsXPTType::T_UTF8STRING: + case nsXPTType::T_CSTRING: + v.val.p = new nsCString(); + if (!v.val.p) + return NS_ERROR_OUT_OF_MEMORY; + v.type = t; + v.SetValIsCString(); + break; + + default: + LOG(("unhandled dipper: type=%d\n", t.TagPart())); + return NS_ERROR_UNEXPECTED; + } + } + else if (p.IsOut() || p.IsRetval()) + { + memset(&v.val, 0, sizeof(v.val)); + v.ptr = &v.val; + v.type = t; + v.flags = 0; + v.SetPtrIsData(); + + // the ownership of output nsID, string, wstring, interface pointers and + // arrays is transferred to the receiving party. Therefore, we need to + // instruct FinishParam() to perform a cleanup after serializing them. + switch (t.TagPart()) + { + case nsXPTType::T_IID: + case nsXPTType::T_CHAR_STR: + case nsXPTType::T_WCHAR_STR: + case nsXPTType::T_ARRAY: + v.SetValIsAllocated(); + break; + case nsXPTType::T_INTERFACE: + case nsXPTType::T_INTERFACE_IS: + v.SetValIsInterface(); + break; + default: + break; + } + } + + return NS_OK; +} + +static void +FinishParam(nsXPTCVariant &v) +{ +#ifdef VBOX + /* make valgrind happy */ + if (!v.MustFreeVal()) + return; +#endif + if (!v.val.p) + return; + + if (v.IsValAllocated()) + nsMemory::Free(v.val.p); + else if (v.IsValInterface()) + ((nsISupports *) v.val.p)->Release(); + else if (v.IsValDOMString()) + delete (nsAString *) v.val.p; + else if (v.IsValUTF8String() || v.IsValCString()) + delete (nsACString *) v.val.p; +} + +static nsresult +DeserializeResult(ipcMessageReader &reader, const nsXPTType &t, nsXPTCMiniVariant &v) +{ + if (v.val.p == nsnull) + return NS_OK; + + switch (t.TagPart()) + { + case nsXPTType::T_I8: + case nsXPTType::T_U8: + *((PRUint8 *) v.val.p) = reader.GetInt8(); + break; + + case nsXPTType::T_I16: + case nsXPTType::T_U16: + *((PRUint16 *) v.val.p) = reader.GetInt16(); + break; + + case nsXPTType::T_I32: + case nsXPTType::T_U32: + *((PRUint32 *) v.val.p) = reader.GetInt32(); + break; + + case nsXPTType::T_I64: + case nsXPTType::T_U64: + reader.GetBytes(v.val.p, sizeof(PRUint64)); + break; + + case nsXPTType::T_FLOAT: + reader.GetBytes(v.val.p, sizeof(float)); + break; + + case nsXPTType::T_DOUBLE: + reader.GetBytes(v.val.p, sizeof(double)); + break; + + case nsXPTType::T_BOOL: + reader.GetBytes(v.val.p, sizeof(PRBool)); + break; + + case nsXPTType::T_CHAR: + reader.GetBytes(v.val.p, sizeof(char)); + break; + + case nsXPTType::T_WCHAR: + reader.GetBytes(v.val.p, sizeof(PRUnichar)); + break; + + case nsXPTType::T_IID: + { + nsID *buf = (nsID *) nsMemory::Alloc(sizeof(nsID)); + reader.GetBytes(buf, sizeof(nsID)); + *((nsID **) v.val.p) = buf; + } + break; + + case nsXPTType::T_CHAR_STR: + { + PRUint32 len = reader.GetInt32(); + if (len == (PRUint32) -1) + { + // it's a null string +#ifdef VBOX + *((char **) v.val.p) = NULL; +#else + v.val.p = 0; +#endif + } + else + { + char *buf = (char *) nsMemory::Alloc(len + 1); + reader.GetBytes(buf, len); + buf[len] = char(0); + + *((char **) v.val.p) = buf; + } + } + break; + + case nsXPTType::T_WCHAR_STR: + { + PRUint32 len = reader.GetInt32(); + if (len == (PRUint32) -1) + { + // it's a null string +#ifdef VBOX + *((PRUnichar **) v.val.p) = 0; +#else + v.val.p = 0; +#endif + } + else + { + PRUnichar *buf = (PRUnichar *) nsMemory::Alloc(len + 2); + reader.GetBytes(buf, len); + buf[len / 2] = PRUnichar(0); + + *((PRUnichar **) v.val.p) = buf; + } + } + break; + + case nsXPTType::T_INTERFACE: + case nsXPTType::T_INTERFACE_IS: + { + // stub creation will be handled outside this routine. we only + // deserialize the DConAddr and the original value of v.val.p + // into v.val.p temporarily. needs temporary memory alloc. + DConAddrPlusPtr *buf = (DConAddrPlusPtr *) nsMemory::Alloc(sizeof(DConAddrPlusPtr)); + reader.GetBytes(&buf->addr, sizeof(DConAddr)); + buf->p = v.val.p; + v.val.p = buf; + } + break; + + case nsXPTType::T_ASTRING: + case nsXPTType::T_DOMSTRING: + { + PRUint32 len = reader.GetInt32(); + + nsAString *str = (nsAString *) v.val.p; + + nsAString::iterator begin; + str->SetLength(len / 2); + str->BeginWriting(begin); + + reader.GetBytes(begin.get(), len); + } + break; + + case nsXPTType::T_UTF8STRING: + case nsXPTType::T_CSTRING: + { + PRUint32 len = reader.GetInt32(); + + nsACString *str = (nsACString *) v.val.p; + + nsACString::iterator begin; + str->SetLength(len); + str->BeginWriting(begin); + + reader.GetBytes(begin.get(), len); + } + break; + + case nsXPTType::T_ARRAY: + // arrays are deserialized after all other params outside this routine + break; + + case nsXPTType::T_VOID: + case nsXPTType::T_PSTRING_SIZE_IS: + case nsXPTType::T_PWSTRING_SIZE_IS: + default: + LOG(("unexpected parameter type\n")); + return NS_ERROR_UNEXPECTED; + } + return NS_OK; +} + +//----------------------------------------------------------------------------- +// +// Returns an element from the nsXPTCMiniVariant array by properly casting it to +// nsXPTCVariant when requested +#define GET_PARAM(params, isXPTCVariantArray, idx) \ + (isXPTCVariantArray ? ((nsXPTCVariant *) params) [idx] : params [idx]) + +// isResult is PR_TRUE if the size_is and length_is params are out or retval +// so that nsXPTCMiniVariants contain pointers to their locations instead of the +// values themselves. +static nsresult +GetArrayParamInfo(nsIInterfaceInfo *iinfo, uint16 methodIndex, + const nsXPTMethodInfo &methodInfo, nsXPTCMiniVariant *params, + PRBool isXPTCVariantArray, const nsXPTParamInfo ¶mInfo, + PRBool isResult, PRUint32 &size, PRUint32 &length, + nsXPTType &elemType) +{ + // XXX multidimensional arrays are not supported so dimension is always 0 for + // getting the size_is argument number of the array itself and 1 for getting + // the type of elements stored in the array. + + nsresult rv; + + // get the array size + PRUint8 sizeArg; + rv = iinfo->GetSizeIsArgNumberForParam(methodIndex, ¶mInfo, 0, &sizeArg); + if (NS_FAILED(rv)) + return rv; + + // get the number of valid elements + PRUint8 lenArg; + rv = iinfo->GetLengthIsArgNumberForParam(methodIndex, ¶mInfo, 0, &lenArg); + if (NS_FAILED(rv)) + return rv; + + // according to XPT specs + // (http://www.mozilla.org/scriptable/typelib_file.html), size_is and + // length_is for arrays is always uint32. Check this too. + { + nsXPTParamInfo pi = methodInfo.GetParam (sizeArg); + if (pi.GetType().TagPart() != nsXPTType::T_U32) + { + LOG(("unexpected size_is() parameter type: $d\n", + pi.GetType().TagPart())); + return NS_ERROR_UNEXPECTED; + } + + pi = methodInfo.GetParam (lenArg); + if (pi.GetType().TagPart() != nsXPTType::T_U32) + { + LOG(("unexpected length_is() parameter type: $d\n", + pi.GetType().TagPart())); + return NS_ERROR_UNEXPECTED; + } + } + + if (isResult) + { + length = *((PRUint32 *) GET_PARAM(params,isXPTCVariantArray, lenArg).val.p); + size = *((PRUint32 *) GET_PARAM(params, isXPTCVariantArray, sizeArg).val.p); + } + else + { + length = GET_PARAM(params, isXPTCVariantArray, lenArg).val.u32; + size = GET_PARAM(params, isXPTCVariantArray, sizeArg).val.u32; + } + + if (length > size) + { + NS_WARNING("length_is() value is greater than size_is() value"); + length = size; + } + + // get type of array elements + rv = iinfo->GetTypeForParam(methodIndex, ¶mInfo, 1, &elemType); + if (NS_FAILED(rv)) + return rv; + + if (elemType.IsArithmetic() && + (elemType.IsPointer() || elemType.IsUniquePointer() || + elemType.IsReference())) + { + LOG(("arrays of pointers and references to arithmetic types are " + "not yet supported\n")); + return NS_ERROR_NOT_IMPLEMENTED; + } + + if (elemType.IsArray()) + { + LOG(("multidimensional arrays are not yet supported\n")); + return NS_ERROR_NOT_IMPLEMENTED; + } + + return NS_OK; +} + +static nsresult +GetTypeSize(const nsXPTType &type, PRUint32 &size, PRBool &isSimple) +{ + // get the type size in bytes + size = 0; + isSimple = PR_TRUE; + switch (type.TagPart()) + { + case nsXPTType::T_I8: size = sizeof(PRInt8); break; + case nsXPTType::T_I16: size = sizeof(PRInt16); break; + case nsXPTType::T_I32: size = sizeof(PRInt32); break; + case nsXPTType::T_I64: size = sizeof(PRInt64); break; + case nsXPTType::T_U8: size = sizeof(PRUint8); break; + case nsXPTType::T_U16: size = sizeof(PRUint16); break; + case nsXPTType::T_U32: size = sizeof(PRUint32); break; + case nsXPTType::T_U64: size = sizeof(PRUint64); break; + case nsXPTType::T_FLOAT: size = sizeof(float); break; + case nsXPTType::T_DOUBLE: size = sizeof(double); break; + case nsXPTType::T_BOOL: size = sizeof(PRBool); break; + case nsXPTType::T_CHAR: size = sizeof(char); break; + case nsXPTType::T_WCHAR: size = sizeof(PRUnichar); break; + case nsXPTType::T_IID: /* fall through */ + case nsXPTType::T_CHAR_STR: /* fall through */ + case nsXPTType::T_WCHAR_STR: /* fall through */ + case nsXPTType::T_ASTRING: /* fall through */ + case nsXPTType::T_DOMSTRING: /* fall through */ + case nsXPTType::T_UTF8STRING: /* fall through */ + case nsXPTType::T_CSTRING: /* fall through */ + size = sizeof(void *); + isSimple = PR_FALSE; + break; + case nsXPTType::T_INTERFACE: /* fall through */ + case nsXPTType::T_INTERFACE_IS: /* fall through */ + size = sizeof(DConAddr); + isSimple = PR_FALSE; + break; + default: + LOG(("unexpected parameter type: %d\n", type.TagPart())); + return NS_ERROR_UNEXPECTED; + } + + return NS_OK; +} + +static nsresult +SerializeArrayParam(ipcDConnectService *dConnect, + ipcMessageWriter &writer, PRUint32 peerID, + nsIInterfaceInfo *iinfo, uint16 methodIndex, + const nsXPTMethodInfo &methodInfo, + nsXPTCMiniVariant *params, PRBool isXPTCVariantArray, + const nsXPTParamInfo ¶mInfo, + void *array, nsVoidArray &wrappers) +{ + if (!array) + { + // put 0 to indicate null array + writer.PutInt8(0); + return NS_OK; + } + + // put 1 to indicate non-null array + writer.PutInt8(1); + + PRUint32 size = 0; + PRUint32 length = 0; + nsXPTType elemType; + + nsresult rv = GetArrayParamInfo(iinfo, methodIndex, methodInfo, params, + isXPTCVariantArray, paramInfo, PR_FALSE, + size, length, elemType); + if (NS_FAILED (rv)) + return rv; + + PRUint32 elemSize = 0; + PRBool isSimple = PR_TRUE; + rv = GetTypeSize(elemType, elemSize, isSimple); + if (NS_FAILED (rv)) + return rv; + + if (isSimple) + { + // this is a simple arithmetic type, write the whole array at once + writer.PutBytes(array, length * elemSize); + return NS_OK; + } + + // iterate over valid (length_is) elements of the array + // and serialize each of them + nsXPTCMiniVariant v; + for (PRUint32 i = 0; i < length; ++i) + { + v.val.p = ((void **) array) [i]; + + if (elemType.IsInterfacePointer()) + { + nsID iid; + rv = dConnect->GetIIDForMethodParam(iinfo, &methodInfo, paramInfo, elemType, + methodIndex, params, isXPTCVariantArray, + iid); + if (NS_SUCCEEDED(rv)) + rv = dConnect->SerializeInterfaceParam(writer, peerID, iid, + (nsISupports *) v.val.p, + wrappers); + } + else + rv = SerializeParam(writer, elemType, v); + + if (NS_FAILED(rv)) + return rv; + } + + return NS_OK; +} + +// isResult is PR_TRUE if the array param is out or retval +static nsresult +DeserializeArrayParam(ipcDConnectService *dConnect, + ipcMessageReader &reader, PRUint32 peerID, + nsIInterfaceInfo *iinfo, uint16 methodIndex, + const nsXPTMethodInfo &methodInfo, + nsXPTCMiniVariant *params, PRBool isXPTCVariantArray, + const nsXPTParamInfo ¶mInfo, + PRBool isResult, void *&array) +{ + PRUint32 size = 0; + PRUint32 length = 0; + nsXPTType elemType; + + nsresult rv = GetArrayParamInfo(iinfo, methodIndex, methodInfo, params, + isXPTCVariantArray, paramInfo, isResult, + size, length, elemType); + if (NS_FAILED(rv)) + return rv; + + PRUint8 prefix = reader.GetInt8(); + if (prefix == 0) + { + // it's a null array + array = nsnull; + return NS_OK; + } + // sanity + if (prefix != 1) + { + LOG(("unexpected array prefix: %u\n", prefix)); + return NS_ERROR_UNEXPECTED; + } + + PRUint32 elemSize = 0; + PRBool isSimple = PR_TRUE; + rv = GetTypeSize(elemType, elemSize, isSimple); + if (NS_FAILED (rv)) + return rv; + + // Note: for zero-sized arrays, we use the size of 1 because whether + // malloc(0) returns a null pointer or not (which is used in isNull()) + // is implementation-dependent according to the C standard + void *arr = nsMemory::Alloc((size ? size : 1) * elemSize); + if (arr == nsnull) + return NS_ERROR_OUT_OF_MEMORY; + + // initialize the unused space of the array with zeroes + if (length < size) + memset(((PRUint8 *) arr) + length * elemSize, 0, + (size - length) * elemSize); + + if (isSimple) + { + // this is a simple arithmetic type, read the whole array at once + reader.GetBytes(arr, length * elemSize); + + array = arr; + return NS_OK; + } + + // iterate over valid (length_is) elements of the array + // and deserialize each of them individually + nsXPTCVariant v; + for (PRUint32 i = 0; i < length; ++i) + { + rv = DeserializeParam(reader, elemType, v); + + if (NS_SUCCEEDED(rv) && elemType.IsInterfacePointer()) + { + // grab the DConAddr value temporarily stored in the param + PtrBits bits = v.val.u64; + + // DeserializeInterfaceParamBits needs IID only if it's a remote object + nsID iid; + if (bits & PTRBITS_REMOTE_BIT) + rv = dConnect->GetIIDForMethodParam(iinfo, &methodInfo, paramInfo, + elemType, methodIndex, + params, isXPTCVariantArray, iid); + if (NS_SUCCEEDED(rv)) + { + nsISupports *obj = nsnull; + rv = dConnect->DeserializeInterfaceParamBits(bits, peerID, iid, obj); + if (NS_SUCCEEDED(rv)) + v.val.p = obj; + } + } + + if (NS_FAILED(rv)) + break; + + // note that we discard extended param informaton provided by nsXPTCVariant + // and will have to "reconstruct" it from the type tag in FinishArrayParam() + ((void **) arr) [i] = v.val.p; + } + + if (NS_FAILED(rv)) + nsMemory::Free(arr); + else + array = arr; + + return rv; +} + +static void +FinishArrayParam(nsIInterfaceInfo *iinfo, uint16 methodIndex, + const nsXPTMethodInfo &methodInfo, nsXPTCMiniVariant *params, + PRBool isXPTCVariantArray, const nsXPTParamInfo ¶mInfo, + const nsXPTCMiniVariant &arrayVal) +{ + // nothing to do for a null array + void *arr = arrayVal.val.p; + if (!arr) + return; + + PRUint32 size = 0; + PRUint32 length = 0; + nsXPTType elemType; + + // note that FinishArrayParam is called only from OnInvoke to free memory + // after the call has been served. When OnInvoke sets up out and retval + // parameters for the real method, it passes pointers to the nsXPTCMiniVariant + // elements of the params array themselves so that they will eventually + // receive the returned values. For this reason, both in 'in' param and + // 'out/retaval' param cases, size_is and length_is may be read by + // GetArrayParamInfo() by value. Therefore, isResult is always PR_FALSE. + nsresult rv = GetArrayParamInfo(iinfo, methodIndex, methodInfo, params, + isXPTCVariantArray, paramInfo, PR_FALSE, + size, length, elemType); + if (NS_FAILED (rv)) + return; + + nsXPTCVariant v; + v.ptr = nsnull; + v.flags = 0; + + // iterate over valid (length_is) elements of the array + // and free each of them + for (PRUint32 i = 0; i < length; ++i) + { + v.type = elemType.TagPart(); + + switch (elemType.TagPart()) + { + case nsXPTType::T_I8: /* fall through */ + case nsXPTType::T_I16: /* fall through */ + case nsXPTType::T_I32: /* fall through */ + case nsXPTType::T_I64: /* fall through */ + case nsXPTType::T_U8: /* fall through */ + case nsXPTType::T_U16: /* fall through */ + case nsXPTType::T_U32: /* fall through */ + case nsXPTType::T_U64: /* fall through */ + case nsXPTType::T_FLOAT: /* fall through */ + case nsXPTType::T_DOUBLE: /* fall through */ + case nsXPTType::T_BOOL: /* fall through */ + case nsXPTType::T_CHAR: /* fall through */ + case nsXPTType::T_WCHAR: /* fall through */ + // nothing to free for arithmetic types + continue; + case nsXPTType::T_IID: /* fall through */ + case nsXPTType::T_CHAR_STR: /* fall through */ + case nsXPTType::T_WCHAR_STR: /* fall through */ + v.val.p = ((void **) arr) [i]; + v.SetValIsAllocated(); + break; + case nsXPTType::T_INTERFACE: /* fall through */ + case nsXPTType::T_INTERFACE_IS: /* fall through */ + v.val.p = ((void **) arr) [i]; + v.SetValIsInterface(); + break; + case nsXPTType::T_ASTRING: /* fall through */ + case nsXPTType::T_DOMSTRING: /* fall through */ + v.val.p = ((void **) arr) [i]; + v.SetValIsDOMString(); + break; + case nsXPTType::T_UTF8STRING: /* fall through */ + v.val.p = ((void **) arr) [i]; + v.SetValIsUTF8String(); + break; + case nsXPTType::T_CSTRING: /* fall through */ + v.val.p = ((void **) arr) [i]; + v.SetValIsCString(); + break; + default: + LOG(("unexpected parameter type: %d\n", elemType.TagPart())); + return; + } + + FinishParam(v); + } +} + +//----------------------------------------------------------------------------- + +static PRUint32 +NewRequestIndex() +{ + static PRInt32 sRequestIndex; + return (PRUint32) PR_AtomicIncrement(&sRequestIndex); +} + +//----------------------------------------------------------------------------- + +#ifdef VBOX +typedef struct ClientDownInfo +{ + ClientDownInfo(PRUint32 aClient) + { + uClient = aClient; + uTimestamp = PR_IntervalNow(); + } + + PRUint32 uClient; + PRIntervalTime uTimestamp; +} ClientDownInfo; +typedef std::map<PRUint32, ClientDownInfo *> ClientDownMap; +typedef std::list<ClientDownInfo *> ClientDownList; + +#define MAX_CLIENT_DOWN_SIZE 10000 + +/* Protected by the queue monitor. */ +static ClientDownMap g_ClientDownMap; +static ClientDownList g_ClientDownList; + +#endif /* VBOX */ + +class DConnectMsgSelector : public ipcIMessageObserver +{ +public: + DConnectMsgSelector(PRUint32 peer, PRUint8 opCodeMajor, PRUint32 requestIndex) + : mPeer (peer) + , mOpCodeMajor(opCodeMajor) + , mRequestIndex(requestIndex) + {} + + // stack based only + NS_IMETHOD_(nsrefcnt) AddRef() { return 1; } + NS_IMETHOD_(nsrefcnt) Release() { return 1; } + + NS_IMETHOD QueryInterface(const nsIID &aIID, void **aInstancePtr); + + NS_IMETHOD OnMessageAvailable(PRUint32 aSenderID, const nsID &aTarget, + const PRUint8 *aData, PRUint32 aDataLen) + { + // accept special "client dead" messages for a given peer + // (empty target id, zero data and data length) +#ifndef VBOX + if (aSenderID == mPeer && aTarget.Equals(nsID()) && !aData && !aDataLen) + return NS_OK; +#else /* VBOX */ + if (aSenderID != IPC_SENDER_ANY && aTarget.Equals(nsID()) && !aData && !aDataLen) + { + // Insert new client down information. Start by expiring outdated + // entries and free one element if there's still no space (if needed). + PRIntervalTime now = PR_IntervalNow(); + while (!g_ClientDownList.empty()) + { + ClientDownInfo *cInfo = g_ClientDownList.back(); + PRInt64 diff = (PRInt64)now - cInfo->uTimestamp; + if (diff < 0) + diff += (PRInt64)((PRIntervalTime)-1) + 1; + if (diff > PR_SecondsToInterval(15 * 60)) + { + g_ClientDownMap.erase(cInfo->uClient); + g_ClientDownList.pop_back(); + NS_ASSERTION(g_ClientDownMap.size() == g_ClientDownList.size(), + "client down info inconsistency during expiry"); + delete cInfo; + } + else + break; + } + + ClientDownMap::iterator it = g_ClientDownMap.find(aSenderID); + if (it == g_ClientDownMap.end()) + { + /* Getting size of a map is O(1), size of a list can be O(n). */ + while (g_ClientDownMap.size() >= MAX_CLIENT_DOWN_SIZE) + { + ClientDownInfo *cInfo = g_ClientDownList.back(); + g_ClientDownMap.erase(cInfo->uClient); + g_ClientDownList.pop_back(); + NS_ASSERTION(g_ClientDownMap.size() == g_ClientDownList.size(), + "client down info inconsistency during emergency evicting"); + delete cInfo; + } + + ClientDownInfo *cInfo = new ClientDownInfo(aSenderID); + RTMEM_MAY_LEAK(cInfo); /* tstVBoxAPIPerf leaks one allocated during ComPtr<IVirtualBoxClient>::createInprocObject(). */ + g_ClientDownMap[aSenderID] = cInfo; + g_ClientDownList.push_front(cInfo); + NS_ASSERTION(g_ClientDownMap.size() == g_ClientDownList.size(), + "client down info inconsistency after adding entry"); + } + return (aSenderID == mPeer) ? NS_OK : IPC_WAIT_NEXT_MESSAGE; + } + // accept special "client up" messages for a given peer + // (empty target id, zero data and data length=1) + if (aTarget.Equals(nsID()) && !aData && aDataLen == 1) + { + ClientDownMap::iterator it = g_ClientDownMap.find(aSenderID); + if (it != g_ClientDownMap.end()) + { + ClientDownInfo *cInfo = it->second; + g_ClientDownMap.erase(it); + g_ClientDownList.remove(cInfo); + NS_ASSERTION(g_ClientDownMap.size() == g_ClientDownList.size(), + "client down info inconsistency in client up case"); + delete cInfo; + } + return (aSenderID == mPeer) ? NS_OK : IPC_WAIT_NEXT_MESSAGE; + } + // accept special "client check" messages for an anonymous sender + // (invalid sender id, empty target id, zero data and data length + if (aSenderID == IPC_SENDER_ANY && aTarget.Equals(nsID()) && !aData && !aDataLen) + { + LOG(("DConnectMsgSelector::OnMessageAvailable: poll liveness for mPeer=%d\n", + mPeer)); + ClientDownMap::iterator it = g_ClientDownMap.find(mPeer); + return (it == g_ClientDownMap.end()) ? IPC_WAIT_NEXT_MESSAGE : NS_OK; + } +#endif /* VBOX */ + const DConnectOp *op = (const DConnectOp *) aData; + // accept only reply messages with the given peer/opcode/index + // (to prevent eating replies the other thread might be waiting for) + // as well as any non-reply messages (to serve external requests that + // might arrive while we're waiting for the given reply). + if (aDataLen >= sizeof(DConnectOp) && + ((op->opcode_major != DCON_OP_SETUP_REPLY && + op->opcode_major != DCON_OP_INVOKE_REPLY) || + (aSenderID == mPeer && + op->opcode_major == mOpCodeMajor && + op->request_index == mRequestIndex))) + return NS_OK; + else + return IPC_WAIT_NEXT_MESSAGE; + } + + const PRUint32 mPeer; + const PRUint8 mOpCodeMajor; + const PRUint32 mRequestIndex; +}; +NS_IMPL_QUERY_INTERFACE1(DConnectMsgSelector, ipcIMessageObserver) + +class DConnectCompletion : public ipcIMessageObserver +{ +public: + DConnectCompletion(PRUint32 peer, PRUint8 opCodeMajor, PRUint32 requestIndex) + : mSelector(peer, opCodeMajor, requestIndex) + {} + + // stack based only + NS_IMETHOD_(nsrefcnt) AddRef() { return 1; } + NS_IMETHOD_(nsrefcnt) Release() { return 1; } + + NS_IMETHOD QueryInterface(const nsIID &aIID, void **aInstancePtr); + + NS_IMETHOD OnMessageAvailable(PRUint32 aSenderID, const nsID &aTarget, + const PRUint8 *aData, PRUint32 aDataLen) + { + const DConnectOp *op = (const DConnectOp *) aData; + LOG(( + "DConnectCompletion::OnMessageAvailable: " + "senderID=%d, opcode_major=%d, index=%d (waiting for %d)\n", + aSenderID, op->opcode_major, op->request_index, mSelector.mRequestIndex + )); + if (aSenderID == mSelector.mPeer && + op->opcode_major == mSelector.mOpCodeMajor && + op->request_index == mSelector.mRequestIndex) + { + OnResponseAvailable(aSenderID, op, aDataLen); + } + else + { + // ensure ipcDConnectService is not deleted before we finish + nsRefPtr <ipcDConnectService> dConnect (ipcDConnectService::GetInstance()); + if (dConnect) + dConnect->OnMessageAvailable(aSenderID, aTarget, aData, aDataLen); + } + return NS_OK; + } + + virtual void OnResponseAvailable(PRUint32 sender, const DConnectOp *op, PRUint32 opLen) = 0; + + DConnectMsgSelector &GetSelector() + { + return mSelector; + } + +protected: + DConnectMsgSelector mSelector; +}; +NS_IMPL_QUERY_INTERFACE1(DConnectCompletion, ipcIMessageObserver) + +//----------------------------------------------------------------------------- + +class DConnectInvokeCompletion : public DConnectCompletion +{ +public: + DConnectInvokeCompletion(PRUint32 peer, const DConnectInvoke *invoke) + : DConnectCompletion(peer, DCON_OP_INVOKE_REPLY, invoke->request_index) + , mReply(nsnull) + , mParamsLen(0) + {} + + ~DConnectInvokeCompletion() { if (mReply) free(mReply); } + + void OnResponseAvailable(PRUint32 sender, const DConnectOp *op, PRUint32 opLen) + { + mReply = (DConnectInvokeReply *) malloc(opLen); + memcpy(mReply, op, opLen); + + // the length in bytes of the parameter blob + mParamsLen = opLen - sizeof(*mReply); + } + + PRBool IsPending() const { return mReply == nsnull; } + nsresult GetResult() const { return mReply->result; } + + const PRUint8 *Params() const { return (const PRUint8 *) (mReply + 1); } + PRUint32 ParamsLen() const { return mParamsLen; } + + const DConnectInvokeReply *Reply() const { return mReply; } + +private: + DConnectInvokeReply *mReply; + PRUint32 mParamsLen; +}; + +//----------------------------------------------------------------------------- + +#define DCONNECT_STUB_ID \ +{ /* 132c1f14-5442-49cb-8fe6-e60214bbf1db */ \ + 0x132c1f14, \ + 0x5442, \ + 0x49cb, \ + {0x8f, 0xe6, 0xe6, 0x02, 0x14, 0xbb, 0xf1, 0xdb} \ +} +static NS_DEFINE_IID(kDConnectStubID, DCONNECT_STUB_ID); + +// this class represents the non-local object instance. + +class DConnectStub : public nsXPTCStubBase +{ +public: + NS_DECL_ISUPPORTS + + DConnectStub(nsIInterfaceInfo *aIInfo, + DConAddr aInstance, + PRUint32 aPeerID) + : mIInfo(aIInfo) + , mInstance(aInstance) + , mPeerID(aPeerID) + , mCachedISupports(0) + , mRefCntLevels(0) + {} + + NS_HIDDEN ~DConnectStub(); + + // return a refcounted pointer to the InterfaceInfo for this object + // NOTE: on some platforms this MUST not fail or we crash! + NS_IMETHOD GetInterfaceInfo(nsIInterfaceInfo **aInfo); + + // call this method and return result + NS_IMETHOD CallMethod(PRUint16 aMethodIndex, + const nsXPTMethodInfo *aInfo, + nsXPTCMiniVariant *aParams); + + DConAddr Instance() { return mInstance; } + PRUint32 PeerID() { return mPeerID; } + + DConnectStubKey::Key GetKey() { + return DConnectStubKey::Key(mPeerID, mInstance); + } + + NS_IMETHOD_(nsrefcnt) AddRefIPC(); + +private: + nsCOMPtr<nsIInterfaceInfo> mIInfo; + + // uniquely identifies this object instance between peers. + DConAddr mInstance; + + // the "client id" of our IPC peer. this guy owns the real object. + PRUint32 mPeerID; + + // cached nsISupports stub for this object + DConnectStub *mCachedISupports; + + // stack of reference counter values (protected by + // ipcDConnectService::StubLock()) + nsDeque mRefCntLevels; +}; + +NS_IMETHODIMP_(nsrefcnt) +DConnectStub::AddRefIPC() +{ + // in this special version, we memorize the resulting reference count in the + // associated stack array. This stack is then used by Release() to determine + // when it is necessary to send a RELEASE request to the peer owning the + // object in order to balance AddRef() the peer does on DConnectInstance every + // time it passes an object over IPC. + + // NOTE: this function is to be called from DConnectInstance::CreateStub only! + + nsRefPtr <ipcDConnectService> dConnect (ipcDConnectService::GetInstance()); + NS_ASSERTION(dConnect, "no ipcDConnectService (uninitialized?)"); + if (!dConnect) + return 0; + + // dConnect->StubLock() must be already locked here by + // DConnectInstance::CreateStub + + nsrefcnt count = AddRef(); + mRefCntLevels.Push((void *)(uintptr_t) count); + return count; +} + +nsresult +ipcDConnectService::CreateStub(const nsID &iid, PRUint32 peer, DConAddr instance, + DConnectStub **result) +{ + nsresult rv; + + nsCOMPtr<nsIInterfaceInfo> iinfo; + rv = GetInterfaceInfo(iid, getter_AddRefs(iinfo)); + if (NS_FAILED(rv)) + return rv; + + nsAutoLock lock (mLock); + + if (mDisconnected) + return NS_ERROR_NOT_INITIALIZED; + + // we also need the stub lock which protects DConnectStub::mRefCntLevels and + // ipcDConnectService::mStubs + nsAutoLock stubLock (mStubLock); + + DConnectStub *stub = nsnull; + + // first try to find an existing stub for a given peer and instance + // (we do not care about IID because every DConAddr instance represents + // exactly one interface of the real object on the peer's side) + if (!mStubs.Get(DConnectStubKey::Key(peer, instance), &stub)) + { + stub = new DConnectStub(iinfo, instance, peer); + + if (NS_UNLIKELY(!stub)) + rv = NS_ERROR_OUT_OF_MEMORY; + else + { + rv = StoreStub(stub); + if (NS_FAILED(rv)) + delete stub; + } + } + + if (NS_SUCCEEDED(rv)) + { + stub->AddRefIPC(); + *result = stub; + } + + return rv; +} + +nsresult +ipcDConnectService::SerializeInterfaceParam(ipcMessageWriter &writer, + PRUint32 peer, const nsID &iid, + nsISupports *obj, + nsVoidArray &wrappers) +{ + nsAutoLock lock (mLock); + + if (mDisconnected) + return NS_ERROR_NOT_INITIALIZED; + + // we create an instance wrapper, and assume that the other side will send a + // RELEASE message when it no longer needs the instance wrapper. that will + // usually happen after the call returns. + // + // XXX a lazy scheme might be better, but for now simplicity wins. + + // if the interface pointer references a DConnectStub corresponding + // to an object in the address space of the peer, then no need to + // create a new wrapper. + + // if the interface pointer references an object for which we already have + // an existing wrapper, then we use it instead of creating a new one. this + // is based on the assumption that a valid COM object always returns exactly + // the same pointer value in response to every + // QueryInterface(NS_GET_IID(nsISupports), ...). + + if (!obj) + { + // write null address + DConAddr nullobj = 0; + writer.PutBytes(&nullobj, sizeof(nullobj)); + } + else + { + DConnectStub *stub = nsnull; + nsresult rv = obj->QueryInterface(kDConnectStubID, (void **) &stub); + if (NS_SUCCEEDED(rv) && (stub->PeerID() == peer)) + { + DConAddr p = stub->Instance(); + writer.PutBytes(&p, sizeof(p)); + } + else + { + // create instance wrapper + + nsCOMPtr<nsIInterfaceInfo> iinfo; + rv = GetInterfaceInfo(iid, getter_AddRefs(iinfo)); + if (NS_FAILED(rv)) + return rv; + + DConnectInstance *wrapper = nsnull; + + // first try to find an existing wrapper for the given object + if (!FindInstanceAndAddRef(peer, obj, &iid, &wrapper)) + { + wrapper = new DConnectInstance(peer, iinfo, obj); + if (!wrapper) + return NS_ERROR_OUT_OF_MEMORY; + + rv = StoreInstance(wrapper); + if (NS_FAILED(rv)) + { + delete wrapper; + return rv; + } + + // reference the newly created wrapper + wrapper->AddRef(); + } + + // increase the second, IPC-only, reference counter (mandatory before + // trying wrappers.AppendElement() to make sure ReleaseIPC() will remove + // the wrapper from the instance map on failure) + wrapper->AddRefIPC(); + + if (!wrappers.AppendElement(wrapper)) + { + wrapper->ReleaseIPC(); + wrapper->Release(); + return NS_ERROR_OUT_OF_MEMORY; + } + + // wrapper remains referenced when passing it to the client + // (will be released upon DCON_OP_RELEASE) + + // send address of the instance wrapper, and set the low bit to indicate + // to the remote party that this is a remote instance wrapper. + PtrBits bits = ((PtrBits)(uintptr_t) wrapper); + NS_ASSERTION((bits & PTRBITS_REMOTE_BIT) == 0, "remote bit wrong)"); + bits |= PTRBITS_REMOTE_BIT; + writer.PutBytes(&bits, sizeof(bits)); + } + NS_IF_RELEASE(stub); + } + return NS_OK; +} + +// NOTE: peer and iid are ignored if bits doesn't contain PTRBITS_REMOTE_BIT +nsresult +ipcDConnectService::DeserializeInterfaceParamBits(PtrBits bits, PRUint32 peer, + const nsID &iid, + nsISupports *&obj) +{ + nsresult rv; + + obj = nsnull; + + if (bits & PTRBITS_REMOTE_BIT) + { + // pointer is to a remote object. we need to build a stub. + + bits &= ~PTRBITS_REMOTE_BIT; + + DConnectStub *stub; + rv = CreateStub(iid, peer, (DConAddr) bits, &stub); + if (NS_SUCCEEDED(rv)) + obj = stub; + } + else if (bits) + { + // pointer is to one of our instance wrappers. Replace it with the + // real instance. + + DConnectInstance *wrapper = (DConnectInstance *) bits; + // make sure we've been sent a valid wrapper + if (!CheckInstanceAndAddRef(wrapper, peer)) + { + NS_NOTREACHED("instance wrapper not found"); + return NS_ERROR_INVALID_ARG; + } + obj = wrapper->RealInstance(); + NS_ADDREF(obj); + NS_RELEASE(wrapper); + } + else + { + // obj is alredy nsnull + } + + return NS_OK; +} + +//----------------------------------------------------------------------------- + +#define EXCEPTION_STUB_ID \ +{ /* 70578d68-b25e-4370-a70c-89bbe56e6699 */ \ + 0x70578d68, \ + 0xb25e, \ + 0x4370, \ + {0xa7, 0x0c, 0x89, 0xbb, 0xe5, 0x6e, 0x66, 0x99} \ +} +static NS_DEFINE_IID(kExceptionStubID, EXCEPTION_STUB_ID); + +// ExceptionStub is used to cache all primitive-typed bits of a remote nsIException +// instance (such as the error message or line number) to: +// +// a) reduce the number of IPC calls; +// b) make sure exception information is available to the calling party even if +// the called party terminates immediately after returning an exception. +// To achieve this, all cacheable information is serialized together with +// the instance wrapper itself. + +class ExceptionStub : public nsIException +{ +public: + + NS_DECL_ISUPPORTS + NS_DECL_NSIEXCEPTION + + ExceptionStub(const nsACString &aMessage, nsresult aResult, + const nsACString &aName, const nsACString &aFilename, + PRUint32 aLineNumber, PRUint32 aColumnNumber, + DConnectStub *aXcptStub) + : mMessage(aMessage), mResult(aResult) + , mName(aName), mFilename(aFilename) + , mLineNumber (aLineNumber), mColumnNumber (aColumnNumber) + , mXcptStub (aXcptStub) { NS_ASSERTION(aXcptStub, "NULL"); } + + ~ExceptionStub() {} + + nsIException *Exception() { return (nsIException *)(nsISupports *) mXcptStub; } + DConnectStub *Stub() { return mXcptStub; } + +private: + + nsCString mMessage; + nsresult mResult; + nsCString mName; + nsCString mFilename; + PRUint32 mLineNumber; + PRUint32 mColumnNumber; + nsRefPtr<DConnectStub> mXcptStub; +}; + +NS_IMPL_THREADSAFE_ADDREF(ExceptionStub) +NS_IMPL_THREADSAFE_RELEASE(ExceptionStub) + +NS_IMETHODIMP +ExceptionStub::QueryInterface(const nsID &aIID, void **aInstancePtr) +{ + NS_ASSERTION(aInstancePtr, + "QueryInterface requires a non-NULL destination!"); + + // used to discover if this is an ExceptionStub instance. + if (aIID.Equals(kExceptionStubID)) + { + *aInstancePtr = this; + NS_ADDREF_THIS(); + return NS_OK; + } + + // regular NS_IMPL_QUERY_INTERFACE1 sequence + + nsISupports* foundInterface = 0; + + if (aIID.Equals(NS_GET_IID(nsIException))) + foundInterface = NS_STATIC_CAST(nsIException*, this); + else + if (aIID.Equals(NS_GET_IID(nsISupports))) + foundInterface = NS_STATIC_CAST(nsISupports*, + NS_STATIC_CAST(nsIException *, this)); + else + if (mXcptStub) + { + // ask the real nsIException object + return mXcptStub->QueryInterface(aIID, aInstancePtr); + } + + nsresult status; + if (!foundInterface) + status = NS_NOINTERFACE; + else + { + NS_ADDREF(foundInterface); + status = NS_OK; + } + *aInstancePtr = foundInterface; + return status; +} + +/* readonly attribute string message; */ +NS_IMETHODIMP ExceptionStub::GetMessage(char **aMessage) +{ + AssertReturn(aMessage, NS_ERROR_INVALID_POINTER); + *aMessage = ToNewCString(mMessage); + return NS_OK; +} + +/* readonly attribute nsresult result; */ +NS_IMETHODIMP ExceptionStub::GetResult(nsresult *aResult) +{ + AssertReturn(aResult, NS_ERROR_INVALID_POINTER); + *aResult = mResult; + return NS_OK; +} + +/* readonly attribute string name; */ +NS_IMETHODIMP ExceptionStub::GetName(char **aName) +{ + AssertReturn(aName, NS_ERROR_INVALID_POINTER); + *aName = ToNewCString(mName); + return NS_OK; +} + +/* readonly attribute string filename; */ +NS_IMETHODIMP ExceptionStub::GetFilename(char **aFilename) +{ + AssertReturn(aFilename, NS_ERROR_INVALID_POINTER); + *aFilename = ToNewCString(mFilename); + return NS_OK; +} + +/* readonly attribute PRUint32 lineNumber; */ +NS_IMETHODIMP ExceptionStub::GetLineNumber(PRUint32 *aLineNumber) +{ + AssertReturn(aLineNumber, NS_ERROR_INVALID_POINTER); + *aLineNumber = mLineNumber; + return NS_OK; +} + +/* readonly attribute PRUint32 columnNumber; */ +NS_IMETHODIMP ExceptionStub::GetColumnNumber(PRUint32 *aColumnNumber) +{ + AssertReturn(aColumnNumber, NS_ERROR_INVALID_POINTER); + *aColumnNumber = mColumnNumber; + return NS_OK; +} + +/* readonly attribute nsIStackFrame location; */ +NS_IMETHODIMP ExceptionStub::GetLocation(nsIStackFrame **aLocation) +{ + if (Exception()) + return Exception()->GetLocation (aLocation); + return NS_ERROR_UNEXPECTED; +} + +/* readonly attribute nsIException inner; */ +NS_IMETHODIMP ExceptionStub::GetInner(nsIException **aInner) +{ + if (Exception()) + return Exception()->GetInner (aInner); + return NS_ERROR_UNEXPECTED; +} + +/* readonly attribute nsISupports data; */ +NS_IMETHODIMP ExceptionStub::GetData(nsISupports * *aData) +{ + if (Exception()) + return Exception()->GetData (aData); + return NS_ERROR_UNEXPECTED; +} + +/* string toString (); */ +NS_IMETHODIMP ExceptionStub::ToString(char **_retval) +{ + if (Exception()) + return Exception()->ToString (_retval); + return NS_ERROR_UNEXPECTED; +} + +nsresult +ipcDConnectService::SerializeException(ipcMessageWriter &writer, + PRUint32 peer, nsIException *xcpt, + nsVoidArray &wrappers) +{ + PRBool cache_fields = PR_FALSE; + + // first, seralize the nsIException pointer. The code is merely the same as + // in SerializeInterfaceParam() except that when the exception to serialize + // is an ExceptionStub instance and the real instance it stores as mXcpt + // is a DConnectStub corresponding to an object in the address space of the + // peer, we simply pass that object back instead of creating a new wrapper. + + { + nsAutoLock lock (mLock); + + if (mDisconnected) + return NS_ERROR_NOT_INITIALIZED; + + if (!xcpt) + { + // write null address +#ifdef VBOX + // see ipcDConnectService::DeserializeException()! + PtrBits bits = 0; + writer.PutBytes(&bits, sizeof(bits)); +#else + writer.PutBytes(&xcpt, sizeof(xcpt)); +#endif + } + else + { + ExceptionStub *stub = nsnull; + nsresult rv = xcpt->QueryInterface(kExceptionStubID, (void **) &stub); + if (NS_SUCCEEDED(rv) && (stub->Stub()->PeerID() == peer)) + { + // send the wrapper instance back to the peer + DConAddr p = stub->Stub()->Instance(); + writer.PutBytes(&p, sizeof(p)); + } + else + { + // create instance wrapper + + const nsID &iid = nsIException::GetIID(); + nsCOMPtr<nsIInterfaceInfo> iinfo; + rv = GetInterfaceInfo(iid, getter_AddRefs(iinfo)); + if (NS_FAILED(rv)) + return rv; + + DConnectInstance *wrapper = nsnull; + + // first try to find an existing wrapper for the given object + if (!FindInstanceAndAddRef(peer, xcpt, &iid, &wrapper)) + { + wrapper = new DConnectInstance(peer, iinfo, xcpt); + if (!wrapper) + return NS_ERROR_OUT_OF_MEMORY; + + rv = StoreInstance(wrapper); + if (NS_FAILED(rv)) + { + delete wrapper; + return rv; + } + + // reference the newly created wrapper + wrapper->AddRef(); + } + + // increase the second, IPC-only, reference counter (mandatory before + // trying wrappers.AppendElement() to make sure ReleaseIPC() will remove + // the wrapper from the instance map on failure) + wrapper->AddRefIPC(); + + if (!wrappers.AppendElement(wrapper)) + { + wrapper->ReleaseIPC(); + wrapper->Release(); + return NS_ERROR_OUT_OF_MEMORY; + } + + // wrapper remains referenced when passing it to the client + // (will be released upon DCON_OP_RELEASE) + + // send address of the instance wrapper, and set the low bit to indicate + // to the remote party that this is a remote instance wrapper. + PtrBits bits = ((PtrBits)(uintptr_t) wrapper) | PTRBITS_REMOTE_BIT; + writer.PutBytes(&bits, sizeof(bits)); + + // we want to cache fields to minimize the number of IPC calls when + // accessing exception data on the peer side + cache_fields = PR_TRUE; + } + NS_IF_RELEASE(stub); + } + } + + if (!cache_fields) + return NS_OK; + + nsresult rv; + nsXPIDLCString str; + PRUint32 num; + + // message + rv = xcpt->GetMessage(getter_Copies(str)); + if (NS_SUCCEEDED (rv)) + { + PRUint32 len = str.Length(); + nsACString::const_iterator begin; + const char *data = str.BeginReading(begin).get(); + writer.PutInt32(len); + writer.PutBytes(data, len); + } + else + writer.PutInt32(0); + + // result + nsresult res = 0; + xcpt->GetResult(&res); + writer.PutInt32(res); + + // name + rv = xcpt->GetName(getter_Copies(str)); + if (NS_SUCCEEDED (rv)) + { + PRUint32 len = str.Length(); + nsACString::const_iterator begin; + const char *data = str.BeginReading(begin).get(); + writer.PutInt32(len); + writer.PutBytes(data, len); + } + else + writer.PutInt32(0); + + // filename + rv = xcpt->GetFilename(getter_Copies(str)); + if (NS_SUCCEEDED (rv)) + { + PRUint32 len = str.Length(); + nsACString::const_iterator begin; + const char *data = str.BeginReading(begin).get(); + writer.PutInt32(len); + writer.PutBytes(data, len); + } + else + writer.PutInt32(0); + + // lineNumber + num = 0; + xcpt->GetLineNumber(&num); + writer.PutInt32(num); + + // columnNumber + num = 0; + xcpt->GetColumnNumber(&num); + writer.PutInt32(num); + + return writer.HasError() ? NS_ERROR_OUT_OF_MEMORY : NS_OK; +} + +nsresult +ipcDConnectService::DeserializeException(ipcMessageReader &reader, + PRUint32 peer, + nsIException **xcpt) +{ + NS_ASSERTION (xcpt, "NULL"); + if (!xcpt) + return NS_ERROR_INVALID_POINTER; + + nsresult rv; + PRUint32 len; + + PtrBits bits = 0; + reader.GetBytes(&bits, sizeof(DConAddr)); + if (reader.HasError()) + return NS_ERROR_INVALID_ARG; + + if (bits & PTRBITS_REMOTE_BIT) + { + // pointer is a peer-side exception instance wrapper, + // read cahced exception data and create a stub for it. + + nsCAutoString message; + len = reader.GetInt32(); + if (len) + { + message.SetLength(len); + char *buf = message.BeginWriting(); + reader.GetBytes(buf, len); + } + + nsresult result = reader.GetInt32(); + + nsCAutoString name; + len = reader.GetInt32(); + if (len) + { + name.SetLength(len); + char *buf = name.BeginWriting(); + reader.GetBytes(buf, len); + } + + nsCAutoString filename; + len = reader.GetInt32(); + if (len) + { + filename.SetLength(len); + char *buf = filename.BeginWriting(); + reader.GetBytes(buf, len); + } + + PRUint32 lineNumber = reader.GetInt32(); + PRUint32 columnNumber = reader.GetInt32(); + + if (reader.HasError()) + rv = NS_ERROR_INVALID_ARG; + else + { + DConAddr addr = (DConAddr) (bits & ~PTRBITS_REMOTE_BIT); + nsRefPtr<DConnectStub> stub; + rv = CreateStub(nsIException::GetIID(), peer, addr, + getter_AddRefs(stub)); + if (NS_SUCCEEDED(rv)) + { + // create a special exception "stub" with cached error info + ExceptionStub *xcptStub = + new ExceptionStub (message, result, + name, filename, + lineNumber, columnNumber, + stub); + if (xcptStub) + { + *xcpt = xcptStub; + NS_ADDREF(xcptStub); + } + else + rv = NS_ERROR_OUT_OF_MEMORY; + } + } + } + else if (bits) + { + // pointer is to our instance wrapper for nsIException we've sent before + // (the remote method we've called had called us back and got an exception + // from us that it decided to return as its own result). Replace it with + // the real instance. + DConnectInstance *wrapper = (DConnectInstance *) bits; + if (CheckInstanceAndAddRef(wrapper, peer)) + { + *xcpt = (nsIException *) wrapper->RealInstance(); + NS_ADDREF(wrapper->RealInstance()); + wrapper->Release(); + rv = NS_OK; + } + else + { + NS_NOTREACHED("instance wrapper not found"); + rv = NS_ERROR_INVALID_ARG; + } + } + else + { + // the peer explicitly passed us a NULL exception to indicate that the + // exception on the current thread should be reset + *xcpt = NULL; + return NS_OK; + } + + + return rv; +} + +//----------------------------------------------------------------------------- + +DConnectStub::~DConnectStub() +{ +#ifdef IPC_LOGGING + if (IPC_LOG_ENABLED()) + { + const char *name = NULL; + mIInfo->GetNameShared(&name); + LOG(("{%p} DConnectStub::<dtor>(): peer=%d instance=0x%Lx {%s}\n", + this, mPeerID, mInstance, name)); + } +#endif + + // release the cached nsISupports instance if it's not the same object + if (mCachedISupports != 0 && mCachedISupports != this) + NS_RELEASE(mCachedISupports); +} + +NS_IMETHODIMP_(nsrefcnt) +DConnectStub::AddRef() +{ + nsrefcnt count; + count = PR_AtomicIncrement((PRInt32*)&mRefCnt); + NS_LOG_ADDREF(this, count, "DConnectStub", sizeof(*this)); + return count; +} + +NS_IMETHODIMP_(nsrefcnt) +DConnectStub::Release() +{ + nsrefcnt count; + + nsRefPtr <ipcDConnectService> dConnect (ipcDConnectService::GetInstance()); + if (dConnect) + { + // lock the stub lock on every release to make sure that once the counter + // drops to zero, we delete the stub from the set of stubs before a new + // request to create a stub on other thread tries to find the existing + // stub in the set (wchich could otherwise AddRef the object after it had + // Released to zero and pass it to the client right before its + // destruction). + nsAutoLock stubLock (dConnect->StubLock()); + + count = PR_AtomicDecrement((PRInt32 *)&mRefCnt); + NS_LOG_RELEASE(this, count, "DConnectStub"); + + + #ifdef IPC_LOGGING + if (IPC_LOG_ENABLED()) + { + const char *name; + mIInfo->GetNameShared(&name); + LOG(("{%p} DConnectStub::Release(): peer=%d instance=0x%Lx {%s}, new count=%d\n", + this, mPeerID, mInstance, name, count)); + } + #endif + + // mRefCntLevels may already be empty here (due to the "stabilize" trick below) + if (mRefCntLevels.GetSize() > 0) + { + nsrefcnt top = (nsrefcnt) (long) mRefCntLevels.Peek(); + NS_ASSERTION(top <= count + 1, "refcount is beyond the top level"); + + if (top == count + 1) + { + // refcount dropped to a value stored in ipcDConnectService::CreateStub. + // Send a RELEASE request to the peer (see also AddRefIPC). + + // remove the top refcount value + mRefCntLevels.Pop(); + + if (0 == count) + { + // this is the last reference, remove from the set before we leave + // the lock, to provide atomicity of these two operations + dConnect->DeleteStub (this); + + NS_ASSERTION(mRefCntLevels.GetSize() == 0, "refcnt levels are still left"); + } + + // leave the lock before sending a message + stubLock.unlock(); + + nsresult rv; + + DConnectRelease msg; + msg.opcode_major = DCON_OP_RELEASE; + msg.opcode_minor = 0; + msg.flags = 0; + msg.request_index = 0; // not used, set to some unused value + msg.instance = mInstance; + + // fire off asynchronously... we don't expect any response to this message. + rv = IPC_SendMessage(mPeerID, kDConnectTargetID, + (const PRUint8 *) &msg, sizeof(msg)); + if (NS_FAILED(rv)) + NS_WARNING("failed to send RELEASE event"); + } + } + } + else + { + count = PR_AtomicDecrement((PRInt32 *)&mRefCnt); + NS_LOG_RELEASE(this, count, "DConnectStub"); + } + + if (0 == count) + { + mRefCnt = 1; /* stabilize */ + delete this; + return 0; + } + + return count; +} + +NS_IMETHODIMP +DConnectStub::QueryInterface(const nsID &aIID, void **aInstancePtr) +{ + // used to discover if this is a DConnectStub instance. + if (aIID.Equals(kDConnectStubID)) + { + *aInstancePtr = this; + NS_ADDREF_THIS(); + return NS_OK; + } + + // In order to truely support the COM Identity Rule across processes, + // we need to make the following code work: + // + // IFoo *foo = ... + // nsISupports unk; + // foo->QueryInterface(NS_GET_IID(nsISupports), (void **) &unk); + // unk->Release(); + // nsISupports unk2; + // foo->QueryInterface(NS_GET_IID(nsISupports), (void **) &unk2); + // Assert (unk == unk2); + // + // I.e. querying nsISupports on the same object must always return the same + // pointer, even if the nsISupports object returned for the first time is + // released before it is requested for the second time, as long as the + // original object is kept alive (referenced by the client) between these + // two queries. + // + // This is done by remembering the nsISupports stub returned by the peer + // when nsISupports is queried for the first time. The remembered stub, when + // it is not the same as this object, is strongly referenced in order to + // keep it alive (and therefore have the same pointer value) as long as this + // object is alive. + // + // Besides supporting the Identity Rule, this also reduces the number of IPC + // calls, since an IPC call requesting nsISupports will be done only once + // per every stub object. + + nsRefPtr <ipcDConnectService> dConnect (ipcDConnectService::GetInstance()); + NS_ASSERTION(dConnect, "no ipcDConnectService (uninitialized?)"); + if (!dConnect) + return NS_ERROR_NOT_INITIALIZED; + + nsresult rv; + PRBool needISupports = aIID.Equals(NS_GET_IID(nsISupports)); + + if (needISupports) + { + // XXX it would be sufficient to use cmpxchg here to protect access to + // mCachedISupports, but NSPR doesn't provide cross-platform cmpxchg + // functionality, so we have to use a shared lock instead... + PR_Lock(dConnect->StubQILock()); + + // check if we have already got a nsISupports stub for this object + if (mCachedISupports != 0) + { + *aInstancePtr = mCachedISupports; + NS_ADDREF(mCachedISupports); + + PR_Unlock(dConnect->StubQILock()); + return NS_OK; + } + + // check if this object is nsISupports itself + { + nsIID *iid = 0; + rv = mIInfo->GetInterfaceIID(&iid); + NS_ASSERTION(NS_SUCCEEDED(rv) && iid, + "nsIInterfaceInfo::GetInterfaceIID failed"); + if (NS_SUCCEEDED(rv) && iid && + iid->Equals(NS_GET_IID(nsISupports))) + { + nsMemory::Free((void*)iid); + + // nsISupports is queried on nsISupports, return ourselves + *aInstancePtr = this; + NS_ADDREF_THIS(); + // cache ourselves weakly + mCachedISupports = this; + + PR_Unlock(dConnect->StubQILock()); + return NS_OK; + } + if (iid) + nsMemory::Free((void*)iid); + } + + // stub lock remains held until we've queried the peer + } + + // else, we need to query the peer object by making an IPC call + +#ifdef IPC_LOGGING + if (IPC_LOG_ENABLED()) + { + const char *name; + mIInfo->GetNameShared(&name); + const char *nameQ; + nsCOMPtr <nsIInterfaceInfo> iinfoQ; + dConnect->GetInterfaceInfo(aIID, getter_AddRefs(iinfoQ)); + if (iinfoQ) { + iinfoQ->GetNameShared(&nameQ); + LOG(("calling QueryInterface {%s} on peer object " + "(stub=%p, instance=0x%Lx {%s})\n", + nameQ, this, mInstance, name)); + } + } +#endif + + DConnectSetupQueryInterface msg; + msg.opcode_minor = DCON_OP_SETUP_QUERY_INTERFACE; + msg.iid = aIID; + msg.instance = mInstance; + + rv = SetupPeerInstance(mPeerID, &msg, sizeof(msg), aInstancePtr); + + if (needISupports) + { + if (NS_SUCCEEDED(rv)) + { + // cache the nsISupports object (SetupPeerInstance returns DConnectStub) + mCachedISupports = (DConnectStub *) *aInstancePtr; + // use a weak reference if nsISupports is the same object as us + if (this != mCachedISupports) + NS_ADDREF(mCachedISupports); + } + + PR_Unlock(dConnect->StubQILock()); + } + + return rv; +} + +NS_IMETHODIMP +DConnectStub::GetInterfaceInfo(nsIInterfaceInfo **aInfo) +{ + NS_ADDREF(*aInfo = mIInfo); + return NS_OK; +} + +NS_IMETHODIMP +DConnectStub::CallMethod(PRUint16 aMethodIndex, + const nsXPTMethodInfo *aInfo, + nsXPTCMiniVariant *aParams) +{ + LOG(("DConnectStub::CallMethod [methodIndex=%hu]\n", aMethodIndex)); + + nsresult rv; + + // reset the exception early. this is necessary because we may return a + // failure from here without setting an exception (which might be expected + // by the caller to detect the error origin: the interface we are stubbing + // may indicate in some way that it always sets the exception info on + // failure, therefore an "infoless" failure means the origin is RPC). + // besides that, resetting the excetion before every IPC call is exactly the + // same thing as Win32 RPC does, so doing this is useful for getting + // similarity in behaviors. + + nsCOMPtr <nsIExceptionService> es; + es = do_GetService (NS_EXCEPTIONSERVICE_CONTRACTID, &rv); + if (NS_FAILED (rv)) + return rv; + nsCOMPtr <nsIExceptionManager> em; + rv = es->GetCurrentExceptionManager (getter_AddRefs(em)); + if (NS_FAILED (rv)) + return rv; + rv = em->SetCurrentException(NULL); + if (NS_FAILED (rv)) + return rv; + + // ensure ipcDConnectService is not deleted before we finish + nsRefPtr <ipcDConnectService> dConnect (ipcDConnectService::GetInstance()); + if (!dConnect) + return NS_ERROR_FAILURE; + + // dump arguments + + PRUint8 i, paramCount = aInfo->GetParamCount(); + +#ifdef IPC_LOGGING + if (IPC_LOG_ENABLED()) + { + const char *name; + nsCOMPtr<nsIInterfaceInfo> iinfo; + GetInterfaceInfo(getter_AddRefs(iinfo)); + iinfo->GetNameShared(&name); + LOG((" instance=0x%Lx {%s}\n", mInstance, name)); + LOG((" name=%s\n", aInfo->GetName())); + LOG((" param-count=%u\n", (PRUint32) paramCount)); + } +#endif + + + ipcMessageWriter writer(16 * paramCount); + + // INVOKE message header + DConnectInvoke invoke; + invoke.opcode_major = DCON_OP_INVOKE; + invoke.opcode_minor = 0; + invoke.flags = 0; + invoke.request_index = NewRequestIndex(); + invoke.instance = mInstance; + invoke.method_index = aMethodIndex; + + LOG((" request-index=%d\n", (PRUint32) invoke.request_index)); + + writer.PutBytes(&invoke, sizeof(invoke)); + + // list of wrappers that get created during parameter serialization. if we + // are unable to send the INVOKE message, then we'll clean these up. + nsVoidArray wrappers; + + for (i=0; i<paramCount; ++i) + { + const nsXPTParamInfo ¶mInfo = aInfo->GetParam(i); + + if (paramInfo.IsIn() && !paramInfo.IsDipper()) + { + const nsXPTType &type = paramInfo.GetType(); + + if (type.IsInterfacePointer()) + { + nsID iid; + rv = dConnect->GetIIDForMethodParam(mIInfo, aInfo, paramInfo, type, + aMethodIndex, aParams, PR_FALSE, iid); + if (NS_SUCCEEDED(rv)) + rv = dConnect->SerializeInterfaceParam(writer, mPeerID, iid, + (nsISupports *) aParams[i].val.p, + wrappers); + } + else + rv = SerializeParam(writer, type, aParams[i]); + + AssertMsgBreak(NS_SUCCEEDED(rv), ("i=%d rv=%#x\n", i, rv)); + } + else if ((paramInfo.IsOut() || paramInfo.IsRetval()) && !aParams[i].val.p) + { + // report error early if NULL pointer is passed as an output parameter + rv = NS_ERROR_NULL_POINTER; + AssertMsgFailedBreak(("i=%d IsOut=%d IsRetval=%d NS_ERROR_NULL_POINTER\n", i, paramInfo.IsOut(), paramInfo.IsRetval())); + break; + } + } + + if (NS_FAILED(rv)) + { + // INVOKE message wasn't sent; clean up wrappers + dConnect->ReleaseWrappers(wrappers, mPeerID); + return rv; + } + + // serialize input array parameters after everything else since the + // deserialization procedure will need to get a size_is value which may be + // stored in any preceeding or following param + for (i=0; i<paramCount; ++i) + { + const nsXPTParamInfo ¶mInfo = aInfo->GetParam(i); + + if (paramInfo.GetType().IsArray() && + paramInfo.IsIn() && !paramInfo.IsDipper()) + { + rv = SerializeArrayParam(dConnect, writer, mPeerID, mIInfo, aMethodIndex, + *aInfo, aParams, PR_FALSE, paramInfo, + aParams[i].val.p, wrappers); + if (NS_FAILED(rv)) + { + // INVOKE message wasn't sent; clean up wrappers + dConnect->ReleaseWrappers(wrappers, mPeerID); + return rv; + } + } + } + + // temporarily disable the DConnect target observer to block normal processing + // of pending messages through the event queue. + IPC_DISABLE_MESSAGE_OBSERVER_FOR_SCOPE(kDConnectTargetID); + + rv = IPC_SendMessage(mPeerID, kDConnectTargetID, + writer.GetBuffer(), + writer.GetSize()); + LOG(("DConnectStub::CallMethod: IPC_SendMessage()=%08X\n", rv)); + if (NS_FAILED(rv)) + { + // INVOKE message wasn't delivered; clean up wrappers + dConnect->ReleaseWrappers(wrappers, mPeerID); + return rv; + } + + // now, we wait for the method call to complete. during that time, it's + // possible that we'll receive other method call requests. we'll process + // those while waiting for out method call to complete. it's critical that + // we do so since those other method calls might need to complete before + // out method call can complete! + + DConnectInvokeCompletion completion(mPeerID, &invoke); + + do + { + rv = IPC_WaitMessage(IPC_SENDER_ANY, kDConnectTargetID, + &completion.GetSelector(), &completion, + DCON_WAIT_TIMEOUT); + LOG(("DConnectStub::CallMethod: IPC_WaitMessage()=%08X\n", rv)); + if (NS_FAILED(rv)) + { + // INVOKE message wasn't received; clean up wrappers + dConnect->ReleaseWrappers(wrappers, mPeerID); + return rv; + } + } + while (completion.IsPending()); + + ipcMessageReader reader(completion.Params(), completion.ParamsLen()); + + rv = completion.GetResult(); + if (NS_SUCCEEDED(rv)) + { + PRUint8 i; + + // handle out-params and retvals: DCON_OP_INVOKE_REPLY has the data + for (i=0; i<paramCount; ++i) + { + const nsXPTParamInfo ¶mInfo = aInfo->GetParam(i); + + if (paramInfo.IsOut() || paramInfo.IsRetval()) + DeserializeResult(reader, paramInfo.GetType(), aParams[i]); + } + + // fixup any interface pointers using a second pass so we can properly + // handle INTERFACE_IS referencing an IID that is an out param! This pass is + // also used to deserialize arrays (array data goes after all other params). + for (i=0; i<paramCount && NS_SUCCEEDED(rv); ++i) + { + const nsXPTParamInfo ¶mInfo = aInfo->GetParam(i); + if ((paramInfo.IsOut() || paramInfo.IsRetval()) && aParams[i].val.p) + { + const nsXPTType &type = paramInfo.GetType(); + if (type.IsInterfacePointer()) + { + // grab the DConAddr value temporarily stored in the param, restore + // the pointer and free the temporarily allocated memory. + DConAddrPlusPtr *dptr = (DConAddrPlusPtr *)aParams[i].val.p; + PtrBits bits = dptr->addr; + aParams[i].val.p = dptr->p; + nsMemory::Free((void *)dptr); + + // DeserializeInterfaceParamBits needs IID only if it's a remote object + nsID iid; + if (bits & PTRBITS_REMOTE_BIT) + rv = dConnect->GetIIDForMethodParam(mIInfo, aInfo, paramInfo, type, + aMethodIndex, aParams, PR_FALSE, + iid); + if (NS_SUCCEEDED(rv)) + { + nsISupports *obj = nsnull; + rv = dConnect->DeserializeInterfaceParamBits(bits, mPeerID, iid, obj); + if (NS_SUCCEEDED(rv)) + *(void **)aParams[i].val.p = obj; + } + } + else if (type.IsArray()) + { + void *array = nsnull; + rv = DeserializeArrayParam(dConnect, reader, mPeerID, mIInfo, + aMethodIndex, *aInfo, aParams, PR_FALSE, + paramInfo, PR_TRUE, array); + if (NS_SUCCEEDED(rv)) + *(void **)aParams[i].val.p = array; + } + } + } + } + + if (completion.Reply()->flags & DCON_OP_FLAGS_REPLY_EXCEPTION) + { + LOG(("got nsIException instance, will create a stub\n")); + + nsIException *xcpt = nsnull; + rv = dConnect->DeserializeException (reader, mPeerID, &xcpt); + if (NS_SUCCEEDED(rv)) + { + rv = em->SetCurrentException(xcpt); + NS_IF_RELEASE(xcpt); + } + NS_ASSERTION(NS_SUCCEEDED(rv), "failed to deserialize/set exception"); + } + + return NS_SUCCEEDED(rv) ? completion.GetResult() : rv; +} + +//----------------------------------------------------------------------------- + +class DConnectSetupCompletion : public DConnectCompletion +{ +public: + DConnectSetupCompletion(PRUint32 peer, const DConnectSetup *setup) + : DConnectCompletion(peer, DCON_OP_SETUP_REPLY, setup->request_index) + , mSetup(setup) + , mStatus(NS_OK) + {} + + void OnResponseAvailable(PRUint32 sender, const DConnectOp *op, PRUint32 opLen) + { + if (op->opcode_major != DCON_OP_SETUP_REPLY) + { + NS_NOTREACHED("unexpected response"); + mStatus = NS_ERROR_UNEXPECTED; + return; + } + + if (opLen < sizeof(DConnectSetupReply)) + { + NS_NOTREACHED("unexpected response size"); + mStatus = NS_ERROR_UNEXPECTED; + return; + } + + const DConnectSetupReply *reply = (const DConnectSetupReply *) op; + + LOG(("got SETUP_REPLY: status=%x instance=0x%Lx\n", reply->status, reply->instance)); + + mStatus = reply->status; + + if (NS_SUCCEEDED(reply->status)) + { + // ensure ipcDConnectService is not deleted before we finish + nsRefPtr <ipcDConnectService> dConnect (ipcDConnectService::GetInstance()); + nsresult rv; + if (dConnect) + rv = dConnect->CreateStub(mSetup->iid, sender, reply->instance, + getter_AddRefs(mStub)); + else + rv = NS_ERROR_FAILURE; + if (NS_FAILED(rv)) + mStatus = rv; + } + + if (reply->flags & DCON_OP_FLAGS_REPLY_EXCEPTION) + { + const PRUint8 *params = ((const PRUint8 *) op) + sizeof (DConnectSetupReply); + const PRUint32 paramsLen = opLen - sizeof (DConnectSetupReply); + + ipcMessageReader reader(params, paramsLen); + + LOG(("got nsIException instance, will create a stub\n")); + + nsresult rv; + nsCOMPtr <nsIExceptionService> es; + es = do_GetService (NS_EXCEPTIONSERVICE_CONTRACTID, &rv); + if (NS_SUCCEEDED(rv)) + { + nsCOMPtr <nsIExceptionManager> em; + rv = es->GetCurrentExceptionManager (getter_AddRefs(em)); + if (NS_SUCCEEDED(rv)) + { + // ensure ipcDConnectService is not deleted before we finish + nsRefPtr <ipcDConnectService> dConnect (ipcDConnectService::GetInstance()); + if (dConnect) + { + nsIException *xcpt = nsnull; + rv = dConnect->DeserializeException (reader, sender, &xcpt); + if (NS_SUCCEEDED(rv)) + { + rv = em->SetCurrentException(xcpt); + NS_IF_RELEASE(xcpt); + } + } + else + rv = NS_ERROR_UNEXPECTED; + } + } + NS_ASSERTION(NS_SUCCEEDED(rv), "failed to deserialize/set exception"); + if (NS_FAILED(rv)) + mStatus = rv; + } + } + + nsresult GetStub(void **aInstancePtr) + { + if (NS_FAILED(mStatus)) + return mStatus; + + DConnectStub *stub = mStub; + NS_IF_ADDREF(stub); + *aInstancePtr = stub; + return NS_OK; + } + +private: + const DConnectSetup *mSetup; + nsresult mStatus; + nsRefPtr<DConnectStub> mStub; +}; + +// static +nsresult +SetupPeerInstance(PRUint32 aPeerID, DConnectSetup *aMsg, PRUint32 aMsgLen, + void **aInstancePtr) +{ + *aInstancePtr = nsnull; + + aMsg->opcode_major = DCON_OP_SETUP; + aMsg->flags = 0; + aMsg->request_index = NewRequestIndex(); + + // temporarily disable the DConnect target observer to block normal processing + // of pending messages through the event queue. + IPC_DISABLE_MESSAGE_OBSERVER_FOR_SCOPE(kDConnectTargetID); + + // send SETUP message, expect SETUP_REPLY + + nsresult rv = IPC_SendMessage(aPeerID, kDConnectTargetID, + (const PRUint8 *) aMsg, aMsgLen); + if (NS_FAILED(rv)) + return rv; + + DConnectSetupCompletion completion(aPeerID, aMsg); + + // need to allow messages from other clients to be processed immediately + // to avoid distributed dead locks. the completion's OnMessageAvailable + // will call our default OnMessageAvailable if it receives any message + // other than the one for which it is waiting. + + do + { + rv = IPC_WaitMessage(IPC_SENDER_ANY, kDConnectTargetID, + &completion.GetSelector(), &completion, + DCON_WAIT_TIMEOUT); + if (NS_FAILED(rv)) + break; + + rv = completion.GetStub(aInstancePtr); + } + while (NS_SUCCEEDED(rv) && *aInstancePtr == nsnull); + + return rv; +} + +//----------------------------------------------------------------------------- + +#if defined(DCONNECT_MULTITHREADED) && !defined(DCONNECT_WITH_IPRT_REQ_POOL) + +class DConnectWorker : public nsIRunnable +{ +public: + // no reference counting + NS_IMETHOD_(nsrefcnt) AddRef() { return 1; } + NS_IMETHOD_(nsrefcnt) Release() { return 1; } + NS_IMETHOD QueryInterface(const nsIID &aIID, void **aInstancePtr); + + NS_DECL_NSIRUNNABLE + + DConnectWorker(ipcDConnectService *aDConnect) : mDConnect (aDConnect), mIsRunnable (PR_FALSE) {} + NS_HIDDEN_(nsresult) Init(); + NS_HIDDEN_(void) Join() { mThread->Join(); }; + NS_HIDDEN_(bool) IsRunning() { return mIsRunnable; }; + +private: + nsCOMPtr <nsIThread> mThread; + ipcDConnectService *mDConnect; + + // Indicate if thread might be quickly joined on shutdown. + volatile bool mIsRunnable; +}; + +NS_IMPL_QUERY_INTERFACE1(DConnectWorker, nsIRunnable) + +nsresult +DConnectWorker::Init() +{ + return NS_NewThread(getter_AddRefs(mThread), this, 0, PR_JOINABLE_THREAD); +} + +NS_IMETHODIMP +DConnectWorker::Run() +{ + LOG(("DConnect Worker thread started.\n")); + + mIsRunnable = PR_TRUE; + + nsAutoMonitor mon(mDConnect->mPendingMon); + + while (!mDConnect->mDisconnected) + { + DConnectRequest *request = mDConnect->mPendingQ.First(); + if (!request) + { + mDConnect->mWaitingWorkers++; + { + // Note: we attempt to enter mWaitingWorkersMon from under mPendingMon + // here, but it should be safe because it's the only place where it + // happens. We could exit mPendingMon first, but we need to wait on it + // shorltly afterwards, which in turn will require us to enter it again + // just to exit immediately and start waiting. This seems to me a bit + // stupid (exit->enter->exit->wait). + nsAutoMonitor workersMon(mDConnect->mWaitingWorkersMon); + workersMon.NotifyAll(); + } + + nsresult rv = mon.Wait(); + mDConnect->mWaitingWorkers--; + + if (NS_FAILED(rv)) + break; + } + else + { + LOG(("DConnect Worker thread got request.\n")); + + // remove the request from the queue + mDConnect->mPendingQ.RemoveFirst(); + + PRBool pendingQEmpty = mDConnect->mPendingQ.IsEmpty(); + mon.Exit(); + + if (pendingQEmpty) + { + nsAutoMonitor workersMon(mDConnect->mWaitingWorkersMon); + workersMon.NotifyAll(); + } + + // request is processed outside the queue monitor + mDConnect->OnIncomingRequest(request->peer, request->op, request->opLen); + delete request; + + mon.Enter(); + } + } + + mIsRunnable = PR_FALSE; + + LOG(("DConnect Worker thread stopped.\n")); + return NS_OK; +} + +// called only on DConnect message thread +nsresult +ipcDConnectService::CreateWorker() +{ + DConnectWorker *worker = new DConnectWorker(this); + if (!worker) + return NS_ERROR_OUT_OF_MEMORY; + nsresult rv = worker->Init(); + if (NS_SUCCEEDED(rv)) + { + nsAutoLock lock(mLock); + /* tracking an illegal join in Shutdown. */ + NS_ASSERTION(!mDisconnected, "CreateWorker racing Shutdown"); + if (!mWorkers.AppendElement(worker)) + rv = NS_ERROR_OUT_OF_MEMORY; + } + if (NS_FAILED(rv)) + delete worker; + return rv; +} + +#endif // defined(DCONNECT_MULTITHREADED) && !defined(DCONNECT_WITH_IPRT_REQ_POOL) + +//----------------------------------------------------------------------------- + +ipcDConnectService::ipcDConnectService() + : mLock(NULL) + , mStubLock(NULL) + , mDisconnected(PR_TRUE) + , mStubQILock(NULL) +#if defined(DCONNECT_WITH_IPRT_REQ_POOL) + , mhReqPool(NIL_RTREQPOOL) +#endif +{ +} + +PR_STATIC_CALLBACK(PLDHashOperator) +EnumerateInstanceMapAndDelete (const DConnectInstanceKey::Key &aKey, + DConnectInstance *aData, + void *userArg) +{ + // this method is to be called on ipcDConnectService shutdown only + // (after which no DConnectInstances may exist), so forcibly delete them + // disregarding the reference counter + +#ifdef IPC_LOGGING + if (IPC_LOG_ENABLED()) + { + const char *name; + aData->InterfaceInfo()->GetNameShared(&name); + LOG(("ipcDConnectService: WARNING: deleting unreleased " + "instance=%p iface=%p {%s}\n", aData, aData->RealInstance(), name)); + } +#endif + + delete aData; + return PL_DHASH_NEXT; +} + +ipcDConnectService::~ipcDConnectService() +{ + if (!mDisconnected) + Shutdown(); + + mInstance = nsnull; + PR_DestroyLock(mStubQILock); + PR_DestroyLock(mStubLock); + PR_DestroyLock(mLock); +#if defined(DCONNECT_WITH_IPRT_REQ_POOL) + RTReqPoolRelease(mhReqPool); + mhReqPool = NIL_RTREQPOOL; +#endif +} + +//----------------------------------------------------------------------------- + +nsresult +ipcDConnectService::Init() +{ + nsresult rv; + + LOG(("ipcDConnectService::Init.\n")); + + rv = IPC_DefineTarget(kDConnectTargetID, this); + if (NS_FAILED(rv)) + return rv; + + rv = IPC_AddClientObserver(this); + if (NS_FAILED(rv)) + return rv; + + mLock = PR_NewLock(); + if (!mLock) + return NS_ERROR_OUT_OF_MEMORY; + + if (!mInstances.Init()) + return NS_ERROR_OUT_OF_MEMORY; + if (!mInstanceSet.Init()) + return NS_ERROR_OUT_OF_MEMORY; + + mStubLock = PR_NewLock(); + if (!mStubLock) + return NS_ERROR_OUT_OF_MEMORY; + + if (!mStubs.Init()) + return NS_ERROR_OUT_OF_MEMORY; + + mIIM = do_GetService(NS_INTERFACEINFOMANAGER_SERVICE_CONTRACTID, &rv); + if (NS_FAILED(rv)) + return rv; + + mStubQILock = PR_NewLock(); + if (!mStubQILock) + return NS_ERROR_OUT_OF_MEMORY; + +#if defined(DCONNECT_MULTITHREADED) +# if defined(DCONNECT_WITH_IPRT_REQ_POOL) + int vrc = RTReqPoolCreate(1024 /*cMaxThreads*/, 10*RT_MS_1SEC /*cMsMinIdle*/, + 8 /*cThreadsPushBackThreshold */, RT_MS_1SEC /* cMsMaxPushBack */, + "DCon", &mhReqPool); + if (RT_FAILURE(vrc)) + { + mhReqPool = NIL_RTREQPOOL; + return NS_ERROR_FAILURE; + } + + mDisconnected = PR_FALSE; + +# else + + mPendingMon = nsAutoMonitor::NewMonitor("DConnect pendingQ monitor"); + if (!mPendingMon) + return NS_ERROR_OUT_OF_MEMORY; + + mWaitingWorkers = 0; + + mWaitingWorkersMon = nsAutoMonitor::NewMonitor("DConnect waiting workers monitor"); + if (!mWaitingWorkersMon) + return NS_ERROR_OUT_OF_MEMORY; + + /* The DConnectWorker::Run method checks the ipcDConnectService::mDisconnected. + * So mDisconnect must be set here to avoid an immediate exit of the worker thread. + */ + mDisconnected = PR_FALSE; + + // create a single worker thread + rv = CreateWorker(); + if (NS_FAILED(rv)) + { + mDisconnected = PR_TRUE; + return rv; + } + +# endif +#else + + mDisconnected = PR_FALSE; + +#endif + + mInstance = this; + + LOG(("ipcDConnectService::Init NS_OK.\n")); + return NS_OK; +} + +void +ipcDConnectService::Shutdown() +{ + { + // set the disconnected flag to make sensitive public methods + // unavailale from other (non worker) threads. + nsAutoLock lock(mLock); + mDisconnected = PR_TRUE; + } + +#if defined(DCONNECT_MULTITHREADED) +# if defined(DCONNECT_WITH_IPRT_REQ_POOL) + +# if defined(DCONNECT_STATS) + fprintf(stderr, "ipcDConnectService Stats\n"); + fprintf(stderr, + " => number of worker threads: %llu (created %llu)\n" + " => requests processed: %llu\n" + " => avg requests process time: %llu ns\n" + " => avg requests waiting time: %llu ns\n", + RTReqPoolGetStat(mhReqPool, RTREQPOOLSTAT_THREADS), + RTReqPoolGetStat(mhReqPool, RTREQPOOLSTAT_THREADS_CREATED), + RTReqPoolGetStat(mhReqPool, RTREQPOOLSTAT_REQUESTS_PROCESSED), + RTReqPoolGetStat(mhReqPool, RTREQPOOLSTAT_NS_AVERAGE_REQ_PROCESSING), + RTReqPoolGetStat(mhReqPool, RTREQPOOLSTAT_NS_AVERAGE_REQ_QUEUED) + ); +# endif + + RTReqPoolRelease(mhReqPool); + mhReqPool = NIL_RTREQPOOL; + +# else + + { + // remove all pending messages and wake up all workers. + // mDisconnected is true here and they will terminate execution after + // processing the last request. + nsAutoMonitor mon(mPendingMon); + mPendingQ.DeleteAll(); + mon.NotifyAll(); + } + +#if defined(DCONNECT_STATS) + fprintf(stderr, "ipcDConnectService Stats\n"); + fprintf(stderr, " => number of worker threads: %d\n", mWorkers.Count()); + LOG(("ipcDConnectService Stats\n")); + LOG((" => number of worker threads: %d\n", mWorkers.Count())); +#endif + + + // Iterate over currently running worker threads + // during VBOX_XPCOM_SHUTDOWN_TIMEOUT_MS, join() those who + // exited a working loop and abandon ones which have not + // managed to do that when timeout occurred. + LOG(("Worker threads: %d\n", mWorkers.Count())); + uint64_t tsStart = RTTimeMilliTS(); + while ((tsStart + VBOX_XPCOM_SHUTDOWN_TIMEOUT_MS ) > RTTimeMilliTS() && mWorkers.Count() > 0) + { + // Some array elements might be deleted while iterating. Going from the last + // to the first array element (intentionally) in order to do not conflict with + // array indexing once element is deleted. + for (int i = mWorkers.Count() - 1; i >= 0; i--) + { + DConnectWorker *worker = NS_STATIC_CAST(DConnectWorker *, mWorkers[i]); + if (worker->IsRunning() == PR_FALSE) + { + LOG(("Worker %p joined.\n", worker)); + worker->Join(); + delete worker; + mWorkers.RemoveElementAt(i); + } + } + + /* Double-ckeck if we already allowed to quit. */ + if ((tsStart + VBOX_XPCOM_SHUTDOWN_TIMEOUT_MS ) < RTTimeMilliTS() || mWorkers.Count() == 0) + break; + + // Relax a bit before the next round. + RTThreadSleep(10); + } + + LOG(("There are %d thread(s) left.\n", mWorkers.Count())); + + // If there are some running threads left, terminate the process. + if (mWorkers.Count() > 0) + exit(1); + + + nsAutoMonitor::DestroyMonitor(mWaitingWorkersMon); + nsAutoMonitor::DestroyMonitor(mPendingMon); + +# endif +#endif + + // make sure we have released all instances + mInstances.EnumerateRead(EnumerateInstanceMapAndDelete, nsnull); + + mInstanceSet.Clear(); + mInstances.Clear(); + + // clear the stub table + // (this will not release stubs -- it's the client's responsibility) + mStubs.Clear(); +} + +// this should be inlined +nsresult +ipcDConnectService::GetInterfaceInfo(const nsID &iid, nsIInterfaceInfo **result) +{ + return mIIM->GetInfoForIID(&iid, result); +} + +// this is adapted from the version in xpcwrappednative.cpp +nsresult +ipcDConnectService::GetIIDForMethodParam(nsIInterfaceInfo *iinfo, + const nsXPTMethodInfo *methodInfo, + const nsXPTParamInfo ¶mInfo, + const nsXPTType &type, + PRUint16 methodIndex, + nsXPTCMiniVariant *dispatchParams, + PRBool isXPTCVariantArray, + nsID &result) +{ + PRUint8 argnum, tag = type.TagPart(); + nsresult rv; + + if (tag == nsXPTType::T_INTERFACE) + { + rv = iinfo->GetIIDForParamNoAlloc(methodIndex, ¶mInfo, &result); + } + else if (tag == nsXPTType::T_INTERFACE_IS) + { + rv = iinfo->GetInterfaceIsArgNumberForParam(methodIndex, ¶mInfo, &argnum); + if (NS_FAILED(rv)) + return rv; + + const nsXPTParamInfo& arg_param = methodInfo->GetParam(argnum); + const nsXPTType& arg_type = arg_param.GetType(); + + // The xpidl compiler ensures this. We reaffirm it for safety. + if (!arg_type.IsPointer() || arg_type.TagPart() != nsXPTType::T_IID) + return NS_ERROR_UNEXPECTED; + + nsID *p = (nsID *) GET_PARAM(dispatchParams, isXPTCVariantArray, argnum).val.p; + if (!p) + return NS_ERROR_UNEXPECTED; + + result = *p; + } + else + rv = NS_ERROR_UNEXPECTED; + return rv; +} + +nsresult +ipcDConnectService::StoreInstance(DConnectInstance *wrapper) +{ +#ifdef IPC_LOGGING + if (IPC_LOG_ENABLED()) + { + const char *name; + wrapper->InterfaceInfo()->GetNameShared(&name); + LOG(("ipcDConnectService::StoreInstance(): instance=%p iface=%p {%s}\n", + wrapper, wrapper->RealInstance(), name)); + } +#endif + + nsresult rv = mInstanceSet.Put(wrapper); + if (NS_SUCCEEDED(rv)) + { + rv = mInstances.Put(wrapper->GetKey(), wrapper) + ? NS_OK : NS_ERROR_OUT_OF_MEMORY; + if (NS_FAILED(rv)) + mInstanceSet.Remove(wrapper); + } + return rv; +} + +void +ipcDConnectService::DeleteInstance(DConnectInstance *wrapper, + PRBool locked /* = PR_FALSE */) +{ + if (!locked) + PR_Lock(mLock); + +#ifdef IPC_LOGGING + if (IPC_LOG_ENABLED()) + { + const char *name; + wrapper->InterfaceInfo()->GetNameShared(&name); + LOG(("ipcDConnectService::DeleteInstance(): instance=%p iface=%p {%s}\n", + wrapper, wrapper->RealInstance(), name)); + } +#endif + + mInstances.Remove(wrapper->GetKey()); + mInstanceSet.Remove(wrapper); + + if (!locked) + PR_Unlock(mLock); +} + +PRBool +ipcDConnectService::FindInstanceAndAddRef(PRUint32 peer, + const nsISupports *obj, + const nsIID *iid, + DConnectInstance **wrapper) +{ + PRBool result = mInstances.Get(DConnectInstanceKey::Key(peer, obj, iid), wrapper); + if (result) + (*wrapper)->AddRef(); + return result; +} + +PRBool +ipcDConnectService::CheckInstanceAndAddRef(DConnectInstance *wrapper, PRUint32 peer) +{ + nsAutoLock lock (mLock); + + if (mInstanceSet.Contains(wrapper) && wrapper->Peer() == peer) + { + wrapper->AddRef(); + return PR_TRUE; + } + return PR_FALSE; +} + +nsresult +ipcDConnectService::StoreStub(DConnectStub *stub) +{ +#ifdef IPC_LOGGING + if (IPC_LOG_ENABLED()) + { + const char *name; + nsCOMPtr<nsIInterfaceInfo> iinfo; + stub->GetInterfaceInfo(getter_AddRefs(iinfo)); + iinfo->GetNameShared(&name); + LOG(("ipcDConnectService::StoreStub(): stub=%p instance=0x%Lx {%s}\n", + stub, stub->Instance(), name)); + } +#endif + + return mStubs.Put(stub->GetKey(), stub) + ? NS_OK : NS_ERROR_OUT_OF_MEMORY; +} + +void +ipcDConnectService::DeleteStub(DConnectStub *stub) +{ +#ifdef IPC_LOGGING + if (IPC_LOG_ENABLED()) + { + const char *name; + nsCOMPtr<nsIInterfaceInfo> iinfo; + stub->GetInterfaceInfo(getter_AddRefs(iinfo)); + iinfo->GetNameShared(&name); + LOG(("ipcDConnectService::DeleteStub(): stub=%p instance=0x%Lx {%s}\n", + stub, stub->Instance(), name)); + } +#endif + + // this method is intended to be called only from DConnectStub::Release(). + // the stub object is not deleted when removed from the table, because + // DConnectStub pointers are not owned by mStubs. + mStubs.Remove(stub->GetKey()); +} + +// not currently used +#if 0 +PRBool +ipcDConnectService::FindStubAndAddRef(PRUint32 peer, const DConAddr instance, + DConnectStub **stub) +{ + nsAutoLock stubLock (mStubLock); + + PRBool result = mStubs.Get(DConnectStubKey::Key(peer, instance), stub); + if (result) + NS_ADDREF(*stub); + return result; +} +#endif + +NS_IMPL_THREADSAFE_ISUPPORTS3(ipcDConnectService, ipcIDConnectService, + ipcIMessageObserver, + ipcIClientObserver) + +NS_IMETHODIMP +ipcDConnectService::CreateInstance(PRUint32 aPeerID, + const nsID &aCID, + const nsID &aIID, + void **aInstancePtr) +{ + DConnectSetupClassID msg; + msg.opcode_minor = DCON_OP_SETUP_NEW_INST_CLASSID; + msg.iid = aIID; + msg.classid = aCID; + + return SetupPeerInstance(aPeerID, &msg, sizeof(msg), aInstancePtr); +} + +NS_IMETHODIMP +ipcDConnectService::CreateInstanceByContractID(PRUint32 aPeerID, + const char *aContractID, + const nsID &aIID, + void **aInstancePtr) +{ + size_t slen = strlen(aContractID); + size_t size = sizeof(DConnectSetupContractID) + slen; + + DConnectSetupContractID *msg = + (DConnectSetupContractID *) malloc(size); + + msg->opcode_minor = DCON_OP_SETUP_NEW_INST_CONTRACTID; + msg->iid = aIID; + memcpy(&msg->contractid, aContractID, slen + 1); + + nsresult rv = SetupPeerInstance(aPeerID, msg, size, aInstancePtr); + + free(msg); + return rv; +} + +NS_IMETHODIMP +ipcDConnectService::GetService(PRUint32 aPeerID, + const nsID &aCID, + const nsID &aIID, + void **aInstancePtr) +{ + DConnectSetupClassID msg; + msg.opcode_minor = DCON_OP_SETUP_GET_SERV_CLASSID; + msg.iid = aIID; + msg.classid = aCID; + + return SetupPeerInstance(aPeerID, &msg, sizeof(msg), aInstancePtr); +} + +NS_IMETHODIMP +ipcDConnectService::GetServiceByContractID(PRUint32 aPeerID, + const char *aContractID, + const nsID &aIID, + void **aInstancePtr) +{ + size_t slen = strlen(aContractID); + size_t size = sizeof(DConnectSetupContractID) + slen; + + DConnectSetupContractID *msg = + (DConnectSetupContractID *) malloc(size); + + msg->opcode_minor = DCON_OP_SETUP_GET_SERV_CONTRACTID; + msg->iid = aIID; + memcpy(&msg->contractid, aContractID, slen + 1); + + nsresult rv = SetupPeerInstance(aPeerID, msg, size, aInstancePtr); + + free(msg); + return rv; +} + +//----------------------------------------------------------------------------- + +NS_IMETHODIMP +ipcDConnectService::OnMessageAvailable(PRUint32 aSenderID, + const nsID &aTarget, + const PRUint8 *aData, + PRUint32 aDataLen) +{ + if (mDisconnected) + return NS_ERROR_NOT_INITIALIZED; + + const DConnectOp *op = (const DConnectOp *) aData; + + LOG (("ipcDConnectService::OnMessageAvailable: " + "senderID=%d, opcode_major=%d, index=%d\n", + aSenderID, op->opcode_major, op->request_index)); + +#if defined(DCONNECT_MULTITHREADED) +# if defined(DCONNECT_WITH_IPRT_REQ_POOL) + + void *pvDataDup = RTMemDup(aData, aDataLen); + if (RT_UNLIKELY(!pvDataDup)) + return NS_ERROR_OUT_OF_MEMORY; + int rc = RTReqPoolCallVoidNoWait(mhReqPool, (PFNRT)ProcessMessageOnWorkerThread, 4, + this, aSenderID, pvDataDup, aDataLen); + if (RT_FAILURE(rc)) + return NS_ERROR_FAILURE; + +# else + + nsAutoMonitor mon(mPendingMon); + mPendingQ.Append(new DConnectRequest(aSenderID, op, aDataLen)); + // notify a worker + mon.Notify(); + mon.Exit(); + + // Yield the cpu so a worker can get a chance to start working without too much fuss. + PR_Sleep(PR_INTERVAL_NO_WAIT); + mon.Enter(); + // examine the queue + if (mPendingQ.Count() > mWaitingWorkers) + { + // wait a little while to let the workers empty the queue. + mon.Exit(); + { + PRUint32 ticks = PR_MillisecondsToInterval(PR_MIN(mWorkers.Count() / 20 + 1, 10)); + nsAutoMonitor workersMon(mWaitingWorkersMon); + workersMon.Wait(ticks); + } + mon.Enter(); + // examine the queue again + if (mPendingQ.Count() > mWaitingWorkers) + { + // we need one more worker + nsresult rv = CreateWorker(); + NS_ASSERTION(NS_SUCCEEDED(rv), "failed to create one more worker thread"); + rv = rv; + } + } + +# endif +#else + + OnIncomingRequest(aSenderID, op, aDataLen); + +#endif + + return NS_OK; +} + +struct PruneInstanceMapForPeerArgs +{ + ipcDConnectService *that; + PRUint32 clientID; + nsVoidArray &wrappers; +}; + +PR_STATIC_CALLBACK(PLDHashOperator) +PruneInstanceMapForPeer (const DConnectInstanceKey::Key &aKey, + DConnectInstance *aData, + void *userArg) +{ + PruneInstanceMapForPeerArgs *args = (PruneInstanceMapForPeerArgs *)userArg; + NS_ASSERTION(args, "PruneInstanceMapForPeerArgs is NULL"); + + if (args && args->clientID == aData->Peer()) + { + nsrefcnt countIPC = aData->ReleaseIPC(PR_TRUE /* locked */); + + LOG(("ipcDConnectService::PruneInstanceMapForPeer: " + "instance=%p: %d IPC refs to release\n", + aData, countIPC + 1)); + + // release all IPC instances of the "officially dead" client (see + // #OnRelease() to understand why it must be done under the lock). Note + // that due to true multithreading, late OnRelease() requests may still + // happen on other worker threads *after* OnClientStateChange() has been + // called, but it's OK because the instance will be removed from the map + // by the below code alreay and won't be deleted for the second time. + while (countIPC) + { + countIPC = aData->ReleaseIPC(PR_TRUE /* locked */); + aData->Release(); + } + + // collect the instance for the last release + // (we'll do it later outside the lock) + if (!args->wrappers.AppendElement(aData)) + { + NS_NOTREACHED("Not enough memory"); + // bad but what to do + aData->Release(); + } + } + return PL_DHASH_NEXT; +} + +NS_IMETHODIMP +ipcDConnectService::OnClientStateChange(PRUint32 aClientID, + PRUint32 aClientState) +{ + LOG(("ipcDConnectService::OnClientStateChange: aClientID=%d, aClientState=%d\n", + aClientID, aClientState)); + + if (aClientState == ipcIClientObserver::CLIENT_DOWN) + { + if (aClientID == IPC_SENDER_ANY) + { + // a special case: our IPC system is being shutdown, try to safely + // uninitialize everything... + Shutdown(); + } + else + { + LOG(("ipcDConnectService::OnClientStateChange: " + "pruning all instances created for peer %d...\n", aClientID)); + + nsVoidArray wrappers; + + { + nsAutoLock lock (mLock); + + // make sure we have removed all instances from instance maps + PruneInstanceMapForPeerArgs args = { this, aClientID, wrappers }; + mInstances.EnumerateRead(PruneInstanceMapForPeer, (void *)&args); + } + + LOG(("ipcDConnectService::OnClientStateChange: " + "%d lost instances\n", wrappers.Count())); + + // release all pending references left after PruneInstanceMapForPeer(). + // this may call wrapper destructors so it's important to do that + // outside the lock because destructors will release the real objects + // which may need to make asynchronous use our service + for (PRInt32 i = 0; i < wrappers.Count(); ++i) + ((DConnectInstance *) wrappers[i])->Release(); + } + } + + return NS_OK; +} + +//----------------------------------------------------------------------------- + +#if defined(DCONNECT_WITH_IPRT_REQ_POOL) +/** + * Function called by the request thread pool to process a incoming request in + * the context of a worker thread. + */ +/* static */ DECLCALLBACK(void) +ipcDConnectService::ProcessMessageOnWorkerThread(ipcDConnectService *aThis, PRUint32 aSenderID, void *aData, PRUint32 aDataLen) +{ + if (!aThis->mDisconnected) + aThis->OnIncomingRequest(aSenderID, (const DConnectOp *)aData, aDataLen); + RTMemFree(aData); +} +#endif + +void +ipcDConnectService::OnIncomingRequest(PRUint32 peer, const DConnectOp *op, PRUint32 opLen) +{ + switch (op->opcode_major) + { + case DCON_OP_SETUP: + OnSetup(peer, (const DConnectSetup *) op, opLen); + break; + case DCON_OP_RELEASE: + OnRelease(peer, (const DConnectRelease *) op); + break; + case DCON_OP_INVOKE: + OnInvoke(peer, (const DConnectInvoke *) op, opLen); + break; + default: + NS_NOTREACHED("unknown opcode major"); + } +} + +void +ipcDConnectService::OnSetup(PRUint32 peer, const DConnectSetup *setup, PRUint32 opLen) +{ + nsISupports *instance = nsnull; + nsresult rv = NS_ERROR_FAILURE; + + switch (setup->opcode_minor) + { + // CreateInstance + case DCON_OP_SETUP_NEW_INST_CLASSID: + { + const DConnectSetupClassID *setupCI = (const DConnectSetupClassID *) setup; + + nsCOMPtr<nsIComponentManager> compMgr; + rv = NS_GetComponentManager(getter_AddRefs(compMgr)); + if (NS_SUCCEEDED(rv)) + rv = compMgr->CreateInstance(setupCI->classid, nsnull, setupCI->iid, (void **) &instance); + + break; + } + + // CreateInstanceByContractID + case DCON_OP_SETUP_NEW_INST_CONTRACTID: + { + const DConnectSetupContractID *setupCI = (const DConnectSetupContractID *) setup; + + nsCOMPtr<nsIComponentManager> compMgr; + rv = NS_GetComponentManager(getter_AddRefs(compMgr)); + if (NS_SUCCEEDED(rv)) + rv = compMgr->CreateInstanceByContractID(setupCI->contractid, nsnull, setupCI->iid, (void **) &instance); + + break; + } + + // GetService + case DCON_OP_SETUP_GET_SERV_CLASSID: + { + const DConnectSetupClassID *setupCI = (const DConnectSetupClassID *) setup; + + nsCOMPtr<nsIServiceManager> svcMgr; + rv = NS_GetServiceManager(getter_AddRefs(svcMgr)); + if (NS_SUCCEEDED(rv)) + rv = svcMgr->GetService(setupCI->classid, setupCI->iid, (void **) &instance); + break; + } + + // GetServiceByContractID + case DCON_OP_SETUP_GET_SERV_CONTRACTID: + { + const DConnectSetupContractID *setupCI = (const DConnectSetupContractID *) setup; + + nsCOMPtr<nsIServiceManager> svcMgr; + rv = NS_GetServiceManager(getter_AddRefs(svcMgr)); + if (NS_SUCCEEDED(rv)) + rv = svcMgr->GetServiceByContractID(setupCI->contractid, setupCI->iid, (void **) &instance); + + break; + } + + // QueryInterface + case DCON_OP_SETUP_QUERY_INTERFACE: + { + const DConnectSetupQueryInterface *setupQI = (const DConnectSetupQueryInterface *) setup; + DConnectInstance *QIinstance = (DConnectInstance *)setupQI->instance; + + // make sure we've been sent a valid wrapper + if (!CheckInstanceAndAddRef(QIinstance, peer)) + { + NS_NOTREACHED("instance wrapper not found"); + rv = NS_ERROR_INVALID_ARG; + } + else + { + rv = QIinstance->RealInstance()->QueryInterface(setupQI->iid, (void **) &instance); + QIinstance->Release(); + } + break; + } + + default: + NS_NOTREACHED("unexpected minor opcode"); + rv = NS_ERROR_UNEXPECTED; + break; + } + + nsVoidArray wrappers; + + // now, create instance wrapper, and store it in our instances set. + // this allows us to keep track of object references held on behalf of a + // particular peer. we can use this information to cleanup after a peer + // that disconnects without sending RELEASE messages for its objects. + DConnectInstance *wrapper = nsnull; + if (NS_SUCCEEDED(rv)) + { + nsCOMPtr<nsIInterfaceInfo> iinfo; + rv = GetInterfaceInfo(setup->iid, getter_AddRefs(iinfo)); + if (NS_SUCCEEDED(rv)) + { + nsAutoLock lock (mLock); + + // first try to find an existing wrapper for the given object + if (!FindInstanceAndAddRef(peer, instance, &setup->iid, &wrapper)) + { + wrapper = new DConnectInstance(peer, iinfo, instance); + if (!wrapper) + rv = NS_ERROR_OUT_OF_MEMORY; + else + { + rv = StoreInstance(wrapper); + if (NS_FAILED(rv)) + { + delete wrapper; + wrapper = nsnull; + } + else + { + // reference the newly created wrapper + wrapper->AddRef(); + } + } + } + + if (wrapper) + { + // increase the second, IPC-only, reference counter (mandatory before + // trying wrappers.AppendElement() to make sure ReleaseIPC() will remove + // the wrapper from the instance map on failure) + wrapper->AddRefIPC(); + + if (!wrappers.AppendElement(wrapper)) + { + wrapper->ReleaseIPC(); + wrapper->Release(); + rv = NS_ERROR_OUT_OF_MEMORY; + } + } + + // wrapper remains referenced when passing it to the client + // (will be released upon DCON_OP_RELEASE) + } + } + + NS_IF_RELEASE(instance); + + nsCOMPtr <nsIException> exception; + PRBool got_exception = PR_FALSE; + + if (rv != NS_OK) + { + // try to fetch an nsIException possibly set by one of the setup methods + nsresult rv2; + nsCOMPtr <nsIExceptionService> es; + es = do_GetService(NS_EXCEPTIONSERVICE_CONTRACTID, &rv2); + if (NS_SUCCEEDED(rv2)) + { + nsCOMPtr <nsIExceptionManager> em; + rv2 = es->GetCurrentExceptionManager (getter_AddRefs (em)); + if (NS_SUCCEEDED(rv2)) + { + rv2 = em->GetCurrentException (getter_AddRefs (exception)); + if (NS_SUCCEEDED(rv2)) + { + LOG(("got nsIException instance, will serialize\n")); + got_exception = PR_TRUE; + } + } + } + NS_ASSERTION(NS_SUCCEEDED(rv2), "failed to get/serialize exception"); + if (NS_FAILED(rv2)) + rv = rv2; + } + + ipcMessageWriter writer(64); + + DConnectSetupReply msg; + msg.opcode_major = DCON_OP_SETUP_REPLY; + msg.opcode_minor = 0; + msg.flags = 0; + msg.request_index = setup->request_index; + msg.instance = (DConAddr)(uintptr_t)wrapper; + msg.status = rv; + + if (got_exception) + msg.flags |= DCON_OP_FLAGS_REPLY_EXCEPTION; + + writer.PutBytes(&msg, sizeof(msg)); + + if (got_exception) + { + rv = SerializeException(writer, peer, exception, wrappers); + NS_ASSERTION(NS_SUCCEEDED(rv), "failed to get/serialize exception"); + } + + // fire off SETUP_REPLY, don't wait for a response + if (NS_FAILED(rv)) + rv = IPC_SendMessage(peer, kDConnectTargetID, + (const PRUint8 *) &msg, sizeof(msg)); + else + rv = IPC_SendMessage(peer, kDConnectTargetID, + writer.GetBuffer(), writer.GetSize()); + + if (NS_FAILED(rv)) + { + LOG(("unable to send SETUP_REPLY: rv=%x\n", rv)); + ReleaseWrappers(wrappers, peer); + } +} + +void +ipcDConnectService::OnRelease(PRUint32 peer, const DConnectRelease *release) +{ + LOG(("ipcDConnectService::OnRelease [peer=%u instance=0x%Lx]\n", + peer, release->instance)); + + DConnectInstance *wrapper = (DConnectInstance *)release->instance; + + nsAutoLock lock (mLock); + + // make sure we've been sent a valid wrapper from the same peer we created + // this wrapper for + if (mInstanceSet.Contains(wrapper) && wrapper->Peer() == peer) + { + // release the IPC reference from under the lock to ensure atomicity of + // the "check + possible delete" sequence ("delete" is remove this wrapper + // from the instance map when the IPC reference counter drops to zero) + wrapper->ReleaseIPC(PR_TRUE /* locked */); + // leave the lock before Release() because it may call the destructor + // which will release the real object which may need to make asynchronous + // use our service + lock.unlock(); + wrapper->Release(); + } + else + { + // it is possible that the client disconnection event handler has released + // all client instances before the DCON_OP_RELEASE message sent by the + // client gets processed here (because of true multithreading). Just log + // a debug warning + LOG(("ipcDConnectService::OnRelease: WARNING: " + "instance wrapper %p for peer %d not found", wrapper, peer)); + } +} + +void +ipcDConnectService::OnInvoke(PRUint32 peer, const DConnectInvoke *invoke, PRUint32 opLen) +{ + LOG(("ipcDConnectService::OnInvoke [peer=%u instance=0x%Lx method=%u]\n", + peer, invoke->instance, invoke->method_index)); + + DConnectInstance *wrapper = (DConnectInstance *)invoke->instance; + + ipcMessageReader reader((const PRUint8 *) (invoke + 1), opLen - sizeof(*invoke)); + + const nsXPTMethodInfo *methodInfo; + nsXPTCVariant *params = nsnull; + nsCOMPtr<nsIInterfaceInfo> iinfo = nsnull; + PRUint8 i, paramCount = 0, paramUsed = 0; + nsresult rv; + + nsCOMPtr <nsIException> exception; + PRBool got_exception = PR_FALSE; + + // make sure we've been sent a valid wrapper + if (!CheckInstanceAndAddRef(wrapper, peer)) + { + NS_NOTREACHED("instance wrapper not found"); + wrapper = nsnull; + rv = NS_ERROR_INVALID_ARG; + goto end; + } + + iinfo = wrapper->InterfaceInfo(); + + rv = iinfo->GetMethodInfo(invoke->method_index, &methodInfo); + if (NS_FAILED(rv)) + goto end; + + paramCount = methodInfo->GetParamCount(); + + LOG((" iface=%p\n", wrapper->RealInstance())); + LOG((" name=%s\n", methodInfo->GetName())); + LOG((" param-count=%u\n", (PRUint32) paramCount)); + LOG((" request-index=%d\n", (PRUint32) invoke->request_index)); + + params = new nsXPTCVariant[paramCount]; + if (!params) + { + rv = NS_ERROR_OUT_OF_MEMORY; + goto end; + } + + // setup |params| for xptcall + + for (i=0; i<paramCount; ++i, ++paramUsed) + { + const nsXPTParamInfo ¶mInfo = methodInfo->GetParam(i); + + // XXX are inout params an issue? + // yes, we will need to do v.ptr = &v.val for them (DeserializeParam doesn't + // currently do that) to let the callee correctly pick it up and change. + + if (paramInfo.IsIn() && !paramInfo.IsDipper()) + rv = DeserializeParam(reader, paramInfo.GetType(), params[i]); + else + rv = SetupParam(paramInfo, params[i]); + + if (NS_FAILED(rv)) + goto end; + } + + // fixup any interface pointers. we do this with a second pass so that + // we can properly handle INTERFACE_IS. This pass is also used to deserialize + // arrays (array data goes after all other params). + for (i=0; i<paramCount; ++i) + { + const nsXPTParamInfo ¶mInfo = methodInfo->GetParam(i); + if (paramInfo.IsIn()) + { + const nsXPTType &type = paramInfo.GetType(); + if (type.IsInterfacePointer()) + { + // grab the DConAddr value temporarily stored in the param +#ifdef VBOX + PtrBits bits = params[i].val.u64; +#else + PtrBits bits = (PtrBits)(uintptr_t) params[i].val.p; +#endif + + // DeserializeInterfaceParamBits needs IID only if it's a remote object + nsID iid; + if (bits & PTRBITS_REMOTE_BIT) + { + rv = GetIIDForMethodParam(iinfo, methodInfo, paramInfo, type, + invoke->method_index, params, PR_TRUE, iid); + if (NS_FAILED(rv)) + goto end; + } + + nsISupports *obj = nsnull; + rv = DeserializeInterfaceParamBits(bits, peer, iid, obj); + if (NS_FAILED(rv)) + goto end; + + params[i].val.p = obj; + // mark as interface to let FinishParam() release this param + params[i].SetValIsInterface(); + } + else if (type.IsArray()) + { + void *array = nsnull; + rv = DeserializeArrayParam(this, reader, peer, iinfo, + invoke->method_index, *methodInfo, params, + PR_TRUE, paramInfo, PR_FALSE, array); + if (NS_FAILED(rv)) + goto end; + + params[i].val.p = array; + // mark to let FinishParam() free this param + params[i].SetValIsAllocated(); + } + } + } + + rv = XPTC_InvokeByIndex(wrapper->RealInstance(), + invoke->method_index, + paramCount, + params); + + if (rv != NS_OK) + { + // try to fetch an nsIException possibly set by the method + nsresult rv2; + nsCOMPtr <nsIExceptionService> es; + es = do_GetService(NS_EXCEPTIONSERVICE_CONTRACTID, &rv2); + if (NS_SUCCEEDED(rv2)) + { + nsCOMPtr <nsIExceptionManager> em; + rv2 = es->GetCurrentExceptionManager (getter_AddRefs (em)); + if (NS_SUCCEEDED(rv2)) + { + rv2 = em->GetCurrentException (getter_AddRefs (exception)); + if (NS_SUCCEEDED(rv2)) + { + LOG(("got nsIException instance, will serialize\n")); + got_exception = PR_TRUE; + } + } + } + NS_ASSERTION(NS_SUCCEEDED(rv2), "failed to get/serialize exception"); + if (NS_FAILED(rv2)) + rv = rv2; + } + +end: + LOG(("sending INVOKE_REPLY: rv=%x\n", rv)); + + // balance CheckInstanceAndAddRef() + if (wrapper) + wrapper->Release(); + + ipcMessageWriter writer(64); + + DConnectInvokeReply reply; + reply.opcode_major = DCON_OP_INVOKE_REPLY; + reply.opcode_minor = 0; + reply.flags = 0; + reply.request_index = invoke->request_index; + reply.result = rv; + + if (got_exception) + reply.flags |= DCON_OP_FLAGS_REPLY_EXCEPTION; + + writer.PutBytes(&reply, sizeof(reply)); + + nsVoidArray wrappers; + + if (NS_SUCCEEDED(rv) && params) + { + // serialize out-params and retvals + for (i=0; i<paramCount; ++i) + { + const nsXPTParamInfo paramInfo = methodInfo->GetParam(i); + + if (paramInfo.IsRetval() || paramInfo.IsOut()) + { + const nsXPTType &type = paramInfo.GetType(); + + if (type.IsInterfacePointer()) + { + nsID iid; + rv = GetIIDForMethodParam(iinfo, methodInfo, paramInfo, type, + invoke->method_index, params, PR_TRUE, iid); + if (NS_SUCCEEDED(rv)) + rv = SerializeInterfaceParam(writer, peer, iid, + (nsISupports *) params[i].val.p, wrappers); + } + else + rv = SerializeParam(writer, type, params[i]); + + if (NS_FAILED(rv)) + { + reply.result = rv; + break; + } + } + } + + if (NS_SUCCEEDED(rv)) + { + // serialize output array parameters after everything else since the + // deserialization procedure will need to get a size_is value which may be + // stored in any preceeding or following param + for (i=0; i<paramCount; ++i) + { + const nsXPTParamInfo ¶mInfo = methodInfo->GetParam(i); + + if (paramInfo.GetType().IsArray() && + (paramInfo.IsRetval() || paramInfo.IsOut())) + { + rv = SerializeArrayParam(this, writer, peer, iinfo, invoke->method_index, + *methodInfo, params, PR_TRUE, paramInfo, + params[i].val.p, wrappers); + if (NS_FAILED(rv)) + { + reply.result = rv; + break; + } + } + } + } + } + + if (got_exception) + { + rv = SerializeException(writer, peer, exception, wrappers); + NS_ASSERTION(NS_SUCCEEDED(rv), "failed to get/serialize exception"); + } + + if (NS_FAILED(rv)) + rv = IPC_SendMessage(peer, kDConnectTargetID, (const PRUint8 *) &reply, sizeof(reply)); + else + rv = IPC_SendMessage(peer, kDConnectTargetID, writer.GetBuffer(), writer.GetSize()); + if (NS_FAILED(rv)) + { + LOG(("unable to send INVOKE_REPLY: rv=%x\n", rv)); + ReleaseWrappers(wrappers, peer); + } + + if (params) + { + // free individual elements of arrays (note: before freeing arrays + // themselves in FinishParam()) + for (i=0; i<paramUsed; ++i) + { + const nsXPTParamInfo ¶mInfo = methodInfo->GetParam(i); + if (paramInfo.GetType().IsArray()) + FinishArrayParam(iinfo, invoke->method_index, *methodInfo, + params, PR_TRUE, paramInfo, params[i]); + } + + for (i=0; i<paramUsed; ++i) + FinishParam(params[i]); + delete[] params; + } +} diff --git a/src/libs/xpcom18a4/ipc/ipcd/extensions/dconnect/src/ipcDConnectService.h b/src/libs/xpcom18a4/ipc/ipcd/extensions/dconnect/src/ipcDConnectService.h new file mode 100644 index 00000000..517b2650 --- /dev/null +++ b/src/libs/xpcom18a4/ipc/ipcd/extensions/dconnect/src/ipcDConnectService.h @@ -0,0 +1,365 @@ +/* vim:set ts=2 sw=2 et cindent: */ +/* ***** 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 IBM Corporation. + * Portions created by the Initial Developer are Copyright (C) 2004 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Darin Fisher <darin@meer.net> + * Dmitry A. Kuminov <dmik@innotek.de> + * + * 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 ***** */ + +// DConnect service is multithreaded by default... +#if !defined(DCONNECT_SINGLETHREADED) && !defined(DCONNECT_MULTITHREADED) +#define DCONNECT_MULTITHREADED +# ifdef VBOX +//# define DCONNECT_WITH_IPRT_REQ_POOL +# endif +#endif + +#include "ipcIDConnectService.h" +#include "ipcdclient.h" + +#include "nsIInterfaceInfo.h" +#include "nsIInterfaceInfoManager.h" + +#include "nsCOMPtr.h" +#include "nsAutoPtr.h" +#include "nsDataHashtable.h" +#include "nsHashKeys.h" +#include "nsHashSets.h" +#include "nsVoidArray.h" +#include "nsAutoLock.h" +#include "xptcall.h" +#include "xptinfo.h" + +#if defined(DCONNECT_MULTITHREADED) +# if defined(DCONNECT_WITH_IPRT_REQ_POOL) + +# include <iprt/req.h> + +# else /* !DCONNECT_WITH_IPRT_REQ_POOL*/ + +#include "ipcList.h" + +struct DConnectOp; + +struct DConnectRequest : public ipcListNode<DConnectRequest> +{ + DConnectRequest (PRUint32 aPeer, const DConnectOp *aOp, PRUint32 aOpLen) + : peer(aPeer) + , opLen(aOpLen) + { + op = (const DConnectOp *) malloc(aOpLen); + memcpy ((void *) op, aOp, aOpLen); + } + ~DConnectRequest() { free((void *) op); } + + const PRUint32 peer; + const DConnectOp *op; + const PRUint32 opLen; +}; + +# endif // !DCONNECT_WITH_IPRT_REQ_POOL +#endif // DCONNECT_MULTITHREADED + +class nsIException; +class ipcMessageReader; +class ipcMessageWriter; + +// a key class used to identify DConnectInstance objects stored in a hash table +// by a composite of peer ID, XPCOM object pointer and IID this pointer represents + +class DConnectInstanceKey : public PLDHashEntryHdr +{ +public: + struct Key + { + Key(PRUint32 aPeer, const nsISupports *aObj, const nsID *aIID) + : mPeer (aPeer), mObj (aObj), mIID (aIID) {} + const PRUint32 mPeer; + const nsISupports *mObj; + const nsIID *mIID; + }; + + typedef const Key &KeyType; + typedef const Key *KeyTypePointer; + + DConnectInstanceKey(const Key *aKey) : mKey (*aKey) {} + DConnectInstanceKey(const DConnectInstanceKey &toCopy) : mKey (toCopy.mKey) {} + ~DConnectInstanceKey() {} + + KeyType GetKey() const { return mKey; } + KeyTypePointer GetKeyPointer() const { return &mKey; } + + PRBool KeyEquals(KeyTypePointer aKey) const { + return mKey.mPeer == aKey->mPeer && + mKey.mObj == aKey->mObj && + mKey.mIID->Equals(*aKey->mIID); + } + + static KeyTypePointer KeyToPointer(KeyType aKey) { return &aKey; } + static PLDHashNumber HashKey(KeyTypePointer aKey) { + return aKey->mPeer ^ + (NS_PTR_TO_INT32(aKey->mObj) >> 2) ^ + nsIDHashKey::HashKey(aKey->mIID); + } + enum { ALLOW_MEMMOVE = PR_TRUE }; + +private: + const Key mKey; +}; + +class DConnectInstance; +typedef nsDataHashtable<DConnectInstanceKey, DConnectInstance *> DConnectInstanceMap; + +// extend nsVoidHashSet for compatibility with some nsTHashtable methods +class DConnectInstanceSet : public nsVoidHashSet +{ +public: + PRBool Init(PRUint32 initSize = PL_DHASH_MIN_SIZE) { + return nsVoidHashSet::Init(initSize) == NS_OK; + } + void Clear() { + PL_DHashTableEnumerate(&mHashTable, PL_DHashStubEnumRemove, nsnull); + } +}; + +typedef PRUint64 DConAddr; + +// a key class used to identify DConnectStub objects stored in a hash table +// by a composite of peer ID and DConAddr + +class DConnectStubKey : public PLDHashEntryHdr +{ +public: + struct Key + { + Key(PRUint32 aPeer, const DConAddr aInstance) + : mPeer (aPeer), mInstance (aInstance) {} + const PRUint32 mPeer; + const DConAddr mInstance; + }; + + typedef const Key &KeyType; + typedef const Key *KeyTypePointer; + + DConnectStubKey(const Key *aKey) : mKey (*aKey) {} + DConnectStubKey(const DConnectStubKey &toCopy) : mKey (toCopy.mKey) {} + ~DConnectStubKey() {} + + KeyType GetKey() const { return mKey; } + KeyTypePointer GetKeyPointer() const { return &mKey; } + + PRBool KeyEquals(KeyTypePointer aKey) const { + return mKey.mPeer == aKey->mPeer && + mKey.mInstance == aKey->mInstance; + } + + static KeyTypePointer KeyToPointer(KeyType aKey) { return &aKey; } + static PLDHashNumber HashKey(KeyTypePointer aKey) { + return aKey->mPeer ^ + (NS_PTR_TO_INT32(aKey->mInstance) >> 2); + } + enum { ALLOW_MEMMOVE = PR_TRUE }; + +private: + const Key mKey; +}; + +// used elsewhere like nsAtomTable to safely represent the integral value +// of an address. +typedef PRUint64 PtrBits; + +// bit flag that defines if a PtrBits value represents a remote object +#define PTRBITS_REMOTE_BIT 0x1 + +class DConnectStub; +typedef nsDataHashtable<DConnectStubKey, DConnectStub *> DConnectStubMap; + +class ipcDConnectService : public ipcIDConnectService + , public ipcIMessageObserver + , public ipcIClientObserver +{ +public: + NS_DECL_ISUPPORTS + NS_DECL_IPCIDCONNECTSERVICE + NS_DECL_IPCIMESSAGEOBSERVER + NS_DECL_IPCICLIENTOBSERVER + + ipcDConnectService(); + + NS_HIDDEN_(nsresult) Init(); + NS_HIDDEN_(void) Shutdown(); + + NS_HIDDEN_(nsresult) GetInterfaceInfo(const nsID &iid, nsIInterfaceInfo **); + NS_HIDDEN_(nsresult) GetIIDForMethodParam(nsIInterfaceInfo *iinfo, + const nsXPTMethodInfo *methodInfo, + const nsXPTParamInfo ¶mInfo, + const nsXPTType &type, + PRUint16 methodIndex, + nsXPTCMiniVariant *dispatchParams, + PRBool isXPTCVariantArray, + nsID &result); + + NS_HIDDEN_(nsresult) SerializeInterfaceParam(ipcMessageWriter &writer, + PRUint32 peer, const nsID &iid, + nsISupports *obj, + nsVoidArray &wrappers); + NS_HIDDEN_(nsresult) DeserializeInterfaceParamBits(PtrBits bits, PRUint32 peer, + const nsID &iid, + nsISupports *&obj); + + NS_HIDDEN_(nsresult) SerializeException(ipcMessageWriter &writer, + PRUint32 peer, nsIException *xcpt, + nsVoidArray &wrappers); + NS_HIDDEN_(nsresult) DeserializeException(ipcMessageReader &reader, + PRUint32 peer, nsIException **xcpt); + + NS_HIDDEN_(void) ReleaseWrappers(nsVoidArray &wrappers, PRUint32 peer); + + NS_HIDDEN_(nsresult) CreateStub(const nsID &, PRUint32, DConAddr, DConnectStub **); +#if 0 + NS_HIDDEN_(PRBool) FindStubAndAddRef(PRUint32, const DConAddr, DConnectStub **); +#endif + // public only for DConnectStub::~DConnectStub() + NS_HIDDEN_(void) DeleteStub(DConnectStub *); + + // public only for DConnectInstance::Release() + NS_HIDDEN_(void) DeleteInstance(DConnectInstance *, PRBool locked = PR_FALSE); + // public only for DConnectStub::CallMethod() + NS_HIDDEN_(PRBool) CheckInstanceAndAddRef(DConnectInstance *, PRUint32); + + PRLock *StubLock() { return mStubLock; } + PRLock *StubQILock() { return mStubQILock; } + + static nsRefPtr <ipcDConnectService> GetInstance() { + return nsRefPtr <ipcDConnectService> (mInstance); + } + +private: + + NS_HIDDEN ~ipcDConnectService(); + + NS_HIDDEN_(nsresult) StoreInstance(DConnectInstance *); + NS_HIDDEN_(PRBool) FindInstanceAndAddRef(PRUint32, + const nsISupports *, + const nsIID *, + DConnectInstance **); + + NS_HIDDEN_(nsresult) StoreStub(DConnectStub *); + + NS_HIDDEN_(void) OnIncomingRequest(PRUint32 peer, const struct DConnectOp *op, PRUint32 opLen); + + NS_HIDDEN_(void) OnSetup(PRUint32 peer, const struct DConnectSetup *, PRUint32 opLen); + NS_HIDDEN_(void) OnRelease(PRUint32 peer, const struct DConnectRelease *); + NS_HIDDEN_(void) OnInvoke(PRUint32 peer, const struct DConnectInvoke *, PRUint32 opLen); + +#if defined(DCONNECT_MULTITHREADED) +# if defined(DCONNECT_WITH_IPRT_REQ_POOL) + static DECLCALLBACK(void) ProcessMessageOnWorkerThread(ipcDConnectService *aThis, PRUint32 aSenderID, void *aData, PRUint32 aDataLen); +# else + NS_HIDDEN_(nsresult) CreateWorker(); +# endif +#endif + +private: + nsCOMPtr<nsIInterfaceInfoManager> mIIM; + + // lock to protect access to instance sets and the disconnected flag + PRLock *mLock; + + // table of local object instances allocated on behalf of a peer + // (keys are interface pointers of real objects these instances represent) + DConnectInstanceMap mInstances; + // hashset containing the same instances as above + // (used for quick parameter validity checks) + DConnectInstanceSet mInstanceSet; + + // lock to protect access to mStubs and DConnectStub::mRefCntLevels + // (also guards every DConnectStub::Release call to provide atomicity) + PRLock *mStubLock; + + // table of remote object stubs allocated to communicate with peer's instances + DConnectStubMap mStubs; + + // this is true after IPC_Shutdown() has been called + PRBool mDisconnected; + +// member is never initialized or used, no point in wasting memory or making +// someone believe it contains anything relevant +#ifndef VBOX + // our IPC client ID + PRUint32 mSelfID; +#endif + + // global lock to protect access to protect DConnectStub::QueryInterface() + // (we cannot use mStubLock because it isn't supposed to be held long, + // like in case of an IPC call and such) + PRLock *mStubQILock; + +#if defined(DCONNECT_MULTITHREADED) +# if defined(DCONNECT_WITH_IPRT_REQ_POOL) + + /** Request pool. */ + RTREQPOOL mhReqPool; + +# else + + friend class DConnectWorker; + + // pool of worker threads to serve incoming requests + nsVoidArray mWorkers; + // queue of pending requests + ipcList<DConnectRequest> mPendingQ; + // monitor to protect mPendingQ + PRMonitor *mPendingMon; + // number of waiting workers + PRUint32 mWaitingWorkers; + // monitor used to wait on changes in mWaitingWorkers. + PRMonitor *mWaitingWorkersMon; +# endif +#endif + + // global ipcDConnectService instance for internal usage + static ipcDConnectService *mInstance; +}; + +#define IPC_DCONNECTSERVICE_CLASSNAME \ + "ipcDConnectService" +#define IPC_DCONNECTSERVICE_CONTRACTID \ + "@mozilla.org/ipc/dconnect-service;1" +#define IPC_DCONNECTSERVICE_CID \ +{ /* 63a5d9dc-4828-425a-bd50-bd10a4b26f2c */ \ + 0x63a5d9dc, \ + 0x4828, \ + 0x425a, \ + {0xbd, 0x50, 0xbd, 0x10, 0xa4, 0xb2, 0x6f, 0x2c} \ +} diff --git a/src/libs/xpcom18a4/ipc/ipcd/extensions/dconnect/test/Makefile.in b/src/libs/xpcom18a4/ipc/ipcd/extensions/dconnect/test/Makefile.in new file mode 100644 index 00000000..2d511de2 --- /dev/null +++ b/src/libs/xpcom18a4/ipc/ipcd/extensions/dconnect/test/Makefile.in @@ -0,0 +1,33 @@ +# vim:set ts=8 sw=8 noet: + +DEPTH = ../../../../.. +topsrcdir = @top_srcdir@ +srcdir = @srcdir@ +VPATH = @srcdir@ + +include $(DEPTH)/config/autoconf.mk + +MODULE = dconnect + +REQUIRES = ipcd \ + nspr \ + string \ + xpcom \ + $(NULL) + +CPPSRCS = \ + TestDConnect.cpp \ + $(NULL) + +SIMPLE_PROGRAMS = $(CPPSRCS:.cpp=$(BIN_SUFFIX)) + +include $(topsrcdir)/config/config.mk + +LIBS = \ + $(EXTRA_DSO_LIBS) \ + $(XPCOM_LIBS) \ + $(NSPR_LIBS) \ + $(NULL) + +include $(topsrcdir)/config/rules.mk + diff --git a/src/libs/xpcom18a4/ipc/ipcd/extensions/dconnect/test/TestClient.js b/src/libs/xpcom18a4/ipc/ipcd/extensions/dconnect/test/TestClient.js new file mode 100644 index 00000000..8403fcdd --- /dev/null +++ b/src/libs/xpcom18a4/ipc/ipcd/extensions/dconnect/test/TestClient.js @@ -0,0 +1,106 @@ +/* vim:set ts=2 sw=2 et cindent: */ +/* ***** 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 IBM Corporation. + * Portions created by the Initial Developer are Copyright (C) 2004 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Darin Fisher <darin@meer.net> + * + * 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 ***** */ + +/** + * This file contains code that mirrors the code in TestDConnect.cpp:DoTest + */ + +const ipcIService = Components.interfaces.ipcIService; +const ipcIDConnectService = Components.interfaces.ipcIDConnectService; +const nsIFile = Components.interfaces.nsIFile; +const nsILocalFile = Components.interfaces.nsILocalFile; +const nsIEventQueueService = Components.interfaces.nsIEventQueueService; + +// XXX use directory service for this +const TEST_PATH = "/tmp"; + +var serverID = 0; + +function findServer() +{ + var ipc = Components.classes["@mozilla.org/ipc/service;1"].getService(ipcIService); + serverID = ipc.resolveClientName("test-server"); +} + +function doTest() +{ + var dcon = Components.classes["@mozilla.org/ipc/dconnect-service;1"] + .getService(ipcIDConnectService); + + var file = dcon.createInstanceByContractID(serverID, "@mozilla.org/file/local;1", nsIFile); + + var localFile = file.QueryInterface(nsILocalFile); + + localFile.initWithPath(TEST_PATH); + + if (file.path != TEST_PATH) + { + dump("*** path test failed [path=" + file.path + "]\n"); + return; + } + + dump("file exists: " + file.exists() + "\n"); + + var clone = file.clone(); + + const node = "hello.txt"; + clone.append(node); + dump("files are equal: " + file.equals(clone) + "\n"); + + if (!clone.exists()) + clone.create(nsIFile.NORMAL_FILE_TYPE, 0600); + + clone.moveTo(null, "helloworld.txt"); + + var localObj = Components.classes["@mozilla.org/file/local;1"].createInstance(nsILocalFile); + localObj.initWithPath(TEST_PATH); + dump("file.equals(localObj) = " + file.equals(localObj) + "\n"); + dump("localObj.equals(file) = " + localObj.equals(file) + "\n"); +} + +function setupEventQ() +{ + var eqs = Components.classes["@mozilla.org/event-queue-service;1"] + .getService(nsIEventQueueService); + eqs.createMonitoredThreadEventQueue(); +} + +setupEventQ(); +findServer(); +dump("\n---------------------------------------------------\n"); +doTest(); +dump("---------------------------------------------------\n\n"); diff --git a/src/libs/xpcom18a4/ipc/ipcd/extensions/dconnect/test/TestDConnect.cpp b/src/libs/xpcom18a4/ipc/ipcd/extensions/dconnect/test/TestDConnect.cpp new file mode 100644 index 00000000..8eabe13c --- /dev/null +++ b/src/libs/xpcom18a4/ipc/ipcd/extensions/dconnect/test/TestDConnect.cpp @@ -0,0 +1,268 @@ +/* vim:set ts=2 sw=2 et cindent: */ +/* ***** 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 IBM Corporation. + * Portions created by the Initial Developer are Copyright (C) 2004 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Darin Fisher <darin@meer.net> + * + * 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 "ipcIDConnectService.h" +#include "ipcCID.h" + +#include "nsIEventQueueService.h" +#include "nsIServiceManager.h" +#include "nsIComponentRegistrar.h" + +#include "nsXPCOMCID.h" +#include "nsILocalFile.h" + +#include "nsString.h" +#include "prmem.h" + +#if defined( XP_WIN ) || defined( XP_OS2 ) +#define TEST_PATH "c:" +#else +#define TEST_PATH "/tmp" +#endif + +#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 ipcIService *gIpcServ = nsnull; + +static nsresult DoTest() +{ + nsresult rv; + + nsCOMPtr<ipcIDConnectService> dcon = do_GetService("@mozilla.org/ipc/dconnect-service;1", &rv); + RETURN_IF_FAILED(rv, "getting dconnect service"); + + PRUint32 remoteClientID = 1; + + nsCOMPtr<nsIFile> file; + rv = dcon->CreateInstanceByContractID(remoteClientID, + NS_LOCAL_FILE_CONTRACTID, + NS_GET_IID(nsIFile), + getter_AddRefs(file)); + NS_ENSURE_SUCCESS(rv, rv); + + nsCOMPtr<nsISupports> sup = do_QueryInterface(file, &rv); + NS_ENSURE_SUCCESS(rv, rv); + + printf("*** calling QueryInterface\n"); + nsCOMPtr<nsILocalFile> localFile = do_QueryInterface(file, &rv); + NS_ENSURE_SUCCESS(rv, rv); + + nsAutoString path; + path.AssignLiteral(TEST_PATH); + + printf("*** calling InitWithNativePath\n"); + rv = localFile->InitWithPath(path); + NS_ENSURE_SUCCESS(rv, rv); + + nsAutoString buf; + rv = file->GetPath(buf); + NS_ENSURE_SUCCESS(rv, rv); + + if (!buf.Equals(path)) + { + NS_ConvertUTF16toUTF8 temp(buf); + printf("*** GetPath erroneously returned [%s]\n", temp.get()); + return NS_ERROR_FAILURE; + } + + PRBool exists; + rv = file->Exists(&exists); + if (NS_FAILED(rv)) + { + printf("*** Exists test failed [rv=%x]\n", rv); + return NS_ERROR_FAILURE; + } + printf("File exists? [%d]\n", exists); + + nsCOMPtr<nsIFile> clone; + rv = file->Clone(getter_AddRefs(clone)); + if (NS_FAILED(rv)) + { + printf("*** Clone test failed [rv=%x]\n", rv); + return NS_ERROR_FAILURE; + } + + nsAutoString node; + node.AssignLiteral("hello.txt"); + + rv = clone->Append(node); + if (NS_FAILED(rv)) + { + printf("*** Append test failed [rv=%x]\n", rv); + return NS_ERROR_FAILURE; + } + + PRBool match; + rv = file->Equals(clone, &match); + if (NS_FAILED(rv)) + { + printf("*** Equals test failed [rv=%x]\n", rv); + return NS_ERROR_FAILURE; + } + printf("Files are equals? [%d]\n", match); + + // now test passing null for interface pointer + + rv = clone->Exists(&exists); + if (NS_FAILED(rv)) + { + printf("*** Exists test failed [rv=%x]\n", rv); + return NS_ERROR_FAILURE; + } + if (!exists) + { + rv = clone->Create(nsIFile::NORMAL_FILE_TYPE, 0600); + if (NS_FAILED(rv)) + { + printf("*** Create test failed [rv=%x]\n", rv); + return NS_ERROR_FAILURE; + } + } + + rv = clone->MoveTo(nsnull, NS_LITERAL_STRING("helloworld.txt")); + if (NS_FAILED(rv)) + { + printf("*** MoveTo test failed [rv=%x]\n", rv); + return NS_ERROR_FAILURE; + } + + // now test passing local objects to a remote object + + nsCOMPtr<nsILocalFile> myLocalFile; + rv = NS_NewLocalFile(path, PR_TRUE, getter_AddRefs(myLocalFile)); + if (NS_FAILED(rv)) + { + printf("*** NS_NewLocalFile failed [rv=%x]\n", rv); + return NS_ERROR_FAILURE; + } + + rv = file->Equals(myLocalFile, &match); + if (NS_FAILED(rv)) + { + printf("*** second Equals test failed [rv=%x]\n", rv); + return NS_ERROR_FAILURE; + } + printf("Files are equals? [%d]\n", match); + + printf("*** DoTest completed successfully :-)\n"); + return NS_OK; +} + +int main(int argc, char **argv) +{ + nsresult rv; + + PRBool serverMode = PR_FALSE; + if (argc > 1) + { + if (strcmp(argv[1], "-server") == 0) + { + serverMode = PR_TRUE; + } + else + { + printf("usage: %s [-server]\n", argv[0]); + return -1; + } + } + + { + 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(IPC_SERVICE_CONTRACTID, &rv)); + RETURN_IF_FAILED(rv, "do_GetService(ipcServ)"); + NS_ADDREF(gIpcServ = ipcServ); + + if (!serverMode) + { + rv = DoTest(); + RETURN_IF_FAILED(rv, "DoTest()"); + } + else + { + gIpcServ->AddName("DConnectServer"); + } + + PLEvent *ev; + while (gKeepRunning) + { + gEventQ->WaitForEvent(&ev); + gEventQ->HandleEvent(ev); + } + + NS_RELEASE(gIpcServ); + + printf("*** processing remaining events\n"); + + // process any remaining events + 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"); +} diff --git a/src/libs/xpcom18a4/ipc/ipcd/extensions/dconnect/test/TestServer.js b/src/libs/xpcom18a4/ipc/ipcd/extensions/dconnect/test/TestServer.js new file mode 100644 index 00000000..a72e133b --- /dev/null +++ b/src/libs/xpcom18a4/ipc/ipcd/extensions/dconnect/test/TestServer.js @@ -0,0 +1,66 @@ +/* vim:set ts=2 sw=2 et cindent: */ +/* ***** 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 IBM Corporation. + * Portions created by the Initial Developer are Copyright (C) 2004 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Darin Fisher <darin@meer.net> + * + * 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 ***** */ + +/** + * This file contains code for a "server" that does nothing more + * than set a name for itself on the IPC system so that the client + * can connect and control this process. + */ + +const ipcIService = Components.interfaces.ipcIService; +const nsIEventQueueService = Components.interfaces.nsIEventQueueService; +const nsIEventQueue = Components.interfaces.nsIEventQueue; + +function registerServer() +{ + var ipc = Components.classes["@mozilla.org/ipc/service;1"].getService(ipcIService); + ipc.addName("test-server"); +} + +function runEventQ() +{ + var eqs = Components.classes["@mozilla.org/event-queue-service;1"] + .getService(nsIEventQueueService); + eqs.createMonitoredThreadEventQueue(); + var queue = eqs.getSpecialEventQueue(eqs.CURRENT_THREAD_EVENT_QUEUE); + + // this never returns + queue.eventLoop(); +} + +registerServer(); +runEventQ(); diff --git a/src/libs/xpcom18a4/ipc/ipcd/extensions/lock/.cvsignore b/src/libs/xpcom18a4/ipc/ipcd/extensions/lock/.cvsignore new file mode 100644 index 00000000..f3c7a7c5 --- /dev/null +++ b/src/libs/xpcom18a4/ipc/ipcd/extensions/lock/.cvsignore @@ -0,0 +1 @@ +Makefile diff --git a/src/libs/xpcom18a4/ipc/ipcd/extensions/lock/Makefile.in b/src/libs/xpcom18a4/ipc/ipcd/extensions/lock/Makefile.in new file mode 100644 index 00000000..4b94a31f --- /dev/null +++ b/src/libs/xpcom18a4/ipc/ipcd/extensions/lock/Makefile.in @@ -0,0 +1,52 @@ +# vim: noexpandtab ts=4 sw=4 +# ***** 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 + +DIRS = public src + +ifdef ENABLE_TESTS +DIRS += test +endif + +include $(topsrcdir)/config/rules.mk diff --git a/src/libs/xpcom18a4/ipc/ipcd/extensions/lock/public/.cvsignore b/src/libs/xpcom18a4/ipc/ipcd/extensions/lock/public/.cvsignore new file mode 100644 index 00000000..f3c7a7c5 --- /dev/null +++ b/src/libs/xpcom18a4/ipc/ipcd/extensions/lock/public/.cvsignore @@ -0,0 +1 @@ +Makefile diff --git a/src/libs/xpcom18a4/ipc/ipcd/extensions/lock/public/Makefile.in b/src/libs/xpcom18a4/ipc/ipcd/extensions/lock/public/Makefile.in new file mode 100644 index 00000000..9a08abdb --- /dev/null +++ b/src/libs/xpcom18a4/ipc/ipcd/extensions/lock/public/Makefile.in @@ -0,0 +1,58 @@ +# vim: noexpandtab ts=4 sw=4 +# ***** 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 = ipcd +XPIDL_MODULE = ipcd_lock + +EXPORTS = \ + ipcLockCID.h \ + $(NULL) + +XPIDLSRCS = \ + ipcILockService.idl \ + $(NULL) + +include $(topsrcdir)/config/rules.mk + diff --git a/src/libs/xpcom18a4/ipc/ipcd/extensions/lock/public/ipcILockService.idl b/src/libs/xpcom18a4/ipc/ipcd/extensions/lock/public/ipcILockService.idl new file mode 100644 index 00000000..1cbf8651 --- /dev/null +++ b/src/libs/xpcom18a4/ipc/ipcd/extensions/lock/public/ipcILockService.idl @@ -0,0 +1,65 @@ +/* ***** 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" + +/** + * This service provides named interprocess locking. + */ +[scriptable, uuid(9f6dbe15-d851-4b00-912a-5ac0be88a409)] +interface ipcILockService : nsISupports +{ + /** + * Call this method to acquire a named interprocess lock. + * + * @param aLockName + * specifies the name of the lock + * @param aWaitIfBusy + * wait for the lock to become available; otherwise, fail if lock + * is already held by some other process. + */ + void acquireLock(in string aLockName, + in boolean aWaitIfBusy); + + /** + * Call this method to release a named lock. + * + * @param aLockName + * specifies the name of the lock + */ + void releaseLock(in string aLockName); +}; diff --git a/src/libs/xpcom18a4/ipc/ipcd/extensions/lock/public/ipcLockCID.h b/src/libs/xpcom18a4/ipc/ipcd/extensions/lock/public/ipcLockCID.h new file mode 100644 index 00000000..ba756d9b --- /dev/null +++ b/src/libs/xpcom18a4/ipc/ipcd/extensions/lock/public/ipcLockCID.h @@ -0,0 +1,53 @@ +/* ***** 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 ***** */ + +#ifndef ipcLockCID_h__ +#define ipcLockCID_h__ + +#define IPC_LOCKSERVICE_CLASSNAME \ + "ipcLockService" +#define IPC_LOCKSERVICE_CONTRACTID \ + "@mozilla.org/ipc/lock-service;1" +#define IPC_LOCKSERVICE_CID \ +{ /* d9e56bf8-e32e-4b6d-87f1-06d73b0ce7ca */ \ + 0xd9e56bf8, \ + 0xe32e, \ + 0x4b6d, \ + {0x87, 0xf1, 0x06, 0xd7, 0x3b, 0x0c, 0xe7, 0xca} \ +} + +#endif // !ipcLockCID_h__ diff --git a/src/libs/xpcom18a4/ipc/ipcd/extensions/lock/src/.cvsignore b/src/libs/xpcom18a4/ipc/ipcd/extensions/lock/src/.cvsignore new file mode 100644 index 00000000..f3c7a7c5 --- /dev/null +++ b/src/libs/xpcom18a4/ipc/ipcd/extensions/lock/src/.cvsignore @@ -0,0 +1 @@ +Makefile diff --git a/src/libs/xpcom18a4/ipc/ipcd/extensions/lock/src/Makefile.in b/src/libs/xpcom18a4/ipc/ipcd/extensions/lock/src/Makefile.in new file mode 100644 index 00000000..26d943a8 --- /dev/null +++ b/src/libs/xpcom18a4/ipc/ipcd/extensions/lock/src/Makefile.in @@ -0,0 +1,69 @@ +# vim: noexpandtab ts=4 sw=4 +# ***** 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 + +DIRS = module + +MODULE = ipcd +LIBRARY_NAME = ipcdlock_s +FORCE_STATIC_LIB = 1 +MODULE_NAME = ipcd + +FORCE_USE_PIC = 1 + +REQUIRES = \ + xpcom \ + string \ + $(NULL) + +CPPSRCS = \ + ipcLockProtocol.cpp \ + ipcLockService.cpp \ + $(NULL) + +LOCAL_INCLUDES = \ + -I$(srcdir)/../../../shared/src \ + $(NULL) + +include $(topsrcdir)/config/rules.mk diff --git a/src/libs/xpcom18a4/ipc/ipcd/extensions/lock/src/ipcLockProtocol.cpp b/src/libs/xpcom18a4/ipc/ipcd/extensions/lock/src/ipcLockProtocol.cpp new file mode 100644 index 00000000..5a7e7755 --- /dev/null +++ b/src/libs/xpcom18a4/ipc/ipcd/extensions/lock/src/ipcLockProtocol.cpp @@ -0,0 +1,87 @@ +/* ***** 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 <stdlib.h> +#include <string.h> +#include "prlog.h" +#include "ipcLockProtocol.h" + +//----------------------------------------------------------------------------- + +static inline PRUint8 get_opcode(const PRUint8 *buf) +{ + return (buf[0] & 0x0f); +} + +static inline PRUint8 get_flags(const PRUint8 *buf) +{ + return (buf[0] & 0xf0) >> 4; +} + +static inline const char *get_key(const PRUint8 *buf) +{ + return ((const char *) buf) + 1; +} + +//----------------------------------------------------------------------------- + +PRUint8 * +IPC_FlattenLockMsg(const ipcLockMsg *msg, PRUint32 *bufLen) +{ + PRUint32 len = 1 // header byte + + strlen(msg->key) // key + + 1; // null terminator + + PRUint8 *buf = (PRUint8 *) ::operator new(len); + if (!buf) + return NULL; + + buf[0] = (msg->opcode | (msg->flags << 4)); + + memcpy(&buf[1], msg->key, len - 1); + *bufLen = len; + return buf; +} + +void +IPC_UnflattenLockMsg(const PRUint8 *buf, PRUint32 bufLen, ipcLockMsg *msg) +{ + PR_ASSERT(bufLen > 2); // malformed buffer otherwise + msg->opcode = get_opcode(buf); + msg->flags = get_flags(buf); + msg->key = get_key(buf); +} diff --git a/src/libs/xpcom18a4/ipc/ipcd/extensions/lock/src/ipcLockProtocol.h b/src/libs/xpcom18a4/ipc/ipcd/extensions/lock/src/ipcLockProtocol.h new file mode 100644 index 00000000..b74b5cde --- /dev/null +++ b/src/libs/xpcom18a4/ipc/ipcd/extensions/lock/src/ipcLockProtocol.h @@ -0,0 +1,98 @@ +/* ***** 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 ***** */ + +#ifndef ipcLockProtocol_h__ +#define ipcLockProtocol_h__ + +#include "prtypes.h" + +// +// ipc lock message format: +// +// +----------------------------------+ +// | opcode : 4 bits | +// +----------------------------------+ +// | flags : 4 bits | +// +----------------------------------+ +// | key : null terminated string | +// +----------------------------------+ +// + +// lock opcodes +#define IPC_LOCK_OP_ACQUIRE 1 +#define IPC_LOCK_OP_RELEASE 2 +#define IPC_LOCK_OP_STATUS_ACQUIRED 3 +#define IPC_LOCK_OP_STATUS_FAILED 4 +#define IPC_LOCK_OP_STATUS_BUSY 5 + +// lock flags +#define IPC_LOCK_FL_NONBLOCKING 1 + +// data structure for representing lock request message +struct ipcLockMsg +{ + PRUint8 opcode; + PRUint8 flags; + const char * key; +}; + +// +// flatten a lock message +// +// returns a malloc'd buffer containing the flattened message. on return, +// bufLen contains the length of the flattened message. +// +PRUint8 *IPC_FlattenLockMsg(const ipcLockMsg *msg, PRUint32 *bufLen); + +// +// unflatten a lock message. upon return, msg->key points into buf, so +// buf must not be deallocated until after msg is no longer needed. +// +void IPC_UnflattenLockMsg(const PRUint8 *buf, PRUint32 bufLen, ipcLockMsg *msg); + +// +// TargetID for message passing +// +#define IPC_LOCK_TARGETID \ +{ /* 703ada8a-2d38-4d5d-9d39-03d1ccceb567 */ \ + 0x703ada8a, \ + 0x2d38, \ + 0x4d5d, \ + {0x9d, 0x39, 0x03, 0xd1, 0xcc, 0xce, 0xb5, 0x67} \ +} + +#endif // !ipcLockProtocol_h__ diff --git a/src/libs/xpcom18a4/ipc/ipcd/extensions/lock/src/ipcLockService.cpp b/src/libs/xpcom18a4/ipc/ipcd/extensions/lock/src/ipcLockService.cpp new file mode 100644 index 00000000..771aa787 --- /dev/null +++ b/src/libs/xpcom18a4/ipc/ipcd/extensions/lock/src/ipcLockService.cpp @@ -0,0 +1,168 @@ +/* vim:set ts=4 sw=4 sts=4 et cindent: */ +/* ***** 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 <stdlib.h> +#include "nsDependentString.h" +#include "nsHashKeys.h" +#include "nsAutoPtr.h" +#include "ipcLockService.h" +#include "ipcLockProtocol.h" +#include "ipcLog.h" +#include "prthread.h" + +static const nsID kLockTargetID = IPC_LOCK_TARGETID; + +//----------------------------------------------------------------------------- + +struct ipcPendingLock +{ + const char *name; + nsresult status; + PRBool complete; +}; + +//----------------------------------------------------------------------------- + +nsresult +ipcLockService::Init() +{ + if (PR_NewThreadPrivateIndex(&mTPIndex, nsnull) != PR_SUCCESS) + return NS_ERROR_OUT_OF_MEMORY; + + // Configure OnMessageAvailable to be called on the IPC thread. This is + // done to allow us to proxy OnAcquireLockComplete events to the right + // thread immediately even if the main thread is blocked waiting to acquire + // some other lock synchronously. + + return IPC_DefineTarget(kLockTargetID, this, PR_FALSE); +} + +NS_IMPL_THREADSAFE_ISUPPORTS2(ipcLockService, ipcILockService, ipcIMessageObserver) + +NS_IMETHODIMP +ipcLockService::AcquireLock(const char *lockName, PRBool waitIfBusy) +{ + LOG(("ipcLockService::AcquireLock [lock=%s wait=%u]\n", lockName, waitIfBusy)); + + ipcLockMsg msg; + msg.opcode = IPC_LOCK_OP_ACQUIRE; + msg.flags = (waitIfBusy ? 0 : IPC_LOCK_FL_NONBLOCKING); + msg.key = lockName; + + PRUint32 bufLen; + nsAutoPtr<PRUint8> buf( IPC_FlattenLockMsg(&msg, &bufLen) ); + if (!buf) + return NS_ERROR_OUT_OF_MEMORY; + + ipcPendingLock pendingLock; + pendingLock.name = lockName; + pendingLock.status = 0xDEADBEEF; // something bogus + pendingLock.complete = PR_FALSE; + if (PR_SetThreadPrivate(mTPIndex, &pendingLock) != PR_SUCCESS) + return NS_ERROR_UNEXPECTED; + + // prevent our OnMessageAvailable from being called until we explicitly ask + // for it to be called via IPC_WaitMessage. + IPC_DISABLE_MESSAGE_OBSERVER_FOR_SCOPE(kLockTargetID); + + nsresult rv = IPC_SendMessage(0, kLockTargetID, buf, bufLen); + if (NS_SUCCEEDED(rv)) { + do { + // block the calling thread until we get a response from the daemon + rv = IPC_WaitMessage(0, kLockTargetID, this, nsnull, PR_INTERVAL_NO_TIMEOUT); + } + while (NS_SUCCEEDED(rv) && !pendingLock.complete); + + if (NS_SUCCEEDED(rv)) + rv = pendingLock.status; + } + + // we could clear the TPD, but that isn't really necessary. + + return rv; +} + +NS_IMETHODIMP +ipcLockService::ReleaseLock(const char *lockName) +{ + LOG(("ipcLockService::ReleaseLock [lock=%s]\n", lockName)); + + ipcLockMsg msg; + msg.opcode = IPC_LOCK_OP_RELEASE; + msg.flags = 0; + msg.key = lockName; + + PRUint32 bufLen; + PRUint8 *buf = IPC_FlattenLockMsg(&msg, &bufLen); + if (!buf) + return NS_ERROR_OUT_OF_MEMORY; + + nsresult rv = IPC_SendMessage(0, kLockTargetID, buf, bufLen); + delete buf; + + if (NS_FAILED(rv)) + return rv; + + return NS_OK; +} + +// called on the same thread that called IPC_WaitMessage +NS_IMETHODIMP +ipcLockService::OnMessageAvailable(PRUint32 unused, const nsID &target, + const PRUint8 *data, PRUint32 dataLen) +{ + ipcLockMsg msg; + IPC_UnflattenLockMsg(data, dataLen, &msg); + + LOG(("ipcLockService::OnMessageAvailable [lock=%s opcode=%u]\n", msg.key, msg.opcode)); + + ipcPendingLock *pendingLock = (ipcPendingLock *) PR_GetThreadPrivate(mTPIndex); + if (strcmp(pendingLock->name, msg.key) == 0) { + pendingLock->complete = PR_TRUE; + if (msg.opcode == IPC_LOCK_OP_STATUS_ACQUIRED) + pendingLock->status = NS_OK; + else + pendingLock->status = NS_ERROR_FAILURE; + return NS_OK; + } + + LOG(("message does not match; waiting for another...\n")); + + // else, we got a message that another thread is waiting to receive. + return IPC_WAIT_NEXT_MESSAGE; +} diff --git a/src/libs/xpcom18a4/ipc/ipcd/extensions/lock/src/ipcLockService.h b/src/libs/xpcom18a4/ipc/ipcd/extensions/lock/src/ipcLockService.h new file mode 100644 index 00000000..6014172b --- /dev/null +++ b/src/libs/xpcom18a4/ipc/ipcd/extensions/lock/src/ipcLockService.h @@ -0,0 +1,63 @@ +/* vim:set ts=4 sw=4 sts=4 et cindent: */ +/* ***** 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 ***** */ + +#ifndef ipcLockService_h__ +#define ipcLockService_h__ + +#include "ipcILockService.h" +#include "ipcdclient.h" + +//----------------------------------------------------------------------------- + +class ipcLockService : public ipcILockService + , public ipcIMessageObserver +{ +public: + NS_DECL_ISUPPORTS + NS_DECL_IPCILOCKSERVICE + NS_DECL_IPCIMESSAGEOBSERVER + + NS_HIDDEN_(nsresult) Init(); + +private: + PRUintn mTPIndex; +}; + +//----------------------------------------------------------------------------- + +#endif // !ipcLockService_h__ diff --git a/src/libs/xpcom18a4/ipc/ipcd/extensions/lock/src/module/.cvsignore b/src/libs/xpcom18a4/ipc/ipcd/extensions/lock/src/module/.cvsignore new file mode 100644 index 00000000..f3c7a7c5 --- /dev/null +++ b/src/libs/xpcom18a4/ipc/ipcd/extensions/lock/src/module/.cvsignore @@ -0,0 +1 @@ +Makefile diff --git a/src/libs/xpcom18a4/ipc/ipcd/extensions/lock/src/module/Makefile.in b/src/libs/xpcom18a4/ipc/ipcd/extensions/lock/src/module/Makefile.in new file mode 100644 index 00000000..b35db1bc --- /dev/null +++ b/src/libs/xpcom18a4/ipc/ipcd/extensions/lock/src/module/Makefile.in @@ -0,0 +1,92 @@ +# vim: noexpandtab ts=4 sw=4 +# ***** 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 = ipcd +LIBRARY_NAME = lockmodule +ifeq ($(OS_ARCH), OS2) +SHORT_LIBNAME = lockmod +endif +MODULE_NAME = ipcd + +FORCE_SHARED_LIB = 1 +NO_DIST_INSTALL = 1 +NO_INSTALL = 1 + +ifeq ($(OS_ARCH),Darwin) +NO_COMPONENT_LINK_MAP = 1 +MOZ_COMPONENTS_VERSION_SCRIPT_LDFLAGS = +endif + +# required for #include "nsID.h" +REQUIRES = \ + xpcom \ + $(NULL) + +CPPSRCS = ipcLockModule.cpp + +LOCAL_INCLUDES = \ + -I$(srcdir)/.. \ + $(NULL) + +EXTRA_OBJS = ../ipcLockProtocol.$(OBJ_SUFFIX) + +EXTRA_DSO_LDOPTS = \ + $(LIBS_DIR) \ + $(NSPR_LIBS) \ + $(EXTRA_DSO_LIBS) \ + $(EXTRA_OBJS) \ + $(NULL) + +include $(topsrcdir)/config/rules.mk + +_IPC_FILES = \ + $(DLL_PREFIX)$(LIBRARY_NAME)$(DLL_SUFFIX) \ + $(NULL) + +libs:: $(_IPC_FILES) + $(INSTALL) $^ $(DIST)/bin/ipc/modules + +install:: $(_IPC_FILES) + $(SYSINSTALL) $(IFLAGS1) $^ $(DESTDIR)$(mozappdir)/ipc/modules diff --git a/src/libs/xpcom18a4/ipc/ipcd/extensions/lock/src/module/ipcLockModule.cpp b/src/libs/xpcom18a4/ipc/ipcd/extensions/lock/src/module/ipcLockModule.cpp new file mode 100644 index 00000000..151c0f89 --- /dev/null +++ b/src/libs/xpcom18a4/ipc/ipcd/extensions/lock/src/module/ipcLockModule.cpp @@ -0,0 +1,337 @@ +/* ***** 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 <stdlib.h> +#include <stdio.h> +#include "ipcModuleUtil.h" +#include "ipcLockProtocol.h" +#include "plhash.h" +#include "plstr.h" + +#ifdef DEBUG +#define LOG(args) printf args +#else +#define LOG(args) +#endif + +static const nsID kLockTargetID = IPC_LOCK_TARGETID; + +static void +ipcLockModule_Send(PRUint32 cid, const char *key, PRUint8 opcode) +{ + ipcLockMsg msg = { opcode, 0, key }; + PRUint32 bufLen; + PRUint8 *buf = IPC_FlattenLockMsg(&msg, &bufLen); + if (!buf) + return; + IPC_SendMsg(cid, kLockTargetID, buf, bufLen); + free(buf); +} + +//----------------------------------------------------------------------------- + +// +// gLockTable stores mapping from lock name to ipcLockContext +// +static PLHashTable *gLockTable = NULL; + +//----------------------------------------------------------------------------- + +struct ipcLockContext +{ + PRUint32 mOwnerID; // client ID of this lock's owner + struct ipcLockContext *mNextPending; // pointer to client next in line to + // acquire this lock. + + ipcLockContext(PRUint32 ownerID) + : mOwnerID(ownerID) + , mNextPending(NULL) {} +}; + +//----------------------------------------------------------------------------- + +PR_STATIC_CALLBACK(void *) +ipcLockModule_AllocTable(void *pool, PRSize size) +{ + return malloc(size); +} + +PR_STATIC_CALLBACK(void) +ipcLockModule_FreeTable(void *pool, void *item) +{ + free(item); +} + +PR_STATIC_CALLBACK(PLHashEntry *) +ipcLockModule_AllocEntry(void *pool, const void *key) +{ + return (PLHashEntry *) malloc(sizeof(PLHashEntry)); +} + +PR_STATIC_CALLBACK(void) +ipcLockModule_FreeEntry(void *pool, PLHashEntry *he, PRUintn flag) +{ + PL_strfree((char *) he->key); + free(he); +} + +static const PLHashAllocOps ipcLockModule_AllocOps = { + ipcLockModule_AllocTable, + ipcLockModule_FreeTable, + ipcLockModule_AllocEntry, + ipcLockModule_FreeEntry +}; + +//----------------------------------------------------------------------------- + +static void +ipcLockModule_AcquireLock(PRUint32 cid, PRUint8 flags, const char *key) +{ + LOG(("$$$ acquiring lock [key=%s]\n", key)); + + if (!gLockTable) + return; + + ipcLockContext *ctx; + + ctx = (ipcLockContext *) PL_HashTableLookup(gLockTable, key); + if (ctx) { + // + // lock is already acquired, add this client to the queue. make + // sure this client doesn't already own the lock or live on the queue. + // + while (ctx->mOwnerID != cid && ctx->mNextPending) + ctx = ctx->mNextPending; + if (ctx->mOwnerID != cid) { + // + // if nonblocking, then send busy status message. otherwise, + // proceed to add this client to the pending queue. + // + if (flags & IPC_LOCK_FL_NONBLOCKING) + ipcLockModule_Send(cid, key, IPC_LOCK_OP_STATUS_BUSY); + else + ctx->mNextPending = new ipcLockContext(cid); + } + } + else { + // + // ok, add this lock to the table, and notify client that it now owns + // the lock! + // + ctx = new ipcLockContext(cid); + if (!ctx) + return; + + PL_HashTableAdd(gLockTable, PL_strdup(key), ctx); + + ipcLockModule_Send(cid, key, IPC_LOCK_OP_STATUS_ACQUIRED); + } +} + +static PRBool +ipcLockModule_ReleaseLockHelper(PRUint32 cid, const char *key, ipcLockContext *ctx) +{ + LOG(("$$$ releasing lock [key=%s]\n", key)); + + PRBool removeEntry = PR_FALSE; + + // + // lock is already acquired _or_ maybe client is on the pending list. + // + if (ctx->mOwnerID == cid) { + if (ctx->mNextPending) { + // + // remove this element from the list. since this is the + // first element in the list, instead of removing it we + // shift the data from the next context into this one and + // delete the next context. + // + ipcLockContext *next = ctx->mNextPending; + ctx->mOwnerID = next->mOwnerID; + ctx->mNextPending = next->mNextPending; + delete next; + // + // notify client that it now owns the lock + // + ipcLockModule_Send(ctx->mOwnerID, key, IPC_LOCK_OP_STATUS_ACQUIRED); + } + else { + delete ctx; + removeEntry = PR_TRUE; + } + } + else { + ipcLockContext *prev; + for (;;) { + prev = ctx; + ctx = ctx->mNextPending; + if (!ctx) + break; + if (ctx->mOwnerID == cid) { + // remove ctx from list + prev->mNextPending = ctx->mNextPending; + delete ctx; + break; + } + } + } + + return removeEntry; +} + +static void +ipcLockModule_ReleaseLock(PRUint32 cid, const char *key) +{ + if (!gLockTable) + return; + + ipcLockContext *ctx; + + ctx = (ipcLockContext *) PL_HashTableLookup(gLockTable, key); + if (ctx && ipcLockModule_ReleaseLockHelper(cid, key, ctx)) + PL_HashTableRemove(gLockTable, key); +} + +PR_STATIC_CALLBACK(PRIntn) +ipcLockModule_ReleaseByCID(PLHashEntry *he, PRIntn i, void *arg) +{ + PRUint32 cid = *(PRUint32 *) arg; + + ipcLockContext *ctx = (ipcLockContext *) he->value; + if (ctx->mOwnerID != cid) + return HT_ENUMERATE_NEXT; + + LOG(("$$$ ipcLockModule_ReleaseByCID [cid=%u key=%s he=%p]\n", + cid, (char*)he->key, (void*)he)); + + if (ipcLockModule_ReleaseLockHelper(cid, (const char *) he->key, ctx)) + return HT_ENUMERATE_REMOVE; + + return HT_ENUMERATE_NEXT; +} + +//----------------------------------------------------------------------------- + +static void +ipcLockModule_Init() +{ + LOG(("$$$ ipcLockModule_Init\n")); + + gLockTable = PL_NewHashTable(32, + PL_HashString, + PL_CompareStrings, + PL_CompareValues, + &ipcLockModule_AllocOps, + NULL); +} + +static void +ipcLockModule_Shutdown() +{ + LOG(("$$$ ipcLockModule_Shutdown\n")); + + if (gLockTable) { + // XXX walk table destroying all ipcLockContext objects + + PL_HashTableDestroy(gLockTable); + gLockTable = NULL; + } +} + +static void +ipcLockModule_HandleMsg(ipcClientHandle client, + const nsID &target, + const void *data, + PRUint32 dataLen) +{ + PRUint32 cid = IPC_GetClientID(client); + + LOG(("$$$ ipcLockModule_HandleMsg [cid=%u]\n", cid)); + + ipcLockMsg msg; + IPC_UnflattenLockMsg((const PRUint8 *) data, dataLen, &msg); + + switch (msg.opcode) { + case IPC_LOCK_OP_ACQUIRE: + ipcLockModule_AcquireLock(cid, msg.flags, msg.key); + break; + case IPC_LOCK_OP_RELEASE: + ipcLockModule_ReleaseLock(cid, msg.key); + break; + default: + PR_NOT_REACHED("invalid opcode"); + } +} + +static void +ipcLockModule_ClientUp(ipcClientHandle client) +{ + LOG(("$$$ ipcLockModule_ClientUp [%u]\n", IPC_GetClientID(client))); +} + +static void +ipcLockModule_ClientDown(ipcClientHandle client) +{ + PRUint32 cid = IPC_GetClientID(client); + + LOG(("$$$ ipcLockModule_ClientDown [%u]\n", cid)); + + // + // enumerate lock table, release any locks held by this client. + // + + PL_HashTableEnumerateEntries(gLockTable, ipcLockModule_ReleaseByCID, &cid); +} + +//----------------------------------------------------------------------------- + +static ipcModuleMethods gLockMethods = +{ + IPC_MODULE_METHODS_VERSION, + ipcLockModule_Init, + ipcLockModule_Shutdown, + ipcLockModule_HandleMsg, + ipcLockModule_ClientUp, + ipcLockModule_ClientDown +}; + +static ipcModuleEntry gLockModuleEntry[] = +{ + { IPC_LOCK_TARGETID, &gLockMethods } +}; + +IPC_IMPL_GETMODULES(ipcLockModule, gLockModuleEntry) diff --git a/src/libs/xpcom18a4/ipc/ipcd/extensions/lock/test/Makefile.in b/src/libs/xpcom18a4/ipc/ipcd/extensions/lock/test/Makefile.in new file mode 100644 index 00000000..bcf9299a --- /dev/null +++ b/src/libs/xpcom18a4/ipc/ipcd/extensions/lock/test/Makefile.in @@ -0,0 +1,67 @@ +# vim:set ts=8 sw=8 noet: +# ***** 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 IBM Corporation. +# Portions created by the Initial Developer are Copyright (C) 2004 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# Darin Fisher <darin@meer.net> +# +# 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 = dconnect + +REQUIRES = ipcd \ + nspr \ + string \ + xpcom \ + $(NULL) + +CPPSRCS = \ + TestIPCLocks.cpp \ + $(NULL) + +SIMPLE_PROGRAMS = $(CPPSRCS:.cpp=$(BIN_SUFFIX)) + +include $(topsrcdir)/config/config.mk + +LIBS = \ + $(EXTRA_DSO_LIBS) \ + $(XPCOM_LIBS) \ + $(NSPR_LIBS) \ + $(NULL) + +include $(topsrcdir)/config/rules.mk diff --git a/src/libs/xpcom18a4/ipc/ipcd/extensions/lock/test/TestIPCLocks.cpp b/src/libs/xpcom18a4/ipc/ipcd/extensions/lock/test/TestIPCLocks.cpp new file mode 100644 index 00000000..f35e8434 --- /dev/null +++ b/src/libs/xpcom18a4/ipc/ipcd/extensions/lock/test/TestIPCLocks.cpp @@ -0,0 +1,244 @@ +/* vim:set ts=2 sw=2 sts=2 et cindent: */ +/* ***** 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 IBM Corporation. + * Portions created by the Initial Developer are Copyright (C) 2004 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Darin Fisher <darin@meer.net> + * + * 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 ***** */ + +// This test program spawns N copies of itself, and each copy spawns M threads. +// Each thread acquires and releases a named, interprocess lock. +// Randomized delays are injected at various points to exercise the system, and +// help expose any race conditions that may exist. +// +// Usage: TestIPCLocks [-N] + +#include <stdlib.h> +#include <stdio.h> +#include "ipcILockService.h" +#include "ipcLockCID.h" +#include "nsIServiceManagerUtils.h" +#include "nsIEventQueueService.h" +#include "nsCOMPtr.h" +#include "nsXPCOM.h" +#include "prproces.h" +#include "prprf.h" + +#if defined(XP_UNIX) || defined(XP_OS2) || defined(XP_BEOS) +#include <unistd.h> +static unsigned GetPID() +{ + return (unsigned) getpid(); +} +#elif defined(XP_WIN) +#include <windows.h> +static unsigned GetPID() +{ + return (unsigned) GetCurrentProcessId(); +} +#else +static unsigned int GetPID() +{ + return 0; // implement me! +} +#endif + +static void LOG(const char *fmt, ... ) +{ + va_list ap; + va_start(ap, fmt); + PRUint32 nb = 0; + char buf[512]; + + nb = PR_snprintf(buf, sizeof(buf), "[%u:%p] ", GetPID(), PR_GetCurrentThread()); + + PR_vsnprintf(buf + nb, sizeof(buf) - nb, fmt, ap); + buf[sizeof(buf) - 1] = '\0'; + + fwrite(buf, strlen(buf), 1, stdout); + fflush(stdout); + + va_end(ap); +} + +static void RandomSleep(PRUint32 fromMS, PRUint32 toMS) +{ + PRUint32 ms = fromMS + (PRUint32) ((toMS - fromMS) * ((double) rand() / RAND_MAX)); + //LOG("putting thread to sleep for %u ms\n", ms); + PR_Sleep(PR_MillisecondsToInterval(ms)); +} + +static ipcILockService *gLockService; + +PR_STATIC_CALLBACK(void) TestThread(void *arg) +{ + const char *lockName = (const char *) arg; + + LOG("entering TestThread [lock=%s]\n", lockName); + + nsresult rv; + + RandomSleep(1000, 1100); + + //LOG("done sleeping\n"); + + rv = gLockService->AcquireLock(lockName, PR_TRUE); + if (NS_SUCCEEDED(rv)) + { + //LOG("acquired lock \"%s\"\n", lockName); + RandomSleep(500, 1000); + //LOG("releasing lock \"%s\"\n", lockName); + rv = gLockService->ReleaseLock(lockName); + if (NS_FAILED(rv)) + { + LOG("failed to release lock [rv=%x]\n", rv); + NS_ERROR("failed to release lock"); + } + } + else + { + LOG("failed to acquire lock [rv=%x]\n", rv); + NS_NOTREACHED("failed to acquire lock"); + } + + LOG("exiting TestThread [lock=%s rv=%x]\n", lockName, rv); +} + +static const char *kLockNames[] = { + "foopy", + "test", + "1", + "xyz", + "moz4ever", + nsnull +}; + +static nsresult DoTest() +{ + nsresult rv; + + nsCOMPtr<nsIEventQueueService> eqs = + do_GetService(NS_EVENTQUEUESERVICE_CONTRACTID, &rv); + if (NS_FAILED(rv)) + return rv; + + rv = eqs->CreateMonitoredThreadEventQueue(); + if (NS_FAILED(rv)) + return rv; + + nsCOMPtr<nsIEventQueue> eq; + rv = eqs->GetThreadEventQueue(NS_CURRENT_THREAD, getter_AddRefs(eq)); + if (NS_FAILED(rv)) + return rv; + + nsCOMPtr<ipcILockService> lockService = + do_GetService(IPC_LOCKSERVICE_CONTRACTID); + + gLockService = lockService; + + PRThread *threads[10] = {0}; + int i = 0; + + for (const char **lockName = kLockNames; *lockName; ++lockName, ++i) + { + threads[i] = PR_CreateThread(PR_USER_THREAD, + TestThread, + (void *) *lockName, + PR_PRIORITY_NORMAL, + PR_GLOBAL_THREAD, + PR_JOINABLE_THREAD, + 0); + } + + for (i=0; threads[i]; ++i) + { + PR_JoinThread(threads[i]); + threads[i] = nsnull; + } + + gLockService = nsnull; + + LOG("joined with all threads; exiting DoTest\n"); + return NS_OK; +} + +int main(int argc, char **argv) +{ + LOG("entering main\n"); + + int numProcs = 10; + + // if this is a child process, then just run the test + if (argc > 1) + { + if (strcmp(argv[1], "-child") == 0) + { + RandomSleep(1000, 1000); + LOG("running child test\n"); + NS_InitXPCOM2(nsnull, nsnull, nsnull); + DoTest(); + NS_ShutdownXPCOM(nsnull); + return 0; + } + else if (argv[1][0] == '-') + { + // argument is a number + numProcs = atoi(argv[1] + 1); + if (numProcs == 0) + { + printf("### usage: TestIPCLocks [-N]\n" + "where, N is the number of test processes to spawn.\n"); + return -1; + } + } + } + + LOG("sleeping for 1 second\n"); + PR_Sleep(PR_SecondsToInterval(1)); + + PRProcess **procs = (PRProcess **) malloc(sizeof(PRProcess*) * numProcs); + int i; + + // else, spawn the child processes + for (i=0; i<numProcs; ++i) + { + char *const argv[] = {"./TestIPCLocks", "-child", nsnull}; + LOG("spawning child test\n"); + procs[i] = PR_CreateProcess("./TestIPCLocks", argv, nsnull, nsnull); + } + + PRInt32 exitCode; + for (i=0; i<numProcs; ++i) + PR_WaitProcess(procs[i], &exitCode); + + return 0; +} diff --git a/src/libs/xpcom18a4/ipc/ipcd/extensions/transmngr/.cvsignore b/src/libs/xpcom18a4/ipc/ipcd/extensions/transmngr/.cvsignore new file mode 100644 index 00000000..f3c7a7c5 --- /dev/null +++ b/src/libs/xpcom18a4/ipc/ipcd/extensions/transmngr/.cvsignore @@ -0,0 +1 @@ +Makefile diff --git a/src/libs/xpcom18a4/ipc/ipcd/extensions/transmngr/Makefile.in b/src/libs/xpcom18a4/ipc/ipcd/extensions/transmngr/Makefile.in new file mode 100644 index 00000000..5f80740d --- /dev/null +++ b/src/libs/xpcom18a4/ipc/ipcd/extensions/transmngr/Makefile.in @@ -0,0 +1,52 @@ +# ***** 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 Transaction Manager. +# +# The Initial Developer of the Original Code is +# Netscape Communications Corp. +# Portions created by the Initial Developer are Copyright (C) 2003 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# John Gaunt <jgaunt@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 + +DIRS = common module public src + +ifdef ENABLE_TESTS +DIRS += test +endif + +include $(topsrcdir)/config/rules.mk + diff --git a/src/libs/xpcom18a4/ipc/ipcd/extensions/transmngr/build/.cvsignore b/src/libs/xpcom18a4/ipc/ipcd/extensions/transmngr/build/.cvsignore new file mode 100644 index 00000000..b292dd21 --- /dev/null +++ b/src/libs/xpcom18a4/ipc/ipcd/extensions/transmngr/build/.cvsignore @@ -0,0 +1,9 @@ +Makefile +module.rc +module.res +tmModule.obj +transmngr_client.dll +transmngr_client.pdb +transmngr_client.exp +transmngr_client.lib +transmngr_client.ilk
\ No newline at end of file diff --git a/src/libs/xpcom18a4/ipc/ipcd/extensions/transmngr/build/Makefile.in b/src/libs/xpcom18a4/ipc/ipcd/extensions/transmngr/build/Makefile.in new file mode 100644 index 00000000..87277839 --- /dev/null +++ b/src/libs/xpcom18a4/ipc/ipcd/extensions/transmngr/build/Makefile.in @@ -0,0 +1,75 @@ +# ***** 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 Transaction Manager. +# +# The Initial Developer of the Original Code is +# Netscape Communications Corp. +# Portions created by the Initial Developer are Copyright (C) 2003 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# John Gaunt <jgaunt@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 = transmngr +LIBRARY_NAME = transmngr_client +EXPORT_LIBRARY = 1 +IS_COMPONENT = 1 +MODULE_NAME = transmngr + +REQUIRES = ipcd \ + string \ + xpcom \ + $(NULL) + +CPPSRCS = tmModule.cpp +EXPORTS = tmCID.h + +SHARED_LIBRARY_LIBS = \ + $(DIST)/lib/$(LIB_PREFIX)transmngr_s.$(LIB_SUFFIX) \ + $(DIST)/lib/$(LIB_PREFIX)transmngrcom_s.$(LIB_SUFFIX) \ + $(NULL) + +LOCAL_INCLUDES = \ + -I$(srcdir)/../src \ + -I$(srcdir)/../common \ + $(NULL) + +EXTRA_DSO_LDOPTS = \ + $(LIBS_DIR) \ + $(EXTRA_DSO_LIBS) \ + $(MOZ_COMPONENT_LIBS) \ + $(NULL) + +include $(topsrcdir)/config/rules.mk diff --git a/src/libs/xpcom18a4/ipc/ipcd/extensions/transmngr/build/tmCID.h b/src/libs/xpcom18a4/ipc/ipcd/extensions/transmngr/build/tmCID.h new file mode 100644 index 00000000..8ca23b84 --- /dev/null +++ b/src/libs/xpcom18a4/ipc/ipcd/extensions/transmngr/build/tmCID.h @@ -0,0 +1,53 @@ +/* ***** 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 Transaction Manager. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corp. + * Portions created by the Initial Developer are Copyright (C) 2003 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * John Gaunt <jgaunt@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 ***** */ + +#ifndef tmCID_h__ +#define tmCID_h__ + +#define TRANSACTION_SERVICE_CLASSNAME \ + "tmTransactionService" +#define TRANSACTION_SERVICE_CONTRACTID \ + "@mozilla.org/transaction/service;1" +#define TRANSACTION_SERVICE_CID \ +{ /* 1403adf4-94d1-4c67-a8ae-d9f86972d378 */ \ + 0x1403adf4, \ + 0x94d1, \ + 0x4c67, \ + {0xa8, 0xae, 0xd9, 0xf8, 0x69, 0x72, 0xd3, 0x78} \ +} + +#endif // !tmCID_h__ diff --git a/src/libs/xpcom18a4/ipc/ipcd/extensions/transmngr/build/tmModule.cpp b/src/libs/xpcom18a4/ipc/ipcd/extensions/transmngr/build/tmModule.cpp new file mode 100644 index 00000000..2853ba0a --- /dev/null +++ b/src/libs/xpcom18a4/ipc/ipcd/extensions/transmngr/build/tmModule.cpp @@ -0,0 +1,71 @@ +/* ***** 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 Transaction Manager. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corp. + * Portions created by the Initial Developer are Copyright (C) 2003 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * John Gaunt <jgaunt@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 "nsICategoryManager.h" +#include "nsIGenericFactory.h" +#include "nsIServiceManager.h" +#include "tmCID.h" +#include "tmTransactionService.h" + +//----------------------------------------------------------------------------- +// Define the contructor function for the objects +// +// NOTE: This creates an instance of objects by using the default constructor +//----------------------------------------------------------------------------- +NS_GENERIC_FACTORY_CONSTRUCTOR(tmTransactionService) + +//----------------------------------------------------------------------------- +// Define a table of CIDs implemented by this module along with other +// information like the function to create an instance, contractid, and +// class name. +//----------------------------------------------------------------------------- +static const nsModuleComponentInfo components[] = { + { TRANSACTION_SERVICE_CLASSNAME, + TRANSACTION_SERVICE_CID, + TRANSACTION_SERVICE_CONTRACTID, + tmTransactionServiceConstructor }, + /* + tmTransactionServiceRegisterProc, + tmTransactionServiceUnregisterProc }, + */ +}; + +//----------------------------------------------------------------------------- +// Implement the NSGetModule() exported function for your module +// and the entire implementation of the module object. +//----------------------------------------------------------------------------- +NS_IMPL_NSGETMODULE(transmngr, components) diff --git a/src/libs/xpcom18a4/ipc/ipcd/extensions/transmngr/common/.cvsignore b/src/libs/xpcom18a4/ipc/ipcd/extensions/transmngr/common/.cvsignore new file mode 100644 index 00000000..df8a43a2 --- /dev/null +++ b/src/libs/xpcom18a4/ipc/ipcd/extensions/transmngr/common/.cvsignore @@ -0,0 +1,5 @@ +Makefile +tmTransaction.obj +tmVector.obj +transmgrcom_s.pdb +transmgrcom_s.lib diff --git a/src/libs/xpcom18a4/ipc/ipcd/extensions/transmngr/common/Makefile.in b/src/libs/xpcom18a4/ipc/ipcd/extensions/transmngr/common/Makefile.in new file mode 100644 index 00000000..4521c39a --- /dev/null +++ b/src/libs/xpcom18a4/ipc/ipcd/extensions/transmngr/common/Makefile.in @@ -0,0 +1,61 @@ +# ***** 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 Transaction Manager. +# +# The Initial Developer of the Original Code is +# Netscape Communications Corp. +# Portions created by the Initial Developer are Copyright (C) 2003 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# John Gaunt <jgaunt@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 = ipcd +LIBRARY_NAME = transmgrcom_s +FORCE_STATIC_LIB = 1 +MODULE_NAME = ipcd + +FORCE_USE_PIC = 1 + +REQUIRES = \ + xpcom \ + $(NULL) + +CPPSRCS = \ + tmTransaction.cpp \ + tmVector.cpp \ + $(NULL) + +include $(topsrcdir)/config/rules.mk diff --git a/src/libs/xpcom18a4/ipc/ipcd/extensions/transmngr/common/tmTransaction.cpp b/src/libs/xpcom18a4/ipc/ipcd/extensions/transmngr/common/tmTransaction.cpp new file mode 100644 index 00000000..5fb7e5bf --- /dev/null +++ b/src/libs/xpcom18a4/ipc/ipcd/extensions/transmngr/common/tmTransaction.cpp @@ -0,0 +1,107 @@ +/* ***** 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 Transaction Manager. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corp. + * Portions created by the Initial Developer are Copyright (C) 2003 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * John Gaunt <jgaunt@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 <stdlib.h> +#include "tmTransaction.h" +#ifdef VBOX_USE_IPRT_IN_XPCOM +# include <iprt/mem.h> +#endif + +/////////////////////////////////////////////////////////////////////////////// +// Constructor(s) & Destructor + +tmTransaction::~tmTransaction() { + if (mHeader) +#ifdef VBOX_USE_IPRT_IN_XPCOM + RTMemFree(mHeader); +#else + free(mHeader); +#endif +} + +// call only once per lifetime of object. does not reclaim the +// raw message, only sets it. +nsresult +tmTransaction::Init(PRUint32 aOwnerID, + PRInt32 aQueueID, + PRUint32 aAction, + PRInt32 aStatus, + const PRUint8 *aMessage, + PRUint32 aLength) { + nsresult rv = NS_OK; + tmHeader *header = nsnull; + + // indicates the message is the entire raw message + if (aQueueID == TM_INVALID_ID) { +#ifdef VBOX_USE_IPRT_IN_XPCOM + header = (tmHeader*) RTMemAlloc(aLength); +#else + header = (tmHeader*) malloc(aLength); +#endif + if (header) { + mRawMessageLength = aLength; + memcpy(header, aMessage, aLength); + } + else + rv = NS_ERROR_OUT_OF_MEMORY; + } + else { // need to create the tmHeader and concat the message +#ifdef VBOX_USE_IPRT_IN_XPCOM + header = (tmHeader*) RTMemAlloc (sizeof(tmHeader) + aLength); +#else + header = (tmHeader*) malloc (sizeof(tmHeader) + aLength); +#endif + if (header) { + mRawMessageLength = sizeof(tmHeader) + aLength; + header->action = aAction; + header->queueID = aQueueID; + header->status = aStatus; + header->reserved = 0x00000000; + if (aLength > 0) // add the message if it exists + memcpy((header + 1), aMessage, aLength); + } + else + rv = NS_ERROR_OUT_OF_MEMORY; + } + + if (NS_SUCCEEDED(rv)) { + mOwnerID = aOwnerID; + mHeader = header; + } + + return rv; +} diff --git a/src/libs/xpcom18a4/ipc/ipcd/extensions/transmngr/common/tmTransaction.h b/src/libs/xpcom18a4/ipc/ipcd/extensions/transmngr/common/tmTransaction.h new file mode 100644 index 00000000..b86f20f0 --- /dev/null +++ b/src/libs/xpcom18a4/ipc/ipcd/extensions/transmngr/common/tmTransaction.h @@ -0,0 +1,234 @@ +/* ***** 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 Transaction Manager. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corp. + * Portions created by the Initial Developer are Copyright (C) 2003 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * John Gaunt <jgaunt@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 ***** */ + +#ifndef _tmTransaction_H_ +#define _tmTransaction_H_ + +#include "tmUtils.h" + +////////////////////////////////////////////////////////////////////////////// +// +// Message format +// +// |------------------------------------|-- +// |QueueID | | +// |------------------------------------| | +// | Action - Post/Flush/Attach etc | |- this is the tmHeader struct +// |------------------------------------| | +// |Status | | +// |------------------------------------| | +// |Padding | | +// |------------------------------------|-- +// |Message Data (payload) | +// |------------------------------------| +// +// The Attach call is a special case in that it doesn't have a QueueID yet. A +// QueueID will be 0's. The message Data will be the Queue Name String which +// will be the profile name with a domain attached, a domain being +// [prefs|cookies|etc] +// +////////////////////////////////////////////////////////////////////////////// + +/** + * tmHeader contains various flags identifying + */ +struct tmHeader { + PRInt32 queueID; // will be the index of the queue in the TM, can be < 0 + PRUint32 action; // defined by tmUtils.h will be > 0 + PRInt32 status; // return values from methods, could be < 0 + PRUint32 reserved; // not currently used, maintaining word alignment +}; + +/** + * Using tmTransaction: + * + * After creating a tmTransaction either through new or as a member + * or local variable a process must call Init() with the proper set of + * arguements to initialize the state of the transaction. tmTransaction is + * set up to accept 3 types of initialization. + * + * 1) Raw message - All data is carried in the byte pointer aMessage, + * args 2,3,4 should be set to TM_NO_ID and aLength + * must be set to the full length of aMessage, including null + * termination if the payload is a null-term string and the size of the + * tmHeader struct preceeding the message. Currently this + * format is used at the IPC boundary, where we receive a byte pointer + * from the IPC Daemon. + * + * 2) Flags only - aQueueID, aAction and aStatus are all set. aMessage + * should be set to nsnull and aLength to 0. This format is used when + * sending reply messages (except for ATTACH_REPLY) and when the TS + * Transaction Service is sending "control" messages to the Manager - + * flush, detach, etc... + * + * 3) Flags and message - All arguements are set. The aMessage is only + * the message for the client app. aLength should be set to the length + * of aMessage and not include the length of the tmHeader struct. + * + * The only data member you can set post initialization is the QueueID. + * You should only call Init() once in the lifetime of a tmTransaction + * as it doesn't clean up the exisiting data before assigning the new + * data. Therefore it would leak most heinously if Init() were to be + * called twice. + * + * mOwnerID only has relevance on the IPC daemon side of things. The + * Transaction Service has no knowledge of this ID and makes no use + * of it. + */ +class tmTransaction +{ + +public: + + //////////////////////////////////////////////////////////////////////////// + // Constructor(s) & Destructor + + tmTransaction(): mHeader(nsnull), mRawMessageLength(0), mOwnerID(0) { } + + virtual ~tmTransaction(); + + //////////////////////////////////////////////////////////////////////////// + // Public Member Functions + + // Initializer //////////// + + /** + * Sets up the internal data of the transaction. Allows for 3 basic ways + * to call this function: No flags and just one big raw message, Just + * flags and no message, and finally flags and message. If the message + * exists it is copied into the transaction. + * + * @param aOwnerID is given to us by the IPC Daemon and is specific + * to that transport layer. It is only set when transactions + * are sent from the TM to the TS. + * + * @param aQueueID is the either the destination queue, or the queue from + * where this transaction is eminating + * + * @param aAction is the action that occured to generate this transaction + * + * @param aStatus is the success state of the action. + * + * @param aMessage can be a raw message including the 3 flags above or it + * can be just the "payload" of the transaction that the destination + * process is going deal with. + * + * @param aLength is the length of the message. If there is a null + * terminated string in the message make sure the length includes + * the null termination. + * + * @returns NS_OK if everything was successful + * @returns NS_ERROR_OUT_OF_MEMORY if allocation of space for the + * copy of the message fails. + */ + nsresult Init(PRUint32 aOwnerID, + PRInt32 aQueueID, + PRUint32 aAction, + PRInt32 aStatus, + const PRUint8 *aMessage, + PRUint32 aLength); + + // Data Accessors ///////// + + /** + * @returns a const byte pointer to the message + */ + const PRUint8* GetMessage() const { return (PRUint8*)(mHeader + 1); } + + /** + * @returns the length of the message + */ + PRUint32 GetMessageLength() const { + return (mRawMessageLength > sizeof(tmHeader)) ? + (mRawMessageLength - sizeof(tmHeader)) : 0; + } + + /** + * @returns a const pointer to the memory containing the + * flag information followed immediately by the message + * data. + */ + const PRUint8* GetRawMessage() const { return (PRUint8*) mHeader; } + + /** + * @returns the length of the flags and message combined + */ + PRUint32 GetRawMessageLength() const { return mRawMessageLength; } + + /** + * @returns the id of the destination or sending queue, depending on the + * direction of the transaction. + */ + PRInt32 GetQueueID() const { return mHeader->queueID; } + + /** + * @returns the action represented by this transaction + */ + PRUint32 GetAction() const { return mHeader->action; } + + /** + * @returns the success state, if applicable of the action leading + * up to this message + */ + PRInt32 GetStatus() const { return mHeader->status; } + + /** + * @returns the client ID (in IPC daemon terms) of the client who initiated + * the exchange that generated this transaction. + */ + PRUint32 GetOwnerID() const { return mOwnerID; } + + // Data Mutator /////////// + + /** + * Sets the ID of the destination or source queue. Init should have been + * called before the call to this function. + */ + void SetQueueID(PRInt32 aID) { mHeader->queueID = aID; } + +protected: + + //////////////////////////////////////////////////////////////////////////// + // Protected Member Variables + + tmHeader* mHeader; // points to beginning of entire message + PRUint32 mRawMessageLength; // length of entire message, incl tmHeader + PRUint32 mOwnerID; // client who sent this trans. - a IPC ClientID + +}; + +#endif diff --git a/src/libs/xpcom18a4/ipc/ipcd/extensions/transmngr/common/tmUtils.h b/src/libs/xpcom18a4/ipc/ipcd/extensions/transmngr/common/tmUtils.h new file mode 100644 index 00000000..f85184d3 --- /dev/null +++ b/src/libs/xpcom18a4/ipc/ipcd/extensions/transmngr/common/tmUtils.h @@ -0,0 +1,93 @@ +/* ***** 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 Transaction Manager. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corp. + * Portions created by the Initial Developer are Copyright (C) 2003 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * John Gaunt <jgaunt@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 ***** */ + +#ifndef _tmUtils_H_ +#define _tmUtils_H_ + +#include "nscore.h" +#include "nsError.h" +#include "nsID.h" +#include "prlog.h" +#include <stdio.h> + +// UUID used to identify the Transaction Module in both daemon and client +// not part of the XPCOM hooks, but rather a means of identifying +// modules withing the IPC daemon. +#define TRANSACTION_MODULE_ID \ +{ /* c3dfbcd5-f51d-420b-abf4-3bae445b96a9 */ \ + 0xc3dfbcd5, \ + 0xf51d, \ + 0x420b, \ + {0xab, 0xf4, 0x3b, 0xae, 0x44, 0x5b, 0x96, 0xa9} \ +} + +//static const nsID kTransModuleID = TRANSACTION_MODULE_ID; + +/////////////////////////////////////////////////////////////////////////////// +// match NS_ERROR_FOO error code formats +// +// only create new errors for those errors that are specific to TM + +#define NS_ERROR_MODULE_TM 27 /* XXX goes in nserror.h -- integrating with ns error codes */ + +#define TM_ERROR NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_TM, 1) +#define TM_ERROR_WRONG_QUEUE NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_TM, 2) +#define TM_ERROR_NOT_POSTED NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_TM, 3) +#define TM_ERROR_QUEUE_EXISTS NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_TM, 4) +#define TM_SUCCESS_DELETE_QUEUE NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_TM, 6) + + +// XXX clean up: +#define TM_INVALID_ID 0xFFFFFFFF +#define TM_INVALID 0xFFFFFFFF +#define TM_NO_ID 0xFFFFFFFE + +// Transaction Actions +enum { + TM_ATTACH = 0, + TM_ATTACH_REPLY, + TM_POST, + TM_POST_REPLY, + TM_NOTIFY, + TM_FLUSH, + TM_FLUSH_REPLY, + TM_DETACH, + TM_DETACH_REPLY +}; + +#endif + diff --git a/src/libs/xpcom18a4/ipc/ipcd/extensions/transmngr/common/tmVector.cpp b/src/libs/xpcom18a4/ipc/ipcd/extensions/transmngr/common/tmVector.cpp new file mode 100644 index 00000000..99213f27 --- /dev/null +++ b/src/libs/xpcom18a4/ipc/ipcd/extensions/transmngr/common/tmVector.cpp @@ -0,0 +1,179 @@ +/* ***** 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 Transaction Manager. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corp. + * Portions created by the Initial Developer are Copyright (C) 2003 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * John Gaunt <jgaunt@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 <stdlib.h> +#include "tmVector.h" +#ifdef VBOX_USE_IPRT_IN_XPCOM +# include <iprt/mem.h> +#endif + +//////////////////////////////////////////////////////////////////////////// +// Constructor(s) & Destructor + +// can not be responsible for reclaiming memory pointed to by the void*s in +// the collection - how would we reclaim, don't know how they were allocated +tmVector::~tmVector() { + if (mElements) +#ifdef VBOX_USE_IPRT_IN_XPCOM + RTMemFree((void*)mElements); +#else + free((void*)mElements); +#endif +} + +/////////////////////////////////////////////////////////////////////////////// +// Public Member Functions + +nsresult +tmVector::Init() { + +#ifdef VBOX_USE_IPRT_IN_XPCOM + mElements = (void**) RTMemAllocZ (mCapacity * sizeof(void*)); +#else + mElements = (void**) calloc (mCapacity, sizeof(void*)); +#endif + if (!mElements) + return NS_ERROR_OUT_OF_MEMORY; + return NS_OK; +} + +/////////////////////////////////////////////////////////////////////////////// +// mutators + +PRInt32 +tmVector::Append(void *aElement){ + PR_ASSERT(aElement); + + // make sure there is room + if (mNext == mCapacity) + if (NS_FAILED(Grow())) + return -1; + + // put the element in the array + mElements[mNext] = aElement; + mCount++; + + // encapsulates the index into a success value + return mNext++; // post increment. +} + +void +tmVector::Remove(void *aElement) { + PR_ASSERT(aElement); + + for (PRUint32 index = 0; index < mNext; index++) { + if (mElements[index] == aElement) { + mElements[index] = nsnull; + mCount--; + if (index == mNext-1) { // if we removed the last element + mNext--; + // don't test for success of the shrink + Shrink(); + } + } + } +} + +void +tmVector::RemoveAt(PRUint32 aIndex) { + PR_ASSERT(aIndex < mNext); + + // remove the element if it isn't already nsnull + if (mElements[aIndex] != nsnull) { + mElements[aIndex] = nsnull; + mCount--; + if (aIndex == mNext-1) { // if we removed the last element + mNext--; + // don't test for success of the shrink + Shrink(); + } + } +} + +//void* +//tmVector::operator[](int index) { +// if (index < mNext && index >= 0) +// return mElements[index]; +// return nsnull; +//} + +// Does not delete any of the data, merely removes references to them +void +tmVector::Clear(){ + memset(mElements, 0, mCapacity); + mCount = 0; + mNext = 0; +} + +////////////////////////////////////////////////////////////////////////////// +// Protected Member Functions + +// increases the capacity by the growth increment +nsresult +tmVector::Grow() { + + PRUint32 newcap = mCapacity + GROWTH_INC; +#ifdef VBOX_USE_IPRT_IN_XPCOM + mElements = (void**) RTMemRealloc(mElements, (newcap * sizeof(void*))); +#else + mElements = (void**) realloc(mElements, (newcap * sizeof(void*))); +#endif + if (mElements) { + mCapacity = newcap; + return NS_OK; + } + return NS_ERROR_FAILURE; +} + +// reduces the capacity by the growth increment. leaves room +// for one more add before needing to Grow(). +nsresult +tmVector::Shrink() { + + PRUint32 newcap = mCapacity - GROWTH_INC; + if (mNext < newcap) { +#ifdef VBOX_USE_IPRT_IN_XPCOM + mElements = (void**) RTMemRealloc(mElements, newcap * sizeof(void*)); +#else + mElements = (void**) realloc(mElements, newcap * sizeof(void*)); +#endif + if (!mElements) + return NS_ERROR_OUT_OF_MEMORY; + mCapacity = newcap; + } + return NS_OK; +} diff --git a/src/libs/xpcom18a4/ipc/ipcd/extensions/transmngr/common/tmVector.h b/src/libs/xpcom18a4/ipc/ipcd/extensions/transmngr/common/tmVector.h new file mode 100644 index 00000000..4ddb68ef --- /dev/null +++ b/src/libs/xpcom18a4/ipc/ipcd/extensions/transmngr/common/tmVector.h @@ -0,0 +1,160 @@ +/* ***** 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 Transaction Manager. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corp. + * Portions created by the Initial Developer are Copyright (C) 2003 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * John Gaunt <jgaunt@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 ***** */ + +#ifndef _tmVector_H_ +#define _tmVector_H_ + +#include "tmUtils.h" + +#define GROWTH_INC 5 + +/** + * A simple, clear, self-growing, collection of objects. typed independant + * basically a growing array. Useful in situations where you need an + * indexed collection but do not know the size in advance and need the + * ability for increase and decrease in size. Not optimized for anything + * in particular, or any size in particular. + * + * Is able to guarantee the index of an item will + * not change due to removals of a lower indexed item. The growing, + * and shrinking all happens to the end of the collection + * + * Does not backfill, adds to the end. At some point this should be + * changed to make best use of space. + */ +class tmVector +{ +public: + + //////////////////////////////////////////////////////////////////////////// + // Constructor(s) & Destructor + + /** + * Set some sane default values to set up the internal storage. Init() + * must be called after construction of the object to allcate the + * backing store. + */ + tmVector() : mNext(0), mCount(0), mCapacity(10), mElements(nsnull) {;} + + /** + * Reclaim the memory allocated in the Init() method. + */ + virtual ~tmVector(); + + //////////////////////////////////////////////////////////////////////////// + // Public Member Functions + + /** + * Allocates the storage back-end + * + * @returns NS_OK if allocation succeeded + * @returns NS_ERROR_OUT_OF_MEMORY if the allocation failed + */ + nsresult Init(); + + // mutators + + /** + * @returns the index of the element added, if successful + * @returns -1 if an error occured during allocation of space + */ + PRInt32 Append(void *aElement); + + /** + * This does not collapse the collection, it leaves holes. Note, it also + * doesn't delete the element, it merely removes it from the collection + */ + void Remove(void *aElement); + + /** + * This does not collapse the collection, it leaves holes. Note, it also + * doesn't delete the element, it merely removes it from the collection + */ + void RemoveAt(PRUint32 aIndex); + + /** + * Does not call delete on the elements since we have no idea how to + * reclaim the memory. Sets all array slots to 0. + */ + void Clear(); + + /** + * @returns the element at the index indicated, including nsnull if the + * slot is empty. + */ + void* operator[](PRUint32 index) { + PR_ASSERT(index < mNext); + return mElements[index]; + } + + /** + * @returns the number of elements stored + */ + PRUint32 Count() { return mCount; } + + /** + * Meant to be used as the conditional in a loop. |index < size| should + * reach all elements of the collection and not run out of bounds. If + * slots 0,1,4,5,6 contain elements Size() will return 7, Count() will + * return 5. + * + * @returns the number of slots in the array taken, irrespective of + * holes in the collection. + */ + PRUint32 Size() { return mNext; } + +protected: + + nsresult Grow(); // mCapacity += GROWTH_INC - realloc()s + nsresult Shrink(); // mCapacity -= GROWTH_INC - dumb, free, malloc + + //////////////////////////////////////////////////////////////////////////// + // Protected Member Variables + + // bookkeeping variables + PRUint32 mNext; // next element insertion slot (0 based) + PRUint32 mCount; // how many elements in the Vector (1 based) + PRUint32 mCapacity; // current capacity of the Vector (1 based) + + // the actual array of objects being stored + void **mElements; + +private: + +}; + +#endif diff --git a/src/libs/xpcom18a4/ipc/ipcd/extensions/transmngr/module/.cvsignore b/src/libs/xpcom18a4/ipc/ipcd/extensions/transmngr/module/.cvsignore new file mode 100644 index 00000000..ccfb2bc3 --- /dev/null +++ b/src/libs/xpcom18a4/ipc/ipcd/extensions/transmngr/module/.cvsignore @@ -0,0 +1,11 @@ +Makefile +module.rc +module.res +tmIPCModule.obj +tmQueue.obj +tmTransactionManager.obj +transmgr.dll +transmgr.exp +transmgr.ilk +transmgr.lib +transmgr.pdb diff --git a/src/libs/xpcom18a4/ipc/ipcd/extensions/transmngr/module/Makefile.in b/src/libs/xpcom18a4/ipc/ipcd/extensions/transmngr/module/Makefile.in new file mode 100644 index 00000000..b8baa225 --- /dev/null +++ b/src/libs/xpcom18a4/ipc/ipcd/extensions/transmngr/module/Makefile.in @@ -0,0 +1,100 @@ +# ***** 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 Transaction Manager. +# +# The Initial Developer of the Original Code is +# Netscape Communications Corp. +# Portions created by the Initial Developer are Copyright (C) 2003 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# John Gaunt <jgaunt@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 = ipcd +LIBRARY_NAME = transmgr +MODULE_NAME = ipcd + +FORCE_SHARED_LIB = 1 +NO_DIST_INSTALL = 1 +NO_INSTALL = 1 + +ifeq ($(OS_ARCH),Darwin) +NO_COMPONENT_LINK_MAP = 1 +MOZ_COMPONENTS_VERSION_SCRIPT_LDFLAGS = +endif + +REQUIRES = nspr \ + xpcom \ + $(NULL) + +CPPSRCS = \ + tmIPCModule.cpp \ + tmQueue.cpp \ + tmTransactionManager.cpp \ + $(NULL) + +EXPORTS = \ + tmIPCModule.h \ + $(NULL) + +LOCAL_INCLUDES = \ + -I$(srcdir)/../common \ + $(NULL) + +include $(topsrcdir)/config/config.mk + +LIBS = \ + $(EXTRA_DSO_LIBS) \ + $(NSPR_LIBS) \ + $(NULL) + +EXTRA_DSO_LDOPTS = \ + $(LIBS_DIR) \ + $(NSPR_LIBS) \ + $(DIST)/lib/$(LIB_PREFIX)transmgrcom_s.$(LIB_SUFFIX) \ + $(EXTRA_DSO_LIBS) \ + $(NULL) + +include $(topsrcdir)/config/rules.mk + +_IPC_FILES = \ + $(DLL_PREFIX)$(LIBRARY_NAME)$(DLL_SUFFIX) \ + $(NULL) + +libs:: $(_IPC_FILES) + $(INSTALL) $^ $(DIST)/bin/ipc/modules + +install:: $(_IPC_FILES) + $(SYSINSTALL) $(IFLAGS1) $^ $(DESTDIR)$(mozappdir)/ipc/modules diff --git a/src/libs/xpcom18a4/ipc/ipcd/extensions/transmngr/module/tmIPCModule.cpp b/src/libs/xpcom18a4/ipc/ipcd/extensions/transmngr/module/tmIPCModule.cpp new file mode 100644 index 00000000..3982c93c --- /dev/null +++ b/src/libs/xpcom18a4/ipc/ipcd/extensions/transmngr/module/tmIPCModule.cpp @@ -0,0 +1,137 @@ +/* ***** 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 Transaction Manager. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corp. + * Portions created by the Initial Developer are Copyright (C) 2003 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * John Gaunt <jgaunt@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 "tmIPCModule.h" +#include "tmTransaction.h" +#include "tmTransactionManager.h" + +/////////////////////////////////////////////////////////////////////////////// +// IPC Daemon hookup stuff + +// functionpointer array giving access to this module from the IPC daemon +static ipcModuleMethods gTransMethods = +{ + IPC_MODULE_METHODS_VERSION, + tmIPCModule::Init, + tmIPCModule::Shutdown, + tmIPCModule::HandleMsg +}; + +static ipcModuleEntry gTransModuleEntry[] = +{ + { TRANSACTION_MODULE_ID, &gTransMethods } +}; + +IPC_IMPL_GETMODULES(TransactionModule, gTransModuleEntry) + +static const nsID kTransModuleID = TRANSACTION_MODULE_ID; + +/////////////////////////////////////////////////////////////////////////////// +// Define global variable + +tmTransactionManager *tmIPCModule::tm; + +/////////////////////////////////////////////////////////////////////////////// +// IPC Module API + +void +tmIPCModule::Init() { + if (!tm) + InitInternal(); +} + +void +tmIPCModule::Shutdown() { + if (tm) { + delete tm; + tm = nsnull; + } +} + +// straight pass-through, don't check args, let the TM do it. +void +tmIPCModule::HandleMsg(ipcClientHandle client, const nsID &target, + const void *data, PRUint32 dataLen) { + + // make sure the trans mngr is there + if (!tm && (InitInternal() < 0)) + return; + + // create the transaction + tmTransaction *trans = new tmTransaction(); + + // initialize it + if (trans) { + if(NS_SUCCEEDED(trans->Init(IPC_GetClientID(client), // who owns it + TM_INVALID_ID, // in data + TM_INVALID, // in data + TM_INVALID, // in data + (PRUint8 *)data, // raw message + dataLen))) { // length of message + // pass it on to the trans mngr + tm->HandleTransaction(trans); + } + else + delete trans; + } +} + +/////////////////////////////////////////////////////////////////////////////// +// tmIPCModule API + +// straight pass-through, don't check args, let the TS & TM do it. +void +tmIPCModule::SendMsg(PRUint32 aDestClientIPCID, tmTransaction *aTransaction) { + + IPC_SendMsg(aDestClientIPCID, + kTransModuleID, + (void *)aTransaction->GetRawMessage(), + aTransaction->GetRawMessageLength()); +} + +/////////////////////////////////////////////////////////////////////////////// +// Protected Methods + +PRInt32 +tmIPCModule::InitInternal() { + + tm = new tmTransactionManager(); + if (tm) + return tm->Init(); + return -1; +} + diff --git a/src/libs/xpcom18a4/ipc/ipcd/extensions/transmngr/module/tmIPCModule.h b/src/libs/xpcom18a4/ipc/ipcd/extensions/transmngr/module/tmIPCModule.h new file mode 100644 index 00000000..d4623b1e --- /dev/null +++ b/src/libs/xpcom18a4/ipc/ipcd/extensions/transmngr/module/tmIPCModule.h @@ -0,0 +1,109 @@ +/* ***** 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 Transaction Manager. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corp. + * Portions created by the Initial Developer are Copyright (C) 2003 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * John Gaunt <jgaunt@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 ***** */ + +#ifndef _tmIPCModule_H_ +#define _tmIPCModule_H_ + +#include "ipcModuleUtil.h" +#include "tmUtils.h" + +// forward declarations +class tmTransaction; +class tmTransactionManager; + +/** + * Basically an interface between the tmTransactionManager and the IPC + * daemon. Does little else than format the data from one party into + * a format understandable to the other. + * + * The reason for this class is to try and abstract the transportation + * layer the transaction service uses. By using this class the Transaction + * Manager itself only needs to know that clients are identified by + * PRUint32 IDs. + */ +class tmIPCModule +{ +public: + + //////////////////////////////////////////////////////////////////////////// + // ipcModule API - called from IPC daemon + + /** + * Clean up the TM + */ + static void Shutdown(); + + /** + * Check the TM, create it if neccessary. + */ + static void Init(); + + /** + * Receives a message from the IPC daemon, creates a transaction and sends + * it to the TM to deal with. + */ + static void HandleMsg(ipcClientHandle client, + const nsID &target, + const void *data, + PRUint32 dataLen); + + //////////////////////////////////////////////////////////////////////////// + // tmIPCModule API - called from tmTransactionManager + + /** + * Sends the message to the IPC daemon to be deliverd to the client arg. + */ + static void SendMsg(PRUint32 aDestClientIPCID, tmTransaction *aTransaction); + +protected: + + /** + * tm should be null coming into this method. This does NOT delete tm + * first. + * + * @returns NS_OK if everything succeeds + * @returns -1 if initialization fails + */ + static PRInt32 InitInternal(); + + static tmTransactionManager *tm; + +}; + +#endif + + diff --git a/src/libs/xpcom18a4/ipc/ipcd/extensions/transmngr/module/tmQueue.cpp b/src/libs/xpcom18a4/ipc/ipcd/extensions/transmngr/module/tmQueue.cpp new file mode 100644 index 00000000..5164446f --- /dev/null +++ b/src/libs/xpcom18a4/ipc/ipcd/extensions/transmngr/module/tmQueue.cpp @@ -0,0 +1,223 @@ +/* ***** 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 Transaction Manager. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corp. + * Portions created by the Initial Developer are Copyright (C) 2003 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * John Gaunt <jgaunt@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 "plstr.h" +#include "tmQueue.h" +#include "tmTransaction.h" +#include "tmTransactionManager.h" +#include "tmUtils.h" + +/////////////////////////////////////////////////////////////////////////////// +// Constructors & Destructor + +tmQueue::~tmQueue() { + + // empty the vectors + PRUint32 index = 0; + PRUint32 size = mTransactions.Size(); + for ( ; index < size ; index++) { + if (mTransactions[index]) + delete (tmTransaction *)mTransactions[index]; + } + + // don't need to delete the mListeners because + // we just insert PRUint32s, no allocation + + mTM = nsnull; + mID = 0; + if (mName) + PL_strfree(mName); +} + +/////////////////////////////////////////////////////////////////////////////// +// Public Methods + +PRInt32 +tmQueue::Init(const char* aName, PRUint32 aID, tmTransactionManager *aTM) { + PR_ASSERT(mTM == nsnull); + + if (NS_SUCCEEDED(mTransactions.Init()) && + NS_SUCCEEDED(mListeners.Init()) && + ((mName = PL_strdup(aName)) != nsnull) ) { + mTM = aTM; + mID = aID; + return NS_OK; + } + return -1; +} + +PRInt32 +tmQueue::AttachClient(PRUint32 aClientID) { + + PRInt32 status = NS_OK; // success of adding client + + if (!IsAttached(aClientID)) { + // add the client to the listener list -- null safe call + status = mListeners.Append((void*) aClientID); + } + else + status = -2; + + // create & init a reply transaction + tmTransaction trans; + if (NS_SUCCEEDED(trans.Init(aClientID, // owner's ipc ID + mID, // client gets our ID + TM_ATTACH_REPLY, // action + status, // success of the add + (PRUint8*)mName, // client matches name to ID + PL_strlen(mName)+1))) { + // send the reply + mTM->SendTransaction(aClientID, &trans); + } + + // if we successfully added the client - send all current transactions + if (status >= 0) { // append returns the index of the added element + + PRUint32 size = mTransactions.Size(); + for (PRUint32 index = 0; index < size; index++) { + if (mTransactions[index]) + mTM->SendTransaction(aClientID, (tmTransaction*) mTransactions[index]); + } + } + return status; +} + +PRInt32 +tmQueue::DetachClient(PRUint32 aClientID) { + + PRUint32 size = mListeners.Size(); + PRUint32 id = 0; + PRInt32 status = -1; + + for (PRUint32 index = 0; index < size; index++) { + id = (PRUint32)NS_PTR_TO_INT32(mListeners[index]); + if(id == aClientID) { + mListeners.RemoveAt(index); + status = NS_OK; + break; + } + } + + tmTransaction trans; + if (NS_SUCCEEDED(trans.Init(aClientID, + mID, + TM_DETACH_REPLY, + status, + nsnull, + 0))) { + // send the reply + mTM->SendTransaction(aClientID, &trans); + } + + // if we've removed all the listeners, remove the queue. + if (mListeners.Size() == 0) + return TM_SUCCESS_DELETE_QUEUE; + return status; +} + +void +tmQueue::FlushQueue(PRUint32 aClientID) { + + if(!IsAttached(aClientID)) + return; + + PRUint32 size = mTransactions.Size(); + for (PRUint32 index = 0; index < size; index++) + if (mTransactions[index]) + delete (tmTransaction*)mTransactions[index]; + + mTransactions.Clear(); + + tmTransaction trans; + if (NS_SUCCEEDED(trans.Init(aClientID, + mID, + TM_FLUSH_REPLY, + NS_OK, + nsnull, + 0))) { + mTM->SendTransaction(aClientID, &trans); + } +} + +PRInt32 +tmQueue::PostTransaction(tmTransaction *aTrans) { + + PRInt32 status = -1; + PRUint32 ownerID = aTrans->GetOwnerID(); + + // if we are attached, have the right queue and have successfully + // appended the transaction to the queue, send the transaction + // to all the listeners. + + if (IsAttached(ownerID) && aTrans->GetQueueID() == mID) + status = mTransactions.Append(aTrans); + + if (status >= 0) { + // send the transaction to all members of mListeners except the owner + PRUint32 size = mListeners.Size(); + PRUint32 id = 0; + for (PRUint32 index = 0; index < size; index++) { + id = (PRUint32)NS_PTR_TO_INT32(mListeners[index]); + if (ownerID != id) + mTM->SendTransaction(id, aTrans); + } + } + + tmTransaction trans; + if (NS_SUCCEEDED(trans.Init(ownerID, + mID, + TM_POST_REPLY, + status, + nsnull, + 0))) { + // send the reply + mTM->SendTransaction(ownerID, &trans); + } + return status; +} + +PRBool +tmQueue::IsAttached(PRUint32 aClientID) { + // XXX could be an issue if the aClientID is 0 and there + // is a "hole" in the mListeners vector. - may NEED to store PRUint32*s + PRUint32 size = mListeners.Size(); + for (PRUint32 index = 0; index < size; index++) { + if (aClientID == (PRUint32)NS_PTR_TO_INT32(mListeners[index])) + return PR_TRUE; + } + return PR_FALSE; +} diff --git a/src/libs/xpcom18a4/ipc/ipcd/extensions/transmngr/module/tmQueue.h b/src/libs/xpcom18a4/ipc/ipcd/extensions/transmngr/module/tmQueue.h new file mode 100644 index 00000000..1c680084 --- /dev/null +++ b/src/libs/xpcom18a4/ipc/ipcd/extensions/transmngr/module/tmQueue.h @@ -0,0 +1,186 @@ +/* ***** 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 Transaction Manager. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corp. + * Portions created by the Initial Developer are Copyright (C) 2003 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * John Gaunt <jgaunt@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 ***** */ + +#ifndef _tmQueue_H_ +#define _tmQueue_H_ + +#include "tmUtils.h" +#include "tmVector.h" + +class tmClient; +class tmTransaction; +class tmTransactionManager; + +/** + * This class isn't so much a queue as it is storage for transactions. It + * is set up to recieve and store transactions in a growing collection + * (using tmVectors). Different messages can be recieved from the + * Transaction Manager(TM) the queue belongs to which can add and remove + * listeners, empty the queue (flush), and add messages to the queue. + * + * See the documentation in tmTransactionService.h for details on the + * messages you can send to and recieve from the queues in the TM + */ +class tmQueue +{ + +public: + + //////////////////////////////////////////////////////////////////////////// + // Constructor & Destructor + + /** + * Set the internal state to default values. Init() must be called + * after construction to allocate the storage and set the name and ID. + */ + tmQueue(): mID(0), mName(nsnull), mTM(nsnull) { } + + /** + * Reclaim the memory allocated in Init(). Destroys the transactions in + * the transaction storage and the ids in the listener storage + */ + virtual ~tmQueue(); + + //////////////////////////////////////////////////////////////////////////// + // Public Member Functions + + /** + * Initialize internal storage vectors and set the name of the queue + * and the pointer to the TM container. + * + * @returns NS_OK if everything succeeds + * @returns -1 if initialization fails + */ + PRInt32 Init(const char* aName, PRUint32 aID, tmTransactionManager *aTM); + + // Queue Operations + + /** + * Adds the clientID to the list of queue listeners. A reply is created + * and sent to the client. The reply contains both the name of the + * queue and the id, so the client can match the id to the name and + * then use the id in all further communications to the queue. All + * current transactions in the queue are then sent to the client. + * + * If the client was already attached the reply is sent, but not the + * outstanding transactions, the assumption being made that all + * transactions have already been sent to the client. + * + * The reply is sent for all cases, with the return value in the status + * field. + * + * @returns >= 0 if the client was attached successfully + * @returns -1 if the client was not attached + * @returns -2 if the client was already attached + */ + PRInt32 AttachClient(PRUint32 aClientID); + + /** + * Removes the client from the list of queue listeners. A reply is created + * and sent to the client to indicate the success of the removal. + * + * The reply is sent for all cases, with the status field set to either + * -1 or NS_OK. + * + * @returns NS_OK on success + * @returns -1 if client is not attached to this queue + * @returns TM_SUCCESS_DELETE_QUEUE if there are no more listeners, + * instructing the Transaction Mangaer to delete the queue. + */ + PRInt32 DetachClient(PRUint32 aClientID); + + /** + * Removes all the transactions being held in the queue. + * A reply is created and sent to the client to indicate the + * completion of the operation. + */ + void FlushQueue(PRUint32 aClientID); + + /** + * Places the transaction passed in on the queue. Takes ownership of the + * transaction, deletes it in the destructor. A reply is created and + * sent to the client to indicate the success of the posting of the + * transaction. + * + * The reply is sent for all cases, with the status field containing the + * return value. + * + * @returns >= 0 if the message was posted properly. + * @returns -1 if the client posting is not attached to this queue, + * if the transaction has been posted to the wrong queue or + * if an error occured when trying to add the post to the + * internal storage. + */ + PRInt32 PostTransaction(tmTransaction *aTrans); + + // Accessors + + /** + * @returns the ID of the queue + */ + PRUint32 GetID() const { return mID; } + + /** + * @returns the name of the queue + */ + const char* GetName() const { return mName; } + +protected: + + /** + * Helper method to determine if the client has already attached. + * + * @returns PR_TRUE if the client is attached to the queue. + * @returns PR_FALSE if the client is not attached to the queue. + */ + PRBool IsAttached(PRUint32 aClientID); + + //////////////////////////////////////////////////////////////////////////// + // Protected Member Variables + + // storage + tmVector mTransactions; // transactions that have been posted + tmVector mListeners; // programs listening to this queue + + // bookkeeping + PRUint32 mID; // a number linked to the name in the mTM + char *mName; // format: [namespace][domainname(ie prefs)] + tmTransactionManager *mTM; // the container that holds the queue + +}; + +#endif diff --git a/src/libs/xpcom18a4/ipc/ipcd/extensions/transmngr/module/tmTransactionManager.cpp b/src/libs/xpcom18a4/ipc/ipcd/extensions/transmngr/module/tmTransactionManager.cpp new file mode 100644 index 00000000..9d6cc98e --- /dev/null +++ b/src/libs/xpcom18a4/ipc/ipcd/extensions/transmngr/module/tmTransactionManager.cpp @@ -0,0 +1,162 @@ +/* ***** 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 Transaction Manager. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corp. + * Portions created by the Initial Developer are Copyright (C) 2003 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * John Gaunt <jgaunt@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 "plstr.h" +#include <stdlib.h> +#include "tmQueue.h" +#include "tmTransactionManager.h" +#include "tmTransaction.h" +#include "tmUtils.h" + +/////////////////////////////////////////////////////////////////////////////// +// Constructors & Destructor & Initializer + +tmTransactionManager::~tmTransactionManager() { + + PRUint32 size = mQueues.Size(); + tmQueue *queue = nsnull; + for (PRUint32 index = 0; index < size; index++) { + queue = (tmQueue *)mQueues[index]; + if (queue) { + delete queue; + } + } +} + +PRInt32 +tmTransactionManager::Init() { + return mQueues.Init(); +} + +/////////////////////////////////////////////////////////////////////////////// +// public transaction module methods + +void +tmTransactionManager::HandleTransaction(tmTransaction *aTrans) { + + PRUint32 action = aTrans->GetAction(); + PRUint32 ownerID = aTrans->GetOwnerID(); + tmQueue *queue = nsnull; + + // get the right queue -- attaches do it differently + if (action == TM_ATTACH) { + const char *name = (char*) aTrans->GetMessage(); // is qName for Attaches + queue = GetQueue(name); + if (!queue) { + PRInt32 index = AddQueue(name); + if (index >= 0) + queue = GetQueue(index); // GetQueue may return nsnull + } + } + else // all other trans should have a valid queue ID already + queue = GetQueue(aTrans->GetQueueID()); + + if (queue) { + // All possible actions should have a case, default is not valid + // delete trans when done with them, let the queue own the trans + // that are posted to them. + PRInt32 result = 0; + switch (action) { + case TM_ATTACH: + queue->AttachClient(ownerID); + break; + case TM_POST: + result = queue->PostTransaction(aTrans); + if (result >= 0) // post failed, aTrans cached in a tmQueue + return; + break; + case TM_FLUSH: + queue->FlushQueue(ownerID); + break; + case TM_DETACH: + if (queue->DetachClient(ownerID) == TM_SUCCESS_DELETE_QUEUE) { + // the last client has been removed, remove the queue + RemoveQueue(aTrans->GetQueueID()); // this _could_ be out of bounds + } + break; + default: + PR_NOT_REACHED("bad action in the transaction"); + } + } + delete aTrans; +} + +/////////////////////////////////////////////////////////////////////////////// +// Protected member functions + +// +// Queue Handling +// + +tmQueue* +tmTransactionManager::GetQueue(const char *aQueueName) { + + PRUint32 size = mQueues.Size(); + tmQueue *queue = nsnull; + for (PRUint32 index = 0; index < size; index++) { + queue = (tmQueue*) mQueues[index]; + if (queue && strcmp(queue->GetName(), aQueueName) == 0) + return queue; + } + return nsnull; +} + +// if successful the nsresult contains the index of the added queue +PRInt32 +tmTransactionManager::AddQueue(const char *aQueueName) { + + tmQueue* queue = new tmQueue(); + if (!queue) + return -1; + PRInt32 index = mQueues.Append(queue); + if (index < 0) + delete queue; + else + queue->Init(aQueueName, index, this); + return index; +} + +void +tmTransactionManager::RemoveQueue(PRUint32 aQueueID) { + PR_ASSERT(aQueueID <= mQueues.Size()); + + tmQueue *queue = (tmQueue*)mQueues[aQueueID]; + if (queue) { + mQueues.RemoveAt(aQueueID); + delete queue; + } +} diff --git a/src/libs/xpcom18a4/ipc/ipcd/extensions/transmngr/module/tmTransactionManager.h b/src/libs/xpcom18a4/ipc/ipcd/extensions/transmngr/module/tmTransactionManager.h new file mode 100644 index 00000000..a03ba528 --- /dev/null +++ b/src/libs/xpcom18a4/ipc/ipcd/extensions/transmngr/module/tmTransactionManager.h @@ -0,0 +1,147 @@ +/* ***** 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 Transaction Manager. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corp. + * Portions created by the Initial Developer are Copyright (C) 2003 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * John Gaunt <jgaunt@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 ***** */ + +#ifndef _tmTransactionManager_H_ +#define _tmTransactionManager_H_ + +#include "plhash.h" +#include "tmUtils.h" +#include "tmVector.h" +#include "tmIPCModule.h" + +// forward declarations +class tmQueue; +class tmClient; +class tmTransaction; + +/** + * This class manages the flow of messages from the IPC daemon (coming to + * it through the tmIPCModule) that ultimately come from a Transaction + * Service (TS) in a mozilla based client somewhere. The message is + * delivered to the proper queue, where it is dealt with. + * + * New queues get created here as clients request them. + */ +class tmTransactionManager +{ + +public: + + //////////////////////////////////////////////////////////////////////////// + // Constructor(s) & Destructor & Initializer + + /** + * reclaim the memory allcoated during initialization + */ + virtual ~tmTransactionManager(); + + /** + * Set up the storage of the queues - initialize the vector + * + * @returns NS_OK if successful + * @returns -1 if initialization fails + */ + PRInt32 Init(); + + //////////////////////////////////////////////////////////////////////////// + // Public Member Functions + + /** + * Called from the tmIPCModule. Decide where to send the message and + * dispatch it. + */ + void HandleTransaction(tmTransaction *aTrans); + + /** + * Called by the queues when they need to get a message back out to a + * client. + */ + void SendTransaction(PRUint32 aDestClientIPCID, tmTransaction *aTrans) { + PR_ASSERT(aTrans); + tmIPCModule::SendMsg(aDestClientIPCID, aTrans); + } + +protected: + + //////////////////////////////////////////////////////////////////////////// + // Protected Member Functions + + // Queue management + + /** + * @returns the queue indexed by the ID passed in, which could be nsnull + */ + tmQueue* GetQueue(PRUint32 aQueueID) { + return (tmQueue*) mQueues[aQueueID]; + } + + /** + * @returns the queue with the name passed in + * @returns nsnull if there is no queue with that name + */ + tmQueue* GetQueue(const char *aQueueName); + + /** + * If all is successful a new queue with the name provided will be created, + * and added to the collection of queues. It will be initialized and ready + * to have transactions added. + * + * This doesn't check for the existance of a queue with this name. IF + * there is already a queue with this name then you will + * get that when using GetQueue(qName) and never get the new queue + * created here. A call to GetQueue(qID) will be able to get at the new + * queue, however you had better cache the ID. + * + * @returns -1 if the queue can't be created, or is not added + * @returns >= 0 if the queue was added successfully + */ + PRInt32 AddQueue(const char *aQueueType); + + /** + */ + void RemoveQueue(PRUint32 aQueueID); + + //////////////////////////////////////////////////////////////////////////// + // Protected Member Variables + + tmVector mQueues; + +private: + +}; + +#endif diff --git a/src/libs/xpcom18a4/ipc/ipcd/extensions/transmngr/public/.cvsignore b/src/libs/xpcom18a4/ipc/ipcd/extensions/transmngr/public/.cvsignore new file mode 100644 index 00000000..f3c7a7c5 --- /dev/null +++ b/src/libs/xpcom18a4/ipc/ipcd/extensions/transmngr/public/.cvsignore @@ -0,0 +1 @@ +Makefile diff --git a/src/libs/xpcom18a4/ipc/ipcd/extensions/transmngr/public/Makefile.in b/src/libs/xpcom18a4/ipc/ipcd/extensions/transmngr/public/Makefile.in new file mode 100644 index 00000000..65b126e9 --- /dev/null +++ b/src/libs/xpcom18a4/ipc/ipcd/extensions/transmngr/public/Makefile.in @@ -0,0 +1,53 @@ +# ***** 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 Transaction Manager. +# +# The Initial Developer of the Original Code is +# Netscape Communications Corp. +# Portions created by the Initial Developer are Copyright (C) 2003 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# John Gaunt <jgaunt@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 = ipcd +XPIDL_MODULE = ipcd_transmngr + +XPIDLSRCS = \ + ipcITransactionService.idl \ + ipcITransactionObserver.idl \ + $(NULL) + +include $(topsrcdir)/config/rules.mk diff --git a/src/libs/xpcom18a4/ipc/ipcd/extensions/transmngr/public/ipcITransactionObserver.idl b/src/libs/xpcom18a4/ipc/ipcd/extensions/transmngr/public/ipcITransactionObserver.idl new file mode 100644 index 00000000..c7df965d --- /dev/null +++ b/src/libs/xpcom18a4/ipc/ipcd/extensions/transmngr/public/ipcITransactionObserver.idl @@ -0,0 +1,100 @@ +/* ***** 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 Transaction Manager. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corp. + * Portions created by the Initial Developer are Copyright (C) 2003 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * John Gaunt <jgaunt@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" + +[scriptable, uuid(656c0a6a-5cb3-45ec-8cb6-e7678897f937)] +interface ipcITransactionObserver : nsISupports +{ + /** + * This gets called when a Transaction has been sent from the + * TransactionManager. If the data passed in needs to be stored + * for longer than the life of the method the observer needs + * to make a copy. + * + * @param aQueueID + * The queue from which the transaction originated + * + * @param aData + * The data to be sent. + * + * @param aDataLen + * The length of the data argument + */ + void onTransactionAvailable(in unsigned long aQueueID, + [array, const, size_is(aDataLen)] + in octet aData, + in unsigned long aDataLen); + + /** + * Called after an application sends an Attach message to the + * Transaction Manager. + * + * @param aQueueID + * The client has been attached to the queue with this ID + * + * @param aStatus + * The status of the operation, as defined in tmUtils.h + */ + void onAttachReply(in unsigned long aQueueID, in unsigned long aStatus); + + /** + * Called after an application sends a Detach message. Indicates + * to the client that no more messages will be coming from the + * the TM to this client. Also, no messages posted from this + * client to the indicated queue will be accepted. + * + * @param aQueueID + * The client has been detached from the queue with this ID + * + * @param aStatus + * The status of the operation, as defined in tmUtils.h + */ + void onDetachReply(in unsigned long aQueueID, in unsigned long aStatus); + + /** + * The reply from the TM indicating all messages have been removed + * from the queue indicated. + * + * @param aQueueID + * The queue that has been flushed. + * + * @param aStatus + * The status of the operation, as defined in tmUtils.h + */ + void onFlushReply(in unsigned long aQueueID, in unsigned long aStatus); +}; diff --git a/src/libs/xpcom18a4/ipc/ipcd/extensions/transmngr/public/ipcITransactionService.idl b/src/libs/xpcom18a4/ipc/ipcd/extensions/transmngr/public/ipcITransactionService.idl new file mode 100644 index 00000000..0bcbc453 --- /dev/null +++ b/src/libs/xpcom18a4/ipc/ipcd/extensions/transmngr/public/ipcITransactionService.idl @@ -0,0 +1,239 @@ +/* ***** 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 Transaction Manager. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corp. + * Portions created by the Initial Developer are Copyright (C) 2003 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * John Gaunt <jgaunt@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 ***** */ + +// from tmTransactionManager.h +// +// XXX documentation needs work +////////////////////////////////////////////////////////////////////////////// +// Overview of TransactionManager IPC Module +// +// Classes: +// tmIPCModule - From the tmTransactionManager's point of view, this +// is a proxy for the IPC daemon itself. The reverse is true +// from the daemon's point of view. This is an interface for the +// Transaction system to work with the IPC daemon as its transport +// layer. +// tmTransactionManager (TM) - Manages the different queues. Maintains +// the queues neccessary for different clients. Receives messages +// from the tmIPCModule and passes message to the IPC daemon through +// the tmIPCModule. +// tmQueue - this class manages the transactions for the different areas +// of the profiles being shared. Broken down by functional area there +// will be a queue for prefs, cookies etc, but not for profileA and +// profileB, and not for pref_delete, pref_create, pref_change etc... +// tmTransaction - the actual transaction being shared with the different +// tmClients. It contains the type of transaction, which will equate with +// a type of queue in existance, the owner of the transaction (an IPC daemon ID) +// and the actual text message to be shared. +// +////////////////////////////////////////////////////////////////////////////// + +/// XXX some docs that need to be put somewhere: +// +// from tmqueue.cpp +// Docs - note that the status of the TM_ATTACH_REPLY is only for checking +// for TM_ERROR_FAILURE. Other numbers have no importance +// success of the status means the NS_ERROR_GET_CODE(status) will +// yield the index of the listener. +// +// move to documentation page - from tmqueue.h +// +// a queue is specific to profile +// +// UUID going out from the module is a handler in the client +// (will go to the XPCOM service impling that UUID) +// -- does it make sense to have different UUIDs for cookies/prefs/etc +// + +#include "nsISupports.idl" + +interface ipcITransactionObserver; + +[scriptable, uuid(15561efb-8c58-4a47-813a-fa91cf730895)] +interface ipcITransactionService : nsISupports +{ + /** + * Connects the application to the transaction manager, defines the + * namespace and initializes internal storage + * + * @param aNamespace + * A string defining the scope of the transaction domains. It is + * used internally to seperate process listening to the same domain + * (ie. preferences) but for two different namespaces (ie. profile1 vs + * profile2). + * + * @returns NS_OK if all memory allocated properly and the IPC service was + * reached and attached to successfully. + * + * @returns an NS_ERROR_<foo> code specific to the failure otherwise + */ + void init(in ACString aNamespace); + + /** + * Links the observer passed in with the domain specified. This will allow + * the observer to post transactions dealing with this domain as well as + * receive transactions posted by other applications observing this + * domain. + * + * Return codes for this method confer information about the success of + * this call, not of the actual attaching of the observer to the domain. + * (except the TM_ERROR code - which means the observer can not attach) + * If the attach is successful the observer will have its OnAttachReply + * method called before this method returns. + * + * Note: This call is synchronous and will not return until the call to + * OnAttachReply is made. + * + * @param aDomainName + * the name of the domain, in the current namespace, to listen for + * transactions from. i.e. cookies + * + * @param aObserver + * this will be used to notify the application when transactions + * and messages come in. + * + * @param aLockingCall + * Have the Transaction Sevice acquire a lock based on the domain + * before attaching. This should be used when persistant storage + * is being used to prevent data corruption. + * + * @returns NS_OK if the attach message was sent to the Transaction Manager. + * + * @returns an NS_ERROR_<foo> code specific to the failure otherwise + * + * @returns TM_ERROR_QUEUE_EXISTS if the queue already exists which means + * someone has already attached to it. + */ + void attach(in ACString aDomainName, + in ipcITransactionObserver aObserver, + in PRBool aLockingCall); + + /** + * Sends a detach message to the Transaction Manager to unlink the observer + * associated with the domain passed in. + * + * As in attach, return codes do not indicate success of detachment. The + * observer will have it's OnDetach method called if it is successfully + * detached. + * + * Note: This call is an asynchronous call. + * + * @param aDomainName + * the domain, in the current namespace, from which the client + * should be removed. + * + * @returns NS_OK if the detach message is sent to the Transaction Manager + * + * @returns NS_ERROR_FAILURE is something goes wrong + * + * @returns NS_ERRROR_UNEXPECTD if the domain does not have an observer + * attached + */ + void detach(in ACString aDomainName); + + /** + * Sends a flush message to the Transaction Manager to remove all + * transactions for the domain. After this call there will be no + * transactions in the Transaction Manager for the namespace/domain + * pairing. It is up to the application to coordinate the flushing + * of the Transaction Manager with the writing of data to files, + * if needed. + * + * Note: This call is synchronous and will not return until the call to + * OnFlushReply is made. + * + * @param aDomainName + * The domain, in the current namespace, to flush. + * + * @param aLockingCall + * Have the Transaction Sevice acquire a lock based on the domain + * before flushing. This should be used when persistant storage + * is being used to prevent data corruption. + * + * @returns NS_OK if the flush message is sent to the Transaction Manager + * + * @returns NS_ERROR_FAILURE is something goes wrong + * + * @returns NS_ERRROR_UNEXPECTD if the domain does not have an observer + * attached + */ + void flush(in ACString aDomainName, in PRBool aLockingCall); + + /** + * Send the data to the Transaction Manager to be broadcast to any + * applications that have registered as observers of this particular + * namespace/domain pairing. + * + * If this domain is not being observed (attach has not been called for + * this domain) the message is queued until the attach is made and then + * the message is sent to the Transaction Manager with the proper domain + * information. + * + * XXXjg - this may not be neccessary with the synch attach call. + * + * Note: This call is an asynchronous call. + * + * @param aDomainName + * the domain, in the current namespace, to which the data will be + * sent. + * + * @param aData + * The actual data to be sent. + * + * @param aDataLen + * The length of the data argument + */ + void postTransaction(in ACString aDomainName, + [array, const, size_is(aDataLen)] + in octet aData, + in unsigned long aDataLen); +}; + +%{C++ +// singleton implementing ipcITransactionService +#define IPC_TRANSACTIONSERVICE_CLASSNAME \ + "tmTransactionService" +#define IPC_TRANSACTIONSERVICE_CONTRACTID \ + "@mozilla.org/ipc/transaction-service;1" +#define IPC_TRANSACTIONSERVICE_CID \ +{ /* 1403adf4-94d1-4c67-a8ae-d9f86972d378 */ \ + 0x1403adf4, \ + 0x94d1, \ + 0x4c67, \ + {0xa8, 0xae, 0xd9, 0xf8, 0x69, 0x72, 0xd3, 0x78} \ +} +%} diff --git a/src/libs/xpcom18a4/ipc/ipcd/extensions/transmngr/src/.cvsignore b/src/libs/xpcom18a4/ipc/ipcd/extensions/transmngr/src/.cvsignore new file mode 100644 index 00000000..7726b497 --- /dev/null +++ b/src/libs/xpcom18a4/ipc/ipcd/extensions/transmngr/src/.cvsignore @@ -0,0 +1,4 @@ +Makefile +tmTransactionService.obj +transmgr_s.lib +transmgr_s.pdb
\ No newline at end of file diff --git a/src/libs/xpcom18a4/ipc/ipcd/extensions/transmngr/src/Makefile.in b/src/libs/xpcom18a4/ipc/ipcd/extensions/transmngr/src/Makefile.in new file mode 100644 index 00000000..6510e489 --- /dev/null +++ b/src/libs/xpcom18a4/ipc/ipcd/extensions/transmngr/src/Makefile.in @@ -0,0 +1,64 @@ +# ***** 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 Transaction Manager. +# +# The Initial Developer of the Original Code is +# Netscape Communications Corp. +# Portions created by the Initial Developer are Copyright (C) 2003 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# John Gaunt <jgaunt@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 = ipcd +LIBRARY_NAME = transmgr_s +FORCE_STATIC_LIB = 1 +MODULE_NAME = ipcd + +REQUIRES = string \ + xpcom \ + $(NULL) + +CPPSRCS = \ + tmTransactionService.cpp \ + $(NULL) + +include $(topsrcdir)/config/config.mk + +LOCAL_INCLUDES = \ + -I$(srcdir)/../common \ + $(NULL) + +include $(topsrcdir)/config/rules.mk diff --git a/src/libs/xpcom18a4/ipc/ipcd/extensions/transmngr/src/tmTransactionService.cpp b/src/libs/xpcom18a4/ipc/ipcd/extensions/transmngr/src/tmTransactionService.cpp new file mode 100644 index 00000000..e53d6aa7 --- /dev/null +++ b/src/libs/xpcom18a4/ipc/ipcd/extensions/transmngr/src/tmTransactionService.cpp @@ -0,0 +1,504 @@ +/* ***** 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 Transaction Manager. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corp. + * Portions created by the Initial Developer are Copyright (C) 2003 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * John Gaunt <jgaunt@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 "nsCOMPtr.h" +#include "nsIServiceManager.h" +#include "nsReadableUtils.h" +#include "plstr.h" +#include "ipcITransactionObserver.h" +#include "tmTransaction.h" +#include "tmTransactionService.h" +#include "tmUtils.h" + +static const nsID kTransModuleID = TRANSACTION_MODULE_ID; + +struct tm_waiting_msg { + tmTransaction trans; // a transaction waiting to be sent to a queue + char* domainName; // the short queue name + + ~tm_waiting_msg(); +}; + +tm_waiting_msg::~tm_waiting_msg() { + if (domainName) + PL_strfree(domainName); +} + +struct tm_queue_mapping { + PRInt32 queueID; // the ID in the TM + char* domainName; // used by the consumers of this service + char* joinedQueueName; // used by the service -- namespace + domain name + + ~tm_queue_mapping(); +}; + +tm_queue_mapping::~tm_queue_mapping() { + if (domainName) + PL_strfree(domainName); + if (joinedQueueName) + PL_strfree(joinedQueueName); +} + +////////////////////////////////////////////////////////////////////////////// +// Constructor and Destructor + +tmTransactionService::~tmTransactionService() { + + // just destroy this, it contains 2 pointers it doesn't own. + if (mObservers) + PL_HashTableDestroy(mObservers); + + PRUint32 index = 0; + PRUint32 size = mWaitingMessages.Size(); + tm_waiting_msg *msg = nsnull; + for ( ; index < size; index ++) { + msg = (tm_waiting_msg*) mWaitingMessages[index]; + delete msg; + } + + size = mQueueMaps.Size(); + tm_queue_mapping *qmap = nsnull; + for (index = 0; index < size; index++) { + qmap = (tm_queue_mapping*) mQueueMaps[index]; + if (qmap) + delete qmap; + } +} + +////////////////////////////////////////////////////////////////////////////// +// ISupports + +NS_IMPL_ISUPPORTS2(tmTransactionService, + ipcITransactionService, + ipcIMessageObserver) + +////////////////////////////////////////////////////////////////////////////// +// ipcITransactionService + +NS_IMETHODIMP +tmTransactionService::Init(const nsACString & aNamespace) { + + nsresult rv; + + rv = IPC_DefineTarget(kTransModuleID, this, PR_TRUE); + if (NS_FAILED(rv)) + return rv; + + // get the lock service + lockService = do_GetService("@mozilla.org/ipc/lock-service;1"); + if (!lockService) + return NS_ERROR_FAILURE; + + // create some internal storage + mObservers = PL_NewHashTable(20, + PL_HashString, + PL_CompareStrings, + PL_CompareValues, 0, 0); + if (!mObservers) + return NS_ERROR_FAILURE; + + // init some internal storage + mQueueMaps.Init(); + mWaitingMessages.Init(); + + // store the namespace + mNamespace.Assign(aNamespace); + return NS_OK; +} + +NS_IMETHODIMP +tmTransactionService::Attach(const nsACString & aDomainName, + ipcITransactionObserver *aObserver, + PRBool aLockingCall) { + + // if the queue already exists, then someone else is attached to it. must + // return an error here. Only one module attached to a queue per app. + if (GetQueueID(aDomainName) != TM_NO_ID) + return TM_ERROR_QUEUE_EXISTS; + + // create the full queue name: namespace + queue + nsCString jQName; + jQName.Assign(mNamespace); + jQName.Append(aDomainName); + + // this char* has two homes, make sure it gets PL_free()ed properly + char* joinedQueueName = ToNewCString(jQName); + if (!joinedQueueName) + return NS_ERROR_OUT_OF_MEMORY; + + // link the observer to the joinedqueuename. home #1 for joinedQueueName + // these currently don't get removed until the destructor on this is called. + PL_HashTableAdd(mObservers, joinedQueueName, aObserver); + + // store the domainName and JoinedQueueName, create a place to store the ID + tm_queue_mapping *qm = new tm_queue_mapping(); + if (!qm) + return NS_ERROR_OUT_OF_MEMORY; + qm->queueID = TM_NO_ID; // initially no ID for the queue + qm->joinedQueueName = joinedQueueName; // home #2, owner of joinedQueueName + qm->domainName = ToNewCString(aDomainName); + if (!qm->domainName) { + PL_HashTableRemove(mObservers, joinedQueueName); + delete qm; + return NS_ERROR_OUT_OF_MEMORY; + } + mQueueMaps.Append(qm); + + nsresult rv = NS_ERROR_FAILURE; + tmTransaction trans; + + // acquire a lock if neccessary + if (aLockingCall) + lockService->AcquireLock(joinedQueueName, PR_TRUE); + // XXX need to handle lock failures + + if (NS_SUCCEEDED(trans.Init(0, // no IPC client + TM_NO_ID, // qID gets returned to us + TM_ATTACH, // action + NS_OK, // default status + (PRUint8 *)joinedQueueName, // qName gets copied + PL_strlen(joinedQueueName)+1))) { // message length + // send the attach msg + SendMessage(&trans, PR_TRUE); // synchronous + rv = NS_OK; + } + + // drop the lock if neccessary + if (aLockingCall) + lockService->ReleaseLock(joinedQueueName); + + return rv; +} + +// actual removal of the observer takes place when we get the detach reply +NS_IMETHODIMP +tmTransactionService::Detach(const nsACString & aDomainName) { + + // asynchronous detach + return SendDetachOrFlush(GetQueueID(aDomainName), TM_DETACH, PR_FALSE); + +} + +NS_IMETHODIMP +tmTransactionService::Flush(const nsACString & aDomainName, + PRBool aLockingCall) { + // acquire a lock if neccessary + if (aLockingCall) + lockService->AcquireLock(GetJoinedQueueName(aDomainName), PR_TRUE); + + // synchronous flush + nsresult rv = SendDetachOrFlush(GetQueueID(aDomainName), TM_FLUSH, PR_TRUE); + + // drop the lock if neccessary + if (aLockingCall) + lockService->ReleaseLock(GetJoinedQueueName(aDomainName)); + + return rv; + +} + +NS_IMETHODIMP +tmTransactionService::PostTransaction(const nsACString & aDomainName, + const PRUint8 *aData, + PRUint32 aDataLen) { + + tmTransaction trans; + if (NS_SUCCEEDED(trans.Init(0, // no IPC client + GetQueueID(aDomainName), // qID returned to us + TM_POST, // action + NS_OK, // default status + aData, // message data + aDataLen))) { // message length + if (trans.GetQueueID() == TM_NO_ID) { + // stack it and pack it + tm_waiting_msg *msg = new tm_waiting_msg(); + if (!msg) + return NS_ERROR_OUT_OF_MEMORY; + msg->trans = trans; + msg->domainName = ToNewCString(aDomainName); + if (!msg->domainName) { + delete msg; + return NS_ERROR_OUT_OF_MEMORY; + } + mWaitingMessages.Append(msg); + } + else { + // send it + SendMessage(&trans, PR_FALSE); + } + return NS_OK; + } + return NS_ERROR_FAILURE; +} + +////////////////////////////////////////////////////////////////////////////// +// ipcIMessageObserver + +NS_IMETHODIMP +tmTransactionService::OnMessageAvailable(const PRUint32 aSenderID, + const nsID & aTarget, + const PRUint8 *aData, + PRUint32 aDataLength) { + + nsresult rv = NS_ERROR_OUT_OF_MEMORY; // prime the return value + + tmTransaction *trans = new tmTransaction(); + if (trans) { + rv = trans->Init(0, // no IPC client ID + TM_INVALID_ID, // in aData + TM_INVALID_ID, // in aData + TM_INVALID_ID, // in aData + aData, // message data + aDataLength); // message length + + if (NS_SUCCEEDED(rv)) { + switch(trans->GetAction()) { + case TM_ATTACH_REPLY: + OnAttachReply(trans); + break; + case TM_POST_REPLY: + // OnPostReply() would be called here + // isn't neccessary at the current time 2/19/03 + break; + case TM_DETACH_REPLY: + OnDetachReply(trans); + break; + case TM_FLUSH_REPLY: + OnFlushReply(trans); + break; + case TM_POST: + OnPost(trans); + break; + default: // error, should not happen + NS_NOTREACHED("Recieved a TM reply outside of mapped messages"); + break; + } + } + delete trans; + } + return rv; +} + +////////////////////////////////////////////////////////////////////////////// +// Protected Member Functions + +void +tmTransactionService::SendMessage(tmTransaction *aTrans, PRBool aSync) { + + NS_ASSERTION(aTrans, "tmTransactionService::SendMessage called with null transaction"); + + IPC_SendMessage(0, kTransModuleID, + aTrans->GetRawMessage(), + aTrans->GetRawMessageLength()); + if (aSync) + IPC_WaitMessage(0, kTransModuleID, nsnull, nsnull, PR_INTERVAL_NO_TIMEOUT); +} + +void +tmTransactionService::OnAttachReply(tmTransaction *aTrans) { + + // if we attached, store the queue's ID + if (aTrans->GetStatus() >= 0) { + + PRUint32 size = mQueueMaps.Size(); + tm_queue_mapping *qmap = nsnull; + for (PRUint32 index = 0; index < size; index++) { + qmap = (tm_queue_mapping*) mQueueMaps[index]; + if (qmap && + PL_strcmp(qmap->joinedQueueName, (char*) aTrans->GetMessage()) == 0) { + + // set the ID in the mapping + qmap->queueID = aTrans->GetQueueID(); + // send any stored messges to the queue + DispatchStoredMessages(qmap); + } + } + } + + // notify the observer we have attached (or didn't) + ipcITransactionObserver *observer = + (ipcITransactionObserver *)PL_HashTableLookup(mObservers, + (char*)aTrans->GetMessage()); + if (observer) + observer->OnAttachReply(aTrans->GetQueueID(), aTrans->GetStatus()); +} + +void +tmTransactionService::OnDetachReply(tmTransaction *aTrans) { + + tm_queue_mapping *qmap = GetQueueMap(aTrans->GetQueueID()); + + // get the observer before we release the hashtable entry + ipcITransactionObserver *observer = + (ipcITransactionObserver *)PL_HashTableLookup(mObservers, + qmap->joinedQueueName); + + // if it was removed, clean up + if (aTrans->GetStatus() >= 0) { + + // remove the link between observer and queue + PL_HashTableRemove(mObservers, qmap->joinedQueueName); + + // remove the mapping of queue names and id + mQueueMaps.Remove(qmap); + delete qmap; + } + + + // notify the observer -- could be didn't detach + if (observer) + observer->OnDetachReply(aTrans->GetQueueID(), aTrans->GetStatus()); +} + +void +tmTransactionService::OnFlushReply(tmTransaction *aTrans) { + + ipcITransactionObserver *observer = + (ipcITransactionObserver *)PL_HashTableLookup(mObservers, + GetJoinedQueueName(aTrans->GetQueueID())); + if (observer) + observer->OnFlushReply(aTrans->GetQueueID(), aTrans->GetStatus()); +} + +void +tmTransactionService::OnPost(tmTransaction *aTrans) { + + ipcITransactionObserver *observer = + (ipcITransactionObserver*) PL_HashTableLookup(mObservers, + GetJoinedQueueName(aTrans->GetQueueID())); + if (observer) + observer->OnTransactionAvailable(aTrans->GetQueueID(), + aTrans->GetMessage(), + aTrans->GetMessageLength()); +} + +void +tmTransactionService::DispatchStoredMessages(tm_queue_mapping *aQMapping) { + + PRUint32 size = mWaitingMessages.Size(); + tm_waiting_msg *msg = nsnull; + for (PRUint32 index = 0; index < size; index ++) { + msg = (tm_waiting_msg*) mWaitingMessages[index]; + // if the message is waiting on the queue passed in + if (msg && strcmp(aQMapping->domainName, msg->domainName) == 0) { + + // found a match, send it and remove + msg->trans.SetQueueID(aQMapping->queueID); + SendMessage(&(msg->trans), PR_FALSE); + + // clean up + mWaitingMessages.Remove(msg); + delete msg; + } + } +} + +// searches against the short queue name +PRInt32 +tmTransactionService::GetQueueID(const nsACString & aDomainName) { + + PRUint32 size = mQueueMaps.Size(); + tm_queue_mapping *qmap = nsnull; + for (PRUint32 index = 0; index < size; index++) { + qmap = (tm_queue_mapping*) mQueueMaps[index]; + if (qmap && aDomainName.Equals(qmap->domainName)) + return qmap->queueID; + } + return TM_NO_ID; +} + +char* +tmTransactionService::GetJoinedQueueName(PRUint32 aQueueID) { + + PRUint32 size = mQueueMaps.Size(); + tm_queue_mapping *qmap = nsnull; + for (PRUint32 index = 0; index < size; index++) { + qmap = (tm_queue_mapping*) mQueueMaps[index]; + if (qmap && qmap->queueID == aQueueID) + return qmap->joinedQueueName; + } + return nsnull; +} + +char* +tmTransactionService::GetJoinedQueueName(const nsACString & aDomainName) { + + PRUint32 size = mQueueMaps.Size(); + tm_queue_mapping *qmap = nsnull; + for (PRUint32 index = 0; index < size; index++) { + qmap = (tm_queue_mapping*) mQueueMaps[index]; + if (qmap && aDomainName.Equals(qmap->domainName)) + return qmap->joinedQueueName; + } + return nsnull; +} + +tm_queue_mapping* +tmTransactionService::GetQueueMap(PRUint32 aQueueID) { + + PRUint32 size = mQueueMaps.Size(); + tm_queue_mapping *qmap = nsnull; + for (PRUint32 index = 0; index < size; index++) { + qmap = (tm_queue_mapping*) mQueueMaps[index]; + if (qmap && qmap->queueID == aQueueID) + return qmap; + } + return nsnull; +} + +nsresult +tmTransactionService::SendDetachOrFlush(PRUint32 aQueueID, + PRUint32 aAction, + PRBool aSync) { + + // if the queue isn't attached to, just return + if (aQueueID == TM_NO_ID) + return NS_ERROR_UNEXPECTED; + + tmTransaction trans; + if (NS_SUCCEEDED(trans.Init(0, // no IPC client + aQueueID, // qID to detach from + aAction, // action + NS_OK, // default status + nsnull, // no message + 0))) { // no message + // send it + SendMessage(&trans, aSync); + return NS_OK; + } + return NS_ERROR_FAILURE; +} diff --git a/src/libs/xpcom18a4/ipc/ipcd/extensions/transmngr/src/tmTransactionService.h b/src/libs/xpcom18a4/ipc/ipcd/extensions/transmngr/src/tmTransactionService.h new file mode 100644 index 00000000..cd9310fe --- /dev/null +++ b/src/libs/xpcom18a4/ipc/ipcd/extensions/transmngr/src/tmTransactionService.h @@ -0,0 +1,196 @@ +/* ***** 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 Transaction Manager. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corp. + * Portions created by the Initial Developer are Copyright (C) 2003 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * John Gaunt <jgaunt@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 ***** */ + +#ifndef _tmTransactionService_H_ +#define _tmTransactionService_H_ + +#include "ipcdclient.h" +#include "ipcILockService.h" +#include "ipcIMessageObserver.h" +#include "ipcITransactionService.h" +#include "nsString.h" +#include "nsVoidArray.h" +#include "plhash.h" +#include "tmTransaction.h" +#include "tmVector.h" + +struct tm_queue_mapping; + +/** + * The tmTransactionService shares packets of information + * (transactions) with other Gecko based applications interested in the same + * namespace and domain. An application registers with the Transaction Service + * for a particular namespace and domain and then can post transactions to the + * service and receive transactions from the service. + * + * For applications using the Transaction Service to share changes in state that + * get reflected in files on disk there are certain pattersn to follow to ensure + * data loss does not occur. + * + * Startup: XXX docs needed + * + * Shutdown/writing to disk: XXX docs needed + * + * + */ +class tmTransactionService : public ipcITransactionService, + public ipcIMessageObserver +{ + +public: + + //////////////////////////////////////////////////////////////////////////// + // Constructor & Destructor + tmTransactionService() : mObservers(0) {}; + + /** + * Reclaim all the memory allocated: PL_hashtable, tmVectors + */ + virtual ~tmTransactionService(); + + //////////////////////////////////////////////////////////////////////////// + // Interface Declarations + + // for API docs, see the respective *.idl files + NS_DECL_ISUPPORTS + NS_DECL_IPCITRANSACTIONSERVICE + NS_DECL_IPCIMESSAGEOBSERVER + +protected: + + //////////////////////////////////////////////////////////////////////////// + // Protected Member Functions + + /** + * Pulls the raw message out of the transaction and sends it to the IPC + * service to be delivered to the TM. + * + * @param aTrans + * The transaction to send to the TM + * + * @param aSync + * If TRUE, calling thread will be blocked until a reply is + * received. + */ + void SendMessage(tmTransaction *aTrans, PRBool aSync); + + // handlers for reply messages from TransactionManager + + /** + * Pulls the queueID out of the ATTACH_REPLY message and stores it in the + * proper tm_queue_mapping object. Calls DispatchStoredMessages() to make + * sure we send any messages that have been waiting on the ATTACH_REPLY. + * Also calls the OnAttachReply() method for the observer of the queue. + */ + void OnAttachReply(tmTransaction *aTrans); + + /** + * Removes the tm_queue_mapping object and calls the OnDetachReply() method + * on the observer of the queue detached. + */ + void OnDetachReply(tmTransaction *aTrans); + + /** + * Calls the OnFlushReply method of the observer of the queue. + */ + void OnFlushReply(tmTransaction *aTrans); + + /** + * Calls the OnPost method of the observer of the queue. + */ + void OnPost(tmTransaction *aTrans); + + // other helper functions + + /** + * Cycle through the collection of transactions waiting to go out and + * send any that are waiting on an ATTACH_REPLY from the queue + * specified by the tm_queue_mapping passed in. + */ + void DispatchStoredMessages(tm_queue_mapping *aQMapping); + + // helper methods for accessing the void arrays + + /** + * @returns the ID corresponding to the domain name passed in + * @returns TM_NO_ID if the name is not found. + */ + PRInt32 GetQueueID(const nsACString & aDomainName); + + /** + * @returns the joined queue name - namespace + domain + * (prefs, cookies etc) corresponding to the ID passed in. + * @returns nsnull if the ID is not found. + */ + char* GetJoinedQueueName(PRUint32 aQueueID); + + /** + * @returns the joined queue name - namespace + domain + * (prefs, cookies etc) corresponding to the ID passed in. + * @returns nsnull if the ID is not found. + */ + char* GetJoinedQueueName(const nsACString & aDomainName); + + /** + * @returns the tm_queue_mapping object that contains the ID passed in. + * @returns nsnull if the ID is not found. + */ + tm_queue_mapping* GetQueueMap(PRUint32 aQueueID); + + /** + * Helper method for Detach and Flush requests. + */ + nsresult SendDetachOrFlush(PRUint32 aQueueID, + PRUint32 aAction, + PRBool aSync); + + //////////////////////////////////////////////////////////////////////////// + // Protected Member Variables + + nsCString mNamespace; // limit domains to the namespace + PLHashTable *mObservers; // maps qName -> ipcITransactionObserver + + tmVector mQueueMaps; // queue - name - domain mappings + tmVector mWaitingMessages; // messages sent before ATTACH_REPLY + + nsCOMPtr<ipcILockService> lockService; // cache the lock service + +private: + +}; + +#endif diff --git a/src/libs/xpcom18a4/ipc/ipcd/extensions/transmngr/test/.cvsignore b/src/libs/xpcom18a4/ipc/ipcd/extensions/transmngr/test/.cvsignore new file mode 100644 index 00000000..e3c610be --- /dev/null +++ b/src/libs/xpcom18a4/ipc/ipcd/extensions/transmngr/test/.cvsignore @@ -0,0 +1,6 @@ +Makefile +tmModuleTest.exe +tmModuleTest.ilk +tmModuleTest.pdb +tmModuleTest.obj +tmModuleTest diff --git a/src/libs/xpcom18a4/ipc/ipcd/extensions/transmngr/test/Makefile.in b/src/libs/xpcom18a4/ipc/ipcd/extensions/transmngr/test/Makefile.in new file mode 100644 index 00000000..385751a3 --- /dev/null +++ b/src/libs/xpcom18a4/ipc/ipcd/extensions/transmngr/test/Makefile.in @@ -0,0 +1,68 @@ +# ***** 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 Transaction Manager. +# +# The Initial Developer of the Original Code is +# Netscape Communications Corp. +# Portions created by the Initial Developer are Copyright (C) 2003 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# John Gaunt <jgaunt@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 = tm_test + +REQUIRES = ipcd \ + nspr \ + string \ + xpcom \ + $(NULL) + +CPPSRCS = \ + tmModuleTest.cpp \ + $(NULL) + +SIMPLE_PROGRAMS = $(CPPSRCS:.cpp=$(BIN_SUFFIX)) + +include $(topsrcdir)/config/config.mk + +LIBS = \ + $(EXTRA_DSO_LIBS) \ + $(XPCOM_LIBS) \ + $(NSPR_LIBS) \ + $(NULL) + +include $(topsrcdir)/config/rules.mk + diff --git a/src/libs/xpcom18a4/ipc/ipcd/extensions/transmngr/test/tmModuleTest.cpp b/src/libs/xpcom18a4/ipc/ipcd/extensions/transmngr/test/tmModuleTest.cpp new file mode 100644 index 00000000..5d1dccd4 --- /dev/null +++ b/src/libs/xpcom18a4/ipc/ipcd/extensions/transmngr/test/tmModuleTest.cpp @@ -0,0 +1,323 @@ +/* ***** 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 Transaction Manager. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corp. + * Portions created by the Initial Developer are Copyright (C) 2003 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * John Gaunt <jgaunt@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 ***** */ + +// transaction manager includes +#include "ipcITransactionService.h" +#include "ipcITransactionObserver.h" + +// ipc daemon includes +#include "ipcIService.h" + +// core & xpcom ns includes +#include "nsDebug.h" +#include "nsIEventQueueService.h" +#include "nsIServiceManager.h" +#include "nsIComponentRegistrar.h" +#include "nsString.h" + +// nspr includes +#include "prmem.h" +#include "plgetopt.h" +#include "nspr.h" +#include "prlog.h" + +////////////////////////////////////////////////////////////////////////////// +// Testing/Debug/Logging BEGIN + +const int NameSize = 1024; + +/* command line options */ +PRIntn optDebug = 0; +char optMode = 's'; +char *profileName = new char[NameSize]; +char *queueName = new char[NameSize]; + +char *data = new char[NameSize]; +PRUint32 dataLen = 10; // includes the null terminator for "test data" + +// Testing/Debug/Logging END +////////////////////////////////////////////////////////////////////////////// + +#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 ipcITransactionService *gTransServ = nsnull; + +//----------------------------------------------------------------------------- + +class myTransactionObserver : public ipcITransactionObserver +{ +public: + NS_DECL_ISUPPORTS + NS_DECL_IPCITRANSACTIONOBSERVER + + myTransactionObserver() { } +}; + +NS_IMPL_ISUPPORTS1(myTransactionObserver, ipcITransactionObserver) + +NS_IMETHODIMP myTransactionObserver::OnTransactionAvailable(PRUint32 aQueueID, const PRUint8 *aData, PRUint32 aDataLen) +{ + printf("tmModuleTest: myTransactionObserver::OnTransactionAvailable [%s]\n", aData); + return NS_OK; +} + +NS_IMETHODIMP myTransactionObserver::OnAttachReply(PRUint32 aQueueID, PRUint32 aStatus) +{ + printf("tmModuleTest: myTransactionObserver::OnAttachReply [%d]\n", aStatus); + return NS_OK; +} + +NS_IMETHODIMP myTransactionObserver::OnDetachReply(PRUint32 aQueueID, PRUint32 aStatus) +{ + printf("tmModuleTest: myTransactionObserver::OnDetachReply [%d]\n", aStatus); + return NS_OK; +} + +NS_IMETHODIMP myTransactionObserver::OnFlushReply(PRUint32 aQueueID, PRUint32 aStatus) +{ + printf("tmModuleTest: myTransactionObserver::OnFlushReply [%d]\n", aStatus); + return NS_OK; +} + + +//----------------------------------------------------------------------------- + +int main(PRInt32 argc, char *argv[]) +{ + nsresult rv; + + // default string values + strcpy(profileName, "defaultProfile"); + strcpy(queueName, "defaultQueue"); + strcpy(data, "test data"); + + { // scope the command line option gathering (needed for some reason) + + // Get command line options + PLOptStatus os; + PLOptState *opt = PL_CreateOptState(argc, argv, "bdfhlp:q:"); + + while (PL_OPT_EOL != (os = PL_GetNextOpt(opt))) + { + if (PL_OPT_BAD == os) continue; + switch (opt->option) + { + case 'b': /* broadcast a bunch of messages */ + printf("tmModuleTest: broadcaster\n"); + optMode = 'b'; + break; + case 'd': /* debug mode */ + printf("tmModuleTest: debugging baby\n"); + optDebug = 1; + break; + case 'f': /* broadcast and flush */ + printf("tmModuleTest: flusher\n"); + optMode = 'f'; + break; + case 'h': /* broadcast and detach */ + printf("tmModuleTest: hit-n-run\n"); + optMode = 'h'; + break; + case 'l': /* don't broadcast, just listen */ + printf("tmModuleTest: listener\n"); + optMode = 'l'; + break; + case 'p': /* set the profile name */ + strcpy(profileName, opt->value); + printf("tmModuleTest: profilename:%s\n",profileName); + break; + case 'q': /* set the queue name */ + strcpy(queueName, opt->value); + printf("tmModuleTest: queuename:%s\n",queueName); + break; + default: + printf("tmModuleTest: default\n"); + break; + } + } + PL_DestroyOptState(opt); + } // scope the command line option gathering (needed for some reason) + + { // scope the nsCOMPtrs + + printf("tmModuleTest: Starting xpcom\n"); + + // xpcom startup stuff + 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"); + + // Need to make sure the ipc system has been started + printf("tmModuleTest: getting ipc service\n"); + nsCOMPtr<ipcIService> ipcServ(do_GetService("@mozilla.org/ipc/service;1", &rv)); + RETURN_IF_FAILED(rv, "do_GetService(ipcServ)"); + NS_ADDREF(gIpcServ = ipcServ); + + // Get the transaction service + printf("tmModuleTest: getting transaction service\n"); + nsCOMPtr<ipcITransactionService> transServ(do_GetService("@mozilla.org/ipc/transaction-service;1", &rv)); + RETURN_IF_FAILED(rv, "do_GetService(transServ)"); + NS_ADDREF(gTransServ = transServ); + + // transaction specifc startup stuff, done for all cases + + nsCOMPtr<ipcITransactionObserver> observ = new myTransactionObserver(); + + // initialize the transaction service with a specific profile + gTransServ->Init(nsDependentCString(profileName)); + printf("tmModuleTest: using profileName [%s]\n", profileName); + + // attach to the queue in the transaction manager + gTransServ->Attach(nsDependentCString(queueName), observ, PR_TRUE); + printf("tmModuleTest: observing queue [%s]\n", queueName); + + + // run specific patterns based on the mode + int i = 0; // wasn't working inside the cases + switch (optMode) + { + case 's': + printf("tmModuleTest: start standard\n"); + // post a couple events + for (; i < 5 ; i++) { + gTransServ->PostTransaction(nsDependentCString(queueName), (PRUint8 *)data, dataLen); + } + // listen for events + while (gKeepRunning) + gEventQ->ProcessPendingEvents(); + printf("tmModuleTest: end standard\n"); + break; + case 'b': + printf("tmModuleTest: start broadcast\n"); + // post a BUNCH of messages + for (; i < 50; i++) { + gTransServ->PostTransaction(nsDependentCString(queueName), (PRUint8 *)data, dataLen); + } + // listen for events + while (gKeepRunning) + gEventQ->ProcessPendingEvents(); + printf("tmModuleTest: end broadcast\n"); + break; + case 'f': + printf("tmModuleTest: start flush\n"); + // post a couple events + for (; i < 5; i++) { + gTransServ->PostTransaction(nsDependentCString(queueName), (PRUint8 *)data, dataLen); + } + // flush the queue + gTransServ->Flush(nsDependentCString(queueName), PR_TRUE); + // post a couple events + for (i=0; i < 8; i++) { + gTransServ->PostTransaction(nsDependentCString(queueName), (PRUint8 *)data, dataLen); + } + // listen for events + while (gKeepRunning) + gEventQ->ProcessPendingEvents(); + // detach + gTransServ->Detach(nsDependentCString(queueName)); + printf("tmModuleTest: end flush\n"); + break; + case 'h': + printf("tmModuleTest: start hit-n-run\n"); + // post a couple events + for (; i < 5; i++) { + gTransServ->PostTransaction(nsDependentCString(queueName), (PRUint8 *)data, dataLen); + } + // detach + gTransServ->Detach(nsDependentCString(queueName)); + printf("tmModuleTest: end hit-n-run\n"); + break; + case 'l': + printf("tmModuleTest: start listener\n"); + // listen for events + while (gKeepRunning) + gEventQ->ProcessPendingEvents(); + printf("tmModuleTest: end listener\n"); + break; + default : + printf("tmModuleTest: start & end default\n"); + break; + } + + // shutdown process + + NS_RELEASE(gTransServ); + NS_RELEASE(gIpcServ); + + printf("tmModuleTest: processing remaining events\n"); + + // process any remaining events + PLEvent *ev; + while (NS_SUCCEEDED(gEventQ->GetEvent(&ev)) && ev) + gEventQ->HandleEvent(ev); + + printf("tmModuleTest: done\n"); + } // this scopes the nsCOMPtrs + + // helps with shutdown on some cases + PR_Sleep(PR_SecondsToInterval(4)); + + // 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; +} diff --git a/src/libs/xpcom18a4/ipc/ipcd/ipc.pkg b/src/libs/xpcom18a4/ipc/ipcd/ipc.pkg new file mode 100644 index 00000000..a2171122 --- /dev/null +++ b/src/libs/xpcom18a4/ipc/ipcd/ipc.pkg @@ -0,0 +1,21 @@ +[gecko] +dist/bin/mozipcd@BINS@ +#if OS_ARCH==OS2 +dist/bin/ipc/modules/@DLLP@lockmod@DLLS@ +#else +dist/bin/ipc/modules/@DLLP@lockmodule@DLLS@ +#endif +dist/bin/ipc/modules/@DLLP@transmgr@DLLS@ +dist/bin/components/@DLLP@ipcdc@DLLS@ +!xpt dist/bin/components/ipcd.xpt + +#if ENABLE_TESTS +[gecko-tests] +dist/bin/TestIPC@BINS@ +dist/bin/tmModuleTest@BINS@ +#if USE_SHORT_LIBNAME +dist/bin/ipc/modules/@DLLP@testmod@DLLS@ +#else +dist/bin/ipc/modules/@DLLP@testmodule@DLLS@ +#endif +#endif diff --git a/src/libs/xpcom18a4/ipc/ipcd/shared/src/.cvsignore b/src/libs/xpcom18a4/ipc/ipcd/shared/src/.cvsignore new file mode 100644 index 00000000..f3c7a7c5 --- /dev/null +++ b/src/libs/xpcom18a4/ipc/ipcd/shared/src/.cvsignore @@ -0,0 +1 @@ +Makefile diff --git a/src/libs/xpcom18a4/ipc/ipcd/shared/src/Makefile.in b/src/libs/xpcom18a4/ipc/ipcd/shared/src/Makefile.in new file mode 100644 index 00000000..ffcaa10c --- /dev/null +++ b/src/libs/xpcom18a4/ipc/ipcd/shared/src/Makefile.in @@ -0,0 +1,65 @@ +# vim: noexpandtab ts=4 sw=4 +# ***** 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 = ipcd +LIBRARY_NAME = ipcdshared_s +FORCE_STATIC_LIB = 1 +MODULE_NAME = ipcd + +# required for #include "nsID.h" +REQUIRES = \ + xpcom \ + $(NULL) + +CPPSRCS = \ + ipcLog.cpp \ + ipcConfig.cpp \ + ipcMessage.cpp \ + ipcMessagePrimitives.cpp \ + ipcStringList.cpp \ + ipcIDList.cpp \ + ipcm.cpp + +include $(topsrcdir)/config/rules.mk diff --git a/src/libs/xpcom18a4/ipc/ipcd/shared/src/ipcConfig.cpp b/src/libs/xpcom18a4/ipc/ipcd/shared/src/ipcConfig.cpp new file mode 100644 index 00000000..5f829626 --- /dev/null +++ b/src/libs/xpcom18a4/ipc/ipcd/shared/src/ipcConfig.cpp @@ -0,0 +1,104 @@ +/* ***** 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) 2003 + * 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 ***** */ + +#if defined(XP_WIN) +#elif defined(XP_OS2) && defined(XP_OS2_NATIVEIPC) +#else +#include <string.h> +#ifdef XP_UNIX +#include <unistd.h> +#include <sys/types.h> +#include <pwd.h> +#endif +#include "ipcConfig.h" +#include "ipcLog.h" +#include "prenv.h" +#include "plstr.h" + +#if defined(XP_OS2) && !defined(XP_OS2_NATIVEIPC) +#ifdef VBOX +static const char kDefaultSocketPrefix[] = "\\socket\\vbox-"; +#else +static const char kDefaultSocketPrefix[] = "\\socket\\mozilla-"; +#endif +static const char kDefaultSocketSuffix[] = "-ipc\\ipcd"; +#else +#ifdef VBOX +static const char kDefaultSocketPrefix[] = "/tmp/.vbox-"; +#else +static const char kDefaultSocketPrefix[] = "/tmp/.mozilla-"; +#endif +static const char kDefaultSocketSuffix[] = "-ipc/ipcd"; +#endif + +void IPC_GetDefaultSocketPath(char *buf, PRUint32 bufLen) +{ + const char *logName; + int len; + + PL_strncpyz(buf, kDefaultSocketPrefix, bufLen); + buf += (sizeof(kDefaultSocketPrefix) - 1); + bufLen -= (sizeof(kDefaultSocketPrefix) - 1); + + logName = PR_GetEnv("VBOX_IPC_SOCKETID"); +#if defined(VBOX) && defined(XP_UNIX) + if (!logName || !logName[0]) { + struct passwd *passStruct = getpwuid(getuid()); + if (passStruct) + logName = passStruct->pw_name; + } +#endif + if (!logName || !logName[0]) { + logName = PR_GetEnv("LOGNAME"); + if (!logName || !logName[0]) { + logName = PR_GetEnv("USER"); + if (!logName || !logName[0]) { + LOG(("could not determine username from environment\n")); + goto end; + } + } + } + PL_strncpyz(buf, logName, bufLen); + len = strlen(logName); + buf += len; + bufLen -= len; + +end: + PL_strncpyz(buf, kDefaultSocketSuffix, bufLen); +} + +#endif diff --git a/src/libs/xpcom18a4/ipc/ipcd/shared/src/ipcConfig.h b/src/libs/xpcom18a4/ipc/ipcd/shared/src/ipcConfig.h new file mode 100644 index 00000000..ab8b10e5 --- /dev/null +++ b/src/libs/xpcom18a4/ipc/ipcd/shared/src/ipcConfig.h @@ -0,0 +1,98 @@ +/* ***** 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 ***** */ + +#ifndef ipcProto_h__ +#define ipcProto_h__ + +#if defined(XP_WIN) +// +// use WM_COPYDATA messages +// +#include "prprf.h" + +#define IPC_WINDOW_CLASS "Mozilla:IPCWindowClass" +#define IPC_WINDOW_NAME "Mozilla:IPCWindow" +#define IPC_CLIENT_WINDOW_CLASS "Mozilla:IPCAppWindowClass" +#define IPC_CLIENT_WINDOW_NAME_PREFIX "Mozilla:IPCAppWindow:" +#define IPC_SYNC_EVENT_NAME "Local\\MozillaIPCSyncEvent" +#ifndef IPC_DAEMON_APP_NAME +#define IPC_DAEMON_APP_NAME "mozilla-ipcd.exe" +#endif +#define IPC_PATH_SEP_CHAR '\\' +#define IPC_MODULES_DIR "ipc\\modules" + +#define IPC_CLIENT_WINDOW_NAME_MAXLEN (sizeof(IPC_CLIENT_WINDOW_NAME_PREFIX) + 20) + +// writes client name into buf. buf must be at least +// IPC_CLIENT_WINDOW_NAME_MAXLEN bytes in length. +inline void IPC_GetClientWindowName(PRUint32 pid, char *buf) +{ + PR_snprintf(buf, IPC_CLIENT_WINDOW_NAME_MAXLEN, "%s%u", + IPC_CLIENT_WINDOW_NAME_PREFIX, pid); +} + +#else +#include "nscore.h" +// +// use UNIX domain socket +// +#define IPC_PORT 0 +#define IPC_SOCKET_TYPE "ipc" +#if defined(XP_OS2) +#ifndef IPC_DAEMON_APP_NAME +#define IPC_DAEMON_APP_NAME "mozilla-ipcd.exe" +#endif +#define IPC_PATH_SEP_CHAR '\\' +#define IPC_MODULES_DIR "ipc\\modules" +#else +#ifndef IPC_DAEMON_APP_NAME +#define IPC_DAEMON_APP_NAME "mozilla-ipcd" +#endif +#define IPC_PATH_SEP_CHAR '/' +#define IPC_MODULES_DIR "ipc/modules" +#endif + +NS_HIDDEN_(void) IPC_GetDefaultSocketPath(char *buf, PRUint32 bufLen); + +#endif + +// common shared configuration values + +#define IPC_STARTUP_PIPE_NAME "ipc:startup-pipe" +#define IPC_STARTUP_PIPE_MAGIC 0x1C + +#endif // !ipcProto_h__ diff --git a/src/libs/xpcom18a4/ipc/ipcd/shared/src/ipcIDList.cpp b/src/libs/xpcom18a4/ipc/ipcd/shared/src/ipcIDList.cpp new file mode 100644 index 00000000..0f64a3a0 --- /dev/null +++ b/src/libs/xpcom18a4/ipc/ipcd/shared/src/ipcIDList.cpp @@ -0,0 +1,62 @@ +/* ***** 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 "ipcIDList.h" + +ipcIDNode * +ipcIDList::FindNode(ipcIDNode *node, const nsID &id) +{ + while (node) { + if (node->Equals(id)) + return node; + node = node->mNext; + } + return NULL; +} + +ipcIDNode * +ipcIDList::FindNodeBefore(ipcIDNode *node, const nsID &id) +{ + ipcIDNode *prev = NULL; + while (node) { + if (node->Equals(id)) + return prev; + prev = node; + node = node->mNext; + } + return NULL; +} diff --git a/src/libs/xpcom18a4/ipc/ipcd/shared/src/ipcIDList.h b/src/libs/xpcom18a4/ipc/ipcd/shared/src/ipcIDList.h new file mode 100644 index 00000000..b4fd1512 --- /dev/null +++ b/src/libs/xpcom18a4/ipc/ipcd/shared/src/ipcIDList.h @@ -0,0 +1,108 @@ +/* ***** 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 ***** */ + +#ifndef ipcIDList_h__ +#define ipcIDList_h__ + +#include "nsID.h" +#include "ipcList.h" + +//----------------------------------------------------------------------------- +// nsID node +//----------------------------------------------------------------------------- + +class ipcIDNode +{ +public: + ipcIDNode(const nsID &id) + : mID(id) + { } + + const nsID &Value() const { return mID; } + + PRBool Equals(const nsID &id) const { return mID.Equals(id); } + + class ipcIDNode *mNext; +private: + nsID mID; +}; + +//----------------------------------------------------------------------------- +// singly-linked list of nsIDs +//----------------------------------------------------------------------------- + +class ipcIDList : public ipcList<ipcIDNode> +{ +public: + typedef ipcList<ipcIDNode> Super; + + void Prepend(const nsID &id) + { + Super::Prepend(new ipcIDNode(id)); + } + + void Append(const nsID &id) + { + Super::Append(new ipcIDNode(id)); + } + + const ipcIDNode *Find(const nsID &id) const + { + return FindNode(mHead, id); + } + + PRBool FindAndDelete(const nsID &id) + { + ipcIDNode *node = FindNodeBefore(mHead, id); + if (node) { + DeleteAfter(node); + return PR_TRUE; + } + else if (!IsEmpty()) { + DeleteFirst(); + return PR_TRUE; + } + + return PR_FALSE; + } + +private: + static NS_HIDDEN_(ipcIDNode *) FindNode (ipcIDNode *head, const nsID &id); + static NS_HIDDEN_(ipcIDNode *) FindNodeBefore(ipcIDNode *head, const nsID &id); +}; + +#endif // !ipcIDList_h__ diff --git a/src/libs/xpcom18a4/ipc/ipcd/shared/src/ipcList.h b/src/libs/xpcom18a4/ipc/ipcd/shared/src/ipcList.h new file mode 100644 index 00000000..a9b9c959 --- /dev/null +++ b/src/libs/xpcom18a4/ipc/ipcd/shared/src/ipcList.h @@ -0,0 +1,211 @@ +/* ***** 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 ***** */ + +#ifndef ipcList_h__ +#define ipcList_h__ + +#include "prtypes.h" + +//----------------------------------------------------------------------------- +// simple list of singly-linked objects. class T must have the following +// structure: +// +// class T { +// ... +// public: +// T *mNext; +// }; +// +// objects added to the list must be allocated with operator new. class T may +// optionally inherit from ipcListNode<T> if it doesn't wish to define mNext +// explicitly. +//----------------------------------------------------------------------------- + +template<class T> +class ipcList +{ +public: + ipcList() + : mHead(NULL) + , mTail(NULL) + { } + ~ipcList() { DeleteAll(); } + + // + // prepends obj at the beginning of the list. + // + void Prepend(T *obj) + { + obj->mNext = mHead; + mHead = obj; + if (!mTail) + mTail = mHead; + } + + // + // appends obj to the end of the list. + // + void Append(T *obj) + { + obj->mNext = NULL; + if (mTail) { + mTail->mNext = obj; + mTail = obj; + } + else + mTail = mHead = obj; + } + + // + // inserts b into the list after a. + // + void InsertAfter(T *a, T *b) + { + b->mNext = a->mNext; + a->mNext = b; + if (mTail == a) + mTail = b; + } + + // + // removes first element w/o deleting it + // + void RemoveFirst() + { + if (mHead) + AdvanceHead(); + } + + // + // removes element after the given element w/o deleting it + // + void RemoveAfter(T *obj) + { + T *rej = obj->mNext; + if (rej) { + obj->mNext = rej->mNext; + if (rej == mTail) + mTail = obj; + } + } + + // + // deletes first element + // + void DeleteFirst() + { + T *first = mHead; + if (first) { + AdvanceHead(); + delete first; + } + } + + // + // deletes element after the given element + // + void DeleteAfter(T *obj) + { + T *rej = obj->mNext; + if (rej) { + RemoveAfter(obj); + delete rej; + } + } + + // + // deletes all elements + // + void DeleteAll() + { + while (mHead) + DeleteFirst(); + } + + const T *First() const { return mHead; } + T *First() { return mHead; } + const T *Last() const { return mTail; } + T *Last() { return mTail; } + + PRBool IsEmpty() const { return mHead == NULL; } + + // + // moves contents of list to another list + // + void MoveTo(ipcList<T> &other) + { + other.mHead = mHead; + other.mTail = mTail; + mHead = NULL; + mTail = NULL; + } + + // gets count of list elements + PRUint32 Count() + { + T *obj = mHead; + PRUint32 count = 0; + while (obj) { + count++; + obj = obj->mNext; + } + + return count; + } + +protected: + void AdvanceHead() + { + mHead = mHead->mNext; + if (!mHead) + mTail = NULL; + } + + T *mHead; + T *mTail; +}; + +template<class T> +class ipcListNode +{ +public: + ipcListNode() : mNext(nsnull) {} + + T *mNext; +}; + +#endif // !ipcList_h__ diff --git a/src/libs/xpcom18a4/ipc/ipcd/shared/src/ipcLog.cpp b/src/libs/xpcom18a4/ipc/ipcd/shared/src/ipcLog.cpp new file mode 100644 index 00000000..340b211c --- /dev/null +++ b/src/libs/xpcom18a4/ipc/ipcd/shared/src/ipcLog.cpp @@ -0,0 +1,181 @@ +/* ***** 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 "ipcLog.h" + +#ifdef IPC_LOGGING + +#include <string.h> +#include <ctype.h> + +#include "prenv.h" +#include "prprf.h" +#include "prthread.h" +#include "plstr.h" + +#ifdef VBOX +# if defined(__OS2__) && defined(PAGE_SIZE) +# undef PAGE_SIZE +# endif +# include <iprt/initterm.h> // for RTR3InitDll +#else // !VBOX +PRBool ipcLogEnabled = PR_FALSE; +#endif // !VBOX + +char ipcLogPrefix[10] = {0}; + +#ifndef VBOX + +//----------------------------------------------------------------------------- +// UNIX +//----------------------------------------------------------------------------- +#if defined(XP_UNIX) || defined(XP_OS2) || defined(XP_BEOS) + +#include <sys/types.h> +#include <unistd.h> + +static inline PRUint32 +WritePrefix(char *buf, PRUint32 bufLen) +{ + return PR_snprintf(buf, bufLen, "[%u:%p] %s ", + (unsigned) getpid(), + PR_GetCurrentThread(), + ipcLogPrefix); +} +#endif + +//----------------------------------------------------------------------------- +// WIN32 +//----------------------------------------------------------------------------- +#ifdef XP_WIN +#include <windows.h> + +static inline PRUint32 +WritePrefix(char *buf, PRUint32 bufLen) +{ + return PR_snprintf(buf, bufLen, "[%u:%p] %s ", + GetCurrentProcessId(), + PR_GetCurrentThread(), + ipcLogPrefix); +} +#endif + +#endif // !VBOX + +//----------------------------------------------------------------------------- +// logging API impl +//----------------------------------------------------------------------------- + +void +IPC_InitLog(const char *prefix) +{ +#ifdef VBOX + // initialize VBox Runtime + RTR3InitDll(RTR3INIT_FLAGS_UNOBTRUSIVE); + + PL_strncpyz(ipcLogPrefix, prefix, sizeof(ipcLogPrefix)); +#else + if (PR_GetEnv("IPC_LOG_ENABLE")) { + ipcLogEnabled = PR_TRUE; + PL_strncpyz(ipcLogPrefix, prefix, sizeof(ipcLogPrefix)); + } +#endif +} + +void +IPC_Log(const char *fmt, ... ) +{ + va_list ap; + va_start(ap, fmt); + PRUint32 nb = 0; + char buf[512]; + +#ifdef VBOX + if (ipcLogPrefix[0]) { + nb = strlen(ipcLogPrefix); + if (nb > sizeof(buf) - 2) + nb = sizeof(buf) - 2; + PL_strncpy(buf, ipcLogPrefix, nb); + buf[nb++] = ' '; + } +#else + if (ipcLogPrefix[0]) + nb = WritePrefix(buf, sizeof(buf)); +#endif + + PR_vsnprintf(buf + nb, sizeof(buf) - nb, fmt, ap); + buf[sizeof(buf) - 1] = '\0'; + +#ifdef VBOX + LogFlow(("%s", buf)); +#else + fwrite(buf, strlen(buf), 1, stdout); +#endif + + va_end(ap); +} + +void +IPC_LogBinary(const PRUint8 *data, PRUint32 len) +{ + PRUint32 i, j, ln; + for (i=0; i<len; ) { + char line[100] = ""; + const PRUint8 *p; + + ln = 0; + + p = &data[i]; + for (j=0; j<PR_MIN(8, len - i); ++j, ++p) + ln += PR_snprintf(line + ln, sizeof(line) - ln, "%02x ", *p); + + for (; ln < 32; ++ln) + line[ln] = ' '; + + p = &data[i]; + for (j=0; j<PR_MIN(8, len - i); ++j, ++p) + ln += PR_snprintf(line + ln, sizeof(line) - ln, "%c", isprint(*p) ? *p : '.'); + + line[ln] = '\0'; + + i += (p - &data[i]); + + LOG(("%s\n", line)); + } +} + +#endif // IPC_LOGGING diff --git a/src/libs/xpcom18a4/ipc/ipcd/shared/src/ipcLog.h b/src/libs/xpcom18a4/ipc/ipcd/shared/src/ipcLog.h new file mode 100644 index 00000000..e21f6211 --- /dev/null +++ b/src/libs/xpcom18a4/ipc/ipcd/shared/src/ipcLog.h @@ -0,0 +1,111 @@ +/* ***** 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 ***** */ + +#ifndef ipcLog_h__ +#define ipcLog_h__ + +#include "nscore.h" +#include "prtypes.h" + +#ifndef VBOX +#ifdef DEBUG +#define IPC_LOGGING +#endif +#endif + +#ifdef IPC_LOGGING + +#ifdef VBOX + +/* Redefine logging group to IPC */ +# ifdef LOG_GROUP +# undef LOG_GROUP +# endif +# define LOG_GROUP LOG_GROUP_IPC + +/* Ensure log macros are enabled */ +# ifndef LOG_ENABLED +# define LOG_ENABLED +# endif + +#include <VBox/log.h> + +extern NS_HIDDEN_(void) IPC_InitLog(const char *prefix); +extern NS_HIDDEN_(void) IPC_Log(const char *fmt, ...); +extern NS_HIDDEN_(void) IPC_LogBinary(const PRUint8 *data, PRUint32 len); + +# define IPC_LOG(_args) \ + PR_BEGIN_MACRO \ + if (IPC_LOG_ENABLED()) \ + IPC_Log _args; \ + PR_END_MACRO + +/* IPC_Log() internally uses LogFlow() so use LogIsFlowEnabled() below */ +# define IPC_LOG_ENABLED() (LogIsFlowEnabled()) + +# define LOG(args) IPC_LOG(args) + +#else /* !VBOX */ + +extern PRBool ipcLogEnabled; +extern NS_HIDDEN_(void) IPC_InitLog(const char *prefix); +extern NS_HIDDEN_(void) IPC_Log(const char *fmt, ...); +extern NS_HIDDEN_(void) IPC_LogBinary(const PRUint8 *data, PRUint32 len); + +#define IPC_LOG(_args) \ + PR_BEGIN_MACRO \ + if (ipcLogEnabled) \ + IPC_Log _args; \ + PR_END_MACRO + +#define IPC_LOG_ENABLED() ipcLogEnabled + +#define LOG(args) IPC_LOG(args) + +#endif /* !VBOX */ + +#else // IPC_LOGGING + +#define IPC_InitLog(prefix) PR_BEGIN_MACRO PR_END_MACRO +#define IPC_LogBinary(data, len) PR_BEGIN_MACRO PR_END_MACRO +#define IPC_LOG(_args) PR_BEGIN_MACRO PR_END_MACRO +#define IPC_LOG_ENABLED() (0) +#define LOG(args) PR_BEGIN_MACRO PR_END_MACRO + +#endif // IPC_LOGGING + +#endif // !ipcLog_h__ diff --git a/src/libs/xpcom18a4/ipc/ipcd/shared/src/ipcMessage.cpp b/src/libs/xpcom18a4/ipc/ipcd/shared/src/ipcMessage.cpp new file mode 100644 index 00000000..bfdc7efe --- /dev/null +++ b/src/libs/xpcom18a4/ipc/ipcd/shared/src/ipcMessage.cpp @@ -0,0 +1,280 @@ +/* ***** 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 <stdlib.h> +#include <string.h> +#include "prlog.h" +#include "ipcMessage.h" +#ifdef VBOX_USE_IPRT_IN_XPCOM +# include <iprt/mem.h> +#endif + +ipcMessage::~ipcMessage() +{ + if (mMsgHdr) +#ifdef VBOX_USE_IPRT_IN_XPCOM + RTMemFree(mMsgHdr); +#else + free(mMsgHdr); +#endif +} + +void +ipcMessage::Reset() +{ + if (mMsgHdr) { +#ifdef VBOX_USE_IPRT_IN_XPCOM + RTMemFree(mMsgHdr); +#else + free(mMsgHdr); +#endif + mMsgHdr = NULL; + } + + mMsgOffset = 0; + mMsgComplete = PR_FALSE; +} + +ipcMessage * +ipcMessage::Clone() const +{ + ipcMessage *clone = new ipcMessage(); + if (!clone) + return NULL; + + // copy buf if non-null + if (mMsgHdr) { +#ifdef VBOX_USE_IPRT_IN_XPCOM + clone->mMsgHdr = (ipcMessageHeader *) RTMemDup(mMsgHdr, mMsgHdr->mLen); +#else + clone->mMsgHdr = (ipcMessageHeader *) malloc(mMsgHdr->mLen); + memcpy(clone->mMsgHdr, mMsgHdr, mMsgHdr->mLen); +#endif + } + else + clone->mMsgHdr = NULL; + + clone->mMsgOffset = mMsgOffset; + clone->mMsgComplete = mMsgComplete; + + return clone; +} + +PRStatus +ipcMessage::Init(const nsID &target, const char *data, PRUint32 dataLen) +{ + if (mMsgHdr) +#ifdef VBOX_USE_IPRT_IN_XPCOM + RTMemFree(mMsgHdr); +#else + free(mMsgHdr); +#endif + mMsgComplete = PR_FALSE; + + // allocate message data + PRUint32 msgLen = IPC_MSG_HEADER_SIZE + dataLen; +#ifdef VBOX_USE_IPRT_IN_XPCOM + mMsgHdr = (ipcMessageHeader *) RTMemAlloc(msgLen); +#else + mMsgHdr = (ipcMessageHeader *) malloc(msgLen); +#endif + if (!mMsgHdr) { + mMsgHdr = NULL; + return PR_FAILURE; + } + + // fill in message data + mMsgHdr->mLen = msgLen; + mMsgHdr->mVersion = IPC_MSG_VERSION; + mMsgHdr->mFlags = 0; + mMsgHdr->mTarget = target; + + if (data) + SetData(0, data, dataLen); + + mMsgComplete = PR_TRUE; + return PR_SUCCESS; +} + +PRStatus +ipcMessage::SetData(PRUint32 offset, const char *data, PRUint32 dataLen) +{ + PR_ASSERT(mMsgHdr != NULL); + + if (offset + dataLen > DataLen()) + return PR_FAILURE; + + memcpy((char *) Data() + offset, data, dataLen); + return PR_SUCCESS; +} + +PRBool +ipcMessage::Equals(const nsID &target, const char *data, PRUint32 dataLen) const +{ + return mMsgComplete && + mMsgHdr->mTarget.Equals(target) && + DataLen() == dataLen && + memcmp(Data(), data, dataLen) == 0; +} + +PRBool +ipcMessage::Equals(const ipcMessage *msg) const +{ + PRUint32 msgLen = MsgLen(); + return mMsgComplete && msg->mMsgComplete && + msgLen == msg->MsgLen() && + memcmp(MsgBuf(), msg->MsgBuf(), msgLen) == 0; +} + +PRStatus +ipcMessage::WriteTo(char *buf, + PRUint32 bufLen, + PRUint32 *bytesWritten, + PRBool *complete) +{ + if (!mMsgComplete) + return PR_FAILURE; + + if (mMsgOffset == MsgLen()) { + *bytesWritten = 0; + *complete = PR_TRUE; + return PR_SUCCESS; + } + + PRUint32 count = MsgLen() - mMsgOffset; + if (count > bufLen) + count = bufLen; + + memcpy(buf, MsgBuf() + mMsgOffset, count); + mMsgOffset += count; + + *bytesWritten = count; + *complete = (mMsgOffset == MsgLen()); + + return PR_SUCCESS; +} + +PRStatus +ipcMessage::ReadFrom(const char *buf, + PRUint32 bufLen, + PRUint32 *bytesRead, + PRBool *complete) +{ + *bytesRead = 0; + + if (mMsgComplete) { + *complete = PR_TRUE; + return PR_SUCCESS; + } + + if (mMsgHdr) { + // appending data to buffer + if (mMsgOffset < sizeof(PRUint32)) { + // we haven't learned the message length yet + if (mMsgOffset + bufLen < sizeof(PRUint32)) { + // we still don't know the length of the message! + memcpy((char *) mMsgHdr + mMsgOffset, buf, bufLen); + mMsgOffset += bufLen; + *bytesRead = bufLen; + *complete = PR_FALSE; + return PR_SUCCESS; + } + else { + // we now have enough data to determine the message length + PRUint32 count = sizeof(PRUint32) - mMsgOffset; + memcpy((char *) MsgBuf() + mMsgOffset, buf, count); + mMsgOffset += count; + buf += count; + bufLen -= count; + *bytesRead = count; + + if (MsgLen() > IPC_MSG_GUESSED_SIZE) { + // realloc message buffer to the correct size +#ifdef VBOX_USE_IPRT_IN_XPCOM + mMsgHdr = (ipcMessageHeader *) RTMemRealloc(mMsgHdr, MsgLen()); +#else + mMsgHdr = (ipcMessageHeader *) realloc(mMsgHdr, MsgLen()); +#endif + } + } + } + } + else { + if (bufLen < sizeof(PRUint32)) { + // not enough data available in buffer to determine allocation size + // allocate a partial buffer + PRUint32 msgLen = IPC_MSG_GUESSED_SIZE; +#ifdef VBOX_USE_IPRT_IN_XPCOM + mMsgHdr = (ipcMessageHeader *) RTMemAlloc(msgLen); +#else + mMsgHdr = (ipcMessageHeader *) malloc(msgLen); +#endif + if (!mMsgHdr) + return PR_FAILURE; + memcpy(mMsgHdr, buf, bufLen); + mMsgOffset = bufLen; + *bytesRead = bufLen; + *complete = PR_FALSE; + return PR_SUCCESS; + } + else { + PRUint32 msgLen = *(PRUint32 *) buf; +#ifdef VBOX_USE_IPRT_IN_XPCOM + mMsgHdr = (ipcMessageHeader *) RTMemAlloc(msgLen); +#else + mMsgHdr = (ipcMessageHeader *) malloc(msgLen); +#endif + if (!mMsgHdr) + return PR_FAILURE; + mMsgHdr->mLen = msgLen; + mMsgOffset = 0; + } + } + + // have mMsgHdr at this point + + PRUint32 count = MsgLen() - mMsgOffset; + if (count > bufLen) + count = bufLen; + + memcpy((char *) mMsgHdr + mMsgOffset, buf, count); + mMsgOffset += count; + *bytesRead += count; + + *complete = mMsgComplete = (mMsgOffset == MsgLen()); + return PR_SUCCESS; +} diff --git a/src/libs/xpcom18a4/ipc/ipcd/shared/src/ipcMessage.h b/src/libs/xpcom18a4/ipc/ipcd/shared/src/ipcMessage.h new file mode 100644 index 00000000..c9f8f184 --- /dev/null +++ b/src/libs/xpcom18a4/ipc/ipcd/shared/src/ipcMessage.h @@ -0,0 +1,214 @@ +/* ***** 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 ***** */ + +#ifndef ipcMessage_h__ +#define ipcMessage_h__ + +#include "nsID.h" + +// +// ipc message format: +// +// +------------------------------------+ +// | DWORD : length | +// +------------------+-----------------+ +// | WORD : version | WORD : flags | +// +------------------+-----------------+ +// | nsID : target | +// +------------------------------------+ +// | data | +// +------------------------------------+ +// +// header is 24 bytes. flags are defined below. default value of flags is +// zero. protocol implementations should ignore unrecognized flags. target +// is a 16 byte UUID indicating the intended receiver of this message. +// + +struct ipcMessageHeader +{ + PRUint32 mLen; + PRUint16 mVersion; + PRUint16 mFlags; + nsID mTarget; +}; + +#define IPC_MSG_VERSION (0x1) +#define IPC_MSG_HEADER_SIZE (sizeof(ipcMessageHeader)) +#define IPC_MSG_GUESSED_SIZE (IPC_MSG_HEADER_SIZE + 64) + +// +// the IPC message protocol supports synchronous messages. these messages can +// only be sent from a client to the daemon. a daemon module cannot send a +// synchronous message. the client sets the SYNC_QUERY flag to indicate that +// it is expecting a response with the SYNC_REPLY flag set. +// +#define IPC_MSG_FLAG_SYNC_QUERY (0x1) +#define IPC_MSG_FLAG_SYNC_REPLY (0x2) + +// +// a special flag to prevent repeated processing of the same message by two +// or more selectors when walking through the queue of pending messages +// in WaitTarget(). +// +#define IPC_MSG_FLAG_IN_PROCESS (0x4) + +//----------------------------------------------------------------------------- +// ipcMessage +//----------------------------------------------------------------------------- + +class ipcMessage +{ +public: + ipcMessage() + : mNext(NULL) + , mMetaData(0) + , mMsgHdr(NULL) + , mMsgOffset(0) + , mMsgComplete(PR_FALSE) + { } + ipcMessage(const nsID &target, const char *data, PRUint32 dataLen) + : mNext(NULL) + , mMetaData(0) + , mMsgHdr(NULL) + , mMsgOffset(0) + { Init(target, data, dataLen); } + ~ipcMessage() NS_HIDDEN; + + // + // reset message to uninitialized state + // + NS_HIDDEN_(void) Reset(); + + // + // create a copy of this message + // + NS_HIDDEN_(ipcMessage *) Clone() const; + + // + // initialize message + // + // param: + // topic - message topic string + // data - message data (may be null to leave data uninitialized) + // dataLen - message data len + // + NS_HIDDEN_(PRStatus) Init(const nsID &target, const char *data, PRUint32 dataLen); + + // + // copy data into the message's data section, starting from offset. this + // function can be used to write any portion of the message's data. + // + // param: + // offset - destination offset + // data - data to write + // dataLen - number of bytes to write + // + NS_HIDDEN_(PRStatus) SetData(PRUint32 offset, const char *data, PRUint32 dataLen); + + // + // access message flags + // + void SetFlag(PRUint16 flag) { mMsgHdr->mFlags |= flag; } + void ClearFlag(PRUint16 flag) { mMsgHdr->mFlags &= ~flag; } + PRBool TestFlag(PRUint16 flag) const { return mMsgHdr->mFlags & flag; } + + // + // if true, the message is complete and the members of the message + // can be accessed. + // + PRBool IsComplete() const { return mMsgComplete; } + + // + // readonly accessors + // + const ipcMessageHeader *Header() const { return mMsgHdr; } + const nsID &Target() const { return mMsgHdr->mTarget; } + const char *Data() const { return (char *) mMsgHdr + IPC_MSG_HEADER_SIZE; } + PRUint32 DataLen() const { return mMsgHdr->mLen - IPC_MSG_HEADER_SIZE; } + const char *MsgBuf() const { return (char *) mMsgHdr; } + PRUint32 MsgLen() const { return mMsgHdr->mLen; } + + // + // message comparison functions + // + // param: + // topic - message topic (may be null) + // data - message data (must not be null) + // dataLen - message data length + // + NS_HIDDEN_(PRBool) Equals(const nsID &target, const char *data, PRUint32 dataLen) const; + NS_HIDDEN_(PRBool) Equals(const ipcMessage *msg) const; + + // + // write the message to a buffer segment; segment need not be large + // enough to hold entire message. called repeatedly. + // + NS_HIDDEN_(PRStatus) WriteTo(char *buf, + PRUint32 bufLen, + PRUint32 *bytesWritten, + PRBool *complete); + + // + // read the message from a buffer segment; segment need not contain + // the entire messgae. called repeatedly. + // + NS_HIDDEN_(PRStatus) ReadFrom(const char *buf, + PRUint32 bufLen, + PRUint32 *bytesRead, + PRBool *complete); + + // + // a message can be added to a singly-linked list. + // + class ipcMessage *mNext; + + // + // meta data associated with this message object. the owner of the + // ipcMessage object is free to use this field for any purpose. by + // default, it is initialized to 0. + // + PRUint32 mMetaData; + +private: + ipcMessageHeader *mMsgHdr; + + // XXX document me + PRUint32 mMsgOffset; + PRPackedBool mMsgComplete; +}; + +#endif // !ipcMessage_h__ diff --git a/src/libs/xpcom18a4/ipc/ipcd/shared/src/ipcMessagePrimitives.cpp b/src/libs/xpcom18a4/ipc/ipcd/shared/src/ipcMessagePrimitives.cpp new file mode 100644 index 00000000..08753ddb --- /dev/null +++ b/src/libs/xpcom18a4/ipc/ipcd/shared/src/ipcMessagePrimitives.cpp @@ -0,0 +1,81 @@ +/* ***** 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 <string.h> +#include "ipcMessagePrimitives.h" + +ipcMessage_DWORD_STR::ipcMessage_DWORD_STR(const nsID &target, + PRUint32 first, + const char *second) +{ + int sLen = strlen(second); + Init(target, NULL, 4 + sLen + 1); + SetData(0, (char *) &first, 4); + SetData(4, second, sLen + 1); +} + +ipcMessage_DWORD_DWORD_STR::ipcMessage_DWORD_DWORD_STR(const nsID &target, + PRUint32 first, + PRUint32 second, + const char *third) +{ + int sLen = strlen(third); + Init(target, NULL, 8 + sLen + 1); + SetData(0, (char *) &first, 4); + SetData(4, (char *) &second, 4); + SetData(8, third, sLen + 1); +} + +ipcMessage_DWORD_ID::ipcMessage_DWORD_ID(const nsID &target, + PRUint32 first, + const nsID &second) +{ + Init(target, NULL, 4 + sizeof(nsID)); + SetData(0, (char *) &first, 4); + SetData(4, (char *) &second, sizeof(nsID)); +} + +ipcMessage_DWORD_DWORD_ID::ipcMessage_DWORD_DWORD_ID(const nsID &target, + PRUint32 first, + PRUint32 second, + const nsID &third) +{ + Init(target, NULL, 8 + sizeof(nsID)); + SetData(0, (char *) &first, 4); + SetData(4, (char *) &second, 4); + SetData(8, (char *) &third, sizeof(nsID)); +} diff --git a/src/libs/xpcom18a4/ipc/ipcd/shared/src/ipcMessagePrimitives.h b/src/libs/xpcom18a4/ipc/ipcd/shared/src/ipcMessagePrimitives.h new file mode 100644 index 00000000..abfa7d69 --- /dev/null +++ b/src/libs/xpcom18a4/ipc/ipcd/shared/src/ipcMessagePrimitives.h @@ -0,0 +1,206 @@ +/* ***** 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 ***** */ + +#ifndef ipcMessagePrimitives_h__ +#define ipcMessagePrimitives_h__ + +#include "ipcMessage.h" + +class ipcMessage_DWORD : public ipcMessage +{ +public: + ipcMessage_DWORD(const nsID &target, PRUint32 first) + { + Init(target, (char *) &first, sizeof(first)); + } + + PRUint32 First() const + { + return ((PRUint32 *) Data())[0]; + } +}; + +class ipcMessage_DWORD_DWORD : public ipcMessage +{ +public: + ipcMessage_DWORD_DWORD(const nsID &target, PRUint32 first, PRUint32 second) + { + PRUint32 data[2] = { first, second }; + Init(target, (char *) data, sizeof(data)); + } + + PRUint32 First() const + { + return ((PRUint32 *) Data())[0]; + } + + PRUint32 Second() const + { + return ((PRUint32 *) Data())[1]; + } +}; + +class ipcMessage_DWORD_DWORD_DWORD : public ipcMessage +{ +public: + ipcMessage_DWORD_DWORD_DWORD(const nsID &target, PRUint32 first, PRUint32 second, PRUint32 third) + { + PRUint32 data[3] = { first, second, third }; + Init(target, (char *) data, sizeof(data)); + } + + PRUint32 First() const + { + return ((PRUint32 *) Data())[0]; + } + + PRUint32 Second() const + { + return ((PRUint32 *) Data())[1]; + } + + PRUint32 Third() const + { + return ((PRUint32 *) Data())[2]; + } +}; + +class ipcMessage_DWORD_DWORD_DWORD_DWORD : public ipcMessage +{ +public: + ipcMessage_DWORD_DWORD_DWORD_DWORD(const nsID &target, PRUint32 first, PRUint32 second, PRUint32 third, PRUint32 fourth) + { + PRUint32 data[4] = { first, second, third, fourth }; + Init(target, (char *) data, sizeof(data)); + } + + PRUint32 First() const + { + return ((PRUint32 *) Data())[0]; + } + + PRUint32 Second() const + { + return ((PRUint32 *) Data())[1]; + } + + PRUint32 Third() const + { + return ((PRUint32 *) Data())[2]; + } + + PRUint32 Fourth() const + { + return ((PRUint32 *) Data())[3]; + } +}; + +class ipcMessage_DWORD_STR : public ipcMessage +{ +public: + ipcMessage_DWORD_STR(const nsID &target, PRUint32 first, const char *second) NS_HIDDEN; + + PRUint32 First() const + { + return ((PRUint32 *) Data())[0]; + } + + const char *Second() const + { + return Data() + sizeof(PRUint32); + } +}; + +class ipcMessage_DWORD_DWORD_STR : public ipcMessage +{ +public: + ipcMessage_DWORD_DWORD_STR(const nsID &target, PRUint32 first, PRUint32 second, const char *third) NS_HIDDEN; + + PRUint32 First() const + { + return ((PRUint32 *) Data())[0]; + } + + PRUint32 Second() const + { + return ((PRUint32 *) Data())[1]; + } + + const char *Third() const + { + return Data() + 2 * sizeof(PRUint32); + } +}; + +class ipcMessage_DWORD_ID : public ipcMessage +{ +public: + ipcMessage_DWORD_ID(const nsID &target, PRUint32 first, const nsID &second) NS_HIDDEN; + + PRUint32 First() const + { + return ((PRUint32 *) Data())[0]; + } + + const nsID &Second() const + { + return * (const nsID *) (Data() + sizeof(PRUint32)); + } +}; + +class ipcMessage_DWORD_DWORD_ID : public ipcMessage +{ +public: + ipcMessage_DWORD_DWORD_ID(const nsID &target, PRUint32 first, PRUint32 second, const nsID &third) NS_HIDDEN; + + PRUint32 First() const + { + return ((PRUint32 *) Data())[0]; + } + + PRUint32 Second() const + { + return ((PRUint32 *) Data())[1]; + } + + const nsID &Third() const + { + return * (const nsID *) (Data() + 2 * sizeof(PRUint32)); + } +}; + +#endif // !ipcMessagePrimitives_h__ diff --git a/src/libs/xpcom18a4/ipc/ipcd/shared/src/ipcMessageQ.h b/src/libs/xpcom18a4/ipc/ipcd/shared/src/ipcMessageQ.h new file mode 100644 index 00000000..d372713e --- /dev/null +++ b/src/libs/xpcom18a4/ipc/ipcd/shared/src/ipcMessageQ.h @@ -0,0 +1,46 @@ +/* ***** 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 ***** */ + +#ifndef ipcMessageQ_h__ +#define ipcMessageQ_h__ + +#include "ipcMessage.h" +#include "ipcList.h" + +typedef ipcList<ipcMessage> ipcMessageQ; + +#endif // !ipcMessageQ_h__ diff --git a/src/libs/xpcom18a4/ipc/ipcd/shared/src/ipcMessageUtils.h b/src/libs/xpcom18a4/ipc/ipcd/shared/src/ipcMessageUtils.h new file mode 100644 index 00000000..99e3e6b8 --- /dev/null +++ b/src/libs/xpcom18a4/ipc/ipcd/shared/src/ipcMessageUtils.h @@ -0,0 +1,66 @@ +/* ***** 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 ***** */ + +#ifndef ipcMessageUtils_h__ +#define ipcMessageUtils_h__ + +class ipcMessage; + +// +// given code like this: +// +// const ipcmMessageClientID *msg = (const ipcmMessageClientID *) rawMsg; +// +// we can write: +// +// ipcMessageCast<ipcmMessageClientID> msg(rawMsg); +// +// XXX ipcMessageCast is probably not the best name for this class. +// +template<class T> +class ipcMessageCast +{ +public: + ipcMessageCast() : mPtr(NULL) {} + ipcMessageCast(const ipcMessage *ptr) : mPtr((const T *) ptr) {} + void operator=(const ipcMessage *ptr) { mPtr = (const T *) ptr; } + const T *operator->() { return mPtr; } +private: + const T *mPtr; +}; + +#endif // !ipcMessageUtils_h__ diff --git a/src/libs/xpcom18a4/ipc/ipcd/shared/src/ipcStringList.cpp b/src/libs/xpcom18a4/ipc/ipcd/shared/src/ipcStringList.cpp new file mode 100644 index 00000000..5464f9a7 --- /dev/null +++ b/src/libs/xpcom18a4/ipc/ipcd/shared/src/ipcStringList.cpp @@ -0,0 +1,80 @@ +/* ***** 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 "ipcStringList.h" + +void * +ipcStringNode::operator new(size_t size, const char *str) CPP_THROW_NEW +{ + int len = strlen(str); + + size += len; + + ipcStringNode *node = (ipcStringNode *) ::operator new(size); + if (!node) + return NULL; + + node->mNext = NULL; + memcpy(node->mData, str, len); + node->mData[len] = '\0'; + + return node; +} + +ipcStringNode * +ipcStringList::FindNode(ipcStringNode *node, const char *str) +{ + while (node) { + if (node->Equals(str)) + return node; + node = node->mNext; + } + return NULL; +} + +ipcStringNode * +ipcStringList::FindNodeBefore(ipcStringNode *node, const char *str) +{ + ipcStringNode *prev = NULL; + while (node) { + if (node->Equals(str)) + return prev; + prev = node; + node = node->mNext; + } + return NULL; +} diff --git a/src/libs/xpcom18a4/ipc/ipcd/shared/src/ipcStringList.h b/src/libs/xpcom18a4/ipc/ipcd/shared/src/ipcStringList.h new file mode 100644 index 00000000..2a2316b6 --- /dev/null +++ b/src/libs/xpcom18a4/ipc/ipcd/shared/src/ipcStringList.h @@ -0,0 +1,114 @@ +/* ***** 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 ***** */ + +#ifndef ipcStringList_h__ +#define ipcStringList_h__ + +#include <string.h> +#include "plstr.h" +#include "nscore.h" +#include "ipcList.h" + +//----------------------------------------------------------------------------- +// string node +//----------------------------------------------------------------------------- + +class ipcStringNode +{ +public: + ipcStringNode() {} + + const char *Value() const { return mData; } + + PRBool Equals(const char *val) const { return strcmp(mData, val) == 0; } + PRBool EqualsIgnoreCase(const char *val) const { return PL_strcasecmp(mData, val) == 0; } + + class ipcStringNode *mNext; +private: + void *operator new(size_t size, const char *str) CPP_THROW_NEW; + + // this is actually bigger + char mData[1]; + + friend class ipcStringList; +}; + +//----------------------------------------------------------------------------- +// singly-linked list of strings +//----------------------------------------------------------------------------- + +class ipcStringList : public ipcList<ipcStringNode> +{ +public: + typedef ipcList<ipcStringNode> Super; + + void Prepend(const char *str) + { + Super::Prepend(new (str) ipcStringNode()); + } + + void Append(const char *str) + { + Super::Append(new (str) ipcStringNode()); + } + + const ipcStringNode *Find(const char *str) const + { + return FindNode(mHead, str); + } + + PRBool FindAndDelete(const char *str) + { + ipcStringNode *node = FindNodeBefore(mHead, str); + if (node) { + DeleteAfter(node); + return PR_TRUE; + } + else if (!IsEmpty()) { + DeleteFirst(); + return PR_TRUE; + } + + return PR_FALSE; + } + +private: + static NS_HIDDEN_(ipcStringNode *) FindNode (ipcStringNode *head, const char *str); + static NS_HIDDEN_(ipcStringNode *) FindNodeBefore(ipcStringNode *head, const char *str); +}; + +#endif // !ipcStringList_h__ diff --git a/src/libs/xpcom18a4/ipc/ipcd/shared/src/ipcm.cpp b/src/libs/xpcom18a4/ipc/ipcd/shared/src/ipcm.cpp new file mode 100644 index 00000000..7f7ed060 --- /dev/null +++ b/src/libs/xpcom18a4/ipc/ipcd/shared/src/ipcm.cpp @@ -0,0 +1,303 @@ +/* ***** 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 <string.h> +#include "ipcm.h" +#include "pratom.h" + +const nsID IPCM_TARGET = +{ /* 753ca8ff-c8c2-4601-b115-8c2944da1150 */ + 0x753ca8ff, + 0xc8c2, + 0x4601, + {0xb1, 0x15, 0x8c, 0x29, 0x44, 0xda, 0x11, 0x50} +}; + +PRUint32 +IPCM_NewRequestIndex() +{ + static PRInt32 sRequestIndex; + return (PRUint32) PR_AtomicIncrement(&sRequestIndex); +} + +#if 0 + +// +// MSG_TYPE values +// +const PRUint32 ipcmMessagePing::MSG_TYPE = IPCM_MSG_TYPE_PING; +const PRUint32 ipcmMessageError::MSG_TYPE = IPCM_MSG_TYPE_ERROR; +const PRUint32 ipcmMessageClientHello::MSG_TYPE = IPCM_MSG_TYPE_CLIENT_HELLO; +const PRUint32 ipcmMessageClientID::MSG_TYPE = IPCM_MSG_TYPE_CLIENT_ID; +const PRUint32 ipcmMessageClientInfo::MSG_TYPE = IPCM_MSG_TYPE_CLIENT_INFO; +const PRUint32 ipcmMessageClientAddName::MSG_TYPE = IPCM_MSG_TYPE_CLIENT_ADD_NAME; +const PRUint32 ipcmMessageClientDelName::MSG_TYPE = IPCM_MSG_TYPE_CLIENT_DEL_NAME; +const PRUint32 ipcmMessageClientAddTarget::MSG_TYPE = IPCM_MSG_TYPE_CLIENT_ADD_TARGET; +const PRUint32 ipcmMessageClientDelTarget::MSG_TYPE = IPCM_MSG_TYPE_CLIENT_DEL_TARGET; +const PRUint32 ipcmMessageQueryClientByName::MSG_TYPE = IPCM_MSG_TYPE_QUERY_CLIENT_BY_NAME; +const PRUint32 ipcmMessageQueryClientInfo::MSG_TYPE = IPCM_MSG_TYPE_QUERY_CLIENT_INFO; +const PRUint32 ipcmMessageForward::MSG_TYPE = IPCM_MSG_TYPE_FORWARD; +const PRUint32 ipcmMessageClientStatus::MSG_TYPE = IPCM_MSG_TYPE_CLIENT_STATUS; + +// +// CLIENT_INFO message +// +// +-----------------------------------------+ +// | DWORD : MSG_TYPE | +// +--------------------+--------------------+ +// | DWORD : clientID | +// +--------------------+--------------------+ +// | DWORD : requestIndex | +// +--------------------+--------------------+ +// | WORD : nameStart | WORD : nameCount | +// +--------------------+--------------------+ +// | WORD : targetStart | WORD : targetCount | +// +--------------------+--------------------+ +// | name[0] | (null byte) | +// +--------------------+--------------------+ +// . . . +// . . . +// +--------------------+--------------------+ +// | name[count - 1] | (null byte) | +// +--------------------+--------------------+ +// | target[0] | +// +-----------------------------------------+ +// . . . +// . . . +// +-----------------------------------------+ +// | target[count - 1] | +// +-----------------------------------------+ +// + +struct ipcmClientInfoHeader +{ + PRUint32 mType; + PRUint32 mID; + PRUint32 mRequestIndex; + PRUint16 mNameStart; + PRUint16 mNameCount; + PRUint16 mTargetStart; + PRUint16 mTargetCount; +}; + +ipcmMessageClientInfo::ipcmMessageClientInfo(PRUint32 cID, PRUint32 rIdx, const char *names[], const nsID *targets[]) +{ + ipcmClientInfoHeader hdr = {0}; + + hdr.mType = MSG_TYPE; + hdr.mID = cID; + hdr.mRequestIndex = rIdx; + hdr.mNameStart = sizeof(hdr); + + PRUint32 i, namesLen = 0; + + i = 0; + while (names[i]) { + namesLen += (strlen(names[i]) + 1); + ++hdr.mNameCount; + ++i; + } + + i = 0; + while (targets[i]) { + ++hdr.mTargetCount; + ++i; + } + + // + // compute target array starting offset + // + hdr.mTargetStart = hdr.mNameStart + namesLen; + + // + // compute message length + // + PRUint32 dataLen = sizeof(hdr) + namesLen + hdr.mTargetCount * sizeof(nsID); + + Init(IPCM_TARGET, NULL, dataLen); + + // + // write message data + // + SetData(0, (const char *) &hdr, sizeof(hdr)); + + PRUint32 offset = sizeof(hdr); + + for (i = 0; names[i]; ++i) { + PRUint32 len = strlen(names[i]) + 1; + SetData(offset, names[i], len); + offset += len; + } + + for (i = 0; targets[i]; ++i) { + PRUint32 len = sizeof(nsID); + SetData(offset, (const char *) targets[i], len); + offset += len; + } +} + +PRUint32 +ipcmMessageClientInfo::ClientID() const +{ + ipcmClientInfoHeader *hdr = (ipcmClientInfoHeader *) Data(); + return hdr->mID; +} + +PRUint32 +ipcmMessageClientInfo::RequestIndex() const +{ + ipcmClientInfoHeader *hdr = (ipcmClientInfoHeader *) Data(); + return hdr->mRequestIndex; +} + +PRUint32 +ipcmMessageClientInfo::NameCount() const +{ + ipcmClientInfoHeader *hdr = (ipcmClientInfoHeader *) Data(); + return hdr->mNameCount; +} + +PRUint32 +ipcmMessageClientInfo::TargetCount() const +{ + ipcmClientInfoHeader *hdr = (ipcmClientInfoHeader *) Data(); + return hdr->mTargetCount; +} + +const char * +ipcmMessageClientInfo::NextName(const char *name) const +{ + ipcmClientInfoHeader *hdr = (ipcmClientInfoHeader *) Data(); + + if (!name) + return (const char *) hdr + hdr->mNameStart; + + name += strlen(name) + 1; + if (name == (const char *) hdr + hdr->mTargetStart) + name = NULL; + return name; +} + +const nsID * +ipcmMessageClientInfo::NextTarget(const nsID *target) const +{ + ipcmClientInfoHeader *hdr = (ipcmClientInfoHeader *) Data(); + + if (!target) + return (const nsID *) (Data() + hdr->mTargetStart); + + if (++target == (const nsID *) (MsgBuf() + MsgLen())) + target = NULL; + return target; +} +#endif + +// +// FORWARD message +// +// +-------------------------+ +// | DWORD : MSG_TYPE | +// +-------------------------+ +// | clientID | +// +-------------------------+ +// | innerMsgHeader | +// +-------------------------+ +// | innerMsgData | +// +-------------------------+ +// + +ipcmMessageForward::ipcmMessageForward(PRUint32 type, + PRUint32 cID, + const nsID &target, + const char *data, + PRUint32 dataLen) +{ + int len = sizeof(ipcmMessageHeader) + // IPCM header + sizeof(cID) + // cID + IPC_MSG_HEADER_SIZE + // innerMsgHeader + dataLen; // innerMsgData + + Init(IPCM_TARGET, NULL, len); + + ipcmMessageHeader ipcmHdr = + { type, IPCM_NewRequestIndex() }; + + SetData(0, (char *) &ipcmHdr, sizeof(ipcmHdr)); + SetData(sizeof(ipcmHdr), (char *) &cID, sizeof(cID)); + + ipcMessageHeader hdr; + hdr.mLen = IPC_MSG_HEADER_SIZE + dataLen; + hdr.mVersion = IPC_MSG_VERSION; + hdr.mFlags = 0; + hdr.mTarget = target; + + SetData(sizeof(ipcmHdr) + sizeof(cID), (char *) &hdr, IPC_MSG_HEADER_SIZE); + if (data) + SetInnerData(0, data, dataLen); +} + +void +ipcmMessageForward::SetInnerData(PRUint32 offset, const char *data, PRUint32 dataLen) +{ + SetData(sizeof(ipcmMessageHeader) + 4 + IPC_MSG_HEADER_SIZE + offset, data, dataLen); +} + +PRUint32 +ipcmMessageForward::ClientID() const +{ + return ((PRUint32 *) Data())[2]; +} + +const nsID & +ipcmMessageForward::InnerTarget() const +{ + ipcMessageHeader *hdr = (ipcMessageHeader *) (Data() + 12); + return hdr->mTarget; +} + +const char * +ipcmMessageForward::InnerData() const +{ + return Data() + 12 + IPC_MSG_HEADER_SIZE; +} + +PRUint32 +ipcmMessageForward::InnerDataLen() const +{ + ipcMessageHeader *hdr = (ipcMessageHeader *) (Data() + 12); + return hdr->mLen - IPC_MSG_HEADER_SIZE; +} diff --git a/src/libs/xpcom18a4/ipc/ipcd/shared/src/ipcm.h b/src/libs/xpcom18a4/ipc/ipcd/shared/src/ipcm.h new file mode 100644 index 00000000..a6445654 --- /dev/null +++ b/src/libs/xpcom18a4/ipc/ipcd/shared/src/ipcm.h @@ -0,0 +1,502 @@ +/* ***** 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 ***** */ + +#ifndef ipcm_h__ +#define ipcm_h__ + +#include "ipcMessage.h" +#include "ipcMessagePrimitives.h" + +//----------------------------------------------------------------------------- + +// +// IPCM (IPC Manager) protocol support +// + +// The IPCM message target identifier: +extern const nsID IPCM_TARGET; + +// +// Every IPCM message has the following structure: +// +// +-----------------------------------------+ +// | (ipc message header) | +// +-----------------------------------------+ +// | DWORD : type | +// +-----------------------------------------+ +// | DWORD : requestIndex | +// +-----------------------------------------+ +// . . +// . (payload) . +// . . +// +-----------------------------------------+ +// +// where |type| is an integer uniquely identifying the message. the type is +// composed of a message class identifier and a message number. there are 3 +// message classes: +// +// ACK - acknowledging a request +// REQ - making a request +// PSH - providing unrequested, "pushed" information +// +// The requestIndex field is initialized when a request is made. An +// acknowledgement's requestIndex is equal to that of its corresponding +// request message. This enables the requesting side of the message exchange +// to match acknowledgements to requests. The requestIndex field is ignored +// for PSH messages. +// + +// The IPCM message class is stored in the most significant byte. +#define IPCM_MSG_CLASS_REQ (1 << 24) +#define IPCM_MSG_CLASS_ACK (2 << 24) +#define IPCM_MSG_CLASS_PSH (4 << 24) + +// Requests +#define IPCM_MSG_REQ_PING (IPCM_MSG_CLASS_REQ | 1) +#define IPCM_MSG_REQ_FORWARD (IPCM_MSG_CLASS_REQ | 2) +#define IPCM_MSG_REQ_CLIENT_HELLO (IPCM_MSG_CLASS_REQ | 3) +#define IPCM_MSG_REQ_CLIENT_ADD_NAME (IPCM_MSG_CLASS_REQ | 4) +#define IPCM_MSG_REQ_CLIENT_DEL_NAME (IPCM_MSG_CLASS_REQ | 5) +#define IPCM_MSG_REQ_CLIENT_ADD_TARGET (IPCM_MSG_CLASS_REQ | 6) +#define IPCM_MSG_REQ_CLIENT_DEL_TARGET (IPCM_MSG_CLASS_REQ | 7) +#define IPCM_MSG_REQ_QUERY_CLIENT_BY_NAME (IPCM_MSG_CLASS_REQ | 8) +#define IPCM_MSG_REQ_QUERY_CLIENT_NAMES (IPCM_MSG_CLASS_REQ | 9) // TODO +#define IPCM_MSG_REQ_QUERY_CLIENT_TARGETS (IPCM_MSG_CLASS_REQ | 10) // TODO + +// Acknowledgements +#define IPCM_MSG_ACK_RESULT (IPCM_MSG_CLASS_ACK | 1) +#define IPCM_MSG_ACK_CLIENT_ID (IPCM_MSG_CLASS_ACK | 2) +#define IPCM_MSG_ACK_CLIENT_NAMES (IPCM_MSG_CLASS_ACK | 3) // TODO +#define IPCM_MSG_ACK_CLIENT_TARGETS (IPCM_MSG_CLASS_ACK | 4) // TODO + +// Push messages +#define IPCM_MSG_PSH_CLIENT_STATE (IPCM_MSG_CLASS_PSH | 1) +#define IPCM_MSG_PSH_FORWARD (IPCM_MSG_CLASS_PSH | 2) + +//----------------------------------------------------------------------------- + +// +// IPCM header +// +struct ipcmMessageHeader +{ + PRUint32 mType; + PRUint32 mRequestIndex; +}; + +// +// returns IPCM message type. +// +static inline int +IPCM_GetType(const ipcMessage *msg) +{ + return ((const ipcmMessageHeader *) msg->Data())->mType; +} + +// +// return IPCM message request index. +// +static inline PRUint32 +IPCM_GetRequestIndex(const ipcMessage *msg) +{ + return ((const ipcmMessageHeader *) msg->Data())->mRequestIndex; +} + +// +// return a request index that is unique to this process. +// +NS_HIDDEN_(PRUint32) +IPCM_NewRequestIndex(); + +//----------------------------------------------------------------------------- + +// +// The IPCM protocol is detailed below: +// + +// REQUESTS + +// +// req: IPCM_MSG_REQ_PING +// ack: IPCM_MSG_ACK_RESULT +// +// A PING can be sent from either a client to the daemon, or from the daemon +// to a client. The expected acknowledgement is a RESULT message with a status +// code of 0. +// +// This request message has no payload. +// + +// +// req: IPCM_MSG_REQ_FORWARD +// ack: IPCM_MSG_ACK_RESULT +// +// A FORWARD is sent when a client wishes to send a message to another client. +// The payload of this message is another message that should be forwarded by +// the daemon's IPCM to the specified client. The expected acknowledgment is +// a RESULT message with a status code indicating success or failure. +// +// When the daemon receives a FORWARD message, it creates a PSH_FORWARD message +// and sends that on to the destination client. +// +// This request message has as its payload: +// +// +-----------------------------------------+ +// | DWORD : clientID | +// +-----------------------------------------+ +// | (innerMsgHeader) | +// +-----------------------------------------+ +// | (innerMsgData) | +// +-----------------------------------------+ +// + +// +// req: IPCM_MSG_REQ_CLIENT_HELLO +// ack: IPCM_MSG_REQ_CLIENT_ID <or> IPCM_MSG_REQ_RESULT +// +// A CLIENT_HELLO is sent when a client connects to the IPC daemon. The +// expected acknowledgement is a CLIENT_ID message informing the new client of +// its ClientID. If for some reason the IPC daemon cannot accept the new +// client, it returns a RESULT message with a failure status code. +// +// This request message has no payload. +// + +// +// req: IPCM_MSG_REQ_CLIENT_ADD_NAME +// ack: IPCM_MSG_ACK_RESULT +// +// A CLIENT_ADD_NAME is sent when a client wishes to register an additional +// name for itself. The expected acknowledgement is a RESULT message with a +// status code indicating success or failure. +// +// This request message has as its payload a null-terminated ASCII character +// string indicating the name of the client. +// + +// +// req: IPCM_MSG_REQ_CLIENT_DEL_NAME +// ack: IPCM_MSG_ACK_RESULT +// +// A CLIENT_DEL_NAME is sent when a client wishes to unregister a name that it +// has registered. The expected acknowledgement is a RESULT message with a +// status code indicating success or failure. +// +// This request message has as its payload a null-terminated ASCII character +// string indicating the name of the client. +// + +// +// req: IPCM_MSG_REQ_CLIENT_ADD_TARGET +// ack: IPCM_MSG_ACK_RESULT +// +// A CLIENT_ADD_TARGET is sent when a client wishes to register an additional +// target that it supports. The expected acknowledgement is a RESULT message +// with a status code indicating success or failure. +// +// This request message has as its payload a 128-bit UUID indicating the +// target to add. +// + +// +// req: IPCM_MSG_REQ_CLIENT_DEL_TARGET +// ack: IPCM_MSG_ACK_RESULT +// +// A CLIENT_DEL_TARGET is sent when a client wishes to unregister a target +// that it has registered. The expected acknowledgement is a RESULT message +// with a status code indicating success or failure. +// +// This request message has as its payload a 128-bit UUID indicating the +// target to remove. +// + +// +// req: IPCM_MSG_REQ_QUERY_CLIENT_BY_NAME +// ack: IPCM_MSG_ACK_CLIENT_ID <or> IPCM_MSG_ACK_RESULT +// +// A QUERY_CLIENT_BY_NAME may be sent by a client to discover the client that +// is known by a common name. If more than one client matches the name, then +// only the ID of the more recently registered client is returned. The +// expected acknowledgement is a CLIENT_ID message carrying the ID of the +// corresponding client. If no client matches the given name or if some error +// occurs, then a RESULT message with a failure status code is returned. +// +// This request message has as its payload a null-terminated ASCII character +// string indicating the client name to query. +// + +// ACKNOWLEDGEMENTS + +// +// ack: IPCM_MSG_ACK_RESULT +// +// This acknowledgement is returned to indicate a success or failure status. +// +// The payload consists of a single DWORD value. +// +// Possible status codes are listed below (negative values indicate failure +// codes): +// +#define IPCM_OK 0 // success: generic +#define IPCM_ERROR_GENERIC -1 // failure: generic +#define IPCM_ERROR_NO_CLIENT -2 // failure: client does not exist +#define IPCM_ERROR_INVALID_ARG -3 // failure: invalid request argument +#define IPCM_ERROR_NO_SUCH_DATA -4 // failure: requested data does not exist +#define IPCM_ERROR_ALREADY_EXISTS -5 // failure: data to set already exists + +// +// ack: IPCM_MSG_ACK_CLIENT_ID +// +// This acknowledgement is returned to specify a client ID. +// +// The payload consists of a single DWORD value. +// + +// PUSH MESSAGES + +// +// psh: ICPM_MSG_PSH_CLIENT_STATE +// +// This message is sent to clients to indicate the status of other clients. +// +// The payload consists of: +// +// +-----------------------------------------+ +// | DWORD : clientID | +// +-----------------------------------------+ +// | DWORD : clientState | +// +-----------------------------------------+ +// +// where, clientState is one of the following values indicating whether the +// client has recently connected (up) or disconnected (down): +// +#define IPCM_CLIENT_STATE_UP 1 +#define IPCM_CLIENT_STATE_DOWN 2 + +// +// psh: IPCM_MSG_PSH_FORWARD +// +// This message is sent by the daemon to a client on behalf of another client. +// The recipient is expected to unpack the contained message and process it. +// +// The payload of this message matches the payload of IPCM_MSG_REQ_FORWARD, +// with the exception that the clientID field is set to the clientID of the +// sender of the IPCM_MSG_REQ_FORWARD message. +// + +//----------------------------------------------------------------------------- + +// +// NOTE: This file declares some helper classes that simplify constructing +// and parsing IPCM messages. Each class subclasses ipcMessage, but +// adds no additional member variables. |operator new| should be used +// to allocate one of the IPCM helper classes, e.g.: +// +// ipcMessage *msg = new ipcmMessageClientHello("foo"); +// +// Given an arbitrary ipcMessage, it can be parsed using logic similar +// to the following: +// +// void func(const ipcMessage *unknown) +// { +// if (unknown->Topic().Equals(IPCM_TARGET)) { +// if (IPCM_GetMsgType(unknown) == IPCM_MSG_TYPE_CLIENT_ID) { +// ipcMessageCast<ipcmMessageClientID> msg(unknown); +// printf("Client ID: %u\n", msg->ClientID()); +// } +// } +// } +// + +// REQUESTS + +class ipcmMessagePing : public ipcMessage_DWORD_DWORD +{ +public: + ipcmMessagePing() + : ipcMessage_DWORD_DWORD( + IPCM_TARGET, + IPCM_MSG_REQ_PING, + IPCM_NewRequestIndex()) {} +}; + +class ipcmMessageForward : public ipcMessage +{ +public: + // @param type the type of this message: IPCM_MSG_{REQ,PSH}_FORWARD + // @param clientID the client id of the sender or receiver + // @param target the message target + // @param data the message data + // @param dataLen the message data length + ipcmMessageForward(PRUint32 type, + PRUint32 clientID, + const nsID &target, + const char *data, + PRUint32 dataLen) NS_HIDDEN; + + // set inner message data, constrained to the data length passed + // to this class's constructor. + NS_HIDDEN_(void) SetInnerData(PRUint32 offset, const char *data, PRUint32 dataLen); + + NS_HIDDEN_(PRUint32) ClientID() const; + NS_HIDDEN_(const nsID &) InnerTarget() const; + NS_HIDDEN_(const char *) InnerData() const; + NS_HIDDEN_(PRUint32) InnerDataLen() const; +}; + +class ipcmMessageClientHello : public ipcMessage_DWORD_DWORD +{ +public: + ipcmMessageClientHello() + : ipcMessage_DWORD_DWORD( + IPCM_TARGET, + IPCM_MSG_REQ_CLIENT_HELLO, + IPCM_NewRequestIndex()) {} +}; + +class ipcmMessageClientAddName : public ipcMessage_DWORD_DWORD_STR +{ +public: + ipcmMessageClientAddName(const char *name) + : ipcMessage_DWORD_DWORD_STR( + IPCM_TARGET, + IPCM_MSG_REQ_CLIENT_ADD_NAME, + IPCM_NewRequestIndex(), + name) {} + + const char *Name() const { return Third(); } +}; + +class ipcmMessageClientDelName : public ipcMessage_DWORD_DWORD_STR +{ +public: + ipcmMessageClientDelName(const char *name) + : ipcMessage_DWORD_DWORD_STR( + IPCM_TARGET, + IPCM_MSG_REQ_CLIENT_DEL_NAME, + IPCM_NewRequestIndex(), + name) {} + + const char *Name() const { return Third(); } +}; + +class ipcmMessageClientAddTarget : public ipcMessage_DWORD_DWORD_ID +{ +public: + ipcmMessageClientAddTarget(const nsID &target) + : ipcMessage_DWORD_DWORD_ID( + IPCM_TARGET, + IPCM_MSG_REQ_CLIENT_ADD_TARGET, + IPCM_NewRequestIndex(), + target) {} + + const nsID &Target() const { return Third(); } +}; + +class ipcmMessageClientDelTarget : public ipcMessage_DWORD_DWORD_ID +{ +public: + ipcmMessageClientDelTarget(const nsID &target) + : ipcMessage_DWORD_DWORD_ID( + IPCM_TARGET, + IPCM_MSG_REQ_CLIENT_ADD_TARGET, + IPCM_NewRequestIndex(), + target) {} + + const nsID &Target() const { return Third(); } +}; + +class ipcmMessageQueryClientByName : public ipcMessage_DWORD_DWORD_STR +{ +public: + ipcmMessageQueryClientByName(const char *name) + : ipcMessage_DWORD_DWORD_STR( + IPCM_TARGET, + IPCM_MSG_REQ_QUERY_CLIENT_BY_NAME, + IPCM_NewRequestIndex(), + name) {} + + const char *Name() const { return Third(); } + PRUint32 RequestIndex() const { return Second(); } +}; + +// ACKNOWLEDGEMENTS + +class ipcmMessageResult : public ipcMessage_DWORD_DWORD_DWORD +{ +public: + ipcmMessageResult(PRUint32 requestIndex, PRInt32 status) + : ipcMessage_DWORD_DWORD_DWORD( + IPCM_TARGET, + IPCM_MSG_ACK_RESULT, + requestIndex, + (PRUint32) status) {} + + PRInt32 Status() const { return (PRInt32) Third(); } +}; + +class ipcmMessageClientID : public ipcMessage_DWORD_DWORD_DWORD +{ +public: + ipcmMessageClientID(PRUint32 requestIndex, PRUint32 clientID) + : ipcMessage_DWORD_DWORD_DWORD( + IPCM_TARGET, + IPCM_MSG_ACK_CLIENT_ID, + requestIndex, + clientID) {} + + PRUint32 ClientID() const { return Third(); } +}; + +// PUSH MESSAGES + +class ipcmMessageClientState : public ipcMessage_DWORD_DWORD_DWORD_DWORD +{ +public: + ipcmMessageClientState(PRUint32 clientID, PRUint32 clientStatus) + : ipcMessage_DWORD_DWORD_DWORD_DWORD( + IPCM_TARGET, + IPCM_MSG_PSH_CLIENT_STATE, + 0, + clientID, + clientStatus) {} + + PRUint32 ClientID() const { return Third(); } + PRUint32 ClientState() const { return Fourth(); } +}; + +#endif // !ipcm_h__ diff --git a/src/libs/xpcom18a4/ipc/ipcd/test/.cvsignore b/src/libs/xpcom18a4/ipc/ipcd/test/.cvsignore new file mode 100644 index 00000000..d1a44d27 --- /dev/null +++ b/src/libs/xpcom18a4/ipc/ipcd/test/.cvsignore @@ -0,0 +1,2 @@ +Makefile +TestIPC diff --git a/src/libs/xpcom18a4/ipc/ipcd/test/Makefile.in b/src/libs/xpcom18a4/ipc/ipcd/test/Makefile.in new file mode 100644 index 00000000..bad636f3 --- /dev/null +++ b/src/libs/xpcom18a4/ipc/ipcd/test/Makefile.in @@ -0,0 +1,69 @@ +# vim: noexpandtab ts=4 sw=4 +# ***** 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 + +DIRS = module + +MODULE = ipcd_test +REQUIRES = \ + xpcom \ + string \ + ipcd \ + $(NULL) + +CPPSRCS = \ + TestIPC.cpp \ + $(NULL) + +SIMPLE_PROGRAMS = $(CPPSRCS:.cpp=$(BIN_SUFFIX)) + +include $(topsrcdir)/config/config.mk + +LIBS = \ + $(EXTRA_DSO_LIBS) \ + $(XPCOM_LIBS) \ + $(NSPR_LIBS) \ + $(NULL) + +include $(topsrcdir)/config/rules.mk diff --git a/src/libs/xpcom18a4/ipc/ipcd/test/TestIPC.cpp b/src/libs/xpcom18a4/ipc/ipcd/test/TestIPC.cpp new file mode 100644 index 00000000..1100d674 --- /dev/null +++ b/src/libs/xpcom18a4/ipc/ipcd/test/TestIPC.cpp @@ -0,0 +1,338 @@ +/* ***** 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 "ipcIMessageObserver.h" +#include "ipcILockService.h" +#include "ipcCID.h" +#include "ipcLockCID.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 ipcIService *gIpcServ = nsnull; +static ipcILockService *gIpcLockServ = nsnull; + +static void +SendMsg(ipcIService *ipc, PRUint32 cID, const nsID &target, const char *data, PRUint32 dataLen, PRBool sync = PR_FALSE) +{ + printf("*** sending message: [to-client=%u dataLen=%u]\n", cID, dataLen); + + nsresult rv; + + rv = ipc->SendMessage(cID, target, (const PRUint8 *) data, dataLen); + if (NS_FAILED(rv)) { + printf("*** sending message failed: rv=%x\n", rv); + return; + } + + if (sync) { + rv = ipc->WaitMessage(cID, target, nsnull, PR_UINT32_MAX); + if (NS_FAILED(rv)) + printf("*** waiting for message failed: rv=%x\n", rv); + } +} + +//----------------------------------------------------------------------------- + +class myIpcMessageObserver : public ipcIMessageObserver +{ +public: + NS_DECL_ISUPPORTS + NS_DECL_IPCIMESSAGEOBSERVER +}; +NS_IMPL_ISUPPORTS1(myIpcMessageObserver, ipcIMessageObserver) + +NS_IMETHODIMP +myIpcMessageObserver::OnMessageAvailable(PRUint32 sender, const nsID &target, const PRUint8 *data, PRUint32 dataLen) +{ + printf("*** got message: [sender=%u data=%s]\n", sender, (const char *) data); + return NS_OK; +} + +//----------------------------------------------------------------------------- + +#if 0 +class myIpcClientQueryHandler : public ipcIClientQueryHandler +{ +public: + NS_DECL_ISUPPORTS + NS_DECL_IPCICLIENTQUERYHANDLER +}; + +NS_IMPL_ISUPPORTS1(myIpcClientQueryHandler, ipcIClientQueryHandler) + +NS_IMETHODIMP +myIpcClientQueryHandler::OnQueryComplete(PRUint32 aQueryID, + nsresult aStatus, + PRUint32 aClientID, + const char **aNames, + PRUint32 aNameCount, + const nsID **aTargets, + PRUint32 aTargetCount) +{ + printf("*** query complete [queryID=%u status=0x%x clientID=%u]\n", + aQueryID, aStatus, 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; +} +#endif + +//----------------------------------------------------------------------------- + +#if 0 +class myIpcLockNotify : public ipcILockNotify +{ +public: + NS_DECL_ISUPPORTS + NS_DECL_IPCILOCKNOTIFY +}; + +NS_IMPL_ISUPPORTS1(myIpcLockNotify, ipcILockNotify) + +NS_IMETHODIMP +myIpcLockNotify::OnAcquireLockComplete(const char *lockName, nsresult status) +{ + printf("*** OnAcquireLockComplete [lock=%s status=%x]\n", lockName, status); + gIpcLockServ->ReleaseLock(lockName); + return NS_OK; +} +#endif + +//----------------------------------------------------------------------------- + +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"); + + printf("*** getting ipc service\n"); + nsCOMPtr<ipcIService> ipcServ(do_GetService(IPC_SERVICE_CONTRACTID, &rv)); + RETURN_IF_FAILED(rv, "do_GetService(ipcServ)"); + NS_ADDREF(gIpcServ = ipcServ); + + if (argc > 1) { + printf("*** using client name [%s]\n", argv[1]); + gIpcServ->AddName(argv[1]); + } + + ipcServ->DefineTarget(kTestTargetID, new myIpcMessageObserver(), PR_TRUE); + + 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, sizeof(data), PR_TRUE); + +// PRUint32 queryID; +// nsCOMPtr<ipcIClientQueryHandler> handler(new myIpcClientQueryHandler()); +// ipcServ->QueryClientByName("foopy", handler, PR_FALSE, &queryID); + + PRUint32 foopyID; + nsresult foopyRv = ipcServ->ResolveClientName("foopy", &foopyID); + printf("*** query for 'foopy' returned [rv=%x id=%u]\n", foopyRv, foopyID); + + if (NS_SUCCEEDED(foopyRv)) { + const char hello[] = "hello friend!"; + SendMsg(ipcServ, foopyID, kTestTargetID, hello, sizeof(hello)); + } + + // + // test lock service + // + nsCOMPtr<ipcILockService> lockService = do_GetService(IPC_LOCKSERVICE_CONTRACTID, &rv); + RETURN_IF_FAILED(rv, "do_GetService(ipcLockServ)"); + NS_ADDREF(gIpcLockServ = lockService); + + //nsCOMPtr<ipcILockNotify> notify(new myIpcLockNotify()); + gIpcLockServ->AcquireLock("blah", PR_TRUE); + + rv = gIpcLockServ->AcquireLock("foo", PR_TRUE); + printf("*** sync AcquireLock returned [rv=%x]\n", rv); + + PLEvent *ev; + while (gKeepRunning) { + gEventQ->WaitForEvent(&ev); + gEventQ->HandleEvent(ev); + } + + NS_RELEASE(gIpcServ); + + printf("*** processing remaining events\n"); + + // process any remaining events + 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; +} diff --git a/src/libs/xpcom18a4/ipc/ipcd/test/module/.cvsignore b/src/libs/xpcom18a4/ipc/ipcd/test/module/.cvsignore new file mode 100644 index 00000000..f3c7a7c5 --- /dev/null +++ b/src/libs/xpcom18a4/ipc/ipcd/test/module/.cvsignore @@ -0,0 +1 @@ +Makefile diff --git a/src/libs/xpcom18a4/ipc/ipcd/test/module/Makefile.in b/src/libs/xpcom18a4/ipc/ipcd/test/module/Makefile.in new file mode 100644 index 00000000..aec7c169 --- /dev/null +++ b/src/libs/xpcom18a4/ipc/ipcd/test/module/Makefile.in @@ -0,0 +1,81 @@ +# vim: noexpandtab ts=4 sw=4 +# ***** 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 = ipcd_test +LIBRARY_NAME = testmodule +SHORT_LIBNAME = testmod +MODULE_NAME = ipcd_test + +FORCE_SHARED_LIB = 1 +NO_DIST_INSTALL = 1 + +REQUIRES = \ + xpcom \ + ipcd \ + $(NULL) + +CPPSRCS = TestModule.cpp + +LOCAL_INCLUDES = \ + -I$(srcdir)/../common \ + $(NULL) + +EXTRA_DSO_LDOPTS = \ + $(LIBS_DIR) \ + $(NSPR_LIBS) \ + $(EXTRA_DSO_LIBS) \ + $(NULL) + +include $(topsrcdir)/config/rules.mk + +_IPC_FILES = \ + $(DLL_PREFIX)$(LIBRARY_NAME)$(DLL_SUFFIX) \ + $(NULL) + +libs:: $(_IPC_FILES) + $(INSTALL) $^ $(DIST)/bin/ipc/modules + +install:: $(_IPC_FILES) + $(SYSINSTALL) $(IFLAGS1) $^ $(DESTDIR)$(mozappdir)/ipc/modules diff --git a/src/libs/xpcom18a4/ipc/ipcd/test/module/TestModule.cpp b/src/libs/xpcom18a4/ipc/ipcd/test/module/TestModule.cpp new file mode 100644 index 00000000..fbd78867 --- /dev/null +++ b/src/libs/xpcom18a4/ipc/ipcd/test/module/TestModule.cpp @@ -0,0 +1,62 @@ +#include <stdio.h> +#include "ipcModuleUtil.h" + +#define TEST_MODULE_ID \ +{ /* e628fc6e-a6a7-48c7-adba-f241d1128fb8 */ \ + 0xe628fc6e, \ + 0xa6a7, \ + 0x48c7, \ + {0xad, 0xba, 0xf2, 0x41, 0xd1, 0x12, 0x8f, 0xb8} \ +} +static const nsID kTestModuleID = TEST_MODULE_ID; + +struct TestModule +{ + static void Init() + { + printf("*** TestModule::Init\n"); + } + + static void Shutdown() + { + printf("*** TestModule::Shutdown\n"); + } + + static void HandleMsg(ipcClientHandle client, + const nsID &target, + const void *data, + PRUint32 dataLen) + { + printf("*** TestModule::HandleMsg [%s]\n", (const char *) data); + + static const char buf[] = "pong"; + IPC_SendMsg(client, kTestModuleID, buf, sizeof(buf)); + } + + static void ClientUp(ipcClientHandle client) + { + printf("*** TestModule::ClientUp [%u]\n", IPC_GetClientID(client)); + } + + static void ClientDown(ipcClientHandle client) + { + printf("*** TestModule::ClientDown [%u]\n", IPC_GetClientID(client)); + } +}; + +static ipcModuleMethods gTestMethods = +{ + IPC_MODULE_METHODS_VERSION, + TestModule::Init, + TestModule::Shutdown, + TestModule::HandleMsg, + TestModule::ClientUp, + TestModule::ClientDown +}; + +static ipcModuleEntry gTestModuleEntry[] = +{ + { TEST_MODULE_ID, &gTestMethods } +}; + +IPC_IMPL_GETMODULES(TestModule, gTestModuleEntry) diff --git a/src/libs/xpcom18a4/ipc/ipcd/util/.cvsignore b/src/libs/xpcom18a4/ipc/ipcd/util/.cvsignore new file mode 100644 index 00000000..f3c7a7c5 --- /dev/null +++ b/src/libs/xpcom18a4/ipc/ipcd/util/.cvsignore @@ -0,0 +1 @@ +Makefile diff --git a/src/libs/xpcom18a4/ipc/ipcd/util/Makefile.in b/src/libs/xpcom18a4/ipc/ipcd/util/Makefile.in new file mode 100644 index 00000000..323f87f8 --- /dev/null +++ b/src/libs/xpcom18a4/ipc/ipcd/util/Makefile.in @@ -0,0 +1,49 @@ +# vim: noexpandtab ts=4 sw=4 +# ***** 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) 2003 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# Conrad Carlen <ccarlen@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 = ipcd +DIRS = public src + +include $(topsrcdir)/config/rules.mk diff --git a/src/libs/xpcom18a4/ipc/ipcd/util/public/.cvsignore b/src/libs/xpcom18a4/ipc/ipcd/util/public/.cvsignore new file mode 100644 index 00000000..f3c7a7c5 --- /dev/null +++ b/src/libs/xpcom18a4/ipc/ipcd/util/public/.cvsignore @@ -0,0 +1 @@ +Makefile diff --git a/src/libs/xpcom18a4/ipc/ipcd/util/public/Makefile.in b/src/libs/xpcom18a4/ipc/ipcd/util/public/Makefile.in new file mode 100644 index 00000000..84d980c4 --- /dev/null +++ b/src/libs/xpcom18a4/ipc/ipcd/util/public/Makefile.in @@ -0,0 +1,53 @@ +# vim: noexpandtab ts=4 sw=4 +# ***** 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) 2003 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# Conrad Carlen <ccarlen@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 = ipcd + +EXPORTS = \ + ipcMessageReader.h \ + ipcMessageWriter.h \ + $(NULL) + +include $(topsrcdir)/config/rules.mk diff --git a/src/libs/xpcom18a4/ipc/ipcd/util/public/ipcMessageReader.h b/src/libs/xpcom18a4/ipc/ipcd/util/public/ipcMessageReader.h new file mode 100644 index 00000000..f93bfa20 --- /dev/null +++ b/src/libs/xpcom18a4/ipc/ipcd/util/public/ipcMessageReader.h @@ -0,0 +1,89 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* ***** 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) 2003 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Conrad Carlen <ccarlen@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 ***** */ + +#ifndef ipcMessageReader_h__ +#define ipcMessageReader_h__ + +#include "prtypes.h" + +//***************************************************************************** +// ipcMessageReader +// +// Reads from a const buffer supplied to it. Does not take ownership of the +// buffer. Bytes are read in native endianess, as ipcMessageWriter writes them +// in native endianess. +//***************************************************************************** + +class ipcMessageReader +{ +public: + ipcMessageReader(const PRUint8* inBuffer, PRUint32 bufferSize) : + mBuf(inBuffer), mBufEnd(inBuffer + bufferSize), + mBufPtr(mBuf), + mError(PR_FALSE) + { } + + ~ipcMessageReader() + { } + + // Returns PR_TRUE if an error has occured at any point + // during the lifetime of this object. Any read operation + // will safely return 0 on an error condition. + PRBool HasError() + { return mError; } + + PRUint8 GetInt8(); + PRUint16 GetInt16(); + PRUint32 GetInt32(); + PRInt32 GetBytes(void* destBuffer, PRInt32 n); + + // Returns data at the current read position. + const PRUint8* GetPtr() + { return mBufPtr; } + + // Returns PR_TRUE if the new position is within the buffer. + // Returns PR_FALSE and sets error state if not. + PRBool AdvancePtr(PRInt32 n); + +private: + const PRUint8 *mBuf, *mBufEnd; + const PRUint8 *mBufPtr; + PRBool mError; +}; + +#endif // ipcMessageReader_h__ diff --git a/src/libs/xpcom18a4/ipc/ipcd/util/public/ipcMessageWriter.h b/src/libs/xpcom18a4/ipc/ipcd/util/public/ipcMessageWriter.h new file mode 100644 index 00000000..8e0a8178 --- /dev/null +++ b/src/libs/xpcom18a4/ipc/ipcd/util/public/ipcMessageWriter.h @@ -0,0 +1,99 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* ***** 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) 2003 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Conrad Carlen <ccarlen@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 ***** */ + +#ifndef ipcMessageWriter_h__ +#define ipcMessageWriter_h__ + +#include "prtypes.h" + +//***************************************************************************** +// ipcMessageWriter +// +// Creates a block of memory and resizes it in order to hold writes. The +// block will be freed when this object is destroyed. Bytes are written in +// native endianess, as ipcMessageReader reads them in native endianess. +//***************************************************************************** + +class ipcMessageWriter +{ +public: + ipcMessageWriter(PRUint32 initialCapacity) : + mBuf(NULL), + mBufPtr(NULL), mBufEnd(NULL), + mCapacity(0), + mError(PR_FALSE) + { + EnsureCapacity(initialCapacity); + } + + ~ipcMessageWriter(); + + // Returns PR_TRUE if an error has occured at any point + // during the lifetime of this object, due to the buffer + // not being able to be grown to the required size. + PRBool HasError() + { return mError; } + + void PutInt8(PRUint8 val); + void PutInt16(PRUint16 val); + void PutInt32(PRUint32 val); + PRUint32 PutBytes(const void* src, PRUint32 n); + + // Returns the beginning of the buffer. Do not free this. + PRUint8* GetBuffer() + { return mBuf; } + + PRInt32 GetSize() + { return mBufPtr - mBuf; } + +private: + PRBool EnsureCapacity(PRInt32 sizeNeeded) + { + return (mBuf && ((mBufPtr + sizeNeeded) <= mBufEnd)) ? + PR_TRUE : GrowCapacity(sizeNeeded); + } + PRBool GrowCapacity(PRInt32 sizeNeeded); + +private: + PRUint8 *mBuf; + PRUint8 *mBufPtr, *mBufEnd; + PRInt32 mCapacity; + PRBool mError; +}; + +#endif // ipcMessageWriter_h__ diff --git a/src/libs/xpcom18a4/ipc/ipcd/util/src/.cvsignore b/src/libs/xpcom18a4/ipc/ipcd/util/src/.cvsignore new file mode 100644 index 00000000..f3c7a7c5 --- /dev/null +++ b/src/libs/xpcom18a4/ipc/ipcd/util/src/.cvsignore @@ -0,0 +1 @@ +Makefile diff --git a/src/libs/xpcom18a4/ipc/ipcd/util/src/Makefile.in b/src/libs/xpcom18a4/ipc/ipcd/util/src/Makefile.in new file mode 100644 index 00000000..6ebca736 --- /dev/null +++ b/src/libs/xpcom18a4/ipc/ipcd/util/src/Makefile.in @@ -0,0 +1,56 @@ +# vim: noexpandtab ts=4 sw=4 +# ***** 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) 2003 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# Conrad Carlen <ccarlen@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 = ipcd +LIBRARY_NAME = ipcdutil_s +FORCE_STATIC_LIB = 1 +MODULE_NAME = ipcd + + +CPPSRCS = \ + ipcMessageReader.cpp \ + ipcMessageWriter.cpp + +include $(topsrcdir)/config/rules.mk diff --git a/src/libs/xpcom18a4/ipc/ipcd/util/src/ipcMessageReader.cpp b/src/libs/xpcom18a4/ipc/ipcd/util/src/ipcMessageReader.cpp new file mode 100644 index 00000000..87279df2 --- /dev/null +++ b/src/libs/xpcom18a4/ipc/ipcd/util/src/ipcMessageReader.cpp @@ -0,0 +1,100 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* ***** 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) 2003 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Conrad Carlen <ccarlen@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 "ipcMessageReader.h" +#include <string.h> + +//***************************************************************************** +// ipcMessageReader +//***************************************************************************** + +PRUint8 ipcMessageReader::GetInt8() +{ + if (mBufPtr < mBufEnd) + return *mBufPtr++; + mError = PR_TRUE; + return 0; +} + +// GetInt16 and GetInt32 go to pains to avoid unaligned memory accesses that +// are larger than a byte. On some platforms, that causes a performance penalty. +// On other platforms, Tru64 for instance, it's an error. + +PRUint16 ipcMessageReader::GetInt16() +{ + if (mBufPtr + sizeof(PRUint16) <= mBufEnd) { + PRUint8 temp[2] = { mBufPtr[0], mBufPtr[1] }; + mBufPtr += sizeof(PRUint16); + return *(PRUint16*)temp; + } + mError = PR_TRUE; + return 0; +} + +PRUint32 ipcMessageReader::GetInt32() +{ + if (mBufPtr + sizeof(PRUint32) <= mBufEnd) { + PRUint8 temp[4] = { mBufPtr[0], mBufPtr[1], mBufPtr[2], mBufPtr[3] }; + mBufPtr += sizeof(PRUint32); + return *(PRUint32*)temp; + } + mError = PR_TRUE; + return 0; +} + +PRInt32 ipcMessageReader::GetBytes(void* destBuffer, PRInt32 n) +{ + if (mBufPtr + n <= mBufEnd) { + memcpy(destBuffer, mBufPtr, n); + mBufPtr += n; + return n; + } + mError = PR_TRUE; + return 0; +} + +PRBool ipcMessageReader::AdvancePtr(PRInt32 n) +{ + const PRUint8 *newPtr = mBufPtr + n; + if (newPtr >= mBuf && newPtr <= mBufEnd) { + mBufPtr = newPtr; + return PR_TRUE; + } + mError = PR_TRUE; + return PR_FALSE; +} diff --git a/src/libs/xpcom18a4/ipc/ipcd/util/src/ipcMessageWriter.cpp b/src/libs/xpcom18a4/ipc/ipcd/util/src/ipcMessageWriter.cpp new file mode 100644 index 00000000..4af0dcba --- /dev/null +++ b/src/libs/xpcom18a4/ipc/ipcd/util/src/ipcMessageWriter.cpp @@ -0,0 +1,130 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* ***** 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) 2003 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Conrad Carlen <ccarlen@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 "ipcMessageWriter.h" +#include "prmem.h" +#include <string.h> +#ifdef VBOX_USE_IPRT_IN_XPCOM +# include <iprt/mem.h> +#endif + +//***************************************************************************** +// ipcMessageWriter +//***************************************************************************** + +ipcMessageWriter::~ipcMessageWriter() +{ + if (mBuf) +#ifdef VBOX_USE_IPRT_IN_XPCOM + RTMemFree(mBuf); +#else + free(mBuf); +#endif +} + +void ipcMessageWriter::PutInt8(PRUint8 val) +{ + if (EnsureCapacity(sizeof(PRUint8))) + *mBufPtr++ = val; +} + +// PutInt16 and PutInt32 go to pains to avoid unaligned memory accesses that +// are larger than a byte. On some platforms, that causes a performance penalty. +// On other platforms, Tru64 for instance, it's an error. + +void ipcMessageWriter::PutInt16(PRUint16 val) +{ + if (EnsureCapacity(sizeof(PRUint16))) { + PRUint8 temp[2]; + *(PRUint16*)temp = val; + *mBufPtr++ = temp[0]; + *mBufPtr++ = temp[1]; + } +} + +void ipcMessageWriter::PutInt32(PRUint32 val) +{ + if (EnsureCapacity(sizeof(PRUint32))) { + PRUint8 temp[4]; + *(PRUint32*)temp = val; + *mBufPtr++ = temp[0]; + *mBufPtr++ = temp[1]; + *mBufPtr++ = temp[2]; + *mBufPtr++ = temp[3]; + } +} + +PRUint32 ipcMessageWriter::PutBytes(const void* src, PRUint32 n) +{ + if (EnsureCapacity(n)) { + memcpy(mBufPtr, src, n); + mBufPtr += n; + return n; + } + return 0; +} + +PRBool ipcMessageWriter::GrowCapacity(PRInt32 sizeNeeded) +{ + if (sizeNeeded < 0) + return PR_FALSE; + PRInt32 newCapacity = (mBufPtr - mBuf) + sizeNeeded; + if (mCapacity == 0) + mCapacity = newCapacity; + else + { + while (newCapacity > mCapacity && (mCapacity << 1) > 0) + mCapacity <<= 1; + if (newCapacity > mCapacity) // if we broke out because of rollover + return PR_FALSE; + } + + PRInt32 curPos = mBufPtr - mBuf; +#ifdef VBOX_USE_IPRT_IN_XPCOM + mBuf = (PRUint8*)RTMemRealloc(mBuf, mCapacity); +#else + mBuf = (PRUint8*)realloc(mBuf, mCapacity); +#endif + if (!mBuf) { + mError = PR_TRUE; + return PR_FALSE; + } + mBufPtr = mBuf + curPos; + mBufEnd = mBuf + mCapacity; + return PR_TRUE; +} |