Back to index

lightning-sunbird  0.9+nobinonly
ipcdWin.cpp
Go to the documentation of this file.
00001 /* ***** BEGIN LICENSE BLOCK *****
00002  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00003  *
00004  * The contents of this file are subject to the Mozilla Public License Version
00005  * 1.1 (the "License"); you may not use this file except in compliance with
00006  * the License. You may obtain a copy of the License at
00007  * http://www.mozilla.org/MPL/
00008  *
00009  * Software distributed under the License is distributed on an "AS IS" basis,
00010  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00011  * for the specific language governing rights and limitations under the
00012  * License.
00013  *
00014  * The Original Code is Mozilla IPC.
00015  *
00016  * The Initial Developer of the Original Code is
00017  * Netscape Communications Corporation.
00018  * Portions created by the Initial Developer are Copyright (C) 2002
00019  * the Initial Developer. All Rights Reserved.
00020  *
00021  * Contributor(s):
00022  *   Darin Fisher <darin@netscape.com>
00023  *
00024  * Alternatively, the contents of this file may be used under the terms of
00025  * either the GNU General Public License Version 2 or later (the "GPL"), or
00026  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00027  * in which case the provisions of the GPL or the LGPL are applicable instead
00028  * of those above. If you wish to allow use of your version of this file only
00029  * under the terms of either the GPL or the LGPL, and not to allow others to
00030  * use your version of this file under the terms of the MPL, indicate your
00031  * decision by deleting the provisions above and replace them with the notice
00032  * and other provisions required by the GPL or the LGPL. If you do not delete
00033  * the provisions above, a recipient may use your version of this file under
00034  * the terms of any one of the MPL, the GPL or the LGPL.
00035  *
00036  * ***** END LICENSE BLOCK ***** */
00037 
00038 #include <windows.h>
00039 
00040 #include "prthread.h"
00041 
00042 #include "ipcConfig.h"
00043 #include "ipcLog.h"
00044 #include "ipcMessage.h"
00045 #include "ipcClient.h"
00046 #include "ipcModuleReg.h"
00047 #include "ipcdPrivate.h"
00048 #include "ipcd.h"
00049 #include "ipcm.h"
00050 
00051 //
00052 // declared in ipcdPrivate.h
00053 //
00054 ipcClient *ipcClients = NULL;
00055 int        ipcClientCount = 0;
00056 
00057 static ipcClient ipcClientArray[IPC_MAX_CLIENTS];
00058 
00059 static HWND   ipcHwnd = NULL;
00060 static PRBool ipcShutdown = PR_FALSE;
00061 
00062 #define IPC_PURGE_TIMER_ID 1
00063 #define IPC_WM_SENDMSG  (WM_USER + 1)
00064 #define IPC_WM_SHUTDOWN (WM_USER + 2)
00065 
00066 //-----------------------------------------------------------------------------
00067 // client array manipulation
00068 //-----------------------------------------------------------------------------
00069 
00070 static void
00071 RemoveClient(ipcClient *client)
00072 {
00073     LOG(("RemoveClient\n"));
00074 
00075     int clientIndex = client - ipcClientArray;
00076 
00077     client->Finalize();
00078 
00079     //
00080     // move last ipcClient object down into the spot occupied by this client.
00081     //
00082     int fromIndex = ipcClientCount - 1;
00083     int toIndex = clientIndex;
00084     if (toIndex != fromIndex)
00085         memcpy(&ipcClientArray[toIndex], &ipcClientArray[fromIndex], sizeof(ipcClient));
00086 
00087     memset(&ipcClientArray[fromIndex], 0, sizeof(ipcClient));
00088 
00089     --ipcClientCount;
00090     LOG(("  num clients = %u\n", ipcClientCount));
00091 
00092     if (ipcClientCount == 0) {
00093         LOG(("  shutting down...\n"));
00094         KillTimer(ipcHwnd, IPC_PURGE_TIMER_ID);
00095         PostQuitMessage(0);
00096         ipcShutdown = PR_TRUE;
00097     }
00098 }
00099 
00100 static void
00101 PurgeStaleClients()
00102 {
00103     if (ipcClientCount == 0)
00104         return;
00105 
00106     LOG(("PurgeStaleClients [num-clients=%u]\n", ipcClientCount));
00107     //
00108     // walk the list of supposedly active clients, and verify the existance of
00109     // their respective message windows.
00110     //
00111     char wName[IPC_CLIENT_WINDOW_NAME_MAXLEN];
00112     for (int i=ipcClientCount-1; i>=0; --i) {
00113         ipcClient *client = &ipcClientArray[i];
00114 
00115         LOG(("  checking client at index %u [client-id=%u pid=%u]\n", 
00116             i, client->ID(), client->PID()));
00117 
00118         IPC_GetClientWindowName(client->PID(), wName);
00119 
00120         // XXX dougt has ideas about how to make this better
00121 
00122         HWND hwnd = FindWindow(IPC_CLIENT_WINDOW_CLASS, wName);
00123         if (!hwnd) {
00124             LOG(("  client window not found; removing client!\n"));
00125             RemoveClient(client);
00126         }
00127     }
00128 }
00129 
00130 static ipcClient *
00131 AddClient(HWND hwnd, PRUint32 pid)
00132 {
00133     LOG(("AddClient\n"));
00134 
00135     //
00136     // before adding a new client, verify that all existing clients are
00137     // still up and running.  remove any stale clients.
00138     //
00139     PurgeStaleClients();
00140 
00141     if (ipcClientCount == IPC_MAX_CLIENTS) {
00142         LOG(("  reached maximum client count!\n"));
00143         return NULL;
00144     }
00145 
00146     ipcClient *client = &ipcClientArray[ipcClientCount];
00147     client->Init();
00148     client->SetHwnd(hwnd);
00149     client->SetPID(pid);    // XXX one function instead of 3
00150 
00151     ++ipcClientCount;
00152     LOG(("  num clients = %u\n", ipcClientCount));
00153 
00154     if (ipcClientCount == 1)
00155         SetTimer(ipcHwnd, IPC_PURGE_TIMER_ID, 1000, NULL);
00156 
00157     return client;
00158 }
00159 
00160 static ipcClient *
00161 GetClientByPID(PRUint32 pid)
00162 {
00163     for (int i=0; i<ipcClientCount; ++i) {
00164         if (ipcClientArray[i].PID() == pid)
00165             return &ipcClientArray[i];
00166     }
00167     return NULL;
00168 }
00169 
00170 //-----------------------------------------------------------------------------
00171 // message processing
00172 //-----------------------------------------------------------------------------
00173 
00174 static void
00175 ProcessMsg(HWND hwnd, PRUint32 pid, const ipcMessage *msg)
00176 {
00177     LOG(("ProcessMsg [pid=%u len=%u]\n", pid, msg->MsgLen()));
00178 
00179     ipcClient *client = GetClientByPID(pid);
00180 
00181     if (client) {
00182         //
00183         // if this is an IPCM "client hello" message, then reset the client
00184         // instance object.
00185         //
00186         if (msg->Target().Equals(IPCM_TARGET) &&
00187             IPCM_GetType(msg) == IPCM_MSG_REQ_CLIENT_HELLO) {
00188             RemoveClient(client);
00189             client = NULL;
00190         }
00191     }
00192 
00193     if (client == NULL) {
00194         client = AddClient(hwnd, pid);
00195         if (!client)
00196             return;
00197     }
00198 
00199     IPC_DispatchMsg(client, msg);
00200 }
00201 
00202 //-----------------------------------------------------------------------------
00203 
00204 PRStatus
00205 IPC_PlatformSendMsg(ipcClient  *client, ipcMessage *msg)
00206 {
00207     LOG(("IPC_PlatformSendMsg [clientID=%u clientPID=%u]\n",
00208         client->ID(), client->PID()));
00209 
00210     // use PostMessage to make this asynchronous; otherwise we might get
00211     // some wierd SendMessage recursion between processes.
00212 
00213     WPARAM wParam = (WPARAM) client->Hwnd();
00214     LPARAM lParam = (LPARAM) msg;
00215     if (!PostMessage(ipcHwnd, IPC_WM_SENDMSG, wParam, lParam)) {
00216         LOG(("PostMessage failed\n"));
00217         delete msg;
00218         return PR_FAILURE;
00219     }
00220     return PR_SUCCESS;
00221 }
00222 
00223 //-----------------------------------------------------------------------------
00224 // windows message loop
00225 //-----------------------------------------------------------------------------
00226 
00227 static LRESULT CALLBACK
00228 WindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
00229 {
00230     LOG(("got message [msg=%x wparam=%x lparam=%x]\n", uMsg, wParam, lParam));
00231 
00232     if (uMsg == WM_COPYDATA) {
00233         if (ipcShutdown) {
00234             LOG(("ignoring message b/c daemon is shutting down\n"));
00235             return TRUE;
00236         }
00237         COPYDATASTRUCT *cd = (COPYDATASTRUCT *) lParam;
00238         if (cd && cd->lpData) {
00239             ipcMessage msg;
00240             PRUint32 bytesRead;
00241             PRBool complete;
00242             // XXX avoid extra malloc
00243             PRStatus rv = msg.ReadFrom((const char *) cd->lpData, cd->cbData,
00244                                        &bytesRead, &complete);
00245             if (rv == PR_SUCCESS && complete) {
00246                 //
00247                 // grab client PID and hwnd.
00248                 //
00249                 ProcessMsg((HWND) wParam, (PRUint32) cd->dwData, &msg);
00250             }
00251             else
00252                 LOG(("ignoring malformed message\n"));
00253         }
00254         return TRUE;
00255     }
00256 
00257     if (uMsg == IPC_WM_SENDMSG) {
00258         HWND hWndDest = (HWND) wParam;
00259         ipcMessage *msg = (ipcMessage *) lParam;
00260 
00261         COPYDATASTRUCT cd;
00262         cd.dwData = GetCurrentProcessId();
00263         cd.cbData = (DWORD) msg->MsgLen();
00264         cd.lpData = (PVOID) msg->MsgBuf();
00265 
00266         LOG(("calling SendMessage...\n"));
00267         SendMessage(hWndDest, WM_COPYDATA, (WPARAM) hWnd, (LPARAM) &cd);
00268         LOG(("  done.\n"));
00269 
00270         delete msg;
00271         return 0;
00272     }
00273 
00274     if (uMsg == WM_TIMER) {
00275         PurgeStaleClients();
00276         return 0;
00277     }
00278 
00279 #if 0
00280     if (uMsg == IPC_WM_SHUTDOWN) {
00281         //
00282         // since this message is handled asynchronously, it is possible
00283         // that other clients may have come online since this was issued.
00284         // in which case, we need to ignore this message.
00285         //
00286         if (ipcClientCount == 0) {
00287             ipcShutdown = PR_TRUE;
00288             PostQuitMessage(0);
00289         }
00290         return 0;
00291     }
00292 #endif
00293 
00294     return DefWindowProc(hWnd, uMsg, wParam, lParam);
00295 }
00296 
00297 //-----------------------------------------------------------------------------
00298 // daemon startup synchronization
00299 //-----------------------------------------------------------------------------
00300 
00301 static HANDLE ipcSyncEvent;
00302 
00303 static PRBool
00304 AcquireLock()
00305 {
00306     ipcSyncEvent = CreateEvent(NULL, FALSE, FALSE,
00307                                IPC_SYNC_EVENT_NAME);
00308     if (!ipcSyncEvent) {
00309         LOG(("CreateEvent failed [%u]\n", GetLastError()));
00310         return PR_FALSE;
00311     }
00312 
00313     // check to see if event already existed prior to this call.
00314     if (GetLastError() == ERROR_ALREADY_EXISTS) {
00315         LOG(("  lock already set; exiting...\n"));
00316         return PR_FALSE;
00317     }
00318     
00319     LOG(("  acquired lock\n"));
00320     return PR_TRUE;
00321 }
00322 
00323 static void
00324 ReleaseLock()
00325 {
00326     if (ipcSyncEvent) {
00327         LOG(("releasing lock...\n"));
00328         CloseHandle(ipcSyncEvent);
00329         ipcSyncEvent = NULL;
00330     }
00331 }
00332 
00333 //-----------------------------------------------------------------------------
00334 // main
00335 //-----------------------------------------------------------------------------
00336 
00337 #ifdef DEBUG
00338 int
00339 main()
00340 #else
00341 int WINAPI WinMain(HINSTANCE, HINSTANCE, LPSTR, int)
00342 #endif
00343 {
00344     IPC_InitLog("###");
00345 
00346     LOG(("daemon started...\n"));
00347 
00348     if (!AcquireLock()) {
00349         // unblock the parent; it should be able to find the IPC window of the
00350         // other daemon process.
00351         IPC_NotifyParent();
00352         return 0;
00353     }
00354 
00355     // initialize global data
00356     memset(ipcClientArray, 0, sizeof(ipcClientArray));
00357     ipcClients = ipcClientArray;
00358     ipcClientCount = 0;
00359 
00360     // create message window up front...
00361     WNDCLASS wc;
00362     memset(&wc, 0, sizeof(wc));
00363     wc.lpfnWndProc = WindowProc;
00364     wc.lpszClassName = IPC_WINDOW_CLASS;
00365 
00366     RegisterClass(&wc);
00367 
00368     ipcHwnd = CreateWindow(IPC_WINDOW_CLASS, IPC_WINDOW_NAME,
00369                            0, 0, 0, 10, 10, NULL, NULL, NULL, NULL);
00370 
00371     // unblock the parent process; it should now look for the IPC window.
00372     IPC_NotifyParent();
00373 
00374     if (!ipcHwnd)
00375         return -1;
00376 
00377     // load modules relative to the location of the executable...
00378     {
00379         char path[MAX_PATH];
00380         GetModuleFileName(NULL, path, sizeof(path));
00381         IPC_InitModuleReg(path);
00382     }
00383 
00384     LOG(("entering message loop...\n"));
00385     MSG msg;
00386     while (GetMessage(&msg, ipcHwnd, 0, 0))
00387         DispatchMessage(&msg);
00388 
00389     // unload modules
00390     IPC_ShutdownModuleReg();
00391 
00392     //
00393     // we release the daemon lock before destroying the window because the
00394     // absence of our window is what will cause clients to try to spawn the
00395     // daemon.
00396     //
00397     ReleaseLock();
00398 
00399     //LOG(("sleeping 5 seconds...\n"));
00400     //PR_Sleep(PR_SecondsToInterval(5));
00401 
00402     LOG(("destroying message window...\n"));
00403     DestroyWindow(ipcHwnd);
00404     ipcHwnd = NULL;
00405 
00406     LOG(("exiting\n"));
00407     return 0;
00408 }