/*++ Copyright (c) 2013 Minoca Corp. All Rights Reserved Module Name: swiss.c Abstract: This module implements the Swiss utility, which contains many of the standard commands packaged into one binary. Author: Evan Green 5-Jun-2013 Environment: User Mode --*/ // // ------------------------------------------------------------------- Includes // #include #include #include #include #include #include "swiss.h" #include "swisscmd.h" #include "swlib.h" // // ---------------------------------------------------------------- Definitions // #define SWISS_VERSION_MAJOR 1 #define SWISS_VERSION_MINOR 0 // // ------------------------------------------------------ Data Type Definitions // // // ----------------------------------------------- Internal Function Prototypes // // // -------------------------------------------------------------------- Globals // // // ------------------------------------------------------------------ Functions // INT main ( INT ArgumentCount, CHAR **Arguments ) /*++ Routine Description: This routine implements the main entry point for the Swiss utility. Arguments: ArgumentCount - Supplies the number of arguments on the command line. Arguments - Supplies an array of pointers to strings representing the arguments. Return Value: 0 on success. Non-zero on failure. --*/ { PSWISS_COMMAND_ENTRY Command; ULONG CommandIndex; PSTR CommandName; PSTR Dot; id_t EffectiveId; PSTR LastSlash; id_t RealId; BOOL Result; INT ReturnValue; SwSetCurrentApplicationName("swiss"); // // Figure out the command name. Something like c:/mydir/sh.exe should // become just 'sh'. // CommandName = Arguments[0]; LastSlash = strrchr(Arguments[0], '/'); if (LastSlash == NULL) { LastSlash = strrchr(Arguments[0], '\\'); } if (LastSlash != NULL) { LastSlash += 1; } else { LastSlash = Arguments[0]; } CommandName = strdup(LastSlash); if (CommandName == NULL) { ReturnValue = 1; goto mainEnd; } Dot = strchr(CommandName, '.'); if (Dot != NULL) { *Dot = '\0'; } // // Look for the command in either the last component of argv[0] or in // argv[1]. // Command = SwissFindCommand(CommandName); if (Command == NULL) { if (ArgumentCount > 1) { Command = SwissFindCommand(Arguments[1]); if (Command != NULL) { Arguments += 1; ArgumentCount -= 1; } // // Default to sh if swiss was just run directly with no arguments. // } else if (strncasecmp(CommandName, "swiss", 5) == 0) { Command = SwissFindCommand(SH_COMMAND_NAME); if (Command != NULL) { SwPrintVersion(SWISS_VERSION_MAJOR, SWISS_VERSION_MINOR); } } } // // This is not a valid command. // if (Command == NULL) { ReturnValue = 1; // // The entry was not valid, so look at the next argument. If there is // no next argument, print the usage and exit. // if ((ArgumentCount < 2) || (strcmp(Arguments[1], "--help") == 0)) { printf("Usage: swiss ...\n\nValid Commands:\n\n"); CommandIndex = 0; while (SwissCommands[CommandIndex].CommandName != NULL) { if ((SwissCommands[CommandIndex].Flags & SWISS_APP_HIDDEN) == 0) { printf("%10s - %s\n", SwissCommands[CommandIndex].CommandName, SwissCommands[CommandIndex].CommandDescription); } CommandIndex += 1; } printf("\n"); } else if (strcmp(Arguments[1], "--list") == 0) { CommandIndex = 0; while (SwissCommands[CommandIndex].CommandName != NULL) { if ((SwissCommands[CommandIndex].Flags & SWISS_APP_HIDDEN) == 0) { printf("%s\n", SwissCommands[CommandIndex].CommandName); } CommandIndex += 1; } ReturnValue = 0; } else { SwPrintError(0, NULL, "Command not found in either '%s' nor '%s'", CommandName, Arguments[1]); } goto mainEnd; } // // Drop setuid privileges unless the app wants to keep them. // if ((Command->Flags & SWISS_APP_SETUID_OK) == 0) { RealId = SwGetRealUserId(); EffectiveId = SwGetEffectiveUserId(); if ((RealId != 0) && (RealId != EffectiveId)) { ReturnValue = SwSetRealUserId(RealId); if (ReturnValue != 0) { SwPrintError(ReturnValue, NULL, "Failed to drop privileges"); ReturnValue = 1; goto mainEnd; } assert(SwGetEffectiveUserId() == RealId); } RealId = SwGetRealGroupId(); EffectiveId = SwGetEffectiveGroupId(); if ((RealId != 0) && (RealId != EffectiveId)) { ReturnValue = SwSetRealGroupId(RealId); if (ReturnValue != 0) { SwPrintError(ReturnValue, NULL, "Failed to drop privileges"); ReturnValue = 1; goto mainEnd; } assert(SwGetEffectiveGroupId() == RealId); } } // // Run the command. // Result = SwissRunCommand(Command, Arguments, ArgumentCount, FALSE, TRUE, &ReturnValue); if (Result == FALSE) { ReturnValue = 1; goto mainEnd; } mainEnd: if (CommandName != NULL) { free(CommandName); } return ReturnValue; } PSWISS_COMMAND_ENTRY SwissFindCommand ( PSTR Command ) /*++ Routine Description: This routine searches for a command in the global command list. Arguments: Command - Supplies a pointer to the string of the command to look for. Return Value: Returns a pointer to the command entry on success. NULL if the command could not be found. --*/ { ULONG CommandIndex; // // Skip the dash which indicates a login process. // if (Command[0] == '-') { Command += 1; } CommandIndex = 0; while (SwissCommands[CommandIndex].CommandName != NULL) { if (strcmp(Command, SwissCommands[CommandIndex].CommandName) == 0) { return &(SwissCommands[CommandIndex]); } CommandIndex += 1; } return NULL; } BOOL SwissRunCommand ( PSWISS_COMMAND_ENTRY Command, CHAR **Arguments, ULONG ArgumentCount, BOOL SeparateProcess, BOOL Wait, PINT ReturnValue ) /*++ Routine Description: This routine runs a builtin command. Arguments: Command - Supplies the command entry to run. Arguments - Supplies an array of pointers to strings representing the arguments. ArgumentCount - Supplies the number of arguments on the command line. SeparateProcess - Supplies a boolean indicating if the command should be executed in a separate process space. Wait - Supplies a boolean indicating if this routine should not return until the command has completed. ReturnValue - Supplies a pointer where the return value of the command will be returned on success. Return Value: TRUE on success. FALSE if the command is not a recognized swiss builtin command. --*/ { pid_t Child; PSTR ExecutablePath; PSTR OriginalApplication; INT Result; pid_t WaitPid; // // If the command needs to be run in a separate process, there's one of two // options. Either fork and run the command directly, or execute the same // process with the new arguments. // if (SeparateProcess != FALSE) { if (SwForkSupported != 0) { Child = SwFork(); if (Child < 0) { return FALSE; // // If this is the child, run the command and then exit. // } else if (Child == 0) { OriginalApplication = SwSetCurrentApplicationName( Command->CommandName); *ReturnValue = Command->MainFunction(ArgumentCount, Arguments); exit(*ReturnValue); // // If this is the parent, potentially wait for the child. // } else { WaitPid = SwWaitPid(Child, 0, ReturnValue); if (WaitPid == -1) { return FALSE; } } // // Fork is not supported, so run the process again with different // arguments. // } else { ExecutablePath = SwGetExecutableName(); assert(ExecutablePath != NULL); Result = SwRunCommand(ExecutablePath, Arguments, ArgumentCount, !Wait, ReturnValue); if (Result != 0) { return FALSE; } } // // Just run it directly. // } else { // // Asynchronous but same-process execution is not supported. // assert(Wait != FALSE); OriginalApplication = SwSetCurrentApplicationName(Command->CommandName); *ReturnValue = Command->MainFunction(ArgumentCount, Arguments); SwSetCurrentApplicationName(OriginalApplication); } fflush(NULL); return TRUE; } // // --------------------------------------------------------- Internal Functions //