Question

I did already posted this question before, I did get the answer but i am not...

I did already posted this question before, I did get the answer but i am not satisfied with the answer

i did the code as a solution not the description as my solution, so i am reposting this question again. Please send me the code as my solution not the description

In this project, build a simple Unix shell. The shell is the heart of the command-line interface, and thus is central to the Unix/C programming environment. Mastering use of the shell is necessary to become proficient in this world; knowing how the shell itself is built is the focus of this project. There are three specific objectives to this assignment:

  • To further familiarize yourself with the Linux programming environment.
  • To learn how processes are created, destroyed, and managed.
  • To gain exposure to the necessary functionality in shells.
  • implement a command line interpreter (CLI) or, as it is more commonly known, a shell. The shell should operate in this basic way: when you type in a command (in response to its prompt), the shell creates a child process that executes the command you entered and then prompts for more user input when it has finished. The shells you implement will be similar to, but simpler than, the one you run every day in Unix. If you don't know what shell you are running, it's probably bash.

Program Specification:

Basic Shell: grsh

Your basic shell, called grsh (short for Golden Ram Shell), is basically an interactive loop: it repeatedly prints a prompt grsh>
(note the space after the greater-than sign), parses the input, executes the command specified on that line of input, and waits for the command to finish. This is repeated until the user types exit. The name of your source code should be grsh.c.

The shell can be invoked with either no arguments or a single argument; anything else is an error. Here is the no-argument way:

prompt> ./grsh
grsh>  

At this point, grsh is running, and ready to accept commands. Type away!

The mode above is called interactive mode, and allows the user to type commands directly. The shell also supports a batch mode, which instead reads input from a batch file and executes commands from therein. Here is how you run the shell with a batch file named batch.txt:

prompt> ./grsh batch.txt

