windows-afsifs-20050804
[openafs.git] / src / WINNT / afsd / afsd_service.c
index 31e488c..51e92ae 100644 (file)
@@ -6,6 +6,30 @@
  * License.  For details, see the LICENSE file in the top-level source
  * directory or online at http://www.openafs.org/dl/license10.html
  */
+/* AFSIFS portions copyright (c) 2005
+ * the regents of the university of michigan
+ * all rights reserved
+ * 
+ * permission is granted to use, copy, create derivative works and
+ * redistribute this software and such derivative works for any purpose,
+ * so long as the name of the university of michigan is not used in
+ * any advertising or publicity pertaining to the use or distribution
+ * of this software without specific, written prior authorization.  if
+ * the above copyright notice or any other identification of the
+ * university of michigan is included in any copy of any portion of
+ * this software, then the disclaimer below must also be included.
+ * 
+ * this software is provided as is, without representation from the
+ * university of michigan as to its fitness for any purpose, and without
+ * warranty by the university of michigan of any kind, either express 
+ * or implied, including without limitation the implied warranties of
+ * merchantability and fitness for a particular purpose.  the regents
+ * of the university of michigan shall not be liable for any damages,   
+ * including special, indirect, incidental, or consequential damages, 
+ * with respect to any claim arising out or in connection with the use
+ * of the software, even if it has been or is hereafter advised of the
+ * possibility of such damages.
+ */
 
 #include <afs/param.h>
 #include <afs/stds.h>
@@ -21,6 +45,7 @@
 #include <stdio.h>
 #include <stdlib.h>
 #include <winsock2.h>
+#include <WINNT\afsreg.h>
 
 #include <osi.h>
 
 #ifdef _DEBUG
 #include <crtdbg.h>
 #endif
+#include "afsdifs.h"
 
-/*
-// The following is defined if you want to receive Power notifications,
-// including Hibernation, and also subsequent flushing of AFS volumes
-//
-#define REGISTER_POWER_NOTIFICATIONS 1
-#define FLUSH_VOLUME                 1
-//
-// Check
-*/
+//#define REGISTER_POWER_NOTIFICATIONS 1
 #include "afsd_flushvol.h"
 
 extern void afsi_log(char *pattern, ...);
@@ -48,8 +66,14 @@ static SERVICE_STATUS                ServiceStatus;
 static SERVICE_STATUS_HANDLE   StatusHandle;
 
 HANDLE hAFSDMainThread = NULL;
+#ifdef AFSIFS
+HANDLE hAFSDWorkerThread[WORKER_THREADS];
+#endif
 
-HANDLE WaitToTerminate;
+/* for the IFS version, set the event DoTerminate, on which all
+   worker threads wait.  they will exit, and then everything else
+   can uninitialize. */
+HANDLE WaitToTerminate, DoTerminate;
 
 int GlobalStatus;
 
@@ -71,6 +95,7 @@ static void afsd_notifier(char *msgp, char *filep, long line)
     char tbuffer[512];
     char *ptbuf[1];
     HANDLE h;
