Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

501 lines
12 KiB

  1. //Copyright (c) 1998 - 1999 Microsoft Corporation
  2. /*************************************************************************
  3. *
  4. * TSSHUTDN.C
  5. * This module is the TSSHUTDN utility code.
  6. *
  7. *
  8. *************************************************************************/
  9. #include <nt.h>
  10. #include <ntrtl.h>
  11. #include <nturtl.h>
  12. #include <ntlsa.h>
  13. #include <stdio.h>
  14. #include <wchar.h>
  15. #include <windows.h>
  16. #include <ntddkbd.h>
  17. #include <ntddmou.h>
  18. #include <winstaw.h>
  19. #include <stdlib.h>
  20. #include <utilsub.h>
  21. #include <string.h>
  22. #include <malloc.h>
  23. #include <locale.h>
  24. #include <winnlsp.h>
  25. #include "tsshutdn.h"
  26. #include "printfoa.h"
  27. // max length of the locale string
  28. #define MAX_LOCALE_STRING 64
  29. #define DEFAULT_WAIT_TIME 60
  30. #define DEFAULT_LOGOFF_DELAY 30
  31. #define MAX_MESSAGE_LENGTH 256
  32. WCHAR WSTime[MAX_IDS_LEN+2];
  33. WCHAR WDTime[MAX_IDS_LEN+2];
  34. USHORT help_flag = FALSE;
  35. USHORT v_flag = FALSE;
  36. USHORT RebootFlag = FALSE;
  37. USHORT PowerDownFlag = FALSE;
  38. USHORT FastFlag = FALSE;
  39. #if 0
  40. USHORT DumpFlag = FALSE;
  41. #endif
  42. HANDLE hServerName = SERVERNAME_CURRENT;
  43. WCHAR ServerName[MAX_IDS_LEN+1];
  44. TOKMAP ptm[] =
  45. {
  46. {TOKEN_TIME, TMFLAG_OPTIONAL, TMFORM_S_STRING, MAX_IDS_LEN, WSTime},
  47. {TOKEN_SERVER, TMFLAG_OPTIONAL, TMFORM_STRING, MAX_IDS_LEN, ServerName},
  48. {TOKEN_DELAY, TMFLAG_OPTIONAL, TMFORM_STRING, MAX_IDS_LEN, WDTime},
  49. {TOKEN_HELP, TMFLAG_OPTIONAL, TMFORM_BOOLEAN, sizeof(USHORT), &help_flag},
  50. {TOKEN_VERBOSE, TMFLAG_OPTIONAL, TMFORM_BOOLEAN, sizeof(USHORT), &v_flag},
  51. {TOKEN_REBOOT, TMFLAG_OPTIONAL, TMFORM_BOOLEAN, sizeof(USHORT), &RebootFlag},
  52. {TOKEN_POWERDOWN,TMFLAG_OPTIONAL, TMFORM_BOOLEAN, sizeof(USHORT), &PowerDownFlag},
  53. {TOKEN_FAST, TMFLAG_OPTIONAL, TMFORM_BOOLEAN, sizeof(USHORT), &FastFlag},
  54. #if 0
  55. {TOKEN_DUMP, TMFLAG_OPTIONAL, TMFORM_BOOLEAN, sizeof(USHORT), &DumpFlag},
  56. #endif
  57. {0, 0, 0, 0, 0}
  58. };
  59. /*
  60. * Local function prototypes.
  61. */
  62. void Usage( BOOLEAN bError );
  63. void NotifyUsers( ULONG WaitTime );
  64. void NotifyWinStations( PLOGONIDW, ULONG, ULONG );
  65. BOOLEAN CheckShutdownPrivilege();
  66. /*************************************************************************
  67. *
  68. * main
  69. * Main function and entry point of the TSSHUTDN utility.
  70. *
  71. * ENTRY:
  72. * argc - count of the command line arguments.
  73. * argv - vector of strings containing the command line arguments.
  74. *
  75. * EXIT
  76. * Nothing.
  77. *
  78. *************************************************************************/
  79. int __cdecl
  80. main(INT argc, CHAR **argv)
  81. {
  82. int rc, i;
  83. ULONG Error;
  84. WCHAR *CmdLine;
  85. WCHAR **argvW, *endptr;
  86. ULONG ShutdownFlags = WSD_SHUTDOWN | WSD_LOGOFF;
  87. ULONG WaitTime = DEFAULT_WAIT_TIME;
  88. ULONG LogoffDelay = DEFAULT_LOGOFF_DELAY;
  89. WCHAR wszString[MAX_LOCALE_STRING + 1];
  90. setlocale(LC_ALL, ".OCP");
  91. // We don't want LC_CTYPE set the same as the others or else we will see
  92. // garbage output in the localized version, so we need to explicitly
  93. // set it to correct console output code page
  94. _snwprintf(wszString, sizeof(wszString)/sizeof(WCHAR), L".%d", GetConsoleOutputCP());
  95. wszString[sizeof(wszString)/sizeof(WCHAR) - 1] = L'\0';
  96. _wsetlocale(LC_CTYPE, wszString);
  97. SetThreadUILanguage(0);
  98. /*
  99. * Massage the command line.
  100. */
  101. argvW = MassageCommandLine((DWORD)argc);
  102. if (argvW == NULL) {
  103. ErrorPrintf(IDS_ERROR_MALLOC);
  104. return(FAILURE);
  105. }
  106. /*
  107. * parse the cmd line without parsing the program name (argc-1, argv+1)
  108. */
  109. WSTime[0] = L'\0';
  110. rc = ParseCommandLine(argc-1, argvW+1, ptm, 0);
  111. /*
  112. * Check for error from ParseCommandLine
  113. */
  114. if ( help_flag || (rc && !(rc & PARSE_FLAG_NO_PARMS)) ) {
  115. if ( !help_flag ) {
  116. Usage(TRUE);
  117. return(FAILURE);
  118. } else {
  119. Usage(FALSE);
  120. return(SUCCESS);
  121. }
  122. }
  123. // If no remote server was specified, then check if we are running under Terminal Server
  124. if ((!IsTokenPresent(ptm, TOKEN_SERVER) ) && (!AreWeRunningTerminalServices()))
  125. {
  126. ErrorPrintf(IDS_ERROR_NOT_TS);
  127. return(FAILURE);
  128. }
  129. /*
  130. * Open the specified server
  131. */
  132. if( ServerName[0] ) {
  133. hServerName = WinStationOpenServer( ServerName );
  134. if( hServerName == NULL ) {
  135. StringErrorPrintf(IDS_ERROR_SERVER,ServerName);
  136. PutStdErr( GetLastError(), 0 );
  137. return(FAILURE);
  138. }
  139. }
  140. // Make sure the user has the proper privilege
  141. // SM should really do the check
  142. /*
  143. if( !CheckShutdownPrivilege() ) {
  144. ErrorPrintf(IDS_ERROR_NO_RIGHTS);
  145. return(FAILURE);
  146. }
  147. */
  148. // Make sure its a number
  149. if ( WSTime[0] ) {
  150. if( !iswdigit(WSTime[0]) ) {
  151. StringErrorPrintf(IDS_ERROR_INVALID_TIME, WSTime);
  152. return(FAILURE);
  153. }
  154. WaitTime = wcstoul(WSTime, &endptr, 10);
  155. if ( *endptr ) {
  156. StringErrorPrintf(IDS_ERROR_INVALID_TIME, WSTime);
  157. return(FAILURE);
  158. }
  159. }
  160. // Make sure its a number
  161. if ( WDTime[0] ) {
  162. if( !iswdigit(WDTime[0]) ) {
  163. StringErrorPrintf(IDS_ERROR_INVALID_DELAY, WDTime);
  164. return(FAILURE);
  165. }
  166. LogoffDelay = wcstoul(WDTime, &endptr, 10);
  167. if ( *endptr ) {
  168. StringErrorPrintf(IDS_ERROR_INVALID_DELAY, WDTime);
  169. return(FAILURE);
  170. }
  171. }
  172. #if 0
  173. /*
  174. * If /dump option was specified, call NT function directly
  175. */
  176. if ( DumpFlag ) {
  177. NtShutdownSystem( ShutdownDump ); // will not return
  178. }
  179. #endif
  180. if( RebootFlag ) {
  181. ShutdownFlags |= WSD_REBOOT;
  182. }
  183. if( PowerDownFlag )
  184. ShutdownFlags |= WSD_POWEROFF;
  185. if( FastFlag ) {
  186. ShutdownFlags |= WSD_FASTREBOOT;
  187. ShutdownFlags &= ~WSD_LOGOFF;
  188. WaitTime = 0;
  189. }
  190. if( WaitTime ) {
  191. NotifyUsers( WaitTime );
  192. }
  193. /*
  194. * If necessary, force all WinStations to logoff
  195. */
  196. if ( ShutdownFlags & WSD_LOGOFF ) {
  197. Message( IDS_SHUTTING_DOWN, 0 );
  198. if ( !WinStationShutdownSystem( hServerName, WSD_LOGOFF ) ) {
  199. Error = GetLastError();
  200. ErrorPrintf( IDS_ERROR_SHUTDOWN_FAILED, Error );
  201. PutStdErr( Error, 0 );
  202. return( FAILURE );
  203. }
  204. Message( IDS_LOGOFF_USERS, 0);
  205. if (LogoffDelay) {
  206. NotifyUsers( LogoffDelay );
  207. }
  208. Message( IDS_SHUTDOWN_DONE, 0 );
  209. }
  210. /*
  211. * Inform user of impending reboot/poweroff
  212. */
  213. if ( ShutdownFlags & WSD_REBOOT ) {
  214. Message( IDS_SHUTDOWN_REBOOT, 0 );
  215. Sleep( 4000 );
  216. } else if ( ShutdownFlags & WSD_POWEROFF ) {
  217. Message( IDS_SHUTDOWN_POWERDOWN, 0 );
  218. Sleep( 4000 );
  219. }
  220. /*
  221. * Perform system shutdown, reboot, or poweroff, depending on flags
  222. */
  223. if( WinStationShutdownSystem( hServerName, ShutdownFlags & ~WSD_LOGOFF ) != ERROR_SUCCESS )
  224. {
  225. PutStdErr( GetLastError(), 0 );
  226. }
  227. // WinStationShutdownSystem is done asynchronously.
  228. // No way to know when the shudown is completed.
  229. //if ( !(ShutdownFlags & WSD_REBOOT) && !( ShutdownFlags & WSD_POWEROFF ) ) {
  230. // /*
  231. // * If we get here, shutdown is complete, all disks are write protected.
  232. // */
  233. // Message(IDS_SHUTDOWN_WRITEPROT, 0);
  234. //}
  235. return(SUCCESS);
  236. } /* main() */
  237. /*******************************************************************************
  238. *
  239. * Usage
  240. *
  241. * Output the usage message for this utility.
  242. *
  243. * ENTRY:
  244. * bError (input)
  245. * TRUE if the 'invalid parameter(s)' message should preceed the usage
  246. * message and the output go to stderr; FALSE for no such error
  247. * string and output goes to stdout.
  248. *
  249. * EXIT:
  250. *
  251. *
  252. ******************************************************************************/
  253. void
  254. Usage( BOOLEAN bError )
  255. {
  256. WCHAR sz1[1024];
  257. LoadString( NULL, IDS_USAGE, sz1, 1024 );
  258. if ( bError ) {
  259. ErrorPrintf(IDS_ERROR_INVALID_PARAMETERS);
  260. fwprintf(stderr, sz1);
  261. } else {
  262. fwprintf(stdout,sz1);
  263. }
  264. } /* Usage() */
  265. /*****************************************************************************
  266. *
  267. * NotifyUsers
  268. *
  269. * Notify Users that the system is being shutdown
  270. *
  271. * ENTRY:
  272. * WaitTime (input)
  273. * Amount of time to give them to log off.
  274. *
  275. * EXIT:
  276. *
  277. ****************************************************************************/
  278. void
  279. NotifyUsers( ULONG WaitTime )
  280. {
  281. BOOLEAN Result;
  282. ULONG Entries;
  283. ULONG Error;
  284. PLOGONIDW ptr;
  285. //
  286. // Get all of the WinStations call the function to notify them.
  287. //
  288. if ( WinStationEnumerateW( hServerName, &ptr, &Entries ) ) {
  289. NotifyWinStations( ptr, Entries, WaitTime );
  290. WinStationFreeMemory(ptr);
  291. } else {
  292. Error = GetLastError();
  293. #if DBG
  294. printf("TSSHUTDN: Error emumerating Sessions %d\n",Error);
  295. #endif
  296. return;
  297. }
  298. Message(IDS_NOTIFYING_USERS);
  299. // Now wait the wait time
  300. SleepEx( WaitTime*1000, FALSE );
  301. return;
  302. }
  303. /*****************************************************************************
  304. *
  305. * NotifyWinStations
  306. *
  307. * Notify the group of WinStations about the impending system shutdown
  308. *
  309. * ENTRY:
  310. * pId (input)
  311. * Array of LOGONIDW's
  312. *
  313. * Entries (input)
  314. * Number of entries in array
  315. *
  316. * WaitTime (input)
  317. * Amount of time to wait in seconds
  318. *
  319. * EXIT:
  320. * STATUS_SUCCESS - no error
  321. *
  322. ****************************************************************************/
  323. void
  324. NotifyWinStations(
  325. PLOGONIDW pId,
  326. ULONG Entries,
  327. ULONG WaitTime
  328. )
  329. {
  330. ULONG Index;
  331. PLOGONIDW p;
  332. ULONG Response;
  333. BOOLEAN Result;
  334. WCHAR mBuf[MAX_MESSAGE_LENGTH+2];
  335. // PWCHAR pTitle = L"SYSTEM SHUTDOWN";
  336. PWCHAR pTitle;
  337. WCHAR sz1[256], sz2[512];
  338. LoadString( NULL, IDS_SHUTDOWN_TITLE, sz1, 256 );
  339. pTitle = &(sz1[0]);
  340. // Create the message
  341. LoadString( NULL, IDS_SHUTDOWN_MESSAGE, sz2, 512 );
  342. _snwprintf( mBuf, MAX_MESSAGE_LENGTH, sz2, WaitTime);
  343. for( Index=0; Index < Entries; Index++ ) {
  344. p = &pId[Index];
  345. if( p->State != State_Active ) continue;
  346. // Notify this WinStation
  347. if( v_flag ) {
  348. StringMessage(IDS_SENDING_WINSTATION, p->WinStationName);
  349. }
  350. #if DBG
  351. if( v_flag ) {
  352. printf("Open, Now really sending message to Session %ws\n", p->WinStationName);
  353. }
  354. #endif
  355. Result = WinStationSendMessage(
  356. hServerName,
  357. p->LogonId,
  358. pTitle,
  359. (wcslen(pTitle)+1)*sizeof(WCHAR),
  360. mBuf,
  361. (wcslen(mBuf)+1)*sizeof(WCHAR),
  362. MB_OK,
  363. WaitTime,
  364. &Response,
  365. TRUE
  366. );
  367. if( !Result ) {
  368. StringErrorPrintf(IDS_ERROR_SENDING_WINSTATION, p->WinStationName);
  369. }
  370. }
  371. }
  372. /*****************************************************************************
  373. *
  374. * CheckShutdownPrivilege
  375. *
  376. * Check whether the current process has shutdown permission.
  377. *
  378. * ENTRY:
  379. *
  380. * EXIT:
  381. *
  382. *
  383. ****************************************************************************/
  384. BOOLEAN
  385. CheckShutdownPrivilege()
  386. {
  387. NTSTATUS Status;
  388. BOOLEAN WasEnabled;
  389. //
  390. // Try the thread token first
  391. //
  392. Status = RtlAdjustPrivilege(SE_SHUTDOWN_PRIVILEGE,
  393. TRUE,
  394. TRUE,
  395. &WasEnabled);
  396. if (Status == STATUS_NO_TOKEN) {
  397. //
  398. // No thread token, use the process token
  399. //
  400. Status = RtlAdjustPrivilege(SE_SHUTDOWN_PRIVILEGE,
  401. TRUE,
  402. FALSE,
  403. &WasEnabled);
  404. }
  405. if (!NT_SUCCESS(Status)) {
  406. return(FALSE);
  407. }
  408. return(TRUE);
  409. }