Reading from FIFO after unlink() - c

I have created a FIFO, wrote to it and unlinked it.
To my surprise I was able to read data from the fifo after unlinking, why is that?
#include <fcntl.h>
#include <sys/stat.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
#define MAX_BUF 256
int main()
{
int fd;
char * myfifo = "/tmp/myfifo";
/* create the FIFO (named pipe) */
mkfifo(myfifo, 0666);
int pid = fork();
if (pid != 0)
{
/* write "Hi" to the FIFO */
fd = open(myfifo, O_WRONLY);
write(fd, "Hi", sizeof("Hi"));
close(fd);
/* remove the FIFO */
unlink(myfifo);
}
else
{
wait(NULL);
char buf[MAX_BUF];
/* open, read, and display the message from the FIFO */
fd = open(myfifo, O_RDONLY);
read(fd, buf, MAX_BUF);
printf("Received: %s\n", buf);
close(fd);
return 0;
}
return 0;
}

Unless you pass the O_NONBLOCK flag to open(2), opening a FIFO blocks until the other end is opened. From man 7 fifo:
The FIFO must be opened on both ends (reading and writing) before data
can be passed. Normally, opening the FIFO blocks until the other end
is opened also.
A process can open a FIFO in nonblocking mode. In this case, opening
for read only will succeed even if no-one has opened on the write side
yet, opening for write only will fail with ENXIO (no such device or
address) unless the other end has already been opened.
Which is to say, your parent / child processes are implicitly synchronized upon opening the FIFO. So by the time the parent process calls unlink(2), the child process opened the FIFO long ago. So the child will always find the FIFO object and open it before the parent calls unlink(2) on it.
A note about unlink(2): unlink(2) simply deletes the filename from the filesystem; as long as there is at least one process with the file (FIFO in this case) open, the underlying object will persist. Only after that process terminates or closes the file descriptor will the operating system free the associated resources. FWIW, this is irrelevant in the scope of this question, but it seems worth noting.
A couple of other (unrelated) remarks:
Don't call wait(2) on the child. It will return an error (which you promptly ignore), because the child didn't fork any process.
mkfifo(3), fork(2), open(2), read(2), write(2), close(2) and unlink(2) can all fail and return -1. You should gracefully handle possible errors instead of ignoring them. A common strategy for these toy programs is to print a descriptive error message with perror(3) and terminate.
If you just want parent to child communication, use a pipe: it's easier to setup, you don't need to unlink it, and it is not exposed in the filesystem (but you need to create it with pipe(2) before forking, so that the child can access it).

Related

Using multiple pipes in c

