I am self-implementing a linux shell. I am just about done but I need to sort out an issue with zombie processes. In this program, by adding "&" to a command, you can specify that the command to be run should be run in the background.
For example, sleep 30 & will execute the sleep function for 30 seconds but immediately allow the user to enter another command without waiting.
My problem is that in my current implementation, the background process starts but I don't know how to have it inform the program when it has finished execution.
I need to know when it has finished execution because I have a built-in function called "jobs". This function will print out the list of programs currently running.
Ideally, if I execute "sleep 30 &" and then immediately run "jobs" before sleep has finished, it should show something like this:
[1] [PROCESS ID HERE] sleep 30 &
However, if 30 seconds have elapsed and sleep has finished execution, I want it to show that no processes are running. Right now, if I run "sleep 30 &", it shows up as a zombie process ( in the ps -e command). I don't want this. How do I fix this?
Here is my code:
#include <iostream>
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
#include <signal.h>
#include <stdio.h>
#include <string.h>
#include <sys/wait.h>
#include <stack>
#include <vector>
#include <ctype.h>
#include <sstream>
using namespace std;
#define MAX_INPUT_STRING_SIZE 80
//This function executes the command that was entered
int executeCommand(char ** commandArguments, int mode, char **supplementalPointer);
//This function parses the command that was entered
int parseCommand(char* inputString, char * commandArguments[], char** supplementalPointer,
int *modePointer);
//A bit of string handling
void chop(char * sourcePointer);
//print the history of commands
void printHistory();
//print the list of background processes running
void printJobs();
//Return the nth command in the history of commands
string returnNth(int nth);
//Modes
#define NORMAL 00
#define OUTPUT_REDIRECT 11
#define INPUT_REDIRECT 22
#define PIPELINE 33
#define BACKGROUND 44
//This struct defines a job that is stored in the list of background processes
struct job
{
pid_t processID;
string command;
job(pid_t pid, string com)
{
processID = pid;
command = com;
}
};
//The history of commands entered
stack<string> commandHistory;
//A place to store commands that have been popped before that are added back in
stack<string> popHistory;
//The list of background processes currently running
vector<struct job> jobList;
int main(int argc, char *argv[])
{
int i, mode = NORMAL, numOfArguments;
size_t length = MAX_INPUT_STRING_SIZE;
char *cpt, *inputString, *commandArguments[MAX_INPUT_STRING_SIZE], *supplement = NULL;
//The string that holds the command that the user entered
inputString = (char*)malloc(sizeof(char)*MAX_INPUT_STRING_SIZE);
char currentDirectory[100];
//continue until "quit" has been entered
while(strcmp(inputString, "quit\n") != 0)
{
mode = NORMAL;
//get and print the current working directory
getcwd(currentDirectory, 100);
cout<<"%"<<currentDirectory<<"% ";
//get the command from the user
getline(&inputString, &length, stdin);
executeFromHistory:
string inputStringS(inputString);
//push the command to the history stack
commandHistory.push(inputStringS);
//quit the program
if(inputStringS == "quit\n")
{
continue;
}
//print the history
else if(inputStringS == "history\n")
{
printHistory();
continue;
}
//print the list of jobs
else if(inputStringS == "jobs\n")
{
printJobs();
continue;
}
else if(inputStringS[0] == '!')
{
commandHistory.pop();
//execute the most recent command
if(inputStringS[1] == '!')
{
if(commandHistory.empty())
{
cout<<"No commands in history"<<endl;
}
else
{
inputStringS = commandHistory.top();
strcpy(inputString, inputStringS.c_str());
goto executeFromHistory;
}
}
//Execute the nth command in history (specified by user)
else if(isdigit(inputString[1]))
{
int nth;
inputStringS = inputStringS.substr(1);
istringstream iss(inputStringS);
iss >> nth;
if(commandHistory.size() < nth || nth < 1)
{
cout<<"No such command could be found"<<endl;
}
else
{
inputStringS = returnNth(nth);
strcpy(inputString, inputStringS.c_str());
goto executeFromHistory;
}
}
else
{
continue;
}
}
//Parse a command and execute
else
{
numOfArguments = parseCommand(inputString, commandArguments, &supplement, &mode);
//Change directory
if(strcmp(*commandArguments, "cd") == 0)
{
chdir(commandArguments[1]);
}
else
//Execute
{
int returnstatus = executeCommand(commandArguments, mode, &supplement);
if(returnstatus == -1)
{
cout<<"Execution failed"<<endl;
continue;
}
}
;
}
}
return 0;
}
int parseCommand(char * inputString, char *commandArguments[], char **supplementalPointer,
int *modePointer)
{
int numOfArguments = 0;
bool terminate = false;
char* sourcePointer = inputString;
//Continue until the character we are on is NULL and terminate flag has been tripped
while(*sourcePointer != '\0' && !terminate)
{
//New argument
*commandArguments = sourcePointer;
numOfArguments++;
while(*sourcePointer != ' ' && *sourcePointer != '\t' && *sourcePointer != '\0'
&& *sourcePointer != '\n' && !terminate)
{
//Handle various special characters
switch(*sourcePointer)
{
case '&':
*modePointer = BACKGROUND;
*commandArguments = '\0';
*sourcePointer++;
while(*sourcePointer == ' ' || *sourcePointer == '\t')
{
sourcePointer++;
}
break;
case '>':
*modePointer = OUTPUT_REDIRECT;
*commandArguments = '\0';
*sourcePointer++;
while(*sourcePointer == ' ' || *sourcePointer == '\t')
{
sourcePointer++;
}
*supplementalPointer = sourcePointer;
chop(*supplementalPointer);
terminate = true;
break;
case '<':
*modePointer = INPUT_REDIRECT;
*commandArguments = '\0';
sourcePointer++;
while(*sourcePointer == ' ' || *sourcePointer == '\t')
{
sourcePointer++;
}
*supplementalPointer = sourcePointer;
chop(*supplementalPointer);
terminate = true;
break;
case '|':
*modePointer = PIPELINE;
*commandArguments = '\0';
sourcePointer++;
while(*sourcePointer == ' ' || *sourcePointer == '\t')
{
sourcePointer++;
}
*supplementalPointer = sourcePointer;
terminate = true;
break;
}
sourcePointer++;
}
while((*sourcePointer == ' ' || *sourcePointer == '\t' || *sourcePointer == '\n') &&
!terminate)
{
*sourcePointer = '\0';
sourcePointer++;
}
commandArguments++;
}
*commandArguments = '\0';
return numOfArguments;
}
void chop(char * sourcePointer)
{
while(*sourcePointer != ' ' && *sourcePointer != '\t' && *sourcePointer != '\n')
{
sourcePointer++;
}
*sourcePointer = '\0';
}
int executeCommand(char** commandArguments, int mode, char ** supplementalPointer)
{
pid_t pid1;
pid_t pid2;
FILE *filePointer;
int mode2 = NORMAL;
int numOfArguments;
int status1;
int status2;
char * commandArguments2[MAX_INPUT_STRING_SIZE];
char * supplement2 = NULL;
int pipes[2];
//Pipeline
if(mode == PIPELINE)
{
if(pipe(pipes))
{
cout<<"Pipe failed"<<endl;
return -1;
}
parseCommand(*supplementalPointer, commandArguments2, &supplement2, &mode2);
}
pid1 = fork();
string str(*commandArguments);
//Push the command to the list of running processes
jobList.push_back(job(pid1, str));
if(pid1<0)
{
cout<<"Fork failed"<<endl;
return -1;
}
//Child process
else if(pid1 == 0)
{
switch(mode)
{
case BACKGROUND:
//Child process is a background process
setpgid(0, 0);
case OUTPUT_REDIRECT:
filePointer = fopen(*supplementalPointer, "w+");
dup2(fileno(filePointer), 1);
break;
case INPUT_REDIRECT:
filePointer= fopen(*supplementalPointer, "r");
dup2(fileno(filePointer), 0);
break;
case PIPELINE:
close(pipes[0]);
dup2(pipes[1], fileno(stdout));
close(pipes[1]);
break;
}
execvp(*commandArguments, commandArguments);
}
//Parent process
else
{
if(mode == BACKGROUND)
{
//Wait for child process to complete
;
}
else if(mode == PIPELINE)
{
waitpid(pid1, &status1, 0);
pid2 = fork();
string str2(*commandArguments2);
jobList.push_back(job(pid2, str2));
if(pid2 < 0)
{
cout<<"fork failed"<<endl;
return -1;
}
else if(pid2 == 0)
{
close(pipes[1]);
dup2(pipes[0], fileno(stdin));
close(pipes[0]);
execvp(*commandArguments2, commandArguments);
}
else
{
close(pipes[0]);
close(pipes[1]);
}
}
else
{
waitpid(pid1, &status1, 0);
}
}
return 1;
}
void printHistory()
{
int commandHistorySize = commandHistory.size();
int i;
string commandPop;
for(i = commandHistorySize; i > 0; i--)
{
commandPop = commandHistory.top();
cout<<i<<" "<<commandPop;
commandHistory.pop();
popHistory.push(commandPop);
}
for(i = 0; i < commandHistorySize; i++)
{
commandPop = popHistory.top();
popHistory.pop();
commandHistory.push(commandPop);
}
}
//Print list of running processes
void printJobs()
{
int i;
for(i = 0; i < jobList.size(); i++)
{
//If the process is no longer running, remove it from the list
if(kill(jobList[i].processID, 0 )!= 0)
{
jobList.erase(jobList.begin() + i);
}
}
for(i = 0; i < jobList.size(); i++)
{
cout<<"["<<i+1<<"] "<<jobList[i].processID<<" "<<jobList[i].command<<endl;
}
}
string returnNth(int nth)
{
int i;
int commandHistorySize = commandHistory.size();
string commandPop;
for(i = commandHistorySize; i > nth; i--)
{
commandPop = commandHistory.top();
commandHistory.pop();
popHistory.push(commandPop);
}
string returnvalue = commandHistory.top();
for(i = commandHistorySize; i > nth; i--)
{
commandPop = popHistory.top();
popHistory.pop();
commandHistory.push(commandPop);
}
return returnvalue;
}