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 */
367