/*++ Copyright (c) 1992 Microsoft Corporation Module Name: Shutdown.c Abstract: This module contains the client side wrappers for the Win32 remote shutdown APIs, that is: - InitiateSystemShutdownA - InitiateSystemShutdownW - AbortSystemShutdownA - AbortSystemShutdownW Author: Dave Chalmers (davidc) 29-Apr-1992 Notes: Revision History: Dragos C. Sambotin (dragoss) 21-May-1999 Added support for the new winlogon's Shutdown interface --*/ #define UNICODE #include #include "regrpc.h" #include "client.h" #include "shutinit.h" #include "..\regconn\regconn.h" LONG BaseBindToMachine( IN LPCWSTR lpMachineName, IN PBIND_CALLBACK BindCallback, IN PVOID Context1, IN PVOID Context2 ); LONG ShutdownCallback( IN RPC_BINDING_HANDLE *pbinding, IN PUNICODE_STRING Message, IN PVOID Context2 ); LONG ShutdownCallbackEx( IN RPC_BINDING_HANDLE *pbinding, IN PUNICODE_STRING Message, IN PVOID Context2 ); LONG AbortShutdownCallback( IN RPC_BINDING_HANDLE *pbinding, IN PVOID Context1, IN PVOID Context2 ); BOOL APIENTRY InitiateSystemShutdownW( IN LPWSTR lpMachineName OPTIONAL, IN LPWSTR lpMessage OPTIONAL, IN DWORD dwTimeout, IN BOOL bForceAppsClosed, IN BOOL bRebootAfterShutdown ) /*++ Routine Description: Win32 Unicode API for initiating the shutdown of a (possibly remote) machine. Arguments: lpMachineName - Name of machine to shutdown. lpMessage - Message to display during shutdown timeout period. if Message(comment) is longer than MAX_REASON_COMMENT_LEN return FALSE.(ERROR_INVALID_PARAMETER) dwTimeout - Number of seconds to delay before shutting down if dwTimeout is larger than MAX_SHUTDOWN_TIMEOUT, return FALSE. (ERROR_INVALID_PARAMETER) bForceAppsClosed - Normally applications may prevent system shutdown. If this flag is set, all applications are terminated unconditionally. bRebootAfterShutdown - TRUE if the system should reboot. FALSE if it should be left in a shutdown state. Return Value: Returns TRUE on success, FALSE on failure (GetLastError() returns error code) Possible errors : ERROR_SHUTDOWN_IN_PROGRESS - a shutdown has already been started on the specified machine. --*/ { DWORD Result; UNICODE_STRING Message; SHUTDOWN_CONTEXT ShutdownContext; BOOL TryOld = TRUE; // // Explicitly bind to the given server. // if (!ARGUMENT_PRESENT(lpMachineName)) { lpMachineName = L""; TryOld = FALSE; } ShutdownContext.dwTimeout = dwTimeout; ShutdownContext.bForceAppsClosed = (bForceAppsClosed != 0); ShutdownContext.bRebootAfterShutdown = (bRebootAfterShutdown != 0); RtlInitUnicodeString(&Message, lpMessage); if ( ( Message.Length / sizeof(WCHAR) < MAX_REASON_COMMENT_LEN ) && ( dwTimeout < MAX_SHUTDOWN_TIMEOUT ) ){ // // Call the server // // // First try to connect to the new InitShutdown interface // Result = BaseBindToMachineShutdownInterface(lpMachineName, NewShutdownCallback, &Message, &ShutdownContext); if( (Result != ERROR_SUCCESS) && (TryOld == TRUE) ) { // // try the old one, maybe we are calling into a NT4 machine // which doesn't know about the new interface // Result = BaseBindToMachine(lpMachineName, ShutdownCallback, &Message, &ShutdownContext); } } else{ Result = ERROR_INVALID_PARAMETER; } if (Result != ERROR_SUCCESS) { SetLastError(Result); } return(Result == ERROR_SUCCESS); } BOOL APIENTRY InitiateSystemShutdownExW( IN LPWSTR lpMachineName OPTIONAL, IN LPWSTR lpMessage OPTIONAL, IN DWORD dwTimeout, IN BOOL bForceAppsClosed, IN BOOL bRebootAfterShutdown, IN DWORD dwReason ) /*++ Routine Description: Win32 Unicode API for initiating the shutdown of a (possibly remote) machine. Arguments: lpMachineName - Name of machine to shutdown. lpMessage - Message to display during shutdown timeout period. if Message(comment) is longer than MAX_REASON_COMMENT_LEN return FALSE.(ERROR_INVALID_PARAMETER) dwTimeout - Number of seconds to delay before shutting down if dwTimeout is larger than MAX_SHUTDOWN_TIMEOUT, return FALSE. (ERROR_INVALID_PARAMETER) bForceAppsClosed - Normally applications may prevent system shutdown. If this flag is set, all applications are terminated unconditionally. bRebootAfterShutdown - TRUE if the system should reboot. FALSE if it should be left in a shutdown state. dwReason - Reason for initiating the shutdown. This reason is logged in the eventlog #6006 event. Return Value: Returns TRUE on success, FALSE on failure (GetLastError() returns error code) Possible errors : ERROR_SHUTDOWN_IN_PROGRESS - a shutdown has already been started on the specified machine. --*/ { DWORD Result; UNICODE_STRING Message; SHUTDOWN_CONTEXTEX ShutdownContext; BOOL TryOld = TRUE; // // Explicitly bind to the given server. // if (!ARGUMENT_PRESENT(lpMachineName)) { lpMachineName = L""; TryOld = FALSE; } ShutdownContext.dwTimeout = dwTimeout; ShutdownContext.bForceAppsClosed = (bForceAppsClosed != 0); ShutdownContext.bRebootAfterShutdown = (bRebootAfterShutdown != 0); ShutdownContext.dwReason = dwReason; RtlInitUnicodeString(&Message, lpMessage); if ( ( Message.Length / sizeof(WCHAR) < MAX_REASON_COMMENT_LEN ) && ( dwTimeout < MAX_SHUTDOWN_TIMEOUT ) ) { // // Call the server // // // First try to connect to the new InitShutdown interface // Result = BaseBindToMachineShutdownInterface(lpMachineName, NewShutdownCallbackEx, &Message, &ShutdownContext); if( (Result != ERROR_SUCCESS) && (TryOld == TRUE) ) { // // try the old one, maybe we are calling into a NT4 machine // which doesn't know about the new interface // Result = BaseBindToMachine(lpMachineName, ShutdownCallbackEx, &Message, &ShutdownContext); } } else{ Result = ERROR_INVALID_PARAMETER; } if (Result != ERROR_SUCCESS) { SetLastError(Result); } return(Result == ERROR_SUCCESS); } BOOL APIENTRY InitiateSystemShutdownA( IN LPSTR lpMachineName OPTIONAL, IN LPSTR lpMessage OPTIONAL, IN DWORD dwTimeout, IN BOOL bForceAppsClosed, IN BOOL bRebootAfterShutdown ) /*++ Routine Description: See InitiateSystemShutdownW --*/ { UNICODE_STRING MachineName; UNICODE_STRING Message; ANSI_STRING AnsiString; NTSTATUS Status; BOOL Result; // // Convert the ansi machinename to wide-character // RtlInitAnsiString( &AnsiString, lpMachineName ); Status = RtlAnsiStringToUnicodeString( &MachineName, &AnsiString, TRUE ); if( NT_SUCCESS( Status )) { // // Convert the ansi message to wide-character // RtlInitAnsiString( &AnsiString, lpMessage ); Status = RtlAnsiStringToUnicodeString( &Message, &AnsiString, TRUE ); if (NT_SUCCESS(Status)) { // // Call the wide-character api // Result = InitiateSystemShutdownW( MachineName.Buffer, Message.Buffer, dwTimeout, bForceAppsClosed, bRebootAfterShutdown ); RtlFreeUnicodeString(&Message); } RtlFreeUnicodeString(&MachineName); } if (!NT_SUCCESS(Status)) { SetLastError(RtlNtStatusToDosError(Status)); Result = FALSE; } return(Result); } BOOL APIENTRY InitiateSystemShutdownExA( IN LPSTR lpMachineName OPTIONAL, IN LPSTR lpMessage OPTIONAL, IN DWORD dwTimeout, IN BOOL bForceAppsClosed, IN BOOL bRebootAfterShutdown, IN DWORD dwReason ) /*++ Routine Description: See InitiateSystemShutdownW --*/ { UNICODE_STRING MachineName; UNICODE_STRING Message; ANSI_STRING AnsiString; NTSTATUS Status; BOOL Result; // // Convert the ansi machinename to wide-character // RtlInitAnsiString( &AnsiString, lpMachineName ); Status = RtlAnsiStringToUnicodeString( &MachineName, &AnsiString, TRUE ); if( NT_SUCCESS( Status )) { // // Convert the ansi message to wide-character // RtlInitAnsiString( &AnsiString, lpMessage ); Status = RtlAnsiStringToUnicodeString( &Message, &AnsiString, TRUE ); if (NT_SUCCESS(Status)) { // // Call the wide-character api // Result = InitiateSystemShutdownExW( MachineName.Buffer, Message.Buffer, dwTimeout, bForceAppsClosed, bRebootAfterShutdown, dwReason ); RtlFreeUnicodeString(&Message); } RtlFreeUnicodeString(&MachineName); } if (!NT_SUCCESS(Status)) { SetLastError(RtlNtStatusToDosError(Status)); Result = FALSE; } return(Result); } BOOL APIENTRY AbortSystemShutdownW( IN LPWSTR lpMachineName OPTIONAL ) /*++ Routine Description: Win32 Unicode API for aborting the shutdown of a (possibly remote) machine. Arguments: lpMachineName - Name of target machine. Return Value: Returns TRUE on success, FALSE on failure (GetLastError() returns error code) --*/ { DWORD Result; RPC_BINDING_HANDLE binding; BOOL TryOld = TRUE; // // Explicitly bind to the given server. // if (!ARGUMENT_PRESENT(lpMachineName)) { lpMachineName = L""; TryOld = FALSE; } // // Call the server // // // First try to connect to the new InitShutdown interface // Result = BaseBindToMachineShutdownInterface(lpMachineName, NewAbortShutdownCallback, NULL, NULL); if( (Result != ERROR_SUCCESS) && (TryOld == TRUE) ) { // // try the old one, maybe we are calling into a NT4 machine // which doesn't know about the new interface Result = BaseBindToMachine(lpMachineName, AbortShutdownCallback, NULL, NULL); } if (Result != ERROR_SUCCESS) { SetLastError(Result); } return(Result == ERROR_SUCCESS); } BOOL APIENTRY AbortSystemShutdownA( IN LPSTR lpMachineName OPTIONAL ) /*++ Routine Description: See AbortSystemShutdownW --*/ { UNICODE_STRING MachineName; ANSI_STRING AnsiString; NTSTATUS Status; BOOL Result; // // Convert the ansi machinename to wide-character // RtlInitAnsiString( &AnsiString, lpMachineName ); Status = RtlAnsiStringToUnicodeString( &MachineName, &AnsiString, TRUE ); if( NT_SUCCESS( Status )) { // // Call the wide-character api // Result = AbortSystemShutdownW( MachineName.Buffer ); RtlFreeUnicodeString(&MachineName); } if (!NT_SUCCESS(Status)) { SetLastError(RtlNtStatusToDosError(Status)); Result = FALSE; } return(Result); } LONG ShutdownCallback( IN RPC_BINDING_HANDLE *pbinding, IN PUNICODE_STRING Message, IN PSHUTDOWN_CONTEXT ShutdownContext ) /*++ Routine Description: Callback for binding to a machine to initiate a shutdown. Arguments: pbinding - Supplies a pointer to the RPC binding context Message - Supplies message to display during shutdown timeout period. ShutdownContext - Supplies remaining parameters for BaseInitiateSystemShutdown Return Value: ERROR_SUCCESS if no error. --*/ { DWORD Result; RpcTryExcept { Result = BaseInitiateSystemShutdown((PREGISTRY_SERVER_NAME)pbinding, Message, ShutdownContext->dwTimeout, ShutdownContext->bForceAppsClosed, ShutdownContext->bRebootAfterShutdown); } RpcExcept(EXCEPTION_EXECUTE_HANDLER) { Result = RpcExceptionCode(); } RpcEndExcept; if (Result != ERROR_SUCCESS) { RpcBindingFree(pbinding); } return(Result); } LONG ShutdownCallbackEx( IN RPC_BINDING_HANDLE *pbinding, IN PUNICODE_STRING Message, IN PSHUTDOWN_CONTEXTEX ShutdownContext ) /*++ Routine Description: Callback for binding to a machine to initiate a shutdown. Arguments: pbinding - Supplies a pointer to the RPC binding context Message - Supplies message to display during shutdown timeout period. ShutdownContext - Supplies remaining parameters for BaseInitiateSystemShutdown Return Value: ERROR_SUCCESS if no error. --*/ { DWORD Result; RpcTryExcept { Result = BaseInitiateSystemShutdownEx((PREGISTRY_SERVER_NAME)pbinding, Message, ShutdownContext->dwTimeout, ShutdownContext->bForceAppsClosed, ShutdownContext->bRebootAfterShutdown, ShutdownContext->dwReason); } RpcExcept(EXCEPTION_EXECUTE_HANDLER) { Result = RpcExceptionCode(); } RpcEndExcept; if (Result != ERROR_SUCCESS) { RpcBindingFree(pbinding); } return(Result); } LONG AbortShutdownCallback( IN RPC_BINDING_HANDLE *pbinding, IN PVOID Unused1, IN PVOID Unused2 ) /*++ Routine Description: Callback for binding to a machine to abort a shutdown. Arguments: pbinding - Supplies a pointer to the RPC binding context Return Value: ERROR_SUCCESS if no error. --*/ { DWORD Result; RpcTryExcept { Result = BaseAbortSystemShutdown((PREGISTRY_SERVER_NAME)pbinding); } RpcExcept(EXCEPTION_EXECUTE_HANDLER) { Result = RpcExceptionCode(); } RpcEndExcept; if (Result != ERROR_SUCCESS) { RpcBindingFree(pbinding); } return(Result); }