How to pass an integer between childprocesses and parent - c

I have a program that is supposed to count the chars in a txt file and use a child process to count the chars and then print out the amount. The parent just feeds the lines in the file to the child in a while loop. The program works in that sense that it can open the file, read it line by line and then if the child process prints the amount it is correct.
But now I want to modify it so that the child process passes back the amount and the parent writes out the nr of chars instead. But when I try to do it the program gives me 1153416175 instead of the real number of chars in the file, and also it just get stuck and I have to kill it. Why is this happening?
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <stdlib.h>
#include <string.h>
#define MAXLINE 100
int main(int argc, char *argv[]) {
int fds[2]; /* file descriptors */
int child; /* child process */
int n; /* size of line in file */
int count, alphacount, total=0; /* used in loop for counting chars in line */
int read_bytes; /* amount of bytes read in read function */
char line[MAXLINE];
FILE *fp;
char *str;
if (argc < 2) {
printf("Format: './rakna <filename>'.\nThe file should have the .txt extension.\n");
exit(1);
} else {
if (pipe(fds) < 0) {
perror("Can't open pipe\n");
exit(1);
}
child = fork();
if (child==-1) {
perror("fork error");
exit(1);
}
/* child that reads chars and returns amount */
if(child == 0)
{
/* close(fds[1]); child process close input of pipe Used to be before ..*/
/* count chars through read */
while (read_bytes = read(fds[0], line, sizeof(line)) > 0) {
/* check if line is received */
if (read_bytes < 0) {
perror("Can't read line");
exit(1);
}
/* count chars in the line */
else {
count=0;
alphacount=0;
while (line[count] != '\0')
{
/* counting chars from 'a' to 'z' */
if (line[count] >= 'a' && line[count] <= 'z')
alphacount++;
/* counting chars from 'A' to 'Z' */
else if (line[count] >= 'A' && line[count] <= 'Z')
alphacount++;
count++;
}
/* adding the nr of chars in line to total */
total += alphacount;
}
}
write(fds[1], &total, sizeof(total)); /* passing total nr of chars to parent */
close(fds[0]); /* closing output part of pipe */
exit(0); /* ending child process */
}
/* parent that sends chars to child-reader and writes out amount */
else
{
/* close(fds[0]); Parent process close output of pipe */
fp = fopen(argv[1], "r");
if (fp == 0) {
perror("Could not read the file.\n");
exit(1);
}
while (fgets(line, MAXLINE, fp) != NULL) {
n = strlen(line);
if (n >= 0) {
line[n]='\0';
if (write(fds[1], line, n) != n) {
perror("write error in pipe");
exit(1);
}
}
else {
perror("problem with fgets");
exit(1);
}
}
int nrofchars;
read_bytes = read(fds[0], &nrofchars, sizeof(nrofchars));
printf("The file has %d nr of chars between a-z\n", nrofchars); //<-- Here it f**ks up, it gives me 1153416175
close(fds[1]); /* closing input part of pipe */
wait(NULL); /* waits for child to read end of file */
}
return 0;
}
}

A pipe is NOT two-way communications, it is unidirectional, fd[0] is the read-end and fd[1] is the write-end.
Your code is using one pipe as bidirectional (you commented out the close of FDs assuming it would become bidirectional) and that's why you are getting the undefined behaviour.
If you want bi-directional you need two pipes :-) OR you can use socketpair(2) to create bidirectional IPC fds.
If you need more help just drop a comment.

Related

How to synchronize read and write in the pipe in C?

