Having issues when using dup2 twice within the same process - c

I cannot figure out why does this program display only garbage characters when trying to print (at the standard output) some data read from both a file and a pipe. It is interesting that this problem occurs only when I use more than one dup2 instruction within the same process.
#define BSIZE 256
const char in[] = "i.txt";
int main(){
int id, fd[2];
pipe(fd);
id = fork();
if(id == 0){
close(fd[0]);
dup2(fd[1],1);
printf("blbl");
close(fd[1]);
}
if(id != 0){
close(fd[1]);
char tmp[BSIZE];
int f = open(in,O_RDONLY);
dup2(f,0);
fgets(tmp,BSIZE,stdin);
puts(tmp);
dup2(fd[0],0);
fgets(tmp,BSIZE,stdin);
puts(tmp);
close(fd[0]);
close(f);
wait(NULL);
}
return 0;
}

Related

how make cat and grep work in the first and the second pipe in c writing like heredoc in bash <<

I am working to make a shell like bash, but i have trouble solving heredoc << so i made a test code as simple as possible for this question.
void pipeline()
{
int i = 0;
int fd[2];
pid_t pid;
int fdd = 0;
while (i < 2)
{
pipe(fd);
pid = fork();
if (pid == 0)
{
//dup2(fd[1],1); if i dup in the first pipe cat dont finalize
if (i == 0)
dup2(fd[0],0);
write(fd[1], "hello\nhow\nare\nyou\n", 17);
close(fd[0]);
close(fd[1]);
dup2(fdd, 0);
if (i == 0)
execlp("cat", "cat", NULL);
else
execlp("grep", "grep", "you" , NULL);
perror("error");
exit(1);
}
else
{
close(fd[1]);
fdd = fd[0];
wait(NULL);
i++;
}
}
}
int main(int *argc, char **argv, char **env)
{
pipeline();
}
I know that cat and grep need an EOF to run; what I'm doing is writing in stdin and running cat, but my question is: how do I save stdout for grep without duping stdout on the first pipe?
If I dup on dup2(fd[1],1) cat does not work in the first pipe, could someone help me out to make this code work? And make it as similar to bash heredoc as well if possible.
how do I save stdout for grep without duping stdout on the first pipe?
I'd rearrange the creation of the child processes from rightmost to leftmost - then grep is created first and can output to the initial output descriptor. A necessary change is to run all child processes before waiting on one as well as before writing, so that there's no deadlock even if the pipe buffer wouldn't suffice for the heredoc.
void pipeline()
{
int i = 2; // create children from last to first
int fd[2];
pid_t pid;
int fdd = 1; // output of last child is STDOUT
while (i--)
{
pipe(fd);
pid = fork();
if (pid == 0)
{
dup2(fdd, 1); // child's output
dup2(fd[0], 0);
close(fd[0]);
close(fd[1]);
if (i == 0)
execlp("cat", "cat", "-A", NULL);
else
execlp("grep", "grep", "you" , NULL);
perror("error");
exit(1);
}
if (fdd != 1) close(fdd); // close if a pipe write end
fdd = fd[1]; // preceding child's output is pipe write end
close(fd[0]);
}
write(fd[1], "hello\nhow\nare\nyou\n", 17);
close(fd[1]); // signal EOF to child
while (wait(NULL) > 0) ; // wait for all children
}

Linux C - child process thinks the pipe is empty

I have a problem with this homework exercise. I am learning the Linux C so I am a beginner.
Now the exercise is simple: I have to create a child process. Now the parent process needs to read a text file (e.g. a.txt) and sends through a pipe. The child process reads from pipe and prints the content of the pipe to the terminal. But I don't understand that the child process doesn't read the pipe because it thinks the pipe is empty.
I post the code what I did so far:
#include "myinclude.h" //a separate file which contains all needed headers to run the program.
#define MERET 80
int main(int argc,char *argv[]){
int pfd[2];
int status;
char buffer[MERET];
pid_t pid;
FILE *fp1,*fp2;
if(argc != 2){
printf("Nincs eleg argumentum");
}
if(pipe(pfd) < 0){
syserr("pipe");
}
if((pid = fork()) < 0){
syserr("fork");
}
if(pid == 0){
close(pfd[1]);
if ((fp1 = fdopen (pfd[0],"r")) <0){
syserr("fdopen");
}
printf("mukodsz");
while(fgets(buffer,MERET,fp1) != NULL){//something here is not good
printf("%s",buffer);
fprintf(stdout,"Siker");
}
close(pfd[0]);
exit(0);
}
close(pfd[0]);
if ((fp1 = fdopen (pfd[1],"w")) == NULL){
syserr("fdopen");
}
if((fp2 = fopen(argv[1],"r")) < 0){
syserr("fopen");
}
while(fgets(buffer,MERET,fp2) != NULL){
fprintf(fp1,"%s",buffer);
//fprintf(stdout,"Siker\n");
}
close(pfd[1]);
wait(&status);
//fprintf(stdout,"Siker");
exit(0);
}
In my language "siker" means Success. I used it to debug the program but while loop of the child process is not printing anything.
When you fdopen. you must fclose.
If you close the original file descriptor instead, all not-yet-written data in buffers associated with the FILE* get lost.

