I've an input file whose content is abcefghz. I want to use a pipe so that a child process p1 sends 1-letter per time to his parent process which will converts this string using the ASCII code+1 (abcefghz will become cdfghi{). This new string will be send through another pipe to another child process which will print the result on an output file.
This is the code:
int main (int argc, char **argv)
{
pid_t pid1, pid2;
int inputFile, outputFile;
char stringaDalFile[256];
char successivo;
char stringaRisultato[256];
int fd1[2], fd2[2]; // Pipe
inputFile = open(argv[1], O_RDONLY);
outputFile = open(argv[2], O_WRONLY | O_CREAT, S_IRUSR | S_IWUSR);
pipe(fd1);
pipe(fd2);
pid1 = fork();
if (pid1 == 0) {
while ( (nread=read(inputFile, stringaDalFile, 1)) > 0) {
close(fd1[0]);
write(fd1[1], stringaDalFile, 1);
}
close(inputFile);
}
else {
close(fd1[1]);
while ( read(fd1[0], stringaDalFile, 1) > 0 ) {
successivo = converti(stringaDalFile[0]);
write(fd2[1], &successivo, 1);
}
}
pid2 = fork();
if (pid2 == 0) {
close(fd2[1]);
if (read(fd2[0], stringaRisultato, 1) == -1) {
perror("Errore");
exit(1);
}
else {
while ( read(fd2[0], stringaRisultato, 1) > 0 ) {
write(STDOUT_FILENO, stringaRisultato, strlen(stringaRisultato)); //dbg
write(outputFile, stringaRisultato, strlen(stringaRisultato));
}
}
close(outputFile);
exit(0);
}
return 0;
}
char converti (char carattere)
{
return carattere+1;
}
Unfortunately, this seems to not work at 100%, the string is converted but the program enters what seems an infinite loop:
If I CTRL-C and gedit file2.txt, this is its content:
.
How do I fix this?
There are a few problems in your code:
The real problem, here:
write(outputFile, stringaRisultato, strlen(stringaRisultato));
you're using strlen(stringaRisultato) when stringaRisultato only has one valid character at the beginning and no NUL-terminator after it. Just use 1 instead.
You are only reading one character at a time, you don't need a string of 256 characters. Change stringaDalFile and stringaRisultato into two single char variables: carattereDalFile and carattereRisultante.
In the first child (inside if (pid1 == 0)) you are doing close(fd1[0]) in a loop. You should move it out of the while.
Again in the first child, you're not doing exit(0). This is what causes your program to keep running.
This line inside the second child:
if (read(fd2[0], stringaRisultato, 1) == -1) {
is only useful if you want to skip the first character. Is that intended? If not, remove that if and only use the while (read(...) > 0).
You are writing everything to the pipe fd2[1] before starting the second child (that will read from it). If the input file is too large (some KB) this will result in filling the pipe internal buffer and will block your program on the next write() that it tries to perform, making it never end. To fix this, you should start the two child processes together. This would require changing the structure and logic of the code, but it's doable.
Since I don't think that's the real problem here and this program is most probably written for educational purposes I'll leave that to you and fix the rest of the above points.
Working code:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
char converti (char carattere);
int main (int argc, char **argv)
{
pid_t pid1, pid2;
int inputFile, outputFile;
char carattereDalFile, carattereRisultante, successivo; // renamed
int fd1[2], fd2[2];
inputFile = open(argv[1], O_RDONLY);
outputFile = open(argv[2], O_WRONLY | O_CREAT, S_IRUSR | S_IWUSR);
pipe(fd1);
pipe(fd2);
pid1 = fork();
if (pid1 == 0) {
close(fd1[0]); // <== moved out of the while loop
while (read(inputFile, &carattereDalFile, 1) > 0) {
write(fd1[1], &carattereDalFile, 1);
}
close(inputFile);
exit(0); // <== added
} else {
close(fd1[1]);
while (read(fd1[0], &carattereDalFile, 1) > 0) {
successivo = converti(carattereDalFile);
write(fd2[1], &successivo, 1);
}
}
pid2 = fork();
if (pid2 == 0) {
close(fd2[1]);
if (read(fd2[0], &carattereRisultante, 1) == -1) {
perror("Errore");
exit(1);
} else {
while (read(fd2[0], &carattereRisultante, 1) > 0) {
write(outputFile, &carattereRisultante, 1);
}
}
close(outputFile);
exit(0);
}
return 0;
}
char converti (char carattere)
{
return carattere+1;
}
read returns the number of byte read in so you should use that to add the NUL terminating character to the string it's populated like this
while ( (bytesread = read(inputFile, stringaDalFile, 1)) > 0) {
stringaDalFile[bytesread] = '\0';
....
Related
I'm having the following code:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main(int argc, char *argv[]) {
if (argc != 2) {
fprintf(stderr, "Usage: %s <file>\n", argv[0]);
exit(-1);
}
int pfd[2]; // pipe
int pfd2[2]; // pipe2
pid_t pid; // child
if (pipe(pfd) < 0) {
fprintf(stderr, "error.\n");
exit(-1);
}
if (pipe(pfd2) < 0) {
fprintf(stderr, "error.\n");
exit(-1);
}
if ((pid = fork()) < 0) {
fprintf(stderr, "error.\n");
exit(-1);
}
// child process
if (pid == 0) {
close(pfd[1]);
int v[100] = {0};
int k = 0;
int r;
char buf[128];
while ((r = read(pfd[0], buf, sizeof(buf)))) {
buf[r] = '\0';
// some processing here vor the 'v' variable
}
close(pfd[0]); // close the reading end 1st pipe
dup2(pfd2[1], 1); // redirect
close(pfd2[1]);
execlp("bash", "bash", "script.sh", v, NULL);
exit(-1);
} else {
// parent code
close(pfd2[1]);
dup2(pfd2[0], 0);
char buf[16];
int r = 0;
while ((r = read(pfd2[0], buf, sizeof(buf)))) {
buf[r] = '\0';
// read here from second pipe
}
close(pfd2[0]); // close reading end from 2pnd pipe
close(pfd[0]); // close reading end from 1st pipe
dup2(pfd[1], 1); // redirect
execlp("cat", "cat", argv[1], NULL); // exec 'cat' on the given arg
exit(-1);
}
return 0;
}
That respect the following flow: the parent process executes the 'cat' command on the given arg file, then pass it to the child -> the child does some processing and then stores values in the 'v' variable -> then executes the 'script.sh' script with the values taken from the 'v' variable and then passes the output to the parent which will print to stdout the output based on the result from the child.
I'm unsure where should the code for the 2nd pipe reading should go, I'm pretty sure that's the problem right now.
Can anyone take a look at it and point where the problem is? Thanks!
I am trying to understand why my program hangs. The Parent sends input froma
file it reads to the child program, and the child program will send the result of its computation back to it's parent. However, I have trouble sending the message back through a second pipe. The parent seems to hang when reading from the pipe.
From the other posts, I have read it seems to indicate that the parent should wait for the child to finish by using wait or waitpid (which in my case both of them does not resolve my issue).
I have notice by adding print statement that neither the PARENT or the CHILD finishes.. Could someone please explain to me why this is happening?
Why does this not work?
int main(int argc,char** argv) {
char buffer[1];
int i;
int fd1[2]; int fd2[2];
pipe(fd1); pipe(fd2);
pid_t pid;
// FIRST PROCESS.
// -------------------
pid = fork();
if(pid == 0) {
int cnt;
dup2(fd1[0], STDIN_FILENO);
dup2(fd2[1], STDOUT_FILENO);
for (i = 0; i < 2; i++) {
close(fd1[i]);
close(fd2[i]);
}
while(read(STDIN_FILENO, buffer, sizeof(buffer)) > 0) {
fprintf(stderr, "( %s )", buffer);
cnt = cnt + *buffer - 48;
}
write(STDOUT_FILENO, &cnt, sizeof(cnt));
exit(0);
}
// PARENT.
// ------------------------
int file = open(argv[1], O_RDONLY);
// READ THE FILE.
while(read(file, buffer, 1) > 0) {
if (48 <= *buffer && *buffer <= 57) {
// PIPE TO CHILD.
write(fd1[1], buffer, 1);
}
}
// WAIT FOR CHILD TO FINISH SENDING BACK.
// int status = 0;
// waitpid(pid, &status, 0);
// THIS BLOCK DOESN'T RESOLVE ANYTHING. IT HANGS AT WAIT OR WAITPID.
// **** THIS IS THE PART WHERE IT DOESN'T WORK.
while(read(fd2[0], buffer, 1) > 0) {
fprintf(stderr, "RESULT : %s", buffer);
}
// CLOSING PIPES
for (i = 0; i < 2; i++) {
close(fd1[i]);
close(fd2[i]);
}
close(file);
exit(0);
}
You aren't closing enough file descriptors in the parent soon enough.
Rule of thumb: If you
dup2()
one end of a pipe to standard input or standard output, close both of the
original file descriptors returned by
pipe()
as soon as possible.
In particular, you should close them before using any of the
exec*()
family of functions.
The rule also applies if you duplicate the descriptors with either
dup()
or
fcntl()
with F_DUPFD
Now, your child process is following the RoT perfectly. But the corollary for parent processes is that they need to close the unused ends of the pipe, and they must close the write end of a pipe that they use to signal EOF to the reading end of that pipe. This is where your code fails.
Arguably, before reading the file, the parent process should close the read end of the pipe it uses to write to the child, and it should close the write end of the pipe it uses to read from the child.
Then, after reading the whole of the file, it should close the write end of the pipe to the child, before going into the 'read from child' loop. That loop never terminates because the parent still has the write end of the pipe open, so there's a process that could (but won't) write to the pipe.
Also, since the child writes the bytes of an integer onto a pipe, the parent should read the bytes of an integer. Using char buffer[1]; with a %s format is pointless; you need a null terminator for the string, and a single char buffer can't hold both a null byte and any data.
Along with various other improvements ('0' instead of 48, for example), you might end up with:
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main(int argc, char** argv)
{
if (argc != 2) {
fprintf(stderr, "Usage: %s filename\n", argv[0]);
exit(EXIT_FAILURE);
}
int fd1[2];
int fd2[2];
char buffer[1];
pipe(fd1);
pipe(fd2);
pid_t pid = fork();
if (pid == 0) {
int cnt = 0;
dup2(fd1[0], STDIN_FILENO);
dup2(fd2[1], STDOUT_FILENO);
for (int i = 0; i < 2; i++) {
close(fd1[i]);
close(fd2[i]);
}
while (read(STDIN_FILENO, buffer, sizeof(buffer)) > 0) {
fprintf(stderr, "(%c)", buffer[0]); // Changed
cnt = cnt + buffer[0] - '0';
}
putc('\n', stderr); // Aesthetics
write(STDOUT_FILENO, &cnt, sizeof(cnt));
exit(0);
}
int file = open(argv[1], O_RDONLY);
if (file < 0) {
fprintf(stderr, "failed to open file '%s' for reading\n", argv[1]);
exit(EXIT_FAILURE);
}
close(fd1[0]); // Added
close(fd2[1]); // Added
while (read(file, buffer, sizeof(buffer)) > 0) {
if ('0' <= buffer[0] && buffer[0] <= '9') {
write(fd1[1], buffer, sizeof(buffer));
}
}
close(file); // Moved
close(fd1[1]); // Added
// Rewritten
int result;
while (read(fd2[0], &result, sizeof(result)) == sizeof(result)) {
fprintf(stderr, "RESULT : %d\n", result);
}
close(fd2[0]); // Added
// Close loop removed
return 0;
}
If that is stored in file pipe71.c and compiled, I get the following outputs when it is run:
$ ./pipe71 pipe71.c
(2)(0)(1)(2)(2)(2)(1)(1)(2)(0)(0)(2)(1)(0)(2)(2)(1)(0)(2)(1)(2)(0)(0)(0)(0)(0)(1)(0)(1)(1)(0)(2)(1)(0)(0)(0)(0)(9)(1)(1)(1)(1)(2)(0)(2)(0)(0)
RESULT : 49
$ ./pipe71 pipe71
(0)(0)(8)(0)(0)(2)(2)(0)(8)(1)(1)(5)(1)(1)(1)(1)(5)(1)(1)(1)(8)(5)(1)(9)(8)(5)(1)(1)(0)(4)(4)(4)(6)(0)(2)(8)(0)(0)(0)(2)(7)(1)(3)(8)(3)(0)(4)(3)(0)(4)(9)(0)(0)(0)(0)(7)(1)(9)(8)(1)(3)(0)
RESULT : 178
$
I'm trying to make a program where the parent reads from a file some operations, passes them to the child with a pipe, and the child makes all the operations with bc. Later on, the child has to pass it back to the parent and this one has to write it on a file.
However, when I execute it, I don't get any result and don't know where the problem is. The child seems to receive the operation correctly, but with 'EXT' character.
My code works perfectly when the parent doesn't read from the file, the problem is when I try to read from a file.
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define EOL '\n'
int main(int argc, char *argv[]){
int tube1[2];
int tube2[2];
int fID;
pipe(tube1);
pipe(tube2);
if(argc != 2){
perror("./yourProgram.c [origin]");
exit(EXIT_FAILURE);
}
if (fork() == 0){
//Child Process
close(tube1[1]); // writing end of the pipe
dup2(tube1[0], 0); // stdin ----> pipe reading end
close(tube2[0]); // reading end of the pipe
dup2(tube2[1], 1); // stdout ---> pipe writing end
//Execute and write the output in the tube2
execlp("bc", "bc", "-q", NULL);
}else {
//Parent Process
close(tube1[0]); // reading end of the pipe
//dup2(tube1[1], 1); // stdout ---> pipe writing end
close(tube2[1]); // reading end of the pipe
//dup2(tube1[1], 1); // stdout ---> pipe writing end
//Files
//Destiny
char *destiny = "final.txt";
int destinyFile = open(destiny, O_WRONLY | O_CREAT | O_TRUNC, 0644);
//Origin
char *origin = argv[1];
int originFile = open(origin, O_RDONLY);
//Variables
char block;
char result;
char buffer[4096];
int i = 0;
int numbytes;
while(numbytes = read(originFile, &block, sizeof(block)) > 0){
if(block == EOL){
//Write on the tube, so the child can read it
if(write(tube1[1], buffer, strlen(buffer)) == -1){
perror("error en write en pipe");
exit(EXIT_FAILURE);
}
//Read the child's answer
while(numbytes = read(tube2[0], &result, 1) > 0){
if(result != EOL){
//Concatenate strings as: 'X + Y = Result \n'
char str[80];
strcat(str, buffer);
strcat(str, " = ");
strcat(str, &result);
strcat(str, "\n");
//Write the result in the Final File
if(write(destinyFile, str, strlen(str)) == -1){
perror("error en write en stdout");
exit(EXIT_FAILURE);
}
}else
continue;
}
//Reset Buffer
buffer[0] = '\0';
i = 0;
}else{
buffer[i] = block;
i = i + 1;
}
}
}
}
And the file from where I read is:
2+3
4*5
8/2
quit
This code fixes the critical problems noted in the comments (but not the performance issues from reading one byte at a time). If the calculation produces a result that's big enough, bc splits its output over multiple lines (calculating factorial 100, for example). The code does not attempt to deal with that. Also, some operations produce no output. For example, c=2345 produces no output. The program does not handle this, either.
You aren't closing enough file descriptors in the child.
Rule of thumb: If you
dup2()
one end of a pipe to standard input or standard output, close both ends
of the pipe as soon as possible.
That means before using any of the
exec*()
family of functions in particular.
The rule of thumb also includes using
dup()
or
fcntl()
with F_DUPFD.
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#define EOL '\n'
int main(int argc, char *argv[])
{
int tube1[2]; // Parent -> child
int tube2[2]; // Child -> parent
if (argc != 2)
{
perror("./yourProgram.c origin");
exit(EXIT_FAILURE);
}
pipe(tube1);
pipe(tube2);
if (fork() == 0)
{
// Child Process
dup2(tube1[0], 0); // stdin ----> pipe reading end
close(tube1[0]); // reading end of the pipe to child
close(tube1[1]); // writing end of the pipe to child
dup2(tube2[1], 1); // stdout ---> pipe writing end
close(tube2[0]); // reading end of the pipe to parent
close(tube2[1]); // writing end of the pipe to parent
// Execute and write the output in the tube2
execlp("bc", "bc", "-q", NULL);
perror("bc");
exit(EXIT_FAILURE);
}
else
{
// Parent Process
close(tube1[0]); // reading end of the pipe to child
close(tube2[1]); // writing end of the pipe to parent
// Files
// Destiny
char *destiny = "final.txt";
int destinyFile = open(destiny, O_WRONLY | O_CREAT | O_TRUNC, 0644);
if (destinyFile < 0)
{
perror(destiny);
exit(EXIT_FAILURE);
}
// Origin
char *origin = argv[1];
int originFile = open(origin, O_RDONLY);
if (originFile < 0)
{
perror(origin);
exit(EXIT_FAILURE);
}
// Variables
char block;
char result;
char buffer[256];
int i = 0;
int numbytes;
while ((numbytes = read(originFile, &block, sizeof(block))) > 0)
{
buffer[i++] = block;
//printf("Block: [%.*s]\n", i, buffer);
if (block == EOL)
{
// Write on the tube, so the child can read it
if (write(tube1[1], buffer, i) == -1)
{
perror("error en write en pipe");
exit(EXIT_FAILURE);
}
buffer[i-1] = '\0';
// Read the child's answer
int j = 0;
char reply[256];
while ((numbytes = read(tube2[0], &result, sizeof(result))) > 0)
{
reply[j++] = result;
//printf("Reply: [%.*s]\n", j, reply);
if (result == EOL)
{
// Concatenate strings as: 'X + Y = Result \n'
char str[256];
str[0] = '\0';
strcat(str, buffer);
strcat(str, " = ");
strcat(str, reply);
// Write the result in the Final File
if (write(destinyFile, str, strlen(str)) == -1)
{
perror("error en write en stdout");
exit(EXIT_FAILURE);
}
printf("Response: [%.*s]\n", j-1, reply);
break;
}
}
// Reset Buffer
i = 0;
}
}
close(tube1[1]);
close(tube2[0]);
}
return 0;
}
Given the sample input file:
2+3
4*5
8/2
quit
the output on the screen is:
Response: [5]
Response: [20]
Response: [4]
and the output in final.txt is:
2+3 = 5
4*5 = 20
8/2 = 4
I'm writing a program to execute another program as a forked process and redirect it's output to a file or /dev/null on demand.
Currently I have forked and executed the external program using execvp().
Then redirected the stdout from a thread created before forking as the forked process will inherit parents file descriptor table allowing me to redirect after foking.
But, I can initially redirect stdout to a desired file and both parents and child's stdouts are being redirected. However if I try to redirect it again to another file, only parents stdout is redirected, child's stdout stays the same.
Here's the code without all the error checking bits.
struct params {
const char *p;
int fd;
int wait;
};
#define EXIT_NOEXEC 126
#define EXIT_NOTFOUND 127
#define EXIT_MISC 127
static void dofile(struct params* st);
void dupit(const char *p, struct params* st);
void* reload_config(void* para);
int
main(int argc, char *argv[]) {
int exit_status, prog_status;
struct params init;
pid_t prog_pid;
dofile(&init);
prog_pid = fork();
if (prog_pid == 0) {
execvp(*argv, argv);
exit_status = (errno == ENOENT) ? EXIT_NOTFOUND : EXIT_NOEXEC;
err(exit_status, "%s", argv[0]);
exit(EXIT_FAILURE);
} else {
while (wait(&prog_status) != prog_pid);
return prog_status;
}
}
static void dofile(struct params* st) {
const char *p
p = out.txt;
dupit(p, st);
}
void dupit(const char *p, struct params* st) {
pthread_t tid;
st->wait = 0;
int err = pthread_create(&(tid), NULL, &reload_config, st);
if (err != 0) {
printf("\ncan't create thread :[%s]", strerror(err));
exit(1);
} else {
while (st->wait == 0) {
sleep(1)
}
}
}
void* reload_config(void* para) {
struct params *passed = (struct params *) para;
int pre_config = 3;
int cur_config = 1;
int saved_stdout = dup(STDOUT_FILENO);
char infile[5];
int devNull = open("/dev/null", O_WRONLY);
int file = open("out.txt", O_WRONLY);
FILE *config;
config = fopen("config.txt", "r");
if (access("config.txt", F_OK) != -1) {
while (1) {
fgets(infile, 5, config);
fclose(config);
cur_config = infile[0] - '0';
printf("output from thread, current config = %d\n", cur_config);
if (pre_config != cur_config) {
if (cur_config == 1) {
if (dup2(file, STDOUT_FILENO) == -1) {
err(EXIT_MISC, NULL);
}
} else {
dup2(devNull, STDOUT_FILENO);
}
pre_config = cur_config;
}
if (passed->wait==0) {
passed->wait = 1;
}
sleep(1);
}
} else {
if (dup2(passed->fd, STDOUT_FILENO) == -1) {
err(EXIT_MISC, NULL);
}
}
}
Well, I changed the code a bit so you guys will understand, so some parts will make no sense. But you get the basic idea.
How can I redirect child's stdout as I wish after forking.
Since you asked, here is a simple example. Some shortcuts have been taken for brevity but hopefully it gives you some idea. The program opens file1 and redirects stdout to that file. It then does a fork. The child process writes a counter to stdout (via printf) every 1 second. After a few seconds the parent process uses IPC, a pipe in this example, to tell the child to switch redirect file.
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
int main(int argc, char **argv)
{
pid_t pid;
const char *file1 = "file1.txt";
const char *file2 = "file2.txt";
int pipefd[2];
int fd;
int rval;
fd = open(file1, O_WRONLY|O_CREAT|O_TRUNC, S_IRWXU);
if (fd == -1) {
perror("file1 open");
exit(-1);
}
/*
* This pipe will be used by parent process to tell child which file
* to redirect to.
*/
rval = pipe2(pipefd, O_NONBLOCK);
if (fd == -1) {
perror("pipe");
exit(-1);
}
/* Redirect stdout to the file opened before the fork. */
dup2(fd, STDOUT_FILENO);
pid = fork();
if (pid == -1) {
perror("fork");
exit(-1);
} else if (pid == 0) {
/* Child process. */
int ix;
char redirect_file[100];
close(pipefd[1]);
for (ix = 0; ix < 10; ix++) {
printf("%d\n", ix);
sleep(1);
rval = read(pipefd[0], redirect_file, sizeof(redirect_file));
if (rval > 0) {
/*
* Parent process has written a filename to the pipe.
*/
fd = open(redirect_file, O_WRONLY|O_CREAT|O_TRUNC, S_IRWXU);
if (fd == -1) {
perror("file2 open");
exit(-1);
}
/* Ensure previous output has been written to current file. */
fflush(stdout);
/* Change redirect now. */
dup2(fd, STDOUT_FILENO);
}
}
} else {
/* Parent process. */
close(pipefd[0]);
/* Wait a little and then tell child to change redirect file. */
sleep(5);
write(pipefd[1], file2, strlen(file2) + 1);
wait();
}
}
If this program is run you will find that half the child output went to file1 (first redirect) and other half of the output goes to file2 (second redirect).
$ cat file1.txt
0
1
2
3
4
$ cat file2.txt
5
6
7
8
9
One final note. The example program does the first dup before the fork. I did it like that because that's how your code was shown and also to emphasise the before and after fork aspect of the issue. But in real code the conventional way of doing that is to do fork first, then dup and finally exec. The dup is done after the fork so that only the child process gets affected and not the parent (unless that is really what you want).
I'm trying to implement a very small shell of my own. I have to be able to handle pipes, like
ls -l | wc -l
but only for two programs at a time. Right now, I have this:
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/wait.h>
#define BUFFER_SIZE 256
#define NO_PARAMS 32
void split_string(char **params, char *string){
char *arg;
int i;
arg = strtok(string, " ");
params[0] = arg;
i = 1;
while(arg != NULL){
arg = strtok(NULL, " ");
params[i] = arg;
i++;
}
}
int main(int argc, char **argv){
char string[BUFFER_SIZE];
char *prog1, *prog2;
int i, err;
int fd[2];
pid_t pid1, pid2;
size_t buffer = BUFFER_SIZE;
char *params1[NO_PARAMS], *params2[NO_PARAMS];
int pipe_exists = 0;
memset(string,0,buffer);
while(1){
/*Read command*/
fgets(string, BUFFER_SIZE-1, stdin);
if(string == NULL){
perror("Error reading input:\n");
exit(1);
}
/*replace linefeed character with end of line character*/
for(i=0;i<BUFFER_SIZE;i++){
if(string[i] == 10){
string[i] = 0;
}
}
/*check if command is "exit"*/
if(strcmp(string,"exit") == 0){
return 0;
}
/*split command into different program calls*/
prog1 = strtok(string, "|");
prog2 = strtok(NULL,"\0");
if(prog2 != NULL){
pipe_exists = 1;
printf("PIPE!\n");
err = pipe(fd);
if(err<0){
perror("Error creating pipe:\n");
exit(1);
}
}
/*split string into arguments*/
split_string(params1, prog1);
if(pipe_exists){
split_string(params2, prog2);
}
/*fork child process*/
pid1 = fork();
if(pid1==0){ /*child 1*/
if(pipe_exists){
close(fd[0]); /*close read-end*/
err = dup2(fd[1], 1);
if(err<0){
perror("Error with dup in child 1!\n");
exit(1);
}
}
execvp(params1[0],params1);
perror("Error calling exec()!\n");
exit(1);
}else{ /*parent*/
if(pipe_exists){
pid2 = fork();
if(pid2==0){ /*child 2*/
close(fd[1]); /*close pipe write-end*/
err = dup2(fd[0], 0);
if(err<0){
perror("Error with dup in child 2!\n");
exit(1);
}
execvp(params2[0],params2);
perror("Error calling exec()!\n");
exit(1);
}else{ /*parent with 2 children*/
waitpid(pid1,0,0);
waitpid(pid2,0,0);
}
}else{ /*parent with 1 child*/
waitpid(pid1,0,0);
}
}
}
}
Right now, it'll handle single commands fine, but when I input something like the command above, nothing happens!
Thanks!
Oh! I've already figured it out. I had to close the pipe in the parent program as well :)
To start with, you should loop as long as you find the pipe character. Then you need to create a pipe for each "piping".
Real shells usually forks and exec itself for each command in the pipeline. This is so it should be able to handle internal commands.
There are 3 main parts in a command with pipes.
The begining, that takes stdin and pipes its output something |
The middle, optionnal or repeated at will with two pipes | something |
The end, that outputs to stdout | something
Then use three functions, one for each of those:
#define PIPE_INPUT 0
#define PIPE_OUTPUT 1
execute_pipe_start(t_cmdlist *commands)
{
int pid;
int fd[2];
if (!commands)
return;
if (commands->next)
{
if (pipe(fd) < 0)
{
perror("pipe failed");
exit(1);
}
pid = fork();
if (!pid)
{
close(fd[PIPE_INPUT]);
if (dup2(fd[PIPE_OUTPUT, 1) < 0)
{
perror("dup2 failed");
exit(1);
}
parse_and_exec_cmd(commands->cmd);
}
else
{
waitpid(...); //what you put here is a bit tricky because
//some shells like tcsh will execute all
//commands at the same time (try cat | cat | cat | cat)
}
if (commands->next->next != null) //If you have 2 commands in line there is a middle
execute_pipe_middle(commands->next, fd);
else // no middle
execute_pipe_end(commands->next, fd);
}
else
parse_and_exec_cmd(commands->cmd);
}
execute_pipe_middle(t_cmdlist *commands, int fd_before[2])
{
int pid;
int fd_after[2];
if (pipe(fd_after) < 0)
{
perror("pipe failed");
exit(1);
}
pid = fork();
if (!pid)
{
close(fd_before[PIPE_OUTPUT]);
close(fd_after[PIPE_INPUT]);
if (dup2(fd_after[PIPE_OUTPUT, 1) < 0)
{
perror("dup2 failed");
exit(1);
}
if (dup2(fd_before[PIPE_INPUT, 0) < 0)
{
perror("dup2 failed");
exit(1);
}
parse_and_exec_cmd(commands->cmd);
}
else
waitpid(...);
if (commands->next->next != null) //More than two following commands : a middle again
execute_pipe_middle(commands->next, fd_after);
else // No more repetition
execute_pipe_end(commands->next, fd_after);
}
execute_pipe_end(t_cmdlist *commands, int fd_before[2])
{
int pid;
if (!commands)
return;
if (commands->next)
{
pid = fork();
if (!pid)
{
close(fd_before[PIPE_OUTPUT]);
if (dup2(fd_before[PIPE_INPUT, 0) < 0)
{
perror("dup2 failed");
exit(1);
}
parse_and_exec_cmd(commands->cmd);
}
else
waitpid(...);
}
}