diff options
Diffstat (limited to 'src/libs/xpcom18a4/ipc/ipcd/client')
19 files changed, 3920 insertions, 0 deletions
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 + } +} |