Revert "[SHELL32] SHChangeNotify: Use tree for CDirectoryList (#6784)" (#6800)
[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 * 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 (!argv[index+1] || wcslen(argv[index+1]) <= 512)
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 */