Cannot Write in Named Pipe - c

In this code my program crashes in when I am opening the pipe for writing.
char pipe[30];
int fd, tmp = 2;
sprintf(pipe, "root_%d", getpid());
ret_val = mkfifo(pipe, 0666);
fd = open(pipe, O_WRONLY); //HERE IS CRASHING - SUDDENLY FREEZES
write(fd, &tmp, sizeof(int));
close(fd)
All seems good, but where is my mistake;

It is an expected behavior. From man 7 fifo:
Normally, opening the FIFO blocks until the other end is opened also.
So your open does not return until somebody opens the same pipe for reading. You may want to add O_NONBLOCK flag (and likely get SIGPIPE on writing), or revisit the design.

Related

Linux named fifo non-blocking read select returns bogus read_fds

Similar to the problem asked a while ago on kernel 3.x, but I'm seeing it on 4.9.37.
The named fifo is created with mkfifo -m 0666. On the read side it is opened with
int fd = open(FIFO_NAME, O_RDONLY | O_NONBLOCK);
The resulting fd is passed into a call to select(). Everything works ok, till I run echo >> <fifo-name>.
Now the fd appears in the read_fds after the select() returns. A read() on the fd will return one byte of data. So far so good.
The next time when select() is called and it returns, the fd still appears in the read_fds, but read() will always return zero meaning with no data. Effectively the read side would consume 100% of the processor capacity. This is exactly the same problem as observed by the referenced question.
Has anybody seen the same issue? And how can it be resolved or worked-around properly?
I've figured out if I close the read end of the fifo, and re-open it again, it will work properly. This probably is ok because we are not sending a lot of data. Though this is not a nice or general work-around.
This is expected behaviour, because the end-of-input case causes a read() to not block; it returns 0 immediately.
If you look at man 2 select, it says clearly that a descriptor in readfds is set if a read() on that descriptor would not block (at the time of the select() call).
If you used poll(), it too would immediately return with POLLHUP in revents.
As OP notes, the correct workaround is to reopen the FIFO.
Because the Linux kernel maintains exactly one internal pipe object to represent each open FIFO (see man 7 fifo and man 7 pipe), the robust approach in Linux is to open another descriptor to the FIFO whenever an end of input is encountered (read() returning 0), and close the original. During the time when both descriptors are open, they refer to the same kernel pipe object, so there is no race window or risk of data loss.
In pseudo-C:
fifoflags = O_RDONLY | O_NONBLOCK;
fifofd = open(fifoname, fifoflags);
if (fifofd == -1) {
/* Error checking */
}
/* ... */
/* select() readfds contains fifofd, or
poll() returns POLLIN for fifofd: */
n = read(fifofd, buffer, sizeof buffer)
if (!n) {
int tempfd;
tempfd = open(fifopath, fifoflags);
if (tempfd == -1) {
const int cause = errno;
close(fifofd);
/* Error handling */
}
close(fifofd);
fifofd = tempfd;
/* A writer has closed the FIFO. */
} else
/* Handling for the other read() result cases */
The file descriptor allocation policy in Linux is such that tempfd will be the lowest-numbered free descriptor.
On my system (Core i5-7200U laptop), reopening a FIFO in this way takes less than 1.5 µs. That is, it can be done about 680,000 times a second. I do not think this reopening is a bottleneck for any sensible scenario, even on low-powered embedded Linux machines.

Writing string to pipe

I am trying to send a string to a pipe in unix. When I go through a line-by-line debugging process, the call mkfifo() creates the file in the same directory as the source code. However, when I reach the open() call, the debugger is no longer able to proceed. I'm not sure why it is unable to access the pipe file.
Here's the code in question:
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
int main()
{
int fd;
char * myfifo = "myfifo";
/* create the FIFO (named pipe) */
mkfifo(myfifo, 0666);
/* write "Hi" to the FIFO */
fd = open(myfifo, O_WRONLY);
write(fd, "Hi", sizeof("Hi"));
close(fd);
/* remove the FIFO */
unlink(myfifo);
return 0;
}
Any suggestions are appreciated. Thank you.
Normally a FIFO has to be open at both ends simultaneously before either side can proceed. Since you didn't mention anything about a reader, the most likely answer is that you haven't got one, or you haven't set it up yet. Once you do, the open will be allowed to proceed.
mkfifo(3) routes to fifo(7) which reads:
The kernel maintains exactly one pipe object for each FIFO special file that is opened by at least one process. 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.
There is a solution for non-blocking read:
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.
So you could fork another process for reading:
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
long strlen(char * c){
return c[0] == 0 ? 0 : 1 + strlen(++c);
}
int main()
{
int fd;
int fr;
char buf[3];
char * MESSAGE = "Hi\n";
char * myfifo = "myfifo";
/* create the FIFO (named pipe) */
mkfifo(myfifo, 0666);
int msglen = strlen(MESSAGE);
int child = fork();
if (child == 0){
/* read "Hi" from the FIFO (CHILD)*/
fr = open(myfifo, O_RDONLY);
read(fr, buf, msglen);
write(1, buf, msglen);
close(fr);
} else {
/* write "Hi" to the FIFO (PARENT)*/
fd = open(myfifo, O_WRONLY);
write(fd, MESSAGE, sizeof(char) * msglen);
close(fd);
/* remove the FIFO */
wait(child);
unlink(myfifo);
}
return 0;
}
I guess you have to open both ends before you write.
here is an excerpt from the man page for mkfifo() See my notes at the end
mkfifo() makes a FIFO special file with name pathname. mode
specifies the FIFO's permissions. It is modified by the process's
umask in the usual way: the permissions of the created file are (mode
& ~umask).
A FIFO special file is similar to a pipe, except that it is created
in a different way. Instead of being an anonymous communications
channel, a FIFO special file is entered into the filesystem by
calling mkfifo().
Once you have created a FIFO special file in this way, any process
can open it for reading or writing, in the same way as an ordinary
file. However, it has to be open at both ends simultaneously before
you can proceed to do any input or output operations on it. Opening
a FIFO for reading normally blocks until some other process opens the
same FIFO for writing, and vice versa. See fifo(7) for nonblocking
handling of FIFO special files.
Notes: There are two details of importance:
1) the 'mode' parameter is modified by the value of 'umask'
2) both ends of the fifo must be open at the same time
before any I/O operations can be performed.

