Index: base/system/services/rpcserver.c =================================================================== --- base/system/services/rpcserver.c (révision 56737) +++ base/system/services/rpcserver.c (copie de travail) @@ -133,6 +133,35 @@ } +LPWSTR ScmFindSubStrI(LPCWSTR str, LPCWSTR strSearch) +{ + LPWSTR cp = (LPWSTR)str; + LPWSTR s1, s2; + + if (!str) + return NULL; + + if (!strSearch || !*strSearch) + return (LPWSTR)str; + + while (*cp) + { + s1 = cp; + s2 = (LPWSTR)strSearch; + + while (*s1 && *s2 && (towupper(*s1) == towupper(*s2))) + ++s1, ++s2; + + if (!*s2) + return cp; + + ++cp; + } + + return NULL; +} + + static DWORD ScmCreateManagerHandle(LPWSTR lpDatabaseName, SC_HANDLE *Handle) @@ -4815,7 +4844,7 @@ /* Open the service key */ dwError = ScmOpenServiceKey(lpService->szServiceName, - KEY_SET_VALUE, + KEY_READ | KEY_SET_VALUE, &hServiceKey); if (dwError != ERROR_SUCCESS) goto done; @@ -4842,9 +4871,291 @@ } else if (Info.dwInfoLevel == SERVICE_CONFIG_FAILURE_ACTIONS) { - UNIMPLEMENTED; - dwError = ERROR_CALL_NOT_IMPLEMENTED; - goto done; + LPSERVICE_FAILURE_ACTIONSW lpFailureActions = (LPSERVICE_FAILURE_ACTIONSW)Info.psfa; + + if (lpFailureActions != NULL) + { + DWORD i = 0; + BOOL bIsActionRebootSet = FALSE; + DWORD dwDesiredAccess = SERVICE_CHANGE_CONFIG; + + LPSERVICE_FAILURE_ACTIONSW lpReadBuffer = NULL; + LPSERVICE_FAILURE_ACTIONSW lpWriteBuffer = NULL; + DWORD dwRequiredSize = 0; + DWORD dwType = 0; + + + /* + * 1- Check whether or not we can set + * failure actions for this service. + */ + + /* ERROR: Failure actions can only be set for Win32 services, not for drivers */ + if (lpService->Status.dwServiceType & SERVICE_DRIVER) + { + dwError = ERROR_CANNOT_DETECT_DRIVER_FAILURE; + goto done; + } + + /* + * We cannot set the SERVICE_CONFIG_FAILURE_ACTIONS value for a service + * that shares the service control manager's process. This includes all + * services whose executable image is "Services.exe". + * + * ERROR: This service runs in the same process as the service control manager. + * Therefore, the service control manager cannot take action if this service's + * process terminates unexpectedly. + */ + if (lpService->lpImage && ScmFindSubStrI(lpService->lpImage->szImagePath, L"\\services.exe")) + { + dwError = ERROR_CANNOT_DETECT_PROCESS_ABORT; + goto done; + } + + /* + * If the service controller handles the SC_ACTION_RESTART action, + * hService must have the SERVICE_START access right. + * + * If you specify SC_ACTION_REBOOT, the caller must have the + * SE_SHUTDOWN_NAME privilege. + */ + if ( lpFailureActions->cActions > 0 && + lpFailureActions->lpsaActions != NULL ) + { + for (i = 0 ; i < lpFailureActions->cActions ; ++i) + { + if (lpFailureActions->lpsaActions[i].Type == SC_ACTION_RESTART) + dwDesiredAccess |= SERVICE_START; + else if (lpFailureActions->lpsaActions[i].Type == SC_ACTION_REBOOT) + bIsActionRebootSet = TRUE; + } + } + + /* Re-check the access rights */ + if (!RtlAreAllAccessesGranted(hSvc->Handle.DesiredAccess, + dwDesiredAccess)) + { + DPRINT("Insufficient access rights! 0x%lx\n", hSvc->Handle.DesiredAccess); + dwError = ERROR_ACCESS_DENIED; + goto done; + } + + /* TODO: Check if the caller has the SE_SHUTDOWN_NAME privilege */ + /* + if (bIsActionRebootSet) + { + } + */ + + + /* + * 2- Retrieve the original value of FailureActions. + */ + + /* Query value length */ + dwError = RegQueryValueExW(hServiceKey, + L"FailureActions", + NULL, + &dwType, + NULL, + &dwRequiredSize); + if (dwError != ERROR_SUCCESS && + dwError != ERROR_MORE_DATA && + dwError != ERROR_FILE_NOT_FOUND) + goto done; + + dwRequiredSize = (dwType == REG_BINARY) ? max(sizeof(SERVICE_FAILURE_ACTIONSW), dwRequiredSize) + : sizeof(SERVICE_FAILURE_ACTIONSW); + + /* Initialize the read buffer */ + lpReadBuffer = HeapAlloc(GetProcessHeap(), + HEAP_ZERO_MEMORY, + dwRequiredSize); + if (lpReadBuffer == NULL) + { + dwError = ERROR_NOT_ENOUGH_MEMORY; + goto done; + } + + /* Now we can fill the read buffer */ + if (dwError != ERROR_FILE_NOT_FOUND && dwType == REG_BINARY) + { + dwError = RegQueryValueExW(hServiceKey, + L"FailureActions", + NULL, + NULL, + (LPBYTE)lpReadBuffer, + &dwRequiredSize); + if (dwError != ERROR_SUCCESS && dwError != ERROR_FILE_NOT_FOUND) + { + HeapFree(GetProcessHeap(), 0, lpReadBuffer); + goto done; + } + + if (dwRequiredSize < sizeof(SERVICE_FAILURE_ACTIONSW)) + dwRequiredSize = sizeof(SERVICE_FAILURE_ACTIONSW); + } + else + { + /* + * The value of the error doesn't really matter, the only + * important thing is that it must be != ERROR_SUCCESS . + */ + dwError = ERROR_INVALID_DATA; + } + + if (dwError == ERROR_SUCCESS) + { + lpReadBuffer->cActions = min(lpReadBuffer->cActions, (dwRequiredSize - sizeof(SERVICE_FAILURE_ACTIONSW)) / sizeof(SC_ACTION)); + lpReadBuffer->lpsaActions = (lpReadBuffer->cActions > 0 ? (LPSC_ACTION)(lpReadBuffer + 1) : NULL); + } + else + { + lpReadBuffer->dwResetPeriod = 0; + lpReadBuffer->cActions = 0; + lpReadBuffer->lpsaActions = NULL; + } + + lpReadBuffer->lpRebootMsg = NULL; + lpReadBuffer->lpCommand = NULL; + + + /* + * 3- Initialize the new value to set. + */ + + dwRequiredSize = sizeof(SERVICE_FAILURE_ACTIONSW); + + if (lpFailureActions->lpsaActions == NULL) + { + /* + * lpFailureActions->cActions is ignored. + * Therefore we use the original values + * of cActions and lpsaActions. + */ + dwRequiredSize += lpReadBuffer->cActions * sizeof(SC_ACTION); + } + else + { + /* + * The reset period and array of failure actions + * are deleted if lpFailureActions->cActions == 0 . + */ + dwRequiredSize += lpFailureActions->cActions * sizeof(SC_ACTION); + } + + lpWriteBuffer = HeapAlloc(GetProcessHeap(), + HEAP_ZERO_MEMORY, + dwRequiredSize); + if (lpWriteBuffer == NULL) + { + HeapFree(GetProcessHeap(), 0, lpReadBuffer); + dwError = ERROR_NOT_ENOUGH_MEMORY; + goto done; + } + + /* Clean the pointers as they have no meaning when the structure is stored in the registry */ + lpWriteBuffer->lpRebootMsg = NULL; + lpWriteBuffer->lpCommand = NULL; + lpWriteBuffer->lpsaActions = NULL; + + /* Set the members */ + if (lpFailureActions->lpsaActions == NULL) + { + /* + * lpFailureActions->dwResetPeriod and lpFailureActions->cActions are ignored. + * Therefore we use the original values of dwResetPeriod, cActions and lpsaActions. + */ + lpWriteBuffer->dwResetPeriod = lpReadBuffer->dwResetPeriod; + lpWriteBuffer->cActions = lpReadBuffer->cActions; + + if (lpReadBuffer->lpsaActions != NULL) + { + memmove(lpWriteBuffer + 1, + lpReadBuffer->lpsaActions, + lpReadBuffer->cActions * sizeof(SC_ACTION)); + } + } + else + { + if (lpFailureActions->cActions > 0) + { + lpWriteBuffer->dwResetPeriod = lpFailureActions->dwResetPeriod; + lpWriteBuffer->cActions = lpFailureActions->cActions; + + memmove(lpWriteBuffer + 1, + lpFailureActions->lpsaActions, + lpFailureActions->cActions * sizeof(SC_ACTION)); + } + else + { + /* The reset period and array of failure actions are deleted */ + lpWriteBuffer->dwResetPeriod = 0; + lpWriteBuffer->cActions = 0; + } + } + + /* Save the new failure actions into the registry */ + dwError = RegSetValueExW(hServiceKey, + L"FailureActions", + 0, + REG_BINARY, + (LPBYTE)lpWriteBuffer, + dwRequiredSize); + + /* We modify the strings only in case of success.*/ + if (dwError == ERROR_SUCCESS) + { + /* Modify the Reboot Message value, if specified */ + if (lpFailureActions->lpRebootMsg != NULL) + { + /* If the Reboot Message is "" then we delete it */ + if (*lpFailureActions->lpRebootMsg == 0) + { + DPRINT("Delete Reboot Message value\n"); + RegDeleteValueW(hServiceKey, L"RebootMessage"); + } + else + { + DPRINT("Setting Reboot Message value %S\n", lpFailureActions->lpRebootMsg); + RegSetValueExW(hServiceKey, + L"RebootMessage", + 0, + REG_SZ, + (LPBYTE)lpFailureActions->lpRebootMsg, + (wcslen(lpFailureActions->lpRebootMsg) + 1) * sizeof(WCHAR)); + } + } + + /* Modify the Failure Command value, if specified */ + if (lpFailureActions->lpCommand != NULL) + { + /* If the Failure Command is "" then we delete it */ + if (*lpFailureActions->lpCommand == 0) + { + DPRINT("Delete Failure Command value\n"); + RegDeleteValueW(hServiceKey, L"FailureCommand"); + } + else + { + DPRINT("Setting Failure Command value %S\n", lpFailureActions->lpCommand); + RegSetValueExW(hServiceKey, + L"FailureCommand", + 0, + REG_SZ, + (LPBYTE)lpFailureActions->lpCommand, + (wcslen(lpFailureActions->lpCommand) + 1) * sizeof(WCHAR)); + } + } + } + + HeapFree(GetProcessHeap(), 0, lpWriteBuffer); + HeapFree(GetProcessHeap(), 0, lpReadBuffer ); + } + else + { + dwError = ERROR_SUCCESS; + } } done: Index: dll/win32/advapi32/service/scm.c =================================================================== --- dll/win32/advapi32/service/scm.c (révision 56737) +++ dll/win32/advapi32/service/scm.c (copie de travail) @@ -233,7 +233,7 @@ break; case SERVICE_CONFIG_FAILURE_ACTIONS: - Info.psfa = (LPSERVICE_FAILURE_ACTIONSW)&lpInfo; + Info.psfa = (LPSERVICE_FAILURE_ACTIONSW)lpInfo; break; default: