From 085f7002ae9345287044e12d96bc9cc60edd6fcd Mon Sep 17 00:00:00 2001 From: Colin Finck Date: Sun, 15 Jul 2007 11:15:27 +0000 Subject: [PATCH] Add a "shutdown" utility written by Frode Lillerud (frode AT enkelt DOT no), which is more compatible with the syntax of the Windows "shutdown" utility I applied the second patch of bug #2379 and made the following changes to it: - Removed the LocalFree line LocalFree can only be used if the memory is allocated with LocalAlloc. The memory doesn't need to be freed explicitly here. - Removed the "uncertain if this is needed. Do boolean struct members default to TRUE?" comment Independent of the value, this setting is needed here. - Removed a wrong 'exitOpts.shouldExit = TRUE;' in line 101. shouldExit was already set before and resetting it here to TRUE would also lead to TRUE if the user only sets opts.force, but not any of the other shutdown options. - Use a consistent indentation and coding style - Modified the Usage text a little bit, so all lines fit into console lines (max. 80 characters) See issue #2379 for more details. svn path=/trunk/; revision=27674 --- reactos/base/applications/shutdown/shutdown.c | 336 ++++++++++-------- 1 file changed, 181 insertions(+), 155 deletions(-) diff --git a/reactos/base/applications/shutdown/shutdown.c b/reactos/base/applications/shutdown/shutdown.c index 1a45728c408..439d1a11a16 100644 --- a/reactos/base/applications/shutdown/shutdown.c +++ b/reactos/base/applications/shutdown/shutdown.c @@ -1,7 +1,7 @@ /* * COPYRIGHT: See COPYING in the top level directory * PROJECT: ReactOS shutdown/logoff utility - * FILE: apps/utils/shutdown/shutdown.c + * FILE: base/application/shutdown/shutdown.c * PURPOSE: Initiate logoff, shutdown or reboot of the system */ @@ -9,162 +9,188 @@ #include #include #include +#include //shutdown codes -static void -PrintUsage(LPCTSTR Cmd) -{ - _ftprintf(stderr, _T("usage: %s [action] [flag]\n"), Cmd); - _ftprintf(stderr, _T(" action = \"logoff\", \"reboot\", \"shutdown\" or \"poweroff\"\n")); - _ftprintf(stderr, _T(" flag = \"force\"\n")); +// Print information about which commandline arguments the program accepts. +static void PrintUsage() { + _tprintf("Usage: shutdown [-?] [-l | -s | -r] [-f]\n"); + _tprintf("\n No args or -?\t\tDisplay this message"); + _tprintf("\n -l\t\t\tLog off"); + _tprintf("\n -s\t\t\tShutdown the computer"); + _tprintf("\n -r\t\t\tShutdown and restart the computer"); + _tprintf("\n -f\t\t\tForces running applications to close without warnings"); + _tprintf("\n"); } -int -_tmain(int argc, TCHAR *argv[]) +struct CommandLineOptions { + BOOL abort; // Not used yet + BOOL force; + BOOL logoff; + BOOL restart; + BOOL shutdown; +}; + +struct ExitOptions { + // This flag is used to distinguish between a user-initiated LOGOFF (which has value 0) + // and an underdetermined situation because user didn't give an argument to start Exit. + BOOL shouldExit; + // flags is the type of shutdown to do - EWX_LOGOFF, EWX_REBOOT, EWX_POWEROFF, etc.. + UINT flags; + // reason is the System Shutdown Reason code. F.instance SHTDN_REASON_MAJOR_OTHER | SHTDN_REASON_MINOR_OTHER | SHTDN_REASON_FLAG_PLANNED. + DWORD reason; +}; + +// Takes the commandline arguments, and creates a struct which matches the arguments supplied. +static struct CommandLineOptions ParseArguments(int argc, TCHAR *argv[]) { - static struct - { - TCHAR *Name; - UINT ExitType; - UINT ExitFlags; - } - Options[] = - { - { _T("logoff"), EWX_LOGOFF, 0 }, - { _T("logout"), EWX_LOGOFF, 0 }, - { _T("poweroff"), EWX_POWEROFF, 0 }, - { _T("powerdown"), EWX_POWEROFF, 0 }, - { _T("reboot"), EWX_REBOOT, 0 }, - { _T("restart"), EWX_REBOOT, 0 }, - { _T("shutdown"), EWX_SHUTDOWN, 0 }, - { _T("force"), 0, EWX_FORCE }, - { _T("forceifhung"), 0, EWX_FORCEIFHUNG }, - { _T("ifhung"), 0, EWX_FORCEIFHUNG }, - { _T("hung"), 0, EWX_FORCEIFHUNG }, - }; - UINT ExitType, ExitFlags; - HANDLE hToken; - TOKEN_PRIVILEGES npr; - TCHAR *Arg; - TCHAR BaseName[_MAX_FNAME]; - unsigned i, j; - BOOL HaveType, Matched; - - ExitType = 0; - ExitFlags = 0; - HaveType = FALSE; - - _tsplitpath(argv[0], NULL, NULL, BaseName, NULL); - - /* Process optional arguments */ - for (i = 1; i < (unsigned) argc; i++) - { - /* Allow e.g. "/s" or "-l" for shutdown resp. logoff */ - Arg = argv[i]; - if (_T('/') == *Arg || _T('-') == *Arg) - { - Arg++; - } - - /* Search known options */ - Matched = FALSE; - for (j = 0; j < sizeof(Options) / sizeof(Options[0]) && ! Matched; j++) - { - /* Match if arg starts the same as the option name */ - if (0 == _tcsnicmp(Options[j].Name, Arg, _tcslen(Arg))) - { - if (0 == Options[j].ExitFlags) - { - /* Can have only 1 type */ - if (HaveType) - { - PrintUsage(BaseName); - exit(1); - } - ExitType = Options[j].ExitType; - HaveType = TRUE; - } - else - { - /* Can have only 1 flag */ - if (0 != ExitFlags) - { - PrintUsage(BaseName); - exit(1); - } - ExitFlags |= Options[j].ExitFlags; - } - Matched = TRUE; - } - } - - /* Was the argument processed? */ - if (! Matched) - { - PrintUsage(BaseName); - exit(1); - } - } - - /* Check command name if user didn't explicitly specify action */ - if (! HaveType) - { - for (j = 0; j < sizeof(Options) / sizeof(Options[0]); j++) - { - if (0 == _tcsicmp(Options[j].Name, BaseName) && 0 == Options[j].ExitFlags) - { - ExitType = Options[j].ExitType; - HaveType = TRUE; - } - } - } - - /* Still not sure what to do? */ - if (! HaveType) - { - PrintUsage(BaseName); - exit(1); - } - - /* Everyone can logoff, for the other actions you need the appropriate privilege */ - if (EWX_LOGOFF != ExitType) - { - /* enable shutdown privilege for current process */ - if (! OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &hToken)) - { - _ftprintf(stderr, _T("OpenProcessToken failed with error %d\n"), (int) GetLastError()); - exit(1); - } - npr.PrivilegeCount = 1; - npr.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; - if (! LookupPrivilegeValue(NULL, SE_SHUTDOWN_NAME, &npr.Privileges[0].Luid)) - { - CloseHandle(hToken); - _ftprintf(stderr, _T("LookupPrivilegeValue failed with error %d\n"), (int) GetLastError()); - exit(1); - } - if (! AdjustTokenPrivileges(hToken, FALSE, &npr, 0, 0, 0) - || ERROR_SUCCESS != GetLastError()) - { - if (ERROR_NOT_ALL_ASSIGNED == GetLastError()) - { - _ftprintf(stderr, _T("You are not authorized to shutdown the system\n")); - } - else - { - _ftprintf(stderr, _T("AdjustTokenPrivileges failed with error %d\n"), (int) GetLastError()); - } - CloseHandle(hToken); - exit(1); - } - CloseHandle(hToken); - } - - /* Finally do it */ - if (! ExitWindowsEx(ExitType | ExitFlags, 0)) - { - _ftprintf(stderr, _T("ExitWindowsEx failed with error %d\n"), (int) GetLastError()); - exit(1); - } - - return 0; + struct CommandLineOptions opts; + int i; + + // Reset all flags in struct + opts.abort = FALSE; + opts.force = FALSE; + opts.logoff = FALSE; + opts.restart = FALSE; + opts.shutdown = FALSE; + + for (i = 1; i < argc; i++) + { + if (argv[i][0] == '-' || argv[i][0] == '/') + { + switch(argv[i][1]) { + case '?': PrintUsage(); exit(0); + case 'f': opts.force = TRUE; break; + case 'l': opts.logoff = TRUE; break; + case 'r': opts.restart = TRUE; break; + case 's': opts.shutdown = TRUE; break; + default: + // Unknown arguments will exit program. + PrintUsage(); + exit(0); + break; + } + } + } + + return opts; } + +// Converts the commandline arguments to flags used to shutdown computer +static struct ExitOptions ParseCommandLineOptionsToExitOptions(struct CommandLineOptions opts) +{ + struct ExitOptions exitOpts; + exitOpts.shouldExit = TRUE; + + // Sets ONE of the exit type flags + if (opts.logoff) + exitOpts.flags = EWX_LOGOFF; + else if (opts.restart) + exitOpts.flags = EWX_REBOOT; + else if(opts.shutdown) + exitOpts.flags = EWX_POWEROFF; + else + { + exitOpts.flags = 0; + exitOpts.shouldExit = FALSE; + } + + // Sets additional flags + if (opts.force) + exitOpts.flags = exitOpts.flags | EWX_FORCE; + + // Reason for shutdown + // Hardcoded to "Other (Planned)" + exitOpts.reason = SHTDN_REASON_MAJOR_OTHER | SHTDN_REASON_MINOR_OTHER | SHTDN_REASON_FLAG_PLANNED; + + return exitOpts; +} + +// Writes the last error as both text and error code to the console. +void DisplayLastError() +{ + int errorCode = GetLastError(); + LPTSTR lpMsgBuf; + + // Display the error message to the user + FormatMessage( + FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, + NULL, + errorCode, + LANG_USER_DEFAULT, + (LPTSTR) &lpMsgBuf, + 0, + NULL); + + _ftprintf(stderr, lpMsgBuf); + _ftprintf(stderr, _T("Error code: %d\n"), errorCode); +} + +void EnableShutdownPrivileges() +{ + HANDLE token; + TOKEN_PRIVILEGES privs; + + // Check to see if the choosen action is allowed by the user. Everyone can call LogOff, but only privilieged users can shutdown/restart etc. + if (! OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &token)) + { + DisplayLastError(); + exit(1); + } + + // Get LUID (Locally Unique Identifier) for the privilege we need + if (!LookupPrivilegeValue( + NULL, // system - NULL is localsystem + SE_SHUTDOWN_NAME, // name of the privilege + &privs.Privileges[0].Luid) // output + ) + { + DisplayLastError(); + exit(1); + } + // and give our current process (i.e. shutdown.exe) the privilege to shutdown the machine. + privs.PrivilegeCount = 1; + privs.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; + if (AdjustTokenPrivileges( + token, + FALSE, + &privs, + 0, + (PTOKEN_PRIVILEGES)NULL, // previous state. Set to NULL, we don't care about previous state. + NULL + ) == 0) // return value 0 means failure + { + DisplayLastError(); + exit(1); + } +} + + // Main entry for program +int _tmain(int argc, TCHAR *argv[]) +{ + struct CommandLineOptions opts; + struct ExitOptions exitOpts; + + if (argc == 1) // i.e. no commandline arguments given + { + PrintUsage(); + exit(0); + } + + opts = ParseArguments(argc, argv); + exitOpts = ParseCommandLineOptionsToExitOptions(opts); + + // Perform the shutdown/restart etc. action + if (exitOpts.shouldExit) + { + EnableShutdownPrivileges(); + + if (!ExitWindowsEx(exitOpts.flags, exitOpts.reason)) + { + DisplayLastError(); + exit(1); + } + } + return 0; +} + +// EOF