connect commands trough pipes in a C Shell Linux - c

This is the function that I call inside the main when I recognize a pipe
void execArgsPiped(char* input)
{
char* parsedpipe[MAXCOM];
parsePipe(input,parsedpipe);
pid_t pid;
int in, fd [2];
int i= 0;
in = STDIN_FILENO;
while(i<nCommands-1)
{
pipe (fd);
char *toEx[100];
parseSpace(parsedpipe[i],toEx);
if(i!=nCommands-1)
spawn_proc (in, fd [1], toEx);
close(fd [1]);
in = fd [0];
i++;
}
if (in != 0)
dup2 (in, 0);
char *toEx1[100];
parseSpace(parsedpipe[i],toEx1);
printf(" command %i is %s \n",i,parsedpipe[1]);
if (execvp(toEx1[0], toEx1) < 0) {
printf("\nCould not execute command..");
}
and here is the call to the function that spawns the process to execute the command
void spawn_proc (int in, int out, char **toEx)
{
pid_t pid;
pid = fork();
if (pid < 0)
{
printf("error creating process");
}
else if(pid==0){
if (in != 0)
{
dup2 (in, 0);
close (in);
}
if (out != 1)
{
printf("the if 12");
dup2 (out, 1);
close (out);
}
if (execvp(toEx[0], toEx) < 0) {
printf("\nCould not execute command..");
}
}
else{
wait(NULL);
}
}
My problem is that when I compile and insert two commands connected by a pipe, the Shell goes into a kinda of infinite loop and I have to close the terminal because of that.
I really cannot understand what's wrong with the code, I don't know very much of C.
Any help is really appreciated.

Related

Bind the pipe to stdin, there is a blocking problem

I want to associate the standard output of the parent process with the standard input of the child process through an anonymous pipe. I did achieve my needs, but I also encountered a problem: the program blocks when calling scanf, even if stdin is set to none Buffer, replace scanf with read to run normally! I know this has something to do with the standard I/O function buffer, but I don't know the internal reason, I hope I can get help, thank you!
Code:
int main(void)
{
int fd[2];
int cpid;
pipe(fd);
if((cpid = fork()) < 0)
{
perror("fork error");
exit(-1);
}
else if(cpid == 0)
{
sleep(1);
char buf[100] = {0};
close(fd[1]);
if(dup2(fd[0], 0) < 0)
{
perror("parent - dup2 error");
exit(-1);
}
setbuf(stdin, NULL);
scanf("%s", buf);
//read(0, buf, 100);
printf("child: \n\t%s", buf);
return 0;
}
sleep(1);
close(fd[0]);
if(dup2(fd[1], 1) < 0)
{
perror("parent - dup2 error");
exit(-1);
}
setbuf(stdout, NULL);
printf("123");
wait(NULL);
return 0;
}
Problem is that to scanf to terminates its reading, necessitate that the data is ended in some way.
You are reading a string, but how scanf can know that there is nothing after 123? So it is waiting for something, either the end of communication, space, newlines, whatever can tell it that a string is fully available.
Try adding a simple spacing, a new line in the writing part:
printf("123 ");
or
printf("123\n");
Note that as is, you can not use the closing solution, just because you forgot to close the original descriptors after the dup.
Rewrite the code like this:
#include <stdio.h>
#include <unistd.h>
#include <sys/wait.h>
#include <stdlib.h>
int main(void) {
int fd[2];
int cpid;
pipe(fd);
if((cpid = fork()) < 0) {
perror("fork error");
exit(-1);
}
else if(cpid == 0) {
sleep(1);
char buf[100] = {0};
close(fd[1]);
if(dup2(fd[0], 0) < 0) {
perror("parent - dup2 error");
exit(-1);
}
close(fd[0]); // close not useful original desriptor
setbuf(stdin, NULL);
scanf("%s", buf);
printf("child: \n\t%s", buf);
return 0;
}
sleep(1);
close(fd[0]);
if(dup2(fd[1], 1) < 0) {
perror("parent - dup2 error");
exit(-1);
}
close(fd[1]); // close not useful original desriptor
setbuf(stdout, NULL);
printf("123");
close(1); // close the "connexion" (ie the last open for writing descriptor on the pipe)
wait(NULL);
return 0;
}

minishell created in c not working as expected, pipes related

im making an UNIX minishell in c, in my OS signature. I only have to improve the shell itself, we have a premade parser for implement yacc and a scanner for implement lex among other files.
The file to modify is msh.c, the main executable file.
Well, the problem begins when I try to implement the pipe secuences. This is what I made:
int executePipeLine (char*** argvv, int bg, char** filev, int n){
int i;
int in = 0;
pid_t pid;
int fd[2];
for (i = 0 ; i < n-1 ; i++){
pipe(fd);
pid_t pid = fork();
if (pid == 0){ //child
if (in != STDIN_FILENO){
dup2(in, STDIN_FILENO);
close(in);
}
if (fd[1] != STDOUT_FILENO){
dup2(fd[1], STDOUT_FILENO);
close(fd[1]);
}
execvp(argvv[i][0], argvv[0]);
}
else { //parent
close(fd[1]);
in = fd[0];
}
}
if(in != STDIN_FILENO){
dup2(in, STDIN_FILENO);
close(in);
}
pid_t lastpid = fork();
if(lastpid == 0){ //child
execvp(argvv[i][0], argvv[0]);
}
if(lastpid == -1){
perror("no se pudo crear el hijo\n");
exit(-1);
}
else { //parent
/* not bg*/
if(!bg ) {
int status;
while (wait(&status) != lastpid); /* wait the child. */
}
else {
/*bg mode*/
printf("pid del proceso last: %d\n", lastpid);
}
return 0;
}
}//end executePipeLine
This seems to work fine. In the main , where we have an extern function called obtain_order(); that returns the number of commands + 1 and save it to ret. If ret is 1 we continue with the prompt, if is 0 means EOF (Control + D keybinding) to end the shell, and if is >1 execute the commands.
Here you have it:
int main(void)
{
char ***argvv;
int command_counter;
int num_commands;
int args_counter;
char *filev[3];
int bg;
int ret;
int reset = 0;
setbuf(stdout, NULL); /* Unbuffered */
setbuf(stdin, NULL);
while (1)
{
fprintf(stderr, "%s", "msh> "); /* Prompt */
ret = obtain_order(&argvv, filev, &bg);
printf("ret: %d\n", ret);
if (ret == 0) break; /* EOF */
if (ret == -1) continue; /* Syntax error */
num_commands = ret - 1; /* Line */
if (num_commands == 0) continue; /* Empty line */
if(num_commands > 1){
executePipeLine(argvv, bg, filev, num_commands);
}
else if (num_commands == 1){
executeCommand(argvv, bg, filev);
}
} //fin while
return 0;
} //end main
All works fine with a simple command. The problem is when I try to execute a pipe line. It show a good result, but i dont know we, after that ret always is 0 in the next iteration, so every time I try to execute a pipe line, it works but close the shell process, and have to execute it again instead of continue with the promt.
You know what is the problem here?
I hope you understand me, my english is not perfec. Thanks
In the parent process (i.e. your mini-shell) you are duping the standard input:
...
else { //parent
close(fd[1]);
in = fd[0];
}
...
if(in != STDIN_FILENO){
dup2(in, STDIN_FILENO);
close(in);
}
When your pipeline terminates, children are dead, pipes are no longer readable => stdin is considered as closed. When you later call obtain_order in your main loop, it returns 0 (EOF) and your program exits.

Piping in my own C shell

I've implemented the beginning of a C shell as below. So far I have my redirection working, and I thought I would implement | in a similar way but am having difficulty.
Can anyone help?
I would begin with checking for the pipe operator, then saving the sa[i-1] and sa[i+1] as the two separate commands, but I'm not sure how to fork() and exec() properly after this.
int startProcess (StringArray sa)
{
int pid;
int status;
int fd1;
int fd2;
int current_in;
int current_out;
int fd0;
int fd00;
int in = 0;
int out = 0;
char input[64]="";
char output[64]="";
char cmd1[64] ="";
char cmd2[64] ="";
int fd[2];
int pipe = 0;
switch( pid = fork()){
case -1://This is an error
perror("Failure of child.");
return 1;
case 0: // This is the child
// Redirection
/* finds where '<' or '>' occurs and make that sa[i] = NULL ,
to ensure that command wont' read that*/
for(int i=0;sa[i]!='\0';i++)
{
if(strcmp(sa[i],"<")==0)
{
sa[i]=NULL;
strcpy(input,sa[i+1]);
in=2;
}
if(strcmp(sa[i],">")==0)
{
sa[i]=NULL;
strcpy(output,sa[i+1]);
out=2;
}
}
//if '<' char was found in string inputted by user
if(in)
{
// fdo is file-descriptor
int fd0;
if ((fd0 = open(input, O_RDONLY, 0)) < 0) {
perror("Couldn't open input file");
exit(0);
}
// dup2() copies content of fdo in input of preceeding file
dup2(fd0, 0); // STDIN_FILENO here can be replaced by 0
close(fd0); // necessary
}
//if '>' char was found in string inputted by user
if (out)
{
int fd00 ;
if ((fd00 = creat(output , 0644)) < 0) {
perror("Couldn't open the output file");
exit(0);
}
dup2(fd00, STDOUT_FILENO); // 1 here can be replaced by STDOUT_FILENO
close(fd00);
}
execvp(sa[0], sa);
perror("execvp");
_exit(1);
printf("Could not execute '%s'\n", sa[0]);
default:// This is the parent
wait(&status);
return (status == 0) ? 0: 1;
}
}
Make a pipe.
fork().
In the parent set the STDOUT file descriptor (1) to the input of your pipe.
In the child set the STDIN file descriptor (0) to the output of your pipe.
exec() in both the parent and the child.
Do all of this in the child after you fork(), just like for redirection.

Read pipe from another .c with use of exec, c programm

i have a problem reading from a pipe that was created from another .c via execl! I have tried it with a lot of different approaches but i still can't find the solution. I'd appreciate it if you could help me!
Here is the first code :
...some other code before for loop...
for (counter=0; counter<arithmos; counter++)
{
if (pipe(pinakas[counter]) == -1)
{
perror("pipe");
exit(1);
}
sem_wait(&sima);
strcpy(buffer,queueHead(q));
write(fd[WRITE], buffer, strlen(buffer));
queueRemove(&q);
nodes--;
sem_post(&sima);
pid = fork();
if (pid < 0)
{
perror("fork error");
exit(1);
}
if (pid == 0)
{
execl("./paidi","paidi", (char*)pinakas[counter], (char*)NULL);
exit(1);
}
if (pid > 0)
{
printf ("I am the parent with pid %d\n", getpid());
wait(NULL);
}
}
And here is what my child does...
includes etc etc...
int main(int argc, char **argv)
{
//char fd[2];
int *fd = (int*) argv[1];
int nbytes;
char buffer[256];
char *command;
int i;
for (i=0; i<256; i++)
{
buffer[i] = '\0';
}
printf("test2 %d\n",fd[READ]);
//close(fd[WRITE]);
printf("test3\n");
read(fd[READ], buffer, 256);
printf("test4\n");
close(fd[READ]);
printf("test5\n");
printf("Received url : %s", buffer );
printf("test6\n");
//sprintf(command,"wget %zd", url);
//system(command);
printf("I am a child with pid %d\n", getpid());
return 0;
}
Seems like i am doing something wrong with execl. I am trying to pass pointer as argument and i have a pipe : bad address error. I also tried it with string but nothing... Any ideas?
execve(2) creates a brand-new memory space for loaded executable, you cannot pass pointers from previous program, they don't make any sense in that new memory space.
The tried and true approach here is to replace child's standard input (file descriptor 0) with read-end of the pipe(2) after the fork(2) but before the execve(2) using dup2(2).

Pipes and Forks

The goal of this project is to use pipes and forks to execute a line-count utility already written in a multi-process manner (one process per argument). I'm currently working on getting a single process working before expanding to handle multiple args.
Given two executables, lc1 and lc2, I want lc2 to establish a pipe to the stdout file descriptor of lc1, so that when execlp("lc1", argv[1], NULL) is called, the output will be read in by
while ((c= read(pipefd[0], readin, SIZE)) > 0)
According to my Unix book, I should use the open, dup2, close method for redirecting stdout to stdin, and here's my code:
int pid, c, i;
char *readin= (char *)malloc(sizeof(SIZE));
if (pipe(pipefd)== -1)
perror("Can't open a pipe\n");
for (i=1; i< argc; i++){
if ((pid= fork())==-1)
perror("Can't fork\n");
run(argv[i]);
}
//close pipe
close(1);
if (dup2(pipefd[0], 0)==-1)
perror("Can't redirect stdin");
close(pipefd[1]);
for (i=1; i< argc; i++){
if ((wait(NULL))== -1)
perror("Wait error");
while ((c= read(pipefd[0], readin, SIZE)) > 0){;
//print buf count
total += atoi(readin);
}
}
The run function is
void run(char *f){
int fp;
if ((fp= open(f, O_RDONLY)) == -1)
perror("Can't open the file");
close(pipefd[0]);
dup2(pipefd[1], 1);
close(pipefd[1]);
execlp("ls1", f, NULL);
}
When I try to execute this code, I get a stdin redirect error saying bad file descriptor. Why is this happening, and would appreciate any hints to for fixing.
run(argv[i]) is executed by both parent and child because are not assigning the functionality based on the returned PID, so one close after the other may have closed.
See below code, can he handy, I will use the code sample for situations like this. :
int main()
{
int pipe_fd[2] = {0};
int pid = -1;
int status = -1;
int ret_value = INVALID_CMD;
int cmd_output_len = -1;
status = pipe(pipe_fd);
if(status<0)
{
perror("pipe create err");
}
else
{
pid = fork();
if(pid<0)
{
}
else if (pid == 0)
{
/*Child functionality*/
child_func(pipe_fd, cmd);
}
else
{
/*Parent functionality*/
cmd_output_len = parent_fun(pid, pipe_fd);
}
}
return ret_value;
}
int child_func(int pipe_fd[], const char * cmd)
{
int status = 5;
int read_fd = pipe_fd[0]; /*read file descriptor*/
int write_fd = pipe_fd[1]; /*write file descriptor*/
int exit_status = 0;
/*close read fd*/
close(read_fd);
/*dup2 stdout to write fd*/
//status = dup2(1, write_fd);
status = dup2(write_fd, 1);
if(status<0)
{
exit(-1);
}
else
{
system(cmd);
exit(0);
}
}
int parent_fun(int child_id, int pipe_fd[])
{
int status = -1;
int len = 0;
bool_e break_loop = FALSE;
int read_fd = pipe_fd[0]; /*read file descriptor*/
int write_fd = pipe_fd[1]; /*write file descriptor*/
/*close write fd*/
close(write_fd);
while(1)
{
sleep(1);
status = waitpid(child_id, &status, WNOHANG);
switch(status)
{
case 0:
/*Child is still active*/
printf("No process waiting to exit..\n");
len = do_ur_fun(read_fd);
write(1, output, len);
break;
/*case EINTR:
case ECHILD:
case EINVAL:
perror("waitpid error");
break_loop = TRUE;
break;*/
default:
if(status<0)
{
perror("waitpid error");
break_loop = TRUE;
len = -1;
}
else if(child_id == status)
{
/*Valid staus from child*/
len = read_output(read_fd, output);
//write(1, output, len);
break_loop = TRUE;
}
else
{
}
break;
}
if(TRUE == break_loop)
{
break;
}
}
return len;
}
int do_ur_fun (int read_fd)
{
/*Do your exec*/
}
MaheshGupta024 identified a very important problem in your code; I'm assuming you will fix that.
One of the other problem areas is:
close(1);
if (dup2(pipefd[0], 0)==-1)
perror("Can't redirect stdin");
close(pipefd[1]);
for (i=1; i< argc; i++){
if ((wait(NULL))== -1)
perror("Wait error");
while ((c= read(pipefd[0], readin, SIZE)) > 0){;
//print buf count
total += atoi(readin);
}
}
The first close closes the process's standard output; this is seldom a good idea. The next line duplicates the read end of the pipe to standard input - which is fine. As noted in a comment above, perror() does not exit. You then close the write end of the pipe - that's correct; but you should presumably close the read end of the pipe too since you have set it to come from the pipe.
Your loop starts OK; you have redundant parentheses in the wait() line. You read from pipefd[0] instead of standard input - so maybe you didn't want to close pipefd[0] but neither did you need to duplicate it to standard input. You then have a nested loop that reads on the pipe while there's more data to be read from a child - you don't absolutely need the wait() code with its loop since the inner while won't terminate until all the children are dead. On the other hand, there's no great harm in it - after the first child dies, you'll read the data from all the other children, then go into the outer loop and wait for each other child, with the inner loop terminating immediately since there is no data left to read.
So:
Don't close stdout.
Don't dup the pipe read to stdin.
Decide whether you want to clean up the loop - it will work, but could be cleaner.
The run() function is:
void run(char *f){
int fp;
if ((fp= open(f, O_RDONLY)) == -1)
perror("Can't open the file");
close(pipefd[0]);
dup2(pipefd[1], 1);
close(pipefd[1]);
execlp("ls1", f, NULL);
}
The argument should be const char *f (or use name or file instead of f). I would also pass the pipefd array to the function rather than use a global variable
.
Do not call a file descriptor fp; that name conventionally indicates a variable of type FILE *, not int.
However, you don't need to open the file in the first place - unless you want the calling program to do the error reporting instead of the invoked program. However, if you do want the calling program to do the error reporting, you should close the file descriptor before proceeding. (I've already commented on perror() returning).
It would be a good idea to print an error message after execlp(); the only time the function returns is when it fails, so there is no need to test its return value. You might want to exit too - rather than have the failed function go through the rest of the main program after the call to run().
Good points: you did close both the pipe file descriptors.
Hence:
void run(const char *file, int *pipefd)
{
close(pipefd[0]);
dup2(pipefd[1], 1);
close(pipefd[1]);
execlp("ls1", f, NULL);
perror("Failed to exec ls1");
exit(EXIT_FAILURE);
}

Resources