Reputation: 107
The goal is to implement terminal command prog1 > file && prog2 | prog3 given the arguments
prog1
,file
,prog2
andprog3
.
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/stat.h>
#include <fcntl.h>
int main(int argc, char **argv)
{
int fd,status,pipefd[2];
pipe(pipefd);
/* S_IRWXU - full acces to file */
fd = open(argv[2], O_WRONLY | O_CREAT | O_TRUNC, S_IRWXU);
if (!fork()){
dup2(fd, 1); /* redirection to file */
close(fd); /* it's useless now */
execlp(argv[1], argv[1], NULL);
}
else {
wait(&status);
if (!status){
if (fork()){
dup2(pipefd[1],1);
close(pipefd[1]);
close(pipefd[0]);
execlp(argv[3], argv[3],NULL);
}
wait(NULL);
dup2(pipefd[0],0);
close(pipefd[0]);
close(pipefd[1]);
execlp(argv[4], argv[4],NULL);
}
}
return 0;
}
I can't make this code to show data that last programme got from previous one.
For example (assume programme's name is prog
):
./prog ps f date echo
doesn't show current date. And I don't understand why. Can you explain?
Upvotes: 0
Views: 72
Reputation: 753495
If you run:
date | echo
at the command line prompt of a shell, you will get a newline and no date. Change echo
to cat
and you will see the date:
$ date | echo
$ date | cat
Mon Dec 11 16:35:54 PST 2017
$
You could fix your code so it works better on errors:
#include <stdio.h>
#include <unistd.h>
#include <sys/wait.h>
#include <sys/stat.h>
#include <fcntl.h>
int main(int argc, char **argv)
{
if (argc != 5)
{
fprintf(stderr, "Usage: %s prog1 file prog2 prog3\n", argv[0]);
return 1;
}
int pid = fork();
if (pid < 0)
{
fprintf(stderr, "%s: failed to fork\n", argv[0]);
return 1;
}
else if (pid == 0)
{
/* S_IRWXU - full access to file */
int fd = open(argv[2], O_WRONLY | O_CREAT | O_TRUNC, S_IRWXU);
if (fd < 0)
{
fprintf(stderr, "%s: failed to open file %s for writing\n", argv[0], argv[2]);
return 1;
}
dup2(fd, 1); /* redirection to file */
close(fd); /* it's not needed now */
execlp(argv[1], argv[1], NULL);
fprintf(stderr, "%s: failed to execute %s\n", argv[0], argv[1]);
return 1;
}
else
{
int status;
wait(&status);
if (status == 0)
{
int pipefd[2];
pipe(pipefd); // Error check?
pid = fork();
if (pid < 0)
{
fprintf(stderr, "%s: failed to fork\n", argv[0]);
return 1;
}
else if (pid == 0)
{
dup2(pipefd[1], 1);
close(pipefd[1]);
close(pipefd[0]);
execlp(argv[3], argv[3], NULL);
fprintf(stderr, "%s: failed to execute %s\n", argv[0], argv[3]);
return 1;
}
else
{
dup2(pipefd[0], 0);
close(pipefd[0]);
close(pipefd[1]);
execlp(argv[4], argv[4], NULL);
fprintf(stderr, "%s: failed to execute %s\n", argv[0], argv[4]);
return 1;
}
}
}
return 0;
}
A lot of C programming is about error handling. Having a function library that makes error reporting simpler helps enormously. For example, I have a library with header file stderr.h
and implementation in stderr.c
— source available from GitHub. It simplifies (and improves) the error reporting:
#include <stdio.h>
#include <unistd.h>
#include <sys/wait.h>
#include <sys/stat.h>
#include <fcntl.h>
#include "stderr.h"
int main(int argc, char **argv)
{
err_setarg0(argv[0]);
if (argc != 5)
err_usage("prog1 file prog2 prog3");
int pid = fork();
if (pid < 0)
err_syserr("failed to fork: ");
else if (pid == 0)
{
/* S_IRWXU - full access to file */
int fd = open(argv[2], O_WRONLY | O_CREAT | O_TRUNC, S_IRWXU);
if (fd < 0)
err_syserr("failed to open file %s for writing: ", argv[2]);
dup2(fd, 1); /* redirection to file */
close(fd); /* it's not needed now */
execlp(argv[1], argv[1], NULL);
err_syserr("failed to execute %s: ", argv[1]);
}
else
{
int status;
wait(&status);
if (status == 0)
{
int pipefd[2];
pipe(pipefd); // Error check?
pid = fork();
if (pid < 0)
err_syserr("failed to fork");
else if (pid == 0)
{
dup2(pipefd[1], 1);
close(pipefd[1]);
close(pipefd[0]);
execlp(argv[3], argv[3], NULL);
err_syserr("failed to execute %s: ", argv[3]);
}
else
{
dup2(pipefd[0], 0);
close(pipefd[0]);
close(pipefd[1]);
execlp(argv[4], argv[4], NULL);
err_syserr("failed to execute %s: ", argv[4]);
}
}
}
return 0;
}
For example, the err_syserr()
function reports the system error number and message as well as the command name (prog41
) and the message specified in the call. (And yes, there are ways to compress both programs a bit more — the else
on the outer if
block could be removed, leaving the following code one level less indented (and the code three lines shorter), for example.)
This program was prog41
, and the test output could be:
$ ./prog41 rigmarole /dev/not-there fudge pumpkin
prog41: failed to open file /dev/not-there for writing: error (1) Operation not permitted
$ ./prog41 rigmarole not-there fudge pumpkin
prog41: failed to execute rigmarole: error (2) No such file or directory
$ ./prog41 ps not-there fudge pumpkin
prog41: failed to execute pumpkin: error (2) No such file or directory
prog41: failed to execute fudge: error (2) No such file or directory
$ ./prog41 ps not-there date pumpkin
prog41: failed to execute pumpkin: error (2) No such file or directory
$ ./prog41 ps not-there date cat
Mon Dec 11 16:49:11 PST 2017
$ ./prog41 proxy mangler
Usage: prog41 prog1 file prog2 prog3
$
After running the program successfully, the file not-there
was there, of course.
Upvotes: 2
Reputation: 29
You have three exec's, but 2 forks. I personally would do three forks (one for each exec), and have 2 pipes to communicate within the processes.
Upvotes: -1