my_shell implemented by linuxc

problem

1. Implementation of CD command (cd, CD ~, CD -, CD path)

Because the CD command is built-in in shell command, it can not call the command of the system to implement cd, so it has to be handwritten.

The functions used are:

char * getcwd(char * buf, size_t size); //Get the current working directory
buf Path for Achievement,   size For the size taken
int chdir(const char * path); //change current directory
path To switch paths

Because we want to implement cd-, we need to save every path (cd_pathnametemp is used here).

                getcwd(cd_pathname,100);
                if((argcount == 1)  || strcmp(arg[1],"~") == 0)  
                {   
                        strcpy(cd_pathnametemp,cd_pathname);
                        my_chdir();   //Change Home Directory
                }   
                else if(strcmp(arg[1],"-") == 0)
                {   
                //      strcpy(cd_pathnametemp,cd_pathname);
                        chdir(cd_pathnametemp);
                        strcpy(cd_pathnametemp,cd_pathname);
                }   
                else
                {   
                        strcpy(cd_pathnametemp,cd_pathname);
                        chdir(arg[1]);    //Change the current working directory
                }   
                
2.ls output plus color

Because ls itself does not output colours, and our shell can output colours because we use aliases.

 alias ls='ls --color=auto'

So when you're dealing with ls, add the - - color=auto parameter to it.

3. Shielding ctrl + c

A good program is not easy to hang up, so if you do not block ctrl + c, a user ctrl + c will let the program hang up, which will be more embarrassing.
And shielding ctrl + c is not difficult

 signal(SIGINT, SIG_IGN);

SIGINT: When the user presses <ctrl+c>, the user terminal sends this signal to the running program initiated by the terminal, which defaults to terminating the process. When the process terminates, the program is terminated naturally.
SIG_IGN: Ignore the signal

4. Automatic Completion of Commands

After understanding, we found that readline library is really powerful, there are many very useful and convenient functions, the following history is also the function of this library.

Automatically completing this function requires the Readline library, if not the installation.

			sudo apt-get install libreadline6-dev 

Because readline is a dynamic link library, you need to add - L readline when compiling
To add a header file

#include<readline/readline.h>

        char * str = readline(" ");
5. Look up historical commands from top to bottom

header file

#include<readline/history.h>

Similarly, you need to add - L readLine when compiling

        add_history(str);

Every time you add the read command to add_history(), it's ok.

readline is not very familiar with you can do a little test to familiarize yourself with it.

6. Set my_shell as environment variable so that the program can run like bash and zsh shell.

This problem is solved by adding my_shell executable to / bin.

That's the main problem with this shell.
Other issues
_1. Support for input and output redirection (< > >>)
_2. Supporting Pipeline (|)
_3. Supporting background running procedures (&)
Look at the source code and you can do it. I won't go into it any more.

#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<string.h>
#include<sys/types.h>
#include<sys/wait.h>
#include<fcntl.h>
#include<sys/stat.h>
#include<dirent.h>
#include<pwd.h>
#include<readline/readline.h>
#include<readline/history.h>


#define normal  0 //General orders
#define out_redirect  1  //Output redirection
#define in_redirect 2 //input redirection
#define have_pipe   3  //There is a pipeline in the command
#define add_out_redirect 4
#define add_in_redirect 5
#define CLOSE "\001\033[0m\002"                 // Close all properties
char cd_pathnametemp[PATH_MAX] = "/home/tt";    //Default home directory
char cd_pathname[PATH_MAX];