+       int i;
 
     if (filep)
         sprintf(tbuffer, "Error at file %s, line %d: %s",
@@ -91,19 +116,26 @@ static void afsd_notifier(char *msgp, char *filep, long line)
     buf_ForceTrace(TRUE);
 
     afsi_log("--- begin dump ---");
-    cm_DumpSCache(afsi_file, "a");
+    cm_DumpSCache(afsi_file, "a", 0);
 #ifdef keisa
     cm_dnlcDump(afsi_file, "a");
 #endif
-    cm_DumpBufHashTable(afsi_file, "a");
-    smb_DumpVCP(afsi_file, "a");                       
+    cm_DumpBufHashTable(afsi_file, "a", 0);
+    smb_DumpVCP(afsi_file, "a", 0);                    
     afsi_log("--- end   dump ---");
     
 #ifdef DEBUG
     DebugBreak();      
 #endif
 
+#ifndef AFSIFS
     SetEvent(WaitToTerminate);
+#else
+    SetEvent(DoTerminate);
+    WaitForMultipleObjects(WORKER_THREADS, hAFSDWorkerThread, TRUE, INFINITE);
+    for (i = 0; i < WORKER_THREADS; i++)
+        CloseHandle(hAFSDWorkerThread[i]);
+#endif
 
 #ifdef JUMP
     if (GetCurrentThreadId() == MainThreadId)
@@ -180,12 +212,17 @@ afsd_ServiceControlHandler(DWORD ctrlCode)
         ServiceStatus.dwControlsAccepted = 0;
         SetServiceStatus(StatusHandle, &ServiceStatus);
 
-#ifdef FLUSH_VOLUME
-        afsd_ServiceFlushVolume((DWORD) lpEventData);                         
-#endif                                                                                      
+        if (ctrlCode == SERVICE_CONTROL_STOP)
+            afsi_log("SERVICE_CONTROL_STOP");
+        else
+            afsi_log("SERVICE_CONTROL_SHUTDOWN");
+
+        /* Write all dirty buffers back to server */
+        buf_CleanAndReset();
+
         /* Force trace if requested */
         code = RegOpenKeyEx(HKEY_LOCAL_MACHINE,
-                             AFSConfigKeyName,
+                             AFSREG_CLT_SVC_PARAM_SUBKEY,
                              0, KEY_QUERY_VALUE, &parmKey);
         if (code != ERROR_SUCCESS)
             goto doneTrace;
@@ -203,7 +240,11 @@ afsd_ServiceControlHandler(DWORD ctrlCode)
         }
 
       doneTrace:
+#ifndef AFSIFS
         SetEvent(WaitToTerminate);
+#else
+        SetEvent(DoTerminate);
+#endif
         break;
 
     case SERVICE_CONTROL_INTERROGATE:
