Dup2-------------------------------------------------------------------------------------------------------
dup2(fd1, fd2) ; will redirect fd2 to fd1
// This program is an example of how to run a command such as
// ps aux | grep root | grep sbin
// using C and Unix.
#include <stdlib.h>
#include <unistd.h>
int pid;
int pipe1[2];
int pipe2[2];
void main() {
// create pipe1
if (pipe(pipe1) == -1) {
perror("bad pipe1");
exit(1);
}
// fork (ps aux)
if ((pid = fork()) == -1) {
perror("bad fork1");
exit(1);
} else if (pid == 0) {
// stdin --> ps --> pipe1
exec1();
}
// parent
// create pipe2
if (pipe(pipe2) == -1) {
perror("bad pipe2");
exit(1);
}
// fork (grep root)
if ((pid = fork()) == -1) {
perror("bad fork2");
exit(1);
} else if (pid == 0) {
// pipe1 --> grep --> pipe2
exec2();
}
// parent
// close unused fds
close(pipe1[0]);
close(pipe1[1]);
// fork (grep sbin)
if ((pid = fork()) == -1) {
perror("bad fork3");
exit(1);
} else if (pid == 0) {
// pipe2 --> grep --> stdout
exec3();
}
// parent
}
void exec1() {
// input from stdin (already done)
// output to pipe1
dup2(pipe1[1], 1);
// close fds
close(pipe1[0]);
close(pipe1[1]); //***
// exec
execlp("ps", "ps", "aux", NULL);
// exec didn't work, exit
perror("bad exec ps");
_exit(1);
}
void exec2() {
// input from pipe1
dup2(pipe1[0], 0);
// output to pipe2
dup2(pipe2[1], 1);
// close fds
close(pipe1[0]);
close(pipe1[1]);
close(pipe2[0]);
close(pipe2[1]);
// exec
execlp("grep", "grep", "root", NULL);
// exec didn't work, exit
perror("bad exec grep root");
_exit(1);
}
void exec3() {
// input from pipe2
dup2(pipe2[0], 0);
// output to stdout (already done)
// close fds
close(pipe2[0]);
close(pipe2[1]);
// exec
execlp("grep", "grep", "sbin", NULL);
// exec didn't work, exit
perror("bad exec grep sbin");
_exit(1);
}
*** The effect of using close on a pipe end that is duplicated is explained here: https://www.youtube.com/watch?v=PIb2aShU_H4
From the perspective of exec being successfully executed, when the process terminates the file descripors are inherited and closed automatically, so this is not mandatory, but may save some space in the FD table.
Popen-------------------------------------------------------------------------------------------------------
popen - pipe to/from shell
FILE *popen(const char *command, const char *type);//create pipe, do a fork, exec the ``çommand'`
int pclose(FILE *stream); //close pipe, wait for child process
Typically usage is:
FILE *fp;
char *command;
/* command contains the command string (a character array) */
/* If you want to read output from command */
fp = popen(command,"r");
/* read output from command */
fscanf(fp,....); /* or other STDIO input functions */
pclose(fp);
/* If you want to send input to command */
fp = popen(command,"w");
/* write to command */
fprintf(fp,....); /* or other STDIO output functions */
pclose(fp);
The program cannot both send input to the command and read its output. The file fp behaves like an ordinary STDIO file.
Here is an example showing how to use popen and pclose to filter output through another program, in this case the paging program more.
#include <stdio.h>
#include <stdlib.h>
void
write_data (FILE * stream)
{
int i;
for (i = 0; i < 100; i++)
fprintf (stream, "%d\n", i);
if (ferror (stream))
{
fprintf (stderr, "Output to stream failed.\n");
exit (EXIT_FAILURE);
}
}
int main (void)
{
FILE *output;
output = popen ("more", "w");
if (!output)
{
fprintf (stderr, "incorrect parameters or too many files.\n");
return EXIT_FAILURE;
}
write_data (output);
if (pclose (output) != 0)
{
fprintf (stderr, "Could not run more or other error.\n");
}
return EXIT_SUCCESS;
}
==========================================================================================
The following program uses popen to execute ls -l | sort
Possible solution
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
int main(){
FILE *output;
FILE *f2;
f1=popen("ls -l","r");
f2=popen("sort","w");
char line[50];
while (fgets(line,50,output)){
fprintf(f2,"%s",line);
}
pclose(f1);
pclose(f2);
}
Training:
1. Modify the program to execute ls -l | sort | awk '{print $1}' keeping the first two popen calls and adding either an additional popen or combination of fork, exec and dup2.
2. Write the same program to execute ls -l | sort or ls -l |sort | awk '{print$1}' using only fork, exec, pipe and dup2 (no popen).
3. Use one popen call to execute combination of commands ps -f -u | sort | grep -v "PID" such as popen("ps -f -u | sort | grep -v "PID" ", "r") and display the output on the screen. What do you see, which are the running processes, and how many? Run this combination the the fork+dup2+exec+ pipe solution from 2 and compare the output - the type and number of processes running.