Receiving data from multiple pipes [duplicate] - c

Can one do non-blocking I/O on a pipe? fcntl fails to set O_NONBLOCK. Page 918 of The Linux Programming Interface includes a table 'Semantics of reading n bytes from pipe or FIFO (p)'. This table lists the behaviour of pipes and FIFO's with one column titled O_NONBLOCK enabled? This would imply that you can set the O_NONBLOCK flag on a pipe. Is this correct? The following code fails to set the flag, fcntl(2) does not report an error though.
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <fcntl.h>
int main()
{
int fds[2];
pid_t pid;
char wr_buf[100];
char rd_buf[100];
pipe(fds);
pid = fork();
if ( pid )
{
while (1 )
{
memcpy( wr_buf, "abcdefghi\0",10);
write( fds[1], wr_buf, 10);
sleep(2);
}
}
else
{
int retval = fcntl( fds[0], F_SETFL, fcntl(fds[0], F_GETFL) | O_NONBLOCK);
printf("Ret from fcntl: %d\n", retval);
while (1)
{
ssize_t r=read( fds[0], rd_buf, 10 );
printf("read: %d\n", r);
if ( r > 0 )
{
printf("Buffer: %s\n", rd_buf);
}
else
{
printf("Read nothing\n");
perror("Error was");
sleep(1);
}
}
}
}

There is nothing special to pipe and O_NONBLOCK. The following example work as expected. I did not check every retval from every call to make the example a bit more readable. A real world application must do the checks.
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <fcntl.h>
int main()
{
int fds[2];
pid_t pid;
char buf[100];
pipe(fds);
pid = fork();
if ( pid )
{
while (1 )
{
memcpy( buf, "abcdefghi\0",10);
write( fds[1], buf, 10);
sleep(2);
}
}
else
{
int retval = fcntl( fds[0], F_SETFL, fcntl(fds[0], F_GETFL) | O_NONBLOCK);
printf("Ret from fcntl: %d\n", retval);
while (1)
{
ssize_t r=read( fds[0], buf, 10 );
printf("read: %d\n", r);
if ( r > 0 )
{
printf("Buffer: %s\n", buf);
}
else
{
printf("Read nothing\n");
perror("Error was");
sleep(1);
}
}
}
}
After writing my example I inspect your code and found:
flags = fcntl(pfd[0], F_GETFD);
flags |= O_NONBLOCK;
if (fcntl(pfd[0], F_SETFD, flags))
Please change F_SETFD to F_SETFL and also for the get operation. You would not change the file descriptor flags but the file status flags :-)
From man 3 fcntl:
File descriptor flags
The following commands manipulate the flags associated with a file
descriptor. Currently, only one such flag is defined: FD_CLOEXEC, the
close-on-exec flag. If the FD_CLOEXEC bit is 0, the file descriptor
will remain open across an execve(2), otherwise it will be closed.
File status flags
Each open file description has certain associated status flags, ini‐
tialized by open(2) and possibly modified by fcntl(). Duplicated file
descriptors (made with dup(2), fcntl(F_DUPFD), fork(2), etc.) refer to
the same open file description, and thus share the same file status
flags.
F_SETFL (int)
Set the file status flags to the value specified by arg. File
access mode (O_RDONLY, O_WRONLY, O_RDWR) and file creation flags
(i.e., O_CREAT, O_EXCL, O_NOCTTY, O_TRUNC) in arg are ignored.
On Linux this command can change only the O_APPEND, O_ASYNC,
O_DIRECT, O_NOATIME, and O_NONBLOCK flags. It is not possible
to change the O_DSYNC and O_SYNC flags; see BUGS, below.

Related

Difference between O_CLOEXEC and TIOCEXCL

