487 lines
11 KiB
C
487 lines
11 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);
|
|
}
|
|
|