How to distinguish one child process from other child processes

I have an assignment for class and I am confused on this part of the requirements. So we need to make a multi process word counter with n number of processes and n will be an input argument for the program. Each process needs to do their own mini word count of a select portion of the inputted file. So essentially the inputted file will be divided into 1/n parts and split between n processes.
I understand how to fork the processes through a for loop and how to use pipes to send the mini word count from the children processes to the parent process, but I unsure of how to tell a certain process to do a select part of the input file.
Would you use their PID values to check which process they are then assign them their task?
This is my code so far.
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#define MSGLEN 64
#define MESSES 3
int main(){
int fd[2];
pid_t pid;
int result;
//Creating a pipe
result = pipe (fd);
if (result < 0) {
//failure in creating a pipe
perror("pipe error\n");
exit (1);
}
//Creating a child process
for(int i = 0; i < MESSES; i++){
if ((pid = fork()) < 0) {
//failure in creating a child
perror ("fork error\n");
exit(2);
}
if(pid == 0)
break;
}
if (pid == 0) {
// ACTUALLY CHILD PROCESS
char message[MSGLEN];
//Clearing the message
memset (message, 0, sizeof(message));
printf ("Enter a message: ");
//scanf ("%s",message);
fgets (message, 1024, stdin);
close(fd[0]);
//Writing message to the pipe
write(fd[1], message, strlen(message));
close(fd[1]);
close(fd[0]);
exit (0);
}
else {
//Parent Process
char message[MSGLEN];
char *ptr;
long wc;
close(fd[1]);
while (1) {
//Clearing the message buffer
memset (message, 0, sizeof(message));
//Reading message from the pipe
if(read(fd[0], message, sizeof(message)) == 0)
exit(0);
printf("Message entered %s\n",message);
/*
Message entered needs to be in the format of number first space then string for it to work
*/
wc = 0;
wc = strtol(message, &ptr, 10);
printf("The number(unsigned long integer) is %ld\n", wc);
printf("String part is %s", ptr);
}
close(fd[0]);
wait(NULL);
// exit(0);
}
return 0;
}
The key thing to remember when using fork is that the parent and child share the same memory and a copy of everything the parent has is passed to the child. At which point the child has now forked the parents data.
In the code below we're counting how many processes we've created. You could if you wanted use this as an argument in the child ie the nth child gets value n.
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#define PROCESS_COUNT 50
int main(void) {
pid_t pid;
size_t pid_count = 0;
//pid_t pid_array[PROCESS_COUNT];
for(int i = 0; i < PROCESS_COUNT; i++) {
if ((pid = fork()) < 0) {
perror ("fork error\n");
exit(2);
}
if (pid == 0) {//child
size_t n = 0;
size_t p = getpid();
while(n++ < 2) {
//Next line is illustration purposes only ie I'm taking liberties by
//printing a pid_t value
printf("child %zu has pid_count == %zu\n", p, pid_count);
sleep(1);
}
exit (0);
}
else {
//Count how many process we've created.
pid_count++;
int status;
waitpid( -1, &status, WNOHANG);
}
}
wait(NULL);
return 0;
}
If you want to get really fancy you can use IPC using pipes or shared memory. There are lots of ways to get data from one process to another, sometimes something as simple as temporary files is more than sufficient. For your problem I'd use mmap but it does not need to be that complicated

What is the correct way to read from multiple pipes?