void print_prompt();   //Print prompt  
void get_input(char *);   //Get the input command
void explain_input(char *,int *,char a[ ][256]);  //Resolve input commands
void do_cmd(int ,char a[ ][256]);  //Execution of orders
int find_command(char *);    //Find Executable Programs in Commands
void my_dir();//cd to home directory
int main(int argc,char **argv)
{
	 signal(SIGINT, SIG_IGN);
	int i;
	int argcount = 0; //Record the number of commands
	char arglist[100][256];   //Storage commands
	char **arg = NULL;
	char *buf = NULL;

	buf = (char *)malloc(256);
	if(buf == NULL)
	{
		perror("malloc failed\n");
		exit(-1);
	}


	while(1)
	{
		memset(buf,0,sizeof((buf)));
		print_prompt();   //Output command prompt
		get_input(buf);   //Get input
		//If the input command is exit or logout, exit the program
		if(strcmp("exit\n",buf) == 0 || strcmp("logout\n",buf) == 0)  break;
		if(strcmp(buf,"\n") == 0) continue;
		//Clean up arglist 
		for(i = 0;i < 100;i++)    arglist[i][0] = '\0';
		argcount = 0;//Number of commands 0


		explain_input(buf,&argcount,arglist);

		do_cmd(argcount,arglist);

	}
	if(buf == NULL)
	{
		free(buf);
		buf = NULL;
	}
	exit(0);

}
void my_chdir()
{
/*	int i,flag = 0,j;
	char name[30];
	char pathname[100];
	char pathnametemp[100];
	int uid;
	struct passwd *data;
	//uid_t uid;
	uid = getuid();  //Get uid 
	data = getpwuid(uid);
	getcwd(pathname,100);
	//Processing Path  
	int len = strlen(pathname);
	for(i = 0;i < len;i++)
	{
		if(pathname[i] == '/') flag++;
		if(flag == 3)  break;
	}
	for(j = 0;j < i;j++)
	{
		pathnametemp[j] = pathname[j];
	}

	pathnametemp[j] = '\0';
*/
	//printf( "%s\n",pathnametemp);
	//chdir(pathnametemp);
	chdir("/home/tt");
}

//Output command prompt
void print_prompt()
{
	int i,flag = 0,j;
	char name[30];
	char pathname[100];
	char pathnametemp[100];
	int uid;
	struct passwd *data;

	//uid_t uid;
	uid = getuid();
	data = getpwuid(uid);
	printf("\033[43;35m%s@\033[0m",data->pw_name);
	gethostname(name,30);
	printf( "\033[43;35m%s:\033[0m",name);
	getcwd(pathname,100);
	
	if(pathname[1] != 'h' && pathname[2] != 'o')
	{
		printf( "\033[35;43m%s\033[0m",pathname);
		return ;
	}

	//Processing Path  
	int len = strlen(pathname);
	for(i = 0;i < len;i++)
	{
		if(pathname[i] == '/') flag++;
		if(flag == 3)  break;
	}
	for(j = i;j < len;j++)
	{
		pathnametemp[j-i] = pathname[j];
	}
	pathnametemp[len-i] = '\0';
	strcpy(pathname,"~");
	strcat(pathname,pathnametemp);
	printf( "\033[35;43m%s\033[0m",pathname);

	//Print user prompts
	if(0 == uid)  printf( "\033[40;32m#\033[0m");
	else printf( "\033[40;32m$\033[0m");
	
	return ;
}

//Get user input  
void get_input(char *buf)
{
	int len = 0;
	int ch;

	char * str = readline(" "CLOSE);
	add_history(str);
	strcpy(buf,str);
	buf[strlen(buf)] = '\n';


	/*free(str);
	ch = getchar();
	while(len < 256 && ch != '\n')
	{
		//char * str = readline("");
		//free(str);
		buf[len++] = ch;
		ch = getchar( );
	}

	if(len == 256)
	{
		printf( "command is too long \n");
		exit(-1);  //If the command entered is too long, exit the program
	}

	buf[len] = '\n';
	len++;
	buf[len] = '\0';
	*/

}

//Resolve the command in buf, store the result in arglist, and the command ends with the carriage return sign'n'
//If you enter the command "ls-l/temp", then arglist [0], arglist [1], and arglist [2], respectively, are ls,-l,/temp.

void explain_input(char *buf,int *argcount,char arglist[100][256])
{
	char *p = buf;
	char *q = buf;
	int number = 0;

	while(1)
	{
		if(p[0] == '\n')  break;
		if(p[0] == ' ') p++;
		else
		{
			q = p;
			number = 0;  //Record the length of each command

			while(q[0] != ' ' && (q[0] != '\n'))
			{
				number++;
				q++;
			}

			strncpy(arglist[*argcount],p,number+1);  //* argcount is the number of commands
			arglist[*argcount][number] = '\0';
			*argcount = *argcount + 1;
			p = q;
		}
	}
}

