[SERVICES] On service start control message, send the service tag
[reactos.git] / base / system / services / database.c
index a5c24d2..60e6d4b 100644 (file)
 
 #include "services.h"
 
-#define NDEBUG
-#include <debug.h>
+#include <userenv.h>
+#include <strsafe.h>
 
-/*
- * Uncomment the line below to start services
- * using the SERVICE_START_PENDING state.
- */
-#define USE_SERVICE_START_PENDING
+#include <reactos/undocuser.h>
 
-/*
- * Uncomment the line below to use asynchronous IO operations
- * on the service control pipes.
- */
-// #define USE_ASYNCHRONOUS_IO
+#define NDEBUG
+#include <debug.h>
 
 
 /* GLOBALS *******************************************************************/
@@ -37,6 +30,8 @@ LIST_ENTRY ServiceListHead;
 
 static RTL_RESOURCE DatabaseLock;
 static DWORD ResumeCount = 1;
+static DWORD NoInteractiveServices = 0;
+static DWORD ServiceTag = 0;
 
 /* The critical section synchronizes service control requests */
 static CRITICAL_SECTION ControlServiceCriticalSection;
@@ -97,16 +92,13 @@ ScmCreateNewControlPipe(PSERVICE_IMAGE pServiceImage)
     }
 
     /* Create '\\.\pipe\net\NtControlPipeXXX' instance */
-    swprintf(szControlPipeName, L"\\\\.\\pipe\\net\\NtControlPipe%u", ServiceCurrent);
+    StringCchPrintfW(szControlPipeName, ARRAYSIZE(szControlPipeName),
+                     L"\\\\.\\pipe\\net\\NtControlPipe%lu", ServiceCurrent);
 
     DPRINT("PipeName: %S\n", szControlPipeName);
 
     pServiceImage->hControlPipe = CreateNamedPipeW(szControlPipeName,
-#ifdef USE_ASYNCHRONOUS_IO
                                                    PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED,
-#else
-                                                   PIPE_ACCESS_DUPLEX,
-#endif
                                                    PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT,
                                                    100,
                                                    8000,
@@ -138,9 +130,9 @@ ScmGetServiceImageByImagePath(LPWSTR lpImagePath)
         CurrentImage = CONTAINING_RECORD(ImageEntry,
                                          SERVICE_IMAGE,
                                          ImageListEntry);
-        if (_wcsicmp(CurrentImage->szImagePath, lpImagePath) == 0)
+        if (_wcsicmp(CurrentImage->pszImagePath, lpImagePath) == 0)
         {
-            DPRINT("Found image: '%S'\n", CurrentImage->szImagePath);
+            DPRINT("Found image: '%S'\n", CurrentImage->pszImagePath);
             return CurrentImage;
         }
 
@@ -154,18 +146,242 @@ ScmGetServiceImageByImagePath(LPWSTR lpImagePath)
 }
 
 