So in my program, the user gives three arguments. Then, I pipe it into three children.
Two children all do their own calculations, then exits. The last child displays the results.
The parent waits for all children to finish, then it simply terminates the program.
A summary of what the children do:
inputs are a, b, c.
child0: c0 = a*b
child1: c1 = b*c
overview: printf("%d %d", c0, c1);
I can't figure out how to get the overview to print out correctly. It keeps printing out weird corrupted characters or boxes.
Anyone have any advice on how to do it correctly? I read a book, but it only went over in detail single child-parent pipes. This deals with multiple children, and I guess that's where I got confused. Thanks for your help!
Code below:
int main(int argc, char *argv[]) {
int fd[4];
int a, b, c, c0, c1, status;
char char0[10];
char char1[10];
pid_t child0;
pid_t child1;
pid_t overview;
// Set argv to ints
a = atoi(argv[1]);
b = atoi(argv[2]);
c = atoi(argv[3]);
// Pipe
pipe(fd);
// child0
if((child0 = fork()) == 0) {
close(fd[2]);
c0 = a*b;
sprintf(char0, "%d", c0);
write(fd[0], char0, 10);
close(fd[0]);
exit(0);
}
// child1
if((child1 = fork()) == 0) {
close(fd[2]);
c1 = b*c;
sprintf(char1, "%d", c1);
write(fd[1], char1, 10);
close(fd[1]);
exit(0);
}
// overview
if((overview = fork()) == 0) {
close(fd[0]);
close(fd[1]);
read(fd[2], char0, 10);
read(fd[2], char1, 10);
printf("%s %s", char0, char1); //Prints weird stuff??
close(fd[2]);
exit(0);
}
// Wait for children to finish
waitpid(child0, &status, 0);
waitpid(child1, &status, 0);
waitpid(overview, &status, 0);
exit(0);
}
Your code for declaring pipe is totally wrong, pipes will have only two ends and for declaring three pipes you need to declare as follows
pd1[2];
pd2[2];
pd3[2];
from one end you can write that is pd1[1];
and from the other end you can read pd1[0];
So your code will look like,
int main(int argc, char *argv[]) {
int fd1[2];
int fd2[2];
int fd1[2];
int a, b, c, c0, c1, status;
char char0[10];
char char1[10];
pid_t child0;
pid_t child1;
pid_t overview;
// Set argv to ints
a = atoi(argv[1]);
b = atoi(argv[2]);
c = atoi(argv[3]);
// Pipe
pipe(fd1);
pipe(fd2);
pipe(fd3);
// child0
if((child0 = fork()) == 0) {
close(fd1[0]);
c0 = a*b;
sprintf(char0, "%d", c0);
write(fd1[1], char0, 10);
close(fd[1]);
exit(0);
}
// child1
if((child1 = fork()) == 0) {
close(fd2[0]);
c1 = b*c;
sprintf(char1, "%d", c1);
write(fd2[1], char1, 10);
close(fd2[1]);
exit(0);
}
// overview
if((overview = fork()) == 0) {
close(fd1[1]);
close(fd2[1]);
read(fd1[0], char0, 10);
read(fd2[0], char1, 10);
printf("%s %s", char0, char1); //Prints weird stuff??
//close(fd[2]);
exit(0);
}
// Wait for children to finish
waitpid(child0, &status, 0);
waitpid(child1, &status, 0);
waitpid(overview, &status, 0);
exit(0);
}
This code might also not correct I have just explained how to use pipes, see opening and closing of pipes, that is while writing reading end should be closed and while reading writing end should be closed.
edit
see this post and execute small program then modify your code step by step you will get understand.
How to send a simple string between two programs using pipes?

Having issues with pipe, fork, dup2

I am using pipes, fork , dup2 to implement “ls | more” or “ls | sort” etc.
I am just not able to understand the issue here.
When I run my program, I get this error:
./a.out
Missing filename ("less --help" for help)
Why am I getting "less" ??
What is wrong with this code ? If I change “more” to “ls” again, it works fine. I mean, its like doing ls | ls.
#define STDIN 0
#define STDOUT 1
int main()
{
int fd[2];
int pid;
char *lschar[20]={"ls",NULL};
char *morechar[20]={"more",NULL};
pid = fork();
if (pid == 0) {
/* child */
int cpid;
cpid = fork();
if(cpid == 0) {
//printf("\n in ls \n");
pipe(fd);
dup2(fd[1], STDOUT);
close(fd[0]);
close (fd[1]);
execvp("ls",lschar);
} else if(cpid>0) {
waitpid(cpid, NULL,0);
dup2(fd[0],STDIN);
close(fd[0]);
close(fd[1]);
execvp("more", morechar);
}
} else if (pid > 0) {
/* Parent */
waitpid(pid, NULL,0);
}
return 0;
}
Appreciate your help.
Your main problem lies in your placement of the pipe() call. You must call it before you fork():
#include <unistd.h>
#include <stdio.h>
#include <sys/types.h>
#define STDIN 0
#define STDOUT 1
int main()
{
int fd[2];
int pid;
char *lschar[20]={"ls",NULL};
char *morechar[20]={"more", NULL};
pid = fork();
if (pid == 0) {
/* child */
int cpid;
pipe(fd);
cpid = fork();
if(cpid == 0) {
//printf("\n in ls \n");
dup2(fd[1], STDOUT);
close(fd[0]);
close (fd[1]);
execvp("ls",lschar);
} else if(cpid>0) {
dup2(fd[0],STDIN);
close(fd[0]);
close(fd[1]);
execvp("more", morechar);
}
} else if (pid > 0) {
/* Parent */
waitpid(pid, NULL,0);
}
return 0;
}
Otherwise, the more process doesn't have the correct file descriptors. Further, the waitpid() in your more process is problematic and unnecessary (more will wait for input on its own). If ls had a particularly long output the pipe could get full causing ls to block on its writes. The result is a deadlock and it waits forever. Hence, I've also removed the offending waitpid() call.
Also, if you make a good practice of checking the return values of functions like pipe() and dup2() this error would have been much easier to find -- you would have seen that your dup2() was failing.

Resources