I wan't to write a program that uses 2 pipes to pass values between parent and child process.Code:
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
#include <signal.h>
#include <sys/stat.h>
#include <fcntl.h>
int mkfifo(const char *path, mode_t mode);
int main(int argc, char **argv)
{
int fd,df,val;
char * myfifo = "/tmp/myfifo";
mkfifo(myfifo, 0666);
char * myfifo2 = "/tmp/myfifo2";
mkfifo(myfifo2, 0666);
pid_t c=fork();
if(c==0){
df = open(myfifo2, O_RDONLY);
read(df, &val, sizeof(val));
close(df);
val= val+10;
df = open(myfifo2, O_WRONLY);
write(df, &val, sizeof(val));
fd = open(myfifo, O_WRONLY); //if i put this write first print works and program finishes
write(fd, &val, sizeof(val));
}
if(c>0)
{
val=1;
df = open(myfifo2, O_WRONLY);
write(df, &val, sizeof(val));
fd = open(myfifo, O_RDONLY);
read(fd, &val, sizeof(val));
printf("val is %d \n",val);
}
}
The first read of the child waits for parent to write val inside the fifo file.After that it writes a new val into this fifo file and afterwards writes a value into the second text file.The read in parent's code waits for this write to happen.So,all in all what i want is the parent process to execute the print only after the child process has written val in myfifo2 file.That is the purpose of the second named pipe i have used.However,this code gets stuck and prints nothing.I noticed that if i change the turn of the two write in child process it works but it does not satisfy the above thing i need.Why is this happening and how do i solve this?
What's happening
While named pipes can be used with buffering (as you intend them to), relying on it to not block your program (because that's what you have, a deadlock) is a really bad practice.
Moreover, it comes with some caveats:
In order to have that "buffering" effect, the both ends of the pipe need to be opened (man 7 fifo)
The FIFO must be opened on both ends (reading and writing) before data can be passed. Normally, opening the FIFO blocks until the other end is opened also.
Writes will eventually block once the pipe reaches it capacity. This size depends on the system but can be set via F_SETPIPE_SZ fcntl. But also:
Note that because of the way the pages of the pipe buffer are
employed when data is written to the pipe, the number of bytes
that can be written may be less than the nominal size, dependā€
ing on the size of the writes.
So, you have this situation:
Parent (probably, could be even not yet there) waits in read on /tmp/myfifo.
/tmp/myfifo2 is opened WR_ONLY by parent.
child calls df = open(myfifo2, O_WRONLY);, opening /tmp/myfifo2 also as WR_ONLY. Thus the read end of the pipe is not opened, therefore the call to open blocks.
Deadlock.
To solve this, opening myfifo2 in the child as read-write should be sufficient.
df = open(myfifo2, O_RDWR); // instead of df = open(myfifo2, O_WRONLY);
IMO this communication pattern looks a bit strange (and hard to understand), though.

dup2 paramater order confusion

I have written this simple program:
#include<stdio.h>
#include<unistd.h>
#include <fcntl.h>
#include <stdlib.h>
int main(){
int fd = open("theFile.txt", O_CREAT | O_RDWR, 0666);
if(fd<0){
printf("coudlnt open File descriptor \n");
}
pid_t pid = fork();
if(pid==0){
dup2(int oldFD, int newFD);
dup2(fd,1);
execlp("/bin/ls","ls","-l", NULL);
}
return 0;
}
What i want is, redirect the output of ls - l to a file called "theFile.txt". The code works as i expect. What confuses me here is the order of dup2 parameters. I believe that correct order should be dup2(1, fd) - considering fd as the newFD and 1 as the oldFD. But the code works when i use it as dup2(fd,1) which basically is stdout to fd according to some other answers on SO.
How is the oldFD fd here and how is the newFD 1 here? If 1 is newFD, why does this program work in the first place?
Also, execlp overwrites child's address space after I call dup2. How is dup2 connected to execlp such that the desired result is obtained. i.e. what i do cat theFile.txt i get the current directly listed.
Can i get some explanation here please?
According to [man7]: DUP(2):
int dup2(int oldfd, int newfd);
...
The dup() system call creates a copy of the file descriptor oldfd,
using the lowest-numbered unused file descriptor for the new
descriptor.
...
The dup2() system call performs the same task as dup(), but instead
of using the lowest-numbered unused file descriptor, it uses the file
descriptor number specified in newfd. If the file descriptor newfd
was previously open, it is silently closed before being reused.
When outputing data (e.g. text) to console, applications use the stdout stream (also stderr, but for simplicity's sake, let's leave that out). stdout's fileno is 1 (it's better to use constants instead of values, as values might change - not very likely in this case, but in general):
cfati#cfati-ubtu16x64-0:~/Work/Dev/StackOverflow/q048923791$ cat /usr/include/unistd.h | grep STDOUT
#define STDOUT_FILENO 1 /* Standard output. */
In your child process, ls (via execlp) spits its data to stdout (fileno 1). Before that, the dup2 call is made. The current situation, before the dup2 call (for clarity, I'm going to use the defined macro when referring to stdout's fileno):
fd: points to the custom file (opened previously)
STDOUT_FILENO: points to stdout
The dup2 call:
dup2(fd, STDOUT_FILENO) (as it is now): closes current STDOUT_FILENO and duplicates fd to STDOUT_FILENO. Current situation:
fd: points to the custom file
STDOUT_FILENO: points to the custom file
dup2(STDOUT_FILENO, fd): closes current fd and duplicates STDOUT_FILENO to fd. Current situation:
fd: points to stdout
STDOUT_FILENO: points to stdout
As seen, for #1., when data will be output to stdout, it will actually go to the custom file (as opposed to #2. where it would go to stdout, even when using fd).
Regarding the 2nd question:
[man7]: EXEC(3):
The exec() family of functions replaces the current process image
with a new process image. The functions described in this manual
page are front-ends for execve(2).
[man7]: EXECVE(2):
By default, file descriptors remain open across an execve().
...
POSIX.1 says that if file descriptors 0, 1, and 2 would
otherwise be closed after a successful execve(), and the process
would gain privilege because the set-user-ID or set-group_ID mode
bit was set on the executed file, then the system may open an
unspecified file for each of these file descriptors. As a general
principle, no portable program, whether privileged or not, can
assume that these three file descriptors will remain closed across
an execve().
The file descriptors will be passed from the child process to ls.
Here's an improved version (minor changes only) of your code (code00.c):
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
int main() {
int ret = 0, fd = open("thefile.txt", O_CREAT | O_RDWR, 0666);
if (fd < 0) {
printf("Coudln't open file: %d\n", errno);
ret = 1;
}
pid_t pid = fork();
if (pid == 0) {
// dup2(int oldFD, int newFD);
if (dup2(fd, STDOUT_FILENO) < 0) {
printf("Couldn't redirect stdout: %d\n", errno);
ret = 2;
}
execlp("/bin/ls", "ls", "-l", NULL);
} else if (pid < 0) {
printf("Couldn't spawn child process: %d\n", errno);
ret = 3;
}
return ret;
}