Complete C beginner here.
I am trying to write some strings from the child process and read the strings in the parent process. But it looks like I haven't implemented the read and write properly. So my parent just reads the first string it gets. Below is my code
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <sys/wait.h>
#include <stdbool.h>
#include <ctype.h>
/* Function declaration */
bool isNumeric(char* str);
int main(int argc, char* argv[]) {
// Check if input is recieved
if (argc == 1) {
printf("Input not received!\n");
exit(1);
}
// Check if the input is an int
if (isNumeric(argv[1]) == 0) {
printf("Input is not an Integer!\n");
exit(1);
}
// Initialize pipe
int fd[2];
pid_t childpid;
pipe(fd);
childpid = fork();
if (childpid == -1) {
printf("fork failed");
exit(1);
}
else if (childpid > 0) {
char string[100];
close(fd[1]);
printf("PARENT START\n");
while (read(fd[0], string, sizeof(string)) > 0) {
printf("%s\n", string);
}
printf("PARENT END\n");
close(fd[0]);
}
else {
close(fd[0]);
char string[100];
string[0] = '\0';
printf("CHILD START\n");
for (int i = 0; i < 5; i++) {
sprintf(string, "%d", i);
write(fd[1], string, strlen(string)+1);
}
printf("CHILD END\n");
close(fd[1]);
exit(0);
}
}
The output is just
PARENT START
CHILD START
CHILD END
0
PARENT END
My expected output is
PARENT START
CHILD START
CHILD END
0
1
2
3
4
PARENT END
I spent hours trying to synchronize the process, but I couldn't figure out how to fix the problem.
You ignore the return value of read, so the call to printf stops at the first zero byte. You send the messages delimited by zero bytes. Where's the code to find the zero bytes in the received data and extract the messages from the pipe?
You have code to send a message. It separates the messages with a terminating zero byte. Where's the code to receive a message, searching the incoming stream of data for zero bytes and passing on the data prior to it as a message?
Here's some ugly, inefficient code to receive a message. It checks the incoming stream of bytes for the terminating zero byte. It returns 0 on end of file, negative on error and 1 on success.:
int recvMessage (int fd, char* buf, int len)
{
while (len > 0)
{
int r = read(fd, buf, 1);
if (r <= 0) // pipe closed or error
return r;
if (*buf == 0) // we received a terminating zero byte
return 1;
buf++;
len--;
}
return -2; // message larger than buffer
}
Five things you need to learn:
sizeof(string) doesn't return the length of a string, much less one you haven't read in yet! It always returns 100 in this case.
write is not guaranteed to write the number of bytes provided as its third argument. You need to call write repeatedly until the entire buffer you want to write has been written.
read is not guaranteed to read the number of bytes provided as its third argument. You need to call read repeatedly until you've read the desired number of bytes.
In this case, you don't want to read a specific number of bytes. You want to read until one of the character you've read is a NUL.
Unless you pass 1 for read's third parameter, you might end up reading too much. This is not a problem; you just need to factor that in the next time you read.
Writer
Replace
write(fd[1], string, strlen(string)+1);
with
char *p = string;
size_t n = strlen(string) + 1;
while (n > 0) {
ssize_t rv = write(fd[1], p, n);
if (rv == -1) {
perror("write");
exit(1);
}
n -= rv;
p += rv;
}
Reader
Replace
char string[100];
while (read(fd[0], string, sizeof(string)) > 0) {
...
}
with
#define BLOCK_SIZE 100
char *string = NULL;
size_t size = 0;
size_t len = 0;
while (1) {
// Increase the buffer size if necessary.
if (size < len + BLOCK_SIZE) {
char *tmp = realloc(string, len + BLOCK_SIZE);
if (!tmp) {
perror("realloc");
exit(1);
}
string = tmp;
}
// Read from the pipe.
ssize_t rv = read(fd[0], string+len, BLOCK_SIZE);
if (rv == -1) {
perror("read");
exit(1);
}
// Handle EOF
if (rv == 0)
break;
len += rv;
// Check if we've received a message (or even more than one).
while (1) {
for (size_t i=0; i<len; ++i) {
if (!string[i]) {
// Handle a message.
...
len -= i+1;
memmove(string, string+i+1, len);
break;
}
}
}
}
// Handle a partial message.
if (len) {
fprintf(stderr, "Premature EOF");
exit(1);
}
free(string);

Why does program hang on child to parent communication?

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
$

Parent reads operations from file and the child sums it with bc

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

Unable to process the pipe function