Explanation on dup syscall

close(fileno(stdout));
int fd = dup(fileno(stdin));
//printf("Hello World\n");
write(fd, "Hello", 7);
Here for both printf and write is writing Hello to the screen..But I think they should not because I am duplicating the stdin to 1 or stdout closed perviously. And printf is connected to stdout but not stdin but still they are printing...Please explain if I have anything wrong in understanding the dup
The printf and write in your code happen to work, due to historical custom. Here's what's happening:
When a user logs into a terminal on a Unix-like system, or opens a terminal window under X11, file descriptors 0, 1, and 2 are connected to a terminal device, and each of them is opened for both reading and writing. This is the case despite the fact that one normally only reads from fd 0 and writes to fd 1 and 2.
Yet here is the code from 7th edition init.c:
open(tty, 2);
dup(0);
dup(0);
...
execl(getty, minus, tty, (char *)0);
And here is how ssh does it:
ioctl(*ttyfd, TCSETCTTY, NULL);
fd = open("/dev/tty", O_RDWR);
if (fd < 0)
error("%.100s: %.100s", tty, strerror(errno));
close(*ttyfd);
*ttyfd = fd;
...
/* Redirect stdin/stdout/stderr from the pseudo tty. */
if (dup2(ttyfd, 0) < 0)
error("dup2 stdin: %s", strerror(errno));
if (dup2(ttyfd, 1) < 0)
error("dup2 stdout: %s", strerror(errno));
if (dup2(ttyfd, 2) < 0)
error("dup2 stderr: %s", strerror(errno));
(The dup2 function dups arg1 into arg2, closing arg2 first if necessary.)
And here is how xterm does it:
if ((ttyfd = open(ttydev, O_RDWR)) >= 0) {
/* make /dev/tty work */
ioctl(ttyfd, TCSETCTTY, 0);
...
/* this is the time to go and set up stdin, out, and err
*/
{
/* dup the tty */
for (i = 0; i <= 2; i++)
if (i != ttyfd) {
IGNORE_RC(close(i));
IGNORE_RC(dup(ttyfd));
}
/* and close the tty */
if (ttyfd > 2)
close_fd(ttyfd);
Back to your code.
close(fileno(stdout));
This closes fd 1.
int fd = dup(fileno(stdin));
This duplicates fd 0 into the lowest available fd, which is 1, and assigns 1 to fd. (This assumes that fd 0 is open, of course.) Both fd's 0 and 1 are now open for reading and writing (assuming they're connected to a terminal device). On a Linux system you can verify this:
$ cat /proc/self/fdinfo/0
pos: 0
flags: 0100002
mnt_id: 20
$ cat /proc/self/fdinfo/1
pos: 0
flags: 0100002
mnt_id: 20
The 2 in flags, which is the constant O_RDWR, means open for reading and writing.
printf("Hello World\n");
This writes to fd 1, which is, once again, open for reading and writing to your terminal.
write(fd, "Hello", 7);
This writes to fd 1 again. (Note that "Hello" is only 6 bytes, though.)
If you are running your program on a terminal, then stdin and stdout are opens of the same file - your terminal device. So it should not be surprising that writing to stdin - or a dup() of fileno(stdin) - writes to your terminal device, and you see it.
If you run your program with stdin connected somewhere else, like a disk file, you'll see a different result:
./program < file

Read and Write operations get bad fd and no such file

I have this C program.
I have two processes, father and son, and use semaphores to make them synchronize one at time.
The father has to write (n) numbers, ten in this case, always in the first byte of the opened file and the son has to read it.
The problem is that when I print the results, I get bad file descriptor for the write (father) and no such file for the read(the son).
Can you help me, please?? Thank you
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <pthread.h>
#include <semaphore.h>
#define FILENAME "test.txt"
#define MUTEX "/mutex"
#define READ "/read"
#define WRITE "/write"
int main(int argc, char *argv[]){
int i, pid, n=10, fd, x;
int nread, nwrite;
char c = 'a';
sem_t *mutex, *reader, *writer;
//fd = open(FILENAME, O_CREAT | O_TRUNC, 0666);
mutex = sem_open(MUTEX, O_CREAT, 0666, 1);
reader = sem_open(READ, O_CREAT, 0666, 0);
writer = sem_open(WRITE, O_CREAT, 0666, 1);
pid = fork();
fd = open(FILENAME, O_CREAT | O_TRUNC, 0777);
if(fd < 0){
perror("Open FILE error");
exit(-1);}
if(pid == 0){ // son
do{
sem_wait(reader); // si può leggere???
sem_wait(mutex);
lseek(fd, 0, SEEK_SET);
nread = read(fd, &x, sizeof(int));
if(nread <=0)
perror("Read error");
printf("Son has read (%d byte) = %d\n", nread, x);
fflush(NULL);
sem_post(mutex);
sem_post(writer);
}
while(x != (n-1));
exit(0);
}
else{
for(i=0; i<n; i++){
sem_wait(writer); // can I write??
sem_wait(mutex);
lseek(fd, 0, SEEK_SET);
nwrite = write(fd, &c, sizeof(char));
if(nwrite <= 0)
perror("nwrite error");
printf("Father has written (%d byte) %d\n", nwrite, i);
fflush(NULL);
sem_post(mutex);
sem_post(reader); // it's possible to read
}
//wait(NULL);
}
sem_unlink(MUTEX);
sem_unlink(READ);
sem_unlink(WRITE);
//remove(FILENAME);
exit(0);
}
First, you opened the file without specifying an o_flag. That's actually undefined behavior ("Applications shall specify exactly one of .... O_RDONLY .... O_WRONLY .... O_RDWR"), but for practical purposes means the file was opened read only.
Thus the parent's write operation fails with EBADF. Can't write to a read only file!
Second, the child's error checking is incorrect. read() may return zero on success, in which case errno, consulted by perror(), is not guaranteed to be meaningful. You mean to check for a return value of less than zero, not of less than or equal to zero.
Your open() call is opening the file for read only. You have:
fd = open(FILENAME, O_CREAT | O_TRUNC, 0777);
Because you don't explicitly say O_WRONLY or O_RDWR, and because the traditional value for O_RDONLY is 0, you are effectively opening the file read-only.
The 0777 permissions are suspect too. You are not creating an executable; you should not be giving the file executable permissions. In my book, you probably shouldn't be giving others write permission on the file. In fact, I'd probably go with 0600 permissions.
Your program is a bit strange.
First off, you are asking for trouble by having parent and child processes race to be the one to create the target file. It would be better for the parent to create and open the file before forking, and for (only) the child to open it after. Be aware that in that case the child should first close (its copy of) the file descriptor opened by the parent. Alternatively, the way you're doing things, if the parent opened the file then it would probably be sufficient for the child to just use the file descriptor it inherits, without opening the file itself at all.
With that said, it would be more usual for parent and child processes to communicate via a pipe than via a physical file. That has the particular advantage that you do not need to synchronize access via a semaphore; ordinary blocking I/O does the job.
Additionally, I don't see what your mutex semaphore is doing for you. Even with a design that requires you to manually synchronize writing and reading, it looks like your reader and writer semaphores will serve that purpose without help.
Importantly, your parent process is writing in sizeof(char)-byte uints, whereas your child process is trying to read in sizeof(int)-byte units. This is unlikely to have the result you want.
Furthermore, the read() and write() functions may return successfully without having transferred the full number of bytes requested (unless that number is 1 and the file is open in blocking mode). You need to account for that by being prepared to use multiple I/O operations to transfer multi-byte data, if necessary.
Finally, it would be best for just one process to unlink your semaphores. It is ok for that to happen while the other process is still running.

Use non-blocking FIFO as a ring-buffer?

I have created a FIFO where I can do non-blocking writes into in this way:
// others, searching for a non-blocking FIFO-writer may copy this ;-)
mkfifo("/tmp/myfifo", S_IRWXU);
int fifo_fd = open("/tmp/myfifo", O_RDWR);
fcntl(fifo_fd, F_SETFL, fcntl(fifo_fd, F_GETFL) | O_NONBLOCK);
// and then in a loop:
LOGI("Writing into fifo.");
if (write(fifo_fd, data, count) < 0) {
LOGE("Failed to write into fifo: %s", strerror(errno));
}
The non-blocking write works perfect, but due to my logging I can see:
(..)
Writing into fifo.
Writing into fifo.
Writing into fifo.
Failed to write into fifo: Try again
Writing into fifo.
Failed to write into fifo: Try again
Writing into fifo.
Failed to write into fifo: Try again
After I created a reader like cat /tmp/myfifo > foo.out, the error goes away.
I thought the FIFO works like a ring-buffer and drops the first-written bytes when the buffer is full. But now I have learned that it blocks/prevents new bytes until the first bytes are read.
Does anyone know a simple other way or additional operation I can make so that the FIFO behaves like a ring-buffer?

Resources