Linux project -- imitating bash program

function

  • Print prompt information: [current user name @ hostname current working directory name] identifier ($#)
  • Get user's command: command name option parameter
  • To classify commands:
    1) Built in command: exit cd
    2) External command: a separate program. The subprocess created by bash runs the executable file under / bin
  • Create subprocess: the subprocess replaces the executable corresponding to the command entered by the user

Source code

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <assert.h>

#include <sys/types.h>
#include <sys/utsname.h>
#include <pwd.h>
#include <signal.h>

#define NUM 20
#define LENGTH 128

char OLDPWD[LENGTH] = { 0 }; // Save last working path

//Output prompt
void PrintfTag()
{
	//[username @ hostname current working directory] symbol
	
	char flag = '$';    //Ordinary users
	if(getuid() == 0)   //root users
	{
		flag = '#';
	} 

	//getuid returns the uid of the current user
	//getpwuid returns user information according to uid (return type is structure pointer)
	struct passwd *pw = getpwuid(getuid());
	assert(pw != NULL);

	//Get host information
	struct utsname hostname;
	uname(&hostname);

	//getcwd gets the absolute path of the current working directory
	char path[LENGTH] = { 0 };
	getcwd(path, LENGTH - 1);

	char *dirName = NULL;
	if(strcmp(path, pw->pw_dir) == 0) //Determine whether the current path is home directory
	{
		dirName = "~";
	}
	else if(strlen(path) == 1)        //Determine whether the path is the root directory
	{
		dirName = "/";
	}
	else
	{
		dirName = path + strlen(path);
		while(*dirName != '/')
		{
			dirName--;
		}
		dirName++;
	}

	printf("[%s@%s %s]%c", pw->pw_name, hostname.nodename, dirName, flag);
}

//Cut command string by space
void CutCommandString(char *cmd, char *Argv[])
{
	// cmd: "ls -l /bin"
	// Each pointer of the Argv array points to a cut string
	int index = 0;
	char *p = strtok(cmd, " ");
	while(p != NULL)
	{
		Argv[index++] = p;
		p = strtok(NULL, " ");
	}

	//Verify the cut is successful
	/*
	int i = 0;
	for(; i < index; i++)
	{
		printf("%s\n", Argv[i]);
	}
	*/
}

//Implement cd command
void AchieveCd(char *path)
{
	/*
	 * cd Path -- > int chdir (const char * path); success returns 1, failure returns 0
	 * cd / cd ~
	 * cd -
	*/

	// Get the absolute path of the current working directory
	char nowPath[LENGTH] = { 0 };
	getcwd(nowPath, LENGTH - 1);

	if(path == NULL || strncmp(path, "~", 1) == 0)  //Switch to home directory or directory under home directory
	{
		struct passwd *pw = getpwuid(getuid());
		path = strcat(pw->pw_dir, path + 1);
	}
	
	if(strncmp(path, "-", 1) == 0) //Switch to the last directory
	{
		if(strlen(OLDPWD) == 0)
		{
			printf("mybush: OLDPWD no set\n");
			return;
		}
		path = OLDPWD;
	}

	//Toggle directory
	if(-1 == chdir(path))
	{
		perror(path);
		return;
	}

	//Save the path before switching to OLDPWD
	memset(OLDPWD, 0, LENGTH);
	strcpy(OLDPWD, nowPath);

	//Test code
	/*
	memset(nowPath, 0, LENGTH);
	getcwd(nowPath, LENRTH - 1);
	printf("%s\n", nowPath);
	*/
}

//Sort commands and process built-in commands
int DealBuiltInCmd(char *Argv[])
{
	// Argv[0]: user entered command
	if(strncmp(Argv[0], "exit", 4) == 0)
	{
		exit(0);  //End mybush
	}
	if(strncmp(Argv[0], "cd", 2) == 0)
	{
		AchieveCd(Argv[1]); // Switch to the specified working directory
		return 1;
	}

	return 0;
}

//Signal processing function
void SignBack(int sign)
{
	wait(NULL);
}

//Create child process
void CreatChild(char *Argv[])
{
	// SIGCHLD will be sent to the parent process after the child process ends
	signal(SIGCHLD, SignBack);

	pid_t pid = fork();
	assert(pid != -1);

	if(pid == 0)
	{
		//Search for executable files in / bin by default
		char path[LENGTH] = "/bin/";     //Must be an absolute path
		if(strstr(Argv[0], "/") != NULL) //Determine whether the command given by the user has a path
		{
			memset(path, 0, LENGTH);
			strcpy(path, Argv[0]);
		}
		else
		{
			strcat(path, Argv[0]);
		}

		execv(path, Argv);
		perror(path);
		exit(0);  //Prevent sub process exec replacement failure
	}
	else // Background operation
	{
		int i = NUM - 1;
		for(; i >= 0; i--)
		{
			if(Argv[i] != NULL)
			{
				break;
			}
		}
		if(strcmp(Argv[i], "&") != 0)	
		{
			wait(NULL);
		}
	}
}


int main()
{
	while(1)
	{
		//1. Output prompt information
		PrintfTag();

		//2. Get user input command string
		char cmd[LENGTH] = { 0 };
		fgets(cmd, LENGTH - 1, stdin);  //Writing string to cmd from standard input encountered end of carriage return and also got carriage return
		cmd[strlen(cmd) - 1] = 0;       //Set carriage return to 0

		//3. The cutting of string separates command from option and parameter according to space
		char *Argv[NUM] = { 0 };        //Pointer array to store the separated strings
		CutCommandString(cmd, Argv);

		//4. Classification command 1. CD exit 2. External
		if(DealBuiltInCmd(Argv))        //If the DealBuiltInCmd function processes built-in commands, the program returns the first sentence of while, otherwise it continues
		{
			continue;
		}
		
		//5. Create a child process. The child process replaces the external command. The parent process wait s for the child process to execute the command
		CreatChild(Argv);
	}
}
Published 21 original articles, won praise 0, visited 332
Private letter follow

Posted by garfx on Sat, 07 Mar 2020 08:41:52 -0800