@@ -248,13 +289,12 @@ afsd_ServiceControlHandlerEx(
         ServiceStatus.dwControlsAccepted = 0;
         SetServiceStatus(StatusHandle, &ServiceStatus);
 
-#ifdef FLUSH_VOLUME
-        afsd_ServiceFlushVolume((DWORD) lpEventData);                         
-#endif                                                                                      
+        /* Write all dirty buffers back to server */
+        buf_CleanAndReset();
 
         /* Force trace if requested */
         code = RegOpenKeyEx(HKEY_LOCAL_MACHINE,
-                            AFSConfigKeyName,
+                            AFSREG_CLT_SVC_PARAM_SUBKEY,
                             0, KEY_QUERY_VALUE, &parmKey);
         if (code != ERROR_SUCCESS)
             goto doneTrace;
@@ -272,7 +312,11 @@ afsd_ServiceControlHandlerEx(
         }
 
       doneTrace:
+#ifndef AFSIFS
         SetEvent(WaitToTerminate);
+#else
+        SetEvent(DoTerminate);
+#endif
         dwRet = NO_ERROR;
         break;
 
@@ -283,13 +327,15 @@ afsd_ServiceControlHandlerEx(
         ServiceStatus.dwWaitHint = 0;
         ServiceStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN | SERVICE_ACCEPT_POWEREVENT;
         SetServiceStatus(StatusHandle, &ServiceStatus);
+        afsi_log("SERVICE_CONTROL_INTERROGATE");
         dwRet = NO_ERROR;
         break;
 
         /* XXX handle system shutdown */
         /* XXX handle pause & continue */
     case SERVICE_CONTROL_POWEREVENT:                                              
-        {                                                                                     
+        { 
+            afsi_log("SERVICE_CONTROL_POWEREVENT");
             /*                                                                                
             ** dwEventType of this notification == WPARAM of WM_POWERBROADCAST               
             ** Return NO_ERROR == return TRUE for that message, i.e. accept request          
@@ -299,32 +345,70 @@ afsd_ServiceControlHandlerEx(
             if (powerEventsRegistered) {
                 switch((int) dwEventType)                                                         
                 {                                                                               
-                case PBT_APMQUERYSUSPEND:                                                         
+                case PBT_APMQUERYSUSPEND:       
+                    afsi_log("SERVICE_CONTROL_APMQUERYSUSPEND"); 
+                    /* Write all dirty buffers back to server */
+                    buf_CleanAndReset();
+                    dwRet = NO_ERROR;                       
+                    break;                                  
                 case PBT_APMQUERYSTANDBY:                                                         
-
-#ifdef FLUSH_VOLUME
-                    /* handle event */                                                            
-                    dwRet = afsd_ServiceFlushVolume((DWORD) lpEventData);                         
-#else                                                                                       
+                    afsi_log("SERVICE_CONTROL_APMQUERYSTANDBY"); 
+                    /* Write all dirty buffers back to server */
+                    buf_CleanAndReset();
                     dwRet = NO_ERROR;                                                             
-#endif                                                                                      
                     break;                                                                        
                                                                                                                          
                     /* allow remaining case PBT_WhatEver */                                           
-                case PBT_APMSUSPEND:                                                              
-                case PBT_APMSTANDBY:                                                              
-                case PBT_APMRESUMECRITICAL:                                                       
+                case PBT_APMSUSPEND:                         
+                    afsi_log("SERVICE_CONTROL_APMSUSPEND"); 
+                    dwRet = NO_ERROR;                       
+                    break;                                  
+                case PBT_APMSTANDBY:                  
+                    afsi_log("SERVICE_CONTROL_APMSTANDBY"); 
+                    dwRet = NO_ERROR;                       
+                    break;                                  
+                case PBT_APMRESUMECRITICAL:             
+                    afsi_log("SERVICE_CONTROL_APMRESUMECRITICAL"); 
+                    dwRet = NO_ERROR;                       
+                    break;                                  
                 case PBT_APMRESUMESUSPEND:                                                        
+                    afsi_log("SERVICE_CONTROL_APMRESUMESUSPEND"); 
+                    dwRet = NO_ERROR;                       
+                    break;                                  
                 case PBT_APMRESUMESTANDBY:                                                        
+                    afsi_log("SERVICE_CONTROL_APMRESUMESTANDBY"); 
+                    dwRet = NO_ERROR;                       
+                    break;                                  
                 case PBT_APMBATTERYLOW:                                                           
+                    afsi_log("SERVICE_CONTROL_APMBATTERYLOW"); 
+                    dwRet = NO_ERROR;                       
+                    break;                                  
                 case PBT_APMPOWERSTATUSCHANGE:                                                    
+                    afsi_log("SERVICE_CONTROL_APMPOWERSTATUSCHANGE"); 
+                    dwRet = NO_ERROR;                       
+                    break;                                  
                 case PBT_APMOEMEVENT:                                                             
+                    afsi_log("SERVICE_CONTROL_APMOEMEVENT"); 
+                    dwRet = NO_ERROR;                       
+                    break;                                  
                 case PBT_APMRESUMEAUTOMATIC:                                                      
+                    afsi_log("SERVICE_CONTROL_APMRESUMEAUTOMATIC"); 
+                    dwRet = NO_ERROR;                       
+                    break;                                  
                 default:                                                                          
-                    dwRet = NO_ERROR;                                                             
+                    afsi_log("SERVICE_CONTROL_unknown"); 
+                    dwRet = NO_ERROR;                       
                 }   
             }
         }
+        break;
+    case SERVICE_CONTROL_CUSTOM_DUMP: 
+        {
+            afsi_log("SERVICE_CONTROL_CUSTOM_DUMP"); 
+            GenerateMiniDump(NULL);
+                       dwRet = NO_ERROR;
+        }
+        break;
     }          /* end switch(ctrlCode) */                                                        
     return dwRet;   
 }
@@ -348,7 +432,7 @@ static void MountGlobalDrives(void)
     char szSubMount[256];
     DWORD dwType;
 
-    sprintf(szKeyName, "%s\\GlobalAutoMapper", AFSConfigKeyName);
+    sprintf(szKeyName, "%s\\GlobalAutoMapper", AFSREG_CLT_SVC_PARAM_SUBKEY);
 
     dwResult = RegOpenKeyEx(HKEY_LOCAL_MACHINE, szKeyName, 0, KEY_QUERY_VALUE, &hKey);
     if (dwResult != ERROR_SUCCESS)
