//Copyright (c) 1998 - 1999 Microsoft Corporation /************************************************************************* * * TSSHUTDN.C * This module is the TSSHUTDN utility code. * * *************************************************************************/ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #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); }