I'm am trying to use the fork method to call a different program and wait until it completes,
I tried to check what is the problem and it seems that the wait function doesn't wait to the childReturns and it makes doesn't print the answer.
(the assignment was to loop compare method until ctrl+c )
the program I call is a comparator that compares 2 strings.
the problem is that the wait func doesn't wait for the child
the compile is GCC
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <unistd.h>
#include <sys/wait.h>
#define LINELEN (80)
char *mygets(char *buf, int len);
char *progName(char *prog);
int main(int argc, char *argv[])
{
char *args[3];
char str1[LINELEN + 1];
char str2[LINELEN + 1];
int childReturns;
if (argc != 2)
return -1;
char *prog = progName(argv[1]);
if (prog == NULL)
return -1;
args[0] = prog;
while (1)
{
printf("Enter string:");
if (mygets(str1, LINELEN) == NULL)
break;
printf("Enter string:");
if (mygets(str2, LINELEN) == NULL)
break;
args[1] = str1;
args[2] = str2;
int processId = fork();
if (processId == 0)
execvp(prog, args);
else
{
wait(&childReturns); // Wait for the child
printf("Child code is %d\n", WEXITSTATUS(childReturns));
}
}
free(prog);
return 0;
}
char *progName(char *prog)
{
char *filePath = (char *)malloc(strlen(prog) + 3);
if (!filePath)
return NULL;
strcpy(filePath, "./");
strcat(filePath, prog);
return filePath;
}
char *mygets(char *buf, int len)
{
char *retval;
retval = fgets(buf, len, stdin);
buf[len] = '\0';
if (buf[strlen(buf) - 1] == 10) /* trim \r */
buf[strlen(buf) - 1] = '\0';
else if (retval)
while (getchar() != '\n')
; /* get to eol */
return retval;
} ```
Try chaining these lines:
if (processId == 0)
execvp(prog, args);
else
To these:
if (processId == 0) {
execvp(prog, args);
perror(prog);
abort();
} else
Also, note that WEXITSTATUS, is only valid if WIFEXITTED() is true. In this case, when your child process abort()'s, it will not be true.
So
wait(&childReturns); // Wait for the child
printf("Child code is %d\n",
needs a bit more work, like:
if (WIFEXITTED(childReturns)) {
printf("Child code is %d\n", WEXITSTATUS(childReturns));
} else if (WIFSIGNALED(childReturns)) {
printf("Child killed by signal %d\n", WTERMSIG(childReturns);
} else {
printf("Child suffered %#x\n", childReturns);
}
But, your real problem is why is execvp() failing? Look carefully at how big your args array is. Does it match the documentation?
fork() system call duplicates the mother process. The resulting child process and the parent process will have their own address spaces. So child process can not modify childReturns variable of the parent.
Additionally, wait(NULL) waits for child processes to end.
As Jonathan Leffler said the problem was with the args, execvp() needs that the array off args has in the end NULL.
and that fixed the problem.
the Right code :
char str1[LINELEN + 1];
char str2[LINELEN + 1];
int childReturns = 1;
if (argc != 2)
return -1;
char *prog = progName(argv[1]);
if (prog == NULL)
return -1;
char *args[4];
args[3] = NULL;
args[0] = prog;
while (1)
{
printf("Enter string:");
if (mygets(str1, LINELEN) == NULL)
break;
printf("Enter string:");
if (mygets(str2, LINELEN) == NULL)
break;
args[1] = str1;
args[2] = str2;
int processId = fork();
if (processId == 0)
{
execvp(prog, args);
}
else
{
wait(&childReturns); // Wait for the child
printf("Child code is %d\n", WEXITSTATUS(childReturns));
}
}
free(prog);
return 0;```
Related
I have an exercise where I need to interact with a C program through pipe.
I have the following source, which I can't modify.
#include <stdio.h>
#include <stdlib.h>
int main()
{
int number;
int answer;
number = rand() % 100;
printf("Print the double of the number %d\n", number);
scanf("%d", &answer);
if(number * 2 == answer)
printf("Success\n");
else
printf("Error\n");
}
I tried to interact with this program with this code
#include <unistd.h>
#include <stdio.h>
int main(int argc, char **argv, char **env)
{
int STDIN_PIPE[2];
int STDOUT_PIPE[2];
pipe(STDIN_PIPE);
pipe(STDOUT_PIPE);
pid_t pid = fork();
if(pid == 0)
{
char *path = "/path/to/binary";
char *args[2];
args[0] = path;
args[1] = NULL;
close(STDIN_PIPE[1]);
close(STDOUT_PIPE[0]);
dup2(STDIN_PIPE[0], STDIN_FILENO);
dup2(STDOUT_PIPE[1], STDOUT_FILENO);
execve(path, args, env);
}
else
{
char buf[128];
close(STDIN_PIPE[0]);
close(STDOUT_PIPE[1]);
while(read(STDOUT_PIPE[0], buf, 1))
write(1, buf, 1);
}
}
But when I run it, it falls in an infinite loop without printing nothing.
I have fixed a number of issues in your code, added a lot of error checks and completed it so that the end goal is reached.
In the child process, srand() must be called to initialize the random number generator or you always get the same value.
The in the child process, you must flush(stdout) after printing the question so that it is really written to the pipe.
And finally, scanf() return value must be checked.
In the main process, I added a lot of error checks. And I write a readLine function to - guess what - read a line from the pipe. A line is terminated by the end-of-line character \n.
There is still room for some enhancements...
I tested my code using Visual Studio Code configured for gcc and running under Ubuntu 20.04.
Here is the child process source:
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
int main()
{
int number;
int answer;
time_t t;
srand((unsigned)time(&t));
number = rand() % 100;
printf("Print the double of the number %d\n", number);
fflush(stdout);
int n = scanf("%d", &answer);
if (n != 1) {
printf("Invalid input\n");
return 1;
}
if ((number * 2) == answer) {
printf("Success\n");
return 0;
}
printf("Error %d is not 2 * %d\n", answer, number);
return 1;
}
And here is the main process source:
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <stdlib.h>
int readLine(int fd, char *buf, int bufSize);
int main(int argc, char **argv, char **env)
{
int STDIN_PIPE[2];
int STDOUT_PIPE[2];
if (pipe(STDIN_PIPE))
{
perror("pipe(STDIN_PIPE)");
return 1;
}
if (pipe(STDOUT_PIPE)) {
perror("pipe(STDOUT_PIPE)");
return 1;
}
pid_t pid = fork();
if (pid == 0)
{
char *path = "../Child/Child"; // Path to child process, adapt to your environment
char *args[2];
args[0] = path;
args[1] = NULL;
if (dup2(STDIN_PIPE[0], STDIN_FILENO) == -1) {
perror("dup2(STDIN) failed");
return 1;
}
if (dup2(STDOUT_PIPE[1], STDOUT_FILENO) == -1) {
perror("dup2(STDIN) failed");
return 1;
}
// Close all pipe ends
close(STDIN_PIPE[0]); // Close read end of STDIN_PIPE
close(STDIN_PIPE[1]); // Write end of STDIN_PIPE
close(STDOUT_PIPE[0]); // Read end of STDOUT_PIPE
close(STDOUT_PIPE[1]); // Close write end of STDOUT_PIPE
if (execve(path, args, env) == -1) {
perror("execve failed");
return 1;
}
}
else
{
char buf[128];
int bufSize = sizeof(buf) / sizeof(buf[0]);
int i;
// Read the question asked by child process
if (readLine(STDOUT_PIPE[0], buf, bufSize) < 0) {
printf("readLine failed.\n");
return 1;
}
// We receive something like "Print the double of the number 83"
printf("Child process question is \"%s\".\n", buf);
// Extract the number at end of string
i = strlen(buf) - 1;
while ((i >= 0) && isdigit(buf[i]))
i--;
int value = atoi(buf + i + 1);
// Write our answer to write end of STDIN_PIPE
char answer[128];
int answerSize = sizeof(answer) / sizeof(answer[0]);
int answerLen = snprintf(answer, answerSize, "%d\n", value * 2);
printf("Our answer is \"%d\".\n", value * 2);
if (write(STDIN_PIPE[1], answer, answerLen) != answerLen) {
printf("write failed.\n");
return 1;
}
// Read the response (success or failure) sent by child process
if (readLine(STDOUT_PIPE[0], buf, bufSize) < 0) {
printf("readLine failed.\n");
return 1;
}
if (strcasecmp(buf, "Success") == 0)
printf("Child process returned success.\n");
else
printf("Child process returned failure.\n");
// Close all pipe ends
close(STDIN_PIPE[0]); // Close read end of STDIN_PIPE
close(STDIN_PIPE[1]); // Write end of STDIN_PIPE
close(STDOUT_PIPE[0]); // Read end of STDOUT_PIPE
close(STDOUT_PIPE[1]); // Close write end of STDOUT_PIPE
}
return 0;
}
// Read a line from file descriptor
// A line is any characters until \n is received or EOF
// \n is not kept
// Return the number of characters read or <0 if error:
// -1 => Input buffer overflow
// -2 => read() failed and errno has the error
int readLine(int fd, char *buf, int bufSize)
{
int i = 0;
while (1)
{
// Check if enough room in the buffer
if (i >= bufSize) {
printf("Input buffer overflow\n");
return -1;
}
// Read one character from the pipe
ssize_t n = read(fd, buf + i, 1);
if (n == -1)
{
perror("read() failed");
return -2;
}
if (n == 0)
{
// EOF received, that's OK
return i;
}
// NUL terminate the buffer
buf[i + 1] = 0;
// Check for end of line character
if (buf[i] == '\n') {
buf[i] = 0; // Remove ending \n
return i;
}
i++;
}
}
so I'm working on a simulated linux shell using C (homework), and I have it exiting the program if the user just simply types in "quit".
However, if they throw "exit" somewhere inside a command... "cat file ; exit"
I need it to execute the command like normal, then quit.
I know that I still have to filter the string entered and strip it of the exit, but for now, I'm just simply trying to get it to recognize the substring of "exit" using strstr() and to exit the program.
It just keeps looping if input contains a string with substring "exit" currently.
Thank you.
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#define bSize 1000
void driveLoop();
char *userInput(void);
int main(int argc, char **argv){
driveLoop();
return 0;
}
void driveLoop(void){
char *comTokens[100];
char *tempTokens;
char *command;
char *cd;
char *cdDir;
char *cdTemp;
char cdBuf[bSize];
char checkExit[] = "exit";
for (;;){
printf("> ");
command = userInput();
if (!command)
break;
char *exitPtr = strstr(command, checkExit); // using strstr on the pointer containing the input
int i = 0;
tempTokens = strtok(command, " \t\n");
while (tempTokens && i < 99){
comTokens[i++] = tempTokens;
tempTokens = strtok(NULL, "\t\n");
}
if (strcmp(comTokens[0], "exit") == 0){ // this exit is working normally
exit(0);
}
if (strcmp(comTokens[0], "cd") == 0){
cd = getcwd(cdBuf, sizeof(cdBuf));
cdDir = strcat(cd, "/");
cdTemp = strcat(cdDir, comTokens[1]);
continue;
}
comTokens[i] = NULL;
pid_t cFork = fork();
if (cFork == (pid_t) - 1){
perror("fork");
}
else if (cFork == 0){
execvp(comTokens[0], comTokens);
perror("exec");
if (exitPtr != NULL){ // here's where I'm calling the exit
exit(0); // nothing happens though. It just keeps looping.
}
}
else {
int status;
waitpid(cFork, &status, 0);
}
}
}
char *userInput(void){
char *input = NULL;
size_t size = 0;
getline(&input, &size, stdin);
return input;
}
else if (cFork == 0){
execvp(comTokens[0], comTokens);
perror("exec");
if (exitPtr != NULL){ // here's where I'm calling the exit
exit(0); // nothing happens though. It just keeps looping.
}
}
execvp doesn't return if it succeeds, so your if will normally never be executed. (And if execvp does return, you probably want to exit the child unconditionally, preferably with _exit().)
What you really want is for the parent process to exit. So you probably wanted to put this code in the parent branch of the fork:
else {
int status;
waitpid(cFork, &status, 0);
// add it here
if (exitPtr != NULL){
exit(0);
}
}
I am trying to use fork with execvp to run two shell commands concurrently. I have two problems which are when I input mkdir folder1&mkdir folder2, it creates a folder named folder1 and another folder named folder2? (the question mark is included in the folder name). The other problem is that the code exits after performing the two commands.
Here is the code:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#define MAXLINE 80 /* The maximum length command */
int main(void) {
char *args [MAXLINE / 2 + 1]; /* command line arguments */
char *line = (char *) malloc((MAXLINE + 1) * sizeof (char));
char *firstCommand = (char *) malloc((MAXLINE + 1) * sizeof (char));
char *secondCommand = (char *) malloc((MAXLINE + 1) * sizeof (char));
int shouldrun = 1; /* flag to determine when to exit program */
pid_t pid;
while (shouldrun) {
printf("osh>");
fflush(stdout);
fgets(line, MAXLINE, stdin);
if (strncmp(line, "exit", 4) == 0) {
shouldrun = 0;
} else {
firstCommand = strsep(&line, "&");
secondCommand = strsep(&line, "&");
pid = fork();
if (pid == 0) {
// child
if (secondCommand != NULL) {
char *token;
int n = 0;
do {
token = strsep(&secondCommand, " ");
args[n] = token;
n++;
} while (token != NULL);
execvp(args[0], args);
}
} else {
// parent
char *token;
int n = 0;
do {
token = strsep(&firstCommand, " ");
args[n] = token;
n++;
} while (token != NULL);
execvp(args[0], args);
}
}
}
return 0;
}
UPDATE 1:
I tried to follow Kevin's answer. I am trying to execute multiple processes concurrently, e.g. ps&ls&who&date. I tried a recursive method which gave me the same behavior. Here is my code:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#define MAXLINE 80 /* The maximum length command */
void execute(char *command) {
char *args [MAXLINE / 2 + 1]; /* command line arguments */
char *parentCommand = strsep(&command, "&");
pid_t pid = fork();;
if (pid == 0) {
// child
if (command != NULL) {
execute(command);
}
} else {
// parent
char *token;
int n = 0;
do {
token = strsep(&parentCommand, " ");
args[n] = token;
n++;
} while (token != NULL);
execvp(args[0], args);
}
}
int main(void) {
char *line = (char *) malloc((MAXLINE + 1) * sizeof (char));
int shouldrun = 1; /* flag to determine when to exit program */
while (shouldrun) {
printf("osh>");
fflush(stdout);
fgets(line, MAXLINE, stdin);
if (strncmp(line, "exit", 4) == 0) {
shouldrun = 0;
} else {
execute(line);
}
}
return 0;
}
For your question about why it doesn't loop, you're calling fork once but calling execvp twice. If successful, execvp will not return. Nothing will get back to run the loop again. What you need to do is call fork once for each execvp. I suggest you move the fork and execvp calls to a separate function:
void run_command(const char* command) {
/* I suggest you also check for errors here */
pid_t pid = fork();
if (pid == 0) {
/* get args, call execvp */
}
}
/* in your loop */
run_command(firstCommand);
run_command(secondCommand);
Regarding the first question, you need to truncate the \n from the line. Regarding the second question, you can use system function from stdlib.h header file which will not terminate your program.
I want to create a UNIX based shell that supports commands with 1 pipe.
For example, the command ls | wc
I have wrote the following code but when I try an input like the one above, the program actes weird and can't figure out where the fault is.
To be more specific, it appears to fork() properly, then exec() with the proper parameters but only the command before the pipe (ls) exits properly. The command after the pipe (wc) never exits actually and as a result there is no output from the program.
I 've put some printf to help me figure out where is the fault in the code.
Functions fetch_input and string_tokenizer are tested and work fine.
The fault must be somewhere below the point pointed in the code.
Any suggestion to help me find what is the fault would be appreciated.
Thanks in advance.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#define ANSI_COLOR_GREEN "\x1b[32m"
#define ANSI_COLOR_CYAN "\x1b[36m"
#define ANSI_COLOR_RESET "\x1b[0m"
char * fetch_input(char * buffer);
char * trim(char * string);
char ** string_tokenizer(char * string, char c);
int main(int arc, char * argv[]){
char *input, *init_input, **commands, **com1, **com2;
pid_t fork_pid_1, fork_pid_2, ret_pid;
int stat,i, fd[2];
init_input = (char *)malloc(sizeof(char)*256);
input = init_input;
input = fetch_input(input);
while(strcmp(input,"exit")!=0){
commands = string_tokenizer(input,'|');
com1 = string_tokenizer(commands[0],' ');
if(commands[1]==0){
fork_pid_1 = fork();
if(fork_pid_1<0){
printf("Fork Error!\n");
_exit(1);
}
else if(fork_pid_1==0){
execvp(com1[0],com1);
printf("Exec Error!\n");
_exit(1);
}
else{
ret_pid=wait(&stat);
}
}
else{ //the fault is probably somewhere below that point
if(pipe(fd)<0){ _exit(1);}
fork_pid_1 = fork();
if(fork_pid_1<0){
printf("Fork Error!\n");
_exit(1);
}
else if(fork_pid_1==0){
printf("First command, ready to exec...(pid:%d)\n",getpid());
close(fd[0]);
dup2(fd[1],1);
close(fd[1]);
execvp(com1[0],com1);
printf("Exec Error!\n");
_exit(1);
}
else{
com2 = string_tokenizer(commands[1],' ');
fork_pid_2=fork();
if(fork_pid_2<0){
printf("Fork Error!\n");
_exit(1);
}
else if(fork_pid_2==0){
printf("Second command, ready to exec...(pid:%d)\n",getpid());
close(fd[1]);
dup2(fd[0],0);
close(fd[0]);
execvp(com2[0],com2);
printf("Exec Error!\n");
_exit(1);
}
else{
printf("Now we 're in the parent process...(p_pid:%d)\n",getpid());
while( (ret_pid=waitpid(-1,&stat,0)) >0 ){
printf("Child process (%d) exited with status:%d\n",ret_pid,stat);
}
}
}
}
input = init_input;
input = fetch_input(input);
}
return 0;
}
char * fetch_input(char * buffer){
int i, sum;
do{
printf(ANSI_COLOR_CYAN "$" ANSI_COLOR_RESET);
fflush(stdin);
fgets(buffer,256,stdin);
if(buffer[strlen(buffer)-1]=='\n'){
buffer[strlen(buffer)-1]='\0';
}
sum=0;
for(i=0;i<strlen(buffer);i++){
if(buffer[i]==' '){sum++;}
}
}while(strlen(buffer)==sum);
buffer = trim(buffer);
return buffer;
}
char * trim(char * string){
int i;
i=strlen(string);
while(string[i-1]==' '){
i--;
}
*(string+i)='\0';
while(isspace(*string)){string++;}
return string;
}
char ** string_tokenizer(char * string, char c){
int j=0,k,i,done,found,first,last;
char ** ret, *str;
str=string;
if( (str==0) || (strlen(str)==0) ) return NULL;
ret = (char **)malloc(sizeof(char*));
do{
done=0;
found=0;
i=0;
first=-1;
last=-1;
while( (str[i]!='\0') && (done==0) ){
if( (str[i]==c) && (found==0) ){
i++;
}
else if( (str[i]!=c) && (found==0) ){
found=1;
first=i;
i++;
}
else if( (str[i]!=c) && (found!=0) ){
i++;
}
else if( (str[i]==c) && (found!=0) ){
done=1;
last=i;
}
}
if(done!=0){
*(ret+j) = (char *)malloc(sizeof(char)*(last-first+1));
for(k=first;k<last;k++){
*(*(ret+j)+(k-first)) = str[k];
}
*(*(ret+j)+(k-first)) = '\0';
*(ret+j) = trim(*(ret+j));
j++;
str=str+last;
}
}while(done!=0);
if( (done==0) && (found==0) ){
*(ret+j)=NULL;
}
else if( (done==0) && (found!=0) ){
*(ret+j) = (char *)malloc(sizeof(char)*(i-first+1));
for(k=first;k<i;k++){
*(*(ret+j)+(k-first)) = str[k];
}
*(*(ret+j)+(k-first)) = '\0';
*(ret+j)=trim(*(ret+j));
*(ret+j+1) = NULL;
}
return ret;
}
I'm trying to write a simple C shell. My problem is that I have written the program so that when the user enters a NULL value I've got the shell to exit and stop running. However after using different shells i've realised that the shell continues to loop. Is there anyway to fix this without having to rewrite my code? I'm still quite a novice to C.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/wait.h>
#include <unistd.h>
#define MAX_CMD_SIZE 512
int getPath(){
printf("PATH = %s\n", getenv("PATH"));
return 0;
}
int setPath(char* arg){
setenv("PATH", arg, 1);
return 0;
}
int setwd() {
char *arg;
arg = getenv("HOME");
chdir(arg);
return 0;
}
int main()
{
char buff[MAX_CMD_SIZE]; //buff used to hold commands the user will type in
char *defaultPath = getenv("PATH");
char *args[50] = {NULL};
char *arg;
int i;
pid_t pid;
setwd();
while(1){
printf(">");
if (fgets(buff, MAX_CMD_SIZE, stdin) == NULL) { //Will exit if no value is typed on pressing enter
setPath(defaultPath);
getPath();
exit(0);
}
arg = strtok(buff, " <|>\n\t");
i = 0;
if (arg == NULL) return -1;
while (arg != NULL){
printf("%s\n", arg);
args[i] = arg;
arg = strtok(NULL, " <|>\n\t");
i++;
}
if (strcmp(args[0], "exit") == 0 && !feof(stdin)){ //Will exit if input is equal to "exit" or ctrl + d
setPath(defaultPath);
getPath();
exit(0);
}
else {
pid = fork();
if (pid < 0){ //Error checking
fprintf(stderr, "Fork Failed\n");
} else if (pid == 0){ //This is the child procsess
execvp(args[0], args);
exit(-1);
} else { //Parent Process
wait(NULL); // Parent will wait for child to complete
}
}
}
return 0;
}