@@ -366,21 +450,22 @@ static void MountGlobalDrives(void)
             }
         }
 
+#ifndef AFSIFS
         for ( ; dwRetry < MAX_RETRIES; dwRetry++)
-               {
-                   NETRESOURCE nr;
-                   memset (&nr, 0x00, sizeof(NETRESOURCE));
-                   sprintf(szAfsPath,"\\\\%s\\%s",cm_NetbiosName,szSubMount);
-                   
-                   nr.dwScope = RESOURCE_GLOBALNET;              /* ignored parameter */
-                   nr.dwType=RESOURCETYPE_DISK;
-                   nr.lpLocalName=szDriveToMapTo;
-                   nr.lpRemoteName=szAfsPath;
-                   nr.dwDisplayType = RESOURCEDISPLAYTYPE_SHARE; /* ignored parameter */
-                   nr.dwUsage = RESOURCEUSAGE_CONNECTABLE;       /* ignored parameter */
-
-                   dwResult = WNetAddConnection2(&nr,NULL,NULL,0);
+        {
+            NETRESOURCE nr;
+            memset (&nr, 0x00, sizeof(NETRESOURCE));
+
+            sprintf(szAfsPath,"\\\\%s\\%s",cm_NetbiosName,szSubMount);
+
+            nr.dwScope = RESOURCE_GLOBALNET;              /* ignored parameter */
+            nr.dwType=RESOURCETYPE_DISK;
+            nr.lpLocalName=szDriveToMapTo;
+            nr.lpRemoteName=szAfsPath;
+            nr.dwDisplayType = RESOURCEDISPLAYTYPE_SHARE; /* ignored parameter */
+            nr.dwUsage = RESOURCEUSAGE_CONNECTABLE;       /* ignored parameter */
+
+            dwResult = WNetAddConnection2(&nr,NULL,NULL,0);
             afsi_log("GlobalAutoMap of %s to %s %s (%d)", szDriveToMapTo, szSubMount, 
                      (dwResult == NO_ERROR) ? "succeeded" : "failed", dwResult);
             if (dwResult == NO_ERROR) {
@@ -392,6 +477,9 @@ static void MountGlobalDrives(void)
             /* Disconnect any previous mappings */
             dwResult = WNetCancelConnection2(szDriveToMapTo, 0, TRUE);
         }
+#else
+       /* FIXFIX: implement */
+#endif
     }        
 
     RegCloseKey(hKey);
@@ -410,12 +498,13 @@ static void DismountGlobalDrives()
     char szSubMount[256];
     DWORD dwType;
 
-    sprintf(szKeyName, "%s\\GlobalAutoMapper", AFSConfigKeyName);
+    sprintf(szKeyName, "%s\\GlobalAutoMapper", AFSREG_CLT_SVC_PARAM_SUBKEY);
 
     dwResult = RegOpenKeyEx(HKEY_LOCAL_MACHINE, szKeyName, 0, KEY_QUERY_VALUE, &hKey);
     if (dwResult != ERROR_SUCCESS)
         return;
 
+#ifndef AFSIFS    
     while (1) {
         dwDriveSize = sizeof(szDriveToMapTo);
         dwSubMountSize = sizeof(szSubMount);
@@ -435,6 +524,9 @@ static void DismountGlobalDrives()
         
         afsi_log("Disconnect from GlobalAutoMap of %s to %s %s", szDriveToMapTo, szSubMount, dwResult ? "succeeded" : "failed");
     }        
+#else
+       /* FIXFIX: implement */
+#endif
 
     RegCloseKey(hKey);
 }
@@ -853,7 +945,7 @@ BOOL AFSModulesVerify(void)
 
 
     code = RegOpenKeyEx(HKEY_LOCAL_MACHINE, 
-                        "SYSTEM\\CurrentControlSet\\Services\\TransarcAFSDaemon\\Parameters",
+                        AFSREG_CLT_SVC_PARAM_SUBKEY,
                         0, KEY_QUERY_VALUE, &parmKey);
     if (code == ERROR_SUCCESS) {
         dummyLen = sizeof(cacheSize);
@@ -862,7 +954,7 @@ BOOL AFSModulesVerify(void)
         RegCloseKey (parmKey);
     }
 
