diff --git a/mozilla/modules/ipc/build/ipcModule.cpp b/mozilla/modules/ipc/build/ipcModule.cpp index 9527bd4f9fd..59e7c3a676e 100644 --- a/mozilla/modules/ipc/build/ipcModule.cpp +++ b/mozilla/modules/ipc/build/ipcModule.cpp @@ -37,36 +37,41 @@ #include "nsIGenericFactory.h" #include "ipcService.h" -#include "ipcSocketProvider.h" #include "ipcCID.h" +#include "ipcConfig.h" -//////////////////////////////////////////////////////////////////////// +//----------------------------------------------------------------------------- // Define the contructor function for the objects // // NOTE: This creates an instance of objects by using the default constructor -// +//----------------------------------------------------------------------------- NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(ipcService, Init) -NS_GENERIC_FACTORY_CONSTRUCTOR(ipcSocketProvider) -//////////////////////////////////////////////////////////////////////// +#ifndef IPC_USE_INET +#include "ipcSocketProvider.h" +NS_GENERIC_FACTORY_CONSTRUCTOR(ipcSocketProvider) +#endif + +//----------------------------------------------------------------------------- // 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, }, +#ifndef IPC_USE_INET { IPC_SOCKETPROVIDER_CLASSNAME, IPC_SOCKETPROVIDER_CID, NS_NETWORK_SOCKET_CONTRACTID_PREFIX "ipc", ipcSocketProviderConstructor, }, +#endif }; - -//////////////////////////////////////////////////////////////////////// +//----------------------------------------------------------------------------- // Implement the NSGetModule() exported function for your module // and the entire implementation of the module object. -// +//----------------------------------------------------------------------------- NS_IMPL_NSGETMODULE(ipcModule, components) diff --git a/mozilla/modules/ipc/common/ipcConfig.h b/mozilla/modules/ipc/common/ipcConfig.h index 6c235148b55..40bb1ea533d 100644 --- a/mozilla/modules/ipc/common/ipcConfig.h +++ b/mozilla/modules/ipc/common/ipcConfig.h @@ -52,9 +52,15 @@ #define IPC_PORT 14400 #define IPC_SOCKET_TYPE nsnull #define IPC_DEFAULT_SOCKET_PATH "" + +#ifdef XP_UNIX +#define IPC_DAEMON_APP_NAME "mozipcd" +#define IPC_PATH_SEP_CHAR '/' +#else #define IPC_DAEMON_APP_NAME "mozipcd.exe" #define IPC_PATH_SEP_CHAR '\\' -#define IPC_PATH_SEP_CHAR_S "\\" +#endif + #else // // use UNIX domain socket @@ -64,7 +70,6 @@ #define IPC_DEFAULT_SOCKET_PATH "/tmp/sock" #define IPC_DAEMON_APP_NAME "mozipcd" #define IPC_PATH_SEP_CHAR '/' -#define IPC_PATH_SEP_CHAR_S "/" #endif diff --git a/mozilla/modules/ipc/daemon/ipcd.cpp b/mozilla/modules/ipc/daemon/ipcd.cpp index ba04c513dff..a5effcfa29f 100644 --- a/mozilla/modules/ipc/daemon/ipcd.cpp +++ b/mozilla/modules/ipc/daemon/ipcd.cpp @@ -42,7 +42,10 @@ #ifdef XP_UNIX #include #include +#include +#include #include +#include "prprf.h" #endif #include "prio.h" @@ -63,6 +66,127 @@ #include "prnetdb.h" #endif +//----------------------------------------------------------------------------- +// ipc directory and locking... +//----------------------------------------------------------------------------- + +#ifdef XP_UNIX + +static char *ipcDir; +static char *ipcSockFile; +static char *ipcLockFile; +static int ipcLockFD; + +static PRBool AcquireDaemonLock() +{ + const char lockName[] = "lock"; + + int dirLen = strlen(ipcDir); + int len = dirLen // ipcDir + + 1 // "/" + + sizeof(lockName); // "lock" + + ipcLockFile = (char *) malloc(len); + memcpy(ipcLockFile, ipcDir, dirLen); + ipcLockFile[dirLen] = '/'; + memcpy(ipcLockFile + dirLen + 1, lockName, sizeof(lockName)); + + ipcLockFD = open(ipcLockFile, O_WRONLY|O_CREAT|O_TRUNC, S_IWUSR|S_IRUSR); + if (ipcLockFD == -1) + return PR_FALSE; + + // + // we use fcntl for locking. assumption: filesystem should be local. + // this API is nice because the lock will be automatically released + // when the process dies. it will also be released when the file + // descriptor is closed. + // + struct flock lock; + lock.l_type = F_WRLCK; + lock.l_start = 0; + lock.l_len = 0; + lock.l_whence = SEEK_SET; + if (fcntl(ipcLockFD, F_SETLK, &lock) == -1) + return PR_FALSE; + + // + // write our PID into the lock file (this just seems like a good idea... + // no real purpose otherwise). + // + char buf[256]; + int nb = PR_snprintf(buf, sizeof(buf), "%u\n", (unsigned long) getpid()); + write(ipcLockFD, buf, nb); + + return PR_TRUE; +} + +static PRBool InitDaemonDir(const char *socketPath) +{ + LOG(("InitDaemonDir [sock=%s]\n", socketPath)); + + ipcSockFile = PL_strdup(socketPath); + ipcDir = PL_strdup(socketPath); + + // + // make sure IPC directory exists (XXX this should be recursive) + // + char *p = strrchr(ipcDir, '/'); + if (p) + p[0] = '\0'; + mkdir(ipcDir, 0700); + + // + // if we can't acquire the daemon lock, then another daemon + // must be active, so bail. + // + if (!AcquireDaemonLock()) + return PR_FALSE; + + // + // delete an existing socket to prevent bind from failing. + // + unlink(socketPath); + + return PR_TRUE; +} + +static void ShutdownDaemonDir() +{ + LOG(("ShutdownDaemonDir [sock=%s]\n", ipcSockFile)); + + // + // unlink files from directory before giving up lock; otherwise, we'd be + // unable to delete it w/o introducing a race condition. + // + unlink(ipcLockFile); + unlink(ipcSockFile); + + // + // should be able to remove the ipc directory now. + // + rmdir(ipcDir); + + // + // this removes the advisory lock, allowing other processes to acquire it. + // + close(ipcLockFD); + + // + // free allocated memory + // + PL_strfree(ipcDir); + PL_strfree(ipcSockFile); + free(ipcLockFile); + + ipcDir = NULL; + ipcSockFile = NULL; + ipcLockFile = NULL; +} + +#endif + +//----------------------------------------------------------------------------- +// poll list //----------------------------------------------------------------------------- // upper limit on the number of active connections @@ -247,7 +371,7 @@ int main(int argc, char **argv) IPC_InitLog("###"); #endif -start: +//start: #ifdef IPC_USE_INET listen_fd = PR_OpenTCPSocket(PR_AF_INET); if (!listen_fd) { @@ -258,17 +382,20 @@ start: PR_InitializeNetAddr(PR_IpAddrLoopback, IPC_PORT, &addr); #else + listen_fd = PR_OpenTCPSocket(PR_AF_LOCAL); + if (!listen_fd) { + LOG(("PR_OpenUDPSocket failed [%d]\n", PR_GetError())); + return -1; + } + const char *socket_path; if (argc < 2) socket_path = IPC_DEFAULT_SOCKET_PATH; else socket_path = argv[1]; - listen_fd = PR_OpenTCPSocket(PR_AF_LOCAL); - if (!listen_fd) { - LOG(("PR_OpenUDPSocket failed [%d]\n", PR_GetError())); - return -1; - } + if (!InitDaemonDir(socket_path)) + return 0; addr.local.family = PR_AF_LOCAL; PL_strncpyz(addr.local.path, socket_path, sizeof(addr.local.path)); @@ -276,6 +403,9 @@ start: if (PR_Bind(listen_fd, &addr) != PR_SUCCESS) { LOG(("PR_Bind failed [%d]\n", PR_GetError())); + return -1; + } +#if 0 // // failure here indicates that another process may be bound // to the socket already. let's try connecting to that socket. @@ -310,6 +440,7 @@ start: PR_Close(listen_fd); goto start; } +#endif InitModuleReg(argv[0]); @@ -322,22 +453,9 @@ start: IPC_ShutdownModuleReg(); - // - // XXX enable this delay for startup testing - // - //LOG(("sleeping for 5 seconds...\n")); - //PR_Sleep(PR_SecondsToInterval(5)); - #ifndef IPC_USE_INET - LOG(("deleting socket at %s\n", socket_path)); - // - // we delete the file itself first to avoid a race between shutting - // ourselves down and another instance of the daemon starting up. - // - if (PR_Delete(socket_path) != PR_SUCCESS) { - LOG(("PR_Delete failed [%d]\n", PR_GetError())); - return -1; - } + + ShutdownDaemonDir(); #endif LOG(("closing socket\n")); diff --git a/mozilla/modules/ipc/public/ipcIService.idl b/mozilla/modules/ipc/public/ipcIService.idl index 8c1ec2c8667..b23fc734640 100644 --- a/mozilla/modules/ipc/public/ipcIService.idl +++ b/mozilla/modules/ipc/public/ipcIService.idl @@ -43,22 +43,23 @@ interface ipcIClientObserver; /** * the IPC service provides the means to communicate with an external IPC - * daemon and/or other mozilla-based applications sharing the same user - * profile. the IPC daemon hosts modules (some builtin and others dynamically - * loaded) with which applications may interact. + * 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 for the current profile. (there is one IPC - * daemon per profile.) the IPC daemon will be automatically started if - * necessary. when a connection has been established, the IPC service will - * broadcast an "ipc-startup" notification using the observer service. + * connection with the IPC daemon. the IPC daemon will be automatically + * started if necessary. when a connection has been established, the IPC + * service will broadcast an "ipc-startup" notification using the observer + * service. * - * when the connection to the IPC daemon is closed (due to error, profile - * change, or normal shutdown), an "ipc-shutdown" notification will be - * broadcast. + * XXX startup category + * + * 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. this IPC service does not require + * 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 @@ -192,8 +193,8 @@ interface ipcIClientObserver : nsISupports * @param aStatus client status (e.g. CLIENT_UP). */ void onClientStatus(in unsigned long aRequestToken, - in ipcIClientInfo aClientInfo, - in unsigned long aStatus); + in unsigned long aStatus, + in ipcIClientInfo aClientInfo); // XXX do we care about changes to the aliases and targets supported // by a client? if so, how should we pass this information up? diff --git a/mozilla/modules/ipc/src/Makefile.in b/mozilla/modules/ipc/src/Makefile.in index a32808a0ddc..6180b8c85c7 100644 --- a/mozilla/modules/ipc/src/Makefile.in +++ b/mozilla/modules/ipc/src/Makefile.in @@ -48,16 +48,20 @@ EXPORT_LIBRARY = 1 FORCE_STATIC_LIB = 1 MODULE_NAME = ipc -REQUIRES = xpcom \ - string \ - necko \ - pref \ - $(NULL) +REQUIRES = \ + xpcom \ + string \ + necko \ + pref \ + $(NULL) -CPPSRCS = \ - ipcService.cpp \ - ipcTransport.cpp \ - ipcSocketProvider.cpp +CPPSRCS = \ + ipcService.cpp \ + ipcTransport.cpp + +ifneq ($(OS_ARCH),WINNT) +CPPSRC += ipcSocketProvider.cpp +endif LOCAL_INCLUDES = \ -I$(srcdir)/../common \ diff --git a/mozilla/modules/ipc/src/ipcService.cpp b/mozilla/modules/ipc/src/ipcService.cpp index ec225b97745..c2d4b2f5f30 100644 --- a/mozilla/modules/ipc/src/ipcService.cpp +++ b/mozilla/modules/ipc/src/ipcService.cpp @@ -35,9 +35,18 @@ * * ***** END LICENSE BLOCK ***** */ +#ifdef XP_UNIX +#include +#include +#include +#endif + #include "plstr.h" +#include "nsIFile.h" #include "nsIServiceManager.h" +#include "nsDirectoryServiceUtils.h" +#include "nsDirectoryServiceDefs.h" #include "nsIPrefService.h" #include "nsIPrefBranch.h" @@ -144,10 +153,12 @@ ipcService::Init() if (appName.IsEmpty()) appName = NS_LITERAL_CSTRING("test-app"); - // XXX use directory service to locate socket - rv = mTransport->Init(appName, - NS_LITERAL_CSTRING(IPC_DEFAULT_SOCKET_PATH), - this); + // get socket path from directory service + nsCAutoString socketPath; + if (NS_FAILED(GetSocketPath(socketPath))) + socketPath = NS_LITERAL_CSTRING(IPC_DEFAULT_SOCKET_PATH); + + rv = mTransport->Init(appName, socketPath, this); if (NS_FAILED(rv)) return rv; return NS_OK; @@ -180,12 +191,34 @@ ipcService::HandleQueryResult(const ipcMessage *rawMsg, PRBool succeeded) info = nsnull; } - query->mObserver->OnClientStatus(query->mReqToken, info, cStatus); + query->mObserver->OnClientStatus(query->mReqToken, cStatus, info); NS_IF_RELEASE(info); mQueryQ.DeleteFirst(); } +nsresult +ipcService::GetSocketPath(nsACString &socketPath) +{ + nsCOMPtr file; + NS_GetSpecialDirectory(NS_OS_TEMP_DIR, getter_AddRefs(file)); + if (!file) + return NS_ERROR_FAILURE; +#ifdef XP_UNIX + // XXX may want to use getpwuid_r when available + struct passwd *pw = getpwuid(geteuid()); + if (!pw) + return NS_ERROR_UNEXPECTED; + nsCAutoString leaf; + leaf = NS_LITERAL_CSTRING(".mozilla-ipc-") + + nsDependentCString(pw->pw_name); + file->AppendNative(leaf); +#endif + file->AppendNative(NS_LITERAL_CSTRING("ipcd")); + file->GetNativePath(socketPath); + return NS_OK; +} + //----------------------------------------------------------------------------- // interface impl //----------------------------------------------------------------------------- diff --git a/mozilla/modules/ipc/src/ipcService.h b/mozilla/modules/ipc/src/ipcService.h index 84150aac5a8..46b35e61e42 100644 --- a/mozilla/modules/ipc/src/ipcService.h +++ b/mozilla/modules/ipc/src/ipcService.h @@ -112,6 +112,7 @@ public: private: nsresult ErrorAccordingToIPCM(PRUint32 err); void HandleQueryResult(const ipcMessage *, PRBool succeeded); + nsresult GetSocketPath(nsACString &); // ipcTransportObserver: void OnConnectionEstablished(PRUint32 clientID); diff --git a/mozilla/modules/ipc/src/ipcSocketProvider.cpp b/mozilla/modules/ipc/src/ipcSocketProvider.cpp index 2132fb17032..5ad6eb736e9 100644 --- a/mozilla/modules/ipc/src/ipcSocketProvider.cpp +++ b/mozilla/modules/ipc/src/ipcSocketProvider.cpp @@ -35,7 +35,62 @@ * * ***** END LICENSE BLOCK ***** */ +#include +#include +#include + +#include "private/pprio.h" + #include "ipcSocketProvider.h" +#include "ipcLog.h" + +static PRDescIdentity ipcIOLayerIdentity; +static PRIOMethods ipcIOLayerMethods; + +static PRStatus PR_CALLBACK +ipcIOLayerConnect(PRFileDesc* fd, const PRNetAddr* addr, PRIntervalTime timeout) +{ + if (!fd || !fd->lower) + return PR_FAILURE; + + PRStatus status; + + status = fd->lower->methods->connect(fd->lower, addr, timeout); + if (status != PR_SUCCESS) + return status; + + // + // 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->lower); + + struct stat st; + if (fstat(unix_fd, &st) == -1) { + LOG(("ipcIOLayerConnect: stat failed\n")); + return PR_FAILURE; + } + + if (st.st_uid != getuid() && st.st_uid != geteuid()) { + LOG(("ipcIOLayerConnect: uid check failed\n")); + return PR_FAILURE; + } + + return PR_SUCCESS; +} + +static void InitIPCMethods() +{ + ipcIOLayerIdentity = PR_GetUniqueIdentity("moz:ipc-layer"); + + ipcIOLayerMethods = *PR_GetDefaultIOMethods(); + ipcIOLayerMethods.connect = ipcIOLayerConnect; +} NS_IMETHODIMP ipcSocketProvider::NewSocket(const char *host, @@ -45,8 +100,32 @@ ipcSocketProvider::NewSocket(const char *host, PRFileDesc **fd, nsISupports **securityInfo) { - *fd = PR_OpenTCPSocket(PR_AF_LOCAL); + static PRBool firstTime = PR_TRUE; + if (firstTime) { + InitIPCMethods(); + firstTime = PR_FALSE; + } + + PRFileDesc *sock = PR_OpenTCPSocket(PR_AF_LOCAL); + if (!sock) return NS_ERROR_OUT_OF_MEMORY; + + PRFileDesc *layer = PR_CreateIOLayerStub(ipcIOLayerIdentity, &ipcIOLayerMethods); + if (!layer) + goto loser; + layer->secret = NULL; + + if (PR_PushIOLayer(sock, PR_GetLayersIdentity(sock), layer) != PR_SUCCESS) + goto loser; + + *fd = sock; return NS_OK; + +loser: + if (layer) + layer->dtor(layer); + if (sock) + PR_Close(sock); + return NS_ERROR_FAILURE; } NS_IMETHODIMP diff --git a/mozilla/modules/ipc/src/ipcTransport.cpp b/mozilla/modules/ipc/src/ipcTransport.cpp index 2886cc64bf2..60313a6ffda 100644 --- a/mozilla/modules/ipc/src/ipcTransport.cpp +++ b/mozilla/modules/ipc/src/ipcTransport.cpp @@ -319,7 +319,8 @@ ipcTransport::SpawnDaemon() if (NS_FAILED(rv)) return rv; PRUint32 pid; - return proc->Run(PR_FALSE, nsnull, 0, &pid); + const char *args[] = { mSocketPath.get() }; + return proc->Run(PR_FALSE, args, 1, &pid); } NS_IMPL_THREADSAFE_ISUPPORTS0(ipcTransport) diff --git a/mozilla/modules/ipc/test/TestIPC.cpp b/mozilla/modules/ipc/test/TestIPC.cpp index 59e062e6cc7..3557fcd365d 100644 --- a/mozilla/modules/ipc/test/TestIPC.cpp +++ b/mozilla/modules/ipc/test/TestIPC.cpp @@ -103,9 +103,10 @@ public: NS_IMPL_ISUPPORTS1(myIpcClientObserver, ipcIClientObserver) NS_IMETHODIMP -myIpcClientObserver::OnClientStatus(PRUint32 aReqToken, ipcIClientInfo *aClientInfo, PRUint32 aStatus) +myIpcClientObserver::OnClientStatus(PRUint32 aReqToken, PRUint32 aStatus, ipcIClientInfo *aClientInfo) { - printf("*** got client status [token=%u info=%p status=%u]\n", aReqToken, (void *) aClientInfo, aStatus); + printf("*** got client status [token=%u status=%u info=%p]\n", + aReqToken, aStatus, (void *) aClientInfo); if (aClientInfo) { PRUint32 cID;