piping for a simple shell in c - c

I'm trying to implement piping in a simple shell program that I'm writing in C.
But for some reason, I'm not getting output when I try to run ls | wc -l.
I'm really not sure why this is happening since I'm basically putting the child process's output to pipe[1] which does the command before pipe indicator and I'm putting parent's input to pipe[0] which does the command after pipe indicator and it should be printing to the terminal since the output of parent's never been changed, my approach right now is if piping is flagged the call fork in child and do the piping.
code below
int pipe1[2];
int pipepid;
int piping; /*flag for piping*/
int pipeposition;/*index of pipe indicator*/
//* code... */
if(pipe(pipe1)!= 0){
perror("pipe");
exit(1);
};
/* split commands to before pipe indicator and after */
for(int p = 0;p<pipeposition;p++){
argsbefore[p]=args[p];
}
/* after */
int e=0;
for(int h = pipeposition+1; h<cnt;h++){
argsafter[e]=args[h];
e++;
}
/* code ... */
if(piping){
pipepid = fork();
if(pid == 0){
/* do child */
if(dup2(pipe1[1],1)==-1){
perror("dup2 child");
exit(1);
}
close(pipe1[1]);
if (execvp(argsbefore[0], argsbefore) < 0) {
printf("exec failed\n");
exit(1);
}
exit(0);
}/* else if error */
else if(pid == -1){
printf("ERROR: fork failed\n");
exit(1);
}/* parent */
else{
if(dup2(pipe1[0],0)==-1){
perror("dup2 parent");
exit(1);
}
close(pipe1[0]);
if (execvp(argsafter[0], argsafter) < 0) {
printf("exec failed\n");
exit(1);
}
}
}

you seem to be doing that on a unix-like system. If you're lucky, your system might have a tool that reports every system call your program perform (strace -f my_program my_ar gu_ments would do that on Linux).
That would give you a list of what process did what and when, and whether there have been error code for some operations. That usually helps a lot with these multi-process setups.

It turns out I didn't close all the pipes so the second command wasn't able to finish, after putting close for both ends in the main parent process it was fixed

Related

Execution of UNIX command is being outputted after I exit the program

For some unknown reason, when I'm executing piped commands in my shell program, they're only outputting once I exit the program, anyone see why?
Code:
int execCmdsPiped(char **cmds, char **pipedCmds){
// 0 is read end, 1 is write end
int pipefd[2];
pid_t pid1, pid2;
if (pipe(pipefd) == -1) {
fprintf(stderr,"Pipe failed");
return 1;
}
pid1 = fork();
if (pid1 < 0) {
fprintf(stderr, "Fork Failure");
}
if (pid1 == 0) {
// Child 1 executing..
// It only needs to write at the write end
close(pipefd[0]);
dup2(pipefd[1], STDOUT_FILENO);
close(pipefd[1]);
if (execvp(pipedCmds[0], pipedCmds) < 0) {
printf("\nCouldn't execute command 1: %s\n", *pipedCmds);
exit(0);
}
} else {
// Parent executing
pid2 = fork();
if (pid2 < 0) {
fprintf(stderr, "Fork Failure");
exit(0);
}
// Child 2 executing..
// It only needs to read at the read end
if (pid2 == 0) {
close(pipefd[1]);
dup2(pipefd[0], STDIN_FILENO);
close(pipefd[0]);
if (execvp(cmds[0], cmds) < 0) {
//printf("\nCouldn't execute command 2...");
printf("\nCouldn't execute command 2: %s\n", *cmds);
exit(0);
}
} else {
// parent executing, waiting for two children
wait(NULL);
}
}
}
Output:
In this example of the output, I have used "ls | sort -r" as the example, another important note is that my program is designed to only handle one pipe, I'm not supporting multi-piped commands. But with all that in mind, where am I going wrong, and what should I do to fix it so that it's outputting within the shell, not outside it. Many thanks in advance for any and all advice and help given.
The reason would be your parent process file descriptors are not closed yet. When you wait for the second command to terminate, it hangs because the writing end is not closed so it wait until either the writing end is closed, or new data is available to read.
Try closing both pipefd[0] and pipefd[1] before waiting for process to terminate.
Also note that wait(NULL); will immediately return when one process has terminated, you would need a second one as to not generate zombies if your process still runs after that.

Pipes & exec & C

