C: Multi process CMD does nothing after running all commands - c

I made this in C, and it does run all commands concurrently. But at the end, it puts the last command in the user input part of the CMD then does nothing right after.
This is the output of the command:
hen03:~/Lab_08> ./assign8 whoami , ls -a , pwd
Child Process: PID=11895, PPID=11894, Command=whoami
Child Process: PID=11897, PPID=11894, Command=pwd
Child Process: PID=11896, PPID=11894, Command=ls
/home/uid454/Lab_08
hen03:~/Lab_08> . .. assign8 assign8.c makefile uid454.zip
uid454
hen03:~/Lab_08>
And this is the code I wrote
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <wait.h>
void startProcesses(int commands, char *commandList[6][100]){
static pid_t forks[6];
int status[6];
int i;
for(i = 0; i < commands; i++){
switch(forks[i] = fork()){
case -1: //failed
perror("fork failed");
break;
case 0: //child does things
printf("Child Process: PID=%ld, PPID=%ld, Command=%s\n", (long) getpid(), (long) getppid(), commandList[i][0]);
if(execvp(commandList[i][0], commandList[i]) < 0){
printf("something failed\n");
}
break;
default:
if(i == commands-1){
waitpid(forks[i], &status[i], 0);
}
}
}
}
int main(int argc, char *argv[])
{
int commandNum = 0, index, numOfCommands = 1, j =0;
static char *commandList[6][100];
//start of separate algo
for (index = 1; index < argc; index++){
if(strcmp(strtok(argv[index], " "), ",") == 0){
commandNum++;
numOfCommands++;
commandList[commandNum-1][j] = NULL;
j = 0;
continue;
}
else{
commandList[commandNum][j] = argv[index];
j++;
}
}
//end of separation algo
startProcesses(numOfCommands, commandList);
return 0;
}
I honestly have no idea what the problem is nor how to even google it for that matter. Could someone explain why its doing this?
edit: took out line numbers

Is the problem that the output of some child-processes are shown tacked on to the shell prompt?
Then that's because you don't wait for all your child processes, only the last one. And there's no guarantee that the child-processes will execute in any specific order.
That means your parent process could exit before all child-processes have finished, which means that the shell is back in control and will display the prompt.
You should really wait for all child-processes. Preferably in a second loop if you want the child-processes to run in parallel.

Related

How can I get my C Shell to recognize that this is a command?

I am very new at C but am currently working on creating a C program to serve as a shell interface. It is supposed to accept commands and then execute each command in a separate process. I am currently stuck trying to get C to recognize that it is a command. I am unsure how to do this, and can't seem to find any useful examples.
Here is my code, it is saying that everything is not a valid command ("no cmd"). Does anyone have any idea why this would be occurring? Is C not able to recognize it is a command in the execvp() function or do I need to implement something for that specific purpose?
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/wait.h>
#define MAX_LINE 80
/* 80 chars per line per command */
int main(void) {
//char *args[MAX_LINE/2 + 1];
char *args = calloc(MAX_LINE, (MAX_LINE/2 +1));
const size_t sz = MAX_LINE;
pid_t pid;
/* command line (of 80) has max of 40 arguments*/
int should_run = 1;
while (should_run) {
printf("osh>"); //beginning of command line
fgets(args, sz, stdin); //gets the stdin
char *token = strtok(args, " \n"); //supposed to break str if it has a space or line and recognize there are 2 commands
printf("%s\n", token);
token = strtok(NULL," \n");
printf("%s\n", token);
pid_t parent = getpid();
pid = fork(); //forking child
if(pid == 0){ //if forking occurred
int status = execvp(&args[0], &args); //status of input, is it a command?
printf("%d", status);
printf("forked!");
if(status == -1) { //if cmd err, print
printf("no cmd");
return 1;
} else {
printf("line will be printed");
}
return 0;
}
fflush(stdout); //flush
/*
* After reading user input, the steps are :
* 1: fork a child process
* 2: the child process will invoke execvp()
* 3: parent process waits for the child to exit before
continuing
*/
}
exit(0);
/**
return to the operating system:
-exit this process
-flush all
*/
}
If you look at the documentation for the exec family of functions, you'll note that the functions only return if the exec failed. That's because exec, when successful, completely replaces the calling process with the invoked program.
What you need to do is, from the parent process (i.e., the one that got a positive value returned from fork), wait on the child process via waitpid.
pid_t pid;
pid = fork();
if ( pid < 0 ) {
// Handle the error.
}
else if ( pid == 0 ) {
execvp(&args[0], &args);
// The fact that we've reached this line means that execvp failed.
exit(1);
}
else {
int status;
while ( waitpid(pid, &status, 0) != pid ) {} // We need this loop in case waitpid gets interrupted by a signal.
// status doesn't equal the return value of the child process. We need to extract that with macros.
if ( WIFEXITED(status) ) {
printf("Child process exited with code %i\n", WEXITSTATUS(status));
}
else {
printf("Child process was terminated by signal number %i\n", WTERMSIG(status));
}
}