I use a device on serial port /dev/ttyUSB0 (uses FTDI) and I don't want to leak any file descriptors to other spawned processes so I set the close-on-exec flag on the descriptor. Could you tell me what is the difference between setting O_CLOEXEC while opening:
#include <unistd.h>
#include <fcntl.h>
#include <stdlib.h>
#include <stdio.h>
int main()
{
int fd, rc;
fd = open("/dev/ttyUSB1", O_RDWR | O_NOCTTY | O_CLOEXEC);
if(fd < 0)
{
perror("error open:");
exit(-1);
}
rc = close(fd);
if(rc != 0)
{
perror("error close:");
exit(-1);
}
return 0;
}
And setting close-on-exec with ioctl(fd, TIOCEXCL):
#include <unistd.h>
#include <fcntl.h>
#include <termios.h>
#include <sys/ioctl.h>
#include <stdlib.h>
#include <stdio.h>
int main()
{
int fd, rc;
fd = open("/dev/ttyUSB1", O_RDWR | O_NOCTTY);
if(fd < 0)
{
perror("error open:");
exit(-1);
}
rc = ioctl(fd, TIOCEXCL);
if(rc != 0)
{
perror("error ioctl:");
exit(-1);
}
rc = close(fd);
if(rc != 0)
{
perror("error close:");
exit(-1);
}
return 0;
}
The TIOCEXCL does not set the close-on-exec flag (that would be FIOCLEX, or, equivalently, fcntl(fd, F_SETFD, FD_CLOEXEC)).
To answer the question you thought you were asking:
Specifying O_CLOEXEC when you open() a file will set the close-on-exec flag before it returns, saving you another call and, importantly, ensuring that there is no race condition where another thread might call exec() after open() but before the subsequent fcntl().
If you really need to set or unset the flag at any other time (I've never needed to), you can do so with fcntl F_SETFD, passing FD_CLOEXEC or 0 respectively.

Why are processes blocked when open FIFO

I write a test for FIFO. Server writes string "hello" to the client through FIFO. But it seems that the two processes are blocked.I think the FIFO are opened for writing and reading by server and client. But the two processes output nothing.
/* FIFO test */
#include <stdio.h>
#include <sys/types.h>
#include <sys.stat.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#define FIFOPATH "/home/hel/fifo" // define file path
int client(void);
int server(void);
int main(void)
{
pid_t pid;
/* create FIFO */
if (mkfifo(FIFOPATH, S_IRUSR | S_IWUSR) < 0) {
if (errno == EEXIST) { // already exists, ok
}
/* error */
else {
exit(-1);
}
}
/* create process */
pid = fork();
if (pid < 0) { // error, process exits.
exit(-1);
} else if (pid == 0) { // child, server
server();
return 0; // exit
}
/* parent, client */
client();
return 0;
}
/* server */
int server(void)
{
int ret;
int fd;
/* open fifo for writing */
if ((fd = open(FIFOPATH, 0200)) < 0) {
printf("%s\n", strerror(errno));
return -1; // error
}
ret = write(fd, "hello", 5);
close(fd);
return 0;
}
/* client */
int client(void)
{
char recvBuf[100];
int fd;
int ret;
/* open fifo for reading */
if ((fd = open(FIFOPATH, 0400)) < 0) {
printf("%s\n", strerror(errno));
return -1; // error
}
ret = read(fd, recvBuf, 5);
printf("ret: %d %d\n", ret, fd);
printf("client receive %s\n", recvBuf);
close(fd);
return 0;
}
Your code has two problems. The first one is the main problem.
The flags parameters passed to open are incorrect. They are not supposed to be unix file permission flags as it appears you have provided. The server should use O_WRONLY and the client should use O_RDONLY.
write(fd, "hello", 5); and read(fd, recvBuf, 5); are not writing and reading the terminating NUL character of the string. But then it is printed as a string: printf("client receive %s\n", recvBuf);. This invokes Undefined Behaviour (even though there is a good chance the program may appear to "work"). Change 5 to 6.
open() uses following flags:-
O_RDONLY open for reading only
O_WRONLY open for writing only
O_RDWR open for reading and writing
O_NONBLOCK do not block on open or for data to become available
O_APPEND append on each write
O_CREAT create file if it does not exist
O_TRUNC truncate size to 0
O_EXCL error if O_CREAT and the file exists
O_SHLOCK atomically obtain a shared lock
O_EXLOCK atomically obtain an exclusive lock
O_NOFOLLOW do not follow symlinks
O_SYMLINK allow open of symlinks
O_EVTONLY descriptor requested for event notifications only
O_CLOEXEC mark as close-on-exec
for FIFO you must use O_RDONLY in client and O_WRONLY in server in your program.
0200 and 0400 permissions are not working for open(). you can check the the flag value in as
#define O_RDONLY 0x0000 / open for reading only */
#define O_WRONLY 0x0001 / open for writing only */
thats why open blocks in your case as it doesn't get correct flag.

Non-blocking read on pipe

Can one do non-blocking I/O on a pipe? fcntl fails to set O_NONBLOCK. Page 918 of The Linux Programming Interface includes a table 'Semantics of reading n bytes from pipe or FIFO (p)'. This table lists the behaviour of pipes and FIFO's with one column titled O_NONBLOCK enabled? This would imply that you can set the O_NONBLOCK flag on a pipe. Is this correct? The following code fails to set the flag, fcntl(2) does not report an error though.
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <fcntl.h>
int main()
{
int fds[2];
pid_t pid;
char wr_buf[100];
char rd_buf[100];
pipe(fds);
pid = fork();
if ( pid )
{
while (1 )
{
memcpy( wr_buf, "abcdefghi\0",10);
write( fds[1], wr_buf, 10);
sleep(2);
}
}
else
{
int retval = fcntl( fds[0], F_SETFL, fcntl(fds[0], F_GETFL) | O_NONBLOCK);
printf("Ret from fcntl: %d\n", retval);
while (1)
{
ssize_t r=read( fds[0], rd_buf, 10 );
printf("read: %d\n", r);
if ( r > 0 )
{
printf("Buffer: %s\n", rd_buf);
}
else
{
printf("Read nothing\n");
perror("Error was");
sleep(1);
}
}
}
}
There is nothing special to pipe and O_NONBLOCK. The following example work as expected. I did not check every retval from every call to make the example a bit more readable. A real world application must do the checks.
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <fcntl.h>
int main()
{
int fds[2];
pid_t pid;
char buf[100];
pipe(fds);
pid = fork();
if ( pid )
{
while (1 )
{
memcpy( buf, "abcdefghi\0",10);
write( fds[1], buf, 10);
sleep(2);
}
}
else
{
int retval = fcntl( fds[0], F_SETFL, fcntl(fds[0], F_GETFL) | O_NONBLOCK);
printf("Ret from fcntl: %d\n", retval);
while (1)
{
ssize_t r=read( fds[0], buf, 10 );
printf("read: %d\n", r);
if ( r > 0 )
{
printf("Buffer: %s\n", buf);
}
else
{
printf("Read nothing\n");
perror("Error was");
sleep(1);
}
}
}
}
After writing my example I inspect your code and found:
flags = fcntl(pfd[0], F_GETFD);
flags |= O_NONBLOCK;
if (fcntl(pfd[0], F_SETFD, flags))
Please change F_SETFD to F_SETFL and also for the get operation. You would not change the file descriptor flags but the file status flags :-)
From man 3 fcntl:
File descriptor flags
The following commands manipulate the flags associated with a file
descriptor. Currently, only one such flag is defined: FD_CLOEXEC, the
close-on-exec flag. If the FD_CLOEXEC bit is 0, the file descriptor
will remain open across an execve(2), otherwise it will be closed.
File status flags
Each open file description has certain associated status flags, ini‐
tialized by open(2) and possibly modified by fcntl(). Duplicated file
descriptors (made with dup(2), fcntl(F_DUPFD), fork(2), etc.) refer to
the same open file description, and thus share the same file status
flags.
F_SETFL (int)
Set the file status flags to the value specified by arg. File
access mode (O_RDONLY, O_WRONLY, O_RDWR) and file creation flags
(i.e., O_CREAT, O_EXCL, O_NOCTTY, O_TRUNC) in arg are ignored.
On Linux this command can change only the O_APPEND, O_ASYNC,
O_DIRECT, O_NOATIME, and O_NONBLOCK flags. It is not possible
to change the O_DSYNC and O_SYNC flags; see BUGS, below.