One difference between batch and interactive modes: in interactive mode, a prompt is printed (grsh> ). In batch mode, no prompt should be printed. You should structure your shell such that it creates a process for each new command (the exception are built-in commands, discussed below). Your basic shell should be able to parse a command and run the program corresponding to the command. For example, if the user types ls -la /tmp, your shell should run the program /bin/ls with the given arguments -la and /tmp (how does the shell know to run /bin/ls? It's something called the shell path; more on this below).

Structure

Basic Shell

  • The shell is conceptually straightforward: it runs in a while loop, repeatedly asking for input to tell it what command to execute. It then executes that command. The loop continues indefinitely, until the user types the built-in command exit, at which point it exits. That's it!
  • For reading lines of input, you should use getline(). This allows you to obtain arbitrarily long input lines with ease.
  • Generally, the shell will be run in interactive mode, where the user types a command (one at a time) and the shell acts on it.
  • However, your shell will also support batch mode, in which the shell is given an input file of commands; in this case, the shell should not read user input (from stdin) but rather from this file to get the commands to execute.
  • In either mode, if you hit the end-of-file marker (EOF), you should call exit(0) and exit gracefully.
  • To parse the input line into constituent pieces, you might want to use strtok() (or, if doing nested tokenization, use strtok_r()). Read the man page (carefully) for more details.
  • To execute commands, look into fork(), exec(), and wait()/waitpid(). See the man pages for these functions, and also read the relevant book chapter for a brief overview.
  • You will note that there are a variety of commands in the exec family; for this project, you must use execv. You should not use the system() library function call to run a command. Remember that if execv() is successful, it will not return; if it does return, there was an error (e.g., the command does not exist). The most challenging part is getting the arguments correctly specified.

Paths

  • In our example above, the user typed ls but the shell knew to execute the program /bin/ls. How does your shell know this?
  • It turns out that the user must specify a path variable to describe the set of directories to search for executables; the set of directories that comprise the path are sometimes called the search path of the shell. The path variable contains the list of all directories to search, in order, when the user types a command.
  • Important: Note that the shell itself does not implement ls or other commands (except built-ins). All it does is find those executables in one of the directories specified by path and create a new process to run them.
  • To check if a particular file exists in a directory and is executable, consider the access() system call. For example, when the user types ls, and path is set to include both /bin and /usr/bin, try access("/bin/ls", X_OK). If that fails, try /usr/bin/ls. If that fails too, it is an error.
  • Your initial shell path should contain one directory: /bin
  • Note: Most shells allow you to specify a binary specifically without using a search path, using either absolute paths or relative paths. For example, a user could type the absolute path /bin/ls and execute the ls binary without a search path being needed. A user could also specify a relative path which starts with the current working directory and specifies the executable directly, e.g., ./main. In this project, you do not have to worry about these features.

Built-in Commands

  • Whenever your shell accepts a command, it should check whether the command is a built-in command or not. If it is, it should not be executed like other programs. Instead, your shell will invoke your implementation of the built-in command. For example, to implement the exit built-in command, you simply call exit(0); in your grsh source code, which then will exit the shell.
  • In this project, you should implement exit, cd, and path as built-in commands.
  • exit: When the user types exit, your shell should simply call the exit() system call with 0 as a parameter. It is an error to pass any arguments to exit.
  • cd: cd always take one argument (0 or >1 args should be signaled as an error). To change directories, use the chdir() system call with the argument supplied by the user; if chdir fails, that is also an error.
  • path: The path command takes 0 or more arguments, with each argument separated by a space from the others. A typical usage would be like this: grsh> path /bin /usr/bin, which would add /bin and /usr/bin to the search path of the shell. If the user sets path to be empty, then the shell should not be able to run any programs (except built-in commands). The path command always overwrites the old path with the newly specified path.

Redirection

  • Many times, a shell user prefers to send the output of a program to a file rather than to the screen. Usually, a shell provides this nice feature with the > character. Formally this is named as redirection of standard output. To make your shell users happy, your shell should also include this feature, but with a slight twist (explained below).
  • For example, if a user types ls -la /tmp > output, nothing should be printed on the screen. Instead, the standard output of the ls program should be rerouted to the file output. In addition, the standard error output of the program should be rerouted to the file output (the twist is that this is a little different than standard redirection). If the output file exists before you run your program, you should simply overwrite it (after truncating it).
  • The exact format of redirection is a command (and possibly some arguments) followed by the redirection symbol followed by a filename. Multiple redirection operators or multiple files to the right of the redirection sign are errors.
  • Note: don't worry about redirection for built-in commands (e.g., we will not test what happens when you type path /bin > file).

Parallel Commands

  • Your shell will also allow the user to launch parallel commands. This is accomplished with the ampersand operator as follows:
grsh> cmd1 & cmd2 args1 args2 & cmd3 args1
  • In this case, instead of running cmd1 and then waiting for it to finish, your shell should run cmd1, cmd2, and cmd3 (each with whatever arguments the user has passed to it) in parallel, before waiting for any of them to complete.
  • Then, after starting all such processes, you must make sure to use wait() (or waitpid()) to wait for them to complete. After all processes are done, return control to the user as usual (or, if in batch mode, move on to the next line).

Program Errors

  • The one and only error message. You should print this one and only error message whenever you encounter an error of any type:
  char error_message[30] = "An error has occurred\n";
  write(STDERR_FILENO, error_message, strlen(error_message)); 
  • The error message should be printed to stderr (standard error), as shown above.
  • After any most errors, your shell simply continue processing after printing the one and only error message. However, if the shell is invoked with more than one file, or if the shell is passed a bad batch file, it should exit by calling exit(1).
  • There is a difference between errors that your shell catches and those that the program catches. Your shell should catch all the syntax errors specified in this project page. If the syntax of the command looks perfect, you simply run the specified program. If there are any program-related errors (e.g., invalid arguments to ls when you run it, for example), the shell does not have to worry about that (rather, the program will print its own error messages and exit).

Miscellaneous Hints

  • Remember to get the basic functionality of your shell working before worrying about all of the error conditions and end cases. For example, first get a single command running (probably first a command with no arguments, such as ls).
  • Next, add built-in commands. Then, try working on redirection. Finally, think about parallel commands. Each of these requires a little more effort on parsing, but each should not be too hard to implement.
  • At some point, you should make sure your code is robust to white space of various kinds, including spaces ( ) and tabs (\t). In general, the user should be able to put variable amounts of white space before and after the commands, arguments, and various operators; however, the operators (redirection and parallel commands) do not require whitespace.
  • Check the return codes of all system calls from the very beginning of your work. This will often catch errors in how you are invoking these new system calls. It's also just good programming sense.

Programming References:

  • https://www.gnu.org/software/gnu-c-manual/gnu-c-manual.html
  • https://users.ece.utexas.edu/~adnan/c-refcard.pdf
  • https://www.cprogramming.com/tutorial/c-tutorial.html?inl=nv
  • https://www.tutorialspoint.com/cprogramming/

Homework Answers

Answer #1

// C Program to design a shell in Linux
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/wait.h>
#include<readline/readline.h>
#include<readline/history.h>

#define MAXCOM 1000 // max number of letters to be supported
#define MAXLIST 100 // max number of commands to be supported

// Clearing the shell using escape sequences
#define clear() printf("\033[H\033[J")

// Greeting shell during startup
void init_shell()
{
   clear();
   printf("\n\n\n\n******************"
       "************************");
   printf("\n\n\n\t****MY SHELL****");
   printf("\n\n\t-USE AT YOUR OWN RISK-");
   printf("\n\n\n\n*******************"
       "***********************");
   char* username = getenv("USER");
   printf("\n\n\nUSER is: @%s", username);
   printf("\n");
   sleep(1);
   clear();
}

// Function to take input
int takeInput(char* str)
{
   char* buf;

   buf = readline("\n>>> ");
   if (strlen(buf) != 0) {
       add_history(buf);
       strcpy(str, buf);
       return 0;
   } else {
       return 1;
   }
}

// Function to print Current Directory.
void printDir()
{
   char cwd[1024];
   getcwd(cwd, sizeof(cwd));
   printf("\nDir: %s", cwd);
}

// Function where the system command is executed
void execArgs(char** parsed)
{
   // Forking a child
   pid_t pid = fork();

   if (pid == -1) {
       printf("\nFailed forking child..");
       return;
   } else if (pid == 0) {
       if (execvp(parsed[0], parsed) < 0) {
           printf("\nCould not execute command..");
       }
       exit(0);
   } else {
       // waiting for child to terminate
       wait(NULL);
       return;
   }
}

// Function where the piped system commands is executed
void execArgsPiped(char** parsed, char** parsedpipe)
{
   // 0 is read end, 1 is write end
   int pipefd[2];
   pid_t p1, p2;

   if (pipe(pipefd) < 0) {
       printf("\nPipe could not be initialized");
       return;
   }
   p1 = fork();
   if (p1 < 0) {
       printf("\nCould not fork");
       return;
   }

   if (p1 == 0) {
       // Child 1 executing..
       // It only needs to write at the write end
       close(pipefd[0]);
       dup2(pipefd[1], STDOUT_FILENO);
       close(pipefd[1]);

       if (execvp(parsed[0], parsed) < 0) {
           printf("\nCould not execute command 1..");
           exit(0);
       }
   } else {
       // Parent executing
       p2 = fork();

       if (p2 < 0) {
           printf("\nCould not fork");
           return;
       }

       // Child 2 executing..
       // It only needs to read at the read end
       if (p2 == 0) {
           close(pipefd[1]);
           dup2(pipefd[0], STDIN_FILENO);
           close(pipefd[0]);
           if (execvp(parsedpipe[0], parsedpipe) < 0) {
               printf("\nCould not execute command 2..");
               exit(0);
           }
       } else {
           // parent executing, waiting for two children
           wait(NULL);
           wait(NULL);
       }
   }
}

// Help command builtin
void openHelp()
{
   puts("\n***WELCOME TO MY SHELL HELP***"
       "\nCopyright @ Suprotik Dey"
       "\n-Use the shell at your own risk..."
       "\nList of Commands supported:"
       "\n>cd"
       "\n>ls"
       "\n>exit"
       "\n>all other general commands available in UNIX shell"
       "\n>pipe handling"
       "\n>improper space handling");

   return;
}

// Function to execute builtin commands
int ownCmdHandler(char** parsed)
{
   int NoOfOwnCmds = 4, i, switchOwnArg = 0;
   char* ListOfOwnCmds[NoOfOwnCmds];
   char* username;

   ListOfOwnCmds[0] = "exit";
   ListOfOwnCmds[1] = "cd";
   ListOfOwnCmds[2] = "help";
   ListOfOwnCmds[3] = "hello";

   for (i = 0; i < NoOfOwnCmds; i++) {
       if (strcmp(parsed[0], ListOfOwnCmds[i]) == 0) {
           switchOwnArg = i + 1;
           break;
       }
   }

   switch (switchOwnArg) {
   case 1:
       printf("\nGoodbye\n");
       exit(0);
   case 2:
       chdir(parsed[1]);
       return 1;
   case 3:
       openHelp();
       return 1;
   case 4:
       username = getenv("USER");
       printf("\nHello %s.\nMind that this is "
           "not a place to play around."
           "\nUse help to know more..\n",
           username);
       return 1;
   default:
       break;
   }

   return 0;
}

// function for finding pipe
int parsePipe(char* str, char** strpiped)
{
   int i;
   for (i = 0; i < 2; i++) {
       strpiped[i] = strsep(&str, "|");
       if (strpiped[i] == NULL)
           break;
   }

   if (strpiped[1] == NULL)
       return 0; // returns zero if no pipe is found.
   else {
       return 1;
   }
}

// function for parsing command words
void parseSpace(char* str, char** parsed)
{
   int i;

   for (i = 0; i < MAXLIST; i++) {
       parsed[i] = strsep(&str, " ");

       if (parsed[i] == NULL)
           break;
       if (strlen(parsed[i]) == 0)
           i--;
   }
}

int processString(char* str, char** parsed, char** parsedpipe)
{

   char* strpiped[2];
   int piped = 0;

   piped = parsePipe(str, strpiped);

   if (piped) {
       parseSpace(strpiped[0], parsed);
       parseSpace(strpiped[1], parsedpipe);

   } else {

       parseSpace(str, parsed);
   }

   if (ownCmdHandler(parsed))
       return 0;
   else
       return 1 + piped;
}

int main()
{
   char inputString[MAXCOM], *parsedArgs[MAXLIST];
   char* parsedArgsPiped[MAXLIST];
   int execFlag = 0;
   init_shell();

   while (1) {
       // print shell line
       printDir();
       // take input
       if (takeInput(inputString))
           continue;
       // process
       execFlag = processString(inputString,
       parsedArgs, parsedArgsPiped);
       // execflag returns zero if there is no command
       // or it is a builtin command,
       // 1 if it is a simple command
       // 2 if it is including a pipe.

       // execute
       if (execFlag == 1)
           execArgs(parsedArgs);

       if (execFlag == 2)
           execArgsPiped(parsedArgs, parsedArgsPiped);
   }
   return 0;
}

Know the answer?
Your Answer:

Post as a guest

Your Name:

What's your source?

Earn Coins

Coins can be redeemed for fabulous gifts.

Not the answer you're looking for?
Ask your own homework help question
Similar Questions
PART 4: Learn about commands to navigate the directory tree structure: use the ls –ali /  ...
PART 4: Learn about commands to navigate the directory tree structure: use the ls –ali /   command to review the contents of the / directory Review the Linux Filesystem Standard (FSSTD) and document the following directories’ contents / ------- |--bin |--boot               |--dev               |--etc |--home               |--lib               |--mnt               |--opt               |--root               |--sbin               |--tmp               |--usr               |--var 3. use the cd etc  OR cd /etc  to change your default directory to the etc directory 4. use the...
Subject: Shell Scripting Practice A File: practice-script-a Create File practice-script-a that: 1. Accepts any number of...
Subject: Shell Scripting Practice A File: practice-script-a Create File practice-script-a that: 1. Accepts any number of userids on the command line 2. For each userid, display the userid, PID, CPU time, and current command for each process owned by that userid Your output should look similar to this example: practice-script-a doug.jones User: doug.jones PID: 8748 TIME: 00:00:00 COMMAND: /usr/lib/systemd/systemd --user User: doug.jones PID: 8749 TIME: 00:00:00 COMMAND: (sd-pam)    User: doug.jones PID: 8750 TIME: 00:00:00 COMMAND: sshd: doug.jones@pts/5 User: doug.jones...
Write a shell program named HELOO (this should be done linux) Your program should set permissions...
Write a shell program named HELOO (this should be done linux) Your program should set permissions for a file named A1testFile so that the current premissions remain, except execute permissions are REMOVED for everyone, including owner. Your must accomplish this with a single chmod command. A1testFile should be specified as a relative path name to a file in the current directory (the directory the user is in when they run your program). Your program should not display any error messages,...
The course is server management and I am using FREEBSD Create a folder off the root...
The course is server management and I am using FREEBSD Create a folder off the root that can be used for the backed-up files. You will need to use root to create this directory. Set the user and group for this directory to the account that you normally log in to. Create a cron process that will run every thirty minutes and copy the complete contents, including directories and their contents, to the backup location. The copy command should use...
NOTE: I just need the answer for question 5 and 6. I already knew the question...
NOTE: I just need the answer for question 5 and 6. I already knew the question 1-4, and wrote down all the requirements for clarification. In this assignment, you are required to write a Bash script, call it assignment2.sh. Your Bash script has to accept at least four input arguments, and must: 1) Print to the user a set of instructions explaining how the PATH variable can be used in Bash. 2) Save the manual of the 'awk' command in...
Project 10-3 In this hands-on project, you view the configuration of the System Log Daemon and...
Project 10-3 In this hands-on project, you view the configuration of the System Log Daemon and the logrotate utility on Ubuntu Server Linux. 1. Boot your Ubuntu Server Linux virtual machine. After your Linux system has been loaded, log into tty1 using the user name of root and the password of LNXrocks!. 2. At the command prompt, type ls –l /dev/log and press Enter. What is the file type? Which daemon on Ubuntu Server Linux uses this file and what...
5. Write a bash command that will display the name of every file on the Linux...
5. Write a bash command that will display the name of every file on the Linux system whose file contaent contains the string "doug.jones". The command must run in the background, must redirect standard error to /dev/null, and must redirect standard output to ~/out. 6. Write a bash command that will kill all of the even-numbered processes associated with your userid, and no other processes. 7. Write a bash command that will start the program /home/courses/140u-doug.jones/zombie_maker as a background process,...
You will write a program that loops until the user selects 0 to exit. In the...
You will write a program that loops until the user selects 0 to exit. In the loop the user interactively selects a menu choice to compress or decompress a file. There are three menu options: Option 0: allows the user to exit the program. Option 1: allows the user to compress the specified input file and store the result in an output file. Option 2: allows the user to decompress the specified input file and store the result in an...
Chapter 8: Searching, Extracting, and Archiving Data Exercise 8.a: Using grep, find, and regular expressions (Objective...
Chapter 8: Searching, Extracting, and Archiving Data Exercise 8.a: Using grep, find, and regular expressions (Objective 3.2) Linux Distribution: Fedora (non-root user & password needed) Desktop Environment: GNOME 3 1.   If you have not already done so, boot up your computer (or virtual machine) to start the Fedora Linux distribution. 2.   When the machine has booted up, access the tty2 virtual terminal by pressing Ctrl+Alt+F2. 3.   Log on to a regular user’s account by typing in the username at the...
1. By convention, how are configuration files separated from regular files? (NOTE: A practical effect of...
1. By convention, how are configuration files separated from regular files? (NOTE: A practical effect of the separation is that they are not displayed by the default version of the ls command)                         a. the prefix "rc" (rc.filename)               c. the extension .cfig                         b. a dot (.) at the beginning                    d. by having the SUID bit set 2. The IP address which is reserved for local loopback (equivalent to "localhost") is:                         a. 255.255.255.0                                   c. 192.168.70.1...