Unable to process the pipe function where a give pipes in which one process sends a string message to a second process, and the second process reverses the case of each character in the message and sends it back to the first process.
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
#include <string.h>
#include <ctype.h>
#include <assert.h>
#include <stdbool.h>
// Parent: reads from P1_READ, writes on P1_WRITE
// Child: reads from P2_READ, writes on P2_WRITE
#define P1_READ 0
#define P2_WRITE 1
#define P2_READ 2
#define P1_WRITE 3
// the total number of pipe *pairs* we need
#define NUM_PIPES 2
/*
toggleString accepts an a pointer to char array, allocates size for the
string to be toggled,
copys the argument into a string, loops through the string and for every
uppercase character
we set it to its lower case counterpart and vice versa, returning the
toggled string
*/
char *toggleString(char *argv){
int i; /* Declare counter */
char *str = malloc(sizeof(argv[1])); /* Declare array sizeof input */
strcpy(str, argv); /* Copy String to char array */
for(i=0;str[i]!='\0';i++) { //Loop through length of string
if(str[i]>='A'&&str[i]<='Z'){ //if the array at i is uppercase
str[i]+=32; //Make it lower case
} else if (str[i]>='a'&&str[i]<='z') {// if the array at i is lowercase
str[i]-=32; //Make it uppercase
}
}
return str;
}
/*
int inputValidation accept and integer (number of arugments) and a
pointer to the cmd line input array
We check to see if the command line input contains the minimal number of
arugments and check to see
whether or not the user input contains at least one reversible haracter,
if all goes well we return 0
*/
int inputValidation(int argc, char *argv[]){
int i; //Declare counter variable
bool c = false; //Declare boolean flag using imported <stdbool.h>
char str[strlen(argv[1])]; //Declare str
strcpy(str, argv[1]); //copy argument into str
if (argc != 2) { // check to see if we have enough arguments to
continue
// Prompt user of correct usage
fprintf(stderr, "\nUsage: %s <string> or <'string 1, string 2', ...,
string n'> for multiple strings\n", argv[0]);
exit(EXIT_FAILURE); //Exit on improper input
} else {
//loop through our string
for(i=0;i<strlen(str);i++) {
//if any any char is a reversible character
if(isalpha((int) str[i])){
c = true; //set the flag to true
}
}
if(c == false){ //If flag is false input does not contain any
reversible charachters
printf("\nSorry, The string you entered did NOT contain any
Alphabetical Characters\nRun me again, with at least 1 Alphabetical
character\n\n");
exit(EXIT_FAILURE); //Exit on improper input
}
return (0);
}
}
/*
Main takes input from command line, calls input validation to make sure of
proper input,
then creates the pipes we will need and the forks the child process, Parent
and Child
execute they're respective code
*/
int main(int argc, char *argv[]) {
assert(argc>1);
int fd[2*NUM_PIPES]; //Declare int[] of file descriptors
int len, i; //Declare length and integer for count
pid_t pid; //Declare process id
char parent[strlen(argv[1])]; //Declare Parent array
char child[strlen(argv[1])]; //Declare Child array
if(inputValidation(argc, argv) == 0) /* Check for proper input */
strcpy(parent, argv[1]);
// create all the descriptor pairs we need
for (i=0; i<NUM_PIPES; ++i)
{
if (pipe(fd+(i*2)) < 0)
{
perror("Failed to allocate pipes");
exit(EXIT_FAILURE);
}
}
// fork() returns 0 for child process, child-pid for parent process.
if ((pid = fork()) < 0)
{
perror("Failed to fork process");
return EXIT_FAILURE;
}
//////////////////////////////Childs Code
BEGINS//////////////////////////////////
// if the pid is zero, this is the child process
if (pid == 0)
{
// Child. Start by closing descriptors we
// don't need in this process
close(fd[P1_READ]);
close(fd[P1_WRITE]);
// used for output
pid = getpid();
// wait for parent to send us a value
len = read(fd[P2_READ], &child, len);
if (len < 0)
{
perror("Child: Failed to read data from pipe");
exit(EXIT_FAILURE);
}
else if (len == 0)
{
// not an error, but certainly unexpected
fprintf(stderr, "Child: Read EOF from pipe");
}
else
{
// report pid to console
printf("Child(%d): Recieved Message\n\nChild(%d): Toggling Case and
Sending to Parent\n",pid, pid);
// send the message to toggleString and write it to pipe//
if (write(fd[P2_WRITE], toggleString(child), strlen(child)) < 0)
{
perror("Child: Failed to write response value");
exit(EXIT_FAILURE);
}
}
// finished. close remaining descriptors.
close(fd[P2_READ]);
close(fd[P2_WRITE]);
return EXIT_SUCCESS;
}
//child code ends///
//////////////////////////////Parent Code
BEGINS//////////////////////////////////
// Parent. close unneeded descriptors
close(fd[P2_READ]);
close(fd[P2_WRITE]);
// used for output
pid = getpid();
// send a value to the child
printf("\nParent(%d): Sending %s to Child\n\n", pid, argv[1]);
if (write(fd[P1_WRITE], argv[1], strlen(argv[1])) != strlen(argv[1]))
{
perror("Parent: Failed to send value to child ");
exit(EXIT_FAILURE);
}
// now wait for a response
len = read(fd[P1_READ], &parent, strlen(parent));
if (len < 0)
{
perror("Parent: failed to read value from pipe");
exit(EXIT_FAILURE);
}
else if (len == 0)
{
// not an error, but certainly unexpected
fprintf(stderr, "Parent(%d): Read EOF from pipe", pid);
}
else
{
// report what we received
printf("\nParent(%d): Received %s from Child\n\n", pid, parent);
}
// close down remaining descriptors
close(fd[P1_READ]);
close(fd[P1_WRITE]);
// wait for child termination
wait(NULL);
return EXIT_SUCCESS;
}
//////////////////////////////Parent Code
ENDS//////////////////////////////////
This works:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
#include <string.h>
#include <ctype.h>
#include <assert.h>
#include <stdbool.h>
#define P1_READ 0
#define P2_WRITE 1
#define P2_READ 2
#define P1_WRITE 3
#define NUM_PIPES 2
static
char *toggleString(char *argv)
{
int i;
char *str = malloc(strlen(argv) + 1); /* Key Fix */
strcpy(str, argv);
for (i = 0; str[i] != '\0'; i++)
{
if (str[i] >= 'A' && str[i] <= 'Z')
{
str[i] += 32;
}
else if (str[i] >= 'a' && str[i] <= 'z')
{
str[i] -= 32;
}
}
return str;
}
static
int inputValidation(int argc, char *argv[])
{
bool c = false;
char str[strlen(argv[1])];
strcpy(str, argv[1]);
if (argc != 2)
{
fprintf(stderr, "\nUsage: %s <string> or <'string 1, string 2', ..., string n'> for multiple strings\n", argv[0]);
exit(EXIT_FAILURE);
}
else
{
for (size_t i = 0; i < strlen(str); i++)
{
if (isalpha((int)str[i]))
{
c = true;
}
}
if (c == false)
{
printf("\nSorry, The string you entered did NOT contain any"
" Alphabetical Characters\nRun me again, with at least 1 Alphabetical"
" character\n\n");
exit(EXIT_FAILURE);
}
return(0);
}
}
int main(int argc, char *argv[])
{
assert(argc > 1);
int fd[2 * NUM_PIPES];
int len, i;
pid_t pid;
char parent[strlen(argv[1])];
char child[strlen(argv[1])];
if (inputValidation(argc, argv) == 0)
strcpy(parent, argv[1]);
for (i = 0; i < NUM_PIPES; ++i)
{
if (pipe(fd + (i * 2)) < 0)
{
perror("Failed to allocate pipes");
exit(EXIT_FAILURE);
}
}
if ((pid = fork()) < 0)
{
perror("Failed to fork process");
exit(EXIT_FAILURE);
}
if (pid == 0)
{
close(fd[P1_READ]);
close(fd[P1_WRITE]);
pid = getpid();
len = read(fd[P2_READ], child, sizeof(child));
if (len < 0)
{
perror("Child: Failed to read data from pipe");
exit(EXIT_FAILURE);
}
else if (len == 0)
{
fprintf(stderr, "Child: Read EOF from pipe\n");
}
else
{
child[len] = '\0';
printf("Child(%d): Received Message [%s]\nChild(%d): Toggling Case and Sending to Parent\n", pid, child, pid);
char *toggled = toggleString(child);
printf("Child(%d): Sending [%s]\n", pid, toggled);
if (write(fd[P2_WRITE], toggled, len) < 0)
{
perror("Child: Failed to write response value");
exit(EXIT_FAILURE);
}
free(toggled);
}
close(fd[P2_READ]);
close(fd[P2_WRITE]);
return EXIT_SUCCESS;
}
close(fd[P2_READ]);
close(fd[P2_WRITE]);
pid = getpid();
printf("\nParent(%d): Sending [%s] to Child\n\n", pid, argv[1]);
len = strlen(argv[1]);
if (write(fd[P1_WRITE], argv[1], len) != len)
{
perror("Parent: Failed to send value to child");
exit(EXIT_FAILURE);
}
len = read(fd[P1_READ], parent, sizeof(parent));
if (len < 0)
{
perror("Parent: failed to read value from pipe");
exit(EXIT_FAILURE);
}
else if (len == 0)
{
fprintf(stderr, "Parent(%d): Read EOF from pipe\n", pid);
}
else
{
parent[len] = '\0';
printf("\nParent(%d): Received [%s] from Child\n\n", pid, parent);
}
close(fd[P1_READ]);
close(fd[P1_WRITE]);
wait(NULL);
return EXIT_SUCCESS;
}
It was painful extracting your code from your comments, and the split over multiple line strings, and so on. The toggleString() function was broken — allocating 1 byte and then copying a string over that. The other code was not careful about null-terminating strings and handling them. These are basically the problems diagnosed in the comments.
Sample run:
$ pp53 'AbSoLuTeLy GlOrIoUs'
Parent(5209): Sending [AbSoLuTeLy GlOrIoUs] to Child
Child(5210): Received Message [AbSoLuTeLy GlOrIoUs]
Child(5210): Toggling Case and Sending to Parent
Child(5210): Sending [aBsOlUtElY gLoRiOuS]
Parent(5209): Received [aBsOlUtElY gLoRiOuS] from Child
$

monitor bytes in stdin, stdout, and stderr in C

I need to count how many bytes are being sent to a child process through stdin, and how many bytes a child process is writing to stdout and stderr. The child process calls execvp, so I have no way to monitor those stats from within the process itself. My current tactic involves creating 3 additional child processes, one each to monitor each of the std streams through pipes (or in the case of stdin, just reading from stdin).
This tactic seems really frail at best, and I'm doing something strange which makes it so that the processes monitoring stdout/err cannot read from their respective ends of the pipes (and makes them hang indefinitely). Code below.
This creates the three helper child processes, and should allow them to count the stats:
void controles(struct fds *des)
{
int ex[2];
int err[2];
int n_in = 0;
int c_in;
int n_ex = 0;
int c_ex;
int n_err = 0;
int c_err;
pipe(ex);
pipe(err);
/*has two fields, for the write end of the stdout pipe and the stderr pipe. */
des->err = err[1];
des->ex = ex[1];
switch (fork()) {
case 0: /*stdin */
while (read(0, &c_in, 1) == 1)
n_in++;
if (n_in > 0)
printf("%d bytes to stdin\n", n_in);
exit(n_in);
default:
break;
}
switch (fork()) {
case 0: /*stdout */
close(ex[1]);
/*pretty sure this is wrong */
while (read(ex[0], &c_ex, 1) == 1) {
n_ex++;
write(1, &c_ex, 1);
}
if (n_ex > 0)
printf("%d bytes to stdout\n", n_ex);
close(ex[0]);
exit(n_ex);
default:
close(ex[0]);
}
switch (fork()) {
case 0: /*error */
close(err[1]);
/*also probably have a problem here */
while (read(err[0], &c_err, 1) == 1) {
n_err++;
write(2, &c_err, 1);
}
if (n_err > 0)
printf("%d bytes to stderr\n", n_err);
close(err[0]);
exit(n_err);
default:
close(err[0]);
}
}
and this is a code fragment (within the child process) which sets up the two fd's from the fds struct so that the child process should write to the pipe instead of stdin/stderr.
dup2(des.ex, 1);
dup2(des.err, 2);
close(des.ex); close(des.err); /*Is this right?*/
execvp(opts->exec, opts->options); /*sure this is working fine*/
I'm lost, any help would be appreciated.
I think your code could be improved by breaking things apart a little; the accounting and copying routines are all basically the same task, and if you choose to continue down the road with multiple processes, can be written simply:
void handle_fd_pair(char *name, int in, int out) {
char buf[1024];
int count = 0, n;
char fn[PATH_MAX];
snprintf(fn, PATH_MAX - 1, "/tmp/%s_count", name);
fn[PATH_MAX-1] = '\0';
FILE *output = fopen(fn, "w");
/* handle error */
while((n = read(in, buf, 1024)) > 0) {
count+=n;
writen(out, buf, n); /* see below */
}
fprintf(output, "%s copied %d bytes\n", name, count);
fclose(output);
}
Rather than one-char-at-a-time, which is inefficient for moderate amounts of data, we can handle partial writes with the writen() function from the Advanced Programming in the Unix Environment source code:
ssize_t /* Write "n" bytes to a descriptor */
writen(int fd, const void *ptr, size_t n)
{
size_t nleft;
ssize_t nwritten;
nleft = n;
while (nleft > 0) {
if ((nwritten = write(fd, ptr, nleft)) < 0) {
if (nleft == n)
return(-1); /* error, return -1 */
else
break; /* error, return amount written so far */
} else if (nwritten == 0) {
break;
}
nleft -= nwritten;
ptr += nwritten;
}
return(n - nleft); /* return >= 0 */
}
With the helper in place, I think the rest can go more easily. Fork a
new child for each stream, and give the in[0] read-end, out[1] and
err[1] write-ends of the pipes to the child.
All those close() calls in each child are pretty ugly, but trying to
write a little wrapper around an array of all the fds, and exempting the
ones passed in as arguments, also seems like trouble.
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#ifndef PATH_MAX
#define PATH_MAX 128
#endif
void handle_fd_pair(char *name, int in, int out) {
char buf[1024];
int count = 0, n;
char fn[PATH_MAX];
snprintf(fn, PATH_MAX - 1, "/tmp/%s_count", name);
fn[PATH_MAX-1] = '\0';
FILE *output = fopen(fn, "w");
/* handle error */
while((n = read(in, buf, 1024)) > 0) {
count+=n;
writen(out, buf, n); /* see below */
}
fprintf(output, "%s copied %d bytes\n", name, count);
fclose(output);
}
int main(int argc, char* argv[]) {
int in[2], out[2], err[2];
pid_t c1, c2, c3;
pipe(in);
pipe(out);
pipe(err);
if ((c1 = fork()) < 0) {
perror("can't fork first child");
exit(1);
} else if (c1 == 0) {
close(in[0]);
close(out[0]);
close(out[1]);
close(err[0]);
close(err[1]);
handle_fd_pair("stdin", 0, in[1]);
exit(0);
}
if ((c2 = fork()) < 0) {
perror("can't fork second child");
exit(1);
} else if (c2 == 0) {
close(in[0]);
close(in[1]);
close(out[1]);
close(err[0]);
close(err[1]);
handle_fd_pair("stdout", out[0], 1);
exit(0);
}
if ((c3 = fork()) < 0) {
perror("can't fork third child");
exit(1);
} else if (c3 == 0) {
close(in[0]);
close(in[1]);
close(out[0]);
close(out[1]);
close(err[1]);
handle_fd_pair("stderr", err[0], 2);
exit(0);
}
/* parent falls through to here, no children */
close(in[1]);
close(out[0]);
close(err[0]);
close(0);
close(1);
close(2);
dup2(in[0], 0);
dup2(out[1], 1);
dup2(err[1], 2);
system(argv[1]);
exit(1); /* can't reach */
}
It seems to work for toy applications anyway :)
$ ./dup cat
hello
hello
$ ls -l *count
-rw-r--r-- 1 sarnold sarnold 22 2011-05-26 17:41 stderr_count
-rw-r--r-- 1 sarnold sarnold 21 2011-05-26 17:41 stdin_count
-rw-r--r-- 1 sarnold sarnold 22 2011-05-26 17:41 stdout_count
$ cat *count
stderr copied 0 bytes
stdin copied 6 bytes
stdout copied 6 bytes
I think it is worth pointing out that you could also implement this
program with only one process, and use select(2) to determine which
file descriptors need reading and writing.
Overall, I think you're on the right track.
One problem is that in your stderr and stdout handlers, which should be picking bytes off the pipe and writing to the real stderr/stdout, you're writing back to the same pipe.
It would also be helpful to see how you start the child processes. You gave a code fragment to close the real stderr and then dup2 the pipe fd back to stderr's fd, but you probably want this in the parent process (after fork and before exec) so that you don't need to modify the source code to the child process. You should be able to do this generically all from the parent.

Resources