How to use pipe and fork - c

I have the following code(there's a bunch of extra headers that were used in other parts of the code that I removed since they don't have anything to do with my issue). I have also removed the error checking for fork and pipe for brevity:
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <dirent.h>
#include <sys/stat.h>
#include <string.h>
#include <stdbool.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/wait.h>
void compile(char *fullname)
{
int pid = fork();
int pipe_send_output[2];
pipe(pipe_send_output);
if (pid == 0)
{
close(pipe_send_output[0]);
dup2(pipe_send_output[1], STDERR_FILENO); // redirect output of gcc to the pipe
execlp("gcc", "gcc", "-Wall", fullname, NULL);
printf("execlp error");
}
else
{
close(pipe_send_output[1]);
dup2(STDIN_FILENO, pipe_send_output[0]); // redirect from pipe to stdin
close(pipe_send_output[0]);
exit(0);
}
}
int main(){
compile("folder/small.c");
}
The compile function is supposed to create a new process which compiles some c file using gcc, and then sends the output(errors and warnings) to the parent process, to be printed. To do this I redirected STDERR to the write end of the pipe, and then in the parent process redirected the read end to stdin. I can't for the life of me figure out why it's not displaying anything. If I remove the redirection of stderr to the pipe, then plenty of stuff it outputed, so the issue isn't gcc not outputting anything. I tried to replace the redirection to stdin with reading from the pipe and then printing it, but that has the same result. A couple of time this did print one or 2 characters, which is even more confusing. This is the code that replaces the second call to dup2 for printing:
char buffer[1024];
while(read(pipe_send_output[0], buffer, 1024)) printf("%s", buffer);
I've looked everywhere on google and on the man page and still don't have a clue what's wrong.
Disclaimer:This is part of a project for a lab at university. The code that I wrote follows the blueprint from the materials my professor provided, and makes perfect sense to me. Any hint on what the issue may be is appreciated.

Related

C - Redirecting awk/sed stdout to pipe doesn't work