Hi i have the following problem , I have to create a program that performs these Linux commands ls –la | sort | wc –l but in my code i just can read two of this command , can sameone help me with this??
int main(){
pid_t pids[3];
int dados[2],dados2[2],i;
if(pipe(dados) == -1 && pipe(dados2) == -1){
perror("pipe failed");
exit(1);
}
for(i=0;i<3;i++){
pids[i] = fork();
if(pids[i] == 0){
if(i==0){
close(dados[0]);
dup2(dados[1],1);
close(dados[1]);
execlp("ls","ls","-la",NULL);
perror("exec failed");
exit(-1);
}
if(i==1){
close(dados[1]);
dup2(dados[0],0);
close(dados[0]);
close(dados2[0]);
dup2(dados2[1],1);
close(dados2[1]);
execlp("sort","sort",NULL);
perror("exec failed");
exit(-1);
}else{
close(dados2[1]);
dup2(dados2[0],0);
close(dados2[0]);
printf("aaaaaaaaaaaaaaaaaa");
execlp("wc","wc","-l",NULL);
perror("exec failed");
exit(-1);
}
}
}
/* Pai tem de fechar a sua copia da extremidade de escrita
para a leitura do sort desbloquear */
close(dados[1]);
close(dados2[0]);
for(i=0;i<3;i++){
wait(NULL);
}
return 0;
}
i dont understand what miss in this
What catches my eye immediatly is
-if(pipe(dados) == -1 && pipe(dados2) == -1){
+if(pipe(dados) == -1 || pipe(dados2) == -1){
perror("pipe failed");
exit(1);
}
The parent pid just needs to read the output of the last command, so the parent neeeds to close all other pipe ends. The child processes needs to close stdin/stdout and dup2() the pipe descriptors to bind them to stdin/stdout and then exec(). With pipes it is really important that you close the not needed pipe end in your process. See http://linux.die.net/man/2/pipe
Currently i dont have an linux box beneith me, so i cant write and test linux code.
My summary for you is:
- parent reads from the last pipe (stdout of the last process in the chain)
- all other childs need to close() their stdin/stdout, dup2() the right pipeend, close all unneeded pipes (remember to close the dup2() source too, because it is a fd) and then exec.
Here is an example how i did create a pipe and rebind the fds.
// unset FD_CLOEXEC
set_close_on_exec(to_child,false);
set_close_on_exec(from_child, false);
// close and rebind the stdin/stdout/stderr
// dup the fd and recreate stdin/stdout with fd as the target
if (dup2(to_child, STDIN_FILENO) != 0 || dup2(from_child, STDOUT_FILENO) != 1) {
shared::perror("error duplicating socket for stdin/stdout");
exit(EXIT_FAILURE);
}
// close these FDs
close(to_child);
close(from_child);
// now we can exec the new sub process and talk to it through
// stdin/stdout/stderr
execlp(exe.c_str(), exe.c_str(), argv.c_str(), (char*)0);
// this should never be reached
shared::perror("error: executing the binary: " + exe);
exit(EXIT_FAILURE);
Remember that at pipe(int fds[2]) the index 1 is the write end of the pipe and index 0 is the read end of the pipe. So you need
Regards
Georg
Edit: I recommend you the book "The Linux Programming Interface: A Linux and UNIX System Programming Handbook" ISBN-13: 978-1593272203

implementing a shell in C

im currently implementing a shell in C.
My problem arises when i try to run a command like this:
SHELL$: sort < txtFile | grep key
im running sort < txtFile in a process (child), and in the parent i.e else if(pid > 0) im running the other command to the right of the pipe.
The program runs fine, but it exits the infinite loop that i set up in main to keep receiving input from the user.
How could i solve this problem?
this is the code i have so far to deal with the pipe, i didnt include the code that i have to deal with the redirects:
c2p is the pipe i setup for this.
if(pid == 0)
{
if( PIPE_FLAG )
{
close(c2p[0]);
if(dup2(c2p[1], STDOUT_FILENO) == -1){
perror("dup2() failed");
exit(2);
}
}
/* Execute command */
execvp(cmd_args[0], cmd_args);
perror("exec failed 1. "); /* return only when exec fails */
exit(-1);
}
else if(pid > 0)
{
if(PIPE_FLAG)
{
close(c2p[1]);
if(dup2(c2p[0], STDIN_FILENO) == -1){
perror("dup2() failed");
exit(-1);
}
execvp(nxt_args[0], nxt_args);
perror("exec failed 2. ");
exit(-1);
}
}
else
{
/* error occurred */
perror("fork failed");
exit(1);
}
I'm running sort < txtFile in the child process, and in the parent I'm running the command to the right of the pipe.
What happens to your shell process, then? The parent process is the shell. By running the right-side command in the parent process you're having it take over the shell's process. Remember that exec() replaces the current process.
You'll need to fork() twice, and execute the two sides of the pipe in the child processes. The parent must remain the shell, which will then wait() for the children to exit before presenting the next command prompt.
/* How shell works */
#include<stdio.h>
#include<unistd.h>
main (int argc, char **argv)
{
if (argc < 2)
{
fprintf (stderr, "\nUsage: ./a.out cmd [options]...\n");
}
if (!fork ())
{
argv++;
execvp (argv[0], argv);
}
}

Troubles with a pipe and a fork

I'm making a program that search files and sends it's results to other commands, like a pipe. ls | sort
When I run the program nothing happens.The problem I think is that the child's waits for the parent to stop writting in the SO buffer for starting the reading.
This is what it sends to stdout and what the pipe should send to the other command.
troneras#troneras-VirtualBox:~/Escritorio/busca.2012$ ./busca . -n . -print
./permisos.txt
./busca2.c
./mmap.pdf
./busca3.c~
./cuadernoso4.2011b.pdf
./busca.c~
./busca.c
./busca2.c~
./busca3.c
I don't understand what the problem is.
if(!strcmp(argv[4],"-pipe"))
{
int pipefd[2];
int pid,dummi;
if (pipe(pipefd)<0){
perror("pipe");
exit(1);
}
pid = fork();
if (pid<0){
perror("fork");
exit(1);
}
if (pid == 0){//Child process
close(pipefd[1]);//The child is only reading from the pipe
if(dup2(pipefd[0],0)!=0){perror("dup2");exit(1);}
close(pipefd[0]);
char *argumentos[argc-4];
int j;
for (j=5;j<argc;j++){
argumentos[j-5]=argv[j];
}
argumentos[j-5]= NULL;
execvp(argv[5],argumentos);
perror("execve: ");
}else{ //parent
close(pipefd[0]);
if(dup2(pipefd[1],1)!=1){perror("dup2");exit(1);}
close(pipefd[1]);
while(count--){
if(strcmp(files[count]->d_name,".") && strcmp(files[count]->d_name,"..")){
printf("%s/%s\n",argv[1],files[count]->d_name);
free(files[count]);
}
wait(&dummi);
}
}//end pipe
free(files);
BTW There is no reason to duplicate the argv[] array. Instead of
char *argumentos[argc-4];
int j;
for (j=5;j<argc;j++){
argumentos[j-5]=argv[j];
}
argumentos[j-5]= NULL;
execvp(argv[5],argumentos);
You could just as well do
execvp(argv[5],argv+5);

How to catch the ouput from a execl command

I'm using the execl function to run a Linux process from C. When I do, for example:
int cmd_quem() {
int result;
result = fork();
if(result < 0) {
exit(-1);
}
if (result == 0) {
execl("/usr/bin/who", "who", NULL);
sleep(4); //checking if father is being polite
exit(1);
}
else {
// father's time
wait();
}
return 0;
}
I get on the console the result of doing "who" on the terminal. What I'd like to know is if there is any function to "catch" the output result from a command. What I mean is, if there is anyway to catch this:
feuplive tty5 2009-11-21 18:20
Which is one of the lines resulting from the who command.
To do this, you need to open a pipe. You then replace the child's stdout with the writing end of the pipe, and read from the reading end of the pipe in the parent. Like this modified version of your code:
int cmd_quem(void) {
int result;
int pipefd[2];
FILE *cmd_output;
char buf[1024];
int status;
result = pipe(pipefd);
if (result < 0) {
perror("pipe");
exit(-1);
}
result = fork();
if(result < 0) {
exit(-1);
}
if (result == 0) {
dup2(pipefd[1], STDOUT_FILENO); /* Duplicate writing end to stdout */
close(pipefd[0]);
close(pipefd[1]);
execl("/usr/bin/who", "who", NULL);
_exit(1);
}
/* Parent process */
close(pipefd[1]); /* Close writing end of pipe */
cmd_output = fdopen(pipefd[0], "r");
if (fgets(buf, sizeof buf, cmd_output)) {
printf("Data from who command: %s\n", buf);
} else {
printf("No data received.\n");
}
wait(&status);
printf("Child exit status = %d\n", status);
return 0;
}
First, execl does not return unless there's a problem like the executable is not found. That sleep(4) is probably never executed.
As for redirecting and getting the output, check out the Unix Programming FAQ. Look for spawn_background_command.
The exec() family of functions creates a new process image from a regular, executable file. This file is either an executable object file, or an interpreter script. There is no return from a successful call to an exec() function, because the calling process is functionally replaced by the new process.
So any code after exec() is never executed unless it is failed.
If you want to capture output of a shell command you need popen.

Resources