//Execute the commands stored in arglist, argcount is the number of parameters of the commands to be executed  
void do_cmd(int argcount,char arglist[100][256])
{
	int flag = 0;
	int how = 0;   //Used only if the command contains <,>,| 
	int background = 0; //Indicates whether there is a background runtime indicator in the command
	int status;
	int i;
	int fd;
	char *arg[argcount + 1];
	char *argnext[argcount + 1];
	char *file;  //Save File Name
	pid_t pid;
	int cdflag = 0;

	//Take commands out 
	for(i = 0;i < argcount;i++)   arg[i] = (char *)arglist[i];
	//Colour ls
	if(strcmp(arg[0],"ls") == 0)
	{
		arg[argcount] = "--color=auto";
		arg[argcount + 1] = NULL;
	}
	else 	arg[argcount] = NULL;


	//cd   
	if(strcmp(arg[0],"cd") == 0)
	{
		getcwd(cd_pathname,100);
		if((argcount == 1)  || strcmp(arg[1],"~") == 0)  
		{
			strcpy(cd_pathnametemp,cd_pathname);
			my_chdir();   //Change Home Directory
		}
		else if(strcmp(arg[1],"-") == 0)
		{
		//	strcpy(cd_pathnametemp,cd_pathname);
			chdir(cd_pathnametemp);
			strcpy(cd_pathnametemp,cd_pathname);
		}
		else
		{
			strcpy(cd_pathnametemp,cd_pathname);
			chdir(arg[1]);    //Change the current working directory
		}


		return ;
	}

	//Check the command line for background runners
	for(i = 0;i < argcount; i++)
	{
		if(strncmp(arg[i],"&",1) == 0)
		{
			if(i == argcount -1)
			{
				background = 1;
				arg[argcount - 1] = NULL;
				break;
			}
			else
			{
				printf( "wrong command\n");
				return ;
			}
		}
	}

	//See if the command line has redirection and pipe characters
	for(i = 0; arg[i] != NULL;i++)
	{
		if(strcmp(arg[i],">") == 0)
		{
			flag++;
			how = out_redirect;
			if(arg[i+1] == NULL) flag++;
		}
		if(strcmp(arg[i],"<") == 0)
		{
			flag++;
			how = in_redirect;
			if(i == 0) flag++;
		}
		if(strcmp(arg[i],"|") == 0)
		{
			flag++;
			how = have_pipe;
			if(arg[i+1] == NULL) flag++;
			if(i == 0)  flag++;
		}
		if(strcmp(arg[i],">>") == 0)
		{
			flag++;
			how = add_out_redirect;
			if(arg[i+1] == NULL)  flag++;
		}
		if(strcmp(arg[i],"<<") == 0)
		{
			flag++;
			how = add_in_redirect;
			if(i == 0) flag++;
		}

	}
	//If flag = 1, then there are
	//If flag > 1, the format is incorrect and not supported
	if(flag > 1)
	{
		printf( "wrong command\n");
		return ;
	}

	//Command contains only one output substitution redirection symbol
	if(how == out_redirect)
	{
		for(i = 0;arg[i] != NULL;i++)
		{
			if(strcmp(arg[i],">") == 0)
			{
				file = arg[i+1];
				arg[i] = NULL;
			}
		}
	}

	//Command contains only one input replacement redirection	
	if(how == in_redirect)
	{
		for(i = 0;arg[i] != NULL;i++)
		{
			if(strcmp(arg[i],"<") == 0)
			{
				file = arg[i+1];
				arg[i] = NULL;
			}
		}
	}
	//Command contains only output tracing orientation
	if(how == add_out_redirect)
	{
		for(i = 0;arg[i] != NULL;i++)
		{
			if(strcmp(arg[i],">>") == 0)
			{
				file = arg[i+1];
				arg[i] = NULL;
				arg[i] = NULL;
			}
		}
	}

	//Commands contain only the orientation of input pursuit
	if(how == add_in_redirect)
	{
		for(i = 0;arg[i] != NULL;i++)
		{
			if(strcmp(arg[i],"<<") == 0)
			{
				file = arg[i+1];
				arg[i] = NULL;
			}
		}
	}
	//Command contains only one pipe symbol
	//Store the part behind the pipe symbol in argnext, and the part behind the pipe is an executable shell command
	if(how == have_pipe)
	{
		for(i = 0;arg[i] != NULL;i++)
		{
			if(strcmp(arg[i],"|") == 0)
			{
				arg[i] = NULL;
				int j;
				//Save the following command in argnext
				//j - i -1 is to start from scratch and add later
				for(j = i+1;arg[j] != NULL;j++)  argnext[j-i-1] = arg[j];
				argnext[j-i-1] = arg[j];
				break;
			}
		}
	}

	if((pid = fork()) < 0)
	{
		printf( "fork error\n");
		return ;
	}

 	    //0 General Order
	    //1 Output redirection
	    //2 Input redirection
	    //3 There is a pipeline in the command
	//pid is 0 to indicate that it is a sub-process and executes commands in the sub-process.
	switch(how)
	{
		case 0: //General orders
			if(pid == 0)
			{
				if(!(find_command(arg[0])))
				{
					printf( "%s : command not found\n",arg[0]);
					exit(0);
				}

				execvp(arg[0],arg);
				exit(0);
			}
			break;
		case 1:  //Replacement output redirection
			if(pid == 0)
			{
				if(!(find_command(arg[0])))
				{
					printf( "%s : command not found\n",arg[0]);
					exit(0);
				}
				fd = open(file,O_RDWR | O_CREAT | O_TRUNC,0644);  //Readable and Writable. If the file does not exist, it is automatically created and the length of the file is cleared to 0.
				dup2(fd,1);  //Assignment File Descriptor Change the original File Descriptor to 1 Standard Input
				execvp(arg[0],arg);
				exit(0);
			}
			break;
		case 2:  //Replacement input redirection
			if(pid == 0)
			{
				if(!(find_command(arg[0])))
				{
					printf( "%s : command not found\n",arg[0]);
					exit(0);
				}
				fd = open(file,O_RDONLY);
				dup2(fd,0);
				execvp(arg[0],arg);
				exit(0);
			}
			break;
		case 3:  //Containing pipes
			if(pid == 0)
			{
				int pid2;
				int status2;
				int fd2;

				if((pid2 = fork()) < 0)
				{
					printf( "fork2 error\n");
					return ;
				}
				else if(pid2 == 0)
				{
					if(!(find_command(arg[0])))
					{
						printf( "%s : command not found\n",arg[0]);
						exit(0);
					}

					fd2 = open("/tmp/youdonotknowfile",O_WRONLY | O_CREAT | O_TRUNC,0644);
					dup2(fd2,1);
					execvp(arg[0],arg);
					exit(0);
				}
				if(waitpid(pid2,&status2,0) == -1)  printf( "wait for child procrss error\n");

				if(!(find_command(argnext[0])))
				{
					printf( "%s : command not found\n",argnext[0]);
					exit(0);
				}

				fd2 = open("/tmp/youdonotknowfile",O_RDONLY);
				dup2(fd2,0);
				execvp(argnext[0],argnext);

				if(remove( "/tmp/youdonotknowfile"))  printf( "remove error\n");
				exit(0);

			}
			break;
		case 4://Additional output redirection
			if(pid == 0)
			{
				if(!(find_command(arg[0])))
				{
					printf( "%s : command not found\n",arg[0]);
					exit(0);
				}

				fd = open(file,O_RDWR | O_CREAT | O_APPEND);
				dup2(fd,1);
				execvp(arg[0],arg);
				exit(0);
			} break;
		default : break;
	}

	//If the command has &, it means that the background runs, and the parent process returns directly without waiting for the child process to finish.
	if(background == 1)
	{
		printf( "[process id %d ]\n",pid);
		return ;
	}

	//The parent process waits for the child process to finish
	if(waitpid(pid,&status,0) == -1)  printf( "wait for child process error\n");

}

//Find Executable Files in Commands
int find_command(char *command)
{
	DIR* dp;
	struct dirent * dirp;
	char * path[] = {"./","/bin","/usr/bin",NULL};

	//Programs in the current directory can be run, such as the command ". / fork" can be interpreted and executed correctly. 
	if(strncmp(command,"./",2) == 0)   command = command + 2;

	//Find the program to execute in the current directory / bin, / usr / bin directory, respectively
	//
	
	int i = 0;
	while(path[i] != NULL)
	{
		if((dp = opendir(path[i])) == NULL)   printf( "can not open /bin\n");

		while((dirp = readdir(dp)) != NULL)
		{
			if(strcmp(dirp->d_name,command) == 0)
			{
				closedir(dp);
				return 1;
			}
		}

		closedir(dp);
		i++;
	}

	return 0;
}


Keywords: shell sudo

Added by LDM2009 on Tue, 23 Jul 2019 09:03:56 +0300