Dealing with pipes in C - c

I am trying to implement a shell in C language on Linux.This project that I am working on, asks to create a shell in C, starting from creating a very basic one (myShell), which goes deeper step by step.First I had to create a shell with simple commands like
ls,pwd,echo,date,time
(shell1).
After that, my Shell had to be improved so it could sort things from files (txt's for example) (shell2) and as it goes on, I had to do more by developing it, and make it execute commands like
sort -r,sort -u.(shell3).
Until the 3rd shell, I was working with redirections and everything was going well.
Now for the 4th shell, I am supposed to make it run commands with pipes, e.g. ls -l /home/ | sort > out.txt. I have managed to make the command work, the out.txt file gets created succesfully and is sorted accordingly. There is a while() on my code, so that after every command that I give to the shell it asks for the next one etc. etc.. But when the above example command is given and the pipes are used, the program stops. The terminal doesn't show "myShell4>" but Desktop$ and it basically exits the shell. Giving it simple commands like "ls -l" ,that don't use the pipes, works perfectly, so from that I realise that the problem is in the pipes and that they stop my program from looping.
The part where this happens in my code:
//CHILD
dup2(pipefd[1],1);
close(pipefd[0]);
execute(cmd,path,argm);
//PARENT
dup2(pipefd[0],0);
close(pipefd[1]);
execlp(cmd2,cmd2,NULL);
Any thoughts? Thanks in advance!

The parent is the shell, right? Don't exec there; create children for both ends of the pipe and wait for them in the parent. If you do, the shell will be replaced and no longer run after the command ends.
Below is some pseudo-code for a pipe between two commands:
int pipefd[2];
pipe (pipefd);
// child for first command
if (fork () == 0) {
// setup in redirection if any
...
// setup out redirection
close (pipefd[0]);
dup2 (pipefd[1], STDOUT_FILENO);
...
exec (cmd1, ...);
exit (1);
}
// child for second command
if (fork () == 0) {
// setup in redirection
close (pipefd[1]);
dup2 (pipefd[0], STDIN_FILENO);
// setup out redirection if any
dup2 (output_file_fd, STDOUT_FILENO);
exec (cmd2, ...);
exit (1);
}
close (pipefd[0]);
close (pipefd[1]);
// parent waits and then restarts the loop
wait (NULL);
wait (NULL);
Things get more complicated for a list of more than two commands connected by pipes.

Related

C - Redirecting IO of Child Process

I am trying to redirect the IO of a child process (after fork()) into a file, and I can't figure out why it isn't working.
Here's what I've done:
if(fork() == 0){
execv(exe, (char*[]){ exe, "> temp.exe" });
...
And the executable runs, but it doesn't redirect to the file. I would appreciate it if anyone could explain what am I doing wrong, and how I should do it. I'm getting a feeling I need to redirect before the execv() but I have no idea how to.
Thanks in advance!
Shell redirections (like > file) are implemented by the shell. By using execve(), you are bypassing the shell; the child process will see "> temp.exe" in argv, and will attempt to process it as an argument.
If you want to redirect output to a file, the easiest approach will be to implement that redirection yourself by opening the file after forking and using dup2() to move its file descriptor to standard output:
if (fork() == 0) {
int fd = open("temp.exe", O_CREAT | O_WRONLY, 0666);
if (fd < 0) { handle error... exit(255); }
dup2(fd, 1);
close(fd);
execv(exe, ...);
}
The execX() family of calls does not have the same flexibility as, say system() or popen(). These latter methods call shell to do the interpretation of the command.
The arguments to the execX call are the exact path of the program you want to run and the arguments you want to give to that program. Any "shell" features such as redirection you have to implement yourself before calling execX.
Alternatively, you can let shell actually do the work, execp("sh","sh",myexe+" >test.txt");, but that is lazy, and then why not just use system anyway?
Two very useful methods are pipe() and dup2(): pipe allows you to create pipes to your host program; dup2 lets you set a scenario where the program being executed thinks that it is writing to stdout (1), or reading from stdin (0), but is actually writing or reading to a file or pipe that you created.
You will get a long way by reading the man pages for pipe and dup2, or in google looking for exec pipe and dup2, so I won't take your enjoyment away by writing a full implementation here.

Execlp execute in another terminal

I am creating an application in C which I have to execute the firefox with the command execlp but every time I execute it I "lost" my current terminal, but after the execlp i still need to use the terminal which I was before, so my question is: Is there a way where I can be in one terminal call execlp and it executes in another one without block the one I am on?
here is a snippet of my code:
pid_t child = fork();
if (child == -1) {
perror("fork error");
} else if (child == 0) {
exec_pid = getpid();
execlp("firefox", "firefox", URL, NULL);
perror("exec error");
}
// keep with program logic
If I'm understanding you correctly, you're saying that your program launches Firefox and then keeps control of your shell until Firefox terminates. If this is the case, there are a couple of ways around this.
The easiest solution is to run your program in the background. Execute it like ./my_program & and it be launched in a separate process and control of your terminal will be returned to you immediately.
If you want to solve this from your C code, the first step would be to print out the process ID of the child process after the fork. In a separate shell, use ps to monitor both your program and the forked PID. Ensure that your program is actually terminating and that it's not just stuck waiting on something.

"more" as a target of piped command breaks bash

Consider following source, reduced for simplicity
int main()
{
int d[2];
pipe(d);
if(fork())
{
close(1);
dup(d[1]);
execlp("ls", "ls", NULL);
}
else
{
close(0);
dup(d[0]);
execlp("cat", "cat", NULL);
}
}
So it creates a pipe and redirects the output from ls to cat.
It works perfectly fine, no problems. But change cat to more and bash breaks.
The symptoms are:
you don't see anything you type
pressing "enter" shows up a new prompt, but not in a new line, but in the same one
you can execute any command and see the output
reset helps fixing things up.
So there is a problem with input from keyboard, it is there, but is not visible.
Why is that?
UPDATE:
the output from ls | more is equivalent to the output of my program
more process does not finish, it's is orphaned by ls
the only visible problem is with the state of the console after the parent process quits
on some systems it does work like intended. E.g., on OpenSUSE I had no problems, on Kubuntu. I couldn't find any information on what differences should I look for, more binaries are different on both systems
Because unlike cat, more is an interactive program that requires more than stdin, stdout and stderr -- it requires a terminal, which your system call cannot provide. Try to run more in a pipe or from a script and see what happens.
Also note that bash is not involved here at any stage, the execlp function call replaces the current process by another one specified as an argument.

How to know if a command given to execlp() exists?

I've searched quite a lot, but I still don't have an answer for this. I've got a program that creates other processes by asking the user the desired command, then I use execlp to open this new process. I wanted to know if there's an easy way to the parent process find out if the command was executed, or if the received command doesn't exist.
I have the following code:
if (executarComando(comando) != OK)
fprintf(stderr,"Nao foi possivel executar esse comando. ");
where executarComando is:
int executarComando(char* cmd) {
if ( execlp("xterm", "xterm", "-hold", "-e", cmd, NULL) == ERROR) // error
return ERROR;
return OK;
}
Your problem is that your execlp always succeeds; it's running xterm, not the command you're passing to the shell xterm runs. You will need to add some kind of communication channel between your program and this shell so that you can communicate back success or failure. I would do something like replacing the command with
( command ) 99>&- ; echo $? >&99
Then, open a pipe before forking to call execlp, and in the child, use dup2 to create as file descriptor number 99 corresponding to the write end of the pipe. Now, you can read back the exit status of the command across the pipe.
Just hope xterm doesn't go closing all file descriptors on you; otherwise you're out of luck and you'll have to make a temporary fifo (via mkfifo) somewhere in the filesystem to achieve the same result.
Note that the number 99 was arbitrary; anything other than 0, 1, or 2 should work.
There's no trivial way; a convention often used is that the fork()ed child will report the error and exit(-1) (or exit(255)) in the specific case where the exec() fails, and most commands avoid using that for their own failure modes.

capturing commandline output directly in a buffer

I want to execute a command using system() command or execl and want to capture the output directly in a buffer in C. Is ther any possibility to capture the output in a buffer using dup() system call or using pipe(). I dont want to use any file in between using mkstemp or any other temporary file. please help me in this.Thanks in advance.
I tried it with fork() creating two process and piping the output and it is working.However I dont want to use fork system call since i am going to run the module infinitely using seperate thread and it is invoking lot of fork() and system is running out of resources sometimes after.
To be clear about what i am doing is capturing an output of a shell script in a buffer processing the ouput and displaying it in a window which i have designed using ncurses.Thankyou.
Here is some code for capturing the output of program; it uses exec() instead of system(), but that is straightforward to accomodate by invoking the shell directly:
How can I implement 'tee' programmatically in C?
void tee(const char* fname) {
int pipe_fd[2];
check(pipe(pipe_fd));
const pid_t pid = fork();
check(pid);
if(!pid) { // our log child
close(pipe_fd[1]); // Close unused write end
FILE* logFile = fname? fopen(fname,"a"): NULL;
if(fname && !logFile)
fprintf(stderr,"cannot open log file \"%s\": %d (%s)\n",fname,errno,strerror(errno));
char ch;
while(read(pipe_fd[0],&ch,1) > 0) {
//### any timestamp logic or whatever here
putchar(ch);
if(logFile)
fputc(ch,logFile);
if('\n'==ch) {
fflush(stdout);
if(logFile)
fflush(logFile);
}
}
putchar('\n');
close(pipe_fd[0]);
if(logFile)
fclose(logFile);
exit(EXIT_SUCCESS);
} else {
close(pipe_fd[0]); // Close unused read end
// redirect stdout and stderr
dup2(pipe_fd[1],STDOUT_FILENO);
dup2(pipe_fd[1],STDERR_FILENO);
close(pipe_fd[1]);
}
}
A simple way is to use popen ( http://www.opengroup.org/onlinepubs/007908799/xsh/popen.html), which returns a FILE*.
You can try popen(), but your fundamental problem is running too many processes. You have to make sure your commands finish, otherwise you will end up with exactly the problems you're having. popen() internally calls fork() anyway (or the effect is as if it did).
So, in the end, you have to make sure that the program you want to run from your threads exits "soon enough".
You want to use a sequence like this:
Call pipe once per stream you want to create (eg. stdin, stdout, stderr)
Call fork
in the child
close the parent end of the handles
close any other handles you have open
set up stdin, stdout, stderr to be the appropriate child side of the pipe
exec your desired command
If that fails, die.
in the parent
close the child side of the handles
Read and write to the pipes as appropriate
When done, call waitpid() (or similar) to clean up the child process.
Beware of blocking and buffering. You don't want your parent process to block on a write while the child is blocked on a read; make sure you use non-blocking I/O or threads to deal with those issues.
If you are have implemented a C program and you want to execute a script, you want to use a fork(). Unless you are willing to consider embedding the script interpreter in your program, you have to use fork() (system() uses fork() internally).
If you are running out of resources, most likely, you are not reaping your children. Until the parent process get the exit code, the OS needs keeps the child around as a 'zombie' process. You need to issue a wait() call to get the OS to free up the final resources associated with the child.

Resources