So I have an exercise to do, and one part of this exercise requires us to execute a command passed as an argument, be able to pass it some strings on stdin, and get its output on stdout and stderr.
How I did it, I need to redirect the stdout and stderr (of the child, which is gonna call an exec) to a couple of pipes (other end of the pipes is held open by the parent).
I managed to do it, when I ask it to execute bash and send it "ls", it gives me what i want, where i want it. Same with cat and others.
Problem is, when I try executing awk or sed, nothing is ever written on the pipe. Ever.
If i leave stdout untouched, it does print it how it should. But as soon as i redirect the stdout, nothing.
I tried everything, select(), wait(), sleep() (even though it's not allowed). Nothing seems to work.
I made a minimum working example of what i mean (clearly, it lacks of conventions and mindful writing, as free() and close(), but it does it's job) which Is the one I'm attaching. The code works when i call it like this:
./program $(which bash)
It prompts for something, i write "ls" and it gives me the result expected
but when i try
./program $(which awk) '{print $0;}'
I get nothing at all
Here's the code (minimum working example):
#define _GNU_SOURCE
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <stdlib.h>
#include <fcntl.h>
#include <string.h>
#include <sys/wait.h>
#include <sys/time.h>
#include <signal.h>
#include <errno.h>
int main(int argc, char* argv[]){
int fdStdinP[2],fdStdoutP[2];
char *string,*array[3];
array[0]=argv[1];
array[1]=argv[2];
array[2]=0;
pipe(fdStdinP);
pipe(fdStdoutP);
int pid=fork();
if(pid==0){
close(fdStdinP[1]);
close(fdStdoutP[0]);
dup2(fdStdinP[0],0);
close(fdStdinP[0]);
dup2(fdStdoutP[1],1);
close(fdStdoutP[1]);
//as suggested, the file descriptors are now closed
execvp(argv[1],array);
perror("");
return 0;
}
close(fdStdinP[0]);
close(fdStdoutP[1];
string=calloc(1024,sizeof(char));
read(0,string,1024);
write(fdStdinP[1],string,1024);
free(string);
string=calloc(1024,sizeof(char));
read(fdStdoutP[0],string,1024);
printf("I have read:%s",string);
return 0;
}
Thank you for your time.
Awk continues to wait for input and buffers its output, so appears to hang. Closing the sending end will tell awk that it's input has ended so that it will end and flush its output.
write(fdStdinP[1],string,1024);
close(fdStdinP[1]); // just added this line.

Reading and writing into the same file using a named pipe in C

More specifically, the program is supposed to emulate the bash command cat file| grep $keyword > file.
What I've done is: In the parent I read every character from the file and format them into lines which I then send to the named pipe, then in the child write the lines containing the keyword into the original file.
However, I receive a segmentation fault error when attempting to read the second character from the original file, which I assume is because the parent is waiting for the child to write in the original file instead of instead of reading the contents of said file.
Any help with the implementation/explanation of why exactly the error occurs would be great.
#include <stdio.h>
#include <string.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
#include <signal.h>
#include <errno.h>
char key[20], *c,line[40];
int fd,fd_r,fd_w,fd_fr,fd_fw,counter=0;
int main(){
pid_t pid;
mkfifo("fifo1",0777);
fgets(key,10,stdin);
int k=0;
if ((pid=fork()) < 0)
perror("err_fork");
if(pid){ //PARENT
printf("%d\n",fd_r=open("prog.c",O_RDONLY));
printf("%d\n",fd_fw=open("fifo1",O_WRONLY));
while(read(fd_r,c,1)){
line[k++]=(*c);
while(read(fd_r,c,1) && ((*c)!='\n'))
line[k++]=(*c);
line[k]=0;
write(fd_fw,line,strlen(line)+1);
memset(line,0,sizeof(line));
}
close(fd_r);
close(fd_fw);
}
else{ //CHILD
printf("%d\n",fd_w=open("prog.c",O_WRONLY));
printf("%d\n",fd_fr=open("fifo1",O_RDONLY));
while(read(fd_fr,line,sizeof(line))){
c=strstr(line,key);
if(c)
write(fd_w,line,strlen(line)+1);
}
close(fd_w);
close(fd_fr);
}
unlink("fifo1");
}
You're segfaulting because you're trying to read a byte into c. However, c is an uninitialized global pointer and thus it's equal to NULL. Trying to read data at that location is therefore an invalid use of memory.
What you do instead is declare
char c;
and then
read(fd_r,&c,1)

How to reopen stdout if it is closed by the process who called exec

Is it possible to reopen stdout, if it is already closed.
For example the below code.
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <fcntl.h>
#include <stdlib.h>
#include <string.h>
int main(){
fcntl(1,F_SETFD,FD_CLOEXEC) ;
execve("./test2",NULL, NULL);
printf("Can you see me [TWO]\n");
}
The above code closes Standard output file descriptor and then calls execve. The code for test2 is below
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <fcntl.h>
int main() {
printf(" standard output\n");
perror("standard error");
}
Output:
standard error: Bad file descriptor
Is there any way of opening stdout in test2.c
First, it is important to notice - you just set fd 1 as CLOEXEC, so of course it will be closed upon the 'execve' call. Make sure you understand that. If you are able to edit 'test1' - you can just remove the 'fcntl' call.
But, on the other hand - if you can't edit 'test'1 at all - there might be a few ways to get around it.
If you are OK with 'stdout' and 'stderr' are pointing to the same place (maybe they are initially?) - you can just duplicate fd=2 into 1, using the following call: dup2(2,1).
(Linux specific) if 'fork' is somehow called before the 'execve', you might be able to access the closed fd using the procfs filesystem. Assuming the parent process of 'test2' have the 'stdout' you want open in fd=1, you can just get its PID using getppid(), and then open /proc/<ppid>/fd/1 (and of course - dup2 the resulting fd into 1).
Otherwise - there might be no way to "reopen" the file descriptor - as it was closed. Obviously, you can always open the file you want the stdout to point on from now on (dup2 it to 1!), and avoid the 'Bad file descriptor' error.
Good luck!

Program running mkfifo doesn't work

I'm trying to make a named pipe on c under linux using the mkfifo command. But when I run the program, I either get a "no such file or directory" error or absolutely nothing (console doesn't display anything)
Here is my code :
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
#define MAX_LINE 80
int main(int argc, char** argv) {
int create;
//mkfifo("/tmp/myfifo", 0666);
create = mkfifo("tmp/myfifo", 0666);
if (create==-1)
{
printf("error%s", strerror(errno));
}
char line[MAX_LINE];
int pipe;
pipe = open("/tmp/myfifo", O_WRONLY);
if (pipe==-1)
{printf("error");
}
printf("Enter line: ");
fgets(line, MAX_LINE, stdin);
write(pipe, line, strlen(line));
sleep (100);
close(pipe);
return 0;
}
I am still learning, and I don't understand what i'm doing wrong. Thanks for your help.
For a named pipe to be useful, somebody has to read it and somebody has to write it. Usually this will be 2 separate programs. Your program is the writer. Where is the reader?
If there is no reader, it is normal for the program to block on the O_WRONLY open. So when your program appears to be doing nothing, it's really just doing this:
pipe = open("/tmp/myfifo", O_WRONLY);
which waits for a reader to show up.
In another terminal, run cat /tmp/myfifo. The presence of a reader will allow the writer to make progress. Your program will wake up and move on to the Enter line prompt, and what you enter will be read by the cat and written to the second terminal.
The other problem is an inconsistency in your filenames. In one place you wrote "tmp/myfifo" without a leading slash, so you are trying to create the named pipe in a tmp directory that is inside the current working directory. If that tmp directory doesn't exist, No such file or directory will be the result.

How do I prevent a library to print to stdout (in Linux/C)?

I am using a library that prints all kind of crappy messages to stdout. I try to keep a clean output on my program, but that makes it impossible.
Any idea?
You can close() the stdout socket and then open a new socket to /dev/null (assuming pretty much anything but windows here).
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
int main() {
int newfd;
printf("good things come...\n");
close(1);
newfd = open("/dev/null", O_WRONLY);
if (newfd != 1) {
fprintf(stderr, "uh oh... we didn't duplicate the socket properly\n");
exit(1);
}
printf("...to those that wait()\n");
}
And then running this you get:
$ ./test
good things come...
Note the no final line from printf.
[but I agree with the comments: using libraries that show signs of bad-things is probably a bad-choice for other reasons beyond the first one you spot]

Resources