WindowsXP-SP1/termsrv/admtools/shutdown/tsshutdn.c
2020-09-30 16:53:49 +02:00

487 lines
12 KiB
C

//Copyright (c) 1998 - 1999 Microsoft Corporation
/*************************************************************************
*
* TSSHUTDN.C
* This module is the TSSHUTDN utility code.
*
*
*************************************************************************/
#include <nt.h>
#include <ntrtl.h>
#include <nturtl.h>
#include <ntlsa.h>
#include <stdio.h>
#include <wchar.h>
#include <windows.h>
#include <ntddkbd.h>
#include <ntddmou.h>
#include <winstaw.h>
#include <stdlib.h>
#include <utilsub.h>
#include <string.h>
#include <malloc.h>
#include <locale.h>
#include "tsshutdn.h"
#include "printfoa.h"
#define DEFAULT_WAIT_TIME 60
#define DEFAULT_LOGOFF_DELAY 30
#define MAX_MESSAGE_LENGTH 256
WCHAR WSTime[MAX_IDS_LEN+2];
WCHAR WDTime[MAX_IDS_LEN+2];
USHORT help_flag = FALSE;
USHORT v_flag = FALSE;
USHORT RebootFlag = FALSE;
USHORT PowerDownFlag = FALSE;
USHORT FastFlag = FALSE;
#if 0
USHORT DumpFlag = FALSE;
#endif
HANDLE hServerName = SERVERNAME_CURRENT;
WCHAR ServerName[MAX_IDS_LEN+1];
TOKMAP ptm[] =
{
{TOKEN_TIME, TMFLAG_OPTIONAL, TMFORM_S_STRING, MAX_IDS_LEN, WSTime},
{TOKEN_SERVER, TMFLAG_OPTIONAL, TMFORM_STRING, MAX_IDS_LEN, ServerName},
{TOKEN_DELAY, TMFLAG_OPTIONAL, TMFORM_STRING, MAX_IDS_LEN, WDTime},
{TOKEN_HELP, TMFLAG_OPTIONAL, TMFORM_BOOLEAN, sizeof(USHORT), &help_flag},
{TOKEN_VERBOSE, TMFLAG_OPTIONAL, TMFORM_BOOLEAN, sizeof(USHORT), &v_flag},
{TOKEN_REBOOT, TMFLAG_OPTIONAL, TMFORM_BOOLEAN, sizeof(USHORT), &RebootFlag},
{TOKEN_POWERDOWN,TMFLAG_OPTIONAL, TMFORM_BOOLEAN, sizeof(USHORT), &PowerDownFlag},
{TOKEN_FAST, TMFLAG_OPTIONAL, TMFORM_BOOLEAN, sizeof(USHORT), &FastFlag},
#if 0
{TOKEN_DUMP, TMFLAG_OPTIONAL, TMFORM_BOOLEAN, sizeof(USHORT), &DumpFlag},
#endif
{0, 0, 0, 0, 0}
};
/*
* Local function prototypes.
*/
void Usage( BOOLEAN bError );
void NotifyUsers( ULONG WaitTime );
void NotifyWinStations( PLOGONIDW, ULONG, ULONG );
BOOLEAN CheckShutdownPrivilege();
/*************************************************************************
*
* main
* Main function and entry point of the TSSHUTDN utility.
*
* ENTRY:
* argc - count of the command line arguments.
* argv - vector of strings containing the command line arguments.
*
* EXIT
* Nothing.
*
*************************************************************************/
int __cdecl
main(INT argc, CHAR **argv)
{
int rc, i;
ULONG Error;
WCHAR *CmdLine;
WCHAR **argvW, *endptr;
ULONG ShutdownFlags = WSD_SHUTDOWN | WSD_LOGOFF;
ULONG WaitTime = DEFAULT_WAIT_TIME;
ULONG LogoffDelay = DEFAULT_LOGOFF_DELAY;
setlocale(LC_ALL, ".OCP");
/*
* Massage the command line.
*/
argvW = MassageCommandLine((DWORD)argc);
if (argvW == NULL) {
ErrorPrintf(IDS_ERROR_MALLOC);
return(FAILURE);
}
/*
* parse the cmd line without parsing the program name (argc-1, argv+1)
*/
WSTime[0] = L'\0';
rc = ParseCommandLine(argc-1, argvW+1, ptm, 0);
/*
* Check for error from ParseCommandLine
*/
if ( help_flag || (rc && !(rc & PARSE_FLAG_NO_PARMS)) ) {
if ( !help_flag ) {
Usage(TRUE);
return(FAILURE);
} else {
Usage(FALSE);
return(SUCCESS);
}
}
// If no remote server was specified, then check if we are running under Terminal Server
if ((!IsTokenPresent(ptm, TOKEN_SERVER) ) && (!AreWeRunningTerminalServices()))
{
ErrorPrintf(IDS_ERROR_NOT_TS);
return(FAILURE);
}
/*
* Open the specified server
*/
if( ServerName[0] ) {
hServerName = WinStationOpenServer( ServerName );
if( hServerName == NULL ) {
StringErrorPrintf(IDS_ERROR_SERVER,ServerName);
PutStdErr( GetLastError(), 0 );
return(FAILURE);
}
}
// Make sure the user has the proper privilege
// SM should really do the check
/*
if( !CheckShutdownPrivilege() ) {
ErrorPrintf(IDS_ERROR_NO_RIGHTS);
return(FAILURE);
}
*/
// Make sure its a number
if ( WSTime[0] ) {
if( !iswdigit(WSTime[0]) ) {
StringErrorPrintf(IDS_ERROR_INVALID_TIME, WSTime);
return(FAILURE);
}
WaitTime = wcstoul(WSTime, &endptr, 10);
if ( *endptr ) {
StringErrorPrintf(IDS_ERROR_INVALID_TIME, WSTime);
return(FAILURE);
}
}
// Make sure its a number
if ( WDTime[0] ) {
if( !iswdigit(WDTime[0]) ) {
StringErrorPrintf(IDS_ERROR_INVALID_DELAY, WDTime);
return(FAILURE);
}
LogoffDelay = wcstoul(WDTime, &endptr, 10);
if ( *endptr ) {
StringErrorPrintf(IDS_ERROR_INVALID_DELAY, WDTime);
return(FAILURE);
}
}
#if 0
/*
* If /dump option was specified, call NT function directly
*/
if ( DumpFlag ) {
NtShutdownSystem( ShutdownDump ); // will not return
}
#endif
if( RebootFlag ) {
ShutdownFlags |= WSD_REBOOT;
}
if( PowerDownFlag )
ShutdownFlags |= WSD_POWEROFF;
if( FastFlag ) {
ShutdownFlags |= WSD_FASTREBOOT;
ShutdownFlags &= ~WSD_LOGOFF;
WaitTime = 0;
}
if( WaitTime ) {
NotifyUsers( WaitTime );
}
/*
* If necessary, force all WinStations to logoff
*/
if ( ShutdownFlags & WSD_LOGOFF ) {
Message( IDS_SHUTTING_DOWN, 0 );
if ( !WinStationShutdownSystem( hServerName, WSD_LOGOFF ) ) {
Error = GetLastError();
ErrorPrintf( IDS_ERROR_SHUTDOWN_FAILED, Error );
PutStdErr( Error, 0 );
return( FAILURE );
}
Message( IDS_LOGOFF_USERS, 0);
if (LogoffDelay) {
NotifyUsers( LogoffDelay );
}
Message( IDS_SHUTDOWN_DONE, 0 );
}
/*
* Inform user of impending reboot/poweroff
*/
if ( ShutdownFlags & WSD_REBOOT ) {
Message( IDS_SHUTDOWN_REBOOT, 0 );
Sleep( 4000 );
} else if ( ShutdownFlags & WSD_POWEROFF ) {
Message( IDS_SHUTDOWN_POWERDOWN, 0 );
Sleep( 4000 );
}
/*
* Perform system shutdown, reboot, or poweroff, depending on flags
*/
if( WinStationShutdownSystem( hServerName, ShutdownFlags & ~WSD_LOGOFF ) != ERROR_SUCCESS )
{
PutStdErr( GetLastError(), 0 );
}
// WinStationShutdownSystem is done asynchronously.
// No way to know when the shudown is completed.
//if ( !(ShutdownFlags & WSD_REBOOT) && !( ShutdownFlags & WSD_POWEROFF ) ) {
// /*
// * If we get here, shutdown is complete, all disks are write protected.
// */
// Message(IDS_SHUTDOWN_WRITEPROT, 0);
//}
return(SUCCESS);
} /* main() */
/*******************************************************************************
*
* Usage
*
* Output the usage message for this utility.
*
* ENTRY:
* bError (input)
* TRUE if the 'invalid parameter(s)' message should preceed the usage
* message and the output go to stderr; FALSE for no such error
* string and output goes to stdout.
*
* EXIT:
*
*
******************************************************************************/
void
Usage( BOOLEAN bError )
{
WCHAR sz1[1024];
LoadString( NULL, IDS_USAGE, sz1, 1024 );
if ( bError ) {
ErrorPrintf(IDS_ERROR_INVALID_PARAMETERS);
fwprintf(stderr, sz1);
} else {
fwprintf(stdout,sz1);
}
} /* Usage() */
/*****************************************************************************
*
* NotifyUsers
*
* Notify Users that the system is being shutdown
*
* ENTRY:
* WaitTime (input)
* Amount of time to give them to log off.
*
* EXIT:
*
****************************************************************************/
void
NotifyUsers( ULONG WaitTime )
{
BOOLEAN Result;
ULONG Entries;
ULONG Error;
PLOGONIDW ptr;
//
// Get all of the WinStations call the function to notify them.
//
if ( WinStationEnumerateW( hServerName, &ptr, &Entries ) ) {
NotifyWinStations( ptr, Entries, WaitTime );
WinStationFreeMemory(ptr);
} else {
Error = GetLastError();
#if DBG
printf("TSSHUTDN: Error emumerating Sessions %d\n",Error);
#endif
return;
}
Message(IDS_NOTIFYING_USERS);
// Now wait the wait time
SleepEx( WaitTime*1000, FALSE );
return;
}
/*****************************************************************************
*
* NotifyWinStations
*
* Notify the group of WinStations about the impending system shutdown
*
* ENTRY:
* pId (input)
* Array of LOGONIDW's
*
* Entries (input)
* Number of entries in array
*
* WaitTime (input)
* Amount of time to wait in seconds
*
* EXIT:
* STATUS_SUCCESS - no error
*
****************************************************************************/
void
NotifyWinStations(
PLOGONIDW pId,
ULONG Entries,
ULONG WaitTime
)
{
ULONG Index;
PLOGONIDW p;
ULONG Response;
BOOLEAN Result;
WCHAR mBuf[MAX_MESSAGE_LENGTH+2];
// PWCHAR pTitle = L"SYSTEM SHUTDOWN";
PWCHAR pTitle;
WCHAR sz1[256], sz2[512];
LoadString( NULL, IDS_SHUTDOWN_TITLE, sz1, 256 );
pTitle = &(sz1[0]);
// Create the message
LoadString( NULL, IDS_SHUTDOWN_MESSAGE, sz2, 512 );
_snwprintf( mBuf, MAX_MESSAGE_LENGTH, sz2, WaitTime);
for( Index=0; Index < Entries; Index++ ) {
p = &pId[Index];
if( p->State != State_Active ) continue;
// Notify this WinStation
if( v_flag ) {
StringMessage(IDS_SENDING_WINSTATION, p->WinStationName);
}
#if DBG
if( v_flag ) {
printf("Open, Now really sending message to Session %ws\n", p->WinStationName);
}
#endif
Result = WinStationSendMessage(
hServerName,
p->LogonId,
pTitle,
(wcslen(pTitle)+1)*sizeof(WCHAR),
mBuf,
(wcslen(mBuf)+1)*sizeof(WCHAR),
MB_OK,
WaitTime,
&Response,
TRUE
);
if( !Result ) {
StringErrorPrintf(IDS_ERROR_SENDING_WINSTATION, p->WinStationName);
}
}
}
/*****************************************************************************
*
* CheckShutdownPrivilege
*
* Check whether the current process has shutdown permission.
*
* ENTRY:
*
* EXIT:
*
*
****************************************************************************/
BOOLEAN
CheckShutdownPrivilege()
{
NTSTATUS Status;
BOOLEAN WasEnabled;
//
// Try the thread token first
//
Status = RtlAdjustPrivilege(SE_SHUTDOWN_PRIVILEGE,
TRUE,
TRUE,
&WasEnabled);
if (Status == STATUS_NO_TOKEN) {
//
// No thread token, use the process token
//
Status = RtlAdjustPrivilege(SE_SHUTDOWN_PRIVILEGE,
TRUE,
FALSE,
&WasEnabled);
}
if (!NT_SUCCESS(Status)) {
return(FALSE);
}
return(TRUE);
}