How to set O_NONBLOCKING flag in Unix

I am writing a sender/reader IPC C program for class, and I am having trouble setting the O_NONBLOCK flag to 0 so that my reader will block when the buffer it is attempting to read from is empty. Here are the functions I am using:
int set_nonblock_flag(int desc, int value)
{
int oldflags = fcntl(desc, F_GETFL, 0);
if (oldflags == -1)
return -1;
if (value != 0)
oldflags |= O_NONBLOCK;
else
oldflags &= ~O_NONBLOCK;
return fcntl(desc, F_SETFL, oldflags);
}
main()
main ()
{
int fd[2], nbytes;
char readbuff[26];
int r_pid = 0;
int s_pid = 0;
/* THIS IS ALL UPDATED!*/
fd[0] = open("fd.txt",O_RDONLY);
fd[1] = open("fd.txt",O_WRONLY);
set_nonblock_flag(fd[0], 0);
set_nonblock_flag(fd[1], 0);
/* END UPDATES */
pipe(fd);
r_pid = fork();
if (r_pid < 0) /* error */
{
fprintf( stderr, "Failed to fork receiver\n" );
exit( -1 );
}
else if (r_pid == 0) /* this is the receiver */
{
fprintf( stdout, "I, %d am the receiver!\n", getpid() );
close( fd[1] ); /* close write end */
nbytes = read( fd[0], readbuff, 1 );
printf ("nonblocking flag = %d\n", fcntl(fd, F_GETFL, 0));
printf ("Nbytes read: %d\n", nbytes );
}
... /* rest of function removed */
The line printf ("nonblocking flag = %d\n", fcntl(fd, F_GETFL, 0));
is just returning -1 as the flag status. Shouldn't it be 0 if it is cleared?
You are calling set_nonblock_flag with first argument as an array of ints.
Here's a snippet from fcntl manpage. The first argument should be a file descriptor.
SYNOPSIS
#include <fcntl.h>
int fcntl(int fildes, int cmd, ...);
DESCRIPTION
The fcntl() function shall perform the operations described below on open files. The fildes argument is a file
descriptor.
I think you want to first call pipe and then call set_nonblock_flag. So, I think what you really want is the following:
int fd[2];
...
pipe(fd);
set_nonblock_flag(fd[0], 0);

Writing to file descriptor

In the following snippet i am redirecting the output of the ls command to input of wc -l which works perfectly .Now i also want to redirect the output of ls command to a file named "beejoutput.txt" using the following code but its not working. Need help.
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main(void)
{
int pfds[2];
pipe(pfds);
if (!fork())
{
dup2(pfds[1],1);
close(pfds[0]);
execlp("ls", "ls",NULL);
}
else
{
FILE *outputO=fopen ("beejoutput.txt", "w"); //opening file for writing
dup2(pfds[0],0);
dup2(fileno(outputO),pfds[0]);
close(pfds[1]);
execlp("wc", "wc","-l", NULL);
}
return 0;
}
The dup function duplicates a file descriptor, that is, both the old and new file descriptors refer to the same open file afterwards. That is different from having a single file descriptor refer to two different files at the same time.
If you want to send the same data to two different destinations, you need to spawn both commands in separate processes, and do the copying yourself, or spawn a copy of the "tee" command -- either way, you end up with three processes.
This worked for me :
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
int main(void)
{
int pfds[2];
pipe(pfds);
pid_t childpid = fork();
if (childpid == 0) {
/* Child */
dup2(pfds[1],1);
close(pfds[0]);
execlp("ls", "ls",NULL);
} else {
/* Parent */
pid_t retpid;
int child_stat;
while ((retpid = waitpid(childpid, &child_stat, 0)) != childpid && retpid != (pid_t) -1)
;
close(pfds[1]);
char buf[100];
ssize_t bytesread;
int fd = open("beejoutput.txt", O_CREAT | O_RDWR, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
if (fd == -1) {
fprintf(stderr, "Opening of beejoutput.txt failed!\n");
exit(1);
}
/* This part writes to beejoutput.txt */
while ((bytesread = read(pfds[0], buf, 100)) > 0) {
write(fd, buf, bytesread);
}
lseek(fd, (off_t) 0, SEEK_SET);
dup2(fd, 0);
execlp("wc", "wc", "-l", NULL);
}
return 0;
}
Try checking the result codes of all the system calls that you do (including dup2). This will possibly lead you to an answer. This is a good habbit, anyway.

Resources