1 /* 2 * COPYRIGHT: See COPYING in the top level directory 3 * PROJECT: ReactOS shutdown/logoff utility 4 * FILE: base/applications/shutdown/shutdown.c 5 * PURPOSE: Initiate logoff, shutdown or reboot of the system 6 */ 7 8 #include "precomp.h" 9 10 #include <stdlib.h> 11 #include <tchar.h> 12 #include <powrprof.h> 13 14 /* 15 * Takes the commandline arguments, and creates a 16 * struct which matches the arguments supplied. 17 */ 18 static DWORD 19 ParseArguments(struct CommandLineOptions* pOpts, int argc, WCHAR *argv[]) 20 { 21 int index; 22 23 if (!pOpts) 24 return ERROR_INVALID_PARAMETER; 25 26 /* Reset all flags in struct */ 27 pOpts->abort = FALSE; 28 pOpts->force = FALSE; 29 pOpts->logoff = FALSE; 30 pOpts->restart = FALSE; 31 pOpts->shutdown = FALSE; 32 pOpts->document_reason = FALSE; 33 pOpts->hibernate = FALSE; 34 pOpts->shutdown_delay = 30; 35 pOpts->remote_system = NULL; 36 pOpts->reason = ParseReasonCode(NULL); /* NOTE: NEVER use 0 here since it can delay the shutdown. */ 37 pOpts->message = NULL; 38 pOpts->show_gui = FALSE; 39 40 /* 41 * Determine which flags the user has specified 42 * to the program so we can use them later. 43 */ 44 for (index = 1; index < argc; index++) 45 { 46 if (argv[index][0] == L'-' || argv[index][0] == L'/') 47 { 48 switch (towlower(argv[index][1])) 49 { 50 case L'?': /* Help */ 51 ConResPuts(StdOut, IDS_USAGE); 52 return ERROR_SUCCESS; 53 54 case L'a': /* Cancel delayed shutdown */ 55 pOpts->abort = TRUE; 56 break; 57 58 case L'c': /* Comment on reason for shutdown */ 59 if (index+1 > argc) 60 return ERROR_INVALID_DATA; 61 if(CheckCommentLength(argv[index+1])) 62 { 63 pOpts->message = argv[index+1]; 64 index++; 65 } 66 else 67 { 68 ConResPuts(StdErr, IDS_ERROR_MAX_COMMENT_LENGTH); 69 return ERROR_BAD_LENGTH; 70 } 71 break; 72 73 case L'd': /* Reason code [p|u:]xx:yy */ 74 if (index+1 >= argc) 75 return ERROR_INVALID_DATA; 76 pOpts->reason = ParseReasonCode(argv[index+1]); 77 index++; 78 break; 79 80 case L'e': /* Documents reason for shutdown */ 81 /* TODO: Determine what this flag does exactly. */ 82 pOpts->document_reason = TRUE; 83 break; 84 85 case L'f': /* Force shutdown without warning */ 86 pOpts->force = TRUE; 87 break; 88 89 case L'h': /* Hibernate the local computer */ 90 pOpts->hibernate = TRUE; 91 break; 92 93 case L'i': /* Shows GUI version of the tool */ 94 pOpts->show_gui = TRUE; 95 break; 96 97 case L'l': /* Logoff the current user */ 98 pOpts->logoff = TRUE; 99 break; 100 101 case L'm': /* Target remote systems (UNC name/IP address) */ 102 if (index+1 >= argc) 103 return ERROR_INVALID_DATA; 104 pOpts->remote_system = argv[index+1]; 105 index++; 106 break; 107 108 case L'p': /* Turn off local computer with no warning/time-out */ 109 pOpts->force = TRUE; 110 pOpts->shutdown_delay = 0; 111 break; 112 113 case L'r': /* Restart computer */ 114 pOpts->restart = TRUE; 115 break; 116 117 case L's': /* Shutdown */ 118 pOpts->shutdown = TRUE; 119 break; 120 121 case L't': /* Shutdown delay */ 122 if (index+1 >= argc) 123 return ERROR_INVALID_DATA; 124 pOpts->shutdown_delay = _wtoi(argv[index+1]); 125 if (pOpts->shutdown_delay > 0) 126 pOpts->force = TRUE; 127 index++; 128 break; 129 130 default: 131 /* Unknown arguments will exit the program. */ 132 ConResPuts(StdOut, IDS_USAGE); 133 return ERROR_SUCCESS; 134 } 135 } 136 } 137 138 return ERROR_SUCCESS; 139 } 140 141 static DWORD 142 EnablePrivilege(LPCWSTR lpszPrivilegeName, BOOL bEnablePrivilege) 143 { 144 DWORD dwRet = ERROR_SUCCESS; 145 HANDLE hToken = NULL; 146 147 if (OpenProcessToken(GetCurrentProcess(), 148 TOKEN_ADJUST_PRIVILEGES, 149 &hToken)) 150 { 151 TOKEN_PRIVILEGES tp; 152 153 tp.PrivilegeCount = 1; 154 tp.Privileges[0].Attributes = (bEnablePrivilege ? SE_PRIVILEGE_ENABLED : 0); 155 156 if (LookupPrivilegeValueW(NULL, 157 lpszPrivilegeName, 158 &tp.Privileges[0].Luid)) 159 { 160 if (AdjustTokenPrivileges(hToken, FALSE, &tp, 0, NULL, NULL)) 161 { 162 if (GetLastError() == ERROR_NOT_ALL_ASSIGNED) 163 dwRet = ERROR_NOT_ALL_ASSIGNED; 164 } 165 else 166 { 167 dwRet = GetLastError(); 168 } 169 } 170 else 171 { 172 dwRet = GetLastError(); 173 } 174 175 CloseHandle(hToken); 176 } 177 else 178 { 179 dwRet = GetLastError(); 180 } 181 182 /* Display the error description if any */ 183 if (dwRet != ERROR_SUCCESS) DisplayError(dwRet); 184 185 return dwRet; 186 } 187 188 /* Main entry for program */ 189 int wmain(int argc, WCHAR *argv[]) 190 { 191 DWORD error = ERROR_SUCCESS; 192 struct CommandLineOptions opts; 193 194 /* Initialize the Console Standard Streams */ 195 ConInitStdStreams(); 196 197 if (argc == 1) /* i.e. no commandline arguments given */ 198 { 199 ConResPuts(StdOut, IDS_USAGE); 200 return EXIT_SUCCESS; 201 } 202 203 error = ParseArguments(&opts, argc, argv); 204 if (error != ERROR_SUCCESS) 205 { 206 DisplayError(error); 207 return EXIT_FAILURE; 208 } 209 210 /* If the user wants to abort a shutdown */ 211 if (opts.abort) 212 { 213 /* First, the program has to determine if the shutdown/restart is local 214 or remote. This is done since each one requires separate privileges. */ 215 if (opts.remote_system == NULL) 216 EnablePrivilege(SE_SHUTDOWN_NAME, TRUE); 217 else 218 EnablePrivilege(SE_REMOTE_SHUTDOWN_NAME, TRUE); 219 220 /* Abort the delayed system shutdown specified. */ 221 if (!AbortSystemShutdownW(opts.remote_system)) 222 { 223 ConResPuts(StdErr, IDS_ERROR_ABORT); 224 DisplayError(GetLastError()); 225 return EXIT_FAILURE; 226 } 227 else 228 { 229 return EXIT_SUCCESS; 230 } 231 } 232 233 /* 234 * If the user wants to hibernate the computer. Assume 235 * that the user wants to wake the computer up from 236 * hibernation and it should not force it on the system. 237 */ 238 if (opts.hibernate) 239 { 240 if (IsPwrHibernateAllowed()) 241 { 242 EnablePrivilege(SE_SHUTDOWN_NAME, TRUE); 243 244 /* The shutdown utility cannot hibernate remote systems */ 245 if (opts.remote_system != NULL) 246 { 247 return EXIT_FAILURE; 248 } 249 250 if (!SetSuspendState(TRUE, FALSE, FALSE)) 251 { 252 ConResPuts(StdErr, IDS_ERROR_HIBERNATE); 253 DisplayError(GetLastError()); 254 return EXIT_FAILURE; 255 } 256 else 257 { 258 ConResPuts(StdOut, IDS_ERROR_HIBERNATE_ENABLED); 259 return EXIT_SUCCESS; 260 } 261 } 262 else 263 { 264 return EXIT_FAILURE; 265 } 266 } 267 268 /* Both shutdown and restart flags cannot both be true */ 269 if (opts.shutdown && opts.restart) 270 { 271 ConResPuts(StdErr, IDS_ERROR_SHUTDOWN_REBOOT); 272 return EXIT_FAILURE; 273 } 274 275 /* Ensure that the timeout amount is not too high or a negative number */ 276 if (opts.shutdown_delay > MAX_SHUTDOWN_TIMEOUT) 277 { 278 ConResPrintf(StdErr, IDS_ERROR_TIMEOUT, opts.shutdown_delay); 279 return EXIT_FAILURE; 280 } 281 282 /* If the user wants a GUI environment */ 283 if (opts.show_gui) 284 { 285 if (ShutdownGuiMain(opts)) 286 return EXIT_SUCCESS; 287 else 288 return EXIT_FAILURE; 289 } 290 291 if (opts.logoff && (opts.remote_system == NULL)) 292 { 293 /* 294 * NOTE: Sometimes, shutdown and logoff are used together. If the logoff 295 * flag is used by itself, then simply logoff. But if used with shutdown, 296 * then skip logging off of the computer and eventually go to the action 297 * for shutdown. 298 */ 299 if (!opts.shutdown && !opts.restart) 300 { 301 EnablePrivilege(SE_SHUTDOWN_NAME, TRUE); 302 303 if (ExitWindowsEx(EWX_LOGOFF, opts.reason)) 304 { 305 return EXIT_SUCCESS; 306 } 307 else 308 { 309 ConResPuts(StdErr, IDS_ERROR_LOGOFF); 310 DisplayError(GetLastError()); 311 return EXIT_FAILURE; 312 } 313 } 314 } 315 316 /* 317 * Since both shutting down the system and restarting calls the exact same 318 * function, all we need to know is if we wanted to restart or shutdown. 319 */ 320 if (opts.shutdown || opts.restart) 321 { 322 /* 323 * First, the program has to determine if the shutdown/restart is local 324 * or remote. This is done since each one requires separate privileges. 325 */ 326 if (opts.remote_system == NULL) 327 { 328 EnablePrivilege(SE_SHUTDOWN_NAME, TRUE); 329 } 330 else 331 { 332 /* TODO: Remote shutdown is not supported yet */ 333 // EnablePrivilege(SE_REMOTE_SHUTDOWN_NAME, TRUE); 334 return EXIT_SUCCESS; 335 } 336 337 /* Initiate the shutdown */ 338 if (!InitiateSystemShutdownExW(opts.remote_system, 339 opts.message, 340 opts.shutdown_delay, 341 opts.force, 342 opts.restart, 343 opts.reason)) 344 { 345 /* 346 * If there is an error, give the proper output depending 347 * on whether the user wanted to shutdown or restart. 348 */ 349 if (opts.restart) 350 ConResPuts(StdErr, IDS_ERROR_RESTART); 351 else 352 ConResPuts(StdErr, IDS_ERROR_SHUTDOWN); 353 354 DisplayError(GetLastError()); 355 return EXIT_FAILURE; 356 } 357 else 358 { 359 return EXIT_SUCCESS; 360 } 361 } 362 363 return EXIT_SUCCESS; 364 } 365 366 /* EOF */ 367