Unix : What happens to a pipe(half-duplex) if the process dies

I am trying to prove one of my doubts, that two non-related processes can share the fd of half-duplex pipe and have communication.
I have created two programs for that. But then I had this another question, that what happens to the pipe if process dies?Because my reader got some garbage message, when I printed out the message.
Writer
#include <stdio.h>
#include <unistd.h>
#include <string.h>
int main()
{
int fd[2];
char str[] = "hello\n";
if(pipe(fd) < 0)
perror("Pipe creation failed\n");
//Since i am a writer, i should close the reading end as a best practice
close(fd[0]);
/*
The processes need not to be related processes, in order to use the half duplex pipes. fd is just a number/identifier
which can be shared across different processes
*/
printf("Hey there !!! use this file descriptor for reading : %d\n", fd[0]);
//writing message
write(fd[1],str,strlen(str)+1);
return 0;
}
Reader
#include <stdio.h>
#include <unistd.h>
int main()
{
int fd,bytesRead;
char buffer[1024];
printf("please enter the fd :");
scanf("%d",&fd);
bytesRead = read(fd,buffer,1024);
printf("Bytes Read : %d\nMessage : %s\n", bytesRead, buffer);
return 0;
}
You can't do this.
The table of file descriptors is per-process; every process has its own separate set of open file descriptors (note the distinction between open file descriptors, which are per-process, and open file descriptions, which are system-wide, discussed in open(2)). If you want to share a file descriptor between processes, you need to either inherit it over a fork(2) or pass it through a unix(7) domain socket via sendmesg(2) with SCM_RIGHTS in the cmesg(3) header.
(On Linux, you can also pass around paths to /proc/[PID]/fd/..., and other systems may have their own non-portable equivalents).
What is happening in your case is that the read is failing (you're giving it a file descriptor which is not open), leaving your buffer with uninitialized garbage. Since you don't check the return value, you never know that it failed.
In pipe man page,
pipe() creates a pipe, a unidirectional data channel that can be used for interprocess communication. The array pipefd is used to return two file descriptors referring to the ends of the pipe. pipefd[0] refers to the read end of the pipe. pipefd[1] refers to the write end of the pipe. Data written to the write end of the pipe is buffered by the kernel until it is read from the read end of the pipe.
The pipe is mainly used for the related process(parent and child). You are not able to use the pipe for non related process.
In related process, one end is closed. In other end process gets the SIGPIPE signal.
Example program using pipe :-
#include <sys/wait.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
int
main(int argc, char *argv[])
{
int pipefd[2];
pid_t cpid;
char buf;
if (argc != 2) {
fprintf(stderr, "Usage: %s <string>\n", argv[0]);
exit(EXIT_FAILURE);
}
if (pipe(pipefd) == -1) {
perror("pipe");
exit(EXIT_FAILURE);
}
cpid = fork();
if (cpid == -1) {
perror("fork");
exit(EXIT_FAILURE);
}
if (cpid == 0) { /* Child reads from pipe */
close(pipefd[1]); /* Close unused write end */
while (read(pipefd[0], &buf, 1) > 0)
write(STDOUT_FILENO, &buf, 1);
write(STDOUT_FILENO, "\n", 1);
close(pipefd[0]);
_exit(EXIT_SUCCESS);
} else { /* Parent writes argv[1] to pipe */
close(pipefd[0]); /* Close unused read end */
write(pipefd[1], argv[1], strlen(argv[1]));
close(pipefd[1]); /* Reader will see EOF */
wait(NULL); /* Wait for child */
exit(EXIT_SUCCESS);
}
}
The above program creates a pipe, and then fork(2)s to create a child process; the child inherits a duplicate set of file descriptors that refer to the same pipe. After the fork(2), each process closes the descriptors that it doesn't need for the pipe (see pipe(7)). The parent then writes the string contained in the program's command-line argument to the pipe, and the child reads this string a byte at a time from the pipe and echoes it on standard output.

I Have a problem with 2 FIFO for read and write in each

The attached code should allow the communication between 2 terminals. The communication is made through 2 FIFO, which are created in the current directory. The program has to open the 2 fifos and the son reads from STDIN and puts on the fifo1 and the father reads from the other fifo and prints on terminal. In this way the communication takes place since the call to the program is: ./myprog fifo1 fifo2 (for the first terminal) and ./myprog fifo2 fifo1 (for the second terminal). The code does not work well, I suspect that the child write() performs on the fifo does not work well. Hoping that I explained well, help meeee :'(
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <poll.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <limits.h>
int main(int argc,char* argv[])
{
if(argc<3)
{
printf("Error: Too few arguments...\n");
exit(-1);
}
char** buffer_in=(char**) malloc(sizeof(char*));
char** buffer_out=(char**) malloc(sizeof(char*));
size_t dim_buff=sizeof(char*);
FILE* stream;
FILE* input;
int fifo_in, fifo_out, num_poll_c, num_poll_f, read_count, i,write_b;
pid_t pid;
ssize_t length;
struct pollfd* fd_set_c=(struct pollfd*) malloc(sizeof(int));//for the child
struct pollfd* fd_set_f=(struct pollfd*) malloc(sizeof(int));//for the father
printf("Write character e press enter:\n");
if((fifo_in=open(argv[1],O_RDWR|O_NONBLOCK))==-1)
perror("error open");
if((fifo_out=open(argv[2],O_RDWR|O_NONBLOCK))==-1)
perror("error open");
if((input=fdopen(STDIN_FILENO,"r"))==NULL)
perror("error fdopen");
if((pid=fork())==-1)
perror("error fork");
while(1)
{
if(pid==0) /*child*/
{
fd_set_c->fd=STDIN_FILENO;
fd_set_c->events=POLLIN;
if((num_poll_c=poll(fd_set_c, 1, -1))==-1)
perror("error poll child");//poll on fifo_in
if((length=getline(buffer_in,&dim_buff,input))==-1)
perror("error getline");
printf("The written word is::%s\n",*buffer_in);/*my control for see what in buffer_in is*/
if((write_b=write(fifo_in,*buffer_in,dim_buff))==-1)
perror("error write");
}
else /*father*/
{
fd_set_f->fd=fifo_out;
fd_set_c->events=POLLIN;
if((num_poll_f=poll(fd_set_f, 1, 5000))==-1)
perror("error poll father");//poll on fifo_out
if((read_count=read(fifo_out,*buffer_out,SSIZE_MAX))==-1)
perror("error read");//read on fifo_out
for(i=0;i<=read_count;i++)
printf("%s",buffer_out[i]);//print on stdout buffer_out
}
}
return 0;
}
You should use pipes(man 2 pipe. or shared memory: man shmget) for communication between your processes and a semaphore to protect reads/writes. Look for "producer/consumer" on google.
take a look at this: http://users.evtek.fi/~tk/rtp/sem-producer-consumer.c
and this: http://knol.google.com/k/producer-consumer-problem#
You have a mess here.
Parent might block in read() from FIFO because there are still open writable file descriptors for it. And the open writable file descriptor belongs to the parent process itself. And since it is blocked in the read() it can't write into it.
Put opening of FIFOs after fork(). Open in a stricter mode: either only for writing or only for reading, not wildcard O_RDWR. That way you would mirror the logic generally applied to pipe()s.
Additionally, poll()ing on STDIN_FILENO is dangerous, as stdio is buffered. File descriptor might have nothing to read - because it was all already read out before, but is inside the stdio buffer. If you really sure you know what you do, at least try to disable buffering, man setvbuf. But I still wouldn't do it in the presence of fork(): buffered data might be read twice: once by child and once by parent.

Using named pipes in a single process

I am trying to use a named pipe for communication within a process.
Here is the code
#include <stdio.h>
#include <fcntl.h>
#include <signal.h>
void sigint(int num)
{
int fd = open("np", O_WRONLY);
write(fd, "y", 1);
close(fd);
}
main()
{
char ch[1];
int fd;
mkfifo("np", 0666);
signal(SIGINT, sigint);
fd = open("np", O_RDONLY);
read(fd, ch, 1);
close(fd);
printf("%c\n", ch[0]);
return;
}
What I want is for main to block till something is written to the pipe.
The problem is that the signal handler sigint() also blocks after opening the pipe. Is this supposed to happen given that the pipe is already opened for reading earlier in main() ?
You're blocking in open() , opening a fifo for reading blocks until someone opens it for writing.
And opening a fifo for writing blocks until someone opens it for reading.
the signal handler runs in the same thread as your main(), so you'll get a deadlock. Neither will be able to open the fifo.
You can check what's going on by running your program under strace.
From the man page:
Opening a FIFO for reading normally
blocks until some other process opens
the same FIFO for writing, and vice
versa.
and:
A process can open a FIFO in
non-blocking mode. In this case,
opening for read only will succeed
even if no-one has opened on the write
side yet; opening for write only will
fail with ENXIO (no such device or
address) unless the other end has
already been opened.
Under Linux, opening a FIFO for read
and write will succeed both in
blocking and non-blocking mode. POSIX
leaves this behaviour undefined. This
can be used to open a FIFO for writing
while there are no readers available.
A process that uses both ends of the
connection in order to communicate
with itself should be very careful to
avoid deadlocks.

Resources