+DWORD
+ScmGetServiceNameFromTag(PTAG_INFO_NAME_FROM_TAG_IN_PARAMS InParams, PTAG_INFO_NAME_FROM_TAG_OUT_PARAMS *OutParams)
+{
+    PLIST_ENTRY ServiceEntry;
+    PSERVICE CurrentService;
+    PSERVICE_IMAGE CurrentImage;
+    PTAG_INFO_NAME_FROM_TAG_OUT_PARAMS OutBuffer = NULL;
+    DWORD dwError;
+
+    /* Lock the database */
+    ScmLockDatabaseExclusive();
+
+    /* Find the matching service */
+    ServiceEntry = ServiceListHead.Flink;
+    while (ServiceEntry != &ServiceListHead)
+    {
+        CurrentService = CONTAINING_RECORD(ServiceEntry,
+                                           SERVICE,
+                                           ServiceListEntry);
+
+        /* We must match the tag */
+        if (CurrentService->dwTag == InParams->dwTag &&
+            CurrentService->lpImage != NULL)
+        {
+            CurrentImage = CurrentService->lpImage;
+            /* And matching the PID */
+            if (CurrentImage->hProcess == (HANDLE)InParams->dwPid)
+            {
+                break;
+            }
+        }
+
+        ServiceEntry = ServiceEntry->Flink;
+    }
+
+    /* No match! */
+    if (ServiceEntry == &ServiceListHead)
+    {
+        dwError = ERROR_RETRY;
+        goto Cleanup;
+    }
+
+    /* Allocate the output buffer */
+    OutBuffer = MIDL_user_allocate(sizeof(TAG_INFO_NAME_FROM_TAG_OUT_PARAMS));
+    if (OutBuffer == NULL)
+    {
+        dwError = ERROR_NOT_ENOUGH_MEMORY;
+        goto Cleanup;
+    }
+
+    /* And the buffer for the name */
+    OutBuffer->pszName = MIDL_user_allocate(wcslen(CurrentService->lpServiceName) * sizeof(WCHAR) + sizeof(UNICODE_NULL));
+    if (OutBuffer->pszName == NULL)
+    {
+        dwError = ERROR_NOT_ENOUGH_MEMORY;
+        goto Cleanup;
+    }
+
+    /* Fill in output data */
+    wcscpy(OutBuffer->pszName, CurrentService->lpServiceName);
+    OutBuffer->TagType = TagTypeService;
+
+    /* And return */
+    *OutParams = OutBuffer;
+    dwError = ERROR_SUCCESS;
+
+Cleanup:
+
+    /* Unlock database */
+    ScmUnlockDatabase();
+
+    /* If failure, free allocated memory */
+    if (dwError != ERROR_SUCCESS)
+    {
+        MIDL_user_free(OutBuffer);
+    }
+
+    /* Return error/success */
+    return dwError;
+}
+
+
+static
+BOOL
+ScmIsSameServiceAccount(
+    _In_ PCWSTR pszAccountName1,
+    _In_ PCWSTR pszAccountName2)
+{
+    if (pszAccountName1 == NULL &&
+        pszAccountName2 == NULL)
+        return TRUE;
+
+    if (pszAccountName1 == NULL &&
+        pszAccountName2 != NULL &&
+        _wcsicmp(pszAccountName2, L"LocalSystem") == 0)
+        return TRUE;
+
+    if (pszAccountName1 != NULL &&
+        pszAccountName2 == NULL &&
+        _wcsicmp(pszAccountName1, L"LocalSystem") == 0)
+        return TRUE;
+
+    if (pszAccountName1 != NULL &&
+        pszAccountName2 != NULL &&
+        _wcsicmp(pszAccountName1, pszAccountName2) == 0)
+        return TRUE;
+
+    return FALSE;
+}
+
+
+static
+BOOL
+ScmIsLocalSystemAccount(
+    _In_ PCWSTR pszAccountName)
+{
+    if (pszAccountName == NULL ||
+        _wcsicmp(pszAccountName, L"LocalSystem") == 0)
+        return TRUE;
+
+    return FALSE;
+}
+
+
+static
+DWORD
+ScmLogonService(
+    IN PSERVICE pService,
+    IN PSERVICE_IMAGE pImage)
+{
+    DWORD dwError = ERROR_SUCCESS;
+    PROFILEINFOW ProfileInfo;
+    PWSTR pszUserName = NULL;
+    PWSTR pszDomainName = NULL;
+    PWSTR pszPassword = NULL;
+    PWSTR ptr;
+
+    DPRINT("ScmLogonService(%p %p)\n", pService, pImage);
+    DPRINT("Service %S\n", pService->lpServiceName);
+
+    if (ScmIsLocalSystemAccount(pImage->pszAccountName))
+        return ERROR_SUCCESS;
+
+    /* Get the user and domain names */
+    ptr = wcschr(pImage->pszAccountName, L'\\');
+    if (ptr != NULL)
+    {
+        *ptr = L'\0';
+        pszUserName = ptr + 1;
+        pszDomainName = pImage->pszAccountName;
+    }
+    else
+    {
+        // ERROR_INVALID_SERVICE_ACCOUNT
+        pszUserName = pImage->pszAccountName;
+        pszDomainName = NULL;
+    }
+
+    /* Build the service 'password' */
+    pszPassword = HeapAlloc(GetProcessHeap(),
+                            HEAP_ZERO_MEMORY,
+                            (wcslen(pService->lpServiceName) + 5) * sizeof(WCHAR));
+    if (pszPassword == NULL)
+    {
+        dwError = ERROR_NOT_ENOUGH_MEMORY;
+        goto done;
+    }
+
+    wcscpy(pszPassword, L"_SC_");
+    wcscat(pszPassword, pService->lpServiceName);
+
+    DPRINT("Domain: %S  User: %S  Password: %S\n", pszDomainName, pszUserName, pszPassword);
+
+    /* Do the service logon */
+    if (!LogonUserW(pszUserName,
+                    pszDomainName,
+                    pszPassword,
+                    LOGON32_LOGON_SERVICE,
+                    LOGON32_PROVIDER_DEFAULT,
+                    &pImage->hToken))
+    {
+        dwError = GetLastError();
+        DPRINT1("LogonUserW() failed (Error %lu)\n", dwError);
+
+        /* Normalize the returned error */
+        dwError = ERROR_SERVICE_LOGON_FAILED;
+        goto done;
+    }
+
+    /* Load the user profile; the per-user environment variables are thus correctly initialized */
+    ZeroMemory(&ProfileInfo, sizeof(ProfileInfo));
+    ProfileInfo.dwSize = sizeof(ProfileInfo);
+    ProfileInfo.dwFlags = PI_NOUI;
+    ProfileInfo.lpUserName = pszUserName;
+    // ProfileInfo.lpProfilePath = NULL;
+    // ProfileInfo.lpDefaultPath = NULL;
+    // ProfileInfo.lpServerName = NULL;
+    // ProfileInfo.lpPolicyPath = NULL;
+    // ProfileInfo.hProfile = NULL;
+
+    if (!LoadUserProfileW(pImage->hToken, &ProfileInfo))
+    {
+        dwError = GetLastError();
+        DPRINT1("LoadUserProfileW() failed (Error %lu)\n", dwError);
+        goto done;
+    }
+
+    pImage->hProfile = ProfileInfo.hProfile;
+
+done:
+    if (pszPassword != NULL)
+        HeapFree(GetProcessHeap(), 0, pszPassword);
+
+    if (ptr != NULL)
+        *ptr = L'\\';
+
+    return dwError;
+}
+
+
 static DWORD
 ScmCreateOrReferenceServiceImage(PSERVICE pService)
 {
-    RTL_QUERY_REGISTRY_TABLE QueryTable[2];
+    RTL_QUERY_REGISTRY_TABLE QueryTable[3];
     UNICODE_STRING ImagePath;
+    UNICODE_STRING ObjectName;
     PSERVICE_IMAGE pServiceImage = NULL;
     NTSTATUS Status;
     DWORD dwError = ERROR_SUCCESS;
+    DWORD dwRecordSize;
+    LPWSTR pString;
 
     DPRINT("ScmCreateOrReferenceServiceImage(%p)\n", pService);
 
     RtlInitUnicodeString(&ImagePath, NULL);
+    RtlInitUnicodeString(&ObjectName, NULL);
 
     /* Get service data */
     RtlZeroMemory(&QueryTable,
@@ -174,6 +390,9 @@ ScmCreateOrReferenceServiceImage(PSERVICE pService)
     QueryTable[0].Name = L"ImagePath";
     QueryTable[0].Flags = RTL_QUERY_REGISTRY_DIRECT | RTL_QUERY_REGISTRY_REQUIRED;
     QueryTable[0].EntryContext = &ImagePath;
+    QueryTable[1].Name = L"ObjectName";
+    QueryTable[1].Flags = RTL_QUERY_REGISTRY_DIRECT;
+    QueryTable[1].EntryContext = &ObjectName;
 
     Status = RtlQueryRegistryValues(RTL_REGISTRY_SERVICES,
                                     pService->lpServiceName,
@@ -187,14 +406,19 @@ ScmCreateOrReferenceServiceImage(PSERVICE pService)
     }
 
     DPRINT("ImagePath: '%wZ'\n", &ImagePath);
+    DPRINT("ObjectName: '%wZ'\n", &ObjectName);
 
     pServiceImage = ScmGetServiceImageByImagePath(ImagePath.Buffer);
     if (pServiceImage == NULL)
     {
+        dwRecordSize = sizeof(SERVICE_IMAGE) +
+                       ImagePath.Length + sizeof(WCHAR) +
+                       ((ObjectName.Length != 0) ? (ObjectName.Length + sizeof(WCHAR)) : 0);
+
         /* Create a new service image */
         pServiceImage = HeapAlloc(GetProcessHeap(),
                                   HEAP_ZERO_MEMORY,
-                                  FIELD_OFFSET(SERVICE_IMAGE, szImagePath[ImagePath.Length / sizeof(WCHAR) + 1]));
+                                  dwRecordSize);
         if (pServiceImage == NULL)
         {
             dwError = ERROR_NOT_ENOUGH_MEMORY;
@@ -203,18 +427,54 @@ ScmCreateOrReferenceServiceImage(PSERVICE pService)
 
         pServiceImage->dwImageRunCount = 1;
         pServiceImage->hControlPipe = INVALID_HANDLE_VALUE;
+        pServiceImage->hProcess = INVALID_HANDLE_VALUE;
+
+        pString = (PWSTR)((INT_PTR)pServiceImage + sizeof(SERVICE_IMAGE));
 
         /* Set the image path */
-        wcscpy(pServiceImage->szImagePath,
+        pServiceImage->pszImagePath = pString;
+        wcscpy(pServiceImage->pszImagePath,
                ImagePath.Buffer);
 
-        RtlFreeUnicodeString(&ImagePath);
+        /* Set the account name */
+        if (ObjectName.Length > 0)
+        {
+            pString = pString + wcslen(pString) + 1;
+
+            pServiceImage->pszAccountName = pString;
+            wcscpy(pServiceImage->pszAccountName,
+                   ObjectName.Buffer);
+        }
+
+        /* Service logon */
+        dwError = ScmLogonService(pService, pServiceImage);
+        if (dwError != ERROR_SUCCESS)
+        {
+            DPRINT1("ScmLogonService() failed (Error %lu)\n", dwError);
+
+            /* Release the service image */
+            HeapFree(GetProcessHeap(), 0, pServiceImage);
+
+            goto done;
+        }
 
         /* Create the control pipe */
         dwError = ScmCreateNewControlPipe(pServiceImage);
         if (dwError != ERROR_SUCCESS)
         {
+            DPRINT1("ScmCreateNewControlPipe() failed (Error %lu)\n", dwError);
+
+            /* Unload the user profile */
+            if (pServiceImage->hProfile != NULL)
+                UnloadUserProfile(pServiceImage->hToken, pServiceImage->hProfile);
+
+            /* Close the logon token */
+            if (pServiceImage->hToken != NULL)
+                CloseHandle(pServiceImage->hToken);
+
+            /* Release the service image */
             HeapFree(GetProcessHeap(), 0, pServiceImage);
+
             goto done;
         }
 
@@ -227,16 +487,28 @@ ScmCreateOrReferenceServiceImage(PSERVICE pService)
     }
     else
     {
+//        if ((lpService->Status.dwServiceType & SERVICE_WIN32_SHARE_PROCESS) == 0)
+
+        /* Fail if services in an image use different accounts */
+        if (!ScmIsSameServiceAccount(pServiceImage->pszAccountName, ObjectName.Buffer))
+        {
+            dwError = ERROR_DIFFERENT_SERVICE_ACCOUNT;
+            goto done;
+        }
+
         /* Increment the run counter */
         pServiceImage->dwImageRunCount++;
     }
 
+    DPRINT("pServiceImage->pszImagePath: %S\n", pServiceImage->pszImagePath);
+    DPRINT("pServiceImage->pszAccountName: %S\n", pServiceImage->pszAccountName);
     DPRINT("pServiceImage->dwImageRunCount: %lu\n", pServiceImage->dwImageRunCount);
 
     /* Link the service image to the service */
     pService->lpImage = pServiceImage;
 
-done:;
+done:
+    RtlFreeUnicodeString(&ObjectName);
     RtlFreeUnicodeString(&ImagePath);
 
     DPRINT("ScmCreateOrReferenceServiceImage() done (Error: %lu)\n", dwError);
@@ -245,29 +517,34 @@ done:;
 }
 
 
-static VOID
-ScmDereferenceServiceImage(PSERVICE_IMAGE pServiceImage)
+VOID
+ScmRemoveServiceImage(PSERVICE_IMAGE pServiceImage)
 {
-    DPRINT1("ScmDereferenceServiceImage() called\n");
+    DPRINT1("ScmRemoveServiceImage() called\n");
 
-    pServiceImage->dwImageRunCount--;
+    /* FIXME: Terminate the process */
 
-    if (pServiceImage->dwImageRunCount == 0)
-    {
-        DPRINT1("dwImageRunCount == 0\n");
+    /* Remove the service image from the list */
+    RemoveEntryList(&pServiceImage->ImageListEntry);
 
-        /* FIXME: Terminate the process */
+    /* Close the process handle */
+    if (pServiceImage->hProcess != INVALID_HANDLE_VALUE)
+        CloseHandle(pServiceImage->hProcess);
 
-        /* Remove the service image from the list */
-        RemoveEntryList(&pServiceImage->ImageListEntry);
+    /* Close the control pipe */
+    if (pServiceImage->hControlPipe != INVALID_HANDLE_VALUE)
+        CloseHandle(pServiceImage->hControlPipe);
 
-        /* Close the control pipe */
-        if (pServiceImage->hControlPipe != INVALID_HANDLE_VALUE)
-            CloseHandle(pServiceImage->hControlPipe);
+    /* Unload the user profile */
+    if (pServiceImage->hProfile != NULL)
+        UnloadUserProfile(pServiceImage->hToken, pServiceImage->hProfile);
 
-        /* Release the service image */
-        HeapFree(GetProcessHeap(), 0, pServiceImage);
-    }
+    /* Close the logon token */
+    if (pServiceImage->hToken != NULL)
+        CloseHandle(pServiceImage->hToken);
+
+    /* Release the service image */
+    HeapFree(GetProcessHeap(), 0, pServiceImage);
 }
 
 
@@ -358,9 +635,34 @@ ScmGetServiceEntryByResumeCount(DWORD dwResumeCount)
 }
 
 
+DWORD
+ScmGenerateServiceTag(PSERVICE lpServiceRecord)
+{
+    /* Check for an overflow */
+    if (ServiceTag == -1)
+    {
+        return ERROR_INVALID_DATA;
+    }
+
+    /* This is only valid for Win32 services */
+    if (!(lpServiceRecord->Status.dwServiceType & SERVICE_WIN32))
+    {
+        return ERROR_INVALID_PARAMETER;
+    }
+
+    /* Increment the tag counter and set it */
+    ServiceTag = ServiceTag % 0xFFFFFFFF + 1;
+    lpServiceRecord->dwTag = ServiceTag;
+
+    return ERROR_SUCCESS;
+}
+
+
 DWORD
 ScmCreateNewServiceRecord(LPCWSTR lpServiceName,
-                          PSERVICE* lpServiceRecord)
+                          PSERVICE *lpServiceRecord,
+                          DWORD dwServiceType,
+                          DWORD dwStartType)
 {
     PSERVICE lpService = NULL;
 
@@ -380,6 +682,9 @@ ScmCreateNewServiceRecord(LPCWSTR lpServiceName,
     lpService->lpServiceName = lpService->szServiceName;
     lpService->lpDisplayName = lpService->lpServiceName;
 
+    /* Set the start type */
+    lpService->dwStartType = dwStartType;
+
     /* Set the resume count */
     lpService->dwResumeCount = ResumeCount++;
 
@@ -388,12 +693,15 @@ ScmCreateNewServiceRecord(LPCWSTR lpServiceName,
                    &lpService->ServiceListEntry);
 
     /* Initialize the service status */
+    lpService->Status.dwServiceType = dwServiceType;
     lpService->Status.dwCurrentState = SERVICE_STOPPED;
     lpService->Status.dwControlsAccepted = 0;
-    lpService->Status.dwWin32ExitCode = ERROR_SERVICE_NEVER_STARTED;
+    lpService->Status.dwWin32ExitCode =
+        (dwStartType == SERVICE_DISABLED) ? ERROR_SERVICE_DISABLED : ERROR_SERVICE_NEVER_STARTED;
     lpService->Status.dwServiceSpecificExitCode = 0;
     lpService->Status.dwCheckPoint = 0;
-    lpService->Status.dwWaitHint = 2000; /* 2 seconds */
+    lpService->Status.dwWaitHint =
+        (dwServiceType & SERVICE_DRIVER) ? 0 : 2000; /* 2 seconds */
 
     return ERROR_SUCCESS;
 }
@@ -411,14 +719,22 @@ ScmDeleteServiceRecord(PSERVICE lpService)
 
     /* Dereference the service image */
     if (lpService->lpImage)
-        ScmDereferenceServiceImage(lpService->lpImage);
+    {
+        lpService->lpImage->dwImageRunCount--;
 
-    /* Decrement the group reference counter */
-    if (lpService->lpGroup)
-        lpService->lpGroup->dwRefCount--;
+        if (lpService->lpImage->dwImageRunCount == 0)
+        {
+            ScmRemoveServiceImage(lpService->lpImage);
+            lpService->lpImage = NULL;
+        }
+    }
 
-    /* FIXME: SecurityDescriptor */
+    /* Decrement the group reference counter */
+    ScmSetServiceGroup(lpService, NULL);
 
+    /* Release the SecurityDescriptor */
+    if (lpService->pSecurityDescriptor != NULL)
+        HeapFree(GetProcessHeap(), 0, lpService->pSecurityDescriptor);
 
     /* Remove the Service from the List */
     RemoveEntryList(&lpService->ServiceListEntry);
@@ -520,12 +836,12 @@ CreateServiceListEntry(LPCWSTR lpServiceName,
     DPRINT("Display name: %S\n", lpDisplayName);
 
     dwError = ScmCreateNewServiceRecord(lpServiceName,
-                                        &lpService);
+                                        &lpService,
+                                        dwServiceType,
+                                        dwStartType);
     if (dwError != ERROR_SUCCESS)
         goto done;
 
-    lpService->Status.dwServiceType = dwServiceType;
-    lpService->dwStartType = dwStartType;
     lpService->dwErrorControl = dwErrorControl;
     lpService->dwTag = dwTagId;
 
@@ -555,8 +871,32 @@ CreateServiceListEntry(LPCWSTR lpServiceName,
 
     if (ScmIsDeleteFlagSet(hServiceKey))
         lpService->bDeleted = TRUE;
+    else
+        ScmGenerateServiceTag(lpService);
+
+    if (lpService->Status.dwServiceType & SERVICE_WIN32)
+    {
+        dwError = ScmReadSecurityDescriptor(hServiceKey,
+                                            &lpService->pSecurityDescriptor);
+        if (dwError != ERROR_SUCCESS)
+            goto done;
+
+        /* Assing the default security descriptor if the security descriptor cannot be read */
+        if (lpService->pSecurityDescriptor == NULL)
+        {
+            DPRINT("No security descriptor found! Assign default security descriptor!\n");
+            dwError = ScmCreateDefaultServiceSD(&lpService->pSecurityDescriptor);
+            if (dwError != ERROR_SUCCESS)
+                goto done;
+
+            dwError = ScmWriteSecurityDescriptor(hServiceKey,
+                                                 lpService->pSecurityDescriptor);
+            if (dwError != ERROR_SUCCESS)
+                goto done;
+        }
+    }
 
-done:;
+done:
     if (lpGroup != NULL)
         HeapFree(GetProcessHeap(), 0, lpGroup);
 
@@ -565,63 +905,13 @@ done:;
 
     if (lpService != NULL)
     {
-        if (lpService->lpImage != NULL)
-            ScmDereferenceServiceImage(lpService->lpImage);
+        ASSERT(lpService->lpImage == NULL);
     }
 
     return dwError;
 }
 
 
-DWORD
-ScmDeleteRegKey(HKEY hKey, LPCWSTR lpszSubKey)
-{
-    DWORD dwRet, dwMaxSubkeyLen = 0, dwSize;
-    WCHAR szNameBuf[MAX_PATH], *lpszName = szNameBuf;
-    HKEY hSubKey = 0;
-
-    dwRet = RegOpenKeyExW(hKey, lpszSubKey, 0, KEY_READ, &hSubKey);
-    if (!dwRet)
-    {
-        /* Find the maximum subkey length so that we can allocate a buffer */
-        dwRet = RegQueryInfoKeyW(hSubKey, NULL, NULL, NULL, NULL,
-                                 &dwMaxSubkeyLen, NULL, NULL, NULL, NULL, NULL, NULL);
-        if (!dwRet)
-        {
-            dwMaxSubkeyLen++;
-            if (dwMaxSubkeyLen > sizeof(szNameBuf) / sizeof(WCHAR))
-            {
-                /* Name too big: alloc a buffer for it */
-                lpszName = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, dwMaxSubkeyLen * sizeof(WCHAR));
-            }
-
-            if (!lpszName)
-                dwRet = ERROR_NOT_ENOUGH_MEMORY;
-            else
-            {
-                while (dwRet == ERROR_SUCCESS)
-                {
-                    dwSize = dwMaxSubkeyLen;
-                    dwRet = RegEnumKeyExW(hSubKey, 0, lpszName, &dwSize, NULL, NULL, NULL, NULL);
-                    if (dwRet == ERROR_SUCCESS || dwRet == ERROR_MORE_DATA)
-                        dwRet = ScmDeleteRegKey(hSubKey, lpszName);
-                }
-                if (dwRet == ERROR_NO_MORE_ITEMS)
-                    dwRet = ERROR_SUCCESS;
-
-                if (lpszName != szNameBuf)
-                    HeapFree(GetProcessHeap(), 0, lpszName); /* Free buffer if allocated */
-            }
-        }
-
-        RegCloseKey(hSubKey);
-        if (!dwRet)
-            dwRet = RegDeleteKeyW(hKey, lpszSubKey);
-    }
-    return dwRet;
-}
-
-
 VOID
 ScmDeleteMarkedServices(VOID)
 {
@@ -637,7 +927,7 @@ ScmDeleteMarkedServices(VOID)
 
         ServiceEntry = ServiceEntry->Flink;
 
-        if (CurrentService->bDeleted == TRUE)
+        if (CurrentService->bDeleted != FALSE)
         {
             dwError = RegOpenKeyExW(HKEY_LOCAL_MACHINE,
                                     L"System\\CurrentControlSet\\Services",
@@ -662,6 +952,33 @@ ScmDeleteMarkedServices(VOID)
 }
 
 
+static
+VOID
+ScmGetNoInteractiveServicesValue(VOID)
+{
+    HKEY hKey;
+    DWORD dwKeySize;
+    LONG lError;
+
+    lError = RegOpenKeyExW(HKEY_LOCAL_MACHINE,
+                           L"SYSTEM\\CurrentControlSet\\Control\\Windows",
+                           0,
+                           KEY_READ,
+                           &hKey);
+    if (lError == ERROR_SUCCESS)
+    {
+        dwKeySize = sizeof(NoInteractiveServices);
+        lError = RegQueryValueExW(hKey,
+                                  L"NoInteractiveServices",
+                                  0,
+                                  NULL,
+                                  (LPBYTE)&NoInteractiveServices,
+                                  &dwKeySize);
+        RegCloseKey(hKey);
+    }
+}
+
+
 DWORD
 ScmCreateServiceDatabase(VOID)
 {
@@ -675,11 +992,15 @@ ScmCreateServiceDatabase(VOID)
 
     DPRINT("ScmCreateServiceDatabase() called\n");
 
+    /* Retrieve the NoInteractiveServies value */
+    ScmGetNoInteractiveServicesValue();
+
+    /* Create the service group list */
     dwError = ScmCreateGroupList();
     if (dwError != ERROR_SUCCESS)
         return dwError;
 
-    /* Initialize basic variables */
+    /* Initialize image and service lists */
     InitializeListHead(&ImageListHead);
     InitializeListHead(&ServiceListHead);
 
@@ -830,6 +1151,11 @@ ScmCheckDriver(PSERVICE Service)
 
             /* Mark service as 'running' */
             Service->Status.dwCurrentState = SERVICE_RUNNING;
+            Service->Status.dwControlsAccepted = SERVICE_ACCEPT_STOP;
+            Service->Status.dwWin32ExitCode = ERROR_SUCCESS;
+            Service->Status.dwServiceSpecificExitCode = 0;
+            Service->Status.dwCheckPoint = 0;
+            Service->Status.dwWaitHint = 0;
 
             /* Mark the service group as 'running' */
             if (Service->lpGroup != NULL)
@@ -880,7 +1206,9 @@ ScmGetBootAndSystemDriverState(VOID)
 
 
 DWORD
-ScmControlService(PSERVICE Service,
+ScmControlService(HANDLE hControlPipe,
+                  PWSTR pServiceName,
+                  SERVICE_STATUS_HANDLE hServiceStatus,
                   DWORD dwControl)
 {
     PSCM_CONTROL_PACKET ControlPacket;
@@ -892,9 +1220,7 @@ ScmControlService(PSERVICE Service,
     PWSTR Ptr;
     DWORD dwError = ERROR_SUCCESS;
     BOOL bResult;
-#ifdef USE_ASYNCHRONOUS_IO
-    OVERLAPPED Overlapped = {0, 0, 0, 0, 0};
-#endif
+    OVERLAPPED Overlapped = {0};
 
     DPRINT("ScmControlService() called\n");
 
@@ -903,7 +1229,7 @@ ScmControlService(PSERVICE Service,
 
     /* Calculate the total length of the start command line */
     PacketSize = sizeof(SCM_CONTROL_PACKET);
-    PacketSize += (DWORD)((wcslen(Service->lpServiceName) + 1) * sizeof(WCHAR));
+    PacketSize += (DWORD)((wcslen(pServiceName) + 1) * sizeof(WCHAR));
 
     ControlPacket = HeapAlloc(GetProcessHeap(),
                               HEAP_ZERO_MEMORY,
@@ -916,38 +1242,37 @@ ScmControlService(PSERVICE Service,
 
     ControlPacket->dwSize = PacketSize;
     ControlPacket->dwControl = dwControl;
-    ControlPacket->hServiceStatus = (SERVICE_STATUS_HANDLE)Service;
+    ControlPacket->hServiceStatus = hServiceStatus;
 
     ControlPacket->dwServiceNameOffset = sizeof(SCM_CONTROL_PACKET);
 
     Ptr = (PWSTR)((PBYTE)ControlPacket + ControlPacket->dwServiceNameOffset);
-    wcscpy(Ptr, Service->lpServiceName);
+    wcscpy(Ptr, pServiceName);
 
     ControlPacket->dwArgumentsCount = 0;
     ControlPacket->dwArgumentsOffset = 0;
 
-#ifdef USE_ASYNCHRONOUS_IO
-    bResult = WriteFile(Service->lpImage->hControlPipe,
+    bResult = WriteFile(hControlPipe,
                         ControlPacket,
                         PacketSize,
                         &dwWriteCount,
                         &Overlapped);
     if (bResult == FALSE)
     {
-        DPRINT1("WriteFile() returned FALSE\n");
+        DPRINT("WriteFile() returned FALSE\n");
 
         dwError = GetLastError();
         if (dwError == ERROR_IO_PENDING)
         {
-            DPRINT1("dwError: ERROR_IO_PENDING\n");
+            DPRINT("dwError: ERROR_IO_PENDING\n");
 
-            dwError = WaitForSingleObject(Service->lpImage->hControlPipe,
+            dwError = WaitForSingleObject(hControlPipe,
                                           PipeTimeout);
-            DPRINT1("WaitForSingleObject() returned %lu\n", dwError);
+            DPRINT("WaitForSingleObject() returned %lu\n", dwError);
 
             if (dwError == WAIT_TIMEOUT)
             {
-                bResult = CancelIo(Service->lpImage->hControlPipe);
+                bResult = CancelIo(hControlPipe);
                 if (bResult == FALSE)
                 {
                     DPRINT1("CancelIo() failed (Error: %lu)\n", GetLastError());
@@ -958,7 +1283,7 @@ ScmControlService(PSERVICE Service,
             }
             else if (dwError == WAIT_OBJECT_0)
             {
-                bResult = GetOverlappedResult(Service->lpImage->hControlPipe,
+                bResult = GetOverlappedResult(hControlPipe,
                                               &Overlapped,
                                               &dwWriteCount,
                                               TRUE);
@@ -981,27 +1306,27 @@ ScmControlService(PSERVICE Service,
     /* Read the reply */
     Overlapped.hEvent = (HANDLE) NULL;
 
-    bResult = ReadFile(Service->lpImage->hControlPipe,
+    bResult = ReadFile(hControlPipe,
                        &ReplyPacket,
                        sizeof(SCM_REPLY_PACKET),
                        &dwReadCount,
                        &Overlapped);
     if (bResult == FALSE)
     {
-        DPRINT1("ReadFile() returned FALSE\n");
+        DPRINT("ReadFile() returned FALSE\n");
 
         dwError = GetLastError();
         if (dwError == ERROR_IO_PENDING)
         {
-            DPRINT1("dwError: ERROR_IO_PENDING\n");
+            DPRINT("dwError: ERROR_IO_PENDING\n");
 
-            dwError = WaitForSingleObject(Service->lpImage->hControlPipe,
+            dwError = WaitForSingleObject(hControlPipe,
                                           PipeTimeout);
-            DPRINT1("WaitForSingleObject() returned %lu\n", dwError);
+            DPRINT("WaitForSingleObject() returned %lu\n", dwError);
 
             if (dwError == WAIT_TIMEOUT)
             {
-                bResult = CancelIo(Service->lpImage->hControlPipe);
+                bResult = CancelIo(hControlPipe);
                 if (bResult == FALSE)
                 {
                     DPRINT1("CancelIo() failed (Error: %lu)\n", GetLastError());
@@ -1012,7 +1337,7 @@ ScmControlService(PSERVICE Service,
             }
             else if (dwError == WAIT_OBJECT_0)
             {
-                bResult = GetOverlappedResult(Service->lpImage->hControlPipe,
+                bResult = GetOverlappedResult(hControlPipe,
                                               &Overlapped,
                                               &dwReadCount,
                                               TRUE);
@@ -1032,45 +1357,8 @@ ScmControlService(PSERVICE Service,
         }
     }
 
-#else
-    /* Send the control packet */
-    bResult = WriteFile(Service->lpImage->hControlPipe,
-                        ControlPacket,
-                        PacketSize,
-                        &dwWriteCount,
-                        NULL);
-    if (bResult == FALSE)
-    {
-        dwError = GetLastError();
-        DPRINT("WriteFile() failed (Error %lu)\n", dwError);
-
-        if ((dwError == ERROR_GEN_FAILURE) &&
-            (dwControl == SERVICE_CONTROL_STOP))
-        {
-            /* Service is already terminated */
-            Service->Status.dwCurrentState = SERVICE_STOPPED;
-            Service->Status.dwControlsAccepted = 0;
-            Service->Status.dwWin32ExitCode = ERROR_SERVICE_NOT_ACTIVE;
-            dwError = ERROR_SUCCESS;
-        }
-        goto Done;
-    }
-
-    /* Read the reply */
-    bResult = ReadFile(Service->lpImage->hControlPipe,
-                       &ReplyPacket,
-                       sizeof(SCM_REPLY_PACKET),
-                       &dwReadCount,
-                       NULL);
-    if (bResult == FALSE)
-    {
-        dwError = GetLastError();
-        DPRINT("ReadFile() failed (Error %lu)\n", dwError);
-    }
-#endif
-
 Done:
-    /* Release the contol packet */
+    /* Release the control packet */
     HeapFree(GetProcessHeap(),
              0,
              ControlPacket);
@@ -1080,12 +1368,6 @@ Done:
         dwError = ReplyPacket.dwError;
     }
 
-    if (dwError == ERROR_SUCCESS &&
-        dwControl == SERVICE_CONTROL_STOP)
-    {
-        ScmDereferenceServiceImage(Service->lpImage);
-    }
-
     LeaveCriticalSection(&ControlServiceCriticalSection);
 
     DPRINT("ScmControlService() done\n");
@@ -1099,44 +1381,48 @@ ScmSendStartCommand(PSERVICE Service,
                     DWORD argc,
                     LPWSTR* argv)
 {
+    DWORD dwError = ERROR_SUCCESS;
     PSCM_CONTROL_PACKET ControlPacket;
     SCM_REPLY_PACKET ReplyPacket;
     DWORD PacketSize;
-    PWSTR Ptr;
-    DWORD dwWriteCount = 0;
-    DWORD dwReadCount = 0;
-    DWORD dwError = ERROR_SUCCESS;
     DWORD i;
+    PWSTR Ptr;
     PWSTR *pOffPtr;
     PWSTR pArgPtr;
     BOOL bResult;
-#ifdef USE_ASYNCHRONOUS_IO
-    OVERLAPPED Overlapped = {0, 0, 0, 0, 0};
-#endif
+    DWORD dwWriteCount = 0;
+    DWORD dwReadCount = 0;
+    OVERLAPPED Overlapped = {0};
 
     DPRINT("ScmSendStartCommand() called\n");
 
     /* Calculate the total length of the start command line */
-    PacketSize = sizeof(SCM_CONTROL_PACKET) +
-                 (DWORD)((wcslen(Service->lpServiceName) + 1) * sizeof(WCHAR));
+    PacketSize = sizeof(SCM_CONTROL_PACKET);
+    PacketSize += (DWORD)((wcslen(Service->lpServiceName) + 1) * sizeof(WCHAR));
 
-    /* Calculate the required packet size for the start arguments */
+    /*
+     * Calculate the required packet size for the start argument vector 'argv',
+     * composed of the list of pointer offsets, followed by UNICODE strings.
+     * The strings are stored continuously after the vector of offsets, with
+     * the offsets being relative to the beginning of the vector, as in the
+     * following layout (with N == argc):
+     *     [argOff(0)]...[argOff(N-1)][str(0)]...[str(N-1)] .
+     */
     if (argc > 0 && argv != NULL)
     {
-        PacketSize = ALIGN_UP(PacketSize, LPWSTR);
+        PacketSize = ALIGN_UP(PacketSize, PWSTR);
+        PacketSize += (argc * sizeof(PWSTR));
 
         DPRINT("Argc: %lu\n", argc);
         for (i = 0; i < argc; i++)
         {
             DPRINT("Argv[%lu]: %S\n", i, argv[i]);
-            PacketSize += (DWORD)((wcslen(argv[i]) + 1) * sizeof(WCHAR) + sizeof(PWSTR));
+            PacketSize += (DWORD)((wcslen(argv[i]) + 1) * sizeof(WCHAR));
         }
     }
 
     /* Allocate a control packet */
-    ControlPacket = HeapAlloc(GetProcessHeap(),
-                              HEAP_ZERO_MEMORY,
-                              PacketSize);
+    ControlPacket = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, PacketSize);
     if (ControlPacket == NULL)
         return ERROR_NOT_ENOUGH_MEMORY;
 
@@ -1145,22 +1431,24 @@ ScmSendStartCommand(PSERVICE Service,
                                ? SERVICE_CONTROL_START_OWN
                                : SERVICE_CONTROL_START_SHARE;
     ControlPacket->hServiceStatus = (SERVICE_STATUS_HANDLE)Service;
-    ControlPacket->dwServiceNameOffset = sizeof(SCM_CONTROL_PACKET);
+    ControlPacket->dwServiceTag = Service->dwTag;
 
-    Ptr = (PWSTR)((PBYTE)ControlPacket + ControlPacket->dwServiceNameOffset);
+    /* Copy the start command line */
+    ControlPacket->dwServiceNameOffset = sizeof(SCM_CONTROL_PACKET);
+    Ptr = (PWSTR)((ULONG_PTR)ControlPacket + ControlPacket->dwServiceNameOffset);
     wcscpy(Ptr, Service->lpServiceName);
 
-    ControlPacket->dwArgumentsCount = 0;
+    ControlPacket->dwArgumentsCount  = 0;
     ControlPacket->dwArgumentsOffset = 0;
 
-    /* Copy argument list */
+    /* Copy the argument vector */
     if (argc > 0 && argv != NULL)
     {
         Ptr += wcslen(Service->lpServiceName) + 1;
         pOffPtr = (PWSTR*)ALIGN_UP_POINTER(Ptr, PWSTR);
         pArgPtr = (PWSTR)((ULONG_PTR)pOffPtr + argc * sizeof(PWSTR));
 
-        ControlPacket->dwArgumentsCount = argc;
+        ControlPacket->dwArgumentsCount  = argc;
         ControlPacket->dwArgumentsOffset = (DWORD)((ULONG_PTR)pOffPtr - (ULONG_PTR)ControlPacket);
 
         DPRINT("dwArgumentsCount: %lu\n", ControlPacket->dwArgumentsCount);
@@ -1168,16 +1456,13 @@ ScmSendStartCommand(PSERVICE Service,
 
         for (i = 0; i < argc; i++)
         {
-             wcscpy(pArgPtr, argv[i]);
-             *pOffPtr = (PWSTR)((ULONG_PTR)pArgPtr - (ULONG_PTR)pOffPtr);
-             DPRINT("offset: %p\n", *pOffPtr);
-
-             pArgPtr += wcslen(argv[i]) + 1;
-             pOffPtr++;
+            wcscpy(pArgPtr, argv[i]);
+            pOffPtr[i] = (PWSTR)((ULONG_PTR)pArgPtr - (ULONG_PTR)pOffPtr);
+            DPRINT("offset[%lu]: %p\n", i, pOffPtr[i]);
+            pArgPtr += wcslen(argv[i]) + 1;
         }
     }
 
-#ifdef USE_ASYNCHRONOUS_IO
     bResult = WriteFile(Service->lpImage->hControlPipe,
                         ControlPacket,
                         PacketSize,
@@ -1185,16 +1470,16 @@ ScmSendStartCommand(PSERVICE Service,
                         &Overlapped);
     if (bResult == FALSE)
     {
-        DPRINT1("WriteFile() returned FALSE\n");
+        DPRINT("WriteFile() returned FALSE\n");
 
         dwError = GetLastError();
         if (dwError == ERROR_IO_PENDING)
         {
-            DPRINT1("dwError: ERROR_IO_PENDING\n");
+            DPRINT("dwError: ERROR_IO_PENDING\n");
 
             dwError = WaitForSingleObject(Service->lpImage->hControlPipe,
                                           PipeTimeout);
-            DPRINT1("WaitForSingleObject() returned %lu\n", dwError);
+            DPRINT("WaitForSingleObject() returned %lu\n", dwError);
 
             if (dwError == WAIT_TIMEOUT)
             {
@@ -1239,16 +1524,16 @@ ScmSendStartCommand(PSERVICE Service,
                        &Overlapped);
     if (bResult == FALSE)
     {
-        DPRINT1("ReadFile() returned FALSE\n");
+        DPRINT("ReadFile() returned FALSE\n");
 
         dwError = GetLastError();
         if (dwError == ERROR_IO_PENDING)
         {
-            DPRINT1("dwError: ERROR_IO_PENDING\n");
+            DPRINT("dwError: ERROR_IO_PENDING\n");
 
             dwError = WaitForSingleObject(Service->lpImage->hControlPipe,
                                           PipeTimeout);
-            DPRINT1("WaitForSingleObject() returned %lu\n", dwError);
+            DPRINT("WaitForSingleObject() returned %lu\n", dwError);
 
             if (dwError == WAIT_TIMEOUT)
             {
@@ -1283,35 +1568,8 @@ ScmSendStartCommand(PSERVICE Service,
         }
     }
 
-#else
-    /* Send the start command */
-    bResult = WriteFile(Service->lpImage->hControlPipe,
-                        ControlPacket,
-                        PacketSize,
-                        &dwWriteCount,
-                        NULL);
-    if (bResult == FALSE)
-    {
-        dwError = GetLastError();
-        DPRINT("WriteFile() failed (Error %lu)\n", dwError);
-        goto Done;
-    }
-
-    /* Read the reply */
-    bResult = ReadFile(Service->lpImage->hControlPipe,
-                       &ReplyPacket,
-                       sizeof(SCM_REPLY_PACKET),
-                       &dwReadCount,
-                       NULL);
-    if (bResult == FALSE)
-    {
-        dwError = GetLastError();
-        DPRINT("ReadFile() failed (Error %lu)\n", dwError);
-    }
-#endif
-
 Done:
-    /* Release the contol packet */
+    /* Release the control packet */
     HeapFree(GetProcessHeap(),
              0,
              ControlPacket);
@@ -1334,13 +1592,15 @@ ScmWaitForServiceConnect(PSERVICE Service)
     DWORD dwProcessId = 0;
     DWORD dwError = ERROR_SUCCESS;
     BOOL bResult;
-#ifdef USE_ASYNCHRONOUS_IO
-    OVERLAPPED Overlapped = {0, 0, 0, 0, 0};
+    OVERLAPPED Overlapped = {0};
+#if 0
+    LPCWSTR lpLogStrings[3];
+    WCHAR szBuffer1[20];
+    WCHAR szBuffer2[20];
 #endif
 
     DPRINT("ScmWaitForServiceConnect()\n");
 
-#ifdef USE_ASYNCHRONOUS_IO
     Overlapped.hEvent = (HANDLE)NULL;
 
     bResult = ConnectNamedPipe(Service->lpImage->hControlPipe,
@@ -1368,6 +1628,18 @@ ScmWaitForServiceConnect(PSERVICE Service)
                     DPRINT1("CancelIo() failed (Error: %lu)\n", GetLastError());
                 }
 
+#if 0
+                _ultow(PipeTimeout, szBuffer1, 10);
+                lpLogStrings[0] = Service->lpDisplayName;
+                lpLogStrings[1] = szBuffer1;
+
+                ScmLogEvent(EVENT_CONNECTION_TIMEOUT,
+                            EVENTLOG_ERROR_TYPE,
+                            2,
+                            lpLogStrings);
+#endif
+                DPRINT1("Log EVENT_CONNECTION_TIMEOUT by %S\n", Service->lpDisplayName);
+
                 return ERROR_SERVICE_REQUEST_TIMEOUT;
             }
             else if (dwError == WAIT_OBJECT_0)
@@ -1423,6 +1695,17 @@ ScmWaitForServiceConnect(PSERVICE Service)
                     DPRINT1("CancelIo() failed (Error: %lu)\n", GetLastError());
                 }
 
+#if 0
+                _ultow(PipeTimeout, szBuffer1, 10);
+                lpLogStrings[0] = szBuffer1;
+
+                ScmLogEvent(EVENT_READFILE_TIMEOUT,
+                            EVENTLOG_ERROR_TYPE,
+                            1,
+                            lpLogStrings);
+#endif
+                DPRINT1("Log EVENT_READFILE_TIMEOUT by %S\n", Service->lpDisplayName);
+
                 return ERROR_SERVICE_REQUEST_TIMEOUT;
             }
             else if (dwError == WAIT_OBJECT_0)
@@ -1455,42 +1738,28 @@ ScmWaitForServiceConnect(PSERVICE Service)
         }
     }
 
-    DPRINT1("ScmWaitForServiceConnect() done\n");
+    if (dwProcessId != Service->lpImage->dwProcessId)
+    {
+#if 0
+        _ultow(Service->lpImage->dwProcessId, szBuffer1, 10);
+        _ultow(dwProcessId, szBuffer2, 10);
 
-    return ERROR_SUCCESS;
-#else
+        lpLogStrings[0] = Service->lpDisplayName;
+        lpLogStrings[1] = szBuffer1;
+        lpLogStrings[2] = szBuffer2;
 
-    /* Connect control pipe */
-    if (ConnectNamedPipe(Service->lpImage->hControlPipe, NULL) ?
-        TRUE : (dwError = GetLastError()) == ERROR_PIPE_CONNECTED)
-    {
-        DPRINT("Control pipe connected!\n");
-
-        /* Read SERVICE_STATUS_HANDLE from pipe */
-        bResult = ReadFile(Service->lpImage->hControlPipe,
-                           (LPVOID)&dwProcessId,
-                           sizeof(DWORD),
-                           &dwRead,
-                           NULL);
-        if (bResult == FALSE)
-        {
-            dwError = GetLastError();
-            DPRINT1("Reading the service control pipe failed (Error %lu)\n",
-                    dwError);
-        }
-        else
-        {
-            dwError = ERROR_SUCCESS;
-            DPRINT("Read control pipe successfully\n");
-        }
-    }
-    else
-    {
-        DPRINT1("Connecting control pipe failed! (Error %lu)\n", dwError);
+        ScmLogEvent(EVENT_SERVICE_DIFFERENT_PID_CONNECTED,
+                    EVENTLOG_WARNING_TYPE,
+                    3,
+                    lpLogStrings);
+#endif
+
+        DPRINT1("Log EVENT_SERVICE_DIFFERENT_PID_CONNECTED by %S\n", Service->lpDisplayName);
     }
 
-    return dwError;
-#endif
+    DPRINT("ScmWaitForServiceConnect() done\n");
+
+    return ERROR_SUCCESS;
 }
 
 
@@ -1501,6 +1770,7 @@ ScmStartUserModeService(PSERVICE Service,
 {
     PROCESS_INFORMATION ProcessInformation;
     STARTUPINFOW StartupInfo;
+    LPVOID lpEnvironment;
     BOOL Result;
     DWORD dwError = ERROR_SUCCESS;
 
@@ -1518,20 +1788,87 @@ ScmStartUserModeService(PSERVICE Service,
     StartupInfo.cb = sizeof(StartupInfo);
     ZeroMemory(&ProcessInformation, sizeof(ProcessInformation));
 
-    Result = CreateProcessW(NULL,
-                            Service->lpImage->szImagePath,
-                            NULL,
-                            NULL,
-                            FALSE,
-                            DETACHED_PROCESS | CREATE_SUSPENDED,
-                            NULL,
-                            NULL,
-                            &StartupInfo,
-                            &ProcessInformation);
+    if (Service->lpImage->hToken)
+    {
+        /* User token: Run the service under the user account */
+
+        if (!CreateEnvironmentBlock(&lpEnvironment, Service->lpImage->hToken, FALSE))
+        {
+            /* We failed, run the service with the current environment */
+            DPRINT1("CreateEnvironmentBlock() failed with error %d; service '%S' will run with the current environment.\n",
+                    GetLastError(), Service->lpServiceName);
+            lpEnvironment = NULL;
+        }
+
+        /* Impersonate the new user */
+        Result = ImpersonateLoggedOnUser(Service->lpImage->hToken);
+        if (Result)
+        {
+            /* Launch the process in the user's logon session */
+            Result = CreateProcessAsUserW(Service->lpImage->hToken,
+                                          NULL,
+                                          Service->lpImage->pszImagePath,
+                                          NULL,
+                                          NULL,
+                                          FALSE,
+                                          CREATE_UNICODE_ENVIRONMENT | DETACHED_PROCESS | CREATE_SUSPENDED,
+                                          lpEnvironment,
+                                          NULL,
+                                          &StartupInfo,
+                                          &ProcessInformation);
+            if (!Result)
+                dwError = GetLastError();
+
+            /* Revert the impersonation */
+            RevertToSelf();
+        }
+        else
+        {
+            dwError = GetLastError();
+            DPRINT1("ImpersonateLoggedOnUser() failed with error %d\n", dwError);
+        }
+    }
+    else
+    {
+        /* No user token: Run the service under the LocalSystem account */
+
+        if (!CreateEnvironmentBlock(&lpEnvironment, NULL, TRUE))
+        {
+            /* We failed, run the service with the current environment */
+            DPRINT1("CreateEnvironmentBlock() failed with error %d; service '%S' will run with the current environment.\n",
+                    GetLastError(), Service->lpServiceName);
+            lpEnvironment = NULL;
+        }
+
+        /* Use the interactive desktop if the service is interactive */
+        if ((NoInteractiveServices == 0) &&
+            (Service->Status.dwServiceType & SERVICE_INTERACTIVE_PROCESS))
+        {
+            StartupInfo.dwFlags |= STARTF_INHERITDESKTOP;
+            StartupInfo.lpDesktop = L"WinSta0\\Default";
+        }
+
+        Result = CreateProcessW(NULL,
+                                Service->lpImage->pszImagePath,
+                                NULL,
+                                NULL,
+                                FALSE,
+                                CREATE_UNICODE_ENVIRONMENT | DETACHED_PROCESS | CREATE_SUSPENDED,
+                                lpEnvironment,
+                                NULL,
+                                &StartupInfo,
+                                &ProcessInformation);
+        if (!Result)
+            dwError = GetLastError();
+    }
+
+    if (lpEnvironment)
+        DestroyEnvironmentBlock(lpEnvironment);
+
     if (!Result)
     {
-        dwError = GetLastError();
-        DPRINT1("Starting '%S' failed!\n", Service->lpServiceName);
+        DPRINT1("Starting '%S' failed with error %d\n",
+                Service->lpServiceName, dwError);
         return dwError;
     }
 
@@ -1542,30 +1879,25 @@ ScmStartUserModeService(PSERVICE Service,
            ProcessInformation.dwThreadId,
            ProcessInformation.hThread);
 
-    /* Get process handle and id */
+    /* Get the process handle and ID */
+    Service->lpImage->hProcess = ProcessInformation.hProcess;
     Service->lpImage->dwProcessId = ProcessInformation.dwProcessId;
 
-    /* Resume Thread */
+    /* Resume the main thread and close its handle */
     ResumeThread(ProcessInformation.hThread);
+    CloseHandle(ProcessInformation.hThread);
 
     /* Connect control pipe */
     dwError = ScmWaitForServiceConnect(Service);
-    if (dwError == ERROR_SUCCESS)
-    {
-        /* Send start command */
-        dwError = ScmSendStartCommand(Service, argc, argv);
-    }
-    else
+    if (dwError != ERROR_SUCCESS)
     {
         DPRINT1("Connecting control pipe failed! (Error %lu)\n", dwError);
         Service->lpImage->dwProcessId = 0;
+        return dwError;
     }
 
-    /* Close thread and process handle */
-    CloseHandle(ProcessInformation.hThread);
-    CloseHandle(ProcessInformation.hProcess);
-
-    return dwError;
+    /* Send the start command */
+    return ScmSendStartCommand(Service, argc, argv);
 }
 
 
@@ -1576,8 +1908,8 @@ ScmLoadService(PSERVICE Service,
 {
     PSERVICE_GROUP Group = Service->lpGroup;
     DWORD dwError = ERROR_SUCCESS;
-    LPCWSTR ErrorLogStrings[2];
-    WCHAR szErrorBuffer[32];
+    LPCWSTR lpLogStrings[2];
+    WCHAR szLogBuffer[80];
 
     DPRINT("ScmLoadService() called\n");
     DPRINT("Start Service %p (%S)\n", Service, Service->lpServiceName);
@@ -1592,13 +1924,8 @@ ScmLoadService(PSERVICE Service,
 
     if (Service->Status.dwServiceType & SERVICE_DRIVER)
     {
-        /* Load driver */
-        dwError = ScmLoadDriver(Service);
-        if (dwError == ERROR_SUCCESS)
-        {
-            Service->Status.dwControlsAccepted = SERVICE_ACCEPT_STOP;
-            Service->Status.dwCurrentState = SERVICE_RUNNING;
-        }
+        /* Start the driver */
+        dwError = ScmStartDriver(Service);
     }
     else // if (Service->Status.dwServiceType & (SERVICE_WIN32 | SERVICE_INTERACTIVE_PROCESS))
     {
@@ -1609,16 +1936,17 @@ ScmLoadService(PSERVICE Service,
             dwError = ScmStartUserModeService(Service, argc, argv);
             if (dwError == ERROR_SUCCESS)
             {
-#ifdef USE_SERVICE_START_PENDING
                 Service->Status.dwCurrentState = SERVICE_START_PENDING;
-#else
-                Service->Status.dwCurrentState = SERVICE_RUNNING;
-#endif
+                Service->Status.dwControlsAccepted = 0;
             }
             else
             {
-                ScmDereferenceServiceImage(Service->lpImage);
-                Service->lpImage = NULL;
+                Service->lpImage->dwImageRunCount--;
+                if (Service->lpImage->dwImageRunCount == 0)
+                {
+                    ScmRemoveServiceImage(Service->lpImage);
+                    Service->lpImage = NULL;
+                }
             }
         }
     }
@@ -1631,18 +1959,30 @@ ScmLoadService(PSERVICE Service,
         {
             Group->ServicesRunning = TRUE;
         }
+
+        /* Log a successful service start */
+        LoadStringW(GetModuleHandle(NULL), IDS_SERVICE_START, szLogBuffer, 80);
+        lpLogStrings[0] = Service->lpDisplayName;
+        lpLogStrings[1] = szLogBuffer;
+
+        ScmLogEvent(EVENT_SERVICE_CONTROL_SUCCESS,
+                    EVENTLOG_INFORMATION_TYPE,
+                    2,
+                    lpLogStrings);
     }
     else
     {
         if (Service->dwErrorControl != SERVICE_ERROR_IGNORE)
         {
             /* Log a failed service start */
-            swprintf(szErrorBuffer, L"%lu", dwError);
-            ErrorLogStrings[0] = Service->lpServiceName;
-            ErrorLogStrings[1] = szErrorBuffer;
-            ScmLogError(EVENT_SERVICE_START_FAILED,
+            StringCchPrintfW(szLogBuffer, ARRAYSIZE(szLogBuffer),
+                             L"%lu", dwError);
+            lpLogStrings[0] = Service->lpServiceName;
+            lpLogStrings[1] = szLogBuffer;
+            ScmLogEvent(EVENT_SERVICE_START_FAILED,
+                        EVENTLOG_ERROR_TYPE,
                         2,
-                        ErrorLogStrings);
+                        lpLogStrings);
         }
 
 #if 0
@@ -1716,13 +2056,15 @@ done:
 VOID
 ScmAutoStartServices(VOID)
 {
-    DWORD dwError = ERROR_SUCCESS;
+    DWORD dwError;
     PLIST_ENTRY GroupEntry;
     PLIST_ENTRY ServiceEntry;
     PSERVICE_GROUP CurrentGroup;
     PSERVICE CurrentService;
     WCHAR szSafeBootServicePath[MAX_PATH];
+    DWORD SafeBootEnabled;
     HKEY hKey;
+    DWORD dwKeySize;
     ULONG i;
 
     /*
@@ -1731,6 +2073,28 @@ ScmAutoStartServices(VOID)
      */
     ASSERT(ScmInitialize);
 
+    /* Retrieve the SafeBoot parameter */
+    dwError = RegOpenKeyExW(HKEY_LOCAL_MACHINE,
+                            L"SYSTEM\\CurrentControlSet\\Control\\SafeBoot\\Option",
+                            0,
+                            KEY_READ,
+                            &hKey);
+    if (dwError == ERROR_SUCCESS)
+    {
+        dwKeySize = sizeof(SafeBootEnabled);
+        dwError = RegQueryValueExW(hKey,
+                                   L"OptionValue",
+                                   0,
+                                   NULL,
+                                   (LPBYTE)&SafeBootEnabled,
+                                   &dwKeySize);
+        RegCloseKey(hKey);
+    }
+
+    /* Default to Normal boot if the value doesn't exist */
+    if (dwError != ERROR_SUCCESS)
+        SafeBootEnabled = 0;
+
     /* Acquire the service control critical section, to synchronize starts */
     EnterCriticalSection(&ControlServiceCriticalSection);
 
@@ -1741,23 +2105,25 @@ ScmAutoStartServices(VOID)
         CurrentService = CONTAINING_RECORD(ServiceEntry, SERVICE, ServiceListEntry);
 
         /* Build the safe boot path */
-        wcscpy(szSafeBootServicePath,
-               L"SYSTEM\\CurrentControlSet\\Control\\SafeBoot");
+        StringCchCopyW(szSafeBootServicePath, ARRAYSIZE(szSafeBootServicePath),
+                       L"SYSTEM\\CurrentControlSet\\Control\\SafeBoot");
 
-        switch (GetSystemMetrics(SM_CLEANBOOT))
+        switch (SafeBootEnabled)
         {
             /* NOTE: Assumes MINIMAL (1) and DSREPAIR (3) load same items */
             case 1:
             case 3:
-                wcscat(szSafeBootServicePath, L"\\Minimal\\");
+                StringCchCatW(szSafeBootServicePath, ARRAYSIZE(szSafeBootServicePath),
+                              L"\\Minimal\\");
                 break;
 
             case 2:
-                wcscat(szSafeBootServicePath, L"\\Network\\");
+                StringCchCatW(szSafeBootServicePath, ARRAYSIZE(szSafeBootServicePath),
+                              L"\\Network\\");
                 break;
         }
 
-        if (GetSystemMetrics(SM_CLEANBOOT))
+        if (SafeBootEnabled != 0)
         {
             /* If key does not exist then do not assume safe mode */
             dwError = RegOpenKeyExW(HKEY_LOCAL_MACHINE,
@@ -1770,9 +2136,8 @@ ScmAutoStartServices(VOID)
                 RegCloseKey(hKey);
 
                 /* Finish Safe Boot path off */
-                wcsncat(szSafeBootServicePath,
-                        CurrentService->lpServiceName,
-                        MAX_PATH - wcslen(szSafeBootServicePath));
+                StringCchCatW(szSafeBootServicePath, ARRAYSIZE(szSafeBootServicePath),
+                              CurrentService->lpServiceName);
 
                 /* Check that the key is in the Safe Boot path */
                 dwError = RegOpenKeyExW(HKEY_LOCAL_MACHINE,
@@ -1915,12 +2280,16 @@ ScmAutoShutdownServices(VOID)
     {
         CurrentService = CONTAINING_RECORD(ServiceEntry, SERVICE, ServiceListEntry);
 
-        if (CurrentService->Status.dwCurrentState == SERVICE_RUNNING ||
-            CurrentService->Status.dwCurrentState == SERVICE_START_PENDING)
+        if ((CurrentService->Status.dwControlsAccepted & SERVICE_ACCEPT_SHUTDOWN) &&
+            (CurrentService->Status.dwCurrentState == SERVICE_RUNNING ||
+             CurrentService->Status.dwCurrentState == SERVICE_START_PENDING))
         {
-            /* shutdown service */
-            DPRINT("Shutdown service: %S\n", CurrentService->szServiceName);
-            ScmControlService(CurrentService, SERVICE_CONTROL_SHUTDOWN);
+            /* Send the shutdown notification */
+            DPRINT("Shutdown service: %S\n", CurrentService->lpServiceName);
+            ScmControlService(CurrentService->lpImage->hControlPipe,
+                              CurrentService->lpServiceName,
+                              (SERVICE_STATUS_HANDLE)CurrentService,
+                              SERVICE_CONTROL_SHUTDOWN);
         }
 
         ServiceEntry = ServiceEntry->Flink;
@@ -1970,14 +2339,13 @@ ScmInitNamedPipeCriticalSection(VOID)
                             &hKey);
    if (dwError == ERROR_SUCCESS)
    {
-        dwKeySize = sizeof(DWORD);
+        dwKeySize = sizeof(PipeTimeout);
         RegQueryValueExW(hKey,
                          L"ServicesPipeTimeout",
                          0,
                          NULL,
                          (LPBYTE)&PipeTimeout,
                          &dwKeySize);
-
        RegCloseKey(hKey);
    }
 }