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