-    code = RegOpenKeyEx(HKEY_LOCAL_MACHINE, "SOFTWARE\\OpenAFS\\Client",
+    code = RegOpenKeyEx(HKEY_LOCAL_MACHINE, AFSREG_CLT_OPENAFS_SUBKEY,
                          0, KEY_QUERY_VALUE, &parmKey);
     if (code == ERROR_SUCCESS) {
         dummyLen = sizeof(verifyServiceSig);
@@ -958,10 +1050,6 @@ BOOL AFSModulesVerify(void)
     return success;
 }
 
-typedef BOOL ( APIENTRY * AfsdInitHook )(void);
-#define AFSD_INIT_HOOK "AfsdInitHook"
-#define AFSD_HOOK_DLL  "afsdhook.dll"
-
 /*
 control serviceex exists only on 2000/xp. These functions will be loaded dynamically.
 */
@@ -972,16 +1060,17 @@ typedef SERVICE_STATUS_HANDLE ( * RegisterServiceCtrlHandlerFunc   )(  LPCTSTR ,
 RegisterServiceCtrlHandlerExFunc pRegisterServiceCtrlHandlerEx = NULL;
 RegisterServiceCtrlHandlerFunc   pRegisterServiceCtrlHandler   = NULL; 
 
-void afsd_Main(DWORD argc, LPTSTR *argv)
+VOID WINAPI
+afsd_Main(DWORD argc, LPTSTR *argv)
 {
     long code;
     char *reason;
 #ifdef JUMP
     int jmpret;
 #endif /* JUMP */
-    HANDLE hInitHookDll;
-    HANDLE hAdvApi32;
-    AfsdInitHook initHook;
+    HMODULE hHookDll;
+    HMODULE hAdvApi32;
+       int cnt;
 
 #ifdef _DEBUG
     _CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF /*| _CRTDBG_CHECK_ALWAYS_DF*/ | 
@@ -999,6 +1088,12 @@ void afsd_Main(DWORD argc, LPTSTR *argv)
     if ( GetLastError() == ERROR_ALREADY_EXISTS )
         afsi_log("Event Object Already Exists: %s", TEXT("afsd_service_WaitToTerminate"));
 
+#ifdef AFSIFS
+    DoTerminate = CreateEvent(NULL, TRUE, FALSE, TEXT("afsd_service_DoTerminate"));
+    if ( GetLastError() == ERROR_ALREADY_EXISTS )
+        afsi_log("Event Object Already Exists: %s", TEXT("afsd_service_DoTerminate"));
+#endif
+
 #ifndef NOTSERVICE
     hAdvApi32 = LoadLibrary("advapi32.dll");
     if (hAdvApi32 == NULL)
@@ -1025,7 +1120,7 @@ void afsd_Main(DWORD argc, LPTSTR *argv)
     ServiceStatus.dwCheckPoint = 1;
     ServiceStatus.dwWaitHint = 30000;
     /* accept Power Events */
-    ServiceStatus.dwControlsAccepted = SERVICE_ACCEPT_POWEREVENT;
+    ServiceStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN | SERVICE_ACCEPT_POWEREVENT | SERVICE_ACCEPT_PARAMCHANGE;
     SetServiceStatus(StatusHandle, &ServiceStatus);
 #endif
 
@@ -1045,7 +1140,8 @@ void afsd_Main(DWORD argc, LPTSTR *argv)
         int bpower = TRUE;
 
         /* see if we should handle power notifications */
-        code = RegOpenKeyEx(HKEY_LOCAL_MACHINE, AFSConfigKeyName, 0, KEY_QUERY_VALUE, &hkParm);
+        code = RegOpenKeyEx(HKEY_LOCAL_MACHINE, AFSREG_CLT_SVC_PARAM_SUBKEY, 
+                            0, KEY_QUERY_VALUE, &hkParm);
         if (code == ERROR_SUCCESS) {
             dummyLen = sizeof(bpower);
             code = RegQueryValueEx(hkParm, "FlushOnHibernate", NULL, NULL,
@@ -1072,23 +1168,31 @@ void afsd_Main(DWORD argc, LPTSTR *argv)
         ServiceStatus.dwWaitHint = 0;
         ServiceStatus.dwControlsAccepted = 0;
         SetServiceStatus(StatusHandle, &ServiceStatus);
+
+               {       
+               HANDLE h; char *ptbuf[1];
+               h = RegisterEventSource(NULL, AFS_DAEMON_EVENT_NAME);
+               ptbuf[0] = "Incorrect module versions loaded";
+               ReportEvent(h, EVENTLOG_ERROR_TYPE, 0, 0, NULL, 1, 0, ptbuf, NULL);
+               DeregisterEventSource(h);
+               }
                        
         /* exit if initialization failed */
         return;
     }
 
     /* allow an exit to be called prior to any initialization */
-    hInitHookDll = LoadLibrary(AFSD_HOOK_DLL);
-    if (hInitHookDll)
+    hHookDll = LoadLibrary(AFSD_HOOK_DLL);
+    if (hHookDll)
     {
-        BOOL hookRc = FALSE;
-        initHook = ( AfsdInitHook ) GetProcAddress(hInitHookDll, AFSD_INIT_HOOK);
+        BOOL hookRc = TRUE;
+        AfsdInitHook initHook = ( AfsdInitHook ) GetProcAddress(hHookDll, AFSD_INIT_HOOK);
         if (initHook)
         {
             hookRc = initHook();
         }
-        FreeLibrary(hInitHookDll);
-        hInitHookDll = NULL;
+        FreeLibrary(hHookDll);
+        hHookDll = NULL;
 
         if (hookRc == FALSE)
         {
@@ -1112,7 +1216,7 @@ void afsd_Main(DWORD argc, LPTSTR *argv)
             ServiceStatus.dwCheckPoint = 2;
             ServiceStatus.dwWaitHint = 20000;
             /* accept Power Events */
-            ServiceStatus.dwControlsAccepted = SERVICE_ACCEPT_POWEREVENT;
+            ServiceStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN | SERVICE_ACCEPT_POWEREVENT | SERVICE_ACCEPT_PARAMCHANGE;
             SetServiceStatus(StatusHandle, &ServiceStatus);
         }
     }
@@ -1141,16 +1245,84 @@ void afsd_Main(DWORD argc, LPTSTR *argv)
                        osi_panic(reason, __FILE__, __LINE__);
         }
 
+        /* allow an exit to be called post rx initialization */
+        hHookDll = LoadLibrary(AFSD_HOOK_DLL);
+        if (hHookDll)
+        {
+            BOOL hookRc = TRUE;
+            AfsdRxStartedHook rxStartedHook = ( AfsdRxStartedHook ) GetProcAddress(hHookDll, AFSD_RX_STARTED_HOOK);
+            if (rxStartedHook)
+            {
+                hookRc = rxStartedHook();
+            }
+            FreeLibrary(hHookDll);
+            hHookDll = NULL;
+
+            if (hookRc == FALSE)
+            {
+                ServiceStatus.dwCurrentState = SERVICE_STOPPED;
+                ServiceStatus.dwWin32ExitCode = NO_ERROR;
+                ServiceStatus.dwCheckPoint = 0;
+                ServiceStatus.dwWaitHint = 0;
+                ServiceStatus.dwControlsAccepted = 0;
+                SetServiceStatus(StatusHandle, &ServiceStatus);
+                       
+                /* exit if initialization failed */
+                return;
+            }
+        }
+
 #ifndef NOTSERVICE
         ServiceStatus.dwCheckPoint++;
         ServiceStatus.dwWaitHint -= 5000;
         SetServiceStatus(StatusHandle, &ServiceStatus);
 #endif
+
+/* the following ifdef chooses the mode of operation for the service.  to enable
+ * a runtime flag (instead of compile-time), pioctl() would need to dynamically
+ * determine the mode, in order to use the correct ioctl special-file path. */
+#ifndef AFSIFS
         code = afsd_InitSMB(&reason, MessageBox);
         if (code != 0) {
             afsi_log("afsd_InitSMB failed: %s (code = %d)", reason, code);
             osi_panic(reason, __FILE__, __LINE__);
         }
+#else
+        code = ifs_Init(&reason);
+        if (code != 0) {
+            afsi_log("ifs_Init failed: %s (code = %d)", reason, code);
+            osi_panic(reason, __FILE__, __LINE__);
+        }     
+        for (cnt = 0; cnt < WORKER_THREADS; cnt++)
+            hAFSDWorkerThread[cnt] = CreateThread(NULL, 0, ifs_MainLoop, 0, 0, NULL);
+#endif  
+
+        /* allow an exit to be called post smb initialization */
+        hHookDll = LoadLibrary(AFSD_HOOK_DLL);
+        if (hHookDll)
+        {
+            BOOL hookRc = TRUE;
+            AfsdSmbStartedHook smbStartedHook = ( AfsdSmbStartedHook ) GetProcAddress(hHookDll, AFSD_SMB_STARTED_HOOK);
+            if (smbStartedHook)
+            {
+                hookRc = smbStartedHook();
+            }
+            FreeLibrary(hHookDll);
+            hHookDll = NULL;
+
+            if (hookRc == FALSE)
+            {
+                ServiceStatus.dwCurrentState = SERVICE_STOPPED;
+                ServiceStatus.dwWin32ExitCode = NO_ERROR;
+                ServiceStatus.dwCheckPoint = 0;
+                ServiceStatus.dwWaitHint = 0;
+                ServiceStatus.dwControlsAccepted = 0;
+                SetServiceStatus(StatusHandle, &ServiceStatus);
+                       
+                /* exit if initialization failed */
+                return;
+            }
+        }
 
         MountGlobalDrives();
 
@@ -1161,7 +1333,7 @@ void afsd_Main(DWORD argc, LPTSTR *argv)
         ServiceStatus.dwWaitHint = 0;
 
         /* accept Power events */
-        ServiceStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN | SERVICE_ACCEPT_POWEREVENT;
+        ServiceStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN | SERVICE_ACCEPT_POWEREVENT | SERVICE_ACCEPT_PARAMCHANGE;
         SetServiceStatus(StatusHandle, &ServiceStatus);
 #endif  
         {
@@ -1173,7 +1345,42 @@ void afsd_Main(DWORD argc, LPTSTR *argv)
         }
     }
 
+    /* allow an exit to be called when started */
+    hHookDll = LoadLibrary(AFSD_HOOK_DLL);
+    if (hHookDll)
+    {
+        BOOL hookRc = TRUE;
+        AfsdStartedHook startedHook = ( AfsdStartedHook ) GetProcAddress(hHookDll, AFSD_STARTED_HOOK);
+        if (startedHook)
+        {
+            hookRc = startedHook();
+        }
+        FreeLibrary(hHookDll);
+        hHookDll = NULL;
+
+        if (hookRc == FALSE)
+        {
+            ServiceStatus.dwCurrentState = SERVICE_STOPPED;
+            ServiceStatus.dwWin32ExitCode = NO_ERROR;
+            ServiceStatus.dwCheckPoint = 0;
+            ServiceStatus.dwWaitHint = 0;
+            ServiceStatus.dwControlsAccepted = 0;
+            SetServiceStatus(StatusHandle, &ServiceStatus);
+                       
+            /* exit if initialization failed */
+            return;
+        }
+    }
+
+#ifndef AFSIFS
     WaitForSingleObject(WaitToTerminate, INFINITE);
+#else
+    WaitForMultipleObjects(WORKER_THREADS, hAFSDWorkerThread, TRUE, INFINITE);
+    for (cnt = 0; cnt < WORKER_THREADS; cnt++)
+        CloseHandle(hAFSDWorkerThread[cnt]);
+#endif
+
+    afsi_log("Received Termination Signal, Stopping Service");
 
     {   
         HANDLE h; char *ptbuf[1];
@@ -1184,11 +1391,59 @@ void afsd_Main(DWORD argc, LPTSTR *argv)
         DeregisterEventSource(h);
     }
 
+    /* allow an exit to be called prior to stopping the service */
+    hHookDll = LoadLibrary(AFSD_HOOK_DLL);
+    if (hHookDll)
+    {
+        BOOL hookRc = TRUE;
+        AfsdStoppingHook stoppingHook = ( AfsdStoppingHook ) GetProcAddress(hHookDll, AFSD_STOPPING_HOOK);
+        if (stoppingHook)
+        {
+            hookRc = stoppingHook();
+        }
+        FreeLibrary(hHookDll);
+        hHookDll = NULL;
+
+        if (hookRc == FALSE)
+        {
+            ServiceStatus.dwCurrentState = SERVICE_STOPPED;
+            ServiceStatus.dwWin32ExitCode = NO_ERROR;
+            ServiceStatus.dwCheckPoint = 0;
+            ServiceStatus.dwWaitHint = 0;
+            ServiceStatus.dwControlsAccepted = 0;
+            SetServiceStatus(StatusHandle, &ServiceStatus);
+                       
+            /* exit if initialization failed */
+            return;
+        }
+    }
+
+
+#ifdef AFS_FREELANCE_CLIENT
+    cm_FreelanceShutdown();
+    afsi_log("Freelance Shutdown complete");
+#endif
+
     DismountGlobalDrives();
-    smb_Shutdown();
-    rx_Finalize();
-    RpcShutdown();
-    buf_Shutdown();
+    afsi_log("Global Drives dismounted");
+                                         
+    cm_DaemonShutdown();                 
+    afsi_log("Daemon shutdown complete");
+                                         
+    buf_Shutdown();                      
+    afsi_log("Buffer shutdown complete");
+                                         
+    rx_Finalize();                       
+    afsi_log("rx finalization complete");
+                                         
+#ifndef AFSIFS
+       smb_Shutdown();                      
+    afsi_log("smb shutdown complete");   
+#endif
+                                         
+    RpcShutdown();                       
+
+    cm_ShutdownMappedMemory();           
 
 #ifdef REGISTER_POWER_NOTIFICATIONS
     /* terminate thread used to flush cache */
@@ -1196,6 +1451,20 @@ void afsd_Main(DWORD argc, LPTSTR *argv)
         PowerNotificationThreadExit();
 #endif
 
+    /* allow an exit to be called after stopping the service */
+    hHookDll = LoadLibrary(AFSD_HOOK_DLL);
+    if (hHookDll)
+    {
+        BOOL hookRc = TRUE;
+        AfsdStoppedHook stoppedHook = ( AfsdStoppedHook ) GetProcAddress(hHookDll, AFSD_STOPPED_HOOK);
+        if (stoppedHook)
+        {
+            hookRc = stoppedHook();
+        }
+        FreeLibrary(hHookDll);
+        hHookDll = NULL;
+    }
+
     /* Remove the ExceptionFilter */
     SetUnhandledExceptionFilter(NULL);
 
@@ -1214,13 +1483,33 @@ DWORD __stdcall afsdMain_thread(void* notUsed)
     return(0);
 }
 
+void usage(void)
+{
+    fprintf(stderr, "afsd_service.exe [--validate-cache <cache-path>]");
+}
+
 int
-main(void)
+main(int argc, char * argv[])
 {
     static SERVICE_TABLE_ENTRY dispatchTable[] = {
         {AFS_DAEMON_SERVICE_NAME, (LPSERVICE_MAIN_FUNCTION) afsd_Main},
         {NULL, NULL}
     };
+    int i;
+
+    for (i = 1; i < argc; i++) {
+        if (!stricmp(argv[i],"--validate-cache")) {
+            if (++i != argc - 1) {
+                usage();
+                return(1);
+            }
+
+            return cm_ValidateMappedMemory(argv[i]);
+        } else {
+            usage();
+            return(1);
+        }
+    }
 
     if (!StartServiceCtrlDispatcher(dispatchTable))
     {
@@ -1232,7 +1521,12 @@ main(void)
                
             printf("Hit <Enter> to terminate OpenAFS Client Service\n");
             getchar();  
+#ifndef AFSIFS
             SetEvent(WaitToTerminate);
+#else
+            SetEvent(DoTerminate);
+                       dc_release_hooks();
+#endif
         }
     }