Shell to run under the MINUX operating system

User Documentation | C code

User Documentation

John Lynch
Project 1
CS 345-01
Spring 2000
Shell program:
This shell was made using step top-down stepwise refinement. This began with psuedo code:
shell to take user commands to run programs
refinement 1:
prompt user for input
read input from keyboard
if it is the parent
wait for child to exit

if it is the child
execute commands from keyboard

request next command (prompt)
Before attempting this program, I tried several mini programs to test operating system calls such as execve.
It took about a week or more until I was successful in getting execve to work with parameters from the keyboard.
I created programs that would call exec with out using fork. First just with a string of text hard coded -"/usr/bin/who". I then used string manipulation functions to put the user entered command such as "who" and added it to the default string "/usr/bin/". While this work and the complete string could be printed out, execve would ignore the string, even though when printed out, it was correct. My next attempt at getting execve to eork was to just simply pass the user entered command to it. So if who was entered the call would be execve("who", 0, 0). This worked but the shell would have to reside in usr/bin.
As in interesting note, the code :
strncat(programs, x, count+1);
(where programs is my default string "/usr/bin/who", and x is the user input).
produces different results in UNIX, and MINIX. in UNIX the first string - programs get the second string appended to it- so the first string gets changed. In MINIX the second string is appended to the first and the result overwrites the second string instead! This was responsible for much stress, I can tell you. My book on C agrees with the MINIX way. While this feature is not necasary, it allows me to put my shell in any directory, not just usr/bin.After getting exec to work, fork no real problems. I then worked on how the shell would deal with user who just hit return with no parameters, end allowed the user to exit using "exit". I declared values of "exit" and "" to be compared with the user input. For some reason, these strings were ignored, unless declared outside main.
Now I was ready to modify the program to take in arguments. I assumed that when a user typed a command with multiple arguments such as: ls -l sh* ,
they would be broken up into a linked list, just as arguments would be if you passed them to a program. I wrote a program called parser to break up a string into arguments to be passed as argv arguments to a program. I was able to get the program to break up the string, put each separate piece into a string made using malloc, and put a pointer to that string in an array that would be passed to argv. However it seems to "lose" the pointers, and the values they point to when the loop that walks through the input sting is exited.

 

Sample Program Run

Name:
jshell
Purpose:
This program a shell. It accepts commands, and acts as aninterface between a user and the computer
Usage:
this shell can accept single command inputs:
such as ls, who, finger, mail
Sample Inputs:
$ mail
No mail
$ ls
jshell jshell3 jshell3.c test1.c test6
$ pwd
/user/john
$ google
command: not found
$ exit
#

Back to top
/*********************************************************************************/
/*                                                                               */
/* Program Name: jshell                                                          */
/* Usage:                                                                        */
/* Purpose: provides a interface (shell) between user and computer               */
/* Author: John Lynch                                                            */
/* Date: 5 2, 2000                                                               */
/* comments: this shell can only take in single commands without parameters      */
/* 	                                                                             */
/*********************************************************************************/

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#define Extern extern
#include 
#include 
#include 

/* these values are ignored if declared inside main */
const char exit_value[] = "exit"; /* test user input against this string */
const char nothing[] = "";
const char programs[]="/usr/bin/"; /* to hold directory path of user programs */

int main(int argc, char *argv[]){
	int runvalue = 1;		/* value to allow user to exit shell */
	int exec_value = 0;		/* returned value of from execve */
	int pid = 0;			/* process id number returned from fork call */
	int status = 0;			/* value for exit status value from waitpid call*/
	int count = 0;			/* the length of a string */
	char x[25];				/* string read from keyboard */
	char temp[50];			/* string to hold temp pathname*/

	/* we will loop unless user enters "exit", to exit runvalue gets set to 0 */
	while (runvalue != 0){	
		/*clear out value of temp if it has been changed*/
		temp[0] = '\0';
		
		/*copies a constant pathname*/
		strcat(temp, programs);
		/*temp[9] = "\0";*/
		
		/*prompt user for input*/
		printf("\n$ ");

		/*read input from keyboard*/
		gets(x);
		count = strlen(x);
		strncat(temp, x, count+1);
		/*printf("\n %s", temp);*/
		/* test to see if user wants to leave shell*/
		if((strcmp(exit_value, x)) == 0){
			runvalue = 0;
		}else{
				/* test if user just hit return/enter*/
				if(!(strcmp(x, nothing))){
				/* do nothing, prompt will be redisplayed */

				}else{
						/* if call to fork returns 0, then it is in the child process*/
						pid = fork(); 
						if(pid == 0){
							/* This process' code will be replace by code of the */
							/* program specified by the user */
							exec_value = execve(temp, 0, 0);
							
							/* This next statement checks to see if execve ran correctly */
							/* if not, a negative value is returned*/
							if(exec_value < 0){
								printf("\ncommand: not found");
							}/*if*/
						}else{	
								/*this is code for the parent*/
								if(pid > 0){
									pid = waitpid(pid, &status, 0);
								}else{
										printf("\nfork failed");
									}/*else*/		
							  }/*else*/	
					  }/*else*/	
		     }/*end if -else*/	
		/*a test value*/				
	}/*while*/					
return(0);						
}/*main*/

Back to top