Why my child process exit without actual termination?

I am writing a program about process
The process accept a bash command and run it with exec
I use fork() to create child process run exec inside it
and plan to do some post process in parent process
but for some reason the exec run properly but wait is not working poperly
"post process here" print put before exec's program terminate
It worked before but suddenly not working,
I have not idea what changes I have made to cause this problem
#include <stdio.h>
#include <sys/wait.h>
#include <unistd.h>
int main(int argc, char *argv[]) {
/* code */
for(int i = 0; i < argc; i++){
//delete argv 0 for convincent
argv[i] = argv[i + 1];
}
int pid = fork();
if(pid == -1){
return 1;
}
if(pid == 0){
execvp(argv[0], argv);
}else{
wait(NULL);
printf("post process here");
}
return 0;
}
First:
//delete argv 0 for convincent
argv[i] = argv[i + 1];
i would us a new array with bound [argc+1] otherwise you are in danger of an array out of bound error (bufferoverflow).
Second:
the called bash command is important. Some commands are just starting a deamon and finish themself.

Trouble creating a C program using fork() and execvp() functions

Here is the following code I am current having issues with:
#include <stdio.h>
#include <unistd.h>
#include<sys/types.h>
#include<sys/wait.h>
#define MAX_LINE 80
int main(void)
{
char *args[MAX_LINE/2+1];
int background= 0;//integer that acts a boolean for if & appears
int should_run = 1;
int status;
while(should_run)//Just to keep the interface going until the user chooses to stop
{
printf("osh>");//prompt
fflush(stdout);
int i = 0;
while(getchar() != '\n')//Use scanf until a new line is found
{
scanf("%s", &args[i]);
if(strcmp(&args[i], "exit") == 0)//temporary exit protocal
{
printf("Exiting now...");
return 0;
}
if(strcmp(&args[i], "&") == 0)//If we find a & in our input then we changed background to 1 or true
background = 1;
printf("Args[%i] = %s\n", i, &args[i]);//Tester
i++;
}
printf("Background = %i\n",background);//Test
pid_t pid= fork();// Create new child process
printf("process %i created\n", pid);//Test
if(pid < 0)//fork() failed
{
printf("Fork Failed.\n");
return 1;
}
else if(pid == 0)//Child process id
{
printf("Child process started %s command\n", &args[0]);
if(execvp(args[0], args) < 0)//change the current child process to execute the input given by the user
//with args[0] being the command and args being the parameters(ls -l is an example).
{
printf("Command failed\n");
}
return 0;
}
else//Parent Process
{
if(background == 1)//If the user typed in a & then the parent will wait for a change in state from the child, if there is no &
//then we will just finish the parent process
{
printf("Parent process waiting on child\n");
wait(NULL);
}
}
}
return 0;
I have one major issue and one minor issue right now. The major issue is that I have a printf method before execvp starts that says "Child Process started" and I get this line to print, but then nothing else happens. No interrupts are thrown, the program just seems to be frozen on my execvp command.
My minor issue is that when my program starts a prompt "osh>" before asking for input. Now if, for example, I would type in "osh>ls -l" then I get args[0] = s, args1 = -l. Now if I put "osh> ls -l" in that exact format I get args[0] = ls, args1 = -l. Is that a part of scanf() that I am not using properly here to make sure I get ever character after "osh>" and between blank spaces as strings?
EDIT:
here is my output for user input "ls -l"
The problem you're having with the missing character is because getchar() is consuming the first character of your input before scanf gets to take a stab at it. You probably want to do something like:
while (scanf("%s", &buffer) > 0)
{
strcpy(args[i], buffer);
/* then do stuff with args[i] */
}

Writing a shell in C with fork() and execvp(). Why is execvp not executing anything?

I'm trying to implement a minimal shell using C. I will run it on a linux machine.
Basically, execvp() does not seem to execute anything. Why is this? I have some code that tries to see if there is an error. Any command I enter into my mini-shell returns that error statement. Also, nothing happens, a sure sign something isn't working.
The problem from the book I'm working on says to invoke execvp() as execvp(args[0], args). I see this referenced on stackoverflow as the way to invoke it, but some also suggest execvp(args[0],args[1]) Which one is it?
I think by now I have little to no errors in the way I'm handling user input, but if there's anything just point it out.
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<string.h>
#include<time.h>
#define MAXLINE 80
int main(void)
{
char *args[MAXLINE/2 + 1]; //cl w/ 40 max args
//the above is an array of pointers-to-char
char *s;
int k;
int pid;
int lastarg;
int status;
int should_run = 1; //determines when to exit
printf("CS149 Shell from MYNAME HERE\n");
while(should_run)
{
/* PROMPT */
printf("AUTHOR-L3006323213> ");
fflush(stdout);
s = (char*) calloc ( 200, sizeof(char) );
fgets(s,199,stdin);
/* PARSE */
k=0;
args[k] = strtok(s," \n\t");
while(args[k])
{
printf("%d %s\n",k, args[k] );
++k;
args[k] = strtok(NULL," \n\t");
}
args[k]=NULL;
lastarg=k-1;
/* HANDLE EXIT */
if (strcmp(args[0],"exit\n") == 0)
{
should_run=0;
continue;
}
//fork child process using fork();
pid = fork();
if(pid<0) printf("ERROR!\n");
else if(pid==0)
{ //child
if(execvp(args[0], &args[1]) < 0)
printf("Command not found.\n");
exit(1);
}
else
{ //parent
//check last arg for == &
printf("Parent is ");
if(strcmp(args[lastarg],"&") == 0)
printf(" not waiting...\n");
else
{
printf("waiting...\n");
while (wait(&status) != pid);
}
}
/* Cleanup for next prompt */
free(s);
}
return 0;
}
EDIT: So I've managed to fix somethings. I'm invoking execvp() using execvp(args[0], &args[1]) now. If I give my terminal ps &, I see a list of processes, but another prompt does not show up. When I give my terminal ps, I do not see any list of processes at all. I think there's an issue with the parent code, and maybe with my control logic.

c parallel processes involving pipe

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
int main(int argc, char* argv[])
{
int i;
int fd[2];
int values[argc - 1];
for( i = 1; i < argc; i++ ) {
pipe(fd);
switch( fork()) {
case 0: /* child */
/* Do stuff */
close(fd[0]);
int value = atoi(argv[i]);
write(fd[1], &value, sizeof(value));
exit( 0 );
case -1:
perror( "fork" );
exit(1);
default: /* parent */
close(fd[1]);
read(fd[0], &values[i - 1], sizeof(values[i - 1]));
/* do stuff, but don't wait() or terminate */
}
}
for (i = 0; i < (argc - 1); i++)
{
printf("%d\n", values[i]);
}
return 0;
}
im trying to create as many processes as the number of arguments given to the executable and have each process pipe the argument to the parent and store into an array and at the end print out the elements of the array. since the processes run in parallel, when i print out the array elements, the order should be random with respect to the order i entered these elements, but that doesnt seem to be the case as i have ran the executable 1 million times, can someone tell me what the problem is? So someone kindly pointed out that read serializes things, what should i do to make the processes truly parallel?
Your pipe read in the parent serializes things.
You (1) create the child; (2) child writes to the pipe; (3) parent will block on read until there is something to read. This repeats for all the children.

Resources