Created
November 13, 2017 03:35
-
-
Save SibTiger/12f3db66515d34af114d7247f9309a24 to your computer and use it in GitHub Desktop.
Operating Systems: Simulation of a Shell Environment [without Pipes]
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// ============================================================================= | |
// ----------------------------------------------------------------------------- | |
// ============================================================================= | |
// Programmer: Nicholas Gautier | |
// Class: CS3513; Operating Systems | |
// Assignment #: 1 | |
// Due Date: 19.September.2017 | |
// Instructor: Dr. Zhao | |
// Description: This program allows the user to interact with a basic shell | |
// environment and executes external commands at the user's | |
// request. This shell environment is very basic and offers | |
// very limited functionality. | |
// NOTES: This program was originally developed under the Windows | |
// environment, however due to limitations - this was | |
// expeditiously converted to work with *nix environment. | |
// The reason for this change-over was how CreateProcess() and | |
// fork() works. fork() copies the program and furthers the | |
// execution as if nothing really changed (visually), | |
// CreateProcess() however starts a brand new process without | |
// copying the main instance source and furthering the execution. | |
// Hacks can be made to emulate this behavior, but this may not | |
// be suitable for class material. Thus, we will have to shift | |
// to work with Linux and expeditiously get this program to work | |
// given that it took several days already.... | |
// Credits: | |
// Dr. Zhao for resolving how the PATH data is stored internally within the | |
// program. | |
// Kyle Ressel for helping to resolve how PATH data is stored by the | |
// further revisements of improvements, also helping to get the | |
// execl() statement to work dynamically. | |
// Return Codes: | |
// 0 = Successful | |
// 1 = %PATH% Environment System Variable not obtained | |
// ============================================================================= | |
// ----------------------------------------------------------------------------- | |
// ============================================================================= | |
#pragma region Online Resources - HELP ME | |
// printf special formatting: | |
// https://www.codingunit.com/printf-format-specifiers-format-conversions-and-formatted-output | |
// Passing and returning char's: | |
// https://www.quora.com/How-can-I-pass-a-string-as-an-argument-to-a-function-and-return-a-string-in-C | |
// strtok example: | |
// http://www.cplusplus.com/forum/general/13058/ | |
// strtok_s [Microsoft's thread-safe version]: | |
// https://msdn.microsoft.com/en-us/library/ftsafwz3.aspx | |
// Time features | |
// https://www.tutorialspoint.com/c_standard_library/time_h.htm | |
// fgets [user input] | |
// https://www.tutorialspoint.com/c_standard_library/c_function_fgets.htm | |
// _stricmp() [Windows] | |
// https://stackoverflow.com/questions/17899993/compare-strings-after-converting-to-lower-case-in-c | |
// https://msdn.microsoft.com/en-us/library/k59z8dwe(v=vs.80).aspx | |
// Page 118 - OS book | |
#pragma endregion | |
#pragma region Libraries | |
#include <stdio.h> | |
#include <stdlib.h> // Holds malloc (if we ever need it), system(), | |
#include <stddef.h> // Holds are NULLPTR | |
#include <string.h> // for functions: strtok(), strtok_s() - MICROSOFT's 'THREAD SAFE' version, strcmp() | |
#include <time.h> // Used for the tm struct functionality; for prompt | |
#include <ctype.h> // used for tolower() | |
#include <unistd.h> // Used for fork() and pid_t | |
#include <stdbool.h> // Because I was spoiled in C[++ | #]; give me bool datatypes! | |
#pragma endregion | |
#pragma region Macros | |
#define _NAME_ "Sea Shell" // Program name | |
#define _VERSION_ "1.0" // Program version | |
#define _INPUT_MAX_LENGTH_ 255 // Entire input limit | |
#pragma endregion | |
#pragma region Function Prototypes | |
char *FetchEnvironmentVariable(char *[]); // Retrieve the System Environment variables | |
void BootUpScreen(); // Display bootup message | |
void WelcomeScreen(); // Display the Welcome Screen | |
void ExitingScreen(); // Display the terminating message | |
void PromptMSG(); // Display the Prompt message before STDIN is provided. | |
char *PromptSTDIN(); // Allow the end-user to type a command into the shell | |
#pragma endregion | |
// Main - Entry Point | |
// ------- | |
// This function is the starting or entry-point of the program. | |
// Help for Main Arguments: https://stackoverflow.com/a/4176337 | |
int main(int argc, char **argv) | |
{ | |
// Declarations and Initializations | |
char *pathRaw = FetchEnvironmentVariable("PATH"); // Fetch %PATH% (or $PATH) and store it directly to the pointer of chars | |
char *pathToken = NULL; // Used for parsing through the PATH system environment variable | |
char storedToken[1000][100]; // Stores the entire tokens in an array for easy access. | |
// This will allow the program to access each directory easily when invoking | |
char *execCMD; // This variable will allow the end-user to execute the specific command. | |
char *execArgs; // This allows the user to pass arguments to the array of char's | |
// in which allows the user to issue arguments to perform special | |
// operations that could not be normally performed without. | |
// the execl() or other means (or actions). | |
char tokenDelim[] = ":;"; // Specifies the delimiters for the tokens | |
// Delim: : = Linux's $PATH, ; = Windows's %PATH% or $PATH | |
// This will cover both fields as an 'or'. Thus : OR ; but never both | |
int parsingCounter = 0; // This will allow the program to pass through iterations and record storing. | |
// Record storing will be for caching the value held within this variable to | |
// another variable, such as 'parsingCounter_Result'. | |
int parsingCounter_Result = 0; // This variable can help to determine the maximum indexes used in the | |
// variable 'parsingCounter'. | |
char *userSTDIN; // This pointer of char's variable will be used for capturing user input. | |
char path[_INPUT_MAX_LENGTH_]; // This variable is dedicated to the execl() for capturing the path | |
// and the binary. | |
pid_t processID; // Retains the process id of the child process that is duplicated. | |
// ---------- | |
// Provide the user with a bootup message | |
BootUpScreen(); | |
// Check to make sure that pathRaw contains data we need; else - terminate this program abruptly. | |
if (!strcmp(pathRaw, "ERROR")) | |
{ | |
printf("<!> OPERATION HALTED <!>\n"); | |
printf("===============================\n\n"); | |
printf("UNABLE TO SUCCESSFULLY OBTAIN SYSTEM ENVIRONMENT %PATH%!\nPlease complain to the developer of this program to make better software.\n\n"); | |
return 1; // Failure to obtain %PATH% - KILL PROGRAM | |
} // if: %PATH% not retrieved successfully | |
else | |
{ | |
pathToken = strtok(pathRaw, tokenDelim); // Immediately capture the first token and have strtok() cache the initial string. | |
// NOTE: This is NOT thread safe and stores data in a static pointer. | |
while(pathToken != NULL) | |
{ | |
strcpy(storedToken[parsingCounter], pathToken); // Duplicate the token to an array for access for later | |
// As required for this assignment, provide output in the following way: | |
// Index - of the array we are going to store into. | |
// Path - Store data within based on the index of the array | |
// Token - Token that has been generated from the PATH system environment. | |
printf("Path Index: %d\n\t\tPath Directory: %s\n\t\tGenerated Token: %s\n", parsingCounter, storedToken[parsingCounter], pathToken); | |
parsingCounter++; // Update the parsingCounter to the next index. | |
pathToken = strtok(NULL, tokenDelim); // Shift to the next token | |
} // Scan through the %PATH% and generate tokens | |
printf("\n"); // Visual purposes only | |
// Save the final index for later | |
parsingCounter_Result = parsingCounter; | |
} // else | |
// Provide the user with the basic information about this shell | |
WelcomeScreen(); | |
// Never-Ending loop | |
do | |
{ | |
//Provide the prompt message | |
PromptMSG(); | |
// Get user-feedback | |
userSTDIN = PromptSTDIN(); | |
// As requested for this assignment, provide what the user entered into the program | |
printf("\nRequested Command: %s\n\n", userSTDIN); | |
// Terminate the program by request? | |
if ((strcasecmp(userSTDIN, "exit") == 0) || | |
(strcasecmp(userSTDIN, "quit") == 0)) | |
break; // Leave the loop and start the termination process | |
else | |
{ | |
processID = fork(); // Create a new child process | |
if (processID < 0) | |
{ // Were we able to fork a new process? Check child state | |
fprintf(stderr, "Unable To Generate Child; Fork Failure!"); | |
return 2; | |
} // if: Fork() status | |
else if (processID == 0) | |
{ // Child Process: | |
parsingCounter = 0; // Reset the parser counter as we will use this to access | |
// the path array index. | |
// Fetch the initial command that the user requested for | |
execCMD = strtok(userSTDIN, " "); | |
// Try to retrieve parameters issued by the end-user - - if any was provided | |
execArgs = strtok(NULL, ""); | |
do | |
{ | |
strcpy(path, storedToken[parsingCounter]); // Capture path into a cache variable | |
strcat(path, "/"); // Add the UNIX filesystem '/' directory char. | |
strcat(path, userSTDIN); // Add the user's requested command to the cache variable | |
// ---- | |
printf("%s\n", path); // Display the path before execution | |
// ---- | |
// Execute the command as requested | |
execl (path, execCMD, execArgs, (char *)NULL); | |
parsingCounter++; // Update to the next index | |
} while (parsingCounter <= parsingCounter_Result); // Loop until we reach the max index limit | |
printf("Invalid External or Internal command!\n"); | |
exit(0); | |
} // if: Child Process | |
else // Parent process; wait for child (fork()) to finish process. | |
wait(NULL); | |
} // else: Run extCMD | |
} while (1); | |
// Terminating process | |
ExitingScreen(); | |
// Terminate the software | |
return 0; | |
} // main() | |
// Fetch Environment Variable | |
// This function is designed to retrieve a variable from the host system | |
// and return the data as requested. | |
// ------- | |
// Parameters: | |
// envVar[] - Char | |
// This string will state what environment variable is being requested. | |
// ------- | |
// Output: | |
// String (char[]) | |
// Returns the results from the system environment variable as a string (or char[]). | |
// If failure, the return will be 'ERROR'. Which in this case, the program is unable to work. | |
// ------- | |
char *FetchEnvironmentVariable(char *envVar[]) | |
{ | |
// Declaration and Initializations | |
// ---------------- | |
char *buffer = NULL; // Capture the data into the buffer-pointer | |
size_t sizeNum = 0; // Retains the number of elements | |
// ---------------- | |
buffer = getenv(envVar); // Capture the system environment variable data | |
if (buffer != NULL) // Check to see if the data is valid (failed or passed) | |
return buffer; | |
else | |
return "ERROR"; | |
} // FetchPathEnvironment() | |
// Welcome Screen | |
// ------- | |
// This function will provide basic information to the user about the program. Adjacent to the Command Shell (and PowerShell) | |
void WelcomeScreen() | |
{ | |
printf("%s [version: %s]\nOn the C shore\n\n", _NAME_, _VERSION_); | |
} // WelcomeScreen() | |
// Boot Up Screen | |
// ------- | |
// This function will merely display the bootup message on the terminal screen. | |
void BootUpScreen() | |
{ | |
printf("\nStarting up %s. . .\n\n", _NAME_); | |
} // BootUpScreen() | |
// Exiting Screen | |
// ------- | |
// This function will display a message in regards to the shell terminating | |
void ExitingScreen() | |
{ | |
printf("\n\n%s is closing. . .\n", _NAME_); | |
} // ExitingScreen() | |
// Prompt Message | |
// ------- | |
// With this function, this can provide the user basic information that would matter most. | |
void PromptMSG() | |
{ | |
// Declarations and Initializations | |
struct tm * timeDateObj; // Create an instance of the 'tm' object | |
char timeDateBuffer[100]; // Declare a buffer for the time\date formatting | |
char *programWorkingDirectory; // Create a new variable for holding the program's WD | |
// Gather information as needed | |
// =========================== | |
// Program's Working Directory | |
programWorkingDirectory = getcwd(NULL, 0); // Cache the program's working directory. | |
if (programWorkingDirectory == NULL) // Did 'getcwd()' failed? | |
programWorkingDirectory = "<ERROR_GET_WD_FAILED>"; // Provide an error message, but DON'T kill the program. | |
// =========================== | |
// Date and Time | |
// I wasted over an hour on this, if something breaks - just nuke this feature. | |
// https://stackoverflow.com/questions/23024862/localtime-s-in-c-using-microsoft-visual-studio-2013 | |
// https://stackoverflow.com/questions/13658756/example-of-tm-use | |
// http://www.cplusplus.com/reference/ctime/strftime/ | |
// https://www.tutorialspoint.com/c_standard_library/time_h.htm <-- USING TM DIRECTLY VIA OBJECT | |
time_t rawTime; // We will need this var. for the next step | |
time(&rawTime); // Capture current date in seconds as since 1970's | |
timeDateObj = localtime(&rawTime); // Populate the time\date data in 'tm' | |
strftime(timeDateBuffer, 100, "[%a.%e.%b.%Y %H.%M:%S]", timeDateObj); // Perform special formatting to time and date scheme. | |
// =========================== | |
// Display the Prompt Message: Date and Time | |
printf("%s ", timeDateBuffer); | |
// Display the Prompt Message: WD | |
printf("[%s]\n", programWorkingDirectory); | |
} // PromptMSG() | |
// Prompt STDIN [keyboard] | |
// ------- | |
// This function allows the end-user to type a command into the shell for evaluation and execution. | |
// ------- | |
// Output | |
// char* | |
// Command requested by the end user | |
char *PromptSTDIN() | |
{ | |
char *interactionSTDIN = (char *) malloc(sizeof(char) * _INPUT_MAX_LENGTH_); // Used for storing user input | |
printf(">>>>> "); // This should help indicate that we are waiting for user interaction | |
fgets(interactionSTDIN, _INPUT_MAX_LENGTH_, stdin); // Capture user input | |
interactionSTDIN[strlen(interactionSTDIN) -1] = '\0'; | |
// Report back the requested command | |
return interactionSTDIN; | |
} // PromptSTDIN() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment