I want to make it so when a user attaches a – after a command it will be executed in the background. For some reason if I execute a command normally it will wait, then if I execute a command in the background it will work but then if I execute a command normally it won’t wait for it. I am sure I am just doing something small-ish wrong. Any ideas:
void executeSystemCommand(char *strippedCommand, char *background, int argc, char **args) {
char pathToExecute[80];
// Check if command will be executed in the background
int shellArgs;
bool bg;
if (!strcmp(background, "-")) {
bg = true;
shellArgs = argc -1;
} else {
bg = false;
shellArgs = argc;
}
// Save the linux commands in a new array
char *executableCommands[shellArgs+1];
int j;
for (j = 0; j < shellArgs+1; j++) {
executableCommands[j] = args[j];
}
executableCommands[shellArgs] = NULL;
// Check the $PATH
const char delimiters[] = ":";
char *token, *cp;
char *forLater;
int count = 0;
char *path;
path = getenv("PATH");
// All of this just breaks up the path into separate strings
cp = strdup(path);
forLater = strdup(path);
token = strtok (cp, delimiters);
while ((token = strtok (NULL, delimiters)) != NULL) {
count++;
}
char **argv;
int size = count+1;
argv = (char**) malloc (size);
count = 0;
token = strtok (forLater, delimiters);
argv[0] = (char*) malloc (50);
argv[0] = token;
strcpy(argv[0],token);
while ((token = strtok (NULL, delimiters)) != NULL) {
count++;
argv[count] = (char*) malloc (50);
argv[count] = token;
}
// This goes through the path to see if the linux command they entered
// Ex: sleep exists in one of those files and saves it to a var
int i;
bool weHaveIt = false;
int ac;
for (i = 0; i < count; i++) {
char str[80];
strcpy(str, argv[i]);
strcat(str, "/");
strcat(str, args[0]);
ac = access(str, F_OK);
if (ac == 0) {
weHaveIt = true;
strcpy(pathToExecute, str);
break;
}
}
if (!weHaveIt) {
printf("That is not a valid command. SORRY!\n");
return;
}
executableCommands[0] = pathToExecute;
int status;
// Get the array for
// If user wants command to be a background process
if (bg) {
int background_process_id;
pid_t fork_return;
fork_return = fork();
if (fork_return == 0) {
background_process_id = getpid();
addJobToTable(strippedCommand, background_process_id);
setpgid(0, 0);
execve(executableCommands[0], executableCommands, NULL);
exit(0);
} else {
return;
}
} else {
int background_process_id;
pid_t fork_return;
fork_return = fork();
if (fork_return == 0) {
background_process_id = getpid();
status = execve(executableCommands[0], executableCommands, NULL);
exit(0);
} else {
wait(&status);
return;
}
}
}
The call to
waitmade for the third job returns immediately because the second job has finished and is waiting to be handled (also called “zombie”). You could check the return value ofwait(&status), which is the PID of the process that has exited, and make sure it is the process you were waiting for. If it’s not, just callwaitagain.Alternatively use
waitpid, which waits for a specific process:If you do this you should implement a signal handler for
SIGCHLDto handle finished background jobs to prevent the accumulation of “zombie” child processes.In addition to that, in the background job case, the branch where
fork()returns 0 you are already in the new process, so the call toaddJobToTablehappens in the wrong process. Also, you should check the return values of all the calls; otherwise something may be failing and you don’t know it. So the code for running a job in the background should look more like this: