My program will pick a random number and have the use guess what it is. I am almost done with it, except I need to duplicate the file descriptors to point to a pipe, and I'm not entirely sure how to do that. I think i'd have to use dup2, but I'm not entirely sure how to implement it. Anything helps. Here is my code so far:
#include <stdio.h>
#include <stdlib.h>
int main()
{
int pid;
int n;
char buf[101];
int pfdA[2];
int pfdB[2];
// CREATE FIRST PIPE
if (pipe(pfdA) == -1)
{
perror("pipe failed");
exit(-1);
}
// CREATE SECOND PIPE
if (pipe(pfdB) == -1)
{
perror("pipe failed");
exit(-1);
}
// FORK()
if ((pid == fork()) < 0)
{
perror("fork failed");
exit(-2);
}
if (pid == 0)
{
// duplicate file descriptor 0 to point to FIRST pipe
// CLOSE ends of FIRST pipe you don't need anymore
close(pfdA[0]);
close(pdfA[1]);
// duplicate file descriptor 1 to point to SECOND pipe
// CLOSE ends of SECOND pipe you don't need anymore
close(pfdB[0]);
close(pfdB[1]);
execlp("./A5_CHILD", "./A5_CHILD", (char *) 0);
perror("execlp");
exit(-3);
}
else
{
while (1)
{
char NUM[100];
close(pfdA[0]);
close(pfdB[1]);
int r = 0;
printf("Enter a Number: ");
fflush(stdout);
scanf("%s", NUM);
// SEND NUM to Child process
write(pdfA[1], NUM, strlen(NUM));
// READ FROM CHILD THE RESPONSE into the variable buf and
// store the return value from read() into the variable r
r = read(pfdB[0], buf, 100);
if (r > 0)
{
buf[r] = '\0';
printf("%s\n", buf);
fflush(stdout);
}
else
{
printf("[PARENT] Reading from child: read() returned %d\n", r);
break;
}
}
}
return (0);
}
I think i'd have to use dup2, but I'm not entirely sure how to
implement it.
You are right.
// duplicate file descriptor 0 to point to FIRST pipe
dup2(pfdA[0], 0);
...
// duplicate file descriptor 1 to point to SECOND pipe
dup2(pfdB[1], 1);
Related
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 am trying to use pipes in C. I have two create two pipes between parent and child process.I have to read a file in chunks of 4096 bytes (or smaller if there is less) and I have to send through the pipes the amount of data that was read and how many times there have been readings. For example, to copy a 6KB file, the parent writes the first 4KB data of the file to the shared memory and send two integers, 1 and 4096, to the child via the pipe. The child receives these two numbers, copies 4096 bytes from the shared memory to the output file, and sends back 1 to the parent via the other pipe. After receiving 1, the parent copies the left 2KB data to the shared memory and send 2 and 2048 to the child. The child receives them from the pipe, copies 2048 bytes to the output file, and replies with 2 to the parent. The parent then send 0, 0 to the child. The child receives 0 and replies with a 0 and then exit. The parent receives 0 and exits too.
Currently my program works for file less than one block but not for file greater then one block (4096 bytes)
697, thank you for pointing out, I have modified my program as following but still has issues, basically how to control the flow as parent-child-parent-child ...
#include <sys/types.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#define SIZE 4096
int file_exist (char *filename)
{
struct stat buffer;
return (stat (filename, &buffer) == 0);
}
int main(int argv, char *argc[]) {
/*Check if program is called correctly*/
if(argv != 3) {
printf("Please call program appropriately\n");
exit(EXIT_FAILURE);
}
FILE *r, *w;
void *sharedMem;
int datapipe[2];
int blockpipe[2];
int shm;
char userInput[5];
char *name = "fsuid_cop4610";
if (file_exist (argc[2]))
{
printf("Would you like to overwrite file (yes/no): ");
scanf("%s", userInput);
if(!strcmp(userInput, "yes")) {
printf("Overwriting file...\n");
//fclose(w);
w = fopen(argc[2], "wb");
if(w == NULL) {
perror("Error with write file");
exit(EXIT_FAILURE);
}
}
else if(!strcmp(userInput, "no")) {
printf("Will not overwrite\n");
exit(EXIT_FAILURE);
}
else {
printf("User input not accepted\n");
exit(EXIT_FAILURE);
}
}
/*Check if read file can open*/
r = fopen(argc[1], "rb");
if(r == NULL) {
perror("Error opening read file");
exit(EXIT_FAILURE);
}
fseek(r, 0, SEEK_END); // seek to end of file
int inputlength = ftell(r); // get current file pointer
printf("inputlength is %d\n",inputlength);
int numofblock = inputlength/SIZE + 1;
fseek(r, 0, SEEK_SET); // seek back to beginning of file
/*Check if write file can open*/
if (pipe(datapipe) < 0) {
perror("Pipe");
exit(EXIT_FAILURE);
}
if (pipe(blockpipe) < 0) {
perror("Pipe");
exit(EXIT_FAILURE);
}
/*Check if forking process is successful*/
pid_t pid = fork();
if(pid < 0) {
perror("Fork");
exit(EXIT_FAILURE);
}
shm = shm_open(name, O_CREAT | O_RDWR, S_IRUSR | S_IWUSR);
if(shm == -1) {
perror("Shared memory");
exit(EXIT_FAILURE);
}
if(ftruncate(shm, SIZE) == -1) {
perror("Shared Memory");
exit(EXIT_FAILURE);
}
sharedMem = mmap(NULL, SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, shm, 0);
if(sharedMem == MAP_FAILED) {
perror("Mapping shared memory");
exit(EXIT_FAILURE);
}
if (pid>0) { // parent
printf(" parent before close data0,block1\n");
close(datapipe[0]); // close read, will write data
close(blockpipe[1]); // close write, will read block number
printf(" parent close data0,block1\n");
for (int i=1; i<=numofblock; i++)
{
printf("... parent process\n");
int blocknumber=i;
printf("parent read from input file into shared memory\n");
int P2SHM = fread(sharedMem, 1, SIZE, r);
if(P2SHM < 0) {
perror("Could not store to shared memory");
exit(EXIT_FAILURE);
}
//printf("parent shared memory conent: %s\n",(char *)sharedMem);
printf("parent data read %d\n",P2SHM);
if (i==1)
{
printf("i=%d parent write to data pipe\n",i);
if(write(datapipe[1], &P2SHM, sizeof(int)) < 0) {
perror("parent failed to write to pipe bytes");
exit(EXIT_FAILURE);
}
if(write(datapipe[1], &blocknumber, sizeof(int)) < 0) {
perror("parent failed to write to pipe block number");
exit(EXIT_FAILURE);
}
}
printf("parent read from block pipe\n");
int C2P = read(blockpipe[0], &blocknumber, sizeof(int));
if(C2P < 0) {
perror("parent failed to read value from blockpipe");
exit(EXIT_FAILURE);
}
/*else if(C2P == 0) {
printf("End of file reached\n");
}*/
else {
printf("parent block %d Received succesfully\n", blocknumber);
if (i>= 2)
{
printf("i=%d parent write to data pipe\n",i);
//close(datapipe[0]); // close read, will write data
if(write(datapipe[1], &P2SHM, sizeof(int)) < 0) {
perror("parent failed to write to pipe bytes");
exit(EXIT_FAILURE);
}
if(write(datapipe[1], &blocknumber, sizeof(int)) < 0) {
perror("parent failed to write to pipe block number");
exit(EXIT_FAILURE);
}
//close(datapipe[1]);
}
if(C2P == 0) {
printf("parent End of file reached\n");
}
}
} // end for
printf(" parent before close data1,block0\n");
close(datapipe[1]);
close(blockpipe[0]);
printf(" parent close data1,block0\n");
printf(" ... existing parent process\n");
}
else { // pid=0 child
printf(" child before close data1,block0\n");
close(datapipe[1]); // close write, will read data
close(blockpipe[0]); // close read, will write block number
printf(" child close data1,block0\n");
for (int j=1; j<=numofblock; j++)
{
printf(".... child process\n");
int cBytes, len, len2;
int blocknumber = 1;
printf("child read from datapipe\n");
len = read(datapipe[0], &cBytes, sizeof(cBytes));
len2 = read(datapipe[0], &blocknumber, sizeof(blocknumber));
printf("child wrote to blockpipe blocknumber=%d\n", blocknumber);
write(blockpipe[1], &blocknumber, sizeof(blocknumber));
printf("child There are %i bytes\n", cBytes);
if(len >= 0)
{
printf("child writing to file\n");
//fwrite(sharedMem, 1, sizeof(sharedMem), w);
//printf("child shared memory conent: %s\n",(char *)sharedMem);
char* res = (char *)sharedMem;
//printf("res = %s\n",res);
//printf("errno before write=%d",errno);
shm_unlink(name);
//int writtenbyte = fwrite(res, sizeof(char), strlen(res), w);
int writtenbyte = fwrite(res, sizeof(char), cBytes, w);
if(errno == EINTR) {
printf("somewhting wrong");
}
//printf("errno after write=%d",errno);
printf("writtenbyte = %d\n",writtenbyte);
//fclose(w);
//fputs((char *)sharedMem, w);
//fwrite(s->name, sizeof(char), strlen(s->name) + 1, fp);
}
/*else if (len == 0) {
printf("End of fle reached\n");
}*/
else { // len < 0
perror("Failed to read from pipe");
exit(EXIT_FAILURE);
}
} // after for
printf(" child before close data0,block1\n");
close(datapipe[0]);
close(blockpipe[1]);
printf(" child close data0,block1\n");
printf("... exiting Child process\n");
}
//shm_unlink(name);
//fclose(r);
fclose(w);
return 0;
}
Result:
inputlength is 4177
parent before close data0,block1
parent close data0,block1
... parent process
parent read from input file into shared memory
parent data read 4096
i=1 parent write to data pipe
parent read from block pipe
child before close data1,block0
child close data1,block0
.... child process
child read from datapipe
child wrote to blockpipe blocknumber=1
child There are 4096 bytes
child writing to file
parent block 1 Received succesfully
... parent process
parent read from input file into shared memory
parent data read 81
parent read from block pipe
parent block 2 Received succesfully
i=2 parent write to data pipe
Note child write block number 1, but parent received block number 1, then received block number 2, and after the 2nd write from parent it stopped, without writing output file. Any thoughts for that?
I am trying to use pipes in C. I have two create two pipes between parent and child process.I have to read a file in chunks of 4096 bytes (or smaller if there is less) and I have to send through the pipes the amount of data that was read and how many times there have been readings. For example, to copy a 6KB
file, the parent writes the first 4KB data of the file to the shared memory and send two integers, 1 and 4096, to the child via the pipe. The child receives these two numbers, copies 4096 bytes from the shared memory to the output file, and sends back 1 to the parent via the other pipe. After receiving 1,
the parent copies the left 2KB data to the shared memory and send 2 and 2048 to the child. The child receives them from the pipe, copies 2048 bytes to the output file, and replies with 2 to the parent. The parent then send 0, 0 to the child. The child receives 0 and replies with a 0 and then exit. The parent
receives 0 and exits too.
#include <sys/types.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#define SIZE 4096
#define NUM_OF_PIPES 2
#define P_READ 0
#define P_WRITE 1
#define C_READ 2
#define C_WRITE 3
int main(int argv, char *argc[]) {
/*Check if program is called correctly*/
if(argv != 3) {
printf("Please call program appropriately\n");
exit(EXIT_FAILURE);
}
FILE *r, *w, *check;
void *sharedMem;
int pipes[4];
int shm;
char userInput[5];
char *name = "dm11ad_cop4610";
int inChild = 0;
int inParent = 0;
r = fopen(argc[1], "rb");
check = fopen(argc[2], "rb");
/*Check if read file can open*/
if(r == NULL) {
perror("Error opening read file");
exit(EXIT_FAILURE);
}
/*Check if write file can open*/
if(check == NULL) {
perror("Error with write file");
exit(EXIT_FAILURE);
}
else {
fseek(check, 0, SEEK_END);
int writeLen = ftell(check);
if(writeLen > 0) {
rewind(check);
printf("Would you like to overwrite file (yes/no): ");
scanf("%s", userInput);
if(!strcmp(userInput, "yes")) {
printf("Overwriting file...\n");
w = fopen(argc[2], "wb");
}
else if(!strcmp(userInput, "no")) {
printf("Will not overwrite\n");
exit(EXIT_FAILURE);
}
else {
printf("User input not accepted\n");
exit(EXIT_FAILURE);
}
}
}
for (int i = 0; i < NUM_OF_PIPES; i++) {
if (pipe(pipes+(i*2)) < 0) {
perror("Pipe");
exit(EXIT_FAILURE);
}
}
/*Check if forking process is successful*/
pid_t pid = fork();
if(pid < 0) {
perror("Fork");
exit(EXIT_FAILURE);
}
shm = shm_open(name, O_CREAT | O_RDWR, S_IRUSR | S_IWUSR);
if(shm == -1) {
perror("Shared memory");
exit(EXIT_FAILURE);
}
if(ftruncate(shm, SIZE) == -1) {
perror("Shared Memory");
exit(EXIT_FAILURE);
}
sharedMem = mmap(NULL, SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, shm, 0);
if(sharedMem == MAP_FAILED) {
perror("Mapping shared memory");
exit(EXIT_FAILURE);
}
if(pid == 0) {
while(inParent);
inChild = 1;
printf("I am in child\n");
close(pipes[P_READ]);
close(pipes[P_WRITE]);
printf("Closed P pipes\n");
int cBytes, len;
printf("Im stuck\n");
len = read(pipes[C_READ], &cBytes, sizeof(cBytes));
printf("There are %i bytes\n", len);
if(len < 0) {
perror("Failed to read from pipe");
exit(EXIT_FAILURE);
}
else if(len == 0) {
printf("End of fle reached\n");
}
else {
printf("Writing to file\n");
fwrite(sharedMem, 1, sizeof(sharedMem), w);
}
printf("Closing C pipes\n");
close(pipes[C_READ]);
close(pipes[C_WRITE]);
printf("Exiting Child\n");
inChild = 0;
}
else {
while(inChild);
inParent = 1;
close(pipes[C_READ]);
close(pipes[C_WRITE]);
int pBytes;
int P2SHM = fread(sharedMem, 1, SIZE, r);
if(P2SHM < 0) {
perror("Could not store to shared memory");
exit(EXIT_FAILURE);
}
if(write(pipes[P_WRITE], &P2SHM, sizeof(int)) < 0) {
perror("Failed to write to pipe");
exit(EXIT_FAILURE);
}
int C2P = read(pipes[P_READ], &pBytes, sizeof(int));
if(C2P < 0) {
perror("Failed to read value from pipe");
exit(EXIT_FAILURE);
}
else if(C2P == 0) {
printf("End of file reached\n");
}
else {
printf("Received succesfully\n");
}
close(pipes[P_READ]);
close(pipes[P_WRITE]);
inParent = 0;
printf("Waiting for child\n");
wait(NULL);
}
return 0;
}
The printfs are there to help me see where the program is during execution. It gets stuck in child process, it seems during
len = read(pipes[C_READ], &cBytes, sizeof(cBytes));
This is an assignment, so please do not post code as an answer but rather please hep me understand what I am doing wrong. Thanks
Synchronization mechanism between child and parent looks suspicious:
while(inParent);
inChild = 1;
and
while(inChild);
inParent = 1;
Initial values for inChild and inParent is 0. After child process created each process has it's own copy of variable values. When you change inChild = 1 and inParent = 1, it's changed inside the current process only. Other process doesn't see new values and cannot wait for the input/output.
To fix it you should use better synchronization algorithm, e.g. processes semaphores. Read "5.2 Processes Semaphores" to get details.
It gets stuck in child process, it seems during
len = read(pipes[C_READ], &cBytes, sizeof(cBytes));
Well yes, I imagine it does.
You've been a bit too clever, I think, in setting up a single 4-element array for the pipe-end file descriptors. That's not inherently wrong, but it tends to obscure what's going on a bit.
Consider what the pipes are supposed to do for you: one process writes to the write end of a pipe, and the other reads what was written from the read end of that same pipe. Look carefully at which file descriptors each process is reading from and writing to.
I have a program where the child runs a program but the parent process passes the child a number and the child writes back to the parent a response. However whenever I run the code, it does not give me anything back, so I must be passing or receiving to the child wrong, but I'm not sure how. Any help is appreciated. Here is my code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
int main() {
int pid;
int n;
char buf[101];
int pfdA[2];
int pfdB[2];
// CREATES FIRST PIPE
if (pipe(pfdA) == -1) {
perror("pipe failed");
exit(-1);
}
// CREATES SECOND PIPE
if (pipe(pfdB) == -1) {
perror("pipe failed");
exit(-1);
}
// FORK()
if ((pid == fork()) < 0) {
perror("fork failed");
exit(-2);
}
if (pid == 0 ) {
// duplicate file descriptor 0 to point to FIRST pipe
dup(pfdA[0]);
// CLOSES ends of FIRST pipe you don't need anymore
close(pfdA[0]);
close(pfdA[1]);
// duplicates file descriptor 1 to point to SECOND pipe
dup(pfdA[1]);
// CLOSES ends of SECOND pipe you don't need anymore
close(pfdB[0]);
close(pfdB[1]);
execlp("./A5_CHILD", "./A5_CHILD", (char *) 0);
perror("execlp");
exit(-3);
}
else {
while( 1 ) {
char NUM[100];
close(pfdA[0]);
close(pfdB[1]);
int r=0;
printf("Enter a Number: ");
fflush(stdout);
scanf("%s", NUM);
// SENDS NUM to Child process
write(pfdA[1], NUM, strlen(NUM));
// READS FROM CHILD THE RESPONSE into the variable buf and
// store the return value from read() into the variable r
r= read(pfdB[0], buf, 100);
if( r > 0 ) {
buf[r] = '\0';
printf("%s\n", buf);
fflush(stdout);
}
else {
printf("[PARENT] Reading from child: read() returned %d\n", r);
break;
}
}
}
return(0);
}
Unless you explicitly close(0), dup(pfdA[0]) almost certainly does not return 0. Try dup2 to specify which descriptor you want as the new one. That is (error checking omitted for brevity):
dup2( pfdA[0], STDIN_FILENO );
close( pfdA[0])
Similarly for stdout.
The goal of this project is to use pipes and forks to execute a line-count utility already written in a multi-process manner (one process per argument). I'm currently working on getting a single process working before expanding to handle multiple args.
Given two executables, lc1 and lc2, I want lc2 to establish a pipe to the stdout file descriptor of lc1, so that when execlp("lc1", argv[1], NULL) is called, the output will be read in by
while ((c= read(pipefd[0], readin, SIZE)) > 0)
According to my Unix book, I should use the open, dup2, close method for redirecting stdout to stdin, and here's my code:
int pid, c, i;
char *readin= (char *)malloc(sizeof(SIZE));
if (pipe(pipefd)== -1)
perror("Can't open a pipe\n");
for (i=1; i< argc; i++){
if ((pid= fork())==-1)
perror("Can't fork\n");
run(argv[i]);
}
//close pipe
close(1);
if (dup2(pipefd[0], 0)==-1)
perror("Can't redirect stdin");
close(pipefd[1]);
for (i=1; i< argc; i++){
if ((wait(NULL))== -1)
perror("Wait error");
while ((c= read(pipefd[0], readin, SIZE)) > 0){;
//print buf count
total += atoi(readin);
}
}
The run function is
void run(char *f){
int fp;
if ((fp= open(f, O_RDONLY)) == -1)
perror("Can't open the file");
close(pipefd[0]);
dup2(pipefd[1], 1);
close(pipefd[1]);
execlp("ls1", f, NULL);
}
When I try to execute this code, I get a stdin redirect error saying bad file descriptor. Why is this happening, and would appreciate any hints to for fixing.
run(argv[i]) is executed by both parent and child because are not assigning the functionality based on the returned PID, so one close after the other may have closed.
See below code, can he handy, I will use the code sample for situations like this. :
int main()
{
int pipe_fd[2] = {0};
int pid = -1;
int status = -1;
int ret_value = INVALID_CMD;
int cmd_output_len = -1;
status = pipe(pipe_fd);
if(status<0)
{
perror("pipe create err");
}
else
{
pid = fork();
if(pid<0)
{
}
else if (pid == 0)
{
/*Child functionality*/
child_func(pipe_fd, cmd);
}
else
{
/*Parent functionality*/
cmd_output_len = parent_fun(pid, pipe_fd);
}
}
return ret_value;
}
int child_func(int pipe_fd[], const char * cmd)
{
int status = 5;
int read_fd = pipe_fd[0]; /*read file descriptor*/
int write_fd = pipe_fd[1]; /*write file descriptor*/
int exit_status = 0;
/*close read fd*/
close(read_fd);
/*dup2 stdout to write fd*/
//status = dup2(1, write_fd);
status = dup2(write_fd, 1);
if(status<0)
{
exit(-1);
}
else
{
system(cmd);
exit(0);
}
}
int parent_fun(int child_id, int pipe_fd[])
{
int status = -1;
int len = 0;
bool_e break_loop = FALSE;
int read_fd = pipe_fd[0]; /*read file descriptor*/
int write_fd = pipe_fd[1]; /*write file descriptor*/
/*close write fd*/
close(write_fd);
while(1)
{
sleep(1);
status = waitpid(child_id, &status, WNOHANG);
switch(status)
{
case 0:
/*Child is still active*/
printf("No process waiting to exit..\n");
len = do_ur_fun(read_fd);
write(1, output, len);
break;
/*case EINTR:
case ECHILD:
case EINVAL:
perror("waitpid error");
break_loop = TRUE;
break;*/
default:
if(status<0)
{
perror("waitpid error");
break_loop = TRUE;
len = -1;
}
else if(child_id == status)
{
/*Valid staus from child*/
len = read_output(read_fd, output);
//write(1, output, len);
break_loop = TRUE;
}
else
{
}
break;
}
if(TRUE == break_loop)
{
break;
}
}
return len;
}
int do_ur_fun (int read_fd)
{
/*Do your exec*/
}
MaheshGupta024 identified a very important problem in your code; I'm assuming you will fix that.
One of the other problem areas is:
close(1);
if (dup2(pipefd[0], 0)==-1)
perror("Can't redirect stdin");
close(pipefd[1]);
for (i=1; i< argc; i++){
if ((wait(NULL))== -1)
perror("Wait error");
while ((c= read(pipefd[0], readin, SIZE)) > 0){;
//print buf count
total += atoi(readin);
}
}
The first close closes the process's standard output; this is seldom a good idea. The next line duplicates the read end of the pipe to standard input - which is fine. As noted in a comment above, perror() does not exit. You then close the write end of the pipe - that's correct; but you should presumably close the read end of the pipe too since you have set it to come from the pipe.
Your loop starts OK; you have redundant parentheses in the wait() line. You read from pipefd[0] instead of standard input - so maybe you didn't want to close pipefd[0] but neither did you need to duplicate it to standard input. You then have a nested loop that reads on the pipe while there's more data to be read from a child - you don't absolutely need the wait() code with its loop since the inner while won't terminate until all the children are dead. On the other hand, there's no great harm in it - after the first child dies, you'll read the data from all the other children, then go into the outer loop and wait for each other child, with the inner loop terminating immediately since there is no data left to read.
So:
Don't close stdout.
Don't dup the pipe read to stdin.
Decide whether you want to clean up the loop - it will work, but could be cleaner.
The run() function is:
void run(char *f){
int fp;
if ((fp= open(f, O_RDONLY)) == -1)
perror("Can't open the file");
close(pipefd[0]);
dup2(pipefd[1], 1);
close(pipefd[1]);
execlp("ls1", f, NULL);
}
The argument should be const char *f (or use name or file instead of f). I would also pass the pipefd array to the function rather than use a global variable
.
Do not call a file descriptor fp; that name conventionally indicates a variable of type FILE *, not int.
However, you don't need to open the file in the first place - unless you want the calling program to do the error reporting instead of the invoked program. However, if you do want the calling program to do the error reporting, you should close the file descriptor before proceeding. (I've already commented on perror() returning).
It would be a good idea to print an error message after execlp(); the only time the function returns is when it fails, so there is no need to test its return value. You might want to exit too - rather than have the failed function go through the rest of the main program after the call to run().
Good points: you did close both the pipe file descriptors.
Hence:
void run(const char *file, int *pipefd)
{
close(pipefd[0]);
dup2(pipefd[1], 1);
close(pipefd[1]);
execlp("ls1", f, NULL);
perror("Failed to exec ls1");
exit(EXIT_FAILURE);
}